@ -55,7 +55,10 @@ Modifications made by Anorov (https://github.com/Anorov)
"""
from base64 import b64encode
from collections import Callable
try :
from collections . abc import Callable
except ImportError :
from collections import Callable
from errno import EOPNOTSUPP , EINVAL , EAGAIN
import functools
from io import BytesIO
@ -114,7 +117,7 @@ class ProxyError(IOError):
self . socket_err = socket_err
if socket_err :
self . msg + = " : {0 } " . format ( socket_err )
self . msg + = " : {} " . format ( socket_err )
def __str__ ( self ) :
return self . msg
@ -533,6 +536,14 @@ class socksocket(_BaseSocket):
if chosen_auth [ 1 : 2 ] == b " \x02 " :
# Okay, we need to perform a basic username/password
# authentication.
if not ( username and password ) :
# Although we said we don't support authentication, the
# server may still request basic username/password
# authentication
raise SOCKS5AuthError ( " No username/password supplied. "
" Server requested username/password "
" authentication " )
writer . write ( b " \x01 " + chr ( len ( username ) ) . encode ( )
+ username
+ chr ( len ( password ) ) . encode ( )
@ -575,7 +586,7 @@ class socksocket(_BaseSocket):
if status != 0x00 :
# Connection failed: server returned an error
error = SOCKS5_ERRORS . get ( status , " Unknown error " )
raise SOCKS5Error ( " {0 :#04x} : {1 } " . format ( status , error ) )
raise SOCKS5Error ( " {:#04x} : {} " . format ( status , error ) )
# Get the bound address/port
bnd = self . _read_SOCKS5_address ( reader )
@ -693,7 +704,7 @@ class socksocket(_BaseSocket):
if status != 0x5A :
# Connection failed: server returned an error
error = SOCKS4_ERRORS . get ( status , " Unknown error " )
raise SOCKS4Error ( " {0 :#04x} : {1 } " . format ( status , error ) )
raise SOCKS4Error ( " {:#04x} : {} " . format ( status , error ) )
# Get the bound address/port
self . proxy_sockname = ( socket . inet_ntoa ( resp [ 4 : ] ) ,
@ -753,7 +764,7 @@ class socksocket(_BaseSocket):
" HTTP proxy server did not return a valid HTTP status " )
if status_code != 200 :
error = " {0 } : {1 } " . format ( status_code , status_msg )
error = " {} : {} " . format ( status_code , status_msg )
if status_code in ( 400 , 403 , 405 ) :
# It's likely that the HTTP proxy server does not support the
# CONNECT tunneling method
@ -772,7 +783,7 @@ class socksocket(_BaseSocket):
}
@set_self_blocking
def connect ( self , dest_pair ) :
def connect ( self , dest_pair , catch_errors = None ) :
"""
Connects to the specified destination through a proxy .
Uses the same API as socket ' s connect().
@ -834,14 +845,17 @@ class socksocket(_BaseSocket):
except socket . error as error :
# Error while connecting to proxy
self . close ( )
proxy_addr , proxy_port = proxy_addr
proxy_server = " {0} : {1} " . format ( proxy_addr , proxy_port )
printable_type = PRINTABLE_PROXY_TYPES [ proxy_type ]
msg = " Error connecting to {0} proxy {1} " . format ( printable_type ,
proxy_server )
log . debug ( " %s due to: %s " , msg , error )
raise ProxyConnectionError ( msg , error )
if not catch_errors :
proxy_addr , proxy_port = proxy_addr
proxy_server = " {} : {} " . format ( proxy_addr , proxy_port )
printable_type = PRINTABLE_PROXY_TYPES [ proxy_type ]
msg = " Error connecting to {} proxy {} " . format ( printable_type ,
proxy_server )
log . debug ( " %s due to: %s " , msg , error )
raise ProxyConnectionError ( msg , error )
else :
raise error
else :
# Connected to proxy server, now negotiate
@ -850,13 +864,32 @@ class socksocket(_BaseSocket):
negotiate = self . _proxy_negotiators [ proxy_type ]
negotiate ( self , dest_addr , dest_port )
except socket . error as error :
# Wrap socket errors
self . close ( )
raise GeneralProxyError ( " Socket error " , error )
if not catch_errors :
# Wrap socket errors
self . close ( )
raise GeneralProxyError ( " Socket error " , error )
else :
raise error
except ProxyError :
# Protocol error while negotiating with proxy
self . close ( )
raise
@set_self_blocking
def connect_ex ( self , dest_pair ) :
""" https://docs.python.org/3/library/socket.html#socket.socket.connect_ex
Like connect ( address ) , but return an error indicator instead of raising an exception for errors returned by the C - level connect ( ) call ( other problems , such as " host not found " can still raise exceptions ) .
"""
try :
self . connect ( dest_pair , catch_errors = True )
return 0
except OSError as e :
# If the error is numeric (socket errors are numeric), then return number as
# connect_ex expects. Otherwise raise the error again (socket timeout for example)
if e . errno :
return e . errno
else :
raise
def _proxy_addr ( self ) :
"""
@ -867,4 +900,4 @@ class socksocket(_BaseSocket):
proxy_port = proxy_port or DEFAULT_PORTS . get ( proxy_type )
if not proxy_port :
raise GeneralProxyError ( " Invalid proxy type " )
return proxy_addr , proxy_port
return proxy_addr , proxy_port