def get_next_link(self, request: Request) -> Optional[Link]: if not self.should_accept_request(request): logger.error(str(self), "Request {} rejected.".format(request)) return None high_priority, normal_priority, low_priority = self.get_link_ids_for_request( request) if len(high_priority) != 0: links_id = high_priority elif len(normal_priority) != 0: links_id = normal_priority elif len(low_priority) != 0: links_id = low_priority else: logger.error( str(self), "No link available to take this request ({}).".format(request)) return None links = [self.links[link_id] for link_id in links_id] self._last_link = get_next_link(links, self._last_link, self.strategy) return self._last_link
def _socks_request(self, socket_client: socket) -> (Optional[Link], Optional[int], Optional[socks.socksocket]): (domain, port) = self._socks_request_get_dest(socket_client) request = Request(domain, port) link = self.balancer.get_next_link(request) if link is None: logger.error(str(self), "No Link available to handle the request.") return None connection_id = self.generate_connection_id() socket_link = link.open_connection(connection_id) if socket_link is None: link.close_connection(connection_id) return None try: socket_link.connect((domain, port)) except socket.error as err: logger.error(str(self), "Socket error while trying to connect to {}:{}: \"{}\".".format(domain, port, err)) link.close_connection(connection_id) self._socks_request_send_reply(socket_client, SocksReply.NETWORK_UNREACHABLE) return None if not self._socks_request_send_reply(socket_client, SocksReply.SUCCEEDED): link.close_connection(connection_id) return None return link, connection_id, socket_link
def _socks_sub_negotiation_send_chosen_method(self, socket_client: socket, method: SocksMethod) -> bool: chosen_method_packet = b'\x05' + method.value try: socket_client.sendall(chosen_method_packet) except socket.error as err: logger.error(str(self), "Socket error while trying to communicate with client: \"{}\".".format(err)) return False return True
def open_connection(self, connection_id: str) -> Optional[socks.socksocket]: if connection_id in self.connections: logger.error( str(self), "Connection id '{}' already in use for this link.".format( connection_id)) return None self.connections[connection_id] = self._build_socket() return self.connections[connection_id]
def _socks_request_send_reply(self, socket_client: socket, reply: SocksReply) -> bool: reply_packet = b'\x05' + reply.value + b'\x00' + SocksAddressType.IPV4.value + \ b'\x00' + b'\x00' + b'\x00' + b'\x00' + \ b'\x00' + b'\x00' try: socket_client.sendall(reply_packet) except socket.error as err: logger.error(str(self), "Socket error while trying to communicate with client: \"{}\".".format(err)) return False return True
def _accept_client_loop(self, server_socket: socket): logger.info(str(self), "Ready to receive requests.") while not self.STOP: if threading.active_count() > self.max_threads: sleep(5) # It means that the thread will take 5 seconds maximum to return continue try: client_socket, _ = server_socket.accept() client_socket.setblocking(True) except socket.timeout: continue except socket.error: continue except TypeError as err: logger.error(str(self), "Error: \"{}\".".format(err)) return exchange_thread = threading.Thread(target=self.handle_request, args=(client_socket,)) exchange_thread.start() server_socket.close() logger.info(str(self), "Stopping server.")
def start(self) -> bool: self.STOP = False try: server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.settimeout(self.timeout) except socket.error as err: logger.error(str(self), "Failed to create the socket server, error: \"{}\".".format(err)) return False try: server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_socket.bind((self.domain, self.port)) logger.info(str(self), 'Bind {}.'.format(str(self.port))) except socket.error as err: server_socket.close() logger.error(str(self), "Cannot bind {}:{}, error: \"{}\".".format(self.domain, self.port, err)) return False try: server_socket.listen(10) except socket.error as err: server_socket.close() logger.error(str(self), "Listen failed, error: \"{}\".".format(err)) return False self._server_thread = threading.Thread(target=self._accept_client_loop, args=(server_socket,)) self._server_thread.start() self._balancer_thread = threading.Thread(target=self._balancer_loop) self._balancer_thread.start() return True
def _exchange_with_client(self, socket_client: socket, socket_link: socks.socksocket): while not self.STOP: try: reader, _, _ = select.select([socket_client, socket_link], [], [], socket_link.gettimeout()) except select.error as err: logger.error(str(self), "Select failed: \"{}\".".format(err)) return if not reader: return try: for sock in reader: data = sock.recv(2048) if not data: return if sock is socket_link: socket_client.send(data) else: socket_link.send(data) except socket.error as err: logger.error(str(self), "Socket error while trying to communicate with client: \"{}\".".format(err)) return
def _socks_request_get_dest(self, socket_client: socket) -> (Optional[str], Optional[int]): try: request_packet = socket_client.recv(2048) except socket.error: logger.error(str(self), "Socket error while trying to communicate with client.") self._socks_request_send_reply(socket_client, SocksReply.SERVER_FAILURE) raise Exception() ver = request_packet[0] cmd = request_packet[1] atyp = request_packet[3] if ver != 5: logger.error(str(self), "SOCKS version '{}' not supported.".format(ver)) self._socks_request_send_reply(socket_client, SocksReply.CONNECTION_REFUSED) return None if cmd != ord(SocksCommand.CONNECT.value): logger.error(str(self), "SOCKS command '{}' not supported.".format(cmd)) self._socks_request_send_reply(socket_client, SocksReply.COMMAND_NOT_SUPPORTED) return None if atyp == ord(SocksAddressType.IPV4.value): dst_addr = socket.inet_ntop(socket.AF_INET, request_packet[4:-2]) elif atyp == ord(SocksAddressType.DOMAINNAME.value): dst_addr = request_packet[5:-2].decode() elif atyp == ord(SocksAddressType.IPV6.value): dst_addr = socket.inet_ntop(socket.AF_INET6, request_packet[4:-2]) else: logger.error(str(self), "SOCKS address type '{}' not supported.".format(atyp)) self._socks_request_send_reply(socket_client, SocksReply.ADDRESS_TYPE_NOT_SUPPORTED) return None dst_port = unpack('>H', request_packet[-2:])[0] return dst_addr, dst_port
def _socks_sub_negotiation_choose_method(self, socket_client: socket) -> SocksMethod: try: methods_packet = socket_client.recv(2048) except socket.error: logger.error(str(self), "Socket error while trying to communicate with client.") return SocksMethod.NO_ACCEPTABLE_METHODS ver = methods_packet[0] nmethods = methods_packet[1] methods = methods_packet[2:] if ver != 5: logger.error(str(self), "SOCKS version '{}' not supported.".format(ver)) return SocksMethod.NO_ACCEPTABLE_METHODS if nmethods != len(methods): logger.error(str(self), "Malformed SOCKS packet received from client.") return SocksMethod.NO_ACCEPTABLE_METHODS if ord(SocksMethod.NO_AUTH.value) in methods: return SocksMethod.NO_AUTH return SocksMethod.NO_ACCEPTABLE_METHODS