X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilchttp%2Fsilchttpserver.c;h=ed01beaed1d546a91c74eba09090f9b7d8de91b5;hb=9905799a86c606304fd7df2cd401de1740a272a1;hp=fb5dae10e9c03095f81a571cf77b1b9fb0df3696;hpb=c33005f2f5ead342fd0417931e38013aacfb98b8;p=silc.git diff --git a/lib/silchttp/silchttpserver.c b/lib/silchttp/silchttpserver.c index fb5dae10..ed01beae 100644 --- a/lib/silchttp/silchttpserver.c +++ b/lib/silchttp/silchttpserver.c @@ -47,6 +47,9 @@ struct SilcHttpConnectionStruct { SilcInt64 touched; /* Time last connection was touched */ SilcMime curheaders; /* HTTP request headers */ SilcMime headers; /* HTTP reply headers */ + unsigned char *hptr; /* Pointer to start of headers */ + char *method; /* Method */ + char *uri; /* URI */ unsigned int keepalive : 1; /* Keep alive */ }; @@ -68,13 +71,15 @@ static void silc_http_server_close_connection(SilcHttpConnection conn) silc_buffer_clear(conn->outbuf); silc_buffer_reset(conn->inbuf); silc_buffer_reset(conn->outbuf); + conn->hptr = conn->method = conn->uri = NULL; if (conn->keepalive) return; - SILC_LOG_DEBUG(("Closing HTTP connection")); + SILC_LOG_DEBUG(("Closing HTTP connection %p", conn)); silc_schedule_task_del_by_context(conn->httpd->schedule, conn); + silc_stream_set_notifier(conn->stream, conn->httpd->schedule, NULL, NULL); silc_stream_destroy(conn->stream); /* Add to free list */ @@ -86,97 +91,126 @@ static void silc_http_server_close_connection(SilcHttpConnection conn) static SilcBool silc_http_server_parse(SilcHttpServer httpd, SilcHttpConnection conn) { - SilcUInt32 data_len; + SilcUInt32 data_len, cll; unsigned char *data, *tmp; - char *method, *uri; - const char *value; + const char *value, *cl; SilcBufferStruct postdata; + int i; SILC_LOG_DEBUG(("Parsing HTTP data")); - SILC_LOG_HEXDUMP(("HTTP data"), silc_buffer_data(conn->inbuf), - silc_buffer_len(conn->inbuf)); data = silc_buffer_data(conn->inbuf); data_len = silc_buffer_len(conn->inbuf); - if (data_len < 3) + /* Check for end of headers */ + for (i = 0; i < data_len ; i++) { + if (data_len - i >= 4 && + data[i ] == '\r' && data[i + 1] == '\n' && + data[i + 2] == '\r' && data[i + 3] == '\n') + break; + } + if (i == data_len) return TRUE; - tmp = memchr(data, '\n', data_len); - if (!tmp || tmp[-1] != '\r') { - if (data_len < SILC_HTTP_SERVER_BUFLEN) - return TRUE; - return FALSE; - } - *tmp = 0; - - /* Get method */ - if (strchr(data, ' ')) - *strchr(data, ' ') = 0; - method = data; - SILC_LOG_DEBUG(("Method: '%s'", method)); - - /* Get URI */ - tmp = memchr(data, '\0', data_len); - if (!tmp) { - if (data_len < SILC_HTTP_SERVER_BUFLEN) - return TRUE; - return FALSE; - } - tmp++; - if (strchr(tmp, ' ')) - *strchr(tmp, ' ') = 0; - uri = tmp; - SILC_LOG_DEBUG(("URI: '%s'", uri)); - - /* Get HTTP headers */ - tmp++; - tmp = memchr(tmp, '\n', data_len - (tmp - data)) + 1; - if (!tmp) { - if (data_len < SILC_HTTP_SERVER_BUFLEN) - return TRUE; - return FALSE; + SILC_LOG_HEXDUMP(("HTTP data"), silc_buffer_data(conn->inbuf), + silc_buffer_len(conn->inbuf)); + + if (!conn->method && !conn->uri) { + tmp = memchr(data, '\n', data_len); + if (!tmp || tmp[-1] != '\r') { + if (data_len < SILC_HTTP_SERVER_BUFLEN) + return TRUE; + return FALSE; + } + *tmp = 0; + + /* Get method */ + if (strchr(data, ' ')) + *strchr(data, ' ') = 0; + conn->method = data; + SILC_LOG_DEBUG(("Method: '%s'", conn->method)); + + /* Get URI */ + tmp = memchr(data, '\0', data_len); + if (!tmp) { + if (data_len < SILC_HTTP_SERVER_BUFLEN) + return TRUE; + return FALSE; + } + tmp++; + if (strchr(tmp, ' ')) + *strchr(tmp, ' ') = 0; + conn->uri = tmp; + SILC_LOG_DEBUG(("URI: '%s'", conn->uri)); + + /* Protocol version compatibility */ + tmp = memchr(tmp, '\0', data_len - (tmp - data)) + 1; + SILC_LOG_DEBUG(("Protocol: %s", tmp)); + if (strstr(tmp, "HTTP/1.0")) + conn->keepalive = FALSE; + if (strstr(tmp, "HTTP/1.1")) + conn->keepalive = TRUE; + if (strstr(tmp, "HTTP/1.2")) + conn->keepalive = TRUE; + + /* Get HTTP headers */ + tmp = memchr(tmp, '\0', data_len - (tmp - data)); + if (!tmp) { + if (data_len < SILC_HTTP_SERVER_BUFLEN) + return TRUE; + return FALSE; + } + if (data_len - (tmp - data) < 2) { + if (data_len < SILC_HTTP_SERVER_BUFLEN) + return TRUE; + return FALSE; + } + conn->hptr = ++tmp; } - conn->curheaders = silc_mime_decode(NULL, tmp, data_len - (tmp - data)); - if (!conn->curheaders) { - if (data_len < SILC_HTTP_SERVER_BUFLEN) - return TRUE; + + /* Parse headers and data area */ + conn->curheaders = silc_mime_decode(NULL, conn->hptr, + data_len - (conn->hptr - data)); + if (!conn->curheaders) return FALSE; - } /* Check for persistent connection */ - value = silc_mime_get_field(conn->curheaders, "Keep-alive"); - if (value) - conn->keepalive = TRUE; value = silc_mime_get_field(conn->curheaders, "Connection"); - if (value && !strcasecmp(value, "keep-alive")) - conn->keepalive = TRUE; if (value && !strcasecmp(value, "close")) conn->keepalive = FALSE; /* Deliver request to caller */ - if (!strcasecmp(method, "GET") || !strcasecmp(method, "HEAD")) { - /* Send request to caller */ - httpd->callback(httpd, conn, uri, method, NULL, httpd->context); + if (!strcasecmp(conn->method, "GET") || !strcasecmp(conn->method, "HEAD")) { + httpd->callback(httpd, conn, conn->uri, conn->method, + NULL, httpd->context); - } else if (!strcasecmp(method, "POST")) { + } else if (!strcasecmp(conn->method, "POST")) { /* Get POST data */ tmp = (unsigned char *)silc_mime_get_data(conn->curheaders, &data_len); - if (!tmp) { - silc_mime_free(conn->curheaders); - conn->curheaders = NULL; - if (data_len < SILC_HTTP_SERVER_BUFLEN) - return TRUE; + if (!tmp) return FALSE; + + /* Check we have received all data */ + cl = silc_mime_get_field(conn->curheaders, "Content-Length"); + if (cl && sscanf(cl, "%lu", (unsigned long *)&cll) == 1) { + if (data_len < cll) { + /* More data to come */ + silc_mime_free(conn->curheaders); + conn->curheaders = NULL; + return TRUE; + } } + silc_buffer_set(&postdata, tmp, data_len); SILC_LOG_HEXDUMP(("HTTP POST data"), tmp, data_len); - /* Send request to caller */ - httpd->callback(httpd, conn, uri, method, &postdata, httpd->context); - + httpd->callback(httpd, conn, conn->uri, conn->method, + &postdata, httpd->context); } else { - /* XXX Send bad request error */ + /* Send bad request */ + silc_http_server_send_error(httpd, conn, "400 Bad Request", + "