GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/relayd/relay_udp.c Lines: 0 243 0.0 %
Date: 2017-11-13 Branches: 0 125 0.0 %

Line Branch Exec Source
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
}