blob: 3731bdb46622e80f5a40c2209f192ef8e0943859 [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 Kelley36717ee2004-09-20 19:20:58 +0100117static unsigned short search_servers(struct daemon *daemon, time_t now, struct all_addr **addrpp,
118 unsigned short qtype, char *qdomain, int *type, char **domain)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100119
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
Simon Kelley3be34542004-09-11 19:12:13 +0100130 for (serv = daemon->servers; serv; serv=serv->next)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100131 /* 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;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100136 if (serv->flags & SERV_NO_ADDR)
Simon Kelley36717ee2004-09-20 19:20:58 +0100137 flags = F_NXDOMAIN;
138 else if (serv->flags & SERV_LITERAL_ADDRESS)
139 {
140 if (sflag & qtype)
141 {
142 flags = sflag;
143 if (serv->addr.sa.sa_family == AF_INET)
144 *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100145#ifdef HAVE_IPV6
Simon Kelley36717ee2004-09-20 19:20:58 +0100146 else
147 *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100148#endif
Simon Kelley36717ee2004-09-20 19:20:58 +0100149 }
150 else if (!flags)
151 flags = F_NOERR;
152 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100153 }
154 else if (serv->flags & SERV_HAS_DOMAIN)
155 {
156 unsigned int domainlen = strlen(serv->domain);
157 if (namelen >= domainlen &&
158 hostname_isequal(qdomain + namelen - domainlen, serv->domain) &&
159 domainlen >= matchlen)
160 {
161 unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
162 *type = SERV_HAS_DOMAIN;
163 *domain = serv->domain;
164 matchlen = domainlen;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100165 if (serv->flags & SERV_NO_ADDR)
Simon Kelley36717ee2004-09-20 19:20:58 +0100166 flags = F_NXDOMAIN;
167 else if (serv->flags & SERV_LITERAL_ADDRESS)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100168 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100169 if ((sflag | F_QUERY ) & qtype)
170 {
171 flags = qtype;
172 if (serv->addr.sa.sa_family == AF_INET)
173 *addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100174#ifdef HAVE_IPV6
Simon Kelley36717ee2004-09-20 19:20:58 +0100175 else
176 *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100177#endif
Simon Kelley36717ee2004-09-20 19:20:58 +0100178 }
179 else if (!flags)
180 flags = F_NOERR;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100181 }
182 }
183 }
184
Simon Kelley36717ee2004-09-20 19:20:58 +0100185 if (flags & ~(F_NOERR | F_NXDOMAIN)) /* flags set here means a literal found */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100186 {
187 if (flags & F_QUERY)
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100188 log_query(F_CONFIG | F_FORWARD | F_NEG, qdomain, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100189 else
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100190 log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100191 }
Simon Kelley3be34542004-09-11 19:12:13 +0100192 else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.'))
Simon Kelley36717ee2004-09-20 19:20:58 +0100193 flags = F_NXDOMAIN;
194
195 if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon->mxnames))
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100196 flags = F_NOERR;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100197
Simon Kelley36717ee2004-09-20 19:20:58 +0100198 if (flags == F_NXDOMAIN || flags == F_NOERR)
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100199 log_query(F_CONFIG | F_FORWARD | F_NEG | qtype | (flags & F_NXDOMAIN), qdomain, NULL, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100200
201 return flags;
202}
Simon Kelley44a2a312004-03-10 20:04:35 +0000203
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000204/* returns new last_server */
Simon Kelley3be34542004-09-11 19:12:13 +0100205static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *udpaddr,
206 struct all_addr *dst_addr, unsigned int dst_iface,
207 HEADER *header, int plen, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000208{
209 struct frec *forward;
210 char *domain = NULL;
Simon Kelleyde379512004-06-22 20:23:33 +0100211 int forwardall = 0, type = 0;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000212 struct all_addr *addrp = NULL;
213 unsigned short flags = 0;
Simon Kelley3be34542004-09-11 19:12:13 +0100214 unsigned short gotname = extract_request(header, (unsigned int)plen, daemon->namebuff, NULL);
Simon Kelleyde379512004-06-22 20:23:33 +0100215 struct server *start = NULL;
216
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000217 /* may be recursion not speced or no servers available. */
Simon Kelley3be34542004-09-11 19:12:13 +0100218 if (!header->rd || !daemon->servers)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000219 forward = NULL;
220 else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr)))
221 {
Simon Kelleyde379512004-06-22 20:23:33 +0100222 /* retry on existing query, send to all available servers */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000223 domain = forward->sentto->domain;
Simon Kelley3be34542004-09-11 19:12:13 +0100224 if (!(daemon->options & OPT_ORDER))
Simon Kelleyde379512004-06-22 20:23:33 +0100225 {
226 forwardall = 1;
Simon Kelley3be34542004-09-11 19:12:13 +0100227 daemon->last_server = NULL;
Simon Kelleyde379512004-06-22 20:23:33 +0100228 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000229 type = forward->sentto->flags & SERV_TYPE;
Simon Kelleyde379512004-06-22 20:23:33 +0100230 if (!(start = forward->sentto->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100231 start = daemon->servers; /* at end of list, recycle */
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000232 header->id = htons(forward->new_id);
233 }
234 else
235 {
236 if (gotname)
Simon Kelley36717ee2004-09-20 19:20:58 +0100237 flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain);
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000238
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100239 if (!flags && !(forward = get_new_frec(now)))
240 /* table full - server failure. */
241 flags = F_NEG;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000242
243 if (forward)
244 {
245 /* In strict_order mode, or when using domain specific servers
246 always try servers in the order specified in resolv.conf,
247 otherwise, use the one last known to work. */
248
Simon Kelley3be34542004-09-11 19:12:13 +0100249 if (type != 0 || (daemon->options & OPT_ORDER))
250 start = daemon->servers;
251 else if (!(start = daemon->last_server))
Simon Kelleyde379512004-06-22 20:23:33 +0100252 {
Simon Kelley3be34542004-09-11 19:12:13 +0100253 start = daemon->servers;
Simon Kelleyde379512004-06-22 20:23:33 +0100254 forwardall = 1;
255 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100256
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000257 forward->source = *udpaddr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000258 forward->dest = *dst_addr;
Simon Kelleydfa666f2004-08-02 18:27:27 +0100259 forward->iface = dst_iface;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000260 forward->new_id = get_id();
261 forward->fd = udpfd;
262 forward->orig_id = ntohs(header->id);
263 header->id = htons(forward->new_id);
264 }
265 }
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100266
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000267 /* check for send errors here (no route to host)
268 if we fail to send to all nameservers, send back an error
269 packet straight away (helps modem users when offline) */
270
271 if (!flags && forward)
272 {
Simon Kelleyde379512004-06-22 20:23:33 +0100273 struct server *firstsentto = start;
274 int forwarded = 0;
275
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000276 while (1)
277 {
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000278 /* only send to servers dealing with our domain.
279 domain may be NULL, in which case server->domain
280 must be NULL also. */
281
Simon Kelleyde379512004-06-22 20:23:33 +0100282 if (type == (start->flags & SERV_TYPE) &&
283 (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000284 {
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100285 if (!(start->flags & SERV_LITERAL_ADDRESS) &&
286 sendto(start->sfd->fd, (char *)header, plen, 0,
287 &start->addr.sa,
288 sa_len(&start->addr)) != -1)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000289 {
Simon Kelleyde379512004-06-22 20:23:33 +0100290 if (!gotname)
Simon Kelley3be34542004-09-11 19:12:13 +0100291 strcpy(daemon->namebuff, "query");
Simon Kelleyde379512004-06-22 20:23:33 +0100292 if (start->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100293 log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100294 (struct all_addr *)&start->addr.in.sin_addr, 0);
Simon Kelleyde379512004-06-22 20:23:33 +0100295#ifdef HAVE_IPV6
296 else
Simon Kelley3be34542004-09-11 19:12:13 +0100297 log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100298 (struct all_addr *)&start->addr.in6.sin6_addr, 0);
Simon Kelleyde379512004-06-22 20:23:33 +0100299#endif
300 forwarded = 1;
301 forward->sentto = start;
302 if (!forwardall)
303 break;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000304 }
305 }
306
Simon Kelleyde379512004-06-22 20:23:33 +0100307 if (!(start = start->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100308 start = daemon->servers;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000309
Simon Kelleyde379512004-06-22 20:23:33 +0100310 if (start == firstsentto)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000311 break;
312 }
313
Simon Kelleyde379512004-06-22 20:23:33 +0100314 if (forwarded)
Simon Kelley3be34542004-09-11 19:12:13 +0100315 return;
Simon Kelleyde379512004-06-22 20:23:33 +0100316
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000317 /* could not send on, prepare to return */
318 header->id = htons(forward->orig_id);
319 forward->new_id = 0; /* cancel */
320 }
321
322 /* could not send on, return empty answer or address if known for whole domain */
Simon Kelley3be34542004-09-11 19:12:13 +0100323 plen = setup_reply(header, (unsigned int)plen, addrp, flags, daemon->local_ttl);
324 send_from(udpfd, daemon->options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface);
Simon Kelley44a2a312004-03-10 20:04:35 +0000325
Simon Kelley3be34542004-09-11 19:12:13 +0100326 return;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000327}
328
Simon Kelley3be34542004-09-11 19:12:13 +0100329static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
Simon Kelley36717ee2004-09-20 19:20:58 +0100330 union mysockaddr *serveraddr, unsigned int n)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100331{
Simon Kelley36717ee2004-09-20 19:20:58 +0100332 unsigned char *pheader, *sizep;
333 unsigned int plen;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100334
335 /* If upstream is advertising a larger UDP packet size
336 than we allow, trim it so that we don't get overlarge
337 requests for the client. */
338
Simon Kelley36717ee2004-09-20 19:20:58 +0100339 if ((pheader = find_pseudoheader(header, n, &plen, &sizep)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100340 {
341 unsigned short udpsz;
Simon Kelley36717ee2004-09-20 19:20:58 +0100342 unsigned char *psave = sizep;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100343
Simon Kelley36717ee2004-09-20 19:20:58 +0100344 GETSHORT(udpsz, sizep);
Simon Kelley3be34542004-09-11 19:12:13 +0100345 if (udpsz > daemon->edns_pktsz)
346 PUTSHORT(daemon->edns_pktsz, psave);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100347 }
348
349 /* Complain loudly if the upstream server is non-recursive. */
350 if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0)
351 {
352 char addrbuff[ADDRSTRLEN];
353#ifdef HAVE_IPV6
354 if (serveraddr->sa.sa_family == AF_INET)
355 inet_ntop(AF_INET, &serveraddr->in.sin_addr, addrbuff, ADDRSTRLEN);
356 else if (serveraddr->sa.sa_family == AF_INET6)
357 inet_ntop(AF_INET6, &serveraddr->in6.sin6_addr, addrbuff, ADDRSTRLEN);
358#else
359 strcpy(addrbuff, inet_ntoa(serveraddr->in.sin_addr));
360#endif
361 syslog(LOG_WARNING, "nameserver %s refused to do a recursive query", addrbuff);
362 return 0;
363 }
364
Simon Kelley36717ee2004-09-20 19:20:58 +0100365 if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
366 return n;
367
368 if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100369 {
Simon Kelley3be34542004-09-11 19:12:13 +0100370 if (!(daemon->bogus_addr &&
Simon Kelley36717ee2004-09-20 19:20:58 +0100371 check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now)))
372 extract_addresses(header, n, daemon->namebuff, now, daemon->doctors);
373 }
374 else
375 {
376 unsigned short flags = F_NEG;
377 int munged = 0;
378
379 if (header->rcode == NXDOMAIN)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100380 {
Simon Kelley36717ee2004-09-20 19:20:58 +0100381 /* if we forwarded a query for a locally known name (because it was for
382 an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
383 since we know that the domain exists, even if upstream doesn't */
384 if (extract_request(header, n, daemon->namebuff, NULL) &&
385 check_for_local_domain(daemon->namebuff, now, daemon->mxnames))
386 {
387 munged = 1;
388 header->rcode = NOERROR;
389 }
390 else
391 flags |= F_NXDOMAIN;
392 }
393
394 if (!(daemon->options & OPT_NO_NEG))
395 extract_neg_addrs(header, n, daemon->namebuff, now, flags);
396
397 /* do this after extract_neg_addrs. Ensure NODATA reply and remove
398 nameserver info. */
399 if (munged)
400 {
401 header->ancount = htons(0);
402 header->nscount = htons(0);
403 header->arcount = htons(0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100404 }
405 }
Simon Kelley36717ee2004-09-20 19:20:58 +0100406
407 /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
408 sections of the packet. Find the new length here and put back pseudoheader
409 if it was removed. */
410 return resize_packet(header, n, pheader, plen);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100411}
412
Simon Kelley3be34542004-09-11 19:12:13 +0100413/* sets new last_server */
414void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000415{
416 /* packet from peer server, extract data for cache, and send to
417 original requester */
418 struct frec *forward;
419 HEADER *header;
Simon Kelleyde379512004-06-22 20:23:33 +0100420 union mysockaddr serveraddr;
421 socklen_t addrlen = sizeof(serveraddr);
Simon Kelley3be34542004-09-11 19:12:13 +0100422 int n = recvfrom(sfd->fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen);
Simon Kelleyde379512004-06-22 20:23:33 +0100423
424 /* Determine the address of the server replying so that we can mark that as good */
425 serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family;
426#ifdef HAVE_IPV6
427 if (serveraddr.sa.sa_family == AF_INET6)
428 serveraddr.in6.sin6_flowinfo = htonl(0);
429#endif
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000430
Simon Kelley3be34542004-09-11 19:12:13 +0100431 header = (HEADER *)daemon->packet;
Simon Kelleyde379512004-06-22 20:23:33 +0100432 if (n >= (int)sizeof(HEADER) && header->qr && (forward = lookup_frec(ntohs(header->id))))
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000433 {
Simon Kelleyde379512004-06-22 20:23:33 +0100434 /* find good server by address if possible, otherwise assume the last one we sent to */
435 if ((forward->sentto->flags & SERV_TYPE) == 0)
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000436 {
Simon Kelley3be34542004-09-11 19:12:13 +0100437 struct server *last_server;
438 daemon->last_server = forward->sentto;
439 for (last_server = daemon->servers; last_server; last_server = last_server->next)
Simon Kelleyde379512004-06-22 20:23:33 +0100440 if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
441 sockaddr_isequal(&last_server->addr, &serveraddr))
Simon Kelley3be34542004-09-11 19:12:13 +0100442 {
443 daemon->last_server = last_server;
444 break;
445 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000446 }
Simon Kelleyde379512004-06-22 20:23:33 +0100447
Simon Kelley36717ee2004-09-20 19:20:58 +0100448 if ((n = process_reply(daemon, header, now, &serveraddr, (unsigned int)n)))
Simon Kelley3be34542004-09-11 19:12:13 +0100449 {
450 header->id = htons(forward->orig_id);
451 send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n,
452 &forward->source, &forward->dest, forward->iface);
453 forward->new_id = 0; /* cancel */
454 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000455 }
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000456}
Simon Kelley44a2a312004-03-10 20:04:35 +0000457
Simon Kelley3be34542004-09-11 19:12:13 +0100458void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
Simon Kelley44a2a312004-03-10 20:04:35 +0000459{
Simon Kelley3be34542004-09-11 19:12:13 +0100460 HEADER *header = (HEADER *)daemon->packet;
Simon Kelley44a2a312004-03-10 20:04:35 +0000461 union mysockaddr source_addr;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100462 unsigned short type;
Simon Kelley44a2a312004-03-10 20:04:35 +0000463 struct iname *tmp;
464 struct all_addr dst_addr;
Simon Kelley3be34542004-09-11 19:12:13 +0100465 int check_dst = !(daemon->options & OPT_NOWILD);
Simon Kelley8a911cc2004-03-16 18:35:52 +0000466 int m, n, if_index = 0;
Simon Kelley44a2a312004-03-10 20:04:35 +0000467 struct iovec iov[1];
468 struct msghdr msg;
469 struct cmsghdr *cmptr;
Simon Kelley44a2a312004-03-10 20:04:35 +0000470 union {
471 struct cmsghdr align; /* this ensures alignment */
472#ifdef HAVE_IPV6
473 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
474#endif
475#if defined(IP_PKTINFO)
476 char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
477#elif defined(IP_RECVDSTADDR)
478 char control[CMSG_SPACE(sizeof(struct in_addr)) +
479 CMSG_SPACE(sizeof(struct sockaddr_dl))];
480#endif
481 } control_u;
482
Simon Kelley3be34542004-09-11 19:12:13 +0100483 iov[0].iov_base = daemon->packet;
484 iov[0].iov_len = daemon->edns_pktsz;
Simon Kelley44a2a312004-03-10 20:04:35 +0000485
486 msg.msg_control = control_u.control;
487 msg.msg_controllen = sizeof(control_u);
488 msg.msg_flags = 0;
489 msg.msg_name = &source_addr;
490 msg.msg_namelen = sizeof(source_addr);
491 msg.msg_iov = iov;
492 msg.msg_iovlen = 1;
493
Simon Kelleyde379512004-06-22 20:23:33 +0100494 if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
Simon Kelley3be34542004-09-11 19:12:13 +0100495 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000496
497 source_addr.sa.sa_family = listen->family;
498#ifdef HAVE_IPV6
499 if (listen->family == AF_INET6)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100500 {
501 check_dst = 1;
502 source_addr.in6.sin6_flowinfo = htonl(0);
503 }
Simon Kelley44a2a312004-03-10 20:04:35 +0000504#endif
505
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100506 if (check_dst && msg.msg_controllen < sizeof(struct cmsghdr))
Simon Kelley3be34542004-09-11 19:12:13 +0100507 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000508
509#if defined(IP_PKTINFO)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100510 if (check_dst && listen->family == AF_INET)
Simon Kelley44a2a312004-03-10 20:04:35 +0000511 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
512 if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
513 {
514 dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
Simon Kelley8a911cc2004-03-16 18:35:52 +0000515 if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
Simon Kelley44a2a312004-03-10 20:04:35 +0000516 }
517#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100518 if (check_dst && listen->family == AF_INET)
Simon Kelley44a2a312004-03-10 20:04:35 +0000519 {
520 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
521 if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
Simon Kelley8a911cc2004-03-16 18:35:52 +0000522 dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
Simon Kelley44a2a312004-03-10 20:04:35 +0000523 else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
Simon Kelley8a911cc2004-03-16 18:35:52 +0000524 if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
Simon Kelley44a2a312004-03-10 20:04:35 +0000525 }
526#endif
527
528#ifdef HAVE_IPV6
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100529 if (listen->family == AF_INET6)
Simon Kelley44a2a312004-03-10 20:04:35 +0000530 {
531 for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
532 if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO)
533 {
534 dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
Simon Kelley8a911cc2004-03-16 18:35:52 +0000535 if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
Simon Kelley44a2a312004-03-10 20:04:35 +0000536 }
537 }
538#endif
539
540 if (n < (int)sizeof(HEADER) || header->qr)
Simon Kelley3be34542004-09-11 19:12:13 +0100541 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000542
543 /* enforce available interface configuration */
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100544 if (check_dst)
Simon Kelley44a2a312004-03-10 20:04:35 +0000545 {
Simon Kelley8a911cc2004-03-16 18:35:52 +0000546 struct ifreq ifr;
547
548 if (if_index == 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100549 return;
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000550
Simon Kelley3be34542004-09-11 19:12:13 +0100551 if (daemon->if_except || daemon->if_names)
Simon Kelley8a911cc2004-03-16 18:35:52 +0000552 {
553#ifdef SIOCGIFNAME
554 ifr.ifr_ifindex = if_index;
555 if (ioctl(listen->fd, SIOCGIFNAME, &ifr) == -1)
Simon Kelley3be34542004-09-11 19:12:13 +0100556 return;
Simon Kelley8a911cc2004-03-16 18:35:52 +0000557#else
558 if (!if_indextoname(if_index, ifr.ifr_name))
Simon Kelley3be34542004-09-11 19:12:13 +0100559 return;
Simon Kelley8a911cc2004-03-16 18:35:52 +0000560#endif
561 }
562
Simon Kelley3be34542004-09-11 19:12:13 +0100563 for (tmp = daemon->if_except; tmp; tmp = tmp->next)
Simon Kelley8a911cc2004-03-16 18:35:52 +0000564 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
Simon Kelley3be34542004-09-11 19:12:13 +0100565 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000566
Simon Kelley3be34542004-09-11 19:12:13 +0100567 if (daemon->if_names || daemon->if_addrs)
Simon Kelley44a2a312004-03-10 20:04:35 +0000568 {
Simon Kelley3be34542004-09-11 19:12:13 +0100569 for (tmp = daemon->if_names; tmp; tmp = tmp->next)
Simon Kelley8a911cc2004-03-16 18:35:52 +0000570 if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
Simon Kelley44a2a312004-03-10 20:04:35 +0000571 break;
572 if (!tmp)
Simon Kelley3be34542004-09-11 19:12:13 +0100573 for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
Simon Kelley44a2a312004-03-10 20:04:35 +0000574 if (tmp->addr.sa.sa_family == listen->family)
575 {
576 if (tmp->addr.sa.sa_family == AF_INET &&
577 tmp->addr.in.sin_addr.s_addr == dst_addr.addr.addr4.s_addr)
578 break;
579#ifdef HAVE_IPV6
580 else if (tmp->addr.sa.sa_family == AF_INET6 &&
581 memcmp(&tmp->addr.in6.sin6_addr,
582 &dst_addr.addr.addr6,
583 sizeof(struct in6_addr)) == 0)
584 break;
585#endif
586 }
587 if (!tmp)
Simon Kelley3be34542004-09-11 19:12:13 +0100588 return;
Simon Kelley44a2a312004-03-10 20:04:35 +0000589 }
590 }
591
Simon Kelley3be34542004-09-11 19:12:13 +0100592 if (extract_request(header, (unsigned int)n, daemon->namebuff, &type))
Simon Kelley44a2a312004-03-10 20:04:35 +0000593 {
594 if (listen->family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100595 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100596 (struct all_addr *)&source_addr.in.sin_addr, type);
Simon Kelley44a2a312004-03-10 20:04:35 +0000597#ifdef HAVE_IPV6
598 else
Simon Kelley3be34542004-09-11 19:12:13 +0100599 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100600 (struct all_addr *)&source_addr.in6.sin6_addr, type);
Simon Kelley44a2a312004-03-10 20:04:35 +0000601#endif
602 }
603
Simon Kelley3be34542004-09-11 19:12:13 +0100604 m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, daemon, now);
Simon Kelley44a2a312004-03-10 20:04:35 +0000605 if (m >= 1)
Simon Kelley3be34542004-09-11 19:12:13 +0100606 send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index);
Simon Kelley44a2a312004-03-10 20:04:35 +0000607 else
Simon Kelley3be34542004-09-11 19:12:13 +0100608 forward_query(daemon, listen->fd, &source_addr, &dst_addr, if_index,
609 header, n, now);
Simon Kelley44a2a312004-03-10 20:04:35 +0000610}
611
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100612static int read_write(int fd, char *packet, int size, int rw)
613{
614 int n, done;
615
616 for (done = 0; done < size; done += n)
617 {
618 retry:
619 if (rw)
620 n = read(fd, &packet[done], (size_t)(size - done));
621 else
622 n = write(fd, &packet[done], (size_t)(size - done));
623
624 if (n == 0)
625 return 0;
626 else if (n == -1)
627 {
628 if (errno == EINTR)
629 goto retry;
630 else if (errno == EAGAIN)
631 {
632 struct timespec waiter;
633 waiter.tv_sec = 0;
634 waiter.tv_nsec = 10000;
635 nanosleep(&waiter, NULL);
636 goto retry;
637 }
638 else
639 return 0;
640 }
641 }
642 return 1;
643}
644
645/* The daemon forks before calling this: it should deal with one connection,
646 blocking as neccessary, and then return. Note, need to be a bit careful
647 about resources for debug mode, when the fork is suppressed: that's
648 done by the caller. */
Simon Kelley3be34542004-09-11 19:12:13 +0100649char *tcp_request(struct daemon *daemon, int confd, time_t now)
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100650{
651 int size = 0, m;
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100652 unsigned short qtype, gotname;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100653 unsigned char c1, c2;
654 /* Max TCP packet + slop */
655 char *packet = malloc(65536 + MAXDNAME + RRFIXEDSZ);
656 HEADER *header;
Simon Kelley3be34542004-09-11 19:12:13 +0100657 struct server *last_server;
658
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100659 while (1)
660 {
661 if (!packet ||
662 !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
663 !(size = c1 << 8 | c2) ||
664 !read_write(confd, packet, size, 1))
665 return packet;
666
667 if (size < (int)sizeof(HEADER))
668 continue;
669
670 header = (HEADER *)packet;
671
Simon Kelley3be34542004-09-11 19:12:13 +0100672 if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100673 {
674 union mysockaddr peer_addr;
675 socklen_t peer_len = sizeof(union mysockaddr);
676
677 if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
678 {
679 if (peer_addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100680 log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100681 (struct all_addr *)&peer_addr.in.sin_addr, qtype);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100682#ifdef HAVE_IPV6
683 else
Simon Kelley3be34542004-09-11 19:12:13 +0100684 log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100685 (struct all_addr *)&peer_addr.in6.sin6_addr, qtype);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100686#endif
687 }
688 }
689
690 /* m > 0 if answered from cache */
Simon Kelley3be34542004-09-11 19:12:13 +0100691 m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, daemon, now);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100692
693 if (m == 0)
694 {
695 unsigned short flags = 0;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100696 struct all_addr *addrp = NULL;
697 int type = 0;
698 char *domain = NULL;
699
700 if (gotname)
Simon Kelley36717ee2004-09-20 19:20:58 +0100701 flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100702
Simon Kelley3be34542004-09-11 19:12:13 +0100703 if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server)
704 last_server = daemon->servers;
705 else
706 last_server = daemon->last_server;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100707
708 if (!flags && last_server)
709 {
710 struct server *firstsendto = NULL;
711
712 /* Loop round available servers until we succeed in connecting to one.
713 Note that this code subtley ensures that consecutive queries on this connection
714 which can go to the same server, do so. */
715 while (1)
716 {
717 if (!firstsendto)
718 firstsendto = last_server;
719 else
720 {
721 if (!(last_server = last_server->next))
Simon Kelley3be34542004-09-11 19:12:13 +0100722 last_server = daemon->servers;
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100723
724 if (last_server == firstsendto)
725 break;
726 }
727
728 /* server for wrong domain */
729 if (type != (last_server->flags & SERV_TYPE) ||
730 (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
731 continue;
732
733 if ((last_server->tcpfd == -1) &&
734 (last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
735 connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1)
736 {
737 close(last_server->tcpfd);
738 last_server->tcpfd = -1;
739 }
740
741 if (last_server->tcpfd == -1)
742 continue;
743
744 c1 = size >> 8;
745 c2 = size;
746
747 if (!read_write(last_server->tcpfd, &c1, 1, 0) ||
748 !read_write(last_server->tcpfd, &c2, 1, 0) ||
749 !read_write(last_server->tcpfd, packet, size, 0) ||
750 !read_write(last_server->tcpfd, &c1, 1, 1) ||
751 !read_write(last_server->tcpfd, &c2, 1, 1))
752 {
753 close(last_server->tcpfd);
754 last_server->tcpfd = -1;
755 continue;
756 }
757
758 m = (c1 << 8) | c2;
759 if (!read_write(last_server->tcpfd, packet, m, 1))
760 return packet;
761
762 if (!gotname)
Simon Kelley3be34542004-09-11 19:12:13 +0100763 strcpy(daemon->namebuff, "query");
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100764 if (last_server->addr.sa.sa_family == AF_INET)
Simon Kelley3be34542004-09-11 19:12:13 +0100765 log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100766 (struct all_addr *)&last_server->addr.in.sin_addr, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100767#ifdef HAVE_IPV6
768 else
Simon Kelley3be34542004-09-11 19:12:13 +0100769 log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
Simon Kelleyc1bb8502004-08-11 18:40:17 +0100770 (struct all_addr *)&last_server->addr.in6.sin6_addr, 0);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100771#endif
772
773 /* There's no point in updating the cache, since this process will exit and
774 lose the information after one query. We make this call for the alias and
775 bogus-nxdomain side-effects. */
Simon Kelley36717ee2004-09-20 19:20:58 +0100776 m = process_reply(daemon, header, now, &last_server->addr, (unsigned int)m);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100777
778 break;
779 }
780 }
781
782 /* In case of local answer or no connections made. */
783 if (m == 0)
Simon Kelley3be34542004-09-11 19:12:13 +0100784 m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100785 }
786
787 c1 = m>>8;
788 c2 = m;
789 if (!read_write(confd, &c1, 1, 0) ||
790 !read_write(confd, &c2, 1, 0) ||
791 !read_write(confd, packet, m, 0))
792 return packet;
793 }
794}
795
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000796static struct frec *get_new_frec(time_t now)
797{
798 struct frec *f = frec_list, *oldest = NULL;
799 time_t oldtime = now;
800 int count = 0;
801 static time_t warntime = 0;
802
803 while (f)
804 {
805 if (f->new_id == 0)
806 {
807 f->time = now;
808 return f;
809 }
810
811 if (difftime(f->time, oldtime) <= 0)
812 {
813 oldtime = f->time;
814 oldest = f;
815 }
816
817 count++;
818 f = f->next;
819 }
820
821 /* can't find empty one, use oldest if there is one
822 and it's older than timeout */
823 if (oldest && difftime(now, oldtime) > TIMEOUT)
824 {
825 oldest->time = now;
826 return oldest;
827 }
828
829 if (count > FTABSIZ)
830 { /* limit logging rate so syslog isn't DOSed either */
831 if (!warntime || difftime(now, warntime) > LOGRATE)
832 {
833 warntime = now;
834 syslog(LOG_WARNING, "forwarding table overflow: check for server loops.");
835 }
836 return NULL;
837 }
838
839 if ((f = (struct frec *)malloc(sizeof(struct frec))))
840 {
841 f->next = frec_list;
842 f->time = now;
843 frec_list = f;
844 }
845 return f; /* OK if malloc fails and this is NULL */
846}
847
848static struct frec *lookup_frec(unsigned short id)
849{
850 struct frec *f;
851
852 for(f = frec_list; f; f = f->next)
853 if (f->new_id == id)
854 return f;
855
856 return NULL;
857}
858
859static struct frec *lookup_frec_by_sender(unsigned short id,
860 union mysockaddr *addr)
861{
Simon Kelleyfeba5c12004-07-27 20:28:58 +0100862 struct frec *f;
863
Simon Kelley9e4abcb2004-01-22 19:47:41 +0000864 for(f = frec_list; f; f = f->next)
865 if (f->new_id &&
866 f->orig_id == id &&
867 sockaddr_isequal(&f->source, addr))
868 return f;
869
870 return NULL;
871}
872
873
874/* return unique random ids between 1 and 65535 */
875static unsigned short get_id(void)
876{
877 unsigned short ret = 0;
878
879 while (ret == 0)
880 {
881 ret = rand16();
882
883 /* scrap ids already in use */
884 if ((ret != 0) && lookup_frec(ret))
885 ret = 0;
886 }
887
888 return ret;
889}
890
891
892
893
894