]>
Commit | Line | Data |
---|---|---|
1 | /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */ | |
2 | /* GeoIPUpdate.c | |
3 | * | |
4 | * Copyright (C) 2006 MaxMind LLC | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #include "GeoIPCity.h" | |
22 | #include "GeoIP.h" | |
23 | #include "GeoIPUpdate.h" | |
24 | #include "GeoIP_internal.h" | |
25 | ||
26 | #include "global.h" | |
27 | #include "md5.h" | |
28 | #include <sys/types.h> | |
29 | #if !defined(WIN32) && !defined(WIN64) | |
30 | #include <netinet/in.h> | |
31 | #include <arpa/inet.h> | |
32 | #include <sys/socket.h> | |
33 | #include <netdb.h> | |
34 | #else | |
35 | #include <windows.h> | |
36 | #include <winsock.h> | |
37 | #endif | |
38 | #include <zlib.h> | |
39 | #include <time.h> | |
40 | #include <stdio.h> | |
41 | #include <unistd.h> | |
42 | ||
43 | #define BLOCK_SIZE 1024 | |
44 | ||
45 | /* Update DB Host & HTTP GET Request formats: | |
46 | * ------------------------------------------ | |
47 | * GET must support an optional HTTP Proxy. | |
48 | */ | |
49 | const char *GeoIPUpdateHost = "updates.maxmind.com"; | |
50 | /* This is the direct, or proxy port number. */ | |
51 | static int GeoIPHTTPPort = 80; | |
52 | /* License-only format (OLD) */ | |
53 | const char *GeoIPHTTPRequest = "GET %s%s/app/update?license_key=%s&md5=%s HTTP/1.0\nHost: updates.maxmind.com\n\n"; | |
54 | /* General DB Types formats */ | |
55 | const char *GeoIPHTTPRequestFilename = "GET %s%s/app/update_getfilename?product_id=%s HTTP/1.0\nHost: %s\n\n"; | |
56 | const char *GeoIPHTTPRequestClientIP = "GET %s%s/app/update_getipaddr HTTP/1.0\nHost: %s\n\n"; | |
57 | const char *GeoIPHTTPRequestMD5 = "GET %s%s/app/update_secure?db_md5=%s&challenge_md5=%s&user_id=%s&edition_id=%s HTTP/1.0\nHost: updates.maxmind.com\n\n"; | |
58 | ||
59 | /* messages */ | |
60 | const char *NoCurrentDB = "%s can't be opened, proceeding to download database\n"; | |
61 | const char *MD5Info = "MD5 Digest of installed database is %s\n"; | |
62 | const char *SavingGzip = "Saving gzip file to %s ... "; | |
63 | const char *WritingFile = "Writing uncompressed data to %s ..."; | |
64 | ||
65 | const char * GeoIP_get_error_message(int i) { | |
66 | switch (i) { | |
67 | case GEOIP_NO_NEW_UPDATES: | |
68 | return "no new updates"; | |
69 | case GEOIP_SUCCESS: | |
70 | return "Success"; | |
71 | case GEOIP_LICENSE_KEY_INVALID_ERR: | |
72 | return "License Key Invalid"; | |
73 | case GEOIP_DNS_ERR: | |
74 | return "Unable to resolve hostname"; | |
75 | case GEOIP_NON_IPV4_ERR: | |
76 | return "Non - IPv4 address"; | |
77 | case GEOIP_SOCKET_OPEN_ERR: | |
78 | return "Error opening socket"; | |
79 | case GEOIP_CONNECTION_ERR: | |
80 | return "Unable to connect"; | |
81 | case GEOIP_GZIP_IO_ERR: | |
82 | return "Unable to write GeoIP.dat.gz file"; | |
83 | case GEOIP_TEST_IO_ERR: | |
84 | return "Unable to write GeoIP.dat.test file"; | |
85 | case GEOIP_GZIP_READ_ERR: | |
86 | return "Unable to read gzip data"; | |
87 | case GEOIP_OUT_OF_MEMORY_ERR: | |
88 | return "Out of memory error"; | |
89 | case GEOIP_SOCKET_READ_ERR: | |
90 | return "Error reading from socket, see errno"; | |
91 | case GEOIP_SANITY_OPEN_ERR: | |
92 | return "Sanity check GeoIP_open error"; | |
93 | case GEOIP_SANITY_INFO_FAIL: | |
94 | return "Sanity check database_info string failed"; | |
95 | case GEOIP_SANITY_LOOKUP_FAIL: | |
96 | return "Sanity check ip address lookup failed"; | |
97 | case GEOIP_RENAME_ERR: | |
98 | return "Rename error while installing db, check errno"; | |
99 | case GEOIP_USER_ID_INVALID_ERR: | |
100 | return "Invalid userID"; | |
101 | case GEOIP_PRODUCT_ID_INVALID_ERR: | |
102 | return "Invalid product ID or subscription expired"; | |
103 | case GEOIP_INVALID_SERVER_RESPONSE: | |
104 | return "Server returned something unexpected"; | |
105 | default: | |
106 | return "no error"; | |
107 | } | |
108 | } | |
109 | int GeoIP_fprintf(int (*f)(FILE *, char *),FILE *fp, const char *str, ...) { | |
110 | va_list ap; | |
111 | int rc; | |
112 | char * f_str; | |
113 | if ( f == NULL ) | |
114 | return 0; | |
115 | va_start(ap, str); | |
116 | vasprintf(&f_str, str, ap); | |
117 | va_end(ap); | |
118 | if ( f_str == NULL ) | |
119 | return -1; | |
120 | rc = (*f)(fp, f_str); | |
121 | free(f_str); | |
122 | return(rc); | |
123 | } | |
124 | ||
125 | void GeoIP_printf(void (*f)(char *), const char *str,...) { | |
126 | va_list params; | |
127 | char * f_str; | |
128 | if (f == NULL) | |
129 | return; | |
130 | va_start(params, str); | |
131 | vasprintf(&f_str, str, params); | |
132 | va_end(params); | |
133 | if ( f_str == NULL ) | |
134 | return; | |
135 | (*f)(f_str); | |
136 | free(f_str); | |
137 | } | |
138 | ||
139 | /* Support HTTP Proxy Host | |
140 | * -------------------------------------------------- | |
141 | * Use typical OS support for the optional HTTP Proxy. | |
142 | * | |
143 | * Proxy adds http://{real-hostname} to URI format strings: | |
144 | * sprintf("GET %s%s/ HTTP/1.0\r\n",GeoIPProxyHTTP,GeoIPProxiedHost, ...); | |
145 | */ | |
146 | ||
147 | /* The Protocol is usually "" OR "http://" with a proxy. */ | |
148 | static char *GeoIPProxyHTTP = ""; | |
149 | /* GeoIP Hostname where proxy forwards requests. */ | |
150 | static char *GeoIPProxiedHost = ""; | |
151 | ||
152 | /* Read http_proxy env. variable & parse it. | |
153 | * ----------------------------------------- | |
154 | * Allow only these formats: | |
155 | * "http://server.com", "http://server.com:8080" | |
156 | * OR | |
157 | * "server.com", "server.com:8080" | |
158 | * | |
159 | * A "user:password@" part will break this. | |
160 | */ | |
161 | short int parse_http_proxy(char **proxy_host, int *port) { | |
162 | char * http_proxy; | |
163 | char * port_value; | |
164 | ||
165 | if ((http_proxy = getenv("http_proxy"))) { | |
166 | ||
167 | if (! strncmp("http://", http_proxy, 7)) http_proxy += 7; | |
168 | ||
169 | *proxy_host = strdup(http_proxy); | |
170 | if ( *proxy_host == NULL ) | |
171 | return 0; /* let the other functions deal with the memory error */ | |
172 | ||
173 | if ((port_value = strchr(*proxy_host, ':'))) { | |
174 | *port_value++ = '\0'; | |
175 | *port = atoi(port_value); | |
176 | } | |
177 | else { | |
178 | *port = 80; | |
179 | } | |
180 | return(1); | |
181 | } | |
182 | else { | |
183 | return(0); | |
184 | } | |
185 | } | |
186 | ||
187 | /* Get the GeoIP host or the current HTTP Proxy host. */ | |
188 | struct hostent *GeoIP_get_host_or_proxy (void) { | |
189 | char * hostname = (char *) GeoIPUpdateHost; | |
190 | char * proxy_host; | |
191 | int proxy_port; | |
192 | ||
193 | /* Set Proxy from OS: Unix/Linux */ | |
194 | if (parse_http_proxy(&proxy_host,&proxy_port)) { | |
195 | hostname = proxy_host; | |
196 | GeoIPProxyHTTP = "http://"; | |
197 | GeoIPProxiedHost = (char *) GeoIPUpdateHost; | |
198 | GeoIPHTTPPort = proxy_port; | |
199 | } | |
200 | ||
201 | /* Resolve DNS host entry. */ | |
202 | return(gethostbyname(hostname)); | |
203 | } | |
204 | ||
205 | short int GeoIP_update_database (char * license_key, int verbose, void (*f)( char * )) { | |
206 | struct hostent *hostlist; | |
207 | int sock; | |
208 | char * buf; | |
209 | struct sockaddr_in sa; | |
210 | int offset = 0, err; | |
211 | char * request_uri; | |
212 | char * compr; | |
213 | unsigned long comprLen; | |
214 | FILE *comp_fh, *cur_db_fh, *gi_fh; | |
215 | gzFile gz_fh; | |
216 | char * file_path_gz, * file_path_test; | |
217 | MD5_CONTEXT context; | |
218 | unsigned char buffer[1024], digest[16]; | |
219 | char hex_digest[33] = "00000000000000000000000000000000\0"; | |
220 | unsigned int i; | |
221 | GeoIP * gi; | |
222 | char * db_info; | |
223 | char block[BLOCK_SIZE]; | |
224 | int block_size = BLOCK_SIZE; | |
225 | size_t len; | |
226 | ||
227 | _GeoIP_setup_dbfilename(); | |
228 | ||
229 | /* get MD5 of current GeoIP database file */ | |
230 | if ((cur_db_fh = fopen (GeoIPDBFileName[GEOIP_COUNTRY_EDITION], "rb")) == NULL) { | |
231 | GeoIP_printf(f,"%s%s", NoCurrentDB, GeoIPDBFileName[GEOIP_COUNTRY_EDITION]); | |
232 | } else { | |
233 | md5_init(&context); | |
234 | while ((len = fread (buffer, 1, 1024, cur_db_fh)) > 0) | |
235 | md5_write (&context, buffer, len); | |
236 | md5_final (&context); | |
237 | memcpy(digest,context.buf,16); | |
238 | fclose (cur_db_fh); | |
239 | for (i = 0; i < 16; i++) { | |
240 | // "%02x" will write 3 chars | |
241 | snprintf (&hex_digest[2*i], 3, "%02x", digest[i]); | |
242 | } | |
243 | GeoIP_printf(f, MD5Info, hex_digest); | |
244 | } | |
245 | ||
246 | hostlist = GeoIP_get_host_or_proxy(); | |
247 | ||
248 | if (hostlist == NULL) | |
249 | return GEOIP_DNS_ERR; | |
250 | ||
251 | if (hostlist->h_addrtype != AF_INET) | |
252 | return GEOIP_NON_IPV4_ERR; | |
253 | ||
254 | if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { | |
255 | return GEOIP_SOCKET_OPEN_ERR; | |
256 | } | |
257 | ||
258 | memset(&sa, 0, sizeof(struct sockaddr_in)); | |
259 | sa.sin_port = htons(GeoIPHTTPPort); | |
260 | memcpy(&sa.sin_addr, hostlist->h_addr_list[0], hostlist->h_length); | |
261 | sa.sin_family = AF_INET; | |
262 | ||
263 | if (verbose == 1){ | |
264 | GeoIP_printf(f,"Connecting to MaxMind GeoIP Update server\n"); | |
265 | GeoIP_printf(f, "via Host or Proxy Server: %s:%d\n", hostlist->h_name, GeoIPHTTPPort); | |
266 | } | |
267 | ||
268 | /* Download gzip file */ | |
269 | if (connect(sock, (struct sockaddr *)&sa, sizeof(struct sockaddr))< 0) | |
270 | return GEOIP_CONNECTION_ERR; | |
271 | ||
272 | request_uri = malloc(sizeof(char) * (strlen(license_key) + strlen(GeoIPHTTPRequest)+36)); | |
273 | if (request_uri == NULL) | |
274 | return GEOIP_OUT_OF_MEMORY_ERR; | |
275 | sprintf(request_uri,GeoIPHTTPRequest,GeoIPProxyHTTP,GeoIPProxiedHost,license_key, hex_digest); | |
276 | send(sock, request_uri, strlen(request_uri),0); | |
277 | free(request_uri); | |
278 | ||
279 | buf = malloc(sizeof(char) * block_size); | |
280 | if (buf == NULL) | |
281 | return GEOIP_OUT_OF_MEMORY_ERR; | |
282 | ||
283 | if (verbose == 1) | |
284 | GeoIP_printf(f,"Downloading gzipped GeoIP Database...\n"); | |
285 | ||
286 | for (;;) { | |
287 | int amt; | |
288 | amt = recv(sock, &buf[offset], block_size,0); | |
289 | if (amt == 0) { | |
290 | break; | |
291 | } else if (amt == -1) { | |
292 | free(buf); | |
293 | return GEOIP_SOCKET_READ_ERR; | |
294 | } | |
295 | offset += amt; | |
296 | buf = realloc(buf, offset+block_size); | |
297 | if (buf == NULL) | |
298 | return GEOIP_OUT_OF_MEMORY_ERR; | |
299 | } | |
300 | ||
301 | compr = strstr(buf, "\r\n\r\n") + 4; | |
302 | comprLen = offset + buf - compr; | |
303 | ||
304 | if (strstr(compr, "License Key Invalid") != NULL) { | |
305 | if (verbose == 1) | |
306 | GeoIP_printf(f,"Failed\n"); | |
307 | free(buf); | |
308 | return GEOIP_LICENSE_KEY_INVALID_ERR; | |
309 | } else if (strstr(compr, "Invalid product ID or subscription expired") != NULL){ | |
310 | free(buf); | |
311 | return GEOIP_PRODUCT_ID_INVALID_ERR; | |
312 | } else if (strstr(compr, "No new updates available") != NULL) { | |
313 | free(buf); | |
314 | return GEOIP_NO_NEW_UPDATES; | |
315 | } | |
316 | ||
317 | if (verbose == 1) | |
318 | GeoIP_printf(f,"Done\n"); | |
319 | ||
320 | /* save gzip file */ | |
321 | file_path_gz = malloc(sizeof(char) * (strlen(GeoIPDBFileName[GEOIP_COUNTRY_EDITION]) + 4)); | |
322 | if (file_path_gz == NULL) | |
323 | return GEOIP_OUT_OF_MEMORY_ERR; | |
324 | strcpy(file_path_gz,GeoIPDBFileName[GEOIP_COUNTRY_EDITION]); | |
325 | strcat(file_path_gz,".gz"); | |
326 | if (verbose == 1) { | |
327 | GeoIP_printf(f, SavingGzip, file_path_gz); | |
328 | } | |
329 | comp_fh = fopen(file_path_gz, "wb"); | |
330 | ||
331 | if(comp_fh == NULL) { | |
332 | free(file_path_gz); | |
333 | free(buf); | |
334 | return GEOIP_GZIP_IO_ERR; | |
335 | } | |
336 | ||
337 | fwrite(compr, 1, comprLen, comp_fh); | |
338 | fclose(comp_fh); | |
339 | free(buf); | |
340 | ||
341 | if (verbose == 1) | |
342 | GeoIP_printf(f,"Done\n"); | |
343 | ||
344 | if (verbose == 1) | |
345 | GeoIP_printf(f,"Uncompressing gzip file ... "); | |
346 | ||
347 | /* uncompress gzip file */ | |
348 | gz_fh = gzopen(file_path_gz, "rb"); | |
349 | file_path_test = malloc(sizeof(char) * (strlen(GeoIPDBFileName[GEOIP_COUNTRY_EDITION]) + 6)); | |
350 | if (file_path_test == NULL) | |
351 | return GEOIP_OUT_OF_MEMORY_ERR; | |
352 | strcpy(file_path_test,GeoIPDBFileName[GEOIP_COUNTRY_EDITION]); | |
353 | strcat(file_path_test,".test"); | |
354 | gi_fh = fopen(file_path_test, "wb"); | |
355 | ||
356 | if(gi_fh == NULL) { | |
357 | free(file_path_test); | |
358 | return GEOIP_TEST_IO_ERR; | |
359 | } | |
360 | for (;;) { | |
361 | int amt; | |
362 | amt = gzread(gz_fh, block, block_size); | |
363 | if (amt == -1) { | |
364 | free(file_path_test); | |
365 | fclose(gi_fh); | |
366 | gzclose(gz_fh); | |
367 | return GEOIP_GZIP_READ_ERR; | |
368 | } | |
369 | if (amt == 0) { | |
370 | break; | |
371 | } | |
372 | fwrite(block,1,amt,gi_fh); | |
373 | } | |
374 | gzclose(gz_fh); | |
375 | unlink(file_path_gz); | |
376 | free(file_path_gz); | |
377 | fclose(gi_fh); | |
378 | ||
379 | if (verbose == 1) | |
380 | GeoIP_printf(f,"Done\n"); | |
381 | ||
382 | if (verbose == 1) { | |
383 | GeoIP_printf(f, WritingFile, GeoIPDBFileName[GEOIP_COUNTRY_EDITION]); | |
384 | } | |
385 | ||
386 | /* sanity check */ | |
387 | gi = GeoIP_open(file_path_test, GEOIP_STANDARD); | |
388 | ||
389 | if (verbose == 1) | |
390 | GeoIP_printf(f,"Performing santity checks ... "); | |
391 | ||
392 | if (gi == NULL) { | |
393 | GeoIP_printf(f,"Error opening sanity check database\n"); | |
394 | return GEOIP_SANITY_OPEN_ERR; | |
395 | } | |
396 | ||
397 | /* this checks to make sure the files is complete, since info is at the end */ | |
398 | /* dependent on future databases having MaxMind in info */ | |
399 | if (verbose == 1) | |
400 | GeoIP_printf(f,"database_info "); | |
401 | db_info = GeoIP_database_info(gi); | |
402 | if (db_info == NULL) { | |
403 | GeoIP_delete(gi); | |
404 | if (verbose == 1) | |
405 | GeoIP_printf(f,"FAIL\n"); | |
406 | return GEOIP_SANITY_INFO_FAIL; | |
407 | } | |
408 | if (strstr(db_info, "MaxMind") == NULL) { | |
409 | free(db_info); | |
410 | GeoIP_delete(gi); | |
411 | if (verbose == 1) | |
412 | GeoIP_printf(f,"FAIL\n"); | |
413 | return GEOIP_SANITY_INFO_FAIL; | |
414 | } | |
415 | free(db_info); | |
416 | if (verbose == 1) | |
417 | GeoIP_printf(f,"PASS "); | |
418 | ||
419 | /* this performs an IP lookup test of a US IP address */ | |
420 | if (verbose == 1) | |
421 | GeoIP_printf(f,"lookup "); | |
422 | if (strcmp(GeoIP_country_code_by_addr(gi,"24.24.24.24"), "US") != 0) { | |
423 | GeoIP_delete(gi); | |
424 | if (verbose == 1) | |
425 | GeoIP_printf(f,"FAIL\n"); | |
426 | return GEOIP_SANITY_LOOKUP_FAIL; | |
427 | } | |
428 | GeoIP_delete(gi); | |
429 | if (verbose == 1) | |
430 | GeoIP_printf(f,"PASS\n"); | |
431 | ||
432 | /* install GeoIP.dat.test -> GeoIP.dat */ | |
433 | err = rename(file_path_test, GeoIPDBFileName[GEOIP_COUNTRY_EDITION]); | |
434 | if (err != 0) { | |
435 | GeoIP_printf(f,"GeoIP Install error while renaming file\n"); | |
436 | return GEOIP_RENAME_ERR; | |
437 | } | |
438 | ||
439 | if (verbose == 1) | |
440 | GeoIP_printf(f,"Done\n"); | |
441 | ||
442 | return 0; | |
443 | } | |
444 | ||
445 | short int GeoIP_update_database_general (char * user_id,char * license_key,char *data_base_type, int verbose,char ** client_ipaddr, void (*f)( char *)) { | |
446 | struct hostent *hostlist; | |
447 | int sock; | |
448 | char * buf; | |
449 | struct sockaddr_in sa; | |
450 | int offset = 0, err; | |
451 | char * request_uri; | |
452 | char * compr; | |
453 | unsigned long comprLen; | |
454 | FILE *comp_fh, *cur_db_fh, *gi_fh; | |
455 | gzFile gz_fh; | |
456 | char * file_path_gz, * file_path_test; | |
457 | MD5_CONTEXT context; | |
458 | MD5_CONTEXT context2; | |
459 | unsigned char buffer[1024], digest[16] ,digest2[16]; | |
460 | char hex_digest[33] = "0000000000000000000000000000000\0"; | |
461 | char hex_digest2[33] = "0000000000000000000000000000000\0"; | |
462 | unsigned int i; | |
463 | char *f_str; | |
464 | GeoIP * gi; | |
465 | char * db_info; | |
466 | char *ipaddress; | |
467 | char *geoipfilename; | |
468 | char *tmpstr; | |
469 | int dbtype; | |
470 | int lookupresult = 1; | |
471 | char block[BLOCK_SIZE]; | |
472 | int block_size = BLOCK_SIZE; | |
473 | size_t len; | |
474 | size_t request_uri_len; | |
475 | ||
476 | hostlist = GeoIP_get_host_or_proxy(); | |
477 | ||
478 | if (hostlist == NULL) | |
479 | return GEOIP_DNS_ERR; | |
480 | ||
481 | if (hostlist->h_addrtype != AF_INET) | |
482 | return GEOIP_NON_IPV4_ERR; | |
483 | if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { | |
484 | return GEOIP_SOCKET_OPEN_ERR; | |
485 | } | |
486 | ||
487 | memset(&sa, 0, sizeof(struct sockaddr_in)); | |
488 | sa.sin_port = htons(GeoIPHTTPPort); | |
489 | memcpy(&sa.sin_addr, hostlist->h_addr_list[0], hostlist->h_length); | |
490 | sa.sin_family = AF_INET; | |
491 | ||
492 | if (verbose == 1) { | |
493 | GeoIP_printf(f,"Connecting to MaxMind GeoIP server\n"); | |
494 | GeoIP_printf(f, "via Host or Proxy Server: %s:%d\n", hostlist->h_name, GeoIPHTTPPort); | |
495 | } | |
496 | ||
497 | if (connect(sock, (struct sockaddr *)&sa, sizeof(struct sockaddr))< 0) | |
498 | return GEOIP_CONNECTION_ERR; | |
499 | request_uri = malloc(sizeof(char) * (strlen(license_key) + strlen(GeoIPHTTPRequestMD5)+1036)); | |
500 | if (request_uri == NULL) | |
501 | return GEOIP_OUT_OF_MEMORY_ERR; | |
502 | ||
503 | /* get the file name from a web page using the product id */ | |
504 | sprintf(request_uri,GeoIPHTTPRequestFilename,GeoIPProxyHTTP,GeoIPProxiedHost,data_base_type,GeoIPUpdateHost); | |
505 | if (verbose == 1) { | |
506 | GeoIP_printf(f, "sending request %s \n",request_uri); | |
507 | } | |
508 | send(sock, request_uri, strlen(request_uri),0); /* send the request */ | |
509 | free(request_uri); | |
510 | buf = malloc(sizeof(char) * (block_size+4)); | |
511 | if (buf == NULL) | |
512 | return GEOIP_OUT_OF_MEMORY_ERR; | |
513 | offset = 0; | |
514 | for (;;){ | |
515 | int amt; | |
516 | amt = recv(sock, &buf[offset], block_size,0); | |
517 | if (amt == 0){ | |
518 | break; | |
519 | } else if (amt == -1) { | |
520 | free(buf); | |
521 | return GEOIP_SOCKET_READ_ERR; | |
522 | } | |
523 | offset += amt; | |
524 | buf = realloc(buf, offset + block_size + 4); | |
525 | } | |
526 | buf[offset] = 0; | |
527 | offset = 0; | |
528 | tmpstr = strstr(buf, "\r\n\r\n") + 4; | |
529 | if (tmpstr[0] == '.' || strchr(tmpstr, '/') != NULL) { | |
530 | free(buf); | |
531 | return GEOIP_INVALID_SERVER_RESPONSE; | |
532 | } | |
533 | geoipfilename = _GeoIP_full_path_to(tmpstr); | |
534 | free(buf); | |
535 | ||
536 | /* print the database product id and the database filename */ | |
537 | if (verbose == 1){ | |
538 | GeoIP_printf(f, "database product id %s database file name %s \n",data_base_type,geoipfilename); | |
539 | } | |
540 | _GeoIP_setup_dbfilename(); | |
541 | ||
542 | /* get MD5 of current GeoIP database file */ | |
543 | if ((cur_db_fh = fopen (geoipfilename, "rb")) == NULL) { | |
544 | GeoIP_printf(f, NoCurrentDB, geoipfilename); | |
545 | } else { | |
546 | md5_init(&context); | |
547 | while ((len = fread (buffer, 1, 1024, cur_db_fh)) > 0) | |
548 | md5_write (&context, buffer, len); | |
549 | md5_final (&context); | |
550 | memcpy(digest,context.buf,16); | |
551 | fclose (cur_db_fh); | |
552 | for (i = 0; i < 16; i++) | |
553 | sprintf (&hex_digest[2*i], "%02x", digest[i]); | |
554 | GeoIP_printf(f, MD5Info, hex_digest ); | |
555 | } | |
556 | if (verbose == 1) { | |
557 | GeoIP_printf(f,"MD5 sum of database %s is %s \n",geoipfilename,hex_digest); | |
558 | } | |
559 | if (client_ipaddr[0] == NULL) { | |
560 | /* We haven't gotten our IP address yet, so let's request it */ | |
561 | if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { | |
562 | free(geoipfilename); | |
563 | return GEOIP_SOCKET_OPEN_ERR; | |
564 | } | |
565 | ||
566 | memset(&sa, 0, sizeof(struct sockaddr_in)); | |
567 | sa.sin_port = htons(GeoIPHTTPPort); | |
568 | memcpy(&sa.sin_addr, hostlist->h_addr_list[0], hostlist->h_length); | |
569 | sa.sin_family = AF_INET; | |
570 | ||
571 | if (verbose == 1) | |
572 | GeoIP_printf(f,"Connecting to MaxMind GeoIP Update server\n"); | |
573 | ||
574 | /* Download gzip file */ | |
575 | if (connect(sock, (struct sockaddr *)&sa, sizeof(struct sockaddr))< 0) { | |
576 | free(geoipfilename); | |
577 | return GEOIP_CONNECTION_ERR; | |
578 | } | |
579 | request_uri = malloc(sizeof(char) * (strlen(license_key) + strlen(GeoIPHTTPRequestMD5)+1036)); | |
580 | if (request_uri == NULL) { | |
581 | free(geoipfilename); | |
582 | return GEOIP_OUT_OF_MEMORY_ERR; | |
583 | } | |
584 | ||
585 | /* get client ip address from MaxMind web page */ | |
586 | sprintf(request_uri,GeoIPHTTPRequestClientIP,GeoIPProxyHTTP,GeoIPProxiedHost,GeoIPUpdateHost); | |
587 | send(sock, request_uri, strlen(request_uri),0); /* send the request */ | |
588 | if (verbose == 1) { | |
589 | GeoIP_printf(f, "sending request %s", request_uri); | |
590 | } | |
591 | free(request_uri); | |
592 | buf = malloc(sizeof(char) * (block_size+1)); | |
593 | if (buf == NULL) { | |
594 | free(geoipfilename); | |
595 | return GEOIP_OUT_OF_MEMORY_ERR; | |
596 | } | |
597 | offset = 0; | |
598 | ||
599 | for (;;){ | |
600 | int amt; | |
601 | amt = recv(sock, &buf[offset], block_size,0); | |
602 | if (amt == 0) { | |
603 | break; | |
604 | } else if (amt == -1) { | |
605 | free(buf); | |
606 | return GEOIP_SOCKET_READ_ERR; | |
607 | } | |
608 | offset += amt; | |
609 | buf = realloc(buf, offset+block_size+1); | |
610 | } | |
611 | ||
612 | buf[offset] = 0; | |
613 | offset = 0; | |
614 | ipaddress = strstr(buf, "\r\n\r\n") + 4; /* get the ip address */ | |
615 | ipaddress = malloc(strlen(strstr(buf, "\r\n\r\n") + 4)+5); | |
616 | strcpy(ipaddress,strstr(buf, "\r\n\r\n") + 4); | |
617 | client_ipaddr[0] = ipaddress; | |
618 | if (verbose == 1) { | |
619 | GeoIP_printf(f, "client ip address: %s\n",ipaddress); | |
620 | } | |
621 | free(buf); | |
622 | close(sock); | |
623 | } | |
624 | ||
625 | ipaddress = client_ipaddr[0]; | |
626 | ||
627 | /* make a md5 sum of ip address and license_key and store it in hex_digest2 */ | |
628 | request_uri_len = sizeof(char) * 2036; | |
629 | request_uri = malloc(request_uri_len); | |
630 | md5_init(&context2); | |
631 | md5_write (&context2, license_key, 12);//add license key to the md5 sum | |
632 | md5_write (&context2, ipaddress, strlen(ipaddress));//add ip address to the md5 sum | |
633 | md5_final (&context2); | |
634 | memcpy(digest2,context2.buf,16); | |
635 | for (i = 0; i < 16; i++) | |
636 | snprintf (&hex_digest2[2*i], 3, "%02x", digest2[i]);// change the digest to a hex digest | |
637 | if (verbose == 1) { | |
638 | GeoIP_printf(f, "md5sum of ip address and license key is %s \n",hex_digest2); | |
639 | } | |
640 | ||
641 | /* send the request using the user id,product id, | |
642 | * md5 sum of the prev database and | |
643 | * the md5 sum of the license_key and ip address */ | |
644 | if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { | |
645 | return GEOIP_SOCKET_OPEN_ERR; | |
646 | } | |
647 | memset(&sa, 0, sizeof(struct sockaddr_in)); | |
648 | sa.sin_port = htons(GeoIPHTTPPort); | |
649 | memcpy(&sa.sin_addr, hostlist->h_addr_list[0], hostlist->h_length); | |
650 | sa.sin_family = AF_INET; | |
651 | if (connect(sock, (struct sockaddr *)&sa, sizeof(struct sockaddr))< 0) | |
652 | return GEOIP_CONNECTION_ERR; | |
653 | snprintf(request_uri, request_uri_len, GeoIPHTTPRequestMD5,GeoIPProxyHTTP,GeoIPProxiedHost,hex_digest,hex_digest2,user_id,data_base_type); | |
654 | send(sock, request_uri, strlen(request_uri),0); | |
655 | if (verbose == 1) { | |
656 | GeoIP_printf(f, "sending request %s\n",request_uri); | |
657 | } | |
658 | ||
659 | free(request_uri); | |
660 | ||
661 | offset = 0; | |
662 | buf = malloc(sizeof(char) * block_size); | |
663 | if (buf == NULL) | |
664 | return GEOIP_OUT_OF_MEMORY_ERR; | |
665 | ||
666 | if (verbose == 1) | |
667 | GeoIP_printf(f,"Downloading gzipped GeoIP Database...\n"); | |
668 | ||
669 | for (;;) { | |
670 | int amt; | |
671 | amt = recv(sock, &buf[offset], block_size,0); | |
672 | ||
673 | if (amt == 0) { | |
674 | break; | |
675 | } else if (amt == -1) { | |
676 | free(buf); | |
677 | return GEOIP_SOCKET_READ_ERR; | |
678 | } | |
679 | offset += amt; | |
680 | buf = realloc(buf, offset+block_size); | |
681 | if (buf == NULL) | |
682 | return GEOIP_OUT_OF_MEMORY_ERR; | |
683 | } | |
684 | ||
685 | compr = strstr(buf, "\r\n\r\n") + 4; | |
686 | comprLen = offset + buf - compr; | |
687 | ||
688 | if (strstr(compr, "License Key Invalid") != NULL) { | |
689 | if (verbose == 1) | |
690 | GeoIP_printf(f,"Failed\n"); | |
691 | free(buf); | |
692 | return GEOIP_LICENSE_KEY_INVALID_ERR; | |
693 | } else if (strstr(compr, "No new updates available") != NULL) { | |
694 | free(buf); | |
695 | GeoIP_printf(f, "%s is up to date, no updates required\n", geoipfilename); | |
696 | return GEOIP_NO_NEW_UPDATES; | |
697 | } else if (strstr(compr, "Invalid UserId") != NULL){ | |
698 | free(buf); | |
699 | return GEOIP_USER_ID_INVALID_ERR; | |
700 | } else if (strstr(compr, "Invalid product ID or subscription expired") != NULL){ | |
701 | free(buf); | |
702 | return GEOIP_PRODUCT_ID_INVALID_ERR; | |
703 | } | |
704 | ||
705 | if (verbose == 1) | |
706 | GeoIP_printf(f,"Done\n"); | |
707 | ||
708 | GeoIP_printf(f, "Updating %s\n", geoipfilename); | |
709 | ||
710 | /* save gzip file */ | |
711 | file_path_gz = malloc(sizeof(char) * (strlen(geoipfilename) + 4)); | |
712 | ||
713 | if (file_path_gz == NULL) | |
714 | return GEOIP_OUT_OF_MEMORY_ERR; | |
715 | strcpy(file_path_gz,geoipfilename); | |
716 | strcat(file_path_gz,".gz"); | |
717 | if (verbose == 1) { | |
718 | GeoIP_printf(f, "%s%s", SavingGzip, file_path_gz ); | |
719 | } | |
720 | comp_fh = fopen(file_path_gz, "wb"); | |
721 | ||
722 | if(comp_fh == NULL) { | |
723 | free(file_path_gz); | |
724 | free(buf); | |
725 | return GEOIP_GZIP_IO_ERR; | |
726 | } | |
727 | ||
728 | fwrite(compr, 1, comprLen, comp_fh); | |
729 | fclose(comp_fh); | |
730 | free(buf); | |
731 | ||
732 | if (verbose == 1) { | |
733 | GeoIP_printf(f, "download data to a gz file named %s \n",file_path_gz); | |
734 | GeoIP_printf(f,"Done\n"); | |
735 | GeoIP_printf(f,"Uncompressing gzip file ... "); | |
736 | } | |
737 | ||
738 | file_path_test = malloc(sizeof(char) * (strlen(GeoIPDBFileName[GEOIP_COUNTRY_EDITION]) + 6)); | |
739 | if (file_path_test == NULL) { | |
740 | free(file_path_gz); | |
741 | return GEOIP_OUT_OF_MEMORY_ERR; | |
742 | } | |
743 | strcpy(file_path_test,GeoIPDBFileName[GEOIP_COUNTRY_EDITION]); | |
744 | strcat(file_path_test,".test"); | |
745 | gi_fh = fopen(file_path_test, "wb"); | |
746 | if(gi_fh == NULL) { | |
747 | free(file_path_test); | |
748 | free(file_path_gz); | |
749 | return GEOIP_TEST_IO_ERR; | |
750 | } | |
751 | /* uncompress gzip file */ | |
752 | offset = 0; | |
753 | gz_fh = gzopen(file_path_gz, "rb"); | |
754 | for (;;) { | |
755 | int amt; | |
756 | amt = gzread(gz_fh, block, block_size); | |
757 | if (amt == -1) { | |
758 | free(file_path_gz); | |
759 | free(file_path_test); | |
760 | gzclose(gz_fh); | |
761 | fclose(gi_fh); | |
762 | return GEOIP_GZIP_READ_ERR; | |
763 | } | |
764 | if (amt == 0) { | |
765 | break; | |
766 | } | |
767 | fwrite(block,1,amt,gi_fh); | |
768 | } | |
769 | gzclose(gz_fh); | |
770 | unlink(file_path_gz); | |
771 | free(file_path_gz); | |
772 | fclose(gi_fh); | |
773 | ||
774 | if (verbose == 1) | |
775 | GeoIP_printf(f,"Done\n"); | |
776 | ||
777 | if (verbose == 1) { | |
778 | len = strlen(WritingFile) + strlen(geoipfilename) - 1; | |
779 | f_str = malloc(len); | |
780 | snprintf(f_str,len,WritingFile,geoipfilename); | |
781 | free(f_str); | |
782 | } | |
783 | ||
784 | /* sanity check */ | |
785 | gi = GeoIP_open(file_path_test, GEOIP_STANDARD); | |
786 | ||
787 | if (verbose == 1) | |
788 | GeoIP_printf(f,"Performing santity checks ... "); | |
789 | ||
790 | if (gi == NULL) { | |
791 | GeoIP_printf(f,"Error opening sanity check database\n"); | |
792 | return GEOIP_SANITY_OPEN_ERR; | |
793 | } | |
794 | ||
795 | ||
796 | /* get the database type */ | |
797 | dbtype = GeoIP_database_edition(gi); | |
798 | if (verbose == 1) { | |
799 | GeoIP_printf(f, "Database type is %d\n",dbtype); | |
800 | } | |
801 | ||
802 | /* this checks to make sure the files is complete, since info is at the end | |
803 | dependent on future databases having MaxMind in info (ISP and Organization databases currently don't have info string */ | |
804 | ||
805 | if ((dbtype != GEOIP_ISP_EDITION)&& | |
806 | (dbtype != GEOIP_ORG_EDITION)) { | |
807 | if (verbose == 1) | |
808 | GeoIP_printf(f,"database_info "); | |
809 | db_info = GeoIP_database_info(gi); | |
810 | if (db_info == NULL) { | |
811 | GeoIP_delete(gi); | |
812 | if (verbose == 1) | |
813 | GeoIP_printf(f,"FAIL null\n"); | |
814 | return GEOIP_SANITY_INFO_FAIL; | |
815 | } | |
816 | if (strstr(db_info, "MaxMind") == NULL) { | |
817 | free(db_info); | |
818 | GeoIP_delete(gi); | |
819 | if (verbose == 1) | |
820 | GeoIP_printf(f,"FAIL maxmind\n"); | |
821 | return GEOIP_SANITY_INFO_FAIL; | |
822 | } | |
823 | free(db_info); | |
824 | if (verbose == 1) | |
825 | GeoIP_printf(f,"PASS "); | |
826 | } | |
827 | ||
828 | /* this performs an IP lookup test of a US IP address */ | |
829 | if (verbose == 1) | |
830 | GeoIP_printf(f,"lookup "); | |
831 | if (dbtype == GEOIP_NETSPEED_EDITION) { | |
832 | int netspeed = GeoIP_id_by_name(gi,"24.24.24.24"); | |
833 | lookupresult = 0; | |
834 | if (netspeed == GEOIP_CABLEDSL_SPEED){ | |
835 | lookupresult = 1; | |
836 | } | |
837 | } | |
838 | if (dbtype == GEOIP_COUNTRY_EDITION) { | |
839 | /* if data base type is country then call the function | |
840 | * named GeoIP_country_code_by_addr */ | |
841 | lookupresult = 1; | |
842 | if (strcmp(GeoIP_country_code_by_addr(gi,"24.24.24.24"), "US") != 0) { | |
843 | lookupresult = 0; | |
844 | } | |
845 | if (verbose == 1) { | |
846 | GeoIP_printf(f,"testing GEOIP_COUNTRY_EDITION\n"); | |
847 | } | |
848 | } | |
849 | if (dbtype == GEOIP_REGION_EDITION_REV1) { | |
850 | /* if data base type is region then call the function | |
851 | * named GeoIP_region_by_addr */ | |
852 | GeoIPRegion *r = GeoIP_region_by_addr(gi,"24.24.24.24"); | |
853 | lookupresult = 0; | |
854 | if (r != NULL) { | |
855 | lookupresult = 1; | |
856 | free(r); | |
857 | } | |
858 | if (verbose == 1) { | |
859 | GeoIP_printf(f,"testing GEOIP_REGION_EDITION\n"); | |
860 | } | |
861 | } | |
862 | if (dbtype == GEOIP_CITY_EDITION_REV1) { | |
863 | /* if data base type is city then call the function | |
864 | * named GeoIP_record_by_addr */ | |
865 | GeoIPRecord *r = GeoIP_record_by_addr(gi,"24.24.24.24"); | |
866 | lookupresult = 0; | |
867 | if (r != NULL) { | |
868 | lookupresult = 1; | |
869 | free(r); | |
870 | } | |
871 | if (verbose == 1) { | |
872 | GeoIP_printf(f,"testing GEOIP_CITY_EDITION\n"); | |
873 | } | |
874 | } | |
875 | if ((dbtype == GEOIP_ISP_EDITION)|| | |
876 | (dbtype == GEOIP_ORG_EDITION)) { | |
877 | /* if data base type is isp or org then call the function | |
878 | * named GeoIP_org_by_addr */ | |
879 | GeoIPRecord *r = (GeoIPRecord*)GeoIP_org_by_addr(gi,"24.24.24.24"); | |
880 | lookupresult = 0; | |
881 | if (r != NULL) { | |
882 | lookupresult = 1; | |
883 | free(r); | |
884 | } | |
885 | if (verbose == 1) { | |
886 | if (dbtype == GEOIP_ISP_EDITION) { | |
887 | GeoIP_printf(f,"testing GEOIP_ISP_EDITION\n"); | |
888 | } | |
889 | if (dbtype == GEOIP_ORG_EDITION) { | |
890 | GeoIP_printf(f,"testing GEOIP_ORG_EDITION\n"); | |
891 | } | |
892 | } | |
893 | } | |
894 | if (lookupresult == 0) { | |
895 | GeoIP_delete(gi); | |
896 | if (verbose == 1) | |
897 | GeoIP_printf(f,"FAIL\n"); | |
898 | return GEOIP_SANITY_LOOKUP_FAIL; | |
899 | } | |
900 | GeoIP_delete(gi); | |
901 | if (verbose == 1) | |
902 | GeoIP_printf(f,"PASS\n"); | |
903 | ||
904 | /* install GeoIP.dat.test -> GeoIP.dat */ | |
905 | err = rename(file_path_test, geoipfilename); | |
906 | if (err != 0) { | |
907 | GeoIP_printf(f,"GeoIP Install error while renaming file\n"); | |
908 | return GEOIP_RENAME_ERR; | |
909 | } | |
910 | ||
911 | if (verbose == 1) | |
912 | GeoIP_printf(f,"Done\n"); | |
913 | free(geoipfilename); | |
914 | return 0; | |
915 | } |