diff options
Diffstat (limited to 'fatvpn.c')
| -rw-r--r-- | fatvpn.c | 215 |
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; +} + + |