Golang trie tree IP 轉國碼(Country Code)
最近因為有人 report 說 Zoraxy 的 GeoIP 功能不準,於是我在重新 review 這個 geoip database module 的時候發現:欸幹真的寫錯了欸!? 然後在花了一整個晚上 debug 之後,我終於找到正確的演算法和做法了。這裡為了省下後人在掘怎樣用 Golang 寫 IP 轉 Country Code 的 resolver,我這裡給大家一個快速上手的教學。 甚麼是 IP 轉 CC? IP 就是 IP 地址,CC 是 country code,有時稱為 ISO 國碼,例如說 HK TW US GB 之類的。IPv4 跟 v6 的地址區間通常會被 assign 到某一個國家的 ISP 上,而造成說只要知道 IP 地址,某程度上你可以知道那個 request 是從哪個國家出來的(先不要說 VPN 或是 ip spoofing 之類的部分的話)。可是由於每隔一段時間就會有 ISP 倒閉、收購或是重組之類的,所以 IP 轉 CC 的可靠度並非 100% 準確,這個時候我們就需要從一個可靠的來源來定期更新這一個 IP range 到 CC 的 mapping database 了。 這個 database 最出名的應該就是 maxmind 的 geoip database,人家都整理好所有數據到一個 database 檔案裡面,也提供 api 讓你可以快速查詢 cc,很多現代只會拉 library 的開發者通常就會直接選用它。然而,基於它的 license 問題,如果真接用它的方案的話會讓授權和 licensing 變得一團亂,所以基於多種考慮之下這個方案我們最終沒有使用,而是使用 Public domain 或 CC-0 的資料來源。 GeoIP Data Source 最後我們使用了來自 GeoFeed + Whois + ASN (CC-0)的資料來源: https://github.com/sapics/ip-location-db/tree/main/geolite2-country 問題是它的資料表是一個 csv 檔案,大概長這樣: 1.0.0.0,1.0.0.255,AU 1.0.1.0,1.0.3.255,CN 1.0.4.0,1.0.7.255,AU 1.0.8.0,1.0.15.255,CN 1.0.16.0,1.0.31.255,JP 1.0.32.0,1.0.63.255,CN 1.0.64.0,1.0.127.255,JP 1.0.128.0,1.0.255.255,TH 1.1.0.0,1.1.0.255,CN 1.1.1.0,1.1.1.255,AU 1.1.2.0,1.1.63.255,CN 1.1.64.0,1.1.127.255,JP .... 223.255.248.0,223.255.251.255,HK 223.255.252.0,223.255.253.255,CN 223.255.254.0,223.255.254.255,SG 223.255.255.0,223.255.255.255,AU 那假設我給你一個 ip 地址:A.B.C.D,你要怎樣把 country code 從 csv 裡面抓出來呢? 最直觀的答案:O(n) 當然如果會一點寫程式的人就會想到:那我把整個 csv loop 一次,看看哪一行的 start ip 跟 end ip 是包含我這個 ip 地址的就好了啦?這個的確是我們做給嵌入式裝置的做法(不過是為了省 memory),原理大概這樣: func isIPv4InRange(startIP, endIP, testIP string) (bool, error) { start := net.ParseIP(startIP) end := net.ParseIP(endIP) test := net.ParseIP(testIP) if start == nil || end == nil || test == nil {…