]> pd.if.org Git - zpackage/blob - zpm-fetchurl.c
expand zpm man page
[zpackage] / zpm-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 int verify(struct TLSContext *context, struct TLSCertificate **chain, int len) {
34         int i, err;
35
36         if (chain) {
37                 for (i = 0; i < len; i++) {
38                         struct TLSCertificate *certificate = chain[i];
39                         // check validity date
40                         err = tls_certificate_is_valid(certificate);
41                         if (err)
42                                 return err;
43                         // check certificate in certificate->bytes of length certificate->len
44                         // the certificate is in ASN.1 DER format
45                 }
46         }
47         // check if chain is valid
48         err = tls_certificate_chain_is_valid(chain, len);
49         if (err) {
50                 return err;
51         }
52
53         if (len > 0 && context->sni) {
54                 err = tls_certificate_valid_subject(chain[0], context->sni);
55                 if (err) {
56                         return err;
57                 }
58         }
59
60         /* Perform certificate validation against ROOT CA */
61         err = tls_certificate_chain_is_valid_root(context, chain, len);
62         if (err) {
63                 return err;
64         }
65
66         return no_error;
67 }
68
69 struct io {
70         struct tls_buffer response;
71         struct TLSContext *tls;
72         int socket;
73         int status_code;
74         time_t last_modified;
75         time_t date;
76         size_t content_length;
77         char *redirect;
78 };
79
80 int month(char *m) {
81         char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
82                 "Aug", "Sep", "Oct", "Nov", "Dec" };
83         int i;
84
85         for (i=0; i < 12; i++) {
86                 if (!strncasecmp(m, months[i], 3)) {
87                         return i+1;
88                 }
89         }
90         return 0;
91 }
92
93 /* Wed, 06 Feb 2019 10:06:05 GMT */
94 time_t parse_date(char *d) {
95         //static const char format[] = "%a, %d %b %Y %H:%M:%S %Z"; // rfc 1123
96         struct tm tm = { 0 };
97         int rv;
98
99         //char *data = "Tue, 13 Dec 2011 16:08:21 GMT";
100         int h, m, s, dom, Y;
101         char M[4];
102         rv = sscanf(d, "%*[a-zA-Z,] %d %s %d %d:%d:%d", &dom, M, &Y, &h, &m, &s);
103         if (rv == 6) {
104                 tm.tm_year = Y - 1900;
105                 tm.tm_hour = h;
106                 tm.tm_min = m;
107                 tm.tm_sec = s;
108                 tm.tm_mon = month(M)-1;
109                 tm.tm_mday = dom;
110
111                 return mktime(&tm);
112         }
113         return -1;
114 }
115
116 char *find_header(struct io *io, char *header, size_t *len) {
117         char *eoh, *soh;
118         size_t hlen;
119
120         *len = 0;
121
122         hlen = strlen(header);
123         eoh = strstr(io->response.buffer, "\r\n\r\n");
124         if (!eoh) {
125                 return 0;
126         }
127         soh = io->response.buffer;
128         do {
129                 soh = strstr(soh, "\r\n");
130                 if (soh == eoh) {
131                         break;
132                 }
133                 soh += 2;
134                 if (!memcmp(soh, header, hlen)) {
135                         break;
136                 }
137         } while (soh < eoh);
138
139         if (soh >= eoh) {
140                 return 0;
141         }
142         eoh = strstr(soh, "\r\n");
143         soh += hlen;
144         while (soh < eoh && isspace(*soh)) {
145                 soh++;
146         }
147         *len = (eoh - soh);
148         return soh;
149 }
150
151 void parse_header(struct io *io) {
152         char *s = io->response.buffer;
153         int code = 0;
154         char *hval;
155         size_t hlen;
156
157         while (!isspace(*s)) {
158                 s++;
159         }
160         while (isspace(*s)) {
161                 s++;
162         }
163         code = strtol(s, 0, 10);
164         io->status_code = code;
165
166         hval = find_header(io, "Date:", &hlen);
167         if (hval) {
168                 hval[hlen] = 0;
169                 io->date = parse_date(hval);
170                 hval[hlen] = '\r';
171         }
172         hval = find_header(io, "Last-Modified:", &hlen);
173         if (hval) {
174                 hval[hlen] = 0;
175                 io->last_modified = parse_date(hval);
176                 hval[hlen] = '\r';
177         }
178
179         hval = find_header(io, "Content-Length:", &hlen);
180         if (hval) {
181                 hval[hlen] = 0;
182                 io->content_length = strtoul(hval, 0, 10);
183                 hval[hlen] = '\r';
184         }
185
186         switch (code) {
187                 case 301:
188                 case 302:
189                 case 303:
190                 case 307:
191                         hval = find_header(io, "Location:", &hlen);
192                         if (hval) {
193                                 io->redirect = strndup(hval, hlen);
194                         }
195                         break;
196                 default:
197                         break;
198         }
199
200 }
201
202 ssize_t fill_buffer(struct io *io) {
203         unsigned char buffer[4096];
204         ssize_t ret;
205
206         if (io->tls) {
207                 ret = tls_read(io->tls, buffer, sizeof buffer);
208         } else {
209                 ret = read(io->socket, buffer, sizeof buffer);
210         }
211
212         if (ret > 0) {
213                 tls_buffer_append(&io->response, buffer, ret);
214         }
215
216         return ret;
217 }
218
219 #if 0
220 char *nextline(struct io *io) {
221         char *eol = 0;;
222
223         eol = memchr(io->response.buffer, '\n', io.response.size);
224         while (eol == 0) {
225                 fill_buffer(io);
226                 eol = memchr(io->response.buffer, '\n', io.response.size);
227         }
228         if (eol) {
229
230 }
231 #endif
232
233 void append_header(struct tls_buffer *buf, char *header, char *val) {
234         tls_buffer_append(buf, header, strlen(header));
235         tls_buffer_append(buf, ": ", 2);
236         tls_buffer_append(buf, val, strlen(val));
237         tls_buffer_append(buf, "\r\n", 2);
238 }
239
240 static void pdots(int len, int ch, unsigned long was, unsigned long now,
241                 unsigned long total) {
242         was = len * was / total;
243         if (now > total) {
244                 now = total;
245         }
246         now = len * now / total;
247         while (was++ < now) {
248                 putc(ch,stderr);
249         }
250 }
251
252 int main(int ac, char *av[]) {
253         int sockfd, port = -1, rv;
254         ssize_t ret;
255         int option;
256 #if 0
257         char msg[] = "GET %s HTTP/1.1\r\nHost: %s:%i\r\nConnection: close\r\n\r\n";
258         char msg2[] = "GET %s HTTP/1.1\r\nHost: %s:%i\r\nLast-Modified: %s\r\nConnection: close\r\n\r\n";
259         char msg_buffer[1024];
260 #endif
261         char *req_file = 0;
262         char *host = 0;
263         struct tls_uri uri;
264         char *outfile = 0;
265         int raw = 0, head = 0;
266         int out = 1;
267         int use_tls = 0;
268         struct io io = { {0}, 0, -1, 0, 0, 0, 0, 0 };
269         struct TLSContext *clientssl = 0;
270         int failsilent = 0;
271         char *lmfile = 0;
272         int progressbar = 0;
273         struct tls_buffer request;
274         char lmtime[80];
275         char *eoh = 0;
276         size_t total = 0;
277         size_t header_len;
278         char *url = 0;
279         int redirs = 0, redirlimit = 50, printstatus = 0;
280
281         ltc_mp = tfm_desc;
282
283         while ((option = getopt(ac, av, "o:rIfz:#R:S")) != -1) {
284                 switch (option) {
285                         case 'o': outfile = optarg; break;
286                         case 'S': printstatus = 1; head = 1; break;
287                         case 'I': head = 1;
288                         case 'r': raw = 1; break;
289                         case 'f': failsilent = 1; break;
290                         case 'z': lmfile = optarg; break;
291                         case 'R': redirlimit = strtol(optarg, 0, 10); break;
292                         case '#': progressbar = 1; break;
293                         default:
294                                   exit(EXIT_FAILURE);
295                                   break;
296                 }
297         }
298
299         if (ac < optind) {
300                 fprintf(stderr, "Usage: %s uri\n", av[0]);
301                 exit(EXIT_FAILURE);
302         }
303
304         if (lmfile) {
305                 struct stat st;
306                 int rv;
307                 struct tm *mtime;
308                 time_t ts;
309
310                 rv = stat(lmfile, &st);
311                 if (rv == -1) {
312                         perror("stat failed:");
313                         exit(EXIT_FAILURE);
314                 }
315                 ts = st.st_mtime;
316                 mtime = gmtime(&ts);
317                 strftime(lmtime, sizeof lmtime, "%a, %d %b %Y %H:%M:%S GMT", mtime);
318         }
319
320         url = strdup(av[optind]);
321         if (!url) {
322                 exit(EXIT_FAILURE);
323         }
324
325         if (outfile) {
326                 out = open(outfile, O_WRONLY|O_CREAT, 0600);
327                 if (out == -1) {
328                         perror("can't open output file:");
329                         exit(EXIT_FAILURE);
330                 }
331         }
332
333         signal(SIGPIPE, SIG_IGN);
334
335         tls_buffer_init(&io.response, 0);
336         tls_buffer_init(&request, 128);
337
338         while (redirs++ <= redirlimit) {
339                 tls_free_uri(&uri);
340                 io.response.len = 0;
341                 request.len = 0;
342
343                 tls_parse_uri(url, &uri);
344                 host = uri.host;
345                 port = atoi(uri.port);
346                 req_file = uri.path;
347
348                 /* construct request */
349                 if (head) {
350                         tls_buffer_append(&request, "HEAD ", 5);
351                 } else {
352                         tls_buffer_append(&request, "GET ", 4);
353                 }
354                 tls_buffer_append(&request, uri.path, strlen(uri.path));
355                 tls_buffer_append(&request, " HTTP/1.1", 9);
356                 tls_buffer_append(&request, "\r\n", 2);
357
358                 append_header(&request, "Host", host);
359                 append_header(&request, "Connection", "close");
360                 if (lmfile) {
361                         append_header(&request, "If-Modified-Since", lmtime);
362                 }
363                 tls_buffer_append(&request, "\r\n", 2);
364                 //fprintf(stderr, "msg =\n%.*s", (int)request.len, request.buffer);
365
366                 if (!strcmp(uri.scheme, "https")) {
367                         use_tls = 1;
368
369                         clientssl = tls_create_context(TLS_CLIENT, TLS_V12);
370
371                         rv = tls_load_root_file(clientssl, "root.pem");
372                         if (rv == -1) {
373                                 fprintf(stderr, "Error loading root certs\n");
374                                 return 1;
375                         }
376
377                         /* optionally, we can set a certificate validation
378                          * callback function if set_verify is not called, and
379                          * root ca is set, `tls_default_verify` will be used
380                          * (does exactly what `verify` does in this example)
381                          */
382                         tls_set_verify(clientssl, verify);
383
384                         if (!clientssl) {
385                                 fprintf(stderr, "Error initializing client context\n");
386                                 return -1;
387                         }
388                         tls_sni_set(clientssl, uri.host);
389                         clientssl->sync = 1;
390                         io.tls = clientssl;
391                 }
392
393                 sockfd = open_tcp_connection(host, port);
394                 io.socket = sockfd;
395
396                 if (sockfd < 0) {
397                         exit(EXIT_FAILURE);
398                 }
399
400                 if (use_tls) {
401                         tls_set_fd(clientssl, sockfd);
402
403                         if ((rv = tls_connect(clientssl)) != 1) {
404                                 fprintf(stderr, "Handshake Error %i\n", rv);
405                                 return -5;
406                         }
407
408                         ret = tls_write(clientssl, request.buffer, request.len);
409                 } else {
410                         ret = write(io.socket, request.buffer, request.len);
411                 }
412
413                 if (ret < 0) {
414                         fprintf(stderr, "write error %zd\n", ret);
415                         return -6;
416                 }
417
418                 do {
419                         ret = fill_buffer(&io);
420                         if (ret < 0) {
421                                 break;
422                         }
423                         eoh = strstr(io.response.buffer, "\r\n\r\n");
424                         if (ret == 0) {
425                                 break;
426                         }
427                 } while (!eoh);
428
429                 if (!eoh) {
430                         /* never got (complet) header */
431                         fprintf(stderr, "incomplete response to %s\n", av[optind]);
432                         exit(EXIT_FAILURE);
433                 }
434
435                 header_len = (size_t)(eoh - io.response.buffer) + 4;
436                 parse_header(&io);
437
438                 switch (io.status_code) {
439                         case 301:
440                         case 302:
441                         case 303:
442                         case 307:
443                                 free(url);
444                                 url = strdup(io.redirect);
445                                 continue;
446                                 break;
447                 }
448
449                 if (printstatus) {
450                         printf("%d\n", io.status_code);
451                         break;
452                 }
453
454                 if (io.status_code != 200) {
455                         break;
456                 }
457
458                 if (!raw) {
459                         tls_buffer_shift(&io.response, header_len);
460                 }
461                 if (head) {
462                         io.response.len -= 2;
463                 }
464
465                 if (progressbar) {
466                         if (io.content_length) {
467                                 fprintf(stderr, "(%lu) ", io.content_length);
468                         }
469                 }
470
471                 do {
472                         write(out, io.response.buffer, io.response.len);
473                         ret = io.response.len;
474                         io.response.len = 0;
475
476                         if (progressbar) {
477                                 if (io.content_length) {
478                                         pdots(50, '.', total, total+ret,
479                                                         io.content_length);
480                                 } else {
481                                         int old = total / 1000000;
482                                         int new = (total+ret)/1000000;
483                                         while (old < new) {
484                                                 putc('.',stderr);
485                                         }
486                                 }
487                                 total += ret;
488                         }
489                         ret = fill_buffer(&io);
490                 } while (ret > 0);
491
492                 if (ret < 0) {
493                         fprintf(stderr, "%s read error %zd\n", uri.scheme, ret);
494                 }
495                 /* futimens(out, ...) */
496                 close(out);
497                 tls_buffer_free(&io.response);
498                 break;
499         }
500
501         if (use_tls) {
502                 tls_shutdown(clientssl);
503                 tls_free(clientssl);
504         }
505
506         close(sockfd);
507         if (progressbar && io.status_code == 200) {
508                 fprintf(stderr, "(%lu)", total);
509                 putc('\n',stderr);
510         }
511
512         return io.status_code == 200 ? 0 : EXIT_FAILURE;
513 }