diff --git a/ChangeLog b/ChangeLog index 27bc164..0ae2e87 100644 --- a/ChangeLog +++ b/ChangeLog @@ -81,3 +81,5 @@ SOCKSify Ruby 1.7.3 =================== * add Rakefile * fix missing :timeout kwarg in TCPSocket class (thanks @lizzypy) +* Authentication support added to Net::HTTP.SOCKSProxy + (thanks to @ojab) diff --git a/doc/index.html b/doc/index.html index 5d68607..86711d5 100644 --- a/doc/index.html +++ b/doc/index.html @@ -105,6 +105,14 @@

Use Net::HTTP explicitly via SOCKS

explicitly or use Net::HTTP directly.

+

+ Net::HTTP.SOCKSProxy also supports SOCKS authentication: +

+
+Net::HTTP.SOCKSProxy('127.0.0.1', 9050, 'username', 'p4ssw0rd')
+      
+ +

Resolve addresses via SOCKS

Socksify::resolve("spaceboyz.net")
 # => "87.106.131.203"
diff --git a/lib/socksify/http.rb b/lib/socksify/http.rb index 73eea48..a0629dd 100644 --- a/lib/socksify/http.rb +++ b/lib/socksify/http.rb @@ -21,16 +21,20 @@ module Net # patched class class HTTP - def self.socks_proxy(p_host, p_port) + def self.socks_proxy(p_host, p_port, p_username = nil, p_password = nil) proxyclass = Class.new(self) proxyclass.send(:include, SOCKSProxyDelta) proxyclass.module_eval do include Ruby3NetHTTPConnectable if RUBY_VERSION.to_f > 3.0 # patch #connect method include SOCKSProxyDelta::InstanceMethods extend SOCKSProxyDelta::ClassMethods + @socks_server = p_host @socks_port = p_port + @socks_username = p_username + @socks_password = p_password end + proxyclass end @@ -41,13 +45,18 @@ class << self module SOCKSProxyDelta # class methods module ClassMethods - attr_reader :socks_server, :socks_port + attr_reader :socks_server, :socks_port, + :socks_username, :socks_password end # instance methods - no long supports Ruby < 2 module InstanceMethods def address - TCPSocket::SOCKSConnectionPeerAddress.new(self.class.socks_server, self.class.socks_port, @address) + TCPSocket::SOCKSConnectionPeerAddress.new( + self.class.socks_server, self.class.socks_port, + @address, + self.class.socks_username, self.class.socks_password + ) end end end diff --git a/lib/socksify/socksproxyable.rb b/lib/socksify/socksproxyable.rb index d79f54f..4a5c26f 100644 --- a/lib/socksify/socksproxyable.rb +++ b/lib/socksify/socksproxyable.rb @@ -26,8 +26,8 @@ def socks_version_hex # instance method #socks_authenticate module InstanceMethodsAuthenticate # rubocop:disable Metrics - def socks_authenticate - if self.class.socks_username || self.class.socks_password + def socks_authenticate(socks_username, socks_password) + if socks_username || socks_password Socksify.debug_debug 'Sending username/password authentication' write "\005\001\002" else @@ -42,16 +42,16 @@ def socks_authenticate raise SOCKSError, "SOCKS version #{auth_reply[0..0]} not supported" end - if self.class.socks_username || self.class.socks_password + if socks_username || socks_password if auth_reply[1..1] != "\002" raise SOCKSError, "SOCKS authentication method #{auth_reply[1..1]} neither requested nor supported" end auth = "\001" - auth += self.class.socks_username.to_s.length.chr - auth += self.class.socks_username.to_s - auth += self.class.socks_password.to_s.length.chr - auth += self.class.socks_password.to_s + auth += username.to_s.length.chr + auth += socks_username.to_s + auth += socks_password.to_s.length.chr + auth += socks_password.to_s write auth auth_reply = recv(2) raise SOCKSError, 'SOCKS authentication failed' if auth_reply[1..1] != "\000" diff --git a/lib/socksify/tcpsocket.rb b/lib/socksify/tcpsocket.rb index 84ec288..0b7e1fe 100644 --- a/lib/socksify/tcpsocket.rb +++ b/lib/socksify/tcpsocket.rb @@ -10,14 +10,19 @@ class TCPSocket # See http://tools.ietf.org/html/rfc1928 # rubocop:disable Metrics/ParameterLists - def initialize(host = nil, port = nil, local_host = nil, local_port = nil, **kwargs) + def initialize(host = nil, port = nil, + local_host = nil, local_port = nil, + **kwargs) socks_peer = host if host.is_a?(SOCKSConnectionPeerAddress) socks_server = set_socks_server(socks_peer) socks_port = set_socks_port(socks_peer) + socks_username = set_socks_username(socks_peer) + socks_password = set_socks_password(socks_peer) socks_ignores = set_socks_ignores(socks_peer) host = socks_peer.peer_host if socks_peer + if socks_server && socks_port && !socks_ignores.include?(host) - make_socks_connection(host, port, socks_server, socks_port, **kwargs) + make_socks_connection(host, port, socks_server, socks_port, socks_username, socks_password, **kwargs) else make_direct_connection(host, port, local_host, local_port, **kwargs) end @@ -26,11 +31,13 @@ def initialize(host = nil, port = nil, local_host = nil, local_port = nil, **kwa # string representation of the peer host address class SOCKSConnectionPeerAddress < String - attr_reader :socks_server, :socks_port + attr_reader :socks_server, :socks_port, :socks_username, :socks_password - def initialize(socks_server, socks_port, peer_host) + def initialize(socks_server, socks_port, peer_host, socks_username = nil, socks_password = nil) @socks_server = socks_server @socks_port = socks_port + @socks_username = socks_username + @socks_password = socks_password super(peer_host) end @@ -53,14 +60,25 @@ def set_socks_port(socks_peer = nil) socks_peer ? socks_peer.socks_port : self.class.socks_port end + def set_socks_username(socks_peer = nil) + socks_peer ? socks_peer.socks_username : self.class.socks_username + end + + def set_socks_password(socks_peer = nil) + socks_peer ? socks_peer.socks_password : self.class.socks_password + end + def set_socks_ignores(socks_peer = nil) socks_peer ? [] : self.class.socks_ignores end - def make_socks_connection(host, port, socks_server, socks_port, **kwargs) + def make_socks_connection(host, port, + socks_server, socks_port, + socks_username, socks_password, + **kwargs) Socksify.debug_notice "Connecting to SOCKS server #{socks_server}:#{socks_port}" initialize_tcp socks_server, socks_port, **kwargs - socks_authenticate unless @socks_version =~ /^4/ + socks_authenticate(socks_username, socks_password) unless @socks_version =~ /^4/ socks_connect(host, port) if host end