socks.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847
  1. from base64 import b64encode
  2. try:
  3. from collections.abc import Callable
  4. except ImportError:
  5. from collections import Callable
  6. from errno import EOPNOTSUPP, EINVAL, EAGAIN
  7. import functools
  8. from io import BytesIO
  9. import logging
  10. import os
  11. from os import SEEK_CUR
  12. import socket
  13. import struct
  14. import sys
  15. __version__ = "1.7.1"
  16. if os.name == "nt" and sys.version_info < (3, 0):
  17. try:
  18. import win_inet_pton
  19. except ImportError:
  20. raise ImportError(
  21. "To run PySocks on Windows you must install win_inet_pton")
  22. log = logging.getLogger(__name__)
  23. PROXY_TYPE_SOCKS4 = SOCKS4 = 1
  24. PROXY_TYPE_SOCKS5 = SOCKS5 = 2
  25. PROXY_TYPE_HTTP = HTTP = 3
  26. PROXY_TYPES = {"SOCKS4": SOCKS4, "SOCKS5": SOCKS5, "HTTP": HTTP}
  27. PRINTABLE_PROXY_TYPES = dict(zip(PROXY_TYPES.values(), PROXY_TYPES.keys()))
  28. _orgsocket = _orig_socket = socket.socket
  29. def set_self_blocking(function):
  30. @functools.wraps(function)
  31. def wrapper(*args, **kwargs):
  32. self = args[0]
  33. try:
  34. _is_blocking = self.gettimeout()
  35. if _is_blocking == 0:
  36. self.setblocking(True)
  37. return function(*args, **kwargs)
  38. except Exception as e:
  39. raise
  40. finally:
  41. # set orgin blocking
  42. if _is_blocking == 0:
  43. self.setblocking(False)
  44. return wrapper
  45. class ProxyError(IOError):
  46. """Socket_err contains original socket.error exception."""
  47. def __init__(self, msg, socket_err=None):
  48. self.msg = msg
  49. self.socket_err = socket_err
  50. if socket_err:
  51. self.msg += ": {}".format(socket_err)
  52. def __str__(self):
  53. return self.msg
  54. class GeneralProxyError(ProxyError):
  55. pass
  56. class ProxyConnectionError(ProxyError):
  57. pass
  58. class SOCKS5AuthError(ProxyError):
  59. pass
  60. class SOCKS5Error(ProxyError):
  61. pass
  62. class SOCKS4Error(ProxyError):
  63. pass
  64. class HTTPError(ProxyError):
  65. pass
  66. SOCKS4_ERRORS = {
  67. 0x5B: "Request rejected or failed",
  68. 0x5C: ("Request rejected because SOCKS server cannot connect to identd on"
  69. " the client"),
  70. 0x5D: ("Request rejected because the client program and identd report"
  71. " different user-ids")
  72. }
  73. SOCKS5_ERRORS = {
  74. 0x01: "General SOCKS server failure",
  75. 0x02: "Connection not allowed by ruleset",
  76. 0x03: "Network unreachable",
  77. 0x04: "Host unreachable",
  78. 0x05: "Connection refused",
  79. 0x06: "TTL expired",
  80. 0x07: "Command not supported, or protocol error",
  81. 0x08: "Address type not supported"
  82. }
  83. DEFAULT_PORTS = {SOCKS4: 1080, SOCKS5: 1080, HTTP: 8080}
  84. def set_default_proxy(proxy_type=None, addr=None, port=None, rdns=True,
  85. username=None, password=None):
  86. """Sets a default proxy.
  87. All further socksocket objects will use the default unless explicitly
  88. changed. All parameters are as for socket.set_proxy()."""
  89. socksocket.default_proxy = (proxy_type, addr, port, rdns,
  90. username.encode() if username else None,
  91. password.encode() if password else None)
  92. def setdefaultproxy(*args, **kwargs):
  93. if "proxytype" in kwargs:
  94. kwargs["proxy_type"] = kwargs.pop("proxytype")
  95. return set_default_proxy(*args, **kwargs)
  96. def get_default_proxy():
  97. """Returns the default proxy, set by set_default_proxy."""
  98. return socksocket.default_proxy
  99. getdefaultproxy = get_default_proxy
  100. def wrap_module(module):
  101. """Attempts to replace a module's socket library with a SOCKS socket.
  102. Must set a default proxy using set_default_proxy(...) first. This will
  103. only work on modules that import socket directly into the namespace;
  104. most of the Python Standard Library falls into this category."""
  105. if socksocket.default_proxy:
  106. module.socket.socket = socksocket
  107. else:
  108. raise GeneralProxyError("No default proxy specified")
  109. wrapmodule = wrap_module
  110. def create_connection(dest_pair,
  111. timeout=None, source_address=None,
  112. proxy_type=None, proxy_addr=None,
  113. proxy_port=None, proxy_rdns=True,
  114. proxy_username=None, proxy_password=None,
  115. socket_options=None):
  116. """create_connection(dest_pair, *[, timeout], **proxy_args) -> socket object
  117. Like socket.create_connection(), but connects to proxy
  118. before returning the socket object.
  119. dest_pair - 2-tuple of (IP/hostname, port).
  120. **proxy_args - Same args passed to socksocket.set_proxy() if present.
  121. timeout - Optional socket timeout value, in seconds.
  122. source_address - tuple (host, port) for the socket to bind to as its source
  123. address before connecting (only for compatibility)
  124. """
  125. # Remove IPv6 brackets on the remote address and proxy address.
  126. remote_host, remote_port = dest_pair
  127. if remote_host.startswith("["):
  128. remote_host = remote_host.strip("[]")
  129. if proxy_addr and proxy_addr.startswith("["):
  130. proxy_addr = proxy_addr.strip("[]")
  131. err = None
  132. # Allow the SOCKS proxy to be on IPv4 or IPv6 addresses.
  133. for r in socket.getaddrinfo(proxy_addr, proxy_port, 0, socket.SOCK_STREAM):
  134. family, socket_type, proto, canonname, sa = r
  135. sock = None
  136. try:
  137. sock = socksocket(family, socket_type, proto)
  138. if socket_options:
  139. for opt in socket_options:
  140. sock.setsockopt(*opt)
  141. if isinstance(timeout, (int, float)):
  142. sock.settimeout(timeout)
  143. if proxy_type:
  144. sock.set_proxy(proxy_type, proxy_addr, proxy_port, proxy_rdns,
  145. proxy_username, proxy_password)
  146. if source_address:
  147. sock.bind(source_address)
  148. sock.connect((remote_host, remote_port))
  149. return sock
  150. except (socket.error, ProxyError) as e:
  151. err = e
  152. if sock:
  153. sock.close()
  154. sock = None
  155. if err:
  156. raise err
  157. raise socket.error("gai returned empty list.")
  158. class _BaseSocket(socket.socket):
  159. """Allows Python 2 delegated methods such as send() to be overridden."""
  160. def __init__(self, *pos, **kw):
  161. _orig_socket.__init__(self, *pos, **kw)
  162. self._savedmethods = dict()
  163. for name in self._savenames:
  164. self._savedmethods[name] = getattr(self, name)
  165. delattr(self, name) # Allows normal overriding mechanism to work
  166. _savenames = list()
  167. def _makemethod(name):
  168. return lambda self, *pos, **kw: self._savedmethods[name](*pos, **kw)
  169. for name in ("sendto", "send", "recvfrom", "recv"):
  170. method = getattr(_BaseSocket, name, None)
  171. # Determine if the method is not defined the usual way
  172. # as a function in the class.
  173. # Python 2 uses __slots__, so there are descriptors for each method,
  174. # but they are not functions.
  175. if not isinstance(method, Callable):
  176. _BaseSocket._savenames.append(name)
  177. setattr(_BaseSocket, name, _makemethod(name))
  178. class socksocket(_BaseSocket):
  179. """socksocket([family[, type[, proto]]]) -> socket object
  180. Open a SOCKS enabled socket. The parameters are the same as
  181. those of the standard socket init. In order for SOCKS to work,
  182. you must specify family=AF_INET and proto=0.
  183. The "type" argument must be either SOCK_STREAM or SOCK_DGRAM.
  184. """
  185. default_proxy = None
  186. def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM,
  187. proto=0, *args, **kwargs):
  188. if type not in (socket.SOCK_STREAM, socket.SOCK_DGRAM):
  189. msg = "Socket type must be stream or datagram, not {!r}"
  190. raise ValueError(msg.format(type))
  191. super(socksocket, self).__init__(family, type, proto, *args, **kwargs)
  192. self._proxyconn = None # TCP connection to keep UDP relay alive
  193. if self.default_proxy:
  194. self.proxy = self.default_proxy
  195. else:
  196. self.proxy = (None, None, None, None, None, None)
  197. self.proxy_sockname = None
  198. self.proxy_peername = None
  199. self._timeout = None
  200. def _readall(self, file, count):
  201. """Receive EXACTLY the number of bytes requested from the file object.
  202. Blocks until the required number of bytes have been received."""
  203. data = b""
  204. while len(data) < count:
  205. d = file.read(count - len(data))
  206. if not d:
  207. raise GeneralProxyError("Connection closed unexpectedly")
  208. data += d
  209. return data
  210. def settimeout(self, timeout):
  211. self._timeout = timeout
  212. try:
  213. # test if we're connected, if so apply timeout
  214. peer = self.get_proxy_peername()
  215. super(socksocket, self).settimeout(self._timeout)
  216. except socket.error:
  217. pass
  218. def gettimeout(self):
  219. return self._timeout
  220. def setblocking(self, v):
  221. if v:
  222. self.settimeout(None)
  223. else:
  224. self.settimeout(0.0)
  225. def set_proxy(self, proxy_type=None, addr=None, port=None, rdns=True,
  226. username=None, password=None):
  227. """ Sets the proxy to be used.
  228. proxy_type - The type of the proxy to be used. Three types
  229. are supported: PROXY_TYPE_SOCKS4 (including socks4a),
  230. PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
  231. addr - The address of the server (IP or DNS).
  232. port - The port of the server. Defaults to 1080 for SOCKS
  233. servers and 8080 for HTTP proxy servers.
  234. rdns - Should DNS queries be performed on the remote side
  235. (rather than the local side). The default is True.
  236. Note: This has no effect with SOCKS4 servers.
  237. username - Username to authenticate with to the server.
  238. The default is no authentication.
  239. password - Password to authenticate with to the server.
  240. Only relevant when username is also provided."""
  241. self.proxy = (proxy_type, addr, port, rdns,
  242. username.encode() if username else None,
  243. password.encode() if password else None)
  244. def setproxy(self, *args, **kwargs):
  245. if "proxytype" in kwargs:
  246. kwargs["proxy_type"] = kwargs.pop("proxytype")
  247. return self.set_proxy(*args, **kwargs)
  248. def bind(self, *pos, **kw):
  249. """Implements proxy connection for UDP sockets.
  250. Happens during the bind() phase."""
  251. (proxy_type, proxy_addr, proxy_port, rdns, username,
  252. password) = self.proxy
  253. if not proxy_type or self.type != socket.SOCK_DGRAM:
  254. return _orig_socket.bind(self, *pos, **kw)
  255. if self._proxyconn:
  256. raise socket.error(EINVAL, "Socket already bound to an address")
  257. if proxy_type != SOCKS5:
  258. msg = "UDP only supported by SOCKS5 proxy type"
  259. raise socket.error(EOPNOTSUPP, msg)
  260. super(socksocket, self).bind(*pos, **kw)
  261. # Need to specify actual local port because
  262. # some relays drop packets if a port of zero is specified.
  263. # Avoid specifying host address in case of NAT though.
  264. _, port = self.getsockname()
  265. dst = ("0", port)
  266. self._proxyconn = _orig_socket()
  267. proxy = self._proxy_addr()
  268. self._proxyconn.connect(proxy)
  269. UDP_ASSOCIATE = b"\x03"
  270. _, relay = self._SOCKS5_request(self._proxyconn, UDP_ASSOCIATE, dst)
  271. # The relay is most likely on the same host as the SOCKS proxy,
  272. # but some proxies return a private IP address (10.x.y.z)
  273. host, _ = proxy
  274. _, port = relay
  275. super(socksocket, self).connect((host, port))
  276. super(socksocket, self).settimeout(self._timeout)
  277. self.proxy_sockname = ("0.0.0.0", 0) # Unknown
  278. def sendto(self, bytes, *args, **kwargs):
  279. if self.type != socket.SOCK_DGRAM:
  280. return super(socksocket, self).sendto(bytes, *args, **kwargs)
  281. if not self._proxyconn:
  282. self.bind(("", 0))
  283. address = args[-1]
  284. flags = args[:-1]
  285. header = BytesIO()
  286. RSV = b"\x00\x00"
  287. header.write(RSV)
  288. STANDALONE = b"\x00"
  289. header.write(STANDALONE)
  290. self._write_SOCKS5_address(address, header)
  291. sent = super(socksocket, self).send(header.getvalue() + bytes, *flags,
  292. **kwargs)
  293. return sent - header.tell()
  294. def send(self, bytes, flags=0, **kwargs):
  295. if self.type == socket.SOCK_DGRAM:
  296. return self.sendto(bytes, flags, self.proxy_peername, **kwargs)
  297. else:
  298. return super(socksocket, self).send(bytes, flags, **kwargs)
  299. def recvfrom(self, bufsize, flags=0):
  300. if self.type != socket.SOCK_DGRAM:
  301. return super(socksocket, self).recvfrom(bufsize, flags)
  302. if not self._proxyconn:
  303. self.bind(("", 0))
  304. buf = BytesIO(super(socksocket, self).recv(bufsize + 1024, flags))
  305. buf.seek(2, SEEK_CUR)
  306. frag = buf.read(1)
  307. if ord(frag):
  308. raise NotImplementedError("Received UDP packet fragment")
  309. fromhost, fromport = self._read_SOCKS5_address(buf)
  310. if self.proxy_peername:
  311. peerhost, peerport = self.proxy_peername
  312. if fromhost != peerhost or peerport not in (0, fromport):
  313. raise socket.error(EAGAIN, "Packet filtered")
  314. return (buf.read(bufsize), (fromhost, fromport))
  315. def recv(self, *pos, **kw):
  316. bytes, _ = self.recvfrom(*pos, **kw)
  317. return bytes
  318. def close(self):
  319. if self._proxyconn:
  320. self._proxyconn.close()
  321. return super(socksocket, self).close()
  322. def get_proxy_sockname(self):
  323. """Returns the bound IP address and port number at the proxy."""
  324. return self.proxy_sockname
  325. getproxysockname = get_proxy_sockname
  326. def get_proxy_peername(self):
  327. """
  328. Returns the IP and port number of the proxy.
  329. """
  330. return self.getpeername()
  331. getproxypeername = get_proxy_peername
  332. def get_peername(self):
  333. """Returns the IP address and port number of the destination machine.
  334. Note: get_proxy_peername returns the proxy."""
  335. return self.proxy_peername
  336. getpeername = get_peername
  337. def _negotiate_SOCKS5(self, *dest_addr):
  338. """Negotiates a stream connection through a SOCKS5 server."""
  339. CONNECT = b"\x01"
  340. self.proxy_peername, self.proxy_sockname = self._SOCKS5_request(
  341. self, CONNECT, dest_addr)
  342. def _SOCKS5_request(self, conn, cmd, dst):
  343. """
  344. Send SOCKS5 request with given command (CMD field) and
  345. address (DST field). Returns resolved DST address that was used.
  346. """
  347. proxy_type, addr, port, rdns, username, password = self.proxy
  348. writer = conn.makefile("wb")
  349. reader = conn.makefile("rb", 0) # buffering=0 renamed in Python 3
  350. try:
  351. # First we'll send the authentication packages we support.
  352. if username and password:
  353. # The username/password details were supplied to the
  354. # set_proxy method so we support the USERNAME/PASSWORD
  355. # authentication (in addition to the standard none).
  356. writer.write(b"\x05\x02\x00\x02")
  357. else:
  358. # No username/password were entered, therefore we
  359. # only support connections with no authentication.
  360. writer.write(b"\x05\x01\x00")
  361. # We'll receive the server's response to determine which
  362. # method was selected
  363. writer.flush()
  364. chosen_auth = self._readall(reader, 2)
  365. if chosen_auth[0:1] != b"\x05":
  366. # Note: string[i:i+1] is used because indexing of a bytestring
  367. # via bytestring[i] yields an integer in Python 3
  368. raise GeneralProxyError(
  369. "SOCKS5 proxy server sent invalid data")
  370. # Check the chosen authentication method
  371. if chosen_auth[1:2] == b"\x02":
  372. # Okay, we need to perform a basic username/password
  373. # authentication.
  374. if not (username and password):
  375. # Although we said we don't support authentication, the
  376. # server may still request basic username/password
  377. # authentication
  378. raise SOCKS5AuthError("No username/password supplied. "
  379. "Server requested username/password"
  380. " authentication")
  381. writer.write(b"\x01" + chr(len(username)).encode()
  382. + username
  383. + chr(len(password)).encode()
  384. + password)
  385. writer.flush()
  386. auth_status = self._readall(reader, 2)
  387. if auth_status[0:1] != b"\x01":
  388. # Bad response
  389. raise GeneralProxyError(
  390. "SOCKS5 proxy server sent invalid data")
  391. if auth_status[1:2] != b"\x00":
  392. # Authentication failed
  393. raise SOCKS5AuthError("SOCKS5 authentication failed")
  394. # Otherwise, authentication succeeded
  395. # No authentication is required if 0x00
  396. elif chosen_auth[1:2] != b"\x00":
  397. # Reaching here is always bad
  398. if chosen_auth[1:2] == b"\xFF":
  399. raise SOCKS5AuthError(
  400. "All offered SOCKS5 authentication methods were"
  401. " rejected")
  402. else:
  403. raise GeneralProxyError(
  404. "SOCKS5 proxy server sent invalid data")
  405. # Now we can request the actual connection
  406. writer.write(b"\x05" + cmd + b"\x00")
  407. resolved = self._write_SOCKS5_address(dst, writer)
  408. writer.flush()
  409. # Get the response
  410. resp = self._readall(reader, 3)
  411. if resp[0:1] != b"\x05":
  412. raise GeneralProxyError(
  413. "SOCKS5 proxy server sent invalid data")
  414. status = ord(resp[1:2])
  415. if status != 0x00:
  416. # Connection failed: server returned an error
  417. error = SOCKS5_ERRORS.get(status, "Unknown error")
  418. raise SOCKS5Error("{:#04x}: {}".format(status, error))
  419. # Get the bound address/port
  420. bnd = self._read_SOCKS5_address(reader)
  421. super(socksocket, self).settimeout(self._timeout)
  422. return (resolved, bnd)
  423. finally:
  424. reader.close()
  425. writer.close()
  426. def _write_SOCKS5_address(self, addr, file):
  427. """
  428. Return the host and port packed for the SOCKS5 protocol,
  429. and the resolved address as a tuple object.
  430. """
  431. host, port = addr
  432. proxy_type, _, _, rdns, username, password = self.proxy
  433. family_to_byte = {socket.AF_INET: b"\x01", socket.AF_INET6: b"\x04"}
  434. # If the given destination address is an IP address, we'll
  435. # use the IP address request even if remote resolving was specified.
  436. # Detect whether the address is IPv4/6 directly.
  437. for family in (socket.AF_INET, socket.AF_INET6):
  438. try:
  439. addr_bytes = socket.inet_pton(family, host)
  440. file.write(family_to_byte[family] + addr_bytes)
  441. host = socket.inet_ntop(family, addr_bytes)
  442. file.write(struct.pack(">H", port))
  443. return host, port
  444. except socket.error:
  445. continue
  446. # Well it's not an IP number, so it's probably a DNS name.
  447. if rdns:
  448. # Resolve remotely
  449. host_bytes = host.encode("idna")
  450. file.write(b"\x03" + chr(len(host_bytes)).encode() + host_bytes)
  451. else:
  452. # Resolve locally
  453. addresses = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
  454. socket.SOCK_STREAM,
  455. socket.IPPROTO_TCP,
  456. socket.AI_ADDRCONFIG)
  457. # We can't really work out what IP is reachable, so just pick the
  458. # first.
  459. target_addr = addresses[0]
  460. family = target_addr[0]
  461. host = target_addr[4][0]
  462. addr_bytes = socket.inet_pton(family, host)
  463. file.write(family_to_byte[family] + addr_bytes)
  464. host = socket.inet_ntop(family, addr_bytes)
  465. file.write(struct.pack(">H", port))
  466. return host, port
  467. def _read_SOCKS5_address(self, file):
  468. atyp = self._readall(file, 1)
  469. if atyp == b"\x01":
  470. addr = socket.inet_ntoa(self._readall(file, 4))
  471. elif atyp == b"\x03":
  472. length = self._readall(file, 1)
  473. addr = self._readall(file, ord(length))
  474. elif atyp == b"\x04":
  475. addr = socket.inet_ntop(socket.AF_INET6, self._readall(file, 16))
  476. else:
  477. raise GeneralProxyError("SOCKS5 proxy server sent invalid data")
  478. port = struct.unpack(">H", self._readall(file, 2))[0]
  479. return addr, port
  480. def _negotiate_SOCKS4(self, dest_addr, dest_port):
  481. """Negotiates a connection through a SOCKS4 server."""
  482. proxy_type, addr, port, rdns, username, password = self.proxy
  483. writer = self.makefile("wb")
  484. reader = self.makefile("rb", 0) # buffering=0 renamed in Python 3
  485. try:
  486. # Check if the destination address provided is an IP address
  487. remote_resolve = False
  488. try:
  489. addr_bytes = socket.inet_aton(dest_addr)
  490. except socket.error:
  491. # It's a DNS name. Check where it should be resolved.
  492. if rdns:
  493. addr_bytes = b"\x00\x00\x00\x01"
  494. remote_resolve = True
  495. else:
  496. addr_bytes = socket.inet_aton(
  497. socket.gethostbyname(dest_addr))
  498. # Construct the request packet
  499. writer.write(struct.pack(">BBH", 0x04, 0x01, dest_port))
  500. writer.write(addr_bytes)
  501. # The username parameter is considered userid for SOCKS4
  502. if username:
  503. writer.write(username)
  504. writer.write(b"\x00")
  505. # DNS name if remote resolving is required
  506. # NOTE: This is actually an extension to the SOCKS4 protocol
  507. # called SOCKS4A and may not be supported in all cases.
  508. if remote_resolve:
  509. writer.write(dest_addr.encode("idna") + b"\x00")
  510. writer.flush()
  511. # Get the response from the server
  512. resp = self._readall(reader, 8)
  513. if resp[0:1] != b"\x00":
  514. # Bad data
  515. raise GeneralProxyError(
  516. "SOCKS4 proxy server sent invalid data")
  517. status = ord(resp[1:2])
  518. if status != 0x5A:
  519. # Connection failed: server returned an error
  520. error = SOCKS4_ERRORS.get(status, "Unknown error")
  521. raise SOCKS4Error("{:#04x}: {}".format(status, error))
  522. # Get the bound address/port
  523. self.proxy_sockname = (socket.inet_ntoa(resp[4:]),
  524. struct.unpack(">H", resp[2:4])[0])
  525. if remote_resolve:
  526. self.proxy_peername = socket.inet_ntoa(addr_bytes), dest_port
  527. else:
  528. self.proxy_peername = dest_addr, dest_port
  529. finally:
  530. reader.close()
  531. writer.close()
  532. def _negotiate_HTTP(self, dest_addr, dest_port):
  533. """Negotiates a connection through an HTTP server.
  534. NOTE: This currently only supports HTTP CONNECT-style proxies."""
  535. proxy_type, addr, port, rdns, username, password = self.proxy
  536. # If we need to resolve locally, we do this now
  537. addr = dest_addr if rdns else socket.gethostbyname(dest_addr)
  538. http_headers = [
  539. (b"CONNECT " + addr.encode("idna") + b":"
  540. + str(dest_port).encode() + b" HTTP/1.1"),
  541. b"Host: " + dest_addr.encode("idna")
  542. ]
  543. if username and password:
  544. http_headers.append(b"Proxy-Authorization: basic "
  545. + b64encode(username + b":" + password))
  546. http_headers.append(b"\r\n")
  547. self.sendall(b"\r\n".join(http_headers))
  548. # We just need the first line to check if the connection was successful
  549. fobj = self.makefile()
  550. status_line = fobj.readline()
  551. fobj.close()
  552. if not status_line:
  553. raise GeneralProxyError("Connection closed unexpectedly")
  554. try:
  555. proto, status_code, status_msg = status_line.split(" ", 2)
  556. except ValueError:
  557. raise GeneralProxyError("HTTP proxy server sent invalid response")
  558. if not proto.startswith("HTTP/"):
  559. raise GeneralProxyError(
  560. "Proxy server does not appear to be an HTTP proxy")
  561. try:
  562. status_code = int(status_code)
  563. except ValueError:
  564. raise HTTPError(
  565. "HTTP proxy server did not return a valid HTTP status")
  566. if status_code != 200:
  567. error = "{}: {}".format(status_code, status_msg)
  568. if status_code in (400, 403, 405):
  569. # It's likely that the HTTP proxy server does not support the
  570. # CONNECT tunneling method
  571. error += ("\n[*] Note: The HTTP proxy server may not be"
  572. " supported by PySocks (must be a CONNECT tunnel"
  573. " proxy)")
  574. raise HTTPError(error)
  575. self.proxy_sockname = (b"0.0.0.0", 0)
  576. self.proxy_peername = addr, dest_port
  577. _proxy_negotiators = {
  578. SOCKS4: _negotiate_SOCKS4,
  579. SOCKS5: _negotiate_SOCKS5,
  580. HTTP: _negotiate_HTTP
  581. }
  582. @set_self_blocking
  583. def connect(self, dest_pair, catch_errors=None):
  584. """
  585. Connects to the specified destination through a proxy.
  586. Uses the same API as socket's connect().
  587. To select the proxy server, use set_proxy().
  588. dest_pair - 2-tuple of (IP/hostname, port).
  589. """
  590. if len(dest_pair) != 2 or dest_pair[0].startswith("["):
  591. # Probably IPv6, not supported -- raise an error, and hope
  592. # Happy Eyeballs (RFC6555) makes sure at least the IPv4
  593. # connection works...
  594. raise socket.error("PySocks doesn't support IPv6: %s"
  595. % str(dest_pair))
  596. dest_addr, dest_port = dest_pair
  597. if self.type == socket.SOCK_DGRAM:
  598. if not self._proxyconn:
  599. self.bind(("", 0))
  600. dest_addr = socket.gethostbyname(dest_addr)
  601. # If the host address is INADDR_ANY or similar, reset the peer
  602. # address so that packets are received from any peer
  603. if dest_addr == "0.0.0.0" and not dest_port:
  604. self.proxy_peername = None
  605. else:
  606. self.proxy_peername = (dest_addr, dest_port)
  607. return
  608. (proxy_type, proxy_addr, proxy_port, rdns, username,
  609. password) = self.proxy
  610. # Do a minimal input check first
  611. if (not isinstance(dest_pair, (list, tuple))
  612. or len(dest_pair) != 2
  613. or not dest_addr
  614. or not isinstance(dest_port, int)):
  615. # Inputs failed, raise an error
  616. raise GeneralProxyError(
  617. "Invalid destination-connection (host, port) pair")
  618. # We set the timeout here so that we don't hang in connection or during
  619. # negotiation.
  620. super(socksocket, self).settimeout(self._timeout)
  621. if proxy_type is None:
  622. # Treat like regular socket object
  623. self.proxy_peername = dest_pair
  624. super(socksocket, self).settimeout(self._timeout)
  625. super(socksocket, self).connect((dest_addr, dest_port))
  626. return
  627. proxy_addr = self._proxy_addr()
  628. try:
  629. # Initial connection to proxy server.
  630. super(socksocket, self).connect(proxy_addr)
  631. except socket.error as error:
  632. # Error while connecting to proxy
  633. self.close()
  634. if not catch_errors:
  635. proxy_addr, proxy_port = proxy_addr
  636. proxy_server = "{}:{}".format(proxy_addr, proxy_port)
  637. printable_type = PRINTABLE_PROXY_TYPES[proxy_type]
  638. msg = "Error connecting to {} proxy {}".format(printable_type,
  639. proxy_server)
  640. log.debug("%s due to: %s", msg, error)
  641. raise ProxyConnectionError(msg, error)
  642. else:
  643. raise error
  644. else:
  645. # Connected to proxy server, now negotiate
  646. try:
  647. # Calls negotiate_{SOCKS4, SOCKS5, HTTP}
  648. negotiate = self._proxy_negotiators[proxy_type]
  649. negotiate(self, dest_addr, dest_port)
  650. except socket.error as error:
  651. if not catch_errors:
  652. # Wrap socket errors
  653. self.close()
  654. raise GeneralProxyError("Socket error", error)
  655. else:
  656. raise error
  657. except ProxyError:
  658. # Protocol error while negotiating with proxy
  659. self.close()
  660. raise
  661. @set_self_blocking
  662. def connect_ex(self, dest_pair):
  663. """ https://docs.python.org/3/library/socket.html#socket.socket.connect_ex
  664. 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).
  665. """
  666. try:
  667. self.connect(dest_pair, catch_errors=True)
  668. return 0
  669. except OSError as e:
  670. # If the error is numeric (socket errors are numeric), then return number as
  671. # connect_ex expects. Otherwise raise the error again (socket timeout for example)
  672. if e.errno:
  673. return e.errno
  674. else:
  675. raise
  676. def _proxy_addr(self):
  677. """
  678. Return proxy address to connect to as tuple object
  679. """
  680. (proxy_type, proxy_addr, proxy_port, rdns, username,
  681. password) = self.proxy
  682. proxy_port = proxy_port or DEFAULT_PORTS.get(proxy_type)
  683. if not proxy_port:
  684. raise GeneralProxyError("Invalid proxy type")
  685. return proxy_addr, proxy_port