]>
git.ipfire.org Git - thirdparty/openssl.git/blob - apps/lib/http_server.c
1858d04ccb56dda2c01a007008126dc2737acfb3
2 * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
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
10 /* Very basic HTTP server */
12 #if !defined(_POSIX_C_SOURCE) && defined(OPENSSL_SYS_VMS)
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.
17 # define _POSIX_C_SOURCE 2
22 #include "http_server.h"
23 #include "internal/sockets.h"
24 #include <openssl/err.h>
25 #include <openssl/rand.h>
29 # if defined(OPENSSL_TANDEM_FLOSS)
30 # include <floss.h(floss_fork)>
34 static int verbosity
= LOG_INFO
;
36 #define HTTP_PREFIX "HTTP/"
37 #define HTTP_VERSION_PATT "1." /* allow 1.x */
38 #define HTTP_PREFIX_VERSION HTTP_PREFIX""HTTP_VERSION_PATT
39 #define HTTP_1_0 HTTP_PREFIX_VERSION"0" /* "HTTP/1.0" */
43 int multi
= 0; /* run multiple responder processes */
44 int acfd
= (int) INVALID_SOCKET
;
46 static int print_syslog(const char *str
, size_t len
, void *levPtr
)
48 int level
= *(int *)levPtr
;
49 int ilen
= len
> MAXERRLEN
? MAXERRLEN
: len
;
51 syslog(level
, "%.*s", ilen
, str
);
57 void log_message(const char *prog
, int level
, const char *fmt
, ...)
61 if (verbosity
< level
)
69 if (vsnprintf(buf
, sizeof(buf
), fmt
, ap
) > 0)
70 syslog(level
, "%s", buf
);
72 ERR_print_errors_cb(print_syslog
, &level
);
76 BIO_printf(bio_err
, "%s: ", prog
);
77 BIO_vprintf(bio_err
, fmt
, ap
);
78 BIO_printf(bio_err
, "\n");
79 (void)BIO_flush(bio_err
);
85 void socket_timeout(int signum
)
87 if (acfd
!= (int)INVALID_SOCKET
)
88 (void)shutdown(acfd
, SHUT_RD
);
91 static void killall(int ret
, pid_t
*kidpids
)
95 for (i
= 0; i
< multi
; ++i
)
97 (void)kill(kidpids
[i
], SIGTERM
);
98 OPENSSL_free(kidpids
);
103 static int termsig
= 0;
105 static void noteterm(int sig
)
111 * Loop spawning up to `multi` child processes, only child processes return
112 * from this function. The parent process loops until receiving a termination
113 * signal, kills extant children and exits without returning.
115 void spawn_loop(const char *prog
)
117 pid_t
*kidpids
= NULL
;
122 openlog(prog
, LOG_PID
, LOG_DAEMON
);
125 syslog(LOG_ERR
, "fatal: error detaching from parent process group: %s",
129 kidpids
= app_malloc(multi
* sizeof(*kidpids
), "child PID array");
130 for (i
= 0; i
< multi
; ++i
)
133 signal(SIGINT
, noteterm
);
134 signal(SIGTERM
, noteterm
);
136 while (termsig
== 0) {
140 * Wait for a child to replace when we're at the limit.
141 * Slow down if a child exited abnormally or waitpid() < 0
143 while (termsig
== 0 && procs
>= multi
) {
144 if ((fpid
= waitpid(-1, &status
, 0)) > 0) {
145 for (i
= 0; i
< procs
; ++i
) {
146 if (kidpids
[i
] == fpid
) {
153 syslog(LOG_ERR
, "fatal: internal error: "
154 "no matching child slot for pid: %ld",
159 if (WIFEXITED(status
))
160 syslog(LOG_WARNING
, "child process: %ld, exit status: %d",
161 (long)fpid
, WEXITSTATUS(status
));
162 else if (WIFSIGNALED(status
))
163 syslog(LOG_WARNING
, "child process: %ld, term signal %d%s",
164 (long)fpid
, WTERMSIG(status
),
166 WCOREDUMP(status
) ? " (core dumped)" :
172 } else if (errno
!= EINTR
) {
173 syslog(LOG_ERR
, "fatal: waitpid(): %s", strerror(errno
));
180 switch (fpid
= fork()) {
182 /* System critically low on memory, pause and try again later */
186 OPENSSL_free(kidpids
);
187 signal(SIGINT
, SIG_DFL
);
188 signal(SIGTERM
, SIG_DFL
);
191 if (RAND_poll() <= 0) {
192 syslog(LOG_ERR
, "fatal: RAND_poll() failed");
196 default: /* parent */
197 for (i
= 0; i
< multi
; ++i
) {
198 if (kidpids
[i
] == 0) {
205 syslog(LOG_ERR
, "fatal: internal error: no free child slots");
212 /* The loop above can only break on termsig */
213 syslog(LOG_INFO
, "terminating on signal: %d", termsig
);
218 #ifndef OPENSSL_NO_SOCK
219 BIO
*http_server_init_bio(const char *prog
, const char *port
)
221 BIO
*acbio
= NULL
, *bufbio
;
224 bufbio
= BIO_new(BIO_f_buffer());
227 acbio
= BIO_new(BIO_s_accept());
229 || BIO_set_bind_mode(acbio
, BIO_BIND_REUSEADDR
) < 0
230 || BIO_set_accept_port(acbio
, port
) < 0) {
231 log_message(prog
, LOG_ERR
, "Error setting up accept BIO");
235 BIO_set_accept_bios(acbio
, bufbio
);
237 if (BIO_do_accept(acbio
) <= 0) {
238 log_message(prog
, LOG_ERR
, "Error starting accept");
242 /* Report back what address and port are used */
243 BIO_get_fd(acbio
, &asock
);
244 if (!report_server_accept(bio_out
, asock
, 1)) {
245 log_message(prog
, LOG_ERR
, "Error printing ACCEPT string");
258 * Decode %xx URL-decoding in-place. Ignores malformed sequences.
260 static int urldecode(char *p
)
262 unsigned char *out
= (unsigned char *)p
;
263 unsigned char *save
= out
;
268 } else if (isxdigit(_UC(p
[1])) && isxdigit(_UC(p
[2]))) {
269 /* Don't check, can't fail because of ixdigit() call. */
270 *out
++ = (OPENSSL_hexchar2int(p
[1]) << 4)
271 | OPENSSL_hexchar2int(p
[2]);
278 return (int)(out
- save
);
281 /* if *pcbio != NULL, continue given connected session, else accept new */
282 /* if found_keep_alive != NULL, return this way connection persistence state */
283 int http_server_get_asn1_req(const ASN1_ITEM
*it
, ASN1_VALUE
**preq
,
284 char **ppath
, BIO
**pcbio
, BIO
*acbio
,
285 int *found_keep_alive
,
286 const char *prog
, const char *port
,
287 int accept_get
, int timeout
)
289 BIO
*cbio
= *pcbio
, *getbio
= NULL
, *b64
= NULL
;
291 char reqbuf
[2048], inbuf
[2048];
292 char *meth
, *url
, *end
;
301 log_message(prog
, LOG_DEBUG
,
302 "Awaiting new connection on port %s...", port
);
303 if (BIO_do_accept(acbio
) <= 0)
304 /* Connection loss before accept() is routine, ignore silently */
307 *pcbio
= cbio
= BIO_pop(acbio
);
309 log_message(prog
, LOG_DEBUG
, "Awaiting next request...");
312 /* Cannot call http_server_send_status(cbio, ...) */
319 (void)BIO_get_fd(cbio
, &acfd
);
324 /* Read the request line. */
325 len
= BIO_gets(cbio
, reqbuf
, sizeof(reqbuf
));
330 log_message(prog
, LOG_WARNING
, "Request line read error");
331 (void)http_server_send_status(cbio
, 400, "Bad Request");
334 if ((end
= strchr(reqbuf
, '\r')) != NULL
335 || (end
= strchr(reqbuf
, '\n')) != NULL
)
337 log_message(prog
, LOG_INFO
, "Received request, 1st line: %s", reqbuf
);
341 if ((accept_get
&& strncmp(meth
, "GET ", 4) == 0)
342 || (url
++, strncmp(meth
, "POST ", 5) == 0)) {
343 static const char http_version_str
[] = " "HTTP_PREFIX_VERSION
;
344 static const size_t http_version_str_len
= sizeof(http_version_str
) - 1;
346 /* Expecting (GET|POST) {sp} /URL {sp} HTTP/1.x */
351 log_message(prog
, LOG_WARNING
,
352 "Invalid %s -- URL does not begin with '/': %s",
354 (void)http_server_send_status(cbio
, 400, "Bad Request");
359 /* Splice off the HTTP version identifier. */
360 for (end
= url
; *end
!= '\0'; end
++)
363 if (strncmp(end
, http_version_str
, http_version_str_len
) != 0) {
364 log_message(prog
, LOG_WARNING
,
365 "Invalid %s -- bad HTTP/version string: %s",
367 (void)http_server_send_status(cbio
, 400, "Bad Request");
371 /* above HTTP 1.0, connection persistence is the default */
372 if (found_keep_alive
!= NULL
)
373 *found_keep_alive
= end
[http_version_str_len
] > '0';
376 * Skip "GET / HTTP..." requests often used by load-balancers.
377 * 'url' was incremented above to point to the first byte *after*
378 * the leading slash, so in case 'GET / ' it is now an empty string.
380 if (strlen(meth
) == 3 && url
[0] == '\0') {
381 (void)http_server_send_status(cbio
, 200, "OK");
385 len
= urldecode(url
);
387 log_message(prog
, LOG_WARNING
,
388 "Invalid %s request -- bad URL encoding: %s",
390 (void)http_server_send_status(cbio
, 400, "Bad Request");
393 if (strlen(meth
) == 3) { /* GET */
394 if ((getbio
= BIO_new_mem_buf(url
, len
)) == NULL
395 || (b64
= BIO_new(BIO_f_base64())) == NULL
) {
396 log_message(prog
, LOG_ERR
,
397 "Could not allocate base64 bio with size = %d",
401 BIO_set_flags(b64
, BIO_FLAGS_BASE64_NO_NL
);
402 getbio
= BIO_push(b64
, getbio
);
405 log_message(prog
, LOG_WARNING
,
406 "HTTP request does not begin with %sPOST: %s",
407 accept_get
? "GET or " : "", reqbuf
);
408 (void)http_server_send_status(cbio
, 400, "Bad Request");
412 /* chop any further/duplicate leading or trailing '/' */
415 while (end
>= url
+ 2 && end
[-2] == '/' && end
[-1] == '/')
419 /* Read and skip past the headers. */
421 char *key
, *value
, *line_end
= NULL
;
423 len
= BIO_gets(cbio
, inbuf
, sizeof(inbuf
));
425 log_message(prog
, LOG_WARNING
, "Error reading HTTP header");
426 (void)http_server_send_status(cbio
, 400, "Bad Request");
430 if (inbuf
[0] == '\r' || inbuf
[0] == '\n')
434 value
= strchr(key
, ':');
436 log_message(prog
, LOG_WARNING
,
437 "Error parsing HTTP header: missing ':'");
438 (void)http_server_send_status(cbio
, 400, "Bad Request");
442 while (*value
== ' ')
444 line_end
= strchr(value
, '\r');
445 if (line_end
== NULL
) {
446 line_end
= strchr(value
, '\n');
447 if (line_end
== NULL
) {
448 log_message(prog
, LOG_WARNING
,
449 "Error parsing HTTP header: missing end of line");
450 (void)http_server_send_status(cbio
, 400, "Bad Request");
455 /* https://tools.ietf.org/html/rfc7230#section-6.3 Persistence */
456 if (found_keep_alive
!= NULL
&& strcasecmp(key
, "Connection") == 0) {
457 if (strcasecmp(value
, "keep-alive") == 0)
458 *found_keep_alive
= 1;
459 else if (strcasecmp(value
, "close") == 0)
460 *found_keep_alive
= 0;
465 /* Clear alarm before we close the client socket */
470 /* Try to read and parse request */
471 req
= ASN1_item_d2i_bio(it
, getbio
!= NULL
? getbio
: cbio
, NULL
);
473 log_message(prog
, LOG_WARNING
,
474 "Error parsing DER-encoded request content");
475 (void)http_server_send_status(cbio
, 400, "Bad Request");
476 } else if (ppath
!= NULL
&& (*ppath
= OPENSSL_strdup(url
)) == NULL
) {
477 log_message(prog
, LOG_ERR
,
478 "Out of memory allocating %zu bytes", strlen(url
) + 1);
479 ASN1_item_free(req
, it
);
486 BIO_free_all(getbio
);
490 acfd
= (int)INVALID_SOCKET
;
495 (void)http_server_send_status(cbio
, 500, "Internal Server Error");
497 OPENSSL_free(*ppath
);
506 /* assumes that cbio does not do an encoding that changes the output length */
507 int http_server_send_asn1_resp(BIO
*cbio
, int keep_alive
,
508 const char *content_type
,
509 const ASN1_ITEM
*it
, const ASN1_VALUE
*resp
)
511 int ret
= BIO_printf(cbio
, HTTP_1_0
" 200 OK\r\n%s"
512 "Content-type: %s\r\n"
513 "Content-Length: %d\r\n\r\n",
514 keep_alive
? "Connection: keep-alive\r\n" : "",
516 ASN1_item_i2d(resp
, NULL
, it
)) > 0
517 && ASN1_item_i2d_bio(it
, cbio
, resp
) > 0;
519 (void)BIO_flush(cbio
);
523 int http_server_send_status(BIO
*cbio
, int status
, const char *reason
)
525 int ret
= BIO_printf(cbio
, HTTP_1_0
" %d %s\r\n\r\n",
526 /* This implicitly cancels keep-alive */
529 (void)BIO_flush(cbio
);