https.c - frontends - front-ends for some sites (experiment) | |
Log | |
Files | |
Refs | |
README | |
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_RE… | |
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), ho… | |
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, siz… | |
102 die("%s: setsockopt: %s\n", __func__, strerror(e… | |
103 | |
104 timeout.tv_sec = MAX_RESPONSETIMEOUT; | |
105 timeout.tv_usec = 0; | |
106 if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, siz… | |
107 die("%s: setsockopt: %s\n", __func__, strerror(e… | |
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", strerro… | |
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(… | |
208 exit(1); | |
209 } | |
210 fclose(fp); | |
211 | |
212 return buf; | |
213 } |