blob: 542008dee8cc9ac49d26ac66f8b09481e4d4805e [file] [log] [blame]
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001/* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
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,
Simon Kelleyfd9fa482004-10-21 20:24:00 +010022 union mysockaddr *addr,
23 unsigned int crc);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000024static unsigned short get_id(void);
25
26/* May be called more than once. */
27void forward_init(int first)
28{
29 struct frec *f;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010030
Simon Kelley9e4abcb2004-01-22 19:47:41 +000031 if (first)
32 frec_list = NULL;
33 for (f = frec_list; f; f = f->next)
34 f->new_id = 0;
35}
36
Simon Kelley44a2a312004-03-10 20:04:35 +000037/* Send a UDP packet with it's source address set as "source"
38 unless nowild is true, when we just send it with the kernel default */
39static void send_from(int fd, int nowild, char *packet, int len,
Simon Kelleydfa666f2004-08-02 18:27:27 +010040 union mysockaddr *to, struct all_addr *source,
41 unsigned int iface)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000042{
Simon Kelley44a2a312004-03-10 20:04:35 +000043 struct msghdr msg;
44 struct iovec iov[1];
Simon Kelley44a2a312004-03-10 20:04:35 +000045 union {
46 struct cmsghdr align; /* this ensures alignment */
47#if defined(IP_PKTINFO)
48 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
49#elif defined(IP_SENDSRCADDR)
50 char control[CMSG_SPACE(sizeof(struct in_addr))];
51#endif
52#ifdef HAVE_IPV6
53 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
54#endif
55 } control_u;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010056
Simon Kelley44a2a312004-03-10 20:04:35 +000057 iov[0].iov_base = packet;
58 iov[0].iov_len = len;
59
Simon Kelleyfeba5c12004-07-27 20:28:58 +010060 msg.msg_control = NULL;
61 msg.msg_controllen = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +000062 msg.msg_flags = 0;
63 msg.msg_name = to;
64 msg.msg_namelen = sa_len(to);
65 msg.msg_iov = iov;
66 msg.msg_iovlen = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010067
Simon Kelley26128d22004-11-14 16:43:54 +000068 if (!nowild)
Simon Kelleyfeba5c12004-07-27 20:28:58 +010069 {
Simon Kelley26128d22004-11-14 16:43:54 +000070 struct cmsghdr *cmptr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010071 msg.msg_control = &control_u;
72 msg.msg_controllen = sizeof(control_u);
Simon Kelley26128d22004-11-14 16:43:54 +000073 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley44a2a312004-03-10 20:04:35 +000074
Simon Kelley26128d22004-11-14 16:43:54 +000075 if (to->sa.sa_family == AF_INET)
76 {
77#if defined(IP_PKTINFO)
78 struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
79 pkt->ipi_ifindex = 0;
80 pkt->ipi_spec_dst = source->addr.addr4;
81 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
82 cmptr->cmsg_level = SOL_IP;
83 cmptr->cmsg_type = IP_PKTINFO;
84#elif defined(IP_SENDSRCADDR)
85 struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
86 *a = source->addr.addr4;
87 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
88 cmptr->cmsg_level = IPPROTO_IP;
89 cmptr->cmsg_type = IP_SENDSRCADDR;
Simon Kelley44a2a312004-03-10 20:04:35 +000090#endif
Simon Kelley26128d22004-11-14 16:43:54 +000091 }
92
93#ifdef HAVE_IPV6
94 else
95 {
96 struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
97 pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
98 pkt->ipi6_addr = source->addr.addr6;
99 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
100 cmptr->cmsg_type = IPV6_PKTINFO;
101 cmptr->cmsg_level = IPV6_LEVEL;
102 }
103#endif
104 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100105
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100106 retry:
107 if (sendmsg(fd, &msg, 0) == -1)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100108 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100109 /* certain Linux kernels seem to object to setting the source address in the IPv6 stack
110 by returning EINVAL from sendmsg. In that case, try again without setting the
111 source address, since it will nearly alway be correct anyway. IPv6 stinks. */
112 if (errno == EINVAL && msg.msg_controllen)
113 {
114 msg.msg_controllen = 0;
115 goto retry;
116 }
117 if (retry_send())
118 goto retry;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100119 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000120}
121
Simon Kelley36717ee2004-09-20 19:20:58 +0100122static unsigned short search_servers(struct daemon *daemon, time_t now, struct all_addr **addrpp,
123 unsigned short qtype, char *qdomain, int *type, char **domain)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100124
125{
126 /* If the query ends in the domain in one of our servers, set
127 domain to point to that name. We find the largest match to allow both
128 domain.org and sub.domain.org to exist. */
129
130 unsigned int namelen = strlen(qdomain);
131 unsigned int matchlen = 0;
132 struct server *serv;
133 unsigned short flags = 0;
134
Simon Kelley3be34542004-09-11 19:12:13 +0100135 for (serv = daemon->servers; serv; serv=serv->next)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100136 /* domain matches take priority over NODOTS matches */
137 if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.'))
138 {
139 unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
140 *type = SERV_FOR_NODOTS;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100141 if (serv->flags & SERV_NO_ADDR)
Simon Kelley36717ee2004-09-20 19:20:58 +0100142 flags = F_NXDOMAIN;
143 else if (serv->flags & SERV_LITERAL_ADDRESS)
144 {
145 if (sflag & qtype)
146 {
147 flags = sflag;
148 if (serv->addr.sa.sa_family == AF_INET)
149 *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100150#ifdef HAVE_IPV6
Simon Kelley36717ee2004-09-20 19:20:58 +0100151 else
152 *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100153#endif
Simon Kelley36717ee2004-09-20 19:20:58 +0100154 }
155 else if (!flags)
156 flags = F_NOERR;
157 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100158 }
159 else if (serv->flags & SERV_HAS_DOMAIN)
160 {
161 unsigned int domainlen = strlen(serv->domain);
162 if (namelen >= domainlen &&
163 hostname_isequal(qdomain + namelen - domainlen, serv->domain) &&
164 domainlen >= matchlen)
165 {
166 unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
167 *type = SERV_HAS_DOMAIN;
168 *domain = serv->domain;
169 matchlen = domainlen;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100170 if (serv->flags & SERV_NO_ADDR)
Simon Kelley36717ee2004-09-20 19:20:58 +0100171 flags = F_NXDOMAIN;
172 else if (serv->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100173 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100174 if ((sflag | F_QUERY ) & qtype)
175 {
176 flags = qtype;
177 if (serv->addr.sa.sa_family == AF_INET)
178 *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100179#ifdef HAVE_IPV6
Simon Kelley36717ee2004-09-20 19:20:58 +0100180 else
181 *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100182#endif
Simon Kelley36717ee2004-09-20 19:20:58 +0100183 }
184 else if (!flags)
185 flags = F_NOERR;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100186 }
187 }
188 }
189
Simon Kelley36717ee2004-09-20 19:20:58 +0100190 if (flags & ~(F_NOERR | F_NXDOMAIN)) /* flags set here means a literal found */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100191 {
192 if (flags & F_QUERY)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100193 log_query(F_CONFIG | F_FORWARD | F_NEG, qdomain, NULL, 0, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100194 else
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100195 log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp, 0, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100196 }
Simon Kelley3be34542004-09-11 19:12:13 +0100197 else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.'))
Simon Kelley36717ee2004-09-20 19:20:58 +0100198 flags = F_NXDOMAIN;
199
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000200 if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon))
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100201 flags = F_NOERR;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100202
Simon Kelley36717ee2004-09-20 19:20:58 +0100203 if (flags == F_NXDOMAIN || flags == F_NOERR)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100204 log_query(F_CONFIG | F_FORWARD | F_NEG | qtype | (flags & F_NXDOMAIN), qdomain, NULL, 0, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100205
206 return flags;
207}
Simon Kelley44a2a312004-03-10 20:04:35 +0000208
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000209/* returns new last_server */
Simon Kelley3be34542004-09-11 19:12:13 +0100210static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *udpaddr,
211 struct all_addr *dst_addr, unsigned int dst_iface,
212 HEADER *header, int plen, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000213{
214 struct frec *forward;
215 char *domain = NULL;
Simon Kelleyde379512004-06-22 20:23:33 +0100216 int forwardall = 0, type = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000217 struct all_addr *addrp = NULL;
218 unsigned short flags = 0;
Simon Kelley3be34542004-09-11 19:12:13 +0100219 unsigned short gotname = extract_request(header, (unsigned int)plen, daemon->namebuff, NULL);
Simon Kelleyde379512004-06-22 20:23:33 +0100220 struct server *start = NULL;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100221 unsigned int crc = questions_crc(header,(unsigned int)plen);
Simon Kelleyde379512004-06-22 20:23:33 +0100222
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000223 /* may be recursion not speced or no servers available. */
Simon Kelley3be34542004-09-11 19:12:13 +0100224 if (!header->rd || !daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000225 forward = NULL;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100226 else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000227 {
Simon Kelleyde379512004-06-22 20:23:33 +0100228 /* retry on existing query, send to all available servers */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000229 domain = forward->sentto->domain;
Simon Kelley3be34542004-09-11 19:12:13 +0100230 if (!(daemon->options & OPT_ORDER))
Simon Kelleyde379512004-06-22 20:23:33 +0100231 {
232 forwardall = 1;
Simon Kelley3be34542004-09-11 19:12:13 +0100233 daemon->last_server = NULL;
Simon Kelleyde379512004-06-22 20:23:33 +0100234 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000235 type = forward->sentto->flags & SERV_TYPE;
Simon Kelleyde379512004-06-22 20:23:33 +0100236 if (!(start = forward->sentto->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100237 start = daemon->servers; /* at end of list, recycle */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000238 header->id = htons(forward->new_id);
239 }
240 else
241 {
242 if (gotname)
Simon Kelley36717ee2004-09-20 19:20:58 +0100243 flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000244
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100245 if (!flags && !(forward = get_new_frec(now)))
246 /* table full - server failure. */
247 flags = F_NEG;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000248
249 if (forward)
250 {
251 /* In strict_order mode, or when using domain specific servers
252 always try servers in the order specified in resolv.conf,
253 otherwise, use the one last known to work. */
254
Simon Kelley3be34542004-09-11 19:12:13 +0100255 if (type != 0 || (daemon->options & OPT_ORDER))
256 start = daemon->servers;
257 else if (!(start = daemon->last_server))
Simon Kelleyde379512004-06-22 20:23:33 +0100258 {
Simon Kelley3be34542004-09-11 19:12:13 +0100259 start = daemon->servers;
Simon Kelleyde379512004-06-22 20:23:33 +0100260 forwardall = 1;
261 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100262
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000263 forward->source = *udpaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000264 forward->dest = *dst_addr;
Simon Kelleydfa666f2004-08-02 18:27:27 +0100265 forward->iface = dst_iface;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000266 forward->new_id = get_id();
267 forward->fd = udpfd;
268 forward->orig_id = ntohs(header->id);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100269 forward->crc = crc;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000270 header->id = htons(forward->new_id);
271 }
272 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100273
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000274 /* check for send errors here (no route to host)
275 if we fail to send to all nameservers, send back an error
276 packet straight away (helps modem users when offline) */
277
278 if (!flags && forward)
279 {
Simon Kelleyde379512004-06-22 20:23:33 +0100280 struct server *firstsentto = start;
281 int forwarded = 0;
282
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000283 while (1)
284 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000285 /* only send to servers dealing with our domain.
286 domain may be NULL, in which case server->domain
287 must be NULL also. */
288
Simon Kelleyde379512004-06-22 20:23:33 +0100289 if (type == (start->flags & SERV_TYPE) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100290 (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
291 !(start->flags & SERV_LITERAL_ADDRESS))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000292 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100293 if (sendto(start->sfd->fd, (char *)header, plen, 0,
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100294 &start->addr.sa,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100295 sa_len(&start->addr)) == -1)
296 {
297 if (retry_send())
298 continue;
299 }
300 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000301 {
Simon Kelleyde379512004-06-22 20:23:33 +0100302 if (!gotname)
Simon Kelley3be34542004-09-11 19:12:13 +0100303 strcpy(daemon->namebuff, "query");
Simon Kelleyde379512004-06-22 20:23:33 +0100304 if (start->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100305 log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100306 (struct all_addr *)&start->addr.in.sin_addr, 0,
307 NULL, 0);
Simon Kelleyde379512004-06-22 20:23:33 +0100308#ifdef HAVE_IPV6
309 else
Simon Kelley3be34542004-09-11 19:12:13 +0100310 log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100311 (struct all_addr *)&start->addr.in6.sin6_addr, 0,
312 NULL, 0);
Simon Kelleyde379512004-06-22 20:23:33 +0100313#endif
314 forwarded = 1;
315 forward->sentto = start;
316 if (!forwardall)
317 break;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000318 }
319 }
320
Simon Kelleyde379512004-06-22 20:23:33 +0100321 if (!(start = start->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100322 start = daemon->servers;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000323
Simon Kelleyde379512004-06-22 20:23:33 +0100324 if (start == firstsentto)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000325 break;
326 }
327
Simon Kelleyde379512004-06-22 20:23:33 +0100328 if (forwarded)
Simon Kelley3be34542004-09-11 19:12:13 +0100329 return;
Simon Kelleyde379512004-06-22 20:23:33 +0100330
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000331 /* could not send on, prepare to return */
332 header->id = htons(forward->orig_id);
333 forward->new_id = 0; /* cancel */
334 }
335
336 /* could not send on, return empty answer or address if known for whole domain */
Simon Kelley3be34542004-09-11 19:12:13 +0100337 plen = setup_reply(header, (unsigned int)plen, addrp, flags, daemon->local_ttl);
338 send_from(udpfd, daemon->options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface);
Simon Kelley44a2a312004-03-10 20:04:35 +0000339
Simon Kelley3be34542004-09-11 19:12:13 +0100340 return;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000341}
342
Simon Kelley3be34542004-09-11 19:12:13 +0100343static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
Simon Kelley36717ee2004-09-20 19:20:58 +0100344 union mysockaddr *serveraddr, unsigned int n)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100345{
Simon Kelley36717ee2004-09-20 19:20:58 +0100346 unsigned char *pheader, *sizep;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100347 unsigned int plen, munged = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100348
349 /* If upstream is advertising a larger UDP packet size
350 than we allow, trim it so that we don't get overlarge
351 requests for the client. */
352
Simon Kelley36717ee2004-09-20 19:20:58 +0100353 if ((pheader = find_pseudoheader(header, n, &plen, &sizep)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100354 {
355 unsigned short udpsz;
Simon Kelley36717ee2004-09-20 19:20:58 +0100356 unsigned char *psave = sizep;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100357
Simon Kelley36717ee2004-09-20 19:20:58 +0100358 GETSHORT(udpsz, sizep);
Simon Kelley3be34542004-09-11 19:12:13 +0100359 if (udpsz > daemon->edns_pktsz)
360 PUTSHORT(daemon->edns_pktsz, psave);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100361 }
362
363 /* Complain loudly if the upstream server is non-recursive. */
364 if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0)
365 {
366 char addrbuff[ADDRSTRLEN];
367#ifdef HAVE_IPV6
368 if (serveraddr->sa.sa_family == AF_INET)
369 inet_ntop(AF_INET, &serveraddr->in.sin_addr, addrbuff, ADDRSTRLEN);
370 else if (serveraddr->sa.sa_family == AF_INET6)
371 inet_ntop(AF_INET6, &serveraddr->in6.sin6_addr, addrbuff, ADDRSTRLEN);
372#else
373 strcpy(addrbuff, inet_ntoa(serveraddr->in.sin_addr));
374#endif
375 syslog(LOG_WARNING, "nameserver %s refused to do a recursive query", addrbuff);
376 return 0;
377 }
378
Simon Kelley36717ee2004-09-20 19:20:58 +0100379 if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
380 return n;
381
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100382 if (daemon->bogus_addr && header->rcode != NXDOMAIN &&
383 check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100384 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100385 munged = 1;
386 header->rcode = NXDOMAIN;
387 header->aa = 0;
Simon Kelley36717ee2004-09-20 19:20:58 +0100388 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100389 else
Simon Kelley36717ee2004-09-20 19:20:58 +0100390 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100391 if (header->rcode == NXDOMAIN &&
392 extract_request(header, n, daemon->namebuff, NULL) &&
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000393 check_for_local_domain(daemon->namebuff, now, daemon))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100394 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100395 /* if we forwarded a query for a locally known name (because it was for
396 an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
397 since we know that the domain exists, even if upstream doesn't */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100398 munged = 1;
399 header->aa = 1;
400 header->rcode = NOERROR;
Simon Kelley36717ee2004-09-20 19:20:58 +0100401 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100402
403 extract_addresses(header, n, daemon->namebuff, now, daemon);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100404 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100405
406 /* do this after extract_addresses. Ensure NODATA reply and remove
407 nameserver info. */
408
409 if (munged)
410 {
411 header->ancount = htons(0);
412 header->nscount = htons(0);
413 header->arcount = htons(0);
414 }
415
Simon Kelley36717ee2004-09-20 19:20:58 +0100416 /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
417 sections of the packet. Find the new length here and put back pseudoheader
418 if it was removed. */
419 return resize_packet(header, n, pheader, plen);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100420}
421
Simon Kelley3be34542004-09-11 19:12:13 +0100422/* sets new last_server */
423void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000424{
425 /* packet from peer server, extract data for cache, and send to
426 original requester */
427 struct frec *forward;
428 HEADER *header;
Simon Kelleyde379512004-06-22 20:23:33 +0100429 union mysockaddr serveraddr;
430 socklen_t addrlen = sizeof(serveraddr);
Simon Kelley3be34542004-09-11 19:12:13 +0100431 int n = recvfrom(sfd->fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
Simon Kelleyde379512004-06-22 20:23:33 +0100432
433 /* Determine the address of the server replying so that we can mark that as good */
434 serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family;
435#ifdef HAVE_IPV6
436 if (serveraddr.sa.sa_family == AF_INET6)
437 serveraddr.in6.sin6_flowinfo = htonl(0);
438#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000439
Simon Kelley3be34542004-09-11 19:12:13 +0100440 header = (HEADER *)daemon->packet;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100441 forward = lookup_frec(ntohs(header->id));
442
443 if (n >= (int)sizeof(HEADER) && header->qr && forward)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000444 {
Simon Kelleyde379512004-06-22 20:23:33 +0100445 /* find good server by address if possible, otherwise assume the last one we sent to */
446 if ((forward->sentto->flags & SERV_TYPE) == 0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000447 {
Simon Kelley3be34542004-09-11 19:12:13 +0100448 struct server *last_server;
449 daemon->last_server = forward->sentto;
450 for (last_server = daemon->servers; last_server; last_server = last_server->next)
Simon Kelleyde379512004-06-22 20:23:33 +0100451 if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
452 sockaddr_isequal(&last_server->addr, &serveraddr))
Simon Kelley3be34542004-09-11 19:12:13 +0100453 {
454 daemon->last_server = last_server;
455 break;
456 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000457 }
Simon Kelleyde379512004-06-22 20:23:33 +0100458
Simon Kelley36717ee2004-09-20 19:20:58 +0100459 if ((n = process_reply(daemon, header, now, &serveraddr, (unsigned int)n)))
Simon Kelley3be34542004-09-11 19:12:13 +0100460 {
461 header->id = htons(forward->orig_id);
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100462 header->ra = 1; /* recursion if available */
Simon Kelley26128d22004-11-14 16:43:54 +0000463 send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n,
Simon Kelley3be34542004-09-11 19:12:13 +0100464 &forward->source, &forward->dest, forward->iface);
465 forward->new_id = 0; /* cancel */
466 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000467 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000468}
Simon Kelley44a2a312004-03-10 20:04:35 +0000469
Simon Kelley3be34542004-09-11 19:12:13 +0100470void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
Simon Kelley44a2a312004-03-10 20:04:35 +0000471{
Simon Kelley3be34542004-09-11 19:12:13 +0100472 HEADER *header = (HEADER *)daemon->packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000473 union mysockaddr source_addr;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100474 unsigned short type;
Simon Kelley44a2a312004-03-10 20:04:35 +0000475 struct iname *tmp;
476 struct all_addr dst_addr;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000477 struct in_addr netmask, dst_addr_4;
Simon Kelley8a911cc2004-03-16 18:35:52 +0000478 int m, n, if_index = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000479 struct iovec iov[1];
480 struct msghdr msg;
481 struct cmsghdr *cmptr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000482 union {
483 struct cmsghdr align; /* this ensures alignment */
484#ifdef HAVE_IPV6
485 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
486#endif
487#if defined(IP_PKTINFO)
488 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
489#elif defined(IP_RECVDSTADDR)
490 char control[CMSG_SPACE(sizeof(struct in_addr)) +
491 CMSG_SPACE(sizeof(struct sockaddr_dl))];
492#endif
493 } control_u;
494
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000495 if (listen->family == AF_INET && (daemon->options & OPT_NOWILD))
496 {
497 dst_addr_4 = listen->iface->addr.in.sin_addr;
498 netmask = listen->iface->netmask;
499 }
500 else
501 dst_addr_4.s_addr = 0;
502
Simon Kelley3be34542004-09-11 19:12:13 +0100503 iov[0].iov_base = daemon->packet;
504 iov[0].iov_len = daemon->edns_pktsz;
Simon Kelley44a2a312004-03-10 20:04:35 +0000505
506 msg.msg_control = control_u.control;
507 msg.msg_controllen = sizeof(control_u);
508 msg.msg_flags = 0;
509 msg.msg_name = &source_addr;
510 msg.msg_namelen = sizeof(source_addr);
511 msg.msg_iov = iov;
512 msg.msg_iovlen = 1;
513
Simon Kelleyde379512004-06-22 20:23:33 +0100514 if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
Simon Kelley3be34542004-09-11 19:12:13 +0100515 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000516
Simon Kelley44a2a312004-03-10 20:04:35 +0000517 if (n < (int)sizeof(HEADER) || header->qr)
Simon Kelley3be34542004-09-11 19:12:13 +0100518 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000519
Simon Kelley26128d22004-11-14 16:43:54 +0000520 source_addr.sa.sa_family = listen->family;
521#ifdef HAVE_IPV6
522 if (listen->family == AF_INET6)
523 source_addr.in6.sin6_flowinfo = htonl(0);
524#endif
525
526 if (!(daemon->options & OPT_NOWILD))
Simon Kelley44a2a312004-03-10 20:04:35 +0000527 {
Simon Kelley8a911cc2004-03-16 18:35:52 +0000528 struct ifreq ifr;
529
Simon Kelley26128d22004-11-14 16:43:54 +0000530 if (msg.msg_controllen < sizeof(struct cmsghdr))
531 return;
532
533#if defined(IP_PKTINFO)
534 if (listen->family == AF_INET)
535 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
536 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
537 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000538 dst_addr_4 = dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
Simon Kelley26128d22004-11-14 16:43:54 +0000539 if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
540 }
541#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
542 if (listen->family == AF_INET)
543 {
544 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
545 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000546 dst_addr_4 = dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
Simon Kelley26128d22004-11-14 16:43:54 +0000547 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
548 if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
549 }
550#endif
551
552#ifdef HAVE_IPV6
553 if (listen->family == AF_INET6)
554 {
555 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
556 if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
557 {
558 dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
559 if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
560 }
561 }
562#endif
563
564 /* enforce available interface configuration */
565
Simon Kelley8a911cc2004-03-16 18:35:52 +0000566 if (if_index == 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100567 return;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000568
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000569 if (daemon->if_except || daemon->if_names || (daemon->options & OPT_LOCALISE))
Simon Kelley8a911cc2004-03-16 18:35:52 +0000570 {
571#ifdef SIOCGIFNAME
572 ifr.ifr_ifindex = if_index;
573 if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
Simon Kelley3be34542004-09-11 19:12:13 +0100574 return;
Simon Kelley8a911cc2004-03-16 18:35:52 +0000575#else
576 if (!if_indextoname(if_index, ifr.ifr_name))
Simon Kelley3be34542004-09-11 19:12:13 +0100577 return;
Simon Kelley8a911cc2004-03-16 18:35:52 +0000578#endif
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000579
580 if (listen->family == AF_INET &&
581 (daemon->options & OPT_LOCALISE) &&
582 ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
583 return;
584
585 netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
Simon Kelley8a911cc2004-03-16 18:35:52 +0000586 }
587
Simon Kelley3be34542004-09-11 19:12:13 +0100588 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
Simon Kelley8a911cc2004-03-16 18:35:52 +0000589 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
Simon Kelley3be34542004-09-11 19:12:13 +0100590 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000591
Simon Kelley3be34542004-09-11 19:12:13 +0100592 if (daemon->if_names || daemon->if_addrs)
Simon Kelley44a2a312004-03-10 20:04:35 +0000593 {
Simon Kelley3be34542004-09-11 19:12:13 +0100594 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
Simon Kelley8a911cc2004-03-16 18:35:52 +0000595 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000596 break;
597 if (!tmp)
Simon Kelley3be34542004-09-11 19:12:13 +0100598 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley44a2a312004-03-10 20:04:35 +0000599 if (tmp->addr.sa.sa_family == listen->family)
600 {
601 if (tmp->addr.sa.sa_family == AF_INET &&
602 tmp->addr.in.sin_addr.s_addr == dst_addr.addr.addr4.s_addr)
603 break;
604#ifdef HAVE_IPV6
605 else if (tmp->addr.sa.sa_family == AF_INET6 &&
606 memcmp(&tmp->addr.in6.sin6_addr,
607 &dst_addr.addr.addr6,
608 sizeof(struct in6_addr)) == 0)
609 break;
610#endif
611 }
612 if (!tmp)
Simon Kelley3be34542004-09-11 19:12:13 +0100613 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000614 }
615 }
616
Simon Kelley3be34542004-09-11 19:12:13 +0100617 if (extract_request(header, (unsigned int)n, daemon->namebuff, &type))
Simon Kelley44a2a312004-03-10 20:04:35 +0000618 {
619 if (listen->family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100620 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100621 (struct all_addr *)&source_addr.in.sin_addr, type, NULL, 0);
Simon Kelley44a2a312004-03-10 20:04:35 +0000622#ifdef HAVE_IPV6
623 else
Simon Kelley3be34542004-09-11 19:12:13 +0100624 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100625 (struct all_addr *)&source_addr.in6.sin6_addr, type, NULL, 0);
Simon Kelley44a2a312004-03-10 20:04:35 +0000626#endif
627 }
628
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000629 m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, daemon,
630 dst_addr_4, netmask, now);
Simon Kelley44a2a312004-03-10 20:04:35 +0000631 if (m >= 1)
Simon Kelley3be34542004-09-11 19:12:13 +0100632 send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index);
Simon Kelley44a2a312004-03-10 20:04:35 +0000633 else
Simon Kelley3be34542004-09-11 19:12:13 +0100634 forward_query(daemon, listen->fd, &source_addr, &dst_addr, if_index,
635 header, n, now);
Simon Kelley44a2a312004-03-10 20:04:35 +0000636}
637
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100638static int read_write(int fd, char *packet, int size, int rw)
639{
640 int n, done;
641
642 for (done = 0; done < size; done += n)
643 {
644 retry:
645 if (rw)
646 n = read(fd, &packet[done], (size_t)(size - done));
647 else
648 n = write(fd, &packet[done], (size_t)(size - done));
649
650 if (n == 0)
651 return 0;
652 else if (n == -1)
653 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100654 if (retry_send())
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100655 goto retry;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100656 else
657 return 0;
658 }
659 }
660 return 1;
661}
662
663/* The daemon forks before calling this: it should deal with one connection,
664 blocking as neccessary, and then return. Note, need to be a bit careful
665 about resources for debug mode, when the fork is suppressed: that's
666 done by the caller. */
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000667char *tcp_request(struct daemon *daemon, int confd, time_t now,
668 struct in_addr local_addr, struct in_addr netmask)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100669{
670 int size = 0, m;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100671 unsigned short qtype, gotname;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100672 unsigned char c1, c2;
673 /* Max TCP packet + slop */
674 char *packet = malloc(65536 + MAXDNAME + RRFIXEDSZ);
675 HEADER *header;
Simon Kelley3be34542004-09-11 19:12:13 +0100676 struct server *last_server;
677
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100678 while (1)
679 {
680 if (!packet ||
681 !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
682 !(size = c1 << 8 | c2) ||
683 !read_write(confd, packet, size, 1))
684 return packet;
685
686 if (size < (int)sizeof(HEADER))
687 continue;
688
689 header = (HEADER *)packet;
690
Simon Kelley3be34542004-09-11 19:12:13 +0100691 if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100692 {
693 union mysockaddr peer_addr;
694 socklen_t peer_len = sizeof(union mysockaddr);
695
696 if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
697 {
698 if (peer_addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100699 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100700 (struct all_addr *)&peer_addr.in.sin_addr, qtype, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100701#ifdef HAVE_IPV6
702 else
Simon Kelley3be34542004-09-11 19:12:13 +0100703 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100704 (struct all_addr *)&peer_addr.in6.sin6_addr, qtype, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100705#endif
706 }
707 }
708
709 /* m > 0 if answered from cache */
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000710 m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, daemon,
711 local_addr, netmask, now);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100712
713 if (m == 0)
714 {
715 unsigned short flags = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100716 struct all_addr *addrp = NULL;
717 int type = 0;
718 char *domain = NULL;
719
720 if (gotname)
Simon Kelley36717ee2004-09-20 19:20:58 +0100721 flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100722
Simon Kelley3be34542004-09-11 19:12:13 +0100723 if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server)
724 last_server = daemon->servers;
725 else
726 last_server = daemon->last_server;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100727
728 if (!flags && last_server)
729 {
730 struct server *firstsendto = NULL;
731
732 /* Loop round available servers until we succeed in connecting to one.
733 Note that this code subtley ensures that consecutive queries on this connection
734 which can go to the same server, do so. */
735 while (1)
736 {
737 if (!firstsendto)
738 firstsendto = last_server;
739 else
740 {
741 if (!(last_server = last_server->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100742 last_server = daemon->servers;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100743
744 if (last_server == firstsendto)
745 break;
746 }
747
748 /* server for wrong domain */
749 if (type != (last_server->flags & SERV_TYPE) ||
750 (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
751 continue;
752
753 if ((last_server->tcpfd == -1) &&
754 (last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
755 connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1)
756 {
757 close(last_server->tcpfd);
758 last_server->tcpfd = -1;
759 }
760
761 if (last_server->tcpfd == -1)
762 continue;
763
764 c1 = size >> 8;
765 c2 = size;
766
767 if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
768 !read_write(last_server->tcpfd, &c2, 1, 0) ||
769 !read_write(last_server->tcpfd, packet, size, 0) ||
770 !read_write(last_server->tcpfd, &c1, 1, 1) ||
771 !read_write(last_server->tcpfd, &c2, 1, 1))
772 {
773 close(last_server->tcpfd);
774 last_server->tcpfd = -1;
775 continue;
776 }
777
778 m = (c1 << 8) | c2;
779 if (!read_write(last_server->tcpfd, packet, m, 1))
780 return packet;
781
782 if (!gotname)
Simon Kelley3be34542004-09-11 19:12:13 +0100783 strcpy(daemon->namebuff, "query");
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100784 if (last_server->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100785 log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100786 (struct all_addr *)&last_server->addr.in.sin_addr, 0, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100787#ifdef HAVE_IPV6
788 else
Simon Kelley3be34542004-09-11 19:12:13 +0100789 log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100790 (struct all_addr *)&last_server->addr.in6.sin6_addr, 0, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100791#endif
792
793 /* There's no point in updating the cache, since this process will exit and
794 lose the information after one query. We make this call for the alias and
795 bogus-nxdomain side-effects. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100796 m = process_reply(daemon, header, now, &last_server->addr, (unsigned int)m);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100797
798 break;
799 }
800 }
801
802 /* In case of local answer or no connections made. */
803 if (m == 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100804 m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100805 }
806
807 c1 = m>>8;
808 c2 = m;
809 if (!read_write(confd, &c1, 1, 0) ||
810 !read_write(confd, &c2, 1, 0) ||
811 !read_write(confd, packet, m, 0))
812 return packet;
813 }
814}
815
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000816static struct frec *get_new_frec(time_t now)
817{
818 struct frec *f = frec_list, *oldest = NULL;
819 time_t oldtime = now;
820 int count = 0;
821 static time_t warntime = 0;
822
823 while (f)
824 {
825 if (f->new_id == 0)
826 {
827 f->time = now;
828 return f;
829 }
830
831 if (difftime(f->time, oldtime) <= 0)
832 {
833 oldtime = f->time;
834 oldest = f;
835 }
836
837 count++;
838 f = f->next;
839 }
840
841 /* can't find empty one, use oldest if there is one
842 and it's older than timeout */
843 if (oldest && difftime(now, oldtime) > TIMEOUT)
844 {
845 oldest->time = now;
846 return oldest;
847 }
848
849 if (count > FTABSIZ)
850 { /* limit logging rate so syslog isn't DOSed either */
851 if (!warntime || difftime(now, warntime) > LOGRATE)
852 {
853 warntime = now;
854 syslog(LOG_WARNING, "forwarding table overflow: check for server loops.");
855 }
856 return NULL;
857 }
858
859 if ((f = (struct frec *)malloc(sizeof(struct frec))))
860 {
861 f->next = frec_list;
862 f->time = now;
863 frec_list = f;
864 }
865 return f; /* OK if malloc fails and this is NULL */
866}
867
868static struct frec *lookup_frec(unsigned short id)
869{
870 struct frec *f;
871
872 for(f = frec_list; f; f = f->next)
873 if (f->new_id == id)
874 return f;
875
876 return NULL;
877}
878
879static struct frec *lookup_frec_by_sender(unsigned short id,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100880 union mysockaddr *addr,
881 unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000882{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100883 struct frec *f;
884
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000885 for(f = frec_list; f; f = f->next)
886 if (f->new_id &&
887 f->orig_id == id &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100888 f->crc == crc &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000889 sockaddr_isequal(&f->source, addr))
890 return f;
891
892 return NULL;
893}
894
895
896/* return unique random ids between 1 and 65535 */
897static unsigned short get_id(void)
898{
899 unsigned short ret = 0;
900
901 while (ret == 0)
902 {
903 ret = rand16();
904
905 /* scrap ids already in use */
906 if ((ret != 0) && lookup_frec(ret))
907 ret = 0;
908 }
909
910 return ret;
911}
912
913
914
915
916