]>
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; | |
de37951c | 123 | int forwardall = 0, type = 0; |
9e4abcb5 SK |
124 | struct all_addr *addrp = NULL; |
125 | unsigned short flags = 0; | |
126 | unsigned short gotname = extract_request(header, (unsigned int)plen, dnamebuff); | |
de37951c SK |
127 | struct server *start = NULL; |
128 | ||
9e4abcb5 SK |
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 | { | |
de37951c | 134 | /* retry on existing query, send to all available servers */ |
9e4abcb5 | 135 | domain = forward->sentto->domain; |
de37951c SK |
136 | if (!(options & OPT_ORDER)) |
137 | { | |
138 | forwardall = 1; | |
139 | last_server = NULL; | |
140 | } | |
9e4abcb5 | 141 | type = forward->sentto->flags & SERV_TYPE; |
de37951c SK |
142 | if (!(start = forward->sentto->next)) |
143 | start = servers; /* at end of list, recycle */ | |
9e4abcb5 SK |
144 | header->id = htons(forward->new_id); |
145 | } | |
146 | else | |
147 | { | |
148 | if (gotname) | |
149 | { | |
150 | /* If the query ends in the domain in one of our servers, set | |
151 | domain to point to that name. We find the largest match to allow both | |
152 | domain.org and sub.domain.org to exist. */ | |
153 | ||
154 | unsigned int namelen = strlen(dnamebuff); | |
155 | unsigned int matchlen = 0; | |
de37951c SK |
156 | struct server *serv; |
157 | ||
9e4abcb5 SK |
158 | for (serv=servers; serv; serv=serv->next) |
159 | /* domain matches take priority over NODOTS matches */ | |
160 | if ((serv->flags & SERV_FOR_NODOTS) && type != SERV_HAS_DOMAIN && !strchr(dnamebuff, '.')) | |
161 | { | |
de37951c SK |
162 | unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; |
163 | type = SERV_FOR_NODOTS; | |
164 | flags = 0; | |
165 | if ((serv->flags & SERV_LITERAL_ADDRESS) && (sflag & gotname)) | |
9e4abcb5 | 166 | { |
de37951c SK |
167 | flags = sflag; |
168 | if (serv->addr.sa.sa_family == AF_INET) | |
169 | addrp = (struct all_addr *)&serv->addr.in.sin_addr; | |
9e4abcb5 | 170 | #ifdef HAVE_IPV6 |
de37951c SK |
171 | else |
172 | addrp = (struct all_addr *)&serv->addr.in6.sin6_addr; | |
9e4abcb5 | 173 | #endif |
9e4abcb5 | 174 | } |
9e4abcb5 SK |
175 | } |
176 | else if (serv->flags & SERV_HAS_DOMAIN) | |
177 | { | |
178 | unsigned int domainlen = strlen(serv->domain); | |
179 | if (namelen >= domainlen && | |
180 | hostname_isequal(dnamebuff + namelen - domainlen, serv->domain) && | |
a222641c | 181 | domainlen >= matchlen) |
9e4abcb5 | 182 | { |
de37951c SK |
183 | unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; |
184 | type = SERV_HAS_DOMAIN; | |
185 | domain = serv->domain; | |
186 | matchlen = domainlen; | |
187 | flags = 0; | |
188 | if ((serv->flags & SERV_LITERAL_ADDRESS) && ((sflag | F_QUERY ) & gotname)) | |
189 | { | |
190 | flags = gotname; | |
191 | if (serv->addr.sa.sa_family == AF_INET) | |
192 | addrp = (struct all_addr *)&serv->addr.in.sin_addr; | |
9e4abcb5 | 193 | #ifdef HAVE_IPV6 |
de37951c SK |
194 | else |
195 | addrp = (struct all_addr *)&serv->addr.in6.sin6_addr; | |
9e4abcb5 | 196 | #endif |
9e4abcb5 SK |
197 | } |
198 | } | |
199 | } | |
200 | } | |
201 | ||
202 | if (flags) /* flags set here means a literal found */ | |
44a2a316 SK |
203 | { |
204 | if (flags & F_QUERY) | |
205 | log_query(F_CONFIG | F_FORWARD | F_NEG, dnamebuff, NULL); | |
206 | else | |
207 | log_query(F_CONFIG | F_FORWARD | flags, dnamebuff, addrp); | |
208 | } | |
9e4abcb5 SK |
209 | else |
210 | { | |
211 | /* we may by policy not forward names without a domain part */ | |
212 | if (gotname && (options & OPT_NODOTS_LOCAL) && !strchr(dnamebuff, '.')) | |
213 | flags = F_NXDOMAIN; | |
214 | else if (!(forward = get_new_frec(now))) | |
215 | /* table full - server failure. */ | |
216 | flags = F_NEG; | |
217 | } | |
218 | ||
219 | if (forward) | |
220 | { | |
221 | /* In strict_order mode, or when using domain specific servers | |
222 | always try servers in the order specified in resolv.conf, | |
223 | otherwise, use the one last known to work. */ | |
224 | ||
225 | if (type != 0 || (options & OPT_ORDER)) | |
de37951c SK |
226 | start = servers; |
227 | else if (!(start = last_server)) | |
228 | { | |
229 | start = servers; | |
230 | forwardall = 1; | |
231 | } | |
232 | ||
9e4abcb5 | 233 | forward->source = *udpaddr; |
44a2a316 | 234 | forward->dest = *dst_addr; |
9e4abcb5 SK |
235 | forward->new_id = get_id(); |
236 | forward->fd = udpfd; | |
237 | forward->orig_id = ntohs(header->id); | |
238 | header->id = htons(forward->new_id); | |
239 | } | |
240 | } | |
241 | ||
242 | /* check for send errors here (no route to host) | |
243 | if we fail to send to all nameservers, send back an error | |
244 | packet straight away (helps modem users when offline) */ | |
245 | ||
246 | if (!flags && forward) | |
247 | { | |
de37951c SK |
248 | struct server *firstsentto = start; |
249 | int forwarded = 0; | |
250 | ||
9e4abcb5 SK |
251 | while (1) |
252 | { | |
9e4abcb5 SK |
253 | /* only send to servers dealing with our domain. |
254 | domain may be NULL, in which case server->domain | |
255 | must be NULL also. */ | |
256 | ||
de37951c SK |
257 | if (type == (start->flags & SERV_TYPE) && |
258 | (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain))) | |
9e4abcb5 | 259 | { |
de37951c | 260 | if (start->flags & SERV_NO_ADDR) |
9e4abcb5 | 261 | flags = F_NOERR; /* NULL servers are OK. */ |
de37951c SK |
262 | else if (!(start->flags & SERV_LITERAL_ADDRESS) && |
263 | sendto(start->sfd->fd, (char *)header, plen, 0, | |
264 | &start->addr.sa, | |
265 | sa_len(&start->addr)) != -1) | |
9e4abcb5 | 266 | { |
de37951c SK |
267 | if (!gotname) |
268 | strcpy(dnamebuff, "query"); | |
269 | if (start->addr.sa.sa_family == AF_INET) | |
270 | log_query(F_SERVER | F_IPV4 | F_FORWARD, dnamebuff, | |
271 | (struct all_addr *)&start->addr.in.sin_addr); | |
272 | #ifdef HAVE_IPV6 | |
273 | else | |
274 | log_query(F_SERVER | F_IPV6 | F_FORWARD, dnamebuff, | |
275 | (struct all_addr *)&start->addr.in6.sin6_addr); | |
276 | #endif | |
277 | forwarded = 1; | |
278 | forward->sentto = start; | |
279 | if (!forwardall) | |
280 | break; | |
9e4abcb5 SK |
281 | } |
282 | } | |
283 | ||
de37951c SK |
284 | if (!(start = start->next)) |
285 | start = servers; | |
9e4abcb5 | 286 | |
de37951c | 287 | if (start == firstsentto) |
9e4abcb5 SK |
288 | break; |
289 | } | |
290 | ||
de37951c SK |
291 | if (forwarded) |
292 | return last_server; | |
293 | ||
9e4abcb5 SK |
294 | /* could not send on, prepare to return */ |
295 | header->id = htons(forward->orig_id); | |
296 | forward->new_id = 0; /* cancel */ | |
297 | } | |
298 | ||
299 | /* could not send on, return empty answer or address if known for whole domain */ | |
300 | plen = setup_reply(header, (unsigned int)plen, addrp, flags, local_ttl); | |
44a2a316 SK |
301 | send_from(udpfd, options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr); |
302 | ||
9e4abcb5 SK |
303 | if (flags & (F_NOERR | F_NXDOMAIN)) |
304 | log_query(F_CONFIG | F_FORWARD | F_NEG | gotname | (flags & F_NXDOMAIN), dnamebuff, NULL); | |
305 | ||
306 | return last_server; | |
307 | } | |
308 | ||
309 | /* returns new last_server */ | |
de37951c SK |
310 | struct server *reply_query(struct serverfd *sfd, int options, char *packet, time_t now, |
311 | char *dnamebuff, struct server *servers, struct server *last_server, | |
1cff166d | 312 | struct bogus_addr *bogus_nxdomain, struct doctor *doctors) |
9e4abcb5 SK |
313 | { |
314 | /* packet from peer server, extract data for cache, and send to | |
315 | original requester */ | |
316 | struct frec *forward; | |
317 | HEADER *header; | |
de37951c SK |
318 | union mysockaddr serveraddr; |
319 | socklen_t addrlen = sizeof(serveraddr); | |
320 | int n = recvfrom(sfd->fd, packet, PACKETSZ, 0, &serveraddr.sa, &addrlen); | |
321 | ||
322 | /* Determine the address of the server replying so that we can mark that as good */ | |
323 | serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family; | |
324 | #ifdef HAVE_IPV6 | |
325 | if (serveraddr.sa.sa_family == AF_INET6) | |
326 | serveraddr.in6.sin6_flowinfo = htonl(0); | |
327 | #endif | |
9e4abcb5 SK |
328 | |
329 | header = (HEADER *)packet; | |
de37951c | 330 | if (n >= (int)sizeof(HEADER) && header->qr && (forward = lookup_frec(ntohs(header->id)))) |
9e4abcb5 | 331 | { |
de37951c SK |
332 | /* find good server by address if possible, otherwise assume the last one we sent to */ |
333 | if ((forward->sentto->flags & SERV_TYPE) == 0) | |
334 | { | |
335 | for (last_server = servers; last_server; last_server = last_server->next) | |
336 | if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) && | |
337 | sockaddr_isequal(&last_server->addr, &serveraddr)) | |
338 | break; | |
339 | if (!last_server) | |
340 | last_server = forward->sentto; | |
341 | } | |
342 | ||
343 | /* Complain loudly if the upstream server is non-recursive. */ | |
344 | if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0) | |
9e4abcb5 | 345 | { |
de37951c SK |
346 | char addrbuff[ADDRSTRLEN]; |
347 | #ifdef HAVE_IPV6 | |
348 | if (serveraddr.sa.sa_family == AF_INET) | |
349 | inet_ntop(AF_INET, &serveraddr.in.sin_addr, addrbuff, ADDRSTRLEN); | |
350 | else if (serveraddr.sa.sa_family == AF_INET6) | |
351 | inet_ntop(AF_INET6, &serveraddr.in6.sin6_addr, addrbuff, ADDRSTRLEN); | |
352 | #else | |
353 | strcpy(addrbuff, inet_ntoa(serveraddr.in.sin_addr)); | |
354 | #endif | |
355 | syslog(LOG_WARNING, "nameserver %s refused to do a recursive query", addrbuff); | |
356 | return NULL; | |
357 | } | |
358 | ||
359 | if ((header->rcode == NOERROR || header->rcode == NXDOMAIN) && header->opcode == QUERY) | |
360 | { | |
361 | if (!(bogus_nxdomain && | |
362 | header->rcode == NOERROR && | |
363 | check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now))) | |
9e4abcb5 | 364 | { |
de37951c SK |
365 | if (header->rcode == NOERROR && ntohs(header->ancount) != 0) |
366 | extract_addresses(header, (unsigned int)n, dnamebuff, now, doctors); | |
367 | else if (!(options & OPT_NO_NEG)) | |
368 | extract_neg_addrs(header, (unsigned int)n, dnamebuff, now); | |
9e4abcb5 | 369 | } |
9e4abcb5 | 370 | } |
de37951c SK |
371 | |
372 | header->id = htons(forward->orig_id); | |
373 | /* There's no point returning an upstream reply marked as truncated, | |
374 | since that will prod the resolver into moving to TCP - which we | |
375 | don't support. */ | |
376 | header->tc = 0; /* goodbye truncate */ | |
377 | send_from(forward->fd, options & OPT_NOWILD, packet, n, &forward->source, &forward->dest); | |
378 | forward->new_id = 0; /* cancel */ | |
9e4abcb5 | 379 | } |
de37951c | 380 | |
9e4abcb5 SK |
381 | return last_server; |
382 | } | |
44a2a316 | 383 | |
de37951c | 384 | struct server *receive_query(struct listener *listen, char *packet, struct mx_record *mxnames, |
44a2a316 SK |
385 | char *mxtarget, unsigned int options, time_t now, |
386 | unsigned long local_ttl, char *namebuff, | |
387 | struct iname *names, struct iname *addrs, struct iname *except, | |
388 | struct server *last_server, struct server *servers) | |
389 | { | |
390 | HEADER *header = (HEADER *)packet; | |
391 | union mysockaddr source_addr; | |
392 | struct iname *tmp; | |
393 | struct all_addr dst_addr; | |
8a911ccc | 394 | int m, n, if_index = 0; |
44a2a316 SK |
395 | struct iovec iov[1]; |
396 | struct msghdr msg; | |
397 | struct cmsghdr *cmptr; | |
44a2a316 SK |
398 | union { |
399 | struct cmsghdr align; /* this ensures alignment */ | |
400 | #ifdef HAVE_IPV6 | |
401 | char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; | |
402 | #endif | |
403 | #if defined(IP_PKTINFO) | |
404 | char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; | |
405 | #elif defined(IP_RECVDSTADDR) | |
406 | char control[CMSG_SPACE(sizeof(struct in_addr)) + | |
407 | CMSG_SPACE(sizeof(struct sockaddr_dl))]; | |
408 | #endif | |
409 | } control_u; | |
410 | ||
411 | iov[0].iov_base = packet; | |
412 | iov[0].iov_len = PACKETSZ; | |
413 | ||
414 | msg.msg_control = control_u.control; | |
415 | msg.msg_controllen = sizeof(control_u); | |
416 | msg.msg_flags = 0; | |
417 | msg.msg_name = &source_addr; | |
418 | msg.msg_namelen = sizeof(source_addr); | |
419 | msg.msg_iov = iov; | |
420 | msg.msg_iovlen = 1; | |
421 | ||
de37951c SK |
422 | if ((n = recvmsg(listen->fd, &msg, 0)) == -1) |
423 | return last_server; | |
44a2a316 SK |
424 | |
425 | source_addr.sa.sa_family = listen->family; | |
426 | #ifdef HAVE_IPV6 | |
427 | if (listen->family == AF_INET6) | |
428 | source_addr.in6.sin6_flowinfo = htonl(0); | |
429 | #endif | |
430 | ||
431 | if (!(options & OPT_NOWILD) && msg.msg_controllen < sizeof(struct cmsghdr)) | |
432 | return last_server; | |
433 | ||
434 | #if defined(IP_PKTINFO) | |
435 | if (!(options & OPT_NOWILD) && listen->family == AF_INET) | |
436 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
437 | if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) | |
438 | { | |
439 | dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst; | |
8a911ccc | 440 | if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex; |
44a2a316 SK |
441 | } |
442 | #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) | |
443 | if (!(options & OPT_NOWILD) && listen->family == AF_INET) | |
444 | { | |
445 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
446 | if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) | |
8a911ccc | 447 | dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr)); |
44a2a316 | 448 | else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) |
8a911ccc | 449 | if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index; |
44a2a316 SK |
450 | } |
451 | #endif | |
452 | ||
453 | #ifdef HAVE_IPV6 | |
454 | if (!(options & OPT_NOWILD) && listen->family == AF_INET6) | |
455 | { | |
456 | for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) | |
457 | if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO) | |
458 | { | |
459 | dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr; | |
8a911ccc | 460 | if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex; |
44a2a316 SK |
461 | } |
462 | } | |
463 | #endif | |
464 | ||
465 | if (n < (int)sizeof(HEADER) || header->qr) | |
466 | return last_server; | |
467 | ||
468 | /* enforce available interface configuration */ | |
469 | if (!(options & OPT_NOWILD)) | |
470 | { | |
8a911ccc SK |
471 | struct ifreq ifr; |
472 | ||
473 | if (if_index == 0) | |
44a2a316 SK |
474 | return last_server; |
475 | ||
8a911ccc SK |
476 | if (except || names) |
477 | { | |
478 | #ifdef SIOCGIFNAME | |
479 | ifr.ifr_ifindex = if_index; | |
480 | if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1) | |
481 | return last_server; | |
482 | #else | |
483 | if (!if_indextoname(if_index, ifr.ifr_name)) | |
484 | return last_server; | |
485 | #endif | |
486 | } | |
487 | ||
44a2a316 | 488 | for (tmp = except; tmp; tmp = tmp->next) |
8a911ccc | 489 | if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0)) |
44a2a316 | 490 | return last_server; |
9e4abcb5 | 491 | |
44a2a316 SK |
492 | if (names || addrs) |
493 | { | |
494 | for (tmp = names; tmp; tmp = tmp->next) | |
8a911ccc | 495 | if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0)) |
44a2a316 SK |
496 | break; |
497 | if (!tmp) | |
498 | for (tmp = addrs; tmp; tmp = tmp->next) | |
499 | if (tmp->addr.sa.sa_family == listen->family) | |
500 | { | |
501 | if (tmp->addr.sa.sa_family == AF_INET && | |
502 | tmp->addr.in.sin_addr.s_addr == dst_addr.addr.addr4.s_addr) | |
503 | break; | |
504 | #ifdef HAVE_IPV6 | |
505 | else if (tmp->addr.sa.sa_family == AF_INET6 && | |
506 | memcmp(&tmp->addr.in6.sin6_addr, | |
507 | &dst_addr.addr.addr6, | |
508 | sizeof(struct in6_addr)) == 0) | |
509 | break; | |
510 | #endif | |
511 | } | |
512 | if (!tmp) | |
513 | return last_server; | |
514 | } | |
515 | } | |
516 | ||
517 | if (extract_request(header, (unsigned int)n, namebuff)) | |
518 | { | |
519 | if (listen->family == AF_INET) | |
520 | log_query(F_QUERY | F_IPV4 | F_FORWARD, namebuff, | |
521 | (struct all_addr *)&source_addr.in.sin_addr); | |
522 | #ifdef HAVE_IPV6 | |
523 | else | |
524 | log_query(F_QUERY | F_IPV6 | F_FORWARD, namebuff, | |
525 | (struct all_addr *)&source_addr.in6.sin6_addr); | |
526 | #endif | |
527 | } | |
528 | ||
529 | m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, | |
de37951c | 530 | mxnames, mxtarget, options, now, local_ttl, namebuff); |
44a2a316 SK |
531 | if (m >= 1) |
532 | send_from(listen->fd, options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr); | |
533 | else | |
534 | last_server = forward_query(listen->fd, &source_addr, &dst_addr, | |
535 | header, n, options, namebuff, servers, | |
536 | last_server, now, local_ttl); | |
537 | return last_server; | |
538 | } | |
539 | ||
9e4abcb5 SK |
540 | static struct frec *get_new_frec(time_t now) |
541 | { | |
542 | struct frec *f = frec_list, *oldest = NULL; | |
543 | time_t oldtime = now; | |
544 | int count = 0; | |
545 | static time_t warntime = 0; | |
546 | ||
547 | while (f) | |
548 | { | |
549 | if (f->new_id == 0) | |
550 | { | |
551 | f->time = now; | |
552 | return f; | |
553 | } | |
554 | ||
555 | if (difftime(f->time, oldtime) <= 0) | |
556 | { | |
557 | oldtime = f->time; | |
558 | oldest = f; | |
559 | } | |
560 | ||
561 | count++; | |
562 | f = f->next; | |
563 | } | |
564 | ||
565 | /* can't find empty one, use oldest if there is one | |
566 | and it's older than timeout */ | |
567 | if (oldest && difftime(now, oldtime) > TIMEOUT) | |
568 | { | |
569 | oldest->time = now; | |
570 | return oldest; | |
571 | } | |
572 | ||
573 | if (count > FTABSIZ) | |
574 | { /* limit logging rate so syslog isn't DOSed either */ | |
575 | if (!warntime || difftime(now, warntime) > LOGRATE) | |
576 | { | |
577 | warntime = now; | |
578 | syslog(LOG_WARNING, "forwarding table overflow: check for server loops."); | |
579 | } | |
580 | return NULL; | |
581 | } | |
582 | ||
583 | if ((f = (struct frec *)malloc(sizeof(struct frec)))) | |
584 | { | |
585 | f->next = frec_list; | |
586 | f->time = now; | |
587 | frec_list = f; | |
588 | } | |
589 | return f; /* OK if malloc fails and this is NULL */ | |
590 | } | |
591 | ||
592 | static struct frec *lookup_frec(unsigned short id) | |
593 | { | |
594 | struct frec *f; | |
595 | ||
596 | for(f = frec_list; f; f = f->next) | |
597 | if (f->new_id == id) | |
598 | return f; | |
599 | ||
600 | return NULL; | |
601 | } | |
602 | ||
603 | static struct frec *lookup_frec_by_sender(unsigned short id, | |
604 | union mysockaddr *addr) | |
605 | { | |
606 | struct frec *f; | |
607 | ||
608 | for(f = frec_list; f; f = f->next) | |
609 | if (f->new_id && | |
610 | f->orig_id == id && | |
611 | sockaddr_isequal(&f->source, addr)) | |
612 | return f; | |
613 | ||
614 | return NULL; | |
615 | } | |
616 | ||
617 | ||
618 | /* return unique random ids between 1 and 65535 */ | |
619 | static unsigned short get_id(void) | |
620 | { | |
621 | unsigned short ret = 0; | |
622 | ||
623 | while (ret == 0) | |
624 | { | |
625 | ret = rand16(); | |
626 | ||
627 | /* scrap ids already in use */ | |
628 | if ((ret != 0) && lookup_frec(ret)) | |
629 | ret = 0; | |
630 | } | |
631 | ||
632 | return ret; | |
633 | } | |
634 | ||
635 | ||
636 | ||
637 | ||
638 |