Home | History | Annotate | Line # | Download | only in mountd
mountd.c revision 1.121
      1 /* 	$NetBSD: mountd.c,v 1.121 2011/08/30 17:06:21 plunky Exp $	 */
      2 
      3 /*
      4  * Copyright (c) 1989, 1993
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Herb Hasler and Rick Macklem at The University of Guelph.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. Neither the name of the University nor the names of its contributors
     19  *    may be used to endorse or promote products derived from this software
     20  *    without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 #include <sys/cdefs.h>
     36 #ifndef lint
     37 __COPYRIGHT("@(#) Copyright (c) 1989, 1993\
     38  The Regents of the University of California.  All rights reserved.");
     39 #endif				/* not lint */
     40 
     41 #ifndef lint
     42 #if 0
     43 static char     sccsid[] = "@(#)mountd.c  8.15 (Berkeley) 5/1/95";
     44 #else
     45 __RCSID("$NetBSD: mountd.c,v 1.121 2011/08/30 17:06:21 plunky Exp $");
     46 #endif
     47 #endif				/* not lint */
     48 
     49 #include <sys/param.h>
     50 #include <sys/file.h>
     51 #include <sys/ioctl.h>
     52 #include <sys/mount.h>
     53 #include <sys/socket.h>
     54 #include <sys/stat.h>
     55 #include <syslog.h>
     56 #include <sys/ucred.h>
     57 
     58 #include <rpc/rpc.h>
     59 #include <rpc/pmap_clnt.h>
     60 #include <rpc/pmap_prot.h>
     61 #include <rpcsvc/mount.h>
     62 #include <nfs/rpcv2.h>
     63 #include <nfs/nfsproto.h>
     64 #include <nfs/nfs.h>
     65 #include <nfs/nfsmount.h>
     66 
     67 #include <arpa/inet.h>
     68 
     69 #include <ctype.h>
     70 #include <errno.h>
     71 #include <grp.h>
     72 #include <netdb.h>
     73 #include <pwd.h>
     74 #include <netgroup.h>
     75 #include <signal.h>
     76 #include <stdio.h>
     77 #include <stdlib.h>
     78 #include <string.h>
     79 #include <unistd.h>
     80 #include <err.h>
     81 #include <util.h>
     82 #include "pathnames.h"
     83 
     84 #ifdef IPSEC
     85 #include <netinet6/ipsec.h>
     86 #ifndef IPSEC_POLICY_IPSEC	/* no ipsec support on old ipsec */
     87 #undef IPSEC
     88 #endif
     89 #include "ipsec.h"
     90 #endif
     91 
     92 #include <stdarg.h>
     93 
     94 /*
     95  * Structures for keeping the mount list and export list
     96  */
     97 struct mountlist {
     98 	struct mountlist *ml_next;
     99 	char ml_host[RPCMNT_NAMELEN + 1];
    100 	char ml_dirp[RPCMNT_PATHLEN + 1];
    101 	int ml_flag;/* XXX more flags (same as dp_flag) */
    102 };
    103 
    104 struct dirlist {
    105 	struct dirlist *dp_left;
    106 	struct dirlist *dp_right;
    107 	int dp_flag;
    108 	struct hostlist *dp_hosts;	/* List of hosts this dir exported to */
    109 	char dp_dirp[1];		/* Actually malloc'd to size of dir */
    110 };
    111 /* dp_flag bits */
    112 #define	DP_DEFSET	0x1
    113 #define DP_HOSTSET	0x2
    114 #define DP_KERB		0x4
    115 #define DP_NORESMNT	0x8
    116 
    117 struct exportlist {
    118 	struct exportlist *ex_next;
    119 	struct dirlist *ex_dirl;
    120 	struct dirlist *ex_defdir;
    121 	int             ex_flag;
    122 	fsid_t          ex_fs;
    123 	char           *ex_fsdir;
    124 	char           *ex_indexfile;
    125 };
    126 /* ex_flag bits */
    127 #define	EX_LINKED	0x1
    128 
    129 struct netmsk {
    130 	struct sockaddr_storage nt_net;
    131 	int		nt_len;
    132 	char           *nt_name;
    133 };
    134 
    135 union grouptypes {
    136 	struct addrinfo *gt_addrinfo;
    137 	struct netmsk   gt_net;
    138 };
    139 
    140 struct grouplist {
    141 	int             gr_type;
    142 	union grouptypes gr_ptr;
    143 	struct grouplist *gr_next;
    144 };
    145 /* Group types */
    146 #define	GT_NULL		0x0
    147 #define	GT_HOST		0x1
    148 #define	GT_NET		0x2
    149 
    150 struct hostlist {
    151 	int             ht_flag;/* Uses DP_xx bits */
    152 	struct grouplist *ht_grp;
    153 	struct hostlist *ht_next;
    154 };
    155 
    156 struct fhreturn {
    157 	int             fhr_flag;
    158 	int             fhr_vers;
    159 	size_t		fhr_fhsize;
    160 	union {
    161 		uint8_t v2[NFSX_V2FH];
    162 		uint8_t v3[NFSX_V3FHMAX];
    163 	} fhr_fh;
    164 };
    165 
    166 /* Global defs */
    167 static char    *add_expdir __P((struct dirlist **, char *, int));
    168 static void add_dlist __P((struct dirlist **, struct dirlist *,
    169     struct grouplist *, int));
    170 static void add_mlist __P((char *, char *, int));
    171 static int check_dirpath __P((const char *, size_t, char *));
    172 static int check_options __P((const char *, size_t, struct dirlist *));
    173 static int chk_host __P((struct dirlist *, struct sockaddr *, int *, int *));
    174 static int del_mlist __P((char *, char *, struct sockaddr *));
    175 static struct dirlist *dirp_search __P((struct dirlist *, char *));
    176 static int do_nfssvc __P((const char *, size_t, struct exportlist *,
    177     struct grouplist *, int, struct uucred *, char *, int, struct statvfs *));
    178 static int do_opt __P((const char *, size_t, char **, char **,
    179     struct exportlist *, struct grouplist *, int *, int *, struct uucred *));
    180 static struct exportlist *ex_search __P((fsid_t *));
    181 static int parse_directory __P((const char *, size_t, struct grouplist *,
    182     int, char *, struct exportlist **, struct statvfs *));
    183 static int parse_host_netgroup __P((const char *, size_t, struct exportlist *,
    184     struct grouplist *, char *, int *, struct grouplist **));
    185 static struct exportlist *get_exp __P((void));
    186 static void free_dir __P((struct dirlist *));
    187 static void free_exp __P((struct exportlist *));
    188 static void free_grp __P((struct grouplist *));
    189 static void free_host __P((struct hostlist *));
    190 static void get_exportlist __P((int));
    191 static int get_host __P((const char *, size_t, const char *,
    192     struct grouplist *));
    193 static struct hostlist *get_ht __P((void));
    194 static void get_mountlist __P((void));
    195 static int get_net __P((char *, struct netmsk *, int));
    196 static void free_exp_grp __P((struct exportlist *, struct grouplist *));
    197 static struct grouplist *get_grp __P((void));
    198 static void hang_dirp __P((struct dirlist *, struct grouplist *,
    199     struct exportlist *, int));
    200 static void mntsrv __P((struct svc_req *, SVCXPRT *));
    201 static void nextfield __P((char **, char **));
    202 static void parsecred __P((char *, struct uucred *));
    203 static int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
    204 static int scan_tree __P((struct dirlist *, struct sockaddr *));
    205 static void send_umntall __P((int));
    206 static int umntall_each __P((caddr_t, struct sockaddr_in *));
    207 static int xdr_dir __P((XDR *, char *));
    208 static int xdr_explist __P((XDR *, caddr_t));
    209 static int xdr_fhs __P((XDR *, caddr_t));
    210 static int xdr_mlist __P((XDR *, caddr_t));
    211 static int bitcmp __P((void *, void *, int));
    212 static int netpartcmp __P((struct sockaddr *, struct sockaddr *, int));
    213 static int sacmp __P((struct sockaddr *, struct sockaddr *));
    214 static int allones __P((struct sockaddr_storage *, int));
    215 static int countones __P((struct sockaddr *));
    216 static void bind_resv_port __P((int, sa_family_t, in_port_t));
    217 static void no_nfs(int);
    218 static struct exportlist *exphead;
    219 static struct mountlist *mlhead;
    220 static struct grouplist *grphead;
    221 static const char *exname;
    222 static struct uucred def_anon = {
    223 	1,
    224 	(uid_t) -2,
    225 	(gid_t) -2,
    226 	0,
    227 	{ 0 }
    228 };
    229 
    230 static int      opt_flags;
    231 static int	have_v6 = 1;
    232 static const int ninumeric = NI_NUMERICHOST;
    233 
    234 /* Bits for above */
    235 #define	OP_MAPROOT	0x001
    236 #define	OP_MAPALL	0x002
    237 #define	OP_KERB		0x004
    238 #define	OP_MASK		0x008
    239 #define	OP_NET		0x010
    240 #define	OP_ALLDIRS	0x040
    241 #define OP_NORESPORT	0x080
    242 #define OP_NORESMNT	0x100
    243 #define OP_MASKLEN	0x200
    244 
    245 static int      debug = 0;
    246 #if 0
    247 static void SYSLOG __P((int, const char *,...));
    248 #endif
    249 int main __P((int, char *[]));
    250 
    251 /*
    252  * If this is non-zero, -noresvport and -noresvmnt are implied for
    253  * each export.
    254  */
    255 static int noprivports;
    256 
    257 /*
    258  * Mountd server for NFS mount protocol as described in:
    259  * NFS: Network File System Protocol Specification, RFC1094, Appendix A
    260  * The optional arguments are the exports file name
    261  * default: _PATH_EXPORTS
    262  * "-d" to enable debugging
    263  * and "-n" to allow nonroot mount.
    264  */
    265 int
    266 main(argc, argv)
    267 	int argc;
    268 	char **argv;
    269 {
    270 	SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp;
    271 	struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
    272 	int udpsock, tcpsock, udp6sock, tcp6sock;
    273 	int xcreated = 0, s;
    274 	int c, one = 1;
    275 	int maxrec = RPC_MAXDATASIZE;
    276 	in_port_t forcedport = 0;
    277 #ifdef IPSEC
    278 	char *policy = NULL;
    279 #define ADDOPTS "P:"
    280 #else
    281 #define ADDOPTS
    282 #endif
    283 
    284 	while ((c = getopt(argc, argv, "dNnrp:" ADDOPTS)) != -1)
    285 		switch (c) {
    286 #ifdef IPSEC
    287 		case 'P':
    288 			if (ipsecsetup_test(policy = optarg))
    289 				errx(1, "Invalid ipsec policy `%s'", policy);
    290 			break;
    291 #endif
    292 		case 'p':
    293 			/* A forced port "0" will dynamically allocate a port */
    294 			forcedport = atoi(optarg);
    295 			break;
    296 		case 'd':
    297 			debug = 1;
    298 			break;
    299 		case 'N':
    300 			noprivports = 1;
    301 			break;
    302 			/* Compatibility */
    303 		case 'n':
    304 		case 'r':
    305 			break;
    306 		default:
    307 			fprintf(stderr, "usage: %s [-dNn]"
    308 #ifdef IPSEC
    309 			    " [-P policy]"
    310 #endif
    311 			    " [-p port] [exportsfile]\n", getprogname());
    312 			exit(1);
    313 		};
    314 	argc -= optind;
    315 	argv += optind;
    316 	grphead = NULL;
    317 	exphead = NULL;
    318 	mlhead = NULL;
    319 	if (argc == 1)
    320 		exname = *argv;
    321 	else
    322 		exname = _PATH_EXPORTS;
    323 	openlog("mountd", LOG_PID | (debug ? LOG_PERROR : 0), LOG_DAEMON);
    324 	(void)signal(SIGSYS, no_nfs);
    325 
    326 	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
    327 	if (s < 0)
    328 		have_v6 = 0;
    329 	else
    330 		close(s);
    331 
    332 	if (debug)
    333 		(void)fprintf(stderr, "Getting export list.\n");
    334 	get_exportlist(0);
    335 	if (debug)
    336 		(void)fprintf(stderr, "Getting mount list.\n");
    337 	get_mountlist();
    338 	if (debug)
    339 		(void)fprintf(stderr, "Here we go.\n");
    340 	if (debug == 0) {
    341 		daemon(0, 0);
    342 		(void)signal(SIGINT, SIG_IGN);
    343 		(void)signal(SIGQUIT, SIG_IGN);
    344 	}
    345 	(void)signal(SIGHUP, get_exportlist);
    346 	(void)signal(SIGTERM, send_umntall);
    347 	pidfile(NULL);
    348 
    349 	rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
    350 	rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
    351 
    352 	udpsock  = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    353 	tcpsock  = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    354 	udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
    355 	tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
    356 
    357 	/*
    358 	 * We're doing host-based access checks here, so don't allow
    359 	 * v4-in-v6 to confuse things. The kernel will disable it
    360 	 * by default on NFS sockets too.
    361 	 */
    362 	if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6,
    363 	    IPV6_V6ONLY, &one, sizeof one) < 0){
    364 		syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
    365 		exit(1);
    366 	}
    367 	if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6,
    368 	    IPV6_V6ONLY, &one, sizeof one) < 0){
    369 		syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
    370 		exit(1);
    371 	}
    372 
    373 	udpconf  = getnetconfigent("udp");
    374 	tcpconf  = getnetconfigent("tcp");
    375 	udp6conf = getnetconfigent("udp6");
    376 	tcp6conf = getnetconfigent("tcp6");
    377 
    378 	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
    379 
    380 	if (udpsock != -1 && udpconf != NULL) {
    381 		bind_resv_port(udpsock, AF_INET, forcedport);
    382 #ifdef IPSEC
    383 		if (policy)
    384 			ipsecsetup(AF_INET, udpsock, policy);
    385 #endif
    386 		udptransp = svc_dg_create(udpsock, 0, 0);
    387 		if (udptransp != NULL) {
    388 			if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1,
    389 				mntsrv, udpconf) ||
    390 			    !svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3,
    391 				mntsrv, udpconf))
    392 				syslog(LOG_WARNING, "can't register UDP service");
    393 			else
    394 				xcreated++;
    395 		} else
    396 			syslog(LOG_WARNING, "can't create UDP service");
    397 
    398 	}
    399 
    400 	if (tcpsock != -1 && tcpconf != NULL) {
    401 		bind_resv_port(tcpsock, AF_INET, forcedport);
    402 #ifdef IPSEC
    403 		if (policy)
    404 			ipsecsetup(AF_INET, tcpsock, policy);
    405 #endif
    406 		listen(tcpsock, SOMAXCONN);
    407 		tcptransp = svc_vc_create(tcpsock, RPC_MAXDATASIZE,
    408 		    RPC_MAXDATASIZE);
    409 		if (tcptransp != NULL) {
    410 			if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1,
    411 				mntsrv, tcpconf) ||
    412 			    !svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3,
    413 				mntsrv, tcpconf))
    414 				syslog(LOG_WARNING, "can't register TCP service");
    415 			else
    416 				xcreated++;
    417 		} else
    418 			syslog(LOG_WARNING, "can't create TCP service");
    419 
    420 	}
    421 
    422 	if (udp6sock != -1 && udp6conf != NULL) {
    423 		bind_resv_port(udp6sock, AF_INET6, forcedport);
    424 #ifdef IPSEC
    425 		if (policy)
    426 			ipsecsetup(AF_INET6, tcpsock, policy);
    427 #endif
    428 		udp6transp = svc_dg_create(udp6sock, 0, 0);
    429 		if (udp6transp != NULL) {
    430 			if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1,
    431 				mntsrv, udp6conf) ||
    432 			    !svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3,
    433 				mntsrv, udp6conf))
    434 				syslog(LOG_WARNING, "can't register UDP6 service");
    435 			else
    436 				xcreated++;
    437 		} else
    438 			syslog(LOG_WARNING, "can't create UDP6 service");
    439 
    440 	}
    441 
    442 	if (tcp6sock != -1 && tcp6conf != NULL) {
    443 		bind_resv_port(tcp6sock, AF_INET6, forcedport);
    444 #ifdef IPSEC
    445 		if (policy)
    446 			ipsecsetup(AF_INET6, tcpsock, policy);
    447 #endif
    448 		listen(tcp6sock, SOMAXCONN);
    449 		tcp6transp = svc_vc_create(tcp6sock, RPC_MAXDATASIZE,
    450 		    RPC_MAXDATASIZE);
    451 		if (tcp6transp != NULL) {
    452 			if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1,
    453 				mntsrv, tcp6conf) ||
    454 			    !svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3,
    455 				mntsrv, tcp6conf))
    456 				syslog(LOG_WARNING, "can't register TCP6 service");
    457 			else
    458 				xcreated++;
    459 		} else
    460 			syslog(LOG_WARNING, "can't create TCP6 service");
    461 
    462 	}
    463 
    464 	if (xcreated == 0) {
    465 		syslog(LOG_ERR, "could not create any services");
    466 		exit(1);
    467 	}
    468 
    469 	svc_run();
    470 	syslog(LOG_ERR, "Mountd died");
    471 	exit(1);
    472 }
    473 
    474 /*
    475  * The mount rpc service
    476  */
    477 void
    478 mntsrv(rqstp, transp)
    479 	struct svc_req *rqstp;
    480 	SVCXPRT *transp;
    481 {
    482 	struct exportlist *ep;
    483 	struct dirlist *dp;
    484 	struct fhreturn fhr;
    485 	struct stat     stb;
    486 	struct statvfs   fsb;
    487 	struct addrinfo *ai;
    488 	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
    489 	int lookup_failed = 1;
    490 	struct sockaddr *saddr;
    491 	u_short         sport;
    492 	char            rpcpath[RPCMNT_PATHLEN + 1], rdirpath[MAXPATHLEN];
    493 	long            bad = EACCES;
    494 	int             defset, hostset, ret;
    495 	sigset_t        sighup_mask;
    496 	struct sockaddr_in6 *sin6;
    497 	struct sockaddr_in *sin;
    498 	size_t fh_size;
    499 
    500 	(void)sigemptyset(&sighup_mask);
    501 	(void)sigaddset(&sighup_mask, SIGHUP);
    502 	saddr = svc_getrpccaller(transp)->buf;
    503 	switch (saddr->sa_family) {
    504 	case AF_INET6:
    505 		sin6 = (struct sockaddr_in6 *)saddr;
    506 		sport = ntohs(sin6->sin6_port);
    507 		break;
    508 	case AF_INET:
    509 		sin = (struct sockaddr_in *)saddr;
    510 		sport = ntohs(sin->sin_port);
    511 		break;
    512 	default:
    513 		syslog(LOG_ERR, "request from unknown address family");
    514 		return;
    515 	}
    516 	lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
    517 	    NULL, 0, 0);
    518 	if (getnameinfo(saddr, saddr->sa_len, numerichost,
    519 	    sizeof numerichost, NULL, 0, ninumeric) != 0)
    520 		strlcpy(numerichost, "?", sizeof(numerichost));
    521 	ai = NULL;
    522 	ret = 0;
    523 	switch (rqstp->rq_proc) {
    524 	case NULLPROC:
    525 		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
    526 			syslog(LOG_ERR, "Can't send reply");
    527 		return;
    528 	case MOUNTPROC_MNT:
    529 		if (debug)
    530 			fprintf(stderr,
    531 			    "got mount request from %s\n", numerichost);
    532 		if (!svc_getargs(transp, xdr_dir, rpcpath)) {
    533 			if (debug)
    534 				fprintf(stderr, "-> garbage args\n");
    535 			svcerr_decode(transp);
    536 			return;
    537 		}
    538 		if (debug)
    539 			fprintf(stderr,
    540 			    "-> rpcpath: %s\n", rpcpath);
    541 		/*
    542 		 * Get the real pathname and make sure it is a file or
    543 		 * directory that exists.
    544 		 */
    545 		if (realpath(rpcpath, rdirpath) == 0 ||
    546 		    stat(rdirpath, &stb) < 0 ||
    547 		    (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) ||
    548 		    statvfs(rdirpath, &fsb) < 0) {
    549 			(void)chdir("/"); /* Just in case realpath doesn't */
    550 			if (debug)
    551 				(void)fprintf(stderr, "-> stat failed on %s\n",
    552 				    rdirpath);
    553 			if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t) &bad))
    554 				syslog(LOG_ERR, "Can't send reply");
    555 			return;
    556 		}
    557 		if (debug)
    558 			fprintf(stderr,
    559 			    "-> dirpath: %s\n", rdirpath);
    560 		/* Check in the exports list */
    561 		(void)sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
    562 		ep = ex_search(&fsb.f_fsidx);
    563 		hostset = defset = 0;
    564 		if (ep && (chk_host(ep->ex_defdir, saddr, &defset,
    565 		   &hostset) || ((dp = dirp_search(ep->ex_dirl, rdirpath)) &&
    566 		   chk_host(dp, saddr, &defset, &hostset)) ||
    567 		   (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
    568 		   scan_tree(ep->ex_dirl, saddr) == 0))) {
    569 			if ((hostset & DP_HOSTSET) == 0) {
    570 				hostset = defset;
    571 			}
    572 			if (sport >= IPPORT_RESERVED &&
    573 			    !(hostset & DP_NORESMNT)) {
    574 				syslog(LOG_NOTICE,
    575 				    "Refused mount RPC from host %s port %d",
    576 				    numerichost, sport);
    577 				svcerr_weakauth(transp);
    578 				goto out;
    579 			}
    580 			fhr.fhr_flag = hostset;
    581 			fhr.fhr_vers = rqstp->rq_vers;
    582 			/* Get the file handle */
    583 			memset(&fhr.fhr_fh, 0, sizeof(fhr.fhr_fh)); /* for v2 */
    584 			fh_size = sizeof(fhr.fhr_fh);
    585 			if (getfh(rdirpath, &fhr.fhr_fh, &fh_size) < 0) {
    586 				bad = errno;
    587 				syslog(LOG_ERR, "Can't get fh for %s", rdirpath);
    588 				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
    589 				    (char *)&bad))
    590 					syslog(LOG_ERR, "Can't send reply");
    591 				goto out;
    592 			}
    593 			if ((fhr.fhr_vers == 1 && fh_size > NFSX_V2FH) ||
    594 			    fh_size > NFSX_V3FHMAX) {
    595 				bad = EINVAL; /* XXX */
    596 				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
    597 				    (char *)&bad))
    598 					syslog(LOG_ERR, "Can't send reply");
    599 				goto out;
    600 			}
    601 			fhr.fhr_fhsize = fh_size;
    602 			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, (char *) &fhr))
    603 				syslog(LOG_ERR, "Can't send reply");
    604 			if (!lookup_failed)
    605 				add_mlist(host, rdirpath, hostset);
    606 			else
    607 				add_mlist(numerichost, rdirpath, hostset);
    608 			if (debug)
    609 				(void)fprintf(stderr, "Mount successful.\n");
    610 		} else {
    611 			if (!svc_sendreply(transp, (xdrproc_t)xdr_long, (caddr_t) &bad))
    612 				syslog(LOG_ERR, "Can't send reply");
    613 		}
    614 out:
    615 		(void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
    616 		return;
    617 	case MOUNTPROC_DUMP:
    618 		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, NULL))
    619 			syslog(LOG_ERR, "Can't send reply");
    620 		return;
    621 	case MOUNTPROC_UMNT:
    622 		if (!svc_getargs(transp, xdr_dir, rdirpath)) {
    623 			svcerr_decode(transp);
    624 			return;
    625 		}
    626 		if (!lookup_failed)
    627 			ret = del_mlist(host, rdirpath, saddr);
    628 		ret |= del_mlist(numerichost, rdirpath, saddr);
    629 		if (ret) {
    630 			svcerr_weakauth(transp);
    631 			return;
    632 		}
    633 		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
    634 			syslog(LOG_ERR, "Can't send reply");
    635 		return;
    636 	case MOUNTPROC_UMNTALL:
    637 		if (!lookup_failed)
    638 			ret = del_mlist(host, NULL, saddr);
    639 		ret |= del_mlist(numerichost, NULL, saddr);
    640 		if (ret) {
    641 			svcerr_weakauth(transp);
    642 			return;
    643 		}
    644 		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
    645 			syslog(LOG_ERR, "Can't send reply");
    646 		return;
    647 	case MOUNTPROC_EXPORT:
    648 	case MOUNTPROC_EXPORTALL:
    649 		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, NULL))
    650 			syslog(LOG_ERR, "Can't send reply");
    651 		return;
    652 
    653 
    654 	default:
    655 		svcerr_noproc(transp);
    656 		return;
    657 	}
    658 }
    659 
    660 /*
    661  * Xdr conversion for a dirpath string
    662  */
    663 static int
    664 xdr_dir(xdrsp, dirp)
    665 	XDR *xdrsp;
    666 	char *dirp;
    667 {
    668 
    669 	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
    670 }
    671 
    672 /*
    673  * Xdr routine to generate file handle reply
    674  */
    675 static int
    676 xdr_fhs(xdrsp, cp)
    677 	XDR *xdrsp;
    678 	caddr_t cp;
    679 {
    680 	struct fhreturn *fhrp = (struct fhreturn *) cp;
    681 	long ok = 0, len, auth;
    682 
    683 	if (!xdr_long(xdrsp, &ok))
    684 		return (0);
    685 	switch (fhrp->fhr_vers) {
    686 	case 1:
    687 		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
    688 	case 3:
    689 		len = fhrp->fhr_fhsize;
    690 		if (!xdr_long(xdrsp, &len))
    691 			return (0);
    692 		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
    693 			return (0);
    694 		if (fhrp->fhr_flag & DP_KERB)
    695 			auth = RPCAUTH_KERB4;
    696 		else
    697 			auth = RPCAUTH_UNIX;
    698 		len = 1;
    699 		if (!xdr_long(xdrsp, &len))
    700 			return (0);
    701 		return (xdr_long(xdrsp, &auth));
    702 	};
    703 	return (0);
    704 }
    705 
    706 int
    707 xdr_mlist(xdrsp, cp)
    708 	XDR *xdrsp;
    709 	caddr_t cp;
    710 {
    711 	struct mountlist *mlp;
    712 	int trueval = 1;
    713 	int falseval = 0;
    714 	char *strp;
    715 
    716 	mlp = mlhead;
    717 	while (mlp) {
    718 		if (!xdr_bool(xdrsp, &trueval))
    719 			return (0);
    720 		strp = &mlp->ml_host[0];
    721 		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
    722 			return (0);
    723 		strp = &mlp->ml_dirp[0];
    724 		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
    725 			return (0);
    726 		mlp = mlp->ml_next;
    727 	}
    728 	if (!xdr_bool(xdrsp, &falseval))
    729 		return (0);
    730 	return (1);
    731 }
    732 
    733 /*
    734  * Xdr conversion for export list
    735  */
    736 int
    737 xdr_explist(xdrsp, cp)
    738 	XDR *xdrsp;
    739 	caddr_t cp;
    740 {
    741 	struct exportlist *ep;
    742 	int falseval = 0;
    743 	int putdef;
    744 	sigset_t sighup_mask;
    745 
    746 	(void)sigemptyset(&sighup_mask);
    747 	(void)sigaddset(&sighup_mask, SIGHUP);
    748 	(void)sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
    749 	ep = exphead;
    750 	while (ep) {
    751 		putdef = 0;
    752 		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
    753 			goto errout;
    754 		if (ep->ex_defdir && putdef == 0 &&
    755 		    put_exlist(ep->ex_defdir, xdrsp, NULL, &putdef))
    756 			goto errout;
    757 		ep = ep->ex_next;
    758 	}
    759 	(void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
    760 	if (!xdr_bool(xdrsp, &falseval))
    761 		return (0);
    762 	return (1);
    763 errout:
    764 	(void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
    765 	return (0);
    766 }
    767 
    768 /*
    769  * Called from xdr_explist() to traverse the tree and export the
    770  * directory paths.  Assumes SIGHUP has already been masked.
    771  */
    772 int
    773 put_exlist(dp, xdrsp, adp, putdefp)
    774 	struct dirlist *dp;
    775 	XDR *xdrsp;
    776 	struct dirlist *adp;
    777 	int *putdefp;
    778 {
    779 	struct grouplist *grp;
    780 	struct hostlist *hp;
    781 	int trueval = 1;
    782 	int falseval = 0;
    783 	int gotalldir = 0;
    784 	char *strp;
    785 
    786 	if (dp) {
    787 		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
    788 			return (1);
    789 		if (!xdr_bool(xdrsp, &trueval))
    790 			return (1);
    791 		strp = dp->dp_dirp;
    792 		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
    793 			return (1);
    794 		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
    795 			gotalldir = 1;
    796 			*putdefp = 1;
    797 		}
    798 		if ((dp->dp_flag & DP_DEFSET) == 0 &&
    799 		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
    800 			hp = dp->dp_hosts;
    801 			while (hp) {
    802 				grp = hp->ht_grp;
    803 				if (grp->gr_type == GT_HOST) {
    804 					if (!xdr_bool(xdrsp, &trueval))
    805 						return (1);
    806 					strp =
    807 					  grp->gr_ptr.gt_addrinfo->ai_canonname;
    808 					if (!xdr_string(xdrsp, &strp,
    809 							RPCMNT_NAMELEN))
    810 						return (1);
    811 				} else if (grp->gr_type == GT_NET) {
    812 					if (!xdr_bool(xdrsp, &trueval))
    813 						return (1);
    814 					strp = grp->gr_ptr.gt_net.nt_name;
    815 					if (!xdr_string(xdrsp, &strp,
    816 							RPCMNT_NAMELEN))
    817 						return (1);
    818 				}
    819 				hp = hp->ht_next;
    820 				if (gotalldir && hp == NULL) {
    821 					hp = adp->dp_hosts;
    822 					gotalldir = 0;
    823 				}
    824 			}
    825 		}
    826 		if (!xdr_bool(xdrsp, &falseval))
    827 			return (1);
    828 		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
    829 			return (1);
    830 	}
    831 	return (0);
    832 }
    833 
    834 static int
    835 parse_host_netgroup(line, lineno, ep, tgrp, cp, has_host, grp)
    836 	const char *line;
    837 	size_t lineno;
    838 	struct exportlist *ep;
    839 	struct grouplist *tgrp;
    840 	char *cp;
    841 	int *has_host;
    842 	struct grouplist **grp;
    843 {
    844 	const char *hst, *usr, *dom;
    845 	int netgrp;
    846 
    847 	if (ep == NULL) {
    848 		syslog(LOG_ERR, "\"%s\", line %ld: No current export",
    849 		    line, (unsigned long)lineno);
    850 		return 0;
    851 	}
    852 	setnetgrent(cp);
    853 	netgrp = getnetgrent(&hst, &usr, &dom);
    854 	do {
    855 		if (*has_host) {
    856 			(*grp)->gr_next = get_grp();
    857 			*grp = (*grp)->gr_next;
    858 		}
    859 		if (netgrp) {
    860 			if (hst == NULL) {
    861 				syslog(LOG_ERR,
    862 				    "\"%s\", line %ld: No host in netgroup %s",
    863 				    line, (unsigned long)lineno, cp);
    864 				goto bad;
    865 			}
    866 			if (get_host(line, lineno, hst, *grp))
    867 				goto bad;
    868 		} else if (get_host(line, lineno, cp, *grp))
    869 			goto bad;
    870 		*has_host = TRUE;
    871 	} while (netgrp && getnetgrent(&hst, &usr, &dom));
    872 
    873 	endnetgrent();
    874 	return 1;
    875 bad:
    876 	endnetgrent();
    877 	return 0;
    878 
    879 }
    880 
    881 static int
    882 parse_directory(line, lineno, tgrp, got_nondir, cp, ep, fsp)
    883 	const char *line;
    884 	size_t lineno;
    885 	struct grouplist *tgrp;
    886 	int got_nondir;
    887 	char *cp;
    888 	struct exportlist **ep;
    889 	struct statvfs *fsp;
    890 {
    891 	if (!check_dirpath(line, lineno, cp))
    892 		return 0;
    893 
    894 	if (statvfs(cp, fsp) == -1) {
    895 		syslog(LOG_ERR, "\"%s\", line %ld: statvfs for `%s' failed: %m",
    896 		    line, (unsigned long)lineno, cp);
    897 		return 0;
    898 	}
    899 
    900 	if (got_nondir) {
    901 		syslog(LOG_ERR,
    902 		    "\"%s\", line %ld: Directories must precede files",
    903 		    line, (unsigned long)lineno);
    904 		return 0;
    905 	}
    906 	if (*ep) {
    907 		if ((*ep)->ex_fs.__fsid_val[0] != fsp->f_fsidx.__fsid_val[0] ||
    908 		    (*ep)->ex_fs.__fsid_val[1] != fsp->f_fsidx.__fsid_val[1]) {
    909 			syslog(LOG_ERR,
    910 			    "\"%s\", line %ld: filesystem ids disagree",
    911 			    line, (unsigned long)lineno);
    912 			return 0;
    913 		}
    914 	} else {
    915 		/*
    916 		 * See if this directory is already
    917 		 * in the list.
    918 		 */
    919 		*ep = ex_search(&fsp->f_fsidx);
    920 		if (*ep == NULL) {
    921 			*ep = get_exp();
    922 			(*ep)->ex_fs = fsp->f_fsidx;
    923 			(*ep)->ex_fsdir = estrdup(fsp->f_mntonname);
    924 			if (debug)
    925 				(void)fprintf(stderr,
    926 				    "Making new ep fs=0x%x,0x%x\n",
    927 				    fsp->f_fsidx.__fsid_val[0], fsp->f_fsidx.__fsid_val[1]);
    928 		} else {
    929 			if (debug)
    930 				(void)fprintf(stderr,
    931 				    "Found ep fs=0x%x,0x%x\n",
    932 				    fsp->f_fsidx.__fsid_val[0], fsp->f_fsidx.__fsid_val[1]);
    933 		}
    934 	}
    935 
    936 	return 1;
    937 }
    938 
    939 
    940 /*
    941  * Get the export list
    942  */
    943 /* ARGSUSED */
    944 void
    945 get_exportlist(n)
    946 	int n;
    947 {
    948 	struct exportlist *ep, *ep2;
    949 	struct grouplist *grp, *tgrp;
    950 	struct exportlist **epp;
    951 	struct dirlist *dirhead;
    952 	struct statvfs fsb, *fsp;
    953 	struct addrinfo *ai;
    954 	struct uucred anon;
    955 	char *cp, *endcp, *dirp, savedc;
    956 	int has_host, exflags, got_nondir, dirplen, num, i;
    957 	FILE *exp_file;
    958 	char *line;
    959 	size_t lineno = 0, len;
    960 
    961 
    962 	/*
    963 	 * First, get rid of the old list
    964 	 */
    965 	ep = exphead;
    966 	while (ep) {
    967 		ep2 = ep;
    968 		ep = ep->ex_next;
    969 		free_exp(ep2);
    970 	}
    971 	exphead = NULL;
    972 
    973 	dirp = NULL;
    974 	dirplen = 0;
    975 	grp = grphead;
    976 	while (grp) {
    977 		tgrp = grp;
    978 		grp = grp->gr_next;
    979 		free_grp(tgrp);
    980 	}
    981 	grphead = NULL;
    982 
    983 	/*
    984 	 * And delete exports that are in the kernel for all local
    985 	 * file systems.
    986 	 */
    987 	num = getmntinfo(&fsp, MNT_NOWAIT);
    988 	for (i = 0; i < num; i++) {
    989 		struct mountd_exports_list mel;
    990 
    991 		/* Delete all entries from the export list. */
    992 		mel.mel_path = fsp->f_mntonname;
    993 		mel.mel_nexports = 0;
    994 		if (nfssvc(NFSSVC_SETEXPORTSLIST, &mel) == -1 &&
    995 		    errno != EOPNOTSUPP)
    996 			syslog(LOG_ERR, "Can't delete exports for %s (%m)",
    997 			    fsp->f_mntonname);
    998 
    999 		fsp++;
   1000 	}
   1001 
   1002 	/*
   1003 	 * Read in the exports file and build the list, calling
   1004 	 * mount() as we go along to push the export rules into the kernel.
   1005 	 */
   1006 	if ((exp_file = fopen(exname, "r")) == NULL) {
   1007 		/*
   1008 		 * Don't exit here; we can still reload the config
   1009 		 * after a SIGHUP.
   1010 		 */
   1011 		if (debug)
   1012 			(void)fprintf(stderr, "Can't open %s: %s\n", exname,
   1013 			    strerror(errno));
   1014 		return;
   1015 	}
   1016 	dirhead = NULL;
   1017 	while ((line = fparseln(exp_file, &len, &lineno, NULL, 0)) != NULL) {
   1018 		if (debug)
   1019 			(void)fprintf(stderr, "Got line %s\n", line);
   1020 		cp = line;
   1021 		nextfield(&cp, &endcp);
   1022 		if (cp == endcp)
   1023 			goto nextline;	/* skip empty line */
   1024 		/*
   1025 		 * Set defaults.
   1026 		 */
   1027 		has_host = FALSE;
   1028 		anon = def_anon;
   1029 		exflags = MNT_EXPORTED;
   1030 		got_nondir = 0;
   1031 		opt_flags = 0;
   1032 		ep = NULL;
   1033 
   1034 		if (noprivports) {
   1035 			opt_flags |= OP_NORESMNT | OP_NORESPORT;
   1036 			exflags |= MNT_EXNORESPORT;
   1037 		}
   1038 
   1039 		/*
   1040 		 * Create new exports list entry
   1041 		 */
   1042 		len = endcp - cp;
   1043 		tgrp = grp = get_grp();
   1044 		while (len > 0) {
   1045 			if (len > RPCMNT_NAMELEN) {
   1046 				*endcp = '\0';
   1047 				syslog(LOG_ERR,
   1048 				    "\"%s\", line %ld: name `%s' is too long",
   1049 				    line, (unsigned long)lineno, cp);
   1050 				goto badline;
   1051 			}
   1052 			switch (*cp) {
   1053 			case '-':
   1054 				/*
   1055 				 * Option
   1056 				 */
   1057 				if (ep == NULL) {
   1058 					syslog(LOG_ERR,
   1059 				"\"%s\", line %ld: No current export list",
   1060 					    line, (unsigned long)lineno);
   1061 					goto badline;
   1062 				}
   1063 				if (debug)
   1064 					(void)fprintf(stderr, "doing opt %s\n",
   1065 					    cp);
   1066 				got_nondir = 1;
   1067 				if (do_opt(line, lineno, &cp, &endcp, ep, grp,
   1068 				    &has_host, &exflags, &anon))
   1069 					goto badline;
   1070 				break;
   1071 
   1072 			case '/':
   1073 				/*
   1074 				 * Directory
   1075 				 */
   1076 				savedc = *endcp;
   1077 				*endcp = '\0';
   1078 
   1079 				if (!parse_directory(line, lineno, tgrp,
   1080 				    got_nondir, cp, &ep, &fsb))
   1081 					goto badline;
   1082 				/*
   1083 				 * Add dirpath to export mount point.
   1084 				 */
   1085 				dirp = add_expdir(&dirhead, cp, len);
   1086 				dirplen = len;
   1087 
   1088 				*endcp = savedc;
   1089 				break;
   1090 
   1091 			default:
   1092 				/*
   1093 				 * Host or netgroup.
   1094 				 */
   1095 				savedc = *endcp;
   1096 				*endcp = '\0';
   1097 
   1098 				if (!parse_host_netgroup(line, lineno, ep,
   1099 				    tgrp, cp, &has_host, &grp))
   1100 					goto badline;
   1101 
   1102 				got_nondir = 1;
   1103 
   1104 				*endcp = savedc;
   1105 				break;
   1106 			}
   1107 
   1108 			cp = endcp;
   1109 			nextfield(&cp, &endcp);
   1110 			len = endcp - cp;
   1111 		}
   1112 		if (check_options(line, lineno, dirhead))
   1113 			goto badline;
   1114 
   1115 		if (!has_host) {
   1116 			grp->gr_type = GT_HOST;
   1117 			if (debug)
   1118 				(void)fprintf(stderr,
   1119 				    "Adding a default entry\n");
   1120 			/* add a default group and make the grp list NULL */
   1121 			ai = emalloc(sizeof(struct addrinfo));
   1122 			ai->ai_flags = 0;
   1123 			ai->ai_family = AF_INET;	/* XXXX */
   1124 			ai->ai_socktype = SOCK_DGRAM;
   1125 			/* setting the length to 0 will match anything */
   1126 			ai->ai_addrlen = 0;
   1127 			ai->ai_flags = AI_CANONNAME;
   1128 			ai->ai_canonname = estrdup("Default");
   1129 			ai->ai_addr = NULL;
   1130 			ai->ai_next = NULL;
   1131 			grp->gr_ptr.gt_addrinfo = ai;
   1132 
   1133 		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
   1134 			/*
   1135 			 * Don't allow a network export coincide with a list of
   1136 			 * host(s) on the same line.
   1137 			 */
   1138 			syslog(LOG_ERR,
   1139 			    "\"%s\", line %ld: Mixed exporting of networks and hosts is disallowed",
   1140 			    line, (unsigned long)lineno);
   1141 			goto badline;
   1142 		}
   1143 		/*
   1144 		 * Loop through hosts, pushing the exports into the kernel.
   1145 		 * After loop, tgrp points to the start of the list and
   1146 		 * grp points to the last entry in the list.
   1147 		 */
   1148 		grp = tgrp;
   1149 		do {
   1150 			if (do_nfssvc(line, lineno, ep, grp, exflags, &anon,
   1151 			    dirp, dirplen, &fsb))
   1152 				goto badline;
   1153 		} while (grp->gr_next && (grp = grp->gr_next));
   1154 
   1155 		/*
   1156 		 * Success. Update the data structures.
   1157 		 */
   1158 		if (has_host) {
   1159 			hang_dirp(dirhead, tgrp, ep, opt_flags);
   1160 			grp->gr_next = grphead;
   1161 			grphead = tgrp;
   1162 		} else {
   1163 			hang_dirp(dirhead, NULL, ep, opt_flags);
   1164 			free_grp(tgrp);
   1165 		}
   1166 		tgrp = NULL;
   1167 		dirhead = NULL;
   1168 		if ((ep->ex_flag & EX_LINKED) == 0) {
   1169 			ep2 = exphead;
   1170 			epp = &exphead;
   1171 
   1172 			/*
   1173 			 * Insert in the list in alphabetical order.
   1174 			 */
   1175 			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
   1176 				epp = &ep2->ex_next;
   1177 				ep2 = ep2->ex_next;
   1178 			}
   1179 			if (ep2)
   1180 				ep->ex_next = ep2;
   1181 			*epp = ep;
   1182 			ep->ex_flag |= EX_LINKED;
   1183 		}
   1184 		goto nextline;
   1185 badline:
   1186 		free_exp_grp(ep, grp);
   1187 nextline:
   1188 		if (dirhead) {
   1189 			free_dir(dirhead);
   1190 			dirhead = NULL;
   1191 		}
   1192 		free(line);
   1193 	}
   1194 	(void)fclose(exp_file);
   1195 }
   1196 
   1197 /*
   1198  * Allocate an export list element
   1199  */
   1200 static struct exportlist *
   1201 get_exp()
   1202 {
   1203 	struct exportlist *ep;
   1204 
   1205 	ep = emalloc(sizeof(struct exportlist));
   1206 	(void)memset(ep, 0, sizeof(struct exportlist));
   1207 	return (ep);
   1208 }
   1209 
   1210 /*
   1211  * Allocate a group list element
   1212  */
   1213 static struct grouplist *
   1214 get_grp()
   1215 {
   1216 	struct grouplist *gp;
   1217 
   1218 	gp = emalloc(sizeof(struct grouplist));
   1219 	(void)memset(gp, 0, sizeof(struct grouplist));
   1220 	return (gp);
   1221 }
   1222 
   1223 /*
   1224  * Clean up upon an error in get_exportlist().
   1225  */
   1226 static void
   1227 free_exp_grp(ep, grp)
   1228 	struct exportlist *ep;
   1229 	struct grouplist *grp;
   1230 {
   1231 	struct grouplist *tgrp;
   1232 
   1233 	if (ep && (ep->ex_flag & EX_LINKED) == 0)
   1234 		free_exp(ep);
   1235 	while (grp) {
   1236 		tgrp = grp;
   1237 		grp = grp->gr_next;
   1238 		free_grp(tgrp);
   1239 	}
   1240 }
   1241 
   1242 /*
   1243  * Search the export list for a matching fs.
   1244  */
   1245 static struct exportlist *
   1246 ex_search(fsid)
   1247 	fsid_t *fsid;
   1248 {
   1249 	struct exportlist *ep;
   1250 
   1251 	ep = exphead;
   1252 	while (ep) {
   1253 		if (ep->ex_fs.__fsid_val[0] == fsid->__fsid_val[0] &&
   1254 		    ep->ex_fs.__fsid_val[1] == fsid->__fsid_val[1])
   1255 			return (ep);
   1256 		ep = ep->ex_next;
   1257 	}
   1258 	return (ep);
   1259 }
   1260 
   1261 /*
   1262  * Add a directory path to the list.
   1263  */
   1264 static char *
   1265 add_expdir(dpp, cp, len)
   1266 	struct dirlist **dpp;
   1267 	char *cp;
   1268 	int len;
   1269 {
   1270 	struct dirlist *dp;
   1271 
   1272 	dp = emalloc(sizeof(struct dirlist) + len);
   1273 	dp->dp_left = *dpp;
   1274 	dp->dp_right = NULL;
   1275 	dp->dp_flag = 0;
   1276 	dp->dp_hosts = NULL;
   1277 	(void)strcpy(dp->dp_dirp, cp);
   1278 	*dpp = dp;
   1279 	return (dp->dp_dirp);
   1280 }
   1281 
   1282 /*
   1283  * Hang the dir list element off the dirpath binary tree as required
   1284  * and update the entry for host.
   1285  */
   1286 void
   1287 hang_dirp(dp, grp, ep, flags)
   1288 	struct dirlist *dp;
   1289 	struct grouplist *grp;
   1290 	struct exportlist *ep;
   1291 	int flags;
   1292 {
   1293 	struct hostlist *hp;
   1294 	struct dirlist *dp2;
   1295 
   1296 	if (flags & OP_ALLDIRS) {
   1297 		if (ep->ex_defdir)
   1298 			free(dp);
   1299 		else
   1300 			ep->ex_defdir = dp;
   1301 		if (grp == NULL) {
   1302 			ep->ex_defdir->dp_flag |= DP_DEFSET;
   1303 			if (flags & OP_KERB)
   1304 				ep->ex_defdir->dp_flag |= DP_KERB;
   1305 			if (flags & OP_NORESMNT)
   1306 				ep->ex_defdir->dp_flag |= DP_NORESMNT;
   1307 		} else
   1308 			while (grp) {
   1309 				hp = get_ht();
   1310 				if (flags & OP_KERB)
   1311 					hp->ht_flag |= DP_KERB;
   1312 				if (flags & OP_NORESMNT)
   1313 					hp->ht_flag |= DP_NORESMNT;
   1314 				hp->ht_grp = grp;
   1315 				hp->ht_next = ep->ex_defdir->dp_hosts;
   1316 				ep->ex_defdir->dp_hosts = hp;
   1317 				grp = grp->gr_next;
   1318 			}
   1319 	} else {
   1320 
   1321 		/*
   1322 		 * Loop through the directories adding them to the tree.
   1323 		 */
   1324 		while (dp) {
   1325 			dp2 = dp->dp_left;
   1326 			add_dlist(&ep->ex_dirl, dp, grp, flags);
   1327 			dp = dp2;
   1328 		}
   1329 	}
   1330 }
   1331 
   1332 /*
   1333  * Traverse the binary tree either updating a node that is already there
   1334  * for the new directory or adding the new node.
   1335  */
   1336 static void
   1337 add_dlist(dpp, newdp, grp, flags)
   1338 	struct dirlist **dpp;
   1339 	struct dirlist *newdp;
   1340 	struct grouplist *grp;
   1341 	int flags;
   1342 {
   1343 	struct dirlist *dp;
   1344 	struct hostlist *hp;
   1345 	int cmp;
   1346 
   1347 	dp = *dpp;
   1348 	if (dp) {
   1349 		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
   1350 		if (cmp > 0) {
   1351 			add_dlist(&dp->dp_left, newdp, grp, flags);
   1352 			return;
   1353 		} else if (cmp < 0) {
   1354 			add_dlist(&dp->dp_right, newdp, grp, flags);
   1355 			return;
   1356 		} else
   1357 			free(newdp);
   1358 	} else {
   1359 		dp = newdp;
   1360 		dp->dp_left = NULL;
   1361 		*dpp = dp;
   1362 	}
   1363 	if (grp) {
   1364 
   1365 		/*
   1366 		 * Hang all of the host(s) off of the directory point.
   1367 		 */
   1368 		do {
   1369 			hp = get_ht();
   1370 			if (flags & OP_KERB)
   1371 				hp->ht_flag |= DP_KERB;
   1372 			if (flags & OP_NORESMNT)
   1373 				hp->ht_flag |= DP_NORESMNT;
   1374 			hp->ht_grp = grp;
   1375 			hp->ht_next = dp->dp_hosts;
   1376 			dp->dp_hosts = hp;
   1377 			grp = grp->gr_next;
   1378 		} while (grp);
   1379 	} else {
   1380 		dp->dp_flag |= DP_DEFSET;
   1381 		if (flags & OP_KERB)
   1382 			dp->dp_flag |= DP_KERB;
   1383 		if (flags & OP_NORESMNT)
   1384 			dp->dp_flag |= DP_NORESMNT;
   1385 	}
   1386 }
   1387 
   1388 /*
   1389  * Search for a dirpath on the export point.
   1390  */
   1391 static struct dirlist *
   1392 dirp_search(dp, dirp)
   1393 	struct dirlist *dp;
   1394 	char *dirp;
   1395 {
   1396 	int cmp;
   1397 
   1398 	if (dp) {
   1399 		cmp = strcmp(dp->dp_dirp, dirp);
   1400 		if (cmp > 0)
   1401 			return (dirp_search(dp->dp_left, dirp));
   1402 		else if (cmp < 0)
   1403 			return (dirp_search(dp->dp_right, dirp));
   1404 		else
   1405 			return (dp);
   1406 	}
   1407 	return (dp);
   1408 }
   1409 
   1410 /*
   1411  * Some helper functions for netmasks. They all assume masks in network
   1412  * order (big endian).
   1413  */
   1414 static int
   1415 bitcmp(void *dst, void *src, int bitlen)
   1416 {
   1417 	int i;
   1418 	u_int8_t *p1 = dst, *p2 = src;
   1419 	u_int8_t bitmask;
   1420 	int bytelen, bitsleft;
   1421 
   1422 	bytelen = bitlen / 8;
   1423 	bitsleft = bitlen % 8;
   1424 
   1425 	if (debug) {
   1426 		printf("comparing:\n");
   1427 		for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++)
   1428 			printf("%02x", p1[i]);
   1429 		printf("\n");
   1430 		for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++)
   1431 			printf("%02x", p2[i]);
   1432 		printf("\n");
   1433 	}
   1434 
   1435 	for (i = 0; i < bytelen; i++) {
   1436 		if (*p1 != *p2)
   1437 			return 1;
   1438 		p1++;
   1439 		p2++;
   1440 	}
   1441 
   1442 	for (i = 0; i < bitsleft; i++) {
   1443 		bitmask = 1 << (7 - i);
   1444 		if ((*p1 & bitmask) != (*p2 & bitmask))
   1445 			return 1;
   1446 	}
   1447 
   1448 	return 0;
   1449 }
   1450 
   1451 static int
   1452 netpartcmp(struct sockaddr *s1, struct sockaddr *s2, int bitlen)
   1453 {
   1454 	void *src, *dst;
   1455 
   1456 	if (s1->sa_family != s2->sa_family)
   1457 		return 1;
   1458 
   1459 	switch (s1->sa_family) {
   1460 	case AF_INET:
   1461 		src = &((struct sockaddr_in *)s1)->sin_addr;
   1462 		dst = &((struct sockaddr_in *)s2)->sin_addr;
   1463 		if (bitlen > (int)sizeof(((struct sockaddr_in *)s1)->sin_addr) * 8)
   1464 			return 1;
   1465 		break;
   1466 	case AF_INET6:
   1467 		src = &((struct sockaddr_in6 *)s1)->sin6_addr;
   1468 		dst = &((struct sockaddr_in6 *)s2)->sin6_addr;
   1469 		if (((struct sockaddr_in6 *)s1)->sin6_scope_id !=
   1470 		    ((struct sockaddr_in6 *)s2)->sin6_scope_id)
   1471 			return 1;
   1472 		if (bitlen > (int)sizeof(((struct sockaddr_in6 *)s1)->sin6_addr) * 8)
   1473 			return 1;
   1474 		break;
   1475 	default:
   1476 		return 1;
   1477 	}
   1478 
   1479 	return bitcmp(src, dst, bitlen);
   1480 }
   1481 
   1482 static int
   1483 allones(struct sockaddr_storage *ssp, int bitlen)
   1484 {
   1485 	u_int8_t *p;
   1486 	int bytelen, bitsleft, i;
   1487 	int zerolen;
   1488 
   1489 	switch (ssp->ss_family) {
   1490 	case AF_INET:
   1491 		p = (u_int8_t *)&((struct sockaddr_in *)ssp)->sin_addr;
   1492 		zerolen = sizeof (((struct sockaddr_in *)ssp)->sin_addr);
   1493 		break;
   1494 	case AF_INET6:
   1495 		p = (u_int8_t *)&((struct sockaddr_in6 *)ssp)->sin6_addr;
   1496 		zerolen = sizeof (((struct sockaddr_in6 *)ssp)->sin6_addr);
   1497 		break;
   1498 	default:
   1499 		return -1;
   1500 	}
   1501 
   1502 	memset(p, 0, zerolen);
   1503 
   1504 	bytelen = bitlen / 8;
   1505 	bitsleft = bitlen % 8;
   1506 
   1507 	if (bytelen > zerolen)
   1508 		return -1;
   1509 
   1510 	for (i = 0; i < bytelen; i++)
   1511 		*p++ = 0xff;
   1512 
   1513 	for (i = 0; i < bitsleft; i++)
   1514 		*p |= 1 << (7 - i);
   1515 
   1516 	return 0;
   1517 }
   1518 
   1519 static int
   1520 countones(struct sockaddr *sa)
   1521 {
   1522 	void *mask;
   1523 	int i, bits = 0, bytelen;
   1524 	u_int8_t *p;
   1525 
   1526 	switch (sa->sa_family) {
   1527 	case AF_INET:
   1528 		mask = (u_int8_t *)&((struct sockaddr_in *)sa)->sin_addr;
   1529 		bytelen = 4;
   1530 		break;
   1531 	case AF_INET6:
   1532 		mask = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_addr;
   1533 		bytelen = 16;
   1534 		break;
   1535 	default:
   1536 		return 0;
   1537 	}
   1538 
   1539 	p = mask;
   1540 
   1541 	for (i = 0; i < bytelen; i++, p++) {
   1542 		if (*p != 0xff) {
   1543 			for (bits = 0; bits < 8; bits++) {
   1544 				if (!(*p & (1 << (7 - bits))))
   1545 					break;
   1546 			}
   1547 			break;
   1548 		}
   1549 	}
   1550 
   1551 	return (i * 8 + bits);
   1552 }
   1553 
   1554 static int
   1555 sacmp(struct sockaddr *sa1, struct sockaddr *sa2)
   1556 {
   1557 	void *p1, *p2;
   1558 	int len;
   1559 
   1560 	if (sa1->sa_family != sa2->sa_family)
   1561 		return 1;
   1562 
   1563 	switch (sa1->sa_family) {
   1564 	case AF_INET:
   1565 		p1 = &((struct sockaddr_in *)sa1)->sin_addr;
   1566 		p2 = &((struct sockaddr_in *)sa2)->sin_addr;
   1567 		len = 4;
   1568 		break;
   1569 	case AF_INET6:
   1570 		p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
   1571 		p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
   1572 		len = 16;
   1573 		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
   1574 		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
   1575 			return 1;
   1576 		break;
   1577 	default:
   1578 		return 1;
   1579 	}
   1580 
   1581 	return memcmp(p1, p2, len);
   1582 }
   1583 
   1584 /*
   1585  * Scan for a host match in a directory tree.
   1586  */
   1587 static int
   1588 chk_host(dp, saddr, defsetp, hostsetp)
   1589 	struct dirlist *dp;
   1590 	struct sockaddr *saddr;
   1591 	int *defsetp;
   1592 	int *hostsetp;
   1593 {
   1594 	struct hostlist *hp;
   1595 	struct grouplist *grp;
   1596 	struct addrinfo *ai;
   1597 
   1598 	if (dp) {
   1599 		if (dp->dp_flag & DP_DEFSET)
   1600 			*defsetp = dp->dp_flag;
   1601 		hp = dp->dp_hosts;
   1602 		while (hp) {
   1603 			grp = hp->ht_grp;
   1604 			switch (grp->gr_type) {
   1605 			case GT_HOST:
   1606 				ai = grp->gr_ptr.gt_addrinfo;
   1607 				for (; ai; ai = ai->ai_next) {
   1608 					if (!sacmp(ai->ai_addr, saddr)) {
   1609 						*hostsetp =
   1610 						    (hp->ht_flag | DP_HOSTSET);
   1611 						return (1);
   1612 					}
   1613 				}
   1614 				break;
   1615 			case GT_NET:
   1616 				if (!netpartcmp(saddr,
   1617 				    (struct sockaddr *)
   1618 					&grp->gr_ptr.gt_net.nt_net,
   1619 				    grp->gr_ptr.gt_net.nt_len)) {
   1620 					*hostsetp = (hp->ht_flag | DP_HOSTSET);
   1621 					return (1);
   1622 				}
   1623 				break;
   1624 			};
   1625 			hp = hp->ht_next;
   1626 		}
   1627 	}
   1628 	return (0);
   1629 }
   1630 
   1631 /*
   1632  * Scan tree for a host that matches the address.
   1633  */
   1634 static int
   1635 scan_tree(dp, saddr)
   1636 	struct dirlist *dp;
   1637 	struct sockaddr *saddr;
   1638 {
   1639 	int defset, hostset;
   1640 
   1641 	if (dp) {
   1642 		if (scan_tree(dp->dp_left, saddr))
   1643 			return (1);
   1644 		if (chk_host(dp, saddr, &defset, &hostset))
   1645 			return (1);
   1646 		if (scan_tree(dp->dp_right, saddr))
   1647 			return (1);
   1648 	}
   1649 	return (0);
   1650 }
   1651 
   1652 /*
   1653  * Traverse the dirlist tree and free it up.
   1654  */
   1655 static void
   1656 free_dir(dp)
   1657 	struct dirlist *dp;
   1658 {
   1659 
   1660 	if (dp) {
   1661 		free_dir(dp->dp_left);
   1662 		free_dir(dp->dp_right);
   1663 		free_host(dp->dp_hosts);
   1664 		free(dp);
   1665 	}
   1666 }
   1667 
   1668 /*
   1669  * Parse the option string and update fields.
   1670  * Option arguments may either be -<option>=<value> or
   1671  * -<option> <value>
   1672  */
   1673 static int
   1674 do_opt(line, lineno, cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
   1675 	const char *line;
   1676 	size_t lineno;
   1677 	char **cpp, **endcpp;
   1678 	struct exportlist *ep;
   1679 	struct grouplist *grp;
   1680 	int *has_hostp;
   1681 	int *exflagsp;
   1682 	struct uucred *cr;
   1683 {
   1684 	char *cpoptarg, *cpoptend;
   1685 	char *cp, *cpopt, savedc, savedc2;
   1686 	char *endcp = NULL;	/* XXX: GCC */
   1687 	int allflag, usedarg;
   1688 
   1689 	cpopt = *cpp;
   1690 	cpopt++;
   1691 	cp = *endcpp;
   1692 	savedc = *cp;
   1693 	*cp = '\0';
   1694 	while (cpopt && *cpopt) {
   1695 		allflag = 1;
   1696 		usedarg = -2;
   1697 		savedc2 = '\0';
   1698 		if ((cpoptend = strchr(cpopt, ',')) != NULL) {
   1699 			*cpoptend++ = '\0';
   1700 			if ((cpoptarg = strchr(cpopt, '=')) != NULL)
   1701 				*cpoptarg++ = '\0';
   1702 		} else {
   1703 			if ((cpoptarg = strchr(cpopt, '=')) != NULL)
   1704 				*cpoptarg++ = '\0';
   1705 			else {
   1706 				*cp = savedc;
   1707 				nextfield(&cp, &endcp);
   1708 				**endcpp = '\0';
   1709 				if (endcp > cp && *cp != '-') {
   1710 					cpoptarg = cp;
   1711 					savedc2 = *endcp;
   1712 					*endcp = '\0';
   1713 					usedarg = 0;
   1714 				}
   1715 			}
   1716 		}
   1717 		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
   1718 			*exflagsp |= MNT_EXRDONLY;
   1719 		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
   1720 		    !(allflag = strcmp(cpopt, "mapall")) ||
   1721 		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
   1722 			usedarg++;
   1723 			parsecred(cpoptarg, cr);
   1724 			if (allflag == 0) {
   1725 				*exflagsp |= MNT_EXPORTANON;
   1726 				opt_flags |= OP_MAPALL;
   1727 			} else
   1728 				opt_flags |= OP_MAPROOT;
   1729 		} else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
   1730 			*exflagsp |= MNT_EXKERB;
   1731 			opt_flags |= OP_KERB;
   1732 		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
   1733 		    !strcmp(cpopt, "m"))) {
   1734 			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
   1735 				syslog(LOG_ERR,
   1736 				    "\"%s\", line %ld: Bad mask: %s",
   1737 				    line, (unsigned long)lineno, cpoptarg);
   1738 				return (1);
   1739 			}
   1740 			usedarg++;
   1741 			opt_flags |= OP_MASK;
   1742 		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
   1743 		    !strcmp(cpopt, "n"))) {
   1744 			if (strchr(cpoptarg, '/') != NULL) {
   1745 				if (debug)
   1746 					fprintf(stderr, "setting OP_MASKLEN\n");
   1747 				opt_flags |= OP_MASKLEN;
   1748 			}
   1749 			if (grp->gr_type != GT_NULL) {
   1750 				syslog(LOG_ERR,
   1751 				    "\"%s\", line %ld: Network/host conflict",
   1752 				    line, (unsigned long)lineno);
   1753 				return (1);
   1754 			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
   1755 				syslog(LOG_ERR,
   1756 				    "\"%s\", line %ld: Bad net: %s",
   1757 				    line, (unsigned long)lineno, cpoptarg);
   1758 				return (1);
   1759 			}
   1760 			grp->gr_type = GT_NET;
   1761 			*has_hostp = 1;
   1762 			usedarg++;
   1763 			opt_flags |= OP_NET;
   1764 		} else if (!strcmp(cpopt, "alldirs")) {
   1765 			opt_flags |= OP_ALLDIRS;
   1766 		} else if (!strcmp(cpopt, "noresvmnt")) {
   1767 			opt_flags |= OP_NORESMNT;
   1768 		} else if (!strcmp(cpopt, "noresvport")) {
   1769 			opt_flags |= OP_NORESPORT;
   1770 			*exflagsp |= MNT_EXNORESPORT;
   1771 		} else if (!strcmp(cpopt, "public")) {
   1772 			*exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC);
   1773 			opt_flags |= OP_NORESPORT;
   1774 		} else if (!strcmp(cpopt, "webnfs")) {
   1775 			*exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC |
   1776 			    MNT_EXRDONLY | MNT_EXPORTANON);
   1777 			opt_flags |= (OP_MAPALL | OP_NORESPORT);
   1778 		} else if (cpoptarg && !strcmp(cpopt, "index")) {
   1779 			ep->ex_indexfile = strdup(cpoptarg);
   1780 		} else {
   1781 			syslog(LOG_ERR,
   1782 			    "\"%s\", line %ld: Bad opt %s",
   1783 			    line, (unsigned long)lineno, cpopt);
   1784 			return (1);
   1785 		}
   1786 		if (usedarg >= 0) {
   1787 			*endcp = savedc2;
   1788 			**endcpp = savedc;
   1789 			if (usedarg > 0) {
   1790 				*cpp = cp;
   1791 				*endcpp = endcp;
   1792 			}
   1793 			return (0);
   1794 		}
   1795 		cpopt = cpoptend;
   1796 	}
   1797 	**endcpp = savedc;
   1798 	return (0);
   1799 }
   1800 
   1801 /*
   1802  * Translate a character string to the corresponding list of network
   1803  * addresses for a hostname.
   1804  */
   1805 static int
   1806 get_host(line, lineno, cp, grp)
   1807 	const char *line;
   1808 	size_t lineno;
   1809 	const char *cp;
   1810 	struct grouplist *grp;
   1811 {
   1812 	struct addrinfo *ai, hints;
   1813 	int ecode;
   1814 	char host[NI_MAXHOST];
   1815 
   1816 	if (grp->gr_type != GT_NULL) {
   1817 		syslog(LOG_ERR,
   1818 		    "\"%s\", line %ld: Bad netgroup type for ip host %s",
   1819 		    line, (unsigned long)lineno, cp);
   1820 		return (1);
   1821 	}
   1822 	memset(&hints, 0, sizeof hints);
   1823 	hints.ai_flags = AI_CANONNAME;
   1824 	hints.ai_protocol = IPPROTO_UDP;
   1825 	ecode = getaddrinfo(cp, NULL, &hints, &ai);
   1826 	if (ecode != 0) {
   1827 		syslog(LOG_ERR, "\"%s\", line %ld: can't get address info for "
   1828 				"host %s",
   1829 		    line, (long)lineno, cp);
   1830 		return 1;
   1831 	}
   1832 	grp->gr_type = GT_HOST;
   1833 	grp->gr_ptr.gt_addrinfo = ai;
   1834 	while (ai != NULL) {
   1835 		if (ai->ai_canonname == NULL) {
   1836 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
   1837 			    sizeof host, NULL, 0, ninumeric) != 0)
   1838 				strlcpy(host, "?", sizeof(host));
   1839 			ai->ai_canonname = estrdup(host);
   1840 			ai->ai_flags |= AI_CANONNAME;
   1841 		} else
   1842 			ai->ai_flags &= ~AI_CANONNAME;
   1843 		if (debug)
   1844 			(void)fprintf(stderr, "got host %s\n", ai->ai_canonname);
   1845 		ai = ai->ai_next;
   1846 	}
   1847 	return (0);
   1848 }
   1849 
   1850 /*
   1851  * Free up an exports list component
   1852  */
   1853 static void
   1854 free_exp(ep)
   1855 	struct exportlist *ep;
   1856 {
   1857 
   1858 	if (ep->ex_defdir) {
   1859 		free_host(ep->ex_defdir->dp_hosts);
   1860 		free(ep->ex_defdir);
   1861 	}
   1862 	if (ep->ex_fsdir)
   1863 		free(ep->ex_fsdir);
   1864 	if (ep->ex_indexfile)
   1865 		free(ep->ex_indexfile);
   1866 	free_dir(ep->ex_dirl);
   1867 	free(ep);
   1868 }
   1869 
   1870 /*
   1871  * Free hosts.
   1872  */
   1873 static void
   1874 free_host(hp)
   1875 	struct hostlist *hp;
   1876 {
   1877 	struct hostlist *hp2;
   1878 
   1879 	while (hp) {
   1880 		hp2 = hp;
   1881 		hp = hp->ht_next;
   1882 		free(hp2);
   1883 	}
   1884 }
   1885 
   1886 static struct hostlist *
   1887 get_ht()
   1888 {
   1889 	struct hostlist *hp;
   1890 
   1891 	hp = emalloc(sizeof(struct hostlist));
   1892 	hp->ht_next = NULL;
   1893 	hp->ht_flag = 0;
   1894 	return (hp);
   1895 }
   1896 
   1897 /*
   1898  * Do the nfssvc syscall to push the export info into the kernel.
   1899  */
   1900 static int
   1901 do_nfssvc(line, lineno, ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
   1902 	const char *line;
   1903 	size_t lineno;
   1904 	struct exportlist *ep;
   1905 	struct grouplist *grp;
   1906 	int exflags;
   1907 	struct uucred *anoncrp;
   1908 	char *dirp;
   1909 	int dirplen;
   1910 	struct statvfs *fsb;
   1911 {
   1912 	struct sockaddr *addrp;
   1913 	struct sockaddr_storage ss;
   1914 	struct addrinfo *ai;
   1915 	int addrlen;
   1916 	int done;
   1917 	struct export_args export;
   1918 
   1919 	export.ex_flags = exflags;
   1920 	export.ex_anon = *anoncrp;
   1921 	export.ex_indexfile = ep->ex_indexfile;
   1922 	if (grp->gr_type == GT_HOST) {
   1923 		ai = grp->gr_ptr.gt_addrinfo;
   1924 		addrp = ai->ai_addr;
   1925 		addrlen = ai->ai_addrlen;
   1926 	} else {
   1927 		addrp = NULL;
   1928 		ai = NULL;	/* XXXGCC -Wuninitialized */
   1929 		addrlen = 0;	/* XXXGCC -Wuninitialized */
   1930 	}
   1931 	done = FALSE;
   1932 	while (!done) {
   1933 		struct mountd_exports_list mel;
   1934 
   1935 		switch (grp->gr_type) {
   1936 		case GT_HOST:
   1937 			if (addrp != NULL && addrp->sa_family == AF_INET6 &&
   1938 			    have_v6 == 0)
   1939 				goto skip;
   1940 			export.ex_addr = addrp;
   1941 			export.ex_addrlen = addrlen;
   1942 			export.ex_masklen = 0;
   1943 			break;
   1944 		case GT_NET:
   1945 			export.ex_addr = (struct sockaddr *)
   1946 			    &grp->gr_ptr.gt_net.nt_net;
   1947 			if (export.ex_addr->sa_family == AF_INET6 &&
   1948 			    have_v6 == 0)
   1949 				goto skip;
   1950 			export.ex_addrlen = export.ex_addr->sa_len;
   1951 			memset(&ss, 0, sizeof ss);
   1952 			ss.ss_family = export.ex_addr->sa_family;
   1953 			ss.ss_len = export.ex_addr->sa_len;
   1954 			if (allones(&ss, grp->gr_ptr.gt_net.nt_len) != 0) {
   1955 				syslog(LOG_ERR,
   1956 				    "\"%s\", line %ld: Bad network flag",
   1957 				    line, (unsigned long)lineno);
   1958 				return (1);
   1959 			}
   1960 			export.ex_mask = (struct sockaddr *)&ss;
   1961 			export.ex_masklen = ss.ss_len;
   1962 			break;
   1963 		default:
   1964 			syslog(LOG_ERR, "\"%s\", line %ld: Bad netgroup type",
   1965 			    line, (unsigned long)lineno);
   1966 			return (1);
   1967 		};
   1968 
   1969 		/*
   1970 		 * XXX:
   1971 		 * Maybe I should just use the fsb->f_mntonname path?
   1972 		 */
   1973 
   1974 		mel.mel_path = dirp;
   1975 		mel.mel_nexports = 1;
   1976 		mel.mel_exports = &export;
   1977 
   1978 		if (nfssvc(NFSSVC_SETEXPORTSLIST, &mel) != 0) {
   1979 			syslog(LOG_ERR,
   1980 	    "\"%s\", line %ld: Can't change attributes for %s to %s: %m",
   1981 			    line, (unsigned long)lineno,
   1982 			    dirp, (grp->gr_type == GT_HOST) ?
   1983 			    grp->gr_ptr.gt_addrinfo->ai_canonname :
   1984 			    (grp->gr_type == GT_NET) ?
   1985 			    grp->gr_ptr.gt_net.nt_name :
   1986 			    "Unknown");
   1987 			return (1);
   1988 		}
   1989 skip:
   1990 		if (addrp) {
   1991 			ai = ai->ai_next;
   1992 			if (ai == NULL)
   1993 				done = TRUE;
   1994 			else {
   1995 				addrp = ai->ai_addr;
   1996 				addrlen = ai->ai_addrlen;
   1997 			}
   1998 		} else
   1999 			done = TRUE;
   2000 	}
   2001 	return (0);
   2002 }
   2003 
   2004 /*
   2005  * Translate a net address.
   2006  */
   2007 static int
   2008 get_net(cp, net, maskflg)
   2009 	char *cp;
   2010 	struct netmsk *net;
   2011 	int maskflg;
   2012 {
   2013 	struct netent *np;
   2014 	char *nname, *p, *prefp;
   2015 	struct sockaddr_in sin, *sinp;
   2016 	struct sockaddr *sa;
   2017 	struct addrinfo hints, *ai = NULL;
   2018 	char netname[NI_MAXHOST];
   2019 	long preflen;
   2020 	int ecode;
   2021 
   2022 	(void)memset(&sin, 0, sizeof(sin));
   2023 	if ((opt_flags & OP_MASKLEN) && !maskflg) {
   2024 		p = strchr(cp, '/');
   2025 		*p = '\0';
   2026 		prefp = p + 1;
   2027 	} else {
   2028 		p = NULL;	/* XXXGCC -Wuninitialized */
   2029 		prefp = NULL;	/* XXXGCC -Wuninitialized */
   2030 	}
   2031 
   2032 	if ((np = getnetbyname(cp)) != NULL) {
   2033 		sin.sin_family = AF_INET;
   2034 		sin.sin_len = sizeof sin;
   2035 		sin.sin_addr = inet_makeaddr(np->n_net, 0);
   2036 		sa = (struct sockaddr *)&sin;
   2037 	} else if (isdigit((unsigned char)*cp)) {
   2038 		memset(&hints, 0, sizeof hints);
   2039 		hints.ai_family = AF_UNSPEC;
   2040 		hints.ai_flags = AI_NUMERICHOST;
   2041 		if (getaddrinfo(cp, NULL, &hints, &ai) != 0) {
   2042 			/*
   2043 			 * If getaddrinfo() failed, try the inet4 network
   2044 			 * notation with less than 3 dots.
   2045 			 */
   2046 			sin.sin_family = AF_INET;
   2047 			sin.sin_len = sizeof sin;
   2048 			sin.sin_addr = inet_makeaddr(inet_network(cp),0);
   2049 			if (debug)
   2050 				fprintf(stderr, "get_net: v4 addr %x\n",
   2051 				    sin.sin_addr.s_addr);
   2052 			sa = (struct sockaddr *)&sin;
   2053 		} else
   2054 			sa = ai->ai_addr;
   2055 	} else if (isxdigit((unsigned char)*cp) || *cp == ':') {
   2056 		memset(&hints, 0, sizeof hints);
   2057 		hints.ai_family = AF_UNSPEC;
   2058 		hints.ai_flags = AI_NUMERICHOST;
   2059 		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
   2060 			sa = ai->ai_addr;
   2061 		else
   2062 			goto fail;
   2063 	} else
   2064 		goto fail;
   2065 
   2066 	/*
   2067 	 * Only allow /pref notation for v6 addresses.
   2068 	 */
   2069 	if (sa->sa_family == AF_INET6 && (!(opt_flags & OP_MASKLEN) || maskflg))
   2070 		return 1;
   2071 
   2072 	ecode = getnameinfo(sa, sa->sa_len, netname, sizeof netname,
   2073 	    NULL, 0, ninumeric);
   2074 	if (ecode != 0)
   2075 		goto fail;
   2076 
   2077 	if (maskflg)
   2078 		net->nt_len = countones(sa);
   2079 	else {
   2080 		if (opt_flags & OP_MASKLEN) {
   2081 			errno = 0;
   2082 			preflen = strtol(prefp, NULL, 10);
   2083 			if (preflen == LONG_MIN && errno == ERANGE)
   2084 				goto fail;
   2085 			net->nt_len = (int)preflen;
   2086 			*p = '/';
   2087 		}
   2088 
   2089 		if (np)
   2090 			nname = np->n_name;
   2091 		else {
   2092 			if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
   2093 			    NULL, 0, ninumeric) != 0)
   2094 				strlcpy(netname, "?", sizeof(netname));
   2095 			nname = netname;
   2096 		}
   2097 		net->nt_name = estrdup(nname);
   2098 		memcpy(&net->nt_net, sa, sa->sa_len);
   2099 	}
   2100 
   2101 	if (!maskflg && sa->sa_family == AF_INET &&
   2102 	    !(opt_flags & (OP_MASK|OP_MASKLEN))) {
   2103 		sinp = (struct sockaddr_in *)sa;
   2104 		if (IN_CLASSA(sinp->sin_addr.s_addr))
   2105 			net->nt_len = 8;
   2106 		else if (IN_CLASSB(sinp->sin_addr.s_addr))
   2107 			net->nt_len = 16;
   2108 		else if (IN_CLASSC(sinp->sin_addr.s_addr))
   2109 			net->nt_len = 24;
   2110 		else if (IN_CLASSD(sinp->sin_addr.s_addr))
   2111 			net->nt_len = 28;
   2112 		else
   2113 			net->nt_len = 32;	/* XXX */
   2114 	}
   2115 
   2116 	if (ai)
   2117 		freeaddrinfo(ai);
   2118 	return 0;
   2119 
   2120 fail:
   2121 	if (ai)
   2122 		freeaddrinfo(ai);
   2123 	return 1;
   2124 }
   2125 
   2126 /*
   2127  * Parse out the next white space separated field
   2128  */
   2129 static void
   2130 nextfield(cp, endcp)
   2131 	char **cp;
   2132 	char **endcp;
   2133 {
   2134 	char *p;
   2135 
   2136 	p = *cp;
   2137 	while (*p == ' ' || *p == '\t')
   2138 		p++;
   2139 	if (*p == '\n' || *p == '\0')
   2140 		*cp = *endcp = p;
   2141 	else {
   2142 		*cp = p++;
   2143 		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
   2144 			p++;
   2145 		*endcp = p;
   2146 	}
   2147 }
   2148 
   2149 /*
   2150  * Parse a description of a credential.
   2151  */
   2152 static void
   2153 parsecred(namelist, cr)
   2154 	char *namelist;
   2155 	struct uucred *cr;
   2156 {
   2157 	char *username;
   2158 	int cnt;
   2159 	char *names;
   2160 	struct passwd *pw;
   2161 	struct group *gr;
   2162 	int ngroups;
   2163 	gid_t usergroups[NGROUPS + 1];
   2164 
   2165 	/*
   2166 	 * Set up the unprivileged user.
   2167 	 */
   2168 	*cr = def_anon;
   2169 	/*
   2170 	 * Get the user's password table entry.
   2171 	 */
   2172 	names = strsep(&namelist, " \t\n");
   2173 	username = strsep(&names, ":");
   2174 	if (isdigit((unsigned char)*username) || *username == '-')
   2175 		pw = getpwuid(atoi(username));
   2176 	else
   2177 		pw = getpwnam(username);
   2178 	/*
   2179 	 * Credentials specified as those of a user.
   2180 	 */
   2181 	if (names == NULL) {
   2182 		if (pw == NULL) {
   2183 			syslog(LOG_ERR, "Unknown user: %s", username);
   2184 			return;
   2185 		}
   2186 		cr->cr_uid = pw->pw_uid;
   2187 		ngroups = NGROUPS + 1;
   2188 		if (getgrouplist(pw->pw_name, pw->pw_gid, usergroups, &ngroups))
   2189 			syslog(LOG_ERR, "Too many groups for user %s", username);
   2190 		/*
   2191 		 * Convert from int's to gid_t's and compress out duplicate
   2192 		 */
   2193 		cr->cr_ngroups = ngroups - 1;
   2194 		cr->cr_gid = usergroups[0];
   2195 		for (cnt = 1; cnt < ngroups; cnt++)
   2196 			cr->cr_groups[cnt - 1] = usergroups[cnt];
   2197 		return;
   2198 	}
   2199 	/*
   2200 	 * Explicit credential specified as a colon separated list:
   2201 	 *	uid:gid:gid:...
   2202 	 */
   2203 	if (pw != NULL)
   2204 		cr->cr_uid = pw->pw_uid;
   2205 	else if (isdigit((unsigned char)*username) || *username == '-')
   2206 		cr->cr_uid = atoi(username);
   2207 	else {
   2208 		syslog(LOG_ERR, "Unknown user: %s", username);
   2209 		return;
   2210 	}
   2211 	cr->cr_ngroups = 0;
   2212 	while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
   2213 		username = strsep(&names, ":");
   2214 		if (isdigit((unsigned char)*username) || *username == '-') {
   2215 			cr->cr_groups[cr->cr_ngroups++] = atoi(username);
   2216 		} else {
   2217 			if ((gr = getgrnam(username)) == NULL) {
   2218 				syslog(LOG_ERR, "Unknown group: %s", username);
   2219 				continue;
   2220 			}
   2221 			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
   2222 		}
   2223 	}
   2224 	if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
   2225 		syslog(LOG_ERR, "Too many groups");
   2226 }
   2227 
   2228 #define	STRSIZ	(RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
   2229 /*
   2230  * Routines that maintain the remote mounttab
   2231  */
   2232 static void
   2233 get_mountlist()
   2234 {
   2235 	struct mountlist *mlp, **mlpp;
   2236 	char *host, *dirp, *cp;
   2237 	char str[STRSIZ];
   2238 	FILE *mlfile;
   2239 
   2240 	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
   2241 		syslog(LOG_ERR, "Can't open %s: %m", _PATH_RMOUNTLIST);
   2242 		return;
   2243 	}
   2244 	mlpp = &mlhead;
   2245 	while (fgets(str, STRSIZ, mlfile) != NULL) {
   2246 		cp = str;
   2247 		host = strsep(&cp, " \t\n");
   2248 		dirp = strsep(&cp, " \t\n");
   2249 		if (host == NULL || dirp == NULL)
   2250 			continue;
   2251 		mlp = emalloc(sizeof(*mlp));
   2252 		(void)strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
   2253 		mlp->ml_host[RPCMNT_NAMELEN] = '\0';
   2254 		(void)strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
   2255 		mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
   2256 		mlp->ml_next = NULL;
   2257 		*mlpp = mlp;
   2258 		mlpp = &mlp->ml_next;
   2259 	}
   2260 	(void)fclose(mlfile);
   2261 }
   2262 
   2263 static int
   2264 del_mlist(hostp, dirp, saddr)
   2265 	char *hostp, *dirp;
   2266 	struct sockaddr *saddr;
   2267 {
   2268 	struct mountlist *mlp, **mlpp;
   2269 	struct mountlist *mlp2;
   2270 	u_short sport;
   2271 	FILE *mlfile;
   2272 	int fnd = 0, ret = 0;
   2273 	char host[NI_MAXHOST];
   2274 
   2275 	switch (saddr->sa_family) {
   2276 	case AF_INET6:
   2277 		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
   2278 		break;
   2279 	case AF_INET:
   2280 		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
   2281 		break;
   2282 	default:
   2283 		return -1;
   2284 	}
   2285 	mlpp = &mlhead;
   2286 	mlp = mlhead;
   2287 	while (mlp) {
   2288 		if (!strcmp(mlp->ml_host, hostp) &&
   2289 		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
   2290 			if (!(mlp->ml_flag & DP_NORESMNT) &&
   2291 			    sport >= IPPORT_RESERVED) {
   2292 				if (getnameinfo(saddr, saddr->sa_len, host,
   2293 				    sizeof host, NULL, 0, ninumeric) != 0)
   2294 					strlcpy(host, "?", sizeof(host));
   2295 				syslog(LOG_NOTICE,
   2296 				"Umount request for %s:%s from %s refused\n",
   2297 				    mlp->ml_host, mlp->ml_dirp, host);
   2298 				ret = -1;
   2299 				goto cont;
   2300 			}
   2301 			fnd = 1;
   2302 			mlp2 = mlp;
   2303 			*mlpp = mlp = mlp->ml_next;
   2304 			free(mlp2);
   2305 		} else {
   2306 cont:
   2307 			mlpp = &mlp->ml_next;
   2308 			mlp = mlp->ml_next;
   2309 		}
   2310 	}
   2311 	if (fnd) {
   2312 		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
   2313 			syslog(LOG_ERR, "Can't update %s: %m",
   2314 			    _PATH_RMOUNTLIST);
   2315 			return ret;
   2316 		}
   2317 		mlp = mlhead;
   2318 		while (mlp) {
   2319 			(void)fprintf(mlfile, "%s %s\n", mlp->ml_host,
   2320 		            mlp->ml_dirp);
   2321 			mlp = mlp->ml_next;
   2322 		}
   2323 		(void)fclose(mlfile);
   2324 	}
   2325 	return ret;
   2326 }
   2327 
   2328 static void
   2329 add_mlist(hostp, dirp, flags)
   2330 	char *hostp, *dirp;
   2331 	int flags;
   2332 {
   2333 	struct mountlist *mlp, **mlpp;
   2334 	FILE *mlfile;
   2335 
   2336 	mlpp = &mlhead;
   2337 	mlp = mlhead;
   2338 	while (mlp) {
   2339 		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
   2340 			return;
   2341 		mlpp = &mlp->ml_next;
   2342 		mlp = mlp->ml_next;
   2343 	}
   2344 	mlp = emalloc(sizeof(*mlp));
   2345 	strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
   2346 	mlp->ml_host[RPCMNT_NAMELEN] = '\0';
   2347 	strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
   2348 	mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
   2349 	mlp->ml_flag = flags;
   2350 	mlp->ml_next = NULL;
   2351 	*mlpp = mlp;
   2352 	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
   2353 		syslog(LOG_ERR, "Can't update %s: %m", _PATH_RMOUNTLIST);
   2354 		return;
   2355 	}
   2356 	(void)fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
   2357 	(void)fclose(mlfile);
   2358 }
   2359 
   2360 /*
   2361  * This function is called via. SIGTERM when the system is going down.
   2362  * It sends a broadcast RPCMNT_UMNTALL.
   2363  */
   2364 /* ARGSUSED */
   2365 static void
   2366 send_umntall(n)
   2367 	int n;
   2368 {
   2369 	(void)clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
   2370 	    (xdrproc_t)xdr_void, NULL, (xdrproc_t)xdr_void, NULL,
   2371 	    (resultproc_t)umntall_each);
   2372 	exit(0);
   2373 }
   2374 
   2375 static int
   2376 umntall_each(resultsp, raddr)
   2377 	caddr_t resultsp;
   2378 	struct sockaddr_in *raddr;
   2379 {
   2380 	return (1);
   2381 }
   2382 
   2383 /*
   2384  * Free up a group list.
   2385  */
   2386 static void
   2387 free_grp(grp)
   2388 	struct grouplist *grp;
   2389 {
   2390 
   2391 	if (grp->gr_type == GT_HOST) {
   2392 		if (grp->gr_ptr.gt_addrinfo != NULL)
   2393 			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
   2394 	} else if (grp->gr_type == GT_NET) {
   2395 		if (grp->gr_ptr.gt_net.nt_name)
   2396 			free(grp->gr_ptr.gt_net.nt_name);
   2397 	}
   2398 	free(grp);
   2399 }
   2400 
   2401 #if 0
   2402 static void
   2403 SYSLOG(int pri, const char *fmt,...)
   2404 {
   2405 	va_list ap;
   2406 
   2407 	va_start(ap, fmt);
   2408 
   2409 	if (debug)
   2410 		vfprintf(stderr, fmt, ap);
   2411 	else
   2412 		vsyslog(pri, fmt, ap);
   2413 
   2414 	va_end(ap);
   2415 }
   2416 #endif
   2417 
   2418 /*
   2419  * Check options for consistency.
   2420  */
   2421 static int
   2422 check_options(line, lineno, dp)
   2423 	const char *line;
   2424 	size_t lineno;
   2425 	struct dirlist *dp;
   2426 {
   2427 
   2428 	if (dp == NULL) {
   2429 		syslog(LOG_ERR,
   2430 		    "\"%s\", line %ld: missing directory list",
   2431 		    line, (unsigned long)lineno);
   2432 		return (1);
   2433 	}
   2434 	if ((opt_flags & (OP_MAPROOT|OP_MAPALL)) == (OP_MAPROOT|OP_MAPALL) ||
   2435 	    (opt_flags & (OP_MAPROOT|OP_KERB)) == (OP_MAPROOT|OP_KERB) ||
   2436 	    (opt_flags & (OP_MAPALL|OP_KERB)) == (OP_MAPALL|OP_KERB)) {
   2437 		syslog(LOG_ERR,
   2438 		    "\"%s\", line %ld: -mapall, -maproot and -kerb mutually exclusive",
   2439 		    line, (unsigned long)lineno);
   2440 		return (1);
   2441 	}
   2442 	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
   2443 		syslog(LOG_ERR, "\"%s\", line %ld: -mask requires -net",
   2444 		    line, (unsigned long)lineno);
   2445 		return (1);
   2446 	}
   2447 	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN) != 0) {
   2448 		syslog(LOG_ERR, "\"%s\", line %ld: /pref and -mask mutually"
   2449 		    " exclusive",
   2450 		    line, (unsigned long)lineno);
   2451 		return (1);
   2452 	}
   2453 	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
   2454 		syslog(LOG_ERR,
   2455 		    "\"%s\", line %ld: -alldirs has multiple directories",
   2456 		    line, (unsigned long)lineno);
   2457 		return (1);
   2458 	}
   2459 	return (0);
   2460 }
   2461 
   2462 /*
   2463  * Check an absolute directory path for any symbolic links. Return true
   2464  * if no symbolic links are found.
   2465  */
   2466 static int
   2467 check_dirpath(line, lineno, dirp)
   2468 	const char *line;
   2469 	size_t lineno;
   2470 	char *dirp;
   2471 {
   2472 	char *cp;
   2473 	struct stat sb;
   2474 	const char *file = "";
   2475 
   2476 	for (cp = dirp + 1; *cp; cp++) {
   2477 		if (*cp == '/') {
   2478 			*cp = '\0';
   2479 			if (lstat(dirp, &sb) == -1)
   2480 				goto bad;
   2481 			if (!S_ISDIR(sb.st_mode))
   2482 				goto bad1;
   2483 			*cp = '/';
   2484 		}
   2485 	}
   2486 
   2487 	cp = NULL;
   2488 	if (lstat(dirp, &sb) == -1)
   2489 		goto bad;
   2490 
   2491 	if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) {
   2492 		file = " file or a";
   2493 		goto bad1;
   2494 	}
   2495 
   2496 	return 1;
   2497 
   2498 bad:
   2499 	syslog(LOG_ERR,
   2500 	    "\"%s\", line %ld: lstat for `%s' failed: %m",
   2501 	    line, (unsigned long)lineno, dirp);
   2502 	if (cp)
   2503 		*cp = '/';
   2504 	return 0;
   2505 
   2506 bad1:
   2507 	syslog(LOG_ERR,
   2508 	    "\"%s\", line %ld: `%s' is not a%s directory",
   2509 	    line, (unsigned long)lineno, dirp, file);
   2510 	if (cp)
   2511 		*cp = '/';
   2512 	return 0;
   2513 }
   2514 
   2515 static void
   2516 bind_resv_port(int sock, sa_family_t family, in_port_t port)
   2517 {
   2518 	struct sockaddr *sa;
   2519 	struct sockaddr_in sasin;
   2520 	struct sockaddr_in6 sasin6;
   2521 
   2522 	switch (family) {
   2523 	case AF_INET:
   2524 		(void)memset(&sasin, 0, sizeof(sasin));
   2525 		sasin.sin_len = sizeof(sasin);
   2526 		sasin.sin_family = family;
   2527 		sasin.sin_port = htons(port);
   2528 		sa = (struct sockaddr *)(void *)&sasin;
   2529 		break;
   2530 	case AF_INET6:
   2531 		(void)memset(&sasin6, 0, sizeof(sasin6));
   2532 		sasin6.sin6_len = sizeof(sasin6);
   2533 		sasin6.sin6_family = family;
   2534 		sasin6.sin6_port = htons(port);
   2535 		sa = (struct sockaddr *)(void *)&sasin6;
   2536 		break;
   2537 	default:
   2538 		syslog(LOG_ERR, "Unsupported address family %d", family);
   2539 		return;
   2540 	}
   2541 	if (bindresvport_sa(sock, sa) == -1)
   2542 		syslog(LOG_ERR, "Cannot bind to reserved port %d (%m)", port);
   2543 }
   2544 
   2545 /* ARGSUSED */
   2546 static void
   2547 no_nfs(int sig)
   2548 {
   2549 	syslog(LOG_ERR, "kernel NFS support not present; exiting");
   2550 	exit(1);
   2551 }
   2552