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