xref: /netbsd/sbin/mount_nfs/getnfsargs.c (revision 87b33420)
1 /*	$NetBSD: getnfsargs.c,v 1.18 2017/02/05 00:24:24 christos 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\
38  The Regents of the University of California.  All rights reserved.");
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.18 2017/02/05 00:24:24 christos 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 #include <nfs/rpcv2.h>
60 #include <nfs/nfsproto.h>
61 #include <nfs/nfs.h>
62 #include <nfs/nfsmount.h>
63 
64 #include <arpa/inet.h>
65 
66 #include <err.h>
67 #include <errno.h>
68 #include <fcntl.h>
69 #include <netdb.h>
70 #include <signal.h>
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include <unistd.h>
75 #include <util.h>
76 
77 #include "mount_nfs.h"
78 
79 int retrycnt = DEF_RETRY;
80 int opflags = 0;
81 int force2 = 0;
82 int force3 = 0;
83 int mnttcp_ok = 1;
84 int port = 0;
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 #ifndef MOUNTNFS_RETRYRPC
98 #define MOUNTNFS_RETRYRPC 60
99 #endif
100 
101 int
getnfsargs(char * spec,struct nfs_args * nfsargsp)102 getnfsargs(char *spec, struct nfs_args *nfsargsp)
103 {
104 	CLIENT *clp;
105 	struct addrinfo hints, *ai_nfs, *ai;
106 	int ecode;
107 	static struct netbuf nfs_nb;
108 	static struct sockaddr_storage nfs_ss;
109 	struct netconfig *nconf;
110 	const char *netid;
111 	struct timeval pertry, try;
112 	enum clnt_stat clnt_stat;
113 	int i, nfsvers, mntvers;
114 	int retryleft;
115 	char *hostp, *delimp;
116 	static struct nfhret nfhret;
117 	static char nam[MNAMELEN + 1];
118 
119 	strlcpy(nam, spec, sizeof(nam));
120 	if ((delimp = strchr(spec, '@')) != NULL) {
121 		hostp = delimp + 1;
122 	} else if ((delimp = strrchr(spec, ':')) != NULL) {
123 		hostp = spec;
124 		spec = delimp + 1;
125 	} else {
126 		warnx("no <host>:<dirpath> or <dirpath>@<host> spec");
127 		return (0);
128 	}
129 	*delimp = '\0';
130 
131 	/*
132 	 * Handle an internet host address.
133 	 */
134 	memset(&hints, 0, sizeof hints);
135 	hints.ai_flags = AI_NUMERICHOST;
136 	hints.ai_socktype = nfsargsp->sotype;
137 	if (getaddrinfo(hostp, "nfs", &hints, &ai_nfs) != 0) {
138 		hints.ai_flags = 0;
139 		if ((ecode = getaddrinfo(hostp, "nfs", &hints, &ai_nfs)) != 0) {
140 			warnx("can't get net id for host \"%s\": %s", hostp,
141 			    gai_strerror(ecode));
142 			return (0);
143 		}
144 	}
145 
146 	if ((nfsargsp->flags & NFSMNT_NFSV3) != 0) {
147 		nfsvers = NFS_VER3;
148 		mntvers = RPCMNT_VER3;
149 	} else {
150 		nfsvers = NFS_VER2;
151 		mntvers = RPCMNT_VER1;
152 	}
153 	nfhret.stat = EACCES;	/* Mark not yet successful */
154 
155     for (ai = ai_nfs; ai; ai = ai->ai_next) {
156 	/*
157 	 * XXX. Need a generic (family, type, proto) -> nconf interface.
158 	 * __rpc_*2nconf exist, maybe they should be exported.
159 	 */
160 	if (nfsargsp->sotype == SOCK_STREAM) {
161 		if (ai->ai_family == AF_INET6)
162 			netid = "tcp6";
163 		else
164 			netid = "tcp";
165 	} else {
166 		if (ai->ai_family == AF_INET6)
167 			netid = "udp6";
168 		else
169 			netid = "udp";
170 	}
171 
172 	nconf = getnetconfigent(netid);
173 
174 tryagain:
175 	retryleft = retrycnt;
176 
177 	while (retryleft > 0) {
178 		nfs_nb.buf = &nfs_ss;
179 		nfs_nb.maxlen = sizeof nfs_ss;
180 		if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb, hostp)){
181 			if (rpc_createerr.cf_stat == RPC_SYSTEMERROR) {
182 				nfhret.stat = rpc_createerr.cf_error.re_errno;
183 				break;
184 			}
185 			if (rpc_createerr.cf_stat == RPC_UNKNOWNPROTO) {
186 				nfhret.stat = EPROTONOSUPPORT;
187 				break;
188 			}
189 			if ((opflags & ISBGRND) == 0) {
190 				char buf[64];
191 
192 				snprintf(buf, sizeof(buf),
193 				    "%s: rpcbind to nfs on server",
194 				    getprogname());
195 				clnt_pcreateerror(buf);
196 			}
197 		} else {
198 			pertry.tv_sec = 30;
199 			pertry.tv_usec = 0;
200 			/*
201 			 * XXX relies on clnt_tcp_create to bind to a reserved
202 			 * socket.
203 			 */
204 			clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers,
205 			     mnttcp_ok ? nconf : getnetconfigent(netid));
206 			if (clp == NULL) {
207 				if ((opflags & ISBGRND) == 0) {
208 					clnt_pcreateerror(
209 					    "Cannot MNT RPC (mountd)");
210 				}
211 			} else {
212 				CLNT_CONTROL(clp, CLSET_RETRY_TIMEOUT,
213 				    (char *)&pertry);
214 				clp->cl_auth = authsys_create_default();
215 				try.tv_sec = 30;
216 				try.tv_usec = 0;
217 				nfhret.auth = RPCAUTH_UNIX;
218 				nfhret.vers = mntvers;
219 				clnt_stat = clnt_call(clp, RPCMNT_MOUNT,
220 				    xdr_dir, spec, xdr_fh, &nfhret, try);
221 				switch (clnt_stat) {
222 				case RPC_PROGVERSMISMATCH:
223 					if (nfsvers == NFS_VER3 && !force3) {
224 						nfsvers = NFS_VER2;
225 						mntvers = RPCMNT_VER1;
226 						nfsargsp->flags &=
227 							~NFSMNT_NFSV3;
228 						goto tryagain;
229 					} else {
230 						errx(1, "%s", clnt_sperror(clp,
231 							"MNT RPC"));
232 					}
233 				case RPC_SUCCESS:
234 					auth_destroy(clp->cl_auth);
235 					clnt_destroy(clp);
236 					retryleft = 0;
237 					break;
238 				default:
239 					/* XXX should give up on some errors */
240 					if ((opflags & ISBGRND) == 0)
241 						warnx("%s", clnt_sperror(clp,
242 						    "bad MNT RPC"));
243 					break;
244 				}
245 			}
246 		}
247 		if (--retryleft > 0) {
248 			if (opflags & BGRND) {
249 				opflags &= ~BGRND;
250 				if ((i = fork()) != 0) {
251 					if (i == -1)
252 						err(1, "fork");
253 					exit(0);
254 				}
255 				(void) setsid();
256 				(void) close(STDIN_FILENO);
257 				(void) close(STDOUT_FILENO);
258 				(void) close(STDERR_FILENO);
259 				(void) chdir("/");
260 				opflags |= ISBGRND;
261 			}
262 			sleep(MOUNTNFS_RETRYRPC);
263 		}
264 	}
265 	if (nfhret.stat == 0)
266 		break;
267     }
268 	freeaddrinfo(ai_nfs);
269 	if (nfhret.stat) {
270 		if (opflags & ISBGRND)
271 			exit(1);
272 		errno = nfhret.stat;
273 		warnx("can't access %s: %s", spec, strerror(nfhret.stat));
274 		return (0);
275 	} else {
276 		nfsargsp->addr = (struct sockaddr *) nfs_nb.buf;
277 		nfsargsp->addrlen = nfs_nb.len;
278 		if (port != 0) {
279 			struct sockaddr *sa = nfsargsp->addr;
280 			switch (sa->sa_family) {
281 			case AF_INET:
282 				((struct sockaddr_in *)sa)->sin_port = port;
283 				break;
284 #ifdef INET6
285 			case AF_INET6:
286 				((struct sockaddr_in6 *)sa)->sin6_port = port;
287 				break;
288 #endif
289 			default:
290 				errx(1, "Unsupported socket family %d",
291 				    sa->sa_family);
292 			}
293 		}
294 	}
295 	nfsargsp->fh = nfhret.nfh;
296 	nfsargsp->fhsize = nfhret.fhsize;
297 	nfsargsp->hostname = nam;
298 	return (1);
299 }
300 
301 /*
302  * xdr routines for mount rpc's
303  */
304 static int
xdr_dir(XDR * xdrsp,char * dirp)305 xdr_dir(XDR *xdrsp, char *dirp)
306 {
307 	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
308 }
309 
310 static int
xdr_fh(XDR * xdrsp,struct nfhret * np)311 xdr_fh(XDR *xdrsp, struct nfhret *np)
312 {
313 	int i;
314 	long auth, authcnt, authfnd = 0;
315 
316 	if (!xdr_u_long(xdrsp, &np->stat))
317 		return (0);
318 	if (np->stat)
319 		return (1);
320 	switch (np->vers) {
321 	case 1:
322 		np->fhsize = NFSX_V2FH;
323 		return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH));
324 	case 3:
325 		if (!xdr_long(xdrsp, &np->fhsize))
326 			return (0);
327 		if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX)
328 			return (0);
329 		if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
330 			return (0);
331 		if (!xdr_long(xdrsp, &authcnt))
332 			return (0);
333 		for (i = 0; i < authcnt; i++) {
334 			if (!xdr_long(xdrsp, &auth))
335 				return (0);
336 			if (auth == np->auth)
337 				authfnd++;
338 		}
339 		/*
340 		 * Some servers, such as DEC's OSF/1 return a nil authenticator
341 		 * list to indicate RPCAUTH_UNIX.
342 		 */
343 		if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX))
344 			np->stat = EAUTH;
345 		return (1);
346 	};
347 	return (0);
348 }
349