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