Annotation of embedaddon/miniupnpd/upnphttp.c, revision 1.1.1.3

1.1.1.3 ! misho       1: /* $Id: upnphttp.c,v 1.85 2013/01/29 21:52:43 nanard Exp $ */
1.1       misho       2: /* Project :  miniupnp
                      3:  * Website :  http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
                      4:  * Author :   Thomas Bernard
1.1.1.3 ! misho       5:  * Copyright (c) 2005-2012 Thomas Bernard
1.1       misho       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>
1.1.1.3 ! misho      16: #include <netinet/in.h>
1.1.1.2   misho      17: #include <arpa/inet.h>
1.1       misho      18: #include <syslog.h>
                     19: #include <ctype.h>
1.1.1.3 ! misho      20: #include <errno.h>
1.1       misho      21: #include "config.h"
1.1.1.3 ! misho      22: #ifdef ENABLE_HTTP_DATE
        !            23: #include <time.h>
        !            24: #endif
1.1       misho      25: #include "upnphttp.h"
                     26: #include "upnpdescgen.h"
                     27: #include "miniupnpdpath.h"
                     28: #include "upnpsoap.h"
                     29: #include "upnpevents.h"
1.1.1.3 ! misho      30: #include "upnputils.h"
1.1       misho      31: 
1.1.1.3 ! misho      32: struct upnphttp *
1.1       misho      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;
1.1.1.3 ! misho      43:        if(!set_non_blocking(s))
        !            44:                syslog(LOG_WARNING, "New_upnphttp::set_non_blocking(): %m");
1.1       misho      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;
1.1.1.3 ! misho      56:        h->state = EToDelete;
1.1       misho      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: 
1.1.1.3 ! misho      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 */
1.1       misho      77: static void
                     78: ParseHttpHeaders(struct upnphttp * h)
                     79: {
                     80:        char * line;
                     81:        char * colon;
                     82:        char * p;
                     83:        int n;
1.1.1.3 ! misho      84:        if((h->req_buf == NULL) || (h->req_contentoff <= 0))
        !            85:                return;
1.1       misho      86:        line = h->req_buf;
                     87:        while(line < (h->req_buf + h->req_contentoff))
                     88:        {
1.1.1.3 ! misho      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:                }
1.1       misho      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);
1.1.1.3 ! misho     107:                                if(h->req_contentlen < 0) {
        !           108:                                        syslog(LOG_WARNING, "ParseHttpHeaders() invalid Content-Length %d", h->req_contentlen);
        !           109:                                        h->req_contentlen = 0;
        !           110:                                }
1.1       misho     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:                                }
1.1.1.3 ! misho     128:                                h->req_soapActionOff = p - h->req_buf;
1.1       misho     129:                                h->req_soapActionLen = n;
                    130:                        }
1.1.1.3 ! misho     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:                        }
1.1       misho     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++;
1.1.1.3 ! misho     171:                                h->req_CallbackOff = p + 1 - h->req_buf;
1.1       misho     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++;
1.1.1.3 ! misho     182:                                h->req_SIDOff = p - h->req_buf;
1.1       misho     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:                        }
1.1.1.3 ! misho     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
1.1       misho     214: #endif
                    215:                }
1.1.1.3 ! misho     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 */
1.1       misho     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";
1.1.1.3 ! misho     233: 
1.1       misho     234:        h->respflags = FLAG_HTML;
                    235:        BuildResp2_upnphttp(h, 404, "Not Found",
                    236:                            body404, sizeof(body404) - 1);
1.1.1.3 ! misho     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);
1.1       misho     252: }
                    253: 
                    254: /* very minimalistic 501 error message */
                    255: static void
                    256: Send501(struct upnphttp * h)
                    257: {
1.1.1.3 ! misho     258:        static const char body501[] =
1.1       misho     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";
1.1.1.3 ! misho     262: 
1.1       misho     263:        h->respflags = FLAG_HTML;
                    264:        BuildResp2_upnphttp(h, 501, "Not Implemented",
                    265:                            body501, sizeof(body501) - 1);
1.1.1.3 ! misho     266:        SendRespAndClose_upnphttp(h);
1.1       misho     267: }
                    268: 
