1/* $Id: upnpsoap.c,v 1.135 2015/02/10 15:01:24 nanard Exp $ */
2/* MiniUPnP project
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2015 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <limits.h>
11#include <string.h>
12#include <errno.h>
13#include <sys/socket.h>
14#include <unistd.h>
15#include <syslog.h>
16#include <sys/types.h>
17#include <netinet/in.h>
18#include <arpa/inet.h>
19#include <netdb.h>
20
21#include "macros.h"
22#include "config.h"
23#include "upnpglobalvars.h"
24#include "upnphttp.h"
25#include "upnpsoap.h"
26#include "upnpreplyparse.h"
27#include "upnpredirect.h"
28#include "upnppinhole.h"
29#include "getifaddr.h"
30#include "getifstats.h"
31#include "getconnstatus.h"
32#include "upnpurns.h"
33
34static void
35BuildSendAndCloseSoapResp(struct upnphttp * h,
36                          const char * body, int bodylen)
37{
38	static const char beforebody[] =
39		"<?xml version=\"1.0\"?>\r\n"
40		"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
41		"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
42		"<s:Body>";
43
44	static const char afterbody[] =
45		"</s:Body>"
46		"</s:Envelope>\r\n";
47
48	int r = BuildHeader_upnphttp(h, 200, "OK",  sizeof(beforebody) - 1
49	                             + sizeof(afterbody) - 1 + bodylen );
50
51	if(r >= 0) {
52		memcpy(h->res_buf + h->res_buflen, beforebody, sizeof(beforebody) - 1);
53		h->res_buflen += sizeof(beforebody) - 1;
54
55		memcpy(h->res_buf + h->res_buflen, body, bodylen);
56		h->res_buflen += bodylen;
57
58		memcpy(h->res_buf + h->res_buflen, afterbody, sizeof(afterbody) - 1);
59		h->res_buflen += sizeof(afterbody) - 1;
60	} else {
61		BuildResp2_upnphttp(h, 500, "Internal Server Error", NULL, 0);
62	}
63
64	SendRespAndClose_upnphttp(h);
65}
66
67static void
68GetConnectionTypeInfo(struct upnphttp * h, const char * action)
69{
70	static const char resp[] =
71		"<u:GetConnectionTypeInfoResponse "
72		"xmlns:u=\"" SERVICE_TYPE_WANIPC "\">"
73		"<NewConnectionType>IP_Routed</NewConnectionType>"
74		"<NewPossibleConnectionTypes>IP_Routed</NewPossibleConnectionTypes>"
75		"</u:GetConnectionTypeInfoResponse>";
76	UNUSED(action);
77
78	BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
79}
80
81static void
82GetTotalBytesSent(struct upnphttp * h, const char * action)
83{
84	int r;
85
86	static const char resp[] =
87		"<u:%sResponse "
88		"xmlns:u=\"%s\">"
89		"<NewTotalBytesSent>%lu</NewTotalBytesSent>"
90		"</u:%sResponse>";
91
92	char body[512];
93	int bodylen;
94	struct ifdata data;
95
96	r = getifstats(ext_if_name, &data);
97	bodylen = snprintf(body, sizeof(body), resp,
98	         action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
99             r<0?0:data.obytes, action);
100	BuildSendAndCloseSoapResp(h, body, bodylen);
101}
102
103static void
104GetTotalBytesReceived(struct upnphttp * h, const char * action)
105{
106	int r;
107
108	static const char resp[] =
109		"<u:%sResponse "
110		"xmlns:u=\"%s\">"
111		"<NewTotalBytesReceived>%lu</NewTotalBytesReceived>"
112		"</u:%sResponse>";
113
114	char body[512];
115	int bodylen;
116	struct ifdata data;
117
118	r = getifstats(ext_if_name, &data);
119	bodylen = snprintf(body, sizeof(body), resp,
120	         action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
121	         r<0?0:data.ibytes, action);
122	BuildSendAndCloseSoapResp(h, body, bodylen);
123}
124
125static void
126GetTotalPacketsSent(struct upnphttp * h, const char * action)
127{
128	int r;
129
130	static const char resp[] =
131		"<u:%sResponse "
132		"xmlns:u=\"%s\">"
133		"<NewTotalPacketsSent>%lu</NewTotalPacketsSent>"
134		"</u:%sResponse>";
135
136	char body[512];
137	int bodylen;
138	struct ifdata data;
139
140	r = getifstats(ext_if_name, &data);
141	bodylen = snprintf(body, sizeof(body), resp,
142	         action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
143	         r<0?0:data.opackets, action);
144	BuildSendAndCloseSoapResp(h, body, bodylen);
145}
146
147static void
148GetTotalPacketsReceived(struct upnphttp * h, const char * action)
149{
150	int r;
151
152	static const char resp[] =
153		"<u:%sResponse "
154		"xmlns:u=\"%s\">"
155		"<NewTotalPacketsReceived>%lu</NewTotalPacketsReceived>"
156		"</u:%sResponse>";
157
158	char body[512];
159	int bodylen;
160	struct ifdata data;
161
162	r = getifstats(ext_if_name, &data);
163	bodylen = snprintf(body, sizeof(body), resp,
164	         action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
165	         r<0?0:data.ipackets, action);
166	BuildSendAndCloseSoapResp(h, body, bodylen);
167}
168
169static void
170GetCommonLinkProperties(struct upnphttp * h, const char * action)
171{
172	/* WANAccessType : set depending on the hardware :
173	 * DSL, POTS (plain old Telephone service), Cable, Ethernet */
174	static const char resp[] =
175		"<u:%sResponse "
176		"xmlns:u=\"%s\">"
177		"<NewWANAccessType>%s</NewWANAccessType>"
178		"<NewLayer1UpstreamMaxBitRate>%lu</NewLayer1UpstreamMaxBitRate>"
179		"<NewLayer1DownstreamMaxBitRate>%lu</NewLayer1DownstreamMaxBitRate>"
180		"<NewPhysicalLinkStatus>%s</NewPhysicalLinkStatus>"
181		"</u:%sResponse>";
182
183	char body[2048];
184	int bodylen;
185	struct ifdata data;
186	const char * status = "Up";	/* Up, Down (Required),
187	                             * Initializing, Unavailable (Optional) */
188	const char * wan_access_type = "Cable"; /* DSL, POTS, Cable, Ethernet */
189	char ext_ip_addr[INET_ADDRSTRLEN];
190
191	if((downstream_bitrate == 0) || (upstream_bitrate == 0))
192	{
193		if(getifstats(ext_if_name, &data) >= 0)
194		{
195			if(downstream_bitrate == 0) downstream_bitrate = data.baudrate;
196			if(upstream_bitrate == 0) upstream_bitrate = data.baudrate;
197		}
198	}
199	if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN, NULL, NULL) < 0) {
200		status = "Down";
201	}
202	bodylen = snprintf(body, sizeof(body), resp,
203	    action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
204	    wan_access_type,
205	    upstream_bitrate, downstream_bitrate,
206	    status, action);
207	BuildSendAndCloseSoapResp(h, body, bodylen);
208}
209
210static void
211GetStatusInfo(struct upnphttp * h, const char * action)
212{
213	static const char resp[] =
214		"<u:%sResponse "
215		"xmlns:u=\"%s\">"
216		"<NewConnectionStatus>%s</NewConnectionStatus>"
217		"<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>"
218		"<NewUptime>%ld</NewUptime>"
219		"</u:%sResponse>";
220
221	char body[512];
222	int bodylen;
223	time_t uptime;
224	const char * status;
225	/* ConnectionStatus possible values :
226	 * Unconfigured, Connecting, Connected, PendingDisconnect,
227	 * Disconnecting, Disconnected */
228
229	status = get_wan_connection_status_str(ext_if_name);
230	uptime = (time(NULL) - startup_time);
231	bodylen = snprintf(body, sizeof(body), resp,
232		action, SERVICE_TYPE_WANIPC,
233		status, (long)uptime, action);
234	BuildSendAndCloseSoapResp(h, body, bodylen);
235}
236
237static void
238GetNATRSIPStatus(struct upnphttp * h, const char * action)
239{
240	static const char resp[] =
241		"<u:GetNATRSIPStatusResponse "
242		"xmlns:u=\"" SERVICE_TYPE_WANIPC "\">"
243		"<NewRSIPAvailable>0</NewRSIPAvailable>"
244		"<NewNATEnabled>1</NewNATEnabled>"
245		"</u:GetNATRSIPStatusResponse>";
246	UNUSED(action);
247	/* 2.2.9. RSIPAvailable
248	 * This variable indicates if Realm-specific IP (RSIP) is available
249	 * as a feature on the InternetGatewayDevice. RSIP is being defined
250	 * in the NAT working group in the IETF to allow host-NATing using
251	 * a standard set of message exchanges. It also allows end-to-end
252	 * applications that otherwise break if NAT is introduced
253	 * (e.g. IPsec-based VPNs).
254	 * A gateway that does not support RSIP should set this variable to 0. */
255	BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
256}
257
258static void
259GetExternalIPAddress(struct upnphttp * h, const char * action)
260{
261	static const char resp[] =
262		"<u:%sResponse "
263		"xmlns:u=\"%s\">"
264		"<NewExternalIPAddress>%s</NewExternalIPAddress>"
265		"</u:%sResponse>";
266
267	char body[512];
268	int bodylen;
269	char ext_ip_addr[INET_ADDRSTRLEN];
270	/* Does that method need to work with IPv6 ?
271	 * There is usually no NAT with IPv6 */
272
273#ifndef MULTIPLE_EXTERNAL_IP
274	if(use_ext_ip_addr)
275	{
276		strncpy(ext_ip_addr, use_ext_ip_addr, INET_ADDRSTRLEN);
277	}
278	else if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN, NULL, NULL) < 0)
279	{
280		syslog(LOG_ERR, "Failed to get ip address for interface %s",
281			ext_if_name);
282		strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
283	}
284#else
285	struct lan_addr_s * lan_addr;
286	strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
287	for(lan_addr = lan_addrs.lh_first; lan_addr != NULL; lan_addr = lan_addr->list.le_next)
288	{
289		if( (h->clientaddr.s_addr & lan_addr->mask.s_addr)
290		   == (lan_addr->addr.s_addr & lan_addr->mask.s_addr))
291		{
292			strncpy(ext_ip_addr, lan_addr->ext_ip_str, INET_ADDRSTRLEN);
293			break;
294		}
295	}
296#endif
297	bodylen = snprintf(body, sizeof(body), resp,
298	              action, SERVICE_TYPE_WANIPC,
299				  ext_ip_addr, action);
300	BuildSendAndCloseSoapResp(h, body, bodylen);
301}
302
303/* AddPortMapping method of WANIPConnection Service
304 * Ignored argument : NewEnabled */
305static void
306AddPortMapping(struct upnphttp * h, const char * action)
307{
308	int r;
309
310	static const char resp[] =
311		"<u:AddPortMappingResponse "
312		"xmlns:u=\"" SERVICE_TYPE_WANIPC "\"/>";
313
314	struct NameValueParserData data;
315	char * int_ip, * int_port, * ext_port, * protocol, * desc;
316	char * leaseduration_str;
317	unsigned int leaseduration;
318	char * r_host;
319	unsigned short iport, eport;
320
321	struct hostent *hp; /* getbyhostname() */
322	char ** ptr; /* getbyhostname() */
323	struct in_addr result_ip;/*unsigned char result_ip[16];*/ /* inet_pton() */
324
325	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
326	int_ip = GetValueFromNameValueList(&data, "NewInternalClient");
327	if (!int_ip)
328	{
329		ClearNameValueList(&data);
330		SoapError(h, 402, "Invalid Args");
331		return;
332	}
333
334	/* IGD 2 MUST support both wildcard and specific IP address values
335	 * for RemoteHost (only the wildcard value was REQUIRED in release 1.0) */
336	r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
337#ifndef SUPPORT_REMOTEHOST
338#ifdef UPNP_STRICT
339	if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*")))
340	{
341		ClearNameValueList(&data);
342		SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
343		return;
344	}
345#endif
346#endif
347
348	/* if ip not valid assume hostname and convert */
349	if (inet_pton(AF_INET, int_ip, &result_ip) <= 0)
350	{
351		hp = gethostbyname(int_ip);
352		if(hp && hp->h_addrtype == AF_INET)
353		{
354			for(ptr = hp->h_addr_list; ptr && *ptr; ptr++)
355		   	{
356				int_ip = inet_ntoa(*((struct in_addr *) *ptr));
357				result_ip = *((struct in_addr *) *ptr);
358				/* TODO : deal with more than one ip per hostname */
359				break;
360			}
361		}
362		else
363		{
364			syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip);
365			ClearNameValueList(&data);
366			SoapError(h, 402, "Invalid Args");
367			return;
368		}
369	}
370
371	/* check if NewInternalAddress is the client address */
372	if(GETFLAG(SECUREMODEMASK))
373	{
374		if(h->clientaddr.s_addr != result_ip.s_addr)
375		{
376			syslog(LOG_INFO, "Client %s tried to redirect port to %s",
377			       inet_ntoa(h->clientaddr), int_ip);
378			ClearNameValueList(&data);
379			SoapError(h, 718, "ConflictInMappingEntry");
380			return;
381		}
382	}
383
384	int_port = GetValueFromNameValueList(&data, "NewInternalPort");
385	ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
386	protocol = GetValueFromNameValueList(&data, "NewProtocol");
387	desc = GetValueFromNameValueList(&data, "NewPortMappingDescription");
388	leaseduration_str = GetValueFromNameValueList(&data, "NewLeaseDuration");
389
390	if (!int_port || !ext_port || !protocol)
391	{
392		ClearNameValueList(&data);
393		SoapError(h, 402, "Invalid Args");
394		return;
395	}
396
397	eport = (unsigned short)atoi(ext_port);
398	iport = (unsigned short)atoi(int_port);
399
400	leaseduration = leaseduration_str ? atoi(leaseduration_str) : 0;
401#ifdef IGD_V2
402	/* PortMappingLeaseDuration can be either a value between 1 and
403	 * 604800 seconds or the zero value (for infinite lease time).
404	 * Note that an infinite lease time can be only set by out-of-band
405	 * mechanisms like WWW-administration, remote management or local
406	 * management.
407	 * If a control point uses the value 0 to indicate an infinite lease
408	 * time mapping, it is REQUIRED that gateway uses the maximum value
409	 * instead (e.g. 604800 seconds) */
410	if(leaseduration == 0 || leaseduration > 604800)
411		leaseduration = 604800;
412#endif
413
414	syslog(LOG_INFO, "%s: ext port %hu to %s:%hu protocol %s for: %s leaseduration=%u rhost=%s",
415	       action, eport, int_ip, iport, protocol, desc, leaseduration,
416	       r_host ? r_host : "NULL");
417
418	/* TODO : be compliant with IGD spec for updating existing port mappings.
419	See "WANIPConnection:1 Service Template Version 1.01" 2.2.20.PortMappingDescription :
420	Overwriting Previous / Existing Port Mappings:
421	If the RemoteHost, ExternalPort, PortMappingProtocol and InternalClient are
422	exactly the same as an existing mapping, the existing mapping values for InternalPort,
423	PortMappingDescription, PortMappingEnabled and PortMappingLeaseDuration are
424	overwritten.
425	*/
426	r = upnp_redirect(r_host, eport, int_ip, iport, protocol, desc, leaseduration);
427
428	ClearNameValueList(&data);
429
430	/* possible error codes for AddPortMapping :
431	 * 402 - Invalid Args
432	 * 501 - Action Failed
433	 * 715 - Wildcard not permited in SrcAddr
434	 * 716 - Wildcard not permited in ExtPort
435	 * 718 - ConflictInMappingEntry
436	 * 724 - SamePortValuesRequired (deprecated in IGD v2)
437     * 725 - OnlyPermanentLeasesSupported
438             The NAT implementation only supports permanent lease times on
439             port mappings (deprecated in IGD v2)
440     * 726 - RemoteHostOnlySupportsWildcard
441             RemoteHost must be a wildcard and cannot be a specific IP
442             address or DNS name (deprecated in IGD v2)
443     * 727 - ExternalPortOnlySupportsWildcard
444             ExternalPort must be a wildcard and cannot be a specific port
445             value (deprecated in IGD v2)
446     * 728 - NoPortMapsAvailable
447             There are not enough free prots available to complete the mapping
448             (added in IGD v2)
449	 * 729 - ConflictWithOtherMechanisms (added in IGD v2) */
450	switch(r)
451	{
452	case 0:	/* success */
453		BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
454		break;
455	case -2:	/* already redirected */
456	case -3:	/* not permitted */
457		SoapError(h, 718, "ConflictInMappingEntry");
458		break;
459	default:
460		SoapError(h, 501, "ActionFailed");
461	}
462}
463
464/* AddAnyPortMapping was added in WANIPConnection v2 */
465static void
466AddAnyPortMapping(struct upnphttp * h, const char * action)
467{
468	int r;
469	static const char resp[] =
470		"<u:%sResponse "
471		"xmlns:u=\"%s\">"
472		"<NewReservedPort>%hu</NewReservedPort>"
473		"</u:%sResponse>";
474
475	char body[512];
476	int bodylen;
477
478	struct NameValueParserData data;
479	const char * int_ip, * int_port, * ext_port, * protocol, * desc;
480	const char * r_host;
481	unsigned short iport, eport;
482	const char * leaseduration_str;
483	unsigned int leaseduration;
484
485	struct hostent *hp; /* getbyhostname() */
486	char ** ptr; /* getbyhostname() */
487	struct in_addr result_ip;/*unsigned char result_ip[16];*/ /* inet_pton() */
488
489	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
490	r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
491	ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
492	protocol = GetValueFromNameValueList(&data, "NewProtocol");
493	int_port = GetValueFromNameValueList(&data, "NewInternalPort");
494	int_ip = GetValueFromNameValueList(&data, "NewInternalClient");
495	/* NewEnabled */
496	desc = GetValueFromNameValueList(&data, "NewPortMappingDescription");
497	leaseduration_str = GetValueFromNameValueList(&data, "NewLeaseDuration");
498
499	leaseduration = leaseduration_str ? atoi(leaseduration_str) : 0;
500	if(leaseduration == 0)
501		leaseduration = 604800;
502
503	if (!int_ip || !ext_port || !int_port)
504	{
505		ClearNameValueList(&data);
506		SoapError(h, 402, "Invalid Args");
507		return;
508	}
509
510	eport = (unsigned short)atoi(ext_port);
511	iport = (unsigned short)atoi(int_port);
512#ifndef SUPPORT_REMOTEHOST
513#ifdef UPNP_STRICT
514	if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*")))
515	{
516		ClearNameValueList(&data);
517		SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
518		return;
519	}
520#endif
521#endif
522
523	/* if ip not valid assume hostname and convert */
524	if (inet_pton(AF_INET, int_ip, &result_ip) <= 0)
525	{
526		hp = gethostbyname(int_ip);
527		if(hp && hp->h_addrtype == AF_INET)
528		{
529			for(ptr = hp->h_addr_list; ptr && *ptr; ptr++)
530		   	{
531				int_ip = inet_ntoa(*((struct in_addr *) *ptr));
532				result_ip = *((struct in_addr *) *ptr);
533				/* TODO : deal with more than one ip per hostname */
534				break;
535			}
536		}
537		else
538		{
539			syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip);
540			ClearNameValueList(&data);
541			SoapError(h, 402, "Invalid Args");
542			return;
543		}
544	}
545
546	/* check if NewInternalAddress is the client address */
547	if(GETFLAG(SECUREMODEMASK))
548	{
549		if(h->clientaddr.s_addr != result_ip.s_addr)
550		{
551			syslog(LOG_INFO, "Client %s tried to redirect port to %s",
552			       inet_ntoa(h->clientaddr), int_ip);
553			ClearNameValueList(&data);
554			SoapError(h, 606, "Action not authorized");
555			return;
556		}
557	}
558
559	/* TODO : accept a different external port
560	 * have some smart strategy to choose the port */
561	for(;;) {
562		r = upnp_redirect(r_host, eport, int_ip, iport, protocol, desc, leaseduration);
563		if(r==-2 && eport < 65535) {
564			eport++;
565		} else {
566			break;
567		}
568	}
569
570	ClearNameValueList(&data);
571
572	switch(r)
573	{
574	case 0:	/* success */
575		bodylen = snprintf(body, sizeof(body), resp,
576		              action, SERVICE_TYPE_WANIPC,
577					  eport, action);
578		BuildSendAndCloseSoapResp(h, body, bodylen);
579		break;
580	case -2:	/* already redirected */
581		SoapError(h, 718, "ConflictInMappingEntry");
582		break;
583	case -3:	/* not permitted */
584		SoapError(h, 606, "Action not authorized");
585		break;
586	default:
587		SoapError(h, 501, "ActionFailed");
588	}
589}
590
591static void
592GetSpecificPortMappingEntry(struct upnphttp * h, const char * action)
593{
594	int r;
595
596	static const char resp[] =
597		"<u:%sResponse "
598		"xmlns:u=\"%s\">"
599		"<NewInternalPort>%u</NewInternalPort>"
600		"<NewInternalClient>%s</NewInternalClient>"
601		"<NewEnabled>1</NewEnabled>"
602		"<NewPortMappingDescription>%s</NewPortMappingDescription>"
603		"<NewLeaseDuration>%u</NewLeaseDuration>"
604		"</u:%sResponse>";
605
606	char body[1024];
607	int bodylen;
608	struct NameValueParserData data;
609	const char * r_host, * ext_port, * protocol;
610	unsigned short eport, iport;
611	char int_ip[32];
612	char desc[64];
613	unsigned int leaseduration = 0;
614
615	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
616	r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
617	ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
618	protocol = GetValueFromNameValueList(&data, "NewProtocol");
619
620#ifdef UPNP_STRICT
621	if(!ext_port || !protocol || !r_host)
622#else
623	if(!ext_port || !protocol)
624#endif
625	{
626		ClearNameValueList(&data);
627		SoapError(h, 402, "Invalid Args");
628		return;
629	}
630#ifndef SUPPORT_REMOTEHOST
631#ifdef UPNP_STRICT
632	if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*")))
633	{
634		ClearNameValueList(&data);
635		SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
636		return;
637	}
638#endif
639#endif
640
641	eport = (unsigned short)atoi(ext_port);
642
643	/* TODO : add r_host as an input parameter ...
644	 * We prevent several Port Mapping with same external port
645	 * but different remoteHost to be set up, so that is not
646	 * a priority. */
647	r = upnp_get_redirection_infos(eport, protocol, &iport,
648	                               int_ip, sizeof(int_ip),
649	                               desc, sizeof(desc),
650	                               NULL, 0,
651	                               &leaseduration);
652
653	if(r < 0)
654	{
655		SoapError(h, 714, "NoSuchEntryInArray");
656	}
657	else
658	{
659		syslog(LOG_INFO, "%s: rhost='%s' %s %s found => %s:%u desc='%s'",
660		       action,
661		       r_host ? r_host : "NULL", ext_port, protocol, int_ip,
662		       (unsigned int)iport, desc);
663		bodylen = snprintf(body, sizeof(body), resp,
664				action, SERVICE_TYPE_WANIPC,
665				(unsigned int)iport, int_ip, desc, leaseduration,
666				action);
667		BuildSendAndCloseSoapResp(h, body, bodylen);
668	}
669
670	ClearNameValueList(&data);
671}
672
673static void
674DeletePortMapping(struct upnphttp * h, const char * action)
675{
676	int r;
677
678	static const char resp[] =
679		"<u:DeletePortMappingResponse "
680		"xmlns:u=\"" SERVICE_TYPE_WANIPC "\">"
681		"</u:DeletePortMappingResponse>";
682
683	struct NameValueParserData data;
684	const char * ext_port, * protocol;
685	unsigned short eport;
686#ifdef UPNP_STRICT
687	const char * r_host;
688#endif /* UPNP_STRICT */
689
690	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
691	ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
692	protocol = GetValueFromNameValueList(&data, "NewProtocol");
693#ifdef UPNP_STRICT
694	r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
695#endif /* UPNP_STRICT */
696
697#ifdef UPNP_STRICT
698	if(!ext_port || !protocol || !r_host)
699#else
700	if(!ext_port || !protocol)
701#endif /* UPNP_STRICT */
702	{
703		ClearNameValueList(&data);
704		SoapError(h, 402, "Invalid Args");
705		return;
706	}
707#ifndef SUPPORT_REMOTEHOST
708#ifdef UPNP_STRICT
709	if (r_host && (strlen(r_host) > 0) && (0 != strcmp(r_host, "*")))
710	{
711		ClearNameValueList(&data);
712		SoapError(h, 726, "RemoteHostOnlySupportsWildcard");
713		return;
714	}
715#endif /* UPNP_STRICT */
716#endif /* SUPPORT_REMOTEHOST */
717
718	eport = (unsigned short)atoi(ext_port);
719
720	syslog(LOG_INFO, "%s: external port: %hu, protocol: %s",
721		action, eport, protocol);
722
723	/* if in secure mode, check the IP
724	 * Removing a redirection is not a security threat,
725	 * just an annoyance for the user using it. So this is not
726	 * a priority. */
727	if(GETFLAG(SECUREMODEMASK))
728	{
729		char int_ip[32];
730		struct in_addr int_ip_addr;
731		unsigned short iport;
732		unsigned int leaseduration = 0;
733		r = upnp_get_redirection_infos(eport, protocol, &iport,
734		                               int_ip, sizeof(int_ip),
735		                               NULL, 0, NULL, 0,
736		                               &leaseduration);
737		if(r >= 0)
738		{
739			if(inet_pton(AF_INET, int_ip, &int_ip_addr) > 0)
740			{
741				if(h->clientaddr.s_addr != int_ip_addr.s_addr)
742				{
743					SoapError(h, 606, "Action not authorized");
744					/*SoapError(h, 714, "NoSuchEntryInArray");*/
745					ClearNameValueList(&data);
746					return;
747				}
748			}
749		}
750	}
751
752	r = upnp_delete_redirection(eport, protocol);
753
754	if(r < 0)
755	{
756		SoapError(h, 714, "NoSuchEntryInArray");
757	}
758	else
759	{
760		BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
761	}
762
763	ClearNameValueList(&data);
764}
765
766/* DeletePortMappingRange was added in IGD spec v2 */
767static void
768DeletePortMappingRange(struct upnphttp * h, const char * action)
769{
770	int r = -1;
771	static const char resp[] =
772		"<u:DeletePortMappingRangeResponse "
773		"xmlns:u=\"" SERVICE_TYPE_WANIPC "\">"
774		"</u:DeletePortMappingRangeResponse>";
775	struct NameValueParserData data;
776	const char * protocol;
777	const char * startport_s, * endport_s;
778	unsigned short startport, endport;
779	/*int manage;*/
780	unsigned short * port_list;
781	unsigned int i, number = 0;
782	UNUSED(action);
783
784	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
785	startport_s = GetValueFromNameValueList(&data, "NewStartPort");
786	endport_s = GetValueFromNameValueList(&data, "NewEndPort");
787	protocol = GetValueFromNameValueList(&data, "NewProtocol");
788	/*manage = atoi(GetValueFromNameValueList(&data, "NewManage"));*/
789	if(startport_s == NULL || endport_s == NULL || protocol == NULL) {
790		SoapError(h, 402, "Invalid Args");
791		ClearNameValueList(&data);
792		return;
793	}
794	startport = (unsigned short)atoi(startport_s);
795	endport = (unsigned short)atoi(endport_s);
796
797	/* possible errors :
798	   606 - Action not authorized
799	   730 - PortMappingNotFound
800	   733 - InconsistentParameter
801	 */
802	if(startport > endport)
803	{
804		SoapError(h, 733, "InconsistentParameter");
805		ClearNameValueList(&data);
806		return;
807	}
808
809	syslog(LOG_INFO, "%s: deleting external ports: %hu-%hu, protocol: %s",
810	       action, startport, endport, protocol);
811
812	port_list = upnp_get_portmappings_in_range(startport, endport,
813	                                           protocol, &number);
814	if(number == 0)
815	{
816		SoapError(h, 730, "PortMappingNotFound");
817		ClearNameValueList(&data);
818		return;
819	}
820
821	for(i = 0; i < number; i++)
822	{
823		r = upnp_delete_redirection(port_list[i], protocol);
824		syslog(LOG_INFO, "%s: deleting external port: %hu, protocol: %s: %s",
825		       action, port_list[i], protocol, r < 0 ? "failed" : "ok");
826	}
827	free(port_list);
828	BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
829
830	ClearNameValueList(&data);
831}
832
833static void
834GetGenericPortMappingEntry(struct upnphttp * h, const char * action)
835{
836	int r;
837
838	static const char resp[] =
839		"<u:%sResponse "
840		"xmlns:u=\"%s\">"
841		"<NewRemoteHost>%s</NewRemoteHost>"
842		"<NewExternalPort>%u</NewExternalPort>"
843		"<NewProtocol>%s</NewProtocol>"
844		"<NewInternalPort>%u</NewInternalPort>"
845		"<NewInternalClient>%s</NewInternalClient>"
846		"<NewEnabled>1</NewEnabled>"
847		"<NewPortMappingDescription>%s</NewPortMappingDescription>"
848		"<NewLeaseDuration>%u</NewLeaseDuration>"
849		"</u:%sResponse>";
850
851	long int index = 0;
852	unsigned short eport, iport;
853	const char * m_index;
854	char * endptr;
855	char protocol[4], iaddr[32];
856	char desc[64];
857	char rhost[40];
858	unsigned int leaseduration = 0;
859	struct NameValueParserData data;
860
861	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
862	m_index = GetValueFromNameValueList(&data, "NewPortMappingIndex");
863
864	if(!m_index)
865	{
866		ClearNameValueList(&data);
867		SoapError(h, 402, "Invalid Args");
868		return;
869	}
870	errno = 0;	/* To distinguish success/failure after call */
871	index = strtol(m_index, &endptr, 10);
872	if((errno == ERANGE && (index == LONG_MAX || index == LONG_MIN))
873	   || (errno != 0 && index == 0) || (m_index == endptr))
874	{
875		/* should condition (*endptr != '\0') be also an error ? */
876		if(m_index == endptr)
877			syslog(LOG_WARNING, "%s: no digits were found in <%s>",
878			       "GetGenericPortMappingEntry", "NewPortMappingIndex");
879		else
880			syslog(LOG_WARNING, "%s: strtol('%s'): %m",
881			       "GetGenericPortMappingEntry", m_index);
882		ClearNameValueList(&data);
883		SoapError(h, 402, "Invalid Args");
884		return;
885	}
886
887	syslog(LOG_INFO, "%s: index=%d", action, (int)index);
888
889	rhost[0] = '\0';
890	r = upnp_get_redirection_infos_by_index((int)index, &eport, protocol, &iport,
891                                            iaddr, sizeof(iaddr),
892	                                        desc, sizeof(desc),
893	                                        rhost, sizeof(rhost),
894	                                        &leaseduration);
895
896	if(r < 0)
897	{
898		SoapError(h, 713, "SpecifiedArrayIndexInvalid");
899	}
900	else
901	{
902		int bodylen;
903		char body[2048];
904		bodylen = snprintf(body, sizeof(body), resp,
905			action, SERVICE_TYPE_WANIPC, rhost,
906			(unsigned int)eport, protocol, (unsigned int)iport, iaddr, desc,
907		    leaseduration, action);
908		BuildSendAndCloseSoapResp(h, body, bodylen);
909	}
910
911	ClearNameValueList(&data);
912}
913
914/* GetListOfPortMappings was added in the IGD v2 specification */
915static void
916GetListOfPortMappings(struct upnphttp * h, const char * action)
917{
918	static const char resp_start[] =
919		"<u:%sResponse "
920		"xmlns:u=\"%s\">"
921		"<NewPortListing><![CDATA[";
922	static const char resp_end[] =
923		"]]></NewPortListing>"
924		"</u:%sResponse>";
925
926	static const char list_start[] =
927		"<p:PortMappingList xmlns:p=\"urn:schemas-upnp-org:gw:WANIPConnection\""
928		" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
929		" xsi:schemaLocation=\"urn:schemas-upnp-org:gw:WANIPConnection"
930		" http://www.upnp.org/schemas/gw/WANIPConnection-v2.xsd\">";
931	static const char list_end[] =
932		"</p:PortMappingList>";
933
934	static const char entry[] =
935		"<p:PortMappingEntry>"
936		"<p:NewRemoteHost>%s</p:NewRemoteHost>"
937		"<p:NewExternalPort>%hu</p:NewExternalPort>"
938		"<p:NewProtocol>%s</p:NewProtocol>"
939		"<p:NewInternalPort>%hu</p:NewInternalPort>"
940		"<p:NewInternalClient>%s</p:NewInternalClient>"
941		"<p:NewEnabled>1</p:NewEnabled>"
942		"<p:NewDescription>%s</p:NewDescription>"
943		"<p:NewLeaseTime>%u</p:NewLeaseTime>"
944		"</p:PortMappingEntry>";
945
946	char * body;
947	size_t bodyalloc;
948	int bodylen;
949
950	int r = -1;
951	unsigned short iport;
952	char int_ip[32];
953	char desc[64];
954	char rhost[64];
955	unsigned int leaseduration = 0;
956
957	struct NameValueParserData data;
958	const char * startport_s, * endport_s;
959	unsigned short startport, endport;
960	const char * protocol;
961	/*int manage;*/
962	const char * number_s;
963	int number;
964	unsigned short * port_list;
965	unsigned int i, list_size = 0;
966
967	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
968	startport_s = GetValueFromNameValueList(&data, "NewStartPort");
969	endport_s = GetValueFromNameValueList(&data, "NewEndPort");
970	protocol = GetValueFromNameValueList(&data, "NewProtocol");
971	/*manage_s = GetValueFromNameValueList(&data, "NewManage");*/
972	number_s = GetValueFromNameValueList(&data, "NewNumberOfPorts");
973	if(startport_s == NULL || endport_s == NULL || protocol == NULL ||
974	   number_s == NULL) {
975		SoapError(h, 402, "Invalid Args");
976		ClearNameValueList(&data);
977		return;
978	}
979
980	startport = (unsigned short)atoi(startport_s);
981	endport = (unsigned short)atoi(endport_s);
982	/*manage = atoi(manage_s);*/
983	number = atoi(number_s);
984	if(number == 0) number = 1000;	/* return up to 1000 mappings by default */
985
986	if(startport > endport)
987	{
988		SoapError(h, 733, "InconsistentParameter");
989		ClearNameValueList(&data);
990		return;
991	}
992/*
993build the PortMappingList xml document :
994
995<p:PortMappingList xmlns:p="urn:schemas-upnp-org:gw:WANIPConnection"
996xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
997xsi:schemaLocation="urn:schemas-upnp-org:gw:WANIPConnection
998http://www.upnp.org/schemas/gw/WANIPConnection-v2.xsd">
999<p:PortMappingEntry>
1000<p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
1001<p:NewExternalPort>2345</p:NewExternalPort>
1002<p:NewProtocol>TCP</p:NewProtocol>
1003<p:NewInternalPort>2345</p:NewInternalPort>
1004<p:NewInternalClient>192.168.1.137</p:NewInternalClient>
1005<p:NewEnabled>1</p:NewEnabled>
1006<p:NewDescription>dooom</p:NewDescription>
1007<p:NewLeaseTime>345</p:NewLeaseTime>
1008</p:PortMappingEntry>
1009</p:PortMappingList>
1010*/
1011	bodyalloc = 4096;
1012	body = malloc(bodyalloc);
1013	if(!body)
1014	{
1015		ClearNameValueList(&data);
1016		SoapError(h, 501, "ActionFailed");
1017		return;
1018	}
1019	bodylen = snprintf(body, bodyalloc, resp_start,
1020	              action, SERVICE_TYPE_WANIPC);
1021	if(bodylen < 0)
1022	{
1023		SoapError(h, 501, "ActionFailed");
1024		free(body);
1025		return;
1026	}
1027	memcpy(body+bodylen, list_start, sizeof(list_start));
1028	bodylen += (sizeof(list_start) - 1);
1029
1030	port_list = upnp_get_portmappings_in_range(startport, endport,
1031	                                           protocol, &list_size);
1032	/* loop through port mappings */
1033	for(i = 0; number > 0 && i < list_size; i++)
1034	{
1035		/* have a margin of 1024 bytes to store the new entry */
1036		if((unsigned int)bodylen + 1024 > bodyalloc)
1037		{
1038			char * body_sav = body;
1039			bodyalloc += 4096;
1040			body = realloc(body, bodyalloc);
1041			if(!body)
1042			{
1043				syslog(LOG_CRIT, "realloc(%p, %u) FAILED", body_sav, (unsigned)bodyalloc);
1044				ClearNameValueList(&data);
1045				SoapError(h, 501, "ActionFailed");
1046				free(body_sav);
1047				free(port_list);
1048				return;
1049			}
1050		}
1051		rhost[0] = '\0';
1052		r = upnp_get_redirection_infos(port_list[i], protocol, &iport,
1053		                               int_ip, sizeof(int_ip),
1054		                               desc, sizeof(desc),
1055		                               rhost, sizeof(rhost),
1056		                               &leaseduration);
1057		if(r == 0)
1058		{
1059			bodylen += snprintf(body+bodylen, bodyalloc-bodylen, entry,
1060			                    rhost, port_list[i], protocol,
1061			                    iport, int_ip, desc, leaseduration);
1062			number--;
1063		}
1064	}
1065	free(port_list);
1066	port_list = NULL;
1067
1068	if((bodylen + sizeof(list_end) + 1024) > bodyalloc)
1069	{
1070		char * body_sav = body;
1071		bodyalloc += (sizeof(list_end) + 1024);
1072		body = realloc(body, bodyalloc);
1073		if(!body)
1074		{
1075			syslog(LOG_CRIT, "realloc(%p, %u) FAILED", body_sav, (unsigned)bodyalloc);
1076			ClearNameValueList(&data);
1077			SoapError(h, 501, "ActionFailed");
1078			free(body_sav);
1079			return;
1080		}
1081	}
1082	memcpy(body+bodylen, list_end, sizeof(list_end));
1083	bodylen += (sizeof(list_end) - 1);
1084	bodylen += snprintf(body+bodylen, bodyalloc-bodylen, resp_end,
1085	                    action);
1086	BuildSendAndCloseSoapResp(h, body, bodylen);
1087	free(body);
1088
1089	ClearNameValueList(&data);
1090}
1091
1092#ifdef ENABLE_L3F_SERVICE
1093static void
1094SetDefaultConnectionService(struct upnphttp * h, const char * action)
1095{
1096	static const char resp[] =
1097		"<u:SetDefaultConnectionServiceResponse "
1098		"xmlns:u=\"urn:schemas-upnp-org:service:Layer3Forwarding:1\">"
1099		"</u:SetDefaultConnectionServiceResponse>";
1100	struct NameValueParserData data;
1101	char * p;
1102	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1103	p = GetValueFromNameValueList(&data, "NewDefaultConnectionService");
1104	if(p) {
1105		/* 720 InvalidDeviceUUID
1106		 * 721 InvalidServiceID
1107		 * 723 InvalidConnServiceSelection */
1108#ifdef UPNP_STRICT
1109		char * service;
1110		service = strchr(p, ',');
1111		if(0 != memcmp(uuidvalue_wcd, p, sizeof("uuid:00000000-0000-0000-0000-000000000000") - 1)) {
1112			SoapError(h, 720, "InvalidDeviceUUID");
1113		} else if(service == NULL || 0 != strcmp(service+1, SERVICE_ID_WANIPC)) {
1114			SoapError(h, 721, "InvalidServiceID");
1115		} else
1116#endif
1117		{
1118			syslog(LOG_INFO, "%s(%s) : Ignored", action, p);
1119			BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
1120		}
1121	} else {
1122		/* missing argument */
1123		SoapError(h, 402, "Invalid Args");
1124	}
1125	ClearNameValueList(&data);
1126}
1127
1128static void
1129GetDefaultConnectionService(struct upnphttp * h, const char * action)
1130{
1131	static const char resp[] =
1132		"<u:%sResponse "
1133		"xmlns:u=\"urn:schemas-upnp-org:service:Layer3Forwarding:1\">"
1134		"<NewDefaultConnectionService>%s:WANConnectionDevice:1,"
1135		SERVICE_ID_WANIPC "</NewDefaultConnectionService>"
1136		"</u:%sResponse>";
1137	/* example from UPnP_IGD_Layer3Forwarding 1.0.pdf :
1138	 * uuid:44f5824f-c57d-418c-a131-f22b34e14111:WANConnectionDevice:1,
1139	 * urn:upnp-org:serviceId:WANPPPConn1 */
1140	char body[1024];
1141	int bodylen;
1142
1143	bodylen = snprintf(body, sizeof(body), resp,
1144	                   action, uuidvalue_wcd, action);
1145	BuildSendAndCloseSoapResp(h, body, bodylen);
1146}
1147#endif
1148
1149/* Added for compliance with WANIPConnection v2 */
1150static void
1151SetConnectionType(struct upnphttp * h, const char * action)
1152{
1153#ifdef UPNP_STRICT
1154	const char * connection_type;
1155#endif /* UPNP_STRICT */
1156	struct NameValueParserData data;
1157	UNUSED(action);
1158
1159	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1160#ifdef UPNP_STRICT
1161	connection_type = GetValueFromNameValueList(&data, "NewConnectionType");
1162	if(!connection_type) {
1163		ClearNameValueList(&data);
1164		SoapError(h, 402, "Invalid Args");
1165		return;
1166	}
1167#endif /* UPNP_STRICT */
1168	/* Unconfigured, IP_Routed, IP_Bridged */
1169	ClearNameValueList(&data);
1170	/* always return a ReadOnly error */
1171	SoapError(h, 731, "ReadOnly");
1172}
1173
1174/* Added for compliance with WANIPConnection v2 */
1175static void
1176RequestConnection(struct upnphttp * h, const char * action)
1177{
1178	UNUSED(action);
1179	SoapError(h, 606, "Action not authorized");
1180}
1181
1182/* Added for compliance with WANIPConnection v2 */
1183static void
1184ForceTermination(struct upnphttp * h, const char * action)
1185{
1186	UNUSED(action);
1187	SoapError(h, 606, "Action not authorized");
1188}
1189
1190/*
1191If a control point calls QueryStateVariable on a state variable that is not
1192buffered in memory within (or otherwise available from) the service,
1193the service must return a SOAP fault with an errorCode of 404 Invalid Var.
1194
1195QueryStateVariable remains useful as a limited test tool but may not be
1196part of some future versions of UPnP.
1197*/
1198static void
1199QueryStateVariable(struct upnphttp * h, const char * action)
1200{
1201	static const char resp[] =
1202        "<u:%sResponse "
1203        "xmlns:u=\"%s\">"
1204		"<return>%s</return>"
1205        "</u:%sResponse>";
1206
1207	char body[512];
1208	int bodylen;
1209	struct NameValueParserData data;
1210	const char * var_name;
1211
1212	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1213	/*var_name = GetValueFromNameValueList(&data, "QueryStateVariable"); */
1214	/*var_name = GetValueFromNameValueListIgnoreNS(&data, "varName");*/
1215	var_name = GetValueFromNameValueList(&data, "varName");
1216
1217	/*syslog(LOG_INFO, "QueryStateVariable(%.40s)", var_name); */
1218
1219	if(!var_name)
1220	{
1221		SoapError(h, 402, "Invalid Args");
1222	}
1223	else if(strcmp(var_name, "ConnectionStatus") == 0)
1224	{
1225		const char * status;
1226
1227		status = get_wan_connection_status_str(ext_if_name);
1228		bodylen = snprintf(body, sizeof(body), resp,
1229                           action, "urn:schemas-upnp-org:control-1-0",
1230		                   status, action);
1231		BuildSendAndCloseSoapResp(h, body, bodylen);
1232	}
1233#if 0
1234	/* not usefull */
1235	else if(strcmp(var_name, "ConnectionType") == 0)
1236	{
1237		bodylen = snprintf(body, sizeof(body), resp, "IP_Routed");
1238		BuildSendAndCloseSoapResp(h, body, bodylen);
1239	}
1240	else if(strcmp(var_name, "LastConnectionError") == 0)
1241	{
1242		bodylen = snprintf(body, sizeof(body), resp, "ERROR_NONE");
1243		BuildSendAndCloseSoapResp(h, body, bodylen);
1244	}
1245#endif
1246	else if(strcmp(var_name, "PortMappingNumberOfEntries") == 0)
1247	{
1248		char strn[10];
1249		snprintf(strn, sizeof(strn), "%i",
1250		         upnp_get_portmapping_number_of_entries());
1251		bodylen = snprintf(body, sizeof(body), resp,
1252                           action, "urn:schemas-upnp-org:control-1-0",
1253		                   strn, action);
1254		BuildSendAndCloseSoapResp(h, body, bodylen);
1255	}
1256	else
1257	{
1258		syslog(LOG_NOTICE, "%s: Unknown: %s", action, var_name?var_name:"");
1259		SoapError(h, 404, "Invalid Var");
1260	}
1261
1262	ClearNameValueList(&data);
1263}
1264
1265#ifdef ENABLE_6FC_SERVICE
1266#ifndef ENABLE_IPV6
1267#error "ENABLE_6FC_SERVICE needs ENABLE_IPV6"
1268#endif
1269/* WANIPv6FirewallControl actions */
1270static void
1271GetFirewallStatus(struct upnphttp * h, const char * action)
1272{
1273	static const char resp[] =
1274		"<u:%sResponse "
1275		"xmlns:u=\"%s\">"
1276		"<FirewallEnabled>%d</FirewallEnabled>"
1277		"<InboundPinholeAllowed>%d</InboundPinholeAllowed>"
1278		"</u:%sResponse>";
1279
1280	char body[512];
1281	int bodylen;
1282
1283	bodylen = snprintf(body, sizeof(body), resp,
1284		action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
1285	    GETFLAG(IPV6FCFWDISABLEDMASK) ? 0 : 1,
1286	    GETFLAG(IPV6FCINBOUNDDISALLOWEDMASK) ? 0 : 1,
1287	    action);
1288	BuildSendAndCloseSoapResp(h, body, bodylen);
1289}
1290
1291static int
1292CheckStatus(struct upnphttp * h)
1293{
1294	if (GETFLAG(IPV6FCFWDISABLEDMASK))
1295	{
1296		SoapError(h, 702, "FirewallDisabled");
1297		return 0;
1298	}
1299	else if(GETFLAG(IPV6FCINBOUNDDISALLOWEDMASK))
1300	{
1301		SoapError(h, 703, "InboundPinholeNotAllowed");
1302		return 0;
1303	}
1304	else
1305		return 1;
1306}
1307
1308#if 0
1309static int connecthostport(const char * host, unsigned short port, char * result)
1310{
1311	int s, n;
1312	char hostname[INET6_ADDRSTRLEN];
1313	char port_str[8], ifname[8], tmp[4];
1314	struct addrinfo *ai, *p;
1315	struct addrinfo hints;
1316
1317	memset(&hints, 0, sizeof(hints));
1318	/* hints.ai_flags = AI_ADDRCONFIG; */
1319#ifdef AI_NUMERICSERV
1320	hints.ai_flags = AI_NUMERICSERV;
1321#endif
1322	hints.ai_socktype = SOCK_STREAM;
1323	hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
1324	/* hints.ai_protocol = IPPROTO_TCP; */
1325	snprintf(port_str, sizeof(port_str), "%hu", port);
1326	strcpy(hostname, host);
1327	if(!strncmp(host, "fe80", 4))
1328	{
1329		printf("Using an linklocal address\n");
1330		strcpy(ifname, "%");
1331		snprintf(tmp, sizeof(tmp), "%d", linklocal_index);
1332		strcat(ifname, tmp);
1333		strcat(hostname, ifname);
1334		printf("host: %s\n", hostname);
1335	}
1336	n = getaddrinfo(hostname, port_str, &hints, &ai);
1337	if(n != 0)
1338	{
1339		fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
1340		return -1;
1341	}
1342	s = -1;
1343	for(p = ai; p; p = p->ai_next)
1344	{
1345#ifdef DEBUG
1346		char tmp_host[256];
1347		char tmp_service[256];
1348		printf("ai_family=%d ai_socktype=%d ai_protocol=%d ai_addrlen=%d\n ",
1349		       p->ai_family, p->ai_socktype, p->ai_protocol, p->ai_addrlen);
1350		getnameinfo(p->ai_addr, p->ai_addrlen, tmp_host, sizeof(tmp_host),
1351		            tmp_service, sizeof(tmp_service),
1352		            NI_NUMERICHOST | NI_NUMERICSERV);
1353		printf(" host=%s service=%s\n", tmp_host, tmp_service);
1354#endif
1355		inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)p->ai_addr)->sin6_addr), result, INET6_ADDRSTRLEN);
1356		return 0;
1357	}
1358	freeaddrinfo(ai);
1359}
1360#endif
1361
1362/* Check the security policy right */
1363static int
1364PinholeVerification(struct upnphttp * h, char * int_ip, unsigned short int_port)
1365{
1366	int n;
1367	char senderAddr[INET6_ADDRSTRLEN]="";
1368	struct addrinfo hints, *ai, *p;
1369	struct in6_addr result_ip;
1370
1371	/* Pinhole InternalClient address must correspond to the action sender */
1372	syslog(LOG_INFO, "Checking internal IP@ and port (Security policy purpose)");
1373
1374	hints.ai_socktype = SOCK_STREAM;
1375	hints.ai_family = AF_UNSPEC;
1376
1377	/* if ip not valid assume hostname and convert */
1378	if (inet_pton(AF_INET6, int_ip, &result_ip) <= 0)
1379	{
1380		n = getaddrinfo(int_ip, NULL, &hints, &ai);
1381		if(!n && ai->ai_family == AF_INET6)
1382		{
1383			for(p = ai; p; p = p->ai_next)
1384			{
1385				inet_ntop(AF_INET6, (struct in6_addr *) p, int_ip, sizeof(struct in6_addr));
1386				result_ip = *((struct in6_addr *) p);
1387				/* TODO : deal with more than one ip per hostname */
1388				break;
1389			}
1390		}
1391		else
1392		{
1393			syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip);
1394			SoapError(h, 402, "Invalid Args");
1395			return -1;
1396		}
1397        freeaddrinfo(p);
1398	}
1399
1400	if(inet_ntop(AF_INET6, &(h->clientaddr_v6), senderAddr, INET6_ADDRSTRLEN) == NULL)
1401	{
1402		syslog(LOG_ERR, "inet_ntop: %m");
1403	}
1404#ifdef DEBUG
1405	printf("\tPinholeVerification:\n\t\tCompare sender @: %s\n\t\t  to intClient @: %s\n", senderAddr, int_ip);
1406#endif
1407	if(strcmp(senderAddr, int_ip) != 0)
1408	if(h->clientaddr_v6.s6_addr != result_ip.s6_addr)
1409	{
1410		syslog(LOG_INFO, "Client %s tried to access pinhole for internal %s and is not authorized to do it",
1411		       senderAddr, int_ip);
1412		SoapError(h, 606, "Action not authorized");
1413		return 0;
1414	}
1415
1416	/* Pinhole InternalPort must be greater than or equal to 1024 */
1417	if (int_port < 1024)
1418	{
1419		syslog(LOG_INFO, "Client %s tried to access pinhole with port < 1024 and is not authorized to do it",
1420		       senderAddr);
1421		SoapError(h, 606, "Action not authorized");
1422		return 0;
1423	}
1424	return 1;
1425}
1426
1427static void
1428AddPinhole(struct upnphttp * h, const char * action)
1429{
1430	int r;
1431	static const char resp[] =
1432		"<u:%sResponse "
1433		"xmlns:u=\"%s\">"
1434		"<UniqueID>%d</UniqueID>"
1435		"</u:%sResponse>";
1436	char body[512];
1437	int bodylen;
1438	struct NameValueParserData data;
1439	char * rem_host, * rem_port, * int_ip, * int_port, * protocol, * leaseTime;
1440	int uid = 0;
1441	unsigned short iport, rport;
1442	int ltime;
1443	long proto;
1444	char rem_ip[INET6_ADDRSTRLEN];
1445
1446	if(CheckStatus(h)==0)
1447		return;
1448
1449	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1450	rem_host = GetValueFromNameValueList(&data, "RemoteHost");
1451	rem_port = GetValueFromNameValueList(&data, "RemotePort");
1452	int_ip = GetValueFromNameValueList(&data, "InternalClient");
1453	int_port = GetValueFromNameValueList(&data, "InternalPort");
1454	protocol = GetValueFromNameValueList(&data, "Protocol");
1455	leaseTime = GetValueFromNameValueList(&data, "LeaseTime");
1456
1457	rport = (unsigned short)(rem_port ? atoi(rem_port) : 0);
1458	iport = (unsigned short)(int_port ? atoi(int_port) : 0);
1459	ltime = leaseTime ? atoi(leaseTime) : -1;
1460	errno = 0;
1461	proto = protocol ? strtol(protocol, NULL, 0) : -1;
1462	if(errno != 0 || proto > 65535 || proto < 0)
1463	{
1464		SoapError(h, 402, "Invalid Args");
1465		goto clear_and_exit;
1466	}
1467	if(iport == 0)
1468	{
1469		SoapError(h, 706, "InternalPortWilcardingNotAllowed");
1470		goto clear_and_exit;
1471	}
1472
1473	/* In particular, [IGD2] RECOMMENDS that unauthenticated and
1474	 * unauthorized control points are only allowed to invoke
1475	 * this action with:
1476	 * - InternalPort value greater than or equal to 1024,
1477	 * - InternalClient value equals to the control point's IP address.
1478	 * It is REQUIRED that InternalClient cannot be one of IPv6
1479	 * addresses used by the gateway. */
1480	if(!int_ip || 0 == strlen(int_ip) || 0 == strcmp(int_ip, "*"))
1481	{
1482		SoapError(h, 708, "WildCardNotPermittedInSrcIP");
1483		goto clear_and_exit;
1484	}
1485	/* I guess it is useless to convert int_ip to literal ipv6 address */
1486	/* rem_host should be converted to literal ipv6 : */
1487	if(rem_host && (rem_host[0] != '\0'))
1488	{
1489		struct addrinfo *ai, *p;
1490		struct addrinfo hints;
1491		int err;
1492		memset(&hints, 0, sizeof(struct addrinfo));
1493		hints.ai_family = AF_INET6;
1494		/*hints.ai_flags = */
1495		/* hints.ai_protocol = proto; */
1496		err = getaddrinfo(rem_host, rem_port, &hints, &ai);
1497		if(err == 0)
1498		{
1499			/* take the 1st IPv6 address */
1500			for(p = ai; p; p = p->ai_next)
1501			{
1502				if(p->ai_family == AF_INET6)
1503				{
1504					inet_ntop(AF_INET6,
1505					          &(((struct sockaddr_in6 *)p->ai_addr)->sin6_addr),
1506					          rem_ip, sizeof(rem_ip));
1507					syslog(LOG_INFO, "resolved '%s' to '%s'", rem_host, rem_ip);
1508					rem_host = rem_ip;
1509					break;
1510				}
1511			}
1512			freeaddrinfo(ai);
1513		}
1514		else
1515		{
1516			syslog(LOG_WARNING, "AddPinhole : getaddrinfo(%s) : %s",
1517			       rem_host, gai_strerror(err));
1518#if 0
1519			SoapError(h, 402, "Invalid Args");
1520			goto clear_and_exit;
1521#endif
1522		}
1523	}
1524
1525	if(proto == 65535)
1526	{
1527		SoapError(h, 707, "ProtocolWilcardingNotAllowed");
1528		goto clear_and_exit;
1529	}
1530	if(proto != IPPROTO_UDP && proto != IPPROTO_TCP
1531#ifdef IPPROTO_UDPITE
1532	   && atoi(protocol) != IPPROTO_UDPLITE
1533#endif
1534	  )
1535	{
1536		SoapError(h, 705, "ProtocolNotSupported");
1537		goto clear_and_exit;
1538	}
1539	if(ltime < 1 || ltime > 86400)
1540	{
1541		syslog(LOG_WARNING, "%s: LeaseTime=%d not supported, (ip=%s)",
1542		       action, ltime, int_ip);
1543		SoapError(h, 402, "Invalid Args");
1544		goto clear_and_exit;
1545	}
1546
1547	if(PinholeVerification(h, int_ip, iport) <= 0)
1548		goto clear_and_exit;
1549
1550	syslog(LOG_INFO, "%s: (inbound) from [%s]:%hu to [%s]:%hu with proto %ld during %d sec",
1551	       action, rem_host?rem_host:"any",
1552	       rport, int_ip, iport,
1553	       proto, ltime);
1554
1555	/* In cases where the RemoteHost, RemotePort, InternalPort,
1556	 * InternalClient and Protocol are the same than an existing pinhole,
1557	 * but LeaseTime is different, the device MUST extend the existing
1558	 * pinhole's lease time and return the UniqueID of the existing pinhole. */
1559	r = upnp_add_inboundpinhole(rem_host, rport, int_ip, iport, proto, "IGD2 pinhole", ltime, &uid);
1560
1561	switch(r)
1562	{
1563		case 1:	        /* success */
1564			bodylen = snprintf(body, sizeof(body),
1565			                   resp, action,
1566			                   "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
1567			                   uid, action);
1568			BuildSendAndCloseSoapResp(h, body, bodylen);
1569			break;
1570		case -1: 	/* not permitted */
1571			SoapError(h, 701, "PinholeSpaceExhausted");
1572			break;
1573		default:
1574			SoapError(h, 501, "ActionFailed");
1575			break;
1576	}
1577	/* 606 Action not authorized
1578	 * 701 PinholeSpaceExhausted
1579	 * 702 FirewallDisabled
1580	 * 703 InboundPinholeNotAllowed
1581	 * 705 ProtocolNotSupported
1582	 * 706 InternalPortWildcardingNotAllowed
1583	 * 707 ProtocolWildcardingNotAllowed
1584	 * 708 WildCardNotPermittedInSrcIP */
1585clear_and_exit:
1586	ClearNameValueList(&data);
1587}
1588
1589static void
1590UpdatePinhole(struct upnphttp * h, const char * action)
1591{
1592	static const char resp[] =
1593		"<u:UpdatePinholeResponse "
1594		"xmlns:u=\"urn:schemas-upnp-org:service:WANIPv6FirewallControl:1\">"
1595		"</u:UpdatePinholeResponse>";
1596	struct NameValueParserData data;
1597	const char * uid_str, * leaseTime;
1598	char iaddr[INET6_ADDRSTRLEN];
1599	unsigned short iport;
1600	int ltime;
1601	int uid;
1602	int n;
1603
1604	if(CheckStatus(h)==0)
1605		return;
1606
1607	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1608	uid_str = GetValueFromNameValueList(&data, "UniqueID");
1609	leaseTime = GetValueFromNameValueList(&data, "NewLeaseTime");
1610	uid = uid_str ? atoi(uid_str) : -1;
1611	ltime = leaseTime ? atoi(leaseTime) : -1;
1612	ClearNameValueList(&data);
1613
1614	if(uid < 0 || uid > 65535 || ltime <= 0 || ltime > 86400)
1615	{
1616		SoapError(h, 402, "Invalid Args");
1617		return;
1618	}
1619
1620	/* Check that client is not updating an pinhole
1621	 * it doesn't have access to, because of its public access */
1622	n = upnp_get_pinhole_info(uid, NULL, 0, NULL,
1623	                          iaddr, sizeof(iaddr), &iport,
1624	                          NULL, /* proto */
1625	                          NULL, 0, /* desc, desclen */
1626	                          NULL, NULL);
1627	if (n >= 0)
1628	{
1629		if(PinholeVerification(h, iaddr, iport) <= 0)
1630			return;
1631	}
1632	else if(n == -2)
1633	{
1634		SoapError(h, 704, "NoSuchEntry");
1635		return;
1636	}
1637	else
1638	{
1639		SoapError(h, 501, "ActionFailed");
1640		return;
1641	}
1642
1643	syslog(LOG_INFO, "%s: (inbound) updating lease duration to %d for pinhole with ID: %d",
1644	       action, ltime, uid);
1645
1646	n = upnp_update_inboundpinhole(uid, ltime);
1647	if(n == -1)
1648		SoapError(h, 704, "NoSuchEntry");
1649	else if(n < 0)
1650		SoapError(h, 501, "ActionFailed");
1651	else
1652		BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
1653}
1654
1655static void
1656GetOutboundPinholeTimeout(struct upnphttp * h, const char * action)
1657{
1658	int r;
1659
1660	static const char resp[] =
1661		"<u:%sResponse "
1662		"xmlns:u=\"%s\">"
1663		"<OutboundPinholeTimeout>%d</OutboundPinholeTimeout>"
1664		"</u:%sResponse>";
1665
1666	char body[512];
1667	int bodylen;
1668	struct NameValueParserData data;
1669	char * int_ip, * int_port, * rem_host, * rem_port, * protocol;
1670	int opt=0, proto=0;
1671	unsigned short iport, rport;
1672
1673	if (GETFLAG(IPV6FCFWDISABLEDMASK))
1674	{
1675		SoapError(h, 702, "FirewallDisabled");
1676		return;
1677	}
1678
1679	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1680	int_ip = GetValueFromNameValueList(&data, "InternalClient");
1681	int_port = GetValueFromNameValueList(&data, "InternalPort");
1682	rem_host = GetValueFromNameValueList(&data, "RemoteHost");
1683	rem_port = GetValueFromNameValueList(&data, "RemotePort");
1684	protocol = GetValueFromNameValueList(&data, "Protocol");
1685
1686	rport = (unsigned short)atoi(rem_port);
1687	iport = (unsigned short)atoi(int_port);
1688	proto = atoi(protocol);
1689
1690	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);
1691
1692	/* TODO */
1693	r = -1;/*upnp_check_outbound_pinhole(proto, &opt);*/
1694
1695	switch(r)
1696	{
1697		case 1:	/* success */
1698			bodylen = snprintf(body, sizeof(body), resp,
1699			                   action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
1700			                   opt, action);
1701			BuildSendAndCloseSoapResp(h, body, bodylen);
1702			break;
1703		case -5:	/* Protocol not supported */
1704			SoapError(h, 705, "ProtocolNotSupported");
1705			break;
1706		default:
1707			SoapError(h, 501, "ActionFailed");
1708	}
1709	ClearNameValueList(&data);
1710}
1711
1712static void
1713DeletePinhole(struct upnphttp * h, const char * action)
1714{
1715	int n;
1716
1717	static const char resp[] =
1718		"<u:DeletePinholeResponse "
1719		"xmlns:u=\"urn:schemas-upnp-org:service:WANIPv6FirewallControl:1\">"
1720		"</u:DeletePinholeResponse>";
1721
1722	struct NameValueParserData data;
1723	const char * uid_str;
1724	char iaddr[INET6_ADDRSTRLEN];
1725	int proto;
1726	unsigned short iport;
1727	unsigned int leasetime;
1728	int uid;
1729
1730	if(CheckStatus(h)==0)
1731		return;
1732
1733	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1734	uid_str = GetValueFromNameValueList(&data, "UniqueID");
1735	uid = uid_str ? atoi(uid_str) : -1;
1736	ClearNameValueList(&data);
1737
1738	if(uid < 0 || uid > 65535)
1739	{
1740		SoapError(h, 402, "Invalid Args");
1741		return;
1742	}
1743
1744	/* Check that client is not deleting an pinhole
1745	 * it doesn't have access to, because of its public access */
1746	n = upnp_get_pinhole_info(uid, NULL, 0, NULL,
1747	                          iaddr, sizeof(iaddr), &iport,
1748	                          &proto,
1749	                          NULL, 0, /* desc, desclen */
1750	                          &leasetime, NULL);
1751	if (n >= 0)
1752	{
1753		if(PinholeVerification(h, iaddr, iport) <= 0)
1754			return;
1755	}
1756	else if(n == -2)
1757	{
1758		SoapError(h, 704, "NoSuchEntry");
1759		return;
1760	}
1761	else
1762	{
1763		SoapError(h, 501, "ActionFailed");
1764		return;
1765	}
1766
1767	n = upnp_delete_inboundpinhole(uid);
1768	if(n < 0)
1769	{
1770		syslog(LOG_INFO, "%s: (inbound) failed to remove pinhole with ID: %d",
1771	           action, uid);
1772		SoapError(h, 501, "ActionFailed");
1773		return;
1774	}
1775	syslog(LOG_DEBUG, "%s: (inbound) pinhole with ID %d successfully removed",
1776	       action, uid);
1777	BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
1778}
1779
1780static void
1781CheckPinholeWorking(struct upnphttp * h, const char * action)
1782{
1783	static const char resp[] =
1784		"<u:%sResponse "
1785		"xmlns:u=\"%s\">"
1786		"<IsWorking>%d</IsWorking>"
1787		"</u:%sResponse>";
1788	char body[512];
1789	int bodylen;
1790	int r;
1791	struct NameValueParserData data;
1792	const char * uid_str;
1793	int uid;
1794	char iaddr[INET6_ADDRSTRLEN];
1795	unsigned short iport;
1796	unsigned int packets;
1797
1798	if(CheckStatus(h)==0)
1799		return;
1800
1801	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1802	uid_str = GetValueFromNameValueList(&data, "UniqueID");
1803	uid = uid_str ? atoi(uid_str) : -1;
1804	ClearNameValueList(&data);
1805
1806	if(uid < 0 || uid > 65535)
1807	{
1808		SoapError(h, 402, "Invalid Args");
1809		return;
1810	}
1811
1812	/* Check that client is not checking a pinhole
1813	 * it doesn't have access to, because of its public access */
1814	r = upnp_get_pinhole_info(uid,
1815	                          NULL, 0, NULL,
1816	                          iaddr, sizeof(iaddr), &iport,
1817	                          NULL, /* proto */
1818	                          NULL, 0, /* desc, desclen */
1819	                          NULL, &packets);
1820	if (r >= 0)
1821	{
1822		if(PinholeVerification(h, iaddr, iport) <= 0)
1823			return ;
1824		if(packets == 0)
1825		{
1826			SoapError(h, 709, "NoPacketSent");
1827			return;
1828		}
1829		bodylen = snprintf(body, sizeof(body), resp,
1830						action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
1831						1, action);
1832		BuildSendAndCloseSoapResp(h, body, bodylen);
1833	}
1834	else if(r == -2)
1835		SoapError(h, 704, "NoSuchEntry");
1836	else
1837		SoapError(h, 501, "ActionFailed");
1838}
1839
1840static void
1841GetPinholePackets(struct upnphttp * h, const char * action)
1842{
1843	static const char resp[] =
1844		"<u:%sResponse "
1845		"xmlns:u=\"%s\">"
1846		"<PinholePackets>%u</PinholePackets>"
1847		"</u:%sResponse>";
1848	char body[512];
1849	int bodylen;
1850	struct NameValueParserData data;
1851	const char * uid_str;
1852	int n;
1853	char iaddr[INET6_ADDRSTRLEN];
1854	unsigned short iport;
1855	unsigned int packets = 0;
1856	int uid;
1857	int proto;
1858	unsigned int leasetime;
1859
1860	if(CheckStatus(h)==0)
1861		return;
1862
1863	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1864	uid_str = GetValueFromNameValueList(&data, "UniqueID");
1865	uid = uid_str ? atoi(uid_str) : -1;
1866	ClearNameValueList(&data);
1867
1868	if(uid < 0 || uid > 65535)
1869	{
1870		SoapError(h, 402, "Invalid Args");
1871		return;
1872	}
1873
1874	/* Check that client is not getting infos of a pinhole
1875	 * it doesn't have access to, because of its public access */
1876	n = upnp_get_pinhole_info(uid, NULL, 0, NULL,
1877	                          iaddr, sizeof(iaddr), &iport,
1878	                          &proto,
1879	                          NULL, 0, /* desc, desclen */
1880	                          &leasetime, &packets);
1881	if (n >= 0)
1882	{
1883		if(PinholeVerification(h, iaddr, iport)<=0)
1884			return ;
1885	}
1886#if 0
1887	else if(r == -4 || r == -1)
1888	{
1889		SoapError(h, 704, "NoSuchEntry");
1890	}
1891#endif
1892
1893	bodylen = snprintf(body, sizeof(body), resp,
1894			action, "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1",
1895			packets, action);
1896	BuildSendAndCloseSoapResp(h, body, bodylen);
1897}
1898#endif
1899
1900#ifdef ENABLE_DP_SERVICE
1901static void
1902SendSetupMessage(struct upnphttp * h, const char * action)
1903{
1904	static const char resp[] =
1905		"<u:%sResponse "
1906		"xmlns:u=\"%s\">"
1907		"<NewOutMessage>%s</NewOutMessage>"
1908		"</u:%sResponse>";
1909	char body[1024];
1910	int bodylen;
1911	struct NameValueParserData data;
1912	const char * ProtocolType;	/* string */
1913	const char * InMessage;		/* base64 */
1914	const char * OutMessage = "";	/* base64 */
1915
1916	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
1917	ProtocolType = GetValueFromNameValueList(&data, "NewProtocolType");	/* string */
1918	InMessage = GetValueFromNameValueList(&data, "NewInMessage");	/* base64 */
1919
1920	if(ProtocolType == NULL || InMessage == NULL)
1921	{
1922		ClearNameValueList(&data);
1923		SoapError(h, 402, "Invalid Args");
1924		return;
1925	}
1926	/*if(strcmp(ProtocolType, "DeviceProtection:1") != 0)*/
1927	if(strcmp(ProtocolType, "WPS") != 0)
1928	{
1929		ClearNameValueList(&data);
1930		SoapError(h, 600, "Argument Value Invalid"); /* 703 ? */
1931		return;
1932	}
1933	/* TODO : put here code for WPS */
1934
1935	bodylen = snprintf(body, sizeof(body), resp,
1936	                   action, "urn:schemas-upnp-org:service:DeviceProtection:1",
1937	                   OutMessage, action);
1938	BuildSendAndCloseSoapResp(h, body, bodylen);
1939	ClearNameValueList(&data);
1940}
1941
1942static void
1943GetSupportedProtocols(struct upnphttp * h, const char * action)
1944{
1945	static const char resp[] =
1946		"<u:%sResponse "
1947		"xmlns:u=\"%s\">"
1948		"<NewProtocolList>%s</NewProtocolList>"
1949		"</u:%sResponse>";
1950	char body[1024];
1951	int bodylen;
1952	const char * ProtocolList =
1953		"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1954		"<SupportedProtocols xmlns=\"urn:schemas-upnp-org:gw:DeviceProtection\""
1955		" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
1956		" xsi:schemaLocation=\"urn:schemas-upnp-org:gw:DeviceProtection"
1957		" http://www.upnp.org/schemas/gw/DeviceProtection-v1.xsd\">"
1958		"<Introduction><Name>WPS</Name></Introduction>"
1959		"<Login><Name>PKCS5</Name></Login>"
1960		"</SupportedProtocols>";
1961
1962	bodylen = snprintf(body, sizeof(body), resp,
1963	                   action, "urn:schemas-upnp-org:service:DeviceProtection:1",
1964	                   ProtocolList, action);
1965	BuildSendAndCloseSoapResp(h, body, bodylen);
1966}
1967
1968static void
1969GetAssignedRoles(struct upnphttp * h, const char * action)
1970{
1971	static const char resp[] =
1972		"<u:%sResponse "
1973		"xmlns:u=\"%s\">"
1974		"<NewRoleList>%s</NewRoleList>"
1975		"</u:%sResponse>";
1976	char body[1024];
1977	int bodylen;
1978	const char * RoleList = "Public"; /* list of roles separated by spaces */
1979
1980#ifdef ENABLE_HTTPS
1981	if(h->ssl != NULL) {
1982		/* we should get the Roles of the session (based on client certificate) */
1983		X509 * peercert;
1984		peercert = SSL_get_peer_certificate(h->ssl);
1985		if(peercert != NULL) {
1986			RoleList = "Admin Basic";
1987			X509_free(peercert);
1988		}
1989	}
1990#endif
1991
1992	bodylen = snprintf(body, sizeof(body), resp,
1993	                   action, "urn:schemas-upnp-org:service:DeviceProtection:1",
1994	                   RoleList, action);
1995	BuildSendAndCloseSoapResp(h, body, bodylen);
1996}
1997#endif
1998
1999/* Windows XP as client send the following requests :
2000 * GetConnectionTypeInfo
2001 * GetNATRSIPStatus
2002 * ? GetTotalBytesSent - WANCommonInterfaceConfig
2003 * ? GetTotalBytesReceived - idem
2004 * ? GetTotalPacketsSent - idem
2005 * ? GetTotalPacketsReceived - idem
2006 * GetCommonLinkProperties - idem
2007 * GetStatusInfo - WANIPConnection
2008 * GetExternalIPAddress
2009 * QueryStateVariable / ConnectionStatus!
2010 */
2011static const struct
2012{
2013	const char * methodName;
2014	void (*methodImpl)(struct upnphttp *, const char *);
2015}
2016soapMethods[] =
2017{
2018	/* WANCommonInterfaceConfig */
2019	{ "QueryStateVariable", QueryStateVariable},
2020	{ "GetTotalBytesSent", GetTotalBytesSent},
2021	{ "GetTotalBytesReceived", GetTotalBytesReceived},
2022	{ "GetTotalPacketsSent", GetTotalPacketsSent},
2023	{ "GetTotalPacketsReceived", GetTotalPacketsReceived},
2024	{ "GetCommonLinkProperties", GetCommonLinkProperties},
2025	{ "GetStatusInfo", GetStatusInfo},
2026	/* WANIPConnection */
2027	{ "GetConnectionTypeInfo", GetConnectionTypeInfo },
2028	{ "GetNATRSIPStatus", GetNATRSIPStatus},
2029	{ "GetExternalIPAddress", GetExternalIPAddress},
2030	{ "AddPortMapping", AddPortMapping},
2031	{ "DeletePortMapping", DeletePortMapping},
2032	{ "GetGenericPortMappingEntry", GetGenericPortMappingEntry},
2033	{ "GetSpecificPortMappingEntry", GetSpecificPortMappingEntry},
2034/* Required in WANIPConnection:2 */
2035	{ "SetConnectionType", SetConnectionType},
2036	{ "RequestConnection", RequestConnection},
2037	{ "ForceTermination", ForceTermination},
2038	{ "AddAnyPortMapping", AddAnyPortMapping},
2039	{ "DeletePortMappingRange", DeletePortMappingRange},
2040	{ "GetListOfPortMappings", GetListOfPortMappings},
2041#ifdef ENABLE_L3F_SERVICE
2042	/* Layer3Forwarding */
2043	{ "SetDefaultConnectionService", SetDefaultConnectionService},
2044	{ "GetDefaultConnectionService", GetDefaultConnectionService},
2045#endif
2046#ifdef ENABLE_6FC_SERVICE
2047	/* WANIPv6FirewallControl */
2048	{ "GetFirewallStatus", GetFirewallStatus},	/* Required */
2049	{ "AddPinhole", AddPinhole},				/* Required */
2050	{ "UpdatePinhole", UpdatePinhole},			/* Required */
2051	{ "GetOutboundPinholeTimeout", GetOutboundPinholeTimeout},	/* Optional */
2052	{ "DeletePinhole", DeletePinhole},			/* Required */
2053	{ "CheckPinholeWorking", CheckPinholeWorking},	/* Optional */
2054	{ "GetPinholePackets", GetPinholePackets},	/* Required */
2055#endif
2056#ifdef ENABLE_DP_SERVICE
2057	/* DeviceProtection */
2058	{ "SendSetupMessage", SendSetupMessage},	/* Required */
2059	{ "GetSupportedProtocols", GetSupportedProtocols},	/* Required */
2060	{ "GetAssignedRoles", GetAssignedRoles},	/* Required */
2061#endif
2062	{ 0, 0 }
2063};
2064
2065void
2066ExecuteSoapAction(struct upnphttp * h, const char * action, int n)
2067{
2068	char * p;
2069	char * p2;
2070	int i, len, methodlen;
2071
2072	/* SoapAction example :
2073	 * urn:schemas-upnp-org:service:WANIPConnection:1#GetStatusInfo */
2074	p = strchr(action, '#');
2075	if(p && (p - action) < n) {
2076		p++;
2077		p2 = strchr(p, '"');
2078		if(p2 && (p2 - action) <= n)
2079			methodlen = p2 - p;
2080		else
2081			methodlen = n - (p - action);
2082		/*syslog(LOG_DEBUG, "SoapMethod: %.*s %d %d %p %p %d",
2083		       methodlen, p, methodlen, n, action, p, (int)(p - action));*/
2084		for(i = 0; soapMethods[i].methodName; i++) {
2085			len = strlen(soapMethods[i].methodName);
2086			if((len == methodlen) && memcmp(p, soapMethods[i].methodName, len) == 0) {
2087#ifdef DEBUG
2088				syslog(LOG_DEBUG, "Remote Call of SoapMethod '%s'",
2089				       soapMethods[i].methodName);
2090#endif /* DEBUG */
2091				soapMethods[i].methodImpl(h, soapMethods[i].methodName);
2092				return;
2093			}
2094		}
2095		syslog(LOG_NOTICE, "SoapMethod: Unknown: %.*s", methodlen, p);
2096	} else {
2097		syslog(LOG_NOTICE, "cannot parse SoapAction");
2098	}
2099
2100	SoapError(h, 401, "Invalid Action");
2101}
2102
2103/* Standard Errors:
2104 *
2105 * errorCode errorDescription Description
2106 * --------	---------------- -----------
2107 * 401 		Invalid Action 	No action by that name at this service.
2108 * 402 		Invalid Args 	Could be any of the following: not enough in args,
2109 * 							too many in args, no in arg by that name,
2110 * 							one or more in args are of the wrong data type.
2111 * 403 		Out of Sync 	Out of synchronization.
2112 * 501 		Action Failed 	May be returned in current state of service
2113 * 							prevents invoking that action.
2114 * 600-699 	TBD 			Common action errors. Defined by UPnP Forum
2115 * 							Technical Committee.
2116 * 700-799 	TBD 			Action-specific errors for standard actions.
2117 * 							Defined by UPnP Forum working committee.
2118 * 800-899 	TBD 			Action-specific errors for non-standard actions.
2119 * 							Defined by UPnP vendor.
2120*/
2121void
2122SoapError(struct upnphttp * h, int errCode, const char * errDesc)
2123{
2124	static const char resp[] =
2125		"<s:Envelope "
2126		"xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
2127		"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
2128		"<s:Body>"
2129		"<s:Fault>"
2130		"<faultcode>s:Client</faultcode>"
2131		"<faultstring>UPnPError</faultstring>"
2132		"<detail>"
2133		"<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">"
2134		"<errorCode>%d</errorCode>"
2135		"<errorDescription>%s</errorDescription>"
2136		"</UPnPError>"
2137		"</detail>"
2138		"</s:Fault>"
2139		"</s:Body>"
2140		"</s:Envelope>";
2141
2142	char body[2048];
2143	int bodylen;
2144
2145	syslog(LOG_INFO, "Returning UPnPError %d: %s", errCode, errDesc);
2146	bodylen = snprintf(body, sizeof(body), resp, errCode, errDesc);
2147	BuildResp2_upnphttp(h, 500, "Internal Server Error", body, bodylen);
2148	SendRespAndClose_upnphttp(h);
2149}
2150
2151