"Fossies" - the Fresh Open Source Software Archive 
Member "geoipupdate-3.1.1/bin/geoipupdate.c" (10 Sep 2018, 30740 Bytes) of package /linux/misc/geoipupdate-3.1.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "geoipupdate.c" see the
Fossies "Dox" file reference documentation.
1 #include "geoipupdate.h"
2 #include "functions.h"
3 #include "md5.h"
4
5 #include <ctype.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <getopt.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <sys/types.h>
15 #include <unistd.h>
16 #include <utime.h>
17 #include <zlib.h>
18
19 #define OLD_FREE_ACCOUNT_ID (999999)
20 #define ZERO_LICENSE_KEY ("000000000000")
21 #define ZERO_MD5 ("00000000000000000000000000000000")
22 #define say(fmt, ...) say_if(1, fmt, ##__VA_ARGS__)
23
24 enum gu_status {
25 GU_OK = 0,
26 GU_ERROR = 1,
27 GU_NO_UPDATE = 2,
28 };
29
30 typedef struct {
31 char *ptr;
32 size_t size;
33 } in_mem_s;
34
35 static void xasprintf(char **, const char *, ...);
36 static void *xrealloc(void *, size_t);
37 static void usage(void);
38 static int parse_opts(geoipupdate_s *, int, char *const[]);
39 static ssize_t my_getline(char **, size_t *, FILE *);
40 static int parse_license_file(geoipupdate_s *);
41 static char *join_path(char const *const, char const *const);
42 static int acquire_run_lock(geoipupdate_s const *const);
43 static int md5hex(const char *, char *);
44 static void common_req(CURL *, geoipupdate_s *);
45 static size_t get_expected_file_md5(char *, size_t, size_t, void *);
46 static int
47 download_to_file(geoipupdate_s *, const char *, const char *, char *);
48 static long get_server_time(geoipupdate_s *);
49 static size_t mem_cb(void *, size_t, size_t, void *);
50 static in_mem_s *in_mem_s_new(void);
51 static void in_mem_s_delete(in_mem_s *);
52 static int update_database_general_all(geoipupdate_s *);
53 static int update_database_general(geoipupdate_s *, const char *);
54 static int gunzip_and_replace(geoipupdate_s const *const,
55 char const *const,
56 char const *const,
57 char const *const,
58 long);
59
60 void exit_unless(int expr, const char *fmt, ...) {
61 va_list ap;
62 if (expr) {
63 return;
64 }
65 va_start(ap, fmt);
66 vfprintf(stderr, fmt, ap);
67 va_end(ap);
68 exit(1);
69 }
70
71 static void xasprintf(char **ptr, const char *fmt, ...) {
72 va_list ap;
73 va_start(ap, fmt);
74 int rc = vasprintf(ptr, fmt, ap);
75 va_end(ap);
76 exit_if(rc == -1, "Error calling vasprintf: %s\n", strerror(errno));
77 }
78
79 void say_if(int expr, const char *fmt, ...) {
80 va_list ap;
81 if (!expr) {
82 return;
83 }
84 va_start(ap, fmt);
85 vfprintf(stdout, fmt, ap);
86 va_end(ap);
87 }
88
89 void *xcalloc(size_t nmemb, size_t size) {
90 void *ptr = calloc(nmemb, size);
91 exit_if(!ptr, "Error allocating memory: %s\n", strerror(errno));
92 return ptr;
93 }
94
95 static void *xrealloc(void *ptr, size_t size) {
96 void *mem = realloc(ptr, size);
97 exit_if(mem == NULL, "Error reallocating memory: %s\n", strerror(errno));
98 return mem;
99 }
100
101 static void usage(void) {
102 fprintf(
103 stderr,
104 "Usage: geoipupdate [-Vhv] [-f license_file] [-d custom directory]\n\n"
105 " -d DIR store downloaded files in DIR\n"
106 " -f FILE use configuration found in FILE (see GeoIP.conf(5) man "
107 "page)\n"
108 " -h display this help text\n"
109 " -v use verbose output\n"
110 " -V display the version and exit\n");
111 }
112
113 static int parse_opts(geoipupdate_s *gu, int argc, char *const argv[]) {
114 int c;
115
116 opterr = 0;
117
118 while ((c = getopt(argc, argv, "Vvhf:d:")) != -1) {
119 switch (c) {
120 case 'V':
121 puts(PACKAGE_STRING);
122 exit(0);
123 case 'v':
124 gu->verbose = 1;
125 break;
126 case 'd':
127 free(gu->database_dir);
128 gu->database_dir = strdup(optarg);
129 exit_if(NULL == gu->database_dir,
130 "Unable to allocate memory for database directory "
131 "path: %s\n",
132 strerror(errno));
133
134 // The database directory in the config file is ignored if we
135 // use -d
136 gu->do_not_overwrite_database_directory = 1;
137 break;
138 case 'f':
139 free(gu->license_file);
140 gu->license_file = strdup(optarg);
141 exit_if(NULL == gu->license_file,
142 "Unable to allocate memory for license file path: %s\n",
143 strerror(errno));
144 break;
145 case 'h':
146 default:
147 usage();
148 exit(1);
149 case '?':
150 if (optopt == 'f' || optopt == 'd') {
151 fprintf(
152 stderr, "Option -%c requires an argument.\n", optopt);
153 } else if (isprint(optopt)) {
154 fprintf(stderr, "Unknown option `-%c'.\n", optopt);
155 } else {
156 fprintf(
157 stderr, "Unknown option character `\\x%x'.\n", optopt);
158 }
159 exit(1);
160 }
161 }
162 return GU_OK;
163 }
164
165 int main(int argc, char *const argv[]) {
166 struct stat st;
167 int err = GU_ERROR;
168 curl_global_init(CURL_GLOBAL_DEFAULT);
169 geoipupdate_s *gu = geoipupdate_s_new();
170 if (gu) {
171 parse_opts(gu, argc, argv);
172 if (parse_license_file(gu)) {
173 exit_unless(stat(gu->database_dir, &st) == 0,
174 "%s does not exist: %s\n",
175 gu->database_dir,
176 strerror(errno));
177 exit_unless(S_ISDIR(st.st_mode),
178 "%s is not a directory\n",
179 gu->database_dir);
180 // Note: access(2) checks only the real UID/GID. This is probably
181 // okay, but we could perform more complex checks using the stat
182 // struct. Alternatively, simply report more thoroughly when we
183 // open the file, and avoid potential race issues where permissions
184 // change between now and then.
185 exit_unless(access(gu->database_dir, W_OK) == 0,
186 "%s is not writable: %s\n",
187 gu->database_dir,
188 strerror(errno));
189
190 if (acquire_run_lock(gu) != 0) {
191 geoipupdate_s_delete(gu);
192 curl_global_cleanup();
193 return GU_ERROR;
194 }
195
196 err = update_database_general_all(gu);
197 }
198 geoipupdate_s_delete(gu);
199 }
200 curl_global_cleanup();
201 return err & GU_ERROR ? GU_ERROR : GU_OK;
202 }
203
204 static ssize_t my_getline(char **linep, size_t *linecapp, FILE *stream) {
205 #if defined HAVE_GETLINE
206 return getline(linep, linecapp, stream);
207 #elif defined HAVE_FGETS
208 // Unbelievable, but OS X 10.6 Snow Leopard did not provide getline
209 char *p = fgets(*linep, *linecapp, stream);
210 return p == NULL ? -1 : strlen(p);
211 #else
212 #error Your OS is not supported
213 #endif
214 }
215
216 static int parse_license_file(geoipupdate_s *up) {
217 say_if(up->verbose, "%s\n", PACKAGE_STRING);
218 FILE *fh = fopen(up->license_file, "rb");
219 exit_unless(!!fh,
220 "Can't open license file %s: %s\n",
221 up->license_file,
222 strerror(errno));
223 say_if(up->verbose, "Opened License file %s\n", up->license_file);
224
225 const char *sep = " \t\r\n";
226 size_t bsize = 1024;
227 char *buffer = (char *)xcalloc(bsize, sizeof(char));
228 ssize_t read_bytes;
229 while ((read_bytes = my_getline(&buffer, &bsize, fh)) != -1) {
230 size_t idx = strspn(buffer, sep);
231 char *strt = &buffer[idx];
232 if (*strt == '#') {
233 continue;
234 }
235 if (sscanf(strt, "UserId %d", &up->license.account_id) == 1) {
236 say_if(up->verbose, "UserId %d\n", up->license.account_id);
237 continue;
238 }
239 if (sscanf(strt, "AccountID %d", &up->license.account_id) == 1) {
240 say_if(up->verbose, "AccountID %d\n", up->license.account_id);
241 continue;
242 }
243 if (sscanf(strt, "LicenseKey %99s", &up->license.license_key[0]) == 1) {
244 say_if(
245 up->verbose, "LicenseKey %.4s...\n", up->license.license_key);
246 continue;
247 }
248
249 char *p, *last;
250 if ((p = strtok_r(strt, sep, &last))) {
251 if (!strcmp(p, "ProductIds") || !strcmp(p, "EditionIDs")) {
252 while ((p = strtok_r(NULL, sep, &last))) {
253 edition_insert_once(up, p);
254 }
255 } else if (!strcmp(p, "PreserveFileTimes")) {
256 p = strtok_r(NULL, sep, &last);
257 exit_if(NULL == p ||
258 (0 != strcmp(p, "0") && 0 != strcmp(p, "1")),
259 "PreserveFileTimes must be 0 or 1\n");
260 up->preserve_file_times = atoi(p);
261 } else if (!strcmp(p, "Host")) {
262 p = strtok_r(NULL, sep, &last);
263 exit_if(NULL == p, "Host must be defined\n");
264 free(up->host);
265 up->host = strdup(p);
266 exit_if(NULL == up->host,
267 "Unable to allocate memory for update host: %s\n",
268 strerror(errno));
269 } else if (!strcmp(p, "DatabaseDirectory")) {
270 if (!up->do_not_overwrite_database_directory) {
271 p = strtok_r(NULL, sep, &last);
272 exit_if(NULL == p, "DatabaseDirectory must be defined\n");
273 free(up->database_dir);
274 up->database_dir = strdup(p);
275 exit_if(NULL == up->database_dir,
276 "Unable to allocate memory for database directory "
277 "path: %s\n",
278 strerror(errno));
279 }
280 } else if (!strcmp(p, "Proxy")) {
281 p = strtok_r(NULL, sep, &last);
282 exit_if(NULL == p, "Proxy must be defined 1.2.3.4:12345\n");
283 free(up->proxy);
284 up->proxy = strdup(p);
285 exit_if(NULL == up->proxy,
286 "Unable to allocate memory for proxy host: %s\n",
287 strerror(errno));
288 } else if (!strcmp(p, "ProxyUserPassword")) {
289 p = strtok_r(NULL, sep, &last);
290 exit_if(NULL == p,
291 "ProxyUserPassword must be defined xyz:abc\n");
292 free(up->proxy_user_password);
293 up->proxy_user_password = strdup(p);
294 exit_if(NULL == up->proxy_user_password,
295 "Unable to allocate memory for proxy credentials: %s\n",
296 strerror(errno));
297 } else if (!strcmp(p, "LockFile")) {
298 p = strtok_r(NULL, sep, &last);
299 exit_if(NULL == p, "LockFile must be a file path\n");
300 // We could check the value looks like a path, but trying to use
301 // it will fail if it isn't.
302 free(up->lock_file);
303 up->lock_file = strdup(p);
304 exit_if(NULL == up->lock_file,
305 "Unable to allocate memory for LockFile string: %s\n",
306 strerror(errno));
307 } else {
308 say_if(up->verbose, "Skip unknown directive: %s\n", p);
309 }
310 }
311 }
312
313 bool is_zero_license_key = !strncmp(ZERO_LICENSE_KEY,
314 up->license.license_key,
315 sizeof(ZERO_LICENSE_KEY) - 1);
316
317 // We used to recommend using 999999 / 000000000000 for free downloads and
318 // many people still use this combination. We need to check for the
319 // ZERO_LICENSE_KEY to ensure that a real AccountID of 999999 will work in
320 // the future.
321 if (up->license.account_id == OLD_FREE_ACCOUNT_ID && is_zero_license_key) {
322 up->license.account_id = NO_ACCOUNT_ID;
323 }
324
325 exit_if(up->license.account_id == NO_ACCOUNT_ID &&
326 up->license.license_key[0] != 0 && !is_zero_license_key,
327 "AccountID must be set if LicenseKey is set\n");
328
329 // If we don't have a LockFile specified, then default to .geoipupdate.lock
330 // in the database directory. Do this here as the database directory may
331 // have been set either on the command line or in the configuration file.
332 if (strlen(up->lock_file) == 0) {
333 free(up->lock_file);
334 up->lock_file = join_path(up->database_dir, ".geoipupdate.lock");
335 exit_if(NULL == up->lock_file, "Unable to create path to lock file.");
336 }
337
338 free(buffer);
339 exit_if(-1 == fclose(fh), "Error closing stream: %s", strerror(errno));
340 say_if(up->verbose,
341 "Read in license key %s\nNumber of edition IDs %d\n",
342 up->license_file,
343 edition_count(up));
344 return 1;
345 }
346
347 // Given a directory and a filename in that directory, combine the two to make a
348 // path to the file.
349 //
350 // TODO: This function assumes Unix style paths (/ separator).
351 //
352 // TODO: This function performs no validation on the given inputs beyond that
353 // they are present.
354 static char *join_path(char const *const dir, char const *const file) {
355 size_t sz = -1;
356 char *path = NULL;
357
358 if (dir == NULL || strlen(dir) == 0 || file == NULL || strlen(file) == 0) {
359 fprintf(stderr, "join_path: %s\n", strerror(EINVAL));
360 return NULL;
361 }
362
363 // dir '/' file '\0'
364 sz = strlen(dir) + 1 + strlen(file) + 1;
365
366 path = calloc(sz, sizeof(char));
367 if (path == NULL) {
368 fprintf(stderr, "join_path: %s\n", strerror(errno));
369 return NULL;
370 }
371
372 strcat(path, dir);
373 strcat(path, "/");
374 strcat(path, file);
375
376 return path;
377 }
378
379 // Acquire a lock to ensure this is the only running geoipupdate instance. This
380 // is to avoid race conditions where multiple geoipupdate instances run at
381 // once, leading to failures.
382 //
383 // Wait for a lock. If locking fails, return non-zero. If it succeeds, return
384 // zero.
385 //
386 // Use fcntl(2) to acquire the lock. The primary rationale to use this over
387 // something like open(2) with O_EXCL is that we don't need to perform clean up
388 // to release the lock. In particular, if execution ends unexpectedly, such as
389 // due to a crash, the lock will be automatically released. It also means we
390 // don't need to worry about lock bookkeeping even in the normal case, since
391 // the lock gets released automatically at program exit.
392 //
393 // This method does have the drawback that removing the lock file is not
394 // possible due to the potential for race conditions. Consider the case where
395 // another instance opens the lock file, then we remove the file and close the
396 // file (releasing our lock), then that other instance acquires a lock. At the
397 // same time, another instance runs and creates the file anew and also acquires
398 // a lock.
399 static int acquire_run_lock(geoipupdate_s const *const gu) {
400 int fd = -1;
401 struct flock fl;
402 int i = 0;
403
404 memset(&fl, 0, sizeof(struct flock));
405
406 if (gu == NULL || gu->lock_file == NULL || strlen(gu->lock_file) == 0) {
407 fprintf(stderr, "maybe_acquire_run_lock: %s\n", strerror(EINVAL));
408 return 1;
409 }
410
411 fd = open(gu->lock_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
412 if (fd == -1) {
413 fprintf(stderr,
414 "Unable to open lock file %s: %s\n",
415 gu->lock_file,
416 strerror(errno));
417 return 1;
418 }
419
420 fl.l_type = F_WRLCK;
421
422 // Try 3 times to acquire a lock. Arbitrary number.
423 for (i = 0; i < 3; i++) {
424 if (fcntl(fd, F_SETLKW, &fl) == 0) {
425 // Locked.
426 return 0;
427 }
428
429 // Interrupted? Retry.
430 if (errno == EINTR) {
431 continue;
432 }
433
434 // Something else went wrong. Abort.
435 fprintf(stderr,
436 "Unable to acquire lock on %s: %s\n",
437 gu->lock_file,
438 strerror(errno));
439 close(fd);
440 return 1;
441 }
442
443 fprintf(stderr,
444 "Unable to acquire lock on %s: Gave up after %d attempts\n",
445 gu->lock_file,
446 i);
447 close(fd);
448 return 1;
449 }
450
451 static int md5hex(const char *fname, char *hex_digest) {
452 int bsize = 1024;
453 unsigned char buffer[bsize], digest[16];
454
455 size_t len;
456 MD5_CONTEXT context;
457
458 FILE *fh = fopen(fname, "rb");
459 if (fh == NULL) {
460 strcpy(hex_digest, ZERO_MD5);
461 return 0;
462 }
463
464 struct stat st;
465 exit_unless(stat(fname, &st) == 0,
466 "Unable to stat %s: %s\n",
467 fname,
468 strerror(errno));
469 exit_unless(S_ISREG(st.st_mode), "%s is not a file\n", fname);
470
471 md5_init(&context);
472 while ((len = fread(buffer, 1, bsize, fh)) > 0) {
473 md5_write(&context, buffer, len);
474 }
475 exit_if(ferror(fh), "Unable to read %s: %s\n", fname, strerror(errno));
476
477 md5_final(&context);
478 memcpy(digest, context.buf, 16);
479 exit_if(-1 == fclose(fh), "Error closing stream: %s", strerror(errno));
480 for (int i = 0; i < 16; i++) {
481 int c = snprintf(&hex_digest[2 * i], 3, "%02x", digest[i]);
482 exit_if(c < 0, "Unable to write digest: %s\n", strerror(errno));
483 }
484 return 1;
485 }
486
487 static void common_req(CURL *curl, geoipupdate_s *gu) {
488 curl_easy_setopt(curl, CURLOPT_USERAGENT, GEOIP_USERAGENT);
489 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
490
491 // CURLOPT_TCP_KEEPALIVE appeared in 7.25. It is a typedef enum, not a
492 // macro so we resort to version detection.
493 #if LIBCURL_VERSION_NUM >= 0x071900
494 curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);
495 #endif
496
497 // These should be the default already, but setting them to ensure
498 // they are set correctly on all curl versions.
499 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
500 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
501
502 if (gu->preserve_file_times) {
503 curl_easy_setopt(curl, CURLOPT_FILETIME, 1L);
504 }
505
506 if (gu->proxy_user_password && strlen(gu->proxy_user_password)) {
507 say_if(gu->verbose,
508 "Use proxy_user_password: %s\n",
509 gu->proxy_user_password);
510 curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, gu->proxy_user_password);
511 }
512 if (gu->proxy && strlen(gu->proxy)) {
513 say_if(gu->verbose, "Use proxy: %s\n", gu->proxy);
514 curl_easy_setopt(curl, CURLOPT_PROXY, gu->proxy);
515 }
516 }
517
518 static size_t get_expected_file_md5(char *buffer,
519 size_t size,
520 size_t nitems,
521 void *userdata) {
522 char *md5 = (char *)userdata;
523 size_t total_size = size * nitems;
524 if (strncasecmp(buffer, "X-Database-MD5:", 15) == 0 && total_size > 48) {
525 char *start = buffer + 16;
526 char *value = start + strspn(start, " \t\r\n");
527 strncpy(md5, value, 32);
528 md5[32] = '\0';
529 }
530
531 return size * nitems;
532 }
533
534 // Make an HTTP request and download the response body to a file.
535 //
536 // If the HTTP status is 200, we have a file. If it is 304, the file has
537 // not changed and we display an error message. If it is 401, there was
538 // an authentication issue and we display an error message. If it is
539 // any other status code, we assume it is an error and write the body
540 // to stderr.
541 static int download_to_file(geoipupdate_s *gu,
542 const char *url,
543 const char *fname,
544 char *expected_file_md5) {
545 FILE *f = fopen(fname, "wb");
546 if (f == NULL) {
547 fprintf(stderr, "Can't open %s: %s\n", fname, strerror(errno));
548 exit(1);
549 }
550
551 say_if(gu->verbose, "url: %s\n", url);
552 CURL *curl = gu->curl;
553
554 expected_file_md5[0] = '\0';
555
556 // If the account ID is not set, the user is likely trying to do a free
557 // download, e.g., GeoLite2. We don't need to send the basic auth header
558 // for these.
559 if (gu->license.account_id != NO_ACCOUNT_ID) {
560 char account_id[10] = {0};
561 int n = snprintf(account_id, 10, "%d", gu->license.account_id);
562 exit_if(n < 0,
563 "Error creating account ID string for %d: %s\n",
564 gu->license.account_id,
565 strerror(errno));
566 exit_if(n < 0 || n >= 10,
567 "An unexpectedly large account ID was encountered: %d\n",
568 gu->license.account_id);
569
570 curl_easy_setopt(curl, CURLOPT_USERNAME, account_id);
571 curl_easy_setopt(curl, CURLOPT_PASSWORD, gu->license.license_key);
572 }
573
574 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, get_expected_file_md5);
575 curl_easy_setopt(curl, CURLOPT_HEADERDATA, expected_file_md5);
576
577 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
578 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)f);
579
580 curl_easy_setopt(curl, CURLOPT_URL, url);
581 common_req(curl, gu);
582 CURLcode res = curl_easy_perform(curl);
583
584 exit_unless(res == CURLE_OK,
585 "curl_easy_perform() failed: %s\nConnect to %s\n",
586 curl_easy_strerror(res),
587 url);
588
589 long status = 0;
590 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
591
592 if (fclose(f) == -1) {
593 fprintf(stderr, "Error closing file: %s: %s\n", fname, strerror(errno));
594 unlink(fname);
595 exit(1);
596 }
597
598 if (status == 304) {
599 say_if(gu->verbose, "No new updates available\n");
600 unlink(fname);
601 return GU_NO_UPDATE;
602 }
603
604 if (status == 401) {
605 fprintf(stderr, "Your account ID or license key is invalid\n");
606 unlink(fname);
607 return GU_ERROR;
608 }
609
610 if (status != 200) {
611 fprintf(stderr,
612 "Received an unexpected HTTP status code of %ld from %s:\n",
613 status,
614 url);
615 // The response should contain a message containing exactly why.
616 char *const message = slurp_file(fname);
617 if (message) {
618 fprintf(stderr, "%s\n", message);
619 free(message);
620 }
621 unlink(fname);
622 return GU_ERROR;
623 }
624
625 // We have HTTP 2xx.
626
627 // In this case, the server must have told us the current MD5 hash of the
628 // database we asked for.
629 if (gu_strnlen(expected_file_md5, 33) != 32) {
630 fprintf(stderr,
631 "Did not receive a valid expected database MD5 from server\n");
632 unlink(fname);
633 return GU_ERROR;
634 }
635 return GU_OK;
636 }
637
638 // Retrieve the server file time for the previous HTTP request.
639 static long get_server_time(geoipupdate_s *gu) {
640 CURL *curl = gu->curl;
641 long filetime = -1;
642 if (curl != NULL) {
643 curl_easy_getinfo(curl, CURLINFO_FILETIME, &filetime);
644 }
645 return filetime;
646 }
647
648 static size_t mem_cb(void *contents, size_t size, size_t nmemb, void *userp) {
649 size_t realsize = size * nmemb;
650
651 if (realsize == 0) {
652 return realsize;
653 }
654
655 in_mem_s *mem = (in_mem_s *)userp;
656
657 mem->ptr = (char *)xrealloc(mem->ptr, mem->size + realsize + 1);
658 memcpy(&(mem->ptr[mem->size]), contents, realsize);
659 mem->size += realsize;
660 mem->ptr[mem->size] = 0;
661
662 return realsize;
663 }
664
665 static in_mem_s *in_mem_s_new(void) {
666 in_mem_s *mem = (in_mem_s *)xcalloc(1, sizeof(in_mem_s));
667 mem->ptr = (char *)xcalloc(1, sizeof(char));
668 mem->size = 0;
669 return mem;
670 }
671
672 static void in_mem_s_delete(in_mem_s *mem) {
673 if (mem) {
674 free(mem->ptr);
675 free(mem);
676 }
677 }
678
679 static int update_database_general(geoipupdate_s *gu, const char *edition_id) {
680 char *url = NULL, *geoip_filename = NULL, *geoip_gz_filename = NULL;
681 char hex_digest[33] = {0};
682
683 // Get the filename.
684 xasprintf(&url,
685 "https://%s/app/update_getfilename?product_id=%s",
686 gu->host,
687 edition_id);
688
689 in_mem_s *mem = in_mem_s_new();
690
691 say_if(gu->verbose, "url: %s\n", url);
692 CURL *curl = gu->curl;
693 curl_easy_setopt(curl, CURLOPT_URL, url);
694 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, mem_cb);
695 curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)mem);
696 common_req(curl, gu);
697 CURLcode res = curl_easy_perform(curl);
698 exit_unless(res == CURLE_OK,
699 "curl_easy_perform() failed: %s\nConnect to %s\n",
700 curl_easy_strerror(res),
701 url);
702
703 long status = 0;
704 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status);
705
706 if (status != 200) {
707 fprintf(stderr,
708 "Received an unexpected HTTP status code of %ld from %s\n",
709 status,
710 url);
711 free(url);
712 in_mem_s_delete(mem);
713 return GU_ERROR;
714 }
715
716 free(url);
717 if (mem->size == 0) {
718 fprintf(stderr, "edition_id %s not found\n", edition_id);
719 in_mem_s_delete(mem);
720 return GU_ERROR;
721 }
722 xasprintf(&geoip_filename, "%s/%s", gu->database_dir, mem->ptr);
723 in_mem_s_delete(mem);
724
725 // Calculate the MD5 hash of the database we currently have, if any. We get
726 // back a zero MD5 hash if we don't have it yet.
727 md5hex(geoip_filename, hex_digest);
728 say_if(gu->verbose, "md5hex_digest: %s\n", hex_digest);
729
730 // Download.
731 xasprintf(&url,
732 "https://%s/geoip/databases/%s/update?db_md5=%s",
733 gu->host,
734 edition_id,
735 hex_digest);
736 xasprintf(&geoip_gz_filename, "%s.gz", geoip_filename);
737
738 char expected_file_md5[33] = {0};
739 int rc = download_to_file(gu, url, geoip_gz_filename, expected_file_md5);
740 free(url);
741
742 if (rc == GU_OK) {
743 long filetime = -1;
744 if (gu->preserve_file_times) {
745 filetime = get_server_time(gu);
746 }
747 rc = gunzip_and_replace(
748 gu, geoip_gz_filename, geoip_filename, expected_file_md5, filetime);
749 }
750
751 free(geoip_gz_filename);
752 free(geoip_filename);
753
754 return rc;
755 }
756
757 static int update_database_general_all(geoipupdate_s *gu) {
758 int err = 0;
759 for (edition_s **next = &gu->license.first; *next; next = &(*next)->next) {
760 err |= update_database_general(gu, (*next)->edition_id);
761 }
762 return err;
763 }
764
765 // Decompress the compressed database and move it into place in the database
766 // directory.
767 //
768 // We are given the path to the compressed (gzip'd) new database, and the path
769 // to where it should end up once decompressed. We are also given the MD5 hash
770 // it should have once decompressed for verification purposes.
771 //
772 // We verify the file is actually a gzip file. If it isn't we abort with an
773 // error, and remove the file.
774 //
775 // We also remove the gzip file once we successfully decompress and move the
776 // new database into place.
777 static int gunzip_and_replace(geoipupdate_s const *const gu,
778 char const *const gzipfile,
779 char const *const geoip_filename,
780 char const *const expected_file_md5,
781 long filetime) {
782 if (gu == NULL || gu->database_dir == NULL ||
783 strlen(gu->database_dir) == 0 || gzipfile == NULL ||
784 strlen(gzipfile) == 0 || geoip_filename == NULL ||
785 strlen(geoip_filename) == 0 || expected_file_md5 == NULL ||
786 strlen(expected_file_md5) == 0) {
787 fprintf(stderr, "gunzip_and_replace: %s\n", strerror(EINVAL));
788 return GU_ERROR;
789 }
790
791 if (!is_valid_gzip_file(gzipfile)) {
792 // We should have already reported an error.
793 unlink(gzipfile);
794 return GU_ERROR;
795 }
796
797 // Decompress to the filename with the suffix ".test".
798 char *file_path_test = NULL;
799 xasprintf(&file_path_test, "%s.test", geoip_filename);
800 say_if(gu->verbose, "Uncompress file %s to %s\n", gzipfile, file_path_test);
801
802 gzFile gz_fh = gzopen(gzipfile, "rb");
803 exit_if(gz_fh == NULL, "Can't open %s: %s\n", gzipfile, strerror(errno));
804
805 FILE *fhw = fopen(file_path_test, "wb");
806 exit_if(
807 fhw == NULL, "Can't open %s: %s\n", file_path_test, strerror(errno));
808
809 size_t const bsize = 8192;
810 char *const buffer = calloc(bsize, sizeof(char));
811 if (!buffer) {
812 fprintf(stderr, "gunzip_and_replace: %s\n", strerror(errno));
813 free(file_path_test);
814 gzclose(gz_fh);
815 fclose(fhw);
816 return GU_ERROR;
817 }
818
819 for (;;) {
820 int amt = gzread(gz_fh, buffer, bsize);
821 if (amt <= 0) {
822 if (gzeof(gz_fh)) {
823 // EOF
824 break;
825 }
826 int gzerr = 0;
827 const char *msg = gzerror(gz_fh, &gzerr);
828 if (gzerr == Z_ERRNO) {
829 fprintf(stderr,
830 "Unable to read %s: %s\n",
831 gzipfile,
832 strerror(errno));
833 } else {
834 fprintf(stderr, "Unable to decompress %s: %s\n", gzipfile, msg);
835 }
836 exit(1);
837 }
838 exit_unless(fwrite(buffer, 1, amt, fhw) == (size_t)amt,
839 "Unable to write to %s: %s\n",
840 file_path_test,
841 strerror(errno));
842 }
843 exit_if(-1 == fclose(fhw), "Error closing stream: %s\n", strerror(errno));
844 if (gzclose(gz_fh) != Z_OK) {
845 int gzerr = 0;
846 const char *msg = gzerror(gz_fh, &gzerr);
847 if (gzerr == Z_ERRNO) {
848 msg = strerror(errno);
849 }
850 fprintf(stderr, "Unable to close %s: %s\n", gzipfile, msg);
851 exit(1);
852 }
853 free(buffer);
854
855 char actual_md5[33] = {0};
856 md5hex(file_path_test, actual_md5);
857 exit_if(strncasecmp(actual_md5, expected_file_md5, 32),
858 "MD5 of new database (%s) does not match expected MD5 (%s)\n",
859 actual_md5,
860 expected_file_md5);
861
862 say_if(gu->verbose, "Rename %s to %s\n", file_path_test, geoip_filename);
863 int err = rename(file_path_test, geoip_filename);
864 exit_if(err,
865 "Rename %s to %s failed: %s\n",
866 file_path_test,
867 geoip_filename,
868 strerror(errno));
869
870 if (gu->preserve_file_times && filetime > 0) {
871 struct utimbuf utb;
872 utb.modtime = utb.actime = (time_t)filetime;
873 err = utime(geoip_filename, &utb);
874 exit_if(err,
875 "Setting timestamp of %s to %ld failed: %s\n",
876 geoip_filename,
877 filetime,
878 strerror(errno));
879 }
880
881 // fsync directory to ensure the rename is durable
882 int dirfd = open(gu->database_dir, O_DIRECTORY);
883 exit_if(
884 -1 == dirfd, "Error opening database directory: %s\n", strerror(errno));
885 exit_if(-1 == fsync(dirfd),
886 "Error syncing database directory: %s\n",
887 strerror(errno));
888 exit_if(-1 == close(dirfd),
889 "Error closing database directory: %s\n",
890 strerror(errno));
891 exit_if(-1 == unlink(gzipfile),
892 "Error unlinking %s: %s\n",
893 gzipfile,
894 strerror(errno));
895
896 free(file_path_test);
897 return GU_OK;
898 }