mupnp 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +13 -0
- data/Rakefile +80 -0
- data/ext/Changelog.txt +188 -0
- data/ext/LICENSE +27 -0
- data/ext/README +53 -0
- data/ext/declspec.h +15 -0
- data/ext/extconf.rb +5 -0
- data/ext/igd_desc_parse.c +115 -0
- data/ext/igd_desc_parse.h +38 -0
- data/ext/minisoap.c +112 -0
- data/ext/minisoap.h +15 -0
- data/ext/minissdpc.c +107 -0
- data/ext/minissdpc.h +15 -0
- data/ext/miniupnpc.c +751 -0
- data/ext/miniupnpc.h +108 -0
- data/ext/miniwget.c +219 -0
- data/ext/miniwget.h +28 -0
- data/ext/minixml.c +191 -0
- data/ext/minixml.h +37 -0
- data/ext/upnp.i +13 -0
- data/ext/upnp_wrap.c +5207 -0
- data/ext/upnpcommands.c +569 -0
- data/ext/upnpcommands.h +179 -0
- data/ext/upnperrors.c +55 -0
- data/ext/upnperrors.h +16 -0
- data/ext/upnpreplyparse.c +127 -0
- data/ext/upnpreplyparse.h +62 -0
- data/lib/UPnP.rb +400 -0
- data/test/UPnP/tc_upnp.rb +20 -0
- metadata +83 -0
data/ext/upnpcommands.h
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
/* $Id: upnpcommands.h,v 1.13 2008/02/18 13:27:24 nanard Exp $ */
|
2
|
+
/* Miniupnp project : http://miniupnp.free.fr/
|
3
|
+
* Author : Thomas Bernard
|
4
|
+
* Copyright (c) 2005-2006 Thomas Bernard
|
5
|
+
* This software is subject to the conditions detailed in the
|
6
|
+
* LICENCE file provided within this distribution */
|
7
|
+
#ifndef __UPNPCOMMANDS_H__
|
8
|
+
#define __UPNPCOMMANDS_H__
|
9
|
+
|
10
|
+
#include "upnpreplyparse.h"
|
11
|
+
#include "declspec.h"
|
12
|
+
|
13
|
+
/* MiniUPnPc return codes : */
|
14
|
+
#define UPNPCOMMAND_SUCCESS (0)
|
15
|
+
#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
|
16
|
+
#define UPNPCOMMAND_INVALID_ARGS (-2)
|
17
|
+
|
18
|
+
#ifdef __cplusplus
|
19
|
+
extern "C" {
|
20
|
+
#endif
|
21
|
+
|
22
|
+
LIBSPEC unsigned int
|
23
|
+
UPNP_GetTotalBytesSent(const char * controlURL,
|
24
|
+
const char * servicetype);
|
25
|
+
|
26
|
+
LIBSPEC unsigned int
|
27
|
+
UPNP_GetTotalBytesReceived(const char * controlURL,
|
28
|
+
const char * servicetype);
|
29
|
+
|
30
|
+
LIBSPEC unsigned int
|
31
|
+
UPNP_GetTotalPacketsSent(const char * controlURL,
|
32
|
+
const char * servicetype);
|
33
|
+
|
34
|
+
LIBSPEC unsigned int
|
35
|
+
UPNP_GetTotalPacketsReceived(const char * controlURL,
|
36
|
+
const char * servicetype);
|
37
|
+
|
38
|
+
/* UPNP_GetStatusInfo()
|
39
|
+
* status and lastconnerror are 64 byte buffers
|
40
|
+
* Return values :
|
41
|
+
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
42
|
+
* or a UPnP Error code */
|
43
|
+
LIBSPEC int
|
44
|
+
UPNP_GetStatusInfo(const char * controlURL,
|
45
|
+
const char * servicetype,
|
46
|
+
char * status,
|
47
|
+
unsigned int * uptime,
|
48
|
+
char * lastconnerror);
|
49
|
+
|
50
|
+
/* UPNP_GetConnectionTypeInfo()
|
51
|
+
* argument connectionType is a 64 character buffer
|
52
|
+
* Return Values :
|
53
|
+
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
54
|
+
* or a UPnP Error code */
|
55
|
+
LIBSPEC int
|
56
|
+
UPNP_GetConnectionTypeInfo(const char * controlURL,
|
57
|
+
const char * servicetype,
|
58
|
+
char * connectionType);
|
59
|
+
|
60
|
+
/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
|
61
|
+
* if the third arg is not null the value is copied to it.
|
62
|
+
* at least 16 bytes must be available
|
63
|
+
*
|
64
|
+
* Return values :
|
65
|
+
* 0 : SUCCESS
|
66
|
+
* NON ZERO : ERROR Either an UPnP error code or an unknown error.
|
67
|
+
*
|
68
|
+
* possible UPnP Errors :
|
69
|
+
* 402 Invalid Args - See UPnP Device Architecture section on Control.
|
70
|
+
* 501 Action Failed - See UPnP Device Architecture section on Control. */
|
71
|
+
LIBSPEC int
|
72
|
+
UPNP_GetExternalIPAddress(const char * controlURL,
|
73
|
+
const char * servicetype,
|
74
|
+
char * extIpAdd);
|
75
|
+
|
76
|
+
/* UPNP_GetLinkLayerMaxBitRates()
|
77
|
+
* call WANCommonInterfaceConfig:1#GetCommonLinkProperties
|
78
|
+
*
|
79
|
+
* return values :
|
80
|
+
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
81
|
+
* or a UPnP Error Code. */
|
82
|
+
LIBSPEC int
|
83
|
+
UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
|
84
|
+
const char* servicetype,
|
85
|
+
unsigned int * bitrateDown,
|
86
|
+
unsigned int * bitrateUp);
|
87
|
+
|
88
|
+
/* UPNP_AddPortMapping()
|
89
|
+
*
|
90
|
+
* Return values :
|
91
|
+
* 0 : SUCCESS
|
92
|
+
* NON ZERO : ERROR. Either an UPnP error code or an unknown error.
|
93
|
+
*
|
94
|
+
* List of possible UPnP errors for AddPortMapping :
|
95
|
+
* errorCode errorDescription (short) - Description (long)
|
96
|
+
* 402 Invalid Args - See UPnP Device Architecture section on Control.
|
97
|
+
* 501 Action Failed - See UPnP Device Architecture section on Control.
|
98
|
+
* 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
|
99
|
+
* wild-carded
|
100
|
+
* 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
|
101
|
+
* 718 ConflictInMappingEntry - The port mapping entry specified conflicts
|
102
|
+
* with a mapping assigned previously to another client
|
103
|
+
* 724 SamePortValuesRequired - Internal and External port values
|
104
|
+
* must be the same
|
105
|
+
* 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
|
106
|
+
* permanent lease times on port mappings
|
107
|
+
* 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
|
108
|
+
* and cannot be a specific IP address or DNS name
|
109
|
+
* 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
|
110
|
+
* cannot be a specific port value */
|
111
|
+
LIBSPEC int
|
112
|
+
UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
|
113
|
+
const char * extPort,
|
114
|
+
const char * inPort,
|
115
|
+
const char * inClient,
|
116
|
+
const char * desc,
|
117
|
+
const char * proto);
|
118
|
+
|
119
|
+
/* UPNP_DeletePortMapping()
|
120
|
+
* Return Values :
|
121
|
+
* 0 : SUCCESS
|
122
|
+
* NON ZERO : error. Either an UPnP error code or an undefined error.
|
123
|
+
*
|
124
|
+
* List of possible UPnP errors for DeletePortMapping :
|
125
|
+
* 402 Invalid Args - See UPnP Device Architecture section on Control.
|
126
|
+
* 714 NoSuchEntryInArray - The specified value does not exist in the array */
|
127
|
+
LIBSPEC int
|
128
|
+
UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
|
129
|
+
const char * extPort, const char * proto);
|
130
|
+
|
131
|
+
/* UPNP_GetPortMappingNumberOfEntries()
|
132
|
+
* not supported by all routers */
|
133
|
+
LIBSPEC int
|
134
|
+
UPNP_GetPortMappingNumberOfEntries(const char* controlURL, const char* servicetype, unsigned int * num);
|
135
|
+
|
136
|
+
/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping
|
137
|
+
* the result is returned in the intClient and intPort strings
|
138
|
+
* please provide 16 and 6 bytes of data
|
139
|
+
*
|
140
|
+
* return value :
|
141
|
+
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
142
|
+
* or a UPnP Error Code. */
|
143
|
+
LIBSPEC int
|
144
|
+
UPNP_GetSpecificPortMappingEntry(const char * controlURL,
|
145
|
+
const char * servicetype,
|
146
|
+
const char * extPort,
|
147
|
+
const char * proto,
|
148
|
+
char * intClient,
|
149
|
+
char * intPort);
|
150
|
+
|
151
|
+
/* UPNP_GetGenericPortMappingEntry()
|
152
|
+
*
|
153
|
+
* return value :
|
154
|
+
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
155
|
+
* or a UPnP Error Code.
|
156
|
+
*
|
157
|
+
* Possible UPNP Error codes :
|
158
|
+
* 402 Invalid Args - See UPnP Device Architecture section on Control.
|
159
|
+
* 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
|
160
|
+
*/
|
161
|
+
LIBSPEC int
|
162
|
+
UPNP_GetGenericPortMappingEntry(const char * controlURL,
|
163
|
+
const char * servicetype,
|
164
|
+
const char * index,
|
165
|
+
char * extPort,
|
166
|
+
char * intClient,
|
167
|
+
char * intPort,
|
168
|
+
char * protocol,
|
169
|
+
char * desc,
|
170
|
+
char * enabled,
|
171
|
+
char * rHost,
|
172
|
+
char * duration);
|
173
|
+
|
174
|
+
#ifdef __cplusplus
|
175
|
+
}
|
176
|
+
#endif
|
177
|
+
|
178
|
+
#endif
|
179
|
+
|
data/ext/upnperrors.c
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
/* $Id: upnperrors.c,v 1.2 2008/02/05 12:50:22 nanard Exp $ */
|
2
|
+
/* Project : miniupnp
|
3
|
+
* Author : Thomas BERNARD
|
4
|
+
* copyright (c) 2007 Thomas Bernard
|
5
|
+
* All Right reserved.
|
6
|
+
* This software is subjet to the conditions detailed in the
|
7
|
+
* provided LICENCE file. */
|
8
|
+
#include <string.h>
|
9
|
+
#include "upnperrors.h"
|
10
|
+
|
11
|
+
const char * strupnperror(int err)
|
12
|
+
{
|
13
|
+
const char * s = NULL;
|
14
|
+
switch(err) {
|
15
|
+
case 401:
|
16
|
+
s = "Invalid Action";
|
17
|
+
break;
|
18
|
+
case 402:
|
19
|
+
s = "Invalid Args";
|
20
|
+
break;
|
21
|
+
case 501:
|
22
|
+
s = "Action Failed";
|
23
|
+
break;
|
24
|
+
case 713:
|
25
|
+
s = "SpecifiedArrayIndexInvalid";
|
26
|
+
break;
|
27
|
+
case 714:
|
28
|
+
s = "NoSuchEntryInArray";
|
29
|
+
break;
|
30
|
+
case 715:
|
31
|
+
s = "WildCardNotPermittedInSrcIP";
|
32
|
+
break;
|
33
|
+
case 716:
|
34
|
+
s = "WildCardNotPermittedInExtPort";
|
35
|
+
break;
|
36
|
+
case 718:
|
37
|
+
s = "ConflictInMappingEntry";
|
38
|
+
break;
|
39
|
+
case 724:
|
40
|
+
s = "SamePortValuesRequired";
|
41
|
+
break;
|
42
|
+
case 725:
|
43
|
+
s = "OnlyPermanentLeasesSupported";
|
44
|
+
break;
|
45
|
+
case 726:
|
46
|
+
s = "RemoteHostOnlySupportsWildcard";
|
47
|
+
break;
|
48
|
+
case 727:
|
49
|
+
s = "ExternalPortOnlySupportsWildcard";
|
50
|
+
break;
|
51
|
+
default:
|
52
|
+
s = NULL;
|
53
|
+
}
|
54
|
+
return s;
|
55
|
+
}
|
data/ext/upnperrors.h
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
/* $Id: upnperrors.h,v 1.1 2007/12/22 11:28:04 nanard Exp $ */
|
2
|
+
/* (c) 2007 Thomas Bernard
|
3
|
+
* All rights reserved.
|
4
|
+
* MiniUPnP Project.
|
5
|
+
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
6
|
+
* This software is subjet to the conditions detailed in the
|
7
|
+
* provided LICENCE file. */
|
8
|
+
#ifndef __UPNPERRORS_H__
|
9
|
+
#define __UPNPERRORS_H__
|
10
|
+
|
11
|
+
/* strupnperror()
|
12
|
+
* Return a string description of the UPnP error code
|
13
|
+
* or NULL for undefinded errors */
|
14
|
+
const char * strupnperror(int err);
|
15
|
+
|
16
|
+
#endif
|
@@ -0,0 +1,127 @@
|
|
1
|
+
/* $Id: upnpreplyparse.c,v 1.10 2008/02/21 13:05:27 nanard Exp $ */
|
2
|
+
/* MiniUPnP project
|
3
|
+
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
4
|
+
* (c) 2006 Thomas Bernard
|
5
|
+
* This software is subject to the conditions detailed
|
6
|
+
* in the LICENCE file provided within the distribution */
|
7
|
+
|
8
|
+
#include <stdlib.h>
|
9
|
+
#include <string.h>
|
10
|
+
#include <stdio.h>
|
11
|
+
|
12
|
+
#include "upnpreplyparse.h"
|
13
|
+
#include "minixml.h"
|
14
|
+
|
15
|
+
static void
|
16
|
+
NameValueParserStartElt(void * d, const char * name, int l)
|
17
|
+
{
|
18
|
+
struct NameValueParserData * data = (struct NameValueParserData *)d;
|
19
|
+
if(l>63)
|
20
|
+
l = 63;
|
21
|
+
memcpy(data->curelt, name, l);
|
22
|
+
data->curelt[l] = '\0';
|
23
|
+
}
|
24
|
+
|
25
|
+
static void
|
26
|
+
NameValueParserGetData(void * d, const char * datas, int l)
|
27
|
+
{
|
28
|
+
struct NameValueParserData * data = (struct NameValueParserData *)d;
|
29
|
+
struct NameValue * nv;
|
30
|
+
nv = malloc(sizeof(struct NameValue));
|
31
|
+
if(l>63)
|
32
|
+
l = 63;
|
33
|
+
strncpy(nv->name, data->curelt, 64);
|
34
|
+
nv->name[63] = '\0';
|
35
|
+
memcpy(nv->value, datas, l);
|
36
|
+
nv->value[l] = '\0';
|
37
|
+
LIST_INSERT_HEAD( &(data->head), nv, entries);
|
38
|
+
}
|
39
|
+
|
40
|
+
void
|
41
|
+
ParseNameValue(const char * buffer, int bufsize,
|
42
|
+
struct NameValueParserData * data)
|
43
|
+
{
|
44
|
+
struct xmlparser parser;
|
45
|
+
LIST_INIT(&(data->head));
|
46
|
+
/* init xmlparser object */
|
47
|
+
parser.xmlstart = buffer;
|
48
|
+
parser.xmlsize = bufsize;
|
49
|
+
parser.data = data;
|
50
|
+
parser.starteltfunc = NameValueParserStartElt;
|
51
|
+
parser.endeltfunc = 0;
|
52
|
+
parser.datafunc = NameValueParserGetData;
|
53
|
+
parser.attfunc = 0;
|
54
|
+
parsexml(&parser);
|
55
|
+
}
|
56
|
+
|
57
|
+
void
|
58
|
+
ClearNameValueList(struct NameValueParserData * pdata)
|
59
|
+
{
|
60
|
+
struct NameValue * nv;
|
61
|
+
while((nv = pdata->head.lh_first) != NULL)
|
62
|
+
{
|
63
|
+
LIST_REMOVE(nv, entries);
|
64
|
+
free(nv);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
char *
|
69
|
+
GetValueFromNameValueList(struct NameValueParserData * pdata,
|
70
|
+
const char * Name)
|
71
|
+
{
|
72
|
+
struct NameValue * nv;
|
73
|
+
char * p = NULL;
|
74
|
+
for(nv = pdata->head.lh_first;
|
75
|
+
(nv != NULL) && (p == NULL);
|
76
|
+
nv = nv->entries.le_next)
|
77
|
+
{
|
78
|
+
if(strcmp(nv->name, Name) == 0)
|
79
|
+
p = nv->value;
|
80
|
+
}
|
81
|
+
return p;
|
82
|
+
}
|
83
|
+
|
84
|
+
#if 0
|
85
|
+
/* useless now that minixml ignores namespaces by itself */
|
86
|
+
char *
|
87
|
+
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
|
88
|
+
const char * Name)
|
89
|
+
{
|
90
|
+
struct NameValue * nv;
|
91
|
+
char * p = NULL;
|
92
|
+
char * pname;
|
93
|
+
for(nv = pdata->head.lh_first;
|
94
|
+
(nv != NULL) && (p == NULL);
|
95
|
+
nv = nv->entries.le_next)
|
96
|
+
{
|
97
|
+
pname = strrchr(nv->name, ':');
|
98
|
+
if(pname)
|
99
|
+
pname++;
|
100
|
+
else
|
101
|
+
pname = nv->name;
|
102
|
+
if(strcmp(pname, Name)==0)
|
103
|
+
p = nv->value;
|
104
|
+
}
|
105
|
+
return p;
|
106
|
+
}
|
107
|
+
#endif
|
108
|
+
|
109
|
+
/* debug all-in-one function
|
110
|
+
* do parsing then display to stdout */
|
111
|
+
#ifdef DEBUG
|
112
|
+
void
|
113
|
+
DisplayNameValueList(char * buffer, int bufsize)
|
114
|
+
{
|
115
|
+
struct NameValueParserData pdata;
|
116
|
+
struct NameValue * nv;
|
117
|
+
ParseNameValue(buffer, bufsize, &pdata);
|
118
|
+
for(nv = pdata.head.lh_first;
|
119
|
+
nv != NULL;
|
120
|
+
nv = nv->entries.le_next)
|
121
|
+
{
|
122
|
+
printf("%s = %s\n", nv->name, nv->value);
|
123
|
+
}
|
124
|
+
ClearNameValueList(&pdata);
|
125
|
+
}
|
126
|
+
#endif
|
127
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
/* $Id: upnpreplyparse.h,v 1.8 2008/02/21 13:05:27 nanard Exp $ */
|
2
|
+
/* MiniUPnP project
|
3
|
+
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
4
|
+
* (c) 2006 Thomas Bernard
|
5
|
+
* This software is subject to the conditions detailed
|
6
|
+
* in the LICENCE file provided within the distribution */
|
7
|
+
|
8
|
+
#ifndef __UPNPREPLYPARSE_H__
|
9
|
+
#define __UPNPREPLYPARSE_H__
|
10
|
+
|
11
|
+
#if defined(NO_SYS_QUEUE_H) || defined(WIN32)
|
12
|
+
#include "bsdqueue.h"
|
13
|
+
#else
|
14
|
+
#include <sys/queue.h>
|
15
|
+
#endif
|
16
|
+
|
17
|
+
#ifdef __cplusplus
|
18
|
+
extern "C" {
|
19
|
+
#endif
|
20
|
+
|
21
|
+
struct NameValue {
|
22
|
+
LIST_ENTRY(NameValue) entries;
|
23
|
+
char name[64];
|
24
|
+
char value[64];
|
25
|
+
};
|
26
|
+
|
27
|
+
struct NameValueParserData {
|
28
|
+
LIST_HEAD(listhead, NameValue) head;
|
29
|
+
char curelt[64];
|
30
|
+
};
|
31
|
+
|
32
|
+
/* ParseNameValue() */
|
33
|
+
void
|
34
|
+
ParseNameValue(const char * buffer, int bufsize,
|
35
|
+
struct NameValueParserData * data);
|
36
|
+
|
37
|
+
/* ClearNameValueList() */
|
38
|
+
void
|
39
|
+
ClearNameValueList(struct NameValueParserData * pdata);
|
40
|
+
|
41
|
+
/* GetValueFromNameValueList() */
|
42
|
+
char *
|
43
|
+
GetValueFromNameValueList(struct NameValueParserData * pdata,
|
44
|
+
const char * Name);
|
45
|
+
|
46
|
+
/* GetValueFromNameValueListIgnoreNS() */
|
47
|
+
char *
|
48
|
+
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
|
49
|
+
const char * Name);
|
50
|
+
|
51
|
+
/* DisplayNameValueList() */
|
52
|
+
#ifdef DEBUG
|
53
|
+
void
|
54
|
+
DisplayNameValueList(char * buffer, int bufsize);
|
55
|
+
#endif
|
56
|
+
|
57
|
+
#ifdef __cplusplus
|
58
|
+
}
|
59
|
+
#endif
|
60
|
+
|
61
|
+
#endif
|
62
|
+
|
data/lib/UPnP.rb
ADDED
@@ -0,0 +1,400 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
# Author: Dario Meloni <mellon85@gmail.com>
|
3
|
+
# This module is a binding to the Thomas Bernard miniupnp library
|
4
|
+
# written in C. It supports the IGD specification and works with IPv4
|
5
|
+
# only. Every exception that comes from inside the library is report as
|
6
|
+
# UPnPException while wrong arguments are returned as ArgumentError.
|
7
|
+
|
8
|
+
require 'MiniUPnP'
|
9
|
+
include ObjectSpace
|
10
|
+
|
11
|
+
module UPnP
|
12
|
+
|
13
|
+
# Represent a port mapping decriptor received from the router.
|
14
|
+
class PortMapping
|
15
|
+
# Internal address.
|
16
|
+
attr_reader :client
|
17
|
+
|
18
|
+
# Internal port.
|
19
|
+
attr_reader :lport
|
20
|
+
|
21
|
+
# External port.
|
22
|
+
attr_reader :nport
|
23
|
+
|
24
|
+
# External protocol.
|
25
|
+
attr_reader :protocol
|
26
|
+
|
27
|
+
# Provided description.
|
28
|
+
attr_reader :description
|
29
|
+
|
30
|
+
# Is the mapping enabled?.
|
31
|
+
attr_reader :enabled
|
32
|
+
|
33
|
+
# Don't know ...
|
34
|
+
attr_reader :rhost
|
35
|
+
|
36
|
+
# Duration of the binding.
|
37
|
+
attr_reader :duration
|
38
|
+
|
39
|
+
def initialize(cl,lp,np,p,d,e,rh,du)
|
40
|
+
@client = cl
|
41
|
+
@lport = lp
|
42
|
+
@nport = np
|
43
|
+
@protocol = p
|
44
|
+
@description = d
|
45
|
+
@enabled = e
|
46
|
+
@rhost = rh
|
47
|
+
@duration = du
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_s()
|
51
|
+
return "#{@nport}->#{@client}:#{@lport} #{@protocol} for #{@duration} -- #{@description}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# Enumeration of protocol values to pass to the library.
|
57
|
+
class Protocol
|
58
|
+
# TCP protcol
|
59
|
+
TCP = "TCP"
|
60
|
+
|
61
|
+
# UDP protocol
|
62
|
+
UDP = "UDP"
|
63
|
+
end
|
64
|
+
|
65
|
+
# Represents an exception from inside the library.
|
66
|
+
class UPnPException < Exception
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# The UPNP class represent the binding to the library. It exports
|
71
|
+
# all the functions the library itself exports.
|
72
|
+
class UPnP
|
73
|
+
|
74
|
+
# Max time to wait for a broadcast answer from the routers.
|
75
|
+
attr_reader :max_wait_time
|
76
|
+
|
77
|
+
# This will create a new UPnP instance. max_wait is the maximum
|
78
|
+
# time the instance will wait for an answer from the router
|
79
|
+
# while seaching or it autodiscover to true will start a thread
|
80
|
+
# on the background to scan the network. All the other
|
81
|
+
# functions are safe to be called in the meanwhile, they will
|
82
|
+
# just wait for the scan to end before operating.
|
83
|
+
def initialize(autodiscover=true,max_wait=1000)
|
84
|
+
if max_wait <= 0 then
|
85
|
+
raise ArgumentError, "Max wait time must be >= 1."
|
86
|
+
end
|
87
|
+
if !(autodiscover.is_a? TrueClass) &&
|
88
|
+
!(autodiscover.is_a? FalseClass) then
|
89
|
+
raise ArgumentError, "Autodiscover must be a boolean value."
|
90
|
+
end
|
91
|
+
@max_wait_time = max_wait
|
92
|
+
# start the discover process at the object initialization.
|
93
|
+
# until ruby2, this thread will block the ruby environment
|
94
|
+
# for the wait time.
|
95
|
+
if autodiscover then
|
96
|
+
@igd_thread = Thread.new { discoverIGD }
|
97
|
+
else
|
98
|
+
@igd_thread = nil
|
99
|
+
end
|
100
|
+
return nil
|
101
|
+
end
|
102
|
+
|
103
|
+
# This method will search for other routers in the network and
|
104
|
+
# will wait the specified number of milliseconds that can be
|
105
|
+
# ovveridden by the parameter. It has currently (ruby 1.9) a
|
106
|
+
# limitation. As long as thread are cooperative the upnpDiscover
|
107
|
+
# function will block the ruby implementation waiting for the
|
108
|
+
# library. If this will not be solved with ruby 2.0 the
|
109
|
+
# interface upnp_wrap.c needs to be hacked. You can avoid to
|
110
|
+
# call this function if autodiscover is true. If no router or
|
111
|
+
# no UPnP devices are found an UPnPException is thrown.
|
112
|
+
def discoverIGD(max_wait_time=@max_wait_time)
|
113
|
+
joinThread()
|
114
|
+
if max_wait_time <= 0 then
|
115
|
+
raise ArgumentError, "Max wait time must be >= 1"
|
116
|
+
end
|
117
|
+
@list = MiniUPnP.upnpDiscover(max_wait_time,nil,nil)
|
118
|
+
if @list == nil then
|
119
|
+
raise UPnPException.new,"No UPNP Device Found"
|
120
|
+
end
|
121
|
+
define_finalizer(@list,proc {|o| MiniUPnP.freeUPNPDevlist(o)})
|
122
|
+
|
123
|
+
@urls = MiniUPnP::UPNPUrls.new
|
124
|
+
define_finalizer(@urls,proc {|o| MiniUPnP.FreeUPNPUrls(o)})
|
125
|
+
@data = MiniUPnP::IGDdatas.new
|
126
|
+
@lan = getCString()
|
127
|
+
|
128
|
+
r = MiniUPnP.UPNP_GetValidIGD(@list,@urls,@data,@lan,64);
|
129
|
+
if r == 0 || r == 3 then
|
130
|
+
raise UPnPException.new, "No IGD Found"
|
131
|
+
end
|
132
|
+
@lan = @lan.rstrip()
|
133
|
+
return nil
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns the ip of this client
|
137
|
+
def lanIP()
|
138
|
+
joinThread()
|
139
|
+
return @lan
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns the external network ip
|
143
|
+
def externalIP()
|
144
|
+
joinThread()
|
145
|
+
external_ip = getCString()
|
146
|
+
r = MiniUPnP.UPNP_GetExternalIPAddress(@urls.controlURL,
|
147
|
+
@data.servicetype,external_ip)
|
148
|
+
if r != 0 then
|
149
|
+
raise UPnPException.new, "Error while retriving the external ip address. #{code2error(r)}."
|
150
|
+
end
|
151
|
+
return external_ip.rstrip()
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns the ip of the router
|
155
|
+
def routerIP()
|
156
|
+
joinThread()
|
157
|
+
@data.urlbase.sub(/^.*\//,"").sub(/\:.*/,"")
|
158
|
+
end
|
159
|
+
|
160
|
+
# Returns the status of the router which is an array of 3 elements.
|
161
|
+
# Connection status, Last error, Uptime.
|
162
|
+
def status()
|
163
|
+
joinThread()
|
164
|
+
lastconnerror = getCString()
|
165
|
+
status = getCString()
|
166
|
+
uptime = 0
|
167
|
+
begin
|
168
|
+
uptime_uint = MiniUPnP.new_uintp()
|
169
|
+
r = MiniUPnP.UPNP_GetStatusInfo(@urls.controlURL,
|
170
|
+
@data.servicetype, status, uptime_uint,
|
171
|
+
lastconnerror) != 0
|
172
|
+
if r != 0 then
|
173
|
+
raise UPnPException.new, "Error while retriving status info. #{code2error(r)}."
|
174
|
+
end
|
175
|
+
uptime = MiniUPnP.uintp_value(uptime_uint)
|
176
|
+
rescue
|
177
|
+
raise
|
178
|
+
ensure
|
179
|
+
MiniUPnP.delete_uintp(uptime_uint)
|
180
|
+
end
|
181
|
+
return status.rstrip,lastconnerror.rstrip,uptime
|
182
|
+
end
|
183
|
+
|
184
|
+
# Router connection information
|
185
|
+
def connectionType()
|
186
|
+
joinThread()
|
187
|
+
type = getCString()
|
188
|
+
if MiniUPnP.UPNP_GetConnectionTypeInfo(@urls.controlURL,
|
189
|
+
@data.servicetype,type) != 0 then
|
190
|
+
raise UPnPException.new, "Error while retriving connection info."
|
191
|
+
end
|
192
|
+
type.rstrip
|
193
|
+
end
|
194
|
+
|
195
|
+
# Total bytes sent from the router to external network
|
196
|
+
def totalBytesSent()
|
197
|
+
joinThread()
|
198
|
+
v = MiniUPnP.UPNP_GetTotalBytesSent(@urls.controlURL_CIF,
|
199
|
+
@data.servicetype_CIF)
|
200
|
+
if v < 0 then
|
201
|
+
raise UPnPException.new, "Error while retriving total bytes sent."
|
202
|
+
end
|
203
|
+
return v
|
204
|
+
end
|
205
|
+
|
206
|
+
# Total bytes received from the external network.
|
207
|
+
def totalBytesReceived()
|
208
|
+
joinThread()
|
209
|
+
v = MiniUPnP.UPNP_GetTotalBytesReceived(@urls.controlURL_CIF,
|
210
|
+
@data.servicetype_CIF)
|
211
|
+
if v < 0 then
|
212
|
+
raise UPnPException.new, "Error while retriving total bytes received."
|
213
|
+
end
|
214
|
+
return v
|
215
|
+
end
|
216
|
+
|
217
|
+
# Total packets sent from the router to the external network.
|
218
|
+
def totalPacketsSent()
|
219
|
+
joinThread()
|
220
|
+
v = MiniUPnP.UPNP_GetTotalPacketsSent(@urls.controlURL_CIF,
|
221
|
+
@data.servicetype_CIF);
|
222
|
+
if v < 0 then
|
223
|
+
raise UPnPException.new, "Error while retriving total packets sent."
|
224
|
+
end
|
225
|
+
return v
|
226
|
+
end
|
227
|
+
|
228
|
+
# Total packets received from the router from the external network.
|
229
|
+
def totalPacketsReceived()
|
230
|
+
joinThread()
|
231
|
+
v = MiniUPnP.UPNP_GetTotalBytesSent(@urls.controlURL_CIF,
|
232
|
+
@data.servicetype_CIF)
|
233
|
+
if v < 0 then
|
234
|
+
raise UPnPException.new, "Error while retriving total packets received."
|
235
|
+
end
|
236
|
+
return v
|
237
|
+
end
|
238
|
+
|
239
|
+
# Returns the maximum bitrates detected from the router (may be an
|
240
|
+
# ADSL router) The result is in bytes/s.
|
241
|
+
def maxLinkBitrates()
|
242
|
+
joinThread()
|
243
|
+
up, down = 0, 0
|
244
|
+
begin
|
245
|
+
up_p = MiniUPnP.new_uintp()
|
246
|
+
down_p = MiniUPnP.new_uintp()
|
247
|
+
if MiniUPnP.UPNP_GetLinkLayerMaxBitRates(@urls.controlURL_CIF,
|
248
|
+
@data.servicetype_CIF,
|
249
|
+
down_p,up_p) != 0 then
|
250
|
+
raise UPnPException.new, "Error while retriving maximum link bitrates."
|
251
|
+
end
|
252
|
+
up = MiniUPnP.uintp_value(up_p)
|
253
|
+
down = MiniUPnP.uintp_value(down_p)
|
254
|
+
rescue
|
255
|
+
raise
|
256
|
+
ensure
|
257
|
+
MiniUPnP.delete_uintp(up_p)
|
258
|
+
MiniUPnP.delete_uintp(down_p)
|
259
|
+
end
|
260
|
+
return down,up
|
261
|
+
end
|
262
|
+
|
263
|
+
# An array of mappings registered on the router
|
264
|
+
def portMappings()
|
265
|
+
joinThread()
|
266
|
+
i, r = 0, 0
|
267
|
+
mappings = Array.new
|
268
|
+
while r == 0
|
269
|
+
rhost = getCString()
|
270
|
+
enabled = getCString()
|
271
|
+
duration = getCString()
|
272
|
+
description = getCString()
|
273
|
+
nport = getCString()
|
274
|
+
lport = getCString()
|
275
|
+
duration = getCString()
|
276
|
+
client = getCString()
|
277
|
+
protocol = getCString()
|
278
|
+
|
279
|
+
r = MiniUPnP.UPNP_GetGenericPortMappingEntry(@urls.controlURL,
|
280
|
+
@data.servicetype,i.to_s,nport,client,lport,
|
281
|
+
protocol,description,enabled,rhost,duration)
|
282
|
+
if r != 0 then
|
283
|
+
break;
|
284
|
+
end
|
285
|
+
i = i+1
|
286
|
+
mappings << PortMapping.new(client.rstrip,lport.rstrip.to_i,
|
287
|
+
nport.rstrip.to_i,protocol.rstrip,
|
288
|
+
description.rstrip,enabled.rstrip,
|
289
|
+
rhost.rstrip,duration.rstrip)
|
290
|
+
end
|
291
|
+
return mappings
|
292
|
+
end
|
293
|
+
|
294
|
+
# Get the mapping registered for a specific port and protocol
|
295
|
+
def portMapping(nport,proto)
|
296
|
+
checkProto(proto)
|
297
|
+
checkPort(nport)
|
298
|
+
if nport.to_i == 0 then
|
299
|
+
raise ArgumentError, "Port must be an int value and greater then 0."
|
300
|
+
end
|
301
|
+
joinThread()
|
302
|
+
client = getCString()
|
303
|
+
lport = getCString()
|
304
|
+
if MiniUPnP.UPNP_GetSpecificPortMappingEntry(@urls.controlURL,
|
305
|
+
@data.servicetype, nport.to_s,proto,
|
306
|
+
client,lport) != 0 then
|
307
|
+
raise UPnPException.new, "Error while retriving the port mapping."
|
308
|
+
end
|
309
|
+
return client.rstrip, lport.rstrip.to_i
|
310
|
+
end
|
311
|
+
|
312
|
+
# Add a port mapping on the router. Parametes are: network
|
313
|
+
# port, local port, description, protocol, ip address to
|
314
|
+
# register (or do not specify it to register for yours).
|
315
|
+
# Protocol must be Protocol::TCP or Protocol::UDP
|
316
|
+
def addPortMapping(nport,lport,proto,desc,client=nil)
|
317
|
+
checkProto(proto)
|
318
|
+
checkPort(nport)
|
319
|
+
checkPort(lport)
|
320
|
+
joinThread()
|
321
|
+
client ||= @lan if client == nil
|
322
|
+
r = MiniUPnP.UPNP_AddPortMapping(@urls.controlURL,@data.servicetype,
|
323
|
+
nport.to_s,lport.to_s,client,desc,proto)
|
324
|
+
if r != 0 then
|
325
|
+
raise UPnPException.new , "Failed add mapping: #{code2error(r)}."
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# Delete the port mapping for specified network port and protocol
|
330
|
+
def deletePortMapping(nport,proto)
|
331
|
+
checkProto(proto)
|
332
|
+
checkPort(nport)
|
333
|
+
joinThread()
|
334
|
+
r = MiniUPnP.UPNP_DeletePortMapping(@urls.controlURL,@data.servicetype,
|
335
|
+
nport.to_s,proto)
|
336
|
+
if r != 0 then
|
337
|
+
raise UPnPException.new , "Failed delete mapping: #{code2error(r)}."
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
private
|
342
|
+
|
343
|
+
# Generates an empty string to use with the library
|
344
|
+
def getCString(len=128)
|
345
|
+
"\0"*len
|
346
|
+
end
|
347
|
+
|
348
|
+
# Method to wait until the scan is complete
|
349
|
+
def joinThread()
|
350
|
+
if @igd_thread != nil && Thread.current != @igd_thread then
|
351
|
+
@igd_thread.join()
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
# Check that the protocol is a correct value
|
356
|
+
def checkProto(proto)
|
357
|
+
if proto != Protocol::UDP && proto != Protocol::TCP then
|
358
|
+
raise ArgumentError, "Unknown protocol #{proto}, only Protocol::TCP and Protocol::UDP are valid."
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def checkPort(port)
|
363
|
+
iport = port.to_i
|
364
|
+
if port.to_i != port || iport < 1 || iport > 65535 then
|
365
|
+
raise ArgumentError, "Port must be an integer beetween 1 and 65535."
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
def code2error(code)
|
370
|
+
case code
|
371
|
+
when 402
|
372
|
+
"402 Invalid Args"
|
373
|
+
when 501
|
374
|
+
"501 Action Failed"
|
375
|
+
when 713
|
376
|
+
"713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds"
|
377
|
+
when 714
|
378
|
+
"714 NoSuchEntryInArray - The specified value does not exist in the array"
|
379
|
+
when 715
|
380
|
+
"715 WildCardNotPermittedInSrcIP - The source IP address cannot be wild-carded"
|
381
|
+
when 716
|
382
|
+
"716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded"
|
383
|
+
when 718
|
384
|
+
"718 ConflictInMappingEntry - The port mapping entry specified conflicts with a mapping assigned previously to another client"
|
385
|
+
when 724
|
386
|
+
"724 SamePortValuesRequired - Internal and External port values must be the same"
|
387
|
+
when 725
|
388
|
+
"725 OnlyPermanentLeasesSupported - The NAT implementation only supports permanent lease times on port mappings"
|
389
|
+
when 726
|
390
|
+
"726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard and cannot be a specific IP address or DNS name"
|
391
|
+
when 727
|
392
|
+
"727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and cannot be a specific port value"
|
393
|
+
else
|
394
|
+
"Unknown Error - #{code2error(r)}"
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|