aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoon <moon@140774ce-b5e7-0310-ab8b-a85725594a96>2010-10-09 13:53:47 +0000
committerMoon <moon@140774ce-b5e7-0310-ab8b-a85725594a96>2010-10-09 13:53:47 +0000
commit922a0672749ba9d496d1dd8f6596bb4f8035e71d (patch)
treeccd4b351bd5acd5cc3b8b1cc358c712d6c0c116d
parentdc24b5bf44bf8d9460b2571fe529403637aa3e16 (diff)
downloadgnunet-922a0672749ba9d496d1dd8f6596bb4f8035e71d.tar.gz
gnunet-922a0672749ba9d496d1dd8f6596bb4f8035e71d.zip
rework UPnP code to use GNUnet scheduler and network API
disable NAT-PMP support for now
-rw-r--r--src/nat/Makefile.am15
-rw-r--r--src/nat/bsdqueue.h157
-rw-r--r--src/nat/nat.c362
-rw-r--r--src/nat/nat.h17
-rw-r--r--src/nat/natpmp.c87
-rw-r--r--src/nat/natpmp.h20
-rw-r--r--src/nat/test_nat.c27
-rw-r--r--src/nat/upnp-commands.c880
-rw-r--r--src/nat/upnp-commands.h281
-rw-r--r--src/nat/upnp-discover.c1285
-rw-r--r--src/nat/upnp-discover.h84
-rw-r--r--src/nat/upnp-igd-parse.c207
-rw-r--r--src/nat/upnp-igd-parse.h99
-rw-r--r--src/nat/upnp-minixml.c239
-rw-r--r--src/nat/upnp-minixml.h131
-rw-r--r--src/nat/upnp-reply-parse.c166
-rw-r--r--src/nat/upnp-reply-parse.h107
-rw-r--r--src/nat/upnp.c459
-rw-r--r--src/nat/upnp.h29
19 files changed, 4225 insertions, 427 deletions
diff --git a/src/nat/Makefile.am b/src/nat/Makefile.am
index c47348b7c..1b53095f3 100644
--- a/src/nat/Makefile.am
+++ b/src/nat/Makefile.am
@@ -1,5 +1,3 @@
1SUBDIRS = .
2
3INCLUDES = -I$(top_srcdir)/src/include 1INCLUDES = -I$(top_srcdir)/src/include
4 2
5if MINGW 3if MINGW
@@ -18,11 +16,16 @@ endif
18 16
19libgnunetnat_la_SOURCES = \ 17libgnunetnat_la_SOURCES = \
20 upnp.c upnp.h \ 18 upnp.c upnp.h \
21 natpmp.c natpmp.h \ 19 upnp-commands.c upnp-commands.h \
20 upnp-discover.c upnp-discover.h \
21 upnp-igd-parse.c upnp-igd-parse.h \
22 upnp-minixml.c upnp-minixml.h \
23 upnp-reply-parse.c upnp-reply-parse.h bsdqueue.h \
22 nat.c 24 nat.c
23 25
24libgnunetnat_la_CFLAGS = \ 26libgnunetnat_la_CFLAGS = \
25 -I$(top_scrdir)/include 27 -I$(top_scrdir)/include \
28 -DDEBUG_UPNP -g -O0
26 29
27libgnunetnat_la_LIBADD = \ 30libgnunetnat_la_LIBADD = \
28 $(top_builddir)/src/util/libgnunetutil.la \ 31 $(top_builddir)/src/util/libgnunetutil.la \
@@ -46,6 +49,6 @@ test_nat_SOURCES = \
46 49
47test_nat_LDADD = \ 50test_nat_LDADD = \
48 $(top_builddir)/src/nat/libgnunetnat.la \ 51 $(top_builddir)/src/nat/libgnunetnat.la \
49 $(top_builddir)/src/util/libgnunetutil.la 52 $(top_builddir)/src/util/libgnunetutil.la \
50 53 @LIBCURL@
51endif 54endif
diff --git a/src/nat/bsdqueue.h b/src/nat/bsdqueue.h
new file mode 100644
index 000000000..bdef3ac0b
--- /dev/null
+++ b/src/nat/bsdqueue.h
@@ -0,0 +1,157 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010 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 3, 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/bsd-queue.h
23 * @brief BSD implementation of simple lists
24 *
25 * @author Milan Bouchet-Valat
26 */
27
28/*
29 * Copyright (c) 1991, 1993
30 * The Regents of the University of California. All rights reserved.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 * 3. Neither the name of the University nor the names of its contributors
41 * may be used to endorse or promote products derived from this software
42 * without specific prior written permission.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * SUCH DAMAGE.
55 *
56 * @(#)queue.h 8.5 (Berkeley) 8/20/94
57 */
58
59#ifndef BSD_QUEUE_H
60#define BSD_QUEUE_H
61
62/*
63 * A list is headed by a single forward pointer (or an array of forward
64 * pointers for a hash table header). The elements are doubly linked
65 * so that an arbitrary element can be removed without a need to
66 * traverse the list. New elements can be added to the list before
67 * or after an existing element or at the head of the list. A list
68 * may only be traversed in the forward direction.
69 */
70
71#ifdef QUEUE_MACRO_DEBUG
72#define _Q_INVALIDATE(a) (a) = ((void *)-1)
73#else
74#define _Q_INVALIDATE(a)
75#endif
76
77/*
78 * List definitions.
79 */
80#define LIST_HEAD(name, type) \
81struct name { \
82 struct type *lh_first; /* first element */ \
83}
84
85#define LIST_HEAD_INITIALIZER(head) \
86 { NULL }
87
88#define LIST_ENTRY(type) \
89struct { \
90 struct type *le_next; /* next element */ \
91 struct type **le_prev; /* address of previous next element */ \
92}
93
94/*
95 * List access methods
96 */
97#define LIST_FIRST(head) ((head)->lh_first)
98#define LIST_END(head) NULL
99#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
100#define LIST_NEXT(elm, field) ((elm)->field.le_next)
101
102#define LIST_FOREACH(var, head, field) \
103 for((var) = LIST_FIRST(head); \
104 (var)!= LIST_END(head); \
105 (var) = LIST_NEXT(var, field))
106
107/*
108 * List functions.
109 */
110#define LIST_INIT(head) do { \
111 LIST_FIRST(head) = LIST_END(head); \
112} while (0)
113
114#define LIST_INSERT_AFTER(listelm, elm, field) do { \
115 if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
116 (listelm)->field.le_next->field.le_prev = \
117 &(elm)->field.le_next; \
118 (listelm)->field.le_next = (elm); \
119 (elm)->field.le_prev = &(listelm)->field.le_next; \
120} while (0)
121
122#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
123 (elm)->field.le_prev = (listelm)->field.le_prev; \
124 (elm)->field.le_next = (listelm); \
125 *(listelm)->field.le_prev = (elm); \
126 (listelm)->field.le_prev = &(elm)->field.le_next; \
127} while (0)
128
129#define LIST_INSERT_HEAD(head, elm, field) do { \
130 if (((elm)->field.le_next = (head)->lh_first) != NULL) \
131 (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
132 (head)->lh_first = (elm); \
133 (elm)->field.le_prev = &(head)->lh_first; \
134} while (0)
135
136#define LIST_REMOVE(elm, field) do { \
137 if ((elm)->field.le_next != NULL) \
138 (elm)->field.le_next->field.le_prev = \
139 (elm)->field.le_prev; \
140 *(elm)->field.le_prev = (elm)->field.le_next; \
141 _Q_INVALIDATE((elm)->field.le_prev); \
142 _Q_INVALIDATE((elm)->field.le_next); \
143} while (0)
144
145#define LIST_REPLACE(elm, elm2, field) do { \
146 if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
147 (elm2)->field.le_next->field.le_prev = \
148 &(elm2)->field.le_next; \
149 (elm2)->field.le_prev = (elm)->field.le_prev; \
150 *(elm2)->field.le_prev = (elm2); \
151 _Q_INVALIDATE((elm)->field.le_prev); \
152 _Q_INVALIDATE((elm)->field.le_next); \
153} while (0)
154
155#endif
156
157/* end of bsd-queue.h */
diff --git a/src/nat/nat.c b/src/nat/nat.c
index 794d9405c..5159e0903 100644
--- a/src/nat/nat.c
+++ b/src/nat/nat.c
@@ -46,12 +46,12 @@ struct GNUNET_NAT_Handle
46 /** 46 /**
47 * Handle for UPnP operations. 47 * Handle for UPnP operations.
48 */ 48 */
49 GNUNET_NAT_UPNP_Handle *upnp; 49 struct GNUNET_NAT_UPNP_Handle *upnp;
50 50
51 /** 51 /**
52 * Handle for NAT PMP operations. 52 * Handle for NAT PMP operations.
53 */ 53 */
54 GNUNET_NAT_NATPMP_Handle *natpmp; 54 struct GNUNET_NAT_NATPMP_Handle *natpmp;
55 55
56 /** 56 /**
57 * Scheduler. 57 * Scheduler.
@@ -61,17 +61,23 @@ struct GNUNET_NAT_Handle
61 /** 61 /**
62 * LAN address as passed by the caller 62 * LAN address as passed by the caller
63 */ 63 */
64 struct sockaddr *local_addr; 64 struct sockaddr *local_addr;
65 65
66 /** 66 /**
67 * External address as reported by NAT box 67 * External address as reported by found NAT box
68 */ 68 */
69 struct sockaddr *ext_addr; 69 struct sockaddr *ext_addr;
70
71 /**
72 * External address as reported by each type of NAT box
73 */
74 struct sockaddr *ext_addr_upnp;
75 struct sockaddr *ext_addr_natpmp;
70 76
71 /** 77 /**
72 * External address and port where packets are redirected 78 * External address and port where packets are redirected
73 */ 79 */
74 struct sockaddr *contact_addr; 80 struct sockaddr *contact_addr;
75 81
76 GNUNET_NAT_AddressCallback callback; 82 GNUNET_NAT_AddressCallback callback;
77 83
@@ -92,8 +98,14 @@ struct GNUNET_NAT_Handle
92 98
93 int port_mapped; 99 int port_mapped;
94 100
101 int old_status;
102
103 int new_status;
104
95 int did_warn; 105 int did_warn;
96 106
107 int processing;
108
97 uint16_t public_port; 109 uint16_t public_port;
98 110
99}; 111};
@@ -122,9 +134,9 @@ get_nat_state_str (enum GNUNET_NAT_PortState state)
122 134
123 135
124static int 136static int
125get_traversal_status (const struct GNUNET_NAT_Handle * s) 137get_traversal_status (const struct GNUNET_NAT_Handle *h)
126{ 138{
127 return MAX (s->natpmp_status, s->upnp_status); 139 return MAX (h->natpmp_status, h->upnp_status);
128} 140}
129 141
130 142
@@ -134,16 +146,15 @@ get_traversal_status (const struct GNUNET_NAT_Handle * s)
134 * @param b second sockaddr 146 * @param b second sockaddr
135 * @return 0 if addresses are equal, non-null value otherwise */ 147 * @return 0 if addresses are equal, non-null value otherwise */
136int 148int
137GNUNET_NAT_cmp_addr (const struct sockaddr *a, 149GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b)
138 const struct sockaddr *b)
139{ 150{
140 if (!(a && b)) 151 if (!(a && b))
141 return -1; 152 return -1;
142 if ( (a->sa_family == AF_INET) && (b->sa_family == AF_INET) ) 153 if ((a->sa_family == AF_INET) && (b->sa_family == AF_INET))
143 return memcmp (&(((struct sockaddr_in *) a)->sin_addr), 154 return memcmp (&(((struct sockaddr_in *) a)->sin_addr),
144 &(((struct sockaddr_in *) b)->sin_addr), 155 &(((struct sockaddr_in *) b)->sin_addr),
145 sizeof (struct in_addr)); 156 sizeof (struct in_addr));
146 if ( (a->sa_family == AF_INET6) && (b->sa_family == AF_INET6) ) 157 if ((a->sa_family == AF_INET6) && (b->sa_family == AF_INET6))
147 return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr), 158 return memcmp (&(((struct sockaddr_in6 *) a)->sin6_addr),
148 &(((struct sockaddr_in6 *) b)->sin6_addr), 159 &(((struct sockaddr_in6 *) b)->sin6_addr),
149 sizeof (struct in6_addr)); 160 sizeof (struct in6_addr));
@@ -157,50 +168,45 @@ GNUNET_NAT_cmp_addr (const struct sockaddr *a,
157 * or nullify the previous sockaddr. Change the port if needed. 168 * or nullify the previous sockaddr. Change the port if needed.
158 */ 169 */
159static void 170static void
160notify_change (struct GNUNET_NAT_Handle *nat, 171notify_change (struct GNUNET_NAT_Handle *h,
161 struct sockaddr *addr, 172 struct sockaddr *addr, size_t addrlen, int new_port_mapped)
162 size_t addrlen,
163 int new_port_mapped)
164{ 173{
165 if (new_port_mapped == nat->port_mapped) 174 if (new_port_mapped == h->port_mapped)
166 return; 175 return;
167 nat->port_mapped = new_port_mapped; 176 h->port_mapped = new_port_mapped;
168 177
169 if ( (NULL != nat->contact_addr) && 178 if ((NULL != h->contact_addr) && (NULL != h->callback))
170 (NULL != nat->callback) ) 179 h->callback (h->callback_cls,
171 nat->callback (nat->callback_cls, 180 GNUNET_NO, h->contact_addr, sizeof (h->contact_addr));
172 GNUNET_NO, 181 GNUNET_free_non_null (h->contact_addr);
173 nat->contact_addr, 182 h->contact_addr = NULL;
174 sizeof (nat->contact_addr)); 183 GNUNET_free_non_null (h->ext_addr);
175 GNUNET_free_non_null (nat->contact_addr); 184 h->ext_addr = NULL;
176 nat->contact_addr = NULL;
177 GNUNET_free_non_null (nat->ext_addr);
178 nat->ext_addr = NULL;
179 if (NULL == addr) 185 if (NULL == addr)
180 return; 186 return;
181 nat->ext_addr = GNUNET_malloc (addrlen); 187 h->ext_addr = GNUNET_malloc (addrlen);
182 memcpy (nat->ext_addr, addr, addrlen); 188 memcpy (h->ext_addr, addr, addrlen);
183 189
184 /* Recreate the ext_addr:public_port bogus address to pass to the callback */ 190 /* Recreate the ext_addr:public_port bogus address to pass to the callback */
185 if (nat->ext_addr->sa_family == AF_INET) 191 if (h->ext_addr->sa_family == AF_INET)
186 { 192 {
187 struct sockaddr_in tmp_addr; 193 struct sockaddr_in *tmp_addr;
188 194
189 tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in)); 195 tmp_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
190 tmp_addr->sin_family = AF_INET; 196 tmp_addr->sin_family = AF_INET;
191#ifdef HAVE_SOCKADDR_IN_SIN_LEN 197#ifdef HAVE_SOCKADDR_IN_SIN_LEN
192 tmp_addr->sin_len = sizeof (struct sockaddr_in); 198 tmp_addr->sin_len = sizeof (struct sockaddr_in);
193#endif 199#endif
194 tmp_addr->sin_port = port_mapped ? htons (nat->public_port) : 0; 200 tmp_addr->sin_port = h->port_mapped ? htons (h->public_port) : 0;
195 tmp_addr->sin_addr = ((struct sockaddr_in *) nat->ext_addr)->sin_addr; 201 tmp_addr->sin_addr = ((struct sockaddr_in *) h->ext_addr)->sin_addr;
196 nat->contact_addr = (struct sockaddr *) tmp_addr; 202 h->contact_addr = (struct sockaddr *) tmp_addr;
197 if (NULL != nat->callback) 203
198 nat->callback (nat->callback_cls, 204 if (NULL != h->callback)
199 GNUNET_YES, 205 h->callback (h->callback_cls,
200 nat->contact_addr, 206 GNUNET_YES,
201 sizeof (struct sockaddr_in)); 207 h->contact_addr, sizeof (struct sockaddr_in));
202 } 208 }
203 else if (nat->ext_addr->sa_family == AF_INET6) 209 else if (h->ext_addr->sa_family == AF_INET6)
204 { 210 {
205 struct sockaddr_in6 *tmp_addr; 211 struct sockaddr_in6 *tmp_addr;
206 212
@@ -209,14 +215,14 @@ notify_change (struct GNUNET_NAT_Handle *nat,
209#ifdef HAVE_SOCKADDR_IN_SIN_LEN 215#ifdef HAVE_SOCKADDR_IN_SIN_LEN
210 tmp_addr->sin6_len = sizeof (struct sockaddr_in6); 216 tmp_addr->sin6_len = sizeof (struct sockaddr_in6);
211#endif 217#endif
212 tmp_addr->sin6_port = port_mapped ? htons (nat->public_port) : 0; 218 tmp_addr->sin6_port = h->port_mapped ? htons (h->public_port) : 0;
213 tmp_addr->sin6_addr = ((struct sockaddr_in6 *) nat->ext_addr)->sin6_addr; 219 tmp_addr->sin6_addr = ((struct sockaddr_in6 *) h->ext_addr)->sin6_addr;
214 nat->contact_addr = (struct sockaddr *) tmp_addr; 220 h->contact_addr = (struct sockaddr *) tmp_addr;
215 if (NULL != nat->callback) 221
216 nat->callback (nat->callback_cls, 222 if (NULL != h->callback)
217 GNUNET_YES, 223 h->callback (h->callback_cls,
218 nat->contact_addr, 224 GNUNET_YES,
219 sizeof (struct sockaddr_in6)); 225 h->contact_addr, sizeof (struct sockaddr_in6));
220 } 226 }
221 else 227 else
222 { 228 {
@@ -224,87 +230,140 @@ notify_change (struct GNUNET_NAT_Handle *nat,
224 } 230 }
225} 231}
226 232
233static void nat_pulse (void *cls,
234 const struct GNUNET_SCHEDULER_TaskContext *tc);
227 235
228static void 236static void
229nat_pulse (void *cls, 237pulse_cb (struct GNUNET_NAT_Handle *h)
230 const struct GNUNET_SCHEDULER_TaskContext *tc)
231{ 238{
232 struct GNUNET_NAT_Handle *nat = cls; 239 socklen_t addrlen;
233 int old_status;
234 int new_status;
235 int port_mapped; 240 int port_mapped;
236 struct sockaddr *ext_addr_upnp = NULL;
237 struct sockaddr *ext_addr_natpmp = NULL;
238 241
239 nat->pulse_timer = GNUNET_SCHEDULER_NO_TASK; 242 /* One of the protocols is still working, wait for it to complete */
240 old_status = get_traversal_status (nat); 243 if (h->processing)
244 return;
241 245
242 /* Only update the protocol that has been successful until now */ 246 h->new_status = get_traversal_status (h);
243 if (nat->upnp_status >= GNUNET_NAT_PORT_UNMAPPED) 247 if ((h->old_status != h->new_status) &&
244 nat->upnp_status = 248 ((h->new_status == GNUNET_NAT_PORT_UNMAPPED) ||
245 GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES, 249 (h->new_status == GNUNET_NAT_PORT_ERROR)))
246 &ext_addr_upnp);
247 else if (nat->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED)
248 nat->natpmp_status =
249 GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
250 &ext_addr_natpmp);
251 else
252 {
253 /* try both */
254 nat->upnp_status =
255 GNUNET_NAT_UPNP_pulse (nat->upnp, nat->is_enabled, GNUNET_YES,
256 &ext_addr_upnp);
257 nat->natpmp_status =
258 GNUNET_NAT_NATPMP_pulse (nat->natpmp, nat->is_enabled,
259 &ext_addr_natpmp);
260 }
261 new_status = get_traversal_status (nat);
262 if ( (old_status != new_status) &&
263 ( (new_status == GNUNET_NAT_PORT_UNMAPPED) ||
264 (new_status == GNUNET_NAT_PORT_ERROR) ) )
265 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, 250 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
266 "NAT", 251 "NAT",
267 _("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n")); 252 _
253 ("Port redirection failed: no UPnP or NAT-PMP routers supporting this feature found\n"));
268#ifdef DEBUG 254#ifdef DEBUG
269 if (new_status != old_status) 255 if (h->new_status != h->old_status)
270 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NAT", 256 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NAT",
271 _("State changed from `%s' to `%s'\n"), 257 _("State changed from `%s' to `%s'\n"),
272 get_nat_state_str (old_status), 258 get_nat_state_str (h->old_status),
273 get_nat_state_str (new_status)); 259 get_nat_state_str (h->new_status));
274#endif 260#endif
275 261
276 port_mapped = (new_status == GNUNET_NAT_PORT_MAPPED); 262 port_mapped = (h->new_status == GNUNET_NAT_PORT_MAPPED);
277 if (!(ext_addr_upnp || ext_addr_natpmp)) 263 if (!(h->ext_addr_upnp || h->ext_addr_natpmp))
278 { 264 {
279 /* Address has just changed and we could not get it, or it's the first try */ 265 /* Address has just changed and we could not get it, or it's the first try,
280 if ( (NULL != nat->ext_addr) || 266 * and we're not waiting for a reply from UPnP or NAT-PMP */
281 (GNUNET_NO == nat->did_warn) ) 267 if (((NULL != h->ext_addr) ||
268 (GNUNET_NO == h->did_warn)) && h->processing != 0)
282 { 269 {
283 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, 270 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
284 "NAT", 271 "NAT",
285 _("Could not determine external IP address\n")); 272 _("Could not determine external IP address\n"));
286 nat->did_warn = GNUNET_YES; 273 h->did_warn = GNUNET_YES;
287 } 274 }
288 notify_change (nat, NULL, port_mapped); 275 notify_change (h, NULL, 0, port_mapped);
289 } 276 }
290 else if (ext_addr_upnp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_upnp) != 0) 277 else if (h->ext_addr_upnp
278 && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_upnp) != 0)
291 { 279 {
280 addrlen = h->ext_addr_upnp->sa_family == AF_INET ?
281 sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6);
292 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, 282 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
293 "NAT", 283 "NAT",
294 _("External IP address changed to %s\n"), 284 _("External IP address changed to %s\n"),
295 GNUNET_a2s (ext_addr_upnp, sizeof (ext_addr_upnp))); 285 GNUNET_a2s (h->ext_addr_upnp, addrlen));
296 notify_change (nat, ext_addr_upnp, port_mapped); 286 notify_change (h, h->ext_addr_upnp, addrlen, port_mapped);
297 } 287 }
298 else if (ext_addr_natpmp && GNUNET_NAT_cmp_addr (nat->ext_addr, ext_addr_natpmp) != 0) 288 else if (h->ext_addr_natpmp
289 && GNUNET_NAT_cmp_addr (h->ext_addr, h->ext_addr_natpmp) != 0)
299 { 290 {
291 addrlen = h->ext_addr_natpmp->sa_family == AF_INET ?
292 sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6);
300 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "NAT", 293 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "NAT",
301 _("External IP address changed to `%s'\n"), 294 _("External IP address changed to `%s'\n"),
302 GNUNET_a2s (ext_addr_natpmp, sizeof (ext_addr_natpmp))); 295 GNUNET_a2s (h->ext_addr_natpmp, addrlen));
303 notify_change (nat, ext_addr_natpmp, port_mapped); 296 notify_change (h, h->ext_addr_natpmp, addrlen, port_mapped);
297 }
298
299 h->pulse_timer = GNUNET_SCHEDULER_add_delayed (h->sched,
300 GNUNET_TIME_UNIT_SECONDS,
301 &nat_pulse, h);
302}
303
304static void
305upnp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls)
306{
307 struct GNUNET_NAT_Handle *h = cls;
308
309 h->upnp_status = status;
310 h->ext_addr_upnp = ext_addr;
311
312 h->processing--;
313 pulse_cb (h);
314}
315
316#if 0
317static void
318natpmp_pulse_cb (int status, struct sockaddr *ext_addr, void *cls)
319{
320 struct GNUNET_NAT_Handle *h = cls;
321
322 h->natpmp_status = status;
323 h->ext_addr_natpmp = ext_addr;
324
325 h->processing--;
326 pulse_cb (h);
327}
328#endif
329
330static void
331nat_pulse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
332{
333 struct GNUNET_NAT_Handle *h = cls;
334
335 /* Stop if we're already waiting for an action to complete */
336 if (h->processing)
337 return;
338
339 h->pulse_timer = GNUNET_SCHEDULER_NO_TASK;
340 h->old_status = get_traversal_status (h);
341
342 /* Only update the protocol that has been successful until now */
343 if (h->upnp_status >= GNUNET_NAT_PORT_UNMAPPED)
344 {
345 h->processing = 1;
346 GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES);
347
348 /* Wait for the callback to call pulse_cb() to handle changes */
349 return;
350 }
351 else if (h->natpmp_status >= GNUNET_NAT_PORT_UNMAPPED)
352 {
353 h->processing = 1;
354#if 0
355 GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled);
356#endif
357 }
358 else /* try both */
359 {
360 h->processing = 2;
361
362 GNUNET_NAT_UPNP_pulse (h->upnp, h->is_enabled, GNUNET_YES);
363#if 0
364 GNUNET_NAT_NATPMP_pulse (h->natpmp, h->is_enabled, natpmp_pulse_cb, h);
365#endif
304 } 366 }
305 nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (nat->sched,
306 GNUNET_TIME_UNIT_SECONDS,
307 &nat_pulse, nat);
308} 367}
309 368
310 369
@@ -322,43 +381,52 @@ nat_pulse (void *cls,
322 * @return NULL on error, otherwise handle that can be used to unregister 381 * @return NULL on error, otherwise handle that can be used to unregister
323 */ 382 */
324struct GNUNET_NAT_Handle * 383struct GNUNET_NAT_Handle *
325GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle *sched, 384GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle
326 const struct sockaddr *addr, socklen_t addrlen, 385 *sched,
386 const struct sockaddr *addr,
387 socklen_t addrlen,
327 GNUNET_NAT_AddressCallback callback, void *callback_cls) 388 GNUNET_NAT_AddressCallback callback, void *callback_cls)
328{ 389{
329 struct GNUNET_NAT_Handle *nat; 390 struct GNUNET_NAT_Handle *h;
391
392 h = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle));
330 393
331 nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_Handle));
332 if (addr) 394 if (addr)
333 { 395 {
334 GNUNET_assert ( (addr->sa_family == AF_INET) || 396 GNUNET_assert ((addr->sa_family == AF_INET) ||
335 (addr->sa_family == AF_INET6) ); 397 (addr->sa_family == AF_INET6));
336 nat->local_addr = GNUNET_malloc (addrlen); 398 h->local_addr = GNUNET_malloc (addrlen);
337 memcpy (nat->local_addr, addr, addrlen); 399 memcpy (h->local_addr, addr, addrlen);
338 if (addr->sa_family == AF_INET) 400 if (addr->sa_family == AF_INET)
339 { 401 {
340 nat->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port); 402 h->public_port = ntohs (((struct sockaddr_in *) addr)->sin_port);
341 ((struct sockaddr_in *) nat->local_addr)->sin_port = 0; 403 ((struct sockaddr_in *) h->local_addr)->sin_port = 0;
342 } 404 }
343 else if (addr->sa_family == AF_INET6) 405 else if (addr->sa_family == AF_INET6)
344 { 406 {
345 nat->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port); 407 h->public_port = ntohs (((struct sockaddr_in6 *) addr)->sin6_port);
346 ((struct sockaddr_in6 *) nat->local_addr)->sin6_port = 0; 408 ((struct sockaddr_in6 *) h->local_addr)->sin6_port = 0;
347 } 409 }
348 } 410 }
349 nat->should_change = GNUNET_YES; 411 h->should_change = GNUNET_YES;
350 nat->sched = sched; 412 h->sched = sched;
351 nat->is_enabled = GNUNET_YES; 413 h->is_enabled = GNUNET_YES;
352 nat->upnp_status = GNUNET_NAT_PORT_UNMAPPED; 414 h->upnp_status = GNUNET_NAT_PORT_UNMAPPED;
353 nat->natpmp_status = GNUNET_NAT_PORT_UNMAPPED; 415 h->natpmp_status = GNUNET_NAT_PORT_UNMAPPED;
354 nat->callback = callback; 416 h->callback = callback;
355 nat->callback_cls = callback_cls; 417 h->callback_cls = callback_cls;
356 nat->natpmp = GNUNET_NAT_NATPMP_init (nat->local_addr, addrlen, nat->public_port); 418 h->upnp =
357 nat->upnp = GNUNET_NAT_UPNP_init (nat->local_addr, addrlen, nat->public_port); 419 GNUNET_NAT_UPNP_init (h->sched, h->local_addr, addrlen, h->public_port,
358 nat->pulse_timer = GNUNET_SCHEDULER_add_delayed (sched, 420 upnp_pulse_cb, h);
359 GNUNET_TIME_UNIT_SECONDS, 421#if 0
360 &nat_pulse, nat); 422 h->natpmp =
361 return nat; 423 GNUNET_NAT_NATPMP_init (h->sched, h->local_addr, addrlen, h->public_port,
424 natpmp_pulse_cb, h);
425#endif
426 h->pulse_timer = GNUNET_SCHEDULER_add_delayed (sched,
427 GNUNET_TIME_UNIT_SECONDS,
428 &nat_pulse, h);
429 return h;
362} 430}
363 431
364 432
@@ -371,23 +439,21 @@ GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle *sched,
371void 439void
372GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h) 440GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h)
373{ 441{
374 struct sockaddr *addr; 442 GNUNET_NAT_UPNP_pulse (h->upnp, GNUNET_NO, GNUNET_NO);
375
376 GNUNET_SCHEDULER_cancel (h->sched,
377 h->pulse_timer);
378 h->upnp_status =
379 GNUNET_NAT_UPNP_pulse (h->upnp,
380 GNUNET_NO, GNUNET_NO,
381 &addr);
382 h->natpmp_status =
383 GNUNET_NAT_NATPMP_pulse (h->natpmp, GNUNET_NO,
384 &addr);
385 GNUNET_NAT_NATPMP_close (h->natpmp);
386 GNUNET_NAT_UPNP_close (h->upnp); 443 GNUNET_NAT_UPNP_close (h->upnp);
444
445#if 0
446 GNUNET_NAT_NATPMP_pulse (h->natpmp, GNUNET_NO);
447 GNUNET_NAT_NATPMP_close (h->natpmp);
448#endif
449
450 GNUNET_SCHEDULER_cancel (h->sched, h->pulse_timer);
451
387 GNUNET_free_non_null (h->local_addr); 452 GNUNET_free_non_null (h->local_addr);
388 GNUNET_free_non_null (h->ext_addr); 453 GNUNET_free_non_null (h->ext_addr);
454 GNUNET_free_non_null (h->ext_addr_upnp);
455 GNUNET_free_non_null (h->ext_addr_natpmp);
389 GNUNET_free (h); 456 GNUNET_free (h);
390} 457}
391 458
392/* end of nat.c */ 459/* end of nat.c */
393
diff --git a/src/nat/nat.h b/src/nat/nat.h
index a0924af08..3a0f21e7b 100644
--- a/src/nat/nat.h
+++ b/src/nat/nat.h
@@ -34,29 +34,29 @@
34 * Used to communicate with the UPnP and NAT-PMP plugins 34 * Used to communicate with the UPnP and NAT-PMP plugins
35 */ 35 */
36enum GNUNET_NAT_PortState 36enum GNUNET_NAT_PortState
37 { 37{
38 GNUNET_NAT_PORT_ERROR, 38 GNUNET_NAT_PORT_ERROR,
39 39
40 /** 40 /**
41 * the port isn't forwarded 41 * the port isn't forwarded
42 */ 42 */
43 GNUNET_NAT_PORT_UNMAPPED, 43 GNUNET_NAT_PORT_UNMAPPED,
44 44
45 /** 45 /**
46 * we're cancelling the port forwarding 46 * we're cancelling the port forwarding
47 */ 47 */
48 GNUNET_NAT_PORT_UNMAPPING, 48 GNUNET_NAT_PORT_UNMAPPING,
49 49
50 /** 50 /**
51 * we're in the process of trying to set up port forwarding 51 * we're in the process of trying to set up port forwarding
52 */ 52 */
53 GNUNET_NAT_PORT_MAPPING, 53 GNUNET_NAT_PORT_MAPPING,
54 54
55 /** 55 /**
56 * we've successfully forwarded the port 56 * we've successfully forwarded the port
57 */ 57 */
58 GNUNET_NAT_PORT_MAPPED 58 GNUNET_NAT_PORT_MAPPED
59 }; 59};
60 60
61 61
62/** 62/**
@@ -66,8 +66,7 @@ enum GNUNET_NAT_PortState
66 * @param b second sockaddr 66 * @param b second sockaddr
67 * @return 0 if addresses are equal, non-null value otherwise 67 * @return 0 if addresses are equal, non-null value otherwise
68 */ 68 */
69int GNUNET_NAT_cmp_addr (const struct sockaddr *a, 69int GNUNET_NAT_cmp_addr (const struct sockaddr *a, const struct sockaddr *b);
70 const struct sockaddr *b);
71 70
72 71
73#endif 72#endif
diff --git a/src/nat/natpmp.c b/src/nat/natpmp.c
index 308ce7f44..79a6bfd33 100644
--- a/src/nat/natpmp.c
+++ b/src/nat/natpmp.c
@@ -41,6 +41,7 @@
41#include "platform.h" 41#include "platform.h"
42#include "gnunet_common.h" 42#include "gnunet_common.h"
43#include "gnunet_nat_lib.h" 43#include "gnunet_nat_lib.h"
44#include "nat.h"
44#include "natpmp.h" 45#include "natpmp.h"
45 46
46#define LIFETIME_SECS 3600 47#define LIFETIME_SECS 3600
@@ -48,7 +49,7 @@
48/* Component name for logging */ 49/* Component name for logging */
49#define COMP_NAT_NATPMP _("NAT (NAT-PMP))") 50#define COMP_NAT_NATPMP _("NAT (NAT-PMP))")
50 51
51typedef enum 52enum NATPMP_state
52{ 53{
53 NATPMP_IDLE, 54 NATPMP_IDLE,
54 NATPMP_ERR, 55 NATPMP_ERR,
@@ -59,20 +60,21 @@ typedef enum
59 NATPMP_SEND_UNMAP, 60 NATPMP_SEND_UNMAP,
60 NATPMP_RECV_UNMAP 61 NATPMP_RECV_UNMAP
61} 62}
62NATPMP_state; 63 ;
63 64
64struct GNUNET_NAT_NATPMP_Handle 65struct GNUNET_NAT_NATPMP_Handle
65{ 66{
66 const struct sockaddr *addr; 67 const struct sockaddr *addr;
67 socklen_t addrlen; 68 socklen_t addrlen;
68 struct sockaddr*ext_addr; 69 struct sockaddr *ext_addr;
69 int is_mapped; 70 int is_mapped;
70 int has_discovered; 71 int has_discovered;
71 int port; 72 int port;
72 time_t renew_time; 73 time_t renew_time;
73 time_t command_time; 74 time_t command_time;
74 NATPMP_state state; 75 enum NATPMP_state state;
75 natpmp_t natpmp; 76 struct natpmp_t natpmp;
77 struct GNUNET_SCHEDULER_Handle *sched;
76}; 78};
77 79
78 80
@@ -82,28 +84,27 @@ log_val (const char *func, int ret)
82#ifdef DEBUG 84#ifdef DEBUG
83 if (ret == NATPMP_TRYAGAIN) 85 if (ret == NATPMP_TRYAGAIN)
84 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 86 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
85 COMP_NAT_NATPMP, _("%s retry (%d)\n"), 87 COMP_NAT_NATPMP, _("%s retry (%d)\n"), func, ret);
86 func, ret);
87 if (ret >= 0) 88 if (ret >= 0)
88 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, 89 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
89 COMP_NAT_NATPMP, _("%s succeeded (%d)\n"), 90 COMP_NAT_NATPMP, _("%s succeeded (%d)\n"), func, ret);
90 func, ret);
91 else 91 else
92 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 92 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
93 COMP_NAT_NATPMP, 93 COMP_NAT_NATPMP,
94 "%s failed. natpmp returned %d (%s); errno is %d (%s)\n", 94 "%s failed. natpmp returned %d (%s); errno is %d (%s)\n",
95 func, ret, 95 func, ret, strnatpmperr (ret), errno, strerror (errno));
96 strnatpmperr (ret), errno, strerror (errno));
97#endif 96#endif
98} 97}
99 98
100struct GNUNET_NAT_NATPMP_Handle * 99struct GNUNET_NAT_NATPMP_Handle *
101GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, socklen_t addrlen, 100GNUNET_NAT_NATPMP_init (struct GNUNET_SCHEDULER_Handle *sched,
101 const struct sockaddr *addr, socklen_t addrlen,
102 u_short port) 102 u_short port)
103{ 103{
104 struct GNUNET_NAT_NATPMP_Handle *nat; 104 struct GNUNET_NAT_NATPMP_Handle *nat;
105 105
106 nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_NATPMP_Handle)); 106 nat = GNUNET_malloc (sizeof (struct GNUNET_NAT_NATPMP_Handle));
107 nat->sched = sched;
107 nat->state = NATPMP_DISCOVER; 108 nat->state = NATPMP_DISCOVER;
108 nat->port = port; 109 nat->port = port;
109 nat->addr = addr; 110 nat->addr = addr;
@@ -112,7 +113,7 @@ GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, socklen_t addrlen,
112} 113}
113 114
114void 115void
115GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat) 116GNUNET_NAT_NATPMP_close (struct GNUNET_NAT_NATPMP_Handle *nat)
116{ 117{
117 if (nat) 118 if (nat)
118 { 119 {
@@ -160,7 +161,7 @@ GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, int is_enabled,
160 161
161 if ((nat->state == NATPMP_RECV_PUB) && can_send_command (nat)) 162 if ((nat->state == NATPMP_RECV_PUB) && can_send_command (nat))
162 { 163 {
163 natpmpresp_t response; 164 struct natpmpresp_t response;
164 const int val = readnatpmpresponseorretry (&nat->natpmp, 165 const int val = readnatpmpresponseorretry (&nat->natpmp,
165 &response); 166 &response);
166 log_val ("readnatpmpresponseorretry", val); 167 log_val ("readnatpmpresponseorretry", val);
@@ -174,40 +175,38 @@ GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, int is_enabled,
174 175
175 if (response.pnu.publicaddress.family == AF_INET) 176 if (response.pnu.publicaddress.family == AF_INET)
176 { 177 {
177 v4 = GNUNET_malloc (sizeof (struct sockaddr_in)); 178 v4 = GNUNET_malloc (sizeof (struct sockaddr_in));
178 nat->ext_addr = (struct sockaddr*) v4; 179 nat->ext_addr = (struct sockaddr *) v4;
179 v4->sin_family = AF_INET; 180 v4->sin_family = AF_INET;
180 v4->sin_port = response.pnu.newportmapping.mappedpublicport; 181 v4->sin_port = response.pnu.newportmapping.mappedpublicport;
181 memcpy (&v4->sin_addr, &response.pnu.publicaddress.addr, 182 memcpy (&v4->sin_addr, &response.pnu.publicaddress.addr,
182 sizeof (struct in_addr)); 183 sizeof (struct in_addr));
183#ifdef DEBUG 184#ifdef DEBUG
184 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP, 185 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
185 _("Found public IP address %s\n"), 186 _("Found public IP address %s\n"),
186 inet_ntop (AF_INET, 187 inet_ntop (AF_INET,
187 &response.pnu.publicaddress.addr, 188 &response.pnu.publicaddress.addr,
188 buf, 189 buf, sizeof (buf)));
189 sizeof(buf)));
190#endif 190#endif
191 } 191 }
192 else 192 else
193 { 193 {
194 v6 = GNUNET_malloc (sizeof (struct sockaddr_in6)); 194 v6 = GNUNET_malloc (sizeof (struct sockaddr_in6));
195 nat->ext_addr = (struct sockaddr*) v6; 195 nat->ext_addr = (struct sockaddr *) v6;
196 v6->sin6_family = AF_INET6; 196 v6->sin6_family = AF_INET6;
197 v6->sin6_port = response.pnu.newportmapping.mappedpublicport; 197 v6->sin6_port = response.pnu.newportmapping.mappedpublicport;
198 memcpy (&v6->sin6_addr, 198 memcpy (&v6->sin6_addr,
199 &response.pnu.publicaddress.addr6, 199 &response.pnu.publicaddress.addr6,
200 (sizeof (struct in6_addr))); 200 (sizeof (struct in6_addr)));
201#ifdef DEBUG 201#ifdef DEBUG
202 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP, 202 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_NATPMP,
203 _("Found public IP address %s\n"), 203 _("Found public IP address %s\n"),
204 inet_ntop (AF_INET6, 204 inet_ntop (AF_INET6,
205 &response.pnu.publicaddress.addr6, 205 &response.pnu.publicaddress.addr6,
206 buf, 206 buf, sizeof (buf)));
207 sizeof(buf)));
208#endif 207#endif
209 } 208 }
210 *ext_addr = nat->ext_addr; 209 *ext_addr = nat->ext_addr;
211 nat->state = NATPMP_IDLE; 210 nat->state = NATPMP_IDLE;
212 } 211 }
213 else if (val != NATPMP_TRYAGAIN) 212 else if (val != NATPMP_TRYAGAIN)
@@ -235,14 +234,14 @@ GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, int is_enabled,
235 234
236 if (nat->state == NATPMP_RECV_UNMAP) 235 if (nat->state == NATPMP_RECV_UNMAP)
237 { 236 {
238 natpmpresp_t resp; 237 struct natpmpresp_t resp;
239 const int val = readnatpmpresponseorretry (&nat->natpmp, &resp); 238 const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
240 log_val ("readnatpmpresponseorretry", val); 239 log_val ("readnatpmpresponseorretry", val);
241 if (val >= 0) 240 if (val >= 0)
242 { 241 {
243 const int p = resp.pnu.newportmapping.privateport; 242 const int p = resp.pnu.newportmapping.privateport;
244 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP, 243 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP,
245 _("No longer forwarding port %d\n"), p); 244 _("No longer forwarding port %d\n"), p);
246 if (nat->port == p) 245 if (nat->port == p)
247 { 246 {
248 nat->port = -1; 247 nat->port = -1;
@@ -279,7 +278,7 @@ GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, int is_enabled,
279 278
280 if (nat->state == NATPMP_RECV_MAP) 279 if (nat->state == NATPMP_RECV_MAP)
281 { 280 {
282 natpmpresp_t resp; 281 struct natpmpresp_t resp;
283 const int val = readnatpmpresponseorretry (&nat->natpmp, &resp); 282 const int val = readnatpmpresponseorretry (&nat->natpmp, &resp);
284 log_val ("readnatpmpresponseorretry", val); 283 log_val ("readnatpmpresponseorretry", val);
285 if (val >= 0) 284 if (val >= 0)
@@ -289,7 +288,7 @@ GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat, int is_enabled,
289 nat->renew_time = time (NULL) + LIFETIME_SECS; 288 nat->renew_time = time (NULL) + LIFETIME_SECS;
290 nat->port = resp.pnu.newportmapping.privateport; 289 nat->port = resp.pnu.newportmapping.privateport;
291 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP, 290 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_NATPMP,
292 _("Port %d forwarded successfully\n"), nat->port); 291 _("Port %d forwarded successfully\n"), nat->port);
293 } 292 }
294 else if (val != NATPMP_TRYAGAIN) 293 else if (val != NATPMP_TRYAGAIN)
295 { 294 {
diff --git a/src/nat/natpmp.h b/src/nat/natpmp.h
index 8e1531cee..0c198e203 100644
--- a/src/nat/natpmp.h
+++ b/src/nat/natpmp.h
@@ -32,16 +32,18 @@
32 32
33struct GNUNET_NAT_NATPMP_Handle; 33struct GNUNET_NAT_NATPMP_Handle;
34 34
35struct GNUNET_NAT_NATPMP_Handle * 35struct GNUNET_NAT_NATPMP_Handle *GNUNET_NAT_NATPMP_init (struct
36GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, 36 GNUNET_SCHEDULER_Handle
37 socklen_t addrlen, 37 *sched,
38 unsigned short port); 38 const struct sockaddr
39 *addr,
40 socklen_t addrlen,
41 unsigned short port);
39 42
40void GNUNET_NAT_NATPMP_close (struct GNUNET_NAT_NATPMP_Handle * nat); 43void GNUNET_NAT_NATPMP_close (struct GNUNET_NAT_NATPMP_Handle *nat);
41 44
42int GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle * nat, 45int GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle *nat,
43 int is_enabled, 46 int is_enabled, struct sockaddr **ext_addr);
44 struct sockaddr **ext_addr);
45 47
46#endif 48#endif
47/* NATPMP_H */ 49/* NATPMP_H */
diff --git a/src/nat/test_nat.c b/src/nat/test_nat.c
index f4f0b62ea..e42d76b7f 100644
--- a/src/nat/test_nat.c
+++ b/src/nat/test_nat.c
@@ -41,14 +41,13 @@
41#include "gnunet_nat_lib.h" 41#include "gnunet_nat_lib.h"
42 42
43/* Time to wait before stopping NAT, in seconds */ 43/* Time to wait before stopping NAT, in seconds */
44#define TIMEOUT 30 44#define TIMEOUT 60
45 45
46struct addr_cls 46struct addr_cls
47{ 47{
48 const struct sockaddr *addr; 48 const struct sockaddr *addr;
49 socklen_t addrlen; 49 socklen_t addrlen;
50}; 50};
51//typedef addr_cls addr_cls;
52 51
53static void 52static void
54addr_callback (void *cls, int add_remove, 53addr_callback (void *cls, int add_remove,
@@ -73,9 +72,7 @@ stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
73static int 72static int
74process_if (void *cls, 73process_if (void *cls,
75 const char *name, 74 const char *name,
76 int isDefault, 75 int isDefault, const struct sockaddr *addr, socklen_t addrlen)
77 const struct sockaddr *addr,
78 socklen_t addrlen)
79{ 76{
80 struct addr_cls *data = cls; 77 struct addr_cls *data = cls;
81 78
@@ -85,6 +82,12 @@ process_if (void *cls,
85 data->addrlen = addrlen; 82 data->addrlen = addrlen;
86 } 83 }
87 84
85 if (strcmp (name, "eth1") == 0 && addr->sa_family == AF_INET)
86 return GNUNET_SYSERR;
87
88 return GNUNET_OK;
89
90
88 if (isDefault && addr) 91 if (isDefault && addr)
89 return GNUNET_SYSERR; 92 return GNUNET_SYSERR;
90 else 93 else
@@ -107,7 +110,8 @@ run (void *cls,
107 GNUNET_OS_network_interfaces_list (process_if, &data); 110 GNUNET_OS_network_interfaces_list (process_if, &data);
108 if (!data.addr) 111 if (!data.addr)
109 { 112 {
110 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not find a valid interface address!\n"); 113 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
114 "Could not find a valid interface address!\n");
111 exit (GNUNET_SYSERR); 115 exit (GNUNET_SYSERR);
112 } 116 }
113 117
@@ -120,14 +124,17 @@ run (void *cls,
120 else 124 else
121 ((struct sockaddr_in6 *) addr)->sin6_port = htons (2086); 125 ((struct sockaddr_in6 *) addr)->sin6_port = htons (2086);
122 126
123 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Requesting NAT redirection from address %s...\n", GNUNET_a2s (addr, data.addrlen)); 127 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
128 "Requesting NAT redirection from address %s...\n",
129 GNUNET_a2s (addr, data.addrlen));
124 130
125 nat = GNUNET_NAT_register (sched, addr, data.addrlen, addr_callback, NULL); 131 nat = GNUNET_NAT_register (sched, addr, data.addrlen, addr_callback, NULL);
126 GNUNET_free (addr); 132 GNUNET_free (addr);
127 133
128 GNUNET_SCHEDULER_add_delayed (sched, 134 GNUNET_SCHEDULER_add_delayed (sched,
129 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, TIMEOUT), 135 GNUNET_TIME_relative_multiply
130 stop, nat); 136 (GNUNET_TIME_UNIT_SECONDS, TIMEOUT), stop,
137 nat);
131} 138}
132 139
133int 140int
diff --git a/src/nat/upnp-commands.c b/src/nat/upnp-commands.c
new file mode 100644
index 000000000..ddc5d9473
--- /dev/null
+++ b/src/nat/upnp-commands.c
@@ -0,0 +1,880 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010 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 3, 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 * Code in this file is originally based on the miniupnp library.
23 * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
24 *
25 * Original licence:
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are met:
29 *
30 * * Redistributions of source code must retain the above copyright notice,
31 * this list of conditions and the following disclaimer.
32 * * Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 * * The name of the author may not be used to endorse or promote products
36 * derived from this software without specific prior written permission.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
42 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
43 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
44 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
48 * POSSIBILITY OF SUCH DAMAGE.
49 */
50
51/**
52 * @file nat/upnp-commands.c
53 * @brief Implementation of a basic set of UPnP commands
54 *
55 * @author Milan Bouchet-Valat
56 */
57
58#include "platform.h"
59#include "gnunet_util_lib.h"
60
61#include <stdlib.h>
62#include <stdio.h>
63#include <string.h>
64
65#include "upnp-reply-parse.h"
66#include "upnp-igd-parse.h"
67#include "upnp-discover.h"
68#include "upnp-commands.h"
69
70#define SOAP_PREFIX "s"
71#define SERVICE_PREFIX "u"
72#define SERVICE_PREFIX2 'u'
73#define MAX_HOSTNAME_LEN 64
74
75#define PRINT_UPNP_ERROR(a, b) GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "UPnP", _("%s failed at %s:%d: %s\n"), a, __FILE__, __LINE__, b);
76
77
78/**
79 * Private closure used by UPNP_command() and its callbacks.
80 */
81struct UPNP_command_cls
82{
83 /**
84 * Connection handle used for sending and receiving.
85 */
86 struct GNUNET_CONNECTION_Handle *s;
87
88 /**
89 * Transmission handle used for sending command.
90 */
91 struct GNUNET_CONNECTION_TransmitHandle *th;
92
93 /**
94 * HTML content to send to run command.
95 */
96 char *content;
97
98 /**
99 * Buffer where to copy received data to pass to caller.
100 */
101 char *buffer;
102
103 /**
104 * Size of buffer.
105 */
106 size_t buf_size;
107
108 /**
109 * User callback to trigger when done.
110 */
111 UPNP_command_cb_ caller_cb;
112
113 /**
114 * User closure to pass to caller_cb.
115 */
116 void *caller_cls;
117};
118
119/**
120 * Get the length of content included in an HTML line.
121 *
122 * @param p line to parse
123 * @param n size of p
124 * @return the length of the content
125 */
126static ssize_t
127get_content_len_from_line (const char *p, int n)
128{
129 static const char cont_len_str[] = "content-length";
130 const char *p2 = cont_len_str;
131 int a = 0;
132
133 while (*p2)
134 {
135 if (n == 0)
136 return -1;
137
138 if (*p2 != *p && *p2 != (*p + 32))
139 return -1;
140
141 p++;
142 p2++;
143 n--;
144 }
145
146 if (n == 0)
147 return -1;
148
149 if (*p != ':')
150 return -1;
151
152 p++;
153 n--;
154
155 while (*p == ' ')
156 {
157 if (n == 0)
158 return -1;
159
160 p++;
161 n--;
162 }
163
164 while (*p >= '0' && *p <= '9')
165 {
166 if (n == 0)
167 return -1;
168
169 a = (a * 10) + (*p - '0');
170 p++;
171 n--;
172 }
173
174 return a;
175}
176
177/**
178 * Get the respective lengths of content and header from an HTML reply.
179 *
180 * @param p HTML to parse
181 * @param n size of p
182 * @param content_len pointer to store content length to
183 * @param content_len pointer to store header length to
184 */
185static void
186get_content_and_header_len (const char *p, int n,
187 int *content_len, int *header_len)
188{
189 const char *line;
190 int line_len;
191 int r;
192
193 line = p;
194
195 while (line < p + n)
196 {
197 line_len = 0;
198
199 while (line[line_len] != '\r' && line[line_len] != '\r')
200 {
201 if (line + line_len >= p + n)
202 return;
203
204 line_len++;
205 }
206
207 r = get_content_len_from_line (line, line_len);
208
209 if (r > 0)
210 *content_len = r;
211
212 line = line + line_len + 2;
213
214 if (line[0] == '\r' && line[1] == '\n')
215 {
216 *header_len = (line - p) + 2;
217 return;
218 }
219 }
220}
221
222/**
223 * Receive reply of the device to our UPnP command.
224 *
225 * @param data closure from UPNP_command()
226 * @param buf struct UPNP_command_cls *cls
227 * @param available number of bytes in buf
228 * @param addr address of the sender
229 * @param addrlen size of addr
230 * @errCode value of errno
231 */
232static void
233UPNP_command_receiver (void *data,
234 const void *buf,
235 size_t available,
236 const struct sockaddr *addr,
237 socklen_t addrlen, int errCode)
238{
239 struct UPNP_command_cls *cls = data;
240 int content_len;
241 int header_len;
242
243 if (available > 0)
244 {
245 content_len = -1;
246 header_len = -1;
247 get_content_and_header_len (buf, available, &content_len, &header_len);
248
249 strncpy (cls->buffer, (char *) buf, cls->buf_size - 2);
250 cls->buffer[cls->buf_size - 2] = '\0';
251 }
252 else
253 {
254 cls->buffer[0] = '\0';
255 }
256
257 GNUNET_CONNECTION_destroy (cls->s, GNUNET_NO);
258
259 (*cls->caller_cb) (cls->buffer, cls->buf_size, cls->caller_cls);
260
261 GNUNET_free (cls->content);
262 GNUNET_free (cls);
263}
264
265/**
266 * Send UPnP command to device.
267 */
268static size_t
269UPNP_command_transmit (void *data, size_t size, void *buf)
270{
271 struct UPNP_command_cls *cls = data;
272 int n;
273 char *content = cls->content;
274
275 n = strlen (content);
276 memcpy (buf, content, size);
277
278 GNUNET_CONNECTION_receive (cls->s, cls->buf_size, GNUNET_TIME_UNIT_MINUTES,
279 UPNP_command_receiver, cls);
280
281 return n;
282}
283
284/**
285 * Parse a HTTP URL string to extract hostname, port and path it points to.
286 *
287 * @param url source string corresponding to URL
288 * @param hostname pointer where to store hostname (size of MAX_HOSTNAME_LEN+1)
289 * @param port pointer where to store port
290 * @param path pointer where to store path
291 *
292 * @return GNUNET_OK on success, GNUNET_SYSERR on failure
293 */
294int
295parse_url (const char *url, char *hostname, unsigned short *port, char **path)
296{
297 char *p1, *p2, *p3;
298
299 if (!url)
300 return GNUNET_SYSERR;
301
302 p1 = strstr (url, "://");
303
304 if (!p1)
305 return GNUNET_SYSERR;
306
307 p1 += 3;
308
309 if ((url[0] != 'h') || (url[1] != 't')
310 || (url[2] != 't') || (url[3] != 'p'))
311 return GNUNET_SYSERR;
312
313 p2 = strchr (p1, ':');
314 p3 = strchr (p1, '/');
315
316 if (!p3)
317 return GNUNET_SYSERR;
318
319 memset (hostname, 0, MAX_HOSTNAME_LEN + 1);
320
321 if (!p2 || (p2 > p3))
322 {
323 strncpy (hostname, p1, MIN (MAX_HOSTNAME_LEN, (int) (p3 - p1)));
324 *port = 80;
325 }
326 else
327 {
328 strncpy (hostname, p1, MIN (MAX_HOSTNAME_LEN, (int) (p2 - p1)));
329 *port = 0;
330 p2++;
331
332 while ((*p2 >= '0') && (*p2 <= '9'))
333 {
334 *port *= 10;
335 *port += (unsigned short) (*p2 - '0');
336 p2++;
337 }
338 }
339
340 *path = p3;
341 return GNUNET_OK;
342}
343
344/**
345 * Send UPnP command to the device identified by url and service.
346 *
347 * @param sched scheduler to use for network tasks
348 * @param url control URL of the device
349 * @param service type of the service corresponding to the command
350 * @param action action to send
351 * @param args arguments for action
352 * @param caller_cb user callback to trigger when done
353 * @param caller_cls closure to pass to caller_cb
354 */
355void
356UPNP_command_ (struct GNUNET_SCHEDULER_Handle *sched,
357 const char *url, const char *service,
358 const char *action, struct UPNP_Arg_ *args,
359 char *buffer, size_t buf_size,
360 UPNP_command_cb_ caller_cb, void *caller_cls)
361{
362 struct GNUNET_CONNECTION_Handle *s;
363 struct UPNP_command_cls *cls;
364 struct sockaddr_in dest;
365 struct sockaddr_in6 dest6;
366 char hostname[MAX_HOSTNAME_LEN + 1];
367 unsigned short port = 0;
368 char *path;
369 char soap_act[128];
370 char soap_body[2048];
371 int body_size;
372 char *content_buf;
373 int headers_size;
374 char port_str[8];
375
376 snprintf (soap_act, sizeof (soap_act), "%s#%s", service, action);
377
378 if (args == NULL)
379 {
380 snprintf (soap_body, sizeof (soap_body),
381 "<?xml version=\"1.0\"?>\r\n"
382 "<" SOAP_PREFIX ":Envelope "
383 "xmlns:" SOAP_PREFIX
384 "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
385 SOAP_PREFIX
386 ":encodingStyle=\"http://schema GNUNET_free (content_buf);s.xmlsoap.org/soap/encoding/\">"
387 "<" SOAP_PREFIX ":Body>" "<" SERVICE_PREFIX
388 ":%s xmlns:" SERVICE_PREFIX "=\"%s\">" "</"
389 SERVICE_PREFIX ":%s>" "</" SOAP_PREFIX
390 ":Body></" SOAP_PREFIX ":Envelope>" "\r\n",
391 action, service, action);
392 }
393 else
394 {
395 char *p;
396 const char *pe, *pv;
397 int soap_body_len;
398
399 soap_body_len = snprintf (soap_body, sizeof (soap_body),
400 "<?xml version=\"1.0\"?>\r\n"
401 "<" SOAP_PREFIX ":Envelope "
402 "xmlns:" SOAP_PREFIX
403 "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
404 SOAP_PREFIX
405 ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
406 "<" SOAP_PREFIX ":Body>" "<" SERVICE_PREFIX
407 ":%s xmlns:" SERVICE_PREFIX "=\"%s\">",
408 action, service);
409
410 p = soap_body + soap_body_len;
411
412 while (args->elt)
413 {
414 /* check that we are never overflowing the string... */
415 if (soap_body + sizeof (soap_body) <= p + 100)
416 {
417 GNUNET_assert (GNUNET_NO);
418 (*caller_cb) (buffer, 0, caller_cls);
419 return;
420 }
421 *(p++) = '<';
422 pe = args->elt;
423 while (*pe)
424 *(p++) = *(pe++);
425 *(p++) = '>';
426 if ((pv = args->val))
427 {
428 while (*pv)
429 *(p++) = *(pv++);
430 }
431 *(p++) = '<';
432 *(p++) = '/';
433 pe = args->elt;
434 while (*pe)
435 *(p++) = *(pe++);
436 *(p++) = '>';
437 args++;
438 }
439 *(p++) = '<';
440 *(p++) = '/';
441 *(p++) = SERVICE_PREFIX2;
442 *(p++) = ':';
443 pe = action;
444
445 while (*pe)
446 *(p++) = *(pe++);
447
448 strncpy (p, "></" SOAP_PREFIX ":Body></" SOAP_PREFIX ":Envelope>\r\n",
449 soap_body + sizeof (soap_body) - p);
450 }
451
452 if (GNUNET_OK != parse_url (url, hostname, &port, &path))
453 {
454 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
455 "Invalid URL passed to UPNP_command(): %s\n", url);
456 return;
457 }
458
459
460 /* Test IPv4 address, else use IPv6 */
461 memset (&dest, 0, sizeof (dest));
462 memset (&dest6, 0, sizeof (dest6));
463
464 if (inet_pton (AF_INET, hostname, &dest.sin_addr) == 1)
465 {
466 dest.sin_family = AF_INET;
467 dest.sin_port = htons (port);
468#ifdef HAVE_SOCKADDR_IN_SIN_LEN
469 dest.sin_len = sizeof (dest);
470#endif
471
472 s = GNUNET_CONNECTION_create_from_sockaddr (sched, PF_INET,
473 (struct sockaddr *) &dest,
474 sizeof (dest));
475 }
476 else if (inet_pton (AF_INET6, hostname, &dest6.sin6_addr) == 1)
477 {
478 dest6.sin6_family = AF_INET6;
479 dest6.sin6_port = htons (port);
480#ifdef HAVE_SOCKADDR_IN_SIN_LEN
481 dest6.sin6_len = sizeof (dest6);
482#endif
483
484 s = GNUNET_CONNECTION_create_from_sockaddr (sched, PF_INET6,
485 (struct sockaddr *) &dest6,
486 sizeof (dest6));
487 }
488 else
489 {
490 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d\n"),
491 "UPnP", "inet_pton", __FILE__, __LINE__);
492
493 (*caller_cb) (buffer, 0, caller_cls);
494 return;
495 }
496
497 body_size = (int) strlen (soap_body);
498 content_buf = GNUNET_malloc (512 + body_size);
499
500 /* We are not using keep-alive HTTP connections.
501 * HTTP/1.1 needs the header Connection: close to do that.
502 * This is the default with HTTP/1.0 */
503 /* Connection: Close is normally there only in HTTP/1.1 but who knows */
504 port_str[0] = '\0';
505
506 if (port != 80)
507 snprintf (port_str, sizeof (port_str), ":%hu", port);
508
509 headers_size = snprintf (content_buf, 512, "POST %s HTTP/1.1\r\n" "Host: %s%s\r\n" "User-Agent: GNU, UPnP/1.0, GNUnet/" PACKAGE_VERSION "\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" /* ??? */
510 "Pragma: no-cache\r\n"
511 "\r\n", path, hostname, port_str, body_size,
512 soap_act);
513 memcpy (content_buf + headers_size, soap_body, body_size);
514
515#ifdef DEBUG_UPNP
516 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
517 "Sending command '%s' to '%s' (service '%s')\n",
518 action, url, service);
519#endif
520
521 cls = GNUNET_malloc (sizeof (struct UPNP_command_cls));
522 cls->s = s;
523 cls->content = content_buf;
524 cls->buffer = buffer;
525 cls->buf_size = buf_size;
526 cls->caller_cb = caller_cb;
527 cls->caller_cls = caller_cls;
528
529 cls->th =
530 GNUNET_CONNECTION_notify_transmit_ready (s, body_size + headers_size,
531 GNUNET_TIME_relative_multiply
532 (GNUNET_TIME_UNIT_SECONDS, 15),
533 &UPNP_command_transmit, cls);
534
535
536 if (cls->th == NULL)
537 {
538#ifdef DEBUG_UPNP
539 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
540 "Error sending SOAP request at %s:%d\n", __FILE__,
541 __LINE__);
542#endif
543
544 (*caller_cb) (buffer, 0, caller_cls);
545
546 GNUNET_free (content_buf);
547 GNUNET_free (cls);
548 GNUNET_CONNECTION_destroy (s, GNUNET_NO);
549 return;
550 }
551}
552
553struct get_external_ip_address_cls
554{
555 UPNP_get_external_ip_address_cb_ caller_cb;
556 void *caller_cls;
557};
558
559static void
560get_external_ip_address_receiver (char *response, size_t received, void *data)
561{
562 struct get_external_ip_address_cls *cls = data;
563 struct UPNP_REPLY_NameValueList_ pdata;
564 char extIpAdd[128];
565 char *p;
566 int ret = UPNP_COMMAND_UNKNOWN_ERROR;
567
568 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Response: %s", response);
569
570 UPNP_REPLY_parse_ (response, received, &pdata);
571 p = UPNP_REPLY_get_value_ (&pdata, "NewExternalIPAddress");
572 if (p)
573 {
574 strncpy (extIpAdd, p, 128);
575 extIpAdd[127] = '\0';
576 ret = UPNP_COMMAND_SUCCESS;
577 }
578 else
579 extIpAdd[0] = '\0';
580
581 p = UPNP_REPLY_get_value_ (&pdata, "errorCode");
582 if (p)
583 {
584 ret = UPNP_COMMAND_UNKNOWN_ERROR;
585 sscanf (p, "%d", &ret);
586 }
587 cls->caller_cb (ret, extIpAdd, cls->caller_cls);
588
589 UPNP_REPLY_free_ (&pdata);
590 GNUNET_free (response);
591 GNUNET_free (cls);
592}
593
594/* UPNP_get_external_ip_address_() call the corresponding UPNP method.
595 *
596 * Return values :
597 * 0 : SUCCESS
598 * NON ZERO : ERROR Either an UPnP error code or an unknown error.
599 *
600 * 402 Invalid Args - See UPnP Device Architecture section on Control.
601 * 501 Action Failed - See UPnP Device Architecture section on Control.
602 */
603void
604UPNP_get_external_ip_address_ (struct GNUNET_SCHEDULER_Handle *sched,
605 const char *control_url,
606 const char *service_type,
607 UPNP_get_external_ip_address_cb_ caller_cb,
608 void *caller_cls)
609{
610 struct get_external_ip_address_cls *cls;
611 char *buffer;
612
613 if (!control_url || !service_type)
614 caller_cb (UPNP_COMMAND_INVALID_ARGS, NULL, caller_cls);
615
616 cls = GNUNET_malloc (sizeof (struct get_external_ip_address_cls));
617 cls->caller_cb = caller_cb;
618 cls->caller_cls = caller_cls;
619
620 buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
621
622 UPNP_command_ (sched, control_url, service_type, "GetExternalIPAddress",
623 NULL, buffer, UPNP_COMMAND_BUFSIZE,
624 (UPNP_command_cb_) get_external_ip_address_receiver, cls);
625}
626
627struct PortMapping_cls
628{
629 const char *control_url;
630 const char *service_type;
631 const char *ext_port;
632 const char *in_port;
633 const char *proto;
634 const char *remoteHost;
635 UPNP_port_mapping_cb_ caller_cb;
636 void *caller_cls;
637};
638
639static void
640add_delete_port_mapping_receiver (char *response, size_t received, void *data)
641{
642 struct PortMapping_cls *cls = data;
643 struct UPNP_REPLY_NameValueList_ pdata;
644 const char *resVal;
645 int ret;
646
647 UPNP_REPLY_parse_ (response, received, &pdata);
648 resVal = UPNP_REPLY_get_value_ (&pdata, "errorCode");
649 if (resVal)
650 {
651 ret = UPNP_COMMAND_UNKNOWN_ERROR;
652 sscanf (resVal, "%d", &ret);
653 }
654 else
655 {
656 ret = UPNP_COMMAND_SUCCESS;
657 }
658
659 cls->caller_cb (ret, cls->control_url, cls->service_type,
660 cls->ext_port, cls->in_port, cls->proto,
661 cls->remoteHost, cls->caller_cls);
662
663 UPNP_REPLY_free_ (&pdata);
664 GNUNET_free (response);
665 GNUNET_free (cls);
666}
667
668void
669UPNP_add_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched,
670 const char *control_url, const char *service_type,
671 const char *ext_port,
672 const char *in_port,
673 const char *inClient,
674 const char *desc,
675 const char *proto, const char *remoteHost,
676 UPNP_port_mapping_cb_ caller_cb, void *caller_cls)
677{
678 struct UPNP_Arg_ args[9];
679 struct PortMapping_cls *cls;
680 char *buffer;
681
682 if (!in_port || !inClient || !proto || !ext_port)
683 {
684 caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
685 ext_port, in_port, proto, remoteHost, caller_cls);
686 return;
687 }
688
689 args[0].elt = "NewRemoteHost";
690 args[0].val = remoteHost;
691 args[1].elt = "NewExternalPort";
692 args[1].val = ext_port;
693 args[2].elt = "NewProtocol";
694 args[2].val = proto;
695 args[3].elt = "NewInternalPort";
696 args[3].val = in_port;
697 args[4].elt = "NewInternalClient";
698 args[4].val = inClient;
699 args[5].elt = "NewEnabled";
700 args[5].val = "1";
701 args[6].elt = "NewPortMappingDescription";
702 args[6].val = desc ? desc : "GNUnet";
703 args[7].elt = "NewLeaseDuration";
704 args[7].val = "0";
705 args[8].elt = NULL;
706 args[8].val = NULL;
707
708 cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
709 cls->control_url = control_url;
710 cls->service_type = service_type;
711 cls->ext_port = ext_port;;
712 cls->in_port = in_port;
713 cls->proto = proto;
714 cls->remoteHost = remoteHost;
715 cls->caller_cb = caller_cb;
716 cls->caller_cls = caller_cls;
717
718 buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
719
720 UPNP_command_ (sched, control_url, service_type, "AddPortMapping",
721 args, buffer, UPNP_COMMAND_BUFSIZE,
722 add_delete_port_mapping_receiver, cls);
723}
724
725void
726UPNP_delete_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched,
727 const char *control_url, const char *service_type,
728 const char *ext_port, const char *proto,
729 const char *remoteHost,
730 UPNP_port_mapping_cb_ caller_cb, void *caller_cls)
731{
732 struct UPNP_Arg_ args[4];
733 struct PortMapping_cls *cls;
734 char *buffer;
735
736 if (!ext_port || !proto)
737 {
738 caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
739 ext_port, NULL, proto, remoteHost, caller_cls);
740 return;
741 }
742
743 args[0].elt = "NewRemoteHost";
744 args[0].val = remoteHost;
745 args[1].elt = "NewExternalPort";
746 args[1].val = ext_port;
747 args[2].elt = "NewProtocol";
748 args[2].val = proto;
749 args[3].elt = NULL;
750 args[3].val = NULL;
751
752 cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
753 cls->control_url = control_url;
754 cls->service_type = service_type;
755 cls->ext_port = ext_port;
756 cls->in_port = "0";
757 cls->proto = proto;
758 cls->remoteHost = remoteHost;
759 cls->caller_cb = caller_cb;
760 cls->caller_cls = caller_cls;
761
762 buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
763
764 UPNP_command_ (sched, control_url, service_type,
765 "DeletePortMapping",
766 args, buffer, UPNP_COMMAND_BUFSIZE,
767 add_delete_port_mapping_receiver, cls);
768}
769
770
771struct get_specific_port_mapping_entry_cls
772{
773 const char *control_url;
774 const char *service_type;
775 const char *ext_port;
776 const char *proto;
777 UPNP_port_mapping_cb_ caller_cb;
778 void *caller_cls;
779};
780
781static void
782get_specific_port_mapping_entry_receiver (char *response, size_t received,
783 void *data)
784{
785 struct PortMapping_cls *cls = data;
786 struct UPNP_REPLY_NameValueList_ pdata;
787 char *p;
788 char in_port[128];
789 char in_client[128];
790 int ret;
791
792 UPNP_REPLY_parse_ (response, received, &pdata);
793
794 p = UPNP_REPLY_get_value_ (&pdata, "NewInternalClient");
795 if (p)
796 {
797 strncpy (in_client, p, 128);
798 in_client[127] = '\0';
799 }
800 else
801 in_client[0] = '\0';
802
803 p = UPNP_REPLY_get_value_ (&pdata, "NewInternalPort");
804 if (p)
805 {
806 strncpy (in_port, p, 6);
807 in_port[5] = '\0';
808 }
809 else
810 in_port[0] = '\0';
811
812 p = UPNP_REPLY_get_value_ (&pdata, "errorCode");
813 if (p)
814 {
815 if (p)
816 {
817 ret = UPNP_COMMAND_UNKNOWN_ERROR;
818 sscanf (p, "%d", &ret);
819 }
820#if DEBUG_UPNP
821 PRINT_UPNP_ERROR ("GetSpecificPortMappingEntry", p);
822#endif
823 }
824
825 cls->caller_cb (ret, cls->control_url, cls->service_type,
826 cls->ext_port, cls->proto, in_port, in_client,
827 cls->caller_cls);
828
829 UPNP_REPLY_free_ (&pdata);
830 GNUNET_free (response);
831 GNUNET_free (cls);
832}
833
834/* UPNP_get_specific_port_mapping_entry _ retrieves an existing port mapping
835 * the result is returned in the in_client and in_port strings
836 * please provide 128 and 6 bytes of data */
837void
838UPNP_get_specific_port_mapping_entry_ (struct GNUNET_SCHEDULER_Handle *sched,
839 const char *control_url,
840 const char *service_type,
841 const char *ext_port,
842 const char *proto,
843 UPNP_get_specific_port_mapping_entry_cb_
844 caller_cb, void *caller_cls)
845{
846 struct UPNP_Arg_ args[4];
847 struct get_specific_port_mapping_entry_cls *cls;
848 char *buffer;
849
850 if (!ext_port || !proto)
851 {
852 caller_cb (UPNP_COMMAND_INVALID_ARGS, control_url, service_type,
853 ext_port, proto, NULL, NULL, caller_cls);
854 return;
855 }
856
857 args[0].elt = "NewRemoteHost";
858 args[0].val = NULL;
859 args[1].elt = "NewExternalPort";
860 args[1].val = ext_port;
861 args[2].elt = "NewProtocol";
862 args[2].val = proto;
863 args[3].elt = NULL;
864 args[3].val = NULL;
865
866 cls = GNUNET_malloc (sizeof (struct PortMapping_cls));
867 cls->control_url = control_url;
868 cls->service_type = service_type;
869 cls->ext_port = ext_port;
870 cls->proto = proto;
871 cls->caller_cb = caller_cb;
872 cls->caller_cls = caller_cls;
873
874 buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
875
876 UPNP_command_ (sched, control_url, service_type,
877 "GetSpecificPortMappingEntry",
878 args, buffer, UPNP_COMMAND_BUFSIZE,
879 get_specific_port_mapping_entry_receiver, cls);
880}
diff --git a/src/nat/upnp-commands.h b/src/nat/upnp-commands.h
new file mode 100644
index 000000000..8e7510dbf
--- /dev/null
+++ b/src/nat/upnp-commands.h
@@ -0,0 +1,281 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010 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 3, 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 * Code in this file is originally based on the miniupnp library.
23 * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
24 *
25 * Original licence:
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are met:
29 *
30 * * Redistributions of source code must retain the above copyright notice,
31 * this list of conditions and the following disclaimer.
32 * * Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 * * The name of the author may not be used to endorse or promote products
36 * derived from this software without specific prior written permission.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
42 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
43 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
44 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
48 * POSSIBILITY OF SUCH DAMAGE.
49 */
50
51/**
52 * @file nat/upnp-commands.h
53 * @brief Commands to control UPnP IGD devices
54 *
55 * @author Milan Bouchet-Valat
56 */
57#ifndef UPNP_COMMANDS_H
58#define UPNP_COMMANDS_H
59
60#include "platform.h"
61#include "gnunet_scheduler_lib.h"
62
63/**
64 * Generic UPnP error codes.
65 */
66#define UPNP_COMMAND_SUCCESS (0)
67#define UPNP_COMMAND_UNKNOWN_ERROR (-1)
68#define UPNP_COMMAND_INVALID_ARGS (-2)
69
70/**
71 * Size of the buffer used to store anwsers to UPnP commands.
72 */
73#define UPNP_COMMAND_BUFSIZE 4096
74
75/**
76 * Name-value pair containing an argumeny to a UPnP command.
77 */
78struct UPNP_Arg_
79{
80 const char *elt;
81 const char *val;
82};
83
84/**
85 * Callback for UPNP_command_().
86 *
87 * @param response the buffer passed to UPNP_command_(), filled with
88 * NULL-terminated content (if any)
89 * @param received length of the content received and stored in response
90 * @param cls closure passed to UPNP_command_()
91 */
92typedef void (*UPNP_command_cb_) (char *response, size_t received, void *cls);
93
94/**
95 * Send UPnP command to the device identified by url and service.
96 *
97 * @param sched scheduler to use for network tasks
98 * @param url control URL of the device
99 * @param service type of the service corresponding to the command
100 * @param action action to send
101 * @param args arguments for action
102 * @param caller_cb user callback to trigger when done
103 * @param caller_cls closure to pass to caller_cb
104 */
105void UPNP_command_ (struct GNUNET_SCHEDULER_Handle *sched,
106 const char *url, const char *service,
107 const char *action, struct UPNP_Arg_ *args,
108 char *buffer, size_t buf_size,
109 UPNP_command_cb_ caller_cb, void *caller_cls);
110
111/**
112 * Callback to UPNP_get_external_ip_address_().
113 *
114 * Possible UPnP Errors :
115 * 402 Invalid Args - See UPnP Device Architecture section on Control.
116 * 501 Action Failed - See UPnP Device Architecture section on Control.
117 *
118 * @param error GNUNET_OK on success, another value on error (see above)
119 * @param ext_ip_addr the external IP address reported by the device (IPv4 or v6)
120 * @param cls the closure passed to UPNP_get_external_ip_address_()
121 */
122typedef void (*UPNP_get_external_ip_address_cb_) (int error,
123 char *ext_ip_addr,
124 void *cls);
125
126/**
127 * Get the IP address associated with the WAN connection of the device.
128 * See UPNP_get_external_ip_address_cb_.
129 *
130 * @param sched the scheduler to use for networking
131 * @param control_url the control URL corresponding to service_type on the device
132 * @param service_type service type to call the command on
133 * @param caller_cb function to call when done
134 * @param cls closure passed to caller_cb
135 */
136void
137UPNP_get_external_ip_address_ (struct GNUNET_SCHEDULER_Handle *sched,
138 const char *control_url,
139 const char *service_type,
140 UPNP_get_external_ip_address_cb_ caller_cb,
141 void *caller_cls);
142
143/**
144 * Callback to UPNP_add_port_mapping_() and UPNP_delete_port_mapping_().
145 *
146 * Possible UPnP Errors with UPNP_add_port_mapping_():
147 * 402 Invalid Args - See UPnP Device Architecture section on Control.
148 * 501 Action Failed - See UPnP Device Architecture section on Control.
149 * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
150 * wild-carded
151 * 716 WildCardNotPermittedInext_port - The external port cannot be wild-carded
152 * 718 ConflictInMappingEntry - The port mapping entry specified conflicts
153 * with a mapping assigned previously to another client
154 * 724 SamePortValuesRequired - Internal and External port values
155 * must be the same
156 * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
157 * permanent lease times on port mappings
158 * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
159 * and cannot be a specific IP address or DNS name
160 * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
161 * cannot be a specific port value
162 *
163 * Possible UPnP Errors with UPNP_delete_port_mapping_():
164 * 402 Invalid Args - See UPnP Device Architecture section on Control.
165 * 714 NoSuchEntryInArray - The specified value does not exist in the array
166 *
167 * @param error GNUNET_OK on success, another value on error (see above)
168 * @param control_url the control URL the command was called on
169 * @param service_type service the command was called on
170 * @param in_port port on the gateway on the LAN side which was requested
171 * @param in_client address in the LAN which was requested
172 * @param proto protocol for which port mapping was requested
173 * @param remote_host remote host for which port mapping was requested
174 * @param cls the closure passed to the command function
175 */
176typedef void (*UPNP_port_mapping_cb_) (int error,
177 const char *control_url,
178 const char *service_type,
179 const char *ext_port,
180 const char *inPort, const char *proto,
181 const char *remote_host, void *cls);
182
183
184/**
185 * Request opening a port on the IGD device.
186 * (remote_host is usually NULL because IGDs don't support it.)
187 *
188 * @param sched the scheduler to use for networking
189 * @param control_url the control URL corresponding to service_type on the device
190 * @param service_type service type to call the command on
191 * @param ext_port port that should be opened on the WAN side
192 * @param in_port port on the gateway on the LAN side which should map ext_port
193 * @param in_client address in the LAN to which packets should be redirected
194 * @param proto protocol for which to request port mapping
195 * @param remote_host remote host for which to request port mapping
196 * @param caller_cb function to call when done
197 * @param cls closure passed to caller_cb
198 */
199void
200UPNP_add_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched,
201 const char *control_url, const char *service_type,
202 const char *ext_port,
203 const char *in_port,
204 const char *in_client,
205 const char *desc,
206 const char *proto, const char *remote_host,
207 UPNP_port_mapping_cb_ caller_cb, void *caller_cls);
208
209/**
210 * Request closing a a port on the IGD device that was previously opened
211 * using UPNP_add_port_mapping_(). Use the same argument values that were
212 * used when opening the port.
213 * (remote_host is usually NULL because IGDs don't support it.)
214 *
215 * @param sched the scheduler to use for networking
216 * @param control_url the control URL the command was called on
217 * @param service_type service the command was called on
218 * @param in_port port on the gateway on the LAN side which was requested
219 * @param in_client address in the LAN which was requested
220 * @param proto protocol for which port mapping was requested
221 * @param remote_host remote host for which port mapping was requested
222 * @param caller_cb function to call when done
223 * @param cls closure passed to caller_cb
224 */
225void
226UPNP_delete_port_mapping_ (struct GNUNET_SCHEDULER_Handle *sched,
227 const char *control_url, const char *service_type,
228 const char *ext_port, const char *proto,
229 const char *remote_host,
230 UPNP_port_mapping_cb_ caller_cb, void *caller_cls);
231
232
233/**
234 * Callback to UPNP_get_specific_port_mapping_entry _().
235 *
236 * @param error GNUNET_OK if port is currently mapped, another value on error
237 * @param control_url the control URL the command was called on
238 * @param service_type service the command was called on
239 * @param in_port port on the gateway on the LAN side which was requested
240 * @param in_client address in the LAN which was requested
241 * @param proto protocol for which port mapping was requested
242 * @param remote_host remote host for which port mapping was requested
243 * @param cls the closure passed to the command function
244 */
245typedef void (*UPNP_get_specific_port_mapping_entry_cb_) (int error,
246 const char
247 *control_url,
248 const char
249 *service_type,
250 const char
251 *ext_port,
252 const char *proto,
253 const char *in_port,
254 const char
255 *in_client,
256 void *cls);
257
258/**
259 * Check that a port mapping set up with UPNP_add_port_mapping_()
260 * is alive.
261 *
262 * @param sched the scheduler to use for networking
263 * @param control_url the control URL the command was called on
264 * @param service_type service the command was called on
265 * @param in_port port on the gateway on the LAN side which was requested
266 * @param in_client address in the LAN which was requested
267 * @param proto protocol for which port mapping was requested
268 * @param remote_host remote host for which port mapping was requested
269 * @param caller_cb function to call when done
270 * @param cls closure passed to caller_cb
271 */
272void
273UPNP_get_specific_port_mapping_entry_ (struct GNUNET_SCHEDULER_Handle *sched,
274 const char *control_url,
275 const char *service_type,
276 const char *ext_port,
277 const char *proto,
278 UPNP_get_specific_port_mapping_entry_cb_
279 caller_cb, void *caller_cls);
280
281#endif
diff --git a/src/nat/upnp-discover.c b/src/nat/upnp-discover.c
new file mode 100644
index 000000000..715f2fcee
--- /dev/null
+++ b/src/nat/upnp-discover.c
@@ -0,0 +1,1285 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010 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 3, 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 * Code in this file is originally based on the miniupnp library.
23 * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
24 *
25 * Original licence:
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are met:
29 *
30 * * Redistributions of source code must retain the above copyright notice,
31 * this list of conditions and the following disclaimer.
32 * * Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 * * The name of the author may not be used to endorse or promote products
36 * derived from this software without specific prior written permission.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
42 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
43 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
44 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
48 * POSSIBILITY OF SUCH DAMAGE.
49 */
50
51/**
52 * @file nat/upnp-discover.c
53 * @brief Look for UPnP IGD devices
54 *
55 * @author Milan Bouchet-Valat
56 */
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60#include <curl/curl.h>
61
62#include "platform.h"
63#include "gnunet_util_lib.h"
64#include "upnp-discover.h"
65#include "upnp-reply-parse.h"
66#include "upnp-igd-parse.h"
67#include "upnp-minixml.h"
68
69#define DISCOVER_BUFSIZE 512
70#define DESCRIPTION_BUFSIZE 2048
71#define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_log(GNUNET_ERROR_TYPE_WARNING, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0)
72#define PRINT_SOCKET_ERROR(a) GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "UPnP", _("%s failed at %s:%d: '%s'\n"), a, __FILE__, __LINE__, strerror (errno));
73
74
75/**
76 * Callback function called when download is finished.
77 *
78 * @param data the contents of the downloaded file, or NULL
79 * @param cls closure passed via download_device_description()
80 */
81typedef void (*download_cb) (char *data, void *cls);
82
83/**
84 * Private closure used by download_device_description() and it's callbacks.
85 */
86struct download_cls
87{
88 /**
89 * Scheduler used for the download task.
90 */
91 struct GNUNET_SCHEDULER_Handle *sched;
92
93 /**
94 * curl_easy handle.
95 */
96 CURL *curl;
97
98 /**
99 * curl_multi handle.
100 */
101 CURLM *multi;
102
103 /**
104 * URL of the file to download.
105 */
106 char *url;
107
108 /**
109 * Time corresponding to timeout wanted by the caller.
110 */
111 struct GNUNET_TIME_Absolute end_time;
112
113 /**
114 * Buffer to store downloaded content.
115 */
116 char download_buffer[DESCRIPTION_BUFSIZE];
117
118 /**
119 * Size of the already downloaded content.
120 */
121 size_t download_pos;
122
123 /**
124 * User callback to trigger when done.
125 */
126 download_cb caller_cb;
127
128 /**
129 * User closure to pass to caller_cb.
130 */
131 void *caller_cls;
132};
133
134/**
135 * Clean up the state of CURL multi handle and that of
136 * the only easy handle it uses.
137 */
138static void
139download_clean_up (struct download_cls *cls)
140{
141 CURLMcode mret;
142
143 mret = curl_multi_cleanup (cls->multi);
144 if (mret != CURLM_OK)
145 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP",
146 _("%s failed at %s:%d: `%s'\n"),
147 "curl_multi_cleanup", __FILE__, __LINE__,
148 curl_multi_strerror (mret));
149
150 curl_easy_cleanup (cls->curl);
151 GNUNET_free (cls);
152}
153
154/**
155 * Process downloaded bits by calling callback on each HELLO.
156 *
157 * @param ptr buffer with downloaded data
158 * @param size size of a record
159 * @param nmemb number of records downloaded
160 * @param ctx closure
161 * @return number of bytes that were processed (always size*nmemb)
162 */
163static size_t
164callback_download (void *ptr, size_t size, size_t nmemb, void *ctx)
165{
166 struct download_cls *cls = ctx;
167 const char *cbuf = ptr;
168 size_t total;
169 size_t cpy;
170
171 total = size * nmemb;
172 if (total == 0)
173 return total; /* ok, no data */
174
175 cpy = GNUNET_MIN (total, DESCRIPTION_BUFSIZE - cls->download_pos - 1);
176 memcpy (&cls->download_buffer[cls->download_pos], cbuf, cpy);
177 cbuf += cpy;
178 cls->download_pos += cpy;
179
180#if DEBUG_UPNP
181 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
182 "Downloaded %d records of size %d, download position: %d\n",
183 size, nmemb, cls->download_pos);
184#endif
185
186 return total;
187}
188
189static void
190task_download (struct download_cls *cls,
191 const struct GNUNET_SCHEDULER_TaskContext *tc);
192
193/**
194 * Ask CURL for the select set and then schedule the
195 * receiving task with the scheduler.
196 */
197static void
198download_prepare (struct download_cls *cls)
199{
200 CURLMcode mret;
201 fd_set rs;
202 fd_set ws;
203 fd_set es;
204 int max;
205 struct GNUNET_NETWORK_FDSet *grs;
206 struct GNUNET_NETWORK_FDSet *gws;
207 long timeout;
208 struct GNUNET_TIME_Relative rtime;
209
210 max = -1;
211 FD_ZERO (&rs);
212 FD_ZERO (&ws);
213 FD_ZERO (&es);
214 mret = curl_multi_fdset (cls->multi, &rs, &ws, &es, &max);
215 if (mret != CURLM_OK)
216 {
217 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP",
218 _("%s failed at %s:%d: `%s'\n"),
219 "curl_multi_fdset", __FILE__, __LINE__,
220 curl_multi_strerror (mret));
221 download_clean_up (cls);
222 cls->caller_cb (NULL, cls->caller_cls);
223 return;
224 }
225 mret = curl_multi_timeout (cls->multi, &timeout);
226 if (mret != CURLM_OK)
227 {
228 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP",
229 _("%s failed at %s:%d: `%s'\n"),
230 "curl_multi_timeout", __FILE__, __LINE__,
231 curl_multi_strerror (mret));
232 download_clean_up (cls);
233 cls->caller_cb (NULL, cls->caller_cls);
234 return;
235 }
236 rtime =
237 GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining
238 (cls->end_time),
239 GNUNET_TIME_relative_multiply
240 (GNUNET_TIME_UNIT_MILLISECONDS, timeout));
241 grs = GNUNET_NETWORK_fdset_create ();
242 gws = GNUNET_NETWORK_fdset_create ();
243 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
244 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
245
246 GNUNET_SCHEDULER_add_select (cls->sched,
247 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
248 GNUNET_SCHEDULER_NO_TASK,
249 rtime,
250 grs,
251 gws,
252 (GNUNET_SCHEDULER_Task) & task_download, cls);
253 GNUNET_NETWORK_fdset_destroy (gws);
254 GNUNET_NETWORK_fdset_destroy (grs);
255}
256
257/**
258 * Task that is run when we are ready to receive more data from the device.
259 *
260 * @param cls closure
261 * @param tc task context
262 */
263static void
264task_download (struct download_cls *cls,
265 const struct GNUNET_SCHEDULER_TaskContext *tc)
266{
267
268 int running;
269 struct CURLMsg *msg;
270 CURLMcode mret;
271
272 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
273 {
274#if DEBUG_UPNP
275 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
276 "Shutdown requested while trying to download device description from `%s'\n",
277 cls->url);
278#endif
279 cls->caller_cb (NULL, cls->caller_cls);
280 download_clean_up (cls);
281 return;
282 }
283 if (GNUNET_TIME_absolute_get_remaining (cls->end_time).value == 0)
284 {
285 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
286 _
287 ("Timeout trying to download UPnP device description from '%s'\n"),
288 cls->url);
289 cls->caller_cb (NULL, cls->caller_cls);
290 download_clean_up (cls);
291 return;
292 }
293
294 do
295 {
296 running = 0;
297 mret = curl_multi_perform (cls->multi, &running);
298
299 if (running == 0)
300 {
301 do
302 {
303 msg = curl_multi_info_read (cls->multi, &running);
304 GNUNET_break (msg != NULL);
305 if (msg == NULL)
306 break;
307
308 if ((msg->data.result != CURLE_OK) &&
309 (msg->data.result != CURLE_GOT_NOTHING))
310 {
311 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
312 _("%s failed for `%s' at %s:%d: `%s'\n"),
313 "curl_multi_perform",
314 cls->url,
315 __FILE__,
316 __LINE__,
317 curl_easy_strerror (msg->data.result));
318 cls->caller_cb (NULL, cls->caller_cls);
319 }
320 else
321 {
322 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
323 _
324 ("Download of device description `%s' completed.\n"),
325 cls->url);
326 cls->caller_cb (GNUNET_strdup (cls->download_buffer),
327 cls->caller_cls);
328 }
329
330 download_clean_up (cls);
331 return;
332 }
333 while ((running > 0));
334 }
335 }
336 while (mret == CURLM_CALL_MULTI_PERFORM);
337
338 if (mret != CURLM_OK)
339 {
340 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, "UPnP",
341 _("%s failed at %s:%d: `%s'\n"),
342 "curl_multi_perform", __FILE__, __LINE__,
343 curl_multi_strerror (mret));
344 download_clean_up (cls);
345 cls->caller_cb (NULL, cls->caller_cls);
346 }
347
348 download_prepare (cls);
349}
350
351
352/**
353 * Download description from devices.
354 *
355 * @param sched the scheduler to use for the download task
356 * @param url URL of the file to download
357 * @param caller_cb user function to call when done
358 * @caller_cls closure to pass to caller_cb
359 */
360void
361download_device_description (struct GNUNET_SCHEDULER_Handle *sched,
362 char *url, download_cb caller_cb,
363 void *caller_cls)
364{
365 CURL *curl;
366 CURLM *multi;
367 CURLcode ret;
368 CURLMcode mret;
369 struct download_cls *cls;
370
371 cls = GNUNET_malloc (sizeof (struct download_cls));
372
373 curl = curl_easy_init ();
374 if (curl == NULL)
375 goto error;
376
377 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &callback_download);
378 if (ret != CURLE_OK)
379 goto error;
380
381 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, cls);
382 if (ret != CURLE_OK)
383 goto error;
384
385 CURL_EASY_SETOPT (curl, CURLOPT_FOLLOWLOCATION, 1);
386 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 4);
387 /* no need to abort if the above failed */
388 CURL_EASY_SETOPT (curl, CURLOPT_URL, url);
389 if (ret != CURLE_OK)
390 goto error;
391
392 CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
393 CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, DESCRIPTION_BUFSIZE);
394 CURL_EASY_SETOPT (curl, CURLOPT_USERAGENT, "GNUnet");
395 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 60L);
396 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 60L);
397
398 multi = curl_multi_init ();
399 if (multi == NULL)
400 {
401 GNUNET_break (0);
402 /* clean_up (); */
403 return;
404 }
405 mret = curl_multi_add_handle (multi, curl);
406 if (mret != CURLM_OK)
407 {
408 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP",
409 _("%s failed at %s:%d: `%s'\n"),
410 "curl_multi_add_handle", __FILE__, __LINE__,
411 curl_multi_strerror (mret));
412 mret = curl_multi_cleanup (multi);
413 if (mret != CURLM_OK)
414 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "UPnP",
415 _("%s failed at %s:%d: `%s'\n"),
416 "curl_multi_cleanup", __FILE__, __LINE__,
417 curl_multi_strerror (mret));
418 goto error;
419 return;
420 }
421
422#if DEBUG_UPNP
423 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
424 "Preparing to download device description from '%s'\n",
425 url);
426#endif
427
428 cls->sched = sched;
429 cls->curl = curl;
430 cls->multi = multi;
431 cls->url = url;
432 cls->end_time = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
433 memset (cls->download_buffer, 0, DESCRIPTION_BUFSIZE);
434 cls->download_pos = 0;
435 cls->caller_cb = caller_cb;
436 cls->caller_cls = caller_cls;
437 download_prepare (cls);
438 return;
439
440
441error:
442 GNUNET_break (0);
443 GNUNET_free (cls);
444 curl_easy_cleanup (curl);
445 caller_cb (NULL, caller_cls);
446}
447
448/**
449 * Parse SSDP packet received in reply to a M-SEARCH message.
450 *
451 * @param reply contents of the packet
452 * @param size length of reply
453 * @param location address of a pointer that will be set to the start
454 * of the "location" field
455 * @param location_size pointer where to store the length of the "location" field
456 * @param st pointer address of a pointer that will be set to the start
457 * of the "st" (search target) field
458 * @param st_size pointer where to store the length of the "st" field
459 * The strings are NOT null terminated */
460static void
461parse_msearch_reply (const char *reply, int size,
462 const char **location, int *location_size,
463 const char **st, int *st_size)
464{
465 int a, b, i;
466
467 i = 0;
468 b = 0;
469 /* Start of the line */
470 a = i;
471
472 while (i < size)
473 {
474 switch (reply[i])
475 {
476 case ':':
477 if (b == 0)
478 /* End of the "header" */
479 b = i;
480 break;
481 case '\x0a':
482 case '\x0d':
483 if (b != 0)
484 {
485 do
486 {
487 b++;
488 }
489 while (reply[b] == ' ');
490
491 if (0 == strncasecmp (reply + a, "location", 8))
492 {
493 *location = reply + b;
494 *location_size = i - b;
495 }
496 else if (0 == strncasecmp (reply + a, "st", 2))
497 {
498 *st = reply + b;
499 *st_size = i - b;
500 }
501
502 b = 0;
503 }
504
505 a = i + 1;
506 break;
507 default:
508 break;
509 }
510
511 i++;
512 }
513}
514
515/**
516 * Standard port for UPnP discovery (SSDP protocol).
517 */
518#define PORT 1900
519
520/**
521 * Convert a constant integer into a string.
522 */
523#define XSTR(s) STR(s)
524#define STR(s) #s
525
526/**
527 * Standard IPv4 multicast adress for UPnP discovery (SSDP protocol).
528 */
529#define UPNP_MCAST_ADDR "239.255.255.250"
530
531/**
532 * Standard IPv6 multicast adress for UPnP discovery (SSDP protocol).
533 */
534#define UPNP_MCAST_ADDR6 "FF02:0:0:0:0:0:0:F"
535
536/**
537 * Size of the buffer needed to store SSDP requests we send.
538 */
539#define UPNP_DISCOVER_BUFSIZE 1536
540
541/**
542 * Description of a UPnP device containing everything
543 * we may need to control it.
544 *
545 * Meant to be member of a chained list.
546 */
547struct UPNP_Dev_
548{
549 /**
550 * Next device in the list, if any.
551 */
552 struct UPNP_Dev_ *pNext;
553
554 /**
555 * Path to the file describing the device.
556 */
557 char *desc_url;
558
559 /**
560 * UPnP search target.
561 */
562 char *st;
563
564 /**
565 * Service type associated with the control_url for the device.
566 */
567 char *service_type;
568
569 /**
570 * URL to send commands to.
571 */
572 char *control_url;
573
574 /**
575 * Whether the device is currently connected to the WAN.
576 */
577 int is_connected;
578
579 /**
580 * IGD Data associated with the device.
581 */
582 struct UPNP_IGD_Data_ *data;
583};
584
585/**
586 * Private closure used by UPNP_discover() and its callbacks.
587 */
588struct UPNP_discover_cls
589{
590 /**
591 * Scheduler to use for networking tasks.
592 */
593 struct GNUNET_SCHEDULER_Handle *sched;
594
595 /**
596 * Remote address used for multicast emission and reception.
597 */
598 struct sockaddr *multicast_addr;
599
600 /**
601 * Network handle used to send and receive discovery messages.
602 */
603 struct GNUNET_NETWORK_Handle *sudp;
604
605 /**
606 * fdset used with sudp.
607 */
608 struct GNUNET_NETWORK_FDSet *fdset;
609
610 /**
611 * Connection handle used to download device description.
612 */
613 struct GNUNET_CONNECTION_Handle *s;
614
615 /**
616 * Transmission handle used with s.
617 */
618 struct GNUNET_CONNECTION_TransmitHandle *th;
619
620 /**
621 * Index of the UPnP device type we're currently sending discovery messages to.
622 */
623 int type_index;
624
625 /**
626 * List of discovered devices.
627 */
628 struct UPNP_Dev_ *dev_list;
629
630 /**
631 * Device we're currently fetching description from.
632 */
633 struct UPNP_Dev_ *current_dev;
634
635 /**
636 * User callback to trigger when done.
637 */
638 UPNP_discover_cb_ caller_cb;
639
640 /**
641 * Closure passed to caller_cb.
642 */
643 void *caller_cls;
644};
645
646/**
647 * Check that raw_url is absolute, and if not, use ref_url to resolve it:
648 * if is_desc_file is GNUNET_YES, the path to the parent of the file is used;
649 * if it is GNUNET_NO, ref_url will be considered as the base URL for raw URL.
650 *
651 * @param ref_url base URL for the device
652 * @param is_desc_file whether ref_url is a path to the description file
653 * @param raw_url a possibly relative URL
654 * @returns a new string with an absolute URL
655 */
656static char *
657get_absolute_url (const char *ref_url, int is_desc_file, const char *raw_url)
658{
659 char *final_url;
660
661 if ((raw_url[0] == 'h')
662 && (raw_url[1] == 't')
663 && (raw_url[2] == 't')
664 && (raw_url[3] == 'p')
665 && (raw_url[4] == ':') && (raw_url[5] == '/') && (raw_url[6] == '/'))
666 {
667 final_url = GNUNET_strdup (raw_url);
668 }
669 else
670 {
671 int n = strlen (raw_url);
672 int l = strlen (ref_url);
673 int cpy_len = l;
674 char *slash;
675
676 /* If base URL is a path to the description file, go one level higher */
677 if (is_desc_file == GNUNET_YES)
678 {
679 slash = strrchr (ref_url, '/');
680 cpy_len = slash - ref_url;
681 }
682
683 final_url = GNUNET_malloc (l + n + 1);
684
685 /* Add trailing slash to base URL if needed */
686 if (raw_url[0] != '/' && ref_url[cpy_len] != '\0')
687 final_url[cpy_len++] = '/';
688
689 strncpy (final_url, ref_url, cpy_len);
690 strcpy (final_url + cpy_len, raw_url);
691 final_url[cpy_len + n] = '\0';
692 }
693
694 return final_url;
695}
696
697
698/**
699 * Construct control URL for device from its description URL and
700 * UPNP_IGD_Data_ information. This involves resolving relative paths
701 * and choosing between Common Interface Config and interface-specific
702 * paths.
703 *
704 * @param desc_url URL to the description file of the device
705 * @param data IGD information obtained from the description file
706 * @returns a URL to control the IGD device, or the empty string
707 * in case of failure
708 */
709static char *
710format_control_urls (const char *desc_url, struct UPNP_IGD_Data_ *data)
711{
712 const char *ref_url;
713 int is_desc_file;
714
715 if (data->base_url[0] != '\0')
716 {
717 ref_url = data->base_url;
718 is_desc_file = GNUNET_NO;
719 }
720 else
721 {
722 ref_url = desc_url;
723 is_desc_file = GNUNET_YES;
724 }
725
726 if (data->control_url[0] != '\0')
727 return get_absolute_url (ref_url, is_desc_file, data->control_url);
728 else if (data->control_url_CIF[0] != '\0')
729 return get_absolute_url (ref_url, is_desc_file, data->control_url_CIF);
730 else
731 return GNUNET_strdup ("");
732}
733
734static void get_valid_igd (struct UPNP_discover_cls *cls);
735
736/**
737 * Called when "GetStatusInfo" command finishes. Check whether IGD device reports
738 * to be currently connected or not.
739 *
740 * @param response content of the UPnP message answered by the device
741 * @param received number of received bytes stored in response
742 * @param data closure from UPNP_discover()
743 */
744static void
745get_valid_igd_connected_cb (char *response, size_t received, void *data)
746{
747 struct UPNP_discover_cls *cls = data;
748 struct UPNP_REPLY_NameValueList_ pdata;
749 char *status;
750 char *error;
751
752 UPNP_REPLY_parse_ (response, received, &pdata);
753
754 status = UPNP_REPLY_get_value_ (&pdata, "NewConnectionStatus");
755 error = UPNP_REPLY_get_value_ (&pdata, "errorCode");
756
757 if (status)
758 cls->current_dev->is_connected = (strcmp ("Connected", status) == 0);
759 else
760 cls->current_dev->is_connected = GNUNET_NO;
761
762 if (error)
763 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
764 _("Could not get UPnP device status: error %s\n"),
765 error);
766
767 GNUNET_free (response);
768 UPNP_REPLY_free_ (&pdata);
769
770 /* Go on to next device, or finish discovery process */
771 cls->current_dev = cls->current_dev->pNext;
772 get_valid_igd (cls);
773}
774
775/**
776 * Receive contents of the downloaded UPnP IGD description file,
777 * and fill UPNP_Dev_ and UPNP_IGD_Data_ structs with this data.
778 * Then, schedule UPnP command to check whether device is connected.
779 *
780 * @param desc UPnP IGD description (in XML)
781 * @data closure from UPNP_discover()
782 */
783static void
784get_valid_igd_receive (char *desc, void *data)
785{
786 struct UPNP_discover_cls *cls = data;
787 struct UPNP_IGD_Data_ *igd_data;
788 char *buffer;
789
790 if (!desc || strlen (desc) == 0)
791 {
792 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, "UPnP",
793 "Error getting IGD XML description at %s:%d\n",
794 __FILE__, __LINE__);
795
796 /* Skip device */
797 cls->current_dev->data = NULL;
798 cls->current_dev->is_connected = GNUNET_NO;
799 get_valid_igd (cls);
800 }
801
802 igd_data = GNUNET_malloc (sizeof (struct UPNP_IGD_Data_));
803 memset (igd_data, 0, sizeof (struct UPNP_IGD_Data_));
804 UPNP_IGD_parse_desc_ (desc, strlen (desc), igd_data);
805
806 cls->current_dev->control_url =
807 format_control_urls (cls->current_dev->desc_url, igd_data);
808
809 if (igd_data->service_type != '\0')
810 cls->current_dev->service_type = GNUNET_strdup (igd_data->service_type);
811 else if (igd_data->service_type_CIF != '\0')
812 cls->current_dev->service_type =
813 GNUNET_strdup (igd_data->service_type_CIF);
814 else
815 cls->current_dev->service_type = GNUNET_strdup ("");
816
817 cls->current_dev->data = igd_data;
818
819 /* Check whether device is connected */
820 buffer = GNUNET_malloc (UPNP_COMMAND_BUFSIZE);
821 UPNP_command_ (cls->sched,
822 cls->current_dev->control_url,
823 cls->current_dev->data->service_type,
824 "GetStatusInfo", NULL, buffer, UPNP_COMMAND_BUFSIZE,
825 get_valid_igd_connected_cb, cls);
826
827 GNUNET_free (desc);
828}
829
830/**
831 * Free a chained list of UPnP devices.
832 */
833static void
834free_dev_list (struct UPNP_Dev_ *devlist)
835{
836 struct UPNP_Dev_ *next;
837
838 while (devlist)
839 {
840 next = devlist->pNext;
841 GNUNET_free (devlist->control_url);
842 GNUNET_free (devlist->service_type);
843 GNUNET_free (devlist->desc_url);
844 GNUNET_free (devlist->data);
845 GNUNET_free (devlist->st);
846 GNUNET_free (devlist);
847 devlist = next;
848 }
849}
850
851/**
852 * Walk over the list of found devices looking for a connected IGD,
853 * if present, or at least a disconnected one.
854 */
855static void
856get_valid_igd (struct UPNP_discover_cls *cls)
857{
858 struct UPNP_Dev_ *dev;
859 int step;
860
861 /* No device was discovered */
862 if (!cls->dev_list)
863 {
864 cls->caller_cb (NULL, NULL, cls->caller_cls);
865
866 GNUNET_free (cls);
867 return;
868 }
869 /* We already walked over all devices, see what we got,
870 * and return the device with the best state we have. */
871 else if (cls->current_dev == NULL)
872 {
873 for (step = 1; step <= 3; step++)
874 {
875 for (dev = cls->dev_list; dev; dev = dev->pNext)
876 {
877#if DEBUG_UPNP
878 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
879 "Found device: control_url: %s, service_type: %s\n",
880 dev->control_url, dev->service_type);
881#endif
882 /* Accept connected IGDs on step 1, non-connected IGDs
883 * on step 2, and other device types on step 3. */
884 if ((step == 1 && dev->is_connected)
885 || (step < 3 && 0 != strcmp (dev->service_type,
886 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")))
887 continue;
888
889 cls->caller_cb (dev->control_url,
890 dev->service_type, cls->caller_cls);
891
892 free_dev_list (cls->dev_list);
893 GNUNET_free (cls);
894 return;
895 }
896 }
897
898 /* We cannot reach this... */
899 GNUNET_assert (GNUNET_NO);
900 }
901
902 /* There are still devices to ask, go on */
903 download_device_description (cls->sched, cls->current_dev->desc_url,
904 get_valid_igd_receive, cls);
905}
906
907static const char *const discover_type_list[] = {
908 "urn:schemas-upnp-org:device:InternetGatewayDevice:1",
909 "urn:schemas-upnp-org:service:WANIPConnection:1",
910 "urn:schemas-upnp-org:service:WANPPPConnection:1",
911 "upnp:rootdevice",
912 NULL
913};
914
915static void
916discover_send (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc);
917
918/**
919 * Handle response from device. Stop when all device types have been tried,
920 * and get their descriptions.
921 *
922 * @param data closure from UPNP_discover()
923 * @buf content of the reply
924 * @available number of bytes stored in buf
925 * @addr address of the sender
926 * @addrlen size of addr
927 * @param errCode value of errno
928 */
929static void
930discover_recv (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc)
931{
932 struct UPNP_discover_cls *cls = data;
933 GNUNET_SCHEDULER_TaskIdentifier task_w;
934 struct UPNP_Dev_ *tmp;
935 socklen_t addrlen;
936 ssize_t received;
937 char buf[DISCOVER_BUFSIZE];
938 const char *desc_url = NULL;
939 int urlsize = 0;
940 const char *st = NULL;
941 int stsize = 0;
942
943 /* Free fdset that was used for this sned/receive operation */
944 GNUNET_NETWORK_fdset_destroy (cls->fdset);
945
946 if (cls->multicast_addr->sa_family == AF_INET)
947 addrlen = sizeof (struct sockaddr_in);
948 else
949 addrlen = sizeof (struct sockaddr_in6);
950
951 errno = 0;
952 received =
953 GNUNET_NETWORK_socket_recvfrom (cls->sudp, &buf, DISCOVER_BUFSIZE - 1,
954 (struct sockaddr *) cls->multicast_addr,
955 &addrlen);
956 if (received == GNUNET_SYSERR)
957 {
958 PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_recvfrom");
959 }
960#if DEBUG_UPNP
961 else
962 {
963 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
964 "Received %d bytes from %s\n", received,
965 GNUNET_a2s (cls->multicast_addr, addrlen));
966 }
967#endif
968
969 parse_msearch_reply (buf, received, &desc_url, &urlsize, &st, &stsize);
970
971 if (st && desc_url)
972 {
973 tmp = (struct UPNP_Dev_ *) GNUNET_malloc (sizeof (struct UPNP_Dev_));
974 tmp->pNext = cls->dev_list;
975
976 tmp->desc_url = GNUNET_malloc (urlsize + 1);
977 strncpy (tmp->desc_url, desc_url, urlsize);
978 tmp->desc_url[urlsize] = '\0';
979
980 tmp->st = GNUNET_malloc (stsize + 1);
981 strncpy (tmp->st, st, stsize);
982 tmp->st[stsize] = '\0';
983 cls->dev_list = tmp;
984#if DEBUG_UPNP
985 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
986 "Found device %s when looking for type %s\n",
987 tmp->desc_url, tmp->st);
988#endif
989 }
990
991 /* Continue discovery until all types of devices have been tried */
992 if (discover_type_list[cls->type_index])
993 {
994 /* Send queries for each device type and wait for a possible reply.
995 * receiver callback takes care of trying another device type,
996 * and eventually calls the caller's callback. */
997 cls->fdset = GNUNET_NETWORK_fdset_create ();
998 GNUNET_NETWORK_fdset_zero (cls->fdset);
999 GNUNET_NETWORK_fdset_set (cls->fdset, cls->sudp);
1000
1001 task_w = GNUNET_SCHEDULER_add_select (cls->sched,
1002 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1003 GNUNET_SCHEDULER_NO_TASK,
1004 GNUNET_TIME_relative_multiply
1005 (GNUNET_TIME_UNIT_SECONDS, 15),
1006 NULL, cls->fdset, &discover_send,
1007 cls);
1008
1009 GNUNET_SCHEDULER_add_select (cls->sched,
1010 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1011 task_w,
1012 GNUNET_TIME_relative_multiply
1013 (GNUNET_TIME_UNIT_SECONDS, 5), cls->fdset,
1014 NULL, &discover_recv, cls);
1015 }
1016 else
1017 {
1018 GNUNET_NETWORK_socket_close (cls->sudp);
1019 GNUNET_free (cls->multicast_addr);
1020 cls->current_dev = cls->dev_list;
1021 get_valid_igd (cls);
1022 }
1023}
1024
1025/**
1026 * Send the SSDP M-SEARCH packet.
1027 *
1028 * @param data closure from UPNP_discover()
1029 * @param tc task context
1030 */
1031static void
1032discover_send (void *data, const struct GNUNET_SCHEDULER_TaskContext *tc)
1033{
1034 struct UPNP_discover_cls *cls = data;
1035 socklen_t addrlen;
1036 ssize_t n, sent;
1037 char buf[DISCOVER_BUFSIZE];
1038 static const char msearch_msg[] =
1039 "M-SEARCH * HTTP/1.1\r\n"
1040 "HOST: " UPNP_MCAST_ADDR ":" XSTR (PORT) "\r\n"
1041 "ST: %s\r\n" "MAN: \"ssdp:discover\"\r\n" "MX: 3\r\n" "\r\n";
1042
1043 if (cls->multicast_addr->sa_family == AF_INET)
1044 addrlen = sizeof (struct sockaddr_in);
1045 else
1046 addrlen = sizeof (struct sockaddr_in6);
1047
1048 n =
1049 snprintf (buf, DISCOVER_BUFSIZE, msearch_msg,
1050 discover_type_list[cls->type_index++]);
1051
1052 errno = 0;
1053 sent = GNUNET_NETWORK_socket_sendto (cls->sudp, buf, n,
1054 (struct sockaddr *)
1055 cls->multicast_addr, addrlen);
1056 if (sent == GNUNET_SYSERR)
1057 {
1058 PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_sendto");
1059 }
1060 else if (sent < n)
1061 {
1062 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
1063 "Could only send %d bytes to %s, needed %d bytes\n",
1064 sent, GNUNET_a2s (cls->multicast_addr, addrlen), n);
1065 }
1066#if DEBUG_UPNP
1067 else
1068 {
1069 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
1070 "Sent %d bytes to %s\n", sent,
1071 GNUNET_a2s (cls->multicast_addr, addrlen));
1072 }
1073#endif
1074}
1075
1076/**
1077 * Search for UPnP Internet Gateway Devices (IGD) on a given network interface.
1078 * If several devices are found, a device that is connected to the WAN
1079 * is returned first (if any).
1080 *
1081 * @param sched scheduler to use for network tasks
1082 * @param multicastif network interface to send discovery messages, or NULL
1083 * @param addr address used to send messages on multicastif, or NULL
1084 * @param caller_cb user function to call when done
1085 * @param caller_cls closure to pass to caller_cb
1086 */
1087void
1088UPNP_discover_ (struct GNUNET_SCHEDULER_Handle *sched,
1089 const char *multicastif,
1090 const struct sockaddr *addr,
1091 UPNP_discover_cb_ caller_cb, void *caller_cls)
1092{
1093 int opt = 1;
1094 int domain = PF_INET;
1095 int if_index;
1096 struct in6_addr any_addr = IN6ADDR_ANY_INIT;
1097 struct sockaddr_in sockudp_r, sockudp_w;
1098 struct sockaddr_in6 sockudp6_r, sockudp6_w;
1099 GNUNET_SCHEDULER_TaskIdentifier task_w;
1100 struct GNUNET_NETWORK_Handle *sudp;
1101 struct UPNP_discover_cls *cls;
1102
1103
1104 if (addr && addr->sa_family == AF_INET)
1105 {
1106 domain = PF_INET;
1107 }
1108 else if (addr && addr->sa_family == AF_INET6)
1109 {
1110 domain = PF_INET6;
1111 }
1112 else if (addr)
1113 {
1114 GNUNET_break (0);
1115 caller_cb (NULL, NULL, caller_cls);
1116 return;
1117 }
1118
1119 errno = 0;
1120 sudp = GNUNET_NETWORK_socket_create (domain, SOCK_DGRAM, 0);
1121
1122 if (sudp == NULL)
1123 {
1124 PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_create");
1125 caller_cb (NULL, NULL, caller_cls);
1126 return;
1127 }
1128
1129
1130 cls = GNUNET_malloc (sizeof (struct UPNP_discover_cls));
1131 cls->sched = sched;
1132 cls->sudp = sudp;
1133 cls->type_index = 0;
1134 cls->dev_list = NULL;
1135 cls->current_dev = NULL;
1136 cls->caller_cb = caller_cb;
1137 cls->caller_cls = caller_cls;
1138
1139
1140 if (domain == PF_INET)
1141 {
1142 /* receive */
1143 memset (&sockudp_r, 0, sizeof (struct sockaddr_in));
1144 sockudp_r.sin_family = AF_INET;
1145#ifdef HAVE_SOCKADDR_IN_SIN_LEN
1146 sockudp_r.sin_len = sizeof (struct sockaddr_in);
1147#endif
1148 sockudp_r.sin_port = 0;
1149 sockudp_r.sin_addr.s_addr = INADDR_ANY;
1150
1151 /* send */
1152 memset (&sockudp_w, 0, sizeof (struct sockaddr_in));
1153 sockudp_w.sin_family = AF_INET;
1154 sockudp_w.sin_port = htons (PORT);
1155 sockudp_w.sin_addr.s_addr = inet_addr (UPNP_MCAST_ADDR);
1156#ifdef HAVE_SOCKADDR_IN_SIN_LEN
1157 sockudp_w.sin_len = sizeof (struct sockaddr_in);
1158#endif
1159
1160 cls->multicast_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
1161 memcpy (cls->multicast_addr, &sockudp_w, sizeof (struct sockaddr_in));
1162 }
1163 else
1164 {
1165 /* receive */
1166 memcpy (&sockudp6_r, addr, sizeof (struct sockaddr_in6));
1167 sockudp6_r.sin6_port = 0;
1168 sockudp6_r.sin6_addr = any_addr;
1169#ifdef HAVE_SOCKADDR_IN_SIN_LEN
1170 sockudp6_r.sin6_len = sizeof (struct sockaddr_in6);
1171#endif
1172
1173 /* send */
1174 memset (&sockudp6_w, 0, sizeof (struct sockaddr_in6));
1175 sockudp6_w.sin6_family = AF_INET6;
1176 sockudp6_w.sin6_port = htons (PORT);
1177 if (inet_pton (AF_INET6, UPNP_MCAST_ADDR6, &sockudp6_w.sin6_addr) != 1)
1178 {
1179 PRINT_SOCKET_ERROR ("inet_pton");
1180 caller_cb (NULL, NULL, caller_cls);
1181 return;
1182 }
1183#ifdef HAVE_SOCKADDR_IN_SIN_LEN
1184 sockudp6_w.sin6_len = sizeof (struct sockaddr_in6);
1185#endif
1186
1187 cls->multicast_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
1188 memcpy (cls->multicast_addr, &sockudp6_w, sizeof (struct sockaddr_in6));
1189 }
1190
1191 if (GNUNET_NETWORK_socket_setsockopt
1192 (sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) == GNUNET_SYSERR)
1193 {
1194 PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_setsockopt");
1195 GNUNET_NETWORK_socket_close (sudp);
1196 caller_cb (NULL, NULL, caller_cls);
1197 return;
1198 }
1199
1200 if (addr)
1201 {
1202 if (domain == PF_INET)
1203 {
1204 sockudp_r.sin_addr.s_addr =
1205 ((struct sockaddr_in *) addr)->sin_addr.s_addr;
1206 if (GNUNET_NETWORK_socket_setsockopt
1207 (sudp, IPPROTO_IP, IP_MULTICAST_IF,
1208 (const char *) &sockudp_r.sin_addr,
1209 sizeof (struct in_addr)) == GNUNET_SYSERR)
1210 {
1211 PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_setsockopt");
1212 }
1213 }
1214 else
1215 {
1216 if (multicastif)
1217 {
1218 if_index = if_nametoindex (multicastif);
1219 if (!if_index)
1220 PRINT_SOCKET_ERROR ("if_nametoindex");
1221
1222 if (GNUNET_NETWORK_socket_setsockopt
1223 (sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &if_index,
1224 sizeof (if_index)) == GNUNET_SYSERR)
1225 {
1226 PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_setsockopt");
1227 }
1228 }
1229
1230 memcpy (&sockudp6_r.sin6_addr,
1231 &((struct sockaddr_in6 *) addr)->sin6_addr,
1232 sizeof (sockudp6_r.sin6_addr));
1233 }
1234 }
1235
1236 if (domain == PF_INET)
1237 {
1238 /* Bind to receive response before sending packet */
1239 if (GNUNET_NETWORK_socket_bind
1240 (sudp, (struct sockaddr *) &sockudp_r,
1241 sizeof (struct sockaddr_in)) != GNUNET_OK)
1242 {
1243 PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_bind");
1244 GNUNET_NETWORK_socket_close (sudp);
1245 GNUNET_free (cls->multicast_addr);
1246 caller_cb (NULL, NULL, caller_cls);
1247 return;
1248 }
1249 }
1250 else
1251 {
1252 /* Bind to receive response before sending packet */
1253 if (GNUNET_NETWORK_socket_bind
1254 (sudp, (struct sockaddr *) &sockudp6_r,
1255 sizeof (struct sockaddr_in6)) != GNUNET_OK)
1256 {
1257 PRINT_SOCKET_ERROR ("GNUNET_NETWORK_socket_bind");
1258 GNUNET_free (cls->multicast_addr);
1259 GNUNET_NETWORK_socket_close (sudp);
1260 caller_cb (NULL, NULL, caller_cls);
1261 return;
1262 }
1263 }
1264
1265 /* Send queries for each device type and wait for a possible reply.
1266 * receiver callback takes care of trying another device type,
1267 * and eventually calls the caller's callback. */
1268 cls->fdset = GNUNET_NETWORK_fdset_create ();
1269 GNUNET_NETWORK_fdset_zero (cls->fdset);
1270 GNUNET_NETWORK_fdset_set (cls->fdset, sudp);
1271
1272 task_w = GNUNET_SCHEDULER_add_select (sched,
1273 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1274 GNUNET_SCHEDULER_NO_TASK,
1275 GNUNET_TIME_relative_multiply
1276 (GNUNET_TIME_UNIT_SECONDS, 15), NULL,
1277 cls->fdset, &discover_send, cls);
1278
1279 GNUNET_SCHEDULER_add_select (sched,
1280 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1281 task_w,
1282 GNUNET_TIME_relative_multiply
1283 (GNUNET_TIME_UNIT_SECONDS, 15), cls->fdset,
1284 NULL, &discover_recv, cls);
1285}
diff --git a/src/nat/upnp-discover.h b/src/nat/upnp-discover.h
new file mode 100644
index 000000000..2e996cd88
--- /dev/null
+++ b/src/nat/upnp-discover.h
@@ -0,0 +1,84 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010 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 3, 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 * Code in this file is originally based on the miniupnp library.
23 * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
24 *
25 * Original licence:
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are met:
29 *
30 * * Redistributions of source code must retain the above copyright notice,
31 * this list of conditions and the following disclaimer.
32 * * Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 * * The name of the author may not be used to endorse or promote products
36 * derived from this software without specific prior written permission.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
42 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
43 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
44 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
48 * POSSIBILITY OF SUCH DAMAGE.
49 */
50
51/**
52 * @file nat/upnp-discover.h
53 * @brief Look for UPnP IGD devices
54 *
55 * @author Milan Bouchet-Valat
56 */
57#ifndef UPNPC_H
58#define UPNPC_H
59
60#include "platform.h"
61#include "gnunet_common.h"
62#include "gnunet_util_lib.h"
63#include "upnp-commands.h"
64
65typedef void (*UPNP_discover_cb_) (const char *control_urls,
66 const char *service_type, void *cls);
67
68/**
69 * Search for UPnP Internet Gateway Devices (IGD) on a given network interface.
70 * If several devices are found, a device that is connected to the WAN
71 * is returned first (if any).
72 *
73 * @param sched scheduler to use for network tasks
74 * @param multicastif network interface to send discovery messages, or NULL
75 * @param addr address used to send messages on multicastif, or NULL
76 * @param caller_cb user function to call when done
77 * @param caller_cls closure to pass to caller_cb
78 */
79void UPNP_discover_ (struct GNUNET_SCHEDULER_Handle *sched,
80 const char *multicastif,
81 const struct sockaddr *addr,
82 UPNP_discover_cb_ caller_cb, void *caller_cls);
83
84#endif
diff --git a/src/nat/upnp-igd-parse.c b/src/nat/upnp-igd-parse.c
new file mode 100644
index 000000000..0812065ed
--- /dev/null
+++ b/src/nat/upnp-igd-parse.c
@@ -0,0 +1,207 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010 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 3, 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 * Code in this file is originally based on the miniupnp library.
23 * Copyright (c) 2005-2008, Thomas BERNARD. All rights reserved.
24 *
25 * Original licence:
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are met:
29 *
30 * * Redistributions of source code must retain the above copyright notice,
31 * this list of conditions and the following disclaimer.
32 * * Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 * * The name of the author may not be used to endorse or promote products
36 * derived from this software without specific prior written permission.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
42 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
43 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
44 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
48 * POSSIBILITY OF SUCH DAMAGE.
49 */
50
51/**
52 * @file nat/upnp-igd-parse.h
53 * @brief Parser for XML descriptions of UPnP Internet Gateway Devices
54 *
55 * @author Milan Bouchet-Valat
56 */
57#include <stdio.h>
58#include <string.h>
59
60#include "platform.h"
61#include "gnunet_util_lib.h"
62#include "upnp-minixml.h"
63#include "upnp-igd-parse.h"
64
65/**
66 * Start element handler: update nesting level counter
67 * and copy element name.
68 */
69static void
70start_elt (void *d, const char *name, int l)
71{
72 struct UPNP_IGD_Data_ *datas = (struct UPNP_IGD_Data_ *) d;
73
74 memcpy (datas->cur_elt_name, name, l);
75 datas->cur_elt_name[l] = '\0';
76 datas->level++;
77 if ((l == 7) && !memcmp (name, "service", l))
78 {
79 datas->control_url_tmp[0] = '\0';
80 datas->event_sub_url_tmp[0] = '\0';
81 datas->scpd_url_tmp[0] = '\0';
82 datas->service_type_tmp[0] = '\0';
83 }
84}
85
86/**
87 * End element handler: update nesting level counter
88 * and update parser state if service element is parsed.
89 */
90static void
91end_elt (void *d, const char *name, int l)
92{
93 struct UPNP_IGD_Data_ *datas = (struct UPNP_IGD_Data_ *) d;
94
95 datas->level--;
96
97 if ((l == 7) && !memcmp (name, "service", l))
98 {
99 if (0 == strcmp (datas->service_type_tmp,
100 "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1"))
101 {
102 memcpy (datas->control_url_CIF, datas->control_url_tmp,
103 MINIUPNPC_URL_MAXSIZE);
104 memcpy (datas->event_sub_url_CIF, datas->event_sub_url_tmp,
105 MINIUPNPC_URL_MAXSIZE);
106 memcpy (datas->scpd_url_CIF, datas->scpd_url_tmp,
107 MINIUPNPC_URL_MAXSIZE);
108 memcpy (datas->service_type_CIF, datas->service_type_tmp,
109 MINIUPNPC_URL_MAXSIZE);
110 }
111 else if (0 == strcmp (datas->service_type_tmp,
112 "urn:schemas-upnp-org:service:WANIPConnection:1")
113 || 0 == strcmp (datas->service_type_tmp,
114 "urn:schemas-upnp-org:service:WANPPPConnection:1"))
115 {
116 memcpy (datas->control_url, datas->control_url_tmp,
117 MINIUPNPC_URL_MAXSIZE);
118 memcpy (datas->event_sub_url, datas->event_sub_url_tmp,
119 MINIUPNPC_URL_MAXSIZE);
120 memcpy (datas->scpd_url, datas->scpd_url_tmp,
121 MINIUPNPC_URL_MAXSIZE);
122 memcpy (datas->service_type, datas->service_type_tmp,
123 MINIUPNPC_URL_MAXSIZE);
124 }
125 }
126}
127
128/**
129 * Data handler: copy data depending on the current
130 * element name and state.
131 */
132static void
133IGDdata (void *d, const char *data, int l)
134{
135 struct UPNP_IGD_Data_ *datas = (struct UPNP_IGD_Data_ *) d;
136 char *dstmember = NULL;
137
138 if (!strcmp (datas->cur_elt_name, "URLBase"))
139 dstmember = datas->base_url;
140 else if (!strcmp (datas->cur_elt_name, "serviceType"))
141 dstmember = datas->service_type_tmp;
142 else if (!strcmp (datas->cur_elt_name, "controlURL"))
143 dstmember = datas->control_url_tmp;
144 else if (!strcmp (datas->cur_elt_name, "eventSubURL"))
145 dstmember = datas->event_sub_url_tmp;
146 else if (!strcmp (datas->cur_elt_name, "SCPDURL"))
147 dstmember = datas->scpd_url_tmp;
148
149 /* Copy current element name into destination member */
150 if (dstmember)
151 {
152 if (l >= MINIUPNPC_URL_MAXSIZE)
153 l = MINIUPNPC_URL_MAXSIZE - 1;
154
155 memcpy (dstmember, data, l);
156 dstmember[l] = '\0';
157 }
158}
159
160#ifdef DEBUG_UPNP
161static void
162print_IGD (struct UPNP_IGD_Data_ *d)
163{
164 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
165 "base_url = %s\n", d->base_url);
166 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
167 "WAN Device (Common interface config) :\n"
168 " sevice_type = %s\n"
169 " control_url = %s\n"
170 " event_sub_url = %s\n"
171 " scpd_url = %s\n",
172 d->service_type_CIF,
173 d->control_url_CIF, d->event_sub_url_CIF, d->scpd_url_CIF);
174 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
175 "WAN Connection Device (IP or PPP Connection):\n"
176 " service_type = %s\n"
177 " control_url = %s\n"
178 " event_sub_url = %s\n"
179 " scpd_url = %s\n",
180 d->service_type,
181 d->control_url, d->event_sub_url, d->scpd_url);
182}
183#endif
184
185/**
186 * Parse XML description of an IGD device into a UPNP_IGD_Data_ struct.
187 */
188void
189UPNP_IGD_parse_desc_ (const char *buffer, int buf_size,
190 struct UPNP_IGD_Data_ *data)
191{
192 struct UPNP_xml_parser_ parser;
193
194 parser.xml_start = buffer;
195 parser.xml_size = buf_size;
196 parser.cls = data;
197 parser.start_elt_func = start_elt;
198 parser.end_elt_func = end_elt;
199 parser.data_func = IGDdata;
200 parser.att_func = 0;
201
202 UPNP_parse_xml_ (&parser);
203
204#ifdef DEBUG_UPNP
205 print_IGD (data);
206#endif
207}
diff --git a/src/nat/upnp-igd-parse.h b/src/nat/upnp-igd-parse.h
new file mode 100644
index 000000000..8e0b8510c
--- /dev/null
+++ b/src/nat/upnp-igd-parse.h
@@ -0,0 +1,99 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010 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 3, 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 * Code in this file is originally based on the miniupnp library.
23 * Copyright (c) 2005-2008, Thomas BERNARD. All rights reserved.
24 *
25 * Original licence:
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are met:
29 *
30 * * Redistributions of source code must retain the above copyright notice,
31 * this list of conditions and the following disclaimer.
32 * * Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 * * The name of the author may not be used to endorse or promote products
36 * derived from this software without specific prior written permission.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
42 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
43 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
44 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
48 * POSSIBILITY OF SUCH DAMAGE.
49 */
50
51/**
52 * @file nat/upnp-igd-parse.h
53 * @brief Parser for XML descriptions of UPnP Internet Gateway Devices
54 *
55 * @author Milan Bouchet-Valat
56 */
57#ifndef UPNP_IGD_PARSE_H
58#define UPNP_IGD_PARSE_H
59
60#define MINIUPNPC_URL_MAXSIZE (128)
61
62/**
63 * Structure to store the result of the parsing of UPnP
64 * descriptions of Internet Gateway Devices.
65 */
66struct UPNP_IGD_Data_
67{
68 char cur_elt_name[MINIUPNPC_URL_MAXSIZE];
69 char base_url[MINIUPNPC_URL_MAXSIZE];
70 int level;
71
72 /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
73 char control_url_CIF[MINIUPNPC_URL_MAXSIZE];
74 char event_sub_url_CIF[MINIUPNPC_URL_MAXSIZE];
75 char scpd_url_CIF[MINIUPNPC_URL_MAXSIZE];
76 char service_type_CIF[MINIUPNPC_URL_MAXSIZE];
77
78 /* "urn:schemas-upnp-org:service:WANIPConnection:1"
79 * "urn:schemas-upnp-org:service:WANPPPConnection:1" */
80 char control_url[MINIUPNPC_URL_MAXSIZE];
81 char event_sub_url[MINIUPNPC_URL_MAXSIZE];
82 char scpd_url[MINIUPNPC_URL_MAXSIZE];
83 char service_type[MINIUPNPC_URL_MAXSIZE];
84
85 /* Used temporarily by the parser */
86 char control_url_tmp[MINIUPNPC_URL_MAXSIZE];
87 char event_sub_url_tmp[MINIUPNPC_URL_MAXSIZE];
88 char scpd_url_tmp[MINIUPNPC_URL_MAXSIZE];
89 char service_type_tmp[MINIUPNPC_URL_MAXSIZE];
90};
91
92/**
93 * Parse UPnP IGD XML description to a UPNP_IGD_Data_ structure.
94 */
95void
96UPNP_IGD_parse_desc_ (const char *buffer, int buf_size,
97 struct UPNP_IGD_Data_ *data);
98
99#endif
diff --git a/src/nat/upnp-minixml.c b/src/nat/upnp-minixml.c
new file mode 100644
index 000000000..9f4bd735d
--- /dev/null
+++ b/src/nat/upnp-minixml.c
@@ -0,0 +1,239 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010 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 3, 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 * Code in this file is originally based on the miniupnp library.
23 * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
24 *
25 * Original licence:
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are met:
29 *
30 * * Redistributions of source code must retain the above copyright notice,
31 * this list of conditions and the following disclaimer.
32 * * Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 * * The name of the author may not be used to endorse or promote products
36 * derived from this software without specific prior written permission.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
42 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
43 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
44 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
48 * POSSIBILITY OF SUCH DAMAGE.
49 */
50
51/**
52 * @file nat/minixml.c
53 * @brief Simple XML parser used by UPnP
54 *
55 * @author Milan Bouchet-Valat
56 */
57
58#include "platform.h"
59#include "gnunet_util_lib.h"
60#include "upnp-minixml.h"
61
62/**
63 * Used to parse the argument list.
64 *
65 * @returns GNUNET_OK on success, or GNUNET_SYSERR if the end
66 * of the xmlbuffer is reached
67 */
68static int
69parse_att (struct UPNP_xml_parser_ *p)
70{
71 const char *att_name;
72 int att_name_len;
73 const char *att_value;
74 int att_value_len;
75 while (p->xml < p->xml_end)
76 {
77 if (*p->xml == '/' || *p->xml == '>')
78 return GNUNET_OK;
79 if (!IS_WHITE_SPACE (*p->xml))
80 {
81 char sep;
82 att_name = p->xml;
83 att_name_len = 0;
84 while (*p->xml != '=' && !IS_WHITE_SPACE (*p->xml))
85 {
86 att_name_len++;
87 p->xml++;
88 if (p->xml >= p->xml_end)
89 return GNUNET_SYSERR;
90 }
91 while (*(p->xml++) != '=')
92 {
93 if (p->xml >= p->xml_end)
94 return GNUNET_SYSERR;
95 }
96 while (IS_WHITE_SPACE (*p->xml))
97 {
98 p->xml++;
99 if (p->xml >= p->xml_end)
100 return GNUNET_SYSERR;
101 }
102 sep = *p->xml;
103 if (sep == '\'' || sep == '\"')
104 {
105 p->xml++;
106 if (p->xml >= p->xml_end)
107 return GNUNET_SYSERR;
108 att_value = p->xml;
109 att_value_len = 0;
110 while (*p->xml != sep)
111 {
112 att_value_len++;
113 p->xml++;
114 if (p->xml >= p->xml_end)
115 return GNUNET_SYSERR;
116 }
117 }
118 else
119 {
120 att_value = p->xml;
121 att_value_len = 0;
122 while (!IS_WHITE_SPACE (*p->xml)
123 && *p->xml != '>' && *p->xml != '/')
124 {
125 att_value_len++;
126 p->xml++;
127 if (p->xml >= p->xml_end)
128 return GNUNET_SYSERR;
129 }
130 }
131
132 if (p->att_func)
133 p->att_func (p->cls, att_name, att_name_len, att_value,
134 att_value_len);
135 }
136 p->xml++;
137 }
138 return GNUNET_SYSERR;
139}
140
141/**
142 * Parse the xml stream and call the callback
143 * functions when needed...
144 */
145void
146parse_elt (struct UPNP_xml_parser_ *p)
147{
148 int i;
149 const char *element_name;
150 while (p->xml < (p->xml_end - 1))
151 {
152 /* Element name */
153 if ((p->xml)[0] == '<' && (p->xml)[1] != '?')
154 {
155 i = 0;
156 element_name = ++p->xml;
157 while (!IS_WHITE_SPACE (*p->xml)
158 && (*p->xml != '>') && (*p->xml != '/'))
159 {
160 i++;
161 p->xml++;
162 if (p->xml >= p->xml_end)
163 return;
164 /* to ignore namespace : */
165 if (*p->xml == ':')
166 {
167 i = 0;
168 element_name = ++p->xml;
169 }
170 }
171
172 /* Start of element */
173 if (i > 0)
174 {
175 if (p->start_elt_func)
176 p->start_elt_func (p->cls, element_name, i);
177 if (parse_att (p) != GNUNET_OK)
178 return;
179 if (*p->xml != '/')
180 {
181 const char *data;
182 i = 0;
183 data = ++p->xml;
184 if (p->xml >= p->xml_end)
185 return;
186 while (IS_WHITE_SPACE (*p->xml))
187 {
188 p->xml++;
189 if (p->xml >= p->xml_end)
190 return;
191 }
192 while (*p->xml != '<')
193 {
194 i++;
195 p->xml++;
196 if (p->xml >= p->xml_end)
197 return;
198 }
199 if (i > 0 && p->data_func)
200 p->data_func (p->cls, data, i);
201 }
202 }
203 /* End of element */
204 else if (*p->xml == '/')
205 {
206 i = 0;
207 element_name = ++p->xml;
208 if (p->xml >= p->xml_end)
209 return;
210 while ((*p->xml != '>'))
211 {
212 i++;
213 p->xml++;
214 if (p->xml >= p->xml_end)
215 return;
216 }
217 if (p->end_elt_func)
218 p->end_elt_func (p->cls, element_name, i);
219 p->xml++;
220 }
221 }
222 else
223 {
224 p->xml++;
225 }
226 }
227}
228
229/**
230 * Parse XML content according to the values stored in the parser struct.
231 * The parser must be initialized before calling this function
232 */
233void
234UPNP_parse_xml_ (struct UPNP_xml_parser_ *parser)
235{
236 parser->xml = parser->xml_start;
237 parser->xml_end = parser->xml_start + parser->xml_size;
238 parse_elt (parser);
239}
diff --git a/src/nat/upnp-minixml.h b/src/nat/upnp-minixml.h
new file mode 100644
index 000000000..07cf70939
--- /dev/null
+++ b/src/nat/upnp-minixml.h
@@ -0,0 +1,131 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010 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 3, 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 * Code in this file is originally based on the miniupnp library.
23 * Copyright (c) 2005-2008, Thomas BERNARD. All rights reserved.
24 *
25 * Original licence:
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are met:
29 *
30 * * Redistributions of source code must retain the above copyright notice,
31 * this list of conditions and the following disclaimer.
32 * * Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 * * The name of the author may not be used to endorse or promote products
36 * derived from this software without specific prior written permission.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
42 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
43 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
44 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
48 * POSSIBILITY OF SUCH DAMAGE.
49 */
50
51/**
52 * @file nat/upnp-minixml.h
53 * @brief Simple XML parser used by UPnP
54 *
55 * @author Milan Bouchet-Valat
56 */
57
58#ifndef MINIXML_H
59#define MINIXML_H
60
61#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
62
63/**
64 * Structure describing the contents and methods that should be
65 * used when running parse_xml();
66 *
67 * If a callback function pointer is set to NULL, the function
68 * is not called */
69struct UPNP_xml_parser_
70{
71 /**
72 * Pointer to the XML data to parse
73 */
74 const char *xml_start;
75
76 /**
77 * Pointer to the last character to parse (optional)
78 */
79 const char *xml_end;
80
81 /**
82 * Size of the data stored at xml_start
83 */
84 int xml_size;
85
86 /**
87 * Pointer to current character (private)
88 */
89 const char *xml;
90
91 /**
92 * Closure for user-provided callback functions
93 */
94 void *cls;
95
96 /**
97 * User function called when reaching the start of an XML element.
98 */
99 void (*start_elt_func) (void *cls, const char *elt, int elt_len);
100
101 /**
102 * User function called when reaching the end of an XML element.
103 */
104 void (*end_elt_func) (void *cls, const char *elt, int elt_len);
105
106 /**
107 * User function called when an XML element data is found.
108 */
109 void (*data_func) (void *cls, const char *data, int data_len);
110
111 /**
112 * User function called for every XML element attribute.
113 */
114 void (*att_func) (void *cls, const char *att_name, int att_name_len,
115 const char *att_value, int att_value_len);
116};
117
118/**
119 * Parse data provided to the xml_parser structure, using
120 * user-provided functions.
121 *
122 * The xmlparser structure must be initialized before the call;
123 * the following structure members have to be set:
124 * xml_start, xml_size, cls, *func.
125 * The xml member is for internal usage, xml_end is computed
126 * automatically.
127 *
128 * @param parser the structure used for parsing */
129void UPNP_parse_xml_ (struct UPNP_xml_parser_ *parser);
130
131#endif
diff --git a/src/nat/upnp-reply-parse.c b/src/nat/upnp-reply-parse.c
new file mode 100644
index 000000000..398cde834
--- /dev/null
+++ b/src/nat/upnp-reply-parse.c
@@ -0,0 +1,166 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010 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 3, 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 * Code in this file is originally inspired by the miniupnp library.
23 * Copyright (c) 2006, Thomas BERNARD. All rights reserved.
24 *
25 * Original licence:
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are met:
29 *
30 * * Redistributions of source code must retain the above copyright notice,
31 * this list of conditions and the following disclaimer.
32 * * Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 * * The name of the author may not be used to endorse or promote products
36 * derived from this software without specific prior written permission.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
42 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
43 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
44 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
48 * POSSIBILITY OF SUCH DAMAGE.
49 */
50
51/**
52 * @file nat/upnp-reply-parse.c
53 * @brief Parser for XML replies to UPnP commands
54 *
55 * @author Milan Bouchet-Valat
56 */
57
58#include <stdlib.h>
59#include <string.h>
60#include <stdio.h>
61
62#include "platform.h"
63#include "gnunet_util_lib.h"
64#include "upnp-minixml.h"
65#include "upnp-reply-parse.h"
66
67static void
68start_elt (void *d, const char *name, int l)
69{
70 struct UPNP_REPLY_NameValueList_ *data =
71 (struct UPNP_REPLY_NameValueList_ *) d;
72
73 if (l > 63)
74 l = 63;
75
76 memcpy (data->curelt, name, l);
77 data->curelt[l] = '\0';
78}
79
80static void
81get_data (void *d, const char *datas, int l)
82{
83 struct UPNP_REPLY_NameValueList_ *data =
84 (struct UPNP_REPLY_NameValueList_ *) d;
85 struct UPNP_REPLY_NameValue_ *nv;
86
87 nv = malloc (sizeof (struct UPNP_REPLY_NameValue_));
88
89 if (l > 63)
90 l = 63;
91
92 strncpy (nv->name, data->curelt, 64);
93 nv->name[63] = '\0';
94 memcpy (nv->value, datas, l);
95 nv->value[l] = '\0';
96
97 LIST_INSERT_HEAD (&(data->head), nv, entries);
98}
99
100void
101UPNP_REPLY_parse_ (const char *buffer, int buf_size,
102 struct UPNP_REPLY_NameValueList_ *data)
103{
104 struct UPNP_xml_parser_ parser;
105
106 LIST_INIT (&(data->head));
107
108 /* Init xml_parser object */
109 parser.xml_start = buffer;
110 parser.xml_size = buf_size;
111 parser.cls = data;
112 parser.start_elt_func = start_elt;
113 parser.end_elt_func = 0;
114 parser.data_func = get_data;
115 parser.att_func = 0;
116
117 UPNP_parse_xml_ (&parser);
118}
119
120void
121UPNP_REPLY_free_ (struct UPNP_REPLY_NameValueList_ *pdata)
122{
123 struct UPNP_REPLY_NameValue_ *nv;
124
125 while ((nv = pdata->head.lh_first) != NULL)
126 {
127 LIST_REMOVE (nv, entries);
128 GNUNET_free (nv);
129 }
130}
131
132char *
133UPNP_REPLY_get_value_ (struct UPNP_REPLY_NameValueList_ *pdata,
134 const char *Name)
135{
136 struct UPNP_REPLY_NameValue_ *nv;
137 char *p = NULL;
138
139 for (nv = pdata->head.lh_first;
140 (nv != NULL) && (p == NULL); nv = nv->entries.le_next)
141 {
142 if (strcmp (nv->name, Name) == 0)
143 p = nv->value;
144 }
145
146 return p;
147}
148
149#if DEBUG_UPNP
150void
151UPNP_REPLY_print_ (char *buffer, int buf_size)
152{
153 struct UPNP_REPLY_NameValueList_ pdata;
154 struct UPNP_REPLY_NameValue_ *nv;
155
156 UPNP_REPLY_parse_ (buffer, buf_size, &pdata);
157
158 for (nv = pdata.head.lh_first; nv != NULL; nv = nv->entries.le_next)
159 {
160 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "UPnP",
161 "%s = %s", nv->name, nv->value);
162 }
163
164 UPNP_REPLY_free_ (&pdata);
165}
166#endif
diff --git a/src/nat/upnp-reply-parse.h b/src/nat/upnp-reply-parse.h
new file mode 100644
index 000000000..e5bedbd8f
--- /dev/null
+++ b/src/nat/upnp-reply-parse.h
@@ -0,0 +1,107 @@
1/*
2 This file is part of GNUnet.
3 (C) 2009, 2010 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 3, 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 * Code in this file is originally inspired by the miniupnp library.
23 * Copyright (c) 2005-2009, Thomas BERNARD. All rights reserved.
24 *
25 * Original licence:
26 *
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions are met:
29 *
30 * * Redistributions of source code must retain the above copyright notice,
31 * this list of conditions and the following disclaimer.
32 * * Redistributions in binary form must reproduce the above copyright notice,
33 * this list of conditions and the following disclaimer in the documentation
34 * and/or other materials provided with the distribution.
35 * * The name of the author may not be used to endorse or promote products
36 * derived from this software without specific prior written permission.
37 *
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
39 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
40 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
41 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
42 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
43 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
44 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
45 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
46 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
47 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
48 * POSSIBILITY OF SUCH DAMAGE.
49 */
50
51/**
52 * @file nat/upnp-reply-parse.h
53 * @brief Parser for XML replies to UPnP commands
54 *
55 * @author Milan Bouchet-Valat
56 */
57
58#ifndef UPNP_PARSE_REPLY_H
59#define UPNP_PARSE_REPLY_H
60
61#include "bsdqueue.h"
62
63 /**
64 * Name-value pair used by UPNP_REPLY_NameValueList.
65 */
66struct UPNP_REPLY_NameValue_
67{
68 LIST_ENTRY (UPNP_REPLY_NameValue_) entries;
69 char name[64];
70 char value[64];
71};
72
73 /**
74 * Name-value list to store data parsed from a UPnP reply.
75 */
76struct UPNP_REPLY_NameValueList_
77{
78 LIST_HEAD (listhead, UPNP_REPLY_NameValue_) head;
79 char curelt[64];
80};
81
82 /**
83 * Parse UPnP XML reply to a name-value list.
84 */
85void
86UPNP_REPLY_parse_ (const char *buffer, int buf_size,
87 struct UPNP_REPLY_NameValueList_ *data);
88
89 /**
90 * Free name-value list obtained using UPNP_REPLY_parse().
91 */
92void UPNP_REPLY_free_ (struct UPNP_REPLY_NameValueList_ *pdata);
93
94 /**
95 * Get value corresponding to name from a name-value list.
96 */
97char *UPNP_REPLY_get_value_ (struct UPNP_REPLY_NameValueList_ *pdata,
98 const char *name);
99
100#if DEBUG_UPNP
101 /**
102 * Parse a UPnP XMl reply and print the result as names-value pairs.
103 */
104void UPNP_REPLY_print_ (char *buffer, int buf_size);
105#endif
106
107#endif
diff --git a/src/nat/upnp.c b/src/nat/upnp.c
index 84abe339d..e383055b5 100644
--- a/src/nat/upnp.c
+++ b/src/nat/upnp.c
@@ -35,12 +35,12 @@
35#include <errno.h> 35#include <errno.h>
36#include <string.h> 36#include <string.h>
37 37
38#include <miniupnp/miniupnpc.h>
39#include <miniupnp/upnpcommands.h>
40
41#include "platform.h" 38#include "platform.h"
42#include "gnunet_common.h" 39#include "gnunet_common.h"
43#include "gnunet_nat_lib.h" 40#include "gnunet_nat_lib.h"
41#include "nat.h"
42#include "upnp-discover.h"
43#include "upnp-commands.h"
44#include "upnp.h" 44#include "upnp.h"
45 45
46/* Component name for logging */ 46/* Component name for logging */
@@ -57,9 +57,10 @@ enum UPNP_State
57 57
58struct GNUNET_NAT_UPNP_Handle 58struct GNUNET_NAT_UPNP_Handle
59{ 59{
60 struct GNUNET_SCHEDULER_Handle *sched;
60 int hasDiscovered; 61 int hasDiscovered;
61 struct UPNPUrls urls; 62 char *control_url;
62 struct IGDdatas data; 63 char *service_type;
63 int port; 64 int port;
64 const struct sockaddr *addr; 65 const struct sockaddr *addr;
65 socklen_t addrlen; 66 socklen_t addrlen;
@@ -67,20 +68,21 @@ struct GNUNET_NAT_UPNP_Handle
67 enum UPNP_State state; 68 enum UPNP_State state;
68 struct sockaddr *ext_addr; 69 struct sockaddr *ext_addr;
69 const char *iface; 70 const char *iface;
71 int processing;
72 GNUNET_NAT_UPNP_pulse_cb pulse_cb;
73 void *pulse_cls;
70}; 74};
71 75
72static int 76static int
73process_if (void *cls, 77process_if (void *cls,
74 const char *name, 78 const char *name,
75 int isDefault, 79 int isDefault, const struct sockaddr *addr, socklen_t addrlen)
76 const struct sockaddr *addr,
77 socklen_t addrlen)
78{ 80{
79 struct GNUNET_NAT_UPNP_Handle *upnp = cls; 81 struct GNUNET_NAT_UPNP_Handle *upnp = cls;
80 82
81 if (addr && GNUNET_NAT_cmp_addr (upnp->addr, addr) == 0) 83 if (addr && GNUNET_NAT_cmp_addr (upnp->addr, addr) == 0)
82 { 84 {
83 upnp->iface = name; // BADNESS! 85 upnp->iface = name; // BADNESS!
84 return GNUNET_SYSERR; 86 return GNUNET_SYSERR;
85 } 87 }
86 88
@@ -88,41 +90,245 @@ process_if (void *cls,
88} 90}
89 91
90 92
91GNUNET_NAT_UPNP_Handle * 93struct GNUNET_NAT_UPNP_Handle *
92GNUNET_NAT_UPNP_init (const struct sockaddr *addr, 94GNUNET_NAT_UPNP_init (struct GNUNET_SCHEDULER_Handle *sched,
93 socklen_t addrlen, 95 const struct sockaddr *addr,
94 u_short port) 96 socklen_t addrlen,
97 u_short port,
98 GNUNET_NAT_UPNP_pulse_cb pulse_cb, void *pulse_cls)
95{ 99{
96 GNUNET_NAT_UPNP_Handle *upnp; 100 struct GNUNET_NAT_UPNP_Handle *handle;
101
102 handle = GNUNET_malloc (sizeof (struct GNUNET_NAT_UPNP_Handle));
103 handle->sched = sched;
104 handle->processing = GNUNET_NO;
105 handle->state = UPNP_DISCOVER;
106 handle->addr = addr;
107 handle->addrlen = addrlen;
108 handle->port = port;
109 handle->pulse_cb = pulse_cb;
110 handle->pulse_cls = pulse_cls;
111 handle->control_url = NULL;
112 handle->service_type = NULL;
97 113
98 upnp = GNUNET_malloc (sizeof (GNUNET_NAT_UPNP_Handle));
99 upnp->state = UPNP_DISCOVER;
100 upnp->addr = addr;
101 upnp->addrlen = addrlen;
102 upnp->port = port;
103 /* Find the interface corresponding to the address, 114 /* Find the interface corresponding to the address,
104 * on which we should broadcast call for routers */ 115 * on which we should broadcast call for routers */
105 GNUNET_OS_network_interfaces_list (&process_if, upnp); 116 GNUNET_OS_network_interfaces_list (&process_if, handle);
106 if (!upnp->iface) 117 if (!handle->iface)
107 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, 118 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING,
108 COMP_NAT_UPNP, 119 COMP_NAT_UPNP,
109 "Could not find an interface matching the wanted address.\n"); 120 "Could not find an interface matching the wanted address.\n");
110 return upnp; 121 return handle;
111} 122}
112 123
113 124
114void 125void
115GNUNET_NAT_UPNP_close (GNUNET_NAT_UPNP_Handle * handle) 126GNUNET_NAT_UPNP_close (struct GNUNET_NAT_UPNP_Handle *handle)
116{ 127{
117 GNUNET_assert (!handle->is_mapped); 128 GNUNET_assert (!handle->is_mapped);
118 GNUNET_assert ((handle->state == UPNP_IDLE) 129 GNUNET_assert ((handle->state == UPNP_IDLE)
119 || (handle->state == UPNP_ERR) || (handle->state == UPNP_DISCOVER)); 130 || (handle->state == UPNP_ERR)
131 || (handle->state == UPNP_DISCOVER));
120 132
121 if (handle->hasDiscovered) 133 GNUNET_free_non_null (handle->control_url);
122 FreeUPNPUrls (&handle->urls); 134 GNUNET_free_non_null (handle->service_type);
123 GNUNET_free (handle); 135 GNUNET_free (handle);
124} 136}
125 137
138static void
139pulse_finish (struct GNUNET_NAT_UPNP_Handle *handle)
140{
141 enum GNUNET_NAT_PortState status;
142 handle->processing = GNUNET_NO;
143
144 switch (handle->state)
145 {
146 case UPNP_DISCOVER:
147 status = GNUNET_NAT_PORT_UNMAPPED;
148 break;
149
150 case UPNP_MAP:
151 status = GNUNET_NAT_PORT_MAPPING;
152 break;
153
154 case UPNP_UNMAP:
155 status = GNUNET_NAT_PORT_UNMAPPING;
156 break;
157
158 case UPNP_IDLE:
159 status =
160 handle->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
161 break;
162
163 default:
164 status = GNUNET_NAT_PORT_ERROR;
165 break;
166 }
167
168 handle->pulse_cb (status, handle->ext_addr, handle->pulse_cls);
169}
170
171static void
172discover_cb (const char *control_url, const char *service_type, void *cls)
173{
174 struct GNUNET_NAT_UPNP_Handle *handle = cls;
175
176 if (control_url)
177 {
178 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
179 _("Found Internet Gateway Device \"%s\"\n"),
180 control_url);
181
182 GNUNET_free_non_null (handle->control_url);
183 GNUNET_free_non_null (handle->service_type);
184
185 handle->control_url = GNUNET_strdup (control_url);
186 handle->service_type = GNUNET_strdup (service_type);
187 handle->state = UPNP_IDLE;
188 handle->hasDiscovered = 1;
189 }
190 else
191 {
192 handle->control_url = NULL;
193 handle->service_type = NULL;
194 handle->state = UPNP_ERR;
195#ifdef DEBUG_UPNP
196 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
197 "UPNP device discovery failed\n");
198#endif
199 }
200
201 pulse_finish (handle);
202}
203
204static void
205check_port_mapping_cb (int error, const char *control_url,
206 const char *service_type, const char *extPort,
207 const char *inPort, const char *proto,
208 const char *remoteHost, void *cls)
209{
210 struct GNUNET_NAT_UPNP_Handle *handle = cls;
211
212 if (error)
213 {
214 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
215 _("Port %d isn't forwarded\n"), handle->port);
216 handle->is_mapped = GNUNET_NO;
217 }
218
219 pulse_finish (handle);
220}
221
222static void
223delete_port_mapping_cb (int error, const char *control_url,
224 const char *service_type, const char *extPort,
225 const char *inPort, const char *proto,
226 const char *remoteHost, void *cls)
227{
228 struct GNUNET_NAT_UPNP_Handle *handle = cls;
229
230 if (error)
231 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
232 _
233 ("Could not stop port forwarding through \"%s\", service \"%s\": error %d\n"),
234 handle->control_url, handle->service_type, error);
235 else
236 {
237 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
238 _
239 ("Stopped port forwarding through \"%s\", service \"%s\"\n"),
240 handle->control_url, handle->service_type);
241 handle->is_mapped = !error;
242 handle->state = UPNP_IDLE;
243 handle->port = -1;
244 }
245
246 pulse_finish (handle);
247}
248
249static void
250add_port_mapping_cb (int error, const char *control_url,
251 const char *service_type, const char *extPort,
252 const char *inPort, const char *proto,
253 const char *remoteHost, void *cls)
254{
255 struct GNUNET_NAT_UPNP_Handle *handle = cls;
256
257 if (error)
258 {
259 handle->is_mapped = GNUNET_NO;
260 handle->state = UPNP_ERR;
261 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
262 _
263 ("Port forwarding through \"%s\", service \"%s\" failed with error %d\n"),
264 handle->control_url, handle->service_type, error);
265 return;
266 }
267 else
268 {
269 handle->is_mapped = GNUNET_NO;
270 handle->state = UPNP_IDLE;
271 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
272 _("Port %d forwarded successfully\n"), handle->port);
273 }
274
275 pulse_finish (handle);
276}
277
278static void
279get_ip_address_cb (int error, char *ext_addr, void *cls)
280{
281 struct GNUNET_NAT_UPNP_Handle *handle = cls;
282
283 if (error)
284 {
285 if (handle->ext_addr)
286 {
287 GNUNET_free (handle->ext_addr);
288 handle->ext_addr = NULL;
289 }
290#ifdef DEBUG_UPNP
291 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
292 "UPNP_get_external_ip_address_ failed (error %d)\n",
293 error);
294#endif
295 }
296 else
297 {
298 struct in_addr addr;
299 struct in6_addr addr6;
300
301 if (handle->ext_addr)
302 {
303 GNUNET_free (handle->ext_addr);
304 handle->ext_addr = NULL;
305 }
306
307 /* Try IPv4 and IPv6 as we don't know what's the format */
308 if (inet_aton (ext_addr, &addr) != 0)
309 {
310 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
311 handle->ext_addr->sa_family = AF_INET;
312 ((struct sockaddr_in *) handle->ext_addr)->sin_addr = addr;
313 }
314 else if (inet_pton (AF_INET6, ext_addr, &addr6) != 1)
315 {
316 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
317 handle->ext_addr->sa_family = AF_INET6;
318 ((struct sockaddr_in6 *) handle->ext_addr)->sin6_addr = addr6;
319 }
320 else
321 GNUNET_assert (GNUNET_YES);
322
323#ifdef DEBUG_UPNP
324 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
325 _("Found public IP address %s\n"), ext_addr);
326#endif
327 }
328
329 pulse_finish (handle);
330}
331
126/** 332/**
127 * Check state of UPnP NAT: port redirection, external IP address. 333 * Check state of UPnP NAT: port redirection, external IP address.
128 * 334 *
@@ -133,45 +339,19 @@ GNUNET_NAT_UPNP_close (GNUNET_NAT_UPNP_Handle * handle)
133 * @param ext_addr pointer for returning external IP address. 339 * @param ext_addr pointer for returning external IP address.
134 * Will be set to NULL if address could not be found. Don't free the sockaddr. 340 * Will be set to NULL if address could not be found. Don't free the sockaddr.
135 */ 341 */
136int 342void
137GNUNET_NAT_UPNP_pulse (GNUNET_NAT_UPNP_Handle * handle, int is_enabled, 343GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *handle,
138 int doPortCheck, struct sockaddr **ext_addr) 344 int is_enabled, int doPortCheck)
139{ 345{
140 int ret; 346 /* Stop if we're already waiting for an action to complete */
347 if (handle->processing == GNUNET_YES)
348 return;
141 349
142 if (is_enabled && (handle->state == UPNP_DISCOVER)) 350 if (is_enabled && (handle->state == UPNP_DISCOVER))
143 { 351 {
144 struct UPNPDev *devlist; 352 handle->processing = GNUNET_YES;
145 errno = 0; 353 UPNP_discover_ (handle->sched, handle->iface, handle->addr, discover_cb,
146 devlist = upnpDiscover (2000, handle->iface, handle->addr, NULL, 0); 354 handle);
147 if (devlist == NULL)
148 {
149#ifdef DEBUG
150 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
151 "upnpDiscover failed (errno %d - %s)\n", errno,
152 strerror (errno));
153#endif
154 }
155 errno = 0;
156 if (UPNP_GetValidIGD (devlist, &handle->urls, &handle->data,
157 NULL, 0))
158 {
159 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
160 _("Found Internet Gateway Device \"%s\"\n"),
161 handle->urls.controlURL);
162 handle->state = UPNP_IDLE;
163 handle->hasDiscovered = 1;
164 }
165 else
166 {
167 handle->state = UPNP_ERR;
168#ifdef DEBUG
169 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
170 "UPNP_GetValidIGD failed (errno %d - %s)\n",
171 errno, strerror (errno));
172#endif
173 }
174 freeUPNPDevlist (devlist);
175 } 355 }
176 356
177 if (handle->state == UPNP_IDLE) 357 if (handle->state == UPNP_IDLE)
@@ -183,35 +363,26 @@ GNUNET_NAT_UPNP_pulse (GNUNET_NAT_UPNP_Handle * handle, int is_enabled,
183 if (is_enabled && handle->is_mapped && doPortCheck) 363 if (is_enabled && handle->is_mapped && doPortCheck)
184 { 364 {
185 char portStr[8]; 365 char portStr[8];
186 char intPort[8];
187 char intClient[128];
188 int i;
189 366
190 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port); 367 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
191 i = UPNP_GetSpecificPortMappingEntry (handle->urls.controlURL, 368
192 handle->data.servicetype, portStr, 369 handle->processing = GNUNET_YES;
193 "TCP", intClient, intPort); 370 UPNP_get_specific_port_mapping_entry_ (handle->sched,
194 if (i != UPNPCOMMAND_SUCCESS) 371 handle->control_url,
195 { 372 handle->service_type, portStr,
196 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, 373 "TCP", check_port_mapping_cb,
197 _("Port %d isn't forwarded\n"), handle->port); 374 handle);
198 handle->is_mapped = GNUNET_NO;
199 }
200 } 375 }
201 376
202 if (handle->state == UPNP_UNMAP) 377 if (handle->state == UPNP_UNMAP)
203 { 378 {
204 char portStr[16]; 379 char portStr[16];
205 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port); 380 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
206 UPNP_DeletePortMapping (handle->urls.controlURL, 381
207 handle->data.servicetype, portStr, "TCP", NULL); 382 handle->processing = GNUNET_YES;
208 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP, 383 UPNP_delete_port_mapping_ (handle->sched, handle->control_url,
209 _ 384 handle->service_type, portStr, "TCP", NULL,
210 ("Stopping port forwarding through \"%s\", service \"%s\"\n"), 385 delete_port_mapping_cb, handle);
211 handle->urls.controlURL, handle->data.servicetype);
212 handle->is_mapped = 0;
213 handle->state = UPNP_IDLE;
214 handle->port = -1;
215 } 386 }
216 387
217 if (handle->state == UPNP_IDLE) 388 if (handle->state == UPNP_IDLE)
@@ -222,10 +393,7 @@ GNUNET_NAT_UPNP_pulse (GNUNET_NAT_UPNP_Handle * handle, int is_enabled,
222 393
223 if (handle->state == UPNP_MAP) 394 if (handle->state == UPNP_MAP)
224 { 395 {
225 int err = -1; 396 if (!handle->control_url)
226 errno = 0;
227
228 if (!handle->urls.controlURL)
229 handle->is_mapped = 0; 397 handle->is_mapped = 0;
230 else 398 else
231 { 399 {
@@ -233,111 +401,22 @@ GNUNET_NAT_UPNP_pulse (GNUNET_NAT_UPNP_Handle * handle, int is_enabled,
233 char desc[64]; 401 char desc[64];
234 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port); 402 GNUNET_snprintf (portStr, sizeof (portStr), "%d", handle->port);
235 GNUNET_snprintf (desc, sizeof (desc), "GNUnet at %d", handle->port); 403 GNUNET_snprintf (desc, sizeof (desc), "GNUnet at %d", handle->port);
236 err = UPNP_AddPortMapping (handle->urls.controlURL,
237 handle->data.servicetype,
238 portStr, portStr, GNUNET_a2s (handle->addr, handle->addrlen),
239 desc, "TCP", NULL);
240 handle->is_mapped = !err;
241 }
242 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
243 _
244 ("Port forwarding through \"%s\", service \"%s\"\n"),
245 handle->urls.controlURL, handle->data.servicetype);
246 if (handle->is_mapped)
247 {
248 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
249 _("Port %d forwarded successfully\n"), handle->port);
250 handle->state = UPNP_IDLE;
251 }
252 else
253 {
254 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, COMP_NAT_UPNP,
255 "Port forwarding failed with error %d (errno %d - %s)\n",
256 err, errno, strerror (errno));
257 handle->state = UPNP_ERR;
258 }
259 }
260 404
261 if (ext_addr && handle->state != UPNP_DISCOVER) 405 handle->processing = GNUNET_YES;
262 { 406 UPNP_add_port_mapping_ (handle->sched, handle->control_url,
263 int err; 407 handle->service_type,
264 char addr_str[128]; 408 portStr, portStr, GNUNET_a2s (handle->addr,
265 struct in_addr addr; 409 handle->addrlen),
266 struct in6_addr addr6; 410 desc, "TCP", NULL, add_port_mapping_cb,
267 411 handle);
268 /* Keep to NULL if address could not be found */
269 *ext_addr = NULL;
270 err = UPNP_GetExternalIPAddress (handle->urls.controlURL,
271 handle->data.servicetype, addr_str);
272 if (err == 0)
273 {
274 if (handle->ext_addr)
275 {
276 GNUNET_free (handle->ext_addr);
277 handle->ext_addr = NULL;
278 }
279
280 /* Try IPv4 and IPv6 as we don't know what's the format */
281 if (inet_aton (addr_str, &addr) != 0)
282 {
283 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in));
284 handle->ext_addr->sa_family = AF_INET;
285 ((struct sockaddr_in *) handle->ext_addr)->sin_addr = addr;
286 *ext_addr = handle->ext_addr;
287 }
288 else if (inet_pton (AF_INET6, addr_str, &addr6) != 1)
289 {
290 handle->ext_addr = GNUNET_malloc (sizeof (struct sockaddr_in6));
291 handle->ext_addr->sa_family = AF_INET6;
292 ((struct sockaddr_in6 *) handle->ext_addr)->sin6_addr = addr6;
293 *ext_addr = handle->ext_addr;
294 }
295 else
296 GNUNET_assert (GNUNET_YES);
297#ifdef DEBUG
298 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
299 _("Found public IP address %s\n"),
300 addr_str);
301#endif
302 }
303 else
304 {
305 *ext_addr = NULL;
306 if (handle->ext_addr)
307 {
308 GNUNET_free (handle->ext_addr);
309 handle->ext_addr = NULL;
310 }
311#ifdef DEBUG
312 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, COMP_NAT_UPNP,
313 "UPNP_GetExternalIPAddress failed (error %d)\n", err);
314#endif
315 } 412 }
316 } 413 }
317 414
318 switch (handle->state) 415 if (handle->state != UPNP_DISCOVER)
319 { 416 {
320 case UPNP_DISCOVER: 417 handle->processing = GNUNET_YES;
321 ret = GNUNET_NAT_PORT_UNMAPPED; 418 UPNP_get_external_ip_address_ (handle->sched, handle->control_url,
322 break; 419 handle->service_type,
323 420 get_ip_address_cb, handle);
324 case UPNP_MAP:
325 ret = GNUNET_NAT_PORT_MAPPING;
326 break;
327
328 case UPNP_UNMAP:
329 ret = GNUNET_NAT_PORT_UNMAPPING;
330 break;
331
332 case UPNP_IDLE:
333 ret =
334 handle->is_mapped ? GNUNET_NAT_PORT_MAPPED : GNUNET_NAT_PORT_UNMAPPED;
335 break;
336
337 default:
338 ret = GNUNET_NAT_PORT_ERROR;
339 break;
340 } 421 }
341
342 return ret;
343} 422}
diff --git a/src/nat/upnp.h b/src/nat/upnp.h
index ccb04ed00..da95106a0 100644
--- a/src/nat/upnp.h
+++ b/src/nat/upnp.h
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet.
3 (C) 2009 Christian Grothoff (and other contributing authors) 3 (C) 2009, 2010 Christian Grothoff (and other contributing authors)
4 4
5 GNUnet is free software; you can redistribute it and/or modify 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 6 it under the terms of the GNU General Public License as published
@@ -32,17 +32,24 @@
32 32
33struct GNUNET_NAT_UPNP_Handle; 33struct GNUNET_NAT_UPNP_Handle;
34 34
35struct GNUNET_NAT_UPNP_Handle * 35typedef void (*GNUNET_NAT_UPNP_pulse_cb) (int status,
36GNUNET_NAT_UPNP_init (const struct sockaddr *addr, 36 struct sockaddr * ext_addr,
37 socklen_t addrlen, 37 void *cls);
38 unsigned short port);
39 38
40void GNUNET_NAT_UPNP_close (struct GNUNET_NAT_UPNP_Handle * h); 39struct GNUNET_NAT_UPNP_Handle *GNUNET_NAT_UPNP_init (struct
40 GNUNET_SCHEDULER_Handle
41 *sched,
42 const struct sockaddr
43 *addr, socklen_t addrlen,
44 unsigned short port,
45 GNUNET_NAT_UPNP_pulse_cb
46 pulse_cb,
47 void *pulse_cls);
41 48
42int GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *h, 49void GNUNET_NAT_UPNP_close (struct GNUNET_NAT_UPNP_Handle *h);
43 int is_enabled,
44 int do_port_check,
45 struct sockaddr **ext_addr);
46 50
47#endif 51void GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *h,
52 int is_enabled, int do_port_check);
53
54#endif
48/* UPNP_H */ 55/* UPNP_H */