]>
git.ipfire.org Git - people/ms/dnsmasq.git/blob - src/forward.c
1 /* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley
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.
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.
13 /* Author's email: simon@thekelleys.org.uk */
17 static struct frec
*frec_list
;
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);
25 /* May be called more than once. */
26 void forward_init(int first
)
32 for (f
= frec_list
; f
; f
= f
->next
)
36 /* delete all forward records recieved from socket fd */
37 void reap_forward(int fd
)
41 for (f
= frec_list
; f
; f
= f
->next
)
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
)
56 struct all_addr
*addrp
= NULL
;
57 unsigned short flags
= 0;
58 unsigned short gotname
= extract_request(header
, (unsigned int)plen
, dnamebuff
);
60 /* may be recursion not speced or no servers available. */
61 if (!header
->rd
|| !servers
)
63 else if ((forward
= lookup_frec_by_sender(ntohs(header
->id
), udpaddr
)))
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
);
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. */
80 unsigned int namelen
= strlen(dnamebuff
);
81 unsigned int matchlen
= 0;
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
, '.'))
87 if (serv
->flags
& SERV_LITERAL_ADDRESS
)
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 */
93 type
= SERV_FOR_NODOTS
;
95 if (serv
->addr
.sa
.sa_family
== AF_INET
)
96 addrp
= (struct all_addr
*)&serv
->addr
.in
.sin_addr
;
99 addrp
= (struct all_addr
*)&serv
->addr
.in6
.sin6_addr
;
106 else if (serv
->flags
& SERV_HAS_DOMAIN
)
108 unsigned int domainlen
= strlen(serv
->domain
);
109 if (namelen
>= domainlen
&&
110 hostname_isequal(dnamebuff
+ namelen
- domainlen
, serv
->domain
) &&
111 domainlen
> matchlen
)
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 */
118 type
= SERV_HAS_DOMAIN
;
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
;
126 addrp
= (struct all_addr
*)&serv
->addr
.in6
.sin6_addr
;
132 flags
= 0; /* may be better match from previous literal */
133 domain
= serv
->domain
;
134 matchlen
= domainlen
;
140 if (flags
) /* flags set here means a literal found */
141 log_query(F_CONFIG
| F_FORWARD
| flags
, dnamebuff
, addrp
);
144 /* we may by policy not forward names without a domain part */
145 if (gotname
&& (options
& OPT_NODOTS_LOCAL
) && !strchr(dnamebuff
, '.'))
147 else if (!(forward
= get_new_frec(now
)))
148 /* table full - server failure. */
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. */
158 if (type
!= 0 || (options
& OPT_ORDER
))
159 forward
->sentto
= servers
;
161 forward
->sentto
= last_server
;
163 forward
->source
= *udpaddr
;
164 forward
->new_id
= get_id();
166 forward
->orig_id
= ntohs(header
->id
);
167 header
->id
= htons(forward
->new_id
);
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) */
175 if (!flags
&& forward
)
177 struct server
*firstsentto
= forward
->sentto
;
183 if (forward
->sentto
->addr
.sa
.sa_family
== AF_INET
)
185 logflags
= F_SERVER
| F_IPV4
| F_FORWARD
;
186 addrp
= (struct all_addr
*)&forward
->sentto
->addr
.in
.sin_addr
;
191 logflags
= F_SERVER
| F_IPV6
| F_FORWARD
;
192 addrp
= (struct all_addr
*)&forward
->sentto
->addr
.in6
.sin6_addr
;
195 /* only send to servers dealing with our domain.
196 domain may be NULL, in which case server->domain
197 must be NULL also. */
199 if (type
== (forward
->sentto
->flags
& SERV_TYPE
) &&
200 (type
!= SERV_HAS_DOMAIN
|| hostname_isequal(domain
, forward
->sentto
->domain
)))
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)
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
);
215 if (!(forward
->sentto
= forward
->sentto
->next
))
216 forward
->sentto
= servers
;
218 /* check if we tried all without success */
219 if (forward
->sentto
== firstsentto
)
223 /* could not send on, prepare to return */
224 header
->id
= htons(forward
->orig_id
);
225 forward
->new_id
= 0; /* cancel */
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
));
232 if (flags
& (F_NOERR
| F_NXDOMAIN
))
233 log_query(F_CONFIG
| F_FORWARD
| F_NEG
| gotname
| (flags
& F_NXDOMAIN
), dnamebuff
, NULL
);
238 /* returns new last_server */
239 struct server
*reply_query(int fd
, int options
, char *packet
, time_t now
,
240 char *dnamebuff
, struct server
*last_server
, struct bogus_addr
*bogus_nxdomain
)
242 /* packet from peer server, extract data for cache, and send to
243 original requester */
244 struct frec
*forward
;
246 int n
= recv(fd
, packet
, PACKETSZ
, 0);
248 header
= (HEADER
*)packet
;
249 if (n
>= (int)sizeof(HEADER
) && header
->qr
)
251 if ((forward
= lookup_frec(ntohs(header
->id
))))
253 if (header
->rcode
== NOERROR
|| header
->rcode
== NXDOMAIN
)
255 if (!forward
->sentto
->domain
)
256 last_server
= forward
->sentto
; /* known good */
257 if (header
->opcode
== QUERY
)
259 if (!(bogus_nxdomain
&&
260 header
->rcode
== NOERROR
&&
261 check_for_bogus_wildcard(header
, (unsigned int)n
, dnamebuff
, bogus_nxdomain
, now
)))
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
);
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
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 */
284 static struct frec
*get_new_frec(time_t now
)
286 struct frec
*f
= frec_list
, *oldest
= NULL
;
287 time_t oldtime
= now
;
289 static time_t warntime
= 0;
299 if (difftime(f
->time
, oldtime
) <= 0)
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
)
318 { /* limit logging rate so syslog isn't DOSed either */
319 if (!warntime
|| difftime(now
, warntime
) > LOGRATE
)
322 syslog(LOG_WARNING
, "forwarding table overflow: check for server loops.");
327 if ((f
= (struct frec
*)malloc(sizeof(struct frec
))))
333 return f
; /* OK if malloc fails and this is NULL */
336 static struct frec
*lookup_frec(unsigned short id
)
340 for(f
= frec_list
; f
; f
= f
->next
)
347 static struct frec
*lookup_frec_by_sender(unsigned short id
,
348 union mysockaddr
*addr
)
352 for(f
= frec_list
; f
; f
= f
->next
)
355 sockaddr_isequal(&f
->source
, addr
))
362 /* return unique random ids between 1 and 65535 */
363 static unsigned short get_id(void)
365 unsigned short ret
= 0;
371 /* scrap ids already in use */
372 if ((ret
!= 0) && lookup_frec(ret
))