blob: 408a2f00e41eb9fa1977adcbb19e46d4820ff9cc [file] [log] [blame]
Simon Kelley59546082012-01-06 20:02:04 +00001/* dnsmasq is Copyright (c) 2000-2012 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 Kelley29689cf2012-03-22 14:01:00 +000029int send_from(int fd, int nowild, char *packet, size_t len,
30 union mysockaddr *to, struct all_addr *source,
Simon Kelley50303b12012-04-04 22:13:17 +010031 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));
Simon Kelleyc72daea2012-01-05 21:33:27 +000073 cmptr->cmsg_level = IPPROTO_IP;
Simon Kelley26128d22004-11-14 16:43:54 +000074 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 Kelleyc72daea2012-01-05 21:33:27 +000091 cmptr->cmsg_level = IPPROTO_IPV6;
Simon Kelley26128d22004-11-14 16:43:54 +000092 }
Simon Kelley3d8df262005-08-29 12:19:27 +010093#else
Simon Kelleyc72daea2012-01-05 21:33:27 +000094 (void)iface; /* eliminate warning */
Simon Kelley26128d22004-11-14 16:43:54 +000095#endif
96 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +010097
Simon Kelley29d28dd2012-12-03 14:05:59 +000098 while (sendmsg(fd, &msg, 0) == -1)
Simon Kelleyfeba5c12004-07-27 20:28:58 +010099 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100100 if (retry_send())
Simon Kelley29d28dd2012-12-03 14:05:59 +0000101 continue;
102
103 /* If interface is still in DAD, EINVAL results - ignore that. */
104 if (errno == EINVAL)
105 break;
Simon Kelley22d904d2012-02-26 20:13:45 +0000106
Simon Kelley50303b12012-04-04 22:13:17 +0100107 my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno));
Simon Kelley29689cf2012-03-22 14:01:00 +0000108 return 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100109 }
Simon Kelley29d28dd2012-12-03 14:05:59 +0000110
Simon Kelley29689cf2012-03-22 14:01:00 +0000111 return 1;
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 Kelley54dd3932012-06-20 11:23:38 +0100432 send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (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 Kelley54dd3932012-06-20 11:23:38 +0100620 send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
Simon Kelley50303b12012-04-04 22:13:17 +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 Kelley4f7b3042012-11-28 21:27:02 +0000638 int auth_dns = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000639 struct iovec iov[1];
640 struct msghdr msg;
641 struct cmsghdr *cmptr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000642 union {
643 struct cmsghdr align; /* this ensures alignment */
644#ifdef HAVE_IPV6
645 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
646#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100647#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000648 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000649#elif defined(IP_RECVDSTADDR) && defined(HAVE_SOLARIS_NETWORK)
650 char control[CMSG_SPACE(sizeof(struct in_addr)) +
651 CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley44a2a312004-03-10 20:04:35 +0000652#elif defined(IP_RECVDSTADDR)
653 char control[CMSG_SPACE(sizeof(struct in_addr)) +
654 CMSG_SPACE(sizeof(struct sockaddr_dl))];
655#endif
656 } control_u;
657
Simon Kelleycdeda282006-03-16 20:16:06 +0000658 /* packet buffer overwritten */
659 daemon->srv_save = NULL;
660
Simon Kelley4f7b3042012-11-28 21:27:02 +0000661 dst_addr_4.s_addr = 0;
662 netmask.s_addr = 0;
663
664 if (listen->iface && option_bool(OPT_NOWILD))
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000665 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000666 auth_dns = listen->iface->dns_auth;
667
668 if (listen->family == AF_INET)
669 {
670 dst_addr_4 = listen->iface->addr.in.sin_addr;
671 netmask = listen->iface->netmask;
672 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000673 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000674
Simon Kelley3be34542004-09-11 19:12:13 +0100675 iov[0].iov_base = daemon->packet;
676 iov[0].iov_len = daemon->edns_pktsz;
Simon Kelley44a2a312004-03-10 20:04:35 +0000677
678 msg.msg_control = control_u.control;
679 msg.msg_controllen = sizeof(control_u);
680 msg.msg_flags = 0;
681 msg.msg_name = &source_addr;
682 msg.msg_namelen = sizeof(source_addr);
683 msg.msg_iov = iov;
684 msg.msg_iovlen = 1;
685
Simon Kelleyde379512004-06-22 20:23:33 +0100686 if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
Simon Kelley3be34542004-09-11 19:12:13 +0100687 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000688
Simon Kelley572b41e2011-02-18 18:11:18 +0000689 if (n < (int)sizeof(struct dns_header) ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100690 (msg.msg_flags & MSG_TRUNC) ||
Simon Kelley572b41e2011-02-18 18:11:18 +0000691 (header->hb3 & HB3_QR))
Simon Kelley3be34542004-09-11 19:12:13 +0100692 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000693
Simon Kelley26128d22004-11-14 16:43:54 +0000694 source_addr.sa.sa_family = listen->family;
695#ifdef HAVE_IPV6
696 if (listen->family == AF_INET6)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100697 source_addr.in6.sin6_flowinfo = 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000698#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000699
700 if (!option_bool(OPT_NOWILD))
Simon Kelley44a2a312004-03-10 20:04:35 +0000701 {
Simon Kelley8a911cc2004-03-16 18:35:52 +0000702 struct ifreq ifr;
703
Simon Kelley26128d22004-11-14 16:43:54 +0000704 if (msg.msg_controllen < sizeof(struct cmsghdr))
705 return;
706
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100707#if defined(HAVE_LINUX_NETWORK)
Simon Kelley26128d22004-11-14 16:43:54 +0000708 if (listen->family == AF_INET)
709 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000710 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley26128d22004-11-14 16:43:54 +0000711 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100712 union {
713 unsigned char *c;
714 struct in_pktinfo *p;
715 } p;
716 p.c = CMSG_DATA(cmptr);
717 dst_addr_4 = dst_addr.addr.addr4 = p.p->ipi_spec_dst;
718 if_index = p.p->ipi_ifindex;
Simon Kelley26128d22004-11-14 16:43:54 +0000719 }
720#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
721 if (listen->family == AF_INET)
722 {
723 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100724 {
725 union {
726 unsigned char *c;
727 unsigned int *i;
728 struct in_addr *a;
729#ifndef HAVE_SOLARIS_NETWORK
730 struct sockaddr_dl *s;
Simon Kelley824af852008-02-12 20:43:05 +0000731#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100732 } p;
733 p.c = CMSG_DATA(cmptr);
734 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
735 dst_addr_4 = dst_addr.addr.addr4 = *(p.a);
736 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
737#ifdef HAVE_SOLARIS_NETWORK
738 if_index = *(p.i);
739#else
740 if_index = p.s->sdl_index;
741#endif
742 }
Simon Kelley26128d22004-11-14 16:43:54 +0000743 }
744#endif
745
746#ifdef HAVE_IPV6
747 if (listen->family == AF_INET6)
748 {
749 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000750 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
Simon Kelley26128d22004-11-14 16:43:54 +0000751 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100752 union {
753 unsigned char *c;
754 struct in6_pktinfo *p;
755 } p;
756 p.c = CMSG_DATA(cmptr);
757
758 dst_addr.addr.addr6 = p.p->ipi6_addr;
759 if_index = p.p->ipi6_ifindex;
Simon Kelley26128d22004-11-14 16:43:54 +0000760 }
761 }
762#endif
763
764 /* enforce available interface configuration */
765
Simon Kelleye25db1f2013-01-29 22:10:26 +0000766 if (!indextoname(listen->fd, if_index, ifr.ifr_name))
Simon Kelley832af0b2007-01-21 20:01:28 +0000767 return;
768
Simon Kelleye25db1f2013-01-29 22:10:26 +0000769 if (!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))
770 {
771 if (!option_bool(OPT_CLEVERBIND))
772 enumerate_interfaces();
773 if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name))
774 return;
775 }
776
Simon Kelley552af8b2012-02-29 20:10:31 +0000777 if (listen->family == AF_INET && option_bool(OPT_LOCALISE))
778 {
779 struct irec *iface;
780
781 /* get the netmask of the interface whch has the address we were sent to.
782 This is no neccessarily the interface we arrived on. */
783
784 for (iface = daemon->interfaces; iface; iface = iface->next)
785 if (iface->addr.sa.sa_family == AF_INET &&
786 iface->addr.in.sin_addr.s_addr == dst_addr_4.s_addr)
787 break;
788
789 /* interface may be new */
Simon Kelleye25db1f2013-01-29 22:10:26 +0000790 if (!iface && !option_bool(OPT_CLEVERBIND))
Simon Kelley552af8b2012-02-29 20:10:31 +0000791 enumerate_interfaces();
792
793 for (iface = daemon->interfaces; iface; iface = iface->next)
794 if (iface->addr.sa.sa_family == AF_INET &&
795 iface->addr.in.sin_addr.s_addr == dst_addr_4.s_addr)
796 break;
797
798 /* If we failed, abandon localisation */
799 if (iface)
800 netmask = iface->netmask;
801 else
802 dst_addr_4.s_addr = 0;
803 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000804 }
805
Simon Kelleycdeda282006-03-16 20:16:06 +0000806 if (extract_request(header, (size_t)n, daemon->namebuff, &type))
Simon Kelley44a2a312004-03-10 20:04:35 +0000807 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100808 char types[20];
809
Simon Kelley4f7b3042012-11-28 21:27:02 +0000810 querystr(auth_dns ? "auth" : "query", types, type);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100811
Simon Kelley44a2a312004-03-10 20:04:35 +0000812 if (listen->family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100813 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100814 (struct all_addr *)&source_addr.in.sin_addr, types);
Simon Kelley44a2a312004-03-10 20:04:35 +0000815#ifdef HAVE_IPV6
816 else
Simon Kelley3be34542004-09-11 19:12:13 +0100817 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100818 (struct all_addr *)&source_addr.in6.sin6_addr, types);
Simon Kelley44a2a312004-03-10 20:04:35 +0000819#endif
820 }
821
Simon Kelley4820dce2012-12-18 18:30:30 +0000822#ifdef HAVE_AUTH
Simon Kelley4f7b3042012-11-28 21:27:02 +0000823 if (auth_dns)
Simon Kelley824af852008-02-12 20:43:05 +0000824 {
Simon Kelley49678762012-12-09 18:24:58 +0000825 m = answer_auth(header, ((char *) header) + PACKETSZ, (size_t)n, now, &source_addr);
Simon Kelley4f7b3042012-11-28 21:27:02 +0000826 if (m >= 1)
827 send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
828 (char *)header, m, &source_addr, &dst_addr, if_index);
Simon Kelley824af852008-02-12 20:43:05 +0000829 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000830 else
Simon Kelley4820dce2012-12-18 18:30:30 +0000831#endif
Simon Kelley4f7b3042012-11-28 21:27:02 +0000832 {
833 m = answer_request(header, ((char *) header) + PACKETSZ, (size_t)n,
834 dst_addr_4, netmask, now);
835
836 if (m >= 1)
837 {
838 send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
839 (char *)header, m, &source_addr, &dst_addr, if_index);
840 daemon->local_answer++;
841 }
842 else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
843 header, (size_t)n, now, NULL))
844 daemon->queries_forwarded++;
845 else
846 daemon->local_answer++;
847 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000848}
849
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100850/* The daemon forks before calling this: it should deal with one connection,
851 blocking as neccessary, and then return. Note, need to be a bit careful
852 about resources for debug mode, when the fork is suppressed: that's
853 done by the caller. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100854unsigned char *tcp_request(int confd, time_t now,
Simon Kelley4f7b3042012-11-28 21:27:02 +0000855 union mysockaddr *local_addr, struct in_addr netmask, int auth_dns)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100856{
Simon Kelley28866e92011-02-14 20:19:14 +0000857 size_t size = 0;
858 int norebind = 0;
859 int checking_disabled;
Simon Kelleycdeda282006-03-16 20:16:06 +0000860 size_t m;
Simon Kelleyee86ce62012-12-07 11:54:46 +0000861 unsigned short qtype;
862 unsigned int gotname;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100863 unsigned char c1, c2;
864 /* Max TCP packet + slop */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100865 unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
Simon Kelley572b41e2011-02-18 18:11:18 +0000866 struct dns_header *header;
Simon Kelley3be34542004-09-11 19:12:13 +0100867 struct server *last_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100868 struct in_addr dst_addr_4;
869 union mysockaddr peer_addr;
870 socklen_t peer_len = sizeof(union mysockaddr);
Simon Kelley3be34542004-09-11 19:12:13 +0100871
Simon Kelley7de060b2011-08-26 17:24:52 +0100872 if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
873 return packet;
874
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100875 while (1)
876 {
877 if (!packet ||
878 !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
879 !(size = c1 << 8 | c2) ||
880 !read_write(confd, packet, size, 1))
881 return packet;
882
Simon Kelley572b41e2011-02-18 18:11:18 +0000883 if (size < (int)sizeof(struct dns_header))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100884 continue;
885
Simon Kelley572b41e2011-02-18 18:11:18 +0000886 header = (struct dns_header *)packet;
Simon Kelley28866e92011-02-14 20:19:14 +0000887
888 /* save state of "cd" flag in query */
Simon Kelley572b41e2011-02-18 18:11:18 +0000889 checking_disabled = header->hb4 & HB4_CD;
Simon Kelley28866e92011-02-14 20:19:14 +0000890
891 /* RFC 4035: sect 4.6 para 2 */
Simon Kelley572b41e2011-02-18 18:11:18 +0000892 header->hb4 &= ~HB4_AD;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100893
Simon Kelley3be34542004-09-11 19:12:13 +0100894 if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100895 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100896 char types[20];
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100897
Simon Kelley4f7b3042012-11-28 21:27:02 +0000898 querystr(auth_dns ? "auth" : "query", types, qtype);
Simon Kelley7de060b2011-08-26 17:24:52 +0100899
900 if (peer_addr.sa.sa_family == AF_INET)
901 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
902 (struct all_addr *)&peer_addr.in.sin_addr, types);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100903#ifdef HAVE_IPV6
Simon Kelley7de060b2011-08-26 17:24:52 +0100904 else
905 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
906 (struct all_addr *)&peer_addr.in6.sin6_addr, types);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100907#endif
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100908 }
909
Simon Kelley7de060b2011-08-26 17:24:52 +0100910 if (local_addr->sa.sa_family == AF_INET)
911 dst_addr_4 = local_addr->in.sin_addr;
912 else
913 dst_addr_4.s_addr = 0;
914
Simon Kelley4820dce2012-12-18 18:30:30 +0000915#ifdef HAVE_AUTH
Simon Kelley4f7b3042012-11-28 21:27:02 +0000916 if (auth_dns)
Simon Kelley49678762012-12-09 18:24:58 +0000917 m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr);
Simon Kelley4f7b3042012-11-28 21:27:02 +0000918 else
Simon Kelley4820dce2012-12-18 18:30:30 +0000919#endif
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100920 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000921 /* m > 0 if answered from cache */
922 m = answer_request(header, ((char *) header) + 65536, (size_t)size,
923 dst_addr_4, netmask, now);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100924
Simon Kelley4f7b3042012-11-28 21:27:02 +0000925 /* Do this by steam now we're not in the select() loop */
926 check_log_writer(NULL);
927
928 if (m == 0)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100929 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000930 unsigned int flags = 0;
931 struct all_addr *addrp = NULL;
932 int type = 0;
933 char *domain = NULL;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100934
Simon Kelley4f7b3042012-11-28 21:27:02 +0000935 if (option_bool(OPT_ADD_MAC))
936 size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
937
938 if (gotname)
939 flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
940
941 if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
942 last_server = daemon->servers;
943 else
944 last_server = daemon->last_server;
945
946 if (!flags && last_server)
947 {
948 struct server *firstsendto = NULL;
949 unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
950
951 /* Loop round available servers until we succeed in connecting to one.
952 Note that this code subtley ensures that consecutive queries on this connection
953 which can go to the same server, do so. */
954 while (1)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100955 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000956 if (!firstsendto)
957 firstsendto = last_server;
958 else
959 {
960 if (!(last_server = last_server->next))
961 last_server = daemon->servers;
962
963 if (last_server == firstsendto)
964 break;
965 }
966
967 /* server for wrong domain */
968 if (type != (last_server->flags & SERV_TYPE) ||
969 (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
Simon Kelley7de060b2011-08-26 17:24:52 +0100970 continue;
971
Simon Kelley4f7b3042012-11-28 21:27:02 +0000972 if (last_server->tcpfd == -1)
973 {
974 if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
975 continue;
976
977 if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
978 connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
979 {
980 close(last_server->tcpfd);
981 last_server->tcpfd = -1;
982 continue;
983 }
984
985#ifdef HAVE_CONNTRACK
986 /* Copy connection mark of incoming query to outgoing connection. */
987 if (option_bool(OPT_CONNTRACK))
988 {
989 unsigned int mark;
990 struct all_addr local;
991#ifdef HAVE_IPV6
992 if (local_addr->sa.sa_family == AF_INET6)
993 local.addr.addr6 = local_addr->in6.sin6_addr;
994 else
995#endif
996 local.addr.addr4 = local_addr->in.sin_addr;
997
998 if (get_incoming_mark(&peer_addr, &local, 1, &mark))
999 setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
1000 }
1001#endif
1002 }
1003
1004 c1 = size >> 8;
1005 c2 = size;
1006
1007 if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
1008 !read_write(last_server->tcpfd, &c2, 1, 0) ||
1009 !read_write(last_server->tcpfd, packet, size, 0) ||
1010 !read_write(last_server->tcpfd, &c1, 1, 1) ||
1011 !read_write(last_server->tcpfd, &c2, 1, 1))
Simon Kelley7de060b2011-08-26 17:24:52 +01001012 {
1013 close(last_server->tcpfd);
1014 last_server->tcpfd = -1;
1015 continue;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001016 }
1017
1018 m = (c1 << 8) | c2;
1019 if (!read_write(last_server->tcpfd, packet, m, 1))
1020 return packet;
1021
1022 if (!gotname)
1023 strcpy(daemon->namebuff, "query");
1024 if (last_server->addr.sa.sa_family == AF_INET)
1025 log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
1026 (struct all_addr *)&last_server->addr.in.sin_addr, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001027#ifdef HAVE_IPV6
Simon Kelley4f7b3042012-11-28 21:27:02 +00001028 else
1029 log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
1030 (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001031#endif
Simon Kelley4f7b3042012-11-28 21:27:02 +00001032
1033 /* There's no point in updating the cache, since this process will exit and
1034 lose the information after a few queries. We make this call for the alias and
1035 bogus-nxdomain side-effects. */
1036 /* If the crc of the question section doesn't match the crc we sent, then
1037 someone might be attempting to insert bogus values into the cache by
1038 sending replies containing questions and bogus answers. */
1039 if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
1040 m = process_reply(header, now, last_server, (unsigned int)m,
1041 option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
1042
1043 break;
1044 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001045 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00001046
1047 /* In case of local answer or no connections made. */
1048 if (m == 0)
1049 m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001050 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001051 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00001052
Simon Kelley5aabfc72007-08-29 11:24:47 +01001053 check_log_writer(NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001054
1055 c1 = m>>8;
1056 c2 = m;
Simon Kelley49678762012-12-09 18:24:58 +00001057 if (m == 0 ||
1058 !read_write(confd, &c1, 1, 0) ||
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001059 !read_write(confd, &c2, 1, 0) ||
1060 !read_write(confd, packet, m, 0))
1061 return packet;
1062 }
1063}
1064
Simon Kelley16972692006-10-16 20:04:18 +01001065static struct frec *allocate_frec(time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001066{
Simon Kelley16972692006-10-16 20:04:18 +01001067 struct frec *f;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001068
Simon Kelley5aabfc72007-08-29 11:24:47 +01001069 if ((f = (struct frec *)whine_malloc(sizeof(struct frec))))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001070 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001071 f->next = daemon->frec_list;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001072 f->time = now;
Simon Kelley832af0b2007-01-21 20:01:28 +00001073 f->sentto = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001074 f->rfd4 = NULL;
Simon Kelley28866e92011-02-14 20:19:14 +00001075 f->flags = 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001076#ifdef HAVE_IPV6
1077 f->rfd6 = NULL;
1078#endif
1079 daemon->frec_list = f;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001080 }
Simon Kelley16972692006-10-16 20:04:18 +01001081
1082 return f;
1083}
1084
Simon Kelley1a6bca82008-07-11 11:11:42 +01001085static struct randfd *allocate_rfd(int family)
1086{
1087 static int finger = 0;
1088 int i;
1089
1090 /* limit the number of sockets we have open to avoid starvation of
1091 (eg) TFTP. Once we have a reasonable number, randomness should be OK */
1092
1093 for (i = 0; i < RANDOM_SOCKS; i++)
Simon Kelley9009d742008-11-14 20:04:27 +00001094 if (daemon->randomsocks[i].refcount == 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001095 {
Simon Kelley9009d742008-11-14 20:04:27 +00001096 if ((daemon->randomsocks[i].fd = random_sock(family)) == -1)
1097 break;
1098
Simon Kelley1a6bca82008-07-11 11:11:42 +01001099 daemon->randomsocks[i].refcount = 1;
1100 daemon->randomsocks[i].family = family;
1101 return &daemon->randomsocks[i];
1102 }
1103
Simon Kelley9009d742008-11-14 20:04:27 +00001104 /* No free ones or cannot get new socket, grab an existing one */
Simon Kelley1a6bca82008-07-11 11:11:42 +01001105 for (i = 0; i < RANDOM_SOCKS; i++)
1106 {
1107 int j = (i+finger) % RANDOM_SOCKS;
Simon Kelley9009d742008-11-14 20:04:27 +00001108 if (daemon->randomsocks[j].refcount != 0 &&
1109 daemon->randomsocks[j].family == family &&
1110 daemon->randomsocks[j].refcount != 0xffff)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001111 {
1112 finger = j;
1113 daemon->randomsocks[j].refcount++;
1114 return &daemon->randomsocks[j];
1115 }
1116 }
1117
1118 return NULL; /* doom */
1119}
1120
1121static void free_frec(struct frec *f)
1122{
1123 if (f->rfd4 && --(f->rfd4->refcount) == 0)
1124 close(f->rfd4->fd);
1125
1126 f->rfd4 = NULL;
1127 f->sentto = NULL;
Simon Kelley28866e92011-02-14 20:19:14 +00001128 f->flags = 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001129
1130#ifdef HAVE_IPV6
1131 if (f->rfd6 && --(f->rfd6->refcount) == 0)
1132 close(f->rfd6->fd);
1133
1134 f->rfd6 = NULL;
1135#endif
1136}
1137
Simon Kelley16972692006-10-16 20:04:18 +01001138/* if wait==NULL return a free or older than TIMEOUT record.
1139 else return *wait zero if one available, or *wait is delay to
Simon Kelley1a6bca82008-07-11 11:11:42 +01001140 when the oldest in-use record will expire. Impose an absolute
1141 limit of 4*TIMEOUT before we wipe things (for random sockets) */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001142struct frec *get_new_frec(time_t now, int *wait)
Simon Kelley16972692006-10-16 20:04:18 +01001143{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001144 struct frec *f, *oldest, *target;
Simon Kelley16972692006-10-16 20:04:18 +01001145 int count;
1146
1147 if (wait)
1148 *wait = 0;
1149
Simon Kelley1a6bca82008-07-11 11:11:42 +01001150 for (f = daemon->frec_list, oldest = NULL, target = NULL, count = 0; f; f = f->next, count++)
Simon Kelley832af0b2007-01-21 20:01:28 +00001151 if (!f->sentto)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001152 target = f;
1153 else
Simon Kelley16972692006-10-16 20:04:18 +01001154 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001155 if (difftime(now, f->time) >= 4*TIMEOUT)
1156 {
1157 free_frec(f);
1158 target = f;
1159 }
1160
1161 if (!oldest || difftime(f->time, oldest->time) <= 0)
1162 oldest = f;
Simon Kelley16972692006-10-16 20:04:18 +01001163 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001164
1165 if (target)
1166 {
1167 target->time = now;
1168 return target;
1169 }
Simon Kelley16972692006-10-16 20:04:18 +01001170
1171 /* can't find empty one, use oldest if there is one
1172 and it's older than timeout */
1173 if (oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
1174 {
1175 /* keep stuff for twice timeout if we can by allocating a new
1176 record instead */
1177 if (difftime(now, oldest->time) < 2*TIMEOUT &&
1178 count <= daemon->ftabsize &&
1179 (f = allocate_frec(now)))
1180 return f;
1181
1182 if (!wait)
1183 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001184 free_frec(oldest);
Simon Kelley16972692006-10-16 20:04:18 +01001185 oldest->time = now;
1186 }
1187 return oldest;
1188 }
1189
1190 /* none available, calculate time 'till oldest record expires */
1191 if (count > daemon->ftabsize)
1192 {
1193 if (oldest && wait)
1194 *wait = oldest->time + (time_t)TIMEOUT - now;
1195 return NULL;
1196 }
1197
1198 if (!(f = allocate_frec(now)) && wait)
1199 /* wait one second on malloc failure */
1200 *wait = 1;
1201
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001202 return f; /* OK if malloc fails and this is NULL */
1203}
1204
Simon Kelley832af0b2007-01-21 20:01:28 +00001205/* crc is all-ones if not known. */
1206static struct frec *lookup_frec(unsigned short id, unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001207{
1208 struct frec *f;
1209
Simon Kelley1a6bca82008-07-11 11:11:42 +01001210 for(f = daemon->frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +00001211 if (f->sentto && f->new_id == id &&
1212 (f->crc == crc || crc == 0xffffffff))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001213 return f;
1214
1215 return NULL;
1216}
1217
1218static struct frec *lookup_frec_by_sender(unsigned short id,
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001219 union mysockaddr *addr,
1220 unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001221{
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001222 struct frec *f;
1223
Simon Kelley1a6bca82008-07-11 11:11:42 +01001224 for(f = daemon->frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +00001225 if (f->sentto &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001226 f->orig_id == id &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001227 f->crc == crc &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001228 sockaddr_isequal(&f->source, addr))
1229 return f;
1230
1231 return NULL;
1232}
1233
Simon Kelley849a8352006-06-09 21:02:31 +01001234/* A server record is going away, remove references to it */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001235void server_gone(struct server *server)
Simon Kelley849a8352006-06-09 21:02:31 +01001236{
1237 struct frec *f;
1238
Simon Kelley1a6bca82008-07-11 11:11:42 +01001239 for (f = daemon->frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +00001240 if (f->sentto && f->sentto == server)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001241 free_frec(f);
Simon Kelley849a8352006-06-09 21:02:31 +01001242
1243 if (daemon->last_server == server)
1244 daemon->last_server = NULL;
1245
1246 if (daemon->srv_save == server)
1247 daemon->srv_save = NULL;
1248}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001249
Simon Kelley316e2732010-01-22 20:16:09 +00001250/* return unique random ids. */
1251static unsigned short get_id(unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001252{
1253 unsigned short ret = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001254
Simon Kelley316e2732010-01-22 20:16:09 +00001255 do
Simon Kelley832af0b2007-01-21 20:01:28 +00001256 ret = rand16();
1257 while (lookup_frec(ret, crc));
1258
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001259 return ret;
1260}
1261
1262
1263
1264
1265