[BACK]Return to http.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ftp

Annotation of src/usr.bin/ftp/http.c, Revision 1.5

1.5     ! jasper      1: /*     $OpenBSD$ */
        !             2:
1.1       deraadt     3: /*
1.4       kmos        4:  * Copyright (c) 2015 Sunil Nimmagadda <sunil@openbsd.org>
                      5:  * Copyright (c) 2012 - 2015 Reyk Floeter <reyk@openbsd.org>
1.1       deraadt     6:  *
1.4       kmos        7:  * Permission to use, copy, modify, and distribute this software for any
                      8:  * purpose with or without fee is hereby granted, provided that the above
                      9:  * copyright notice and this permission notice appear in all copies.
1.1       deraadt    10:  *
1.4       kmos       11:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     12:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     13:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     14:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     15:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     16:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     17:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.1       deraadt    18:  */
                     19:
                     20: #include <err.h>
1.4       kmos       21: #include <fcntl.h>
                     22: #include <libgen.h>
                     23: #include <limits.h>
1.1       deraadt    24: #include <stdio.h>
1.4       kmos       25: #include <stdint.h>
1.1       deraadt    26: #include <stdlib.h>
                     27: #include <string.h>
1.4       kmos       28: #include <strings.h>
1.1       deraadt    29: #include <unistd.h>
1.4       kmos       30: #ifndef NOSSL
                     31: #include <tls.h>
                     32: #endif
                     33:
                     34: #include "ftp.h"
                     35: #include "xmalloc.h"
                     36:
                     37: #define MAX_REDIRECTS  10
                     38:
                     39: #ifndef NOSSL
                     40: #define MINBUF         128
                     41:
                     42: static struct tls_config       *tls_config;
                     43: static struct tls              *ctx;
                     44: static int                      tls_session_fd = -1;
                     45: static char * const             tls_verify_opts[] = {
                     46: #define HTTP_TLS_CAFILE                0
                     47:        "cafile",
                     48: #define HTTP_TLS_CAPATH                1
                     49:        "capath",
                     50: #define HTTP_TLS_CIPHERS       2
                     51:        "ciphers",
                     52: #define HTTP_TLS_DONTVERIFY    3
                     53:        "dont",
                     54: #define HTTP_TLS_VERIFYDEPTH   4
                     55:        "depth",
                     56: #define HTTP_TLS_MUSTSTAPLE    5
                     57:        "muststaple",
                     58: #define HTTP_TLS_NOVERIFYTIME  6
                     59:        "noverifytime",
                     60: #define HTTP_TLS_SESSION       7
                     61:        "session",
                     62: #define HTTP_TLS_DOVERIFY      8
                     63:        "do",
                     64:        NULL
                     65: };
                     66: #endif /* NOSSL */
