1 /* $OpenBSD: relay_udp.c,v 1.51 2024/05/18 06:34:46 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/time.h>
22 #include <sys/socket.h>
23 #include <sys/tree.h>
24
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27
28 #include <signal.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <event.h>
36 #include <imsg.h>
37
38 #include "relayd.h"
39
40 extern volatile sig_atomic_t relay_sessions;
41 extern objid_t relay_conid;
42
43 static struct relayd *env = NULL;
44 struct shuffle relay_shuffle;
45
46 int relay_udp_socket(struct sockaddr_storage *, in_port_t,
47 struct protocol *);
48 void relay_udp_timeout(int, short, void *);
49
50 void relay_dns_log(struct rsession *, u_int8_t *, size_t);
51 void *relay_dns_validate(struct rsession *,
52 struct relay *, struct sockaddr_storage *,
53 u_int8_t *, size_t);
54 int relay_dns_request(struct rsession *);
55 void relay_udp_response(int, short, void *);
56 void relay_dns_result(struct rsession *, u_int8_t *, size_t);
57 int relay_dns_cmp(struct rsession *, struct rsession *);
58
59 void
relay_udp_privinit(struct relay * rlay)60 relay_udp_privinit(struct relay *rlay)
61 {
62 if (rlay->rl_conf.flags & F_TLS)
63 fatalx("tls over udp is not supported");
64 rlay->rl_conf.flags |= F_UDP;
65 }
66
67 void
relay_udp_init(struct relayd * x_env,struct relay * rlay)68 relay_udp_init(struct relayd *x_env, struct relay *rlay)
69 {
70 struct protocol *proto = rlay->rl_proto;
71
72 if (env == NULL)
73 env = x_env;
74
75 switch (proto->type) {
76 case RELAY_PROTO_DNS:
77 proto->validate = relay_dns_validate;
78 proto->request = relay_dns_request;
79 proto->cmp = relay_dns_cmp;
80 shuffle_init(&relay_shuffle);
81 break;
82 default:
83 fatalx("unsupported udp protocol");
84 break;
85 }
86 }
87
88 int
relay_udp_bind(struct sockaddr_storage * ss,in_port_t port,struct protocol * proto)89 relay_udp_bind(struct sockaddr_storage *ss, in_port_t port,
90 struct protocol *proto)
91 {
92 int s;
93
94 if ((s = relay_udp_socket(ss, port, proto)) == -1)
95 return (-1);
96
97 if (bind(s, (struct sockaddr *)ss, ss->ss_len) == -1)
98 goto bad;
99
100 return (s);
101
102 bad:
103 close(s);
104 return (-1);
105 }
106
107 int
relay_udp_socket(struct sockaddr_storage * ss,in_port_t port,struct protocol * proto)108 relay_udp_socket(struct sockaddr_storage *ss, in_port_t port,
109 struct protocol *proto)
110 {
111 int s = -1, val;
112
113 if (relay_socket_af(ss, port) == -1)
114 goto bad;
115
116 if ((s = socket(ss->ss_family, SOCK_DGRAM | SOCK_NONBLOCK,
117 IPPROTO_UDP)) == -1)
118 goto bad;
119
120 /*
121 * Socket options
122 */
123 if (proto->tcpflags & TCPFLAG_BUFSIZ) {
124 val = proto->tcpbufsiz;
125 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
126 &val, sizeof(val)) == -1)
127 goto bad;
128 val = proto->tcpbufsiz;
129 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
130 &val, sizeof(val)) == -1)
131 goto bad;
132 }
133
134 /*
135 * IP options
136 */
137 if (proto->tcpflags & TCPFLAG_IPTTL) {
138 val = (int)proto->tcpipttl;
139 switch (ss->ss_family) {
140 case AF_INET:
141 if (setsockopt(s, IPPROTO_IP, IP_TTL,
142 &val, sizeof(val)) == -1)
143 goto bad;
144 break;
145 case AF_INET6:
146 if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
147 &val, sizeof(val)) == -1)
148 goto bad;
149 break;
150 }
151 }
152 if (proto->tcpflags & TCPFLAG_IPMINTTL) {
153 val = (int)proto->tcpipminttl;
154 switch (ss->ss_family) {
155 case AF_INET:
156 if (setsockopt(s, IPPROTO_IP, IP_MINTTL,
157 &val, sizeof(val)) == -1)
158 goto bad;
159 break;
160 case AF_INET6:
161 if (setsockopt(s, IPPROTO_IPV6, IPV6_MINHOPCOUNT,
162 &val, sizeof(val)) == -1)
163 goto bad;
164 break;
165 }
166 }
167
168 return (s);
169
170 bad:
171 if (s != -1)
172 close(s);
173 return (-1);
174 }
175
176 void
relay_udp_response(int fd,short sig,void * arg)177 relay_udp_response(int fd, short sig, void *arg)
178 {
179 struct rsession *con = arg;
180 struct relay *rlay = con->se_relay;
181 struct protocol *proto = rlay->rl_proto;
182 void *priv = NULL;
183 struct sockaddr_storage ss;
184 u_int8_t buf[IBUF_READ_SIZE];
185 ssize_t len;
186 socklen_t slen;
187
188 if (sig == EV_TIMEOUT) {
189 relay_udp_timeout(fd, sig, arg);
190 return;
191 }
192
193 if (rlay->rl_conf.flags & F_DISABLE)
194 return;
195
196 slen = sizeof(ss);
197 if ((len = recvfrom(fd, buf, sizeof(buf), 0,
198 (struct sockaddr*)&ss, &slen)) < 1)
199 return;
200
201 /* Parse and validate the packet header */
202 if (proto->validate != NULL &&
203 (priv = (*proto->validate)(con, rlay, &ss, buf, len)) == NULL)
204 return;
205
206 relay_close(con, "unknown response", 1);
207 free(priv);
208 }
209
210 void
relay_udp_server(int fd,short sig,void * arg)211 relay_udp_server(int fd, short sig, void *arg)
212 {
213 struct privsep *ps = env->sc_ps;
214 struct relay *rlay = arg;
215 struct protocol *proto = rlay->rl_proto;
216 struct rsession *con = NULL;
217 struct ctl_natlook *cnl = NULL;
218 socklen_t slen;
219 struct timeval tv;
220 struct sockaddr_storage ss;
221 u_int8_t buf[IBUF_READ_SIZE];
222 void *priv = NULL;
223 ssize_t len;
224
225 event_add(&rlay->rl_ev, NULL);
226
227 if (rlay->rl_conf.flags & F_DISABLE)
228 return;
229
230 slen = sizeof(ss);
231 if ((len = recvfrom(fd, buf, sizeof(buf), 0,
232 (struct sockaddr*)&ss, &slen)) < 1)
233 return;
234
235 if (proto->validate != NULL &&
236 (priv = (*proto->validate)(NULL, rlay, &ss, buf, len)) == NULL)
237 return;
238
239 if ((con = calloc(1, sizeof(*con))) == NULL) {
240 free(priv);
241 return;
242 }
243
244 /*
245 * Replace the DNS request Id with a random Id.
246 */
247 con->se_priv = priv;
248 con->se_in.s = -1;
249 con->se_out.s = -1;
250 con->se_in.dst = &con->se_out;
251 con->se_out.dst = &con->se_in;
252 con->se_in.con = con;
253 con->se_out.con = con;
254 con->se_relay = rlay;
255 con->se_id = ++relay_conid;
256 con->se_in.dir = RELAY_DIR_REQUEST;
257 con->se_out.dir = RELAY_DIR_RESPONSE;
258 con->se_retry = rlay->rl_conf.dstretry;
259 con->se_out.port = rlay->rl_conf.dstport;
260 switch (ss.ss_family) {
261 case AF_INET:
262 con->se_in.port = ((struct sockaddr_in *)&ss)->sin_port;
263 break;
264 case AF_INET6:
265 con->se_in.port = ((struct sockaddr_in6 *)&ss)->sin6_port;
266 break;
267 }
268 bcopy(&ss, &con->se_in.ss, sizeof(con->se_in.ss));
269
270 getmonotime(&con->se_tv_start);
271 bcopy(&con->se_tv_start, &con->se_tv_last, sizeof(con->se_tv_last));
272
273 relay_sessions++;
274 SPLAY_INSERT(session_tree, &rlay->rl_sessions, con);
275 relay_session_publish(con);
276
277 /* Increment the per-relay session counter */
278 rlay->rl_stats[ps->ps_instance].last++;
279
280 /* Pre-allocate output buffer */
281 con->se_out.output = evbuffer_new();
282 if (con->se_out.output == NULL) {
283 relay_close(con, "failed to allocate output buffer", 1);
284 return;
285 }
286
287 /* Pre-allocate log buffer */
288 con->se_haslog = 0;
289 con->se_log = evbuffer_new();
290 if (con->se_log == NULL) {
291 relay_close(con, "failed to allocate log buffer", 1);
292 return;
293 }
294
295 if (rlay->rl_conf.flags & F_NATLOOK) {
296 if ((cnl = calloc(1, sizeof(*cnl))) == NULL) {
297 relay_close(con, "failed to allocate natlookup", 1);
298 return;
299 }
300 }
301
302 /* Save the received data */
303 if (evbuffer_add(con->se_out.output, buf, len) == -1) {
304 relay_close(con, "failed to store buffer", 1);
305 free(cnl);
306 return;
307 }
308
309 if (cnl != NULL) {
310 con->se_cnl = cnl;
311 bzero(cnl, sizeof(*cnl));
312 cnl->in = -1;
313 cnl->id = con->se_id;
314 cnl->proc = ps->ps_instance;
315 cnl->proto = IPPROTO_UDP;
316 bcopy(&con->se_in.ss, &cnl->src, sizeof(cnl->src));
317 bcopy(&rlay->rl_conf.ss, &cnl->dst, sizeof(cnl->dst));
318 proc_compose(env->sc_ps, PROC_PFE,
319 IMSG_NATLOOK, cnl, sizeof(*cnl));
320
321 /* Schedule timeout */
322 evtimer_set(&con->se_ev, relay_natlook, con);
323 bcopy(&rlay->rl_conf.timeout, &tv, sizeof(tv));
324 evtimer_add(&con->se_ev, &tv);
325 return;
326 }
327
328 relay_session(con);
329 }
330
331 void
relay_udp_timeout(int fd,short sig,void * arg)332 relay_udp_timeout(int fd, short sig, void *arg)
333 {
334 struct rsession *con = arg;
335
336 if (sig != EV_TIMEOUT)
337 fatalx("invalid timeout event");
338
339 relay_close(con, "udp timeout", 1);
340 }
341
342 /*
343 * Domain Name System support
344 */
345
346 struct relay_dns_priv {
347 u_int16_t dp_inkey;
348 u_int16_t dp_outkey;
349 };
350
351 struct relay_dnshdr {
352 u_int16_t dns_id;
353
354 u_int8_t dns_flags0;
355 #define DNS_F0_QR 0x80 /* response flag */
356 #define DNS_F0_OPCODE 0x78 /* message type */
357 #define DNS_F0_AA 0x04 /* authoritative answer */
358 #define DNS_F0_TC 0x02 /* truncated message */
359 #define DNS_F0_RD 0x01 /* recursion desired */
360
361 u_int8_t dns_flags1;
362 #define DNS_F1_RA 0x80 /* recursion available */
363 #define DNS_F1_RES 0x40 /* reserved */
364 #define DNS_F1_AD 0x20 /* authentic data */
365 #define DNS_F1_CD 0x10 /* checking disabled */
366 #define DNS_F1_RCODE 0x0f /* response code */
367
368 u_int16_t dns_qdcount;
369 u_int16_t dns_ancount;
370 u_int16_t dns_nscount;
371 u_int16_t dns_arcount;
372 } __packed;
373
374 void
relay_dns_log(struct rsession * con,u_int8_t * buf,size_t len)375 relay_dns_log(struct rsession *con, u_int8_t *buf, size_t len)
376 {
377 struct relay_dnshdr *hdr = (struct relay_dnshdr *)buf;
378
379 /* Validate the header length */
380 if (len < sizeof(*hdr)) {
381 log_debug("%s: session %d: short dns packet", __func__,
382 con->se_id);
383 return;
384 }
385
386 log_debug("%s: session %d: %s id 0x%x "
387 "flags 0x%x:0x%x qd %u an %u ns %u ar %u", __func__,
388 con->se_id,
389 hdr->dns_flags0 & DNS_F0_QR ? "response" : "request",
390 ntohs(hdr->dns_id),
391 hdr->dns_flags0,
392 hdr->dns_flags1,
393 ntohs(hdr->dns_qdcount),
394 ntohs(hdr->dns_ancount),
395 ntohs(hdr->dns_nscount),
396 ntohs(hdr->dns_arcount));
397 }
398
399 void *
relay_dns_validate(struct rsession * con,struct relay * rlay,struct sockaddr_storage * ss,u_int8_t * buf,size_t len)400 relay_dns_validate(struct rsession *con, struct relay *rlay,
401 struct sockaddr_storage *ss, u_int8_t *buf, size_t len)
402 {
403 struct relay_dnshdr *hdr = (struct relay_dnshdr *)buf;
404 struct rsession lookup;
405 u_int16_t key;
406 struct relay_dns_priv *priv, lpriv;
407
408 /* Validate the header length */
409 if (len < sizeof(*hdr))
410 return (NULL);
411
412 key = ntohs(hdr->dns_id);
413
414 /*
415 * Check if the header has the response flag set, otherwise
416 * return 0 to tell the UDP server to create a new session.
417 */
418 if ((hdr->dns_flags0 & DNS_F0_QR) == 0) {
419 priv = malloc(sizeof(struct relay_dns_priv));
420 if (priv == NULL)
421 return (NULL);
422 priv->dp_inkey = shuffle_generate16(&relay_shuffle);
423 priv->dp_outkey = key;
424 return ((void *)priv);
425 }
426
427 /*
428 * Lookup if this response is for a known session and if the
429 * remote host matches the original destination of the request.
430 */
431 if (con == NULL) {
432 lpriv.dp_inkey = key;
433 lookup.se_priv = &lpriv;
434 if ((con = SPLAY_FIND(session_tree,
435 &rlay->rl_sessions, &lookup)) != NULL &&
436 con->se_priv != NULL &&
437 relay_cmp_af(ss, &con->se_out.ss) == 0)
438 relay_dns_result(con, buf, len);
439 } else {
440 priv = con->se_priv;
441 if (priv == NULL || key != priv->dp_inkey) {
442 relay_close(con, "invalid response", 1);
443 return (NULL);
444 }
445 relay_dns_result(con, buf, len);
446 }
447
448 /*
449 * This is not a new session, ignore it in the UDP server.
450 */
451 return (NULL);
452 }
453
454 int
relay_dns_request(struct rsession * con)455 relay_dns_request(struct rsession *con)
456 {
457 struct relay *rlay = con->se_relay;
458 struct relay_dns_priv *priv = con->se_priv;
459 u_int8_t *buf = EVBUFFER_DATA(con->se_out.output);
460 size_t len = EVBUFFER_LENGTH(con->se_out.output);
461 struct relay_dnshdr *hdr;
462 socklen_t slen;
463
464 if (buf == NULL || priv == NULL || len < 1)
465 return (-1);
466 if (log_getverbose() > 1)
467 relay_dns_log(con, buf, len);
468
469 getmonotime(&con->se_tv_start);
470
471 if (!TAILQ_EMPTY(&rlay->rl_tables)) {
472 if (relay_from_table(con) != 0)
473 return (-1);
474 } else if (con->se_out.ss.ss_family == AF_UNSPEC) {
475 bcopy(&rlay->rl_conf.dstss, &con->se_out.ss,
476 sizeof(con->se_out.ss));
477 con->se_out.port = rlay->rl_conf.dstport;
478 }
479
480 if ((con->se_out.s = relay_udp_socket(&con->se_out.ss,
481 con->se_out.port, rlay->rl_proto)) == -1)
482 return (-1);
483 slen = con->se_out.ss.ss_len;
484
485 hdr = (struct relay_dnshdr *)buf;
486 hdr->dns_id = htons(priv->dp_inkey);
487
488 retry:
489 if (sendto(con->se_out.s, buf, len, 0,
490 (struct sockaddr *)&con->se_out.ss, slen) == -1) {
491 if (con->se_retry) {
492 con->se_retry--;
493 log_debug("%s: session %d: "
494 "forward failed: %s, %s", __func__,
495 con->se_id, strerror(errno),
496 con->se_retry ? "next retry" : "last retry");
497 goto retry;
498 }
499 log_debug("%s: session %d: forward failed: %s", __func__,
500 con->se_id, strerror(errno));
501 return (-1);
502 }
503
504 event_again(&con->se_ev, con->se_out.s, EV_TIMEOUT|EV_READ,
505 relay_udp_response, &con->se_tv_start, &rlay->rl_conf.timeout, con);
506
507 return (0);
508 }
509
510 void
relay_dns_result(struct rsession * con,u_int8_t * buf,size_t len)511 relay_dns_result(struct rsession *con, u_int8_t *buf, size_t len)
512 {
513 struct relay *rlay = con->se_relay;
514 struct relay_dns_priv *priv = con->se_priv;
515 struct relay_dnshdr *hdr;
516 socklen_t slen;
517
518 if (priv == NULL)
519 fatalx("%s: response to invalid session", __func__);
520
521 if (log_getverbose() > 1)
522 relay_dns_log(con, buf, len);
523
524 /*
525 * Replace the random DNS request Id with the original Id
526 */
527 hdr = (struct relay_dnshdr *)buf;
528 hdr->dns_id = htons(priv->dp_outkey);
529
530 slen = con->se_out.ss.ss_len;
531 if (sendto(rlay->rl_s, buf, len, 0,
532 (struct sockaddr *)&con->se_in.ss, slen) == -1) {
533 relay_close(con, "response failed", 1);
534 return;
535 }
536
537 relay_close(con, "session closed", 0);
538 }
539
540 int
relay_dns_cmp(struct rsession * a,struct rsession * b)541 relay_dns_cmp(struct rsession *a, struct rsession *b)
542 {
543 struct relay_dns_priv *ap = a->se_priv;
544 struct relay_dns_priv *bp = b->se_priv;
545
546 if (ap == NULL || bp == NULL)
547 fatalx("%s: invalid session", __func__);
548
549 return (memcmp(&ap->dp_inkey, &bp->dp_inkey, sizeof(u_int16_t)));
550 }
551