i2p 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ * Arto Bendiken <arto.bendiken@gmail.com>
File without changes
data/README ADDED
@@ -0,0 +1,155 @@
1
+ I2P.rb: Anonymous Networking for Ruby
2
+ =====================================
3
+
4
+ This is a Ruby library for interacting with the [I2P][] anonymity network.
5
+
6
+ * <http://github.com/bendiken/i2p-ruby>
7
+
8
+ Features
9
+ --------
10
+
11
+ * Supports checking whether I2P is installed in the user's current `PATH`
12
+ and whether the I2P router is currently running.
13
+ * Supports starting, restarting and stopping the I2P router daemon.
14
+ * Implements the [I2P Basic Open Bridge (BOB)][BOB] protocol.
15
+ * Implements the basics of the [I2P Simple Anonymous Messaging (SAM)][SAM]
16
+ protocol.
17
+ * Supports I2P name resolution using both `hosts.txt` as well as SAM.
18
+ * Compatible with Ruby 1.8.7+, Ruby 1.9.x, and JRuby 1.4/1.5.
19
+ * Bundles the I2P 0.8 [SDK][] and [Streaming Library][Streaming] for use
20
+ with [JRuby][],
21
+
22
+ Examples
23
+ --------
24
+
25
+ require 'rubygems'
26
+ require 'i2p'
27
+
28
+ ### Checking whether an I2P router is running locally
29
+
30
+ I2P.available? #=> true, if the I2P router is installed
31
+ I2P.running? #=> true, if the I2P router is running
32
+
33
+ ### Starting and stopping the local I2P router daemon
34
+
35
+ I2P.start! #=> executes `i2prouter start`
36
+ I2P.restart! #=> executes `i2prouter restart`
37
+ I2P.stop! #=> executes `i2prouter stop`
38
+
39
+ ### Looking up the public key for an I2P name from hosts.txt
40
+
41
+ puts I2P::Hosts["forum.i2p"].to_base64
42
+
43
+ ### Looking up the public key for an I2P name using SAM
44
+
45
+ I2P::SAM::Client.open(:port => 7656) do |sam|
46
+ puts sam.lookup_name("forum.i2p").to_base64
47
+ end
48
+
49
+ ### Generating a new key pair and I2P destination using SAM
50
+
51
+ I2P::SAM::Client.open(:port => 7656) do |sam|
52
+ key_pair = sam.generate_destination
53
+ puts key_pair.destination.to_base64
54
+ end
55
+
56
+ ### Creating an inproxy tunnel to an eepsite using BOB
57
+
58
+ I2P::BOB::Tunnel.start(:inport => 12345) do |tunnel|
59
+ sleep 0.1 until tunnel.running?
60
+
61
+ TCPSocket.open("127.0.0.1", 12345) do |socket|
62
+ socket.puts "bob.i2p" # the I2P destination
63
+
64
+ socket.write "HEAD / HTTP/1.1\r\n\r\n"
65
+ socket.flush
66
+ until (line = socket.readline).chomp.empty?
67
+ puts line
68
+ end
69
+ socket.close
70
+ end
71
+ end
72
+
73
+ ### Creating an outproxy tunnel to your SSH daemon using BOB
74
+
75
+ tunnel = I2P::BOB::Tunnel.start({
76
+ :nickname => :myssh,
77
+ :outhost => "127.0.0.1",
78
+ :outport => 22, # SSH port
79
+ :quiet => true,
80
+ })
81
+ puts tunnel.destination.to_base64
82
+
83
+ ### Using the I2P SDK and Streaming Library directly from JRuby
84
+
85
+ I2P.rb bundles the public-domain [I2P SDK][SDK] (`i2p/sdk.jar`) and
86
+ [Streaming Library][Streaming] (`i2p/streaming.jar`) archives, which means
87
+ that to [script][JRuby howto] the I2P Java client implementation from
88
+ [JRuby][], you need only require these two files as follows:
89
+
90
+ require 'i2p/sdk.jar'
91
+ require 'i2p/streaming.jar'
92
+
93
+ Documentation
94
+ -------------
95
+
96
+ * <http://cypherpunk.rubyforge.org/i2p/>
97
+
98
+ Dependencies
99
+ ------------
100
+
101
+ * [Ruby](http://ruby-lang.org/) (>= 1.8.7) or (>= 1.8.1 with [Backports][])
102
+ * [I2P](http://www.i2p2.de/download.html) (>= 0.8)
103
+
104
+ Installation
105
+ ------------
106
+
107
+ The recommended installation method is via [RubyGems](http://rubygems.org/).
108
+ To install the latest official release of I2P.rb, do:
109
+
110
+ % [sudo] gem install i2p # Ruby 1.8.7+ or 1.9.x
111
+ % [sudo] gem install backports i2p # Ruby 1.8.1+
112
+
113
+ Environment
114
+ -----------
115
+
116
+ The following are the default values for environment variables that let
117
+ you customize I2P.rb's implicit configuration:
118
+
119
+ $ export I2P_PATH=$PATH
120
+ $ export I2P_BOB_HOST=127.0.0.1
121
+ $ export I2P_BOB_PORT=2827
122
+ $ export I2P_SAM_HOST=127.0.0.1
123
+ $ export I2P_SAM_PORT=7656
124
+
125
+ Download
126
+ --------
127
+
128
+ To get a local working copy of the development repository, do:
129
+
130
+ % git clone git://github.com/bendiken/i2p-ruby.git
131
+
132
+ Alternatively, you can download the latest development version as a tarball
133
+ as follows:
134
+
135
+ % wget http://github.com/bendiken/i2p-ruby/tarball/master
136
+
137
+ Author
138
+ ------
139
+
140
+ * [Arto Bendiken](mailto:arto.bendiken@gmail.com) - <http://ar.to/>
141
+
142
+ License
143
+ -------
144
+
145
+ I2P.rb is free and unencumbered public domain software. For more
146
+ information, see <http://unlicense.org/> or the accompanying UNLICENSE file.
147
+
148
+ [I2P]: http://www.i2p2.de/
149
+ [SDK]: http://www.i2p2.de/package-client.html
150
+ [Streaming]: http://www.i2p2.de/package-streaming.html
151
+ [SAM]: http://www.i2p2.de/samv3.html
152
+ [BOB]: http://bob.i2p.to/bridge.htm
153
+ [JRuby]: http://jruby.org/
154
+ [JRuby howto]: http://kenai.com/projects/jruby/pages/CallingJavaFromJRuby
155
+ [Backports]: http://rubygems.org/gems/backports
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.4
@@ -0,0 +1,177 @@
1
+ require 'pathname'
2
+ require 'socket'
3
+ require 'stringio'
4
+
5
+ if RUBY_VERSION < '1.8.7'
6
+ # @see http://rubygems.org/gems/backports
7
+ begin
8
+ require 'backports/1.8.7'
9
+ rescue LoadError
10
+ begin
11
+ require 'rubygems'
12
+ require 'backports/1.8.7'
13
+ rescue LoadError
14
+ abort "I2P.rb requires Ruby 1.8.7 or the Backports gem (hint: `gem install backports')."
15
+ end
16
+ end
17
+ end
18
+
19
+ ##
20
+ # @example Checking whether an I2P router is running locally
21
+ # I2P.available? #=> true, if the I2P router is installed
22
+ # I2P.running? #=> true, if the I2P router is running
23
+ #
24
+ # @example Starting and stopping the local I2P router daemon
25
+ # I2P.start! #=> executes `i2prouter start`
26
+ # I2P.restart! #=> executes `i2prouter restart`
27
+ # I2P.stop! #=> executes `i2prouter stop`
28
+ #
29
+ # @see http://www.i2p2.de/download.html
30
+ module I2P
31
+ # Name resolution
32
+ autoload :Hosts, 'i2p/hosts'
33
+
34
+ # Data structures
35
+ autoload :Structure, 'i2p/data/structure'
36
+ autoload :Certificate, 'i2p/data/certificate'
37
+ autoload :Key, 'i2p/data/key'
38
+ autoload :PrivateKey, 'i2p/data/private_key'
39
+ autoload :SigningPrivateKey, 'i2p/data/signing_private_key'
40
+ autoload :PublicKey, 'i2p/data/public_key'
41
+ autoload :SigningPublicKey, 'i2p/data/signing_public_key'
42
+ autoload :Destination, 'i2p/data/destination'
43
+ autoload :KeyPair, 'i2p/data/key_pair'
44
+
45
+ # Client protocols
46
+ autoload :BOB, 'i2p/bob'
47
+ autoload :SAM, 'i2p/sam'
48
+
49
+ # Miscellaneous
50
+ autoload :VERSION, 'i2p/version'
51
+
52
+ # The path used to locate the `i2prouter` executable.
53
+ PATH = (ENV['I2P_PATH'] || ENV['PATH']).split(File::PATH_SEPARATOR) unless defined?(PATH)
54
+
55
+ ##
56
+ # Returns `true` if I2P is available, `false` otherwise.
57
+ #
58
+ # This attempts to locate the `i2prouter` executable in the user's current
59
+ # `PATH` environment.
60
+ #
61
+ # @example
62
+ # I2P.available? #=> true
63
+ #
64
+ # @return [Boolean]
65
+ def self.available?
66
+ !!program_path
67
+ end
68
+
69
+ ##
70
+ # Returns `true` if the I2P router is running locally, `false` otherwise.
71
+ #
72
+ # This first attempts to call `i2prouter status` if the executable can be
73
+ # located in the user's current `PATH` environment, falling back to
74
+ # attempting to establish a Simple Anonymous Messaging (SAM) protocol
75
+ # connection to the standard SAM port 7656 on `localhost`.
76
+ #
77
+ # If I2P isn't in the `PATH` and hasn't been configured with SAM enabled,
78
+ # this will return `false` regardless of whether I2P actually is running
79
+ # or not.
80
+ #
81
+ # @example
82
+ # I2P.running? #=> false
83
+ #
84
+ # @return [Boolean]
85
+ def self.running?
86
+ if available?
87
+ /is running/ === `#{program_path} status`.chomp
88
+ else
89
+ begin
90
+ I2P::SAM::Client.open.disconnect
91
+ true
92
+ rescue Errno::ECONNREFUSED
93
+ false
94
+ end
95
+ end
96
+ end
97
+
98
+ ##
99
+ # Starts the local I2P router daemon.
100
+ #
101
+ # Returns the process identifier (PID) if the I2P router daemon was
102
+ # successfully started, `nil` otherwise.
103
+ #
104
+ # This relies on being able to execute `i2prouter start`, which requires
105
+ # the `i2prouter` executable to be located in the user's current `PATH`
106
+ # environment.
107
+ #
108
+ # @return [Integer]
109
+ # @since 0.1.1
110
+ def self.start!
111
+ if available?
112
+ `#{program_path} start` unless running?
113
+ `#{program_path} status` =~ /is running \((\d+)\)/ ? $1.to_i : nil
114
+ end
115
+ end
116
+
117
+ ##
118
+ # Restarts the local I2P router daemon, starting it in case it wasn't
119
+ # already running.
120
+ #
121
+ # Returns `true` if the I2P router daemon was successfully restarted,
122
+ # `false` otherwise.
123
+ #
124
+ # This relies on being able to execute `i2prouter restart`, which requires
125
+ # the `i2prouter` executable to be located in the user's current `PATH`
126
+ # environment.
127
+ #
128
+ # @return [Boolean]
129
+ # @since 0.1.1
130
+ def self.restart!
131
+ if available?
132
+ /Starting I2P Service/ === `#{program_path} restart`
133
+ end
134
+ end
135
+
136
+ ##
137
+ # Stops the local I2P router daemon.
138
+ #
139
+ # Returns `true` if the I2P router daemon was successfully shut down,
140
+ # `false` otherwise.
141
+ #
142
+ # This relies on being able to execute `i2prouter stop`, which requires
143
+ # the `i2prouter` executable to be located in the user's current `PATH`
144
+ # environment.
145
+ #
146
+ # @return [Boolean]
147
+ # @since 0.1.1
148
+ def self.stop!
149
+ if available?
150
+ /Stopped I2P Service/ === `#{program_path} stop`
151
+ end
152
+ end
153
+
154
+ ##
155
+ # Returns the path to the `i2prouter` executable.
156
+ #
157
+ # Returns `nil` if the program could not be located in any of the
158
+ # directories denoted by the user's current `I2P_PATH` or `PATH`
159
+ # environment variables.
160
+ #
161
+ # @example
162
+ # I2P.program_path #=> "/opt/local/bin/i2prouter"
163
+ #
164
+ # @param [String, #to_s] program_name
165
+ # @return [Pathname]
166
+ def self.program_path(program_name = :i2prouter)
167
+ program_name = program_name.to_s
168
+ @program_paths ||= {}
169
+ @program_paths[program_name] ||= begin
170
+ PATH.find do |dir|
171
+ if File.executable?(file = File.join(dir, program_name))
172
+ break Pathname(file)
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,27 @@
1
+ module I2P
2
+ ##
3
+ # **I2P Basic Open Bridge (BOB) protocol.**
4
+ #
5
+ # This is an implementation of the BOB protocol, available since I2P
6
+ # release [0.6.5](http://www.i2p2.de/release-0.6.5.html).
7
+ #
8
+ # Note that for security reasons, the BOB application bridge is not
9
+ # enabled by default in new I2P installations. To use `I2P::BOB`,
10
+ # you must first manually enable BOB in the router console's
11
+ # [client configuration](http://localhost:7657/configclients.jsp).
12
+ #
13
+ # @see http://www.i2p2.de/applications.html
14
+ # @see http://bob.i2p.to/bridge.html
15
+ module BOB
16
+ PROTOCOL_VERSION = 1
17
+ DEFAULT_HOST = (ENV['I2P_BOB_HOST'] || '127.0.0.1').to_s
18
+ DEFAULT_PORT = (ENV['I2P_BOB_PORT'] || 2827).to_i
19
+
20
+ autoload :Client, 'i2p/bob/client'
21
+ autoload :Tunnel, 'i2p/bob/tunnel'
22
+
23
+ ##
24
+ # **I2P Basic Open Bridge (BOB) protocol error conditions.**
25
+ class Error < StandardError; end
26
+ end
27
+ end
@@ -0,0 +1,489 @@
1
+ module I2P; module BOB
2
+ ##
3
+ # **I2P Basic Open Bridge (BOB) client.**
4
+ #
5
+ # @example Connecting to the I2P BOB bridge (1)
6
+ # bob = I2P::BOB::Client.new(:port => 2827)
7
+ #
8
+ # @example Connecting to the I2P BOB bridge (2)
9
+ # I2P::BOB::Client.open(:port => 2827) do |bob|
10
+ # ...
11
+ # end
12
+ #
13
+ # @example Generating a new destination
14
+ # I2P::BOB::Client.open(:nickname => :foo) do |bob|
15
+ # bob.newkeys
16
+ # end
17
+ #
18
+ # @example Generating a new key pair
19
+ # I2P::BOB::Client.open(:nickname => :foo) do |bob|
20
+ # bob.newkeys
21
+ # bob.getkeys
22
+ # end
23
+ #
24
+ # @see http://www.i2p2.de/applications.html
25
+ # @see http://bob.i2p.to/bridge.html
26
+ # @since 0.1.4
27
+ class Client
28
+ ##
29
+ # Establishes a connection to the BOB bridge.
30
+ #
31
+ # @example Connecting to the default port
32
+ # bob = I2P::BOB::Client.open
33
+ #
34
+ # @example Connecting to the given port
35
+ # bob = I2P::BOB::Client.open(:port => 2827)
36
+ #
37
+ # @param [Hash{Symbol => Object}] options
38
+ # @option options [String, #to_s] :host (DEFAULT_HOST)
39
+ # @option options [Integer, #to_i] :port (DEFAULT_PORT)
40
+ # @yield [client]
41
+ # @yieldparam [Client] client
42
+ # @return [void]
43
+ def self.open(options = {}, &block)
44
+ client = self.new(options)
45
+ client.connect
46
+
47
+ if options[:nickname]
48
+ begin
49
+ client.getnick(options[:nickname])
50
+ rescue Error => e
51
+ client.setnick(options[:nickname])
52
+ end
53
+ end
54
+
55
+ client.setkeys(options[:keys]) if options[:keys]
56
+ client.quiet(options[:quiet]) if options[:quiet]
57
+ client.inhost(options[:inhost]) if options[:inhost]
58
+ client.inport(options[:inport]) if options[:inport]
59
+ client.outhost(options[:outhost]) if options[:outhost]
60
+ client.outport(options[:outport]) if options[:outport]
61
+
62
+ unless block_given?
63
+ client
64
+ else
65
+ begin
66
+ result = case block.arity
67
+ when 1 then block.call(client)
68
+ else client.instance_eval(&block)
69
+ end
70
+ ensure
71
+ client.disconnect
72
+ end
73
+ result
74
+ end
75
+ end
76
+
77
+ ##
78
+ # Returns the socket connection to the BOB bridge.
79
+ #
80
+ # @return [TCPSocket]
81
+ attr_reader :socket
82
+
83
+ ##
84
+ # Returns the host name or IP address of the BOB bridge.
85
+ #
86
+ # @return [String]
87
+ attr_reader :host
88
+
89
+ ##
90
+ # Returns the port number of the BOB bridge.
91
+ #
92
+ # @return [Integer]
93
+ attr_reader :port
94
+
95
+ ##
96
+ # Initializes a new client instance.
97
+ #
98
+ # @param [Hash{Symbol => Object}] options
99
+ # @option options [String, #to_s] :host (DEFAULT_HOST)
100
+ # @option options [Integer, #to_i] :port (DEFAULT_PORT)
101
+ # @yield [client]
102
+ # @yieldparam [Client] client
103
+ def initialize(options = {}, &block)
104
+ @options = options.dup
105
+ @host = (@options.delete(:host) || DEFAULT_HOST).to_s
106
+ @port = (@options.delete(:port) || DEFAULT_PORT).to_i
107
+
108
+ if block_given?
109
+ case block.arity
110
+ when 1 then block.call(self)
111
+ else instance_eval(&block)
112
+ end
113
+ end
114
+ end
115
+
116
+ ##
117
+ # Returns `true` if a connection to the BOB bridge has been established
118
+ # and is active.
119
+ #
120
+ # @example
121
+ # bob.connected? #=> true
122
+ # bob.disconnect
123
+ # bob.connected? #=> false
124
+ #
125
+ # @return [Boolean]
126
+ def connected?
127
+ !!@socket
128
+ end
129
+
130
+ ##
131
+ # Establishes a connection to the BOB bridge.
132
+ #
133
+ # If called after the connection has already been established,
134
+ # disconnects and then reconnects to the bridge.
135
+ #
136
+ # @example
137
+ # bob.connect
138
+ #
139
+ # @return [void]
140
+ def connect
141
+ disconnect if connected?
142
+ @socket = TCPSocket.new(@host, @port)
143
+ @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
144
+ read_line # "BOB 00.00.0D"
145
+ read_line # "OK"
146
+ self
147
+ end
148
+ alias_method :reconnect, :connect
149
+
150
+ ##
151
+ # Closes the connection to the BOB bridge.
152
+ #
153
+ # If called after the connection has already been closed, does nothing.
154
+ #
155
+ # @example
156
+ # bob.disconnect
157
+ #
158
+ # @return [void]
159
+ def disconnect
160
+ @socket.close if @socket && !@socket.closed?
161
+ @socket = nil
162
+ self
163
+ end
164
+ alias_method :close, :disconnect
165
+
166
+ ##
167
+ # Closes the connection to the BOB bridge cleanly.
168
+ #
169
+ # @example
170
+ # bob.quit
171
+ #
172
+ # @return [void]
173
+ def quit
174
+ send_command(:quit)
175
+ read_response # "Bye!"
176
+ disconnect
177
+ end
178
+ alias_method :quit!, :quit
179
+
180
+ ##
181
+ # Verifies a Base64-formatted key pair or destination, returning `true`
182
+ # for valid input.
183
+ #
184
+ # @example
185
+ # bob.verify("foobar") #=> false
186
+ # bob.verify(I2P::Hosts["forum.i2p"]) #=> true
187
+ #
188
+ # @param [#to_base64, #to_s] data
189
+ # @return [Boolean]
190
+ def verify(data)
191
+ send_command(:verify, data.respond_to?(:to_base64) ? data.to_base64 : data.to_s)
192
+ read_response rescue false
193
+ end
194
+
195
+ ##
196
+ # Creates a new tunnel with the given nickname.
197
+ #
198
+ # @example
199
+ # bob.setnick(:foo)
200
+ #
201
+ # @param [String, #to_s] nickname
202
+ # @return [void]
203
+ def setnick(nickname)
204
+ send_command(:setnick, @options[:nickname] = nickname.to_s)
205
+ read_response # "Nickname set to #{nickname}"
206
+ self
207
+ end
208
+ alias_method :nickname=, :setnick
209
+
210
+ ##
211
+ # Selects an existing tunnel with the given nickname.
212
+ #
213
+ # @example
214
+ # bob.getnick(:foo)
215
+ #
216
+ # @param [String, #to_s] nickname
217
+ # @return [void]
218
+ def getnick(nickname)
219
+ send_command(:getnick, @options[:nickname] = nickname.to_s)
220
+ read_response # "Nickname set to #{nickname}"
221
+ self
222
+ end
223
+
224
+ ##
225
+ # Generates a new keypair for the current tunnel.
226
+ #
227
+ # @example
228
+ # bob.newkeys
229
+ #
230
+ # @return [Destination]
231
+ def newkeys
232
+ send_command(:newkeys)
233
+ Destination.parse(read_response)
234
+ end
235
+
236
+ ##
237
+ # Returns the destination for the current tunnel.
238
+ #
239
+ # @example
240
+ # bob.getdest
241
+ #
242
+ # @return [Destination]
243
+ # @raise [Error] if no tunnel has been selected
244
+ def getdest
245
+ send_command(:getdest)
246
+ Destination.parse(read_response)
247
+ end
248
+
249
+ ##
250
+ # Returns the key pair for the current tunnel.
251
+ #
252
+ # @example
253
+ # bob.getkeys
254
+ #
255
+ # @return [KeyPair]
256
+ # @raise [Error] if no public key has been set
257
+ def getkeys
258
+ send_command(:getkeys)
259
+ KeyPair.parse(read_response)
260
+ end
261
+
262
+ ##
263
+ # Sets the key pair for the current tunnel.
264
+ #
265
+ # @example
266
+ # bob.setkeys(I2P::KeyPair.parse("..."))
267
+ #
268
+ # @param [KeyPair, #to_s] key_pair
269
+ # @return [void]
270
+ # @raise [Error] if no tunnel has been selected
271
+ def setkeys(key_pair)
272
+ send_command(:setkeys, @options[:keys] = key_pair.respond_to?(:to_base64) ? key_pair.to_base64 : key_pair.to_s)
273
+ read_response # the Base64-encoded destination
274
+ self
275
+ end
276
+ alias_method :keys=, :setkeys
277
+
278
+ ##
279
+ # Sets the inbound host name or IP address that the current tunnel
280
+ # listens on.
281
+ #
282
+ # The default for new tunnels is `inhost("localhost")`.
283
+ #
284
+ # @example
285
+ # bob.inhost('127.0.0.1')
286
+ #
287
+ # @param [String, #to_s] host
288
+ # @return [void]
289
+ # @raise [Error] if no tunnel has been selected
290
+ def inhost(host)
291
+ send_command(:inhost, @options[:inhost] = host.to_s)
292
+ read_response # "inhost set"
293
+ self
294
+ end
295
+ alias_method :inhost=, :inhost
296
+
297
+ ##
298
+ # Sets the inbound port number that the current tunnel listens on.
299
+ #
300
+ # @example
301
+ # bob.inport(37337)
302
+ #
303
+ # @param [Integer, #to_i] port
304
+ # @return [void]
305
+ # @raise [Error] if no tunnel has been selected
306
+ def inport(port)
307
+ send_command(:inport, @options[:inport] = port.to_i)
308
+ read_response # "inbound port set"
309
+ self
310
+ end
311
+ alias_method :inport=, :inport
312
+
313
+ ##
314
+ # Sets the outbound host name or IP address that the current tunnel
315
+ # connects to.
316
+ #
317
+ # The default for new tunnels is `outhost("localhost")`.
318
+ #
319
+ # @example
320
+ # bob.outhost('127.0.0.1')
321
+ #
322
+ # @param [String, #to_s] host
323
+ # @return [void]
324
+ # @raise [Error] if no tunnel has been selected
325
+ def outhost(host)
326
+ send_command(:outhost, @options[:outhost] = host.to_s)
327
+ read_response # "outhost set"
328
+ self
329
+ end
330
+ alias_method :outhost=, :outhost
331
+
332
+ ##
333
+ # Sets the outbound port number that the current tunnel connects to.
334
+ #
335
+ # @example
336
+ # bob.outport(80)
337
+ #
338
+ # @param [Integer, #to_i] port
339
+ # @return [void]
340
+ # @raise [Error] if no tunnel has been selected
341
+ def outport(port)
342
+ send_command(:outport, @options[:output] = port.to_i)
343
+ read_response # "outbound port set"
344
+ self
345
+ end
346
+ alias_method :outport=, :outport
347
+
348
+ ##
349
+ # Toggles whether to send the incoming destination key to listening
350
+ # sockets.
351
+ #
352
+ # This only applies to outbound tunnels and has no effect on inbound
353
+ # tunnels.
354
+ #
355
+ # The default for new tunnels is `quiet(false)`.
356
+ #
357
+ # @example Enabling quiet mode
358
+ # bob.quiet
359
+ # bob.quiet(true)
360
+ # bob.quiet = true
361
+ #
362
+ # @example Disabling quiet mode
363
+ # bob.quiet(false)
364
+ # bob.quiet = false
365
+ #
366
+ # @param [Boolean] value
367
+ # @return [void]
368
+ # @raise [Error] if no tunnel has been selected
369
+ def quiet(value = true)
370
+ send_command(:quiet, @options[:quiet] = value.to_s)
371
+ read_response # "Quiet set"
372
+ self
373
+ end
374
+ alias_method :quiet=, :quiet
375
+
376
+ ##
377
+ # Sets an I2P Control Protocol (I2CP) option for the current tunnel.
378
+ #
379
+ # @example
380
+ # bob.option(key, value)
381
+ #
382
+ # @param [String, #to_s] key
383
+ # @param [String, #to_s] value
384
+ # @return [void]
385
+ # @raise [Error] if no tunnel has been selected
386
+ def option(key, value)
387
+ send_command(:option, [key, value].join('='))
388
+ read_response # "#{key} set to #{value}"
389
+ self
390
+ end
391
+
392
+ ##
393
+ # Starts and activates the current tunnel.
394
+ #
395
+ # @example
396
+ # bob.start
397
+ #
398
+ # @return [void]
399
+ # @raise [Error] if the tunnel settings are incomplete
400
+ # @raise [Error] if the tunnel is already active
401
+ def start
402
+ send_command(:start)
403
+ read_response # "tunnel starting"
404
+ self
405
+ end
406
+ alias_method :start!, :start
407
+
408
+ ##
409
+ # Stops and inactivates the current tunnel.
410
+ #
411
+ # @example
412
+ # bob.stop
413
+ #
414
+ # @return [void]
415
+ # @raise [Error] if no tunnel has been selected
416
+ # @raise [Error] if the tunnel is already inactive
417
+ def stop
418
+ send_command(:stop)
419
+ read_response # "tunnel stopping"
420
+ self
421
+ end
422
+ alias_method :stop!, :stop
423
+
424
+ ##
425
+ # Removes the current tunnel. The tunnel must be inactive.
426
+ #
427
+ # @example
428
+ # bob.clear
429
+ #
430
+ # @return [void]
431
+ # @raise [Error] if no tunnel has been selected
432
+ # @raise [Error] if the tunnel is still active
433
+ def clear
434
+ send_command(:clear)
435
+ read_response # "cleared"
436
+ self
437
+ end
438
+ alias_method :clear!, :clear
439
+
440
+ protected
441
+
442
+ ##
443
+ # Sends a command over the BOB bridge socket.
444
+ #
445
+ # @param [String, #to_s] command
446
+ # @param [Array<#to_s>] args
447
+ # @return [void]
448
+ def send_command(command, *args)
449
+ send_line([command.to_s, *args].join(' '))
450
+ end
451
+
452
+ ##
453
+ # Sends a text line over the BOB bridge socket.
454
+ #
455
+ # @param [String, #to_s] line
456
+ # @return [void]
457
+ def send_line(line)
458
+ connect unless connected?
459
+ warn "-> #{line}" if @options[:debug]
460
+ @socket.write(line.to_s + "\n")
461
+ @socket.flush
462
+ self
463
+ end
464
+
465
+ ##
466
+ # Reads a response from the BOB bridge socket.
467
+ #
468
+ # @return [Object]
469
+ # @raise [Error] on an ERROR response
470
+ def read_response
471
+ case line = read_line
472
+ when 'OK' then true
473
+ when /^OK\s*(.*)$/ then $1.strip
474
+ when /^ERROR\s+(.*)$/ then raise Error.new($1.strip)
475
+ else line
476
+ end
477
+ end
478
+
479
+ ##
480
+ # Reads a text line from the BOB bridge socket.
481
+ #
482
+ # @return [String]
483
+ def read_line
484
+ line = @socket.readline.chomp
485
+ warn "<- #{line}" if @options[:debug]
486
+ line
487
+ end
488
+ end
489
+ end; end