xref: /openbsd/usr.sbin/relayd/relay_udp.c (revision e15601b9)
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