blob: a7c0acb6b3096cc452e595b52e95ba84b403f448 [file] [log] [blame]
Simon Kelley9e4abcb2004-01-22 19:47:41 +00001/* dnsmasq is Copyright (c) 2000 - 2003 Simon Kelley
2
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
13/* Author's email: simon@thekelleys.org.uk */
14
15#include "dnsmasq.h"
16
17static struct frec *frec_list;
18
19static struct frec *get_new_frec(time_t now);
20static struct frec *lookup_frec(unsigned short id);
21static struct frec *lookup_frec_by_sender(unsigned short id,
22 union mysockaddr *addr);
23static unsigned short get_id(void);
24
25/* May be called more than once. */
26void forward_init(int first)
27{
28 struct frec *f;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010029
Simon Kelley9e4abcb2004-01-22 19:47:41 +000030 if (first)
31 frec_list = NULL;
32 for (f = frec_list; f; f = f->next)
33 f->new_id = 0;
34}
35
Simon Kelley44a2a312004-03-10 20:04:35 +000036/* Send a UDP packet with it's source address set as "source"
37 unless nowild is true, when we just send it with the kernel default */
38static void send_from(int fd, int nowild, char *packet, int len,
Simon Kelleydfa666f2004-08-02 18:27:27 +010039 union mysockaddr *to, struct all_addr *source,
40 unsigned int iface)
Simon Kelley9e4abcb2004-01-22 19:47:41 +000041{
Simon Kelley44a2a312004-03-10 20:04:35 +000042 struct msghdr msg;
43 struct iovec iov[1];
Simon Kelley44a2a312004-03-10 20:04:35 +000044 union {
45 struct cmsghdr align; /* this ensures alignment */
46#if defined(IP_PKTINFO)
47 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
48#elif defined(IP_SENDSRCADDR)
49 char control[CMSG_SPACE(sizeof(struct in_addr))];
50#endif
51#ifdef HAVE_IPV6
52 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
53#endif
54 } control_u;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010055
Simon Kelley44a2a312004-03-10 20:04:35 +000056 iov[0].iov_base = packet;
57 iov[0].iov_len = len;
58
Simon Kelleyfeba5c12004-07-27 20:28:58 +010059 msg.msg_control = NULL;
60 msg.msg_controllen = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +000061 msg.msg_flags = 0;
62 msg.msg_name = to;
63 msg.msg_namelen = sa_len(to);
64 msg.msg_iov = iov;
65 msg.msg_iovlen = 1;
Simon Kelleyfeba5c12004-07-27 20:28:58 +010066
67 if (!nowild && to->sa.sa_family == AF_INET)
68 {
69 msg.msg_control = &control_u;
70 msg.msg_controllen = sizeof(control_u);
71 {
72 struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
Simon Kelley44a2a312004-03-10 20:04:35 +000073#if defined(IP_PKTINFO)
Simon Kelleyfeba5c12004-07-27 20:28:58 +010074 struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
75 pkt->ipi_ifindex = 0;
76 pkt->ipi_spec_dst = source->addr.addr4;
77 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
78 cmptr->cmsg_level = SOL_IP;
79 cmptr->cmsg_type = IP_PKTINFO;
Simon Kelley44a2a312004-03-10 20:04:35 +000080#elif defined(IP_SENDSRCADDR)
Simon Kelleyfeba5c12004-07-27 20:28:58 +010081 struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
82 *a = source->addr.addr4;
83 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
84 cmptr->cmsg_level = IPPROTO_IP;
85 cmptr->cmsg_type = IP_SENDSRCADDR;
Simon Kelley44a2a312004-03-10 20:04:35 +000086#endif
Simon Kelleyfeba5c12004-07-27 20:28:58 +010087 }
88 }
Simon Kelley44a2a312004-03-10 20:04:35 +000089
90#ifdef HAVE_IPV6
Simon Kelleyfeba5c12004-07-27 20:28:58 +010091 if (to->sa.sa_family == AF_INET6)
Simon Kelley44a2a312004-03-10 20:04:35 +000092 {
Simon Kelleyfeba5c12004-07-27 20:28:58 +010093 msg.msg_control = &control_u;
94 msg.msg_controllen = sizeof(control_u);
95 {
96 struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
97 struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
Simon Kelleydfa666f2004-08-02 18:27:27 +010098 pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
Simon Kelleyfeba5c12004-07-27 20:28:58 +010099 pkt->ipi6_addr = source->addr.addr6;
100 msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
101 cmptr->cmsg_type = IPV6_PKTINFO;
102 cmptr->cmsg_level = IPV6_LEVEL;
103 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000104 }
105#endif
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100106
107 /* certain Linux kernels seem to object to setting the source address in the IPv6 stack
108 by returning EINVAL from sendmsg. In that case, try again without setting the
109 source address, since it will nearly alway be correct anyway. IPv6 stinks. */
110 if (sendmsg(fd, &msg, 0) == -1 && errno == EINVAL)
111 {
112 msg.msg_controllen = 0;
113 sendmsg(fd, &msg, 0);
114 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000115}
116
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100117unsigned short search_servers(struct server *servers, unsigned int options, struct all_addr **addrpp,
118 unsigned short qtype, char *qdomain, int *type, char **domain)
119
120{
121 /* If the query ends in the domain in one of our servers, set
122 domain to point to that name. We find the largest match to allow both
123 domain.org and sub.domain.org to exist. */
124
125 unsigned int namelen = strlen(qdomain);
126 unsigned int matchlen = 0;
127 struct server *serv;
128 unsigned short flags = 0;
129
130 for (serv=servers; serv; serv=serv->next)
131 /* domain matches take priority over NODOTS matches */
132 if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.'))
133 {
134 unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
135 *type = SERV_FOR_NODOTS;
136 flags = 0;
137 if (serv->flags & SERV_NO_ADDR)
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100138 flags = F_NXDOMAIN;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100139 else if ((serv->flags & SERV_LITERAL_ADDRESS) && (sflag & qtype))
140 {
141 flags = sflag;
142 if (serv->addr.sa.sa_family == AF_INET)
143 *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
144#ifdef HAVE_IPV6
145 else
146 *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
147#endif
148 }
149 }
150 else if (serv->flags & SERV_HAS_DOMAIN)
151 {
152 unsigned int domainlen = strlen(serv->domain);
153 if (namelen >= domainlen &&
154 hostname_isequal(qdomain + namelen - domainlen, serv->domain) &&
155 domainlen >= matchlen)
156 {
157 unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
158 *type = SERV_HAS_DOMAIN;
159 *domain = serv->domain;
160 matchlen = domainlen;
161 flags = 0;
162 if (serv->flags & SERV_NO_ADDR)
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100163 flags = F_NXDOMAIN;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100164 else if ((serv->flags & SERV_LITERAL_ADDRESS) && ((sflag | F_QUERY ) & qtype))
165 {
166 flags = qtype;
167 if (serv->addr.sa.sa_family == AF_INET)
168 *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
169#ifdef HAVE_IPV6
170 else
171 *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
172#endif
173 }
174 }
175 }
176
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100177 if (flags & ~F_NXDOMAIN) /* flags set here means a literal found */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100178 {
179 if (flags & F_QUERY)
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100180 log_query(F_CONFIG | F_FORWARD | F_NEG, qdomain, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100181 else
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100182 log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100183 }
184 else if (qtype && (options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.'))
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100185 flags = F_NOERR;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100186
187 if (flags & (F_NOERR | F_NXDOMAIN))
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100188 log_query(F_CONFIG | F_FORWARD | F_NEG | qtype | (flags & F_NXDOMAIN), qdomain, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100189
190 return flags;
191}
Simon Kelley44a2a312004-03-10 20:04:35 +0000192
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000193/* returns new last_server */
Simon Kelley44a2a312004-03-10 20:04:35 +0000194static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
Simon Kelleydfa666f2004-08-02 18:27:27 +0100195 struct all_addr *dst_addr, unsigned int dst_iface,
196 HEADER *header, int plen, unsigned int options, char *dnamebuff,
Simon Kelley44a2a312004-03-10 20:04:35 +0000197 struct server *servers, struct server *last_server,
198 time_t now, unsigned long local_ttl)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000199{
200 struct frec *forward;
201 char *domain = NULL;
Simon Kelleyde379512004-06-22 20:23:33 +0100202 int forwardall = 0, type = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000203 struct all_addr *addrp = NULL;
204 unsigned short flags = 0;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100205 unsigned short gotname = extract_request(header, (unsigned int)plen, dnamebuff, NULL);
Simon Kelleyde379512004-06-22 20:23:33 +0100206 struct server *start = NULL;
207
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000208 /* may be recursion not speced or no servers available. */
209 if (!header->rd || !servers)
210 forward = NULL;
211 else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr)))
212 {
Simon Kelleyde379512004-06-22 20:23:33 +0100213 /* retry on existing query, send to all available servers */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000214 domain = forward->sentto->domain;
Simon Kelleyde379512004-06-22 20:23:33 +0100215 if (!(options & OPT_ORDER))
216 {
217 forwardall = 1;
218 last_server = NULL;
219 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000220 type = forward->sentto->flags & SERV_TYPE;
Simon Kelleyde379512004-06-22 20:23:33 +0100221 if (!(start = forward->sentto->next))
222 start = servers; /* at end of list, recycle */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000223 header->id = htons(forward->new_id);
224 }
225 else
226 {
227 if (gotname)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100228 flags = search_servers(servers, options, &addrp, gotname, dnamebuff, &type, &domain);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000229
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100230 if (!flags && !(forward = get_new_frec(now)))
231 /* table full - server failure. */
232 flags = F_NEG;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000233
234 if (forward)
235 {
236 /* In strict_order mode, or when using domain specific servers
237 always try servers in the order specified in resolv.conf,
238 otherwise, use the one last known to work. */
239
240 if (type != 0 || (options & OPT_ORDER))
Simon Kelleyde379512004-06-22 20:23:33 +0100241 start = servers;
242 else if (!(start = last_server))
243 {
244 start = servers;
245 forwardall = 1;
246 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100247
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000248 forward->source = *udpaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000249 forward->dest = *dst_addr;
Simon Kelleydfa666f2004-08-02 18:27:27 +0100250 forward->iface = dst_iface;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000251 forward->new_id = get_id();
252 forward->fd = udpfd;
253 forward->orig_id = ntohs(header->id);
254 header->id = htons(forward->new_id);
255 }
256 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100257
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000258 /* check for send errors here (no route to host)
259 if we fail to send to all nameservers, send back an error
260 packet straight away (helps modem users when offline) */
261
262 if (!flags && forward)
263 {
Simon Kelleyde379512004-06-22 20:23:33 +0100264 struct server *firstsentto = start;
265 int forwarded = 0;
266
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000267 while (1)
268 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000269 /* only send to servers dealing with our domain.
270 domain may be NULL, in which case server->domain
271 must be NULL also. */
272
Simon Kelleyde379512004-06-22 20:23:33 +0100273 if (type == (start->flags & SERV_TYPE) &&
274 (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000275 {
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100276 if (!(start->flags & SERV_LITERAL_ADDRESS) &&
277 sendto(start->sfd->fd, (char *)header, plen, 0,
278 &start->addr.sa,
279 sa_len(&start->addr)) != -1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000280 {
Simon Kelleyde379512004-06-22 20:23:33 +0100281 if (!gotname)
282 strcpy(dnamebuff, "query");
283 if (start->addr.sa.sa_family == AF_INET)
284 log_query(F_SERVER | F_IPV4 | F_FORWARD, dnamebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100285 (struct all_addr *)&start->addr.in.sin_addr, 0);
Simon Kelleyde379512004-06-22 20:23:33 +0100286#ifdef HAVE_IPV6
287 else
288 log_query(F_SERVER | F_IPV6 | F_FORWARD, dnamebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100289 (struct all_addr *)&start->addr.in6.sin6_addr, 0);
Simon Kelleyde379512004-06-22 20:23:33 +0100290#endif
291 forwarded = 1;
292 forward->sentto = start;
293 if (!forwardall)
294 break;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000295 }
296 }
297
Simon Kelleyde379512004-06-22 20:23:33 +0100298 if (!(start = start->next))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100299 start = servers;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000300
Simon Kelleyde379512004-06-22 20:23:33 +0100301 if (start == firstsentto)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000302 break;
303 }
304
Simon Kelleyde379512004-06-22 20:23:33 +0100305 if (forwarded)
306 return last_server;
307
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000308 /* could not send on, prepare to return */
309 header->id = htons(forward->orig_id);
310 forward->new_id = 0; /* cancel */
311 }
312
313 /* could not send on, return empty answer or address if known for whole domain */
314 plen = setup_reply(header, (unsigned int)plen, addrp, flags, local_ttl);
Simon Kelleydfa666f2004-08-02 18:27:27 +0100315 send_from(udpfd, options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface);
Simon Kelley44a2a312004-03-10 20:04:35 +0000316
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000317 return last_server;
318}
319
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100320static int process_reply(HEADER *header, time_t now, char *dnamebuff, struct bogus_addr *bogus_nxdomain,
321 struct doctor *doctors, union mysockaddr *serveraddr,
322 int n, int options, unsigned short edns_pcktsz)
323{
324 unsigned char *pheader;
325
326 /* If upstream is advertising a larger UDP packet size
327 than we allow, trim it so that we don't get overlarge
328 requests for the client. */
329
330 if ((pheader = find_pseudoheader(header, n)))
331 {
332 unsigned short udpsz;
333 unsigned char *psave = pheader;
334
335 GETSHORT(udpsz, pheader);
336 if (udpsz > edns_pcktsz)
337 PUTSHORT(edns_pcktsz, psave);
338 }
339
340 /* Complain loudly if the upstream server is non-recursive. */
341 if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0)
342 {
343 char addrbuff[ADDRSTRLEN];
344#ifdef HAVE_IPV6
345 if (serveraddr->sa.sa_family == AF_INET)
346 inet_ntop(AF_INET, &serveraddr->in.sin_addr, addrbuff, ADDRSTRLEN);
347 else if (serveraddr->sa.sa_family == AF_INET6)
348 inet_ntop(AF_INET6, &serveraddr->in6.sin6_addr, addrbuff, ADDRSTRLEN);
349#else
350 strcpy(addrbuff, inet_ntoa(serveraddr->in.sin_addr));
351#endif
352 syslog(LOG_WARNING, "nameserver %s refused to do a recursive query", addrbuff);
353 return 0;
354 }
355
356 if ((header->rcode == NOERROR || header->rcode == NXDOMAIN) && header->opcode == QUERY)
357 {
358 if (!(bogus_nxdomain &&
359 header->rcode == NOERROR &&
360 check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now)))
361 {
362 if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
363 extract_addresses(header, (unsigned int)n, dnamebuff, now, doctors);
364 else if (!(options & OPT_NO_NEG))
365 extract_neg_addrs(header, (unsigned int)n, dnamebuff, now);
366 }
367 }
368
369 return 1;
370}
371
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000372/* returns new last_server */
Simon Kelleyde379512004-06-22 20:23:33 +0100373struct server *reply_query(struct serverfd *sfd, int options, char *packet, time_t now,
374 char *dnamebuff, struct server *servers, struct server *last_server,
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100375 struct bogus_addr *bogus_nxdomain, struct doctor *doctors, unsigned short edns_pcktsz)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000376{
377 /* packet from peer server, extract data for cache, and send to
378 original requester */
379 struct frec *forward;
380 HEADER *header;
Simon Kelleyde379512004-06-22 20:23:33 +0100381 union mysockaddr serveraddr;
382 socklen_t addrlen = sizeof(serveraddr);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100383 int n = recvfrom(sfd->fd, packet, edns_pcktsz, 0, &serveraddr.sa, &addrlen);
Simon Kelleyde379512004-06-22 20:23:33 +0100384
385 /* Determine the address of the server replying so that we can mark that as good */
386 serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family;
387#ifdef HAVE_IPV6
388 if (serveraddr.sa.sa_family == AF_INET6)
389 serveraddr.in6.sin6_flowinfo = htonl(0);
390#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000391
392 header = (HEADER *)packet;
Simon Kelleyde379512004-06-22 20:23:33 +0100393 if (n >= (int)sizeof(HEADER) && header->qr && (forward = lookup_frec(ntohs(header->id))))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000394 {
Simon Kelleyde379512004-06-22 20:23:33 +0100395 /* find good server by address if possible, otherwise assume the last one we sent to */
396 if ((forward->sentto->flags & SERV_TYPE) == 0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000397 {
Simon Kelleyde379512004-06-22 20:23:33 +0100398 for (last_server = servers; last_server; last_server = last_server->next)
399 if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
400 sockaddr_isequal(&last_server->addr, &serveraddr))
401 break;
402 if (!last_server)
403 last_server = forward->sentto;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000404 }
Simon Kelleyde379512004-06-22 20:23:33 +0100405
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100406 if (!process_reply(header, now, dnamebuff, bogus_nxdomain, doctors, &serveraddr, n, options, edns_pcktsz))
407 return NULL;
408
Simon Kelleyde379512004-06-22 20:23:33 +0100409 header->id = htons(forward->orig_id);
Simon Kelleydfa666f2004-08-02 18:27:27 +0100410 send_from(forward->fd, options & OPT_NOWILD, packet, n, &forward->source, &forward->dest, forward->iface);
Simon Kelleyde379512004-06-22 20:23:33 +0100411 forward->new_id = 0; /* cancel */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000412 }
Simon Kelleyde379512004-06-22 20:23:33 +0100413
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000414 return last_server;
415}
Simon Kelley44a2a312004-03-10 20:04:35 +0000416
Simon Kelleyde379512004-06-22 20:23:33 +0100417struct server *receive_query(struct listener *listen, char *packet, struct mx_record *mxnames,
Simon Kelley44a2a312004-03-10 20:04:35 +0000418 char *mxtarget, unsigned int options, time_t now,
419 unsigned long local_ttl, char *namebuff,
420 struct iname *names, struct iname *addrs, struct iname *except,
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100421 struct server *last_server, struct server *servers, unsigned short edns_pcktsz)
Simon Kelley44a2a312004-03-10 20:04:35 +0000422{
423 HEADER *header = (HEADER *)packet;
424 union mysockaddr source_addr;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100425 unsigned short type;
Simon Kelley44a2a312004-03-10 20:04:35 +0000426 struct iname *tmp;
427 struct all_addr dst_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100428 int check_dst = !(options & OPT_NOWILD);
Simon Kelley8a911cc2004-03-16 18:35:52 +0000429 int m, n, if_index = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000430 struct iovec iov[1];
431 struct msghdr msg;
432 struct cmsghdr *cmptr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000433 union {
434 struct cmsghdr align; /* this ensures alignment */
435#ifdef HAVE_IPV6
436 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
437#endif
438#if defined(IP_PKTINFO)
439 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
440#elif defined(IP_RECVDSTADDR)
441 char control[CMSG_SPACE(sizeof(struct in_addr)) +
442 CMSG_SPACE(sizeof(struct sockaddr_dl))];
443#endif
444 } control_u;
445
446 iov[0].iov_base = packet;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100447 iov[0].iov_len = edns_pcktsz;
Simon Kelley44a2a312004-03-10 20:04:35 +0000448
449 msg.msg_control = control_u.control;
450 msg.msg_controllen = sizeof(control_u);
451 msg.msg_flags = 0;
452 msg.msg_name = &source_addr;
453 msg.msg_namelen = sizeof(source_addr);
454 msg.msg_iov = iov;
455 msg.msg_iovlen = 1;
456
Simon Kelleyde379512004-06-22 20:23:33 +0100457 if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
458 return last_server;
Simon Kelley44a2a312004-03-10 20:04:35 +0000459
460 source_addr.sa.sa_family = listen->family;
461#ifdef HAVE_IPV6
462 if (listen->family == AF_INET6)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100463 {
464 check_dst = 1;
465 source_addr.in6.sin6_flowinfo = htonl(0);
466 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000467#endif
468
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100469 if (check_dst && msg.msg_controllen < sizeof(struct cmsghdr))
Simon Kelley44a2a312004-03-10 20:04:35 +0000470 return last_server;
471
472#if defined(IP_PKTINFO)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100473 if (check_dst && listen->family == AF_INET)
Simon Kelley44a2a312004-03-10 20:04:35 +0000474 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
475 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
476 {
477 dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
Simon Kelley8a911cc2004-03-16 18:35:52 +0000478 if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
Simon Kelley44a2a312004-03-10 20:04:35 +0000479 }
480#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100481 if (check_dst && listen->family == AF_INET)
Simon Kelley44a2a312004-03-10 20:04:35 +0000482 {
483 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
484 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
Simon Kelley8a911cc2004-03-16 18:35:52 +0000485 dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
Simon Kelley44a2a312004-03-10 20:04:35 +0000486 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8a911cc2004-03-16 18:35:52 +0000487 if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
Simon Kelley44a2a312004-03-10 20:04:35 +0000488 }
489#endif
490
491#ifdef HAVE_IPV6
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100492 if (listen->family == AF_INET6)
Simon Kelley44a2a312004-03-10 20:04:35 +0000493 {
494 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
495 if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
496 {
497 dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
Simon Kelley8a911cc2004-03-16 18:35:52 +0000498 if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
Simon Kelley44a2a312004-03-10 20:04:35 +0000499 }
500 }
501#endif
502
503 if (n < (int)sizeof(HEADER) || header->qr)
504 return last_server;
505
506 /* enforce available interface configuration */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100507 if (check_dst)
Simon Kelley44a2a312004-03-10 20:04:35 +0000508 {
Simon Kelley8a911cc2004-03-16 18:35:52 +0000509 struct ifreq ifr;
510
511 if (if_index == 0)
Simon Kelley44a2a312004-03-10 20:04:35 +0000512 return last_server;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000513
Simon Kelley8a911cc2004-03-16 18:35:52 +0000514 if (except || names)
515 {
516#ifdef SIOCGIFNAME
517 ifr.ifr_ifindex = if_index;
518 if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
519 return last_server;
520#else
521 if (!if_indextoname(if_index, ifr.ifr_name))
522 return last_server;
523#endif
524 }
525
Simon Kelley44a2a312004-03-10 20:04:35 +0000526 for (tmp = except; tmp; tmp = tmp->next)
Simon Kelley8a911cc2004-03-16 18:35:52 +0000527 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000528 return last_server;
529
530 if (names || addrs)
531 {
532 for (tmp = names; tmp; tmp = tmp->next)
Simon Kelley8a911cc2004-03-16 18:35:52 +0000533 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000534 break;
535 if (!tmp)
536 for (tmp = addrs; tmp; tmp = tmp->next)
537 if (tmp->addr.sa.sa_family == listen->family)
538 {
539 if (tmp->addr.sa.sa_family == AF_INET &&
540 tmp->addr.in.sin_addr.s_addr == dst_addr.addr.addr4.s_addr)
541 break;
542#ifdef HAVE_IPV6
543 else if (tmp->addr.sa.sa_family == AF_INET6 &&
544 memcmp(&tmp->addr.in6.sin6_addr,
545 &dst_addr.addr.addr6,
546 sizeof(struct in6_addr)) == 0)
547 break;
548#endif
549 }
550 if (!tmp)
551 return last_server;
552 }
553 }
554
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100555 if (extract_request(header, (unsigned int)n, namebuff, &type))
Simon Kelley44a2a312004-03-10 20:04:35 +0000556 {
557 if (listen->family == AF_INET)
558 log_query(F_QUERY | F_IPV4 | F_FORWARD, namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100559 (struct all_addr *)&source_addr.in.sin_addr, type);
Simon Kelley44a2a312004-03-10 20:04:35 +0000560#ifdef HAVE_IPV6
561 else
562 log_query(F_QUERY | F_IPV6 | F_FORWARD, namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100563 (struct all_addr *)&source_addr.in6.sin6_addr, type);
Simon Kelley44a2a312004-03-10 20:04:35 +0000564#endif
565 }
566
567 m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n,
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100568 mxnames, mxtarget, options, now, local_ttl, namebuff, edns_pcktsz);
Simon Kelley44a2a312004-03-10 20:04:35 +0000569 if (m >= 1)
Simon Kelleydfa666f2004-08-02 18:27:27 +0100570 send_from(listen->fd, options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index);
Simon Kelley44a2a312004-03-10 20:04:35 +0000571 else
Simon Kelleydfa666f2004-08-02 18:27:27 +0100572 last_server = forward_query(listen->fd, &source_addr, &dst_addr, if_index,
Simon Kelley44a2a312004-03-10 20:04:35 +0000573 header, n, options, namebuff, servers,
574 last_server, now, local_ttl);
575 return last_server;
576}
577
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100578static int read_write(int fd, char *packet, int size, int rw)
579{
580 int n, done;
581
582 for (done = 0; done < size; done += n)
583 {
584 retry:
585 if (rw)
586 n = read(fd, &packet[done], (size_t)(size - done));
587 else
588 n = write(fd, &packet[done], (size_t)(size - done));
589
590 if (n == 0)
591 return 0;
592 else if (n == -1)
593 {
594 if (errno == EINTR)
595 goto retry;
596 else if (errno == EAGAIN)
597 {
598 struct timespec waiter;
599 waiter.tv_sec = 0;
600 waiter.tv_nsec = 10000;
601 nanosleep(&waiter, NULL);
602 goto retry;
603 }
604 else
605 return 0;
606 }
607 }
608 return 1;
609}
610
611/* The daemon forks before calling this: it should deal with one connection,
612 blocking as neccessary, and then return. Note, need to be a bit careful
613 about resources for debug mode, when the fork is suppressed: that's
614 done by the caller. */
615char *tcp_request(int confd, struct mx_record *mxnames,
616 char *mxtarget, unsigned int options, time_t now,
617 unsigned long local_ttl, char *namebuff,
618 struct server *last_server, struct server *servers,
619 struct bogus_addr *bogus_nxdomain, struct doctor *doctors,
620 unsigned short edns_pktsz)
621{
622 int size = 0, m;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100623 unsigned short qtype, gotname;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100624 unsigned char c1, c2;
625 /* Max TCP packet + slop */
626 char *packet = malloc(65536 + MAXDNAME + RRFIXEDSZ);
627 HEADER *header;
628
629 while (1)
630 {
631 if (!packet ||
632 !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
633 !(size = c1 << 8 | c2) ||
634 !read_write(confd, packet, size, 1))
635 return packet;
636
637 if (size < (int)sizeof(HEADER))
638 continue;
639
640 header = (HEADER *)packet;
641
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100642 if ((gotname = extract_request(header, (unsigned int)size, namebuff, &qtype)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100643 {
644 union mysockaddr peer_addr;
645 socklen_t peer_len = sizeof(union mysockaddr);
646
647 if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
648 {
649 if (peer_addr.sa.sa_family == AF_INET)
650 log_query(F_QUERY | F_IPV4 | F_FORWARD, namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100651 (struct all_addr *)&peer_addr.in.sin_addr, qtype);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100652#ifdef HAVE_IPV6
653 else
654 log_query(F_QUERY | F_IPV6 | F_FORWARD, namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100655 (struct all_addr *)&peer_addr.in6.sin6_addr, qtype);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100656#endif
657 }
658 }
659
660 /* m > 0 if answered from cache */
661 m = answer_request (header, ((char *) header) + 65536, (unsigned int)size,
662 mxnames, mxtarget, options, now, local_ttl, namebuff, edns_pktsz);
663
664 if (m == 0)
665 {
666 unsigned short flags = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100667 struct all_addr *addrp = NULL;
668 int type = 0;
669 char *domain = NULL;
670
671 if (gotname)
672 flags = search_servers(servers, options, &addrp, gotname, namebuff, &type, &domain);
673
674 if (type != 0 || (options & OPT_ORDER) || !last_server)
675 last_server = servers;
676
677 if (!flags && last_server)
678 {
679 struct server *firstsendto = NULL;
680
681 /* Loop round available servers until we succeed in connecting to one.
682 Note that this code subtley ensures that consecutive queries on this connection
683 which can go to the same server, do so. */
684 while (1)
685 {
686 if (!firstsendto)
687 firstsendto = last_server;
688 else
689 {
690 if (!(last_server = last_server->next))
691 last_server = servers;
692
693 if (last_server == firstsendto)
694 break;
695 }
696
697 /* server for wrong domain */
698 if (type != (last_server->flags & SERV_TYPE) ||
699 (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
700 continue;
701
702 if ((last_server->tcpfd == -1) &&
703 (last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
704 connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1)
705 {
706 close(last_server->tcpfd);
707 last_server->tcpfd = -1;
708 }
709
710 if (last_server->tcpfd == -1)
711 continue;
712
713 c1 = size >> 8;
714 c2 = size;
715
716 if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
717 !read_write(last_server->tcpfd, &c2, 1, 0) ||
718 !read_write(last_server->tcpfd, packet, size, 0) ||
719 !read_write(last_server->tcpfd, &c1, 1, 1) ||
720 !read_write(last_server->tcpfd, &c2, 1, 1))
721 {
722 close(last_server->tcpfd);
723 last_server->tcpfd = -1;
724 continue;
725 }
726
727 m = (c1 << 8) | c2;
728 if (!read_write(last_server->tcpfd, packet, m, 1))
729 return packet;
730
731 if (!gotname)
732 strcpy(namebuff, "query");
733 if (last_server->addr.sa.sa_family == AF_INET)
734 log_query(F_SERVER | F_IPV4 | F_FORWARD, namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100735 (struct all_addr *)&last_server->addr.in.sin_addr, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100736#ifdef HAVE_IPV6
737 else
738 log_query(F_SERVER | F_IPV6 | F_FORWARD, namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100739 (struct all_addr *)&last_server->addr.in6.sin6_addr, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100740#endif
741
742 /* There's no point in updating the cache, since this process will exit and
743 lose the information after one query. We make this call for the alias and
744 bogus-nxdomain side-effects. */
745 process_reply(header, now, namebuff, bogus_nxdomain, doctors,
746 &last_server->addr, m, options, edns_pktsz);
747
748 break;
749 }
750 }
751
752 /* In case of local answer or no connections made. */
753 if (m == 0)
754 m = setup_reply(header, (unsigned int)size, addrp, flags, local_ttl);
755 }
756
757 c1 = m>>8;
758 c2 = m;
759 if (!read_write(confd, &c1, 1, 0) ||
760 !read_write(confd, &c2, 1, 0) ||
761 !read_write(confd, packet, m, 0))
762 return packet;
763 }
764}
765
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000766static struct frec *get_new_frec(time_t now)
767{
768 struct frec *f = frec_list, *oldest = NULL;
769 time_t oldtime = now;
770 int count = 0;
771 static time_t warntime = 0;
772
773 while (f)
774 {
775 if (f->new_id == 0)
776 {
777 f->time = now;
778 return f;
779 }
780
781 if (difftime(f->time, oldtime) <= 0)
782 {
783 oldtime = f->time;
784 oldest = f;
785 }
786
787 count++;
788 f = f->next;
789 }
790
791 /* can't find empty one, use oldest if there is one
792 and it's older than timeout */
793 if (oldest && difftime(now, oldtime) > TIMEOUT)
794 {
795 oldest->time = now;
796 return oldest;
797 }
798
799 if (count > FTABSIZ)
800 { /* limit logging rate so syslog isn't DOSed either */
801 if (!warntime || difftime(now, warntime) > LOGRATE)
802 {
803 warntime = now;
804 syslog(LOG_WARNING, "forwarding table overflow: check for server loops.");
805 }
806 return NULL;
807 }
808
809 if ((f = (struct frec *)malloc(sizeof(struct frec))))
810 {
811 f->next = frec_list;
812 f->time = now;
813 frec_list = f;
814 }
815 return f; /* OK if malloc fails and this is NULL */
816}
817
818static struct frec *lookup_frec(unsigned short id)
819{
820 struct frec *f;
821
822 for(f = frec_list; f; f = f->next)
823 if (f->new_id == id)
824 return f;
825
826 return NULL;
827}
828
829static struct frec *lookup_frec_by_sender(unsigned short id,
830 union mysockaddr *addr)
831{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100832 struct frec *f;
833
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000834 for(f = frec_list; f; f = f->next)
835 if (f->new_id &&
836 f->orig_id == id &&
837 sockaddr_isequal(&f->source, addr))
838 return f;
839
840 return NULL;
841}
842
843
844/* return unique random ids between 1 and 65535 */
845static unsigned short get_id(void)
846{
847 unsigned short ret = 0;
848
849 while (ret == 0)
850 {
851 ret = rand16();
852
853 /* scrap ids already in use */
854 if ((ret != 0) && lookup_frec(ret))
855 ret = 0;
856 }
857
858 return ret;
859}
860
861
862
863
864