1 /*
2     wcmgr - Webalizer (DNS) Cache file Manager
3 
4     webalizer - a web server log analysis program
5 
6     Copyright (C) 1997-2013  Bradford L. Barrett
7 
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version, and provided that the above
12     copyright and permission notice is included with all distributed
13     copies of this or derived software.
14 
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18     GNU General Public License for more details.
19 
20     You should have received a copy of the GNU General Public License
21     along with this program; if not, write to the Free Software
22     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
23 
24 */
25 
26 /*********************************************/
27 /* STANDARD INCLUDES                         */
28 /*********************************************/
29 
30 #include <time.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <locale.h>
35 
36 #ifndef USE_DNS
37 
38 /* ********************************************************** */
39 /* If DNS support is not enabled, then we just compile a stub */
40 /* program that displays an appropriate warning when run.     */
41 /* ********************************************************** */
42 
main()43 int main()
44 {
45    printf("********************* NOTICE!! *********************\n");
46    printf("This version of the Webalizer was not compiled with\n");
47    printf("DNS support.  In order to use this program, you must\n");
48    printf("configure the Webalizer at build time with the DNS\n");
49    printf("support enabled (--enable-dns configure option).\n");
50    printf("****************************************************\n\n");
51    exit(1);   /* exit with error code */
52 }
53 
54 #else /* USE_DNS defined */
55 
56 #include <errno.h>
57 #include <unistd.h>                           /* normal stuff             */
58 #include <fcntl.h>
59 #include <ctype.h>
60 #include <sys/utsname.h>
61 #include <sys/stat.h>
62 
63 /* ensure getopt    */
64 #ifdef HAVE_GETOPT_H
65 #include <getopt.h>
66 #endif
67 
68 /* ensure sys/types */
69 #ifndef _SYS_TYPES_H
70 #include <sys/types.h>
71 #endif
72 
73 #include <db.h>
74 #include "webalizer.h"
75 
76 /* Stupid pre-processor tricks */
77 #define xstr(x) #x
78 #define str(x) xstr(x)
79 #define SMAXHOST str(MAXHOST)   /* String version of MAXHOST value */
80 
81 #ifndef DB_NOTFOUND
82 #define DB_NOTFOUND 1
83 #endif
84 
85 /*********************************************/
86 /* Forward reference local functions         */
87 /*********************************************/
88 
89 void list_cache(void);
90 void stat_cache(void);
91 void export_cache(void);
92 void import_cache(void);
93 void find_rec(void);
94 void add_rec(void);
95 void del_rec(void);
96 void purge_cache(void);
97 void create_cache(void);
98 static int db_put(char *, char *, int, time_t);
99 
100 /*********************************************/
101 /* GLOBAL VARIABLES                          */
102 /*********************************************/
103 
104 char    *pname       = "WCMGR - Webalizer (DNS) Cache file Manager";
105 char    *version     = "1.00";             /* program version             */
106 char    *editlvl     = "04";               /* edit level                  */
107 char    *moddate     = "26-Aug-2013";      /* modification date           */
108 char    *copyright   = "Copyright 2007-2013 by Bradford L. Barrett";
109 
110 int       action     = 'l';                /* action flag (default=list)  */
111 int       create     = 0;                  /* cache creation flag         */
112 int       verbose    = 0;                  /* Verbose flag (1=be verbose) */
113 int       rec_ttl    = 7;                  /* purge TTL in days           */
114 DB        *dns_db    = NULL;               /* DNS cache database          */
115 DB        *out_db    = NULL;               /* output cache db if needed   */
116 #ifdef USE_DB185
117 int       cursorp    = 0;                  /* database cursor             */
118 #else
119 DBC       *cursorp   = NULL;               /* database cursor             */
120 #endif
121 DBT       q, r;                            /* query/reply structures      */
122 char      *in_file   = NULL;               /* input cache filename        */
123 char      out_file[MAXHOST+4];             /* output cache filename       */
124 int       dns_fd     = 0;                  /* database file descriptor    */
125 time_t    runtime;                         /* runtime for TTL calcs       */
126 char      addr[129];                       /* buffer for IP search addr   */
127 char      name[MAXHOST+1];                 /* buffer for name value       */
128 
129 extern char  *optarg;                      /* command line processing     */
130 extern int   optind;
131 extern int   opterr;
132 
133 /* dnsRecord structure used in wcmgr */
134 struct dnsRec
135        {
136           time_t    timeStamp;             /* Timestamp of resolv data    */
137           int       numeric;               /* 0: Name, 1: IP-address      */
138           char      hostName[MAXHOST+1];   /* Hostname buffer (variable)  */
139        } dns_rec;
140 
141 #define DNSZ sizeof(struct dnsRec)         /* define static record size   */
142 
143 /*********************************************/
144 /* PRINT_VER - display version information   */
145 /*********************************************/
146 
print_ver()147 void print_ver()
148 {
149 #ifndef USE_DB185
150    int v,r,l;
151 #endif
152    struct utsname system_info;
153    uname(&system_info);
154    printf("%s V%s-%s\n%s\n",pname,version,editlvl,copyright);
155    if (verbose)
156    {
157 #ifndef USE_DB185
158       db_version(&v,&r,&l);
159 #endif
160       printf("System  : %s %s (%s)\n",
161              system_info.sysname,
162              system_info.release,
163              system_info.machine);
164 #ifndef USE_DB185
165       printf("DB Ver. : V%d.%d.%d\n",v,r,l);
166 #endif
167       printf("Mod Date: %s\n",moddate);
168    }
169    printf("\n");
170    exit(0);
171 }
172 
173 /*********************************************/
174 /* PRINT_HELP - Command help display         */
175 /*********************************************/
176 
print_help(void)177 void print_help(void)
178 {
179    printf("Usage: wcmgr [options] cache-file\n\n");
180    printf("Options:\n");
181    printf(" -h         This help display\n");
182    printf(" -V         Version information\n");
183    printf(" -v         be verbose\n");
184    printf(" -a addr    Add DNS record\n");
185    printf(" -c         Create new cache file\n");
186    printf(" -d addr    Delete DNS record\n");
187    printf(" -f addr    Find DNS record\n");
188    printf(" -i name    Import cache from file\n");
189    printf(" -l         List cache file contents\n");
190    printf(" -n name    hostname (used for add)\n");
191    printf(" -p num     Purge after num days\n");
192 #ifndef USE_DB185
193    printf(" -s         Display cache file stats/info\n");
194 #endif
195    printf(" -t num     TTL value (for add and stats)\n");
196    printf(" -x name    Export cache to tab file\n");
197    printf("\n");
198    printf("If no options are specified, the default\n");
199    printf("action is to list the cache file contents.\n\n");
200    exit(0);
201 }
202 
203 /*********************************************/
204 /* TTL_AGE - format TTL age for printing     */
205 /*********************************************/
206 
ttl_age(time_t now,time_t then)207 const char *ttl_age(time_t now, time_t then)
208 {
209    static   char our_buffer[32];         /* string return buffer      */
210    time_t   age;                         /* age value in seconds      */
211    int      days, hours, mins;           /* day/hour/min counters     */
212 
213    /* get age in seconds */
214    age=now-then;
215 
216    /* now calc days/hours/min */
217    days=age/86400;   age=age-(days*86400);
218    hours=age/3600;   age=age-(hours*3600);
219    mins=age/60;
220 
221    /* format the string */
222    sprintf(our_buffer,"%02dd:%02dh:%02dm",days, hours, mins);
223 
224    /* and return to caller */
225    return our_buffer;
226 }
227 
228 /*********************************************/
229 /* MAIN entry point here                     */
230 /*********************************************/
231 
main(int argc,char * argv[])232 int main(int argc, char *argv[])
233 {
234    int       i;                          /* gotta have one of these :-)     */
235 
236    /* some systems need this */
237    setlocale(LC_CTYPE,"");
238 
239    /* initalize name/addr */
240    memset(addr, 0, sizeof(addr));
241    memset(name, 0, sizeof(name));
242    memset(out_file,0,sizeof(out_file));
243 
244    /* Get our command line arguments */
245    opterr = 0;
246    while ((i=getopt(argc,argv,"a:cd:f:hi:ln:p::st:vVx:"))!=EOF)
247    {
248       switch (i)
249       {
250          case 'a':  action='a'; strncpy(addr,optarg,sizeof(addr)-1);  break;
251          case 'c':  if (action!='i') action='c'; create=1;            break;
252          case 'd':  action='d'; strncpy(addr,optarg,sizeof(addr)-1);  break;
253          case 'f':  action='f'; strncpy(addr,optarg,sizeof(addr)-1);  break;
254          case 'i':  action='i'; strncpy(out_file,optarg,sizeof(out_file)-1);
255                                                                       break;
256          case 'h':  print_help();                                     break;
257          case 'n':  strncpy(name,optarg,sizeof(name)-1);              break;
258          case 'p':  action='p'; if (optarg!=NULL) rec_ttl=atoi(optarg); break;
259 #ifndef USE_DB185
260          case 's':  action='s';                                       break;
261 #endif
262          case 't':  rec_ttl=atoi(optarg);                             break;
263          case 'v':  verbose=1;                                        break;
264          case 'V':  print_ver();                                      break;
265          case 'x':  action='x'; strncpy(out_file,optarg,sizeof(out_file)-1);
266                                                                       break;
267          case ':':  /* catch invalid options here */
268          case '?':                                                    break;
269          case 'l':  /* This is the default action */
270          default:   action='l';                                       break;
271       }
272    }
273 
274    /* Get cache filename if specified */
275    if (argc - optind == 0) print_help();   /* gots to have a filename!!     */
276    in_file = argv[optind];
277 
278 #ifndef USE_DB185
279    /* Try to create our DB handle */
280    if ( db_create(&dns_db, NULL, 0) )
281    {
282       fprintf(stderr,"Error: unable to create db handle!\n");
283       exit(1);
284    }
285 #endif
286 
287    /* force sane TTL value */
288    if (rec_ttl > 99) rec_ttl=99;
289    if (rec_ttl < 0 ) rec_ttl=7;
290 
291    /* Branch on 'action' specified   */
292    switch (action)
293    {
294       case 'a': add_rec();                                            break;
295       case 'c': create_cache();                                       break;
296       case 'd': del_rec();                                            break;
297       case 'f': find_rec();                                           break;
298       case 'i': import_cache();                                       break;
299 #ifndef USE_DB185
300       case 's': stat_cache();                                         break;
301 #endif
302       case 'p': purge_cache();                                        break;
303       case 'x': export_cache();                                       break;
304       case 'l':
305       default:  list_cache();                                         break;
306    }
307    exit(0);
308 }
309 
310 /*********************************************/
311 /* LIST_CACHE - Dump out cache contents      */
312 /*********************************************/
313 
list_cache()314 void list_cache()
315 {
316    int       i;
317    char      ip_buf[48];
318    u_int64_t t_rec=0;
319    u_int64_t t_num=0;
320 
321    /* open the database (read-only) */
322 #ifdef USE_DB185
323    dns_db = dbopen(in_file, O_RDONLY, 0664, DB_HASH, NULL);
324    i = errno;
325    if (!dns_db)
326 #else
327    if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, DB_RDONLY, 0)))
328 #endif
329    {
330       /* Error opening the cache file.. tell user and exit */
331 #ifdef USE_DB185
332       fprintf(stderr,"Error: %s: %s\n",in_file,strerror(i));
333 #else
334       fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i));
335 #endif
336       exit(1);
337    }
338 
339 #ifndef USE_DB185
340    /* Create a cursor */
341    if ( dns_db->cursor(dns_db, NULL, &cursorp, 0) )
342    {
343       fprintf(stderr,"Error: Unable to create cursor!\n");
344       exit(1);
345    }
346 #endif
347 
348    /* get our runtime for TTL calculations */
349    time(&runtime);
350 
351    if (verbose)
352    {
353       printf("Webalizer DNS Cache file listing generated %s\n",ctime(&runtime));
354       printf("IP Address         TTL Age    Hostname\n");
355       printf("--------------- ------------- ------------------------" \
356              "-----------------------\n");
357    }
358 
359    /* initalize data areas */
360    memset(&q, 0, sizeof(DBT));
361    memset(&r, 0, sizeof(DBT));
362    memset(&dns_rec, 0, sizeof(struct dnsRec));
363 
364    /* Loop through database */
365 #ifdef USE_DB185
366    cursorp = dns_db->seq(dns_db, &q, &r, R_FIRST);
367    while (!cursorp)
368 #else
369    while (!cursorp->c_get(cursorp, &q, &r, DB_NEXT))
370 #endif
371    {
372       /* got a record */
373       t_rec++;
374       memset(ip_buf, 0, sizeof(ip_buf));
375       strncpy(ip_buf, q.data, (q.size>47)?47:q.size);  /* save IP address  */
376       memcpy(&dns_rec, r.data, (r.size>DNSZ)?DNSZ:r.size);
377 
378       if (dns_rec.numeric) t_num++;
379       printf("%-15s [%s] %s\n",ip_buf,
380              (dns_rec.timeStamp)?
381                 ttl_age(runtime, dns_rec.timeStamp):
382                 "-permanent-",
383              dns_rec.hostName);
384 
385       /* done, clear for next rec */
386       memset(&q, 0, sizeof(DBT));
387       memset(&r, 0, sizeof(DBT));
388 #ifdef USE_DB185
389       cursorp = dns_db->seq(dns_db, &q, &r, R_NEXT);
390 #endif
391    }
392 
393    if (verbose)
394    {
395       printf("------------------------------------------------------" \
396              "-----------------------\n");
397       printf("Filename: %s  (%llu records)\n",in_file, t_rec);
398    }
399 }
400 
401 /*********************************************/
402 /* PURGE_CACHE - Purge cache of expired recs */
403 /*********************************************/
404 
purge_cache()405 void purge_cache()
406 {
407    int       i;
408    char      ip_buf[48];
409    u_int64_t age=0;
410    u_int64_t t_in=0;
411    u_int64_t t_out=0;
412    u_int64_t t_exp=0;
413 
414    /* file control struct */
415    struct    flock our_flock;
416 
417    if (verbose) printf("Purging records over %d days from '%s'\n",
418                         rec_ttl, in_file);
419 
420    /* open the input database (read-write) */
421 #ifdef USE_DB185
422    dns_db = dbopen(in_file, O_RDWR|O_CREAT, 0664, DB_HASH, NULL);
423    i = errno;
424    if (!dns_db)
425 #else
426    if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, 0, 0)))
427 #endif
428    {
429       /* Error opening the cache file.. tell user and exit */
430 #ifdef USE_DB185
431       fprintf(stderr,"Error: %s: %s\n",in_file,strerror(i));
432 #else
433       fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i));
434 #endif
435       exit(1);
436    }
437 
438    /* get file descriptor */
439 #ifdef USE_DB185
440    dns_fd = dns_db->fd(dns_db);
441 #else
442    dns_db->fd(dns_db, &dns_fd);
443 #endif
444 
445    /* Try to lock the file */
446    our_flock.l_whence=SEEK_SET;
447    our_flock.l_start=0;
448    our_flock.l_len=0;
449    our_flock.l_type=F_WRLCK;
450 
451    if (fcntl(dns_fd,F_SETLK,&our_flock) <0)
452    {
453       /* Error - can't lock file */
454       printf("Error: Unable to lock cache file: %s\n",strerror(errno));
455       exit(1);
456    }
457 
458 #ifndef USE_DB185
459    /* Create a cursor */
460    if ( dns_db->cursor(dns_db, NULL, &cursorp, 0) )
461    {
462       fprintf(stderr,"Error: Unable to create cursor!\n");
463       exit(1);
464    }
465 #endif
466 
467 #ifndef USE_DB185
468    /* Try to create our output DB handle */
469    if ( db_create(&out_db, NULL, 0) )
470    {
471       fprintf(stderr,"Error: unable to create output db handle!\n");
472       exit(1);
473    }
474 #endif
475 
476    /* generate output filename */
477    memset(out_file, 0, sizeof(out_file));
478    sprintf(out_file, "%s.new", in_file);
479 
480    /* open the output database (read-write) */
481 #ifdef USE_DB185
482    out_db = dbopen(out_file, O_RDWR|O_CREAT, 0664, DB_HASH, NULL);
483    i = errno;
484    if (!out_db)
485 #else
486    if ((i=out_db->open(out_db, NULL, out_file, NULL,
487                   DB_HASH, DB_CREATE|DB_EXCL, 0644)))
488 #endif
489    {
490       /* Error opening the cache file.. tell user and exit */
491 #ifdef USE_DB185
492       fprintf(stderr,"Error: %s: %s\n",out_file,strerror(i));
493 #else
494       fprintf(stderr,"Error: %s: %s\n",out_file,db_strerror(i));
495 #endif
496       exit(1);
497    }
498 
499    /* get our runtime for TTL calculations */
500    time(&runtime);
501 
502    /* initalize data areas */
503    memset(&q, 0, sizeof(DBT));
504    memset(&r, 0, sizeof(DBT));
505 
506    /* Loop through database */
507 #ifdef USE_DB185
508    cursorp = dns_db->seq(dns_db, &q, &r, R_FIRST);
509    while (!cursorp)
510 #else
511    while (!cursorp->c_get(cursorp, &q, &r, DB_NEXT))
512 #endif
513    {
514       /* got a record */
515       t_in++;
516       memcpy(&dns_rec, r.data, (r.size>DNSZ)?DNSZ:r.size);
517 
518       /* get record ttl age */
519       if (dns_rec.timeStamp==0) age=0;
520       else age = runtime - dns_rec.timeStamp;
521 
522       if ( age <= (rec_ttl*86400) )
523       {
524          /* Good record.. insert into new cache file */
525 #ifdef USE_DB185
526          if ( (out_db->put)(out_db, &q, &r, 0) < 0)
527 #else
528          if ( (i=out_db->put(out_db, NULL, &q, &r, 0)) != 0 )
529 #endif
530          {
531 #ifdef USE_DB185
532             fprintf(stderr,"Error: db_put fail: %s!\n",strerror(i));
533 #else
534             fprintf(stderr,"Error: db_put fail: %s!\n",db_strerror(i));
535 #endif
536             exit(1);
537          }
538          else t_out++;
539       }
540       else
541       {
542          /* Expired record */
543          t_exp++;
544          if (verbose)
545          {
546             memset(ip_buf, 0, sizeof(ip_buf));
547             strncpy(ip_buf, q.data, (q.size>47)?47:q.size);
548             printf("Purging %-16s [%s]\n",ip_buf,
549                ttl_age(runtime,dns_rec.timeStamp));
550          }
551       }
552 
553       /* done, clear for next rec */
554       memset(&q, 0, sizeof(DBT));
555       memset(&r, 0, sizeof(DBT));
556 #ifdef USE_DB185
557       cursorp = dns_db->seq(dns_db, &q, &r, R_NEXT);
558 #endif
559    }
560 
561    /* Successful exit! */
562    our_flock.l_type=F_UNLCK;
563    fcntl(dns_fd, F_SETLK, &our_flock);
564 #ifdef USE_DB185
565    dns_db->close(dns_db);
566    out_db->close(out_db);
567 #else
568    dns_db->close(dns_db, 0);
569    out_db->close(out_db, 0);
570 #endif
571 
572    /* rename files */
573    if (rename(out_file, in_file))
574    {
575       fprintf(stderr,"Error renaming file: %s\n",strerror(errno));
576       exit(1);
577    }
578 
579    if (verbose)
580       printf("%llu of %llu records purged from '%s'\n",t_exp,t_in,in_file);
581 }
582 
583 /*********************************************/
584 /* STAT_CACHE - Display cache stats/info     */
585 /*********************************************/
586 
587 #ifndef USE_DB185
stat_cache()588 void stat_cache()
589 {
590    /* Define some variables */
591    int        i;
592    time_t     min_age=0;                     /* min/max TTL age in cache   */
593    time_t     max_age=0;
594    u_int64_t  t_rec=0;                       /* Various record totals      */
595    u_int64_t  t_err=0;
596    u_int64_t  t_name=0;
597    u_int64_t  t_num=0;
598    u_int64_t  t_perm=0;
599    u_int64_t  t_old=0;
600    time_t     age;
601 
602    /* open the database (read-only) */
603    if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, DB_RDONLY, 0)))
604    {
605       /* Error opening the cache file.. tell user and exit */
606       fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i));
607       exit(1);
608    }
609 
610    /* Create a cursor */
611    if ( dns_db->cursor(dns_db, NULL, &cursorp, 0) )
612    {
613       fprintf(stderr,"Error: Unable to create cursor!\n");
614       exit(1);
615    }
616 
617    /* get our runtime for TTL calculations */
618    time(&runtime);
619 
620    /* initalize data areas */
621    memset(&q, 0, sizeof(DBT));
622    memset(&r, 0, sizeof(DBT));
623    memset(&dns_rec, 0, sizeof(struct dnsRec));
624 
625    /* Loop through database */
626    while (!cursorp->c_get(cursorp, &q, &r, DB_NEXT))
627    {
628       t_rec++;                                               /* add to total */
629       if (r.size >= sizeof(dns_rec)) { t_err++; continue; }  /* size error?  */
630       memcpy(&dns_rec, r.data, r.size);                      /* get record   */
631       if (dns_rec.numeric) t_num++; else t_name++;           /* resolved?    */
632 
633       if (dns_rec.timeStamp!=0)                              /* permanent?   */
634       {
635          age=runtime-dns_rec.timeStamp;                      /* calc age     */
636          if ((age < min_age) || (t_rec==1) ) min_age=age;    /* min/max age  */
637          if ( age > max_age ) max_age=age;                   /* if not perm  */
638          if ( age > (rec_ttl*86400)) t_old++;                /* purgable?    */
639       }
640       else t_perm++;                                         /* inc counter  */
641 
642       /* done, clear for next rec */
643       memset(&q, 0, sizeof(DBT));
644       memset(&r, 0, sizeof(DBT));
645    }
646 
647    /* Print actual record counts */
648    printf("Report generated on: %s",ctime(&runtime));
649    printf("DNS Cache Filename : %s\n",in_file);
650 
651    printf("Total Records      : %llu\n",t_rec);
652    printf("Total Resolved     : %llu\n",t_name);
653    printf("Total Unresolved   : %llu\n",t_num);
654    printf("Total Permanent    : %llu\n",t_perm);
655    printf("Newest Record age  : %s\n",ttl_age(min_age,0));
656    printf("Oldest Record age  : %s\n",ttl_age(max_age,0));
657    printf("Total over %02d days : %llu\n",rec_ttl,t_old);
658    if (t_err) printf("Record Size Errors : %llu\n",t_err);
659    printf("\n");
660 }
661 #endif
662 
663 /*********************************************/
664 /* FIND_REC - Find IP record in cache        */
665 /*********************************************/
666 
find_rec()667 void find_rec()
668 {
669    int   i;
670    char  ip_buf[48];
671 
672    /* open the database (read-only) */
673 #ifdef USE_DB185
674    dns_db = dbopen(in_file, O_RDONLY, 0664, DB_HASH, NULL);
675    i = errno;
676    if (!dns_db)
677 #else
678    if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, DB_RDONLY, 0)))
679 #endif
680    {
681       /* Error opening the cache file.. tell user and exit */
682 #ifdef USE_DB185
683       fprintf(stderr,"Error: %s: %s\n",in_file,strerror(i));
684 #else
685       fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i));
686 #endif
687       exit(1);
688    }
689 
690    /* get our runtime for TTL calculations */
691    time(&runtime);
692 
693    /* initalize data areas */
694    memset(&q, 0, sizeof(DBT));
695    memset(&r, 0, sizeof(DBT));
696    memset(&dns_rec, 0, sizeof(struct dnsRec));
697 
698    /* search the cache */
699    q.data = &addr;
700    q.size = strlen(addr);
701 #ifdef USE_DB185
702    if ( (i=(dns_db->get)(dns_db, &q, &r, 0)) == 0 )
703 #else
704    if ( (i=dns_db->get(dns_db, NULL, &q, &r, 0)) == 0)
705 #endif
706    {
707       /* We found it! display info */
708       memset(ip_buf, 0, sizeof(ip_buf));
709       strncpy(ip_buf, q.data, (q.size>47)?47:q.size);  /* save IP address  */
710       memcpy(&dns_rec, r.data, (r.size>DNSZ)?DNSZ:r.size);
711 
712       if (verbose)
713       {
714          /* Verbose display */
715          printf("Address  : %s\n",ip_buf);
716          printf("Hostname : %s\n",dns_rec.hostName);
717          printf("Resolved : %s\n",(dns_rec.numeric)?"No":"Yes");
718          if (dns_rec.timeStamp)
719          {
720             /* Not Permanent */
721             printf("Timestamp: %s",ctime(&dns_rec.timeStamp));
722             printf("TTL age  : %s\n\n",ttl_age(runtime, dns_rec.timeStamp));
723          }
724          else
725          {
726             printf("Timestamp: N/A\n");
727             printf("TTL age  : Permanent\n");
728          }
729       }
730       else
731       {
732          /* Standard 1 line display */
733          printf("%-15s [%s] %s\n",ip_buf,
734              (dns_rec.timeStamp)?
735                  ttl_age(runtime, dns_rec.timeStamp):
736                  "-permanent-",
737              dns_rec.hostName);
738       }
739    }
740    else
741    {
742       if (i==DB_NOTFOUND)
743          printf("%s not found!\n",addr);
744       else
745 #ifdef USE_DB185
746          printf("Error: %s\n",strerror(i));
747 #else
748          printf("Error: %s\n",db_strerror(i));
749 #endif
750    }
751 }
752 
753 /*********************************************/
754 /* DEL_REC - Delete record from cache file   */
755 /*********************************************/
756 
del_rec()757 void del_rec()
758 {
759    int   i;
760    char  *cp;
761 
762    /* ensure we have addr string */
763    if (addr[0]!='\0') cp=addr;
764    else
765    {
766       fprintf(stderr,"Error: No IP address specified!\n");
767       exit(1);
768    }
769 
770    /* ensure IPv6 addresses are lowercase */
771    cp=addr; while (*cp!='\0') *cp++=tolower(*cp);
772 
773    /* open the database (read-write) */
774 #ifdef USE_DB185
775    dns_db = dbopen(in_file, O_RDWR, 0664, DB_HASH, NULL);
776    i = errno;
777    if (!dns_db)
778 #else
779    if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, 0, 0)))
780 #endif
781    {
782       /* Error opening the cache file.. tell user and exit */
783 #ifdef USE_DB185
784       fprintf(stderr,"Error: %s: %s\n",in_file,strerror(i));
785 #else
786       fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i));
787 #endif
788       exit(1);
789    }
790 
791    /* initalize data areas */
792    memset(&q, 0, sizeof(DBT));
793    memset(&r, 0, sizeof(DBT));
794    memset(&dns_rec, 0, sizeof(struct dnsRec));
795 
796    /* search the cache */
797    q.data = &addr;
798    q.size = strlen(addr);
799 
800    /* Try to delete the record */
801 #ifdef USE_DB185
802    if ( (i=dns_db->del(dns_db, &q, 0)) )
803 #else
804    if ( (i=dns_db->del(dns_db, NULL, &q, 0)) )
805 #endif
806    {
807       if (i==DB_NOTFOUND)
808       {
809          printf("%s not found in cache!\n",addr);
810          exit(1);
811       }
812       else
813       {
814 #ifdef USE_DB185
815          fprintf(stderr,"Error: %s\n",strerror(i));
816 #else
817          fprintf(stderr,"Error: %s\n",db_strerror(i));
818 #endif
819          exit(1);
820       }
821    }
822 #ifdef USE_DB185
823    dns_db->close(dns_db);
824 #else
825    dns_db->close(dns_db, 0);
826 #endif
827    if (verbose)
828       printf("%s sucessfully deleted from cache file\n",addr);
829 }
830 
831 /*********************************************/
832 /* ADD_REC - Add record to cache file        */
833 /*********************************************/
834 
add_rec()835 void add_rec()
836 {
837    int   i;
838    char  *cp;
839 
840    /* ensure we have addr string */
841    if (addr[0]!='\0') cp=addr;
842    else
843    {
844       fprintf(stderr,"Error: No IP address specified!\n");
845       exit(1);
846    }
847 
848    /* and check size */
849    if (strlen(addr)>47)
850    {
851       fprintf(stderr,"Error: IP address too long!\n");
852       exit(1);
853    }
854 
855    /* ensure everything is lowercase */
856    cp=addr; while (*cp!='\0') *cp++=tolower(*cp);
857    if (name[0]!='\0')
858    {
859       cp=name; while (*cp!='\0') *cp++=tolower(*cp);
860    }
861 
862    /* open the database (read-write) */
863 #ifdef USE_DB185
864    dns_db = dbopen(in_file, O_RDWR, 0664, DB_HASH, NULL);
865    i = errno;
866    if (!dns_db)
867 #else
868    if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, 0, 0)))
869 #endif
870    {
871       /* Error opening the cache file.. tell user and exit */
872 #ifdef USE_DB185
873       fprintf(stderr,"Error: %s: %s\n",in_file,strerror(i));
874 #else
875       fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i));
876 #endif
877       exit(1);
878    }
879 
880    /* get our runtime for TTL calculations */
881    time(&runtime);
882 
883    /* initalize data areas */
884    memset(&q, 0, sizeof(DBT));
885    memset(&r, 0, sizeof(DBT));
886    memset(&dns_rec, 0, sizeof(struct dnsRec));
887 
888    /* search the cache */
889    q.data = &addr;
890    q.size = strlen(addr);
891 #ifdef USE_DB185
892    if ( (i=(dns_db->get)(dns_db, &q, &r, 0)) == 0 )
893 #else
894    if ( (i=dns_db->get(dns_db, NULL, &q, &r, 0)) == 0)
895 #endif
896    {
897       fprintf(stderr,"Error: %s already exists in cache!\n",addr);
898       exit(1);
899    }
900    else
901    {
902       if (i!=DB_NOTFOUND)
903       {
904 #ifdef USE_DB185
905          fprintf(stderr,"Error: %s\n",strerror(i));
906 #else
907          fprintf(stderr,"Error: %s\n",db_strerror(i));
908 #endif
909          exit(1);
910       }
911       else
912       {
913          /* check hostname */
914          if (name[0]=='\0')
915             strncpy(name,addr,strlen(addr));
916 
917          /* check if perm */
918          if (rec_ttl==0) runtime=0;
919 
920          /* put it in the database */
921          if (db_put(addr, name, (strcmp(name,addr))?0:1, runtime)==0)
922 #ifdef USE_DB185
923             dns_db->close(dns_db);
924 #else
925             dns_db->close(dns_db,0);
926 #endif
927          if (verbose)
928             printf("%s sucessfully added to cache file\n",addr);
929       }
930    }
931 }
932 
933 /*********************************************/
934 /* CREATE_CACHE - Create a new cache file    */
935 /*********************************************/
936 
create_cache()937 void create_cache()
938 {
939    int   i;
940 
941    /* create the database */
942 #ifdef USE_DB185
943    dns_db = dbopen(in_file, O_RDWR|O_CREAT, 0664, DB_HASH, NULL);
944    i = errno;
945    if (!dns_db)
946 #else
947    if ((i=dns_db->open(dns_db,NULL,in_file,NULL,
948                   DB_HASH,DB_CREATE|DB_EXCL,0644)))
949 #endif
950    {
951       /* Error opening the cache file.. tell user and exit */
952 #ifdef USE_DB185
953       fprintf(stderr,"Error: %s: %s\n",in_file,strerror(i));
954 #else
955       fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i));
956 #endif
957       exit(1);
958    }
959 #ifdef USE_DB185
960    dns_db->close(dns_db);
961 #else
962    dns_db->close(dns_db,0);
963 #endif
964    if (verbose) printf("Cache file %s created successfully\n",in_file);
965 }
966 
967 /*********************************************/
968 /* IMPORT_CACHE - import cache from tab file */
969 /*********************************************/
970 
import_cache()971 void import_cache()
972 {
973    int       i, flag=0;
974    u_int64_t t_rec=0;
975    FILE      *in_fp;
976    char      ip_buf[48];
977    char      buffer[4096];
978 
979    /* open the database (read-write) */
980 #ifdef USE_DB185
981    flag=O_RDWR;
982    if (create) flag|=O_CREAT;
983    dns_db = dbopen(in_file, flag, 0664, DB_HASH, NULL);
984    i = errno;
985    if (!dns_db)
986 #else
987    if (create) flag=DB_CREATE|DB_EXCL;
988    if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, flag, 0644)))
989 #endif
990    {
991       /* Error opening the cache file.. tell user and exit */
992 #ifdef USE_DB185
993       fprintf(stderr,"Error: %s: %s\n",in_file,strerror(i));
994 #else
995       fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i));
996 #endif
997       exit(1);
998    }
999 
1000    /* open our import file */
1001    in_fp=fopen(out_file,"r");
1002    if (in_fp)
1003    {
1004       while ((fgets(buffer,4096,in_fp)) != NULL)
1005       {
1006          memset(&dns_rec, 0, sizeof(dns_rec));
1007          memset(&ip_buf, 0, sizeof(ip_buf));
1008          i = sscanf(buffer,"%47s\t%lu\t%d\t%" SMAXHOST "s",
1009                     ip_buf,
1010                     &dns_rec.timeStamp,
1011                     &dns_rec.numeric,
1012                     dns_rec.hostName);
1013 
1014          if (ip_buf[0]=='#') continue;  /* skip comments */
1015 
1016          if (i!=4)
1017          {
1018             fprintf(stderr,"Error reading tab file %s\n",out_file);
1019             exit(1);
1020          }
1021 
1022          t_rec++;   /* bump totals */
1023 
1024          /* put it in the database */
1025          if (db_put(ip_buf, dns_rec.hostName,
1026                     dns_rec.numeric, dns_rec.timeStamp)!=0)
1027          {
1028             fprintf(stderr,"Error inserting cache record:\n%s\n",buffer);
1029             exit(1);
1030          }
1031       }
1032    }
1033    else fprintf(stderr,"Error: File not found: %s\n",out_file);
1034 #ifdef USE_DB185
1035    dns_db->close(dns_db);
1036 #else
1037    dns_db->close(dns_db,0);
1038 #endif
1039 
1040    if (verbose) printf("%llu records imported into '%s' from file '%s'\n",
1041                        t_rec, in_file, out_file);
1042 }
1043 
1044 /*********************************************/
1045 /* EXPORT_CACHE - export cache to tab file   */
1046 /*********************************************/
1047 
export_cache()1048 void export_cache()
1049 {
1050    int       i;
1051    u_int64_t t_rec=0;
1052    char      ip_buf[48];
1053    FILE      *out_fp;
1054    struct    stat out_stat;
1055 
1056    /* make sure files are different! */
1057    if (!strcmp(in_file,out_file))
1058    {
1059       fprintf(stderr,"Error: Bad export filename: %s\n",out_file);
1060       exit(1);
1061    }
1062 
1063    /* open the database (read-only) */
1064 #ifdef USE_DB185
1065    dns_db = dbopen(in_file, O_RDONLY, 0664, DB_HASH, NULL);
1066    i = errno;
1067    if (!dns_db)
1068 #else
1069    if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, DB_RDONLY, 0)))
1070 #endif
1071    {
1072       /* Error opening the cache file.. tell user and exit */
1073 #ifdef USE_DB185
1074       fprintf(stderr,"Error: %s: %s\n",in_file,strerror(i));
1075 #else
1076       fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i));
1077 #endif
1078       exit(1);
1079    }
1080 
1081 #ifndef USE_DB185
1082    /* Create a cursor */
1083    if ( dns_db->cursor(dns_db, NULL, &cursorp, 0) )
1084    {
1085       fprintf(stderr,"Error: Unable to create cursor!\n");
1086       exit(1);
1087    }
1088 #endif
1089 
1090    /* stat output file */
1091    if ( !(lstat(out_file, &out_stat)) )
1092    {
1093       /* check if the file is a symlink */
1094       if ( S_ISLNK(out_stat.st_mode) )
1095       {
1096          fprintf(stderr,"%s %s\n","Error: File is a symlink:",out_file);
1097          exit(1);
1098       }
1099    }
1100 
1101    /* open output file */
1102    if ( (out_fp=fopen(out_file,"w")) == NULL)
1103    {
1104       fprintf(stderr,"%s %s\n","Error: Cannot create file:",out_file);
1105       exit(1);
1106    }
1107 
1108    /* initalize data areas */
1109    memset(&q, 0, sizeof(DBT));
1110    memset(&r, 0, sizeof(DBT));
1111    memset(&dns_rec, 0, sizeof(struct dnsRec));
1112 
1113    /* Loop through database */
1114 #ifdef USE_DB185
1115    cursorp = dns_db->seq(dns_db, &q, &r, R_FIRST);
1116    while (!cursorp)
1117 #else
1118    while (!cursorp->c_get(cursorp, &q, &r, DB_NEXT))
1119 #endif
1120    {
1121       /* got a record */
1122       t_rec++;
1123       memset(ip_buf, 0, sizeof(ip_buf));
1124       strncpy(ip_buf, q.data, (q.size>47)?47:q.size);  /* save IP address  */
1125       memcpy(&dns_rec, r.data, (r.size>DNSZ)?DNSZ:r.size);
1126 
1127       /* Print out tab delimited line          */
1128       /* Format: IP timestamp numeric hostname */
1129       fprintf(out_fp,"%s\t%lu\t%d\t%s\n",
1130               ip_buf,dns_rec.timeStamp,
1131               dns_rec.numeric,
1132               dns_rec.hostName);
1133 
1134       /* done, clear for next rec */
1135       memset(&q, 0, sizeof(DBT));
1136       memset(&r, 0, sizeof(DBT));
1137 #ifdef USE_DB185
1138       cursorp = dns_db->seq(dns_db, &q, &r, R_NEXT);
1139 #endif
1140    }
1141 #ifdef USE_DB185
1142    dns_db->close(dns_db);
1143 #else
1144    dns_db->close(dns_db,0);
1145 #endif
1146    fclose(out_fp);
1147 
1148    if (verbose) printf("%llu records exported from '%s' to file '%s'\n",
1149                        t_rec, in_file, out_file);
1150 }
1151 
1152 /*********************************************/
1153 /* DB_PUT - put key/val in the cache db      */
1154 /*********************************************/
1155 
db_put(char * key,char * value,int numeric,time_t ttl)1156 static int db_put(char *key, char *value, int numeric, time_t ttl)
1157 {
1158 
1159    /* dnsRecord structure used in database */
1160    struct dnsRecord
1161    {
1162           time_t    timeStamp;             /* Timestamp of resolv data    */
1163           int       numeric;               /* 0: Name, 1: IP-address      */
1164           char      hostName[1];           /* Hostname buffer (variable)  */
1165    };
1166 
1167    int    i;
1168    DBT    k, v;
1169    struct dnsRecord *recPtr = NULL;
1170    int    nameLen = strlen(value)+1;
1171 
1172    /* Align to multiple of eight bytes */
1173    int recSize = (sizeof(struct dnsRecord)+nameLen+7) & ~0x7;
1174 
1175    /* make sure we have a db ;) */
1176    if(dns_db)
1177    {
1178       if((recPtr = calloc(1, recSize)))
1179       {
1180          recPtr->timeStamp = ttl;
1181          recPtr->numeric = numeric;
1182          memcpy(&recPtr->hostName, value, nameLen);
1183          memset(&k, 0, sizeof(k));
1184          memset(&v, 0, sizeof(v));
1185 
1186          k.data = key;
1187          k.size = strlen(key);
1188 
1189          v.size = recSize;
1190          v.data = recPtr;
1191 
1192 #ifdef USE_DB185
1193          if ( (dns_db->put)(dns_db, &k, &v, 0) < 0)
1194             fprintf(stderr,"Error: db_put fail: %s!\n",strerror(errno));
1195 #else
1196          if ( (i=dns_db->put(dns_db, NULL, &k, &v, 0)) != 0 )
1197             fprintf(stderr,"Error: db_put fail: %s!\n",db_strerror(i));
1198 #endif
1199          free(recPtr);
1200       }
1201       else return 1;
1202    }
1203    else return 1;
1204    return i;
1205 }
1206 #endif /* USE_DNS */
1207