1 /* Copyright (c) 2006 Adam Warrington
2 ** $Id$
3 **
4 ** Permission is hereby granted, free of charge, to any person obtaining a copy
5 ** of this software and associated documentation files (the "Software"), to deal
6 ** in the Software without restriction, including without limitation the rights
7 ** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 ** copies of the Software, and to permit persons to whom the Software is
9 ** furnished to do so, subject to the following conditions:
10 **
11 ** The above copyright notice and this permission notice shall be included in
12 ** all copies or substantial portions of the Software.
13 **
14 ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 ** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 ** SOFTWARE.
21 **
22 ******************************************************************************
23 **
24 ** This file defines UPnP functions to control an Internet Gateway Device using
25 ** the Universal Plug and Play protocol.
26 */
27
28 /* define this to deprecate unsecure string warnings in vs2005.net */
29 #define _CRT_SECURE_NO_DEPRECATE 1
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include "libnat.h"
35 #include "http_libnat.h"
36 #include "ssdp.h"
37 #include "os.h" /* for getting the local ip */
38 #include "utility.h"
39 #include "expat.h"
40
41 /***************************************************************
42 ** Discovery/Description Defines *
43 ****************************************************************/
44 #define NUM_SSDP_ATTEMPTS 2
45
46 /* Search target for an SSDP query */
47 #define SSDP_TARGET "urn:schemas-upnp-org:service:%s"
48
49 /* wanip target */
50 #define WANIP_TARGET "WANIPConnection:1"
51
52 /* wanppp target */
53 #define WANPPP_TARGET "WANPPPConnection:1"
54
55 /* Header tag for the location of the description url */
56 #define LOCATION_TAG "LOCATION:"
57
58 /* xml element for control url */
59 #define SERVICE_TYPE_ELEMENT "serviceType"
60 #define BASE_URL_ELEMENT "baseURL"
61 #define CONTROL_URL_ELEMENT "controlURL"
62
63
64 /******************************************************************
65 ** Action Defines *
66 *******************************************************************/
67 #define SOAP_ACTION_TAG "SOAPACTION"
68 #define SOAP_ACTION_VAL "\"" SSDP_TARGET "#%s\""
69
70 #define CONTENT_TYPE_TAG "CONTENT-TYPE"
71
72 #define CONTENT_TYPE_VAL "text/xml ; charset=\"utf-8\""
73
74 #define CONTENT_LENGTH_TAG "Content-Length"
75 #define MAX_CONTENT_STRING_LEN 5
76 #define CONTENT_LENGTH_VAL "%5d"
77
78 #define SOAP_ACTION "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" \
79 "<s:Envelope xmlns:s=" \
80 "\"http://schemas.xmlsoap.org/soap/envelope/\" " \
81 "s:encodingStyle=" \
82 "\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" \
83 "<s:Body>\r\n" \
84 "<u:%s xmlns:u=" \
85 "\"urn:schemas-upnp-org:service:%s\">\r\n%s" \
86 "</u:%s>\r\n" \
87 "</s:Body>\r\n" \
88 "</s:Envelope>\r\n"
89
90 #define GET_PUBLIC_IP_ACTION_NAME "GetExternalIPAddress"
91 #define GET_PUBLIC_IP_RESPONSE_ELEMENT "NewExternalIPAddress"
92 #define SET_PORT_MAPPING_ACTION_NAME "AddPortMapping"
93 #define REMOVE_PORT_MAPPING_ACTION_NAME "DeletePortMapping"
94
95 #define PORT_MAPPING_LEASE_TIME "0"
96
97 #define GET_PUBLIC_IP_PARAMS ""
98 #define SET_PORT_MAPPING_PARAMS "<NewRemoteHost></NewRemoteHost>\r\n" \
99 "<NewExternalPort>%d</NewExternalPort>\r\n"\
100 "<NewProtocol>%s</NewProtocol>\r\n" \
101 "<NewInternalPort>%d</NewInternalPort>\r\n"\
102 "<NewInternalClient>%s" \
103 "</NewInternalClient>\r\n" \
104 "<NewEnabled>1</NewEnabled>\r\n" \
105 "<NewPortMappingDescription>" \
106 "</NewPortMappingDescription>\r\n" \
107 "<NewLeaseDuration>" \
108 PORT_MAPPING_LEASE_TIME \
109 "</NewLeaseDuration>\r\n"
110
111 #define REMOVE_PORT_MAPPING_PARAMS "<NewRemoteHost></NewRemoteHost>\r\n" \
112 "<NewExternalPort>%d" \
113 "</NewExternalPort>\r\n" \
114 "<NewProtocol>%s</NewProtocol>\r\n"
115
116
117 /************************************************************************
118 ** Internal Structures
119 *************************************************************************/
120
121 /* data to pass to xml description parse events */
122 struct XmlDescData
123 {
124 char * control_url; /* control url of the service */
125 char * url_base; /* base of the control url */
126 char * correct_service; /* service type we are looking for */
127
128 int found_url_base; /* 1 if we found a base element */
129 int found_service_type; /* 1 if we found a service type element */
130 int found_correct_service; /* 1 if the service type was the right one */
131 int found_control_url; /* 1 if we found a control url element */
132 };
133 typedef struct XmlDescData XmlDescData;
134
135 /* data to pass to xml get public ip parse events */
136 struct XmlGetPublicIpData
137 {
138 char * ext_ip; /* the external ip address found in the xml */
139 int found_ext_ip; /* will equal 1 when the external ip element is found */
140 };
141 typedef struct XmlGetPublicIpData XmlGetPublicIpData;
142
143
144 /* local file function prototypes */
145 static int Send_Ssdp_Discover(UpnpController * c, char ** ssdp_response);
146 static int Get_Description_Url(const char * ssdp_response, char ** desc_url);
147 static int Get_Description(const char * desc_url, char ** description);
148 static int Get_Control_Url(UpnpController * c, const char * desc_url, const char * description);
149 static int Parse_Url(const char * desc_url, char * host, char * resource, unsigned short int * port);
150 static void Start_Desc_Element(void * userData, const char * name, const char ** atts);
151 static void End_Desc_Element(void * userData, const char * name);
152 static void Data_Desc_Handler(void * userData, const char * s, int len);
153 static void XmlDescData_Free(XmlDescData d);
154 static int Get_Public_Ip_From_Response(const char * response, char * public_ip, int public_ip_size);
155 static void Start_Ip_Element(void * userData, const char * name, const char ** atts);
156 static void End_Ip_Element(void * userData, const char * name);
157 static void Data_Ip_Handler(void * userData, const char * s, int len);
158 static int Create_Action_Message(const char * service_type, const char * action_name, const char * action_params, char ** action_message);
159 static int Send_Action_Message(const UpnpController * c, const char * action_name, const char * body, char ** response);
160 static int Get_Local_Ip(const UpnpController * c, char ** ip_map);
161
162 /********************************************************************************
163 ** UPnP Discover Specific Functions *
164 ********************************************************************************/
165
166 /**
167 * Sends a discovery request to search for a UPnP enabled IGD that
168 * contains the WANIPConnection service or WANPPPConnection that will
169 * allow us to recieve the public IP address of the IGD, and control
170 * it for forwarding ports.
171 *
172 * Return the UpnpController for the IGD in the c parameter.
173 * User should call LNat_Upnp_Free() to free the memory allocated for
174 * this UpnpController object.
175 *
176 * Return OK if everything was successful.
177 */
178 LIBNAT_API
LNat_Upnp_Discover(UpnpController ** c)179 int LNat_Upnp_Discover(UpnpController ** c)
180 {
181 int ret;
182 char * ssdp_response;
183 char * desc_url = NULL;
184 char * description;
185
186 /* allocate space for our controller object, and initialize it's members */
187 *c = (UpnpController *)malloc(sizeof(UpnpController));
188 if(NULL == *c) {
189 return BAD_MALLOC;
190 }
191 (*c)->control_url = NULL;
192 (*c)->service_type = NULL;
193
194 /* send a discovery request, and get a response */
195 if((ret = Send_Ssdp_Discover(*c, &ssdp_response)) != OK) {
196 (void)LNat_Upnp_Controller_Free(c);
197 return ret;
198 }
199
200 /* get the description URL out of the ssdp discovery request */
201 if((ret = Get_Description_Url(ssdp_response, &desc_url)) != OK) {
202 free(ssdp_response);
203 (void)LNat_Upnp_Controller_Free(c);
204 return ret;
205 }
206
207 /* retreive the description from the desc_url */
208 if((ret = Get_Description(desc_url, &description)) != OK) {
209 free(ssdp_response);
210 free(desc_url);
211 (void)LNat_Upnp_Controller_Free(c);
212 return ret;
213 }
214
215 /* get the control_url from the description, and store it in c */
216 if((ret = Get_Control_Url(*c, desc_url, description)) != OK) {
217 free(ssdp_response);
218 free(desc_url);
219 free(description);
220 (void)LNat_Upnp_Controller_Free(c);
221 return ret;
222 }
223
224 free(ssdp_response);
225 free(desc_url);
226 free(description);
227
228 return OK;
229 }
230
231
232 /* Send SSDP discovery requests searching for WANIPConnection and
233 WANPPPConnection services on the local network. Return OK if we
234 can find one. Also set the serviceType parameter of the
235 UpnpController structure. */
Send_Ssdp_Discover(UpnpController * c,char ** ssdp_response)236 static int Send_Ssdp_Discover(UpnpController * c, char ** ssdp_response)
237 {
238 int i;
239 int ret;
240 /* allocate space for our search string */
241 char * search_target = (char *)malloc(strlen(SSDP_TARGET) +
242 strlen(WANPPP_TARGET)+
243 NULL_TERM_LEN);
244 if(NULL == search_target) {
245 return BAD_MALLOC;
246 }
247
248 for(i=0; i<NUM_SSDP_ATTEMPTS; i++) {
249 if(i%2 == 0) {
250 (void)sprintf(search_target, SSDP_TARGET, WANIP_TARGET);
251 } else {
252 (void)sprintf(search_target, SSDP_TARGET, WANPPP_TARGET);
253 }
254 ret = LNat_Ssdp_Discover(search_target, ssdp_response);
255
256 /* if ret == OK, we need to use i with the val it has, so break out */
257 if(ret == OK) {
258 break;
259 }
260 }
261 if(i == NUM_SSDP_ATTEMPTS) {
262 free(search_target);
263 return ret;
264 }
265
266 /* store the service type variable */
267 if(i%2 == 0) {
268 c->service_type = (char *)malloc(strlen(WANIP_TARGET) +
269 NULL_TERM_LEN);
270 if(NULL == c->service_type) {
271 free(search_target);
272 return BAD_MALLOC;
273 }
274 (void)strcpy(c->service_type, WANIP_TARGET);
275 } else {
276 c->service_type = (char *)malloc(strlen(WANPPP_TARGET) +
277 NULL_TERM_LEN);
278 if(NULL == c->service_type) {
279 free(search_target);
280 return BAD_MALLOC;
281 }
282 (void)strcpy(c->service_type, WANPPP_TARGET);
283 }
284 free(search_target);
285
286 return OK;
287 }
288
289
290 /* Get the url to retrieve the upnp description from. This URL is parsed
291 out of the ssdp response retrieved from an ssdp request. */
Get_Description_Url(const char * ssdp_response,char ** desc_url)292 static int Get_Description_Url(const char * ssdp_response, char ** desc_url)
293 {
294 const char * fir_rn;
295 const char * sec_rn;
296 const char * end_loc_tag;
297 int end_loc_offset;
298 int desc_url_malloc_size;
299 char * ssdp_upper_response = (char *)malloc(strlen(ssdp_response) +
300 NULL_TERM_LEN);
301 if(NULL == ssdp_upper_response) {
302 return BAD_MALLOC;
303 }
304 /* parsing done on all caps version */
305 (void)LNat_Str_To_Upper(ssdp_response, ssdp_upper_response);
306
307 /* if we didnt receive an HTTP OK response, return error */
308 if(NULL == strstr(ssdp_response, HTTP_OK)) {
309 free(ssdp_upper_response);
310 return SSDP_HEADER_NOT_OK;
311 }
312
313 /* we will scan for successive pairs of /r/n's, and evaluate
314 the data between them to see if it is the location tag. if
315 it is the location tag, then copy the data between that, and
316 the last /r/n. */
317 fir_rn = ssdp_upper_response;
318 while(1) {
319 if((fir_rn = strstr(fir_rn, "\r\n")) == NULL) {
320 break;
321 }
322 fir_rn += strlen("\r\n");
323 if((sec_rn = strstr(fir_rn, "\r\n")) == NULL) {
324 break;
325 }
326 end_loc_tag = fir_rn + strlen(LOCATION_TAG);
327 end_loc_offset = end_loc_tag - ssdp_upper_response;
328 if(!strncmp(fir_rn, LOCATION_TAG, strlen(LOCATION_TAG))) {
329 desc_url_malloc_size = (sec_rn - end_loc_tag) + NULL_TERM_LEN;
330 *desc_url = (char *)malloc(desc_url_malloc_size);
331 if(NULL == *desc_url) {
332 free(ssdp_upper_response);
333 return BAD_MALLOC;
334 }
335 sscanf(ssdp_response + end_loc_offset, "%s", *desc_url);
336 free(ssdp_upper_response);
337 return OK;
338 }
339 }
340 free(ssdp_upper_response);
341
342 /* now make sure that the url size is appropriate */
343 if(*desc_url && strlen(*desc_url)+NULL_TERM_LEN > MAX_URL_LEN) {
344 free(*desc_url);
345 return UPNP_URL_OVER_MAX_LEN;
346 }
347 return SSDP_NO_LOCATION;
348 }
349
350
351 /* Get the description from the description url by making an http request to it.
352 The caller is responsible for freeing the space allocated for description. */
Get_Description(const char * desc_url,char ** description)353 static int Get_Description(const char * desc_url, char ** description)
354 {
355 int ret = 0;
356 char host[MAX_HOST_LEN];
357 unsigned short int port;
358 char resource[MAX_RESOURCE_LEN];
359 GetMessage * gm;
360
361 if((ret = Parse_Url(desc_url, host, resource, &port)) != OK) {
362 return ret;
363 }
364
365 if((ret = LNat_Generate_Http_Get(host, resource, port, &gm)) != OK) {
366 return ret;
367 }
368 if((ret = LNat_Http_Request_Get(gm, description)) != OK) {
369 (void)LNat_Destroy_Http_Get(&gm);
370 return ret;
371 }
372
373 (void)LNat_Destroy_Http_Get(&gm);
374 return OK;
375 }
376
377
378 /* get the control url from the description, which is in xml format, and
379 set the control_url variable in c equal to the string */
Get_Control_Url(UpnpController * c,const char * desc_url,const char * description)380 static int Get_Control_Url(UpnpController * c, const char * desc_url,
381 const char * description)
382 {
383 int ret;
384 char host[MAX_HOST_LEN];
385 char host_port[MAX_HOST_LEN];
386 unsigned short int port;
387 XML_Parser parser;
388 XmlDescData xdescdat = {0, 0, 0, 0, 0, 0, 0};
389
390 if((ret = Parse_Url(desc_url, host, NULL, &port)) != OK) {
391 return ret;
392 }
393 sprintf(host_port, "%s:%d", host, port);
394
395 parser = XML_ParserCreate(NULL);
396 xdescdat.correct_service = (char *)malloc(strlen(SSDP_TARGET) +
397 strlen(c->service_type) +
398 NULL_TERM_LEN);
399 (void)sprintf(xdescdat.correct_service, SSDP_TARGET, c->service_type);
400
401 XML_SetUserData(parser, &xdescdat);
402 XML_SetElementHandler(parser, Start_Desc_Element, End_Desc_Element);
403 XML_SetCharacterDataHandler(parser, Data_Desc_Handler);
404
405 if(!XML_Parse(parser, description, (int)strlen(description), 1)) {
406 XML_ParserFree(parser);
407 XmlDescData_Free(xdescdat);
408 return UPNP_BAD_DESCRIPTION;
409 }
410
411 /* if we didn't find a control url, then bail */
412 if(NULL == xdescdat.control_url) {
413 XmlDescData_Free(xdescdat);
414 return UPNP_BAD_DESCRIPTION;
415 }
416
417 /* if we didn't find a base url in the description, then it is the
418 same url as the desc_url */
419 if(NULL == xdescdat.url_base) {
420 xdescdat.url_base = (char *)malloc(strlen(host_port) + NULL_TERM_LEN);
421 strcpy(xdescdat.url_base, host_port);
422 }
423
424 /* if the control_url contains the http protocol string,
425 then don't prepend the base url to it */
426 if(strncmp(xdescdat.control_url, HTTP_PROTOCOL_STRING,
427 strlen(HTTP_PROTOCOL_STRING)) == 0) {
428 c->control_url = (char *)malloc(strlen(xdescdat.control_url) +
429 NULL_TERM_LEN);
430 if(NULL == c->control_url) {
431 XmlDescData_Free(xdescdat);
432 return BAD_MALLOC;
433 }
434 strcpy(c->control_url, xdescdat.control_url);
435 } else {
436 c->control_url = (char *)malloc(strlen(xdescdat.url_base)+
437 strlen(xdescdat.control_url) +
438 NULL_TERM_LEN);
439 if(NULL == c->control_url) {
440 XmlDescData_Free(xdescdat);
441 return BAD_MALLOC;
442 }
443 (void)sprintf(c->control_url, "%s%s", xdescdat.url_base, xdescdat.control_url);
444 }
445
446 XmlDescData_Free(xdescdat);
447 XML_ParserFree(parser);
448
449 /* now make sure that the url size is appropriate */
450 if(strlen(c->control_url)+NULL_TERM_LEN > MAX_URL_LEN) {
451 free(c->control_url);
452 c->control_url = NULL;
453 return UPNP_URL_OVER_MAX_LEN;
454 }
455 return OK;
456 }
457
458
XmlDescData_Free(XmlDescData d)459 static void XmlDescData_Free(XmlDescData d)
460 {
461 if(d.control_url != NULL) {
462 free(d.control_url);
463 }
464
465 if(d.url_base != NULL) {
466 free(d.url_base);
467 }
468
469 if(d.correct_service != NULL) {
470 free(d.correct_service);
471 }
472 }
473
474
475 /* parser start element handler for the description xml */
Start_Desc_Element(void * userData,const char * name,const char ** atts)476 static void Start_Desc_Element(void * userData, const char * name,
477 const char ** atts)
478 {
479 XmlDescData * xdescdat = (XmlDescData *)userData;
480 if(strcmp(name, BASE_URL_ELEMENT) == 0) {
481 xdescdat->found_url_base = 1;
482 return;
483 }
484
485 if(strcmp(name, SERVICE_TYPE_ELEMENT) == 0) {
486 xdescdat->found_service_type = 1;
487 return;
488 }
489
490 if(strcmp(name, CONTROL_URL_ELEMENT) == 0) {
491 xdescdat->found_control_url = 1;
492 }
493 }
494
495 /* parser end element handler for the description xml */
End_Desc_Element(void * userData,const char * name)496 static void End_Desc_Element(void * userData, const char * name)
497 {
498 XmlDescData * xdescdat = (XmlDescData *)userData;
499
500 /* if we found the url base, reset the bit */
501 if(xdescdat->found_url_base) {
502 xdescdat->found_url_base = 0;
503 }
504
505 /* if we found a service element, reset the bit */
506 if(xdescdat->found_service_type) {
507 xdescdat->found_service_type = 0;
508 }
509
510 /* only reset the correct service flag if we found the control url */
511 if(xdescdat->found_correct_service && xdescdat->found_control_url) {
512 xdescdat->found_correct_service = 0;
513 }
514
515 /* if we found the control url element, we are done, so reset bit */
516 if(xdescdat->found_control_url) {
517 xdescdat->found_control_url = 0;
518 }
519 }
520
521 /* parser character data handler for the description xml */
Data_Desc_Handler(void * userData,const char * s,int len)522 static void Data_Desc_Handler(void * userData, const char * s, int len)
523 {
524 XmlDescData * xdescdat = (XmlDescData *)userData;
525
526 /* if it is the url base we found, set it */
527 if(xdescdat->found_url_base == 1) {
528 xdescdat->url_base = (char *)malloc(len+NULL_TERM_LEN);
529 if(NULL == xdescdat->url_base) {
530 return;
531 }
532 strncpy(xdescdat->url_base, s, len);
533 xdescdat->url_base[len] = NULL_TERM;
534 return;
535 }
536
537 /* if it is the service_type we found, see if it is the right one */
538 if(xdescdat->found_service_type == 1) {
539 /* if we fond the right one, set found correct service */
540 if(strncmp(xdescdat->correct_service, s, len) == 0) {
541 xdescdat->found_correct_service = 1;
542 }
543 return;
544 }
545
546 /* if we found th control url and have the correct service, store controlurl */
547 if(xdescdat->found_correct_service && xdescdat->found_control_url) {
548 xdescdat->control_url = (char *)malloc(len+NULL_TERM_LEN);
549 if(NULL == xdescdat->control_url) {
550 return;
551 }
552 strncpy(xdescdat->control_url, s, len);
553 xdescdat->control_url[len] = NULL_TERM;
554 return;
555 }
556 }
557
558
559
560 /********************************************************************************
561 ** UPnP Get Public IP Functions *
562 ********************************************************************************/
563
564 /**
565 * Gets the IP address from a UPnP enabled IGD that sits on the local
566 * network, so when getting the network IP, instead of returning the
567 * local network IP, the public IP is retrieved.
568 *
569 * controller: The UpnpController retrieved from LNat_Upnp_Discover.
570 *
571 * return the IP address of the network in a null terminated string
572 * in the public_ip parameter or NULL if something went wrong.
573 *
574 * Return OK if everything was successful.
575 */
576 LIBNAT_API
LNat_Upnp_Get_Public_Ip(const UpnpController * c,char * public_ip,int public_ip_size)577 int LNat_Upnp_Get_Public_Ip(const UpnpController * c,
578 char * public_ip, int public_ip_size)
579 {
580 int ret;
581 char * body;
582 char * response;
583
584 /* create the body of the http post request */
585 ret = Create_Action_Message(c->service_type, GET_PUBLIC_IP_ACTION_NAME,
586 GET_PUBLIC_IP_PARAMS, &body);
587 if(OK != ret) {
588 return ret;
589 }
590
591 ret = Send_Action_Message(c, GET_PUBLIC_IP_ACTION_NAME, body, &response);
592 if(OK != ret) {
593 free(body);
594 return ret;
595 }
596 free(body);
597
598 ret = Get_Public_Ip_From_Response(response, public_ip, public_ip_size);
599 if(OK != ret) {
600 free(response);
601 return ret;
602 }
603 free(response);
604
605 return OK;
606 }
607
608 /* we have a response of the ip address, so parse it out of the xml */
Get_Public_Ip_From_Response(const char * response,char * public_ip,int public_ip_size)609 static int Get_Public_Ip_From_Response(const char * response,
610 char * public_ip, int public_ip_size)
611 {
612 XmlGetPublicIpData dat = {0, 0};
613 XML_Parser parser = XML_ParserCreate(NULL);
614
615 XML_SetUserData(parser, &dat);
616 XML_SetElementHandler(parser, Start_Ip_Element, End_Ip_Element);
617 XML_SetCharacterDataHandler(parser, Data_Ip_Handler);
618
619 if(!XML_Parse(parser, response, (int)strlen(response), 1)) {
620 XML_ParserFree(parser);
621 return UPNP_BAD_PUBLIC_IP_RESPONSE;
622 }
623
624 if(NULL == dat.ext_ip) {
625 return UPNP_GET_PUBLIC_IP_FAILED;
626 }
627 strncpy(public_ip, dat.ext_ip, public_ip_size - NULL_TERM_LEN);
628 public_ip[public_ip_size - NULL_TERM_LEN] = NULL_TERM;
629
630 free(dat.ext_ip);
631 XML_ParserFree(parser);
632 return OK;
633 }
634
635
Start_Ip_Element(void * userData,const char * name,const char ** atts)636 static void Start_Ip_Element(void * userData, const char * name,
637 const char ** atts)
638 {
639 XmlGetPublicIpData * dat = userData;
640 if(strcmp(name, GET_PUBLIC_IP_RESPONSE_ELEMENT) == 0) {
641 dat->found_ext_ip = 1;
642 }
643 }
644
645
End_Ip_Element(void * userData,const char * name)646 static void End_Ip_Element(void * userData, const char * name)
647 {
648 XmlGetPublicIpData * dat = userData;
649 dat->found_ext_ip = 0;
650 }
651
652
Data_Ip_Handler(void * userData,const char * s,int len)653 static void Data_Ip_Handler(void * userData, const char * s, int len)
654 {
655 XmlGetPublicIpData * dat = userData;
656 if(dat->found_ext_ip == 1) {
657 dat->ext_ip = (char *)malloc(len+NULL_TERM_LEN);
658 // TODO insert a check if the malloc fails
659 strncpy(dat->ext_ip, s, len);
660 dat->ext_ip[len] = NULL_TERM;
661 }
662 }
663
664
665
666 /********************************************************************************
667 ** UPnP Port Mapping Functions *
668 ********************************************************************************/
669
670
671 /**
672 * Maps Ports in a UPnP enabled IGD that sits on the local network to.
673 * Essentially, this function takes care of the port forwarding so things
674 * like file transfers can work behind NAT firewalls.
675 *
676 * controller: The UpnpController retrieved from LNat_Upnp_Discover.
677 * ipMap: The ip to add this mapping for. If NULL, this function will
678 * attempt to determine the IP address of this machine.
679 * portMap: The port to map to this client
680 * protocol: The protocol to map, either "TCP" or "UDP"
681 *
682 * return OK if successful.
683 */
684 LIBNAT_API
LNat_Upnp_Set_Port_Mapping(const UpnpController * c,const char * ip_map,short int port_map,const char * protocol)685 int LNat_Upnp_Set_Port_Mapping(const UpnpController * c,
686 const char * ip_map,
687 short int port_map,
688 const char* protocol)
689 {
690 int ret;
691 char * body;
692 char * response;
693 char * params;
694 char * local_ip = NULL;
695
696 /* if ipMap is null, attempt to get own ip address */
697 if(ip_map == NULL) {
698 if((ret = Get_Local_Ip(c, &local_ip)) != OK) {
699 return ret;
700 }
701 ip_map = local_ip;
702 }
703
704 params = (char *)malloc(strlen(SET_PORT_MAPPING_PARAMS) +
705 MAX_PORT_STRING_LEN +
706 strlen(protocol) +
707 MAX_PORT_STRING_LEN +
708 strlen(ip_map));
709 if(NULL == params) {
710 free(local_ip);
711 return BAD_MALLOC;
712 }
713 (void)sprintf(params, SET_PORT_MAPPING_PARAMS, port_map,
714 protocol, port_map, ip_map);
715
716 /* create the body of the http post request */
717 ret = Create_Action_Message(c->service_type, SET_PORT_MAPPING_ACTION_NAME,
718 params, &body);
719 if(OK != ret) {
720 free(local_ip);
721 free(params);
722 return ret;
723 }
724 free(params);
725
726 ret = Send_Action_Message(c, SET_PORT_MAPPING_ACTION_NAME, body, &response);
727 if(OK != ret) {
728 free(local_ip);
729 free(body);
730 return ret;
731 }
732 free(local_ip);
733 free(body);
734 free(response);
735
736 return OK;
737 }
738
Get_Local_Ip(const UpnpController * c,char ** ip_map)739 static int Get_Local_Ip(const UpnpController * c, char ** ip_map)
740 {
741 int ret;
742 OsSocket * s;
743 char host[MAX_HOST_LEN];
744 char resource[MAX_RESOURCE_LEN];
745 unsigned short int port;
746
747 /* parse the host, resource, and port out of the url */
748 ret = Parse_Url(c->control_url, host, resource, &port);
749 if(OK != ret) {
750 return ret;
751 }
752
753 if((ret = LNat_Os_Socket_Connect(&s, host, port, 2)) != OK) {
754 return ret;
755 }
756
757 if((ret = LNat_Os_Get_Local_Ip(s, ip_map)) != OK) {
758 return ret;
759 }
760
761 (void)LNat_Os_Socket_Close(&s);
762 return OK;
763 }
764
765
766 /**
767 * Deletes a port mapping in a UPnP enabled IGD that sits on the local network.
768 * Essentially, this function takes care of deleting the port forwarding after
769 * they have completed a connection so another client on the local network can
770 * take advantage of the port forwarding
771 *
772 * controller: The UpnpController retrieved from LNat_Upnp_Discover.
773 * ipMap: The ip to delete this mapping for. If NULL, this function will
774 * attempt to determine the IP address of this machine.
775 * portMap: The port to delete the mapping for
776 * protocol : The protocol the port was mapped to. Either "TCP" or "UDP"
777 *
778 * return OK if successful.
779 */
780 LIBNAT_API
LNat_Upnp_Remove_Port_Mapping(const UpnpController * c,short int port_map,const char * protocol)781 int LNat_Upnp_Remove_Port_Mapping(const UpnpController * c,
782 short int port_map,
783 const char* protocol)
784 {
785 int ret;
786 char * body;
787 char * response;
788 char * params;
789
790 params = (char *)malloc(strlen(REMOVE_PORT_MAPPING_PARAMS) +
791 MAX_PORT_STRING_LEN +
792 strlen(protocol));
793 if(NULL == params) {
794 return BAD_MALLOC;
795 }
796 (void)sprintf(params, REMOVE_PORT_MAPPING_PARAMS, port_map, protocol);
797
798 /* create the body of the http post request */
799 ret = Create_Action_Message(c->service_type, REMOVE_PORT_MAPPING_ACTION_NAME,
800 params, &body);
801 if(OK != ret) {
802 free(params);
803 return ret;
804 }
805 free(params);
806
807 ret = Send_Action_Message(c, REMOVE_PORT_MAPPING_ACTION_NAME, body, &response);
808 if(OK != ret) {
809 free(body);
810 return ret;
811 }
812 free(response);
813 free(body);
814
815 return OK;
816 }
817
818
819
820
821 /********************************************************************************
822 ** Other Helper Functions *
823 ********************************************************************************/
824
825 /**
826 * Destroy a UpnpController object that was allocated by LNat_Upnp_Discover.
827 * return OK if successful.
828 */
829 LIBNAT_API
LNat_Upnp_Controller_Free(UpnpController ** controller)830 int LNat_Upnp_Controller_Free(UpnpController ** controller)
831 {
832 if(NULL == *controller) {
833 return OK;
834 }
835 if(NULL != (*controller)->control_url) {
836 free((*controller)->control_url);
837 }
838 if(NULL != (*controller)->service_type) {
839 free((*controller)->service_type);
840 }
841 free(*controller);
842 return OK;
843 }
844
845
846 /* send an action message using http post */
Send_Action_Message(const UpnpController * c,const char * action_name,const char * body,char ** response)847 static int Send_Action_Message(const UpnpController * c,
848 const char * action_name,
849 const char * body,
850 char ** response)
851 {
852 int ret;
853 char host[MAX_HOST_LEN];
854 char resource[MAX_RESOURCE_LEN];
855 unsigned short int port;
856 char * soap_action_header;
857 char * content_length_header;
858 PostMessage * pm;
859
860 /* parse the host, resource, and port out of the url */
861 ret = Parse_Url(c->control_url, host, resource, &port);
862 if(OK != ret) {
863 return ret;
864 }
865
866 /* generate an http_post request */
867 ret = LNat_Generate_Http_Post(host, resource, port, body, &pm);
868 if(OK != ret) {
869 return ret;
870 }
871
872 /* add some entity headers, start with content length */
873 content_length_header = (char *)malloc(MAX_CONTENT_STRING_LEN +
874 NULL_TERM_LEN);
875 if(NULL == content_length_header) {
876 LNat_Destroy_Http_Post(&pm);
877 return BAD_MALLOC;
878 }
879 (void)sprintf(content_length_header, CONTENT_LENGTH_VAL, (int)strlen(body));
880 ret = LNat_Http_Post_Add_Entity_Header(pm, CONTENT_LENGTH_TAG, content_length_header);
881 if(OK != ret) {
882 free(content_length_header);
883 LNat_Destroy_Http_Post(&pm);
884 return ret;
885 }
886 free(content_length_header);
887
888 /* add content type entity header */
889 ret = LNat_Http_Post_Add_Entity_Header(pm, CONTENT_TYPE_TAG, CONTENT_TYPE_VAL);
890 if(OK != ret) {
891 LNat_Destroy_Http_Post(&pm);
892 return ret;
893 }
894
895 /* add soap action entity header */
896 soap_action_header = (char *)malloc(strlen(SOAP_ACTION_VAL) +
897 strlen(c->service_type) +
898 strlen(action_name) +
899 NULL_TERM_LEN);
900 if(NULL == soap_action_header) {
901 LNat_Destroy_Http_Post(&pm);
902 return BAD_MALLOC;
903 }
904 (void)sprintf(soap_action_header, SOAP_ACTION_VAL, c->service_type, action_name);
905 ret = LNat_Http_Post_Add_Entity_Header(pm, SOAP_ACTION_TAG, soap_action_header);
906 if(OK != ret) {
907 free(soap_action_header);
908 LNat_Destroy_Http_Post(&pm);
909 return ret;
910 }
911 free(soap_action_header);
912
913 /* now send the http request */
914 ret = LNat_Http_Request_Post(pm, response);
915 if(OK != ret) {
916 return ret;
917 }
918
919 (void)LNat_Destroy_Http_Post(&pm);
920 return OK;
921 }
922
923
924
925 /* create an action message for one of the action requests, such as
926 Get_Public_Ip, Set_Port_Mapping, and Remove_Port_Mapping */
Create_Action_Message(const char * service_type,const char * action_name,const char * action_params,char ** action_message)927 static int Create_Action_Message(const char * service_type,
928 const char * action_name,
929 const char * action_params,
930 char ** action_message)
931 {
932 *action_message = (char *)malloc(strlen(SOAP_ACTION) +
933 strlen(service_type) +
934 strlen(action_name) +
935 strlen(action_params) +
936 strlen(action_name) +
937 NULL_TERM_LEN);
938 if(NULL == *action_message) {
939 return BAD_MALLOC;
940 }
941
942 (void)sprintf(*action_message, SOAP_ACTION, action_name,
943 service_type, action_params, action_name);
944 return OK;
945 }
946
947
948
949
Parse_Url(const char * url,char * host,char * resource,unsigned short int * port)950 static int Parse_Url(const char * url, char * host,
951 char * resource, unsigned short int * port)
952 {
953 char * loc_of_semicolon;
954 char * loc_of_slash;
955 char * end_of_host_loc;
956 char * loc_of_resource;
957 char * loc_of_port;
958 char url_wo_http[MAX_HOST_LEN];
959
960 if(sscanf(url, "http://%255s", url_wo_http) != 1) {
961 if(sscanf(url, "%255s", url_wo_http) != 1) {
962 return HTTP_INVALID_URL;
963 }
964 }
965
966 loc_of_semicolon = strchr(url_wo_http, ':');
967 loc_of_slash = strchr(url_wo_http, '/');
968
969 /* we need a slash for the resource */
970 if(NULL == loc_of_slash) {
971 return HTTP_INVALID_URL;
972 } else {
973 loc_of_resource = loc_of_slash+1;
974 }
975
976 /* dont necessarily need a port, so if we don't have one default port is 80 */
977 if(NULL == loc_of_semicolon || loc_of_semicolon > loc_of_slash) {
978 loc_of_semicolon = NULL;
979 if(NULL != port) {
980 *port = DEFAULT_HTTP_PORT;
981 }
982 end_of_host_loc = loc_of_slash;
983 } else {
984 /* port is right after the semicolon */
985 loc_of_port = loc_of_semicolon + 1;
986 /* make sure it is not larger than MAX_PORT_STRING_LENGTH */
987 if((loc_of_slash - loc_of_port) > MAX_PORT_STRING_LEN) {
988 return HTTP_INVALID_URL;
989 }
990 end_of_host_loc = loc_of_semicolon;
991 }
992
993 /* store the host */
994 if(NULL != host) {
995 strncpy(host, url_wo_http, end_of_host_loc - url_wo_http);
996 host[end_of_host_loc - url_wo_http] = NULL_TERM;
997 }
998 /* extract the port */
999 if(NULL != loc_of_semicolon) {
1000 if(sscanf(loc_of_port, "%hu", port) != 1) {
1001 *port = DEFAULT_HTTP_PORT;
1002 }
1003 if(*port > MAX_PORT_SIZE) {
1004 return HTTP_INVALID_URL;
1005 }
1006 }
1007 /* extract the resource */
1008 if(NULL != resource) {
1009 strcpy(resource, loc_of_resource);
1010 }
1011
1012 return OK;
1013 }
1014
1015
1016