1.1.1.3 ! misho     269: /* findendheaders() find the \r\n\r\n character sequence and
        !           270:  * return a pointer to it.
        !           271:  * It returns NULL if not found */
1.1       misho     272: static const char *
                    273: findendheaders(const char * s, int len)
                    274: {
1.1.1.3 ! misho     275:        while(len-->3)
1.1       misho     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);
1.1.1.3 ! misho     298:        SendRespAndClose_upnphttp(h);
1.1       misho     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:        }
1.1.1.3 ! misho     322:        SendRespAndClose_upnphttp(h);
1.1       misho     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:        {
1.1.1.3 ! misho     333:                /* the request body is received */
        !           334:                if(h->req_soapActionOff > 0)
1.1       misho     335:                {
                    336:                        /* we can process the request */
                    337:                        syslog(LOG_INFO, "SOAPAction: %.*s",
1.1.1.3 ! misho     338:                               h->req_soapActionLen, h->req_buf + h->req_soapActionOff);
        !           339:                        ExecuteSoapAction(h,
        !           340:                                h->req_buf + h->req_soapActionOff,
1.1       misho     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);
1.1.1.3 ! misho     351:                        SendRespAndClose_upnphttp(h);
1.1       misho     352:                }
                    353:        }
1.1.1.3 ! misho     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:        }
1.1       misho     368:        else
                    369:        {
                    370:                /* waiting for remaining data */
1.1.1.3 ! misho     371:                h->state = EWaitingForHttpContent;
1.1       misho     372:        }
                    373: }
                    374: 
                    375: #ifdef ENABLE_EVENTS
1.1.1.2   misho     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;
1.1.1.3 ! misho     386:        unsigned int i;
1.1.1.2   misho     387: 
1.1.1.3 ! misho     388:        if(h->req_CallbackOff <= 0 || h->req_CallbackLen < 8)
1.1.1.2   misho     389:                return 0;
1.1.1.3 ! misho     390:        if(memcmp(h->req_buf + h->req_CallbackOff, "http://", 7) != 0)
1.1.1.2   misho     391:                return 0;
                    392:        ipv6 = 0;
                    393:        i = 0;
1.1.1.3 ! misho     394:        p = h->req_buf + h->req_CallbackOff + 7;
1.1.1.2   misho     395:        if(*p == '[') {
                    396:                p++;
                    397:                ipv6 = 1;
                    398:                while(*p != ']' && i < (sizeof(addrstr)-1)
1.1.1.3 ! misho     399:                      && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
1.1.1.2   misho     400:                        addrstr[i++] = *(p++);
                    401:        } else {
                    402:                while(*p != '/' && *p != ':' && i < (sizeof(addrstr)-1)
1.1.1.3 ! misho     403:                      && p < (h->req_buf + h->req_CallbackOff + h->req_CallbackLen))
1.1.1.2   misho     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: 
1.1       misho     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",
1.1.1.3 ! misho     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)) {
1.1       misho     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);
1.1.1.3 ! misho     454:                SendRespAndClose_upnphttp(h);
1.1       misho     455:        } else {
                    456:        /* - add to the subscriber list
1.1.1.3 ! misho     457:         * - respond HTTP/x.x 200 OK
1.1       misho     458:         * - Send the initial event message */
                    459: /* Server:, SID:; Timeout: Second-(xx|infinite) */
1.1.1.2   misho     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 ?) */
1.1.1.3 ! misho     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
1.1.1.2   misho     476:                        if(checkCallbackURL(h)) {
1.1.1.3 ! misho     477:                                sid = upnpevents_addSubscriber(path, h->req_buf + h->req_CallbackOff,
1.1.1.2   misho     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;
1.1.1.3 ! misho     483:                                        h->res_SID = sid;
1.1.1.2   misho     484:                                }
                    485:                                BuildResp_upnphttp(h, 0, 0);
                    486:                        } else {
                    487:                                syslog(LOG_WARNING, "Invalid Callback in SUBSCRIBE %.*s",
1.1.1.3 ! misho     488:                                       h->req_CallbackLen, h->req_buf + h->req_CallbackOff);
1.1.1.2   misho     489:                                BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
1.1       misho     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. */
1.1.1.3 ! misho     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) {
1.1       misho     506:                                BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
                    507:                        } else {
1.1.1.2   misho     508:                                h->respflags = FLAG_TIMEOUT;
1.1       misho     509:                                BuildResp_upnphttp(h, 0, 0);
                    510:                        }
                    511:                }
