https.c - frontends - front-ends for some sites (experiment)
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
       https.c (4485B)
       ---
            1 #include <sys/socket.h>
            2 #include <sys/types.h>
            3 
            4 #include <ctype.h>
            5 #include <errno.h>
            6 #include <netdb.h>
            7 #include <stdarg.h>
            8 #include <stdint.h>
            9 #include <stdio.h>
           10 #include <stdlib.h>
           11 #include <string.h>
           12 #include <time.h>
           13 #include <unistd.h>
           14 
           15 #include <tls.h>
           16 
           17 #define READ_BUF_SIZ        16384  /* read buffer in bytes */
           18 #define MAX_RESPONSETIMEOUT 10     /* timeout in seconds */
           19 #define MAX_RESPONSESIZ     4194304 /* max download size in bytes */
           20 
           21 static void
           22 die(const char *fmt, ...)
           23 {
           24         va_list ap;
           25 
           26         va_start(ap, fmt);
           27         vfprintf(stderr, fmt, ap);
           28         va_end(ap);
           29 
           30         exit(1);
           31 }
           32 
           33 /* TODO: use die or rename die to fatal */
           34 void
           35 fatal(const char *s)
           36 {
           37         fputs(s, stderr);
           38         exit(1);
           39 }
           40 
           41 char *
           42 readtls(struct tls *t)
           43 {
           44         char *buf;
           45         size_t len = 0, size = 0;
           46         ssize_t r;
           47 
           48         /* always allocate an empty buffer */
           49         if (!(buf = calloc(1, size + 1)))
           50                 die("calloc: %s\n", strerror(errno));
           51 
           52         while (1) {
           53                 if (len + READ_BUF_SIZ + 1 > size) {
           54                         /* allocate size: common case is small textfiles */
           55                         size += READ_BUF_SIZ;
           56                         if (!(buf = realloc(buf, size + 1)))
           57                                 die("realloc: %s\n", strerror(errno));
           58                 }
           59                 r = tls_read(t, &buf[len], READ_BUF_SIZ);
           60                 if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT) {
           61                         continue;
           62                 } else if (r <= 0) {
           63                         break;
           64                 }
           65                 len += r;
           66                 buf[len] = '\0';
           67                 if (len > MAX_RESPONSESIZ)
           68                         die("response is too big: > %zu bytes\n", MAX_RESPONSESIZ);
           69         }
           70         if (r == -1)
           71                 die("tls_read: %s\n", tls_error(t));
           72 
           73         return buf;
           74 }
           75 
           76 int
           77 edial(const char *host, const char *port)
           78 {
           79         struct addrinfo hints, *res, *res0;
           80         int error, save_errno, s;
           81         const char *cause = NULL;
           82         struct timeval timeout;
           83 
           84         memset(&hints, 0, sizeof(hints));
           85         hints.ai_family = AF_UNSPEC;
           86         hints.ai_socktype = SOCK_STREAM;
           87         hints.ai_flags = AI_NUMERICSERV; /* numeric port only */
           88         if ((error = getaddrinfo(host, port, &hints, &res0)))
           89                 die("%s: %s: %s:%s\n", __func__, gai_strerror(error), host, port);
           90         s = -1;
           91         for (res = res0; res; res = res->ai_next) {
           92                 s = socket(res->ai_family, res->ai_socktype,
           93                            res->ai_protocol);
           94                 if (s == -1) {
           95                         cause = "socket";
           96                         continue;
           97                 }
           98 
           99                 timeout.tv_sec = MAX_RESPONSETIMEOUT;
          100                 timeout.tv_usec = 0;
          101                 if (setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) == -1)
          102                         die("%s: setsockopt: %s\n", __func__, strerror(errno));
          103 
          104                 timeout.tv_sec = MAX_RESPONSETIMEOUT;
          105                 timeout.tv_usec = 0;
          106                 if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1)
          107                         die("%s: setsockopt: %s\n", __func__, strerror(errno));
          108 
          109                 if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
          110                         cause = "connect";
          111                         save_errno = errno;
          112                         close(s);
          113                         errno = save_errno;
          114                         s = -1;
          115                         continue;
          116                 }
          117                 break;
          118         }
          119         if (s == -1)
          120                 die("%s: %s: %s:%s\n", __func__, cause, host, port);
          121         freeaddrinfo(res0);
          122 
          123         return s;
          124 }
          125 
          126 char *
          127 request(const char *host, const char *path, const char *headers)
          128 {
          129         struct tls *t;
          130         char request[4096];
          131         char *data;
          132         size_t len;
          133         ssize_t w;
          134         int fd;
          135 
          136         /* use HTTP/1.0, don't use HTTP/1.1 using ugly chunked-encoding */
          137         snprintf(request, sizeof(request),
          138                 "GET %s HTTP/1.0\r\n"
          139                 "Host: %s\r\n"
          140                 "Accept-Language: en-US,en;q=0.5\r\n"
          141                 "Connection: close\r\n"
          142                 "%s"
          143                 "\r\n", path, host, headers);
          144 
          145         if (tls_init() == -1)
          146                 die("tls_init\n");
          147 
          148         if (!(t = tls_client()))
          149                 die("tls_client: %s\n", tls_error(t));
          150 
          151         fd = edial(host, "443");
          152 
          153         if (tls_connect_socket(t, fd, host) == -1)
          154                 die("tls_connect: %s\n", tls_error(t));
          155 
          156         data = request;
          157         len = strlen(data);
          158         while (len > 0) {
          159                 w = tls_write(t, data, len);
          160                 if (w == TLS_WANT_POLLIN || w == TLS_WANT_POLLOUT)
          161                         continue;
          162                 else if (w == -1)
          163                         die("tls_write: %s\n", tls_error(t));
          164                 data += w;
          165                 len -= w;
          166         }
          167 
          168         data = readtls(t);
          169 
          170         tls_close(t);
          171         tls_free(t);
          172 
          173         return data;
          174 }
          175 
          176 /* DEBUG */
          177 char *
          178 readfile(const char *file)
          179 {
          180         FILE *fp;
          181         char *buf;
          182         size_t n, len = 0, size = 0;
          183 
          184         fp = fopen(file, "rb");
          185         if (!fp)
          186                 die("fopen");
          187         buf = calloc(1, size + 1); /* always allocate an empty buffer */
          188         if (!buf)
          189                 die("calloc");
          190         while (!feof(fp)) {
          191                 if (len + READ_BUF_SIZ + 1 > size) {
          192                         /* allocate size: common case is small textfiles */
          193                         size += READ_BUF_SIZ;
          194                         if (!(buf = realloc(buf, size + 1))) {
          195                                 fprintf(stderr, "realloc: %s\n", strerror(errno));
          196                                 exit(1);
          197                         }
          198                 }
          199                 if (!(n = fread(&buf[len], 1, READ_BUF_SIZ, fp)))
          200                         break;
          201                 len += n;
          202                 buf[len] = '\0';
          203                 if (n != READ_BUF_SIZ)
          204                         break;
          205         }
          206         if (ferror(fp)) {
          207                 fprintf(stderr, "fread: file: %s: %s\n", file, strerror(errno));
          208                 exit(1);
          209         }
          210         fclose(fp);
          211 
          212         return buf;
          213 }