1/*
2 * Special wrappers for NSNetService methods with 'difficult' arguments.
3 *
4 * -addresses				[call]
5 */
6#include <Python.h>
7#include <sys/socket.h>
8#include <netinet/in.h>
9#include <sys/un.h>
10#include <netdb.h>
11
12#include <Foundation/Foundation.h>
13#include "pyobjc-api.h"
14
15
16static PyObject *
17makeipaddr(struct sockaddr *addr, int addrlen)
18{
19	char buf[NI_MAXHOST];
20	int error;
21	PyObject* v;
22
23	error = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0,
24		NI_NUMERICHOST);
25	if (error) {
26		v = Py_BuildValue("(is)", error, gai_strerror(error));
27		PyErr_SetObject(PyExc_RuntimeError, v);
28		Py_DECREF(v);
29		return NULL;
30	}
31	return PyString_FromString(buf);
32}
33
34static PyObject *
35makesockaddr(struct sockaddr *addr, int addrlen)
36{
37	if (addrlen == 0) {
38		/* No address -- may be recvfrom() from known socket */
39		Py_INCREF(Py_None);
40		return Py_None;
41	}
42
43	switch (addr->sa_family) {
44
45	case AF_INET:
46	{
47		struct sockaddr_in *a;
48		PyObject *addrobj = makeipaddr(addr, sizeof(*a));
49		PyObject *ret = NULL;
50		if (addrobj) {
51			a = (struct sockaddr_in *)addr;
52			ret = Py_BuildValue("Oi", addrobj, ntohs(a->sin_port));
53			Py_DECREF(addrobj);
54		}
55		return ret;
56	}
57
58	case AF_UNIX:
59	{
60		struct sockaddr_un *a = (struct sockaddr_un *) addr;
61		return PyString_FromString(a->sun_path);
62	}
63
64	case AF_INET6:
65	{
66		struct sockaddr_in6 *a;
67		PyObject *addrobj = makeipaddr(addr, sizeof(*a));
68		PyObject *ret = NULL;
69		if (addrobj) {
70			a = (struct sockaddr_in6 *)addr;
71			ret = Py_BuildValue("Oiii",
72					    addrobj,
73					    ntohs(a->sin6_port),
74					    a->sin6_flowinfo,
75					    a->sin6_scope_id);
76			Py_DECREF(addrobj);
77		}
78		return ret;
79	}
80
81	/* More cases here... */
82
83	default:
84		/* If we don't know the address family, don't raise an
85		   exception -- return it as a tuple. */
86		return Py_BuildValue("is#",
87				     addr->sa_family,
88				     addr->sa_data,
89				     sizeof(addr->sa_data));
90
91	}
92}
93
94static PyObject* call_NSNetService_addresses(
95	PyObject* method, PyObject* self, PyObject* arguments)
96{
97	PyObject* result;
98	struct objc_super super;
99	NSArray*  res;
100	int len, i;
101	NSData* item;
102
103	if  (!PyArg_ParseTuple(arguments, "")) {
104		return NULL;
105	}
106
107	PyObjC_DURING
108		PyObjC_InitSuper(&super,
109			PyObjCSelector_GetClass(method),
110			PyObjCObject_GetObject(self));
111
112
113		res = objc_msgSendSuper(&super, @selector(addresses));
114	PyObjC_HANDLER
115		PyObjCErr_FromObjC(localException);
116		res = nil;
117	PyObjC_ENDHANDLER
118
119	if (res == nil && PyErr_Occurred()) {
120		return NULL;
121	}
122
123	if (res == nil) {
124		Py_INCREF(Py_None);
125		return Py_None;
126	}
127
128	len = [res count];
129	result = PyTuple_New(len);
130	if (result == NULL) {
131		return NULL;
132	}
133
134	for (i = 0; i < len; i++) {
135
136		PyObject* v;
137
138		item = [res objectAtIndex:i];
139
140		v = makesockaddr((struct sockaddr*)[item bytes], [item length]);
141		if (v == NULL) {
142			Py_DECREF(result);
143			return NULL;
144		}
145		PyTuple_SET_ITEM(result, i, v);
146	}
147
148	return result;
149}
150
151
152static PyMethodDef _methods[] = {
153	{ 0, 0, 0, 0 } /* sentinel */
154};
155
156void
157init_netservice(void)
158{
159	PyObject* m = Py_InitModule4("_netservice", _methods, "", NULL,
160			PYTHON_API_VERSION);
161	if (m == NULL) return;
162	if (PyObjC_ImportAPI(m) < 0) return;
163
164
165	Class classNSNetService = objc_lookUpClass("NSNetService");
166	if (classNSNetService == NULL) {
167		return;
168	}
169
170	if (PyObjC_RegisterMethodMapping(
171		classNSNetService,
172		@selector(addresses),
173		call_NSNetService_addresses,
174		PyObjCUnsupportedMethod_IMP) < 0) {
175
176		return;
177	}
178
179	return;
180}
181