1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */
4 * Copyright (C) 2003 MaxMind LLC All Rights Reserved.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
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 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include <sys/socket.h>
31 #include <netinet/in.h> /* For ntohl */
32 #include <arpa/inet.h>
37 #include <sys/types.h> /* for fstat */
38 #include <sys/stat.h> /* for fstat */
41 #include <stdint.h> /* For uint32_t */
45 #define INADDR_NONE -1
48 #define COUNTRY_BEGIN 16776960
49 #define STATE_BEGIN_REV0 16700000
50 #define STATE_BEGIN_REV1 16000000
51 #define STRUCTURE_INFO_MAX_SIZE 20
52 #define DATABASE_INFO_MAX_SIZE 100
53 #define MAX_ORG_RECORD_LENGTH 300
55 #define CANADA_OFFSET 677
56 #define WORLD_OFFSET 1353
57 #define FIPS_RANGE 360
59 #define CHECK_ERR(err, msg) { \
61 fprintf(stderr, "%s error: %d\n", msg, err); \
66 const char GeoIP_country_code
[247][3] = { "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN","AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR","FX","GA","GB","GD","GE","GF","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR","HT","HU","ID","IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY","QA","RE","RO","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH","TJ","TK","TM","TN","TO","TP","TR","TT","TV","TW","TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE","YT","YU","ZA","ZM","ZR","ZW","A1","A2","O1"};
68 const char GeoIP_country_code3
[247][4] = { "--","AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT","AGO","AQ","ARG","ASM","AUT","AUS","ABW","AZE","BIH","BRB","BGD","BEL","BFA","BGR","BHR","BDI","BEN","BMU","BRN","BOL","BRA","BHS","BTN","BV","BWA","BLR","BLZ","CAN","CC","COD","CAF","COG","CHE","CIV","COK","CHL","CMR","CHN","COL","CRI","CUB","CPV","CX","CYP","CZE","DEU","DJI","DNK","DMA","DOM","DZA","ECU","EST","EGY","ESH","ERI","ESP","ETH","FIN","FJI","FLK","FSM","FRO","FRA","FX","GAB","GBR","GRD","GEO","GUF","GHA","GIB","GRL","GMB","GIN","GLP","GNQ","GRC","GS","GTM","GUM","GNB","GUY","HKG","HM","HND","HRV","HTI","HUN","IDN","IRL","ISR","IND","IO","IRQ","IRN","ISL","ITA","JAM","JOR","JPN","KEN","KGZ","KHM","KIR","COM","KNA","PRK","KOR","KWT","CYM","KAZ","LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU","LUX","LVA","LBY","MAR","MCO","MDA","MDG","MHL","MKD","MLI","MMR","MNG","MAC","MNP","MTQ","MRT","MSR","MLT","MUS","MDV","MWI","MEX","MYS","MOZ","NAM","NCL","NER","NFK","NGA","NIC","NLD","NOR","NPL","NRU","NIU","NZL","OMN","PAN","PER","PYF","PNG","PHL","PAK","POL","SPM","PCN","PRI","PSE","PRT","PLW","PRY","QAT","REU","ROU","RUS","RWA","SAU","SLB","SYC","SDN","SWE","SGP","SHN","SVN","SJM","SVK","SLE","SMR","SEN","SOM","SUR","STP","SLV","SYR","SWZ","TCA","TCD","TF","TGO","THA","TJK","TKL","TLS","TKM","TUN","TON","TUR","TTO","TUV","TWN","TZA","UKR","UGA","UM","USA","URY","UZB","VAT","VCT","VEN","VGB","VIR","VNM","VUT","WLF","WSM","YEM","YT","YUG","ZAF","ZMB","ZR","ZWE","A1","A2","O1"};
70 const char * GeoIP_country_name
[247] = {"N/A","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles","Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados","Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia","Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the","Central African Republic","Congo","Switzerland","Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica","Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic","Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji","Falkland Islands (Malvinas)","Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana","Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala","Guam","Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia","Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan","Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis",
71 "Korea, Democratic People's Republic of","Korea, Republic of","Kuwait","Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania","Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia","Mali","Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives","Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua","Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia","Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory, Occupied","Portugal","Palau","Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands","Seychelles","Sudan","Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname","Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand","Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","East Timor","Turkey","Trinidad and Tobago","Tuvalu","Taiwan","Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela","Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Yugoslavia","South Africa","Zambia","Zaire","Zimbabwe",
72 "Anonymous Proxy","Satellite Provider","Other"};
74 const char GeoIP_country_continent
[247][3] = {"--","AS","EU","EU","AS","AS","SA","SA","EU","AS","SA","AF","AN","SA","OC","EU","OC","SA","AS","EU","SA","AS","EU","AF","EU","AS","AF","AF","SA","AS","SA","SA","SA","AS","AF","AF","EU","SA","NA","AS","AF","AF","AF","EU","AF","OC","SA","AF","AS","SA","SA","SA","AF","AS","AS","EU","EU","AF","EU","SA","SA","AF","SA","EU","AF","AF","AF","EU","AF","EU","OC","SA","OC","EU","EU","EU","AF","EU","SA","AS","SA","AF","EU","SA","AF","AF","SA","AF","EU","SA","SA","OC","AF","SA","AS","AF","SA","EU","SA","EU","AS","EU","AS","AS","AS","AS","AS","EU","EU","SA","AS","AS","AF","AS","AS","OC","AF","SA","AS","AS","AS","SA","AS","AS","AS","SA","EU","AS","AF","AF","EU","EU","EU","AF","AF","EU","EU","AF","OC","EU","AF","AS","AS","AS","OC","SA","AF","SA","EU","AF","AS","AF","NA","AS","AF","AF","OC","AF","OC","AF","SA","EU","EU","AS","OC","OC","OC","AS","SA","SA","OC","OC","AS","AS","EU","SA","OC","SA","AS","EU","OC","SA","AS","AF","EU","AS","AF","AS","OC","AF","AF","EU","AS","AF","EU","EU","EU","AF","EU","AF","AF","SA","AF","SA","AS","AF","SA","AF","AF","AF","AS","AS","OC","AS","AF","OC","AS","AS","SA","OC","AS","AF","EU","AF","OC","NA","SA","AS","EU","SA","SA","SA","SA","AS","OC","OC","OC","AS","AF","EU","AF","AF","AF","AF"};
76 const char * GeoIPDBDescription
[NUM_DB_TYPES
] = {NULL
, "GeoIP Country Edition", "GeoIP City Edition, Rev 1", "GeoIP Region Edition, Rev 1", "GeoIP ISP Edition", "GeoIP Organization Edition", "GeoIP City Edition, Rev 0", "GeoIP Region Edition, Rev 0","GeoIP Proxy Edition","GeoIP ASNum Edition","GeoIP Netspeed Edition"};
78 #define GEOIPDATADIR "/"
80 char *_full_path_to(const char *file_name
) {
81 char *path
= malloc(sizeof(char) * 1024);
84 memset(path
, 0, sizeof(char) * 1024);
85 snprintf(path
, sizeof(char) * 1024 - 1, "%s/%s", GEOIPDATADIR
, file_name
);
87 char buf
[MAX_PATH
], *p
, *q
= NULL
;
89 memset(buf
, 0, sizeof(buf
));
90 len
= GetModuleFileName(GetModuleHandle(NULL
), buf
, sizeof(buf
) - 1);
91 for (p
= buf
+ len
; p
> buf
; p
--)
100 memset(path
, 0, sizeof(char) * 1024);
101 snprintf(path
, sizeof(char) * 1024 - 1, "%s/%s", buf
, file_name
);
107 char ** GeoIPDBFileName
= NULL
;
109 void _setup_dbfilename() {
110 if (NULL
== GeoIPDBFileName
) {
111 GeoIPDBFileName
= malloc(sizeof(char *) * NUM_DB_TYPES
);
112 memset(GeoIPDBFileName
, 0, sizeof(char *) * NUM_DB_TYPES
);
114 GeoIPDBFileName
[GEOIP_COUNTRY_EDITION
] = _full_path_to("GeoIP.dat");
115 GeoIPDBFileName
[GEOIP_REGION_EDITION_REV0
] = _full_path_to("GeoIPRegion.dat");
116 GeoIPDBFileName
[GEOIP_REGION_EDITION_REV1
] = _full_path_to("GeoIPRegion.dat");
117 GeoIPDBFileName
[GEOIP_CITY_EDITION_REV0
] = _full_path_to("GeoIPCity.dat");
118 GeoIPDBFileName
[GEOIP_CITY_EDITION_REV1
] = _full_path_to("GeoIPCity.dat");
119 GeoIPDBFileName
[GEOIP_ISP_EDITION
] = _full_path_to("GeoIPISP.dat");
120 GeoIPDBFileName
[GEOIP_ORG_EDITION
] = _full_path_to("GeoIPOrg.dat");
121 GeoIPDBFileName
[GEOIP_PROXY_EDITION
] = _full_path_to("GeoIPProxy.dat");
122 GeoIPDBFileName
[GEOIP_ASNUM_EDITION
] = _full_path_to("GeoIPASNum.dat");
123 GeoIPDBFileName
[GEOIP_NETSPEED_EDITION
] = _full_path_to("GeoIPNetSpeed.dat");
127 int _file_exists(const char *file_name
) {
129 f
= fopen(file_name
, "r");
136 int GeoIP_db_avail(int type
) {
137 const char * filePath
;
138 if (type
< 0 || type
>= NUM_DB_TYPES
) {
141 filePath
= GeoIPDBFileName
[type
];
142 if (NULL
== filePath
) {
145 return _file_exists(filePath
);
148 void _setup_segments(GeoIP
* gi
) {
150 unsigned char delim
[3];
151 unsigned char buf
[SEGMENT_RECORD_LENGTH
];
153 /* default to GeoIP Country Edition */
154 gi
->databaseType
= GEOIP_COUNTRY_EDITION
;
155 gi
->record_length
= STANDARD_RECORD_LENGTH
;
156 fseek(gi
->GeoIPDatabase
, -3l, SEEK_END
);
157 for (i
= 0; i
< STRUCTURE_INFO_MAX_SIZE
; i
++) {
158 fread(delim
, 1, 3, gi
->GeoIPDatabase
);
159 if (delim
[0] == 255 && delim
[1] == 255 && delim
[2] == 255) {
160 fread(&gi
->databaseType
, 1, 1, gi
->GeoIPDatabase
);
161 if (gi
->databaseType
>= 106) {
162 /* backwards compatibility with databases from April 2003 and earlier */
163 gi
->databaseType
-= 105;
166 if (gi
->databaseType
== GEOIP_REGION_EDITION_REV0
) {
167 /* Region Edition, pre June 2003 */
168 gi
->databaseSegments
= malloc(sizeof(int));
169 gi
->databaseSegments
[0] = STATE_BEGIN_REV0
;
170 } else if (gi
->databaseType
== GEOIP_REGION_EDITION_REV1
) {
171 /* Region Edition, post June 2003 */
172 gi
->databaseSegments
= malloc(sizeof(int));
173 gi
->databaseSegments
[0] = STATE_BEGIN_REV1
;
174 } else if (gi
->databaseType
== GEOIP_CITY_EDITION_REV0
||
175 gi
->databaseType
== GEOIP_CITY_EDITION_REV1
||
176 gi
->databaseType
== GEOIP_ORG_EDITION
||
177 gi
->databaseType
== GEOIP_ISP_EDITION
||
178 gi
->databaseType
== GEOIP_ASNUM_EDITION
) {
179 /* City/Org Editions have two segments, read offset of second segment */
180 gi
->databaseSegments
= malloc(sizeof(int));
181 gi
->databaseSegments
[0] = 0;
182 fread(buf
, SEGMENT_RECORD_LENGTH
, 1, gi
->GeoIPDatabase
);
183 for (j
= 0; j
< SEGMENT_RECORD_LENGTH
; j
++) {
184 gi
->databaseSegments
[0] += (buf
[j
] << (j
* 8));
186 if (gi
->databaseType
== GEOIP_ORG_EDITION
||
187 gi
->databaseType
== GEOIP_ISP_EDITION
)
188 gi
->record_length
= ORG_RECORD_LENGTH
;
192 fseek(gi
->GeoIPDatabase
, -4l, SEEK_CUR
);
195 if (gi
->databaseType
== GEOIP_COUNTRY_EDITION
||
196 gi
->databaseType
== GEOIP_PROXY_EDITION
||
197 gi
->databaseType
== GEOIP_NETSPEED_EDITION
) {
198 gi
->databaseSegments
= malloc(sizeof(int));
199 gi
->databaseSegments
[0] = COUNTRY_BEGIN
;
203 unsigned int _seek_record (GeoIP
*gi
, unsigned long ipnum
) {
206 unsigned char stack_buffer
[2 * MAX_RECORD_LENGTH
];
207 const unsigned char *buf
= (gi
->cache
== NULL
) ? stack_buffer
: NULL
;
208 unsigned int offset
= 0;
210 const unsigned char * p
;
213 for (depth
= 31; depth
>= 0; depth
--) {
214 if (gi
->cache
== NULL
&& gi
->index_cache
== NULL
) {
216 fseek(gi
->GeoIPDatabase
, (long)gi
->record_length
* 2 * offset
, SEEK_SET
);
217 fread(stack_buffer
,gi
->record_length
,2,gi
->GeoIPDatabase
);
218 } else if (gi
->index_cache
== NULL
) {
219 /* simply point to record in memory */
220 buf
= gi
->cache
+ (long)gi
->record_length
* 2 *offset
;
222 buf
= gi
->index_cache
+ (long)gi
->record_length
* 2 * offset
;
225 if (ipnum
& (1 << depth
)) {
226 /* Take the right-hand branch */
227 if ( gi
->record_length
== 3 ) {
228 /* Most common case is completely unrolled and uses constants. */
229 x
= (buf
[3*1 + 0] << (0*8))
230 + (buf
[3*1 + 1] << (1*8))
231 + (buf
[3*1 + 2] << (2*8));
235 j
= gi
->record_length
;
245 /* Take the left-hand branch */
246 if ( gi
->record_length
== 3 ) {
247 /* Most common case is completely unrolled and uses constants. */
248 x
= (buf
[3*0 + 0] << (0*8))
249 + (buf
[3*0 + 1] << (1*8))
250 + (buf
[3*0 + 2] << (2*8));
253 j
= gi
->record_length
;
263 if (x
>= gi
->databaseSegments
[0]) {
269 /* shouldn't reach here */
270 fprintf(stderr
,"Error Traversing Database for ipnum = %lu - Perhaps database is corrupt?\n",ipnum
);
274 unsigned long _addr_to_num (const char *addr
) {
279 unsigned long ipnum
= 0;
282 for (i
=0; i
<4; i
++) {
285 if (c
== '.' || c
== '\0') {
290 ipnum
+= (octet
<< ((3-i
)*8));
293 } else if (c
>= '0' && c
<= '9') {
302 if(c
== '\0' && i
<3) {
309 GeoIP
* GeoIP_open_type (int type
, int flags
) {
311 const char * filePath
;
312 if (type
< 0 || type
>= NUM_DB_TYPES
) {
313 printf("Invalid database type %d\n", type
);
317 filePath
= GeoIPDBFileName
[type
];
318 if (filePath
== NULL
) {
319 printf("Invalid database type %d\n", type
);
322 gi
= GeoIP_open (filePath
, flags
);
326 GeoIP
* GeoIP_new (int flags
, char *filename
) {
328 gi
= GeoIP_open (filename
, flags
);
332 GeoIP
* GeoIP_open (const char * filename
, int flags
) {
335 if (WSAStartup(MAKEWORD(1, 1), &wsa
) != 0)
339 GeoIP
*gi
= (GeoIP
*)malloc(sizeof(GeoIP
));
342 gi
->file_path
= malloc(sizeof(char) * (strlen(filename
)+1));
343 if (gi
->file_path
== NULL
)
345 strcpy(gi
->file_path
, filename
);
346 gi
->GeoIPDatabase
= fopen(filename
,"rb");
347 if (gi
->GeoIPDatabase
== NULL
) {
348 fprintf(stderr
,"Error Opening file %s\n",filename
);
354 fseek(gi
->GeoIPDatabase
, 0, SEEK_END
);
355 st_size
= ftell(gi
->GeoIPDatabase
);
356 fseek(gi
->GeoIPDatabase
, 0, SEEK_SET
);
357 if (flags
& GEOIP_MEMORY_CACHE
) {
358 gi
->cache
= (unsigned char *) malloc(sizeof(unsigned char) * st_size
);
359 if (gi
->cache
!= NULL
) {
360 if (fread(gi
->cache
, sizeof(unsigned char), st_size
, gi
->GeoIPDatabase
) != st_size
) {
361 fprintf(stderr
,"Error reading file %s\n",filename
);
372 if (flags
& GEOIP_INDEX_CACHE
) {
373 gi
->index_cache
= (unsigned char *) malloc(sizeof(unsigned char) * gi
->databaseSegments
[0]);
374 if (gi
->index_cache
!= NULL
) {
375 fseek(gi
->GeoIPDatabase
, 0, SEEK_SET
);
376 if (fread(gi
->index_cache
, sizeof(unsigned char), gi
->databaseSegments
[0], gi
->GeoIPDatabase
) != (size_t) gi
->databaseSegments
[0]) {
377 fprintf(stderr
,"Error reading file %s\n",filename
);
378 free(gi
->index_cache
);
384 gi
->index_cache
= NULL
;
390 void GeoIP_delete (GeoIP
*gi
) {
391 if (gi
->GeoIPDatabase
!= NULL
)
392 fclose(gi
->GeoIPDatabase
);
393 if (gi
->cache
!= NULL
)
395 if (gi
->index_cache
!= NULL
)
396 free(gi
->index_cache
);
397 if (gi
->file_path
!= NULL
)
399 if (gi
->databaseSegments
!= NULL
)
400 free(gi
->databaseSegments
);
404 const char *GeoIP_country_code_by_name (GeoIP
* gi
, const char *name
) {
406 country_id
= GeoIP_id_by_name(gi
, name
);
407 return (country_id
> 0) ? GeoIP_country_code
[country_id
] : NULL
;
410 const char *GeoIP_country_code3_by_name (GeoIP
* gi
, const char *name
) {
412 country_id
= GeoIP_id_by_name(gi
, name
);
413 return (country_id
> 0) ? GeoIP_country_code3
[country_id
] : NULL
;
416 const char *GeoIP_country_name_by_name (GeoIP
* gi
, const char *name
) {
418 country_id
= GeoIP_id_by_name(gi
, name
);
419 return (country_id
> 0) ? GeoIP_country_name
[country_id
] : NULL
;
422 unsigned long lookupaddress (const char *host
) {
423 unsigned long addr
= inet_addr(host
);
425 struct hostent
* phe
= &phe2
;
429 #ifdef HAVE_GETHOSTBYNAME_R
431 int buflength
= 16384;
432 buf
= malloc(buflength
);
434 if (addr
== INADDR_NONE
) {
435 #ifdef HAVE_GETHOSTBYNAME_R
437 /* we use gethostbyname_r here because it is thread-safe and gethostbyname is not */
438 #ifdef GETHOSTBYNAME_R_RETURNS_INT
439 result
= gethostbyname_r(host
,&phe2
,buf
,buflength
,&phe
,&herr
);
441 phe
= gethostbyname_r(host
,&phe2
,buf
,buflength
,&herr
);
447 /* double the buffer if the buffer is too small */
448 buflength
= buflength
* 2;
449 buf
= realloc(buf
,buflength
);
452 #ifndef HAVE_GETHOSTBYNAME_R
453 /* Some systems do not support gethostbyname_r, such as Mac OS X */
454 phe
= gethostbyname(host
);
456 if (!phe
|| result
!= 0) {
460 addr
= *((unsigned long *) phe
->h_addr_list
[0]);
462 #ifdef HAVE_GETHOSTBYNAME_R
468 int GeoIP_id_by_name (GeoIP
* gi
, const char *name
) {
474 if (gi
->databaseType
!= GEOIP_COUNTRY_EDITION
&& gi
->databaseType
!= GEOIP_PROXY_EDITION
&& gi
->databaseType
!= GEOIP_NETSPEED_EDITION
) {
475 printf("Invalid database type %s, expected %s\n", GeoIPDBDescription
[(int)gi
->databaseType
], GeoIPDBDescription
[GEOIP_COUNTRY_EDITION
]);
478 if (!(ipnum
= lookupaddress(name
)))
480 ret
= _seek_record(gi
, ipnum
) - COUNTRY_BEGIN
;
485 const char *GeoIP_country_code_by_addr (GeoIP
* gi
, const char *addr
) {
487 country_id
= GeoIP_id_by_addr(gi
, addr
);
488 return (country_id
> 0) ? GeoIP_country_code
[country_id
] : NULL
;
491 const char *GeoIP_country_code3_by_addr (GeoIP
* gi
, const char *addr
) {
493 country_id
= GeoIP_id_by_addr(gi
, addr
);
494 return (country_id
> 0) ? GeoIP_country_code3
[country_id
] : NULL
;
495 return GeoIP_country_code3
[country_id
];
498 const char *GeoIP_country_name_by_addr (GeoIP
* gi
, const char *addr
) {
500 country_id
= GeoIP_id_by_addr(gi
, addr
);
501 return (country_id
> 0) ? GeoIP_country_name
[country_id
] : NULL
;
502 return GeoIP_country_name
[country_id
];
505 const char *GeoIP_country_code_by_ipnum (GeoIP
* gi
, unsigned long ipnum
) {
507 country_id
= GeoIP_id_by_ipnum(gi
, ipnum
);
508 return (country_id
> 0) ? GeoIP_country_code
[country_id
] : NULL
;
511 int GeoIP_country_info_by_id(int id
, char **two
, char **three
, char **full
) {
514 *two
= (char *)GeoIP_country_code
[id
];
515 *three
= (char *)GeoIP_country_code3
[id
];
516 *full
= (char *)GeoIP_country_name
[id
];
521 int GeoIP_country_id_by_addr (GeoIP
* gi
, const char *addr
) {
522 return GeoIP_id_by_addr(gi
, addr
);
525 int GeoIP_country_id_by_name (GeoIP
* gi
, const char *host
) {
526 return GeoIP_id_by_name(gi
, host
);
529 int GeoIP_id_by_addr (GeoIP
* gi
, const char *addr
) {
535 if (gi
->databaseType
!= GEOIP_COUNTRY_EDITION
&&
536 gi
->databaseType
!= GEOIP_PROXY_EDITION
&&
537 gi
->databaseType
!= GEOIP_NETSPEED_EDITION
) {
538 printf("Invalid database type %s, expected %s\n",
539 GeoIPDBDescription
[(int)gi
->databaseType
],
540 GeoIPDBDescription
[GEOIP_COUNTRY_EDITION
]);
543 ipnum
= _addr_to_num(addr
);
544 ret
= _seek_record(gi
, ipnum
) - COUNTRY_BEGIN
;
548 int GeoIP_id_by_ipnum (GeoIP
* gi
, unsigned long ipnum
) {
551 return _seek_record(gi
, ipnum
) - COUNTRY_BEGIN
;
554 char *GeoIP_database_info (GeoIP
* gi
) {
556 unsigned char buf
[3];
558 int hasStructureInfo
= 0;
563 fseek(gi
->GeoIPDatabase
, -3l, SEEK_END
);
565 /* first get past the database structure information */
566 for (i
= 0; i
< STRUCTURE_INFO_MAX_SIZE
; i
++) {
567 fread(buf
, 1, 3, gi
->GeoIPDatabase
);
568 if (buf
[0] == 255 && buf
[1] == 255 && buf
[2] == 255) {
569 hasStructureInfo
= 1;
572 fseek(gi
->GeoIPDatabase
, -4l, SEEK_CUR
);
574 if (hasStructureInfo
== 1) {
575 fseek(gi
->GeoIPDatabase
, -3l, SEEK_CUR
);
577 /* no structure info, must be pre Sep 2002 database, go back to end */
578 fseek(gi
->GeoIPDatabase
, -3l, SEEK_END
);
581 for (i
= 0; i
< DATABASE_INFO_MAX_SIZE
; i
++) {
582 fread(buf
, 1, 3, gi
->GeoIPDatabase
);
583 if (buf
[0] == 0 && buf
[1] == 0 && buf
[2] == 0) {
584 retval
= malloc(sizeof(char) * (i
+1));
585 if (retval
== NULL
) {
588 fread(retval
, 1, i
, gi
->GeoIPDatabase
);
592 fseek(gi
->GeoIPDatabase
, -4l, SEEK_CUR
);
597 /* GeoIP Region Edition functions */
599 void GeoIP_assign_region_by_inetaddr(GeoIP
* gi
, unsigned long inetaddr
, GeoIPRegion
*region
) {
600 unsigned int seek_region
;
602 /* This also writes in the terminating NULs (if you decide to
603 * keep them) and clear any fields that are not set. */
604 memset(region
, 0, sizeof(GeoIPRegion
));
606 seek_region
= _seek_record(gi
, ntohl(inetaddr
));
608 if (gi
->databaseType
== GEOIP_REGION_EDITION_REV0
) {
609 /* Region Edition, pre June 2003 */
610 seek_region
-= STATE_BEGIN_REV0
;
611 if (seek_region
>= 1000) {
612 region
->country_code
[0] = 'U';
613 region
->country_code
[1] = 'S';
614 region
->region
[0] = (char) ((seek_region
- 1000)/26 + 65);
615 region
->region
[1] = (char) ((seek_region
- 1000)%26
+ 65);
617 memcpy(region
->country_code
, GeoIP_country_code
[seek_region
], 2);
619 } else if (gi
->databaseType
== GEOIP_REGION_EDITION_REV1
) {
620 /* Region Edition, post June 2003 */
621 seek_region
-= STATE_BEGIN_REV1
;
622 if (seek_region
< US_OFFSET
) {
624 /* we don't need to do anything here b/c we memset region to 0 */
625 } else if (seek_region
< CANADA_OFFSET
) {
627 region
->country_code
[0] = 'U';
628 region
->country_code
[1] = 'S';
629 region
->region
[0] = (char) ((seek_region
- US_OFFSET
)/26 + 65);
630 region
->region
[1] = (char) ((seek_region
- US_OFFSET
)%26
+ 65);
631 } else if (seek_region
< WORLD_OFFSET
) {
632 /* Canada Province */
633 region
->country_code
[0] = 'C';
634 region
->country_code
[1] = 'A';
635 region
->region
[0] = (char) ((seek_region
- CANADA_OFFSET
)/26 + 65);
636 region
->region
[1] = (char) ((seek_region
- CANADA_OFFSET
)%26
+ 65);
638 /* Not US or Canada */
639 memcpy(region
->country_code
, GeoIP_country_code
[(seek_region
- WORLD_OFFSET
) / FIPS_RANGE
], 2);
644 GeoIPRegion
* _get_region(GeoIP
* gi
, unsigned long ipnum
) {
645 GeoIPRegion
* region
;
647 region
= malloc(sizeof(GeoIPRegion
));
649 GeoIP_assign_region_by_inetaddr(gi
, htonl(ipnum
), region
);
654 GeoIPRegion
* GeoIP_region_by_addr (GeoIP
* gi
, const char *addr
) {
659 if (gi
->databaseType
!= GEOIP_REGION_EDITION_REV0
&&
660 gi
->databaseType
!= GEOIP_REGION_EDITION_REV1
) {
661 printf("Invalid database type %s, expected %s\n", GeoIPDBDescription
[(int)gi
->databaseType
], GeoIPDBDescription
[GEOIP_REGION_EDITION_REV1
]);
664 ipnum
= _addr_to_num(addr
);
665 return _get_region(gi
, ipnum
);
668 GeoIPRegion
* GeoIP_region_by_name (GeoIP
* gi
, const char *name
) {
673 if (gi
->databaseType
!= GEOIP_REGION_EDITION_REV0
&&
674 gi
->databaseType
!= GEOIP_REGION_EDITION_REV1
) {
675 printf("Invalid database type %s, expected %s\n", GeoIPDBDescription
[(int)gi
->databaseType
], GeoIPDBDescription
[GEOIP_REGION_EDITION_REV1
]);
678 if (!(ipnum
= lookupaddress(name
)))
680 return _get_region(gi
, ipnum
);
683 void GeoIPRegion_delete (GeoIPRegion
*gir
) {
687 /* GeoIP Organization, ISP and AS Number Edition private method */
688 char *_get_name (GeoIP
* gi
, unsigned long ipnum
) {
690 char buf
[MAX_ORG_RECORD_LENGTH
];
691 char * org_buf
, * buf_pointer
;
694 if (gi
->databaseType
!= GEOIP_ORG_EDITION
&&
695 gi
->databaseType
!= GEOIP_ISP_EDITION
&&
696 gi
->databaseType
!= GEOIP_ASNUM_EDITION
) {
697 printf("Invalid database type %s, expected %s\n", GeoIPDBDescription
[(int)gi
->databaseType
], GeoIPDBDescription
[GEOIP_ORG_EDITION
]);
701 seek_org
= _seek_record(gi
, ipnum
);
702 if (seek_org
== gi
->databaseSegments
[0])
705 record_pointer
= seek_org
+ (2 * gi
->record_length
- 1) * gi
->databaseSegments
[0];
707 if (gi
->cache
== NULL
) {
708 fseek(gi
->GeoIPDatabase
, record_pointer
, SEEK_SET
);
709 fread(buf
, sizeof(char), MAX_ORG_RECORD_LENGTH
, gi
->GeoIPDatabase
);
710 org_buf
= malloc(sizeof(char) * (strlen(buf
)+1));
711 strcpy(org_buf
, buf
);
713 buf_pointer
= (char *)gi
->cache
+ (long)record_pointer
;
714 org_buf
= malloc(sizeof(char) * (strlen(buf_pointer
)+1));
715 strcpy(org_buf
, buf_pointer
);
720 char *GeoIP_name_by_addr (GeoIP
* gi
, const char *addr
) {
725 ipnum
= _addr_to_num(addr
);
726 return _get_name(gi
, ipnum
);
729 char *GeoIP_name_by_name (GeoIP
* gi
, const char *name
) {
734 if (!(ipnum
= lookupaddress(name
)))
736 return _get_name(gi
, ipnum
);
739 char *GeoIP_org_by_addr (GeoIP
* gi
, const char *addr
) {
740 return GeoIP_name_by_addr(gi
, addr
);
743 char *GeoIP_org_by_name (GeoIP
* gi
, const char *name
) {
744 return GeoIP_name_by_name(gi
, name
);
747 unsigned char GeoIP_database_edition (GeoIP
* gi
) {
748 return gi
->databaseType
;