diff options
author | Moon <moon@140774ce-b5e7-0310-ab8b-a85725594a96> | 2009-10-25 09:44:36 +0000 |
---|---|---|
committer | Moon <moon@140774ce-b5e7-0310-ab8b-a85725594a96> | 2009-10-25 09:44:36 +0000 |
commit | b94248cf8b37caaea9436d9973a6641eaeca90cb (patch) | |
tree | d612aa43f7a899630e4a237422a6db9123078143 /src/nat | |
parent | 38fea0d2e4fe5410fcba7b115080a464f9af1930 (diff) | |
download | gnunet-b94248cf8b37caaea9436d9973a6641eaeca90cb.tar.gz gnunet-b94248cf8b37caaea9436d9973a6641eaeca90cb.zip |
initial NAT lib commit (UPnP and NAT-PMP support)
Diffstat (limited to 'src/nat')
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 @@ | |||
1 | SUBDIRS = miniupnp libnatpmp | ||
2 | |||
3 | INCLUDES = -I$(top_srcdir)/src/include | ||
4 | |||
5 | if MINGW | ||
6 | WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols | ||
7 | endif | ||
8 | |||
9 | if USE_COVERAGE | ||
10 | AM_CFLAGS = -fprofile-arcs -ftest-coverage | ||
11 | endif | ||
12 | |||
13 | lib_LTLIBRARIES = libgnunetnat.la | ||
14 | |||
15 | libgnunetnat_la_SOURCES = \ | ||
16 | upnp.c upnp.h \ | ||
17 | natpmp.c natpmp.h \ | ||
18 | nat.c nat.h | ||
19 | |||
20 | libgnunetnat_la_CFLAGS = \ | ||
21 | -I$(top_scrdir)/include | ||
22 | |||
23 | libgnunetnat_la_LIBADD = \ | ||
24 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
25 | $(GN_LIBINTL) @EXT_LIBS@ | ||
26 | |||
27 | libgnunetnat_la_LDFLAGS = \ | ||
28 | $(GN_LIB_LDFLAGS) $(WINFLAGS) \ | ||
29 | -version-info 0:0:0 | ||
30 | |||
31 | check_PROGRAMS = \ | ||
32 | test-nat | ||
33 | |||
34 | TESTS = $(check_PROGRAMS) | ||
35 | |||
36 | test_nat_SOURCES = \ | ||
37 | test_nat.c | ||
38 | |||
39 | test_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 @@ | |||
1 | noinst_LIBRARIES = libnatpmp.a | ||
2 | |||
3 | AM_CPPFLAGS = -DENABLE_STRNATPMPERR | ||
4 | |||
5 | libnatpmp_a_SOURCES = \ | ||
6 | getgateway.c \ | ||
7 | natpmp.c | ||
8 | |||
9 | noinst_HEADERS = \ | ||
10 | declspec.h \ | ||
11 | getgateway.h \ | ||
12 | natpmp.h | ||
13 | |||
14 | extra_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 @@ | |||
1 | libnatpmp is written by Thomas Bernard. | ||
2 | Its homepage is http://miniupnp.tuxfamily.org/libnatpmp.html | ||
3 | This 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 | ||
90 | int | ||
91 | getdefaultgateway (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 | |||
173 | int | ||
174 | getdefaultgateway (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 | |||
267 | struct | ||
268 | { | ||
269 | struct rt_msghdr m_rtm; | ||
270 | char m_space[512]; | ||
271 | } m_rtmsg; | ||
272 | |||
273 | int | ||
274 | getdefaultgateway (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 | ||
362 | int | ||
363 | getdefaultgateway (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 */ | ||
30 | LIBSPEC 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 | |||
37 | int | ||
38 | initnatpmp (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 | |||
107 | int | ||
108 | closenatpmp (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 | |||
117 | static int | ||
118 | sendpendingrequest (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 | |||
134 | static int | ||
135 | sendnatpmprequest (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 | |||
154 | int | ||
155 | getnatpmprequesttimeout (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 | |||
174 | int | ||
175 | sendpublicaddressrequest (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 | |||
187 | int | ||
188 | sendnewportmappingrequest (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 | |||
206 | static int | ||
207 | readnatpmpresponse (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 | |||
288 | int | ||
289 | readnatpmpresponseorretry (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 | ||
336 | const char * | ||
337 | strnatpmperr (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 | |||
36 | typedef 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 | |||
49 | typedef 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 */ | ||
132 | LIBSPEC 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 */ | ||
140 | LIBSPEC 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 */ | ||
148 | LIBSPEC 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 */ | ||
162 | LIBSPEC 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 */ | ||
175 | LIBSPEC 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 */ | ||
194 | LIBSPEC int readnatpmpresponseorretry (natpmp_t * p, natpmpresp_t * response); | ||
195 | |||
196 | #ifdef ENABLE_STRNATPMPERR | ||
197 | LIBSPEC 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 @@ | |||
1 | noinst_LIBRARIES = libminiupnp.a | ||
2 | |||
3 | AM_CPPFLAGS = -DNDEBUG | ||
4 | |||
5 | libminiupnp_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 | |||
15 | noinst_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 | |||
29 | extra_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 @@ | |||
1 | MiniUPnP is written by Thomas Bernard. | ||
2 | Its homepage is http://miniupnp.free.fr/ | ||
3 | This 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) \ | ||
95 | struct 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) \ | ||
107 | struct { \ | ||
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) \ | ||
172 | struct name { \ | ||
173 | struct type *lh_first; /* first element */ \ | ||
174 | } | ||
175 | |||
176 | #define LIST_HEAD_INITIALIZER(head) \ | ||
177 | { NULL } | ||
178 | |||
179 | #define LIST_ENTRY(type) \ | ||
180 | struct { \ | ||
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) \ | ||
250 | struct 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) \ | ||
259 | struct { \ | ||
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) \ | ||
311 | struct 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) \ | ||
320 | struct { \ | ||
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) \ | ||
418 | struct 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) \ | ||
427 | struct { \ | ||
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 */ | ||
18 | void | ||
19 | IGDstartelt (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 */ | ||
37 | void | ||
38 | IGDendelt (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 */ | ||
82 | void | ||
83 | IGDdata (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 | |||
110 | void | ||
111 | printIGD (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) | ||
15 | struct 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 | |||
42 | void IGDstartelt (void *, const char *, int); | ||
43 | void IGDendelt (void *, const char *, int); | ||
44 | void IGDdata (void *, const char *, int); | ||
45 | void 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 */ | ||
35 | static int | ||
36 | httpWrite (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 */ | ||
76 | int | ||
77 | soapPostSubmit (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 *);*/ | ||
11 | int 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 | |||
27 | struct UPNPDev * | ||
28 | getDevicesFromMiniSSDPD (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 | |||
11 | struct 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 */ | ||
54 | void | ||
55 | parserootdesc (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 */ | ||
73 | static int | ||
74 | getcontentlenfromline (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 | |||
113 | static void | ||
114 | getContentLengthAndHeaderLength (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 */ | ||
147 | int | ||
148 | simpleUPnPcommand (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 */ | ||
327 | static void | ||
328 | parseMSEARCHReply (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) */ | ||
398 | struct UPNPDev * | ||
399 | upnpDiscover (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() */ | ||
634 | void | ||
635 | freeUPNPDevlist (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 | |||
646 | static void | ||
647 | url_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 | */ | ||
669 | void | ||
670 | GetUPNPUrls (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 | |||
715 | void | ||
716 | FreeUPNPUrls (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 | |||
729 | int | ||
730 | ReceiveData (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 | |||
774 | int | ||
775 | UPNPIGD_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 | */ | ||
803 | int | ||
804 | UPNP_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 */ | ||
868 | int | ||
869 | UPNP_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 | ||
21 | extern "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 | * */ | ||
37 | static void * | ||
38 | miniwget2 (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 */ | ||
161 | int | ||
162 | parseURL (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 | |||
198 | void * | ||
199 | miniwget (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 | |||
211 | void * | ||
212 | miniwget_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 | ||
14 | extern "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 | |||
7 | Copyright (c) 2005-2007, Thomas BERNARD | ||
8 | All rights reserved. | ||
9 | |||
10 | Redistribution and use in source and binary forms, with or without | ||
11 | modification, 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 | |||
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
24 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||
25 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
26 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
27 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
28 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
29 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
31 | POSSIBILITY 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. */ | ||
38 | int | ||
39 | parseatt (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... */ | ||
113 | void | ||
114 | parseelt (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 */ | ||
194 | void | ||
195 | parsexml (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 */ | ||
17 | struct 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 */ | ||
35 | void 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 | |||
14 | static UNSIGNED_INTEGER | ||
15 | my_atoui (const char *s) | ||
16 | { | ||
17 | return s ? ((UNSIGNED_INTEGER) STRTOUI (s, NULL, 0)) : 0; | ||
18 | } | ||
19 | |||
20 | /* | ||
21 | * */ | ||
22 | UNSIGNED_INTEGER | ||
23 | UPNP_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 | * */ | ||
42 | UNSIGNED_INTEGER | ||
43 | UPNP_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 | * */ | ||
62 | UNSIGNED_INTEGER | ||
63 | UPNP_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 | * */ | ||
82 | UNSIGNED_INTEGER | ||
83 | UPNP_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 */ | ||
102 | int | ||
103 | UPNP_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 */ | ||
170 | int | ||
171 | UPNP_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 */ | ||
212 | int | ||
213 | UPNP_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 | */ | ||
280 | int | ||
281 | UPNP_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 | |||
319 | int | ||
320 | UPNP_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 | |||
376 | int | ||
377 | UPNP_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 | |||
419 | int | ||
420 | UPNP_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 | |||
508 | int | ||
509 | UPNP_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 */ | ||
547 | int | ||
548 | UPNP_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 | ||
19 | extern "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 | |||
15 | static void | ||
16 | NameValueParserStartElt (void *d, const char *name, int l) | ||
17 | { | ||
18 | struct NameValueParserData *data = (struct NameValueParserData *) d; | ||
19 | if (l > 63) | ||
20 | l = 63; | ||
21 | memcpy (data->curelt, name, l); | ||
22 | data->curelt[l] = '\0'; | ||
23 | } | ||
24 | |||
25 | static void | ||
26 | NameValueParserGetData (void *d, const char *datas, int l) | ||
27 | { | ||
28 | struct NameValueParserData *data = (struct NameValueParserData *) d; | ||
29 | struct NameValue *nv; | ||
30 | nv = malloc (sizeof (struct NameValue)); | ||
31 | if (l > 63) | ||
32 | l = 63; | ||
33 | strncpy (nv->name, data->curelt, 64); | ||
34 | nv->name[63] = '\0'; | ||
35 | memcpy (nv->value, datas, l); | ||
36 | nv->value[l] = '\0'; | ||
37 | LIST_INSERT_HEAD (&(data->head), nv, entries); | ||
38 | } | ||
39 | |||
40 | void | ||
41 | ParseNameValue (const char *buffer, int bufsize, | ||
42 | struct NameValueParserData *data) | ||
43 | { | ||
44 | struct xmlparser parser; | ||
45 | LIST_INIT (&(data->head)); | ||
46 | /* init xmlparser object */ | ||
47 | parser.xmlstart = buffer; | ||
48 | parser.xmlsize = bufsize; | ||
49 | parser.data = data; | ||
50 | parser.starteltfunc = NameValueParserStartElt; | ||
51 | parser.endeltfunc = 0; | ||
52 | parser.datafunc = NameValueParserGetData; | ||
53 | parser.attfunc = 0; | ||
54 | parsexml (&parser); | ||
55 | } | ||
56 | |||
57 | void | ||
58 | ClearNameValueList (struct NameValueParserData *pdata) | ||
59 | { | ||
60 | struct NameValue *nv; | ||
61 | while ((nv = pdata->head.lh_first) != NULL) | ||
62 | { | ||
63 | LIST_REMOVE (nv, entries); | ||
64 | free (nv); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | char * | ||
69 | GetValueFromNameValueList (struct NameValueParserData *pdata, | ||
70 | const char *Name) | ||
71 | { | ||
72 | struct NameValue *nv; | ||
73 | char *p = NULL; | ||
74 | for (nv = pdata->head.lh_first; | ||
75 | (nv != NULL) && (p == NULL); 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 */ | ||
85 | char * | ||
86 | GetValueFromNameValueListIgnoreNS (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 | ||
110 | void | ||
111 | DisplayNameValueList (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 | ||
18 | extern "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 | ||
36 | extern "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 | |||
60 | struct 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 | ||
84 | static const char * | ||
85 | get_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 | |||
113 | static int | ||
114 | get_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 */ | ||
124 | int | ||
125 | GNUNET_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 | */ | ||
146 | static void | ||
147 | notify_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 | |||
218 | static void | ||
219 | nat_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 | |||
304 | struct GNUNET_NAT_Handle * | ||
305 | GNUNET_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 | |||
350 | void | ||
351 | GNUNET_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 | ||
35 | extern "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 | |||
60 | typedef 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 | } | ||
71 | NATPMP_state; | ||
72 | |||
73 | struct 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 | |||
90 | static void | ||
91 | log_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 | |||
108 | struct GNUNET_NAT_NATPMP_Handle * | ||
109 | GNUNET_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 | |||
122 | void | ||
123 | GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat) | ||
124 | { | ||
125 | if (nat) | ||
126 | { | ||
127 | closenatpmp (&nat->natpmp); | ||
128 | GNUNET_free (nat); | ||
129 | } | ||
130 | } | ||
131 | |||
132 | static int | ||
133 | can_send_command (const struct GNUNET_NAT_NATPMP_Handle *nat) | ||
134 | { | ||
135 | return time (NULL) >= nat->command_time; | ||
136 | } | ||
137 | |||
138 | static void | ||
139 | set_command_time (struct GNUNET_NAT_NATPMP_Handle *nat) | ||
140 | { | ||
141 | nat->command_time = time (NULL) + COMMAND_WAIT_SECS; | ||
142 | } | ||
143 | |||
144 | int | ||
145 | GNUNET_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 | |||
33 | typedef struct GNUNET_NAT_NATPMP_Handle GNUNET_NAT_NATPMP_Handle; | ||
34 | |||
35 | GNUNET_NAT_NATPMP_Handle *GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, | ||
36 | socklen_t addrlen, | ||
37 | unsigned short port); | ||
38 | |||
39 | void GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat); | ||
40 | |||
41 | int 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 | ||
35 | extern "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 | |||
58 | typedef enum | ||
59 | { | ||
60 | UPNP_IDLE, | ||
61 | UPNP_ERR, | ||
62 | UPNP_DISCOVER, | ||
63 | UPNP_MAP, | ||
64 | UPNP_UNMAP | ||
65 | } | ||
66 | UPNP_state; | ||
67 | |||
68 | struct 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 | |||
82 | static int | ||
83 | process_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 | |||
100 | GNUNET_NAT_UPNP_Handle * | ||
101 | GNUNET_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 | |||
122 | void | ||
123 | GNUNET_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 | */ | ||
143 | int | ||
144 | GNUNET_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 | |||
33 | typedef struct GNUNET_NAT_UPNP_Handle GNUNET_NAT_UPNP_Handle; | ||
34 | |||
35 | GNUNET_NAT_UPNP_Handle *GNUNET_NAT_UPNP_init (const struct sockaddr *addr, | ||
36 | socklen_t addrlen, | ||
37 | unsigned short port); | ||
38 | |||
39 | void GNUNET_NAT_UPNP_close (GNUNET_NAT_UPNP_Handle *); | ||
40 | |||
41 | int GNUNET_NAT_UPNP_pulse (GNUNET_NAT_UPNP_Handle *, int is_enabled, | ||
42 | int do_port_check, struct sockaddr **ext_addr); | ||
43 | #endif /* UPNP_H */ | ||