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