]>
Commit | Line | Data |
---|---|---|
9e4abcb5 SK |
1 | /* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley |
2 | ||
3 | This program is free software; you can redistribute it and/or modify | |
4 | it under the terms of the GNU General Public License as published by | |
5 | the Free Software Foundation; version 2 dated June, 1991. | |
6 | ||
7 | This program is distributed in the hope that it will be useful, | |
8 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | GNU General Public License for more details. | |
11 | */ | |
12 | ||
13 | /* Author's email: simon@thekelleys.org.uk */ | |
14 | ||
15 | #include "dnsmasq.h" | |
16 | ||
17 | static struct frec *frec_list; | |
18 | ||
19 | static struct frec *get_new_frec(time_t now); | |
20 | static struct frec *lookup_frec(unsigned short id); | |
21 | static struct frec *lookup_frec_by_sender(unsigned short id, | |
22 | union mysockaddr *addr); | |
23 | static unsigned short get_id(void); | |
24 | ||
25 | /* May be called more than once. */ | |
26 | void forward_init(int first) | |
27 | { | |
28 | struct frec *f; | |
29 | ||
30 | if (first) | |
31 | frec_list = NULL; | |
32 | for (f = frec_list; f; f = f->next) | |
33 | f->new_id = 0; | |
34 | } | |
35 | ||
44a2a316 SK |
36 | /* Send a UDP packet with it's source address set as "source" |
37 | unless nowild is true, when we just send it with the kernel default */ | |
38 | static void send_from(int fd, int nowild, char *packet, int len, | |
39 | union mysockaddr *to, struct all_addr *source) | |
9e4abcb5 | 40 | { |
44a2a316 SK |
41 | struct msghdr msg; |
42 | struct iovec iov[1]; | |
43 | struct cmsghdr *cmptr; | |
44 | union { | |
45 | struct cmsghdr align; /* this ensures alignment */ | |
46 | #if defined(IP_PKTINFO) | |
47 | char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; | |
48 | #elif defined(IP_SENDSRCADDR) | |
49 | char control[CMSG_SPACE(sizeof(struct in_addr))]; | |
50 | #endif | |
51 | #ifdef HAVE_IPV6 | |
52 | char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; | |
53 | #endif | |
54 | } control_u; | |
55 | ||
56 | iov[0].iov_base = packet; | |
57 | iov[0].iov_len = len; | |
58 | ||
59 | if (nowild) | |
60 | { | |
61 | msg.msg_control = NULL; | |
62 | msg.msg_controllen = 0; | |
63 | } | |
64 | else | |
65 | { | |
66 | msg.msg_control = &control_u; | |
67 | msg.msg_controllen = sizeof(control_u); | |
68 | } | |
69 | msg.msg_flags = 0; | |
70 | msg.msg_name = to; | |
71 | msg.msg_namelen = sa_len(to); | |
72 | msg.msg_iov = iov; | |
73 | msg.msg_iovlen = 1; | |
74 | ||
75 | cmptr = CMSG_FIRSTHDR(&msg); | |
9e4abcb5 | 76 | |
44a2a316 SK |
77 | #if defined(IP_PKTINFO) |
78 | if (!nowild && to->sa.sa_family == AF_INET) | |
79 | { | |
80 | struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr); | |
81 | pkt->ipi_ifindex = 0; | |
82 | pkt->ipi_spec_dst = source->addr.addr4; | |
83 | msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); | |
84 | cmptr->cmsg_level = SOL_IP; | |
85 | cmptr->cmsg_type = IP_PKTINFO; | |
86 | } | |
87 | #elif defined(IP_SENDSRCADDR) | |
88 | if (!nowild && to->sa.sa_family == AF_INET) | |
89 | { | |
90 | struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr); | |
91 | *a = source->addr.addr4; | |
92 | msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); | |
93 | cmptr->cmsg_level = IPPROTO_IP; | |
94 | cmptr->cmsg_type = IP_SENDSRCADDR; | |
95 | } | |
96 | #endif | |
97 | ||
98 | #ifdef HAVE_IPV6 | |
99 | if (!nowild && to->sa.sa_family == AF_INET6) | |
100 | { | |
101 | struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr); | |
102 | pkt->ipi6_ifindex = 0; | |
103 | pkt->ipi6_addr = source->addr.addr6; | |
104 | msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); | |
105 | cmptr->cmsg_type = IPV6_PKTINFO; | |
106 | cmptr->cmsg_level = IPV6_LEVEL; | |
44a2a316 SK |
107 | } |
108 | #endif | |
109 | ||
110 | sendmsg(fd, &msg, 0); | |
9e4abcb5 | 111 | } |
44a2a316 SK |
112 | |
113 | ||
9e4abcb5 | 114 | /* returns new last_server */ |
44a2a316 SK |
115 | static struct server *forward_query(int udpfd, union mysockaddr *udpaddr, |
116 | struct all_addr *dst_addr, HEADER *header, | |
117 | int plen, unsigned int options, char *dnamebuff, | |
118 | struct server *servers, struct server *last_server, | |
119 | time_t now, unsigned long local_ttl) | |
9e4abcb5 SK |
120 | { |
121 | struct frec *forward; | |
122 | char *domain = NULL; | |
123 | int type = 0; | |
124 | struct server *serv; | |
125 | struct all_addr *addrp = NULL; | |
126 | unsigned short flags = 0; | |
127 | unsigned short gotname = extract_request(header, (unsigned int)plen, dnamebuff); | |
128 | ||
129 | /* may be recursion not speced or no servers available. */ | |
130 | if (!header->rd || !servers) | |
131 | forward = NULL; | |
132 | else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr))) | |
133 | { | |
134 | /* retry on existing query, send to next server */ | |
135 | domain = forward->sentto->domain; | |
136 | type = forward->sentto->flags & SERV_TYPE; | |
137 | if (!(forward->sentto = forward->sentto->next)) | |
138 | forward->sentto = servers; /* at end of list, recycle */ | |
139 | header->id = htons(forward->new_id); | |
140 | } | |
141 | else | |
142 | { | |
143 | if (gotname) | |
144 | { | |
145 | /* If the query ends in the domain in one of our servers, set | |
146 | domain to point to that name. We find the largest match to allow both | |
147 | domain.org and sub.domain.org to exist. */ | |
148 | ||
149 | unsigned int namelen = strlen(dnamebuff); | |
150 | unsigned int matchlen = 0; | |
151 | ||
152 | for (serv=servers; serv; serv=serv->next) | |
153 | /* domain matches take priority over NODOTS matches */ | |
154 | if ((serv->flags & SERV_FOR_NODOTS) && type != SERV_HAS_DOMAIN && !strchr(dnamebuff, '.')) | |
155 | { | |
156 | if (serv->flags & SERV_LITERAL_ADDRESS) | |
157 | { | |
158 | /* flags gets set if server is in fact an answer */ | |
159 | unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; | |
160 | if (sflag & gotname) /* only OK if addrfamily == query */ | |
161 | { | |
162 | type = SERV_FOR_NODOTS; | |
163 | flags = sflag; | |
164 | if (serv->addr.sa.sa_family == AF_INET) | |
165 | addrp = (struct all_addr *)&serv->addr.in.sin_addr; | |
166 | #ifdef HAVE_IPV6 | |
167 | else | |
168 | addrp = (struct all_addr *)&serv->addr.in6.sin6_addr; | |
169 | #endif | |
170 | } | |
171 | } | |
172 | else | |
173 | flags = 0; | |
174 | } | |
175 | else if (serv->flags & SERV_HAS_DOMAIN) | |
176 | { | |
177 | unsigned int domainlen = strlen(serv->domain); | |
178 | if (namelen >= domainlen && | |
179 | hostname_isequal(dnamebuff + namelen - domainlen, serv->domain) && | |
a222641c | 180 | domainlen >= matchlen) |
9e4abcb5 SK |
181 | { |
182 | if (serv->flags & SERV_LITERAL_ADDRESS) | |
183 | { /* flags gets set if server is in fact an answer */ | |
184 | unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; | |
44a2a316 | 185 | if ((sflag | F_QUERY ) & gotname) /* only OK if addrfamily == query */ |
9e4abcb5 SK |
186 | { |
187 | type = SERV_HAS_DOMAIN; | |
44a2a316 | 188 | flags = gotname; |
9e4abcb5 SK |
189 | domain = serv->domain; |
190 | matchlen = domainlen; | |
191 | if (serv->addr.sa.sa_family == AF_INET) | |
192 | addrp = (struct all_addr *)&serv->addr.in.sin_addr; | |
193 | #ifdef HAVE_IPV6 | |
194 | else | |
195 | addrp = (struct all_addr *)&serv->addr.in6.sin6_addr; | |
196 | #endif | |
197 | } | |
198 | } | |
199 | else | |
200 | { | |
201 | flags = 0; /* may be better match from previous literal */ | |
202 | domain = serv->domain; | |
203 | matchlen = domainlen; | |
1ab84e2f | 204 | type = SERV_HAS_DOMAIN; |
9e4abcb5 SK |
205 | } |
206 | } | |
207 | } | |
208 | } | |
209 | ||
210 | if (flags) /* flags set here means a literal found */ | |
44a2a316 SK |
211 | { |
212 | if (flags & F_QUERY) | |
213 | log_query(F_CONFIG | F_FORWARD | F_NEG, dnamebuff, NULL); | |
214 | else | |
215 | log_query(F_CONFIG | F_FORWARD | flags, dnamebuff, addrp); | |
216 | } | |
9e4abcb5 SK |
217 | else |
218 | { | |
219 | /* we may by policy not forward names without a domain part */ | |
220 | if (gotname && (options & OPT_NODOTS_LOCAL) && !strchr(dnamebuff, '.')) | |
221 | flags = F_NXDOMAIN; | |
222 | else if (!(forward = get_new_frec(now))) | |
223 | /* table full - server failure. */ | |
224 | flags = F_NEG; | |
225 | } | |
226 | ||
227 | if (forward) | |
228 | { | |
229 | /* In strict_order mode, or when using domain specific servers | |
230 | always try servers in the order specified in resolv.conf, | |
231 | otherwise, use the one last known to work. */ | |
232 | ||
233 | if (type != 0 || (options & OPT_ORDER)) | |
234 | forward->sentto = servers; | |
235 | else | |
236 | forward->sentto = last_server; | |
237 | ||
238 | forward->source = *udpaddr; | |
44a2a316 | 239 | forward->dest = *dst_addr; |
9e4abcb5 SK |
240 | forward->new_id = get_id(); |
241 | forward->fd = udpfd; | |
242 | forward->orig_id = ntohs(header->id); | |
243 | header->id = htons(forward->new_id); | |
244 | } | |
245 | } | |
246 | ||
247 | /* check for send errors here (no route to host) | |
248 | if we fail to send to all nameservers, send back an error | |
249 | packet straight away (helps modem users when offline) */ | |
250 | ||
251 | if (!flags && forward) | |
252 | { | |
253 | struct server *firstsentto = forward->sentto; | |
254 | ||
255 | while (1) | |
256 | { | |
257 | int logflags = 0; | |
258 | ||
259 | if (forward->sentto->addr.sa.sa_family == AF_INET) | |
260 | { | |
261 | logflags = F_SERVER | F_IPV4 | F_FORWARD; | |
262 | addrp = (struct all_addr *)&forward->sentto->addr.in.sin_addr; | |
263 | } | |
264 | #ifdef HAVE_IPV6 | |
265 | else | |
266 | { | |
267 | logflags = F_SERVER | F_IPV6 | F_FORWARD; | |
268 | addrp = (struct all_addr *)&forward->sentto->addr.in6.sin6_addr; | |
269 | } | |
270 | #endif | |
271 | /* only send to servers dealing with our domain. | |
272 | domain may be NULL, in which case server->domain | |
273 | must be NULL also. */ | |
274 | ||
275 | if (type == (forward->sentto->flags & SERV_TYPE) && | |
276 | (type != SERV_HAS_DOMAIN || hostname_isequal(domain, forward->sentto->domain))) | |
277 | { | |
278 | if (forward->sentto->flags & SERV_NO_ADDR) | |
279 | flags = F_NOERR; /* NULL servers are OK. */ | |
280 | else if (!(forward->sentto->flags & SERV_LITERAL_ADDRESS) && | |
281 | sendto(forward->sentto->sfd->fd, (char *)header, plen, 0, | |
282 | &forward->sentto->addr.sa, | |
283 | sa_len(&forward->sentto->addr)) != -1) | |
284 | { | |
285 | log_query(logflags, gotname ? dnamebuff : "query", addrp); | |
286 | /* for no-domain, don't update last_server */ | |
287 | return domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers); | |
288 | } | |
289 | } | |
290 | ||
291 | if (!(forward->sentto = forward->sentto->next)) | |
292 | forward->sentto = servers; | |
293 | ||
294 | /* check if we tried all without success */ | |
295 | if (forward->sentto == firstsentto) | |
296 | break; | |
297 | } | |
298 | ||
299 | /* could not send on, prepare to return */ | |
300 | header->id = htons(forward->orig_id); | |
301 | forward->new_id = 0; /* cancel */ | |
302 | } | |
303 | ||
304 | /* could not send on, return empty answer or address if known for whole domain */ | |
305 | plen = setup_reply(header, (unsigned int)plen, addrp, flags, local_ttl); | |
44a2a316 SK |
306 | send_from(udpfd, options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr); |
307 | ||
9e4abcb5 SK |
308 | if (flags & (F_NOERR | F_NXDOMAIN)) |
309 | log_query(F_CONFIG | F_FORWARD | F_NEG | gotname | (flags & F_NXDOMAIN), dnamebuff, NULL); | |
310 | ||
311 | return last_server; | |
312 | } | |
313 | ||
314 | /* returns new last_server */ | |
315 | struct server *reply_query(int fd, int options, char *packet, time_t now, | |
1cff166d SK |
316 | char *dnamebuff, struct server *last_server, |
317 | struct bogus_addr *bogus_nxdomain, struct doctor *doctors) | |
9e4abcb5 SK |
318 | { |
319 | /* packet from peer server, extract data for cache, and send to | |
320 | original requester */ | |
321 | struct frec *forward; | |
322 | HEADER *header; | |
323 | int n = recv(fd, packet, PACKETSZ, 0); | |
324 | ||
325 | header = (HEADER *)packet; | |
326 | if (n >= (int)sizeof(HEADER) && header->qr) | |
327 | { | |
328 | if ((forward = lookup_frec(ntohs(header->id)))) | |
329 | { | |
330 | if (header->rcode == NOERROR || header->rcode == NXDOMAIN) | |
331 | { | |
332 | if (!forward->sentto->domain) | |
333 | last_server = forward->sentto; /* known good */ | |
334 | if (header->opcode == QUERY) | |
335 | { | |
336 | if (!(bogus_nxdomain && | |
337 | header->rcode == NOERROR && | |
338 | check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now))) | |
339 | { | |
340 | if (header->rcode == NOERROR && ntohs(header->ancount) != 0) | |
1cff166d | 341 | extract_addresses(header, (unsigned int)n, dnamebuff, now, doctors); |
9e4abcb5 SK |
342 | else if (!(options & OPT_NO_NEG)) |
343 | extract_neg_addrs(header, (unsigned int)n, dnamebuff, now); | |
344 | } | |
345 | } | |
346 | } | |
347 | header->id = htons(forward->orig_id); | |
348 | /* There's no point returning an upstream reply marked as truncated, | |
349 | since that will prod the resolver into moving to TCP - which we | |
350 | don't support. */ | |
351 | header->tc = 0; /* goodbye truncate */ | |
44a2a316 | 352 | send_from(forward->fd, options & OPT_NOWILD, packet, n, &forward->source, &forward->dest); |
9e4abcb5 SK |
353 | forward->new_id = 0; /* cancel */ |
354 | } | |
355 | } | |
356 | ||
357 | return last_server; | |
358 | } | |
44a2a316 SK |
359 | |
360 | struct server *receive_query(struct listener *listen, char *packet, char *mxname, | |
361 | char *mxtarget, unsigned int options, time_t now, | |
362 | unsigned long local_ttl, char *namebuff, | |
363 | struct iname *names, struct iname *addrs, struct iname *except, | |
364 | struct server *last_server, struct server *servers) | |
365 | { | |
366 | HEADER *header = (HEADER *)packet; | |
367 | union mysockaddr source_addr; | |
368 | struct iname *tmp; | |
369 | struct all_addr dst_addr; | |
8a911ccc | 370 | int m, n, if_index = 0; |
44a2a316 SK |
371 | struct iovec iov[1]; |
372 | struct msghdr msg; | |
373 | struct cmsghdr *cmptr; | |
44a2a316 SK |
374 | union { |
375 | struct cmsghdr align; /* this ensures alignment */ | |
376 | #ifdef HAVE_IPV6 | |
377 | char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; | |
378 | #endif | |
379 | #if defined(IP_PKTINFO) | |
380 | char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; | |
381 | #elif defined(IP_RECVDSTADDR) | |
382 | char control[CMSG_SPACE(sizeof(struct in_addr)) + | |
383 | CMSG_SPACE(sizeof(struct sockaddr_dl))]; | |
384 | #endif | |
385 | } control_u; | |
386 | ||
387 | iov[0].iov_base = packet; | |
388 | iov[0].iov_len = PACKETSZ; | |
389 | ||
390 | msg.msg_control = control_u.control; | |
391 | msg.msg_controllen = sizeof(control_u); | |
392 | msg.msg_flags = 0; | |
393 | msg.msg_name = &source_addr; | |
394 | msg.msg_namelen = sizeof(source_addr); | |
395 | msg.msg_iov = iov; | |
396 | msg.msg_iovlen = 1; | |
397 | ||
398 | n = recvmsg(listen->fd, &msg, 0); | |
399 | ||
400 | source_addr.sa.sa_family = listen->family; | |
401 | #ifdef HAVE_IPV6 | |
402 | if (listen->family == AF_INET6) | |
403 | source_addr.in6.sin6_flowinfo = htonl(0); | |
404 | #endif | |
405 | ||
406 | if (!(options & OPT_NOWILD) && msg.msg_controllen < sizeof(struct cmsghdr)) | |
407 | return last_server; | |
408 | ||
409 | #if defined(IP_PKTINFO) | |
410 | if (!(options & OPT_NOWILD) && listen->family == AF_INET) | |
411 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
412 | if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) | |
413 | { | |
414 | dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst; | |
8a911ccc | 415 | if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex; |
44a2a316 SK |
416 | } |
417 | #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) | |
418 | if (!(options & OPT_NOWILD) && listen->family == AF_INET) | |
419 | { | |
420 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
421 | if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) | |
8a911ccc | 422 | dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr)); |
44a2a316 | 423 | else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) |
8a911ccc | 424 | if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index; |
44a2a316 SK |
425 | } |
426 | #endif | |
427 | ||
428 | #ifdef HAVE_IPV6 | |
429 | if (!(options & OPT_NOWILD) && listen->family == AF_INET6) | |
430 | { | |
431 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
432 | if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO) | |
433 | { | |
434 | dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr; | |
8a911ccc | 435 | if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex; |
44a2a316 SK |
436 | } |
437 | } | |
438 | #endif | |
439 | ||
440 | if (n < (int)sizeof(HEADER) || header->qr) | |
441 | return last_server; | |
442 | ||
443 | /* enforce available interface configuration */ | |
444 | if (!(options & OPT_NOWILD)) | |
445 | { | |
8a911ccc SK |
446 | struct ifreq ifr; |
447 | ||
448 | if (if_index == 0) | |
44a2a316 SK |
449 | return last_server; |
450 | ||
8a911ccc SK |
451 | if (except || names) |
452 | { | |
453 | #ifdef SIOCGIFNAME | |
454 | ifr.ifr_ifindex = if_index; | |
455 | if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1) | |
456 | return last_server; | |
457 | #else | |
458 | if (!if_indextoname(if_index, ifr.ifr_name)) | |
459 | return last_server; | |
460 | #endif | |
461 | } | |
462 | ||
44a2a316 | 463 | for (tmp = except; tmp; tmp = tmp->next) |
8a911ccc | 464 | if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0)) |
44a2a316 | 465 | return last_server; |
9e4abcb5 | 466 | |
44a2a316 SK |
467 | if (names || addrs) |
468 | { | |
469 | for (tmp = names; tmp; tmp = tmp->next) | |
8a911ccc | 470 | if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0)) |
44a2a316 SK |
471 | break; |
472 | if (!tmp) | |
473 | for (tmp = addrs; tmp; tmp = tmp->next) | |
474 | if (tmp->addr.sa.sa_family == listen->family) | |
475 | { | |
476 | if (tmp->addr.sa.sa_family == AF_INET && | |
477 | tmp->addr.in.sin_addr.s_addr == dst_addr.addr.addr4.s_addr) | |
478 | break; | |
479 | #ifdef HAVE_IPV6 | |
480 | else if (tmp->addr.sa.sa_family == AF_INET6 && | |
481 | memcmp(&tmp->addr.in6.sin6_addr, | |
482 | &dst_addr.addr.addr6, | |
483 | sizeof(struct in6_addr)) == 0) | |
484 | break; | |
485 | #endif | |
486 | } | |
487 | if (!tmp) | |
488 | return last_server; | |
489 | } | |
490 | } | |
491 | ||
492 | if (extract_request(header, (unsigned int)n, namebuff)) | |
493 | { | |
494 | if (listen->family == AF_INET) | |
495 | log_query(F_QUERY | F_IPV4 | F_FORWARD, namebuff, | |
496 | (struct all_addr *)&source_addr.in.sin_addr); | |
497 | #ifdef HAVE_IPV6 | |
498 | else | |
499 | log_query(F_QUERY | F_IPV6 | F_FORWARD, namebuff, | |
500 | (struct all_addr *)&source_addr.in6.sin6_addr); | |
501 | #endif | |
502 | } | |
503 | ||
504 | m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, | |
505 | mxname, mxtarget, options, now, local_ttl, namebuff); | |
506 | if (m >= 1) | |
507 | send_from(listen->fd, options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr); | |
508 | else | |
509 | last_server = forward_query(listen->fd, &source_addr, &dst_addr, | |
510 | header, n, options, namebuff, servers, | |
511 | last_server, now, local_ttl); | |
512 | return last_server; | |
513 | } | |
514 | ||
9e4abcb5 SK |
515 | static struct frec *get_new_frec(time_t now) |
516 | { | |
517 | struct frec *f = frec_list, *oldest = NULL; | |
518 | time_t oldtime = now; | |
519 | int count = 0; | |
520 | static time_t warntime = 0; | |
521 | ||
522 | while (f) | |
523 | { | |
524 | if (f->new_id == 0) | |
525 | { | |
526 | f->time = now; | |
527 | return f; | |
528 | } | |
529 | ||
530 | if (difftime(f->time, oldtime) <= 0) | |
531 | { | |
532 | oldtime = f->time; | |
533 | oldest = f; | |
534 | } | |
535 | ||
536 | count++; | |
537 | f = f->next; | |
538 | } | |
539 | ||
540 | /* can't find empty one, use oldest if there is one | |
541 | and it's older than timeout */ | |
542 | if (oldest && difftime(now, oldtime) > TIMEOUT) | |
543 | { | |
544 | oldest->time = now; | |
545 | return oldest; | |
546 | } | |
547 | ||
548 | if (count > FTABSIZ) | |
549 | { /* limit logging rate so syslog isn't DOSed either */ | |
550 | if (!warntime || difftime(now, warntime) > LOGRATE) | |
551 | { | |
552 | warntime = now; | |
553 | syslog(LOG_WARNING, "forwarding table overflow: check for server loops."); | |
554 | } | |
555 | return NULL; | |
556 | } | |
557 | ||
558 | if ((f = (struct frec *)malloc(sizeof(struct frec)))) | |
559 | { | |
560 | f->next = frec_list; | |
561 | f->time = now; | |
562 | frec_list = f; | |
563 | } | |
564 | return f; /* OK if malloc fails and this is NULL */ | |
565 | } | |
566 | ||
567 | static struct frec *lookup_frec(unsigned short id) | |
568 | { | |
569 | struct frec *f; | |
570 | ||
571 | for(f = frec_list; f; f = f->next) | |
572 | if (f->new_id == id) | |
573 | return f; | |
574 | ||
575 | return NULL; | |
576 | } | |
577 | ||
578 | static struct frec *lookup_frec_by_sender(unsigned short id, | |
579 | union mysockaddr *addr) | |
580 | { | |
581 | struct frec *f; | |
582 | ||
583 | for(f = frec_list; f; f = f->next) | |
584 | if (f->new_id && | |
585 | f->orig_id == id && | |
586 | sockaddr_isequal(&f->source, addr)) | |
587 | return f; | |
588 | ||
589 | return NULL; | |
590 | } | |
591 | ||
592 | ||
593 | /* return unique random ids between 1 and 65535 */ | |
594 | static unsigned short get_id(void) | |
595 | { | |
596 | unsigned short ret = 0; | |
597 | ||
598 | while (ret == 0) | |
599 | { | |
600 | ret = rand16(); | |
601 | ||
602 | /* scrap ids already in use */ | |
603 | if ((ret != 0) && lookup_frec(ret)) | |
604 | ret = 0; | |
605 | } | |
606 | ||
607 | return ret; | |
608 | } | |
609 | ||
610 | ||
611 | ||
612 | ||
613 |