aboutsummaryrefslogtreecommitdiff
path: root/src/upnp/upnp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/upnp/upnp.c')
-rw-r--r--src/upnp/upnp.c721
1 files changed, 721 insertions, 0 deletions
diff --git a/src/upnp/upnp.c b/src/upnp/upnp.c
new file mode 100644
index 000000000..6dc4338a0
--- /dev/null
+++ b/src/upnp/upnp.c
@@ -0,0 +1,721 @@
1/**
2 * @file upnp.c UPnP Implementation
3 * @ingroup core
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#include "platform.h"
27#include "upnp_xmlnode.h"
28#include "upnp_util.h"
29#include "upnp.h"
30
31#include <curl/curl.h>
32
33#define TRUE GNUNET_YES
34#define FALSE GNUNET_NO
35#define g_return_if_fail(a) if(!(a)) return;
36#define g_return_val_if_fail(a, val) if(!(a)) return (val);
37
38#define HTTP_OK "200 OK"
39#define NUM_UDP_ATTEMPTS 2
40#define HTTPMU_HOST_ADDRESS "239.255.255.250"
41#define HTTPMU_HOST_PORT 1900
42#define SEARCH_REQUEST_DEVICE "urn:schemas-upnp-org:service:%s"
43#define SEARCH_REQUEST_STRING \
44 "M-SEARCH * HTTP/1.1\r\n" \
45 "MX: 2\r\n" \
46 "HOST: 239.255.255.250:1900\r\n" \
47 "MAN: \"ssdp:discover\"\r\n" \
48 "ST: urn:schemas-upnp-org:service:%s\r\n" \
49 "\r\n"
50#define WAN_IP_CONN_SERVICE "WANIPConnection:1"
51#define WAN_PPP_CONN_SERVICE "WANPPPConnection:1"
52#define HTTP_POST_SOAP_HEADER \
53 "SOAPACTION: \"urn:schemas-upnp-org:service:%s#%s\""
54#define HTTP_POST_SIZE_HEADER "CONTENT-LENGTH: %u"
55#define SOAP_ACTION \
56 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" \
57 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
58 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" \
59 "<s:Body>\r\n" \
60 "<u:%s xmlns:u=\"urn:schemas-upnp-org:service:%s\">\r\n" \
61 "%s" \
62 "</u:%s>\r\n" \
63 "</s:Body>\r\n" \
64 "</s:Envelope>"
65#define PORT_MAPPING_LEASE_TIME "0"
66#define PORT_MAPPING_DESCRIPTION "GNUNET_UPNP_PORT_FORWARD"
67#define ADD_PORT_MAPPING_PARAMS \
68 "<NewRemoteHost></NewRemoteHost>\r\n" \
69 "<NewExternalPort>%i</NewExternalPort>\r\n" \
70 "<NewProtocol>%s</NewProtocol>\r\n" \
71 "<NewInternalPort>%i</NewInternalPort>\r\n" \
72 "<NewInternalClient>%s</NewInternalClient>\r\n" \
73 "<NewEnabled>1</NewEnabled>\r\n" \
74 "<NewPortMappingDescription>" \
75 PORT_MAPPING_DESCRIPTION \
76 "</NewPortMappingDescription>\r\n" \
77 "<NewLeaseDuration>" \
78 PORT_MAPPING_LEASE_TIME \
79 "</NewLeaseDuration>\r\n"
80#define DELETE_PORT_MAPPING_PARAMS \
81 "<NewRemoteHost></NewRemoteHost>\r\n" \
82 "<NewExternalPort>%i</NewExternalPort>\r\n" \
83 "<NewProtocol>%s</NewProtocol>\r\n"
84
85typedef enum
86{
87 GAIM_UPNP_STATUS_UNDISCOVERED = -1,
88 GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER,
89 GAIM_UPNP_STATUS_DISCOVERING,
90 GAIM_UPNP_STATUS_DISCOVERED
91} GaimUPnPStatus;
92
93typedef struct
94{
95 GaimUPnPStatus status;
96 char *control_url;
97 const char *service_type;
98 char publicip[16];
99} GaimUPnPControlInfo;
100
101typedef struct
102{
103 const char *service_type;
104 char *full_url;
105 char *buf;
106 unsigned int buf_len;
107 int sock;
108} UPnPDiscoveryData;
109
110static GaimUPnPControlInfo control_info = {
111 GAIM_UPNP_STATUS_UNDISCOVERED,
112 NULL,
113 NULL,
114 "",
115};
116
117/**
118 * This is the signature used for functions that act as a callback
119 * to CURL.
120 */
121typedef size_t (*GaimUtilFetchUrlCallback) (void *url_data,
122 size_t size,
123 size_t nmemb, void *user_data);
124
125
126
127static char *
128g_strstr_len (const char *haystack, int haystack_len, const char *needle)
129{
130 int i;
131
132 g_return_val_if_fail (haystack != NULL, NULL);
133 g_return_val_if_fail (needle != NULL, NULL);
134
135 if (haystack_len < 0)
136 return strstr (haystack, needle);
137 else
138 {
139 const char *p = haystack;
140 int needle_len = strlen (needle);
141 const char *end = haystack + haystack_len - needle_len;
142
143 if (needle_len == 0)
144 return (char *) haystack;
145
146 while (*p && p <= end)
147 {
148 for (i = 0; i < needle_len; i++)
149 if (p[i] != needle[i])
150 goto next;
151
152 return (char *) p;
153
154 next:
155 p++;
156 }
157 }
158
159 return NULL;
160}
161
162static int
163gaim_upnp_compare_device (const xmlnode * device, const char *deviceType)
164{
165 xmlnode *deviceTypeNode = xmlnode_get_child (device, "deviceType");
166 char *tmp;
167 int ret;
168
169 if (deviceTypeNode == NULL)
170 return FALSE;
171 tmp = xmlnode_get_data (deviceTypeNode);
172 ret = !strcasecmp (tmp, deviceType);
173 GNUNET_free (tmp);
174 return ret;
175}
176
177static int
178gaim_upnp_compare_service (const xmlnode * service, const char *serviceType)
179{
180 xmlnode *serviceTypeNode;
181 char *tmp;
182 int ret;
183
184 if (service == NULL)
185 return FALSE;
186 serviceTypeNode = xmlnode_get_child (service, "serviceType");
187 if (serviceTypeNode == NULL)
188 return FALSE;
189 tmp = xmlnode_get_data (serviceTypeNode);
190 ret = !strcasecmp (tmp, serviceType);
191 GNUNET_free (tmp);
192 return ret;
193}
194
195static char *
196gaim_upnp_parse_description_response (const char *httpResponse,
197 size_t len,
198 const char *httpURL,
199 const char *serviceType)
200{
201 char *xmlRoot, *baseURL, *controlURL, *service;
202 xmlnode *xmlRootNode, *serviceTypeNode, *controlURLNode, *baseURLNode;
203 char *tmp;
204
205 /* find the root of the xml document */
206 xmlRoot = g_strstr_len (httpResponse, len, "<root");
207 if (xmlRoot == NULL)
208 return NULL;
209 if (g_strstr_len (httpResponse, len, "</root") == NULL)
210 return NULL;
211
212 /* create the xml root node */
213 xmlRootNode = xmlnode_from_str (xmlRoot, len - (xmlRoot - httpResponse));
214 if (xmlRootNode == NULL)
215 return NULL;
216
217 /* get the baseURL of the device */
218 baseURLNode = xmlnode_get_child (xmlRootNode, "URLBase");
219 if (baseURLNode != NULL)
220 {
221 baseURL = xmlnode_get_data (baseURLNode);
222 }
223 else
224 {
225 baseURL = GNUNET_strdup (httpURL);
226 }
227
228 /* get the serviceType child that has the service type as its data */
229 /* get urn:schemas-upnp-org:device:InternetGatewayDevice:1 and its devicelist */
230 serviceTypeNode = xmlnode_get_child (xmlRootNode, "device");
231 while (!gaim_upnp_compare_device (serviceTypeNode,
232 "urn:schemas-upnp-org:device:InternetGatewayDevice:1")
233 && serviceTypeNode != NULL)
234 {
235 serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
236 }
237 if (serviceTypeNode == NULL)
238 {
239 GNUNET_free (baseURL);
240 xmlnode_free (xmlRootNode);
241 return NULL;
242 }
243 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "deviceList");
244 if (serviceTypeNode == NULL)
245 {
246 GNUNET_free (baseURL);
247 xmlnode_free (xmlRootNode);
248 return NULL;
249 }
250
251 /* get urn:schemas-upnp-org:device:WANDevice:1 and its devicelist */
252 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "device");
253 while (!gaim_upnp_compare_device (serviceTypeNode,
254 "urn:schemas-upnp-org:device:WANDevice:1")
255 && serviceTypeNode != NULL)
256 {
257 serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
258 }
259 if (serviceTypeNode == NULL)
260 {
261 GNUNET_free (baseURL);
262 xmlnode_free (xmlRootNode);
263 return NULL;
264 }
265 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "deviceList");
266 if (serviceTypeNode == NULL)
267 {
268 GNUNET_free (baseURL);
269 xmlnode_free (xmlRootNode);
270 return NULL;
271 }
272
273 /* get urn:schemas-upnp-org:device:WANConnectionDevice:1 and its servicelist */
274 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "device");
275 while (serviceTypeNode && !gaim_upnp_compare_device (serviceTypeNode,
276 "urn:schemas-upnp-org:device:WANConnectionDevice:1"))
277 {
278 serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
279 }
280 if (serviceTypeNode == NULL)
281 {
282 GNUNET_free (baseURL);
283 xmlnode_free (xmlRootNode);
284 return NULL;
285 }
286 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "serviceList");
287 if (serviceTypeNode == NULL)
288 {
289 GNUNET_free (baseURL);
290 xmlnode_free (xmlRootNode);
291 return NULL;
292 }
293
294 /* get the serviceType variable passed to this function */
295 service = g_strdup_printf (SEARCH_REQUEST_DEVICE, serviceType);
296 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "service");
297 while (!gaim_upnp_compare_service (serviceTypeNode, service) &&
298 serviceTypeNode != NULL)
299 {
300 serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
301 }
302
303 GNUNET_free (service);
304 if (serviceTypeNode == NULL)
305 {
306 GNUNET_free (baseURL);
307 xmlnode_free (xmlRootNode);
308 return NULL;
309 }
310
311 /* get the controlURL of the service */
312 if ((controlURLNode = xmlnode_get_child (serviceTypeNode,
313 "controlURL")) == NULL)
314 {
315 GNUNET_free (baseURL);
316 xmlnode_free (xmlRootNode);
317 return NULL;
318 }
319
320 tmp = xmlnode_get_data (controlURLNode);
321 if (baseURL && !gaim_str_has_prefix (tmp, "http://") &&
322 !gaim_str_has_prefix (tmp, "HTTP://"))
323 {
324 if (tmp[0] == '/')
325 {
326 size_t len;
327 const char *end;
328 /* absolute path */
329 end = strstr (&baseURL[strlen ("http://")], "/");
330 if (end == NULL)
331 len = strlen (&baseURL[strlen ("http://")]);
332 else
333 len = end - &baseURL[strlen ("http://")];
334 controlURL = g_strdup_printf ("http://%.*s%s",
335 len,
336 &baseURL[strlen ("http://")], tmp);
337 }
338 else
339 {
340 controlURL = g_strdup_printf ("%s%s", baseURL, tmp);
341 }
342 GNUNET_free (tmp);
343 }
344 else
345 {
346 controlURL = tmp;
347 }
348 GNUNET_free (baseURL);
349 xmlnode_free (xmlRootNode);
350
351 return controlURL;
352}
353
354#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 | GNUNET_ERROR_TYPE_BULK, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0);
355
356/**
357 * Do the generic curl setup.
358 */
359static int
360setup_curl (const char *proxy, CURL * curl)
361{
362 int ret;
363
364 CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
365 if (strlen (proxy) > 0)
366 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
367 CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, 1024); /* a bit more than one HELLO */
368 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 150L);
369 /* NOTE: use of CONNECTTIMEOUT without also
370 setting NOSIGNAL results in really weird
371 crashes on my system! */
372 CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
373 return GNUNET_OK;
374}
375
376static int
377gaim_upnp_generate_action_message_and_send (const char *proxy,
378 const char *actionName,
379 const char *actionParams,
380 GaimUtilFetchUrlCallback cb,
381 void *cb_data)
382{
383 CURL *curl;
384 int ret;
385 char *soapHeader;
386 char *sizeHeader;
387 char *soapMessage;
388 struct curl_slist *headers = NULL;
389
390 GNUNET_assert (cb != NULL);
391 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
392 return GNUNET_SYSERR;
393 /* set the soap message */
394 soapMessage = g_strdup_printf (SOAP_ACTION,
395 actionName,
396 control_info.service_type,
397 actionParams, actionName);
398 soapHeader = g_strdup_printf (HTTP_POST_SOAP_HEADER,
399 control_info.service_type, actionName);
400 sizeHeader = g_strdup_printf (HTTP_POST_SIZE_HEADER, strlen (soapMessage));
401 curl = curl_easy_init ();
402 setup_curl (proxy, curl);
403 CURL_EASY_SETOPT (curl, CURLOPT_URL, control_info.control_url);
404 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, cb);
405 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, cb_data);
406 CURL_EASY_SETOPT (curl, CURLOPT_POST, 1);
407 headers = curl_slist_append (headers,
408 "CONTENT-TYPE: text/xml ; charset=\"utf-8\"");
409 headers = curl_slist_append (headers, soapHeader);
410 headers = curl_slist_append (headers, sizeHeader);
411 CURL_EASY_SETOPT (curl, CURLOPT_HTTPHEADER, headers);
412 CURL_EASY_SETOPT (curl, CURLOPT_POSTFIELDS, soapMessage);
413 CURL_EASY_SETOPT (curl, CURLOPT_POSTFIELDSIZE, strlen (soapMessage));
414 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 1L);
415 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 1L);
416 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 2L);
417 /* NOTE: use of CONNECTTIMEOUT without also
418 setting NOSIGNAL results in really weird
419 crashes on my system! */
420 CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
421 if (ret == CURLE_OK)
422 ret = curl_easy_perform (curl);
423#if 0
424 if (ret != CURLE_OK)
425 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
426 _
427 ("%s failed for url `%s' and post-data `%s' at %s:%d: `%s'\n"),
428 "curl_easy_perform", control_info.control_url, soapMessage,
429 __FILE__, __LINE__, curl_easy_strerror (ret));
430#endif
431 curl_slist_free_all (headers);
432 curl_easy_cleanup (curl);
433 curl_global_cleanup ();
434 GNUNET_free (sizeHeader);
435 GNUNET_free (soapMessage);
436 GNUNET_free (soapHeader);
437 if (ret != CURLE_OK)
438 return GNUNET_SYSERR;
439 return GNUNET_OK;
440}
441
442
443static size_t
444looked_up_public_ip_cb (void *url_data,
445 size_t size, size_t nmemb, void *user_data)
446{
447 UPnPDiscoveryData *dd = user_data;
448 size_t len = size * nmemb;
449 const char *temp;
450 const char *temp2;
451
452 if (len + dd->buf_len > 1024 * 1024 * 4)
453 return 0; /* refuse to process - too big! */
454 GNUNET_array_grow (dd->buf, dd->buf_len, dd->buf_len + len);
455 memcpy (&dd->buf[dd->buf_len - len], url_data, len);
456 if (dd->buf_len == 0)
457 return len;
458 /* extract the ip, or see if there is an error */
459 if ((temp = g_strstr_len (dd->buf,
460 dd->buf_len, "<NewExternalIPAddress")) == NULL)
461 return len;
462 if (!(temp = g_strstr_len (temp, dd->buf_len - (temp - dd->buf), ">")))
463 return len;
464 if (!(temp2 = g_strstr_len (temp, dd->buf_len - (temp - dd->buf), "<")))
465 return len;
466 memset (control_info.publicip, 0, sizeof (control_info.publicip));
467 if (temp2 - temp >= sizeof (control_info.publicip))
468 temp2 = temp + sizeof (control_info.publicip) - 1;
469 memcpy (control_info.publicip, temp + 1, temp2 - (temp + 1));
470 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
471 _("upnp: NAT Returned IP: %s\n"), control_info.publicip);
472 return len;
473}
474
475
476static size_t
477ignore_response (void *url_data, size_t size, size_t nmemb, void *user_data)
478{
479 return size * nmemb;
480}
481
482/**
483 * Process downloaded bits of service description.
484 */
485static size_t
486upnp_parse_description_cb (void *httpResponse,
487 size_t size, size_t nmemb, void *user_data)
488{
489 UPnPDiscoveryData *dd = user_data;
490 size_t len = size * nmemb;
491 char *control_url = NULL;
492
493 if (len + dd->buf_len > 1024 * 1024 * 4)
494 return len; /* refuse to process - too big! */
495 GNUNET_array_grow (dd->buf, dd->buf_len, dd->buf_len + len);
496 memcpy (&dd->buf[dd->buf_len - len], httpResponse, len);
497 if (dd->buf_len > 0)
498 control_url = gaim_upnp_parse_description_response (dd->buf,
499 dd->buf_len,
500 dd->full_url,
501 dd->service_type);
502 control_info.status = control_url ? GAIM_UPNP_STATUS_DISCOVERED
503 : GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER;
504 GNUNET_free_non_null (control_info.control_url);
505 control_info.control_url = control_url;
506 control_info.service_type = dd->service_type;
507 return len;
508}
509
510static int
511gaim_upnp_parse_description (char *proxy, UPnPDiscoveryData * dd)
512{
513 CURL *curl;
514 int ret;
515
516 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
517 return GNUNET_SYSERR;
518 curl = curl_easy_init ();
519 setup_curl (proxy, curl);
520 ret = CURLE_OK;
521 CURL_EASY_SETOPT (curl, CURLOPT_URL, dd->full_url);
522 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &upnp_parse_description_cb);
523 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, dd);
524 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 1L);
525 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 1L);
526 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 2L);
527
528 /* NOTE: use of CONNECTTIMEOUT without also
529 setting NOSIGNAL results in really weird
530 crashes on my system! */
531 CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
532 ret = curl_easy_perform (curl);
533 if (ret != CURLE_OK)
534 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
535 _("%s failed at %s:%d: `%s'\n"),
536 "curl_easy_perform", __FILE__, __LINE__,
537 curl_easy_strerror (ret));
538 curl_easy_cleanup (curl);
539 curl_global_cleanup ();
540 if (control_info.control_url == NULL)
541 return GNUNET_SYSERR;
542 return GNUNET_OK;
543}
544
545int
546gaim_upnp_discover (struct GNUNET_CONFIGURATION_Handle *cfg, int sock)
547{
548 char *proxy;
549 socklen_t avail;
550 struct sockaddr_in server;
551 int retry_count;
552 char *sendMessage;
553 size_t totalSize;
554 int sentSuccess;
555 char buf[65536];
556 int buf_len;
557 const char *startDescURL;
558 const char *endDescURL;
559 int ret;
560 UPnPDiscoveryData dd;
561 struct sockaddr *sa;
562
563 memset (&dd, 0, sizeof (UPnPDiscoveryData));
564 if (control_info.status == GAIM_UPNP_STATUS_DISCOVERING)
565 return GNUNET_NO;
566 dd.sock = sock;
567 memset (&server, 0, sizeof (struct sockaddr_in));
568 server.sin_family = AF_INET;
569 avail = sizeof (struct sockaddr_in);
570 sa = (struct sockaddr *) &server;
571 if (GNUNET_OK !=
572 GNUNET_get_ip_from_hostname (HTTPMU_HOST_ADDRESS, AF_INET, &sa, &avail))
573 {
574 return GNUNET_SYSERR;
575 }
576 server.sin_port = htons (HTTPMU_HOST_PORT);
577 control_info.status = GAIM_UPNP_STATUS_DISCOVERING;
578
579 /* because we are sending over UDP, if there is a failure
580 we should retry the send NUM_UDP_ATTEMPTS times. Also,
581 try different requests for WANIPConnection and WANPPPConnection */
582 for (retry_count = 0; retry_count < NUM_UDP_ATTEMPTS; retry_count++)
583 {
584 sentSuccess = FALSE;
585 if ((retry_count % 2) == 0)
586 dd.service_type = WAN_IP_CONN_SERVICE;
587 else
588 dd.service_type = WAN_PPP_CONN_SERVICE;
589 sendMessage = g_strdup_printf (SEARCH_REQUEST_STRING, dd.service_type);
590 totalSize = strlen (sendMessage);
591 do
592 {
593 if (SENDTO (dd.sock,
594 sendMessage,
595 totalSize,
596 0,
597 (struct sockaddr *) &server,
598 sizeof (struct sockaddr_in)) == totalSize)
599 {
600 sentSuccess = TRUE;
601 break;
602 }
603 }
604 while (((errno == EINTR) || (errno == EAGAIN)) &&
605 (GNUNET_shutdown_test () == GNUNET_NO));
606 GNUNET_free (sendMessage);
607 if (sentSuccess)
608 break;
609 }
610 if (sentSuccess == FALSE)
611 return GNUNET_SYSERR;
612
613 /* try to read response */
614 do
615 {
616 buf_len = recv (dd.sock, buf, sizeof (buf) - 1, 0);
617 if (buf_len > 0)
618 {
619 buf[buf_len] = '\0';
620 break;
621 }
622 else if (errno != EINTR)
623 {
624 continue;
625 }
626 }
627 while ((errno == EINTR) && (GNUNET_shutdown_test () == GNUNET_NO));
628
629 /* parse the response, and see if it was a success */
630 if (g_strstr_len (buf, buf_len, HTTP_OK) == NULL)
631 return GNUNET_SYSERR;
632 if ((startDescURL = g_strstr_len (buf, buf_len, "http://")) == NULL)
633 return GNUNET_SYSERR;
634
635 endDescURL = g_strstr_len (startDescURL,
636 buf_len - (startDescURL - buf), "\r");
637 if (endDescURL == NULL)
638 endDescURL = g_strstr_len (startDescURL,
639 buf_len - (startDescURL - buf), "\n");
640 if (endDescURL == NULL)
641 return GNUNET_SYSERR;
642 if (endDescURL == startDescURL)
643 return GNUNET_SYSERR;
644 dd.full_url = GNUNET_strdup (startDescURL);
645 dd.full_url[endDescURL - startDescURL] = '\0';
646 proxy = NULL;
647 GNUNET_CONFIGURATION_get_value_string (cfg,
648 "GNUNETD", "HTTP-PROXY", &proxy);
649 ret = gaim_upnp_parse_description (proxy, &dd);
650 GNUNET_free (dd.full_url);
651 GNUNET_array_grow (dd.buf, dd.buf_len, 0);
652 if (ret == GNUNET_OK)
653 {
654 ret = gaim_upnp_generate_action_message_and_send (proxy,
655 "GetExternalIPAddress",
656 "",
657 looked_up_public_ip_cb,
658 &dd);
659 GNUNET_array_grow (dd.buf, dd.buf_len, 0);
660 }
661 GNUNET_free (proxy);
662 return ret;
663}
664
665const char *
666gaim_upnp_get_public_ip ()
667{
668 if ((control_info.status == GAIM_UPNP_STATUS_DISCOVERED)
669 && (strlen (control_info.publicip) > 0))
670 return control_info.publicip;
671 return NULL;
672}
673
674int
675gaim_upnp_change_port_mapping (struct GNUNET_CONFIGURATION_Handle *cfg,
676 int do_add,
677 unsigned short portmap, const char *protocol)
678{
679 const char *action_name;
680 char *action_params;
681 char *internal_ip;
682 char *proxy;
683 int ret;
684
685 if (control_info.status != GAIM_UPNP_STATUS_DISCOVERED)
686 return GNUNET_NO;
687 if (do_add)
688 {
689 internal_ip = GNUNET_upnp_get_internal_ip (cfg);
690 if (internal_ip == NULL)
691 {
692 gaim_debug_error ("upnp",
693 "gaim_upnp_set_port_mapping(): couldn't get local ip\n");
694 return GNUNET_NO;
695 }
696 action_name = "AddPortMapping";
697 action_params = g_strdup_printf (ADD_PORT_MAPPING_PARAMS,
698 portmap,
699 protocol, portmap, internal_ip);
700 GNUNET_free (internal_ip);
701 }
702 else
703 {
704 action_name = "DeletePortMapping";
705 action_params = g_strdup_printf (DELETE_PORT_MAPPING_PARAMS,
706 portmap, protocol);
707 }
708 proxy = NULL;
709 GNUNET_CONFIGURATION_get_value_string (cfg,
710 "GNUNETD", "HTTP-PROXY", &proxy);
711 ret =
712 gaim_upnp_generate_action_message_and_send (proxy, action_name,
713 action_params,
714 &ignore_response, NULL);
715
716 GNUNET_free (action_params);
717 GNUNET_free (proxy);
718 return ret;
719}
720
721/* end of upnp.c */