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