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 }