1.1.1.3 ! misho     512:                SendRespAndClose_upnphttp(h);
1.1       misho     513:        }
                    514: }
                    515: 
                    516: static void
                    517: ProcessHTTPUnSubscribe_upnphttp(struct upnphttp * h, const char * path)
                    518: {
                    519:        syslog(LOG_DEBUG, "ProcessHTTPUnSubscribe %s", path);
1.1.1.3 ! misho     520:        syslog(LOG_DEBUG, "SID '%.*s'", h->req_SIDLen, h->req_buf + h->req_SIDOff);
1.1       misho     521:        /* Remove from the list */
1.1.1.3 ! misho     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) {
1.1       misho     532:                BuildResp2_upnphttp(h, 412, "Precondition Failed", 0, 0);
                    533:        } else {
                    534:                BuildResp_upnphttp(h, 0, 0);
                    535:        }
1.1.1.3 ! misho     536:        SendRespAndClose_upnphttp(h);
1.1       misho     537: }
                    538: #endif
                    539: 
1.1.1.3 ! misho     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 */
1.1       misho     544: static void
                    545: ProcessHttpQuery_upnphttp(struct upnphttp * h)
                    546: {
1.1.1.3 ! misho     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:        };
1.1       misho     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;
1.1.1.3 ! misho     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 */
1.1       misho     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;
1.1.1.3 ! misho     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
1.1       misho     609: #ifdef HAS_DUMMY_SERVICE
1.1.1.3 ! misho     610:                                        sendDummyDesc(h);
        !           611: #else
        !           612:                                        continue;
1.1       misho     613: #endif
1.1.1.3 ! misho     614:                                return;
        !           615:                        }
1.1       misho     616:                }
1.1.1.3 ! misho     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;
1.1.1.2   misho     623:                }
1.1.1.3 ! misho     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;
1.1.1.2   misho     631:                }
                    632: #endif
1.1.1.3 ! misho     633:                syslog(LOG_NOTICE, "%s not found, responding ERROR 404", HttpUrl);
        !           634:                Send404(h);
1.1       misho     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: {
1.1.1.3 ! misho     665:        char * h_tmp;
1.1       misho     666:        char buf[2048];
                    667:        int n;
1.1.1.3 ! misho     668: 
1.1       misho     669:        if(!h)
                    670:                return;
                    671:        switch(h->state)
                    672:        {
1.1.1.3 ! misho     673:        case EWaitingForHttpRequest:
        !           674:                n = recv(h->socket, buf, sizeof(buf), 0);
1.1       misho     675:                if(n<0)
                    676:                {
1.1.1.3 ! misho     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 */
1.1       misho     685:                }
                    686:                else if(n==0)
                    687:                {
1.1.1.3 ! misho     688:                        syslog(LOG_WARNING, "HTTP Connection closed unexpectedly");
        !           689:                        h->state = EToDelete;
1.1       misho     690:                }
                    691:                else
                    692:                {
                    693:                        const char * endheaders;
                    694:                        /* if 1st arg of realloc() is null,
                    695:                         * realloc behaves the same as malloc() */
1.1.1.3 ! misho     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:                        }
1.1       misho     709:                        /* search for the string "\r\n\r\n" */
                    710:                        endheaders = findendheaders(h->req_buf, h->req_buflen);
                    711:                        if(endheaders)
                    712:                        {
1.1.1.3 ! misho     713:                                /* at this point, the request buffer (h->req_buf)
        !           714:                                 * is guaranteed to contain the \r\n\r\n character sequence */
1.1       misho     715:                                h->req_contentoff = endheaders - h->req_buf + 4;
                    716:                                ProcessHttpQuery_upnphttp(h);
                    717:                        }
                    718:                }
                    719:                break;
1.1.1.3 ! misho     720:        case EWaitingForHttpContent:
        !           721:                n = recv(h->socket, buf, sizeof(buf), 0);
1.1       misho     722:                if(n<0)
                    723:                {
1.1.1.3 ! misho     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 */
1.1       misho     732:                }
                    733:                else if(n==0)
                    734:                {
                    735:                        syslog(LOG_WARNING, "HTTP Connection closed inexpectedly");
1.1.1.3 ! misho     736:                        h->state = EToDelete;
1.1       misho     737:                }
                    738:                else
                    739:                {
1.1.1.3 ! misho     740:                        void * tmp = realloc(h->req_buf, n + h->req_buflen);
        !           741:                        if(!tmp)
1.1       misho     742:                        {
1.1.1.3 ! misho     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:                                }
1.1       misho     755:                        }
                    756:                }
                    757:                break;
