]>
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 | ||
36 | /* delete all forward records recieved from socket fd */ | |
37 | void reap_forward(int fd) | |
38 | { | |
39 | struct frec *f; | |
40 | ||
41 | for (f = frec_list; f; f = f->next) | |
42 | if (f->fd == fd) | |
43 | f->new_id = 0; | |
44 | } | |
45 | ||
46 | /* returns new last_server */ | |
47 | struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *header, | |
48 | int plen, unsigned int options, char *dnamebuff, | |
49 | struct server *servers, struct server *last_server, | |
50 | time_t now, unsigned long local_ttl) | |
51 | { | |
52 | struct frec *forward; | |
53 | char *domain = NULL; | |
54 | int type = 0; | |
55 | struct server *serv; | |
56 | struct all_addr *addrp = NULL; | |
57 | unsigned short flags = 0; | |
58 | unsigned short gotname = extract_request(header, (unsigned int)plen, dnamebuff); | |
59 | ||
60 | /* may be recursion not speced or no servers available. */ | |
61 | if (!header->rd || !servers) | |
62 | forward = NULL; | |
63 | else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr))) | |
64 | { | |
65 | /* retry on existing query, send to next server */ | |
66 | domain = forward->sentto->domain; | |
67 | type = forward->sentto->flags & SERV_TYPE; | |
68 | if (!(forward->sentto = forward->sentto->next)) | |
69 | forward->sentto = servers; /* at end of list, recycle */ | |
70 | header->id = htons(forward->new_id); | |
71 | } | |
72 | else | |
73 | { | |
74 | if (gotname) | |
75 | { | |
76 | /* If the query ends in the domain in one of our servers, set | |
77 | domain to point to that name. We find the largest match to allow both | |
78 | domain.org and sub.domain.org to exist. */ | |
79 | ||
80 | unsigned int namelen = strlen(dnamebuff); | |
81 | unsigned int matchlen = 0; | |
82 | ||
83 | for (serv=servers; serv; serv=serv->next) | |
84 | /* domain matches take priority over NODOTS matches */ | |
85 | if ((serv->flags & SERV_FOR_NODOTS) && type != SERV_HAS_DOMAIN && !strchr(dnamebuff, '.')) | |
86 | { | |
87 | if (serv->flags & SERV_LITERAL_ADDRESS) | |
88 | { | |
89 | /* flags gets set if server is in fact an answer */ | |
90 | unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; | |
91 | if (sflag & gotname) /* only OK if addrfamily == query */ | |
92 | { | |
93 | type = SERV_FOR_NODOTS; | |
94 | flags = sflag; | |
95 | if (serv->addr.sa.sa_family == AF_INET) | |
96 | addrp = (struct all_addr *)&serv->addr.in.sin_addr; | |
97 | #ifdef HAVE_IPV6 | |
98 | else | |
99 | addrp = (struct all_addr *)&serv->addr.in6.sin6_addr; | |
100 | #endif | |
101 | } | |
102 | } | |
103 | else | |
104 | flags = 0; | |
105 | } | |
106 | else if (serv->flags & SERV_HAS_DOMAIN) | |
107 | { | |
108 | unsigned int domainlen = strlen(serv->domain); | |
109 | if (namelen >= domainlen && | |
110 | hostname_isequal(dnamebuff + namelen - domainlen, serv->domain) && | |
111 | domainlen > matchlen) | |
112 | { | |
113 | if (serv->flags & SERV_LITERAL_ADDRESS) | |
114 | { /* flags gets set if server is in fact an answer */ | |
115 | unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; | |
116 | if (sflag & gotname) /* only OK if addrfamily == query */ | |
117 | { | |
118 | type = SERV_HAS_DOMAIN; | |
119 | flags = sflag; | |
120 | domain = serv->domain; | |
121 | matchlen = domainlen; | |
122 | if (serv->addr.sa.sa_family == AF_INET) | |
123 | addrp = (struct all_addr *)&serv->addr.in.sin_addr; | |
124 | #ifdef HAVE_IPV6 | |
125 | else | |
126 | addrp = (struct all_addr *)&serv->addr.in6.sin6_addr; | |
127 | #endif | |
128 | } | |
129 | } | |
130 | else | |
131 | { | |
132 | flags = 0; /* may be better match from previous literal */ | |
133 | domain = serv->domain; | |
134 | matchlen = domainlen; | |
1ab84e2f | 135 | type = SERV_HAS_DOMAIN; |
9e4abcb5 SK |
136 | } |
137 | } | |
138 | } | |
139 | } | |
140 | ||
141 | if (flags) /* flags set here means a literal found */ | |
142 | log_query(F_CONFIG | F_FORWARD | flags, dnamebuff, addrp); | |
143 | else | |
144 | { | |
145 | /* we may by policy not forward names without a domain part */ | |
146 | if (gotname && (options & OPT_NODOTS_LOCAL) && !strchr(dnamebuff, '.')) | |
147 | flags = F_NXDOMAIN; | |
148 | else if (!(forward = get_new_frec(now))) | |
149 | /* table full - server failure. */ | |
150 | flags = F_NEG; | |
151 | } | |
152 | ||
153 | if (forward) | |
154 | { | |
155 | /* In strict_order mode, or when using domain specific servers | |
156 | always try servers in the order specified in resolv.conf, | |
157 | otherwise, use the one last known to work. */ | |
158 | ||
159 | if (type != 0 || (options & OPT_ORDER)) | |
160 | forward->sentto = servers; | |
161 | else | |
162 | forward->sentto = last_server; | |
163 | ||
164 | forward->source = *udpaddr; | |
165 | forward->new_id = get_id(); | |
166 | forward->fd = udpfd; | |
167 | forward->orig_id = ntohs(header->id); | |
168 | header->id = htons(forward->new_id); | |
169 | } | |
170 | } | |
171 | ||
172 | /* check for send errors here (no route to host) | |
173 | if we fail to send to all nameservers, send back an error | |
174 | packet straight away (helps modem users when offline) */ | |
175 | ||
176 | if (!flags && forward) | |
177 | { | |
178 | struct server *firstsentto = forward->sentto; | |
179 | ||
180 | while (1) | |
181 | { | |
182 | int logflags = 0; | |
183 | ||
184 | if (forward->sentto->addr.sa.sa_family == AF_INET) | |
185 | { | |
186 | logflags = F_SERVER | F_IPV4 | F_FORWARD; | |
187 | addrp = (struct all_addr *)&forward->sentto->addr.in.sin_addr; | |
188 | } | |
189 | #ifdef HAVE_IPV6 | |
190 | else | |
191 | { | |
192 | logflags = F_SERVER | F_IPV6 | F_FORWARD; | |
193 | addrp = (struct all_addr *)&forward->sentto->addr.in6.sin6_addr; | |
194 | } | |
195 | #endif | |
196 | /* only send to servers dealing with our domain. | |
197 | domain may be NULL, in which case server->domain | |
198 | must be NULL also. */ | |
199 | ||
200 | if (type == (forward->sentto->flags & SERV_TYPE) && | |
201 | (type != SERV_HAS_DOMAIN || hostname_isequal(domain, forward->sentto->domain))) | |
202 | { | |
203 | if (forward->sentto->flags & SERV_NO_ADDR) | |
204 | flags = F_NOERR; /* NULL servers are OK. */ | |
205 | else if (!(forward->sentto->flags & SERV_LITERAL_ADDRESS) && | |
206 | sendto(forward->sentto->sfd->fd, (char *)header, plen, 0, | |
207 | &forward->sentto->addr.sa, | |
208 | sa_len(&forward->sentto->addr)) != -1) | |
209 | { | |
210 | log_query(logflags, gotname ? dnamebuff : "query", addrp); | |
211 | /* for no-domain, don't update last_server */ | |
212 | return domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers); | |
213 | } | |
214 | } | |
215 | ||
216 | if (!(forward->sentto = forward->sentto->next)) | |
217 | forward->sentto = servers; | |
218 | ||
219 | /* check if we tried all without success */ | |
220 | if (forward->sentto == firstsentto) | |
221 | break; | |
222 | } | |
223 | ||
224 | /* could not send on, prepare to return */ | |
225 | header->id = htons(forward->orig_id); | |
226 | forward->new_id = 0; /* cancel */ | |
227 | } | |
228 | ||
229 | /* could not send on, return empty answer or address if known for whole domain */ | |
230 | plen = setup_reply(header, (unsigned int)plen, addrp, flags, local_ttl); | |
231 | sendto(udpfd, (char *)header, plen, 0, &udpaddr->sa, sa_len(udpaddr)); | |
232 | ||
233 | if (flags & (F_NOERR | F_NXDOMAIN)) | |
234 | log_query(F_CONFIG | F_FORWARD | F_NEG | gotname | (flags & F_NXDOMAIN), dnamebuff, NULL); | |
235 | ||
236 | return last_server; | |
237 | } | |
238 | ||
239 | /* returns new last_server */ | |
240 | struct server *reply_query(int fd, int options, char *packet, time_t now, | |
241 | char *dnamebuff, struct server *last_server, struct bogus_addr *bogus_nxdomain) | |
242 | { | |
243 | /* packet from peer server, extract data for cache, and send to | |
244 | original requester */ | |
245 | struct frec *forward; | |
246 | HEADER *header; | |
247 | int n = recv(fd, packet, PACKETSZ, 0); | |
248 | ||
249 | header = (HEADER *)packet; | |
250 | if (n >= (int)sizeof(HEADER) && header->qr) | |
251 | { | |
252 | if ((forward = lookup_frec(ntohs(header->id)))) | |
253 | { | |
254 | if (header->rcode == NOERROR || header->rcode == NXDOMAIN) | |
255 | { | |
256 | if (!forward->sentto->domain) | |
257 | last_server = forward->sentto; /* known good */ | |
258 | if (header->opcode == QUERY) | |
259 | { | |
260 | if (!(bogus_nxdomain && | |
261 | header->rcode == NOERROR && | |
262 | check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now))) | |
263 | { | |
264 | if (header->rcode == NOERROR && ntohs(header->ancount) != 0) | |
265 | extract_addresses(header, (unsigned int)n, dnamebuff, now); | |
266 | else if (!(options & OPT_NO_NEG)) | |
267 | extract_neg_addrs(header, (unsigned int)n, dnamebuff, now); | |
268 | } | |
269 | } | |
270 | } | |
271 | header->id = htons(forward->orig_id); | |
272 | /* There's no point returning an upstream reply marked as truncated, | |
273 | since that will prod the resolver into moving to TCP - which we | |
274 | don't support. */ | |
275 | header->tc = 0; /* goodbye truncate */ | |
276 | sendto(forward->fd, packet, n, 0, | |
277 | &forward->source.sa, sa_len(&forward->source)); | |
278 | forward->new_id = 0; /* cancel */ | |
279 | } | |
280 | } | |
281 | ||
282 | return last_server; | |
283 | } | |
284 | ||
285 | static struct frec *get_new_frec(time_t now) | |
286 | { | |
287 | struct frec *f = frec_list, *oldest = NULL; | |
288 | time_t oldtime = now; | |
289 | int count = 0; | |
290 | static time_t warntime = 0; | |
291 | ||
292 | while (f) | |
293 | { | |
294 | if (f->new_id == 0) | |
295 | { | |
296 | f->time = now; | |
297 | return f; | |
298 | } | |
299 | ||
300 | if (difftime(f->time, oldtime) <= 0) | |
301 | { | |
302 | oldtime = f->time; | |
303 | oldest = f; | |
304 | } | |
305 | ||
306 | count++; | |
307 | f = f->next; | |
308 | } | |
309 | ||
310 | /* can't find empty one, use oldest if there is one | |
311 | and it's older than timeout */ | |
312 | if (oldest && difftime(now, oldtime) > TIMEOUT) | |
313 | { | |
314 | oldest->time = now; | |
315 | return oldest; | |
316 | } | |
317 | ||
318 | if (count > FTABSIZ) | |
319 | { /* limit logging rate so syslog isn't DOSed either */ | |
320 | if (!warntime || difftime(now, warntime) > LOGRATE) | |
321 | { | |
322 | warntime = now; | |
323 | syslog(LOG_WARNING, "forwarding table overflow: check for server loops."); | |
324 | } | |
325 | return NULL; | |
326 | } | |
327 | ||
328 | if ((f = (struct frec *)malloc(sizeof(struct frec)))) | |
329 | { | |
330 | f->next = frec_list; | |
331 | f->time = now; | |
332 | frec_list = f; | |
333 | } | |
334 | return f; /* OK if malloc fails and this is NULL */ | |
335 | } | |
336 | ||
337 | static struct frec *lookup_frec(unsigned short id) | |
338 | { | |
339 | struct frec *f; | |
340 | ||
341 | for(f = frec_list; f; f = f->next) | |
342 | if (f->new_id == id) | |
343 | return f; | |
344 | ||
345 | return NULL; | |
346 | } | |
347 | ||
348 | static struct frec *lookup_frec_by_sender(unsigned short id, | |
349 | union mysockaddr *addr) | |
350 | { | |
351 | struct frec *f; | |
352 | ||
353 | for(f = frec_list; f; f = f->next) | |
354 | if (f->new_id && | |
355 | f->orig_id == id && | |
356 | sockaddr_isequal(&f->source, addr)) | |
357 | return f; | |
358 | ||
359 | return NULL; | |
360 | } | |
361 | ||
362 | ||
363 | /* return unique random ids between 1 and 65535 */ | |
364 | static unsigned short get_id(void) | |
365 | { | |
366 | unsigned short ret = 0; | |
367 | ||
368 | while (ret == 0) | |
369 | { | |
370 | ret = rand16(); | |
371 | ||
372 | /* scrap ids already in use */ | |
373 | if ((ret != 0) && lookup_frec(ret)) | |
374 | ret = 0; | |
375 | } | |
376 | ||
377 | return ret; | |
378 | } | |
379 | ||
380 | ||
381 | ||
382 | ||
383 |