1 /* $OpenBSD: relay_udp.c,v 1.52 2026/03/02 19:28:01 rsadowski Exp $ */
4 * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org>
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.
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.
19 #include <sys/types.h>
20 #include <sys/queue.h>
22 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
41 extern volatile sig_atomic_t relay_sessions;
42 extern objid_t relay_conid;
44 static struct relayd *env = NULL;
45 struct shuffle relay_shuffle;
47 int relay_udp_socket(struct sockaddr_storage *, in_port_t,
49 void relay_udp_timeout(int, short, void *);
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 *,
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 *);
61 relay_udp_privinit(struct relay *rlay)
63 if (rlay->rl_conf.flags & F_TLS)
64 fatalx("tls over udp is not supported");
65 rlay->rl_conf.flags |= F_UDP;
69 relay_udp_init(struct relayd *x_env, struct relay *rlay)
71 struct protocol *proto = rlay->rl_proto;
76 switch (proto->type) {
78 proto->validate = relay_dns_validate;
79 proto->request = relay_dns_request;
80 proto->cmp = relay_dns_cmp;
81 shuffle_init(&relay_shuffle);
84 fatalx("unsupported udp protocol");
90 relay_udp_bind(struct sockaddr_storage *ss, in_port_t port,
91 struct protocol *proto)
95 if ((s = relay_udp_socket(ss, port, proto)) == -1)
98 if (bind(s, (struct sockaddr *)ss, ss->ss_len) == -1)
109 relay_udp_socket(struct sockaddr_storage *ss, in_port_t port,
110 struct protocol *proto)
114 if (relay_socket_af(ss, port) == -1)
117 if ((s = socket(ss->ss_family, SOCK_DGRAM | SOCK_NONBLOCK,
124 if (proto->tcpflags & TCPFLAG_BUFSIZ) {
125 val = proto->tcpbufsiz;
126 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
127 &val, sizeof(val)) == -1)
129 val = proto->tcpbufsiz;
130 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
131 &val, sizeof(val)) == -1)
138 if (proto->tcpflags & TCPFLAG_IPTTL) {
139 val = (int)proto->tcpipttl;
140 switch (ss->ss_family) {
142 if (setsockopt(s, IPPROTO_IP, IP_TTL,
143 &val, sizeof(val)) == -1)
147 if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
148 &val, sizeof(val)) == -1)
153 if (proto->tcpflags & TCPFLAG_IPMINTTL) {
154 val = (int)proto->tcpipminttl;
155 switch (ss->ss_family) {
157 if (setsockopt(s, IPPROTO_IP, IP_MINTTL,
158 &val, sizeof(val)) == -1)
162 if (setsockopt(s, IPPROTO_IPV6, IPV6_MINHOPCOUNT,
163 &val, sizeof(val)) == -1)
178 relay_udp_response(int fd, short sig, void *arg)
180 struct rsession *con = arg;
181 struct relay *rlay = con->se_relay;
182 struct protocol *proto = rlay->rl_proto;
184 struct sockaddr_storage ss;
185 u_int8_t buf[IBUF_READ_SIZE];
189 if (sig == EV_TIMEOUT) {
190 relay_udp_timeout(fd, sig, arg);
194 if (rlay->rl_conf.flags & F_DISABLE)
198 if ((len = recvfrom(fd, buf, sizeof(buf), 0,
199 (struct sockaddr*)&ss, &slen)) < 1)
202 /* Parse and validate the packet header */
203 if (proto->validate != NULL &&
204 (priv = (*proto->validate)(con, rlay, &ss, buf, len)) == NULL)
207 relay_close(con, "unknown response", 1);
212 relay_udp_server(int fd, short sig, void *arg)
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;
221 struct sockaddr_storage ss;
222 u_int8_t buf[IBUF_READ_SIZE];
226 event_add(&rlay->rl_ev, NULL);
228 if (rlay->rl_conf.flags & F_DISABLE)
232 if ((len = recvfrom(fd, buf, sizeof(buf), 0,
233 (struct sockaddr*)&ss, &slen)) < 1)
236 if (proto->validate != NULL &&
237 (priv = (*proto->validate)(NULL, rlay, &ss, buf, len)) == NULL)
240 if ((con = calloc(1, sizeof(*con))) == NULL) {
246 * Replace the DNS request Id with a random Id.
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) {
263 con->se_in.port = ((struct sockaddr_in *)&ss)->sin_port;
266 con->se_in.port = ((struct sockaddr_in6 *)&ss)->sin6_port;
269 bcopy(&ss, &con->se_in.ss, sizeof(con->se_in.ss));
271 getmonotime(&con->se_tv_start);
272 bcopy(&con->se_tv_start, &con->se_tv_last, sizeof(con->se_tv_last));
275 SPLAY_INSERT(session_tree, &rlay->rl_sessions, con);
276 relay_session_publish(con);
278 /* Increment the per-relay session counter */
279 rlay->rl_stats[ps->ps_instance].last++;
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);
288 /* Pre-allocate log buffer */
290 con->se_log = evbuffer_new();
291 if (con->se_log == NULL) {
292 relay_close(con, "failed to allocate log buffer", 1);
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);
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);
312 bzero(cnl, sizeof(*cnl));
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));
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);
333 relay_udp_timeout(int fd, short sig, void *arg)
335 struct rsession *con = arg;
337 if (sig != EV_TIMEOUT)
338 fatalx("invalid timeout event");
340 relay_close(con, "udp timeout", 1);
344 * Domain Name System support
347 struct relay_dns_priv {
352 struct relay_dnshdr {
356 #define DNS_F0_QR 0x80 /* response flag */
357 #define DNS_F0_OPCODE 0x78 /* message type */
358 #define DNS_F0_AA 0x04 /* authoritative answer */
359 #define DNS_F0_TC 0x02 /* truncated message */
360 #define DNS_F0_RD 0x01 /* recursion desired */
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 */
369 u_int16_t dns_qdcount;
370 u_int16_t dns_ancount;
371 u_int16_t dns_nscount;
372 u_int16_t dns_arcount;
376 relay_dns_log(struct rsession *con, u_int8_t *buf, size_t len)
378 struct relay_dnshdr *hdr = (struct relay_dnshdr *)buf;
380 /* Validate the header length */
381 if (len < sizeof(*hdr)) {
382 log_debug("%s: session %d: short dns packet", __func__,
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__,
390 hdr->dns_flags0 & DNS_F0_QR ? "response" : "request",
394 ntohs(hdr->dns_qdcount),
395 ntohs(hdr->dns_ancount),
396 ntohs(hdr->dns_nscount),
397 ntohs(hdr->dns_arcount));
401 relay_dns_validate(struct rsession *con, struct relay *rlay,
402 struct sockaddr_storage *ss, u_int8_t *buf, size_t len)
404 struct relay_dnshdr *hdr = (struct relay_dnshdr *)buf;
405 struct rsession lookup;
407 struct relay_dns_priv *priv, lpriv;
409 /* Validate the header length */
410 if (len < sizeof(*hdr))
413 key = ntohs(hdr->dns_id);
416 * Check if the header has the response flag set, otherwise
417 * return 0 to tell the UDP server to create a new session.
419 if ((hdr->dns_flags0 & DNS_F0_QR) == 0) {
420 priv = malloc(sizeof(struct relay_dns_priv));
423 priv->dp_inkey = shuffle_generate16(&relay_shuffle);
424 priv->dp_outkey = key;
425 return ((void *)priv);
429 * Lookup if this response is for a known session and if the
430 * remote host matches the original destination of the request.
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);
442 if (priv == NULL || key != priv->dp_inkey) {
443 relay_close(con, "invalid response", 1);
446 relay_dns_result(con, buf, len);
450 * This is not a new session, ignore it in the UDP server.
456 relay_dns_request(struct rsession *con)
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;
465 if (buf == NULL || priv == NULL || len < 1)
467 if (log_getverbose() > 1)
468 relay_dns_log(con, buf, len);
470 getmonotime(&con->se_tv_start);
472 if (!TAILQ_EMPTY(&rlay->rl_tables)) {
473 if (relay_from_table(con) != 0)
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;
481 if ((con->se_out.s = relay_udp_socket(&con->se_out.ss,
482 con->se_out.port, rlay->rl_proto)) == -1)
484 slen = con->se_out.ss.ss_len;
486 hdr = (struct relay_dnshdr *)buf;
487 hdr->dns_id = htons(priv->dp_inkey);
490 if (sendto(con->se_out.s, buf, len, 0,
491 (struct sockaddr *)&con->se_out.ss, slen) == -1) {
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");
500 log_debug("%s: session %d: forward failed: %s", __func__,
501 con->se_id, strerror(errno));
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);
512 relay_dns_result(struct rsession *con, u_int8_t *buf, size_t len)
514 struct relay *rlay = con->se_relay;
515 struct relay_dns_priv *priv = con->se_priv;
516 struct relay_dnshdr *hdr;
520 fatalx("%s: response to invalid session", __func__);
522 if (log_getverbose() > 1)
523 relay_dns_log(con, buf, len);
526 * Replace the random DNS request Id with the original Id
528 hdr = (struct relay_dnshdr *)buf;
529 hdr->dns_id = htons(priv->dp_outkey);
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);
538 relay_close(con, "session closed", 0);
542 relay_dns_cmp(struct rsession *a, struct rsession *b)
544 struct relay_dns_priv *ap = a->se_priv;
545 struct relay_dns_priv *bp = b->se_priv;
547 if (ap == NULL || bp == NULL)
548 fatalx("%s: invalid session", __func__);
550 return (memcmp(&ap->dp_inkey, &bp->dp_inkey, sizeof(u_int16_t)));