Skip to content

Commit 4af5542

Browse files
musybiteastro
authored andcommitted
Add authentication support to Net::HTTP.SOCKSProxy
Inspired by #24. This version does not require thread-local variables, everything kept inside instance and local variables. Thanks @ojab!
1 parent d486263 commit 4af5542

File tree

5 files changed

+53
-16
lines changed

5 files changed

+53
-16
lines changed

ChangeLog

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,5 @@ SOCKSify Ruby 1.7.3
8181
===================
8282
* add Rakefile
8383
* fix missing :timeout kwarg in TCPSocket class (thanks @lizzypy)
84+
* Authentication support added to Net::HTTP.SOCKSProxy
85+
(thanks to @ojab)

doc/index.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ <h3>Use Net::HTTP explicitly via SOCKS</h3>
105105
explicitly or use <code>Net::HTTP</code> directly.
106106
</p>
107107

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+
108116
<h3>Resolve addresses via SOCKS</h3>
109117
<pre>Socksify::resolve("spaceboyz.net")
110118
# => "87.106.131.203"</pre>

lib/socksify/http.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,20 @@
2121
module Net
2222
# patched class
2323
class HTTP
24-
def self.socks_proxy(p_host, p_port)
24+
def self.socks_proxy(p_host, p_port, p_username = nil, p_password = nil)
2525
proxyclass = Class.new(self)
2626
proxyclass.send(:include, SOCKSProxyDelta)
2727
proxyclass.module_eval do
2828
include Ruby3NetHTTPConnectable if RUBY_VERSION.to_f > 3.0 # patch #connect method
2929
include SOCKSProxyDelta::InstanceMethods
3030
extend SOCKSProxyDelta::ClassMethods
31+
3132
@socks_server = p_host
3233
@socks_port = p_port
34+
@socks_username = p_username
35+
@socks_password = p_password
3336
end
37+
3438
proxyclass
3539
end
3640

@@ -41,13 +45,18 @@ class << self
4145
module SOCKSProxyDelta
4246
# class methods
4347
module ClassMethods
44-
attr_reader :socks_server, :socks_port
48+
attr_reader :socks_server, :socks_port,
49+
:socks_username, :socks_password
4550
end
4651

4752
# instance methods - no long supports Ruby < 2
4853
module InstanceMethods
4954
def address
50-
TCPSocket::SOCKSConnectionPeerAddress.new(self.class.socks_server, self.class.socks_port, @address)
55+
TCPSocket::SOCKSConnectionPeerAddress.new(
56+
self.class.socks_server, self.class.socks_port,
57+
@address,
58+
self.class.socks_username, self.class.socks_password
59+
)
5160
end
5261
end
5362
end

lib/socksify/socksproxyable.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ def socks_version_hex
2626
# instance method #socks_authenticate
2727
module InstanceMethodsAuthenticate
2828
# rubocop:disable Metrics
29-
def socks_authenticate
30-
if self.class.socks_username || self.class.socks_password
29+
def socks_authenticate(socks_username, socks_password)
30+
if socks_username || socks_password
3131
Socksify.debug_debug 'Sending username/password authentication'
3232
write "\005\001\002"
3333
else
@@ -42,16 +42,16 @@ def socks_authenticate
4242
raise SOCKSError, "SOCKS version #{auth_reply[0..0]} not supported"
4343
end
4444

45-
if self.class.socks_username || self.class.socks_password
45+
if socks_username || socks_password
4646
if auth_reply[1..1] != "\002"
4747
raise SOCKSError, "SOCKS authentication method #{auth_reply[1..1]} neither requested nor supported"
4848
end
4949

5050
auth = "\001"
51-
auth += self.class.socks_username.to_s.length.chr
52-
auth += self.class.socks_username.to_s
53-
auth += self.class.socks_password.to_s.length.chr
54-
auth += self.class.socks_password.to_s
51+
auth += username.to_s.length.chr
52+
auth += socks_username.to_s
53+
auth += socks_password.to_s.length.chr
54+
auth += socks_password.to_s
5555
write auth
5656
auth_reply = recv(2)
5757
raise SOCKSError, 'SOCKS authentication failed' if auth_reply[1..1] != "\000"

lib/socksify/tcpsocket.rb

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,19 @@ class TCPSocket
1010

1111
# See http://tools.ietf.org/html/rfc1928
1212
# rubocop:disable Metrics/ParameterLists
13-
def initialize(host = nil, port = nil, local_host = nil, local_port = nil, **kwargs)
13+
def initialize(host = nil, port = nil,
14+
local_host = nil, local_port = nil,
15+
**kwargs)
1416
socks_peer = host if host.is_a?(SOCKSConnectionPeerAddress)
1517
socks_server = set_socks_server(socks_peer)
1618
socks_port = set_socks_port(socks_peer)
19+
socks_username = set_socks_username(socks_peer)
20+
socks_password = set_socks_password(socks_peer)
1721
socks_ignores = set_socks_ignores(socks_peer)
1822
host = socks_peer.peer_host if socks_peer
23+
1924
if socks_server && socks_port && !socks_ignores.include?(host)
20-
make_socks_connection(host, port, socks_server, socks_port, **kwargs)
25+
make_socks_connection(host, port, socks_server, socks_port, socks_username, socks_password, **kwargs)
2126
else
2227
make_direct_connection(host, port, local_host, local_port, **kwargs)
2328
end
@@ -26,11 +31,13 @@ def initialize(host = nil, port = nil, local_host = nil, local_port = nil, **kwa
2631

2732
# string representation of the peer host address
2833
class SOCKSConnectionPeerAddress < String
29-
attr_reader :socks_server, :socks_port
34+
attr_reader :socks_server, :socks_port, :socks_username, :socks_password
3035

31-
def initialize(socks_server, socks_port, peer_host)
36+
def initialize(socks_server, socks_port, peer_host, socks_username = nil, socks_password = nil)
3237
@socks_server = socks_server
3338
@socks_port = socks_port
39+
@socks_username = socks_username
40+
@socks_password = socks_password
3441
super(peer_host)
3542
end
3643

@@ -53,14 +60,25 @@ def set_socks_port(socks_peer = nil)
5360
socks_peer ? socks_peer.socks_port : self.class.socks_port
5461
end
5562

63+
def set_socks_username(socks_peer = nil)
64+
socks_peer ? socks_peer.socks_username : self.class.socks_username
65+
end
66+
67+
def set_socks_password(socks_peer = nil)
68+
socks_peer ? socks_peer.socks_password : self.class.socks_password
69+
end
70+
5671
def set_socks_ignores(socks_peer = nil)
5772
socks_peer ? [] : self.class.socks_ignores
5873
end
5974

60-
def make_socks_connection(host, port, socks_server, socks_port, **kwargs)
75+
def make_socks_connection(host, port,
76+
socks_server, socks_port,
77+
socks_username, socks_password,
78+
**kwargs)
6179
Socksify.debug_notice "Connecting to SOCKS server #{socks_server}:#{socks_port}"
6280
initialize_tcp socks_server, socks_port, **kwargs
63-
socks_authenticate unless @socks_version =~ /^4/
81+
socks_authenticate(socks_username, socks_password) unless @socks_version =~ /^4/
6482
socks_connect(host, port) if host
6583
end
6684

0 commit comments

Comments
 (0)