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 |
|
|
} |