1 N [0-9]
2 O ({N}{1,3})
3
4 C [0-9A-Fa-f]
5 H ({C}{1,4})
6
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <sys/time.h>
10 #include <sys/stat.h>
11
12 #include <netinet/in.h>
13
14 #include <arpa/inet.h>
15 #include <arpa/nameser.h>
16
17 #include <ctype.h>
18 #include <errno.h>
19 #ifdef HAVE_MEMORY_H
20 #include <memory.h>
21 #endif
22 #include <netdb.h>
23 #include <resolv.h>
24 #include <setjmp.h>
25 #include <signal.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "gnuc.h"
32 #ifdef HAVE_OS_PROTO_H
33 #include "os-proto.h"
34 #endif
35
36 #include "nb_dns.h"
37 #include "setsignal.h"
38 #include "version.h"
39
40 #undef yywrap
41 #ifdef FLEX_SCANNER
42 #define YY_NO_INPUT
43 #define YY_NO_UNPUT
44 #endif
45
46 /* -x: Only print the ip addresses, one per line; -xx: include cidr width */
47 #define MATCH(isv6) if (xflag > 1) { \
48 echo(); \
49 BEGIN(WIDTH); \
50 } else if (xflag) { \
51 echo(); \
52 putchar('\n'); \
53 } else { \
54 convert(yytext, (isv6)); \
55 }
56 #define NOMATCH if (!xflag) { \
57 echo(); \
58 }
59
60 int doinfo;
61 int linemode; /* convert at most one entry per line */
62 #ifdef HAVE_ASYNC_DNS
63 int lookup_pass; /* true if lookup only */
64 #endif
65 int triedone;
66 int xflag; /* only print matches with no translation */
67
68 void convert(const char *, int);
69 void reportinfo(void);
70 int yywrap(void);
71 int yylex(void);
72
73 static inline void
echo()74 echo()
75 {
76 #ifdef HAVE_ASYNC_DNS
77 if (!lookup_pass)
78 #endif
79 (void)fwrite(yytext, yyleng, 1, yyout);
80 #ifdef SIGINFO
81 if (doinfo) {
82 reportinfo();
83 doinfo = 0;
84 }
85 #endif
86 }
87 #define ECHO echo()
88
89 %x WIDTH
90
91 %%
92
93 ::{O}(\.{O}){3} MATCH(1);
94 {O}(\.{O}){3} MATCH(0);
95
96 :(:{H}){1,7} MATCH(1);
97 {H}(:{H}){7} MATCH(1);
98 {H}:(:{H}){1,6} MATCH(1);
99 ({H}:){2}(:{H}){1,5} MATCH(1);
100 ({H}:){3}(:{H}){1,4} MATCH(1);
101 ({H}:){4}(:{H}){1,3} MATCH(1);
102 ({H}:){5}(:{H}){1,2} MATCH(1);
103 ({H}:){6}:{H} MATCH(1);
104
105 <WIDTH>"/"{N}+ {
106 /* Output width part of cidr and newline */
107 echo();
108 putchar('\n');
109 BEGIN(0);
110 }
111
112 <WIDTH>. {
113 /* No cdir width; output newline */
114 putchar('\n');
115 BEGIN(0);
116 }
117
118 ({O}\.){1,3} NOMATCH; /* anti-backtrack */
119 {O}((\.{O}){1,2}) NOMATCH; /* anti-backtrack */
120
121 /*
122 * Speedups: Avoid handling unmatched characters one at a time
123 * via the default echo rule.
124 */
125
126 {H}+ NOMATCH;
127 [^0-9A-Fa-f\n]+ NOMATCH;
128
129 [^0-9A-Fa-f\n]+\n {
130 NOMATCH;
131 triedone = 0;
132 }
133
134 <WIDTH>\n {
135 /* No cdir width; output newline */
136 putchar('\n');
137 triedone = 0;
138 BEGIN(0);
139 }
140
141 \n {
142 NOMATCH;
143 triedone = 0;
144 }
145
146 %%
147
148 /*
149 * Copyright (c) 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2009, 2013, 2014, 2016, 2017, 2020
150 * The Regents of the University of California. All rights reserved.
151 *
152 * Redistribution and use in source and binary forms, with or without
153 * modification, are permitted provided that the following conditions are met:
154 * * Redistributions of source code must retain the above copyright
155 * notice, this list of conditions and the following disclaimer.
156 * * Redistributions in binary form must reproduce the above copyright
157 * notice, this list of conditions and the following disclaimer in the
158 * documentation and/or other materials provided with the distribution.
159 * * Neither the name of the University nor the names of its contributors
160 * may be used to endorse or promote products derived from this software
161 * without specific prior written permission.
162 *
163 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
164 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
165 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
166 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
167 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
168 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
169 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
170 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
171 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
172 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
173 * SUCH DAMAGE.
174 */
175
176 #ifndef lint
177 static const char copyright[] __attribute__ ((unused)) =
178 "@(#) Copyright (c) 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2009, 2013, 2014, 2016, 2017, 2020\n\
179 The Regents of the University of California. All rights reserved.\n";
180 static const char rcsid[] __attribute__ ((unused)) =
181 "@(#) $Id: hf.l 221 2020-09-15 23:17:21Z leres $ (LBL)";
182 #endif
183
184 #define HSIZE 8192 /* must be a power of two */
185
186 struct htable {
187 char addr[NS_IN6ADDRSZ];
188 int af; /* address family */
189 int alen;
190 int state;
191 char *name; /* overloaded variable */
192 struct htable *next;
193 } htable[HSIZE];
194
195 #define STATE_FREE 0 /* not in use */
196 #define STATE_RAW 1 /* couldn't translate */
197 #define STATE_SENTPTR 2
198 #define STATE_HAVEPTR 3
199 #define STATE_SENTA 4
200 #define STATE_HAVEA 5
201
202 #define PLURAL(n) ((n) == 1 ? "" : "s")
203
204 int strip = 1; /* strip local domain when possible */
205 int lcase = 1; /* force lowercase */
206 int shortdomain; /* strip entire domain */
207 const char defaultefmt[] = "%h";
208 const char *efmt = defaultefmt; /* expansion format */
209 #ifdef HAVE_ASYNC_DNS
210 int asyncdns; /* 2 pass async dns hack */
211 int numasync; /* number of outstanding async requests */
212 int asyncfd;
213 #endif
214 int networknumber; /* convert to network numbers */
215 int check; /* check PTR's against A records */
216 int debug;
217
218 int tmo; /* seconds to wait for a answer from the dns */
219 int doingdns; /* true if we're waiting for the nameserver */
220 sigjmp_buf alrmenv; /* siglongjmp() buffer */
221
222 /* Rate limit */
223 float queriespersec = 1000.0; /* maximum queries per second with async */
224 struct timeval startts; /* start time for rate limit calculation */
225 long querycount; /* number of queries so far */
226
227 long cachehits; /* number of successful cache hits */
228 int filecount; /* number of files so far */
229 char *prog;
230
231 char domain[64]; /* current domain name (including '.') */
232 int domainlen; /* length of domain name */
233
234 char azero[NS_IN6ADDRSZ];
235
236 #ifdef HAVE_ASYNC_DNS
237 struct nb_dns_info *nd;
238 #endif
239
240 int targc;
241 char **targv;
242
243 extern char *optarg;
244 extern int optind, opterr;
245
246 /* ANSI C defines this */
247 #ifndef __STDC__
248 extern char *malloc();
249 #endif
250
251 /* Forwards */
252 char *a2h(const char *, int, int);
253 char *addr2host(const char *, int, int);
254 #ifdef HAVE_ASYNC_DNS
255 void asyncreap(int);
256 #endif
257 float calculaterate(void);
258 struct htable *cacheaddr(const char *, int, int, int, const char *);
259 void dump(void);
260 const char *format(const char *, const char *, const char *);
261 int getdomain(void);
262 void gettime(struct timeval *);
263 struct htable *hash(const char *, int);
264 #ifdef SIGINFO
265 RETSIGTYPE info(int);
266 #endif
267 #ifdef HAVE_ASYNC_DNS
268 int ispipe(FILE *);
269 #endif
270 struct htable *lookupaddr(const char *, int, int);
271 int main(int, char **);
272 void massagename(char *);
273 void mysleep(int);
274 void ratelimit(void);
275 RETSIGTYPE timeout(int);
276 void usage(void) __attribute__((noreturn));
277
278 int
main(argc,argv)279 main(argc, argv)
280 int argc;
281 char **argv;
282 {
283 char *cp, *ep;
284 int op, nflags;
285 long v;
286 #ifdef HAVE_ASYNC_DNS
287 char errstr[NB_DNS_ERRSIZE];
288 #endif
289
290 if (argv[0] == NULL)
291 prog = "hf";
292 else if ((cp = strrchr(argv[0], '/')) != NULL)
293 prog = cp + 1;
294 else
295 prog = argv[0];
296
297 opterr = 0;
298 nflags = 0;
299 while ((op = getopt(argc, argv, "1abcdf:ilnNR:t:x")) != EOF)
300 switch (op) {
301
302 case '1':
303 ++linemode;
304 ++nflags;
305 break;
306
307 case 'a':
308 #ifdef HAVE_ASYNC_DNS
309 ++asyncdns;
310 #else
311 fprintf(stderr,
312 "%s: warning: -a not supported; ignored\n", prog);
313 #endif
314 ++nflags;
315 break;
316
317 case 'b':
318 efmt = "%h(%i)";
319 ++nflags;
320 break;
321
322 case 'c':
323 ++check;
324 ++nflags;
325 break;
326
327 case 'd':
328 ++debug;
329 ++nflags;
330 break;
331
332 case 'f':
333 if (*optarg == '\0')
334 efmt = defaultefmt;
335 else
336 efmt = optarg;
337 ++nflags;
338 break;
339
340 case 'i':
341 lcase = 0;
342 ++nflags;
343 break;
344
345 case 'l':
346 strip = 0;
347 ++nflags;
348 break;
349
350 case 'n':
351 #ifdef notdef
352 ++networknumber;
353 #else
354 fprintf(stderr, "%s: -n not currently impemented\n",
355 prog);
356 exit(1);
357 #endif
358 ++nflags;
359 break;
360
361 case 'N':
362 ++shortdomain;
363 ++nflags;
364 break;
365
366 case 'R':
367 v = strtol(optarg, &ep, 10);
368 if (*ep != '\0' || v > 1000000)
369 usage();
370 queriespersec = (float)v;
371 break;
372
373 case 't':
374 tmo = atoi(optarg);
375 if (tmo <= 0)
376 usage();
377 ++nflags;
378 break;
379
380 case 'x':
381 ++xflag;
382 break;
383
384 default:
385 usage();
386 }
387
388 if (xflag && nflags)
389 usage();
390
391 #ifdef HAVE_ASYNC_DNS
392 if (asyncdns) {
393 nd = nb_dns_init(errstr);
394 if (nd == NULL) {
395 fprintf(stderr, "%s: nb_dns_init: %s\n", prog, errstr);
396 exit(1);
397 }
398 asyncfd = nb_dns_fd(nd);
399 /* If no explicit timeout, use resolver retransmit */
400 if (tmo == 0)
401 tmo = _res.retrans;
402 }
403 #endif
404
405 /* Figure out our domain, if necessary */
406 if (!strip || shortdomain || !getdomain())
407 domain[0] = '\0';
408
409 /* Up number of retries, we really want answers */
410 _res.retry = 20;
411
412 /* Don't search, we'll use only FQDNs */
413 _res.options &= ~RES_DNSRCH;
414
415 /* Use a persistent TCP connection */
416 _res.options |= (RES_USEVC | RES_STAYOPEN);
417
418 /* Start time for rate limit calculations */
419 gettime(&startts);
420
421 #ifdef SIGINFO
422 (void)setsignal(SIGINFO, info);
423 #endif
424
425 /* Setup alarm catcher if -t */
426 #ifdef HAVE_ASYNC_DNS
427 if (!asyncdns)
428 #endif
429 if (tmo > 0)
430 (void)setsignal(SIGALRM, timeout);
431
432 /* Let yywrap() figure out if there are any arguments to open */
433 targc = argc - optind;
434 targv = &argv[optind];
435 yyin = NULL;
436 (void)yywrap();
437
438 /* Process file opened by yywrap() or stdin if no arguments */
439 if (yyin) {
440 #ifdef HAVE_ASYNC_DNS
441 /* XXX depends on the type of stdin */
442 /* XXX can we do a test rewind? */
443 #ifdef notdef
444 if (asyncdns && yyin == stdin)
445 fprintf(stderr,
446 "%s: warning: can't use -a on stdin\n", prog);
447 #endif
448 #endif
449 yylex();
450 }
451
452 if (debug) {
453 fflush(stdout);
454 reportinfo();
455 if (debug > 1)
456 dump();
457 }
458 exit(0);
459 }
460
461 int
yywrap()462 yywrap()
463 {
464 char *file;
465 static int didany = 0;
466
467 /* Close file, if necessary */
468 if (yyin) {
469 #ifdef HAVE_ASYNC_DNS
470 if (asyncdns) {
471 if (lookup_pass) {
472 if (fseek(yyin, 0L, SEEK_SET) < 0) {
473 fprintf(stderr,
474 "%s: fseek/rewind: %s\n",
475 prog, strerror(errno));
476 exit(1);
477 }
478 yyrestart(yyin);
479 lookup_pass = 0;
480 asyncreap(1);
481 return (0);
482 }
483 numasync = 0;
484 }
485 #endif
486 if (yyin != stdin)
487 (void)fclose(yyin);
488 yyin = NULL;
489 }
490
491 /* Spin through arguments until we run out or successfully open one */
492 while (targc > 0) {
493 file = targv[0];
494 --targc;
495 ++targv;
496 ++didany;
497 if ((yyin = fopen(file, "r")) != NULL) {
498 #ifdef HAVE_ASYNC_DNS
499 if (asyncdns)
500 lookup_pass = 1;
501 #endif
502 ++filecount;
503 return (0);
504 }
505 perror(file);
506 }
507 if (!didany) {
508 ++didany;
509 ++filecount;
510 yyin = stdin;
511 #ifdef HAVE_ASYNC_DNS
512 if (asyncdns) {
513 if (ispipe(yyin)) {
514 fprintf(stderr,
515 "%s: warning: can't use -a on a pipe\n",
516 prog);
517 asyncdns = 0;
518 } else
519 lookup_pass = 1;
520 }
521 #endif
522 return (0);
523 }
524 return (1);
525 }
526
527 const char *
format(const char * fmt,const char * hnstr,const char * ipstr)528 format(const char *fmt, const char *hnstr, const char *ipstr)
529 {
530 char *cp;
531 const char *cp2, *item;
532 size_t len, ilen, llen, dlen, nlen, hlen, itemlen;
533 static char buf[1024];
534
535 /* ip address length */
536 ilen = strlen(ipstr);
537
538 /* Normally we are passed the long hostname */
539 if (hnstr != NULL) {
540 /* Long hostname length */
541 llen = strlen(hnstr);
542
543 /* Local domain truncated hostname length */
544 dlen = llen;
545 if (*domain != '\0') {
546 cp2 = hnstr + (llen - domainlen);
547 if (cp2 > hnstr && strcasecmp(cp2, domain) == 0)
548 dlen = cp2 - hnstr;
549 }
550
551 /* Local domain truncated hostname length */
552 cp = strchr(hnstr, '.');
553 if (cp == NULL)
554 nlen = llen;
555 else
556 nlen = cp - hnstr;
557
558 /* Default hostname length */
559 if (shortdomain)
560 hlen = nlen;
561 else
562 hlen = dlen;
563 } else {
564 /* We only have the ip address string */
565 hnstr = ipstr;
566 llen = strlen(hnstr);
567 dlen = llen;
568 nlen = llen;
569 hlen = llen;
570 }
571
572 cp = buf;
573 /* Leave room for EOS */
574 len = sizeof(buf) - 1;
575 cp2 = fmt;
576 while (*cp2 != '\0' && len > 0) {
577 if (*cp2 != '%') {
578 *cp++ = *cp2++;
579 --len;
580 continue;
581 }
582 ++cp2;
583 switch (*cp2) {
584
585 case 'h':
586 /* Default hostname */
587 item = hnstr;
588 itemlen = hlen;
589 break;
590
591 case 'D':
592 /* Local domain truncated hostname */
593 item = hnstr;
594 itemlen = dlen;
595 break;
596
597 case 'N':
598 /* Domain truncated hostname */
599 item = hnstr;
600 itemlen = nlen;
601 break;
602
603 case 'i':
604 /* ip address */
605 item = ipstr;
606 itemlen = ilen;
607 break;
608
609 case 'l':
610 /* Long hostname */
611 item = hnstr;
612 itemlen = llen;
613 break;
614
615 default:
616 /* Random character */
617 *cp++ = *cp2++;
618 --len;
619 continue;
620 }
621 if (itemlen > len)
622 itemlen = len;
623 strncpy(cp, item, itemlen);
624 cp += itemlen;
625 len -= itemlen;
626 ++cp2;
627 }
628 *cp = '\0';
629
630 return (buf);
631 }
632
633 int
getdomain()634 getdomain()
635 {
636 char *cp;
637 struct hostent *hp;
638 char host[128];
639
640 if (gethostname(host, sizeof(host) - 1) < 0)
641 return (0);
642 if ((cp = strchr(host, '.')) == NULL) {
643 /* Not already canonical */
644 if (tmo > 0)
645 alarm(tmo);
646 doingdns = 1;
647 if (sigsetjmp(alrmenv, 1))
648 return (0);
649 hp = gethostbyname(host);
650 doingdns = 0;
651 if (hp == NULL)
652 return (0);
653 if ((cp = strchr(hp->h_name, '.')) == NULL)
654 return (0);
655 }
656 (void)strncpy(domain, cp, sizeof(domain));
657 domain[sizeof(domain) - 1] = '\0';
658 if (lcase)
659 for (cp = domain; *cp; ++cp)
660 if (isupper((int)*cp))
661 *cp = tolower(*cp);
662 domainlen = strlen(domain);
663 return (1);
664 }
665
666 void
gettime(struct timeval * tvp)667 gettime(struct timeval *tvp)
668 {
669 if (gettimeofday(tvp, NULL) < 0) {
670 fprintf(stderr, "%s: gettimeofday: %s\n",
671 prog, strerror(errno));
672 exit(1);
673 }
674 }
675
676 void
ratelimit()677 ratelimit()
678 {
679 if (queriespersec > 0.0) {
680 if (querycount > 0)
681 while (calculaterate() > queriespersec)
682 mysleep(10);
683 ++querycount;
684 }
685 }
686
687 RETSIGTYPE
timeout(int signo)688 timeout(int signo)
689 {
690 if (doingdns) {
691 doingdns = 0;
692 siglongjmp(alrmenv, 1);
693 }
694 return RETSIGVAL;
695 }
696
697 /* Convert address to hostname via the dns */
698 char *
a2h(const char * ap,int alen,int af)699 a2h(const char *ap, int alen, int af)
700 {
701 char **pp, *host2;
702 size_t len;
703 static struct hostent *hp;
704 static char *host = NULL;
705 static size_t hostlen = 0;
706
707 /* Look up the PTR */
708 if (tmo > 0)
709 alarm(tmo);
710 doingdns = 1;
711 if (sigsetjmp(alrmenv, 1))
712 return (NULL);
713
714 /* Handle rate limiting */
715 ratelimit();
716
717 hp = gethostbyaddr(ap, alen, af);
718 doingdns = 0;
719 if (hp == NULL)
720 return (NULL);
721
722 len = strlen(hp->h_name) + 1;
723 if (hostlen < len) {
724 if (len < 132)
725 len = 132;
726 if (host == NULL)
727 host = malloc(len);
728 else {
729 host2 = realloc(host, len);
730 if (host2 != NULL)
731 host = host2;
732 else {
733 free(host);
734 host = NULL;
735 }
736 }
737 if (host == NULL) {
738 hostlen = 0;
739 return (NULL);
740 }
741 hostlen = len;
742 }
743 (void)strcpy(host, hp->h_name);
744
745 /* Done if we aren't checking */
746 if (!check)
747 return (host);
748
749 #ifndef HAVE_GETHOSTBYNAME2
750 if (af != AF_INET)
751 return (NULL);
752 #endif
753
754 /* Check PTR against the A record */
755 if (tmo > 0)
756 alarm(tmo);
757 doingdns = 1;
758 if (sigsetjmp(alrmenv, 1))
759 return (NULL);
760
761 /* Handle rate limiting */
762 ratelimit();
763
764 #ifdef HAVE_GETHOSTBYNAME2
765 hp = gethostbyname2(host, af);
766 #else
767 hp = gethostbyname(host);
768 #endif
769 doingdns = 0;
770 if (hp == NULL)
771 return (NULL);
772 if (af != hp->h_addrtype)
773 return (NULL);
774
775 /* Spin through ip addresses looking for a match */
776 for (pp = hp->h_addr_list; *pp != NULL; ++pp)
777 if (memcmp(ap, *pp, alen) == 0)
778 return (host);
779
780 return (NULL);
781 }
782
783 /* Convert address to hostname via the cache and/or dns */
784 char *
addr2host(const char * ap,int alen,int af)785 addr2host(const char *ap, int alen, int af)
786 {
787 int state;
788 char *host;
789 struct htable *p;
790
791 /* First look in hash table */
792 p = lookupaddr(ap, alen, af);
793 if (p != NULL)
794 return (p->name);
795
796 /* Lookup this host */
797 host = a2h(ap, alen, af);
798 state = STATE_RAW;
799 if (host != NULL) {
800 if (check)
801 state = STATE_HAVEA;
802 else
803 state = STATE_HAVEPTR;
804 massagename(host);
805 }
806
807 p = cacheaddr(ap, state, alen, af, host);
808 if (p != NULL)
809 return (p->name);
810
811 return (host);
812 }
813
814 /* Look hash table entry for address */
815 struct htable *
lookupaddr(const char * ap,int alen,int af)816 lookupaddr(const char *ap, int alen, int af)
817 {
818 struct htable *p;
819
820 for (p = hash(ap, af); p != NULL; p = p->next) {
821 if (p->af == af && memcmp(p->addr, ap, alen) == 0) {
822 #ifdef HAVE_ASYNC_DNS
823 if (!asyncdns || lookup_pass)
824 #endif
825 ++cachehits;
826 return (p);
827 }
828 }
829 return (NULL);
830 }
831
832 void
massagename(char * name)833 massagename(char *name)
834 {
835 char *cp;
836
837 if (lcase)
838 for (cp = name; *cp; ++cp)
839 if (isupper((int)*cp))
840 *cp = tolower(*cp);
841 }
842
843 void
mysleep(int ms)844 mysleep(int ms)
845 {
846 struct timeval tv;
847 div_t res;
848
849 res = div(ms, 1000);
850 tv.tv_sec = res.quot;
851 tv.tv_usec = res.rem * 1000;
852 if (select(0, NULL, NULL, NULL, &tv) < 0 && errno != EINTR) {
853 fprintf(stderr, "%s: mysleep: select: %s\n",
854 prog, strerror(errno));
855 exit(1);
856 }
857 }
858
859 struct htable *
cacheaddr(const char * ap,int state,int alen,int af,const char * host)860 cacheaddr(const char *ap, int state, int alen, int af, const char *host)
861 {
862 struct htable *p, *p2;
863
864 /* Don't cache zero */
865 if (memcmp(ap, azero, alen) == 0)
866 return (NULL);
867
868 /* Look for existing slot in hash table */
869 for (p = hash(ap, af); p != NULL; p = p->next)
870 if (p->state != STATE_FREE &&
871 p->af == af &&
872 memcmp(p->addr, ap, alen) == 0)
873 break;
874
875 /* Allocate a new slot */
876 if (p == NULL) {
877 p = hash(ap, af);
878 if (p->state != STATE_FREE) {
879 /* Handle the collision */
880 p2 = (struct htable *)malloc(sizeof(struct htable));
881 /* Lose, lose */
882 if (p2 == NULL)
883 return (NULL);
884 memset((char *)p2, 0, sizeof(struct htable));
885 p2->next = p->next;
886 p->next = p2;
887 p = p2;
888 }
889 }
890
891 /* Install new host */
892 memmove(p->addr, ap, alen);
893 p->alen = alen;
894 p->af = af;
895 if (host != NULL)
896 p->name = strdup(host);
897 if (state != 0)
898 p->state = state;
899 if (p->state == STATE_FREE)
900 abort();
901
902 /* Return answer entry */
903 return (p);
904 }
905
906 void
dump()907 dump()
908 {
909 char *cp;
910 int i, j, n, d;
911 struct htable *p, *p2;
912 char buf[132];
913
914 d = n = 0;
915 for (p = htable, i = 0; i < HSIZE; ++p, ++i)
916 if (p->name) {
917 ++n;
918 j = 0;
919 for (p2 = p; p2; p2 = p2->next) {
920 if ((cp = p2->name) == NULL)
921 cp = "<nil>";
922 else if (cp == (char *)1)
923 cp = "<raw>";
924 (void)fprintf(stderr, "%4d:%d ", i, j);
925 if (inet_ntop(p2->af, p2->addr,
926 buf, sizeof(buf)) == NULL)
927 (void)fprintf(stderr, "?");
928 else
929 (void)fprintf(stderr, "%s", buf);
930 switch (p2->state) {
931
932 case STATE_HAVEA:
933 (void)fprintf(stderr, " HAVEA");
934 break;
935
936 case STATE_HAVEPTR:
937 (void)fprintf(stderr, " HAVEPTR");
938 break;
939
940 case STATE_SENTPTR:
941 (void)fprintf(stderr, " SENTPTR");
942 break;
943
944 case STATE_RAW:
945 (void)fprintf(stderr, " RAW");
946 break;
947
948 default:
949 (void)fprintf(stderr, " #%d",
950 p2->state);
951 break;
952 }
953 (void)fprintf(stderr, " \"%s\"\n", cp);
954 ++d;
955 ++j;
956 }
957 }
958 d -= n;
959 (void)fprintf(stderr, "%d entries (%d dynamically linked)\n", n, d);
960 }
961
962 #ifdef HAVE_ASYNC_DNS
963 void
asyncreap(int ateof)964 asyncreap(int ateof)
965 {
966 char *host;
967 int n;
968 char **pp;
969 struct htable *p;
970 struct nb_dns_result *nr;
971 struct hostent *hp;
972 fd_set fds;
973 struct timeval to;
974 char errstr[NB_DNS_ERRSIZE];
975 struct nb_dns_result xxxnr;
976
977 nr = &xxxnr;
978 memset(nr, 0, sizeof(*nr));
979 while (numasync > 0) {
980 FD_ZERO(&fds);
981 FD_SET(asyncfd, &fds);
982 /* If we're not at EOF, just poll */
983 if (!ateof) {
984 to.tv_sec = 0;
985 to.tv_usec = 0;
986 } else {
987 to.tv_sec = tmo;
988 to.tv_usec = 0;
989 }
990 n = select(asyncfd + 1, &fds, NULL, NULL, &to);
991 if (n < 0) {
992 fprintf(stderr, "%s: select: %s\n",
993 prog, strerror(errno));
994 exit(1);
995 }
996
997 /* Done if timed out */
998 if (n == 0)
999 break;
1000
1001 n = nb_dns_activity(nd, nr, errstr);
1002 if (n < 0) {
1003 fprintf(stderr, "%s: nb_dns_activity: %s\n",
1004 prog, errstr);
1005 exit(1);
1006 }
1007
1008 /* Bail if reply doesn't match any current queries */
1009 if (n == 0)
1010 continue;
1011
1012 /* Decrement outstanding request counter */
1013 --numasync;
1014
1015 /* Bail if not a good answer */
1016 if (nr->host_errno != NETDB_SUCCESS)
1017 continue;
1018
1019 /* Bail if no hostname (probably shouldn't happen) */
1020 hp = nr->hostent;
1021 host = hp->h_name;
1022 if (host == NULL)
1023 continue;
1024
1025 /* Recover hash table pointer */
1026 p = (struct htable *)nr->cookie;
1027
1028 switch (p->state) {
1029
1030 case STATE_SENTPTR:
1031 /* Are we done? */
1032 if (!check) {
1033 p->state = STATE_HAVEPTR;
1034 break;
1035 }
1036
1037 /* Now look up the A record */
1038 if (nb_dns_host_request2(nd, host, p->af,
1039 (void *)p, errstr) < 0) {
1040 fprintf(stderr, "%s: nb_dns_host_request: %s\n",
1041 prog, errstr);
1042 p->state = STATE_RAW;
1043 free(p->name);
1044 p->name = NULL;
1045 break;
1046 }
1047
1048 /* Cache the fact that we're looking */
1049 ++numasync;
1050 p->state = STATE_SENTA;
1051 break;
1052
1053 case STATE_SENTA:
1054 /* Check A against our address */
1055 if (p->af != hp->h_addrtype) {
1056 p->state = STATE_RAW;
1057 free(p->name);
1058 p->name = NULL;
1059 break;
1060 }
1061
1062 /* Spin through ip addresses looking for a match */
1063 for (pp = hp->h_addr_list; *pp != NULL; ++pp)
1064 if (memcmp(p->addr, *pp, p->alen) == 0)
1065 break;
1066
1067 if (pp == NULL) {
1068 p->state = STATE_RAW;
1069 free(p->name);
1070 p->name = NULL;
1071 break;
1072 }
1073 p->state = STATE_HAVEA;
1074 break;
1075
1076 default:
1077 abort();
1078 }
1079 massagename(host);
1080 if (p->name != NULL)
1081 abort();
1082 if (host != NULL)
1083 p->name = strdup(host);
1084 }
1085 }
1086 #endif
1087
1088 float
calculaterate()1089 calculaterate()
1090 {
1091 struct timeval now, delta;
1092
1093 gettime(&now);
1094 timersub(&now, &startts, &delta);
1095 return ((float)querycount) /
1096 (((float)delta.tv_sec) + (((float)delta.tv_usec) / 1000000.0));
1097 }
1098
1099 void
convert(const char * ipstr,int isv6)1100 convert(const char *ipstr, int isv6)
1101 {
1102 const char *hnstr;
1103 int alen;
1104 int af;
1105 #ifdef HAVE_ASYNC_DNS
1106 struct htable *p;
1107 char errstr[NB_DNS_ERRSIZE];
1108 static int num = 0;
1109 #endif
1110 char addr[NS_IN6ADDRSZ];
1111
1112 if (isv6) {
1113 #ifdef AF_INET6
1114 af = AF_INET6;
1115 alen = NS_IN6ADDRSZ;
1116 #else
1117 #ifdef HAVE_ASYNC_DNS
1118 if (!asyncdns || !lookup_pass)
1119 #endif
1120 fputs(ipstr, stdout);
1121 return;
1122 #endif
1123 } else {
1124 af = AF_INET;
1125 alen = NS_INADDRSZ;
1126 }
1127
1128 #ifdef HAVE_ASYNC_DNS
1129 if (asyncdns && lookup_pass) {
1130 if (inet_pton(af, ipstr, addr) != 1)
1131 return;
1132
1133 /* Done if already in hash table */
1134 if (lookupaddr(addr, alen, af) != NULL)
1135 return;
1136
1137 p = cacheaddr(addr, STATE_SENTPTR, alen, af, NULL);
1138 if (p == NULL)
1139 return;
1140
1141 /* Handle rate limiting */
1142 ratelimit();
1143
1144 if (nb_dns_addr_request2(nd, addr, af,
1145 (void *)p, errstr) >= 0) {
1146 /* Cache the fact that we're looking */
1147 ++numasync;
1148 ++num;
1149 } else
1150 fprintf(stderr, "%s: nb_dns_host_request: %s\n",
1151 prog, errstr);
1152 /* reap replies after we send a number of queries */
1153 if (num > 10) {
1154 asyncreap(0);
1155 num = 0;
1156 }
1157 return;
1158 }
1159 #endif
1160
1161 /* Only attempt to translate one address per line */
1162 if (linemode && triedone) {
1163 fputs(ipstr, stdout);
1164 return;
1165 }
1166 ++triedone;
1167
1168 if (inet_pton(af, ipstr, addr) == 1)
1169 hnstr = addr2host(addr, alen, af);
1170 else
1171 hnstr = NULL;
1172 fputs(format(efmt, hnstr, ipstr), stdout);
1173 }
1174
1175 struct htable *
hash(const char * ap,int af)1176 hash(const char *ap, int af)
1177 {
1178 u_int32_t h;
1179
1180 switch (af) {
1181
1182 case AF_INET:
1183 memmove(&h, ap, sizeof(h));
1184 break;
1185
1186 case AF_INET6:
1187 memmove(&h, ap + NS_IN6ADDRSZ - sizeof(h), sizeof(h));
1188 break;
1189
1190 default:
1191 abort();
1192 }
1193 return (&htable[h & (HSIZE - 1)]);
1194 }
1195
1196 #ifdef SIGINFO
1197 RETSIGTYPE
info(int signo)1198 info(int signo)
1199 {
1200 doinfo = 1;
1201 return RETSIGVAL;
1202 }
1203 #endif
1204
1205 #ifdef HAVE_ASYNC_DNS
1206 int
ispipe(FILE * f)1207 ispipe(FILE *f)
1208 {
1209 struct stat sbuf;
1210
1211 if (fstat(fileno(f), &sbuf) < 0) {
1212 fprintf(stderr, "%s: fstat: %s\n", prog, strerror(errno));
1213 exit(1);
1214 }
1215 if ((sbuf.st_mode & S_IFMT) != S_IFREG)
1216 return (1);
1217 return (0);
1218 }
1219 #endif
1220
1221 void
reportinfo(void)1222 reportinfo(void)
1223 {
1224 struct timeval now, delta;
1225
1226 gettime(&now);
1227 timersub(&now, &startts, &delta);
1228 (void)fprintf(stderr, "[%d file%s, %ld querie%s,"
1229 " %ld.%03ld seconds, %ld cache hit%s, %.1f queries/sec]\n",
1230 filecount, PLURAL(filecount),
1231 querycount, PLURAL(querycount),
1232 delta.tv_sec, delta.tv_usec / 100,
1233 cachehits, PLURAL(cachehits),
1234 calculaterate());
1235 }
1236
1237 void
usage()1238 usage()
1239 {
1240
1241 (void)fprintf(stderr, "Version %s\n", version);
1242 (void)fprintf(stderr, "usage: %s [-1abcdilN] [-f format] [-R qps]"
1243 " [-t secs] [file ...]\n", prog);
1244 (void)fprintf(stderr, " %s -x [file ...]\n", prog);
1245 exit(1);
1246 }
1247