async def tcp_forward_server(conns, server, forward_hosts, speed_limiter): while True: conn = await server.accept() conn_ip, forward_address, subnet = conn.address[0], None, None for a, s in forward_hosts: if is_subnet(conn_ip, s): forward_address, subnet = a, s break if not forward_address: conn.close() logging.info("tcp forward subnet fail %s:%d", conn.address[0], conn.address[1]) return status = { "recv_len": 0, "send_len": 0, "last_time": time.time(), "check_recv_len": 0, "check_send_len": 0, "speed_limiter": speed_limiter } sevent.current().call_async(tcp_forward, conns, conn, forward_address, subnet, status) conns[id(conn)] = (conn, status)
async def loop_global_speed(self): try: current_timestamp = time.time() await sevent.current().sleep(0.1) while self.buffers: try: avg_speed = int(self.global_speed / len(self.buffers)) max_speed, over_speed = min(avg_speed, self.speed), 0 speed_buffers = [] for _, (data, callback) in self.buffers.items(): dl = len(data) if max_speed > dl: speed_buffers.append((data, dl, dl, callback)) over_speed += avg_speed - dl else: speed_buffers.append( (data, dl, max_speed, callback)) over_speed += avg_speed - max_speed for data, dl, speed, callback in speed_buffers: if over_speed > 0 and dl > speed and speed < self.speed: can_speed = min( min(dl, self.speed) - speed, over_speed) speed += can_speed over_speed -= can_speed sevent.current().add_async(callback, data, speed) self.current_speed = over_speed finally: now = time.time() sleep_time = 0.2 - (now - current_timestamp) current_timestamp = now await sevent.current().sleep(sleep_time) finally: self.current_speed = self.global_speed self.is_running = False
async def tcp_forward_servers(servers, timeout, speed, global_speed): conns, speed_limiter = {}, (SpeedLimiter(speed, global_speed) if speed or global_speed else None) for server, forward_hosts in servers: sevent.current().call_async(tcp_forward_server, conns, server, forward_hosts, speed_limiter) await check_timeout(conns, timeout)
def run_check(): while True: try: now = time.time() for conn in tuple(conn_status["remote_conn"]): if not hasattr(conn, "_authed_time"): if now - conn._connected_time >= 30: sevent.current().add_async_safe(conn.close) elif now - conn._authed_time >= 180: sevent.current().add_async_safe(conn.close) for conn in tuple(conn_status["local_conn"]): if now - conn._connected_time >= 180: sevent.current().add_async_safe(conn.close) for conn_id, (conn, pconn, status) in list(conns.items()): if status['check_recv_len'] != status['recv_len'] or status[ 'check_send_len'] != status['send_len']: status["check_recv_len"] = status["recv_len"] status["check_send_len"] = status["send_len"] status['last_time'] = now continue if now - status['last_time'] >= timeout: sevent.current().add_async_safe(conn.close) sevent.current().add_async(pconn.close) conns.pop(conn_id, None) finally: time.sleep(min(float(timeout) / 2.0, 30))
async def loop(self): try: while self.queues: now = time.time() timeout_time, data, data_len, callback = self.queues.popleft() if timeout_time > now: await sevent.current().sleep(timeout_time - now) sevent.current().add_async(callback, data, data_len) finally: self.is_running = False
async def server_run_server(server, forward_address, key, proxy_type, conns, status, handle): while True: try: conn = await server.accept() sevent.current().call_async(handle, conn, forward_address, key, proxy_type, conns, status) except sevent.errors.SocketClosed as e: sevent.current().call_async(sevent.current().stop) raise e
def main(argv): parser = argparse.ArgumentParser(description='iptables redirect forward to http or socks5 uplink proxy') parser.add_argument('-b', dest='bind', default="0.0.0.0", help='local bind host (default: 0.0.0.0)') parser.add_argument('-p', dest='port', default=8088, type=int, help='local bind port (default: 8088)') parser.add_argument('-t', dest='timeout', default=7200, type=int, help='no read/write timeout (default: 7200)') parser.add_argument('-T', dest='proxy_type', default="http", choices=("http", "socks5"), help='proxy type (default: http)') parser.add_argument('-P', dest='proxy_host', default="127.0.0.1:8088", help='proxy host, accept format [proxy_host:proxy_port] (default: 127.0.0.1:8088)') args = parser.parse_args(args=argv) config_signal() server = create_server((args.bind, args.port)) logging.info("listen server at %s:%d", args.bind, args.port) sevent.current().call_async(tcp_accept, server, args)
async def tcp_accept(server, timeout): conns = {} sevent.current().call_async(check_timeout, conns, timeout) while True: conn = await server.accept() status = { "recv_len": 0, "send_len": 0, "last_time": time.time(), "check_recv_len": 0, "check_send_len": 0 } sevent.current().call_async(tcp_proxy, conns, conn, status) conns[id(conn)] = (conn, status)
def __(data): data_len = max(len(data) - status[key], 0) if delayer.delay: delayer.queues.append( (time.time() + delayer.delay, data, data_len, delay_write)) else: delayer.queues.append( (time.time() + random.randint(*delayer.rdelays) / 1000000.0, data, data_len, delay_write)) status[key] += data_len if delayer.is_running: return delayer.is_running = True sevent.current().call_async(delayer.loop)
async def loop_speed(self): try: current_timestamp = time.time() await sevent.current().sleep(0.1) while self.buffers: try: for _, (data, callback) in list(self.buffers.items()): sevent.current().add_async(callback, data, self.speed) finally: now = time.time() sleep_time = 0.2 - (now - current_timestamp) current_timestamp = now await sevent.current().sleep(sleep_time) finally: self.is_running = False
def run_check(): while True: try: now = time.time() for conn_id, (conn, status) in list(conns.items()): if status['check_recv_len'] != status['recv_len'] or status['check_send_len'] != status['send_len']: status["check_recv_len"] = status["recv_len"] status["check_send_len"] = status["send_len"] status['last_time'] = now continue if now - status['last_time'] >= timeout: sevent.current().add_async_safe(conn.close) conns.pop(conn_id, None) finally: time.sleep(min(float(timeout) / 2.0, 30))
async def client_run_server(server, remote_address, key, proxy_type, conns): while True: try: conn = await server.accept() status = { "recv_len": 0, "send_len": 0, "last_time": time.time(), "check_recv_len": 0, "check_send_len": 0 } sevent.current().call_async(client_handle_local_connect, conn, remote_address, key, proxy_type, conns, status) conns[id(conn)] = (conn, conn, status) except sevent.errors.SocketClosed as e: sevent.current().call_async(sevent.current().stop) raise e
async def tcp_accept(server, args): proxy_info = args.proxy_host.split(":") if len(proxy_info) == 1: if not proxy_info[0].isdigit(): proxy_host, proxy_port = proxy_info[0], 8088 else: proxy_host, proxy_port = "127.0.0.1", int(proxy_info[0]) else: proxy_host, proxy_port = proxy_info[0], int(proxy_info[1]) logging.info("use %s proxy %s:%d", args.proxy_type, proxy_host, proxy_port) conns = {} sevent.current().call_async(check_timeout, conns, args.timeout) while True: conn = await server.accept() status = {"recv_len": 0, "send_len": 0, "last_time": time.time(), "check_recv_len": 0, "check_send_len": 0} sevent.current().call_async(tcp_proxy, conns, conn, args.proxy_type, proxy_host, proxy_port, status) conns[id(conn)] = (conn, status)
def main(argv): parser = argparse.ArgumentParser( description='simple http and socks5 proxy server') parser.add_argument('-b', dest='bind', default="0.0.0.0", help='local bind host (default: 0.0.0.0)') parser.add_argument('-p', dest='port', default=8088, type=int, help='local bind port (default: 8088)') parser.add_argument('-t', dest='timeout', default=7200, type=int, help='no read/write timeout (default: 7200)') args = parser.parse_args(args=argv) config_signal() logging.info("listen server at %s:%d", args.bind, args.port) server = create_server((args.bind, args.port)) sevent.current().call_async(tcp_accept, server, args.timeout)
async def client_run_connect(remote_address, forward_address, key, conns, status): while True: start_time = time.time() try: conn = create_socket(remote_address) await conn.connectof(remote_address) sign_key = gen_sign_key(key) await conn.send(struct.pack("!BB", 1, len(sign_key)) + sign_key) connect_type = (await conn.recv(1)).read(1) if connect_type == b'\x01': current_forward_address = await read_forward_address(conn) else: current_forward_address = forward_address forward_status = { "recv_len": 0, "send_len": 0, "last_time": time.time(), "check_recv_len": 0, "check_send_len": 0 } sevent.current().call_async(tcp_forward, conn, current_forward_address, conns, forward_status) except sevent.errors.SocketClosed as e: logging.info("connect error %s:%d %s", remote_address[0], remote_address[1], e) if time.time() - start_time < 5: await sevent.sleep(5) except (sevent.errors.ResolveError, ConnectionRefusedError) as e: logging.info("connect error %s:%d %s", remote_address[0], remote_address[1], e) await sevent.sleep(5) except Exception as e: sevent.current().call_async(sevent.current().stop) raise e
async def tcp_accept(server, args): proxy_info = args.proxy_host.split(":") if len(proxy_info) == 1: if not proxy_info[0].isdigit(): proxy_host, proxy_port = proxy_info[0], 8088 else: proxy_host, proxy_port = "127.0.0.1", int(proxy_info[0]) else: proxy_host, proxy_port = proxy_info[0], int(proxy_info[1]) noproxy_hosts = [] for noproxy_host in args.noproxy_hosts.split(","): if "*" in noproxy_host and "*" != noproxy_host: noproxy_hosts.append(re.compile(noproxy_host.replace(".", "\.").replace("*", ".+?"))) else: noproxy_hosts.append(noproxy_host) logging.info("use %s proxy %s:%d", args.proxy_type, proxy_host, proxy_port) conns = {} sevent.current().call_async(check_timeout, conns, args.timeout) while True: conn = await server.accept() status = {"recv_len": 0, "send_len": 0, "last_time": time.time(), "check_recv_len": 0, "check_send_len": 0} sevent.current().call_async(tcp_proxy, conns, conn, args.proxy_type, proxy_host, proxy_port, noproxy_hosts, status) conns[id(conn)] = (conn, status)
def speed_write(data, speed): dl = len(data) if dl > speed: if speed <= 0: if speed_limiter.current_speed <= 0: return speed = min(speed_limiter.current_speed, speed_limiter.speed) if dl > speed: status[key] += buffer.fetch(data, speed) speed_limiter.current_speed -= speed else: status[key] += dl speed_limiter.current_speed -= dl else: status[key] += buffer.fetch(data, speed) try: return origin_write(buffer) except sevent.tcp.SocketClosed: speed_limiter.buffers.pop(conn_id, None) if status[end_key]: status[end_key] = False sevent.current().add_async(origin_end) return False if not data: speed_limiter.buffers.pop(conn_id, None) if status[end_key]: status[end_key] = False sevent.current().add_async(origin_end) return True status[key] += dl try: return origin_write(data) except sevent.tcp.SocketClosed: return False
async def server_handle_remote_connect(conn, forward_address, key, proxy_type, conns, status): setattr(conn, "_connected_time", time.time()) def on_close(conn): if conn not in status["remote_conn"]: return status["remote_conn"].remove(conn) logging.info("remote conn waited close %s:%d", conn.address[0], conn.address[1]) status["remote_conn"].append(conn) conn.on_close(on_close) try: connect_type, sign_key_len = struct.unpack("!BB", (await conn.recv(2)).read(2)) sign_key = (await conn.recv(sign_key_len) ).read(sign_key_len) if sign_key_len > 0 else b'' except sevent.errors.SocketClosed: return if not check_sign_key(key, sign_key): await conn.closeof() logging.info("remote conn auth fail %s:%d %s", conn.address[0], conn.address[1], sign_key) return if connect_type == 1: setattr(conn, "_authed_time", time.time()) if status["local_conn"]: forward_status = { "recv_len": 0, "send_len": 0, "last_time": time.time(), "check_recv_len": 0, "check_send_len": 0 } local_conn = status["local_conn"].pop(0) sevent.go(reverse_port_forward, conn, local_conn, forward_status, local_conn._connected_forward_address) conns[id(conn)] = (conn, local_conn, forward_status) if conn not in status["remote_conn"]: return status["remote_conn"].remove(conn) return logging.info("remote conn waiting %s:%d", conn.address[0], conn.address[1]) return if connect_type == 2 or connect_type == 3: try: if connect_type == 3: forward_address = await read_forward_address(conn) await conn.send(b'\x00') forward_status = { "recv_len": 0, "send_len": 0, "last_time": time.time(), "check_recv_len": 0, "check_send_len": 0 } sevent.current().call_async(tcp_forward, conn, forward_address, conns, forward_status) except sevent.errors.SocketClosed: pass except Exception as e: logging.info("tcp forward error %s:%d -> %s:%d %s\r%s", conn.address[0], conn.address[1], forward_address[0], forward_address[1], e, traceback.format_exc()) return await conn.closeof() logging.info("remote conn unsupport connect type %s:%d %s", conn.address[0], conn.address[1], key)
def main(argv): parser = argparse.ArgumentParser(description='tcp reverse port forward') parser.add_argument('-c', dest='is_client_mode', nargs='?', const=True, default=False, type=bool, help='is client mode (defualt: False)') parser.add_argument('-k', dest='key', default='', type=str, help='auth key (defualt: "")') parser.add_argument( '-b', dest='bind_host', default="0.0.0.0", help='server and client mode local bind host (default: 0.0.0.0)') parser.add_argument( '-p', dest='bind_port', default=0, type=int, help='server and client mode local bind port (default: 8089)') parser.add_argument( '-r', dest='listen_host', default="0.0.0.0", help='server mode reverse server listen host (default: 0.0.0.0)') parser.add_argument( '-l', dest='listen_port', default=8088, type=int, help='server mode reverse server listen port (default: 8088)') parser.add_argument( '-H', dest='connect_host', default="127.0.0.1", help= 'client mode reverse client connect server host (default: 127.0.0.1)') parser.add_argument( '-P', dest='connect_port', default=8088, type=int, help='client mode reverse client connect server port (default: 8088)') parser.add_argument( '-f', dest='forward_host', default="", help= 'server and client mode forward host , accept format [remote_host:remote_port] (default: )' ) parser.add_argument( '-T', dest='proxy_type', default="", choices=("raw", "http", "socks5", "redirect"), help='server and client mode local listen proxy type (default: raw)') parser.add_argument('-t', dest='timeout', default=7200, type=int, help='no read/write timeout (default: 7200)') args = parser.parse_args(args=argv) config_signal() if not args.forward_host: forward_address = None else: forward_info = args.forward_host.split(":") if len(forward_info) == 1: if not forward_info[0].isdigit(): forward_address = (forward_info[0], 8088) else: forward_address = ("127.0.0.1", int(forward_info[0])) else: forward_address = (forward_info[0], int(forward_info[1])) if not args.is_client_mode: remote_server = create_server((args.listen_host, args.listen_port)) local_server = create_server((args.bind_host, args.bind_port or 8089)) logging.info("listen %s %d -> %s:%d", args.bind_host, args.bind_port or 8089, args.listen_host, args.listen_port) sevent.instance().call_async( server_run_server, remote_server, forward_address if forward_address else ("127.0.0.1", 80), sevent.utils.ensure_bytes(args.key), args.proxy_type, conns, status, server_handle_remote_connect) sevent.instance().call_async(server_run_server, local_server, forward_address, sevent.utils.ensure_bytes(args.key), args.proxy_type, conns, status, server_handle_local_connect) else: if args.bind_port: local_server = create_server((args.bind_host, args.bind_port)) logging.info("listen %s %d", args.bind_host, args.bind_port) sevent.instance().call_async( client_run_server, local_server, (args.connect_host, args.connect_port), sevent.utils.ensure_bytes(args.key), args.proxy_type, conns) logging.info("connect %s:%d -> %s", args.connect_host, args.connect_port, forward_address) sevent.instance().call_async( client_run_connect, (args.connect_host, args.connect_port), forward_address if forward_address else ("127.0.0.1", 80), sevent.utils.ensure_bytes(args.key), conns, status) sevent.current().call_async(check_timeout, conns, status, args.timeout)
def main(argv): global warp_write parser = argparse.ArgumentParser(description="tcp port forward") parser.add_argument( '-L', dest='forwards', default=[], action="append", type=str, help= 'forward host, accept format [[local_bind:]local_port:remote_host:remote_port]|[subnet], support muiti forward args (example: 0.0.0.0:80:127.0.0.1:8088 or 80:192.168.0.2:8088|192.168.0.0/24)' ) parser.add_argument('-t', dest='timeout', default=7200, type=int, help='no read/write timeout (default: 7200)') parser.add_argument('-s', dest='speed', default=0, type=lambda v: int(float(v[:-1]) * BYTES_MAP[v.upper()[-1]]) \ if v and v.upper()[-1] in BYTES_MAP else int(float(v)), help='per connection speed limit byte, example: 1024 or 1M (default: 0 is unlimit), available units : B K M G T') parser.add_argument('-S', dest='global_speed', default=0, type=lambda v: int(float(v[:-1]) * BYTES_MAP[v.upper()[-1]]) \ if v and v.upper()[-1] in BYTES_MAP else int(float(v)), help='global speed limit byte, example: 1024 or 1M (default: 0 is unlimit), available units : B K M G T') parser.add_argument('-d', dest='delay', default=0, type=lambda v: (float(v.split("-")[0]), float(v.split("-")[-1])) \ if v and isinstance(v, str) and "-" in v else float(v), help='delay millisecond (default: 0 is not delay, example -d100 or -d100-200), the - between two numbers will be random delay') parser.add_argument( '-M', dest='mirror_host', default="", type=str, help= 'mirror host, accept format [[up_host:]up_port:[down_host]:down_port] (example: 0.0.0.0:80:127.0.0.1:8088 or :127.0.0.1:8088 or 127.0.0.1:8088: or 8088:8088)' ) parser.add_argument( '-F', dest='mirror_header', default="", type=str, help= 'mirror header, accept variables [from_host|from_port|to_host|to_port|conn_id] (example: "{conn_id}-{from_host}:{from_port}->{to_host}:{to_port}\\r\\n")' ) args = parser.parse_args(args=argv) config_signal() if not args.forwards: exit(0) forwards = {} for forward in args.forwards: hosts, subnet = host_parse(forward) if hosts[0] not in forwards: forwards[hosts[0]] = [] forwards[hosts[0]].append((hosts[1], subnet)) if not forwards: exit(0) if args.speed or args.global_speed: warp_write = warp_speed_limit_write if args.delay: warp_write = warp_delay_write( Delayer( 0 if isinstance(args.delay, tuple) else float(args.delay) / 1000.0, (int(args.delay[0] * 1000), int(args.delay[1] * 1000)) if isinstance(args.delay, tuple) else None), warp_write) if args.mirror_host: warp_write = warp_mirror_write(args.mirror_host, args.mirror_header, warp_write) forward_servers = [] for bind_address, forward_hosts in forwards.items(): server = create_server(bind_address) forward_servers.append( (server, sorted(forward_hosts, key=lambda x: x[1][1][0] * 0xffffffffffffffff + x[1][1][1] if isinstance(x[1][1], tuple) else x[1][1], reverse=True))) logging.info("port forward listen %s:%s", bind_address[0], bind_address[1]) sevent.current().call_async(tcp_forward_servers, forward_servers, args.timeout, int(args.speed / 10), int(args.global_speed / 10))
def _(data): if conn_id in speed_limiter.buffers: current_speed = status[current_speed_key] if current_speed <= 0: return if speed_limiter.global_speed: if speed_limiter.current_speed <= 0: return dl = len(data) speed = min(speed_limiter.current_speed, current_speed) if dl > speed: status[key] += buffer.fetch(data, speed) speed_limiter.current_speed -= speed status[current_speed_key] -= speed return origin_write(buffer) status[key] += dl speed_limiter.current_speed -= dl status[current_speed_key] -= dl return origin_write(data) dl = len(data) if dl > current_speed: status[key] += buffer.fetch(data, current_speed) status[current_speed_key] = 0 return origin_write(buffer) status[key] += dl status[current_speed_key] -= dl return origin_write(data) speed_limiter.buffers[conn_id] = (data, speed_write) if not speed_limiter.is_running: speed_limiter.is_running = True sevent.current().call_async(speed_limiter.loop) dl = len(data) if speed_limiter.global_speed: if speed_limiter.current_speed <= 0: status[current_speed_key] = 0 return speed = min(speed_limiter.current_speed, speed_limiter.speed) if dl > speed: status[key] += buffer.fetch(data, speed) speed_limiter.current_speed -= speed status[current_speed_key] = 0 return origin_write(buffer) status[key] += dl speed_limiter.current_speed -= dl status[current_speed_key] = speed_limiter.speed - dl return origin_write(data) if dl > speed_limiter.speed: status[key] += buffer.fetch(data, speed_limiter.speed) status[current_speed_key] = 0 return origin_write(buffer) status[key] += dl status[current_speed_key] = speed_limiter.speed - dl return origin_write(data)