1 /* $Id: miniupnpcmodule.c,v 1.34 2019/05/20 19:07:16 nanard Exp $*/
2 /* vim: tabstop=4 shiftwidth=4 noexpandtab
3  * Project : miniupnp
4  * Author : Thomas BERNARD
5  * website : https://miniupnp.tuxfamily.org/
6  * copyright (c) 2007-2019 Thomas Bernard
7  * This software is subjet to the conditions detailed in the
8  * provided LICENCE file. */
9 #include <Python.h>
10 #define MINIUPNP_STATICLIB
11 #include "structmember.h"
12 #include "miniupnpc.h"
13 #include "upnpcommands.h"
14 #include "upnperrors.h"
15 
16 #ifdef _WIN32
17 #include <winsock2.h>
18 #endif
19 
20 /* for compatibility with Python < 2.4 */
21 #ifndef Py_RETURN_NONE
22 #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
23 #endif
24 
25 #ifndef Py_RETURN_TRUE
26 #define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
27 #endif
28 
29 #ifndef Py_RETURN_FALSE
30 #define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
31 #endif
32 
33 /* for compatibility with Python < 3.0 */
34 #ifndef PyVarObject_HEAD_INIT
35 #define PyVarObject_HEAD_INIT(type, size) \
36     PyObject_HEAD_INIT(type) size,
37 #endif
38 
39 #ifndef Py_TYPE
40 #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
41 #endif
42 
43 typedef struct {
44     PyObject_HEAD
45     /* Type-specific fields go here. */
46 	struct UPNPDev * devlist;
47 	struct UPNPUrls urls;
48 	struct IGDdatas data;
49 	unsigned int discoverdelay;	/* value passed to upnpDiscover() */
50 	unsigned int localport;		/* value passed to upnpDiscover() */
51 	char lanaddr[40];	/* our ip address on the LAN */
52 	char * multicastif;
53 	char * minissdpdsocket;
54 } UPnPObject;
55 
56 static PyMemberDef UPnP_members[] = {
57 	{"lanaddr", T_STRING_INPLACE, offsetof(UPnPObject, lanaddr),
58 	 READONLY, "ip address on the LAN"
59 	},
60 	{"discoverdelay", T_UINT, offsetof(UPnPObject, discoverdelay),
61 	 0/*READWRITE*/, "value in ms used to wait for SSDP responses"
62 	},
63 	{"localport", T_UINT, offsetof(UPnPObject, localport),
64 	 0/*READWRITE*/,
65 	    "If localport is set to UPNP_LOCAL_PORT_SAME(1) "
66 	    "SSDP packets will be sent from the source port "
67 	    "1900 (same as destination port), if set to "
68 	    "UPNP_LOCAL_PORT_ANY(0) system assign a source "
69 	    "port, any other value will be attempted as the "
70 	    "source port"
71 	},
72 	/* T_STRING is allways readonly :( */
73 	{"multicastif", T_STRING, offsetof(UPnPObject, multicastif),
74 	 0, "IP of the network interface to be used for multicast operations"
75 	},
76 	{"minissdpdsocket", T_STRING, offsetof(UPnPObject, minissdpdsocket),
77 	 0, "path of the MiniSSDPd unix socket"
78 	},
79 	{NULL}
80 };
81 
82 
UPnP_init(UPnPObject * self,PyObject * args,PyObject * kwds)83 static int UPnP_init(UPnPObject *self, PyObject *args, PyObject *kwds)
84 {
85 	char* multicastif = NULL;
86 	char* minissdpdsocket = NULL;
87 	static char *kwlist[] = {
88 		"multicastif", "minissdpdsocket", "discoverdelay",
89 		"localport", NULL
90 	};
91 
92 	if(!PyArg_ParseTupleAndKeywords(args, kwds, "|zzII", kwlist,
93 					&multicastif,
94 					&minissdpdsocket,
95 					&self->discoverdelay,
96 					&self->localport))
97 		return -1;
98 
99 	if(self->localport>1 &&
100 	   (self->localport>65534||self->localport<1024)) {
101 	    PyErr_SetString(PyExc_Exception, "Invalid localport value");
102 	    return -1;
103 	}
104 	if(multicastif)
105 		self->multicastif = strdup(multicastif);
106 	if(minissdpdsocket)
107 		self->minissdpdsocket = strdup(minissdpdsocket);
108 
109 	return 0;
110 }
111 
112 static void
UPnPObject_dealloc(UPnPObject * self)113 UPnPObject_dealloc(UPnPObject *self)
114 {
115 	freeUPNPDevlist(self->devlist);
116 	FreeUPNPUrls(&self->urls);
117 	free(self->multicastif);
118 	free(self->minissdpdsocket);
119 	Py_TYPE(self)->tp_free((PyObject*)self);
120 }
121 
122 static PyObject *
UPnP_discover(UPnPObject * self)123 UPnP_discover(UPnPObject *self)
124 {
125 	struct UPNPDev * dev;
126 	int i;
127 	PyObject *res = NULL;
128 	if(self->devlist)
129 	{
130 		freeUPNPDevlist(self->devlist);
131 		self->devlist = 0;
132 	}
133 	Py_BEGIN_ALLOW_THREADS
134 	self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/,
135 	                             self->multicastif,
136 	                             self->minissdpdsocket,
137 	                             (int)self->localport,
138 	                             0/*ip v6*/,
139 	                             2/* TTL */,
140 	                             0/*error */);
141 	Py_END_ALLOW_THREADS
142 	/* Py_RETURN_NONE ??? */
143 	for(dev = self->devlist, i = 0; dev; dev = dev->pNext)
144 		i++;
145 	res = Py_BuildValue("i", i);
146 	return res;
147 }
148 
149 static PyObject *
UPnP_selectigd(UPnPObject * self)150 UPnP_selectigd(UPnPObject *self)
151 {
152 	int r;
153 Py_BEGIN_ALLOW_THREADS
154 	r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data,
155 	                     self->lanaddr, sizeof(self->lanaddr));
156 Py_END_ALLOW_THREADS
157 	if(r)
158 	{
159 		return Py_BuildValue("s", self->urls.controlURL);
160 	}
161 	else
162 	{
163 		/* TODO: have our own exception type ! */
164 		PyErr_SetString(PyExc_Exception, "No UPnP device discovered");
165 		return NULL;
166 	}
167 }
168 
169 static PyObject *
UPnP_totalbytesent(UPnPObject * self)170 UPnP_totalbytesent(UPnPObject *self)
171 {
172 	UNSIGNED_INTEGER i;
173 Py_BEGIN_ALLOW_THREADS
174 	i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF,
175 	                           self->data.CIF.servicetype);
176 Py_END_ALLOW_THREADS
177 #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
178 	return Py_BuildValue("I", i);
179 #else
180 	return Py_BuildValue("i", (int)i);
181 #endif
182 }
183 
184 static PyObject *
UPnP_totalbytereceived(UPnPObject * self)185 UPnP_totalbytereceived(UPnPObject *self)
186 {
187 	UNSIGNED_INTEGER i;
188 Py_BEGIN_ALLOW_THREADS
189 	i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF,
190 		                           self->data.CIF.servicetype);
191 Py_END_ALLOW_THREADS
192 #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
193 	return Py_BuildValue("I", i);
194 #else
195 	return Py_BuildValue("i", (int)i);
196 #endif
197 }
198 
199 static PyObject *
UPnP_totalpacketsent(UPnPObject * self)200 UPnP_totalpacketsent(UPnPObject *self)
201 {
202 	UNSIGNED_INTEGER i;
203 Py_BEGIN_ALLOW_THREADS
204 	i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF,
205 		                         self->data.CIF.servicetype);
206 Py_END_ALLOW_THREADS
207 #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
208 	return Py_BuildValue("I", i);
209 #else
210 	return Py_BuildValue("i", (int)i);
211 #endif
212 }
213 
214 static PyObject *
UPnP_totalpacketreceived(UPnPObject * self)215 UPnP_totalpacketreceived(UPnPObject *self)
216 {
217 	UNSIGNED_INTEGER i;
218 Py_BEGIN_ALLOW_THREADS
219 	i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF,
220 		                          self->data.CIF.servicetype);
221 Py_END_ALLOW_THREADS
222 #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
223 	return Py_BuildValue("I", i);
224 #else
225 	return Py_BuildValue("i", (int)i);
226 #endif
227 }
228 
229 static PyObject *
UPnP_statusinfo(UPnPObject * self)230 UPnP_statusinfo(UPnPObject *self)
231 {
232 	char status[64];
233 	char lastconnerror[64];
234 	unsigned int uptime = 0;
235 	int r;
236 	status[0] = '\0';
237 	lastconnerror[0] = '\0';
238 Py_BEGIN_ALLOW_THREADS
239 	r = UPNP_GetStatusInfo(self->urls.controlURL, self->data.first.servicetype,
240 	                   status, &uptime, lastconnerror);
241 Py_END_ALLOW_THREADS
242 	if(r==UPNPCOMMAND_SUCCESS) {
243 #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
244 		return Py_BuildValue("(s,I,s)", status, uptime, lastconnerror);
245 #else
246 		return Py_BuildValue("(s,i,s)", status, (int)uptime, lastconnerror);
247 #endif
248 	} else {
249 		/* TODO: have our own exception type ! */
250 		PyErr_SetString(PyExc_Exception, strupnperror(r));
251 		return NULL;
252 	}
253 }
254 
255 static PyObject *
UPnP_connectiontype(UPnPObject * self)256 UPnP_connectiontype(UPnPObject *self)
257 {
258 	char connectionType[64];
259 	int r;
260 	connectionType[0] = '\0';
261 Py_BEGIN_ALLOW_THREADS
262 	r = UPNP_GetConnectionTypeInfo(self->urls.controlURL,
263 	                               self->data.first.servicetype,
264 	                               connectionType);
265 Py_END_ALLOW_THREADS
266 	if(r==UPNPCOMMAND_SUCCESS) {
267 		return Py_BuildValue("s", connectionType);
268 	} else {
269 		/* TODO: have our own exception type ! */
270 		PyErr_SetString(PyExc_Exception, strupnperror(r));
271 		return NULL;
272 	}
273 }
274 
275 static PyObject *
UPnP_externalipaddress(UPnPObject * self)276 UPnP_externalipaddress(UPnPObject *self)
277 {
278 	char externalIPAddress[40];
279 	int r;
280 	externalIPAddress[0] = '\0';
281 Py_BEGIN_ALLOW_THREADS
282 	r = UPNP_GetExternalIPAddress(self->urls.controlURL,
283 	                              self->data.first.servicetype,
284 	                              externalIPAddress);
285 Py_END_ALLOW_THREADS
286 	if(r==UPNPCOMMAND_SUCCESS) {
287 		return Py_BuildValue("s", externalIPAddress);
288 	} else {
289 		/* TODO: have our own exception type ! */
290 		PyErr_SetString(PyExc_Exception, strupnperror(r));
291 		return NULL;
292 	}
293 }
294 
295 /* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc,
296  *                remoteHost, leaseDuration)
297  * protocol is 'UDP' or 'TCP' */
298 static PyObject *
UPnP_addportmapping(UPnPObject * self,PyObject * args)299 UPnP_addportmapping(UPnPObject *self, PyObject *args)
300 {
301 	char extPort[6];
302 	unsigned short ePort;
303 	char inPort[6];
304 	unsigned short iPort;
305 	const char * proto;
306 	const char * host;
307 	const char * desc;
308 	const char * remoteHost;
309 	unsigned int intLeaseDuration = 0;
310 	char strLeaseDuration[12];
311 	int r;
312 #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
313 	if (!PyArg_ParseTuple(args, "HssHzz|I", &ePort, &proto,
314 	                                     &host, &iPort, &desc, &remoteHost, &intLeaseDuration))
315 #else
316 	if (!PyArg_ParseTuple(args, "HssHzz|i", &ePort, &proto,
317 	                                     &host, &iPort, &desc, &remoteHost, (int *)&intLeaseDuration))
318 #endif
319         return NULL;
320 Py_BEGIN_ALLOW_THREADS
321 	sprintf(extPort, "%hu", ePort);
322 	sprintf(inPort, "%hu", iPort);
323 	sprintf(strLeaseDuration, "%u", intLeaseDuration);
324 	r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype,
325 	                        extPort, inPort, host, desc, proto,
326 	                        remoteHost, strLeaseDuration);
327 Py_END_ALLOW_THREADS
328 	if(r==UPNPCOMMAND_SUCCESS)
329 	{
330 		Py_RETURN_TRUE;
331 	}
332 	else
333 	{
334 		// TODO: RAISE an Exception. See upnpcommands.h for errors codes.
335 		// upnperrors.c
336 		//Py_RETURN_FALSE;
337 		/* TODO: have our own exception type ! */
338 		PyErr_SetString(PyExc_Exception, strupnperror(r));
339 		return NULL;
340 	}
341 }
342 
343 /* AddAnyPortMapping(externalPort, protocol, internalHost, internalPort, desc,
344  *                   remoteHost)
345  * protocol is 'UDP' or 'TCP' */
346 static PyObject *
UPnP_addanyportmapping(UPnPObject * self,PyObject * args)347 UPnP_addanyportmapping(UPnPObject *self, PyObject *args)
348 {
349 	char extPort[6];
350 	unsigned short ePort;
351 	char inPort[6];
352 	unsigned short iPort;
353 	char reservedPort[6];
354 	const char * proto;
355 	const char * host;
356 	const char * desc;
357 	const char * remoteHost;
358 	const char * leaseDuration = "0";
359 	int r;
360 	if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto, &host, &iPort, &desc, &remoteHost))
361         return NULL;
362 Py_BEGIN_ALLOW_THREADS
363 	sprintf(extPort, "%hu", ePort);
364 	sprintf(inPort, "%hu", iPort);
365 	r = UPNP_AddAnyPortMapping(self->urls.controlURL, self->data.first.servicetype,
366 	                           extPort, inPort, host, desc, proto,
367 	                           remoteHost, leaseDuration, reservedPort);
368 Py_END_ALLOW_THREADS
369 	if(r==UPNPCOMMAND_SUCCESS) {
370 		return Py_BuildValue("i", atoi(reservedPort));
371 	} else {
372 		/* TODO: have our own exception type ! */
373 		PyErr_SetString(PyExc_Exception, strupnperror(r));
374 		return NULL;
375 	}
376 }
377 
378 
379 /* DeletePortMapping(extPort, proto, removeHost='')
380  * proto = 'UDP', 'TCP' */
381 static PyObject *
UPnP_deleteportmapping(UPnPObject * self,PyObject * args)382 UPnP_deleteportmapping(UPnPObject *self, PyObject *args)
383 {
384 	char extPort[6];
385 	unsigned short ePort;
386 	const char * proto;
387 	const char * remoteHost = "";
388 	int r;
389 	if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
390 		return NULL;
391 Py_BEGIN_ALLOW_THREADS
392 	sprintf(extPort, "%hu", ePort);
393 	r = UPNP_DeletePortMapping(self->urls.controlURL, self->data.first.servicetype,
394 	                           extPort, proto, remoteHost);
395 Py_END_ALLOW_THREADS
396 	if(r==UPNPCOMMAND_SUCCESS) {
397 		Py_RETURN_TRUE;
398 	} else {
399 		/* TODO: have our own exception type ! */
400 		PyErr_SetString(PyExc_Exception, strupnperror(r));
401 		return NULL;
402 	}
403 }
404 
405 /* DeletePortMappingRange(extPort, proto, removeHost='')
406  * proto = 'UDP', 'TCP' */
407 static PyObject *
UPnP_deleteportmappingrange(UPnPObject * self,PyObject * args)408 UPnP_deleteportmappingrange(UPnPObject *self, PyObject *args)
409 {
410 	char extPortStart[6];
411 	unsigned short ePortStart;
412 	char extPortEnd[6];
413 	unsigned short ePortEnd;
414 	const char * proto;
415 	unsigned char manage;
416 	char manageStr[6];
417 	int r;
418 	if(!PyArg_ParseTuple(args, "HHsb", &ePortStart, &ePortEnd, &proto, &manage))
419 		return NULL;
420 Py_BEGIN_ALLOW_THREADS
421 	sprintf(extPortStart, "%hu", ePortStart);
422 	sprintf(extPortEnd, "%hu", ePortEnd);
423 	sprintf(manageStr, "%hu", (unsigned short)manage);
424 	r = UPNP_DeletePortMappingRange(self->urls.controlURL, self->data.first.servicetype,
425 					extPortStart, extPortEnd, proto, manageStr);
426 Py_END_ALLOW_THREADS
427 	if(r==UPNPCOMMAND_SUCCESS) {
428 		Py_RETURN_TRUE;
429 	} else {
430 		/* TODO: have our own exception type ! */
431 		PyErr_SetString(PyExc_Exception, strupnperror(r));
432 		return NULL;
433 	}
434 }
435 
436 static PyObject *
UPnP_getportmappingnumberofentries(UPnPObject * self)437 UPnP_getportmappingnumberofentries(UPnPObject *self)
438 {
439 	unsigned int n = 0;
440 	int r;
441 Py_BEGIN_ALLOW_THREADS
442 	r = UPNP_GetPortMappingNumberOfEntries(self->urls.controlURL,
443 	                                   self->data.first.servicetype,
444 									   &n);
445 Py_END_ALLOW_THREADS
446 	if(r==UPNPCOMMAND_SUCCESS) {
447 #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
448 		return Py_BuildValue("I", n);
449 #else
450 		return Py_BuildValue("i", (int)n);
451 #endif
452 	} else {
453 		/* TODO: have our own exception type ! */
454 		PyErr_SetString(PyExc_Exception, strupnperror(r));
455 		return NULL;
456 	}
457 }
458 
459 /* GetSpecificPortMapping(ePort, proto, remoteHost='')
460  * proto = 'UDP' or 'TCP' */
461 static PyObject *
UPnP_getspecificportmapping(UPnPObject * self,PyObject * args)462 UPnP_getspecificportmapping(UPnPObject *self, PyObject *args)
463 {
464 	char extPort[6];
465 	unsigned short ePort;
466 	const char * proto;
467 	const char * remoteHost = "";
468 	char intClient[40];
469 	char intPort[6];
470 	unsigned short iPort;
471 	char desc[80];
472 	char enabled[4];
473 	char leaseDuration[16];
474 	if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
475 		return NULL;
476 	extPort[0] = '\0'; intClient[0] = '\0'; intPort[0] = '\0';
477 	desc[0] = '\0'; enabled[0] = '\0'; leaseDuration[0] = '\0';
478 Py_BEGIN_ALLOW_THREADS
479 	sprintf(extPort, "%hu", ePort);
480 	UPNP_GetSpecificPortMappingEntry(self->urls.controlURL,
481 	                                 self->data.first.servicetype,
482 									 extPort, proto, remoteHost,
483 									 intClient, intPort,
484 	                                 desc, enabled, leaseDuration);
485 Py_END_ALLOW_THREADS
486 	if(intClient[0])
487 	{
488 		iPort = (unsigned short)atoi(intPort);
489 		return Py_BuildValue("(s,H,s,O,i)",
490 		                     intClient, iPort, desc,
491 		                     PyBool_FromLong(atoi(enabled)),
492 		                     atoi(leaseDuration));
493 	}
494 	else
495 	{
496 		Py_RETURN_NONE;
497 	}
498 }
499 
500 /* GetGenericPortMapping(index) */
501 static PyObject *
UPnP_getgenericportmapping(UPnPObject * self,PyObject * args)502 UPnP_getgenericportmapping(UPnPObject *self, PyObject *args)
503 {
504 	int i, r;
505 	char index[8];
506 	char intClient[40];
507 	char intPort[6];
508 	unsigned short iPort;
509 	char extPort[6];
510 	unsigned short ePort;
511 	char protocol[4];
512 	char desc[80];
513 	char enabled[6];
514 	char rHost[64];
515 	char duration[16];	/* lease duration */
516 	unsigned int dur;
517 	if(!PyArg_ParseTuple(args, "i", &i))
518 		return NULL;
519 Py_BEGIN_ALLOW_THREADS
520 	snprintf(index, sizeof(index), "%d", i);
521 	rHost[0] = '\0'; enabled[0] = '\0';
522 	duration[0] = '\0'; desc[0] = '\0';
523 	extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
524 	r = UPNP_GetGenericPortMappingEntry(self->urls.controlURL,
525 	                                    self->data.first.servicetype,
526 										index,
527 										extPort, intClient, intPort,
528 										protocol, desc, enabled, rHost,
529 										duration);
530 Py_END_ALLOW_THREADS
531 	if(r==UPNPCOMMAND_SUCCESS)
532 	{
533 		ePort = (unsigned short)atoi(extPort);
534 		iPort = (unsigned short)atoi(intPort);
535 		dur = (unsigned int)strtoul(duration, 0, 0);
536 #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
537 		return Py_BuildValue("(H,s,(s,H),s,s,s,I)",
538 		                     ePort, protocol, intClient, iPort,
539 		                     desc, enabled, rHost, dur);
540 #else
541 		return Py_BuildValue("(i,s,(s,i),s,s,s,i)",
542 		                     (int)ePort, protocol, intClient, (int)iPort,
543 		                     desc, enabled, rHost, (int)dur);
544 #endif
545 	}
546 	else
547 	{
548 		Py_RETURN_NONE;
549 	}
550 }
551 
552 /* miniupnpc.UPnP object Method Table */
553 static PyMethodDef UPnP_methods[] = {
554     {"discover", (PyCFunction)UPnP_discover, METH_NOARGS,
555      "discover UPnP IGD devices on the network"
556     },
557 	{"selectigd", (PyCFunction)UPnP_selectigd, METH_NOARGS,
558 	 "select a valid UPnP IGD among discovered devices"
559 	},
560 	{"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS,
561 	 "return the total number of bytes sent by UPnP IGD"
562 	},
563 	{"totalbytereceived", (PyCFunction)UPnP_totalbytereceived, METH_NOARGS,
564 	 "return the total number of bytes received by UPnP IGD"
565 	},
566 	{"totalpacketsent", (PyCFunction)UPnP_totalpacketsent, METH_NOARGS,
567 	 "return the total number of packets sent by UPnP IGD"
568 	},
569 	{"totalpacketreceived", (PyCFunction)UPnP_totalpacketreceived, METH_NOARGS,
570 	 "return the total number of packets received by UPnP IGD"
571 	},
572 	{"statusinfo", (PyCFunction)UPnP_statusinfo, METH_NOARGS,
573 	 "return status and uptime"
574 	},
575 	{"connectiontype", (PyCFunction)UPnP_connectiontype, METH_NOARGS,
576 	 "return IGD WAN connection type"
577 	},
578 	{"externalipaddress", (PyCFunction)UPnP_externalipaddress, METH_NOARGS,
579 	 "return external IP address"
580 	},
581 	{"addportmapping", (PyCFunction)UPnP_addportmapping, METH_VARARGS,
582 	 "add a port mapping"
583 	},
584 	{"addanyportmapping", (PyCFunction)UPnP_addanyportmapping, METH_VARARGS,
585 	 "add a port mapping, IGD to select alternative if necessary"
586 	},
587 	{"deleteportmapping", (PyCFunction)UPnP_deleteportmapping, METH_VARARGS,
588 	 "delete a port mapping"
589 	},
590 	{"deleteportmappingrange", (PyCFunction)UPnP_deleteportmappingrange, METH_VARARGS,
591 	 "delete a range of port mappings"
592 	},
593 	{"getportmappingnumberofentries", (PyCFunction)UPnP_getportmappingnumberofentries, METH_NOARGS,
594 	 "-- non standard --"
595 	},
596 	{"getspecificportmapping", (PyCFunction)UPnP_getspecificportmapping, METH_VARARGS,
597 	 "get details about a specific port mapping entry"
598 	},
599 	{"getgenericportmapping", (PyCFunction)UPnP_getgenericportmapping, METH_VARARGS,
600 	 "get all details about the port mapping at index"
601 	},
602     {NULL}  /* Sentinel */
603 };
604 
605 static PyTypeObject UPnPType = {
606     PyVarObject_HEAD_INIT(NULL,
607     0)                         /*ob_size*/
608     "miniupnpc.UPnP",          /*tp_name*/
609     sizeof(UPnPObject),        /*tp_basicsize*/
610     0,                         /*tp_itemsize*/
611     (destructor)UPnPObject_dealloc,/*tp_dealloc*/
612     0,                         /*tp_print*/
613     0,                         /*tp_getattr*/
614     0,                         /*tp_setattr*/
615     0,                         /*tp_compare*/
616     0,                         /*tp_repr*/
617     0,                         /*tp_as_number*/
618     0,                         /*tp_as_sequence*/
619     0,                         /*tp_as_mapping*/
620     0,                         /*tp_hash */
621     0,                         /*tp_call*/
622     0,                         /*tp_str*/
623     0,                         /*tp_getattro*/
624     0,                         /*tp_setattro*/
625     0,                         /*tp_as_buffer*/
626     Py_TPFLAGS_DEFAULT,        /*tp_flags*/
627     "UPnP objects",            /* tp_doc */
628     0,		                   /* tp_traverse */
629     0,		                   /* tp_clear */
630     0,		                   /* tp_richcompare */
631     0,		                   /* tp_weaklistoffset */
632     0,		                   /* tp_iter */
633     0,		                   /* tp_iternext */
634     UPnP_methods,              /* tp_methods */
635     UPnP_members,              /* tp_members */
636     0,                         /* tp_getset */
637     0,                         /* tp_base */
638     0,                         /* tp_dict */
639     0,                         /* tp_descr_get */
640     0,                         /* tp_descr_set */
641     0,                         /* tp_dictoffset */
642     (initproc)UPnP_init,       /* tp_init */
643     0,                         /* tp_alloc */
644 #ifndef _WIN32
645     PyType_GenericNew,/*UPnP_new,*/      /* tp_new */
646 #else
647     0,
648 #endif
649 };
650 
651 /* module methods */
652 static PyMethodDef miniupnpc_methods[] = {
653     {NULL}  /* Sentinel */
654 };
655 
656 #if PY_MAJOR_VERSION >= 3
657 static struct PyModuleDef moduledef = {
658     PyModuleDef_HEAD_INIT,
659     "miniupnpc",     /* m_name */
660     "miniupnpc module.",  /* m_doc */
661     -1,                  /* m_size */
662     miniupnpc_methods,    /* m_methods */
663     NULL,                /* m_reload */
664     NULL,                /* m_traverse */
665     NULL,                /* m_clear */
666     NULL,                /* m_free */
667 };
668 #endif
669 
670 #ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
671 #define PyMODINIT_FUNC void
672 #endif
673 
674 PyMODINIT_FUNC
675 #if PY_MAJOR_VERSION >= 3
PyInit_miniupnpc(void)676 PyInit_miniupnpc(void)
677 #else
678 initminiupnpc(void)
679 #endif
680 {
681     PyObject* m;
682 
683 #ifdef _WIN32
684     /* initialize Winsock. */
685     WSADATA wsaData;
686     int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
687 	if (nResult != 0)
688 	{
689 		/* error code could be WSASYSNOTREADY WSASYSNOTREADY
690 		 * WSASYSNOTREADY WSASYSNOTREADY WSASYSNOTREADY */
691 #if PY_MAJOR_VERSION >= 3
692         return 0;
693 #else
694         return;
695 #endif
696 	}
697 
698     UPnPType.tp_new = PyType_GenericNew;
699 #endif
700     if (PyType_Ready(&UPnPType) < 0)
701 #if PY_MAJOR_VERSION >= 3
702         return 0;
703 #else
704         return;
705 #endif
706 
707 #if PY_MAJOR_VERSION >= 3
708     m = PyModule_Create(&moduledef);
709 #else
710     m = Py_InitModule3("miniupnpc", miniupnpc_methods,
711                        "miniupnpc module.");
712 #endif
713 
714     Py_INCREF(&UPnPType);
715     PyModule_AddObject(m, "UPnP", (PyObject *)&UPnPType);
716 
717 #if PY_MAJOR_VERSION >= 3
718     return m;
719 #endif
720 }
721 
722