]>
Commit | Line | Data |
---|---|---|
582311d7 | 1 | /* |
da1c088f | 2 | * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. |
582311d7 DDO |
3 | * |
4 | * Licensed under the Apache License 2.0 (the "License"). You may not use | |
5 | * this file except in compliance with the License. You can obtain a copy | |
6 | * in the file LICENSE in the source distribution or at | |
7 | * https://www.openssl.org/source/license.html | |
8 | */ | |
9 | ||
10 | /* Very basic HTTP server */ | |
11 | ||
12 | #if !defined(_POSIX_C_SOURCE) && defined(OPENSSL_SYS_VMS) | |
13 | /* | |
14 | * On VMS, you need to define this to get the declaration of fileno(). The | |
15 | * value 2 is to make sure no function defined in POSIX-2 is left undefined. | |
16 | */ | |
17 | # define _POSIX_C_SOURCE 2 | |
18 | #endif | |
19 | ||
20 | #include <ctype.h> | |
21 | #include "http_server.h" | |
22 | #include "internal/sockets.h" | |
23 | #include <openssl/err.h> | |
8aff29f0 | 24 | #include <openssl/trace.h> |
582311d7 | 25 | #include <openssl/rand.h> |
a12da5da | 26 | #include "s_apps.h" |
8a2ec00d | 27 | #include "log.h" |
582311d7 | 28 | |
19f97fe6 DDO |
29 | #define HTTP_PREFIX "HTTP/" |
30 | #define HTTP_VERSION_PATT "1." /* allow 1.x */ | |
31 | #define HTTP_PREFIX_VERSION HTTP_PREFIX""HTTP_VERSION_PATT | |
32 | #define HTTP_1_0 HTTP_PREFIX_VERSION"0" /* "HTTP/1.0" */ | |
2ff286c2 | 33 | #define HTTP_VERSION_STR " "HTTP_PREFIX_VERSION |
19f97fe6 | 34 | |
8aff29f0 DDO |
35 | #define log_HTTP(prog, level, text) \ |
36 | trace_log_message(OSSL_TRACE_CATEGORY_HTTP, prog, level, "%s", text) | |
37 | #define log_HTTP1(prog, level, fmt, arg) \ | |
38 | trace_log_message(OSSL_TRACE_CATEGORY_HTTP, prog, level, fmt, arg) | |
39 | #define log_HTTP2(prog, level, fmt, arg1, arg2) \ | |
40 | trace_log_message(OSSL_TRACE_CATEGORY_HTTP, prog, level, fmt, arg1, arg2) | |
8a2ec00d DDO |
41 | #define log_HTTP3(prog, level, fmt, a1, a2, a3) \ |
42 | trace_log_message(OSSL_TRACE_CATEGORY_HTTP, prog, level, fmt, a1, a2, a3) | |
8aff29f0 | 43 | |
582311d7 | 44 | #ifdef HTTP_DAEMON |
8a2ec00d DDO |
45 | int n_responders = 0; /* run multiple responder processes, set by ocsp.c */ |
46 | int acfd = (int)INVALID_SOCKET; | |
47 | ||
582311d7 DDO |
48 | void socket_timeout(int signum) |
49 | { | |
50 | if (acfd != (int)INVALID_SOCKET) | |
51 | (void)shutdown(acfd, SHUT_RD); | |
52 | } | |
53 | ||
54 | static void killall(int ret, pid_t *kidpids) | |
55 | { | |
56 | int i; | |
57 | ||
5e87fddc | 58 | for (i = 0; i < n_responders; ++i) |
582311d7 DDO |
59 | if (kidpids[i] != 0) |
60 | (void)kill(kidpids[i], SIGTERM); | |
61 | OPENSSL_free(kidpids); | |
5139dec2 | 62 | OSSL_sleep(1000); |
582311d7 DDO |
63 | exit(ret); |
64 | } | |
65 | ||
66 | static int termsig = 0; | |
67 | ||
68 | static void noteterm(int sig) | |
69 | { | |
70 | termsig = sig; | |
71 | } | |
72 | ||
73 | /* | |
74 | * Loop spawning up to `multi` child processes, only child processes return | |
75 | * from this function. The parent process loops until receiving a termination | |
76 | * signal, kills extant children and exits without returning. | |
77 | */ | |
78 | void spawn_loop(const char *prog) | |
79 | { | |
80 | pid_t *kidpids = NULL; | |
81 | int status; | |
82 | int procs = 0; | |
83 | int i; | |
84 | ||
85 | openlog(prog, LOG_PID, LOG_DAEMON); | |
86 | ||
87 | if (setpgid(0, 0)) { | |
46032426 DDO |
88 | log_HTTP1(prog, LOG_CRIT, |
89 | "error detaching from parent process group: %s", | |
8a2ec00d | 90 | strerror(errno)); |
582311d7 DDO |
91 | exit(1); |
92 | } | |
5e87fddc DDO |
93 | kidpids = app_malloc(n_responders * sizeof(*kidpids), "child PID array"); |
94 | for (i = 0; i < n_responders; ++i) | |
582311d7 DDO |
95 | kidpids[i] = 0; |
96 | ||
97 | signal(SIGINT, noteterm); | |
98 | signal(SIGTERM, noteterm); | |
99 | ||
100 | while (termsig == 0) { | |
101 | pid_t fpid; | |
102 | ||
103 | /* | |
104 | * Wait for a child to replace when we're at the limit. | |
105 | * Slow down if a child exited abnormally or waitpid() < 0 | |
106 | */ | |
5e87fddc | 107 | while (termsig == 0 && procs >= n_responders) { |
582311d7 DDO |
108 | if ((fpid = waitpid(-1, &status, 0)) > 0) { |
109 | for (i = 0; i < procs; ++i) { | |
110 | if (kidpids[i] == fpid) { | |
111 | kidpids[i] = 0; | |
112 | --procs; | |
113 | break; | |
114 | } | |
115 | } | |
5e87fddc | 116 | if (i >= n_responders) { |
46032426 DDO |
117 | log_HTTP1(prog, LOG_CRIT, |
118 | "internal error: no matching child slot for pid: %ld", | |
8a2ec00d | 119 | (long)fpid); |
582311d7 DDO |
120 | killall(1, kidpids); |
121 | } | |
122 | if (status != 0) { | |
8a2ec00d DDO |
123 | if (WIFEXITED(status)) { |
124 | log_HTTP2(prog, LOG_WARNING, | |
125 | "child process: %ld, exit status: %d", | |
126 | (long)fpid, WEXITSTATUS(status)); | |
127 | } else if (WIFSIGNALED(status)) { | |
128 | char *dumped = ""; | |
129 | ||
582311d7 | 130 | # ifdef WCOREDUMP |
8a2ec00d DDO |
131 | if (WCOREDUMP(status)) |
132 | dumped = " (core dumped)"; | |
582311d7 | 133 | # endif |
8a2ec00d DDO |
134 | log_HTTP3(prog, LOG_WARNING, |
135 | "child process: %ld, term signal %d%s", | |
136 | (long)fpid, WTERMSIG(status), dumped); | |
137 | } | |
5139dec2 | 138 | OSSL_sleep(1000); |
582311d7 DDO |
139 | } |
140 | break; | |
141 | } else if (errno != EINTR) { | |
46032426 DDO |
142 | log_HTTP1(prog, LOG_CRIT, |
143 | "waitpid() failed: %s", strerror(errno)); | |
582311d7 DDO |
144 | killall(1, kidpids); |
145 | } | |
146 | } | |
147 | if (termsig) | |
148 | break; | |
149 | ||
150 | switch (fpid = fork()) { | |
151 | case -1: /* error */ | |
152 | /* System critically low on memory, pause and try again later */ | |
5139dec2 | 153 | OSSL_sleep(30000); |
582311d7 DDO |
154 | break; |
155 | case 0: /* child */ | |
156 | OPENSSL_free(kidpids); | |
157 | signal(SIGINT, SIG_DFL); | |
158 | signal(SIGTERM, SIG_DFL); | |
159 | if (termsig) | |
160 | _exit(0); | |
161 | if (RAND_poll() <= 0) { | |
46032426 | 162 | log_HTTP(prog, LOG_CRIT, "RAND_poll() failed"); |
582311d7 DDO |
163 | _exit(1); |
164 | } | |
165 | return; | |
166 | default: /* parent */ | |
5e87fddc | 167 | for (i = 0; i < n_responders; ++i) { |
582311d7 DDO |
168 | if (kidpids[i] == 0) { |
169 | kidpids[i] = fpid; | |
170 | procs++; | |
171 | break; | |
172 | } | |
173 | } | |
5e87fddc | 174 | if (i >= n_responders) { |
46032426 DDO |
175 | log_HTTP(prog, LOG_CRIT, |
176 | "internal error: no free child slots"); | |
582311d7 DDO |
177 | killall(1, kidpids); |
178 | } | |
179 | break; | |
180 | } | |
181 | } | |
182 | ||
183 | /* The loop above can only break on termsig */ | |
8a2ec00d | 184 | log_HTTP1(prog, LOG_INFO, "terminating on signal: %d", termsig); |
582311d7 DDO |
185 | killall(0, kidpids); |
186 | } | |
187 | #endif | |
188 | ||
189 | #ifndef OPENSSL_NO_SOCK | |
4599ea9f | 190 | BIO *http_server_init(const char *prog, const char *port, int verb) |
582311d7 DDO |
191 | { |
192 | BIO *acbio = NULL, *bufbio; | |
a12da5da | 193 | int asock; |
4599ea9f | 194 | int port_num; |
830b6a13 | 195 | char name[40]; |
582311d7 | 196 | |
a19553cd | 197 | BIO_snprintf(name, sizeof(name), "*:%s", port); /* port may be "0" */ |
8a2ec00d DDO |
198 | if (verb >= 0 && !log_set_verbosity(prog, verb)) |
199 | return NULL; | |
582311d7 DDO |
200 | bufbio = BIO_new(BIO_f_buffer()); |
201 | if (bufbio == NULL) | |
202 | goto err; | |
203 | acbio = BIO_new(BIO_s_accept()); | |
204 | if (acbio == NULL | |
205 | || BIO_set_bind_mode(acbio, BIO_BIND_REUSEADDR) < 0 | |
830b6a13 | 206 | || BIO_set_accept_name(acbio, name) < 0) { |
46032426 | 207 | log_HTTP(prog, LOG_ERR, "error setting up accept BIO"); |
582311d7 DDO |
208 | goto err; |
209 | } | |
210 | ||
211 | BIO_set_accept_bios(acbio, bufbio); | |
212 | bufbio = NULL; | |
213 | if (BIO_do_accept(acbio) <= 0) { | |
46032426 | 214 | log_HTTP1(prog, LOG_ERR, "error setting accept on port %s", port); |
582311d7 DDO |
215 | goto err; |
216 | } | |
217 | ||
a12da5da RL |
218 | /* Report back what address and port are used */ |
219 | BIO_get_fd(acbio, &asock); | |
4599ea9f DDO |
220 | port_num = report_server_accept(bio_out, asock, 1, 1); |
221 | if (port_num == 0) { | |
46032426 | 222 | log_HTTP(prog, LOG_ERR, "error printing ACCEPT string"); |
a12da5da RL |
223 | goto err; |
224 | } | |
225 | ||
582311d7 DDO |
226 | return acbio; |
227 | ||
228 | err: | |
46032426 | 229 | ERR_print_errors(bio_err); |
582311d7 DDO |
230 | BIO_free_all(acbio); |
231 | BIO_free(bufbio); | |
232 | return NULL; | |
233 | } | |
234 | ||
235 | /* | |
236 | * Decode %xx URL-decoding in-place. Ignores malformed sequences. | |
237 | */ | |
238 | static int urldecode(char *p) | |
239 | { | |
240 | unsigned char *out = (unsigned char *)p; | |
241 | unsigned char *save = out; | |
242 | ||
243 | for (; *p; p++) { | |
244 | if (*p != '%') { | |
245 | *out++ = *p; | |
246 | } else if (isxdigit(_UC(p[1])) && isxdigit(_UC(p[2]))) { | |
247 | /* Don't check, can't fail because of ixdigit() call. */ | |
248 | *out++ = (OPENSSL_hexchar2int(p[1]) << 4) | |
249 | | OPENSSL_hexchar2int(p[2]); | |
250 | p += 2; | |
251 | } else { | |
252 | return -1; | |
253 | } | |
254 | } | |
255 | *out = '\0'; | |
256 | return (int)(out - save); | |
257 | } | |
258 | ||
19f97fe6 DDO |
259 | /* if *pcbio != NULL, continue given connected session, else accept new */ |
260 | /* if found_keep_alive != NULL, return this way connection persistence state */ | |
582311d7 | 261 | int http_server_get_asn1_req(const ASN1_ITEM *it, ASN1_VALUE **preq, |
5a2ba207 | 262 | char **ppath, BIO **pcbio, BIO *acbio, |
19f97fe6 | 263 | int *found_keep_alive, |
4599ea9f | 264 | const char *prog, int accept_get, int timeout) |
582311d7 | 265 | { |
19f97fe6 | 266 | BIO *cbio = *pcbio, *getbio = NULL, *b64 = NULL; |
582311d7 DDO |
267 | int len; |
268 | char reqbuf[2048], inbuf[2048]; | |
5a2ba207 | 269 | char *meth, *url, *end; |
582311d7 | 270 | ASN1_VALUE *req; |
cc1af4db | 271 | int ret = 0; |
582311d7 DDO |
272 | |
273 | *preq = NULL; | |
5a2ba207 DDO |
274 | if (ppath != NULL) |
275 | *ppath = NULL; | |
582311d7 | 276 | |
19f97fe6 | 277 | if (cbio == NULL) { |
4599ea9f DDO |
278 | char *port; |
279 | ||
280 | get_sock_info_address(BIO_get_fd(acbio, NULL), NULL, &port); | |
281 | if (port == NULL) { | |
46032426 | 282 | log_HTTP(prog, LOG_ERR, "cannot get port listening on"); |
4599ea9f DDO |
283 | goto fatal; |
284 | } | |
8aff29f0 | 285 | log_HTTP1(prog, LOG_DEBUG, |
46032426 | 286 | "awaiting new connection on port %s ...", port); |
4599ea9f DDO |
287 | OPENSSL_free(port); |
288 | ||
19f97fe6 DDO |
289 | if (BIO_do_accept(acbio) <= 0) |
290 | /* Connection loss before accept() is routine, ignore silently */ | |
291 | return ret; | |
582311d7 | 292 | |
19f97fe6 DDO |
293 | *pcbio = cbio = BIO_pop(acbio); |
294 | } else { | |
46032426 | 295 | log_HTTP(prog, LOG_DEBUG, "awaiting next request ..."); |
19f97fe6 | 296 | } |
582311d7 | 297 | if (cbio == NULL) { |
8aff29f0 | 298 | /* Cannot call http_server_send_status(..., cbio, ...) */ |
582311d7 DDO |
299 | ret = -1; |
300 | goto out; | |
301 | } | |
302 | ||
303 | # ifdef HTTP_DAEMON | |
304 | if (timeout > 0) { | |
305 | (void)BIO_get_fd(cbio, &acfd); | |
306 | alarm(timeout); | |
307 | } | |
308 | # endif | |
309 | ||
310 | /* Read the request line. */ | |
311 | len = BIO_gets(cbio, reqbuf, sizeof(reqbuf)); | |
cc1af4db DDO |
312 | if (len == 0) |
313 | return ret; | |
314 | ret = 1; | |
315 | if (len < 0) { | |
46032426 | 316 | log_HTTP(prog, LOG_WARNING, "request line read error"); |
8aff29f0 | 317 | (void)http_server_send_status(prog, cbio, 400, "Bad Request"); |
582311d7 | 318 | goto out; |
5a2ba207 | 319 | } |
8aff29f0 DDO |
320 | |
321 | if (((end = strchr(reqbuf, '\r')) != NULL && end[1] == '\n') | |
cc1af4db DDO |
322 | || (end = strchr(reqbuf, '\n')) != NULL) |
323 | *end = '\0'; | |
8a2ec00d | 324 | if (log_get_verbosity() < LOG_TRACE) |
8aff29f0 | 325 | trace_log_message(-1, prog, LOG_INFO, |
46032426 DDO |
326 | "received request, 1st line: %s", reqbuf); |
327 | log_HTTP(prog, LOG_TRACE, "received request header:"); | |
8aff29f0 DDO |
328 | log_HTTP1(prog, LOG_TRACE, "%s", reqbuf); |
329 | if (end == NULL) { | |
330 | log_HTTP(prog, LOG_WARNING, | |
46032426 | 331 | "cannot parse HTTP header: missing end of line"); |
8aff29f0 DDO |
332 | (void)http_server_send_status(prog, cbio, 400, "Bad Request"); |
333 | goto out; | |
334 | } | |
582311d7 | 335 | |
2ff286c2 DDO |
336 | url = meth = reqbuf; |
337 | if ((accept_get && CHECK_AND_SKIP_PREFIX(url, "GET ")) | |
338 | || CHECK_AND_SKIP_PREFIX(url, "POST ")) { | |
19f97fe6 | 339 | |
5a2ba207 | 340 | /* Expecting (GET|POST) {sp} /URL {sp} HTTP/1.x */ |
2ff286c2 | 341 | url[-1] = '\0'; |
5a2ba207 DDO |
342 | while (*url == ' ') |
343 | url++; | |
582311d7 | 344 | if (*url != '/') { |
8aff29f0 | 345 | log_HTTP2(prog, LOG_WARNING, |
46032426 | 346 | "invalid %s -- URL does not begin with '/': %s", |
8aff29f0 DDO |
347 | meth, url); |
348 | (void)http_server_send_status(prog, cbio, 400, "Bad Request"); | |
582311d7 DDO |
349 | goto out; |
350 | } | |
351 | url++; | |
352 | ||
353 | /* Splice off the HTTP version identifier. */ | |
354 | for (end = url; *end != '\0'; end++) | |
355 | if (*end == ' ') | |
356 | break; | |
2ff286c2 | 357 | if (!HAS_PREFIX(end, HTTP_VERSION_STR)) { |
8aff29f0 | 358 | log_HTTP2(prog, LOG_WARNING, |
46032426 | 359 | "invalid %s -- bad HTTP/version string: %s", |
8aff29f0 DDO |
360 | meth, end + 1); |
361 | (void)http_server_send_status(prog, cbio, 400, "Bad Request"); | |
582311d7 DDO |
362 | goto out; |
363 | } | |
364 | *end = '\0'; | |
19f97fe6 DDO |
365 | /* above HTTP 1.0, connection persistence is the default */ |
366 | if (found_keep_alive != NULL) | |
2ff286c2 | 367 | *found_keep_alive = end[sizeof(HTTP_VERSION_STR) - 1] > '0'; |
582311d7 DDO |
368 | |
369 | /*- | |
370 | * Skip "GET / HTTP..." requests often used by load-balancers. | |
371 | * 'url' was incremented above to point to the first byte *after* | |
372 | * the leading slash, so in case 'GET / ' it is now an empty string. | |
373 | */ | |
5a2ba207 | 374 | if (strlen(meth) == 3 && url[0] == '\0') { |
8aff29f0 | 375 | (void)http_server_send_status(prog, cbio, 200, "OK"); |
582311d7 | 376 | goto out; |
5a2ba207 | 377 | } |
582311d7 DDO |
378 | |
379 | len = urldecode(url); | |
5a2ba207 | 380 | if (len < 0) { |
8aff29f0 | 381 | log_HTTP2(prog, LOG_WARNING, |
46032426 | 382 | "invalid %s request -- bad URL encoding: %s", meth, url); |
8aff29f0 | 383 | (void)http_server_send_status(prog, cbio, 400, "Bad Request"); |
582311d7 DDO |
384 | goto out; |
385 | } | |
5a2ba207 DDO |
386 | if (strlen(meth) == 3) { /* GET */ |
387 | if ((getbio = BIO_new_mem_buf(url, len)) == NULL | |
388 | || (b64 = BIO_new(BIO_f_base64())) == NULL) { | |
8aff29f0 | 389 | log_HTTP1(prog, LOG_ERR, |
46032426 | 390 | "could not allocate base64 bio with size = %d", len); |
5a2ba207 DDO |
391 | goto fatal; |
392 | } | |
393 | BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); | |
394 | getbio = BIO_push(b64, getbio); | |
582311d7 | 395 | } |
5a2ba207 | 396 | } else { |
8aff29f0 DDO |
397 | log_HTTP2(prog, LOG_WARNING, |
398 | "HTTP request does not begin with %sPOST: %s", | |
399 | accept_get ? "GET or " : "", reqbuf); | |
400 | (void)http_server_send_status(prog, cbio, 400, "Bad Request"); | |
582311d7 DDO |
401 | goto out; |
402 | } | |
403 | ||
5a2ba207 DDO |
404 | /* chop any further/duplicate leading or trailing '/' */ |
405 | while (*url == '/') | |
406 | url++; | |
407 | while (end >= url + 2 && end[-2] == '/' && end[-1] == '/') | |
408 | end--; | |
409 | *end = '\0'; | |
410 | ||
582311d7 DDO |
411 | /* Read and skip past the headers. */ |
412 | for (;;) { | |
8aff29f0 | 413 | char *key, *value; |
19f97fe6 | 414 | |
582311d7 DDO |
415 | len = BIO_gets(cbio, inbuf, sizeof(inbuf)); |
416 | if (len <= 0) { | |
46032426 | 417 | log_HTTP(prog, LOG_WARNING, "error reading HTTP header"); |
8aff29f0 DDO |
418 | (void)http_server_send_status(prog, cbio, 400, "Bad Request"); |
419 | goto out; | |
420 | } | |
421 | ||
422 | if (((end = strchr(inbuf, '\r')) != NULL && end[1] == '\n') | |
423 | || (end = strchr(inbuf, '\n')) != NULL) | |
424 | *end = '\0'; | |
425 | log_HTTP1(prog, LOG_TRACE, "%s", *inbuf == '\0' ? | |
426 | " " /* workaround for "" getting ignored */ : inbuf); | |
427 | if (end == NULL) { | |
428 | log_HTTP(prog, LOG_WARNING, | |
46032426 | 429 | "error parsing HTTP header: missing end of line"); |
8aff29f0 | 430 | (void)http_server_send_status(prog, cbio, 400, "Bad Request"); |
582311d7 DDO |
431 | goto out; |
432 | } | |
19f97fe6 | 433 | |
8aff29f0 | 434 | if (inbuf[0] == '\0') |
582311d7 | 435 | break; |
19f97fe6 DDO |
436 | |
437 | key = inbuf; | |
438 | value = strchr(key, ':'); | |
cef71ebb | 439 | if (value == NULL) { |
8aff29f0 | 440 | log_HTTP(prog, LOG_WARNING, |
46032426 | 441 | "error parsing HTTP header: missing ':'"); |
8aff29f0 | 442 | (void)http_server_send_status(prog, cbio, 400, "Bad Request"); |
19f97fe6 DDO |
443 | goto out; |
444 | } | |
cef71ebb P |
445 | *(value++) = '\0'; |
446 | while (*value == ' ') | |
447 | value++; | |
cef71ebb | 448 | /* https://tools.ietf.org/html/rfc7230#section-6.3 Persistence */ |
fba140c7 DB |
449 | if (found_keep_alive != NULL |
450 | && OPENSSL_strcasecmp(key, "Connection") == 0) { | |
451 | if (OPENSSL_strcasecmp(value, "keep-alive") == 0) | |
cef71ebb | 452 | *found_keep_alive = 1; |
fba140c7 | 453 | else if (OPENSSL_strcasecmp(value, "close") == 0) |
cef71ebb P |
454 | *found_keep_alive = 0; |
455 | } | |
582311d7 DDO |
456 | } |
457 | ||
458 | # ifdef HTTP_DAEMON | |
459 | /* Clear alarm before we close the client socket */ | |
460 | alarm(0); | |
461 | timeout = 0; | |
462 | # endif | |
463 | ||
464 | /* Try to read and parse request */ | |
465 | req = ASN1_item_d2i_bio(it, getbio != NULL ? getbio : cbio, NULL); | |
5a2ba207 | 466 | if (req == NULL) { |
8aff29f0 | 467 | log_HTTP(prog, LOG_WARNING, |
46032426 | 468 | "error parsing DER-encoded request content"); |
8aff29f0 | 469 | (void)http_server_send_status(prog, cbio, 400, "Bad Request"); |
5a2ba207 | 470 | } else if (ppath != NULL && (*ppath = OPENSSL_strdup(url)) == NULL) { |
8aff29f0 | 471 | log_HTTP1(prog, LOG_ERR, |
46032426 | 472 | "out of memory allocating %zu bytes", strlen(url) + 1); |
5a2ba207 DDO |
473 | ASN1_item_free(req, it); |
474 | goto fatal; | |
475 | } | |
582311d7 DDO |
476 | |
477 | *preq = req; | |
478 | ||
479 | out: | |
480 | BIO_free_all(getbio); | |
481 | # ifdef HTTP_DAEMON | |
482 | if (timeout > 0) | |
483 | alarm(0); | |
484 | acfd = (int)INVALID_SOCKET; | |
485 | # endif | |
486 | return ret; | |
5a2ba207 DDO |
487 | |
488 | fatal: | |
8aff29f0 | 489 | (void)http_server_send_status(prog, cbio, 500, "Internal Server Error"); |
5a2ba207 DDO |
490 | if (ppath != NULL) { |
491 | OPENSSL_free(*ppath); | |
492 | *ppath = NULL; | |
493 | } | |
494 | BIO_free_all(cbio); | |
495 | *pcbio = NULL; | |
496 | ret = -1; | |
497 | goto out; | |
582311d7 DDO |
498 | } |
499 | ||
500 | /* assumes that cbio does not do an encoding that changes the output length */ | |
8aff29f0 | 501 | int http_server_send_asn1_resp(const char *prog, BIO *cbio, int keep_alive, |
19f97fe6 | 502 | const char *content_type, |
582311d7 DDO |
503 | const ASN1_ITEM *it, const ASN1_VALUE *resp) |
504 | { | |
8aff29f0 | 505 | char buf[200], *p; |
4a6e5a11 F |
506 | int ret = BIO_snprintf(buf, sizeof(buf), HTTP_1_0" 200 OK\r\n%s" |
507 | "Content-type: %s\r\n" | |
508 | "Content-Length: %d\r\n", | |
509 | keep_alive ? "Connection: keep-alive\r\n" : "", | |
510 | content_type, | |
511 | ASN1_item_i2d(resp, NULL, it)); | |
8aff29f0 DDO |
512 | |
513 | if (ret < 0 || (size_t)ret >= sizeof(buf)) | |
514 | return 0; | |
8a2ec00d | 515 | if (log_get_verbosity() < LOG_TRACE && (p = strchr(buf, '\r')) != NULL) |
8aff29f0 | 516 | trace_log_message(-1, prog, LOG_INFO, |
46032426 | 517 | "sending response, 1st line: %.*s", (int)(p - buf), |
8aff29f0 | 518 | buf); |
46032426 | 519 | log_HTTP1(prog, LOG_TRACE, "sending response header:\n%s", buf); |
8aff29f0 DDO |
520 | |
521 | ret = BIO_printf(cbio, "%s\r\n", buf) > 0 | |
522 | && ASN1_item_i2d_bio(it, cbio, resp) > 0; | |
582311d7 DDO |
523 | |
524 | (void)BIO_flush(cbio); | |
525 | return ret; | |
526 | } | |
5a2ba207 | 527 | |
8aff29f0 DDO |
528 | int http_server_send_status(const char *prog, BIO *cbio, |
529 | int status, const char *reason) | |
5a2ba207 | 530 | { |
8aff29f0 | 531 | char buf[200]; |
4a6e5a11 F |
532 | int ret = BIO_snprintf(buf, sizeof(buf), HTTP_1_0" %d %s\r\n\r\n", |
533 | /* This implicitly cancels keep-alive */ | |
534 | status, reason); | |
8aff29f0 DDO |
535 | |
536 | if (ret < 0 || (size_t)ret >= sizeof(buf)) | |
537 | return 0; | |
46032426 | 538 | log_HTTP1(prog, LOG_TRACE, "sending response header:\n%s", buf); |
5a2ba207 | 539 | |
8aff29f0 | 540 | ret = BIO_printf(cbio, "%s\r\n", buf) > 0; |
5a2ba207 DDO |
541 | (void)BIO_flush(cbio); |
542 | return ret; | |
543 | } | |
582311d7 | 544 | #endif |