1.1       deraadt    67:
                     68: /*
1.4       kmos       69:  * HTTP status codes based on IANA assignments (2014-06-11 version):
                     70:  * https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
                     71:  * plus legacy (306) and non-standard (420).
1.1       deraadt    72:  */
1.4       kmos       73: static struct http_status {
                     74:        int              code;
                     75:        const char      *name;
                     76: } http_status[] = {
                     77:        { 100,  "Continue" },
                     78:        { 101,  "Switching Protocols" },
                     79:        { 102,  "Processing" },
                     80:        /* 103-199 unassigned */
                     81:        { 200,  "OK" },
                     82:        { 201,  "Created" },
                     83:        { 202,  "Accepted" },
                     84:        { 203,  "Non-Authoritative Information" },
                     85:        { 204,  "No Content" },
                     86:        { 205,  "Reset Content" },
                     87:        { 206,  "Partial Content" },
                     88:        { 207,  "Multi-Status" },
                     89:        { 208,  "Already Reported" },
                     90:        /* 209-225 unassigned */
                     91:        { 226,  "IM Used" },
                     92:        /* 227-299 unassigned */
                     93:        { 300,  "Multiple Choices" },
                     94:        { 301,  "Moved Permanently" },
                     95:        { 302,  "Found" },
                     96:        { 303,  "See Other" },
                     97:        { 304,  "Not Modified" },
                     98:        { 305,  "Use Proxy" },
                     99:        { 306,  "Switch Proxy" },
                    100:        { 307,  "Temporary Redirect" },
                    101:        { 308,  "Permanent Redirect" },
                    102:        /* 309-399 unassigned */
                    103:        { 400,  "Bad Request" },
                    104:        { 401,  "Unauthorized" },
                    105:        { 402,  "Payment Required" },
                    106:        { 403,  "Forbidden" },
                    107:        { 404,  "Not Found" },
                    108:        { 405,  "Method Not Allowed" },
                    109:        { 406,  "Not Acceptable" },
                    110:        { 407,  "Proxy Authentication Required" },
                    111:        { 408,  "Request Timeout" },
                    112:        { 409,  "Conflict" },
                    113:        { 410,  "Gone" },
                    114:        { 411,  "Length Required" },
                    115:        { 412,  "Precondition Failed" },
                    116:        { 413,  "Payload Too Large" },
                    117:        { 414,  "URI Too Long" },
                    118:        { 415,  "Unsupported Media Type" },
                    119:        { 416,  "Range Not Satisfiable" },
                    120:        { 417,  "Expectation Failed" },
                    121:        { 418,  "I'm a teapot" },
                    122:        /* 419-421 unassigned */
                    123:        { 420,  "Enhance Your Calm" },
                    124:        { 422,  "Unprocessable Entity" },
                    125:        { 423,  "Locked" },
                    126:        { 424,  "Failed Dependency" },
                    127:        /* 425 unassigned */
                    128:        { 426,  "Upgrade Required" },
                    129:        /* 427 unassigned */
                    130:        { 428,  "Precondition Required" },
                    131:        { 429,  "Too Many Requests" },
                    132:        /* 430 unassigned */
                    133:        { 431,  "Request Header Fields Too Large" },
                    134:        /* 432-450 unassigned */
                    135:        { 451,  "Unavailable For Legal Reasons" },
                    136:        /* 452-499 unassigned */
                    137:        { 500,  "Internal Server Error" },
                    138:        { 501,  "Not Implemented" },
                    139:        { 502,  "Bad Gateway" },
                    140:        { 503,  "Service Unavailable" },
                    141:        { 504,  "Gateway Timeout" },
                    142:        { 505,  "HTTP Version Not Supported" },
                    143:        { 506,  "Variant Also Negotiates" },
                    144:        { 507,  "Insufficient Storage" },
                    145:        { 508,  "Loop Detected" },
                    146:        /* 509 unassigned */
                    147:        { 510,  "Not Extended" },
                    148:        { 511,  "Network Authentication Required" },
                    149:        /* 512-599 unassigned */
                    150:        { 0,    NULL },
                    151:        };
                    152:
                    153: struct http_headers {
                    154:        char    *location;
                    155:        off_t    content_length;
                    156:        int      chunked;
                    157: };
                    158:
                    159: static void             decode_chunk(int, uint, FILE *);
                    160: static char            *header_lookup(const char *, const char *);
                    161: static const char      *http_error(int);
                    162: static void             http_headers_free(struct http_headers *);
                    163: static ssize_t          http_getline(int, char **, size_t *);
                    164: static size_t           http_read(int, char *, size_t);
                    165: static struct url      *http_redirect(struct url *, char *);
                    166: static void             http_save_chunks(struct url *, FILE *, off_t *);
                    167: static int              http_status_cmp(const void *, const void *);
                    168: static int              http_request(int, const char *,
                    169:                            struct http_headers **);
                    170: static char            *relative_path_resolve(const char *, const char *);
                    171:
                    172: #ifndef NOSSL
                    173: static void             tls_copy_file(struct url *, FILE *, off_t *);
                    174: static ssize_t          tls_getline(char **, size_t *, struct tls *);
                    175: #endif
                    176:
                    177: static FILE    *fp;
                    178:
                    179: void
                    180: http_connect(struct url *url, struct url *proxy, int timeout)
                    181: {
                    182:        const char      *host, *port;
                    183:        int              sock;
                    184:
                    185:        host = proxy ? proxy->host : url->host;
                    186:        port = proxy ? proxy->port : url->port;
                    187:        if ((sock = tcp_connect(host, port, timeout)) == -1)
                    188:                exit(1);
                    189:
                    190:        if ((fp = fdopen(sock, "r+")) == NULL)
                    191:                err(1, "%s: fdopen", __func__);
                    192:
                    193: #ifndef NOSSL
                    194:        struct http_headers     *headers;
                    195:        char                    *auth = NULL, *req;
                    196:        int                      authlen = 0, code;
                    197:
                    198:        if (url->scheme != S_HTTPS)
                    199:                return;
                    200:
                    201:        if (proxy) {
                    202:                if (url->basic_auth)
                    203:                        authlen = xasprintf(&auth,
                    204:                            "Proxy-Authorization: Basic %s\r\n",
                    205:                            url->basic_auth);
                    206:
                    207:                xasprintf(&req,
                    208:                    "CONNECT %s:%s HTTP/1.0\r\n"
                    209:                    "User-Agent: %s\r\n"
                    210:                    "%s"
                    211:                    "\r\n",
                    212:                    url->host, url->port,
                    213:                    useragent,
                    214:                    url->basic_auth ? auth : "");
                    215:
                    216:                freezero(auth, authlen);
                    217:                if ((code = http_request(S_HTTP, req, &headers)) != 200)
                    218:                        errx(1, "%s: failed to CONNECT to %s:%s: %s",
                    219:                            __func__, url->host, url->port, http_error(code));
                    220:
                    221:                free(req);
                    222:                http_headers_free(headers);
                    223:        }
                    224:
                    225:        if ((ctx = tls_client()) == NULL)
                    226:                errx(1, "failed to create tls client");
                    227:
                    228:        if (tls_configure(ctx, tls_config) != 0)
                    229:                errx(1, "%s: %s", __func__, tls_error(ctx));
                    230:
                    231:        if (tls_connect_socket(ctx, sock, url->host) != 0)
                    232:                errx(1, "%s: %s", __func__, tls_error(ctx));
                    233: #endif /* NOSSL */
                    234: }
                    235:
                    236: struct url *
                    237: http_get(struct url *url, struct url *proxy, off_t *offset, off_t *sz)
                    238: {
                    239:        struct http_headers     *headers;
                    240:        char                    *auth = NULL, *path = NULL, *range = NULL, *req;
                    241:        int                      authlen = 0, code, redirects = 0;
                    242:
                    243:  redirected:
                    244:        log_request("Requesting", url, proxy);
                    245:        if (*offset)
                    246:                xasprintf(&range, "Range: bytes=%lld-\r\n", *offset);
                    247:
                    248:        if (url->basic_auth)
                    249:                authlen = xasprintf(&auth, "Authorization: Basic %s\r\n",
                    250:                    url->basic_auth);
                    251:
                    252:        if (proxy && url->scheme != S_HTTPS)
                    253:                path = url_str(url);
                    254:        else if (url->path)
                    255:                path = url_encode(url->path);
                    256:
                    257:        xasprintf(&req,
                    258:            "GET %s HTTP/1.1\r\n"
                    259:            "Host: %s\r\n"
                    260:            "%s"
                    261:            "%s"
                    262:            "Connection: close\r\n"
                    263:            "User-Agent: %s\r\n"
                    264:            "\r\n",
                    265:            path ? path : "/",
                    266:            url->host,
                    267:            *offset ? range : "",
                    268:            url->basic_auth ? auth : "",
                    269:            useragent);
                    270:        code = http_request(url->scheme, req, &headers);
                    271:        freezero(auth, authlen);
                    272:        free(range);
                    273:        free(path);
                    274:        free(req);
                    275:        switch (code) {
                    276:        case 200:
                    277:                if (*offset) {
                    278:                        warnx("Server does not support resume.");
                    279:                        *offset = 0;
                    280:                }
                    281:                break;
                    282:        case 206:
                    283:                break;
                    284:        case 301:
                    285:        case 302:
                    286:        case 303:
                    287:        case 307:
                    288:                http_close(url);
                    289:                if (++redirects > MAX_REDIRECTS)
                    290:                        errx(1, "Too many redirections requested");
                    291:
                    292:                if (headers->location == NULL)
                    293:                        errx(1, "%s: Location header missing", __func__);
                    294:
                    295:                url = http_redirect(url, headers->location);
                    296:                http_headers_free(headers);
                    297:                log_request("Redirected to", url, proxy);
                    298:                http_connect(url, proxy, 0);
                    299:                goto redirected;
                    300:        case 416:
                    301:                errx(1, "File is already fully retrieved.");
                    302:                break;
                    303:        default:
                    304:                errx(1, "Error retrieving file: %d %s", code, http_error(code));
                    305:        }
                    306:
                    307:        *sz = headers->content_length + *offset;
                    308:        url->chunked = headers->chunked;
                    309:        http_headers_free(headers);
                    310:        return url;
                    311: }
                    312:
                    313: void
                    314: http_save(struct url *url, FILE *dst_fp, off_t *offset)
                    315: {
                    316:        if (url->chunked)
                    317:                http_save_chunks(url, dst_fp, offset);
                    318: #ifndef NOSSL
                    319:        else if (url->scheme == S_HTTPS)
                    320:                tls_copy_file(url, dst_fp, offset);
                    321: #endif
                    322:        else
                    323:                copy_file(dst_fp, fp, offset);
                    324: }
                    325:
                    326: static struct url *
                    327: http_redirect(struct url *old_url, char *location)
                    328: {
                    329:        struct url      *new_url;
                    330:
                    331:        /* absolute uri reference */
                    332:        if (strncasecmp(location, "http", 4) == 0 ||
                    333:            strncasecmp(location, "https", 5) == 0) {
                    334:                if ((new_url = url_parse(location)) == NULL)
                    335:                        exit(1);
                    336:
                    337:                goto done;
                    338:        }
                    339:
                    340:        /* relative uri reference */
                    341:        new_url = xcalloc(1, sizeof *new_url);
                    342:        new_url->scheme = old_url->scheme;
                    343:        new_url->host = xstrdup(old_url->host);
                    344:        new_url->port = xstrdup(old_url->port);
                    345:
                    346:        /* absolute-path reference */
                    347:        if (location[0] == '/')
                    348:                new_url->path = xstrdup(location);
                    349:        else
                    350:                new_url->path = relative_path_resolve(old_url->path, location);
                    351:
                    352:  done:
                    353:        new_url->fname = xstrdup(old_url->fname);
                    354:        url_free(old_url);
                    355:        return new_url;
                    356: }
                    357:
                    358: static char *
                    359: relative_path_resolve(const char *base_path, const char *location)
                    360: {
                    361:        char    *new_path, *p;
                    362:
                    363:        /* trim fragment component from both uri */
                    364:        if ((p = strchr(location, '#')) != NULL)
1.1       deraadt   365:                *p = '\0';
1.4       kmos      366:        if (base_path && (p = strchr(base_path, '#')) != NULL)
                    367:                *p = '\0';
                    368:
                    369:        if (base_path == NULL)
                    370:                xasprintf(&new_path, "/%s", location);
                    371:        else if (base_path[strlen(base_path) - 1] == '/')
                    372:                xasprintf(&new_path, "%s%s", base_path, location);
                    373:        else {
                    374:                p = dirname(base_path);
                    375:                xasprintf(&new_path, "%s/%s",
                    376:                    strcmp(p, ".") == 0 ? "" : p, location);
1.1       deraadt   377:        }
                    378:
1.4       kmos      379:        return new_path;
                    380: }
                    381:
                    382: static void
                    383: http_save_chunks(struct url *url, FILE *dst_fp, off_t *offset)
                    384: {
                    385:        char    *buf = NULL;
                    386:        size_t   n = 0;
                    387:        uint     chunk_sz;
                    388:
                    389:        http_getline(url->scheme, &buf, &n);
                    390:        if (sscanf(buf, "%x", &chunk_sz) != 1)
                    391:                errx(1, "%s: Failed to get chunk size", __func__);
                    392:
                    393:        while (chunk_sz > 0) {
                    394:                decode_chunk(url->scheme, chunk_sz, dst_fp);
                    395:                *offset += chunk_sz;
                    396:                http_getline(url->scheme, &buf, &n);
                    397:                if (sscanf(buf, "%x", &chunk_sz) != 1)
                    398:                        errx(1, "%s: Failed to get chunk size", __func__);
1.1       deraadt   399:        }
                    400:
1.4       kmos      401:        free(buf);
                    402: }
                    403:
                    404: static void
                    405: decode_chunk(int scheme, uint sz, FILE *dst_fp)
                    406: {
                    407:        size_t  bufsz;
                    408:        size_t  r;
                    409:        char    buf[BUFSIZ], crlf[2];
                    410:
                    411:        bufsz = sizeof(buf);
                    412:        while (sz > 0) {
                    413:                if (sz < bufsz)
                    414:                        bufsz = sz;
                    415:
                    416:                r = http_read(scheme, buf, bufsz);
                    417:                if (fwrite(buf, 1, r, dst_fp) != r)
                    418:                        errx(1, "%s: fwrite", __func__);
                    419:
                    420:                sz -= r;
                    421:        }
                    422:
                    423:        /* CRLF terminating the chunk */
                    424:        if (http_read(scheme, crlf, sizeof(crlf)) != sizeof(crlf))
                    425:                errx(1, "%s: Failed to read terminal crlf", __func__);
                    426:
                    427:        if (crlf[0] != '\r' || crlf[1] != '\n')
                    428:                errx(1, "%s: Invalid chunked encoding", __func__);
                    429: }
                    430:
                    431: void
                    432: http_close(struct url *url)
                    433: {
                    434: #ifndef NOSSL
                    435:        ssize_t r;
                    436:
                    437:        if (url->scheme == S_HTTPS) {
                    438:                if (tls_session_fd != -1)
                    439:                        dprintf(STDERR_FILENO, "tls session resumed: %s\n",
                    440:                            tls_conn_session_resumed(ctx) ? "yes" : "no");
                    441:
                    442:                do {
                    443:                        r = tls_close(ctx);
                    444:                } while (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT);
                    445:                tls_free(ctx);
                    446:        }
                    447:
                    448: #endif
                    449:        fclose(fp);
                    450: }
                    451:
                    452: static int
                    453: http_request(int scheme, const char *req, struct http_headers **hdrs)
                    454: {
                    455:        struct http_headers     *headers;
                    456:        const char              *e;
                    457:        char                    *buf = NULL, *p;
                    458:        size_t                   n = 0;
                    459:        ssize_t                  buflen;
                    460:        uint                     code;
                    461: #ifndef NOSSL
                    462:        ssize_t                  nw;
                    463: #endif
                    464:
                    465:        if (io_debug)
                    466:                fprintf(stderr, "<<< %s", req);
                    467:
                    468:        switch (scheme) {
                    469: #ifndef NOSSL
                    470:        case S_HTTPS:
                    471:                do {
                    472:                        nw = tls_write(ctx, req, strlen(req));
                    473:                } while (nw == TLS_WANT_POLLIN || nw == TLS_WANT_POLLOUT);
                    474:                if (nw == -1)
                    475:                        errx(1, "%s: tls_write: %s", __func__, tls_error(ctx));
                    476:                break;
                    477: #endif
                    478:        case S_FTP:
                    479:        case S_HTTP:
                    480:                if (fprintf(fp, "%s", req) < 0)
                    481:                        errx(1, "%s: fprintf", __func__);
                    482:                (void)fflush(fp);
                    483:                break;
                    484:        }
                    485:
                    486:        http_getline(scheme, &buf, &n);
                    487:        if (io_debug)
                    488:                fprintf(stderr, ">>> %s", buf);
                    489:
                    490:        if (sscanf(buf, "%*s %u %*s", &code) != 1)
                    491:                errx(1, "%s: failed to extract status code", __func__);
                    492:
                    493:        if (code < 100 || code > 511)
                    494:                errx(1, "%s: invalid status code %d", __func__, code);
                    495:
                    496:        headers = xcalloc(1, sizeof *headers);
                    497:        for (;;) {
                    498:                buflen = http_getline(scheme, &buf, &n);
                    499:                buflen -= 1;
                    500:                if (buflen > 0 && buf[buflen - 1] == '\r')
                    501:                        buflen -= 1;
                    502:                buf[buflen] = '\0';
                    503:
                    504:                if (io_debug)
                    505:                        fprintf(stderr, ">>> %s\n", buf);
                    506:
                    507:                if (buflen == 0)
                    508:                        break; /* end of headers */
                    509:
                    510:                if ((p = header_lookup(buf, "Content-Length:")) != NULL) {
                    511:                        headers->content_length = strtonum(p, 0, INT64_MAX, &e);
                    512:                        if (e)
                    513:                                err(1, "%s: Content-Length is %s: %lld",
                    514:                                    __func__, e, headers->content_length);
                    515:                }
                    516:
                    517:                if ((p = header_lookup(buf, "Location:")) != NULL)
                    518:                        headers->location = xstrdup(p);
                    519:
                    520:                if ((p = header_lookup(buf, "Transfer-Encoding:")) != NULL)
                    521:                        if (strcasestr(p, "chunked") != NULL)
                    522:                                headers->chunked = 1;
                    523:
1.1       deraadt   524:        }
                    525:
1.4       kmos      526:        *hdrs = headers;
                    527:        free(buf);
                    528:        return code;
                    529: }
                    530:
                    531: static void
                    532: http_headers_free(struct http_headers *headers)
                    533: {
                    534:        if (headers == NULL)
                    535:                return;
                    536:
                    537:        free(headers->location);
                    538:        free(headers);
                    539: }
                    540:
                    541: static char *
                    542: header_lookup(const char *buf, const char *key)
                    543: {
                    544:        char    *p;
                    545:
                    546:        if (strncasecmp(buf, key, strlen(key)) == 0) {
                    547:                if ((p = strchr(buf, ' ')) == NULL)
                    548:                        errx(1, "Failed to parse %s", key);
                    549:                return ++p;
1.1       deraadt   550:        }
                    551:
1.4       kmos      552:        return NULL;
                    553: }
                    554:
                    555: static const char *
                    556: http_error(int code)
                    557: {
                    558:        struct http_status      error, *res;
                    559:
                    560:        /* Set up key */
                    561:        error.code = code;
                    562:
                    563:        if ((res = bsearch(&error, http_status,
                    564:            sizeof(http_status) / sizeof(http_status[0]) - 1,
                    565:            sizeof(http_status[0]), http_status_cmp)) != NULL)
                    566:                return (res->name);
                    567:
                    568:        return (NULL);
                    569: }
                    570:
                    571: static int
                    572: http_status_cmp(const void *a, const void *b)
                    573: {
                    574:        const struct http_status *ea = a;
                    575:        const struct http_status *eb = b;
                    576:
                    577:        return (ea->code - eb->code);
                    578: }
