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