1#include <stdio.h>
2#include <stdlib.h>
3#include <sys/time.h>
4#include <sys/types.h>
5#include <unistd.h>
6#include <sys/socket.h>
7#include <netinet/in.h>
8#include <string.h>
9#include <arpa/inet.h>
10#include <errno.h>
11#include <stdarg.h>
12#include <signal.h>
13#include <syslog.h>
14
15#include "dproxy.h"
16#include "dns_decode.h"
17#include "cache.h"
18#include "conf.h"
19#include "dns_list.h"
20#include "dns_construct.h"
21#include "dns_io.h"
22
23/*****************************************************************************/
24/*****************************************************************************/
25int dns_main_quit;
26int dns_sock;
27fd_set rfds;
28dns_request_t *dns_request_list;
29/*****************************************************************************/
30int is_connected()
31{
32  FILE *fp;
33
34  if(!config.ppp_detect)return 1;
35
36  fp = fopen( config.ppp_device_file, "r" );
37  if(!fp)return 0;
38  fclose(fp);
39  return 1;
40}
41/*****************************************************************************/
42int dns_init()
43{
44  struct sockaddr_in sa;
45  struct in_addr ip;
46
47  /* Clear it out */
48  memset((void *)&sa, 0, sizeof(sa));
49
50  dns_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
51
52  /* Error */
53  if( dns_sock < 0 ){
54	 debug_perror("Could not create socket");
55	 exit(1);
56  }
57
58  ip.s_addr = INADDR_ANY;
59  sa.sin_family = AF_INET;
60  memcpy((void *)&sa.sin_addr, (void *)&ip, sizeof(struct in_addr));
61  sa.sin_port = htons(PORT);
62
63  /* bind() the socket to the interface */
64  if (bind(dns_sock, (struct sockaddr *)&sa, sizeof(struct sockaddr)) < 0){
65	 debug_perror("dns_init: bind: Could not bind to port");
66	 exit(1);
67  }
68
69  dns_main_quit = 0;
70
71  FD_ZERO( &rfds );
72  FD_SET( dns_sock, &rfds );
73
74  dns_request_list = NULL;
75
76  cache_purge( config.purge_time );
77
78  return 1;
79}
80/*****************************************************************************/
81/* This function is added by CMC 8/4/2001 */
82void forward_dns_query(dns_request_t *node, dns_request_t *m)
83{
84  struct in_addr	in;
85  FILE			*fp;
86  char			line[81], dns_ser_ip[81];
87
88  inet_aton( config.name_server, &in );
89
90  if( (fp = fopen( "/tmp/resolv.conf" , "r")) != NULL) {
91	while ( fgets(line, 80, fp) != NULL ){
92		if ( sscanf(line, "nameserver %s", dns_ser_ip) == 1 ){
93			inet_aton( dns_ser_ip, &in );
94			/* the first or lastest nameserver */
95			if ( !(node->duplicate_queries & 0x01) )
96				break;
97		}
98	}
99	fclose(fp);
100  }
101  debug("forward_dns_query: query DNS server -- %s\n", inet_ntoa(in) );
102  dns_write_packet( dns_sock, in, PORT, m );
103}
104/*****************************************************************************/
105void dns_handle_new_query(dns_request_t *m)
106{
107  //struct in_addr in;
108  int retval = 0;	/* modified by CMC from retval=-1 2002/12/6 */
109
110  if( m->message.question[0].type == A || m->message.question[0].type == AAA){
111    /* added by CMC to deny name 2002/11/19 */
112    if ( deny_lookup_name( m->cname ) ) {
113      debug("%s --> blocked.\n", m->cname);
114      dns_construct_error_reply(m);
115      dns_write_packet( dns_sock, m->src_addr, m->src_port, m );
116      return;
117    }
118    /* standard query */
119    retval = cache_lookup_name( m->cname, m->ip );
120  }else if( m->message.question[0].type == PTR ){
121    /* reverse lookup */
122    retval = cache_lookup_ip( m->ip, m->cname );
123  }
124
125  debug(".......... %s ---- %s\n", m->cname, m->ip );
126
127  switch( retval )
128    {
129    case 0:
130      if( is_connected() ){
131	debug("Adding to list-> id: %d\n", m->message.header.id);
132	dns_request_list = dns_list_add( dns_request_list, m );
133	/* relay the query untouched */
134	forward_dns_query( dns_request_list, m );  /* modified by CMC 8/3/2001 */
135      }else{
136	debug("Not connected **\n");
137	dns_construct_error_reply(m);
138	dns_write_packet( dns_sock, m->src_addr, m->src_port, m );
139      }
140      break;
141    case 1:
142      dns_construct_reply( m );
143      dns_write_packet( dns_sock, m->src_addr, m->src_port, m );
144      debug("Cache hit\n");
145      break;
146    default:
147      debug("Unknown query type: %d\n", m->message.question[0].type );
148      debug("CMC: Here is un-reachable code! (2002/12/6)\n");
149    }
150
151}
152/*****************************************************************************/
153void dns_handle_request(dns_request_t *m)
154{
155  dns_request_t *ptr = NULL;
156
157  /* request may be a new query or a answer from the upstream server */
158  ptr = dns_list_find_by_id( dns_request_list, m );
159
160  if( ptr != NULL ){
161    debug("Found query in list\n");
162    /* message may be a response */
163    if( m->message.header.flags.f.question == 1 ){
164      dns_write_packet( dns_sock, ptr->src_addr, ptr->src_port, m );
165      debug("Replying with answer from %s\n", inet_ntoa( m->src_addr ));
166      if( m->message.header.flags.f.rcode == 0 && /* modified by CMC 2002/12/6 */
167          (ptr->message.question[0].type == A || ptr->message.question[0].type == PTR) ){
168	debug("Cache append: %s ----> %s\n", m->cname, m->ip );
169	cache_name_append( m->cname, m->ip );
170      }
171      dns_request_list = dns_list_remove( dns_request_list, ptr );
172    }else{
173      ptr->duplicate_queries++;	   /* added by CMC 8/4/2001 */
174      debug("Duplicate query(%d)\n", ptr->duplicate_queries);
175      forward_dns_query( ptr, m ); /* added by CMC 8/4/2001 */
176    }
177  }else{
178    dns_handle_new_query( m );
179  }
180
181}
182/*****************************************************************************/
183int dns_main_loop()
184{
185  struct timeval tv;
186  fd_set active_rfds;
187  int retval;
188  dns_request_t m;
189  dns_request_t *ptr, *next;
190  //int purge_time = config.purge_time / 60;
191  int purge_time = CACHE_CHECK_TIME / DNS_TICK_TIME;	//(30sec) modified by CMC 8/4/2001
192
193  while( !dns_main_quit ){
194
195    /* set the one second time out */
196    tv.tv_sec = DNS_TICK_TIME;	  //modified by CMC 8/3/2001
197    tv.tv_usec = 0;
198
199    /* now copy the main rfds in the active one as it gets modified by select*/
200    active_rfds = rfds;
201
202    retval = select( FD_SETSIZE, &active_rfds, NULL, NULL, &tv );
203
204    if (retval){
205      /* data is now available */
206      dns_read_packet( dns_sock, &m );
207      dns_handle_request( &m );
208    }else{
209      /* select time out */
210      ptr = dns_request_list;
211      while( ptr ){
212	next = ptr->next;
213	ptr->time_pending++;
214	if( ptr->time_pending > DNS_TIMEOUT/DNS_TICK_TIME ){
215	  /* CMC: ptr->time_pending= DNS_TIMEOUT ~ DNS_TIMEOUT+DNS_TICK_TIME */
216	  debug("Request timed out\n");
217	  /* send error back */
218	  dns_construct_error_reply(ptr);
219	  dns_write_packet( dns_sock, ptr->src_addr, ptr->src_port, ptr );
220	  dns_request_list = dns_list_remove( dns_request_list, ptr );
221	}
222	ptr = next;
223      } /* while(ptr) */
224
225      /* purge cache */
226      purge_time--;
227      if( purge_time <= 0 ){			//modified by CMC 8/4/2001
228	cache_purge( config.purge_time );
229	//purge_time = config.purge_time / 60;
230	purge_time = CACHE_CHECK_TIME / DNS_TICK_TIME; 	//(30sec) modified by CMC 8/3/2001
231      }
232
233    } /* if (retval) */
234  }
235  return 0;
236}
237
238
239#if DNS_DEBUG	//added by CMC 8/4/2001
240/*****************************************************************************/
241void debug_perror( char * msg ) {
242	debug( "%s : %s\n" , msg , strerror(errno) );
243}
244/*****************************************************************************/
245void debug(char *fmt, ...)
246{
247#define MAX_MESG_LEN 1024
248
249  va_list args;
250  char text[ MAX_MESG_LEN ];
251
252  sprintf( text, "[ %d ]: ", getpid());
253  va_start (args, fmt);
254  vsnprintf( &text[strlen(text)], MAX_MESG_LEN - strlen(text), fmt, args);
255  va_end (args);
256
257  if( config.debug_file[0] ){
258    FILE *fp;
259    fp = fopen( config.debug_file, "a");
260    if(!fp){
261      syslog( LOG_ERR, "could not open log file %m" );
262      return;
263    }
264    fprintf( fp, "%s", text);
265    fclose(fp);
266  }
267
268  /** if not in daemon-mode stderr was not closed, use it. */
269  if( ! config.daemon_mode ) {
270    fprintf( stderr, "%s", text);
271  }
272}
273#else
274void debug_perror( char * msg ) {}
275void debug(char *fmt, ...) {}
276#endif
277/*****************************************************************************
278 * print usage informations to stderr.
279 *
280 *****************************************************************************/
281void usage(char * program , char * message ) {
282  fprintf(stderr,"%s\n" , message );
283  fprintf(stderr,"usage : %s [-c <config-file>] [-d] [-h] [-P]\n", program );
284  fprintf(stderr,"\t-c <config-file>\tread configuration from <config-file>\n");
285#if DNS_DEBUG	//added by CMC 8/6/2001
286  fprintf(stderr,"\t-d \t\trun in debug (=non-daemon) mode.\n");
287#else
288  fprintf(stderr,"\t-d \t\trun in debug (=non-daemon) mode.\n");
289  fprintf(stderr,"\t-d \t\tCMC: Please re-compile dproxy with DNS_DEBUG=1\n");
290#endif
291  fprintf(stderr,"\t-P \t\tprint configuration on stdout and exit.\n");
292  fprintf(stderr,"\t-h \t\tthis message.\n");
293}
294/*****************************************************************************
295 * get commandline options.
296 *
297 * @return 0 on success, < 0 on error.
298 *****************************************************************************/
299int get_options( int argc, char ** argv )
300{
301  char c = 0;
302  int not_daemon = 0, cc;
303  int want_printout = 0;
304  char * progname = argv[0];
305
306  conf_defaults();
307
308  while( (cc = getopt( argc, argv, "c:dhP")) != EOF ) {
309    c = (char)cc;	//added by CMC 8/3/2001
310    switch(c) {
311	 case 'c':
312  		conf_load(optarg);
313		break;
314	 case 'd':
315		not_daemon = 1;
316		break;
317	 case 'h':
318		usage(progname,"");
319		return -1;
320	 case 'P':
321		want_printout = 1;
322		break;
323	 default:
324		usage(progname,"");
325		return -1;
326    }
327  }
328
329  /** unset daemon-mode if -d was given. */
330  if( not_daemon ) {
331	 config.daemon_mode = 0;
332  }
333
334  if( want_printout ) {
335	 conf_print();
336	 exit(0);
337  }
338  return 0;
339}
340/*****************************************************************************/
341void sig_hup (int signo)
342{
343  signal(SIGHUP, sig_hup); /* set this for the next sighup */
344  conf_load (config.config_file);
345}
346/*****************************************************************************/
347int main(int argc, char **argv)
348{
349
350  /* get commandline options, load config if needed. */
351  if(get_options( argc, argv ) < 0 ) {
352	  exit(1);
353  }
354
355  signal(SIGHUP, sig_hup);
356
357  dns_init();
358
359  if (config.daemon_mode) {
360    /* Standard fork and background code */
361    switch (fork()) {
362	 case -1:	/* Oh shit, something went wrong */
363		debug_perror("fork");
364		exit(-1);
365	 case 0:	/* Child: close off stdout, stdin and stderr */
366		close(0);
367		close(1);
368		close(2);
369		break;
370	 default:	/* Parent: Just exit */
371		exit(0);
372    }
373  }
374
375  dns_main_loop();
376
377  return 0;
378}
379
380