blob: d778c5224a2ae0a1705ab45f6d9bef1afc61c827 [file] [log] [blame]
Simon Kelleyf6b7dc42005-01-23 12:06:08 +00001/* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
Simon Kelley9e4abcb2004-01-22 19:47:41 +00002
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11*/
12
Simon Kelley9e4abcb2004-01-22 19:47:41 +000013#include "dnsmasq.h"
14
Simon Kelley849a8352006-06-09 21:02:31 +010015static struct frec *frec_list = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +000016
Simon Kelley832af0b2007-01-21 20:01:28 +000017static struct frec *lookup_frec(unsigned short id, unsigned int crc);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000018static struct frec *lookup_frec_by_sender(unsigned short id,
Simon Kelleyfd9fa482004-10-21 20:24:00 +010019 union mysockaddr *addr,
20 unsigned int crc);
Simon Kelley832af0b2007-01-21 20:01:28 +000021static unsigned short get_id(int force, unsigned short force_id, unsigned int crc);
Simon Kelley9e4abcb2004-01-22 19:47:41 +000022
Simon Kelley9e4abcb2004-01-22 19:47:41 +000023
Simon Kelley44a2a312004-03-10 20:04:35 +000024/* Send a UDP packet with it's source address set as "source"
25 unless nowild is true, when we just send it with the kernel default */
Simon Kelleycdeda282006-03-16 20:16:06 +000026static void send_from(int fd, int nowild, char *packet, size_t len,
Simon Kelleydfa666f2004-08-02 18:27:27 +010027 union mysockaddr *to, struct all_addr *source,
28 unsigned int iface)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000029{
Simon Kelley44a2a312004-03-10 20:04:35 +000030 struct msghdr msg;
31 struct iovec iov[1];
Simon Kelley44a2a312004-03-10 20:04:35 +000032 union {
33 struct cmsghdr align; /* this ensures alignment */
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010034#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +000035 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
36#elif defined(IP_SENDSRCADDR)
37 char control[CMSG_SPACE(sizeof(struct in_addr))];
38#endif
39#ifdef HAVE_IPV6
40 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
41#endif
42 } control_u;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010043
Simon Kelley44a2a312004-03-10 20:04:35 +000044 iov[0].iov_base = packet;
45 iov[0].iov_len = len;
46
Simon Kelleyfeba5c12004-07-27 20:28:58 +010047 msg.msg_control = NULL;
48 msg.msg_controllen = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +000049 msg.msg_flags = 0;
50 msg.msg_name = to;
51 msg.msg_namelen = sa_len(to);
52 msg.msg_iov = iov;
53 msg.msg_iovlen = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010054
Simon Kelley26128d22004-11-14 16:43:54 +000055 if (!nowild)
Simon Kelleyfeba5c12004-07-27 20:28:58 +010056 {
Simon Kelley26128d22004-11-14 16:43:54 +000057 struct cmsghdr *cmptr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010058 msg.msg_control = &control_u;
59 msg.msg_controllen = sizeof(control_u);
Simon Kelley26128d22004-11-14 16:43:54 +000060 cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley44a2a312004-03-10 20:04:35 +000061
Simon Kelley26128d22004-11-14 16:43:54 +000062 if (to->sa.sa_family == AF_INET)
63 {
Simon Kelley5e9e0ef2006-04-17 14:24:29 +010064#if defined(HAVE_LINUX_NETWORK)
Simon Kelley26128d22004-11-14 16:43:54 +000065 struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
66 pkt->ipi_ifindex = 0;
67 pkt->ipi_spec_dst = source->addr.addr4;
68 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
69 cmptr->cmsg_level = SOL_IP;
70 cmptr->cmsg_type = IP_PKTINFO;
71#elif defined(IP_SENDSRCADDR)
72 struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
73 *a = source->addr.addr4;
74 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
75 cmptr->cmsg_level = IPPROTO_IP;
76 cmptr->cmsg_type = IP_SENDSRCADDR;
Simon Kelley44a2a312004-03-10 20:04:35 +000077#endif
Simon Kelley26128d22004-11-14 16:43:54 +000078 }
Simon Kelley26128d22004-11-14 16:43:54 +000079 else
Simon Kelleyb8187c82005-11-26 21:46:27 +000080#ifdef HAVE_IPV6
Simon Kelley26128d22004-11-14 16:43:54 +000081 {
82 struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
83 pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
84 pkt->ipi6_addr = source->addr.addr6;
85 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
86 cmptr->cmsg_type = IPV6_PKTINFO;
87 cmptr->cmsg_level = IPV6_LEVEL;
88 }
Simon Kelley3d8df262005-08-29 12:19:27 +010089#else
90 iface = 0; /* eliminate warning */
Simon Kelley26128d22004-11-14 16:43:54 +000091#endif
92 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +010093
Simon Kelleyfd9fa482004-10-21 20:24:00 +010094 retry:
95 if (sendmsg(fd, &msg, 0) == -1)
Simon Kelleyfeba5c12004-07-27 20:28:58 +010096 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +010097 /* certain Linux kernels seem to object to setting the source address in the IPv6 stack
98 by returning EINVAL from sendmsg. In that case, try again without setting the
99 source address, since it will nearly alway be correct anyway. IPv6 stinks. */
100 if (errno == EINVAL && msg.msg_controllen)
101 {
102 msg.msg_controllen = 0;
103 goto retry;
104 }
105 if (retry_send())
106 goto retry;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100107 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000108}
109
Simon Kelley36717ee2004-09-20 19:20:58 +0100110static unsigned short search_servers(struct daemon *daemon, time_t now, struct all_addr **addrpp,
111 unsigned short qtype, char *qdomain, int *type, char **domain)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100112
113{
114 /* If the query ends in the domain in one of our servers, set
115 domain to point to that name. We find the largest match to allow both
116 domain.org and sub.domain.org to exist. */
117
118 unsigned int namelen = strlen(qdomain);
119 unsigned int matchlen = 0;
120 struct server *serv;
121 unsigned short flags = 0;
122
Simon Kelley3be34542004-09-11 19:12:13 +0100123 for (serv = daemon->servers; serv; serv=serv->next)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100124 /* domain matches take priority over NODOTS matches */
Simon Kelley3d8df262005-08-29 12:19:27 +0100125 if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') && namelen != 0)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100126 {
127 unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
128 *type = SERV_FOR_NODOTS;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100129 if (serv->flags & SERV_NO_ADDR)
Simon Kelley36717ee2004-09-20 19:20:58 +0100130 flags = F_NXDOMAIN;
131 else if (serv->flags & SERV_LITERAL_ADDRESS)
132 {
133 if (sflag & qtype)
134 {
135 flags = sflag;
136 if (serv->addr.sa.sa_family == AF_INET)
137 *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100138#ifdef HAVE_IPV6
Simon Kelley36717ee2004-09-20 19:20:58 +0100139 else
140 *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100141#endif
Simon Kelley36717ee2004-09-20 19:20:58 +0100142 }
143 else if (!flags)
144 flags = F_NOERR;
145 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100146 }
147 else if (serv->flags & SERV_HAS_DOMAIN)
148 {
149 unsigned int domainlen = strlen(serv->domain);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000150 char *matchstart = qdomain + namelen - domainlen;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100151 if (namelen >= domainlen &&
Simon Kelleyb8187c82005-11-26 21:46:27 +0000152 hostname_isequal(matchstart, serv->domain) &&
153 domainlen >= matchlen &&
Simon Kelleycdeda282006-03-16 20:16:06 +0000154 (domainlen == 0 || namelen == domainlen || *(serv->domain) == '.' || *(matchstart-1) == '.' ))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100155 {
156 unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
157 *type = SERV_HAS_DOMAIN;
158 *domain = serv->domain;
159 matchlen = domainlen;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100160 if (serv->flags & SERV_NO_ADDR)
Simon Kelley36717ee2004-09-20 19:20:58 +0100161 flags = F_NXDOMAIN;
162 else if (serv->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100163 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100164 if ((sflag | F_QUERY ) & qtype)
165 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000166 flags = qtype & ~F_BIGNAME;
Simon Kelley36717ee2004-09-20 19:20:58 +0100167 if (serv->addr.sa.sa_family == AF_INET)
168 *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100169#ifdef HAVE_IPV6
Simon Kelley36717ee2004-09-20 19:20:58 +0100170 else
171 *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100172#endif
Simon Kelley36717ee2004-09-20 19:20:58 +0100173 }
174 else if (!flags)
175 flags = F_NOERR;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100176 }
177 }
178 }
179
Simon Kelley36717ee2004-09-20 19:20:58 +0100180 if (flags & ~(F_NOERR | F_NXDOMAIN)) /* flags set here means a literal found */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100181 {
182 if (flags & F_QUERY)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100183 log_query(F_CONFIG | F_FORWARD | F_NEG, qdomain, NULL, 0, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100184 else
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100185 log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp, 0, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100186 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000187 else if (qtype && !(qtype & F_BIGNAME) &&
188 (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
189 /* don't forward simple names, make exception from NS queries and empty name. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100190 flags = F_NXDOMAIN;
191
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000192 if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon))
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100193 flags = F_NOERR;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100194
Simon Kelley36717ee2004-09-20 19:20:58 +0100195 if (flags == F_NXDOMAIN || flags == F_NOERR)
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100196 log_query(F_CONFIG | F_FORWARD | F_NEG | qtype | (flags & F_NXDOMAIN), qdomain, NULL, 0, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100197
198 return flags;
199}
Simon Kelley44a2a312004-03-10 20:04:35 +0000200
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000201/* returns new last_server */
Simon Kelley3be34542004-09-11 19:12:13 +0100202static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *udpaddr,
203 struct all_addr *dst_addr, unsigned int dst_iface,
Simon Kelleycdeda282006-03-16 20:16:06 +0000204 HEADER *header, size_t plen, time_t now, struct frec *forward)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000205{
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000206 char *domain = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +0000207 int type = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000208 struct all_addr *addrp = NULL;
Simon Kelleycdeda282006-03-16 20:16:06 +0000209 unsigned int crc = questions_crc(header, plen, daemon->namebuff);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000210 unsigned short flags = 0;
Simon Kelleycdeda282006-03-16 20:16:06 +0000211 unsigned short gotname = extract_request(header, plen, daemon->namebuff, NULL);
Simon Kelleyde379512004-06-22 20:23:33 +0100212 struct server *start = NULL;
Simon Kelley3d8df262005-08-29 12:19:27 +0100213
214 /* may be no servers available. */
215 if (!daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000216 forward = NULL;
Simon Kelleyb8187c82005-11-26 21:46:27 +0000217 else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000218 {
Simon Kelleyde379512004-06-22 20:23:33 +0100219 /* retry on existing query, send to all available servers */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000220 domain = forward->sentto->domain;
Simon Kelley3be34542004-09-11 19:12:13 +0100221 if (!(daemon->options & OPT_ORDER))
Simon Kelleyde379512004-06-22 20:23:33 +0100222 {
Simon Kelley0a852542005-03-23 20:28:59 +0000223 forward->forwardall = 1;
Simon Kelley3be34542004-09-11 19:12:13 +0100224 daemon->last_server = NULL;
Simon Kelleyde379512004-06-22 20:23:33 +0100225 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000226 type = forward->sentto->flags & SERV_TYPE;
Simon Kelleyde379512004-06-22 20:23:33 +0100227 if (!(start = forward->sentto->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100228 start = daemon->servers; /* at end of list, recycle */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000229 header->id = htons(forward->new_id);
230 }
231 else
232 {
233 if (gotname)
Simon Kelley36717ee2004-09-20 19:20:58 +0100234 flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000235
Simon Kelley16972692006-10-16 20:04:18 +0100236 if (!flags && !(forward = get_new_frec(daemon, now, NULL)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100237 /* table full - server failure. */
238 flags = F_NEG;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000239
240 if (forward)
241 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000242 /* force unchanging id for signed packets */
243 int is_sign;
244 find_pseudoheader(header, plen, NULL, NULL, &is_sign);
245
Simon Kelley0a852542005-03-23 20:28:59 +0000246 forward->source = *udpaddr;
247 forward->dest = *dst_addr;
248 forward->iface = dst_iface;
Simon Kelley0a852542005-03-23 20:28:59 +0000249 forward->orig_id = ntohs(header->id);
Simon Kelley832af0b2007-01-21 20:01:28 +0000250 forward->new_id = get_id(is_sign, forward->orig_id, crc);
251 forward->fd = udpfd;
Simon Kelley0a852542005-03-23 20:28:59 +0000252 forward->crc = crc;
253 forward->forwardall = 0;
254 header->id = htons(forward->new_id);
255
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000256 /* In strict_order mode, or when using domain specific servers
257 always try servers in the order specified in resolv.conf,
258 otherwise, use the one last known to work. */
259
Simon Kelley3be34542004-09-11 19:12:13 +0100260 if (type != 0 || (daemon->options & OPT_ORDER))
261 start = daemon->servers;
262 else if (!(start = daemon->last_server))
Simon Kelleyde379512004-06-22 20:23:33 +0100263 {
Simon Kelley3be34542004-09-11 19:12:13 +0100264 start = daemon->servers;
Simon Kelley0a852542005-03-23 20:28:59 +0000265 forward->forwardall = 1;
Simon Kelleyde379512004-06-22 20:23:33 +0100266 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000267 }
268 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100269
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000270 /* check for send errors here (no route to host)
271 if we fail to send to all nameservers, send back an error
272 packet straight away (helps modem users when offline) */
273
274 if (!flags && forward)
275 {
Simon Kelleyde379512004-06-22 20:23:33 +0100276 struct server *firstsentto = start;
277 int forwarded = 0;
278
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000279 while (1)
280 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000281 /* only send to servers dealing with our domain.
282 domain may be NULL, in which case server->domain
283 must be NULL also. */
284
Simon Kelleyde379512004-06-22 20:23:33 +0100285 if (type == (start->flags & SERV_TYPE) &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100286 (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
287 !(start->flags & SERV_LITERAL_ADDRESS))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000288 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100289 if (sendto(start->sfd->fd, (char *)header, plen, 0,
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100290 &start->addr.sa,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100291 sa_len(&start->addr)) == -1)
292 {
293 if (retry_send())
294 continue;
295 }
296 else
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000297 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000298 /* Keep info in case we want to re-send this packet */
299 daemon->srv_save = start;
300 daemon->packet_len = plen;
301
Simon Kelleyde379512004-06-22 20:23:33 +0100302 if (!gotname)
Simon Kelley3be34542004-09-11 19:12:13 +0100303 strcpy(daemon->namebuff, "query");
Simon Kelleyde379512004-06-22 20:23:33 +0100304 if (start->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100305 log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100306 (struct all_addr *)&start->addr.in.sin_addr, 0,
307 NULL, 0);
Simon Kelleyde379512004-06-22 20:23:33 +0100308#ifdef HAVE_IPV6
309 else
Simon Kelley3be34542004-09-11 19:12:13 +0100310 log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100311 (struct all_addr *)&start->addr.in6.sin6_addr, 0,
312 NULL, 0);
Simon Kelleyde379512004-06-22 20:23:33 +0100313#endif
314 forwarded = 1;
315 forward->sentto = start;
Simon Kelley0a852542005-03-23 20:28:59 +0000316 if (!forward->forwardall)
Simon Kelleyde379512004-06-22 20:23:33 +0100317 break;
Simon Kelley0a852542005-03-23 20:28:59 +0000318 forward->forwardall++;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000319 }
320 }
321
Simon Kelleyde379512004-06-22 20:23:33 +0100322 if (!(start = start->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100323 start = daemon->servers;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000324
Simon Kelleyde379512004-06-22 20:23:33 +0100325 if (start == firstsentto)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000326 break;
327 }
328
Simon Kelleyde379512004-06-22 20:23:33 +0100329 if (forwarded)
Simon Kelley3be34542004-09-11 19:12:13 +0100330 return;
Simon Kelleyde379512004-06-22 20:23:33 +0100331
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000332 /* could not send on, prepare to return */
333 header->id = htons(forward->orig_id);
Simon Kelley832af0b2007-01-21 20:01:28 +0000334 forward->sentto = NULL; /* cancel */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000335 }
336
337 /* could not send on, return empty answer or address if known for whole domain */
Simon Kelleyb8187c82005-11-26 21:46:27 +0000338 if (udpfd != -1)
339 {
Simon Kelleycdeda282006-03-16 20:16:06 +0000340 plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
Simon Kelleyb8187c82005-11-26 21:46:27 +0000341 send_from(udpfd, daemon->options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface);
342 }
343
Simon Kelley3be34542004-09-11 19:12:13 +0100344 return;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000345}
346
Simon Kelleycdeda282006-03-16 20:16:06 +0000347static size_t process_reply(struct daemon *daemon, HEADER *header, time_t now,
Simon Kelley832af0b2007-01-21 20:01:28 +0000348 struct server *server, size_t n)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100349{
Simon Kelley36717ee2004-09-20 19:20:58 +0100350 unsigned char *pheader, *sizep;
Simon Kelley832af0b2007-01-21 20:01:28 +0000351 int munged = 0, is_sign;
Simon Kelleycdeda282006-03-16 20:16:06 +0000352 size_t plen;
353
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100354 /* If upstream is advertising a larger UDP packet size
355 than we allow, trim it so that we don't get overlarge
Simon Kelley832af0b2007-01-21 20:01:28 +0000356 requests for the client. We can't do this for signed packets. */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100357
Simon Kelley832af0b2007-01-21 20:01:28 +0000358 if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign)) && !is_sign)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100359 {
360 unsigned short udpsz;
Simon Kelley36717ee2004-09-20 19:20:58 +0100361 unsigned char *psave = sizep;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100362
Simon Kelley36717ee2004-09-20 19:20:58 +0100363 GETSHORT(udpsz, sizep);
Simon Kelley3be34542004-09-11 19:12:13 +0100364 if (udpsz > daemon->edns_pktsz)
365 PUTSHORT(daemon->edns_pktsz, psave);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100366 }
367
Simon Kelley1b7ecd12007-02-05 14:57:57 +0000368 if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
Simon Kelley36717ee2004-09-20 19:20:58 +0100369 return n;
370
Simon Kelley0a852542005-03-23 20:28:59 +0000371 /* Complain loudly if the upstream server is non-recursive. */
372 if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0 &&
373 server && !(server->flags & SERV_WARNED_RECURSIVE))
374 {
Simon Kelley3d8df262005-08-29 12:19:27 +0100375 prettyprint_addr(&server->addr, daemon->namebuff);
Simon Kelleyf2621c72007-04-29 19:47:21 +0100376 my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
Simon Kelley0a852542005-03-23 20:28:59 +0000377 if (!(daemon->options & OPT_LOG))
378 server->flags |= SERV_WARNED_RECURSIVE;
379 }
380
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100381 if (daemon->bogus_addr && header->rcode != NXDOMAIN &&
382 check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100383 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100384 munged = 1;
385 header->rcode = NXDOMAIN;
386 header->aa = 0;
Simon Kelley36717ee2004-09-20 19:20:58 +0100387 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100388 else
Simon Kelley36717ee2004-09-20 19:20:58 +0100389 {
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100390 if (header->rcode == NXDOMAIN &&
391 extract_request(header, n, daemon->namebuff, NULL) &&
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000392 check_for_local_domain(daemon->namebuff, now, daemon))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100393 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100394 /* if we forwarded a query for a locally known name (because it was for
395 an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
396 since we know that the domain exists, even if upstream doesn't */
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100397 munged = 1;
398 header->aa = 1;
399 header->rcode = NOERROR;
Simon Kelley36717ee2004-09-20 19:20:58 +0100400 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000401
402 extract_addresses(header, n, daemon->namebuff, now, daemon);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100403 }
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100404
405 /* do this after extract_addresses. Ensure NODATA reply and remove
406 nameserver info. */
407
408 if (munged)
409 {
410 header->ancount = htons(0);
411 header->nscount = htons(0);
412 header->arcount = htons(0);
413 }
414
Simon Kelley36717ee2004-09-20 19:20:58 +0100415 /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
416 sections of the packet. Find the new length here and put back pseudoheader
417 if it was removed. */
418 return resize_packet(header, n, pheader, plen);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100419}
420
Simon Kelley3be34542004-09-11 19:12:13 +0100421/* sets new last_server */
422void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000423{
424 /* packet from peer server, extract data for cache, and send to
425 original requester */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000426 HEADER *header;
Simon Kelleyde379512004-06-22 20:23:33 +0100427 union mysockaddr serveraddr;
Simon Kelley832af0b2007-01-21 20:01:28 +0000428 struct frec *forward;
Simon Kelleyde379512004-06-22 20:23:33 +0100429 socklen_t addrlen = sizeof(serveraddr);
Simon Kelleycdeda282006-03-16 20:16:06 +0000430 ssize_t n = recvfrom(sfd->fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
431 size_t nn;
432
433 /* packet buffer overwritten */
434 daemon->srv_save = NULL;
Simon Kelley832af0b2007-01-21 20:01:28 +0000435
Simon Kelleyde379512004-06-22 20:23:33 +0100436 /* Determine the address of the server replying so that we can mark that as good */
437 serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family;
438#ifdef HAVE_IPV6
439 if (serveraddr.sa.sa_family == AF_INET6)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100440 serveraddr.in6.sin6_flowinfo = 0;
Simon Kelleyde379512004-06-22 20:23:33 +0100441#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000442
Simon Kelley3be34542004-09-11 19:12:13 +0100443 header = (HEADER *)daemon->packet;
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100444
Simon Kelley832af0b2007-01-21 20:01:28 +0000445 if (n >= (int)sizeof(HEADER) && header->qr &&
446 (forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000447 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000448 struct server *server = forward->sentto;
449
Simon Kelley6b010842007-02-12 20:32:07 +0000450 if ((header->rcode == SERVFAIL || header->rcode == REFUSED) &&
451 !(daemon->options & OPT_ORDER) &&
452 forward->forwardall == 0)
Simon Kelley832af0b2007-01-21 20:01:28 +0000453 /* for broken servers, attempt to send to another one. */
454 {
455 unsigned char *pheader;
456 size_t plen;
457 int is_sign;
458
459 /* recreate query from reply */
460 pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign);
461 if (!is_sign)
462 {
463 header->ancount = htons(0);
464 header->nscount = htons(0);
465 header->arcount = htons(0);
466 if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
467 {
468 header->qr = 0;
469 header->tc = 0;
470 forward_query(daemon, -1, NULL, NULL, 0, header, nn, now, forward);
471 return;
472 }
473 }
474 }
475
476 if ((forward->sentto->flags & SERV_TYPE) == 0)
477 {
478 if (header->rcode == SERVFAIL || header->rcode == REFUSED)
479 server = NULL;
480 else
481 {
482 struct server *last_server;
483 /* find good server by address if possible, otherwise assume the last one we sent to */
484 for (last_server = daemon->servers; last_server; last_server = last_server->next)
485 if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
486 sockaddr_isequal(&last_server->addr, &serveraddr))
487 {
488 server = last_server;
489 break;
490 }
491 }
492 daemon->last_server = server;
493 }
494
Simon Kelley0a852542005-03-23 20:28:59 +0000495 /* If the answer is an error, keep the forward record in place in case
496 we get a good reply from another server. Kill it when we've
497 had replies from all to avoid filling the forwarding table when
498 everything is broken */
499 if (forward->forwardall == 0 || --forward->forwardall == 1 ||
500 (header->rcode != REFUSED && header->rcode != SERVFAIL))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000501 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000502 if ((nn = process_reply(daemon, header, now, server, (size_t)n)))
Simon Kelleyb8187c82005-11-26 21:46:27 +0000503 {
504 header->id = htons(forward->orig_id);
505 header->ra = 1; /* recursion if available */
Simon Kelleycdeda282006-03-16 20:16:06 +0000506 send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, nn,
Simon Kelleyb8187c82005-11-26 21:46:27 +0000507 &forward->source, &forward->dest, forward->iface);
508 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000509 forward->sentto = NULL; /* cancel */
Simon Kelleyb8187c82005-11-26 21:46:27 +0000510 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000511 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000512}
Simon Kelley44a2a312004-03-10 20:04:35 +0000513
Simon Kelley3be34542004-09-11 19:12:13 +0100514void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
Simon Kelley44a2a312004-03-10 20:04:35 +0000515{
Simon Kelley3be34542004-09-11 19:12:13 +0100516 HEADER *header = (HEADER *)daemon->packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000517 union mysockaddr source_addr;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100518 unsigned short type;
Simon Kelley44a2a312004-03-10 20:04:35 +0000519 struct all_addr dst_addr;
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000520 struct in_addr netmask, dst_addr_4;
Simon Kelleycdeda282006-03-16 20:16:06 +0000521 size_t m;
522 ssize_t n;
523 int if_index = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000524 struct iovec iov[1];
525 struct msghdr msg;
526 struct cmsghdr *cmptr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000527 union {
528 struct cmsghdr align; /* this ensures alignment */
529#ifdef HAVE_IPV6
530 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
531#endif
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100532#if defined(HAVE_LINUX_NETWORK)
Simon Kelley44a2a312004-03-10 20:04:35 +0000533 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
534#elif defined(IP_RECVDSTADDR)
535 char control[CMSG_SPACE(sizeof(struct in_addr)) +
536 CMSG_SPACE(sizeof(struct sockaddr_dl))];
537#endif
538 } control_u;
539
Simon Kelleycdeda282006-03-16 20:16:06 +0000540 /* packet buffer overwritten */
541 daemon->srv_save = NULL;
542
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000543 if (listen->family == AF_INET && (daemon->options & OPT_NOWILD))
544 {
545 dst_addr_4 = listen->iface->addr.in.sin_addr;
546 netmask = listen->iface->netmask;
547 }
548 else
Simon Kelley3d8df262005-08-29 12:19:27 +0100549 {
550 dst_addr_4.s_addr = 0;
551 netmask.s_addr = 0;
552 }
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000553
Simon Kelley3be34542004-09-11 19:12:13 +0100554 iov[0].iov_base = daemon->packet;
555 iov[0].iov_len = daemon->edns_pktsz;
Simon Kelley44a2a312004-03-10 20:04:35 +0000556
557 msg.msg_control = control_u.control;
558 msg.msg_controllen = sizeof(control_u);
559 msg.msg_flags = 0;
560 msg.msg_name = &source_addr;
561 msg.msg_namelen = sizeof(source_addr);
562 msg.msg_iov = iov;
563 msg.msg_iovlen = 1;
564
Simon Kelleyde379512004-06-22 20:23:33 +0100565 if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
Simon Kelley3be34542004-09-11 19:12:13 +0100566 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000567
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100568 if (n < (int)sizeof(HEADER) ||
569 (msg.msg_flags & MSG_TRUNC) ||
570 header->qr)
Simon Kelley3be34542004-09-11 19:12:13 +0100571 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000572
Simon Kelley26128d22004-11-14 16:43:54 +0000573 source_addr.sa.sa_family = listen->family;
574#ifdef HAVE_IPV6
575 if (listen->family == AF_INET6)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100576 source_addr.in6.sin6_flowinfo = 0;
Simon Kelley26128d22004-11-14 16:43:54 +0000577#endif
578
579 if (!(daemon->options & OPT_NOWILD))
Simon Kelley44a2a312004-03-10 20:04:35 +0000580 {
Simon Kelley8a911cc2004-03-16 18:35:52 +0000581 struct ifreq ifr;
582
Simon Kelley26128d22004-11-14 16:43:54 +0000583 if (msg.msg_controllen < sizeof(struct cmsghdr))
584 return;
585
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100586#if defined(HAVE_LINUX_NETWORK)
Simon Kelley26128d22004-11-14 16:43:54 +0000587 if (listen->family == AF_INET)
588 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
589 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
590 {
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000591 dst_addr_4 = dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
Simon Kelley26128d22004-11-14 16:43:54 +0000592 if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
593 }
594#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
595 if (listen->family == AF_INET)
596 {
597 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
598 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000599 dst_addr_4 = dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
Simon Kelley26128d22004-11-14 16:43:54 +0000600 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
601 if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
602 }
603#endif
604
605#ifdef HAVE_IPV6
606 if (listen->family == AF_INET6)
607 {
608 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
609 if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
610 {
611 dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
612 if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
613 }
614 }
615#endif
616
617 /* enforce available interface configuration */
618
Simon Kelley8a911cc2004-03-16 18:35:52 +0000619 if (if_index == 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100620 return;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000621
Simon Kelley8a911cc2004-03-16 18:35:52 +0000622#ifdef SIOCGIFNAME
Simon Kelley832af0b2007-01-21 20:01:28 +0000623 ifr.ifr_ifindex = if_index;
624 if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
Simon Kelley5e9e0ef2006-04-17 14:24:29 +0100625 return;
Simon Kelley832af0b2007-01-21 20:01:28 +0000626#else
627 if (!if_indextoname(if_index, ifr.ifr_name))
628 return;
629#endif
630
631 if (!iface_check(daemon, listen->family, &dst_addr, &ifr, &if_index))
632 return;
633
634 if (listen->family == AF_INET &&
635 (daemon->options & OPT_LOCALISE) &&
636 ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
637 return;
638
639 netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000640 }
641
Simon Kelleycdeda282006-03-16 20:16:06 +0000642 if (extract_request(header, (size_t)n, daemon->namebuff, &type))
Simon Kelley44a2a312004-03-10 20:04:35 +0000643 {
644 if (listen->family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100645 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100646 (struct all_addr *)&source_addr.in.sin_addr, type, NULL, 0);
Simon Kelley44a2a312004-03-10 20:04:35 +0000647#ifdef HAVE_IPV6
648 else
Simon Kelley3be34542004-09-11 19:12:13 +0100649 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100650 (struct all_addr *)&source_addr.in6.sin6_addr, type, NULL, 0);
Simon Kelley44a2a312004-03-10 20:04:35 +0000651#endif
652 }
653
Simon Kelleycdeda282006-03-16 20:16:06 +0000654 m = answer_request (header, ((char *) header) + PACKETSZ, (size_t)n, daemon,
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000655 dst_addr_4, netmask, now);
Simon Kelley44a2a312004-03-10 20:04:35 +0000656 if (m >= 1)
Simon Kelley3be34542004-09-11 19:12:13 +0100657 send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index);
Simon Kelley44a2a312004-03-10 20:04:35 +0000658 else
Simon Kelley3be34542004-09-11 19:12:13 +0100659 forward_query(daemon, listen->fd, &source_addr, &dst_addr, if_index,
Simon Kelleycdeda282006-03-16 20:16:06 +0000660 header, (size_t)n, now, NULL);
Simon Kelley44a2a312004-03-10 20:04:35 +0000661}
662
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100663/* The daemon forks before calling this: it should deal with one connection,
664 blocking as neccessary, and then return. Note, need to be a bit careful
665 about resources for debug mode, when the fork is suppressed: that's
666 done by the caller. */
Simon Kelley3d8df262005-08-29 12:19:27 +0100667unsigned char *tcp_request(struct daemon *daemon, int confd, time_t now,
668 struct in_addr local_addr, struct in_addr netmask)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100669{
Simon Kelleycdeda282006-03-16 20:16:06 +0000670 int size = 0;
671 size_t m;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100672 unsigned short qtype, gotname;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100673 unsigned char c1, c2;
674 /* Max TCP packet + slop */
Simon Kelley3d8df262005-08-29 12:19:27 +0100675 unsigned char *packet = malloc(65536 + MAXDNAME + RRFIXEDSZ);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100676 HEADER *header;
Simon Kelley3be34542004-09-11 19:12:13 +0100677 struct server *last_server;
678
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100679 while (1)
680 {
681 if (!packet ||
682 !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
683 !(size = c1 << 8 | c2) ||
684 !read_write(confd, packet, size, 1))
685 return packet;
686
687 if (size < (int)sizeof(HEADER))
688 continue;
689
690 header = (HEADER *)packet;
691
Simon Kelley3be34542004-09-11 19:12:13 +0100692 if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100693 {
694 union mysockaddr peer_addr;
695 socklen_t peer_len = sizeof(union mysockaddr);
696
697 if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
698 {
699 if (peer_addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100700 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100701 (struct all_addr *)&peer_addr.in.sin_addr, qtype, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100702#ifdef HAVE_IPV6
703 else
Simon Kelley3be34542004-09-11 19:12:13 +0100704 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100705 (struct all_addr *)&peer_addr.in6.sin6_addr, qtype, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100706#endif
707 }
708 }
709
710 /* m > 0 if answered from cache */
Simon Kelleyf6b7dc42005-01-23 12:06:08 +0000711 m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, daemon,
712 local_addr, netmask, now);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100713
714 if (m == 0)
715 {
716 unsigned short flags = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100717 struct all_addr *addrp = NULL;
718 int type = 0;
719 char *domain = NULL;
720
721 if (gotname)
Simon Kelley36717ee2004-09-20 19:20:58 +0100722 flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100723
Simon Kelley3be34542004-09-11 19:12:13 +0100724 if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server)
725 last_server = daemon->servers;
726 else
727 last_server = daemon->last_server;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100728
729 if (!flags && last_server)
730 {
731 struct server *firstsendto = NULL;
Simon Kelley0a852542005-03-23 20:28:59 +0000732 unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
733
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100734 /* Loop round available servers until we succeed in connecting to one.
735 Note that this code subtley ensures that consecutive queries on this connection
736 which can go to the same server, do so. */
737 while (1)
738 {
739 if (!firstsendto)
740 firstsendto = last_server;
741 else
742 {
743 if (!(last_server = last_server->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100744 last_server = daemon->servers;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100745
746 if (last_server == firstsendto)
747 break;
748 }
749
750 /* server for wrong domain */
751 if (type != (last_server->flags & SERV_TYPE) ||
752 (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
753 continue;
754
755 if ((last_server->tcpfd == -1) &&
756 (last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
757 connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1)
758 {
759 close(last_server->tcpfd);
760 last_server->tcpfd = -1;
761 }
762
763 if (last_server->tcpfd == -1)
764 continue;
765
766 c1 = size >> 8;
767 c2 = size;
768
769 if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
770 !read_write(last_server->tcpfd, &c2, 1, 0) ||
771 !read_write(last_server->tcpfd, packet, size, 0) ||
772 !read_write(last_server->tcpfd, &c1, 1, 1) ||
773 !read_write(last_server->tcpfd, &c2, 1, 1))
774 {
775 close(last_server->tcpfd);
776 last_server->tcpfd = -1;
777 continue;
778 }
779
780 m = (c1 << 8) | c2;
781 if (!read_write(last_server->tcpfd, packet, m, 1))
782 return packet;
783
784 if (!gotname)
Simon Kelley3be34542004-09-11 19:12:13 +0100785 strcpy(daemon->namebuff, "query");
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100786 if (last_server->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100787 log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100788 (struct all_addr *)&last_server->addr.in.sin_addr, 0, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100789#ifdef HAVE_IPV6
790 else
Simon Kelley3be34542004-09-11 19:12:13 +0100791 log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100792 (struct all_addr *)&last_server->addr.in6.sin6_addr, 0, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100793#endif
794
795 /* There's no point in updating the cache, since this process will exit and
Simon Kelley832af0b2007-01-21 20:01:28 +0000796 lose the information after a few queries. We make this call for the alias and
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100797 bogus-nxdomain side-effects. */
Simon Kelley832af0b2007-01-21 20:01:28 +0000798 /* If the crc of the question section doesn't match the crc we sent, then
799 someone might be attempting to insert bogus values into the cache by
800 sending replies containing questions and bogus answers. */
801 if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
802 m = process_reply(daemon, header, now, last_server, (unsigned int)m);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100803
804 break;
805 }
806 }
807
808 /* In case of local answer or no connections made. */
809 if (m == 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100810 m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100811 }
812
813 c1 = m>>8;
814 c2 = m;
815 if (!read_write(confd, &c1, 1, 0) ||
816 !read_write(confd, &c2, 1, 0) ||
817 !read_write(confd, packet, m, 0))
818 return packet;
819 }
820}
821
Simon Kelley16972692006-10-16 20:04:18 +0100822static struct frec *allocate_frec(time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000823{
Simon Kelley16972692006-10-16 20:04:18 +0100824 struct frec *f;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000825
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000826 if ((f = (struct frec *)malloc(sizeof(struct frec))))
827 {
828 f->next = frec_list;
829 f->time = now;
Simon Kelley832af0b2007-01-21 20:01:28 +0000830 f->sentto = NULL;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000831 frec_list = f;
832 }
Simon Kelley16972692006-10-16 20:04:18 +0100833
834 return f;
835}
836
837/* if wait==NULL return a free or older than TIMEOUT record.
838 else return *wait zero if one available, or *wait is delay to
839 when the oldest in-use record will expire. */
840struct frec *get_new_frec(struct daemon *daemon, time_t now, int *wait)
841{
842 struct frec *f, *oldest;
843 int count;
844
845 if (wait)
846 *wait = 0;
847
848 for (f = frec_list, oldest = NULL, count = 0; f; f = f->next, count++)
Simon Kelley832af0b2007-01-21 20:01:28 +0000849 if (!f->sentto)
Simon Kelley16972692006-10-16 20:04:18 +0100850 {
851 f->time = now;
852 return f;
853 }
854 else if (!oldest || difftime(f->time, oldest->time) <= 0)
855 oldest = f;
856
857 /* can't find empty one, use oldest if there is one
858 and it's older than timeout */
859 if (oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
860 {
861 /* keep stuff for twice timeout if we can by allocating a new
862 record instead */
863 if (difftime(now, oldest->time) < 2*TIMEOUT &&
864 count <= daemon->ftabsize &&
865 (f = allocate_frec(now)))
866 return f;
867
868 if (!wait)
869 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000870 oldest->sentto = 0;
Simon Kelley16972692006-10-16 20:04:18 +0100871 oldest->time = now;
872 }
873 return oldest;
874 }
875
876 /* none available, calculate time 'till oldest record expires */
877 if (count > daemon->ftabsize)
878 {
879 if (oldest && wait)
880 *wait = oldest->time + (time_t)TIMEOUT - now;
881 return NULL;
882 }
883
884 if (!(f = allocate_frec(now)) && wait)
885 /* wait one second on malloc failure */
886 *wait = 1;
887
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000888 return f; /* OK if malloc fails and this is NULL */
889}
890
Simon Kelley832af0b2007-01-21 20:01:28 +0000891/* crc is all-ones if not known. */
892static struct frec *lookup_frec(unsigned short id, unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000893{
894 struct frec *f;
895
896 for(f = frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +0000897 if (f->sentto && f->new_id == id &&
898 (f->crc == crc || crc == 0xffffffff))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000899 return f;
900
901 return NULL;
902}
903
904static struct frec *lookup_frec_by_sender(unsigned short id,
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100905 union mysockaddr *addr,
906 unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000907{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100908 struct frec *f;
909
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000910 for(f = frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +0000911 if (f->sentto &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000912 f->orig_id == id &&
Simon Kelleyfd9fa482004-10-21 20:24:00 +0100913 f->crc == crc &&
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000914 sockaddr_isequal(&f->source, addr))
915 return f;
916
917 return NULL;
918}
919
Simon Kelley849a8352006-06-09 21:02:31 +0100920/* A server record is going away, remove references to it */
921void server_gone(struct daemon *daemon, struct server *server)
922{
923 struct frec *f;
924
925 for (f = frec_list; f; f = f->next)
Simon Kelley832af0b2007-01-21 20:01:28 +0000926 if (f->sentto && f->sentto == server)
927 f->sentto = NULL;
Simon Kelley849a8352006-06-09 21:02:31 +0100928
929 if (daemon->last_server == server)
930 daemon->last_server = NULL;
931
932 if (daemon->srv_save == server)
933 daemon->srv_save = NULL;
934}
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000935
Simon Kelley832af0b2007-01-21 20:01:28 +0000936/* return unique random ids.
937 For signed packets we can't change the ID without breaking the
938 signing, so we keep the same one. In this case force is set, and this
939 routine degenerates into killing any conflicting forward record. */
940static unsigned short get_id(int force, unsigned short force_id, unsigned int crc)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000941{
942 unsigned short ret = 0;
Simon Kelley832af0b2007-01-21 20:01:28 +0000943
944 if (force)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000945 {
Simon Kelley832af0b2007-01-21 20:01:28 +0000946 struct frec *f = lookup_frec(force_id, crc);
947 if (f)
948 f->sentto = NULL; /* free */
949 ret = force_id;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000950 }
Simon Kelley832af0b2007-01-21 20:01:28 +0000951 else do
952 ret = rand16();
953 while (lookup_frec(ret, crc));
954
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000955 return ret;
956}
957
958
959
960
961