class NATPMP
Constants
- CLIENT_PORT
- DEFAULT_LIFETIME
- DELAY_MSEC
- MAX_WAIT_SEC
- OPCODE
- PMP_VERSION
- RETCODE
Return codes
- SERVER_PORT
- Version
Attributes
life[R]
mapped[R]
maxlife[R]
priv[R]
pub[R]
type[R]
Public Class Methods
GW()
click to toggle source
Determine the default gateway
# File lib/natpmp.rb, line 28 def self.GW return @gw if @gw @gw = case RUBY_PLATFORM when /darwin/ routes = `netstat -nrf inet`.split("\n").select{|l| l=~/^default/} raise "Can't find default route" unless routes.size > 0 routes.first.split(/\s+/)[1] when /linux/ routes = `ip route list match 0.0.0.0`.split("\n").select{|l| l =~ /^default/} raise "Can't find default route" unless routes.size > 0 routes.first.split(/\s+/)[2] else raise "Platform not supported!" end end
addr()
click to toggle source
Return the externally facing IPv4 address
# File lib/natpmp.rb, line 83 def self.addr reply = self.send [0, OPCODE[:addr]].pack("CC") sssoe = reply.unpack("x4N").first addr = reply.unpack("x8CCCC") return addr.join('.') end
map(priv, pub, maxlife = DEFAULT_LIFETIME, type = :tcp) { |map| ... }
click to toggle source
# File lib/natpmp.rb, line 125 def self.map priv, pub, maxlife = DEFAULT_LIFETIME, type = :tcp, &block map = NATPMP.new(priv, pub, block_given? ? nil: maxlife, type) map.request! if block_given? begin yield map ensure map.revoke! map = nil end end return map end
new(priv, pub, maxlife, type)
click to toggle source
# File lib/natpmp.rb, line 92 def initialize priv, pub, maxlife, type @priv = priv @pub = pub @maxlife = maxlife @type = type @renew = nil # Renewal thread # These are filled in when a request is made # @life = 0 @mapped = 0 end
send(msg)
click to toggle source
# File lib/natpmp.rb, line 52 def self.send msg sop = msg.unpack("xC").first sock = UDPSocket.open sock.connect(NATPMP.GW, SERVER_PORT) cb = sock.send(msg, 0) raise "Couldn't send!" unless cb == msg.size delay = DELAY_MSEC/1000.0 begin sleep delay # to give time for the response to arrive! (reply, sendinfo) = sock.recvfrom_nonblock(16) sender = Addrinfo.new sendinfo raise "Being spoofed!" unless sender.ip_address == NATPMP.GW (ver,op,res) = reply.unpack("CCn") raise "Invalid version #{ver}" unless ver == PMP_VERSION raise "Invalid reply opcode #{op}" unless op == 128 + sop raise "Request failed (code #{RETCODE.key(res)})" unless res == RETCODE[:success] return reply rescue IO::WaitReadable if delay < MAX_WAIT_SEC puts "Retrying after #{delay}..." if NATPMP.verbose? delay *= 2 retry end raise "Waited too long, got no response" rescue Errno::ECONNREFUSED raise "Remote NATPMP server not found" end end
verbose(flag)
click to toggle source
# File lib/natpmp.rb, line 44 def self.verbose flag @verbose = flag end
verbose?()
click to toggle source
# File lib/natpmp.rb, line 48 def self.verbose? return @verbose end
Public Instance Methods
inspect()
click to toggle source
# File lib/natpmp.rb, line 121 def inspect "#{NATPMP.GW}:#{@mapped}->#{@type}:#{@priv} (#{@life} sec)" end
request!()
click to toggle source
See section 3.3
# File lib/natpmp.rb, line 106 def request! rsp = NATPMP.send [0, OPCODE[@type], 0, @priv, @pub, @maxlife||DEFAULT_LIFETIME].pack("CCnnnN") (sssoe, priv, @mapped, @life) = rsp.unpack("x4NnnN") raise "Port mismatch: requested #{@priv} received #{priv}" if @priv != priv STDERR.puts "Mapped (at #{sssoe}) #{inspect}" schedule_renew! if @maxlife.nil? end
revoke!()
click to toggle source
See section 3.4
# File lib/natpmp.rb, line 115 def revoke! @renew.exit if @renew rsp = NATPMP.send [0, OPCODE[@type], 0, @priv, 0, 0].pack("CCnnnN") STDERR.puts "Revoked #{inspect}" if NATPMP.verbose? end
Private Instance Methods
schedule_renew!()
click to toggle source
As per section 3.3 re-issue the request using the actual mapped port
# File lib/natpmp.rb, line 144 def schedule_renew! Thread.abort_on_exception = true # As per section 3.3 re-issue the request using the actual mapped port @renew = Thread.new @life do |life| wait_time = life/2 STDERR.puts "Renewal in #{wait_time} seconds" if NATPMP.verbose? sleep wait_time rsp = NATPMP.send [0, OPCODE[@type], 0, @priv, @mapped, @life].pack("CCnnnN") (sssoe, priv, @mapped, @life) = rsp.unpack("x4NnnN") STDERR.puts "Renewed (at #{sssoe}) #{inspect}" if NATPMP.verbose? schedule_renew! end end