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