]> git.decadent.org.uk Git - nfs-utils.git/blob - support/export/hostname.c
232e04038daaa8f5b0c8592369ee5deff3a99c9e
[nfs-utils.git] / support / export / hostname.c
1 /*
2  * Copyright 2010 Oracle.  All rights reserved.
3  *
4  * This file is part of nfs-utils.
5  *
6  * nfs-utils 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.
10  *
11  * nfs-utils is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with nfs-utils.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <string.h>
25 #include <stdlib.h>
26 #include <arpa/inet.h>
27 #include <netdb.h>
28 #include <errno.h>
29
30 #include "sockaddr.h"
31 #include "exportfs.h"
32
33 #ifndef HAVE_DECL_AI_ADDRCONFIG
34 #define AI_ADDRCONFIG   0
35 #endif
36
37 #ifdef HAVE_GETNAMEINFO
38 static socklen_t
39 sockaddr_size(const struct sockaddr *sap)
40 {
41         if (sap->sa_family != AF_INET)
42                 return 0;
43         return (socklen_t)sizeof(struct sockaddr_in);
44 }
45 #endif  /* HAVE_GETNAMEINFO */
46
47 /**
48  * host_ntop - generate presentation address given a sockaddr
49  * @sap: pointer to socket address
50  * @buf: working storage
51  * @buflen: size of @buf in bytes
52  *
53  * Returns a pointer to a @buf.
54  */
55 #ifdef HAVE_GETNAMEINFO
56 char *
57 host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
58 {
59         socklen_t salen = sockaddr_size(sap);
60         int error;
61
62         memset(buf, 0, buflen);
63
64         if (salen == 0) {
65                 (void)strncpy(buf, "bad family", buflen - 1);
66                 return buf;
67         }
68
69         error = getnameinfo(sap, salen, buf, (socklen_t)buflen,
70                                                 NULL, 0, NI_NUMERICHOST);
71         if (error != 0) {
72                 buf[0] = '\0';
73                 (void)strncpy(buf, "bad address", buflen - 1);
74         }
75
76         return buf;
77 }
78 #else   /* !HAVE_GETNAMEINFO */
79 char *
80 host_ntop(const struct sockaddr *sap, char *buf, const size_t buflen)
81 {
82         const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
83
84         memset(buf, 0, buflen);
85
86         if (sin->sin_family != AF_INET)
87                 (void)strncpy(buf, "bad family", buflen - 1);
88                 return buf;
89         }
90
91         if (inet_ntop(AF_INET, &sin->sin_addr.s_addr, buf, buflen) != NULL)
92                 return buf;
93
94         buf[0] = '\0';
95         (void)strncpy(buf, "bad address", buflen - 1);
96         return buf;
97 }
98 #endif  /* !HAVE_GETNAMEINFO */
99
100 /**
101  * host_pton - return addrinfo for a given presentation address
102  * @paddr: pointer to a '\0'-terminated ASCII string containing an
103  *              IP presentation address
104  *
105  * Returns address info structure, or NULL if an error occurs.  Caller
106  * must free the returned structure with freeaddrinfo(3).
107  */
108 __attribute_malloc__
109 struct addrinfo *
110 host_pton(const char *paddr)
111 {
112         struct addrinfo *ai = NULL;
113         struct addrinfo hint = {
114                 /* don't return duplicates */
115                 .ai_protocol    = (int)IPPROTO_UDP,
116                 .ai_flags       = AI_NUMERICHOST,
117                 .ai_family      = AF_UNSPEC,
118         };
119         struct sockaddr_in sin;
120         int error;
121
122         /*
123          * Although getaddrinfo(3) is easier to use and supports
124          * IPv6, it recognizes incomplete addresses like "10.4"
125          * as valid AF_INET addresses.  It also accepts presentation
126          * addresses that end with a blank.
127          *
128          * inet_pton(3) is much stricter.  Use it to be certain we
129          * have a real AF_INET presentation address, before invoking
130          * getaddrinfo(3) to generate the full addrinfo list.
131          */
132         if (inet_pton(AF_INET, paddr, &sin.sin_addr) == 0)
133                 return NULL;
134
135         error = getaddrinfo(paddr, NULL, &hint, &ai);
136         switch (error) {
137         case 0:
138                 return ai;
139         case EAI_NONAME:
140                 if (paddr == NULL)
141                         xlog(D_GENERAL, "%s: passed a NULL presentation address",
142                                 __func__);
143                 break;
144         case EAI_SYSTEM:
145                 xlog(D_GENERAL, "%s: failed to convert %s: (%d) %m",
146                                 __func__, paddr, errno);
147                 break;
148         default:
149                 xlog(D_GENERAL, "%s: failed to convert %s: %s",
150                                 __func__, paddr, gai_strerror(error));
151                 break;
152         }
153
154         return NULL;
155 }
156
157 /**
158  * host_addrinfo - return addrinfo for a given hostname
159  * @hostname: pointer to a '\0'-terminated ASCII string containing a hostname
160  *
161  * Returns address info structure with ai_canonname filled in, or NULL
162  * if no information is available for @hostname.  Caller must free the
163  * returned structure with freeaddrinfo(3).
164  */
165 __attribute_malloc__
166 struct addrinfo *
167 host_addrinfo(const char *hostname)
168 {
169         struct addrinfo *ai = NULL;
170         struct addrinfo hint = {
171                 .ai_family      = AF_INET,
172                 /* don't return duplicates */
173                 .ai_protocol    = (int)IPPROTO_UDP,
174                 .ai_flags       = AI_ADDRCONFIG | AI_CANONNAME,
175         };
176         int error;
177
178         error = getaddrinfo(hostname, NULL, &hint, &ai);
179         switch (error) {
180         case 0:
181                 return ai;
182         case EAI_SYSTEM:
183                 xlog(D_GENERAL, "%s: failed to resolve %s: (%d) %m",
184                                 __func__, hostname, errno);
185                 break;
186         default:
187                 xlog(D_GENERAL, "%s: failed to resolve %s: %s",
188                                 __func__, hostname, gai_strerror(error));
189                 break;
190         }
191
192         return NULL;
193 }
194
195 /**
196  * host_canonname - return canonical hostname bound to an address
197  * @sap: pointer to socket address to look up
198  *
199  * Discover the canonical hostname associated with the given socket
200  * address.  The host's reverse mapping is verified in the process.
201  *
202  * Returns a '\0'-terminated ASCII string containing a hostname, or
203  * NULL if no hostname can be found for @sap.  Caller must free
204  * the string.
205  */
206 #ifdef HAVE_GETNAMEINFO
207 __attribute_malloc__
208 char *
209 host_canonname(const struct sockaddr *sap)
210 {
211         socklen_t salen = sockaddr_size(sap);
212         char buf[NI_MAXHOST];
213         int error;
214
215         if (salen == 0) {
216                 xlog(D_GENERAL, "%s: unsupported address family %d",
217                                 __func__, sap->sa_family);
218                 return NULL;
219         }
220
221         memset(buf, 0, sizeof(buf));
222         error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
223                                                         NULL, 0, NI_NAMEREQD);
224         switch (error) {
225         case 0:
226                 break;
227         case EAI_SYSTEM:
228                 xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m",
229                                 __func__, errno);
230                 return NULL;
231         default:
232                 (void)getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
233                                                         NULL, 0, NI_NUMERICHOST);
234                 xlog(D_GENERAL, "%s: failed to resolve %s: %s",
235                                 __func__, buf, gai_strerror(error));
236                 return NULL;
237         }
238
239         return strdup(buf);
240 }
241 #else   /* !HAVE_GETNAMEINFO */
242 __attribute_malloc__
243 char *
244 host_canonname(const struct sockaddr *sap)
245 {
246         const struct sockaddr_in *sin = (const struct sockaddr_in *)(char *)sap;
247         const struct in_addr *addr = &sin->sin_addr;
248         struct hostent *hp;
249
250         if (sap->sa_family != AF_INET)
251                 return NULL;
252
253         hp = gethostbyaddr(addr, (socklen_t)sizeof(addr), AF_INET);
254         if (hp == NULL)
255                 return NULL;
256
257         return strdup(hp->h_name);
258 }
259 #endif  /* !HAVE_GETNAMEINFO */
260
261 /**
262  * host_reliable_addrinfo - return addrinfo for a given address
263  * @sap: pointer to socket address to look up
264  *
265  * Reverse and forward lookups are performed to ensure the address has
266  * proper forward and reverse mappings.
267  *
268  * Returns address info structure with ai_canonname filled in, or NULL
269  * if no information is available for @sap.  Caller must free the returned
270  * structure with freeaddrinfo(3).
271  */
272 __attribute_malloc__
273 struct addrinfo *
274 host_reliable_addrinfo(const struct sockaddr *sap)
275 {
276         struct addrinfo *ai;
277         char *hostname;
278
279         hostname = host_canonname(sap);
280         if (hostname == NULL)
281                 return NULL;
282
283         ai = host_addrinfo(hostname);
284
285         free(hostname);
286         return ai;
287 }
288
289 /**
290  * host_numeric_addrinfo - return addrinfo without doing DNS queries
291  * @sap: pointer to socket address
292  *
293  * Returns address info structure, or NULL if an error occurred.
294  * Caller must free the returned structure with freeaddrinfo(3).
295  */
296 #ifdef HAVE_GETNAMEINFO
297 __attribute_malloc__
298 struct addrinfo *
299 host_numeric_addrinfo(const struct sockaddr *sap)
300 {
301         socklen_t salen = sockaddr_size(sap);
302         char buf[INET_ADDRSTRLEN];
303         struct addrinfo *ai;
304         int error;
305
306         if (salen == 0) {
307                 xlog(D_GENERAL, "%s: unsupported address family %d",
308                                 __func__, sap->sa_family);
309                 return NULL;
310         }
311
312         memset(buf, 0, sizeof(buf));
313         error = getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
314                                                 NULL, 0, NI_NUMERICHOST);
315         switch (error) {
316         case 0:
317                 break;
318         case EAI_SYSTEM:
319                 xlog(D_GENERAL, "%s: getnameinfo(3) failed: (%d) %m",
320                                 __func__, errno);
321                 return NULL;
322         default:
323                 xlog(D_GENERAL, "%s: getnameinfo(3) failed: %s",
324                                 __func__, gai_strerror(error));
325                 return NULL;
326         }
327
328         ai = host_pton(buf);
329
330         /*
331          * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname
332          */
333         if (ai != NULL) {
334                 free(ai->ai_canonname);         /* just in case */
335                 ai->ai_canonname = strdup(buf);
336                 if (ai->ai_canonname == NULL) {
337                         freeaddrinfo(ai);
338                         ai = NULL;
339                 }
340         }
341
342         return ai;
343 }
344 #else   /* !HAVE_GETNAMEINFO */
345 __attribute_malloc__
346 struct addrinfo *
347 host_numeric_addrinfo(const struct sockaddr *sap)
348 {
349         const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
350         const struct in_addr *addr = &sin->sin_addr;
351         char buf[INET_ADDRSTRLEN];
352         struct addrinfo *ai;
353
354         if (sap->sa_family != AF_INET)
355                 return NULL;
356
357         memset(buf, 0, sizeof(buf));
358         if (inet_ntop(AF_INET, (char *)addr, buf,
359                                         (socklen_t)sizeof(buf)) == NULL)
360                 return NULL;
361
362         ai = host_pton(buf);
363
364         /*
365          * getaddrinfo(AI_NUMERICHOST) never fills in ai_canonname
366          */
367         if (ai != NULL) {
368                 ai->ai_canonname = strdup(buf);
369                 if (ai->ai_canonname == NULL) {
370                         freeaddrinfo(ai);
371                         ai = NULL;
372                 }
373         }
374
375         return ai;
376 }
377 #endif  /* !HAVE_GETNAMEINFO */