From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on gnuweeb.org X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=ALL_TRUSTED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,NO_DNS_FOR_FROM,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.6 Received: from integral2.. (unknown [36.81.65.188]) by gnuweeb.org (Postfix) with ESMTPSA id B090B7E312; Fri, 8 Jul 2022 12:11:05 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gnuweeb.org; s=default; t=1657282267; bh=DYsp1d28HO0F2BvJ3t9wbbu24LzgrdDj0AdF3Nem7rs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WIs1rwiWrHSiAchwBC/Kj/SzwHbTucWCszXjsX8GFmu8lPBTUROuOxWJBYGnMTx3u W7I4NjEoMpvo9N6uMPAHiDYd9Txu3Cjt+tDC9G4piYFYQSu9uf+hPpnlsHFTGMsoRh hHJLp3UQkRKVSF+DhO73krONPFsK0vmnHyXag5xT5Roe5Rw8TXNh7bVXSLQU6BO7Sp FW5f8RebuBEg86ufMqXdr7WvvewkhchgUzpoijdUDggjdrsNIpG6CZEuywPHjbYmld kE602VzS4uUUy0O+GDPOYJOvT/LyF0hhvefPq6yEVyV1ykI/sOUkcZp5WWSLL2vzmW 57iZ8H3U4P2yg== From: Ammar Faizi To: GNU/Weeb Mailing List Cc: Ammar Faizi , Alviro Iskandar Setiawan , Arthur Lapz , Fernanda Ma'rouf , Sprite , Yonle Subject: [PATCH gwhttpd 11/14] gwhttpd: Add SLC support Date: Fri, 8 Jul 2022 19:10:22 +0700 Message-Id: <20220708121025.926162-12-ammarfaizi2@gnuweeb.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220708121025.926162-1-ammarfaizi2@gnuweeb.org> References: <20220708121025.926162-1-ammarfaizi2@gnuweeb.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: SLC (Socket Lost Control) is a Linux software created by me. It has an ability to forward local port to a public port. This commit adds SLC support for gwhttpd so that we can have a built-in port forwarding feature. Signed-off-by: Ammar Faizi --- gwhttpd.cpp | 360 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 357 insertions(+), 3 deletions(-) diff --git a/gwhttpd.cpp b/gwhttpd.cpp index c878c5c..b62ed26 100644 --- a/gwhttpd.cpp +++ b/gwhttpd.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #define noinline __attribute__((__noinline__)) #define __hot __attribute__((__hot__)) @@ -1803,21 +1804,371 @@ static __cold void destroy_state(struct server_state *state) struct cmd_arg { const char *bind_addr; const char *bind_port; - const char *slc_circuit_addr; - const char *slc_circuit_port; + const char *slc_addr; + const char *slc_port; }; +static std::atomic g_slc_stop; +#define PKT_DATA_BUFFER 1024 + +struct slc_packet { + uint8_t type; + uint8_t pad; + uint16_t len; + uint8_t data[PKT_DATA_BUFFER]; +}; + +struct client_private_data { + struct slc_packet pkt; + const char *server_addr; + const char *target_addr; + uint16_t target_port; + uint16_t server_port; +}; + +enum { + PKT_TYPE_SERVER_GET_A_REAL_CLIENT, + PKT_TYPE_CLIENT_INIT_CIRCUIT, + PKT_TYPE_CLIENT_START_PRIVATE_SOCK +}; + +static int create_tcp_sock(void) +{ + int fd; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + fd = errno; + perror("socket"); + return -fd; + } + return fd; +} + +static int start_circuit(int fd_circuit) +{ + struct slc_packet pkt; + ssize_t ret; + int err; + + memset(&pkt, 0, sizeof(pkt)); + pkt.type = PKT_TYPE_CLIENT_INIT_CIRCUIT; + ret = send(fd_circuit, &pkt, sizeof(pkt), 0); + if (unlikely(ret < 0)) { + err = errno; + perror("send"); + return -err; + } + return 0; +} + +static int setup_socket(int fd) +{ + int ret, y; + size_t len = sizeof(y); + + /* + * Ignore any error from these calls. They are not mandatory. + */ + y = 1; + ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&y, len); + if (unlikely(ret < 0)) { + perror("setsockopt(IPPROTO_TCP, TCP_NODELAY)"); + puts("Failed to set TCP nodelay, but this is fine."); + } + + y = 1024 * 1024 * 100; + ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, (void *)&y, len); + y = 1024 * 1024 * 100; + ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, (void *)&y, len); + + return 0; +} + +static int connect_tcp_sock(int fd, const char *addr, uint16_t port) +{ + struct sockaddr_in dst_addr; + int err; + + memset(&dst_addr, 0, sizeof(dst_addr)); + dst_addr.sin_family = AF_INET; + dst_addr.sin_port = htons(port); + dst_addr.sin_addr.s_addr = inet_addr(addr); + + printf("SLC Connecting to %s:%u...\n", addr, port); + err = connect(fd, (struct sockaddr *)&dst_addr, sizeof(dst_addr)); + if (err < 0) { + err = errno; + perror("connect"); + return -err; + } + err = setup_socket(fd); + printf("SLC Connected!\n"); + return 0; +} + +static int recv_and_send(int fd_in, int fd_out, int *pipes, size_t len) +{ + unsigned int fl = SPLICE_F_MOVE | SPLICE_F_NONBLOCK; + ssize_t read_ret; + ssize_t write_ret; + int ret; + + read_ret = splice(fd_in, NULL, pipes[1], NULL, len, fl); + if (unlikely(read_ret <= 0)) { + if (read_ret == 0) { + puts("fd_in is down"); + return -ENETDOWN; + } + ret = errno; + perror("splice fd_in"); + return -ret; + } + +do_write: + write_ret = splice(pipes[0], NULL, fd_out, NULL, read_ret, fl); + if (unlikely(write_ret <= 0)) { + if (write_ret == 0) { + puts("fd_out is down"); + return -ENETDOWN; + } + ret = errno; + perror("splice fd_out"); + return -ret; + } + + read_ret -= write_ret; + if (unlikely(read_ret > 0)) + goto do_write; + + return 0; +} + +static int socket_bridge(int fd1, int fd2) +{ + static const size_t len = 1024 * 1024; + struct pollfd fds[2]; + int pipes[2] = {-1, -1}; + int ret; + + if (pipe(pipes)) { + ret = errno; + perror("pipe"); + return -ret; + } + + fds[0].fd = fd1; + fds[0].events = POLLIN | POLLPRI; + fds[1].fd = fd2; + fds[1].events = POLLIN | POLLPRI; + +do_poll: + if (atomic_load(&g_slc_stop)) { + ret = 0; + goto out; + } + + ret = poll(fds, 2, 1000); + if (unlikely(ret < 0)) { + ret = errno; + perror("poll"); + goto out; + } + + if (ret == 0) + goto do_poll; + + if (fds[0].revents & POLLIN) { + ret = recv_and_send(fd1, fd2, pipes, len); + if (unlikely(ret < 0)) + goto out; + } + + if (fds[1].revents & POLLIN) { + ret = recv_and_send(fd2, fd1, pipes, len); + if (unlikely(ret < 0)) + goto out; + } + goto do_poll; + +out: + if (pipes[0] != -1) + close(pipes[0]); + if (pipes[1] != -1) + close(pipes[1]); + return ret; +} + +static void *start_private_conn(void *pp) +{ + struct client_private_data *p = (struct client_private_data *)pp; + int fd_pa = -1, fd_pb = -1; + int err = 0; + ssize_t ret; + + fd_pa = create_tcp_sock(); + if (unlikely(fd_pa < 0)) { + err = fd_pa; + goto out_free; + } + + fd_pb = create_tcp_sock(); + if (unlikely(fd_pb < 0)) { + err = fd_pb; + goto out_free; + } + + err = connect_tcp_sock(fd_pa, p->server_addr, p->server_port); + if (unlikely(err)) + goto out_free; + + err = connect_tcp_sock(fd_pb, p->target_addr, p->target_port); + if (unlikely(err)) + goto out_free; + + p->pkt.type = PKT_TYPE_CLIENT_START_PRIVATE_SOCK; + ret = send(fd_pa, &p->pkt, sizeof(p->pkt), 0); + if (unlikely(ret < 0)) { + err = errno; + perror("send"); + goto out_free; + } + +out_free: + delete p; + if (err) + goto out; + + socket_bridge(fd_pa, fd_pb); +out: + if (fd_pa != -1) + close(fd_pa); + if (fd_pb != -1) + close(fd_pb); + return NULL; +} + +static int handle_private_conn(int fd_circuit, const char *target_addr, + uint16_t target_port, const char *server_addr, + uint16_t server_port) +{ + struct client_private_data *pp; + struct slc_packet pkt; + pthread_t thread; + ssize_t ret; + int err; + +do_recv: + ret = recv(fd_circuit, &pkt, sizeof(pkt), 0); + if (unlikely(ret <= 0)) { + if (ret == 0) { + puts("Server has been disconnected!"); + return -ENETDOWN; + } + err = errno; + perror("recv"); + return -err; + } + + pp = new struct client_private_data; + if (unlikely(!pp)) + return -ENOMEM; + + pp->pkt = pkt; + pp->server_addr = server_addr; + pp->server_port = server_port; + pp->target_addr = target_addr; + pp->target_port = target_port; + err = pthread_create(&thread, NULL, start_private_conn, pp); + if (unlikely(ret < 0)) { + errno = ret; + perror("pthread_create"); + delete pp; + return -ret; + } + pthread_detach(thread); + + if (!atomic_load(&g_slc_stop)) + goto do_recv; + + return 0; +} + +static int _run_slc_client(const char *target_addr, uint16_t target_port, + const char *server_addr, uint16_t server_port) +{ + int fd_circuit = -1; + int err; + + fd_circuit = create_tcp_sock(); + if (unlikely(fd_circuit < 0)) + return -fd_circuit; + + err = connect_tcp_sock(fd_circuit, server_addr, server_port); + if (unlikely(err)) + goto out; + + err = start_circuit(fd_circuit); + if (unlikely(err)) + goto out; + + err = handle_private_conn(fd_circuit, target_addr, target_port, + server_addr, server_port); +out: + close(fd_circuit); + return (err < 0) ? -err : err; +} + +static int run_slc_client(const char *target_addr, uint16_t target_port, + const char *server_addr, uint16_t server_port) +{ + int ret = 0; + + atomic_store(&g_slc_stop, false); + +repeat: + ret = _run_slc_client(target_addr, target_port, server_addr, + server_port); + if (unlikely(ret)) { + errno = ret; + perror("_run_slc_client()"); + } + + if (atomic_load(&g_slc_stop)) + return ret; + + puts("Sleeing for 3 seconds..."); + sleep(3); + goto repeat; +} + static int _main(struct cmd_arg *arg) { struct server_state *state = NULL; + uint16_t bport = (uint16_t)atoi(arg->bind_port); + pid_t slc_pid = -1; int ret; + if (arg->slc_addr && arg->slc_port) { + slc_pid = fork(); + if (slc_pid < 0) { + ret = errno; + perror("fork()"); + return -ret; + } + + if (!slc_pid) + return run_slc_client(arg->bind_addr, bport, + arg->slc_addr, + (uint16_t)atoi(arg->slc_port)); + } + ret = init_state(&state); if (unlikely(ret)) return ret; state->bind_addr = arg->bind_addr; - state->bind_port = (uint16_t)atoi(arg->bind_port); + state->bind_port = bport; ret = init_socket(state); if (unlikely(ret)) @@ -1825,6 +2176,9 @@ static int _main(struct cmd_arg *arg) ret = run_workers(state); out: destroy_state(state); + if (slc_pid > 0) + kill(slc_pid, SIGTERM); + return ret; } -- Ammar Faizi