]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/statd/hostname.c
statd: statd_matchhostname() doesn't handle localhost properly
[nfs-utils.git] / utils / statd / hostname.c
1 /*
2  * Copyright 2009 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 /*
21  * NSM for Linux.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <sys/types.h>
29 #include <sys/socket.h>
30
31 #include <stdlib.h>
32 #include <stdbool.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <netdb.h>
36 #include <arpa/inet.h>
37
38 #include "sockaddr.h"
39 #include "statd.h"
40 #include "xlog.h"
41
42 /**
43  * statd_present_address - convert sockaddr to presentation address
44  * @sap: pointer to socket address to convert
45  * @buf: pointer to buffer to fill in
46  * @buflen: length of buffer
47  *
48  * Convert the passed-in sockaddr-style address to presentation format.
49  * The presentation format address is placed in @buf and is
50  * '\0'-terminated.
51  *
52  * Returns true if successful; otherwise false.
53  *
54  * getnameinfo(3) is preferred, since it can parse IPv6 scope IDs.
55  * An alternate version of statd_present_address() is available to
56  * handle older glibcs that do not have getnameinfo(3).
57  */
58 #ifdef HAVE_GETNAMEINFO
59 _Bool
60 statd_present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
61 {
62         socklen_t salen;
63         int error;
64
65         salen = nfs_sockaddr_length(sap);
66         if (salen == 0) {
67                 xlog(D_GENERAL, "%s: unsupported address family",
68                                 __func__);
69                 return false;
70         }
71
72         error = getnameinfo(sap, salen, buf, (socklen_t)buflen,
73                                                 NULL, 0, NI_NUMERICHOST);
74         if (error != 0) {
75                 xlog(D_GENERAL, "%s: getnameinfo(3): %s",
76                                 __func__, gai_strerror(error));
77                 return false;
78         }
79         return true;
80 }
81 #else   /* !HAVE_GETNAMEINFO */
82 _Bool
83 statd_present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
84 {
85         const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
86
87         if (sin->sin_family != AF_INET) {
88                 xlog(D_GENERAL, "%s: unsupported address family", __func__);
89                 return false;
90         }
91
92         /* ensure '\0' termination */
93         memset(buf, 0, buflen);
94
95         if (inet_ntop(AF_INET, (char *)&sin->sin_addr,
96                                         buf, (socklen_t)buflen) == NULL) {
97                 xlog(D_GENERAL, "%s: inet_ntop(3): %m", __func__);
98                 return false;
99         }
100         return true;
101 }
102 #endif  /* !HAVE_GETNAMEINFO */
103
104 /*
105  * Look up the hostname; report exceptional errors.  Caller must
106  * call freeaddrinfo(3) if a valid addrinfo is returned.
107  */
108 __attribute_malloc__
109 static struct addrinfo *
110 get_addrinfo(const char *hostname, const struct addrinfo *hint)
111 {
112         struct addrinfo *ai = NULL;
113         int error;
114
115         error = getaddrinfo(hostname, NULL, hint, &ai);
116         switch (error) {
117         case 0:
118                 return ai;
119         case EAI_NONAME:
120                 break;
121         default:
122                 xlog(D_GENERAL, "%s: failed to resolve host %s: %s",
123                                 __func__, hostname, gai_strerror(error));
124         }
125
126         return NULL;
127 }
128
129 #ifdef HAVE_GETNAMEINFO
130 static _Bool
131 get_nameinfo(const struct sockaddr *sap, const socklen_t salen,
132                 /*@out@*/ char *buf, const socklen_t buflen)
133 {
134         int error;
135
136         error = getnameinfo(sap, salen, buf, buflen, NULL, 0, NI_NAMEREQD);
137         if (error != 0) {
138                 xlog(D_GENERAL, "%s: failed to resolve address: %s",
139                                 __func__, gai_strerror(error));
140                 return false;
141         }
142
143         return true;
144 }
145 #else   /* !HAVE_GETNAMEINFO */
146 static _Bool
147 get_nameinfo(const struct sockaddr *sap,
148                 __attribute__ ((unused)) const socklen_t salen,
149                 /*@out@*/ char *buf, socklen_t buflen)
150 {
151         struct sockaddr_in *sin = (struct sockaddr_in *)(char *)sap;
152         struct hostent *hp;
153
154         if (sin->sin_family != AF_INET) {
155                 xlog(D_GENERAL, "%s: unknown address family: %d",
156                                 sin->sin_family);
157                 return false;
158         }
159
160         hp = gethostbyaddr((const char *)&(sin->sin_addr.s_addr),
161                                 sizeof(struct in_addr), AF_INET);
162         if (hp == NULL) {
163                 xlog(D_GENERAL, "%s: failed to resolve address: %m", __func__);
164                 return false;
165         }
166
167         strncpy(buf, hp->h_name, (size_t)buflen);
168         return true;
169 }
170 #endif  /* !HAVE_GETNAMEINFO */
171
172 /**
173  * statd_canonical_name - choose file name for monitor record files
174  * @hostname: C string containing hostname or presentation address
175  *
176  * Returns a '\0'-terminated ASCII string containing a fully qualified
177  * canonical hostname, or NULL if @hostname does not have a reverse
178  * mapping.  Caller must free the result with free(3).
179  *
180  * Incoming hostnames are looked up to determine the canonical hostname,
181  * and incoming presentation addresses are converted to canonical
182  * hostnames.
183  *
184  * We won't monitor peers that don't have a reverse map.  The canonical
185  * name gives us a key for our monitor list.
186  */
187 __attribute_malloc__
188 char *
189 statd_canonical_name(const char *hostname)
190 {
191         struct addrinfo hint = {
192 #ifdef IPV6_SUPPORTED
193                 .ai_family      = AF_UNSPEC,
194 #else   /* !IPV6_SUPPORTED */
195                 .ai_family      = AF_INET,
196 #endif  /* !IPV6_SUPPORTED */
197                 .ai_flags       = AI_NUMERICHOST,
198                 .ai_protocol    = (int)IPPROTO_UDP,
199         };
200         char buf[NI_MAXHOST];
201         struct addrinfo *ai;
202
203         ai = get_addrinfo(hostname, &hint);
204         if (ai != NULL) {
205                 /* @hostname was a presentation address */
206                 _Bool result;
207                 result = get_nameinfo(ai->ai_addr, ai->ai_addrlen,
208                                         buf, (socklen_t)sizeof(buf));
209                 freeaddrinfo(ai);
210                 if (!result)
211                         /* OK to use presentation address,
212                          * if no reverse map exists */
213                         return strdup(hostname);
214                 return strdup(buf);
215         }
216
217         /* @hostname was a hostname */
218         hint.ai_flags = AI_CANONNAME;
219         ai = get_addrinfo(hostname, &hint);
220         if (ai == NULL)
221                 return NULL;
222         strcpy(buf, ai->ai_canonname);
223         freeaddrinfo(ai);
224
225         return strdup(buf);
226 }
227
228 /*
229  * Take care to perform an explicit reverse lookup on presentation
230  * addresses.  Otherwise we don't get a real canonical name or a
231  * complete list of addresses.
232  *
233  * Returns an addrinfo list that has ai_canonname filled in, or
234  * NULL if some error occurs.  Caller must free the returned
235  * list with freeaddrinfo(3).
236  */
237 __attribute_malloc__
238 static struct addrinfo *
239 statd_canonical_list(const char *hostname)
240 {
241         struct addrinfo hint = {
242 #ifdef IPV6_SUPPORTED
243                 .ai_family      = AF_UNSPEC,
244 #else   /* !IPV6_SUPPORTED */
245                 .ai_family      = AF_INET,
246 #endif  /* !IPV6_SUPPORTED */
247                 .ai_flags       = AI_NUMERICHOST,
248                 .ai_protocol    = (int)IPPROTO_UDP,
249         };
250         char buf[NI_MAXHOST];
251         struct addrinfo *ai;
252
253         ai = get_addrinfo(hostname, &hint);
254         if (ai != NULL) {
255                 /* @hostname was a presentation address */
256                 _Bool result;
257                 result = get_nameinfo(ai->ai_addr, ai->ai_addrlen,
258                                         buf, (socklen_t)sizeof(buf));
259                 freeaddrinfo(ai);
260                 if (result)
261                         goto out;
262         }
263         /* @hostname was a hostname or had no reverse mapping */
264         strcpy(buf, hostname);
265
266 out:
267         hint.ai_flags = AI_CANONNAME;
268         return get_addrinfo(buf, &hint);
269 }
270
271 /**
272  * statd_matchhostname - check if two hostnames are equivalent
273  * @hostname1: C string containing hostname
274  * @hostname2: C string containing hostname
275  *
276  * Returns true if the hostnames are the same, the hostnames resolve
277  * to the same canonical name, or the hostnames resolve to at least
278  * one address that is the same.  False is returned if the hostnames
279  * do not match in any of these ways, if either hostname contains
280  * wildcard characters, if either hostname is a netgroup name, or
281  * if an error occurs.
282  */
283 _Bool
284 statd_matchhostname(const char *hostname1, const char *hostname2)
285 {
286         struct addrinfo *ai1, *ai2, *results1 = NULL, *results2 = NULL;
287         _Bool result = false;
288
289         if (strcasecmp(hostname1, hostname2) == 0) {
290                 result = true;
291                 goto out;
292         }
293
294         results1 = statd_canonical_list(hostname1);
295         if (results1 == NULL)
296                 goto out;
297         results2 = statd_canonical_list(hostname2);
298         if (results2 == NULL)
299                 goto out;
300
301         if (strcasecmp(results1->ai_canonname, results2->ai_canonname) == 0) {
302                 result = true;
303                 goto out;
304         }
305
306         for (ai1 = results1; ai1 != NULL; ai1 = ai1->ai_next)
307                 for (ai2 = results2; ai2 != NULL; ai2 = ai2->ai_next)
308                         if (nfs_compare_sockaddr(ai1->ai_addr, ai2->ai_addr)) {
309                                 result = true;
310                                 break;
311                         }
312
313 out:
314         freeaddrinfo(results2);
315         freeaddrinfo(results1);
316
317         xlog(D_CALL, "%s: hostnames %s and %s %s", __func__,
318                         hostname1, hostname2,
319                         (result ? "matched" : "did not match"));
320         return result;
321 }