comparison src/upnp.c @ 11195:3aeb85cc9cda

[gaim-migrate @ 13319] *** empty log message *** committer: Tailor Script <tailor@pidgin.im>
author Adam Warrington <awarring>
date Sun, 07 Aug 2005 17:54:19 +0000
parents
children a015b1cb7368
comparison
equal deleted inserted replaced
11194:4c1f45ac00e9 11195:3aeb85cc9cda
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 #include "internal.h"
26 #include "gtkgaim.h"
27
28 #include "debug.h"
29 #include "network.h"
30 #include "eventloop.h"
31 #include "upnp.h"
32
33
34 /**
35 * Information on the httpResponse callback
36 */
37 typedef struct
38 {
39 guint inpa; /* gaim_input_add handle */
40 guint tima; /* gaim_timout_add handle */
41 char* recvBuffer; /* response data */
42 guint totalSizeRecv;
43 gboolean done;
44
45 } HRD;
46
47
48 /***************************************************************
49 ** General Defines *
50 ****************************************************************/
51 #define HTTP_OK "200 OK"
52 #define SIZEOF_HTTP 7 /* size of "http://" */
53 #define RECIEVE_TIMEOUT 10000
54 #define CONSECUTIVE_RECIEVE_TIMEOUT 500
55 #define DISCOVERY_TIMEOUT 1000
56
57
58 /***************************************************************
59 ** Discovery/Description Defines *
60 ****************************************************************/
61 #define NUM_UDP_ATTEMPTS 2
62
63 /* Address and port of an SSDP request used for discovery */
64 #define HTTPMU_HOST_ADDRESS "239.255.255.250"
65 #define HTTPMU_HOST_PORT 1900
66
67 #define SEARCH_REQUEST_DEVICE "urn:schemas-upnp-org:service:" \
68 "WANIPConnection:1"
69
70 #define SEARCH_REQUEST_STRING "M-SEARCH * HTTP/1.1\r\n" \
71 "MX: 2\r\n" \
72 "HOST: 239.255.255.250:1900\r\n" \
73 "MAN: \"ssdp:discover\"\r\n" \
74 "ST: urn:schemas-upnp-org:service:" \
75 "WANIPConnection:1\r\n" \
76 "\r\n"
77
78 #define MAX_DISCOVERY_RECIEVE_SIZE 400
79 #define MAX_DESCRIPTION_RECIEVE_SIZE 7000
80 #define MAX_DESCRIPTION_HTTP_HEADER_SIZE 100
81
82
83 /******************************************************************
84 ** Action Defines *
85 *******************************************************************/
86 #define HTTP_HEADER_ACTION "POST %s HTTP/1.1\r\n" \
87 "HOST: %s\r\n" \
88 "SOAPACTION: " \
89 "\"urn:schemas-upnp-org:" \
90 "service:%s#%s\"\r\n" \
91 "CONTENT-TYPE: text/xml ; charset=\"utf-8\"\r\n"\
92 "Content-Length: %i\r\n\r\n"
93
94 #define SOAP_ACTION "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" \
95 "<s:Envelope xmlns:s=" \
96 "\"http://schemas.xmlsoap.org/soap/envelope/\" " \
97 "s:encodingStyle=" \
98 "\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" \
99 "<s:Body>\r\n" \
100 "<u:%s xmlns:u=" \
101 "\"urn:schemas-upnp-org:service:%s\">\r\n%s" \
102 "</u:%s>\r\n" \
103 "</s:Body>\r\n" \
104 "</s:Envelope>\r\n"
105
106 #define PORT_MAPPING_LEASE_TIME "0"
107 #define PORT_MAPPING_DESCRIPTION "GAIM_UPNP_PORT_FORWARD"
108
109 #define ADD_PORT_MAPPING_PARAMS "<NewRemoteHost></NewRemoteHost>\r\n" \
110 "<NewExternalPort>%i</NewExternalPort>\r\n"\
111 "<NewProtocol>%s</NewProtocol>\r\n" \
112 "<NewInternalPort>%i</NewInternalPort>\r\n"\
113 "<NewInternalClient>%s" \
114 "</NewInternalClient>\r\n" \
115 "<NewEnabled>1</NewEnabled>\r\n" \
116 "<NewPortMappingDescription>" \
117 PORT_MAPPING_DESCRIPTION \
118 "</NewPortMappingDescription>\r\n" \
119 "<NewLeaseDuration>" \
120 PORT_MAPPING_LEASE_TIME \
121 "</NewLeaseDuration>\r\n"
122
123 #define DELETE_PORT_MAPPING_PARAMS "<NewRemoteHost></NewRemoteHost>\r\n" \
124 "<NewExternalPort>%i" \
125 "</NewExternalPort>\r\n" \
126 "<NewProtocol>%s</NewProtocol>\r\n"
127
128
129
130 /* validate an http url without a port */
131 static gboolean
132 gaim_upnp_validate_url(const char* url)
133 {
134 int i1, i2, i3, i4, r;
135
136 if(url == NULL) {
137 gaim_debug_info("upnp",
138 "gaim_upnp_validate_url(): url == NULL\n\n");
139 return FALSE;
140 }
141 r = sscanf(url, "http://%3i.%3i.%3i.%3i/", &i1, &i2, &i3, &i4);
142 if(r == 4) {
143 return TRUE;
144 }
145
146 gaim_debug_info("upnp",
147 "gaim_upnp_validate_url(): Failed In validate URL\n\n");
148 return FALSE;
149 }
150
151
152 static void
153 gaim_upnp_timeout(gpointer data,
154 gint source,
155 GaimInputCondition cond)
156 {
157 HRD* hrd = data;
158
159 gaim_input_remove(hrd->inpa);
160 gaim_timeout_remove(hrd->tima);
161
162 if(hrd->totalSizeRecv == 0) {
163 free(hrd->recvBuffer);
164 hrd->recvBuffer = NULL;
165 } else {
166 hrd->recvBuffer[hrd->totalSizeRecv] = '\0';
167 }
168
169 hrd->done = TRUE;
170 }
171
172
173 static void
174 gaim_upnp_http_read(gpointer data,
175 gint sock,
176 GaimInputCondition cond)
177 {
178 int sizeRecv;
179 extern int errno;
180 HRD* hrd = data;
181
182 sizeRecv = recv(sock, &(hrd->recvBuffer[hrd->totalSizeRecv]),
183 MAX_DESCRIPTION_RECIEVE_SIZE-hrd->totalSizeRecv, 0);
184 if(sizeRecv < 0 && errno != EINTR) {
185 gaim_debug_info("upnp",
186 "gaim_upnp_http_read(): recv < 0: %i!\n\n", errno);
187 free(hrd->recvBuffer);
188 hrd->recvBuffer = NULL;
189 gaim_timeout_remove(hrd->tima);
190 gaim_input_remove(hrd->inpa);
191 hrd->done = TRUE;
192 return;
193 }else if(errno == EINTR) {
194 sizeRecv = 0;
195 }
196 hrd->totalSizeRecv += sizeRecv;
197
198 if(sizeRecv == 0) {
199 if(hrd->totalSizeRecv == 0) {
200 gaim_debug_info("upnp",
201 "gaim_upnp_http_read(): totalSizeRecv == 0\n\n");
202 free(hrd->recvBuffer);
203 hrd->recvBuffer = NULL;
204 } else {
205 hrd->recvBuffer[hrd->totalSizeRecv] = '\0';
206 }
207 gaim_timeout_remove(hrd->tima);
208 gaim_input_remove(hrd->inpa);
209 hrd->done = TRUE;
210 } else {
211 gaim_timeout_remove(hrd->tima);
212 gaim_input_remove(hrd->inpa);
213 hrd->tima = gaim_timeout_add(CONSECUTIVE_RECIEVE_TIMEOUT,
214 (GSourceFunc)gaim_upnp_timeout, hrd);
215 hrd->inpa = gaim_input_add(sock, GAIM_INPUT_READ,
216 gaim_upnp_http_read, hrd);
217 }
218
219 }
220
221
222
223 static char*
224 gaim_upnp_http_request(const char* address,
225 unsigned short port,
226 const char* httpRequest)
227 {
228 int sock;
229 int sizeSent, totalSizeSent = 0;
230 extern int errno;
231 struct sockaddr_in serv_addr;
232 struct hostent *server;
233 char* recvBuffer;
234
235 HRD* hrd = (HRD*)malloc(sizeof(HRD));
236 if(hrd == NULL) {
237 gaim_debug_info("upnp",
238 "gaim_upnp_http_request(): Failed in hrd MALLOC\n\n");
239 return NULL;
240 }
241
242 hrd->recvBuffer = NULL;
243 hrd->totalSizeRecv = 0;
244 hrd->done = FALSE;
245
246 hrd->recvBuffer = (char*)malloc(MAX_DESCRIPTION_RECIEVE_SIZE);
247 if(hrd == NULL) {
248 gaim_debug_info("upnp",
249 "gaim_upnp_http_request(): Failed in recvBuffer MALLOC\n\n");
250 free(hrd);
251 return NULL;
252 }
253
254 sock = socket(AF_INET, SOCK_STREAM, 0);
255 if(sock < 0) {
256 gaim_debug_info("upnp",
257 "gaim_upnp_http_request(): Failed In sock creation\n\n");
258 free(hrd->recvBuffer);
259 free(hrd);
260 return NULL;
261 }
262
263 server = gethostbyname(address);
264 if(server == NULL) {
265 gaim_debug_info("upnp",
266 "gaim_upnp_http_request(): Failed In gethostbyname\n\n");
267 free(hrd->recvBuffer);
268 free(hrd);
269 close(sock);
270 return NULL;
271 }
272 memset((char*)&serv_addr, 0, sizeof(serv_addr));
273 serv_addr.sin_family = AF_INET;
274 memcpy(&serv_addr.sin_addr,
275 server->h_addr_list[0],
276 server->h_length);
277 serv_addr.sin_port = htons(port);
278
279 if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0) {
280 gaim_debug_info("upnp",
281 "gaim_upnp_http_request(): Failed In connect\n\n");
282 free(hrd->recvBuffer);
283 free(hrd);
284 close(sock);
285 return NULL;
286 }
287
288 while(totalSizeSent < strlen(httpRequest)) {
289 sizeSent = send(sock,(char*)((int)httpRequest+totalSizeSent),
290 strlen(httpRequest),0);
291 if(sizeSent <= 0 && errno != EINTR) {
292 gaim_debug_info("upnp",
293 "gaim_upnp_http_request(): Failed In send\n\n");
294 free(hrd->recvBuffer);
295 free(hrd);
296 close(sock);
297 return NULL;
298 }else if(errno == EINTR) {
299 sizeSent = 0;
300 }
301 totalSizeSent += sizeSent;
302 }
303
304 hrd->tima = gaim_timeout_add(RECIEVE_TIMEOUT,
305 (GSourceFunc)gaim_upnp_timeout, hrd);
306 hrd->inpa = gaim_input_add(sock, GAIM_INPUT_READ,
307 gaim_upnp_http_read, hrd);
308 while (!hrd->done) {
309 gtk_main_iteration();
310 }
311 close(sock);
312
313 recvBuffer = hrd->recvBuffer;
314 free(hrd);
315 return recvBuffer;
316 }
317
318
319
320 /* This function takes the HTTP response requesting the description xml from
321 * the UPnP enabled IGD. It also takes as input the URL the request was sent
322 * to.
323 *
324 * The description contains the URL needed to control the actions of the UPnP
325 * enabled IGD. This URL can be in the form of just a path, in whcih case we
326 * have to append the path to the base URL. The base URL might be specified
327 * in the XML, or it might not, in which case u append to the httpURL.
328 */
329
330 /* at some point, maybe parse the description response using an xml library */
331 static char*
332 gaim_upnp_parse_description_response(const char* httpResponse,
333 const char* httpURL)
334 {
335 char* wanIPConnectionStart;
336 char urlBaseTag[] = "<URLBase>";
337 char urlBaseEndTag[] = "</URLBase>";
338 char controlURLTag[] = "<controlURL>";
339 char controlURLEndTag[] = "</controlURL>";
340 char* urlBaseTagLoc;
341 char* urlBaseEndTagLoc;
342 char* controlURLTagLoc;
343 char* controlURLEndTagLoc;
344 int controlURLSize;
345 int baseURLSize;
346 char* controlURL;
347 char* baseURL;
348
349 if(strstr(httpResponse, HTTP_OK) == NULL) {
350 gaim_debug_info("upnp",
351 "parse_description_response(): Failed In HTTP_OK\n\n");
352 return NULL;
353 }
354
355 if((wanIPConnectionStart =
356 strstr(httpResponse, SEARCH_REQUEST_DEVICE)) == NULL) {
357 gaim_debug_info("upnp",
358 "parse_description_response(): Failed SEARCH_REQUEST_DEVICE\n\n");
359 return NULL;
360 }
361 if((controlURLTagLoc = strstr(wanIPConnectionStart, controlURLTag))
362 == NULL) {
363 gaim_debug_info("upnp",
364 "parse_description_response(): Failed In controlURLTagLoc\n\n");
365 return NULL;
366 }
367 if((controlURLEndTagLoc =
368 strstr(wanIPConnectionStart, controlURLEndTag)) == NULL) {
369 gaim_debug_info("upnp",
370 "parse_description_response(): Failed In controlURLEndTagLoc\n\n");
371 return NULL;
372 }
373
374
375 controlURLSize =
376 controlURLEndTagLoc-&controlURLTagLoc[strlen(controlURLTag)];
377 if((controlURL = (char*)malloc(controlURLSize+1)) == NULL) {
378 gaim_debug_info("upnp",
379 "parse_description_response(): Failed In MALLOC controlURL\n\n");
380 return NULL;
381 }
382 memcpy(controlURL, &controlURLTagLoc[strlen(controlURLTag)],
383 controlURLSize);
384 controlURL[controlURLSize] = '\0';
385
386
387 if((urlBaseTagLoc = strstr(httpResponse, urlBaseTag)) == NULL) {
388 if((baseURL = (char*)malloc(strlen(httpURL)+controlURLSize+1)) == NULL) {
389 gaim_debug_info("upnp",
390 "parse_description_response(): Failed In MALLOC baseURL\n\n");
391 return NULL;
392 }
393 memcpy(baseURL, httpURL, strlen(httpURL));
394 baseURL[strlen(httpURL)] = '\0';
395 } else {
396 if((urlBaseEndTagLoc = strstr(httpResponse, urlBaseEndTag)) == NULL) {
397 gaim_debug_info("upnp",
398 "parse_description_response(): Failed In urlBaseEndTagLoc\n\n");
399 return NULL;
400 }
401 baseURLSize =
402 urlBaseEndTagLoc - &urlBaseTagLoc[strlen(urlBaseTag)];
403 if((baseURL = (char*)malloc(baseURLSize+controlURLSize+1)) == NULL) {
404 gaim_debug_info("upnp",
405 "parse_description_response(): Failed 2nd MALLOC baseURL\n\n");
406 return NULL;
407 }
408 memcpy(baseURL, &urlBaseTagLoc[strlen(urlBaseTag)], baseURLSize);
409 baseURL[baseURLSize] = '\0';
410 }
411
412 if(strstr(controlURL, "http://") == NULL) {
413 memcpy(&baseURL[strlen(baseURL)], controlURL, strlen(controlURL)+1);
414 free(controlURL);
415 controlURL = baseURL;
416 }else{
417 free(baseURL);
418 }
419
420 return controlURL;
421 }
422
423
424
425
426 /* parse a url into it's approrpiate parts:
427 address, port, path. return true on success,
428 false on failure */
429 static gboolean
430 gaim_upnp_parse_url(const char* url, char** path,
431 char** address, char** port)
432 {
433 char* temp, *temp2;
434
435 /* get the path */
436 if((temp = strchr(&url[SIZEOF_HTTP], '/')) < 0) {
437 gaim_debug_info("upnp",
438 "gaim_upnp_parse_url(): Failed In 1\n\n");
439 return FALSE;
440 }
441 if((*path = (char*)malloc(strlen(temp)+1)) == NULL) {
442 gaim_debug_info("upnp",
443 "gaim_upnp_parse_url(): Failed In 2\n\n");
444 return FALSE;
445 }
446 memcpy(*path, temp, strlen(temp));
447 (*path)[strlen(temp)] = '\0';
448
449 /* get the port */
450 if((temp2 = strchr(&url[SIZEOF_HTTP], ':')) == NULL) {
451 gaim_debug_info("upnp",
452 "gaim_upnp_parse_url(): Failed In 3\n\n");
453 free(*path);
454 return FALSE;
455 }
456 if((*port = (char*)malloc(temp-temp2)) == NULL) {
457 gaim_debug_info("upnp",
458 "gaim_upnp_parse_url(): Failed In 4\n\n");
459 free(*path);
460 return FALSE;
461 }
462 memcpy(*port, &temp2[1], (temp-1)-temp2);
463 (*port)[(temp-1)-temp2] = '\0';
464
465 /* get the address */
466 if((*address = (char*)malloc((temp2-&url[SIZEOF_HTTP])+1)) == NULL) {
467 gaim_debug_info("upnp",
468 "gaim_upnp_parse_url(): Failed In 5\n\n");
469 free(*path);
470 free(*port);
471 return FALSE;
472 }
473 memcpy(*address, &url[SIZEOF_HTTP],
474 temp2-&url[SIZEOF_HTTP]);
475 (*address)[temp2-&url[SIZEOF_HTTP]] = '\0';
476
477 return TRUE;
478 }
479
480
481
482 /*
483 * This function takes the URL where the UPnP enabled IGD description is
484 * located at, and sends an HTTP request to retrieve that description. Once
485 * the description is retrieved, we send the response to the
486 * gaim_upnp_parse_description_response() function. This will parse the
487 * description, and return the control URL needed to control the IGD, which
488 * this function returns.
489 */
490 static char*
491 gaim_upnp_parse_description(const char* descriptionURL)
492 {
493 char* fullURL;
494 char* controlURL;
495 char* httpResponse;
496 char httpRequest[MAX_DESCRIPTION_HTTP_HEADER_SIZE];
497
498 char* descriptionXMLAddress;
499 char* descriptionAddressPort;
500 char* descriptionAddress;
501 char* descriptionPort;
502 unsigned short port;
503
504 /* parse the 4 above variables out of the descriptionURL
505 example description URL: http://192.168.1.1:5678/rootDesc.xml */
506
507 /* parse the url into address, port, path variables */
508 if(!gaim_upnp_parse_url(descriptionURL, &descriptionXMLAddress,
509 &descriptionAddress, &descriptionPort)) {
510 return NULL;
511 }
512 if((port = atoi(descriptionPort)) == 0) {
513 gaim_debug_info("upnp",
514 "gaim_upnp_parse_description(): failed atoi\n\n");
515 free(descriptionXMLAddress);
516 free(descriptionAddress);
517 free(descriptionPort);
518 return NULL;
519 }
520
521 if((descriptionAddressPort = (char*)malloc(strlen(descriptionAddress) +
522 strlen(descriptionPort) + 2))
523 == NULL) {
524 gaim_debug_info("upnp",
525 "gaim_upnp_parse_description(): failed MALLOC desAddPort\n\n");
526 free(descriptionXMLAddress);
527 free(descriptionAddress);
528 free(descriptionPort);
529 return NULL;
530 }
531 sprintf(descriptionAddressPort, "%s:%s",
532 descriptionAddress, descriptionPort);
533
534 if((fullURL = (char*)malloc(strlen(descriptionAddressPort) +
535 SIZEOF_HTTP + 1)) == NULL) {
536 gaim_debug_info("upnp",
537 "gaim_upnp_parse_description(): failed MALLOC fullURL\n\n");
538 free(descriptionXMLAddress);
539 free(descriptionAddress);
540 free(descriptionPort);
541 free(descriptionAddressPort);
542 }
543 sprintf(fullURL, "http://%s", descriptionAddressPort);
544
545 /* for example...
546 GET /rootDesc.xml HTTP/1.1\r\nHost: 192.168.1.1:5678\r\n\r\n */
547 sprintf(httpRequest, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n",
548 descriptionXMLAddress, descriptionAddressPort);
549
550 httpResponse = gaim_upnp_http_request(descriptionAddress,
551 port, httpRequest);
552 if(httpResponse == NULL) {
553 gaim_debug_info("upnp",
554 "gaim_upnp_parse_description(): httpResponse is NULL\n\n");
555 free(descriptionXMLAddress);
556 free(descriptionAddress);
557 free(descriptionPort);
558 free(descriptionAddressPort);
559 free(fullURL);
560 return NULL;
561 }
562
563 controlURL = gaim_upnp_parse_description_response(httpResponse, fullURL);
564
565 free(descriptionXMLAddress);
566 free(descriptionAddress);
567 free(descriptionPort);
568 free(descriptionAddressPort);
569 free(fullURL);
570 free(httpResponse);
571
572 if(controlURL == NULL) {
573 gaim_debug_info("upnp",
574 "gaim_upnp_parse_description(): controlURL is NULL\n\n");
575 return NULL;
576 }
577
578 return controlURL;
579 }
580
581
582 static char*
583 gaim_upnp_parse_discover_response(const char* buf,
584 unsigned int bufSize)
585 {
586 char* startDescURL;
587 char* endDescURL;
588 char* descURL;
589 unsigned int descURLSize;
590 char* retVal;
591
592 if(strstr(buf, HTTP_OK) == NULL) {
593 gaim_debug_info("upnp",
594 "parse_discover_response(): Failed In HTTP_OK\n\n");
595 return NULL;
596 }
597
598 if((startDescURL = strstr(buf, "http://")) == NULL) {
599 gaim_debug_info("upnp",
600 "parse_description_response(): Failed In finding http://\n\n");
601 return NULL;
602 }
603
604 endDescURL = strchr(startDescURL, '\r');
605 if(endDescURL == NULL) {
606 endDescURL = strchr(startDescURL, '\n');
607 if(endDescURL == NULL) {
608 gaim_debug_info("upnp",
609 "parse_description_response(): Failed In endDescURL\n\n");
610 return NULL;
611 }else if(endDescURL == startDescURL) {
612 gaim_debug_info("upnp",
613 "parse_description_response(): endDescURL == startDescURL\n\n");
614 return NULL;
615 }
616 }else if(endDescURL == startDescURL) {
617 gaim_debug_info("upnp",
618 "parse_description_response(): 2nd endDescURL == startDescURL\n\n");
619 return NULL;
620 }
621
622 descURLSize = (endDescURL-startDescURL)+1;
623 descURL = (char*)malloc(descURLSize);
624 memcpy(descURL, startDescURL, descURLSize-1);
625 descURL[descURLSize-1] = '\0';
626
627 retVal = gaim_upnp_parse_description(descURL);
628 free(descURL);
629 return retVal;
630 }
631
632
633
634 static void
635 gaim_upnp_discover_udp_read(gpointer data,
636 gint sock,
637 GaimInputCondition cond)
638 {
639 unsigned int length;
640 extern int errno;
641 struct sockaddr_in from;
642 int sizeRecv;
643 HRD* hrd = data;
644
645 gaim_timeout_remove(hrd->tima);
646 length = sizeof(struct sockaddr_in);
647 hrd->recvBuffer = (char*)malloc(MAX_DISCOVERY_RECIEVE_SIZE);
648
649 do {
650 sizeRecv = recvfrom(sock, hrd->recvBuffer,
651 MAX_DISCOVERY_RECIEVE_SIZE, 0,
652 (struct sockaddr*)&from, &length);
653
654 if(sizeRecv > 0) {
655 hrd->recvBuffer[sizeRecv] = '\0';
656 }else if(errno != EINTR) {
657 free(hrd->recvBuffer);
658 hrd->recvBuffer = NULL;
659 }
660 }while(errno == EINTR);
661
662 gaim_input_remove(hrd->inpa);
663 hrd->done = TRUE;
664 return;
665 }
666
667
668
669 const char*
670 gaim_upnp_discover(void)
671 {
672 int sock, i;
673 int sizeSent, totalSizeSent;
674 extern int errno;
675 gboolean sentSuccess, recvSuccess;
676 struct sockaddr_in server;
677 struct hostent *hp;
678 char sendMessage[] = SEARCH_REQUEST_STRING;
679 char *controlURL = NULL;
680
681 HRD* hrd = (HRD*)malloc(sizeof(HRD));
682 hrd->recvBuffer = NULL;
683 hrd->done = FALSE;
684
685 sock = socket(AF_INET, SOCK_DGRAM, 0);
686 if (sock == -1) {
687 close(sock);
688 gaim_debug_info("upnp",
689 "gaim_upnp_discover(): Failed In sock creation\n\n");
690 return NULL;
691 }
692
693 memset(&server, 0, sizeof(struct sockaddr));
694 server.sin_family = AF_INET;
695 if((hp = gethostbyname(HTTPMU_HOST_ADDRESS)) == NULL) {
696 close(sock);
697 gaim_debug_info("upnp",
698 "gaim_upnp_discover(): Failed In gethostbyname\n\n");
699 return NULL;
700 }
701
702 memcpy(&server.sin_addr,
703 hp->h_addr_list[0],
704 hp->h_length);
705 server.sin_port = htons(HTTPMU_HOST_PORT);
706
707 /* because we are sending over UDP, if there is a failure
708 we should retry the send NUM_UDP_ATTEMPTS times */
709 for(i = 0; i < NUM_UDP_ATTEMPTS; i++) {
710 sentSuccess = TRUE;
711 recvSuccess = TRUE;
712 totalSizeSent = 0;
713
714 while(totalSizeSent < strlen(sendMessage)) {
715 sizeSent = sendto(sock,(void*)&sendMessage[totalSizeSent],
716 strlen(&sendMessage[totalSizeSent]),0,
717 (struct sockaddr*)&server,
718 sizeof(struct sockaddr_in));
719 if(sizeSent <= 0 && errno != EINTR) {
720 sentSuccess = FALSE;
721 break;
722 }else if(errno == EINTR) {
723 sizeSent = 0;
724 }
725 totalSizeSent += sizeSent;
726 }
727
728 if(sentSuccess) {
729 hrd->tima = gaim_timeout_add(DISCOVERY_TIMEOUT,
730 (GSourceFunc)gaim_upnp_timeout, hrd);
731 hrd->inpa = gaim_input_add(sock, GAIM_INPUT_READ,
732 gaim_upnp_discover_udp_read, hrd);
733 while (!hrd->done) {
734 gtk_main_iteration();
735 }
736 if(hrd->recvBuffer == NULL) {
737 recvSuccess = FALSE;
738 } else {
739 /* parse the response, and see if it was a success */
740 close(sock);
741 if((controlURL=
742 gaim_upnp_parse_discover_response(hrd->recvBuffer,
743 strlen(hrd->recvBuffer)))==NULL) {
744 gaim_debug_info("upnp",
745 "gaim_upnp_discover(): Failed In parse response\n\n");
746 return NULL;
747 }
748 }
749 }
750
751 /* if sent success and recv successful, then break */
752 if(sentSuccess && recvSuccess) {
753 i = NUM_UDP_ATTEMPTS;
754 }
755 }
756 if(!sentSuccess || !recvSuccess) {
757 close(sock);
758 gaim_debug_info("upnp",
759 "gaim_upnp_discover(): Failed In sent/recv success\n\n");
760 return NULL;
761 }
762
763 return controlURL;
764 }
765
766
767
768
769 static char*
770 gaim_upnp_generate_action_message_and_send(const char* controlURL,
771 const char* actionName,
772 const char* actionParams)
773 {
774 char serviceType[] = "WANIPConnection:1";
775 char* actionMessage;
776 char* soapMessage;
777 char* httpResponse;
778
779 char* pathOfControl;
780 char* addressOfControl;
781 char* addressPortOfControl;
782 char* portOfControl;
783 unsigned short port;
784
785 /* set the soap message */
786 if((soapMessage = (char*)malloc(strlen(SOAP_ACTION) +
787 strlen(serviceType) +
788 strlen(actionParams) +
789 strlen(actionName) +
790 strlen(actionName))) == NULL) {
791 gaim_debug_info("upnp",
792 "generate_action_message_and_send(): Failed MALLOC soapMessage\n\n");
793 return NULL;
794 }
795 sprintf(soapMessage, SOAP_ACTION, actionName, serviceType,
796 actionParams, actionName);
797
798 /* parse the url into address, port, path variables */
799 if(!gaim_upnp_parse_url(controlURL, &pathOfControl,
800 &addressOfControl, &portOfControl)) {
801 gaim_debug_info("upnp",
802 "generate_action_message_and_send(): Failed In Parse URL\n\n");
803 free(soapMessage);
804 return NULL;
805 }
806
807 /* set the addressPortOfControl variable which should have a
808 form like the following: 192.168.1.1:8000 */
809 if((addressPortOfControl = (char*)malloc(strlen(addressOfControl) +
810 strlen(portOfControl) +
811 2)) == NULL) {
812 gaim_debug_info("upnp",
813 "generate_action_message_and_send(): MALLOC addressPortOfControl\n\n");
814 free(soapMessage);
815 free(pathOfControl);
816 free(addressOfControl);
817 free(portOfControl);
818 return NULL;
819 }
820 sprintf(addressPortOfControl, "%s:%s", addressOfControl, portOfControl);
821 if((port = atoi(portOfControl)) == 0) {
822 gaim_debug_info("upnp",
823 "generate_action_message_and_send(): Failed In port = atoi\n\n");
824 free(soapMessage);
825 free(pathOfControl);
826 free(addressOfControl);
827 free(portOfControl);
828 free(addressPortOfControl);
829 return NULL;
830 }
831
832 /* set the HTTP Header */
833 if((actionMessage = (char*)malloc(strlen(HTTP_HEADER_ACTION) +
834 strlen(pathOfControl) +
835 strlen(addressPortOfControl) +
836 strlen(serviceType) +
837 strlen(actionName) +
838 strlen(soapMessage))) == NULL) {
839 gaim_debug_info("upnp",
840 "generate_action_message_and_send(): Failed MALLOC actionMessage\n\n");
841 free(soapMessage);
842 free(pathOfControl);
843 free(addressOfControl);
844 free(portOfControl);
845 free(addressPortOfControl);
846 return NULL;
847 }
848 sprintf(actionMessage, HTTP_HEADER_ACTION,
849 pathOfControl, addressPortOfControl,
850 serviceType, actionName, strlen(soapMessage));
851
852 /* append to the header the body */
853 strcat(actionMessage, soapMessage);
854
855 /* get the return of the http response */
856 httpResponse = gaim_upnp_http_request(addressOfControl,
857 port, actionMessage);
858 if(httpResponse == NULL) {
859 gaim_debug_info("upnp",
860 "generate_action_message_and_send(): Failed In httpResponse\n\n");
861 }
862
863 free(actionMessage);
864 free(soapMessage);
865 free(pathOfControl);
866 free(addressOfControl);
867 free(portOfControl);
868 free(addressPortOfControl);
869
870 return httpResponse;
871 }
872
873
874
875
876 const char*
877 gaim_upnp_get_public_ip(const char* controlURL)
878 {
879 char* extIPAddress;
880 char* httpResponse;
881 char actionName[] = "GetExternalIPAddress";
882 char actionParams[] = "";
883 char* temp, *temp2;
884
885 /* make sure controlURL is a valid URL */
886 if(!gaim_upnp_validate_url(controlURL)) {
887 gaim_debug_info("upnp",
888 "gaim_upnp_get_public_ip(): Failed In Validate URL\n\n");
889 return NULL;
890 }
891
892 httpResponse = gaim_upnp_generate_action_message_and_send(controlURL,
893 actionName,
894 actionParams);
895 if(httpResponse == NULL) {
896 gaim_debug_info("upnp",
897 "gaim_upnp_get_public_ip(): Failed In httpResponse\n\n");
898 return NULL;
899 }
900
901 /* extract the ip, or see if there is an error */
902 /* at some point, maybe extract the ip using an xml library */
903 if((temp = strstr(httpResponse, "<NewExternalIPAddress")) == NULL) {
904 gaim_debug_info("upnp",
905 "gaim_upnp_get_public_ip(): Failed Finding <NewExternalIPAddress\n\n");
906 free(httpResponse);
907 return NULL;
908 }
909 if((temp = strchr(temp, '>')) == NULL) {
910 gaim_debug_info("upnp",
911 "gaim_upnp_get_public_ip(): Failed In Finding >\n\n");
912 free(httpResponse);
913 return NULL;
914 }
915 if((temp2 = strchr(temp, '<')) == NULL) {
916 gaim_debug_info("upnp",
917 "gaim_upnp_get_public_ip(): Failed In Finding <\n\n");
918 free(httpResponse);
919 return NULL;
920 }
921 if((extIPAddress = (char*)malloc(temp2-temp)) == NULL) {
922 gaim_debug_info("upnp",
923 "gaim_upnp_get_public_ip(): Failed In MALLOC extIPAddress\n\n");
924 free(httpResponse);
925 return NULL;
926 }
927 memcpy(extIPAddress, &temp[1], (temp2-1)-temp);
928 extIPAddress[(temp2-1)-temp] = '\0';
929
930 free(httpResponse);
931 gaim_debug_info("upnp",
932 "gaim_upnp_get_public_ip() IP: %s\n\n", extIPAddress);
933 return extIPAddress;
934 }
935
936
937
938 static const char*
939 gaim_upnp_get_local_ip_address(const char* address) {
940 const char* ip;
941 int sock;
942 struct sockaddr_in serv_addr;
943 struct hostent *server;
944 char* pathOfControl;
945 char* addressOfControl;
946 char* portOfControl;
947 unsigned short port;
948
949 /* parse the url into address, port, path variables */
950 if(!gaim_upnp_parse_url(address, &pathOfControl,
951 &addressOfControl, &portOfControl)) {
952 gaim_debug_info("upnp",
953 "get_local_ip_address(): Failed In Parse URL\n\n");
954 return NULL;
955 }
956 if((port = atoi(portOfControl)) == 0) {
957 gaim_debug_info("upnp",
958 "get_local_ip_address(): Failed In port = atoi\n\n");
959 return NULL;
960 }
961
962 sock = socket(AF_INET, SOCK_STREAM, 0);
963 if(sock < 0) {
964 gaim_debug_info("upnp",
965 "get_local_ip_address(): Failed In sock creation\n\n");
966 return NULL;
967 }
968
969 server = gethostbyname(addressOfControl);
970 if(server == NULL) {
971 gaim_debug_info("upnp",
972 "get_local_ip_address(): Failed In gethostbyname\n\n");
973 close(sock);
974 return NULL;
975 }
976 memset((char*)&serv_addr, 0, sizeof(serv_addr));
977 serv_addr.sin_family = AF_INET;
978 memcpy(&serv_addr.sin_addr,
979 server->h_addr_list[0],
980 server->h_length);
981 serv_addr.sin_port = htons(port);
982
983 if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0) {
984 gaim_debug_info("upnp",
985 "get_local_ip_address(): Failed In connect\n\n");
986 close(sock);
987 return NULL;
988 }
989
990 ip = gaim_network_get_local_system_ip(sock);
991
992 close(sock);
993 return ip;
994 }
995
996
997
998 gboolean
999 gaim_upnp_set_port_mapping(const char* controlURL, unsigned short portMap,
1000 const char* protocol)
1001 {
1002 char* httpResponse;
1003 char actionName[] = "AddPortMapping";
1004 char* actionParams;
1005 const char* internalIP;
1006
1007 /* make sure controlURL is a valid URL */
1008 if(!gaim_upnp_validate_url(controlURL)) {
1009 gaim_debug_info("upnp",
1010 "gaim_upnp_set_port_mapping(): Failed In Validate URL\n\n");
1011 return FALSE;
1012 }
1013
1014 /* get the internal IP */
1015 if((internalIP = gaim_upnp_get_local_ip_address(controlURL)) == NULL) {
1016 gaim_debug_info("upnp",
1017 "gaim_upnp_set_port_mapping(): couldn't get local ip\n\n");
1018 return FALSE;
1019 }
1020
1021 /* make the portMappingParams variable */
1022 if((actionParams = (char*)malloc(strlen(ADD_PORT_MAPPING_PARAMS) +
1023 strlen(internalIP) +
1024 10 + /* size of port as int * 2 */
1025 strlen(protocol))) == NULL) {
1026 gaim_debug_info("upnp",
1027 "gaim_upnp_set_port_mapping(): Failed MALLOC portMappingParams\n\n");
1028 return FALSE;
1029 }
1030 sprintf(actionParams, ADD_PORT_MAPPING_PARAMS, portMap,
1031 protocol, portMap, internalIP);
1032
1033 httpResponse = gaim_upnp_generate_action_message_and_send(controlURL,
1034 actionName,
1035 actionParams);
1036
1037 if(httpResponse == NULL) {
1038 gaim_debug_info("upnp",
1039 "gaim_upnp_set_port_mapping(): Failed In httpResponse\n\n");
1040 free(actionParams);
1041 return FALSE;
1042 }
1043
1044 /* determine if port mapping was a success */
1045 if(strstr(httpResponse, HTTP_OK) == NULL) {
1046 gaim_debug_info("upnp",
1047 "gaim_upnp_set_port_mapping(): Failed HTTP_OK\n\n%s\n\n", httpResponse);
1048 free(actionParams);
1049 free(httpResponse);
1050 return FALSE;
1051 }
1052
1053 free(actionParams);
1054 free(httpResponse);
1055 return TRUE;
1056 }
1057
1058
1059 gboolean
1060 gaim_upnp_remove_port_mapping(const char* controlURL, unsigned short portMap,
1061 const char* protocol)
1062 {
1063 char* httpResponse;
1064 char actionName[] = "DeletePortMapping";
1065 char* actionParams;
1066
1067 /* make sure controlURL is a valid URL */
1068 if(!gaim_upnp_validate_url(controlURL)) {
1069 gaim_debug_info("upnp",
1070 "gaim_upnp_set_port_mapping(): Failed In Validate URL\n\n");
1071 return FALSE;
1072 }
1073
1074 /* make the portMappingParams variable */
1075 if((actionParams = (char*)malloc(strlen(DELETE_PORT_MAPPING_PARAMS) +
1076 5 + /* size of port as int */
1077 strlen(protocol))) == NULL) {
1078 gaim_debug_info("upnp",
1079 "gaim_upnp_set_port_mapping(): Failed MALLOC portMappingParams\n\n");
1080 return FALSE;
1081 }
1082 sprintf(actionParams, DELETE_PORT_MAPPING_PARAMS,
1083 portMap, protocol);
1084
1085 httpResponse = gaim_upnp_generate_action_message_and_send(controlURL,
1086 actionName,
1087 actionParams);
1088
1089 if(httpResponse == NULL) {
1090 gaim_debug_info("upnp",
1091 "gaim_upnp_set_port_mapping(): Failed In httpResponse\n\n");
1092 free(actionParams);
1093 return FALSE;
1094 }
1095
1096 /* determine if port mapping was a success */
1097 if(strstr(httpResponse, HTTP_OK) == NULL) {
1098 gaim_debug_info("upnp",
1099 "gaim_upnp_set_port_mapping(): Failed HTTP_OK\n\n%s\n\n", httpResponse);
1100 free(actionParams);
1101 free(httpResponse);
1102 return FALSE;
1103 }
1104
1105 free(actionParams);
1106 free(httpResponse);
1107 return TRUE;
1108 }