Bug Summary

File:src/usr.sbin/httpd/server_file.c
Warning:line 235, column 8
Although the value stored to 'ret' is used in the enclosing expression, the value is never actually read from 'ret'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name server_file.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/httpd/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.sbin/httpd -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/httpd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/httpd/server_file.c
1/* $OpenBSD: server_file.c,v 1.70 2021/04/29 18:23:07 dv 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/time.h>
21#include <sys/stat.h>
22
23#include <limits.h>
24#include <errno(*__errno()).h>
25#include <fcntl.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <stdio.h>
30#include <dirent.h>
31#include <time.h>
32#include <event.h>
33
34#include "httpd.h"
35#include "http.h"
36
37#define MINIMUM(a, b)(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b))
38#define MAXIMUM(a, b)(((a) > (b)) ? (a) : (b)) (((a) > (b)) ? (a) : (b))
39
40int server_file_access(struct httpd *, struct client *,
41 char *, size_t);
42int server_file_request(struct httpd *, struct client *,
43 char *, struct stat *);
44int server_partial_file_request(struct httpd *, struct client *,
45 char *, struct stat *, char *);
46int server_file_index(struct httpd *, struct client *,
47 struct stat *);
48int server_file_modified_since(struct http_descriptor *,
49 struct stat *);
50int server_file_method(struct client *);
51int parse_range_spec(char *, size_t, struct range *);
52int parse_ranges(struct client *, char *, size_t);
53
54int
55server_file_access(struct httpd *env, struct client *clt,
56 char *path, size_t len)
57{
58 struct http_descriptor *desc = clt->clt_descreq;
59 struct server_config *srv_conf = clt->clt_srv_conf;
60 struct stat st;
61 struct kv *r, key;
62 char *newpath, *encodedpath;
63 int ret;
64
65 errno(*__errno()) = 0;
66
67 if (access(path, R_OK0x04) == -1) {
68 goto fail;
69 } else if (stat(path, &st) == -1) {
70 goto fail;
71 } else if (S_ISDIR(st.st_mode)((st.st_mode & 0170000) == 0040000)) {
72 /* Deny access if directory indexing is disabled */
73 if (srv_conf->flags & SRVFLAG_NO_INDEX0x00000002) {
74 errno(*__errno()) = EACCES13;
75 goto fail;
76 }
77
78 if (desc->http_path_alias != NULL((void*)0)) {
79 /* Recursion - the index "file" is a directory? */
80 errno(*__errno()) = EINVAL22;
81 goto fail;
82 }
83
84 /* Redirect to path with trailing "/" */
85 if (path[strlen(path) - 1] != '/') {
86 if ((encodedpath = url_encode(desc->http_pathhttp_pathquery.kv_key)) == NULL((void*)0))
87 return (500);
88 if (asprintf(&newpath, "%s/", encodedpath) == -1) {
89 free(encodedpath);
90 return (500);
91 }
92 free(encodedpath);
93
94 /* Path alias will be used for the redirection */
95 desc->http_path_alias = newpath;
96
97 /* Indicate that the file has been moved */
98 return (301);
99 }
100
101 /* Append the default index file to the location */
102 if (asprintf(&newpath, "%s%s", desc->http_pathhttp_pathquery.kv_key,
103 srv_conf->index) == -1)
104 return (500);
105 desc->http_path_alias = newpath;
106 if (server_getlocation(clt, newpath) != srv_conf) {
107 /* The location has changed */
108 return (server_file(env, clt));
109 }
110
111 /* Otherwise append the default index file to the path */
112 if (strlcat(path, srv_conf->index, len) >= len) {
113 errno(*__errno()) = EACCES13;
114 goto fail;
115 }
116
117 ret = server_file_access(env, clt, path, len);
118 if (ret == 404) {
119 /*
120 * Index file not found; fail if auto-indexing is
121 * not enabled, otherwise return success but
122 * indicate directory with S_ISDIR of the previous
123 * stat.
124 */
125 if ((srv_conf->flags & SRVFLAG_AUTO_INDEX0x00000004) == 0) {
126 errno(*__errno()) = EACCES13;
127 goto fail;
128 }
129
130 return (server_file_index(env, clt, &st));
131 }
132 return (ret);
133 } else if (!S_ISREG(st.st_mode)((st.st_mode & 0170000) == 0100000)) {
134 /* Don't follow symlinks and ignore special files */
135 errno(*__errno()) = EACCES13;
136 goto fail;
137 }
138
139 key.kv_key = "Range";
140 r = kv_find(&desc->http_headers, &key);
141 if (r != NULL((void*)0))
142 return (server_partial_file_request(env, clt, path, &st,
143 r->kv_value));
144 else
145 return (server_file_request(env, clt, path, &st));
146
147 fail:
148 switch (errno(*__errno())) {
149 case ENOENT2:
150 case ENOTDIR20:
151 return (404);
152 case EACCES13:
153 return (403);
154 default:
155 return (500);
156 }
157
158 /* NOTREACHED */
159}
160
161int
162server_file(struct httpd *env, struct client *clt)
163{
164 struct http_descriptor *desc = clt->clt_descreq;
165 struct server_config *srv_conf = clt->clt_srv_conf;
166 char path[PATH_MAX1024];
167 const char *stripped, *errstr = NULL((void*)0);
168 int ret = 500;
169
170 if (srv_conf->flags & SRVFLAG_FCGI0x00000040)
171 return (server_fcgi(env, clt));
172
173 /* Request path is already canonicalized */
174 stripped = server_root_strip(
175 desc->http_path_alias != NULL((void*)0) ?
176 desc->http_path_alias : desc->http_pathhttp_pathquery.kv_key,
177 srv_conf->strip);
178 if ((size_t)snprintf(path, sizeof(path), "%s%s",
179 srv_conf->root, stripped) >= sizeof(path)) {
180 errstr = desc->http_pathhttp_pathquery.kv_key;
181 goto abort;
182 }
183
184 /* Returns HTTP status code on error */
185 if ((ret = server_file_access(env, clt, path, sizeof(path))) > 0) {
186 errstr = desc->http_path_alias != NULL((void*)0) ?
187 desc->http_path_alias : desc->http_pathhttp_pathquery.kv_key;
188 goto abort;
189 }
190
191 return (ret);
192
193 abort:
194 if (errstr == NULL((void*)0))
195 errstr = strerror(errno(*__errno()));
196 server_abort_http(clt, ret, errstr);
197 return (-1);
198}
199
200int
201server_file_method(struct client *clt)
202{
203 struct http_descriptor *desc = clt->clt_descreq;
204
205 switch (desc->http_method) {
206 case HTTP_METHOD_GET:
207 case HTTP_METHOD_HEAD:
208 return (0);
209 default:
210 /* Other methods are not allowed */
211 errno(*__errno()) = EACCES13;
212 return (405);
213 }
214 /* NOTREACHED */
215}
216
217int
218server_file_request(struct httpd *env, struct client *clt, char *path,
219 struct stat *st)
220{
221 struct server_config *srv_conf = clt->clt_srv_conf;
222 struct media_type *media;
223 const char *errstr = NULL((void*)0);
224 int fd = -1, ret, code = 500;
225 size_t bufsiz;
226
227 if ((ret = server_file_method(clt)) != 0) {
228 code = ret;
229 goto abort;
230 }
231
232 if ((ret = server_file_modified_since(clt->clt_descreq, st)) != -1) {
233 /* send the header without a body */
234 media = media_find_config(env, srv_conf, path);
235 if ((ret = server_response_http(clt, ret, media, -1,
Although the value stored to 'ret' is used in the enclosing expression, the value is never actually read from 'ret'
236 MINIMUM(time(NULL), st->st_mtim.tv_sec)(((time(((void*)0))) < (st->st_mtim.tv_sec)) ? (time(((
void*)0))) : (st->st_mtim.tv_sec))
)
) == -1)
237 goto fail;
238 goto done;
239 }
240
241 /* Now open the file, should be readable or we have another problem */
242 if ((fd = open(path, O_RDONLY0x0000)) == -1)
243 goto abort;
244
245 media = media_find_config(env, srv_conf, path);
246 ret = server_response_http(clt, 200, media, st->st_size,
247 MINIMUM(time(NULL), st->st_mtim.tv_sec)(((time(((void*)0))) < (st->st_mtim.tv_sec)) ? (time(((
void*)0))) : (st->st_mtim.tv_sec))
);
248 switch (ret) {
249 case -1:
250 goto fail;
251 case 0:
252 /* Connection is already finished */
253 close(fd);
254 goto done;
255 default:
256 break;
257 }
258
259 clt->clt_fd = fd;
260 if (clt->clt_srvbev != NULL((void*)0))
261 bufferevent_free(clt->clt_srvbev);
262
263 clt->clt_srvbev_throttled = 0;
264 clt->clt_srvbev = bufferevent_new(clt->clt_fd, server_read,
265 server_write, server_file_error, clt);
266 if (clt->clt_srvbev == NULL((void*)0)) {
267 errstr = "failed to allocate file buffer event";
268 goto fail;
269 }
270
271 /* Adjust read watermark to the optimal file io size */
272 bufsiz = MAXIMUM(st->st_blksize, 64 * 1024)(((st->st_blksize) > (64 * 1024)) ? (st->st_blksize)
: (64 * 1024))
;
273 bufferevent_setwatermark(clt->clt_srvbev, EV_READ0x02, 0,
274 bufsiz);
275
276 bufferevent_settimeout(clt->clt_srvbev,
277 srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec);
278 bufferevent_enable(clt->clt_srvbev, EV_READ0x02);
279 bufferevent_disable(clt->clt_bev, EV_READ0x02);
280
281 done:
282 server_reset_http(clt);
283 return (0);
284 fail:
285 bufferevent_disable(clt->clt_bev, EV_READ0x02|EV_WRITE0x04);
286 bufferevent_free(clt->clt_bev);
287 clt->clt_bev = NULL((void*)0);
288 abort:
289 if (fd != -1)
290 close(fd);
291 if (errstr == NULL((void*)0))
292 errstr = strerror(errno(*__errno()));
293 server_abort_http(clt, code, errstr);
294 return (-1);
295}
296
297int
298server_partial_file_request(struct httpd *env, struct client *clt, char *path,
299 struct stat *st, char *range_str)
300{
301 struct server_config *srv_conf = clt->clt_srv_conf;
302 struct http_descriptor *resp = clt->clt_descresp;
303 struct http_descriptor *desc = clt->clt_descreq;
304 struct media_type *media, multipart_media;
305 struct range_data *r = &clt->clt_ranges;
306 struct range *range;
307 size_t content_length = 0, bufsiz;
308 int code = 500, fd = -1, i, nranges, ret;
309 char content_range[64];
310 const char *errstr = NULL((void*)0);
311
312 /* Ignore range request for methods other than GET */
313 if (desc->http_method != HTTP_METHOD_GET)
314 return server_file_request(env, clt, path, st);
315
316 if ((nranges = parse_ranges(clt, range_str, st->st_size)) < 1) {
317 code = 416;
318 (void)snprintf(content_range, sizeof(content_range),
319 "bytes */%lld", st->st_size);
320 errstr = content_range;
321 goto abort;
322 }
323
324 /* Now open the file, should be readable or we have another problem */
325 if ((fd = open(path, O_RDONLY0x0000)) == -1)
326 goto abort;
327
328 media = media_find_config(env, srv_conf, path);
329 r->range_media = media;
330
331 if (nranges == 1) {
332 range = &r->range[0];
333 (void)snprintf(content_range, sizeof(content_range),
334 "bytes %lld-%lld/%lld", range->start, range->end,
335 st->st_size);
336 if (kv_add(&resp->http_headers, "Content-Range",
337 content_range) == NULL((void*)0))
338 goto abort;
339
340 range = &r->range[0];
341 content_length += range->end - range->start + 1;
342 } else {
343 /* Add boundary, all parts will be handled by the callback */
344 arc4random_buf(&clt->clt_boundary, sizeof(clt->clt_boundary));
345
346 /* Calculate Content-Length of the complete multipart body */
347 for (i = 0; i < nranges; i++) {
348 range = &r->range[i];
349
350 /* calculate Content-Length of the complete body */
351 if ((ret = snprintf(NULL((void*)0), 0,
352 "\r\n--%llu\r\n"
353 "Content-Type: %s/%s\r\n"
354 "Content-Range: bytes %lld-%lld/%lld\r\n\r\n",
355 clt->clt_boundary,
356 media->media_type, media->media_subtype,
357 range->start, range->end, st->st_size)) < 0)
358 goto abort;
359
360 /* Add data length */
361 content_length += ret + range->end - range->start + 1;
362
363 }
364 if ((ret = snprintf(NULL((void*)0), 0, "\r\n--%llu--\r\n",
365 clt->clt_boundary)) < 0)
366 goto abort;
367 content_length += ret;
368
369 /* prepare multipart/byteranges media type */
370 (void)strlcpy(multipart_media.media_type, "multipart",
371 sizeof(multipart_media.media_type));
372 (void)snprintf(multipart_media.media_subtype,
373 sizeof(multipart_media.media_subtype),
374 "byteranges; boundary=%llu", clt->clt_boundary);
375 media = &multipart_media;
376 }
377
378 /* Start with first range */
379 r->range_toread = TOREAD_HTTP_RANGE;
380
381 ret = server_response_http(clt, 206, media, content_length,
382 MINIMUM(time(NULL), st->st_mtim.tv_sec)(((time(((void*)0))) < (st->st_mtim.tv_sec)) ? (time(((
void*)0))) : (st->st_mtim.tv_sec))
);
383 switch (ret) {
384 case -1:
385 goto fail;
386 case 0:
387 /* Connection is already finished */
388 close(fd);
389 goto done;
390 default:
391 break;
392 }
393
394 clt->clt_fd = fd;
395 if (clt->clt_srvbev != NULL((void*)0))
396 bufferevent_free(clt->clt_srvbev);
397
398 clt->clt_srvbev_throttled = 0;
399 clt->clt_srvbev = bufferevent_new(clt->clt_fd, server_read_httprange,
400 server_write, server_file_error, clt);
401 if (clt->clt_srvbev == NULL((void*)0)) {
402 errstr = "failed to allocate file buffer event";
403 goto fail;
404 }
405
406 /* Adjust read watermark to the optimal file io size */
407 bufsiz = MAXIMUM(st->st_blksize, 64 * 1024)(((st->st_blksize) > (64 * 1024)) ? (st->st_blksize)
: (64 * 1024))
;
408 bufferevent_setwatermark(clt->clt_srvbev, EV_READ0x02, 0,
409 bufsiz);
410
411 bufferevent_settimeout(clt->clt_srvbev,
412 srv_conf->timeout.tv_sec, srv_conf->timeout.tv_sec);
413 bufferevent_enable(clt->clt_srvbev, EV_READ0x02);
414 bufferevent_disable(clt->clt_bev, EV_READ0x02);
415
416 done:
417 server_reset_http(clt);
418 return (0);
419 fail:
420 bufferevent_disable(clt->clt_bev, EV_READ0x02|EV_WRITE0x04);
421 bufferevent_free(clt->clt_bev);
422 clt->clt_bev = NULL((void*)0);
423 abort:
424 if (fd != -1)
425 close(fd);
426 if (errstr == NULL((void*)0))
427 errstr = strerror(errno(*__errno()));
428 server_abort_http(clt, code, errstr);
429 return (-1);
430}
431
432int
433server_file_index(struct httpd *env, struct client *clt, struct stat *st)
434{
435 char path[PATH_MAX1024];
436 char tmstr[21];
437 struct http_descriptor *desc = clt->clt_descreq;
438 struct server_config *srv_conf = clt->clt_srv_conf;
439 struct dirent **namelist, *dp;
440 int namesize, i, ret, fd = -1, namewidth, skip;
441 int code = 500;
442 struct evbuffer *evb = NULL((void*)0);
443 struct media_type *media;
444 const char *stripped, *style;
445 char *escapeduri, *escapedhtml, *escapedpath;
446 struct tm tm;
447 time_t t, dir_mtime;
448
449 if ((ret = server_file_method(clt)) != 0) {
450 code = ret;
451 goto abort;
452 }
453
454 /* Request path is already canonicalized */
455 stripped = server_root_strip(desc->http_pathhttp_pathquery.kv_key, srv_conf->strip);
456 if ((size_t)snprintf(path, sizeof(path), "%s%s",
457 srv_conf->root, stripped) >= sizeof(path))
458 goto abort;
459
460 /* Now open the file, should be readable or we have another problem */
461 if ((fd = open(path, O_RDONLY0x0000)) == -1)
462 goto abort;
463
464 /* Save last modification time */
465 dir_mtime = MINIMUM(time(NULL), st->st_mtim.tv_sec)(((time(((void*)0))) < (st->st_mtim.tv_sec)) ? (time(((
void*)0))) : (st->st_mtim.tv_sec))
;
466
467 if ((evb = evbuffer_new()) == NULL((void*)0))
468 goto abort;
469
470 if ((namesize = scandir(path, &namelist, NULL((void*)0), alphasort)) == -1)
471 goto abort;
472
473 /* Indicate failure but continue going through the list */
474 skip = 0;
475
476 if ((escapedpath = escape_html(desc->http_pathhttp_pathquery.kv_key)) == NULL((void*)0))
477 goto fail;
478
479 /* A CSS stylesheet allows minimal customization by the user */
480 style = "body { background-color: white; color: black; font-family: "
481 "sans-serif; }\nhr { border: 0; border-bottom: 1px dashed; }\n"
482 "@media (prefers-color-scheme: dark) {\n"
483 "body { background-color: #1E1F21; color: #EEEFF1; }\n"
484 "a { color: #BAD7FF; }\n}";
485
486 /* Generate simple HTML index document */
487 if (evbuffer_add_printf(evb,
488 "<!DOCTYPE html>\n"
489 "<html>\n"
490 "<head>\n"
491 "<meta charset=\"utf-8\">\n"
492 "<title>Index of %s</title>\n"
493 "<style type=\"text/css\"><!--\n%s\n--></style>\n"
494 "</head>\n"
495 "<body>\n"
496 "<h1>Index of %s</h1>\n"
497 "<hr>\n<pre>\n",
498 escapedpath, style, escapedpath) == -1)
499 skip = 1;
500
501 free(escapedpath);
502
503 for (i = 0; i < namesize; i++) {
504 dp = namelist[i];
505
506 if (skip ||
507 fstatat(fd, dp->d_name, st, 0) == -1) {
508 free(dp);
509 continue;
510 }
511
512 t = st->st_mtimest_mtim.tv_sec;
513 localtime_r(&t, &tm);
514 strftime(tmstr, sizeof(tmstr), "%d-%h-%Y %R", &tm);
515 namewidth = 51 - strlen(dp->d_name);
516
517 if ((escapeduri = url_encode(dp->d_name)) == NULL((void*)0))
518 goto fail;
519 if ((escapedhtml = escape_html(dp->d_name)) == NULL((void*)0))
520 goto fail;
521
522 if (dp->d_name[0] == '.' &&
523 !(dp->d_name[1] == '.' && dp->d_name[2] == '\0')) {
524 /* ignore hidden files starting with a dot */
525 } else if (S_ISDIR(st->st_mode)((st->st_mode & 0170000) == 0040000)) {
526 namewidth -= 1; /* trailing slash */
527 if (evbuffer_add_printf(evb,
528 "<a href=\"%s%s/\">%s/</a>%*s%s%20s\n",
529 strchr(escapeduri, ':') != NULL((void*)0) ? "./" : "",
530 escapeduri, escapedhtml,
531 MAXIMUM(namewidth, 0)(((namewidth) > (0)) ? (namewidth) : (0)), " ", tmstr, "-") == -1)
532 skip = 1;
533 } else if (S_ISREG(st->st_mode)((st->st_mode & 0170000) == 0100000)) {
534 if (evbuffer_add_printf(evb,
535 "<a href=\"%s%s\">%s</a>%*s%s%20llu\n",
536 strchr(escapeduri, ':') != NULL((void*)0) ? "./" : "",
537 escapeduri, escapedhtml,
538 MAXIMUM(namewidth, 0)(((namewidth) > (0)) ? (namewidth) : (0)), " ",
539 tmstr, st->st_size) == -1)
540 skip = 1;
541 }
542 free(escapeduri);
543 free(escapedhtml);
544 free(dp);
545 }
546 free(namelist);
547
548 if (skip ||
549 evbuffer_add_printf(evb,
550 "</pre>\n<hr>\n</body>\n</html>\n") == -1)
551 goto abort;
552
553 close(fd);
554 fd = -1;
555
556 media = media_find_config(env, srv_conf, "index.html");
557 ret = server_response_http(clt, 200, media, EVBUFFER_LENGTH(evb)(evb)->off,
558 dir_mtime);
559 switch (ret) {
560 case -1:
561 goto fail;
562 case 0:
563 /* Connection is already finished */
564 evbuffer_free(evb);
565 goto done;
566 default:
567 break;
568 }
569
570 if (server_bufferevent_write_buffer(clt, evb) == -1)
571 goto fail;
572 evbuffer_free(evb);
573 evb = NULL((void*)0);
574
575 bufferevent_enable(clt->clt_bev, EV_READ0x02|EV_WRITE0x04);
576 if (clt->clt_persist)
577 clt->clt_toread = TOREAD_HTTP_HEADER;
578 else
579 clt->clt_toread = TOREAD_HTTP_NONE;
580 clt->clt_done = 0;
581
582 done:
583 server_reset_http(clt);
584 return (0);
585 fail:
586 bufferevent_disable(clt->clt_bev, EV_READ0x02|EV_WRITE0x04);
587 bufferevent_free(clt->clt_bev);
588 clt->clt_bev = NULL((void*)0);
589 abort:
590 if (fd != -1)
591 close(fd);
592 if (evb != NULL((void*)0))
593 evbuffer_free(evb);
594 server_abort_http(clt, code, desc->http_pathhttp_pathquery.kv_key);
595 return (-1);
596}
597
598void
599server_file_error(struct bufferevent *bev, short error, void *arg)
600{
601 struct client *clt = arg;
602 struct evbuffer *src, *dst;
603
604 if (error & EVBUFFER_TIMEOUT0x40) {
605 server_close(clt, "buffer event timeout");
606 return;
607 }
608 if (error & EVBUFFER_ERROR0x20) {
609 if (errno(*__errno()) == EFBIG27) {
610 bufferevent_enable(bev, EV_READ0x02);
611 return;
612 }
613 server_close(clt, "buffer event error");
614 return;
615 }
616 if (error & (EVBUFFER_READ0x01|EVBUFFER_WRITE0x02|EVBUFFER_EOF0x10)) {
617 bufferevent_disable(bev, EV_READ0x02|EV_WRITE0x04);
618
619 clt->clt_done = 1;
620
621 src = EVBUFFER_INPUT(clt->clt_bev)(clt->clt_bev)->input;
622
623 /* Close the connection if a previous pipeline is empty */
624 if (clt->clt_pipelining && EVBUFFER_LENGTH(src)(src)->off == 0)
625 clt->clt_persist = 0;
626
627 if (clt->clt_persist) {
628 /* Close input file and wait for next HTTP request */
629 if (clt->clt_fd != -1)
630 close(clt->clt_fd);
631 clt->clt_fd = -1;
632 clt->clt_toread = TOREAD_HTTP_HEADER;
633 server_reset_http(clt);
634 bufferevent_enable(clt->clt_bev, EV_READ0x02|EV_WRITE0x04);
635
636 /* Start pipelining if the buffer is not empty */
637 if (EVBUFFER_LENGTH(src)(src)->off) {
638 clt->clt_pipelining++;
639 server_read_http(clt->clt_bev, arg);
640 }
641 return;
642 }
643
644 dst = EVBUFFER_OUTPUT(clt->clt_bev)(clt->clt_bev)->output;
645 if (EVBUFFER_LENGTH(dst)(dst)->off) {
646 /* Finish writing all data first */
647 bufferevent_enable(clt->clt_bev, EV_WRITE0x04);
648 return;
649 }
650
651 server_close(clt, "done");
652 return;
653 }
654 server_close(clt, "unknown event error");
655 return;
656}
657
658int
659server_file_modified_since(struct http_descriptor *desc, struct stat *st)
660{
661 struct kv key, *since;
662 struct tm tm;
663
664 key.kv_key = "If-Modified-Since";
665 if ((since = kv_find(&desc->http_headers, &key)) != NULL((void*)0) &&
666 since->kv_value != NULL((void*)0)) {
667 memset(&tm, 0, sizeof(struct tm));
668
669 /*
670 * Return "Not modified" if the file hasn't changed since
671 * the requested time.
672 */
673 if (strptime(since->kv_value,
674 "%a, %d %h %Y %T %Z", &tm) != NULL((void*)0) &&
675 timegm(&tm) >= st->st_mtim.tv_sec)
676 return (304);
677 }
678
679 return (-1);
680}
681
682int
683parse_ranges(struct client *clt, char *str, size_t file_sz)
684{
685 int i = 0;
686 char *p, *q;
687 struct range_data *r = &clt->clt_ranges;
688
689 memset(r, 0, sizeof(*r));
690
691 /* Extract range unit */
692 if ((p = strchr(str, '=')) == NULL((void*)0))
693 return (-1);
694
695 *p++ = '\0';
696 /* Check if it's a bytes range spec */
697 if (strcmp(str, "bytes") != 0)
698 return (-1);
699
700 while ((q = strchr(p, ',')) != NULL((void*)0)) {
701 *q++ = '\0';
702
703 /* Extract start and end positions */
704 if (parse_range_spec(p, file_sz, &r->range[i]) == 0)
705 continue;
706
707 i++;
708 if (i == SERVER_MAX_RANGES4)
709 return (-1);
710
711 p = q;
712 }
713
714 if (parse_range_spec(p, file_sz, &r->range[i]) != 0)
715 i++;
716
717 r->range_total = file_sz;
718 r->range_count = i;
719 return (i);
720}
721
722int
723parse_range_spec(char *str, size_t size, struct range *r)
724{
725 size_t start_str_len, end_str_len;
726 char *p, *start_str, *end_str;
727 const char *errstr;
728
729 if ((p = strchr(str, '-')) == NULL((void*)0))
730 return (0);
731
732 *p++ = '\0';
733 start_str = str;
734 end_str = p;
735 start_str_len = strlen(start_str);
736 end_str_len = strlen(end_str);
737
738 /* Either 'start' or 'end' is optional but not both */
739 if ((start_str_len == 0) && (end_str_len == 0))
740 return (0);
741
742 if (end_str_len) {
743 r->end = strtonum(end_str, 0, LLONG_MAX9223372036854775807LL, &errstr);
744 if (errstr)
745 return (0);
746
747 if ((size_t)r->end >= size)
748 r->end = size - 1;
749 } else
750 r->end = size - 1;
751
752 if (start_str_len) {
753 r->start = strtonum(start_str, 0, LLONG_MAX9223372036854775807LL, &errstr);
754 if (errstr)
755 return (0);
756
757 if ((size_t)r->start >= size)
758 return (0);
759 } else {
760 r->start = size - r->end;
761 r->end = size - 1;
762 }
763
764 if (r->end < r->start)
765 return (0);
766
767 return (1);
768}