1 /*
2  * relay.c - the guts of the program.
3  *
4  * Copyright (C) 1998 Brad M. Garcia <garsh@home.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <errno.h>
29 #include <sys/time.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <time.h>
33 #include <signal.h>
34 
35 #include "query.h"
36 #include "relay.h"
37 #include "cache.h"
38 #include "common.h"
39 #include "tcp.h"
40 #include "udp.h"
41 #include "dns.h"
42 #include "domnode.h"
43 #include "sig.h"
44 
45 #ifndef EXCLUDE_MASTER
46 #include "master.h"
47 #endif
48 
49 
50 /* prepare the dns packet for a not found reply */
51 /* not used anymore
52 char *set_notfound(char *msg, const int len) {
53   if (len < 4) return NULL;
54   msg[2] |= 0x84;
55   msg[3] = 0x83;
56   return msg;
57 }
58 */
59 
60 /* prepare the dns packet for a Server Failure reply */
set_srvfail(char * msg,const int len)61 char *set_srvfail(char *msg, const int len) {
62   if (len < 4) return NULL;
63   /* FIXME: host to network should be called here */
64   /* Set flags QR and AA */
65   msg[2] |= 0x84;
66   /* Set flags RA and RCODE=3 */
67   msg[3] = 0x82;
68   return msg;
69 }
70 
71 
72 
73 /*
74  * handle_query()
75  *
76  * In:      fromaddrp - address of the sender of the query.
77  *
78  * In/Out:  msg       - the query on input, the reply on output.
79  *          len       - length of the query/reply
80  *
81  * Out:     dptr      - dptr->current contains the server to which to forward the query
82  *
83  * Returns:  -1 if the query is bogus
84  *           1  if the query should be forwarded to the srvidx server
85  *           0  if msg now contains the reply
86  *
87  * Takes a single DNS query and determines what to do with it.
88  * This is common code used for both TCP and UDP.
89  *
90  * Assumptions: There is only one request per message.
91  */
handle_query(const struct sockaddr_in * fromaddrp,char * msg,int * len,domnode_t ** dptr)92 int handle_query(const struct sockaddr_in *fromaddrp, char *msg, int *len,
93 		 domnode_t **dptr)
94 
95 {
96     int       replylen;
97     domnode_t *d;
98 
99     if (opt_debug) {
100 	char      cname_buf[256];
101 
102 	snprintf_cname(msg, *len, 12, cname_buf, sizeof(cname_buf));
103 	log_debug(3, "Received DNS query for \"%s\"", cname_buf);
104 	if (dump_dnspacket("query", msg, *len) < 0)
105 	  log_debug(3, "Format error");
106     }
107 
108 #ifndef EXCLUDE_MASTER
109     /* First, check to see if we are master server */
110     if ((replylen = master_lookup(msg, *len)) > 0) {
111 	log_debug(2, "Replying to query as master");
112 	*len = replylen;
113 	return 0;
114     } else if (replylen < 0) return -1;
115 #endif
116 
117     /* Next, see if we have the answer cached */
118     if ((replylen = cache_lookup(msg, *len)) > 0) {
119 	log_debug(3, "Replying to query with cached answer.");
120 	*len = replylen;
121 	return 0;
122     }  else if (replylen < 0) return -1;
123 
124     /* get the server list for this domain */
125     d=search_subdomnode(domain_list, &msg[12], *len);
126 
127     if (no_srvlist(d->srvlist)) {
128       /* there is no servers for this domain, reply with "Server failure" */
129 	log_debug(2, "Replying to query with \"Server failure\"");
130 	if (!set_srvfail(msg, *len)) return -1;
131 	return 0;
132     }
133 
134     if (d->roundrobin) {
135       set_current(d, next_active(d));
136     } else {
137       /* find the first active server */
138       d->current=NULL;
139       d->current=next_active(d);
140     }
141 
142     /* Send to a server until it "times out". */
143     if (d->current) {
144       time_t now = time(NULL);
145       if ((d->current->send_time != 0)
146 	  && (forward_timeout != 0)
147 	  && (reactivate_interval != 0)
148 	  && (now - d->current->send_time > forward_timeout)) {
149 	deactivate_current(d);
150       }
151     }
152 
153     if (d->current) {
154 	log_debug(3, "Forwarding the query to DNS server %s",
155 		  inet_ntoa(d->current->addr.sin_addr));
156     } else {
157       log_debug(3, "All servers deactivated. Replying with \"Server failure\"");
158       if (!set_srvfail(msg, *len)) return -1;
159       return 0;
160     }
161 
162     *dptr = d;
163     return 1;
164 }
165 
166 /* Check if any deactivated server are back online again */
167 
reactivate_servers(int interval)168 static void reactivate_servers(int interval) {
169   time_t now=time(NULL);
170   static int last_try = 0;
171   domnode_t *d = domain_list;
172   /*  srvnode_t *s;*/
173 
174   if (!last_try) last_try = now;
175   /* check for reactivate servers */
176   if ( (now - last_try < interval))
177     return;
178 
179   last_try = now;
180   do {
181     if (!no_srvlist(d->srvlist))
182       retry_srvlist(d, interval);
183   } while ((d = d->next) != domain_list);
184 }
185 /* Check if any server are timing out and should be deactivated */
deactivate_servers(int interval)186 static void deactivate_servers(int interval) {
187   time_t now=time(NULL);
188   static int last_try = 0;
189   domnode_t *d = domain_list;
190   srvnode_t *s;
191 
192   if (!last_try) last_try = now;
193   if (now - last_try < interval)
194     return;
195   last_try = now;
196 
197   do {
198     if ((s=d->srvlist))
199       while ((s=s->next) != d->srvlist) {
200         int current_disabled=0;
201 	if (s->inactive) continue;
202 	if (s->send_time
203 	    && (difftime(now, s->send_time) > forward_timeout)) {
204 	  s->inactive = now;
205 	  if (s == d->current) /* we need to update d->current */
206 	    current_disabled=1;
207 	}
208 	if (current_disabled) deactivate_current(d);
209       }
210   } while ((d = d->next) != domain_list);
211 }
212 
srv_stats(time_t interval)213 void srv_stats(time_t interval) {
214   srvnode_t *s;
215   domnode_t *d=domain_list;
216   time_t now = time(NULL);
217   static time_t last=0;
218 
219   if (last + interval > now) {
220     last = now;
221     do {
222       if ((s=d->srvlist))
223 	while ((s=s->next) != d->srvlist)
224 	  log_debug(1, "stats for %s: send count=%i",
225 		    inet_ntoa(s->addr.sin_addr), s->send_count);
226     } while ((d=d->next) != domain_list);
227   }
228 }
229 
230 
231 /* print statics about the query list and open sockets */
query_stats(time_t interval)232 void query_stats(time_t interval) {
233   time_t now = time(NULL);
234 /*  int count; */ /* UNUSED */
235   static time_t last=0;
236 	if (interval == 0) return;
237   if (last + interval < now) {
238     last = now;
239     log_msg(LOG_INFO, "Hits: %i, Misses: %i, Total: %i, Timeouts: %i",
240 						cache_hits, cache_misses, cache_hits + cache_misses,
241 						total_timeouts);
242 		if (stats_reset)
243 			cache_hits = cache_misses = total_timeouts = 0;
244   }
245 }
246 
247 
248 
249 /*
250  * run()
251  *
252  * Abstract: This function runs continuously, waiting for packets to arrive
253  *           and processing them accordingly.
254  */
run()255 void run()
256 {
257   struct timespec    tout;
258   fd_set             fdread;
259   int                retn;
260   sigset_t          orig_sigmask;
261 
262   FD_ZERO(&fdmaster);
263   FD_SET(isock,   &fdmaster);
264 #ifdef ENABLE_TCP
265   FD_SET(tcpsock, &fdmaster);
266   maxsock = (tcpsock > isock) ? tcpsock : isock;
267 #else
268   maxsock = isock;
269 #endif
270 
271   init_sig_handler(&orig_sigmask);
272 
273   while(1) {
274     query_t *q;
275     tout.tv_sec  = select_timeout;
276     tout.tv_nsec = 0;
277     fdread = fdmaster;
278 
279     /* Wait for input or timeout */
280     retn = pselect(maxsock+1, &fdread, 0, 0, &tout, &orig_sigmask);
281 
282     /* reactivate servers */
283     if (reactivate_interval != 0) {
284       reactivate_servers(reactivate_interval);
285       /* check if any server should be timed out */
286       if (forward_timeout != 0)
287 	deactivate_servers(1);
288     }
289 
290     /* Handle errors */
291     if (retn < 0) {
292 	    if (errno == EINTR) {
293 #ifndef EXCLUDE_MASTER
294     /* Reload the master database if neccessary */
295     master_reinit();
296 #endif
297 	    } else {
298       log_msg(LOG_ERR, "select returned %s", strerror(errno));
299 	    }
300       continue;
301     }
302     else if (retn != 0) {
303       for (q = &qlist; q->next != &qlist; q = q->next) {
304 	if (FD_ISSET(q->next->sock, &fdread)) {
305 	  udp_handle_reply(q);
306 	}
307       }
308 
309 #ifdef ENABLE_TCP
310       /* Check for incoming TCP requests */
311       if (FD_ISSET(tcpsock, &fdread)) tcp_handle_request();
312 #endif
313       /* Check for new DNS queries */
314       if (FD_ISSET(isock, &fdread)) {
315 	q = udp_handle_request();
316 	if (q != NULL) {
317 	}
318       }
319     } else {
320       /* idle */
321     }
322 
323     /* ok, we are done with replies and queries, lets do some
324 	   maintenance work */
325 
326     /* Expire lookups from the cache */
327     cache_expire();
328     /* Remove old unanswered queries */
329     query_timeout(20);
330 
331     /* create new query/socket for next incomming request */
332     /* this did not make the program run any faster
333     d=domain_list;
334     do {
335       if ((s=d->srvlist))
336 	while ((s=s->next) != d->srvlist)
337 	  if (s->newquery == NULL)
338 	    s->newquery = query_get_new(d, s);
339     } while ((d=d->next) != domain_list);
340     */
341 
342     /* print som query statestics */
343     query_stats(stats_interval);
344     srv_stats(10);
345   }
346 }
347