/* * Copyright (c) 2011 and 2012, Dustin Lundquist * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include /* malloc() */ #include /* strncpy() */ #include /* strncasecmp() */ #include /* isblank() */ #include "http.h" #include "protocol.h" #define SERVER_NAME_LEN 256 static int parse_http_header(const char *, size_t, char **); static int get_header(const char *, const char *, int, char **); static int next_header(const char **, int *); static const protocol_t http_protocol_st = { .default_port = 80, .parse_packet = &parse_http_header, }; const protocol_t *const http_protocol = &http_protocol_st; /* * Parses a HTTP request for the Host: header * * Returns: * >=0 - length of the hostname and updates *hostname * caller is responsible for freeing *hostname * -1 - Incomplete request * -2 - No Host header included in this request * -3 - Invalid hostname pointer * -4 - malloc failure * < -4 - Invalid HTTP request * */ static int parse_http_header(const char *data, size_t data_len, char **hostname) { int result, i; if (hostname == NULL) return -3; if (data_len == 0) return -1; result = get_header("Host:", data, data_len, hostname); if (result < 0) return result; /* * if the user specifies the port in the request, it is included here. * Host: example.com:80 * so we trim off port portion */ for (i = result - 1; i >= 0; i--) if ((*hostname)[i] == ':') { (*hostname)[i] = '\0'; result = i; break; } return result; } static int get_header(const char *header, const char *data, int data_len, char **value) { int len, header_len; header_len = strlen(header); /* loop through headers stopping at first blank line */ while ((len = next_header(&data, &data_len)) != 0) if (len > header_len && strncasecmp(header, data, header_len) == 0) { /* Eat leading whitespace */ while (header_len < len && isblank((unsigned char)data[header_len])) header_len++; *value = malloc(len - header_len + 1); if (*value == NULL) return -4; strncpy(*value, data + header_len, len - header_len); (*value)[len - header_len] = '\0'; return len - header_len; } /* If there is no data left after reading all the headers then we do not * have a complete HTTP request, there must be a blank line */ if (data_len == 0) return -1; return -2; } static int next_header(const char **data, int *len) { int header_len; /* perhaps we can optimize this to reuse the value of header_len, rather * than scanning twice. * Walk our data stream until the end of the header */ while (*len > 2 && (*data)[0] != '\r' && (*data)[1] != '\n') { (*len)--; (*data)++; } /* advanced past the pair */ *data += 2; *len -= 2; /* Find the length of the next header */ header_len = 0; while (*len > header_len + 1 && (*data)[header_len] != '\r' && (*data)[header_len + 1] != '\n') header_len++; return header_len; }