Mercurial > pidgin.yaz
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 } |