blob: bdf6bfb3840d8b3709cb94092138f4d8aa56886b [file] [log] [blame]
Simon Kelley28866e92011-02-14 20:19:14 +00001/* dnsmasq is Copyright (c) 2000-2011 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
Simon Kelley824af852008-02-12 20:43:05 +00005 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
7
Simon Kelley9e4abcb2004-01-22 19:47:41 +00008 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
Simon Kelley824af852008-02-12 20:43:05 +000012
Simon Kelley73a08a22009-02-05 20:28:08 +000013 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
Simon Kelley9e4abcb2004-01-22 19:47:41 +000015*/
16
Simon Kelley9e4abcb2004-01-22 19:47:41 +000017#include "dnsmasq.h"
18
Simon Kelley832af0b2007-01-21 20:01:28 +000019static struct frec *lookup_frec(unsigned short id, unsigned int crc);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000020static struct frec *lookup_frec_by_sender(unsigned short id,
Simon Kelleyfd9fa482004-10-21 20:24:00 +010021 union mysockaddr *addr,
22 unsigned int crc);
Simon Kelley316e2732010-01-22 20:16:09 +000023static unsigned short get_id(unsigned int crc);
Simon Kelley1a6bca82008-07-11 11:11:42 +010024static void free_frec(struct frec *f);
25static struct randfd *allocate_rfd(int family);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000026
Simon Kelley824af852008-02-12 20:43:05 +000027/* Send a UDP packet with its source address set as "source"
Simon Kelley44a2a312004-03-10 20:04:35 +000028 unless nowild is true, when we just send it with the kernel default */
Simon Kelleycdeda282006-03-16 20:16:06 +000029static void send_from(int fd, int nowild, char *packet, size_t len,
Simon Kelleydfa666f2004-08-02 18:27:27 +010030 union mysockaddr *to, struct all_addr *source,
31 unsigned int iface)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000032{
Simon Kelley44a2a312004-03-10 20:04:35 +000033 struct msghdr msg;
34 struct iovec iov[1];
Simon Kelley44a2a312004-03-10 20:04:35 +000035 union {
36 struct cmsghdr align; /* this ensures alignment */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010037#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +000038 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
39#elif defined(IP_SENDSRCADDR)
40 char control[CMSG_SPACE(sizeof(struct in_addr))];
41#endif
42#ifdef HAVE_IPV6
43 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
44#endif
45 } control_u;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010046
Simon Kelley44a2a312004-03-10 20:04:35 +000047 iov[0].iov_base = packet;
48 iov[0].iov_len = len;
49
Simon Kelleyfeba5c12004-07-27 20:28:58 +010050 msg.msg_control = NULL;
51 msg.msg_controllen = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +000052 msg.msg_flags = 0;
53 msg.msg_name = to;
54 msg.msg_namelen = sa_len(to);
55 msg.msg_iov = iov;
56 msg.msg_iovlen = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010057
Simon Kelley26128d22004-11-14 16:43:54 +000058 if (!nowild)
Simon Kelleyfeba5c12004-07-27 20:28:58 +010059 {
Simon Kelley26128d22004-11-14 16:43:54 +000060 struct cmsghdr *cmptr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010061 msg.msg_control = &control_u;
62 msg.msg_controllen = sizeof(control_u);
Simon Kelley26128d22004-11-14 16:43:54 +000063 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley44a2a312004-03-10 20:04:35 +000064
Simon Kelley26128d22004-11-14 16:43:54 +000065 if (to->sa.sa_family == AF_INET)
66 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010067#if defined(HAVE_LINUX_NETWORK)
Simon Kelley8ef5ada2010-06-03 19:42:45 +010068 struct in_pktinfo p;
69 p.ipi_ifindex = 0;
70 p.ipi_spec_dst = source->addr.addr4;
71 memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
Simon Kelley26128d22004-11-14 16:43:54 +000072 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
73 cmptr->cmsg_level = SOL_IP;
74 cmptr->cmsg_type = IP_PKTINFO;
75#elif defined(IP_SENDSRCADDR)
Simon Kelley8ef5ada2010-06-03 19:42:45 +010076 memcpy(CMSG_DATA(cmptr), &(source->addr.addr4), sizeof(source->addr.addr4));
Simon Kelley26128d22004-11-14 16:43:54 +000077 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
78 cmptr->cmsg_level = IPPROTO_IP;
79 cmptr->cmsg_type = IP_SENDSRCADDR;
Simon Kelley44a2a312004-03-10 20:04:35 +000080#endif
Simon Kelley26128d22004-11-14 16:43:54 +000081 }
Simon Kelley26128d22004-11-14 16:43:54 +000082 else
Simon Kelleyb8187c82005-11-26 21:46:27 +000083#ifdef HAVE_IPV6
Simon Kelley26128d22004-11-14 16:43:54 +000084 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +010085 struct in6_pktinfo p;
86 p.ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
87 p.ipi6_addr = source->addr.addr6;
88 memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
Simon Kelley26128d22004-11-14 16:43:54 +000089 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
Simon Kelley316e2732010-01-22 20:16:09 +000090 cmptr->cmsg_type = daemon->v6pktinfo;
Simon Kelley26128d22004-11-14 16:43:54 +000091 cmptr->cmsg_level = IPV6_LEVEL;
92 }
Simon Kelley3d8df262005-08-29 12:19:27 +010093#else
94 iface = 0; /* eliminate warning */
Simon Kelley26128d22004-11-14 16:43:54 +000095#endif
96 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +010097
Simon Kelleyfd9fa482004-10-21 20:24:00 +010098 retry:
99 if (sendmsg(fd, &msg, 0) == -1)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100100 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100101 /* certain Linux kernels seem to object to setting the source address in the IPv6 stack
102 by returning EINVAL from sendmsg. In that case, try again without setting the
103 source address, since it will nearly alway be correct anyway. IPv6 stinks. */
104 if (errno == EINVAL && msg.msg_controllen)
105 {
106 msg.msg_controllen = 0;
107 goto retry;
108 }
109 if (retry_send())
110 goto retry;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100111 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000112}
113
Simon Kelley28866e92011-02-14 20:19:14 +0000114static unsigned int search_servers(time_t now, struct all_addr **addrpp,
115 unsigned int qtype, char *qdomain, int *type, char **domain, int *norebind)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100116
117{
118 /* If the query ends in the domain in one of our servers, set
119 domain to point to that name. We find the largest match to allow both
120 domain.org and sub.domain.org to exist. */
121
122 unsigned int namelen = strlen(qdomain);
123 unsigned int matchlen = 0;
124 struct server *serv;
Simon Kelley28866e92011-02-14 20:19:14 +0000125 unsigned int flags = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100126
Simon Kelley3be34542004-09-11 19:12:13 +0100127 for (serv = daemon->servers; serv; serv=serv->next)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100128 /* domain matches take priority over NODOTS matches */
Simon Kelley3d8df262005-08-29 12:19:27 +0100129 if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') && namelen != 0)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100130 {
Simon Kelley28866e92011-02-14 20:19:14 +0000131 unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100132 *type = SERV_FOR_NODOTS;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100133 if (serv->flags & SERV_NO_ADDR)
Simon Kelley36717ee2004-09-20 19:20:58 +0100134 flags = F_NXDOMAIN;
135 else if (serv->flags & SERV_LITERAL_ADDRESS)
136 {
137 if (sflag & qtype)
138 {
139 flags = sflag;
140 if (serv->addr.sa.sa_family == AF_INET)
141 *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100142#ifdef HAVE_IPV6
Simon Kelley36717ee2004-09-20 19:20:58 +0100143 else
144 *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100145#endif
Simon Kelley36717ee2004-09-20 19:20:58 +0100146 }
Simon Kelley824af852008-02-12 20:43:05 +0000147 else if (!flags || (flags & F_NXDOMAIN))
Simon Kelley36717ee2004-09-20 19:20:58 +0100148 flags = F_NOERR;
149 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100150 }
151 else if (serv->flags & SERV_HAS_DOMAIN)
152 {
153 unsigned int domainlen = strlen(serv->domain);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000154 char *matchstart = qdomain + namelen - domainlen;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100155 if (namelen >= domainlen &&
Simon Kelleyb8187c82005-11-26 21:46:27 +0000156 hostname_isequal(matchstart, serv->domain) &&
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100157 (domainlen == 0 || namelen == domainlen || *(matchstart-1) == '.' ))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100158 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100159 if (serv->flags & SERV_NO_REBIND)
160 *norebind = 1;
Simon Kelley28866e92011-02-14 20:19:14 +0000161 else
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100162 {
Simon Kelley28866e92011-02-14 20:19:14 +0000163 unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
164 /* implement priority rules for --address and --server for same domain.
165 --address wins if the address is for the correct AF
166 --server wins otherwise. */
167 if (domainlen != 0 && domainlen == matchlen)
Simon Kelley36717ee2004-09-20 19:20:58 +0100168 {
Simon Kelley28866e92011-02-14 20:19:14 +0000169 if ((serv->flags & SERV_LITERAL_ADDRESS))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100170 {
Simon Kelley28866e92011-02-14 20:19:14 +0000171 if (!(sflag & qtype) && flags == 0)
172 continue;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100173 }
Simon Kelley28866e92011-02-14 20:19:14 +0000174 else
175 {
176 if (flags & (F_IPV4 | F_IPV6))
177 continue;
178 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100179 }
Simon Kelley28866e92011-02-14 20:19:14 +0000180
181 if (domainlen >= matchlen)
182 {
183 *type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND);
184 *domain = serv->domain;
185 matchlen = domainlen;
186 if (serv->flags & SERV_NO_ADDR)
187 flags = F_NXDOMAIN;
188 else if (serv->flags & SERV_LITERAL_ADDRESS)
189 {
190 if (sflag & qtype)
191 {
192 flags = sflag;
193 if (serv->addr.sa.sa_family == AF_INET)
194 *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
195#ifdef HAVE_IPV6
196 else
197 *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
198#endif
199 }
200 else if (!flags || (flags & F_NXDOMAIN))
201 flags = F_NOERR;
202 }
203 else
204 flags = 0;
205 }
206 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100207 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100208 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100209
Simon Kelley7de060b2011-08-26 17:24:52 +0100210 if (flags == 0 && !(qtype & F_QUERY) &&
Simon Kelley28866e92011-02-14 20:19:14 +0000211 option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
Simon Kelley7de060b2011-08-26 17:24:52 +0100212 /* don't forward A or AAAA queries for simple names, except the empty name */
213 flags = F_NOERR;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100214
Simon Kelley5aabfc72007-08-29 11:24:47 +0100215 if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now))
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100216 flags = F_NOERR;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100217
Simon Kelley824af852008-02-12 20:43:05 +0000218 if (flags)
219 {
220 int logflags = 0;
221
222 if (flags == F_NXDOMAIN || flags == F_NOERR)
223 logflags = F_NEG | qtype;
224
Simon Kelley1a6bca82008-07-11 11:11:42 +0100225 log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL);
Simon Kelley824af852008-02-12 20:43:05 +0000226 }
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100227 else if ((*type) & SERV_USE_RESOLV)
228 {
229 *type = 0; /* use normal servers for this domain */
230 *domain = NULL;
231 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100232 return flags;
233}
Simon Kelley44a2a312004-03-10 20:04:35 +0000234
Simon Kelley824af852008-02-12 20:43:05 +0000235static int forward_query(int udpfd, union mysockaddr *udpaddr,
236 struct all_addr *dst_addr, unsigned int dst_iface,
Simon Kelley572b41e2011-02-18 18:11:18 +0000237 struct dns_header *header, size_t plen, time_t now, struct frec *forward)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000238{
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000239 char *domain = NULL;
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100240 int type = 0, norebind = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000241 struct all_addr *addrp = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +0000242 unsigned int crc = questions_crc(header, plen, daemon->namebuff);
Simon Kelley28866e92011-02-14 20:19:14 +0000243 unsigned int flags = 0;
244 unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
Simon Kelleyde379512004-06-22 20:23:33 +0100245 struct server *start = NULL;
Simon Kelley7de060b2011-08-26 17:24:52 +0100246
Simon Kelley28866e92011-02-14 20:19:14 +0000247 /* RFC 4035: sect 4.6 para 2 */
Simon Kelley572b41e2011-02-18 18:11:18 +0000248 header->hb4 &= ~HB4_AD;
249
Simon Kelley3d8df262005-08-29 12:19:27 +0100250 /* may be no servers available. */
251 if (!daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000252 forward = NULL;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000253 else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000254 {
Simon Kelleyde379512004-06-22 20:23:33 +0100255 /* retry on existing query, send to all available servers */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000256 domain = forward->sentto->domain;
Simon Kelley824af852008-02-12 20:43:05 +0000257 forward->sentto->failed_queries++;
Simon Kelley28866e92011-02-14 20:19:14 +0000258 if (!option_bool(OPT_ORDER))
Simon Kelleyde379512004-06-22 20:23:33 +0100259 {
Simon Kelley0a852542005-03-23 20:28:59 +0000260 forward->forwardall = 1;
Simon Kelley3be34542004-09-11 19:12:13 +0100261 daemon->last_server = NULL;
Simon Kelleyde379512004-06-22 20:23:33 +0100262 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000263 type = forward->sentto->flags & SERV_TYPE;
Simon Kelleyde379512004-06-22 20:23:33 +0100264 if (!(start = forward->sentto->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100265 start = daemon->servers; /* at end of list, recycle */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000266 header->id = htons(forward->new_id);
267 }
268 else
269 {
270 if (gotname)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100271 flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000272
Simon Kelley5aabfc72007-08-29 11:24:47 +0100273 if (!flags && !(forward = get_new_frec(now, NULL)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100274 /* table full - server failure. */
275 flags = F_NEG;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000276
277 if (forward)
278 {
Simon Kelley0a852542005-03-23 20:28:59 +0000279 forward->source = *udpaddr;
280 forward->dest = *dst_addr;
281 forward->iface = dst_iface;
Simon Kelley0a852542005-03-23 20:28:59 +0000282 forward->orig_id = ntohs(header->id);
Simon Kelley316e2732010-01-22 20:16:09 +0000283 forward->new_id = get_id(crc);
Simon Kelley832af0b2007-01-21 20:01:28 +0000284 forward->fd = udpfd;
Simon Kelley0a852542005-03-23 20:28:59 +0000285 forward->crc = crc;
286 forward->forwardall = 0;
Simon Kelley28866e92011-02-14 20:19:14 +0000287 if (norebind)
288 forward->flags |= FREC_NOREBIND;
Simon Kelley572b41e2011-02-18 18:11:18 +0000289 if (header->hb4 & HB4_CD)
Simon Kelley28866e92011-02-14 20:19:14 +0000290 forward->flags |= FREC_CHECKING_DISABLED;
Simon Kelley0a852542005-03-23 20:28:59 +0000291
Simon Kelley28866e92011-02-14 20:19:14 +0000292 header->id = htons(forward->new_id);
293
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100294 /* In strict_order mode, always try servers in the order
295 specified in resolv.conf, if a domain is given
296 always try all the available servers,
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000297 otherwise, use the one last known to work. */
298
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100299 if (type == 0)
300 {
Simon Kelley28866e92011-02-14 20:19:14 +0000301 if (option_bool(OPT_ORDER))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100302 start = daemon->servers;
303 else if (!(start = daemon->last_server) ||
304 daemon->forwardcount++ > FORWARD_TEST ||
305 difftime(now, daemon->forwardtime) > FORWARD_TIME)
306 {
307 start = daemon->servers;
308 forward->forwardall = 1;
309 daemon->forwardcount = 0;
310 daemon->forwardtime = now;
311 }
312 }
313 else
Simon Kelleyde379512004-06-22 20:23:33 +0100314 {
Simon Kelley3be34542004-09-11 19:12:13 +0100315 start = daemon->servers;
Simon Kelley28866e92011-02-14 20:19:14 +0000316 if (!option_bool(OPT_ORDER))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100317 forward->forwardall = 1;
Simon Kelleyde379512004-06-22 20:23:33 +0100318 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000319 }
320 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100321
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000322 /* check for send errors here (no route to host)
323 if we fail to send to all nameservers, send back an error
324 packet straight away (helps modem users when offline) */
325
326 if (!flags && forward)
327 {
Simon Kelleyde379512004-06-22 20:23:33 +0100328 struct server *firstsentto = start;
329 int forwarded = 0;
Simon Kelley28866e92011-02-14 20:19:14 +0000330
331 if (udpaddr && option_bool(OPT_ADD_MAC))
332 plen = add_mac(header, plen, ((char *) header) + PACKETSZ, udpaddr);
333
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000334 while (1)
335 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000336 /* only send to servers dealing with our domain.
337 domain may be NULL, in which case server->domain
338 must be NULL also. */
339
Simon Kelleyde379512004-06-22 20:23:33 +0100340 if (type == (start->flags & SERV_TYPE) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100341 (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
342 !(start->flags & SERV_LITERAL_ADDRESS))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000343 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100344 int fd;
345
346 /* find server socket to use, may need to get random one. */
347 if (start->sfd)
348 fd = start->sfd->fd;
349 else
350 {
351#ifdef HAVE_IPV6
352 if (start->addr.sa.sa_family == AF_INET6)
353 {
354 if (!forward->rfd6 &&
355 !(forward->rfd6 = allocate_rfd(AF_INET6)))
356 break;
Simon Kelley3927da42008-07-20 15:10:39 +0100357 daemon->rfd_save = forward->rfd6;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100358 fd = forward->rfd6->fd;
359 }
360 else
361#endif
362 {
363 if (!forward->rfd4 &&
364 !(forward->rfd4 = allocate_rfd(AF_INET)))
365 break;
Simon Kelley3927da42008-07-20 15:10:39 +0100366 daemon->rfd_save = forward->rfd4;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100367 fd = forward->rfd4->fd;
368 }
Simon Kelley7de060b2011-08-26 17:24:52 +0100369
370#ifdef HAVE_CONNTRACK
371 /* Copy connection mark of incoming query to outgoing connection. */
372 if (option_bool(OPT_CONNTRACK))
373 {
374 unsigned int mark;
375 if (get_incoming_mark(udpaddr, dst_addr, 0, &mark))
376 setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
377 }
378#endif
Simon Kelley1a6bca82008-07-11 11:11:42 +0100379 }
380
381 if (sendto(fd, (char *)header, plen, 0,
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100382 &start->addr.sa,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100383 sa_len(&start->addr)) == -1)
384 {
385 if (retry_send())
386 continue;
387 }
388 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000389 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000390 /* Keep info in case we want to re-send this packet */
391 daemon->srv_save = start;
392 daemon->packet_len = plen;
393
Simon Kelleyde379512004-06-22 20:23:33 +0100394 if (!gotname)
Simon Kelley3be34542004-09-11 19:12:13 +0100395 strcpy(daemon->namebuff, "query");
Simon Kelleyde379512004-06-22 20:23:33 +0100396 if (start->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100397 log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100398 (struct all_addr *)&start->addr.in.sin_addr, NULL);
Simon Kelleyde379512004-06-22 20:23:33 +0100399#ifdef HAVE_IPV6
400 else
Simon Kelley3be34542004-09-11 19:12:13 +0100401 log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100402 (struct all_addr *)&start->addr.in6.sin6_addr, NULL);
Simon Kelleyde379512004-06-22 20:23:33 +0100403#endif
Simon Kelley824af852008-02-12 20:43:05 +0000404 start->queries++;
Simon Kelleyde379512004-06-22 20:23:33 +0100405 forwarded = 1;
406 forward->sentto = start;
Simon Kelley0a852542005-03-23 20:28:59 +0000407 if (!forward->forwardall)
Simon Kelleyde379512004-06-22 20:23:33 +0100408 break;
Simon Kelley0a852542005-03-23 20:28:59 +0000409 forward->forwardall++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000410 }
411 }
412
Simon Kelleyde379512004-06-22 20:23:33 +0100413 if (!(start = start->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100414 start = daemon->servers;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000415
Simon Kelleyde379512004-06-22 20:23:33 +0100416 if (start == firstsentto)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000417 break;
418 }
419
Simon Kelleyde379512004-06-22 20:23:33 +0100420 if (forwarded)
Simon Kelley824af852008-02-12 20:43:05 +0000421 return 1;
Simon Kelleyde379512004-06-22 20:23:33 +0100422
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000423 /* could not send on, prepare to return */
424 header->id = htons(forward->orig_id);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100425 free_frec(forward); /* cancel */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000426 }
427
428 /* could not send on, return empty answer or address if known for whole domain */
Simon Kelleyb8187c82005-11-26 21:46:27 +0000429 if (udpfd != -1)
430 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000431 plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
Simon Kelley28866e92011-02-14 20:19:14 +0000432 send_from(udpfd, option_bool(OPT_NOWILD), (char *)header, plen, udpaddr, dst_addr, dst_iface);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000433 }
434
Simon Kelley824af852008-02-12 20:43:05 +0000435 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000436}
437
Simon Kelley572b41e2011-02-18 18:11:18 +0000438static size_t process_reply(struct dns_header *header, time_t now,
Simon Kelley28866e92011-02-14 20:19:14 +0000439 struct server *server, size_t n, int check_rebind, int checking_disabled)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100440{
Simon Kelley36717ee2004-09-20 19:20:58 +0100441 unsigned char *pheader, *sizep;
Simon Kelley832af0b2007-01-21 20:01:28 +0000442 int munged = 0, is_sign;
Simon Kelleycdeda282006-03-16 20:16:06 +0000443 size_t plen;
444
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100445 /* If upstream is advertising a larger UDP packet size
Simon Kelley9009d742008-11-14 20:04:27 +0000446 than we allow, trim it so that we don't get overlarge
447 requests for the client. We can't do this for signed packets. */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100448
Simon Kelley832af0b2007-01-21 20:01:28 +0000449 if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)) && !is_sign)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100450 {
451 unsigned short udpsz;
Simon Kelley36717ee2004-09-20 19:20:58 +0100452 unsigned char *psave = sizep;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100453
Simon Kelley36717ee2004-09-20 19:20:58 +0100454 GETSHORT(udpsz, sizep);
Simon Kelley3be34542004-09-11 19:12:13 +0100455 if (udpsz > daemon->edns_pktsz)
456 PUTSHORT(daemon->edns_pktsz, psave);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100457 }
458
Simon Kelley28866e92011-02-14 20:19:14 +0000459 /* RFC 4035 sect 4.6 para 3 */
460 if (!is_sign && !option_bool(OPT_DNSSEC))
Simon Kelley572b41e2011-02-18 18:11:18 +0000461 header->hb4 &= ~HB4_AD;
Simon Kelley28866e92011-02-14 20:19:14 +0000462
Simon Kelley572b41e2011-02-18 18:11:18 +0000463 if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
Simon Kelley36717ee2004-09-20 19:20:58 +0100464 return n;
465
Simon Kelley0a852542005-03-23 20:28:59 +0000466 /* Complain loudly if the upstream server is non-recursive. */
Simon Kelley572b41e2011-02-18 18:11:18 +0000467 if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR && ntohs(header->ancount) == 0 &&
Simon Kelley0a852542005-03-23 20:28:59 +0000468 server && !(server->flags & SERV_WARNED_RECURSIVE))
469 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100470 prettyprint_addr(&server->addr, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100471 my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
Simon Kelley28866e92011-02-14 20:19:14 +0000472 if (!option_bool(OPT_LOG))
Simon Kelley0a852542005-03-23 20:28:59 +0000473 server->flags |= SERV_WARNED_RECURSIVE;
474 }
475
Simon Kelley572b41e2011-02-18 18:11:18 +0000476 if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100477 check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100478 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100479 munged = 1;
Simon Kelley572b41e2011-02-18 18:11:18 +0000480 SET_RCODE(header, NXDOMAIN);
481 header->hb3 &= ~HB3_AA;
Simon Kelley36717ee2004-09-20 19:20:58 +0100482 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100483 else
Simon Kelley36717ee2004-09-20 19:20:58 +0100484 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000485 if (RCODE(header) == NXDOMAIN &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100486 extract_request(header, n, daemon->namebuff, NULL) &&
Simon Kelley5aabfc72007-08-29 11:24:47 +0100487 check_for_local_domain(daemon->namebuff, now))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100488 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100489 /* if we forwarded a query for a locally known name (because it was for
490 an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
491 since we know that the domain exists, even if upstream doesn't */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100492 munged = 1;
Simon Kelley572b41e2011-02-18 18:11:18 +0000493 header->hb3 |= HB3_AA;
494 SET_RCODE(header, NOERROR);
Simon Kelley36717ee2004-09-20 19:20:58 +0100495 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000496
Simon Kelley28866e92011-02-14 20:19:14 +0000497 if (extract_addresses(header, n, daemon->namebuff, now, is_sign, check_rebind, checking_disabled))
Simon Kelley824af852008-02-12 20:43:05 +0000498 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100499 my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
Simon Kelley824af852008-02-12 20:43:05 +0000500 munged = 1;
501 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100502 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100503
504 /* do this after extract_addresses. Ensure NODATA reply and remove
505 nameserver info. */
506
507 if (munged)
508 {
509 header->ancount = htons(0);
510 header->nscount = htons(0);
511 header->arcount = htons(0);
512 }
513
Simon Kelley36717ee2004-09-20 19:20:58 +0100514 /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
515 sections of the packet. Find the new length here and put back pseudoheader
516 if it was removed. */
517 return resize_packet(header, n, pheader, plen);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100518}
519
Simon Kelley3be34542004-09-11 19:12:13 +0100520/* sets new last_server */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100521void reply_query(int fd, int family, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000522{
523 /* packet from peer server, extract data for cache, and send to
524 original requester */
Simon Kelley572b41e2011-02-18 18:11:18 +0000525 struct dns_header *header;
Simon Kelleyde379512004-06-22 20:23:33 +0100526 union mysockaddr serveraddr;
Simon Kelley832af0b2007-01-21 20:01:28 +0000527 struct frec *forward;
Simon Kelleyde379512004-06-22 20:23:33 +0100528 socklen_t addrlen = sizeof(serveraddr);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100529 ssize_t n = recvfrom(fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
Simon Kelleycdeda282006-03-16 20:16:06 +0000530 size_t nn;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100531 struct server *server;
532
Simon Kelleycdeda282006-03-16 20:16:06 +0000533 /* packet buffer overwritten */
534 daemon->srv_save = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +0000535
Simon Kelleyde379512004-06-22 20:23:33 +0100536 /* Determine the address of the server replying so that we can mark that as good */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100537 serveraddr.sa.sa_family = family;
Simon Kelleyde379512004-06-22 20:23:33 +0100538#ifdef HAVE_IPV6
539 if (serveraddr.sa.sa_family == AF_INET6)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100540 serveraddr.in6.sin6_flowinfo = 0;
Simon Kelleyde379512004-06-22 20:23:33 +0100541#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000542
Simon Kelley1a6bca82008-07-11 11:11:42 +0100543 /* spoof check: answer must come from known server, */
544 for (server = daemon->servers; server; server = server->next)
545 if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
546 sockaddr_isequal(&server->addr, &serveraddr))
547 break;
548
Simon Kelley572b41e2011-02-18 18:11:18 +0000549 header = (struct dns_header *)daemon->packet;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100550
Simon Kelley1a6bca82008-07-11 11:11:42 +0100551 if (!server ||
Simon Kelley572b41e2011-02-18 18:11:18 +0000552 n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) ||
Simon Kelley1a6bca82008-07-11 11:11:42 +0100553 !(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
554 return;
555
556 server = forward->sentto;
557
Simon Kelley572b41e2011-02-18 18:11:18 +0000558 if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
Simon Kelley28866e92011-02-14 20:19:14 +0000559 !option_bool(OPT_ORDER) &&
Simon Kelley1a6bca82008-07-11 11:11:42 +0100560 forward->forwardall == 0)
561 /* for broken servers, attempt to send to another one. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000562 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100563 unsigned char *pheader;
564 size_t plen;
565 int is_sign;
Simon Kelley832af0b2007-01-21 20:01:28 +0000566
Simon Kelley1a6bca82008-07-11 11:11:42 +0100567 /* recreate query from reply */
568 pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign);
569 if (!is_sign)
Simon Kelley832af0b2007-01-21 20:01:28 +0000570 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100571 header->ancount = htons(0);
572 header->nscount = htons(0);
573 header->arcount = htons(0);
574 if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
575 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000576 header->hb3 &= ~(HB3_QR | HB3_TC);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100577 forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
578 return;
579 }
580 }
581 }
582
583 if ((forward->sentto->flags & SERV_TYPE) == 0)
584 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000585 if (RCODE(header) == SERVFAIL || RCODE(header) == REFUSED)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100586 server = NULL;
587 else
588 {
589 struct server *last_server;
Simon Kelley832af0b2007-01-21 20:01:28 +0000590
Simon Kelley1a6bca82008-07-11 11:11:42 +0100591 /* find good server by address if possible, otherwise assume the last one we sent to */
592 for (last_server = daemon->servers; last_server; last_server = last_server->next)
593 if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
594 sockaddr_isequal(&last_server->addr, &serveraddr))
595 {
596 server = last_server;
597 break;
598 }
599 }
Simon Kelley28866e92011-02-14 20:19:14 +0000600 if (!option_bool(OPT_ALL_SERVERS))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100601 daemon->last_server = server;
602 }
603
604 /* If the answer is an error, keep the forward record in place in case
605 we get a good reply from another server. Kill it when we've
606 had replies from all to avoid filling the forwarding table when
607 everything is broken */
608 if (forward->forwardall == 0 || --forward->forwardall == 1 ||
Simon Kelley572b41e2011-02-18 18:11:18 +0000609 (RCODE(header) != REFUSED && RCODE(header) != SERVFAIL))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100610 {
Simon Kelley28866e92011-02-14 20:19:14 +0000611 int check_rebind = !(forward->flags & FREC_NOREBIND);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100612
Simon Kelley28866e92011-02-14 20:19:14 +0000613 if (!option_bool(OPT_NO_REBIND))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100614 check_rebind = 0;
615
Simon Kelley28866e92011-02-14 20:19:14 +0000616 if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, forward->flags & FREC_CHECKING_DISABLED)))
Simon Kelley832af0b2007-01-21 20:01:28 +0000617 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100618 header->id = htons(forward->orig_id);
Simon Kelley572b41e2011-02-18 18:11:18 +0000619 header->hb4 |= HB4_RA; /* recursion if available */
Simon Kelley28866e92011-02-14 20:19:14 +0000620 send_from(forward->fd, option_bool(OPT_NOWILD), daemon->packet, nn,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100621 &forward->source, &forward->dest, forward->iface);
Simon Kelley832af0b2007-01-21 20:01:28 +0000622 }
Simon Kelley1a6bca82008-07-11 11:11:42 +0100623 free_frec(forward); /* cancel */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000624 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000625}
Simon Kelley44a2a312004-03-10 20:04:35 +0000626
Simon Kelley1a6bca82008-07-11 11:11:42 +0100627
Simon Kelley5aabfc72007-08-29 11:24:47 +0100628void receive_query(struct listener *listen, time_t now)
Simon Kelley44a2a312004-03-10 20:04:35 +0000629{
Simon Kelley572b41e2011-02-18 18:11:18 +0000630 struct dns_header *header = (struct dns_header *)daemon->packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000631 union mysockaddr source_addr;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100632 unsigned short type;
Simon Kelley44a2a312004-03-10 20:04:35 +0000633 struct all_addr dst_addr;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000634 struct in_addr netmask, dst_addr_4;
Simon Kelleycdeda282006-03-16 20:16:06 +0000635 size_t m;
636 ssize_t n;
637 int if_index = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000638 struct iovec iov[1];
639 struct msghdr msg;
640 struct cmsghdr *cmptr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000641 union {
642 struct cmsghdr align; /* this ensures alignment */
643#ifdef HAVE_IPV6
644 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
645#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100646#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000647 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000648#elif defined(IP_RECVDSTADDR) && defined(HAVE_SOLARIS_NETWORK)
649 char control[CMSG_SPACE(sizeof(struct in_addr)) +
650 CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley44a2a312004-03-10 20:04:35 +0000651#elif defined(IP_RECVDSTADDR)
652 char control[CMSG_SPACE(sizeof(struct in_addr)) +
653 CMSG_SPACE(sizeof(struct sockaddr_dl))];
654#endif
655 } control_u;
656
Simon Kelleycdeda282006-03-16 20:16:06 +0000657 /* packet buffer overwritten */
658 daemon->srv_save = NULL;
659
Simon Kelley28866e92011-02-14 20:19:14 +0000660 if (listen->family == AF_INET && option_bool(OPT_NOWILD))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000661 {
662 dst_addr_4 = listen->iface->addr.in.sin_addr;
663 netmask = listen->iface->netmask;
664 }
665 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100666 {
667 dst_addr_4.s_addr = 0;
668 netmask.s_addr = 0;
669 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000670
Simon Kelley3be34542004-09-11 19:12:13 +0100671 iov[0].iov_base = daemon->packet;
672 iov[0].iov_len = daemon->edns_pktsz;
Simon Kelley44a2a312004-03-10 20:04:35 +0000673
674 msg.msg_control = control_u.control;
675 msg.msg_controllen = sizeof(control_u);
676 msg.msg_flags = 0;
677 msg.msg_name = &source_addr;
678 msg.msg_namelen = sizeof(source_addr);
679 msg.msg_iov = iov;
680 msg.msg_iovlen = 1;
681
Simon Kelleyde379512004-06-22 20:23:33 +0100682 if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
Simon Kelley3be34542004-09-11 19:12:13 +0100683 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000684
Simon Kelley572b41e2011-02-18 18:11:18 +0000685 if (n < (int)sizeof(struct dns_header) ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100686 (msg.msg_flags & MSG_TRUNC) ||
Simon Kelley572b41e2011-02-18 18:11:18 +0000687 (header->hb3 & HB3_QR))
Simon Kelley3be34542004-09-11 19:12:13 +0100688 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000689
Simon Kelley26128d22004-11-14 16:43:54 +0000690 source_addr.sa.sa_family = listen->family;
691#ifdef HAVE_IPV6
692 if (listen->family == AF_INET6)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100693 source_addr.in6.sin6_flowinfo = 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000694#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000695
696 if (!option_bool(OPT_NOWILD))
Simon Kelley44a2a312004-03-10 20:04:35 +0000697 {
Simon Kelley8a911cc2004-03-16 18:35:52 +0000698 struct ifreq ifr;
699
Simon Kelley26128d22004-11-14 16:43:54 +0000700 if (msg.msg_controllen < sizeof(struct cmsghdr))
701 return;
702
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100703#if defined(HAVE_LINUX_NETWORK)
Simon Kelley26128d22004-11-14 16:43:54 +0000704 if (listen->family == AF_INET)
705 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
706 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
707 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100708 union {
709 unsigned char *c;
710 struct in_pktinfo *p;
711 } p;
712 p.c = CMSG_DATA(cmptr);
713 dst_addr_4 = dst_addr.addr.addr4 = p.p->ipi_spec_dst;
714 if_index = p.p->ipi_ifindex;
Simon Kelley26128d22004-11-14 16:43:54 +0000715 }
716#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
717 if (listen->family == AF_INET)
718 {
719 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100720 {
721 union {
722 unsigned char *c;
723 unsigned int *i;
724 struct in_addr *a;
725#ifndef HAVE_SOLARIS_NETWORK
726 struct sockaddr_dl *s;
Simon Kelley824af852008-02-12 20:43:05 +0000727#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100728 } p;
729 p.c = CMSG_DATA(cmptr);
730 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
731 dst_addr_4 = dst_addr.addr.addr4 = *(p.a);
732 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
733#ifdef HAVE_SOLARIS_NETWORK
734 if_index = *(p.i);
735#else
736 if_index = p.s->sdl_index;
737#endif
738 }
Simon Kelley26128d22004-11-14 16:43:54 +0000739 }
740#endif
741
742#ifdef HAVE_IPV6
743 if (listen->family == AF_INET6)
744 {
745 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelley316e2732010-01-22 20:16:09 +0000746 if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo)
Simon Kelley26128d22004-11-14 16:43:54 +0000747 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100748 union {
749 unsigned char *c;
750 struct in6_pktinfo *p;
751 } p;
752 p.c = CMSG_DATA(cmptr);
753
754 dst_addr.addr.addr6 = p.p->ipi6_addr;
755 if_index = p.p->ipi6_ifindex;
Simon Kelley26128d22004-11-14 16:43:54 +0000756 }
757 }
758#endif
759
760 /* enforce available interface configuration */
761
Simon Kelley7622fc02009-06-04 20:32:05 +0100762 if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
763 !iface_check(listen->family, &dst_addr, ifr.ifr_name, &if_index))
Simon Kelley832af0b2007-01-21 20:01:28 +0000764 return;
765
766 if (listen->family == AF_INET &&
Simon Kelley28866e92011-02-14 20:19:14 +0000767 option_bool(OPT_LOCALISE) &&
Simon Kelley832af0b2007-01-21 20:01:28 +0000768 ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
769 return;
770
771 netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000772 }
773
Simon Kelleycdeda282006-03-16 20:16:06 +0000774 if (extract_request(header, (size_t)n, daemon->namebuff, &type))
Simon Kelley44a2a312004-03-10 20:04:35 +0000775 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100776 char types[20];
777
778 querystr(types, type);
779
Simon Kelley44a2a312004-03-10 20:04:35 +0000780 if (listen->family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100781 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100782 (struct all_addr *)&source_addr.in.sin_addr, types);
Simon Kelley44a2a312004-03-10 20:04:35 +0000783#ifdef HAVE_IPV6
784 else
Simon Kelley3be34542004-09-11 19:12:13 +0100785 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100786 (struct all_addr *)&source_addr.in6.sin6_addr, types);
Simon Kelley44a2a312004-03-10 20:04:35 +0000787#endif
788 }
789
Simon Kelley5aabfc72007-08-29 11:24:47 +0100790 m = answer_request (header, ((char *) header) + PACKETSZ, (size_t)n,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000791 dst_addr_4, netmask, now);
Simon Kelley44a2a312004-03-10 20:04:35 +0000792 if (m >= 1)
Simon Kelley824af852008-02-12 20:43:05 +0000793 {
Simon Kelley28866e92011-02-14 20:19:14 +0000794 send_from(listen->fd, option_bool(OPT_NOWILD), (char *)header,
Simon Kelley824af852008-02-12 20:43:05 +0000795 m, &source_addr, &dst_addr, if_index);
796 daemon->local_answer++;
797 }
798 else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
799 header, (size_t)n, now, NULL))
800 daemon->queries_forwarded++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000801 else
Simon Kelley824af852008-02-12 20:43:05 +0000802 daemon->local_answer++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000803}
804
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100805/* The daemon forks before calling this: it should deal with one connection,
806 blocking as neccessary, and then return. Note, need to be a bit careful
807 about resources for debug mode, when the fork is suppressed: that's
808 done by the caller. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100809unsigned char *tcp_request(int confd, time_t now,
Simon Kelley7de060b2011-08-26 17:24:52 +0100810 union mysockaddr *local_addr, struct in_addr netmask)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100811{
Simon Kelley28866e92011-02-14 20:19:14 +0000812 size_t size = 0;
813 int norebind = 0;
814 int checking_disabled;
Simon Kelleycdeda282006-03-16 20:16:06 +0000815 size_t m;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100816 unsigned short qtype, gotname;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100817 unsigned char c1, c2;
818 /* Max TCP packet + slop */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100819 unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
Simon Kelley572b41e2011-02-18 18:11:18 +0000820 struct dns_header *header;
Simon Kelley3be34542004-09-11 19:12:13 +0100821 struct server *last_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100822 struct in_addr dst_addr_4;
823 union mysockaddr peer_addr;
824 socklen_t peer_len = sizeof(union mysockaddr);
Simon Kelley3be34542004-09-11 19:12:13 +0100825
Simon Kelley7de060b2011-08-26 17:24:52 +0100826 if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
827 return packet;
828
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100829 while (1)
830 {
831 if (!packet ||
832 !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
833 !(size = c1 << 8 | c2) ||
834 !read_write(confd, packet, size, 1))
835 return packet;
836
Simon Kelley572b41e2011-02-18 18:11:18 +0000837 if (size < (int)sizeof(struct dns_header))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100838 continue;
839
Simon Kelley572b41e2011-02-18 18:11:18 +0000840 header = (struct dns_header *)packet;
Simon Kelley28866e92011-02-14 20:19:14 +0000841
842 /* save state of "cd" flag in query */
Simon Kelley572b41e2011-02-18 18:11:18 +0000843 checking_disabled = header->hb4 & HB4_CD;
Simon Kelley28866e92011-02-14 20:19:14 +0000844
845 /* RFC 4035: sect 4.6 para 2 */
Simon Kelley572b41e2011-02-18 18:11:18 +0000846 header->hb4 &= ~HB4_AD;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100847
Simon Kelley3be34542004-09-11 19:12:13 +0100848 if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100849 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100850 char types[20];
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100851
Simon Kelley7de060b2011-08-26 17:24:52 +0100852 querystr(types, qtype);
853
854 if (peer_addr.sa.sa_family == AF_INET)
855 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
856 (struct all_addr *)&peer_addr.in.sin_addr, types);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100857#ifdef HAVE_IPV6
Simon Kelley7de060b2011-08-26 17:24:52 +0100858 else
859 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
860 (struct all_addr *)&peer_addr.in6.sin6_addr, types);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100861#endif
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100862 }
863
Simon Kelley7de060b2011-08-26 17:24:52 +0100864 if (local_addr->sa.sa_family == AF_INET)
865 dst_addr_4 = local_addr->in.sin_addr;
866 else
867 dst_addr_4.s_addr = 0;
868
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100869 /* m > 0 if answered from cache */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100870 m = answer_request(header, ((char *) header) + 65536, (unsigned int)size,
Simon Kelley7de060b2011-08-26 17:24:52 +0100871 dst_addr_4, netmask, now);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100872
873 /* Do this by steam now we're not in the select() loop */
874 check_log_writer(NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100875
876 if (m == 0)
877 {
Simon Kelley28866e92011-02-14 20:19:14 +0000878 unsigned int flags = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100879 struct all_addr *addrp = NULL;
880 int type = 0;
881 char *domain = NULL;
Simon Kelley28866e92011-02-14 20:19:14 +0000882
883 if (option_bool(OPT_ADD_MAC))
Simon Kelley7de060b2011-08-26 17:24:52 +0100884 size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
885
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100886 if (gotname)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100887 flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100888
Simon Kelley28866e92011-02-14 20:19:14 +0000889 if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
Simon Kelley3be34542004-09-11 19:12:13 +0100890 last_server = daemon->servers;
891 else
892 last_server = daemon->last_server;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100893
894 if (!flags && last_server)
895 {
896 struct server *firstsendto = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +0000897 unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
898
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100899 /* Loop round available servers until we succeed in connecting to one.
900 Note that this code subtley ensures that consecutive queries on this connection
901 which can go to the same server, do so. */
902 while (1)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100903 {
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100904 if (!firstsendto)
905 firstsendto = last_server;
906 else
907 {
908 if (!(last_server = last_server->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100909 last_server = daemon->servers;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100910
911 if (last_server == firstsendto)
912 break;
913 }
914
915 /* server for wrong domain */
916 if (type != (last_server->flags & SERV_TYPE) ||
917 (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
918 continue;
Simon Kelley7de060b2011-08-26 17:24:52 +0100919
920 if (last_server->tcpfd == -1)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100921 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100922 if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
923 continue;
924
925 if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
926 connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
927 {
928 close(last_server->tcpfd);
929 last_server->tcpfd = -1;
930 continue;
931 }
932
933#ifdef HAVE_CONNTRACK
934 /* Copy connection mark of incoming query to outgoing connection. */
935 if (option_bool(OPT_CONNTRACK))
936 {
937 unsigned int mark;
938 struct all_addr local;
939#ifdef HAVE_IPV6
940 if (local_addr->sa.sa_family == AF_INET6)
941 local.addr.addr6 = local_addr->in6.sin6_addr;
942 else
943#endif
944 local.addr.addr4 = local_addr->in.sin_addr;
945
946 if (get_incoming_mark(&peer_addr, &local, 1, &mark))
947 setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
948 }
949#endif
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100950 }
Simon Kelley824af852008-02-12 20:43:05 +0000951
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100952 c1 = size >> 8;
953 c2 = size;
954
955 if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
956 !read_write(last_server->tcpfd, &c2, 1, 0) ||
957 !read_write(last_server->tcpfd, packet, size, 0) ||
958 !read_write(last_server->tcpfd, &c1, 1, 1) ||
959 !read_write(last_server->tcpfd, &c2, 1, 1))
960 {
961 close(last_server->tcpfd);
962 last_server->tcpfd = -1;
963 continue;
964 }
Simon Kelley824af852008-02-12 20:43:05 +0000965
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100966 m = (c1 << 8) | c2;
967 if (!read_write(last_server->tcpfd, packet, m, 1))
968 return packet;
969
970 if (!gotname)
Simon Kelley3be34542004-09-11 19:12:13 +0100971 strcpy(daemon->namebuff, "query");
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100972 if (last_server->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100973 log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100974 (struct all_addr *)&last_server->addr.in.sin_addr, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100975#ifdef HAVE_IPV6
976 else
Simon Kelley3be34542004-09-11 19:12:13 +0100977 log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100978 (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100979#endif
980
981 /* There's no point in updating the cache, since this process will exit and
Simon Kelley832af0b2007-01-21 20:01:28 +0000982 lose the information after a few queries. We make this call for the alias and
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100983 bogus-nxdomain side-effects. */
Simon Kelley832af0b2007-01-21 20:01:28 +0000984 /* If the crc of the question section doesn't match the crc we sent, then
985 someone might be attempting to insert bogus values into the cache by
986 sending replies containing questions and bogus answers. */
987 if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
Simon Kelley28866e92011-02-14 20:19:14 +0000988 m = process_reply(header, now, last_server, (unsigned int)m,
989 option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100990
991 break;
992 }
993 }
994
995 /* In case of local answer or no connections made. */
996 if (m == 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100997 m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100998 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100999
1000 check_log_writer(NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001001
1002 c1 = m>>8;
1003 c2 = m;
1004 if (!read_write(confd, &c1, 1, 0) ||
1005 !read_write(confd, &c2, 1, 0) ||
1006 !read_write(confd, packet, m, 0))
1007 return packet;
1008 }
1009}
1010
Simon Kelley16972692006-10-16 20:04:18 +01001011static struct frec *allocate_frec(time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001012{
Simon Kelley16972692006-10-16 20:04:18 +01001013 struct frec *f;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001014
Simon Kelley5aabfc72007-08-29 11:24:47 +01001015 if ((f = (struct frec *)whine_malloc(sizeof(struct frec))))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001016 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001017 f->next = daemon->frec_list;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001018 f->time = now;
Simon Kelley832af0b2007-01-21 20:01:28 +00001019 f->sentto = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001020 f->rfd4 = NULL;
Simon Kelley28866e92011-02-14 20:19:14 +00001021 f->flags = 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001022#ifdef HAVE_IPV6
1023 f->rfd6 = NULL;
1024#endif
1025 daemon->frec_list = f;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001026 }
Simon Kelley16972692006-10-16 20:04:18 +01001027
1028 return f;
1029}
1030
Simon Kelley1a6bca82008-07-11 11:11:42 +01001031static struct randfd *allocate_rfd(int family)
1032{
1033 static int finger = 0;
1034 int i;
1035
1036 /* limit the number of sockets we have open to avoid starvation of
1037 (eg) TFTP. Once we have a reasonable number, randomness should be OK */
1038
1039 for (i = 0; i < RANDOM_SOCKS; i++)
Simon Kelley9009d742008-11-14 20:04:27 +00001040 if (daemon->randomsocks[i].refcount == 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001041 {
Simon Kelley9009d742008-11-14 20:04:27 +00001042 if ((daemon->randomsocks[i].fd = random_sock(family)) == -1)
1043 break;
1044
Simon Kelley1a6bca82008-07-11 11:11:42 +01001045 daemon->randomsocks[i].refcount = 1;
1046 daemon->randomsocks[i].family = family;
1047 return &daemon->randomsocks[i];
1048 }
1049
Simon Kelley9009d742008-11-14 20:04:27 +00001050 /* No free ones or cannot get new socket, grab an existing one */
Simon Kelley1a6bca82008-07-11 11:11:42 +01001051 for (i = 0; i < RANDOM_SOCKS; i++)
1052 {
1053 int j = (i+finger) % RANDOM_SOCKS;
Simon Kelley9009d742008-11-14 20:04:27 +00001054 if (daemon->randomsocks[j].refcount != 0 &&
1055 daemon->randomsocks[j].family == family &&
1056 daemon->randomsocks[j].refcount != 0xffff)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001057 {
1058 finger = j;
1059 daemon->randomsocks[j].refcount++;
1060 return &daemon->randomsocks[j];
1061 }
1062 }
1063
1064 return NULL; /* doom */
1065}
1066
1067static void free_frec(struct frec *f)
1068{
1069 if (f->rfd4 && --(f->rfd4->refcount) == 0)
1070 close(f->rfd4->fd);
1071
1072 f->rfd4 = NULL;
1073 f->sentto = NULL;
Simon Kelley28866e92011-02-14 20:19:14 +00001074 f->flags = 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001075
1076#ifdef HAVE_IPV6
1077 if (f->rfd6 && --(f->rfd6->refcount) == 0)
1078 close(f->rfd6->fd);
1079
1080 f->rfd6 = NULL;
1081#endif
1082}
1083
Simon Kelley16972692006-10-16 20:04:18 +01001084/* if wait==NULL return a free or older than TIMEOUT record.
1085 else return *wait zero if one available, or *wait is delay to
Simon Kelley1a6bca82008-07-11 11:11:42 +01001086 when the oldest in-use record will expire. Impose an absolute
1087 limit of 4*TIMEOUT before we wipe things (for random sockets) */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001088struct frec *get_new_frec(time_t now, int *wait)
Simon Kelley16972692006-10-16 20:04:18 +01001089{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001090 struct frec *f, *oldest, *target;
Simon Kelley16972692006-10-16 20:04:18 +01001091 int count;
1092
1093 if (wait)
1094 *wait = 0;
1095
Simon Kelley1a6bca82008-07-11 11:11:42 +01001096 for (f = daemon->frec_list, oldest = NULL, target = NULL, count = 0; f; f = f->next, count++)
Simon Kelley832af0b2007-01-21 20:01:28 +00001097 if (!f->sentto)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001098 target = f;
1099 else
Simon Kelley16972692006-10-16 20:04:18 +01001100 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001101 if (difftime(now, f->time) >= 4*TIMEOUT)
1102 {
1103 free_frec(f);
1104 target = f;
1105 }
1106
1107 if (!oldest || difftime(f->time, oldest->time) <= 0)
1108 oldest = f;
Simon Kelley16972692006-10-16 20:04:18 +01001109 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001110
1111 if (target)
1112 {
1113 target->time = now;
1114 return target;
1115 }
Simon Kelley16972692006-10-16 20:04:18 +01001116
1117 /* can't find empty one, use oldest if there is one
1118 and it's older than timeout */
1119 if (oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
1120 {
1121 /* keep stuff for twice timeout if we can by allocating a new
1122 record instead */
1123 if (difftime(now, oldest->time) < 2*TIMEOUT &&
1124 count <= daemon->ftabsize &&
1125 (f = allocate_frec(now)))
1126 return f;
1127
1128 if (!wait)
1129 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001130 free_frec(oldest);
Simon Kelley16972692006-10-16 20:04:18 +01001131 oldest->time = now;
1132 }
1133 return oldest;
1134 }
1135
1136 /* none available, calculate time 'till oldest record expires */
1137 if (count > daemon->ftabsize)
1138 {
1139 if (oldest && wait)
1140 *wait = oldest->time + (time_t)TIMEOUT - now;
1141 return NULL;
1142 }
1143
1144 if (!(f = allocate_frec(now)) && wait)
1145 /* wait one second on malloc failure */
1146 *wait = 1;
1147
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001148 return f; /* OK if malloc fails and this is NULL */
1149}
1150
Simon Kelley832af0b2007-01-21 20:01:28 +00001151/* crc is all-ones if not known. */
1152static struct frec *lookup_frec(unsigned short id, unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001153{
1154 struct frec *f;
1155
Simon Kelley1a6bca82008-07-11 11:11:42 +01001156 for(f = daemon->frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +00001157 if (f->sentto && f->new_id == id &&
1158 (f->crc == crc || crc == 0xffffffff))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001159 return f;
1160
1161 return NULL;
1162}
1163
1164static struct frec *lookup_frec_by_sender(unsigned short id,
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001165 union mysockaddr *addr,
1166 unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001167{
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001168 struct frec *f;
1169
Simon Kelley1a6bca82008-07-11 11:11:42 +01001170 for(f = daemon->frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +00001171 if (f->sentto &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001172 f->orig_id == id &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001173 f->crc == crc &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001174 sockaddr_isequal(&f->source, addr))
1175 return f;
1176
1177 return NULL;
1178}
1179
Simon Kelley849a8352006-06-09 21:02:31 +01001180/* A server record is going away, remove references to it */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001181void server_gone(struct server *server)
Simon Kelley849a8352006-06-09 21:02:31 +01001182{
1183 struct frec *f;
1184
Simon Kelley1a6bca82008-07-11 11:11:42 +01001185 for (f = daemon->frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +00001186 if (f->sentto && f->sentto == server)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001187 free_frec(f);
Simon Kelley849a8352006-06-09 21:02:31 +01001188
1189 if (daemon->last_server == server)
1190 daemon->last_server = NULL;
1191
1192 if (daemon->srv_save == server)
1193 daemon->srv_save = NULL;
1194}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001195
Simon Kelley316e2732010-01-22 20:16:09 +00001196/* return unique random ids. */
1197static unsigned short get_id(unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001198{
1199 unsigned short ret = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001200
Simon Kelley316e2732010-01-22 20:16:09 +00001201 do
Simon Kelley832af0b2007-01-21 20:01:28 +00001202 ret = rand16();
1203 while (lookup_frec(ret, crc));
1204
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001205 return ret;
1206}
1207
1208
1209
1210
1211