/* fatvpn client by eeeee * Public Domain */ #include "config.h" #include "common.h" #include #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_storage 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
\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: memcpy(&srv, srvi->ai_addr, sizeof(struct sockaddr_in)); break; case AF_INET6: memcpy(&srv, srvi->ai_addr, sizeof(struct sockaddr_in6)); 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(srv.ss_family, 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, &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, &srv); } /* recv packet from server */ if (0 == FD_ISSET(skt, &rset)) goto inf_loop; res = recv_pkt(skt, pkt, MAX_PKT_SZ, &sender); if (res < 14) goto inf_loop; /* ignore too small packets */ /* compare port and ip */ switch (srvi->ai_family){ case AF_INET: if (((struct sockaddr_in*)&srv)->sin_port != ((struct sockaddr_in*)&sender)->sin_port) goto inf_loop; tmp = memcmp(&((struct sockaddr_in*)&srv)->sin_addr.s_addr, &((struct sockaddr_in*)&sender)->sin_addr.s_addr, 4); break; case AF_INET6: if (((struct sockaddr_in6*)&srv)->sin6_port != ((struct sockaddr_in6*)&sender)->sin6_port) goto inf_loop; tmp = memcmp(((struct sockaddr_in6*)&srv)->sin6_addr.s6_addr, ((struct sockaddr_in6*)&sender)->sin6_addr.s6_addr, 16); break; } 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; }