void download_to_file(geoipupdate_s * gu, const char *url, const char *fname, char *expected_file_md5) { FILE *f = fopen(fname, "wb"); exit_unless(f != NULL, "Can't open %s\n", fname); say_if(gu->verbose, "url: %s\n", url); CURL *curl = gu->curl; expected_file_md5[0] = '\0'; curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, get_expected_file_md5); curl_easy_setopt(curl, CURLOPT_HEADERDATA, expected_file_md5); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)f); curl_easy_setopt(curl, CURLOPT_URL, url); common_req(curl, gu); CURLcode res = curl_easy_perform(curl); exit_unless(res == CURLE_OK, "curl_easy_perform() failed: %s\nConnect to %s\n", curl_easy_strerror(res), url); long status = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); exit_unless( status >= 200 && status < 300, "Received an unexpected HTTP status code of %ld from %s\n", status, url); fclose(f); }
int md5hex(const char *fname, char *hex_digest) { int bsize = 1024; unsigned char buffer[bsize], digest[16]; size_t len; MD5_CONTEXT context; FILE *fh = fopen(fname, "rb"); if (fh == NULL) { strcpy(hex_digest, ZERO_MD5); return 0; } struct stat st; exit_unless(stat(fname, &st) == 0 && S_ISREG(st.st_mode), "%s is not a file\n", fname); md5_init(&context); while ((len = fread(buffer, 1, bsize, fh)) > 0) { md5_write(&context, buffer, len); } md5_final(&context); memcpy(digest, context.buf, 16); exit_if(-1 == fclose(fh), "Error closing stream: %s", strerror(errno)); for (int i = 0; i < 16; i++) { snprintf(&hex_digest[2 * i], 3, "%02x", digest[i]); } return 1; }
int main(int argc, char *const argv[]) { struct stat st; int err = ERROR; curl_global_init(CURL_GLOBAL_DEFAULT); geoipupdate_s *gu = geoipupdate_s_new(); if (gu) { parse_opts(gu, argc, argv); if (parse_license_file(gu)) { exit_unless(stat(gu->database_dir, &st) == 0, "%s does not exist\n", gu->database_dir); exit_unless(S_ISDIR(st.st_mode), "%s is not a directory\n", gu->database_dir); err = (gu->license.user_id == NO_USER_ID) ? update_country_database(gu) : update_database_general_all(gu); } geoipupdate_s_delete(gu); } curl_global_cleanup(); return err ? ERROR : OK; }
static in_mem_s *get(geoipupdate_s * gu, const char *url) { in_mem_s *mem = in_mem_s_new(); say_if(gu->verbose, "url: %s\n", url); CURL *curl = gu->curl; curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, mem_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)mem); common_req(curl, gu); CURLcode res = curl_easy_perform(curl); exit_unless(res == CURLE_OK, "curl_easy_perform() failed: %s\nConnect to %s\n", curl_easy_strerror(res), url); long status = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); exit_unless( status >= 200 && status < 300, "Received an unexpected HTTP status code of %ld from %s", status, url); return mem; }
static int gunzip_and_replace(geoipupdate_s * gu, const char *gzipfile, const char *geoip_filename, const char *expected_file_md5) { gzFile gz_fh; FILE *fh = fopen(gzipfile, "rb"); exit_if(NULL == fh, "Can't open %s\n", gzipfile); size_t bsize = 8096; char *buffer = (char *)xmalloc(bsize); ssize_t read_bytes = my_getline(&buffer, &bsize, fh); exit_if(-1 == fclose(fh), "Error closing stream: %s", strerror(errno)); if (read_bytes < 0) { fprintf(stderr, "Read error %s\n", gzipfile); unlink(gzipfile); free(buffer); return ERROR; } const char *no_new_upd = "No new updates available"; if (!strncmp(no_new_upd, buffer, strlen(no_new_upd))) { say_if(gu->verbose, "%s\n", no_new_upd); unlink(gzipfile); free(buffer); return OK; } if (strncmp(buffer, "\x1f\x8b", 2)) { // error not a zip file unlink(gzipfile); fputs(buffer, stderr); return ERROR; } // We do this here as we have to check that there is an update before // we check for the header. exit_unless( 32 == strnlen(expected_file_md5, 33), "Did not receive a valid expected database MD5 from server\n"); char *file_path_test; xasprintf(&file_path_test, "%s.test", geoip_filename); say_if(gu->verbose, "Uncompress file %s to %s\n", gzipfile, file_path_test); gz_fh = gzopen(gzipfile, "rb"); exit_if(gz_fh == NULL, "Can't open %s\n", gzipfile); FILE *fhw = fopen(file_path_test, "wb"); exit_if(fhw == NULL, "Can't open %s\n", file_path_test); for (;; ) { int amt = gzread(gz_fh, buffer, bsize); if (amt == 0) { break; // EOF } exit_if(amt == -1, "Gzip read error while reading from %s\n", gzipfile); exit_unless(fwrite(buffer, 1, amt, fhw) == (size_t)amt, "Gzip write error\n"); } exit_if(-1 == fclose(fhw), "Error closing stream: %s", strerror(errno)); exit_if(gzclose(gz_fh) != Z_OK, "Gzip read error while closing from %s\n", gzipfile); free(buffer); char actual_md5[33]; md5hex(file_path_test, actual_md5); exit_if(strncasecmp(actual_md5, expected_file_md5, 32), "MD5 of new database (%s) does not match expected MD5 (%s)", actual_md5, expected_file_md5); say_if(gu->verbose, "Rename %s to %s\n", file_path_test, geoip_filename); int err = rename(file_path_test, geoip_filename); exit_if(err, "Rename %s to %s failed\n", file_path_test, geoip_filename); // fsync directory to ensure the rename is durable int dirfd = open(gu->database_dir, O_DIRECTORY); exit_if(-1 == dirfd, "Error opening database directory: %s", strerror(errno)); exit_if(-1 == fsync(dirfd), "Error syncing database directory: %s", strerror(errno)); exit_if(-1 == close(dirfd), "Error closing database directory: %s", strerror(errno)); exit_if(-1 == unlink(gzipfile), "Error unlinking %s: %s", gzipfile, strerror(errno)); free(file_path_test); return OK; }
static int parse_license_file(geoipupdate_s * up) { say_if(up->verbose, "%s\n", PACKAGE_STRING); FILE *fh = fopen(up->license_file, "rb"); exit_unless(!!fh, "Can't open license file %s\n", up->license_file); say_if(up->verbose, "Opened License file %s\n", up->license_file); const char *sep = " \t\r\n"; size_t bsize = 1024; char *buffer = (char *)xmalloc(bsize); ssize_t read_bytes; while ((read_bytes = my_getline(&buffer, &bsize, fh)) != -1) { size_t idx = strspn(buffer, sep); char *strt = &buffer[idx]; if (*strt == '#') { continue; } if (sscanf(strt, "UserId %d", &up->license.user_id) == 1) { say_if(up->verbose, "UserId %d\n", up->license.user_id); continue; } if (sscanf(strt, "LicenseKey %12s", &up->license.license_key[0]) == 1) { say_if(up->verbose, "LicenseKey %s\n", up->license.license_key); continue; } char *p, *last; if ((p = strtok_r(strt, sep, &last))) { if (!strcmp(p, "ProductIds")) { while ((p = strtok_r(NULL, sep, &last))) { product_insert_once(up, p); } } else if (!strcmp(p, "SkipPeerVerification")) { p = strtok_r(NULL, sep, &last); exit_if(NULL == p || (0 != strcmp(p, "0") && 0 != strcmp(p, "1")), "SkipPeerVerification must be 0 or 1\n"); up->skip_peer_verification = atoi(p); } else if (!strcmp(p, "Protocol")) { p = strtok_r(NULL, sep, &last); exit_if(NULL == p || (0 != strcmp(p, "http") && 0 != strcmp(p, "https")), "Protocol must be http or https\n"); free(up->proto); up->proto = strdup(p); } else if (!strcmp(p, "SkipHostnameVerification")) { p = strtok_r(NULL, sep, &last); exit_if(NULL == p || (0 != strcmp(p, "0") && 0 != strcmp(p, "1")), "SkipHostnameVerification must be 0 or 1\n"); up->skip_hostname_verification = atoi(p); } else if (!strcmp(p, "Host")) { p = strtok_r(NULL, sep, &last); exit_if(NULL == p, "Host must be defined\n"); free(up->host); up->host = strdup(p); } else if (!strcmp(p, "DatabaseDirectory")) { if (!up->do_not_overwrite_database_directory) { p = strtok_r(NULL, sep, &last); exit_if(NULL == p, "DatabaseDirectory must be defined\n"); free(up->database_dir); up->database_dir = strdup(p); } } else if (!strcmp(p, "Proxy")) { p = strtok_r(NULL, sep, &last); exit_if(NULL == p, "Proxy must be defined 1.2.3.4:12345\n"); free(up->proxy); up->proxy = strdup(p); } else if (!strcmp(p, "ProxyUserPassword")) { p = strtok_r(NULL, sep, &last); exit_if(NULL == p, "ProxyUserPassword must be defined xyz:abc\n"); free(up->proxy_user_password); up->proxy_user_password = strdup(p); } else { say_if(up->verbose, "Skip unknown directive: %s\n", p); } } } free(buffer); exit_if(-1 == fclose(fh), "Error closing stream: %s", strerror(errno)); say_if(up->verbose, "Read in license key %s\nNumber of product ids %d\n", up->license_file, product_count(up)); return 1; }