async def _handle_user(self, user): # ignore client SOCKS5 greeting data = await user.reader.read(256) logger.debug('ignore SOCK5 greeting ({} bytes)'.format(len(data))) # response greeting without auth server_greeting = socks.ServerGreeting() await self.safe_write(user.writer, server_greeting.to_bytes()) # recv CMD try: msg = await socks.Message.from_reader(user.reader) except asyncio.streams.IncompleteReadError: self._delete_user(user) return if msg.code is not socks.CMD.CONNECT: logger.warn('unhandle msg {}'.format(msg)) rep = socks.Message(socks.VER.SOCKS5, socks.REP.COMMAND_NOT_SUPPORTED, socks.ATYPE.IPV4, ('0', 0)) await self.safe_write(user.writer, rep.to_bytes()) return logger.info('connecting {}:{}'.format(msg.addr[0], msg.addr[1])) # send to tunnel connect_reqeust = protocol.Request(user.user_id, 0, msg) await self.safe_write(self.tunnel_writer, connect_reqeust.to_packet(self.fuzz)) await self._pipe_user(user)
async def connect(self, host, port): self.state = self.CMD loop = asyncio.get_event_loop() bind_addr = ('255.255.255.255', 0) try: logger.info('connecting {}:{}'.format(host, port)) fut = loop.create_connection(Client, host, port) transport, client = await \ asyncio.wait_for(fut, timeout=config.timeout) except (asyncio.TimeoutError, ConnectionRefusedError, socket.gaierror) as e: logger.warn('connect {}'.format(e)) socks_err = socks.Message(socks.VER.SOCKS5, socks.REP.NETWORK_UNREACHABLE, socks.ATYPE.IPV4, bind_addr) rep = protocol.Reply(self.remote, self.user, socks_err) self.tunnel_transport.write(rep.to_packet(self.fuzz)) self.state = self.IDLE return client.channel = self self.remote_transport = transport self.remote = transport._sock_fd bind_addr = transport.get_extra_info('sockname') socks_ok = socks.Message(socks.VER.SOCKS5, socks.REP.SUCCEEDED, socks.ATYPE.IPV4, bind_addr) rep = protocol.Reply(self.remote, self.user, socks_ok) self.tunnel_transport.write(rep.to_packet(self.fuzz)) self.state = self.DATA logger.debug('channel {} opened'.format(self))
def packet_received(self, packet_data): if self.state == self.GREETING: packet = protocol.read_packet(io.BytesIO(packet_data), self.cipher) if packet.mtype is not protocol.MTYPE.HELLO: self.transport.abort() self.state = self.CLOSING return self.transport.write(protocol.Hello().to_packet(self.cipher)) self.tunnel = Tunnel(self.transport, self.fuzz) self.state = self.NEGOTIATING elif self.state == self.NEGOTIATING: packet = protocol.read_packet(io.BytesIO(packet_data), self.cipher) if packet.mtype is not protocol.MTYPE.HANDSHAKE: self.transport.abort() self.state = self.CLOSING fuzz = self.choose_fuzzer(packet.fuzz.fuzz_list) logger.info('choose {}'.format(fuzz)) response = protocol.HandShake(fuzz=fuzz) self.transport.write(response.to_packet(self.cipher)) self.fuzz = fuzz self.tunnel.fuzz = fuzz self.state = self.OPEN elif self.state == self.OPEN: packet = protocol.read_packet(io.BytesIO(packet_data), self.fuzz) self.tunnel.handle_request(packet) else: logger.warn('tunel is closing')
def data_received(self, data): # logger.debug('{}: {}'.format(self.user, data)) if self.state == self.INIT: client_greeting = socks.ClientGreeting.from_stream( io.BytesIO(data)) server_greeting = socks.ServerGreeting() self.transport.write(server_greeting.to_bytes()) self.state = self.CMD elif self.state == self.CMD: msg = socks.Message.from_stream(io.BytesIO(data)) global concurrent logger.info('connecting {}:{} ({})'.format(msg.addr[0], msg.addr[1], concurrent)) bind_addr = ('127.0.0.1', 9999) if msg.code is not socks.CMD.CONNECT: rep = socks.Message(socks.VER.SOCKS5, socks.REP.COMMAND_NOT_SUPPORTED, socks.ATYPE.IPV4, bind_addr) self.transport.write(rep.to_bytes()) return # connect asyncio.ensure_future(self.connect(msg.addr[0], msg.addr[1])) elif self.state == self.DATA: self.client_transport.write(data) else: logger.warn('receiving data from user in CLOSED state')
async def _handle_client(self, client_reader, client_writer): # ignore client greeting data = await client_reader.read(32) logger.debug('Ignore client greeting ({} bytes)'.format(len(data))) server_greeting = socks.ServerGreeting() client_writer.write(server_greeting.to_bytes()) # recv CMD msg = await socks.Message.from_reader(client_reader) if msg.code is not socks.CMD.CONNECT: logger.warn('unhandle msg {}'.format(msg)) return fut = asyncio.open_connection(msg.addr[0], msg.addr[1]) try: remote_reader, remote_writer = await asyncio.wait_for(fut, 3) except (asyncio.TimeoutError, ConnectionRefusedError): logger.warn('connet {}:{} failed'.format(msg.addr[0], msg.addr[1])) err_reply = socks.Message(ver=socks.VER.SOCKS5, code=socks.REP.CONNECTION_REFUSED, atype=socks.ATYPE.IPV4, addr=('127.0.0.1', 9999)) client_writer.write(err_reply.to_bytes()) return logger.info('connected to {}:{}'.format(msg.addr[0], msg.addr[1])) # send REP bind_address = remote_writer.transport._extra['sockname'] reply = socks.Message(ver=socks.VER.SOCKS5, code=socks.REP.SUCCEEDED, atype=socks.ATYPE.IPV4, addr=bind_address) client_writer.write(reply.to_bytes()) # piping await asyncio.gather(pipe('remote', remote_reader, client_writer), pipe('user', client_reader, remote_writer))
def forward(self, payload, upstream=True): if self.state != self.DATA: logger.warn('channel is not ready') return if upstream: return self.remote_transport.write(payload) packet = protocol.Relaying(self.remote, self.user, payload) return self.tunnel_transport.write(packet.to_packet(self.fuzz))
async def _pipe_user(self, user): # may start before connection to remote is established while True: try: data = await user.reader.read(2048) except ConnectionResetError: logger.warn('user connection reset') data = b'' if len(data) == 0: self._user_closed(user) break assert user.established packet = protocol.Relaying(user.user_id, user.remote_id, data) await self.safe_write(self.tunnel_writer, packet.to_packet(self.fuzz))
async def pipe(name, reader, writer): while True: try: data = await reader.read(2048) except ConnectionResetError as e: logger.warn('connection reset by ' + name) break except asyncio.CancelledError as e: logger.debug('pipe canceled.') break if len(data) == 0: logger.debug('{} connection closed'.format(name)) break writer.write(data) return None
def handle_request(self, packet): if packet.mtype is protocol.MTYPE.REQUEST: msg = packet.msg if msg.code is not socks.CMD.CONNECT: logger.warn('unsupported msg: {}'.format(msg)) return user = packet.src chan = Channel(self.transport, self.fuzz, user) asyncio.ensure_future(chan.connect(msg.addr[0], msg.addr[1])) self.channels[user] = chan elif packet.mtype is protocol.MTYPE.RELAYING: user = packet.src self.channels[user].forward(packet.payload) elif packet.mtype is protocol.MTYPE.CLOSE: user = packet.src self.channels[user].close() else: logger.warn('unkown packet {}'.format(packet))
async def _handle_tunnel(self, reader, writer): logger.debug('_handle_tunnel started') while True: packet = await protocol.async_read_packet(reader, self.fuzz) if packet.mtype is protocol.MTYPE.REPLY: # received a SOCKS reply, update mapping # and forward to corresponding user remote_id = packet.src user_id = packet.dst user = self._get_user(user_id) if user is None: # Tell server to close continue await self.safe_write(user.writer, packet.msg.to_bytes()) user.remote_id = remote_id elif packet.mtype is protocol.MTYPE.RELAYING: # received raw data, forwarding remote_id = packet.src user_id = packet.dst user = self._get_user(user_id) if user is None: # Tell server to close continue await self.safe_write(user.writer, packet.payload) elif packet.mtype is protocol.MTYPE.CLOSE: # close user tansport user_id = packet.src logger.debug( 'remote disconnected, close user {}'.format(user_id)) user = self._get_user(user_id) if user is None: # ignore continue self._delete_user(user) else: logger.warn('unknown packet {}'.format(packet)) logger.debug('_handle_tunnel exited')
async def safe_write(self, writer, data): writer.write(data) try: await writer.drain() except ConnectionResetError as e: logger.warn('write error: {}'.format(e))
def tunnel_done(task): logger.warn('tunnel is closed') sys.exit(2)
def connection_lost(self, exc): global concurrent concurrent -= 1 if exc is not None: logger.warn('remote closed: {}'.format(exc)) self.channel.close()
def data_received(self, data): if self.state == self.OPEN: self.server_transport.write(data) else: logger.warn('receiving data from server in CLOSED state')