924 lines
26 KiB
C
924 lines
26 KiB
C
/*
|
|
webalizer - a web server log analysis program
|
|
|
|
Copyright (C) 1997-2013 Bradford L. Barrett
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
/*********************************************/
|
|
/* STANDARD INCLUDES */
|
|
/*********************************************/
|
|
|
|
#include <time.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h> /* normal stuff */
|
|
#include <ctype.h>
|
|
#include <sys/utsname.h>
|
|
#include <zlib.h>
|
|
|
|
/* ensure sys/types */
|
|
#ifndef _SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
|
|
/* Need socket header? */
|
|
#ifdef HAVE_SYS_SOCKET_H
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
/* some systems need this */
|
|
#ifdef HAVE_MATH_H
|
|
#include <math.h>
|
|
#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> /* ensure getaddrinfo/getnameinfo */
|
|
#include <signal.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/wait.h>
|
|
#include <db.h> /* DB header ****************/
|
|
#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 */
|
|
|
|
DB *dns_db = NULL; /* DNS cache database */
|
|
int dns_fd = 0;
|
|
|
|
DB *geo_db = NULL; /* GeoDB database */
|
|
DBC *geo_dbc = NULL; /* GeoDB database cursor */
|
|
|
|
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(void *, 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);
|
|
int iptype(char *, unsigned char *);
|
|
|
|
/*********************************************/
|
|
/* RESOLVE_DNS - lookup IP in cache */
|
|
/*********************************************/
|
|
|
|
void resolve_dns(struct log_struct *log_rec)
|
|
{
|
|
DBT query, response;
|
|
int i;
|
|
/* 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 */
|
|
|
|
memset(&query, 0, sizeof(query));
|
|
memset(&response, 0, sizeof(response));
|
|
query.data = log_rec->hostname;
|
|
query.size = strlen(log_rec->hostname);
|
|
|
|
if (debug_mode) fprintf(stderr,"Checking %s...", log_rec->hostname);
|
|
|
|
if ( (i=dns_db->get(dns_db, NULL, &query, &response, 0)) == 0)
|
|
{
|
|
memcpy(&alignedRecord, response.data, sizeof(struct dnsRecord));
|
|
strncpy (log_rec->hostname,
|
|
((struct dnsRecord *)response.data)->hostName,
|
|
MAXHOST);
|
|
log_rec->hostname[MAXHOST-1]=0;
|
|
if (debug_mode)
|
|
fprintf(stderr," found: %s (%ld)\n",
|
|
log_rec->hostname, alignedRecord.timeStamp);
|
|
}
|
|
else /* not found or error occured during get */
|
|
{
|
|
if (debug_mode)
|
|
{
|
|
if (i==DB_NOTFOUND) fprintf(stderr," not found\n");
|
|
else fprintf(stderr," error (%d)\n",i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*********************************************/
|
|
/* 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_int64_t listEntries = 0;
|
|
|
|
struct sigaction sigPipeAction;
|
|
struct stat dbStat;
|
|
/* 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);
|
|
|
|
/* get processing start time */
|
|
start_time = time(NULL);
|
|
|
|
/* 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 ( (db_create(&dns_db, NULL, 0) != 0) ||
|
|
(dns_db->open(dns_db, NULL,
|
|
dns_cache, NULL, DB_HASH,
|
|
DB_CREATE, 0644) != 0) )
|
|
{
|
|
/* 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_db->fd(dns_db, &dns_fd);
|
|
|
|
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, 0);
|
|
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((void *)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((void *)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 */
|
|
{
|
|
struct addrinfo hints, *ares;
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
if (0 == getaddrinfo(log_rec.hostname, "0", &hints, &ares))
|
|
{
|
|
DBT q, r;
|
|
memset(&q, 0, sizeof(q));
|
|
memset(&r, 0, sizeof(r));
|
|
q.data = log_rec.hostname;
|
|
q.size = strlen(log_rec.hostname);
|
|
|
|
/* Check if we have it in DB */
|
|
if ( (i=dns_db->get(dns_db, NULL, &q, &r, 0)) == 0 )
|
|
{
|
|
/* have a record for this address */
|
|
memcpy(&alignedRecord, r.data, sizeof(struct dnsRecord));
|
|
if (alignedRecord.timeStamp != 0)
|
|
/* If it's not permanent, check if it's TTL has expired */
|
|
if ( (runtime-alignedRecord.timeStamp ) > (86400*cache_ttl) )
|
|
put_dnode(log_rec.hostname, ares->ai_addr,
|
|
ares->ai_addrlen, host_table);
|
|
}
|
|
else
|
|
{
|
|
if (i==DB_NOTFOUND)
|
|
put_dnode(log_rec.hostname, ares->ai_addr,
|
|
ares->ai_addrlen, host_table);
|
|
}
|
|
freeaddrinfo(ares);
|
|
}
|
|
}
|
|
}
|
|
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, 0);
|
|
return 0;
|
|
}
|
|
|
|
/* process our list now... */
|
|
process_list(l_list);
|
|
|
|
/* get processing end time */
|
|
end_time = time(NULL);
|
|
|
|
/* display DNS processing statistics */
|
|
if (time_me || (verbose>1))
|
|
{
|
|
if (verbose<2 && time_me) printf("DNS: ");
|
|
printf("%llu %s ",listEntries, msg_addresses);
|
|
|
|
/* total processing time in seconds */
|
|
temp_time = difftime(end_time,start_time);
|
|
if (temp_time==0) temp_time=1;
|
|
printf("%s %.0f %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, 0);
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*********************************************/
|
|
/* PROCESS_LIST - do the resoluton... */
|
|
/*********************************************/
|
|
|
|
static void process_list(DNODEPTR l_list)
|
|
{
|
|
DNODEPTR trav;
|
|
|
|
char child_buf[MAXHOST+3-((unsigned long)&trav+sizeof(trav))%3];
|
|
char dns_buf[MAXHOST];
|
|
int i;
|
|
int pid;
|
|
int nof_children = 0;
|
|
fd_set rd_set;
|
|
char hbuf[NI_MAXHOST];
|
|
|
|
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;
|
|
|
|
close(child[i].inpipe[0]);
|
|
close(child[i].outpipe[1]);
|
|
|
|
/* get struct sockaddr_storage here */
|
|
while((size = read(child[i].outpipe[0], child_buf, MAXHOST)))
|
|
{
|
|
if(size < 0)
|
|
{
|
|
perror("read error");
|
|
exit(1);
|
|
}
|
|
else
|
|
{
|
|
/* Clear out our buffer */
|
|
memset(hbuf,0,NI_MAXHOST);
|
|
|
|
if(0 == getnameinfo((struct sockaddr*)child_buf, size,
|
|
hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD))
|
|
{
|
|
/* must be at least 4 chars */
|
|
if (strlen(hbuf)>3)
|
|
{
|
|
/* If long hostname, take max domain name part */
|
|
if ((size = strlen(hbuf)) > MAXHOST-2)
|
|
strcpy(child_buf,(hbuf+(size-MAXHOST+1)));
|
|
else strcpy(child_buf, hbuf);
|
|
size = strlen(child_buf);
|
|
}
|
|
else
|
|
{
|
|
if (debug_mode)
|
|
printf("Child %d getnameinfo bad hbuf!\n",i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(debug_mode)
|
|
printf("Child %d getnameinfo failed!\n",i);
|
|
}
|
|
|
|
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,
|
|
trav->addrlen) != -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 %d bytes to Child %d\n",
|
|
trav->addrlen, 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... */
|
|
|
|
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( strlen(dns_buf) > 1 &&
|
|
memcmp(dns_buf, &(child[i].cur->addr),
|
|
sizeof(child[i].cur->addr)))
|
|
{
|
|
if(debug_mode)
|
|
printf("Child %d Got a result: %s -> %s\n",
|
|
i, child[i].cur->string, dns_buf);
|
|
db_put(child[i].cur->string, dns_buf, 0);
|
|
}
|
|
else
|
|
{
|
|
if(debug_mode)
|
|
printf("Child %d could not resolve: %s (%s)\n",
|
|
i, child[i].cur->string,
|
|
(cache_ips)?"cache":"no cache");
|
|
if (cache_ips) /* Cache non-resolved? */
|
|
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;
|
|
char *cp;
|
|
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);
|
|
memset(&k, 0, sizeof(k));
|
|
memset(&v, 0, sizeof(v));
|
|
|
|
/* Ensure all data is lowercase */
|
|
cp=key; while (*cp++!='\0') *cp=tolower(*cp);
|
|
cp=value; while (*cp++!='\0') *cp=tolower(*cp);
|
|
|
|
k.data = key;
|
|
k.size = strlen(key);
|
|
|
|
v.size = recSize;
|
|
v.data = recPtr;
|
|
|
|
if ( dns_db->put(dns_db, NULL, &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 ( (db_create(&dns_db, NULL, 0) != 0) ||
|
|
(dns_db->open(dns_db, NULL,
|
|
dns_cache, NULL, DB_HASH,
|
|
DB_RDONLY, 0644) != 0) )
|
|
{
|
|
/* 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_db->fd(dns_db, &dns_fd);
|
|
|
|
/* 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, 0);
|
|
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, 0);
|
|
return 1;
|
|
}
|
|
|
|
/*********************************************/
|
|
/* GEODB_OPEN - Open GeoDB database/cursor */
|
|
/*********************************************/
|
|
|
|
DB *geodb_open(char *dbname)
|
|
{
|
|
char buf[1025];
|
|
|
|
if (dbname==NULL)
|
|
snprintf(buf,sizeof(buf),"%s/GeoDB.dat",GEODB_LOC);
|
|
else
|
|
strncpy(buf,dbname,sizeof(buf)-1);
|
|
buf[sizeof(buf)-1]='\0';
|
|
|
|
/* create database thingie */
|
|
if ( db_create(&geo_db, NULL, 0) ) return NULL;
|
|
|
|
/* open the database */
|
|
if (geo_db->open(geo_db,NULL,buf,NULL,DB_BTREE,DB_RDONLY,0)) return NULL;
|
|
|
|
/* create our cursor */
|
|
if (geo_db->cursor(geo_db,NULL,&geo_dbc,0))
|
|
{
|
|
geo_db->close(geo_db,0);
|
|
return NULL;
|
|
}
|
|
/* all is well in the world */
|
|
return geo_db;
|
|
}
|
|
|
|
/*********************************************/
|
|
/* GEODB_VER - Get database version info */
|
|
/*********************************************/
|
|
|
|
char *geodb_ver(DB *db, char *str)
|
|
{
|
|
int i;
|
|
DBT k,v;
|
|
unsigned char x[16];
|
|
|
|
memset(&x, 0, sizeof(x));
|
|
memset(&k, 0, sizeof(k));
|
|
memset(&v, 0, sizeof(v));
|
|
k.data=&x;
|
|
k.size=sizeof(x);
|
|
|
|
i=geo_db->get(geo_db, NULL, &k, &v, 0);
|
|
|
|
if (i) strncpy(str, "Unknown", 8);
|
|
else strncpy(str, v.data+3, v.size-3);
|
|
return str;
|
|
}
|
|
|
|
/*********************************************/
|
|
/* GEODB_GET_CC - Get country code for IP */
|
|
/*********************************************/
|
|
|
|
char *geodb_get_cc(DB *db, char *ip, char *buf)
|
|
{
|
|
int i;
|
|
DBT k,v;
|
|
unsigned char addr[16];
|
|
|
|
memset(addr, 0, sizeof(addr));
|
|
strncpy(buf, "--", 3);
|
|
|
|
/* get IP address */
|
|
if (!iptype(ip, addr)) return buf;
|
|
|
|
/* kludge for IPv6 mapped IPv4 */
|
|
if (addr[0]==0 && addr[1]==0 && addr[2]==0) { addr[10]=0; addr[11]=0; }
|
|
|
|
/* kludge for IPv6 6to4 (RFC3056) */
|
|
if (addr[0]==0x20 && addr[1]==0x02)
|
|
{
|
|
memcpy(&addr[12],&addr[2],4);
|
|
memset(&addr,0,12);
|
|
}
|
|
|
|
memset(&k, 0, sizeof(k));
|
|
memset(&v, 0, sizeof(v));
|
|
k.data=&addr;
|
|
k.size=sizeof(addr);
|
|
|
|
i=geo_dbc->c_get(geo_dbc, &k, &v, DB_SET_RANGE);
|
|
if (!i) memcpy(buf, v.data, 2);
|
|
return buf;
|
|
}
|
|
|
|
/*********************************************/
|
|
/* GEODB_CLOSE - close GeoDB database */
|
|
/*********************************************/
|
|
|
|
void geodb_close(DB *db)
|
|
{
|
|
db->close(db,0);
|
|
}
|
|
|
|
/*********************************************/
|
|
/* IPTYPE - get IP type and format addr buf */
|
|
/*********************************************/
|
|
|
|
int iptype(char *ip, unsigned char *buf)
|
|
{
|
|
if (inet_pton(AF_INET6, ip, buf)>0) return 2;
|
|
if (inet_pton(AF_INET, ip, buf+12)>0) return 1;
|
|
else return 0;
|
|
}
|
|
|
|
#endif /* USE_DNS */
|