Browse code

FreshClam: Improved HTTP 304, 403, & 429 handling

Added special warning messages for 403 and 429 HTTP codes.
For 403, FreshClam will fail (non-zero exit code) if not in daemon-mode.
For 429, FreshClam will succeed (exit 0) if not in daemon-mode.

Adds If-Modified-Since header for CVD downloads (not just CVD-head)
which should reduce data usage if DNS is advertising a newer version
than is actually available, which seems to happen sometimes due to
caching issues, it should still fail out when this happens - it just
won't have to download the older CVD, and should detect the HTTP 304
(Not-Modified) response instead.

Also replaced "Freshclam" with "FreshClam" in a few places, for
consistency.

Micah Snyder authored on 2021/03/21 13:25:18
Showing 13 changed files
... ...
@@ -427,7 +427,7 @@ ClamAV 0.102.2 is a bug patch release to address the following issues.
427 427
 
428 428
 - Fixed an issue where running freshclam manually causes a daemonized freshclam
429 429
   process to fail when it updates because the manual instance deletes the
430
-  temporary download directory. Freshclam temporary files will now download to a
430
+  temporary download directory. FreshClam temporary files will now download to a
431 431
   unique directory created at the time of an update instead of using a hardcoded
432 432
   directory created/destroyed at the program start/exit.
433 433
 
... ...
@@ -916,7 +916,7 @@ we've cooked up over the past 6 months.
916 916
   numeric value. You can read more about this feature, see how it works, and
917 917
   look over examples in [our documentation](docs/UserManual/Signatures.md).
918 918
 - Backwards compatibility improvements for detecting the OpenSSL dependency.
919
-- Freshclam updated to match exit codes defined in the freshclam.1 man page.
919
+- FreshClam updated to match exit codes defined in the freshclam.1 man page.
920 920
 - Upgrade from libmspack 0.5alpha to libmspack 0.7.1alpha. As a reminder, we
921 921
   support system-installed versions of libmspack. _However_, at this time the
922 922
   ClamAV-provided version of libmspack provides additional abilities to parse
... ...
@@ -1491,7 +1491,7 @@ ClamAV 0.98.4 is a bug fix release. The following issues are now resolved:
1491 1491
 - Crashes of clamd on Windows and Mac OS X platforms when reloading
1492 1492
   the virus signature database.
1493 1493
 - Infinite loop in clamdscan when clamd is not running.
1494
-- Freshclam failure on Solaris 10.
1494
+- FreshClam failure on Solaris 10.
1495 1495
 - Buffer underruns when handling multi-part MIME email attachments.
1496 1496
 - Configuration of OpenSSL on various platforms.
1497 1497
 - Name collisions on Ubuntu 14.04, Debian sid, and Slackware 14.1.
... ...
@@ -1951,7 +1951,7 @@ version:
1951 1951
   Numbers and credit card numbers (clamd: StructuredDataDetection,
1952 1952
   clamscan: --detect-structured; additional fine-tuning options are available)
1953 1953
 
1954
-- IPv6 Support: Freshclam now supports IPv6
1954
+- IPv6 Support: FreshClam now supports IPv6
1955 1955
 
1956 1956
 - Improved Scanning of Scripts: The normalization of scripts now covers
1957 1957
   JavaScript
... ...
@@ -3240,7 +3240,7 @@ News from ClamAV world:
3240 3240
   - clamav.linux-sxs.org: database mirror - rsync from clamav.ozforces.com
3241 3241
     (thanks to Douglas J Hunley <doug@hunley.homeip.net>)
3242 3242
 
3243
-    Freshclam will automatically use them when the main server is not
3243
+    FreshClam will automatically use them when the main server is not
3244 3244
     accessible.
3245 3245
 
