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