1/* dnsmasq is Copyright (c) 2000 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 *ftab;
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  int i;
29
30  if (first)
31    ftab = safe_malloc(FTABSIZ*sizeof(struct frec));
32  for (i=0; i<FTABSIZ; i++)
33    ftab[i].new_id = 0;
34}
35
36/* returns new last_server */
37struct server *forward_query(int udpfd, int peerfd, int peerfd6,
38			     union mysockaddr *udpaddr, HEADER *header,
39			     int plen, int strict_order, char *dnamebuff,
40			     struct server *servers, struct server *last_server)
41{
42  time_t now = time(NULL);
43  struct frec *forward;
44  char *domain = NULL;
45  struct server *serv;
46  int gotname = extract_request(header, (unsigned int)plen, dnamebuff);
47
48  /* may be  recursion not speced or no servers available. */
49  if (!header->rd || !servers)
50    forward = NULL;
51  else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr)))
52    {
53      /* retry on existing query, send to next server */
54      domain = forward->sentto->domain;
55      if (!(forward->sentto = forward->sentto->next))
56	forward->sentto = servers; /* at end of list, recycle */
57      header->id = htons(forward->new_id);
58    }
59  else
60    {
61      /* new query, pick nameserver and send */
62      forward = get_new_frec(now);
63
64      /* If the query ends in the domain in one of our servers, set
65	 domain to point to that name. We find the largest match to allow both
66	 domain.org and sub.domain.org to exist. */
67
68      if (gotname)
69	{
70	  unsigned int namelen = strlen(dnamebuff);
71	  unsigned int matchlen = 0;
72	  for (serv=servers; serv; serv=serv->next)
73	    if (serv->domain)
74	      {
75		unsigned int domainlen = strlen(serv->domain);
76		if (namelen >= domainlen &&
77		    strcmp(dnamebuff + namelen - domainlen, serv->domain) == 0 &&
78		    domainlen > matchlen)
79		  {
80		    domain = serv->domain;
81		    matchlen = domainlen;
82		  }
83	      }
84	}
85
86      /* In strict_order mode, or when using domain specific servers
87	 always try servers in the order specified in resolv.conf,
88	 otherwise, use the one last known to work. */
89
90      if (domain || strict_order)
91	forward->sentto = servers;
92      else
93	forward->sentto = last_server;
94
95      forward->source = *udpaddr;
96      forward->new_id = get_id();
97      forward->fd = udpfd;
98      forward->orig_id = ntohs(header->id);
99      header->id = htons(forward->new_id);
100    }
101
102  /* check for send errors here (no route to host)
103     if we fail to send to all nameservers, send back an error
104     packet straight away (helps modem users when offline)  */
105
106  if (forward)
107    {
108      struct server *firstsentto = forward->sentto;
109
110      while (1)
111	{
112	  int af = forward->sentto->addr.sa.sa_family;
113	  int fd = af == AF_INET ? peerfd : peerfd6;
114
115	  /* only send to servers dealing with our domain.
116	     domain may be NULL, in which case server->domain
117	     must be NULL also. */
118
119	  if ((!domain && !forward->sentto->domain) ||
120	      (domain && forward->sentto->domain && strcmp(domain, forward->sentto->domain) == 0))
121	    {
122	      if (sendto(fd, (char *)header, plen, 0,
123			 &forward->sentto->addr.sa,
124			 sa_len(&forward->sentto->addr)) != -1)
125		{
126		  if (af == AF_INET)
127		    log_query(F_SERVER | F_IPV4 | F_FORWARD, gotname ? dnamebuff : "query",
128			      (struct all_addr *)&forward->sentto->addr.in.sin_addr);
129#ifdef HAVE_IPV6
130		  else
131		    log_query(F_SERVER | F_IPV6 | F_FORWARD, gotname ? dnamebuff : "query",
132			      (struct all_addr *)&forward->sentto->addr.in6.sin6_addr);
133#endif
134		  /* for no-domain, dont't update last_server */
135		  return domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers);
136		}
137	    }
138
139	  if (!(forward->sentto = forward->sentto->next))
140	    forward->sentto = servers;
141
142	  /* check if we tried all without success */
143	  if (forward->sentto == firstsentto)
144	    break;
145	}
146
147      /* could not send on, prepare to return */
148      header->id = htons(forward->orig_id);
149      forward->new_id = 0; /* cancel */
150    }
151
152  /* could not send on, return empty answer */
153  header->qr = 1; /* response */
154  header->aa = 0; /* authoritive - never */
155  header->ra = 1; /* recursion if available */
156  header->tc = 0; /* not truncated */
157  header->rcode = NOERROR; /* no error */
158  header->ancount = htons(0); /* no answers */
159  header->nscount = htons(0);
160  header->arcount = htons(0);
161  sendto(udpfd, (char *)header, plen, 0, &udpaddr->sa, sa_len(udpaddr));
162
163  return last_server;
164}
165
166/* returns new last_server */
167struct server *reply_query(int fd, char *packet, char *dnamebuff, struct server *last_server)
168{
169  /* packet from peer server, extract data for cache, and send to
170     original requester */
171  struct frec *forward;
172  HEADER *header;
173  int n = recv(fd, packet, PACKETSZ, 0);
174
175  header = (HEADER *)packet;
176  if (n >= (int)sizeof(HEADER) && header->qr)
177    {
178      if ((forward = lookup_frec(ntohs(header->id))))
179	{
180	  if (header->rcode == NOERROR || header->rcode == NXDOMAIN)
181	    {
182	      if (!forward->sentto->domain)
183		last_server = forward->sentto; /* known good */
184	      if (header->opcode == QUERY)
185		extract_addresses(header, (unsigned int)n, dnamebuff);
186	    }
187
188	  header->id = htons(forward->orig_id);
189	  /* There's no point returning an upstream reply marked as truncated,
190	     since that will prod the resolver into moving to TCP - which we
191	     don't support. */
192	  header->tc = 0; /* goodbye truncate */
193	  sendto(forward->fd, packet, n, 0,
194		 &forward->source.sa, sa_len(&forward->source));
195	  forward->new_id = 0; /* cancel */
196	}
197    }
198
199  return last_server;
200}
201
202
203static struct frec *get_new_frec(time_t now)
204{
205  int i;
206  struct frec *oldest = &ftab[0];
207  time_t oldtime = now;
208
209  for(i=0; i<FTABSIZ; i++)
210    {
211      struct frec *f = &ftab[i];
212      if (f->time <= oldtime)
213	{
214	  oldtime = f->time;
215	  oldest = f;
216	}
217      if (f->new_id == 0)
218	{
219	  f->time = now;
220	  return f;
221	}
222    }
223
224  /* table full, use oldest */
225
226  oldest->time = now;
227  return oldest;
228}
229
230static struct frec *lookup_frec(unsigned short id)
231{
232  int i;
233  for(i=0; i<FTABSIZ; i++)
234    {
235      struct frec *f = &ftab[i];
236      if (f->new_id == id)
237	return f;
238    }
239  return NULL;
240}
241
242static struct frec *lookup_frec_by_sender(unsigned short id,
243					  union mysockaddr *addr)
244{
245  int i;
246  for(i=0; i<FTABSIZ; i++)
247    {
248      struct frec *f = &ftab[i];
249      if (f->new_id &&
250	  f->orig_id == id &&
251	  sockaddr_isequal(&f->source, addr))
252	return f;
253    }
254  return NULL;
255}
256
257
258/* return unique random ids between 1 and 65535 */
259static unsigned short get_id(void)
260{
261  unsigned short ret = 0;
262
263  while (ret == 0)
264    {
265      ret = rand16();
266
267      /* scrap ids already in use */
268      if ((ret != 0) && lookup_frec(ret))
269	ret = 0;
270    }
271
272  return ret;
273}
274
275
276