bachue-socksify 1.6.0

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.
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ARGV.size < 2
4
+ puts "Usage: #{$0} <SOCKS host> <SOCKS port> [script args ...]"
5
+ exit
6
+ end
7
+
8
+ require 'socksify'
9
+
10
+ TCPSocket.socks_server = ARGV.shift
11
+ TCPSocket.socks_port = ARGV.shift.to_i
12
+ if ARGV.size >= 1
13
+ load ARGV.shift
14
+ else
15
+ require 'irb'
16
+ IRB.start(__FILE__)
17
+ end
18
+
@@ -0,0 +1,37 @@
1
+ body {
2
+ background-color: #8f001f;
3
+ font-family: sans-serif;
4
+ }
5
+
6
+ a {
7
+ color: green;
8
+ }
9
+
10
+ h1 {
11
+ text-align: center;
12
+ margin: 0px;
13
+ color: white;
14
+ font-weight: bold;
15
+ font-size: 400%;
16
+ }
17
+
18
+ div.content {
19
+ background-color: white;
20
+ color: black;
21
+ max-width: 52em;
22
+ margin-left: auto;
23
+ margin-right: auto;
24
+ padding: 0.2em 1em;
25
+ }
26
+
27
+ p {
28
+ text-align: justify;
29
+ }
30
+
31
+ pre {
32
+ color: #8f001f;
33
+ background-color: #cfffbf;
34
+ padding: 4px;
35
+ margin: 2px 0px;
36
+ border: 1px dashed green;
37
+ }
@@ -0,0 +1,151 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml">
5
+ <head>
6
+ <title>SOCKSify Ruby</title>
7
+ <link rel="stylesheet" type="text/css" href="index.css"/>
8
+ </head>
9
+ <body>
10
+ <h1>SOCKSify Ruby</h1>
11
+
12
+ <div class="content">
13
+
14
+ <h2>What is it?</h2>
15
+
16
+ <p>
17
+ <b>SOCKSify Ruby</b> redirects any TCP connection initiated by
18
+ a Ruby script through a SOCKS5 proxy. It serves as a small
19
+ drop-in alternative
20
+ to <a href="http://tsocks.sourceforge.net/">tsocks</a>, except
21
+ that it handles Ruby programs only and doesn't leak DNS
22
+ queries.
23
+ </p>
24
+
25
+ <h3>How does it work?</h3>
26
+
27
+ <p>
28
+ Modifications to class <code>TCPSocket</code>:
29
+ </p>
30
+ <ul>
31
+ <li>Alias <code>initialize</code>
32
+ as <code>initialize_tcp</code></li>
33
+ <li>The new <code>initialize</code> calls the old method to
34
+ establish a TCP connection to the SOCKS proxy, sends the
35
+ proxying destination and checks for errors</li>
36
+ </ul>
37
+ <p>
38
+ Additionally, <code>Socksify::resolve</code> can be used to
39
+ resolve hostnames to IPv4 addresses via SOCKS. There is also
40
+ <code>socksify/http</code> enabling Net::HTTP to work
41
+ via SOCKS.
42
+ </p>
43
+
44
+ <h2>Installation</h2>
45
+ <pre>$ gem install socksify</pre>
46
+
47
+ <h2>Usage</h2>
48
+
49
+ <h3>Redirect all TCP connections of a Ruby program</h3>
50
+ <p>
51
+ Run a Ruby script with redirected TCP through a
52
+ local <a href="http://www.torproject.org/">Tor</a>
53
+ anonymizer:
54
+ </p>
55
+ <pre>$ socksify_ruby localhost 9050 script.rb</pre>
56
+
57
+ <h3>Explicit SOCKS usage in a Ruby program</h3>
58
+ <p>
59
+ Set up SOCKS connections for a
60
+ local <a href="http://www.torproject.org/">Tor</a>
61
+ anonymizer, TCPSockets can be used as usual:
62
+ </p>
63
+ <pre>require 'socksify'
64
+ TCPSocket::socks_server = "127.0.0.1"
65
+ TCPSocket::socks_port = 9050
66
+ rubyforge_www = TCPSocket.new("rubyforge.org", 80)
67
+ # => #&lt;TCPSocket:0x...&gt;</pre>
68
+
69
+ <p>
70
+ Using block only:
71
+ <pre>require 'socksify'
72
+ require 'open-uri'
73
+ Socksify::proxy("127.0.0.1", 9050) {
74
+ open('http://rubyforge.org').read
75
+ # => #&lt;String: rubyforge's html&gt;
76
+ }
77
+ </pre>
78
+ </p>
79
+
80
+ <h3>Use Net::HTTP explicitly via SOCKS</h3>
81
+ <p>
82
+ Require the additional library <code>socksify/http</code>
83
+ and use the <code>Net::HTTP.SOCKSProxy</code> method. It
84
+ is similar to <code>Net:HTTP.Proxy</code> from the Ruby
85
+ standard library:
86
+ </p>
87
+ <pre>
88
+ require 'socksify/http'
89
+ uri = URI.parse('http://rubyforge.org/')
90
+ Net::HTTP.SOCKSProxy('127.0.0.1', 9050).start(uri.host, uri.port) do |http|
91
+ http.get(uri.path)
92
+ end
93
+ # => #&lt;Net::HTTPOK 200 OK readbody=true&gt;</pre>
94
+
95
+ <p>
96
+ Note that <code>Net::HTTP.SOCKSProxy</code> never relies
97
+ on <code>TCPSocket::socks_server</code>/<code>socks_port</code>.
98
+ You should either set <code>SOCKSProxy</code> arguments
99
+ explicitly or use <code>Net::HTTP</code> directly.
100
+ </p>
101
+
102
+ <h3>Resolve addresses via SOCKS</h3>
103
+ <pre>Socksify::resolve("spaceboyz.net")
104
+ # => "87.106.131.203"</pre>
105
+
106
+ <h3>Debugging</h3>
107
+ <p>
108
+ Colorful diagnostic messages can be enabled via:
109
+ </p>
110
+ <pre>Socksify::debug = true</pre>
111
+
112
+ <h2>Development</h2>
113
+
114
+ <p>
115
+ The <a href="http://github.com/astro/socksify-ruby/">repository</a>
116
+ can be checked out with:
117
+ </p>
118
+ <pre>$ git-clone git://github.com/astro/socksify-ruby.git</pre>
119
+ <p>
120
+ Send patches via E-Mail.
121
+ </p>
122
+
123
+ <h3>Further ideas</h3>
124
+ <ul>
125
+ <li><code>Resolv</code> replacement code, so that programs
126
+ which resolve by themselves don't leak DNS queries</li>
127
+ <li>IPv6 address support</li>
128
+ <li>UDP as soon
129
+ as <a href="http://www.torproject.org/">Tor</a> supports
130
+ it</li>
131
+ <li>Perhaps using standard exceptions for better compatibility
132
+ when acting as a drop-in?</li>
133
+ </ul>
134
+
135
+ <h2>Author</h2>
136
+ <ul>
137
+ <li>
138
+ <a href="mailto:stephan@spaceboyz.net">Stephan Maka</a>
139
+ </li>
140
+ </ul>
141
+
142
+ <h2>License</h2>
143
+ <p>
144
+ SOCKSify Ruby is distributed under the terms of the GNU
145
+ General Public License version 3 (see
146
+ file <code>COPYING</code>) or the Ruby License (see
147
+ file <code>LICENSE</code>) at your option.
148
+ </p>
149
+ </div>
150
+ </body>
151
+ </html>
@@ -0,0 +1,359 @@
1
+ #encoding: us-ascii
2
+ =begin
3
+ Copyright (C) 2007 Stephan Maka <stephan@spaceboyz.net>
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ =end
18
+
19
+ require 'socket'
20
+ require 'resolv'
21
+ require 'socksify/debug'
22
+
23
+ class SOCKSError < RuntimeError
24
+ def initialize(msg)
25
+ Socksify::debug_error("#{self.class}: #{msg}")
26
+ super
27
+ end
28
+
29
+ class ServerFailure < SOCKSError
30
+ def initialize
31
+ super("general SOCKS server failure")
32
+ end
33
+ end
34
+ class NotAllowed < SOCKSError
35
+ def initialize
36
+ super("connection not allowed by ruleset")
37
+ end
38
+ end
39
+ class NetworkUnreachable < SOCKSError
40
+ def initialize
41
+ super("Network unreachable")
42
+ end
43
+ end
44
+ class HostUnreachable < SOCKSError
45
+ def initialize
46
+ super("Host unreachable")
47
+ end
48
+ end
49
+ class ConnectionRefused < SOCKSError
50
+ def initialize
51
+ super("Connection refused")
52
+ end
53
+ end
54
+ class TTLExpired < SOCKSError
55
+ def initialize
56
+ super("TTL expired")
57
+ end
58
+ end
59
+ class CommandNotSupported < SOCKSError
60
+ def initialize
61
+ super("Command not supported")
62
+ end
63
+ end
64
+ class AddressTypeNotSupported < SOCKSError
65
+ def initialize
66
+ super("Address type not supported")
67
+ end
68
+ end
69
+
70
+ def self.for_response_code(code)
71
+ case code
72
+ when 1
73
+ ServerFailure
74
+ when 2
75
+ NotAllowed
76
+ when 3
77
+ NetworkUnreachable
78
+ when 4
79
+ HostUnreachable
80
+ when 5
81
+ ConnectionRefused
82
+ when 6
83
+ TTLExpired
84
+ when 7
85
+ CommandNotSupported
86
+ when 8
87
+ AddressTypeNotSupported
88
+ else
89
+ self
90
+ end
91
+ end
92
+ end
93
+
94
+ class TCPSocket
95
+ @@socks_version ||= "5"
96
+
97
+ def self.socks_version
98
+ (@@socks_version == "4a" or @@socks_version == "4") ? "\004" : "\005"
99
+ end
100
+ def self.socks_version=(version)
101
+ @@socks_version = version.to_s
102
+ end
103
+ def self.socks_server
104
+ @@socks_server ||= nil
105
+ end
106
+ def self.socks_server=(host)
107
+ @@socks_server = host
108
+ end
109
+ def self.socks_port
110
+ @@socks_port ||= nil
111
+ end
112
+ def self.socks_port=(port)
113
+ @@socks_port = port
114
+ end
115
+ def self.socks_username
116
+ @@socks_username ||= nil
117
+ end
118
+ def self.socks_username=(username)
119
+ @@socks_username = username
120
+ end
121
+ def self.socks_password
122
+ @@socks_password ||= nil
123
+ end
124
+ def self.socks_password=(password)
125
+ @@socks_password = password
126
+ end
127
+ def self.socks_ignores
128
+ @@socks_ignores ||= %w(localhost)
129
+ end
130
+ def self.socks_ignores=(ignores)
131
+ @@socks_ignores = ignores
132
+ end
133
+
134
+ class SOCKSConnectionPeerAddress < String
135
+ attr_reader :socks_server, :socks_port
136
+
137
+ def initialize(socks_server, socks_port, peer_host)
138
+ @socks_server, @socks_port = socks_server, socks_port
139
+ super peer_host
140
+ end
141
+
142
+ def inspect
143
+ "#{to_s} (via #{@socks_server}:#{@socks_port})"
144
+ end
145
+
146
+ def peer_host
147
+ to_s
148
+ end
149
+ end
150
+
151
+ alias :initialize_tcp :initialize
152
+
153
+ # See http://tools.ietf.org/html/rfc1928
154
+ def initialize(host=nil, port=0, local_host=nil, local_port=nil)
155
+ if host.is_a?(SOCKSConnectionPeerAddress)
156
+ socks_peer = host
157
+ socks_server = socks_peer.socks_server
158
+ socks_port = socks_peer.socks_port
159
+ socks_ignores = []
160
+ host = socks_peer.peer_host
161
+ else
162
+ socks_server = self.class.socks_server
163
+ socks_port = self.class.socks_port
164
+ socks_ignores = self.class.socks_ignores
165
+ end
166
+
167
+ if socks_server and socks_port and not socks_ignores.include?(host)
168
+ Socksify::debug_notice "Connecting to SOCKS server #{socks_server}:#{socks_port}"
169
+ initialize_tcp socks_server, socks_port
170
+
171
+ socks_authenticate unless @@socks_version =~ /^4/
172
+
173
+ if host
174
+ socks_connect(host, port)
175
+ end
176
+ else
177
+ Socksify::debug_notice "Connecting directly to #{host}:#{port}"
178
+ initialize_tcp host, port, local_host, local_port
179
+ Socksify::debug_debug "Connected to #{host}:#{port}"
180
+ end
181
+ end
182
+
183
+ # Authentication
184
+ def socks_authenticate
185
+ if self.class.socks_username || self.class.socks_password
186
+ Socksify::debug_debug "Sending username/password authentication"
187
+ write "\005\001\002"
188
+ else
189
+ Socksify::debug_debug "Sending no authentication"
190
+ write "\005\001\000"
191
+ end
192
+ Socksify::debug_debug "Waiting for authentication reply"
193
+ auth_reply = recv(2)
194
+ if auth_reply.empty?
195
+ raise SOCKSError.new("Server doesn't reply authentication")
196
+ end
197
+ if auth_reply[0..0] != "\004" and auth_reply[0..0] != "\005"
198
+ raise SOCKSError.new("SOCKS version #{auth_reply[0..0]} not supported")
199
+ end
200
+ if self.class.socks_username || self.class.socks_password
201
+ if auth_reply[1..1] != "\002"
202
+ raise SOCKSError.new("SOCKS authentication method #{auth_reply[1..1]} neither requested nor supported")
203
+ end
204
+ auth = "\001"
205
+ auth += self.class.socks_username.to_s.length.chr
206
+ auth += self.class.socks_username.to_s
207
+ auth += self.class.socks_password.to_s.length.chr
208
+ auth += self.class.socks_password.to_s
209
+ write auth
210
+ auth_reply = recv(2)
211
+ if auth_reply[1..1] != "\000"
212
+ raise SOCKSError.new("SOCKS authentication failed")
213
+ end
214
+ else
215
+ if auth_reply[1..1] != "\000"
216
+ raise SOCKSError.new("SOCKS authentication method #{auth_reply[1..1]} neither requested nor supported")
217
+ end
218
+ end
219
+ end
220
+
221
+ # Connect
222
+ def socks_connect(host, port)
223
+ Socksify::debug_debug "Sending destination address"
224
+ write TCPSocket.socks_version
225
+ Socksify::debug_debug TCPSocket.socks_version.unpack "H*"
226
+ write "\001"
227
+ write "\000" if @@socks_version == "5"
228
+ write [port].pack('n') if @@socks_version =~ /^4/
229
+
230
+ if @@socks_version == "4"
231
+ host = Resolv::DNS.new.getaddress(host).to_s
232
+ end
233
+ Socksify::debug_debug host
234
+ if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ # to IPv4 address
235
+ write "\001" if @@socks_version == "5"
236
+ _ip = [$1.to_i,
237
+ $2.to_i,
238
+ $3.to_i,
239
+ $4.to_i
240
+ ].pack('CCCC')
241
+ write _ip
242
+ elsif host =~ /^[:0-9a-f]+$/ # to IPv6 address
243
+ raise "TCP/IPv6 over SOCKS is not yet supported (inet_pton missing in Ruby & not supported by Tor"
244
+ write "\004"
245
+ else # to hostname
246
+ if @@socks_version == "5"
247
+ write "\003" + [host.size].pack('C') + host
248
+ else
249
+ write "\000\000\000\001"
250
+ write "\007\000"
251
+ Socksify::debug_notice host
252
+ write host
253
+ write "\000"
254
+ end
255
+ end
256
+ write [port].pack('n') if @@socks_version == "5"
257
+
258
+ socks_receive_reply
259
+ Socksify::debug_notice "Connected to #{host}:#{port} over SOCKS"
260
+ end
261
+
262
+ # returns [bind_addr: String, bind_port: Fixnum]
263
+ def socks_receive_reply
264
+ Socksify::debug_debug "Waiting for SOCKS reply"
265
+ if @@socks_version == "5"
266
+ connect_reply = recv(4)
267
+ if connect_reply.empty?
268
+ raise SOCKSError.new("Server doesn't reply")
269
+ end
270
+ Socksify::debug_debug connect_reply.unpack "H*"
271
+ if connect_reply[0..0] != "\005"
272
+ raise SOCKSError.new("SOCKS version #{connect_reply[0..0]} is not 5")
273
+ end
274
+ if connect_reply[1..1] != "\000"
275
+ raise SOCKSError.for_response_code(connect_reply.bytes.to_a[1])
276
+ end
277
+ Socksify::debug_debug "Waiting for bind_addr"
278
+ bind_addr_len = case connect_reply[3..3]
279
+ when "\001"
280
+ 4
281
+ when "\003"
282
+ recv(1).bytes.first
283
+ when "\004"
284
+ 16
285
+ else
286
+ raise SOCKSError.for_response_code(connect_reply.bytes.to_a[3])
287
+ end
288
+ bind_addr_s = recv(bind_addr_len)
289
+ bind_addr = case connect_reply[3..3]
290
+ when "\001"
291
+ bind_addr_s.bytes.to_a.join('.')
292
+ when "\003"
293
+ bind_addr_s
294
+ when "\004" # Untested!
295
+ i = 0
296
+ ip6 = ""
297
+ bind_addr_s.each_byte do |b|
298
+ if i > 0 and i % 2 == 0
299
+ ip6 += ":"
300
+ end
301
+ i += 1
302
+
303
+ ip6 += b.to_s(16).rjust(2, '0')
304
+ end
305
+ end
306
+ bind_port = recv(bind_addr_len + 2)
307
+ [bind_addr, bind_port.unpack('n')]
308
+ else
309
+ connect_reply = recv(8)
310
+ unless connect_reply[0] == "\000" and connect_reply[1] == "\x5A"
311
+ Socksify::debug_debug connect_reply.unpack 'H'
312
+ raise SOCKSError.new("Failed while connecting througth socks")
313
+ end
314
+ end
315
+ end
316
+ end
317
+
318
+ module Socksify
319
+ def self.resolve(host)
320
+ s = TCPSocket.new
321
+
322
+ begin
323
+ Socksify::debug_debug "Sending hostname to resolve: #{host}"
324
+ s.write "\005"
325
+ if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ # to IPv4 address
326
+ s.write "\xF1\000\001" + [$1.to_i,
327
+ $2.to_i,
328
+ $3.to_i,
329
+ $4.to_i
330
+ ].pack('CCCC')
331
+ elsif host =~ /^[:0-9a-f]+$/ # to IPv6 address
332
+ raise "TCP/IPv6 over SOCKS is not yet supported (inet_pton missing in Ruby & not supported by Tor"
333
+ s.write "\004"
334
+ else # to hostname
335
+ s.write "\xF0\000\003" + [host.size].pack('C') + host
336
+ end
337
+ s.write [0].pack('n') # Port
338
+
339
+ addr, port = s.socks_receive_reply
340
+ Socksify::debug_notice "Resolved #{host} as #{addr} over SOCKS"
341
+ addr
342
+ ensure
343
+ s.close
344
+ end
345
+ end
346
+
347
+ def self.proxy(server, port)
348
+ default_server = TCPSocket::socks_server
349
+ default_port = TCPSocket::socks_port
350
+ begin
351
+ TCPSocket::socks_server = server
352
+ TCPSocket::socks_port = port
353
+ yield
354
+ ensure
355
+ TCPSocket::socks_server = default_server
356
+ TCPSocket::socks_port = default_port
357
+ end
358
+ end
359
+ end