1 |
|
|
/* $OpenBSD: server_http.c,v 1.117 2017/05/15 10:40:47 jsg Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2006 - 2017 Reyk Floeter <reyk@openbsd.org> |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
#include <sys/types.h> |
20 |
|
|
#include <sys/queue.h> |
21 |
|
|
#include <sys/socket.h> |
22 |
|
|
#include <sys/tree.h> |
23 |
|
|
|
24 |
|
|
#include <netinet/in.h> |
25 |
|
|
#include <arpa/inet.h> |
26 |
|
|
|
27 |
|
|
#include <errno.h> |
28 |
|
|
#include <stdlib.h> |
29 |
|
|
#include <string.h> |
30 |
|
|
#include <unistd.h> |
31 |
|
|
#include <limits.h> |
32 |
|
|
#include <fnmatch.h> |
33 |
|
|
#include <stdio.h> |
34 |
|
|
#include <time.h> |
35 |
|
|
#include <resolv.h> |
36 |
|
|
#include <event.h> |
37 |
|
|
#include <ctype.h> |
38 |
|
|
#include <vis.h> |
39 |
|
|
|
40 |
|
|
#include "httpd.h" |
41 |
|
|
#include "http.h" |
42 |
|
|
#include "patterns.h" |
43 |
|
|
|
44 |
|
|
static int server_httpmethod_cmp(const void *, const void *); |
45 |
|
|
static int server_httperror_cmp(const void *, const void *); |
46 |
|
|
void server_httpdesc_free(struct http_descriptor *); |
47 |
|
|
int server_http_authenticate(struct server_config *, |
48 |
|
|
struct client *); |
49 |
|
|
char *server_expand_http(struct client *, const char *, |
50 |
|
|
char *, size_t); |
51 |
|
|
|
52 |
|
|
static struct http_method http_methods[] = HTTP_METHODS; |
53 |
|
|
static struct http_error http_errors[] = HTTP_ERRORS; |
54 |
|
|
|
55 |
|
|
void |
56 |
|
|
server_http(void) |
57 |
|
|
{ |
58 |
|
|
DPRINTF("%s: sorting lookup tables, pid %d", __func__, getpid()); |
59 |
|
|
|
60 |
|
|
/* Sort the HTTP lookup arrays */ |
61 |
|
|
qsort(http_methods, sizeof(http_methods) / |
62 |
|
|
sizeof(http_methods[0]) - 1, |
63 |
|
|
sizeof(http_methods[0]), server_httpmethod_cmp); |
64 |
|
|
qsort(http_errors, sizeof(http_errors) / |
65 |
|
|
sizeof(http_errors[0]) - 1, |
66 |
|
|
sizeof(http_errors[0]), server_httperror_cmp); |
67 |
|
|
} |
68 |
|
|
|
69 |
|
|
void |
70 |
|
|
server_http_init(struct server *srv) |
71 |
|
|
{ |
72 |
|
|
/* nothing */ |
73 |
|
|
} |
74 |
|
|
|
75 |
|
|
int |
76 |
|
|
server_httpdesc_init(struct client *clt) |
77 |
|
|
{ |
78 |
|
|
struct http_descriptor *desc; |
79 |
|
|
|
80 |
|
|
if ((desc = calloc(1, sizeof(*desc))) == NULL) |
81 |
|
|
return (-1); |
82 |
|
|
RB_INIT(&desc->http_headers); |
83 |
|
|
clt->clt_descreq = desc; |
84 |
|
|
|
85 |
|
|
if ((desc = calloc(1, sizeof(*desc))) == NULL) { |
86 |
|
|
/* req will be cleaned up later */ |
87 |
|
|
return (-1); |
88 |
|
|
} |
89 |
|
|
RB_INIT(&desc->http_headers); |
90 |
|
|
clt->clt_descresp = desc; |
91 |
|
|
|
92 |
|
|
return (0); |
93 |
|
|
} |
94 |
|
|
|
95 |
|
|
void |
96 |
|
|
server_httpdesc_free(struct http_descriptor *desc) |
97 |
|
|
{ |
98 |
|
|
if (desc == NULL) |
99 |
|
|
return; |
100 |
|
|
|
101 |
|
|
free(desc->http_path); |
102 |
|
|
desc->http_path = NULL; |
103 |
|
|
free(desc->http_path_alias); |
104 |
|
|
desc->http_path_alias = NULL; |
105 |
|
|
free(desc->http_query); |
106 |
|
|
desc->http_query = NULL; |
107 |
|
|
free(desc->http_version); |
108 |
|
|
desc->http_version = NULL; |
109 |
|
|
free(desc->http_host); |
110 |
|
|
desc->http_host = NULL; |
111 |
|
|
|
112 |
|
|
kv_purge(&desc->http_headers); |
113 |
|
|
desc->http_lastheader = NULL; |
114 |
|
|
desc->http_method = 0; |
115 |
|
|
desc->http_chunked = 0; |
116 |
|
|
} |
117 |
|
|
|
118 |
|
|
int |
119 |
|
|
server_http_authenticate(struct server_config *srv_conf, struct client *clt) |
120 |
|
|
{ |
121 |
|
|
char decoded[1024]; |
122 |
|
|
FILE *fp = NULL; |
123 |
|
|
struct http_descriptor *desc = clt->clt_descreq; |
124 |
|
|
const struct auth *auth = srv_conf->auth; |
125 |
|
|
struct kv *ba, key; |
126 |
|
|
size_t linesize = 0; |
127 |
|
|
ssize_t linelen; |
128 |
|
|
int ret = -1; |
129 |
|
|
char *line = NULL, *user = NULL, *pass = NULL; |
130 |
|
|
char *clt_user = NULL, *clt_pass = NULL; |
131 |
|
|
|
132 |
|
|
memset(decoded, 0, sizeof(decoded)); |
133 |
|
|
key.kv_key = "Authorization"; |
134 |
|
|
|
135 |
|
|
if ((ba = kv_find(&desc->http_headers, &key)) == NULL || |
136 |
|
|
ba->kv_value == NULL) |
137 |
|
|
goto done; |
138 |
|
|
|
139 |
|
|
if (strncmp(ba->kv_value, "Basic ", strlen("Basic ")) != 0) |
140 |
|
|
goto done; |
141 |
|
|
|
142 |
|
|
if (b64_pton(strchr(ba->kv_value, ' ') + 1, (uint8_t *)decoded, |
143 |
|
|
sizeof(decoded)) <= 0) |
144 |
|
|
goto done; |
145 |
|
|
|
146 |
|
|
if ((clt_pass = strchr(decoded, ':')) == NULL) |
147 |
|
|
goto done; |
148 |
|
|
|
149 |
|
|
clt_user = decoded; |
150 |
|
|
*clt_pass++ = '\0'; |
151 |
|
|
if ((clt->clt_remote_user = strdup(clt_user)) == NULL) |
152 |
|
|
goto done; |
153 |
|
|
|
154 |
|
|
if (clt_pass == NULL) |
155 |
|
|
goto done; |
156 |
|
|
|
157 |
|
|
if ((fp = fopen(auth->auth_htpasswd, "r")) == NULL) |
158 |
|
|
goto done; |
159 |
|
|
|
160 |
|
|
while ((linelen = getline(&line, &linesize, fp)) != -1) { |
161 |
|
|
if (line[linelen - 1] == '\n') |
162 |
|
|
line[linelen - 1] = '\0'; |
163 |
|
|
user = line; |
164 |
|
|
pass = strchr(line, ':'); |
165 |
|
|
|
166 |
|
|
if (pass == NULL) { |
167 |
|
|
explicit_bzero(line, linelen); |
168 |
|
|
continue; |
169 |
|
|
} |
170 |
|
|
|
171 |
|
|
*pass++ = '\0'; |
172 |
|
|
|
173 |
|
|
if (strcmp(clt_user, user) != 0) { |
174 |
|
|
explicit_bzero(line, linelen); |
175 |
|
|
continue; |
176 |
|
|
} |
177 |
|
|
|
178 |
|
|
if (crypt_checkpass(clt_pass, pass) == 0) { |
179 |
|
|
explicit_bzero(line, linelen); |
180 |
|
|
ret = 0; |
181 |
|
|
break; |
182 |
|
|
} |
183 |
|
|
} |
184 |
|
|
done: |
185 |
|
|
free(line); |
186 |
|
|
if (fp != NULL) |
187 |
|
|
fclose(fp); |
188 |
|
|
|
189 |
|
|
if (ba != NULL && ba->kv_value != NULL) { |
190 |
|
|
explicit_bzero(ba->kv_value, strlen(ba->kv_value)); |
191 |
|
|
explicit_bzero(decoded, sizeof(decoded)); |
192 |
|
|
} |
193 |
|
|
|
194 |
|
|
return (ret); |
195 |
|
|
} |
196 |
|
|
|
197 |
|
|
void |
198 |
|
|
server_read_http(struct bufferevent *bev, void *arg) |
199 |
|
|
{ |
200 |
|
|
struct client *clt = arg; |
201 |
|
|
struct server_config *srv_conf = clt->clt_srv_conf; |
202 |
|
|
struct http_descriptor *desc = clt->clt_descreq; |
203 |
|
|
struct evbuffer *src = EVBUFFER_INPUT(bev); |
204 |
|
|
char *line = NULL, *key, *value; |
205 |
|
|
const char *errstr; |
206 |
|
|
size_t size, linelen; |
207 |
|
|
struct kv *hdr = NULL; |
208 |
|
|
|
209 |
|
|
getmonotime(&clt->clt_tv_last); |
210 |
|
|
|
211 |
|
|
size = EVBUFFER_LENGTH(src); |
212 |
|
|
DPRINTF("%s: session %d: size %lu, to read %lld", |
213 |
|
|
__func__, clt->clt_id, size, clt->clt_toread); |
214 |
|
|
if (!size) { |
215 |
|
|
clt->clt_toread = TOREAD_HTTP_HEADER; |
216 |
|
|
goto done; |
217 |
|
|
} |
218 |
|
|
|
219 |
|
|
while (!clt->clt_headersdone) { |
220 |
|
|
if (!clt->clt_line) { |
221 |
|
|
/* Peek into the buffer to see if it looks like HTTP */ |
222 |
|
|
key = EVBUFFER_DATA(src); |
223 |
|
|
if (!isalpha(*key)) { |
224 |
|
|
server_abort_http(clt, 400, |
225 |
|
|
"invalid request line"); |
226 |
|
|
goto abort; |
227 |
|
|
} |
228 |
|
|
} |
229 |
|
|
|
230 |
|
|
if ((line = evbuffer_readln(src, |
231 |
|
|
&linelen, EVBUFFER_EOL_CRLF_STRICT)) == NULL) { |
232 |
|
|
/* No newline found after too many bytes */ |
233 |
|
|
if (size > SERVER_MAXHEADERLENGTH) { |
234 |
|
|
server_abort_http(clt, 413, |
235 |
|
|
"request line too long"); |
236 |
|
|
goto abort; |
237 |
|
|
} |
238 |
|
|
break; |
239 |
|
|
} |
240 |
|
|
|
241 |
|
|
/* |
242 |
|
|
* An empty line indicates the end of the request. |
243 |
|
|
* libevent already stripped the \r\n for us. |
244 |
|
|
*/ |
245 |
|
|
if (!linelen) { |
246 |
|
|
clt->clt_headersdone = 1; |
247 |
|
|
free(line); |
248 |
|
|
break; |
249 |
|
|
} |
250 |
|
|
key = line; |
251 |
|
|
|
252 |
|
|
/* Limit the total header length minus \r\n */ |
253 |
|
|
clt->clt_headerlen += linelen; |
254 |
|
|
if (clt->clt_headerlen > SERVER_MAXHEADERLENGTH) { |
255 |
|
|
server_abort_http(clt, 413, "request too large"); |
256 |
|
|
goto abort; |
257 |
|
|
} |
258 |
|
|
|
259 |
|
|
/* |
260 |
|
|
* The first line is the GET/POST/PUT/... request, |
261 |
|
|
* subsequent lines are HTTP headers. |
262 |
|
|
*/ |
263 |
|
|
if (++clt->clt_line == 1) |
264 |
|
|
value = strchr(key, ' '); |
265 |
|
|
else if (*key == ' ' || *key == '\t') |
266 |
|
|
/* Multiline headers wrap with a space or tab */ |
267 |
|
|
value = NULL; |
268 |
|
|
else |
269 |
|
|
value = strchr(key, ':'); |
270 |
|
|
if (value == NULL) { |
271 |
|
|
if (clt->clt_line == 1) { |
272 |
|
|
server_abort_http(clt, 400, "malformed"); |
273 |
|
|
goto abort; |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
/* Append line to the last header, if present */ |
277 |
|
|
if (kv_extend(&desc->http_headers, |
278 |
|
|
desc->http_lastheader, line) == NULL) |
279 |
|
|
goto fail; |
280 |
|
|
|
281 |
|
|
free(line); |
282 |
|
|
continue; |
283 |
|
|
} |
284 |
|
|
if (*value == ':') { |
285 |
|
|
*value++ = '\0'; |
286 |
|
|
value += strspn(value, " \t\r\n"); |
287 |
|
|
} else { |
288 |
|
|
*value++ = '\0'; |
289 |
|
|
} |
290 |
|
|
|
291 |
|
|
DPRINTF("%s: session %d: header '%s: %s'", __func__, |
292 |
|
|
clt->clt_id, key, value); |
293 |
|
|
|
294 |
|
|
/* |
295 |
|
|
* Identify and handle specific HTTP request methods |
296 |
|
|
*/ |
297 |
|
|
if (clt->clt_line == 1) { |
298 |
|
|
if ((desc->http_method = server_httpmethod_byname(key)) |
299 |
|
|
== HTTP_METHOD_NONE) { |
300 |
|
|
server_abort_http(clt, 400, "malformed"); |
301 |
|
|
goto abort; |
302 |
|
|
} |
303 |
|
|
|
304 |
|
|
/* |
305 |
|
|
* Decode request path and query |
306 |
|
|
*/ |
307 |
|
|
desc->http_path = strdup(value); |
308 |
|
|
if (desc->http_path == NULL) |
309 |
|
|
goto fail; |
310 |
|
|
|
311 |
|
|
desc->http_version = strchr(desc->http_path, ' '); |
312 |
|
|
if (desc->http_version == NULL) { |
313 |
|
|
server_abort_http(clt, 400, "malformed"); |
314 |
|
|
goto abort; |
315 |
|
|
} |
316 |
|
|
|
317 |
|
|
*desc->http_version++ = '\0'; |
318 |
|
|
desc->http_query = strchr(desc->http_path, '?'); |
319 |
|
|
if (desc->http_query != NULL) |
320 |
|
|
*desc->http_query++ = '\0'; |
321 |
|
|
|
322 |
|
|
/* |
323 |
|
|
* Have to allocate the strings because they could |
324 |
|
|
* be changed independently by the filters later. |
325 |
|
|
*/ |
326 |
|
|
if ((desc->http_version = |
327 |
|
|
strdup(desc->http_version)) == NULL) |
328 |
|
|
goto fail; |
329 |
|
|
|
330 |
|
|
if (desc->http_query != NULL && |
331 |
|
|
(desc->http_query = |
332 |
|
|
strdup(desc->http_query)) == NULL) |
333 |
|
|
goto fail; |
334 |
|
|
|
335 |
|
|
} else if (desc->http_method != HTTP_METHOD_NONE && |
336 |
|
|
strcasecmp("Content-Length", key) == 0) { |
337 |
|
|
if (desc->http_method == HTTP_METHOD_TRACE || |
338 |
|
|
desc->http_method == HTTP_METHOD_CONNECT) { |
339 |
|
|
/* |
340 |
|
|
* These method should not have a body |
341 |
|
|
* and thus no Content-Length header. |
342 |
|
|
*/ |
343 |
|
|
server_abort_http(clt, 400, "malformed"); |
344 |
|
|
goto abort; |
345 |
|
|
} |
346 |
|
|
|
347 |
|
|
/* |
348 |
|
|
* Need to read data from the client after the |
349 |
|
|
* HTTP header. |
350 |
|
|
* XXX What about non-standard clients not using |
351 |
|
|
* the carriage return? And some browsers seem to |
352 |
|
|
* include the line length in the content-length. |
353 |
|
|
*/ |
354 |
|
|
clt->clt_toread = strtonum(value, 0, LLONG_MAX, |
355 |
|
|
&errstr); |
356 |
|
|
if (errstr) { |
357 |
|
|
server_abort_http(clt, 500, errstr); |
358 |
|
|
goto abort; |
359 |
|
|
} |
360 |
|
|
if ((size_t)clt->clt_toread > |
361 |
|
|
srv_conf->maxrequestbody) { |
362 |
|
|
server_abort_http(clt, 413, NULL); |
363 |
|
|
goto abort; |
364 |
|
|
} |
365 |
|
|
} |
366 |
|
|
|
367 |
|
|
if (strcasecmp("Transfer-Encoding", key) == 0 && |
368 |
|
|
strcasecmp("chunked", value) == 0) |
369 |
|
|
desc->http_chunked = 1; |
370 |
|
|
|
371 |
|
|
if (clt->clt_line != 1) { |
372 |
|
|
if ((hdr = kv_add(&desc->http_headers, key, |
373 |
|
|
value)) == NULL) |
374 |
|
|
goto fail; |
375 |
|
|
|
376 |
|
|
desc->http_lastheader = hdr; |
377 |
|
|
} |
378 |
|
|
|
379 |
|
|
free(line); |
380 |
|
|
} |
381 |
|
|
if (clt->clt_headersdone) { |
382 |
|
|
if (desc->http_method == HTTP_METHOD_NONE) { |
383 |
|
|
server_abort_http(clt, 406, "no method"); |
384 |
|
|
return; |
385 |
|
|
} |
386 |
|
|
|
387 |
|
|
switch (desc->http_method) { |
388 |
|
|
case HTTP_METHOD_CONNECT: |
389 |
|
|
/* Data stream */ |
390 |
|
|
clt->clt_toread = TOREAD_UNLIMITED; |
391 |
|
|
bev->readcb = server_read; |
392 |
|
|
break; |
393 |
|
|
case HTTP_METHOD_GET: |
394 |
|
|
case HTTP_METHOD_HEAD: |
395 |
|
|
/* WebDAV methods */ |
396 |
|
|
case HTTP_METHOD_COPY: |
397 |
|
|
case HTTP_METHOD_MOVE: |
398 |
|
|
clt->clt_toread = 0; |
399 |
|
|
break; |
400 |
|
|
case HTTP_METHOD_DELETE: |
401 |
|
|
case HTTP_METHOD_OPTIONS: |
402 |
|
|
case HTTP_METHOD_POST: |
403 |
|
|
case HTTP_METHOD_PUT: |
404 |
|
|
case HTTP_METHOD_RESPONSE: |
405 |
|
|
/* WebDAV methods */ |
406 |
|
|
case HTTP_METHOD_PROPFIND: |
407 |
|
|
case HTTP_METHOD_PROPPATCH: |
408 |
|
|
case HTTP_METHOD_MKCOL: |
409 |
|
|
case HTTP_METHOD_LOCK: |
410 |
|
|
case HTTP_METHOD_UNLOCK: |
411 |
|
|
case HTTP_METHOD_VERSION_CONTROL: |
412 |
|
|
case HTTP_METHOD_REPORT: |
413 |
|
|
case HTTP_METHOD_CHECKOUT: |
414 |
|
|
case HTTP_METHOD_CHECKIN: |
415 |
|
|
case HTTP_METHOD_UNCHECKOUT: |
416 |
|
|
case HTTP_METHOD_MKWORKSPACE: |
417 |
|
|
case HTTP_METHOD_UPDATE: |
418 |
|
|
case HTTP_METHOD_LABEL: |
419 |
|
|
case HTTP_METHOD_MERGE: |
420 |
|
|
case HTTP_METHOD_BASELINE_CONTROL: |
421 |
|
|
case HTTP_METHOD_MKACTIVITY: |
422 |
|
|
case HTTP_METHOD_ORDERPATCH: |
423 |
|
|
case HTTP_METHOD_ACL: |
424 |
|
|
case HTTP_METHOD_MKREDIRECTREF: |
425 |
|
|
case HTTP_METHOD_UPDATEREDIRECTREF: |
426 |
|
|
case HTTP_METHOD_SEARCH: |
427 |
|
|
case HTTP_METHOD_PATCH: |
428 |
|
|
/* HTTP request payload */ |
429 |
|
|
if (clt->clt_toread > 0) |
430 |
|
|
bev->readcb = server_read_httpcontent; |
431 |
|
|
|
432 |
|
|
/* Single-pass HTTP body */ |
433 |
|
|
if (clt->clt_toread < 0) { |
434 |
|
|
clt->clt_toread = TOREAD_UNLIMITED; |
435 |
|
|
bev->readcb = server_read; |
436 |
|
|
} |
437 |
|
|
break; |
438 |
|
|
default: |
439 |
|
|
server_abort_http(clt, 405, "method not allowed"); |
440 |
|
|
return; |
441 |
|
|
} |
442 |
|
|
if (desc->http_chunked) { |
443 |
|
|
/* Chunked transfer encoding */ |
444 |
|
|
clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH; |
445 |
|
|
bev->readcb = server_read_httpchunks; |
446 |
|
|
} |
447 |
|
|
|
448 |
|
|
done: |
449 |
|
|
if (clt->clt_toread != 0) |
450 |
|
|
bufferevent_disable(bev, EV_READ); |
451 |
|
|
server_response(httpd_env, clt); |
452 |
|
|
return; |
453 |
|
|
} |
454 |
|
|
if (clt->clt_done) { |
455 |
|
|
server_close(clt, "done"); |
456 |
|
|
return; |
457 |
|
|
} |
458 |
|
|
if (EVBUFFER_LENGTH(src) && bev->readcb != server_read_http) |
459 |
|
|
bev->readcb(bev, arg); |
460 |
|
|
bufferevent_enable(bev, EV_READ); |
461 |
|
|
return; |
462 |
|
|
fail: |
463 |
|
|
server_abort_http(clt, 500, strerror(errno)); |
464 |
|
|
abort: |
465 |
|
|
free(line); |
466 |
|
|
} |
467 |
|
|
|
468 |
|
|
void |
469 |
|
|
server_read_httpcontent(struct bufferevent *bev, void *arg) |
470 |
|
|
{ |
471 |
|
|
struct client *clt = arg; |
472 |
|
|
struct evbuffer *src = EVBUFFER_INPUT(bev); |
473 |
|
|
size_t size; |
474 |
|
|
|
475 |
|
|
getmonotime(&clt->clt_tv_last); |
476 |
|
|
|
477 |
|
|
size = EVBUFFER_LENGTH(src); |
478 |
|
|
DPRINTF("%s: session %d: size %lu, to read %lld", __func__, |
479 |
|
|
clt->clt_id, size, clt->clt_toread); |
480 |
|
|
if (!size) |
481 |
|
|
return; |
482 |
|
|
|
483 |
|
|
if (clt->clt_toread > 0) { |
484 |
|
|
/* Read content data */ |
485 |
|
|
if ((off_t)size > clt->clt_toread) { |
486 |
|
|
size = clt->clt_toread; |
487 |
|
|
if (fcgi_add_stdin(clt, src) == -1) |
488 |
|
|
goto fail; |
489 |
|
|
clt->clt_toread = 0; |
490 |
|
|
} else { |
491 |
|
|
if (fcgi_add_stdin(clt, src) == -1) |
492 |
|
|
goto fail; |
493 |
|
|
clt->clt_toread -= size; |
494 |
|
|
} |
495 |
|
|
DPRINTF("%s: done, size %lu, to read %lld", __func__, |
496 |
|
|
size, clt->clt_toread); |
497 |
|
|
} |
498 |
|
|
if (clt->clt_toread == 0) { |
499 |
|
|
fcgi_add_stdin(clt, NULL); |
500 |
|
|
clt->clt_toread = TOREAD_HTTP_HEADER; |
501 |
|
|
bufferevent_disable(bev, EV_READ); |
502 |
|
|
bev->readcb = server_read_http; |
503 |
|
|
return; |
504 |
|
|
} |
505 |
|
|
if (clt->clt_done) |
506 |
|
|
goto done; |
507 |
|
|
if (bev->readcb != server_read_httpcontent) |
508 |
|
|
bev->readcb(bev, arg); |
509 |
|
|
|
510 |
|
|
return; |
511 |
|
|
done: |
512 |
|
|
return; |
513 |
|
|
fail: |
514 |
|
|
server_close(clt, strerror(errno)); |
515 |
|
|
} |
516 |
|
|
|
517 |
|
|
void |
518 |
|
|
server_read_httpchunks(struct bufferevent *bev, void *arg) |
519 |
|
|
{ |
520 |
|
|
struct client *clt = arg; |
521 |
|
|
struct evbuffer *src = EVBUFFER_INPUT(bev); |
522 |
|
|
char *line; |
523 |
|
|
long long llval; |
524 |
|
|
size_t size; |
525 |
|
|
|
526 |
|
|
getmonotime(&clt->clt_tv_last); |
527 |
|
|
|
528 |
|
|
size = EVBUFFER_LENGTH(src); |
529 |
|
|
DPRINTF("%s: session %d: size %lu, to read %lld", __func__, |
530 |
|
|
clt->clt_id, size, clt->clt_toread); |
531 |
|
|
if (!size) |
532 |
|
|
return; |
533 |
|
|
|
534 |
|
|
if (clt->clt_toread > 0) { |
535 |
|
|
/* Read chunk data */ |
536 |
|
|
if ((off_t)size > clt->clt_toread) { |
537 |
|
|
size = clt->clt_toread; |
538 |
|
|
if (server_bufferevent_write_chunk(clt, src, size) |
539 |
|
|
== -1) |
540 |
|
|
goto fail; |
541 |
|
|
clt->clt_toread = 0; |
542 |
|
|
} else { |
543 |
|
|
if (server_bufferevent_write_buffer(clt, src) == -1) |
544 |
|
|
goto fail; |
545 |
|
|
clt->clt_toread -= size; |
546 |
|
|
} |
547 |
|
|
DPRINTF("%s: done, size %lu, to read %lld", __func__, |
548 |
|
|
size, clt->clt_toread); |
549 |
|
|
} |
550 |
|
|
switch (clt->clt_toread) { |
551 |
|
|
case TOREAD_HTTP_CHUNK_LENGTH: |
552 |
|
|
line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT); |
553 |
|
|
if (line == NULL) { |
554 |
|
|
/* Ignore empty line, continue */ |
555 |
|
|
bufferevent_enable(bev, EV_READ); |
556 |
|
|
return; |
557 |
|
|
} |
558 |
|
|
if (strlen(line) == 0) { |
559 |
|
|
free(line); |
560 |
|
|
goto next; |
561 |
|
|
} |
562 |
|
|
|
563 |
|
|
/* |
564 |
|
|
* Read prepended chunk size in hex, ignore the trailer. |
565 |
|
|
* The returned signed value must not be negative. |
566 |
|
|
*/ |
567 |
|
|
if (sscanf(line, "%llx", &llval) != 1 || llval < 0) { |
568 |
|
|
free(line); |
569 |
|
|
server_close(clt, "invalid chunk size"); |
570 |
|
|
return; |
571 |
|
|
} |
572 |
|
|
|
573 |
|
|
if (server_bufferevent_print(clt, line) == -1 || |
574 |
|
|
server_bufferevent_print(clt, "\r\n") == -1) { |
575 |
|
|
free(line); |
576 |
|
|
goto fail; |
577 |
|
|
} |
578 |
|
|
free(line); |
579 |
|
|
|
580 |
|
|
if ((clt->clt_toread = llval) == 0) { |
581 |
|
|
DPRINTF("%s: last chunk", __func__); |
582 |
|
|
clt->clt_toread = TOREAD_HTTP_CHUNK_TRAILER; |
583 |
|
|
} |
584 |
|
|
break; |
585 |
|
|
case TOREAD_HTTP_CHUNK_TRAILER: |
586 |
|
|
/* Last chunk is 0 bytes followed by trailer and empty line */ |
587 |
|
|
line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT); |
588 |
|
|
if (line == NULL) { |
589 |
|
|
/* Ignore empty line, continue */ |
590 |
|
|
bufferevent_enable(bev, EV_READ); |
591 |
|
|
return; |
592 |
|
|
} |
593 |
|
|
if (server_bufferevent_print(clt, line) == -1 || |
594 |
|
|
server_bufferevent_print(clt, "\r\n") == -1) { |
595 |
|
|
free(line); |
596 |
|
|
goto fail; |
597 |
|
|
} |
598 |
|
|
if (strlen(line) == 0) { |
599 |
|
|
/* Switch to HTTP header mode */ |
600 |
|
|
clt->clt_toread = TOREAD_HTTP_HEADER; |
601 |
|
|
bev->readcb = server_read_http; |
602 |
|
|
} |
603 |
|
|
free(line); |
604 |
|
|
break; |
605 |
|
|
case 0: |
606 |
|
|
/* Chunk is terminated by an empty newline */ |
607 |
|
|
line = evbuffer_readln(src, NULL, EVBUFFER_EOL_CRLF_STRICT); |
608 |
|
|
free(line); |
609 |
|
|
if (server_bufferevent_print(clt, "\r\n") == -1) |
610 |
|
|
goto fail; |
611 |
|
|
clt->clt_toread = TOREAD_HTTP_CHUNK_LENGTH; |
612 |
|
|
break; |
613 |
|
|
} |
614 |
|
|
|
615 |
|
|
next: |
616 |
|
|
if (clt->clt_done) |
617 |
|
|
goto done; |
618 |
|
|
if (EVBUFFER_LENGTH(src)) |
619 |
|
|
bev->readcb(bev, arg); |
620 |
|
|
bufferevent_enable(bev, EV_READ); |
621 |
|
|
return; |
622 |
|
|
|
623 |
|
|
done: |
624 |
|
|
server_close(clt, "last http chunk read (done)"); |
625 |
|
|
return; |
626 |
|
|
fail: |
627 |
|
|
server_close(clt, strerror(errno)); |
628 |
|
|
} |
629 |
|
|
|
630 |
|
|
void |
631 |
|
|
server_read_httprange(struct bufferevent *bev, void *arg) |
632 |
|
|
{ |
633 |
|
|
struct client *clt = arg; |
634 |
|
|
struct evbuffer *src = EVBUFFER_INPUT(bev); |
635 |
|
|
size_t size; |
636 |
|
|
struct media_type *media; |
637 |
|
|
struct range_data *r = &clt->clt_ranges; |
638 |
|
|
struct range *range; |
639 |
|
|
|
640 |
|
|
getmonotime(&clt->clt_tv_last); |
641 |
|
|
|
642 |
|
|
if (r->range_toread > 0) { |
643 |
|
|
size = EVBUFFER_LENGTH(src); |
644 |
|
|
if (!size) |
645 |
|
|
return; |
646 |
|
|
|
647 |
|
|
/* Read chunk data */ |
648 |
|
|
if ((off_t)size > r->range_toread) { |
649 |
|
|
size = r->range_toread; |
650 |
|
|
if (server_bufferevent_write_chunk(clt, src, size) |
651 |
|
|
== -1) |
652 |
|
|
goto fail; |
653 |
|
|
r->range_toread = 0; |
654 |
|
|
} else { |
655 |
|
|
if (server_bufferevent_write_buffer(clt, src) == -1) |
656 |
|
|
goto fail; |
657 |
|
|
r->range_toread -= size; |
658 |
|
|
} |
659 |
|
|
if (r->range_toread < 1) |
660 |
|
|
r->range_toread = TOREAD_HTTP_RANGE; |
661 |
|
|
DPRINTF("%s: done, size %lu, to read %lld", __func__, |
662 |
|
|
size, r->range_toread); |
663 |
|
|
} |
664 |
|
|
|
665 |
|
|
switch (r->range_toread) { |
666 |
|
|
case TOREAD_HTTP_RANGE: |
667 |
|
|
if (r->range_index >= r->range_count) { |
668 |
|
|
if (r->range_count > 1) { |
669 |
|
|
/* Add end marker */ |
670 |
|
|
if (server_bufferevent_printf(clt, |
671 |
|
|
"\r\n--%llu--\r\n", |
672 |
|
|
clt->clt_boundary) == -1) |
673 |
|
|
goto fail; |
674 |
|
|
} |
675 |
|
|
r->range_toread = TOREAD_HTTP_NONE; |
676 |
|
|
break; |
677 |
|
|
} |
678 |
|
|
|
679 |
|
|
range = &r->range[r->range_index]; |
680 |
|
|
|
681 |
|
|
if (r->range_count > 1) { |
682 |
|
|
media = r->range_media; |
683 |
|
|
if (server_bufferevent_printf(clt, |
684 |
|
|
"\r\n--%llu\r\n" |
685 |
|
|
"Content-Type: %s/%s\r\n" |
686 |
|
|
"Content-Range: bytes %lld-%lld/%zu\r\n\r\n", |
687 |
|
|
clt->clt_boundary, |
688 |
|
|
media->media_type, media->media_subtype, |
689 |
|
|
range->start, range->end, r->range_total) == -1) |
690 |
|
|
goto fail; |
691 |
|
|
} |
692 |
|
|
r->range_toread = range->end - range->start + 1; |
693 |
|
|
|
694 |
|
|
if (lseek(clt->clt_fd, range->start, SEEK_SET) == -1) |
695 |
|
|
goto fail; |
696 |
|
|
|
697 |
|
|
/* Throw away bytes that are already in the input buffer */ |
698 |
|
|
evbuffer_drain(src, EVBUFFER_LENGTH(src)); |
699 |
|
|
|
700 |
|
|
/* Increment for the next part */ |
701 |
|
|
r->range_index++; |
702 |
|
|
break; |
703 |
|
|
case TOREAD_HTTP_NONE: |
704 |
|
|
case 0: |
705 |
|
|
break; |
706 |
|
|
} |
707 |
|
|
|
708 |
|
|
if (clt->clt_done) |
709 |
|
|
goto done; |
710 |
|
|
|
711 |
|
|
if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(clt->clt_bev)) > (size_t) |
712 |
|
|
SERVER_MAX_PREFETCH * clt->clt_sndbufsiz) { |
713 |
|
|
bufferevent_disable(clt->clt_srvbev, EV_READ); |
714 |
|
|
clt->clt_srvbev_throttled = 1; |
715 |
|
|
} |
716 |
|
|
|
717 |
|
|
return; |
718 |
|
|
done: |
719 |
|
|
(*bev->errorcb)(bev, EVBUFFER_READ, bev->cbarg); |
720 |
|
|
return; |
721 |
|
|
fail: |
722 |
|
|
server_close(clt, strerror(errno)); |
723 |
|
|
} |
724 |
|
|
|
725 |
|
|
void |
726 |
|
|
server_reset_http(struct client *clt) |
727 |
|
|
{ |
728 |
|
|
struct server *srv = clt->clt_srv; |
729 |
|
|
|
730 |
|
|
server_log(clt, NULL); |
731 |
|
|
|
732 |
|
|
server_httpdesc_free(clt->clt_descreq); |
733 |
|
|
server_httpdesc_free(clt->clt_descresp); |
734 |
|
|
clt->clt_headerlen = 0; |
735 |
|
|
clt->clt_headersdone = 0; |
736 |
|
|
clt->clt_done = 0; |
737 |
|
|
clt->clt_line = 0; |
738 |
|
|
clt->clt_chunk = 0; |
739 |
|
|
free(clt->clt_remote_user); |
740 |
|
|
clt->clt_remote_user = NULL; |
741 |
|
|
clt->clt_bev->readcb = server_read_http; |
742 |
|
|
clt->clt_srv_conf = &srv->srv_conf; |
743 |
|
|
str_match_free(&clt->clt_srv_match); |
744 |
|
|
} |
745 |
|
|
|
746 |
|
|
ssize_t |
747 |
|
|
server_http_time(time_t t, char *tmbuf, size_t len) |
748 |
|
|
{ |
749 |
|
|
struct tm tm; |
750 |
|
|
|
751 |
|
|
/* New HTTP/1.1 RFC 7231 prefers IMF-fixdate from RFC 5322 */ |
752 |
|
|
if (t == -1 || gmtime_r(&t, &tm) == NULL) |
753 |
|
|
return (-1); |
754 |
|
|
else |
755 |
|
|
return (strftime(tmbuf, len, "%a, %d %h %Y %T %Z", &tm)); |
756 |
|
|
} |
757 |
|
|
|
758 |
|
|
const char * |
759 |
|
|
server_http_host(struct sockaddr_storage *ss, char *buf, size_t len) |
760 |
|
|
{ |
761 |
|
|
char hbuf[HOST_NAME_MAX+1]; |
762 |
|
|
in_port_t port; |
763 |
|
|
|
764 |
|
|
if (print_host(ss, buf, len) == NULL) |
765 |
|
|
return (NULL); |
766 |
|
|
|
767 |
|
|
port = ntohs(server_socket_getport(ss)); |
768 |
|
|
if (port == HTTP_PORT) |
769 |
|
|
return (buf); |
770 |
|
|
|
771 |
|
|
switch (ss->ss_family) { |
772 |
|
|
case AF_INET: |
773 |
|
|
if ((size_t)snprintf(hbuf, sizeof(hbuf), |
774 |
|
|
"%s:%u", buf, port) >= sizeof(hbuf)) |
775 |
|
|
return (NULL); |
776 |
|
|
break; |
777 |
|
|
case AF_INET6: |
778 |
|
|
if ((size_t)snprintf(hbuf, sizeof(hbuf), |
779 |
|
|
"[%s]:%u", buf, port) >= sizeof(hbuf)) |
780 |
|
|
return (NULL); |
781 |
|
|
break; |
782 |
|
|
} |
783 |
|
|
|
784 |
|
|
if (strlcpy(buf, hbuf, len) >= len) |
785 |
|
|
return (NULL); |
786 |
|
|
|
787 |
|
|
return (buf); |
788 |
|
|
} |
789 |
|
|
|
790 |
|
|
char * |
791 |
|
|
server_http_parsehost(char *host, char *buf, size_t len, int *portval) |
792 |
|
|
{ |
793 |
|
|
char *start, *end, *port; |
794 |
|
|
const char *errstr = NULL; |
795 |
|
|
|
796 |
|
|
if (strlcpy(buf, host, len) >= len) { |
797 |
|
|
log_debug("%s: host name too long", __func__); |
798 |
|
|
return (NULL); |
799 |
|
|
} |
800 |
|
|
|
801 |
|
|
start = buf; |
802 |
|
|
end = port = NULL; |
803 |
|
|
|
804 |
|
|
if (*start == '[' && (end = strchr(start, ']')) != NULL) { |
805 |
|
|
/* Address enclosed in [] with port, eg. [2001:db8::1]:80 */ |
806 |
|
|
start++; |
807 |
|
|
*end++ = '\0'; |
808 |
|
|
if ((port = strchr(end, ':')) == NULL || *port == '\0') |
809 |
|
|
port = NULL; |
810 |
|
|
else |
811 |
|
|
port++; |
812 |
|
|
memmove(buf, start, strlen(start) + 1); |
813 |
|
|
} else if ((end = strchr(start, ':')) != NULL) { |
814 |
|
|
/* Name or address with port, eg. www.example.com:80 */ |
815 |
|
|
*end++ = '\0'; |
816 |
|
|
port = end; |
817 |
|
|
} else { |
818 |
|
|
/* Name or address with default port, eg. www.example.com */ |
819 |
|
|
port = NULL; |
820 |
|
|
} |
821 |
|
|
|
822 |
|
|
if (port != NULL) { |
823 |
|
|
/* Save the requested port */ |
824 |
|
|
*portval = strtonum(port, 0, 0xffff, &errstr); |
825 |
|
|
if (errstr != NULL) { |
826 |
|
|
log_debug("%s: invalid port: %s", __func__, |
827 |
|
|
strerror(errno)); |
828 |
|
|
return (NULL); |
829 |
|
|
} |
830 |
|
|
*portval = htons(*portval); |
831 |
|
|
} else { |
832 |
|
|
/* Port not given, indicate the default port */ |
833 |
|
|
*portval = -1; |
834 |
|
|
} |
835 |
|
|
|
836 |
|
|
return (start); |
837 |
|
|
} |
838 |
|
|
|
839 |
|
|
void |
840 |
|
|
server_abort_http(struct client *clt, unsigned int code, const char *msg) |
841 |
|
|
{ |
842 |
|
|
struct server_config *srv_conf = clt->clt_srv_conf; |
843 |
|
|
struct bufferevent *bev = clt->clt_bev; |
844 |
|
|
struct http_descriptor *desc = clt->clt_descreq; |
845 |
|
|
const char *httperr = NULL, *style; |
846 |
|
|
char *httpmsg, *body = NULL, *extraheader = NULL; |
847 |
|
|
char tmbuf[32], hbuf[128], *hstsheader = NULL; |
848 |
|
|
char buf[IBUF_READ_SIZE]; |
849 |
|
|
char *escapedmsg = NULL; |
850 |
|
|
int bodylen; |
851 |
|
|
|
852 |
|
|
if (code == 0) { |
853 |
|
|
server_close(clt, "dropped"); |
854 |
|
|
return; |
855 |
|
|
} |
856 |
|
|
|
857 |
|
|
if ((httperr = server_httperror_byid(code)) == NULL) |
858 |
|
|
httperr = "Unknown Error"; |
859 |
|
|
|
860 |
|
|
if (bev == NULL) |
861 |
|
|
goto done; |
862 |
|
|
|
863 |
|
|
if (server_log_http(clt, code, 0) == -1) |
864 |
|
|
goto done; |
865 |
|
|
|
866 |
|
|
/* Some system information */ |
867 |
|
|
if (print_host(&srv_conf->ss, hbuf, sizeof(hbuf)) == NULL) |
868 |
|
|
goto done; |
869 |
|
|
|
870 |
|
|
if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0) |
871 |
|
|
goto done; |
872 |
|
|
|
873 |
|
|
/* Do not send details of the Internal Server Error */ |
874 |
|
|
switch (code) { |
875 |
|
|
case 301: |
876 |
|
|
case 302: |
877 |
|
|
case 303: |
878 |
|
|
if (msg == NULL) |
879 |
|
|
break; |
880 |
|
|
memset(buf, 0, sizeof(buf)); |
881 |
|
|
if (server_expand_http(clt, msg, buf, sizeof(buf)) == NULL) |
882 |
|
|
goto done; |
883 |
|
|
if (asprintf(&extraheader, "Location: %s\r\n", buf) == -1) { |
884 |
|
|
code = 500; |
885 |
|
|
extraheader = NULL; |
886 |
|
|
} |
887 |
|
|
msg = buf; |
888 |
|
|
break; |
889 |
|
|
case 401: |
890 |
|
|
if (msg == NULL) |
891 |
|
|
break; |
892 |
|
|
if (stravis(&escapedmsg, msg, VIS_DQ) == -1) { |
893 |
|
|
code = 500; |
894 |
|
|
extraheader = NULL; |
895 |
|
|
} else if (asprintf(&extraheader, |
896 |
|
|
"WWW-Authenticate: Basic realm=\"%s\"\r\n", escapedmsg) |
897 |
|
|
== -1) { |
898 |
|
|
code = 500; |
899 |
|
|
extraheader = NULL; |
900 |
|
|
} |
901 |
|
|
break; |
902 |
|
|
case 416: |
903 |
|
|
if (msg == NULL) |
904 |
|
|
break; |
905 |
|
|
if (asprintf(&extraheader, |
906 |
|
|
"Content-Range: %s\r\n", msg) == -1) { |
907 |
|
|
code = 500; |
908 |
|
|
extraheader = NULL; |
909 |
|
|
} |
910 |
|
|
break; |
911 |
|
|
default: |
912 |
|
|
/* |
913 |
|
|
* Do not send details of the error. Traditionally, |
914 |
|
|
* web servers responsed with the request path on 40x |
915 |
|
|
* errors which could be abused to inject JavaScript etc. |
916 |
|
|
* Instead of sanitizing the path here, we just don't |
917 |
|
|
* reprint it. |
918 |
|
|
*/ |
919 |
|
|
break; |
920 |
|
|
} |
921 |
|
|
|
922 |
|
|
free(escapedmsg); |
923 |
|
|
|
924 |
|
|
/* A CSS stylesheet allows minimal customization by the user */ |
925 |
|
|
style = "body { background-color: white; color: black; font-family: " |
926 |
|
|
"'Comic Sans MS', 'Chalkboard SE', 'Comic Neue', sans-serif; }\n" |
927 |
|
|
"hr { border: 0; border-bottom: 1px dashed; }\n"; |
928 |
|
|
|
929 |
|
|
/* Generate simple HTML error document */ |
930 |
|
|
if ((bodylen = asprintf(&body, |
931 |
|
|
"<!DOCTYPE html>\n" |
932 |
|
|
"<html>\n" |
933 |
|
|
"<head>\n" |
934 |
|
|
"<meta http-equiv=\"Content-Type\" content=\"text/html; " |
935 |
|
|
"charset=utf-8\"/>\n" |
936 |
|
|
"<title>%03d %s</title>\n" |
937 |
|
|
"<style type=\"text/css\"><!--\n%s\n--></style>\n" |
938 |
|
|
"</head>\n" |
939 |
|
|
"<body>\n" |
940 |
|
|
"<h1>%03d %s</h1>\n" |
941 |
|
|
"<hr>\n<address>%s</address>\n" |
942 |
|
|
"</body>\n" |
943 |
|
|
"</html>\n", |
944 |
|
|
code, httperr, style, code, httperr, HTTPD_SERVERNAME)) == -1) { |
945 |
|
|
body = NULL; |
946 |
|
|
goto done; |
947 |
|
|
} |
948 |
|
|
|
949 |
|
|
if (srv_conf->flags & SRVFLAG_SERVER_HSTS) { |
950 |
|
|
if (asprintf(&hstsheader, "Strict-Transport-Security: " |
951 |
|
|
"max-age=%d%s%s\r\n", srv_conf->hsts_max_age, |
952 |
|
|
srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ? |
953 |
|
|
"; includeSubDomains" : "", |
954 |
|
|
srv_conf->hsts_flags & HSTSFLAG_PRELOAD ? |
955 |
|
|
"; preload" : "") == -1) { |
956 |
|
|
hstsheader = NULL; |
957 |
|
|
goto done; |
958 |
|
|
} |
959 |
|
|
} |
960 |
|
|
|
961 |
|
|
/* Add basic HTTP headers */ |
962 |
|
|
if (asprintf(&httpmsg, |
963 |
|
|
"HTTP/1.0 %03d %s\r\n" |
964 |
|
|
"Date: %s\r\n" |
965 |
|
|
"Server: %s\r\n" |
966 |
|
|
"Connection: close\r\n" |
967 |
|
|
"Content-Type: text/html\r\n" |
968 |
|
|
"Content-Length: %d\r\n" |
969 |
|
|
"%s" |
970 |
|
|
"%s" |
971 |
|
|
"\r\n" |
972 |
|
|
"%s", |
973 |
|
|
code, httperr, tmbuf, HTTPD_SERVERNAME, bodylen, |
974 |
|
|
extraheader == NULL ? "" : extraheader, |
975 |
|
|
hstsheader == NULL ? "" : hstsheader, |
976 |
|
|
desc->http_method == HTTP_METHOD_HEAD ? "" : body) == -1) |
977 |
|
|
goto done; |
978 |
|
|
|
979 |
|
|
/* Dump the message without checking for success */ |
980 |
|
|
server_dump(clt, httpmsg, strlen(httpmsg)); |
981 |
|
|
free(httpmsg); |
982 |
|
|
|
983 |
|
|
done: |
984 |
|
|
free(body); |
985 |
|
|
free(extraheader); |
986 |
|
|
free(hstsheader); |
987 |
|
|
if (msg == NULL) |
988 |
|
|
msg = "\"\""; |
989 |
|
|
if (asprintf(&httpmsg, "%s (%03d %s)", msg, code, httperr) == -1) { |
990 |
|
|
server_close(clt, msg); |
991 |
|
|
} else { |
992 |
|
|
server_close(clt, httpmsg); |
993 |
|
|
free(httpmsg); |
994 |
|
|
} |
995 |
|
|
} |
996 |
|
|
|
997 |
|
|
void |
998 |
|
|
server_close_http(struct client *clt) |
999 |
|
|
{ |
1000 |
|
|
struct http_descriptor *desc; |
1001 |
|
|
|
1002 |
|
|
desc = clt->clt_descreq; |
1003 |
|
|
server_httpdesc_free(desc); |
1004 |
|
|
free(desc); |
1005 |
|
|
clt->clt_descreq = NULL; |
1006 |
|
|
|
1007 |
|
|
desc = clt->clt_descresp; |
1008 |
|
|
server_httpdesc_free(desc); |
1009 |
|
|
free(desc); |
1010 |
|
|
clt->clt_descresp = NULL; |
1011 |
|
|
free(clt->clt_remote_user); |
1012 |
|
|
clt->clt_remote_user = NULL; |
1013 |
|
|
|
1014 |
|
|
str_match_free(&clt->clt_srv_match); |
1015 |
|
|
} |
1016 |
|
|
|
1017 |
|
|
char * |
1018 |
|
|
server_expand_http(struct client *clt, const char *val, char *buf, |
1019 |
|
|
size_t len) |
1020 |
|
|
{ |
1021 |
|
|
struct http_descriptor *desc = clt->clt_descreq; |
1022 |
|
|
struct server_config *srv_conf = clt->clt_srv_conf; |
1023 |
|
|
char ibuf[128], *str, *path, *query; |
1024 |
|
|
const char *errstr = NULL, *p; |
1025 |
|
|
size_t size; |
1026 |
|
|
int n, ret; |
1027 |
|
|
|
1028 |
|
|
if (strlcpy(buf, val, len) >= len) |
1029 |
|
|
return (NULL); |
1030 |
|
|
|
1031 |
|
|
/* Find previously matched substrings by index */ |
1032 |
|
|
for (p = val; clt->clt_srv_match.sm_nmatch && |
1033 |
|
|
(p = strstr(p, "%")) != NULL; p++) { |
1034 |
|
|
if (!isdigit((unsigned char)*(p + 1))) |
1035 |
|
|
continue; |
1036 |
|
|
|
1037 |
|
|
/* Copy number, leading '%' char and add trailing \0 */ |
1038 |
|
|
size = strspn(p + 1, "0123456789") + 2; |
1039 |
|
|
if (size >= sizeof(ibuf)) |
1040 |
|
|
return (NULL); |
1041 |
|
|
(void)strlcpy(ibuf, p, size); |
1042 |
|
|
n = strtonum(ibuf + 1, 0, |
1043 |
|
|
clt->clt_srv_match.sm_nmatch - 1, &errstr); |
1044 |
|
|
if (errstr != NULL) |
1045 |
|
|
return (NULL); |
1046 |
|
|
|
1047 |
|
|
/* Expand variable with matched value */ |
1048 |
|
|
if ((str = url_encode(clt->clt_srv_match.sm_match[n])) == NULL) |
1049 |
|
|
return (NULL); |
1050 |
|
|
ret = expand_string(buf, len, ibuf, str); |
1051 |
|
|
free(str); |
1052 |
|
|
if (ret != 0) |
1053 |
|
|
return (NULL); |
1054 |
|
|
} |
1055 |
|
|
if (strstr(val, "$DOCUMENT_URI") != NULL) { |
1056 |
|
|
if ((path = url_encode(desc->http_path)) == NULL) |
1057 |
|
|
return (NULL); |
1058 |
|
|
ret = expand_string(buf, len, "$DOCUMENT_URI", path); |
1059 |
|
|
free(path); |
1060 |
|
|
if (ret != 0) |
1061 |
|
|
return (NULL); |
1062 |
|
|
} |
1063 |
|
|
if (strstr(val, "$QUERY_STRING") != NULL) { |
1064 |
|
|
if (desc->http_query == NULL) { |
1065 |
|
|
ret = expand_string(buf, len, "$QUERY_STRING", ""); |
1066 |
|
|
} else { |
1067 |
|
|
if ((query = url_encode(desc->http_query)) == NULL) |
1068 |
|
|
return (NULL); |
1069 |
|
|
ret = expand_string(buf, len, "$QUERY_STRING", query); |
1070 |
|
|
free(query); |
1071 |
|
|
} |
1072 |
|
|
if (ret != 0) |
1073 |
|
|
return (NULL); |
1074 |
|
|
} |
1075 |
|
|
if (strstr(val, "$HTTP_HOST") != NULL) { |
1076 |
|
|
if (desc->http_host == NULL) |
1077 |
|
|
return (NULL); |
1078 |
|
|
if ((str = url_encode(desc->http_host)) == NULL) |
1079 |
|
|
return (NULL); |
1080 |
|
|
expand_string(buf, len, "$HTTP_HOST", str); |
1081 |
|
|
free(str); |
1082 |
|
|
} |
1083 |
|
|
if (strstr(val, "$REMOTE_") != NULL) { |
1084 |
|
|
if (strstr(val, "$REMOTE_ADDR") != NULL) { |
1085 |
|
|
if (print_host(&clt->clt_ss, |
1086 |
|
|
ibuf, sizeof(ibuf)) == NULL) |
1087 |
|
|
return (NULL); |
1088 |
|
|
if (expand_string(buf, len, |
1089 |
|
|
"$REMOTE_ADDR", ibuf) != 0) |
1090 |
|
|
return (NULL); |
1091 |
|
|
} |
1092 |
|
|
if (strstr(val, "$REMOTE_PORT") != NULL) { |
1093 |
|
|
snprintf(ibuf, sizeof(ibuf), |
1094 |
|
|
"%u", ntohs(clt->clt_port)); |
1095 |
|
|
if (expand_string(buf, len, |
1096 |
|
|
"$REMOTE_PORT", ibuf) != 0) |
1097 |
|
|
return (NULL); |
1098 |
|
|
} |
1099 |
|
|
if (strstr(val, "$REMOTE_USER") != NULL) { |
1100 |
|
|
if ((srv_conf->flags & SRVFLAG_AUTH) && |
1101 |
|
|
clt->clt_remote_user != NULL) { |
1102 |
|
|
if ((str = url_encode(clt->clt_remote_user)) |
1103 |
|
|
== NULL) |
1104 |
|
|
return (NULL); |
1105 |
|
|
} else |
1106 |
|
|
str = strdup(""); |
1107 |
|
|
ret = expand_string(buf, len, "$REMOTE_USER", str); |
1108 |
|
|
free(str); |
1109 |
|
|
if (ret != 0) |
1110 |
|
|
return (NULL); |
1111 |
|
|
} |
1112 |
|
|
} |
1113 |
|
|
if (strstr(val, "$REQUEST_URI") != NULL) { |
1114 |
|
|
if ((path = url_encode(desc->http_path)) == NULL) |
1115 |
|
|
return (NULL); |
1116 |
|
|
if (desc->http_query == NULL) { |
1117 |
|
|
str = path; |
1118 |
|
|
} else { |
1119 |
|
|
if ((query = url_encode(desc->http_query)) == NULL) { |
1120 |
|
|
free(path); |
1121 |
|
|
return (NULL); |
1122 |
|
|
} |
1123 |
|
|
ret = asprintf(&str, "%s?%s", path, query); |
1124 |
|
|
free(path); |
1125 |
|
|
free(query); |
1126 |
|
|
if (ret == -1) |
1127 |
|
|
return (NULL); |
1128 |
|
|
} |
1129 |
|
|
|
1130 |
|
|
ret = expand_string(buf, len, "$REQUEST_URI", str); |
1131 |
|
|
free(str); |
1132 |
|
|
if (ret != 0) |
1133 |
|
|
return (NULL); |
1134 |
|
|
} |
1135 |
|
|
if (strstr(val, "$SERVER_") != NULL) { |
1136 |
|
|
if (strstr(val, "$SERVER_ADDR") != NULL) { |
1137 |
|
|
if (print_host(&srv_conf->ss, |
1138 |
|
|
ibuf, sizeof(ibuf)) == NULL) |
1139 |
|
|
return (NULL); |
1140 |
|
|
if (expand_string(buf, len, |
1141 |
|
|
"$SERVER_ADDR", ibuf) != 0) |
1142 |
|
|
return (NULL); |
1143 |
|
|
} |
1144 |
|
|
if (strstr(val, "$SERVER_PORT") != NULL) { |
1145 |
|
|
snprintf(ibuf, sizeof(ibuf), "%u", |
1146 |
|
|
ntohs(srv_conf->port)); |
1147 |
|
|
if (expand_string(buf, len, |
1148 |
|
|
"$SERVER_PORT", ibuf) != 0) |
1149 |
|
|
return (NULL); |
1150 |
|
|
} |
1151 |
|
|
if (strstr(val, "$SERVER_NAME") != NULL) { |
1152 |
|
|
if ((str = url_encode(srv_conf->name)) |
1153 |
|
|
== NULL) |
1154 |
|
|
return (NULL); |
1155 |
|
|
ret = expand_string(buf, len, "$SERVER_NAME", str); |
1156 |
|
|
free(str); |
1157 |
|
|
if (ret != 0) |
1158 |
|
|
return (NULL); |
1159 |
|
|
} |
1160 |
|
|
} |
1161 |
|
|
|
1162 |
|
|
return (buf); |
1163 |
|
|
} |
1164 |
|
|
|
1165 |
|
|
int |
1166 |
|
|
server_response(struct httpd *httpd, struct client *clt) |
1167 |
|
|
{ |
1168 |
|
|
char path[PATH_MAX]; |
1169 |
|
|
char hostname[HOST_NAME_MAX+1]; |
1170 |
|
|
struct http_descriptor *desc = clt->clt_descreq; |
1171 |
|
|
struct http_descriptor *resp = clt->clt_descresp; |
1172 |
|
|
struct server *srv = clt->clt_srv; |
1173 |
|
|
struct server_config *srv_conf = &srv->srv_conf; |
1174 |
|
|
struct kv *kv, key, *host; |
1175 |
|
|
struct str_find sm; |
1176 |
|
|
int portval = -1, ret; |
1177 |
|
|
char *hostval; |
1178 |
|
|
const char *errstr = NULL; |
1179 |
|
|
|
1180 |
|
|
/* Canonicalize the request path */ |
1181 |
|
|
if (desc->http_path == NULL || |
1182 |
|
|
url_decode(desc->http_path) == NULL || |
1183 |
|
|
canonicalize_path(desc->http_path, path, sizeof(path)) == NULL) |
1184 |
|
|
goto fail; |
1185 |
|
|
free(desc->http_path); |
1186 |
|
|
if ((desc->http_path = strdup(path)) == NULL) |
1187 |
|
|
goto fail; |
1188 |
|
|
|
1189 |
|
|
key.kv_key = "Host"; |
1190 |
|
|
if ((host = kv_find(&desc->http_headers, &key)) != NULL && |
1191 |
|
|
host->kv_value == NULL) |
1192 |
|
|
host = NULL; |
1193 |
|
|
|
1194 |
|
|
if (strcmp(desc->http_version, "HTTP/1.1") == 0) { |
1195 |
|
|
/* Host header is mandatory */ |
1196 |
|
|
if (host == NULL) |
1197 |
|
|
goto fail; |
1198 |
|
|
|
1199 |
|
|
/* Is the connection persistent? */ |
1200 |
|
|
key.kv_key = "Connection"; |
1201 |
|
|
if ((kv = kv_find(&desc->http_headers, &key)) != NULL && |
1202 |
|
|
strcasecmp("close", kv->kv_value) == 0) |
1203 |
|
|
clt->clt_persist = 0; |
1204 |
|
|
else |
1205 |
|
|
clt->clt_persist++; |
1206 |
|
|
} else { |
1207 |
|
|
/* Is the connection persistent? */ |
1208 |
|
|
key.kv_key = "Connection"; |
1209 |
|
|
if ((kv = kv_find(&desc->http_headers, &key)) != NULL && |
1210 |
|
|
strcasecmp("keep-alive", kv->kv_value) == 0) |
1211 |
|
|
clt->clt_persist++; |
1212 |
|
|
else |
1213 |
|
|
clt->clt_persist = 0; |
1214 |
|
|
} |
1215 |
|
|
|
1216 |
|
|
if (clt->clt_persist >= srv_conf->maxrequests) |
1217 |
|
|
clt->clt_persist = 0; |
1218 |
|
|
|
1219 |
|
|
/* pipelining should end after the first "idempotent" method */ |
1220 |
|
|
if (clt->clt_pipelining && clt->clt_toread > 0) |
1221 |
|
|
clt->clt_persist = 0; |
1222 |
|
|
|
1223 |
|
|
/* |
1224 |
|
|
* Do we have a Host header and matching configuration? |
1225 |
|
|
* XXX the Host can also appear in the URL path. |
1226 |
|
|
*/ |
1227 |
|
|
if (host != NULL) { |
1228 |
|
|
if ((hostval = server_http_parsehost(host->kv_value, |
1229 |
|
|
hostname, sizeof(hostname), &portval)) == NULL) |
1230 |
|
|
goto fail; |
1231 |
|
|
|
1232 |
|
|
TAILQ_FOREACH(srv_conf, &srv->srv_hosts, entry) { |
1233 |
|
|
#ifdef DEBUG |
1234 |
|
|
if ((srv_conf->flags & SRVFLAG_LOCATION) == 0) { |
1235 |
|
|
DPRINTF("%s: virtual host \"%s:%u\"" |
1236 |
|
|
" host \"%s\" (\"%s\")", |
1237 |
|
|
__func__, srv_conf->name, |
1238 |
|
|
ntohs(srv_conf->port), host->kv_value, |
1239 |
|
|
hostname); |
1240 |
|
|
} |
1241 |
|
|
#endif |
1242 |
|
|
if (srv_conf->flags & SRVFLAG_LOCATION) |
1243 |
|
|
continue; |
1244 |
|
|
else if (srv_conf->flags & SRVFLAG_SERVER_MATCH) { |
1245 |
|
|
str_find(hostname, srv_conf->name, |
1246 |
|
|
&sm, 1, &errstr); |
1247 |
|
|
ret = errstr == NULL ? 0 : -1; |
1248 |
|
|
} else { |
1249 |
|
|
ret = fnmatch(srv_conf->name, |
1250 |
|
|
hostname, FNM_CASEFOLD); |
1251 |
|
|
} |
1252 |
|
|
if (ret == 0 && |
1253 |
|
|
(portval == -1 || |
1254 |
|
|
(portval != -1 && portval == srv_conf->port))) { |
1255 |
|
|
/* Replace host configuration */ |
1256 |
|
|
clt->clt_srv_conf = srv_conf; |
1257 |
|
|
srv_conf = NULL; |
1258 |
|
|
break; |
1259 |
|
|
} |
1260 |
|
|
} |
1261 |
|
|
} |
1262 |
|
|
|
1263 |
|
|
if (srv_conf != NULL) { |
1264 |
|
|
/* Use the actual server IP address */ |
1265 |
|
|
if (server_http_host(&clt->clt_srv_ss, hostname, |
1266 |
|
|
sizeof(hostname)) == NULL) |
1267 |
|
|
goto fail; |
1268 |
|
|
} else { |
1269 |
|
|
/* Host header was valid and found */ |
1270 |
|
|
if (strlcpy(hostname, host->kv_value, sizeof(hostname)) >= |
1271 |
|
|
sizeof(hostname)) |
1272 |
|
|
goto fail; |
1273 |
|
|
srv_conf = clt->clt_srv_conf; |
1274 |
|
|
} |
1275 |
|
|
|
1276 |
|
|
if ((desc->http_host = strdup(hostname)) == NULL) |
1277 |
|
|
goto fail; |
1278 |
|
|
|
1279 |
|
|
/* Now fill in the mandatory parts of the response descriptor */ |
1280 |
|
|
resp->http_method = desc->http_method; |
1281 |
|
|
if ((resp->http_version = strdup(desc->http_version)) == NULL) |
1282 |
|
|
goto fail; |
1283 |
|
|
|
1284 |
|
|
/* Now search for the location */ |
1285 |
|
|
srv_conf = server_getlocation(clt, desc->http_path); |
1286 |
|
|
|
1287 |
|
|
if (srv_conf->flags & SRVFLAG_BLOCK) { |
1288 |
|
|
server_abort_http(clt, srv_conf->return_code, |
1289 |
|
|
srv_conf->return_uri); |
1290 |
|
|
return (-1); |
1291 |
|
|
} else if (srv_conf->flags & SRVFLAG_AUTH && |
1292 |
|
|
server_http_authenticate(srv_conf, clt) == -1) { |
1293 |
|
|
server_abort_http(clt, 401, srv_conf->auth_realm); |
1294 |
|
|
return (-1); |
1295 |
|
|
} else |
1296 |
|
|
return (server_file(httpd, clt)); |
1297 |
|
|
fail: |
1298 |
|
|
server_abort_http(clt, 400, "bad request"); |
1299 |
|
|
return (-1); |
1300 |
|
|
} |
1301 |
|
|
|
1302 |
|
|
const char * |
1303 |
|
|
server_root_strip(const char *path, int n) |
1304 |
|
|
{ |
1305 |
|
|
const char *p; |
1306 |
|
|
|
1307 |
|
|
/* Strip strip leading directories. Leading '/' is ignored. */ |
1308 |
|
|
for (; n > 0 && *path != '\0'; n--) |
1309 |
|
|
if ((p = strchr(++path, '/')) == NULL) |
1310 |
|
|
path = strchr(path, '\0'); |
1311 |
|
|
else |
1312 |
|
|
path = p; |
1313 |
|
|
|
1314 |
|
|
return (path); |
1315 |
|
|
} |
1316 |
|
|
|
1317 |
|
|
struct server_config * |
1318 |
|
|
server_getlocation(struct client *clt, const char *path) |
1319 |
|
|
{ |
1320 |
|
|
struct server *srv = clt->clt_srv; |
1321 |
|
|
struct server_config *srv_conf = clt->clt_srv_conf, *location; |
1322 |
|
|
const char *errstr = NULL; |
1323 |
|
|
int ret; |
1324 |
|
|
|
1325 |
|
|
/* Now search for the location */ |
1326 |
|
|
TAILQ_FOREACH(location, &srv->srv_hosts, entry) { |
1327 |
|
|
#ifdef DEBUG |
1328 |
|
|
if (location->flags & SRVFLAG_LOCATION) { |
1329 |
|
|
DPRINTF("%s: location \"%s\" path \"%s\"", |
1330 |
|
|
__func__, location->location, path); |
1331 |
|
|
} |
1332 |
|
|
#endif |
1333 |
|
|
if ((location->flags & SRVFLAG_LOCATION) && |
1334 |
|
|
location->parent_id == srv_conf->parent_id) { |
1335 |
|
|
errstr = NULL; |
1336 |
|
|
if (location->flags & SRVFLAG_LOCATION_MATCH) { |
1337 |
|
|
ret = str_match(path, location->location, |
1338 |
|
|
&clt->clt_srv_match, &errstr); |
1339 |
|
|
} else { |
1340 |
|
|
ret = fnmatch(location->location, |
1341 |
|
|
path, FNM_CASEFOLD); |
1342 |
|
|
} |
1343 |
|
|
if (ret == 0 && errstr == NULL) { |
1344 |
|
|
/* Replace host configuration */ |
1345 |
|
|
clt->clt_srv_conf = srv_conf = location; |
1346 |
|
|
break; |
1347 |
|
|
} |
1348 |
|
|
} |
1349 |
|
|
} |
1350 |
|
|
|
1351 |
|
|
return (srv_conf); |
1352 |
|
|
} |
1353 |
|
|
|
1354 |
|
|
int |
1355 |
|
|
server_response_http(struct client *clt, unsigned int code, |
1356 |
|
|
struct media_type *media, off_t size, time_t mtime) |
1357 |
|
|
{ |
1358 |
|
|
struct server_config *srv_conf = clt->clt_srv_conf; |
1359 |
|
|
struct http_descriptor *desc = clt->clt_descreq; |
1360 |
|
|
struct http_descriptor *resp = clt->clt_descresp; |
1361 |
|
|
const char *error; |
1362 |
|
|
struct kv *ct, *cl; |
1363 |
|
|
char tmbuf[32]; |
1364 |
|
|
|
1365 |
|
|
if (desc == NULL || media == NULL || |
1366 |
|
|
(error = server_httperror_byid(code)) == NULL) |
1367 |
|
|
return (-1); |
1368 |
|
|
|
1369 |
|
|
if (server_log_http(clt, code, size) == -1) |
1370 |
|
|
return (-1); |
1371 |
|
|
|
1372 |
|
|
/* Add error codes */ |
1373 |
|
|
if (kv_setkey(&resp->http_pathquery, "%u", code) == -1 || |
1374 |
|
|
kv_set(&resp->http_pathquery, "%s", error) == -1) |
1375 |
|
|
return (-1); |
1376 |
|
|
|
1377 |
|
|
/* Add headers */ |
1378 |
|
|
if (kv_add(&resp->http_headers, "Server", HTTPD_SERVERNAME) == NULL) |
1379 |
|
|
return (-1); |
1380 |
|
|
|
1381 |
|
|
/* Is it a persistent connection? */ |
1382 |
|
|
if (clt->clt_persist) { |
1383 |
|
|
if (kv_add(&resp->http_headers, |
1384 |
|
|
"Connection", "keep-alive") == NULL) |
1385 |
|
|
return (-1); |
1386 |
|
|
} else if (kv_add(&resp->http_headers, "Connection", "close") == NULL) |
1387 |
|
|
return (-1); |
1388 |
|
|
|
1389 |
|
|
/* Set media type */ |
1390 |
|
|
if ((ct = kv_add(&resp->http_headers, "Content-Type", NULL)) == NULL || |
1391 |
|
|
kv_set(ct, "%s/%s", media->media_type, media->media_subtype) == -1) |
1392 |
|
|
return (-1); |
1393 |
|
|
|
1394 |
|
|
/* Set content length, if specified */ |
1395 |
|
|
if ((cl = |
1396 |
|
|
kv_add(&resp->http_headers, "Content-Length", NULL)) == NULL || |
1397 |
|
|
kv_set(cl, "%lld", (long long)size) == -1) |
1398 |
|
|
return (-1); |
1399 |
|
|
|
1400 |
|
|
/* Set last modification time */ |
1401 |
|
|
if (server_http_time(mtime, tmbuf, sizeof(tmbuf)) <= 0 || |
1402 |
|
|
kv_add(&resp->http_headers, "Last-Modified", tmbuf) == NULL) |
1403 |
|
|
return (-1); |
1404 |
|
|
|
1405 |
|
|
/* HSTS header */ |
1406 |
|
|
if (srv_conf->flags & SRVFLAG_SERVER_HSTS) { |
1407 |
|
|
if ((cl = |
1408 |
|
|
kv_add(&resp->http_headers, "Strict-Transport-Security", |
1409 |
|
|
NULL)) == NULL || |
1410 |
|
|
kv_set(cl, "max-age=%d%s%s", srv_conf->hsts_max_age, |
1411 |
|
|
srv_conf->hsts_flags & HSTSFLAG_SUBDOMAINS ? |
1412 |
|
|
"; includeSubDomains" : "", |
1413 |
|
|
srv_conf->hsts_flags & HSTSFLAG_PRELOAD ? |
1414 |
|
|
"; preload" : "") == -1) |
1415 |
|
|
return (-1); |
1416 |
|
|
} |
1417 |
|
|
|
1418 |
|
|
/* Date header is mandatory and should be added as late as possible */ |
1419 |
|
|
if (server_http_time(time(NULL), tmbuf, sizeof(tmbuf)) <= 0 || |
1420 |
|
|
kv_add(&resp->http_headers, "Date", tmbuf) == NULL) |
1421 |
|
|
return (-1); |
1422 |
|
|
|
1423 |
|
|
/* Write completed header */ |
1424 |
|
|
if (server_writeresponse_http(clt) == -1 || |
1425 |
|
|
server_bufferevent_print(clt, "\r\n") == -1 || |
1426 |
|
|
server_headers(clt, resp, server_writeheader_http, NULL) == -1 || |
1427 |
|
|
server_bufferevent_print(clt, "\r\n") == -1) |
1428 |
|
|
return (-1); |
1429 |
|
|
|
1430 |
|
|
if (size == 0 || resp->http_method == HTTP_METHOD_HEAD) { |
1431 |
|
|
bufferevent_enable(clt->clt_bev, EV_READ|EV_WRITE); |
1432 |
|
|
if (clt->clt_persist) |
1433 |
|
|
clt->clt_toread = TOREAD_HTTP_HEADER; |
1434 |
|
|
else |
1435 |
|
|
clt->clt_toread = TOREAD_HTTP_NONE; |
1436 |
|
|
clt->clt_done = 0; |
1437 |
|
|
return (0); |
1438 |
|
|
} |
1439 |
|
|
|
1440 |
|
|
return (1); |
1441 |
|
|
} |
1442 |
|
|
|
1443 |
|
|
int |
1444 |
|
|
server_writeresponse_http(struct client *clt) |
1445 |
|
|
{ |
1446 |
|
|
struct http_descriptor *desc = clt->clt_descresp; |
1447 |
|
|
|
1448 |
|
|
DPRINTF("version: %s rescode: %s resmsg: %s", desc->http_version, |
1449 |
|
|
desc->http_rescode, desc->http_resmesg); |
1450 |
|
|
|
1451 |
|
|
if (server_bufferevent_print(clt, desc->http_version) == -1 || |
1452 |
|
|
server_bufferevent_print(clt, " ") == -1 || |
1453 |
|
|
server_bufferevent_print(clt, desc->http_rescode) == -1 || |
1454 |
|
|
server_bufferevent_print(clt, " ") == -1 || |
1455 |
|
|
server_bufferevent_print(clt, desc->http_resmesg) == -1) |
1456 |
|
|
return (-1); |
1457 |
|
|
|
1458 |
|
|
return (0); |
1459 |
|
|
} |
1460 |
|
|
|
1461 |
|
|
int |
1462 |
|
|
server_writeheader_http(struct client *clt, struct kv *hdr, void *arg) |
1463 |
|
|
{ |
1464 |
|
|
char *ptr; |
1465 |
|
|
const char *key; |
1466 |
|
|
|
1467 |
|
|
if (hdr->kv_flags & KV_FLAG_INVALID) |
1468 |
|
|
return (0); |
1469 |
|
|
|
1470 |
|
|
/* The key might have been updated in the parent */ |
1471 |
|
|
if (hdr->kv_parent != NULL && hdr->kv_parent->kv_key != NULL) |
1472 |
|
|
key = hdr->kv_parent->kv_key; |
1473 |
|
|
else |
1474 |
|
|
key = hdr->kv_key; |
1475 |
|
|
|
1476 |
|
|
ptr = hdr->kv_value; |
1477 |
|
|
if (server_bufferevent_print(clt, key) == -1 || |
1478 |
|
|
(ptr != NULL && |
1479 |
|
|
(server_bufferevent_print(clt, ": ") == -1 || |
1480 |
|
|
server_bufferevent_print(clt, ptr) == -1 || |
1481 |
|
|
server_bufferevent_print(clt, "\r\n") == -1))) |
1482 |
|
|
return (-1); |
1483 |
|
|
DPRINTF("%s: %s: %s", __func__, key, |
1484 |
|
|
hdr->kv_value == NULL ? "" : hdr->kv_value); |
1485 |
|
|
|
1486 |
|
|
return (0); |
1487 |
|
|
} |
1488 |
|
|
|
1489 |
|
|
int |
1490 |
|
|
server_headers(struct client *clt, void *descp, |
1491 |
|
|
int (*hdr_cb)(struct client *, struct kv *, void *), void *arg) |
1492 |
|
|
{ |
1493 |
|
|
struct kv *hdr, *kv; |
1494 |
|
|
struct http_descriptor *desc = descp; |
1495 |
|
|
|
1496 |
|
|
RB_FOREACH(hdr, kvtree, &desc->http_headers) { |
1497 |
|
|
if ((hdr_cb)(clt, hdr, arg) == -1) |
1498 |
|
|
return (-1); |
1499 |
|
|
TAILQ_FOREACH(kv, &hdr->kv_children, kv_entry) { |
1500 |
|
|
if ((hdr_cb)(clt, kv, arg) == -1) |
1501 |
|
|
return (-1); |
1502 |
|
|
} |
1503 |
|
|
} |
1504 |
|
|
|
1505 |
|
|
return (0); |
1506 |
|
|
} |
1507 |
|
|
|
1508 |
|
|
enum httpmethod |
1509 |
|
|
server_httpmethod_byname(const char *name) |
1510 |
|
|
{ |
1511 |
|
|
enum httpmethod id = HTTP_METHOD_NONE; |
1512 |
|
|
struct http_method method, *res = NULL; |
1513 |
|
|
|
1514 |
|
|
/* Set up key */ |
1515 |
|
|
method.method_name = name; |
1516 |
|
|
|
1517 |
|
|
if ((res = bsearch(&method, http_methods, |
1518 |
|
|
sizeof(http_methods) / sizeof(http_methods[0]) - 1, |
1519 |
|
|
sizeof(http_methods[0]), server_httpmethod_cmp)) != NULL) |
1520 |
|
|
id = res->method_id; |
1521 |
|
|
|
1522 |
|
|
return (id); |
1523 |
|
|
} |
1524 |
|
|
|
1525 |
|
|
const char * |
1526 |
|
|
server_httpmethod_byid(unsigned int id) |
1527 |
|
|
{ |
1528 |
|
|
const char *name = "<UNKNOWN>"; |
1529 |
|
|
int i; |
1530 |
|
|
|
1531 |
|
|
for (i = 0; http_methods[i].method_name != NULL; i++) { |
1532 |
|
|
if (http_methods[i].method_id == id) { |
1533 |
|
|
name = http_methods[i].method_name; |
1534 |
|
|
break; |
1535 |
|
|
} |
1536 |
|
|
} |
1537 |
|
|
|
1538 |
|
|
return (name); |
1539 |
|
|
} |
1540 |
|
|
|
1541 |
|
|
static int |
1542 |
|
|
server_httpmethod_cmp(const void *a, const void *b) |
1543 |
|
|
{ |
1544 |
|
|
const struct http_method *ma = a; |
1545 |
|
|
const struct http_method *mb = b; |
1546 |
|
|
|
1547 |
|
|
/* |
1548 |
|
|
* RFC 2616 section 5.1.1 says that the method is case |
1549 |
|
|
* sensitive so we don't do a strcasecmp here. |
1550 |
|
|
*/ |
1551 |
|
|
return (strcmp(ma->method_name, mb->method_name)); |
1552 |
|
|
} |
1553 |
|
|
|
1554 |
|
|
const char * |
1555 |
|
|
server_httperror_byid(unsigned int id) |
1556 |
|
|
{ |
1557 |
|
|
struct http_error error, *res; |
1558 |
|
|
|
1559 |
|
|
/* Set up key */ |
1560 |
|
|
error.error_code = (int)id; |
1561 |
|
|
|
1562 |
|
|
if ((res = bsearch(&error, http_errors, |
1563 |
|
|
sizeof(http_errors) / sizeof(http_errors[0]) - 1, |
1564 |
|
|
sizeof(http_errors[0]), server_httperror_cmp)) != NULL) |
1565 |
|
|
return (res->error_name); |
1566 |
|
|
|
1567 |
|
|
return (NULL); |
1568 |
|
|
} |
1569 |
|
|
|
1570 |
|
|
static int |
1571 |
|
|
server_httperror_cmp(const void *a, const void *b) |
1572 |
|
|
{ |
1573 |
|
|
const struct http_error *ea = a; |
1574 |
|
|
const struct http_error *eb = b; |
1575 |
|
|
return (ea->error_code - eb->error_code); |
1576 |
|
|
} |
1577 |
|
|
|
1578 |
|
|
int |
1579 |
|
|
server_log_http(struct client *clt, unsigned int code, size_t len) |
1580 |
|
|
{ |
1581 |
|
|
static char tstamp[64]; |
1582 |
|
|
static char ip[INET6_ADDRSTRLEN]; |
1583 |
|
|
time_t t; |
1584 |
|
|
struct kv key, *agent, *referrer; |
1585 |
|
|
struct tm *tm; |
1586 |
|
|
struct server_config *srv_conf; |
1587 |
|
|
struct http_descriptor *desc; |
1588 |
|
|
int ret = -1; |
1589 |
|
|
char *user = NULL; |
1590 |
|
|
char *path = NULL; |
1591 |
|
|
char *query = NULL; |
1592 |
|
|
char *version = NULL; |
1593 |
|
|
char *referrer_v = NULL; |
1594 |
|
|
char *agent_v = NULL; |
1595 |
|
|
|
1596 |
|
|
if ((srv_conf = clt->clt_srv_conf) == NULL) |
1597 |
|
|
return (-1); |
1598 |
|
|
if ((srv_conf->flags & SRVFLAG_LOG) == 0) |
1599 |
|
|
return (0); |
1600 |
|
|
if ((desc = clt->clt_descreq) == NULL) |
1601 |
|
|
return (-1); |
1602 |
|
|
|
1603 |
|
|
if ((t = time(NULL)) == -1) |
1604 |
|
|
return (-1); |
1605 |
|
|
if ((tm = localtime(&t)) == NULL) |
1606 |
|
|
return (-1); |
1607 |
|
|
if (strftime(tstamp, sizeof(tstamp), "%d/%b/%Y:%H:%M:%S %z", tm) == 0) |
1608 |
|
|
return (-1); |
1609 |
|
|
|
1610 |
|
|
if (print_host(&clt->clt_ss, ip, sizeof(ip)) == NULL) |
1611 |
|
|
return (-1); |
1612 |
|
|
|
1613 |
|
|
/* |
1614 |
|
|
* For details on common log format, see: |
1615 |
|
|
* https://httpd.apache.org/docs/current/mod/mod_log_config.html |
1616 |
|
|
* |
1617 |
|
|
* httpd's format is similar to these Apache LogFormats: |
1618 |
|
|
* "%v %h %l %u %t \"%r\" %>s %B" |
1619 |
|
|
* "%v %h %l %u %t \"%r\" %>s %B \"%{Referer}i\" \"%{User-agent}i\"" |
1620 |
|
|
*/ |
1621 |
|
|
switch (srv_conf->logformat) { |
1622 |
|
|
case LOG_FORMAT_COMMON: |
1623 |
|
|
/* Use vis to encode input values from the header */ |
1624 |
|
|
if (clt->clt_remote_user && |
1625 |
|
|
stravis(&user, clt->clt_remote_user, HTTPD_LOGVIS) == -1) |
1626 |
|
|
goto done; |
1627 |
|
|
if (desc->http_version && |
1628 |
|
|
stravis(&version, desc->http_version, HTTPD_LOGVIS) == -1) |
1629 |
|
|
goto done; |
1630 |
|
|
|
1631 |
|
|
/* The following should be URL-encoded */ |
1632 |
|
|
if (desc->http_path && |
1633 |
|
|
(path = url_encode(desc->http_path)) == NULL) |
1634 |
|
|
goto done; |
1635 |
|
|
if (desc->http_query && |
1636 |
|
|
(query = url_encode(desc->http_query)) == NULL) |
1637 |
|
|
goto done; |
1638 |
|
|
|
1639 |
|
|
ret = evbuffer_add_printf(clt->clt_log, |
1640 |
|
|
"%s %s - %s [%s] \"%s %s%s%s%s%s\" %03d %zu\n", |
1641 |
|
|
srv_conf->name, ip, clt->clt_remote_user == NULL ? "-" : |
1642 |
|
|
user, tstamp, |
1643 |
|
|
server_httpmethod_byid(desc->http_method), |
1644 |
|
|
desc->http_path == NULL ? "" : path, |
1645 |
|
|
desc->http_query == NULL ? "" : "?", |
1646 |
|
|
desc->http_query == NULL ? "" : query, |
1647 |
|
|
desc->http_version == NULL ? "" : " ", |
1648 |
|
|
desc->http_version == NULL ? "" : version, |
1649 |
|
|
code, len); |
1650 |
|
|
|
1651 |
|
|
break; |
1652 |
|
|
|
1653 |
|
|
case LOG_FORMAT_COMBINED: |
1654 |
|
|
key.kv_key = "Referer"; /* sic */ |
1655 |
|
|
if ((referrer = kv_find(&desc->http_headers, &key)) != NULL && |
1656 |
|
|
referrer->kv_value == NULL) |
1657 |
|
|
referrer = NULL; |
1658 |
|
|
|
1659 |
|
|
key.kv_key = "User-Agent"; |
1660 |
|
|
if ((agent = kv_find(&desc->http_headers, &key)) != NULL && |
1661 |
|
|
agent->kv_value == NULL) |
1662 |
|
|
agent = NULL; |
1663 |
|
|
|
1664 |
|
|
/* Use vis to encode input values from the header */ |
1665 |
|
|
if (clt->clt_remote_user && |
1666 |
|
|
stravis(&user, clt->clt_remote_user, HTTPD_LOGVIS) == -1) |
1667 |
|
|
goto done; |
1668 |
|
|
if (desc->http_version && |
1669 |
|
|
stravis(&version, desc->http_version, HTTPD_LOGVIS) == -1) |
1670 |
|
|
goto done; |
1671 |
|
|
if (agent && |
1672 |
|
|
stravis(&agent_v, agent->kv_value, HTTPD_LOGVIS) == -1) |
1673 |
|
|
goto done; |
1674 |
|
|
|
1675 |
|
|
/* The following should be URL-encoded */ |
1676 |
|
|
if (desc->http_path && |
1677 |
|
|
(path = url_encode(desc->http_path)) == NULL) |
1678 |
|
|
goto done; |
1679 |
|
|
if (desc->http_query && |
1680 |
|
|
(query = url_encode(desc->http_query)) == NULL) |
1681 |
|
|
goto done; |
1682 |
|
|
if (referrer && |
1683 |
|
|
(referrer_v = url_encode(referrer->kv_value)) == NULL) |
1684 |
|
|
goto done; |
1685 |
|
|
|
1686 |
|
|
ret = evbuffer_add_printf(clt->clt_log, |
1687 |
|
|
"%s %s - %s [%s] \"%s %s%s%s%s%s\"" |
1688 |
|
|
" %03d %zu \"%s\" \"%s\"\n", |
1689 |
|
|
srv_conf->name, ip, clt->clt_remote_user == NULL ? "-" : |
1690 |
|
|
user, tstamp, |
1691 |
|
|
server_httpmethod_byid(desc->http_method), |
1692 |
|
|
desc->http_path == NULL ? "" : path, |
1693 |
|
|
desc->http_query == NULL ? "" : "?", |
1694 |
|
|
desc->http_query == NULL ? "" : query, |
1695 |
|
|
desc->http_version == NULL ? "" : " ", |
1696 |
|
|
desc->http_version == NULL ? "" : version, |
1697 |
|
|
code, len, |
1698 |
|
|
referrer == NULL ? "" : referrer_v, |
1699 |
|
|
agent == NULL ? "" : agent_v); |
1700 |
|
|
|
1701 |
|
|
break; |
1702 |
|
|
|
1703 |
|
|
case LOG_FORMAT_CONNECTION: |
1704 |
|
|
/* URL-encode the path */ |
1705 |
|
|
if (desc->http_path && |
1706 |
|
|
(path = url_encode(desc->http_path)) == NULL) |
1707 |
|
|
goto done; |
1708 |
|
|
|
1709 |
|
|
ret = evbuffer_add_printf(clt->clt_log, " [%s]", |
1710 |
|
|
desc->http_path == NULL ? "" : path); |
1711 |
|
|
|
1712 |
|
|
break; |
1713 |
|
|
} |
1714 |
|
|
|
1715 |
|
|
done: |
1716 |
|
|
free(user); |
1717 |
|
|
free(path); |
1718 |
|
|
free(query); |
1719 |
|
|
free(version); |
1720 |
|
|
free(referrer_v); |
1721 |
|
|
free(agent_v); |
1722 |
|
|
|
1723 |
|
|
return (ret); |
1724 |
|
|
} |