socksify-with-auth 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,165 @@
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
+ <p>
81
+ Please note: <b>socksify is not thread-safe</b> when used this way!
82
+ <code>socks_server</code> and <code>socks_port</code> are stored in class
83
+ <code>@@</code>-variables, and applied to all threads and fibers of application.
84
+ </p>
85
+
86
+ <h3>Use Net::HTTP explicitly via SOCKS</h3>
87
+ <p>
88
+ Require the additional library <code>socksify/http</code>
89
+ and use the <code>Net::HTTP.SOCKSProxy</code> method. It
90
+ is similar to <code>Net:HTTP.Proxy</code> from the Ruby
91
+ standard library:
92
+ </p>
93
+ <pre>
94
+ require 'socksify/http'
95
+ uri = URI.parse('http://rubyforge.org/')
96
+ Net::HTTP.SOCKSProxy('127.0.0.1', 9050).start(uri.host, uri.port) do |http|
97
+ http.get(uri.path)
98
+ end
99
+ # => #&lt;Net::HTTPOK 200 OK readbody=true&gt;</pre>
100
+
101
+ <p>
102
+ Note that <code>Net::HTTP.SOCKSProxy</code> never relies
103
+ on <code>TCPSocket::socks_server</code>/<code>socks_port</code>.
104
+ You should either set <code>SOCKSProxy</code> arguments
105
+ explicitly or use <code>Net::HTTP</code> directly.
106
+ </p>
107
+
108
+ <p>
109
+ <code>Net::HTTP.SOCKSProxy</code> also supports SOCKS authentication:
110
+ </p>
111
+ <pre>
112
+ Net::HTTP.SOCKSProxy('127.0.0.1', 9050, 'username', 'p4ssw0rd')
113
+ </pre>
114
+
115
+
116
+ <h3>Resolve addresses via SOCKS</h3>
117
+ <pre>Socksify::resolve("spaceboyz.net")
118
+ # => "87.106.131.203"</pre>
119
+
120
+ <h3>Debugging</h3>
121
+ <p>
122
+ Colorful diagnostic messages can be enabled via:
123
+ </p>
124
+ <pre>Socksify::debug = true</pre>
125
+
126
+ <h2>Development</h2>
127
+
128
+ <p>
129
+ The <a href="http://github.com/astro/socksify-ruby/">repository</a>
130
+ can be checked out with:
131
+ </p>
132
+ <pre>$ git-clone git://github.com/astro/socksify-ruby.git</pre>
133
+ <p>
134
+ Send patches via E-Mail.
135
+ </p>
136
+
137
+ <h3>Further ideas</h3>
138
+ <ul>
139
+ <li><code>Resolv</code> replacement code, so that programs
140
+ which resolve by themselves don't leak DNS queries</li>
141
+ <li>IPv6 address support</li>
142
+ <li>UDP as soon
143
+ as <a href="http://www.torproject.org/">Tor</a> supports
144
+ it</li>
145
+ <li>Perhaps using standard exceptions for better compatibility
146
+ when acting as a drop-in?</li>
147
+ </ul>
148
+
149
+ <h2>Author</h2>
150
+ <ul>
151
+ <li>
152
+ <a href="mailto:stephan@spaceboyz.net">Stephan Maka</a>
153
+ </li>
154
+ </ul>
155
+
156
+ <h2>License</h2>
157
+ <p>
158
+ SOCKSify Ruby is distributed under the terms of the GNU
159
+ General Public License version 3 (see
160
+ file <code>COPYING</code>) or the Ruby License (see
161
+ file <code>LICENSE</code>) at your option.
162
+ </p>
163
+ </div>
164
+ </body>
165
+ </html>
@@ -0,0 +1,369 @@
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, :socks_username, :socks_password
136
+
137
+ def initialize(socks_server, socks_port, peer_host, socks_username = nil, socks_password = nil)
138
+ @socks_server, @socks_port = socks_server, socks_port
139
+ @socks_username, @socks_password = socks_username, socks_password
140
+ super peer_host
141
+ end
142
+
143
+ def inspect
144
+ "#{to_s} (via #{@socks_server}:#{@socks_port})"
145
+ end
146
+
147
+ def peer_host
148
+ to_s
149
+ end
150
+ end
151
+
152
+ alias :initialize_tcp :initialize
153
+
154
+ # See http://tools.ietf.org/html/rfc1928
155
+ def initialize(host=nil, port=0, local_host=nil, local_port=nil)
156
+ if host.is_a?(SOCKSConnectionPeerAddress)
157
+ socks_peer = host
158
+ socks_server = socks_peer.socks_server
159
+ socks_port = socks_peer.socks_port
160
+ socks_ignores = []
161
+ host = socks_peer.peer_host
162
+ socks_username = socks_peer.socks_username
163
+ socks_password = socks_peer.socks_password
164
+ else
165
+ socks_server = self.class.socks_server
166
+ socks_port = self.class.socks_port
167
+ socks_ignores = self.class.socks_ignores
168
+ socks_username = self.class.socks_username
169
+ socks_password = self.class.socks_password
170
+ end
171
+
172
+ if socks_server and socks_port and not socks_ignores.include?(host)
173
+ Socksify::debug_notice "Connecting to SOCKS server #{socks_server}:#{socks_port}"
174
+ initialize_tcp socks_server, socks_port
175
+
176
+ socks_authenticate(socks_username, socks_password) unless @@socks_version =~ /^4/
177
+
178
+ if host
179
+ socks_connect(host, port)
180
+ end
181
+ else
182
+ Socksify::debug_notice "Connecting directly to #{host}:#{port}"
183
+ initialize_tcp host, port, local_host, local_port
184
+ Socksify::debug_debug "Connected to #{host}:#{port}"
185
+ end
186
+ end
187
+
188
+ # Authentication
189
+ def socks_authenticate(socks_username, socks_password)
190
+ if socks_username || socks_password
191
+ Socksify::debug_debug "Sending username/password authentication"
192
+ write "\005\001\002"
193
+ else
194
+ Socksify::debug_debug "Sending no authentication"
195
+ write "\005\001\000"
196
+ end
197
+ Socksify::debug_debug "Waiting for authentication reply"
198
+ auth_reply = recv(2)
199
+ if auth_reply.empty?
200
+ raise SOCKSError.new("Server doesn't reply authentication")
201
+ end
202
+ if auth_reply[0..0] != "\004" and auth_reply[0..0] != "\005"
203
+ raise SOCKSError.new("SOCKS version #{auth_reply[0..0]} not supported")
204
+ end
205
+ if socks_username || socks_password
206
+ if auth_reply[1..1] != "\002"
207
+ raise SOCKSError.new("SOCKS authentication method #{auth_reply[1..1]} neither requested nor supported")
208
+ end
209
+ auth = "\001"
210
+ auth += socks_username.to_s.length.chr
211
+ auth += socks_username.to_s
212
+ auth += socks_password.to_s.length.chr
213
+ auth += socks_password.to_s
214
+ write auth
215
+ auth_reply = recv(2)
216
+ if auth_reply[1..1] != "\000"
217
+ raise SOCKSError.new("SOCKS authentication failed")
218
+ end
219
+ else
220
+ if auth_reply[1..1] != "\000"
221
+ raise SOCKSError.new("SOCKS authentication method #{auth_reply[1..1]} neither requested nor supported")
222
+ end
223
+ end
224
+ end
225
+
226
+ # Connect
227
+ def socks_connect(host, port)
228
+ port = Socket.getservbyname(port) if port.is_a?(String)
229
+ req = String.new
230
+ Socksify::debug_debug "Sending destination address"
231
+ req << TCPSocket.socks_version
232
+ Socksify::debug_debug TCPSocket.socks_version.unpack "H*"
233
+ req << "\001"
234
+ req << "\000" if @@socks_version == "5"
235
+ req << [port].pack('n') if @@socks_version =~ /^4/
236
+
237
+ if @@socks_version == "4"
238
+ host = Resolv::DNS.new.getaddress(host).to_s
239
+ end
240
+ Socksify::debug_debug host
241
+ if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ # to IPv4 address
242
+ req << "\001" if @@socks_version == "5"
243
+ _ip = [$1.to_i,
244
+ $2.to_i,
245
+ $3.to_i,
246
+ $4.to_i
247
+ ].pack('CCCC')
248
+ req << _ip
249
+ elsif host =~ /^[:0-9a-f]+$/ # to IPv6 address
250
+ raise "TCP/IPv6 over SOCKS is not yet supported (inet_pton missing in Ruby & not supported by Tor"
251
+ req << "\004"
252
+ else # to hostname
253
+ if @@socks_version == "5"
254
+ req << "\003" + [host.size].pack('C') + host
255
+ else
256
+ req << "\000\000\000\001"
257
+ req << "\007\000"
258
+ Socksify::debug_notice host
259
+ req << host
260
+ req << "\000"
261
+ end
262
+ end
263
+ req << [port].pack('n') if @@socks_version == "5"
264
+ write req
265
+
266
+ socks_receive_reply
267
+ Socksify::debug_notice "Connected to #{host}:#{port} over SOCKS"
268
+ end
269
+
270
+ # returns [bind_addr: String, bind_port: Fixnum]
271
+ def socks_receive_reply
272
+ Socksify::debug_debug "Waiting for SOCKS reply"
273
+ if @@socks_version == "5"
274
+ connect_reply = recv(4)
275
+ if connect_reply.empty?
276
+ raise SOCKSError.new("Server doesn't reply")
277
+ end
278
+ Socksify::debug_debug connect_reply.unpack "H*"
279
+ if connect_reply[0..0] != "\005"
280
+ raise SOCKSError.new("SOCKS version #{connect_reply[0..0]} is not 5")
281
+ end
282
+ if connect_reply[1..1] != "\000"
283
+ raise SOCKSError.for_response_code(connect_reply.bytes.to_a[1])
284
+ end
285
+ Socksify::debug_debug "Waiting for bind_addr"
286
+ bind_addr_len = case connect_reply[3..3]
287
+ when "\001"
288
+ 4
289
+ when "\003"
290
+ recv(1).bytes.first
291
+ when "\004"
292
+ 16
293
+ else
294
+ raise SOCKSError.for_response_code(connect_reply.bytes.to_a[3])
295
+ end
296
+ bind_addr_s = recv(bind_addr_len)
297
+ bind_addr = case connect_reply[3..3]
298
+ when "\001"
299
+ bind_addr_s.bytes.to_a.join('.')
300
+ when "\003"
301
+ bind_addr_s
302
+ when "\004" # Untested!
303
+ i = 0
304
+ ip6 = ""
305
+ bind_addr_s.each_byte do |b|
306
+ if i > 0 and i % 2 == 0
307
+ ip6 += ":"
308
+ end
309
+ i += 1
310
+
311
+ ip6 += b.to_s(16).rjust(2, '0')
312
+ end
313
+ end
314
+ bind_port = recv(bind_addr_len + 2)
315
+ [bind_addr, bind_port.unpack('n')]
316
+ else
317
+ connect_reply = recv(8)
318
+ unless connect_reply[0] == "\000" and connect_reply[1] == "\x5A"
319
+ Socksify::debug_debug connect_reply.unpack 'H'
320
+ raise SOCKSError.new("Failed while connecting througth socks")
321
+ end
322
+ end
323
+ end
324
+ end
325
+
326
+ module Socksify
327
+ def self.resolve(host)
328
+ s = TCPSocket.new
329
+
330
+ begin
331
+ req = String.new
332
+ Socksify::debug_debug "Sending hostname to resolve: #{host}"
333
+ req << "\005"
334
+ if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ # to IPv4 address
335
+ req << "\xF1\000\001" + [$1.to_i,
336
+ $2.to_i,
337
+ $3.to_i,
338
+ $4.to_i
339
+ ].pack('CCCC')
340
+ elsif host =~ /^[:0-9a-f]+$/ # to IPv6 address
341
+ raise "TCP/IPv6 over SOCKS is not yet supported (inet_pton missing in Ruby & not supported by Tor"
342
+ req << "\004"
343
+ else # to hostname
344
+ req << "\xF0\000\003" + [host.size].pack('C') + host
345
+ end
346
+ req << [0].pack('n') # Port
347
+ s.write req
348
+
349
+ addr, _port = s.socks_receive_reply
350
+ Socksify::debug_notice "Resolved #{host} as #{addr} over SOCKS"
351
+ addr
352
+ ensure
353
+ s.close
354
+ end
355
+ end
356
+
357
+ def self.proxy(server, port)
358
+ default_server = TCPSocket::socks_server
359
+ default_port = TCPSocket::socks_port
360
+ begin
361
+ TCPSocket::socks_server = server
362
+ TCPSocket::socks_port = port
363
+ yield
364
+ ensure
365
+ TCPSocket::socks_server = default_server
366
+ TCPSocket::socks_port = default_port
367
+ end
368
+ end
369
+ end