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