@@ -122,40 +122,31 @@ def self.create_ip(param)
122122 # Creates a socket using the supplied Parameter instance.
123123 #
124124 def self . create_by_type ( param , type , proto = 0 )
125-
126125 # Detect IPv6 addresses and enable IPv6 accordingly
127126 if Rex ::Socket . support_ipv6?
128-
129- local = Rex ::Socket . resolv_nbo ( param . localhost ) if param . localhost
130- peer = Rex ::Socket . resolv_nbo ( param . peerhost ) if param . peerhost
131-
132127 # Enable IPv6 dual-bind mode for unbound UDP sockets on Linux
133- if type == ::Socket ::SOCK_DGRAM && Rex ::Compat . is_linux && !local && !peer
128+ if type == ::Socket ::SOCK_DGRAM && Rex ::Compat . is_linux && !param . localhost && !param . peerhost
134129 param . v6 = true
135130
136131 # Check if either of the addresses is 16 octets long
137- elsif ( local && local . length == 16 ) || ( peer && peer . length == 16 )
132+ elsif ( param . localhost && Rex :: Socket . is_ipv6? ( param . localhost ) ) || ( param . peerhost && Rex :: Socket . is_ipv6? ( param . peerhost ) )
138133 param . v6 = true
139134 end
140135
141136 if param . v6
142- if local && local . length == 4
143- if local == " \x00 \x00 \x00 \x00 "
137+ if param . localhost && Rex :: Socket . is_ipv4? ( param . localhost )
138+ if Rex :: Socket . addr_atoi ( param . localhost ) == 0
144139 param . localhost = '::'
145- elsif local == "\x7f \x00 \x00 \x01 "
146- param . localhost = '::1'
147140 else
148- param . localhost = '::ffff:' + Rex :: Socket . getaddress ( param . localhost , true )
141+ param . localhost = '::ffff:' + param . localhost
149142 end
150143 end
151144
152- if peer && peer . length == 4
153- if peer == " \x00 \x00 \x00 \x00 "
145+ if param . peerhost && Rex :: Socket . is_ipv4? ( param . peerhost )
146+ if Rex :: Socket . addr_atoi ( param . peerhost ) == 0
154147 param . peerhost = '::'
155- elsif peer == "\x7f \x00 \x00 \x01 "
156- param . peerhost = '::1'
157148 else
158- param . peerhost = '::ffff:' + Rex :: Socket . getaddress ( param . peerhost , true )
149+ param . peerhost = '::ffff:' + param . peerhost
159150 end
160151 end
161152 end
@@ -179,7 +170,7 @@ def self.create_by_type(param, type, proto = 0)
179170 if param . localport || param . localhost
180171 begin
181172
182- # SO_REUSEADDR has undesired semantics on Windows, intead allowing
173+ # SO_REUSEADDR has undesired semantics on Windows, instead allowing
183174 # sockets to be stolen without warning from other unprotected
184175 # processes.
185176 unless Rex ::Compat . is_windows
@@ -210,7 +201,7 @@ def self.create_by_type(param, type, proto = 0)
210201 klass = Rex ::Socket ::SslTcpServer
211202 end
212203 elsif param . proto == 'sctp'
213- klass = Rex ::Socket ::SctpServer
204+ klass = Rex ::Socket ::SctpServer
214205 else
215206 raise Rex ::BindFailed . new ( param . localhost , param . localport ) , caller
216207 end
@@ -220,8 +211,6 @@ def self.create_by_type(param, type, proto = 0)
220211 end
221212 # Otherwise, if we're creating a client...
222213 else
223- chain = [ ]
224-
225214 # If we were supplied with host information
226215 if param . peerhost
227216
@@ -262,14 +251,13 @@ def self.create_by_type(param, type, proto = 0)
262251 end
263252
264253 ip6_scope_idx = 0
265- ip = Rex ::Socket . getaddress ( param . peerhost )
266- port = param . peerport
267-
268- if param . proxies
269- chain = param . proxies . dup
270- chain . push ( [ 'host' , param . peerhost , param . peerport ] )
271- ip = chain [ 0 ] [ 1 ]
272- port = chain [ 0 ] [ 2 ] . to_i
254+
255+ if param . proxies?
256+ ip = param . proxies . first . host
257+ port = param . proxies . first . port
258+ else
259+ ip = Rex ::Socket . getaddress ( param . peerhost )
260+ port = param . peerport
273261 end
274262
275263 begin
@@ -326,24 +314,20 @@ def self.create_by_type(param, type, proto = 0)
326314 end
327315 end
328316
329- if chain . size > 1
330- chain . each_with_index {
331- |proxy , i |
332- next_hop = chain [ i + 1 ]
333- if next_hop
334- proxy ( sock , proxy [ 0 ] , next_hop [ 1 ] , next_hop [ 2 ] )
335- end
336- }
317+ if param . proxies?
318+ param . proxies . each_cons ( 2 ) do |current_proxy , next_proxy |
319+ proxy ( sock , current_proxy . scheme , next_proxy . host , next_proxy . port )
320+ end
321+ current_proxy = param . proxies . last
322+ proxy ( sock , current_proxy . scheme , param . peerhost , param . peerport )
337323 end
338324
339325 # Now extend the socket with SSL and perform the handshake
340- if ( param . bare? == false and param . ssl )
326+ if ! param . bare? && param . ssl
341327 klass = Rex ::Socket ::SslTcp
342328 sock . extend ( klass )
343329 sock . initsock ( param )
344330 end
345-
346-
347331 end
348332
349333 # Notify handlers that a socket has been created.
@@ -440,76 +424,49 @@ def self.proxy(sock, type, host, port)
440424 raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to receive a response from the proxy" ) , caller
441425 end
442426
443- resp = Rex ::Proto ::Http ::Response . new
444- resp . update_cmd_parts ( ret . split ( /\r ?\n / ) [ 0 ] )
427+ if ( match = ret . match ( /HTTP\/ .+?\s +(\d +)\s ?.+?\r ?\n ?$/ ) )
428+ status_code = match [ 1 ] . to_i
429+ end
445430
446- if resp . code != 200
431+ if status_code != 200
447432 raise Rex ::ConnectionProxyError . new ( host , port , type , "The proxy returned a non-OK response" ) , caller
448433 end
449434 when Rex ::Socket ::Proxies ::ProxyType ::SOCKS4
450- supports_ipv6 = false
451- setup = [ 4 , 1 , port . to_i ] . pack ( 'CCn' ) + Rex ::Socket . resolv_nbo ( host , supports_ipv6 ) + Rex ::Text . rand_text_alpha ( rand ( 8 ) +1 ) + "\x00 "
452- size = sock . put ( setup )
453- if size != setup . length
454- raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to send the entire request to the proxy" ) , caller
455- end
435+ if !Rex ::Socket . is_ipv4? ( host )
436+ if !Rex ::Socket . is_name? ( host )
437+ raise Rex ::ConnectionProxyError . new ( host , port , type , "The SOCKS4 target host must be an IPv4 address or a hostname" ) , caller
438+ end
456439
457- begin
458- ret = sock . get_once ( 8 , 30 )
459- rescue IOError
460- raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to receive a response from the proxy " ) , caller
461- end
440+ begin
441+ address = Rex :: Socket . getaddress ( host , false )
442+ rescue :: SocketError
443+ raise Rex ::ConnectionProxyError . new ( host , port , type , "The SOCKS4 target ' #{ host } ' could not be resolved to an IP address " ) , caller
444+ end
462445
463- if ret . nil? || ret . length < 8
464- raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to receive a complete response from the proxy" ) , caller
465- end
466- if ret [ 1 , 1 ] != "\x5a "
467- raise Rex ::ConnectionProxyError . new ( host , port , type , "Proxy responded with error code #{ ret [ 0 , 1 ] . unpack ( "C" ) [ 0 ] } " ) , caller
468- end
469- when Rex ::Socket ::Proxies ::ProxyType ::SOCKS5
470- auth_methods = [ 5 , 1 , 0 ] . pack ( 'CCC' )
471- size = sock . put ( auth_methods )
472- if size != auth_methods . length
473- raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to send the entire request to the proxy" ) , caller
474- end
475- ret = sock . get_once ( 2 , 30 )
476- if ret [ 1 , 1 ] == "\xff "
477- raise Rex ::ConnectionProxyError . new ( host , port , type , "The proxy requires authentication" ) , caller
446+ host = address
478447 end
479448
480- if Rex ::Socket . is_ipv4? ( host )
481- accepts_ipv6 = false
482- addr = Rex ::Socket . resolv_nbo ( host , accepts_ipv6 )
483- setup = [ 5 , 1 , 0 , 1 ] . pack ( 'C4' ) + addr + [ port . to_i ] . pack ( 'n' )
484- elsif Rex ::Socket . support_ipv6? && Rex ::Socket . is_ipv6? ( host )
485- # IPv6 stuff all untested
486- accepts_ipv6 = true
487- addr = Rex ::Socket . resolv_nbo ( host , accepts_ipv6 )
488- setup = [ 5 , 1 , 0 , 4 ] . pack ( 'C4' ) + addr + [ port . to_i ] . pack ( 'n' )
489- else
490- # Then it must be a domain name.
491- # Unfortunately, it looks like the host has always been
492- # resolved by the time it gets here, so this code never runs.
493- setup = [ 5 , 1 , 0 , 3 ] . pack ( 'C4' ) + [ host . length ] . pack ( 'C' ) + host + [ port . to_i ] . pack ( 'n' )
494- end
449+ self . proxy_socks4a ( sock , type , host , port )
450+ when Rex ::Socket ::Proxies ::ProxyType ::SOCKS5
451+ # follow the unofficial convention where SOCKS5 handles the resolution locally (which leaks DNS)
452+ if !Rex ::Socket . is_ip_addr? ( host )
453+ if !Rex ::Socket . is_name? ( host )
454+ raise Rex ::ConnectionProxyError . new ( host , port , type , "The SOCKS5 target host must be an IP address or a hostname" ) , caller
455+ end
495456
496- size = sock . put ( setup )
497- if size != setup . length
498- raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to send the entire request to the proxy" ) , caller
499- end
457+ begin
458+ address = Rex ::Socket . getaddress ( host , Rex ::Socket . support_ipv6? )
459+ rescue ::SocketError
460+ raise Rex ::ConnectionProxyError . new ( host , port , type , "The SOCKS5 target '#{ host } ' could not be resolved to an IP address" ) , caller
461+ end
500462
501- begin
502- response = sock . get_once ( 10 , 30 )
503- rescue IOError
504- raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to receive a response from the proxy" ) , caller
463+ host = address
505464 end
506465
507- if response . nil? || response . length < 10
508- raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to receive a complete response from the proxy" ) , caller
509- end
510- if response [ 1 , 1 ] != "\x00 "
511- raise Rex ::ConnectionProxyError . new ( host , port , type , "Proxy responded with error code #{ response [ 1 , 1 ] . unpack ( "C" ) [ 0 ] } " ) , caller
512- end
466+ self . proxy_socks5h ( sock , type , host , port )
467+ when Rex ::Socket ::Proxies ::ProxyType ::SOCKS5H
468+ # follow the unofficial convention where SOCKS5H has the proxy server resolve the hostname to and IP address
469+ self . proxy_socks5h ( sock , type , host , port )
513470 else
514471 raise RuntimeError , "The proxy type specified is not valid" , caller
515472 end
@@ -531,4 +488,72 @@ def self.deregister_event_handler(handler) # :nodoc:
531488 def self . each_event_handler ( handler ) # :nodoc:
532489 self . instance . each_event_handler ( handler )
533490 end
491+
492+ private
493+
494+ def self . proxy_socks4a ( sock , type , host , port )
495+ setup = [ 4 , 1 , port . to_i ] . pack ( 'CCn' ) + Rex ::Socket . resolv_nbo ( host , false ) + Rex ::Text . rand_text_alpha ( rand ( 8 ) +1 ) + "\x00 "
496+ size = sock . put ( setup )
497+ if size != setup . length
498+ raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to send the entire request to the proxy" ) , caller
499+ end
500+
501+ begin
502+ ret = sock . get_once ( 8 , 30 )
503+ rescue IOError
504+ raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to receive a response from the proxy" ) , caller
505+ end
506+
507+ if ret . nil? || ret . length < 8
508+ raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to receive a complete response from the proxy" ) , caller
509+ end
510+ if ret [ 1 , 1 ] != "\x5a "
511+ raise Rex ::ConnectionProxyError . new ( host , port , type , "Proxy responded with error code #{ ret [ 0 , 1 ] . unpack ( "C" ) [ 0 ] } " ) , caller
512+ end
513+ end
514+
515+ def self . proxy_socks5h ( sock , type , host , port )
516+ auth_methods = [ 5 , 1 , 0 ] . pack ( 'CCC' )
517+ size = sock . put ( auth_methods )
518+ if size != auth_methods . length
519+ raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to send the entire request to the proxy" ) , caller ( 1 )
520+ end
521+ ret = sock . get_once ( 2 , 30 )
522+ if ret [ 1 , 1 ] == "\xff "
523+ raise Rex ::ConnectionProxyError . new ( host , port , type , "The proxy requires authentication" ) , caller ( 1 )
524+ end
525+
526+ if Rex ::Socket . is_ipv4? ( host )
527+ accepts_ipv6 = false
528+ addr = Rex ::Socket . resolv_nbo ( host , accepts_ipv6 )
529+ setup = [ 5 , 1 , 0 , 1 ] . pack ( 'C4' ) + addr + [ port . to_i ] . pack ( 'n' )
530+ elsif Rex ::Socket . is_ipv6? ( host )
531+ raise Rex ::RuntimeError . new ( 'Rex::Socket does not support IPv6' ) unless Rex ::Socket . support_ipv6?
532+
533+ accepts_ipv6 = true
534+ addr = Rex ::Socket . resolv_nbo ( host , accepts_ipv6 )
535+ setup = [ 5 , 1 , 0 , 4 ] . pack ( 'C4' ) + addr + [ port . to_i ] . pack ( 'n' )
536+ else
537+ # Then it must be a domain name.
538+ setup = [ 5 , 1 , 0 , 3 ] . pack ( 'C4' ) + [ host . length ] . pack ( 'C' ) + host + [ port . to_i ] . pack ( 'n' )
539+ end
540+
541+ size = sock . put ( setup )
542+ if size != setup . length
543+ raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to send the entire request to the proxy" ) , caller ( 1 )
544+ end
545+
546+ begin
547+ response = sock . get_once ( 10 , 30 )
548+ rescue IOError
549+ raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to receive a response from the proxy" ) , caller ( 1 )
550+ end
551+
552+ if response . nil? || response . length < 10
553+ raise Rex ::ConnectionProxyError . new ( host , port , type , "Failed to receive a complete response from the proxy" ) , caller ( 1 )
554+ end
555+ if response [ 1 , 1 ] != "\x00 "
556+ raise Rex ::ConnectionProxyError . new ( host , port , type , "Proxy responded with error code #{ response [ 1 , 1 ] . unpack ( "C" ) [ 0 ] } " ) , caller ( 1 )
557+ end
558+ end
534559end
0 commit comments