6577a3b4d0a45b2f668df981e4a8dfc39005ea5f
[dragonfly.git] / sbin / mount_nfs / mount_nfs.c
1 /*
2  * Copyright (c) 1992, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Rick Macklem at The University of Guelph.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * @(#) Copyright (c) 1992, 1993, 1994 The Regents of the University of California.  All rights reserved.
37  * @(#)mount_nfs.c      8.11 (Berkeley) 5/4/95
38  * $FreeBSD: src/sbin/mount_nfs/mount_nfs.c,v 1.36.2.6 2003/05/13 14:45:40 trhodes Exp $
39  */
40
41 #include <sys/param.h>
42 #include <sys/mount.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #include <sys/syslog.h>
46
47 #include <rpc/rpc.h>
48 #include <rpc/pmap_clnt.h>
49 #include <rpc/pmap_prot.h>
50
51 #include <vfs/nfs/rpcv2.h>
52 #include <vfs/nfs/nfsproto.h>
53 #include <vfs/nfs/nfs.h>
54
55 #include <arpa/inet.h>
56
57 #include <ctype.h>
58 #include <err.h>
59 #include <errno.h>
60 #include <fcntl.h>
61 #include <mntopts.h>
62 #include <netdb.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <sysexits.h>
67 #include <unistd.h>
68
69 #include "mounttab.h"
70
71 #define ALTF_BG         0x1
72 #define ALTF_NOCONN     0x2
73 #define ALTF_DUMBTIMR   0x4
74 #define ALTF_INTR       0x8
75 #define ALTF_KERB       0x10
76 #define ALTF_NFSV3      0x20
77 #define ALTF_RDIRPLUS   0x40
78 #define ALTF_CACHE      0x80
79 #define ALTF_RESVPORT   0x100
80 #define ALTF_SEQPACKET  0x200
81 #define ALTF_UNUSED400  0x400
82 #define ALTF_SOFT       0x800
83 #define ALTF_TCP        0x1000
84 #define ALTF_PORT       0x2000
85 #define ALTF_NFSV2      0x4000
86 #define ALTF_ACREGMIN   0x8000
87 #define ALTF_ACREGMAX   0x10000
88 #define ALTF_ACDIRMIN   0x20000
89 #define ALTF_ACDIRMAX   0x40000
90
91 struct mntopt mopts[] = {
92         MOPT_STDOPTS,
93         MOPT_FORCE,
94         MOPT_UPDATE,
95         MOPT_ASYNC,
96         { "bg", 0, ALTF_BG, 1 },
97         { "conn", 1, ALTF_NOCONN, 1 },
98         { "dumbtimer", 0, ALTF_DUMBTIMR, 1 },
99         { "intr", 0, ALTF_INTR, 1 },
100 #ifdef NFSKERB
101         { "kerb", 0, ALTF_KERB, 1 },
102 #endif
103         { "nfsv3", 0, ALTF_NFSV3, 1 },
104         { "rdirplus", 0, ALTF_RDIRPLUS, 1 },
105         { "mntudp", 1, ALTF_TCP, 1 },
106         { "resvport", 0, ALTF_RESVPORT, 1 },
107         { "soft", 0, ALTF_SOFT, 1 },
108         { "tcp", 0, ALTF_TCP, 1 },
109         { "udp", 1, ALTF_TCP, 1 },
110         { "port=", 0, ALTF_PORT, 1 },
111         { "nfsv2", 0, ALTF_NFSV2, 1 },
112         { "acregmin=", 0, ALTF_ACREGMIN, 1 },
113         { "acregmax=", 0, ALTF_ACREGMAX, 1 },
114         { "acdirmin=", 0, ALTF_ACDIRMIN, 1 },
115         { "acdirmax=", 0, ALTF_ACDIRMAX, 1 },
116         { "cache", 0, ALTF_CACHE, 1 },
117         MOPT_NULL
118 };
119
120 struct nfs_args nfsdefargs = {
121         NFS_ARGSVERSION,
122         NULL,
123         sizeof (struct sockaddr_in),
124         0,
125         0,
126         NULL,
127         0,
128         NFSMNT_RESVPORT,
129         NFS_WSIZE,
130         NFS_RSIZE,
131         NFS_READDIRSIZE,
132         10,
133         NFS_RETRANS,
134         NFS_MAXGRPS,
135         NFS_DEFRAHEAD,
136         0,
137         NFS_DEADTHRESH,
138         NULL,
139         /* args version 4 */
140         NFS_MINATTRTIMO,
141         NFS_MAXATTRTIMO,
142         NFS_MINDIRATTRTIMO,
143         NFS_MAXDIRATTRTIMO,
144 };
145
146 /* Table for af,sotype -> netid conversions. */
147 struct nc_protos {
148         const char *netid;
149         int af;
150         int sotype;
151 } nc_protos[] = {
152         {"udp",         AF_INET,        SOCK_DGRAM},
153         {"tcp",         AF_INET,        SOCK_STREAM},
154         {"udp6",        AF_INET6,       SOCK_DGRAM},
155         {"tcp6",        AF_INET6,       SOCK_STREAM},
156         {NULL,          0,              0}
157 };
158
159 struct nfhret {
160         u_long          stat;
161         long            vers;
162         long            auth;
163         long            fhsize;
164         u_char          nfh[NFSX_V3FHMAX];
165 };
166 #define BGRND   1
167 #define ISBGRND 2
168 int retrycnt = -1;
169 int opflags = 0;
170 int nfsproto;
171 char *portspec = NULL;  /* Server nfs port; NULL means look up via rpcbind. */
172 enum mountmode {
173         ANY,
174         V2,
175         V3
176 } mountmode = ANY;
177
178 #ifdef NFSKERB
179 char inst[INST_SZ];
180 char realm[REALM_SZ];
181 struct {
182         u_long          kind;
183         KTEXT_ST        kt;
184 } ktick;
185 struct nfsrpc_nickverf kverf;
186 struct nfsrpc_fullblock kin, kout;
187 NFSKERBKEY_T kivec;
188 CREDENTIALS kcr;
189 struct timeval ktv;
190 NFSKERBKEYSCHED_T kerb_keysched;
191 #endif
192
193 /* Return codes for nfs_tryproto. */
194 enum tryret {
195         TRYRET_SUCCESS,
196         TRYRET_TIMEOUT,         /* No response received. */
197         TRYRET_REMOTEERR,       /* Error received from remote server. */
198         TRYRET_LOCALERR         /* Local failure. */
199 };
200
201 #if 0
202 void    set_rpc_maxgrouplist(int);
203 #endif
204 static struct netconfig *getnetconf_cached(const char *);
205 static const char       *netidbytype(int, int);
206 static int      getnfsargs(char *, struct nfs_args *);
207 static void     usage(void) __dead2;
208 static int      xdr_dir(XDR *, char *);
209 static int      xdr_fh(XDR *, struct nfhret *);
210 static enum tryret
211                 nfs_tryproto(struct nfs_args *, struct addrinfo *,
212                              char *, char *, char **);
213 static enum tryret
214                 returncode(enum clnt_stat, struct rpc_err *);
215
216 /*
217  * Used to set mount flags with getmntopts.  Call with dir=TRUE to
218  * initialize altflags from the current mount flags.  Call with
219  * dir=FALSE to update mount flags with the new value of altflags after
220  * the call to getmntopts.
221  */
222 static void
223 set_flags(int* altflags, int* nfsflags, int dir)
224 {
225 #define F2(af, nf)                                      \
226         if (dir) {                                      \
227                 if (*nfsflags & NFSMNT_##nf)            \
228                         *altflags |= ALTF_##af;         \
229                 else                                    \
230                         *altflags &= ~ALTF_##af;        \
231         } else {                                        \
232                 if (*altflags & ALTF_##af)              \
233                         *nfsflags |= NFSMNT_##nf;       \
234                 else                                    \
235                         *nfsflags &= ~NFSMNT_##nf;      \
236         }
237 #define F(f)    F2(f,f)
238
239         F(NOCONN);
240         F(DUMBTIMR);
241         F2(INTR, INT);
242 #ifdef NFSKERB
243         F(KERB);
244 #endif
245         F(RDIRPLUS);
246         F(RESVPORT);
247         F(SOFT);
248         F(ACREGMIN);
249         F(ACREGMAX);
250         F(ACDIRMIN);
251         F(ACDIRMAX);
252 #ifdef NFSMNT_CACHE
253         F(CACHE);
254 #endif
255
256 #undef F
257 #undef F2
258 }
259
260 int
261 main(int argc, char **argv)
262 {
263         int c;
264         struct nfs_args *nfsargsp;
265         struct nfs_args nfsargs;
266         struct nfsd_cargs ncd;
267         int mntflags, altflags, nfssvc_flag, num;
268         char *name, *p, *spec;
269         char mntpath[MAXPATHLEN];
270         struct vfsconf vfc;
271         int error = 0;
272 #ifdef NFSKERB
273         uid_t last_ruid;
274
275         last_ruid = -1;
276         strcpy(realm, KRB_REALM);
277         if (sizeof (struct nfsrpc_nickverf) != RPCX_NICKVERF ||
278             sizeof (struct nfsrpc_fullblock) != RPCX_FULLBLOCK ||
279             ((char *)&ktick.kt) - ((char *)&ktick) != NFSX_UNSIGNED ||
280             ((char *)ktick.kt.dat) - ((char *)&ktick) != 2 * NFSX_UNSIGNED)
281                 fprintf(stderr, "Yikes! NFSKERB structs not packed!!\n");
282 #endif /* NFSKERB */
283
284         mntflags = 0;
285         altflags = ALTF_TCP | ALTF_RDIRPLUS;
286         nfsargs = nfsdefargs;
287         nfsargsp = &nfsargs;
288         if (altflags & ALTF_TCP) {
289                 nfsproto = IPPROTO_TCP;
290                 nfsargsp->sotype = SOCK_STREAM;
291         } else {
292                 nfsproto = IPPROTO_UDP;
293                 nfsargsp->sotype = SOCK_DGRAM;
294         }
295         while ((c = getopt(argc, argv,
296             "23a:bcdD:g:I:iKlm:No:PR:r:sTt:w:x:U")) != -1)
297                 switch (c) {
298                 case '2':
299                         mountmode = V2;
300                         break;
301                 case '3':
302                         mountmode = V3;
303                         break;
304                 case 'a':
305                         num = strtol(optarg, &p, 10);
306                         if (*p || num < 0)
307                                 errx(1, "illegal -a value -- %s", optarg);
308                         nfsargsp->readahead = num;
309                         nfsargsp->flags |= NFSMNT_READAHEAD;
310                         break;
311                 case 'b':
312                         opflags |= BGRND;
313                         break;
314                 case 'c':
315                         nfsargsp->flags |= NFSMNT_NOCONN;
316                         break;
317                 case 'D':
318                         num = strtol(optarg, &p, 10);
319                         if (*p || num <= 0)
320                                 errx(1, "illegal -D value -- %s", optarg);
321                         nfsargsp->deadthresh = num;
322                         nfsargsp->flags |= NFSMNT_DEADTHRESH;
323                         break;
324                 case 'd':
325                         nfsargsp->flags |= NFSMNT_DUMBTIMR;
326                         break;
327 #if 0 /* XXXX */
328                 case 'g':
329                         num = strtol(optarg, &p, 10);
330                         if (*p || num <= 0)
331                                 errx(1, "illegal -g value -- %s", optarg);
332                         set_rpc_maxgrouplist(num);
333                         nfsargsp->maxgrouplist = num;
334                         nfsargsp->flags |= NFSMNT_MAXGRPS;
335                         break;
336 #endif
337                 case 'I':
338                         num = strtol(optarg, &p, 10);
339                         if (*p || num <= 0)
340                                 errx(1, "illegal -I value -- %s", optarg);
341                         nfsargsp->readdirsize = num;
342                         nfsargsp->flags |= NFSMNT_READDIRSIZE;
343                         break;
344                 case 'i':
345                         nfsargsp->flags |= NFSMNT_INT;
346                         break;
347 #ifdef NFSKERB
348                 case 'K':
349                         nfsargsp->flags |= NFSMNT_KERB;
350                         break;
351 #endif
352                 case 'l':
353                         nfsargsp->flags |= NFSMNT_RDIRPLUS;
354                         break;
355 #ifdef NFSKERB
356                 case 'm':
357                         strncpy(realm, optarg, REALM_SZ - 1);
358                         realm[REALM_SZ - 1] = '\0';
359                         break;
360 #endif
361                 case 'N':
362                         nfsargsp->flags &= ~NFSMNT_RESVPORT;
363                         break;
364                 case 'o':
365                         set_flags(&altflags, &nfsargsp->flags, TRUE);
366                         altflags |= ALTF_RDIRPLUS;
367                         if (nfsproto == IPPROTO_TCP)
368                                 altflags |= ALTF_TCP;
369                         if (mountmode == V2)
370                                 altflags |= ALTF_NFSV2;
371                         else if (mountmode == V3)
372                                 altflags |= ALTF_NFSV3;
373                         getmntopts(optarg, mopts, &mntflags, &altflags);
374                         set_flags(&altflags, &nfsargsp->flags, FALSE);
375                         /*
376                          * Handle altflags which don't map directly to
377                          * mount flags.
378                          */
379                         if(altflags & ALTF_BG)
380                                 opflags |= BGRND;
381                         if (altflags & ALTF_TCP) {
382                                 nfsproto = IPPROTO_TCP;
383                                 nfsargsp->sotype = SOCK_STREAM;
384                         } else {
385                                 nfsproto = IPPROTO_UDP;
386                                 nfsargsp->sotype = SOCK_DGRAM;
387                         }
388                         if(altflags & ALTF_PORT) {
389                                 /*
390                                  * XXX Converting from a string to an int
391                                  * and back again is silly, and we should
392                                  * allow /etc/services names.
393                                  */
394                                 asprintf(&portspec, "%d",
395                                          atoi(strstr(optarg, "port=") + 5));
396                                 if (portspec == NULL)
397                                         err(1, "asprintf");
398                         }
399                         mountmode = ANY;
400                         if(altflags & ALTF_NFSV2)
401                                 mountmode = V2;
402                         if(altflags & ALTF_NFSV3)
403                                 mountmode = V3;
404                         if(altflags & ALTF_ACREGMIN)
405                                 nfsargsp->acregmin = atoi(strstr(optarg,
406                                     "acregmin=") + 9);
407                         if(altflags & ALTF_ACREGMAX)
408                                 nfsargsp->acregmax = atoi(strstr(optarg,
409                                     "acregmax=") + 9);
410                         if(altflags & ALTF_ACDIRMIN)
411                                 nfsargsp->acdirmin = atoi(strstr(optarg,
412                                     "acdirmin=") + 9);
413                         if(altflags & ALTF_ACDIRMAX)
414                                 nfsargsp->acdirmax = atoi(strstr(optarg,
415                                     "acdirmax=") + 9);
416                         break;
417                 case 'P':
418                         /* obsolete for NFSMNT_RESVPORT, now default */
419                         break;
420                 case 'R':
421                         num = strtol(optarg, &p, 10);
422                         if (*p || num < 0)
423                                 errx(1, "illegal -R value -- %s", optarg);
424                         retrycnt = num;
425                         break;
426                 case 'r':
427                         num = strtol(optarg, &p, 10);
428                         if (*p || num <= 0)
429                                 errx(1, "illegal -r value -- %s", optarg);
430                         nfsargsp->rsize = num;
431                         nfsargsp->flags |= NFSMNT_RSIZE;
432                         break;
433                 case 's':
434                         nfsargsp->flags |= NFSMNT_SOFT;
435                         break;
436                 case 'T':
437                         nfsargsp->sotype = SOCK_STREAM;
438                         nfsproto = IPPROTO_TCP;
439                         altflags |= ALTF_TCP;
440                         break;
441                 case 't':
442                         num = strtol(optarg, &p, 10);
443                         if (*p || num <= 0)
444                                 errx(1, "illegal -t value -- %s", optarg);
445                         nfsargsp->timeo = num;
446                         nfsargsp->flags |= NFSMNT_TIMEO;
447                         break;
448                 case 'w':
449                         num = strtol(optarg, &p, 10);
450                         if (*p || num <= 0)
451                                 errx(1, "illegal -w value -- %s", optarg);
452                         nfsargsp->wsize = num;
453                         nfsargsp->flags |= NFSMNT_WSIZE;
454                         break;
455                 case 'x':
456                         num = strtol(optarg, &p, 10);
457                         if (*p || num <= 0)
458                                 errx(1, "illegal -x value -- %s", optarg);
459                         nfsargsp->retrans = num;
460                         nfsargsp->flags |= NFSMNT_RETRANS;
461                         break;
462                 case 'U':
463                         nfsargsp->sotype = SOCK_DGRAM;
464                         nfsproto = IPPROTO_UDP;
465                         altflags &= ~ALTF_TCP;
466                         break;
467                 default:
468                         usage();
469                         break;
470                 }
471         argc -= optind;
472         argv += optind;
473
474         if (argc != 2) {
475                 usage();
476                 /* NOTREACHED */
477         }
478
479         spec = *argv++;
480         name = *argv;
481
482         if (retrycnt == -1)
483                 /* The default is to keep retrying forever. */
484                 retrycnt = 0;
485         if (!getnfsargs(spec, nfsargsp))
486                 exit(1);
487
488         /* resolve the mountpoint with realpath(3) */
489         checkpath(name, mntpath);
490
491         error = getvfsbyname("nfs", &vfc);
492         if (error && vfsisloadable("nfs")) {
493                 if(vfsload("nfs"))
494                         err(EX_OSERR, "vfsload(nfs)");
495                 endvfsent();    /* clear cache */
496                 error = getvfsbyname("nfs", &vfc);
497         }
498         if (error)
499                 errx(EX_OSERR, "nfs filesystem is not available");
500
501         if (mount(vfc.vfc_name, mntpath, mntflags, nfsargsp))
502                 err(1, "%s", mntpath);
503         if (nfsargsp->flags & NFSMNT_KERB) {
504                 if ((opflags & ISBGRND) == 0) {
505                         if (daemon(0, 0) != 0)
506                                 err(1, "daemon");
507                 }
508                 openlog("mount_nfs", LOG_PID, LOG_DAEMON);
509                 nfssvc_flag = NFSSVC_MNTD;
510                 ncd.ncd_dirp = mntpath;
511                 while (nfssvc(nfssvc_flag, (caddr_t)&ncd) < 0) {
512                         if (errno != ENEEDAUTH) {
513                                 syslog(LOG_ERR, "nfssvc err %m");
514                                 continue;
515                         }
516                         nfssvc_flag =
517                             NFSSVC_MNTD | NFSSVC_GOTAUTH | NFSSVC_AUTHINFAIL;
518 #ifdef NFSKERB
519                         /*
520                          * Set up as ncd_authuid for the kerberos call.
521                          * Must set ruid to ncd_authuid and reset the
522                          * ticket name iff ncd_authuid is not the same
523                          * as last time, so that the right ticket file
524                          * is found.
525                          * Get the Kerberos credential structure so that
526                          * we have the session key and get a ticket for
527                          * this uid.
528                          * For more info see the IETF Draft "Authentication
529                          * in ONC RPC".
530                          */
531                         if (ncd.ncd_authuid != last_ruid) {
532                                 char buf[512];
533                                 sprintf(buf, "%s%d", TKT_ROOT, ncd.ncd_authuid);
534                                 krb_set_tkt_string(buf);
535                                 last_ruid = ncd.ncd_authuid;
536                         }
537                         setreuid(ncd.ncd_authuid, 0);
538                         kret = krb_get_cred(NFS_KERBSRV, inst, realm, &kcr);
539                         if (kret == RET_NOTKT) {
540                             kret = get_ad_tkt(NFS_KERBSRV, inst, realm,
541                                 DEFAULT_TKT_LIFE);
542                             if (kret == KSUCCESS)
543                                 kret = krb_get_cred(NFS_KERBSRV, inst, realm,
544                                     &kcr);
545                         }
546                         if (kret == KSUCCESS)
547                             kret = krb_mk_req(&ktick.kt, NFS_KERBSRV, inst,
548                                 realm, 0);
549
550                         /*
551                          * Fill in the AKN_FULLNAME authenticator and verifier.
552                          * Along with the Kerberos ticket, we need to build
553                          * the timestamp verifier and encrypt it in CBC mode.
554                          */
555                         if (kret == KSUCCESS &&
556                             ktick.kt.length <= (RPCAUTH_MAXSIZ-3*NFSX_UNSIGNED)
557                             && gettimeofday(&ktv, NULL) == 0) {
558                             ncd.ncd_authtype = RPCAUTH_KERB4;
559                             ncd.ncd_authstr = (u_char *)&ktick;
560                             ncd.ncd_authlen = nfsm_rndup(ktick.kt.length) +
561                                 3 * NFSX_UNSIGNED;
562                             ncd.ncd_verfstr = (u_char *)&kverf;
563                             ncd.ncd_verflen = sizeof (kverf);
564                             memmove(ncd.ncd_key, kcr.session,
565                                 sizeof (kcr.session));
566                             kin.t1 = htonl(ktv.tv_sec);
567                             kin.t2 = htonl(ktv.tv_usec);
568                             kin.w1 = htonl(NFS_KERBTTL);
569                             kin.w2 = htonl(NFS_KERBTTL - 1);
570                             bzero((caddr_t)kivec, sizeof (kivec));
571
572                             /*
573                              * Encrypt kin in CBC mode using the session
574                              * key in kcr.
575                              */
576                             XXX
577
578                             /*
579                              * Finally, fill the timestamp verifier into the
580                              * authenticator and verifier.
581                              */
582                             ktick.kind = htonl(RPCAKN_FULLNAME);
583                             kverf.kind = htonl(RPCAKN_FULLNAME);
584                             NFS_KERBW1(ktick.kt) = kout.w1;
585                             ktick.kt.length = htonl(ktick.kt.length);
586                             kverf.verf.t1 = kout.t1;
587                             kverf.verf.t2 = kout.t2;
588                             kverf.verf.w2 = kout.w2;
589                             nfssvc_flag = NFSSVC_MNTD | NFSSVC_GOTAUTH;
590                         }
591                         setreuid(0, 0);
592 #endif /* NFSKERB */
593                 }
594         }
595         exit(0);
596 }
597
598 static int
599 getnfsargs(char *spec, struct nfs_args *nfsargsp)
600 {
601         struct addrinfo hints, *ai_nfs, *ai;
602 #ifdef NFSKERB
603         char host[NI_MAXHOST], serv[NI_MAXSERV];
604 #endif
605         enum tryret ret;
606         int ecode, speclen, remoteerr;
607         char *hostp, *delimp, *errstr;
608 #ifdef NFSKERB
609         char *cp;
610 #endif
611         size_t len;
612         static char nam[MNAMELEN + 1];
613
614         if ((delimp = strrchr(spec, ':')) != NULL) {
615                 hostp = spec;
616                 spec = delimp + 1;
617         } else if ((delimp = strrchr(spec, '@')) != NULL) {
618                 warnx("path@server syntax is deprecated, use server:path");
619                 hostp = delimp + 1;
620         } else {
621                 warnx("no <host>:<dirpath> nfs-name");
622                 return (0);
623         }
624         *delimp = '\0';
625
626         /*
627          * If there has been a trailing slash at mounttime it seems
628          * that some mountd implementations fail to remove the mount
629          * entries from their mountlist while unmounting.
630          */
631         for (speclen = strlen(spec); 
632                 speclen > 1 && spec[speclen - 1] == '/';
633                 speclen--)
634                 spec[speclen - 1] = '\0';
635         if (strlen(hostp) + strlen(spec) + 1 > MNAMELEN) {
636                 warnx("%s:%s: %s", hostp, spec, strerror(ENAMETOOLONG));
637                 return (0);
638         }
639         /* Make both '@' and ':' notations equal */
640         if (*hostp != '\0') {
641                 len = strlen(hostp);
642                 memmove(nam, hostp, len);
643                 nam[len] = ':';
644                 memmove(nam + len + 1, spec, speclen);
645                 nam[len + speclen + 1] = '\0';
646         }
647
648         /*
649          * Handle an internet host address and reverse resolve it if
650          * doing Kerberos.
651          */
652         memset(&hints, 0, sizeof hints);
653         hints.ai_flags = AI_NUMERICHOST;
654         hints.ai_socktype = nfsargsp->sotype;
655         if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) == 0) {
656 #ifdef NFSKERB
657                 if ((nfsargsp->flags & NFSMNT_KERB)) {
658                         hints.ai_flags = 0;
659                         if (getnameinfo(ai_nfs->ai_addr, ai_nfs->ai_addrlen,
660                             host, sizeof host, serv, sizeof serv, 0) != 0) {
661                                 warnx("can't reverse resolve net address");
662                                         return (0);
663                                 }
664                         hostp = host;
665                 }
666 #endif /* NFSKERB */
667         } else {
668                 hints.ai_flags = 0;
669                 if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs))
670                     != 0) {
671                         if (portspec == NULL)
672                                 errx(1, "%s: %s", hostp, gai_strerror(ecode));
673                         else
674                                 errx(1, "%s:%s: %s", hostp, portspec,
675                                     gai_strerror(ecode));
676                         return (0);
677                 }
678         }
679 #ifdef NFSKERB
680         if (nfsargsp->flags & NFSMNT_KERB) {
681                 strncpy(inst, hp->h_name, INST_SZ);
682                 inst[INST_SZ - 1] = '\0';
683                 if (cp = strchr(inst, '.'))
684                         *cp = '\0';
685         }
686 #endif /* NFSKERB */
687
688         ret = TRYRET_LOCALERR;
689         for (;;) {
690                 /*
691                  * Try each entry returned by getaddrinfo(). Note the
692                  * occurence of remote errors by setting `remoteerr'.
693                  */
694                 remoteerr = 0;
695                 for (ai = ai_nfs; ai != NULL; ai = ai->ai_next) {
696                         ret = nfs_tryproto(nfsargsp, ai, hostp, spec, &errstr);
697                         if (ret == TRYRET_SUCCESS)
698                                 break;
699                         if (ret != TRYRET_LOCALERR)
700                                 remoteerr = 1;
701                         if ((opflags & ISBGRND) == 0)
702                                 fprintf(stderr, "%s\n", errstr);
703                 }
704                 if (ret == TRYRET_SUCCESS)
705                         break;
706
707                 /* Exit if all errors were local. */
708                 if (!remoteerr)
709                         exit(1);
710
711                 /*
712                  * If retrycnt == 0, we are to keep retrying forever.
713                  * Otherwise decrement it, and exit if it hits zero.
714                  */
715                 if (retrycnt != 0 && --retrycnt == 0)
716                         exit(1);
717
718                 if ((opflags & (BGRND | ISBGRND)) == BGRND) {
719                         warnx("Cannot immediately mount %s:%s, backgrounding",
720                             hostp, spec);
721                         opflags |= ISBGRND;
722                         if (daemon(0, 0) != 0)
723                                 err(1, "daemon");
724                 }
725                 sleep(60);
726         }
727         freeaddrinfo(ai_nfs);
728         nfsargsp->hostname = nam;
729         /* Add mounted filesystem to PATH_MOUNTTAB */
730         if (!add_mtab(hostp, spec))
731                 warnx("can't update %s for %s:%s", PATH_MOUNTTAB, hostp, spec);
732         return (1);
733 }
734
735 /*
736  * Try to set up the NFS arguments according to the address
737  * family, protocol (and possibly port) specified in `ai'.
738  *
739  * Returns TRYRET_SUCCESS if successful, or:
740  *   TRYRET_TIMEOUT             The server did not respond.
741  *   TRYRET_REMOTEERR           The server reported an error.
742  *   TRYRET_LOCALERR            Local failure.
743  *
744  * In all error cases, *errstr will be set to a statically-allocated string
745  * describing the error.
746  */
747 static enum tryret
748 nfs_tryproto(struct nfs_args *nfsargsp, struct addrinfo *ai, char *hostp,
749     char *spec, char **errstr)
750 {
751         static char errbuf[256];
752         struct sockaddr_storage nfs_ss;
753         struct netbuf nfs_nb;
754         struct nfhret nfhret;
755         struct timeval try;
756         struct rpc_err rpcerr;
757         CLIENT *clp;
758         struct netconfig *nconf, *nconf_mnt;
759         const char *netid, *netid_mnt;
760         int doconnect, nfsvers, mntvers;
761         enum clnt_stat status;
762         enum mountmode trymntmode;
763
764         trymntmode = mountmode;
765         errbuf[0] = '\0';
766         *errstr = errbuf;
767
768         if ((netid = netidbytype(ai->ai_family, nfsargsp->sotype)) == NULL) {
769                 snprintf(errbuf, sizeof errbuf,
770                     "af %d sotype %d not supported", ai->ai_family,
771                     nfsargsp->sotype);
772                 return (TRYRET_LOCALERR);
773         }
774         if ((nconf = getnetconf_cached(netid)) == NULL) {
775                 snprintf(errbuf, sizeof errbuf, "%s: %s", netid, nc_sperror());
776                 return (TRYRET_LOCALERR);
777         }
778
779         netid_mnt = netid;
780         nconf_mnt = nconf;
781
782 tryagain:
783         if (trymntmode == V2) {
784                 nfsvers = 2;
785                 mntvers = 1;
786         } else {
787                 nfsvers = 3;
788                 mntvers = 3;
789         }
790
791         if (portspec != NULL) {
792                 /* `ai' contains the complete nfsd sockaddr. */
793                 nfs_nb.buf = ai->ai_addr;
794                 nfs_nb.len = nfs_nb.maxlen = ai->ai_addrlen;
795         } else {
796                 /* Ask the remote rpcbind. */
797                 nfs_nb.buf = &nfs_ss;
798                 nfs_nb.len = nfs_nb.maxlen = sizeof nfs_ss;
799
800                 if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, &nfs_nb,
801                     hostp)) {
802                         if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH &&
803                             trymntmode == ANY) {
804                                 trymntmode = V2;
805                                 goto tryagain;
806                         }
807                         snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s",
808                             netid, hostp, spec,
809                             clnt_spcreateerror("RPCPROG_NFS"));
810                         return (returncode(rpc_createerr.cf_stat,
811                             &rpc_createerr.cf_error));
812                 }
813         }
814
815         /* Check that the server (nfsd) responds on the port we have chosen. */
816         clp = clnt_tli_create(RPC_ANYFD, nconf, &nfs_nb, RPCPROG_NFS, nfsvers,
817             0, 0);
818         if (clp == NULL) {
819                 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
820                     hostp, spec, clnt_spcreateerror("nfsd: RPCPROG_NFS"));
821                 return (returncode(rpc_createerr.cf_stat,
822                     &rpc_createerr.cf_error));
823         }
824         if (nfsargsp->sotype == SOCK_DGRAM &&
825             !(nfsargsp->flags & NFSMNT_NOCONN)) {
826                 /*
827                  * Use connect(), to match what the kernel does. This
828                  * catches cases where the server responds from the
829                  * wrong source address.
830                  */
831                 doconnect = 1;
832                 if (!clnt_control(clp, CLSET_CONNECT, (char *)&doconnect)) {
833                         clnt_destroy(clp);
834                         snprintf(errbuf, sizeof errbuf,
835                             "[%s] %s:%s: CLSET_CONNECT failed", netid, hostp,
836                             spec);
837                         return (TRYRET_LOCALERR);
838                 }
839         }
840
841         try.tv_sec = 10;
842         try.tv_usec = 0;
843         status = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL,
844             (xdrproc_t)xdr_void, NULL, try);
845         if (status != RPC_SUCCESS) {
846                 if (status == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
847                         clnt_destroy(clp);
848                         trymntmode = V2;
849                         goto tryagain;
850                 }
851                 clnt_geterr(clp, &rpcerr);
852                 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid,
853                     hostp, spec, clnt_sperror(clp, "NFSPROC_NULL"));
854                 clnt_destroy(clp);
855                 return (returncode(status, &rpcerr));
856         }
857         clnt_destroy(clp);
858
859         /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */
860         try.tv_sec = 10;
861         try.tv_usec = 0;
862         clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, nconf_mnt);
863         if (clp == NULL) {
864                 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
865                     hostp, spec, clnt_spcreateerror("RPCMNT: clnt_create"));
866                 return (returncode(rpc_createerr.cf_stat,
867                     &rpc_createerr.cf_error));
868         }
869         clp->cl_auth = authsys_create_default();
870         if (nfsargsp->flags & NFSMNT_KERB)
871                 nfhret.auth = RPCAUTH_KERB4;
872         else
873                 nfhret.auth = RPCAUTH_UNIX;
874         nfhret.vers = mntvers;
875         status = clnt_call(clp, RPCMNT_MOUNT, (xdrproc_t)xdr_dir, spec,
876             (xdrproc_t)xdr_fh, &nfhret, try);
877         auth_destroy(clp->cl_auth);
878         if (status != RPC_SUCCESS) {
879                 if (status == RPC_PROGVERSMISMATCH && trymntmode == ANY) {
880                         clnt_destroy(clp);
881                         trymntmode = V2;
882                         goto tryagain;
883                 }
884                 clnt_geterr(clp, &rpcerr);
885                 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
886                     hostp, spec, clnt_sperror(clp, "RPCPROG_MNT"));
887                 clnt_destroy(clp);
888                 return (returncode(status, &rpcerr));
889         }
890         clnt_destroy(clp);
891
892         if (nfhret.stat != 0) {
893                 snprintf(errbuf, sizeof errbuf, "[%s] %s:%s: %s", netid_mnt,
894                     hostp, spec, strerror(nfhret.stat));
895                 return (TRYRET_REMOTEERR);
896         }
897
898         /*
899          * Store the filehandle and server address in nfsargsp, making
900          * sure to copy any locally allocated structures.
901          */
902         nfsargsp->addrlen = nfs_nb.len;
903         nfsargsp->addr = malloc(nfsargsp->addrlen);
904         nfsargsp->fhsize = nfhret.fhsize;
905         nfsargsp->fh = malloc(nfsargsp->fhsize);
906         if (nfsargsp->addr == NULL || nfsargsp->fh == NULL)
907                 err(1, "malloc");
908         bcopy(nfs_nb.buf, nfsargsp->addr, nfsargsp->addrlen);
909         bcopy(nfhret.nfh, nfsargsp->fh, nfsargsp->fhsize);
910
911         if (nfsvers == 3)
912                 nfsargsp->flags |= NFSMNT_NFSV3;
913         else
914                 nfsargsp->flags &= ~NFSMNT_NFSV3;
915
916         return (TRYRET_SUCCESS);
917 }
918
919
920 /*
921  * Catagorise a RPC return status and error into an `enum tryret'
922  * return code.
923  */
924 static enum tryret
925 returncode(enum clnt_stat status, struct rpc_err *rpcerr)
926 {
927         switch (status) {
928         case RPC_TIMEDOUT:
929                 return (TRYRET_TIMEOUT);
930         case RPC_PMAPFAILURE:
931         case RPC_PROGNOTREGISTERED:
932         case RPC_PROGVERSMISMATCH:
933         /* XXX, these can be local or remote. */
934         case RPC_CANTSEND:
935         case RPC_CANTRECV:
936                 return (TRYRET_REMOTEERR);
937         case RPC_SYSTEMERROR:
938                 switch (rpcerr->re_errno) {
939                 case ETIMEDOUT:
940                         return (TRYRET_TIMEOUT);
941                 case ENOMEM:
942                         break;
943                 default:
944                         return (TRYRET_REMOTEERR);
945                 }
946                 /* FALLTHROUGH */
947         default:
948                 break;
949         }
950         return (TRYRET_LOCALERR);
951 }
952
953 /*
954  * Look up a netid based on an address family and socket type.
955  * `af' is the address family, and `sotype' is SOCK_DGRAM or SOCK_STREAM.
956  *
957  * XXX there should be a library function for this.
958  */
959 static const char *
960 netidbytype(int af, int sotype)
961 {
962         struct nc_protos *p;
963
964         for (p = nc_protos; p->netid != NULL; p++) {
965                 if (af != p->af || sotype != p->sotype)
966                         continue;
967                 return (p->netid);
968         }
969         return (NULL);
970 }
971
972 /*
973  * Look up a netconfig entry based on a netid, and cache the result so
974  * that we don't need to remember to call freenetconfigent().
975  *
976  * Otherwise it behaves just like getnetconfigent(), so nc_*error()
977  * work on failure.
978  */
979 static struct netconfig *
980 getnetconf_cached(const char *netid)
981 {
982         static struct nc_entry {
983                 struct netconfig *nconf;
984                 struct nc_entry *next;
985         } *head;
986         struct nc_entry *p;
987         struct netconfig *nconf;
988
989         for (p = head; p != NULL; p = p->next)
990                 if (strcmp(netid, p->nconf->nc_netid) == 0)
991                         return (p->nconf);
992
993         if ((nconf = getnetconfigent(netid)) == NULL)
994                 return (NULL);
995         if ((p = malloc(sizeof(*p))) == NULL)
996                 err(1, "malloc");
997         p->nconf = nconf;
998         p->next = head;
999         head = p;
1000
1001         return (p->nconf);
1002 }
1003
1004 /*
1005  * xdr routines for mount rpc's
1006  */
1007 static int
1008 xdr_dir(XDR *xdrsp, char *dirp)
1009 {
1010         return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
1011 }
1012
1013 static int
1014 xdr_fh(XDR *xdrsp, struct nfhret *np)
1015 {
1016         int i;
1017         long auth, authcnt, authfnd = 0;
1018
1019         if (!xdr_u_long(xdrsp, &np->stat))
1020                 return (0);
1021         if (np->stat)
1022                 return (1);
1023         switch (np->vers) {
1024         case 1:
1025                 np->fhsize = NFSX_V2FH;
1026                 return (xdr_opaque(xdrsp, (caddr_t)np->nfh, NFSX_V2FH));
1027         case 3:
1028                 if (!xdr_long(xdrsp, &np->fhsize))
1029                         return (0);
1030                 if (np->fhsize <= 0 || np->fhsize > NFSX_V3FHMAX)
1031                         return (0);
1032                 if (!xdr_opaque(xdrsp, (caddr_t)np->nfh, np->fhsize))
1033                         return (0);
1034                 if (!xdr_long(xdrsp, &authcnt))
1035                         return (0);
1036                 for (i = 0; i < authcnt; i++) {
1037                         if (!xdr_long(xdrsp, &auth))
1038                                 return (0);
1039                         if (auth == np->auth)
1040                                 authfnd++;
1041                 }
1042                 /*
1043                  * Some servers, such as DEC's OSF/1 return a nil authenticator
1044                  * list to indicate RPCAUTH_UNIX.
1045                  */
1046                 if (!authfnd && (authcnt > 0 || np->auth != RPCAUTH_UNIX))
1047                         np->stat = EAUTH;
1048                 return (1);
1049         };
1050         return (0);
1051 }
1052
1053 static void
1054 usage(void)
1055 {
1056         fprintf(stderr, "%s\n%s\n%s\n%s\n",
1057 "usage: mount_nfs [-23KNPTUbcdils] [-D deadthresh] [-I readdirsize]",
1058 "                 [-R retrycnt] [-a maxreadahead]",
1059 "                 [-g maxgroups] [-m realm] [-o options] [-r readsize]",
1060 "                 [-t timeout] [-w writesize] [-x retrans] rhost:path node");
1061         exit(1);
1062 }