summaryrefslogtreecommitdiffstats
path: root/nselib/geoip.lua
blob: d8e9c1c770eee012266bddd7022ce390631429a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
local nmap = require "nmap"
local stdnse = require "stdnse"
local table = require "table"
local coroutine = require "coroutine"

_ENV = stdnse.module("geoip", stdnse.seeall)

---
-- Consolidation of GeoIP functions.
--
-- @author "Mak Kolybabi <mak@kolybabi.com>"
-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html

--- Add a geolocation to the registry
-- @param ip The IP that was geolocated
-- @param lat The latitude in degrees
-- @param lon The longitude in degrees
add = function(ip, lat, lon)
  local lat_n = tonumber(lat)
  if not lat_n or lat_n < -90 or lat_n > 90 then
    stdnse.debug1("Invalid latitude for %s: %s.", ip, lat)
    return
  end

  local lon_n = tonumber(lon)
  if not lat_n or lon_n < -180 or lon_n > 180 then
    stdnse.debug1("Invalid longitude for %s: %s.", ip, lon)
    return
  end

  if not nmap.registry.geoip then
    nmap.registry.geoip = {}
  end

  nmap.registry.geoip[ip] = {
    latitude = lat,
    longitude = lon
  }
end

--- Check if any coordinates have been stored in the registry
--@return True if any coordinates have been returned, false otherwise
empty = function()
  return not nmap.registry.geoip
end

--- Retrieve the table of coordinates by IP
--@return A table of coordinates keyed by IP.
get_all_by_ip = function()
  if empty() then
    return nil
  end

  return nmap.registry.geoip
end

--- Retrieve a table of IPs by coordinate
--@return A table of IPs keyed by coordinate in <code>lat,lon</code> format
get_all_by_gps = function()
  if empty() then
    return nil
  end

  local t = {}
  for ip, coords in pairs(get_all_by_ip()) do
    local key = coords["latitude"] .. "," .. coords["longitude"]
    if not t[key] then
      t[key] = {}
    end
    table.insert(t[key], ip)
  end

  return t
end

-- Order in which field names will be shown in XML
local field_order = {
  "latitude",
  "longitude",
  "city",
  "region",
  "country"
}

--- Location object
--
-- The object supports setting the following fields using functions like
-- <code>set_fieldname</code>:
-- * latitude
-- * longitude
-- * city
-- * region
-- * country
--
-- The location object is suitable for returning from a script, and will
-- produce appropriate string and structured XML output.
Location = {
  new = function(self,o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
  end,

  -- Ensure fields are put in the XML in proper order
  __pairs = function(self)
    local function iterator ()
      for i, key in ipairs(field_order) do
        coroutine.yield(key, self[key])
      end
    end
    return coroutine.wrap(iterator)
  end,

  __tostring = function(self)
    local out = {
      ("coordinates: %s, %s"):format(self.latitude, self.longitude)
    }
    -- if any of these are nil, it doesn't increase #place
    local place = {self.city}
    place[#place+1] = self.region
    place[#place+1] = self.country
    if #place > 0 then
      out[#out+1] = ("location: %s"):format(table.concat(place, ", "))
    end

    return table.concat(out, "\n")
  end,
}

-- Generate setter functions
for _, field in ipairs(field_order) do
  Location["set_" .. field] = function(self, value)
    self[field] = value
  end
end

return _ENV;