546 lines
15 KiB
C
Executable File
546 lines
15 KiB
C
Executable File
/* $Id: miniupnpcmodule.c,v 1.22 2014/01/31 13:18:25 nanard Exp $*/
|
|
/* Project : miniupnp
|
|
* Author : Thomas BERNARD
|
|
* website : http://miniupnp.tuxfamily.org/
|
|
* copyright (c) 2007-2014 Thomas Bernard
|
|
* This software is subjet to the conditions detailed in the
|
|
* provided LICENCE file. */
|
|
#include <Python.h>
|
|
#define STATICLIB
|
|
#include "structmember.h"
|
|
#include "miniupnpc.h"
|
|
#include "upnpcommands.h"
|
|
#include "upnperrors.h"
|
|
|
|
/* for compatibility with Python < 2.4 */
|
|
#ifndef Py_RETURN_NONE
|
|
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
|
|
#endif
|
|
|
|
#ifndef Py_RETURN_TRUE
|
|
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
|
|
#endif
|
|
|
|
#ifndef Py_RETURN_FALSE
|
|
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
|
|
#endif
|
|
|
|
/* for compatibility with Python < 3.0 */
|
|
#ifndef PyVarObject_HEAD_INIT
|
|
#define PyVarObject_HEAD_INIT(type, size) \
|
|
PyObject_HEAD_INIT(type) size,
|
|
#endif
|
|
|
|
#ifndef Py_TYPE
|
|
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
|
|
#endif
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
/* Type-specific fields go here. */
|
|
struct UPNPDev * devlist;
|
|
struct UPNPUrls urls;
|
|
struct IGDdatas data;
|
|
unsigned int discoverdelay; /* value passed to upnpDiscover() */
|
|
char lanaddr[40]; /* our ip address on the LAN */
|
|
char * multicastif;
|
|
char * minissdpdsocket;
|
|
} UPnPObject;
|
|
|
|
static PyMemberDef UPnP_members[] = {
|
|
{"lanaddr", T_STRING_INPLACE, offsetof(UPnPObject, lanaddr),
|
|
READONLY, "ip address on the LAN"
|
|
},
|
|
{"discoverdelay", T_UINT, offsetof(UPnPObject, discoverdelay),
|
|
0/*READWRITE*/, "value in ms used to wait for SSDP responses"
|
|
},
|
|
/* T_STRING is allways readonly :( */
|
|
{"multicastif", T_STRING, offsetof(UPnPObject, multicastif),
|
|
0, "IP of the network interface to be used for multicast operations"
|
|
},
|
|
{"minissdpdsocket", T_STRING, offsetof(UPnPObject, multicastif),
|
|
0, "path of the MiniSSDPd unix socket"
|
|
},
|
|
{NULL}
|
|
};
|
|
|
|
static void
|
|
UPnPObject_dealloc(UPnPObject *self)
|
|
{
|
|
freeUPNPDevlist(self->devlist);
|
|
FreeUPNPUrls(&self->urls);
|
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
|
}
|
|
|
|
static PyObject *
|
|
UPnP_discover(UPnPObject *self)
|
|
{
|
|
struct UPNPDev * dev;
|
|
int i;
|
|
PyObject *res = NULL;
|
|
if(self->devlist)
|
|
{
|
|
freeUPNPDevlist(self->devlist);
|
|
self->devlist = 0;
|
|
}
|
|
Py_BEGIN_ALLOW_THREADS
|
|
self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/,
|
|
0/* multicast if*/,
|
|
0/*minissdpd socket*/,
|
|
0/*sameport flag*/,
|
|
0/*ip v6*/,
|
|
0/*error */);
|
|
Py_END_ALLOW_THREADS
|
|
/* Py_RETURN_NONE ??? */
|
|
for(dev = self->devlist, i = 0; dev; dev = dev->pNext)
|
|
i++;
|
|
res = Py_BuildValue("i", i);
|
|
return res;
|
|
}
|
|
|
|
static PyObject *
|
|
UPnP_selectigd(UPnPObject *self)
|
|
{
|
|
int r;
|
|
Py_BEGIN_ALLOW_THREADS
|
|
r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data,
|
|
self->lanaddr, sizeof(self->lanaddr));
|
|
Py_END_ALLOW_THREADS
|
|
if(r)
|
|
{
|
|
return Py_BuildValue("s", self->urls.controlURL);
|
|
}
|
|
else
|
|
{
|
|
/* TODO: have our own exception type ! */
|
|
PyErr_SetString(PyExc_Exception, "No UPnP device discovered");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static PyObject *
|
|
UPnP_totalbytesent(UPnPObject *self)
|
|
{
|
|
UNSIGNED_INTEGER i;
|
|
Py_BEGIN_ALLOW_THREADS
|
|
i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF,
|
|
self->data.CIF.servicetype);
|
|
Py_END_ALLOW_THREADS
|
|
return Py_BuildValue("I", i);
|
|
}
|
|
|
|
static PyObject *
|
|
UPnP_totalbytereceived(UPnPObject *self)
|
|
{
|
|
UNSIGNED_INTEGER i;
|
|
Py_BEGIN_ALLOW_THREADS
|
|
i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF,
|
|
self->data.CIF.servicetype);
|
|
Py_END_ALLOW_THREADS
|
|
return Py_BuildValue("I", i);
|
|
}
|
|
|
|
static PyObject *
|
|
UPnP_totalpacketsent(UPnPObject *self)
|
|
{
|
|
UNSIGNED_INTEGER i;
|
|
Py_BEGIN_ALLOW_THREADS
|
|
i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF,
|
|
self->data.CIF.servicetype);
|
|
Py_END_ALLOW_THREADS
|
|
return Py_BuildValue("I", i);
|
|
}
|
|
|
|
static PyObject *
|
|
UPnP_totalpacketreceived(UPnPObject *self)
|
|
{
|
|
UNSIGNED_INTEGER i;
|
|
Py_BEGIN_ALLOW_THREADS
|
|
i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF,
|
|
self->data.CIF.servicetype);
|
|
Py_END_ALLOW_THREADS
|
|
return Py_BuildValue("I", i);
|
|
}
|
|
|
|
static PyObject *
|
|
UPnP_statusinfo(UPnPObject *self)
|
|
{
|
|
char status[64];
|
|
char lastconnerror[64];
|
|
unsigned int uptime = 0;
|
|
int r;
|
|
status[0] = '\0';
|
|
lastconnerror[0] = '\0';
|
|
Py_BEGIN_ALLOW_THREADS
|
|
r = UPNP_GetStatusInfo(self->urls.controlURL, self->data.first.servicetype,
|
|
status, &uptime, lastconnerror);
|
|
Py_END_ALLOW_THREADS
|
|
if(r==UPNPCOMMAND_SUCCESS) {
|
|
return Py_BuildValue("(s,I,s)", status, uptime, lastconnerror);
|
|
} else {
|
|
/* TODO: have our own exception type ! */
|
|
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static PyObject *
|
|
UPnP_connectiontype(UPnPObject *self)
|
|
{
|
|
char connectionType[64];
|
|
int r;
|
|
connectionType[0] = '\0';
|
|
Py_BEGIN_ALLOW_THREADS
|
|
r = UPNP_GetConnectionTypeInfo(self->urls.controlURL,
|
|
self->data.first.servicetype,
|
|
connectionType);
|
|
Py_END_ALLOW_THREADS
|
|
if(r==UPNPCOMMAND_SUCCESS) {
|
|
return Py_BuildValue("s", connectionType);
|
|
} else {
|
|
/* TODO: have our own exception type ! */
|
|
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static PyObject *
|
|
UPnP_externalipaddress(UPnPObject *self)
|
|
{
|
|
char externalIPAddress[40];
|
|
int r;
|
|
externalIPAddress[0] = '\0';
|
|
Py_BEGIN_ALLOW_THREADS
|
|
r = UPNP_GetExternalIPAddress(self->urls.controlURL,
|
|
self->data.first.servicetype,
|
|
externalIPAddress);
|
|
Py_END_ALLOW_THREADS
|
|
if(r==UPNPCOMMAND_SUCCESS) {
|
|
return Py_BuildValue("s", externalIPAddress);
|
|
} else {
|
|
/* TODO: have our own exception type ! */
|
|
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc,
|
|
* remoteHost)
|
|
* protocol is 'UDP' or 'TCP' */
|
|
static PyObject *
|
|
UPnP_addportmapping(UPnPObject *self, PyObject *args)
|
|
{
|
|
char extPort[6];
|
|
unsigned short ePort;
|
|
char inPort[6];
|
|
unsigned short iPort;
|
|
const char * proto;
|
|
const char * host;
|
|
const char * desc;
|
|
const char * remoteHost;
|
|
const char * leaseDuration = "0";
|
|
int r;
|
|
if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto,
|
|
&host, &iPort, &desc, &remoteHost))
|
|
return NULL;
|
|
Py_BEGIN_ALLOW_THREADS
|
|
sprintf(extPort, "%hu", ePort);
|
|
sprintf(inPort, "%hu", iPort);
|
|
r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype,
|
|
extPort, inPort, host, desc, proto,
|
|
remoteHost, leaseDuration);
|
|
Py_END_ALLOW_THREADS
|
|
if(r==UPNPCOMMAND_SUCCESS)
|
|
{
|
|
Py_RETURN_TRUE;
|
|
}
|
|
else
|
|
{
|
|
// TODO: RAISE an Exception. See upnpcommands.h for errors codes.
|
|
// upnperrors.c
|
|
//Py_RETURN_FALSE;
|
|
/* TODO: have our own exception type ! */
|
|
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* DeletePortMapping(extPort, proto, removeHost='')
|
|
* proto = 'UDP', 'TCP' */
|
|
static PyObject *
|
|
UPnP_deleteportmapping(UPnPObject *self, PyObject *args)
|
|
{
|
|
char extPort[6];
|
|
unsigned short ePort;
|
|
const char * proto;
|
|
const char * remoteHost = "";
|
|
int r;
|
|
if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
|
|
return NULL;
|
|
Py_BEGIN_ALLOW_THREADS
|
|
sprintf(extPort, "%hu", ePort);
|
|
r = UPNP_DeletePortMapping(self->urls.controlURL, self->data.first.servicetype,
|
|
extPort, proto, remoteHost);
|
|
Py_END_ALLOW_THREADS
|
|
if(r==UPNPCOMMAND_SUCCESS) {
|
|
Py_RETURN_TRUE;
|
|
} else {
|
|
/* TODO: have our own exception type ! */
|
|
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static PyObject *
|
|
UPnP_getportmappingnumberofentries(UPnPObject *self)
|
|
{
|
|
unsigned int n = 0;
|
|
int r;
|
|
Py_BEGIN_ALLOW_THREADS
|
|
r = UPNP_GetPortMappingNumberOfEntries(self->urls.controlURL,
|
|
self->data.first.servicetype,
|
|
&n);
|
|
Py_END_ALLOW_THREADS
|
|
if(r==UPNPCOMMAND_SUCCESS) {
|
|
return Py_BuildValue("I", n);
|
|
} else {
|
|
/* TODO: have our own exception type ! */
|
|
PyErr_SetString(PyExc_Exception, strupnperror(r));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* GetSpecificPortMapping(ePort, proto, remoteHost='')
|
|
* proto = 'UDP' or 'TCP' */
|
|
static PyObject *
|
|
UPnP_getspecificportmapping(UPnPObject *self, PyObject *args)
|
|
{
|
|
char extPort[6];
|
|
unsigned short ePort;
|
|
const char * proto;
|
|
const char * remoteHost = "";
|
|
char intClient[40];
|
|
char intPort[6];
|
|
unsigned short iPort;
|
|
char desc[80];
|
|
char enabled[4];
|
|
char leaseDuration[16];
|
|
if(!PyArg_ParseTuple(args, "Hs|z", &ePort, &proto, &remoteHost))
|
|
return NULL;
|
|
extPort[0] = '\0'; intClient[0] = '\0'; intPort[0] = '\0';
|
|
desc[0] = '\0'; enabled[0] = '\0'; leaseDuration[0] = '\0';
|
|
Py_BEGIN_ALLOW_THREADS
|
|
sprintf(extPort, "%hu", ePort);
|
|
UPNP_GetSpecificPortMappingEntry(self->urls.controlURL,
|
|
self->data.first.servicetype,
|
|
extPort, proto, remoteHost,
|
|
intClient, intPort,
|
|
desc, enabled, leaseDuration);
|
|
Py_END_ALLOW_THREADS
|
|
if(intClient[0])
|
|
{
|
|
iPort = (unsigned short)atoi(intPort);
|
|
return Py_BuildValue("(s,H,s,O,i)",
|
|
intClient, iPort, desc,
|
|
PyBool_FromLong(atoi(enabled)),
|
|
atoi(leaseDuration));
|
|
}
|
|
else
|
|
{
|
|
Py_RETURN_NONE;
|
|
}
|
|
}
|
|
|
|
/* GetGenericPortMapping(index) */
|
|
static PyObject *
|
|
UPnP_getgenericportmapping(UPnPObject *self, PyObject *args)
|
|
{
|
|
int i, r;
|
|
char index[8];
|
|
char intClient[40];
|
|
char intPort[6];
|
|
unsigned short iPort;
|
|
char extPort[6];
|
|
unsigned short ePort;
|
|
char protocol[4];
|
|
char desc[80];
|
|
char enabled[6];
|
|
char rHost[64];
|
|
char duration[16]; /* lease duration */
|
|
unsigned int dur;
|
|
if(!PyArg_ParseTuple(args, "i", &i))
|
|
return NULL;
|
|
Py_BEGIN_ALLOW_THREADS
|
|
snprintf(index, sizeof(index), "%d", i);
|
|
rHost[0] = '\0'; enabled[0] = '\0';
|
|
duration[0] = '\0'; desc[0] = '\0';
|
|
extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
|
|
r = UPNP_GetGenericPortMappingEntry(self->urls.controlURL,
|
|
self->data.first.servicetype,
|
|
index,
|
|
extPort, intClient, intPort,
|
|
protocol, desc, enabled, rHost,
|
|
duration);
|
|
Py_END_ALLOW_THREADS
|
|
if(r==UPNPCOMMAND_SUCCESS)
|
|
{
|
|
ePort = (unsigned short)atoi(extPort);
|
|
iPort = (unsigned short)atoi(intPort);
|
|
dur = (unsigned int)strtoul(duration, 0, 0);
|
|
return Py_BuildValue("(H,s,(s,H),s,s,s,I)",
|
|
ePort, protocol, intClient, iPort,
|
|
desc, enabled, rHost, dur);
|
|
}
|
|
else
|
|
{
|
|
Py_RETURN_NONE;
|
|
}
|
|
}
|
|
|
|
/* miniupnpc.UPnP object Method Table */
|
|
static PyMethodDef UPnP_methods[] = {
|
|
{"discover", (PyCFunction)UPnP_discover, METH_NOARGS,
|
|
"discover UPnP IGD devices on the network"
|
|
},
|
|
{"selectigd", (PyCFunction)UPnP_selectigd, METH_NOARGS,
|
|
"select a valid UPnP IGD among discovered devices"
|
|
},
|
|
{"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS,
|
|
"return the total number of bytes sent by UPnP IGD"
|
|
},
|
|
{"totalbytereceived", (PyCFunction)UPnP_totalbytereceived, METH_NOARGS,
|
|
"return the total number of bytes received by UPnP IGD"
|
|
},
|
|
{"totalpacketsent", (PyCFunction)UPnP_totalpacketsent, METH_NOARGS,
|
|
"return the total number of packets sent by UPnP IGD"
|
|
},
|
|
{"totalpacketreceived", (PyCFunction)UPnP_totalpacketreceived, METH_NOARGS,
|
|
"return the total number of packets received by UPnP IGD"
|
|
},
|
|
{"statusinfo", (PyCFunction)UPnP_statusinfo, METH_NOARGS,
|
|
"return status and uptime"
|
|
},
|
|
{"connectiontype", (PyCFunction)UPnP_connectiontype, METH_NOARGS,
|
|
"return IGD WAN connection type"
|
|
},
|
|
{"externalipaddress", (PyCFunction)UPnP_externalipaddress, METH_NOARGS,
|
|
"return external IP address"
|
|
},
|
|
{"addportmapping", (PyCFunction)UPnP_addportmapping, METH_VARARGS,
|
|
"add a port mapping"
|
|
},
|
|
{"deleteportmapping", (PyCFunction)UPnP_deleteportmapping, METH_VARARGS,
|
|
"delete a port mapping"
|
|
},
|
|
{"getportmappingnumberofentries", (PyCFunction)UPnP_getportmappingnumberofentries, METH_NOARGS,
|
|
"-- non standard --"
|
|
},
|
|
{"getspecificportmapping", (PyCFunction)UPnP_getspecificportmapping, METH_VARARGS,
|
|
"get details about a specific port mapping entry"
|
|
},
|
|
{"getgenericportmapping", (PyCFunction)UPnP_getgenericportmapping, METH_VARARGS,
|
|
"get all details about the port mapping at index"
|
|
},
|
|
{NULL} /* Sentinel */
|
|
};
|
|
|
|
static PyTypeObject UPnPType = {
|
|
PyVarObject_HEAD_INIT(NULL,
|
|
0) /*ob_size*/
|
|
"miniupnpc.UPnP", /*tp_name*/
|
|
sizeof(UPnPObject), /*tp_basicsize*/
|
|
0, /*tp_itemsize*/
|
|
(destructor)UPnPObject_dealloc,/*tp_dealloc*/
|
|
0, /*tp_print*/
|
|
0, /*tp_getattr*/
|
|
0, /*tp_setattr*/
|
|
0, /*tp_compare*/
|
|
0, /*tp_repr*/
|
|
0, /*tp_as_number*/
|
|
0, /*tp_as_sequence*/
|
|
0, /*tp_as_mapping*/
|
|
0, /*tp_hash */
|
|
0, /*tp_call*/
|
|
0, /*tp_str*/
|
|
0, /*tp_getattro*/
|
|
0, /*tp_setattro*/
|
|
0, /*tp_as_buffer*/
|
|
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
"UPnP objects", /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
UPnP_methods, /* tp_methods */
|
|
UPnP_members, /* tp_members */
|
|
0, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
0,/*(initproc)UPnP_init,*/ /* tp_init */
|
|
0, /* tp_alloc */
|
|
#ifndef _WIN32
|
|
PyType_GenericNew,/*UPnP_new,*/ /* tp_new */
|
|
#else
|
|
0,
|
|
#endif
|
|
};
|
|
|
|
/* module methods */
|
|
static PyMethodDef miniupnpc_methods[] = {
|
|
{NULL} /* Sentinel */
|
|
};
|
|
|
|
#if PY_MAJOR_VERSION >= 3
|
|
static struct PyModuleDef moduledef = {
|
|
PyModuleDef_HEAD_INIT,
|
|
"miniupnpc", /* m_name */
|
|
"miniupnpc module.", /* m_doc */
|
|
-1, /* m_size */
|
|
miniupnpc_methods, /* m_methods */
|
|
NULL, /* m_reload */
|
|
NULL, /* m_traverse */
|
|
NULL, /* m_clear */
|
|
NULL, /* m_free */
|
|
};
|
|
#endif
|
|
|
|
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
|
|
#define PyMODINIT_FUNC void
|
|
#endif
|
|
|
|
PyMODINIT_FUNC
|
|
#if PY_MAJOR_VERSION >= 3
|
|
PyInit_miniupnpc(void)
|
|
#else
|
|
initminiupnpc(void)
|
|
#endif
|
|
{
|
|
PyObject* m;
|
|
|
|
#ifdef _WIN32
|
|
UPnPType.tp_new = PyType_GenericNew;
|
|
#endif
|
|
if (PyType_Ready(&UPnPType) < 0)
|
|
return;
|
|
|
|
#if PY_MAJOR_VERSION >= 3
|
|
m = PyModule_Create(&moduledef);
|
|
#else
|
|
m = Py_InitModule3("miniupnpc", miniupnpc_methods,
|
|
"miniupnpc module.");
|
|
#endif
|
|
|
|
Py_INCREF(&UPnPType);
|
|
PyModule_AddObject(m, "UPnP", (PyObject *)&UPnPType);
|
|
|
|
#if PY_MAJOR_VERSION >= 3
|
|
return m;
|
|
#endif
|
|
}
|
|
|