X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fuhttpd.git;a=blobdiff_plain;f=client.c;h=860403464ccd9fece961230ac3b192a662bebc99;hp=6d24ce5c80bd07eb3da9455bd4ba2798f9c3d65a;hb=c92b8cb32d10559791e7bd4937ecc5f49a45ba9d;hpb=38801a320d3f10de1f8ebfffd8964a1ad6d511f4 diff --git a/client.c b/client.c index 6d24ce5..8604034 100644 --- a/client.c +++ b/client.c @@ -27,12 +27,18 @@ static LIST_HEAD(clients); int n_clients = 0; struct config conf = {}; -static const char *http_versions[] = { +const char * const http_versions[] = { [UH_HTTP_VER_0_9] = "HTTP/0.9", [UH_HTTP_VER_1_0] = "HTTP/1.0", [UH_HTTP_VER_1_1] = "HTTP/1.1", }; +const char * const http_methods[] = { + [UH_HTTP_MSG_GET] = "GET", + [UH_HTTP_MSG_POST] = "POST", + [UH_HTTP_MSG_HEAD] = "HEAD", +}; + void uh_http_header(struct client *cl, int code, const char *summary) { const char *enc = "Transfer-Encoding: chunked\r\n"; @@ -53,7 +59,7 @@ void uh_http_header(struct client *cl, int code, const char *summary) static void uh_connection_close(struct client *cl) { - cl->state = CLIENT_STATE_DONE; + cl->state = CLIENT_STATE_CLOSE; cl->us->eof = true; ustream_state_change(cl->us); } @@ -113,11 +119,22 @@ static void client_timeout(struct uloop_timeout *timeout) uh_connection_close(cl); } +static int find_idx(const char * const *list, int max, const char *str) +{ + int i; + + for (i = 0; i < max; i++) + if (!strcmp(list[i], str)) + return i; + + return -1; +} + static int client_parse_request(struct client *cl, char *data) { struct http_request *req = &cl->request; char *type, *path, *version; - int i; + int h_method, h_version; type = strtok(data, " "); path = strtok(NULL, " "); @@ -125,26 +142,18 @@ static int client_parse_request(struct client *cl, char *data) if (!type || !path || !version) return CLIENT_STATE_DONE; + memset(&cl->request, 0, sizeof(cl->request)); req->url = path; - if (!strcmp(type, "GET")) - req->method = UH_HTTP_MSG_GET; - else if (!strcmp(type, "POST")) - req->method = UH_HTTP_MSG_POST; - else if (!strcmp(type, "HEAD")) - req->method = UH_HTTP_MSG_HEAD; - else - return CLIENT_STATE_DONE; - cl->request.version = -1; - i = array_size(http_versions); - while (i--) { - if (!strcmp(version, http_versions[i])) { - cl->request.version = i; - break; - } - } - if (cl->request.version < 0) + h_method = find_idx(http_methods, ARRAY_SIZE(http_methods), type); + h_version = find_idx(http_versions, ARRAY_SIZE(http_versions), version); + if (h_method < 0 || h_version < 0) { + req->version = UH_HTTP_VER_1_0; return CLIENT_STATE_DONE; + } + + req->method = h_method; + req->version = h_version; return CLIENT_STATE_HEADER; } @@ -168,39 +177,66 @@ static bool client_init_cb(struct client *cl, char *buf, int len) return true; } +static bool rfc1918_filter_check(struct client *cl) +{ + if (!conf.rfc1918_filter) + return true; + + if (!uh_addr_rfc1918(&cl->peer_addr) || uh_addr_rfc1918(&cl->srv_addr)) + return true; + + uh_client_error(cl, 403, "Forbidden", + "Rejected request from RFC1918 IP " + "to public server address"); + return false; +} + static void client_header_complete(struct client *cl) { - uh_handle_file_request(cl); + if (!rfc1918_filter_check(cl)) + return; + + if (cl->request.expect_cont) + ustream_printf(cl->us, "HTTP/1.1 100 Continue\r\n\r\n"); + + uh_handle_request(cl); } -static int client_parse_header(struct client *cl, char *data) +static void client_parse_header(struct client *cl, char *data) { char *name; char *val; if (!*data) { uloop_timeout_cancel(&cl->timeout); + cl->state = CLIENT_STATE_DATA; client_header_complete(cl); - return CLIENT_STATE_DATA; + return; } - val = strchr(data, ':'); - if (!val) - return CLIENT_STATE_DONE; - - *val = 0; - val++; - - while (isspace(*val)) - val++; + val = uh_split_header(data); + if (!val) { + cl->state = CLIENT_STATE_DONE; + return; + } for (name = data; *name; name++) if (isupper(*name)) *name = tolower(*name); + if (!strcasecmp(data, "Expect")) { + if (!strcasecmp(val, "100-continue")) + cl->request.expect_cont = true; + else { + uh_header_error(cl, 400, "Bad Request"); + return; + } + } + + blobmsg_add_string(&cl->hdr, data, val); - return CLIENT_STATE_HEADER; + cl->state = CLIENT_STATE_HEADER; } static bool client_data_cb(struct client *cl, char *buf, int len) @@ -218,7 +254,7 @@ static bool client_header_cb(struct client *cl, char *buf, int len) return false; *newline = 0; - cl->state = client_parse_header(cl, buf); + client_parse_header(cl, buf); line_len = newline + 2 - buf; ustream_consume(cl->us, line_len); if (cl->state == CLIENT_STATE_DATA) @@ -263,6 +299,7 @@ static void client_close(struct client *cl) ustream_free(&cl->sfd.stream); close(cl->sfd.fd.fd); list_del(&cl->list); + blob_buf_free(&cl->hdr); free(cl); uh_unblock_listeners(); @@ -287,9 +324,30 @@ static void client_notify_state(struct ustream *s) { struct client *cl = container_of(s, struct client, sfd); - if (cl->state == CLIENT_STATE_CLOSE || - (s->eof && !s->w.data_bytes) || s->write_error) - return client_close(cl); + if (!s->write_error) { + if (cl->state == CLIENT_STATE_DATA) + return; + + if (!s->eof || s->w.data_bytes) + return; + } + + return client_close(cl); +} + +static void set_addr(struct uh_addr *addr, void *src) +{ + struct sockaddr_in *sin = src; + struct sockaddr_in6 *sin6 = src; + + addr->family = sin->sin_family; + if (addr->family == AF_INET) { + addr->port = ntohs(sin->sin_port); + memcpy(&addr->in, &sin->sin_addr, sizeof(addr->in)); + } else { + addr->port = ntohs(sin6->sin6_port); + memcpy(&addr->in6, &sin6->sin6_addr, sizeof(addr->in6)); + } } void uh_accept_client(int fd) @@ -299,19 +357,22 @@ void uh_accept_client(int fd) unsigned int sl; int sfd; static int client_id = 0; + struct sockaddr_in6 addr; if (!next_client) next_client = calloc(1, sizeof(*next_client)); cl = next_client; - sl = sizeof(cl->peeraddr); - sfd = accept(fd, (struct sockaddr *) &cl->peeraddr, &sl); + sl = sizeof(addr); + sfd = accept(fd, (struct sockaddr *) &addr, &sl); if (sfd < 0) return; - sl = sizeof(cl->servaddr); - getsockname(fd, (struct sockaddr *) &cl->servaddr, &sl); + set_addr(&cl->peer_addr, &addr); + sl = sizeof(addr); + getsockname(fd, (struct sockaddr *) &addr, &sl); + set_addr(&cl->srv_addr, &addr); cl->us = &cl->sfd.stream; cl->us->string_data = true; cl->us->notify_read = client_ustream_read_cb;