File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / miniupnpd / upnphttp.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 00:32:35 2013 UTC (10 years, 10 months ago) by misho
Branches: miniupnpd, elwix, MAIN
CVS tags: v1_8p0, v1_8, HEAD
1.8

    1: /* $Id: upnphttp.c,v 1.1.1.3 2013/07/22 00:32:35 misho Exp $ */
    2: /* Project :  miniupnp
    3:  * Website :  http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
    4:  * Author :   Thomas Bernard
    5:  * Copyright (c) 2005-2012 Thomas Bernard
    6:  * This software is subject to the conditions detailed in the
    7:  * LICENCE file included in this distribution.
    8:  * */
    9: #include <stdlib.h>
   10: #include <unistd.h>
   11: #include <stdio.h>
   12: #include <string.h>
   13: #include <sys/types.h>
   14: #include <sys/socket.h>
   15: #include <sys/param.h>
   16: #include <netinet/in.h>
   17: #include <arpa/inet.h>
   18: #include <syslog.h>
   19: #include <ctype.h>
   20: #include <errno.h>
   21: #include "config.h"
   22: #ifdef ENABLE_HTTP_DATE
   23: #include <time.h>
   24: #endif
   25: #include "upnphttp.h"
   26: #include "upnpdescgen.h"
   27: #include "miniupnpdpath.h"
   28: #include "upnpsoap.h"
   29: #include "upnpevents.h"
   30: #include "upnputils.h"
   31: 
   32: struct upnphttp *
   33: New_upnphttp(int s)
   34: {
   35: 	struct upnphttp * ret;
   36: 	if(s<0)
   37: 		return NULL;
   38: 	ret = (struct upnphttp *)malloc(sizeof(struct upnphttp));
   39: 	if(ret == NULL)
   40: 		return NULL;
   41: 	memset(ret, 0, sizeof(struct upnphttp));
   42: 	ret->socket = s;
   43: 	if(!set_non_blocking(s))
   44: 		syslog(LOG_WARNING, "New_upnphttp::set_non_blocking(): %m");
   45: 	return ret;
   46: }
   47: 
   48: void
   49: CloseSocket_upnphttp(struct upnphttp * h)
   50: {
   51: 	if(close(h->socket) < 0)
   52: 	{
   53: 		syslog(LOG_ERR, "CloseSocket_upnphttp: close(%d): %m", h->socket);
   54: 	}
   55: 	h->socket = -1;
   56: 	h->state = EToDelete;
   57: }
   58: 
   59: void
   60: Delete_upnphttp(struct upnphttp * h)
   61: {
   62: 	if(h)
   63: 	{
   64: 		if(h->socket >= 0)
   65: 			CloseSocket_upnphttp(h);
   66: 		if(h->req_buf)
   67: 			free(h->req_buf);
   68: 		if(h->res_buf)
   69: 			free(h->res_buf);
   70: 		free(h);
   71: 	}
   72: }
   73: 
   74: /* parse HttpHeaders of the REQUEST
   75:  * This function is called after the \r\n\r\n character
   76:  * sequence has been found in h->req_buf */
   77: static void
   78: ParseHttpHeaders(struct upnphttp * h)
   79: {
   80: 	char * line;
   81: 	char * colon;
   82: 	char * p;
   83: 	int n;
   84: 	if((h->req_buf == NULL) || (h->req_contentoff <= 0))
   85: 		return;
   86: 	line = h->req_buf;
   87: 	while(line < (h->req_buf + h->req_contentoff))
   88: 	{
   89: 		colon = line;
   90: 		while(*colon != ':')
   91: 		{
   92: 			if(*colon == '\r' || *colon == '\n')
   93: 			{
   94: 				colon = NULL;	/* no ':' character found on the line */
   95: 				break;
   96: 			}
   97: 			colon++;
   98: 		}
   99: 		if(colon)
  100: 		{
  101: 			if(strncasecmp(line, "Content-Length", 14)==0)
  102: 			{
  103: 				p = colon;
  104: 				while(*p < '0' || *p > '9')
  105: 					p++;
  106: 				h->req_contentlen = atoi(p);
  107: 				if(h->req_contentlen < 0) {
  108: 					syslog(LOG_WARNING, "ParseHttpHeaders() invalid Content-Length %d", h->req_contentlen);
  109: 					h->req_contentlen = 0;
  110: 				}
  111: 				/*printf("*** Content-Lenght = %d ***\n", h->req_contentlen);
  112: 				printf("    readbufflen=%d contentoff = %d\n",
  113: 					h->req_buflen, h->req_contentoff);*/
  114: 			}
  115: 			else if(strncasecmp(line, "SOAPAction", 10)==0)
  116: 			{
  117: 				p = colon;
  118: 				n = 0;
  119: 				while(*p == ':' || *p == ' ' || *p == '\t')
  120: 					p++;
  121: 				while(p[n]>=' ')
  122: 					n++;
  123: 				if((p[0] == '"' && p[n-1] == '"')
  124: 				  || (p[0] == '\'' && p[n-1] == '\''))
  125: 				{
  126: 					p++; n -= 2;
  127: 				}
  128: 				h->req_soapActionOff = p - h->req_buf;
  129: 				h->req_soapActionLen = n;
  130: 			}
  131: 			else if(strncasecmp(line, "accept-language", 15) == 0)
  132: 			{
  133: 				p = colon;
  134: 				n = 0;
  135: 				while(*p == ':' || *p == ' ' || *p == '\t')
  136: 					p++;
  137: 				while(p[n]>=' ')
  138: 					n++;
  139: 				syslog(LOG_DEBUG, "accept-language HTTP header : '%.*s'", n, p);
  140: 				/* keep only the 1st accepted language */
  141: 				n = 0;
  142: 				while(p[n]>' ' && p[n] != ',')
  143: 					n++;
  144: 				if(n >= (int)sizeof(h->accept_language))
  145: 					n = (int)sizeof(h->accept_language) - 1;
  146: 				memcpy(h->accept_language, p, n);
  147: 				h->accept_language[n] = '\0';
  148: 			}
  149: 			else if(strncasecmp(line, "expect", 6) == 0)
  150: 			{
  151: 				p = colon;
  152: 				n = 0;
  153: 				while(*p == ':' || *p == ' ' || *p == '\t')
  154: 					p++;
  155: 				while(p[n]>=' ')
  156: 					n++;
  157: 				if(strncasecmp(p, "100-continue", 12) == 0) {
  158: 					h->respflags |= FLAG_CONTINUE;
  159: 					syslog(LOG_DEBUG, "\"Expect: 100-Continue\" header detected");
  160: 				}
  161: 			}
  162: #ifdef ENABLE_EVENTS
  163: 			else if(strncasecmp(line, "Callback", 8)==0)
  164: 			{
  165: 				p = colon;
  166: 				while(*p != '<' && *p != '\r' )
  167: 					p++;
  168: 				n = 0;
  169: 				while(p[n] != '>' && p[n] != '\r' )
  170: 					n++;
  171: 				h->req_CallbackOff = p + 1 - h->req_buf;
  172: 				h->req_CallbackLen = MAX(0, n - 1);
  173: 			}
  174: 			else if(strncasecmp(line, "SID", 3)==0)
  175: 			{
  176: 				p = colon + 1;
  177: 				while(isspace(*p))
  178: 					p++;
  179: 				n = 0;
  180: 				while(!isspace(p[n]))
  181: 					n++;
  182: 				h->req_SIDOff = p - h->req_buf;
  183: 				h->req_SIDLen = n;
  184: 			}
  185: 			/* Timeout: Seconds-nnnn */
  186: /* TIMEOUT
  187: Recommended. Requested duration until subscription expires,
  188: either number of seconds or infinite. Recommendation
  189: by a UPnP Forum working committee. Defined by UPnP vendor.
  190:  Consists of the keyword "Second-" followed (without an
  191: intervening space) by either an integer or the keyword "infinite". */
  192: 			else if(strncasecmp(line, "Timeout", 7)==0)
  193: 			{
  194: 				p = colon + 1;
  195: 				while(isspace(*p))
  196: 					p++;
  197: 				if(strncasecmp(p, "Second-", 7)==0) {
  198: 					h->req_Timeout = atoi(p+7);
  199: 				}
  200: 			}
  201: #ifdef UPNP_STRICT
  202: 			else if(strncasecmp(line, "nt", 2)==0)
  203: 			{
  204: 				p = colon + 1;
  205: 				while(isspace(*p))
  206: 					p++;
  207: 				n = 0;
  208: 				while(!isspace(p[n]))
  209: 					n++;
  210: 				h->req_NTOff = p - h->req_buf;
  211: 				h->req_NTLen = n;
  212: 			}
  213: #endif
  214: #endif
  215: 		}
  216: 		/* the loop below won't run off the end of the buffer
  217: 		 * because the buffer is guaranteed to contain the \r\n\r\n
  218: 		 * character sequence */
  219: 		while(!(line[0] == '\r' && line[1] == '\n'))
  220: 			line++;
  221: 		line += 2;
  222: 	}
  223: }
  224: 
  225: /* very minimalistic 404 error message */
  226: static void
  227: Send404(struct upnphttp * h)
  228: {
  229: 	static const char body404[] =
  230: 		"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD>"
  231: 		"<BODY><H1>Not Found</H1>The requested URL was not found"
  232: 		" on this server.</BODY></HTML>\r\n";
  233: 
  234: 	h->respflags = FLAG_HTML;
  235: 	BuildResp2_upnphttp(h, 404, "Not Found",
  236: 	                    body404, sizeof(body404) - 1);
  237: 	SendRespAndClose_upnphttp(h);
  238: }
  239: 
  240: static void
  241: Send405(struct upnphttp * h)
  242: {
  243: 	static const char body405[] =
  244: 		"<HTML><HEAD><TITLE>405 Method Not Allowed</TITLE></HEAD>"
  245: 		"<BODY><H1>Method Not Allowed</H1>The HTTP Method "
  246: 		"is not allowed on this resource.</BODY></HTML>\r\n";
  247: 
  248: 	h->respflags |= FLAG_HTML;
  249: 	BuildResp2_upnphttp(h, 405, "Method Not Allowed",
  250: 	                    body405, sizeof(body405) - 1);
  251: 	SendRespAndClose_upnphttp(h);
  252: }
  253: 
  254: /* very minimalistic 501 error message */
  255: static void
  256: Send501(struct upnphttp * h)
  257: {
  258: 	static const char body501[] =
  259: 		"<HTML><HEAD><TITLE>501 Not Implemented</TITLE></HEAD>"
  260: 		"<BODY><H1>Not Implemented</H1>The HTTP Method "
  261: 		"is not implemented by this server.</BODY></HTML>\r\n";
  262: 
  263: 	h->respflags = FLAG_HTML;
  264: 	BuildResp2_upnphttp(h, 501, "Not Implemented",
  265: 	                    body501, sizeof(body501) - 1);
  266: 	SendRespAndClose_upnphttp(h);
  267: }
  268: 
  269: /* findendheaders() find the \r\n\r\n character sequence and
  270:  * return a pointer to it.
  271:  * It returns NULL if not found */
  272: static const char *
  273: findendheaders(const char * s, int len)
  274: {
  275: 	while(len-->3)
  276: 	{
  277: 		if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n')
  278: 			return s;
  279: 		s++;
  280: 	}
  281: 	return NULL;
  282: }
  283: 
  284: #ifdef HAS_DUMMY_SERVICE
  285: static void
  286: sendDummyDesc(struct upnphttp * h)
  287: {
  288: 	static const char xml_desc[] = "<?xml version=\"1.0\"?>\r\n"
  289: 		"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">"
  290: 		" <specVersion>"
  291: 		"    <major>1</major>"
  292: 		"    <minor>0</minor>"
  293: 		"  </specVersion>"
  294: 		"  <actionList />"
  295: 		"  <serviceStateTable />"
  296: 		"</scpd>\r\n";
  297: 	BuildResp_upnphttp(h, xml_desc, sizeof(xml_desc)-1);
  298: 	SendRespAndClose_upnphttp(h);
  299: }
  300: #endif
  301: 
  302: /* Sends the description generated by the parameter */
  303: static void
  304: sendXMLdesc(struct upnphttp * h, char * (f)(int *))
  305: {
  306: 	char * desc;
  307: 	int len;
  308: 	desc = f(&len);
  309: 	if(!desc)
  310: 	{
  311: 		static const char error500[] = "<HTML><HEAD><TITLE>Error 500</TITLE>"
  312: 		   "</HEAD><BODY>Internal Server Error</BODY></HTML>\r\n";
  313: 		syslog(LOG_ERR, "Failed to generate XML description");
  314: 		h->respflags = FLAG_HTML;
  315: 		BuildResp2_upnphttp(h, 500, "Internal Server Error",
  316: 		                    error500, sizeof(error500)-1);
  317: 	}
  318: 	else
  319: 	{
  320: 		BuildResp_upnphttp(h, desc, len);
  321: 	}
  322: 	SendRespAndClose_upnphttp(h);
  323: 	free(desc);
  324: }
  325: 
  326: /* ProcessHTTPPOST_upnphttp()
  327:  * executes the SOAP query if it is possible */
  328: static void
  329: ProcessHTTPPOST_upnphttp(struct upnphttp * h)
  330: {
  331: 	if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
  332: 	{
  333: 		/* the request body is received */
  334: 		if(h->req_soapActionOff > 0)
  335: 		{
  336: 			/* we can process the request */
  337: 			syslog(LOG_INFO, "SOAPAction: %.*s",
  338: 			       h->req_soapActionLen, h->req_buf + h->req_soapActionOff);
  339: 			ExecuteSoapAction(h,
  340: 				h->req_buf + h->req_soapActionOff,
  341: 				h->req_soapActionLen);
  342: 		}
  343: 		else
  344: 		{
  345: 			static const char err400str[] =
  346: 				"<html><body>Bad request</body></html>";
  347: 			syslog(LOG_INFO, "No SOAPAction in HTTP headers");
  348: 			h->respflags = FLAG_HTML;
  349: 			BuildResp2_upnphttp(h, 400, "Bad Request",
  350: 			                    err400str, sizeof(err400str) - 1);
  351: 			SendRespAndClose_upnphttp(h);
  352: 		}
  353: 	}
  354: 	else if(h->respflags & FLAG_CONTINUE)
  355: 	{
  356: 		/* Sending the 100 Continue response */
  357: 		if(!h->res_buf) {
  358: 			h->res_buf = malloc(256);
  359: 			h->res_buf_alloclen = 256;
  360: 		}
  361: 		h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
  362: 		                         "%s 100 Continue\r\n\r\n", h->HttpVer);
  363: 		h->res_sent = 0;
  364: 		h->state = ESendingContinue;
  365: 		if(SendResp_upnphttp(h))
  366: 			h->state = EWaitingForHttpContent;
  367: 	}
  368: 	else
  369: 	{
  370: 		/* waiting for remaining data */
  371: 		h->state = EWaitingForHttpContent;
  372: 	}
  373: }
  374: 
  375: #ifdef ENABLE_EVENTS
  376: /**
  377:  * returns 0 if the callback header value is not valid
  378:  * 1 if it is valid.
  379:  */
  380: static int
  381: checkCallbackURL(struct upnphttp * h)
  382: {
  383: 	char addrstr[48];
  384: 	int ipv6;
  385: 	const char * p;
  386: 	unsigned int i;
  387: 
  388: 	if(h->req_CallbackOff <= 0 || h->req_CallbackLen < 8)
  389: 		return 0;
  390: 	if(memcmp(h->req_buf + h->req_CallbackOff, "http://", 7) != 0)
  391: 		return 0;
  392: 	ipv6 = 0;
  393: 	i = 0;
  394: 	p = h->req_buf + h->req_CallbackOff + 7;
  395: 	if(*p == '[') {
  396: 		p++;
  397: 		ipv6 = 1;
  398: 		while(*p != ']' && i < (sizeof(addrstr)-1)
  399: 		      && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
  400: 			addrstr[i++] = *(p++);
  401: 	} else {
  402: 		while(*p != '/' && *p != ':' && i < (sizeof(addrstr)-1)
  403: 		      && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
  404: 			addrstr[i++] = *(p++);
  405: 	}
  406: 	addrstr[i] = '\0';
  407: 	if(ipv6) {
  408: 		struct in6_addr addr;
  409: 		if(inet_pton(AF_INET6, addrstr, &addr) <= 0)
  410: 			return 0;
  411: #ifdef ENABLE_IPV6
  412: 		if(!h->ipv6
  413: 		  || (0!=memcmp(&addr, &(h->clientaddr_v6), sizeof(struct in6_addr))))
  414: 			return 0;
  415: #else
  416: 		return 0;
  417: #endif
  418: 	} else {
  419: 		struct in_addr addr;
  420: 		if(inet_pton(AF_INET, addrstr, &addr) <= 0)
  421: 			return 0;
  422: #ifdef ENABLE_IPV6
  423: 		if(h->ipv6) {
  424: 			if(!IN6_IS_ADDR_V4MAPPED(&(h->clientaddr_v6)))
  425: 				return 0;
  426: 			if(0!=memcmp(&addr, ((const char *)&(h->clientaddr_v6) + 12), 4))
  427: 				return 0;
  428: 		} else {
  429: 			if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
  430: 				return 0;
  431: 		}
  432: #else
  433: 		if(0!=memcmp(&addr, &(h->clientaddr), sizeof(struct in_addr)))
  434: 			return 0;
  435: #endif
  436: 	}
  437: 	return 1;
  438: }
  439: 
  440: static void
  441: ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path)
  442: {
  443: 	const char * sid;
  444: 	syslog(LOG_DEBUG, "ProcessHTTPSubscribe %s", path);
  445: 	syslog(LOG_DEBUG, "Callback '%.*s' Timeout=%d",
  446: 	       h->req_CallbackLen, h->req_buf + h->req_CallbackOff,
  447: 	       h->req_Timeout);
  448: 	syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
  449: 	if((h->req_CallbackOff <= 0) && (h->req_SIDOff <= 0)) {
  450: 		/* Missing or invalid CALLBACK : 412 Precondition Failed.
  451: 		 * If CALLBACK header is missing or does not contain a valid HTTP URL,
  452: 		 * the publisher must respond with HTTP error 412 Precondition Failed*/
  453: 		BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
  454: 		SendRespAndClose_upnphttp(h);
  455: 	} else {
  456: 	/* - add to the subscriber list
  457: 	 * - respond HTTP/x.x 200 OK
  458: 	 * - Send the initial event message */
  459: /* Server:, SID:; Timeout: Second-(xx|infinite) */
  460: 	/* Check that the callback URL is on the same IP as
  461: 	 * the request, and not on the internet, nor on ourself (DOS attack ?) */
  462: 		if(h->req_CallbackOff > 0) {
  463: #ifdef UPNP_STRICT
  464: 			/* SID: and Callback: are incompatible */
  465: 			if(h->req_SIDOff > 0) {
  466: 				syslog(LOG_WARNING, "Both Callback: and SID: in SUBSCRIBE");
  467: 				BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
  468: 			/* "NT: upnp:event" header is mandatory */
  469: 			} else if(h->req_NTOff <= 0 || h->req_NTLen != 10 ||
  470: 			   0 != memcmp("upnp:event", h->req_buf + h->req_NTOff, 10)) {
  471: 				syslog(LOG_WARNING, "Invalid NT in SUBSCRIBE %.*s",
  472: 				       h->req_NTLen, h->req_buf + h->req_NTOff);
  473: 				BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
  474: 			} else
  475: #endif
  476: 			if(checkCallbackURL(h)) {
  477: 				sid = upnpevents_addSubscriber(path, h->req_buf + h->req_CallbackOff,
  478: 				                               h->req_CallbackLen, h->req_Timeout);
  479: 				h->respflags = FLAG_TIMEOUT;
  480: 				if(sid) {
  481: 					syslog(LOG_DEBUG, "generated sid=%s", sid);
  482: 					h->respflags |= FLAG_SID;
  483: 					h->res_SID = sid;
  484: 				}
  485: 				BuildResp_upnphttp(h, 0, 0);
  486: 			} else {
  487: 				syslog(LOG_WARNING, "Invalid Callback in SUBSCRIBE %.*s",
  488: 				       h->req_CallbackLen, h->req_buf + h->req_CallbackOff);
  489: 				BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
  490: 			}
  491: 		} else {
  492: 			/* subscription renew */
  493: 			/* Invalid SID
  494: 412 Precondition Failed. If a SID does not correspond to a known,
  495: un-expired subscription, the publisher must respond
  496: with HTTP error 412 Precondition Failed. */
  497: #ifdef UPNP_STRICT
  498: 			/* SID: and NT: headers are incompatibles */
  499: 			if(h->req_NTOff > 0) {
  500: 				syslog(LOG_WARNING, "Both NT: and SID: in SUBSCRIBE");
  501: 				BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
  502: 			} else
  503: #endif
  504: 			if(renewSubscription(h->req_buf + h->req_SIDOff, h->req_SIDLen,
  505: 			                     h->req_Timeout) < 0) {
  506: 				BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
  507: 			} else {
  508: 				h->respflags = FLAG_TIMEOUT;
  509: 				BuildResp_upnphttp(h, 0, 0);
  510: 			}
  511: 		}
  512: 		SendRespAndClose_upnphttp(h);
  513: 	}
  514: }
  515: 
  516: static void
  517: ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
  518: {
  519: 	syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path);
  520: 	syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
  521: 	/* Remove from the list */
  522: #ifdef UPNP_STRICT
  523: 	if(h->req_SIDOff <= 0 || h->req_SIDLen == 0) {
  524: 		/* SID: header missing or empty */
  525: 		BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
  526: 	} else if(h->req_CallbackOff > 0 || h->req_NTOff > 0) {
  527: 		/* no NT: or Callback: header must be present */
  528: 		BuildResp2_upnphttp(h, 400, "Incompatible header fields", 0, 0);
  529: 	} else
  530: #endif
  531: 	if(upnpevents_removeSubscriber(h->req_buf + h->req_SIDOff, h->req_SIDLen) < 0) {
  532: 		BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
  533: 	} else {
  534: 		BuildResp_upnphttp(h, 0, 0);
  535: 	}
  536: 	SendRespAndClose_upnphttp(h);
  537: }
  538: #endif
  539: 
  540: /* Parse and process Http Query
  541:  * called once all the HTTP headers have been received,
  542:  * so it is guaranteed that h->req_buf contains the \r\n\r\n
  543:  * character sequence */
  544: static void
  545: ProcessHttpQuery_upnphttp(struct upnphttp * h)
  546: {
  547: 	static const struct {
  548: 		const char * path;
  549: 		char * (* f)(int *);
  550: 	} path_desc[] = {
  551: 		{ ROOTDESC_PATH, genRootDesc},
  552: 		{ WANIPC_PATH, genWANIPCn},
  553: 		{ WANCFG_PATH, genWANCfg},
  554: #ifdef HAS_DUMMY_SERVICE
  555: 		{ DUMMY_PATH, NULL},
  556: #endif
  557: #ifdef ENABLE_L3F_SERVICE
  558: 		{ L3F_PATH, genL3F},
  559: #endif
  560: #ifdef ENABLE_6FC_SERVICE
  561: 		{ WANIP6FC_PATH, gen6FC},
  562: #endif
  563: #ifdef ENABLE_DP_SERVICE
  564: 		{ DP_PATH, genDP},
  565: #endif
  566: 		{ NULL, NULL}
  567: 	};
  568: 	char HttpCommand[16];
  569: 	char HttpUrl[128];
  570: 	char * HttpVer;
  571: 	char * p;
  572: 	int i;
  573: 	p = h->req_buf;
  574: 	if(!p)
  575: 		return;
  576: 	/* note : checking (*p != '\r') is enough to avoid runing off the
  577: 	 * end of the buffer, because h->req_buf is guaranteed to contain
  578: 	 * the \r\n\r\n character sequence */
  579: 	for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++)
  580: 		HttpCommand[i] = *(p++);
  581: 	HttpCommand[i] = '\0';
  582: 	while(*p==' ')
  583: 		p++;
  584: 	for(i = 0; i<127 && *p != ' ' && *p != '\r'; i++)
  585: 		HttpUrl[i] = *(p++);
  586: 	HttpUrl[i] = '\0';
  587: 	while(*p==' ')
  588: 		p++;
  589: 	HttpVer = h->HttpVer;
  590: 	for(i = 0; i<15 && *p != '\r'; i++)
  591: 		HttpVer[i] = *(p++);
  592: 	HttpVer[i] = '\0';
  593: 	syslog(LOG_INFO, "HTTP REQUEST : %s %s (%s)",
  594: 	       HttpCommand, HttpUrl, HttpVer);
  595: 	ParseHttpHeaders(h);
  596: 	if(strcmp("POST", HttpCommand) == 0)
  597: 	{
  598: 		h->req_command = EPost;
  599: 		ProcessHTTPPOST_upnphttp(h);
  600: 	}
  601: 	else if(strcmp("GET", HttpCommand) == 0)
  602: 	{
  603: 		h->req_command = EGet;
  604: 		for(i=0; path_desc[i].path; i++) {
  605: 			if(strcasecmp(path_desc[i].path, HttpUrl) == 0) {
  606: 				if(path_desc[i].f)
  607: 					sendXMLdesc(h, path_desc[i].f);
  608: 				else
  609: #ifdef HAS_DUMMY_SERVICE
  610: 					sendDummyDesc(h);
  611: #else
  612: 					continue;
  613: #endif
  614: 				return;
  615: 			}
  616: 		}
  617: 		if(0 == memcmp(HttpUrl, "/ctl/", 5)) {
  618: 			/* 405 Method Not Allowed
  619: 			 * Allow: POST */
  620: 			h->respflags = FLAG_ALLOW_POST;
  621: 			Send405(h);
  622: 			return;
  623: 		}
  624: #ifdef ENABLE_EVENTS
  625: 		if(0 == memcmp(HttpUrl, "/evt/", 5)) {
  626: 			/* 405 Method Not Allowed
  627: 			 * Allow: SUBSCRIBE, UNSUBSCRIBE */
  628: 			h->respflags = FLAG_ALLOW_SUB_UNSUB;
  629: 			Send405(h);
  630: 			return;
  631: 		}
  632: #endif
  633: 		syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
  634: 		Send404(h);
  635: 	}
  636: #ifdef ENABLE_EVENTS
  637: 	else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
  638: 	{
  639: 		h->req_command = ESubscribe;
  640: 		ProcessHTTPSubscribe_upnphttp(h, HttpUrl);
  641: 	}
  642: 	else if(strcmp("UNSUBSCRIBE", HttpCommand) == 0)
  643: 	{
  644: 		h->req_command = EUnSubscribe;
  645: 		ProcessHTTPUnSubscribe_upnphttp(h, HttpUrl);
  646: 	}
  647: #else
  648: 	else if(strcmp("SUBSCRIBE", HttpCommand) == 0)
  649: 	{
  650: 		syslog(LOG_NOTICE, "SUBSCRIBE not implemented. ENABLE_EVENTS compile option disabled");
  651: 		Send501(h);
  652: 	}
  653: #endif
  654: 	else
  655: 	{
  656: 		syslog(LOG_NOTICE, "Unsupported HTTP Command %s", HttpCommand);
  657: 		Send501(h);
  658: 	}
  659: }
  660: 
  661: 
  662: void
  663: Process_upnphttp(struct upnphttp * h)
  664: {
  665: 	char * h_tmp;
  666: 	char buf[2048];
  667: 	int n;
  668: 
  669: 	if(!h)
  670: 		return;
  671: 	switch(h->state)
  672: 	{
  673: 	case EWaitingForHttpRequest:
  674: 		n = recv(h->socket, buf, sizeof(buf), 0);
  675: 		if(n<0)
  676: 		{
  677: 			if(errno != EAGAIN &&
  678: 			   errno != EWOULDBLOCK &&
  679: 			   errno != EINTR)
  680: 			{
  681: 				syslog(LOG_ERR, "recv (state0): %m");
  682: 				h->state = EToDelete;
  683: 			}
  684: 			/* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
  685: 		}
  686: 		else if(n==0)
  687: 		{
  688: 			syslog(LOG_WARNING, "HTTP Connection closed unexpectedly");
  689: 			h->state = EToDelete;
  690: 		}
  691: 		else
  692: 		{
  693: 			const char * endheaders;
  694: 			/* if 1st arg of realloc() is null,
  695: 			 * realloc behaves the same as malloc() */
  696: 			h_tmp = (char *)realloc(h->req_buf, n + h->req_buflen + 1);
  697: 			if (h_tmp == NULL)
  698: 			{
  699: 				syslog(LOG_WARNING, "Unable to allocate new memory for h->req_buf)");
  700: 				h->state = EToDelete;
  701: 			}
  702: 			else
  703: 			{
  704: 				h->req_buf = h_tmp;
  705: 				memcpy(h->req_buf + h->req_buflen, buf, n);
  706: 				h->req_buflen += n;
  707: 				h->req_buf[h->req_buflen] = '\0';
  708: 			}
  709: 			/* search for the string "\r\n\r\n" */
  710: 			endheaders = findendheaders(h->req_buf, h->req_buflen);
  711: 			if(endheaders)
  712: 			{
  713: 				/* at this point, the request buffer (h->req_buf)
  714: 				 * is guaranteed to contain the \r\n\r\n character sequence */
  715: 				h->req_contentoff = endheaders - h->req_buf + 4;
  716: 				ProcessHttpQuery_upnphttp(h);
  717: 			}
  718: 		}
  719: 		break;
  720: 	case EWaitingForHttpContent:
  721: 		n = recv(h->socket, buf, sizeof(buf), 0);
  722: 		if(n<0)
  723: 		{
  724: 			if(errno != EAGAIN &&
  725: 			   errno != EWOULDBLOCK &&
  726: 			   errno != EINTR)
  727: 			{
  728: 				syslog(LOG_ERR, "recv (state1): %m");
  729: 				h->state = EToDelete;
  730: 			}
  731: 			/* if errno is EAGAIN, EWOULDBLOCK or EINTR, try again later */
  732: 		}
  733: 		else if(n==0)
  734: 		{
  735: 			syslog(LOG_WARNING, "HTTP Connection closed inexpectedly");
  736: 			h->state = EToDelete;
  737: 		}
  738: 		else
  739: 		{
  740: 			void * tmp = realloc(h->req_buf, n + h->req_buflen);
  741: 			if(!tmp)
  742: 			{
  743: 				syslog(LOG_ERR, "memory allocation error %m");
  744: 				h->state = EToDelete;
  745: 			}
  746: 			else
  747: 			{
  748: 				h->req_buf = tmp;
  749: 				memcpy(h->req_buf + h->req_buflen, buf, n);
  750: 				h->req_buflen += n;
  751: 				if((h->req_buflen - h->req_contentoff) >= h->req_contentlen)
  752: 				{
  753: 					ProcessHTTPPOST_upnphttp(h);
  754: 				}
  755: 			}
  756: 		}
  757: 		break;
  758: 	case ESendingContinue:
  759: 		if(SendResp_upnphttp(h))
  760: 			h->state = EWaitingForHttpContent;
  761: 		break;
  762: 	case ESendingAndClosing:
  763: 		SendRespAndClose_upnphttp(h);
  764: 		break;
  765: 	default:
  766: 		syslog(LOG_WARNING, "Unexpected state: %d", h->state);
  767: 	}
  768: }
  769: 
  770: static const char httpresphead[] =
  771: 	"%s %d %s\r\n"
  772: 	"Content-Type: %s\r\n"
  773: 	"Connection: close\r\n"
  774: 	"Content-Length: %d\r\n"
  775: 	"Server: " MINIUPNPD_SERVER_STRING "\r\n"
  776: 	;	/*"\r\n";*/
  777: /*
  778: 		"<?xml version=\"1.0\"?>\n"
  779: 		"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
  780: 		"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
  781: 		"<s:Body>"
  782: 
  783: 		"</s:Body>"
  784: 		"</s:Envelope>";
  785: */
  786: /* with response code and response message
  787:  * also allocate enough memory */
  788: 
  789: void
  790: BuildHeader_upnphttp(struct upnphttp * h, int respcode,
  791:                      const char * respmsg,
  792:                      int bodylen)
  793: {
  794: 	int templen;
  795: 	if(!h->res_buf ||
  796: 	   h->res_buf_alloclen < ((int)sizeof(httpresphead) + 256 + bodylen)) {
  797: 		if(h->res_buf)
  798: 			free(h->res_buf);
  799: 		templen = sizeof(httpresphead) + 256 + bodylen;
  800: 		h->res_buf = (char *)malloc(templen);
  801: 		if(!h->res_buf) {
  802: 			syslog(LOG_ERR, "malloc error in BuildHeader_upnphttp()");
  803: 			return;
  804: 		}
  805: 		h->res_buf_alloclen = templen;
  806: 	}
  807: 	h->res_sent = 0;
  808: 	h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
  809: 	                         httpresphead, h->HttpVer,
  810: 	                         respcode, respmsg,
  811: 	                         (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
  812: 							 bodylen);
  813: 	/* Content-Type MUST be 'text/xml; charset="utf-8"' according to UDA v1.1 */
  814: 	/* Content-Type MUST be 'text/xml' according to UDA v1.0 */
  815: 	/* Additional headers */
  816: #ifdef ENABLE_HTTP_DATE
  817: 	{
  818: 		char http_date[64];
  819: 		time_t t;
  820: 		struct tm tm;
  821: 		time(&t);
  822: 		gmtime_r(&t, &tm);
  823: 		/* %a and %b depend on locale */
  824: 		strftime(http_date, sizeof(http_date),
  825: 		         "%a, %d %b %Y %H:%M:%S GMT", &tm);
  826: 		h->res_buflen += snprintf(h->res_buf + h->res_buflen,
  827: 		                          h->res_buf_alloclen - h->res_buflen,
  828: 		                          "Date: %s\r\n", http_date);
  829: 	}
  830: #endif
  831: #ifdef ENABLE_EVENTS
  832: 	if(h->respflags & FLAG_TIMEOUT) {
  833: 		h->res_buflen += snprintf(h->res_buf + h->res_buflen,
  834: 		                          h->res_buf_alloclen - h->res_buflen,
  835: 		                          "Timeout: Second-");
  836: 		if(h->req_Timeout) {
  837: 			h->res_buflen += snprintf(h->res_buf + h->res_buflen,
  838: 			                          h->res_buf_alloclen - h->res_buflen,
  839: 			                          "%d\r\n", h->req_Timeout);
  840: 		} else {
  841: 			h->res_buflen += snprintf(h->res_buf + h->res_buflen,
  842: 			                          h->res_buf_alloclen - h->res_buflen,
  843: 			                          "infinite\r\n");
  844: 		}
  845: 	}
  846: 	if(h->respflags & FLAG_SID) {
  847: 		h->res_buflen += snprintf(h->res_buf + h->res_buflen,
  848: 		                          h->res_buf_alloclen - h->res_buflen,
  849: 		                          "SID: %s\r\n", h->res_SID);
  850: 	}
  851: #endif
  852: 	if(h->respflags & FLAG_ALLOW_POST) {
  853: 		h->res_buflen += snprintf(h->res_buf + h->res_buflen,
  854: 		                          h->res_buf_alloclen - h->res_buflen,
  855: 		                          "Allow: %s\r\n", "POST");
  856: 	} else if(h->respflags & FLAG_ALLOW_SUB_UNSUB) {
  857: 		h->res_buflen += snprintf(h->res_buf + h->res_buflen,
  858: 		                          h->res_buf_alloclen - h->res_buflen,
  859: 		                          "Allow: %s\r\n", "SUBSCRIBE, UNSUBSCRIBE");
  860: 	}
  861: 	if(h->accept_language[0] != '\0') {
  862: 		/* defaulting to "en" */
  863: 		h->res_buflen += snprintf(h->res_buf + h->res_buflen,
  864: 		                          h->res_buf_alloclen - h->res_buflen,
  865: 		                          "Content-Language: %s\r\n",
  866: 		                          h->accept_language[0] == '*' ? "en" : h->accept_language);
  867: 	}
  868: 	h->res_buf[h->res_buflen++] = '\r';
  869: 	h->res_buf[h->res_buflen++] = '\n';
  870: 	if(h->res_buf_alloclen < (h->res_buflen + bodylen))
  871: 	{
  872: 		char * tmp;
  873: 		tmp = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
  874: 		if(tmp)
  875: 		{
  876: 			h->res_buf = tmp;
  877: 			h->res_buf_alloclen = h->res_buflen + bodylen;
  878: 		}
  879: 		else
  880: 		{
  881: 			syslog(LOG_ERR, "realloc error in BuildHeader_upnphttp()");
  882: 		}
  883: 	}
  884: }
  885: 
  886: void
  887: BuildResp2_upnphttp(struct upnphttp * h, int respcode,
  888:                     const char * respmsg,
  889:                     const char * body, int bodylen)
  890: {
  891: 	BuildHeader_upnphttp(h, respcode, respmsg, bodylen);
  892: 	if(body)
  893: 		memcpy(h->res_buf + h->res_buflen, body, bodylen);
  894: 	h->res_buflen += bodylen;
  895: }
  896: 
  897: /* responding 200 OK ! */
  898: void
  899: BuildResp_upnphttp(struct upnphttp * h,
  900:                         const char * body, int bodylen)
  901: {
  902: 	BuildResp2_upnphttp(h, 200, "OK", body, bodylen);
  903: }
  904: 
  905: int
  906: SendResp_upnphttp(struct upnphttp * h)
  907: {
  908: 	ssize_t n;
  909: 
  910: 	while (h->res_sent < h->res_buflen)
  911: 	{
  912: 		n = send(h->socket, h->res_buf + h->res_sent,
  913: 		         h->res_buflen - h->res_sent, 0);
  914: 		if(n<0)
  915: 		{
  916: 			if(errno == EINTR)
  917: 				continue;	/* try again immediatly */
  918: 			if(errno == EAGAIN || errno == EWOULDBLOCK)
  919: 			{
  920: 				/* try again later */
  921: 				return 0;
  922: 			}
  923: 			syslog(LOG_ERR, "send(res_buf): %m");
  924: 			break; /* avoid infinite loop */
  925: 		}
  926: 		else if(n == 0)
  927: 		{
  928: 			syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
  929: 							h->res_sent, h->res_buflen);
  930: 			break;
  931: 		}
  932: 		else
  933: 		{
  934: 			h->res_sent += n;
  935: 		}
  936: 	}
  937: 	return 1;	/* finished */
  938: }
  939: 
  940: void
  941: SendRespAndClose_upnphttp(struct upnphttp * h)
  942: {
  943: 	ssize_t n;
  944: 
  945: 	while (h->res_sent < h->res_buflen)
  946: 	{
  947: 		n = send(h->socket, h->res_buf + h->res_sent,
  948: 		         h->res_buflen - h->res_sent, 0);
  949: 		if(n<0)
  950: 		{
  951: 			if(errno == EINTR)
  952: 				continue;	/* try again immediatly */
  953: 			if(errno == EAGAIN || errno == EWOULDBLOCK)
  954: 			{
  955: 				/* try again later */
  956: 				h->state = ESendingAndClosing;
  957: 				return;
  958: 			}
  959: 			syslog(LOG_ERR, "send(res_buf): %m");
  960: 			break; /* avoid infinite loop */
  961: 		}
  962: 		else if(n == 0)
  963: 		{
  964: 			syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
  965: 							h->res_sent, h->res_buflen);
  966: 			break;
  967: 		}
  968: 		else
  969: 		{
  970: 			h->res_sent += n;
  971: 		}
  972: 	}
  973: 	CloseSocket_upnphttp(h);
  974: }
  975: 

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