]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/forward.c
import of dnsmasq-2.2.tar.gz
[people/ms/dnsmasq.git] / src / forward.c
CommitLineData
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
17static struct frec *frec_list;
18
19static struct frec *get_new_frec(time_t now);
20static struct frec *lookup_frec(unsigned short id);
21static struct frec *lookup_frec_by_sender(unsigned short id,
22 union mysockaddr *addr);
23static unsigned short get_id(void);
24
25/* May be called more than once. */
26void 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 */
37void 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 */
47struct 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 */
240struct 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
285static 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
337static 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
348static 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 */
364static 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