libsodium: Needed for Dnscrypto-proxy Release 1.3.0
[tomato.git] / release / src / router / miniupnpd / upnpsoap.c
blob0157f7268b6a4ea06105429ac13f9c81a44a0d54
1 /* $Id: upnpsoap.c,v 1.114 2013/02/06 12:40:25 nanard Exp $ */
2 /* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2012 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <sys/socket.h>
13 #include <unistd.h>
14 #include <syslog.h>
15 #include <sys/types.h>
16 #include <netinet/in.h>
17 #include <arpa/inet.h>
18 #include <netdb.h>
20 #include "macros.h"
21 #include "config.h"
22 #include "upnpglobalvars.h"
23 #include "upnphttp.h"
24 #include "upnpsoap.h"
25 #include "upnpreplyparse.h"
26 #include "upnpredirect.h"
27 #include "upnppinhole.h"
28 #include "getifaddr.h"
29 #include "getifstats.h"
30 #include "getconnstatus.h"
31 #include "upnpurns.h"
33 static void
34 BuildSendAndCloseSoapResp(struct upnphttp * h,
35 const char * body, int bodylen)
37 static const char beforebody[] =
38 "<?xml version=\"1.0\"?>\r\n"
39 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
40 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
41 "<s:Body>";
43 static const char afterbody[] =
44 "</s:Body>"
45 "</s:Envelope>\r\n";
47 BuildHeader_upnphttp(h, 200, "OK", sizeof(beforebody) - 1
48 + sizeof(afterbody) - 1 + bodylen );
50 memcpy(h->res_buf + h->res_buflen, beforebody, sizeof(beforebody) - 1);
51 h->res_buflen += sizeof(beforebody) - 1;
53 memcpy(h->res_buf + h->res_buflen, body, bodylen);
54 h->res_buflen += bodylen;
56 memcpy(h->res_buf + h->res_buflen, afterbody, sizeof(afterbody) - 1);
57 h->res_buflen += sizeof(afterbody) - 1;
59 SendRespAndClose_upnphttp(h);
62 static void
63 GetConnectionTypeInfo(struct upnphttp * h, const char * action)
65 static const char resp[] =
66 "<u:GetConnectionTypeInfoResponse "
67 "xmlns:u=\"" SERVICE_TYPE_WANIPC "\">"
68 "<NewConnectionType>IP_Routed</NewConnectionType>"
69 "<NewPossibleConnectionTypes>IP_Routed</NewPossibleConnectionTypes>"
70 "</u:GetConnectionTypeInfoResponse>";
71 UNUSED(action);
73 BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
76 static void
77 GetTotalBytesSent(struct upnphttp * h, const char * action)
79 int r;
81 static const char resp[] =
82 "<u:%sResponse "
83 "xmlns:u=\"%s\">"
84 "<NewTotalBytesSent>%lu</NewTotalBytesSent>"
85 "</u:%sResponse>";
87 char body[512];
88 int bodylen;
89 struct ifdata data;
91 r = getifstats(ext_if_name, &data);
92 bodylen = snprintf(body, sizeof(body), resp,
93 action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
94 r<0?0:data.obytes, action);
95 BuildSendAndCloseSoapResp(h, body, bodylen);
98 static void
99 GetTotalBytesReceived(struct upnphttp * h, const char * action)
101 int r;
103 static const char resp[] =
104 "<u:%sResponse "
105 "xmlns:u=\"%s\">"
106 "<NewTotalBytesReceived>%lu</NewTotalBytesReceived>"
107 "</u:%sResponse>";
109 char body[512];
110 int bodylen;
111 struct ifdata data;
113 r = getifstats(ext_if_name, &data);
114 bodylen = snprintf(body, sizeof(body), resp,
115 action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
116 r<0?0:data.ibytes, action);
117 BuildSendAndCloseSoapResp(h, body, bodylen);
120 static void
121 GetTotalPacketsSent(struct upnphttp * h, const char * action)
123 int r;
125 static const char resp[] =
126 "<u:%sResponse "
127 "xmlns:u=\"%s\">"
128 "<NewTotalPacketsSent>%lu</NewTotalPacketsSent>"
129 "</u:%sResponse>";
131 char body[512];
132 int bodylen;
133 struct ifdata data;
135 r = getifstats(ext_if_name, &data);
136 bodylen = snprintf(body, sizeof(body), resp,
137 action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
138 r<0?0:data.opackets, action);
139 BuildSendAndCloseSoapResp(h, body, bodylen);
142 static void
143 GetTotalPacketsReceived(struct upnphttp * h, const char * action)
145 int r;
147 static const char resp[] =
148 "<u:%sResponse "
149 "xmlns:u=\"%s\">"
150 "<NewTotalPacketsReceived>%lu</NewTotalPacketsReceived>"
151 "</u:%sResponse>";
153 char body[512];
154 int bodylen;
155 struct ifdata data;
157 r = getifstats(ext_if_name, &data);
158 bodylen = snprintf(body, sizeof(body), resp,
159 action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
160 r<0?0:data.ipackets, action);
161 BuildSendAndCloseSoapResp(h, body, bodylen);
164 static void
165 GetCommonLinkProperties(struct upnphttp * h, const char * action)
167 /* WANAccessType : set depending on the hardware :
168 * DSL, POTS (plain old Telephone service), Cable, Ethernet */
169 static const char resp[] =
170 "<u:%sResponse "
171 "xmlns:u=\"%s\">"
172 /*"<NewWANAccessType>DSL</NewWANAccessType>"*/
173 "<NewWANAccessType>Cable</NewWANAccessType>"
174 "<NewLayer1UpstreamMaxBitRate>%lu</NewLayer1UpstreamMaxBitRate>"
175 "<NewLayer1DownstreamMaxBitRate>%lu</NewLayer1DownstreamMaxBitRate>"
176 "<NewPhysicalLinkStatus>%s</NewPhysicalLinkStatus>"
177 "</u:%sResponse>";
179 char body[2048];
180 int bodylen;
181 struct ifdata data;
182 const char * status = "Up"; /* Up, Down (Required),
183 * Initializing, Unavailable (Optional) */
184 char ext_ip_addr[INET_ADDRSTRLEN];
186 if((downstream_bitrate == 0) || (upstream_bitrate == 0))
188 if(getifstats(ext_if_name, &data) >= 0)
190 if(downstream_bitrate == 0) downstream_bitrate = data.baudrate;
191 if(upstream_bitrate == 0) upstream_bitrate = data.baudrate;
194 if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0) {
195 status = "Down";
197 bodylen = snprintf(body, sizeof(body), resp,
198 action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
199 upstream_bitrate, downstream_bitrate,
200 status, action);
201 BuildSendAndCloseSoapResp(h, body, bodylen);
204 static void
205 GetStatusInfo(struct upnphttp * h, const char * action)
207 static const char resp[] =
208 "<u:%sResponse "
209 "xmlns:u=\"%s\">"
210 "<NewConnectionStatus>%s</NewConnectionStatus>"
211 "<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>"
212 "<NewUptime>%ld</NewUptime>"
213 "</u:%sResponse>";
215 char body[512];
216 int bodylen;
217 time_t uptime;
218 const char * status;
219 /* ConnectionStatus possible values :
220 * Unconfigured, Connecting, Connected, PendingDisconnect,
221 * Disconnecting, Disconnected */
223 status = get_wan_connection_status_str(ext_if_name);
224 uptime = (time(NULL) - startup_time);
225 bodylen = snprintf(body, sizeof(body), resp,
226 action, SERVICE_TYPE_WANIPC,
227 status, (long)uptime, action);
228 BuildSendAndCloseSoapResp(h, body, bodylen);
231 static void
232 GetNATRSIPStatus(struct upnphttp * h, const char * action)
234 static const char resp[] =
235 "<u:GetNATRSIPStatusResponse "
236 "xmlns:u=\"" SERVICE_TYPE_WANIPC "\">"
237 "<NewRSIPAvailable>0</NewRSIPAvailable>"
238 "<NewNATEnabled>1</NewNATEnabled>"
239 "</u:GetNATRSIPStatusResponse>";
240 UNUSED(action);
241 /* 2.2.9. RSIPAvailable
242 * This variable indicates if Realm-specific IP (RSIP) is available
243 * as a feature on the InternetGatewayDevice. RSIP is being defined
244 * in the NAT working group in the IETF to allow host-NATing using
245 * a standard set of message exchanges. It also allows end-to-end
246 * applications that otherwise break if NAT is introduced
247 * (e.g. IPsec-based VPNs).
248 * A gateway that does not support RSIP should set this variable to 0. */
249 BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
252 static void
253 GetExternalIPAddress(struct upnphttp * h, const char * action)
255 static const char resp[] =
256 "<u:%sResponse "
257 "xmlns:u=\"%s\">"
258 "<NewExternalIPAddress>%s</NewExternalIPAddress>"
259 "</u:%sResponse>";
261 char body[512];
262 int bodylen;
263 char ext_ip_addr[INET_ADDRSTRLEN];
264 /* Does that method need to work with IPv6 ?
265 * There is usually no NAT with IPv6 */
267 #ifndef MULTIPLE_EXTERNAL_IP
268 if(use_ext_ip_addr)
270 strncpy(ext_ip_addr, use_ext_ip_addr, INET_ADDRSTRLEN);
272 else if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0)
274 syslog(LOG_ERR, "Failed to get ip address for interface %s",
275 ext_if_name);
276 strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
278 #else
279 struct lan_addr_s * lan_addr;
280 strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
281 for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
283 if( (h->clientaddr.s_addr & lan_addr->mask.s_addr)
284 == (lan_addr->addr.s_addr & lan_addr->mask.s_addr))
286 strncpy(ext_ip_addr, lan_addr->ext_ip_str, INET_ADDRSTRLEN);
287 break;
290 #endif
291 bodylen = snprintf(body, sizeof(body), resp,
292 action, SERVICE_TYPE_WANIPC,
293 ext_ip_addr, action);
294 BuildSendAndCloseSoapResp(h, body, bodylen);
297 /* AddPortMapping method of WANIPConnection Service
298 * Ignored argument : NewEnabled */
299 static void
300 AddPortMapping(struct upnphttp * h, const char * action)
302 int r;
304 static const char resp[] =
305 "<u:AddPortMappingResponse "
306 "xmlns:u=\"" SERVICE_TYPE_WANIPC "\"/>";
308 struct NameValueParserData data;
309 char * int_ip, * int_port, * ext_port, * protocol, * desc;
310 char * leaseduration_str;
311 unsigned int leaseduration;
312 char * r_host;
313 unsigned short iport, eport;
315 struct hostent *hp; /* getbyhostname() */
316 char ** ptr; /* getbyhostname() */
317 struct in_addr result_ip;/*unsigned char result_ip[16];*/ /* inet_pton() */
319 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
320 int_ip = GetValueFromNameValueList(&data, "NewInternalClient");
321 if (!int_ip)
323 ClearNameValueList(&data);
324 SoapError(h, 402, "Invalid Args");
325 return;
328 /* IGD 2 MUST support both wildcard and specific IP address values
329 * for RemoteHost (only the wildcard value was REQUIRED in release 1.0) */
330 r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
331 #ifndef SUPPORT_REMOTEHOST
332 #ifdef UPNP_STRICT
333 if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*")))
335 ClearNameValueList(&data);
336 SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
337 return;
339 #endif
340 #endif
342 /* if ip not valid assume hostname and convert */
343 if (inet_pton(AF_INET, int_ip, &result_ip) <= 0)
345 hp = gethostbyname(int_ip);
346 if(hp && hp->h_addrtype == AF_INET)
348 for(ptr = hp->h_addr_list; ptr && *ptr; ptr++)
350 int_ip = inet_ntoa(*((struct in_addr *) *ptr));
351 result_ip = *((struct in_addr *) *ptr);
352 /* TODO : deal with more than one ip per hostname */
353 break;
356 else
358 syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip);
359 ClearNameValueList(&data);
360 SoapError(h, 402, "Invalid Args");
361 return;
365 /* check if NewInternalAddress is the client address */
366 if(GETFLAG(SECUREMODEMASK))
368 if(h->clientaddr.s_addr != result_ip.s_addr)
370 syslog(LOG_INFO, "Client %s tried to redirect port to %s",
371 inet_ntoa(h->clientaddr), int_ip);
372 ClearNameValueList(&data);
373 SoapError(h, 718, "ConflictInMappingEntry");
374 return;
378 int_port = GetValueFromNameValueList(&data, "NewInternalPort");
379 ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
380 protocol = GetValueFromNameValueList(&data, "NewProtocol");
381 desc = GetValueFromNameValueList(&data, "NewPortMappingDescription");
382 leaseduration_str = GetValueFromNameValueList(&data, "NewLeaseDuration");
384 if (!int_port || !ext_port || !protocol)
386 ClearNameValueList(&data);
387 SoapError(h, 402, "Invalid Args");
388 return;
391 eport = (unsigned short)atoi(ext_port);
392 iport = (unsigned short)atoi(int_port);
394 leaseduration = leaseduration_str ? atoi(leaseduration_str) : 0;
395 #ifdef IGD_V2
396 /* PortMappingLeaseDuration can be either a value between 1 and
397 * 604800 seconds or the zero value (for infinite lease time).
398 * Note that an infinite lease time can be only set by out-of-band
399 * mechanisms like WWW-administration, remote management or local
400 * management.
401 * If a control point uses the value 0 to indicate an infinite lease
402 * time mapping, it is REQUIRED that gateway uses the maximum value
403 * instead (e.g. 604800 seconds) */
404 if(leaseduration == 0 || leaseduration > 604800)
405 leaseduration = 604800;
406 #endif
408 syslog(LOG_INFO, "%s: ext port %hu to %s:%hu protocol %s for: %s leaseduration=%u rhost=%s",
409 action, eport, int_ip, iport, protocol, desc, leaseduration,
410 r_host ? r_host : "NULL");
412 r = upnp_redirect(r_host, eport, int_ip, iport, protocol, desc, leaseduration);
414 ClearNameValueList(&data);
416 /* possible error codes for AddPortMapping :
417 * 402 - Invalid Args
418 * 501 - Action Failed
419 * 715 - Wildcard not permited in SrcAddr
420 * 716 - Wildcard not permited in ExtPort
421 * 718 - ConflictInMappingEntry
422 * 724 - SamePortValuesRequired (deprecated in IGD v2)
423 * 725 - OnlyPermanentLeasesSupported
424 The NAT implementation only supports permanent lease times on
425 port mappings (deprecated in IGD v2)
426 * 726 - RemoteHostOnlySupportsWildcard
427 RemoteHost must be a wildcard and cannot be a specific IP
428 address or DNS name (deprecated in IGD v2)
429 * 727 - ExternalPortOnlySupportsWildcard
430 ExternalPort must be a wildcard and cannot be a specific port
431 value (deprecated in IGD v2)
432 * 728 - NoPortMapsAvailable
433 There are not enough free prots available to complete the mapping
434 (added in IGD v2)
435 * 729 - ConflictWithOtherMechanisms (added in IGD v2) */
436 switch(r)
438 case 0: /* success */
439 BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
440 break;
441 case -2: /* already redirected */
442 case -3: /* not permitted */
443 SoapError(h, 718, "ConflictInMappingEntry");
444 break;
445 default:
446 SoapError(h, 501, "ActionFailed");
450 /* AddAnyPortMapping was added in WANIPConnection v2 */
451 static void
452 AddAnyPortMapping(struct upnphttp * h, const char * action)
454 int r;
455 static const char resp[] =
456 "<u:%sResponse "
457 "xmlns:u=\"%s\">"
458 "<NewReservedPort>%hu</NewReservedPort>"
459 "</u:%sResponse>";
461 char body[512];
462 int bodylen;
464 struct NameValueParserData data;
465 const char * int_ip, * int_port, * ext_port, * protocol, * desc;
466 const char * r_host;
467 unsigned short iport, eport;
468 const char * leaseduration_str;
469 unsigned int leaseduration;
471 struct hostent *hp; /* getbyhostname() */
472 char ** ptr; /* getbyhostname() */
473 struct in_addr result_ip;/*unsigned char result_ip[16];*/ /* inet_pton() */
475 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
476 r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
477 ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
478 protocol = GetValueFromNameValueList(&data, "NewProtocol");
479 int_port = GetValueFromNameValueList(&data, "NewInternalPort");
480 int_ip = GetValueFromNameValueList(&data, "NewInternalClient");
481 /* NewEnabled */
482 desc = GetValueFromNameValueList(&data, "NewPortMappingDescription");
483 leaseduration_str = GetValueFromNameValueList(&data, "NewLeaseDuration");
485 leaseduration = leaseduration_str ? atoi(leaseduration_str) : 0;
486 if(leaseduration == 0)
487 leaseduration = 604800;
489 if (!int_ip || !ext_port || !int_port)
491 ClearNameValueList(&data);
492 SoapError(h, 402, "Invalid Args");
493 return;
496 eport = (unsigned short)atoi(ext_port);
497 iport = (unsigned short)atoi(int_port);
498 #ifndef SUPPORT_REMOTEHOST
499 #ifdef UPNP_STRICT
500 if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*")))
502 ClearNameValueList(&data);
503 SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
504 return;
506 #endif
507 #endif
509 /* if ip not valid assume hostname and convert */
510 if (inet_pton(AF_INET, int_ip, &result_ip) <= 0)
512 hp = gethostbyname(int_ip);
513 if(hp && hp->h_addrtype == AF_INET)
515 for(ptr = hp->h_addr_list; ptr && *ptr; ptr++)
517 int_ip = inet_ntoa(*((struct in_addr *) *ptr));
518 result_ip = *((struct in_addr *) *ptr);
519 /* TODO : deal with more than one ip per hostname */
520 break;
523 else
525 syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip);
526 ClearNameValueList(&data);
527 SoapError(h, 402, "Invalid Args");
528 return;
532 /* check if NewInternalAddress is the client address */
533 if(GETFLAG(SECUREMODEMASK))
535 if(h->clientaddr.s_addr != result_ip.s_addr)
537 syslog(LOG_INFO, "Client %s tried to redirect port to %s",
538 inet_ntoa(h->clientaddr), int_ip);
539 ClearNameValueList(&data);
540 SoapError(h, 606, "Action not authorized");
541 return;
545 /* TODO : accept a different external port
546 * have some smart strategy to choose the port */
547 for(;;) {
548 r = upnp_redirect(r_host, eport, int_ip, iport, protocol, desc, leaseduration);
549 if(r==-2 && eport < 65535) {
550 eport++;
551 } else {
552 break;
556 ClearNameValueList(&data);
558 switch(r)
560 case 0: /* success */
561 bodylen = snprintf(body, sizeof(body), resp,
562 action, SERVICE_TYPE_WANIPC,
563 eport, action);
564 BuildSendAndCloseSoapResp(h, body, bodylen);
565 break;
566 case -2: /* already redirected */
567 SoapError(h, 718, "ConflictInMappingEntry");
568 break;
569 case -3: /* not permitted */
570 SoapError(h, 606, "Action not authorized");
571 break;
572 default:
573 SoapError(h, 501, "ActionFailed");
577 static void
578 GetSpecificPortMappingEntry(struct upnphttp * h, const char * action)
580 int r;
582 static const char resp[] =
583 "<u:%sResponse "
584 "xmlns:u=\"%s\">"
585 "<NewInternalPort>%u</NewInternalPort>"
586 "<NewInternalClient>%s</NewInternalClient>"
587 "<NewEnabled>1</NewEnabled>"
588 "<NewPortMappingDescription>%s</NewPortMappingDescription>"
589 "<NewLeaseDuration>%u</NewLeaseDuration>"
590 "</u:%sResponse>";
592 char body[1024];
593 int bodylen;
594 struct NameValueParserData data;
595 const char * r_host, * ext_port, * protocol;
596 unsigned short eport, iport;
597 char int_ip[32];
598 char desc[64];
599 unsigned int leaseduration = 0;
601 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
602 r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
603 ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
604 protocol = GetValueFromNameValueList(&data, "NewProtocol");
606 #ifdef UPNP_STRICT
607 if(!ext_port || !protocol || !r_host)
608 #else
609 if(!ext_port || !protocol)
610 #endif
612 ClearNameValueList(&data);
613 SoapError(h, 402, "Invalid Args");
614 return;
616 #ifndef SUPPORT_REMOTEHOST
617 #ifdef UPNP_STRICT
618 if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*")))
620 ClearNameValueList(&data);
621 SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
622 return;
624 #endif
625 #endif
627 eport = (unsigned short)atoi(ext_port);
629 /* TODO : add r_host as an input parameter ...
630 * We prevent several Port Mapping with same external port
631 * but different remoteHost to be set up, so that is not
632 * a priority. */
633 r = upnp_get_redirection_infos(eport, protocol, &iport,
634 int_ip, sizeof(int_ip),
635 desc, sizeof(desc),
636 NULL, 0,
637 &leaseduration);
639 if(r < 0)
641 SoapError(h, 714, "NoSuchEntryInArray");
643 else
645 syslog(LOG_INFO, "%s: rhost='%s' %s %s found => %s:%u desc='%s'",
646 action,
647 r_host ? r_host : "NULL", ext_port, protocol, int_ip,
648 (unsigned int)iport, desc);
649 bodylen = snprintf(body, sizeof(body), resp,
650 action, SERVICE_TYPE_WANIPC,
651 (unsigned int)iport, int_ip, desc, leaseduration,
652 action);
653 BuildSendAndCloseSoapResp(h, body, bodylen);
656 ClearNameValueList(&data);
659 static void
660 DeletePortMapping(struct upnphttp * h, const char * action)
662 int r;
664 static const char resp[] =
665 "<u:DeletePortMappingResponse "
666 "xmlns:u=\"" SERVICE_TYPE_WANIPC "\">"
667 "</u:DeletePortMappingResponse>";
669 struct NameValueParserData data;
670 const char * r_host, * ext_port, * protocol;
671 unsigned short eport;
673 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
674 r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
675 ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
676 protocol = GetValueFromNameValueList(&data, "NewProtocol");
678 #ifdef UPNP_STRICT
679 if(!ext_port || !protocol || !r_host)
680 #else
681 if(!ext_port || !protocol)
682 #endif
684 ClearNameValueList(&data);
685 SoapError(h, 402, "Invalid Args");
686 return;
688 #ifndef SUPPORT_REMOTEHOST
689 #ifdef UPNP_STRICT
690 if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*")))
692 ClearNameValueList(&data);
693 SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
694 return;
696 #endif
697 #endif
699 eport = (unsigned short)atoi(ext_port);
701 /* TODO : if in secure mode, check the IP
702 * Removing a redirection is not a security threat,
703 * just an annoyance for the user using it. So this is not
704 * a priority. */
706 syslog(LOG_INFO, "%s: external port: %hu, protocol: %s",
707 action, eport, protocol);
709 r = upnp_delete_redirection(eport, protocol);
711 if(r < 0)
713 SoapError(h, 714, "NoSuchEntryInArray");
715 else
717 BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
720 ClearNameValueList(&data);
723 /* DeletePortMappingRange was added in IGD spec v2 */
724 static void
725 DeletePortMappingRange(struct upnphttp * h, const char * action)
727 int r = -1;
728 static const char resp[] =
729 "<u:DeletePortMappingRangeResponse "
730 "xmlns:u=\"" SERVICE_TYPE_WANIPC "\">"
731 "</u:DeletePortMappingRangeResponse>";
732 struct NameValueParserData data;
733 const char * protocol;
734 const char * startport_s, * endport_s;
735 unsigned short startport, endport;
736 /*int manage;*/
737 unsigned short * port_list;
738 unsigned int i, number = 0;
739 UNUSED(action);
741 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
742 startport_s = GetValueFromNameValueList(&data, "NewStartPort");
743 endport_s = GetValueFromNameValueList(&data, "NewEndPort");
744 protocol = GetValueFromNameValueList(&data, "NewProtocol");
745 /*manage = atoi(GetValueFromNameValueList(&data, "NewManage"));*/
746 if(startport_s == NULL || endport_s == NULL || protocol == NULL) {
747 SoapError(h, 402, "Invalid Args");
748 ClearNameValueList(&data);
749 return;
751 startport = (unsigned short)atoi(startport_s);
752 endport = (unsigned short)atoi(endport_s);
754 /* possible errors :
755 606 - Action not authorized
756 730 - PortMappingNotFound
757 733 - InconsistentParameter
759 if(startport > endport)
761 SoapError(h, 733, "InconsistentParameter");
762 ClearNameValueList(&data);
763 return;
766 port_list = upnp_get_portmappings_in_range(startport, endport,
767 protocol, &number);
768 for(i = 0; i < number; i++)
770 r = upnp_delete_redirection(port_list[i], protocol);
771 /* TODO : check return value for errors */
773 free(port_list);
774 BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
776 ClearNameValueList(&data);
779 static void
780 GetGenericPortMappingEntry(struct upnphttp * h, const char * action)
782 int r;
784 static const char resp[] =
785 "<u:%sResponse "
786 "xmlns:u=\"%s\">"
787 "<NewRemoteHost>%s</NewRemoteHost>"
788 "<NewExternalPort>%u</NewExternalPort>"
789 "<NewProtocol>%s</NewProtocol>"
790 "<NewInternalPort>%u</NewInternalPort>"
791 "<NewInternalClient>%s</NewInternalClient>"
792 "<NewEnabled>1</NewEnabled>"
793 "<NewPortMappingDescription>%s</NewPortMappingDescription>"
794 "<NewLeaseDuration>%u</NewLeaseDuration>"
795 "</u:%sResponse>";
797 int index = 0;
798 unsigned short eport, iport;
799 const char * m_index;
800 char protocol[4], iaddr[32];
801 char desc[64];
802 char rhost[40];
803 unsigned int leaseduration = 0;
804 struct NameValueParserData data;
806 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
807 m_index = GetValueFromNameValueList(&data, "NewPortMappingIndex");
809 if(!m_index)
811 ClearNameValueList(&data);
812 SoapError(h, 402, "Invalid Args");
813 return;
816 index = (int)atoi(m_index);
818 syslog(LOG_INFO, "%s: index=%d", action, index);
820 rhost[0] = '\0';
821 r = upnp_get_redirection_infos_by_index(index, &eport, protocol, &iport,
822 iaddr, sizeof(iaddr),
823 desc, sizeof(desc),
824 rhost, sizeof(rhost),
825 &leaseduration);
827 if(r < 0)
829 SoapError(h, 713, "SpecifiedArrayIndexInvalid");
831 else
833 int bodylen;
834 char body[2048];
835 bodylen = snprintf(body, sizeof(body), resp,
836 action, SERVICE_TYPE_WANIPC, rhost,
837 (unsigned int)eport, protocol, (unsigned int)iport, iaddr, desc,
838 leaseduration, action);
839 BuildSendAndCloseSoapResp(h, body, bodylen);
842 ClearNameValueList(&data);
845 /* GetListOfPortMappings was added in the IGD v2 specification */
846 static void
847 GetListOfPortMappings(struct upnphttp * h, const char * action)
849 static const char resp_start[] =
850 "<u:%sResponse "
851 "xmlns:u=\"%s\">"
852 "<NewPortListing><![CDATA[";
853 static const char resp_end[] =
854 "]]></NewPortListing>"
855 "</u:%sResponse>";
857 static const char list_start[] =
858 "<p:PortMappingList xmlns:p=\"urn:schemas-upnp-org:gw:WANIPConnection\""
859 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
860 " xsi:schemaLocation=\"urn:schemas-upnp-org:gw:WANIPConnection"
861 " http://www.upnp.org/schemas/gw/WANIPConnection-v2.xsd\">";
862 static const char list_end[] =
863 "</p:PortMappingList>";
865 static const char entry[] =
866 "<p:PortMappingEntry>"
867 "<p:NewRemoteHost>%s</p:NewRemoteHost>"
868 "<p:NewExternalPort>%hu</p:NewExternalPort>"
869 "<p:NewProtocol>%s</p:NewProtocol>"
870 "<p:NewInternalPort>%hu</p:NewInternalPort>"
871 "<p:NewInternalClient>%s</p:NewInternalClient>"
872 "<p:NewEnabled>1</p:NewEnabled>"
873 "<p:NewDescription>%s</p:NewDescription>"
874 "<p:NewLeaseTime>%u</p:NewLeaseTime>"
875 "</p:PortMappingEntry>";
877 char * body;
878 size_t bodyalloc;
879 int bodylen;
881 int r = -1;
882 unsigned short iport;
883 char int_ip[32];
884 char desc[64];
885 char rhost[64];
886 unsigned int leaseduration = 0;
888 struct NameValueParserData data;
889 const char * startport_s, * endport_s;
890 unsigned short startport, endport;
891 const char * protocol;
892 /*int manage;*/
893 const char * number_s;
894 int number;
895 unsigned short * port_list;
896 unsigned int i, list_size = 0;
898 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
899 startport_s = GetValueFromNameValueList(&data, "NewStartPort");
900 endport_s = GetValueFromNameValueList(&data, "NewEndPort");
901 protocol = GetValueFromNameValueList(&data, "NewProtocol");
902 /*manage_s = GetValueFromNameValueList(&data, "NewManage");*/
903 number_s = GetValueFromNameValueList(&data, "NewNumberOfPorts");
904 if(startport_s == NULL || endport_s == NULL || protocol == NULL ||
905 number_s == NULL) {
906 SoapError(h, 402, "Invalid Args");
907 ClearNameValueList(&data);
908 return;
911 startport = (unsigned short)atoi(startport_s);
912 endport = (unsigned short)atoi(endport_s);
913 /*manage = atoi(manage_s);*/
914 number = atoi(number_s);
915 if(number == 0) number = 1000; /* return up to 1000 mappings by default */
917 if(startport > endport)
919 SoapError(h, 733, "InconsistentParameter");
920 ClearNameValueList(&data);
921 return;
924 build the PortMappingList xml document :
926 <p:PortMappingList xmlns:p="urn:schemas-upnp-org:gw:WANIPConnection"
927 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
928 xsi:schemaLocation="urn:schemas-upnp-org:gw:WANIPConnection
929 http://www.upnp.org/schemas/gw/WANIPConnection-v2.xsd">
930 <p:PortMappingEntry>
931 <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
932 <p:NewExternalPort>2345</p:NewExternalPort>
933 <p:NewProtocol>TCP</p:NewProtocol>
934 <p:NewInternalPort>2345</p:NewInternalPort>
935 <p:NewInternalClient>192.168.1.137</p:NewInternalClient>
936 <p:NewEnabled>1</p:NewEnabled>
937 <p:NewDescription>dooom</p:NewDescription>
938 <p:NewLeaseTime>345</p:NewLeaseTime>
939 </p:PortMappingEntry>
940 </p:PortMappingList>
942 bodyalloc = 4096;
943 body = malloc(bodyalloc);
944 if(!body)
946 ClearNameValueList(&data);
947 SoapError(h, 501, "ActionFailed");
948 return;
950 bodylen = snprintf(body, bodyalloc, resp_start,
951 action, SERVICE_TYPE_WANIPC);
952 if(bodylen < 0)
954 SoapError(h, 501, "ActionFailed");
955 free(body);
956 return;
958 memcpy(body+bodylen, list_start, sizeof(list_start));
959 bodylen += (sizeof(list_start) - 1);
961 port_list = upnp_get_portmappings_in_range(startport, endport,
962 protocol, &list_size);
963 /* loop through port mappings */
964 for(i = 0; number > 0 && i < list_size; i++)
966 /* have a margin of 1024 bytes to store the new entry */
967 if((unsigned int)bodylen + 1024 > bodyalloc)
969 char * body_sav = body;
970 bodyalloc += 4096;
971 body = realloc(body, bodyalloc);
972 if(!body)
974 ClearNameValueList(&data);
975 SoapError(h, 501, "ActionFailed");
976 free(body_sav);
977 free(port_list);
978 return;
981 rhost[0] = '\0';
982 r = upnp_get_redirection_infos(port_list[i], protocol, &iport,
983 int_ip, sizeof(int_ip),
984 desc, sizeof(desc),
985 rhost, sizeof(rhost),
986 &leaseduration);
987 if(r == 0)
989 bodylen += snprintf(body+bodylen, bodyalloc-bodylen, entry,
990 rhost, port_list[i], protocol,
991 iport, int_ip, desc, leaseduration);
992 number--;
995 free(port_list);
996 port_list = NULL;
998 memcpy(body+bodylen, list_end, sizeof(list_end));
999 bodylen += (sizeof(list_end) - 1);
1000 bodylen += snprintf(body+bodylen, bodyalloc-bodylen, resp_end,
1001 action);
1002 BuildSendAndCloseSoapResp(h, body, bodylen);
1003 free(body);
1005 ClearNameValueList(&data);
1008 #ifdef ENABLE_L3F_SERVICE
1009 static void
1010 SetDefaultConnectionService(struct upnphttp * h, const char * action)
1012 static const char resp[] =
1013 "<u:SetDefaultConnectionServiceResponse "
1014 "xmlns:u=\"urn:schemas-upnp-org:service:Layer3Forwarding:1\">"
1015 "</u:SetDefaultConnectionServiceResponse>";
1016 struct NameValueParserData data;
1017 char * p;
1018 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1019 p = GetValueFromNameValueList(&data, "NewDefaultConnectionService");
1020 if(p) {
1021 /* 720 InvalidDeviceUUID
1022 * 721 InvalidServiceID
1023 * 723 InvalidConnServiceSelection */
1024 #ifdef UPNP_STRICT
1025 if(0 != memcmp(uuidvalue, p, sizeof("uuid:00000000-0000-0000-0000-000000000000") - 1)) {
1026 SoapError(h, 720, "InvalidDeviceUUID");
1027 } else
1028 #endif
1030 syslog(LOG_INFO, "%s(%s) : Ignored", action, p);
1031 BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
1033 } else {
1034 /* missing argument */
1035 SoapError(h, 402, "Invalid Args");
1037 ClearNameValueList(&data);
1040 static void
1041 GetDefaultConnectionService(struct upnphttp * h, const char * action)
1043 static const char resp[] =
1044 "<u:%sResponse "
1045 "xmlns:u=\"urn:schemas-upnp-org:service:Layer3Forwarding:1\">"
1046 "<NewDefaultConnectionService>%s:WANConnectionDevice:1,"
1047 SERVICE_ID_WANIPC "</NewDefaultConnectionService>"
1048 "</u:%sResponse>";
1049 /* example from UPnP_IGD_Layer3Forwarding 1.0.pdf :
1050 * uuid:44f5824f-c57d-418c-a131-f22b34e14111:WANConnectionDevice:1,
1051 * urn:upnp-org:serviceId:WANPPPConn1 */
1052 char body[1024];
1053 int bodylen;
1055 bodylen = snprintf(body, sizeof(body), resp,
1056 action, uuidvalue, action);
1057 BuildSendAndCloseSoapResp(h, body, bodylen);
1059 #endif
1061 /* Added for compliance with WANIPConnection v2 */
1062 static void
1063 SetConnectionType(struct upnphttp * h, const char * action)
1065 const char * connection_type;
1066 struct NameValueParserData data;
1067 UNUSED(action);
1069 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1070 connection_type = GetValueFromNameValueList(&data, "NewConnectionType");
1071 #ifdef UPNP_STRICT
1072 if(!connection_type) {
1073 ClearNameValueList(&data);
1074 SoapError(h, 402, "Invalid Args");
1075 return;
1077 #endif
1078 /* Unconfigured, IP_Routed, IP_Bridged */
1079 ClearNameValueList(&data);
1080 /* always return a ReadOnly error */
1081 SoapError(h, 731, "ReadOnly");
1084 /* Added for compliance with WANIPConnection v2 */
1085 static void
1086 RequestConnection(struct upnphttp * h, const char * action)
1088 UNUSED(action);
1089 SoapError(h, 606, "Action not authorized");
1092 /* Added for compliance with WANIPConnection v2 */
1093 static void
1094 ForceTermination(struct upnphttp * h, const char * action)
1096 UNUSED(action);
1097 SoapError(h, 606, "Action not authorized");
1101 If a control point calls QueryStateVariable on a state variable that is not
1102 buffered in memory within (or otherwise available from) the service,
1103 the service must return a SOAP fault with an errorCode of 404 Invalid Var.
1105 QueryStateVariable remains useful as a limited test tool but may not be
1106 part of some future versions of UPnP.
1108 static void
1109 QueryStateVariable(struct upnphttp * h, const char * action)
1111 static const char resp[] =
1112 "<u:%sResponse "
1113 "xmlns:u=\"%s\">"
1114 "<return>%s</return>"
1115 "</u:%sResponse>";
1117 char body[512];
1118 int bodylen;
1119 struct NameValueParserData data;
1120 const char * var_name;
1122 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1123 /*var_name = GetValueFromNameValueList(&data, "QueryStateVariable"); */
1124 /*var_name = GetValueFromNameValueListIgnoreNS(&data, "varName");*/
1125 var_name = GetValueFromNameValueList(&data, "varName");
1127 /*syslog(LOG_INFO, "QueryStateVariable(%.40s)", var_name); */
1129 if(!var_name)
1131 SoapError(h, 402, "Invalid Args");
1133 else if(strcmp(var_name, "ConnectionStatus") == 0)
1135 const char * status;
1137 status = get_wan_connection_status_str(ext_if_name);
1138 bodylen = snprintf(body, sizeof(body), resp,
1139 action, "urn:schemas-upnp-org:control-1-0",
1140 status, action);
1141 BuildSendAndCloseSoapResp(h, body, bodylen);
1143 #if 0
1144 /* not usefull */
1145 else if(strcmp(var_name, "ConnectionType") == 0)
1147 bodylen = snprintf(body, sizeof(body), resp, "IP_Routed");
1148 BuildSendAndCloseSoapResp(h, body, bodylen);
1150 else if(strcmp(var_name, "LastConnectionError") == 0)
1152 bodylen = snprintf(body, sizeof(body), resp, "ERROR_NONE");
1153 BuildSendAndCloseSoapResp(h, body, bodylen);
1155 #endif
1156 else if(strcmp(var_name, "PortMappingNumberOfEntries") == 0)
1158 char strn[10];
1159 snprintf(strn, sizeof(strn), "%i",
1160 upnp_get_portmapping_number_of_entries());
1161 bodylen = snprintf(body, sizeof(body), resp,
1162 action, "urn:schemas-upnp-org:control-1-0",
1163 strn, action);
1164 BuildSendAndCloseSoapResp(h, body, bodylen);
1166 else
1168 syslog(LOG_NOTICE, "%s: Unknown: %s", action, var_name?var_name:"");
1169 SoapError(h, 404, "Invalid Var");
1172 ClearNameValueList(&data);
1175 #ifdef ENABLE_6FC_SERVICE
1176 #ifndef ENABLE_IPV6
1177 #error "ENABLE_6FC_SERVICE needs ENABLE_IPV6"
1178 #endif
1179 /* WANIPv6FirewallControl actions */
1180 static void
1181 GetFirewallStatus(struct upnphttp * h, const char * action)
1183 static const char resp[] =
1184 "<u:%sResponse "
1185 "xmlns:u=\"%s\">"
1186 "<FirewallEnabled>%d</FirewallEnabled>"
1187 "<InboundPinholeAllowed>%d</InboundPinholeAllowed>"
1188 "</u:%sResponse>";
1190 char body[512];
1191 int bodylen;
1193 bodylen = snprintf(body, sizeof(body), resp,
1194 action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
1195 ipv6fc_firewall_enabled, ipv6fc_inbound_pinhole_allowed, action);
1196 BuildSendAndCloseSoapResp(h, body, bodylen);
1199 static int
1200 CheckStatus(struct upnphttp * h)
1202 if (!ipv6fc_firewall_enabled)
1204 SoapError(h, 702, "FirewallDisabled");
1205 return 0;
1207 else if(!ipv6fc_inbound_pinhole_allowed)
1209 SoapError(h, 703, "InboundPinholeNotAllowed");
1210 return 0;
1212 else
1213 return 1;
1216 #if 0
1217 static int connecthostport(const char * host, unsigned short port, char * result)
1219 int s, n;
1220 char hostname[INET6_ADDRSTRLEN];
1221 char port_str[8], ifname[8], tmp[4];
1222 struct addrinfo *ai, *p;
1223 struct addrinfo hints;
1225 memset(&hints, 0, sizeof(hints));
1226 /* hints.ai_flags = AI_ADDRCONFIG; */
1227 #ifdef AI_NUMERICSERV
1228 hints.ai_flags = AI_NUMERICSERV;
1229 #endif
1230 hints.ai_socktype = SOCK_STREAM;
1231 hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
1232 /* hints.ai_protocol = IPPROTO_TCP; */
1233 snprintf(port_str, sizeof(port_str), "%hu", port);
1234 strcpy(hostname, host);
1235 if(!strncmp(host, "fe80", 4))
1237 printf("Using an linklocal address\n");
1238 strcpy(ifname, "%");
1239 snprintf(tmp, sizeof(tmp), "%d", linklocal_index);
1240 strcat(ifname, tmp);
1241 strcat(hostname, ifname);
1242 printf("host: %s\n", hostname);
1244 n = getaddrinfo(hostname, port_str, &hints, &ai);
1245 if(n != 0)
1247 fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
1248 return -1;
1250 s = -1;
1251 for(p = ai; p; p = p->ai_next)
1253 #ifdef DEBUG
1254 char tmp_host[256];
1255 char tmp_service[256];
1256 printf("ai_family=%d ai_socktype=%d ai_protocol=%d ai_addrlen=%d\n ",
1257 p->ai_family, p->ai_socktype, p->ai_protocol, p->ai_addrlen);
1258 getnameinfo(p->ai_addr, p->ai_addrlen, tmp_host, sizeof(tmp_host),
1259 tmp_service, sizeof(tmp_service),
1260 NI_NUMERICHOST | NI_NUMERICSERV);
1261 printf(" host=%s service=%s\n", tmp_host, tmp_service);
1262 #endif
1263 inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)p->ai_addr)->sin6_addr), result, INET6_ADDRSTRLEN);
1264 return 0;
1266 freeaddrinfo(ai);
1268 #endif
1270 /* Check the security policy right */
1271 static int
1272 PinholeVerification(struct upnphttp * h, char * int_ip, unsigned short int_port)
1274 int n;
1275 char senderAddr[INET6_ADDRSTRLEN]="";
1276 struct addrinfo hints, *ai, *p;
1277 struct in6_addr result_ip;
1279 /* Pinhole InternalClient address must correspond to the action sender */
1280 syslog(LOG_INFO, "Checking internal IP@ and port (Security policy purpose)");
1282 hints.ai_socktype = SOCK_STREAM;
1283 hints.ai_family = AF_UNSPEC;
1285 /* if ip not valid assume hostname and convert */
1286 if (inet_pton(AF_INET6, int_ip, &result_ip) <= 0)
1288 n = getaddrinfo(int_ip, NULL, &hints, &ai);
1289 if(!n && ai->ai_family == AF_INET6)
1291 for(p = ai; p; p = p->ai_next)
1293 inet_ntop(AF_INET6, (struct in6_addr *) p, int_ip, sizeof(struct in6_addr));
1294 result_ip = *((struct in6_addr *) p);
1295 /* TODO : deal with more than one ip per hostname */
1296 break;
1299 else
1301 syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip);
1302 SoapError(h, 402, "Invalid Args");
1303 return -1;
1305 freeaddrinfo(p);
1308 if(inet_ntop(AF_INET6, &(h->clientaddr_v6), senderAddr, INET6_ADDRSTRLEN) == NULL)
1310 syslog(LOG_ERR, "inet_ntop: %m");
1312 #ifdef DEBUG
1313 printf("\tPinholeVerification:\n\t\tCompare sender @: %s\n\t\t to intClient @: %s\n", senderAddr, int_ip);
1314 #endif
1315 if(strcmp(senderAddr, int_ip) != 0)
1316 if(h->clientaddr_v6.s6_addr != result_ip.s6_addr)
1318 syslog(LOG_INFO, "Client %s tried to access pinhole for internal %s and is not authorized to do it",
1319 senderAddr, int_ip);
1320 SoapError(h, 606, "Action not authorized");
1321 return 0;
1324 /* Pinhole InternalPort must be greater than or equal to 1024 */
1325 if (int_port < 1024)
1327 syslog(LOG_INFO, "Client %s tried to access pinhole with port < 1024 and is not authorized to do it",
1328 senderAddr);
1329 SoapError(h, 606, "Action not authorized");
1330 return 0;
1332 return 1;
1335 static void
1336 AddPinhole(struct upnphttp * h, const char * action)
1338 int r;
1339 static const char resp[] =
1340 "<u:%sResponse "
1341 "xmlns:u=\"%s\">"
1342 "<UniqueID>%d</UniqueID>"
1343 "</u:%sResponse>";
1344 char body[512];
1345 int bodylen;
1346 struct NameValueParserData data;
1347 char * rem_host, * rem_port, * int_ip, * int_port, * protocol, * leaseTime;
1348 int uid = 0;
1349 unsigned short iport, rport;
1350 int ltime;
1351 long proto;
1352 char rem_ip[INET6_ADDRSTRLEN];
1354 if(CheckStatus(h)==0)
1355 return;
1357 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1358 rem_host = GetValueFromNameValueList(&data, "RemoteHost");
1359 rem_port = GetValueFromNameValueList(&data, "RemotePort");
1360 int_ip = GetValueFromNameValueList(&data, "InternalClient");
1361 int_port = GetValueFromNameValueList(&data, "InternalPort");
1362 protocol = GetValueFromNameValueList(&data, "Protocol");
1363 leaseTime = GetValueFromNameValueList(&data, "LeaseTime");
1365 rport = (unsigned short)(rem_port ? atoi(rem_port) : 0);
1366 iport = (unsigned short)(int_port ? atoi(int_port) : 0);
1367 ltime = leaseTime ? atoi(leaseTime) : -1;
1368 errno = 0;
1369 proto = protocol ? strtol(protocol, NULL, 0) : -1;
1370 if(errno != 0 || proto > 65535 || proto < 0)
1372 SoapError(h, 402, "Invalid Args");
1373 goto clear_and_exit;
1375 if(iport == 0)
1377 SoapError(h, 706, "InternalPortWilcardingNotAllowed");
1378 goto clear_and_exit;
1381 /* In particular, [IGD2] RECOMMENDS that unauthenticated and
1382 * unauthorized control points are only allowed to invoke
1383 * this action with:
1384 * - InternalPort value greater than or equal to 1024,
1385 * - InternalClient value equals to the control point's IP address.
1386 * It is REQUIRED that InternalClient cannot be one of IPv6
1387 * addresses used by the gateway. */
1388 if(!int_ip || 0 == strlen(int_ip) || 0 == strcmp(int_ip, "*"))
1390 SoapError(h, 708, "WildCardNotPermittedInSrcIP");
1391 goto clear_and_exit;
1393 /* I guess it is useless to convert int_ip to literal ipv6 address */
1394 /* rem_host should be converted to literal ipv6 : */
1395 if(rem_host)
1397 struct addrinfo *ai, *p;
1398 struct addrinfo hints;
1399 int err;
1400 memset(&hints, 0, sizeof(struct addrinfo));
1401 hints.ai_family = AF_INET6;
1402 /*hints.ai_flags = */
1403 /* hints.ai_protocol = proto; */
1404 err = getaddrinfo(rem_host, rem_port, &hints, &ai);
1405 if(err == 0)
1407 /* take the 1st IPv6 address */
1408 for(p = ai; p; p = p->ai_next)
1410 if(p->ai_family == AF_INET6)
1412 inet_ntop(AF_INET6,
1413 &(((struct sockaddr_in6 *)p->ai_addr)->sin6_addr),
1414 rem_ip, sizeof(rem_ip));
1415 syslog(LOG_INFO, "resolved '%s' to '%s'", rem_host, rem_ip);
1416 rem_host = rem_ip;
1417 break;
1420 freeaddrinfo(ai);
1422 else
1424 syslog(LOG_WARNING, "AddPinhole : getaddrinfo(%s) : %s",
1425 rem_host, gai_strerror(err));
1426 #if 0
1427 SoapError(h, 402, "Invalid Args");
1428 goto clear_and_exit;
1429 #endif
1433 if(proto == 65535)
1435 SoapError(h, 707, "ProtocolWilcardingNotAllowed");
1436 goto clear_and_exit;
1438 if(proto != IPPROTO_UDP && proto != IPPROTO_TCP
1439 #ifdef IPPROTO_UDPITE
1440 && atoi(protocol) != IPPROTO_UDPLITE
1441 #endif
1444 SoapError(h, 705, "ProtocolNotSupported");
1445 goto clear_and_exit;
1447 if(ltime < 1 || ltime > 86400)
1449 syslog(LOG_WARNING, "%s: LeaseTime=%d not supported, (ip=%s)",
1450 action, ltime, int_ip);
1451 SoapError(h, 402, "Invalid Args");
1452 goto clear_and_exit;
1455 if(PinholeVerification(h, int_ip, iport) <= 0)
1456 goto clear_and_exit;
1458 syslog(LOG_INFO, "%s: (inbound) from [%s]:%hu to [%s]:%hu with proto %ld during %d sec",
1459 action, rem_host?rem_host:"any",
1460 rport, int_ip, iport,
1461 proto, ltime);
1463 /* In cases where the RemoteHost, RemotePort, InternalPort,
1464 * InternalClient and Protocol are the same than an existing pinhole,
1465 * but LeaseTime is different, the device MUST extend the existing
1466 * pinhole's lease time and return the UniqueID of the existing pinhole. */
1467 r = upnp_add_inboundpinhole(rem_host, rport, int_ip, iport, proto, ltime, &uid);
1469 switch(r)
1471 case 1: /* success */
1472 bodylen = snprintf(body, sizeof(body),
1473 resp, action,
1474 "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
1475 uid, action);
1476 BuildSendAndCloseSoapResp(h, body, bodylen);
1477 break;
1478 case -1: /* not permitted */
1479 SoapError(h, 701, "PinholeSpaceExhausted");
1480 break;
1481 default:
1482 SoapError(h, 501, "ActionFailed");
1483 break;
1485 /* 606 Action not authorized
1486 * 701 PinholeSpaceExhausted
1487 * 702 FirewallDisabled
1488 * 703 InboundPinholeNotAllowed
1489 * 705 ProtocolNotSupported
1490 * 706 InternalPortWildcardingNotAllowed
1491 * 707 ProtocolWildcardingNotAllowed
1492 * 708 WildCardNotPermittedInSrcIP */
1493 clear_and_exit:
1494 ClearNameValueList(&data);
1497 static void
1498 UpdatePinhole(struct upnphttp * h, const char * action)
1500 static const char resp[] =
1501 "<u:UpdatePinholeResponse "
1502 "xmlns:u=\"urn:schemas-upnp-org:service:WANIPv6FirewallControl:1\">"
1503 "</u:UpdatePinholeResponse>";
1504 struct NameValueParserData data;
1505 const char * uid_str, * leaseTime;
1506 char iaddr[INET6_ADDRSTRLEN];
1507 unsigned short iport;
1508 int ltime;
1509 int uid;
1510 int n;
1512 if(CheckStatus(h)==0)
1513 return;
1515 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1516 uid_str = GetValueFromNameValueList(&data, "UniqueID");
1517 leaseTime = GetValueFromNameValueList(&data, "NewLeaseTime");
1518 uid = uid_str ? atoi(uid_str) : -1;
1519 ltime = leaseTime ? atoi(leaseTime) : -1;
1520 ClearNameValueList(&data);
1522 if(uid < 0 || uid > 65535 || ltime <= 0 || ltime > 86400)
1524 SoapError(h, 402, "Invalid Args");
1525 return;
1528 /* Check that client is not updating an pinhole
1529 * it doesn't have access to, because of its public access */
1530 n = upnp_get_pinhole_info(uid, NULL, 0, NULL,
1531 iaddr, sizeof(iaddr), &iport,
1532 NULL, NULL, NULL);
1533 if (n >= 0)
1535 if(PinholeVerification(h, iaddr, iport) <= 0)
1536 return;
1538 else if(n == -2)
1540 SoapError(h, 704, "NoSuchEntry");
1541 return;
1543 else
1545 SoapError(h, 501, "ActionFailed");
1546 return;
1549 syslog(LOG_INFO, "%s: (inbound) updating lease duration to %d for pinhole with ID: %d",
1550 action, ltime, uid);
1552 n = upnp_update_inboundpinhole(uid, ltime);
1553 if(n == -1)
1554 SoapError(h, 704, "NoSuchEntry");
1555 else if(n < 0)
1556 SoapError(h, 501, "ActionFailed");
1557 else
1558 BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
1561 static void
1562 GetOutboundPinholeTimeout(struct upnphttp * h, const char * action)
1564 int r;
1566 static const char resp[] =
1567 "<u:%sResponse "
1568 "xmlns:u=\"%s\">"
1569 "<OutboundPinholeTimeout>%d</OutboundPinholeTimeout>"
1570 "</u:%sResponse>";
1572 char body[512];
1573 int bodylen;
1574 struct NameValueParserData data;
1575 char * int_ip, * int_port, * rem_host, * rem_port, * protocol;
1576 int opt=0, proto=0;
1577 unsigned short iport, rport;
1579 if (!ipv6fc_firewall_enabled)
1581 SoapError(h, 702, "FirewallDisabled");
1582 return;
1585 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1586 int_ip = GetValueFromNameValueList(&data, "InternalClient");
1587 int_port = GetValueFromNameValueList(&data, "InternalPort");
1588 rem_host = GetValueFromNameValueList(&data, "RemoteHost");
1589 rem_port = GetValueFromNameValueList(&data, "RemotePort");
1590 protocol = GetValueFromNameValueList(&data, "Protocol");
1592 rport = (unsigned short)atoi(rem_port);
1593 iport = (unsigned short)atoi(int_port);
1594 proto = atoi(protocol);
1596 syslog(LOG_INFO, "%s: retrieving timeout for outbound pinhole from [%s]:%hu to [%s]:%hu protocol %s", action, int_ip, iport,rem_host, rport, protocol);
1598 /* TODO */
1599 r = -1;/*upnp_check_outbound_pinhole(proto, &opt);*/
1601 switch(r)
1603 case 1: /* success */
1604 bodylen = snprintf(body, sizeof(body), resp,
1605 action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
1606 opt, action);
1607 BuildSendAndCloseSoapResp(h, body, bodylen);
1608 break;
1609 case -5: /* Protocol not supported */
1610 SoapError(h, 705, "ProtocolNotSupported");
1611 break;
1612 default:
1613 SoapError(h, 501, "ActionFailed");
1615 ClearNameValueList(&data);
1618 static void
1619 DeletePinhole(struct upnphttp * h, const char * action)
1621 int n;
1623 static const char resp[] =
1624 "<u:DeletePinholeResponse "
1625 "xmlns:u=\"urn:schemas-upnp-org:service:WANIPv6FirewallControl:1\">"
1626 "</u:DeletePinholeResponse>";
1628 struct NameValueParserData data;
1629 const char * uid_str;
1630 char iaddr[INET6_ADDRSTRLEN];
1631 int proto;
1632 unsigned short iport;
1633 unsigned int leasetime;
1634 int uid;
1636 if(CheckStatus(h)==0)
1637 return;
1639 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1640 uid_str = GetValueFromNameValueList(&data, "UniqueID");
1641 uid = uid_str ? atoi(uid_str) : -1;
1642 ClearNameValueList(&data);
1644 if(uid < 0 || uid > 65535)
1646 SoapError(h, 402, "Invalid Args");
1647 return;
1650 /* Check that client is not deleting an pinhole
1651 * it doesn't have access to, because of its public access */
1652 n = upnp_get_pinhole_info(uid, NULL, 0, NULL,
1653 iaddr, sizeof(iaddr), &iport,
1654 &proto, &leasetime, NULL);
1655 if (n >= 0)
1657 if(PinholeVerification(h, iaddr, iport) <= 0)
1658 return;
1660 else if(n == -2)
1662 SoapError(h, 704, "NoSuchEntry");
1663 return;
1665 else
1667 SoapError(h, 501, "ActionFailed");
1668 return;
1671 n = upnp_delete_inboundpinhole(uid);
1672 if(n < 0)
1674 syslog(LOG_INFO, "%s: (inbound) failed to remove pinhole with ID: %d",
1675 action, uid);
1676 SoapError(h, 501, "ActionFailed");
1677 return;
1679 syslog(LOG_INFO, "%s: (inbound) pinhole with ID %d successfully removed",
1680 action, uid);
1681 BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
1684 static void
1685 CheckPinholeWorking(struct upnphttp * h, const char * action)
1687 static const char resp[] =
1688 "<u:%sResponse "
1689 "xmlns:u=\"%s\">"
1690 "<IsWorking>%d</IsWorking>"
1691 "</u:%sResponse>";
1692 char body[512];
1693 int bodylen;
1694 int r;
1695 struct NameValueParserData data;
1696 const char * uid_str;
1697 int uid;
1698 char iaddr[INET6_ADDRSTRLEN];
1699 unsigned short iport;
1700 unsigned int packets;
1702 if(CheckStatus(h)==0)
1703 return;
1705 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1706 uid_str = GetValueFromNameValueList(&data, "UniqueID");
1707 uid = uid_str ? atoi(uid_str) : -1;
1708 ClearNameValueList(&data);
1710 if(uid < 0 || uid > 65535)
1712 SoapError(h, 402, "Invalid Args");
1713 return;
1716 /* Check that client is not checking a pinhole
1717 * it doesn't have access to, because of its public access */
1718 r = upnp_get_pinhole_info(uid,
1719 NULL, 0, NULL,
1720 iaddr, sizeof(iaddr), &iport,
1721 NULL, NULL, &packets);
1722 if (r >= 0)
1724 if(PinholeVerification(h, iaddr, iport) <= 0)
1725 return ;
1726 if(packets == 0)
1728 SoapError(h, 709, "NoPacketSent");
1729 return;
1731 bodylen = snprintf(body, sizeof(body), resp,
1732 action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
1733 1, action);
1734 BuildSendAndCloseSoapResp(h, body, bodylen);
1736 else if(r == -2)
1737 SoapError(h, 704, "NoSuchEntry");
1738 else
1739 SoapError(h, 501, "ActionFailed");
1742 static void
1743 GetPinholePackets(struct upnphttp * h, const char * action)
1745 static const char resp[] =
1746 "<u:%sResponse "
1747 "xmlns:u=\"%s\">"
1748 "<PinholePackets>%u</PinholePackets>"
1749 "</u:%sResponse>";
1750 char body[512];
1751 int bodylen;
1752 struct NameValueParserData data;
1753 const char * uid_str;
1754 int n;
1755 char iaddr[INET6_ADDRSTRLEN];
1756 unsigned short iport;
1757 unsigned int packets = 0;
1758 int uid;
1759 int proto;
1760 unsigned int leasetime;
1762 if(CheckStatus(h)==0)
1763 return;
1765 ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1766 uid_str = GetValueFromNameValueList(&data, "UniqueID");
1767 uid = uid_str ? atoi(uid_str) : -1;
1768 ClearNameValueList(&data);
1770 if(uid < 0 || uid > 65535)
1772 SoapError(h, 402, "Invalid Args");
1773 return;
1776 /* Check that client is not getting infos of a pinhole
1777 * it doesn't have access to, because of its public access */
1778 n = upnp_get_pinhole_info(uid, NULL, 0, NULL,
1779 iaddr, sizeof(iaddr), &iport,
1780 &proto, &leasetime, &packets);
1781 if (n >= 0)
1783 if(PinholeVerification(h, iaddr, iport)<=0)
1784 return ;
1786 #if 0
1787 else if(r == -4 || r == -1)
1789 SoapError(h, 704, "NoSuchEntry");
1791 #endif
1793 bodylen = snprintf(body, sizeof(body), resp,
1794 action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
1795 packets, action);
1796 BuildSendAndCloseSoapResp(h, body, bodylen);
1798 #endif
1801 /* Windows XP as client send the following requests :
1802 * GetConnectionTypeInfo
1803 * GetNATRSIPStatus
1804 * ? GetTotalBytesSent - WANCommonInterfaceConfig
1805 * ? GetTotalBytesReceived - idem
1806 * ? GetTotalPacketsSent - idem
1807 * ? GetTotalPacketsReceived - idem
1808 * GetCommonLinkProperties - idem
1809 * GetStatusInfo - WANIPConnection
1810 * GetExternalIPAddress
1811 * QueryStateVariable / ConnectionStatus!
1813 static const struct
1815 const char * methodName;
1816 void (*methodImpl)(struct upnphttp *, const char *);
1818 soapMethods[] =
1820 /* WANCommonInterfaceConfig */
1821 { "QueryStateVariable", QueryStateVariable},
1822 { "GetTotalBytesSent", GetTotalBytesSent},
1823 { "GetTotalBytesReceived", GetTotalBytesReceived},
1824 { "GetTotalPacketsSent", GetTotalPacketsSent},
1825 { "GetTotalPacketsReceived", GetTotalPacketsReceived},
1826 { "GetCommonLinkProperties", GetCommonLinkProperties},
1827 { "GetStatusInfo", GetStatusInfo},
1828 /* WANIPConnection */
1829 { "GetConnectionTypeInfo", GetConnectionTypeInfo },
1830 { "GetNATRSIPStatus", GetNATRSIPStatus},
1831 { "GetExternalIPAddress", GetExternalIPAddress},
1832 { "AddPortMapping", AddPortMapping},
1833 { "DeletePortMapping", DeletePortMapping},
1834 { "GetGenericPortMappingEntry", GetGenericPortMappingEntry},
1835 { "GetSpecificPortMappingEntry", GetSpecificPortMappingEntry},
1836 /* Required in WANIPConnection:2 */
1837 { "SetConnectionType", SetConnectionType},
1838 { "RequestConnection", RequestConnection},
1839 { "ForceTermination", ForceTermination},
1840 { "AddAnyPortMapping", AddAnyPortMapping},
1841 { "DeletePortMappingRange", DeletePortMappingRange},
1842 { "GetListOfPortMappings", GetListOfPortMappings},
1843 #ifdef ENABLE_L3F_SERVICE
1844 /* Layer3Forwarding */
1845 { "SetDefaultConnectionService", SetDefaultConnectionService},
1846 { "GetDefaultConnectionService", GetDefaultConnectionService},
1847 #endif
1848 #ifdef ENABLE_6FC_SERVICE
1849 /* WANIPv6FirewallControl */
1850 { "GetFirewallStatus", GetFirewallStatus}, /* Required */
1851 { "AddPinhole", AddPinhole}, /* Required */
1852 { "UpdatePinhole", UpdatePinhole}, /* Required */
1853 { "GetOutboundPinholeTimeout", GetOutboundPinholeTimeout}, /* Optional */
1854 { "DeletePinhole", DeletePinhole}, /* Required */
1855 { "CheckPinholeWorking", CheckPinholeWorking}, /* Optional */
1856 { "GetPinholePackets", GetPinholePackets}, /* Required */
1857 #endif
1858 { 0, 0 }
1861 void
1862 ExecuteSoapAction(struct upnphttp * h, const char * action, int n)
1864 char * p;
1865 char * p2;
1866 int i, len, methodlen;
1868 i = 0;
1869 p = strchr(action, '#');
1871 if(p)
1873 p++;
1874 p2 = strchr(p, '"');
1875 if(p2)
1876 methodlen = p2 - p;
1877 else
1878 methodlen = n - (p - action);
1879 /*syslog(LOG_DEBUG, "SoapMethod: %.*s", methodlen, p);*/
1880 while(soapMethods[i].methodName)
1882 len = strlen(soapMethods[i].methodName);
1883 if(strncmp(p, soapMethods[i].methodName, len) == 0)
1885 soapMethods[i].methodImpl(h, soapMethods[i].methodName);
1886 return;
1888 i++;
1891 syslog(LOG_NOTICE, "SoapMethod: Unknown: %.*s", methodlen, p);
1894 SoapError(h, 401, "Invalid Action");
1897 /* Standard Errors:
1899 * errorCode errorDescription Description
1900 * -------- ---------------- -----------
1901 * 401 Invalid Action No action by that name at this service.
1902 * 402 Invalid Args Could be any of the following: not enough in args,
1903 * too many in args, no in arg by that name,
1904 * one or more in args are of the wrong data type.
1905 * 403 Out of Sync Out of synchronization.
1906 * 501 Action Failed May be returned in current state of service
1907 * prevents invoking that action.
1908 * 600-699 TBD Common action errors. Defined by UPnP Forum
1909 * Technical Committee.
1910 * 700-799 TBD Action-specific errors for standard actions.
1911 * Defined by UPnP Forum working committee.
1912 * 800-899 TBD Action-specific errors for non-standard actions.
1913 * Defined by UPnP vendor.
1915 void
1916 SoapError(struct upnphttp * h, int errCode, const char * errDesc)
1918 static const char resp[] =
1919 "<s:Envelope "
1920 "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
1921 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
1922 "<s:Body>"
1923 "<s:Fault>"
1924 "<faultcode>s:Client</faultcode>"
1925 "<faultstring>UPnPError</faultstring>"
1926 "<detail>"
1927 "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">"
1928 "<errorCode>%d</errorCode>"
1929 "<errorDescription>%s</errorDescription>"
1930 "</UPnPError>"
1931 "</detail>"
1932 "</s:Fault>"
1933 "</s:Body>"
1934 "</s:Envelope>";
1936 char body[2048];
1937 int bodylen;
1939 syslog(LOG_INFO, "Returning UPnPError %d: %s", errCode, errDesc);
1940 bodylen = snprintf(body, sizeof(body), resp, errCode, errDesc);
1941 BuildResp2_upnphttp(h, 500, "Internal Server Error", body, bodylen);
1942 SendRespAndClose_upnphttp(h);