mupnp 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|