Commit | Line | Data |
---|---|---|
e015f748 CE |
1 | /* |
2 | wcmgr - Webalizer (DNS) Cache file Manager | |
3 | ||
4 | webalizer - a web server log analysis program | |
5 | ||
6 | Copyright (C) 1997-2011 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 | ||
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 | /*********************************************/ | |
77 | /* Forward reference local functions */ | |
78 | /*********************************************/ | |
79 | ||
80 | void list_cache(void); | |
81 | void stat_cache(void); | |
82 | void export_cache(void); | |
83 | void import_cache(void); | |
84 | void find_rec(void); | |
85 | void add_rec(void); | |
86 | void del_rec(void); | |
87 | void purge_cache(void); | |
88 | void create_cache(void); | |
89 | static int db_put(char *, char *, int, time_t); | |
90 | ||
91 | /*********************************************/ | |
92 | /* GLOBAL VARIABLES */ | |
93 | /*********************************************/ | |
94 | ||
95 | char *pname = "WCMGR - Webalizer (DNS) Cache file Manager"; | |
96 | char *version = "1.00"; /* program version */ | |
97 | char *editlvl = "03"; /* edit level */ | |
98 | char *moddate = "12-Jul-2008"; /* modification date */ | |
99 | char *copyright = "Copyright 2007-2011 by Bradford L. Barrett"; | |
100 | ||
101 | int action = 'l'; /* action flag (default=list) */ | |
102 | int create = 0; /* cache creation flag */ | |
103 | int verbose = 0; /* Verbose flag (1=be verbose) */ | |
104 | int rec_ttl = 7; /* purge TTL in days */ | |
105 | DB *dns_db = NULL; /* DNS cache database */ | |
106 | DB *out_db = NULL; /* output cache db if needed */ | |
107 | DBC *cursorp = NULL; /* database cursor */ | |
108 | DBT q, r; /* query/reply structures */ | |
109 | char *in_file = NULL; /* input cache filename */ | |
110 | char out_file[MAXHOST+4]; /* output cache filename */ | |
111 | int dns_fd = 0; /* database file descriptor */ | |
112 | time_t runtime; /* runtime for TTL calcs */ | |
113 | char addr[129]; /* buffer for IP search addr */ | |
114 | char name[MAXHOST+1]; /* buffer for name value */ | |
115 | ||
116 | extern char *optarg; /* command line processing */ | |
117 | extern int optind; | |
118 | extern int opterr; | |
119 | ||
120 | /* dnsRecord structure used in wcmgr */ | |
121 | struct dnsRec | |
122 | { | |
123 | time_t timeStamp; /* Timestamp of resolv data */ | |
124 | int numeric; /* 0: Name, 1: IP-address */ | |
125 | char hostName[MAXHOST+1]; /* Hostname buffer (variable) */ | |
126 | } dns_rec; | |
127 | ||
128 | /*********************************************/ | |
129 | /* PRINT_VER - display version information */ | |
130 | /*********************************************/ | |
131 | ||
132 | void print_ver() | |
133 | { | |
134 | int v,r,l; | |
135 | struct utsname system_info; | |
136 | uname(&system_info); | |
137 | printf("%s V%s-%s\n%s\n",pname,version,editlvl,copyright); | |
138 | if (verbose) | |
139 | { | |
140 | db_version(&v,&r,&l); | |
141 | printf("System : %s %s (%s)\n", | |
142 | system_info.sysname, | |
143 | system_info.release, | |
144 | system_info.machine); | |
145 | printf("DB Ver. : V%d.%d.%d\n",v,r,l); | |
146 | printf("Mod Date: %s\n",moddate); | |
147 | } | |
148 | printf("\n"); | |
149 | exit(0); | |
150 | } | |
151 | ||
152 | /*********************************************/ | |
153 | /* PRINT_HELP - Command help display */ | |
154 | /*********************************************/ | |
155 | ||
156 | void print_help(void) | |
157 | { | |
158 | printf("Usage: wcmgr [options] cache-file\n\n"); | |
159 | printf("Options:\n"); | |
160 | printf(" -h This help display\n"); | |
161 | printf(" -V Version information\n"); | |
162 | printf(" -v be verbose\n"); | |
163 | printf(" -a addr Add DNS record\n"); | |
164 | printf(" -c Create new cache file\n"); | |
165 | printf(" -d addr Delete DNS record\n"); | |
166 | printf(" -f addr Find DNS record\n"); | |
167 | printf(" -i name Import cache from file\n"); | |
168 | printf(" -l List cache file contents\n"); | |
169 | printf(" -n name hostname (used for add)\n"); | |
170 | printf(" -p num Purge after num days\n"); | |
171 | printf(" -s Display cache file stats/info\n"); | |
172 | printf(" -t num TTL value (for add and stats)\n"); | |
173 | printf(" -x name Export cache to tab file\n"); | |
174 | printf("\n"); | |
175 | printf("If no options are specified, the default\n"); | |
176 | printf("action is to list the cache file contents.\n\n"); | |
177 | exit(0); | |
178 | } | |
179 | ||
180 | /*********************************************/ | |
181 | /* TTL_AGE - format TTL age for printing */ | |
182 | /*********************************************/ | |
183 | ||
184 | const char *ttl_age(time_t now, time_t then) | |
185 | { | |
186 | static char our_buffer[32]; /* string return buffer */ | |
187 | time_t age; /* age value in seconds */ | |
188 | int days, hours, mins; /* day/hour/min counters */ | |
189 | ||
190 | /* get age in seconds */ | |
191 | age=now-then; | |
192 | ||
193 | /* now calc days/hours/min */ | |
194 | days=age/86400; age=age-(days*86400); | |
195 | hours=age/3600; age=age-(hours*3600); | |
196 | mins=age/60; | |
197 | ||
198 | /* format the string */ | |
199 | sprintf(our_buffer,"%02dd:%02dh:%02dm",days, hours, mins); | |
200 | ||
201 | /* and return to caller */ | |
202 | return our_buffer; | |
203 | } | |
204 | ||
205 | /*********************************************/ | |
206 | /* MAIN entry point here */ | |
207 | /*********************************************/ | |
208 | ||
209 | int main(int argc, char *argv[]) | |
210 | { | |
211 | int i; /* gotta have one of these :-) */ | |
212 | ||
213 | /* some systems need this */ | |
214 | setlocale(LC_CTYPE,""); | |
215 | ||
216 | /* initalize name/addr */ | |
217 | memset(addr, 0, sizeof(addr)); | |
218 | memset(name, 0, sizeof(name)); | |
219 | memset(out_file,0,sizeof(out_file)); | |
220 | ||
221 | /* Get our command line arguments */ | |
222 | opterr = 0; | |
223 | while ((i=getopt(argc,argv,"a:cd:f:hi:ln:p::st:vVx:"))!=EOF) | |
224 | { | |
225 | switch (i) | |
226 | { | |
227 | case 'a': action='a'; strncpy(addr,optarg,sizeof(addr)-1); break; | |
228 | case 'c': if (action!='i') action='c'; create=1; break; | |
229 | case 'd': action='d'; strncpy(addr,optarg,sizeof(addr)-1); break; | |
230 | case 'f': action='f'; strncpy(addr,optarg,sizeof(addr)-1); break; | |
231 | case 'i': action='i'; strncpy(out_file,optarg,sizeof(out_file)-1); | |
232 | break; | |
233 | case 'h': print_help(); break; | |
234 | case 'n': strncpy(name,optarg,sizeof(name)-1); break; | |
235 | case 'p': action='p'; if (optarg!=NULL) rec_ttl=atoi(optarg); break; | |
236 | case 's': action='s'; break; | |
237 | case 't': rec_ttl=atoi(optarg); break; | |
238 | case 'v': verbose=1; break; | |
239 | case 'V': print_ver(); break; | |
240 | case 'x': action='x'; strncpy(out_file,optarg,sizeof(out_file)-1); | |
241 | break; | |
242 | case ':': /* catch invalid options here */ | |
243 | case '?': break; | |
244 | case 'l': /* This is the default action */ | |
245 | default: action='l'; break; | |
246 | } | |
247 | } | |
248 | ||
249 | /* Get cache filename if specified */ | |
250 | if (argc - optind == 0) print_help(); /* gots to have a filename!! */ | |
251 | in_file = argv[optind]; | |
252 | ||
253 | /* Try to create our DB handle */ | |
254 | if ( db_create(&dns_db, NULL, 0) ) | |
255 | { | |
256 | fprintf(stderr,"Error: unable to create db handle!\n"); | |
257 | exit(1); | |
258 | } | |
259 | ||
260 | /* force sane TTL value */ | |
261 | if (rec_ttl > 99) rec_ttl=99; | |
262 | if (rec_ttl < 0 ) rec_ttl=7; | |
263 | ||
264 | /* Branch on 'action' specified */ | |
265 | switch (action) | |
266 | { | |
267 | case 'a': add_rec(); break; | |
268 | case 'c': create_cache(); break; | |
269 | case 'd': del_rec(); break; | |
270 | case 'f': find_rec(); break; | |
271 | case 'i': import_cache(); break; | |
272 | case 's': stat_cache(); break; | |
273 | case 'p': purge_cache(); break; | |
274 | case 'x': export_cache(); break; | |
275 | case 'l': | |
276 | default: list_cache(); break; | |
277 | } | |
278 | exit(0); | |
279 | } | |
280 | ||
281 | /*********************************************/ | |
282 | /* LIST_CACHE - Dump out cache contents */ | |
283 | /*********************************************/ | |
284 | ||
285 | void list_cache() | |
286 | { | |
287 | int i; | |
288 | char ip_buf[48]; | |
289 | u_int64_t t_rec=0; | |
290 | u_int64_t t_num=0; | |
291 | ||
292 | /* open the database (read-only) */ | |
293 | if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, DB_RDONLY, 0))) | |
294 | { | |
295 | /* Error opening the cache file.. tell user and exit */ | |
296 | fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i)); | |
297 | exit(1); | |
298 | } | |
299 | ||
300 | /* Create a cursor */ | |
301 | if ( dns_db->cursor(dns_db, NULL, &cursorp, 0) ) | |
302 | { | |
303 | fprintf(stderr,"Error: Unable to create cursor!\n"); | |
304 | exit(1); | |
305 | } | |
306 | ||
307 | /* get our runtime for TTL calculations */ | |
308 | time(&runtime); | |
309 | ||
310 | if (verbose) | |
311 | { | |
312 | printf("Webalizer DNS Cache file listing generated %s\n",ctime(&runtime)); | |
313 | printf("IP Address TTL Age Hostname\n"); | |
314 | printf("--------------- ------------- ------------------------" \ | |
315 | "-----------------------\n"); | |
316 | } | |
317 | ||
318 | /* initalize data areas */ | |
319 | memset(&q, 0, sizeof(DBT)); | |
320 | memset(&r, 0, sizeof(DBT)); | |
321 | memset(&dns_rec, 0, sizeof(struct dnsRec)); | |
322 | ||
323 | /* Loop through database */ | |
324 | while (!cursorp->c_get(cursorp, &q, &r, DB_NEXT)) | |
325 | { | |
326 | /* got a record */ | |
327 | t_rec++; | |
328 | memset(ip_buf, 0, sizeof(ip_buf)); | |
329 | strncpy(ip_buf, q.data, (q.size>47)?47:q.size); /* save IP address */ | |
330 | memcpy(&dns_rec, r.data, r.size); | |
331 | if (dns_rec.numeric) t_num++; | |
332 | printf("%-15s [%s] %s\n",ip_buf, | |
333 | (dns_rec.timeStamp)? | |
334 | ttl_age(runtime, dns_rec.timeStamp): | |
335 | "-permanent-", | |
336 | dns_rec.hostName); | |
337 | ||
338 | /* done, clear for next rec */ | |
339 | memset(&q, 0, sizeof(DBT)); | |
340 | memset(&r, 0, sizeof(DBT)); | |
341 | } | |
342 | ||
343 | if (verbose) | |
344 | { | |
345 | printf("------------------------------------------------------" \ | |
346 | "-----------------------\n"); | |
347 | printf("Filename: %s (%llu records)\n",in_file, t_rec); | |
348 | } | |
349 | } | |
350 | ||
351 | /*********************************************/ | |
352 | /* PURGE_CACHE - Purge cache of expired recs */ | |
353 | /*********************************************/ | |
354 | ||
355 | void purge_cache() | |
356 | { | |
357 | int i; | |
358 | char ip_buf[48]; | |
359 | u_int64_t age=0; | |
360 | u_int64_t t_in=0; | |
361 | u_int64_t t_out=0; | |
362 | u_int64_t t_exp=0; | |
363 | ||
364 | /* file control struct */ | |
365 | struct flock our_flock; | |
366 | ||
367 | if (verbose) printf("Purging records over %d days from '%s'\n", | |
368 | rec_ttl, in_file); | |
369 | ||
370 | /* open the input database (read-write) */ | |
371 | if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, 0, 0))) | |
372 | { | |
373 | /* Error opening the cache file.. tell user and exit */ | |
374 | fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i)); | |
375 | exit(1); | |
376 | } | |
377 | ||
378 | /* get file descriptor */ | |
379 | dns_db->fd(dns_db, &dns_fd); | |
380 | ||
381 | /* Try to lock the file */ | |
382 | our_flock.l_whence=SEEK_SET; | |
383 | our_flock.l_start=0; | |
384 | our_flock.l_len=0; | |
385 | our_flock.l_type=F_WRLCK; | |
386 | ||
387 | if (fcntl(dns_fd,F_SETLK,&our_flock) <0) | |
388 | { | |
389 | /* Error - can't lock file */ | |
390 | printf("Error: Unable to lock cache file: %s\n",strerror(errno)); | |
391 | exit(1); | |
392 | } | |
393 | ||
394 | /* Create a cursor */ | |
395 | if ( dns_db->cursor(dns_db, NULL, &cursorp, 0) ) | |
396 | { | |
397 | fprintf(stderr,"Error: Unable to create cursor!\n"); | |
398 | exit(1); | |
399 | } | |
400 | ||
401 | /* Try to create our output DB handle */ | |
402 | if ( db_create(&out_db, NULL, 0) ) | |
403 | { | |
404 | fprintf(stderr,"Error: unable to create output db handle!\n"); | |
405 | exit(1); | |
406 | } | |
407 | ||
408 | /* generate output filename */ | |
409 | memset(out_file, 0, sizeof(out_file)); | |
410 | sprintf(out_file, "%s.new", in_file); | |
411 | ||
412 | /* open the output database (read-write) */ | |
413 | if ((i=out_db->open(out_db, NULL, out_file, NULL, | |
414 | DB_HASH, DB_CREATE|DB_EXCL, 0644))) | |
415 | { | |
416 | /* Error opening the cache file.. tell user and exit */ | |
417 | fprintf(stderr,"Error: %s: %s\n",out_file,db_strerror(i)); | |
418 | exit(1); | |
419 | } | |
420 | ||
421 | /* get our runtime for TTL calculations */ | |
422 | time(&runtime); | |
423 | ||
424 | /* initalize data areas */ | |
425 | memset(&q, 0, sizeof(DBT)); | |
426 | memset(&r, 0, sizeof(DBT)); | |
427 | ||
428 | /* Loop through database */ | |
429 | while (!cursorp->c_get(cursorp, &q, &r, DB_NEXT)) | |
430 | { | |
431 | /* got a record */ | |
432 | t_in++; | |
433 | memcpy(&dns_rec, r.data, r.size); | |
434 | ||
435 | /* get record ttl age */ | |
436 | if (dns_rec.timeStamp==0) age=0; | |
437 | else age = runtime - dns_rec.timeStamp; | |
438 | ||
439 | if ( age <= (rec_ttl*86400) ) | |
440 | { | |
441 | /* Good record.. insert into new cache file */ | |
442 | if ( (i=out_db->put(out_db, NULL, &q, &r, 0)) != 0 ) | |
443 | { | |
444 | fprintf(stderr,"Error: db_put fail: %s!\n",db_strerror(i)); | |
445 | exit(1); | |
446 | } | |
447 | else t_out++; | |
448 | } | |
449 | else | |
450 | { | |
451 | /* Expired record */ | |
452 | t_exp++; | |
453 | if (verbose) | |
454 | { | |
455 | memset(ip_buf, 0, sizeof(ip_buf)); | |
456 | strncpy(ip_buf, q.data, (q.size>47)?47:q.size); | |
457 | printf("Purging %-16s [%s]\n",ip_buf, | |
458 | ttl_age(runtime,dns_rec.timeStamp)); | |
459 | } | |
460 | } | |
461 | ||
462 | /* done, clear for next rec */ | |
463 | memset(&q, 0, sizeof(DBT)); | |
464 | memset(&r, 0, sizeof(DBT)); | |
465 | } | |
466 | ||
467 | /* Successful exit! */ | |
468 | our_flock.l_type=F_UNLCK; | |
469 | fcntl(dns_fd, F_SETLK, &our_flock); | |
470 | dns_db->close(dns_db, 0); | |
471 | out_db->close(out_db, 0); | |
472 | ||
473 | /* rename files */ | |
474 | if (rename(out_file, in_file)) | |
475 | { | |
476 | fprintf(stderr,"Error renaming file: %s\n",strerror(errno)); | |
477 | exit(1); | |
478 | } | |
479 | ||
480 | if (verbose) | |
481 | printf("%llu of %llu records purged from '%s'\n",t_exp,t_in,in_file); | |
482 | } | |
483 | ||
484 | /*********************************************/ | |
485 | /* STAT_CACHE - Display cache stats/info */ | |
486 | /*********************************************/ | |
487 | ||
488 | void stat_cache() | |
489 | { | |
490 | /* Define some variables */ | |
491 | int i; | |
492 | time_t min_age=0; /* min/max TTL age in cache */ | |
493 | time_t max_age=0; | |
494 | u_int64_t t_rec=0; /* Various record totals */ | |
495 | u_int64_t t_err=0; | |
496 | u_int64_t t_name=0; | |
497 | u_int64_t t_num=0; | |
498 | u_int64_t t_perm=0; | |
499 | u_int64_t t_old=0; | |
500 | time_t age; | |
501 | ||
502 | /* open the database (read-only) */ | |
503 | if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, DB_RDONLY, 0))) | |
504 | { | |
505 | /* Error opening the cache file.. tell user and exit */ | |
506 | fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i)); | |
507 | exit(1); | |
508 | } | |
509 | ||
510 | /* Create a cursor */ | |
511 | if ( dns_db->cursor(dns_db, NULL, &cursorp, 0) ) | |
512 | { | |
513 | fprintf(stderr,"Error: Unable to create cursor!\n"); | |
514 | exit(1); | |
515 | } | |
516 | ||
517 | /* get our runtime for TTL calculations */ | |
518 | time(&runtime); | |
519 | ||
520 | /* initalize data areas */ | |
521 | memset(&q, 0, sizeof(DBT)); | |
522 | memset(&r, 0, sizeof(DBT)); | |
523 | memset(&dns_rec, 0, sizeof(struct dnsRec)); | |
524 | ||
525 | /* Loop through database */ | |
526 | while (!cursorp->c_get(cursorp, &q, &r, DB_NEXT)) | |
527 | { | |
528 | t_rec++; /* add to total */ | |
529 | if (r.size >= sizeof(dns_rec)) { t_err++; continue; } /* size error? */ | |
530 | memcpy(&dns_rec, r.data, r.size); /* get record */ | |
531 | if (dns_rec.numeric) t_num++; else t_name++; /* resolved? */ | |
532 | ||
533 | if (dns_rec.timeStamp!=0) /* permanent? */ | |
534 | { | |
535 | age=runtime-dns_rec.timeStamp; /* calc age */ | |
536 | if ((age < min_age) || (t_rec==1) ) min_age=age; /* min/max age */ | |
537 | if ( age > max_age ) max_age=age; /* if not perm */ | |
538 | if ( age > (rec_ttl*86400)) t_old++; /* purgable? */ | |
539 | } | |
540 | else t_perm++; /* inc counter */ | |
541 | ||
542 | /* done, clear for next rec */ | |
543 | memset(&q, 0, sizeof(DBT)); | |
544 | memset(&r, 0, sizeof(DBT)); | |
545 | } | |
546 | ||
547 | /* Print actual record counts */ | |
548 | printf("Report generated on: %s",ctime(&runtime)); | |
549 | printf("DNS Cache Filename : %s\n",in_file); | |
550 | ||
551 | printf("Total Records : %llu\n",t_rec); | |
552 | printf("Total Resolved : %llu\n",t_name); | |
553 | printf("Total Unresolved : %llu\n",t_num); | |
554 | printf("Total Permanent : %llu\n",t_perm); | |
555 | printf("Newest Record age : %s\n",ttl_age(min_age,0)); | |
556 | printf("Oldest Record age : %s\n",ttl_age(max_age,0)); | |
557 | printf("Total over %02d days : %llu\n",rec_ttl,t_old); | |
558 | if (t_err) printf("Record Size Errors : %llu\n",t_err); | |
559 | printf("\n"); | |
560 | } | |
561 | ||
562 | /*********************************************/ | |
563 | /* FIND_REC - Find IP record in cache */ | |
564 | /*********************************************/ | |
565 | ||
566 | void find_rec() | |
567 | { | |
568 | int i; | |
569 | char ip_buf[48]; | |
570 | ||
571 | /* open the database (read-only) */ | |
572 | if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, DB_RDONLY, 0))) | |
573 | { | |
574 | /* Error opening the cache file.. tell user and exit */ | |
575 | fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i)); | |
576 | exit(1); | |
577 | } | |
578 | ||
579 | /* get our runtime for TTL calculations */ | |
580 | time(&runtime); | |
581 | ||
582 | /* initalize data areas */ | |
583 | memset(&q, 0, sizeof(DBT)); | |
584 | memset(&r, 0, sizeof(DBT)); | |
585 | memset(&dns_rec, 0, sizeof(struct dnsRec)); | |
586 | ||
587 | /* search the cache */ | |
588 | q.data = &addr; | |
589 | q.size = strlen(addr); | |
590 | if ( (i=dns_db->get(dns_db, NULL, &q, &r, 0)) == 0) | |
591 | { | |
592 | /* We found it! display info */ | |
593 | memset(ip_buf, 0, sizeof(ip_buf)); | |
594 | strncpy(ip_buf, q.data, (q.size>47)?47:q.size); /* save IP address */ | |
595 | memcpy(&dns_rec, r.data, r.size); | |
596 | if (verbose) | |
597 | { | |
598 | /* Verbose display */ | |
599 | printf("Address : %s\n",ip_buf); | |
600 | printf("Hostname : %s\n",dns_rec.hostName); | |
601 | printf("Resolved : %s\n",(dns_rec.numeric)?"No":"Yes"); | |
602 | if (dns_rec.timeStamp) | |
603 | { | |
604 | /* Not Permanent */ | |
605 | printf("Timestamp: %s",ctime(&dns_rec.timeStamp)); | |
606 | printf("TTL age : %s\n\n",ttl_age(runtime, dns_rec.timeStamp)); | |
607 | } | |
608 | else | |
609 | { | |
610 | printf("Timestamp: N/A\n"); | |
611 | printf("TTL age : Permanent\n"); | |
612 | } | |
613 | } | |
614 | else | |
615 | { | |
616 | /* Standard 1 line display */ | |
617 | printf("%-15s [%s] %s\n",ip_buf, | |
618 | (dns_rec.timeStamp)? | |
619 | ttl_age(runtime, dns_rec.timeStamp): | |
620 | "-permanent-", | |
621 | dns_rec.hostName); | |
622 | } | |
623 | } | |
624 | else | |
625 | { | |
626 | if (i==DB_NOTFOUND) | |
627 | printf("%s not found!\n",addr); | |
628 | else | |
629 | printf("Error: %s\n",db_strerror(i)); | |
630 | } | |
631 | } | |
632 | ||
633 | /*********************************************/ | |
634 | /* DEL_REC - Delete record from cache file */ | |
635 | /*********************************************/ | |
636 | ||
637 | void del_rec() | |
638 | { | |
639 | int i; | |
640 | char *cp; | |
641 | ||
642 | /* ensure we have addr string */ | |
643 | if (addr[0]!='\0') cp=addr; | |
644 | else | |
645 | { | |
646 | fprintf(stderr,"Error: No IP address specified!\n"); | |
647 | exit(1); | |
648 | } | |
649 | ||
650 | /* ensure IPv6 addresses are lowercase */ | |
651 | cp=addr; while (*cp!='\0') *cp++=tolower(*cp); | |
652 | ||
653 | /* open the database (read-write) */ | |
654 | if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, 0, 0))) | |
655 | { | |
656 | /* Error opening the cache file.. tell user and exit */ | |
657 | fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i)); | |
658 | exit(1); | |
659 | } | |
660 | ||
661 | /* initalize data areas */ | |
662 | memset(&q, 0, sizeof(DBT)); | |
663 | memset(&r, 0, sizeof(DBT)); | |
664 | memset(&dns_rec, 0, sizeof(struct dnsRec)); | |
665 | ||
666 | /* search the cache */ | |
667 | q.data = &addr; | |
668 | q.size = strlen(addr); | |
669 | ||
670 | /* Try to delete the record */ | |
671 | if ( (i=dns_db->del(dns_db, NULL, &q, 0)) ) | |
672 | { | |
673 | if (i==DB_NOTFOUND) | |
674 | { | |
675 | printf("%s not found in cache!\n",addr); | |
676 | exit(1); | |
677 | } | |
678 | else | |
679 | { | |
680 | fprintf(stderr,"Error: %s\n",db_strerror(i)); | |
681 | exit(1); | |
682 | } | |
683 | } | |
684 | dns_db->close(dns_db, 0); | |
685 | if (verbose) | |
686 | printf("%s sucessfully deleted from cache file\n",addr); | |
687 | } | |
688 | ||
689 | /*********************************************/ | |
690 | /* ADD_REC - Add record to cache file */ | |
691 | /*********************************************/ | |
692 | ||
693 | void add_rec() | |
694 | { | |
695 | int i; | |
696 | char *cp; | |
697 | ||
698 | /* ensure we have addr string */ | |
699 | if (addr[0]!='\0') cp=addr; | |
700 | else | |
701 | { | |
702 | fprintf(stderr,"Error: No IP address specified!\n"); | |
703 | exit(1); | |
704 | } | |
705 | ||
706 | /* and check size */ | |
707 | if (strlen(addr)>47) | |
708 | { | |
709 | fprintf(stderr,"Error: IP address too long!\n"); | |
710 | exit(1); | |
711 | } | |
712 | ||
713 | /* ensure everything is lowercase */ | |
714 | cp=addr; while (*cp!='\0') *cp++=tolower(*cp); | |
715 | if (name[0]!='\0') | |
716 | { | |
717 | cp=name; while (*cp!='\0') *cp++=tolower(*cp); | |
718 | } | |
719 | ||
720 | /* open the database (read-write) */ | |
721 | if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, 0, 0))) | |
722 | { | |
723 | /* Error opening the cache file.. tell user and exit */ | |
724 | fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i)); | |
725 | exit(1); | |
726 | } | |
727 | ||
728 | /* get our runtime for TTL calculations */ | |
729 | time(&runtime); | |
730 | ||
731 | /* initalize data areas */ | |
732 | memset(&q, 0, sizeof(DBT)); | |
733 | memset(&r, 0, sizeof(DBT)); | |
734 | memset(&dns_rec, 0, sizeof(struct dnsRec)); | |
735 | ||
736 | /* search the cache */ | |
737 | q.data = &addr; | |
738 | q.size = strlen(addr); | |
739 | if ( (i=dns_db->get(dns_db, NULL, &q, &r, 0)) == 0) | |
740 | { | |
741 | fprintf(stderr,"Error: %s already exists in cache!\n",addr); | |
742 | exit(1); | |
743 | } | |
744 | else | |
745 | { | |
746 | if (i!=DB_NOTFOUND) | |
747 | { | |
748 | fprintf(stderr,"Error: %s\n",db_strerror(i)); | |
749 | exit(1); | |
750 | } | |
751 | else | |
752 | { | |
753 | /* check hostname */ | |
754 | if (name[0]=='\0') | |
755 | strncpy(name,addr,strlen(addr)); | |
756 | ||
757 | /* check if perm */ | |
758 | if (rec_ttl==0) runtime=0; | |
759 | ||
760 | /* put it in the database */ | |
761 | if (db_put(addr, name, (strcmp(name,addr))?0:1, runtime)==0) | |
762 | dns_db->close(dns_db,0); | |
763 | if (verbose) | |
764 | printf("%s sucessfully added to cache file\n",addr); | |
765 | } | |
766 | } | |
767 | } | |
768 | ||
769 | /*********************************************/ | |
770 | /* CREATE_CACHE - Create a new cache file */ | |
771 | /*********************************************/ | |
772 | ||
773 | void create_cache() | |
774 | { | |
775 | int i; | |
776 | ||
777 | /* create the database */ | |
778 | if ((i=dns_db->open(dns_db,NULL,in_file,NULL, | |
779 | DB_HASH,DB_CREATE|DB_EXCL,0644))) | |
780 | { | |
781 | /* Error opening the cache file.. tell user and exit */ | |
782 | fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i)); | |
783 | exit(1); | |
784 | } | |
785 | dns_db->close(dns_db,0); | |
786 | if (verbose) printf("Cache file %s created successfully\n",in_file); | |
787 | } | |
788 | ||
789 | /*********************************************/ | |
790 | /* IMPORT_CACHE - import cache from tab file */ | |
791 | /*********************************************/ | |
792 | ||
793 | void import_cache() | |
794 | { | |
795 | int i, flag=0; | |
796 | u_int64_t t_rec=0; | |
797 | FILE *in_fp; | |
798 | char ip_buf[48]; | |
799 | char buffer[4096]; | |
800 | ||
801 | /* open the database (read-write) */ | |
802 | if (create) flag=DB_CREATE|DB_EXCL; | |
803 | if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, flag, 0644))) | |
804 | { | |
805 | /* Error opening the cache file.. tell user and exit */ | |
806 | fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i)); | |
807 | exit(1); | |
808 | } | |
809 | ||
810 | /* open our import file */ | |
811 | in_fp=fopen(out_file,"r"); | |
812 | if (in_fp) | |
813 | { | |
814 | while ((fgets(buffer,4096,in_fp)) != NULL) | |
815 | { | |
816 | memset(&dns_rec, 0, sizeof(dns_rec)); | |
817 | memset(&ip_buf, 0, sizeof(ip_buf)); | |
818 | i = sscanf(buffer,"%s\t%lu\t%d\t%s", | |
819 | ip_buf, | |
820 | &dns_rec.timeStamp, | |
821 | &dns_rec.numeric, | |
822 | dns_rec.hostName); | |
823 | ||
824 | if (ip_buf[0]=='#') continue; /* skip comments */ | |
825 | ||
826 | if (i!=4) | |
827 | { | |
828 | fprintf(stderr,"Error reading tab file %s\n",out_file); | |
829 | exit(1); | |
830 | } | |
831 | ||
832 | t_rec++; /* bump totals */ | |
833 | ||
834 | /* put it in the database */ | |
835 | if (db_put(ip_buf, dns_rec.hostName, | |
836 | dns_rec.numeric, dns_rec.timeStamp)!=0) | |
837 | { | |
838 | fprintf(stderr,"Error inserting cache record:\n%s\n",buffer); | |
839 | exit(1); | |
840 | } | |
841 | } | |
842 | } | |
843 | else fprintf(stderr,"Error: File not found: %s\n",out_file); | |
844 | dns_db->close(dns_db,0); | |
845 | ||
846 | if (verbose) printf("%llu records imported into '%s' from file '%s'\n", | |
847 | t_rec, in_file, out_file); | |
848 | } | |
849 | ||
850 | /*********************************************/ | |
851 | /* EXPORT_CACHE - export cache to tab file */ | |
852 | /*********************************************/ | |
853 | ||
854 | void export_cache() | |
855 | { | |
856 | int i; | |
857 | u_int64_t t_rec=0; | |
858 | char ip_buf[48]; | |
859 | FILE *out_fp; | |
860 | struct stat out_stat; | |
861 | ||
862 | /* make sure files are different! */ | |
863 | if (!strcmp(in_file,out_file)) | |
864 | { | |
865 | fprintf(stderr,"Error: Bad export filename: %s\n",out_file); | |
866 | exit(1); | |
867 | } | |
868 | ||
869 | /* open the database (read-only) */ | |
870 | if ((i=dns_db->open(dns_db, NULL, in_file, NULL, DB_HASH, DB_RDONLY, 0))) | |
871 | { | |
872 | /* Error opening the cache file.. tell user and exit */ | |
873 | fprintf(stderr,"Error: %s: %s\n",in_file,db_strerror(i)); | |
874 | exit(1); | |
875 | } | |
876 | ||
877 | /* Create a cursor */ | |
878 | if ( dns_db->cursor(dns_db, NULL, &cursorp, 0) ) | |
879 | { | |
880 | fprintf(stderr,"Error: Unable to create cursor!\n"); | |
881 | exit(1); | |
882 | } | |
883 | ||
884 | /* stat output file */ | |
885 | if ( !(lstat(out_file, &out_stat)) ) | |
886 | { | |
887 | /* check if the file is a symlink */ | |
888 | if ( S_ISLNK(out_stat.st_mode) ) | |
889 | { | |
890 | fprintf(stderr,"%s %s\n","Error: File is a symlink:",out_file); | |
891 | exit(1); | |
892 | } | |
893 | } | |
894 | ||
895 | /* open output file */ | |
896 | if ( (out_fp=fopen(out_file,"w")) == NULL) | |
897 | { | |
898 | fprintf(stderr,"%s %s\n","Error: Cannot create file:",out_file); | |
899 | exit(1); | |
900 | } | |
901 | ||
902 | /* initalize data areas */ | |
903 | memset(&q, 0, sizeof(DBT)); | |
904 | memset(&r, 0, sizeof(DBT)); | |
905 | memset(&dns_rec, 0, sizeof(struct dnsRec)); | |
906 | ||
907 | /* Loop through database */ | |
908 | while (!cursorp->c_get(cursorp, &q, &r, DB_NEXT)) | |
909 | { | |
910 | /* got a record */ | |
911 | t_rec++; | |
912 | memset(ip_buf, 0, sizeof(ip_buf)); | |
913 | strncpy(ip_buf, q.data, (q.size>47)?47:q.size); /* save IP address */ | |
914 | memcpy(&dns_rec, r.data, r.size); | |
915 | ||
916 | /* Print out tab delimited line */ | |
917 | /* Format: IP timestamp numeric hostname */ | |
918 | fprintf(out_fp,"%s\t%lu\t%d\t%s\n", | |
919 | ip_buf,dns_rec.timeStamp, | |
920 | dns_rec.numeric, | |
921 | dns_rec.hostName); | |
922 | ||
923 | /* done, clear for next rec */ | |
924 | memset(&q, 0, sizeof(DBT)); | |
925 | memset(&r, 0, sizeof(DBT)); | |
926 | } | |
927 | dns_db->close(dns_db,0); | |
928 | fclose(out_fp); | |
929 | ||
930 | if (verbose) printf("%llu records exported from '%s' to file '%s'\n", | |
931 | t_rec, in_file, out_file); | |
932 | } | |
933 | ||
934 | /*********************************************/ | |
935 | /* DB_PUT - put key/val in the cache db */ | |
936 | /*********************************************/ | |
937 | ||
938 | static int db_put(char *key, char *value, int numeric, time_t ttl) | |
939 | { | |
940 | ||
941 | /* dnsRecord structure used in database */ | |
942 | struct dnsRecord | |
943 | { | |
944 | time_t timeStamp; /* Timestamp of resolv data */ | |
945 | int numeric; /* 0: Name, 1: IP-address */ | |
946 | char hostName[1]; /* Hostname buffer (variable) */ | |
947 | }; | |
948 | ||
949 | int i; | |
950 | DBT k, v; | |
951 | struct dnsRecord *recPtr = NULL; | |
952 | int nameLen = strlen(value)+1; | |
953 | ||
954 | /* Align to multiple of eight bytes */ | |
955 | int recSize = (sizeof(struct dnsRecord)+nameLen+7) & ~0x7; | |
956 | ||
957 | /* make sure we have a db ;) */ | |
958 | if(dns_db) | |
959 | { | |
960 | if((recPtr = calloc(1, recSize))) | |
961 | { | |
962 | recPtr->timeStamp = ttl; | |
963 | recPtr->numeric = numeric; | |
964 | memcpy(&recPtr->hostName, value, nameLen); | |
965 | memset(&k, 0, sizeof(k)); | |
966 | memset(&v, 0, sizeof(v)); | |
967 | ||
968 | k.data = key; | |
969 | k.size = strlen(key); | |
970 | ||
971 | v.size = recSize; | |
972 | v.data = recPtr; | |
973 | ||
974 | if ( (i=dns_db->put(dns_db, NULL, &k, &v, 0)) != 0 ) | |
975 | fprintf(stderr,"Error: db_put fail: %s!\n",db_strerror(i)); | |
976 | free(recPtr); | |
977 | } | |
978 | else return 1; | |
979 | } | |
980 | else return 1; | |
981 | return i; | |
982 | } | |
983 | #endif /* USE_DNS */ |