Add back the mirrors.dat file to the database directory.
This new version of mirros.dat will store:
- A randomly generated UUID for the FreshClam User-Agent.
- A retry-after timestamp that so FreshClam won't try to update after
having received an HTTP 429 response until the Retry-After timeout has
expired.
Also: FreshClam will now exit with a failure in daemon mode if an HTTP
403 (Forbidden) was received, because retrying later won't help any.
The FreshClam user will have to take actions to get unblocked.
... | ... |
@@ -1932,7 +1932,7 @@ int main(int argc, char **argv) |
1932 | 1932 |
alarm(0); |
1933 | 1933 |
#endif |
1934 | 1934 |
|
1935 |
- if (ret > 1) { |
|
1935 |
+ if (ret > FC_UPTODATE) { |
|
1936 | 1936 |
if ((opt = optget(opts, "OnErrorExecute"))->enabled) |
1937 | 1937 |
arg = opt->strarg; |
1938 | 1938 |
|
... | ... |
@@ -1940,6 +1940,14 @@ int main(int argc, char **argv) |
1940 | 1940 |
execute("OnErrorExecute", arg, optget(opts, "daemon")->enabled); |
1941 | 1941 |
|
1942 | 1942 |
arg = NULL; |
1943 |
+ |
|
1944 |
+ if (FC_EFORBIDDEN == ret) { |
|
1945 |
+ /* We're being actively blocked, which is a fatal error. Exit. */ |
|
1946 |
+ logg("^Freshclam was forbidden from downloading a database.\n"); |
|
1947 |
+ logg("^This is fatal. Retrying later won't help. Exiting now.\n"); |
|
1948 |
+ status = ret; |
|
1949 |
+ goto done; |
|
1950 |
+ } |
|
1943 | 1951 |
} |
1944 | 1952 |
|
1945 | 1953 |
logg("#--------------------------------------\n"); |
... | ... |
@@ -250,6 +250,17 @@ fc_error_t fc_initialize(fc_config *fcConfig) |
250 | 250 |
|
251 | 251 |
g_bCompressLocalDatabase = fcConfig->bCompressLocalDatabase; |
252 | 252 |
|
253 |
+ /* Load or create mirrors.dat */ |
|
254 |
+ if (FC_SUCCESS != load_mirrors_dat()) { |
|
255 |
+ logg("*Failed to load mirrors.dat; will create a new mirrors.dat\n"); |
|
256 |
+ |
|
257 |
+ if (FC_SUCCESS != new_mirrors_dat()) { |
|
258 |
+ logg("^Failed to create a new mirrors.dat!\n"); |
|
259 |
+ status = FC_EINIT; |
|
260 |
+ goto done; |
|
261 |
+ } |
|
262 |
+ } |
|
263 |
+ |
|
253 | 264 |
status = FC_SUCCESS; |
254 | 265 |
|
255 | 266 |
done: |
... | ... |
@@ -667,6 +678,10 @@ fc_error_t fc_update_database( |
667 | 667 |
break; |
668 | 668 |
} |
669 | 669 |
case FC_ERETRYLATER: { |
670 |
+ char retry_after_string[26]; |
|
671 |
+ struct tm *tm_info; |
|
672 |
+ tm_info = localtime(&g_mirrorsDat->retry_after); |
|
673 |
+ strftime(retry_after_string, 26, "%Y-%m-%d %H:%M:%S", tm_info); |
|
670 | 674 |
logg("^FreshClam received error code 429 from the ClamAV Content Delivery Network (CDN).\n"); |
671 | 675 |
logg("This means that you have been rate limited by the CDN.\n"); |
672 | 676 |
logg(" 1. Run FreshClam no more than once an hour to check for updates.\n"); |
... | ... |
@@ -677,6 +692,7 @@ fc_error_t fc_update_database( |
677 | 677 |
logg(" CDN and your own network.\n"); |
678 | 678 |
logg(" 3. Please do not open a ticket asking for an exemption from the rate limit,\n"); |
679 | 679 |
logg(" it will not be granted.\n"); |
680 |
+ logg("^You are on cool-down until after: %s\n", retry_after_string); |
|
680 | 681 |
goto success; |
681 | 682 |
break; |
682 | 683 |
} |
... | ... |
@@ -726,6 +742,33 @@ fc_error_t fc_update_databases( |
726 | 726 |
|
727 | 727 |
*nUpdated = 0; |
728 | 728 |
|
729 |
+ if (g_mirrorsDat->retry_after > 0) { |
|
730 |
+ if (g_mirrorsDat->retry_after > time(NULL)) { |
|
731 |
+ /* We're on cool-down, try again later. */ |
|
732 |
+ char retry_after_string[26]; |
|
733 |
+ struct tm *tm_info; |
|
734 |
+ tm_info = localtime(&g_mirrorsDat->retry_after); |
|
735 |
+ strftime(retry_after_string, 26, "%Y-%m-%d %H:%M:%S", tm_info); |
|
736 |
+ logg("^FreshClam previously received error code 429 from the ClamAV Content Delivery Network (CDN).\n"); |
|
737 |
+ logg("This means that you have been rate limited by the CDN.\n"); |
|
738 |
+ logg(" 1. Run FreshClam no more than once an hour to check for updates.\n"); |
|
739 |
+ logg(" Freshclam should check DNS first to see if an update is needed.\n"); |
|
740 |
+ logg(" 2. If you have more than 10 hosts on your network attempting to download,\n"); |
|
741 |
+ logg(" it is recommended that you set up a private mirror on your network using\n"); |
|
742 |
+ logg(" cvdupdate (https://pypi.org/project/cvdupdate/) to save bandwidth on the\n"); |
|
743 |
+ logg(" CDN and your own network.\n"); |
|
744 |
+ logg(" 3. Please do not open a ticket asking for an exemption from the rate limit,\n"); |
|
745 |
+ logg(" it will not be granted.\n"); |
|
746 |
+ logg("^You are still on cool-down until after: %s\n", retry_after_string); |
|
747 |
+ status = FC_SUCCESS; |
|
748 |
+ goto done; |
|
749 |
+ } else { |
|
750 |
+ g_mirrorsDat->retry_after = 0; |
|
751 |
+ logg("^Cool-down expired, ok to try again.\n"); |
|
752 |
+ save_mirrors_dat(); |
|
753 |
+ } |
|
754 |
+ } |
|
755 |
+ |
|
729 | 756 |
for (i = 0; i < nDatabases; i++) { |
730 | 757 |
if (FC_SUCCESS != (ret = fc_update_database( |
731 | 758 |
databaseList[i], |
... | ... |
@@ -814,6 +857,41 @@ fc_error_t fc_download_url_database( |
814 | 814 |
} |
815 | 815 |
break; |
816 | 816 |
} |
817 |
+ case FC_EFORBIDDEN: { |
|
818 |
+ logg("^FreshClam received error code 403 from the ClamAV Content Delivery Network (CDN).\n"); |
|
819 |
+ logg("This could mean several things:\n"); |
|
820 |
+ logg(" 1. You are running an out of date version of ClamAV / FreshClam.\n"); |
|
821 |
+ logg(" Ensure you are the most updated version by visiting https://www.clamav.net/downloads\n"); |
|
822 |
+ logg(" 2. Your network is explicitly denied by the FreshClam CDN.\n"); |
|
823 |
+ logg(" In order to rectify this please check that you are:\n"); |
|
824 |
+ logg(" a. Running an up to date version of FreshClam\n"); |
|
825 |
+ logg(" b. Running FreshClam no more than once an hour\n"); |
|
826 |
+ logg(" c. If you have checked (a) and (b), please open a ticket at\n"); |
|
827 |
+ logg(" https://bugzilla.clamav.net under the “Mirrors” component\n"); |
|
828 |
+ logg(" and we will investigate why your network is blocked.\n"); |
|
829 |
+ status = ret; |
|
830 |
+ goto done; |
|
831 |
+ break; |
|
832 |
+ } |
|
833 |
+ case FC_ERETRYLATER: { |
|
834 |
+ char retry_after_string[26]; |
|
835 |
+ struct tm *tm_info; |
|
836 |
+ tm_info = localtime(&g_mirrorsDat->retry_after); |
|
837 |
+ strftime(retry_after_string, 26, "%Y-%m-%d %H:%M:%S", tm_info); |
|
838 |
+ logg("^FreshClam received error code 429 from the ClamAV Content Delivery Network (CDN).\n"); |
|
839 |
+ logg("This means that you have been rate limited by the CDN.\n"); |
|
840 |
+ logg(" 1. Run FreshClam no more than once an hour to check for updates.\n"); |
|
841 |
+ logg(" Freshclam should check DNS first to see if an update is needed.\n"); |
|
842 |
+ logg(" 2. If you have more than 10 hosts on your network attempting to download,\n"); |
|
843 |
+ logg(" it is recommended that you set up a private mirror on your network using\n"); |
|
844 |
+ logg(" cvdupdate (https://pypi.org/project/cvdupdate/) to save bandwidth on the\n"); |
|
845 |
+ logg(" CDN and your own network.\n"); |
|
846 |
+ logg(" 3. Please do not open a ticket asking for an exemption from the rate limit,\n"); |
|
847 |
+ logg(" it will not be granted.\n"); |
|
848 |
+ logg("^You are on cool-down until after: %s\n", retry_after_string); |
|
849 |
+ goto success; |
|
850 |
+ break; |
|
851 |
+ } |
|
817 | 852 |
default: { |
818 | 853 |
logg("Unexpected error when attempting to update from custom database URL: %s\n", urlDatabase); |
819 | 854 |
status = ret; |
... | ... |
@@ -66,6 +66,7 @@ |
66 | 66 |
#include <math.h> |
67 | 67 |
|
68 | 68 |
#include <curl/curl.h> |
69 |
+#include <openssl/rand.h> |
|
69 | 70 |
|
70 | 71 |
#include "target.h" |
71 | 72 |
|
... | ... |
@@ -115,6 +116,242 @@ uint32_t g_requestTimeout = 0; |
115 | 115 |
|
116 | 116 |
uint32_t g_bCompressLocalDatabase = 0; |
117 | 117 |
|
118 |
+mirrors_dat_v1_t *g_mirrorsDat = NULL; |
|
119 |
+ |
|
120 |
+/** @brief Generate a Version 4 UUID according to RFC-4122 |
|
121 |
+ * |
|
122 |
+ * Uses the openssl RAND_bytes function to generate a Version 4 UUID. |
|
123 |
+ * |
|
124 |
+ * Copyright 2021 Karthik Velakur |
|
125 |
+ * License: MIT |
|
126 |
+ * From: https://gist.github.com/kvelakur/9069c9896577c3040030 |
|
127 |
+ * |
|
128 |
+ * @param buffer A buffer that is SIZEOF_UUID_V4 |
|
129 |
+ * @retval 1 on success, 0 otherwise. |
|
130 |
+ */ |
|
131 |
+static int uuid_v4_gen(char *buffer) |
|
132 |
+{ |
|
133 |
+ union { |
|
134 |
+ struct |
|
135 |
+ { |
|
136 |
+ uint32_t time_low; |
|
137 |
+ uint16_t time_mid; |
|
138 |
+ uint16_t time_hi_and_version; |
|
139 |
+ uint8_t clk_seq_hi_res; |
|
140 |
+ uint8_t clk_seq_low; |
|
141 |
+ uint8_t node[6]; |
|
142 |
+ }; |
|
143 |
+ uint8_t __rnd[16]; |
|
144 |
+ } uuid; |
|
145 |
+ |
|
146 |
+ int rc = RAND_bytes(uuid.__rnd, sizeof(uuid)); |
|
147 |
+ |
|
148 |
+ // Refer Section 4.2 of RFC-4122 |
|
149 |
+ // https://tools.ietf.org/html/rfc4122#section-4.2 |
|
150 |
+ uuid.clk_seq_hi_res = (uint8_t)((uuid.clk_seq_hi_res & 0x3F) | 0x80); |
|
151 |
+ uuid.time_hi_and_version = (uint16_t)((uuid.time_hi_and_version & 0x0FFF) | 0x4000); |
|
152 |
+ |
|
153 |
+ snprintf(buffer, SIZEOF_UUID_V4, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", |
|
154 |
+ uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, |
|
155 |
+ uuid.clk_seq_hi_res, uuid.clk_seq_low, |
|
156 |
+ uuid.node[0], uuid.node[1], uuid.node[2], |
|
157 |
+ uuid.node[3], uuid.node[4], uuid.node[5]); |
|
158 |
+ buffer[SIZEOF_UUID_V4 - 1] = 0; |
|
159 |
+ |
|
160 |
+ return rc; |
|
161 |
+} |
|
162 |
+ |
|
163 |
+fc_error_t load_mirrors_dat(void) |
|
164 |
+{ |
|
165 |
+ fc_error_t status = FC_EINIT; |
|
166 |
+ int handle = -1; |
|
167 |
+ ssize_t bread = 0; |
|
168 |
+ mirrors_dat_v1_t *mdat = NULL; |
|
169 |
+ uint32_t version = 0; |
|
170 |
+ char magic[13] = {0}; |
|
171 |
+ |
|
172 |
+ /* Change directory to database directory */ |
|
173 |
+ if (chdir(g_databaseDirectory)) { |
|
174 |
+ logg("!Can't change dir to %s\n", g_databaseDirectory); |
|
175 |
+ status = FC_EDIRECTORY; |
|
176 |
+ goto done; |
|
177 |
+ } |
|
178 |
+ logg("*Current working dir is %s\n", g_databaseDirectory); |
|
179 |
+ |
|
180 |
+ if (-1 == (handle = safe_open("mirrors.dat", O_RDONLY | O_BINARY))) { |
|
181 |
+ char currdir[PATH_MAX]; |
|
182 |
+ |
|
183 |
+ if (getcwd(currdir, sizeof(currdir))) |
|
184 |
+ logg("*Can't open mirrors.dat in %s\n", currdir); |
|
185 |
+ else |
|
186 |
+ logg("*Can't open mirrors.dat in the current directory\n"); |
|
187 |
+ |
|
188 |
+ logg("*It probably doesn't exist yet. That's ok.\n"); |
|
189 |
+ status = FC_EFILE; |
|
190 |
+ goto done; |
|
191 |
+ } |
|
192 |
+ |
|
193 |
+ if (strlen(MIRRORS_DAT_MAGIC) != (bread = read(handle, &magic, strlen(MIRRORS_DAT_MAGIC)))) { |
|
194 |
+ char error_message[260]; |
|
195 |
+ cli_strerror(errno, error_message, 260); |
|
196 |
+ logg("!Can't read version from mirrors.dat. Bytes read: %zi, error: %s\n", bread, error_message); |
|
197 |
+ goto done; |
|
198 |
+ } |
|
199 |
+ if (0 != strncmp(magic, MIRRORS_DAT_MAGIC, strlen(MIRRORS_DAT_MAGIC))) { |
|
200 |
+ logg("*Magic bytes for mirrors.dat did not match expectations.\n"); |
|
201 |
+ goto done; |
|
202 |
+ } |
|
203 |
+ |
|
204 |
+ if (sizeof(uint32_t) != (bread = read(handle, &version, sizeof(uint32_t)))) { |
|
205 |
+ char error_message[260]; |
|
206 |
+ cli_strerror(errno, error_message, 260); |
|
207 |
+ logg("!Can't read version from mirrors.dat. Bytes read: %zi, error: %s\n", bread, error_message); |
|
208 |
+ goto done; |
|
209 |
+ } |
|
210 |
+ |
|
211 |
+ switch (version) { |
|
212 |
+ case 1: { |
|
213 |
+ /* Verify that file size is as expected. */ |
|
214 |
+ off_t file_size = lseek(handle, 0L, SEEK_END); |
|
215 |
+ |
|
216 |
+ if (strlen(MIRRORS_DAT_MAGIC) + sizeof(mirrors_dat_v1_t) != (size_t)file_size) { |
|
217 |
+ logg("*mirrors.dat is bigger than expected: %zu != %ld\n", sizeof(mirrors_dat_v1_t), file_size); |
|
218 |
+ goto done; |
|
219 |
+ } |
|
220 |
+ |
|
221 |
+ /* Rewind to just after the magic bytes and read data struct */ |
|
222 |
+ lseek(handle, strlen(MIRRORS_DAT_MAGIC), SEEK_SET); |
|
223 |
+ |
|
224 |
+ mdat = malloc(sizeof(mirrors_dat_v1_t)); |
|
225 |
+ if (NULL == mdat) { |
|
226 |
+ logg("!Failed to allocate memory for mirrors.dat\n"); |
|
227 |
+ status = FC_EMEM; |
|
228 |
+ goto done; |
|
229 |
+ } |
|
230 |
+ |
|
231 |
+ if (sizeof(mirrors_dat_v1_t) != (bread = read(handle, mdat, sizeof(mirrors_dat_v1_t)))) { |
|
232 |
+ char error_message[260]; |
|
233 |
+ cli_strerror(errno, error_message, 260); |
|
234 |
+ logg("!Can't read from mirrors.dat. Bytes read: %zi, error: %s\n", bread, error_message); |
|
235 |
+ goto done; |
|
236 |
+ } |
|
237 |
+ |
|
238 |
+ /* Got it.*/ |
|
239 |
+ close(handle); |
|
240 |
+ |
|
241 |
+ /* This is the latest version. |
|
242 |
+ If we change the format in the future, we may wish to create a new |
|
243 |
+ mirrors dat struct, import the relevant bits to the new format, |
|
244 |
+ and then save (overwrite) mirrors.dat with the new data. */ |
|
245 |
+ g_mirrorsDat = mdat; |
|
246 |
+ break; |
|
247 |
+ } |
|
248 |
+ default: { |
|
249 |
+ logg("*mirrors.dat version is different than expected: %u != %u\n", 1, g_mirrorsDat->version); |
|
250 |
+ goto done; |
|
251 |
+ } |
|
252 |
+ } |
|
253 |
+ |
|
254 |
+ logg("*Loaded mirrors.dat:\n"); |
|
255 |
+ logg("* version: %d\n", g_mirrorsDat->version); |
|
256 |
+ logg("* uuid: %s\n", g_mirrorsDat->uuid); |
|
257 |
+ if (g_mirrorsDat->retry_after > 0) { |
|
258 |
+ char retry_after_string[26]; |
|
259 |
+ struct tm *tm_info = localtime(&g_mirrorsDat->retry_after); |
|
260 |
+ strftime(retry_after_string, 26, "%Y-%m-%d %H:%M:%S", tm_info); |
|
261 |
+ logg("* retry-after: %s\n", retry_after_string); |
|
262 |
+ } |
|
263 |
+ |
|
264 |
+ status = FC_SUCCESS; |
|
265 |
+ |
|
266 |
+done: |
|
267 |
+ if (-1 != handle) { |
|
268 |
+ close(handle); |
|
269 |
+ } |
|
270 |
+ if (FC_SUCCESS != status) { |
|
271 |
+ free(mdat); |
|
272 |
+ } |
|
273 |
+ |
|
274 |
+ return status; |
|
275 |
+} |
|
276 |
+ |
|
277 |
+fc_error_t save_mirrors_dat(void) |
|
278 |
+{ |
|
279 |
+ fc_error_t status = FC_EINIT; |
|
280 |
+ int handle = -1; |
|
281 |
+ |
|
282 |
+ if (-1 == (handle = safe_open("mirrors.dat", O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644))) { |
|
283 |
+ char currdir[PATH_MAX]; |
|
284 |
+ |
|
285 |
+ if (getcwd(currdir, sizeof(currdir))) |
|
286 |
+ logg("!Can't create mirrors.dat in %s\n", currdir); |
|
287 |
+ else |
|
288 |
+ logg("!Can't create mirrors.dat in the current directory\n"); |
|
289 |
+ |
|
290 |
+ logg("Hint: The database directory must be writable for UID %d or GID %d\n", getuid(), getgid()); |
|
291 |
+ status = FC_EDBDIRACCESS; |
|
292 |
+ goto done; |
|
293 |
+ } |
|
294 |
+ if (-1 == write(handle, MIRRORS_DAT_MAGIC, strlen(MIRRORS_DAT_MAGIC))) { |
|
295 |
+ logg("!Can't write to mirrors.dat\n"); |
|
296 |
+ } |
|
297 |
+ if (-1 == write(handle, g_mirrorsDat, sizeof(mirrors_dat_v1_t))) { |
|
298 |
+ logg("!Can't write to mirrors.dat\n"); |
|
299 |
+ } |
|
300 |
+ |
|
301 |
+ logg("*Saved mirrors.dat\n"); |
|
302 |
+ |
|
303 |
+ status = FC_SUCCESS; |
|
304 |
+done: |
|
305 |
+ if (-1 != handle) { |
|
306 |
+ close(handle); |
|
307 |
+ } |
|
308 |
+ |
|
309 |
+ return status; |
|
310 |
+} |
|
311 |
+ |
|
312 |
+fc_error_t new_mirrors_dat(void) |
|
313 |
+{ |
|
314 |
+ fc_error_t status = FC_EINIT; |
|
315 |
+ |
|
316 |
+ mirrors_dat_v1_t *mdat = malloc(sizeof(mirrors_dat_v1_t)); |
|
317 |
+ if (NULL == mdat) { |
|
318 |
+ logg("!Failed to allocate memory for mirrors.dat\n"); |
|
319 |
+ status = FC_EMEM; |
|
320 |
+ goto done; |
|
321 |
+ } |
|
322 |
+ |
|
323 |
+ mdat->version = 1; |
|
324 |
+ mdat->retry_after = 0; |
|
325 |
+ if (0 == uuid_v4_gen(mdat->uuid)) { |
|
326 |
+ /* Failed to create UUID */ |
|
327 |
+ status = FC_EINIT; |
|
328 |
+ logg("!Failed to create random UUID!\n"); |
|
329 |
+ goto done; |
|
330 |
+ } |
|
331 |
+ |
|
332 |
+ g_mirrorsDat = mdat; |
|
333 |
+ |
|
334 |
+ logg("*Creating new mirrors.dat\n"); |
|
335 |
+ |
|
336 |
+ if (FC_SUCCESS != save_mirrors_dat()) { |
|
337 |
+ logg("!Failed to save mirrors.dat!\n"); |
|
338 |
+ status = FC_EFILE; |
|
339 |
+ goto done; |
|
340 |
+ } |
|
341 |
+ |
|
342 |
+ status = FC_SUCCESS; |
|
343 |
+ |
|
344 |
+done: |
|
345 |
+ if (FC_SUCCESS != status) { |
|
346 |
+ if (NULL != mdat) { |
|
347 |
+ free(mdat); |
|
348 |
+ } |
|
349 |
+ g_mirrorsDat = NULL; |
|
350 |
+ } |
|
351 |
+ return status; |
|
352 |
+} |
|
353 |
+ |
|
118 | 354 |
/** |
119 | 355 |
* @brief Get DNS text record field # for official databases. |
120 | 356 |
* |
... | ... |
@@ -328,12 +565,19 @@ static fc_error_t create_curl_handle( |
328 | 328 |
goto done; |
329 | 329 |
} |
330 | 330 |
|
331 |
- if (g_userAgent) |
|
331 |
+ if (g_userAgent) { |
|
332 | 332 |
strncpy(userAgent, g_userAgent, sizeof(userAgent)); |
333 |
- else |
|
333 |
+ } else { |
|
334 |
+ /* |
|
335 |
+ * Use a randomly generated UUID in the User-Agent |
|
336 |
+ * We'll try to load it from a file in the database directory. |
|
337 |
+ * If none exists, we'll create a new one and save it to said file. |
|
338 |
+ */ |
|
334 | 339 |
snprintf(userAgent, sizeof(userAgent), |
335 |
- PACKAGE "/%s (OS: " TARGET_OS_TYPE ", ARCH: " TARGET_ARCH_TYPE ", CPU: " TARGET_CPU_TYPE ")", |
|
336 |
- get_version()); |
|
340 |
+ PACKAGE "/%s (OS: " TARGET_OS_TYPE ", ARCH: " TARGET_ARCH_TYPE ", CPU: " TARGET_CPU_TYPE ", UUID: %s)", |
|
341 |
+ get_version(), |
|
342 |
+ g_mirrorsDat->uuid); |
|
343 |
+ } |
|
337 | 344 |
userAgent[sizeof(userAgent) - 1] = 0; |
338 | 345 |
|
339 | 346 |
if (mprintf_verbose) { |
... | ... |
@@ -730,6 +974,27 @@ static fc_error_t remote_cvdhead( |
730 | 730 |
status = FC_UPTODATE; |
731 | 731 |
goto done; |
732 | 732 |
} |
733 |
+ case 403: { |
|
734 |
+ status = FC_EFORBIDDEN; |
|
735 |
+ break; |
|
736 |
+ } |
|
737 |
+ case 429: { |
|
738 |
+ status = FC_ERETRYLATER; |
|
739 |
+ curl_off_t retry_after; |
|
740 |
+ |
|
741 |
+ /* Find out how long we should wait before allowing a retry. */ |
|
742 |
+ curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &retry_after); |
|
743 |
+ if (retry_after > 0) { |
|
744 |
+ /* The response gave us a Retry-After date. Use that. */ |
|
745 |
+ g_mirrorsDat->retry_after = time(NULL) + (time_t)retry_after; |
|
746 |
+ } else { |
|
747 |
+ /* Try again in no less than 4 hours if the response didn't specify. */ |
|
748 |
+ g_mirrorsDat->retry_after = time(NULL) + 60 * 60 * 4; |
|
749 |
+ } |
|
750 |
+ (void)save_mirrors_dat(); |
|
751 |
+ |
|
752 |
+ break; |
|
753 |
+ } |
|
733 | 754 |
case 404: { |
734 | 755 |
if (g_proxyServer) |
735 | 756 |
logg("^remote_cvdhead: file not found: %s (Proxy: %s:%u)\n", url, g_proxyServer, g_proxyPort); |
... | ... |
@@ -1004,6 +1269,19 @@ static fc_error_t downloadFile( |
1004 | 1004 |
} |
1005 | 1005 |
case 429: { |
1006 | 1006 |
status = FC_ERETRYLATER; |
1007 |
+ curl_off_t retry_after; |
|
1008 |
+ |
|
1009 |
+ /* Find out how long we should wait before allowing a retry. */ |
|
1010 |
+ curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &retry_after); |
|
1011 |
+ if (retry_after > 0) { |
|
1012 |
+ /* The response gave us a Retry-After date. Use that. */ |
|
1013 |
+ g_mirrorsDat->retry_after = time(NULL) + (time_t)retry_after; |
|
1014 |
+ } else { |
|
1015 |
+ /* Try again in no less than 4 hours if the response didn't specify. */ |
|
1016 |
+ g_mirrorsDat->retry_after = time(NULL) + 60 * 60 * 4; |
|
1017 |
+ } |
|
1018 |
+ (void)save_mirrors_dat(); |
|
1019 |
+ |
|
1007 | 1020 |
break; |
1008 | 1021 |
} |
1009 | 1022 |
case 404: { |
... | ... |
@@ -1792,7 +2070,7 @@ static fc_error_t check_for_new_database_version( |
1792 | 1792 |
database, localver, remotever); |
1793 | 1793 |
break; |
1794 | 1794 |
} |
1795 |
- // else: Fall-through to Up-to-date case. |
|
1795 |
+ /* fall-through */ |
|
1796 | 1796 |
} |
1797 | 1797 |
case FC_UPTODATE: { |
1798 | 1798 |
if (NULL == local_database) { |
... | ... |
@@ -1812,6 +2090,12 @@ static fc_error_t check_for_new_database_version( |
1812 | 1812 |
remotever = localver; |
1813 | 1813 |
break; |
1814 | 1814 |
} |
1815 |
+ case FC_EFORBIDDEN: { |
|
1816 |
+ /* We tried to look up the version using HTTP and were actively blocked. */ |
|
1817 |
+ logg("!check_for_new_database_version: Blocked from using server %s.\n", server); |
|
1818 |
+ status = FC_EFORBIDDEN; |
|
1819 |
+ goto done; |
|
1820 |
+ } |
|
1815 | 1821 |
default: { |
1816 | 1822 |
logg("!check_for_new_database_version: Failed to find %s database using server %s.\n", database, server); |
1817 | 1823 |
status = FC_EFAILEDGET; |
... | ... |
@@ -32,6 +32,14 @@ |
32 | 32 |
#define DNS_EXTRADBINFO_RECORDTIME 1 |
33 | 33 |
// clang-format on |
34 | 34 |
|
35 |
+#define SIZEOF_UUID_V4 37 /** For uuid_v4_gen(), includes NULL byte */ |
|
36 |
+#define MIRRORS_DAT_MAGIC "FreshClamData" /** Magic bytes for mirrors.dat found before mirrors_dat_v1_t */ |
|
37 |
+typedef struct _mirrors_dat_v1 { |
|
38 |
+ uint32_t version; /** version of this dat format */ |
|
39 |
+ char uuid[SIZEOF_UUID_V4]; /** uuid to be used in user-agent */ |
|
40 |
+ time_t retry_after; /** retry date. If > 0, don't update until after this date */ |
|
41 |
+} mirrors_dat_v1_t; |
|
42 |
+ |
|
35 | 43 |
/* ---------------------------------------------------------------------------- |
36 | 44 |
* Internal libfreshclam globals |
37 | 45 |
*/ |
... | ... |
@@ -55,6 +63,12 @@ extern uint32_t g_requestTimeout; |
55 | 55 |
|
56 | 56 |
extern uint32_t g_bCompressLocalDatabase; |
57 | 57 |
|
58 |
+extern mirrors_dat_v1_t *g_mirrorsDat; |
|
59 |
+ |
|
60 |
+fc_error_t load_mirrors_dat(void); |
|
61 |
+fc_error_t save_mirrors_dat(void); |
|
62 |
+fc_error_t new_mirrors_dat(void); |
|
63 |
+ |
|
58 | 64 |
fc_error_t updatedb( |
59 | 65 |
const char *database, |
60 | 66 |
const char *dnsUpdateInfo, |