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