aboutsummaryrefslogtreecommitdiff
path: root/src/nat
diff options
context:
space:
mode:
authorMoon <moon@140774ce-b5e7-0310-ab8b-a85725594a96>2009-10-25 09:44:36 +0000
committerMoon <moon@140774ce-b5e7-0310-ab8b-a85725594a96>2009-10-25 09:44:36 +0000
commitb94248cf8b37caaea9436d9973a6641eaeca90cb (patch)
treed612aa43f7a899630e4a237422a6db9123078143 /src/nat
parent38fea0d2e4fe5410fcba7b115080a464f9af1930 (diff)
downloadgnunet-b94248cf8b37caaea9436d9973a6641eaeca90cb.tar.gz
gnunet-b94248cf8b37caaea9436d9973a6641eaeca90cb.zip
initial NAT lib commit (UPnP and NAT-PMP support)
Diffstat (limited to 'src/nat')
-rw-r--r--src/nat/Makefile.am44
-rw-r--r--src/nat/libnatpmp/Makefile.am16
-rw-r--r--src/nat/libnatpmp/README4
-rw-r--r--src/nat/libnatpmp/declspec.h14
-rw-r--r--src/nat/libnatpmp/getgateway.c530
-rw-r--r--src/nat/libnatpmp/getgateway.h32
-rw-r--r--src/nat/libnatpmp/natpmp.c405
-rw-r--r--src/nat/libnatpmp/natpmp.h200
-rw-r--r--src/nat/miniupnp/Makefile.am31
-rw-r--r--src/nat/miniupnp/README3
-rw-r--r--src/nat/miniupnp/bsdqueue.h531
-rw-r--r--src/nat/miniupnp/codelength.h23
-rw-r--r--src/nat/miniupnp/declspec.h14
-rw-r--r--src/nat/miniupnp/igd_desc_parse.c126
-rw-r--r--src/nat/miniupnp/igd_desc_parse.h47
-rw-r--r--src/nat/miniupnp/minisoap.c106
-rw-r--r--src/nat/miniupnp/minisoap.h14
-rw-r--r--src/nat/miniupnp/minissdpc.c111
-rw-r--r--src/nat/miniupnp/minissdpc.h14
-rw-r--r--src/nat/miniupnp/miniupnpc.c890
-rw-r--r--src/nat/miniupnp/miniupnpc.h120
-rw-r--r--src/nat/miniupnp/miniupnpcstrings.h14
-rw-r--r--src/nat/miniupnp/miniwget.c224
-rw-r--r--src/nat/miniupnp/miniwget.h28
-rw-r--r--src/nat/miniupnp/minixml.c200
-rw-r--r--src/nat/miniupnp/minixml.h37
-rw-r--r--src/nat/miniupnp/upnpcommands.c606
-rw-r--r--src/nat/miniupnp/upnpcommands.h189
-rw-r--r--src/nat/miniupnp/upnpreplyparse.c122
-rw-r--r--src/nat/miniupnp/upnpreplyparse.h60
-rw-r--r--src/nat/nat.c375
-rw-r--r--src/nat/natpmp.c297
-rw-r--r--src/nat/natpmp.h44
-rw-r--r--src/nat/upnp.c357
-rw-r--r--src/nat/upnp.h43
35 files changed, 5871 insertions, 0 deletions
diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am
new file mode 100644
index 000000000..dc881a302
--- /dev/null
+++ b/src/nat/Makefile.am
@@ -0,0 +1,44 @@
1SUBDIRS = miniupnp libnatpmp
2
3INCLUDES = -I$(top_srcdir)/src/include
4
5if MINGW
6 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
7endif
8
9if USE_COVERAGE
10 AM_CFLAGS = -fprofile-arcs -ftest-coverage
11endif
12
13lib_LTLIBRARIES = libgnunetnat.la
14
15libgnunetnat_la_SOURCES = \
16 upnp.c upnp.h \
17 natpmp.c natpmp.h \
18 nat.c nat.h
19
20libgnunetnat_la_CFLAGS = \
21 -I$(top_scrdir)/include
22
23libgnunetnat_la_LIBADD = \
24 $(top_builddir)/src/util/libgnunetutil.la \
25 $(GN_LIBINTL) @EXT_LIBS@
26
27libgnunetnat_la_LDFLAGS = \
28 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
29 -version-info 0:0:0
30
31check_PROGRAMS = \
32 test-nat
33
34TESTS = $(check_PROGRAMS)
35
36test_nat_SOURCES = \
37 test_nat.c
38
39test_nat_LDADD = \
40 $(top_builddir)/src/nat/libgnunetnat.la \
41 $(top_builddir)/src/util/libgnunetutil.la \
42 $(top_builddir)/src/nat/miniupnp/libminiupnp.a \
43 $(top_builddir)/src/nat/libnatpmp/libnatpmp.a
44
diff --git a/src/nat/libnatpmp/Makefile.am b/src/nat/libnatpmp/Makefile.am
new file mode 100644
index 000000000..923ab169d
--- /dev/null
+++ b/src/nat/libnatpmp/Makefile.am
@@ -0,0 +1,16 @@
1noinst_LIBRARIES = libnatpmp.a
2
3AM_CPPFLAGS = -DENABLE_STRNATPMPERR
4
5libnatpmp_a_SOURCES = \
6 getgateway.c \
7 natpmp.c
8
9noinst_HEADERS = \
10 declspec.h \
11 getgateway.h \
12 natpmp.h
13
14extra_DIST = \
15 README \
16 LICENSE
diff --git a/src/nat/libnatpmp/README b/src/nat/libnatpmp/README
new file mode 100644
index 000000000..50fdd1093
--- /dev/null
+++ b/src/nat/libnatpmp/README
@@ -0,0 +1,4 @@
1libnatpmp is written by Thomas Bernard.
2Its homepage is http://miniupnp.tuxfamily.org/libnatpmp.html
3This code is from the libnatpmp-20090310 snapshot
4
diff --git a/src/nat/libnatpmp/declspec.h b/src/nat/libnatpmp/declspec.h
new file mode 100644
index 000000000..6c817977a
--- /dev/null
+++ b/src/nat/libnatpmp/declspec.h
@@ -0,0 +1,14 @@
1#ifndef __DECLSPEC_H__
2#define __DECLSPEC_H__
3
4#if defined(WIN32) && !defined(STATICLIB)
5#ifdef NATPMP_EXPORTS
6#define LIBSPEC __declspec(dllexport)
7#else
8#define LIBSPEC __declspec(dllimport)
9#endif
10#else
11#define LIBSPEC
12#endif
13
14#endif
diff --git a/src/nat/libnatpmp/getgateway.c b/src/nat/libnatpmp/getgateway.c
new file mode 100644
index 000000000..10e0f1e41
--- /dev/null
+++ b/src/nat/libnatpmp/getgateway.c
@@ -0,0 +1,530 @@
1/* $Id: getgateway.c,v 1.13 2009/03/10 10:15:31 nanard Exp $ */
2/* libnatpmp
3 * Copyright (c) 2007-2008, Thomas BERNARD <miniupnp@free.fr>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
16#include <stdio.h>
17#include <ctype.h>
18#include <string.h>
19#ifndef WIN32
20#include <netinet/in.h>
21#endif
22#include <sys/param.h>
23/* There is no portable method to get the default route gateway.
24 * So below are three differents functions implementing this.
25 * Parsing /proc/net/route is for linux.
26 * sysctl is the way to access such informations on BSD systems.
27 * Many systems should provide route information through raw PF_ROUTE
28 * sockets. */
29#ifdef __linux__
30#define USE_PROC_NET_ROUTE
31#undef USE_SOCKET_ROUTE
32#undef USE_SYSCTL_NET_ROUTE
33#endif
34
35#ifdef BSD
36#undef USE_PROC_NET_ROUTE
37#define USE_SOCKET_ROUTE
38#undef USE_SYSCTL_NET_ROUTE
39#endif
40
41#ifdef __APPLE__
42#undef USE_PROC_NET_ROUTE
43#undef USE_SOCKET_ROUTE
44#define USE_SYSCTL_NET_ROUTE
45#endif
46
47#if (defined(sun) && defined(__SVR4))
48#undef USE_PROC_NET_ROUTE
49#define USE_SOCKET_ROUTE
50#undef USE_SYSCTL_NET_ROUTE
51#endif
52
53#ifdef WIN32
54#undef USE_PROC_NET_ROUTE
55#undef USE_SOCKET_ROUTE
56#undef USE_SYSCTL_NET_ROUTE
57#define USE_WIN32_CODE
58#endif
59
60#ifdef USE_PROC_NET_ROUTE
61#include <arpa/inet.h>
62#endif
63#ifdef USE_SYSCTL_NET_ROUTE
64#include <stdlib.h>
65#include <sys/sysctl.h>
66#include <sys/socket.h>
67#include <net/route.h>
68#endif
69#ifdef USE_SOCKET_ROUTE
70#include <unistd.h>
71#include <string.h>
72#include <sys/socket.h>
73#include <net/if.h>
74#include <net/route.h>
75#endif
76#ifdef WIN32
77#include <unknwn.h>
78#include <winreg.h>
79#define MAX_KEY_LENGTH 255
80#define MAX_VALUE_LENGTH 16383
81#endif
82#include "getgateway.h"
83
84#ifndef WIN32
85#define SUCCESS (0)
86#define FAILED (-1)
87#endif
88
89#ifdef USE_PROC_NET_ROUTE
90int
91getdefaultgateway (int *af, u_int8_t addr[16])
92{
93 unsigned int tmp;
94 u_int8_t d[16];
95 char buf[256];
96 int line = 0;
97 FILE *f;
98 char *p;
99 int i;
100 f = fopen ("/proc/net/route", "r");
101 if (!f)
102 return FAILED;
103 while (fgets (buf, sizeof (buf), f))
104 {
105 if (line > 0)
106 {
107 p = buf;
108 while (*p && !isspace (*p))
109 p++;
110 while (*p && isspace (*p))
111 p++;
112 for (i = 0; i < 16; i++)
113 {
114 if (sscanf (p, "%2X", &tmp) < 1)
115 d[i] = tmp;
116 else
117 break;
118 }
119
120 if (i == 8) /* IPv4 address on 8 hex chars */
121 {
122 /* Move the 32 bits address to the end of the array */
123 for (i = 4; i < 16; i++)
124 d[i] = 0;
125
126 for (i = 0; i < 4; i++)
127 {
128 d[i+12] = d[i];
129 d[i] = 0;
130 }
131 memcpy (addr, d, 16 * sizeof (u_int8_t));
132 *af = AF_INET;
133 fclose (f);
134 return SUCCESS;
135 }
136 else if (i == 16) /* IPv6 address on 16 hex chars,
137 * or IPv4 address padded with 0 */
138 {
139 memcpy (addr, d, 16 * sizeof (u_int8_t));
140 /* Check at what byte the actual address starts */
141 for (i = 0; i <= 12; i++)
142 if (addr[i]) break;
143
144 if (i == 12)
145 {
146 *af = AF_INET;
147 fclose (f);
148 return SUCCESS;
149 }
150 else if (i == 0)
151 {
152 *af = AF_INET6;
153 fclose (f);
154 return SUCCESS;
155 }
156 }
157 }
158 line++;
159 }
160 /* default route not found ! */
161 if (f)
162 fclose (f);
163 return FAILED;
164}
165#endif /* #ifdef USE_PROC_NET_ROUTE */
166
167
168#ifdef USE_SYSCTL_NET_ROUTE
169
170#define ROUNDUP(a) \
171 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
172
173int
174getdefaultgateway (int *af, u_int8_t addr[16])
175{
176#if 0
177 /* net.route.0.inet.dump.0.0 ? */
178 int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET,
179 NET_RT_DUMP, 0, 0 /*tableid */
180 };
181#endif
182 /* net.route.0.inet.flags.gateway */
183 int mib[] = { CTL_NET, PF_ROUTE, 0, AF_INET,
184 NET_RT_FLAGS, RTF_GATEWAY
185 };
186 size_t l;
187 char *buf, *p;
188 struct rt_msghdr *rt;
189 struct sockaddr *sa;
190 struct sockaddr *sa_tab[RTAX_MAX];
191 int i;
192 int r = FAILED;
193 if (sysctl (mib, sizeof (mib) / sizeof (int), 0, &l, 0, 0) < 0)
194 {
195 return FAILED;
196 }
197 if (l > 0)
198 {
199 buf = malloc (l);
200 if (sysctl (mib, sizeof (mib) / sizeof (int), buf, &l, 0, 0) < 0)
201 {
202 free (buf);
203 return FAILED;
204 }
205 for (p = buf; p < buf + l; p += rt->rtm_msglen)
206 {
207 rt = (struct rt_msghdr *) p;
208 sa = (struct sockaddr *) (rt + 1);
209 for (i = 0; i < RTAX_MAX; i++)
210 {
211 if (rt->rtm_addrs & (1 << i))
212 {
213 sa_tab[i] = sa;
214 sa =
215 (struct sockaddr *) ((char *) sa + ROUNDUP (sa->sa_len));
216 }
217 else
218 {
219 sa_tab[i] = NULL;
220 }
221 }
222 if (((rt->rtm_addrs & (RTA_DST | RTA_GATEWAY)) ==
223 (RTA_DST | RTA_GATEWAY))
224 && sa_tab[RTAX_DST]->sa_family == AF_INET
225 && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET)
226 {
227 if (((struct sockaddr_in *) sa_tab[RTAX_DST])->sin_addr.
228 s_addr == 0)
229 {
230 *addr =
231 ((struct sockaddr_in *) (sa_tab[RTAX_GATEWAY]))->sin_addr.
232 s_addr;
233 *af = AF_INET;
234 r = SUCCESS;
235 }
236 }
237 else if (((rt->rtm_addrs & (RTA_DST | RTA_GATEWAY)) ==
238 (RTA_DST | RTA_GATEWAY))
239 && sa_tab[RTAX_DST]->sa_family == AF_INET6
240 && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET6)
241 {
242 if (((struct sockaddr_in6 *) sa_tab[RTAX_DST])->sin6_addr == 0)
243 {
244 *addr =
245 ((struct sockaddr_in6 *) (sa_tab[RTAX_GATEWAY]))->sin6_addr;
246 *af = AF_INET6;
247 r = SUCCESS;
248 }
249 }
250 }
251 free (buf);
252 }
253 return r;
254}
255#endif /* #ifdef USE_SYSCTL_NET_ROUTE */
256
257
258#ifdef USE_SOCKET_ROUTE
259/* Thanks to Darren Kenny for this code */
260#define NEXTADDR(w, u) \
261 if (rtm_addrs & (w)) {\
262 l = sizeof(struct sockaddr); memmove(cp, &(u), l); cp += l;\
263 }
264
265#define rtm m_rtmsg.m_rtm
266
267struct
268{
269 struct rt_msghdr m_rtm;
270 char m_space[512];
271} m_rtmsg;
272
273int
274getdefaultgateway (int *af, u_int8_t addr[16])
275{
276 int s, seq, l, rtm_addrs, i;
277 pid_t pid;
278 struct sockaddr so_dst, so_mask;
279 char *cp = m_rtmsg.m_space;
280 struct sockaddr *gate = NULL, *sa;
281 struct rt_msghdr *msg_hdr;
282
283 pid = getpid ();
284 seq = 0;
285 rtm_addrs = RTA_DST | RTA_NETMASK;
286
287 memset (&so_dst, 0, sizeof (so_dst));
288 memset (&so_mask, 0, sizeof (so_mask));
289 memset (&rtm, 0, sizeof (struct rt_msghdr));
290
291 rtm.rtm_type = RTM_GET;
292 rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
293 rtm.rtm_version = RTM_VERSION;
294 rtm.rtm_seq = ++seq;
295 rtm.rtm_addrs = rtm_addrs;
296
297 so_dst.sa_family = AF_INET;
298 so_mask.sa_family = AF_INET;
299
300 NEXTADDR (RTA_DST, so_dst);
301 NEXTADDR (RTA_NETMASK, so_mask);
302
303 rtm.rtm_msglen = l = cp - (char *) &m_rtmsg;
304
305 s = socket (PF_ROUTE, SOCK_RAW, 0);
306
307 if (write (s, (char *) &m_rtmsg, l) < 0)
308 {
309 close (s);
310 return FAILED;
311 }
312
313 do
314 {
315 l = read (s, (char *) &m_rtmsg, sizeof (m_rtmsg));
316 }
317 while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid));
318
319 close (s);
320
321 msg_hdr = &rtm;
322
323 cp = ((char *) (msg_hdr + 1));
324 if (msg_hdr->rtm_addrs)
325 {
326 for (i = 1; i; i <<= 1)
327 if (i & msg_hdr->rtm_addrs)
328 {
329 sa = (struct sockaddr *) cp;
330 if (i == RTA_GATEWAY)
331 gate = sa;
332
333 cp += sizeof (struct sockaddr);
334 }
335 }
336 else
337 {
338 return FAILED;
339 }
340
341
342 if (gate != NULL && ((struct sockaddr_in *) gate)->sa_family == AF_INET)
343 {
344 *addr = ((struct sockaddr_in *) gate)->sin_addr.s_addr;
345 *af = AF_INET;
346 return SUCCESS;
347 }
348 else if (gate != NULL && ((struct sockaddr_in *) gate)->sa_family == AF_INET6)
349 {
350 *addr = ((struct sockaddr_in6 *) gate)->sin6_addr.s6_addr;
351 *af = AF_INET6;
352 return SUCCESS;
353 }
354 else
355 {
356 return FAILED;
357 }
358}
359#endif /* #ifdef USE_SOCKET_ROUTE */
360
361#ifdef USE_WIN32_CODE
362int
363getdefaultgateway (int *af, u_int8_t addr[16])
364{
365 HKEY networkCardsKey;
366 HKEY networkCardKey;
367 HKEY interfacesKey;
368 HKEY interfaceKey;
369 DWORD i = 0;
370 DWORD numSubKeys = 0;
371 TCHAR keyName[MAX_KEY_LENGTH];
372 DWORD keyNameLength = MAX_KEY_LENGTH;
373 TCHAR keyValue[MAX_VALUE_LENGTH];
374 DWORD keyValueLength = MAX_VALUE_LENGTH;
375 DWORD keyValueType = REG_SZ;
376 TCHAR gatewayValue[MAX_VALUE_LENGTH];
377 DWORD gatewayValueLength = MAX_VALUE_LENGTH;
378 DWORD gatewayValueType = REG_MULTI_SZ;
379 int done = 0;
380
381 char networkCardsPath[] =
382 "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards";
383 char interfacesPath[] =
384 "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces";
385
386 // The windows registry lists its primary network devices in the following location:
387 // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards
388 //
389 // Each network device has its own subfolder, named with an index, with various properties:
390 // -NetworkCards
391 // -5
392 // -Description = Broadcom 802.11n Network Adapter
393 // -ServiceName = {E35A72F8-5065-4097-8DFE-C7790774EE4D}
394 // -8
395 // -Description = Marvell Yukon 88E8058 PCI-E Gigabit Ethernet Controller
396 // -ServiceName = {86226414-5545-4335-A9D1-5BD7120119AD}
397 //
398 // The above service name is the name of a subfolder within:
399 // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces
400 //
401 // There may be more subfolders in this interfaces path than listed in the network cards path above:
402 // -Interfaces
403 // -{3a539854-6a70-11db-887c-806e6f6e6963}
404 // -DhcpIPAddress = 0.0.0.0
405 // -[more]
406 // -{E35A72F8-5065-4097-8DFE-C7790774EE4D}
407 // -DhcpIPAddress = 10.0.1.4
408 // -DhcpDefaultGateway = 10.0.1.1
409 // -[more]
410 // -{86226414-5545-4335-A9D1-5BD7120119AD}
411 // -DhcpIpAddress = 10.0.1.5
412 // -DhcpDefaultGateay = 10.0.1.1
413 // -[more]
414 //
415 // In order to extract this information, we enumerate each network card, and extract the ServiceName value.
416 // This is then used to open the interface subfolder, and attempt to extract a DhcpDefaultGateway value.
417 // Once one is found, we're done.
418 //
419 // It may be possible to simply enumerate the interface folders until we find one with a DhcpDefaultGateway value.
420 // However, the technique used is the technique most cited on the web, and we assume it to be more correct.
421
422 if (ERROR_SUCCESS != RegOpenKeyEx (HKEY_LOCAL_MACHINE, // Open registry key or predifined key
423 networkCardsPath, // Name of registry subkey to open
424 0, // Reserved - must be zero
425 KEY_READ, // Mask - desired access rights
426 &networkCardsKey)) // Pointer to output key
427 {
428 // Unable to open network cards keys
429 return -1;
430 }
431
432 if (ERROR_SUCCESS != RegOpenKeyEx (HKEY_LOCAL_MACHINE, // Open registry key or predefined key
433 interfacesPath, // Name of registry subkey to open
434 0, // Reserved - must be zero
435 KEY_READ, // Mask - desired access rights
436 &interfacesKey)) // Pointer to output key
437 {
438 // Unable to open interfaces key
439 RegCloseKey (networkCardsKey);
440 return -1;
441 }
442
443 // Figure out how many subfolders are within the NetworkCards folder
444 RegQueryInfoKey (networkCardsKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL,
445 NULL, NULL, NULL, NULL, NULL);
446
447 //printf( "Number of subkeys: %u\n", (unsigned int)numSubKeys);
448
449 // Enumrate through each subfolder within the NetworkCards folder
450 for (i = 0; i < numSubKeys && !done; i++)
451 {
452 keyNameLength = MAX_KEY_LENGTH;
453 if (ERROR_SUCCESS == RegEnumKeyEx (networkCardsKey, // Open registry key
454 i, // Index of subkey to retrieve
455 keyName, // Buffer that receives the name of the subkey
456 &keyNameLength, // Variable that receives the size of the above buffer
457 NULL, // Reserved - must be NULL
458 NULL, // Buffer that receives the class string
459 NULL, // Variable that receives the size of the above buffer
460 NULL)) // Variable that receives the last write time of subkey
461 {
462 if (RegOpenKeyEx
463 (networkCardsKey, keyName, 0, KEY_READ,
464 &networkCardKey) == ERROR_SUCCESS)
465 {
466 keyValueLength = MAX_VALUE_LENGTH;
467 if (ERROR_SUCCESS == RegQueryValueEx (networkCardKey, // Open registry key
468 "ServiceName", // Name of key to query
469 NULL, // Reserved - must be NULL
470 &keyValueType, // Receives value type
471 keyValue, // Receives value
472 &keyValueLength)) // Receives value length in bytes
473 {
474 //printf("keyValue: %s\n", keyValue);
475
476 if (RegOpenKeyEx
477 (interfacesKey, keyValue, 0, KEY_READ,
478 &interfaceKey) == ERROR_SUCCESS)
479 {
480 gatewayValueLength = MAX_VALUE_LENGTH;
481 if (ERROR_SUCCESS == RegQueryValueEx (interfaceKey, // Open registry key
482 "DhcpDefaultGateway", // Name of key to query
483 NULL, // Reserved - must be NULL
484 &gatewayValueType, // Receives value type
485 gatewayValue, // Receives value
486 &gatewayValueLength)) // Receives value length in bytes
487 {
488 // Check to make sure it's a string
489 if (gatewayValueType == REG_MULTI_SZ
490 || gatewayValueType == REG_SZ)
491 {
492 //printf("gatewayValue: %s\n", gatewayValue);
493 done = 1;
494 }
495 }
496 else if (ERROR_SUCCESS == RegQueryValueEx (interfaceKey, // Open registry key
497 "DefaultGateway", // Name of key to query
498 NULL, // Reserved - must be NULL
499 &gatewayValueType, // Receives value type
500 gatewayValue, // Receives value
501 &gatewayValueLength)) // Receives value length in bytes
502 {
503 // Check to make sure it's a string
504 if (gatewayValueType == REG_MULTI_SZ
505 || gatewayValueType == REG_SZ)
506 {
507 //printf("gatewayValue: %s\n", gatewayValue);
508 done = 1;
509 }
510 }
511 RegCloseKey (interfaceKey);
512 }
513 }
514 RegCloseKey (networkCardKey);
515 }
516 }
517 }
518
519 RegCloseKey (interfacesKey);
520 RegCloseKey (networkCardsKey);
521
522 if (done)
523 {
524 *addr = inet_addr (gatewayValue);
525 return 0;
526 }
527
528 return -1;
529}
530#endif /* #ifdef USE_WIN32_CODE */
diff --git a/src/nat/libnatpmp/getgateway.h b/src/nat/libnatpmp/getgateway.h
new file mode 100644
index 000000000..a9b2c82a9
--- /dev/null
+++ b/src/nat/libnatpmp/getgateway.h
@@ -0,0 +1,32 @@
1/* $Id: getgateway.h,v 1.3 2008/07/02 22:33:06 nanard Exp $ */
2/* libnatpmp
3 * Copyright (c) 2007, Thomas BERNARD <miniupnp@free.fr>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
16#ifndef __GETGATEWAY_H__
17#define __GETGATEWAY_H__
18
19#ifdef WIN32
20#include <stdint.h>
21#define in_addr_t uint32_t
22#endif
23#include "declspec.h"
24
25/* getdefaultgateway() :
26 * addr must point to an array of at least 16 u_int8 elements
27 * return value :
28 * 0 : success
29 * -1 : failure */
30LIBSPEC int getdefaultgateway (int *af, u_int8_t addr[16]);
31
32#endif
diff --git a/src/nat/libnatpmp/natpmp.c b/src/nat/libnatpmp/natpmp.c
new file mode 100644
index 000000000..6a94acca3
--- /dev/null
+++ b/src/nat/libnatpmp/natpmp.c
@@ -0,0 +1,405 @@
1/* $Id: natpmp.c,v 1.8 2008/07/02 22:33:06 nanard Exp $ */
2/* libnatpmp
3 * Copyright (c) 2007-2008, Thomas BERNARD <miniupnp@free.fr>
4 * http://miniupnp.free.fr/libnatpmp.html
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
17#include <string.h>
18#include <time.h>
19#include <sys/time.h>
20#ifdef WIN32
21#include <winsock2.h>
22#include <Ws2tcpip.h>
23#include <io.h>
24#define EWOULDBLOCK WSAEWOULDBLOCK
25#define ECONNREFUSED WSAECONNREFUSED
26#else
27#include <errno.h>
28#include <unistd.h>
29#include <fcntl.h>
30#include <sys/types.h>
31#include <sys/socket.h>
32#define closesocket close
33#endif
34#include "natpmp.h"
35#include "getgateway.h"
36
37int
38initnatpmp (natpmp_t * p)
39{
40#ifdef WIN32
41 u_long ioctlArg = 1;
42#else
43 int flags;
44#endif
45 int domain = AF_INET;
46 int gw_domain;
47 struct sockaddr_in addr;
48 struct sockaddr_in6 addr6;
49
50 if (!p)
51 return NATPMP_ERR_INVALIDARGS;
52
53 if (p->addr)
54 domain = (p->addr->sa_family == AF_INET) ? PF_INET : PF_INET6;
55
56 memset (p, 0, sizeof (natpmp_t));
57 p->s = socket (domain, SOCK_DGRAM, 0);
58 if (p->s < 0)
59 return NATPMP_ERR_SOCKETERROR;
60 /* If addr has been set, use it, else get the default from connect() */
61 if (p->addr && bind (p->s, p->addr, p->addrlen) < 0)
62 return NATPMP_ERR_BINDERROR;
63#ifdef WIN32
64 if (ioctlsocket (p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR)
65 return NATPMP_ERR_FCNTLERROR;
66#else
67 if ((flags = fcntl (p->s, F_GETFL, 0)) < 0)
68 return NATPMP_ERR_FCNTLERROR;
69 if (fcntl (p->s, F_SETFL, flags | O_NONBLOCK) < 0)
70 return NATPMP_ERR_FCNTLERROR;
71#endif
72
73 if (getdefaultgateway (&gw_domain, p->gateway) < 0)
74 return NATPMP_ERR_CANNOTGETGATEWAY;
75
76 if (domain != gw_domain)
77 return NATPMP_ERR_ADDRERROR;
78
79 if (domain == AF_INET)
80 {
81 memset (&addr, 0, sizeof (addr));
82 addr.sin_family = AF_INET;
83 addr.sin_port = htons (NATPMP_PORT);
84 memcpy (&addr.sin_addr.s_addr, p->gateway, 4 * sizeof (u_int8_t));
85#ifdef HAVE_SOCKADDR_IN_SIN_LEN
86 addr.sin_len = sizeof (addr);
87#endif
88 if (connect (p->s, (struct sockaddr *) &addr, sizeof (addr)) < 0)
89 return NATPMP_ERR_CONNECTERR;
90 }
91 else
92 {
93 memset (&addr6, 0, sizeof (addr6));
94 addr6.sin6_family = AF_INET6;
95 addr6.sin6_port = htons (NATPMP_PORT);
96 memcpy (addr6.sin6_addr.s6_addr, p->gateway, 16 * sizeof (u_int8_t));
97#ifdef HAVE_SOCKADDR_IN_SIN_LEN
98 addr6.sin6_len = sizeof (addr6);
99#endif
100 if (connect (p->s, (struct sockaddr *) &addr6, sizeof (addr6)) < 0)
101 return NATPMP_ERR_CONNECTERR;
102 }
103
104 return 0;
105}
106
107int
108closenatpmp (natpmp_t * p)
109{
110 if (!p)
111 return NATPMP_ERR_INVALIDARGS;
112 if (closesocket (p->s) < 0)
113 return NATPMP_ERR_CLOSEERR;
114 return 0;
115}
116
117static int
118sendpendingrequest (natpmp_t * p)
119{
120 int r;
121/* struct sockaddr_in addr;*/
122 if (!p)
123 return NATPMP_ERR_INVALIDARGS;
124/* memset(&addr, 0, sizeof(addr));
125 addr.sin_family = AF_INET;
126 addr.sin_port = htons(NATPMP_PORT);
127 addr.sin_addr.s_addr = p->gateway;
128 r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0,
129 (struct sockaddr *)&addr, sizeof(addr));*/
130 r = (int) send (p->s, p->pending_request, p->pending_request_len, 0);
131 return (r < 0) ? NATPMP_ERR_SENDERR : r;
132}
133
134static int
135sendnatpmprequest (natpmp_t * p)
136{
137 int n;
138 if (!p)
139 return NATPMP_ERR_INVALIDARGS;
140 /* TODO : check if no request is allready pending */
141 p->has_pending_request = 1;
142 p->try_number = 1;
143 n = sendpendingrequest (p);
144 gettimeofday (&p->retry_time, NULL); // check errors !
145 p->retry_time.tv_usec += 250000; /* add 250ms */
146 if (p->retry_time.tv_usec >= 1000000)
147 {
148 p->retry_time.tv_usec -= 1000000;
149 p->retry_time.tv_sec++;
150 }
151 return n;
152}
153
154int
155getnatpmprequesttimeout (natpmp_t * p, struct timeval *timeout)
156{
157 struct timeval now;
158 if (!p || !timeout)
159 return NATPMP_ERR_INVALIDARGS;
160 if (!p->has_pending_request)
161 return NATPMP_ERR_NOPENDINGREQ;
162 if (gettimeofday (&now, NULL) < 0)
163 return NATPMP_ERR_GETTIMEOFDAYERR;
164 timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec;
165 timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec;
166 if (timeout->tv_usec < 0)
167 {
168 timeout->tv_usec += 1000000;
169 timeout->tv_sec--;
170 }
171 return 0;
172}
173
174int
175sendpublicaddressrequest (natpmp_t * p)
176{
177 if (!p)
178 return NATPMP_ERR_INVALIDARGS;
179 //static const unsigned char request[] = { 0, 0 };
180 p->pending_request[0] = 0;
181 p->pending_request[1] = 0;
182 p->pending_request_len = 2;
183 // TODO: return 0 instead of sizeof(request) ??
184 return sendnatpmprequest (p);
185}
186
187int
188sendnewportmappingrequest (natpmp_t * p, int protocol,
189 uint16_t privateport, uint16_t publicport,
190 uint32_t lifetime)
191{
192 if (!p
193 || (protocol != NATPMP_PROTOCOL_TCP && protocol != NATPMP_PROTOCOL_UDP))
194 return NATPMP_ERR_INVALIDARGS;
195 p->pending_request[0] = 0;
196 p->pending_request[1] = protocol;
197 p->pending_request[2] = 0;
198 p->pending_request[3] = 0;
199 *((uint16_t *) (p->pending_request + 4)) = htons (privateport);
200 *((uint16_t *) (p->pending_request + 6)) = htons (publicport);
201 *((uint32_t *) (p->pending_request + 8)) = htonl (lifetime);
202 p->pending_request_len = 12;
203 return sendnatpmprequest (p);
204}
205
206static int
207readnatpmpresponse (natpmp_t * p, natpmpresp_t * response)
208{
209 unsigned char buf[16];
210 struct sockaddr_storage addr;
211 socklen_t addrlen = sizeof (addr);
212 int n;
213 if (!p)
214 return NATPMP_ERR_INVALIDARGS;
215 n = recvfrom (p->s, buf, sizeof (buf), 0,
216 (struct sockaddr *) &addr, &addrlen);
217 if (n < 0)
218 switch (errno)
219 {
220 /*case EAGAIN: */
221 case EWOULDBLOCK:
222 n = NATPMP_TRYAGAIN;
223 break;
224 case ECONNREFUSED:
225 n = NATPMP_ERR_NOGATEWAYSUPPORT;
226 break;
227 default:
228 n = NATPMP_ERR_RECVFROM;
229 }
230 /* check that addr is correct (= gateway) */
231 else if (addr.ss_family == AF_INET && memcmp (&((struct sockaddr_in *) &addr)->sin_addr.s_addr, p->gateway, 4 * sizeof (u_int8_t)) == 0)
232 n = NATPMP_ERR_WRONGPACKETSOURCE;
233 else if (addr.ss_family == AF_INET6 && memcmp (((struct sockaddr_in6 *) &addr)->sin6_addr.s6_addr, p->gateway, 16 * sizeof (u_int8_t)) == 0)
234 n = NATPMP_ERR_WRONGPACKETSOURCE;
235 else
236 {
237 response->resultcode = ntohs (*((uint16_t *) (buf + 2)));
238 response->epoch = ntohl (*((uint32_t *) (buf + 4)));
239 if (buf[0] != 0)
240 n = NATPMP_ERR_UNSUPPORTEDVERSION;
241 else if (buf[1] < 128 || buf[1] > 130)
242 n = NATPMP_ERR_UNSUPPORTEDOPCODE;
243 else if (response->resultcode != 0)
244 {
245 switch (response->resultcode)
246 {
247 case 1:
248 n = NATPMP_ERR_UNSUPPORTEDVERSION;
249 break;
250 case 2:
251 n = NATPMP_ERR_NOTAUTHORIZED;
252 break;
253 case 3:
254 n = NATPMP_ERR_NETWORKFAILURE;
255 break;
256 case 4:
257 n = NATPMP_ERR_OUTOFRESOURCES;
258 break;
259 case 5:
260 n = NATPMP_ERR_UNSUPPORTEDOPCODE;
261 break;
262 default:
263 n = NATPMP_ERR_UNDEFINEDERROR;
264 }
265 }
266 else
267 {
268 response->type = buf[1] & 0x7f;
269 if (buf[1] == 128)
270 //response->publicaddress.addr = *((uint32_t *)(buf + 8));
271 response->pnu.publicaddress.addr.s_addr =
272 *((uint32_t *) (buf + 8));
273 else
274 {
275 response->pnu.newportmapping.privateport =
276 ntohs (*((uint16_t *) (buf + 8)));
277 response->pnu.newportmapping.mappedpublicport =
278 ntohs (*((uint16_t *) (buf + 10)));
279 response->pnu.newportmapping.lifetime =
280 ntohl (*((uint32_t *) (buf + 12)));
281 }
282 n = 0;
283 }
284 }
285 return n;
286}
287
288int
289readnatpmpresponseorretry (natpmp_t * p, natpmpresp_t * response)
290{
291 int n;
292 if (!p || !response)
293 return NATPMP_ERR_INVALIDARGS;
294 if (!p->has_pending_request)
295 return NATPMP_ERR_NOPENDINGREQ;
296 n = readnatpmpresponse (p, response);
297 if (n < 0)
298 {
299 if (n == NATPMP_TRYAGAIN)
300 {
301 struct timeval now;
302 gettimeofday (&now, NULL); // check errors !
303 if (timercmp (&now, &p->retry_time, >=))
304 {
305 int delay, r;
306 if (p->try_number >= 9)
307 {
308 return NATPMP_ERR_NOGATEWAYSUPPORT;
309 }
310 /*printf("retry! %d\n", p->try_number); */
311 delay = 250 * (1 << p->try_number); // ms
312 /*for(i=0; i<p->try_number; i++)
313 delay += delay; */
314 p->retry_time.tv_sec += (delay / 1000);
315 p->retry_time.tv_usec += (delay % 1000) * 1000;
316 if (p->retry_time.tv_usec >= 1000000)
317 {
318 p->retry_time.tv_usec -= 1000000;
319 p->retry_time.tv_sec++;
320 }
321 p->try_number++;
322 r = sendpendingrequest (p);
323 if (r < 0)
324 return r;
325 }
326 }
327 }
328 else
329 {
330 p->has_pending_request = 0;
331 }
332 return n;
333}
334
335#ifdef ENABLE_STRNATPMPERR
336const char *
337strnatpmperr (int r)
338{
339 const char *s;
340 switch (r)
341 {
342 case NATPMP_ERR_INVALIDARGS:
343 s = "invalid arguments";
344 break;
345 case NATPMP_ERR_SOCKETERROR:
346 s = "socket() failed";
347 break;
348 case NATPMP_ERR_CANNOTGETGATEWAY:
349 s = "cannot get default gateway ip address";
350 break;
351 case NATPMP_ERR_CLOSEERR:
352#ifdef WIN32
353 s = "closesocket() failed";
354#else
355 s = "close() failed";
356#endif
357 break;
358 case NATPMP_ERR_RECVFROM:
359 s = "recvfrom() failed";
360 break;
361 case NATPMP_ERR_NOPENDINGREQ:
362 s = "no pending request";
363 break;
364 case NATPMP_ERR_NOGATEWAYSUPPORT:
365 s = "the gateway does not support nat-pmp";
366 break;
367 case NATPMP_ERR_CONNECTERR:
368 s = "connect() failed";
369 break;
370 case NATPMP_ERR_WRONGPACKETSOURCE:
371 s = "packet not received from the default gateway";
372 break;
373 case NATPMP_ERR_SENDERR:
374 s = "send() failed";
375 break;
376 case NATPMP_ERR_FCNTLERROR:
377 s = "fcntl() failed";
378 break;
379 case NATPMP_ERR_GETTIMEOFDAYERR:
380 s = "gettimeofday() failed";
381 break;
382 case NATPMP_ERR_UNSUPPORTEDVERSION:
383 s = "unsupported nat-pmp version error from server";
384 break;
385 case NATPMP_ERR_UNSUPPORTEDOPCODE:
386 s = "unsupported nat-pmp opcode error from server";
387 break;
388 case NATPMP_ERR_UNDEFINEDERROR:
389 s = "undefined nat-pmp server error";
390 break;
391 case NATPMP_ERR_NOTAUTHORIZED:
392 s = "not authorized";
393 break;
394 case NATPMP_ERR_NETWORKFAILURE:
395 s = "network failure";
396 break;
397 case NATPMP_ERR_OUTOFRESOURCES:
398 s = "nat-pmp server out of resources";
399 break;
400 default:
401 s = "Unknown libnatpmp error";
402 }
403 return s;
404}
405#endif
diff --git a/src/nat/libnatpmp/natpmp.h b/src/nat/libnatpmp/natpmp.h
new file mode 100644
index 000000000..70d903101
--- /dev/null
+++ b/src/nat/libnatpmp/natpmp.h
@@ -0,0 +1,200 @@
1/* $Id: natpmp.h,v 1.11 2009/02/27 22:38:05 nanard Exp $ */
2/* libnatpmp
3 * Copyright (c) 2007-2008, Thomas BERNARD <miniupnp@free.fr>
4 * http://miniupnp.free.fr/libnatpmp.html
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
17#ifndef __NATPMP_H__
18#define __NATPMP_H__
19
20/* NAT-PMP Port as defined by the NAT-PMP draft */
21#define NATPMP_PORT (5351)
22
23#include <time.h>
24#include <sys/time.h>
25#ifdef WIN32
26#include <winsock2.h>
27#include <stdint.h>
28#define in_addr_t uint32_t
29#include "declspec.h"
30#else
31#define LIBSPEC
32#include <netinet/in.h>
33#include <sys/socket.h>
34#endif
35
36typedef struct
37{
38 int s; /* socket */
39 struct sockaddr *addr;
40 socklen_t addrlen;
41 u_int8_t gateway[16]; /* default gateway (IPv4 or IPv6) */
42 int has_pending_request;
43 unsigned char pending_request[12];
44 int pending_request_len;
45 int try_number;
46 struct timeval retry_time;
47} natpmp_t;
48
49typedef struct
50{
51 uint16_t type; /* NATPMP_RESPTYPE_* */
52 uint16_t resultcode; /* NAT-PMP response code */
53 uint32_t epoch; /* Seconds since start of epoch */
54 union
55 {
56 struct
57 {
58 //in_addr_t addr;
59 struct in_addr addr;
60 } publicaddress;
61 struct
62 {
63 uint16_t privateport;
64 uint16_t mappedpublicport;
65 uint32_t lifetime;
66 } newportmapping;
67 } pnu;
68} natpmpresp_t;
69
70/* possible values for type field of natpmpresp_t */
71#define NATPMP_RESPTYPE_PUBLICADDRESS (0)
72#define NATPMP_RESPTYPE_UDPPORTMAPPING (1)
73#define NATPMP_RESPTYPE_TCPPORTMAPPING (2)
74
75/* Values to pass to sendnewportmappingrequest() */
76#define NATPMP_PROTOCOL_UDP (1)
77#define NATPMP_PROTOCOL_TCP (2)
78
79/* return values */
80/* NATPMP_ERR_INVALIDARGS : invalid arguments passed to the function */
81#define NATPMP_ERR_INVALIDARGS (-1)
82/* NATPMP_ERR_SOCKETERROR : socket() failed. check errno for details */
83#define NATPMP_ERR_SOCKETERROR (-2)
84/* NATPMP_ERR_CANNOTGETGATEWAY : can't get default gateway IP */
85#define NATPMP_ERR_CANNOTGETGATEWAY (-3)
86/* NATPMP_ERR_CLOSEERR : close() failed. check errno for details */
87#define NATPMP_ERR_CLOSEERR (-4)
88/* NATPMP_ERR_RECVFROM : recvfrom() failed. check errno for details */
89#define NATPMP_ERR_RECVFROM (-5)
90/* NATPMP_ERR_NOPENDINGREQ : readnatpmpresponseorretry() called while
91 * no NAT-PMP request was pending */
92#define NATPMP_ERR_NOPENDINGREQ (-6)
93/* NATPMP_ERR_NOGATEWAYSUPPORT : the gateway does not support NAT-PMP */
94#define NATPMP_ERR_NOGATEWAYSUPPORT (-7)
95/* NATPMP_ERR_CONNECTERR : connect() failed. check errno for details */
96#define NATPMP_ERR_CONNECTERR (-8)
97/* NATPMP_ERR_WRONGPACKETSOURCE : packet not received from the network gateway */
98#define NATPMP_ERR_WRONGPACKETSOURCE (-9)
99/* NATPMP_ERR_SENDERR : send() failed. check errno for details */
100#define NATPMP_ERR_SENDERR (-10)
101/* NATPMP_ERR_FCNTLERROR : fcntl() failed. check errno for details */
102#define NATPMP_ERR_FCNTLERROR (-11)
103/* NATPMP_ERR_GETTIMEOFDAYERR : gettimeofday() failed. check errno for details */
104#define NATPMP_ERR_GETTIMEOFDAYERR (-12)
105/* NATPMP_ERR_BINDERROR : bind() failed. check errno for details */
106#define NATPMP_ERR_BINDERROR (-13)
107/* NATPMP_ERR_ADDRERROR : gateway does not use the same inet protocol as the passed address */
108#define NATPMP_ERR_ADDRERROR (-14)
109
110/* */
111#define NATPMP_ERR_UNSUPPORTEDVERSION (-15)
112#define NATPMP_ERR_UNSUPPORTEDOPCODE (-16)
113
114/* Errors from the server : */
115#define NATPMP_ERR_UNDEFINEDERROR (-49)
116#define NATPMP_ERR_NOTAUTHORIZED (-51)
117#define NATPMP_ERR_NETWORKFAILURE (-52)
118#define NATPMP_ERR_OUTOFRESOURCES (-53)
119
120/* NATPMP_TRYAGAIN : no data available for the moment. try again later */
121#define NATPMP_TRYAGAIN (-100)
122
123/* initnatpmp()
124 * initialize a natpmp_t object
125 * Return values :
126 * 0 = OK
127 * NATPMP_ERR_INVALIDARGS
128 * NATPMP_ERR_SOCKETERROR
129 * NATPMP_ERR_FCNTLERROR
130 * NATPMP_ERR_CANNOTGETGATEWAY
131 * NATPMP_ERR_CONNECTERR */
132LIBSPEC int initnatpmp (natpmp_t * p);
133
134/* closenatpmp()
135 * close resources associated with a natpmp_t object
136 * Return values :
137 * 0 = OK
138 * NATPMP_ERR_INVALIDARGS
139 * NATPMP_ERR_CLOSEERR */
140LIBSPEC int closenatpmp (natpmp_t * p);
141
142/* sendpublicaddressrequest()
143 * send a public address NAT-PMP request to the network gateway
144 * Return values :
145 * 2 = OK (size of the request)
146 * NATPMP_ERR_INVALIDARGS
147 * NATPMP_ERR_SENDERR */
148LIBSPEC int sendpublicaddressrequest (natpmp_t * p);
149
150/* sendnewportmappingrequest()
151 * send a new port mapping NAT-PMP request to the network gateway
152 * Arguments :
153 * protocol is either NATPMP_PROTOCOL_TCP or NATPMP_PROTOCOL_UDP,
154 * lifetime is in seconds.
155 * To remove a port mapping, set lifetime to zero.
156 * To remove all port mappings to the host, set lifetime and both ports
157 * to zero.
158 * Return values :
159 * 12 = OK (size of the request)
160 * NATPMP_ERR_INVALIDARGS
161 * NATPMP_ERR_SENDERR */
162LIBSPEC int sendnewportmappingrequest (natpmp_t * p, int protocol,
163 uint16_t privateport,
164 uint16_t publicport,
165 uint32_t lifetime);
166
167/* getnatpmprequesttimeout()
168 * fills the timeval structure with the timeout duration of the
169 * currently pending NAT-PMP request.
170 * Return values :
171 * 0 = OK
172 * NATPMP_ERR_INVALIDARGS
173 * NATPMP_ERR_GETTIMEOFDAYERR
174 * NATPMP_ERR_NOPENDINGREQ */
175LIBSPEC int getnatpmprequesttimeout (natpmp_t * p, struct timeval *timeout);
176
177/* readnatpmpresponseorretry()
178 * fills the natpmpresp_t structure if possible
179 * Return values :
180 * 0 = OK
181 * NATPMP_TRYAGAIN
182 * NATPMP_ERR_INVALIDARGS
183 * NATPMP_ERR_NOPENDINGREQ
184 * NATPMP_ERR_NOGATEWAYSUPPORT
185 * NATPMP_ERR_RECVFROM
186 * NATPMP_ERR_WRONGPACKETSOURCE
187 * NATPMP_ERR_UNSUPPORTEDVERSION
188 * NATPMP_ERR_UNSUPPORTEDOPCODE
189 * NATPMP_ERR_NOTAUTHORIZED
190 * NATPMP_ERR_NETWORKFAILURE
191 * NATPMP_ERR_OUTOFRESOURCES
192 * NATPMP_ERR_UNSUPPORTEDOPCODE
193 * NATPMP_ERR_UNDEFINEDERROR */
194LIBSPEC int readnatpmpresponseorretry (natpmp_t * p, natpmpresp_t * response);
195
196#ifdef ENABLE_STRNATPMPERR
197LIBSPEC const char *strnatpmperr (int t);
198#endif
199
200#endif
diff --git a/src/nat/miniupnp/Makefile.am b/src/nat/miniupnp/Makefile.am
new file mode 100644
index 000000000..cd5e8c7b8
--- /dev/null
+++ b/src/nat/miniupnp/Makefile.am
@@ -0,0 +1,31 @@
1noinst_LIBRARIES = libminiupnp.a
2
3AM_CPPFLAGS = -DNDEBUG
4
5libminiupnp_a_SOURCES = \
6 igd_desc_parse.c \
7 minisoap.c \
8 minissdpc.c \
9 miniupnpc.c \
10 miniwget.c \
11 minixml.c \
12 upnpcommands.c \
13 upnpreplyparse.c
14
15noinst_HEADERS = \
16 bsdqueue.h \
17 codelength.h \
18 declspec.h \
19 igd_desc_parse.h \
20 minisoap.h \
21 minissdpc.h \
22 miniupnpc.h \
23 miniupnpcstrings.h \
24 miniwget.h \
25 minixml.h \
26 upnpcommands.h \
27 upnpreplyparse.h
28
29extra_DIST = \
30 README \
31 LICENSE
diff --git a/src/nat/miniupnp/README b/src/nat/miniupnp/README
new file mode 100644
index 000000000..45aec9084
--- /dev/null
+++ b/src/nat/miniupnp/README
@@ -0,0 +1,3 @@
1MiniUPnP is written by Thomas Bernard.
2Its homepage is http://miniupnp.free.fr/
3This is from miniupnpc-1.3.tar.gz
diff --git a/src/nat/miniupnp/bsdqueue.h b/src/nat/miniupnp/bsdqueue.h
new file mode 100644
index 000000000..f763172c4
--- /dev/null
+++ b/src/nat/miniupnp/bsdqueue.h
@@ -0,0 +1,531 @@
1/* $OpenBSD: queue.h,v 1.31 2005/11/25 08:06:25 otto Exp $ */
2/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
3
4/*
5 * Copyright (c) 1991, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * @(#)queue.h 8.5 (Berkeley) 8/20/94
33 */
34
35#ifndef _SYS_QUEUE_H_
36#define _SYS_QUEUE_H_
37
38/*
39 * This file defines five types of data structures: singly-linked lists,
40 * lists, simple queues, tail queues, and circular queues.
41 *
42 *
43 * A singly-linked list is headed by a single forward pointer. The elements
44 * are singly linked for minimum space and pointer manipulation overhead at
45 * the expense of O(n) removal for arbitrary elements. New elements can be
46 * added to the list after an existing element or at the head of the list.
47 * Elements being removed from the head of the list should use the explicit
48 * macro for this purpose for optimum efficiency. A singly-linked list may
49 * only be traversed in the forward direction. Singly-linked lists are ideal
50 * for applications with large datasets and few or no removals or for
51 * implementing a LIFO queue.
52 *
53 * A list is headed by a single forward pointer (or an array of forward
54 * pointers for a hash table header). The elements are doubly linked
55 * so that an arbitrary element can be removed without a need to
56 * traverse the list. New elements can be added to the list before
57 * or after an existing element or at the head of the list. A list
58 * may only be traversed in the forward direction.
59 *
60 * A simple queue is headed by a pair of pointers, one the head of the
61 * list and the other to the tail of the list. The elements are singly
62 * linked to save space, so elements can only be removed from the
63 * head of the list. New elements can be added to the list before or after
64 * an existing element, at the head of the list, or at the end of the
65 * list. A simple queue may only be traversed in the forward direction.
66 *
67 * A tail queue is headed by a pair of pointers, one to the head of the
68 * list and the other to the tail of the list. The elements are doubly
69 * linked so that an arbitrary element can be removed without a need to
70 * traverse the list. New elements can be added to the list before or
71 * after an existing element, at the head of the list, or at the end of
72 * the list. A tail queue may be traversed in either direction.
73 *
74 * A circle queue is headed by a pair of pointers, one to the head of the
75 * list and the other to the tail of the list. The elements are doubly
76 * linked so that an arbitrary element can be removed without a need to
77 * traverse the list. New elements can be added to the list before or after
78 * an existing element, at the head of the list, or at the end of the list.
79 * A circle queue may be traversed in either direction, but has a more
80 * complex end of list detection.
81 *
82 * For details on the use of these macros, see the queue(3) manual page.
83 */
84
85#ifdef QUEUE_MACRO_DEBUG
86#define _Q_INVALIDATE(a) (a) = ((void *)-1)
87#else
88#define _Q_INVALIDATE(a)
89#endif
90
91/*
92 * Singly-linked List definitions.
93 */
94#define SLIST_HEAD(name, type) \
95struct name { \
96 struct type *slh_first; /* first element */ \
97}
98
99#define SLIST_HEAD_INITIALIZER(head) \
100 { NULL }
101
102#ifdef SLIST_ENTRY
103#undef SLIST_ENTRY
104#endif
105
106#define SLIST_ENTRY(type) \
107struct { \
108 struct type *sle_next; /* next element */ \
109}
110
111/*
112 * Singly-linked List access methods.
113 */
114#define SLIST_FIRST(head) ((head)->slh_first)
115#define SLIST_END(head) NULL
116#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
117#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
118
119#define SLIST_FOREACH(var, head, field) \
120 for((var) = SLIST_FIRST(head); \
121 (var) != SLIST_END(head); \
122 (var) = SLIST_NEXT(var, field))
123
124#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
125 for ((varp) = &SLIST_FIRST((head)); \
126 ((var) = *(varp)) != SLIST_END(head); \
127 (varp) = &SLIST_NEXT((var), field))
128
129/*
130 * Singly-linked List functions.
131 */
132#define SLIST_INIT(head) { \
133 SLIST_FIRST(head) = SLIST_END(head); \
134}
135
136#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
137 (elm)->field.sle_next = (slistelm)->field.sle_next; \
138 (slistelm)->field.sle_next = (elm); \
139} while (0)
140
141#define SLIST_INSERT_HEAD(head, elm, field) do { \
142 (elm)->field.sle_next = (head)->slh_first; \
143 (head)->slh_first = (elm); \
144} while (0)
145
146#define SLIST_REMOVE_NEXT(head, elm, field) do { \
147 (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
148} while (0)
149
150#define SLIST_REMOVE_HEAD(head, field) do { \
151 (head)->slh_first = (head)->slh_first->field.sle_next; \
152} while (0)
153
154#define SLIST_REMOVE(head, elm, type, field) do { \
155 if ((head)->slh_first == (elm)) { \
156 SLIST_REMOVE_HEAD((head), field); \
157 } else { \
158 struct type *curelm = (head)->slh_first; \
159 \
160 while (curelm->field.sle_next != (elm)) \
161 curelm = curelm->field.sle_next; \
162 curelm->field.sle_next = \
163 curelm->field.sle_next->field.sle_next; \
164 _Q_INVALIDATE((elm)->field.sle_next); \
165 } \
166} while (0)
167
168/*
169 * List definitions.
170 */
171#define LIST_HEAD(name, type) \
172struct name { \
173 struct type *lh_first; /* first element */ \
174}
175
176#define LIST_HEAD_INITIALIZER(head) \
177 { NULL }
178
179#define LIST_ENTRY(type) \
180struct { \
181 struct type *le_next; /* next element */ \
182 struct type **le_prev; /* address of previous next element */ \
183}
184
185/*
186 * List access methods
187 */
188#define LIST_FIRST(head) ((head)->lh_first)
189#define LIST_END(head) NULL
190#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
191#define LIST_NEXT(elm, field) ((elm)->field.le_next)
192
193#define LIST_FOREACH(var, head, field) \
194 for((var) = LIST_FIRST(head); \
195 (var)!= LIST_END(head); \
196 (var) = LIST_NEXT(var, field))
197
198/*
199 * List functions.
200 */
201#define LIST_INIT(head) do { \
202 LIST_FIRST(head) = LIST_END(head); \
203} while (0)
204
205#define LIST_INSERT_AFTER(listelm, elm, field) do { \
206 if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
207 (listelm)->field.le_next->field.le_prev = \
208 &(elm)->field.le_next; \
209 (listelm)->field.le_next = (elm); \
210 (elm)->field.le_prev = &(listelm)->field.le_next; \
211} while (0)
212
213#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
214 (elm)->field.le_prev = (listelm)->field.le_prev; \
215 (elm)->field.le_next = (listelm); \
216 *(listelm)->field.le_prev = (elm); \
217 (listelm)->field.le_prev = &(elm)->field.le_next; \
218} while (0)
219
220#define LIST_INSERT_HEAD(head, elm, field) do { \
221 if (((elm)->field.le_next = (head)->lh_first) != NULL) \
222 (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
223 (head)->lh_first = (elm); \
224 (elm)->field.le_prev = &(head)->lh_first; \
225} while (0)
226
227#define LIST_REMOVE(elm, field) do { \
228 if ((elm)->field.le_next != NULL) \
229 (elm)->field.le_next->field.le_prev = \
230 (elm)->field.le_prev; \
231 *(elm)->field.le_prev = (elm)->field.le_next; \
232 _Q_INVALIDATE((elm)->field.le_prev); \
233 _Q_INVALIDATE((elm)->field.le_next); \
234} while (0)
235
236#define LIST_REPLACE(elm, elm2, field) do { \
237 if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
238 (elm2)->field.le_next->field.le_prev = \
239 &(elm2)->field.le_next; \
240 (elm2)->field.le_prev = (elm)->field.le_prev; \
241 *(elm2)->field.le_prev = (elm2); \
242 _Q_INVALIDATE((elm)->field.le_prev); \
243 _Q_INVALIDATE((elm)->field.le_next); \
244} while (0)
245
246/*
247 * Simple queue definitions.
248 */
249#define SIMPLEQ_HEAD(name, type) \
250struct name { \
251 struct type *sqh_first; /* first element */ \
252 struct type **sqh_last; /* addr of last next element */ \
253}
254
255#define SIMPLEQ_HEAD_INITIALIZER(head) \
256 { NULL, &(head).sqh_first }
257
258#define SIMPLEQ_ENTRY(type) \
259struct { \
260 struct type *sqe_next; /* next element */ \
261}
262
263/*
264 * Simple queue access methods.
265 */
266#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
267#define SIMPLEQ_END(head) NULL
268#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
269#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
270
271#define SIMPLEQ_FOREACH(var, head, field) \
272 for((var) = SIMPLEQ_FIRST(head); \
273 (var) != SIMPLEQ_END(head); \
274 (var) = SIMPLEQ_NEXT(var, field))
275
276/*
277 * Simple queue functions.
278 */
279#define SIMPLEQ_INIT(head) do { \
280 (head)->sqh_first = NULL; \
281 (head)->sqh_last = &(head)->sqh_first; \
282} while (0)
283
284#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
285 if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
286 (head)->sqh_last = &(elm)->field.sqe_next; \
287 (head)->sqh_first = (elm); \
288} while (0)
289
290#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
291 (elm)->field.sqe_next = NULL; \
292 *(head)->sqh_last = (elm); \
293 (head)->sqh_last = &(elm)->field.sqe_next; \
294} while (0)
295
296#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
297 if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
298 (head)->sqh_last = &(elm)->field.sqe_next; \
299 (listelm)->field.sqe_next = (elm); \
300} while (0)
301
302#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
303 if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
304 (head)->sqh_last = &(head)->sqh_first; \
305} while (0)
306
307/*
308 * Tail queue definitions.
309 */
310#define TAILQ_HEAD(name, type) \
311struct name { \
312 struct type *tqh_first; /* first element */ \
313 struct type **tqh_last; /* addr of last next element */ \
314}
315
316#define TAILQ_HEAD_INITIALIZER(head) \
317 { NULL, &(head).tqh_first }
318
319#define TAILQ_ENTRY(type) \
320struct { \
321 struct type *tqe_next; /* next element */ \
322 struct type **tqe_prev; /* address of previous next element */ \
323}
324
325/*
326 * tail queue access methods
327 */
328#define TAILQ_FIRST(head) ((head)->tqh_first)
329#define TAILQ_END(head) NULL
330#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
331#define TAILQ_LAST(head, headname) \
332 (*(((struct headname *)((head)->tqh_last))->tqh_last))
333/* XXX */
334#define TAILQ_PREV(elm, headname, field) \
335 (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
336#define TAILQ_EMPTY(head) \
337 (TAILQ_FIRST(head) == TAILQ_END(head))
338
339#define TAILQ_FOREACH(var, head, field) \
340 for((var) = TAILQ_FIRST(head); \
341 (var) != TAILQ_END(head); \
342 (var) = TAILQ_NEXT(var, field))
343
344#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
345 for((var) = TAILQ_LAST(head, headname); \
346 (var) != TAILQ_END(head); \
347 (var) = TAILQ_PREV(var, headname, field))
348
349/*
350 * Tail queue functions.
351 */
352#define TAILQ_INIT(head) do { \
353 (head)->tqh_first = NULL; \
354 (head)->tqh_last = &(head)->tqh_first; \
355} while (0)
356
357#define TAILQ_INSERT_HEAD(head, elm, field) do { \
358 if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
359 (head)->tqh_first->field.tqe_prev = \
360 &(elm)->field.tqe_next; \
361 else \
362 (head)->tqh_last = &(elm)->field.tqe_next; \
363 (head)->tqh_first = (elm); \
364 (elm)->field.tqe_prev = &(head)->tqh_first; \
365} while (0)
366
367#define TAILQ_INSERT_TAIL(head, elm, field) do { \
368 (elm)->field.tqe_next = NULL; \
369 (elm)->field.tqe_prev = (head)->tqh_last; \
370 *(head)->tqh_last = (elm); \
371 (head)->tqh_last = &(elm)->field.tqe_next; \
372} while (0)
373
374#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
375 if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
376 (elm)->field.tqe_next->field.tqe_prev = \
377 &(elm)->field.tqe_next; \
378 else \
379 (head)->tqh_last = &(elm)->field.tqe_next; \
380 (listelm)->field.tqe_next = (elm); \
381 (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
382} while (0)
383
384#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
385 (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
386 (elm)->field.tqe_next = (listelm); \
387 *(listelm)->field.tqe_prev = (elm); \
388 (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
389} while (0)
390
391#define TAILQ_REMOVE(head, elm, field) do { \
392 if (((elm)->field.tqe_next) != NULL) \
393 (elm)->field.tqe_next->field.tqe_prev = \
394 (elm)->field.tqe_prev; \
395 else \
396 (head)->tqh_last = (elm)->field.tqe_prev; \
397 *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
398 _Q_INVALIDATE((elm)->field.tqe_prev); \
399 _Q_INVALIDATE((elm)->field.tqe_next); \
400} while (0)
401
402#define TAILQ_REPLACE(head, elm, elm2, field) do { \
403 if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
404 (elm2)->field.tqe_next->field.tqe_prev = \
405 &(elm2)->field.tqe_next; \
406 else \
407 (head)->tqh_last = &(elm2)->field.tqe_next; \
408 (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
409 *(elm2)->field.tqe_prev = (elm2); \
410 _Q_INVALIDATE((elm)->field.tqe_prev); \
411 _Q_INVALIDATE((elm)->field.tqe_next); \
412} while (0)
413
414/*
415 * Circular queue definitions.
416 */
417#define CIRCLEQ_HEAD(name, type) \
418struct name { \
419 struct type *cqh_first; /* first element */ \
420 struct type *cqh_last; /* last element */ \
421}
422
423#define CIRCLEQ_HEAD_INITIALIZER(head) \
424 { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
425
426#define CIRCLEQ_ENTRY(type) \
427struct { \
428 struct type *cqe_next; /* next element */ \
429 struct type *cqe_prev; /* previous element */ \
430}
431
432/*
433 * Circular queue access methods
434 */
435#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
436#define CIRCLEQ_LAST(head) ((head)->cqh_last)
437#define CIRCLEQ_END(head) ((void *)(head))
438#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
439#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
440#define CIRCLEQ_EMPTY(head) \
441 (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
442
443#define CIRCLEQ_FOREACH(var, head, field) \
444 for((var) = CIRCLEQ_FIRST(head); \
445 (var) != CIRCLEQ_END(head); \
446 (var) = CIRCLEQ_NEXT(var, field))
447
448#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
449 for((var) = CIRCLEQ_LAST(head); \
450 (var) != CIRCLEQ_END(head); \
451 (var) = CIRCLEQ_PREV(var, field))
452
453/*
454 * Circular queue functions.
455 */
456#define CIRCLEQ_INIT(head) do { \
457 (head)->cqh_first = CIRCLEQ_END(head); \
458 (head)->cqh_last = CIRCLEQ_END(head); \
459} while (0)
460
461#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
462 (elm)->field.cqe_next = (listelm)->field.cqe_next; \
463 (elm)->field.cqe_prev = (listelm); \
464 if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
465 (head)->cqh_last = (elm); \
466 else \
467 (listelm)->field.cqe_next->field.cqe_prev = (elm); \
468 (listelm)->field.cqe_next = (elm); \
469} while (0)
470
471#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
472 (elm)->field.cqe_next = (listelm); \
473 (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
474 if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
475 (head)->cqh_first = (elm); \
476 else \
477 (listelm)->field.cqe_prev->field.cqe_next = (elm); \
478 (listelm)->field.cqe_prev = (elm); \
479} while (0)
480
481#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
482 (elm)->field.cqe_next = (head)->cqh_first; \
483 (elm)->field.cqe_prev = CIRCLEQ_END(head); \
484 if ((head)->cqh_last == CIRCLEQ_END(head)) \
485 (head)->cqh_last = (elm); \
486 else \
487 (head)->cqh_first->field.cqe_prev = (elm); \
488 (head)->cqh_first = (elm); \
489} while (0)
490
491#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
492 (elm)->field.cqe_next = CIRCLEQ_END(head); \
493 (elm)->field.cqe_prev = (head)->cqh_last; \
494 if ((head)->cqh_first == CIRCLEQ_END(head)) \
495 (head)->cqh_first = (elm); \
496 else \
497 (head)->cqh_last->field.cqe_next = (elm); \
498 (head)->cqh_last = (elm); \
499} while (0)
500
501#define CIRCLEQ_REMOVE(head, elm, field) do { \
502 if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
503 (head)->cqh_last = (elm)->field.cqe_prev; \
504 else \
505 (elm)->field.cqe_next->field.cqe_prev = \
506 (elm)->field.cqe_prev; \
507 if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
508 (head)->cqh_first = (elm)->field.cqe_next; \
509 else \
510 (elm)->field.cqe_prev->field.cqe_next = \
511 (elm)->field.cqe_next; \
512 _Q_INVALIDATE((elm)->field.cqe_prev); \
513 _Q_INVALIDATE((elm)->field.cqe_next); \
514} while (0)
515
516#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
517 if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
518 CIRCLEQ_END(head)) \
519 (head).cqh_last = (elm2); \
520 else \
521 (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
522 if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
523 CIRCLEQ_END(head)) \
524 (head).cqh_first = (elm2); \
525 else \
526 (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
527 _Q_INVALIDATE((elm)->field.cqe_prev); \
528 _Q_INVALIDATE((elm)->field.cqe_next); \
529} while (0)
530
531#endif /* !_SYS_QUEUE_H_ */
diff --git a/src/nat/miniupnp/codelength.h b/src/nat/miniupnp/codelength.h
new file mode 100644
index 000000000..8a5f49517
--- /dev/null
+++ b/src/nat/miniupnp/codelength.h
@@ -0,0 +1,23 @@
1/* $Id: codelength.h,v 1.1 2008/10/06 22:04:06 nanard Exp $ */
2/* Project : miniupnp
3 * Author : Thomas BERNARD
4 * copyright (c) 2005-2008 Thomas Bernard
5 * This software is subjet to the conditions detailed in the
6 * provided LICENCE file. */
7#ifndef __CODELENGTH_H__
8#define __CODELENGTH_H__
9
10/* Encode length by using 7bit per Byte :
11 * Most significant bit of each byte specifies that the
12 * following byte is part of the code */
13#define DECODELENGTH(n, p) n = 0; \
14 do { n = (n << 7) | (*p & 0x7f); } \
15 while(*(p++)&0x80);
16
17#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \
18 if(n>=2097152) *(p++) = (n >> 21) | 0x80; \
19 if(n>=16384) *(p++) = (n >> 14) | 0x80; \
20 if(n>=128) *(p++) = (n >> 7) | 0x80; \
21 *(p++) = n & 0x7f;
22
23#endif
diff --git a/src/nat/miniupnp/declspec.h b/src/nat/miniupnp/declspec.h
new file mode 100644
index 000000000..c064bded2
--- /dev/null
+++ b/src/nat/miniupnp/declspec.h
@@ -0,0 +1,14 @@
1#ifndef __DECLSPEC_H__
2#define __DECLSPEC_H__
3
4#if defined(WIN32) && !defined(STATICLIB)
5#ifdef MINIUPNP_EXPORTS
6#define LIBSPEC __declspec(dllexport)
7#else
8#define LIBSPEC __declspec(dllimport)
9#endif
10#else
11#define LIBSPEC
12#endif
13
14#endif
diff --git a/src/nat/miniupnp/igd_desc_parse.c b/src/nat/miniupnp/igd_desc_parse.c
new file mode 100644
index 000000000..deb53152c
--- /dev/null
+++ b/src/nat/miniupnp/igd_desc_parse.c
@@ -0,0 +1,126 @@
1/* $Id: igd_desc_parse.c,v 1.8 2008/04/23 11:51:06 nanard Exp $ */
2/* Project : miniupnp
3 * http://miniupnp.free.fr/
4 * Author : Thomas Bernard
5 * Copyright (c) 2005-2008 Thomas Bernard
6 * This software is subject to the conditions detailed in the
7 * LICENCE file provided in this distribution.
8 * */
9#include "igd_desc_parse.h"
10#include <stdio.h>
11#include <string.h>
12
13/* TODO : rewrite this code so it correctly handle descriptions with
14 * both WANIPConnection and/or WANPPPConnection */
15
16/* Start element handler :
17 * update nesting level counter and copy element name */
18void
19IGDstartelt (void *d, const char *name, int l)
20{
21 struct IGDdatas *datas = (struct IGDdatas *) d;
22 memcpy (datas->cureltname, name, l);
23 datas->cureltname[l] = '\0';
24 datas->level++;
25 if ((l == 7) && !memcmp (name, "service", l))
26 {
27 datas->controlurl_tmp[0] = '\0';
28 datas->eventsuburl_tmp[0] = '\0';
29 datas->scpdurl_tmp[0] = '\0';
30 datas->servicetype_tmp[0] = '\0';
31 }
32}
33
34/* End element handler :
35 * update nesting level counter and update parser state if
36 * service element is parsed */
37void
38IGDendelt (void *d, const char *name, int l)
39{
40 struct IGDdatas *datas = (struct IGDdatas *) d;
41 datas->level--;
42 /*printf("endelt %2d %.*s\n", datas->level, l, name); */
43 if ((l == 7) && !memcmp (name, "service", l))
44 {
45 /*
46 if( datas->state < 1
47 && !strcmp(datas->servicetype,
48 // "urn:schemas-upnp-org:service:WANIPConnection:1") )
49 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
50 datas->state ++;
51 */
52 if (0 == strcmp (datas->servicetype_tmp,
53 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
54 {
55 memcpy (datas->controlurl_CIF, datas->controlurl_tmp,
56 MINIUPNPC_URL_MAXSIZE);
57 memcpy (datas->eventsuburl_CIF, datas->eventsuburl_tmp,
58 MINIUPNPC_URL_MAXSIZE);
59 memcpy (datas->scpdurl_CIF, datas->scpdurl_tmp,
60 MINIUPNPC_URL_MAXSIZE);
61 memcpy (datas->servicetype_CIF, datas->servicetype_tmp,
62 MINIUPNPC_URL_MAXSIZE);
63 }
64 else if (0 == strcmp (datas->servicetype_tmp,
65 "urn:schemas-upnp-org:service:WANIPConnection:1")
66 || 0 == strcmp (datas->servicetype_tmp,
67 "urn:schemas-upnp-org:service:WANPPPConnection:1"))
68 {
69 memcpy (datas->controlurl, datas->controlurl_tmp,
70 MINIUPNPC_URL_MAXSIZE);
71 memcpy (datas->eventsuburl, datas->eventsuburl_tmp,
72 MINIUPNPC_URL_MAXSIZE);
73 memcpy (datas->scpdurl, datas->scpdurl_tmp, MINIUPNPC_URL_MAXSIZE);
74 memcpy (datas->servicetype, datas->servicetype_tmp,
75 MINIUPNPC_URL_MAXSIZE);
76 }
77 }
78}
79
80/* Data handler :
81 * copy data depending on the current element name and state */
82void
83IGDdata (void *d, const char *data, int l)
84{
85 struct IGDdatas *datas = (struct IGDdatas *) d;
86 char *dstmember = 0;
87 /*printf("%2d %s : %.*s\n",
88 datas->level, datas->cureltname, l, data); */
89 if (!strcmp (datas->cureltname, "URLBase"))
90 dstmember = datas->urlbase;
91 else if (!strcmp (datas->cureltname, "serviceType"))
92 dstmember = datas->servicetype_tmp;
93 else if (!strcmp (datas->cureltname, "controlURL"))
94 dstmember = datas->controlurl_tmp;
95 else if (!strcmp (datas->cureltname, "eventSubURL"))
96 dstmember = datas->eventsuburl_tmp;
97 else if (!strcmp (datas->cureltname, "SCPDURL"))
98 dstmember = datas->scpdurl_tmp;
99/* else if( !strcmp(datas->cureltname, "deviceType") )
100 dstmember = datas->devicetype_tmp;*/
101 if (dstmember)
102 {
103 if (l >= MINIUPNPC_URL_MAXSIZE)
104 l = MINIUPNPC_URL_MAXSIZE - 1;
105 memcpy (dstmember, data, l);
106 dstmember[l] = '\0';
107 }
108}
109
110void
111printIGD (struct IGDdatas *d)
112{
113 printf ("urlbase = %s\n", d->urlbase);
114 printf ("WAN Device (Common interface config) :\n");
115 /*printf(" deviceType = %s\n", d->devicetype_CIF); */
116 printf (" serviceType = %s\n", d->servicetype_CIF);
117 printf (" controlURL = %s\n", d->controlurl_CIF);
118 printf (" eventSubURL = %s\n", d->eventsuburl_CIF);
119 printf (" SCPDURL = %s\n", d->scpdurl_CIF);
120 printf ("WAN Connection Device (IP or PPP Connection):\n");
121 /*printf(" deviceType = %s\n", d->devicetype); */
122 printf (" servicetype = %s\n", d->servicetype);
123 printf (" controlURL = %s\n", d->controlurl);
124 printf (" eventSubURL = %s\n", d->eventsuburl);
125 printf (" SCPDURL = %s\n", d->scpdurl);
126}
diff --git a/src/nat/miniupnp/igd_desc_parse.h b/src/nat/miniupnp/igd_desc_parse.h
new file mode 100644
index 000000000..5e7e24b86
--- /dev/null
+++ b/src/nat/miniupnp/igd_desc_parse.h
@@ -0,0 +1,47 @@
1/* $Id: igd_desc_parse.h,v 1.6 2008/04/23 11:51:07 nanard Exp $ */
2/* Project : miniupnp
3 * http://miniupnp.free.fr/
4 * Author : Thomas Bernard
5 * Copyright (c) 2005-2008 Thomas Bernard
6 * This software is subject to the conditions detailed in the
7 * LICENCE file provided in this distribution.
8 * */
9#ifndef __IGD_DESC_PARSE_H__
10#define __IGD_DESC_PARSE_H__
11
12/* Structure to store the result of the parsing of UPnP
13 * descriptions of Internet Gateway Devices */
14#define MINIUPNPC_URL_MAXSIZE (128)
15struct IGDdatas
16{
17 char cureltname[MINIUPNPC_URL_MAXSIZE];
18 char urlbase[MINIUPNPC_URL_MAXSIZE];
19 int level;
20 /*int state; */
21 /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
22 char controlurl_CIF[MINIUPNPC_URL_MAXSIZE];
23 char eventsuburl_CIF[MINIUPNPC_URL_MAXSIZE];
24 char scpdurl_CIF[MINIUPNPC_URL_MAXSIZE];
25 char servicetype_CIF[MINIUPNPC_URL_MAXSIZE];
26 /*char devicetype_CIF[MINIUPNPC_URL_MAXSIZE]; */
27 /* "urn:schemas-upnp-org:service:WANIPConnection:1"
28 * "urn:schemas-upnp-org:service:WANPPPConnection:1" */
29 char controlurl[MINIUPNPC_URL_MAXSIZE];
30 char eventsuburl[MINIUPNPC_URL_MAXSIZE];
31 char scpdurl[MINIUPNPC_URL_MAXSIZE];
32 char servicetype[MINIUPNPC_URL_MAXSIZE];
33 /*char devicetype[MINIUPNPC_URL_MAXSIZE]; */
34 /* tmp */
35 char controlurl_tmp[MINIUPNPC_URL_MAXSIZE];
36 char eventsuburl_tmp[MINIUPNPC_URL_MAXSIZE];
37 char scpdurl_tmp[MINIUPNPC_URL_MAXSIZE];
38 char servicetype_tmp[MINIUPNPC_URL_MAXSIZE];
39 /*char devicetype_tmp[MINIUPNPC_URL_MAXSIZE]; */
40};
41
42void IGDstartelt (void *, const char *, int);
43void IGDendelt (void *, const char *, int);
44void IGDdata (void *, const char *, int);
45void printIGD (struct IGDdatas *);
46
47#endif
diff --git a/src/nat/miniupnp/minisoap.c b/src/nat/miniupnp/minisoap.c
new file mode 100644
index 000000000..a020f9a64
--- /dev/null
+++ b/src/nat/miniupnp/minisoap.c
@@ -0,0 +1,106 @@
1/* $Id: minisoap.c,v 1.16 2008/10/11 16:39:29 nanard Exp $ */
2/* Project : miniupnp
3 * Author : Thomas Bernard
4 * Copyright (c) 2005 Thomas Bernard
5 * This software is subject to the conditions detailed in the
6 * LICENCE file provided in this distribution.
7 *
8 * Minimal SOAP implementation for UPnP protocol.
9 */
10#include <stdio.h>
11#include <string.h>
12#ifdef WIN32
13#include <io.h>
14#include <winsock2.h>
15#define snprintf _snprintf
16#else
17#include <unistd.h>
18#include <sys/types.h>
19#include <sys/socket.h>
20#endif
21#include "minisoap.h"
22#include "miniupnpcstrings.h"
23
24/* only for malloc */
25#include <stdlib.h>
26
27#ifdef WIN32
28#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
29#else
30#define PRINT_SOCKET_ERROR(x) perror(x)
31#endif
32
33/* httpWrite sends the headers and the body to the socket
34 * and returns the number of bytes sent */
35static int
36httpWrite (int fd, const char *body, int bodysize,
37 const char *headers, int headerssize)
38{
39 int n = 0;
40 /*n = write(fd, headers, headerssize); */
41 /*if(bodysize>0)
42 n += write(fd, body, bodysize); */
43 /* Note : my old linksys router only took into account
44 * soap request that are sent into only one packet */
45 char *p;
46 /* TODO: AVOID MALLOC */
47 p = malloc (headerssize + bodysize);
48 if (!p)
49 return 0;
50 memcpy (p, headers, headerssize);
51 memcpy (p + headerssize, body, bodysize);
52 /*n = write(fd, p, headerssize+bodysize); */
53 n = send (fd, p, headerssize + bodysize, 0);
54 if (n < 0)
55 {
56 PRINT_SOCKET_ERROR ("send");
57 }
58 /* disable send on the socket */
59 /* draytek routers dont seems to like that... */
60#if 0
61#ifdef WIN32
62 if (shutdown (fd, SD_SEND) < 0)
63 {
64#else
65 if (shutdown (fd, SHUT_WR) < 0)
66 { /*SD_SEND */
67#endif
68 PRINT_SOCKET_ERROR ("shutdown");
69 }
70#endif
71 free (p);
72 return n;
73}
74
75/* self explanatory */
76int
77soapPostSubmit (int fd,
78 const char *url,
79 const char *host,
80 unsigned short port, const char *action, const char *body)
81{
82 int bodysize;
83 char headerbuf[512];
84 int headerssize;
85 char portstr[8];
86 bodysize = (int) strlen (body);
87 /* We are not using keep-alive HTTP connections.
88 * HTTP/1.1 needs the header Connection: close to do that.
89 * This is the default with HTTP/1.0 */
90 /* Connection: Close is normally there only in HTTP/1.1 but who knows */
91 portstr[0] = '\0';
92 if (port != 80)
93 snprintf (portstr, sizeof (portstr), ":%hu", port);
94 headerssize = snprintf (headerbuf, sizeof (headerbuf),
95 "POST %s HTTP/1.1\r\n"
96/* "POST %s HTTP/1.0\r\n"*/
97 "Host: %s%s\r\n" "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" "Content-Length: %d\r\n" "Content-Type: text/xml\r\n" "SOAPAction: \"%s\"\r\n" "Connection: Close\r\n" "Cache-Control: no-cache\r\n" /* ??? */
98 "Pragma: no-cache\r\n"
99 "\r\n", url, host, portstr, bodysize, action);
100#ifdef DEBUG
101 printf ("SOAP request : headersize=%d bodysize=%d\n",
102 headerssize, bodysize);
103 /*printf("%s", headerbuf); */
104#endif
105 return httpWrite (fd, body, bodysize, headerbuf, headerssize);
106}
diff --git a/src/nat/miniupnp/minisoap.h b/src/nat/miniupnp/minisoap.h
new file mode 100644
index 000000000..2505e4c20
--- /dev/null
+++ b/src/nat/miniupnp/minisoap.h
@@ -0,0 +1,14 @@
1/* $Id: minisoap.h,v 1.3 2006/11/19 22:32:34 nanard Exp $ */
2/* Project : miniupnp
3 * Author : Thomas Bernard
4 * Copyright (c) 2005 Thomas Bernard
5 * This software is subject to the conditions detailed in the
6 * LICENCE file provided in this distribution. */
7#ifndef __MINISOAP_H__
8#define __MINISOAP_H__
9
10/*int httpWrite(int, const char *, int, const char *);*/
11int soapPostSubmit (int, const char *, const char *, unsigned short,
12 const char *, const char *);
13
14#endif
diff --git a/src/nat/miniupnp/minissdpc.c b/src/nat/miniupnp/minissdpc.c
new file mode 100644
index 000000000..3fe89ef94
--- /dev/null
+++ b/src/nat/miniupnp/minissdpc.c
@@ -0,0 +1,111 @@
1/* $Id: minissdpc.c,v 1.7 2008/12/18 17:45:48 nanard Exp $ */
2/* Project : miniupnp
3 * Author : Thomas BERNARD
4 * copyright (c) 2005-2008 Thomas Bernard
5 * This software is subjet to the conditions detailed in the
6 * provided LICENCE file. */
7/*#include <syslog.h>*/
8#include <stdio.h>
9#include <string.h>
10#include <stdlib.h>
11#include <unistd.h>
12#include <sys/types.h>
13#ifdef WIN32
14#include <winsock2.h>
15#include <Ws2tcpip.h>
16#include <io.h>
17#else
18#include <sys/socket.h>
19#include <sys/un.h>
20#endif
21
22#include "minissdpc.h"
23#include "miniupnpc.h"
24
25#include "codelength.h"
26
27struct UPNPDev *
28getDevicesFromMiniSSDPD (const char *devtype, const char *socketpath)
29{
30 struct UPNPDev *tmp;
31 struct UPNPDev *devlist = NULL;
32 unsigned char buffer[2048];
33 ssize_t n;
34 unsigned char *p;
35 unsigned char *url;
36 unsigned int i;
37 unsigned int urlsize, stsize, usnsize, l;
38 int s;
39 struct sockaddr_un addr;
40
41 s = socket (AF_UNIX, SOCK_STREAM, 0);
42 if (s < 0)
43 {
44 /*syslog(LOG_ERR, "socket(unix): %m"); */
45 perror ("socket(unix)");
46 return NULL;
47 }
48 addr.sun_family = AF_UNIX;
49 strncpy (addr.sun_path, socketpath, sizeof (addr.sun_path));
50 if (connect (s, (struct sockaddr *) &addr, sizeof (struct sockaddr_un)) < 0)
51 {
52 /*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath); */
53 close (s);
54 return NULL;
55 }
56 stsize = strlen (devtype);
57 buffer[0] = 1; /* request type 1 : request devices/services by type */
58 p = buffer + 1;
59 l = stsize;
60 CODELENGTH (l, p);
61 memcpy (p, devtype, stsize);
62 p += stsize;
63 if (write (s, buffer, p - buffer) < 0)
64 {
65 /*syslog(LOG_ERR, "write(): %m"); */
66 perror ("minissdpc.c: write()");
67 close (s);
68 return NULL;
69 }
70 n = read (s, buffer, sizeof (buffer));
71 if (n <= 0)
72 {
73 perror ("minissdpc.c: read()");
74 close (s);
75 return NULL;
76 }
77 p = buffer + 1;
78 for (i = 0; i < buffer[0]; i++)
79 {
80 if (p + 2 >= buffer + sizeof (buffer))
81 break;
82 DECODELENGTH (urlsize, p);
83 if (p + urlsize + 2 >= buffer + sizeof (buffer))
84 break;
85 url = p;
86 p += urlsize;
87 DECODELENGTH (stsize, p);
88 if (p + stsize + 2 >= buffer + sizeof (buffer))
89 break;
90 tmp =
91 (struct UPNPDev *) malloc (sizeof (struct UPNPDev) + urlsize +
92 stsize);
93 tmp->pNext = devlist;
94 tmp->descURL = tmp->buffer;
95 tmp->st = tmp->buffer + 1 + urlsize;
96 memcpy (tmp->buffer, url, urlsize);
97 tmp->buffer[urlsize] = '\0';
98 memcpy (tmp->buffer + urlsize + 1, p, stsize);
99 p += stsize;
100 tmp->buffer[urlsize + 1 + stsize] = '\0';
101 devlist = tmp;
102 /* added for compatibility with recent versions of MiniSSDPd
103 * >= 2007/12/19 */
104 DECODELENGTH (usnsize, p);
105 p += usnsize;
106 if (p > buffer + sizeof (buffer))
107 break;
108 }
109 close (s);
110 return devlist;
111}
diff --git a/src/nat/miniupnp/minissdpc.h b/src/nat/miniupnp/minissdpc.h
new file mode 100644
index 000000000..f6c7db547
--- /dev/null
+++ b/src/nat/miniupnp/minissdpc.h
@@ -0,0 +1,14 @@
1/* $Id: minissdpc.h,v 1.1 2007/08/31 15:15:33 nanard Exp $ */
2/* Project: miniupnp
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * Author: Thomas Bernard
5 * Copyright (c) 2005-2007 Thomas Bernard
6 * This software is subjects to the conditions detailed
7 * in the LICENCE file provided within this distribution */
8#ifndef __MINISSDPC_H__
9#define __MINISSDPC_H__
10
11struct UPNPDev *getDevicesFromMiniSSDPD (const char *devtype,
12 const char *socketpath);
13
14#endif
diff --git a/src/nat/miniupnp/miniupnpc.c b/src/nat/miniupnp/miniupnpc.c
new file mode 100644
index 000000000..412c8ec25
--- /dev/null
+++ b/src/nat/miniupnp/miniupnpc.c
@@ -0,0 +1,890 @@
1/* $Id: miniupnpc.c,v 1.57 2008/12/18 17:46:36 nanard Exp $ */
2/* Project : miniupnp
3 * Author : Thomas BERNARD
4 * copyright (c) 2005-2007 Thomas Bernard
5 * This software is subjet to the conditions detailed in the
6 * provided LICENCE file. */
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#ifdef WIN32
11/* Win32 Specific includes and defines */
12#include <winsock2.h>
13#include <Ws2tcpip.h>
14#include <Iphlpapi.h>
15#include <io.h>
16#define snprintf _snprintf
17#if defined(_MSC_VER) && (_MSC_VER >= 1400)
18#define strncasecmp _memicmp
19#else
20#define strncasecmp memicmp
21#endif
22#define MAXHOSTNAMELEN 64
23#else
24/* Standard POSIX includes */
25#include <unistd.h>
26#include <sys/socket.h>
27#include <sys/types.h>
28#include <sys/param.h>
29#include <netinet/in.h>
30#include <net/if.h>
31#include <arpa/inet.h>
32#include <poll.h>
33#include <netdb.h>
34#define closesocket close
35#endif
36#include "miniupnpc.h"
37#include "minissdpc.h"
38#include "miniwget.h"
39#include "minisoap.h"
40#include "minixml.h"
41#include "upnpcommands.h"
42
43#ifdef WIN32
44#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
45#else
46#define PRINT_SOCKET_ERROR(x) perror(x)
47#endif
48
49#define SOAPPREFIX "s"
50#define SERVICEPREFIX "u"
51#define SERVICEPREFIX2 'u'
52
53/* root description parsing */
54void
55parserootdesc (const char *buffer, int bufsize, struct IGDdatas *data)
56{
57 struct xmlparser parser;
58 /* xmlparser object */
59 parser.xmlstart = buffer;
60 parser.xmlsize = bufsize;
61 parser.data = data;
62 parser.starteltfunc = IGDstartelt;
63 parser.endeltfunc = IGDendelt;
64 parser.datafunc = IGDdata;
65 parser.attfunc = 0;
66 parsexml (&parser);
67#ifdef DEBUG
68 printIGD (data);
69#endif
70}
71
72/* Content-length: nnn */
73static int
74getcontentlenfromline (const char *p, int n)
75{
76 static const char contlenstr[] = "content-length";
77 const char *p2 = contlenstr;
78 int a = 0;
79 while (*p2)
80 {
81 if (n == 0)
82 return -1;
83 if (*p2 != *p && *p2 != (*p + 32))
84 return -1;
85 p++;
86 p2++;
87 n--;
88 }
89 if (n == 0)
90 return -1;
91 if (*p != ':')
92 return -1;
93 p++;
94 n--;
95 while (*p == ' ')
96 {
97 if (n == 0)
98 return -1;
99 p++;
100 n--;
101 }
102 while (*p >= '0' && *p <= '9')
103 {
104 if (n == 0)
105 return -1;
106 a = (a * 10) + (*p - '0');
107 p++;
108 n--;
109 }
110 return a;
111}
112
113static void
114getContentLengthAndHeaderLength (char *p, int n,
115 int *contentlen, int *headerlen)
116{
117 char *line;
118 int linelen;
119 int r;
120 line = p;
121 while (line < p + n)
122 {
123 linelen = 0;
124 while (line[linelen] != '\r' && line[linelen] != '\r')
125 {
126 if (line + linelen >= p + n)
127 return;
128 linelen++;
129 }
130 r = getcontentlenfromline (line, linelen);
131 if (r > 0)
132 *contentlen = r;
133 line = line + linelen + 2;
134 if (line[0] == '\r' && line[1] == '\n')
135 {
136 *headerlen = (line - p) + 2;
137 return;
138 }
139 }
140}
141
142/* simpleUPnPcommand :
143 * not so simple !
144 * return values :
145 * 0 - OK
146 * -1 - error */
147int
148simpleUPnPcommand (int s, const char *url, const char *service,
149 const char *action, struct UPNParg *args,
150 char *buffer, int *bufsize)
151{
152 struct sockaddr_in dest;
153 struct sockaddr_in6 dest6;
154 char hostname[MAXHOSTNAMELEN + 1];
155 unsigned short port = 0;
156 char *path;
157 char soapact[128];
158 char soapbody[2048];
159 char *buf;
160 int buffree;
161 int n;
162 int err;
163 int contentlen, headerlen; /* for the response */
164 snprintf (soapact, sizeof (soapact), "%s#%s", service, action);
165 if (args == NULL)
166 {
167 /*soapbodylen = */ snprintf (soapbody, sizeof (soapbody),
168 "<?xml version=\"1.0\"?>\r\n"
169 "<" SOAPPREFIX ":Envelope "
170 "xmlns:" SOAPPREFIX
171 "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
172 SOAPPREFIX
173 ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
174 "<" SOAPPREFIX ":Body>" "<" SERVICEPREFIX
175 ":%s xmlns:" SERVICEPREFIX "=\"%s\">" "</"
176 SERVICEPREFIX ":%s>" "</" SOAPPREFIX
177 ":Body></" SOAPPREFIX ":Envelope>" "\r\n",
178 action, service, action);
179 }
180 else
181 {
182 char *p;
183 const char *pe, *pv;
184 int soapbodylen;
185 soapbodylen = snprintf (soapbody, sizeof (soapbody),
186 "<?xml version=\"1.0\"?>\r\n"
187 "<" SOAPPREFIX ":Envelope "
188 "xmlns:" SOAPPREFIX
189 "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
190 SOAPPREFIX
191 ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
192 "<" SOAPPREFIX ":Body>" "<" SERVICEPREFIX
193 ":%s xmlns:" SERVICEPREFIX "=\"%s\">", action,
194 service);
195 p = soapbody + soapbodylen;
196 while (args->elt)
197 {
198 /* check that we are never overflowing the string... */
199 if (soapbody + sizeof (soapbody) <= p + 100)
200 {
201 /* we keep a margin of at least 100 bytes */
202 *bufsize = 0;
203 return -1;
204 }
205 *(p++) = '<';
206 pe = args->elt;
207 while (*pe)
208 *(p++) = *(pe++);
209 *(p++) = '>';
210 if ((pv = args->val))
211 {
212 while (*pv)
213 *(p++) = *(pv++);
214 }
215 *(p++) = '<';
216 *(p++) = '/';
217 pe = args->elt;
218 while (*pe)
219 *(p++) = *(pe++);
220 *(p++) = '>';
221 args++;
222 }
223 *(p++) = '<';
224 *(p++) = '/';
225 *(p++) = SERVICEPREFIX2;
226 *(p++) = ':';
227 pe = action;
228 while (*pe)
229 *(p++) = *(pe++);
230 strncpy (p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
231 soapbody + sizeof (soapbody) - p);
232 }
233 if (!parseURL (url, hostname, &port, &path))
234 return -1;
235
236 if (s < 0)
237 {
238 /* Test IPv4 address, else use IPv6 */
239 if (inet_pton (AF_INET, hostname, &dest.sin_addr) == 1)
240 {
241 dest.sin_family = AF_INET;
242 dest.sin_port = htons (port);
243#ifdef HAVE_SOCKADDR_IN_SIN_LEN
244 dest.sin_len = sizeof (dest);
245#endif
246 if ((s = socket (PF_INET, SOCK_STREAM, 0)) < 0)
247 {
248 PRINT_SOCKET_ERROR ("socket");
249 *bufsize = 0;
250 return -1;
251 }
252 err = connect (s, (struct sockaddr *) &dest, sizeof (dest));
253 }
254 else if (inet_pton (AF_INET6, hostname, &dest6.sin6_addr) == 1)
255 {
256 dest6.sin6_family = AF_INET6;
257 dest6.sin6_port = htons (port);
258 dest6.sin6_flowinfo = 0;
259#ifdef HAVE_SOCKADDR_IN_SIN_LEN
260 dest6.sin6_len = sizeof (dest6);
261#endif
262 if ((s = socket (PF_INET6, SOCK_STREAM, 0)) < 0)
263 {
264 PRINT_SOCKET_ERROR ("socket");
265 *bufsize = 0;
266 return -1;
267 }
268 err = connect (s, (struct sockaddr *) &dest6, sizeof (dest6));
269 }
270 else
271 {
272 PRINT_SOCKET_ERROR ("inet_pton");
273 closesocket (s);
274 *bufsize = 0;
275 return -1;
276 }
277
278 if (err < 0)
279 {
280 PRINT_SOCKET_ERROR ("connect");
281 closesocket (s);
282 *bufsize = 0;
283 return -1;
284 }
285 }
286 n = soapPostSubmit (s, path, hostname, port, soapact, soapbody);
287 if (n <= 0)
288 {
289#ifdef DEBUG
290 printf ("Error sending SOAP request\n");
291#endif
292 closesocket (s);
293 return -1;
294 }
295
296 contentlen = -1;
297 headerlen = -1;
298 buf = buffer;
299 buffree = *bufsize;
300 *bufsize = 0;
301 while ((n = ReceiveData (s, buf, buffree, 5000)) > 0)
302 {
303 buffree -= n;
304 buf += n;
305 *bufsize += n;
306 getContentLengthAndHeaderLength (buffer, *bufsize,
307 &contentlen, &headerlen);
308#ifdef DEBUG
309 printf ("received n=%dbytes bufsize=%d ContLen=%d HeadLen=%d\n",
310 n, *bufsize, contentlen, headerlen);
311#endif
312 /* break if we received everything */
313 if (contentlen > 0 && headerlen > 0
314 && *bufsize >= contentlen + headerlen)
315 break;
316 }
317
318 closesocket (s);
319 return 0;
320}
321
322/* parseMSEARCHReply()
323 * the last 4 arguments are filled during the parsing :
324 * - location/locationsize : "location:" field of the SSDP reply packet
325 * - st/stsize : "st:" field of the SSDP reply packet.
326 * The strings are NOT null terminated */
327static void
328parseMSEARCHReply (const char *reply, int size,
329 const char **location, int *locationsize,
330 const char **st, int *stsize)
331{
332 int a, b, i;
333 i = 0;
334 a = i; /* start of the line */
335 b = 0;
336 while (i < size)
337 {
338 switch (reply[i])
339 {
340 case ':':
341 if (b == 0)
342 {
343 b = i; /* end of the "header" */
344 /*for(j=a; j<b; j++)
345 {
346 putchar(reply[j]);
347 }
348 */
349 }
350 break;
351 case '\x0a':
352 case '\x0d':
353 if (b != 0)
354 {
355 /*for(j=b+1; j<i; j++)
356 {
357 putchar(reply[j]);
358 }
359 putchar('\n'); */
360 do
361 {
362 b++;
363 }
364 while (reply[b] == ' ');
365 if (0 == strncasecmp (reply + a, "location", 8))
366 {
367 *location = reply + b;
368 *locationsize = i - b;
369 }
370 else if (0 == strncasecmp (reply + a, "st", 2))
371 {
372 *st = reply + b;
373 *stsize = i - b;
374 }
375 b = 0;
376 }
377 a = i + 1;
378 break;
379 default:
380 break;
381 }
382 i++;
383 }
384}
385
386/* port upnp discover : SSDP protocol */
387#define PORT 1900
388#define XSTR(s) STR(s)
389#define STR(s) #s
390#define UPNP_MCAST_ADDR "239.255.255.250"
391#define UPNP_MCAST_ADDR6 "FF02:0:0:0:0:0:0:F"
392
393/* upnpDiscover() :
394 * return a chained list of all devices found or NULL if
395 * no devices was found.
396 * It is up to the caller to free the chained list
397 * delay is in millisecond (poll) */
398struct UPNPDev *
399upnpDiscover (int delay, const char *multicastif,
400 const struct sockaddr *addr,
401 const char *minissdpdsock, int sameport)
402{
403 struct UPNPDev *tmp;
404 struct UPNPDev *devlist = 0;
405 int opt = 1;
406 static const char MSearchMsgFmt[] =
407 "M-SEARCH * HTTP/1.1\r\n"
408 "HOST: " UPNP_MCAST_ADDR ":" XSTR (PORT) "\r\n"
409 "ST: %s\r\n" "MAN: \"ssdp:discover\"\r\n" "MX: 3\r\n" "\r\n";
410 static const char *const deviceList[] = {
411 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
412 "urn:schemas-upnp-org:service:WANIPConnection:1",
413 "urn:schemas-upnp-org:service:WANPPPConnection:1",
414 "upnp:rootdevice",
415 0
416 };
417 int deviceIndex = 0;
418 char bufr[1536]; /* reception and emission buffer */
419 int sudp;
420 int n;
421 int domain = PF_INET;
422 int if_index;
423 struct in6_addr any_addr = IN6ADDR_ANY_INIT;
424 struct sockaddr_in sockudp_r, sockudp_w;
425 struct sockaddr_in6 sockudp6_r, sockudp6_w;
426
427#ifndef WIN32
428 /* first try to get infos from minissdpd ! */
429 if (!minissdpdsock)
430 minissdpdsock = "/var/run/minissdpd.sock";
431 while (!devlist && deviceList[deviceIndex])
432 {
433 devlist = getDevicesFromMiniSSDPD (deviceList[deviceIndex],
434 minissdpdsock);
435 /* We return what we have found if it was not only a rootdevice */
436 if (devlist && !strstr (deviceList[deviceIndex], "rootdevice"))
437 return devlist;
438 deviceIndex++;
439 }
440 deviceIndex = 0;
441#endif
442
443 if (addr && addr->sa_family == AF_INET)
444 domain = PF_INET;
445 else if (addr && addr->sa_family == AF_INET6)
446 domain = PF_INET6;
447 else if (addr)
448 return NULL;
449
450 /* fallback to direct discovery */
451#ifdef WIN32
452 sudp = socket (domain, SOCK_DGRAM, IPPROTO_UDP);
453#else
454 sudp = socket (domain, SOCK_DGRAM, 0);
455#endif
456 if (sudp < 0)
457 {
458 PRINT_SOCKET_ERROR ("socket");
459 return NULL;
460 }
461
462 if (domain == PF_INET)
463 {
464 /* receive */
465 memset (&sockudp_r, 0, sizeof (struct sockaddr_in));
466 sockudp_r.sin_family = AF_INET;
467#ifdef HAVE_SOCKADDR_IN_SIN_LEN
468 sockudp_r.sin_len = sizeof (struct sockaddr_in);
469#endif
470 if (sameport)
471 sockudp_r.sin_port = htons (PORT);
472 sockudp_r.sin_addr.s_addr = INADDR_ANY;
473 /* send */
474 memset (&sockudp_w, 0, sizeof (struct sockaddr_in));
475 sockudp_w.sin_family = AF_INET;
476 sockudp_w.sin_port = htons (PORT);
477 sockudp_w.sin_addr.s_addr = inet_addr (UPNP_MCAST_ADDR);
478#ifdef HAVE_SOCKADDR_IN_SIN_LEN
479 sockudp_w.sin_len = sizeof (struct sockaddr_in);
480#endif
481 }
482 else
483 {
484 /* receive */
485 memcpy (&sockudp6_r, addr, sizeof (struct sockaddr_in6));
486 if (sameport)
487 sockudp6_r.sin6_port = htons (PORT);
488 else
489 sockudp6_r.sin6_port = 0;
490 sockudp6_r.sin6_addr = any_addr;
491#ifdef HAVE_SOCKADDR_IN_SIN_LEN
492 sockudp6_r.sin6_len = sizeof (struct sockaddr_in6);
493#endif
494 /* send */
495 memset (&sockudp6_w, 0, sizeof (struct sockaddr_in6));
496 sockudp6_w.sin6_family = AF_INET6;
497 sockudp6_w.sin6_port = htons (PORT);
498 inet_pton (AF_INET6, UPNP_MCAST_ADDR6, &sockudp6_w.sin6_addr);
499#ifdef HAVE_SOCKADDR_IN_SIN_LEN
500 sockudp6_w.sin6_len = sizeof (struct sockaddr_in6);
501#endif
502 }
503
504#ifdef WIN32
505 if (setsockopt
506 (sudp, SOL_SOCKET, SO_REUSEADDR, (const char *) &opt, sizeof (opt)) < 0)
507#else
508 if (setsockopt (sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
509#endif
510 {
511 PRINT_SOCKET_ERROR ("setsockopt");
512 return NULL;
513 }
514
515 if (addr)
516 {
517 if (domain == PF_INET)
518 {
519 sockudp_r.sin_addr.s_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
520 if (setsockopt
521 (sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *) &sockudp_r.sin_addr,
522 sizeof (struct in_addr)) < 0)
523 {
524 PRINT_SOCKET_ERROR ("setsockopt");
525 }
526
527 /* Bind to receive response before sending packet */
528 if (bind (sudp, (struct sockaddr *) &sockudp_r, sizeof (struct sockaddr_in))
529 != 0)
530 {
531 PRINT_SOCKET_ERROR ("bind");
532 closesocket (sudp);
533 return NULL;
534 }
535 }
536 else
537 {
538 if (multicastif && !(if_index = if_nametoindex (multicastif)))
539 PRINT_SOCKET_ERROR ("if_nametoindex");
540
541 if (setsockopt
542 (sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index, sizeof (if_index)) < 0)
543 {
544 PRINT_SOCKET_ERROR ("setsockopt");
545 }
546
547 /* Bind to receive response before sending packet */
548 memcpy (&sockudp6_r.sin6_addr, &((struct sockaddr_in6 *) addr)->sin6_addr,
549 sizeof (sockudp6_r.sin6_addr));
550 if (bind (sudp, (struct sockaddr *) &sockudp6_r, sizeof (struct sockaddr_in6))
551 != 0)
552 {
553 PRINT_SOCKET_ERROR ("bind");
554 closesocket (sudp);
555 return NULL;
556 }
557 }
558 }
559
560 /* receiving SSDP response packet */
561 for (n = 0;;)
562 {
563 if (n == 0)
564 {
565 /* sending the SSDP M-SEARCH packet */
566 n = snprintf (bufr, sizeof (bufr),
567 MSearchMsgFmt, deviceList[deviceIndex++]);
568 /*printf("Sending %s", bufr); */
569 if (domain == PF_INET)
570 n = sendto (sudp, bufr, n, 0,
571 (struct sockaddr *) &sockudp_w,
572 sizeof (struct sockaddr_in));
573 else
574 n = sendto (sudp, bufr, n, 0,
575 (struct sockaddr *) &sockudp6_w,
576 sizeof (struct sockaddr_in6));
577
578 if (n < 0)
579 {
580 PRINT_SOCKET_ERROR ("sendto");
581 closesocket (sudp);
582 return devlist;
583 }
584 }
585 /* Waiting for SSDP REPLY packet to M-SEARCH */
586 n = ReceiveData (sudp, bufr, sizeof (bufr), delay);
587
588 if (n < 0)
589 {
590 /* error */
591 closesocket (sudp);
592 return devlist;
593 }
594 else if (n == 0)
595 {
596 /* no data or Time Out */
597 if (devlist || (deviceList[deviceIndex] == 0))
598 {
599 /* no more device type to look for... */
600 closesocket (sudp);
601 return devlist;
602 }
603 }
604 else
605 {
606 const char *descURL = NULL;
607 int urlsize = 0;
608 const char *st = NULL;
609 int stsize = 0;
610 /*printf("%d byte(s) :\n%s\n", n, bufr); *//* affichage du message */
611 parseMSEARCHReply (bufr, n, &descURL, &urlsize, &st, &stsize);
612 if (st && descURL)
613 {
614 /*printf("M-SEARCH Reply:\nST: %.*s\nLocation: %.*s\n",
615 stsize, st, urlsize, descURL); */
616 tmp =
617 (struct UPNPDev *) malloc (sizeof (struct UPNPDev) + urlsize +
618 stsize);
619 tmp->pNext = devlist;
620 tmp->descURL = tmp->buffer;
621 tmp->st = tmp->buffer + 1 + urlsize;
622 memcpy (tmp->buffer, descURL, urlsize);
623 tmp->buffer[urlsize] = '\0';
624 memcpy (tmp->buffer + urlsize + 1, st, stsize);
625 tmp->buffer[urlsize + 1 + stsize] = '\0';
626 devlist = tmp;
627 }
628 }
629 }
630}
631
632/* freeUPNPDevlist() should be used to
633 * free the chained list returned by upnpDiscover() */
634void
635freeUPNPDevlist (struct UPNPDev *devlist)
636{
637 struct UPNPDev *next;
638 while (devlist)
639 {
640 next = devlist->pNext;
641 free (devlist);
642 devlist = next;
643 }
644}
645
646static void
647url_cpy_or_cat (char *dst, const char *src, int n)
648{
649 if ((src[0] == 'h')
650 && (src[1] == 't')
651 && (src[2] == 't')
652 && (src[3] == 'p')
653 && (src[4] == ':') && (src[5] == '/') && (src[6] == '/'))
654 {
655 strncpy (dst, src, n);
656 }
657 else
658 {
659 int l = strlen (dst);
660 if (src[0] != '/')
661 dst[l++] = '/';
662 if (l <= n)
663 strncpy (dst + l, src, n - l);
664 }
665}
666
667/* Prepare the Urls for usage...
668 */
669void
670GetUPNPUrls (struct UPNPUrls *urls, struct IGDdatas *data,
671 const char *descURL)
672{
673 char *p;
674 int n1, n2, n3;
675 n1 = strlen (data->urlbase);
676 if (n1 == 0)
677 n1 = strlen (descURL);
678 n1 += 2; /* 1 byte more for Null terminator, 1 byte for '/' if needed */
679 n2 = n1;
680 n3 = n1;
681 n1 += strlen (data->scpdurl);
682 n2 += strlen (data->controlurl);
683 n3 += strlen (data->controlurl_CIF);
684
685 urls->ipcondescURL = (char *) malloc (n1);
686 urls->controlURL = (char *) malloc (n2);
687 urls->controlURL_CIF = (char *) malloc (n3);
688 /* maintenant on chope la desc du WANIPConnection */
689 if (data->urlbase[0] != '\0')
690 strncpy (urls->ipcondescURL, data->urlbase, n1);
691 else
692 strncpy (urls->ipcondescURL, descURL, n1);
693 p = strchr (urls->ipcondescURL + 7, '/');
694 if (p)
695 p[0] = '\0';
696 strncpy (urls->controlURL, urls->ipcondescURL, n2);
697 strncpy (urls->controlURL_CIF, urls->ipcondescURL, n3);
698
699 url_cpy_or_cat (urls->ipcondescURL, data->scpdurl, n1);
700
701 url_cpy_or_cat (urls->controlURL, data->controlurl, n2);
702
703 url_cpy_or_cat (urls->controlURL_CIF, data->controlurl_CIF, n3);
704
705#ifdef DEBUG
706 printf ("urls->ipcondescURL='%s' %d n1=%d\n", urls->ipcondescURL,
707 strlen (urls->ipcondescURL), n1);
708 printf ("urls->controlURL='%s' %d n2=%d\n", urls->controlURL,
709 strlen (urls->controlURL), n2);
710 printf ("urls->controlURL_CIF='%s' %d n3=%d\n", urls->controlURL_CIF,
711 strlen (urls->controlURL_CIF), n3);
712#endif
713}
714
715void
716FreeUPNPUrls (struct UPNPUrls *urls)
717{
718 if (!urls)
719 return;
720 free (urls->controlURL);
721 urls->controlURL = 0;
722 free (urls->ipcondescURL);
723 urls->ipcondescURL = 0;
724 free (urls->controlURL_CIF);
725 urls->controlURL_CIF = 0;
726}
727
728
729int
730ReceiveData (int socket, char *data, int length, int timeout)
731{
732 int n;
733#ifndef WIN32
734 struct pollfd fds[1]; /* for the poll */
735 fds[0].fd = socket;
736 fds[0].events = POLLIN;
737 n = poll (fds, 1, timeout);
738 if (n < 0)
739 {
740 PRINT_SOCKET_ERROR ("poll");
741 return -1;
742 }
743 else if (n == 0)
744 {
745 return 0;
746 }
747#else
748 fd_set socketSet;
749 TIMEVAL timeval;
750 FD_ZERO (&socketSet);
751 FD_SET (socket, &socketSet);
752 timeval.tv_sec = timeout / 1000;
753 timeval.tv_usec = (timeout % 1000) * 1000;
754 /*n = select(0, &socketSet, NULL, NULL, &timeval); */
755 n = select (FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
756 if (n < 0)
757 {
758 PRINT_SOCKET_ERROR ("select");
759 return -1;
760 }
761 else if (n == 0)
762 {
763 return 0;
764 }
765#endif
766 n = recv (socket, data, length, 0);
767 if (n < 0)
768 {
769 PRINT_SOCKET_ERROR ("recv");
770 }
771 return n;
772}
773
774int
775UPNPIGD_IsConnected (struct UPNPUrls *urls, struct IGDdatas *data)
776{
777 char status[64];
778 unsigned int uptime;
779 status[0] = '\0';
780 UPNP_GetStatusInfo (urls->controlURL, data->servicetype,
781 status, &uptime, NULL);
782 if (0 == strcmp ("Connected", status))
783 {
784 return 1;
785 }
786 else
787 return 0;
788}
789
790
791/* UPNP_GetValidIGD() :
792 * return values :
793 * 0 = NO IGD found
794 * 1 = A valid connected IGD has been found
795 * 2 = A valid IGD has been found but it reported as
796 * not connected
797 * 3 = an UPnP device has been found but was not recognized as an IGD
798 *
799 * In any non zero return case, the urls and data structures
800 * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
801 * free allocated memory.
802 */
803int
804UPNP_GetValidIGD (struct UPNPDev *devlist,
805 struct UPNPUrls *urls,
806 struct IGDdatas *data, char *lanaddr, int lanaddrlen)
807{
808 char *descXML;
809 int descXMLsize = 0;
810 struct UPNPDev *dev;
811 int ndev = 0;
812 int state; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
813 if (!devlist)
814 {
815#ifdef DEBUG
816 printf ("Empty devlist\n");
817#endif
818 return 0;
819 }
820 for (state = 1; state <= 3; state++)
821 {
822 for (dev = devlist; dev; dev = dev->pNext)
823 {
824 /* we should choose an internet gateway device.
825 * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
826 descXML = miniwget_getaddr (dev->descURL, &descXMLsize,
827 lanaddr, lanaddrlen);
828 if (descXML)
829 {
830 ndev++;
831 memset (data, 0, sizeof (struct IGDdatas));
832 memset (urls, 0, sizeof (struct UPNPUrls));
833 parserootdesc (descXML, descXMLsize, data);
834 free (descXML);
835 descXML = NULL;
836 if (0 == strcmp (data->servicetype_CIF,
837 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")
838 || state >= 3)
839 {
840 GetUPNPUrls (urls, data, dev->descURL);
841
842#ifdef DEBUG
843 printf ("UPNPIGD_IsConnected(%s) = %d\n",
844 urls->controlURL, UPNPIGD_IsConnected (urls, data));
845#endif
846 if ((state >= 2) || UPNPIGD_IsConnected (urls, data))
847 return state;
848 FreeUPNPUrls (urls);
849 }
850 memset (data, 0, sizeof (struct IGDdatas));
851 }
852#ifdef DEBUG
853 else
854 {
855 printf ("error getting XML description %s\n", dev->descURL);
856 }
857#endif
858 }
859 }
860 return 0;
861}
862
863/* UPNP_GetIGDFromUrl()
864 * Used when skipping the discovery process.
865 * return value :
866 * 0 - Not ok
867 * 1 - OK */
868int
869UPNP_GetIGDFromUrl (const char *rootdescurl,
870 struct UPNPUrls *urls,
871 struct IGDdatas *data, char *lanaddr, int lanaddrlen)
872{
873 char *descXML;
874 int descXMLsize = 0;
875 descXML = miniwget_getaddr (rootdescurl, &descXMLsize, lanaddr, lanaddrlen);
876 if (descXML)
877 {
878 memset (data, 0, sizeof (struct IGDdatas));
879 memset (urls, 0, sizeof (struct UPNPUrls));
880 parserootdesc (descXML, descXMLsize, data);
881 free (descXML);
882 descXML = NULL;
883 GetUPNPUrls (urls, data, rootdescurl);
884 return 1;
885 }
886 else
887 {
888 return 0;
889 }
890}
diff --git a/src/nat/miniupnp/miniupnpc.h b/src/nat/miniupnp/miniupnpc.h
new file mode 100644
index 000000000..8aa047b64
--- /dev/null
+++ b/src/nat/miniupnp/miniupnpc.h
@@ -0,0 +1,120 @@
1/* $Id: miniupnpc.h,v 1.18 2008/09/25 18:02:50 nanard Exp $ */
2/* Project: miniupnp
3 * http://miniupnp.free.fr/
4 * Author: Thomas Bernard
5 * Copyright (c) 2005-2006 Thomas Bernard
6 * This software is subjects to the conditions detailed
7 * in the LICENCE file provided within this distribution */
8#ifndef __MINIUPNPC_H__
9#define __MINIUPNPC_H__
10
11#include "declspec.h"
12#include "igd_desc_parse.h"
13
14#ifdef WIN32
15#include <winsock2.h>
16#else
17#include <sys/socket.h>
18#endif
19
20#ifdef __cplusplus
21extern "C"
22{
23#endif
24
25/* Structures definitions : */
26 struct UPNParg
27 {
28 const char *elt;
29 const char *val;
30 };
31
32 int simpleUPnPcommand (int s, const char *, const char *,
33 const char *, struct UPNParg *, char *, int *);
34
35 struct UPNPDev
36 {
37 struct UPNPDev *pNext;
38 char *descURL;
39 char *st;
40 char buffer[2];
41 };
42
43/* upnpDiscover()
44 * discover UPnP devices on the network.
45 * The discovered devices are returned as a chained list.
46 * It is up to the caller to free the list with freeUPNPDevlist().
47 * delay (in millisecond) is the maximum time for waiting any device
48 * response.
49 * If available, device list will be obtained from MiniSSDPd.
50 * Default path for minissdpd socket will be used if minissdpdsock argument
51 * is NULL.
52 * If multicastif is not NULL, it will be used instead of the default
53 * multicast interface for sending SSDP discover packets.
54 * If sameport is not null, SSDP packets will be sent from the source port
55 * 1900 (same as destination port) otherwise system assign a source port. */
56 LIBSPEC struct UPNPDev *upnpDiscover (int delay, const char *multicastif, const struct sockaddr *addr,
57 const char *minissdpdsock, int sameport);
58/* freeUPNPDevlist()
59 * free list returned by upnpDiscover() */
60 LIBSPEC void freeUPNPDevlist (struct UPNPDev *devlist);
61
62/* parserootdesc() :
63 * parse root XML description of a UPnP device and fill the IGDdatas
64 * structure. */
65 LIBSPEC void parserootdesc (const char *, int, struct IGDdatas *);
66
67/* structure used to get fast access to urls
68 * controlURL: controlURL of the WANIPConnection
69 * ipcondescURL: url of the description of the WANIPConnection
70 * controlURL_CIF: controlURL of the WANCommonInterfaceConfig
71 */
72 struct UPNPUrls
73 {
74 char *controlURL;
75 char *ipcondescURL;
76 char *controlURL_CIF;
77 };
78
79/* UPNP_GetValidIGD() :
80 * return values :
81 * 0 = NO IGD found
82 * 1 = A valid connected IGD has been found
83 * 2 = A valid IGD has been found but it reported as
84 * not connected
85 * 3 = an UPnP device has been found but was not recognized as an IGD
86 *
87 * In any non zero return case, the urls and data structures
88 * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
89 * free allocated memory.
90 */
91 LIBSPEC int
92 UPNP_GetValidIGD (struct UPNPDev *devlist,
93 struct UPNPUrls *urls,
94 struct IGDdatas *data, char *lanaddr, int lanaddrlen);
95
96/* UPNP_GetIGDFromUrl()
97 * Used when skipping the discovery process.
98 * return value :
99 * 0 - Not ok
100 * 1 - OK */
101 LIBSPEC int
102 UPNP_GetIGDFromUrl (const char *rootdescurl,
103 struct UPNPUrls *urls,
104 struct IGDdatas *data, char *lanaddr, int lanaddrlen);
105
106 LIBSPEC void GetUPNPUrls (struct UPNPUrls *, struct IGDdatas *,
107 const char *);
108
109 LIBSPEC void FreeUPNPUrls (struct UPNPUrls *);
110
111/* Reads data from the specified socket.
112 * Returns the number of bytes read if successful, zero if no bytes were
113 * read or if we timed out. Returns negative if there was an error. */
114 int ReceiveData (int socket, char *data, int length, int timeout);
115
116#ifdef __cplusplus
117}
118#endif
119
120#endif
diff --git a/src/nat/miniupnp/miniupnpcstrings.h b/src/nat/miniupnp/miniupnpcstrings.h
new file mode 100644
index 000000000..3245e1f69
--- /dev/null
+++ b/src/nat/miniupnp/miniupnpcstrings.h
@@ -0,0 +1,14 @@
1/* $Id: miniupnpcstrings.h,v 1.2 2008/10/14 17:39:04 nanard Exp $ */
2/* Project: miniupnp
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * Author: Thomas Bernard
5 * Copyright (c) 2005-2008 Thomas Bernard
6 * This software is subjects to the conditions detailed
7 * in the LICENCE file provided within this distribution */
8#ifndef __MINIUPNPCSTRINGS_H__
9#define __MINIUPNPCSTRINGS_H__
10
11#define OS_STRING "Debian/4.0"
12#define MINIUPNPC_VERSION_STRING "1.2"
13
14#endif
diff --git a/src/nat/miniupnp/miniwget.c b/src/nat/miniupnp/miniwget.c
new file mode 100644
index 000000000..38d5610d8
--- /dev/null
+++ b/src/nat/miniupnp/miniwget.c
@@ -0,0 +1,224 @@
1/* $Id: miniwget.c,v 1.22 2009/02/28 10:36:35 nanard Exp $ */
2/* Project : miniupnp
3 * Author : Thomas Bernard
4 * Copyright (c) 2005 Thomas Bernard
5 * This software is subject to the conditions detailed in the
6 * LICENCE file provided in this distribution.
7 * */
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include "miniupnpc.h"
12#ifdef WIN32
13#include <winsock2.h>
14#include <io.h>
15#define MAXHOSTNAMELEN 64
16#define MIN(x,y) (((x)<(y))?(x):(y))
17#define snprintf _snprintf
18#define herror
19#define socklen_t int
20#else
21#include <unistd.h>
22#include <sys/param.h>
23#include <sys/socket.h>
24#include <netdb.h>
25#include <netinet/in.h>
26#include <arpa/inet.h>
27#define closesocket close
28#endif
29#if defined(__sun) || defined(sun)
30#define MIN(x,y) (((x)<(y))?(x):(y))
31#endif
32
33#include "miniupnpcstrings.h"
34
35/* miniwget2() :
36 * */
37static void *
38miniwget2 (const char *url, const char *host,
39 unsigned short port, const char *path,
40 int *size, char *addr_str, int addr_str_len)
41{
42 char buf[2048];
43 int s;
44 struct sockaddr_in dest;
45 struct hostent *hp;
46 *size = 0;
47 hp = gethostbyname (host);
48 if (hp == NULL)
49 {
50 herror (host);
51 return NULL;
52 }
53 /* memcpy((char *)&dest.sin_addr, hp->h_addr, hp->h_length); */
54 memcpy (&dest.sin_addr, hp->h_addr, sizeof (dest.sin_addr));
55 memset (dest.sin_zero, 0, sizeof (dest.sin_zero));
56 s = socket (PF_INET, SOCK_STREAM, 0);
57 if (s < 0)
58 {
59 perror ("socket");
60 return NULL;
61 }
62 dest.sin_family = AF_INET;
63 dest.sin_port = htons (port);
64 if (connect (s, (struct sockaddr *) &dest, sizeof (struct sockaddr_in)) < 0)
65 {
66 perror ("connect");
67 closesocket (s);
68 return NULL;
69 }
70
71 /* get address for caller ! */
72 if (addr_str)
73 {
74 struct sockaddr_in saddr;
75 socklen_t len;
76
77 len = sizeof (saddr);
78 getsockname (s, (struct sockaddr *) &saddr, &len);
79#ifndef WIN32
80 inet_ntop (AF_INET, &saddr.sin_addr, addr_str, addr_str_len);
81#else
82 /* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
83 * But his function make a string with the port : nn.nn.nn.nn:port */
84/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
85 NULL, addr_str, (DWORD *)&addr_str_len))
86 {
87 printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
88 }*/
89 strncpy (addr_str, inet_ntoa (saddr.sin_addr), addr_str_len);
90#endif
91#ifdef DEBUG
92 printf ("address miniwget : %s\n", addr_str);
93#endif
94 }
95
96 snprintf (buf, sizeof (buf),
97 "GET %s HTTP/1.1\r\n"
98 "Host: %s:%d\r\n"
99 "Connection: Close\r\n"
100 "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/"
101 MINIUPNPC_VERSION_STRING "\r\n" "\r\n", path, host, port);
102 /*write(s, buf, strlen(buf)); */
103 send (s, buf, strlen (buf), 0);
104 {
105 int n, headers = 1;
106 char *respbuffer = NULL;
107 int allreadyread = 0;
108 /*while((n = recv(s, buf, 2048, 0)) > 0) */
109 while ((n = ReceiveData (s, buf, 2048, 5000)) > 0)
110 {
111 if (headers)
112 {
113 int i = 0;
114 while (i < n - 3)
115 {
116 if (buf[i] == '\r' && buf[i + 1] == '\n'
117 && buf[i + 2] == '\r' && buf[i + 3] == '\n')
118 {
119 headers = 0; /* end */
120 if (i < n - 4)
121 {
122 respbuffer = (char *) realloc ((void *) respbuffer,
123 allreadyread + (n - i -
124 4));
125 memcpy (respbuffer + allreadyread, buf + i + 4,
126 n - i - 4);
127 allreadyread += (n - i - 4);
128 }
129 break;
130 }
131 i++;
132 }
133 }
134 else
135 {
136 respbuffer = (char *) realloc ((void *) respbuffer,
137 allreadyread + n);
138 memcpy (respbuffer + allreadyread, buf, n);
139 allreadyread += n;
140 }
141 }
142 *size = allreadyread;
143#ifdef DEBUG
144 printf ("%d bytes read\n", *size);
145#endif
146 closesocket (s);
147 return respbuffer;
148 }
149}
150
151/* parseURL()
152 * arguments :
153 * url : source string not modified
154 * hostname : hostname destination string (size of MAXHOSTNAMELEN+1)
155 * port : port (destination)
156 * path : pointer to the path part of the URL
157 *
158 * Return values :
159 * 0 - Failure
160 * 1 - Success */
161int
162parseURL (const char *url, char *hostname, unsigned short *port, char **path)
163{
164 char *p1, *p2, *p3;
165 p1 = strstr (url, "://");
166 if (!p1)
167 return 0;
168 p1 += 3;
169 if ((url[0] != 'h') || (url[1] != 't')
170 || (url[2] != 't') || (url[3] != 'p'))
171 return 0;
172 p2 = strchr (p1, ':');
173 p3 = strchr (p1, '/');
174 if (!p3)
175 return 0;
176 memset (hostname, 0, MAXHOSTNAMELEN + 1);
177 if (!p2 || (p2 > p3))
178 {
179 strncpy (hostname, p1, MIN (MAXHOSTNAMELEN, (int) (p3 - p1)));
180 *port = 80;
181 }
182 else
183 {
184 strncpy (hostname, p1, MIN (MAXHOSTNAMELEN, (int) (p2 - p1)));
185 *port = 0;
186 p2++;
187 while ((*p2 >= '0') && (*p2 <= '9'))
188 {
189 *port *= 10;
190 *port += (unsigned short) (*p2 - '0');
191 p2++;
192 }
193 }
194 *path = p3;
195 return 1;
196}
197
198void *
199miniwget (const char *url, int *size)
200{
201 unsigned short port;
202 char *path;
203 /* protocol://host:port/chemin */
204 char hostname[MAXHOSTNAMELEN + 1];
205 *size = 0;
206 if (!parseURL (url, hostname, &port, &path))
207 return NULL;
208 return miniwget2 (url, hostname, port, path, size, 0, 0);
209}
210
211void *
212miniwget_getaddr (const char *url, int *size, char *addr, int addrlen)
213{
214 unsigned short port;
215 char *path;
216 /* protocol://host:port/chemin */
217 char hostname[MAXHOSTNAMELEN + 1];
218 *size = 0;
219 if (addr)
220 addr[0] = '\0';
221 if (!parseURL (url, hostname, &port, &path))
222 return NULL;
223 return miniwget2 (url, hostname, port, path, size, addr, addrlen);
224}
diff --git a/src/nat/miniupnp/miniwget.h b/src/nat/miniupnp/miniwget.h
new file mode 100644
index 000000000..04e91df64
--- /dev/null
+++ b/src/nat/miniupnp/miniwget.h
@@ -0,0 +1,28 @@
1/* $Id: miniwget.h,v 1.5 2007/01/29 20:27:23 nanard Exp $ */
2/* Project : miniupnp
3 * Author : Thomas Bernard
4 * Copyright (c) 2005 Thomas Bernard
5 * This software is subject to the conditions detailed in the
6 * LICENCE file provided in this distribution.
7 * */
8#ifndef __MINIWGET_H__
9#define __MINIWGET_H__
10
11#include "declspec.h"
12
13#ifdef __cplusplus
14extern "C"
15{
16#endif
17
18 LIBSPEC void *miniwget (const char *, int *);
19
20 LIBSPEC void *miniwget_getaddr (const char *, int *, char *, int);
21
22 int parseURL (const char *, char *, unsigned short *, char **);
23
24#ifdef __cplusplus
25}
26#endif
27
28#endif
diff --git a/src/nat/miniupnp/minixml.c b/src/nat/miniupnp/minixml.c
new file mode 100644
index 000000000..094118cfa
--- /dev/null
+++ b/src/nat/miniupnp/minixml.c
@@ -0,0 +1,200 @@
1/* $Id: minixml.c,v 1.6 2007/05/15 18:14:08 nanard Exp $ */
2/* minixml.c : the minimum size a xml parser can be ! */
3/* Project : miniupnp
4 * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
5 * Author : Thomas Bernard
6
7Copyright (c) 2005-2007, Thomas BERNARD
8All rights reserved.
9
10Redistribution and use in source and binary forms, with or without
11modification, are permitted provided that the following conditions are met:
12
13 * Redistributions of source code must retain the above copyright notice,
14 this list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 this list of conditions and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
18 * The name of the author may not be used to endorse or promote products
19 derived from this software without specific prior written permission.
20
21THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31POSSIBILITY OF SUCH DAMAGE.
32*/
33#include "minixml.h"
34
35/* parseatt : used to parse the argument list
36 * return 0 (false) in case of success and -1 (true) if the end
37 * of the xmlbuffer is reached. */
38int
39parseatt (struct xmlparser *p)
40{
41 const char *attname;
42 int attnamelen;
43 const char *attvalue;
44 int attvaluelen;
45 while (p->xml < p->xmlend)
46 {
47 if (*p->xml == '/' || *p->xml == '>')
48 return 0;
49 if (!IS_WHITE_SPACE (*p->xml))
50 {
51 char sep;
52 attname = p->xml;
53 attnamelen = 0;
54 while (*p->xml != '=' && !IS_WHITE_SPACE (*p->xml))
55 {
56 attnamelen++;
57 p->xml++;
58 if (p->xml >= p->xmlend)
59 return -1;
60 }
61 while (*(p->xml++) != '=')
62 {
63 if (p->xml >= p->xmlend)
64 return -1;
65 }
66 while (IS_WHITE_SPACE (*p->xml))
67 {
68 p->xml++;
69 if (p->xml >= p->xmlend)
70 return -1;
71 }
72 sep = *p->xml;
73 if (sep == '\'' || sep == '\"')
74 {
75 p->xml++;
76 if (p->xml >= p->xmlend)
77 return -1;
78 attvalue = p->xml;
79 attvaluelen = 0;
80 while (*p->xml != sep)
81 {
82 attvaluelen++;
83 p->xml++;
84 if (p->xml >= p->xmlend)
85 return -1;
86 }
87 }
88 else
89 {
90 attvalue = p->xml;
91 attvaluelen = 0;
92 while (!IS_WHITE_SPACE (*p->xml)
93 && *p->xml != '>' && *p->xml != '/')
94 {
95 attvaluelen++;
96 p->xml++;
97 if (p->xml >= p->xmlend)
98 return -1;
99 }
100 }
101 /*printf("%.*s='%.*s'\n",
102 attnamelen, attname, attvaluelen, attvalue); */
103 if (p->attfunc)
104 p->attfunc (p->data, attname, attnamelen, attvalue, attvaluelen);
105 }
106 p->xml++;
107 }
108 return -1;
109}
110
111/* parseelt parse the xml stream and
112 * call the callback functions when needed... */
113void
114parseelt (struct xmlparser *p)
115{
116 int i;
117 const char *elementname;
118 while (p->xml < (p->xmlend - 1))
119 {
120 if ((p->xml)[0] == '<' && (p->xml)[1] != '?')
121 {
122 i = 0;
123 elementname = ++p->xml;
124 while (!IS_WHITE_SPACE (*p->xml)
125 && (*p->xml != '>') && (*p->xml != '/'))
126 {
127 i++;
128 p->xml++;
129 if (p->xml >= p->xmlend)
130 return;
131 /* to ignore namespace : */
132 if (*p->xml == ':')
133 {
134 i = 0;
135 elementname = ++p->xml;
136 }
137 }
138 if (i > 0)
139 {
140 if (p->starteltfunc)
141 p->starteltfunc (p->data, elementname, i);
142 if (parseatt (p))
143 return;
144 if (*p->xml != '/')
145 {
146 const char *data;
147 i = 0;
148 data = ++p->xml;
149 if (p->xml >= p->xmlend)
150 return;
151 while (IS_WHITE_SPACE (*p->xml))
152 {
153 p->xml++;
154 if (p->xml >= p->xmlend)
155 return;
156 }
157 while (*p->xml != '<')
158 {
159 i++;
160 p->xml++;
161 if (p->xml >= p->xmlend)
162 return;
163 }
164 if (i > 0 && p->datafunc)
165 p->datafunc (p->data, data, i);
166 }
167 }
168 else if (*p->xml == '/')
169 {
170 i = 0;
171 elementname = ++p->xml;
172 if (p->xml >= p->xmlend)
173 return;
174 while ((*p->xml != '>'))
175 {
176 i++;
177 p->xml++;
178 if (p->xml >= p->xmlend)
179 return;
180 }
181 if (p->endeltfunc)
182 p->endeltfunc (p->data, elementname, i);
183 p->xml++;
184 }
185 }
186 else
187 {
188 p->xml++;
189 }
190 }
191}
192
193/* the parser must be initialized before calling this function */
194void
195parsexml (struct xmlparser *parser)
196{
197 parser->xml = parser->xmlstart;
198 parser->xmlend = parser->xmlstart + parser->xmlsize;
199 parseelt (parser);
200}
diff --git a/src/nat/miniupnp/minixml.h b/src/nat/miniupnp/minixml.h
new file mode 100644
index 000000000..6ffb987ac
--- /dev/null
+++ b/src/nat/miniupnp/minixml.h
@@ -0,0 +1,37 @@
1/* $Id: minixml.h,v 1.6 2006/11/30 11:47:21 nanard Exp $ */
2/* minimal xml parser
3 *
4 * Project : miniupnp
5 * Website : http://miniupnp.free.fr/
6 * Author : Thomas Bernard
7 * Copyright (c) 2005 Thomas Bernard
8 * This software is subject to the conditions detailed in the
9 * LICENCE file provided in this distribution.
10 * */
11#ifndef __MINIXML_H__
12#define __MINIXML_H__
13#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
14
15/* if a callback function pointer is set to NULL,
16 * the function is not called */
17struct xmlparser
18{
19 const char *xmlstart;
20 const char *xmlend;
21 const char *xml; /* pointer to current character */
22 int xmlsize;
23 void *data;
24 void (*starteltfunc) (void *, const char *, int);
25 void (*endeltfunc) (void *, const char *, int);
26 void (*datafunc) (void *, const char *, int);
27 void (*attfunc) (void *, const char *, int, const char *, int);
28};
29
30/* parsexml()
31 * the xmlparser structure must be initialized before the call
32 * the following structure members have to be initialized :
33 * xmlstart, xmlsize, data, *func
34 * xml is for internal usage, xmlend is computed automatically */
35void parsexml (struct xmlparser *);
36
37#endif
diff --git a/src/nat/miniupnp/upnpcommands.c b/src/nat/miniupnp/upnpcommands.c
new file mode 100644
index 000000000..7a342540a
--- /dev/null
+++ b/src/nat/miniupnp/upnpcommands.c
@@ -0,0 +1,606 @@
1/* $Id: upnpcommands.c,v 1.24 2009/04/17 21:21:19 nanard Exp $ */
2/* Project : miniupnp
3 * Author : Thomas Bernard
4 * Copyright (c) 2005-2009 Thomas Bernard
5 * This software is subject to the conditions detailed in the
6 * LICENCE file provided in this distribution.
7 * */
8#include <stdlib.h>
9#include <stdio.h>
10#include <string.h>
11#include "upnpcommands.h"
12#include "miniupnpc.h"
13
14static UNSIGNED_INTEGER
15my_atoui (const char *s)
16{
17 return s ? ((UNSIGNED_INTEGER) STRTOUI (s, NULL, 0)) : 0;
18}
19
20/*
21 * */
22UNSIGNED_INTEGER
23UPNP_GetTotalBytesSent (const char *controlURL, const char *servicetype)
24{
25 struct NameValueParserData pdata;
26 char buffer[4096];
27 int bufsize = 4096;
28 unsigned int r = 0;
29 char *p;
30 simpleUPnPcommand (-1, controlURL, servicetype, "GetTotalBytesSent", 0,
31 buffer, &bufsize);
32 ParseNameValue (buffer, bufsize, &pdata);
33 /*DisplayNameValueList(buffer, bufsize); */
34 p = GetValueFromNameValueList (&pdata, "NewTotalBytesSent");
35 r = my_atoui (p);
36 ClearNameValueList (&pdata);
37 return r;
38}
39
40/*
41 * */
42UNSIGNED_INTEGER
43UPNP_GetTotalBytesReceived (const char *controlURL, const char *servicetype)
44{
45 struct NameValueParserData pdata;
46 char buffer[4096];
47 int bufsize = 4096;
48 unsigned int r = 0;
49 char *p;
50 simpleUPnPcommand (-1, controlURL, servicetype, "GetTotalBytesReceived", 0,
51 buffer, &bufsize);
52 ParseNameValue (buffer, bufsize, &pdata);
53 /*DisplayNameValueList(buffer, bufsize); */
54 p = GetValueFromNameValueList (&pdata, "NewTotalBytesReceived");
55 r = my_atoui (p);
56 ClearNameValueList (&pdata);
57 return r;
58}
59
60/*
61 * */
62UNSIGNED_INTEGER
63UPNP_GetTotalPacketsSent (const char *controlURL, const char *servicetype)
64{
65 struct NameValueParserData pdata;
66 char buffer[4096];
67 int bufsize = 4096;
68 unsigned int r = 0;
69 char *p;
70 simpleUPnPcommand (-1, controlURL, servicetype, "GetTotalPacketsSent", 0,
71 buffer, &bufsize);
72 ParseNameValue (buffer, bufsize, &pdata);
73 /*DisplayNameValueList(buffer, bufsize); */
74 p = GetValueFromNameValueList (&pdata, "NewTotalPacketsSent");
75 r = my_atoui (p);
76 ClearNameValueList (&pdata);
77 return r;
78}
79
80/*
81 * */
82UNSIGNED_INTEGER
83UPNP_GetTotalPacketsReceived (const char *controlURL, const char *servicetype)
84{
85 struct NameValueParserData pdata;
86 char buffer[4096];
87 int bufsize = 4096;
88 unsigned int r = 0;
89 char *p;
90 simpleUPnPcommand (-1, controlURL, servicetype, "GetTotalPacketsReceived",
91 0, buffer, &bufsize);
92 ParseNameValue (buffer, bufsize, &pdata);
93 /*DisplayNameValueList(buffer, bufsize); */
94 p = GetValueFromNameValueList (&pdata, "NewTotalPacketsReceived");
95 r = my_atoui (p);
96 ClearNameValueList (&pdata);
97 return r;
98}
99
100/* UPNP_GetStatusInfo() call the corresponding UPNP method
101 * returns the current status and uptime */
102int
103UPNP_GetStatusInfo (const char *controlURL,
104 const char *servicetype,
105 char *status, unsigned int *uptime, char *lastconnerror)
106{
107 struct NameValueParserData pdata;
108 char buffer[4096];
109 int bufsize = 4096;
110 char *p;
111 char *up;
112 char *err;
113 int ret = UPNPCOMMAND_UNKNOWN_ERROR;
114
115 if (!status && !uptime)
116 return UPNPCOMMAND_INVALID_ARGS;
117
118 simpleUPnPcommand (-1, controlURL, servicetype, "GetStatusInfo", 0, buffer,
119 &bufsize);
120 ParseNameValue (buffer, bufsize, &pdata);
121 /*DisplayNameValueList(buffer, bufsize); */
122 up = GetValueFromNameValueList (&pdata, "NewUptime");
123 p = GetValueFromNameValueList (&pdata, "NewConnectionStatus");
124 err = GetValueFromNameValueList (&pdata, "NewLastConnectionError");
125 if (p && up)
126 ret = UPNPCOMMAND_SUCCESS;
127
128 if (status)
129 {
130 if (p)
131 {
132 strncpy (status, p, 64);
133 status[63] = '\0';
134 }
135 else
136 status[0] = '\0';
137 }
138
139 if (uptime)
140 {
141 if (up)
142 sscanf (up, "%u", uptime);
143 else
144 uptime = 0;
145 }
146
147 if (lastconnerror)
148 {
149 if (err)
150 {
151 strncpy (lastconnerror, err, 64);
152 lastconnerror[63] = '\0';
153 }
154 else
155 lastconnerror[0] = '\0';
156 }
157
158 p = GetValueFromNameValueList (&pdata, "errorCode");
159 if (p)
160 {
161 ret = UPNPCOMMAND_UNKNOWN_ERROR;
162 sscanf (p, "%d", &ret);
163 }
164 ClearNameValueList (&pdata);
165 return ret;
166}
167
168/* UPNP_GetConnectionTypeInfo() call the corresponding UPNP method
169 * returns the connection type */
170int
171UPNP_GetConnectionTypeInfo (const char *controlURL,
172 const char *servicetype, char *connectionType)
173{
174 struct NameValueParserData pdata;
175 char buffer[4096];
176 int bufsize = 4096;
177 char *p;
178 int ret = UPNPCOMMAND_UNKNOWN_ERROR;
179
180 if (!connectionType)
181 return UPNPCOMMAND_INVALID_ARGS;
182
183 simpleUPnPcommand (-1, controlURL, servicetype,
184 "GetConnectionTypeInfo", 0, buffer, &bufsize);
185 ParseNameValue (buffer, bufsize, &pdata);
186 p = GetValueFromNameValueList (&pdata, "NewConnectionType");
187 /*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes"); */
188 /* PossibleConnectionTypes will have several values.... */
189 if (p)
190 {
191 strncpy (connectionType, p, 64);
192 connectionType[63] = '\0';
193 ret = UPNPCOMMAND_SUCCESS;
194 }
195 else
196 connectionType[0] = '\0';
197 p = GetValueFromNameValueList (&pdata, "errorCode");
198 if (p)
199 {
200 ret = UPNPCOMMAND_UNKNOWN_ERROR;
201 sscanf (p, "%d", &ret);
202 }
203 ClearNameValueList (&pdata);
204 return ret;
205}
206
207/* UPNP_GetLinkLayerMaxBitRate() call the corresponding UPNP method.
208 * Returns 2 values: Downloadlink bandwidth and Uplink bandwidth.
209 * One of the values can be null
210 * Note : GetLinkLayerMaxBitRates belongs to WANPPPConnection:1 only
211 * We can use the GetCommonLinkProperties from WANCommonInterfaceConfig:1 */
212int
213UPNP_GetLinkLayerMaxBitRates (const char *controlURL, const char *servicetype,
214 unsigned int *bitrateDown,
215 unsigned int *bitrateUp)
216{
217 struct NameValueParserData pdata;
218 char buffer[4096];
219 int bufsize = 4096;
220 int ret = UPNPCOMMAND_UNKNOWN_ERROR;
221 char *down;
222 char *up;
223 char *p;
224
225 if (!bitrateDown && !bitrateUp)
226 return UPNPCOMMAND_INVALID_ARGS;
227
228 /* shouldn't we use GetCommonLinkProperties ? */
229 simpleUPnPcommand (-1, controlURL, servicetype,
230 "GetCommonLinkProperties", 0, buffer, &bufsize);
231 /*"GetLinkLayerMaxBitRates", 0, buffer, &bufsize); */
232 /*DisplayNameValueList(buffer, bufsize); */
233 ParseNameValue (buffer, bufsize, &pdata);
234 /*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate"); */
235 /*up = GetValueFromNameValueList(&pdata, "NewUpstreamMaxBitRate"); */
236 down = GetValueFromNameValueList (&pdata, "NewLayer1DownstreamMaxBitRate");
237 up = GetValueFromNameValueList (&pdata, "NewLayer1UpstreamMaxBitRate");
238 /*GetValueFromNameValueList(&pdata, "NewWANAccessType"); */
239 /*GetValueFromNameValueList(&pdata, "NewPhysicalLinkSatus"); */
240 if (down && up)
241 ret = UPNPCOMMAND_SUCCESS;
242
243 if (bitrateDown)
244 {
245 if (down)
246 sscanf (down, "%u", bitrateDown);
247 else
248 *bitrateDown = 0;
249 }
250
251 if (bitrateUp)
252 {
253 if (up)
254 sscanf (up, "%u", bitrateUp);
255 else
256 *bitrateUp = 0;
257 }
258 p = GetValueFromNameValueList (&pdata, "errorCode");
259 if (p)
260 {
261 ret = UPNPCOMMAND_UNKNOWN_ERROR;
262 sscanf (p, "%d", &ret);
263 }
264 ClearNameValueList (&pdata);
265 return ret;
266}
267
268
269/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
270 * if the third arg is not null the value is copied to it.
271 * at least 128 bytes must be available
272 *
273 * Return values :
274 * 0 : SUCCESS
275 * NON ZERO : ERROR Either an UPnP error code or an unknown error.
276 *
277 * 402 Invalid Args - See UPnP Device Architecture section on Control.
278 * 501 Action Failed - See UPnP Device Architecture section on Control.
279 */
280int
281UPNP_GetExternalIPAddress (const char *controlURL,
282 const char *servicetype, char *extIpAdd)
283{
284 struct NameValueParserData pdata;
285 char buffer[4096];
286 int bufsize = 4096;
287 char *p;
288 int ret = UPNPCOMMAND_UNKNOWN_ERROR;
289
290 if (!extIpAdd || !controlURL || !servicetype)
291 return UPNPCOMMAND_INVALID_ARGS;
292
293 simpleUPnPcommand (-1, controlURL, servicetype, "GetExternalIPAddress", 0,
294 buffer, &bufsize);
295 /*DisplayNameValueList(buffer, bufsize); */
296 ParseNameValue (buffer, bufsize, &pdata);
297 /*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") ); */
298 p = GetValueFromNameValueList (&pdata, "NewExternalIPAddress");
299 if (p)
300 {
301 strncpy (extIpAdd, p, 128);
302 extIpAdd[127] = '\0';
303 ret = UPNPCOMMAND_SUCCESS;
304 }
305 else
306 extIpAdd[0] = '\0';
307
308 p = GetValueFromNameValueList (&pdata, "errorCode");
309 if (p)
310 {
311 ret = UPNPCOMMAND_UNKNOWN_ERROR;
312 sscanf (p, "%d", &ret);
313 }
314
315 ClearNameValueList (&pdata);
316 return ret;
317}
318
319int
320UPNP_AddPortMapping (const char *controlURL, const char *servicetype,
321 const char *extPort,
322 const char *inPort,
323 const char *inClient,
324 const char *desc,
325 const char *proto, const char *remoteHost)
326{
327 struct UPNParg *AddPortMappingArgs;
328 char buffer[4096];
329 int bufsize = 4096;
330 struct NameValueParserData pdata;
331 const char *resVal;
332 int ret;
333
334 if (!inPort || !inClient || !proto || !extPort)
335 return UPNPCOMMAND_INVALID_ARGS;
336
337 AddPortMappingArgs = calloc (9, sizeof (struct UPNParg));
338 AddPortMappingArgs[0].elt = "NewRemoteHost";
339 AddPortMappingArgs[0].val = remoteHost;
340 AddPortMappingArgs[1].elt = "NewExternalPort";
341 AddPortMappingArgs[1].val = extPort;
342 AddPortMappingArgs[2].elt = "NewProtocol";
343 AddPortMappingArgs[2].val = proto;
344 AddPortMappingArgs[3].elt = "NewInternalPort";
345 AddPortMappingArgs[3].val = inPort;
346 AddPortMappingArgs[4].elt = "NewInternalClient";
347 AddPortMappingArgs[4].val = inClient;
348 AddPortMappingArgs[5].elt = "NewEnabled";
349 AddPortMappingArgs[5].val = "1";
350 AddPortMappingArgs[6].elt = "NewPortMappingDescription";
351 AddPortMappingArgs[6].val = desc ? desc : "libminiupnpc";
352 AddPortMappingArgs[7].elt = "NewLeaseDuration";
353 AddPortMappingArgs[7].val = "0";
354 simpleUPnPcommand (-1, controlURL, servicetype, "AddPortMapping",
355 AddPortMappingArgs, buffer, &bufsize);
356 /*DisplayNameValueList(buffer, bufsize); */
357 /*buffer[bufsize] = '\0'; */
358 /*puts(buffer); */
359 ParseNameValue (buffer, bufsize, &pdata);
360 resVal = GetValueFromNameValueList (&pdata, "errorCode");
361 if (resVal)
362 {
363 /* printf("AddPortMapping errorCode = '%s'\n", resVal); */
364 ret = UPNPCOMMAND_UNKNOWN_ERROR;
365 sscanf (resVal, "%d", &ret);
366 }
367 else
368 {
369 ret = UPNPCOMMAND_SUCCESS;
370 }
371 ClearNameValueList (&pdata);
372 free (AddPortMappingArgs);
373 return ret;
374}
375
376int
377UPNP_DeletePortMapping (const char *controlURL, const char *servicetype,
378 const char *extPort, const char *proto,
379 const char *remoteHost)
380{
381 /*struct NameValueParserData pdata; */
382 struct UPNParg *DeletePortMappingArgs;
383 char buffer[4096];
384 int bufsize = 4096;
385 struct NameValueParserData pdata;
386 const char *resVal;
387 int ret;
388
389 if (!extPort || !proto)
390 return UPNPCOMMAND_INVALID_ARGS;
391
392 DeletePortMappingArgs = calloc (4, sizeof (struct UPNParg));
393 DeletePortMappingArgs[0].elt = "NewRemoteHost";
394 DeletePortMappingArgs[0].val = remoteHost;
395 DeletePortMappingArgs[1].elt = "NewExternalPort";
396 DeletePortMappingArgs[1].val = extPort;
397 DeletePortMappingArgs[2].elt = "NewProtocol";
398 DeletePortMappingArgs[2].val = proto;
399 simpleUPnPcommand (-1, controlURL, servicetype,
400 "DeletePortMapping",
401 DeletePortMappingArgs, buffer, &bufsize);
402 /*DisplayNameValueList(buffer, bufsize); */
403 ParseNameValue (buffer, bufsize, &pdata);
404 resVal = GetValueFromNameValueList (&pdata, "errorCode");
405 if (resVal)
406 {
407 ret = UPNPCOMMAND_UNKNOWN_ERROR;
408 sscanf (resVal, "%d", &ret);
409 }
410 else
411 {
412 ret = UPNPCOMMAND_SUCCESS;
413 }
414 ClearNameValueList (&pdata);
415 free (DeletePortMappingArgs);
416 return ret;
417}
418
419int
420UPNP_GetGenericPortMappingEntry (const char *controlURL,
421 const char *servicetype,
422 const char *index,
423 char *extPort,
424 char *intClient,
425 char *intPort,
426 char *protocol,
427 char *desc,
428 char *enabled, char *rHost, char *duration)
429{
430 struct NameValueParserData pdata;
431 struct UPNParg *GetPortMappingArgs;
432 char buffer[4096];
433 int bufsize = 4096;
434 char *p;
435 int r = UPNPCOMMAND_UNKNOWN_ERROR;
436 if (!index)
437 return UPNPCOMMAND_INVALID_ARGS;
438 intClient[0] = '\0';
439 intPort[0] = '\0';
440 GetPortMappingArgs = calloc (2, sizeof (struct UPNParg));
441 GetPortMappingArgs[0].elt = "NewPortMappingIndex";
442 GetPortMappingArgs[0].val = index;
443 simpleUPnPcommand (-1, controlURL, servicetype,
444 "GetGenericPortMappingEntry",
445 GetPortMappingArgs, buffer, &bufsize);
446 ParseNameValue (buffer, bufsize, &pdata);
447 p = GetValueFromNameValueList (&pdata, "NewRemoteHost");
448 if (p && rHost)
449 {
450 strncpy (rHost, p, 64);
451 rHost[63] = '\0';
452 }
453 p = GetValueFromNameValueList (&pdata, "NewExternalPort");
454 if (p && extPort)
455 {
456 strncpy (extPort, p, 6);
457 extPort[5] = '\0';
458 r = UPNPCOMMAND_SUCCESS;
459 }
460 p = GetValueFromNameValueList (&pdata, "NewProtocol");
461 if (p && protocol)
462 {
463 strncpy (protocol, p, 4);
464 protocol[3] = '\0';
465 }
466 p = GetValueFromNameValueList (&pdata, "NewInternalClient");
467 if (p && intClient)
468 {
469 strncpy (intClient, p, 128);
470 intClient[127] = '\0';
471 r = 0;
472 }
473 p = GetValueFromNameValueList (&pdata, "NewInternalPort");
474 if (p && intPort)
475 {
476 strncpy (intPort, p, 6);
477 intPort[5] = '\0';
478 }
479 p = GetValueFromNameValueList (&pdata, "NewEnabled");
480 if (p && enabled)
481 {
482 strncpy (enabled, p, 4);
483 enabled[3] = '\0';
484 }
485 p = GetValueFromNameValueList (&pdata, "NewPortMappingDescription");
486 if (p && desc)
487 {
488 strncpy (desc, p, 80);
489 desc[79] = '\0';
490 }
491 p = GetValueFromNameValueList (&pdata, "NewLeaseDuration");
492 if (p && duration)
493 {
494 strncpy (duration, p, 16);
495 duration[15] = '\0';
496 }
497 p = GetValueFromNameValueList (&pdata, "errorCode");
498 if (p)
499 {
500 r = UPNPCOMMAND_UNKNOWN_ERROR;
501 sscanf (p, "%d", &r);
502 }
503 ClearNameValueList (&pdata);
504 free (GetPortMappingArgs);
505 return r;
506}
507
508int
509UPNP_GetPortMappingNumberOfEntries (const char *controlURL,
510 const char *servicetype,
511 unsigned int *numEntries)
512{
513 struct NameValueParserData pdata;
514 char buffer[4096];
515 int bufsize = 4096;
516 char *p;
517 int ret = UPNPCOMMAND_UNKNOWN_ERROR;
518 simpleUPnPcommand (-1, controlURL, servicetype,
519 "GetPortMappingNumberOfEntries", 0, buffer, &bufsize);
520#ifdef DEBUG
521 DisplayNameValueList (buffer, bufsize);
522#endif
523 ParseNameValue (buffer, bufsize, &pdata);
524
525 p = GetValueFromNameValueList (&pdata, "NewPortMappingNumberOfEntries");
526 if (numEntries && p)
527 {
528 *numEntries = 0;
529 sscanf (p, "%u", numEntries);
530 ret = UPNPCOMMAND_SUCCESS;
531 }
532
533 p = GetValueFromNameValueList (&pdata, "errorCode");
534 if (p)
535 {
536 ret = UPNPCOMMAND_UNKNOWN_ERROR;
537 sscanf (p, "%d", &ret);
538 }
539
540 ClearNameValueList (&pdata);
541 return ret;
542}
543
544/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping
545 * the result is returned in the intClient and intPort strings
546 * please provide 128 and 6 bytes of data */
547int
548UPNP_GetSpecificPortMappingEntry (const char *controlURL,
549 const char *servicetype,
550 const char *extPort,
551 const char *proto,
552 char *intClient, char *intPort)
553{
554 struct NameValueParserData pdata;
555 struct UPNParg *GetPortMappingArgs;
556 char buffer[4096];
557 int bufsize = 4096;
558 char *p;
559 int ret = UPNPCOMMAND_UNKNOWN_ERROR;
560
561 if (!intPort || !intClient || !extPort || !proto)
562 return UPNPCOMMAND_INVALID_ARGS;
563
564 GetPortMappingArgs = calloc (4, sizeof (struct UPNParg));
565 GetPortMappingArgs[0].elt = "NewRemoteHost";
566 GetPortMappingArgs[1].elt = "NewExternalPort";
567 GetPortMappingArgs[1].val = extPort;
568 GetPortMappingArgs[2].elt = "NewProtocol";
569 GetPortMappingArgs[2].val = proto;
570 simpleUPnPcommand (-1, controlURL, servicetype,
571 "GetSpecificPortMappingEntry",
572 GetPortMappingArgs, buffer, &bufsize);
573 /*fd = simpleUPnPcommand(fd, controlURL, data.servicetype, "GetSpecificPortMappingEntry", AddPortMappingArgs, buffer, &bufsize); */
574 /*DisplayNameValueList(buffer, bufsize); */
575 ParseNameValue (buffer, bufsize, &pdata);
576
577 p = GetValueFromNameValueList (&pdata, "NewInternalClient");
578 if (p)
579 {
580 strncpy (intClient, p, 128);
581 intClient[127] = '\0';
582 ret = UPNPCOMMAND_SUCCESS;
583 }
584 else
585 intClient[0] = '\0';
586
587 p = GetValueFromNameValueList (&pdata, "NewInternalPort");
588 if (p)
589 {
590 strncpy (intPort, p, 6);
591 intPort[5] = '\0';
592 }
593 else
594 intPort[0] = '\0';
595
596 p = GetValueFromNameValueList (&pdata, "errorCode");
597 if (p)
598 {
599 ret = UPNPCOMMAND_UNKNOWN_ERROR;
600 sscanf (p, "%d", &ret);
601 }
602
603 ClearNameValueList (&pdata);
604 free (GetPortMappingArgs);
605 return ret;
606}
diff --git a/src/nat/miniupnp/upnpcommands.h b/src/nat/miniupnp/upnpcommands.h
new file mode 100644
index 000000000..fa1d604ae
--- /dev/null
+++ b/src/nat/miniupnp/upnpcommands.h
@@ -0,0 +1,189 @@
1/* $Id: upnpcommands.h,v 1.17 2009/04/17 21:21:19 nanard Exp $ */
2/* Miniupnp project : http://miniupnp.free.fr/
3 * Author : Thomas Bernard
4 * Copyright (c) 2005-2008 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
19extern "C"
20{
21#endif
22
23#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
24#define UNSIGNED_INTEGER unsigned long long
25#define STRTOUI strtoull
26#else
27#define UNSIGNED_INTEGER unsigned int
28#define STRTOUI strtoul
29#endif
30
31 LIBSPEC UNSIGNED_INTEGER
32 UPNP_GetTotalBytesSent (const char *controlURL, const char *servicetype);
33
34 LIBSPEC UNSIGNED_INTEGER
35 UPNP_GetTotalBytesReceived (const char *controlURL,
36 const char *servicetype);
37
38 LIBSPEC UNSIGNED_INTEGER
39 UPNP_GetTotalPacketsSent (const char *controlURL,
40 const char *servicetype);
41
42 LIBSPEC UNSIGNED_INTEGER
43 UPNP_GetTotalPacketsReceived (const char *controlURL,
44 const char *servicetype);
45
46/* UPNP_GetStatusInfo()
47 * status and lastconnerror are 64 byte buffers
48 * Return values :
49 * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
50 * or a UPnP Error code */
51 LIBSPEC int
52 UPNP_GetStatusInfo (const char *controlURL,
53 const char *servicetype,
54 char *status,
55 unsigned int *uptime, char *lastconnerror);
56
57/* UPNP_GetConnectionTypeInfo()
58 * argument connectionType is a 64 character buffer
59 * Return Values :
60 * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
61 * or a UPnP Error code */
62 LIBSPEC int
63 UPNP_GetConnectionTypeInfo (const char *controlURL,
64 const char *servicetype,
65 char *connectionType);
66
67/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
68 * if the third arg is not null the value is copied to it.
69 * at least 128 bytes must be available
70 *
71 * Return values :
72 * 0 : SUCCESS
73 * NON ZERO : ERROR Either an UPnP error code or an unknown error.
74 *
75 * possible UPnP Errors :
76 * 402 Invalid Args - See UPnP Device Architecture section on Control.
77 * 501 Action Failed - See UPnP Device Architecture section on Control. */
78 LIBSPEC int
79 UPNP_GetExternalIPAddress (const char *controlURL,
80 const char *servicetype, char *extIpAdd);
81
82/* UPNP_GetLinkLayerMaxBitRates()
83 * call WANCommonInterfaceConfig:1#GetCommonLinkProperties
84 *
85 * return values :
86 * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
87 * or a UPnP Error Code. */
88 LIBSPEC int
89 UPNP_GetLinkLayerMaxBitRates (const char *controlURL,
90 const char *servicetype,
91 unsigned int *bitrateDown,
92 unsigned int *bitrateUp);
93
94/* UPNP_AddPortMapping()
95 * if desc is NULL, it will be defaulted to "libminiupnpc"
96 * remoteHost is usually NULL because IGD don't support it.
97 *
98 * Return values :
99 * 0 : SUCCESS
100 * NON ZERO : ERROR. Either an UPnP error code or an unknown error.
101 *
102 * List of possible UPnP errors for AddPortMapping :
103 * errorCode errorDescription (short) - Description (long)
104 * 402 Invalid Args - See UPnP Device Architecture section on Control.
105 * 501 Action Failed - See UPnP Device Architecture section on Control.
106 * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
107 * wild-carded
108 * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
109 * 718 ConflictInMappingEntry - The port mapping entry specified conflicts
110 * with a mapping assigned previously to another client
111 * 724 SamePortValuesRequired - Internal and External port values
112 * must be the same
113 * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
114 * permanent lease times on port mappings
115 * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
116 * and cannot be a specific IP address or DNS name
117 * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
118 * cannot be a specific port value */
119 LIBSPEC int
120 UPNP_AddPortMapping (const char *controlURL, const char *servicetype,
121 const char *extPort,
122 const char *inPort,
123 const char *inClient,
124 const char *desc,
125 const char *proto, const char *remoteHost);
126
127/* UPNP_DeletePortMapping()
128 * Use same argument values as what was used for AddPortMapping().
129 * remoteHost is usually NULL because IGD don't support it.
130 * Return Values :
131 * 0 : SUCCESS
132 * NON ZERO : error. Either an UPnP error code or an undefined error.
133 *
134 * List of possible UPnP errors for DeletePortMapping :
135 * 402 Invalid Args - See UPnP Device Architecture section on Control.
136 * 714 NoSuchEntryInArray - The specified value does not exist in the array */
137 LIBSPEC int
138 UPNP_DeletePortMapping (const char *controlURL, const char *servicetype,
139 const char *extPort, const char *proto,
140 const char *remoteHost);
141
142/* UPNP_GetPortMappingNumberOfEntries()
143 * not supported by all routers */
144 LIBSPEC int
145 UPNP_GetPortMappingNumberOfEntries (const char *controlURL,
146 const char *servicetype,
147 unsigned int *num);
148
149/* UPNP_GetSpecificPortMappingEntry retrieves an existing port mapping
150 * the result is returned in the intClient and intPort strings
151 * please provide 128 and 6 bytes of data
152 *
153 * return value :
154 * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
155 * or a UPnP Error Code. */
156 LIBSPEC int
157 UPNP_GetSpecificPortMappingEntry (const char *controlURL,
158 const char *servicetype,
159 const char *extPort,
160 const char *proto,
161 char *intClient, char *intPort);
162
163/* UPNP_GetGenericPortMappingEntry()
164 *
165 * return value :
166 * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
167 * or a UPnP Error Code.
168 *
169 * Possible UPNP Error codes :
170 * 402 Invalid Args - See UPnP Device Architecture section on Control.
171 * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
172 */
173 LIBSPEC int
174 UPNP_GetGenericPortMappingEntry (const char *controlURL,
175 const char *servicetype,
176 const char *index,
177 char *extPort,
178 char *intClient,
179 char *intPort,
180 char *protocol,
181 char *desc,
182 char *enabled,
183 char *rHost, char *duration);
184
185#ifdef __cplusplus
186}
187#endif
188
189#endif
diff --git a/src/nat/miniupnp/upnpreplyparse.c b/src/nat/miniupnp/upnpreplyparse.c
new file mode 100644
index 000000000..9aa895d1d
--- /dev/null
+++ b/src/nat/miniupnp/upnpreplyparse.c
@@ -0,0 +1,122 @@
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
15static void
16NameValueParserStartElt (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
25static void
26NameValueParserGetData (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
40void
41ParseNameValue (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
57void
58ClearNameValueList (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
68char *
69GetValueFromNameValueList (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); nv = nv->entries.le_next)
76 {
77 if (strcmp (nv->name, Name) == 0)
78 p = nv->value;
79 }
80 return p;
81}
82
83#if 0
84/* useless now that minixml ignores namespaces by itself */
85char *
86GetValueFromNameValueListIgnoreNS (struct NameValueParserData *pdata,
87 const char *Name)
88{
89 struct NameValue *nv;
90 char *p = NULL;
91 char *pname;
92 for (nv = pdata->head.lh_first;
93 (nv != NULL) && (p == NULL); nv = nv->entries.le_next)
94 {
95 pname = strrchr (nv->name, ':');
96 if (pname)
97 pname++;
98 else
99 pname = nv->name;
100 if (strcmp (pname, Name) == 0)
101 p = nv->value;
102 }
103 return p;
104}
105#endif
106
107/* debug all-in-one function
108 * do parsing then display to stdout */
109#ifdef DEBUG
110void
111DisplayNameValueList (char *buffer, int bufsize)
112{
113 struct NameValueParserData pdata;
114 struct NameValue *nv;
115 ParseNameValue (buffer, bufsize, &pdata);
116 for (nv = pdata.head.lh_first; nv != NULL; nv = nv->entries.le_next)
117 {
118 printf ("%s = %s\n", nv->name, nv->value);
119 }
120 ClearNameValueList (&pdata);
121}
122#endif
diff --git a/src/nat/miniupnp/upnpreplyparse.h b/src/nat/miniupnp/upnpreplyparse.h
new file mode 100644
index 000000000..a1c8a9431
--- /dev/null
+++ b/src/nat/miniupnp/upnpreplyparse.h
@@ -0,0 +1,60 @@
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
18extern "C"
19{
20#endif
21
22 struct NameValue
23 {
24 LIST_ENTRY (NameValue) entries;
25 char name[64];
26 char value[64];
27 };
28
29 struct NameValueParserData
30 {
31 LIST_HEAD (listhead, NameValue) head;
32 char curelt[64];
33 };
34
35/* ParseNameValue() */
36 void
37 ParseNameValue (const char *buffer, int bufsize,
38 struct NameValueParserData *data);
39
40/* ClearNameValueList() */
41 void ClearNameValueList (struct NameValueParserData *pdata);
42
43/* GetValueFromNameValueList() */
44 char *GetValueFromNameValueList (struct NameValueParserData *pdata,
45 const char *Name);
46
47/* GetValueFromNameValueListIgnoreNS() */
48 char *GetValueFromNameValueListIgnoreNS (struct NameValueParserData *pdata,
49 const char *Name);
50
51/* DisplayNameValueList() */
52#ifdef DEBUG
53 void DisplayNameValueList (char *buffer, int bufsize);
54#endif
55
56#ifdef __cplusplus
57}
58#endif
59
60#endif
diff --git a/src/nat/nat.c b/src/nat/nat.c
new file mode 100644
index 000000000..dbe4ef643
--- /dev/null
+++ b/src/nat/nat.c
@@ -0,0 +1,375 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/*
22 * Parts of this file have been adapted from the Transmission project:
23 * Originally licensed by the GPL version 2.
24 * Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.com>
25 */
26
27/**
28 * @file nat/nat.c
29 * @brief Library handling UPnP and NAT-PMP port forwarding and
30 * external IP address retrieval
31 *
32 * @author Milan Bouchet-Valat
33 */
34
35#ifdef __cplusplus
36extern "C"
37{
38#if 0 /* keep Emacsens' auto-indent happy */
39}
40#endif
41#endif
42
43#include <errno.h>
44#include <string.h>
45#include <stdio.h>
46
47#include <sys/types.h>
48
49#include "platform.h"
50#include "gnunet_common.h"
51#include "gnunet_util_lib.h"
52#include "gnunet_nat_lib.h"
53#include "natpmp.h"
54#include "upnp.h"
55
56/* Component name for logging */
57#define COMP_NAT _("NAT")
58#define DEBUG
59
60struct GNUNET_NAT_Handle
61{
62 int is_enabled;
63
64 GNUNET_NAT_port_forwarding natpmp_status;
65 GNUNET_NAT_port_forwarding upnp_status;
66
67 int should_change;
68 u_short public_port;
69
70 GNUNET_NAT_UPNP_Handle *upnp;
71 GNUNET_NAT_NATPMP_Handle *natpmp;
72
73 struct GNUNET_SCHEDULER_Handle *sched;
74 GNUNET_SCHEDULER_TaskIdentifier pulse_timer;
75
76 struct sockaddr *local_addr; /* LAN address as passed by the caller */
77 struct sockaddr *ext_addr; /* External address as reported by NAT box */
78 struct sockaddr *contact_addr; /* External address and port where paquets are redirected*/
79 GNUNET_NAT_AddressCallback callback;
80 void *callback_cls;
81};
82
83#ifdef DEBUG
84static const char *
85get_nat_state_str (int state)
86{
87 switch (state)
88 {
89 /* we're in the process of trying to set up port forwarding */
90 case GNUNET_NAT_PORT_MAPPING:
91 return "Starting";
92
93 /* we've successfully forwarded the port */
94 case GNUNET_NAT_PORT_MAPPED:
95 return "Forwarded";
96
97 /* we're cancelling the port forwarding */
98 case GNUNET_NAT_PORT_UNMAPPING:
99 return "Stopping";
100
101 /* the port isn't forwarded */
102 case GNUNET_NAT_PORT_UNMAPPED:
103 return "Not forwarded";
104
105 case GNUNET_NAT_PORT_ERROR:
106 return "Redirection failed";
107 }
108
109 return "notfound";
110}
111#endif
112
113static int
114get_traversal_status (const GNUNET_NAT_Handle * s)
115{
116 return MAX (s->natpmp_status, s->upnp_status);
117}
118
119/**
120 * Compare the sin(6)_addr fields of AF_INET or AF_INET(6) sockaddr.
121 * @param a first sockaddr
122 * @param second sockaddr
123 * @returns 0 if addresses are equal, non-null value otherwise */
124int
125GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b)
126{
127 if (!(a && b))
128 return -1;
129
130 if (a->sa_family == AF_INET && b->sa_family == AF_INET)
131 return memcmp (&(((struct sockaddr_in *) a)->sin_addr),
132 &(((struct sockaddr_in *) b)->sin_addr),
133 sizeof (struct in_addr));
134 else if (a->sa_family == AF_INET6 && b->sa_family == AF_INET6)
135 return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr),
136 &(((struct sockaddr_in6 *) b)->sin6_addr),
137 sizeof (struct in6_addr));
138 else
139 return -1;
140}
141
142/* Deal with a new IP address or port redirection:
143 * Send signals with the appropriate sockaddr (IP and port), free and changes
144 * or nullify the previous sockaddr. Change the port if needed.
145 */
146static void
147notify_change (GNUNET_NAT_Handle *nat, struct sockaddr *addr, int new_port_mapped)
148{
149 static int port_mapped = GNUNET_NO;
150
151 /* Nothing to do. We already check in nat_pulse() that addr has changed */
152 if (new_port_mapped == port_mapped)
153 return;
154
155 port_mapped = new_port_mapped;
156
157 if (nat->contact_addr && nat->callback)
158 (*nat->callback) (nat->callback_cls, GNUNET_NO, (struct sockaddr *) &nat->contact_addr,
159 sizeof (nat->contact_addr));
160
161 /* At this point, we're sure contact_addr has changed */
162 if (nat->contact_addr)
163 {
164 GNUNET_free (nat->contact_addr);
165 nat->contact_addr = NULL;
166 }
167
168 /* No address, don't signal a new one */
169 if (!addr)
170 {
171 if (nat->ext_addr)
172 GNUNET_free (nat->ext_addr);
173 nat->ext_addr = NULL;
174 return;
175 }
176 /* Copy the new address and use it */
177 else if (addr != nat->ext_addr)
178 {
179 if (nat->ext_addr)
180 GNUNET_free (nat->ext_addr);
181 nat->ext_addr = GNUNET_malloc (sizeof (*addr));
182 memcpy (nat->ext_addr, addr, sizeof (*addr));
183 }
184
185 /* Recreate the ext_addr:public_port bogus address to pass to the callback */
186 if (nat->ext_addr->sa_family == AF_INET)
187 {
188 struct sockaddr_in *tmp_addr;
189 tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
190 tmp_addr->sin_family = AF_INET;
191#ifdef HAVE_SOCKADDR_IN_SIN_LEN
192 tmp_addr->sin_len = sizeof (struct sockaddr_in);
193#endif
194 tmp_addr->sin_port = port_mapped ? htons (nat->public_port) : 0;
195 tmp_addr->sin_addr = ((struct sockaddr_in *) nat->ext_addr)->sin_addr;
196 nat->contact_addr = (struct sockaddr *) tmp_addr;
197 if (nat->callback)
198 (*nat->callback) (nat->callback_cls, GNUNET_YES, nat->contact_addr,
199 sizeof (struct sockaddr_in));
200 }
201 else if (nat->ext_addr->sa_family == AF_INET6)
202 {
203 struct sockaddr_in6 *tmp_addr;
204 tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
205 tmp_addr->sin6_family = AF_INET6;
206#ifdef HAVE_SOCKADDR_IN_SIN_LEN
207 tmp_addr->sin6_len = sizeof (struct sockaddr_in6);
208#endif
209 tmp_addr->sin6_port = port_mapped ? htons (nat->public_port) : 0;
210 tmp_addr->sin6_addr = ((struct sockaddr_in6 *) nat->ext_addr)->sin6_addr;
211 nat->contact_addr = (struct sockaddr *) tmp_addr;
212 if (nat->callback)
213 (*nat->callback) (nat->callback_cls, GNUNET_YES, nat->contact_addr,
214 sizeof (struct sockaddr_in6));
215 }
216}
217
218static void
219nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
220{
221 GNUNET_NAT_Handle *nat = cls;
222 static int first_warning = GNUNET_YES;
223 int old_status;
224 int new_status;
225 int port_mapped;
226 struct sockaddr *ext_addr_upnp = NULL;
227 struct sockaddr *ext_addr_natpmp = NULL;
228
229 old_status = get_traversal_status (nat);
230
231 /* Only update the protocol that has been successful until now */
232 if (nat->upnp_status >= GNUNET_NAT_PORT_UNMAPPED)
233 nat->upnp_status =
234 GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
235 &ext_addr_upnp);
236 else if (nat->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED)
237 nat->natpmp_status =
238 GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
239 &ext_addr_natpmp);
240 else
241 {
242 nat->upnp_status =
243 GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
244 &ext_addr_upnp);
245 nat->natpmp_status =
246 GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
247 &ext_addr_natpmp);
248 }
249
250 new_status = get_traversal_status (nat);
251
252 if (old_status != new_status &&
253 (new_status == GNUNET_NAT_PORT_UNMAPPED || new_status == GNUNET_NAT_PORT_ERROR))
254 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
255 _("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n"));
256
257#ifdef DEBUG
258 if (new_status != old_status)
259 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT,
260 _("State changed from \"%s\" to \"%s\"\n"),
261 get_nat_state_str (old_status),
262 get_nat_state_str (new_status));
263#endif
264
265 port_mapped = (new_status == GNUNET_NAT_PORT_MAPPED);
266 if (!(ext_addr_upnp || ext_addr_natpmp))
267 {
268 /* Address has just changed and we could not get it, or it's the first try */
269 if (nat->ext_addr || first_warning)
270 {
271 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
272 _("Could not determine external IP address\n"));
273 first_warning = GNUNET_NO;
274 }
275
276 notify_change (nat, NULL, port_mapped);
277 }
278 else if (ext_addr_upnp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_upnp) != 0)
279 {
280 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
281 _("External IP address changed from %s to %s\n"),
282 GNUNET_a2s (nat->ext_addr, sizeof (nat->ext_addr)),
283 GNUNET_a2s (ext_addr_upnp, sizeof (ext_addr_upnp)));
284
285 notify_change (nat, ext_addr_upnp, port_mapped);
286 }
287 else if (ext_addr_natpmp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_natpmp) != 0)
288 {
289 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT,
290 _("External IP address changed from %s to %s\n"),
291 GNUNET_a2s (nat->ext_addr, sizeof (nat->ext_addr)),
292 GNUNET_a2s (ext_addr_natpmp, sizeof (ext_addr_natpmp)));
293
294 notify_change (nat, ext_addr_natpmp, port_mapped);
295 }
296
297 nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (nat->sched, GNUNET_NO,
298 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
299 GNUNET_SCHEDULER_NO_TASK,
300 GNUNET_TIME_UNIT_SECONDS,
301 &nat_pulse, nat);
302}
303
304struct GNUNET_NAT_Handle *
305GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle *sched,
306 const struct sockaddr *addr, socklen_t addrlen,
307 GNUNET_NAT_AddressCallback callback, void *callback_cls)
308{
309 GNUNET_NAT_Handle *nat = GNUNET_malloc (sizeof (GNUNET_NAT_Handle));
310
311 if (addr)
312 {
313 GNUNET_assert (addr->sa_family == AF_INET
314 || addr->sa_family == AF_INET6);
315 if (addr->sa_family == AF_INET)
316 {
317 nat->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port);
318// ((struct sockaddr_in *) addr)->sin_port = 0;
319 }
320 else if (addr->sa_family == AF_INET6)
321 {
322 nat->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
323// ((struct sockaddr_in6 *) addr)->sin6_port = 0;
324 }
325 }
326
327 nat->should_change = GNUNET_YES;
328 nat->sched = sched;
329 nat->is_enabled = GNUNET_YES;
330 nat->upnp_status = GNUNET_NAT_PORT_UNMAPPED;
331 nat->natpmp_status = GNUNET_NAT_PORT_UNMAPPED;
332 nat->callback = callback;
333 nat->callback_cls = callback_cls;
334 nat->ext_addr = NULL;
335 nat->contact_addr = NULL;
336 nat->local_addr = GNUNET_malloc (addrlen);
337 memcpy (nat->local_addr, addr, addrlen);
338 nat->natpmp = GNUNET_NAT_NATPMP_init (nat->local_addr, addrlen, nat->public_port);
339 nat->upnp = GNUNET_NAT_UPNP_init (nat->local_addr, addrlen, nat->public_port);
340
341 nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (sched, GNUNET_NO,
342 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
343 GNUNET_SCHEDULER_NO_TASK,
344 GNUNET_TIME_UNIT_SECONDS,
345 &nat_pulse, nat);
346
347 return nat;
348}
349
350void
351GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *nat)
352{
353 struct sockaddr *addr;
354 GNUNET_SCHEDULER_cancel (nat->sched, nat->pulse_timer);
355
356 nat->upnp_status =
357 GNUNET_NAT_UPNP_pulse (nat->upnp, GNUNET_NO, GNUNET_NO,
358 &addr);
359 nat->natpmp_status =
360 GNUNET_NAT_NATPMP_pulse (nat->natpmp, GNUNET_NO,
361 &addr);
362
363 GNUNET_NAT_NATPMP_close (nat->natpmp);
364 GNUNET_NAT_UPNP_close (nat->upnp);
365 if (nat->ext_addr)
366 GNUNET_free (nat->ext_addr);
367 GNUNET_free (nat);
368}
369
370#if 0 /* keep Emacsens' auto-indent happy */
371{
372#endif
373#ifdef __cplusplus
374}
375#endif
diff --git a/src/nat/natpmp.c b/src/nat/natpmp.c
new file mode 100644
index 000000000..335228767
--- /dev/null
+++ b/src/nat/natpmp.c
@@ -0,0 +1,297 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/*
22 * This file has been adapted from the Transmission project:
23 * Originally licensed by the GPL version 2.
24 * Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.com>
25 */
26
27/**
28 * @file nat/natpmp.c
29 * @brief NAT-PMP support for the NAT library
30 *
31 * @author Milan Bouchet-Valat
32 */
33
34#ifdef __cplusplus
35extern "C"
36{
37#if 0 /* keep Emacsens' auto-indent happy */
38}
39#endif
40#endif
41
42#include <assert.h>
43#include <errno.h>
44#include <time.h>
45#include <inttypes.h>
46
47#define ENABLE_STRNATPMPERR
48#include <libnatpmp/natpmp.h>
49
50#include "platform.h"
51#include "gnunet_common.h"
52#include "gnunet_nat_lib.h"
53#include "natpmp.h"
54
55#define LIFETIME_SECS 3600
56#define COMMAND_WAIT_SECS 8
57/* Component name for logging */
58#define COMP_NAT_NATPMP _("NAT (NAT-PMP))")
59
60typedef enum
61{
62 NATPMP_IDLE,
63 NATPMP_ERR,
64 NATPMP_DISCOVER,
65 NATPMP_RECV_PUB,
66 NATPMP_SEND_MAP,
67 NATPMP_RECV_MAP,
68 NATPMP_SEND_UNMAP,
69 NATPMP_RECV_UNMAP
70}
71NATPMP_state;
72
73struct GNUNET_NAT_NATPMP_Handle
74{
75 const struct sockaddr *addr;
76 socklen_t addrlen;
77 int is_mapped;
78 int has_discovered;
79 int port;
80 time_t renew_time;
81 time_t command_time;
82 NATPMP_state state;
83 natpmp_t natpmp;
84};
85
86/**
87***
88**/
89
90static void
91log_val (const char *func, int ret)
92{
93#ifdef DEBUG
94 if (ret == NATPMP_TRYAGAIN)
95 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP, _("%s retry (%d)\n"), func, ret);
96// return;
97 if (ret >= 0)
98 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP, _("%s succeeded (%d)\n"), func, ret);
99 else
100 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
101 "%s failed. natpmp returned %d (%s); errno is %d (%s)\n",
102 func, ret, strnatpmperr (ret), errno, strerror (errno));
103#else
104 return;
105#endif
106}
107
108struct GNUNET_NAT_NATPMP_Handle *
109GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, socklen_t addrlen,
110 u_short port)
111{
112 struct GNUNET_NAT_NATPMP_Handle *nat;
113
114 nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_NATPMP_Handle));
115 nat->state = NATPMP_DISCOVER;
116 nat->port = port;
117 nat->addr = addr;
118 nat->addrlen = addrlen;
119 return nat;
120}
121
122void
123GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat)
124{
125 if (nat)
126 {
127 closenatpmp (&nat->natpmp);
128 GNUNET_free (nat);
129 }
130}
131
132static int
133can_send_command (const struct GNUNET_NAT_NATPMP_Handle *nat)
134{
135 return time (NULL) >= nat->command_time;
136}
137
138static void
139set_command_time (struct GNUNET_NAT_NATPMP_Handle *nat)
140{
141 nat->command_time = time (NULL) + COMMAND_WAIT_SECS;
142}
143
144int
145GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, int is_enabled,
146 struct sockaddr **ext_addr)
147{
148 int ret;
149
150 /* Keep to NULL if address could not be found */
151 *ext_addr = NULL;
152
153 if (is_enabled && (nat->state == NATPMP_DISCOVER))
154 {
155 int val = initnatpmp (&nat->natpmp);
156 log_val ("initnatpmp", val);
157 val = sendpublicaddressrequest (&nat->natpmp);
158 log_val ("sendpublicaddressrequest", val);
159 nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_PUB;
160 nat->has_discovered = 1;
161 set_command_time (nat);
162 }
163
164 if ((nat->state == NATPMP_RECV_PUB) && can_send_command (nat))
165 {
166 natpmpresp_t response;
167 const int val = readnatpmpresponseorretry (&nat->natpmp,
168 &response);
169 log_val ("readnatpmpresponseorretry", val);
170 if (val >= 0)
171 {
172 *ext_addr =
173 GNUNET_malloc (sizeof (response.pnu.publicaddress.addr));
174 memcpy (*ext_addr, &response.pnu.publicaddress.addr,
175 (sizeof (response.pnu.publicaddress.addr)));
176#ifdef DEBUG
177 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
178 _("Found public IP address %s\n"),
179 GNUNET_a2s (*ext_addr, sizeof (*ext_addr)));
180#endif
181 nat->state = NATPMP_IDLE;
182 }
183 else if (val != NATPMP_TRYAGAIN)
184 {
185 nat->state = NATPMP_ERR;
186 }
187 }
188
189 if ((nat->state == NATPMP_IDLE) || (nat->state == NATPMP_ERR))
190 {
191 if (nat->is_mapped && !is_enabled)
192 nat->state = NATPMP_SEND_UNMAP;
193 }
194
195 if ((nat->state == NATPMP_SEND_UNMAP) && can_send_command (nat))
196 {
197 const int val =
198 sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP,
199 nat->port, nat->port,
200 0);
201 log_val ("sendnewportmappingrequest", val);
202 nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_UNMAP;
203 set_command_time (nat);
204 }
205
206 if (nat->state == NATPMP_RECV_UNMAP)
207 {
208 natpmpresp_t resp;
209 const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
210 log_val ("readnatpmpresponseorretry", val);
211 if (val >= 0)
212 {
213 const int p = resp.pnu.newportmapping.privateport;
214 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP,
215 _("No longer forwarding port %d\n"), p);
216 if (nat->port == p)
217 {
218 nat->port = -1;
219 nat->state = NATPMP_IDLE;
220 nat->is_mapped = 0;
221 }
222 }
223 else if (val != NATPMP_TRYAGAIN)
224 {
225 nat->state = NATPMP_ERR;
226 }
227 }
228
229 if (nat->state == NATPMP_IDLE)
230 {
231 if (is_enabled && !nat->is_mapped && nat->has_discovered)
232 nat->state = NATPMP_SEND_MAP;
233
234 else if (nat->is_mapped && time (NULL) >= nat->renew_time)
235 nat->state = NATPMP_SEND_MAP;
236 }
237
238 if ((nat->state == NATPMP_SEND_MAP) && can_send_command (nat))
239 {
240 const int val =
241 sendnewportmappingrequest (&nat->natpmp, NATPMP_PROTOCOL_TCP,
242 nat->port,
243 nat->port,
244 LIFETIME_SECS);
245 log_val ("sendnewportmappingrequest", val);
246 nat->state = val < 0 ? NATPMP_ERR : NATPMP_RECV_MAP;
247 set_command_time (nat);
248 }
249
250 if (nat->state == NATPMP_RECV_MAP)
251 {
252 natpmpresp_t resp;
253 const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
254 log_val ("readnatpmpresponseorretry", val);
255 if (val >= 0)
256 {
257 nat->state = NATPMP_IDLE;
258 nat->is_mapped = 1;
259 nat->renew_time = time (NULL) + LIFETIME_SECS;
260 nat->port = resp.pnu.newportmapping.privateport;
261 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP,
262 _("Port %d forwarded successfully\n"), nat->port);
263 }
264 else if (val != NATPMP_TRYAGAIN)
265 {
266 nat->state = NATPMP_ERR;
267 }
268 }
269
270 switch (nat->state)
271 {
272 case NATPMP_IDLE:
273 ret =
274 nat->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
275 break;
276
277 case NATPMP_DISCOVER:
278 ret = GNUNET_NAT_PORT_UNMAPPED;
279 break;
280
281 case NATPMP_RECV_PUB:
282 case NATPMP_SEND_MAP:
283 case NATPMP_RECV_MAP:
284 ret = GNUNET_NAT_PORT_MAPPING;
285 break;
286
287 case NATPMP_SEND_UNMAP:
288 case NATPMP_RECV_UNMAP:
289 ret = GNUNET_NAT_PORT_UNMAPPING;
290 break;
291
292 default:
293 ret = GNUNET_NAT_PORT_ERROR;
294 break;
295 }
296 return ret;
297}
diff --git a/src/nat/natpmp.h b/src/nat/natpmp.h
new file mode 100644
index 000000000..9acb46b9c
--- /dev/null
+++ b/src/nat/natpmp.h
@@ -0,0 +1,44 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file nat/natpmp.h
23 * @brief NAT-PMP support for the NAT library
24 *
25 * @author Milan Bouchet-Valat
26 */
27
28#ifndef NATPMP_H
29#define NATPMP_H 1
30
31#include "platform.h"
32
33typedef struct GNUNET_NAT_NATPMP_Handle GNUNET_NAT_NATPMP_Handle;
34
35GNUNET_NAT_NATPMP_Handle *GNUNET_NAT_NATPMP_init (const struct sockaddr *addr,
36 socklen_t addrlen,
37 unsigned short port);
38
39void GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat);
40
41int GNUNET_NAT_NATPMP_pulse (GNUNET_NAT_NATPMP_Handle * nat, int is_enabled,
42 struct sockaddr **ext_addr);
43
44#endif /* NATPMP_H */
diff --git a/src/nat/upnp.c b/src/nat/upnp.c
new file mode 100644
index 000000000..05c774edd
--- /dev/null
+++ b/src/nat/upnp.c
@@ -0,0 +1,357 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/*
22 * This file has been adapted from the Transmission project:
23 * Originally licensed by the GPL version 2.
24 * Copyright (C) 2007-2009 Charles Kerr <charles@transmissionbt.com>
25 */
26
27/**
28 * @file nat/upnp.c
29 * @brief UPnP support for the NAT library
30 *
31 * @author Milan Bouchet-Valat
32 */
33
34#ifdef __cplusplus
35extern "C"
36{
37#if 0 /* keep Emacsens' auto-indent happy */
38}
39#endif
40#endif
41
42#include <stdlib.h>
43#include <assert.h>
44#include <errno.h>
45#include <string.h>
46
47#include <miniupnp/miniupnpc.h>
48#include <miniupnp/upnpcommands.h>
49
50#include "platform.h"
51#include "gnunet_common.h"
52#include "gnunet_nat_lib.h"
53#include "upnp.h"
54
55/* Component name for logging */
56#define COMP_NAT_UPNP _("NAT (UPnP)")
57
58typedef enum
59{
60 UPNP_IDLE,
61 UPNP_ERR,
62 UPNP_DISCOVER,
63 UPNP_MAP,
64 UPNP_UNMAP
65}
66UPNP_state;
67
68struct GNUNET_NAT_UPNP_Handle
69{
70 int hasDiscovered;
71 struct UPNPUrls urls;
72 struct IGDdatas data;
73 int port;
74 const struct sockaddr *addr;
75 socklen_t addrlen;
76 unsigned int is_mapped;
77 UPNP_state state;
78 struct sockaddr *ext_addr;
79 const char *iface;
80};
81
82static int
83process_if (void *cls,
84 const char *name,
85 int isDefault,
86 const struct sockaddr *addr,
87 socklen_t addrlen)
88{
89 struct GNUNET_NAT_UPNP_Handle *upnp = cls;
90
91 if (addr && GNUNET_NAT_cmp_addr (upnp->addr, addr) == 0)
92 {
93 upnp->iface = name;
94 return GNUNET_SYSERR;
95 }
96
97 return GNUNET_OK;
98}
99
100GNUNET_NAT_UPNP_Handle *
101GNUNET_NAT_UPNP_init (const struct sockaddr *addr, socklen_t addrlen,
102 u_short port)
103{
104 GNUNET_NAT_UPNP_Handle *upnp =
105 GNUNET_malloc (sizeof (GNUNET_NAT_UPNP_Handle));
106
107 upnp->state = UPNP_DISCOVER;
108 upnp->addr = addr;
109 upnp->addrlen = addrlen;
110 upnp->port = port;
111
112 /* Find the interface corresponding to the address,
113 * on which we should broadcast call for routers */
114 upnp->iface = NULL;
115 GNUNET_OS_network_interfaces_list (process_if, upnp);
116 if (!upnp->iface)
117 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, COMP_NAT_UPNP, "Could not find an interface matching the wanted address.\n");
118
119 return upnp;
120}
121
122void
123GNUNET_NAT_UPNP_close (GNUNET_NAT_UPNP_Handle * handle)
124{
125 GNUNET_assert (!handle->is_mapped);
126 GNUNET_assert ((handle->state == UPNP_IDLE)
127 || (handle->state == UPNP_ERR) || (handle->state == UPNP_DISCOVER));
128
129 if (handle->hasDiscovered)
130 FreeUPNPUrls (&handle->urls);
131 GNUNET_free (handle);
132}
133
134/**
135 * Check state of UPnP NAT: port redirection, external IP address.
136 *
137 *
138 * @param handle the handle for UPnP object
139 * @param is_enabled whether enable port redirection
140 * @param ext_addr pointer for returning external IP address.
141 * Will be set to NULL if address could not be found. Don't free the sockaddr.
142 */
143int
144GNUNET_NAT_UPNP_pulse (GNUNET_NAT_UPNP_Handle * handle, int is_enabled,
145 int doPortCheck, struct sockaddr **ext_addr)
146{
147 int ret;
148
149 if (is_enabled && (handle->state == UPNP_DISCOVER))
150 {
151 struct UPNPDev *devlist;
152 errno = 0;
153 devlist = upnpDiscover (2000, handle->iface, handle->addr, NULL, 0);
154 if (devlist == NULL)
155 {
156#ifdef DEBUG
157 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
158 "upnpDiscover failed (errno %d - %s)\n", errno,
159 strerror (errno));
160#endif
161 }
162 errno = 0;
163 if (UPNP_GetValidIGD (devlist, &handle->urls, &handle->data,
164 NULL, 0))
165 {
166 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
167 _("Found Internet Gateway Device \"%s\"\n"),
168 handle->urls.controlURL);
169 handle->state = UPNP_IDLE;
170 handle->hasDiscovered = 1;
171 }
172 else
173 {
174 handle->state = UPNP_ERR;
175#ifdef DEBUG
176 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
177 "UPNP_GetValidIGD failed (errno %d - %s)\n",
178 errno, strerror (errno));
179#endif
180 }
181 freeUPNPDevlist (devlist);
182 }
183
184 if (handle->state == UPNP_IDLE)
185 {
186 if (handle->is_mapped && !is_enabled)
187 handle->state = UPNP_UNMAP;
188 }
189
190 if (is_enabled && handle->is_mapped && doPortCheck)
191 {
192 char portStr[8];
193 char intPort[8];
194 char intClient[128];
195 int i;
196
197 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
198 i = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL,
199 handle->data.servicetype, portStr,
200 "TCP", intClient, intPort);
201 if (i != UPNPCOMMAND_SUCCESS)
202 {
203 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
204 _("Port %d isn't forwarded\n"), handle->port);
205 handle->is_mapped = GNUNET_NO;
206 }
207 }
208
209 if (handle->state == UPNP_UNMAP)
210 {
211 char portStr[16];
212 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
213 UPNP_DeletePortMapping (handle->urls.controlURL,
214 handle->data.servicetype, portStr, "TCP", NULL);
215 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
216 _
217 ("Stopping port forwarding through \"%s\", service \"%s\"\n"),
218 handle->urls.controlURL, handle->data.servicetype);
219 handle->is_mapped = 0;
220 handle->state = UPNP_IDLE;
221 handle->port = -1;
222 }
223
224 if (handle->state == UPNP_IDLE)
225 {
226 if (is_enabled && !handle->is_mapped)
227 handle->state = UPNP_MAP;
228 }
229
230 if (handle->state == UPNP_MAP)
231 {
232 int err = -1;
233 errno = 0;
234
235 if (!handle->urls.controlURL || !handle->data.servicetype)
236 handle->is_mapped = 0;
237 else
238 {
239 char portStr[16];
240 char desc[64];
241 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
242 GNUNET_snprintf (desc, sizeof (desc), "GNUnet at %d", handle->port);
243 err = UPNP_AddPortMapping (handle->urls.controlURL,
244 handle->data.servicetype,
245 portStr, portStr, GNUNET_a2s (handle->addr, handle->addrlen),
246 desc, "TCP", NULL);
247 handle->is_mapped = !err;
248 }
249 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
250 _
251 ("Port forwarding through \"%s\", service \"%s\"\n"),
252 handle->urls.controlURL, handle->data.servicetype);
253 if (handle->is_mapped)
254 {
255 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
256 _("Port %d forwarded successfully\n"), handle->port);
257 handle->state = UPNP_IDLE;
258 }
259 else
260 {
261 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
262 "Port forwarding failed with error %d (errno %d - %s)\n",
263 err, errno, strerror (errno));
264 handle->state = UPNP_ERR;
265 }
266 }
267
268 if (ext_addr && handle->state != UPNP_DISCOVER)
269 {
270 int err;
271 char addr_str[128];
272 struct in_addr addr;
273 struct in6_addr addr6;
274
275 /* Keep to NULL if address could not be found */
276 *ext_addr = NULL;
277 err = UPNP_GetExternalIPAddress (handle->urls.controlURL,
278 handle->data.servicetype, addr_str);
279 if (err == 0)
280 {
281 if (handle->ext_addr)
282 {
283 GNUNET_free (handle->ext_addr);
284 handle->ext_addr = NULL;
285 }
286
287 /* Try IPv4 and IPv6 as we don't know what's the format */
288 if (inet_aton (addr_str, &addr) != 0)
289 {
290 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
291 handle->ext_addr->sa_family = AF_INET;
292 ((struct sockaddr_in *) handle->ext_addr)->sin_addr = addr;
293 *ext_addr = handle->ext_addr;
294 }
295 else if (inet_pton (AF_INET6, addr_str, &addr6) != 1)
296 {
297 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
298 handle->ext_addr->sa_family = AF_INET6;
299 ((struct sockaddr_in6 *) handle->ext_addr)->sin6_addr = addr6;
300 *ext_addr = handle->ext_addr;
301 }
302 else
303 GNUNET_assert (GNUNET_YES);
304#ifdef DEBUG
305 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
306 _("Found public IP address %s\n"),
307 addr_str);
308#endif
309 }
310 else
311 {
312 *ext_addr = NULL;
313 if (handle->ext_addr)
314 {
315 GNUNET_free (handle->ext_addr);
316 handle->ext_addr = NULL;
317 }
318#ifdef DEBUG
319 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
320 "UPNP_GetExternalIPAddress failed (error %d)\n", err);
321#endif
322 }
323 }
324
325 switch (handle->state)
326 {
327 case UPNP_DISCOVER:
328 ret = GNUNET_NAT_PORT_UNMAPPED;
329 break;
330
331 case UPNP_MAP:
332 ret = GNUNET_NAT_PORT_MAPPING;
333 break;
334
335 case UPNP_UNMAP:
336 ret = GNUNET_NAT_PORT_UNMAPPING;
337 break;
338
339 case UPNP_IDLE:
340 ret =
341 handle->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
342 break;
343
344 default:
345 ret = GNUNET_NAT_PORT_ERROR;
346 break;
347 }
348
349 return ret;
350}
351
352#if 0 /* keep Emacsens' auto-indent happy */
353{
354#endif
355#ifdef __cplusplus
356}
357#endif
diff --git a/src/nat/upnp.h b/src/nat/upnp.h
new file mode 100644
index 000000000..18bba4c12
--- /dev/null
+++ b/src/nat/upnp.h
@@ -0,0 +1,43 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file nat/upnp.h
23 * @brief UPnP support for the NAT library
24 *
25 * @author Milan Bouchet-Valat
26 */
27
28#ifndef UPNP_H
29#define UPNP_H 1
30
31#include "platform.h"
32
33typedef struct GNUNET_NAT_UPNP_Handle GNUNET_NAT_UPNP_Handle;
34
35GNUNET_NAT_UPNP_Handle *GNUNET_NAT_UPNP_init (const struct sockaddr *addr,
36 socklen_t addrlen,
37 unsigned short port);
38
39void GNUNET_NAT_UPNP_close (GNUNET_NAT_UPNP_Handle *);
40
41int GNUNET_NAT_UPNP_pulse (GNUNET_NAT_UPNP_Handle *, int is_enabled,
42 int do_port_check, struct sockaddr **ext_addr);
43#endif /* UPNP_H */