blob: 33a68a0dcb12374a2e44ef06b90f946b5ee9aa74 [file] [log] [blame]
Simon Kelley61744352013-01-31 14:34:40 +00001/* dnsmasq is Copyright (c) 2000-2013 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
Giacomo Tazzari797a7af2013-04-22 13:16:37 +0100331 if (option_bool(OPT_ADD_MAC))
332 plen = add_mac(header, plen, ((char *) header) + PACKETSZ, &forward->source);
Simon Kelley28866e92011-02-14 20:19:14 +0000333
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;
Giacomo Tazzari797a7af2013-04-22 13:16:37 +0100375 if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark))
Simon Kelley7de060b2011-08-26 17:24:52 +0100376 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;
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000442 char **sets = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000443 int munged = 0, is_sign;
Simon Kelleycdeda282006-03-16 20:16:06 +0000444 size_t plen;
445
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000446#ifdef HAVE_IPSET
447 /* Similar algorithm to search_servers. */
448 struct ipsets *ipset_pos;
449 unsigned int namelen = strlen(daemon->namebuff);
450 unsigned int matchlen = 0;
451 for (ipset_pos = daemon->ipsets; ipset_pos; ipset_pos = ipset_pos->next)
452 {
453 unsigned int domainlen = strlen(ipset_pos->domain);
454 char *matchstart = daemon->namebuff + namelen - domainlen;
455 if (namelen >= domainlen && hostname_isequal(matchstart, ipset_pos->domain) &&
456 (domainlen == 0 || namelen == domainlen || *(matchstart - 1) == '.' ) &&
457 domainlen >= matchlen) {
458 matchlen = domainlen;
459 sets = ipset_pos->sets;
460 }
461 }
462#endif
463
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100464 /* If upstream is advertising a larger UDP packet size
Simon Kelley9009d742008-11-14 20:04:27 +0000465 than we allow, trim it so that we don't get overlarge
466 requests for the client. We can't do this for signed packets. */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100467
Simon Kelley832af0b2007-01-21 20:01:28 +0000468 if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)) && !is_sign)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100469 {
470 unsigned short udpsz;
Simon Kelley36717ee2004-09-20 19:20:58 +0100471 unsigned char *psave = sizep;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100472
Simon Kelley36717ee2004-09-20 19:20:58 +0100473 GETSHORT(udpsz, sizep);
Simon Kelley3be34542004-09-11 19:12:13 +0100474 if (udpsz > daemon->edns_pktsz)
475 PUTSHORT(daemon->edns_pktsz, psave);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100476 }
477
Simon Kelley28866e92011-02-14 20:19:14 +0000478 /* RFC 4035 sect 4.6 para 3 */
479 if (!is_sign && !option_bool(OPT_DNSSEC))
Simon Kelley572b41e2011-02-18 18:11:18 +0000480 header->hb4 &= ~HB4_AD;
Simon Kelley28866e92011-02-14 20:19:14 +0000481
Simon Kelley572b41e2011-02-18 18:11:18 +0000482 if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
Simon Kelley36717ee2004-09-20 19:20:58 +0100483 return n;
484
Simon Kelley0a852542005-03-23 20:28:59 +0000485 /* Complain loudly if the upstream server is non-recursive. */
Simon Kelley572b41e2011-02-18 18:11:18 +0000486 if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR && ntohs(header->ancount) == 0 &&
Simon Kelley0a852542005-03-23 20:28:59 +0000487 server && !(server->flags & SERV_WARNED_RECURSIVE))
488 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100489 prettyprint_addr(&server->addr, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100490 my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
Simon Kelley28866e92011-02-14 20:19:14 +0000491 if (!option_bool(OPT_LOG))
Simon Kelley0a852542005-03-23 20:28:59 +0000492 server->flags |= SERV_WARNED_RECURSIVE;
493 }
494
Simon Kelley572b41e2011-02-18 18:11:18 +0000495 if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100496 check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100497 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100498 munged = 1;
Simon Kelley572b41e2011-02-18 18:11:18 +0000499 SET_RCODE(header, NXDOMAIN);
500 header->hb3 &= ~HB3_AA;
Simon Kelley36717ee2004-09-20 19:20:58 +0100501 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100502 else
Simon Kelley36717ee2004-09-20 19:20:58 +0100503 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000504 if (RCODE(header) == NXDOMAIN &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100505 extract_request(header, n, daemon->namebuff, NULL) &&
Simon Kelley5aabfc72007-08-29 11:24:47 +0100506 check_for_local_domain(daemon->namebuff, now))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100507 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100508 /* if we forwarded a query for a locally known name (because it was for
509 an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
510 since we know that the domain exists, even if upstream doesn't */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100511 munged = 1;
Simon Kelley572b41e2011-02-18 18:11:18 +0000512 header->hb3 |= HB3_AA;
513 SET_RCODE(header, NOERROR);
Simon Kelley36717ee2004-09-20 19:20:58 +0100514 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000515
Jason A. Donenfeld13d86c72013-02-22 18:20:53 +0000516 if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, checking_disabled))
Simon Kelley824af852008-02-12 20:43:05 +0000517 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100518 my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
Simon Kelley824af852008-02-12 20:43:05 +0000519 munged = 1;
520 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100521 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100522
523 /* do this after extract_addresses. Ensure NODATA reply and remove
524 nameserver info. */
525
526 if (munged)
527 {
528 header->ancount = htons(0);
529 header->nscount = htons(0);
530 header->arcount = htons(0);
531 }
532
Simon Kelley36717ee2004-09-20 19:20:58 +0100533 /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
534 sections of the packet. Find the new length here and put back pseudoheader
535 if it was removed. */
536 return resize_packet(header, n, pheader, plen);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100537}
538
Simon Kelley3be34542004-09-11 19:12:13 +0100539/* sets new last_server */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100540void reply_query(int fd, int family, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000541{
542 /* packet from peer server, extract data for cache, and send to
543 original requester */
Simon Kelley572b41e2011-02-18 18:11:18 +0000544 struct dns_header *header;
Simon Kelleyde379512004-06-22 20:23:33 +0100545 union mysockaddr serveraddr;
Simon Kelley832af0b2007-01-21 20:01:28 +0000546 struct frec *forward;
Simon Kelleyde379512004-06-22 20:23:33 +0100547 socklen_t addrlen = sizeof(serveraddr);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100548 ssize_t n = recvfrom(fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
Simon Kelleycdeda282006-03-16 20:16:06 +0000549 size_t nn;
Simon Kelley1a6bca82008-07-11 11:11:42 +0100550 struct server *server;
551
Simon Kelleycdeda282006-03-16 20:16:06 +0000552 /* packet buffer overwritten */
553 daemon->srv_save = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +0000554
Simon Kelleyde379512004-06-22 20:23:33 +0100555 /* Determine the address of the server replying so that we can mark that as good */
Simon Kelley1a6bca82008-07-11 11:11:42 +0100556 serveraddr.sa.sa_family = family;
Simon Kelleyde379512004-06-22 20:23:33 +0100557#ifdef HAVE_IPV6
558 if (serveraddr.sa.sa_family == AF_INET6)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100559 serveraddr.in6.sin6_flowinfo = 0;
Simon Kelleyde379512004-06-22 20:23:33 +0100560#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000561
Simon Kelley1a6bca82008-07-11 11:11:42 +0100562 /* spoof check: answer must come from known server, */
563 for (server = daemon->servers; server; server = server->next)
564 if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
565 sockaddr_isequal(&server->addr, &serveraddr))
566 break;
567
Simon Kelley572b41e2011-02-18 18:11:18 +0000568 header = (struct dns_header *)daemon->packet;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100569
Simon Kelley1a6bca82008-07-11 11:11:42 +0100570 if (!server ||
Simon Kelley572b41e2011-02-18 18:11:18 +0000571 n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) ||
Simon Kelley1a6bca82008-07-11 11:11:42 +0100572 !(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
573 return;
574
575 server = forward->sentto;
576
Simon Kelley572b41e2011-02-18 18:11:18 +0000577 if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
Simon Kelley28866e92011-02-14 20:19:14 +0000578 !option_bool(OPT_ORDER) &&
Simon Kelley1a6bca82008-07-11 11:11:42 +0100579 forward->forwardall == 0)
580 /* for broken servers, attempt to send to another one. */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000581 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100582 unsigned char *pheader;
583 size_t plen;
584 int is_sign;
Simon Kelley832af0b2007-01-21 20:01:28 +0000585
Simon Kelley1a6bca82008-07-11 11:11:42 +0100586 /* recreate query from reply */
587 pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign);
588 if (!is_sign)
Simon Kelley832af0b2007-01-21 20:01:28 +0000589 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100590 header->ancount = htons(0);
591 header->nscount = htons(0);
592 header->arcount = htons(0);
593 if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
594 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000595 header->hb3 &= ~(HB3_QR | HB3_TC);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100596 forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
597 return;
598 }
599 }
600 }
601
602 if ((forward->sentto->flags & SERV_TYPE) == 0)
603 {
Simon Kelley572b41e2011-02-18 18:11:18 +0000604 if (RCODE(header) == SERVFAIL || RCODE(header) == REFUSED)
Simon Kelley1a6bca82008-07-11 11:11:42 +0100605 server = NULL;
606 else
607 {
608 struct server *last_server;
Simon Kelley832af0b2007-01-21 20:01:28 +0000609
Simon Kelley1a6bca82008-07-11 11:11:42 +0100610 /* find good server by address if possible, otherwise assume the last one we sent to */
611 for (last_server = daemon->servers; last_server; last_server = last_server->next)
612 if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
613 sockaddr_isequal(&last_server->addr, &serveraddr))
614 {
615 server = last_server;
616 break;
617 }
618 }
Simon Kelley28866e92011-02-14 20:19:14 +0000619 if (!option_bool(OPT_ALL_SERVERS))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100620 daemon->last_server = server;
621 }
622
623 /* If the answer is an error, keep the forward record in place in case
624 we get a good reply from another server. Kill it when we've
625 had replies from all to avoid filling the forwarding table when
626 everything is broken */
627 if (forward->forwardall == 0 || --forward->forwardall == 1 ||
Simon Kelley572b41e2011-02-18 18:11:18 +0000628 (RCODE(header) != REFUSED && RCODE(header) != SERVFAIL))
Simon Kelley1a6bca82008-07-11 11:11:42 +0100629 {
Simon Kelley28866e92011-02-14 20:19:14 +0000630 int check_rebind = !(forward->flags & FREC_NOREBIND);
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100631
Simon Kelley28866e92011-02-14 20:19:14 +0000632 if (!option_bool(OPT_NO_REBIND))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100633 check_rebind = 0;
634
Simon Kelley28866e92011-02-14 20:19:14 +0000635 if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, forward->flags & FREC_CHECKING_DISABLED)))
Simon Kelley832af0b2007-01-21 20:01:28 +0000636 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100637 header->id = htons(forward->orig_id);
Simon Kelley572b41e2011-02-18 18:11:18 +0000638 header->hb4 |= HB4_RA; /* recursion if available */
Simon Kelley54dd3932012-06-20 11:23:38 +0100639 send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
Simon Kelley50303b12012-04-04 22:13:17 +0100640 &forward->source, &forward->dest, forward->iface);
Simon Kelley832af0b2007-01-21 20:01:28 +0000641 }
Simon Kelley1a6bca82008-07-11 11:11:42 +0100642 free_frec(forward); /* cancel */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000643 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000644}
Simon Kelley44a2a312004-03-10 20:04:35 +0000645
Simon Kelley1a6bca82008-07-11 11:11:42 +0100646
Simon Kelley5aabfc72007-08-29 11:24:47 +0100647void receive_query(struct listener *listen, time_t now)
Simon Kelley44a2a312004-03-10 20:04:35 +0000648{
Simon Kelley572b41e2011-02-18 18:11:18 +0000649 struct dns_header *header = (struct dns_header *)daemon->packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000650 union mysockaddr source_addr;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100651 unsigned short type;
Simon Kelley44a2a312004-03-10 20:04:35 +0000652 struct all_addr dst_addr;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000653 struct in_addr netmask, dst_addr_4;
Simon Kelleycdeda282006-03-16 20:16:06 +0000654 size_t m;
655 ssize_t n;
656 int if_index = 0;
Simon Kelley4f7b3042012-11-28 21:27:02 +0000657 int auth_dns = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000658 struct iovec iov[1];
659 struct msghdr msg;
660 struct cmsghdr *cmptr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000661 union {
662 struct cmsghdr align; /* this ensures alignment */
663#ifdef HAVE_IPV6
664 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
665#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100666#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000667 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
Simon Kelley824af852008-02-12 20:43:05 +0000668#elif defined(IP_RECVDSTADDR) && defined(HAVE_SOLARIS_NETWORK)
669 char control[CMSG_SPACE(sizeof(struct in_addr)) +
670 CMSG_SPACE(sizeof(unsigned int))];
Simon Kelley44a2a312004-03-10 20:04:35 +0000671#elif defined(IP_RECVDSTADDR)
672 char control[CMSG_SPACE(sizeof(struct in_addr)) +
673 CMSG_SPACE(sizeof(struct sockaddr_dl))];
674#endif
675 } control_u;
676
Simon Kelleycdeda282006-03-16 20:16:06 +0000677 /* packet buffer overwritten */
678 daemon->srv_save = NULL;
679
Simon Kelley4f7b3042012-11-28 21:27:02 +0000680 dst_addr_4.s_addr = 0;
681 netmask.s_addr = 0;
682
Simon Kelley7e5664b2013-04-05 16:57:41 +0100683 if (option_bool(OPT_NOWILD) && listen->iface)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000684 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000685 auth_dns = listen->iface->dns_auth;
686
687 if (listen->family == AF_INET)
688 {
689 dst_addr_4 = listen->iface->addr.in.sin_addr;
690 netmask = listen->iface->netmask;
691 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000692 }
Simon Kelley4f7b3042012-11-28 21:27:02 +0000693
Simon Kelley3be34542004-09-11 19:12:13 +0100694 iov[0].iov_base = daemon->packet;
695 iov[0].iov_len = daemon->edns_pktsz;
Simon Kelley44a2a312004-03-10 20:04:35 +0000696
697 msg.msg_control = control_u.control;
698 msg.msg_controllen = sizeof(control_u);
699 msg.msg_flags = 0;
700 msg.msg_name = &source_addr;
701 msg.msg_namelen = sizeof(source_addr);
702 msg.msg_iov = iov;
703 msg.msg_iovlen = 1;
704
Simon Kelleyde379512004-06-22 20:23:33 +0100705 if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
Simon Kelley3be34542004-09-11 19:12:13 +0100706 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000707
Simon Kelley572b41e2011-02-18 18:11:18 +0000708 if (n < (int)sizeof(struct dns_header) ||
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100709 (msg.msg_flags & MSG_TRUNC) ||
Simon Kelley572b41e2011-02-18 18:11:18 +0000710 (header->hb3 & HB3_QR))
Simon Kelley3be34542004-09-11 19:12:13 +0100711 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000712
Simon Kelley26128d22004-11-14 16:43:54 +0000713 source_addr.sa.sa_family = listen->family;
714#ifdef HAVE_IPV6
715 if (listen->family == AF_INET6)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100716 source_addr.in6.sin6_flowinfo = 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000717#endif
Simon Kelley28866e92011-02-14 20:19:14 +0000718
719 if (!option_bool(OPT_NOWILD))
Simon Kelley44a2a312004-03-10 20:04:35 +0000720 {
Simon Kelley8a911cc2004-03-16 18:35:52 +0000721 struct ifreq ifr;
722
Simon Kelley26128d22004-11-14 16:43:54 +0000723 if (msg.msg_controllen < sizeof(struct cmsghdr))
724 return;
725
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100726#if defined(HAVE_LINUX_NETWORK)
Simon Kelley26128d22004-11-14 16:43:54 +0000727 if (listen->family == AF_INET)
728 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000729 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
Simon Kelley26128d22004-11-14 16:43:54 +0000730 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100731 union {
732 unsigned char *c;
733 struct in_pktinfo *p;
734 } p;
735 p.c = CMSG_DATA(cmptr);
736 dst_addr_4 = dst_addr.addr.addr4 = p.p->ipi_spec_dst;
737 if_index = p.p->ipi_ifindex;
Simon Kelley26128d22004-11-14 16:43:54 +0000738 }
739#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
740 if (listen->family == AF_INET)
741 {
742 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100743 {
744 union {
745 unsigned char *c;
746 unsigned int *i;
747 struct in_addr *a;
748#ifndef HAVE_SOLARIS_NETWORK
749 struct sockaddr_dl *s;
Simon Kelley824af852008-02-12 20:43:05 +0000750#endif
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100751 } p;
752 p.c = CMSG_DATA(cmptr);
753 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
754 dst_addr_4 = dst_addr.addr.addr4 = *(p.a);
755 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
756#ifdef HAVE_SOLARIS_NETWORK
757 if_index = *(p.i);
758#else
759 if_index = p.s->sdl_index;
760#endif
761 }
Simon Kelley26128d22004-11-14 16:43:54 +0000762 }
763#endif
764
765#ifdef HAVE_IPV6
766 if (listen->family == AF_INET6)
767 {
768 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
Simon Kelleyc72daea2012-01-05 21:33:27 +0000769 if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
Simon Kelley26128d22004-11-14 16:43:54 +0000770 {
Simon Kelley8ef5ada2010-06-03 19:42:45 +0100771 union {
772 unsigned char *c;
773 struct in6_pktinfo *p;
774 } p;
775 p.c = CMSG_DATA(cmptr);
776
777 dst_addr.addr.addr6 = p.p->ipi6_addr;
778 if_index = p.p->ipi6_ifindex;
Simon Kelley26128d22004-11-14 16:43:54 +0000779 }
780 }
781#endif
782
783 /* enforce available interface configuration */
784
Simon Kelleye25db1f2013-01-29 22:10:26 +0000785 if (!indextoname(listen->fd, if_index, ifr.ifr_name))
Simon Kelley832af0b2007-01-21 20:01:28 +0000786 return;
787
Simon Kelleye25db1f2013-01-29 22:10:26 +0000788 if (!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))
789 {
790 if (!option_bool(OPT_CLEVERBIND))
Simon Kelley115ac3e2013-05-20 11:28:32 +0100791 enumerate_interfaces(0);
Simon Kelley3f2873d2013-05-14 11:28:47 +0100792 if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name) &&
793 !label_exception(if_index, listen->family, &dst_addr))
Simon Kelleye25db1f2013-01-29 22:10:26 +0000794 return;
795 }
796
Simon Kelley552af8b2012-02-29 20:10:31 +0000797 if (listen->family == AF_INET && option_bool(OPT_LOCALISE))
798 {
799 struct irec *iface;
800
801 /* get the netmask of the interface whch has the address we were sent to.
802 This is no neccessarily the interface we arrived on. */
803
804 for (iface = daemon->interfaces; iface; iface = iface->next)
805 if (iface->addr.sa.sa_family == AF_INET &&
806 iface->addr.in.sin_addr.s_addr == dst_addr_4.s_addr)
807 break;
808
809 /* interface may be new */
Simon Kelleye25db1f2013-01-29 22:10:26 +0000810 if (!iface && !option_bool(OPT_CLEVERBIND))
Simon Kelley115ac3e2013-05-20 11:28:32 +0100811 enumerate_interfaces(0);
Simon Kelley552af8b2012-02-29 20:10:31 +0000812
813 for (iface = daemon->interfaces; iface; iface = iface->next)
814 if (iface->addr.sa.sa_family == AF_INET &&
815 iface->addr.in.sin_addr.s_addr == dst_addr_4.s_addr)
816 break;
817
818 /* If we failed, abandon localisation */
819 if (iface)
820 netmask = iface->netmask;
821 else
822 dst_addr_4.s_addr = 0;
823 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000824 }
825
Simon Kelleycdeda282006-03-16 20:16:06 +0000826 if (extract_request(header, (size_t)n, daemon->namebuff, &type))
Simon Kelley44a2a312004-03-10 20:04:35 +0000827 {
Simon Kelley1a6bca82008-07-11 11:11:42 +0100828 char types[20];
829
Simon Kelley4f7b3042012-11-28 21:27:02 +0000830 querystr(auth_dns ? "auth" : "query", types, type);
Simon Kelley1a6bca82008-07-11 11:11:42 +0100831
Simon Kelley44a2a312004-03-10 20:04:35 +0000832 if (listen->family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100833 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100834 (struct all_addr *)&source_addr.in.sin_addr, types);
Simon Kelley44a2a312004-03-10 20:04:35 +0000835#ifdef HAVE_IPV6
836 else
Simon Kelley3be34542004-09-11 19:12:13 +0100837 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelley1a6bca82008-07-11 11:11:42 +0100838 (struct all_addr *)&source_addr.in6.sin6_addr, types);
Simon Kelley44a2a312004-03-10 20:04:35 +0000839#endif
840 }
841
Simon Kelley4820dce2012-12-18 18:30:30 +0000842#ifdef HAVE_AUTH
Simon Kelley4f7b3042012-11-28 21:27:02 +0000843 if (auth_dns)
Simon Kelley824af852008-02-12 20:43:05 +0000844 {
Simon Kelley49678762012-12-09 18:24:58 +0000845 m = answer_auth(header, ((char *) header) + PACKETSZ, (size_t)n, now, &source_addr);
Simon Kelley4f7b3042012-11-28 21:27:02 +0000846 if (m >= 1)
847 send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
848 (char *)header, m, &source_addr, &dst_addr, if_index);
Simon Kelley824af852008-02-12 20:43:05 +0000849 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000850 else
Simon Kelley4820dce2012-12-18 18:30:30 +0000851#endif
Simon Kelley4f7b3042012-11-28 21:27:02 +0000852 {
853 m = answer_request(header, ((char *) header) + PACKETSZ, (size_t)n,
854 dst_addr_4, netmask, now);
855
856 if (m >= 1)
857 {
858 send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
859 (char *)header, m, &source_addr, &dst_addr, if_index);
860 daemon->local_answer++;
861 }
862 else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
863 header, (size_t)n, now, NULL))
864 daemon->queries_forwarded++;
865 else
866 daemon->local_answer++;
867 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000868}
869
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100870/* The daemon forks before calling this: it should deal with one connection,
871 blocking as neccessary, and then return. Note, need to be a bit careful
872 about resources for debug mode, when the fork is suppressed: that's
873 done by the caller. */
Simon Kelley5aabfc72007-08-29 11:24:47 +0100874unsigned char *tcp_request(int confd, time_t now,
Simon Kelley4f7b3042012-11-28 21:27:02 +0000875 union mysockaddr *local_addr, struct in_addr netmask, int auth_dns)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100876{
Simon Kelley28866e92011-02-14 20:19:14 +0000877 size_t size = 0;
878 int norebind = 0;
879 int checking_disabled;
Simon Kelleycdeda282006-03-16 20:16:06 +0000880 size_t m;
Simon Kelleyee86ce62012-12-07 11:54:46 +0000881 unsigned short qtype;
882 unsigned int gotname;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100883 unsigned char c1, c2;
Simon Kelley4b5ea122013-04-22 10:18:26 +0100884 /* Max TCP packet + slop + size */
885 unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
886 unsigned char *payload = &packet[2];
887 /* largest field in header is 16-bits, so this is still sufficiently aligned */
888 struct dns_header *header = (struct dns_header *)payload;
889 u16 *length = (u16 *)packet;
Simon Kelley3be34542004-09-11 19:12:13 +0100890 struct server *last_server;
Simon Kelley7de060b2011-08-26 17:24:52 +0100891 struct in_addr dst_addr_4;
892 union mysockaddr peer_addr;
893 socklen_t peer_len = sizeof(union mysockaddr);
Simon Kelley3be34542004-09-11 19:12:13 +0100894
Simon Kelley7de060b2011-08-26 17:24:52 +0100895 if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
896 return packet;
897
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100898 while (1)
899 {
900 if (!packet ||
901 !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
902 !(size = c1 << 8 | c2) ||
Simon Kelley4b5ea122013-04-22 10:18:26 +0100903 !read_write(confd, payload, size, 1))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100904 return packet;
905
Simon Kelley572b41e2011-02-18 18:11:18 +0000906 if (size < (int)sizeof(struct dns_header))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100907 continue;
908
Simon Kelley28866e92011-02-14 20:19:14 +0000909 /* save state of "cd" flag in query */
Simon Kelley572b41e2011-02-18 18:11:18 +0000910 checking_disabled = header->hb4 & HB4_CD;
Simon Kelley28866e92011-02-14 20:19:14 +0000911
912 /* RFC 4035: sect 4.6 para 2 */
Simon Kelley572b41e2011-02-18 18:11:18 +0000913 header->hb4 &= ~HB4_AD;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100914
Simon Kelley3be34542004-09-11 19:12:13 +0100915 if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100916 {
Simon Kelley7de060b2011-08-26 17:24:52 +0100917 char types[20];
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100918
Simon Kelley4f7b3042012-11-28 21:27:02 +0000919 querystr(auth_dns ? "auth" : "query", types, qtype);
Simon Kelley7de060b2011-08-26 17:24:52 +0100920
921 if (peer_addr.sa.sa_family == AF_INET)
922 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
923 (struct all_addr *)&peer_addr.in.sin_addr, types);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100924#ifdef HAVE_IPV6
Simon Kelley7de060b2011-08-26 17:24:52 +0100925 else
926 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
927 (struct all_addr *)&peer_addr.in6.sin6_addr, types);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100928#endif
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100929 }
930
Simon Kelley7de060b2011-08-26 17:24:52 +0100931 if (local_addr->sa.sa_family == AF_INET)
932 dst_addr_4 = local_addr->in.sin_addr;
933 else
934 dst_addr_4.s_addr = 0;
935
Simon Kelley4820dce2012-12-18 18:30:30 +0000936#ifdef HAVE_AUTH
Simon Kelley4f7b3042012-11-28 21:27:02 +0000937 if (auth_dns)
Simon Kelley49678762012-12-09 18:24:58 +0000938 m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr);
Simon Kelley4f7b3042012-11-28 21:27:02 +0000939 else
Simon Kelley4820dce2012-12-18 18:30:30 +0000940#endif
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100941 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000942 /* m > 0 if answered from cache */
943 m = answer_request(header, ((char *) header) + 65536, (size_t)size,
944 dst_addr_4, netmask, now);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100945
Simon Kelley4f7b3042012-11-28 21:27:02 +0000946 /* Do this by steam now we're not in the select() loop */
947 check_log_writer(NULL);
948
949 if (m == 0)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100950 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000951 unsigned int flags = 0;
952 struct all_addr *addrp = NULL;
953 int type = 0;
954 char *domain = NULL;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100955
Simon Kelley4f7b3042012-11-28 21:27:02 +0000956 if (option_bool(OPT_ADD_MAC))
957 size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
958
959 if (gotname)
960 flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
961
962 if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
963 last_server = daemon->servers;
964 else
965 last_server = daemon->last_server;
966
967 if (!flags && last_server)
968 {
969 struct server *firstsendto = NULL;
970 unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
971
972 /* Loop round available servers until we succeed in connecting to one.
973 Note that this code subtley ensures that consecutive queries on this connection
974 which can go to the same server, do so. */
975 while (1)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100976 {
Simon Kelley4f7b3042012-11-28 21:27:02 +0000977 if (!firstsendto)
978 firstsendto = last_server;
979 else
980 {
981 if (!(last_server = last_server->next))
982 last_server = daemon->servers;
983
984 if (last_server == firstsendto)
985 break;
986 }
987
988 /* server for wrong domain */
989 if (type != (last_server->flags & SERV_TYPE) ||
990 (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
Simon Kelley7de060b2011-08-26 17:24:52 +0100991 continue;
992
Simon Kelley4f7b3042012-11-28 21:27:02 +0000993 if (last_server->tcpfd == -1)
994 {
995 if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
996 continue;
997
998 if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
999 connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
1000 {
1001 close(last_server->tcpfd);
1002 last_server->tcpfd = -1;
1003 continue;
1004 }
1005
1006#ifdef HAVE_CONNTRACK
1007 /* Copy connection mark of incoming query to outgoing connection. */
1008 if (option_bool(OPT_CONNTRACK))
1009 {
1010 unsigned int mark;
1011 struct all_addr local;
1012#ifdef HAVE_IPV6
1013 if (local_addr->sa.sa_family == AF_INET6)
1014 local.addr.addr6 = local_addr->in6.sin6_addr;
1015 else
1016#endif
1017 local.addr.addr4 = local_addr->in.sin_addr;
1018
1019 if (get_incoming_mark(&peer_addr, &local, 1, &mark))
1020 setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
1021 }
1022#endif
1023 }
1024
Simon Kelley4b5ea122013-04-22 10:18:26 +01001025 *length = htons(size);
Simon Kelley4f7b3042012-11-28 21:27:02 +00001026
Simon Kelley4b5ea122013-04-22 10:18:26 +01001027 if (!read_write(last_server->tcpfd, packet, size + sizeof(u16), 0) ||
Simon Kelley4f7b3042012-11-28 21:27:02 +00001028 !read_write(last_server->tcpfd, &c1, 1, 1) ||
1029 !read_write(last_server->tcpfd, &c2, 1, 1))
Simon Kelley7de060b2011-08-26 17:24:52 +01001030 {
1031 close(last_server->tcpfd);
1032 last_server->tcpfd = -1;
1033 continue;
Simon Kelley4f7b3042012-11-28 21:27:02 +00001034 }
1035
1036 m = (c1 << 8) | c2;
Simon Kelley4b5ea122013-04-22 10:18:26 +01001037 if (!read_write(last_server->tcpfd, payload, m, 1))
Simon Kelley4f7b3042012-11-28 21:27:02 +00001038 return packet;
1039
1040 if (!gotname)
1041 strcpy(daemon->namebuff, "query");
1042 if (last_server->addr.sa.sa_family == AF_INET)
1043 log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
1044 (struct all_addr *)&last_server->addr.in.sin_addr, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001045#ifdef HAVE_IPV6
Simon Kelley4f7b3042012-11-28 21:27:02 +00001046 else
1047 log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
1048 (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001049#endif
Simon Kelley4f7b3042012-11-28 21:27:02 +00001050
1051 /* There's no point in updating the cache, since this process will exit and
1052 lose the information after a few queries. We make this call for the alias and
1053 bogus-nxdomain side-effects. */
1054 /* If the crc of the question section doesn't match the crc we sent, then
1055 someone might be attempting to insert bogus values into the cache by
1056 sending replies containing questions and bogus answers. */
1057 if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
1058 m = process_reply(header, now, last_server, (unsigned int)m,
1059 option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
1060
1061 break;
1062 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001063 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00001064
1065 /* In case of local answer or no connections made. */
1066 if (m == 0)
1067 m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001068 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001069 }
Simon Kelley4f7b3042012-11-28 21:27:02 +00001070
Simon Kelley5aabfc72007-08-29 11:24:47 +01001071 check_log_writer(NULL);
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001072
Simon Kelley4b5ea122013-04-22 10:18:26 +01001073 *length = htons(m);
1074
1075 if (m == 0 || !read_write(confd, packet, m + sizeof(u16), 0))
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001076 return packet;
1077 }
1078}
1079
Simon Kelley16972692006-10-16 20:04:18 +01001080static struct frec *allocate_frec(time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001081{
Simon Kelley16972692006-10-16 20:04:18 +01001082 struct frec *f;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001083
Simon Kelley5aabfc72007-08-29 11:24:47 +01001084 if ((f = (struct frec *)whine_malloc(sizeof(struct frec))))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001085 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001086 f->next = daemon->frec_list;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001087 f->time = now;
Simon Kelley832af0b2007-01-21 20:01:28 +00001088 f->sentto = NULL;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001089 f->rfd4 = NULL;
Simon Kelley28866e92011-02-14 20:19:14 +00001090 f->flags = 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001091#ifdef HAVE_IPV6
1092 f->rfd6 = NULL;
1093#endif
1094 daemon->frec_list = f;
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001095 }
Simon Kelley16972692006-10-16 20:04:18 +01001096
1097 return f;
1098}
1099
Simon Kelley1a6bca82008-07-11 11:11:42 +01001100static struct randfd *allocate_rfd(int family)
1101{
1102 static int finger = 0;
1103 int i;
1104
1105 /* limit the number of sockets we have open to avoid starvation of
1106 (eg) TFTP. Once we have a reasonable number, randomness should be OK */
1107
1108 for (i = 0; i < RANDOM_SOCKS; i++)
Simon Kelley9009d742008-11-14 20:04:27 +00001109 if (daemon->randomsocks[i].refcount == 0)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001110 {
Simon Kelley9009d742008-11-14 20:04:27 +00001111 if ((daemon->randomsocks[i].fd = random_sock(family)) == -1)
1112 break;
1113
Simon Kelley1a6bca82008-07-11 11:11:42 +01001114 daemon->randomsocks[i].refcount = 1;
1115 daemon->randomsocks[i].family = family;
1116 return &daemon->randomsocks[i];
1117 }
1118
Simon Kelley9009d742008-11-14 20:04:27 +00001119 /* No free ones or cannot get new socket, grab an existing one */
Simon Kelley1a6bca82008-07-11 11:11:42 +01001120 for (i = 0; i < RANDOM_SOCKS; i++)
1121 {
1122 int j = (i+finger) % RANDOM_SOCKS;
Simon Kelley9009d742008-11-14 20:04:27 +00001123 if (daemon->randomsocks[j].refcount != 0 &&
1124 daemon->randomsocks[j].family == family &&
1125 daemon->randomsocks[j].refcount != 0xffff)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001126 {
1127 finger = j;
1128 daemon->randomsocks[j].refcount++;
1129 return &daemon->randomsocks[j];
1130 }
1131 }
1132
1133 return NULL; /* doom */
1134}
1135
1136static void free_frec(struct frec *f)
1137{
1138 if (f->rfd4 && --(f->rfd4->refcount) == 0)
1139 close(f->rfd4->fd);
1140
1141 f->rfd4 = NULL;
1142 f->sentto = NULL;
Simon Kelley28866e92011-02-14 20:19:14 +00001143 f->flags = 0;
Simon Kelley1a6bca82008-07-11 11:11:42 +01001144
1145#ifdef HAVE_IPV6
1146 if (f->rfd6 && --(f->rfd6->refcount) == 0)
1147 close(f->rfd6->fd);
1148
1149 f->rfd6 = NULL;
1150#endif
1151}
1152
Simon Kelley16972692006-10-16 20:04:18 +01001153/* if wait==NULL return a free or older than TIMEOUT record.
1154 else return *wait zero if one available, or *wait is delay to
Simon Kelley1a6bca82008-07-11 11:11:42 +01001155 when the oldest in-use record will expire. Impose an absolute
1156 limit of 4*TIMEOUT before we wipe things (for random sockets) */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001157struct frec *get_new_frec(time_t now, int *wait)
Simon Kelley16972692006-10-16 20:04:18 +01001158{
Simon Kelley1a6bca82008-07-11 11:11:42 +01001159 struct frec *f, *oldest, *target;
Simon Kelley16972692006-10-16 20:04:18 +01001160 int count;
1161
1162 if (wait)
1163 *wait = 0;
1164
Simon Kelley1a6bca82008-07-11 11:11:42 +01001165 for (f = daemon->frec_list, oldest = NULL, target = NULL, count = 0; f; f = f->next, count++)
Simon Kelley832af0b2007-01-21 20:01:28 +00001166 if (!f->sentto)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001167 target = f;
1168 else
Simon Kelley16972692006-10-16 20:04:18 +01001169 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001170 if (difftime(now, f->time) >= 4*TIMEOUT)
1171 {
1172 free_frec(f);
1173 target = f;
1174 }
1175
1176 if (!oldest || difftime(f->time, oldest->time) <= 0)
1177 oldest = f;
Simon Kelley16972692006-10-16 20:04:18 +01001178 }
Simon Kelley1a6bca82008-07-11 11:11:42 +01001179
1180 if (target)
1181 {
1182 target->time = now;
1183 return target;
1184 }
Simon Kelley16972692006-10-16 20:04:18 +01001185
1186 /* can't find empty one, use oldest if there is one
1187 and it's older than timeout */
1188 if (oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
1189 {
1190 /* keep stuff for twice timeout if we can by allocating a new
1191 record instead */
1192 if (difftime(now, oldest->time) < 2*TIMEOUT &&
1193 count <= daemon->ftabsize &&
1194 (f = allocate_frec(now)))
1195 return f;
1196
1197 if (!wait)
1198 {
Simon Kelley1a6bca82008-07-11 11:11:42 +01001199 free_frec(oldest);
Simon Kelley16972692006-10-16 20:04:18 +01001200 oldest->time = now;
1201 }
1202 return oldest;
1203 }
1204
1205 /* none available, calculate time 'till oldest record expires */
1206 if (count > daemon->ftabsize)
1207 {
1208 if (oldest && wait)
1209 *wait = oldest->time + (time_t)TIMEOUT - now;
1210 return NULL;
1211 }
1212
1213 if (!(f = allocate_frec(now)) && wait)
1214 /* wait one second on malloc failure */
1215 *wait = 1;
1216
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001217 return f; /* OK if malloc fails and this is NULL */
1218}
1219
Simon Kelley832af0b2007-01-21 20:01:28 +00001220/* crc is all-ones if not known. */
1221static struct frec *lookup_frec(unsigned short id, unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001222{
1223 struct frec *f;
1224
Simon Kelley1a6bca82008-07-11 11:11:42 +01001225 for(f = daemon->frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +00001226 if (f->sentto && f->new_id == id &&
1227 (f->crc == crc || crc == 0xffffffff))
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001228 return f;
1229
1230 return NULL;
1231}
1232
1233static struct frec *lookup_frec_by_sender(unsigned short id,
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001234 union mysockaddr *addr,
1235 unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001236{
Simon Kelleyfeba5c12004-07-27 20:28:58 +01001237 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 &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001241 f->orig_id == id &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +01001242 f->crc == crc &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001243 sockaddr_isequal(&f->source, addr))
1244 return f;
1245
1246 return NULL;
1247}
1248
Simon Kelley849a8352006-06-09 21:02:31 +01001249/* A server record is going away, remove references to it */
Simon Kelley5aabfc72007-08-29 11:24:47 +01001250void server_gone(struct server *server)
Simon Kelley849a8352006-06-09 21:02:31 +01001251{
1252 struct frec *f;
1253
Simon Kelley1a6bca82008-07-11 11:11:42 +01001254 for (f = daemon->frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +00001255 if (f->sentto && f->sentto == server)
Simon Kelley1a6bca82008-07-11 11:11:42 +01001256 free_frec(f);
Simon Kelley849a8352006-06-09 21:02:31 +01001257
1258 if (daemon->last_server == server)
1259 daemon->last_server = NULL;
1260
1261 if (daemon->srv_save == server)
1262 daemon->srv_save = NULL;
1263}
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001264
Simon Kelley316e2732010-01-22 20:16:09 +00001265/* return unique random ids. */
1266static unsigned short get_id(unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001267{
1268 unsigned short ret = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +00001269
Simon Kelley316e2732010-01-22 20:16:09 +00001270 do
Simon Kelley832af0b2007-01-21 20:01:28 +00001271 ret = rand16();
1272 while (lookup_frec(ret, crc));
1273
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001274 return ret;
1275}
1276
1277
1278
1279
1280