There's a small handful of IP-to-geolocation database companies, generally however you need to pay a fair amount per year to receive 12 monthly database updates.
However there is a free database, also updated monthly, called GeoLiteCity, available at http://www.maxmind.com/app/geolitecity
GeoLite City is the free version of their GeoIP City product (which you have to pay for).
Here's a basic comparison ...
So i wrote a little program to utilise the free database. (You can probably pay the $90/mo if you desire and use the paid databases instead as I'm pretty sure they're the same format as the free one, just with a bit more data).
It's in two parts ... a CONVERTER which takes the original CSV-format databases as downloaded off the maxmind.com website and converts them into a more compact, optimised, memory-efficient version. The conversion generally takes less than 10 seconds, and you only need to do it whenever you download the latest databases (so, once a month max).
The second part is the READER, which loads the optimised databases and then allows you to input an IP address and it will then return the geographic location.
Here's a sample output result ...
IP Address: 127.0.0.1 <- localhost
Reserved/local
IP Address: 67.17.204.214 <- powerbasic.com
Country: US Region: FL City: Venice
Because the optimised database is essentially just a list of DWORDs the search itself is very, very quick - practically instant.
To get started ...
1. Download the latest CSV FORMAT database from http://www.maxmind.com/app/geolitecity (there's a direct link to a .zip file on that page). I think the zip file is about 15mbs all up, but expands to approximately 150mbs.
2. Extract the two database files from the zip into the same directory where you'll be running converter.exe and reader.exe from.
3. Compile and run the converter.bas program, this will create the optimized database (ipcities.dat and ipranges.dat), which are about 50mbs all up. This is what the reader loads into memory. (You're free to delete the existing .csv databases at this stage if you want that 150mbs disk space back)
4. Now compile and run the reader.bas program, give it any IP address and it'll return the geographic IP location.
Just a couple notes ... in some cases it may not be able to return a city location but can still return the country location, so never assume it will always be able to determine the city or region. Also, the reader expects a numeric IP address, ie. one that has already been resolved - to keep things as bare-bones as possible i didn't add any DNS lookup code, though that'd only be a few extra lines.
The country returned by the database is simply a country code (ie. "AU") ... to convert those codes to full country names ie Australia see this function
---
The source code is ready-to-compile in PBCC.
To compile in PBWIN should simply be a matter of replacing STDOUT with MSGBOX, removing WAITKEY$ references, and changing the STDIN LINE reference to INPUTBOX$ or similar at the stage where it asks you the user for an IP address.
CONVERTER.BAS
READER.BAS
License
For more info see http://geolite.maxmind.com/download/...se/LICENSE.txt
However there is a free database, also updated monthly, called GeoLiteCity, available at http://www.maxmind.com/app/geolitecity
GeoLite City is the free version of their GeoIP City product (which you have to pay for).
Here's a basic comparison ...
Code:
GeoLite City GeoIP City Cost FREE $370 initial, $90/mo Coverage Worldwide Worldwide Accuracy Over 99.5% on a country level Over 99.8% on a country level and 79% on a city level for the and 83% on a city level for the US within a 25 mile radius. US within a 25 mile radius. Redist. Free, subject to GPL/LPGL for Contact us. APIs and database license. Commercial redistribution licenses are available. Updates Monthly, beginning of month. Monthly, but weekly for binary format.
It's in two parts ... a CONVERTER which takes the original CSV-format databases as downloaded off the maxmind.com website and converts them into a more compact, optimised, memory-efficient version. The conversion generally takes less than 10 seconds, and you only need to do it whenever you download the latest databases (so, once a month max).
The second part is the READER, which loads the optimised databases and then allows you to input an IP address and it will then return the geographic location.
Here's a sample output result ...
IP Address: 127.0.0.1 <- localhost
Reserved/local
IP Address: 67.17.204.214 <- powerbasic.com
Country: US Region: FL City: Venice
Because the optimised database is essentially just a list of DWORDs the search itself is very, very quick - practically instant.
To get started ...
1. Download the latest CSV FORMAT database from http://www.maxmind.com/app/geolitecity (there's a direct link to a .zip file on that page). I think the zip file is about 15mbs all up, but expands to approximately 150mbs.
2. Extract the two database files from the zip into the same directory where you'll be running converter.exe and reader.exe from.
3. Compile and run the converter.bas program, this will create the optimized database (ipcities.dat and ipranges.dat), which are about 50mbs all up. This is what the reader loads into memory. (You're free to delete the existing .csv databases at this stage if you want that 150mbs disk space back)
4. Now compile and run the reader.bas program, give it any IP address and it'll return the geographic IP location.
Just a couple notes ... in some cases it may not be able to return a city location but can still return the country location, so never assume it will always be able to determine the city or region. Also, the reader expects a numeric IP address, ie. one that has already been resolved - to keep things as bare-bones as possible i didn't add any DNS lookup code, though that'd only be a few extra lines.
The country returned by the database is simply a country code (ie. "AU") ... to convert those codes to full country names ie Australia see this function
---
The source code is ready-to-compile in PBCC.
To compile in PBWIN should simply be a matter of replacing STDOUT with MSGBOX, removing WAITKEY$ references, and changing the STDIN LINE reference to INPUTBOX$ or similar at the stage where it asks you the user for an IP address.
CONVERTER.BAS
Code:
#COMPILE EXE TYPE IPRange dwStart AS DWORD dwEnd AS DWORD dwArea AS DWORD END TYPE FUNCTION PBMAIN() AS LONG LOCAL sBuf AS STRING, cnt AS DWORD, i AS DWORD, i2 AS DWORD, sLine AS STRING, x AS DWORD, x2 AS DWORD, IPR AS IPRange, sTmp AS STRING DOBLOCKS: IF DIR$("geolitecity-blocks.csv",39) = "" THEN STDOUT "Warning, geolitecity-blocks.csv not found": WAITKEY$: EXIT FUNCTION END IF STDOUT "Reading 1/2 ... "; OPEN "geolitecity-blocks.csv" FOR BINARY ACCESS READ AS #1 GET$ #1, LOF(1), sBuf CLOSE #1 KILL "ipranges.dat" OPEN "ipranges.dat" FOR BINARY ACCESS WRITE AS #1 STDOUT $CRLF & "Processing 1/2 ... "; i = INSTR(1, sBuf, CHR$(&hA)): i = INSTR(i+1, sBuf, CHR$(&hA)) DO i = i + 1 IF i => LEN(sBuf) THEN EXIT DO i2 = INSTR(i, sBuf, CHR$(&hA)) IF i2 = 0 THEN EXIT DO sLine = MID$(sBuf, i + 1, i2 - i - 1) x = 1 x2 = INSTR(x+1,sLine, CHR$(34)) IPR.dwStart = VAL(MID$(sLine, x, x2 - x)) x = x2 + 3 x2 = INSTR(x+1, sLine, CHR$(34)) IPR.dwEnd = VAL(MID$(sLine, x, x2 - x)) x = x2 + 3 x2 = INSTR(x+1, sLine, CHR$(34)) IPR.dwArea = VAL(MID$(sLine, x, x2 - x)) i = i2 INCR cnt sTmp = PEEK$(VARPTR(IPR), 12) PUT$ #1, sTmp LOOP i = LOF(1) CLOSE #1 STDOUT $CRLF & "Done! " & TRIM$(STR$(cnt)) & " IP Ranges saved. Filesize " & FORMAT$(i / 1024 / 1024, "0.0") & "mbs" DOAREAS: LOCAL dwCode AS DWORD, sCountry AS STRING, sRegion AS STRING, sCity AS STRING, dwHigh AS DWORD IF DIR$("geolitecity-blocks.csv",39) = "" THEN STDOUT "Warning, geolitecity-blocks.csv not found": WAITKEY$: EXIT FUNCTION END IF cnt=0 STDOUT "Reading 2/2 ... "; OPEN "geolitecity-location.csv" FOR BINARY ACCESS READ AS #1 GET$ #1, LOF(1), sBuf CLOSE #1 KILL "ipcities.dat" OPEN "ipcities.dat" FOR BINARY ACCESS WRITE AS #1 STDOUT $CRLF & "Processing 2/2 ... "; i = INSTR(1, sBuf, CHR$(&hA)): i = INSTR(i+1, sBuf, CHR$(&hA)) DO i = i + 1 IF i => LEN(sBuf) THEN EXIT DO i2 = INSTR(i, sBuf, CHR$(&hA)) IF i2 = 0 THEN EXIT DO sLine = MID$(sBuf, i, i2 - i - 1) x = INSTR(1, sLine, ",") dwCode = VAL(LEFT$(sLine, x - 1)) IF dwCode > dwHigh THEN dwHigh = dwCode x = x+1 x2 = INSTR(x, sLine, ",") sCountry = MID$(sLine, x, x2 - x) IF LEFT$(sCountry,1) = CHR$(34) THEN sCountry = RIGHT$(sCountry, LEN(sCountry) - 1) IF RIGHT$(sCountry,1) = CHR$(34) THEN sCountry = LEFT$(sCountry, LEN(sCountry) - 1) x = x2+1 x2 = INSTR(x, sLine, ",") sRegion = MID$(sLine, x, x2 - x) IF LEFT$(sRegion,1) = CHR$(34) THEN sRegion = RIGHT$(sRegion, LEN(sRegion) - 1) IF RIGHT$(sRegion,1) = CHR$(34) THEN sRegion = LEFT$(sRegion, LEN(sRegion) - 1) x = x2+1 x2 = INSTR(x, sLine, ",") sCity = MID$(sLine, x, x2 - x) IF LEFT$(sCity,1) = CHR$(34) THEN sCity = RIGHT$(sCity, LEN(sCity) - 1) IF RIGHT$(sCity,1) = CHR$(34) THEN sCity = LEFT$(sCity, LEN(sCity) - 1) i = i2 sTmp = PEEK$(VARPTR(dwCode),4) & sCountry & CHR$(0) & sRegion & CHR$(0) & sCity & CHR$(0) sTmp = CHR$(LEN(sTmp)) & sTmp PUT$ #1, sTmp INCR cnt LOOP i = LOF(1) CLOSE #1 STDOUT $CRLF & "Done! " & TRIM$(STR$(cnt)) & " locations saved. Filesize " & FORMAT$(i / 1024 / 1024, "0.0") & "mbs" STDOUT "Database conversion complete, ipcities.dat and ipranges.dat are ready for use." WAITKEY$ END FUNCTION
READER.BAS
Code:
#COMPILE EXE DECLARE FUNCTION inet_addr LIB "ws2_32.dll" ALIAS "inet_addr" (cp AS ASCIIZ) AS DWORD FUNCTION ShowLocation(BYVAL dwLoc AS DWORD, sBuf AS STRING) AS STRING LOCAL bPtr AS BYTE PTR, dwPtr AS DWORD PTR, szPtr AS ASCIIZ PTR, sOut AS STRING, sTmp AS STRING bPtr = STRPTR(sBuf) DO dwPtr = bPtr + 1 IF @dwPtr = dwLoc THEN szPtr = dwPtr + 4 IF LEN(@szPtr) > 0 THEN sOut = "Country: " & @szPtr szPtr = szPtr + LEN(@szPtr) + 1 IF LEN(@szPtr) > 0 THEN sOut = sOut & " Region: " & @szPtr szPtr = szPtr + LEN(@szPtr) + 1 IF LEN(@szPtr) > 0 THEN sOut = sOut & " City: " & @szPtr FUNCTION = sOut EXIT FUNCTION END IF bPtr = bPtr + @bPtr + 1 LOOP END FUNCTION FUNCTION PBMAIN() AS LONG LOCAL sPath AS STRING, sBuf AS STRING, sLocBuf AS STRING, i AS DWORD, i2 AS DWORD, cnt1 AS DWORD, hFile AS DWORD LOCAL sIP AS STRING, szIP AS ASCIIZ * 255, dwIP AS DWORD, dwPtr AS DWORD PTR, dwPtr2 AS DWORD PTR, dwLoc AS DWORD, dwEnd AS DWORD IF DIR$("ipcities.dat",39) = "" OR DIR$("ipranges.dat",39) = "" THEN STDOUT "Error: ipcities.dat/ipranges.dat not found": WAITKEY$ EXIT FUNCTION END IF STDOUT "Loading databases ... "; hFile = FREEFILE OPEN "ipranges.dat" FOR BINARY ACCESS READ AS #hFile GET$ #hFile, LOF(hFile), sBuf CLOSE #hFile hFile = FREEFILE OPEN "ipcities.dat" FOR BINARY ACCESS READ AS #hFile GET$ #hFile, LOF(hFile), sLocBuf CLOSE #hFile STDOUT "OK" DO STDOUT "IP Address: "; STDIN LINE szIP dwIP = inet_addr( szIP ) ! mov eax, dwIP ! bswap eax ! mov dwIP, eax IF dwIP => 167772160 AND dwIP <= 184549375 THEN '10.0.0.0 = 167772160 10.255.255.255 = 184549375 ReservedIP: STDOUT "Reserved/local" ITERATE ELSEIF dwIP => 2130706432 AND dwIP <= 2147483647 THEN '127.0.0.0 = 2130706432 127.255.255.255 = 2147483647 GOTO ReservedIP ELSEIF dwIP => 2886729728 AND dwIP <= 2887778303 THEN '172.16.0.0 = 2886729728 172.31.255.255 = 2887778303 GOTO ReservedIP ELSEIF dwIP => 3232235520 AND dwIP <= 3232301055 THEN '192.168.0.0 = 3232235520 192.168.255.255 = 3232301055 GOTO ReservedIP END IF dwLoc = 0 dwPtr = STRPTR(sBuf) dwEnd = dwPtr + LEN(sBuf) - 10 DO dwPtr2 = dwPtr + 4 IF dwIP => @dwPtr AND dwIP <= @dwPtr2 THEN dwPtr2 = dwPtr2 + 4 dwLoc = @dwPtr2 EXIT DO END IF dwPtr = dwPtr + 12 IF dwPtr => dwEnd THEN EXIT DO LOOP IF dwLoc = 0 THEN STDOUT "Unknown" ELSE STDOUT ShowLocation(BYVAL dwLoc, sLocBuf) END IF LOOP END FUNCTION
Under the license agreement, all advertising materials and documentation mentioning features or use of this database must display the following acknowledgment: "This product includes GeoLite data created by MaxMind, available from http://www.maxmind.com/."
Comment