socksify-with-auth 1.7.1
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 +7 -0
- data/COPYING +674 -0
- data/doc/index.css +37 -0
- data/doc/index.html +165 -0
- data/lib/socksify.rb +369 -0
- data/lib/socksify/debug.rb +55 -0
- data/lib/socksify/http.rb +71 -0
- metadata +59 -0
data/doc/index.css
ADDED
@@ -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
|
+
}
|
data/doc/index.html
ADDED
@@ -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
|
+
# => #<TCPSocket:0x...></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
|
+
# => #<String: rubyforge's html>
|
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
|
+
# => #<Net::HTTPOK 200 OK readbody=true></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>
|
data/lib/socksify.rb
ADDED
@@ -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
|