1*e15601b9Sjsg /* $OpenBSD: relay_udp.c,v 1.51 2024/05/18 06:34:46 jsg Exp $ */
22380f4f2Sreyk
32380f4f2Sreyk /*
4d535e21cSreyk * Copyright (c) 2007 - 2013 Reyk Floeter <reyk@openbsd.org>
52380f4f2Sreyk *
62380f4f2Sreyk * Permission to use, copy, modify, and distribute this software for any
72380f4f2Sreyk * purpose with or without fee is hereby granted, provided that the above
82380f4f2Sreyk * copyright notice and this permission notice appear in all copies.
92380f4f2Sreyk *
102380f4f2Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
112380f4f2Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
122380f4f2Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
132380f4f2Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
142380f4f2Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
152380f4f2Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
162380f4f2Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
172380f4f2Sreyk */
182380f4f2Sreyk
192380f4f2Sreyk #include <sys/types.h>
200ca734d7Sreyk #include <sys/queue.h>
212380f4f2Sreyk #include <sys/time.h>
222380f4f2Sreyk #include <sys/socket.h>
232380f4f2Sreyk #include <sys/tree.h>
242380f4f2Sreyk
252380f4f2Sreyk #include <netinet/in.h>
26f04ff968Sreyk #include <arpa/inet.h>
272380f4f2Sreyk
28e2318a52Sderaadt #include <signal.h>
292380f4f2Sreyk #include <errno.h>
302380f4f2Sreyk #include <fcntl.h>
312380f4f2Sreyk #include <stdlib.h>
322380f4f2Sreyk #include <string.h>
332380f4f2Sreyk #include <unistd.h>
342380f4f2Sreyk #include <stdio.h>
352380f4f2Sreyk #include <event.h>
36f04ff968Sreyk #include <imsg.h>
372380f4f2Sreyk
38748ceb64Sreyk #include "relayd.h"
392380f4f2Sreyk
402380f4f2Sreyk extern volatile sig_atomic_t relay_sessions;
412380f4f2Sreyk extern objid_t relay_conid;
422380f4f2Sreyk
43cb8b0e56Sreyk static struct relayd *env = NULL;
448661b3ffSreyk struct shuffle relay_shuffle;
452380f4f2Sreyk
462380f4f2Sreyk int relay_udp_socket(struct sockaddr_storage *, in_port_t,
472380f4f2Sreyk struct protocol *);
482380f4f2Sreyk void relay_udp_timeout(int, short, void *);
492380f4f2Sreyk
50f4a6e73bSreyk void relay_dns_log(struct rsession *, u_int8_t *, size_t);
51f4a6e73bSreyk void *relay_dns_validate(struct rsession *,
52bec706feSreyk struct relay *, struct sockaddr_storage *,
530dbc5f9eSreyk u_int8_t *, size_t);
54f4a6e73bSreyk int relay_dns_request(struct rsession *);
55bec706feSreyk void relay_udp_response(int, short, void *);
56f4a6e73bSreyk void relay_dns_result(struct rsession *, u_int8_t *, size_t);
57f4a6e73bSreyk int relay_dns_cmp(struct rsession *, struct rsession *);
582380f4f2Sreyk
592380f4f2Sreyk void
relay_udp_privinit(struct relay * rlay)60f164d32fSbenno relay_udp_privinit(struct relay *rlay)
612380f4f2Sreyk {
627bb52228Sreyk if (rlay->rl_conf.flags & F_TLS)
637bb52228Sreyk fatalx("tls over udp is not supported");
644a5b9b3eSreyk rlay->rl_conf.flags |= F_UDP;
658661b3ffSreyk }
668661b3ffSreyk
678661b3ffSreyk void
relay_udp_init(struct relayd * x_env,struct relay * rlay)68f164d32fSbenno relay_udp_init(struct relayd *x_env, struct relay *rlay)
698661b3ffSreyk {
708661b3ffSreyk struct protocol *proto = rlay->rl_proto;
712380f4f2Sreyk
72f164d32fSbenno if (env == NULL)
73f164d32fSbenno env = x_env;
74f164d32fSbenno
752380f4f2Sreyk switch (proto->type) {
762380f4f2Sreyk case RELAY_PROTO_DNS:
772380f4f2Sreyk proto->validate = relay_dns_validate;
782380f4f2Sreyk proto->request = relay_dns_request;
792380f4f2Sreyk proto->cmp = relay_dns_cmp;
808661b3ffSreyk shuffle_init(&relay_shuffle);
812380f4f2Sreyk break;
822380f4f2Sreyk default:
832380f4f2Sreyk fatalx("unsupported udp protocol");
842380f4f2Sreyk break;
852380f4f2Sreyk }
862380f4f2Sreyk }
872380f4f2Sreyk
882380f4f2Sreyk int
relay_udp_bind(struct sockaddr_storage * ss,in_port_t port,struct protocol * proto)892380f4f2Sreyk relay_udp_bind(struct sockaddr_storage *ss, in_port_t port,
902380f4f2Sreyk struct protocol *proto)
912380f4f2Sreyk {
922380f4f2Sreyk int s;
932380f4f2Sreyk
942380f4f2Sreyk if ((s = relay_udp_socket(ss, port, proto)) == -1)
952380f4f2Sreyk return (-1);
962380f4f2Sreyk
972380f4f2Sreyk if (bind(s, (struct sockaddr *)ss, ss->ss_len) == -1)
982380f4f2Sreyk goto bad;
992380f4f2Sreyk
1002380f4f2Sreyk return (s);
1012380f4f2Sreyk
1022380f4f2Sreyk bad:
1032380f4f2Sreyk close(s);
1042380f4f2Sreyk return (-1);
1052380f4f2Sreyk }
1062380f4f2Sreyk
1072380f4f2Sreyk int
relay_udp_socket(struct sockaddr_storage * ss,in_port_t port,struct protocol * proto)1082380f4f2Sreyk relay_udp_socket(struct sockaddr_storage *ss, in_port_t port,
1092380f4f2Sreyk struct protocol *proto)
1102380f4f2Sreyk {
1112380f4f2Sreyk int s = -1, val;
1122380f4f2Sreyk
1132380f4f2Sreyk if (relay_socket_af(ss, port) == -1)
1142380f4f2Sreyk goto bad;
1152380f4f2Sreyk
116b045ffeeSreyk if ((s = socket(ss->ss_family, SOCK_DGRAM | SOCK_NONBLOCK,
117b045ffeeSreyk IPPROTO_UDP)) == -1)
1182380f4f2Sreyk goto bad;
1192380f4f2Sreyk
1202380f4f2Sreyk /*
1212380f4f2Sreyk * Socket options
1222380f4f2Sreyk */
1232380f4f2Sreyk if (proto->tcpflags & TCPFLAG_BUFSIZ) {
1242380f4f2Sreyk val = proto->tcpbufsiz;
1252380f4f2Sreyk if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,
1262380f4f2Sreyk &val, sizeof(val)) == -1)
1272380f4f2Sreyk goto bad;
1282380f4f2Sreyk val = proto->tcpbufsiz;
1292380f4f2Sreyk if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,
1302380f4f2Sreyk &val, sizeof(val)) == -1)
1312380f4f2Sreyk goto bad;
1322380f4f2Sreyk }
1332380f4f2Sreyk
1342380f4f2Sreyk /*
1352380f4f2Sreyk * IP options
1362380f4f2Sreyk */
1372380f4f2Sreyk if (proto->tcpflags & TCPFLAG_IPTTL) {
1382380f4f2Sreyk val = (int)proto->tcpipttl;
139829b7945Sjca switch (ss->ss_family) {
140829b7945Sjca case AF_INET:
1412380f4f2Sreyk if (setsockopt(s, IPPROTO_IP, IP_TTL,
1422380f4f2Sreyk &val, sizeof(val)) == -1)
1432380f4f2Sreyk goto bad;
144829b7945Sjca break;
145829b7945Sjca case AF_INET6:
146829b7945Sjca if (setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
147829b7945Sjca &val, sizeof(val)) == -1)
148829b7945Sjca goto bad;
149829b7945Sjca break;
150829b7945Sjca }
1512380f4f2Sreyk }
1522380f4f2Sreyk if (proto->tcpflags & TCPFLAG_IPMINTTL) {
1532380f4f2Sreyk val = (int)proto->tcpipminttl;
154829b7945Sjca switch (ss->ss_family) {
155829b7945Sjca case AF_INET:
1562380f4f2Sreyk if (setsockopt(s, IPPROTO_IP, IP_MINTTL,
1572380f4f2Sreyk &val, sizeof(val)) == -1)
1582380f4f2Sreyk goto bad;
159829b7945Sjca break;
160829b7945Sjca case AF_INET6:
161829b7945Sjca if (setsockopt(s, IPPROTO_IPV6, IPV6_MINHOPCOUNT,
162829b7945Sjca &val, sizeof(val)) == -1)
163829b7945Sjca goto bad;
164829b7945Sjca break;
165829b7945Sjca }
1662380f4f2Sreyk }
1672380f4f2Sreyk
1682380f4f2Sreyk return (s);
1692380f4f2Sreyk
1702380f4f2Sreyk bad:
1712380f4f2Sreyk if (s != -1)
1722380f4f2Sreyk close(s);
1732380f4f2Sreyk return (-1);
1742380f4f2Sreyk }
1752380f4f2Sreyk
1762380f4f2Sreyk void
relay_udp_response(int fd,short sig,void * arg)177bec706feSreyk relay_udp_response(int fd, short sig, void *arg)
178bec706feSreyk {
17948240b8fSbluhm struct rsession *con = arg;
180bec706feSreyk struct relay *rlay = con->se_relay;
181bec706feSreyk struct protocol *proto = rlay->rl_proto;
182ba83ed70Sreyk void *priv = NULL;
183bec706feSreyk struct sockaddr_storage ss;
184e39620e5Snicm u_int8_t buf[IBUF_READ_SIZE];
185bec706feSreyk ssize_t len;
186bec706feSreyk socklen_t slen;
187bec706feSreyk
188bec706feSreyk if (sig == EV_TIMEOUT) {
189bec706feSreyk relay_udp_timeout(fd, sig, arg);
190bec706feSreyk return;
191bec706feSreyk }
192bec706feSreyk
193ea42f25aSclaudio if (rlay->rl_conf.flags & F_DISABLE)
194bec706feSreyk return;
195bec706feSreyk
196bec706feSreyk slen = sizeof(ss);
197bec706feSreyk if ((len = recvfrom(fd, buf, sizeof(buf), 0,
198bec706feSreyk (struct sockaddr*)&ss, &slen)) < 1)
199bec706feSreyk return;
200bec706feSreyk
201bec706feSreyk /* Parse and validate the packet header */
202bec706feSreyk if (proto->validate != NULL &&
203ba83ed70Sreyk (priv = (*proto->validate)(con, rlay, &ss, buf, len)) == NULL)
204bec706feSreyk return;
205ba83ed70Sreyk
2060be9d00aSbenno relay_close(con, "unknown response", 1);
207ba83ed70Sreyk free(priv);
208bec706feSreyk }
209bec706feSreyk
210bec706feSreyk void
relay_udp_server(int fd,short sig,void * arg)2112380f4f2Sreyk relay_udp_server(int fd, short sig, void *arg)
2122380f4f2Sreyk {
213325f6e14Sreyk struct privsep *ps = env->sc_ps;
21448240b8fSbluhm struct relay *rlay = arg;
2154a5b9b3eSreyk struct protocol *proto = rlay->rl_proto;
216f4a6e73bSreyk struct rsession *con = NULL;
2172380f4f2Sreyk struct ctl_natlook *cnl = NULL;
2182380f4f2Sreyk socklen_t slen;
2192380f4f2Sreyk struct timeval tv;
2202380f4f2Sreyk struct sockaddr_storage ss;
221e39620e5Snicm u_int8_t buf[IBUF_READ_SIZE];
2220dbc5f9eSreyk void *priv = NULL;
2232380f4f2Sreyk ssize_t len;
2242380f4f2Sreyk
225b3ad2344Sreyk event_add(&rlay->rl_ev, NULL);
226b3ad2344Sreyk
227ea42f25aSclaudio if (rlay->rl_conf.flags & F_DISABLE)
2282380f4f2Sreyk return;
2292380f4f2Sreyk
2302380f4f2Sreyk slen = sizeof(ss);
2312380f4f2Sreyk if ((len = recvfrom(fd, buf, sizeof(buf), 0,
2322380f4f2Sreyk (struct sockaddr*)&ss, &slen)) < 1)
2332380f4f2Sreyk return;
2342380f4f2Sreyk
2352380f4f2Sreyk if (proto->validate != NULL &&
236bec706feSreyk (priv = (*proto->validate)(NULL, rlay, &ss, buf, len)) == NULL)
2372380f4f2Sreyk return;
2382380f4f2Sreyk
23948240b8fSbluhm if ((con = calloc(1, sizeof(*con))) == NULL) {
2400dbc5f9eSreyk free(priv);
2412380f4f2Sreyk return;
2420dbc5f9eSreyk }
2432380f4f2Sreyk
2445fce3f02Sreyk /*
2455fce3f02Sreyk * Replace the DNS request Id with a random Id.
2465fce3f02Sreyk */
2470dbc5f9eSreyk con->se_priv = priv;
248f8eb77d7Sthib con->se_in.s = -1;
249f8eb77d7Sthib con->se_out.s = -1;
250f8eb77d7Sthib con->se_in.dst = &con->se_out;
251f8eb77d7Sthib con->se_out.dst = &con->se_in;
252f8eb77d7Sthib con->se_in.con = con;
253f8eb77d7Sthib con->se_out.con = con;
254f8eb77d7Sthib con->se_relay = rlay;
255f8eb77d7Sthib con->se_id = ++relay_conid;
256f8eb77d7Sthib con->se_in.dir = RELAY_DIR_REQUEST;
257f8eb77d7Sthib con->se_out.dir = RELAY_DIR_RESPONSE;
258f8eb77d7Sthib con->se_retry = rlay->rl_conf.dstretry;
259f8eb77d7Sthib con->se_out.port = rlay->rl_conf.dstport;
2602380f4f2Sreyk switch (ss.ss_family) {
2612380f4f2Sreyk case AF_INET:
262f8eb77d7Sthib con->se_in.port = ((struct sockaddr_in *)&ss)->sin_port;
2632380f4f2Sreyk break;
2642380f4f2Sreyk case AF_INET6:
265f8eb77d7Sthib con->se_in.port = ((struct sockaddr_in6 *)&ss)->sin6_port;
2662380f4f2Sreyk break;
2672380f4f2Sreyk }
268fd1841a3Sreyk bcopy(&ss, &con->se_in.ss, sizeof(con->se_in.ss));
269fd1841a3Sreyk
270fd1841a3Sreyk getmonotime(&con->se_tv_start);
271fd1841a3Sreyk bcopy(&con->se_tv_start, &con->se_tv_last, sizeof(con->se_tv_last));
2722380f4f2Sreyk
2732380f4f2Sreyk relay_sessions++;
2744a5b9b3eSreyk SPLAY_INSERT(session_tree, &rlay->rl_sessions, con);
2756e07057bSblambert relay_session_publish(con);
2762380f4f2Sreyk
2772380f4f2Sreyk /* Increment the per-relay session counter */
278325f6e14Sreyk rlay->rl_stats[ps->ps_instance].last++;
2792380f4f2Sreyk
2802380f4f2Sreyk /* Pre-allocate output buffer */
281f8eb77d7Sthib con->se_out.output = evbuffer_new();
282f8eb77d7Sthib if (con->se_out.output == NULL) {
2830be9d00aSbenno relay_close(con, "failed to allocate output buffer", 1);
2842380f4f2Sreyk return;
2852380f4f2Sreyk }
2862380f4f2Sreyk
2872380f4f2Sreyk /* Pre-allocate log buffer */
288c84d6099Sbenno con->se_haslog = 0;
289f8eb77d7Sthib con->se_log = evbuffer_new();
290f8eb77d7Sthib if (con->se_log == NULL) {
2910be9d00aSbenno relay_close(con, "failed to allocate log buffer", 1);
2922380f4f2Sreyk return;
2932380f4f2Sreyk }
2942380f4f2Sreyk
2954a5b9b3eSreyk if (rlay->rl_conf.flags & F_NATLOOK) {
29648240b8fSbluhm if ((cnl = calloc(1, sizeof(*cnl))) == NULL) {
2970be9d00aSbenno relay_close(con, "failed to allocate natlookup", 1);
2982380f4f2Sreyk return;
2992380f4f2Sreyk }
3002380f4f2Sreyk }
3012380f4f2Sreyk
3022380f4f2Sreyk /* Save the received data */
303f8eb77d7Sthib if (evbuffer_add(con->se_out.output, buf, len) == -1) {
3040be9d00aSbenno relay_close(con, "failed to store buffer", 1);
305e24b2b8eSreyk free(cnl);
3062380f4f2Sreyk return;
3072380f4f2Sreyk }
3082380f4f2Sreyk
3098d083e57Sreyk if (cnl != NULL) {
310f8eb77d7Sthib con->se_cnl = cnl;
3112380f4f2Sreyk bzero(cnl, sizeof(*cnl));
3122380f4f2Sreyk cnl->in = -1;
313f8eb77d7Sthib cnl->id = con->se_id;
314325f6e14Sreyk cnl->proc = ps->ps_instance;
315aeda6e0eSreyk cnl->proto = IPPROTO_UDP;
316f8eb77d7Sthib bcopy(&con->se_in.ss, &cnl->src, sizeof(cnl->src));
3174a5b9b3eSreyk bcopy(&rlay->rl_conf.ss, &cnl->dst, sizeof(cnl->dst));
318c2c37c5dSreyk proc_compose(env->sc_ps, PROC_PFE,
319c2c37c5dSreyk IMSG_NATLOOK, cnl, sizeof(*cnl));
3202380f4f2Sreyk
3212380f4f2Sreyk /* Schedule timeout */
322f8eb77d7Sthib evtimer_set(&con->se_ev, relay_natlook, con);
3234a5b9b3eSreyk bcopy(&rlay->rl_conf.timeout, &tv, sizeof(tv));
324f8eb77d7Sthib evtimer_add(&con->se_ev, &tv);
3252380f4f2Sreyk return;
3262380f4f2Sreyk }
3272380f4f2Sreyk
3282380f4f2Sreyk relay_session(con);
3292380f4f2Sreyk }
3302380f4f2Sreyk
3312380f4f2Sreyk void
relay_udp_timeout(int fd,short sig,void * arg)3322380f4f2Sreyk relay_udp_timeout(int fd, short sig, void *arg)
3332380f4f2Sreyk {
33448240b8fSbluhm struct rsession *con = arg;
3352380f4f2Sreyk
3362380f4f2Sreyk if (sig != EV_TIMEOUT)
3372380f4f2Sreyk fatalx("invalid timeout event");
3382380f4f2Sreyk
3390be9d00aSbenno relay_close(con, "udp timeout", 1);
3402380f4f2Sreyk }
3412380f4f2Sreyk
3422380f4f2Sreyk /*
3432380f4f2Sreyk * Domain Name System support
3442380f4f2Sreyk */
3452380f4f2Sreyk
3460dbc5f9eSreyk struct relay_dns_priv {
3470dbc5f9eSreyk u_int16_t dp_inkey;
3480dbc5f9eSreyk u_int16_t dp_outkey;
3490dbc5f9eSreyk };
3500dbc5f9eSreyk
3512380f4f2Sreyk struct relay_dnshdr {
3522380f4f2Sreyk u_int16_t dns_id;
3532380f4f2Sreyk
3542380f4f2Sreyk u_int8_t dns_flags0;
3552380f4f2Sreyk #define DNS_F0_QR 0x80 /* response flag */
3562380f4f2Sreyk #define DNS_F0_OPCODE 0x78 /* message type */
3573a50f0a9Sjmc #define DNS_F0_AA 0x04 /* authoritative answer */
3582380f4f2Sreyk #define DNS_F0_TC 0x02 /* truncated message */
3592380f4f2Sreyk #define DNS_F0_RD 0x01 /* recursion desired */
3602380f4f2Sreyk
3612380f4f2Sreyk u_int8_t dns_flags1;
3622380f4f2Sreyk #define DNS_F1_RA 0x80 /* recursion available */
3632380f4f2Sreyk #define DNS_F1_RES 0x40 /* reserved */
3642380f4f2Sreyk #define DNS_F1_AD 0x20 /* authentic data */
3652380f4f2Sreyk #define DNS_F1_CD 0x10 /* checking disabled */
3662380f4f2Sreyk #define DNS_F1_RCODE 0x0f /* response code */
3672380f4f2Sreyk
3682380f4f2Sreyk u_int16_t dns_qdcount;
3692380f4f2Sreyk u_int16_t dns_ancount;
3702380f4f2Sreyk u_int16_t dns_nscount;
3712380f4f2Sreyk u_int16_t dns_arcount;
3722380f4f2Sreyk } __packed;
3732380f4f2Sreyk
3742380f4f2Sreyk void
relay_dns_log(struct rsession * con,u_int8_t * buf,size_t len)375f4a6e73bSreyk relay_dns_log(struct rsession *con, u_int8_t *buf, size_t len)
3762380f4f2Sreyk {
3772380f4f2Sreyk struct relay_dnshdr *hdr = (struct relay_dnshdr *)buf;
3782380f4f2Sreyk
3790ab1bea4Sreyk /* Validate the header length */
3800ab1bea4Sreyk if (len < sizeof(*hdr)) {
38185a8c65fSreyk log_debug("%s: session %d: short dns packet", __func__,
3820ab1bea4Sreyk con->se_id);
3830ab1bea4Sreyk return;
3840ab1bea4Sreyk }
3850ab1bea4Sreyk
38685a8c65fSreyk log_debug("%s: session %d: %s id 0x%x "
38785a8c65fSreyk "flags 0x%x:0x%x qd %u an %u ns %u ar %u", __func__,
388f8eb77d7Sthib con->se_id,
3892380f4f2Sreyk hdr->dns_flags0 & DNS_F0_QR ? "response" : "request",
3902380f4f2Sreyk ntohs(hdr->dns_id),
3912380f4f2Sreyk hdr->dns_flags0,
3922380f4f2Sreyk hdr->dns_flags1,
3932380f4f2Sreyk ntohs(hdr->dns_qdcount),
3942380f4f2Sreyk ntohs(hdr->dns_ancount),
3952380f4f2Sreyk ntohs(hdr->dns_nscount),
3962380f4f2Sreyk ntohs(hdr->dns_arcount));
3972380f4f2Sreyk }
3982380f4f2Sreyk
3990dbc5f9eSreyk void *
relay_dns_validate(struct rsession * con,struct relay * rlay,struct sockaddr_storage * ss,u_int8_t * buf,size_t len)400f4a6e73bSreyk relay_dns_validate(struct rsession *con, struct relay *rlay,
401bec706feSreyk struct sockaddr_storage *ss, u_int8_t *buf, size_t len)
4022380f4f2Sreyk {
4032380f4f2Sreyk struct relay_dnshdr *hdr = (struct relay_dnshdr *)buf;
404f4a6e73bSreyk struct rsession lookup;
4050dbc5f9eSreyk u_int16_t key;
4060dbc5f9eSreyk struct relay_dns_priv *priv, lpriv;
4072380f4f2Sreyk
4082380f4f2Sreyk /* Validate the header length */
4092380f4f2Sreyk if (len < sizeof(*hdr))
4100dbc5f9eSreyk return (NULL);
4112380f4f2Sreyk
4120dbc5f9eSreyk key = ntohs(hdr->dns_id);
4132380f4f2Sreyk
4142380f4f2Sreyk /*
4152380f4f2Sreyk * Check if the header has the response flag set, otherwise
4162380f4f2Sreyk * return 0 to tell the UDP server to create a new session.
4172380f4f2Sreyk */
4180dbc5f9eSreyk if ((hdr->dns_flags0 & DNS_F0_QR) == 0) {
4190dbc5f9eSreyk priv = malloc(sizeof(struct relay_dns_priv));
4200dbc5f9eSreyk if (priv == NULL)
4210dbc5f9eSreyk return (NULL);
4228661b3ffSreyk priv->dp_inkey = shuffle_generate16(&relay_shuffle);
4230dbc5f9eSreyk priv->dp_outkey = key;
4240dbc5f9eSreyk return ((void *)priv);
4250dbc5f9eSreyk }
4262380f4f2Sreyk
4272380f4f2Sreyk /*
4282380f4f2Sreyk * Lookup if this response is for a known session and if the
4292380f4f2Sreyk * remote host matches the original destination of the request.
4302380f4f2Sreyk */
431bec706feSreyk if (con == NULL) {
4320dbc5f9eSreyk lpriv.dp_inkey = key;
4330dbc5f9eSreyk lookup.se_priv = &lpriv;
4342380f4f2Sreyk if ((con = SPLAY_FIND(session_tree,
435bec706feSreyk &rlay->rl_sessions, &lookup)) != NULL &&
436bec706feSreyk con->se_priv != NULL &&
437f8eb77d7Sthib relay_cmp_af(ss, &con->se_out.ss) == 0)
438bec706feSreyk relay_dns_result(con, buf, len);
439ba83ed70Sreyk } else {
44048240b8fSbluhm priv = con->se_priv;
441ba83ed70Sreyk if (priv == NULL || key != priv->dp_inkey) {
4420be9d00aSbenno relay_close(con, "invalid response", 1);
443ba83ed70Sreyk return (NULL);
444ba83ed70Sreyk }
445bec706feSreyk relay_dns_result(con, buf, len);
446ba83ed70Sreyk }
4472380f4f2Sreyk
4482380f4f2Sreyk /*
4492380f4f2Sreyk * This is not a new session, ignore it in the UDP server.
4502380f4f2Sreyk */
4510dbc5f9eSreyk return (NULL);
4522380f4f2Sreyk }
4532380f4f2Sreyk
4542380f4f2Sreyk int
relay_dns_request(struct rsession * con)455f4a6e73bSreyk relay_dns_request(struct rsession *con)
4562380f4f2Sreyk {
45748240b8fSbluhm struct relay *rlay = con->se_relay;
45848240b8fSbluhm struct relay_dns_priv *priv = con->se_priv;
459f8eb77d7Sthib u_int8_t *buf = EVBUFFER_DATA(con->se_out.output);
460f8eb77d7Sthib size_t len = EVBUFFER_LENGTH(con->se_out.output);
4612380f4f2Sreyk struct relay_dnshdr *hdr;
4622380f4f2Sreyk socklen_t slen;
4632380f4f2Sreyk
4640dbc5f9eSreyk if (buf == NULL || priv == NULL || len < 1)
4652380f4f2Sreyk return (-1);
466871fc12cSreyk if (log_getverbose() > 1)
4670ab1bea4Sreyk relay_dns_log(con, buf, len);
4682380f4f2Sreyk
469fd1841a3Sreyk getmonotime(&con->se_tv_start);
4702380f4f2Sreyk
471416fa9c0Sreyk if (!TAILQ_EMPTY(&rlay->rl_tables)) {
4722380f4f2Sreyk if (relay_from_table(con) != 0)
4732380f4f2Sreyk return (-1);
474f8eb77d7Sthib } else if (con->se_out.ss.ss_family == AF_UNSPEC) {
47538426f8cSreyk bcopy(&rlay->rl_conf.dstss, &con->se_out.ss,
47638426f8cSreyk sizeof(con->se_out.ss));
477f8eb77d7Sthib con->se_out.port = rlay->rl_conf.dstport;
4782380f4f2Sreyk }
4792380f4f2Sreyk
480bec706feSreyk if ((con->se_out.s = relay_udp_socket(&con->se_out.ss,
481bec706feSreyk con->se_out.port, rlay->rl_proto)) == -1)
4822380f4f2Sreyk return (-1);
483f8eb77d7Sthib slen = con->se_out.ss.ss_len;
4842380f4f2Sreyk
4852380f4f2Sreyk hdr = (struct relay_dnshdr *)buf;
4860dbc5f9eSreyk hdr->dns_id = htons(priv->dp_inkey);
4872380f4f2Sreyk
4882380f4f2Sreyk retry:
489bec706feSreyk if (sendto(con->se_out.s, buf, len, 0,
490f8eb77d7Sthib (struct sockaddr *)&con->se_out.ss, slen) == -1) {
491f8eb77d7Sthib if (con->se_retry) {
492f8eb77d7Sthib con->se_retry--;
49385a8c65fSreyk log_debug("%s: session %d: "
49485a8c65fSreyk "forward failed: %s, %s", __func__,
495f8eb77d7Sthib con->se_id, strerror(errno),
496f8eb77d7Sthib con->se_retry ? "next retry" : "last retry");
4972380f4f2Sreyk goto retry;
4982380f4f2Sreyk }
49985a8c65fSreyk log_debug("%s: session %d: forward failed: %s", __func__,
500f8eb77d7Sthib con->se_id, strerror(errno));
5012380f4f2Sreyk return (-1);
5022380f4f2Sreyk }
5032380f4f2Sreyk
504bec706feSreyk event_again(&con->se_ev, con->se_out.s, EV_TIMEOUT|EV_READ,
505b3ad2344Sreyk relay_udp_response, &con->se_tv_start, &rlay->rl_conf.timeout, con);
5062380f4f2Sreyk
5072380f4f2Sreyk return (0);
5082380f4f2Sreyk }
5092380f4f2Sreyk
5102380f4f2Sreyk void
relay_dns_result(struct rsession * con,u_int8_t * buf,size_t len)511f4a6e73bSreyk relay_dns_result(struct rsession *con, u_int8_t *buf, size_t len)
5122380f4f2Sreyk {
51348240b8fSbluhm struct relay *rlay = con->se_relay;
51448240b8fSbluhm struct relay_dns_priv *priv = con->se_priv;
5152380f4f2Sreyk struct relay_dnshdr *hdr;
5162380f4f2Sreyk socklen_t slen;
5172380f4f2Sreyk
5180dbc5f9eSreyk if (priv == NULL)
519efc39811Sbenno fatalx("%s: response to invalid session", __func__);
5200dbc5f9eSreyk
521871fc12cSreyk if (log_getverbose() > 1)
5220ab1bea4Sreyk relay_dns_log(con, buf, len);
5232380f4f2Sreyk
5242380f4f2Sreyk /*
5252380f4f2Sreyk * Replace the random DNS request Id with the original Id
5262380f4f2Sreyk */
5272380f4f2Sreyk hdr = (struct relay_dnshdr *)buf;
5280dbc5f9eSreyk hdr->dns_id = htons(priv->dp_outkey);
5292380f4f2Sreyk
530f8eb77d7Sthib slen = con->se_out.ss.ss_len;
5314a5b9b3eSreyk if (sendto(rlay->rl_s, buf, len, 0,
532f8eb77d7Sthib (struct sockaddr *)&con->se_in.ss, slen) == -1) {
5330be9d00aSbenno relay_close(con, "response failed", 1);
5342380f4f2Sreyk return;
5352380f4f2Sreyk }
5362380f4f2Sreyk
5370be9d00aSbenno relay_close(con, "session closed", 0);
5382380f4f2Sreyk }
5392380f4f2Sreyk
5402380f4f2Sreyk int
relay_dns_cmp(struct rsession * a,struct rsession * b)541f4a6e73bSreyk relay_dns_cmp(struct rsession *a, struct rsession *b)
5422380f4f2Sreyk {
54348240b8fSbluhm struct relay_dns_priv *ap = a->se_priv;
54448240b8fSbluhm struct relay_dns_priv *bp = b->se_priv;
5450dbc5f9eSreyk
5460dbc5f9eSreyk if (ap == NULL || bp == NULL)
547efc39811Sbenno fatalx("%s: invalid session", __func__);
5480dbc5f9eSreyk
5490dbc5f9eSreyk return (memcmp(&ap->dp_inkey, &bp->dp_inkey, sizeof(u_int16_t)));
5502380f4f2Sreyk }
551