Blob


1 /*
2 * Copyright (c) 2021 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 #define MIN(a, b) ((a) < (b) ? (a) : (b))
25 static struct timeval handshake_timeout = { 5, 0 };
27 static void proxy_tls_readcb(int, short, void *);
28 static void proxy_tls_writecb(int, short, void *);
29 static void proxy_read(struct bufferevent *, void *);
30 static void proxy_write(struct bufferevent *, void *);
31 static void proxy_error(struct bufferevent *, short, void *);
33 static void
34 proxy_tls_readcb(int fd, short event, void *d)
35 {
36 struct bufferevent *bufev = d;
37 struct client *c = bufev->cbarg;
38 char buf[IBUF_READ_SIZE];
39 int what = EVBUFFER_READ;
40 int howmuch = IBUF_READ_SIZE;
41 int res;
42 ssize_t ret;
43 size_t len;
45 if (event == EV_TIMEOUT) {
46 what |= EVBUFFER_TIMEOUT;
47 goto err;
48 }
50 if (bufev->wm_read.high != 0)
51 howmuch = MIN(sizeof(buf), bufev->wm_read.high);
53 switch (ret = tls_read(c->proxyctx, buf, howmuch)) {
54 case TLS_WANT_POLLIN:
55 case TLS_WANT_POLLOUT:
56 goto retry;
57 case -1:
58 what |= EVBUFFER_ERROR;
59 goto err;
60 }
61 len = ret;
63 if (len == 0) {
64 what |= EVBUFFER_EOF;
65 goto err;
66 }
68 res = evbuffer_add(bufev->input, buf, len);
69 if (res == -1) {
70 what |= EVBUFFER_ERROR;
71 goto err;
72 }
74 event_add(&bufev->ev_read, NULL);
76 len = EVBUFFER_LENGTH(bufev->input);
77 if (bufev->wm_read.low != 0 && len < bufev->wm_read.low)
78 return;
80 if (bufev->readcb != NULL)
81 (*bufev->readcb)(bufev, bufev->cbarg);
82 return;
84 retry:
85 event_add(&bufev->ev_read, NULL);
86 return;
88 err:
89 (*bufev->errorcb)(bufev, what, bufev->cbarg);
90 }
92 static void
93 proxy_tls_writecb(int fd, short event, void *d)
94 {
95 struct bufferevent *bufev = d;
96 struct client *c = bufev->cbarg;
97 ssize_t ret;
98 size_t len;
99 short what = EVBUFFER_WRITE;
101 if (event & EV_TIMEOUT) {
102 what |= EVBUFFER_TIMEOUT;
103 goto err;
106 if (EVBUFFER_LENGTH(bufev->output) != 0) {
107 ret = tls_write(c->proxyctx, EVBUFFER_DATA(bufev->output),
108 EVBUFFER_LENGTH(bufev->output));
109 switch (ret) {
110 case TLS_WANT_POLLIN:
111 case TLS_WANT_POLLOUT:
112 goto retry;
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 retry:
131 event_add(&bufev->ev_write, NULL);
132 return;
134 err:
135 (*bufev->errorcb)(bufev, what, bufev->cbarg);
138 static void
139 proxy_read(struct bufferevent *bev, void *d)
141 struct client *c = d;
142 struct evbuffer *src = EVBUFFER_INPUT(bev);
143 char *hdr;
144 size_t len;
145 int code;
147 /* intercept the header */
148 if (c->code == 0) {
149 hdr = evbuffer_readln(src, &len, EVBUFFER_EOL_CRLF_STRICT);
150 if (hdr == NULL) {
151 /* max reply + \r\n */
152 if (EVBUFFER_LENGTH(src) > 1029) {
153 log_warn(c, "upstream server is trying to "
154 "send a header that's too long.");
155 proxy_error(bev, EVBUFFER_READ, c);
158 /* wait a bit */
159 return;
162 if (len < 3 || len > 1029 ||
163 !isdigit(hdr[0]) ||
164 !isdigit(hdr[1]) ||
165 !isspace(hdr[2])) {
166 free(hdr);
167 log_warn(c, "upstream server is trying to send a "
168 "header that's too long.");
169 proxy_error(bev, EVBUFFER_READ, c);
170 return;
173 c->header = hdr;
174 code = (hdr[0] - '0') * 10 + (hdr[1] - '0');
176 if (code < 10 || code >= 70) {
177 log_warn(c, "upstream server is trying to send an "
178 "invalid reply code: %d", code);
179 proxy_error(bev, EVBUFFER_READ, c);
180 return;
183 start_reply(c, code, hdr + 3);
185 if (c->code < 20 || c->code > 29) {
186 proxy_error(bev, EVBUFFER_EOF, c);
187 return;
191 bufferevent_write_buffer(c->bev, src);
194 static void
195 proxy_write(struct bufferevent *bev, void *d)
197 struct evbuffer *dst = EVBUFFER_OUTPUT(bev);
199 /* request successfully sent */
200 if (EVBUFFER_LENGTH(dst) == 0)
201 bufferevent_disable(bev, EV_WRITE);
204 static void
205 proxy_error(struct bufferevent *bev, short error, void *d)
207 struct client *c = d;
209 /*
210 * If we're here it means that some kind of non-recoverable
211 * error appened.
212 */
214 bufferevent_free(bev);
215 c->proxybev = NULL;
217 tls_free(c->proxyctx);
218 c->proxyctx = NULL;
220 close(c->pfd);
221 c->pfd = -1;
223 /* EOF and no header */
224 if (c->code == 0) {
225 start_reply(c, PROXY_ERROR, "protocol error");
226 return;
229 c->type = REQUEST_DONE;
230 client_write(c->bev, c);
233 static void
234 proxy_enqueue_req(struct client *c)
236 struct proxy *p = c->proxy;
237 struct evbuffer *evb;
238 char iribuf[GEMINI_URL_LEN];
240 c->proxybev = bufferevent_new(c->pfd, proxy_read, proxy_write,
241 proxy_error, c);
242 if (c->proxybev == NULL)
243 fatal("can't allocate bufferevent: %s", strerror(errno));
245 if (!p->notls) {
246 event_set(&c->proxybev->ev_read, c->pfd, EV_READ,
247 proxy_tls_readcb, c->proxybev);
248 event_set(&c->proxybev->ev_write, c->pfd, EV_WRITE,
249 proxy_tls_writecb, c->proxybev);
251 #if HAVE_LIBEVENT2
252 evbuffer_unfreeze(c->proxybev->input, 0);
253 evbuffer_unfreeze(c->proxybev->output, 1);
254 #endif
257 serialize_iri(&c->iri, iribuf, sizeof(iribuf));
259 evb = EVBUFFER_OUTPUT(c->proxybev);
260 evbuffer_add_printf(evb, "%s\r\n", iribuf);
262 bufferevent_enable(c->proxybev, EV_READ|EV_WRITE);
265 static void
266 proxy_handshake(int fd, short event, void *d)
268 struct client *c = d;
270 if (event == EV_TIMEOUT) {
271 start_reply(c, PROXY_ERROR, "timeout");
272 return;
275 switch (tls_handshake(c->proxyctx)) {
276 case TLS_WANT_POLLIN:
277 event_set(&c->proxyev, fd, EV_READ, proxy_handshake, c);
278 event_add(&c->proxyev, &handshake_timeout);
279 return;
280 case TLS_WANT_POLLOUT:
281 event_set(&c->proxyev, fd, EV_WRITE, proxy_handshake, c);
282 event_add(&c->proxyev, &handshake_timeout);
283 return;
284 case -1:
285 log_warn(c, "handshake with proxy failed: %s",
286 tls_error(c->proxyctx));
287 start_reply(c, PROXY_ERROR, "handshake failed");
288 return;
291 proxy_enqueue_req(c);
294 static int
295 proxy_setup_tls(struct client *c)
297 struct proxy *p = c->proxy;
298 struct tls_config *conf = NULL;
300 if ((conf = tls_config_new()) == NULL)
301 return -1;
303 if (p->noverifyname)
304 tls_config_insecure_noverifyname(conf);
306 tls_config_insecure_noverifycert(conf);
307 tls_config_set_protocols(conf, p->protocols);
309 if (p->cert != NULL) {
310 int r;
312 r = tls_config_set_cert_mem(conf, p->cert, p->certlen);
313 if (r == -1)
314 goto err;
316 r = tls_config_set_key_mem(conf, p->key, p->keylen);
317 if (r == -1)
318 goto err;
321 if ((c->proxyctx = tls_client()) == NULL)
322 goto err;
324 if (tls_configure(c->proxyctx, conf) == -1)
325 goto err;
327 if (tls_connect_socket(c->proxyctx, c->pfd, p->host) == -1)
328 goto err;
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 c->type = REQUEST_PROXY;
352 if (p->notls) {
353 proxy_enqueue_req(c);
354 return 0;
357 return proxy_setup_tls(c);