natpmp 0.8 → 0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b79a17201396b0daf760a7454fd50ca223fdd6ab
4
- data.tar.gz: db52b1791207f0330e968f380afdab5238160327
3
+ metadata.gz: 504f5dbfbf0b924f579d4cc21c3329c415bb0d66
4
+ data.tar.gz: 2d29eb61c29f70a0876c5592781518c3514064e3
5
5
  SHA512:
6
- metadata.gz: d287cc858eaecd16279e928acd2a63bc74c9c1f207494ec81d8480c25a74f86bba9ba37bc362da4a64d018fce9ee14d0a4961557d2837884f28abb333a24572d
7
- data.tar.gz: 7436eb03437d9a262c7a97df52d14a45dd1c0061ded6a6a9ba1566a7dee0fad5cedb107b72bf85a85606100bd9b0fa84354011f6202359160c7c835a9048dcad
6
+ metadata.gz: 933f2b1277663de3fe5ac5c7110c53ab4ae65161ff1459ad1a1fe4e684c109719540c537647a822e2ffb11de01bde089d49ccadba8f165caefa602a29153e5f2
7
+ data.tar.gz: d776e773dc7c29c404d896a040120efa19634ba0e88b8d8250e57c94d0f1cfa825539226c1d5ef054fc406751d9f23ee1dbc038e50a67b567b0443ce94cf99be
@@ -0,0 +1,52 @@
1
+ # Ruby Gem for NAT-PMP
2
+
3
+ This is a client implementation of [NAT-PMP](http://tools.ietf.org/html/rfc6886)
4
+
5
+ For a server implementation suitable for Linux see [Stallone](https://github.com/tedjp/stallone)
6
+
7
+ Upon request NAT-PMP can open either a UDP or TCP port on the gateway and bidirectionally relay traffic to a specific external server.
8
+
9
+ ## Usage
10
+ The GEM includes a command-line utility as well as Ruby classes and can be used in several ways
11
+
12
+ ### To open a mapping from the command line
13
+ To open a mapping on specific ports use:
14
+
15
+ natpmp -p 1234
16
+
17
+ This command will hold the port open for the requested time, whereafter it _may_ be closed. To extend the mapping simply re-open it using the same port number.
18
+
19
+ ### To run a command with a mapping
20
+ To run a command with specific port mappings:
21
+
22
+ natpmp -p 1234 'echo using the mapping && sleep 5'
23
+
24
+ The mapping will be held open until the command completes. The command string may contain the tokens %H, %P, %h and %p which will be substituted for the remote (uppercase) and local (lowercase) host (h) and ports (p). The ports can be automatically assigned by the command.
25
+
26
+ ### Example Ruby Code
27
+ To open a port and run code using it:
28
+
29
+ require 'natpmp'
30
+ NATPMP.map 633, 13033, 10, :tcp do |map|
31
+ ... # the mapping will be renewed until this exits
32
+ end
33
+
34
+ ### Command Line Utility
35
+ The command line utility has the following options:
36
+
37
+ usage: natpmp [options] [command]
38
+ Open a port on a NAT-PMP gateway. Options:
39
+ -t, --type TYPE Type: tcp, udp (default: tcp)
40
+ -p, --port PORT Private port (default: auto)
41
+ --ttl TIME TTL if no command (default 7200 sec)
42
+ -P, --public PUBPORT External port (default: auto)
43
+ -v, --verbose Verbose
44
+ --version Version
45
+ In the command string the following substitutions will be made:
46
+ %p the local port
47
+ %h the local IP address
48
+ %P the gateway port
49
+ %H the gateway IP address
50
+ (Use %% to avoid this)
51
+ The mapping will be closed on completion of the command
52
+ -?, --help Display this screen
data/bin/natpmp CHANGED
@@ -5,9 +5,6 @@ require 'natpmp/version'
5
5
  require 'ostruct'
6
6
  require 'optparse'
7
7
 
8
- # TODO Extend the mapping if the command takes longer
9
- # than the timeout.
10
-
11
8
  types = [:tcp, :udp]
12
9
 
13
10
  opts = OpenStruct.new(port: nil, type: :tcp, verbose: false, public: 0, ttl: 7200)
@@ -69,7 +66,7 @@ end
69
66
 
70
67
  if ARGV.size > 0
71
68
  command = ARGV.join(' ')
72
- NATPMP.map opts.port, opts.public, opts.ttl, opts.type do |map|
69
+ NATPMP.map opts.port, opts.public, nil, opts.type do |map|
73
70
  command.gsub! /(?<!%)%p/, map.priv.to_s
74
71
  command.gsub! /(?<!%)%h/, opts.localhost.to_s
75
72
  command.gsub! /(?<!%)%P/, map.mapped.to_s
@@ -79,7 +76,6 @@ if ARGV.size > 0
79
76
  system command
80
77
  end
81
78
  else
82
- puts "noarg"
83
79
  map = NATPMP.map opts.port, opts.public, opts.ttl, opts.type
84
80
  end
85
81
 
@@ -41,7 +41,7 @@ class NATPMP
41
41
  end
42
42
  end
43
43
 
44
- def self.verbose flag = true
44
+ def self.verbose flag
45
45
  @verbose = flag
46
46
  end
47
47
 
@@ -93,8 +93,8 @@ class NATPMP
93
93
  @priv = priv
94
94
  @pub = pub
95
95
  @maxlife = maxlife
96
- raise "Time must be >= 0" if maxlife < 0
97
96
  @type = type
97
+ @renew = nil # Renewal thread
98
98
 
99
99
  # These are filled in when a request is made
100
100
  #
@@ -104,16 +104,18 @@ class NATPMP
104
104
 
105
105
  # See section 3.3
106
106
  def request!
107
- rsp = NATPMP.send [0, OPCODE[@type], 0, @priv, @pub, @maxlife].pack("CCnnnN")
107
+ rsp = NATPMP.send [0, OPCODE[@type], 0, @priv, @pub, @maxlife||DEFAULT_LIFETIME].pack("CCnnnN")
108
108
  (sssoe, priv, @mapped, @life) = rsp.unpack("x4NnnN")
109
109
  raise "Port mismatch: requested #{@priv} received #{priv}" if @priv != priv
110
- STDERR.puts "Mapped #{inspect}" if NATPMP.verbose
110
+ STDERR.puts "Mapped (at #{sssoe}) #{inspect}" if NATPMP.verbose?
111
+ schedule_renew! if @maxlife.nil?
111
112
  end
112
113
 
113
114
  # See section 3.4
114
115
  def revoke!
116
+ @renew.exit if @renew
115
117
  rsp = NATPMP.send [0, OPCODE[@type], 0, @priv, 0, 0].pack("CCnnnN")
116
- STDERR.puts "Revoked #{inspect}" if NATPMP.verbose
118
+ STDERR.puts "Revoked #{inspect}" if NATPMP.verbose?
117
119
  end
118
120
 
119
121
  def inspect
@@ -122,7 +124,7 @@ class NATPMP
122
124
 
123
125
  def self.map priv, pub, maxlife = DEFAULT_LIFETIME, type = :tcp, &block
124
126
 
125
- map = NATPMP.new(priv, pub, maxlife, type)
127
+ map = NATPMP.new(priv, pub, block_given? ? nil: maxlife, type)
126
128
  map.request!
127
129
  if block_given?
128
130
  begin
@@ -135,4 +137,22 @@ class NATPMP
135
137
  return map
136
138
 
137
139
  end
140
+
141
+ private
142
+
143
+ # As per section 3.3 re-issue the request using the actual mapped port
144
+ def schedule_renew!
145
+ Thread.abort_on_exception = true
146
+ # As per section 3.3 re-issue the request using the actual mapped port
147
+ @renew = Thread.new @life do |life|
148
+ wait_time = life/2
149
+ STDERR.puts "Renewal in #{wait_time} seconds" if NATPMP.verbose?
150
+ sleep wait_time
151
+ rsp = NATPMP.send [0, OPCODE[@type], 0, @priv, @mapped, @life].pack("CCnnnN")
152
+ (sssoe, priv, @mapped, @life) = rsp.unpack("x4NnnN")
153
+ STDERR.puts "Renewed (at #{sssoe}) #{inspect}" if NATPMP.verbose?
154
+ schedule_renew!
155
+ end
156
+ end
157
+
138
158
  end
@@ -1,3 +1,3 @@
1
1
  class NATPMP
2
- VERSION='0.8'
2
+ VERSION='0.9'
3
3
  end
@@ -1,9 +1,26 @@
1
- # Simple smoke test
2
- # To be improved!
1
+ # Simple tests
2
+ # Only work if the network gateway has NAT-PMP
3
+ # TODO Mock out the gateway object
3
4
  #
4
- require 'natpmp.rb'
5
+ require 'test/unit'
6
+ require 'natpmp'
5
7
 
6
- NATPMP.map 633, 13033, 30, :tcp do |map|
7
- puts "Executing sleep 10 with #{map.inspect}"
8
- sleep 10
8
+ class TestNATPMP < Test::Unit::TestCase
9
+ def test_gw
10
+ gw = NATPMP.GW
11
+ assert_match(/^(\d{1,3}\.){3}\d{1,3}$/, gw, "Return IPv4 address")
12
+ end
13
+ def test_basic
14
+ map = NATPMP.map 633, 13033, 30, :tcp
15
+ assert_equal(633, map.priv)
16
+ assert_equal(13033, map.pub)
17
+ assert_equal(30, map.life)
18
+ assert_equal(:tcp, map.type)
19
+ end
20
+ def test_block
21
+ m = NATPMP.map 633, 13033, 10, :tcp do |map|
22
+ assert_equal(NATPMP::DEFAULT_LIFETIME, map.life)
23
+ end
24
+ assert_nil(m)
25
+ end
9
26
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: natpmp
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.8'
4
+ version: '0.9'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Townsend
@@ -18,6 +18,7 @@ executables:
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
+ - README.md
21
22
  - lib/natpmp.rb
22
23
  - bin/natpmp
23
24
  - lib/natpmp/version.rb