DWORD WINAPI tun_reader(LPVOID arg) { struct tun_data *tun = arg; char buf[64*1024]; int len; int res; OVERLAPPED olpd; int sock; sock = open_dns(0, INADDR_ANY); olpd.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); while(TRUE) { olpd.Offset = 0; olpd.OffsetHigh = 0; res = ReadFile(tun->tun, buf, sizeof(buf), (LPDWORD) &len, &olpd); if (!res) { WaitForSingleObject(olpd.hEvent, INFINITE); res = GetOverlappedResult(dev_handle, &olpd, (LPDWORD) &len, FALSE); res = sendto(sock, buf, len, 0, (struct sockaddr*) &(tun->addr), sizeof(struct sockaddr_in)); } } return 0; }
int open_dns_from_host(char *host, int port, int addr_family, int flags) { struct sockaddr_storage addr; int addrlen; addrlen = get_addr(host, port, addr_family, flags, &addr); if (addrlen < 0) return addrlen; return open_dns(&addr, addrlen); }
int open_tun(const char *tun_device) { char adapter[256]; char tapfile[512]; int tunfd; in_addr_t local; memset(adapter, 0, sizeof(adapter)); memset(if_name, 0, sizeof(if_name)); get_device(adapter, sizeof(adapter), tun_device); if (strlen(adapter) == 0 || strlen(if_name) == 0) { if (tun_device) { warnx("No TAP adapters found. Try without -d."); } else { warnx("No TAP adapters found. Version 0801 and 0901 are supported."); } return -1; } fprintf(stderr, "Opening device %s\n", if_name); snprintf(tapfile, sizeof(tapfile), "%s%s.tap", TAP_DEVICE_SPACE, adapter); dev_handle = CreateFile(tapfile, GENERIC_WRITE | GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, NULL); if (dev_handle == INVALID_HANDLE_VALUE) { warnx("Could not open device!"); return -1; } /* Use a UDP connection to forward packets from tun, * so we can still use select() in main code. * A thread does blocking reads on tun device and * sends data as udp to this socket */ local = htonl(0x7f000001); /* 127.0.0.1 */ tunfd = open_dns(55353, local); data.tun = dev_handle; memset(&(data.addr), 0, sizeof(data.addr)); data.addr.sin_family = AF_INET; data.addr.sin_port = htons(55353); data.addr.sin_addr.s_addr = local; CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)tun_reader, &data, 0, NULL); return tunfd; }
int main(int argc, char **argv) { extern char *__progname; char *listen_ip4; char *listen_ip6; char *errormsg; #ifndef WINDOWS32 struct passwd *pw = NULL; #endif int foreground; char *username; char *newroot; char *context; char *device; char *pidfile; int choice; int skipipconfig; char *netsize; int ns_get_externalip; int retval; #ifdef HAVE_SYSTEMD int nb_fds; #endif errormsg = NULL; username = NULL; newroot = NULL; context = NULL; device = NULL; foreground = 0; listen_ip4 = NULL; listen_ip6 = NULL; ns_get_externalip = 0; skipipconfig = 0; pidfile = NULL; srand(time(NULL)); retval = 0; #ifdef WINDOWS32 WSAStartup(req_version, &wsa_data); #endif #if !defined(BSD) && !defined(__GLIBC__) __progname = strrchr(argv[0], '/'); if (__progname == NULL) __progname = argv[0]; else __progname++; #endif // Load default values from preset memcpy(&server, &preset_default, sizeof(struct server_instance)); /* each option has format: char *name, int has_arg, int *flag, int val */ static struct option iodined_args[] = { {"version", no_argument, 0, 'v'}, {"noipcheck", no_argument, 0, 'c'}, {"notun", no_argument, 0, 's'}, {"user", required_argument, 0, 'u'}, {"listen4", required_argument, 0, 'l'}, {"listen6", required_argument, 0, 'L'}, {"nsip", required_argument, 0, 'n'}, {"mtu", required_argument, 0, 'm'}, {"idlequit", required_argument, 0, 'i'}, {"forwardto", required_argument, 0, 'b'}, {"localforward", no_argument, 0, 'A'}, {"remoteforward", no_argument, 0, 'R'}, {"help", no_argument, 0, 'h'}, {"context", required_argument, 0, 'z'}, {"chrootdir", required_argument, 0, 't'}, {"pidfile", required_argument, 0, 'F'}, {NULL, 0, 0, 0} }; static char *iodined_args_short = "46vcsfhDARu:t:d:m:l:L:p:n:b:P:z:F:i:"; server.running = 1; while ((choice = getopt_long(argc, argv, iodined_args_short, iodined_args, NULL)) != -1) { switch(choice) { case '4': server.addrfamily = AF_INET; break; case '6': server.addrfamily = AF_INET6; break; case 'v': version(); break; case 'c': server.check_ip = 0; break; case 's': skipipconfig = 1; break; case 'f': foreground = 1; break; case 'h': help(); break; case 'D': server.debug++; break; case 'u': username = optarg; break; case 't': newroot = optarg; break; case 'd': device = optarg; break; case 'm': server.mtu = atoi(optarg); break; case 'l': listen_ip4 = optarg; break; case 'L': listen_ip6 = optarg; break; case 'p': server.port = atoi(optarg); break; case 'n': if (optarg && strcmp("auto", optarg) == 0) { ns_get_externalip = 1; } else { server.ns_ip = inet_addr(optarg); } break; case 'b': server.bind_enable = 1; server.bind_port = atoi(optarg); break; case 'A': server.allow_forward_local_port = 1; break; case 'R': server.allow_forward_local_port = 1; server.allow_forward_remote = 1; break; case 'F': pidfile = optarg; break; case 'i': server.max_idle_time = atoi(optarg); break; case 'P': strncpy(server.password, optarg, sizeof(server.password)); server.password[sizeof(server.password)-1] = 0; /* XXX: find better way of cleaning up ps(1) */ memset(optarg, 0, strlen(optarg)); break; case 'z': context = optarg; break; default: usage(); break; } } argc -= optind; argv += optind; check_superuser(usage); if (argc != 2) usage(); netsize = strchr(argv[0], '/'); if (netsize) { *netsize = 0; netsize++; server.netmask = atoi(netsize); } server.my_ip = inet_addr(argv[0]); if (server.my_ip == INADDR_NONE) { warnx("Bad IP address to use inside tunnel."); usage(); } server.topdomain = strdup(argv[1]); if(check_topdomain(server.topdomain, &errormsg)) { warnx("Invalid topdomain: %s", errormsg); usage(); /* NOTREACHED */ } if (username != NULL) { #ifndef WINDOWS32 if ((pw = getpwnam(username)) == NULL) { warnx("User %s does not exist!", username); usage(); } #endif } if (server.mtu <= 0) { warnx("Bad MTU given."); usage(); } if(server.port < 1 || server.port > 65535) { warnx("Bad port number given."); usage(); } if (server.port != 53) { fprintf(stderr, "ALERT! Other dns servers expect you to run on port 53.\n"); fprintf(stderr, "You must manually forward port 53 to port %d for things to work.\n", server.port); } if (server.debug) { fprintf(stderr, "Debug level %d enabled, will stay in foreground.\n", server.debug); fprintf(stderr, "Add more -D switches to set higher debug level.\n"); foreground = 1; } if (server.addrfamily == AF_UNSPEC || server.addrfamily == AF_INET) { server.dns4addr_len = get_addr(listen_ip4, server.port, AF_INET, AI_PASSIVE | AI_NUMERICHOST, &server.dns4addr); if (server.dns4addr_len < 0) { warnx("Bad IPv4 address to listen on."); usage(); } } if (server.addrfamily == AF_UNSPEC || server.addrfamily == AF_INET6) { server.dns6addr_len = get_addr(listen_ip6, server.port, AF_INET6, AI_PASSIVE | AI_NUMERICHOST, &server.dns6addr); if (server.dns6addr_len < 0) { warnx("Bad IPv6 address to listen on."); usage(); } } if (server.bind_enable) { in_addr_t dns_ip = ((struct sockaddr_in *) &server.dns4addr)->sin_addr.s_addr; if (server.bind_port < 1 || server.bind_port > 65535) { warnx("Bad DNS server port number given."); usage(); /* NOTREACHED */ } /* Avoid forwarding loops */ if (server.bind_port == server.port && (dns_ip == INADDR_ANY || dns_ip == INADDR_LOOPBACK)) { warnx("Forward port is same as listen port (%d), will create a loop!", server.bind_port); fprintf(stderr, "Use -l to set listen ip to avoid this.\n"); usage(); /* NOTREACHED */ } fprintf(stderr, "Requests for domains outside of %s will be forwarded to port %d\n", server.topdomain, server.bind_port); } if (ns_get_externalip) { struct in_addr extip; int res = get_external_ip(&extip); if (res) { fprintf(stderr, "Failed to get external IP via web service.\n"); exit(3); } server.ns_ip = extip.s_addr; fprintf(stderr, "Using %s as external IP.\n", inet_ntoa(extip)); } if (server.ns_ip == INADDR_NONE) { warnx("Bad IP address to return as nameserver."); usage(); } if (server.netmask > 30 || server.netmask < 8) { warnx("Bad netmask (%d bits). Use 8-30 bits.", server.netmask); usage(); } if (strlen(server.password) == 0) { if (NULL != getenv(PASSWORD_ENV_VAR)) snprintf(server.password, sizeof(server.password), "%s", getenv(PASSWORD_ENV_VAR)); else read_password(server.password, sizeof(server.password)); } created_users = init_users(server.my_ip, server.netmask); if ((server.tun_fd = open_tun(device)) == -1) { /* nothing to clean up, just return */ return 1; } if (!skipipconfig) { const char *other_ip = users_get_first_ip(); if (tun_setip(argv[0], other_ip, server.netmask) != 0 || tun_setmtu(server.mtu) != 0) { retval = 1; free((void*) other_ip); goto cleanup; } free((void*) other_ip); } #ifdef HAVE_SYSTEMD nb_fds = sd_listen_fds(0); if (nb_fds > 1) { retval = 1; warnx("Too many file descriptors received!\n"); goto cleanup; } else if (nb_fds == 1) { /* XXX: assume we get IPv4 socket */ server.dns_fds.v4fd = SD_LISTEN_FDS_START; } else { #endif if ((server.addrfamily == AF_UNSPEC || server.addrfamily == AF_INET) && (server.dns_fds.v4fd = open_dns(&server.dns4addr, server.dns4addr_len)) < 0) { retval = 1; goto cleanup; } if ((server.addrfamily == AF_UNSPEC || server.addrfamily == AF_INET6) && /* Set IPv6 socket to V6ONLY */ (server.dns_fds.v6fd = open_dns_opt(&server.dns6addr, server.dns6addr_len, 1)) < 0) { retval = 1; goto cleanup; } #ifdef HAVE_SYSTEMD } #endif /* Setup dns file descriptors to get destination IP address */ if (server.dns_fds.v4fd >= 0) prepare_dns_fd(server.dns_fds.v4fd); if (server.dns_fds.v6fd >= 0) prepare_dns_fd(server.dns_fds.v6fd); if (server.bind_enable) { if ((server.bind_fd = open_dns_from_host(NULL, 0, AF_INET, 0)) < 0) { retval = 1; goto cleanup; } } if (created_users < USERS) { fprintf(stderr, "Limiting to %d simultaneous users because of netmask /%d\n", created_users, server.netmask); } fprintf(stderr, "Listening to dns for domain %s\n", server.topdomain); if (foreground == 0) do_detach(); if (pidfile != NULL) do_pidfile(pidfile); #ifdef FREEBSD tzsetwall(); #endif #ifndef WINDOWS32 openlog( __progname, LOG_NDELAY, LOG_DAEMON ); #endif if (newroot != NULL) do_chroot(newroot); signal(SIGINT, sigint); if (username != NULL) { #ifndef WINDOWS32 gid_t gids[1]; gids[0] = pw->pw_gid; if (setgroups(1, gids) < 0 || setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0) { warnx("Could not switch to user %s!\n", username); usage(); } #endif } if (context != NULL) do_setcon(context); syslog(LOG_INFO, "started, listening on port %d", server.port); server_tunnel(); syslog(LOG_INFO, "stopping"); close_socket(server.bind_fd); cleanup: close_socket(server.dns_fds.v6fd); close_socket(server.dns_fds.v4fd); close_socket(server.tun_fd); #ifdef WINDOWS32 WSACleanup(); #endif /* TODO close user TCP forward sockets */ return retval; }