1.1.1.3 ! misho     758:        case ESendingContinue:
        !           759:                if(SendResp_upnphttp(h))
        !           760:                        h->state = EWaitingForHttpContent;
        !           761:                break;
        !           762:        case ESendingAndClosing:
        !           763:                SendRespAndClose_upnphttp(h);
        !           764:                break;
1.1       misho     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;
1.1.1.3 ! misho     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;
1.1       misho     800:                h->res_buf = (char *)malloc(templen);
1.1.1.3 ! misho     801:                if(!h->res_buf) {
1.1.1.2   misho     802:                        syslog(LOG_ERR, "malloc error in BuildHeader_upnphttp()");
                    803:                        return;
                    804:                }
1.1       misho     805:                h->res_buf_alloclen = templen;
                    806:        }
1.1.1.3 ! misho     807:        h->res_sent = 0;
1.1       misho     808:        h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen,
                    809:                                 httpresphead, h->HttpVer,
                    810:                                 respcode, respmsg,
1.1.1.3 ! misho     811:                                 (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
1.1       misho     812:                                                         bodylen);
1.1.1.3 ! misho     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 */
1.1       misho     815:        /* Additional headers */
1.1.1.3 ! misho     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
1.1       misho     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,
1.1.1.3 ! misho     849:                                          "SID: %s\r\n", h->res_SID);
1.1       misho     850:        }
                    851: #endif
1.1.1.3 ! misho     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:        }
1.1       misho     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:        {
1.1.1.2   misho     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:                }
1.1       misho     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: 
1.1.1.3 ! misho     905: int
1.1       misho     906: SendResp_upnphttp(struct upnphttp * h)
                    907: {
1.1.1.2   misho     908:        ssize_t n;
1.1.1.3 ! misho     909: 
        !           910:        while (h->res_sent < h->res_buflen)
1.1       misho     911:        {
1.1.1.3 ! misho     912:                n = send(h->socket, h->res_buf + h->res_sent,
        !           913:                         h->res_buflen - h->res_sent, 0);
1.1.1.2   misho     914:                if(n<0)
                    915:                {
1.1.1.3 ! misho     916:                        if(errno == EINTR)
        !           917:                                continue;       /* try again immediatly */
        !           918:                        if(errno == EAGAIN || errno == EWOULDBLOCK)
        !           919:                        {
        !           920:                                /* try again later */
        !           921:                                return 0;
        !           922:                        }
1.1.1.2   misho     923:                        syslog(LOG_ERR, "send(res_buf): %m");
1.1.1.3 ! misho     924:                        break; /* avoid infinite loop */
1.1.1.2   misho     925:                }
                    926:                else if(n == 0)
                    927:                {
1.1.1.3 ! misho     928:                        syslog(LOG_ERR, "send(res_buf): %d bytes sent (out of %d)",
        !           929:                                                        h->res_sent, h->res_buflen);
1.1.1.2   misho     930:                        break;
                    931:                }
                    932:                else
                    933:                {
1.1.1.3 ! misho     934:                        h->res_sent += n;
1.1.1.2   misho     935:                }
1.1       misho     936:        }
1.1.1.3 ! misho     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);
1.1       misho     974: }
                    975: 

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