File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / upnpsoap.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:16:02 2012 UTC (12 years, 4 months ago) by misho
Branches: miniupnpd, elwix, MAIN
CVS tags: v1_5, HEAD
miniupnpd

    1: /* $Id: upnpsoap.c,v 1.1.1.1 2012/02/21 23:16:02 misho Exp $ */
    2: /* MiniUPnP project
    3:  * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
    4:  * (c) 2006-2011 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 <string.h>
   11: #include <sys/socket.h>
   12: #include <unistd.h>
   13: #include <syslog.h>
   14: #include <sys/types.h>
   15: #include <arpa/inet.h>
   16: #include <netinet/in.h>
   17: #include <netdb.h>
   18: 
   19: #include "config.h"
   20: #include "upnpglobalvars.h"
   21: #include "upnphttp.h"
   22: #include "upnpsoap.h"
   23: #include "upnpreplyparse.h"
   24: #include "upnpredirect.h"
   25: #include "getifaddr.h"
   26: #include "getifstats.h"
   27: 
   28: static void
   29: BuildSendAndCloseSoapResp(struct upnphttp * h,
   30:                           const char * body, int bodylen)
   31: {
   32: 	static const char beforebody[] =
   33: 		"<?xml version=\"1.0\"?>\r\n"
   34: 		"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
   35: 		"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
   36: 		"<s:Body>";
   37: 
   38: 	static const char afterbody[] =
   39: 		"</s:Body>"
   40: 		"</s:Envelope>\r\n";
   41: 
   42: 	BuildHeader_upnphttp(h, 200, "OK",  sizeof(beforebody) - 1
   43: 		+ sizeof(afterbody) - 1 + bodylen );
   44: 
   45: 	memcpy(h->res_buf + h->res_buflen, beforebody, sizeof(beforebody) - 1);
   46: 	h->res_buflen += sizeof(beforebody) - 1;
   47: 
   48: 	memcpy(h->res_buf + h->res_buflen, body, bodylen);
   49: 	h->res_buflen += bodylen;
   50: 
   51: 	memcpy(h->res_buf + h->res_buflen, afterbody, sizeof(afterbody) - 1);
   52: 	h->res_buflen += sizeof(afterbody) - 1;
   53: 
   54: 	SendResp_upnphttp(h);
   55: 	CloseSocket_upnphttp(h);
   56: }
   57: 
   58: static void
   59: GetConnectionTypeInfo(struct upnphttp * h, const char * action)
   60: {
   61: 	static const char resp[] =
   62: 		"<u:GetConnectionTypeInfoResponse "
   63: 		"xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">"
   64: 		"<NewConnectionType>IP_Routed</NewConnectionType>"
   65: 		"<NewPossibleConnectionTypes>IP_Routed</NewPossibleConnectionTypes>"
   66: 		"</u:GetConnectionTypeInfoResponse>";
   67: 	BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
   68: }
   69: 
   70: static void
   71: GetTotalBytesSent(struct upnphttp * h, const char * action)
   72: {
   73: 	int r;
   74: 
   75: 	static const char resp[] =
   76: 		"<u:%sResponse "
   77: 		"xmlns:u=\"%s\">"
   78: 		"<NewTotalBytesSent>%lu</NewTotalBytesSent>"
   79: 		"</u:%sResponse>";
   80: 
   81: 	char body[512];
   82: 	int bodylen;
   83: 	struct ifdata data;
   84: 
   85: 	r = getifstats(ext_if_name, &data);
   86: 	bodylen = snprintf(body, sizeof(body), resp,
   87: 	         action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
   88:              r<0?0:data.obytes, action);
   89: 	BuildSendAndCloseSoapResp(h, body, bodylen);
   90: }
   91: 
   92: static void
   93: GetTotalBytesReceived(struct upnphttp * h, const char * action)
   94: {
   95: 	int r;
   96: 
   97: 	static const char resp[] =
   98: 		"<u:%sResponse "
   99: 		"xmlns:u=\"%s\">"
  100: 		"<NewTotalBytesReceived>%lu</NewTotalBytesReceived>"
  101: 		"</u:%sResponse>";
  102: 
  103: 	char body[512];
  104: 	int bodylen;
  105: 	struct ifdata data;
  106: 
  107: 	r = getifstats(ext_if_name, &data);
  108: 	bodylen = snprintf(body, sizeof(body), resp,
  109: 	         action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
  110: 	         r<0?0:data.ibytes, action);
  111: 	BuildSendAndCloseSoapResp(h, body, bodylen);
  112: }
  113: 
  114: static void
  115: GetTotalPacketsSent(struct upnphttp * h, const char * action)
  116: {
  117: 	int r;
  118: 
  119: 	static const char resp[] =
  120: 		"<u:%sResponse "
  121: 		"xmlns:u=\"%s\">"
  122: 		"<NewTotalPacketsSent>%lu</NewTotalPacketsSent>"
  123: 		"</u:%sResponse>";
  124: 
  125: 	char body[512];
  126: 	int bodylen;
  127: 	struct ifdata data;
  128: 
  129: 	r = getifstats(ext_if_name, &data);
  130: 	bodylen = snprintf(body, sizeof(body), resp,
  131: 	         action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
  132: 	         r<0?0:data.opackets, action);
  133: 	BuildSendAndCloseSoapResp(h, body, bodylen);
  134: }
  135: 
  136: static void
  137: GetTotalPacketsReceived(struct upnphttp * h, const char * action)
  138: {
  139: 	int r;
  140: 
  141: 	static const char resp[] =
  142: 		"<u:%sResponse "
  143: 		"xmlns:u=\"%s\">"
  144: 		"<NewTotalPacketsReceived>%lu</NewTotalPacketsReceived>"
  145: 		"</u:%sResponse>";
  146: 
  147: 	char body[512];
  148: 	int bodylen;
  149: 	struct ifdata data;
  150: 
  151: 	r = getifstats(ext_if_name, &data);
  152: 	bodylen = snprintf(body, sizeof(body), resp,
  153: 	         action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
  154: 	         r<0?0:data.ipackets, action);
  155: 	BuildSendAndCloseSoapResp(h, body, bodylen);
  156: }
  157: 
  158: static void
  159: GetCommonLinkProperties(struct upnphttp * h, const char * action)
  160: {
  161: 	/* WANAccessType : set depending on the hardware :
  162: 	 * DSL, POTS (plain old Telephone service), Cable, Ethernet */
  163: 	static const char resp[] =
  164: 		"<u:%sResponse "
  165: 		"xmlns:u=\"%s\">"
  166: 		/*"<NewWANAccessType>DSL</NewWANAccessType>"*/
  167: 		"<NewWANAccessType>Cable</NewWANAccessType>"
  168: 		"<NewLayer1UpstreamMaxBitRate>%lu</NewLayer1UpstreamMaxBitRate>"
  169: 		"<NewLayer1DownstreamMaxBitRate>%lu</NewLayer1DownstreamMaxBitRate>"
  170: 		"<NewPhysicalLinkStatus>%s</NewPhysicalLinkStatus>"
  171: 		"</u:%sResponse>";
  172: 
  173: 	char body[2048];
  174: 	int bodylen;
  175: 	struct ifdata data;
  176: 	const char * status = "Up";	/* Up, Down (Required),
  177: 	                             * Initializing, Unavailable (Optional) */
  178: 	char ext_ip_addr[INET_ADDRSTRLEN];
  179: 
  180: 	if((downstream_bitrate == 0) || (upstream_bitrate == 0))
  181: 	{
  182: 		if(getifstats(ext_if_name, &data) >= 0)
  183: 		{
  184: 			if(downstream_bitrate == 0) downstream_bitrate = data.baudrate;
  185: 			if(upstream_bitrate == 0) upstream_bitrate = data.baudrate;
  186: 		}
  187: 	}
  188: 	if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0) {
  189: 		status = "Down";
  190: 	}
  191: 	bodylen = snprintf(body, sizeof(body), resp,
  192: 	    action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
  193: 		upstream_bitrate, downstream_bitrate,
  194: 	    status, action);
  195: 	BuildSendAndCloseSoapResp(h, body, bodylen);
  196: }
  197: 
  198: static void
  199: GetStatusInfo(struct upnphttp * h, const char * action)
  200: {
  201: 	static const char resp[] =
  202: 		"<u:%sResponse "
  203: 		"xmlns:u=\"%s\">"
  204: 		"<NewConnectionStatus>%s</NewConnectionStatus>"
  205: 		"<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>"
  206: 		"<NewUptime>%ld</NewUptime>"
  207: 		"</u:%sResponse>";
  208: 
  209: 	char body[512];
  210: 	int bodylen;
  211: 	time_t uptime;
  212: 	const char * status = "Connected";
  213: 	/* ConnectionStatus possible values :
  214: 	 * Unconfigured, Connecting, Connected, PendingDisconnect,
  215: 	 * Disconnecting, Disconnected */
  216: 	char ext_ip_addr[INET_ADDRSTRLEN];
  217: 
  218: 	if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0) {
  219: 		status = "Disconnected";
  220: 	}
  221: 	uptime = (time(NULL) - startup_time);
  222: 	bodylen = snprintf(body, sizeof(body), resp,
  223: 		action, "urn:schemas-upnp-org:service:WANIPConnection:1",
  224: 		status, (long)uptime, action);	
  225: 	BuildSendAndCloseSoapResp(h, body, bodylen);
  226: }
  227: 
  228: static void
  229: GetNATRSIPStatus(struct upnphttp * h, const char * action)
  230: {
  231: 	static const char resp[] =
  232: 		"<u:GetNATRSIPStatusResponse "
  233: 		"xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">"
  234: 		"<NewRSIPAvailable>0</NewRSIPAvailable>"
  235: 		"<NewNATEnabled>1</NewNATEnabled>"
  236: 		"</u:GetNATRSIPStatusResponse>";
  237: 	/* 2.2.9. RSIPAvailable
  238: 	 * This variable indicates if Realm-specific IP (RSIP) is available
  239: 	 * as a feature on the InternetGatewayDevice. RSIP is being defined
  240: 	 * in the NAT working group in the IETF to allow host-NATing using
  241: 	 * a standard set of message exchanges. It also allows end-to-end
  242: 	 * applications that otherwise break if NAT is introduced
  243: 	 * (e.g. IPsec-based VPNs).
  244: 	 * A gateway that does not support RSIP should set this variable to 0. */
  245: 	BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
  246: }
  247: 
  248: static void
  249: GetExternalIPAddress(struct upnphttp * h, const char * action)
  250: {
  251: 	static const char resp[] =
  252: 		"<u:%sResponse "
  253: 		"xmlns:u=\"%s\">"
  254: 		"<NewExternalIPAddress>%s</NewExternalIPAddress>"
  255: 		"</u:%sResponse>";
  256: 
  257: 	char body[512];
  258: 	int bodylen;
  259: 	char ext_ip_addr[INET_ADDRSTRLEN];
  260: 
  261: #ifndef MULTIPLE_EXTERNAL_IP
  262: 	if(use_ext_ip_addr)
  263: 	{
  264: 		strncpy(ext_ip_addr, use_ext_ip_addr, INET_ADDRSTRLEN);
  265: 	}
  266: 	else if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0)
  267: 	{
  268: 		syslog(LOG_ERR, "Failed to get ip address for interface %s",
  269: 			ext_if_name);
  270: 		strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
  271: 	}
  272: #else
  273: 	int i;
  274: 	strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
  275: 	for(i = 0; i<n_lan_addr; i++)
  276: 	{
  277: 		if( (h->clientaddr.s_addr & lan_addr[i].mask.s_addr)
  278: 		   == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
  279: 		{
  280: 			strncpy(ext_ip_addr, lan_addr[i].ext_ip_str, INET_ADDRSTRLEN);
  281: 			break;
  282: 		}
  283: 	}
  284: #endif
  285: 	bodylen = snprintf(body, sizeof(body), resp,
  286: 	              action, "urn:schemas-upnp-org:service:WANIPConnection:1",
  287: 				  ext_ip_addr, action);
  288: 	BuildSendAndCloseSoapResp(h, body, bodylen);
  289: }
  290: 
  291: static void
  292: AddPortMapping(struct upnphttp * h, const char * action)
  293: {
  294: 	int r;
  295: 
  296: 	static const char resp[] =
  297: 		"<u:AddPortMappingResponse "
  298: 		"xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\"/>";
  299: 
  300: 	struct NameValueParserData data;
  301: 	char * int_ip, * int_port, * ext_port, * protocol, * desc;
  302: 	char * leaseduration;
  303: 	unsigned short iport, eport;
  304: 
  305: 	struct hostent *hp; /* getbyhostname() */
  306: 	char ** ptr; /* getbyhostname() */
  307: 	struct in_addr result_ip;/*unsigned char result_ip[16];*/ /* inet_pton() */
  308: 
  309: 	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
  310: 	int_ip = GetValueFromNameValueList(&data, "NewInternalClient");
  311: 
  312: 	if (!int_ip)
  313: 	{
  314: 		ClearNameValueList(&data);
  315: 		SoapError(h, 402, "Invalid Args");
  316: 		return;
  317: 	}
  318: 
  319: 	/* if ip not valid assume hostname and convert */
  320: 	if (inet_pton(AF_INET, int_ip, &result_ip) <= 0) 
  321: 	{
  322: 		hp = gethostbyname(int_ip);
  323: 		if(hp && hp->h_addrtype == AF_INET) 
  324: 		{ 
  325: 			for(ptr = hp->h_addr_list; ptr && *ptr; ptr++)
  326: 		   	{
  327: 				int_ip = inet_ntoa(*((struct in_addr *) *ptr));
  328: 				result_ip = *((struct in_addr *) *ptr);
  329: 				/* TODO : deal with more than one ip per hostname */
  330: 				break;
  331: 			}
  332: 		} 
  333: 		else 
  334: 		{
  335: 			syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip); 
  336: 			ClearNameValueList(&data);
  337: 			SoapError(h, 402, "Invalid Args");
  338: 			return;
  339: 		}				
  340: 	}
  341: 
  342: 	/* check if NewInternalAddress is the client address */
  343: 	if(GETFLAG(SECUREMODEMASK))
  344: 	{
  345: 		if(h->clientaddr.s_addr != result_ip.s_addr)
  346: 		{
  347: 			syslog(LOG_INFO, "Client %s tried to redirect port to %s",
  348: 			       inet_ntoa(h->clientaddr), int_ip);
  349: 			ClearNameValueList(&data);
  350: 			SoapError(h, 718, "ConflictInMappingEntry");
  351: 			return;
  352: 		}
  353: 	}
  354: 
  355: 	int_port = GetValueFromNameValueList(&data, "NewInternalPort");
  356: 	ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
  357: 	protocol = GetValueFromNameValueList(&data, "NewProtocol");
  358: 	desc = GetValueFromNameValueList(&data, "NewPortMappingDescription");
  359: 	leaseduration = GetValueFromNameValueList(&data, "NewLeaseDuration");
  360: 
  361: 	if (!int_port || !ext_port || !protocol)
  362: 	{
  363: 		ClearNameValueList(&data);
  364: 		SoapError(h, 402, "Invalid Args");
  365: 		return;
  366: 	}
  367: 
  368: 	eport = (unsigned short)atoi(ext_port);
  369: 	iport = (unsigned short)atoi(int_port);
  370: 
  371: 	if(leaseduration && atoi(leaseduration)) {
  372: 		/* at the moment, lease duration is always infinite */
  373: 		syslog(LOG_WARNING, "NewLeaseDuration=%s not supported, ignored. (ip=%s, desc='%s')", leaseduration, int_ip, desc);
  374: 	}
  375: 
  376: 	syslog(LOG_INFO, "%s: ext port %hu to %s:%hu protocol %s for: %s",
  377: 			action, eport, int_ip, iport, protocol, desc);
  378: 
  379: 	r = upnp_redirect(eport, int_ip, iport, protocol, desc);
  380: 
  381: 	ClearNameValueList(&data);
  382: 
  383: 	/* possible error codes for AddPortMapping :
  384: 	 * 402 - Invalid Args
  385: 	 * 501 - Action Failed
  386: 	 * 715 - Wildcard not permited in SrcAddr
  387: 	 * 716 - Wildcard not permited in ExtPort
  388: 	 * 718 - ConflictInMappingEntry
  389: 	 * 724 - SamePortValuesRequired
  390:      * 725 - OnlyPermanentLeasesSupported
  391:              The NAT implementation only supports permanent lease times on
  392:              port mappings
  393:      * 726 - RemoteHostOnlySupportsWildcard
  394:              RemoteHost must be a wildcard and cannot be a specific IP
  395:              address or DNS name
  396:      * 727 - ExternalPortOnlySupportsWildcard
  397:              ExternalPort must be a wildcard and cannot be a specific port
  398:              value
  399:      * 728 - NoPortMapsAvailable
  400:              There are not enough free prots available to complete the mapping
  401:              (added in IGD v2) */
  402: 	switch(r)
  403: 	{
  404: 	case 0:	/* success */
  405: 		BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
  406: 		break;
  407: 	case -2:	/* already redirected */
  408: 	case -3:	/* not permitted */
  409: 		SoapError(h, 718, "ConflictInMappingEntry");
  410: 		break;
  411: 	default:
  412: 		SoapError(h, 501, "ActionFailed");
  413: 	}
  414: }
  415: 
  416: /* AddAnyPortMapping was added in WANIPConnection v2 */
  417: static void
  418: AddAnyPortMapping(struct upnphttp * h, const char * action)
  419: {
  420: 	int r;
  421: 	static const char resp[] =
  422: 		"<u:%sResponse "
  423: 		"xmlns:u=\"%s\">"
  424: 		"<NewReservedPort>%hu</NewReservedPort>"
  425: 		"</u:%sResponse>";
  426: 
  427: 	char body[512];
  428: 	int bodylen;
  429: 
  430: 	struct NameValueParserData data;
  431: 	const char * int_ip, * int_port, * ext_port, * protocol, * desc;
  432: 	const char * r_host;
  433: 	unsigned short iport, eport;
  434: 	unsigned int leaseduration;
  435: 
  436: 	struct hostent *hp; /* getbyhostname() */
  437: 	char ** ptr; /* getbyhostname() */
  438: 	struct in_addr result_ip;/*unsigned char result_ip[16];*/ /* inet_pton() */
  439: 
  440: 	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
  441: 	r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
  442: 	ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
  443: 	protocol = GetValueFromNameValueList(&data, "NewProtocol");
  444: 	int_port = GetValueFromNameValueList(&data, "NewInternalPort");
  445: 	int_ip = GetValueFromNameValueList(&data, "NewInternalClient");
  446: 	/* NewEnabled */
  447: 	desc = GetValueFromNameValueList(&data, "NewPortMappingDescription");
  448: 	leaseduration = atoi(GetValueFromNameValueList(&data, "NewLeaseDuration"));
  449: 	if(leaseduration == 0)
  450: 		leaseduration = 604800;
  451: 
  452: 	eport = (unsigned short)atoi(ext_port);
  453: 	iport = (unsigned short)atoi(int_port);
  454: 
  455: 	if (!int_ip)
  456: 	{
  457: 		ClearNameValueList(&data);
  458: 		SoapError(h, 402, "Invalid Args");
  459: 		return;
  460: 	}
  461: 
  462: 	/* if ip not valid assume hostname and convert */
  463: 	if (inet_pton(AF_INET, int_ip, &result_ip) <= 0) 
  464: 	{
  465: 		hp = gethostbyname(int_ip);
  466: 		if(hp && hp->h_addrtype == AF_INET) 
  467: 		{ 
  468: 			for(ptr = hp->h_addr_list; ptr && *ptr; ptr++)
  469: 		   	{
  470: 				int_ip = inet_ntoa(*((struct in_addr *) *ptr));
  471: 				result_ip = *((struct in_addr *) *ptr);
  472: 				/* TODO : deal with more than one ip per hostname */
  473: 				break;
  474: 			}
  475: 		} 
  476: 		else 
  477: 		{
  478: 			syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip); 
  479: 			ClearNameValueList(&data);
  480: 			SoapError(h, 402, "Invalid Args");
  481: 			return;
  482: 		}				
  483: 	}
  484: 
  485: 	/* check if NewInternalAddress is the client address */
  486: 	if(GETFLAG(SECUREMODEMASK))
  487: 	{
  488: 		if(h->clientaddr.s_addr != result_ip.s_addr)
  489: 		{
  490: 			syslog(LOG_INFO, "Client %s tried to redirect port to %s",
  491: 			       inet_ntoa(h->clientaddr), int_ip);
  492: 			ClearNameValueList(&data);
  493: 			SoapError(h, 606, "Action not authorized");
  494: 			return;
  495: 		}
  496: 	}
  497: 
  498: 	/* TODO : accept a different external port */
  499: 	r = upnp_redirect(eport, int_ip, iport, protocol, desc);
  500: 
  501: 	ClearNameValueList(&data);
  502: 
  503: 	switch(r)
  504: 	{
  505: 	case 0:	/* success */
  506: 		bodylen = snprintf(body, sizeof(body), resp,
  507: 		              action, "urn:schemas-upnp-org:service:WANIPConnection:2",
  508: 					  eport, action);
  509: 		BuildSendAndCloseSoapResp(h, body, bodylen);
  510: 		break;
  511: 	case -2:	/* already redirected */
  512: 		SoapError(h, 718, "ConflictInMappingEntry");
  513: 		break;
  514: 	case -3:	/* not permitted */
  515: 		SoapError(h, 606, "Action not authorized");
  516: 		break;
  517: 	default:
  518: 		SoapError(h, 501, "ActionFailed");
  519: 	}
  520: }
  521: 
  522: static void
  523: GetSpecificPortMappingEntry(struct upnphttp * h, const char * action)
  524: {
  525: 	int r;
  526: 
  527: 	static const char resp[] =
  528: 		"<u:%sResponse "
  529: 		"xmlns:u=\"%s\">"
  530: 		"<NewInternalPort>%u</NewInternalPort>"
  531: 		"<NewInternalClient>%s</NewInternalClient>"
  532: 		"<NewEnabled>1</NewEnabled>"
  533: 		"<NewPortMappingDescription>%s</NewPortMappingDescription>"
  534: 		"<NewLeaseDuration>0</NewLeaseDuration>"
  535: 		"</u:%sResponse>";
  536: 
  537: 	char body[1024];
  538: 	int bodylen;
  539: 	struct NameValueParserData data;
  540: 	const char * r_host, * ext_port, * protocol;
  541: 	unsigned short eport, iport;
  542: 	char int_ip[32];
  543: 	char desc[64];
  544: 
  545: 	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
  546: 	r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
  547: 	ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
  548: 	protocol = GetValueFromNameValueList(&data, "NewProtocol");
  549: 
  550: 	if(!ext_port || !protocol)
  551: 	{
  552: 		ClearNameValueList(&data);
  553: 		SoapError(h, 402, "Invalid Args");
  554: 		return;
  555: 	}
  556: 
  557: 	eport = (unsigned short)atoi(ext_port);
  558: 
  559: 	r = upnp_get_redirection_infos(eport, protocol, &iport,
  560: 	                               int_ip, sizeof(int_ip),
  561: 	                               desc, sizeof(desc));
  562: 
  563: 	if(r < 0)
  564: 	{		
  565: 		SoapError(h, 714, "NoSuchEntryInArray");
  566: 	}
  567: 	else
  568: 	{
  569: 		syslog(LOG_INFO, "%s: rhost='%s' %s %s found => %s:%u desc='%s'",
  570: 		       action,
  571: 		       r_host, ext_port, protocol, int_ip, (unsigned int)iport, desc);
  572: 		bodylen = snprintf(body, sizeof(body), resp,
  573: 				action, "urn:schemas-upnp-org:service:WANIPConnection:1",
  574: 				(unsigned int)iport, int_ip, desc,
  575: 				action);
  576: 		BuildSendAndCloseSoapResp(h, body, bodylen);
  577: 	}
  578: 
  579: 	ClearNameValueList(&data);
  580: }
  581: 
  582: static void
  583: DeletePortMapping(struct upnphttp * h, const char * action)
  584: {
  585: 	int r;
  586: 
  587: 	static const char resp[] =
  588: 		"<u:DeletePortMappingResponse "
  589: 		"xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">"
  590: 		"</u:DeletePortMappingResponse>";
  591: 
  592: 	struct NameValueParserData data;
  593: 	const char * r_host, * ext_port, * protocol;
  594: 	unsigned short eport;
  595: 
  596: 	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
  597: 	r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
  598: 	ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
  599: 	protocol = GetValueFromNameValueList(&data, "NewProtocol");
  600: 
  601: 	if(!ext_port || !protocol)
  602: 	{
  603: 		ClearNameValueList(&data);
  604: 		SoapError(h, 402, "Invalid Args");
  605: 		return;
  606: 	}
  607: 
  608: 	eport = (unsigned short)atoi(ext_port);
  609: 
  610: 	/* TODO : if in secure mode, check the IP */
  611: 
  612: 	syslog(LOG_INFO, "%s: external port: %hu, protocol: %s", 
  613: 		action, eport, protocol);
  614: 
  615: 	r = upnp_delete_redirection(eport, protocol);
  616: 
  617: 	if(r < 0)
  618: 	{	
  619: 		SoapError(h, 714, "NoSuchEntryInArray");
  620: 	}
  621: 	else
  622: 	{
  623: 		BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
  624: 	}
  625: 
  626: 	ClearNameValueList(&data);
  627: }
  628: 
  629: /* DeletePortMappingRange was added in IGD spec v2 */
  630: static void
  631: DeletePortMappingRange(struct upnphttp * h, const char * action)
  632: {
  633: 	static const char resp[] =
  634: 		"<u:DeletePortMappingRangeResponse "
  635: 		"xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:2\">"
  636: 		"</u:DeletePortMappingRangeResponse>";
  637: 	struct NameValueParserData data;
  638: 	const char * protocol;
  639: 	unsigned short startport, endport;
  640: 	int manage;
  641: 
  642: 	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
  643: 	startport = (unsigned short)atoi(GetValueFromNameValueList(&data, "NewStartPort"));
  644: 	endport = (unsigned short)atoi(GetValueFromNameValueList(&data, "NewEndPort"));
  645: 	protocol = GetValueFromNameValueList(&data, "NewProtocol");
  646: 	manage = atoi(GetValueFromNameValueList(&data, "NewManage"));
  647: 
  648: 	/* TODO : implement the method ! */
  649: 
  650: 	/* possible errors :
  651: 	   606 - Action not authorized
  652: 	   730 - PortMappingNotFound
  653: 	   733 - InconsistentParameter
  654: 	 */
  655: 	BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
  656: 
  657: 	ClearNameValueList(&data);
  658: }
  659: 
  660: static void
  661: GetGenericPortMappingEntry(struct upnphttp * h, const char * action)
  662: {
  663: 	int r;
  664: 	
  665: 	static const char resp[] =
  666: 		"<u:%sResponse "
  667: 		"xmlns:u=\"%s\">"
  668: 		"<NewRemoteHost></NewRemoteHost>"
  669: 		"<NewExternalPort>%u</NewExternalPort>"
  670: 		"<NewProtocol>%s</NewProtocol>"
  671: 		"<NewInternalPort>%u</NewInternalPort>"
  672: 		"<NewInternalClient>%s</NewInternalClient>"
  673: 		"<NewEnabled>1</NewEnabled>"
  674: 		"<NewPortMappingDescription>%s</NewPortMappingDescription>"
  675: 		"<NewLeaseDuration>0</NewLeaseDuration>"
  676: 		"</u:%sResponse>";
  677: 
  678: 	int index = 0;
  679: 	unsigned short eport, iport;
  680: 	const char * m_index;
  681: 	char protocol[4], iaddr[32];
  682: 	char desc[64];
  683: 	struct NameValueParserData data;
  684: 
  685: 	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
  686: 	m_index = GetValueFromNameValueList(&data, "NewPortMappingIndex");
  687: 
  688: 	if(!m_index)
  689: 	{
  690: 		ClearNameValueList(&data);
  691: 		SoapError(h, 402, "Invalid Args");
  692: 		return;
  693: 	}	
  694: 
  695: 	index = (int)atoi(m_index);
  696: 
  697: 	syslog(LOG_INFO, "%s: index=%d", action, index);
  698: 
  699: 	r = upnp_get_redirection_infos_by_index(index, &eport, protocol, &iport,
  700:                                             iaddr, sizeof(iaddr),
  701: 	                                        desc, sizeof(desc));
  702: 
  703: 	if(r < 0)
  704: 	{
  705: 		SoapError(h, 713, "SpecifiedArrayIndexInvalid");
  706: 	}
  707: 	else
  708: 	{
  709: 		int bodylen;
  710: 		char body[2048];
  711: 		bodylen = snprintf(body, sizeof(body), resp,
  712: 			action, "urn:schemas-upnp-org:service:WANIPConnection:1",
  713: 			(unsigned int)eport, protocol, (unsigned int)iport, iaddr, desc,
  714: 			action);
  715: 		BuildSendAndCloseSoapResp(h, body, bodylen);
  716: 	}
  717: 
  718: 	ClearNameValueList(&data);
  719: }
  720: 
  721: /* GetListOfPortMappings was added in the IGD v2 specification */
  722: static void
  723: GetListOfPortMappings(struct upnphttp * h, const char * action)
  724: {
  725: 	static const char resp[] =
  726: 		"<u:%sResponse "
  727: 		"xmlns:u=\"%s\">"
  728: 		"<NewPortListing><![CDATA[%s]]</NewPortListing>"
  729: 		"</u:%sResponse>";
  730: 
  731: 	char body[512];
  732: 	int bodylen;
  733: 
  734: 	struct NameValueParserData data;
  735: 	unsigned short startport, endport;
  736: 	const char * protocol;
  737: 	int manage;
  738: 	int number;
  739: 
  740: 	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
  741: 	startport = (unsigned short)atoi(GetValueFromNameValueList(&data, "NewStartPort"));
  742: 	endport = (unsigned short)atoi(GetValueFromNameValueList(&data, "NewEndPort"));
  743: 	protocol = GetValueFromNameValueList(&data, "NewProtocol");
  744: 	manage = atoi(GetValueFromNameValueList(&data, "NewManage"));
  745: 	number = atoi(GetValueFromNameValueList(&data, "NewNumberOfPorts"));
  746: 
  747: /*
  748: TODO : build the PortMappingList xml document :
  749: 
  750: <p:PortMappingList xmlns:p="urn:schemas-upnp-org:gw:WANIPConnection"
  751: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  752: xsi:schemaLocation="urn:schemas-upnp-org:gw:WANIPConnection
  753: http://www.upnp.org/schemas/gw/WANIPConnection-v2.xsd">
  754: <p:PortMappingEntry>
  755: <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
  756: <p:NewExternalPort>2345</p:NewExternalPort>
  757: <p:NewProtocol>TCP</p:NewProtocol>
  758: <p:NewInternalPort>2345</p:NewInternalPort>
  759: <p:NewInternalClient>192.168.1.137</p:NewInternalClient>
  760: <p:NewEnabled>1</p:NewEnabled>
  761: <p:NewDescription>dooom</p:NewDescription>
  762: <p:NewLeaseTime>345</p:NewLeaseTime>
  763: </p:PortMappingEntry>
  764: </p:PortMappingList>
  765: */
  766: 	bodylen = snprintf(body, sizeof(body), resp,
  767: 	              action, "urn:schemas-upnp-org:service:WANIPConnection:2",
  768: 				  "", action);
  769: 	BuildSendAndCloseSoapResp(h, body, bodylen);
  770: 
  771: 	ClearNameValueList(&data);
  772: }
  773: 
  774: #ifdef ENABLE_L3F_SERVICE
  775: static void
  776: SetDefaultConnectionService(struct upnphttp * h, const char * action)
  777: {
  778: 	static const char resp[] =
  779: 		"<u:SetDefaultConnectionServiceResponse "
  780: 		"xmlns:u=\"urn:schemas-upnp-org:service:Layer3Forwarding:1\">"
  781: 		"</u:SetDefaultConnectionServiceResponse>";
  782: 	struct NameValueParserData data;
  783: 	char * p;
  784: 	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
  785: 	p = GetValueFromNameValueList(&data, "NewDefaultConnectionService");
  786: 	if(p) {
  787: 		syslog(LOG_INFO, "%s(%s) : Ignored", action, p);
  788: 	}
  789: 	ClearNameValueList(&data);
  790: 	BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
  791: }
  792: 
  793: static void
  794: GetDefaultConnectionService(struct upnphttp * h, const char * action)
  795: {
  796: 	static const char resp[] =
  797: 		"<u:%sResponse "
  798: 		"xmlns:u=\"urn:schemas-upnp-org:service:Layer3Forwarding:1\">"
  799: 		"<NewDefaultConnectionService>%s:WANConnectionDevice:1,"
  800: 		"urn:upnp-org:serviceId:WANIPConn1</NewDefaultConnectionService>"
  801: 		"</u:%sResponse>";
  802: 	/* example from UPnP_IGD_Layer3Forwarding 1.0.pdf :
  803: 	 * uuid:44f5824f-c57d-418c-a131-f22b34e14111:WANConnectionDevice:1,
  804: 	 * urn:upnp-org:serviceId:WANPPPConn1 */
  805: 	char body[1024];
  806: 	int bodylen;
  807: 
  808: 	bodylen = snprintf(body, sizeof(body), resp,
  809: 	                   action, uuidvalue, action);
  810: 	BuildSendAndCloseSoapResp(h, body, bodylen);
  811: }
  812: #endif
  813: 
  814: /* Added for compliance with WANIPConnection v2 */
  815: static void
  816: SetConnectionType(struct upnphttp * h, const char * action)
  817: {
  818: 	const char * connection_type;
  819: 	struct NameValueParserData data;
  820: 
  821: 	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
  822: 	connection_type = GetValueFromNameValueList(&data, "NewConnectionType");
  823: 	/* Unconfigured, IP_Routed, IP_Bridged */
  824: 	ClearNameValueList(&data);
  825: 	/* always return a ReadOnly error */
  826: 	SoapError(h, 731, "ReadOnly");
  827: }
  828: 
  829: /* Added for compliance with WANIPConnection v2 */
  830: static void
  831: RequestConnection(struct upnphttp * h, const char * action)
  832: {
  833: 	SoapError(h, 606, "Action not authorized");
  834: }
  835: 
  836: /* Added for compliance with WANIPConnection v2 */
  837: static void
  838: ForceTermination(struct upnphttp * h, const char * action)
  839: {
  840: 	SoapError(h, 606, "Action not authorized");
  841: }
  842: 
  843: /*
  844: If a control point calls QueryStateVariable on a state variable that is not
  845: buffered in memory within (or otherwise available from) the service,
  846: the service must return a SOAP fault with an errorCode of 404 Invalid Var.
  847: 
  848: QueryStateVariable remains useful as a limited test tool but may not be
  849: part of some future versions of UPnP.
  850: */
  851: static void
  852: QueryStateVariable(struct upnphttp * h, const char * action)
  853: {
  854: 	static const char resp[] =
  855:         "<u:%sResponse "
  856:         "xmlns:u=\"%s\">"
  857: 		"<return>%s</return>"
  858:         "</u:%sResponse>";
  859: 
  860: 	char body[512];
  861: 	int bodylen;
  862: 	struct NameValueParserData data;
  863: 	const char * var_name;
  864: 
  865: 	ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
  866: 	/*var_name = GetValueFromNameValueList(&data, "QueryStateVariable"); */
  867: 	/*var_name = GetValueFromNameValueListIgnoreNS(&data, "varName");*/
  868: 	var_name = GetValueFromNameValueList(&data, "varName");
  869: 
  870: 	/*syslog(LOG_INFO, "QueryStateVariable(%.40s)", var_name); */
  871: 
  872: 	if(!var_name)
  873: 	{
  874: 		SoapError(h, 402, "Invalid Args");
  875: 	}
  876: 	else if(strcmp(var_name, "ConnectionStatus") == 0)
  877: 	{	
  878: 		const char * status = "Connected";
  879: 		char ext_ip_addr[INET_ADDRSTRLEN];
  880: 		if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0) {
  881: 			status = "Disconnected";
  882: 		}
  883: 		bodylen = snprintf(body, sizeof(body), resp,
  884:                            action, "urn:schemas-upnp-org:control-1-0",
  885: 		                   status, action);
  886: 		BuildSendAndCloseSoapResp(h, body, bodylen);
  887: 	}
  888: #if 0
  889: 	/* not usefull */
  890: 	else if(strcmp(var_name, "ConnectionType") == 0)
  891: 	{	
  892: 		bodylen = snprintf(body, sizeof(body), resp, "IP_Routed");
  893: 		BuildSendAndCloseSoapResp(h, body, bodylen);
  894: 	}
  895: 	else if(strcmp(var_name, "LastConnectionError") == 0)
  896: 	{	
  897: 		bodylen = snprintf(body, sizeof(body), resp, "ERROR_NONE");
  898: 		BuildSendAndCloseSoapResp(h, body, bodylen);
  899: 	}
  900: #endif
  901: 	else if(strcmp(var_name, "PortMappingNumberOfEntries") == 0)
  902: 	{
  903: 		char strn[10];
  904: 		snprintf(strn, sizeof(strn), "%i",
  905: 		         upnp_get_portmapping_number_of_entries());
  906: 		bodylen = snprintf(body, sizeof(body), resp,
  907:                            action, "urn:schemas-upnp-org:control-1-0",
  908: 		                   strn, action);
  909: 		BuildSendAndCloseSoapResp(h, body, bodylen);
  910: 	}
  911: 	else
  912: 	{
  913: 		syslog(LOG_NOTICE, "%s: Unknown: %s", action, var_name?var_name:"");
  914: 		SoapError(h, 404, "Invalid Var");
  915: 	}
  916: 
  917: 	ClearNameValueList(&data);	
  918: }
  919: 
  920: /* Windows XP as client send the following requests :
  921:  * GetConnectionTypeInfo
  922:  * GetNATRSIPStatus
  923:  * ? GetTotalBytesSent - WANCommonInterfaceConfig
  924:  * ? GetTotalBytesReceived - idem
  925:  * ? GetTotalPacketsSent - idem
  926:  * ? GetTotalPacketsReceived - idem
  927:  * GetCommonLinkProperties - idem
  928:  * GetStatusInfo - WANIPConnection
  929:  * GetExternalIPAddress
  930:  * QueryStateVariable / ConnectionStatus!
  931:  */
  932: static const struct 
  933: {
  934: 	const char * methodName; 
  935: 	void (*methodImpl)(struct upnphttp *, const char *);
  936: }
  937: soapMethods[] =
  938: {
  939: 	{ "GetConnectionTypeInfo", GetConnectionTypeInfo },
  940: 	{ "GetNATRSIPStatus", GetNATRSIPStatus},
  941: 	{ "GetExternalIPAddress", GetExternalIPAddress},
  942: 	{ "AddPortMapping", AddPortMapping},
  943: 	{ "DeletePortMapping", DeletePortMapping},
  944: 	{ "GetGenericPortMappingEntry", GetGenericPortMappingEntry},
  945: 	{ "GetSpecificPortMappingEntry", GetSpecificPortMappingEntry},
  946: 	{ "QueryStateVariable", QueryStateVariable},
  947: 	{ "GetTotalBytesSent", GetTotalBytesSent},
  948: 	{ "GetTotalBytesReceived", GetTotalBytesReceived},
  949: 	{ "GetTotalPacketsSent", GetTotalPacketsSent},
  950: 	{ "GetTotalPacketsReceived", GetTotalPacketsReceived},
  951: 	{ "GetCommonLinkProperties", GetCommonLinkProperties},
  952: 	{ "GetStatusInfo", GetStatusInfo},
  953: /* Required in WANIPConnection:2 */
  954: 	{ "SetConnectionType", SetConnectionType},
  955: 	{ "RequestConnection", RequestConnection},
  956: 	{ "ForceTermination", ForceTermination},
  957: 	{ "AddAnyPortMapping", AddAnyPortMapping},
  958: 	{ "DeletePortMappingRange", DeletePortMappingRange},
  959: 	{ "GetListOfPortMappings", GetListOfPortMappings},
  960: #ifdef ENABLE_L3F_SERVICE
  961: 	{ "SetDefaultConnectionService", SetDefaultConnectionService},
  962: 	{ "GetDefaultConnectionService", GetDefaultConnectionService},
  963: #endif
  964: 	{ 0, 0 }
  965: };
  966: 
  967: void
  968: ExecuteSoapAction(struct upnphttp * h, const char * action, int n)
  969: {
  970: 	char * p;
  971: 	char * p2;
  972: 	int i, len, methodlen;
  973: 
  974: 	i = 0;
  975: 	p = strchr(action, '#');
  976: 
  977: 	if(p)
  978: 	{
  979: 		p++;
  980: 		p2 = strchr(p, '"');
  981: 		if(p2)
  982: 			methodlen = p2 - p;
  983: 		else
  984: 			methodlen = n - (p - action);
  985: 		/*syslog(LOG_DEBUG, "SoapMethod: %.*s", methodlen, p);*/
  986: 		while(soapMethods[i].methodName)
  987: 		{
  988: 			len = strlen(soapMethods[i].methodName);
  989: 			if(strncmp(p, soapMethods[i].methodName, len) == 0)
  990: 			{
  991: 				soapMethods[i].methodImpl(h, soapMethods[i].methodName);
  992: 				return;
  993: 			}
  994: 			i++;
  995: 		}
  996: 
  997: 		syslog(LOG_NOTICE, "SoapMethod: Unknown: %.*s", methodlen, p);
  998: 	}
  999: 
 1000: 	SoapError(h, 401, "Invalid Action");
 1001: }
 1002: 
 1003: /* Standard Errors:
 1004:  *
 1005:  * errorCode errorDescription Description
 1006:  * --------	---------------- -----------
 1007:  * 401 		Invalid Action 	No action by that name at this service.
 1008:  * 402 		Invalid Args 	Could be any of the following: not enough in args,
 1009:  * 							too many in args, no in arg by that name, 
 1010:  * 							one or more in args are of the wrong data type.
 1011:  * 403 		Out of Sync 	Out of synchronization.
 1012:  * 501 		Action Failed 	May be returned in current state of service
 1013:  * 							prevents invoking that action.
 1014:  * 600-699 	TBD 			Common action errors. Defined by UPnP Forum
 1015:  * 							Technical Committee.
 1016:  * 700-799 	TBD 			Action-specific errors for standard actions.
 1017:  * 							Defined by UPnP Forum working committee.
 1018:  * 800-899 	TBD 			Action-specific errors for non-standard actions. 
 1019:  * 							Defined by UPnP vendor.
 1020: */
 1021: void
 1022: SoapError(struct upnphttp * h, int errCode, const char * errDesc)
 1023: {
 1024: 	static const char resp[] = 
 1025: 		"<s:Envelope "
 1026: 		"xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
 1027: 		"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
 1028: 		"<s:Body>"
 1029: 		"<s:Fault>"
 1030: 		"<faultcode>s:Client</faultcode>"
 1031: 		"<faultstring>UPnPError</faultstring>"
 1032: 		"<detail>"
 1033: 		"<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">"
 1034: 		"<errorCode>%d</errorCode>"
 1035: 		"<errorDescription>%s</errorDescription>"
 1036: 		"</UPnPError>"
 1037: 		"</detail>"
 1038: 		"</s:Fault>"
 1039: 		"</s:Body>"
 1040: 		"</s:Envelope>";
 1041: 
 1042: 	char body[2048];
 1043: 	int bodylen;
 1044: 
 1045: 	syslog(LOG_INFO, "Returning UPnPError %d: %s", errCode, errDesc);
 1046: 	bodylen = snprintf(body, sizeof(body), resp, errCode, errDesc);
 1047: 	BuildResp2_upnphttp(h, 500, "Internal Server Error", body, bodylen);
 1048: 	SendResp_upnphttp(h);
 1049: 	CloseSocket_upnphttp(h);
 1050: }
 1051: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>