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