1
/* $Id: miniupnpcmodule.c,v 1.21 2012/08/29 07:51:30 nanard Exp $*/
3
* Author : Thomas BERNARD
4
* website : http://miniupnp.tuxfamily.org/
5
* copyright (c) 2007-2012 Thomas Bernard
6
* This software is subjet to the conditions detailed in the
7
* provided LICENCE file. */
10
#include "structmember.h"
11
#include "miniupnpc.h"
12
#include "upnpcommands.h"
13
#include "upnperrors.h"
15
/* for compatibility with Python < 2.4 */
16
#ifndef Py_RETURN_NONE
17
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
20
#ifndef Py_RETURN_TRUE
21
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
24
#ifndef Py_RETURN_FALSE
25
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
28
/* for compatibility with Python < 3.0 */
29
#ifndef PyVarObject_HEAD_INIT
30
#define PyVarObject_HEAD_INIT(type, size) \
31
PyObject_HEAD_INIT(type) size,
35
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
40
/* Type-specific fields go here. */
41
struct UPNPDev * devlist;
44
unsigned int discoverdelay; /* value passed to upnpDiscover() */
45
char lanaddr[40]; /* our ip address on the LAN */
47
char * minissdpdsocket;
50
static PyMemberDef UPnP_members[] = {
51
{"lanaddr", T_STRING_INPLACE, offsetof(UPnPObject, lanaddr),
52
READONLY, "ip address on the LAN"
54
{"discoverdelay", T_UINT, offsetof(UPnPObject, discoverdelay),
55
0/*READWRITE*/, "value in ms used to wait for SSDP responses"
57
/* T_STRING is allways readonly :( */
58
{"multicastif", T_STRING, offsetof(UPnPObject, multicastif),
59
0, "IP of the network interface to be used for multicast operations"
61
{"minissdpdsocket", T_STRING, offsetof(UPnPObject, multicastif),
62
0, "path of the MiniSSDPd unix socket"
68
UPnPObject_dealloc(UPnPObject *self)
70
freeUPNPDevlist(self->devlist);
71
FreeUPNPUrls(&self->urls);
72
Py_TYPE(self)->tp_free((PyObject*)self);
76
UPnP_discover(UPnPObject *self)
83
freeUPNPDevlist(self->devlist);
86
Py_BEGIN_ALLOW_THREADS
87
self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/,
89
0/*minissdpd socket*/,
94
/* Py_RETURN_NONE ??? */
95
for(dev = self->devlist, i = 0; dev; dev = dev->pNext)
97
res = Py_BuildValue("i", i);
102
UPnP_selectigd(UPnPObject *self)
105
Py_BEGIN_ALLOW_THREADS
106
r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data,
107
self->lanaddr, sizeof(self->lanaddr));
111
return Py_BuildValue("s", self->urls.controlURL);
115
/* TODO: have our own exception type ! */
116
PyErr_SetString(PyExc_Exception, "No UPnP device discovered");
122
UPnP_totalbytesent(UPnPObject *self)
125
Py_BEGIN_ALLOW_THREADS
126
i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF,
127
self->data.CIF.servicetype);
129
return Py_BuildValue("I", i);
133
UPnP_totalbytereceived(UPnPObject *self)
136
Py_BEGIN_ALLOW_THREADS
137
i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF,
138
self->data.CIF.servicetype);
140
return Py_BuildValue("I", i);
144
UPnP_totalpacketsent(UPnPObject *self)
147
Py_BEGIN_ALLOW_THREADS
148
i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF,
149
self->data.CIF.servicetype);
151
return Py_BuildValue("I", i);
155
UPnP_totalpacketreceived(UPnPObject *self)
158
Py_BEGIN_ALLOW_THREADS
159
i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF,
160
self->data.CIF.servicetype);
162
return Py_BuildValue("I", i);
166
UPnP_statusinfo(UPnPObject *self)
169
char lastconnerror[64];
170
unsigned int uptime = 0;
173
lastconnerror[0] = '\0';
174
Py_BEGIN_ALLOW_THREADS
175
r = UPNP_GetStatusInfo(self->urls.controlURL, self->data.first.servicetype,
176
status, &uptime, lastconnerror);
178
if(r==UPNPCOMMAND_SUCCESS) {
179
return Py_BuildValue("(s,I,s)", status, uptime, lastconnerror);
181
/* TODO: have our own exception type ! */
182
PyErr_SetString(PyExc_Exception, strupnperror(r));
188
UPnP_connectiontype(UPnPObject *self)
190
char connectionType[64];
192
connectionType[0] = '\0';
193
Py_BEGIN_ALLOW_THREADS
194
r = UPNP_GetConnectionTypeInfo(self->urls.controlURL,
195
self->data.first.servicetype,
198
if(r==UPNPCOMMAND_SUCCESS) {
199
return Py_BuildValue("s", connectionType);
201
/* TODO: have our own exception type ! */
202
PyErr_SetString(PyExc_Exception, strupnperror(r));
208
UPnP_externalipaddress(UPnPObject *self)
210
char externalIPAddress[40];
212
externalIPAddress[0] = '\0';
213
Py_BEGIN_ALLOW_THREADS
214
r = UPNP_GetExternalIPAddress(self->urls.controlURL,
215
self->data.first.servicetype,
218
if(r==UPNPCOMMAND_SUCCESS) {
219
return Py_BuildValue("s", externalIPAddress);
221
/* TODO: have our own exception type ! */
222
PyErr_SetString(PyExc_Exception, strupnperror(r));
227
/* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc,
229
* protocol is 'UDP' or 'TCP' */
231
UPnP_addportmapping(UPnPObject *self, PyObject *args)
234
unsigned short ePort;
236
unsigned short iPort;
240
const char * remoteHost;
241
const char * leaseDuration = "0";
243
if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto,
244
&host, &iPort, &desc, &remoteHost))
246
Py_BEGIN_ALLOW_THREADS
247
sprintf(extPort, "%hu", ePort);
248
sprintf(inPort, "%hu", iPort);
249
r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype,
250
extPort, inPort, host, desc, proto,
251
remoteHost, leaseDuration);
253
if(r==UPNPCOMMAND_SUCCESS)
259
// TODO: RAISE an Exception. See upnpcommands.h for errors codes.
262
/* TODO: have our own exception type ! */
263
PyErr_SetString(PyExc_Exception, strupnperror(r));
268
/* DeletePortMapping(extPort, proto, removeHost='')
269
* proto = 'UDP', 'TCP' */
271
UPnP_deleteportmapping(UPnPObject *self, PyObject *args)
274
unsigned short ePort;
276
const char * remoteHost = "";
278
if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
280
Py_BEGIN_ALLOW_THREADS
281
sprintf(extPort, "%hu", ePort);
282
r = UPNP_DeletePortMapping(self->urls.controlURL, self->data.first.servicetype,
283
extPort, proto, remoteHost);
285
if(r==UPNPCOMMAND_SUCCESS) {
288
/* TODO: have our own exception type ! */
289
PyErr_SetString(PyExc_Exception, strupnperror(r));
295
UPnP_getportmappingnumberofentries(UPnPObject *self)
299
Py_BEGIN_ALLOW_THREADS
300
r = UPNP_GetPortMappingNumberOfEntries(self->urls.controlURL,
301
self->data.first.servicetype,
304
if(r==UPNPCOMMAND_SUCCESS) {
305
return Py_BuildValue("I", n);
307
/* TODO: have our own exception type ! */
308
PyErr_SetString(PyExc_Exception, strupnperror(r));
313
/* GetSpecificPortMapping(ePort, proto)
314
* proto = 'UDP' or 'TCP' */
316
UPnP_getspecificportmapping(UPnPObject *self, PyObject *args)
319
unsigned short ePort;
323
unsigned short iPort;
326
char leaseDuration[16];
327
if(!PyArg_ParseTuple(args, "Hs", &ePort, &proto))
329
extPort[0] = '\0'; intClient[0] = '\0'; intPort[0] = '\0';
330
desc[0] = '\0'; enabled[0] = '\0'; leaseDuration[0] = '\0';
331
Py_BEGIN_ALLOW_THREADS
332
sprintf(extPort, "%hu", ePort);
333
UPNP_GetSpecificPortMappingEntry(self->urls.controlURL,
334
self->data.first.servicetype,
337
desc, enabled, leaseDuration);
341
iPort = (unsigned short)atoi(intPort);
342
return Py_BuildValue("(s,H,s,O,i)",
343
intClient, iPort, desc,
344
PyBool_FromLong(atoi(enabled)),
345
atoi(leaseDuration));
353
/* GetGenericPortMapping(index) */
355
UPnP_getgenericportmapping(UPnPObject *self, PyObject *args)
361
unsigned short iPort;
363
unsigned short ePort;
368
char duration[16]; /* lease duration */
370
if(!PyArg_ParseTuple(args, "i", &i))
372
Py_BEGIN_ALLOW_THREADS
373
snprintf(index, sizeof(index), "%d", i);
374
rHost[0] = '\0'; enabled[0] = '\0';
375
duration[0] = '\0'; desc[0] = '\0';
376
extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
377
r = UPNP_GetGenericPortMappingEntry(self->urls.controlURL,
378
self->data.first.servicetype,
380
extPort, intClient, intPort,
381
protocol, desc, enabled, rHost,
384
if(r==UPNPCOMMAND_SUCCESS)
386
ePort = (unsigned short)atoi(extPort);
387
iPort = (unsigned short)atoi(intPort);
388
dur = (unsigned int)strtoul(duration, 0, 0);
389
return Py_BuildValue("(H,s,(s,H),s,s,s,I)",
390
ePort, protocol, intClient, iPort,
391
desc, enabled, rHost, dur);
399
/* miniupnpc.UPnP object Method Table */
400
static PyMethodDef UPnP_methods[] = {
401
{"discover", (PyCFunction)UPnP_discover, METH_NOARGS,
402
"discover UPnP IGD devices on the network"
404
{"selectigd", (PyCFunction)UPnP_selectigd, METH_NOARGS,
405
"select a valid UPnP IGD among discovered devices"
407
{"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS,
408
"return the total number of bytes sent by UPnP IGD"
410
{"totalbytereceived", (PyCFunction)UPnP_totalbytereceived, METH_NOARGS,
411
"return the total number of bytes received by UPnP IGD"
413
{"totalpacketsent", (PyCFunction)UPnP_totalpacketsent, METH_NOARGS,
414
"return the total number of packets sent by UPnP IGD"
416
{"totalpacketreceived", (PyCFunction)UPnP_totalpacketreceived, METH_NOARGS,
417
"return the total number of packets received by UPnP IGD"
419
{"statusinfo", (PyCFunction)UPnP_statusinfo, METH_NOARGS,
420
"return status and uptime"
422
{"connectiontype", (PyCFunction)UPnP_connectiontype, METH_NOARGS,
423
"return IGD WAN connection type"
425
{"externalipaddress", (PyCFunction)UPnP_externalipaddress, METH_NOARGS,
426
"return external IP address"
428
{"addportmapping", (PyCFunction)UPnP_addportmapping, METH_VARARGS,
431
{"deleteportmapping", (PyCFunction)UPnP_deleteportmapping, METH_VARARGS,
432
"delete a port mapping"
434
{"getportmappingnumberofentries", (PyCFunction)UPnP_getportmappingnumberofentries, METH_NOARGS,
437
{"getspecificportmapping", (PyCFunction)UPnP_getspecificportmapping, METH_VARARGS,
438
"get details about a specific port mapping entry"
440
{"getgenericportmapping", (PyCFunction)UPnP_getgenericportmapping, METH_VARARGS,
441
"get all details about the port mapping at index"
443
{NULL} /* Sentinel */
446
static PyTypeObject UPnPType = {
447
PyVarObject_HEAD_INIT(NULL,
449
"miniupnpc.UPnP", /*tp_name*/
450
sizeof(UPnPObject), /*tp_basicsize*/
452
(destructor)UPnPObject_dealloc,/*tp_dealloc*/
459
0, /*tp_as_sequence*/
467
Py_TPFLAGS_DEFAULT, /*tp_flags*/
468
"UPnP objects", /* tp_doc */
471
0, /* tp_richcompare */
472
0, /* tp_weaklistoffset */
475
UPnP_methods, /* tp_methods */
476
UPnP_members, /* tp_members */
480
0, /* tp_descr_get */
481
0, /* tp_descr_set */
482
0, /* tp_dictoffset */
483
0,/*(initproc)UPnP_init,*/ /* tp_init */
486
PyType_GenericNew,/*UPnP_new,*/ /* tp_new */
493
static PyMethodDef miniupnpc_methods[] = {
494
{NULL} /* Sentinel */
497
#if PY_MAJOR_VERSION >= 3
498
static struct PyModuleDef moduledef = {
499
PyModuleDef_HEAD_INIT,
500
"miniupnpc", /* m_name */
501
"miniupnpc module.", /* m_doc */
503
miniupnpc_methods, /* m_methods */
505
NULL, /* m_traverse */
511
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
512
#define PyMODINIT_FUNC void
516
#if PY_MAJOR_VERSION >= 3
517
PyInit_miniupnpc(void)
525
UPnPType.tp_new = PyType_GenericNew;
527
if (PyType_Ready(&UPnPType) < 0)
530
#if PY_MAJOR_VERSION >= 3
531
m = PyModule_Create(&moduledef);
533
m = Py_InitModule3("miniupnpc", miniupnpc_methods,
534
"miniupnpc module.");
537
Py_INCREF(&UPnPType);
538
PyModule_AddObject(m, "UPnP", (PyObject *)&UPnPType);
540
#if PY_MAJOR_VERSION >= 3