diff options
author | Moon <moon@140774ce-b5e7-0310-ab8b-a85725594a96> | 2010-10-09 13:53:47 +0000 |
---|---|---|
committer | Moon <moon@140774ce-b5e7-0310-ab8b-a85725594a96> | 2010-10-09 13:53:47 +0000 |
commit | 922a0672749ba9d496d1dd8f6596bb4f8035e71d (patch) | |
tree | ccd4b351bd5acd5cc3b8b1cc358c712d6c0c116d | |
parent | dc24b5bf44bf8d9460b2571fe529403637aa3e16 (diff) | |
download | gnunet-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.am | 15 | ||||
-rw-r--r-- | src/nat/bsdqueue.h | 157 | ||||
-rw-r--r-- | src/nat/nat.c | 362 | ||||
-rw-r--r-- | src/nat/nat.h | 17 | ||||
-rw-r--r-- | src/nat/natpmp.c | 87 | ||||
-rw-r--r-- | src/nat/natpmp.h | 20 | ||||
-rw-r--r-- | src/nat/test_nat.c | 27 | ||||
-rw-r--r-- | src/nat/upnp-commands.c | 880 | ||||
-rw-r--r-- | src/nat/upnp-commands.h | 281 | ||||
-rw-r--r-- | src/nat/upnp-discover.c | 1285 | ||||
-rw-r--r-- | src/nat/upnp-discover.h | 84 | ||||
-rw-r--r-- | src/nat/upnp-igd-parse.c | 207 | ||||
-rw-r--r-- | src/nat/upnp-igd-parse.h | 99 | ||||
-rw-r--r-- | src/nat/upnp-minixml.c | 239 | ||||
-rw-r--r-- | src/nat/upnp-minixml.h | 131 | ||||
-rw-r--r-- | src/nat/upnp-reply-parse.c | 166 | ||||
-rw-r--r-- | src/nat/upnp-reply-parse.h | 107 | ||||
-rw-r--r-- | src/nat/upnp.c | 459 | ||||
-rw-r--r-- | src/nat/upnp.h | 29 |
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 @@ | |||
1 | SUBDIRS = . | ||
2 | |||
3 | INCLUDES = -I$(top_srcdir)/src/include | 1 | INCLUDES = -I$(top_srcdir)/src/include |
4 | 2 | ||
5 | if MINGW | 3 | if MINGW |
@@ -18,11 +16,16 @@ endif | |||
18 | 16 | ||
19 | libgnunetnat_la_SOURCES = \ | 17 | libgnunetnat_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 | ||
24 | libgnunetnat_la_CFLAGS = \ | 26 | libgnunetnat_la_CFLAGS = \ |
25 | -I$(top_scrdir)/include | 27 | -I$(top_scrdir)/include \ |
28 | -DDEBUG_UPNP -g -O0 | ||
26 | 29 | ||
27 | libgnunetnat_la_LIBADD = \ | 30 | libgnunetnat_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 | ||
47 | test_nat_LDADD = \ | 50 | test_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@ | |
51 | endif | 54 | endif |
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) \ | ||
81 | struct name { \ | ||
82 | struct type *lh_first; /* first element */ \ | ||
83 | } | ||
84 | |||
85 | #define LIST_HEAD_INITIALIZER(head) \ | ||
86 | { NULL } | ||
87 | |||
88 | #define LIST_ENTRY(type) \ | ||
89 | struct { \ | ||
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 | ||
124 | static int | 136 | static int |
125 | get_traversal_status (const struct GNUNET_NAT_Handle * s) | 137 | get_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 */ |
136 | int | 148 | int |
137 | GNUNET_NAT_cmp_addr (const struct sockaddr *a, | 149 | GNUNET_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 | */ |
159 | static void | 170 | static void |
160 | notify_change (struct GNUNET_NAT_Handle *nat, | 171 | notify_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 | ||
233 | static void nat_pulse (void *cls, | ||
234 | const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
227 | 235 | ||
228 | static void | 236 | static void |
229 | nat_pulse (void *cls, | 237 | pulse_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 | |||
304 | static void | ||
305 | upnp_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 | ||
317 | static void | ||
318 | natpmp_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 | |||
330 | static void | ||
331 | nat_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 | */ |
324 | struct GNUNET_NAT_Handle * | 383 | struct GNUNET_NAT_Handle * |
325 | GNUNET_NAT_register (struct GNUNET_SCHEDULER_Handle *sched, | 384 | GNUNET_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, | |||
371 | void | 439 | void |
372 | GNUNET_NAT_unregister (struct GNUNET_NAT_Handle *h) | 440 | GNUNET_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 | */ |
36 | enum GNUNET_NAT_PortState | 36 | enum 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 | */ |
69 | int GNUNET_NAT_cmp_addr (const struct sockaddr *a, | 69 | int 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 | ||
51 | typedef enum | 52 | enum 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 | } |
62 | NATPMP_state; | 63 | ; |
63 | 64 | ||
64 | struct GNUNET_NAT_NATPMP_Handle | 65 | struct 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 | ||
100 | struct GNUNET_NAT_NATPMP_Handle * | 99 | struct GNUNET_NAT_NATPMP_Handle * |
101 | GNUNET_NAT_NATPMP_init (const struct sockaddr *addr, socklen_t addrlen, | 100 | GNUNET_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 | ||
114 | void | 115 | void |
115 | GNUNET_NAT_NATPMP_close (GNUNET_NAT_NATPMP_Handle * nat) | 116 | GNUNET_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 | ||
33 | struct GNUNET_NAT_NATPMP_Handle; | 33 | struct GNUNET_NAT_NATPMP_Handle; |
34 | 34 | ||
35 | struct GNUNET_NAT_NATPMP_Handle * | 35 | struct GNUNET_NAT_NATPMP_Handle *GNUNET_NAT_NATPMP_init (struct |
36 | GNUNET_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 | ||
40 | void GNUNET_NAT_NATPMP_close (struct GNUNET_NAT_NATPMP_Handle * nat); | 43 | void GNUNET_NAT_NATPMP_close (struct GNUNET_NAT_NATPMP_Handle *nat); |
41 | 44 | ||
42 | int GNUNET_NAT_NATPMP_pulse (struct GNUNET_NAT_NATPMP_Handle * nat, | 45 | int 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 | ||
46 | struct addr_cls | 46 | struct 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 | ||
53 | static void | 52 | static void |
54 | addr_callback (void *cls, int add_remove, | 53 | addr_callback (void *cls, int add_remove, |
@@ -73,9 +72,7 @@ stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
73 | static int | 72 | static int |
74 | process_if (void *cls, | 73 | process_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 | ||
133 | int | 140 | int |
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 | */ | ||
81 | struct 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 | */ | ||
126 | static ssize_t | ||
127 | get_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 | */ | ||
185 | static void | ||
186 | get_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 | */ | ||
232 | static void | ||
233 | UPNP_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 | */ | ||
268 | static size_t | ||
269 | UPNP_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 | */ | ||
294 | int | ||
295 | parse_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 | */ | ||
355 | void | ||
356 | UPNP_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 | |||
553 | struct get_external_ip_address_cls | ||
554 | { | ||
555 | UPNP_get_external_ip_address_cb_ caller_cb; | ||
556 | void *caller_cls; | ||
557 | }; | ||
558 | |||
559 | static void | ||
560 | get_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 | */ | ||
603 | void | ||
604 | UPNP_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 | |||
627 | struct 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 | |||
639 | static void | ||
640 | add_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 | |||
668 | void | ||
669 | UPNP_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 | |||
725 | void | ||
726 | UPNP_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 | |||
771 | struct 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 | |||
781 | static void | ||
782 | get_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 */ | ||
837 | void | ||
838 | UPNP_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 | */ | ||
78 | struct 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 | */ | ||
92 | typedef 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 | */ | ||
105 | void 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 | */ | ||
122 | typedef 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 | */ | ||
136 | void | ||
137 | UPNP_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 | */ | ||
176 | typedef 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 | */ | ||
199 | void | ||
200 | UPNP_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 | */ | ||
225 | void | ||
226 | UPNP_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 | */ | ||
245 | typedef 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 | */ | ||
272 | void | ||
273 | UPNP_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 | */ | ||
81 | typedef void (*download_cb) (char *data, void *cls); | ||
82 | |||
83 | /** | ||
84 | * Private closure used by download_device_description() and it's callbacks. | ||
85 | */ | ||
86 | struct 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 | */ | ||
138 | static void | ||
139 | download_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 | */ | ||
163 | static size_t | ||
164 | callback_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 | |||
189 | static void | ||
190 | task_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 | */ | ||
197 | static void | ||
198 | download_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 | */ | ||
263 | static void | ||
264 | task_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 | */ | ||
360 | void | ||
361 | download_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 | |||
441 | error: | ||
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 */ | ||
460 | static void | ||
461 | parse_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 | */ | ||
547 | struct 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 | */ | ||
588 | struct 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 | */ | ||
656 | static char * | ||
657 | get_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 | */ | ||
709 | static char * | ||
710 | format_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 | |||
734 | static 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 | */ | ||
744 | static void | ||
745 | get_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 | */ | ||
783 | static void | ||
784 | get_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 | */ | ||
833 | static void | ||
834 | free_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 | */ | ||
855 | static void | ||
856 | get_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 | |||
907 | static 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 | |||
915 | static void | ||
916 | discover_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 | */ | ||
929 | static void | ||
930 | discover_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 | */ | ||
1031 | static void | ||
1032 | discover_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 | */ | ||
1087 | void | ||
1088 | UPNP_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 | |||
65 | typedef 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 | */ | ||
79 | void 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 | */ | ||
69 | static void | ||
70 | start_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 | */ | ||
90 | static void | ||
91 | end_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 | */ | ||
132 | static void | ||
133 | IGDdata (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 | ||
161 | static void | ||
162 | print_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 | */ | ||
188 | void | ||
189 | UPNP_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 | */ | ||
66 | struct 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 | */ | ||
95 | void | ||
96 | UPNP_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 | */ | ||
68 | static int | ||
69 | parse_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 | */ | ||
145 | void | ||
146 | parse_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 | */ | ||
233 | void | ||
234 | UPNP_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 */ | ||
69 | struct 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 */ | ||
129 | void 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 | |||
67 | static void | ||
68 | start_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 | |||
80 | static void | ||
81 | get_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 | |||
100 | void | ||
101 | UPNP_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 | |||
120 | void | ||
121 | UPNP_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 | |||
132 | char * | ||
133 | UPNP_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 | ||
150 | void | ||
151 | UPNP_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 | */ | ||
66 | struct 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 | */ | ||
76 | struct 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 | */ | ||
85 | void | ||
86 | UPNP_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 | */ | ||
92 | void UPNP_REPLY_free_ (struct UPNP_REPLY_NameValueList_ *pdata); | ||
93 | |||
94 | /** | ||
95 | * Get value corresponding to name from a name-value list. | ||
96 | */ | ||
97 | char *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 | */ | ||
104 | void 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 | ||
58 | struct GNUNET_NAT_UPNP_Handle | 58 | struct 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 | ||
72 | static int | 76 | static int |
73 | process_if (void *cls, | 77 | process_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 | ||
91 | GNUNET_NAT_UPNP_Handle * | 93 | struct GNUNET_NAT_UPNP_Handle * |
92 | GNUNET_NAT_UPNP_init (const struct sockaddr *addr, | 94 | GNUNET_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 | ||
114 | void | 125 | void |
115 | GNUNET_NAT_UPNP_close (GNUNET_NAT_UPNP_Handle * handle) | 126 | GNUNET_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 | ||
138 | static void | ||
139 | pulse_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 | |||
171 | static void | ||
172 | discover_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 | |||
204 | static void | ||
205 | check_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 | |||
222 | static void | ||
223 | delete_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 | |||
249 | static void | ||
250 | add_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 | |||
278 | static void | ||
279 | get_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 | */ |
136 | int | 342 | void |
137 | GNUNET_NAT_UPNP_pulse (GNUNET_NAT_UPNP_Handle * handle, int is_enabled, | 343 | GNUNET_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 | ||
33 | struct GNUNET_NAT_UPNP_Handle; | 33 | struct GNUNET_NAT_UPNP_Handle; |
34 | 34 | ||
35 | struct GNUNET_NAT_UPNP_Handle * | 35 | typedef void (*GNUNET_NAT_UPNP_pulse_cb) (int status, |
36 | GNUNET_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 | ||
40 | void GNUNET_NAT_UPNP_close (struct GNUNET_NAT_UPNP_Handle * h); | 39 | struct 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 | ||
42 | int GNUNET_NAT_UPNP_pulse (struct GNUNET_NAT_UPNP_Handle *h, | 49 | void 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 | 51 | void 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 */ |