Blob


1 /*
2 * Copyright (c) 2021, 2022, 2023 Omar Polo <op@omarpolo.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include "gmid.h"
19 #include <ctype.h>
20 #include <errno.h>
21 #include <string.h>
23 #include "log.h"
25 #define MINIMUM(a, b) ((a) < (b) ? (a) : (b))
27 static const struct timeval handshake_timeout = { 5, 0 };
29 static void proxy_tls_readcb(int, short, void *);
30 static void proxy_tls_writecb(int, short, void *);
31 static void proxy_read(struct bufferevent *, void *);
32 static void proxy_write(struct bufferevent *, void *);
33 static void proxy_error(struct bufferevent *, short, void *);
35 static void
36 proxy_tls_readcb(int fd, short event, void *d)
37 {
38 struct bufferevent *bufev = d;
39 struct client *c = bufev->cbarg;
40 char buf[IBUF_READ_SIZE];
41 int what = EVBUFFER_READ;
42 int howmuch = IBUF_READ_SIZE;
43 int res;
44 ssize_t ret;
45 size_t len;
47 if (event == EV_TIMEOUT) {
48 what |= EVBUFFER_TIMEOUT;
49 goto err;
50 }
52 if (bufev->wm_read.high != 0)
53 howmuch = MINIMUM(sizeof(buf), bufev->wm_read.high);
55 switch (ret = tls_read(c->proxyctx, buf, howmuch)) {
56 case TLS_WANT_POLLIN:
57 case TLS_WANT_POLLOUT:
58 event_add(&bufev->ev_read, NULL);
59 return;
60 case -1:
61 what |= EVBUFFER_ERROR;
62 goto err;
63 }
64 len = ret;
66 if (len == 0) {
67 what |= EVBUFFER_EOF;
68 goto err;
69 }
71 res = evbuffer_add(bufev->input, buf, len);
72 if (res == -1) {
73 what |= EVBUFFER_ERROR;
74 goto err;
75 }
77 event_add(&bufev->ev_read, NULL);
79 len = EVBUFFER_LENGTH(bufev->input);
80 if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
81 return;
83 if (bufev->readcb != NULL)
84 (*bufev->readcb)(bufev, bufev->cbarg);
85 return;
87 err:
88 (*bufev->errorcb)(bufev, what, bufev->cbarg);
89 }
91 static void
92 proxy_tls_writecb(int fd, short event, void *d)
93 {
94 struct bufferevent *bufev = d;
95 struct client *c = bufev->cbarg;
96 ssize_t ret;
97 size_t len;
98 short what = EVBUFFER_WRITE;
100 if (event & EV_TIMEOUT) {
101 what |= EVBUFFER_TIMEOUT;
102 goto err;
105 if (EVBUFFER_LENGTH(bufev->output) != 0) {
106 ret = tls_write(c->proxyctx, EVBUFFER_DATA(bufev->output),
107 EVBUFFER_LENGTH(bufev->output));
108 switch (ret) {
109 case TLS_WANT_POLLIN:
110 case TLS_WANT_POLLOUT:
111 event_add(&bufev->ev_write, NULL);
112 return;
113 case -1:
114 what |= EVBUFFER_ERROR;
115 goto err;
117 len = ret;
119 evbuffer_drain(bufev->output, len);
122 if (EVBUFFER_LENGTH(bufev->output) != 0)
123 event_add(&bufev->ev_write, NULL);
125 if (bufev->writecb != NULL &&
126 EVBUFFER_LENGTH(bufev->output) <= bufev->wm_write.low)
127 (*bufev->writecb)(bufev, bufev->cbarg);
128 return;
130 err:
131 (*bufev->errorcb)(bufev, what, bufev->cbarg);
134 static void
135 proxy_read(struct bufferevent *bev, void *d)
137 struct client *c = d;
138 struct evbuffer *src = EVBUFFER_INPUT(bev);
139 char *hdr;
140 size_t len;
141 int code;
143 /* intercept the header */
144 if (c->code == 0) {
145 hdr = evbuffer_readln(src, &len, EVBUFFER_EOL_CRLF_STRICT);
146 if (hdr == NULL) {
147 /* max reply + \r\n */
148 if (EVBUFFER_LENGTH(src) > 1029) {
149 log_warnx("upstream server is trying to "
150 "send a header that's too long.");
151 proxy_error(bev, EVBUFFER_READ, c);
154 /* wait a bit */
155 return;
158 if (len < 3 || len > 1029 ||
159 !isdigit((unsigned char)hdr[0]) ||
160 !isdigit((unsigned char)hdr[1]) ||
161 !isspace((unsigned char)hdr[2])) {
162 free(hdr);
163 log_warnx("upstream server is trying to send a "
164 "header that's too long.");
165 proxy_error(bev, EVBUFFER_READ, c);
166 return;
169 c->header = hdr;
170 code = (hdr[0] - '0') * 10 + (hdr[1] - '0');
172 if (code < 10 || code >= 70) {
173 log_warnx("upstream server is trying to send an "
174 "invalid reply code: %d", code);
175 proxy_error(bev, EVBUFFER_READ, c);
176 return;
179 if (start_reply(c, code, hdr + 3) == -1 ||
180 c->code < 20 || c->code > 29) {
181 proxy_error(bev, EVBUFFER_EOF, c);
182 return;
186 bufferevent_write_buffer(c->bev, src);
189 static void
190 proxy_write(struct bufferevent *bev, void *d)
192 struct evbuffer *dst = EVBUFFER_OUTPUT(bev);
194 /* request successfully sent */
195 if (EVBUFFER_LENGTH(dst) == 0)
196 bufferevent_disable(bev, EV_WRITE);
199 static void
200 proxy_error(struct bufferevent *bev, short error, void *d)
202 struct client *c = d;
204 /*
205 * If we're here it means that some kind of non-recoverable
206 * error happened.
207 */
209 bufferevent_free(bev);
210 c->proxybev = NULL;
212 tls_free(c->proxyctx);
213 c->proxyctx = NULL;
215 close(c->pfd);
216 c->pfd = -1;
218 /* EOF and no header */
219 if (c->code == 0) {
220 start_reply(c, PROXY_ERROR, "protocol error");
221 return;
224 c->type = REQUEST_DONE;
225 client_write(c->bev, c);
228 static void
229 proxy_enqueue_req(struct client *c)
231 struct proxy *p = c->proxy;
232 struct evbuffer *evb;
233 char iribuf[GEMINI_URL_LEN];
235 c->proxybev = bufferevent_new(c->pfd, proxy_read, proxy_write,
236 proxy_error, c);
237 if (c->proxybev == NULL)
238 fatal("can't allocate bufferevent");
240 if (!p->notls) {
241 event_set(&c->proxybev->ev_read, c->pfd, EV_READ,
242 proxy_tls_readcb, c->proxybev);
243 event_set(&c->proxybev->ev_write, c->pfd, EV_WRITE,
244 proxy_tls_writecb, c->proxybev);
246 #if HAVE_LIBEVENT2
247 evbuffer_unfreeze(c->proxybev->input, 0);
248 evbuffer_unfreeze(c->proxybev->output, 1);
249 #endif
252 serialize_iri(&c->iri, iribuf, sizeof(iribuf));
254 evb = EVBUFFER_OUTPUT(c->proxybev);
255 evbuffer_add_printf(evb, "%s\r\n", iribuf);
257 bufferevent_enable(c->proxybev, EV_READ|EV_WRITE);
260 static void
261 proxy_handshake(int fd, short event, void *d)
263 struct client *c = d;
265 if (event == EV_TIMEOUT) {
266 start_reply(c, PROXY_ERROR, "timeout");
267 return;
270 switch (tls_handshake(c->proxyctx)) {
271 case TLS_WANT_POLLIN:
272 event_set(&c->proxyev, fd, EV_READ, proxy_handshake, c);
273 event_add(&c->proxyev, &handshake_timeout);
274 return;
275 case TLS_WANT_POLLOUT:
276 event_set(&c->proxyev, fd, EV_WRITE, proxy_handshake, c);
277 event_add(&c->proxyev, &handshake_timeout);
278 return;
279 case -1:
280 log_warnx("handshake with proxy failed: %s",
281 tls_error(c->proxyctx));
282 start_reply(c, PROXY_ERROR, "handshake failed");
283 return;
286 c->proxyevset = 0;
287 proxy_enqueue_req(c);
290 static int
291 proxy_setup_tls(struct client *c)
293 struct proxy *p = c->proxy;
294 struct tls_config *conf = NULL;
295 const char *hn;
297 if ((conf = tls_config_new()) == NULL)
298 return -1;
300 if (p->noverifyname)
301 tls_config_insecure_noverifyname(conf);
303 tls_config_insecure_noverifycert(conf);
304 tls_config_set_protocols(conf, p->protocols);
306 if (p->cert != NULL) {
307 int r;
309 r = tls_config_set_cert_mem(conf, p->cert, p->certlen);
310 if (r == -1)
311 goto err;
313 r = tls_config_set_key_mem(conf, p->key, p->keylen);
314 if (r == -1)
315 goto err;
318 if ((c->proxyctx = tls_client()) == NULL)
319 goto err;
321 if (tls_configure(c->proxyctx, conf) == -1)
322 goto err;
324 if (*(hn = p->sni) == '\0')
325 hn = p->host;
326 if (tls_connect_socket(c->proxyctx, c->pfd, hn) == -1)
327 goto err;
329 c->proxyevset = 1;
330 event_set(&c->proxyev, c->pfd, EV_READ|EV_WRITE, proxy_handshake, c);
331 event_add(&c->proxyev, &handshake_timeout);
333 tls_config_free(conf);
334 return 0;
336 err:
337 tls_config_free(conf);
338 if (c->proxyctx != NULL) {
339 tls_free(c->proxyctx);
340 c->proxyctx = NULL;
342 return -1;
345 int
346 proxy_init(struct client *c)
348 struct proxy *p = c->proxy;
350 if (!p->notls && proxy_setup_tls(c) == -1)
351 return -1;
352 else if (p->notls)
353 proxy_enqueue_req(c);
355 c->type = REQUEST_PROXY;
357 return 0;