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