223 lines
5.1 KiB
Lua
223 lines
5.1 KiB
Lua
---
|
|
-- This library implements the basics of NAT-PMP as described in the
|
|
-- NAT Port Mapping Protocol (NAT-PMP) draft:
|
|
-- o http://tools.ietf.org/html/draft-cheshire-nat-pmp-03
|
|
--
|
|
--
|
|
-- @author Patrik Karlsson <patrik@cqure.net>
|
|
--
|
|
local datetime = require "datetime"
|
|
local ipOps = require "ipOps"
|
|
local nmap = require "nmap"
|
|
local stdnse = require "stdnse"
|
|
local string = require "string"
|
|
_ENV = stdnse.module("natpmp", stdnse.seeall)
|
|
|
|
local ResultCode = {
|
|
SUCCESS = 0,
|
|
UNSUPPORTED_VERSION = 1,
|
|
NOT_AUTHORIZED = 2,
|
|
NETWORK_FAILURE = 3,
|
|
OUT_OF_RESOURCES = 4,
|
|
UNSUPPORTED_OPCODE = 5,
|
|
}
|
|
|
|
local ErrorMessage = {
|
|
[ResultCode.UNSUPPORTED_VERSION] = "The device did not support the protocol version",
|
|
[ResultCode.NOT_AUTHORIZED] = "The operation was not authorized",
|
|
[ResultCode.NETWORK_FAILURE] = "Network failure",
|
|
[ResultCode.OUT_OF_RESOURCES] = "The device is out of resources",
|
|
[ResultCode.UNSUPPORTED_OPCODE] = "The requested operation was not supported",
|
|
}
|
|
|
|
|
|
Request = {
|
|
|
|
GetWANIP = {
|
|
|
|
new = function(self)
|
|
local o = { version = 0, op = 0 }
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
return o
|
|
end,
|
|
|
|
__tostring = function(self)
|
|
return string.pack(">BB", self.version, self.op)
|
|
end,
|
|
|
|
},
|
|
|
|
MapPort = {
|
|
|
|
new = function(self, pubport, privport, proto, lifetime)
|
|
assert(proto == "udp" or proto == "tcp", "Unsupported protocol")
|
|
local o = {
|
|
version = 0,
|
|
pubport = pubport,
|
|
privport = privport,
|
|
proto = proto,
|
|
lifetime = lifetime or 3600
|
|
}
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
return o
|
|
end,
|
|
|
|
__tostring = function(self)
|
|
return string.pack(">BBI2I2I2I4",
|
|
self.version,
|
|
(self.proto=="udp" and 1 or 2),
|
|
0, -- reserved
|
|
self.privport, self.pubport,
|
|
self.lifetime)
|
|
end,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Response = {
|
|
|
|
GetWANIP = {
|
|
|
|
new = function(self, data)
|
|
local o = { data = data }
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
if ( o:parse() ) then
|
|
return o
|
|
end
|
|
end,
|
|
|
|
parse = function(self)
|
|
if ( #self.data ~= 12 ) then
|
|
return
|
|
end
|
|
|
|
local pos
|
|
self.version, self.op, self.rescode, pos = string.unpack(">BBI2", self.data)
|
|
|
|
if ( self.rescode ~= ResultCode.SUCCESS or self.op ~= 128 ) then
|
|
return
|
|
end
|
|
|
|
self.time, self.ip, pos = string.unpack(">I4I4", self.data, pos)
|
|
self.ip = ipOps.fromdword(self.ip)
|
|
self.time = datetime.format_timestamp(self.time)
|
|
return true
|
|
end,
|
|
|
|
},
|
|
|
|
MapPort = {
|
|
|
|
new = function(self, data)
|
|
local o = { data = data }
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
if ( o:parse() ) then
|
|
return o
|
|
end
|
|
end,
|
|
|
|
parse = function(self)
|
|
if ( #self.data ~= 16 ) then
|
|
return
|
|
end
|
|
|
|
local pos
|
|
self.version, self.op, self.rescode, pos = string.unpack(">BBI2", self.data)
|
|
|
|
if ( self.rescode ~= ResultCode.SUCCESS ) then
|
|
return
|
|
end
|
|
|
|
self.time, self.privport, self.pubport, self.lifetime, pos = string.unpack(">I4I2I2I4", self.data, pos)
|
|
return true
|
|
end,
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Helper = {
|
|
|
|
new = function(self, host, port)
|
|
local o = { host = host, port = port }
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
return o
|
|
end,
|
|
|
|
exchPacket = function(self, data)
|
|
local socket = nmap.new_socket("udp")
|
|
socket:set_timeout(5000)
|
|
|
|
local status = socket:sendto(self.host, self.port, data)
|
|
if ( not(status) ) then
|
|
socket:close()
|
|
return false, "Failed to send request to device"
|
|
end
|
|
|
|
local response
|
|
status, response = socket:receive()
|
|
socket:close()
|
|
if ( not(status) ) then
|
|
return false, "Failed to receive response from router"
|
|
end
|
|
return true, response
|
|
end,
|
|
|
|
--- Gets the WAN ip of the router
|
|
getWANIP = function(self)
|
|
local packet = Request.GetWANIP:new()
|
|
local status, response = self:exchPacket(tostring(packet))
|
|
if ( not(status) ) then
|
|
return status, response
|
|
end
|
|
|
|
response = Response.GetWANIP:new(response)
|
|
if ( not(response) ) then
|
|
return false, "Failed to parse response from router"
|
|
end
|
|
|
|
return true, response
|
|
end,
|
|
|
|
--- Maps a public port to a private port
|
|
-- @param pubport number containing the public external port to map
|
|
-- @param privport number containing the private internal port to map
|
|
-- @param protocol string containing the protocol to map (udp|tcp)
|
|
-- @param lifetime [optional] number containing the lifetime in seconds
|
|
mapPort = function(self, pubport, privport, protocol, lifetime)
|
|
local packet = Request.MapPort:new(pubport, privport, protocol, lifetime)
|
|
local status, response = self:exchPacket(tostring(packet))
|
|
if ( not(status) ) then
|
|
return status, response
|
|
end
|
|
|
|
response = Response.MapPort:new(response)
|
|
if ( not(response) ) then
|
|
return false, "Failed to parse response from router"
|
|
end
|
|
|
|
return true, response
|
|
end,
|
|
|
|
unmapPort = function(self, pubport, privport)
|
|
return self:mapPort(pubport, privport, 0)
|
|
end,
|
|
|
|
unmapAllPorts = function(self)
|
|
return self.mapPort(0, 0, 0)
|
|
end,
|
|
|
|
}
|
|
|
|
return _ENV;
|