1fd42697f04ab812f5e6e858d7cc73b9cc32d08a
[nfs-utils.git] / support / export / client.c
1 /*
2  * support/export/client.c
3  *
4  * Maintain list of nfsd clients.
5  *
6  * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
7  */
8
9 #include "config.h"
10
11 #include <sys/types.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <netdb.h>
17 #include "xmalloc.h"
18 #include "misc.h"
19 #include "nfslib.h"
20 #include "exportfs.h"
21
22 /* netgroup stuff never seems to be defined in any header file. Linux is
23  * not alone in this.
24  */
25 #if !defined(__GLIBC__) || __GLIBC__ < 2
26 extern int      innetgr(char *netgr, char *host, char *, char *);
27 #endif
28 static void     client_init(nfs_client *clp, const char *hname,
29                                         struct hostent *hp);
30 static int      client_checkaddr(nfs_client *clp, struct in_addr addr);
31
32 nfs_client      *clientlist[MCL_MAXTYPES] = { NULL, };
33
34
35 nfs_client *
36 client_lookup(char *hname)
37 {
38         nfs_client      *clp = NULL;
39         int             htype;
40         struct hostent  *hp = NULL;
41
42         htype = client_gettype(hname);
43
44         if (htype == MCL_FQDN) {
45                 hp = gethostbyname(hname);
46                 if (hp == NULL || hp->h_addrtype != AF_INET) {
47                         xlog(L_ERROR, "%s has non-inet addr", hname);
48                         return NULL;
49                 }
50                 hp = hostent_dup (hp);
51                 hname = (char *) hp->h_name;
52
53                 for (clp = clientlist[htype]; clp; clp = clp->m_next) {
54                         if (client_check(clp, hp))
55                                 break;
56                 }
57         } else {
58                 for (clp = clientlist[htype]; clp; clp = clp->m_next) {
59                         if (strcmp(hname, clp->m_hostname)==0)
60                                 break;
61                 }
62         }
63
64         if (!clp) {
65                 clp = (nfs_client *) xmalloc(sizeof(*clp));
66                 memset(clp, 0, sizeof(*clp));
67                 clp->m_type = htype;
68                 client_init(clp, hname, NULL);
69                 client_add(clp);
70         }
71
72         if (htype == MCL_FQDN && clp->m_naddr == 0 && hp != NULL) {
73                 char    **ap = hp->h_addr_list;
74                 int     i;
75
76                 for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++)
77                         clp->m_addrlist[i] = *(struct in_addr *)*ap;
78                 clp->m_naddr = i;
79         }
80
81         if (hp)
82                 free (hp);
83
84         return clp;
85 }
86
87 nfs_client *
88 client_dup(nfs_client *clp, struct hostent *hp)
89 {
90         nfs_client              *new;
91
92         new = (nfs_client *) xmalloc(sizeof(*new));
93         memcpy(new, clp, sizeof(*new));
94         new->m_type = MCL_FQDN;
95
96         client_init(new, (char *) hp->h_name, hp);
97         client_add(new);
98         return new;
99 }
100
101 static void
102 client_init(nfs_client *clp, const char *hname, struct hostent *hp)
103 {
104         if (hp) {
105                 strncpy(clp->m_hostname, hp->h_name,
106                         sizeof (clp->m_hostname) -  1);
107         } else {
108                 strncpy(clp->m_hostname, hname,
109                         sizeof (clp->m_hostname) - 1);
110         }
111         clp->m_hostname[sizeof (clp->m_hostname) - 1] = '\0';
112
113         clp->m_exported = 0;
114         clp->m_count = 0;
115
116         if (clp->m_type == MCL_SUBNETWORK) {
117                 char    *cp = strchr(clp->m_hostname, '/');
118
119                 *cp = '\0';
120                 clp->m_addrlist[0].s_addr = inet_addr(clp->m_hostname);
121                 if (strchr(cp + 1, '.')) {
122                         clp->m_addrlist[1].s_addr = inet_addr(cp+1);
123                 }
124                 else {
125                         int netmask = atoi(cp + 1);
126                         if (0 < netmask && netmask <= 32) {
127                                 clp->m_addrlist[1].s_addr =
128                                         htonl ((uint32_t) ~0 << (32 - netmask));
129                         }
130                         else {
131                                 xlog(L_FATAL, "invalid netmask `%s' for %s",
132                                      cp + 1, clp->m_hostname);
133                         }
134                 }
135                 *cp = '/';
136                 clp->m_naddr = 0;
137         } else if (!hp) {
138                 clp->m_naddr = 0;
139         } else {
140                 char    **ap = hp->h_addr_list;
141                 int     i;
142
143                 for (i = 0; *ap && i < NFSCLNT_ADDRMAX; i++, ap++) {
144                         clp->m_addrlist[i] = *(struct in_addr *)*ap;
145                 }
146                 clp->m_naddr = i;
147         }
148 }
149
150 void
151 client_add(nfs_client *clp)
152 {
153         nfs_client      **cpp;
154
155         if (clp->m_type < 0 || clp->m_type >= MCL_MAXTYPES)
156                 xlog(L_FATAL, "unknown client type in client_add");
157         cpp = clientlist + clp->m_type;
158         while (*cpp)
159                 cpp = &((*cpp)->m_next);
160         clp->m_next = NULL;
161         *cpp = clp;
162 }
163
164 void
165 client_release(nfs_client *clp)
166 {
167         if (clp->m_count <= 0)
168                 xlog(L_FATAL, "client_free: m_count <= 0!");
169         clp->m_count--;
170 }
171
172 void
173 client_freeall(void)
174 {
175         nfs_client      *clp, **head;
176         int             i;
177
178         for (i = 0; i < MCL_MAXTYPES; i++) {
179                 head = clientlist + i;
180                 while (*head) {
181                         *head = (clp = *head)->m_next;
182                         xfree(clp);
183                 }
184         }
185 }
186
187 nfs_client *
188 client_find(struct hostent *hp)
189 {
190         nfs_client      *clp;
191         int             i;
192
193         for (i = 0; i < MCL_MAXTYPES; i++) {
194                 for (clp = clientlist[i]; clp; clp = clp->m_next) {
195                         if (!client_check(clp, hp))
196                                 continue;
197 #ifdef notdef
198                         if (clp->m_type == MCL_FQDN)
199                                 return clp;
200                         return client_dup(clp, hp);
201 #else
202                         return clp;
203 #endif
204                 }
205         }
206         return NULL;
207 }
208
209 /*
210  * Match a host (given its hostent record) to a client record. This
211  * is usually called from mountd.
212  */
213 int
214 client_check(nfs_client *clp, struct hostent *hp)
215 {
216         char    *hname = (char *) hp->h_name;
217         char    *cname = clp->m_hostname;
218         char    **ap;
219
220         switch (clp->m_type) {
221         case MCL_FQDN:
222         case MCL_SUBNETWORK:
223                 for (ap = hp->h_addr_list; *ap; ap++) {
224                         if (client_checkaddr(clp, *(struct in_addr *) *ap))
225                                 return 1;
226                 }
227                 return 0;
228         case MCL_WILDCARD:
229                 if (wildmat(hname, cname))
230                         return 1;
231                 else {
232                         for (ap = hp->h_aliases; *ap; ap++)
233                                 if (wildmat(*ap, cname))
234                                         return 1;
235                 }
236                 return 0;
237         case MCL_NETGROUP:
238 #ifdef HAVE_INNETGR
239                 {
240                         char    *dot;
241                         int     match;
242                         struct hostent *nhp = NULL;
243                         struct sockaddr_in addr;
244
245                         /* First, try to match the hostname without
246                          * splitting off the domain */
247                         if (innetgr(cname+1, hname, NULL, NULL))
248                                 return 1;
249
250                         /* If hname is ip address convert to FQDN */
251                         if (inet_aton(hname, &addr.sin_addr) &&
252                            (nhp = gethostbyaddr((const char *)&(addr.sin_addr),
253                             sizeof(addr.sin_addr), AF_INET))) {
254                                 hname = (char *)nhp->h_name;
255                                 if (innetgr(cname+1, hname, NULL, NULL))
256                                         return 1;
257                         }
258
259                         /* Okay, strip off the domain (if we have one) */
260                         if ((dot = strchr(hname, '.')) == NULL)
261                                 return 0;
262
263                         *dot = '\0';
264                         match = innetgr(cname+1, hname, NULL, NULL);
265                         *dot = '.';
266
267                         return match;
268                 }
269 #else
270                 return 0;
271 #endif
272         case MCL_ANONYMOUS:
273                 return 1;
274         default:
275                 xlog(L_FATAL, "internal: bad client type %d", clp->m_type);
276         }
277
278         return 0;
279 }
280
281 static int
282 client_checkaddr(nfs_client *clp, struct in_addr addr)
283 {
284         int     i;
285
286         switch (clp->m_type) {
287         case MCL_FQDN:
288                 for (i = 0; i < clp->m_naddr; i++) {
289                         if (clp->m_addrlist[i].s_addr == addr.s_addr)
290                                 return 1;
291                 }
292                 return 0;
293         case MCL_SUBNETWORK:
294                 return !((clp->m_addrlist[0].s_addr ^ addr.s_addr)
295                         & clp->m_addrlist[1].s_addr);
296         }
297         return 0;
298 }
299
300 int
301 client_gettype(char *ident)
302 {
303         char    *sp;
304
305         if (ident[0] == '\0' || strcmp(ident, "*")==0)
306                 return MCL_ANONYMOUS;
307         if (ident[0] == '@') {
308 #ifndef HAVE_INNETGR
309                 xlog(L_WARNING, "netgroup support not compiled in");
310 #endif
311                 return MCL_NETGROUP;
312         }
313         for (sp = ident; *sp; sp++) {
314                 if (*sp == '*' || *sp == '?' || *sp == '[')
315                         return MCL_WILDCARD;
316                 if (*sp == '/')
317                         return MCL_SUBNETWORK;
318                 if (*sp == '\\' && sp[1])
319                         sp++;
320         }
321         return MCL_FQDN;
322 }