public inbox for [email protected]
 help / color / mirror / Atom feed
From: Ammar Faizi <[email protected]>
To: GNU/Weeb Mailing List <[email protected]>
Cc: Ammar Faizi <[email protected]>,
	Alviro Iskandar Setiawan <[email protected]>,
	Arthur Lapz <[email protected]>,
	Fernanda Ma'rouf <[email protected]>,
	Sprite <[email protected]>, Yonle <[email protected]>
Subject: [PATCH gwhttpd 11/14] gwhttpd: Add SLC support
Date: Fri,  8 Jul 2022 19:10:22 +0700	[thread overview]
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>

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 <[email protected]>
---
 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 <sys/stat.h>
 #include <sys/types.h>
 #include <getopt.h>
+#include <netinet/tcp.h>
 
 #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<bool> 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


  parent reply	other threads:[~2022-07-08 12:11 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-07-08 12:10 [PATCH gwhttpd 00/14] gwhttpd updates Ammar Faizi
2022-07-08 12:10 ` [PATCH gwhttpd 01/14] gwhttpd: Do an early return when `parse_http_header()` fails Ammar Faizi
2022-07-08 12:10 ` [PATCH gwhttpd 02/14] gwhttpd: Don't print any error when mlock fails Ammar Faizi
2022-07-08 12:10 ` [PATCH gwhttpd 03/14] gwhttpd: Replace `send_error_and_close()` with `send_http_error()` Ammar Faizi
2022-07-08 12:10 ` [PATCH gwhttpd 04/14] gwhttpd: Add log in the interrupt handler Ammar Faizi
2022-07-08 12:10 ` [PATCH gwhttpd 05/14] gwhttpd: Refactor HTTP header parser Ammar Faizi
2022-07-08 12:10 ` [PATCH gwhttpd 06/14] gwhttpd: Avoid endless busy spinning on `send()` Ammar Faizi
2022-07-08 12:10 ` [PATCH gwhttpd 07/14] Makefile: Add "make clean" command Ammar Faizi
2022-07-08 12:10 ` [PATCH gwhttpd 08/14] gwhttpd: Skip interrupt error from `epoll_wait()` Ammar Faizi
2022-07-08 12:10 ` [PATCH gwhttpd 09/14] gwhttpd: Add directory listing and download file support Ammar Faizi
2022-07-08 12:10 ` [PATCH gwhttpd 10/14] gwhttpd: Add command line options Ammar Faizi
2022-07-08 12:10 ` Ammar Faizi [this message]
2022-07-08 12:10 ` [PATCH gwhttpd 12/14] gwhttpd: slc: Shut the SLC log up Ammar Faizi
2022-07-08 12:10 ` [PATCH gwhttpd 13/14] gwhttpd: Fix 403 HTTP error when accessing an empty file Ammar Faizi
2022-07-08 12:10 ` [PATCH gwhttpd 14/14] gwhttpd: Add connecting log for SLC Ammar Faizi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    [email protected] \
    [email protected] \
    [email protected] \
    [email protected] \
    [email protected] \
    [email protected] \
    [email protected] \
    [email protected] \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox