xref: /openbsd/usr.sbin/relayd/relay_udp.c (revision 6f40fd34)
1 /*	$OpenBSD: relay_udp.c,v 1.47 2017/07/04 19:59:51 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 (relay_sessions >= RELAY_MAX_SESSIONS ||
195 	    rlay->rl_conf.flags & F_DISABLE)
196 		return;
197 
198 	slen = sizeof(ss);
199 	if ((len = recvfrom(fd, buf, sizeof(buf), 0,
200 	    (struct sockaddr*)&ss, &slen)) < 1)
201 		return;
202 
203 	/* Parse and validate the packet header */
204 	if (proto->validate != NULL &&
205 	    (priv = (*proto->validate)(con, rlay, &ss, buf, len)) == NULL)
206 		return;
207 
208 	relay_close(con, "unknown response");
209 	free(priv);
210 }
211 
212 void
213 relay_udp_server(int fd, short sig, void *arg)
214 {
215 	struct privsep *ps = env->sc_ps;
216 	struct relay *rlay = arg;
217 	struct protocol *proto = rlay->rl_proto;
218 	struct rsession *con = NULL;
219 	struct ctl_natlook *cnl = NULL;
220 	socklen_t slen;
221 	struct timeval tv;
222 	struct sockaddr_storage ss;
223 	u_int8_t buf[IBUF_READ_SIZE];
224 	void *priv = NULL;
225 	ssize_t len;
226 
227 	event_add(&rlay->rl_ev, NULL);
228 
229 	if (relay_sessions >= RELAY_MAX_SESSIONS ||
230 	    rlay->rl_conf.flags & F_DISABLE)
231 		return;
232 
233 	slen = sizeof(ss);
234 	if ((len = recvfrom(fd, buf, sizeof(buf), 0,
235 	    (struct sockaddr*)&ss, &slen)) < 1)
236 		return;
237 
238 	if (proto->validate != NULL &&
239 	    (priv = (*proto->validate)(NULL, rlay, &ss, buf, len)) == NULL)
240 		return;
241 
242 	if ((con = calloc(1, sizeof(*con))) == NULL) {
243 		free(priv);
244 		return;
245 	}
246 
247 	/*
248 	 * Replace the DNS request Id with a random Id.
249 	 */
250 	con->se_priv = priv;
251 	con->se_in.s = -1;
252 	con->se_out.s = -1;
253 	con->se_in.dst = &con->se_out;
254 	con->se_out.dst = &con->se_in;
255 	con->se_in.con = con;
256 	con->se_out.con = con;
257 	con->se_relay = rlay;
258 	con->se_id = ++relay_conid;
259 	con->se_in.dir = RELAY_DIR_REQUEST;
260 	con->se_out.dir = RELAY_DIR_RESPONSE;
261 	con->se_retry = rlay->rl_conf.dstretry;
262 	con->se_out.port = rlay->rl_conf.dstport;
263 	switch (ss.ss_family) {
264 	case AF_INET:
265 		con->se_in.port = ((struct sockaddr_in *)&ss)->sin_port;
266 		break;
267 	case AF_INET6:
268 		con->se_in.port = ((struct sockaddr_in6 *)&ss)->sin6_port;
269 		break;
270 	}
271 	bcopy(&ss, &con->se_in.ss, sizeof(con->se_in.ss));
272 
273 	getmonotime(&con->se_tv_start);
274 	bcopy(&con->se_tv_start, &con->se_tv_last, sizeof(con->se_tv_last));
275 
276 	relay_sessions++;
277 	SPLAY_INSERT(session_tree, &rlay->rl_sessions, con);
278 	relay_session_publish(con);
279 
280 	/* Increment the per-relay session counter */
281 	rlay->rl_stats[ps->ps_instance].last++;
282 
283 	/* Pre-allocate output buffer */
284 	con->se_out.output = evbuffer_new();
285 	if (con->se_out.output == NULL) {
286 		relay_close(con, "failed to allocate output buffer");
287 		return;
288 	}
289 
290 	/* Pre-allocate log buffer */
291 	con->se_haslog = 0;
292 	con->se_log = evbuffer_new();
293 	if (con->se_log == NULL) {
294 		relay_close(con, "failed to allocate log buffer");
295 		return;
296 	}
297 
298 	if (rlay->rl_conf.flags & F_NATLOOK) {
299 		if ((cnl = calloc(1, sizeof(*cnl))) == NULL) {
300 			relay_close(con, "failed to allocate natlookup");
301 			return;
302 		}
303 	}
304 
305 	/* Save the received data */
306 	if (evbuffer_add(con->se_out.output, buf, len) == -1) {
307 		relay_close(con, "failed to store buffer");
308 		free(cnl);
309 		return;
310 	}
311 
312 	if (cnl != NULL) {
313 		con->se_cnl = cnl;
314 		bzero(cnl, sizeof(*cnl));
315 		cnl->in = -1;
316 		cnl->id = con->se_id;
317 		cnl->proc = ps->ps_instance;
318 		cnl->proto = IPPROTO_UDP;
319 		bcopy(&con->se_in.ss, &cnl->src, sizeof(cnl->src));
320 		bcopy(&rlay->rl_conf.ss, &cnl->dst, sizeof(cnl->dst));
321 		proc_compose(env->sc_ps, PROC_PFE,
322 		    IMSG_NATLOOK, cnl, sizeof(*cnl));
323 
324 		/* Schedule timeout */
325 		evtimer_set(&con->se_ev, relay_natlook, con);
326 		bcopy(&rlay->rl_conf.timeout, &tv, sizeof(tv));
327 		evtimer_add(&con->se_ev, &tv);
328 		return;
329 	}
330 
331 	relay_session(con);
332 }
333 
334 void
335 relay_udp_timeout(int fd, short sig, void *arg)
336 {
337 	struct rsession		*con = arg;
338 
339 	if (sig != EV_TIMEOUT)
340 		fatalx("invalid timeout event");
341 
342 	relay_close(con, "udp timeout");
343 }
344 
345 /*
346  * Domain Name System support
347  */
348 
349 struct relay_dns_priv {
350 	u_int16_t	dp_inkey;
351 	u_int16_t	dp_outkey;
352 };
353 
354 struct relay_dnshdr {
355 	u_int16_t	dns_id;
356 
357 	u_int8_t	dns_flags0;
358 #define  DNS_F0_QR	0x80		/* response flag */
359 #define  DNS_F0_OPCODE	0x78		/* message type */
360 #define  DNS_F0_AA	0x04		/* authorative answer */
361 #define  DNS_F0_TC	0x02		/* truncated message */
362 #define  DNS_F0_RD	0x01		/* recursion desired */
363 
364 	u_int8_t	dns_flags1;
365 #define  DNS_F1_RA	0x80		/* recursion available */
366 #define  DNS_F1_RES	0x40		/* reserved */
367 #define  DNS_F1_AD	0x20		/* authentic data */
368 #define  DNS_F1_CD	0x10		/* checking disabled */
369 #define  DNS_F1_RCODE	0x0f		/* response code */
370 
371 	u_int16_t	dns_qdcount;
372 	u_int16_t	dns_ancount;
373 	u_int16_t	dns_nscount;
374 	u_int16_t	dns_arcount;
375 } __packed;
376 
377 void
378 relay_dns_log(struct rsession *con, u_int8_t *buf, size_t len)
379 {
380 	struct relay_dnshdr	*hdr = (struct relay_dnshdr *)buf;
381 
382 	/* Validate the header length */
383 	if (len < sizeof(*hdr)) {
384 		log_debug("%s: session %d: short dns packet", __func__,
385 		    con->se_id);
386 		return;
387 	}
388 
389 	log_debug("%s: session %d: %s id 0x%x "
390 	    "flags 0x%x:0x%x qd %u an %u ns %u ar %u", __func__,
391 	    con->se_id,
392 	    hdr->dns_flags0 & DNS_F0_QR ? "response" : "request",
393 	    ntohs(hdr->dns_id),
394 	    hdr->dns_flags0,
395 	    hdr->dns_flags1,
396 	    ntohs(hdr->dns_qdcount),
397 	    ntohs(hdr->dns_ancount),
398 	    ntohs(hdr->dns_nscount),
399 	    ntohs(hdr->dns_arcount));
400 }
401 
402 void *
403 relay_dns_validate(struct rsession *con, struct relay *rlay,
404     struct sockaddr_storage *ss, u_int8_t *buf, size_t len)
405 {
406 	struct relay_dnshdr	*hdr = (struct relay_dnshdr *)buf;
407 	struct rsession		 lookup;
408 	u_int16_t		 key;
409 	struct relay_dns_priv	*priv, lpriv;
410 
411 	/* Validate the header length */
412 	if (len < sizeof(*hdr))
413 		return (NULL);
414 
415 	key = ntohs(hdr->dns_id);
416 
417 	/*
418 	 * Check if the header has the response flag set, otherwise
419 	 * return 0 to tell the UDP server to create a new session.
420 	 */
421 	if ((hdr->dns_flags0 & DNS_F0_QR) == 0) {
422 		priv = malloc(sizeof(struct relay_dns_priv));
423 		if (priv == NULL)
424 			return (NULL);
425 		priv->dp_inkey = shuffle_generate16(&relay_shuffle);
426 		priv->dp_outkey = key;
427 		return ((void *)priv);
428 	}
429 
430 	/*
431 	 * Lookup if this response is for a known session and if the
432 	 * remote host matches the original destination of the request.
433 	 */
434 	if (con == NULL) {
435 		lpriv.dp_inkey = key;
436 		lookup.se_priv = &lpriv;
437 		if ((con = SPLAY_FIND(session_tree,
438 		    &rlay->rl_sessions, &lookup)) != NULL &&
439 		    con->se_priv != NULL &&
440 		    relay_cmp_af(ss, &con->se_out.ss) == 0)
441 			relay_dns_result(con, buf, len);
442 	} else {
443 		priv = con->se_priv;
444 		if (priv == NULL || key != priv->dp_inkey) {
445 			relay_close(con, "invalid response");
446 			return (NULL);
447 		}
448 		relay_dns_result(con, buf, len);
449 	}
450 
451 	/*
452 	 * This is not a new session, ignore it in the UDP server.
453 	 */
454 	return (NULL);
455 }
456 
457 int
458 relay_dns_request(struct rsession *con)
459 {
460 	struct relay		*rlay = con->se_relay;
461 	struct relay_dns_priv	*priv = con->se_priv;
462 	u_int8_t		*buf = EVBUFFER_DATA(con->se_out.output);
463 	size_t			 len = EVBUFFER_LENGTH(con->se_out.output);
464 	struct relay_dnshdr	*hdr;
465 	socklen_t		 slen;
466 
467 	if (buf == NULL || priv == NULL || len < 1)
468 		return (-1);
469 	if (log_getverbose() > 1)
470 		relay_dns_log(con, buf, len);
471 
472 	getmonotime(&con->se_tv_start);
473 
474 	if (!TAILQ_EMPTY(&rlay->rl_tables)) {
475 		if (relay_from_table(con) != 0)
476 			return (-1);
477 	} else if (con->se_out.ss.ss_family == AF_UNSPEC) {
478 		bcopy(&rlay->rl_conf.dstss, &con->se_out.ss,
479 		    sizeof(con->se_out.ss));
480 		con->se_out.port = rlay->rl_conf.dstport;
481 	}
482 
483 	if ((con->se_out.s = relay_udp_socket(&con->se_out.ss,
484 	    con->se_out.port, rlay->rl_proto)) == -1)
485 		return (-1);
486 	slen = con->se_out.ss.ss_len;
487 
488 	hdr = (struct relay_dnshdr *)buf;
489 	hdr->dns_id = htons(priv->dp_inkey);
490 
491  retry:
492 	if (sendto(con->se_out.s, buf, len, 0,
493 	    (struct sockaddr *)&con->se_out.ss, slen) == -1) {
494 		if (con->se_retry) {
495 			con->se_retry--;
496 			log_debug("%s: session %d: "
497 			    "forward failed: %s, %s", __func__,
498 			    con->se_id, strerror(errno),
499 			    con->se_retry ? "next retry" : "last retry");
500 			goto retry;
501 		}
502 		log_debug("%s: session %d: forward failed: %s", __func__,
503 		    con->se_id, strerror(errno));
504 		return (-1);
505 	}
506 
507 	event_again(&con->se_ev, con->se_out.s, EV_TIMEOUT|EV_READ,
508 	    relay_udp_response, &con->se_tv_start, &rlay->rl_conf.timeout, con);
509 
510 	return (0);
511 }
512 
513 void
514 relay_dns_result(struct rsession *con, u_int8_t *buf, size_t len)
515 {
516 	struct relay		*rlay = con->se_relay;
517 	struct relay_dns_priv	*priv = con->se_priv;
518 	struct relay_dnshdr	*hdr;
519 	socklen_t		 slen;
520 
521 	if (priv == NULL)
522 		fatalx("%s: response to invalid session", __func__);
523 
524 	if (log_getverbose() > 1)
525 		relay_dns_log(con, buf, len);
526 
527 	/*
528 	 * Replace the random DNS request Id with the original Id
529 	 */
530 	hdr = (struct relay_dnshdr *)buf;
531 	hdr->dns_id = htons(priv->dp_outkey);
532 
533 	slen = con->se_out.ss.ss_len;
534 	if (sendto(rlay->rl_s, buf, len, 0,
535 	    (struct sockaddr *)&con->se_in.ss, slen) == -1) {
536 		relay_close(con, "response failed");
537 		return;
538 	}
539 
540 	relay_close(con, "session closed");
541 }
542 
543 int
544 relay_dns_cmp(struct rsession *a, struct rsession *b)
545 {
546 	struct relay_dns_priv	*ap = a->se_priv;
547 	struct relay_dns_priv	*bp = b->se_priv;
548 
549 	if (ap == NULL || bp == NULL)
550 		fatalx("%s: invalid session", __func__);
551 
552 	return (memcmp(&ap->dp_inkey, &bp->dp_inkey, sizeof(u_int16_t)));
553 }
554