Example #1
0
int daemon_main()
{
	network_init(); // (WSAStartup())
	register_signals();
	sdns::conf.read_config();
	try {
		tcp_thread tcp;
		eventloop eloop(&tcp);
		tcp.set_pointers(&eloop);
		if(sdns::conf[sdns::SDNS_USER] != "")
			drop_root(sdns::conf[sdns::SDNS_USER]);
		else
			dout() << "SDNS_USER not set, not changing uid/gid";
		std::thread eventloop_th(std::ref(eloop));
		std::thread tcp_th(std::ref(tcp));
		while(true) {
			os_event event = wait_for_os_event();
			if(event == os_event::shutdown) break;
			if(event == os_event::reload) eloop.reread_static_records();
		}
		eloop.stop();
		tcp.stop();
		eventloop_th.join();
		tcp_th.join();
	} catch(const e_exception &e) {
		eout() << "Error at main(): " << e;
		eout() << "errno was: " << errno << ": " << strerror_rp(errno);
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}
Example #2
0
void configuration::sanity_check_values()
{
	if(conf[DTLS_BIND_PORT] == 0 || conf[DTLS_OUTGOING_PORT] == 0) {
		// use random but persistent ports for these
		std::ofstream conffile(conf[CONFIG_FILE], std::ios_base::out | std::ios_base::binary | std::ios_base::app);
		if(conf[DTLS_BIND_PORT] == 0) {
			uint16_t port = ntohs(get_random_port());
			iout() << "Assigned DTLS_BIND_PORT random port " << port;
			conffile << "\nDTLS_BIND_PORT=" << port << '\n';
			assign_value(DTLS_BIND_PORT, port);
		}
		if(conf[DTLS_OUTGOING_PORT] == 0) {
			uint16_t port = ntohs(get_random_port());
			iout() << "Assigned DTLS_OUTGOING_PORT random port " << port;
			conffile << "\nDTLS_OUTGOING_PORT=" << port << '\n';
			assign_value(DTLS_OUTGOING_PORT, port);
		}
	}
	if(conf[DTLS_BIND_PORT] == conf[DTLS_OUTGOING_PORT] || conf[DTLS_BIND6_PORT] == conf[DTLS_OUTGOING_PORT]) {
		eout() << "DTLS_BIND_PORT or DTLS_OUTGOING_PORT not initialized to separate valid ports, these should have been set to random ports during installation."
			<< " You must set DTLS_BIND_PORT and DTLS_OUTGOING_PORT to separate port numbers in the snow configuration file."
			   << " If you have more than one device try to choose different ports for each device.";
		abort();
	}
	check_port(conf[DTLS_OUTGOING_PORT], "DTLS_OUTGOING_PORT");
	check_port(conf[DTLS_BIND_PORT], "DTLS_BIND_PORT");
	check_port(conf[DTLS_BIND6_PORT], "DTLS_BIND6_PORT");
	check_port(conf[DHT_PORT], "DHT_PORT");
	check_port(conf[NAMESERV_PORT], "NAMESERV_PORT");
	check_nonzero(conf[NAMESERV_TIMEOUT_SECS], "NAMESERV_TIMEOUT_SECS");
	check_nonzero(conf[DTLS_IDLE_TIMEOUT_SECS], "DTLS_IDLE_TIMEOUT_SECS");
	check_nonzero(conf[HEARTBEAT_SECONDS], "HEARTBEAT_SECONDS");
	check_nonzero(conf[HEARTBEAT_RETRIES], "HEARTBEAT_RETRIES");
	if(conf[NAT_IP_GRACE_PERIOD_SECONDS] < 1800) {
		wout() << "NAT IP grace period of " << conf[NAT_IP_GRACE_PERIOD_SECONDS] << " from configuration file is too short, using minimum grace period of 1800 seconds";
		assign_value(NAT_IP_GRACE_PERIOD_SECONDS, 1800);
	}
	if(conf[DHT_BOOTSTRAP_TARGET]==0) {
		wout() << "DHT_BOOTSTRAP_TARGET cannot be zero, using default value";
		assign_value(DHT_BOOTSTRAP_TARGET, 6);
	}
	if(conf[DHT_MAX_PEERS] <= 3) {
		wout() << "DHT_MAX_PEERS cannot be " << conf[DHT_MAX_PEERS] << ", must be at least 4, using default value of 99";
		assign_value(DHT_MAX_PEERS, 99);
	}
	if(conf[NATPOOL_NETMASK_BITS] > 20 || conf[NATPOOL_NETMASK_BITS] < 4) {
		eout() << "NATPOOL_NETMASK_BITS must be between 4 and 20";
		abort();
	}
	uint32_t addr, netmask = ~htonl((1 << (32 - conf[NATPOOL_NETMASK_BITS])) - 1);
	if(inet_pton(AF_INET, conf[NATPOOL_NETWORK].c_str(), &addr) != 1 || (addr & ~netmask) != 0) {
		eout() << "NATPOOL_NETWORK/NATPOOL_NETMASK_BITS as " << conf[NATPOOL_NETWORK] << "/" << conf[NATPOOL_NETMASK_BITS] << " is not a valid subnet.";
		abort();
	}
	if(conf[VIRTUAL_INTERFACE_MTU] > 65535 || conf[VIRTUAL_INTERFACE_MTU] < 576) {
		eout() << "VIRTUAL_INTERFACE_MTU cannot be " << conf[VIRTUAL_INTERFACE_MTU];
		abort();
	}
}
Example #3
0
void check_nonzero(size_t val, std::string str)
{
	if(val==0) {
		eout() << str << " cannot be zero";
		abort();
	}
}
Example #4
0
StateResult handle_client_intro(void *arg) {
	Handler       *h  = (Handler*) arg;
	ClientHandler *ch = (ClientHandler*) h->udata;

	dout(2, "[%d]: %s running...\n", mypid, __FUNCTION__);

	reset_clienthandler(ch, RESET_INPUT|RESET_OUTPUT);

	ch->olen = snprintf(ch->obuf, ch->ocap,
					"%d welcomes you.  Our NOLISTEN status is: [%s]\r\n",
					mypid, (is_nolisten == 0) ? "Disabled" : "Enabled");
	ch->optr = ch->obuf;

	client_write(h);

	migrate_state(h, handle_client);

   	if (kmgr->enqueue_kevent(h->fd, EVFILT_READ, EV_ADD|EV_ENABLE, h) < 0)
	{
       	eout("[%d]: %s failed to enqueue kevent on READ.\n", mypid,
			__FUNCTION__);
       	return STATE_ERROR;
   	}

	return STATE_OKAY;
}
Example #5
0
void check_port(size_t port, std::string str)
{
	if(port == 0 || port > 65535) {
		eout() << str << " set to invalid value in configuration, a port value must be 1-65535";
		abort();
	}
}
Example #6
0
void network_init()
{
#ifdef WINSOCK
	dout() << "network_init: doing WSAStartup()";
	WSADATA not_interested;
	int rv = WSAStartup(MAKEWORD(2,2), &not_interested);
	if(rv != 0) {
		eout() << "WSAStartup failed: " << get_windows_errorstr(rv);
		throw e_check_sock_err("WSAStartup failed", true);
	}
#endif
}
Example #7
0
void dtls_dispatch::icmp_socket_event(size_t, pvevent event, sock_err err)
{
	if(event & pvevent::error) {
		eout() << "Error on ICMP socket: " << err;
		return;
	}
	try {
		dbuf buf(buflist.get());
		size_t bytes = sockets[ICMP4_FD].sock.recv(buf.data(), buf.size());
		if(bytes > 0) {
			snow_packet* packet = reinterpret_cast<snow_packet*>(buf.data());
			if(validate_packet4_length(packet, bytes)) {
				dout() << bytes << " byte icmp4 socket packet: " << *packet;
				if(packet->header.protocol == ipv4_header::ICMP) {
					icmp_header& icmp = packet->header.transport_header().icmp;
					if(icmp.icmp_type == icmp_header::DEST_UNREACHABLE && icmp.payload()->header.protocol == ipv4_header::UDP) {
						const snow_packet* inner_packet = icmp.payload();
						if(icmp.code == icmp_header::PACKET_TOO_BIG) {
							uint16_t mtu = ntohl(icmp.header_data) & 0xffff; // proposed MTU
							mtu += inner_packet->header.ihl();
							set_icmp_pmtu(ip_info(inner_packet->header.src_addr, inner_packet->header.transport_header().udp.src_port),
								ip_info(inner_packet->header.dst_addr, inner_packet->header.transport_header().udp.dst_port), mtu);
						} else {
							icmp_unreachable(ip_info(inner_packet->header.src_addr, inner_packet->header.transport_header().udp.src_port),
								ip_info(inner_packet->header.dst_addr, inner_packet->header.transport_header().udp.dst_port));
						}
					}
				}
			} else {
				dout() << bytes << " byte ICMP packet failed length validation";
			}
		} else {
			// (fail)
			dout() << "0 byte packet from vnet ICMP4 socket";
		}
		buflist.recover(std::move(buf));
	} catch(const e_check_sock_err& e) {
		eout() << "Recv error from ICMP socket: " << e;
	}
}
Example #8
0
int main(int argc, char **argv)
{
	std::string config_filename = sdns::conf[sdns::CONFIG_FILE], daemon_name = "sdns";
	bool daemonize = false;
#ifndef WINDOWS
	srandom((size_t)(&sdns::conf) + time(nullptr));
	for(int opt; (opt = getopt(argc, argv, "c:ds:")) != -1;) {
		switch(opt) {
		case 'c': // config filename
			config_filename = optarg;
			break;
		case 'd': // daemonize
			daemonize = true;
			break;
		case 's': // syslog daemon name
			daemon_name = optarg;
		default: // '?'
			std::cerr << "Usage: " << argv[0] << " [-c config_filename] [-d (daemonize)] [-s syslog_daemon_name]" << std::endl;
			exit(EXIT_FAILURE);
		}
	}
	if(optind < argc) {
		eout() << "Unexpected argument: " << argv[optind];
		exit(EXIT_FAILURE);
	}
#endif
	sdns::conf.set_config_file(config_filename);
	try {
		if(daemonize)
			return daemon_start(&daemon_main, daemon_name.c_str());
		else
			return daemon_main();
	} catch(const e_exception &e) {
		eout() << "main() caught fatal or unhandled error: " << e;
		std::cerr << daemon_name << " main() caught fatal or unhandled error: " << e << std::endl;
	}
	return EXIT_FAILURE;
}
Example #9
0
// pinit -> bye bye
void dtls_dispatch::remove_peer(const ip_info& local, const ip_info& remote)
{
	dout() << "remove_peer local " << local << " remote " << remote;
	auto local_it = socket_map.find(local);
	if(local_it != socket_map.end()) {
		dtls_socket& sock = sockets[local_it->second];
		auto remote_it = sock.peers.find(remote);
		if(remote_it != sock.peers.end()) {
			sock.peers.erase(remote_it);
			if(sock.peers.size() == 0 && (sock.flags & dtls_socket::PERSISTENT) == 0) {
				dout() << "retiring disused non-persistent socket at idx " << local_it->second;
				sockets.mark_defunct(local_it->second);
				socket_map.erase(local_it);
			} else {
				dout() << "not removing socket with " << sock.peers.size() << " peers, flags " << sock.flags;
			}
		} else {
			eout() << "BUG: Requested to remove connection with non-existent peer from local " << local << " to remote " << remote;
		}
	} else {
		eout() << "BUG: Requested to remove connection with non-existent socket from local " << local << " to remote " << remote;
	}
}
Example #10
0
void dtls_dispatch::icmp6_socket_event(size_t, pvevent event, sock_err err)
{
	if(event & pvevent::error) {
		eout() << "Error on ICMP socket: " << err;
		return;
	}
	try {
		dbuf buf(buflist.get());
		size_t bytes = sockets[ICMP6_FD].sock.recv(buf.data(), buf.size());
		if(bytes > 0) {
			icmp6_header* packet = reinterpret_cast<icmp6_header*>(buf.data());
			if(validate_ipv6_icmp(packet, bytes)) {
				dout() << bytes << " byte icmp6 socket packet: " << *packet;
				if(packet->contains_inner_packet() && packet->payload()->header6.next_header == ipv4_header::UDP) {
					const snow_packet* inner_packet = packet->payload();
					if(packet->icmp_type == icmp6_header::PACKET_TOO_BIG) {
						// this fails to account for extension headers, but 'next_header == UDP' is ignoring packets with them anyway
						uint16_t mtu = ntohl(packet->header_data)/*proposed MTU*/ + sizeof(ipv6_header);
						set_icmp_pmtu(ip_info(inner_packet->header6.src_addr, inner_packet->header6.transport_header().udp.src_port),
							ip_info(inner_packet->header6.dst_addr, inner_packet->header6.transport_header().udp.dst_port), mtu);
					} else if(packet->icmp_type == icmp6_header::DEST_UNREACHABLE) {
						icmp_unreachable(ip_info(inner_packet->header6.src_addr, inner_packet->header6.transport_header().udp.src_port),
							ip_info(inner_packet->header6.dst_addr, inner_packet->header6.transport_header().udp.dst_port));
					}
				}
			} else {
				dout() << bytes << " byte ICMP6 packet failed length validation";
			}
		} else {
			// (fail)
			dout() << "0 byte packet from vnet ICMP6 socket";
		}
		buflist.recover(std::move(buf));
	} catch(const e_check_sock_err& e) {
		eout() << "Recv error from ICMP6 socket: " << e;
	}
}
Example #11
0
void dtls_dispatch::send_holepunch(const sockaddrunion& local, const sockaddrunion& remote)
{
	// send holepunch packet with zero byte payload
	dout() << "send_holepunch local " << local << " remote " << remote;
	auto it = socket_map.find(ip_info(local));
	if(it != socket_map.end()) {
		try {
			sockets[it->second].sock.sendto("", 0, remote);
		} catch(const check_err_exception &e) {
			eout() << "dispatch doing UDP holepunch local " << local << " remote " << remote << ": " << e;
		}
	} else {
		dout() << "Could not find local addr " << local << " in socket map for sending holepunch to " << remote;
	}
}
Example #12
0
uint16_t get_random_port()
{
	try {
		csocket sock(AF_INET, SOCK_DGRAM);
		sockaddrunion su;
		memset(&su, 0, sizeof(su));
		su.sa.sin_family = AF_INET;
		sock.bind(su);
		sock.getsockname(su);
		return su.sa.sin_port;
	} catch(const e_check_sock_err& e) {
		eout() << __FILE__ << ":" << __LINE__ << ": Failed to bind port for get_random_port(): " << e;
		abort();
	}
	return 0;
}
Example #13
0
tuntap::if_info tuntap::get_if_info()
{
#ifdef WINDOWS
	if_info rv;
	std::string interface_key = "SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces\\" + adapter_GUID;
	std::vector<std::string> addr, mask;
	try {
		registry_key adapter(HKEY_LOCAL_MACHINE, interface_key.c_str(), KEY_READ);
		addr = adapter.values().get_value_multi_string("IPAddress"), mask = adapter.values().get_value_multi_string("SubnetMask");
	} catch (const registry_exception &re) {
		eout() << "Failed to open registry key for TUN/TAP adapter (HKLM\\" << interface_key << "), cannot determine TUN/TAP adapter IP addr/netmask: " << re;
		throw;
	}
	if(addr.size() != mask.size())
		throw e_invalid_input("Invalid registry data: Number of IP addresses on virtual interface did not match number of subnet masks");
	if(addr.size() == 0)
		throw e_not_found("No IP address assigned to virtual interface");
	if(addr.size() > 1)
		wout() << "Support for multiple IP addresses on virtual interface not implement, only the first address will be used: " << addr.front();
	if(inet_pton(AF_INET, addr.front().c_str(), &rv.if_addr) <= 0)
		throw check_err_exception("Could not convert virtual interface address string to IP address");
	if(inet_pton(AF_INET, mask.front().c_str(), &rv.netmask) <= 0)
	throw check_err_exception("Could not convert virtual interface netmask string to IP address");
	return rv;
#else
	if_info rv;
	csocket sock(AF_INET, SOCK_DGRAM); // need AF_INET socket for SIOCGIFADDR/SIOCGIFNETMASK
	ifreq ifr;
	memset(&ifr, 0, sizeof(ifr));
	if(snow::conf[snow::VIRTUAL_INTERFACE].size() >= IFNAMSIZ)
		throw e_invalid_input("Virtual interface name length is too long");
    strcpy(ifr.ifr_name, snow::conf[snow::VIRTUAL_INTERFACE].c_str());
	ifr.ifr_addr.sa_family = AF_INET; // get IPv4 addr
	check_err(ioctl(sock.fd(), SIOCGIFADDR, &ifr), "getting tun/tap interface IP address");
	sockaddrunion tmp;
	tmp.s = ifr.ifr_addr;
	rv.if_addr = tmp.sa.sin_addr.s_addr;
	check_err(ioctl(sock.fd(), SIOCGIFNETMASK, &ifr), "getting tun/tap interfacet netmask");
	tmp.s = ifr.ifr_addr;
	rv.netmask = tmp.sa.sin_addr.s_addr;
	return rv;
#endif
}
Example #14
0
StateResult handle_client(void* arg) {
	Handler* handler = (Handler*) arg;
	IoResult ior;

	if (kresult == NULL) {
		migrate_state(handler, handle_client_cleanup);
		if (kmgr->enqueue_pevent(handler) < 0) {
			eout("[%d]: %s failed to enqueue pevent.\n", mypid, __FUNCTION__);
			return STATE_ERROR;
		}
		dout(2, "[%d]: %s done.\n", mypid, __FUNCTION__);
		return STATE_OKAY;
	}

	switch (kresult->filter) {
	case EVFILT_READ:
		dout(2, "[%d]: %s got EVFILT_READ...\n", mypid, __FUNCTION__);
		ior = client_read(handler);
		break;
	case EVFILT_WRITE:
		dout(2, "[%d]: %s got EVFILT_WRITE...\n", mypid, __FUNCTION__);
		ior = client_write(handler);
		break;
	default:
		dout(2, "[%d]: %s got unknown filter (%d); closing up shop.\n",
			mypid, __FUNCTION__, kresult->filter);
		ior = IO_ERROR;
	}

	if (ior < 0) {
		dout(2, "[%d]: %s got ior %d; close socket.\n", mypid, __FUNCTION__,
			ior);
		close(handler->fd);
		migrate_state(handler, handle_client_cleanup);
	}

	dout(2, "[%d]: %s done.\n", mypid, __FUNCTION__);
	return STATE_OKAY;
}
Example #15
0
IoResult client_read(Handler *h) {
	ClientHandler *ch = (ClientHandler*) h->udata;
	int nread;
#if 0
	size_t b;
	size_t bsz = sizeof(b);
#endif

	dout(2, "[%d]: %s running...\n", mypid, __FUNCTION__);

	if (h->fd < 0) {
		dout(2, "[%d] %s got dead socket; can't read with that.\n",
			mypid, __FUNCTION__);
		return IO_ERROR;
	}

	if (kresult->flags & (EV_EOF | EV_ERROR)) {
		dout(2, "[%d]: %s got EOF/ERROR...\n", mypid, __FUNCTION__);
		if (kresult->data > 0) {
			dout(2, "[%d] %s got data to read though! (%d bytes)\n", mypid,
				__FUNCTION__, kresult->data);
			goto DoRead;
		}

		dout(2, "[%d]: %s returning EOF.\n", mypid, __FUNCTION__);
		return IO_EOF;
	}

DoRead:

	dout(2, "[%d]: %s performing read operation on fd %d\n", mypid,
		__FUNCTION__, h->fd);

	if ((nread = read(h->fd, ch->iptr, ch->icap - ch->ilen)) < 0) {
		if (errno == EINTR || errno == EAGAIN) {
			dout(2, "[%d]: %s got read error of [%d] '%s' (spin again)\n",
				mypid, __FUNCTION__, errno, strerror(errno));
			return IO_OKAY;
		}

		dout(2, "[%d]: %s got read error of [%d] '%s' (fatal)\n",
			mypid, __FUNCTION__, errno, strerror(errno));
		return IO_ERROR;
	}

	dout(2, "[%d]: %s got %d bytes off of wire.\n", mypid,
		__FUNCTION__, nread);

	ch->ilen += nread;
	ch->iptr += nread;

	ch->ibuf[ch->ilen] = '\0';

	dout(2, "[%d]: %s got %d total bytes so far.\n", mypid, __FUNCTION__,
		ch->ilen);

	if (ch->ilen > 1) {
		if (ch->ibuf[ch->ilen-1] == '\n' && ch->ibuf[ch->ilen-2] == '\r')
		{
			dout(2, "[%d]: %s got complete line.\n", mypid, __FUNCTION__);

			ch->ibuf[ch->ilen-2] = '\0'; ch->ilen -= 2;
			ch->iptr = &ch->ibuf[0];

			if (strcmp(ch->iptr, "listen off") == 0) {
				// turn the listen socket off.
				dout(2, "[%d]: %s got command to turn listen OFF.\n", mypid,
					__FUNCTION__);

#if 0
				b=1;
				if (setsockopt(listenfd, SOL_SOCKET,SO_NOLISTEN, &b, bsz) < 0) {
					eout("[%d]: %s can't set listenfd OFF [%d] %s.\n", mypid,
						__FUNCTION__, errno, strerror(errno));
				} else {
					dout(2, "[%d]: %s listenfd is OFF; enqueuing on write.\n",
						mypid, __FUNCTION__);
					is_nolisten=1;
					ch->olen = snprintf(ch->obuf, ch->ocap,
								"[%d]: listenfd is now OFF.\r\n", mypid);
					ch->optr = ch->obuf;
    				if (kmgr->enqueue_kevent(h->fd, EVFILT_WRITE,
						EV_ADD | EV_ENABLE | EV_ONESHOT, h) < 0)
					{
						eout("[%d]: %s failed to enqueue kevent.\n", mypid,
							__FUNCTION__);
						return IO_ERROR;
					}

					dout(2, "[%d]: %s disabling listenfd from kq.\n", mypid,
						__FUNCTION__);

					if (kmgr->enqueue_kevent(listenfd, EVFILT_READ,
						EV_DISABLE, h) < 0)
					{
						eout("[%d]: %s failed to enqueue kevent.\n", mypid,
							__FUNCTION__);
						return IO_ERROR;
					}
				}
#endif

			} else if (strcmp(ch->iptr, "listen on") == 0) {
				// turn the listen socket on.
				dout(2, "[%d]: %s got command to turn listen ON.\n", mypid,
					__FUNCTION__);

#if 0
				b=0;
				if (setsockopt(listenfd, SOL_SOCKET,SO_NOLISTEN, &b, bsz) < 0) {
					eout("[%d]: %s can't turn listenfd ON [%d] %s.\n", mypid,
						__FUNCTION__, errno, strerror(errno));
				} else {
					dout(2, "[%d]: %s listenfd is ON; enqueuing on write.\n",
						mypid, __FUNCTION__);
					is_nolisten=0;
					ch->olen = snprintf(ch->obuf, ch->ocap,
								"[%d]: listenfd is now ON.\r\n", mypid);
					ch->optr = ch->obuf;
    				if (kmgr->enqueue_kevent(h->fd, EVFILT_WRITE,
						EV_ADD | EV_ENABLE | EV_ONESHOT, h) < 0)
					{
						eout("[%d]: %s failed to enqueue kevent.\n", mypid,
							__FUNCTION__);
						return IO_ERROR;
					}

					dout(2, "[%d]: %s re-enabling listenfd from kq.\n", mypid,
						__FUNCTION__);

					if (kmgr->enqueue_kevent(listenfd, EVFILT_READ,
						EV_ENABLE, h) < 0)
					{
						eout("[%d]: %s failed to enqueue kevent.\n", mypid,
							__FUNCTION__);
						return IO_ERROR;
					}

				}
#endif
			} else if (strcmp(ch->iptr, "listen status") == 0) {
				dout(2, "[%d]: %s writing out is_nolisten status (%d)\n",
					mypid, __FUNCTION__, is_nolisten);
				ch->olen = snprintf(ch->obuf, ch->ocap,
							"[%d]: listenfd is %s.\r\n", mypid,
							(is_nolisten == 1) ? "Off" : "On");
				ch->optr = ch->obuf;
    			if (kmgr->enqueue_kevent(h->fd, EVFILT_WRITE,
					EV_ADD | EV_ENABLE | EV_ONESHOT, h) < 0)
				{
					eout("[%d]: %s failed to enqueue kevent.\n", mypid,
						__FUNCTION__);
					return IO_ERROR;
				}
			}
	
			reset_clienthandler(ch, RESET_INPUT);
		}
	}

	dout(2, "[%d]: %s done.\n", mypid, __FUNCTION__);
	return IO_OKAY;
}
Example #16
0
StateResult handle_accept(void* arg) {

    int connfd;
    sai_t client_addr;
    socklen_t caddr_sz = sizeof(client_addr);
    char ipaddr[INET_ADDRSTRLEN];
	Handler *h, *new_h;
	ClientHandler *new_ch;
	int incoming=0,i=1;

	h        = (Handler*) arg;
	incoming = kresult->data;

	while (incoming) {

		dout(2, "[%d]: %s processing event %d of %d\n", mypid, __FUNCTION__,
			i, incoming);

		if (is_nolisten) {
			dout(2, "[%d]: **** !!! I'm in NO Listen, but got request! ****\n",
				mypid);
		}

		memset(&client_addr, 0, sizeof(client_addr));
		if ((connfd = accept(listenfd, (sa_t*) &client_addr, &caddr_sz)) < 0)
		{
			if (errno == EINTR || errno == EAGAIN) {
				dout(2, "[%d]: accept got '%s'; spin again.\n", mypid,
					strerror(errno));
				return STATE_OKAY;
			}

			eout("[%d]: accept failed - [%d] %s\n", errno, strerror(errno));
		}

		if (inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ipaddr,
				INET_ADDRSTRLEN) < 0)
		{
			eout("[%d]: can't decode client ip addr.\n");
			close(connfd);
			return STATE_OKAY;
		}

		dout(2, "[%d]: got connfd %d { %s, %d }; "
			"spin up handler...\n", mypid, connfd, ipaddr, 
			ntohs(client_addr.sin_port));

		set_fd_nonblock(connfd);

		new_ch = create_clienthandler(BUF_SZ, BUF_SZ);
		new_h  = create_handler(connfd, handle_client_intro, new_ch);

		// We first write an intro to the client and then enqueue on read.

		if (kmgr->enqueue_kevent(connfd, EVFILT_WRITE,
			EV_ADD|EV_ENABLE|EV_ONESHOT, new_h) < 0)
		{
        	eout("[%d]: %s failed to enqueue kevent.\n", mypid, handle_accept);
        	return STATE_ERROR;
    	}

		incoming--; i++;
	}

	return STATE_OKAY;
}
Example #17
0
// 1419 is the mtu using a 1500 byte ethernet mtu - ipv4 header - udp - dtls with default ciphersuite (I think)
// TODO: read mtu from interface on each OS instead of hard coding 1419 (also have installer set sensible interface MTU)
tuntap::tuntap() : mtu(1419)
{
// TODO: use configuration parameters for registry key names instead of hard coded values
// TODO: package TAP-Windows and change component ID or otherwise figure out how to make it not collide with what OpenVPN uses
	// see comment in %PROGRAMFILES%\TAP-Windows\driver\OemWin2k.inf after installing OpenVPN TAP driver
#ifdef WINDOWS
	sent_sync = true; // no async send in progress
	received_sync = 0;
	memset(&recv_overlapped, 0, sizeof(OVERLAPPED));
	memset(&send_overlapped, 0, sizeof(OVERLAPPED));
	recv_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
	if(recv_event == INVALID_HANDLE_VALUE)
		throw check_err_exception("CreateEvent failed for tuntap recv_event");
	recv_overlapped.hEvent = recv_event;
	// default callback emits error
	read_ready_cb = []() { eout() << "BUG: tuntap read ready callback called but not set"; };
	if(RegisterWaitForSingleObject(&recv_wait, recv_event, &tuntap::read_event_cb, this, INFINITE, WT_EXECUTEINWAITTHREAD) == FALSE)
		throw check_err_exception("RegisterWaitForSingleObject on tuntap read event");
	adapter_GUID = snow::conf[snow::VIRTUAL_INTERFACE];
	if(adapter_GUID == "auto") {
		try {
			// open registry key for all network adapters
			registry_key adapters(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}", KEY_READ);
			for(auto &it : adapters.subkeys()) {
				try {
					registry_key adapter = it.get(KEY_READ);
					// TODO: change ComponentId from "tap0901" to some other string to remove possible namespace collision with OpenVPN (this has to occur in the driver too)
					if(adapter.values().get_value_string("ComponentId") == "tap0901") {
						adapter_GUID = adapter.values().get_value_string("NetCfgInstanceId");
						break;
					}
				} catch(const registry_exception &re) {
					dout() << "Failed to access registry subkey: " << re;
				}
				
			}
		} catch (const registry_exception &re) {
			eout() << "FATAL: Failed to open network adapters registry key, cannot determine TUN adapter to use: " << re;
			throw;
		}
	}
	dout() << "Adapter GUID: " << adapter_GUID;
	if(adapter_GUID == "auto")
		throw e_not_found("no usable tuntap interface");
	// [at this point GUID is value of NetCfgInstanceId, although there could be more than one interface: check all against something else? (interface name?)]
	// TODO: probably the thing to do is: on install, create a TAP-Windows interface and set its name to 'snow tunnel interface' or so,
		// then set the GUID in the configuration file [or registry setting], and GUI config program can list interfaces by name and allow user to choose
		// then the configured GUID is the interface we use and all of this mess doesn't even need to go here -- it just goes once on install and in the config editor
		// what would then really be useful would be a way (other than changing the ComponentId) to tell other software (e.g. OpenVPN) not to use a particular interface
			// two possible solutions may be to either make sure that snow starts before the other service [somehow] and gets there first,
			// or setting permissions [somehow] so that only snow process has access
	std::string tap_filename("\\\\.\\Global\\");
	tap_filename += adapter_GUID + ".tap";
	fd = CreateFile(tap_filename.c_str(), GENERIC_READ | GENERIC_WRITE, 0/*no shared access*/, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, nullptr);
	if(fd == INVALID_HANDLE_VALUE)
		throw check_err_exception("Failed to open Windows TUN/TAP device");
	// name of specific instance of adapter as shown in control panel can be found in:
		// "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\" + adapter_GUID + "\\Connection"
		// value is "Name"
	
	// set to TUN mode
	// TAP_WIN_IOCTL_CONFIG_TUN takes three args: interface IP addr, network addr and netmask
	if_info ifinfo = get_if_info();
	uint32_t tun_addrs[3] = { ifinfo.if_addr, ifinfo.if_addr & ifinfo.netmask, ifinfo.netmask };
	DWORD rsize;
	if(DeviceIoControl(fd, TAP_WINDOWS_IOCTL_CONFIG_TUN, tun_addrs, sizeof(tun_addrs), tun_addrs, sizeof(tun_addrs), &rsize, nullptr))
		dout() << "TAP-Windows CONFIG_TUN success: " << ss_ipaddr(tun_addrs[0]) << " " << ss_ipaddr(tun_addrs[1]) << " " << ss_ipaddr(tun_addrs[2]);
	else
		eout() << "TAP-Windows CONFIG_TUN FAIL: " << ss_ipaddr(tun_addrs[0]) << " " << ss_ipaddr(tun_addrs[1]) << " " << ss_ipaddr(tun_addrs[2]);

	// set media status as connected
	ULONG connected = TRUE;
	if(DeviceIoControl(fd, TAP_WINDOWS_IOCTL_SET_MEDIA_STATUS, &connected, sizeof(connected), &connected, sizeof(connected), &rsize, nullptr) == FALSE)
		wout() << "Failed to set TAP-Windows media status to connected";
	ULONG ifmtu;
	if(DeviceIoControl(fd, TAP_WINDOWS_IOCTL_GET_MTU, &ifmtu, sizeof(ifmtu), &ifmtu, sizeof(ifmtu), &rsize, nullptr)) {
		dout() << "Read mtu from TAP-Windows interface: " << ifmtu;
		if(ifmtu > MIN_PMTU)
			mtu = ifmtu;
	} else {
		wout() << "Failed to get Tap-Windows MTU, assuming default";
	}
	
	// [flush ARP cache? -> probably unnecessary with tun, but maybe do it anyway? certainly don't want invalid ARP cache entries for pool addrs from before interface startup]
	
	// ... also (may) need to undo whatever configuration (remove addrs from if, routes, etc.) on destruction, CloseHandle on fd, set media status to disconnected, etc.

#else
	check_err((fd = open(snow::conf[snow::CLONE_DEVICE].c_str(), O_RDWR)), "opening tun/tap clone device");
	ifreq ifr;
	memset(&ifr, 0, sizeof(ifr));
	// check size manually instead of using strncpy: strncpy fails silently and doesn't null terminate if there is insufficient space
	if(snow::conf[snow::VIRTUAL_INTERFACE].size() >= IFNAMSIZ)
		throw check_err_exception("VIRTUAL_INTERFACE name is too long", false);
	strcpy(ifr.ifr_name, snow::conf[snow::VIRTUAL_INTERFACE].c_str());
	check_err(fcntl(fd, F_SETFL, O_NONBLOCK), "setting tuntap socket to non-blocking");
#ifdef __linux__
	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
	check_err(ioctl(fd, TUNSETIFF, (void*) &ifr), "opening tun/tap interface");
#endif
	// note: Mac and BSD require a stupid hack to make tun behave properly because they insist on a single destination address being specified
		// solution is to exclude an address from the address pool and specify it as the destination when configuring the interface
		// then set a route specifying that address as the gateway for the entire snow subnet so OS will send them all to the tun interface
	// TODO: write code to do that programmatically for Mac/BSD
		// existing code seems to work on BSD if you set CLONE_DEVICE to e.g. /dev/tun0 and VIRTUAL_INTERFACE to e.g. tun0 and configure the tun interface manually, e.g.:
		// # ifconfig tun0 create 172.16.0.1 netmask 255.240.0.0 172.31.255.254 mtu 1419
		// # route add -net 172.16.0.0 172.31.255.254 255.240.0.0
		// (note: this must be done each time you start the daemon, when the daemon exits the interface remains but configuration is forgotten; same two commands w/o "create")
		// (note: 'ifconfig tun create' will create next tun# necessary, see if there is any simple way to replicate with API)
		// doing this programmatically requires call to ioctl passing SIOCSIFPHYADDR with in_aliasreq struct as follows:
			// ifra_name = "tun[#]"
			// ifra_addr as local interface addr
			// ifra_dstaddr as fake addr
			// ifra_mask as subnet mask
		// and then adding the route via some call to the BSD routing API
	csocket sock(AF_INET, SOCK_DGRAM); // need ordinary socket for SIOC[GS]*, can't use tun fd
	check_err(ioctl(sock.fd(), SIOCGIFFLAGS, (void*) &ifr), "getting tuntap interface flags");
	if((ifr.ifr_flags & IFF_UP) == 0) {
		dout() << "tuntap interface was not up, trying to bring it up";
		ifr.ifr_flags |= IFF_UP; // make sure interface is up
		check_err(ioctl(sock.fd(), SIOCSIFFLAGS, (void*) &ifr), "setting tuntap interface flags");
	} else {
		dout() << "tuntap interface was up";
	}
	// there are two possible ways of doing this which are both supported: either snow launches as root or with CAP_NET_ADMIN and sets these here,
		// or the interface is persistent and configured ahead of time, in which case only ownership of the interface is necessary
	// so what we do is check that everything is configured correctly and try to fix it if it isn't
	// that way everything is fine as long as the interface is correctly preconfigured -or- it isn't but we have rights to fix it
	in_addr addr, netmask;
	inet_pton(AF_INET, snow::conf[snow::NATPOOL_NETWORK].c_str(), &addr.s_addr);
	addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
	netmask.s_addr = ~htonl((1 << (32 - snow::conf[snow::NATPOOL_NETMASK_BITS])) - 1);
	ifr.ifr_addr.sa_family = AF_INET;
	// EADDRNOTAVAIL is returned if no address is assigned which just means we have to assign the address, so then ifr_addr will be zero and not match addr
	int rv = ioctl(sock.fd(), SIOCGIFADDR, &ifr);
	if(rv < 0 && errno != EADDRNOTAVAIL)
		throw check_err_exception("getting tun/tap interface IP address");
	sockaddrunion su;
	su.s = ifr.ifr_addr;
	dout() << "Got tuntap ifaddr " << ss_ipaddr(su.sa.sin_addr.s_addr);
	if(su.sa.sin_addr.s_addr != addr.s_addr) {
		dout() << "Virtual interface IP addr was " << ss_ipaddr(su.sa.sin_addr.s_addr) << ", should be " << ss_ipaddr(addr.s_addr) << ", trying to fix";
		su.sa.sin_addr = addr;
		ifr.ifr_addr = su.s;
		check_err(ioctl(sock.fd(), SIOCSIFADDR, &ifr), "setting tun/tap interface IP address");
	}
	check_err(ioctl(sock.fd(), SIOCGIFNETMASK, &ifr), "getting tun/tap interface netmask");
	su.s = ifr.ifr_addr;
	dout() << "Got tuntap netmask " << ss_ipaddr(su.sa.sin_addr.s_addr);
	if(su.sa.sin_addr.s_addr != netmask.s_addr) {
		dout() << "Virtual interface netmask was " << ss_ipaddr(su.sa.sin_addr.s_addr) << ", should be " << ss_ipaddr(netmask.s_addr) << ", trying to fix";
		su.sa.sin_addr = netmask;
		ifr.ifr_addr = su.s;
		check_err(ioctl(sock.fd(),  SIOCSIFNETMASK, &ifr), "setting tun/tap interface netmask");
	}
	mtu = snow::conf[snow::VIRTUAL_INTERFACE_MTU];
	check_err(ioctl(sock.fd(), SIOCGIFMTU, (void*) &ifr), "getting tun/tap interface MTU");
	dout() << "Existing tuntap interface MTU: " << ifr.ifr_mtu;
	if(ifr.ifr_mtu <  MIN_PMTU || mtu != static_cast<unsigned>(ifr.ifr_mtu)) {
		dout() << "Virtual interface mtu was " << ifr.ifr_mtu << ", should be " << mtu << ", trying to fix";
		ifr.ifr_mtu = mtu;
		check_err(ioctl(sock.fd(), SIOCSIFMTU, (void*) &ifr), "setting tun/tap interface MTU");
	}
	iout() << "Virtual interface configured with network " << ss_ipaddr(addr.s_addr&netmask.s_addr) << " netmask " << ss_ipaddr(netmask.s_addr) <<  " address " << ss_ipaddr(addr.s_addr) << " MTU " << mtu;	
#endif
}
Example #18
0
csocket::~csocket() {
	try { close(); }
	catch(const e_check_sock_err &e) {
		eout() << "csocket::~csocket():" << e;
	}
}
Example #19
0
IoResult client_write(Handler *h) {
	ClientHandler *ch = (ClientHandler*) h->udata;
	int nwrite;

	dout(2, "[%d]: %s running...\n", mypid, __FUNCTION__);

	if (h->fd < 0) {
		dout(2, "[%d] %s got dead socket; can't read with that.\n",
			mypid, __FUNCTION__);
		return IO_ERROR;
	}

	if (kresult->flags & (EV_EOF | EV_ERROR)) {
		dout(2, "[%d]: %s got EOF/ERROR...bailing out.\n", mypid,
			__FUNCTION__);
		// just get outta' here.
		return IO_EOF;
	}

	if (ch->olen <= 0) {
		dout(2, "[%d] %s has nothing to write (olen: %d)\n", mypid,
			__FUNCTION__, ch->olen);
		return IO_OKAY;
	}

	dout(2, "[%d]: %s has %d bytes to write.\n", mypid,
		__FUNCTION__, ch->olen);

	if ((nwrite = write(h->fd, ch->optr, ch->olen)) < 0) {
		if (errno == EINTR || errno == EAGAIN) {
			dout(2, "[%d]: %s got write error of '%s' (spin again)\n",
				mypid, __FUNCTION__, strerror(errno));
			return IO_OKAY;
		}

		// something dreadful went wrong.
		return IO_ERROR;
	}

	dout(2, "[%d] %s has written %d bytes onto wire.\n", mypid,
		__FUNCTION__, nwrite);

	ch->olen -= nwrite;
	ch->optr += nwrite;

	if (ch->olen > 0) {

		dout(2, "[%d] %s re-nq on write (%d bytes left to write)\n", mypid,
			__FUNCTION__, ch->olen);

		// re-enqueue ourselves on write again.
    	if (kmgr->enqueue_kevent(h->fd, EVFILT_WRITE,
			EV_ADD | EV_ENABLE | EV_ONESHOT, h) < 0)
    	{
			eout("[%d]: %s failed to enqueue kevent.\n", mypid, __FUNCTION__);
			return IO_ERROR;
    	}

		return IO_WR_AGAIN;
	} else {
		dout(2, "[%d] %s done.  no more to bytes to write.\n", mypid,
			__FUNCTION__);
	}

	dout(2, "[%d]: %s done.\n", mypid, __FUNCTION__);
	return IO_OKAY;
}
Example #20
0
dtls_dispatch::dtls_dispatch(worker_thread* io)
	: sockets(std::bind(&dtls_dispatch::cleanup_socket, this, std::placeholders::_1), DISPATCH_NONPEER::NUM_NONPEER_FDS, "dispatch"), buflist(-1),
	  vn(new vnet(this, io, timers, buflist)), pinit(new peer_init(this, vn.get(), timers, &buflist)),
	  run_state(RUNNING), natpmp_addr(0)
{
	buflist.set_bufsize(vn->get_tun_mtu()+200); // tun MTU plus [over-]estimate of DTLS overhead
	sockets.emplace_back(INVALID_SOCKET, pvevent::read, std::bind(&vnet::tuntap_socket_event, vn.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), dtls_socket(csocket(), sockaddrunion()));
#ifdef WINDOWS
	// set_read_ready_cb must be called after sockets.emplace_back above so that TUNTAP_FD is a valid index, as the callback may be called immediately if data is available
	vn->get_tun().set_read_ready_cb(std::bind(&decltype(sockets)::indicate_read, &sockets, TUNTAP_FD), vn->get_tun_mtu());
#else
	sockets.set_fd(TUNTAP_FD, vn->get_tun().get_fd());
#endif
	sockets.emplace_back(interthread_msg_queue.getSD(), pvevent::read, std::bind(&function_tqueue::pv_execute, &interthread_msg_queue, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), dtls_socket(csocket(), sockaddrunion()));
	csocket icmp4, icmp6;
	try {
		icmp4 = csocket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
		icmp6 = csocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
	} catch(const check_err_exception& e) {
		eout() << "Failed to configure ICMP socket: " << e;
	}
	int icmp4_sd = icmp4.fd(), icmp6_sd = icmp6.fd();
	sockets.emplace_back(icmp4_sd, pvevent::read, std::bind(&dtls_dispatch::icmp_socket_event, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), dtls_socket(std::move(icmp4), sockaddrunion()));
	sockets.emplace_back(icmp6_sd, pvevent::read, std::bind(&dtls_dispatch::icmp6_socket_event, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), dtls_socket(std::move(icmp6), sockaddrunion()));
	// that's it for the nonpeers, now grab sockets for all the local ipaddrs
	peer_socket_event_function = std::bind(&dtls_dispatch::peer_socket_event, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
	std::vector<sockaddrunion> local_addrs(detect_local_addrs(vn->get_tun_ipaddr()));
	// TODO: this concept of always using the same incoming and outgoing local ports for all connections theoretically makes the NAT traversal stuff work pretty well
		// and it means we can use a finite number of sockets for arbitrarily many peers
		// but there are probably going to be some circumstances where binding a new socket on a different port will be in order
	// one example of this is when a connection fails (e.g. hard restart of the daemon or device) and the client tries to reconnect using the same ports, which the peer already has associated with the dead connection
		// the current implementation probably does the right thing eventually (maybe bad packet causes peer to do heartbeat and then fail the dead connection?)
		// and if not then the client will send DHT CONNECT which should do the same (induce peer to do heartbeat and fail dead connection)
		// but that's a lot of timeouts and maybes for something that could be avoided by binding a new socket on a random port
			// so could do that for the pre-DHT connection for reconnect-after-failure or reconnects during grace period
	// there are also likely to be situations where the NAT misbehaves in various ways, especially if two nodes behind the same NAT choose the same ports
		// although in that case the NAPT detection should mostly sort it out in theory (at least for incoming port; still needs to be implemented for outgoing port)
	// TODO: figure out what to do if we can't bind the appropriate port on one or more addrs
		// part of the problem is that this sometimes happens normally, e.g. OS (especially stupid Windows) gives useless addrs that don't work but the real addrs are fine
		// but this could also happen if some other program has the port on one or more addrs, or the port is privileged and we don't have privs, etc.
	for(sockaddrunion& su : local_addrs) {
		su.set_ip_port(htons(snow::conf[snow::DTLS_OUTGOING_PORT]));
		try {
			create_socket(su, dtls_socket::PERSISTENT);
		} catch(const e_check_sock_err &e) {
			eout() << "Could not create create UDP socket: " << e;
		}
		if(su.s.sa_family == AF_INET)
			su.set_ip_port(htons(snow::conf[snow::DTLS_BIND_PORT]));
		else
			su.set_ip_port(htons(snow::conf[snow::DTLS_BIND6_PORT]));
		try {
			create_socket(su, dtls_socket::PERSISTENT);
			local_interface_addrs.push_back(su.get_ip_union());
		} catch(const e_check_sock_err &e) {
			eout() << "Could not create create UDP socket: " << e;
		}
	}
	if(local_addrs.size() > 0) {
		std::vector<ip_info> infos;
		for(auto& addr : local_addrs)
			infos.emplace_back(ip_info(addr));
		dout() << "local node info: " << pinit->local_hashkey().key_string() << "," << node_info(infos, htons(snow::conf[snow::DTLS_OUTGOING_PORT]));
	}
	query_natpmpupnp();
}
Example #21
0
int main(int argc, char** argv) {
    int reqd = 0;
    int min_reqd = 2;
    int arg;
    opterr=0;
	int result;
    sai_t addr;
	char server[1024];
	char port[32];
	char prog[1024];
	char debugconf[1024];
	Handler *new_h;
	AcceptHandler *new_ah;

	init_output_api();

	snprintf(prog,1024, "%s", argv[0]);

	dout(2, "kqueuer is running...\n");

	memset(server,0,1024);
	memset(port,0,32);
	memset(prog,0,1024);
	memset(debugconf,0,1024);

    while ((arg = getopt(argc, argv, "d:s:p:")) != -1) {
        switch (arg) {
        case 's':
            if (optarg[0] == '-') {
                fprintf(stderr, "option -s has bad arg (leading '-')\n");
                usage(prog);
            }
            snprintf(server,1024,"%s",optarg);
            reqd++;
            break;
        case 'p':
            if (optarg[0] == '-') {
                fprintf(stderr, "option -p has bad arg (leading '-')\n");
                usage(prog);
            }

            snprintf(port,32,"%s",optarg);
            reqd++;
            break;
        case 'd':
            if (optarg[0] == '-') {
                fprintf(stderr, "option -d has bad arg (leading '-')\n");
                usage(prog);
            }
            snprintf(debugconf, 1024, "%s", optarg);
            break;
        case 'v':
            version(prog);
            break;
        case 'a':
            about(prog);
            break;
        case 'h':
        default:
            usage(prog);
        };
    };

    argc -= optind;
    argv += optind;

    if (min_reqd > reqd) {
        fprintf(stderr, "Missing arguments.  Try -h for more info.\n");
        exit(1);
    }

	if (debugconf[0] != '\0' && atoi(debugconf) != 0) {
		set_debug_level(atoi(debugconf));
	} else {
		set_debug_level(2); // default
	}

    memset(&addr, 0, sizeof(addr));

    if (inet_pton(AF_INET, server, &addr.sin_addr.s_addr) < 0) {
        eout("kqueuer: failed to get network addr for 192.168.0.71.\n");
        return NULL;
    }

    addr.sin_family = AF_INET;
    addr.sin_port   = htons(atoi(port));

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        eout("kqueuer: failed to get socket - [%d] %s\n", errno,
            strerror(errno));
        return NULL;
    }

	dout(2, "binding...\n");

    if (bind(listenfd, (sa_t*) &addr, sizeof(addr)) < 0) {
        eout("kqueuer: failed to bind - [%d] %s\n", errno,
            strerror(errno));
        return NULL;
    }

	dout(2, "listening...\n");

    if (listen(listenfd, 16) < 0) {
        eout("kqueuer: failed to listen - [%d] %s\n", errno,
            strerror(errno));
        return NULL;
    }

    int on=1;
    socklen_t onsz = sizeof(on);

    if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, onsz)<0)
    {
        eout("kqueuer: setsockopt failed - [%d] %s\n", errno,
            strerror(errno));
        return NULL;
    }

	set_fd_nonblock(listenfd);

	ppid = getpid();

	// fork

	result = rfork(RFPROC|RFNOWAIT|RFFDG);

	if (result == 0) {
		mypid = getpid();
		dout(2, "I'm the child [PID: %d] (parent: %d)\n", mypid, ppid);
	} else {
		mypid = getpid();
		dout(2, "I'm the parent [PID: %d]\n", mypid);
	}

	//
	// Both parent and child do the same thing...
	//

	is_nolisten = 0;

	kmgr = new KQueueManager();

    // Add the listenfd to the kmgr

    dout(2, "[%d]: enqueue listenfd %d\n", mypid, listenfd);

	new_ah = create_accepthandler();
	new_h  = create_handler(listenfd, handle_accept, new_ah);

    if (kmgr->enqueue_kevent(listenfd, EVFILT_READ,
            EV_ADD | EV_ENABLE, new_h) < 0)
    {
        eout("[%d]: failed to enqueue kevent.\n", mypid);
        return NULL;
    }

    // Update the kqueue
    if (kmgr->update_kqueue() < 0) {
        eout("[%d]: failed to update kqueue.\n", mypid);
        return NULL;
    }

    dout(2, "[%d]: entering main processing loop...\n", mypid);

    // Enter main processing loop

    int      nevents;
    kevent_t *cur_kevent;
	Handler  *cur_h;
	StateResult sr;

    while (1) {

        dout(2, "[%d]: waiting (blocked) on kqueue for events...\n", mypid);

        if ((nevents =
            kmgr->get_kevents(KQueueManager::KQUEUE_BLOCK)) < 0)
        {
            eout("[%d]: get_kevents failed.\n", mypid);
            return NULL;
        }

        dout(2, "[%d]: got %d kevents (is_nolisten: %d).\n", mypid, nevents,
			is_nolisten);

        // All events in this kqueue are accept events, so we can handle
        // each one in the exact same way.

        while ((cur_kevent = kmgr->get_next_kevent()) != NULL) {

			dout(2, "[%d]: process event for ident %d\n", mypid,
				cur_kevent->ident);

			kresult = cur_kevent;

			cur_h = (Handler*) cur_kevent->udata;
			sr = (cur_h->handler)(cur_h);
		}

        dout(2, "[%d]: done processing kevents.  process pevents...\n",
			mypid);

		while ((cur_h = (Handler*) kmgr->get_next_pevent()) != NULL) {
			dout(2, "[%d]: process pevent for h%d\n", mypid, cur_h->id);
			
			sr = (cur_h->handler)(cur_h);
		}

        dout(2, "[%d]: done processing pevents.  wait for more...\n",
			mypid);

    	// Update the kqueue
    	if (kmgr->update_kqueue() < 0) {
        	eout("[%d]: failed to update kqueue.\n", mypid);
        	return NULL;
    	}

		// Update the pending queue
		if (kmgr->update_pevents() < 0) {
			eout("[%d]: failed to update pending queue.\n", mypid);
			return NULL;
		}
    }

	dout(2, "[%d]: all done.  goodbye.\n", mypid);

	return NULL;
}