3246 3246
 - Official port in FreeBSD available ! (maintained by Masahiro Teramoto
... ...
@@ -147,7 +147,7 @@ m4_include([m4/reorganization/bsd.m4])
147 147
 dnl Clamonacc loading
148 148
 m4_include([m4/reorganization/clamonacc.m4])
149 149
 
150
-dnl Freshclam dependencies
150
+dnl FreshClam dependencies
151 151
 m4_include([m4/reorganization/libs/curl.m4])
152 152
 m4_include([m4/reorganization/substitutions.m4])
153 153
 m4_include([m4/reorganization/strni.m4])
... ...
@@ -10,7 +10,7 @@ freshclam [options]
10 10
 freshclam is a virus database update tool for ClamAV.
11 11
 .SH "OPTIONS"
12 12
 .LP
13
-Freshclam reads its configuration from freshclam.conf. The settings can be overwritten with command line options.
13
+FreshClam reads its configuration from freshclam.conf. The settings can be overwritten with command line options.
14 14
 .TP
15 15
 \fB\-h, \-\-help\fR
16 16
 Output help information and exit.
... ...
@@ -92,7 +92,7 @@ Number of database checks per day.
92 92
 Default: 12
93 93
 .TP
94 94
 \fBDNSDatabaseInfo STRING\fR
95
-Use DNS to verify the virus database version. Freshclam uses DNS TXT records to verify the versions of the database and software itself. With this directive you can change the database verification domain.
95
+Use DNS to verify the virus database version. FreshClam uses DNS TXT records to verify the versions of the database and software itself. With this directive you can change the database verification domain.
96 96
 .br
97 97
 \fBWARNING:\fR Please don't change it unless you're configuring freshclam to use your own database verification domain.
98 98
 .br
... ...
@@ -58,7 +58,7 @@ Example
58 58
 # Default: clamav (may depend on installation options)
59 59
 #DatabaseOwner clamav
60 60
 
61
-# Use DNS to verify virus database version. Freshclam uses DNS TXT records
61
+# Use DNS to verify virus database version. FreshClam uses DNS TXT records
62 62
 # to verify database and software versions. With this directive you can change
63 63
 # the database verification domain.
64 64
 # WARNING: Do not touch it unless you're configuring freshclam to use your
... ...
@@ -1,4 +1,4 @@
1
-PROJECT_NAME     = ClamAV - Freshclam
1
+PROJECT_NAME     = ClamAV - FreshClam
2 2
 OUTPUT_DIRECTORY = ../docs/freshclam
3 3
 WARNINGS         = YES
4 4
 FILE_PATTERNS    = *.c *.h
... ...
@@ -18,4 +18,4 @@ DOT_CLEANUP=NO
18 18
 MAX_DOT_GRAPH_DEPTH=3
19 19
 
20 20
 EXTRACT_ALL=YES
21
-INPUT = . 
21
+INPUT = .
... ...
@@ -261,7 +261,7 @@ fc_error_t download_complete_callback(const char *dbFilename, void *context)
261 261
             ret = FC_ETESTFAIL;
262 262
         }
263 263
         if (FC_SUCCESS != ret) {
264
-            logg("^Database load exited with \"%s\" (%d)\n", fc_strerror(ret), ret);
264
+            logg("^Database load exited with \"%s\"\n", fc_strerror(ret));
265 265
             status = FC_ETESTFAIL;
266 266
             goto done;
267 267
         }
... ...
@@ -276,7 +276,7 @@ fc_error_t download_complete_callback(const char *dbFilename, void *context)
276 276
             logg("^pipe() failed: %s\n", strerror(errno));
277 277
             ret = fc_test_database(dbFilename, fc_context->bBytecodeEnabled);
278 278
             if (FC_SUCCESS != ret) {
279
-                logg("^Database load exited with \"%s\" (%d)\n", fc_strerror(ret), ret);
279
+                logg("^Database load exited with \"%s\"\n", fc_strerror(ret));
280 280
                 status = FC_ETESTFAIL;
281 281
                 goto done;
282 282
             }
... ...
@@ -302,7 +302,7 @@ fc_error_t download_complete_callback(const char *dbFilename, void *context)
302 302
                     /* Test the database without forking. */
303 303
                     ret = fc_test_database(dbFilename, fc_context->bBytecodeEnabled);
304 304
                     if (FC_SUCCESS != ret) {
305
-                        logg("^Database load exited with \"%s\" (%d)\n", fc_strerror(ret), ret);
305
+                        logg("^Database load exited with \"%s\"\n", fc_strerror(ret));
306 306
                         status = FC_ETESTFAIL;
307 307
                         goto done;
308 308
                     }
