summary refs log tree commit diff
path: root/fatvpn.c
diff options
context:
space:
mode:
authorNakidai <nakidai@disroot.org>2025-07-31 17:12:27 +0300
committerNakidai <nakidai@disroot.org>2025-07-31 17:12:27 +0300
commitb4709429ed88563982412a5a027b92143c37e268 (patch)
treecd56dfeac7cafcfcb69925b12376d79b8776d3fd /fatvpn.c
downloadfatvpn-1.0.0.tar.gz
fatvpn-1.0.0.zip
Add files v1.0.0
Diffstat (limited to 'fatvpn.c')
-rw-r--r--fatvpn.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/fatvpn.c b/fatvpn.c
new file mode 100644
index 0000000..3a03067
--- /dev/null
+++ b/fatvpn.c
@@ -0,0 +1,215 @@
+/* fatvpn client by eeeee
+ * Public Domain */
+
+#include "config.h"
+#include "common.h"
+
+#include <netdb.h>
+
+#define LOGFILE "/tmp/fatvpn.log"
+#define TIMEOUT_P 10 /* ping timeout in seconds */
+
+int main(int argc, char *argv[]){
+	u8  *pkt;
+	s32 tmp, res, tap, fdmax, skt;
+	u32 lptime;
+	struct sockaddr_in  *s4;
+	struct sockaddr_in6 srv, sender;
+	struct addrinfo     *srvi;
+	struct timespec     t;
+	struct timeval      slp;
+	struct termios      attr;
+	struct ifreq        ifr;
+	fd_set rset;
+
+	/* arguments */
+	if (argc != 4){
+		fprintf(stderr, "Using:\n\t"
+			"%s <tap name> <address> <port>\n\n"
+		        "Example:\n\t"
+			"%s fvpn0 169.254.123.123 2222\n\t"
+			"%s tap0 vpn.example.org 5555\n\n",
+		        argv[0], argv[0], argv[0]);
+		exit(1);
+	}
+
+	if (strlen(argv[1]) + 1 > IFNAMSIZ)
+		fputs("tap name is too long\n", stderr), exit(1);
+
+	tmp = 0;
+	do { /* dns, ip/port str to num */
+		res = getaddrinfo(argv[2], argv[3], NULL, &srvi);
+		tmp++;
+		usleep(10000); /* 10 ms */
+	} while (res == EAI_AGAIN && tmp < 5);
+	if (res){
+		fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(res));
+		exit(1);
+	}
+
+	/* server address */
+	switch (srvi->ai_family){
+	case AF_INET:
+		s4 = (struct sockaddr_in*) srvi->ai_addr;
+		srv.sin6_family = AF_INET6;
+		srv.sin6_port = s4->sin_port;
+		memset(&srv.sin6_addr.s6_addr, 0, 16);
+		srv.sin6_addr.s6_addr[10] = 0xFF;
+		srv.sin6_addr.s6_addr[11] = 0xFF;
+		memcpy(&srv.sin6_addr.s6_addr[12], &s4->sin_addr.s_addr, 4);
+		break;
+	case AF_INET6:
+		memcpy(&srv, srvi->ai_addr, sizeof(srv));
+		break;
+	default:
+		fprintf(stderr, "unsupported addrfamily: %s\n", argv[2]);
+		exit(1);
+	}
+
+	if (password[0] == 0){
+		/* TODO: check std streams redirected to file */
+		/* get term attrs */
+		res = tcgetattr(STDOUT_FILENO, &attr);
+		ERRDIE(res, "tcgetattr");
+
+		/* echo off */
+		attr.c_lflag &= ~ECHO;
+		res = tcsetattr(STDOUT_FILENO, TCSANOW, &attr);
+		ERRDIE(res, "tcsetattr");
+
+		/* get password */
+		fputs("Password: ", stdout), fflush(stdout);
+		pkt = fgets(password, PASSWORD_BUF_SZ, stdin);
+		fputs("\n", stdout);
+		if (pkt == NULL)
+			fputs("can't read password\n", stderr), exit(1);
+
+		/* echo on */
+		attr.c_lflag |= ECHO;
+		res = tcsetattr(STDOUT_FILENO, TCSANOW, &attr);
+		ERRDIE(res, "tcsetattr");
+	}
+
+	/* password without '\r\n' */
+	res = 0;
+	while (password[res] != '\0'
+	    && password[res] != '\r'
+	    && password[res] != '\n') res++;
+
+	/* hash password */
+	sponge256_hash32(key, password, res);
+	memset(password, 0, PASSWORD_BUF_SZ);
+
+	/* feed prng with key and current time */
+	duplex257_prng_feed(prng_state, ((u32*)key)[0]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[1]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[2]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[3]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[4]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[5]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[6]);
+	duplex257_prng_feed(prng_state, ((u32*)key)[7]);
+	clock_gettime(CLOCK_MONOTONIC, &t);
+	duplex257_prng_feed(prng_state, (u32)t.tv_sec);
+	duplex257_prng_feed(prng_state, (u32)t.tv_nsec);
+
+	lptime = (u32)t.tv_sec;
+
+	/* daemonize: redirect std streams */
+	res = open("/dev/null", O_RDONLY); ERRDIE(res, "open");
+	close(STDIN_FILENO), dup(res), close(res);
+	res = open(LOGFILE, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
+	ERRDIE(res, "open");
+	close(STDOUT_FILENO);
+	close(STDERR_FILENO);
+	dup(res), dup(res), close(res);
+
+	/* daemonize: fork 1 */
+	res = fork(); ERRDIE(res, "fork 1");
+	if (res != 0) exit(0);
+
+	/* daemonize: become session leader */
+	setsid();
+
+	/* daemonize: fork 2 */
+	res = fork(); ERRDIE(res, "fork 2");
+	if (res != 0) exit(0); /* kill session leader */
+
+	/* daemonized! */
+
+	/* cur pkt buf */
+	pkt = malloc(MAX_PKT_SZ);
+	if (pkt == NULL) fputs("pkt = malloc() failed\n",stderr), exit(1);
+
+	/* socket, setsockopt */
+	skt = socket(PF_INET6, SOCK_DGRAM, 0); ERRDIE(skt, "socket");
+	res = 1;
+	res = setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, &res, sizeof(res));
+	ERRDIE(res, "SO_REUSEADDR");
+
+	/* tap */
+	tap = open("/dev/net/tun", O_RDWR);
+	ERRDIE(tap, "/dev/net/tun");
+
+	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+	strncpy(ifr.ifr_name, argv[1], IFNAMSIZ);
+	res = ioctl(tap, TUNSETIFF, &ifr);
+	ERRDIE(res, "TUNSETIFF");
+
+	fdmax = (tap > skt) ? tap+1 : skt+1;
+
+inf_loop:
+	/* select */
+	FD_ZERO(&rset);
+	FD_SET(skt, &rset);
+	FD_SET(tap, &rset);
+
+	slp.tv_sec = 0;
+	slp.tv_usec = 50000;
+	select(fdmax, &rset, NULL, NULL, &slp);
+
+	clock_gettime(CLOCK_MONOTONIC, &t);
+	if (TIMEOUT_P <= (u32)t.tv_sec - lptime){
+		/* ping for NAT keepalive */
+		send_pkt(skt, pkt, 14, (struct sockaddr_storage*)&srv);
+		lptime = t.tv_sec;
+	}
+
+	/* read packet from tap */
+	if (FD_ISSET(tap, &rset)){
+		res = read(tap, pkt, MAX_PKT_SZ);
+		ERRDIE(res, "read(tap)");
+
+		/* TODO: check res+N > MAX_PKT_SZ */
+		send_pkt(skt, pkt, res, (struct sockaddr_storage*)&srv);
+	}
+
+	/* recv packet from server */
+	if (0 == FD_ISSET(skt, &rset)) goto inf_loop;
+
+	res = recv_pkt(skt, pkt, MAX_PKT_SZ,
+	                                 (struct sockaddr_storage*)&sender);
+	if (res < 14) goto inf_loop; /* ignore too small packets */
+
+	/* compare port and ip */
+	if (srv.sin6_port != sender.sin6_port) goto inf_loop;
+	tmp = memcmp(srv.sin6_addr.s6_addr, sender.sin6_addr.s6_addr, 16);
+	if (tmp != 0) goto inf_loop;
+
+	/* packet recv time for entropy and last packet time */
+	clock_gettime(CLOCK_MONOTONIC, &t);
+	duplex257_prng_feed(prng_state, t.tv_nsec);
+	lptime = t.tv_sec;
+
+	tmp = write(tap, pkt, res);
+	if (tmp < res)
+		fprintf(stderr, "tmp < res, tmp=%d, res=%d\n", tmp, res),
+		exit(1);
+
+	goto inf_loop;
+
+	return 0;
+}
+
+