network.ip2country.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package main
  2. import (
  3. "encoding/binary"
  4. "encoding/csv"
  5. "encoding/json"
  6. "fmt"
  7. "log"
  8. "net"
  9. "net/http"
  10. "os"
  11. "strings"
  12. )
  13. type ipRange struct {
  14. start uint
  15. end uint
  16. country string
  17. }
  18. type ipReturnData struct {
  19. IP string
  20. Country string
  21. Status string
  22. }
  23. func network_ipToCountry_service_init() {
  24. log.Println("Starting ip2country service")
  25. http.HandleFunc("/SystemAO/network/getIPLocation", network_ipToCountry_getCurrentIPLocation)
  26. //http.HandleFunc("/SystemAO/network/getPing", network_info_getPing)
  27. /*
  28. //Register as a system setting
  29. registerSetting(settingModule{
  30. Name: "Network Info",
  31. Desc: "System Information",
  32. IconPath: "SystemAO/network/img/ethernet.png",
  33. Group: "Network",
  34. StartDir: "SystemAO/network/hardware.html",
  35. })
  36. */
  37. }
  38. func network_ipToCountry_getCurrentIPLocation(w http.ResponseWriter, r *http.Request) {
  39. /*
  40. if system_auth_chkauth(w, r) == false {
  41. sendErrorResponse(w, "User not logged in")
  42. return
  43. }
  44. */
  45. //Do not try to access IP information if under disable_ip_resolve_services mode
  46. if *disable_ip_resolve_services {
  47. data := ipReturnData{
  48. IP: "0.0.0.0",
  49. Country: "ZZ",
  50. Status: "Resolve Service Disabled",
  51. }
  52. JSONText, _ := json.Marshal(data)
  53. sendJSONResponse(w, string(JSONText))
  54. return
  55. }
  56. UserIP, _, err := net.SplitHostPort(string(r.RemoteAddr))
  57. if err != nil {
  58. log.Println(err)
  59. }
  60. data := ipReturnData{}
  61. if strings.Contains(UserIP, ":") {
  62. data.IP = UserIP
  63. data.Country = "ZZ"
  64. data.Status = "IPv6 not supported in current release"
  65. } else {
  66. data.IP = UserIP
  67. data.Country = network_ipToCountry_GetCountry(UserIP)
  68. data.Status = "OK"
  69. }
  70. JSONText, _ := json.Marshal(data)
  71. sendJSONResponse(w, string(JSONText))
  72. }
  73. //GetCountry returns the country which ip blongs to
  74. func network_ipToCountry_GetCountry(ip string) string {
  75. var arr []ipRange
  76. CSVFile := strings.Split(ip, ".")[0]
  77. lines, err := network_ipToCountry_ReadCsv("./system/ip2country/" + CSVFile + ".csv")
  78. if err != nil {
  79. panic(err)
  80. }
  81. // Loop through lines & turn into object
  82. for _, line := range lines {
  83. StartS := fmt.Sprintf("%s", line[0])
  84. Start, _ := network_ipToCountry_ipToInt(StartS)
  85. EndS := fmt.Sprintf("%s", line[1])
  86. End, _ := network_ipToCountry_ipToInt(EndS)
  87. data := ipRange{
  88. start: Start,
  89. end: End,
  90. country: line[2],
  91. }
  92. arr = append(arr, data)
  93. }
  94. ipNumb, err := network_ipToCountry_ipToInt(ip)
  95. if err != nil {
  96. return ""
  97. }
  98. index := network_ipToCountry_binarySearch(arr, ipNumb, 0, len(arr)-1)
  99. if index == -1 {
  100. return ""
  101. }
  102. return arr[index].country
  103. }
  104. func network_ipToCountry_binarySearch(arr []ipRange, hkey uint, low, high int) int {
  105. for low <= high {
  106. mid := low + (high-low)/2
  107. if hkey >= arr[mid].start && hkey <= arr[mid].end {
  108. return mid
  109. } else if hkey < arr[mid].start {
  110. high = mid - 1
  111. } else if hkey > arr[mid].end {
  112. low = mid + 1
  113. }
  114. }
  115. return -1
  116. }
  117. func network_ipToCountry_ipToInt(ips string) (uint, error) {
  118. ip := net.ParseIP(ips)
  119. if len(ip) == 16 {
  120. return uint(binary.BigEndian.Uint32(ip[12:16])), nil
  121. }
  122. return uint(binary.BigEndian.Uint32(ip)), nil
  123. }
  124. // ReadCsv accepts a file and returns its content as a multi-dimentional type
  125. // with lines and each column. Only parses to string type.
  126. func network_ipToCountry_ReadCsv(filename string) ([][]string, error) {
  127. // Open CSV file
  128. f, err := os.Open(filename)
  129. if err != nil {
  130. return [][]string{}, err
  131. }
  132. defer f.Close()
  133. // Read File into a Variable
  134. lines, err := csv.NewReader(f).ReadAll()
  135. if err != nil {
  136. return [][]string{}, err
  137. }
  138. return lines, nil
  139. }