1.1       deraadt   579:
1.4       kmos      580:
                    581: static ssize_t
                    582: http_getline(int scheme, char **buf, size_t *n)
                    583: {
                    584:        ssize_t buflen;
                    585:
                    586:        switch (scheme) {
                    587: #ifndef NOSSL
                    588:        case S_HTTPS:
                    589:                if ((buflen = tls_getline(buf, n, ctx)) == -1)
                    590:                        errx(1, "%s: tls_getline", __func__);
                    591:                break;
                    592: #endif
                    593:        case S_FTP:
                    594:        case S_HTTP:
                    595:                if ((buflen = getline(buf, n, fp)) == -1)
                    596:                        err(1, "%s: getline", __func__);
                    597:                break;
                    598:        default:
                    599:                errx(1, "%s: invalid scheme", __func__);
1.1       deraadt   600:        }
                    601:
1.4       kmos      602:        return buflen;
                    603: }
                    604:
                    605: static size_t
                    606: http_read(int scheme, char *buf, size_t size)
                    607: {
                    608:        size_t  r;
                    609: #ifndef NOSSL
                    610:        ssize_t rs;
                    611: #endif
                    612:
                    613:        switch (scheme) {
                    614: #ifndef NOSSL
                    615:        case S_HTTPS:
                    616:                do {
                    617:                        rs = tls_read(ctx, buf, size);
                    618:                } while (rs == TLS_WANT_POLLIN || rs == TLS_WANT_POLLOUT);
                    619:                if (rs == -1)
                    620:                        errx(1, "%s: tls_read: %s", __func__, tls_error(ctx));
                    621:                r = rs;
                    622:                break;
                    623: #endif
                    624:        case S_HTTP:
                    625:                if ((r = fread(buf, 1, size, fp)) < size)
                    626:                        if (!feof(fp))
                    627:                                errx(1, "%s: fread", __func__);
                    628:                break;
                    629:        default:
                    630:                errx(1, "%s: invalid scheme", __func__);
                    631:        }
                    632:
                    633:        return r;
                    634: }
                    635:
                    636: #ifndef NOSSL
                    637: void
                    638: https_init(char *tls_options)
                    639: {
                    640:        char            *str;
                    641:        int              depth;
                    642:        const char      *ca_file, *errstr;
                    643:
                    644:        if (tls_init() != 0)
                    645:                errx(1, "tls_init failed");
                    646:
                    647:        if ((tls_config = tls_config_new()) == NULL)
                    648:                errx(1, "tls_config_new failed");
                    649:
                    650:        if (tls_config_set_ciphers(tls_config, "legacy") != 0)
                    651:                errx(1, "tls set ciphers failed: %s",
                    652:                    tls_config_error(tls_config));
                    653:
                    654:        ca_file = tls_default_ca_cert_file();
                    655:        while (tls_options && *tls_options) {
                    656:                switch (getsubopt(&tls_options, tls_verify_opts, &str)) {
                    657:                case HTTP_TLS_CAFILE:
                    658:                        if (str == NULL)
                    659:                                errx(1, "missing CA file");
                    660:                        ca_file = str;
1.1       deraadt   661:                        break;
1.4       kmos      662:                case HTTP_TLS_CAPATH:
                    663:                        if (str == NULL)
                    664:                                errx(1, "missing ca path");
                    665:                        if (tls_config_set_ca_path(tls_config, str) != 0)
                    666:                                errx(1, "tls ca path failed");
                    667:                        break;
                    668:                case HTTP_TLS_CIPHERS:
                    669:                        if (str == NULL)
                    670:                                errx(1, "missing cipher list");
                    671:                        if (tls_config_set_ciphers(tls_config, str) != 0)
                    672:                                errx(1, "tls set ciphers failed");
                    673:                        break;
                    674:                case HTTP_TLS_DONTVERIFY:
                    675:                        tls_config_insecure_noverifycert(tls_config);
                    676:                        tls_config_insecure_noverifyname(tls_config);
                    677:                        break;
                    678:                case HTTP_TLS_VERIFYDEPTH:
                    679:                        if (str == NULL)
                    680:                                errx(1, "missing depth");
                    681:                        depth = strtonum(str, 0, INT_MAX, &errstr);
                    682:                        if (errstr)
                    683:                                errx(1, "Cert validation depth is %s", errstr);
                    684:                        tls_config_set_verify_depth(tls_config, depth);
                    685:                        break;
                    686:                case HTTP_TLS_MUSTSTAPLE:
                    687:                        tls_config_ocsp_require_stapling(tls_config);
                    688:                        break;
                    689:                case HTTP_TLS_NOVERIFYTIME:
                    690:                        tls_config_insecure_noverifytime(tls_config);
                    691:                        break;
                    692:                case HTTP_TLS_SESSION:
                    693:                        if (str == NULL)
                    694:                                errx(1, "missing session file");
                    695:                        tls_session_fd = open(str, O_RDWR|O_CREAT, 0600);
                    696:                        if (tls_session_fd == -1)
                    697:                                err(1, "failed to open or create session file "
                    698:                                    "'%s'", str);
                    699:                        if (tls_config_set_session_fd(tls_config,
                    700:                            tls_session_fd) == -1)
                    701:                                errx(1, "failed to set session: %s",
                    702:                                    tls_config_error(tls_config));
                    703:                        break;
                    704:                case HTTP_TLS_DOVERIFY:
                    705:                        /* For compatibility, we do verify by default */
                    706:                        break;
                    707:                default:
                    708:                        errx(1, "Unknown -S suboption `%s'",
                    709:                            suboptarg ? suboptarg : "");
                    710:                }
1.1       deraadt   711:        }
                    712:
1.4       kmos      713:        if (tls_config_set_ca_file(tls_config, ca_file) == -1)
                    714:                errx(1, "tls_config_set_ca_file failed");
                    715: }
                    716:
                    717: static ssize_t
                    718: tls_getline(char **buf, size_t *buflen, struct tls *tls)
                    719: {
                    720:        char            *newb;
                    721:        size_t           newlen, off;
                    722:        int              ret;
                    723:        unsigned char    c;
                    724:
                    725:        if (buf == NULL || buflen == NULL)
                    726:                return -1;
                    727:
                    728:        /* If buf is NULL, we have to assume a size of zero */
                    729:        if (*buf == NULL)
                    730:                *buflen = 0;
                    731:
                    732:        off = 0;
                    733:        do {
                    734:                do {
                    735:                        ret = tls_read(tls, &c, 1);
                    736:                } while (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT);
                    737:                if (ret == -1)
                    738:                        return -1;
                    739:
                    740:                /* Ensure we can handle it */
                    741:                if (off + 2 > SSIZE_MAX)
                    742:                        return -1;
                    743:
                    744:                newlen = off + 2; /* reserve space for NUL terminator */
                    745:                if (newlen > *buflen) {
                    746:                        newlen = newlen < MINBUF ? MINBUF : *buflen * 2;
                    747:                        newb = recallocarray(*buf, *buflen, newlen, 1);
                    748:                        if (newb == NULL)
                    749:                                return -1;
                    750:
                    751:                        *buf = newb;
                    752:                        *buflen = newlen;
                    753:                }
                    754:
                    755:                *(*buf + off) = c;
                    756:                off += 1;
                    757:        } while (c != '\n');
                    758:
                    759:        *(*buf + off) = '\0';
                    760:        return off;
                    761: }
                    762:
                    763: static void
                    764: tls_copy_file(struct url *url, FILE *dst_fp, off_t *offset)
                    765: {
                    766:        char    *tmp_buf;
                    767:        ssize_t  r;
                    768:
                    769:        tmp_buf = xmalloc(TMPBUF_LEN);
                    770:        for (;;) {
                    771:                do {
                    772:                        r = tls_read(ctx, tmp_buf, TMPBUF_LEN);
                    773:                } while (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT);
                    774:
                    775:                if (r == -1)
                    776:                        errx(1, "%s: tls_read: %s", __func__, tls_error(ctx));
                    777:                else if (r == 0)
                    778:                        break;
1.1       deraadt   779:
1.4       kmos      780:                *offset += r;
                    781:                if (fwrite(tmp_buf, 1, r, dst_fp) != (size_t)r)
                    782:                        err(1, "%s: fwrite", __func__);
                    783:        }
                    784:        free(tmp_buf);
                    785: }
                    786: #endif /* NOSSL */