libexport.a: Add helpers to manage DNS lookups
[nfs-utils.git] / support / export / hostname.c
1 /*
2  * support/export/hostname.c
3  *
4  * Functions for hostname.
5  *
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 /*
13 #define TEST
14 */
15
16 #include <string.h>
17 #include <netdb.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <stdlib.h>
21 #include <errno.h>
22
23 #include <xlog.h>
24 #ifdef TEST
25 #define xmalloc malloc
26 #else
27 #include "xmalloc.h"
28 #include "misc.h"
29 #endif
30
31 #include "sockaddr.h"
32 #include "exportfs.h"
33
34 #ifndef HAVE_DECL_AI_ADDRCONFIG
35 #define AI_ADDRCONFIG   0
36 #endif
37
38 #define ALIGNMENT       sizeof (char *)
39
40 static int
41 align (int len, int al)
42 {
43   int i;
44   i = len % al;
45   if (i)
46     len += al - i;
47   return len;
48 }
49
50 struct hostent *
51 get_hostent (const char *addr, int len, int type)
52 {
53   struct hostent *cp;
54   int len_ent;
55   const char *name;
56   int len_name;
57   int num_aliases = 1;
58   int len_aliases = sizeof (char *);
59   int num_addr_list = 1;
60   int len_addr_list = sizeof (char *);
61   int pos;
62   struct in_addr *ipv4;
63
64   switch (type)
65     {
66     case AF_INET:
67       ipv4 = (struct in_addr *) addr;
68       name = inet_ntoa (*ipv4);
69       break;
70
71     default:
72       return NULL;
73     }
74
75   len_ent = align (sizeof (*cp), ALIGNMENT);
76   len_name = align (strlen (name) + 1, ALIGNMENT);
77
78   num_addr_list++;
79   len_addr_list += align (len, ALIGNMENT) + sizeof (char *);
80
81   cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases
82                                    + len_addr_list);
83
84   cp->h_addrtype = type;
85   cp->h_length = len;
86   pos = len_ent;
87   cp->h_name = (char *) &(((char *) cp) [pos]);
88   strcpy (cp->h_name, name);
89
90   pos += len_name;
91   cp->h_aliases = (char **) &(((char *) cp) [pos]);
92   pos += num_aliases * sizeof (char *);
93   cp->h_aliases [0] = NULL;
94
95   pos = len_ent + len_name + len_aliases;
96   cp->h_addr_list = (char **) &(((char *) cp) [pos]);
97   pos += num_addr_list * sizeof (char *);
98   cp->h_addr_list [0] = (char *) &(((char *) cp) [pos]);
99   memcpy (cp->h_addr_list [0], addr, cp->h_length);
100   pos += align (cp->h_length, ALIGNMENT);
101   cp->h_addr_list [1] = NULL;
102
103   return cp;
104 }
105
106 struct hostent *
107 hostent_dup (struct hostent *hp)
108 {
109   int len_ent = align (sizeof (*hp), ALIGNMENT);
110   int len_name = align (strlen (hp->h_name) + 1, ALIGNMENT);
111   int num_aliases = 1;
112   int len_aliases = sizeof (char *);
113   int num_addr_list = 1;
114   int len_addr_list = sizeof (char *);
115   int pos, i;
116   char **sp;
117   struct hostent *cp;
118
119   for (sp = hp->h_aliases; sp && *sp; sp++)
120     {
121       num_aliases++;
122       len_aliases += align (strlen (*sp) + 1, ALIGNMENT)
123                      + sizeof (char *);
124     }
125
126   for (sp = hp->h_addr_list; *sp; sp++)
127     {
128       num_addr_list++;
129       len_addr_list += align (hp->h_length, ALIGNMENT)
130                        + sizeof (char *);
131     }
132
133   cp = (struct hostent *) xmalloc (len_ent + len_name + len_aliases
134                                    + len_addr_list);
135
136   *cp = *hp;
137   pos = len_ent;
138   cp->h_name = (char *) &(((char *) cp) [pos]);
139   strcpy (cp->h_name, hp->h_name);
140
141   pos += len_name;
142   cp->h_aliases = (char **) &(((char *) cp) [pos]);
143   pos += num_aliases * sizeof (char *);
144   for (sp = hp->h_aliases, i = 0; i < num_aliases; i++, sp++)
145     if (sp && *sp)
146       {
147         cp->h_aliases [i] = (char *) &(((char *) cp) [pos]);
148         strcpy (cp->h_aliases [i], *sp);
149         pos += align (strlen (*sp) + 1, ALIGNMENT);
150       }
151     else
152       cp->h_aliases [i] = NULL;
153
154   pos = len_ent + len_name + len_aliases;
155   cp->h_addr_list = (char **) &(((char *) cp) [pos]);
156   pos += num_addr_list * sizeof (char *);
157   for (sp = hp->h_addr_list, i = 0; i < num_addr_list; i++, sp++)
158     if (*sp)
159       {
160         cp->h_addr_list [i] = (char *) &(((char *) cp) [pos]);
161         memcpy (cp->h_addr_list [i], *sp, hp->h_length);
162         pos += align (hp->h_length, ALIGNMENT);
163       }
164     else
165       cp->h_addr_list [i] = *sp;
166
167   return cp;
168 }
169
170 #ifdef HAVE_GETNAMEINFO
171 static socklen_t
172 sockaddr_size(const struct sockaddr *sap)
173 {
174         if (sap->sa_family != AF_INET)
175                 return 0;
176         return (socklen_t)sizeof(struct sockaddr_in);
177 }
178 #endif  /* HAVE_GETNAMEINFO */
179
180 /**
181  * host_ntop - generate presentation address given a sockaddr
182  * @sap: pointer to socket address
183  * @buf: working storage
184  * @buflen: size of @buf in bytes
185  *
186  * Returns a pointer to a @buf.
187  */
188 #ifdef HAVE_GETNAMEINFO
189 char *
190 host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
191 {
192         socklen_t salen = sockaddr_size(sap);
193         int error;
194
195         memset(buf, 0, buflen);
196
197         if (salen == 0) {
198                 (void)strncpy(buf, "bad family", buflen - 1);
199                 return buf;
200         }
201
202         error = getnameinfo(sap, salen, buf, (socklen_t)buflen,
203                                                 NULL, 0, NI_NUMERICHOST);
204         if (error != 0) {
205                 buf[0] = '\0';
206                 (void)strncpy(buf, "bad address", buflen - 1);
207         }
208
209         return buf;
210 }
211 #else   /* !HAVE_GETNAMEINFO */
212 char *
213 host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
214 {
215         const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
216
217         memset(buf, 0, buflen);
218
219         if (sin->sin_family != AF_INET)
220                 (void)strncpy(buf, "bad family", buflen - 1);
221                 return buf;
222         }
223
224         if (inet_ntop(AF_INET, &sin->sin_addr.s_addr, buf, buflen) != NULL)
225                 return buf;
226
227         buf[0] = '\0';
228         (void)strncpy(buf, "bad address", buflen - 1);
229         return buf;
230 }
231 #endif  /* !HAVE_GETNAMEINFO */
232
233 /**
234  * host_pton - return addrinfo for a given presentation address
235  * @paddr: pointer to a '\0'-terminated ASCII string containing an
236  *              IP presentation address
237  *
238  * Returns address info structure, or NULL if an error occurs.  Caller
239  * must free the returned structure with freeaddrinfo(3).
240  */
241 __attribute_malloc__
242 struct addrinfo *
243 host_pton(const char *paddr)
244 {
245         struct addrinfo *ai = NULL;
246         struct addrinfo hint = {
247                 /* don't return duplicates */
248                 .ai_protocol    = (int)IPPROTO_UDP,
249                 .ai_flags       = AI_NUMERICHOST,
250                 .ai_family      = AF_UNSPEC,
251         };
252         struct sockaddr_in sin;
253         int error;
254
255         /*
256          * Although getaddrinfo(3) is easier to use and supports
257          * IPv6, it recognizes incomplete addresses like "10.4"
258          * as valid AF_INET addresses.  It also accepts presentation
259          * addresses that end with a blank.
260          *
261          * inet_pton(3) is much stricter.  Use it to be certain we
262          * have a real AF_INET presentation address, before invoking
263          * getaddrinfo(3) to generate the full addrinfo list.
264          */
265         if (inet_pton(AF_INET, paddr, &sin.sin_addr) == 0)
266                 return NULL;
267
268         error = getaddrinfo(paddr, NULL, &hint, &ai);
269         switch (error) {
270         case 0:
271                 return ai;
272         case EAI_NONAME:
273                 if (paddr == NULL)
274                         xlog(D_GENERAL, "%s: passed a NULL presentation address",
275                                 __func__);
276                 break;
277         case EAI_SYSTEM:
278                 xlog(D_GENERAL, "%s: failed to convert %s: (%d) %m",
279                                 __func__, paddr, errno);
280                 break;
281         default:
282                 xlog(D_GENERAL, "%s: failed to convert %s: %s",
283                                 __func__, paddr, gai_strerror(error));
284                 break;
285         }
286
287         return NULL;
288 }
289
290 /**
291  * host_addrinfo - return addrinfo for a given hostname
292  * @hostname: pointer to a '\0'-terminated ASCII string containing a hostname
293  *
294  * Returns address info structure with ai_canonname filled in, or NULL
295  * if no information is available for @hostname.  Caller must free the
296  * returned structure with freeaddrinfo(3).
297  */
298 __attribute_malloc__
299 struct addrinfo *
300 host_addrinfo(const char *hostname)
301 {
302         struct addrinfo *ai = NULL;
303         struct addrinfo hint = {
304                 .ai_family      = AF_INET,
305                 /* don't return duplicates */
306                 .ai_protocol    = (int)IPPROTO_UDP,
307                 .ai_flags       = AI_ADDRCONFIG | AI_CANONNAME,
308         };
309         int error;
310
311         error = getaddrinfo(hostname, NULL, &hint, &ai);
312         switch (error) {
313         case 0:
314                 return ai;
315         case EAI_SYSTEM:
316                 xlog(D_GENERAL, "%s: failed to resolve %s: (%d) %m",
317                                 __func__, hostname, errno);
318                 break;
319         default:
320                 xlog(D_GENERAL, "%s: failed to resolve %s: %s",
321                                 __func__, hostname, gai_strerror(error));
322                 break;
323         }
324
325         return NULL;
326 }
327
328 /**
329  * host_canonname - return canonical hostname bound to an address
330  * @sap: pointer to socket address to look up
331  *
332  * Discover the canonical hostname associated with the given socket
333  * address.  The host's reverse mapping is verified in the process.
334  *
335  * Returns a '\0'-terminated ASCII string containing a hostname, or
336  * NULL if no hostname can be found for @sap.  Caller must free
337  * the string.
338  */
339 #ifdef HAVE_GETNAMEINFO
340 __attribute_malloc__
341 char *
342 host_canonname(const struct sockaddr *sap)
343 {
344         socklen_t salen = sockaddr_size(sap);
345         char buf[NI_MAXHOST];
346         int error;
347
348         if (salen == 0) {
349                 xlog(D_GENERAL, "%s: unsupported address family %d",
350                                 __func__, sap->sa_family);
351                 return NULL;
352         }
353
354         memset(buf, 0, sizeof(buf));
355         error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
356                                                         NULL, 0, NI_NAMEREQD);
357         switch (error) {
358         case 0:
359                 break;
360         case EAI_SYSTEM:
361                 xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m",
362                                 __func__, errno);
363                 return NULL;
364         default:
365                 (void)getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
366                                                         NULL, 0, NI_NUMERICHOST);
367                 xlog(D_GENERAL, "%s: failed to resolve %s: %s",
368                                 __func__, buf, gai_strerror(error));
369                 return NULL;
370         }
371
372         return strdup(buf);
373 }
374 #else   /* !HAVE_GETNAMEINFO */
375 __attribute_malloc__
376 char *
377 host_canonname(const struct sockaddr *sap)
378 {
379         const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
380         const struct in_addr *addr = &sin->sin_addr;
381         struct hostent *hp;
382
383         if (sap->sa_family != AF_INET)
384                 return NULL;
385
386         hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET);
387         if (hp == NULL)
388                 return NULL;
389
390         return strdup(hp->h_name);
391 }
392 #endif  /* !HAVE_GETNAMEINFO */
393
394 /**
395  * host_reliable_addrinfo - return addrinfo for a given address
396  * @sap: pointer to socket address to look up
397  *
398  * Reverse and forward lookups are performed to ensure the address has
399  * proper forward and reverse mappings.
400  *
401  * Returns address info structure with ai_canonname filled in, or NULL
402  * if no information is available for @sap.  Caller must free the returned
403  * structure with freeaddrinfo(3).
404  */
405 __attribute_malloc__
406 struct addrinfo *
407 host_reliable_addrinfo(const struct sockaddr *sap)
408 {
409         struct addrinfo *ai;
410         char *hostname;
411
412         hostname = host_canonname(sap);
413         if (hostname == NULL)
414                 return NULL;
415
416         ai = host_addrinfo(hostname);
417
418         free(hostname);
419         return ai;
420 }
421
422 /**
423  * host_numeric_addrinfo - return addrinfo without doing DNS queries
424  * @sap: pointer to socket address
425  *
426  * Returns address info structure, or NULL if an error occurred.
427  * Caller must free the returned structure with freeaddrinfo(3).
428  */
429 #ifdef HAVE_GETNAMEINFO
430 __attribute_malloc__
431 struct addrinfo *
432 host_numeric_addrinfo(const struct sockaddr *sap)
433 {
434         socklen_t salen = sockaddr_size(sap);
435         char buf[INET_ADDRSTRLEN];
436         struct addrinfo *ai;
437         int error;
438
439         if (salen == 0) {
440                 xlog(D_GENERAL, "%s: unsupported address family %d",
441                                 __func__, sap->sa_family);
442                 return NULL;
443         }
444
445         memset(buf, 0, sizeof(buf));
446         error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
447                                                 NULL, 0, NI_NUMERICHOST);
448         switch (error) {
449         case 0:
450                 break;
451         case EAI_SYSTEM:
452                 xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m",
453                                 __func__, errno);
454                 return NULL;
455         default:
456                 xlog(D_GENERAL, "%s: getnameinfo(3) failed: %s",
457                                 __func__, gai_strerror(error));
458                 return NULL;
459         }
460
461         ai = host_pton(buf);
462
463         /*
464          * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname
465          */
466         if (ai != NULL) {
467                 free(ai->ai_canonname);         /* just in case */
468                 ai->ai_canonname = strdup(buf);
469                 if (ai->ai_canonname == NULL) {
470                         freeaddrinfo(ai);
471                         ai = NULL;
472                 }
473         }
474
475         return ai;
476 }
477 #else   /* !HAVE_GETNAMEINFO */
478 __attribute_malloc__
479 struct addrinfo *
480 host_numeric_addrinfo(const struct sockaddr *sap)
481 {
482         const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
483         const struct in_addr *addr = &sin->sin_addr;
484         char buf[INET_ADDRSTRLEN];
485         struct addrinfo *ai;
486
487         if (sap->sa_family != AF_INET)
488                 return NULL;
489
490         memset(buf, 0, sizeof(buf));
491         if (inet_ntop(AF_INET, (char *)addr, buf,
492                                         (socklen_t)sizeof(buf)) == NULL)
493                 return NULL;
494
495         ai = host_pton(buf);
496
497         /*
498          * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname
499          */
500         if (ai != NULL) {
501                 ai->ai_canonname = strdup(buf);
502                 if (ai->ai_canonname == NULL) {
503                         freeaddrinfo(ai);
504                         ai = NULL;
505                 }
506         }
507
508         return ai;
509 }
510 #endif  /* !HAVE_GETNAMEINFO */
511
512 static int
513 is_hostname(const char *sp)
514 {
515   if (*sp == '\0' || *sp == '@')
516     return 0;
517
518   for (; *sp; sp++)
519     {
520       if (*sp == '*' || *sp == '?' || *sp == '[' || *sp == '/')
521         return 0;
522       if (*sp == '\\' && sp[1])
523         sp++;
524     }
525
526   return 1;
527 }
528
529 int
530 matchhostname (const char *h1, const char *h2)
531 {
532   struct hostent *hp1, *hp2;
533   int status;
534
535   if (strcasecmp (h1, h2) == 0)
536     return 1;
537
538   if (!is_hostname (h1) || !is_hostname (h2))
539     return 0;
540
541   hp1 = gethostbyname (h1);
542   if (hp1 == NULL)
543     return 0;
544
545   hp1 = hostent_dup (hp1);
546
547   hp2 = gethostbyname (h2);
548   if (hp2)
549     {
550       if (strcasecmp (hp1->h_name, hp2->h_name) == 0)
551         status = 1;
552       else
553         {
554           char **ap1, **ap2;
555
556           status = 0;
557           for (ap1 = hp1->h_addr_list; *ap1 && status == 0; ap1++)
558             for (ap2 = hp2->h_addr_list; *ap2; ap2++)
559               if (memcmp (*ap1, *ap2, sizeof (struct in_addr)) == 0)
560                 {
561                   status = 1;
562                   break;
563                 }
564         }
565     }
566   else
567     status = 0;
568
569   free (hp1);
570   return status;
571 }
572
573
574 /* Map IP to hostname, and then map back to addr to make sure it is a
575  * reliable hostname
576  */
577 struct hostent *
578 get_reliable_hostbyaddr(const char *addr, int len, int type)
579 {
580         struct hostent *hp = NULL;
581
582         struct hostent *reverse;
583         struct hostent *forward;
584         char **sp;
585
586         reverse = gethostbyaddr (addr, len, type);
587         if (!reverse)
588                 return NULL;
589
590         /* must make sure the hostent is authorative. */
591
592         reverse = hostent_dup (reverse);
593         forward = gethostbyname (reverse->h_name);
594
595         if (forward) {
596                 /* now make sure the "addr" is in the list */
597                 for (sp = forward->h_addr_list ; *sp ; sp++) {
598                         if (memcmp (*sp, addr, forward->h_length) == 0)
599                                 break;
600                 }
601
602                 if (*sp) {
603                         /* it's valid */
604                         hp = hostent_dup (forward);
605                 }
606                 else {
607                         /* it was a FAKE */
608                         xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't match reverse",
609                               reverse->h_name, inet_ntoa(*(struct in_addr*)addr));
610                 }
611         }
612         else {
613                 /* never heard of it. misconfigured DNS? */
614                 xlog (L_WARNING, "Fake hostname %s for %s - forward lookup doesn't exist",
615                       reverse->h_name, inet_ntoa(*(struct in_addr*)addr));
616         }
617
618         free (reverse);
619         return hp;
620 }
621
622
623 #ifdef TEST
624 void
625 print_host (struct hostent *hp)
626 {
627   char **sp;
628
629   if (hp)
630     {
631       printf ("official hostname: %s\n", hp->h_name);
632       printf ("aliases:\n");
633       for (sp = hp->h_aliases; *sp; sp++)
634         printf ("  %s\n", *sp);
635       printf ("IP addresses:\n");
636       for (sp = hp->h_addr_list; *sp; sp++)
637         printf ("  %s\n", inet_ntoa (*(struct in_addr *) *sp));
638     }
639   else
640     printf ("Not host information\n");
641 }
642
643 int
644 main (int argc, char **argv)
645 {
646   struct hostent *hp = gethostbyname (argv [1]);
647   struct hostent *cp;
648   struct in_addr addr;
649
650   print_host (hp);
651
652   if (hp)
653     {
654       cp = hostent_dup (hp);
655       print_host (cp);
656       free (cp);
657     }
658   printf ("127.0.0.1 == %s: %d\n", argv [1],
659           matchhostname ("127.0.0.1", argv [1]));
660   addr.s_addr = inet_addr(argv [2]);
661   printf ("%s\n", inet_ntoa (addr));
662   cp = get_hostent ((const char *)&addr, sizeof(addr), AF_INET);
663   print_host (cp);
664   return 0;
665 }
666 #endif