]> git.ipfire.org Git - people/ms/dnsmasq.git/blame - src/forward.c
import of dnsmasq-2.0.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;
135 }
136 }
137 }
138 }
139
140 if (flags) /* flags set here means a literal found */
141 log_query(F_CONFIG | F_FORWARD | flags, dnamebuff, addrp);
142 else
143 {
144 /* we may by policy not forward names without a domain part */
145 if (gotname && (options & OPT_NODOTS_LOCAL) && !strchr(dnamebuff, '.'))
146 flags = F_NXDOMAIN;
147 else if (!(forward = get_new_frec(now)))
148 /* table full - server failure. */
149 flags = F_NEG;
150 }
151
152 if (forward)
153 {
154 /* In strict_order mode, or when using domain specific servers
155 always try servers in the order specified in resolv.conf,
156 otherwise, use the one last known to work. */
157
158 if (type != 0 || (options & OPT_ORDER))
159 forward->sentto = servers;
160 else
161 forward->sentto = last_server;
162
163 forward->source = *udpaddr;
164 forward->new_id = get_id();
165 forward->fd = udpfd;
166 forward->orig_id = ntohs(header->id);
167 header->id = htons(forward->new_id);
168 }
169 }
170
171 /* check for send errors here (no route to host)
172 if we fail to send to all nameservers, send back an error
173 packet straight away (helps modem users when offline) */
174
175 if (!flags && forward)
176 {
177 struct server *firstsentto = forward->sentto;
178
179 while (1)
180 {
181 int logflags = 0;
182
183 if (forward->sentto->addr.sa.sa_family == AF_INET)
184 {
185 logflags = F_SERVER | F_IPV4 | F_FORWARD;
186 addrp = (struct all_addr *)&forward->sentto->addr.in.sin_addr;
187 }
188#ifdef HAVE_IPV6
189 else
190 {
191 logflags = F_SERVER | F_IPV6 | F_FORWARD;
192 addrp = (struct all_addr *)&forward->sentto->addr.in6.sin6_addr;
193 }
194#endif
195 /* only send to servers dealing with our domain.
196 domain may be NULL, in which case server->domain
197 must be NULL also. */
198
199 if (type == (forward->sentto->flags & SERV_TYPE) &&
200 (type != SERV_HAS_DOMAIN || hostname_isequal(domain, forward->sentto->domain)))
201 {
202 if (forward->sentto->flags & SERV_NO_ADDR)
203 flags = F_NOERR; /* NULL servers are OK. */
204 else if (!(forward->sentto->flags & SERV_LITERAL_ADDRESS) &&
205 sendto(forward->sentto->sfd->fd, (char *)header, plen, 0,
206 &forward->sentto->addr.sa,
207 sa_len(&forward->sentto->addr)) != -1)
208 {
209 log_query(logflags, gotname ? dnamebuff : "query", addrp);
210 /* for no-domain, don't update last_server */
211 return domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers);
212 }
213 }
214
215 if (!(forward->sentto = forward->sentto->next))
216 forward->sentto = servers;
217
218 /* check if we tried all without success */
219 if (forward->sentto == firstsentto)
220 break;
221 }
222
223 /* could not send on, prepare to return */
224 header->id = htons(forward->orig_id);
225 forward->new_id = 0; /* cancel */
226 }
227
228 /* could not send on, return empty answer or address if known for whole domain */
229 plen = setup_reply(header, (unsigned int)plen, addrp, flags, local_ttl);
230 sendto(udpfd, (char *)header, plen, 0, &udpaddr->sa, sa_len(udpaddr));
231
232 if (flags & (F_NOERR | F_NXDOMAIN))
233 log_query(F_CONFIG | F_FORWARD | F_NEG | gotname | (flags & F_NXDOMAIN), dnamebuff, NULL);
234
235 return last_server;
236}
237
238/* returns new last_server */
239struct server *reply_query(int fd, int options, char *packet, time_t now,
240 char *dnamebuff, struct server *last_server, struct bogus_addr *bogus_nxdomain)
241{
242 /* packet from peer server, extract data for cache, and send to
243 original requester */
244 struct frec *forward;
245 HEADER *header;
246 int n = recv(fd, packet, PACKETSZ, 0);
247
248 header = (HEADER *)packet;
249 if (n >= (int)sizeof(HEADER) && header->qr)
250 {
251 if ((forward = lookup_frec(ntohs(header->id))))
252 {
253 if (header->rcode == NOERROR || header->rcode == NXDOMAIN)
254 {
255 if (!forward->sentto->domain)
256 last_server = forward->sentto; /* known good */
257 if (header->opcode == QUERY)
258 {
259 if (!(bogus_nxdomain &&
260 header->rcode == NOERROR &&
261 check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now)))
262 {
263 if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
264 extract_addresses(header, (unsigned int)n, dnamebuff, now);
265 else if (!(options & OPT_NO_NEG))
266 extract_neg_addrs(header, (unsigned int)n, dnamebuff, now);
267 }
268 }
269 }
270 header->id = htons(forward->orig_id);
271 /* There's no point returning an upstream reply marked as truncated,
272 since that will prod the resolver into moving to TCP - which we
273 don't support. */
274 header->tc = 0; /* goodbye truncate */
275 sendto(forward->fd, packet, n, 0,
276 &forward->source.sa, sa_len(&forward->source));
277 forward->new_id = 0; /* cancel */
278 }
279 }
280
281 return last_server;
282}
283
284static struct frec *get_new_frec(time_t now)
285{
286 struct frec *f = frec_list, *oldest = NULL;
287 time_t oldtime = now;
288 int count = 0;
289 static time_t warntime = 0;
290
291 while (f)
292 {
293 if (f->new_id == 0)
294 {
295 f->time = now;
296 return f;
297 }
298
299 if (difftime(f->time, oldtime) <= 0)
300 {
301 oldtime = f->time;
302 oldest = f;
303 }
304
305 count++;
306 f = f->next;
307 }
308
309 /* can't find empty one, use oldest if there is one
310 and it's older than timeout */
311 if (oldest && difftime(now, oldtime) > TIMEOUT)
312 {
313 oldest->time = now;
314 return oldest;
315 }
316
317 if (count > FTABSIZ)
318 { /* limit logging rate so syslog isn't DOSed either */
319 if (!warntime || difftime(now, warntime) > LOGRATE)
320 {
321 warntime = now;
322 syslog(LOG_WARNING, "forwarding table overflow: check for server loops.");
323 }
324 return NULL;
325 }
326
327 if ((f = (struct frec *)malloc(sizeof(struct frec))))
328 {
329 f->next = frec_list;
330 f->time = now;
331 frec_list = f;
332 }
333 return f; /* OK if malloc fails and this is NULL */
334}
335
336static struct frec *lookup_frec(unsigned short id)
337{
338 struct frec *f;
339
340 for(f = frec_list; f; f = f->next)
341 if (f->new_id == id)
342 return f;
343
344 return NULL;
345}
346
347static struct frec *lookup_frec_by_sender(unsigned short id,
348 union mysockaddr *addr)
349{
350 struct frec *f;
351
352 for(f = frec_list; f; f = f->next)
353 if (f->new_id &&
354 f->orig_id == id &&
355 sockaddr_isequal(&f->source, addr))
356 return f;
357
358 return NULL;
359}
360
361
362/* return unique random ids between 1 and 65535 */
363static unsigned short get_id(void)
364{
365 unsigned short ret = 0;
366
367 while (ret == 0)
368 {
369 ret = rand16();
370
371 /* scrap ids already in use */
372 if ((ret != 0) && lookup_frec(ret))
373 ret = 0;
374 }
375
376 return ret;
377}
378
379
380
381
382