1 #define _POSIX_C_SOURCE 200809L
5 #include <sys/socket.h>
6 #include <netinet/in.h>
28 int tls_parse_uri(char *, struct tls_uri *);
29 void tls_free_uri(struct tls_uri *);
31 int open_tcp_connection(char *host, int port);
33 int verify(struct TLSContext *context, struct TLSCertificate **chain, int len) {
37 for (i = 0; i < len; i++) {
38 struct TLSCertificate *certificate = chain[i];
39 // check validity date
40 err = tls_certificate_is_valid(certificate);
43 // check certificate in certificate->bytes of length certificate->len
44 // the certificate is in ASN.1 DER format
47 // check if chain is valid
48 err = tls_certificate_chain_is_valid(chain, len);
53 if (len > 0 && context->sni) {
54 err = tls_certificate_valid_subject(chain[0], context->sni);
60 /* Perform certificate validation against ROOT CA */
61 err = tls_certificate_chain_is_valid_root(context, chain, len);
70 struct tls_buffer response;
71 struct TLSContext *tls;
76 size_t content_length;
81 char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
82 "Aug", "Sep", "Oct", "Nov", "Dec" };
85 for (i=0; i < 12; i++) {
86 if (!strncasecmp(m, months[i], 3)) {
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
99 //char *data = "Tue, 13 Dec 2011 16:08:21 GMT";
102 rv = sscanf(d, "%*[a-zA-Z,] %d %s %d %d:%d:%d", &dom, M, &Y, &h, &m, &s);
104 tm.tm_year = Y - 1900;
108 tm.tm_mon = month(M)-1;
116 char *find_header(struct io *io, char *header, size_t *len) {
122 hlen = strlen(header);
123 eoh = strstr(io->response.buffer, "\r\n\r\n");
127 soh = io->response.buffer;
129 soh = strstr(soh, "\r\n");
134 if (!memcmp(soh, header, hlen)) {
142 eoh = strstr(soh, "\r\n");
144 while (soh < eoh && isspace(*soh)) {
151 void parse_header(struct io *io) {
152 char *s = io->response.buffer;
157 while (!isspace(*s)) {
160 while (isspace(*s)) {
163 code = strtol(s, 0, 10);
164 io->status_code = code;
166 hval = find_header(io, "Date:", &hlen);
169 io->date = parse_date(hval);
172 hval = find_header(io, "Last-Modified:", &hlen);
175 io->last_modified = parse_date(hval);
179 hval = find_header(io, "Content-Length:", &hlen);
182 io->content_length = strtoul(hval, 0, 10);
191 hval = find_header(io, "Location:", &hlen);
193 io->redirect = strndup(hval, hlen);
202 ssize_t fill_buffer(struct io *io) {
203 unsigned char buffer[4096];
207 ret = tls_read(io->tls, buffer, sizeof buffer);
209 ret = read(io->socket, buffer, sizeof buffer);
213 tls_buffer_append(&io->response, buffer, ret);
220 char *nextline(struct io *io) {
223 eol = memchr(io->response.buffer, '\n', io.response.size);
226 eol = memchr(io->response.buffer, '\n', io.response.size);
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);
240 static void pdots(int len, int ch, unsigned long was, unsigned long now,
241 unsigned long total) {
242 was = len * was / total;
246 now = len * now / total;
247 while (was++ < now) {
252 int main(int ac, char *av[]) {
253 int sockfd, port = -1, rv;
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];
265 int raw = 0, head = 0;
268 struct io io = { {0}, 0, -1, 0, 0, 0, 0, 0 };
269 struct TLSContext *clientssl = 0;
273 struct tls_buffer request;
279 int redirs = 0, redirlimit = 50, printstatus = 0;
283 while ((option = getopt(ac, av, "o:rIfz:#R:S")) != -1) {
285 case 'o': outfile = optarg; break;
286 case 'S': printstatus = 1; head = 1; break;
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;
300 fprintf(stderr, "Usage: %s uri\n", av[0]);
310 rv = stat(lmfile, &st);
312 perror("stat failed:");
317 strftime(lmtime, sizeof lmtime, "%a, %d %b %Y %H:%M:%S GMT", mtime);
320 url = strdup(av[optind]);
326 out = open(outfile, O_WRONLY|O_CREAT, 0600);
328 perror("can't open output file:");
333 signal(SIGPIPE, SIG_IGN);
335 tls_buffer_init(&io.response, 0);
336 tls_buffer_init(&request, 128);
338 while (redirs++ <= redirlimit) {
343 tls_parse_uri(url, &uri);
345 port = atoi(uri.port);
348 /* construct request */
350 tls_buffer_append(&request, "HEAD ", 5);
352 tls_buffer_append(&request, "GET ", 4);
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);
358 append_header(&request, "Host", host);
359 append_header(&request, "Connection", "close");
361 append_header(&request, "If-Modified-Since", lmtime);
363 tls_buffer_append(&request, "\r\n", 2);
364 //fprintf(stderr, "msg =\n%.*s", (int)request.len, request.buffer);
366 if (!strcmp(uri.scheme, "https")) {
369 clientssl = tls_create_context(TLS_CLIENT, TLS_V12);
371 rv = tls_load_root_file(clientssl, "root.pem");
373 fprintf(stderr, "Error loading root certs\n");
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)
382 tls_set_verify(clientssl, verify);
385 fprintf(stderr, "Error initializing client context\n");
388 tls_sni_set(clientssl, uri.host);
393 sockfd = open_tcp_connection(host, port);
401 tls_set_fd(clientssl, sockfd);
403 if ((rv = tls_connect(clientssl)) != 1) {
404 fprintf(stderr, "Handshake Error %i\n", rv);
408 ret = tls_write(clientssl, request.buffer, request.len);
410 ret = write(io.socket, request.buffer, request.len);
414 fprintf(stderr, "write error %zd\n", ret);
419 ret = fill_buffer(&io);
423 eoh = strstr(io.response.buffer, "\r\n\r\n");
430 /* never got (complet) header */
431 fprintf(stderr, "incomplete response to %s\n", av[optind]);
435 header_len = (size_t)(eoh - io.response.buffer) + 4;
438 switch (io.status_code) {
444 url = strdup(io.redirect);
450 printf("%d\n", io.status_code);
454 if (io.status_code != 200) {
459 tls_buffer_shift(&io.response, header_len);
462 io.response.len -= 2;
466 if (io.content_length) {
467 fprintf(stderr, "(%lu) ", io.content_length);
472 write(out, io.response.buffer, io.response.len);
473 ret = io.response.len;
477 if (io.content_length) {
478 pdots(50, '.', total, total+ret,
481 int old = total / 1000000;
482 int new = (total+ret)/1000000;
489 ret = fill_buffer(&io);
493 fprintf(stderr, "%s read error %zd\n", uri.scheme, ret);
495 /* futimens(out, ...) */
497 tls_buffer_free(&io.response);
502 tls_shutdown(clientssl);
507 if (progressbar && io.status_code == 200) {
508 fprintf(stderr, "(%lu)", total);
512 return io.status_code == 200 ? 0 : EXIT_FAILURE;