Example #1
0
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;
}
Example #2
0
int
open_dns(struct sockaddr_storage *sockaddr, size_t sockaddr_len)
{
	return open_dns_opt(sockaddr, sockaddr_len, -1);
}