Viewing file: dns_resolv.c (24.44 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/* webalizer - a web server log analysis program
Copyright (C) 1997-2001 Bradford L. Barrett (brad@mrunix.net)
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version, and provided that the above copyright and permission notice is included with all distributed copies of this or derived software.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
This software uses the gd graphics library, which is copyright by Quest Protein Database Center, Cold Spring Harbor Labs. Please see the documentation supplied with the library for additional information and license terms, or visit www.boutell.com/gd/ for the most recent version of the library and supporting documentation.
dns_resolv.c - based on the dns-resolver code submitted by Henning P. Schmiedehausen <hps@tanstaafl.de> and modified for inclusion in the Webalizer directly. Enabled with -DUSE_DNS.
*/
/*********************************************/ /* STANDARD INCLUDES */ /*********************************************/
#include <time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> /* normal stuff */ #include <ctype.h> #include <sys/utsname.h> #include <sys/times.h> #include <zlib.h>
/* Need socket header? */ #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> #endif
/* ensure getopt */ #ifdef HAVE_GETOPT_H #include <getopt.h> #endif
/* ensure sys/types */ #ifndef _SYS_TYPES_H #include <sys/types.h> #endif
/* some systems need this */ #ifdef HAVE_MATH_H #include <math.h> #endif
/* SunOS 4.x Fix */ #ifndef CLK_TCK #define CLK_TCK _SC_CLK_TCK #endif
#ifdef USE_DNS /* skip everything in this file if no DNS */
#include <netinet/in.h> /* include stuff we need for dns lookups, */ #include <arpa/inet.h> /* DB access, file control, etc... */ #include <fcntl.h> #include <netdb.h> #include <signal.h> #include <sys/signal.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/wait.h>
#ifdef HAVE_DB_185_H #include <db_185.h> /* on my RH6.0 system ?!? */ #else #include <db.h> /* DB header ****************/ #endif /* HAVE_DB_185_H */
#include "webalizer.h" /* main header */ #include "lang.h" /* language declares */ #include "hashtab.h" /* hash table functions */ #include "parser.h" /* log parser functions */ #include "dns_resolv.h" /* our header */
/* local data */
#ifndef HAVE_ERRNO_H int errno; /* errno for those in need */ #endif
DB *dns_db = NULL; /* DNS cache database */ int dns_fd = 0;
struct dns_child child[MAXCHILD]; /* DNS child pipe data */
DNODEPTR host_table[MAXHASH]; /* hostname/ip hash table */
char buffer[BUFSIZE]; /* log file record buffer */ char tmp_buf[BUFSIZE]; /* used to temp save above */ struct utsname system_info; /* system info structure */
int raiseSigChild = 1;
time_t runtime; time_t start_time, end_time; float temp_time;
extern char *our_gzgets(gzFile, char *, int); /* external our_gzgets func */
/* internal function prototypes */
static void process_list(DNODEPTR); static void sigChild(int); static void db_put(char *, char *, int); void set_fl(int, int); void clr_fl(int, int);
/*********************************************/ /* RESOLVE_DNS - lookup IP in cache */ /*********************************************/
void resolve_dns(struct log_struct *log_rec) { DBT query, response; /* aligned dnsRecord to prevent Solaris from doing a dump */ /* (not found in debugger, as it can dereference it :( */ struct dnsRecord alignedRecord;
if (!dns_db) return; /* ensure we have a dns db */
query.data = log_rec->hostname; query.size = strlen(log_rec->hostname);
if (debug_mode) fprintf(stderr,"Checking %s...", log_rec->hostname);
switch((dns_db->get)(dns_db, &query, &response, 0)) { case -1: if (debug_mode) fprintf(stderr," Lookup error\n"); break; case 1: if (debug_mode) fprintf(stderr," not found\n"); break; case 0: { memcpy(&alignedRecord, response.data, sizeof(struct dnsRecord)); strncpy (log_rec->hostname, ((struct dnsRecord *)response.data)->hostName, MAXHOST); log_rec->hostname[MAXHOST]=0; if (debug_mode) fprintf(stderr," found: %s (%ld)\n", log_rec->hostname, alignedRecord.timeStamp); break; } default: if (debug_mode) fprintf(stderr," Invalid response\n"); } }
/*********************************************/ /* DNS_RESOLVER - read log and lookup IP's */ /*********************************************/
int dns_resolver(void *log_fp) { DNODEPTR h_entries; DNODEPTR l_list = NULL;
int i; int save_verbose=verbose;
u_long listEntries = 0;
struct sigaction sigPipeAction; struct stat dbStat; struct tms mytms; /* aligned dnsRecord to prevent Solaris from doing a dump */ /* (not found in debugger, as it can dereference it :( */ struct dnsRecord alignedRecord;
struct flock tmp_flock;
tmp_flock.l_whence=SEEK_SET; /* default flock fields */ tmp_flock.l_start=0; tmp_flock.l_len=0; tmp_flock.l_pid=0;
time(&runtime); start_time = times(&mytms); /* get start time */
/* minimal sanity check on it */ if(stat(dns_cache, &dbStat) < 0) { if(errno != ENOENT) { dns_cache=NULL; dns_db=NULL; return 0; /* disable cache */ } } else { if(!dbStat.st_size) /* bogus file, probably from a crash */ { unlink(dns_cache); /* remove it so we can recreate... */ } } /* open cache file */ if(!(dns_db = dbopen(dns_cache, O_RDWR|O_CREAT, 0664, DB_HASH, NULL))) { /* Error: Unable to open DNS cache file <filename> */ if (verbose) fprintf(stderr,"%s %s\n",msg_dns_nodb,dns_cache); dns_cache=NULL; dns_db=NULL; return 0; /* disable cache */ }
/* get file descriptor */ dns_fd = dns_db->fd(dns_db);
tmp_flock.l_type=F_WRLCK; /* set read/write lock type */ if (fcntl(dns_fd,F_SETLK,&tmp_flock) < 0) /* and barf if we cant lock */ { /* Error: Unable to lock DNS cache file <filename> */ if (verbose) fprintf(stderr,"%s %s\n",msg_dns_nolk,dns_cache); dns_db->close(dns_db); dns_cache=NULL; dns_db=NULL; return 0; /* disable cache */ }
/* Setup signal handlers */ sigPipeAction.sa_handler = SIG_IGN; sigPipeAction.sa_flags = SA_RESTART; sigemptyset(&sigPipeAction.sa_mask);
sigaction(SIGPIPE, &sigPipeAction, NULL);
/* disable warnings/errors for this run... */ verbose=0;
/* Main loop to read log records (either regular or zipped) */ while ( (gz_log)?(our_gzgets((gzFile)log_fp,buffer,BUFSIZE) != Z_NULL): (fgets(buffer,BUFSIZE,log_fname?(FILE *)log_fp:stdin) != NULL)) { if (strlen(buffer) == (BUFSIZE-1)) { /* get the rest of the record */ while ( (gz_log)?(our_gzgets((gzFile)log_fp,buffer,BUFSIZE)!=Z_NULL): (fgets(buffer,BUFSIZE,log_fname?(FILE *)log_fp:stdin)!=NULL)) { if (strlen(buffer) < BUFSIZE-1) break; } continue; /* go get next record if any */ }
strcpy(tmp_buf, buffer); /* save buffer in case of error */ if(parse_record(buffer)) /* parse the record */ { if((log_rec.addr.s_addr = inet_addr(log_rec.hostname)) != INADDR_NONE) { DBT q, r; q.data = log_rec.hostname; q.size = strlen(log_rec.hostname); switch((dns_db->get)(dns_db, &q, &r, 0)) { case -1: break; /* Error while retrieving .. just ignore */ case 1: /* No record on file, queue up for resolving */ { put_dnode(log_rec.hostname, &log_rec.addr, host_table); break; }
case 0: /* We have a record for this address */ { memcpy(&alignedRecord, r.data, sizeof(struct dnsRecord)); if((runtime - alignedRecord.timeStamp ) < DNS_CACHE_TTL) { if(!alignedRecord.numeric) /* It is a name. Do nothing */ break; /* otherise, it a number.. fall through */ } else { /* queue up stale entry for retrieval */ put_dnode(log_rec.hostname, &log_rec.addr, host_table); break; } } } } } } verbose = save_verbose; /* restore verbosity level... */
listEntries = 0; /* build our linked list l_list */ for(i=0;i < MAXHASH; i++) { for(h_entries=host_table[i]; h_entries ; h_entries = h_entries->next) { h_entries->llist = l_list; l_list = h_entries; listEntries++; } }
if(!l_list) { /* No valid addresses found... */ if (verbose>1) printf("%s\n",msg_dns_none); tmp_flock.l_type=F_UNLCK; fcntl(dns_fd, F_SETLK, &tmp_flock); dns_db->close(dns_db); return 0; }
/* process our list now... */ process_list(l_list);
/* display timing totals ? */ end_time = times(&mytms); /* display timing totals? */ if (time_me || (verbose>1)) { if (verbose<2 && time_me) printf("DNS: "); printf("%lu %s ",listEntries, msg_addresses);
/* get processing time (end-start) */ temp_time = (float)(end_time-start_time)/CLK_TCK; printf("%s %.2f %s", msg_in, temp_time, msg_seconds);
/* calculate records per second */ if (temp_time) i=( (int)((float)listEntries/temp_time) ); else i=0;
if ( (i>0) && (i<=listEntries) ) printf(", %d/sec\n", i); else printf("\n"); }
/* processing done, exit */ tmp_flock.l_type=F_UNLCK; fcntl(dns_fd, F_SETLK, &tmp_flock); dns_db->close(dns_db); return 0;
}
/*********************************************/ /* PROCESS_LIST - do the resoluton... */ /*********************************************/
static void process_list(DNODEPTR l_list) { DNODEPTR trav;
char child_buf[MAXHOST]; char dns_buf[MAXHOST]; int i; int pid; int nof_children = 0; fd_set rd_set; struct sigaction sigChildAction; sigChildAction.sa_handler = sigChild; sigChildAction.sa_flags = SA_NOCLDSTOP|SA_RESTART; sigemptyset(&sigChildAction.sa_mask);
raiseSigChild = 0; sigaction(SIGCHLD, &sigChildAction, NULL); /* fire up our child processes */ for(i=0; i < dns_children; i++) { if(pipe(child[i].inpipe)) { if (verbose) fprintf(stderr,"INPIPE creation error"); return; /* exit(1) */ }
if(pipe(child[i].outpipe)) { if (verbose) fprintf(stderr,"OUTPIPE creation error"); return; /* exit(1); */ }
/* fork it off */ switch(pid=fork()) { case -1: { if (verbose) fprintf(stderr,"FORK error"); return; /* exit(1); */ } case 0: /* Child */ { int size;
struct hostent *res_ent;
close(child[i].inpipe[0]); close(child[i].outpipe[1]);
/* get struct in_addr here */ while((size = read(child[i].outpipe[0], child_buf, MAXHOST))) { if(size < 0) { perror("read error"); exit(1); } else { if(debug_mode) printf("Child got work: %lx(%d)\n", *((unsigned long *)child_buf), size);
if((res_ent = gethostbyaddr(child_buf, size, AF_INET))) { /* must be at least 4 chars */ if (strlen(res_ent->h_name)>3) { if(debug_mode) printf("Child got %s for %lx(%d), %d bytes\n", res_ent->h_name, *((unsigned long *)child_buf), size,strlen(res_ent->h_name));
/* If long hostname, take max domain name part */ if ((size = strlen(res_ent->h_name)) > MAXHOST-2) strcpy(child_buf,(res_ent->h_name+(size-MAXHOST+1))); else strcpy(child_buf, res_ent->h_name); size = strlen(child_buf); } else { if (debug_mode) printf("gethostbyaddr returned bad h_name!\n"); } } else { if(debug_mode) printf("gethostbyaddr returned NULL! (%d)\n",h_errno); }
if (write(child[i].inpipe[1], child_buf, size) == -1) { perror("write error"); exit(1); } } } close(child[i].inpipe[1]); close(child[i].outpipe[0]); if(debug_mode) printf( "Child %d got closed input, shutting down\n", i);
fflush(stdout); exit(0); } /* case 0 */ default: { child[i].pid = pid; child[i].flags = DNS_CHILD_READY|DNS_CHILD_RUNNING; nof_children++; close(child[i].inpipe[1]); close(child[i].outpipe[0]);
set_fl(child[i].inpipe[0], O_NONBLOCK); } } }
trav = l_list;
while(nof_children) { static struct timeval selectTimeval; int res; int max_fd; FD_ZERO(&rd_set); max_fd = 0;
if(raiseSigChild) { int pid;
while((pid = waitpid(-1, NULL, WNOHANG)) > 0) { for(i=0;i<dns_children;i++) { if(child[i].pid == pid) { child[i].pid = 0; child[i].flags &= ~(DNS_CHILD_READY|DNS_CHILD_RUNNING); nof_children--;
if(debug_mode) printf("Reaped Child %d\n", pid);
break; } } } raiseSigChild--; continue; /* while, nof children has just changed */ }
for(i=0;i<dns_children;i++) { if(child[i].flags & DNS_CHILD_RUNNING) /* Child is running */ { if(child[i].flags & DNS_CHILD_READY) { child[i].flags &= ~DNS_CHILD_READY;
if(trav) /* something to resolve */ { if (write(child[i].outpipe[1], &(trav->addr.s_addr), sizeof(trav->addr.s_addr)) != -1) { /* We will watch this child */ child[i].cur = trav; FD_SET(child[i].inpipe[0], &rd_set); max_fd = MAX(max_fd, child[i].inpipe[0]);
if(debug_mode) printf("Giving %s (%lx) to Child %d for resolving\n", child[i].cur->string, (unsigned long)child[i].cur->addr.s_addr, i);
trav = trav->llist; } else /* write error */ { if(errno != EINTR) /* Could be a signal */ { perror("Could not write to pipe"); close(child[i].outpipe[1]); /* kill */ child[i].flags &= ~DNS_CHILD_RUNNING; /* child */ } } } else /* List is complete */ { close(child[i].outpipe[1]); /* Go away */ child[i].flags &= ~DNS_CHILD_RUNNING; /* Child is dead */ } } else { /* Look, the busy child... */ FD_SET(child[i].inpipe[0], &rd_set); max_fd = MAX(max_fd, child[i].inpipe[0]); } } }
selectTimeval.tv_sec = 5; /* This stuff ticks in 5 second intervals */ selectTimeval.tv_usec = 0;
switch(res = select(max_fd+1, &rd_set, NULL, NULL, &selectTimeval)) { case -1: { if(errno != EINTR) /* Could be a signal */ perror("Error in select");
break; }
case 0: /* Timeout, just fall once through the child loop */ { if(debug_mode) printf("tick\n"); break; }
default: { for(i=0; i< dns_children;i++) { if(!res) /* All file descriptors done */ break;
if(FD_ISSET(child[i].inpipe[0], &rd_set)) { int size;
res--; /* One less... */
if(debug_mode) printf("Work requested from Child %d\n", i);
switch (size=read(child[i].inpipe[0], dns_buf, MAXHOST)) { case -1: { if(errno != EINTR) perror("Could not read from pipe"); break; } case 0: { /* EOF. Child has closed Pipe. It shouldn't have */ /* done that, could be an error or something. */ /* Reap it */ close(child[i].outpipe[1]); child[i].flags &= ~DNS_CHILD_RUNNING;
if(debug_mode) printf("Child %d wants to be reaped\n", i);
break; }
default: { dns_buf[size] = '\0'; if(memcmp(dns_buf, &(child[i].cur->addr.s_addr), sizeof(child[i].cur->addr.s_addr))) { if(debug_mode) printf("Got a result (%d): %s -> %s\n", i, child[i].cur->string, dns_buf); db_put(child[i].cur->string, dns_buf, 0); } else { if(debug_mode) printf("Could not resolve (%d): %s\n", i, child[i].cur->string); /* db_put(child[i].cur->string,child[i].cur->string,1); */ }
if(debug_mode) printf("Child %d back in task pool\n", i);
/* Child is back in the task pool */ child[i].flags |= DNS_CHILD_READY; break; } } } } break; } } } return; }
/*********************************************/ /* SET_FL - set flag on pipe FD */ /*********************************************/
void set_fl(int fd, int flags) { int val;
/* get current flags */ if ((val=fcntl(fd, F_GETFL, 0)) < 0) if (verbose) fprintf(stderr,"set_fl F_GETFL error\n");
/* set them */ val |= flags;
/* and write them back */ if ((val=fcntl(fd, F_SETFL, val)) < 0) if (verbose) fprintf(stderr,"set_fl F_SETFL error\n"); }
/*********************************************/ /* CLR_FL - clear flag on pipe FD */ /*********************************************/
void clr_fl(int fd, int flags) { int val;
/* Get current flags */ if ((val=fcntl(fd, F_GETFL, 0)) < 0) if (verbose) fprintf(stderr,"clr_fl F_GETFL error\n");
/* set them */ val &= ~flags;
/* and write them back */ if ((val=fcntl(fd, F_SETFL, val)) < 0) if (verbose) fprintf(stderr,"clr_fl F_SETFL error\n"); }
/*********************************************/ /* DB_PUT - put key/val in the cache db */ /*********************************************/
static void db_put(char *key, char *value, int numeric) { DBT k, v; struct dnsRecord *recPtr = NULL; int nameLen = strlen(value)+1; /* Align to multiple of eight bytes */ int recSize = (sizeof(struct dnsRecord)+nameLen+7) & ~0x7; /* make sure we have a db ;) */ if(dns_db) { if((recPtr = calloc(1, recSize))) { recPtr->timeStamp = runtime; recPtr->numeric = numeric; memcpy(&recPtr->hostName, value, nameLen);
k.data = key; k.size = strlen(key);
v.size = recSize; v.data = recPtr; if((dns_db->put)(dns_db, &k, &v, 0) < 0) if (verbose>1) fprintf(stderr,"db_put fail!\n"); free(recPtr); } } }
/*********************************************/ /* SIGCHILD - raise our signal */ /*********************************************/
static void sigChild(int signum) { raiseSigChild++; }
/*********************************************/ /* OPEN_CACHE - open our cache file RDONLY */ /*********************************************/
int open_cache() { struct stat dbStat; struct flock tmp_flock;
tmp_flock.l_whence=SEEK_SET; /* default flock fields */ tmp_flock.l_start=0; tmp_flock.l_len=0; tmp_flock.l_pid=0; tmp_flock.l_type=F_RDLCK;
/* double check filename was specified */ if(!dns_cache) { dns_db=NULL; return 0; }
/* minimal sanity check on it */ if(stat(dns_cache, &dbStat) < 0) { if(errno != ENOENT) return 0; } else { if(!dbStat.st_size) /* bogus file, probably from a crash */ { unlink(dns_cache); /* remove it so we can recreate... */ } } /* open cache file */ if(!(dns_db = dbopen(dns_cache, O_RDONLY, 0664, DB_HASH, NULL))) { /* Error: Unable to open DNS cache file <filename> */ if (verbose) fprintf(stderr,"%s %s\n",msg_dns_nodb,dns_cache); return 0; /* disable cache */ }
/* get file descriptor */ dns_fd = dns_db->fd(dns_db);
/* Get shared lock on cache file */ if (fcntl(dns_fd, F_SETLK, &tmp_flock) < 0) { if (verbose) fprintf(stderr,"%s %s\n",msg_dns_nolk,dns_cache); dns_db->close(dns_db); return 0; } return 1; }
/*********************************************/ /* CLOSE_CACHE - close our RDONLY cache */ /*********************************************/
int close_cache() { struct flock tmp_flock;
tmp_flock.l_whence=SEEK_SET; /* default flock fields */ tmp_flock.l_start=0; tmp_flock.l_len=0; tmp_flock.l_pid=0; tmp_flock.l_type=F_UNLCK;
/* clear lock and close cache file */ fcntl(dns_fd, F_SETLK, &tmp_flock); dns_db->close(dns_db); return 1; }
#endif /* USE_DNS */
|