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 */