1*76404edcSAsim Jamshed #include "request.h"
2*76404edcSAsim Jamshed #include "keyvalue.h"
3*76404edcSAsim Jamshed #include "log.h"
4*76404edcSAsim Jamshed 
5*76404edcSAsim Jamshed #include <sys/stat.h>
6*76404edcSAsim Jamshed 
7*76404edcSAsim Jamshed #include <limits.h>
8*76404edcSAsim Jamshed #include <stdlib.h>
9*76404edcSAsim Jamshed #include <string.h>
10*76404edcSAsim Jamshed #include <stdio.h>
11*76404edcSAsim Jamshed #include <ctype.h>
12*76404edcSAsim Jamshed 
request_check_hostname(server * srv,connection * con,buffer * host)13*76404edcSAsim Jamshed static int request_check_hostname(server *srv, connection *con, buffer *host) {
14*76404edcSAsim Jamshed 	enum { DOMAINLABEL, TOPLABEL } stage = TOPLABEL;
15*76404edcSAsim Jamshed 	size_t i;
16*76404edcSAsim Jamshed 	int label_len = 0;
17*76404edcSAsim Jamshed 	size_t host_len;
18*76404edcSAsim Jamshed 	char *colon;
19*76404edcSAsim Jamshed 	int is_ip = -1; /* -1 don't know yet, 0 no, 1 yes */
20*76404edcSAsim Jamshed 	int level = 0;
21*76404edcSAsim Jamshed 
22*76404edcSAsim Jamshed 	UNUSED(srv);
23*76404edcSAsim Jamshed 	UNUSED(con);
24*76404edcSAsim Jamshed 
25*76404edcSAsim Jamshed 	/*
26*76404edcSAsim Jamshed 	 *       hostport      = host [ ":" port ]
27*76404edcSAsim Jamshed 	 *       host          = hostname | IPv4address | IPv6address
28*76404edcSAsim Jamshed 	 *       hostname      = *( domainlabel "." ) toplabel [ "." ]
29*76404edcSAsim Jamshed 	 *       domainlabel   = alphanum | alphanum *( alphanum | "-" ) alphanum
30*76404edcSAsim Jamshed 	 *       toplabel      = alpha | alpha *( alphanum | "-" ) alphanum
31*76404edcSAsim Jamshed 	 *       IPv4address   = 1*digit "." 1*digit "." 1*digit "." 1*digit
32*76404edcSAsim Jamshed 	 *       IPv6address   = "[" ... "]"
33*76404edcSAsim Jamshed 	 *       port          = *digit
34*76404edcSAsim Jamshed 	 */
35*76404edcSAsim Jamshed 
36*76404edcSAsim Jamshed 	/* no Host: */
37*76404edcSAsim Jamshed 	if (!host || host->used == 0) return 0;
38*76404edcSAsim Jamshed 
39*76404edcSAsim Jamshed 	host_len = host->used - 1;
40*76404edcSAsim Jamshed 
41*76404edcSAsim Jamshed 	/* IPv6 adress */
42*76404edcSAsim Jamshed 	if (host->ptr[0] == '[') {
43*76404edcSAsim Jamshed 		char *c = host->ptr + 1;
44*76404edcSAsim Jamshed 		int colon_cnt = 0;
45*76404edcSAsim Jamshed 
46*76404edcSAsim Jamshed 		/* check portnumber */
47*76404edcSAsim Jamshed 		for (; *c && *c != ']'; c++) {
48*76404edcSAsim Jamshed 			if (*c == ':') {
49*76404edcSAsim Jamshed 				if (++colon_cnt > 7) {
50*76404edcSAsim Jamshed 					return -1;
51*76404edcSAsim Jamshed 				}
52*76404edcSAsim Jamshed 			} else if (!light_isxdigit(*c) && '.' != *c) {
53*76404edcSAsim Jamshed 				return -1;
54*76404edcSAsim Jamshed 			}
55*76404edcSAsim Jamshed 		}
56*76404edcSAsim Jamshed 
57*76404edcSAsim Jamshed 		/* missing ] */
58*76404edcSAsim Jamshed 		if (!*c) {
59*76404edcSAsim Jamshed 			return -1;
60*76404edcSAsim Jamshed 		}
61*76404edcSAsim Jamshed 
62*76404edcSAsim Jamshed 		/* check port */
63*76404edcSAsim Jamshed 		if (*(c+1) == ':') {
64*76404edcSAsim Jamshed 			for (c += 2; *c; c++) {
65*76404edcSAsim Jamshed 				if (!light_isdigit(*c)) {
66*76404edcSAsim Jamshed 					return -1;
67*76404edcSAsim Jamshed 				}
68*76404edcSAsim Jamshed 			}
69*76404edcSAsim Jamshed 		}
70*76404edcSAsim Jamshed 		return 0;
71*76404edcSAsim Jamshed 	}
72*76404edcSAsim Jamshed 
73*76404edcSAsim Jamshed 	if (NULL != (colon = memchr(host->ptr, ':', host_len))) {
74*76404edcSAsim Jamshed 		char *c = colon + 1;
75*76404edcSAsim Jamshed 
76*76404edcSAsim Jamshed 		/* check portnumber */
77*76404edcSAsim Jamshed 		for (; *c; c++) {
78*76404edcSAsim Jamshed 			if (!light_isdigit(*c)) return -1;
79*76404edcSAsim Jamshed 		}
80*76404edcSAsim Jamshed 
81*76404edcSAsim Jamshed 		/* remove the port from the host-len */
82*76404edcSAsim Jamshed 		host_len = colon - host->ptr;
83*76404edcSAsim Jamshed 	}
84*76404edcSAsim Jamshed 
85*76404edcSAsim Jamshed 	/* Host is empty */
86*76404edcSAsim Jamshed 	if (host_len == 0) return -1;
87*76404edcSAsim Jamshed 
88*76404edcSAsim Jamshed 	/* if the hostname ends in a "." strip it */
89*76404edcSAsim Jamshed 	if (host->ptr[host_len-1] == '.') {
90*76404edcSAsim Jamshed 		/* shift port info one left */
91*76404edcSAsim Jamshed 		if (NULL != colon) memmove(colon-1, colon, host->used - host_len);
92*76404edcSAsim Jamshed 		else host->ptr[host_len-1] = '\0';
93*76404edcSAsim Jamshed 		host_len -= 1;
94*76404edcSAsim Jamshed 		host->used -= 1;
95*76404edcSAsim Jamshed 	}
96*76404edcSAsim Jamshed 
97*76404edcSAsim Jamshed 	if (host_len == 0) return -1;
98*76404edcSAsim Jamshed 
99*76404edcSAsim Jamshed 	/* scan from the right and skip the \0 */
100*76404edcSAsim Jamshed 	for (i = host_len; i-- > 0; ) {
101*76404edcSAsim Jamshed 		const char c = host->ptr[i];
102*76404edcSAsim Jamshed 
103*76404edcSAsim Jamshed 		switch (stage) {
104*76404edcSAsim Jamshed 		case TOPLABEL:
105*76404edcSAsim Jamshed 			if (c == '.') {
106*76404edcSAsim Jamshed 				/* only switch stage, if this is not the last character */
107*76404edcSAsim Jamshed 				if (i != host_len - 1) {
108*76404edcSAsim Jamshed 					if (label_len == 0) {
109*76404edcSAsim Jamshed 						return -1;
110*76404edcSAsim Jamshed 					}
111*76404edcSAsim Jamshed 
112*76404edcSAsim Jamshed 					/* check the first character at right of the dot */
113*76404edcSAsim Jamshed 					if (is_ip == 0) {
114*76404edcSAsim Jamshed 						if (!light_isalnum(host->ptr[i+1])) {
115*76404edcSAsim Jamshed 							return -1;
116*76404edcSAsim Jamshed 						}
117*76404edcSAsim Jamshed 					} else if (!light_isdigit(host->ptr[i+1])) {
118*76404edcSAsim Jamshed 						is_ip = 0;
119*76404edcSAsim Jamshed 					} else if ('-' == host->ptr[i+1]) {
120*76404edcSAsim Jamshed 						return -1;
121*76404edcSAsim Jamshed 					} else {
122*76404edcSAsim Jamshed 						/* just digits */
123*76404edcSAsim Jamshed 						is_ip = 1;
124*76404edcSAsim Jamshed 					}
125*76404edcSAsim Jamshed 
126*76404edcSAsim Jamshed 					stage = DOMAINLABEL;
127*76404edcSAsim Jamshed 
128*76404edcSAsim Jamshed 					label_len = 0;
129*76404edcSAsim Jamshed 					level++;
130*76404edcSAsim Jamshed 				} else if (i == 0) {
131*76404edcSAsim Jamshed 					/* just a dot and nothing else is evil */
132*76404edcSAsim Jamshed 					return -1;
133*76404edcSAsim Jamshed 				}
134*76404edcSAsim Jamshed 			} else if (i == 0) {
135*76404edcSAsim Jamshed 				/* the first character of the hostname */
136*76404edcSAsim Jamshed 				if (!light_isalnum(c)) {
137*76404edcSAsim Jamshed 					return -1;
138*76404edcSAsim Jamshed 				}
139*76404edcSAsim Jamshed 				label_len++;
140*76404edcSAsim Jamshed 			} else {
141*76404edcSAsim Jamshed 				if (c != '-' && !light_isalnum(c)) {
142*76404edcSAsim Jamshed 					return -1;
143*76404edcSAsim Jamshed 				}
144*76404edcSAsim Jamshed 				if (is_ip == -1) {
145*76404edcSAsim Jamshed 					if (!light_isdigit(c)) is_ip = 0;
146*76404edcSAsim Jamshed 				}
147*76404edcSAsim Jamshed 				label_len++;
148*76404edcSAsim Jamshed 			}
149*76404edcSAsim Jamshed 
150*76404edcSAsim Jamshed 			break;
151*76404edcSAsim Jamshed 		case DOMAINLABEL:
152*76404edcSAsim Jamshed 			if (is_ip == 1) {
153*76404edcSAsim Jamshed 				if (c == '.') {
154*76404edcSAsim Jamshed 					if (label_len == 0) {
155*76404edcSAsim Jamshed 						return -1;
156*76404edcSAsim Jamshed 					}
157*76404edcSAsim Jamshed 
158*76404edcSAsim Jamshed 					label_len = 0;
159*76404edcSAsim Jamshed 					level++;
160*76404edcSAsim Jamshed 				} else if (!light_isdigit(c)) {
161*76404edcSAsim Jamshed 					return -1;
162*76404edcSAsim Jamshed 				} else {
163*76404edcSAsim Jamshed 					label_len++;
164*76404edcSAsim Jamshed 				}
165*76404edcSAsim Jamshed 			} else {
166*76404edcSAsim Jamshed 				if (c == '.') {
167*76404edcSAsim Jamshed 					if (label_len == 0) {
168*76404edcSAsim Jamshed 						return -1;
169*76404edcSAsim Jamshed 					}
170*76404edcSAsim Jamshed 
171*76404edcSAsim Jamshed 					/* c is either - or alphanum here */
172*76404edcSAsim Jamshed 					if ('-' == host->ptr[i+1]) {
173*76404edcSAsim Jamshed 						return -1;
174*76404edcSAsim Jamshed 					}
175*76404edcSAsim Jamshed 
176*76404edcSAsim Jamshed 					label_len = 0;
177*76404edcSAsim Jamshed 					level++;
178*76404edcSAsim Jamshed 				} else if (i == 0) {
179*76404edcSAsim Jamshed 					if (!light_isalnum(c)) {
180*76404edcSAsim Jamshed 						return -1;
181*76404edcSAsim Jamshed 					}
182*76404edcSAsim Jamshed 					label_len++;
183*76404edcSAsim Jamshed 				} else {
184*76404edcSAsim Jamshed 					if (c != '-' && !light_isalnum(c)) {
185*76404edcSAsim Jamshed 						return -1;
186*76404edcSAsim Jamshed 					}
187*76404edcSAsim Jamshed 					label_len++;
188*76404edcSAsim Jamshed 				}
189*76404edcSAsim Jamshed 			}
190*76404edcSAsim Jamshed 
191*76404edcSAsim Jamshed 			break;
192*76404edcSAsim Jamshed 		}
193*76404edcSAsim Jamshed 	}
194*76404edcSAsim Jamshed 
195*76404edcSAsim Jamshed 	/* a IP has to consist of 4 parts */
196*76404edcSAsim Jamshed 	if (is_ip == 1 && level != 3) {
197*76404edcSAsim Jamshed 		return -1;
198*76404edcSAsim Jamshed 	}
199*76404edcSAsim Jamshed 
200*76404edcSAsim Jamshed 	if (label_len == 0) {
201*76404edcSAsim Jamshed 		return -1;
202*76404edcSAsim Jamshed 	}
203*76404edcSAsim Jamshed 
204*76404edcSAsim Jamshed 	return 0;
205*76404edcSAsim Jamshed }
206*76404edcSAsim Jamshed 
207*76404edcSAsim Jamshed #if 0
208*76404edcSAsim Jamshed #define DUMP_HEADER
209*76404edcSAsim Jamshed #endif
210*76404edcSAsim Jamshed 
http_request_split_value(array * vals,buffer * b)211*76404edcSAsim Jamshed static int http_request_split_value(array *vals, buffer *b) {
212*76404edcSAsim Jamshed 	size_t i;
213*76404edcSAsim Jamshed 	int state = 0;
214*76404edcSAsim Jamshed 
215*76404edcSAsim Jamshed 	const char *current;
216*76404edcSAsim Jamshed 	const char *token_start = NULL, *token_end = NULL;
217*76404edcSAsim Jamshed 	/*
218*76404edcSAsim Jamshed 	 * parse
219*76404edcSAsim Jamshed 	 *
220*76404edcSAsim Jamshed 	 * val1, val2, val3, val4
221*76404edcSAsim Jamshed 	 *
222*76404edcSAsim Jamshed 	 * into a array (more or less a explode() incl. striping of whitespaces
223*76404edcSAsim Jamshed 	 */
224*76404edcSAsim Jamshed 
225*76404edcSAsim Jamshed 	if (b->used == 0) return 0;
226*76404edcSAsim Jamshed 
227*76404edcSAsim Jamshed 	current = b->ptr;
228*76404edcSAsim Jamshed 	for (i =  0; i < b->used; ++i, ++current) {
229*76404edcSAsim Jamshed 		data_string *ds;
230*76404edcSAsim Jamshed 
231*76404edcSAsim Jamshed 		switch (state) {
232*76404edcSAsim Jamshed 		case 0: /* find start of a token */
233*76404edcSAsim Jamshed 			switch (*current) {
234*76404edcSAsim Jamshed 			case ' ':
235*76404edcSAsim Jamshed 			case '\t': /* skip white space */
236*76404edcSAsim Jamshed 			case ',': /* skip empty token */
237*76404edcSAsim Jamshed 				break;
238*76404edcSAsim Jamshed 			case '\0': /* end of string */
239*76404edcSAsim Jamshed 				return 0;
240*76404edcSAsim Jamshed 			default:
241*76404edcSAsim Jamshed 				/* found real data, switch to state 1 to find the end of the token */
242*76404edcSAsim Jamshed 				token_start = token_end = current;
243*76404edcSAsim Jamshed 				state = 1;
244*76404edcSAsim Jamshed 				break;
245*76404edcSAsim Jamshed 			}
246*76404edcSAsim Jamshed 			break;
247*76404edcSAsim Jamshed 		case 1: /* find end of token and last non white space character */
248*76404edcSAsim Jamshed 			switch (*current) {
249*76404edcSAsim Jamshed 			case ' ':
250*76404edcSAsim Jamshed 			case '\t':
251*76404edcSAsim Jamshed 				/* space - don't update token_end */
252*76404edcSAsim Jamshed 				break;
253*76404edcSAsim Jamshed 			case ',':
254*76404edcSAsim Jamshed 			case '\0': /* end of string also marks the end of a token */
255*76404edcSAsim Jamshed 				if (NULL == (ds = (data_string *)array_get_unused_element(vals, TYPE_STRING))) {
256*76404edcSAsim Jamshed 					ds = data_string_init();
257*76404edcSAsim Jamshed 				}
258*76404edcSAsim Jamshed 
259*76404edcSAsim Jamshed 				buffer_copy_string_len(ds->value, token_start, token_end-token_start+1);
260*76404edcSAsim Jamshed 				array_insert_unique(vals, (data_unset *)ds);
261*76404edcSAsim Jamshed 
262*76404edcSAsim Jamshed 				state = 0;
263*76404edcSAsim Jamshed 				break;
264*76404edcSAsim Jamshed 			default:
265*76404edcSAsim Jamshed 				/* no white space, update token_end to include current character */
266*76404edcSAsim Jamshed 				token_end = current;
267*76404edcSAsim Jamshed 				break;
268*76404edcSAsim Jamshed 			}
269*76404edcSAsim Jamshed 			break;
270*76404edcSAsim Jamshed 		}
271*76404edcSAsim Jamshed 	}
272*76404edcSAsim Jamshed 
273*76404edcSAsim Jamshed 	return 0;
274*76404edcSAsim Jamshed }
275*76404edcSAsim Jamshed 
request_uri_is_valid_char(unsigned char c)276*76404edcSAsim Jamshed static int request_uri_is_valid_char(unsigned char c) {
277*76404edcSAsim Jamshed 	if (c <= 32) return 0;
278*76404edcSAsim Jamshed 	if (c == 127) return 0;
279*76404edcSAsim Jamshed 	if (c == 255) return 0;
280*76404edcSAsim Jamshed 
281*76404edcSAsim Jamshed 	return 1;
282*76404edcSAsim Jamshed }
283*76404edcSAsim Jamshed 
http_request_parse(server * srv,connection * con)284*76404edcSAsim Jamshed int http_request_parse(server *srv, connection *con) {
285*76404edcSAsim Jamshed 	char *uri = NULL, *proto = NULL, *method = NULL, con_length_set;
286*76404edcSAsim Jamshed 	int is_key = 1, key_len = 0, is_ws_after_key = 0, in_folding;
287*76404edcSAsim Jamshed 	char *value = NULL, *key = NULL;
288*76404edcSAsim Jamshed 	char *reqline_host = NULL;
289*76404edcSAsim Jamshed 	int reqline_hostlen = 0;
290*76404edcSAsim Jamshed 
291*76404edcSAsim Jamshed 	enum { HTTP_CONNECTION_UNSET, HTTP_CONNECTION_KEEPALIVE, HTTP_CONNECTION_CLOSE } keep_alive_set = HTTP_CONNECTION_UNSET;
292*76404edcSAsim Jamshed 
293*76404edcSAsim Jamshed 	int line = 0;
294*76404edcSAsim Jamshed 
295*76404edcSAsim Jamshed 	int request_line_stage = 0;
296*76404edcSAsim Jamshed 	size_t i, first;
297*76404edcSAsim Jamshed 
298*76404edcSAsim Jamshed 	int done = 0;
299*76404edcSAsim Jamshed 
300*76404edcSAsim Jamshed 	/*
301*76404edcSAsim Jamshed 	 * Request: "^(GET|POST|HEAD) ([^ ]+(\\?[^ ]+|)) (HTTP/1\\.[01])$"
302*76404edcSAsim Jamshed 	 * Option : "^([-a-zA-Z]+): (.+)$"
303*76404edcSAsim Jamshed 	 * End    : "^$"
304*76404edcSAsim Jamshed 	 */
305*76404edcSAsim Jamshed 
306*76404edcSAsim Jamshed 	if (con->conf.log_request_header) {
307*76404edcSAsim Jamshed 		log_error_write(srv, __FILE__, __LINE__, "sdsdSb",
308*76404edcSAsim Jamshed 				"fd:", con->fd,
309*76404edcSAsim Jamshed 				"request-len:", con->request.request->used,
310*76404edcSAsim Jamshed 				"\n", con->request.request);
311*76404edcSAsim Jamshed 	}
312*76404edcSAsim Jamshed 
313*76404edcSAsim Jamshed 	if (con->request_count > 1 &&
314*76404edcSAsim Jamshed 	    con->request.request->ptr[0] == '\r' &&
315*76404edcSAsim Jamshed 	    con->request.request->ptr[1] == '\n') {
316*76404edcSAsim Jamshed 		/* we are in keep-alive and might get \r\n after a previous POST request.*/
317*76404edcSAsim Jamshed 
318*76404edcSAsim Jamshed 		buffer_copy_string_len(con->parse_request, con->request.request->ptr + 2, con->request.request->used - 1 - 2);
319*76404edcSAsim Jamshed 	} else {
320*76404edcSAsim Jamshed 		/* fill the local request buffer */
321*76404edcSAsim Jamshed 		buffer_copy_string_buffer(con->parse_request, con->request.request);
322*76404edcSAsim Jamshed 	}
323*76404edcSAsim Jamshed 
324*76404edcSAsim Jamshed 	keep_alive_set = 0;
325*76404edcSAsim Jamshed 	con_length_set = 0;
326*76404edcSAsim Jamshed 
327*76404edcSAsim Jamshed 	/* parse the first line of the request
328*76404edcSAsim Jamshed 	 *
329*76404edcSAsim Jamshed 	 * should be:
330*76404edcSAsim Jamshed 	 *
331*76404edcSAsim Jamshed 	 * <method> <uri> <protocol>\r\n
332*76404edcSAsim Jamshed 	 * */
333*76404edcSAsim Jamshed 	for (i = 0, first = 0; i < con->parse_request->used && line == 0; i++) {
334*76404edcSAsim Jamshed 		char *cur = con->parse_request->ptr + i;
335*76404edcSAsim Jamshed 
336*76404edcSAsim Jamshed 		switch(*cur) {
337*76404edcSAsim Jamshed 		case '\r':
338*76404edcSAsim Jamshed 			if (con->parse_request->ptr[i+1] == '\n') {
339*76404edcSAsim Jamshed 				http_method_t r;
340*76404edcSAsim Jamshed 				char *nuri = NULL;
341*76404edcSAsim Jamshed 				size_t j;
342*76404edcSAsim Jamshed 
343*76404edcSAsim Jamshed 				/* \r\n -> \0\0 */
344*76404edcSAsim Jamshed 				con->parse_request->ptr[i] = '\0';
345*76404edcSAsim Jamshed 				con->parse_request->ptr[i+1] = '\0';
346*76404edcSAsim Jamshed 
347*76404edcSAsim Jamshed 				buffer_copy_string_len(con->request.request_line, con->parse_request->ptr, i);
348*76404edcSAsim Jamshed 
349*76404edcSAsim Jamshed 				if (request_line_stage != 2) {
350*76404edcSAsim Jamshed 					con->http_status = 400;
351*76404edcSAsim Jamshed 					con->response.keep_alive = 0;
352*76404edcSAsim Jamshed 					con->keep_alive = 0;
353*76404edcSAsim Jamshed 
354*76404edcSAsim Jamshed 					if (srv->srvconf.log_request_header_on_error) {
355*76404edcSAsim Jamshed 						log_error_write(srv, __FILE__, __LINE__, "s", "incomplete request line -> 400");
356*76404edcSAsim Jamshed 						log_error_write(srv, __FILE__, __LINE__, "Sb",
357*76404edcSAsim Jamshed 								"request-header:\n",
358*76404edcSAsim Jamshed 								con->request.request);
359*76404edcSAsim Jamshed 					}
360*76404edcSAsim Jamshed 					return 0;
361*76404edcSAsim Jamshed 				}
362*76404edcSAsim Jamshed 
363*76404edcSAsim Jamshed 				proto = con->parse_request->ptr + first;
364*76404edcSAsim Jamshed 
365*76404edcSAsim Jamshed 				*(uri - 1) = '\0';
366*76404edcSAsim Jamshed 				*(proto - 1) = '\0';
367*76404edcSAsim Jamshed 
368*76404edcSAsim Jamshed 				/* we got the first one :) */
369*76404edcSAsim Jamshed 				if (-1 == (r = get_http_method_key(method))) {
370*76404edcSAsim Jamshed 					con->http_status = 501;
371*76404edcSAsim Jamshed 					con->response.keep_alive = 0;
372*76404edcSAsim Jamshed 					con->keep_alive = 0;
373*76404edcSAsim Jamshed 
374*76404edcSAsim Jamshed 					if (srv->srvconf.log_request_header_on_error) {
375*76404edcSAsim Jamshed 						log_error_write(srv, __FILE__, __LINE__, "s", "unknown http-method -> 501");
376*76404edcSAsim Jamshed 						log_error_write(srv, __FILE__, __LINE__, "Sb",
377*76404edcSAsim Jamshed 								"request-header:\n",
378*76404edcSAsim Jamshed 								con->request.request);
379*76404edcSAsim Jamshed 					}
380*76404edcSAsim Jamshed 
381*76404edcSAsim Jamshed 					return 0;
382*76404edcSAsim Jamshed 				}
383*76404edcSAsim Jamshed 
384*76404edcSAsim Jamshed 				con->request.http_method = r;
385*76404edcSAsim Jamshed 
386*76404edcSAsim Jamshed 				/*
387*76404edcSAsim Jamshed 				 * RFC2616 says:
388*76404edcSAsim Jamshed 				 *
389*76404edcSAsim Jamshed 				 * HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT
390*76404edcSAsim Jamshed 				 *
391*76404edcSAsim Jamshed 				 * */
392*76404edcSAsim Jamshed 				if (0 == strncmp(proto, "HTTP/", sizeof("HTTP/") - 1)) {
393*76404edcSAsim Jamshed 					char * major = proto + sizeof("HTTP/") - 1;
394*76404edcSAsim Jamshed 					char * minor = strchr(major, '.');
395*76404edcSAsim Jamshed 					char *err = NULL;
396*76404edcSAsim Jamshed 					int major_num = 0, minor_num = 0;
397*76404edcSAsim Jamshed 
398*76404edcSAsim Jamshed 					int invalid_version = 0;
399*76404edcSAsim Jamshed 
400*76404edcSAsim Jamshed 					if (NULL == minor || /* no dot */
401*76404edcSAsim Jamshed 					    minor == major || /* no major */
402*76404edcSAsim Jamshed 					    *(minor + 1) == '\0' /* no minor */) {
403*76404edcSAsim Jamshed 						invalid_version = 1;
404*76404edcSAsim Jamshed 					} else {
405*76404edcSAsim Jamshed 						*minor = '\0';
406*76404edcSAsim Jamshed 						major_num = strtol(major, &err, 10);
407*76404edcSAsim Jamshed 
408*76404edcSAsim Jamshed 						if (*err != '\0') invalid_version = 1;
409*76404edcSAsim Jamshed 
410*76404edcSAsim Jamshed 						*minor++ = '.';
411*76404edcSAsim Jamshed 						minor_num = strtol(minor, &err, 10);
412*76404edcSAsim Jamshed 
413*76404edcSAsim Jamshed 						if (*err != '\0') invalid_version = 1;
414*76404edcSAsim Jamshed 					}
415*76404edcSAsim Jamshed 
416*76404edcSAsim Jamshed 					if (invalid_version) {
417*76404edcSAsim Jamshed 						con->http_status = 400;
418*76404edcSAsim Jamshed 						con->keep_alive = 0;
419*76404edcSAsim Jamshed 
420*76404edcSAsim Jamshed 						if (srv->srvconf.log_request_header_on_error) {
421*76404edcSAsim Jamshed 							log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400");
422*76404edcSAsim Jamshed 							log_error_write(srv, __FILE__, __LINE__, "Sb",
423*76404edcSAsim Jamshed 									"request-header:\n",
424*76404edcSAsim Jamshed 									con->request.request);
425*76404edcSAsim Jamshed 						}
426*76404edcSAsim Jamshed 						return 0;
427*76404edcSAsim Jamshed 					}
428*76404edcSAsim Jamshed 
429*76404edcSAsim Jamshed 					if (major_num == 1 && minor_num == 1) {
430*76404edcSAsim Jamshed 						con->request.http_version = con->conf.allow_http11 ? HTTP_VERSION_1_1 : HTTP_VERSION_1_0;
431*76404edcSAsim Jamshed 					} else if (major_num == 1 && minor_num == 0) {
432*76404edcSAsim Jamshed 						con->request.http_version = HTTP_VERSION_1_0;
433*76404edcSAsim Jamshed 					} else {
434*76404edcSAsim Jamshed 						con->http_status = 505;
435*76404edcSAsim Jamshed 
436*76404edcSAsim Jamshed 						if (srv->srvconf.log_request_header_on_error) {
437*76404edcSAsim Jamshed 							log_error_write(srv, __FILE__, __LINE__, "s", "unknown HTTP version -> 505");
438*76404edcSAsim Jamshed 							log_error_write(srv, __FILE__, __LINE__, "Sb",
439*76404edcSAsim Jamshed 									"request-header:\n",
440*76404edcSAsim Jamshed 									con->request.request);
441*76404edcSAsim Jamshed 						}
442*76404edcSAsim Jamshed 						return 0;
443*76404edcSAsim Jamshed 					}
444*76404edcSAsim Jamshed 				} else {
445*76404edcSAsim Jamshed 					con->http_status = 400;
446*76404edcSAsim Jamshed 					con->keep_alive = 0;
447*76404edcSAsim Jamshed 
448*76404edcSAsim Jamshed 					if (srv->srvconf.log_request_header_on_error) {
449*76404edcSAsim Jamshed 						log_error_write(srv, __FILE__, __LINE__, "s", "unknown protocol -> 400");
450*76404edcSAsim Jamshed 						log_error_write(srv, __FILE__, __LINE__, "Sb",
451*76404edcSAsim Jamshed 								"request-header:\n",
452*76404edcSAsim Jamshed 								con->request.request);
453*76404edcSAsim Jamshed 					}
454*76404edcSAsim Jamshed 					return 0;
455*76404edcSAsim Jamshed 				}
456*76404edcSAsim Jamshed 
457*76404edcSAsim Jamshed 				if (0 == strncmp(uri, "http://", 7) &&
458*76404edcSAsim Jamshed 				    NULL != (nuri = strchr(uri + 7, '/'))) {
459*76404edcSAsim Jamshed 					reqline_host = uri + 7;
460*76404edcSAsim Jamshed 					reqline_hostlen = nuri - reqline_host;
461*76404edcSAsim Jamshed 
462*76404edcSAsim Jamshed 					buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1);
463*76404edcSAsim Jamshed 				} else if (0 == strncmp(uri, "https://", 8) &&
464*76404edcSAsim Jamshed 				    NULL != (nuri = strchr(uri + 8, '/'))) {
465*76404edcSAsim Jamshed 					reqline_host = uri + 8;
466*76404edcSAsim Jamshed 					reqline_hostlen = nuri - reqline_host;
467*76404edcSAsim Jamshed 
468*76404edcSAsim Jamshed 					buffer_copy_string_len(con->request.uri, nuri, proto - nuri - 1);
469*76404edcSAsim Jamshed 				} else {
470*76404edcSAsim Jamshed 					/* everything looks good so far */
471*76404edcSAsim Jamshed 					buffer_copy_string_len(con->request.uri, uri, proto - uri - 1);
472*76404edcSAsim Jamshed 				}
473*76404edcSAsim Jamshed 
474*76404edcSAsim Jamshed 				/* check uri for invalid characters */
475*76404edcSAsim Jamshed 				for (j = 0; j < con->request.uri->used - 1; j++) {
476*76404edcSAsim Jamshed 					if (!request_uri_is_valid_char(con->request.uri->ptr[j])) {
477*76404edcSAsim Jamshed 						unsigned char buf[2];
478*76404edcSAsim Jamshed 						con->http_status = 400;
479*76404edcSAsim Jamshed 						con->keep_alive = 0;
480*76404edcSAsim Jamshed 
481*76404edcSAsim Jamshed 						if (srv->srvconf.log_request_header_on_error) {
482*76404edcSAsim Jamshed 							buf[0] = con->request.uri->ptr[j];
483*76404edcSAsim Jamshed 							buf[1] = '\0';
484*76404edcSAsim Jamshed 
485*76404edcSAsim Jamshed 							if (con->request.uri->ptr[j] > 32 &&
486*76404edcSAsim Jamshed 							    con->request.uri->ptr[j] != 127) {
487*76404edcSAsim Jamshed 								/* the character is printable -> print it */
488*76404edcSAsim Jamshed 								log_error_write(srv, __FILE__, __LINE__, "ss",
489*76404edcSAsim Jamshed 										"invalid character in URI -> 400",
490*76404edcSAsim Jamshed 										buf);
491*76404edcSAsim Jamshed 							} else {
492*76404edcSAsim Jamshed 								/* a control-character, print ascii-code */
493*76404edcSAsim Jamshed 								log_error_write(srv, __FILE__, __LINE__, "sd",
494*76404edcSAsim Jamshed 										"invalid character in URI -> 400",
495*76404edcSAsim Jamshed 										con->request.uri->ptr[j]);
496*76404edcSAsim Jamshed 							}
497*76404edcSAsim Jamshed 
498*76404edcSAsim Jamshed 							log_error_write(srv, __FILE__, __LINE__, "Sb",
499*76404edcSAsim Jamshed 									"request-header:\n",
500*76404edcSAsim Jamshed 									con->request.request);
501*76404edcSAsim Jamshed 						}
502*76404edcSAsim Jamshed 
503*76404edcSAsim Jamshed 						return 0;
504*76404edcSAsim Jamshed 					}
505*76404edcSAsim Jamshed 				}
506*76404edcSAsim Jamshed 
507*76404edcSAsim Jamshed 				buffer_copy_string_buffer(con->request.orig_uri, con->request.uri);
508*76404edcSAsim Jamshed 
509*76404edcSAsim Jamshed 				con->http_status = 0;
510*76404edcSAsim Jamshed 
511*76404edcSAsim Jamshed 				i++;
512*76404edcSAsim Jamshed 				line++;
513*76404edcSAsim Jamshed 				first = i+1;
514*76404edcSAsim Jamshed 			}
515*76404edcSAsim Jamshed 			break;
516*76404edcSAsim Jamshed 		case ' ':
517*76404edcSAsim Jamshed 			switch(request_line_stage) {
518*76404edcSAsim Jamshed 			case 0:
519*76404edcSAsim Jamshed 				/* GET|POST|... */
520*76404edcSAsim Jamshed 				method = con->parse_request->ptr + first;
521*76404edcSAsim Jamshed 				first = i + 1;
522*76404edcSAsim Jamshed 				break;
523*76404edcSAsim Jamshed 			case 1:
524*76404edcSAsim Jamshed 				/* /foobar/... */
525*76404edcSAsim Jamshed 				uri = con->parse_request->ptr + first;
526*76404edcSAsim Jamshed 				first = i + 1;
527*76404edcSAsim Jamshed 				break;
528*76404edcSAsim Jamshed 			default:
529*76404edcSAsim Jamshed 				/* ERROR, one space to much */
530*76404edcSAsim Jamshed 				con->http_status = 400;
531*76404edcSAsim Jamshed 				con->response.keep_alive = 0;
532*76404edcSAsim Jamshed 				con->keep_alive = 0;
533*76404edcSAsim Jamshed 
534*76404edcSAsim Jamshed 				if (srv->srvconf.log_request_header_on_error) {
535*76404edcSAsim Jamshed 					log_error_write(srv, __FILE__, __LINE__, "s", "overlong request line -> 400");
536*76404edcSAsim Jamshed 					log_error_write(srv, __FILE__, __LINE__, "Sb",
537*76404edcSAsim Jamshed 							"request-header:\n",
538*76404edcSAsim Jamshed 							con->request.request);
539*76404edcSAsim Jamshed 				}
540*76404edcSAsim Jamshed 				return 0;
541*76404edcSAsim Jamshed 			}
542*76404edcSAsim Jamshed 
543*76404edcSAsim Jamshed 			request_line_stage++;
544*76404edcSAsim Jamshed 			break;
545*76404edcSAsim Jamshed 		}
546*76404edcSAsim Jamshed 	}
547*76404edcSAsim Jamshed 
548*76404edcSAsim Jamshed 	in_folding = 0;
549*76404edcSAsim Jamshed 
550*76404edcSAsim Jamshed 	if (con->request.uri->used == 1) {
551*76404edcSAsim Jamshed 		con->http_status = 400;
552*76404edcSAsim Jamshed 		con->response.keep_alive = 0;
553*76404edcSAsim Jamshed 		con->keep_alive = 0;
554*76404edcSAsim Jamshed 
555*76404edcSAsim Jamshed 		if (srv->srvconf.log_request_header_on_error) {
556*76404edcSAsim Jamshed 			log_error_write(srv, __FILE__, __LINE__, "s", "no uri specified -> 400");
557*76404edcSAsim Jamshed 			log_error_write(srv, __FILE__, __LINE__, "Sb",
558*76404edcSAsim Jamshed 							"request-header:\n",
559*76404edcSAsim Jamshed 							con->request.request);
560*76404edcSAsim Jamshed 		}
561*76404edcSAsim Jamshed 		return 0;
562*76404edcSAsim Jamshed 	}
563*76404edcSAsim Jamshed 
564*76404edcSAsim Jamshed 	if (reqline_host) {
565*76404edcSAsim Jamshed 		/* Insert as host header */
566*76404edcSAsim Jamshed 		data_string *ds;
567*76404edcSAsim Jamshed 
568*76404edcSAsim Jamshed 		if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
569*76404edcSAsim Jamshed 			ds = data_string_init();
570*76404edcSAsim Jamshed 		}
571*76404edcSAsim Jamshed 
572*76404edcSAsim Jamshed 		buffer_copy_string_len(ds->key, CONST_STR_LEN("Host"));
573*76404edcSAsim Jamshed 		buffer_copy_string_len(ds->value, reqline_host, reqline_hostlen);
574*76404edcSAsim Jamshed 		array_insert_unique(con->request.headers, (data_unset *)ds);
575*76404edcSAsim Jamshed 		con->request.http_host = ds->value;
576*76404edcSAsim Jamshed 	}
577*76404edcSAsim Jamshed 
578*76404edcSAsim Jamshed 	for (; i < con->parse_request->used && !done; i++) {
579*76404edcSAsim Jamshed 		char *cur = con->parse_request->ptr + i;
580*76404edcSAsim Jamshed 
581*76404edcSAsim Jamshed 		if (is_key) {
582*76404edcSAsim Jamshed 			size_t j;
583*76404edcSAsim Jamshed 			int got_colon = 0;
584*76404edcSAsim Jamshed 
585*76404edcSAsim Jamshed 			/**
586*76404edcSAsim Jamshed 			 * 1*<any CHAR except CTLs or separators>
587*76404edcSAsim Jamshed 			 * CTLs == 0-31 + 127
588*76404edcSAsim Jamshed 			 *
589*76404edcSAsim Jamshed 			 */
590*76404edcSAsim Jamshed 			switch(*cur) {
591*76404edcSAsim Jamshed 			case ':':
592*76404edcSAsim Jamshed 				is_key = 0;
593*76404edcSAsim Jamshed 
594*76404edcSAsim Jamshed 				value = cur + 1;
595*76404edcSAsim Jamshed 
596*76404edcSAsim Jamshed 				if (is_ws_after_key == 0) {
597*76404edcSAsim Jamshed 					key_len = i - first;
598*76404edcSAsim Jamshed 				}
599*76404edcSAsim Jamshed 				is_ws_after_key = 0;
600*76404edcSAsim Jamshed 
601*76404edcSAsim Jamshed 				break;
602*76404edcSAsim Jamshed 			case '(':
603*76404edcSAsim Jamshed 			case ')':
604*76404edcSAsim Jamshed 			case '<':
605*76404edcSAsim Jamshed 			case '>':
606*76404edcSAsim Jamshed 			case '@':
607*76404edcSAsim Jamshed 			case ',':
608*76404edcSAsim Jamshed 			case ';':
609*76404edcSAsim Jamshed 			case '\\':
610*76404edcSAsim Jamshed 			case '\"':
611*76404edcSAsim Jamshed 			case '/':
612*76404edcSAsim Jamshed 			case '[':
613*76404edcSAsim Jamshed 			case ']':
614*76404edcSAsim Jamshed 			case '?':
615*76404edcSAsim Jamshed 			case '=':
616*76404edcSAsim Jamshed 			case '{':
617*76404edcSAsim Jamshed 			case '}':
618*76404edcSAsim Jamshed 				con->http_status = 400;
619*76404edcSAsim Jamshed 				con->keep_alive = 0;
620*76404edcSAsim Jamshed 				con->response.keep_alive = 0;
621*76404edcSAsim Jamshed 
622*76404edcSAsim Jamshed 				log_error_write(srv, __FILE__, __LINE__, "sbsds",
623*76404edcSAsim Jamshed 						"invalid character in key", con->request.request, cur, *cur, "-> 400");
624*76404edcSAsim Jamshed 				return 0;
625*76404edcSAsim Jamshed 			case ' ':
626*76404edcSAsim Jamshed 			case '\t':
627*76404edcSAsim Jamshed 				if (i == first) {
628*76404edcSAsim Jamshed 					is_key = 0;
629*76404edcSAsim Jamshed 					in_folding = 1;
630*76404edcSAsim Jamshed 					value = cur;
631*76404edcSAsim Jamshed 
632*76404edcSAsim Jamshed 					break;
633*76404edcSAsim Jamshed 				}
634*76404edcSAsim Jamshed 
635*76404edcSAsim Jamshed 
636*76404edcSAsim Jamshed 				key_len = i - first;
637*76404edcSAsim Jamshed 
638*76404edcSAsim Jamshed 				/* skip every thing up to the : */
639*76404edcSAsim Jamshed 				for (j = 1; !got_colon; j++) {
640*76404edcSAsim Jamshed 					switch(con->parse_request->ptr[j + i]) {
641*76404edcSAsim Jamshed 					case ' ':
642*76404edcSAsim Jamshed 					case '\t':
643*76404edcSAsim Jamshed 						/* skip WS */
644*76404edcSAsim Jamshed 						continue;
645*76404edcSAsim Jamshed 					case ':':
646*76404edcSAsim Jamshed 						/* ok, done; handle the colon the usual way */
647*76404edcSAsim Jamshed 
648*76404edcSAsim Jamshed 						i += j - 1;
649*76404edcSAsim Jamshed 						got_colon = 1;
650*76404edcSAsim Jamshed 						is_ws_after_key = 1; /* we already know the key length */
651*76404edcSAsim Jamshed 
652*76404edcSAsim Jamshed 						break;
653*76404edcSAsim Jamshed 					default:
654*76404edcSAsim Jamshed 						/* error */
655*76404edcSAsim Jamshed 
656*76404edcSAsim Jamshed 						if (srv->srvconf.log_request_header_on_error) {
657*76404edcSAsim Jamshed 							log_error_write(srv, __FILE__, __LINE__, "s", "WS character in key -> 400");
658*76404edcSAsim Jamshed 							log_error_write(srv, __FILE__, __LINE__, "Sb",
659*76404edcSAsim Jamshed 								"request-header:\n",
660*76404edcSAsim Jamshed 								con->request.request);
661*76404edcSAsim Jamshed 						}
662*76404edcSAsim Jamshed 
663*76404edcSAsim Jamshed 						con->http_status = 400;
664*76404edcSAsim Jamshed 						con->response.keep_alive = 0;
665*76404edcSAsim Jamshed 						con->keep_alive = 0;
666*76404edcSAsim Jamshed 
667*76404edcSAsim Jamshed 						return 0;
668*76404edcSAsim Jamshed 					}
669*76404edcSAsim Jamshed 				}
670*76404edcSAsim Jamshed 
671*76404edcSAsim Jamshed 				break;
672*76404edcSAsim Jamshed 			case '\r':
673*76404edcSAsim Jamshed 				if (con->parse_request->ptr[i+1] == '\n' && i == first) {
674*76404edcSAsim Jamshed 					/* End of Header */
675*76404edcSAsim Jamshed 					con->parse_request->ptr[i] = '\0';
676*76404edcSAsim Jamshed 					con->parse_request->ptr[i+1] = '\0';
677*76404edcSAsim Jamshed 
678*76404edcSAsim Jamshed 					i++;
679*76404edcSAsim Jamshed 
680*76404edcSAsim Jamshed 					done = 1;
681*76404edcSAsim Jamshed 
682*76404edcSAsim Jamshed 					break;
683*76404edcSAsim Jamshed 				} else {
684*76404edcSAsim Jamshed 					if (srv->srvconf.log_request_header_on_error) {
685*76404edcSAsim Jamshed 						log_error_write(srv, __FILE__, __LINE__, "s", "CR without LF -> 400");
686*76404edcSAsim Jamshed 						log_error_write(srv, __FILE__, __LINE__, "Sb",
687*76404edcSAsim Jamshed 							"request-header:\n",
688*76404edcSAsim Jamshed 							con->request.request);
689*76404edcSAsim Jamshed 					}
690*76404edcSAsim Jamshed 
691*76404edcSAsim Jamshed 					con->http_status = 400;
692*76404edcSAsim Jamshed 					con->keep_alive = 0;
693*76404edcSAsim Jamshed 					con->response.keep_alive = 0;
694*76404edcSAsim Jamshed 					return 0;
695*76404edcSAsim Jamshed 				}
696*76404edcSAsim Jamshed 				/* fall thru */
697*76404edcSAsim Jamshed 			case 0: /* illegal characters (faster than a if () :) */
698*76404edcSAsim Jamshed 			case 1:
699*76404edcSAsim Jamshed 			case 2:
700*76404edcSAsim Jamshed 			case 3:
701*76404edcSAsim Jamshed 			case 4:
702*76404edcSAsim Jamshed 			case 5:
703*76404edcSAsim Jamshed 			case 6:
704*76404edcSAsim Jamshed 			case 7:
705*76404edcSAsim Jamshed 			case 8:
706*76404edcSAsim Jamshed 			case 10:
707*76404edcSAsim Jamshed 			case 11:
708*76404edcSAsim Jamshed 			case 12:
709*76404edcSAsim Jamshed 			case 14:
710*76404edcSAsim Jamshed 			case 15:
711*76404edcSAsim Jamshed 			case 16:
712*76404edcSAsim Jamshed 			case 17:
713*76404edcSAsim Jamshed 			case 18:
714*76404edcSAsim Jamshed 			case 19:
715*76404edcSAsim Jamshed 			case 20:
716*76404edcSAsim Jamshed 			case 21:
717*76404edcSAsim Jamshed 			case 22:
718*76404edcSAsim Jamshed 			case 23:
719*76404edcSAsim Jamshed 			case 24:
720*76404edcSAsim Jamshed 			case 25:
721*76404edcSAsim Jamshed 			case 26:
722*76404edcSAsim Jamshed 			case 27:
723*76404edcSAsim Jamshed 			case 28:
724*76404edcSAsim Jamshed 			case 29:
725*76404edcSAsim Jamshed 			case 30:
726*76404edcSAsim Jamshed 			case 31:
727*76404edcSAsim Jamshed 			case 127:
728*76404edcSAsim Jamshed 				con->http_status = 400;
729*76404edcSAsim Jamshed 				con->keep_alive = 0;
730*76404edcSAsim Jamshed 				con->response.keep_alive = 0;
731*76404edcSAsim Jamshed 
732*76404edcSAsim Jamshed 				if (srv->srvconf.log_request_header_on_error) {
733*76404edcSAsim Jamshed 					log_error_write(srv, __FILE__, __LINE__, "sbsds",
734*76404edcSAsim Jamshed 						"CTL character in key", con->request.request, cur, *cur, "-> 400");
735*76404edcSAsim Jamshed 
736*76404edcSAsim Jamshed 					log_error_write(srv, __FILE__, __LINE__, "Sb",
737*76404edcSAsim Jamshed 						"request-header:\n",
738*76404edcSAsim Jamshed 						con->request.request);
739*76404edcSAsim Jamshed 				}
740*76404edcSAsim Jamshed 
741*76404edcSAsim Jamshed 				return 0;
742*76404edcSAsim Jamshed 			default:
743*76404edcSAsim Jamshed 				/* ok */
744*76404edcSAsim Jamshed 				break;
745*76404edcSAsim Jamshed 			}
746*76404edcSAsim Jamshed 		} else {
747*76404edcSAsim Jamshed 			switch(*cur) {
748*76404edcSAsim Jamshed 			case '\r':
749*76404edcSAsim Jamshed 				if (con->parse_request->ptr[i+1] == '\n') {
750*76404edcSAsim Jamshed 					data_string *ds = NULL;
751*76404edcSAsim Jamshed 
752*76404edcSAsim Jamshed 					/* End of Headerline */
753*76404edcSAsim Jamshed 					con->parse_request->ptr[i] = '\0';
754*76404edcSAsim Jamshed 					con->parse_request->ptr[i+1] = '\0';
755*76404edcSAsim Jamshed 
756*76404edcSAsim Jamshed 					if (in_folding) {
757*76404edcSAsim Jamshed 						buffer *key_b;
758*76404edcSAsim Jamshed 						/**
759*76404edcSAsim Jamshed 						 * we use a evil hack to handle the line-folding
760*76404edcSAsim Jamshed 						 *
761*76404edcSAsim Jamshed 						 * As array_insert_unique() deletes 'ds' in the case of a duplicate
762*76404edcSAsim Jamshed 						 * ds points somewhere and we get a evil crash. As a solution we keep the old
763*76404edcSAsim Jamshed 						 * "key" and get the current value from the hash and append us
764*76404edcSAsim Jamshed 						 *
765*76404edcSAsim Jamshed 						 * */
766*76404edcSAsim Jamshed 
767*76404edcSAsim Jamshed 						if (!key || !key_len) {
768*76404edcSAsim Jamshed 							/* 400 */
769*76404edcSAsim Jamshed 
770*76404edcSAsim Jamshed 							if (srv->srvconf.log_request_header_on_error) {
771*76404edcSAsim Jamshed 								log_error_write(srv, __FILE__, __LINE__, "s", "WS at the start of first line -> 400");
772*76404edcSAsim Jamshed 
773*76404edcSAsim Jamshed 								log_error_write(srv, __FILE__, __LINE__, "Sb",
774*76404edcSAsim Jamshed 									"request-header:\n",
775*76404edcSAsim Jamshed 									con->request.request);
776*76404edcSAsim Jamshed 							}
777*76404edcSAsim Jamshed 
778*76404edcSAsim Jamshed 
779*76404edcSAsim Jamshed 							con->http_status = 400;
780*76404edcSAsim Jamshed 							con->keep_alive = 0;
781*76404edcSAsim Jamshed 							con->response.keep_alive = 0;
782*76404edcSAsim Jamshed 							return 0;
783*76404edcSAsim Jamshed 						}
784*76404edcSAsim Jamshed 
785*76404edcSAsim Jamshed 						key_b = buffer_init();
786*76404edcSAsim Jamshed 						buffer_copy_string_len(key_b, key, key_len);
787*76404edcSAsim Jamshed 
788*76404edcSAsim Jamshed 						if (NULL != (ds = (data_string *)array_get_element(con->request.headers, key_b->ptr))) {
789*76404edcSAsim Jamshed 							buffer_append_string(ds->value, value);
790*76404edcSAsim Jamshed 						}
791*76404edcSAsim Jamshed 
792*76404edcSAsim Jamshed 						buffer_free(key_b);
793*76404edcSAsim Jamshed 					} else {
794*76404edcSAsim Jamshed 						int s_len;
795*76404edcSAsim Jamshed 						key = con->parse_request->ptr + first;
796*76404edcSAsim Jamshed 
797*76404edcSAsim Jamshed 						s_len = cur - value;
798*76404edcSAsim Jamshed 
799*76404edcSAsim Jamshed 						/* strip trailing white-spaces */
800*76404edcSAsim Jamshed 						for (; s_len > 0 &&
801*76404edcSAsim Jamshed 								(value[s_len - 1] == ' ' ||
802*76404edcSAsim Jamshed 								 value[s_len - 1] == '\t'); s_len--);
803*76404edcSAsim Jamshed 
804*76404edcSAsim Jamshed 						value[s_len] = '\0';
805*76404edcSAsim Jamshed 
806*76404edcSAsim Jamshed 						if (s_len > 0) {
807*76404edcSAsim Jamshed 							int cmp = 0;
808*76404edcSAsim Jamshed 							if (NULL == (ds = (data_string *)array_get_unused_element(con->request.headers, TYPE_STRING))) {
809*76404edcSAsim Jamshed 								ds = data_string_init();
810*76404edcSAsim Jamshed 							}
811*76404edcSAsim Jamshed 							buffer_copy_string_len(ds->key, key, key_len);
812*76404edcSAsim Jamshed 							buffer_copy_string_len(ds->value, value, s_len);
813*76404edcSAsim Jamshed 
814*76404edcSAsim Jamshed 							/* retreive values
815*76404edcSAsim Jamshed 							 *
816*76404edcSAsim Jamshed 							 *
817*76404edcSAsim Jamshed 							 * the list of options is sorted to simplify the search
818*76404edcSAsim Jamshed 							 */
819*76404edcSAsim Jamshed 
820*76404edcSAsim Jamshed 							if (0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Connection")))) {
821*76404edcSAsim Jamshed 								array *vals;
822*76404edcSAsim Jamshed 								size_t vi;
823*76404edcSAsim Jamshed 
824*76404edcSAsim Jamshed 								/* split on , */
825*76404edcSAsim Jamshed 
826*76404edcSAsim Jamshed 								vals = srv->split_vals;
827*76404edcSAsim Jamshed 
828*76404edcSAsim Jamshed 								array_reset(vals);
829*76404edcSAsim Jamshed 
830*76404edcSAsim Jamshed 								http_request_split_value(vals, ds->value);
831*76404edcSAsim Jamshed 
832*76404edcSAsim Jamshed 								for (vi = 0; vi < vals->used; vi++) {
833*76404edcSAsim Jamshed 									data_string *dsv = (data_string *)vals->data[vi];
834*76404edcSAsim Jamshed 
835*76404edcSAsim Jamshed 									if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("keep-alive"))) {
836*76404edcSAsim Jamshed 										keep_alive_set = HTTP_CONNECTION_KEEPALIVE;
837*76404edcSAsim Jamshed 
838*76404edcSAsim Jamshed 										break;
839*76404edcSAsim Jamshed 									} else if (0 == buffer_caseless_compare(CONST_BUF_LEN(dsv->value), CONST_STR_LEN("close"))) {
840*76404edcSAsim Jamshed 										keep_alive_set = HTTP_CONNECTION_CLOSE;
841*76404edcSAsim Jamshed 
842*76404edcSAsim Jamshed 										break;
843*76404edcSAsim Jamshed 									}
844*76404edcSAsim Jamshed 								}
845*76404edcSAsim Jamshed 
846*76404edcSAsim Jamshed 							} else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Length")))) {
847*76404edcSAsim Jamshed 								char *err;
848*76404edcSAsim Jamshed 								unsigned long int r;
849*76404edcSAsim Jamshed 								size_t j;
850*76404edcSAsim Jamshed 
851*76404edcSAsim Jamshed 								if (con_length_set) {
852*76404edcSAsim Jamshed 									con->http_status = 400;
853*76404edcSAsim Jamshed 									con->keep_alive = 0;
854*76404edcSAsim Jamshed 
855*76404edcSAsim Jamshed 									if (srv->srvconf.log_request_header_on_error) {
856*76404edcSAsim Jamshed 										log_error_write(srv, __FILE__, __LINE__, "s",
857*76404edcSAsim Jamshed 												"duplicate Content-Length-header -> 400");
858*76404edcSAsim Jamshed 										log_error_write(srv, __FILE__, __LINE__, "Sb",
859*76404edcSAsim Jamshed 												"request-header:\n",
860*76404edcSAsim Jamshed 												con->request.request);
861*76404edcSAsim Jamshed 									}
862*76404edcSAsim Jamshed 									array_insert_unique(con->request.headers, (data_unset *)ds);
863*76404edcSAsim Jamshed 									return 0;
864*76404edcSAsim Jamshed 								}
865*76404edcSAsim Jamshed 
866*76404edcSAsim Jamshed 								if (ds->value->used == 0) SEGFAULT();
867*76404edcSAsim Jamshed 
868*76404edcSAsim Jamshed 								for (j = 0; j < ds->value->used - 1; j++) {
869*76404edcSAsim Jamshed 									char c = ds->value->ptr[j];
870*76404edcSAsim Jamshed 									if (!isdigit((unsigned char)c)) {
871*76404edcSAsim Jamshed 										log_error_write(srv, __FILE__, __LINE__, "sbs",
872*76404edcSAsim Jamshed 												"content-length broken:", ds->value, "-> 400");
873*76404edcSAsim Jamshed 
874*76404edcSAsim Jamshed 										con->http_status = 400;
875*76404edcSAsim Jamshed 										con->keep_alive = 0;
876*76404edcSAsim Jamshed 
877*76404edcSAsim Jamshed 										array_insert_unique(con->request.headers, (data_unset *)ds);
878*76404edcSAsim Jamshed 										return 0;
879*76404edcSAsim Jamshed 									}
880*76404edcSAsim Jamshed 								}
881*76404edcSAsim Jamshed 
882*76404edcSAsim Jamshed 								r = strtoul(ds->value->ptr, &err, 10);
883*76404edcSAsim Jamshed 
884*76404edcSAsim Jamshed 								if (*err == '\0') {
885*76404edcSAsim Jamshed 									con_length_set = 1;
886*76404edcSAsim Jamshed 									con->request.content_length = r;
887*76404edcSAsim Jamshed 								} else {
888*76404edcSAsim Jamshed 									log_error_write(srv, __FILE__, __LINE__, "sbs",
889*76404edcSAsim Jamshed 											"content-length broken:", ds->value, "-> 400");
890*76404edcSAsim Jamshed 
891*76404edcSAsim Jamshed 									con->http_status = 400;
892*76404edcSAsim Jamshed 									con->keep_alive = 0;
893*76404edcSAsim Jamshed 
894*76404edcSAsim Jamshed 									array_insert_unique(con->request.headers, (data_unset *)ds);
895*76404edcSAsim Jamshed 									return 0;
896*76404edcSAsim Jamshed 								}
897*76404edcSAsim Jamshed 							} else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Content-Type")))) {
898*76404edcSAsim Jamshed 								/* if dup, only the first one will survive */
899*76404edcSAsim Jamshed 								if (!con->request.http_content_type) {
900*76404edcSAsim Jamshed 									con->request.http_content_type = ds->value->ptr;
901*76404edcSAsim Jamshed 								} else {
902*76404edcSAsim Jamshed 									con->http_status = 400;
903*76404edcSAsim Jamshed 									con->keep_alive = 0;
904*76404edcSAsim Jamshed 
905*76404edcSAsim Jamshed 									if (srv->srvconf.log_request_header_on_error) {
906*76404edcSAsim Jamshed 										log_error_write(srv, __FILE__, __LINE__, "s",
907*76404edcSAsim Jamshed 												"duplicate Content-Type-header -> 400");
908*76404edcSAsim Jamshed 										log_error_write(srv, __FILE__, __LINE__, "Sb",
909*76404edcSAsim Jamshed 												"request-header:\n",
910*76404edcSAsim Jamshed 												con->request.request);
911*76404edcSAsim Jamshed 									}
912*76404edcSAsim Jamshed 									array_insert_unique(con->request.headers, (data_unset *)ds);
913*76404edcSAsim Jamshed 									return 0;
914*76404edcSAsim Jamshed 								}
915*76404edcSAsim Jamshed 							} else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Expect")))) {
916*76404edcSAsim Jamshed 								/* HTTP 2616 8.2.3
917*76404edcSAsim Jamshed 								 * Expect: 100-continue
918*76404edcSAsim Jamshed 								 *
919*76404edcSAsim Jamshed 								 *   -> (10.1.1)  100 (read content, process request, send final status-code)
920*76404edcSAsim Jamshed 								 *   -> (10.4.18) 417 (close)
921*76404edcSAsim Jamshed 								 *
922*76404edcSAsim Jamshed 								 * (not handled at all yet, we always send 417 here)
923*76404edcSAsim Jamshed 								 *
924*76404edcSAsim Jamshed 								 * What has to be added ?
925*76404edcSAsim Jamshed 								 * 1. handling of chunked request body
926*76404edcSAsim Jamshed 								 * 2. out-of-order sending from the HTTP/1.1 100 Continue
927*76404edcSAsim Jamshed 								 *    header
928*76404edcSAsim Jamshed 								 *
929*76404edcSAsim Jamshed 								 */
930*76404edcSAsim Jamshed 
931*76404edcSAsim Jamshed 								if (srv->srvconf.reject_expect_100_with_417 && 0 == buffer_caseless_compare(CONST_BUF_LEN(ds->value), CONST_STR_LEN("100-continue"))) {
932*76404edcSAsim Jamshed 									con->http_status = 417;
933*76404edcSAsim Jamshed 									con->keep_alive = 0;
934*76404edcSAsim Jamshed 									array_insert_unique(con->request.headers, (data_unset *)ds);
935*76404edcSAsim Jamshed 									return 0;
936*76404edcSAsim Jamshed 								}
937*76404edcSAsim Jamshed 							} else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Host")))) {
938*76404edcSAsim Jamshed 								if (reqline_host) {
939*76404edcSAsim Jamshed 									/* ignore all host: headers as we got the host in the request line */
940*76404edcSAsim Jamshed 									ds->free((data_unset*) ds);
941*76404edcSAsim Jamshed 									ds = NULL;
942*76404edcSAsim Jamshed 								} else if (!con->request.http_host) {
943*76404edcSAsim Jamshed 									con->request.http_host = ds->value;
944*76404edcSAsim Jamshed 								} else {
945*76404edcSAsim Jamshed 									con->http_status = 400;
946*76404edcSAsim Jamshed 									con->keep_alive = 0;
947*76404edcSAsim Jamshed 
948*76404edcSAsim Jamshed 									if (srv->srvconf.log_request_header_on_error) {
949*76404edcSAsim Jamshed 										log_error_write(srv, __FILE__, __LINE__, "s",
950*76404edcSAsim Jamshed 												"duplicate Host-header -> 400");
951*76404edcSAsim Jamshed 										log_error_write(srv, __FILE__, __LINE__, "Sb",
952*76404edcSAsim Jamshed 												"request-header:\n",
953*76404edcSAsim Jamshed 												con->request.request);
954*76404edcSAsim Jamshed 									}
955*76404edcSAsim Jamshed 									array_insert_unique(con->request.headers, (data_unset *)ds);
956*76404edcSAsim Jamshed 									return 0;
957*76404edcSAsim Jamshed 								}
958*76404edcSAsim Jamshed 							} else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-Modified-Since")))) {
959*76404edcSAsim Jamshed 								/* Proxies sometimes send dup headers
960*76404edcSAsim Jamshed 								 * if they are the same we ignore the second
961*76404edcSAsim Jamshed 								 * if not, we raise an error */
962*76404edcSAsim Jamshed 								if (!con->request.http_if_modified_since) {
963*76404edcSAsim Jamshed 									con->request.http_if_modified_since = ds->value->ptr;
964*76404edcSAsim Jamshed 								} else if (0 == strcasecmp(con->request.http_if_modified_since,
965*76404edcSAsim Jamshed 											ds->value->ptr)) {
966*76404edcSAsim Jamshed 									/* ignore it if they are the same */
967*76404edcSAsim Jamshed 
968*76404edcSAsim Jamshed 									ds->free((data_unset *)ds);
969*76404edcSAsim Jamshed 									ds = NULL;
970*76404edcSAsim Jamshed 								} else {
971*76404edcSAsim Jamshed 									con->http_status = 400;
972*76404edcSAsim Jamshed 									con->keep_alive = 0;
973*76404edcSAsim Jamshed 
974*76404edcSAsim Jamshed 									if (srv->srvconf.log_request_header_on_error) {
975*76404edcSAsim Jamshed 										log_error_write(srv, __FILE__, __LINE__, "s",
976*76404edcSAsim Jamshed 												"duplicate If-Modified-Since header -> 400");
977*76404edcSAsim Jamshed 										log_error_write(srv, __FILE__, __LINE__, "Sb",
978*76404edcSAsim Jamshed 												"request-header:\n",
979*76404edcSAsim Jamshed 												con->request.request);
980*76404edcSAsim Jamshed 									}
981*76404edcSAsim Jamshed 									array_insert_unique(con->request.headers, (data_unset *)ds);
982*76404edcSAsim Jamshed 									return 0;
983*76404edcSAsim Jamshed 								}
984*76404edcSAsim Jamshed 							} else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("If-None-Match")))) {
985*76404edcSAsim Jamshed 								/* if dup, only the first one will survive */
986*76404edcSAsim Jamshed 								if (!con->request.http_if_none_match) {
987*76404edcSAsim Jamshed 									con->request.http_if_none_match = ds->value->ptr;
988*76404edcSAsim Jamshed 								} else {
989*76404edcSAsim Jamshed 									ds->free((data_unset*) ds);
990*76404edcSAsim Jamshed 									ds = NULL;
991*76404edcSAsim Jamshed 								}
992*76404edcSAsim Jamshed 							} else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Range")))) {
993*76404edcSAsim Jamshed 								if (!con->request.http_range) {
994*76404edcSAsim Jamshed 									/* bytes=.*-.* */
995*76404edcSAsim Jamshed 
996*76404edcSAsim Jamshed 									if (0 == strncasecmp(ds->value->ptr, "bytes=", 6) &&
997*76404edcSAsim Jamshed 									    NULL != strchr(ds->value->ptr+6, '-')) {
998*76404edcSAsim Jamshed 
999*76404edcSAsim Jamshed 										/* if dup, only the first one will survive */
1000*76404edcSAsim Jamshed 										con->request.http_range = ds->value->ptr + 6;
1001*76404edcSAsim Jamshed 									}
1002*76404edcSAsim Jamshed 								} else {
1003*76404edcSAsim Jamshed 									con->http_status = 400;
1004*76404edcSAsim Jamshed 									con->keep_alive = 0;
1005*76404edcSAsim Jamshed 
1006*76404edcSAsim Jamshed 									if (srv->srvconf.log_request_header_on_error) {
1007*76404edcSAsim Jamshed 										log_error_write(srv, __FILE__, __LINE__, "s",
1008*76404edcSAsim Jamshed 												"duplicate Range-header -> 400");
1009*76404edcSAsim Jamshed 										log_error_write(srv, __FILE__, __LINE__, "Sb",
1010*76404edcSAsim Jamshed 												"request-header:\n",
1011*76404edcSAsim Jamshed 												con->request.request);
1012*76404edcSAsim Jamshed 									}
1013*76404edcSAsim Jamshed 									array_insert_unique(con->request.headers, (data_unset *)ds);
1014*76404edcSAsim Jamshed 									return 0;
1015*76404edcSAsim Jamshed 								}
1016*76404edcSAsim Jamshed 							}
1017*76404edcSAsim Jamshed 
1018*76404edcSAsim Jamshed 							if (ds) array_insert_unique(con->request.headers, (data_unset *)ds);
1019*76404edcSAsim Jamshed 						} else {
1020*76404edcSAsim Jamshed 							/* empty header-fields are not allowed by HTTP-RFC, we just ignore them */
1021*76404edcSAsim Jamshed 						}
1022*76404edcSAsim Jamshed 					}
1023*76404edcSAsim Jamshed 
1024*76404edcSAsim Jamshed 					i++;
1025*76404edcSAsim Jamshed 					first = i+1;
1026*76404edcSAsim Jamshed 					is_key = 1;
1027*76404edcSAsim Jamshed 					value = NULL;
1028*76404edcSAsim Jamshed #if 0
1029*76404edcSAsim Jamshed 					/**
1030*76404edcSAsim Jamshed 					 * for Bug 1230 keep the key_len a live
1031*76404edcSAsim Jamshed 					 */
1032*76404edcSAsim Jamshed 					key_len = 0;
1033*76404edcSAsim Jamshed #endif
1034*76404edcSAsim Jamshed 					in_folding = 0;
1035*76404edcSAsim Jamshed 				} else {
1036*76404edcSAsim Jamshed 					if (srv->srvconf.log_request_header_on_error) {
1037*76404edcSAsim Jamshed 						log_error_write(srv, __FILE__, __LINE__, "sbs",
1038*76404edcSAsim Jamshed 								"CR without LF", con->request.request, "-> 400");
1039*76404edcSAsim Jamshed 					}
1040*76404edcSAsim Jamshed 
1041*76404edcSAsim Jamshed 					con->http_status = 400;
1042*76404edcSAsim Jamshed 					con->keep_alive = 0;
1043*76404edcSAsim Jamshed 					con->response.keep_alive = 0;
1044*76404edcSAsim Jamshed 					return 0;
1045*76404edcSAsim Jamshed 				}
1046*76404edcSAsim Jamshed 				break;
1047*76404edcSAsim Jamshed 			case ' ':
1048*76404edcSAsim Jamshed 			case '\t':
1049*76404edcSAsim Jamshed 				/* strip leading WS */
1050*76404edcSAsim Jamshed 				if (value == cur) value = cur+1;
1051*76404edcSAsim Jamshed 			default:
1052*76404edcSAsim Jamshed 				if (*cur >= 0 && *cur < 32 && *cur != '\t') {
1053*76404edcSAsim Jamshed 					if (srv->srvconf.log_request_header_on_error) {
1054*76404edcSAsim Jamshed 						log_error_write(srv, __FILE__, __LINE__, "sds",
1055*76404edcSAsim Jamshed 								"invalid char in header", (int)*cur, "-> 400");
1056*76404edcSAsim Jamshed 					}
1057*76404edcSAsim Jamshed 
1058*76404edcSAsim Jamshed 					con->http_status = 400;
1059*76404edcSAsim Jamshed 					con->keep_alive = 0;
1060*76404edcSAsim Jamshed 
1061*76404edcSAsim Jamshed 					return 0;
1062*76404edcSAsim Jamshed 				}
1063*76404edcSAsim Jamshed 				break;
1064*76404edcSAsim Jamshed 			}
1065*76404edcSAsim Jamshed 		}
1066*76404edcSAsim Jamshed 	}
1067*76404edcSAsim Jamshed 
1068*76404edcSAsim Jamshed 	con->header_len = i;
1069*76404edcSAsim Jamshed 
1070*76404edcSAsim Jamshed 	/* do some post-processing */
1071*76404edcSAsim Jamshed 
1072*76404edcSAsim Jamshed 	if (con->request.http_version == HTTP_VERSION_1_1) {
1073*76404edcSAsim Jamshed 		if (keep_alive_set != HTTP_CONNECTION_CLOSE) {
1074*76404edcSAsim Jamshed 			/* no Connection-Header sent */
1075*76404edcSAsim Jamshed 
1076*76404edcSAsim Jamshed 			/* HTTP/1.1 -> keep-alive default TRUE */
1077*76404edcSAsim Jamshed 			con->keep_alive = 1;
1078*76404edcSAsim Jamshed 		} else {
1079*76404edcSAsim Jamshed 			con->keep_alive = 0;
1080*76404edcSAsim Jamshed 		}
1081*76404edcSAsim Jamshed 
1082*76404edcSAsim Jamshed 		/* RFC 2616, 14.23 */
1083*76404edcSAsim Jamshed 		if (con->request.http_host == NULL ||
1084*76404edcSAsim Jamshed 		    buffer_is_empty(con->request.http_host)) {
1085*76404edcSAsim Jamshed 			con->http_status = 400;
1086*76404edcSAsim Jamshed 			con->response.keep_alive = 0;
1087*76404edcSAsim Jamshed 			con->keep_alive = 0;
1088*76404edcSAsim Jamshed 
1089*76404edcSAsim Jamshed 			if (srv->srvconf.log_request_header_on_error) {
1090*76404edcSAsim Jamshed 				log_error_write(srv, __FILE__, __LINE__, "s", "HTTP/1.1 but Host missing -> 400");
1091*76404edcSAsim Jamshed 				log_error_write(srv, __FILE__, __LINE__, "Sb",
1092*76404edcSAsim Jamshed 						"request-header:\n",
1093*76404edcSAsim Jamshed 						con->request.request);
1094*76404edcSAsim Jamshed 			}
1095*76404edcSAsim Jamshed 			return 0;
1096*76404edcSAsim Jamshed 		}
1097*76404edcSAsim Jamshed 	} else {
1098*76404edcSAsim Jamshed 		if (keep_alive_set == HTTP_CONNECTION_KEEPALIVE) {
1099*76404edcSAsim Jamshed 			/* no Connection-Header sent */
1100*76404edcSAsim Jamshed 
1101*76404edcSAsim Jamshed 			/* HTTP/1.0 -> keep-alive default FALSE  */
1102*76404edcSAsim Jamshed 			con->keep_alive = 1;
1103*76404edcSAsim Jamshed 		} else {
1104*76404edcSAsim Jamshed 			con->keep_alive = 0;
1105*76404edcSAsim Jamshed 		}
1106*76404edcSAsim Jamshed 	}
1107*76404edcSAsim Jamshed 
1108*76404edcSAsim Jamshed 	/* check hostname field if it is set */
1109*76404edcSAsim Jamshed 	if (NULL != con->request.http_host &&
1110*76404edcSAsim Jamshed 	    0 != request_check_hostname(srv, con, con->request.http_host)) {
1111*76404edcSAsim Jamshed 
1112*76404edcSAsim Jamshed 		if (srv->srvconf.log_request_header_on_error) {
1113*76404edcSAsim Jamshed 			log_error_write(srv, __FILE__, __LINE__, "s",
1114*76404edcSAsim Jamshed 					"Invalid Hostname -> 400");
1115*76404edcSAsim Jamshed 			log_error_write(srv, __FILE__, __LINE__, "Sb",
1116*76404edcSAsim Jamshed 					"request-header:\n",
1117*76404edcSAsim Jamshed 					con->request.request);
1118*76404edcSAsim Jamshed 		}
1119*76404edcSAsim Jamshed 
1120*76404edcSAsim Jamshed 		con->http_status = 400;
1121*76404edcSAsim Jamshed 		con->response.keep_alive = 0;
1122*76404edcSAsim Jamshed 		con->keep_alive = 0;
1123*76404edcSAsim Jamshed 
1124*76404edcSAsim Jamshed 		return 0;
1125*76404edcSAsim Jamshed 	}
1126*76404edcSAsim Jamshed 
1127*76404edcSAsim Jamshed 	switch(con->request.http_method) {
1128*76404edcSAsim Jamshed 	case HTTP_METHOD_GET:
1129*76404edcSAsim Jamshed 	case HTTP_METHOD_HEAD:
1130*76404edcSAsim Jamshed 		/* content-length is forbidden for those */
1131*76404edcSAsim Jamshed 		if (con_length_set && con->request.content_length != 0) {
1132*76404edcSAsim Jamshed 			/* content-length is missing */
1133*76404edcSAsim Jamshed 			log_error_write(srv, __FILE__, __LINE__, "s",
1134*76404edcSAsim Jamshed 					"GET/HEAD with content-length -> 400");
1135*76404edcSAsim Jamshed 
1136*76404edcSAsim Jamshed 			con->keep_alive = 0;
1137*76404edcSAsim Jamshed 			con->http_status = 400;
1138*76404edcSAsim Jamshed 			return 0;
1139*76404edcSAsim Jamshed 		}
1140*76404edcSAsim Jamshed 		break;
1141*76404edcSAsim Jamshed 	case HTTP_METHOD_POST:
1142*76404edcSAsim Jamshed 		/* content-length is required for them */
1143*76404edcSAsim Jamshed 		if (!con_length_set) {
1144*76404edcSAsim Jamshed 			/* content-length is missing */
1145*76404edcSAsim Jamshed 			log_error_write(srv, __FILE__, __LINE__, "s",
1146*76404edcSAsim Jamshed 					"POST-request, but content-length missing -> 411");
1147*76404edcSAsim Jamshed 
1148*76404edcSAsim Jamshed 			con->keep_alive = 0;
1149*76404edcSAsim Jamshed 			con->http_status = 411;
1150*76404edcSAsim Jamshed 			return 0;
1151*76404edcSAsim Jamshed 
1152*76404edcSAsim Jamshed 		}
1153*76404edcSAsim Jamshed 		break;
1154*76404edcSAsim Jamshed 	default:
1155*76404edcSAsim Jamshed 		/* the may have a content-length */
1156*76404edcSAsim Jamshed 		break;
1157*76404edcSAsim Jamshed 	}
1158*76404edcSAsim Jamshed 
1159*76404edcSAsim Jamshed 
1160*76404edcSAsim Jamshed 	/* check if we have read post data */
1161*76404edcSAsim Jamshed 	if (con_length_set) {
1162*76404edcSAsim Jamshed 		/* don't handle more the SSIZE_MAX bytes in content-length */
1163*76404edcSAsim Jamshed 		if (con->request.content_length > SSIZE_MAX) {
1164*76404edcSAsim Jamshed 			con->http_status = 413;
1165*76404edcSAsim Jamshed 			con->keep_alive = 0;
1166*76404edcSAsim Jamshed 
1167*76404edcSAsim Jamshed 			log_error_write(srv, __FILE__, __LINE__, "sos",
1168*76404edcSAsim Jamshed 					"request-size too long:", (off_t) con->request.content_length, "-> 413");
1169*76404edcSAsim Jamshed 			return 0;
1170*76404edcSAsim Jamshed 		}
1171*76404edcSAsim Jamshed 
1172*76404edcSAsim Jamshed 		/* divide by 1024 as srvconf.max_request_size is in kBytes */
1173*76404edcSAsim Jamshed 		if (srv->srvconf.max_request_size != 0 &&
1174*76404edcSAsim Jamshed 		    (con->request.content_length >> 10) > srv->srvconf.max_request_size) {
1175*76404edcSAsim Jamshed 			/* the request body itself is larger then
1176*76404edcSAsim Jamshed 			 * our our max_request_size
1177*76404edcSAsim Jamshed 			 */
1178*76404edcSAsim Jamshed 
1179*76404edcSAsim Jamshed 			con->http_status = 413;
1180*76404edcSAsim Jamshed 			con->keep_alive = 0;
1181*76404edcSAsim Jamshed 
1182*76404edcSAsim Jamshed 			log_error_write(srv, __FILE__, __LINE__, "sos",
1183*76404edcSAsim Jamshed 					"request-size too long:", (off_t) con->request.content_length, "-> 413");
1184*76404edcSAsim Jamshed 			return 0;
1185*76404edcSAsim Jamshed 		}
1186*76404edcSAsim Jamshed 
1187*76404edcSAsim Jamshed 
1188*76404edcSAsim Jamshed 		/* we have content */
1189*76404edcSAsim Jamshed 		if (con->request.content_length != 0) {
1190*76404edcSAsim Jamshed 			return 1;
1191*76404edcSAsim Jamshed 		}
1192*76404edcSAsim Jamshed 	}
1193*76404edcSAsim Jamshed 
1194*76404edcSAsim Jamshed 	return 0;
1195*76404edcSAsim Jamshed }
1196*76404edcSAsim Jamshed 
http_request_header_finished(server * srv,connection * con)1197*76404edcSAsim Jamshed int http_request_header_finished(server *srv, connection *con) {
1198*76404edcSAsim Jamshed 	UNUSED(srv);
1199*76404edcSAsim Jamshed 
1200*76404edcSAsim Jamshed 	if (con->request.request->used < 5) return 0;
1201*76404edcSAsim Jamshed 
1202*76404edcSAsim Jamshed 	if (0 == memcmp(con->request.request->ptr + con->request.request->used - 5, "\r\n\r\n", 4)) return 1;
1203*76404edcSAsim Jamshed 	if (NULL != strstr(con->request.request->ptr, "\r\n\r\n")) return 1;
1204*76404edcSAsim Jamshed 
1205*76404edcSAsim Jamshed 	return 0;
1206*76404edcSAsim Jamshed }
1207