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.
@@ -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
+