blob: 92bc6b0e7f72d61f47e06b14e3229af1352f757c [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 Kelley28866e92011-02-14 20:19:14 +0000210 if (flags == 0 && !(qtype & F_NSRR) &&
211 option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
Simon Kelley824af852008-02-12 20:43:05 +0000212 /* don't forward simple names, make exception for NS queries and empty name. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100213 flags = F_NXDOMAIN;
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 Kelley3d8df262005-08-29 12:19:27 +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 }
369 }
370
371 if (sendto(fd, (char *)header, plen, 0,
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100372 &start->addr.sa,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100373 sa_len(&start->addr)) == -1)
374 {
375 if (retry_send())
376 continue;
377 }
378 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000379 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000380 /* Keep info in case we want to re-send this packet */
381 daemon->srv_save = start;
382 daemon->packet_len = plen;
383
Simon Kelleyde379512004-06-22 20:23:33 +0100384 if (!gotname)
Simon Kelley3be34542004-09-11 19:12:13 +0100385 strcpy(daemon->namebuff, "query");
Simon Kelleyde379512004-06-22 20:23:33 +0100386 if (start->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100387 log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100388 (struct all_addr *)&start->addr.in.sin_addr, NULL);
Simon Kelleyde379512004-06-22 20:23:33 +0100389#ifdef HAVE_IPV6
390 else
Simon Kelley3be34542004-09-11 19:12:13 +0100391 log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100392 (struct all_addr *)&start->addr.in6.sin6_addr, NULL);
Simon Kelleyde379512004-06-22 20:23:33 +0100393#endif
Simon Kelley824af852008-02-12 20:43:05 +0000394 start->queries++;
Simon Kelleyde379512004-06-22 20:23:33 +0100395 forwarded = 1;
396 forward->sentto = start;
Simon Kelley0a852542005-03-23 20:28:59 +0000397 if (!forward->forwardall)
Simon Kelleyde379512004-06-22 20:23:33 +0100398 break;
Simon Kelley0a852542005-03-23 20:28:59 +0000399 forward->forwardall++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000400 }
401 }
402
Simon Kelleyde379512004-06-22 20:23:33 +0100403 if (!(start = start->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100404 start = daemon->servers;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000405
Simon Kelleyde379512004-06-22 20:23:33 +0100406 if (start == firstsentto)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000407 break;
408 }
409
Simon Kelleyde379512004-06-22 20:23:33 +0100410 if (forwarded)
Simon Kelley824af852008-02-12 20:43:05 +0000411 return 1;
Simon Kelleyde379512004-06-22 20:23:33 +0100412
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000413 /* could not send on, prepare to return */
414 header->id = htons(forward->orig_id);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100415 free_frec(forward); /* cancel */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000416 }
417
418 /* could not send on, return empty answer or address if known for whole domain */
Simon Kelleyb8187c82005-11-26 21:46:27 +0000419 if (udpfd != -1)
420 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000421 plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
Simon Kelley28866e92011-02-14 20:19:14 +0000422 send_from(udpfd, option_bool(OPT_NOWILD), (char *)header, plen, udpaddr, dst_addr, dst_iface);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000423 }
424
Simon Kelley824af852008-02-12 20:43:05 +0000425 return 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000426}
427
Simon Kelley572b41e2011-02-18 18:11:18 +0000428static size_t process_reply(struct dns_header *header, time_t now,
Simon Kelley28866e92011-02-14 20:19:14 +0000429 struct server *server, size_t n, int check_rebind, int checking_disabled)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100430{
Simon Kelley36717ee2004-09-20 19:20:58 +0100431 unsigned char *pheader, *sizep;
Simon Kelley832af0b2007-01-21 20:01:28 +0000432 int munged = 0, is_sign;
Simon Kelleycdeda282006-03-16 20:16:06 +0000433 size_t plen;
434
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100435 /* If upstream is advertising a larger UDP packet size
Simon Kelley9009d742008-11-14 20:04:27 +0000436 than we allow, trim it so that we don't get overlarge
437 requests for the client. We can't do this for signed packets. */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100438
Simon Kelley832af0b2007-01-21 20:01:28 +0000439 if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)) && !is_sign)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100440 {
441 unsigned short udpsz;
Simon Kelley36717ee2004-09-20 19:20:58 +0100442 unsigned char *psave = sizep;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100443
Simon Kelley36717ee2004-09-20 19:20:58 +0100444 GETSHORT(udpsz, sizep);
Simon Kelley3be34542004-09-11 19:12:13 +0100445 if (udpsz > daemon->edns_pktsz)
446 PUTSHORT(daemon->edns_pktsz, psave);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100447 }
448
Simon Kelley28866e92011-02-14 20:19:14 +0000449 /* RFC 4035 sect 4.6 para 3 */
450 if (!is_sign && !option_bool(OPT_DNSSEC))
Simon Kelley572b41e2011-02-18 18:11:18 +0000451 header->hb4 &= ~HB4_AD;
Simon Kelley28866e92011-02-14 20:19:14 +0000452
Simon Kelley572b41e2011-02-18 18:11:18 +0000453 if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
Simon Kelley36717ee2004-09-20 19:20:58 +0100454 return n;
455
Simon Kelley0a852542005-03-23 20:28:59 +0000456 /* Complain loudly if the upstream server is non-recursive. */
Simon Kelley572b41e2011-02-18 18:11:18 +0000457 if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR && ntohs(header->ancount) == 0 &&
Simon Kelley0a852542005-03-23 20:28:59 +0000458 server && !(server->flags & SERV_WARNED_RECURSIVE))
459 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100460 prettyprint_addr(&server->addr, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100461 my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
Simon Kelley28866e92011-02-14 20:19:14 +0000462 if (!option_bool(OPT_LOG))
Simon Kelley0a852542005-03-23 20:28:59 +0000463 server->flags |= SERV_WARNED_RECURSIVE;
464 }
465
Simon Kelley572b41e2011-02-18 18:11:18 +0000466 if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100467 check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100468 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100469 munged = 1;
Simon Kelley572b41e2011-02-18 18:11:18 +0000470 SET_RCODE(header, NXDOMAIN);
471 header->hb3 &= ~HB3_AA;
Simon Kelley36717ee2004-09-20 19:20:58 +0100472 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100473 else
Simon Kelley36717ee2004-09-20 19:20:58 +0100474 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000475 if (RCODE(header) == NXDOMAIN &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100476 extract_request(header, n, daemon->namebuff, NULL) &&
Simon Kelley5aabfc72007-08-29 11:24:47 +0100477 check_for_local_domain(daemon->namebuff, now))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100478 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100479 /* if we forwarded a query for a locally known name (because it was for
480 an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
481 since we know that the domain exists, even if upstream doesn't */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100482 munged = 1;
Simon Kelley572b41e2011-02-18 18:11:18 +0000483 header->hb3 |= HB3_AA;
484 SET_RCODE(header, NOERROR);
Simon Kelley36717ee2004-09-20 19:20:58 +0100485 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000486
Simon Kelley28866e92011-02-14 20:19:14 +0000487 if (extract_addresses(header, n, daemon->namebuff, now, is_sign, check_rebind, checking_disabled))
Simon Kelley824af852008-02-12 20:43:05 +0000488 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100489 my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
Simon Kelley824af852008-02-12 20:43:05 +0000490 munged = 1;
491 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100492 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100493
494 /* do this after extract_addresses. Ensure NODATA reply and remove
495 nameserver info. */
496
497 if (munged)
498 {
499 header->ancount = htons(0);
500 header->nscount = htons(0);
501 header->arcount = htons(0);
502 }
503
Simon Kelley36717ee2004-09-20 19:20:58 +0100504 /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
505 sections of the packet. Find the new length here and put back pseudoheader
506 if it was removed. */
507 return resize_packet(header, n, pheader, plen);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100508}
509
Simon Kelley3be34542004-09-11 19:12:13 +0100510/* sets new last_server */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100511void reply_query(int fd, int family, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000512{
513 /* packet from peer server, extract data for cache, and send to
514 original requester */
Simon Kelley572b41e2011-02-18 18:11:18 +0000515 struct dns_header *header;
Simon Kelleyde379512004-06-22 20:23:33 +0100516 union mysockaddr serveraddr;
Simon Kelley832af0b2007-01-21 20:01:28 +0000517 struct frec *forward;
Simon Kelleyde379512004-06-22 20:23:33 +0100518 socklen_t addrlen = sizeof(serveraddr);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100519 ssize_t n = recvfrom(fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
Simon Kelleycdeda282006-03-16 20:16:06 +0000520 size_t nn;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100521 struct server *server;
522
Simon Kelleycdeda282006-03-16 20:16:06 +0000523 /* packet buffer overwritten */
524 daemon->srv_save = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +0000525
Simon Kelleyde379512004-06-22 20:23:33 +0100526 /* Determine the address of the server replying so that we can mark that as good */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100527 serveraddr.sa.sa_family = family;
Simon Kelleyde379512004-06-22 20:23:33 +0100528#ifdef HAVE_IPV6
529 if (serveraddr.sa.sa_family == AF_INET6)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100530 serveraddr.in6.sin6_flowinfo = 0;
Simon Kelleyde379512004-06-22 20:23:33 +0100531#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000532
Simon Kelley1a6bca82008-07-11 11:11:42 +0100533 /* spoof check: answer must come from known server, */
534 for (server = daemon->servers; server; server = server->next)
535 if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
536 sockaddr_isequal(&server->addr, &serveraddr))
537 break;
538
Simon Kelley572b41e2011-02-18 18:11:18 +0000539 header = (struct dns_header *)daemon->packet;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100540
Simon Kelley1a6bca82008-07-11 11:11:42 +0100541 if (!server ||
Simon Kelley572b41e2011-02-18 18:11:18 +0000542 n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) ||
Simon Kelley1a6bca82008-07-11 11:11:42 +0100543 !(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
544 return;
545
546 server = forward->sentto;
547
Simon Kelley572b41e2011-02-18 18:11:18 +0000548 if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
Simon Kelley28866e92011-02-14 20:19:14 +0000549 !option_bool(OPT_ORDER) &&
Simon Kelley1a6bca82008-07-11 11:11:42 +0100550 forward->forwardall == 0)
551 /* for broken servers, attempt to send to another one. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000552 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100553 unsigned char *pheader;
554 size_t plen;
555 int is_sign;
Simon Kelley832af0b2007-01-21 20:01:28 +0000556
Simon Kelley1a6bca82008-07-11 11:11:42 +0100557 /* recreate query from reply */
558 pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign);
559 if (!is_sign)
Simon Kelley832af0b2007-01-21 20:01:28 +0000560 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100561 header->ancount = htons(0);
562 header->nscount = htons(0);
563 header->arcount = htons(0);
564 if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
565 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000566 header->hb3 &= ~(HB3_QR | HB3_TC);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100567 forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
568 return;
569 }
570 }
571 }
572
573 if ((forward->sentto->flags & SERV_TYPE) == 0)
574 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000575 if (RCODE(header) == SERVFAIL || RCODE(header) == REFUSED)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100576 server = NULL;
577 else
578 {
579 struct server *last_server;
Simon Kelley832af0b2007-01-21 20:01:28 +0000580
Simon Kelley1a6bca82008-07-11 11:11:42 +0100581 /* find good server by address if possible, otherwise assume the last one we sent to */
582 for (last_server = daemon->servers; last_server; last_server = last_server->next)
583 if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
584 sockaddr_isequal(&last_server->addr, &serveraddr))
585 {
586 server = last_server;
587 break;
588 }
589 }
Simon Kelley28866e92011-02-14 20:19:14 +0000590 if (!option_bool(OPT_ALL_SERVERS))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100591 daemon->last_server = server;
592 }
593
594 /* If the answer is an error, keep the forward record in place in case
595 we get a good reply from another server. Kill it when we've
596 had replies from all to avoid filling the forwarding table when
597 everything is broken */
598 if (forward->forwardall == 0 || --forward->forwardall == 1 ||
Simon Kelley572b41e2011-02-18 18:11:18 +0000599 (RCODE(header) != REFUSED && RCODE(header) != SERVFAIL))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100600 {
Simon Kelley28866e92011-02-14 20:19:14 +0000601 int check_rebind = !(forward->flags & FREC_NOREBIND);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100602
Simon Kelley28866e92011-02-14 20:19:14 +0000603 if (!option_bool(OPT_NO_REBIND))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100604 check_rebind = 0;
605
Simon Kelley28866e92011-02-14 20:19:14 +0000606 if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, forward->flags & FREC_CHECKING_DISABLED)))
Simon Kelley832af0b2007-01-21 20:01:28 +0000607 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100608 header->id = htons(forward->orig_id);
Simon Kelley572b41e2011-02-18 18:11:18 +0000609 header->hb4 |= HB4_RA; /* recursion if available */
Simon Kelley28866e92011-02-14 20:19:14 +0000610 send_from(forward->fd, option_bool(OPT_NOWILD), daemon->packet, nn,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100611 &forward->source, &forward->dest, forward->iface);
Simon Kelley832af0b2007-01-21 20:01:28 +0000612 }
Simon Kelley1a6bca82008-07-11 11:11:42 +0100613 free_frec(forward); /* cancel */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000614 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000615}
Simon Kelley44a2a312004-03-10 20:04:35 +0000616
Simon Kelley1a6bca82008-07-11 11:11:42 +0100617
Simon Kelley5aabfc72007-08-29 11:24:47 +0100618void receive_query(struct listener *listen, time_t now)
Simon Kelley44a2a312004-03-10 20:04:35 +0000619{
Simon Kelley572b41e2011-02-18 18:11:18 +0000620 struct dns_header *header = (struct dns_header *)daemon->packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000621 union mysockaddr source_addr;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100622 unsigned short type;
Simon Kelley44a2a312004-03-10 20:04:35 +0000623 struct all_addr dst_addr;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000624 struct in_addr netmask, dst_addr_4;
Simon Kelleycdeda282006-03-16 20:16:06 +0000625 size_t m;
626 ssize_t n;
627 int if_index = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000628 struct iovec iov[1];
629 struct msghdr msg;
630 struct cmsghdr *cmptr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000631 union {
632 struct cmsghdr align; /* this ensures alignment */
633#ifdef HAVE_IPV6
634 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
635#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100636#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000637 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000638#elif defined(IP_RECVDSTADDR) && defined(HAVE_SOLARIS_NETWORK)
639 char control[CMSG_SPACE(sizeof(struct in_addr)) +
640 CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley44a2a312004-03-10 20:04:35 +0000641#elif defined(IP_RECVDSTADDR)
642 char control[CMSG_SPACE(sizeof(struct in_addr)) +
643 CMSG_SPACE(sizeof(struct sockaddr_dl))];
644#endif
645 } control_u;
646
Simon Kelleycdeda282006-03-16 20:16:06 +0000647 /* packet buffer overwritten */
648 daemon->srv_save = NULL;
649
Simon Kelley28866e92011-02-14 20:19:14 +0000650 if (listen->family == AF_INET && option_bool(OPT_NOWILD))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000651 {
652 dst_addr_4 = listen->iface->addr.in.sin_addr;
653 netmask = listen->iface->netmask;
654 }
655 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100656 {
657 dst_addr_4.s_addr = 0;
658 netmask.s_addr = 0;
659 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000660
Simon Kelley3be34542004-09-11 19:12:13 +0100661 iov[0].iov_base = daemon->packet;
662 iov[0].iov_len = daemon->edns_pktsz;
Simon Kelley44a2a312004-03-10 20:04:35 +0000663
664 msg.msg_control = control_u.control;
665 msg.msg_controllen = sizeof(control_u);
666 msg.msg_flags = 0;
667 msg.msg_name = &source_addr;
668 msg.msg_namelen = sizeof(source_addr);
669 msg.msg_iov = iov;
670 msg.msg_iovlen = 1;
671
Simon Kelleyde379512004-06-22 20:23:33 +0100672 if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
Simon Kelley3be34542004-09-11 19:12:13 +0100673 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000674
Simon Kelley572b41e2011-02-18 18:11:18 +0000675 if (n < (int)sizeof(struct dns_header) ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100676 (msg.msg_flags & MSG_TRUNC) ||
Simon Kelley572b41e2011-02-18 18:11:18 +0000677 (header->hb3 & HB3_QR))
Simon Kelley3be34542004-09-11 19:12:13 +0100678 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000679
Simon Kelley26128d22004-11-14 16:43:54 +0000680 source_addr.sa.sa_family = listen->family;
681#ifdef HAVE_IPV6
682 if (listen->family == AF_INET6)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100683 source_addr.in6.sin6_flowinfo = 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000684#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000685
686 if (!option_bool(OPT_NOWILD))
Simon Kelley44a2a312004-03-10 20:04:35 +0000687 {
Simon Kelley8a911cc2004-03-16 18:35:52 +0000688 struct ifreq ifr;
689
Simon Kelley26128d22004-11-14 16:43:54 +0000690 if (msg.msg_controllen < sizeof(struct cmsghdr))
691 return;
692
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100693#if defined(HAVE_LINUX_NETWORK)
Simon Kelley26128d22004-11-14 16:43:54 +0000694 if (listen->family == AF_INET)
695 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
696 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
697 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100698 union {
699 unsigned char *c;
700 struct in_pktinfo *p;
701 } p;
702 p.c = CMSG_DATA(cmptr);
703 dst_addr_4 = dst_addr.addr.addr4 = p.p->ipi_spec_dst;
704 if_index = p.p->ipi_ifindex;
Simon Kelley26128d22004-11-14 16:43:54 +0000705 }
706#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
707 if (listen->family == AF_INET)
708 {
709 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100710 {
711 union {
712 unsigned char *c;
713 unsigned int *i;
714 struct in_addr *a;
715#ifndef HAVE_SOLARIS_NETWORK
716 struct sockaddr_dl *s;
Simon Kelley824af852008-02-12 20:43:05 +0000717#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100718 } p;
719 p.c = CMSG_DATA(cmptr);
720 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
721 dst_addr_4 = dst_addr.addr.addr4 = *(p.a);
722 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
723#ifdef HAVE_SOLARIS_NETWORK
724 if_index = *(p.i);
725#else
726 if_index = p.s->sdl_index;
727#endif
728 }
Simon Kelley26128d22004-11-14 16:43:54 +0000729 }
730#endif
731
732#ifdef HAVE_IPV6
733 if (listen->family == AF_INET6)
734 {
735 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelley316e2732010-01-22 20:16:09 +0000736 if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo)
Simon Kelley26128d22004-11-14 16:43:54 +0000737 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100738 union {
739 unsigned char *c;
740 struct in6_pktinfo *p;
741 } p;
742 p.c = CMSG_DATA(cmptr);
743
744 dst_addr.addr.addr6 = p.p->ipi6_addr;
745 if_index = p.p->ipi6_ifindex;
Simon Kelley26128d22004-11-14 16:43:54 +0000746 }
747 }
748#endif
749
750 /* enforce available interface configuration */
751
Simon Kelley7622fc02009-06-04 20:32:05 +0100752 if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
753 !iface_check(listen->family, &dst_addr, ifr.ifr_name, &if_index))
Simon Kelley832af0b2007-01-21 20:01:28 +0000754 return;
755
756 if (listen->family == AF_INET &&
Simon Kelley28866e92011-02-14 20:19:14 +0000757 option_bool(OPT_LOCALISE) &&
Simon Kelley832af0b2007-01-21 20:01:28 +0000758 ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
759 return;
760
761 netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000762 }
763
Simon Kelleycdeda282006-03-16 20:16:06 +0000764 if (extract_request(header, (size_t)n, daemon->namebuff, &type))
Simon Kelley44a2a312004-03-10 20:04:35 +0000765 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100766 char types[20];
767
768 querystr(types, type);
769
Simon Kelley44a2a312004-03-10 20:04:35 +0000770 if (listen->family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100771 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100772 (struct all_addr *)&source_addr.in.sin_addr, types);
Simon Kelley44a2a312004-03-10 20:04:35 +0000773#ifdef HAVE_IPV6
774 else
Simon Kelley3be34542004-09-11 19:12:13 +0100775 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100776 (struct all_addr *)&source_addr.in6.sin6_addr, types);
Simon Kelley44a2a312004-03-10 20:04:35 +0000777#endif
778 }
779
Simon Kelley5aabfc72007-08-29 11:24:47 +0100780 m = answer_request (header, ((char *) header) + PACKETSZ, (size_t)n,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000781 dst_addr_4, netmask, now);
Simon Kelley44a2a312004-03-10 20:04:35 +0000782 if (m >= 1)
Simon Kelley824af852008-02-12 20:43:05 +0000783 {
Simon Kelley28866e92011-02-14 20:19:14 +0000784 send_from(listen->fd, option_bool(OPT_NOWILD), (char *)header,
Simon Kelley824af852008-02-12 20:43:05 +0000785 m, &source_addr, &dst_addr, if_index);
786 daemon->local_answer++;
787 }
788 else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
789 header, (size_t)n, now, NULL))
790 daemon->queries_forwarded++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000791 else
Simon Kelley824af852008-02-12 20:43:05 +0000792 daemon->local_answer++;
Simon Kelley44a2a312004-03-10 20:04:35 +0000793}
794
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100795/* The daemon forks before calling this: it should deal with one connection,
796 blocking as neccessary, and then return. Note, need to be a bit careful
797 about resources for debug mode, when the fork is suppressed: that's
798 done by the caller. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100799unsigned char *tcp_request(int confd, time_t now,
Simon Kelley3d8df262005-08-29 12:19:27 +0100800 struct in_addr local_addr, struct in_addr netmask)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100801{
Simon Kelley28866e92011-02-14 20:19:14 +0000802 size_t size = 0;
803 int norebind = 0;
804 int checking_disabled;
Simon Kelleycdeda282006-03-16 20:16:06 +0000805 size_t m;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100806 unsigned short qtype, gotname;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100807 unsigned char c1, c2;
808 /* Max TCP packet + slop */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100809 unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
Simon Kelley572b41e2011-02-18 18:11:18 +0000810 struct dns_header *header;
Simon Kelley3be34542004-09-11 19:12:13 +0100811 struct server *last_server;
812
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100813 while (1)
814 {
815 if (!packet ||
816 !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
817 !(size = c1 << 8 | c2) ||
818 !read_write(confd, packet, size, 1))
819 return packet;
820
Simon Kelley572b41e2011-02-18 18:11:18 +0000821 if (size < (int)sizeof(struct dns_header))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100822 continue;
823
Simon Kelley572b41e2011-02-18 18:11:18 +0000824 header = (struct dns_header *)packet;
Simon Kelley28866e92011-02-14 20:19:14 +0000825
826 /* save state of "cd" flag in query */
Simon Kelley572b41e2011-02-18 18:11:18 +0000827 checking_disabled = header->hb4 & HB4_CD;
Simon Kelley28866e92011-02-14 20:19:14 +0000828
829 /* RFC 4035: sect 4.6 para 2 */
Simon Kelley572b41e2011-02-18 18:11:18 +0000830 header->hb4 &= ~HB4_AD;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100831
Simon Kelley3be34542004-09-11 19:12:13 +0100832 if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100833 {
834 union mysockaddr peer_addr;
835 socklen_t peer_len = sizeof(union mysockaddr);
836
837 if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
838 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100839 char types[20];
840
841 querystr(types, qtype);
842
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100843 if (peer_addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100844 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100845 (struct all_addr *)&peer_addr.in.sin_addr, types);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100846#ifdef HAVE_IPV6
847 else
Simon Kelley3be34542004-09-11 19:12:13 +0100848 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100849 (struct all_addr *)&peer_addr.in6.sin6_addr, types);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100850#endif
851 }
852 }
853
854 /* m > 0 if answered from cache */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100855 m = answer_request(header, ((char *) header) + 65536, (unsigned int)size,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000856 local_addr, netmask, now);
Simon Kelley5aabfc72007-08-29 11:24:47 +0100857
858 /* Do this by steam now we're not in the select() loop */
859 check_log_writer(NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100860
861 if (m == 0)
862 {
Simon Kelley28866e92011-02-14 20:19:14 +0000863 unsigned int flags = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100864 struct all_addr *addrp = NULL;
865 int type = 0;
866 char *domain = NULL;
Simon Kelley28866e92011-02-14 20:19:14 +0000867
868 if (option_bool(OPT_ADD_MAC))
869 {
870 union mysockaddr peer_addr;
871 socklen_t peer_len = sizeof(union mysockaddr);
872
873 if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
874 size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
875 }
876
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100877 if (gotname)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100878 flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100879
Simon Kelley28866e92011-02-14 20:19:14 +0000880 if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
Simon Kelley3be34542004-09-11 19:12:13 +0100881 last_server = daemon->servers;
882 else
883 last_server = daemon->last_server;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100884
885 if (!flags && last_server)
886 {
887 struct server *firstsendto = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +0000888 unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
889
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100890 /* Loop round available servers until we succeed in connecting to one.
891 Note that this code subtley ensures that consecutive queries on this connection
892 which can go to the same server, do so. */
893 while (1)
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100894 {
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100895 if (!firstsendto)
896 firstsendto = last_server;
897 else
898 {
899 if (!(last_server = last_server->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100900 last_server = daemon->servers;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100901
902 if (last_server == firstsendto)
903 break;
904 }
905
906 /* server for wrong domain */
907 if (type != (last_server->flags & SERV_TYPE) ||
908 (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
909 continue;
910
911 if ((last_server->tcpfd == -1) &&
912 (last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
Simon Kelley824af852008-02-12 20:43:05 +0000913 (!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
914 connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100915 {
916 close(last_server->tcpfd);
917 last_server->tcpfd = -1;
918 }
919
920 if (last_server->tcpfd == -1)
921 continue;
Simon Kelley824af852008-02-12 20:43:05 +0000922
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100923 c1 = size >> 8;
924 c2 = size;
925
926 if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
927 !read_write(last_server->tcpfd, &c2, 1, 0) ||
928 !read_write(last_server->tcpfd, packet, size, 0) ||
929 !read_write(last_server->tcpfd, &c1, 1, 1) ||
930 !read_write(last_server->tcpfd, &c2, 1, 1))
931 {
932 close(last_server->tcpfd);
933 last_server->tcpfd = -1;
934 continue;
935 }
Simon Kelley824af852008-02-12 20:43:05 +0000936
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100937 m = (c1 << 8) | c2;
938 if (!read_write(last_server->tcpfd, packet, m, 1))
939 return packet;
940
941 if (!gotname)
Simon Kelley3be34542004-09-11 19:12:13 +0100942 strcpy(daemon->namebuff, "query");
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100943 if (last_server->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100944 log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100945 (struct all_addr *)&last_server->addr.in.sin_addr, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100946#ifdef HAVE_IPV6
947 else
Simon Kelley3be34542004-09-11 19:12:13 +0100948 log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100949 (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100950#endif
951
952 /* There's no point in updating the cache, since this process will exit and
Simon Kelley832af0b2007-01-21 20:01:28 +0000953 lose the information after a few queries. We make this call for the alias and
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100954 bogus-nxdomain side-effects. */
Simon Kelley832af0b2007-01-21 20:01:28 +0000955 /* If the crc of the question section doesn't match the crc we sent, then
956 someone might be attempting to insert bogus values into the cache by
957 sending replies containing questions and bogus answers. */
958 if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
Simon Kelley28866e92011-02-14 20:19:14 +0000959 m = process_reply(header, now, last_server, (unsigned int)m,
960 option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100961
962 break;
963 }
964 }
965
966 /* In case of local answer or no connections made. */
967 if (m == 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100968 m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100969 }
Simon Kelley5aabfc72007-08-29 11:24:47 +0100970
971 check_log_writer(NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100972
973 c1 = m>>8;
974 c2 = m;
975 if (!read_write(confd, &c1, 1, 0) ||
976 !read_write(confd, &c2, 1, 0) ||
977 !read_write(confd, packet, m, 0))
978 return packet;
979 }
980}
981
Simon Kelley16972692006-10-16 20:04:18 +0100982static struct frec *allocate_frec(time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000983{
Simon Kelley16972692006-10-16 20:04:18 +0100984 struct frec *f;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000985
Simon Kelley5aabfc72007-08-29 11:24:47 +0100986 if ((f = (struct frec *)whine_malloc(sizeof(struct frec))))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000987 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100988 f->next = daemon->frec_list;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000989 f->time = now;
Simon Kelley832af0b2007-01-21 20:01:28 +0000990 f->sentto = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100991 f->rfd4 = NULL;
Simon Kelley28866e92011-02-14 20:19:14 +0000992 f->flags = 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100993#ifdef HAVE_IPV6
994 f->rfd6 = NULL;
995#endif
996 daemon->frec_list = f;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000997 }
Simon Kelley16972692006-10-16 20:04:18 +0100998
999 return f;
1000}
1001
Simon Kelley1a6bca82008-07-11 11:11:42 +01001002static struct randfd *allocate_rfd(int family)
1003{
1004 static int finger = 0;
1005 int i;
1006
1007 /* limit the number of sockets we have open to avoid starvation of
1008 (eg) TFTP. Once we have a reasonable number, randomness should be OK */
1009
1010 for (i = 0; i < RANDOM_SOCKS; i++)
Simon Kelley9009d742008-11-14 20:04:27 +00001011 if (daemon->randomsocks[i].refcount == 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001012 {
Simon Kelley9009d742008-11-14 20:04:27 +00001013 if ((daemon->randomsocks[i].fd = random_sock(family)) == -1)
1014 break;
1015
Simon Kelley1a6bca82008-07-11 11:11:42 +01001016 daemon->randomsocks[i].refcount = 1;
1017 daemon->randomsocks[i].family = family;
1018 return &daemon->randomsocks[i];
1019 }
1020
Simon Kelley9009d742008-11-14 20:04:27 +00001021 /* No free ones or cannot get new socket, grab an existing one */
Simon Kelley1a6bca82008-07-11 11:11:42 +01001022 for (i = 0; i < RANDOM_SOCKS; i++)
1023 {
1024 int j = (i+finger) % RANDOM_SOCKS;
Simon Kelley9009d742008-11-14 20:04:27 +00001025 if (daemon->randomsocks[j].refcount != 0 &&
1026 daemon->randomsocks[j].family == family &&
1027 daemon->randomsocks[j].refcount != 0xffff)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001028 {
1029 finger = j;
1030 daemon->randomsocks[j].refcount++;
1031 return &daemon->randomsocks[j];
1032 }
1033 }
1034
1035 return NULL; /* doom */
1036}
1037
1038static void free_frec(struct frec *f)
1039{
1040 if (f->rfd4 && --(f->rfd4->refcount) == 0)
1041 close(f->rfd4->fd);
1042
1043 f->rfd4 = NULL;
1044 f->sentto = NULL;
Simon Kelley28866e92011-02-14 20:19:14 +00001045 f->flags = 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001046
1047#ifdef HAVE_IPV6
1048 if (f->rfd6 && --(f->rfd6->refcount) == 0)
1049 close(f->rfd6->fd);
1050
1051 f->rfd6 = NULL;
1052#endif
1053}
1054
Simon Kelley16972692006-10-16 20:04:18 +01001055/* if wait==NULL return a free or older than TIMEOUT record.
1056 else return *wait zero if one available, or *wait is delay to
Simon Kelley1a6bca82008-07-11 11:11:42 +01001057 when the oldest in-use record will expire. Impose an absolute
1058 limit of 4*TIMEOUT before we wipe things (for random sockets) */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001059struct frec *get_new_frec(time_t now, int *wait)
Simon Kelley16972692006-10-16 20:04:18 +01001060{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001061 struct frec *f, *oldest, *target;
Simon Kelley16972692006-10-16 20:04:18 +01001062 int count;
1063
1064 if (wait)
1065 *wait = 0;
1066
Simon Kelley1a6bca82008-07-11 11:11:42 +01001067 for (f = daemon->frec_list, oldest = NULL, target = NULL, count = 0; f; f = f->next, count++)
Simon Kelley832af0b2007-01-21 20:01:28 +00001068 if (!f->sentto)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001069 target = f;
1070 else
Simon Kelley16972692006-10-16 20:04:18 +01001071 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001072 if (difftime(now, f->time) >= 4*TIMEOUT)
1073 {
1074 free_frec(f);
1075 target = f;
1076 }
1077
1078 if (!oldest || difftime(f->time, oldest->time) <= 0)
1079 oldest = f;
Simon Kelley16972692006-10-16 20:04:18 +01001080 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001081
1082 if (target)
1083 {
1084 target->time = now;
1085 return target;
1086 }
Simon Kelley16972692006-10-16 20:04:18 +01001087
1088 /* can't find empty one, use oldest if there is one
1089 and it's older than timeout */
1090 if (oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
1091 {
1092 /* keep stuff for twice timeout if we can by allocating a new
1093 record instead */
1094 if (difftime(now, oldest->time) < 2*TIMEOUT &&
1095 count <= daemon->ftabsize &&
1096 (f = allocate_frec(now)))
1097 return f;
1098
1099 if (!wait)
1100 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001101 free_frec(oldest);
Simon Kelley16972692006-10-16 20:04:18 +01001102 oldest->time = now;
1103 }
1104 return oldest;
1105 }
1106
1107 /* none available, calculate time 'till oldest record expires */
1108 if (count > daemon->ftabsize)
1109 {
1110 if (oldest && wait)
1111 *wait = oldest->time + (time_t)TIMEOUT - now;
1112 return NULL;
1113 }
1114
1115 if (!(f = allocate_frec(now)) && wait)
1116 /* wait one second on malloc failure */
1117 *wait = 1;
1118
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001119 return f; /* OK if malloc fails and this is NULL */
1120}
1121
Simon Kelley832af0b2007-01-21 20:01:28 +00001122/* crc is all-ones if not known. */
1123static struct frec *lookup_frec(unsigned short id, unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001124{
1125 struct frec *f;
1126
Simon Kelley1a6bca82008-07-11 11:11:42 +01001127 for(f = daemon->frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +00001128 if (f->sentto && f->new_id == id &&
1129 (f->crc == crc || crc == 0xffffffff))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001130 return f;
1131
1132 return NULL;
1133}
1134
1135static struct frec *lookup_frec_by_sender(unsigned short id,
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001136 union mysockaddr *addr,
1137 unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001138{
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001139 struct frec *f;
1140
Simon Kelley1a6bca82008-07-11 11:11:42 +01001141 for(f = daemon->frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +00001142 if (f->sentto &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001143 f->orig_id == id &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001144 f->crc == crc &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001145 sockaddr_isequal(&f->source, addr))
1146 return f;
1147
1148 return NULL;
1149}
1150
Simon Kelley849a8352006-06-09 21:02:31 +01001151/* A server record is going away, remove references to it */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001152void server_gone(struct server *server)
Simon Kelley849a8352006-06-09 21:02:31 +01001153{
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->sentto == server)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001158 free_frec(f);
Simon Kelley849a8352006-06-09 21:02:31 +01001159
1160 if (daemon->last_server == server)
1161 daemon->last_server = NULL;
1162
1163 if (daemon->srv_save == server)
1164 daemon->srv_save = NULL;
1165}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001166
Simon Kelley316e2732010-01-22 20:16:09 +00001167/* return unique random ids. */
1168static unsigned short get_id(unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001169{
1170 unsigned short ret = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001171
Simon Kelley316e2732010-01-22 20:16:09 +00001172 do
Simon Kelley832af0b2007-01-21 20:01:28 +00001173 ret = rand16();
1174 while (lookup_frec(ret, crc));
1175
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001176 return ret;
1177}
1178
1179
1180
1181
1182