... ...
@@ -367,7 +367,7 @@ fc_error_t download_complete_callback(const char *dbFilename, void *context)
367 367
                     if (WIFEXITED(stat_loc)) {
368 368
                         ret = (fc_error_t)WEXITSTATUS(stat_loc);
369 369
                         if (FC_SUCCESS != ret) {
370
-                            logg("^Database load exited with \"%s\" (%d)\n", fc_strerror(ret), ret);
370
+                            logg("^Database load exited with \"%s\"\n", fc_strerror(ret));
371 371
                             status = FC_ETESTFAIL;
372 372
                             goto done;
373 373
                         }
... ...
@@ -555,7 +555,7 @@ static void free_string_list(char **stringList, uint32_t nListItems)
555 555
 /**
556 556
  * @brief Get the database server list object
557 557
  *
558
- * @param opts          Freshclam options struct.
558
+ * @param opts          FreshClam options struct.
559 559
  * @param serverList    [out] List of servers.
560 560
  * @param nServers      [out] Number of servers in list.
561 561
  * @param bPrivate      [out] Non-zero if PrivateMirror servers were selected.
... ...
@@ -1463,7 +1463,7 @@ fc_error_t perform_database_update(
1463 1463
             (void *)fc_context,
1464 1464
             &nUpdated);
1465 1465
         if (FC_SUCCESS != ret) {
1466
-            logg("!Database update process failed: %s (%d)\n", fc_strerror(ret), ret);
1466
+            logg("!Database update process failed: %s\n", fc_strerror(ret));
1467 1467
             status = ret;
1468 1468
             goto done;
1469 1469
         }
... ...
@@ -1480,7 +1480,7 @@ fc_error_t perform_database_update(
1480 1480
             (void *)fc_context,
1481 1481
             &nUpdated);
1482 1482
         if (FC_SUCCESS != ret) {
1483
-            logg("!Database update process failed: %s (%d)\n", fc_strerror(ret), ret);
1483
+            logg("!Database update process failed: %s\n", fc_strerror(ret));
1484 1484
             status = ret;
1485 1485
             goto done;
1486 1486
         }
... ...
@@ -1,4 +1,4 @@
1
-PROJECT_NAME     = ClamAV - Freshclam
1
+PROJECT_NAME     = ClamAV - FreshClam
2 2
 OUTPUT_DIRECTORY = ../docs/freshclam
3 3
 WARNINGS         = YES
4 4
 FILE_PATTERNS    = *.c *.h
... ...
@@ -18,4 +18,4 @@ DOT_CLEANUP=NO
18 18
 MAX_DOT_GRAPH_DEPTH=3
19 19
 
20 20
 EXTRACT_ALL=YES
21
-INPUT = . 
21
+INPUT = .
... ...
@@ -113,6 +113,10 @@ const char *fc_strerror(fc_error_t fcerror)
113 113
             return "Memory allocation error";
114 114
         case FC_EARG:
115 115
             return "Invalid argument(s)";
116
+        case FC_EFORBIDDEN:
117
+            return "Forbidden, Blocked by CDN";
118
+        case FC_ERETRYLATER:
119
+            return "Too-many-requests, Retry later";
116 120
         default:
117 121
             return "Unknown libfreshclam error code!";
118 122
     }
... ...
@@ -627,8 +631,7 @@ fc_error_t fc_update_database(
627 627
                 }
628 628
                 case FC_ECONNECTION:
629 629
                 case FC_EBADCVD:
630
-                case FC_EFAILEDGET:
631
-                case FC_EMIRRORNOTSYNC: {
630
+                case FC_EFAILEDGET: {
632 631
                     if (attempt < g_maxAttempts) {
633 632
                         logg("Trying again in 5 secs...\n");
634 633
                         sleep(5);
... ...
@@ -642,8 +645,43 @@ fc_error_t fc_update_database(
642 642
                     }
643 643
                     break;
644 644
                 }
645
+                case FC_EMIRRORNOTSYNC: {
646
+                    logg("!Update failed for database: %s\n", database);
647
+                    status = ret;
648
+                    goto done;
649
+                }
650
+                case FC_EFORBIDDEN: {
651
+                    logg("^FreshClam received error code 403 from the ClamAV Content Delivery Network (CDN).\n");
652
+                    logg("This could mean several things:\n");
653
+                    logg(" 1. You are running an out of date version of ClamAV / FreshClam.\n");
654
+                    logg("    Ensure you are the most updated version by visiting https://www.clamav.net/downloads\n");
655
+                    logg(" 2. Your network is explicitly denied by the FreshClam CDN.\n");
656
+                    logg("    In order to rectify this please check that you are:\n");
657
+                    logg("   a. Running an up to date version of FreshClam\n");
658
+                    logg("   b. Running FreshClam no more than once an hour\n");
659
+                    logg("   c. If you have checked (a) and (b), please open a ticket at\n");
660
+                    logg("      https://bugzilla.clamav.net under the “Mirrors” component\n");
661
+                    logg("      and we will investigate why your network is blocked.\n");
662
+                    status = ret;
663
+                    goto done;
664
+                    break;
665
+                }
666
+                case FC_ERETRYLATER: {
667
+                    logg("^FreshClam received error code 429 from the ClamAV Content Delivery Network (CDN).\n");
668
+                    logg("This means that you have been rate limited by the CDN.\n");
669
+                    logg(" 1. Run FreshClam no more than once an hour to check for updates.\n");
670
+                    logg("    Freshclam should check DNS first to see if an update is needed.\n");
671
+                    logg(" 2. If you have more than 10 hosts on your network attempting to download,\n");
672
+                    logg("    it is recommended that you set up a private mirror on your network using\n");
673
+                    logg("    cvdupdate (https://pypi.org/project/cvdupdate/) to save bandwidth on the\n");
674
+                    logg("    CDN and your own network.\n");
675
+                    logg(" 3. Please do not open a ticket asking for an exemption from the rate limit,\n");
676
+                    logg("    it will not be granted.\n");
677
+                    goto success;
678
+                    break;
679
+                }
645 680
                 default: {
646
-                    logg("!Unexpected error when attempting to update database: %s\n", database);
681
+                    logg("!Unexpected error when attempting to update %s: %s\n", database, fc_strerror(ret));
647 682
                     status = ret;
648 683
                     goto done;
649 684
                 }
... ...
@@ -698,7 +736,6 @@ fc_error_t fc_update_databases(
698 698
                                bScriptedUpdates,
699 699
                                context,
700 700
                                &bUpdated))) {
701
-            logg("^fc_update_databases: fc_update_database failed: %s (%d)\n", fc_strerror(ret), ret);
702 701
             status = ret;
703 702
             goto done;
704 703
         }
... ...
@@ -24,7 +24,7 @@
24 24
 #include "clamav-types.h"
25 25
 
26 26
 /*
27
- * Freshclam configuration flag options.
27
+ * FreshClam configuration flag options.
28 28
  */
29 29
 // clang-format off
30 30
 #define FC_CONFIG_MSG_DEBUG        0x1  // Enable debug messages.
... ...
@@ -79,7 +79,9 @@ typedef enum fc_error_tag {
79 79
     FC_ELOGGING,
80 80
     FC_EFAILEDUPDATE,
81 81
     FC_EMEM,
82
-    FC_EARG
82
+    FC_EARG,
83
+    FC_EFORBIDDEN,
84
+    FC_ERETRYLATER
83 85
 } fc_error_t;
84 86
 
85 87
 /**
... ...
@@ -240,7 +242,7 @@ fc_error_t fc_update_databases(
240 240
  */
241 241
 
242 242
 /**
243
- * @brief Freshclam callback Download Complete
243
+ * @brief FreshClam callback Download Complete
244 244
  *
245 245
  * Called after each database has been downloaded or updated.
246 246
  *
... ...
@@ -575,13 +575,11 @@ static fc_error_t remote_cvdhead(
575 575
     /*
576 576
      * Request CVD header.
577 577
      */
578
-    logg("Reading CVD header (%s): ", cvdfile);
579
-
580 578
     urlLen = strlen(server) + strlen("/") + strlen(cvdfile);
581 579
     url    = malloc(urlLen + 1);
582 580
     snprintf(url, urlLen + 1, "%s/%s", server, cvdfile);
583 581
 
584
-    logg("*Trying to retrieve CVD header from %s\n", url);
582
+    logg("Trying to retrieve CVD header from %s\n", url);
585 583
 
586 584
     if (FC_SUCCESS != (ret = create_curl_handle(
587 585
                            bHttpServer, // Set extra HTTP-specific headers.
... ...
@@ -1000,6 +998,14 @@ static fc_error_t downloadFile(
1000 1000
             status = FC_UPTODATE;
1001 1001
             break;
1002 1002
         }
1003
+        case 403: {
1004
+            status = FC_EFORBIDDEN;
1005
+            break;
1006
+        }
1007
+        case 429: {
1008
+            status = FC_ERETRYLATER;
1009
+            break;
1010
+        }
1003 1011
         case 404: {
1004 1012
             if (g_proxyServer)
1005 1013
                 logg("^downloadFile: file not found: %s (Proxy: %s:%u)\n", url, g_proxyServer, g_proxyPort);
... ...
@@ -1050,6 +1056,7 @@ static fc_error_t getcvd(
1050 1050
     const char *cvdfile,
1051 1051
     const char *tmpfile,
1052 1052
     char *server,
1053
+    uint32_t ifModifiedSince,
1053 1054
     unsigned int remoteVersion,
1054 1055
     int logerr)
1055 1056
 {
... ...
@@ -1071,8 +1078,13 @@ static fc_error_t getcvd(
1071 1071
     url    = malloc(urlLen + 1);
1072 1072
     snprintf(url, urlLen + 1, "%s/%s", server, cvdfile);
1073 1073
 
1074
-    if (FC_SUCCESS != (ret = downloadFile(url, tmpfile, 1, logerr, 0))) {
1075
-        logg("%cgetcvd: Can't download %s from %s\n", logerr ? '!' : '^', cvdfile, url);
1074
+    ret = downloadFile(url, tmpfile, 1, logerr, ifModifiedSince);
1075
+    if (ret == FC_UPTODATE) {
1076
+        logg("%s is up to date.\n", cvdfile);
1077
+        status = ret;
1078
+        goto done;
1079
+    } else if (ret > FC_UPTODATE) {
1080
+        logg("%cCan't download %s from %s\n", logerr ? '!' : '^', cvdfile, url);
1076 1081
         status = ret;
1077 1082
         goto done;
1078 1083
     }
... ...
@@ -1080,32 +1092,32 @@ static fc_error_t getcvd(
1080 1080
     /* Temporarily rename file to correct extension for verification. */
1081 1081
     tmpfile_with_extension = strdup(tmpfile);
1082 1082
     if (!tmpfile_with_extension) {
1083
-        logg("!getcvd: Can't allocate memory for temp file with extension!\n");
1083
+        logg("!Can't allocate memory for temp file with extension!\n");
1084 1084
         status = FC_EMEM;
1085 1085
         goto done;
1086 1086
     }
1087 1087
     strncpy(tmpfile_with_extension + strlen(tmpfile_with_extension) - 4, cvdfile + strlen(cvdfile) - 4, 4);
1088 1088
     if (rename(tmpfile, tmpfile_with_extension) == -1) {
1089
-        logg("!getcvd: Can't rename %s to %s: %s\n", tmpfile, tmpfile_with_extension, strerror(errno));
1089
+        logg("!Can't rename %s to %s: %s\n", tmpfile, tmpfile_with_extension, strerror(errno));
1090 1090
         status = FC_EDBDIRACCESS;
1091 1091
         goto done;
1092 1092
     }
1093 1093
 
1094 1094
     if (CL_SUCCESS != (cl_ret = cl_cvdverify(tmpfile_with_extension))) {
1095
-        logg("!getcvd: Verification: %s\n", cl_strerror(cl_ret));
1095
+        logg("!Verification: %s\n", cl_strerror(cl_ret));
1096 1096
         status = FC_EBADCVD;
1097 1097
         goto done;
1098 1098
     }
1099 1099
 
1100 1100
     if (NULL == (cvd = cl_cvdhead(tmpfile_with_extension))) {
1101
-        logg("!getcvd: Can't read CVD header of new %s database.\n", cvdfile);
1101
+        logg("!Can't read CVD header of new %s database.\n", cvdfile);
1102 1102
         status = FC_EBADCVD;
1103 1103
         goto done;
1104 1104
     }
1105 1105
 
1106 1106
     /* Rename the file back to the original, since verification passed. */
1107 1107
     if (rename(tmpfile_with_extension, tmpfile) == -1) {
1108
-        logg("!getcvd: Can't rename %s to %s: %s\n", tmpfile_with_extension, tmpfile, strerror(errno));
1108
+        logg("!Can't rename %s to %s: %s\n", tmpfile_with_extension, tmpfile, strerror(errno));
1109 1109
         status = FC_EDBDIRACCESS;
1110 1110
         goto done;
1111 1111
     }
... ...
@@ -1719,7 +1731,8 @@ static fc_error_t check_for_new_database_version(
1719 1719
     uint32_t *localVersion,
1720 1720
     uint32_t *remoteVersion,
1721 1721
     char **localFilename,
1722
-    char **remoteFilename)
1722
+    char **remoteFilename,
1723
+    uint32_t *localTimestamp)
1723 1724
 {
1724 1725
     fc_error_t ret;
1725 1726
     fc_error_t status = FC_EARG;
... ...
@@ -1728,13 +1741,13 @@ static fc_error_t check_for_new_database_version(
1728 1728
     struct cl_cvd *local_database = NULL;
1729 1729
     char *remotename              = NULL;
1730 1730
 
1731
-    uint32_t localver       = 0;
1732
-    uint32_t localTimestamp = 0;
1733
-    uint32_t remotever      = 0;
1731
+    uint32_t localver  = 0;
1732
+    uint32_t remotever = 0;
1734 1733
 
1735 1734
     if ((NULL == database) || (NULL == server) ||
1736 1735
         (NULL == localVersion) || (NULL == remoteVersion) ||
1737
-        (NULL == localFilename) || (NULL == remoteFilename)) {
1736
+        (NULL == localFilename) || (NULL == remoteFilename) ||
1737
+        (NULL == localTimestamp)) {
1738 1738
         logg("!check_for_new_database_version: Invalid args!\n");
1739 1739
         goto done;
1740 1740
     }
... ...
@@ -1743,6 +1756,7 @@ static fc_error_t check_for_new_database_version(
1743 1743
     *remoteVersion  = 0;
1744 1744
     *localFilename  = NULL;
1745 1745
     *remoteFilename = NULL;
1746
+    *localTimestamp = 0;
1746 1747
 
1747 1748
     /*
1748 1749
      * Check local database version (if exists)
... ...
@@ -1751,8 +1765,8 @@ static fc_error_t check_for_new_database_version(
1751 1751
         logg("*check_for_new_database_version: No local copy of \"%s\" database.\n", database);
1752 1752
     } else {
1753 1753
         logg("*check_for_new_database_version: Local copy of %s found: %s.\n", database, localname);
1754
-        localTimestamp = local_database->stime;
1755
-        localver       = local_database->version;
1754
+        *localTimestamp = local_database->stime;
1755
+        localver        = local_database->version;
1756 1756
     }
1757 1757
 
1758 1758
     /*
... ...
@@ -1760,7 +1774,7 @@ static fc_error_t check_for_new_database_version(
1760 1760
      */
1761 1761
     ret = query_remote_database_version(
1762 1762
         database,
1763
-        localTimestamp,
1763
+        *localTimestamp,
1764 1764
         dnsUpdateInfo,
1765 1765
         server,
1766 1766
         bPrivateMirror,
... ...
@@ -1858,11 +1872,12 @@ fc_error_t updatedb(
1858 1858
 
1859 1859
     struct cl_cvd *cvd = NULL;
1860 1860
 
1861
-    uint32_t localVersion  = 0;
1862
-    uint32_t remoteVersion = 0;
1863
-    char *localFilename    = NULL;
1864
-    char *remoteFilename   = NULL;
1865
-    char *newLocalFilename = NULL;
1861
+    uint32_t localTimestamp = 0;
1862
+    uint32_t localVersion   = 0;
1863
+    uint32_t remoteVersion  = 0;
1864
+    char *localFilename     = NULL;
1865
+    char *remoteFilename    = NULL;
1866
+    char *newLocalFilename  = NULL;
1866 1867
 
1867 1868
     char *tmpdir  = NULL;
1868 1869
     char *tmpfile = NULL;
... ...
@@ -1892,7 +1907,8 @@ fc_error_t updatedb(
1892 1892
                            &localVersion,
1893 1893
                            &remoteVersion,
1894 1894
                            &localFilename,
1895
-                           &remoteFilename))) {
1895
+                           &remoteFilename,
1896
+                           &localTimestamp))) {
1896 1897
         logg("*updatedb: %s database update failed.\n", database);
1897 1898
         status = ret;
1898 1899
         goto done;
... ...
@@ -1914,8 +1930,15 @@ fc_error_t updatedb(
1914 1914
         /*
1915 1915
          * Download entire file.
1916 1916
          */
1917
-        ret = getcvd(remoteFilename, tmpfile, server, remoteVersion, logerr);
1918
-        if (FC_SUCCESS != ret) {
1917
+        ret = getcvd(remoteFilename, tmpfile, server, localTimestamp, remoteVersion, logerr);
1918
+        if (FC_UPTODATE == ret) {
1919
+            logg("^Expected newer version of %s database but the server's copy is not newer than our local file (version %d).\n", database, localVersion);
1920
+            if (NULL != localFilename) {
1921
+                /* Received a 304 (not modified), must be up to date after all */
1922
+                *dbFilename = cli_strdup(localFilename);
1923
+            }
1924
+            goto up_to_date;
1925
+        } else if (FC_SUCCESS != ret) {
1919 1926
             status = ret;
1920 1927
             goto done;
1921 1928
         }
... ...
@@ -1980,7 +2003,7 @@ fc_error_t updatedb(
1980 1980
                 logg("^Incremental update failed, trying to download %s\n", remoteFilename);
1981 1981
             }
1982 1982
 
1983
-            ret = getcvd(remoteFilename, tmpfile, server, remoteVersion, logerr);
1983
+            ret = getcvd(remoteFilename, tmpfile, server, localTimestamp, remoteVersion, logerr);
1984 1984
             if (FC_SUCCESS != ret) {
1985 1985
                 status = ret;
1986 1986
                 goto done;
... ...
@@ -496,7 +496,7 @@ const struct clam_option __clam_options[] = {
496 496
     {"DevPerformance", "dev-performance", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, -1, NULL, FLAG_HIDDEN, OPT_CLAMD | OPT_CLAMSCAN, "", ""},
497 497
     {"DevLiblog", "dev-liblog", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, -1, NULL, FLAG_HIDDEN, OPT_CLAMD, "", ""},
498 498
 
499
-    /* Freshclam-only entries */
499
+    /* FreshClam-only entries */
500 500
 
501 501
     /* FIXME: drop this entry and use LogFile */
502 502
     {"UpdateLogFile", "log", 'l', CLOPT_TYPE_STRING, NULL, -1, NULL, 0, OPT_FRESHCLAM, "Save all reports to a log file.", "/var/log/freshclam.log"},
... ...
@@ -505,7 +505,7 @@ const struct clam_option __clam_options[] = {
505 505
 
506 506
     {"Checks", "checks", 'c', CLOPT_TYPE_NUMBER, MATCH_NUMBER, 12, NULL, 0, OPT_FRESHCLAM, "This option defined how many times daily freshclam should check for\na database update.", "24"},
507 507
 
508
-    {"DNSDatabaseInfo", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, "current.cvd.clamav.net", FLAG_REQUIRED, OPT_FRESHCLAM, "Use DNS to verify the virus database version. Freshclam uses DNS TXT records\nto verify the versions of the database and software itself. With this\ndirective you can change the database verification domain.\nWARNING: Please don't change it unless you're configuring freshclam to use\nyour own database verification domain.", "current.cvd.clamav.net"},
508
+    {"DNSDatabaseInfo", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, "current.cvd.clamav.net", FLAG_REQUIRED, OPT_FRESHCLAM, "Use DNS to verify the virus database version. FreshClam uses DNS TXT records\nto verify the versions of the database and software itself. With this\ndirective you can change the database verification domain.\nWARNING: Please don't change it unless you're configuring freshclam to use\nyour own database verification domain.", "current.cvd.clamav.net"},
509 509
 
510 510
     {"DatabaseMirror", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, FLAG_MULTIPLE, OPT_FRESHCLAM, "DatabaseMirror specifies to which mirror(s) freshclam should connect.\nYou should have at least one entry: database.clamav.net.", "database.clamav.net"},
511 511
 
... ...
@@ -55,7 +55,7 @@ Example
55 55
 # Default: clamav (may depend on installation options)
56 56
 #DatabaseOwner clamav
57 57
 
58
-# Use DNS to verify virus database version. Freshclam uses DNS TXT records
58
+# Use DNS to verify virus database version. FreshClam uses DNS TXT records
59 59
 # to verify database and software versions. With this directive you can change
60 60
 # the database verification domain.
61 61
 # WARNING: Do not touch it unless you're configuring freshclam to use your