getnfsargs.c revision 1.6 1 /* $NetBSD: getnfsargs.c,v 1.6 2006/07/07 17:25:01 hubertf Exp $ */
2
3 /*
4 * Copyright (c) 1992, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Rick Macklem at The University of Guelph.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #ifndef lint
37 __COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\n\
38 The Regents of the University of California. All rights reserved.\n");
39 #endif /* not lint */
40
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95";
44 #else
45 __RCSID("$NetBSD: getnfsargs.c,v 1.6 2006/07/07 17:25:01 hubertf Exp $");
46 #endif
47 #endif /* not lint */
48
49 #include <sys/param.h>
50 #include <sys/mount.h>
51 #include <sys/socket.h>
52 #include <sys/stat.h>
53 #include <syslog.h>
54
55 #include <rpc/rpc.h>
56 #include <rpc/pmap_clnt.h>
57 #include <rpc/pmap_prot.h>
58
59 #ifdef ISO
60 #include <netiso/iso.h>
61 #endif
62
63
64 #include <nfs/rpcv2.h>
65 #include <nfs/nfsproto.h>
66 #include <nfs/nfs.h>
67 #include <nfs/nqnfs.h>
68 #include <nfs/nfsmount.h>
69
70 #include <arpa/inet.h>
71
72 #include <ctype.h>
73 #include <err.h>
74 #include <errno.h>
75 #include <fcntl.h>
76 #include <netdb.h>
77 #include <signal.h>
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81 #include <unistd.h>
82 #include <util.h>
83
84 #include "mount_nfs.h"
85
86 struct nfhret {
87 u_long stat;
88 long vers;
89 long auth;
90 long fhsize;
91 u_char nfh[NFSX_V3FHMAX];
92 };
93
94 static int xdr_dir(XDR *, char *);
95 static int xdr_fh(XDR *, struct nfhret *);
96
97 int
98 getnfsargs(char *spec, struct nfs_args *nfsargsp)
99 {
100 CLIENT *clp;
101 struct addrinfo hints, *ai_nfs, *ai;
102 int ecode;
103 char host[NI_MAXHOST], serv[NI_MAXSERV];
104 static struct netbuf nfs_nb;
105 static struct sockaddr_storage nfs_ss;
106 struct netconfig *nconf;
107 const char *netid;
108 #ifdef ISO
109 static struct sockaddr_iso isoaddr;
110 struct iso_addr *isop;
111 int isoflag = 0;
112 #endif
113 struct timeval pertry, try;
114 enum clnt_stat clnt_stat;
115 int i, nfsvers, mntvers;
116 int retryleft;
117 char *hostp, *delimp;
118 static struct nfhret nfhret;
119 static char nam[MNAMELEN + 1];
120
121 strlcpy(nam, spec, sizeof(nam));
122 if ((delimp = strchr(spec, '@')) != NULL) {
123 hostp = delimp + 1;
124 } else if ((delimp = strrchr(spec, ':')) != NULL) {
125 hostp = spec;
126 spec = delimp + 1;
127 } else {
128 warnx("no <host>:<dirpath> or <dirpath>@<host> spec");
129 return (0);
130 }
131 *delimp = '\0';
132 /*
133 * DUMB!! Until the mount protocol works on iso transport, we must
134 * supply both an iso and an inet address for the host.
135 */
136 #ifdef ISO
137 if (!strncmp(hostp, "iso=", 4)) {
138 u_short isoport;
139
140 hostp += 4;
141 isoflag++;
142 if ((delimp = strchr(hostp, '+')) == NULL) {
143 warnx("no iso+inet address");
144 return (0);
145 }
146 *delimp = '\0';
147 if ((isop = iso_addr(hostp)) == NULL) {
148 warnx("bad ISO address");
149 return (0);
150 }
151 memset(&isoaddr, 0, sizeof (isoaddr));
152 memcpy(&isoaddr.siso_addr, isop, sizeof (struct iso_addr));
153 isoaddr.siso_len = sizeof (isoaddr);
154 isoaddr.siso_family = AF_ISO;
155 isoaddr.siso_tlen = 2;
156 isoport = htons(NFS_PORT);
157 memcpy(TSEL(&isoaddr), &isoport, isoaddr.siso_tlen);
158 hostp = delimp + 1;
159 }
160 #endif /* ISO */
161
162 /*
163 * Handle an internet host address and reverse resolve it if
164 * doing Kerberos.
165 */
166 memset(&hints, 0, sizeof hints);
167 hints.ai_flags = AI_NUMERICHOST;
168 hints.ai_socktype = nfsargsp->sotype;
169 if (getaddrinfo(hostp, "nfs", &hints, &ai_nfs) == 0) {
170 if ((nfsargsp->flags & NFSMNT_KERB)) {
171 hints.ai_flags = 0;
172 if ((ecode = getnameinfo(ai_nfs->ai_addr,
173 ai_nfs->ai_addrlen, host, sizeof(host),
174 serv, sizeof serv, 0)) != 0) {
175 warnx("can't reverse resolve net address for "
176 "host \"%s\": %s", hostp,
177 gai_strerror(ecode));
178 return (0);
179 }
180 hostp = host;
181 }
182 } else {
183 hints.ai_flags = 0;
184 if ((ecode = getaddrinfo(hostp, "nfs", &hints, &ai_nfs)) != 0) {
185 warnx("can't get net id for host \"%s\": %s", hostp,
186 gai_strerror(ecode));
187 return (0);
188 }
189 }
190
191 if ((nfsargsp->flags & NFSMNT_NFSV3) != 0) {
192 nfsvers = NFS_VER3;
193 mntvers = RPCMNT_VER3;
194 } else {
195 nfsvers = NFS_VER2;
196 mntvers = RPCMNT_VER1;
197 }
198 nfhret.stat = EACCES; /* Mark not yet successful */
199
200 for (ai = ai_nfs; ai; ai = ai->ai_next) {
201 /*
202 * XXX. Nead a generic (family, type, proto) -> nconf interface.
203 * __rpc_*2nconf exist, maybe they should be exported.
204 */
205 if (nfsargsp->sotype == SOCK_STREAM) {
206 if (ai->ai_family == AF_INET6)
207 netid = "tcp6";
208 else
209 netid = "tcp";
210 } else {
211 if (ai->ai_family == AF_INET6)
212 netid = "udp6";
213 else
214 netid = "udp";
215 }
216
217 nconf = getnetconfigent(netid);
218
219 tryagain:
220 retryleft = retrycnt;
221
222 while (retryleft > 0) {
223 nfs_nb.buf = &nfs_ss;
224 nfs_nb.maxlen = sizeof nfs_ss;
225 if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, hostp)){
226 if (rpc_createerr.cf_stat == RPC_SYSTEMERROR) {
227 nfhret.stat = rpc_createerr.cf_error.re_errno;
228 break;
229 }
230 if (rpc_createerr.cf_stat == RPC_UNKNOWNPROTO) {
231 nfhret.stat = EPROTONOSUPPORT;
232 break;
233 }
234 if ((opflags & ISBGRND) == 0)
235 clnt_pcreateerror(
236 "mount_nfs: rpcbind to nfs on server");
237 } else {
238 pertry.tv_sec = 10;
239 pertry.tv_usec = 0;
240 /*
241 * XXX relies on clnt_tcp_create to bind to a reserved
242 * socket.
243 */
244 clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers,
245 mnttcp_ok ? nconf : getnetconfigent("udp"));
246 if (clp == NULL) {
247 if ((opflags & ISBGRND) == 0) {
248 clnt_pcreateerror(
249 "Cannot MNT RPC (mountd)");
250 }
251 } else {
252 CLNT_CONTROL(clp, CLSET_RETRY_TIMEOUT,
253 (char *)&pertry);
254 clp->cl_auth = authsys_create_default();
255 try.tv_sec = 10;
256 try.tv_usec = 0;
257 if (nfsargsp->flags & NFSMNT_KERB)
258 nfhret.auth = RPCAUTH_KERB4;
259 else
260 nfhret.auth = RPCAUTH_UNIX;
261 nfhret.vers = mntvers;
262 clnt_stat = clnt_call(clp, RPCMNT_MOUNT,
263 xdr_dir, spec, xdr_fh, &nfhret, try);
264 switch (clnt_stat) {
265 case RPC_PROGVERSMISMATCH:
266 if (nfsvers == NFS_VER3 && !force3) {
267 nfsvers = NFS_VER2;
268 mntvers = RPCMNT_VER1;
269 nfsargsp->flags &=
270 ~NFSMNT_NFSV3;
271 goto tryagain;
272 } else {
273 errx(1, "%s", clnt_sperror(clp,
274 "MNT RPC"));
275 }
276 case RPC_SUCCESS:
277 auth_destroy(clp->cl_auth);
278 clnt_destroy(clp);
279 retryleft = 0;
280 break;
281 default:
282 /* XXX should give up on some errors */
283 if ((opflags & ISBGRND) == 0)
284 warnx("%s", clnt_sperror(clp,
285 "bad MNT RPC"));
286 break;
287 }
288 }
289 }
290 if (--retryleft > 0) {
291 if (opflags & BGRND) {
292 opflags &= ~BGRND;
293 if ((i = fork()) != 0) {
294 if (i == -1)
295 err(1, "nqnfs 2");
296 exit(0);
297 }
298 (void) setsid();
299 (void) close(STDIN_FILENO);
300 (void) close(STDOUT_FILENO);
301 (void) close(STDERR_FILENO);
302 (void) chdir("/");
303 opflags |= ISBGRND;
304 }
305 sleep(60);
306 }
307 }
308 if (nfhret.stat == 0)
309 break;
310 }
311 freeaddrinfo(ai_nfs);
312 if (nfhret.stat) {
313 if (opflags & ISBGRND)
314 exit(1);
315 errno = nfhret.stat;
316 warnx("can't access %s: %s", spec, strerror(nfhret.stat));
317 return (0);
318 }
319 #ifdef ISO
320 if (isoflag) {
321 nfsargsp->addr = (struct sockaddr *) &isoaddr;
322 nfsargsp->addrlen = sizeof (isoaddr);
323 } else
324 #endif /* ISO */
325 {
326 nfsargsp->addr = (struct sockaddr *) nfs_nb.buf;
327 nfsargsp->addrlen = nfs_nb.len;
328 if (port != 0) {
329 struct sockaddr *sa = nfsargsp->addr;
330 switch (sa->sa_family) {
331 case AF_INET:
332 ((struct sockaddr_in *)sa)->sin_port = port;
333 break;
334 #ifdef INET6
335 case AF_INET6:
336 ((struct sockaddr_in6 *)sa)->sin6_port = port;
337 break;
338 #endif
339 default:
340 errx(1, "Unsupported socket family %d",
341 sa->sa_family);
342 }
343 }
344 }
345 nfsargsp->fh = nfhret.nfh;
346 nfsargsp->fhsize = nfhret.fhsize;
347 nfsargsp->hostname = nam;
348 return (1);
349 }
350
351 /*
352 * xdr routines for mount rpc's
353 */
354 static int
355 xdr_dir(XDR *xdrsp, char *dirp)
356 {
357 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
358 }
359
360 static int
361 xdr_fh(XDR *xdrsp, struct nfhret *np)
362 {
363 int i;
364 long auth, authcnt, authfnd = 0;
365
366 if (!xdr_u_long(xdrsp, &np->stat))
367 return (0);
368 if (np->stat)
369 return (1);
370 switch (np->vers) {
371 case 1:
372 np->fhsize = NFSX_V2FH;
373 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH));
374 case 3:
375 if (!xdr_long(xdrsp, &np->fhsize))
376 return (0);
377 if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX)
378 return (0);
379 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
380 return (0);
381 if (!xdr_long(xdrsp, &authcnt))
382 return (0);
383 for (i = 0; i < authcnt; i++) {
384 if (!xdr_long(xdrsp, &auth))
385 return (0);
386 if (auth == np->auth)
387 authfnd++;
388 }
389 /*
390 * Some servers, such as DEC's OSF/1 return a nil authenticator
391 * list to indicate RPCAUTH_UNIX.
392 */
393 if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX))
394 np->stat = EAUTH;
395 return (1);
396 };
397 return (0);
398 }
399