ip.c 3.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
#include "tunala.h"

#ifndef NO_IP

#define IP_LISTENER_BACKLOG 511 /* So if it gets masked by 256 or some other
				   such value it'll still be respectable */

/* Any IP-related initialisations. For now, this means blocking SIGPIPE */
int ip_initialise(void)
{
	struct sigaction sa;

	sa.sa_handler = SIG_IGN;
	sa.sa_flags = 0;
	sigemptyset(&sa.sa_mask);
	if(sigaction(SIGPIPE, &sa, NULL) != 0)
		return 0;
	return 1;
}

int ip_create_listener_split(const char *ip, unsigned short port)
{
	struct sockaddr_in in_addr;
	int fd = -1;
	int reuseVal = 1;

	/* Create the socket */
	if((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
		goto err;
	/* Set the SO_REUSEADDR flag - servers act weird without it */
	if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)(&reuseVal),
				sizeof(reuseVal)) != 0)
		goto err;
	/* Prepare the listen address stuff */
	in_addr.sin_family = AF_INET;
	memcpy(&in_addr.sin_addr.s_addr, ip, 4);
	in_addr.sin_port = htons(port);
	/* Bind to the required port/address/interface */
	if(bind(fd, (struct sockaddr *)&in_addr, sizeof(struct sockaddr_in)) != 0)
		goto err;
	/* Start "listening" */
	if(listen(fd, IP_LISTENER_BACKLOG) != 0)
		goto err;
	return fd;
err:
	if(fd != -1)
		close(fd);
	return -1;
}

int ip_create_connection_split(const char *ip, unsigned short port)
{
	struct sockaddr_in in_addr;
	int flags, fd = -1;

	/* Create the socket */
	if((fd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
		goto err;
	/* Make it non-blocking */
	if(((flags = fcntl(fd, F_GETFL, 0)) < 0) ||
			(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0))
		goto err;
	/* Prepare the connection address stuff */
	in_addr.sin_family = AF_INET;
	memcpy(&in_addr.sin_addr.s_addr, ip, 4);
	in_addr.sin_port = htons(port);
	/* Start a connect (non-blocking, in all likelihood) */
	if((connect(fd, (struct sockaddr *)&in_addr,
			sizeof(struct sockaddr_in)) != 0) &&
			(errno != EINPROGRESS))
		goto err;
	return fd;
err:
	if(fd != -1)
		close(fd);
	return -1;
}

static char all_local_ip[] = {0x00,0x00,0x00,0x00};

int ip_parse_address(const char *address, const char **parsed_ip,
		unsigned short *parsed_port, int accept_all_ip)
{
	char buf[256];
	struct hostent *lookup;
	unsigned long port;
	const char *ptr = strstr(address, ":");
	const char *ip = all_local_ip;

	if(!ptr) {
		/* We assume we're listening on all local interfaces and have
		 * only specified a port. */
		if(!accept_all_ip)
			return 0;
		ptr = address;
		goto determine_port;
	}
	if((ptr - address) > 255)
		return 0;
	memset(buf, 0, 256);
	memcpy(buf, address, ptr - address);
	ptr++;
	if((lookup = gethostbyname(buf)) == NULL) {
		/* Spit a message to differentiate between lookup failures and
		 * bad strings. */
		fprintf(stderr, "hostname lookup for '%s' failed\n", buf);
		return 0;
	}
	ip = lookup->h_addr_list[0];
determine_port:
	if(strlen(ptr) < 1)
		return 0;
	if(!int_strtoul(ptr, &port) || (port > 65535))
		return 0;
	*parsed_ip = ip;
	*parsed_port = (unsigned short)port;
	return 1;
}

int ip_create_listener(const char *address)
{
	const char *ip;
	unsigned short port;

	if(!ip_parse_address(address, &ip, &port, 1))
		return -1;
	return ip_create_listener_split(ip, port);
}

int ip_create_connection(const char *address)
{
	const char *ip;
	unsigned short port;

	if(!ip_parse_address(address, &ip, &port, 0))
		return -1;
	return ip_create_connection_split(ip, port);
}

int ip_accept_connection(int listen_fd)
{
	return accept(listen_fd, NULL, NULL);
}

#endif /* !defined(NO_IP) */