1/*
2 * Converting socketaddresses to/from Python in a way that is compatible with
3 * the socket module.
4 *
5 * This code reimplements parts of the socket module, sadly enough the socket
6 * module doesn't have enough of a public C API to do this otherwise.
7 */
8
9#include "pyobjc.h"
10
11#include <sys/socket.h>
12#include <sys/types.h>
13#include <netinet/in.h>
14#include <netdb.h>
15
16static PyObject* socket_error = NULL;
17static PyObject* socket_gaierror = NULL;
18
19static int
20setup_exceptions(void)
21{
22	PyObject* mod;
23
24	mod = PyImport_ImportModule("socket");
25	if (mod == NULL) {
26		return -1;
27	}
28
29	Py_XDECREF(socket_error);
30	socket_error = PyObject_GetAttrString(mod, "error");
31	if (socket_error == NULL) {
32		Py_DECREF(mod);
33		return -1;
34	}
35
36	Py_XDECREF(socket_gaierror);
37	socket_error = PyObject_GetAttrString(mod, "gaierror");
38	if (socket_gaierror == NULL) {
39		Py_DECREF(mod);
40		return -1;
41	}
42
43	Py_DECREF(mod);
44	return 0;
45}
46
47static PyObject*
48set_gaierror(int error)
49{
50	if (error == EAI_SYSTEM) {
51		if (socket_error == NULL) {
52			if (setup_exceptions() == -1) {
53				return NULL;
54			}
55		}
56		PyErr_SetFromErrno(socket_error);
57	}
58
59	PyObject* v = Py_BuildValue("is", error, gai_strerror(error));
60	if (v != NULL) {
61		if (socket_gaierror == NULL) {
62			if (setup_exceptions() == -1) {
63				return NULL;
64			}
65		}
66		PyErr_SetObject(socket_gaierror, v);
67		Py_DECREF(v);
68	}
69	return NULL;
70}
71
72static PyObject*
73makeipaddr(struct sockaddr* addr, int addrlen)
74{
75	char buf[NI_MAXHOST];
76	int r;
77
78	r = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0,
79			NI_NUMERICHOST);
80	if (r) {
81		return set_gaierror(r);
82	}
83	return PyString_FromString(buf);
84}
85
86static int
87setipaddr(char* name, struct sockaddr* addr_ret, size_t addr_ret_size, int af)
88{
89	struct addrinfo hints, *res;
90	int error;
91	int d1, d2, d3, d4;
92	char ch;
93
94	memset((void *) addr_ret, '\0', sizeof(*addr_ret));
95	if (name[0] == '\0') {
96		int siz;
97		memset(&hints, 0, sizeof(hints));
98		hints.ai_family = af;
99		hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
100		hints.ai_flags = AI_PASSIVE;
101		error = getaddrinfo(NULL, "0", &hints, &res);
102		/* We assume that those thread-unsafe getaddrinfo() versions
103		   *are* safe regarding their return value, ie. that a
104		   subsequent call to getaddrinfo() does not destroy the
105		   outcome of the first call. */
106		if (error) {
107			set_gaierror(error);
108			return -1;
109		}
110		switch (res->ai_family) {
111		case AF_INET:
112			siz = 4;
113			break;
114		case AF_INET6:
115			siz = 16;
116			break;
117		default:
118			freeaddrinfo(res);
119			PyErr_SetString(socket_error,
120				"unsupported address family");
121			return -1;
122		}
123		if (res->ai_next) {
124			freeaddrinfo(res);
125			PyErr_SetString(socket_error,
126				"wildcard resolved to multiple address");
127			return -1;
128		}
129		if (res->ai_addrlen < addr_ret_size)
130			addr_ret_size = res->ai_addrlen;
131		memcpy(addr_ret, res->ai_addr, addr_ret_size);
132		freeaddrinfo(res);
133		return siz;
134	}
135	if (name[0] == '<' && strcmp(name, "<broadcast>") == 0) {
136		struct sockaddr_in *sinaddr;
137		if (af != AF_INET && af != AF_UNSPEC) {
138			PyErr_SetString(socket_error,
139				"address family mismatched");
140			return -1;
141		}
142		sinaddr = (struct sockaddr_in *)addr_ret;
143		memset((void *) sinaddr, '\0', sizeof(*sinaddr));
144		sinaddr->sin_family = AF_INET;
145		sinaddr->sin_len = sizeof(*sinaddr);
146		sinaddr->sin_addr.s_addr = INADDR_BROADCAST;
147		return sizeof(sinaddr->sin_addr);
148	}
149	if (sscanf(name, "%d.%d.%d.%d%c", &d1, &d2, &d3, &d4, &ch) == 4 &&
150	    0 <= d1 && d1 <= 255 && 0 <= d2 && d2 <= 255 &&
151	    0 <= d3 && d3 <= 255 && 0 <= d4 && d4 <= 255) {
152		struct sockaddr_in *sinaddr;
153		sinaddr = (struct sockaddr_in *)addr_ret;
154		sinaddr->sin_addr.s_addr = htonl(
155			((long) d1 << 24) | ((long) d2 << 16) |
156			((long) d3 << 8) | ((long) d4 << 0));
157		sinaddr->sin_family = AF_INET;
158		sinaddr->sin_len = sizeof(*sinaddr);
159		return 4;
160	}
161	memset(&hints, 0, sizeof(hints));
162	hints.ai_family = af;
163	error = getaddrinfo(name, NULL, &hints, &res);
164	if (error) {
165		set_gaierror(error);
166		return -1;
167	}
168	if (res->ai_addrlen < addr_ret_size)
169		addr_ret_size = res->ai_addrlen;
170	memcpy((char *) addr_ret, res->ai_addr, addr_ret_size);
171	freeaddrinfo(res);
172	switch (addr_ret->sa_family) {
173	case AF_INET:
174		return 4;
175	case AF_INET6:
176		return 16;
177	default:
178		PyErr_SetString(socket_error, "unknown address family");
179		return -1;
180	}
181}
182
183PyObject*
184PyObjC_SockAddrToPython(void* value)
185{
186	switch (((struct sockaddr*)value)->sa_family) {
187	case AF_INET:
188		{
189			struct sockaddr_in* a = (struct sockaddr_in*)value;
190			PyObject* addrobj = makeipaddr((struct sockaddr*)a, sizeof(*a));
191			if (addrobj != NULL) {
192				return Py_BuildValue("Ni", addrobj,
193						ntohs(a->sin_port));
194			}
195			return NULL;
196		}
197
198	case AF_INET6:
199		{
200			struct sockaddr_in6* a = (struct sockaddr_in6*)value;
201			PyObject* addrobj = makeipaddr((struct sockaddr*)a, sizeof(*a));
202			if (addrobj != NULL) {
203				return Py_BuildValue("Niii", addrobj,
204						ntohs(a->sin6_port),
205						a->sin6_flowinfo,
206						a->sin6_scope_id);
207			}
208			return NULL;
209		}
210
211	default:
212		PyErr_Format(PyExc_ValueError,
213			"Don't know how to convert sockaddr family %d",
214			((struct sockaddr*)value)->sa_family
215		);
216		return NULL;
217	}
218}
219
220int
221PyObjC_SockAddrFromPython(PyObject* value, void* buffer)
222{
223	if (PyTuple_Size(value) == 2) {
224		/* IPv4 address */
225		struct sockaddr_in* addr = (struct sockaddr_in*)buffer;
226		char* host;
227		int port, result;
228
229		if (!PyArg_ParseTuple(value, "eti:getsockaddrarg",
230				"idna", &host, &port)) {
231			return -1;
232		}
233		result = setipaddr(host, (struct sockaddr*)addr,
234				sizeof(*addr), AF_INET);
235		PyMem_Free(host);
236		if (result < 0) {
237			return -1;
238		}
239		addr->sin_family = AF_INET;
240		addr->sin_port = htons((short)port);
241		return 0;
242
243	} else {
244		/* Must be a IPv6 address */
245		struct sockaddr_in6* addr = (struct sockaddr_in6*)buffer;
246		char* host;
247		int port, flowinfo, scope_id, result;
248
249		flowinfo = scope_id = 0;
250		if (!PyArg_ParseTuple(value, "eti|ii",
251			"idna", &host, &port, &flowinfo, &scope_id))  {
252
253			return -1;
254		}
255		result = setipaddr(host, (struct sockaddr*)addr,
256				sizeof(*addr), AF_INET6);
257		PyMem_Free(host);
258		if (result < 0) {
259			return -1;
260		}
261		addr->sin6_family = AF_INET6;
262		addr->sin6_port = htons((short)port);
263		addr->sin6_flowinfo = flowinfo;
264		addr->sin6_scope_id = scope_id;
265		return 0;
266	}
267}
268