]> pd.if.org Git - zpackage/blob - src/fetchurl.c
1ef4abd2c1f70901b01ca1ff020e4ce9d20e7278
[zpackage] / src / fetchurl.c
1 #define _POSIX_C_SOURCE 200809L
2
3 #include <stdio.h>
4 #include <sys/types.h>
5 #include <sys/socket.h>
6 #include <netinet/in.h>
7 #include <netdb.h>
8 #include <string.h>
9 #include <strings.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <signal.h>
13 #include <time.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16
17 #include "tlse.h"
18
19 struct tls_uri {
20         char *scheme;
21         char *userinfo;
22         char *host;
23         char *port;
24         char *path;
25         char *query;
26         char *fragment;
27 };
28 int tls_parse_uri(char *, struct tls_uri *);
29 void tls_free_uri(struct tls_uri *);
30
31 int open_tcp_connection(char *host, int port);
32
33 /* if trustpolicy is 0, we just accept anything */
34 int verify_trust(struct TLSContext *context, struct TLSCertificate **chain, int
35                 len) {
36         /* suppress unused */
37         if (context || chain || len) {
38                 return 0;
39         }
40
41         return 0;
42 }
43
44 static char hexchars[] = "0123456789abcdefABCDEF";
45
46 static void hex(char *dst, uint8_t *src, size_t len) {
47         while (len--) {
48                 dst[0] = hexchars[(src[0]>>4)&0xf];
49                 dst[1] = hexchars[src[0]&0xf];
50                 dst+=2;
51                 src++;
52         }
53 }
54
55 #if 0
56 static void hexbin(uint8_t *dst, unsigned char *src, size_t len) {
57         size_t i;
58         int x;
59
60         for (i=0; i<len; i+=2) {
61                 sscanf((const char *)src+i, "%02x", &x);
62                 dst[i/2] = x;
63         }
64 }
65 #endif
66
67 char *my_getline(struct tls_buffer *b, int fd, size_t *size) {
68         char *loc = 0;
69         char buf[4096];
70         ssize_t rv = 0;
71
72         while (b->error == 0) {
73                 loc = memchr(b->buffer, '\n', b->len);
74                 if (loc) {
75                         *size = loc - b->buffer + 1;
76                         return b->buffer;
77                 } else {
78                         rv = read(fd, buf, sizeof buf);
79                         if (rv == -1) {
80                                 return 0;
81                         }
82                         if (rv == 0) {
83                                 break;
84                         }
85                         tls_buffer_append(b, buf, rv);
86                 }
87         }
88         if (rv == 0) {
89                 *size = b->len;
90                 return b->buffer;
91         }
92         return 0;
93 }
94
95 /*
96  * We use a trust on first use policy.  The trust DB is a simple
97  * file in /var/lib/zpm/known_hosts, or ~/.zpm/known_hosts, or ZPM_KNOWNHOSTS.
98  * if -k is given, no verification is done
99  */
100 int verify_first(struct TLSContext *context, struct TLSCertificate **chain, int
101                 certs) {
102         int err;
103         char *trustfile, *homedir = 0, *host, *fp;
104         unsigned char certhash[65];
105         int trustdb;
106         struct tls_buffer tbuf;
107
108         char *line = 0;
109         size_t len = 0;
110
111         if (certs == 0 || chain == 0) {
112                 return 1;
113         }
114
115         err = tls_certificate_is_valid(chain[0]);
116         if (err) {
117                 return err;
118         }
119
120         if (context->sni) {
121                 err = tls_certificate_valid_subject(chain[0], context->sni);
122                 if (err) {
123                         return err;
124                 }
125         }
126
127         hex(certhash, chain[0]->fp, 32);
128         certhash[64] = 0;
129
130         trustfile = getenv("ZPM_KNOWNHOSTS");
131         if (!trustfile) {
132                 if (geteuid() == 0) {
133                         trustfile = "/var/lib/zpm/known_hosts";
134                 } else {
135                         /* we could do this with a series of
136                          * openat() calls instead of building
137                          * up a string 
138                          */
139                         trustfile = getenv("HOME");
140                         if (!trustfile) {
141                                 fprintf(stderr, "home = %s\n", trustfile);
142                                 return 1;
143                         }
144                         len = snprintf(homedir, 0, "%s/.zpm/known_hosts", trustfile);
145                         homedir = malloc(len+1);
146                         if (!homedir) {
147                                 return 1;
148                         }
149                         len = snprintf(homedir, len+1, "%s/.zpm/known_hosts", trustfile);
150                         trustfile = homedir;
151                 }
152         }
153         /* cert is valid on its face, so check against the trust db */
154         trustdb = open(trustfile, O_RDWR|O_CREAT, 0600);
155         if (trustdb == -1) {
156                 fprintf(stderr, "cannot open trustdb %s: %s\n", trustfile, strerror(errno));
157                 if (homedir) {
158                         free(homedir);
159                 }
160                 return 1;
161         }
162
163         if (homedir) {
164                 free(homedir);
165         }
166
167         len = 0;
168         tls_buffer_init(&tbuf, 128);
169         do {
170                 char *off;
171
172                 tls_buffer_shift(&tbuf, len);
173                 line = my_getline(&tbuf, trustdb, &len);
174
175                 if (!line || !len) {
176                         break;
177                 }
178
179                 fp = line;
180                 while (isspace(*fp)) {
181                         fp++;
182                 }
183                 if (*fp == '#') {
184                         continue;
185                 }
186                 off = strchr(line, ':');
187                 if (!off) {
188                         continue;
189                 }
190                 host = off + 1;
191                 *off = 0;
192                 if (line[len-1] == '\n') {
193                         line[len-1] = 0;
194                 }
195
196                 if (strlen(fp) != 64) {
197                         continue;
198                 }
199
200                 if (len && line[len-1] == '\n') {
201                         line[len-1] = 0;
202                 }
203
204                 if (strcmp(context->sni, host) != 0) {
205                         continue;
206                 }
207
208                 int match = (memcmp(certhash, fp, 64) == 0); 
209                 if (!match) {
210                         fprintf(stderr, "host %s certificate changed\n", host);
211                         fprintf(stderr, "was %.64s\n", fp);
212                         fprintf(stderr, "now %.64s\n", certhash);
213                 }
214
215                 close(trustdb);
216                 tls_buffer_free(&tbuf);
217                 return match ? no_error : bad_certificate;
218         } while (!tbuf.error);
219
220         /* got here, so we should be at EOF, so add this host to trust db */
221         lseek(trustdb, 0, SEEK_END);
222
223         /* re-use the buffer so we only do one write */
224         /* ignore errors, the cert is fine, we just can't update
225          * the trustdb if there's errors here
226          */
227         tbuf.len = 0;
228         tls_buffer_append(&tbuf, certhash, 64);
229         tls_buffer_append_byte(&tbuf, ':');
230         tls_buffer_append(&tbuf, context->sni, strlen(context->sni));
231         tls_buffer_append_byte(&tbuf, '\n');
232         write(trustdb, tbuf.buffer, tbuf.len);
233         close(trustdb);
234         tls_buffer_free(&tbuf);
235
236         return no_error;
237 }
238
239 int verify_roots(struct TLSContext *context, struct TLSCertificate **chain, int
240                 len) {
241         int i, err;
242
243         if (chain) {
244                 for (i = 0; i < len; i++) {
245                         struct TLSCertificate *certificate = chain[i];
246                         err = tls_certificate_is_valid(certificate);
247                         if (err) {
248                                 return err;
249                         }
250                 }
251         }
252         
253         err = tls_certificate_chain_is_valid(chain, len);
254         if (err) {
255                 return err;
256         }
257
258         if (len > 0 && context->sni) {
259                 err = tls_certificate_valid_subject(chain[0], context->sni);
260                 if (err) {
261                         return err;
262                 }
263         }
264
265         /* Perform certificate validation against ROOT CA */
266         err = tls_certificate_chain_is_valid_root(context, chain, len);
267         if (err) {
268                 return err;
269         }
270
271         return no_error;
272 }
273
274 struct io {
275         struct tls_buffer response;
276         struct TLSContext *tls;
277         int socket;
278         int status_code;
279         time_t last_modified;
280         time_t date;
281         size_t content_length;
282         char *redirect;
283 };
284
285 int month(char *m) {
286         char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
287                 "Aug", "Sep", "Oct", "Nov", "Dec" };
288         int i;
289
290         for (i=0; i < 12; i++) {
291                 if (!strncasecmp(m, months[i], 3)) {
292                         return i+1;
293                 }
294         }
295         return 0;
296 }
297
298 /* Wed, 06 Feb 2019 10:06:05 GMT */
299 time_t parse_date(char *d) {
300         struct tm tm = { 0 };
301         int rv;
302
303         int h, m, s, dom, Y;
304         char M[4];
305         rv = sscanf(d, "%*[a-zA-Z,] %d %s %d %d:%d:%d", &dom, M, &Y, &h, &m, &s);
306         if (rv == 6) {
307                 tm.tm_year = Y - 1900;
308                 tm.tm_hour = h;
309                 tm.tm_min = m;
310                 tm.tm_sec = s;
311                 tm.tm_mon = month(M)-1;
312                 tm.tm_mday = dom;
313
314                 return mktime(&tm);
315         }
316         return -1;
317 }
318
319 char *find_header(struct io *io, char *header, size_t *len) {
320         char *eoh, *soh;
321         size_t hlen;
322
323         *len = 0;
324
325         hlen = strlen(header);
326         eoh = strstr(io->response.buffer, "\r\n\r\n");
327         if (!eoh) {
328                 return 0;
329         }
330         soh = io->response.buffer;
331         do {
332                 soh = strstr(soh, "\r\n");
333                 if (soh == eoh) {
334                         break;
335                 }
336                 soh += 2;
337                 if (!memcmp(soh, header, hlen)) {
338                         break;
339                 }
340         } while (soh < eoh);
341
342         if (soh >= eoh) {
343                 return 0;
344         }
345         eoh = strstr(soh, "\r\n");
346         soh += hlen;
347         while (soh < eoh && isspace(*soh)) {
348                 soh++;
349         }
350         *len = (eoh - soh);
351         return soh;
352 }
353
354 void parse_header(struct io *io) {
355         char *s = io->response.buffer;
356         int code = 0;
357         char *hval;
358         size_t hlen;
359
360         while (!isspace(*s)) {
361                 s++;
362         }
363         while (isspace(*s)) {
364                 s++;
365         }
366         code = strtol(s, 0, 10);
367         io->status_code = code;
368
369         hval = find_header(io, "Date:", &hlen);
370         if (hval) {
371                 hval[hlen] = 0;
372                 io->date = parse_date(hval);
373                 hval[hlen] = '\r';
374         }
375         hval = find_header(io, "Last-Modified:", &hlen);
376         if (hval) {
377                 hval[hlen] = 0;
378                 io->last_modified = parse_date(hval);
379                 hval[hlen] = '\r';
380         }
381
382         hval = find_header(io, "Content-Length:", &hlen);
383         if (hval) {
384                 hval[hlen] = 0;
385                 io->content_length = strtoul(hval, 0, 10);
386                 hval[hlen] = '\r';
387         }
388
389         switch (code) {
390                 case 301:
391                 case 302:
392                 case 303:
393                 case 307:
394                         hval = find_header(io, "Location:", &hlen);
395                         if (hval) {
396                                 io->redirect = strndup(hval, hlen);
397                         }
398                         break;
399                 default:
400                         break;
401         }
402
403 }
404
405 ssize_t fill_buffer(struct io *io) {
406         unsigned char buffer[4096];
407         ssize_t ret;
408
409         if (io->tls) {
410                 ret = tls_read(io->tls, buffer, sizeof buffer);
411         } else {
412                 ret = read(io->socket, buffer, sizeof buffer);
413         }
414
415         if (ret > 0) {
416                 tls_buffer_append(&io->response, buffer, ret);
417         }
418
419         return ret;
420 }
421
422 #if 0
423 char *nextline(struct io *io) {
424         char *eol = 0;;
425
426         eol = memchr(io->response.buffer, '\n', io.response.size);
427         while (eol == 0) {
428                 fill_buffer(io);
429                 eol = memchr(io->response.buffer, '\n', io.response.size);
430         }
431         if (eol) {
432
433 }
434 #endif
435
436 void append_header(struct tls_buffer *buf, char *header, char *val) {
437         tls_buffer_append(buf, header, strlen(header));
438         tls_buffer_append(buf, ": ", 2);
439         tls_buffer_append(buf, val, strlen(val));
440         tls_buffer_append(buf, "\r\n", 2);
441 }
442
443 void append_timeheader(struct tls_buffer *buf, char *header, time_t ts) {
444         char timestr[80];
445         struct tm *tm;
446
447         tm = gmtime(&ts);
448
449         strftime(timestr, sizeof timestr, "%a, %d %b %Y %H:%M:%S GMT", tm);
450         append_header(buf, header, timestr);
451 }
452
453 static void pdots(int len, int ch, unsigned long was, unsigned long now,
454                 unsigned long total) {
455         was = len * was / total;
456         if (now > total) {
457                 now = total;
458         }
459         now = len * now / total;
460         while (was++ < now) {
461                 putc(ch,stderr);
462         }
463 }
464
465 static void fake_header(struct io *io, int fd) {
466         struct stat st;
467         int code = 200, rv;
468         char *message, codestr[5], length[32];
469         struct tls_buffer *hdr = &io->response;
470
471         if (fd == -1) {
472                 switch (errno) {
473                         case EACCES: code = 403; break;
474                         case ENOENT: code = 404; break;
475                         default: code = 500; break;
476                 }
477         } else {
478                 rv = fstat(fd, &st);
479                 if (rv == -1) {
480                         code = 500;
481                 }
482         }
483
484         if (io->last_modified >= st.st_mtime) {
485                 code = 304;
486         }
487
488         switch (code) {
489                 case 200: message = "OK"; break;
490                 case 304: message = "Not Modified"; break;
491                 case 403: message = "Forbidden"; break;
492                 case 404: message = "Not Found"; break;
493                 case 500: message = "Internal Server Error"; break;
494                 default: break;
495         }
496         sprintf(codestr, "%0.3d ", code);
497         tls_buffer_append(hdr, "HTTP/1.1 ", 9);
498         tls_buffer_append(hdr, codestr, 4);
499         tls_buffer_append_str(hdr, message);
500         tls_buffer_append(hdr, "\r\n", 2);
501
502         append_timeheader(hdr, "Date", time(NULL));
503         append_header(hdr, "Server", "zpm-fetchurl/0.9");
504         if (code < 400) {
505                 append_timeheader(hdr, "Last-Modified", st.st_mtime);
506                 sprintf(length, "%zu", st.st_size);
507                 append_header(hdr, "Content-Length", length);
508         }
509
510         append_header(hdr, "Connection", "close");
511         append_header(hdr, "Content-Type", "application/octet-stream");
512         tls_buffer_append(hdr, "\r\n", 2);
513 }
514
515 char *pathlast(char *path) {
516         char *last = 0;
517         size_t len = 0;
518
519         if (path == 0) {
520                 return 0;
521         }
522
523         while (*path == '/') {
524                 path++;
525         }
526
527         do {
528                 last = path;
529                 len = 0;
530                 while (*path && *path != '/') {
531                         path++;
532                         len++;
533                 }
534                 while (*path == '/') {
535                         path++;
536                 }
537         } while (*path);
538
539         if (len == 0) {
540                 return 0;
541         }
542
543         return strndup(last, len);
544 }
545
546 static time_t file_mtime(char *path) {
547         struct stat st;
548         int rv;
549
550         rv = stat(path, &st);
551         if (rv == -1) {
552                 if (errno == ENOENT) {
553                         return 0;
554                 } 
555                 perror("stat failed:");
556                 return -1;
557         }
558
559         return st.st_mtime;
560 }
561
562 int main(int ac, char *av[]) {
563         int sockfd, port = -1, rv;
564         ssize_t ret;
565         int option;
566 #if 0
567         char msg[] = "GET %s HTTP/1.1\r\nHost: %s:%i\r\nConnection: close\r\n\r\n";
568         char msg2[] = "GET %s HTTP/1.1\r\nHost: %s:%i\r\nLast-Modified: %s\r\nConnection: close\r\n\r\n";
569         char msg_buffer[1024];
570 #endif
571         char *req_file = 0;
572         char *host = 0;
573         struct tls_uri uri = { 0 };
574         char *outfile = 0;
575         int raw = 0, head = 0;
576         int out = 1; /* output file descriptor */
577         int use_tls = 0;
578         struct io io = { {0}, 0, -1, 0, 0, 0, 0, 0 };
579         struct TLSContext *clientssl = 0;
580         int failsilent = 0;
581         char *lmfile = 0;
582         int progressbar = 0;
583         struct tls_buffer request;
584         char lmtime[80];
585         char *eoh = 0;
586         size_t total = 0;
587         size_t header_len;
588         char *url = 0;
589         int redirs = 0, redirlimit = 50, printstatus = 0;
590         int verifypolicy = 1, calcoutfile = 0, ifnewer = 0;
591
592         ltc_mp = tfm_desc;
593
594         while ((option = getopt(ac, av, "o:OrIfz:np#R:SkK")) != -1) {
595                 switch (option) {
596                         case 'o': outfile = optarg; break;
597                         case 'O': calcoutfile = 1; break;
598                         case 'S': printstatus = 1; head = 1; break;
599                         case 'k': verifypolicy = 0; break;
600                         case 'K': verifypolicy = 2; break;
601                         case 'I': head = 1;
602                         case 'r': raw = 1; break;
603                         case 'f': failsilent = 1; break;
604                         case 'z': lmfile = optarg; break;
605                         case 'n': ifnewer = 1; break;
606                         case 'R': redirlimit = strtol(optarg, 0, 10); break;
607                         case 'p':
608                         case '#': progressbar = 1; break;
609                         default:
610                                   exit(EXIT_FAILURE);
611                                   break;
612                 }
613         }
614
615         if (ac < optind) {
616                 fprintf(stderr, "Usage: %s uri\n", av[0]);
617                 exit(EXIT_FAILURE);
618         }
619
620         url = strdup(av[optind]);
621         if (!url) {
622                 exit(EXIT_FAILURE);
623         }
624
625         io.last_modified = 0;
626
627         if (calcoutfile && !outfile) {
628                 tls_parse_uri(url, &uri);
629                 outfile = pathlast(uri.path);
630                 /* outfile leaks memory here, so if this
631                  * were turned into a library function,
632                  * we'd need to track it
633                  */
634                 if (!outfile) {
635                         fprintf(stderr, "unable to determine outfile\n");
636                         exit(EXIT_FAILURE);
637                 }
638         }
639
640         if (ifnewer && outfile && !lmfile) {
641                 lmfile = outfile;
642         }
643
644         if (lmfile) {
645                 struct tm *mtime;
646                 time_t ts;
647
648                 ts = file_mtime(lmfile);
649
650                 if (ts == -1) {
651                         exit(EXIT_FAILURE);
652                 } else if (ts != 0) {
653                         io.last_modified = ts;
654                         mtime = gmtime(&ts);
655                         strftime(lmtime, sizeof lmtime, "%a, %d %b %Y %H:%M:%S GMT", mtime);
656                 } else {
657                         lmfile = 0;
658                 }
659         }
660
661         signal(SIGPIPE, SIG_IGN);
662
663         tls_buffer_init(&io.response, 0);
664         tls_buffer_init(&request, 128);
665
666         while (redirs++ <= redirlimit) {
667                 tls_free_uri(&uri);
668                 io.response.len = 0;
669                 request.len = 0;
670                 eoh = 0;
671
672                 tls_parse_uri(url, &uri);
673                 host = uri.host;
674                 port = atoi(uri.port);
675                 req_file = uri.path;
676
677                 /* construct request */
678                 if (head) {
679                         tls_buffer_append(&request, "HEAD ", 5);
680                 } else {
681                         tls_buffer_append(&request, "GET ", 4);
682                 }
683                 tls_buffer_append(&request, uri.path, strlen(uri.path));
684                 tls_buffer_append(&request, " HTTP/1.1\r\n", 11);
685
686                 append_header(&request, "Host", host);
687                 append_header(&request, "Connection", "close");
688                 if (lmfile) {
689                         append_header(&request, "If-Modified-Since", lmtime);
690                 }
691                 tls_buffer_append(&request, "\r\n", 2);
692
693                 if (!strcmp(uri.scheme, "https")) {
694                         use_tls = 1;
695
696                         clientssl = tls_create_context(TLS_CLIENT, TLS_V12);
697
698                         /* optionally, we can set a certificate validation
699                          * callback function if set_verify is not called, and
700                          * root ca is set, `tls_default_verify` will be used
701                          * (does exactly what `verify` does in this example)
702                          */
703                         if (verifypolicy == 2) {
704                                 char *cert_path = 0;
705                                 cert_path = getenv("ZPM_CERTFILE");
706                                 if (!cert_path) {
707                                         cert_path = "/var/lib/zpm/roots.pem";
708                                 }
709                                 rv = tls_load_root_file(clientssl, cert_path);
710                                 if (rv == -1) {
711                                         fprintf(stderr, "Error loading root certs\n");
712                                         return 1;
713                                 }
714                                 tls_set_verify(clientssl, verify_roots);
715                         } else if (verifypolicy == 1) {
716                                 tls_set_verify(clientssl, verify_first);
717                         } else {
718                                 tls_set_verify(clientssl, verify_trust);
719                         }
720
721                         if (!clientssl) {
722                                 fprintf(stderr, "Error initializing client context\n");
723                                 return -1;
724                         }
725                         tls_sni_set(clientssl, uri.host);
726                         clientssl->sync = 1;
727                         io.tls = clientssl;
728                         sockfd = open_tcp_connection(host, port);
729                         if (sockfd < 0) {
730                                 perror("can't open connection");
731                                 exit(EXIT_FAILURE);
732                         }
733                         tls_set_fd(clientssl, sockfd);
734                         if ((rv = tls_connect(clientssl)) != 1) {
735                                 fprintf(stderr, "Handshake Error %i\n", rv);
736                                 return 1;
737                         }
738                         ret = tls_write(clientssl, request.buffer, request.len);
739                 } else if (!strcmp(uri.scheme, "http")) {
740                         sockfd = open_tcp_connection(host, port);
741                         if (sockfd < 0) {
742                                 perror("can't open connection");
743                                 exit(EXIT_FAILURE);
744                         }
745                         ret = write(sockfd, request.buffer, request.len);
746                 } else if (!strcmp(uri.scheme, "file")) {
747                         sockfd = open(uri.path, O_RDONLY);
748                         fake_header(&io, sockfd);
749                         ret = 0;
750                 } else {
751                         fprintf(stderr, "scheme %s unknown\n", uri.scheme);
752                         exit(EXIT_FAILURE);
753                 }
754
755                 if (ret == -1) {
756                         fprintf(stderr, "unable to write http request: %s\n", strerror(errno));
757                         exit(EXIT_FAILURE);
758                 }
759
760                 io.socket = sockfd;
761
762                 do {
763                         if (io.response.len >= 4) {
764                                 eoh = strstr(io.response.buffer, "\r\n\r\n");
765                         }
766                         if (!eoh) {
767                                 ret = fill_buffer(&io);
768                                 if (ret <= 0) {
769                                         break;
770                                 }
771                         }
772                 } while (!eoh);
773
774                 if (!eoh) {
775                         /* never got (complet) header */
776                         fprintf(stderr, "incomplete response to %s\n", av[optind]);
777                         exit(EXIT_FAILURE);
778                 }
779
780                 header_len = (size_t)(eoh - io.response.buffer) + 4;
781                 parse_header(&io);
782
783                 switch (io.status_code) {
784                         case 304:
785                                 progressbar = 0;
786                                 break;
787                         case 301:
788                         case 302:
789                         case 303:
790                         case 307:
791                                 free(url);
792                                 url = strdup(io.redirect);
793                                 close(io.socket);
794                                 continue;
795                                 break;
796                 }
797
798                 if (printstatus) {
799                         printf("%d\n", io.status_code);
800                         break;
801                 }
802
803                 if (!raw) {
804                         tls_buffer_shift(&io.response, header_len);
805                 }
806                 if (head) {
807                         io.response.len -= 2;
808                 }
809
810                 if (progressbar) {
811                         if (io.content_length) {
812                                 fprintf(stderr, "(%lu) ", io.content_length);
813                         }
814                 }
815
816                 if (outfile) {
817                         out = open(outfile, O_WRONLY|O_CREAT, 0600);
818                         if (out == -1) {
819                                 perror("can't open output file:");
820                                 exit(EXIT_FAILURE);
821                         }
822                 }
823
824                 do {
825                         write(out, io.response.buffer, io.response.len);
826                         ret = io.response.len;
827                         io.response.len = 0;
828
829                         if (progressbar) {
830                                 if (io.content_length) {
831                                         pdots(50, '.', total, total+ret,
832                                                         io.content_length);
833                                 } else {
834                                         putc('\r', stderr);
835                                         fprintf(stderr, "%zu", total+ret);
836                                 }
837                                 total += ret;
838                         }
839                         if (head) {
840                                 break;
841                         }
842                         ret = fill_buffer(&io);
843                 } while (ret > 0);
844
845                 if (ret < 0) {
846                         fprintf(stderr, "%s read error %zd\n", uri.scheme, ret);
847                 }
848                 struct timespec ts[2];
849                 ts[0].tv_sec = 0; ts[0].tv_nsec = UTIME_OMIT;
850                 ts[1].tv_sec = io.last_modified;
851                 ts[1].tv_nsec = 0;
852
853                 futimens(out, ts);
854                 close(out);
855                 tls_buffer_free(&io.response);
856                 break;
857         }
858
859         if (use_tls) {
860                 tls_shutdown(clientssl);
861                 tls_free(clientssl);
862         }
863
864         close(sockfd);
865         if (progressbar && io.status_code == 200) {
866                 fprintf(stderr, "(%lu)", total);
867                 putc('\n',stderr);
868         }
869
870         return io.status_code < 400 ? 0 : EXIT_FAILURE;
871 }