Imported Debian patch 2.23.05-1
[hcoop/zz_old/debian/webalizer.git] / dns_resolv.c
1 /*
2 webalizer - a web server log analysis program
3
4 Copyright (C) 1997-2011 Bradford L. Barrett
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version, and provided that the above
10 copyright and permission notice is included with all distributed
11 copies of this or derived software.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21
22 */
23
24 /*********************************************/
25 /* STANDARD INCLUDES */
26 /*********************************************/
27
28 #include <time.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <unistd.h> /* normal stuff */
34 #include <ctype.h>
35 #include <sys/utsname.h>
36 #include <zlib.h>
37
38 /* ensure sys/types */
39 #ifndef _SYS_TYPES_H
40 #include <sys/types.h>
41 #endif
42
43 /* Need socket header? */
44 #ifdef HAVE_SYS_SOCKET_H
45 #include <sys/socket.h>
46 #endif
47
48 /* some systems need this */
49 #ifdef HAVE_MATH_H
50 #include <math.h>
51 #endif
52
53 #ifdef USE_DNS /* skip everything in this file if no DNS */
54
55 #include <netinet/in.h> /* include stuff we need for dns lookups, */
56 #include <arpa/inet.h> /* DB access, file control, etc... */
57 #include <fcntl.h>
58 #include <netdb.h> /* ensure getaddrinfo/getnameinfo */
59 #include <signal.h>
60 #include <sys/signal.h>
61 #include <sys/stat.h>
62 #include <sys/time.h>
63 #include <sys/wait.h>
64 #include <db.h> /* DB header ****************/
65 #include "webalizer.h" /* main header */
66 #include "lang.h" /* language declares */
67 #include "hashtab.h" /* hash table functions */
68 #include "parser.h" /* log parser functions */
69 #include "dns_resolv.h" /* our header */
70
71 /* local data */
72
73 DB *dns_db = NULL; /* DNS cache database */
74 int dns_fd = 0;
75
76 DB *geo_db = NULL; /* GeoDB database */
77 DBC *geo_dbc = NULL; /* GeoDB database cursor */
78
79 struct dns_child child[MAXCHILD]; /* DNS child pipe data */
80
81 DNODEPTR host_table[MAXHASH]; /* hostname/ip hash table */
82
83 char buffer[BUFSIZE]; /* log file record buffer */
84 char tmp_buf[BUFSIZE]; /* used to temp save above */
85 struct utsname system_info; /* system info structure */
86
87 int raiseSigChild = 1;
88
89 time_t runtime;
90 time_t start_time, end_time;
91 float temp_time;
92
93 extern char *our_gzgets(void *, char *, int); /* external our_gzgets func */
94
95 /* internal function prototypes */
96
97 static void process_list(DNODEPTR);
98 static void sigChild(int);
99 static void db_put(char *, char *, int);
100 void set_fl(int, int);
101 void clr_fl(int, int);
102 int iptype(char *, unsigned char *);
103
104 /*********************************************/
105 /* RESOLVE_DNS - lookup IP in cache */
106 /*********************************************/
107
108 void resolve_dns(struct log_struct *log_rec)
109 {
110 DBT query, response;
111 int i;
112 /* aligned dnsRecord to prevent Solaris from doing a dump */
113 /* (not found in debugger, as it can dereference it :( */
114 struct dnsRecord alignedRecord;
115
116 if (!dns_db) return; /* ensure we have a dns db */
117
118 memset(&query, 0, sizeof(query));
119 memset(&response, 0, sizeof(response));
120 query.data = log_rec->hostname;
121 query.size = strlen(log_rec->hostname);
122
123 if (debug_mode) fprintf(stderr,"Checking %s...", log_rec->hostname);
124
125 if ( (i=dns_db->get(dns_db, NULL, &query, &response, 0)) == 0)
126 {
127 memcpy(&alignedRecord, response.data, sizeof(struct dnsRecord));
128 strncpy (log_rec->hostname,
129 ((struct dnsRecord *)response.data)->hostName,
130 MAXHOST);
131 log_rec->hostname[MAXHOST-1]=0;
132 if (debug_mode)
133 fprintf(stderr," found: %s (%ld)\n",
134 log_rec->hostname, alignedRecord.timeStamp);
135 }
136 else /* not found or error occured during get */
137 {
138 if (debug_mode)
139 {
140 if (i==DB_NOTFOUND) fprintf(stderr," not found\n");
141 else fprintf(stderr," error (%d)\n",i);
142 }
143 }
144 }
145
146 /*********************************************/
147 /* DNS_RESOLVER - read log and lookup IP's */
148 /*********************************************/
149
150 int dns_resolver(void *log_fp)
151 {
152 DNODEPTR h_entries;
153 DNODEPTR l_list = NULL;
154
155 int i;
156 int save_verbose=verbose;
157
158 u_int64_t listEntries = 0;
159
160 struct sigaction sigPipeAction;
161 struct stat dbStat;
162 /* aligned dnsRecord to prevent Solaris from doing a dump */
163 /* (not found in debugger, as it can dereference it :( */
164 struct dnsRecord alignedRecord;
165
166 struct flock tmp_flock;
167
168 tmp_flock.l_whence=SEEK_SET; /* default flock fields */
169 tmp_flock.l_start=0;
170 tmp_flock.l_len=0;
171 tmp_flock.l_pid=0;
172
173 time(&runtime);
174
175 /* get processing start time */
176 start_time = time(NULL);
177
178 /* minimal sanity check on it */
179 if(stat(dns_cache, &dbStat) < 0)
180 {
181 if(errno != ENOENT)
182 {
183 dns_cache=NULL;
184 dns_db=NULL; return 0; /* disable cache */
185 }
186 }
187 else
188 {
189 if(!dbStat.st_size) /* bogus file, probably from a crash */
190 {
191 unlink(dns_cache); /* remove it so we can recreate... */
192 }
193 }
194
195 /* open cache file */
196 if ( (db_create(&dns_db, NULL, 0) != 0) ||
197 (dns_db->open(dns_db, NULL,
198 dns_cache, NULL, DB_HASH,
199 DB_CREATE, 0644) != 0) )
200 {
201 /* Error: Unable to open DNS cache file <filename> */
202 if (verbose) fprintf(stderr,"%s %s\n",msg_dns_nodb,dns_cache);
203 dns_cache=NULL;
204 dns_db=NULL;
205 return 0; /* disable cache */
206 }
207
208 /* get file descriptor */
209 dns_db->fd(dns_db, &dns_fd);
210
211 tmp_flock.l_type=F_WRLCK; /* set read/write lock type */
212 if (fcntl(dns_fd,F_SETLK,&tmp_flock) < 0) /* and barf if we cant lock */
213 {
214 /* Error: Unable to lock DNS cache file <filename> */
215 if (verbose) fprintf(stderr,"%s %s\n",msg_dns_nolk,dns_cache);
216 dns_db->close(dns_db, 0);
217 dns_cache=NULL;
218 dns_db=NULL;
219 return 0; /* disable cache */
220 }
221
222 /* Setup signal handlers */
223 sigPipeAction.sa_handler = SIG_IGN;
224 sigPipeAction.sa_flags = SA_RESTART;
225 sigemptyset(&sigPipeAction.sa_mask);
226
227 sigaction(SIGPIPE, &sigPipeAction, NULL);
228
229 /* disable warnings/errors for this run... */
230 verbose=0;
231
232 /* Main loop to read log records (either regular or zipped) */
233 while ( (gz_log)?(our_gzgets((void *)log_fp,buffer,BUFSIZE) != Z_NULL):
234 (fgets(buffer,BUFSIZE,log_fname?(FILE *)log_fp:stdin) != NULL))
235 {
236 if (strlen(buffer) == (BUFSIZE-1))
237 {
238 /* get the rest of the record */
239 while ( (gz_log)?(our_gzgets((void *)log_fp,buffer,BUFSIZE)!=Z_NULL):
240 (fgets(buffer,BUFSIZE,log_fname?(FILE *)log_fp:stdin)!=NULL))
241 {
242 if (strlen(buffer) < BUFSIZE-1) break;
243 }
244 continue; /* go get next record if any */
245 }
246
247 strcpy(tmp_buf, buffer); /* save buffer in case of error */
248 if(parse_record(buffer)) /* parse the record */
249 {
250 struct addrinfo hints, *ares;
251 memset(&hints, 0, sizeof(hints));
252 hints.ai_family = AF_UNSPEC;
253 hints.ai_socktype = SOCK_STREAM;
254 hints.ai_flags = AI_NUMERICHOST;
255 if (0 == getaddrinfo(log_rec.hostname, "0", &hints, &ares))
256 {
257 DBT q, r;
258 memset(&q, 0, sizeof(q));
259 memset(&r, 0, sizeof(r));
260 q.data = log_rec.hostname;
261 q.size = strlen(log_rec.hostname);
262
263 /* Check if we have it in DB */
264 if ( (i=dns_db->get(dns_db, NULL, &q, &r, 0)) == 0 )
265 {
266 /* have a record for this address */
267 memcpy(&alignedRecord, r.data, sizeof(struct dnsRecord));
268 if (alignedRecord.timeStamp != 0)
269 /* If it's not permanent, check if it's TTL has expired */
270 if ( (runtime-alignedRecord.timeStamp ) > (86400*cache_ttl) )
271 put_dnode(log_rec.hostname, ares->ai_addr,
272 ares->ai_addrlen, host_table);
273 }
274 else
275 {
276 if (i==DB_NOTFOUND)
277 put_dnode(log_rec.hostname, ares->ai_addr,
278 ares->ai_addrlen, host_table);
279 }
280 freeaddrinfo(ares);
281 }
282 }
283 }
284 verbose = save_verbose; /* restore verbosity level... */
285
286 listEntries = 0;
287
288 /* build our linked list l_list */
289 for(i=0;i < MAXHASH; i++)
290 {
291 for(h_entries=host_table[i]; h_entries ; h_entries = h_entries->next)
292 {
293 h_entries->llist = l_list;
294 l_list = h_entries;
295 listEntries++;
296 }
297 }
298
299 if(!l_list)
300 {
301 /* No valid addresses found... */
302 if (verbose>1) printf("%s\n",msg_dns_none);
303 tmp_flock.l_type=F_UNLCK;
304 fcntl(dns_fd, F_SETLK, &tmp_flock);
305 dns_db->close(dns_db, 0);
306 return 0;
307 }
308
309 /* process our list now... */
310 process_list(l_list);
311
312 /* get processing end time */
313 end_time = time(NULL);
314
315 /* display DNS processing statistics */
316 if (time_me || (verbose>1))
317 {
318 if (verbose<2 && time_me) printf("DNS: ");
319 printf("%llu %s ",listEntries, msg_addresses);
320
321 /* total processing time in seconds */
322 temp_time = difftime(end_time,start_time);
323 if (temp_time==0) temp_time=1;
324 printf("%s %.0f %s", msg_in, temp_time, msg_seconds);
325
326 /* calculate records per second */
327 if (temp_time)
328 i=( (int)((float)listEntries/temp_time) );
329 else i=0;
330
331 if ( (i>0) && (i<=listEntries) ) printf(", %d/sec\n", i);
332 else printf("\n");
333 }
334
335 /* processing done, exit */
336 tmp_flock.l_type=F_UNLCK;
337 fcntl(dns_fd, F_SETLK, &tmp_flock);
338 dns_db->close(dns_db, 0);
339 return 0;
340
341 }
342
343 /*********************************************/
344 /* PROCESS_LIST - do the resoluton... */
345 /*********************************************/
346
347 static void process_list(DNODEPTR l_list)
348 {
349 DNODEPTR trav;
350
351 char child_buf[MAXHOST+3-((unsigned long)&trav+sizeof(trav))%3];
352 char dns_buf[MAXHOST];
353 int i;
354 int pid;
355 int nof_children = 0;
356 fd_set rd_set;
357 char hbuf[NI_MAXHOST];
358
359 struct sigaction sigChildAction;
360
361 sigChildAction.sa_handler = sigChild;
362 sigChildAction.sa_flags = SA_NOCLDSTOP|SA_RESTART;
363 sigemptyset(&sigChildAction.sa_mask);
364
365 raiseSigChild = 0;
366
367 sigaction(SIGCHLD, &sigChildAction, NULL);
368
369 /* fire up our child processes */
370 for(i=0; i < dns_children; i++)
371 {
372 if(pipe(child[i].inpipe))
373 {
374 if (verbose) fprintf(stderr,"INPIPE creation error");
375 return; /* exit(1) */
376 }
377
378 if(pipe(child[i].outpipe))
379 {
380 if (verbose) fprintf(stderr,"OUTPIPE creation error");
381 return; /* exit(1); */
382 }
383
384 /* fork it off */
385 switch(pid=fork())
386 {
387 case -1:
388 {
389 if (verbose) fprintf(stderr,"FORK error");
390 return; /* exit(1); */
391 }
392
393 case 0: /* Child */
394 {
395 int size;
396
397 close(child[i].inpipe[0]);
398 close(child[i].outpipe[1]);
399
400 /* get struct sockaddr_storage here */
401 while((size = read(child[i].outpipe[0], child_buf, MAXHOST)))
402 {
403 if(size < 0)
404 {
405 perror("read error");
406 exit(1);
407 }
408 else
409 {
410 /* Clear out our buffer */
411 memset(hbuf,0,NI_MAXHOST);
412
413 if(0 == getnameinfo((struct sockaddr*)child_buf, size,
414 hbuf, sizeof(hbuf), NULL, 0, NI_NAMEREQD))
415 {
416 /* must be at least 4 chars */
417 if (strlen(hbuf)>3)
418 {
419 /* If long hostname, take max domain name part */
420 if ((size = strlen(hbuf)) > MAXHOST-2)
421 strcpy(child_buf,(hbuf+(size-MAXHOST+1)));
422 else strcpy(child_buf, hbuf);
423 size = strlen(child_buf);
424 }
425 else
426 {
427 if (debug_mode)
428 printf("Child %d getnameinfo bad hbuf!\n",i);
429 }
430 }
431 else
432 {
433 if(debug_mode)
434 printf("Child %d getnameinfo failed!\n",i);
435 }
436
437 if (write(child[i].inpipe[1], child_buf, size) == -1)
438 {
439 perror("write error");
440 exit(1);
441 }
442 }
443 }
444 close(child[i].inpipe[1]);
445 close(child[i].outpipe[0]);
446
447 if(debug_mode)
448 printf( "Child %d got closed input, shutting down\n", i);
449
450 fflush(stdout);
451 exit(0);
452 } /* case 0 */
453
454 default:
455 {
456 child[i].pid = pid;
457 child[i].flags = DNS_CHILD_READY|DNS_CHILD_RUNNING;
458 nof_children++;
459 close(child[i].inpipe[1]);
460 close(child[i].outpipe[0]);
461
462 set_fl(child[i].inpipe[0], O_NONBLOCK);
463 }
464 }
465 }
466
467 trav = l_list;
468
469 while(nof_children)
470 {
471 static struct timeval selectTimeval;
472 int res;
473 int max_fd;
474
475 FD_ZERO(&rd_set);
476 max_fd = 0;
477
478 if(raiseSigChild)
479 {
480 int pid;
481
482 while((pid = waitpid(-1, NULL, WNOHANG)) > 0)
483 {
484 for(i=0;i<dns_children;i++)
485 {
486 if(child[i].pid == pid)
487 {
488 child[i].pid = 0;
489 child[i].flags &= ~(DNS_CHILD_READY|DNS_CHILD_RUNNING);
490 nof_children--;
491
492 if(debug_mode)
493 printf("Reaped Child %d\n", pid);
494
495 break;
496 }
497 }
498 }
499 raiseSigChild--;
500 continue; /* while, nof children has just changed */
501 }
502
503 for(i=0;i<dns_children;i++)
504 {
505 if(child[i].flags & DNS_CHILD_RUNNING) /* Child is running */
506 {
507 if(child[i].flags & DNS_CHILD_READY)
508 {
509 child[i].flags &= ~DNS_CHILD_READY;
510
511 if(trav) /* something to resolve */
512 {
513 if (write(child[i].outpipe[1], &trav->addr,
514 trav->addrlen) != -1)
515 {
516 /* We will watch this child */
517 child[i].cur = trav;
518 FD_SET(child[i].inpipe[0], &rd_set);
519 max_fd = MAX(max_fd, child[i].inpipe[0]);
520
521 if(debug_mode)
522 printf("Giving %d bytes to Child %d\n",
523 trav->addrlen, i);
524
525 trav = trav->llist;
526 }
527 else /* write error */
528 {
529 if(errno != EINTR) /* Could be a signal */
530 {
531 perror("Could not write to pipe");
532 close(child[i].outpipe[1]); /* kill */
533 child[i].flags &= ~DNS_CHILD_RUNNING; /* child */
534 }
535 }
536 }
537 else /* List is complete */
538 {
539 close(child[i].outpipe[1]); /* Go away */
540 child[i].flags &= ~DNS_CHILD_RUNNING; /* Child is dead */
541 }
542 }
543 else
544 {
545 /* Look, the busy child... */
546 FD_SET(child[i].inpipe[0], &rd_set);
547 max_fd = MAX(max_fd, child[i].inpipe[0]);
548 }
549 }
550 }
551
552 selectTimeval.tv_sec = 5; /* This stuff ticks in 5 second intervals */
553 selectTimeval.tv_usec = 0;
554
555 switch(res = select(max_fd+1, &rd_set, NULL, NULL, &selectTimeval))
556 {
557 case -1:
558 {
559 if(errno != EINTR) /* Could be a signal */
560 perror("Error in select");
561
562 break;
563 }
564
565 case 0: /* Timeout, just fall once through the child loop */
566 {
567 if(debug_mode)
568 printf("tick\n");
569
570 break;
571 }
572
573 default:
574 {
575 for(i=0; i< dns_children;i++)
576 {
577 if(!res) /* All file descriptors done */
578 break;
579
580 if(FD_ISSET(child[i].inpipe[0], &rd_set))
581 {
582 int size;
583
584 res--; /* One less... */
585
586 switch (size=read(child[i].inpipe[0], dns_buf, MAXHOST))
587 {
588 case -1:
589 {
590 if(errno != EINTR)
591 perror("Could not read from pipe");
592 break;
593 }
594 case 0:
595 {
596 /* EOF. Child has closed Pipe. It shouldn't have */
597 /* done that, could be an error or something. */
598 /* Reap it */
599 close(child[i].outpipe[1]);
600 child[i].flags &= ~DNS_CHILD_RUNNING;
601
602 if(debug_mode)
603 printf("Child %d wants to be reaped\n", i);
604
605 break;
606 }
607
608 default:
609 {
610 dns_buf[size] = '\0';
611 if( strlen(dns_buf) > 1 &&
612 memcmp(dns_buf, &(child[i].cur->addr),
613 sizeof(child[i].cur->addr)))
614 {
615 if(debug_mode)
616 printf("Child %d Got a result: %s -> %s\n",
617 i, child[i].cur->string, dns_buf);
618 db_put(child[i].cur->string, dns_buf, 0);
619 }
620 else
621 {
622 if(debug_mode)
623 printf("Child %d could not resolve: %s (%s)\n",
624 i, child[i].cur->string,
625 (cache_ips)?"cache":"no cache");
626 if (cache_ips) /* Cache non-resolved? */
627 db_put(child[i].cur->string,
628 child[i].cur->string,1);
629 }
630
631 if(debug_mode)
632 printf("Child %d back in task pool\n", i);
633
634 /* Child is back in the task pool */
635 child[i].flags |= DNS_CHILD_READY;
636 break;
637 }
638 }
639 }
640 }
641 break;
642 }
643 }
644 }
645 return;
646 }
647
648 /*********************************************/
649 /* SET_FL - set flag on pipe FD */
650 /*********************************************/
651
652 void set_fl(int fd, int flags)
653 {
654 int val;
655
656 /* get current flags */
657 if ((val=fcntl(fd, F_GETFL, 0)) < 0)
658 if (verbose) fprintf(stderr,"set_fl F_GETFL error\n");
659
660 /* set them */
661 val |= flags;
662
663 /* and write them back */
664 if ((val=fcntl(fd, F_SETFL, val)) < 0)
665 if (verbose) fprintf(stderr,"set_fl F_SETFL error\n");
666 }
667
668 /*********************************************/
669 /* CLR_FL - clear flag on pipe FD */
670 /*********************************************/
671
672 void clr_fl(int fd, int flags)
673 {
674 int val;
675
676 /* Get current flags */
677 if ((val=fcntl(fd, F_GETFL, 0)) < 0)
678 if (verbose) fprintf(stderr,"clr_fl F_GETFL error\n");
679
680 /* set them */
681 val &= ~flags;
682
683 /* and write them back */
684 if ((val=fcntl(fd, F_SETFL, val)) < 0)
685 if (verbose) fprintf(stderr,"clr_fl F_SETFL error\n");
686 }
687
688 /*********************************************/
689 /* DB_PUT - put key/val in the cache db */
690 /*********************************************/
691
692 static void db_put(char *key, char *value, int numeric)
693 {
694 DBT k, v;
695 char *cp;
696 struct dnsRecord *recPtr = NULL;
697 int nameLen = strlen(value)+1;
698
699 /* Align to multiple of eight bytes */
700 int recSize = (sizeof(struct dnsRecord)+nameLen+7) & ~0x7;
701
702 /* make sure we have a db ;) */
703 if(dns_db)
704 {
705 if((recPtr = calloc(1, recSize)))
706 {
707 recPtr->timeStamp = runtime;
708 recPtr->numeric = numeric;
709 memcpy(&recPtr->hostName, value, nameLen);
710 memset(&k, 0, sizeof(k));
711 memset(&v, 0, sizeof(v));
712
713 /* Ensure all data is lowercase */
714 cp=key; while (*cp++!='\0') *cp=tolower(*cp);
715 cp=value; while (*cp++!='\0') *cp=tolower(*cp);
716
717 k.data = key;
718 k.size = strlen(key);
719
720 v.size = recSize;
721 v.data = recPtr;
722
723 if ( dns_db->put(dns_db, NULL, &k, &v, 0) != 0 )
724 if (verbose>1) fprintf(stderr,"db_put fail!\n");
725 free(recPtr);
726 }
727 }
728 }
729
730 /*********************************************/
731 /* SIGCHILD - raise our signal */
732 /*********************************************/
733
734 static void sigChild(int signum)
735 {
736 raiseSigChild++;
737 }
738
739 /*********************************************/
740 /* OPEN_CACHE - open our cache file RDONLY */
741 /*********************************************/
742
743 int open_cache()
744 {
745 struct stat dbStat;
746 struct flock tmp_flock;
747
748 tmp_flock.l_whence=SEEK_SET; /* default flock fields */
749 tmp_flock.l_start=0;
750 tmp_flock.l_len=0;
751 tmp_flock.l_pid=0;
752 tmp_flock.l_type=F_RDLCK;
753
754 /* double check filename was specified */
755 if(!dns_cache) { dns_db=NULL; return 0; }
756
757 /* minimal sanity check on it */
758 if(stat(dns_cache, &dbStat) < 0)
759 {
760 if(errno != ENOENT) return 0;
761 }
762 else
763 {
764 if(!dbStat.st_size) /* bogus file, probably from a crash */
765 {
766 unlink(dns_cache); /* remove it so we can recreate... */
767 }
768 }
769
770 /* open cache file */
771 if ( (db_create(&dns_db, NULL, 0) != 0) ||
772 (dns_db->open(dns_db, NULL,
773 dns_cache, NULL, DB_HASH,
774 DB_RDONLY, 0644) != 0) )
775 {
776 /* Error: Unable to open DNS cache file <filename> */
777 if (verbose) fprintf(stderr,"%s %s\n",msg_dns_nodb,dns_cache);
778 return 0; /* disable cache */
779 }
780
781 /* get file descriptor */
782 dns_db->fd(dns_db, &dns_fd);
783
784 /* Get shared lock on cache file */
785 if (fcntl(dns_fd, F_SETLK, &tmp_flock) < 0)
786 {
787 if (verbose) fprintf(stderr,"%s %s\n",msg_dns_nolk,dns_cache);
788 dns_db->close(dns_db, 0);
789 return 0;
790 }
791 return 1;
792 }
793
794 /*********************************************/
795 /* CLOSE_CACHE - close our RDONLY cache */
796 /*********************************************/
797
798 int close_cache()
799 {
800 struct flock tmp_flock;
801
802 tmp_flock.l_whence=SEEK_SET; /* default flock fields */
803 tmp_flock.l_start=0;
804 tmp_flock.l_len=0;
805 tmp_flock.l_pid=0;
806 tmp_flock.l_type=F_UNLCK;
807
808 /* clear lock and close cache file */
809 fcntl(dns_fd, F_SETLK, &tmp_flock);
810 dns_db->close(dns_db, 0);
811 return 1;
812 }
813
814 /*********************************************/
815 /* GEODB_OPEN - Open GeoDB database/cursor */
816 /*********************************************/
817
818 DB *geodb_open(char *dbname)
819 {
820 char buf[1025];
821
822 if (dbname==NULL)
823 snprintf(buf,sizeof(buf),"%s/GeoDB.dat",GEODB_LOC);
824 else
825 strncpy(buf,dbname,sizeof(buf)-1);
826 buf[sizeof(buf)-1]='\0';
827
828 /* create database thingie */
829 if ( db_create(&geo_db, NULL, 0) ) return NULL;
830
831 /* open the database */
832 if (geo_db->open(geo_db,NULL,buf,NULL,DB_BTREE,DB_RDONLY,0)) return NULL;
833
834 /* create our cursor */
835 if (geo_db->cursor(geo_db,NULL,&geo_dbc,0))
836 {
837 geo_db->close(geo_db,0);
838 return NULL;
839 }
840 /* all is well in the world */
841 return geo_db;
842 }
843
844 /*********************************************/
845 /* GEODB_VER - Get database version info */
846 /*********************************************/
847
848 char *geodb_ver(DB *db, char *str)
849 {
850 int i;
851 DBT k,v;
852 unsigned char x[16];
853
854 memset(&x, 0, sizeof(x));
855 memset(&k, 0, sizeof(k));
856 memset(&v, 0, sizeof(v));
857 k.data=&x;
858 k.size=sizeof(x);
859
860 i=geo_db->get(geo_db, NULL, &k, &v, 0);
861
862 if (i) strncpy(str, "Unknown", 8);
863 else strncpy(str, v.data+3, v.size-3);
864 return str;
865 }
866
867 /*********************************************/
868 /* GEODB_GET_CC - Get country code for IP */
869 /*********************************************/
870
871 char *geodb_get_cc(DB *db, char *ip, char *buf)
872 {
873 int i;
874 DBT k,v;
875 unsigned char addr[16];
876
877 memset(addr, 0, sizeof(addr));
878 strncpy(buf, "--", 3);
879
880 /* get IP address */
881 if (!iptype(ip, addr)) return buf;
882
883 /* kludge for IPv6 mapped IPv4 */
884 if (addr[0]==0 && addr[1]==0 && addr[2]==0) { addr[10]=0; addr[11]=0; }
885
886 /* kludge for IPv6 6to4 (RFC3056) */
887 if (addr[0]==0x20 && addr[1]==0x02)
888 {
889 memcpy(&addr[12],&addr[2],4);
890 memset(&addr,0,12);
891 }
892
893 memset(&k, 0, sizeof(k));
894 memset(&v, 0, sizeof(v));
895 k.data=&addr;
896 k.size=sizeof(addr);
897
898 i=geo_dbc->c_get(geo_dbc, &k, &v, DB_SET_RANGE);
899 if (!i) memcpy(buf, v.data, 2);
900 return buf;
901 }
902
903 /*********************************************/
904 /* GEODB_CLOSE - close GeoDB database */
905 /*********************************************/
906
907 void geodb_close(DB *db)
908 {
909 db->close(db,0);
910 }
911
912 /*********************************************/
913 /* IPTYPE - get IP type and format addr buf */
914 /*********************************************/
915
916 int iptype(char *ip, unsigned char *buf)
917 {
918 if (inet_pton(AF_INET6, ip, buf)>0) return 2;
919 if (inet_pton(AF_INET, ip, buf+12)>0) return 1;
920 else return 0;
921 }
922
923 #endif /* USE_DNS */