summaryrefslogtreecommitdiffstats
path: root/demos/tunala/ip.c
blob: 96ef4e65360682d64553c70b2ca01e9b0c10f23e (plain)
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) */