natpmp 0.8 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
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