diff options
| author | Alfred Perlstein <alfred@FreeBSD.org> | 2001-03-19 12:50:13 +0000 |
|---|---|---|
| committer | Alfred Perlstein <alfred@FreeBSD.org> | 2001-03-19 12:50:13 +0000 |
| commit | 8360efbd6c932013ffdb2f83d2f2de4278febb5e (patch) | |
| tree | b842b4bf2665ef953be005b10013a2f3daf323c3 /sbin | |
| parent | 1ac2b9fe972a0c73729565f8310fa6eba55718c4 (diff) | |
Notes
Diffstat (limited to 'sbin')
| -rw-r--r-- | sbin/mount_nfs/mount_nfs.c | 296 | ||||
| -rw-r--r-- | sbin/mountd/mountd.c | 872 | ||||
| -rw-r--r-- | sbin/nfsd/nfsd.8 | 24 | ||||
| -rw-r--r-- | sbin/nfsd/nfsd.c | 819 | ||||
| -rw-r--r-- | sbin/umount/umount.c | 195 |
5 files changed, 1446 insertions, 760 deletions
diff --git a/sbin/mount_nfs/mount_nfs.c b/sbin/mount_nfs/mount_nfs.c index 41873cace7a7..c1d746616901 100644 --- a/sbin/mount_nfs/mount_nfs.c +++ b/sbin/mount_nfs/mount_nfs.c @@ -50,6 +50,7 @@ static const char rcsid[] = #include <sys/param.h> #include <sys/mount.h> +#include <sys/socket.h> #include <sys/stat.h> #include <sys/syslog.h> @@ -72,6 +73,7 @@ static const char rcsid[] = #include <ctype.h> #include <err.h> #include <errno.h> +#include <fcntl.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> @@ -193,7 +195,7 @@ NFSKERBKEYSCHED_T kerb_keysched; #endif int getnfsargs __P((char *, struct nfs_args *)); -void set_rpc_maxgrouplist __P((int)); +/* void set_rpc_maxgrouplist __P((int)); */ void usage __P((void)) __dead2; int xdr_dir __P((XDR *, char *)); int xdr_fh __P((XDR *, struct nfhret *)); @@ -299,16 +301,16 @@ main(argc, argv) case 'd': nfsargsp->flags |= NFSMNT_DUMBTIMR; break; +#if 0 /* XXXX */ case 'g': num = strtol(optarg, &p, 10); if (*p || num <= 0) errx(1, "illegal -g value -- %s", optarg); -#ifdef __FreeBSD__ set_rpc_maxgrouplist(num); -#endif nfsargsp->maxgrouplist = num; nfsargsp->flags |= NFSMNT_MAXGRPS; break; +#endif case 'I': num = strtol(optarg, &p, 10); if (*p || num <= 0) @@ -456,7 +458,6 @@ main(argc, argv) /* resolve the mountpoint with realpath(3) */ (void)checkpath(name, mntpath); -#ifdef __FreeBSD__ error = getvfsbyname("nfs", &vfc); if (error && vfsisloadable("nfs")) { if(vfsload("nfs")) @@ -469,10 +470,6 @@ main(argc, argv) if (mount(vfc.vfc_name, mntpath, mntflags, nfsargsp)) err(1, "%s", mntpath); -#else - if (mount("nfs", mntpath, mntflags, nfsargsp)) - err(1, "%s", mntpath); -#endif if (nfsargsp->flags & (NFSMNT_NQNFS | NFSMNT_KERB)) { if ((opflags & ISBGRND) == 0) { if ((i = fork())) { @@ -577,75 +574,32 @@ main(argc, argv) exit(0); } -/* - * Return RPC_SUCCESS if server responds. - */ -enum clnt_stat -pingnfsserver(addr, version, sotype) - struct sockaddr_in *addr; - int version; - int sotype; -{ - struct sockaddr_in sin; - int tport; - CLIENT *clp; - int so = RPC_ANYSOCK; - enum clnt_stat stat; - struct timeval pertry, try; - - sin = *addr; - - if ((tport = port_no ? port_no : - pmap_getport(&sin, RPCPROG_NFS, version, nfsproto)) == 0) { - return rpc_createerr.cf_stat; - } - - sin.sin_port = htons(tport); - - pertry.tv_sec = 10; - pertry.tv_usec = 0; - if (sotype == SOCK_STREAM) - clp = clnttcp_create(&sin, RPCPROG_NFS, version, - &so, 0, 0); - else - clp = clntudp_create(&sin, RPCPROG_NFS, version, - pertry, &so); - if (clp == NULL) - return rpc_createerr.cf_stat; - - try.tv_sec = 10; - try.tv_usec = 0; - stat = clnt_call(clp, NFSPROC_NULL, - xdr_void, NULL, xdr_void, NULL, try); - - clnt_destroy(clp); - - return stat; -} - int getnfsargs(spec, nfsargsp) char *spec; struct nfs_args *nfsargsp; { - register CLIENT *clp; - struct hostent *hp; - static struct sockaddr_in saddr; + CLIENT *clp; + struct addrinfo hints, *ai_nfs, *ai; + int ecode; + char host[NI_MAXHOST], serv[NI_MAXSERV]; + static struct netbuf nfs_nb; + static struct sockaddr_storage nfs_ss; + struct netconfig *nconf; + char *netid; struct timeval pertry, try; enum clnt_stat clnt_stat; - int so = RPC_ANYSOCK, i, nfsvers, mntvers, orgcnt, speclen; + int so, i, nfsvers, mntvers, orgcnt, speclen; char *hostp, *delimp; #ifdef NFSKERB char *cp; #endif - u_short tport; size_t len; static struct nfhret nfhret; static char nam[MNAMELEN + 1]; - tport = 0; - - if ((delimp = strchr(spec, ':')) != NULL) { + so = i = 0; + if ((delimp = strrchr(spec, ':')) != NULL) { hostp = spec; spec = delimp + 1; } else if ((delimp = strrchr(spec, '@')) != NULL) { @@ -684,27 +638,29 @@ getnfsargs(spec, nfsargsp) * Handle an internet host address and reverse resolve it if * doing Kerberos. */ - if (isdigit(*hostp)) { - if ((saddr.sin_addr.s_addr = inet_addr(hostp)) == -1) { - warnx("bad net address %s", hostp); - return (0); + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_NUMERICHOST; + hints.ai_socktype = nfsargsp->sotype; + if (getaddrinfo(hostp, "nfs", &hints, &ai_nfs) == 0) { + if ((nfsargsp->flags & NFSMNT_KERB)) { + hints.ai_flags = 0; + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, + sizeof host, serv, sizeof serv, 0) != 0) { + warnx("can't reverse resolve net address"); + return (0); + } + hostp = host; } - } else if ((hp = gethostbyname(hostp)) != NULL) - memmove(&saddr.sin_addr, hp->h_addr, - MIN(hp->h_length, sizeof(saddr.sin_addr))); - else { - warnx("can't get net id for host"); - return (0); - } -#ifdef NFSKERB - if ((nfsargsp->flags & NFSMNT_KERB)) { - if ((hp = gethostbyaddr((char *)&saddr.sin_addr.s_addr, - sizeof (u_long), AF_INET)) == (struct hostent *)0) { - warnx("can't reverse resolve net address"); + } else { + hints.ai_flags = 0; + if ((ecode = getaddrinfo(hostp, "nfs", &hints, &ai_nfs)) != 0) { + warnx("can't get net id for host/nfs: %s", + gai_strerror(ecode)); return (0); } - memmove(&saddr.sin_addr, hp->h_addr, - MIN(hp->h_length, sizeof(saddr.sin_addr))); + } +#ifdef NFSKERB + if (nfsargsp->flags & NFSMNT_KERB) { strncpy(inst, hp->h_name, INST_SZ); inst[INST_SZ - 1] = '\0'; if (cp = strchr(inst, '.')) @@ -713,7 +669,7 @@ getnfsargs(spec, nfsargsp) #endif /* NFSKERB */ orgcnt = retrycnt; -tryagain: + if (mountmode == ANY || mountmode == V3) { nfsvers = 3; mntvers = 3; @@ -724,103 +680,125 @@ tryagain: nfsargsp->flags &= ~NFSMNT_NFSV3; } nfhret.stat = EACCES; /* Mark not yet successful */ - while (retrycnt > 0) { - saddr.sin_family = AF_INET; - saddr.sin_port = htons(PMAPPORT); - if ((tport = port_no ? port_no : - pmap_getport(&saddr, RPCPROG_NFS, - nfsvers, nfsproto)) == 0) { - if ((opflags & ISBGRND) == 0) - clnt_pcreateerror("NFS Portmap"); + ai = ai_nfs; + while (ai != NULL) { + /* + * XXX. Nead a generic (family, type, proto) -> nconf interface. + * __rpc_*2nconf exist, maybe they should be exported. + */ + if (nfsargsp->sotype == SOCK_STREAM) { + if (ai->ai_family == AF_INET6) + netid = "tcp6"; + else + netid = "tcp"; } else { - /* - * First ping the nfs server to see if it supports - * the version of the protocol we want to use. - */ - clnt_stat = pingnfsserver(&saddr, nfsvers, - nfsargsp->sotype); - if (clnt_stat == RPC_PROGVERSMISMATCH) { - if (mountmode == ANY) { - mountmode = V2; - goto tryagain; - } else { - errx(1, "can't contact NFS server"); - } - } - saddr.sin_port = 0; - pertry.tv_sec = 10; - pertry.tv_usec = 0; - if (mnttcp_ok && nfsargsp->sotype == SOCK_STREAM) - clp = clnttcp_create(&saddr, RPCPROG_MNT, mntvers, - &so, 0, 0); + if (ai->ai_family == AF_INET6) + netid = "udp6"; else - clp = clntudp_create(&saddr, RPCPROG_MNT, mntvers, - pertry, &so); - if (clp == NULL) { + netid = "udp"; + } + + nconf = getnetconfigent(netid); + +tryagain: + retrycnt = orgcnt; + + while (retrycnt > 0) { + nfs_nb.buf = &nfs_ss; + nfs_nb.maxlen = sizeof nfs_ss; + if (!rpcb_getaddr(RPCPROG_NFS, nfsvers, nconf, + &nfs_nb, hostp)){ + if (rpc_createerr.cf_stat == RPC_SYSTEMERROR) { + nfhret.stat = rpc_createerr.cf_error.re_errno; + break; + } + if (rpc_createerr.cf_stat == RPC_UNKNOWNPROTO) { + nfhret.stat = EPROTONOSUPPORT; + break; + } if ((opflags & ISBGRND) == 0) - clnt_pcreateerror("Cannot MNT RPC"); + clnt_pcreateerror( + "mount_nfs: rpcbind on server"); } else { - clp->cl_auth = authunix_create_default(); - try.tv_sec = 10; - try.tv_usec = 0; - if (nfsargsp->flags & NFSMNT_KERB) - nfhret.auth = RPCAUTH_KERB4; - else - nfhret.auth = RPCAUTH_UNIX; - nfhret.vers = mntvers; - clnt_stat = clnt_call(clp, RPCMNT_MOUNT, - xdr_dir, spec, xdr_fh, &nfhret, try); - if (clnt_stat != RPC_SUCCESS) { - if (clnt_stat == RPC_PROGVERSMISMATCH) { - if (mountmode == ANY) { - mountmode = V2; - goto tryagain; - } else { - errx(1, "%s", - clnt_sperror(clp, "MNT RPC")); - } - } + pertry.tv_sec = 10; + pertry.tv_usec = 0; + /* + * XXX relies on clnt_tcp_create to bind + * to a reserved socket. + */ + clp = clnt_tp_create(hostp, RPCPROG_MNT, mntvers, + mnttcp_ok ? nconf : getnetconfigent("udp")); + if (clp == NULL) { if ((opflags & ISBGRND) == 0) - warnx("%s", clnt_sperror(clp, - "bad MNT RPC")); + clnt_pcreateerror("Cannot MNT RPC"); } else { - retrycnt = 0; + CLNT_CONTROL(clp, CLSET_RETRY_TIMEOUT, + (char *)&pertry); + clp->cl_auth = authsys_create_default(); + try.tv_sec = 10; + try.tv_usec = 0; + if (nfsargsp->flags & NFSMNT_KERB) + nfhret.auth = RPCAUTH_KERB4; + else + nfhret.auth = RPCAUTH_UNIX; + nfhret.vers = mntvers; + clnt_stat = clnt_call(clp, RPCMNT_MOUNT, + xdr_dir, spec, xdr_fh, &nfhret, try); + if (clnt_stat != RPC_SUCCESS) { + if (clnt_stat == RPC_PROGVERSMISMATCH) { + if (mountmode == ANY) { + mountmode = V2; + goto tryagain; + } else { + errx(1, "%s", + clnt_sperror(clp, "MNT RPC")); + } + } + if ((opflags & ISBGRND) == 0) + warnx("%s", clnt_sperror(clp, + "bad MNT RPC")); + } else { + retrycnt = 0; + } + auth_destroy(clp->cl_auth); + clnt_destroy(clp); + so = RPC_ANYSOCK; } - auth_destroy(clp->cl_auth); - clnt_destroy(clp); - so = RPC_ANYSOCK; } - } - if (--retrycnt > 0) { - if (opflags & BGRND) { - warnx("Cannot immediately mount %s:%s, " - "backgrounding", hostp, spec); - opflags &= ~BGRND; - if ((i = fork())) { - if (i == -1) - err(1, "nqnfs 2"); - exit(0); + if (--retrycnt > 0) { + if (opflags & BGRND) { + warnx("Cannot immediately mount %s:%s, " + "backgrounding", hostp, spec); + opflags &= ~BGRND; + if ((i = fork())) { + if (i == -1) + err(1, "nqnfs 2"); + exit(0); + } + (void) setsid(); + (void) close(STDIN_FILENO); + (void) close(STDOUT_FILENO); + (void) close(STDERR_FILENO); + (void) chdir("/"); + opflags |= ISBGRND; } - (void) setsid(); - (void) close(STDIN_FILENO); - (void) close(STDOUT_FILENO); - (void) close(STDERR_FILENO); - (void) chdir("/"); - opflags |= ISBGRND; + sleep(60); } - sleep(60); } + if (nfhret.stat == 0) + break; + ai = ai->ai_next; } + freeaddrinfo(ai_nfs); if (nfhret.stat) { if (opflags & ISBGRND) exit(1); warnx("can't access %s: %s", spec, strerror(nfhret.stat)); return (0); } - saddr.sin_port = htons(tport); { - nfsargsp->addr = (struct sockaddr *) &saddr; - nfsargsp->addrlen = sizeof (saddr); + nfsargsp->addr = (struct sockaddr *) nfs_nb.buf; + nfsargsp->addrlen = nfs_nb.len; } nfsargsp->fh = nfhret.nfh; nfsargsp->fhsize = nfhret.fhsize; diff --git a/sbin/mountd/mountd.c b/sbin/mountd/mountd.c index 2f46a9f2ca9c..b5277656ac1e 100644 --- a/sbin/mountd/mountd.c +++ b/sbin/mountd/mountd.c @@ -50,12 +50,15 @@ static const char rcsid[] = #include <sys/param.h> #include <sys/mount.h> +#include <sys/fcntl.h> #include <sys/stat.h> #include <sys/syslog.h> #include <sys/sysctl.h> #include <rpc/rpc.h> #include <rpc/pmap_clnt.h> +#include <rpc/pmap_prot.h> +#include <rpcsvc/mount.h> #include <nfs/rpcv2.h> #include <nfs/nfsproto.h> #include <nfs/nfs.h> @@ -83,6 +86,10 @@ static const char rcsid[] = #include <stdarg.h> #endif +#ifndef MOUNTDLOCK +#define MOUNTDLOCK "/var/run/mountd.lock" +#endif + /* * Structures for keeping the mount list and export list */ @@ -117,13 +124,13 @@ struct exportlist { #define EX_LINKED 0x1 struct netmsk { - u_int32_t nt_net; + struct sockaddr_storage nt_net; u_int32_t nt_mask; char *nt_name; }; union grouptypes { - struct hostent *gt_hostent; + struct addrinfo *gt_addrinfo; struct netmsk gt_net; }; @@ -157,8 +164,8 @@ void add_dlist __P((struct dirlist **, struct dirlist *, void add_mlist __P((char *, char *)); int check_dirpath __P((char *)); int check_options __P((struct dirlist *)); -int chk_host __P((struct dirlist *, u_int32_t, int *, int *)); -void del_mlist __P((char *, char *)); +int chk_host __P((struct dirlist *, struct sockaddr *, int *, int *)); +int del_mlist __P((char *, char *, struct sockaddr *)); struct dirlist *dirp_search __P((struct dirlist *, char *)); int do_mount __P((struct exportlist *, struct grouplist *, int, struct xucred *, char *, int, struct statfs *)); @@ -186,18 +193,25 @@ void nextfield __P((char **, char **)); void out_of_mem __P((void)); void parsecred __P((char *, struct xucred *)); int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *)); -int scan_tree __P((struct dirlist *, u_int32_t)); +int scan_tree __P((struct dirlist *, struct sockaddr *)); static void usage __P((void)); int xdr_dir __P((XDR *, char *)); int xdr_explist __P((XDR *, caddr_t)); int xdr_fhs __P((XDR *, caddr_t)); int xdr_mlist __P((XDR *, caddr_t)); +void terminate __P((int)); /* C library */ int getnetgrent(); void endnetgrent(); void setnetgrent(); +static int bitcmp __P((void *, void *, int)); +static int netpartcmp __P((struct sockaddr *, struct sockaddr *, int)); +static int sacmp __P((struct sockaddr *, struct sockaddr *)); +static int allones __P((struct sockaddr_storage *, int)); +static int countones __P((struct sockaddr *)); + struct exportlist *exphead; struct mountlist *mlhead; struct grouplist *grphead; @@ -213,7 +227,16 @@ int force_v2 = 0; int resvport_only = 1; int dir_only = 1; int log = 0; + int opt_flags; +static int have_v6 = 1; +#ifdef NI_WITHSCOPEID +static const int ninumeric = NI_NUMERICHOST | NI_WITHSCOPEID; +#else +static const int ninumeric = NI_NUMERICHOST; +#endif + +int mountdlockfd; /* Bits for above */ #define OP_MAPROOT 0x01 #define OP_MAPALL 0x02 @@ -221,6 +244,7 @@ int opt_flags; #define OP_MASK 0x08 #define OP_NET 0x10 #define OP_ALLDIRS 0x40 +#define OP_MASKLEN 0x200 #ifdef DEBUG int debug = 1; @@ -242,10 +266,26 @@ main(argc, argv) int argc; char **argv; { - SVCXPRT *udptransp, *tcptransp; + SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp; + struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf; + int udpsock, tcpsock, udp6sock, tcp6sock; + int xcreated = 0, s; + int one = 1; int c, error, mib[3]; struct vfsconf vfc; + /* Check that another mountd isn't already running. */ + + if ((mountdlockfd = (open(MOUNTDLOCK, O_RDONLY|O_CREAT, 0444))) == -1) + err(1, "%s", MOUNTDLOCK); + + if(flock(mountdlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK) + errx(1, "another rpc.mountd is already running. Aborting"); + s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s < 0) + have_v6 = 0; + else + close(s); error = getvfsbyname("nfs", &vfc); if (error && vfsisloadable("nfs")) { if(vfsload("nfs")) @@ -301,12 +341,38 @@ main(argc, argv) signal(SIGQUIT, SIG_IGN); } signal(SIGHUP, (void (*) __P((int))) get_exportlist); + signal(SIGTERM, terminate); { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w"); if (pidfile != NULL) { fprintf(pidfile, "%d\n", getpid()); fclose(pidfile); } } + rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL); + rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL); + udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + tcpsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + /* + * We're doing host-based access checks here, so don't allow + * v4-in-v6 to confuse things. The kernel will disable it + * by default on NFS sockets too. + */ + if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6, + IPV6_BINDV6ONLY, &one, sizeof one) < 0){ + syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket"); + exit(1); + } + if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6, + IPV6_BINDV6ONLY, &one, sizeof one) < 0){ + syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket"); + exit(1); + } + udpconf = getnetconfigent("udp"); + tcpconf = getnetconfigent("tcp"); + udp6conf = getnetconfigent("udp6"); + tcp6conf = getnetconfigent("tcp6"); if (!resvport_only) { mib[0] = CTL_VFS; mib[1] = vfc.vfc_typenum; @@ -322,17 +388,90 @@ main(argc, argv) syslog(LOG_ERR, "can't create socket"); exit(1); } - pmap_unset(RPCPROG_MNT, 1); - pmap_unset(RPCPROG_MNT, 3); - if (!force_v2) - if (!svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) || - !svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) { - syslog(LOG_ERR, "can't register mount"); - exit(1); - } - if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) || - !svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_TCP)) { - syslog(LOG_ERR, "can't register mount"); + if (udpsock != -1 && udpconf != NULL) { + bindresvport(udpsock, NULL); + udptransp = svc_dg_create(udpsock, 0, 0); + if (udptransp != NULL) { + if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1, + mntsrv, udpconf)) + syslog(LOG_WARNING, "can't register UDP RPCMNT_VER1 service"); + else + xcreated++; + if (!force_v2) { + if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3, + mntsrv, udpconf)) + syslog(LOG_WARNING, "can't register UDP RPCMNT_VER3 service"); + else + xcreated++; + } + } else + syslog(LOG_WARNING, "can't create UDP services"); + + } + if (tcpsock != -1 && tcpconf != NULL) { + bindresvport(tcpsock, NULL); + listen(tcpsock, SOMAXCONN); + tcptransp = svc_vc_create(tcpsock, 0, 0); + if (tcptransp != NULL) { + if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1, + mntsrv, tcpconf)) + syslog(LOG_WARNING, "can't register TCP RPCMNT_VER1 service"); + else + xcreated++; + if (!force_v2) { + if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3, + mntsrv, tcpconf)) + syslog(LOG_WARNING, "can't register TCP RPCMNT_VER3 service"); + else + xcreated++; + } + } else + syslog(LOG_WARNING, "can't create TCP service"); + + } + if (udp6sock != -1 && udp6conf != NULL) { + bindresvport(udp6sock, NULL); + udp6transp = svc_dg_create(udp6sock, 0, 0); + if (udp6transp != NULL) { + if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1, + mntsrv, udp6conf)) + syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER1 service"); + else + xcreated++; + if (!force_v2) { + if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3, + mntsrv, udp6conf)) + syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER3 service"); + else + xcreated++; + } + } else + syslog(LOG_WARNING, "can't create UDP6 service"); + + } + if (tcp6sock != -1 && tcp6conf != NULL) { + bindresvport(tcp6sock, NULL); + listen(tcp6sock, SOMAXCONN); + tcp6transp = svc_vc_create(tcp6sock, 0, 0); + if (tcp6transp != NULL) { + if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1, + mntsrv, tcp6conf)) + syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER1 service"); + else + xcreated++; + if (!force_v2) { + if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3, + mntsrv, tcp6conf)) + syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER3 service"); + else + xcreated++; + } + } else + syslog(LOG_WARNING, "can't create TCP6 service"); + + } + if (xcreated == 0) { + syslog(LOG_ERR, "could not create any services"); exit(1); } svc_run(); @@ -361,20 +500,38 @@ mntsrv(rqstp, transp) struct fhreturn fhr; struct stat stb; struct statfs fsb; - struct hostent *hp; - struct in_addr saddrin; - u_int32_t saddr; + struct addrinfo *ai; + char host[NI_MAXHOST], numerichost[NI_MAXHOST]; + int lookup_failed = 1; + struct sockaddr *saddr; u_short sport; char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN]; int bad = 0, defset, hostset; sigset_t sighup_mask; + struct sockaddr_in6 *sin6; + struct sockaddr_in *sin; sigemptyset(&sighup_mask); sigaddset(&sighup_mask, SIGHUP); - saddr = transp->xp_raddr.sin_addr.s_addr; - saddrin = transp->xp_raddr.sin_addr; - sport = ntohs(transp->xp_raddr.sin_port); - hp = (struct hostent *)NULL; + saddr = svc_getrpccaller(transp)->buf; + switch (saddr->sa_family) { + case AF_INET6: + sin6 = (struct sockaddr_in6 *)saddr; + sport = ntohs(sin6->sin6_port); + break; + case AF_INET: + sin = (struct sockaddr_in *)saddr; + sport = ntohs(sin->sin_port); + break; + default: + syslog(LOG_ERR, "request from unknown address family"); + return; + } + lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host, + NULL, 0, 0); + getnameinfo(saddr, saddr->sa_len, numerichost, + sizeof numerichost, NULL, 0, NI_NUMERICHOST); + ai = NULL; switch (rqstp->rq_proc) { case NULLPROC: if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) @@ -384,13 +541,13 @@ mntsrv(rqstp, transp) if (sport >= IPPORT_RESERVED && resvport_only) { syslog(LOG_NOTICE, "mount request from %s from unprivileged port", - inet_ntoa(saddrin)); + numerichost); svcerr_weakauth(transp); return; } if (!svc_getargs(transp, xdr_dir, rpcpath)) { syslog(LOG_NOTICE, "undecodable mount request from %s", - inet_ntoa(saddrin)); + numerichost); svcerr_decode(transp); return; } @@ -403,12 +560,12 @@ mntsrv(rqstp, transp) if (realpath(rpcpath, dirpath) == NULL || stat(dirpath, &stb) < 0 || (!S_ISDIR(stb.st_mode) && - (dir_only || !S_ISREG(stb.st_mode))) || + (dir_only || !S_ISREG(stb.st_mode))) || statfs(dirpath, &fsb) < 0) { chdir("/"); /* Just in case realpath doesn't */ syslog(LOG_NOTICE, "mount request from %s for non existent path %s", - inet_ntoa(saddrin), dirpath); + numerichost, dirpath); if (debug) warnx("stat failed on %s", dirpath); bad = ENOENT; /* We will send error reply later */ @@ -420,9 +577,9 @@ mntsrv(rqstp, transp) hostset = defset = 0; if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) || ((dp = dirp_search(ep->ex_dirl, dirpath)) && - chk_host(dp, saddr, &defset, &hostset)) || - (defset && scan_tree(ep->ex_defdir, saddr) == 0 && - scan_tree(ep->ex_dirl, saddr) == 0))) { + chk_host(dp, saddr, &defset, &hostset)) || + (defset && scan_tree(ep->ex_defdir, saddr) == 0 && + scan_tree(ep->ex_dirl, saddr) == 0))) { if (bad) { if (!svc_sendreply(transp, xdr_long, (caddr_t)&bad)) @@ -448,25 +605,21 @@ mntsrv(rqstp, transp) } if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr)) syslog(LOG_ERR, "can't send reply"); - if (hp == NULL) - hp = gethostbyaddr((caddr_t)&saddr, - sizeof(saddr), AF_INET); - if (hp) - add_mlist(hp->h_name, dirpath); + if (!lookup_failed) + add_mlist(host, dirpath); else - add_mlist(inet_ntoa(saddrin), - dirpath); + add_mlist(numerichost, dirpath); if (debug) warnx("mount successful"); if (log) syslog(LOG_NOTICE, "mount request succeeded from %s for %s", - inet_ntoa(saddrin), dirpath); + numerichost, dirpath); } else { bad = EACCES; syslog(LOG_NOTICE, "mount request denied from %s for %s", - inet_ntoa(saddrin), dirpath); + numerichost, dirpath); } if (bad && !svc_sendreply(transp, xdr_long, (caddr_t)&bad)) @@ -479,56 +632,54 @@ mntsrv(rqstp, transp) else if (log) syslog(LOG_NOTICE, "dump request succeeded from %s", - inet_ntoa(saddrin)); + numerichost); return; case RPCMNT_UMOUNT: if (sport >= IPPORT_RESERVED && resvport_only) { syslog(LOG_NOTICE, "umount request from %s from unprivileged port", - inet_ntoa(saddrin)); + numerichost); svcerr_weakauth(transp); return; } if (!svc_getargs(transp, xdr_dir, rpcpath)) { syslog(LOG_NOTICE, "undecodable umount request from %s", - inet_ntoa(saddrin)); + numerichost); svcerr_decode(transp); return; } if (realpath(rpcpath, dirpath) == NULL) { syslog(LOG_NOTICE, "umount request from %s " "for non existent path %s", - inet_ntoa(saddrin), dirpath); + numerichost, dirpath); } if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) syslog(LOG_ERR, "can't send reply"); - hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); - if (hp) - del_mlist(hp->h_name, dirpath); - del_mlist(inet_ntoa(saddrin), dirpath); + if (!lookup_failed) + del_mlist(host, dirpath, saddr); + del_mlist(numerichost, dirpath, saddr); if (log) syslog(LOG_NOTICE, "umount request succeeded from %s for %s", - inet_ntoa(saddrin), dirpath); + numerichost, dirpath); return; case RPCMNT_UMNTALL: if (sport >= IPPORT_RESERVED && resvport_only) { syslog(LOG_NOTICE, "umountall request from %s from unprivileged port", - inet_ntoa(saddrin)); + numerichost); svcerr_weakauth(transp); return; } if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL)) syslog(LOG_ERR, "can't send reply"); - hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET); - if (hp) - del_mlist(hp->h_name, (char *)NULL); - del_mlist(inet_ntoa(saddrin), (char *)NULL); + if (!lookup_failed) + del_mlist(host, NULL, saddr); + del_mlist(numerichost, NULL, saddr); if (log) syslog(LOG_NOTICE, "umountall request succeeded from %s", - inet_ntoa(saddrin)); + numerichost); return; case RPCMNT_EXPORT: if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL)) @@ -536,7 +687,7 @@ mntsrv(rqstp, transp) if (log) syslog(LOG_NOTICE, "export request succeeded from %s", - inet_ntoa(saddrin)); + numerichost); return; default: svcerr_noproc(transp); @@ -690,7 +841,7 @@ put_exlist(dp, xdrsp, adp, putdefp) if (grp->gr_type == GT_HOST) { if (!xdr_bool(xdrsp, &true)) return (1); - strp = grp->gr_ptr.gt_hostent->h_name; + strp = grp->gr_ptr.gt_addrinfo->ai_canonname; if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN)) return (1); @@ -732,7 +883,7 @@ get_exportlist() struct exportlist **epp; struct dirlist *dirhead; struct statfs fsb, *fsp; - struct hostent *hpe; + struct addrinfo *ai; struct xucred anon; char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp; @@ -786,7 +937,7 @@ get_exportlist() fsp->f_flags | MNT_UPDATE, (caddr_t)&targs) < 0) syslog(LOG_ERR, "can't delete exports for %s", - fsp->f_mntonname); + fsp->f_mntonname); } fsp++; } @@ -874,9 +1025,9 @@ get_exportlist() else out_of_mem(); if (debug) - warnx("making new ep fs=0x%x,0x%x", - fsb.f_fsid.val[0], - fsb.f_fsid.val[1]); + warnx("making new ep fs=0x%x,0x%x", + fsb.f_fsid.val[0], + fsb.f_fsid.val[1]); } else if (debug) warnx("found ep fs=0x%x,0x%x", fsb.f_fsid.val[0], @@ -944,14 +1095,17 @@ get_exportlist() if (debug) warnx("adding a default entry"); /* add a default group and make the grp list NULL */ - hpe = (struct hostent *)malloc(sizeof(struct hostent)); - if (hpe == (struct hostent *)NULL) - out_of_mem(); - hpe->h_name = strdup("Default"); - hpe->h_addrtype = AF_INET; - hpe->h_length = sizeof (u_int32_t); - hpe->h_addr_list = (char **)NULL; - grp->gr_ptr.gt_hostent = hpe; + ai = malloc(sizeof(struct addrinfo)); + ai->ai_flags = 0; + ai->ai_family = AF_INET; /* XXXX */ + ai->ai_socktype = SOCK_DGRAM; + /* setting the length to 0 will match anything */ + ai->ai_addrlen = 0; + ai->ai_flags = AI_CANONNAME; + ai->ai_canonname = strdup("Default"); + ai->ai_addr = NULL; + ai->ai_next = NULL; + grp->gr_ptr.gt_addrinfo = ai; /* * Don't allow a network export coincide with a list of @@ -961,13 +1115,13 @@ get_exportlist() getexp_err(ep, tgrp); goto nextline; - /* - * If an export list was specified on this line, make sure + /* + * If an export list was specified on this line, make sure * that we have at least one valid entry, otherwise skip it. */ } else { grp = tgrp; - while (grp && grp->gr_type == GT_IGNORE) + while (grp && grp->gr_type == GT_IGNORE) grp = grp->gr_next; if (! grp) { getexp_err(ep, tgrp); @@ -1219,19 +1373,27 @@ add_dlist(dpp, newdp, grp, flags) /* * Search for a dirpath on the export point. */ +void * +test() +{ +} + +/* + * Search for a dirpath on the export point. + */ struct dirlist * -dirp_search(dp, dirpath) +dirp_search(dp, dirp) struct dirlist *dp; - char *dirpath; + char *dirp; { int cmp; if (dp) { - cmp = strcmp(dp->dp_dirp, dirpath); + cmp = strcmp(dp->dp_dirp, dirp); if (cmp > 0) - return (dirp_search(dp->dp_left, dirpath)); + return (dirp_search(dp->dp_left, dirp)); else if (cmp < 0) - return (dirp_search(dp->dp_right, dirpath)); + return (dirp_search(dp->dp_right, dirp)); else return (dp); } @@ -1239,18 +1401,59 @@ dirp_search(dp, dirpath) } /* + * Some helper functions for netmasks. They all assume masks in network + * order (big endian). + */ +static int +bitcmp(void *dst, void *src, int bitlen) +{ + int i; + u_int8_t *p1 = dst, *p2 = src; + u_int8_t bitmask; + int bytelen, bitsleft; + + bytelen = bitlen / 8; + bitsleft = bitlen % 8; + + if (debug) { + printf("comparing:\n"); + for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++) + printf("%02x", p1[i]); + printf("\n"); + for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++) + printf("%02x", p2[i]); + printf("\n"); + } + + for (i = 0; i < bytelen; i++) { + if (*p1 != *p2) + return 1; + p1++; + p2++; + } + + for (i = 0; i < bitsleft; i++) { + bitmask = 1 << (7 - i); + if ((*p1 & bitmask) != (*p2 & bitmask)) + return 1; + } + + return 0; +} + +/* * Scan for a host match in a directory tree. */ int chk_host(dp, saddr, defsetp, hostsetp) struct dirlist *dp; - u_int32_t saddr; + struct sockaddr *saddr; int *defsetp; int *hostsetp; { struct hostlist *hp; struct grouplist *grp; - u_int32_t **addrp; + struct addrinfo *ai; if (dp) { if (dp->dp_flag & DP_DEFSET) @@ -1260,22 +1463,22 @@ chk_host(dp, saddr, defsetp, hostsetp) grp = hp->ht_grp; switch (grp->gr_type) { case GT_HOST: - addrp = (u_int32_t **) - grp->gr_ptr.gt_hostent->h_addr_list; - while (*addrp) { - if (**addrp == saddr) { - *hostsetp = (hp->ht_flag | DP_HOSTSET); - return (1); + ai = grp->gr_ptr.gt_addrinfo; + for (; ai; ai = ai->ai_next) { + if (!sacmp(ai->ai_addr, saddr)) { + *hostsetp = + (hp->ht_flag | DP_HOSTSET); + return (1); + } } - addrp++; - } break; case GT_NET: - if ((saddr & grp->gr_ptr.gt_net.nt_mask) == - grp->gr_ptr.gt_net.nt_net) { - *hostsetp = (hp->ht_flag | DP_HOSTSET); - return (1); - } + if (!netpartcmp(saddr, + (struct sockaddr *) &grp->gr_ptr.gt_net.nt_net, + grp->gr_ptr.gt_net.nt_mask)) { + *hostsetp = (hp->ht_flag | DP_HOSTSET); + return (1); + } break; }; hp = hp->ht_next; @@ -1290,7 +1493,7 @@ chk_host(dp, saddr, defsetp, hostsetp) int scan_tree(dp, saddr) struct dirlist *dp; - u_int32_t saddr; + struct sockaddr *saddr; { int defset, hostset; @@ -1392,6 +1595,11 @@ do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr) opt_flags |= OP_MASK; } else if (cpoptarg && (!strcmp(cpopt, "network") || !strcmp(cpopt, "n"))) { + if (strchr(cpoptarg, '/') != NULL) { + if (debug) + fprintf(stderr, "setting OP_MASKLEN\n"); + opt_flags |= OP_MASKLEN; + } if (grp->gr_type != GT_NULL) { syslog(LOG_ERR, "network/host conflict"); return (1); @@ -1442,84 +1650,40 @@ get_host(cp, grp, tgrp) struct grouplist *tgrp; { struct grouplist *checkgrp; - struct hostent *hp, *nhp; - char **addrp, **naddrp; - struct hostent t_host; + struct addrinfo *ai, hints; + int ecode; + char host[NI_MAXHOST]; int i; - u_int32_t saddr; char *aptr[2]; - if (grp->gr_type != GT_NULL) + if (grp->gr_type != GT_NULL) { + syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp); return (1); - if ((hp = gethostbyname(cp)) == NULL) { - if (isdigit(*cp)) { - saddr = inet_addr(cp); - if (saddr == -1) { - syslog(LOG_ERR, "inet_addr failed for %s", cp); - return (1); - } - if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr), - AF_INET)) == NULL) { - hp = &t_host; - hp->h_name = cp; - hp->h_addrtype = AF_INET; - hp->h_length = sizeof (u_int32_t); - hp->h_addr_list = aptr; - aptr[0] = (char *)&saddr; - aptr[1] = (char *)NULL; - } - } else { - syslog(LOG_ERR, "gethostbyname failed for %s", cp); - return (1); - } } - /* - * Sanity check: make sure we don't already have an entry - * for this host in the grouplist. - */ - checkgrp = tgrp; - while (checkgrp != NULL) { - if (checkgrp->gr_type == GT_HOST && - checkgrp->gr_ptr.gt_hostent != NULL && - (!strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name) - || *(u_int32_t *)checkgrp->gr_ptr.gt_hostent->h_addr == - *(u_int32_t *)hp->h_addr)) { - grp->gr_type = GT_IGNORE; - return(0); - } - checkgrp = checkgrp->gr_next; - } - + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_CANONNAME; + hints.ai_protocol = IPPROTO_UDP; + ecode = getaddrinfo(cp, NULL, &hints, &ai); + if (ecode != 0) { + syslog(LOG_ERR,"can't get address info for " + "host %s", cp); + return 1; + } grp->gr_type = GT_HOST; - nhp = grp->gr_ptr.gt_hostent = (struct hostent *) - malloc(sizeof(struct hostent)); - if (nhp == (struct hostent *)NULL) - out_of_mem(); - memmove(nhp, hp, sizeof(struct hostent)); - i = strlen(hp->h_name)+1; - nhp->h_name = (char *)malloc(i); - if (nhp->h_name == (char *)NULL) - out_of_mem(); - memmove(nhp->h_name, hp->h_name, i); - addrp = hp->h_addr_list; - i = 1; - while (*addrp++) - i++; - naddrp = nhp->h_addr_list = (char **)malloc(i*sizeof(char *)); - if (naddrp == (char **)NULL) - out_of_mem(); - addrp = hp->h_addr_list; - while (*addrp) { - *naddrp = (char *)malloc(hp->h_length); - if (*naddrp == (char *)NULL) - out_of_mem(); - memmove(*naddrp, *addrp, hp->h_length); - addrp++; - naddrp++; + grp->gr_ptr.gt_addrinfo = ai; + while (ai != NULL) { + if (ai->ai_canonname == NULL) { + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host, + sizeof host, NULL, 0, ninumeric) != 0) + strlcpy(host, "?", sizeof(host)); + ai->ai_canonname = strdup(host); + ai->ai_flags |= AI_CANONNAME; + } else + ai->ai_flags &= ~AI_CANONNAME; + if (debug) + (void)fprintf(stderr, "got host %s\n", ai->ai_canonname); + ai = ai->ai_next; } - *naddrp = (char *)NULL; - if (debug) - warnx("got host %s", hp->h_name); return (0); } @@ -1597,68 +1761,64 @@ do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb) int dirplen; struct statfs *fsb; { - char *cp = (char *)NULL; - u_int32_t **addrp; + struct sockaddr *addrp; + struct sockaddr_storage ss; + struct addrinfo *ai; + int addrlen; + char *cp = NULL; int done; char savedc = '\0'; - struct sockaddr_in sin, imask; union { struct ufs_args ua; struct iso_args ia; struct mfs_args ma; #ifdef __NetBSD__ struct msdosfs_args da; + struct adosfs_args aa; #endif struct ntfs_args na; } args; - u_int32_t net; args.ua.fspec = 0; args.ua.export.ex_flags = exflags; args.ua.export.ex_anon = *anoncrp; args.ua.export.ex_indexfile = ep->ex_indexfile; - memset(&sin, 0, sizeof(sin)); - memset(&imask, 0, sizeof(imask)); - sin.sin_family = AF_INET; - sin.sin_len = sizeof(sin); - imask.sin_family = AF_INET; - imask.sin_len = sizeof(sin); - if (grp->gr_type == GT_HOST) - addrp = (u_int32_t **)grp->gr_ptr.gt_hostent->h_addr_list; - else - addrp = (u_int32_t **)NULL; + if (grp->gr_type == GT_HOST) { + ai = grp->gr_ptr.gt_addrinfo; + addrp = ai->ai_addr; + addrlen = ai->ai_addrlen; + } else + addrp = NULL; done = FALSE; while (!done) { switch (grp->gr_type) { case GT_HOST: - if (addrp) { - sin.sin_addr.s_addr = **addrp; - args.ua.export.ex_addrlen = sizeof(sin); - } else - args.ua.export.ex_addrlen = 0; - args.ua.export.ex_addr = (struct sockaddr *)&sin; + if (addrp != NULL && addrp->sa_family == AF_INET6 && + have_v6 == 0) + goto skip; + args.ua.export.ex_addr = addrp; + args.ua.export.ex_addrlen = addrlen; args.ua.export.ex_masklen = 0; break; case GT_NET: - if (grp->gr_ptr.gt_net.nt_mask) - imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask; - else { - net = ntohl(grp->gr_ptr.gt_net.nt_net); - if (IN_CLASSA(net)) - imask.sin_addr.s_addr = inet_addr("255.0.0.0"); - else if (IN_CLASSB(net)) - imask.sin_addr.s_addr = - inet_addr("255.255.0.0"); - else - imask.sin_addr.s_addr = - inet_addr("255.255.255.0"); - grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr; + args.ua.export.ex_addr = (struct sockaddr *) + &grp->gr_ptr.gt_net.nt_net; + if (args.ua.export.ex_addr->sa_family == AF_INET6 && + have_v6 == 0) + goto skip; + args.ua.export.ex_addrlen = + args.ua.export.ex_addr->sa_len; + memset(&ss, 0, sizeof ss); + ss.ss_family = args.ua.export.ex_addr->sa_family; + ss.ss_len = args.ua.export.ex_addr->sa_len; + if (allones(&ss, grp->gr_ptr.gt_net.nt_mask) != 0) { + syslog(LOG_ERR, "Bad network flag"); + if (cp) + *cp = savedc; + return (1); } - sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net; - args.ua.export.ex_addr = (struct sockaddr *)&sin; - args.ua.export.ex_addrlen = sizeof (sin); - args.ua.export.ex_mask = (struct sockaddr *)&imask; - args.ua.export.ex_masklen = sizeof (imask); + args.ua.export.ex_mask = (struct sockaddr *)&ss; + args.ua.export.ex_masklen = ss.ss_len; break; case GT_IGNORE: return(0); @@ -1678,7 +1838,7 @@ do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb) * exportable file systems and not just "ufs". */ while (mount(fsb->f_fstypename, dirp, - fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) { + fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) { if (cp) *cp-- = savedc; else @@ -1707,10 +1867,15 @@ do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb) savedc = *cp; *cp = '\0'; } +skip: if (addrp) { - ++addrp; - if (*addrp == (u_int32_t *)NULL) + ai = ai->ai_next; + if (ai == NULL) done = TRUE; + else { + addrp = ai->ai_addr; + addrlen = ai->ai_addrlen; + } } else done = TRUE; } @@ -1729,47 +1894,105 @@ get_net(cp, net, maskflg) int maskflg; { struct netent *np; - long netaddr; - struct in_addr inetaddr, inetaddr2; - char *name; + char *name, *p, *prefp; + struct sockaddr_in sin, *sinp; + struct sockaddr *sa; + struct addrinfo hints, *ai = NULL; + char netname[NI_MAXHOST]; + long preflen; + int ecode; - if (isdigit(*cp) && ((netaddr = inet_network(cp)) != -1)) { - inetaddr = inet_makeaddr(netaddr, 0); - /* - * Due to arbitrary subnet masks, you don't know how many - * bits to shift the address to make it into a network, - * however you do know how to make a network address into - * a host with host == 0 and then compare them. - * (What a pest) - */ - if (!maskflg) { - setnetent(0); - while ((np = getnetent())) { - inetaddr2 = inet_makeaddr(np->n_net, 0); - if (inetaddr2.s_addr == inetaddr.s_addr) - break; - } - endnetent(); - } - } else if ((np = getnetbyname(cp)) != NULL) { - inetaddr = inet_makeaddr(np->n_net, 0); + if ((opt_flags & OP_MASKLEN) && !maskflg) { + p = strchr(cp, '/'); + *p = '\0'; + prefp = p + 1; + } + + if ((np = getnetbyname(cp)) != NULL) { + sin.sin_family = AF_INET; + sin.sin_len = sizeof sin; + sin.sin_addr = inet_makeaddr(np->n_net, 0); + sa = (struct sockaddr *)&sin; + } else if (isdigit(*cp)) { + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(cp, NULL, &hints, &ai) != 0) { + /* + * If getaddrinfo() failed, try the inet4 network + * notation with less than 3 dots. + */ + sin.sin_family = AF_INET; + sin.sin_len = sizeof sin; + sin.sin_addr = inet_makeaddr(inet_network(cp),0); + if (debug) + fprintf(stderr, "get_net: v4 addr %x\n", + sin.sin_addr.s_addr); + sa = (struct sockaddr *)&sin; + } else + sa = ai->ai_addr; + } else if (isxdigit(*cp) || *cp == ':') { + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + if (getaddrinfo(cp, NULL, &hints, &ai) == 0) + sa = ai->ai_addr; + else + goto fail; } else - return (1); + goto fail; + + ecode = getnameinfo(sa, sa->sa_len, netname, sizeof netname, + NULL, 0, ninumeric); + if (ecode != 0) + goto fail; if (maskflg) - net->nt_mask = inetaddr.s_addr; + net->nt_mask = countones(sa); else { + if (opt_flags & OP_MASKLEN) { + preflen = strtol(prefp, NULL, 10); + if (preflen == LONG_MIN && errno == ERANGE) + goto fail; + net->nt_mask = (int)preflen; + *p = '/'; + } + if (np) name = np->n_name; + else { + if (getnameinfo(sa, sa->sa_len, netname, sizeof netname, + NULL, 0, ninumeric) != 0) + strlcpy(netname, "?", sizeof(netname)); + name = netname; + } + net->nt_name = strdup(name); + memcpy(&net->nt_net, sa, sa->sa_len); + } + + if (!maskflg && sa->sa_family == AF_INET && + !(opt_flags & (OP_MASK|OP_MASKLEN))) { + sinp = (struct sockaddr_in *)sa; + if (IN_CLASSA(sinp->sin_addr.s_addr)) + net->nt_mask = 8; + else if (IN_CLASSB(sinp->sin_addr.s_addr)) + net->nt_mask = 16; + else if (IN_CLASSC(sinp->sin_addr.s_addr)) + net->nt_mask = 24; + else if (IN_CLASSD(sinp->sin_addr.s_addr)) + net->nt_mask = 28; else - name = inet_ntoa(inetaddr); - net->nt_name = (char *)malloc(strlen(name) + 1); - if (net->nt_name == (char *)NULL) - out_of_mem(); - strcpy(net->nt_name, name); - net->nt_net = inetaddr.s_addr; + net->nt_mask = 32; /* XXX */ } - return (0); + + if (ai) + freeaddrinfo(ai); + return 0; + +fail: + if (ai) + freeaddrinfo(ai); + return 1; } /* @@ -1958,15 +2181,28 @@ get_mountlist() fclose(mlfile); } -void -del_mlist(hostp, dirp) +int +del_mlist(hostp, dirp, saddr) char *hostp, *dirp; + struct sockaddr *saddr; { struct mountlist *mlp, **mlpp; struct mountlist *mlp2; + u_short sport; FILE *mlfile; int fnd = 0; + char host[NI_MAXHOST]; + switch (saddr->sa_family) { + case AF_INET6: + sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); + break; + case AF_INET: + sport = ntohs(((struct sockaddr_in *)saddr)->sin_port); + break; + default: + return -1; + } mlpp = &mlhead; mlp = mlhead; while (mlp) { @@ -2034,17 +2270,11 @@ void free_grp(grp) struct grouplist *grp; { - char **addrp; + struct addrinfo *ai; if (grp->gr_type == GT_HOST) { - if (grp->gr_ptr.gt_hostent->h_name) { - addrp = grp->gr_ptr.gt_hostent->h_addr_list; - while (addrp && *addrp) - free(*addrp++); - free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list); - free(grp->gr_ptr.gt_hostent->h_name); - } - free((caddr_t)grp->gr_ptr.gt_hostent); + if (grp->gr_ptr.gt_addrinfo != NULL) + freeaddrinfo(grp->gr_ptr.gt_addrinfo); } else if (grp->gr_type == GT_NET) { if (grp->gr_ptr.gt_net.nt_name) free(grp->gr_ptr.gt_net.nt_name); @@ -2093,7 +2323,6 @@ check_options(dp) /* * Check an absolute directory path for any symbolic links. Return true - * if no symbolic links are found. */ int check_dirpath(dirp) @@ -2134,3 +2363,146 @@ get_num(cp) } return (res); } + +static int +netpartcmp(struct sockaddr *s1, struct sockaddr *s2, int bitlen) +{ + void *src, *dst; + + if (s1->sa_family != s2->sa_family) + return 1; + + switch (s1->sa_family) { + case AF_INET: + src = &((struct sockaddr_in *)s1)->sin_addr; + dst = &((struct sockaddr_in *)s2)->sin_addr; + if (bitlen > sizeof(((struct sockaddr_in *)s1)->sin_addr) * 8) + return 1; + break; + case AF_INET6: + src = &((struct sockaddr_in6 *)s1)->sin6_addr; + dst = &((struct sockaddr_in6 *)s2)->sin6_addr; + if (((struct sockaddr_in6 *)s1)->sin6_scope_id != + ((struct sockaddr_in6 *)s2)->sin6_scope_id) + return 1; + if (bitlen > sizeof(((struct sockaddr_in6 *)s1)->sin6_addr) * 8) + return 1; + break; + default: + return 1; + } + + return bitcmp(src, dst, bitlen); +} + +static int +allones(struct sockaddr_storage *ssp, int bitlen) +{ + u_int8_t *p; + int bytelen, bitsleft, i; + int zerolen; + + switch (ssp->ss_family) { + case AF_INET: + p = (u_int8_t *)&((struct sockaddr_in *)ssp)->sin_addr; + zerolen = sizeof (((struct sockaddr_in *)ssp)->sin_addr); + break; + case AF_INET6: + p = (u_int8_t *)&((struct sockaddr_in6 *)ssp)->sin6_addr; + zerolen = sizeof (((struct sockaddr_in6 *)ssp)->sin6_addr); + break; + default: + return -1; + } + + memset(p, 0, zerolen); + + bytelen = bitlen / 8; + bitsleft = bitlen % 8; + + if (bytelen > zerolen) + return -1; + + for (i = 0; i < bytelen; i++) + *p++ = 0xff; + + for (i = 0; i < bitsleft; i++) + *p |= 1 << (7 - i); + + return 0; +} + +static int +countones(struct sockaddr *sa) +{ + void *mask; + int i, bits = 0, bytelen; + u_int8_t *p; + + switch (sa->sa_family) { + case AF_INET: + mask = (u_int8_t *)&((struct sockaddr_in *)sa)->sin_addr; + bytelen = 4; + break; + case AF_INET6: + mask = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_addr; + bytelen = 16; + break; + default: + return 0; + } + + p = mask; + + for (i = 0; i < bytelen; i++, p++) { + if (*p != 0xff) { + for (bits = 0; bits < 8; bits++) { + if (!(*p & (1 << (7 - bits)))) + break; + } + break; + } + } + + return (i * 8 + bits); +} + +static int +sacmp(struct sockaddr *sa1, struct sockaddr *sa2) +{ + void *p1, *p2; + int len; + + if (sa1->sa_family != sa2->sa_family) + return 1; + + switch (sa1->sa_family) { + case AF_INET: + p1 = &((struct sockaddr_in *)sa1)->sin_addr; + p2 = &((struct sockaddr_in *)sa2)->sin_addr; + len = 4; + break; + case AF_INET6: + p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr; + p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr; + len = 16; + if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != + ((struct sockaddr_in6 *)sa2)->sin6_scope_id) + return 1; + break; + default: + return 1; + } + + return memcmp(p1, p2, len); +} + +void terminate(sig) +int sig; +{ + close(mountdlockfd); + unlink(MOUNTDLOCK); + pmap_unset(RPCPROG_MNT, 1); + pmap_unset(RPCPROG_MNT, 3); + exit (0); +} diff --git a/sbin/nfsd/nfsd.8 b/sbin/nfsd/nfsd.8 index fb44149bb4d8..0031ea1b926e 100644 --- a/sbin/nfsd/nfsd.8 +++ b/sbin/nfsd/nfsd.8 @@ -42,7 +42,7 @@ server .Sh SYNOPSIS .Nm -.Op Fl arut +.Op Fl ardut .Op Fl n Ar num_servers .Op Fl h Ar bindip .Sh DESCRIPTION @@ -64,13 +64,19 @@ The following options are available: Register the .Tn NFS service with -.Xr portmap 8 +.Xr rpcbind 8 without creating any servers. This option can be used along with the .Fl u or .Fl t -options to re-register NFS if the portmap server is restarted. +options to re-register NFS if the rpcbind server is restarted. +.It Fl d +Unregister the +.Tn NFS +service with +.Xr rpcbind 8 +without creating any servers. .It Fl n Specifies how many servers to create. .It Fl h Ar bindip @@ -147,6 +153,16 @@ that the NFS sockets can only be accessed by the inside interface. would then be used to block nfs-related packets that come in on the outside interface. .Pp +.Nm +has to be terminated with SIGUSR1 and cannot be killed with SIGTERM oder SIGQUIT. +.Nm +needs to ignore these signals in order to stay alive as long +as possible during a shutdown, otherwise loopback mounts will +not be able to unmount. If you have to kill +.Nm +just do a +.Dq Li "kill -USR1 <PID of master nfsd>" +.Pp The .Nm utility exits 0 on success, and >0 if an error occurs. @@ -156,7 +172,7 @@ utility exits 0 on success, and >0 if an error occurs. .Xr kldload 8 , .Xr mountd 8 , .Xr nfsiod 8 , -.Xr portmap 8 , +.Xr rpcbind 8 , .Xr ipfw 8 .Sh HISTORY The diff --git a/sbin/nfsd/nfsd.c b/sbin/nfsd/nfsd.c index 2d923a42652b..959f0fb04d18 100644 --- a/sbin/nfsd/nfsd.c +++ b/sbin/nfsd/nfsd.c @@ -58,9 +58,6 @@ static const char rcsid[] = #include <netdb.h> #include <arpa/inet.h> -#ifdef ISO -#include <netiso/iso.h> -#endif #include <nfs/rpcv2.h> #include <nfs/nfsproto.h> #include <nfs/nfs.h> @@ -76,6 +73,7 @@ static const char rcsid[] = #include <stdlib.h> #include <strings.h> #include <unistd.h> +#include <netdb.h> /* Global defs */ #ifdef DEBUG @@ -103,15 +101,23 @@ struct timeval ktv; NFSKERBKEYSCHED_T kerb_keysched; #endif -void nonfs __P((int)); -void reapchild __P((int)); -void setbindhost __P((struct sockaddr_in *ia, const char *bindhost)); +#define MAXNFSDCNT 20 +#define DEFNFSDCNT 4 +pid_t children[MAXNFSDCNT]; /* PIDs of children */ +int nfsdcnt; /* number of children */ + +void cleanup(int); +void killchildren(void); +void nonfs (int); +void reapchild (int); +int setbindhost (struct addrinfo **ia, const char *bindhost, struct addrinfo hints); #ifdef OLD_SETPROCTITLE #ifdef __FreeBSD__ -void setproctitle __P((char *)); +void setproctitle (char *); #endif #endif -void usage __P((void)); +void unregistration (void); +void usage (void); /* * Nfs server daemon mostly just a user context for nfssvc() @@ -119,7 +125,7 @@ void usage __P((void)); * 1 - do file descriptor and signal cleanup * 2 - fork the nfsd(s) * 3 - create server socket(s) - * 4 - register socket with portmap + * 4 - register socket with rpcbind * * For connectionless protocols, just pass the socket into the kernel via. * nfssvc(). @@ -127,7 +133,8 @@ void usage __P((void)); * socket from accept, pass the msgsock into the kernel via. nfssvc(). * The arguments are: * -c - support iso cltp clients - * -r - reregister with portmapper + * -r - reregister with rpcbind + * -d - unregister with rpcbind * -t - support tcp nfs clients * -u - support udp nfs clients * followed by "n" which is the number of nfsds' to fork off @@ -138,20 +145,20 @@ main(argc, argv, envp) char *argv[], *envp[]; { struct nfsd_args nfsdargs; - struct sockaddr_in inetaddr, inetpeer; -#ifdef ISO - struct sockaddr_iso isoaddr, isopeer; - char *cp; -#endif + struct addrinfo *ai_udp, *ai_tcp, *ai_udp6, *ai_tcp6, hints; + struct netconfig *nconf_udp, *nconf_tcp, *nconf_udp6, *nconf_tcp6; + struct netbuf nb_udp, nb_tcp, nb_udp6, nb_tcp6; + struct sockaddr_in inetpeer; + struct sockaddr_in6 inet6peer; fd_set ready, sockbits; + fd_set v4bits, v6bits; int ch, cltpflag, connect_type_cnt, i, len, maxsock, msgsock; - int nfsdcnt, nfssvc_flag, on, reregister, sock, tcpflag, tcpsock; - int tp4cnt, tp4flag, tpipcnt, tpipflag, udpflag; - int bindhostc = 0, bindanyflag; + int nfssvc_flag, on = 1, unregister, reregister, sock; + int tcp6sock, ip6flag, tcpflag, tcpsock; + int udpflag, ecode, s; + int bindhostc = 0, bindanyflag, rpcbreg, rpcbregcnt; char **bindhost = NULL; -#ifdef notyet - int tp4sock, tpipsock; -#endif + pid_t pid; #ifdef NFSKERB struct group *grp; struct passwd *pwd; @@ -184,18 +191,11 @@ main(argc, argv, envp) LastArg = envp[-1] + strlen(envp[-1]); #endif -#define MAXNFSDCNT 20 -#define DEFNFSDCNT 4 nfsdcnt = DEFNFSDCNT; - cltpflag = reregister = tcpflag = tp4cnt = tp4flag = tpipcnt = 0; - bindanyflag = tpipflag = udpflag = 0; -#ifdef ISO -#define GETOPT "ach:n:rtu" -#define USAGE "[-acrtu] [-n num_servers] [-h bindip]" -#else -#define GETOPT "ah:n:rtu" -#define USAGE "[-artu] [-n num_servers] [-h bindip]" -#endif + cltpflag = unregister = reregister = tcpflag = 0; + bindanyflag = udpflag = ip6flag = 0; +#define GETOPT "ah:n:rdtu" +#define USAGE "[-ardtu] [-n num_servers] [-h bindip]" while ((ch = getopt(argc, argv, GETOPT)) != -1) switch (ch) { case 'a': @@ -221,25 +221,15 @@ main(argc, argv, envp) case 'r': reregister = 1; break; + case 'd': + unregister = 1; + break; case 't': tcpflag = 1; break; case 'u': udpflag = 1; break; -#ifdef ISO - case 'c': - cltpflag = 1; - break; -#ifdef notyet - case 'i': - tp4cnt = 1; - break; - case 'p': - tpipcnt = 1; - break; -#endif /* notyet */ -#endif /* ISO */ default: case '?': usage(); @@ -263,6 +253,13 @@ main(argc, argv, envp) nfsdcnt = DEFNFSDCNT; } } + ip6flag = 1; + s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (s < 0 && (errno == EPROTONOSUPPORT || + errno == EPFNOSUPPORT || errno == EAFNOSUPPORT)) + ip6flag = 0; + else + close(s); if (bindhostc == 0 || bindanyflag) { bindhostc++; @@ -278,33 +275,126 @@ main(argc, argv, envp) daemon(0, 0); (void)signal(SIGHUP, SIG_IGN); (void)signal(SIGINT, SIG_IGN); - (void)signal(SIGQUIT, SIG_IGN); (void)signal(SIGSYS, nonfs); + (void)signal(SIGUSR1, cleanup); + /* + * nfsd sits in the kernel most of the time. It needs + * to ignore SIGTERM/SIGQUIT in order to stay alive as long + * as possible during a shutdown, otherwise loopback + * mounts will not be able to unmount. + */ (void)signal(SIGTERM, SIG_IGN); + (void)signal(SIGQUIT, SIG_IGN); } (void)signal(SIGCHLD, reapchild); - + if (unregister) { + unregistration(); + exit (0); + } if (reregister) { - if (udpflag && - (!pmap_set(RPCPROG_NFS, 2, IPPROTO_UDP, NFS_PORT) || - !pmap_set(RPCPROG_NFS, 3, IPPROTO_UDP, NFS_PORT))) - err(1, "can't register with portmap for UDP"); - if (tcpflag && - (!pmap_set(RPCPROG_NFS, 2, IPPROTO_TCP, NFS_PORT) || - !pmap_set(RPCPROG_NFS, 3, IPPROTO_TCP, NFS_PORT))) - err(1, "can't register with portmap for TCP"); - exit(0); + if (udpflag) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo udp: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_udp = getnetconfigent("udp"); + if (nconf_udp == NULL) + err(1, "getnetconfigent udp failed"); + nb_udp.buf = ai_udp->ai_addr; + nb_udp.len = nb_udp.maxlen = ai_udp->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp, &nb_udp)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_udp, &nb_udp))) + err(1, "rpcb_set udp failed"); + freeaddrinfo(ai_udp); + } + if (udpflag && ip6flag) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp6); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo udp6: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_udp6 = getnetconfigent("udp6"); + if (nconf_udp6 == NULL) + err(1, "getnetconfigent udp6 failed"); + nb_udp6.buf = ai_udp6->ai_addr; + nb_udp6.len = nb_udp6.maxlen = ai_udp6->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp6, &nb_udp6)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_udp6, &nb_udp6))) + err(1, "rpcb_set udp6 failed"); + freeaddrinfo(ai_udp6); + } + if (tcpflag) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo tcp: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_tcp = getnetconfigent("tcp"); + if (nconf_tcp == NULL) + err(1, "getnetconfigent tcp failed"); + nb_tcp.buf = ai_tcp->ai_addr; + nb_tcp.len = nb_tcp.maxlen = ai_tcp->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp, &nb_tcp)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_tcp, &nb_tcp))) + err(1, "rpcb_set tcp failed"); + freeaddrinfo(ai_tcp); + } + if (tcpflag && ip6flag) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp6); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo tcp6: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_tcp6 = getnetconfigent("tcp6"); + if (nconf_tcp6 == NULL) + err(1, "getnetconfigent tcp6 failed"); + nb_tcp6.buf = ai_tcp6->ai_addr; + nb_tcp6.len = nb_tcp6.maxlen = ai_tcp6->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp6, &nb_tcp6)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_tcp6, &nb_tcp6))) + err(1, "rpcb_set tcp6 failed"); + freeaddrinfo(ai_tcp6); + } + exit (0); } + openlog("nfsd:", LOG_PID, LOG_DAEMON); for (i = 0; i < nfsdcnt; i++) { - switch (fork()) { + switch ((pid = fork())) { case -1: syslog(LOG_ERR, "fork: %m"); + killchildren(); exit (1); case 0: break; default: + children[i] = pid; continue; } @@ -399,182 +489,295 @@ main(argc, argv, envp) exit(0); } - /* If we are serving udp, set up the socket. */ - for (i = 0; udpflag && i < bindhostc; i++) { - if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - syslog(LOG_ERR, "can't create udp socket"); - exit(1); - } - setbindhost(&inetaddr, bindhost[i]); - if (bind(sock, - (struct sockaddr *)&inetaddr, sizeof(inetaddr)) < 0) { - syslog(LOG_ERR, "can't bind udp addr %s: %m", bindhost[i]); - exit(1); - } - if (!pmap_set(RPCPROG_NFS, 2, IPPROTO_UDP, NFS_PORT) || - !pmap_set(RPCPROG_NFS, 3, IPPROTO_UDP, NFS_PORT)) { - syslog(LOG_ERR, "can't register with udp portmap"); - exit(1); + if (atexit(killchildren) == -1) { + syslog(LOG_ERR, "atexit: %s", strerror(errno)); + exit(1); + } + FD_ZERO(&v4bits); + FD_ZERO(&v6bits); + + rpcbregcnt = 0; + /* Set up the socket for udp and rpcb register it. */ + if (udpflag) { + rpcbreg = 0; + for (i = 0; i < bindhostc; i++) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + if (setbindhost(&ai_udp, bindhost[i], hints) == 0) { + rpcbreg = 1; + rpcbregcnt++; + if ((sock = socket(ai_udp->ai_family, + ai_udp->ai_socktype, + ai_udp->ai_protocol)) < 0) { + syslog(LOG_ERR, + "can't create udp socket"); + exit(1); + } + if (bind(sock, ai_udp->ai_addr, + ai_udp->ai_addrlen) < 0) { + syslog(LOG_ERR, + "can't bind udp addr %s: %m", + bindhost[i]); + exit(1); + } + freeaddrinfo(ai_udp); + nfsdargs.sock = sock; + nfsdargs.name = NULL; + nfsdargs.namelen = 0; + if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) { + syslog(LOG_ERR, "can't Add UDP socket"); + exit(1); + } + (void)close(sock); + } } - nfsdargs.sock = sock; - nfsdargs.name = NULL; - nfsdargs.namelen = 0; - if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) { - syslog(LOG_ERR, "can't Add UDP socket"); - exit(1); + if (rpcbreg == 1) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo udp: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_udp = getnetconfigent("udp"); + if (nconf_udp == NULL) + err(1, "getnetconfigent udp failed"); + nb_udp.buf = ai_udp->ai_addr; + nb_udp.len = nb_udp.maxlen = ai_udp->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp, &nb_udp)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_udp, &nb_udp))) + err(1, "rpcb_set udp failed"); + freeaddrinfo(ai_udp); } - (void)close(sock); } -#ifdef ISO - /* If we are serving cltp, set up the socket. */ - if (cltpflag) { - if ((sock = socket(AF_ISO, SOCK_DGRAM, 0)) < 0) { - syslog(LOG_ERR, "can't create cltp socket"); - exit(1); - } - memset(&isoaddr, 0, sizeof(isoaddr)); - isoaddr.siso_family = AF_ISO; - isoaddr.siso_tlen = 2; - cp = TSEL(&isoaddr); - *cp++ = (NFS_PORT >> 8); - *cp = (NFS_PORT & 0xff); - isoaddr.siso_len = sizeof(isoaddr); - if (bind(sock, - (struct sockaddr *)&isoaddr, sizeof(isoaddr)) < 0) { - syslog(LOG_ERR, "can't bind cltp addr"); - exit(1); - } -#ifdef notyet - /* - * XXX - * Someday this should probably use "rpcbind", the son of - * portmap. - */ - if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) { - syslog(LOG_ERR, "can't register with udp portmap"); - exit(1); + /* Set up the socket for udp6 and rpcb register it. */ + if (udpflag && ip6flag) { + rpcbreg = 0; + for (i = 0; i < bindhostc; i++) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + if (setbindhost(&ai_udp6, bindhost[i], hints) == 0) { + rpcbreg = 1; + rpcbregcnt++; + if ((sock = socket(ai_udp6->ai_family, + ai_udp6->ai_socktype, + ai_udp6->ai_protocol)) < 0) { + syslog(LOG_ERR, + "can't create udp6 socket"); + exit(1); + } + if (setsockopt(sock, IPPROTO_IPV6, + IPV6_BINDV6ONLY, + &on, sizeof on) < 0) { + syslog(LOG_ERR, + "can't set v6-only binding for " + "udp6 socket: %m"); + exit(1); + } + if (bind(sock, ai_udp6->ai_addr, + ai_udp6->ai_addrlen) < 0) { + syslog(LOG_ERR, + "can't bind udp6 addr %s: %m", + bindhost[i]); + exit(1); + } + freeaddrinfo(ai_udp6); + nfsdargs.sock = sock; + nfsdargs.name = NULL; + nfsdargs.namelen = 0; + if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) { + syslog(LOG_ERR, + "can't add UDP6 socket"); + exit(1); + } + (void)close(sock); + } } -#endif /* notyet */ - nfsdargs.sock = sock; - nfsdargs.name = NULL; - nfsdargs.namelen = 0; - if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) { - syslog(LOG_ERR, "can't add UDP socket"); - exit(1); + if (rpcbreg == 1) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp6); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo udp6: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_udp6 = getnetconfigent("udp6"); + if (nconf_udp6 == NULL) + err(1, "getnetconfigent udp6 failed"); + nb_udp6.buf = ai_udp6->ai_addr; + nb_udp6.len = nb_udp6.maxlen = ai_udp6->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp6, &nb_udp6)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_udp6, &nb_udp6))) + err(1, "rpcb_set udp6 failed"); + freeaddrinfo(ai_udp6); } - close(sock); } -#endif /* ISO */ - /* Now set up the master server socket waiting for tcp connections. */ - on = 1; - FD_ZERO(&sockbits); - connect_type_cnt = 0; - for (i = 0; tcpflag && i < bindhostc; i++) { - if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - syslog(LOG_ERR, "can't create tcp socket"); - exit(1); - } - if (setsockopt(tcpsock, - SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); - setbindhost(&inetaddr, bindhost[i]); - if (bind(tcpsock, - (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) { - syslog(LOG_ERR, "can't bind tcp addr %s: %m", bindhost[i]); - exit(1); - } - if (listen(tcpsock, 5) < 0) { - syslog(LOG_ERR, "listen failed"); - exit(1); + /* Set up the socket for tcp and rpcb register it. */ + if (tcpflag) { + rpcbreg = 0; + for (i = 0; i < bindhostc; i++) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + if (setbindhost(&ai_tcp, bindhost[i], hints) == 0) { + rpcbreg = 1; + rpcbregcnt++; + if ((tcpsock = socket(AF_INET, SOCK_STREAM, + 0)) < 0) { + syslog(LOG_ERR, + "can't create tpc socket"); + exit(1); + } + if (setsockopt(tcpsock, SOL_SOCKET, + SO_REUSEADDR, + (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, + "setsockopt SO_REUSEADDR: %m"); + if (bind(tcpsock, ai_tcp->ai_addr, + ai_tcp->ai_addrlen) < 0) { + syslog(LOG_ERR, + "can't bind tcp addr %s: %m", + bindhost[i]); + exit(1); + } + if (listen(tcpsock, 5) < 0) { + syslog(LOG_ERR, "listen failed"); + exit(1); + } + freeaddrinfo(ai_tcp); + FD_SET(tcpsock, &sockbits); + FD_SET(tcpsock, &v4bits); + maxsock = tcpsock; + connect_type_cnt++; + } } - if (!pmap_set(RPCPROG_NFS, 2, IPPROTO_TCP, NFS_PORT) || - !pmap_set(RPCPROG_NFS, 3, IPPROTO_TCP, NFS_PORT)) { - syslog(LOG_ERR, "can't register tcp with portmap"); - exit(1); + if (rpcbreg == 1) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + ecode = getaddrinfo(NULL, "nfs", &hints, + &ai_tcp); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo tcp: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_tcp = getnetconfigent("tcp"); + if (nconf_tcp == NULL) + err(1, "getnetconfigent tcp failed"); + nb_tcp.buf = ai_tcp->ai_addr; + nb_tcp.len = nb_tcp.maxlen = ai_tcp->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp, + &nb_tcp)) || (!rpcb_set(RPCPROG_NFS, 3, + nconf_tcp, &nb_tcp))) + err(1, "rpcb_set tcp failed"); + freeaddrinfo(ai_tcp); } - FD_SET(tcpsock, &sockbits); - maxsock = tcpsock; - connect_type_cnt++; } -#ifdef notyet - /* Now set up the master server socket waiting for tp4 connections. */ - if (tp4flag) { - if ((tp4sock = socket(AF_ISO, SOCK_SEQPACKET, 0)) < 0) { - syslog(LOG_ERR, "can't create tp4 socket"); - exit(1); - } - if (setsockopt(tp4sock, - SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); - memset(&isoaddr, 0, sizeof(isoaddr)); - isoaddr.siso_family = AF_ISO; - isoaddr.siso_tlen = 2; - cp = TSEL(&isoaddr); - *cp++ = (NFS_PORT >> 8); - *cp = (NFS_PORT & 0xff); - isoaddr.siso_len = sizeof(isoaddr); - if (bind(tp4sock, - (struct sockaddr *)&isoaddr, sizeof (isoaddr)) < 0) { - syslog(LOG_ERR, "can't bind tp4 addr"); - exit(1); - } - if (listen(tp4sock, 5) < 0) { - syslog(LOG_ERR, "listen failed"); - exit(1); + /* Set up the socket for tcp6 and rpcb register it. */ + if (tcpflag && ip6flag) { + rpcbreg = 0; + for (i = 0; i < bindhostc; i++) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + if (setbindhost(&ai_tcp6, bindhost[i], hints) == 0) { + rpcbreg = 1; + rpcbregcnt++; + if ((tcp6sock = socket(ai_tcp6->ai_family, + ai_tcp6->ai_socktype, + ai_tcp6->ai_protocol)) < 0) { + syslog(LOG_ERR, + "can't create tcp6 socket"); + exit(1); + } + if (setsockopt(tcp6sock, SOL_SOCKET, + SO_REUSEADDR, + (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, + "setsockopt SO_REUSEADDR: %m"); + if (setsockopt(tcp6sock, IPPROTO_IPV6, + IPV6_BINDV6ONLY, &on, sizeof on) < 0) { + syslog(LOG_ERR, + "can't set v6-only binding for tcp6 " + "socket: %m"); + exit(1); + } + if (bind(tcp6sock, ai_tcp6->ai_addr, + ai_tcp6->ai_addrlen) < 0) { + syslog(LOG_ERR, + "can't bind tcp6 addr %s: %m", + bindhost[i]); + exit(1); + } + if (listen(tcp6sock, 5) < 0) { + syslog(LOG_ERR, "listen failed"); + exit(1); + } + freeaddrinfo(ai_tcp6); + FD_SET(tcp6sock, &sockbits); + FD_SET(tcp6sock, &v6bits); + if (maxsock < tcp6sock) + maxsock = tcp6sock; + connect_type_cnt++; + } } - /* - * XXX - * Someday this should probably use "rpcbind", the son of - * portmap. - */ - if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) { - syslog(LOG_ERR, "can't register tcp with portmap"); - exit(1); + if (rpcbreg == 1) { + memset(&hints, 0, sizeof hints); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp6); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo tcp6: %s", + gai_strerror(ecode)); + exit(1); + } + nconf_tcp6 = getnetconfigent("tcp6"); + if (nconf_tcp6 == NULL) + err(1, "getnetconfigent tcp6 failed"); + nb_tcp6.buf = ai_tcp6->ai_addr; + nb_tcp6.len = nb_tcp6.maxlen = ai_tcp6->ai_addrlen; + if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp6, &nb_tcp6)) || + (!rpcb_set(RPCPROG_NFS, 3, nconf_tcp6, &nb_tcp6))) + err(1, "rpcb_set tcp6 failed"); + freeaddrinfo(ai_tcp6); } - FD_SET(tp4sock, &sockbits); - maxsock = tp4sock; - connect_type_cnt++; } - /* Now set up the master server socket waiting for tpip connections. */ - for (i = 0; tpipflag && i < bindhostc; i++) { - if ((tpipsock = socket(AF_INET, SOCK_SEQPACKET, 0)) < 0) { - syslog(LOG_ERR, "can't create tpip socket"); - exit(1); - } - if (setsockopt(tpipsock, - SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); - setbindhost(&inetaddr, bindhost[i]); - if (bind(tpipsock, - (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) { - syslog(LOG_ERR, "can't bind tcp addr %s: %m", bindhost[i]); - exit(1); - } - if (listen(tpipsock, 5) < 0) { - syslog(LOG_ERR, "listen failed"); - exit(1); - } - /* - * XXX - * Someday this should probably use "rpcbind", the son of - * portmap. - */ - if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) { - syslog(LOG_ERR, "can't register tcp with portmap"); - exit(1); - } - FD_SET(tpipsock, &sockbits); - maxsock = tpipsock; - connect_type_cnt++; + if (rpcbregcnt == 0) { + syslog(LOG_ERR, "rpcb_set() failed, nothing to do: %m"); + exit(1); } -#endif /* notyet */ - if (connect_type_cnt == 0) - exit(0); + if ((tcpflag) && (connect_type_cnt == 0)) { + syslog(LOG_ERR, "tcp connects == 0, nothing to do: %m"); + exit(1); + } setproctitle("master"); @@ -591,82 +794,94 @@ main(argc, argv, envp) exit(1); } } - if (tcpflag && FD_ISSET(tcpsock, &ready)) { - len = sizeof(inetpeer); - if ((msgsock = accept(tcpsock, - (struct sockaddr *)&inetpeer, &len)) < 0) { - syslog(LOG_ERR, "accept failed: %m"); - exit(1); - } - memset(inetpeer.sin_zero, 0, sizeof(inetpeer.sin_zero)); - if (setsockopt(msgsock, SOL_SOCKET, - SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, - "setsockopt SO_KEEPALIVE: %m"); - nfsdargs.sock = msgsock; - nfsdargs.name = (caddr_t)&inetpeer; - nfsdargs.namelen = sizeof(inetpeer); - nfssvc(NFSSVC_ADDSOCK, &nfsdargs); - (void)close(msgsock); - } -#ifdef notyet - if (tp4flag && FD_ISSET(tp4sock, &ready)) { - len = sizeof(isopeer); - if ((msgsock = accept(tp4sock, - (struct sockaddr *)&isopeer, &len)) < 0) { - syslog(LOG_ERR, "accept failed: %m"); - exit(1); - } - if (setsockopt(msgsock, SOL_SOCKET, - SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, - "setsockopt SO_KEEPALIVE: %m"); - nfsdargs.sock = msgsock; - nfsdargs.name = (caddr_t)&isopeer; - nfsdargs.namelen = len; - nfssvc(NFSSVC_ADDSOCK, &nfsdargs); - (void)close(msgsock); - } - if (tpipflag && FD_ISSET(tpipsock, &ready)) { - len = sizeof(inetpeer); - if ((msgsock = accept(tpipsock, - (struct sockaddr *)&inetpeer, &len)) < 0) { - syslog(LOG_ERR, "accept failed: %m"); - exit(1); + for (tcpsock = 0; tcpsock <= maxsock; tcpsock++) { + if (FD_ISSET(tcpsock, &ready)) { + if (FD_ISSET(tcpsock, &v4bits)) { + len = sizeof(inetpeer); + if ((msgsock = accept(tcpsock, + (struct sockaddr *)&inetpeer, &len)) < 0) { + syslog(LOG_ERR, "accept failed: %m"); + exit(1); + } + memset(inetpeer.sin_zero, 0, + sizeof(inetpeer.sin_zero)); + if (setsockopt(msgsock, SOL_SOCKET, + SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, + "setsockopt SO_KEEPALIVE: %m"); + nfsdargs.sock = msgsock; + nfsdargs.name = (caddr_t)&inetpeer; + nfsdargs.namelen = sizeof(inetpeer); + nfssvc(NFSSVC_ADDSOCK, &nfsdargs); + (void)close(msgsock); + } else if (FD_ISSET(tcpsock, &v6bits)) { + len = sizeof(inet6peer); + if ((msgsock = accept(tcpsock, + (struct sockaddr *)&inet6peer, + &len)) < 0) { + syslog(LOG_ERR, + "accept failed: %m"); + exit(1); + } + if (setsockopt(msgsock, SOL_SOCKET, + SO_KEEPALIVE, (char *)&on, + sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt " + "SO_KEEPALIVE: %m"); + nfsdargs.sock = msgsock; + nfsdargs.name = (caddr_t)&inet6peer; + nfsdargs.namelen = sizeof(inet6peer); + nfssvc(NFSSVC_ADDSOCK, &nfsdargs); + (void)close(msgsock); + } } - if (setsockopt(msgsock, SOL_SOCKET, - SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) - syslog(LOG_ERR, "setsockopt SO_KEEPALIVE: %m"); - nfsdargs.sock = msgsock; - nfsdargs.name = (caddr_t)&inetpeer; - nfsdargs.namelen = len; - nfssvc(NFSSVC_ADDSOCK, &nfsdargs); - (void)close(msgsock); } -#endif /* notyet */ } } -void -setbindhost(struct sockaddr_in *ia, const char *bindhost) +int +setbindhost(struct addrinfo **ai, const char *bindhost, struct addrinfo hints) { - ia->sin_family = AF_INET; - ia->sin_port = htons(NFS_PORT); - ia->sin_len = sizeof(*ia); - if (bindhost == NULL || strcmp(bindhost,"*") == 0) { - ia->sin_addr.s_addr = INADDR_ANY; - } else { - if (inet_aton(bindhost, &ia->sin_addr) == 0) { - struct hostent *he; + int ecode; + u_int32_t host_addr[4]; /* IPv4 or IPv6 */ + const char *hostptr; - he = gethostbyname2(bindhost, ia->sin_family); - if (he == NULL) { - syslog(LOG_ERR, "gethostbyname of %s failed", bindhost); - exit(1); + if (bindhost == NULL || strcmp("*", bindhost) == 0) + hostptr = NULL; + else + hostptr = bindhost; + + if (hostptr != NULL) { + switch (hints.ai_family) { + case AF_INET: + if (inet_pton(AF_INET, hostptr, host_addr) == 1) { + hints.ai_flags = AI_NUMERICHOST; + } else { + if (inet_pton(AF_INET6, hostptr, + host_addr) == 1) + return (1); + } + break; + case AF_INET6: + if (inet_pton(AF_INET6, hostptr, host_addr) == 1) { + hints.ai_flags = AI_NUMERICHOST; + } else { + if (inet_pton(AF_INET, hostptr, + host_addr) == 1) + return (1); } - bcopy(he->h_addr, &ia->sin_addr, he->h_length); + break; + default: } } + + ecode = getaddrinfo(hostptr, "nfs", &hints, ai); + if (ecode != 0) { + syslog(LOG_ERR, "getaddrinfo %s: %s", bindhost, + gai_strerror(ecode)); + return (1); + } + return (0); } void @@ -691,6 +906,48 @@ reapchild(signo) while (wait3(NULL, WNOHANG, NULL) > 0); } +void +unregistration() +{ + if ((!rpcb_unset(RPCPROG_NFS, 2, NULL)) || + (!rpcb_unset(RPCPROG_NFS, 3, NULL))) + syslog(LOG_ERR, "rpcb_unset failed"); +} + +void +killchildren() +{ + int i; + sigset_t sigs; + + sigemptyset(&sigs); + /* + * Block SIGCHLD to avoid killing a reaped process (although it is + * unlikely, the pid might have been reused). + */ + sigaddset(&sigs, SIGCHLD); + if (sigprocmask(SIG_BLOCK, &sigs, NULL) == -1) { + syslog(LOG_ERR, "sigprocmask: %s", + strerror(errno)); + return; + } + for (i = 0; i < nfsdcnt; i++) { + if (children[i] > 0) + kill(children[i], SIGKILL); + } + if (sigprocmask(SIG_UNBLOCK, &sigs, NULL) == -1) { + syslog(LOG_ERR, "sigprocmask: %s", strerror(errno)); + } + unregistration(); +} + +void +cleanup(signo) +{ + killchildren(); + exit (0); +} + #ifdef OLD_SETPROCTITLE #ifdef __FreeBSD__ void diff --git a/sbin/umount/umount.c b/sbin/umount/umount.c index e9ef990cc2d2..2a3a3373e2e5 100644 --- a/sbin/umount/umount.c +++ b/sbin/umount/umount.c @@ -47,6 +47,7 @@ static const char rcsid[] = #include <sys/param.h> #include <sys/mount.h> +#include <sys/socket.h> #include <netdb.h> #include <rpc/rpc.h> @@ -68,6 +69,7 @@ typedef enum { MNTON, MNTFROM, NOTHING } mntwhat; typedef enum { MARK, UNMARK, NAME, COUNT, FREE } dowhat; struct mtablist *mtabhead; +struct addrinfo *nfshost_ai = NULL; int fflag, vflag; char *nfshost; @@ -78,19 +80,22 @@ char *getmntname (const char *, const char *, char *getrealname(char *, char *resolved_path); char **makevfslist (const char *); size_t mntinfo (struct statfs **); -int namematch (struct hostent *); +int namematch (struct addrinfo *); +int sacmp (struct sockaddr *, struct sockaddr *); int umountall (char **); -int umountfs (char *, char **); +int checkname (char *, char **); +int umountfs (char *, char *, char *); void usage (void); int xdr_dir (XDR *, char *); int main(int argc, char *argv[]) { - int all, errs, ch, mntsize; + int all, errs, ch, mntsize, error; char **typelist = NULL, *mntonname, *mntfromname; char *type, *mntfromnamerev, *mntonnamerev; struct statfs *mntbuf; + struct addrinfo hints; /* Start disks transferring immediately. */ sync(); @@ -133,6 +138,15 @@ main(int argc, char *argv[]) if ((nfshost != NULL) && (typelist == NULL)) typelist = makevfslist("nfs"); + if (nfshost != NULL) { + memset(&hints, 0, sizeof hints); + error = getaddrinfo(nfshost, NULL, &hints, &nfshost_ai); + if (error) { + fprintf(stderr, "ndp: %s: %s\n", nfshost, + gai_strerror(error)); + } + } + switch (all) { case 2: if ((mntsize = mntinfo(&mntbuf)) <= 0) @@ -165,7 +179,7 @@ main(int argc, char *argv[]) "is mounted there, umount it first", mntonname, mntfromnamerev); - if (umountfs(mntbuf[mntsize].f_mntonname, + if (checkname(mntbuf[mntsize].f_mntonname, typelist) != 0) errs = 1; } @@ -178,7 +192,7 @@ main(int argc, char *argv[]) break; case 0: for (errs = 0; *argv != NULL; ++argv) - if (umountfs(*argv, typelist) != 0) + if (checkname(*argv, typelist) != 0) errs = 1; break; } @@ -228,33 +242,29 @@ umountall(char **typelist) err(1, "malloc failed"); (void)strcpy(cp, fs->fs_file); rval = umountall(typelist); - rval = umountfs(cp, typelist) || rval; + rval = checkname(cp, typelist) || rval; free(cp); return (rval); } while ((fs = getfsent()) != NULL); return (0); } +/* + * Do magic checks on mountpoint and device or hand over + * it to unmount(2) if everything fails. + */ int -umountfs(char *name, char **typelist) +checkname(char *name, char **typelist) { - enum clnt_stat clnt_stat; - struct hostent *hp; - struct mtablist *mtab; - struct sockaddr_in saddr; - struct timeval pertry, try; - CLIENT *clp; size_t len; - int so, speclen, do_rpc; + int speclen; char *mntonname, *mntfromname; char *mntfromnamerev; - char *nfsdirname, *orignfsdirname; char *resolved, realname[MAXPATHLEN]; - char *type, *delimp, *hostp, *origname; + char *type, *hostp, *delimp, *origname; len = 0; - mtab = NULL; - mntfromname = mntonname = delimp = hostp = orignfsdirname = NULL; + mntfromname = mntonname = delimp = hostp = NULL; /* * 1. Check if the name exists in the mounttable. @@ -323,14 +333,29 @@ umountfs(char *name, char **typelist) resolved = realname; } /* - * All tests failed, return to main() + * 5. All tests failed, just hand over the + * mountpoint to the kernel, maybe the statfs + * structure has been truncated or is not + * useful anymore because of a chroot(2). + * Please note that nfs will not be able to + * notify the nfs-server about unmounting. + * These things can change in future when the + * fstat structure get's more reliable, + * but at the moment we cannot thrust it. */ if (mntfromname == NULL && mntonname == NULL) { (void)strcpy(name, origname); - warnx("%s: not currently mounted", - origname); - free(origname); - return (1); + if (umountfs(NULL, origname, + "none") == 0) {; + warnx("%s not found in " + "mount table, " + "unmounted it anyway", + origname); + free(origname); + return (0); + } else + free(origname); + return (1); } } } @@ -341,21 +366,6 @@ umountfs(char *name, char **typelist) if (checkvfsname(type, typelist)) return (1); - hp = NULL; - nfsdirname = NULL; - if (!strcmp(type, "nfs")) { - if ((nfsdirname = strdup(mntfromname)) == NULL) - err(1, "strdup"); - orignfsdirname = nfsdirname; - if ((delimp = strchr(nfsdirname, ':')) != NULL) { - *delimp = '\0'; - hostp = nfsdirname; - if ((hp = gethostbyname(hostp)) == NULL) { - warnx("can't get net id for host"); - } - nfsdirname = delimp + 1; - } - } /* * Check if the reverse entrys of the mounttable are really the * same as the normal ones. @@ -383,6 +393,43 @@ umountfs(char *name, char **typelist) return (1); } free(mntfromnamerev); + umountfs(mntfromname, mntonname, type); +} + +/* + * NFS stuff and unmount(2) call + */ +int +umountfs(char *mntfromname, char *mntonname, char *type) +{ + enum clnt_stat clnt_stat; + struct timeval try; + struct mtablist *mtab; + struct addrinfo *ai, hints; + int do_rpc; + CLIENT *clp; + char *nfsdirname, *orignfsdirname; + char *hostp, *delimp; + + mtab = NULL; + ai = NULL; + nfsdirname = delimp = orignfsdirname = NULL; + memset(&hints, 0, sizeof hints); + + if (!strcmp(type, "nfs")) { + if ((nfsdirname = strdup(mntfromname)) == NULL) + err(1, "strdup"); + orignfsdirname = nfsdirname; + if ((delimp = strrchr(nfsdirname, ':')) != NULL) { + *delimp = '\0'; + hostp = nfsdirname; + getaddrinfo(hostp, NULL, &hints, &ai); + if (ai == NULL) { + warnx("can't get net id for host"); + } + nfsdirname = delimp + 1; + } + } /* * Check if we have to start the rpc-call later. * If there are still identical nfs-names mounted, @@ -395,7 +442,8 @@ umountfs(char *name, char **typelist) do_rpc = 1; else do_rpc = 0; - if (!namematch(hp)) + + if (!namematch(ai)) return (1); if (unmount(mntonname, fflag) != 0 ) { warn("unmount of %s failed", mntonname); @@ -407,21 +455,13 @@ umountfs(char *name, char **typelist) * Report to mountd-server which nfsname * has been unmounted. */ - if (hp != NULL && !(fflag & MNT_FORCE) && do_rpc) { - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = 0; - memmove(&saddr.sin_addr, hp->h_addr, - MIN(hp->h_length, sizeof(saddr.sin_addr))); - pertry.tv_sec = 3; - pertry.tv_usec = 0; - so = RPC_ANYSOCK; - if ((clp = clntudp_create(&saddr, - RPCPROG_MNT, RPCMNT_VER1, pertry, &so)) == NULL) { + if (ai != NULL && !(fflag & MNT_FORCE) && do_rpc) { + clp = clnt_create(hostp, RPCPROG_MNT, RPCMNT_VER1, "udp"); + if (clp == NULL) { clnt_pcreateerror("Cannot MNT PRC"); return (1); } - clp->cl_auth = authunix_create_default(); + clp->cl_auth = authsys_create_default(); try.tv_sec = 20; try.tv_usec = 0; clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, xdr_dir, @@ -554,30 +594,53 @@ getmntname(const char *fromname, const char *onname, } int -namematch(struct hostent *hp) +sacmp(struct sockaddr *sa1, struct sockaddr *sa2) { - char *cp, **np; + void *p1, *p2; + int len; - if ((hp == NULL) || (nfshost == NULL)) + if (sa1->sa_family != sa2->sa_family) return (1); - if (strcasecmp(nfshost, hp->h_name) == 0) - return (1); - - if ((cp = strchr(hp->h_name, '.')) != NULL) { - *cp = '\0'; - if (strcasecmp(nfshost, hp->h_name) == 0) + switch (sa1->sa_family) { + case AF_INET: + p1 = &((struct sockaddr_in *)sa1)->sin_addr; + p2 = &((struct sockaddr_in *)sa2)->sin_addr; + len = 4; + break; + case AF_INET6: + p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr; + p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr; + len = 16; + if (((struct sockaddr_in6 *)sa1)->sin6_scope_id != + ((struct sockaddr_in6 *)sa2)->sin6_scope_id) return (1); + break; + default: + return (1); } - for (np = hp->h_aliases; *np; np++) { - if (strcasecmp(nfshost, *np) == 0) - return (1); - if ((cp = strchr(*np, '.')) != NULL) { - *cp = '\0'; - if (strcasecmp(nfshost, *np) == 0) + + return memcmp(p1, p2, len); +} + +int +namematch(struct addrinfo *ai) +{ + struct addrinfo *aip; + + if (nfshost == NULL || nfshost_ai == NULL) + return (1); + + while (ai != NULL) { + aip = nfshost_ai; + while (aip != NULL) { + if (sacmp(ai->ai_addr, aip->ai_addr) == 0) return (1); + aip = aip->ai_next; } + ai = ai->ai_next; } + return (0); } |
