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 }