ip2location 8.2.1 → 8.3.0
raw patch · 4 files changed
+67/−95 lines, 4 files
Files
- ChangeLog.md +4/−0
- IP2Location.hs +60/−92
- LICENSE +1/−1
- ip2location.cabal +2/−2
ChangeLog.md view
@@ -1,5 +1,9 @@ # Revision history for ip2location +## 8.3.0 -- 2021-06-23++* Added support for address type and IAB category. Added exception handling for wrong BIN file.+ ## 8.2.1 -- 2020-04-22 * Modified version requirement for base module.
IP2Location.hs view
@@ -1,16 +1,16 @@ {-| Module : IP2Location Description : IP2Location Haskell package -Copyright : (c) IP2Location, 2020 +Copyright : (c) IP2Location, 2021 License : MIT Maintainer : sales@ip2location.com Stability : experimental -This Haskell package provides a fast lookup of country, region, city, latitude, longitude, ZIP code, time zone, ISP, domain name, connection type, -IDD code, area code, weather station code, weather station name, mcc, mnc, mobile brand, elevation, and usage type from IP address by using IP2Location database. +This Haskell package provides a fast lookup of country, region, city, latitude, longitude, ZIP code, time zone, ISP, domain name, connection type, IDD code, area code, +weather station code, weather station name, mcc, mnc, mobile brand, elevation, usage type, address type and IAB category from IP address by using IP2Location database. This package uses a file based database available at IP2Location.com. This database simply contains IP blocks as keys, and other information such as country, region, city, latitude, longitude, ZIP code, time zone, ISP, domain name, connection type, IDD code, area code, weather station code, weather station name, mcc, mnc, -mobile brand, elevation, and usage type as values. It supports both IP addresses in IPv4 and IPv6. +mobile brand, elevation, usage type, address type and IAB category as values. It supports both IP addresses in IPv4 and IPv6. IP2Location LITE BIN databases are available for free at http://lite.ip2location.com/ -} @@ -23,6 +23,7 @@ import Data.Binary.Get import Data.IP import Control.Exception +import System.Exit -- | Contains geolocation results. data IP2LocationRecord = IP2LocationRecord { @@ -65,7 +66,11 @@ -- | Elevation in meters elevation :: Float, -- | Usage type - usagetype :: String + usagetype :: String, + -- | Address type + addresstype :: String, + -- | Category + category :: String } deriving (Show) -- | Contains the BIN database file metadata. @@ -95,7 +100,9 @@ -- | IPv4 column size ipv4columnsize :: Int, -- | IPv6 column size - ipv6columnsize :: Int + ipv6columnsize :: Int, + -- | Wrong BIN + wrongbin :: Int } deriving (Show) getMeta = do @@ -110,16 +117,26 @@ ipv6databaseaddr <- getWord32le ipv4indexbaseaddr <- getWord32le ipv6indexbaseaddr <- getWord32le + productcode <- getWord8 + producttype <- getWord8 + filesize <- getWord32le + + -- check if is correct BIN (should be 1 for IP2Location BIN file), also checking for zipped file (PK being the first 2 chars) + let wrongbin = if (productcode /= 1 && databaseyear >= 21) || (databasetype == 80 && databasecolumn == 75) + then do + 1 + else do + 0 + let ipv4columnsize = fromIntegral databasecolumn `shiftL` 2 -- 4 bytes each column let ipv6columnsize = 16 + ((fromIntegral databasecolumn - 1) `shiftL` 2) -- 4 bytes each column, except IPFrom column which is 16 bytes - let meta = Meta (fromIntegral databasetype) (fromIntegral databasecolumn) (fromIntegral databaseyear) (fromIntegral databasemonth) (fromIntegral databaseday) (fromIntegral ipv4databasecount) (fromIntegral ipv4databaseaddr) (fromIntegral ipv6databasecount) (fromIntegral ipv6databaseaddr) (fromIntegral ipv4indexbaseaddr) (fromIntegral ipv6indexbaseaddr) ipv4columnsize ipv6columnsize + let meta = Meta (fromIntegral databasetype) (fromIntegral databasecolumn) (fromIntegral databaseyear) (fromIntegral databasemonth) (fromIntegral databaseday) (fromIntegral ipv4databasecount) (fromIntegral ipv4databaseaddr) (fromIntegral ipv6databasecount) (fromIntegral ipv6databaseaddr) (fromIntegral ipv4indexbaseaddr) (fromIntegral ipv6indexbaseaddr) ipv4columnsize ipv6columnsize wrongbin return meta - {-| The 'getAPIVersion' function returns a string containing the API version. -} getAPIVersion :: String -getAPIVersion = "8.2.1" +getAPIVersion = "8.3.0" ipToOcts :: IP -> [Int] ipToOcts (IPv4 ip) = fromIPv4 ip @@ -138,7 +155,13 @@ doInit :: String -> IO Meta doInit myfile = do contents <- BS.readFile myfile - return $ runGet getMeta contents + let stuff = runGet getMeta contents + let iswrong = (show (wrongbin stuff)) + if iswrong == "1" + then do + die(show "Incorrect IP2Location BIN file format. Please make sure that you are using the latest IP2Location BIN file.") + else do + return $ stuff readuint8 :: BS.ByteString -> Int -> Int readuint8 contents startpos = fromIntegral (runGet getWord8 (BS.drop (fromIntegral startpos - 1) contents)) @@ -158,9 +181,6 @@ readuint128 :: BS.ByteString -> Int -> Integer readuint128 contents startpos = runGet getuint128 (BS.drop (fromIntegral startpos - 1) contents) -readfloat :: BS.ByteString -> Int -> Float -readfloat contents startpos = runGet getFloatle (BS.drop (fromIntegral startpos - 1) contents) - readfloatrow :: BS.ByteString -> Int -> Float readfloatrow row startpos = runGet getFloatle (BS.drop (fromIntegral startpos) row) @@ -200,17 +220,6 @@ let x2 = readstr contents (x0 + 3) (x1, x2) -readcolstring :: BS.ByteString -> Int -> Int -> [Int] -> String -readcolstring contents dbtype rowoffset col = do - let [colpos] = take 1 (drop dbtype col) - - if colpos == 0 - then do - "This parameter is unavailable for selected data file. Please upgrade the data file." - else do - let coloffset = (colpos - 1) `shiftL` 2 - readstr contents (readuint32 contents (rowoffset + coloffset)) - readcolstringrow :: BS.ByteString -> BS.ByteString -> Int -> [Int] -> String readcolstringrow contents row dbtype col = do let [colpos] = take 1 (drop dbtype col) @@ -222,17 +231,6 @@ let coloffset = (colpos - 2) `shiftL` 2 readstr contents (readuint32row row coloffset) -readcolfloat :: BS.ByteString -> Int -> Int -> [Int] -> Float -readcolfloat contents dbtype rowoffset col = do - let [colpos] = take 1 (drop dbtype col) - - if colpos == 0 - then do - 0.0 - else do - let coloffset = (colpos - 1) `shiftL` 2 - readfloat contents (rowoffset + coloffset) - readcolfloatrow :: BS.ByteString -> Int -> [Int] -> Float readcolfloatrow row dbtype col = do let [colpos] = take 1 (drop dbtype col) @@ -244,18 +242,6 @@ let coloffset = (colpos - 2) `shiftL` 2 readfloatrow row coloffset -readcolfloatstring :: BS.ByteString -> Int -> Int -> [Int] -> Float -readcolfloatstring contents dbtype rowoffset col = do - let [colpos] = take 1 (drop dbtype col) - - if colpos == 0 - then do - 0.0 - else do - let coloffset = (colpos - 1) `shiftL` 2 - let n = readstr contents (readuint32 contents (rowoffset + coloffset)) - read n :: Float - readcolfloatstringrow :: BS.ByteString -> BS.ByteString -> Int -> [Int] -> Float readcolfloatstringrow contents row dbtype col = do let [colpos] = take 1 (drop dbtype col) @@ -273,50 +259,32 @@ readrecord :: BS.ByteString -> Int -> Int -> IP2LocationRecord readrecord contents dbtype rowoffset = do - let country_position = [0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] - let region_position = [0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] - let city_position = [0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] - let isp_position = [0, 0, 3, 0, 5, 0, 7, 5, 7, 0, 8, 0, 9, 0, 9, 0, 9, 0, 9, 7, 9, 0, 9, 7, 9] - let latitude_position = [0, 0, 0, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5] - let longitude_position = [0, 0, 0, 0, 0, 6, 6, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6] - let domain_position = [0, 0, 0, 0, 0, 0, 0, 6, 8, 0, 9, 0, 10,0, 10, 0, 10, 0, 10, 8, 10, 0, 10, 8, 10] - let zipcode_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 0, 7, 7, 7, 0, 7, 0, 7, 7, 7, 0, 7] - let timezone_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 7, 8, 8, 8, 7, 8, 0, 8, 8, 8, 0, 8] - let netspeed_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 11,0, 11,8, 11, 0, 11, 0, 11, 0, 11] - let iddcode_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 12, 0, 12, 0, 12, 9, 12, 0, 12] - let areacode_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10 ,13 ,0, 13, 0, 13, 10, 13, 0, 13] - let weatherstationcode_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 14, 0, 14, 0, 14, 0, 14] - let weatherstationname_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 15, 0, 15, 0, 15, 0, 15] - let mcc_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 16, 0, 16, 9, 16] - let mnc_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,17, 0, 17, 10, 17] - let mobilebrand_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,18, 0, 18, 11, 18] - let elevation_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 19, 0, 19] - let usagetype_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 20] + let country_position = [0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] + let region_position = [0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3] + let city_position = [0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4] + let isp_position = [0, 0, 3, 0, 5, 0, 7, 5, 7, 0, 8, 0, 9, 0, 9, 0, 9, 0, 9, 7, 9, 0, 9, 7, 9, 9] + let latitude_position = [0, 0, 0, 0, 0, 5, 5, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5] + let longitude_position = [0, 0, 0, 0, 0, 6, 6, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6] + let domain_position = [0, 0, 0, 0, 0, 0, 0, 6, 8, 0, 9, 0, 10,0, 10, 0, 10, 0, 10, 8, 10, 0, 10, 8, 10, 10] + let zipcode_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 7, 0, 7, 7, 7, 0, 7, 0, 7, 7, 7, 0, 7, 7] + let timezone_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 7, 8, 8, 8, 7, 8, 0, 8, 8, 8, 0, 8, 8] + let netspeed_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 11,0, 11,8, 11, 0, 11, 0, 11, 0, 11, 11] + let iddcode_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 12, 0, 12, 0, 12, 9, 12, 0, 12, 12] + let areacode_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10 ,13 ,0, 13, 0, 13, 10, 13, 0, 13, 13] + let weatherstationcode_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 14, 0, 14, 0, 14, 0, 14, 14] + let weatherstationname_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 15, 0, 15, 0, 15, 0, 15, 15] + let mcc_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 16, 0, 16, 9, 16, 16] + let mnc_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,17, 0, 17, 10, 17, 17] + let mobilebrand_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11,18, 0, 18, 11, 18, 18] + let elevation_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 19, 0, 19, 19] + let usagetype_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 20, 20] + let addresstype_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21] + let category_position = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22] - let allcols = (take 1 (drop dbtype country_position)) ++ (take 1 (drop dbtype region_position)) ++ (take 1 (drop dbtype city_position)) ++ (take 1 (drop dbtype isp_position)) ++ (take 1 (drop dbtype latitude_position)) ++ (take 1 (drop dbtype longitude_position)) ++ (take 1 (drop dbtype domain_position)) ++ (take 1 (drop dbtype zipcode_position)) ++ (take 1 (drop dbtype timezone_position)) ++ (take 1 (drop dbtype netspeed_position)) ++ (take 1 (drop dbtype iddcode_position)) ++ (take 1 (drop dbtype areacode_position)) ++ (take 1 (drop dbtype weatherstationcode_position)) ++ (take 1 (drop dbtype weatherstationname_position)) ++ (take 1 (drop dbtype mcc_position)) ++ (take 1 (drop dbtype mnc_position)) ++ (take 1 (drop dbtype mobilebrand_position)) ++ (take 1 (drop dbtype elevation_position)) ++ (take 1 (drop dbtype usagetype_position)) + let allcols = (take 1 (drop dbtype country_position)) ++ (take 1 (drop dbtype region_position)) ++ (take 1 (drop dbtype city_position)) ++ (take 1 (drop dbtype isp_position)) ++ (take 1 (drop dbtype latitude_position)) ++ (take 1 (drop dbtype longitude_position)) ++ (take 1 (drop dbtype domain_position)) ++ (take 1 (drop dbtype zipcode_position)) ++ (take 1 (drop dbtype timezone_position)) ++ (take 1 (drop dbtype netspeed_position)) ++ (take 1 (drop dbtype iddcode_position)) ++ (take 1 (drop dbtype areacode_position)) ++ (take 1 (drop dbtype weatherstationcode_position)) ++ (take 1 (drop dbtype weatherstationname_position)) ++ (take 1 (drop dbtype mcc_position)) ++ (take 1 (drop dbtype mnc_position)) ++ (take 1 (drop dbtype mobilebrand_position)) ++ (take 1 (drop dbtype elevation_position)) ++ (take 1 (drop dbtype usagetype_position)) ++ (take 1 (drop dbtype addresstype_position)) ++ (take 1 (drop dbtype category_position)) let cols = (countif (>0) allcols) `shiftL` 2 let row = BS.take (fromIntegral cols) (BS.drop (fromIntegral rowoffset - 1) contents) - -- let (country_short, country_long) = readcolcountry contents dbtype rowoffset country_position - -- let region = readcolstring contents dbtype rowoffset region_position - -- let city = readcolstring contents dbtype rowoffset city_position - -- let isp = readcolstring contents dbtype rowoffset isp_position - -- let latitude = readcolfloat contents dbtype rowoffset latitude_position - -- let longitude = readcolfloat contents dbtype rowoffset longitude_position - -- let domain = readcolstring contents dbtype rowoffset domain_position - -- let zipcode = readcolstring contents dbtype rowoffset zipcode_position - -- let timezone = readcolstring contents dbtype rowoffset timezone_position - -- let netspeed = readcolstring contents dbtype rowoffset netspeed_position - -- let iddcode = readcolstring contents dbtype rowoffset iddcode_position - -- let areacode = readcolstring contents dbtype rowoffset areacode_position - -- let weatherstationcode = readcolstring contents dbtype rowoffset weatherstationcode_position - -- let weatherstationname = readcolstring contents dbtype rowoffset weatherstationname_position - -- let mcc = readcolstring contents dbtype rowoffset mcc_position - -- let mnc = readcolstring contents dbtype rowoffset mnc_position - -- let mobilebrand = readcolstring contents dbtype rowoffset mobilebrand_position - -- let elevation = readcolfloatstring contents dbtype rowoffset elevation_position - -- let usagetype = readcolstring contents dbtype rowoffset usagetype_position - let (country_short, country_long) = readcolcountryrow contents row dbtype country_position let region = readcolstringrow contents row dbtype region_position let city = readcolstringrow contents row dbtype city_position @@ -336,8 +304,10 @@ let mobilebrand = readcolstringrow contents row dbtype mobilebrand_position let elevation = readcolfloatstringrow contents row dbtype elevation_position let usagetype = readcolstringrow contents row dbtype usagetype_position + let addresstype = readcolstringrow contents row dbtype addresstype_position + let category = readcolstringrow contents row dbtype category_position - IP2LocationRecord country_short country_long region city isp latitude longitude domain zipcode timezone netspeed iddcode areacode weatherstationcode weatherstationname mcc mnc mobilebrand elevation usagetype + IP2LocationRecord country_short country_long region city isp latitude longitude domain zipcode timezone netspeed iddcode areacode weatherstationcode weatherstationname mcc mnc mobilebrand elevation usagetype addresstype category searchtree :: BS.ByteString -> Integer -> Int -> Int -> Int -> Int -> Int -> Int -> IP2LocationRecord searchtree contents ipnum dbtype low high baseaddr colsize iptype = do @@ -359,10 +329,8 @@ then do if iptype == 4 then - -- readrecord contents dbtype rowoffset readrecord contents dbtype (rowoffset + 4) else - -- readrecord contents dbtype (rowoffset + 12) readrecord contents dbtype (rowoffset + 16) else if ipnum < ipfrom then @@ -371,7 +339,7 @@ searchtree contents ipnum dbtype (mid + 1) high baseaddr colsize iptype else do let x = "IP address not found." - IP2LocationRecord x x x x x 0.0 0.0 x x x x x x x x x x x 0.0 x + IP2LocationRecord x x x x x 0.0 0.0 x x x x x x x x x x x 0.0 x x x search4 :: BS.ByteString -> Integer -> Int -> Int -> Int -> Int -> Int -> Int -> IP2LocationRecord search4 contents ipnum dbtype low high baseaddr indexbaseaddr colsize = do @@ -422,7 +390,7 @@ if ipnum == -1 then do let x = "Invalid IP address." - return $ IP2LocationRecord x x x x x 0.0 0.0 x x x x x x x x x x x 0.0 x + return $ IP2LocationRecord x x x x x 0.0 0.0 x x x x x x x x x x x 0.0 x x x else if ipnum >= fromV4Mapped && ipnum <= toV4Mapped then do return $ search4 contents (ipnum - (toInteger fromV4Mapped)) (databasetype meta) 0 (ipv4databasecount meta) (ipv4databaseaddr meta) (ipv4indexbaseaddr meta) (ipv4columnsize meta)
LICENSE view
@@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 IP2Location.com +Copyright (c) 2021 IP2Location.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal
ip2location.cabal view
@@ -10,13 +10,13 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 8.2.1+version: 8.3.0 -- A short (one-line) description of the package. synopsis: IP2Location Haskell package for IP geolocation. -- A longer description of the package.-description: This Haskell package provides a fast lookup of country, region, city, latitude, longitude, ZIP code, time zone, ISP, domain name, connection type, IDD code, area code, weather station code, weather station name, mcc, mnc, mobile brand, elevation, and usage type from IP address by using IP2Location database. This package uses a file based database available at IP2Location.com. This database simply contains IP blocks as keys, and other information such as country, region, city, latitude, longitude, ZIP code, time zone, ISP, domain name, connection type, IDD code, area code, weather station code, weather station name, mcc, mnc, mobile brand, elevation, and usage type as values. It supports both IP addresses in IPv4 and IPv6.+description: This Haskell package provides a fast lookup of country, region, city, latitude, longitude, ZIP code, time zone, ISP, domain name, connection type, IDD code, area code, weather station code, weather station name, mcc, mnc, mobile brand, elevation, usage type, address type and IAB category from IP address by using IP2Location database. This package uses a file based database available at IP2Location.com. This database simply contains IP blocks as keys, and other information such as country, region, city, latitude, longitude, ZIP code, time zone, ISP, domain name, connection type, IDD code, area code, weather station code, weather station name, mcc, mnc, mobile brand, elevation, usage type, address type and IAB category as values. It supports both IP addresses in IPv4 and IPv6. -- URL for the project homepage or repository. homepage: http://www.ip2location.com