Class: NATPMP
- Inherits:
-
Object
- Object
- NATPMP
- Defined in:
- lib/natpmp.rb,
lib/natpmp/version.rb
Constant Summary collapse
- PMP_VERSION =
0
- DEFAULT_LIFETIME =
7200
- DELAY_MSEC =
250
- MAX_WAIT_SEC =
64
- SERVER_PORT =
5351
- CLIENT_PORT =
5350
- OPCODE =
{ addr: 0, udp: 1, tcp: 2 }
- RETCODE =
Return codes
{ success: 0, # Success unsupported: 1, # Unsupported Version refused: 2, # Not Authorized/Refused (e.g., box supports mapping, but user has turned feature off) failed: 3, # Network Failure (e.g., NAT box itself has not obtained a DHCP lease) exhausted: 4, # Out of resources (NAT box cannot create any more mappings at this time) opnotsupp: 5 # Unsupported opcode }
- VERSION =
'0.8'
Instance Attribute Summary collapse
-
#life ⇒ Object
readonly
Returns the value of attribute life.
-
#mapped ⇒ Object
readonly
Returns the value of attribute mapped.
-
#maxlife ⇒ Object
readonly
Returns the value of attribute maxlife.
-
#priv ⇒ Object
readonly
Returns the value of attribute priv.
-
#pub ⇒ Object
readonly
Returns the value of attribute pub.
-
#type ⇒ Object
readonly
Returns the value of attribute type.
Class Method Summary collapse
-
.addr ⇒ Object
Return the externally facing IPv4 address.
-
.GW ⇒ Object
Determine the default gateway.
- .map(priv, pub, maxlife = DEFAULT_LIFETIME, type = :tcp, &block) ⇒ Object
- .send(msg) ⇒ Object
- .verbose(flag = true) ⇒ Object
- .verbose? ⇒ Boolean
Instance Method Summary collapse
-
#initialize(priv, pub, maxlife, type) ⇒ NATPMP
constructor
A new instance of NATPMP.
- #inspect ⇒ Object
-
#request! ⇒ Object
See section 3.3.
-
#revoke! ⇒ Object
See section 3.4.
Constructor Details
#initialize(priv, pub, maxlife, type) ⇒ NATPMP
Returns a new instance of NATPMP.
92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/natpmp.rb', line 92 def initialize priv, pub, maxlife, type @priv = priv @pub = pub @maxlife = maxlife raise "Time must be >= 0" if maxlife < 0 @type = type # These are filled in when a request is made # @life = 0 @mapped = 0 end |
Instance Attribute Details
#life ⇒ Object (readonly)
Returns the value of attribute life.
90 91 92 |
# File 'lib/natpmp.rb', line 90 def life @life end |
#mapped ⇒ Object (readonly)
Returns the value of attribute mapped.
90 91 92 |
# File 'lib/natpmp.rb', line 90 def mapped @mapped end |
#maxlife ⇒ Object (readonly)
Returns the value of attribute maxlife.
90 91 92 |
# File 'lib/natpmp.rb', line 90 def maxlife @maxlife end |
#priv ⇒ Object (readonly)
Returns the value of attribute priv.
90 91 92 |
# File 'lib/natpmp.rb', line 90 def priv @priv end |
#pub ⇒ Object (readonly)
Returns the value of attribute pub.
90 91 92 |
# File 'lib/natpmp.rb', line 90 def pub @pub end |
#type ⇒ Object (readonly)
Returns the value of attribute type.
90 91 92 |
# File 'lib/natpmp.rb', line 90 def type @type end |
Class Method Details
.addr ⇒ Object
Return the externally facing IPv4 address
83 84 85 86 87 88 |
# 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 |
.GW ⇒ Object
Determine the default gateway
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# 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 |
.map(priv, pub, maxlife = DEFAULT_LIFETIME, type = :tcp, &block) ⇒ Object
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'lib/natpmp.rb', line 123 def self.map priv, pub, maxlife = DEFAULT_LIFETIME, type = :tcp, &block map = NATPMP.new(priv, pub, maxlife, type) map.request! if block_given? begin yield map ensure map.revoke! map = nil end end return map end |
.send(msg) ⇒ Object
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 |
# 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 = true) ⇒ Object
44 45 46 |
# File 'lib/natpmp.rb', line 44 def self.verbose flag = true @verbose = flag end |
.verbose? ⇒ Boolean
48 49 50 |
# File 'lib/natpmp.rb', line 48 def self.verbose? return @verbose end |
Instance Method Details
#inspect ⇒ Object
119 120 121 |
# File 'lib/natpmp.rb', line 119 def inspect "#{NATPMP.GW}:#{@mapped}->#{@type}:#{@priv} (#{@life} sec)" end |
#request! ⇒ Object
See section 3.3
106 107 108 109 110 111 |
# File 'lib/natpmp.rb', line 106 def request! rsp = NATPMP.send [0, OPCODE[@type], 0, @priv, @pub, @maxlife].pack("CCnnnN") (sssoe, priv, @mapped, @life) = rsp.unpack("x4NnnN") raise "Port mismatch: requested #{@priv} received #{priv}" if @priv != priv STDERR.puts "Mapped #{inspect}" if NATPMP.verbose end |