src/server/daemon/httpparser.c

Wed, 30 Aug 2017 21:53:08 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 30 Aug 2017 21:53:08 +0200
changeset 180
98462e878ca7
parent 163
3589ed579127
child 182
b982cc285c40
permissions
-rw-r--r--

fixes crash with broken http requests

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2013 Olaf Wintermann. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>

#include "httpparser.h"
#include "../public/nsapi.h"
//include "request.h"


HttpParser* http_parser_new(HTTPRequest *request) {
    HttpParser *parser = malloc(sizeof(HttpParser));
    parser->request = request;
    
    parser->state = 0;
    parser->start_line.ptr = (char*)request->netbuf->inbuf;
    parser->start_line.length = 0;
    
    parser->offset = 0;

    return parser;
}

void http_parser_free(HttpParser *parser) {
    free(parser);
}

int http_parser_process(HttpParser *parser) {
    switch(parser->state) {
        case 0: {
            int r = get_start_line(parser);
            switch(r) {
                case 0: break;
                default: return r;
            }
            parse_request_line(parser);
            parser->state++;
        }
        case 1: {
            return http_parser_parse_header(parser);
        }
        case 2: {
            return 0;
        }
    }
    return -1;
}

int http_parser_validate(HttpParser *parser) {
    HTTPRequest *req = parser->request;
    if(
            !req->method.ptr || req->method.length == 0
            || req->uri.ptr || req->uri.length == 0
            || !req->httpv.ptr || req->httpv.length == 0)
    {
        return 0;
    }
    return 1;
}

int get_start_line(HttpParser *parser) {
    netbuf *buf = parser->request->netbuf;
    while(buf->pos < buf->cursize) {
        unsigned char c = buf->inbuf[buf->pos];
        if(c == '\n') {
            size_t lnlen = buf->pos - parser->offset + 1;
            if(lnlen <= 2) {
                if(lnlen == 1 || buf->inbuf[buf->pos-1] == '\r') {
                    // skip empty line
                    buf->pos++;
                    parser->offset = buf->pos;
                    return 1;
                }
                // insufficient chars for request, return error
                return 2;
            }
            if(buf->inbuf[buf->pos - 1] == '\r') {
                parser->start_line.length = lnlen - 1;
            } else {
                parser->start_line.length = lnlen;
            }
            parser->start_line.ptr = (char*)buf->inbuf + parser->offset;
            buf->pos++;
            return 0;
        }
        buf->pos++;
    }
    return 1;
}

int http_parser_parse_header(HttpParser *parser) {
    netbuf *buf = parser->request->netbuf;

    parser->offset = buf->pos; // line offset
    parser->name.ptr = NULL;
    parser->name.length = 0;
    parser->value.ptr = NULL;
    parser->value.length = 0;
    while(1) {
        if(buf->pos >= buf->cursize) {
            return 1;
        }
        char c = (char)buf->inbuf[buf->pos++];

        if(c > 32) {
            parser->wl = 0;
            if(c == ':' && parser->value.ptr == NULL) {
                parser->name.ptr = (char*)buf->inbuf + parser->offset;
                parser->name.length = buf->pos - parser->offset - 1;
            } else if(parser->name.ptr != NULL && parser->value.ptr == NULL) {
                parser->value.ptr = (char*)buf->inbuf + buf->pos - 1;
            }
        } else if(c == '\n') {
            if(parser->wl) {
                // line contains only white space -> end of request
                parser->state++;
                return 0;
            } else {
                parser->offset = buf->pos;
                if(parser->name.length != 0) {
                    if(parser->value.ptr) {
                        parser->value.length = (buf->inbuf + buf->pos - 1)
                            - (unsigned char*)parser->value.ptr;
                        if(buf->inbuf[buf->pos-2] == '\r') {
                            parser->value.length--;
                        }
                    } else {
                        parser->value.ptr = "";
                    }
                    // add header
                    header_add(
                            parser->request->headers,
                            parser->name,
                            parser->value);
                } else {
                    // error: no value
                    return 2;
                }
                parser->name.ptr = NULL;
                parser->value.ptr = NULL;
                parser->name.length = 0;
                parser->value.length = 0;
                parser->wl = 1;
            }
        }
    }
}

int parse_request_line(HttpParser *parser) {
    sstr_t line = parser->start_line;
    parser->request->request_line = line;
    
    /*
     * parse method, url and http version
     */
    
    int i = 0;
    int ns = 0;

    parser->request->method.ptr = line.ptr;
    for(;i<line.length;i++) {
        if(!ns && line.ptr[i] == ' ') {
            ns = 1;
            //line.ptr[i] = 0; // TODO: remove
            parser->request->method.length = i;
        } else if(ns) {
            if(line.ptr[i] != ' ') {
                break;
            }
        }
    }

    parser->request->uri.ptr = line.ptr + i;
    ns = 0;
    int s = i;
    for(;i<line.length;i++) {
        if(!ns && line.ptr[i] < 33) {
            ns = 1;
            //line.ptr[i] = 0; // TODO: remove
            parser->request->uri.length = i - s;
        } else if(ns) {
            if(line.ptr[i] > 32) {
                break;
            }
        }
    }

    parser->request->httpv.ptr = line.ptr + i;
    ns = 0;
    s = i;
    for(;i<line.length;i++) {
        if(!ns && line.ptr[i] < 33) {
            ns = 1;
            //line.ptr[i] = 0; // TODO: remove
            parser->request->httpv.length = i - s;
        } else if(ns) {
            if(line.ptr[i] > 32) {
                break;
            }
        }
    }

    return 0;
}

mercurial