mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-10-30 22:25:19 +00:00 
			
		
		
		
	[socks] Add socks.py from @bluec0re's public domain implementation
https://gist.github.com/bluec0re/cafd3764412967417fd3
This commit is contained in:
		
							
								
								
									
										336
									
								
								youtube_dl/socks.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										336
									
								
								youtube_dl/socks.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,336 @@ | ||||
| # This is free and unencumbered software released into the public domain. | ||||
| #  | ||||
| # Anyone is free to copy, modify, publish, use, compile, sell, or | ||||
| # distribute this software, either in source code form or as a compiled | ||||
| # binary, for any purpose, commercial or non-commercial, and by any | ||||
| # means. | ||||
| #  | ||||
| # In jurisdictions that recognize copyright laws, the author or authors | ||||
| # of this software dedicate any and all copyright interest in the | ||||
| # software to the public domain. We make this dedication for the benefit | ||||
| # of the public at large and to the detriment of our heirs and | ||||
| # successors. We intend this dedication to be an overt act of | ||||
| # relinquishment in perpetuity of all present and future rights to this | ||||
| # software under copyright law. | ||||
| #  | ||||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||||
| # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||||
| # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||||
| # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||||
| # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||||
| # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | ||||
| # OTHER DEALINGS IN THE SOFTWARE. | ||||
| #  | ||||
| # For more information, please refer to <http://unlicense.org/> | ||||
| # | ||||
| # Example: | ||||
| # import socks | ||||
| # import ftplib | ||||
| # import socket | ||||
| # | ||||
| # socks.patch_socket() | ||||
| # | ||||
| # f = ftplib.FTP('ftp.kernel.org') | ||||
| # f.login() | ||||
| # print f.retrlines('LIST') | ||||
| # f.quit() | ||||
| #  | ||||
| # s = socket.create_connection(('www.google.com', 80)) | ||||
| # s.sendall('HEAD / HTTP/1.0\r\n\r\n') | ||||
| # print s.recv(1024) | ||||
| # s.close() | ||||
| from __future__ import unicode_literals | ||||
| import os | ||||
| import struct | ||||
| import socket | ||||
| import time | ||||
|  | ||||
| __author__ = 'Timo Schmid <coding@timoschmid.de>' | ||||
|  | ||||
| _orig_socket = socket.socket | ||||
|  | ||||
| try: | ||||
|     from collections import namedtuple | ||||
| except ImportError: | ||||
|     from Collections import namedtuple | ||||
|  | ||||
| try: | ||||
|     from urllib.parse import urlparse | ||||
| except: | ||||
|     from urlparse import urlparse | ||||
|  | ||||
| try: | ||||
|     from enum import Enum | ||||
| except ImportError: | ||||
|     Enum = object | ||||
|  | ||||
|  | ||||
| class ProxyError(IOError): pass | ||||
| class Socks4Error(ProxyError): | ||||
|     CODES = { | ||||
|         0x5B: 'request rejected or failed', | ||||
|         0x5C: 'request rejected becasue SOCKS server cannot connect to identd on the client', | ||||
|         0x5D: 'request rejected because the client program and identd report different user-ids' | ||||
|     } | ||||
|     def __init__(self, code=None, msg=None): | ||||
|         if code is not None and msg is None: | ||||
|             msg = self.CODES.get(code) | ||||
|             if msg is None: | ||||
|                 msg = 'unknown error' | ||||
|         super(Socks4Error, self).__init__(code, msg) | ||||
|  | ||||
| class Socks5Error(Socks4Error): | ||||
|     CODES = { | ||||
|         0x01: 'general SOCKS server failure', | ||||
|         0x02: 'connection not allowed by ruleset', | ||||
|         0x03: 'Network unreachable', | ||||
|         0x04: 'Host unreachable', | ||||
|         0x05: 'Connection refused', | ||||
|         0x06: 'TTL expired', | ||||
|         0x07: 'Command not supported', | ||||
|         0x08: 'Address type not supported', | ||||
|         0xFE: 'unknown username or invalid password', | ||||
|         0xFF: 'all offered authentication methods were rejected' | ||||
|     } | ||||
|  | ||||
| class ProxyType(Enum): | ||||
|     SOCKS4  = 0 | ||||
|     SOCKS4A = 1 | ||||
|     SOCKS5  = 2 | ||||
|  | ||||
| Proxy = namedtuple('Proxy', ('type', 'host', 'port', 'username', 'password', 'remote_dns')) | ||||
|  | ||||
| _default_proxy = None | ||||
|  | ||||
| def setdefaultproxy(proxytype=None, addr=None, port=None, rdns=True, username=None, password=None, allow_env_override=True): | ||||
|     global _default_proxy | ||||
|     if allow_env_override: | ||||
|         all_proxy = os.environ.get('ALL_PROXY', os.environ.get('all_proxy')) | ||||
|         if all_proxy: | ||||
|             all_proxy = urlparse(all_proxy) | ||||
|             if all_proxy.scheme.startswith('socks'): | ||||
|                 if all_proxy.scheme == 'socks' or all_proxy.scheme == 'socks4': | ||||
|                     proxytype = ProxyType.SOCKS4 | ||||
|                 elif all_proxy.scheme == 'socks4a': | ||||
|                     proxytype = ProxyType.SOCKS4A | ||||
|                 elif all_proxy.scheme == 'socks5': | ||||
|                     proxytype = ProxyType.SOCKS5 | ||||
|                 addr = all_proxy.hostname | ||||
|                 port = all_proxy.port | ||||
|                 username = all_proxy.username | ||||
|                 password = all_proxy.password | ||||
|  | ||||
|     if proxytype is not None: | ||||
|         _default_proxy = Proxy(proxytype, addr, port, username, password, rdns) | ||||
|  | ||||
|  | ||||
| def wrap_socket(sock): | ||||
|     return socksocket(_sock=sock._sock) | ||||
|  | ||||
| def wrap_module(module): | ||||
|     if hasattr(module, 'socket'): | ||||
|         sock = module.socket | ||||
|         if isinstance(sock, socket.socket): | ||||
|             module.socket = sockssocket | ||||
|         elif hasattr(socket, 'socket'): | ||||
|             socket.socket = sockssocket | ||||
|  | ||||
| def patch_socket(): | ||||
|     import sys | ||||
|     if 'socket' not in sys.modules: | ||||
|         import socket | ||||
|     sys.modules['socket'].socket = sockssocket | ||||
|  | ||||
|  | ||||
| class sockssocket(socket.socket): | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         self.__proxy = None | ||||
|         if 'proxy' in kwargs: | ||||
|             self.__proxy = kwargs['proxy'] | ||||
|             del kwargs['proxy'] | ||||
|         super(sockssocket, self).__init__(*args, **kwargs) | ||||
|  | ||||
|     @property | ||||
|     def _proxy(self): | ||||
|         if self.__proxy: | ||||
|             return self.__proxy | ||||
|         return _default_proxy | ||||
|  | ||||
|     @property | ||||
|     def _proxy_port(self): | ||||
|         if self._proxy: | ||||
|             if self._proxy.port: | ||||
|                 return self._proxy.port | ||||
|             return 1080 | ||||
|         return None | ||||
|  | ||||
|     def setproxy(self, proxytype=None, addr=None, port=None, rdns=True, username=None, password=None): | ||||
|         if proxytype is None: | ||||
|             self.__proxy = None | ||||
|         else: | ||||
|             self.__proxy = Proxy(proxytype, addr, port, username, password, rdns) | ||||
|  | ||||
|     def recvall(self, cnt): | ||||
|         data = b'' | ||||
|         while len(data) < cnt: | ||||
|             cur = self.recv(cnt - len(data)) | ||||
|             if not cur: | ||||
|                 raise IOError("{0} bytes missing".format(cnt-len(data))) | ||||
|             data += cur | ||||
|         return data | ||||
|  | ||||
|     def _setup_socks4(self, address, is_4a=False): | ||||
|         destaddr, port = address | ||||
|  | ||||
|         try: | ||||
|             ipaddr = socket.inet_aton(destaddr) | ||||
|         except socket.error: | ||||
|             if is_4a and self._proxy.remote_dns: | ||||
|                 ipaddr = struct.pack('!BBBB', 0, 0, 0, 0xFF) | ||||
|             else: | ||||
|                 ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) | ||||
|  | ||||
|         packet = struct.pack('!BBH', 0x4, 0x1, port) + ipaddr | ||||
|         if self._proxy.username: | ||||
|             username = self._proxy.username | ||||
|             if hasattr(username, 'encode'): | ||||
|                 username = username.encode() | ||||
|             packet += struct.pack('!{0}s'.format(len(username)+1), username) | ||||
|         else: | ||||
|             packet += b'\x00' | ||||
|  | ||||
|         if is_4a and self._proxy.remote_dns: | ||||
|             if hasattr(destaddr, 'encode'): | ||||
|                 destaddr = destaddr.encode() | ||||
|             packet += struct.pack('!{0}s'.format(len(destaddr)+1), destaddr) | ||||
|  | ||||
|         self.sendall(packet) | ||||
|  | ||||
|         packet = self.recvall(8) | ||||
|         nbyte, resp_code, dstport, dsthost = struct.unpack('!BBHI', packet) | ||||
|  | ||||
|         # check valid response | ||||
|         if nbyte != 0x00: | ||||
|             self.close() | ||||
|             raise ProxyError(0, "Invalid response from server. Expected {0:02x} got {1:02x}".format(0, nbyte)) | ||||
|  | ||||
|         # access granted | ||||
|         if resp_code != 0x5a: | ||||
|             self.close() | ||||
|             raise Socks4Error(resp_code) | ||||
|  | ||||
|     def _setup_socks5(self, address): | ||||
|         destaddr, port = address | ||||
|  | ||||
|         try: | ||||
|             ipaddr = socket.inet_aton(destaddr) | ||||
|         except socket.error: | ||||
|             if self._proxy.remote_dns: | ||||
|                 ipaddr = None | ||||
|             else: | ||||
|                 ipaddr = socket.inet_aton(socket.gethostbyname(destaddr)) | ||||
|  | ||||
|         auth_methods = 1 | ||||
|         if self._proxy.username and self._proxy.password: | ||||
|             # two auth methods available | ||||
|             auth_methods = 2 | ||||
|         packet = struct.pack('!BBB', 0x5, auth_methods, 0x00) # no auth | ||||
|         if self._proxy.username and self._proxy.password: | ||||
|             packet += struct.pack('!B', 0x02) # user/pass auth | ||||
|  | ||||
|         self.sendall(packet) | ||||
|  | ||||
|         packet = self.recvall(2) | ||||
|         version, method = struct.unpack('!BB', packet) | ||||
|  | ||||
|         # check valid response | ||||
|         if version != 0x05: | ||||
|             self.close() | ||||
|             raise ProxyError(0, "Invalid response from server. Expected {0:02x} got {1:02x}".format(5, version)) | ||||
|  | ||||
|         # no auth methods | ||||
|         if method == 0xFF: | ||||
|             self.close() | ||||
|             raise Socks5Error(method) | ||||
|  | ||||
|         # user/pass auth | ||||
|         if method == 0x01: | ||||
|             username = self._proxy.username | ||||
|             if hasattr(username, 'encode'): | ||||
|                 username = username.encode() | ||||
|             password = self._proxy.password | ||||
|             if hasattr(password, 'encode'): | ||||
|                 password = password.encode() | ||||
|             packet = struct.pack('!BB', 1, len(username)) + username | ||||
|             packet += struct.pack('!B', len(password)) + password | ||||
|             self.sendall(packet) | ||||
|  | ||||
|             packet = self.recvall(2) | ||||
|             version, status = struct.unpack('!BB', packet) | ||||
|  | ||||
|             if version != 0x01: | ||||
|                 self.close() | ||||
|                 raise ProxyError(0, "Invalid response from server. Expected {0:02x} got {1:02x}".format(1, version)) | ||||
|  | ||||
|             if status != 0x00: | ||||
|                 self.close() | ||||
|                 raise Socks5Error(1) | ||||
|         elif method == 0x00: # no auth | ||||
|             pass | ||||
|  | ||||
|  | ||||
|         packet = struct.pack('!BBB', 5, 1, 0) | ||||
|         if ipaddr is None: | ||||
|             if hasattr(destaddr, 'encode'): | ||||
|                 destaddr = destaddr.encode() | ||||
|             packet += struct.pack('!BB', 3, len(destaddr)) + destaddr | ||||
|         else: | ||||
|             packet += struct.pack('!B', 1) + ipaddr | ||||
|         packet += struct.pack('!H', port) | ||||
|  | ||||
|         self.sendall(packet) | ||||
|  | ||||
|         packet = self.recvall(4) | ||||
|         version, status, _, atype = struct.unpack('!BBBB', packet) | ||||
|  | ||||
|         if version != 0x05: | ||||
|             self.close() | ||||
|             raise ProxyError(0, "Invalid response from server. Expected {0:02x} got {1:02x}".format(5, version)) | ||||
|  | ||||
|         if status != 0x00: | ||||
|             self.close() | ||||
|             raise Socks5Error(status) | ||||
|  | ||||
|         if atype == 0x01: | ||||
|             destaddr = self.recvall(4) | ||||
|         elif atype == 0x03: | ||||
|             alen = struct.unpack('!B', self.recv(1))[0] | ||||
|             destaddr = self.recvall(alen) | ||||
|         elif atype == 0x04: | ||||
|             destaddr = self.recvall(16) | ||||
|         destport = struct.unpack('!H', self.recvall(2))[0] | ||||
|  | ||||
|     def _make_proxy(self, connect_func, address): | ||||
|         if self._proxy.type == ProxyType.SOCKS4: | ||||
|             result = connect_func(self, (self._proxy.host, self._proxy_port)) | ||||
|             if result != 0 and result is not None: | ||||
|                 return result | ||||
|             self._setup_socks4(address) | ||||
|         elif self._proxy.type == ProxyType.SOCKS4A: | ||||
|             result = connect_func(self, (self._proxy.host, self._proxy_port)) | ||||
|             if result != 0 and result is not None: | ||||
|                 return result | ||||
|             self._setup_socks4(address, is_4a=True) | ||||
|         elif self._proxy.type == ProxyType.SOCKS5: | ||||
|             result = connect_func(self, (self._proxy.host, self._proxy_port)) | ||||
|             if result != 0 and result is not None: | ||||
|                 return result | ||||
|             self._setup_socks5(address) | ||||
|         else: | ||||
|             return connect_func(self, address) | ||||
|  | ||||
|     def connect(self, address): | ||||
|         self._make_proxy(_orig_socket.connect, address) | ||||
|  | ||||
|     def connect_ex(self, address): | ||||
|         return self._make_proxy(_orig_socket.connect_ex, address) | ||||
		Reference in New Issue
	
	Block a user
	 Yen Chi Hsuan
					Yen Chi Hsuan