mountd.c revision 9840df76dd6c45b338a52356a8695450471c46af
1/*-
2 * SPDX-License-Identifier: BSD-3-Clause
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#ifndef lint
36static const char copyright[] =
37"@(#) Copyright (c) 1989, 1993\n\
38	The Regents of the University of California.  All rights reserved.\n";
39#endif /*not lint*/
40
41#if 0
42#ifndef lint
43static char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95";
44#endif /*not lint*/
45#endif
46
47#include <sys/cdefs.h>
48__FBSDID("$FreeBSD$");
49
50#include <sys/param.h>
51#include <sys/fcntl.h>
52#include <sys/linker.h>
53#include <sys/module.h>
54#include <sys/mount.h>
55#include <sys/queue.h>
56#include <sys/stat.h>
57#include <sys/sysctl.h>
58#include <sys/syslog.h>
59
60#include <rpc/rpc.h>
61#include <rpc/rpc_com.h>
62#include <rpc/pmap_clnt.h>
63#include <rpc/pmap_prot.h>
64#include <rpcsvc/mount.h>
65#include <nfs/nfsproto.h>
66#include <nfs/nfssvc.h>
67#include <nfsserver/nfs.h>
68
69#include <fs/nfs/nfsport.h>
70
71#include <arpa/inet.h>
72
73#include <ctype.h>
74#include <err.h>
75#include <errno.h>
76#include <grp.h>
77#include <libutil.h>
78#include <limits.h>
79#include <netdb.h>
80#include <pwd.h>
81#include <signal.h>
82#include <stdio.h>
83#include <stdlib.h>
84#include <string.h>
85#include <unistd.h>
86#include "pathnames.h"
87#include "mntopts.h"
88
89#ifdef DEBUG
90#include <stdarg.h>
91#endif
92
93/*
94 * Structures for keeping the mount list and export list
95 */
96struct mountlist {
97	char	ml_host[MNTNAMLEN+1];
98	char	ml_dirp[MNTPATHLEN+1];
99
100	SLIST_ENTRY(mountlist)	next;
101};
102
103struct dirlist {
104	struct dirlist	*dp_left;
105	struct dirlist	*dp_right;
106	int		dp_flag;
107	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
108	char		*dp_dirp;
109};
110/* dp_flag bits */
111#define	DP_DEFSET	0x1
112#define DP_HOSTSET	0x2
113
114struct exportlist {
115	struct dirlist	*ex_dirl;
116	struct dirlist	*ex_defdir;
117	int		ex_flag;
118	fsid_t		ex_fs;
119	char		*ex_fsdir;
120	char		*ex_indexfile;
121	int		ex_numsecflavors;
122	int		ex_secflavors[MAXSECFLAVORS];
123	int		ex_defnumsecflavors;
124	int		ex_defsecflavors[MAXSECFLAVORS];
125
126	SLIST_ENTRY(exportlist) entries;
127};
128/* ex_flag bits */
129#define	EX_LINKED	0x1
130
131struct netmsk {
132	struct sockaddr_storage nt_net;
133	struct sockaddr_storage nt_mask;
134	char		*nt_name;
135};
136
137union grouptypes {
138	struct addrinfo *gt_addrinfo;
139	struct netmsk	gt_net;
140};
141
142struct grouplist {
143	int gr_type;
144	union grouptypes gr_ptr;
145	struct grouplist *gr_next;
146	int gr_numsecflavors;
147	int gr_secflavors[MAXSECFLAVORS];
148};
149/* Group types */
150#define	GT_NULL		0x0
151#define	GT_HOST		0x1
152#define	GT_NET		0x2
153#define	GT_DEFAULT	0x3
154#define GT_IGNORE	0x5
155
156struct hostlist {
157	int		 ht_flag;	/* Uses DP_xx bits */
158	struct grouplist *ht_grp;
159	struct hostlist	 *ht_next;
160};
161
162struct fhreturn {
163	int	fhr_flag;
164	int	fhr_vers;
165	nfsfh_t	fhr_fh;
166	int	fhr_numsecflavors;
167	int	*fhr_secflavors;
168};
169
170#define	GETPORT_MAXTRY	20	/* Max tries to get a port # */
171
172/* Global defs */
173static char	*add_expdir(struct dirlist **, char *, int);
174static void	add_dlist(struct dirlist **, struct dirlist *,
175		    struct grouplist *, int, struct exportlist *);
176static void	add_mlist(char *, char *);
177static int	check_dirpath(char *);
178static int	check_options(struct dirlist *);
179static int	checkmask(struct sockaddr *sa);
180static int	chk_host(struct dirlist *, struct sockaddr *, int *, int *,
181		    int *, int **);
182static char	*strsep_quote(char **stringp, const char *delim);
183static int	create_service(struct netconfig *nconf);
184static void	complete_service(struct netconfig *nconf, char *port_str);
185static void	clearout_service(void);
186static void	del_mlist(char *hostp, char *dirp);
187static struct dirlist	*dirp_search(struct dirlist *, char *);
188static int	do_mount(struct exportlist *, struct grouplist *, int,
189		    struct xucred *, char *, int, struct statfs *);
190static int	do_opt(char **, char **, struct exportlist *,
191		    struct grouplist *, int *, int *, struct xucred *);
192static struct exportlist	*ex_search(fsid_t *);
193static struct exportlist	*get_exp(void);
194static void	free_dir(struct dirlist *);
195static void	free_exp(struct exportlist *);
196static void	free_grp(struct grouplist *);
197static void	free_host(struct hostlist *);
198static void	get_exportlist(void);
199static int	get_host(char *, struct grouplist *, struct grouplist *);
200static struct hostlist *get_ht(void);
201static int	get_line(void);
202static void	get_mountlist(void);
203static int	get_net(char *, struct netmsk *, int);
204static void	getexp_err(struct exportlist *, struct grouplist *, const char *);
205static struct grouplist	*get_grp(void);
206static void	hang_dirp(struct dirlist *, struct grouplist *,
207				struct exportlist *, int);
208static void	huphandler(int sig);
209static int	makemask(struct sockaddr_storage *ssp, int bitlen);
210static void	mntsrv(struct svc_req *, SVCXPRT *);
211static void	nextfield(char **, char **);
212static void	out_of_mem(void);
213static void	parsecred(char *, struct xucred *);
214static int	parsesec(char *, struct exportlist *);
215static int	put_exlist(struct dirlist *, XDR *, struct dirlist *,
216		    int *, int);
217static void	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
218static int	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
219		    struct sockaddr *samask);
220static int	scan_tree(struct dirlist *, struct sockaddr *);
221static void	usage(void);
222static int	xdr_dir(XDR *, char *);
223static int	xdr_explist(XDR *, caddr_t);
224static int	xdr_explist_brief(XDR *, caddr_t);
225static int	xdr_explist_common(XDR *, caddr_t, int);
226static int	xdr_fhs(XDR *, caddr_t);
227static int	xdr_mlist(XDR *, caddr_t);
228static void	terminate(int);
229
230static SLIST_HEAD(, exportlist) exphead = SLIST_HEAD_INITIALIZER(exphead);
231static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(mlhead);
232static struct grouplist *grphead;
233static char *exnames_default[2] = { _PATH_EXPORTS, NULL };
234static char **exnames;
235static char **hosts = NULL;
236static struct xucred def_anon = {
237	XUCRED_VERSION,
238	(uid_t)65534,
239	1,
240	{ (gid_t)65533 },
241	NULL
242};
243static int force_v2 = 0;
244static int resvport_only = 1;
245static int nhosts = 0;
246static int dir_only = 1;
247static int dolog = 0;
248static int got_sighup = 0;
249static int xcreated = 0;
250
251static char *svcport_str = NULL;
252static int mallocd_svcport = 0;
253static int *sock_fd;
254static int sock_fdcnt;
255static int sock_fdpos;
256static int suspend_nfsd = 0;
257
258static int opt_flags;
259static int have_v6 = 1;
260
261static int v4root_phase = 0;
262static char v4root_dirpath[PATH_MAX + 1];
263static int has_publicfh = 0;
264
265static struct pidfh *pfh = NULL;
266/* Bits for opt_flags above */
267#define	OP_MAPROOT	0x01
268#define	OP_MAPALL	0x02
269/* 0x4 free */
270#define	OP_MASK		0x08
271#define	OP_NET		0x10
272#define	OP_ALLDIRS	0x40
273#define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
274#define	OP_QUIET	0x100
275#define OP_MASKLEN	0x200
276#define OP_SEC		0x400
277
278#ifdef DEBUG
279static int debug = 1;
280static void	SYSLOG(int, const char *, ...) __printflike(2, 3);
281#define syslog SYSLOG
282#else
283static int debug = 0;
284#endif
285
286/*
287 * Similar to strsep(), but it allows for quoted strings
288 * and escaped characters.
289 *
290 * It returns the string (or NULL, if *stringp is NULL),
291 * which is a de-quoted version of the string if necessary.
292 *
293 * It modifies *stringp in place.
294 */
295static char *
296strsep_quote(char **stringp, const char *delim)
297{
298	char *srcptr, *dstptr, *retval;
299	char quot = 0;
300
301	if (stringp == NULL || *stringp == NULL)
302		return (NULL);
303
304	srcptr = dstptr = retval = *stringp;
305
306	while (*srcptr) {
307		/*
308		 * We're looking for several edge cases here.
309		 * First:  if we're in quote state (quot != 0),
310		 * then we ignore the delim characters, but otherwise
311		 * process as normal, unless it is the quote character.
312		 * Second:  if the current character is a backslash,
313		 * we take the next character as-is, without checking
314		 * for delim, quote, or backslash.  Exception:  if the
315		 * next character is a NUL, that's the end of the string.
316		 * Third:  if the character is a quote character, we toggle
317		 * quote state.
318		 * Otherwise:  check the current character for NUL, or
319		 * being in delim, and end the string if either is true.
320		 */
321		if (*srcptr == '\\') {
322			srcptr++;
323			/*
324			 * The edge case here is if the next character
325			 * is NUL, we want to stop processing.  But if
326			 * it's not NUL, then we simply want to copy it.
327			 */
328			if (*srcptr) {
329				*dstptr++ = *srcptr++;
330			}
331			continue;
332		}
333		if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) {
334			quot = *srcptr++;
335			continue;
336		}
337		if (quot && *srcptr == quot) {
338			/* End of the quoted part */
339			quot = 0;
340			srcptr++;
341			continue;
342		}
343		if (!quot && strchr(delim, *srcptr))
344			break;
345		*dstptr++ = *srcptr++;
346	}
347
348	*dstptr = 0; /* Terminate the string */
349	*stringp = (*srcptr == '\0') ? NULL : srcptr + 1;
350	return (retval);
351}
352
353/*
354 * Mountd server for NFS mount protocol as described in:
355 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
356 * The optional arguments are the exports file name
357 * default: _PATH_EXPORTS
358 * and "-n" to allow nonroot mount.
359 */
360int
361main(int argc, char **argv)
362{
363	fd_set readfds;
364	struct netconfig *nconf;
365	char *endptr, **hosts_bak;
366	void *nc_handle;
367	pid_t otherpid;
368	in_port_t svcport;
369	int c, k, s;
370	int maxrec = RPC_MAXDATASIZE;
371	int attempt_cnt, port_len, port_pos, ret;
372	char **port_list;
373
374	/* Check that another mountd isn't already running. */
375	pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
376	if (pfh == NULL) {
377		if (errno == EEXIST)
378			errx(1, "mountd already running, pid: %d.", otherpid);
379		warn("cannot open or create pidfile");
380	}
381
382	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
383	if (s < 0)
384		have_v6 = 0;
385	else
386		close(s);
387
388	while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1)
389		switch (c) {
390		case '2':
391			force_v2 = 1;
392			break;
393		case 'e':
394			/* now a no-op, since this is the default */
395			break;
396		case 'n':
397			resvport_only = 0;
398			break;
399		case 'r':
400			dir_only = 0;
401			break;
402		case 'd':
403			debug = debug ? 0 : 1;
404			break;
405		case 'l':
406			dolog = 1;
407			break;
408		case 'p':
409			endptr = NULL;
410			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
411			if (endptr == NULL || *endptr != '\0' ||
412			    svcport == 0 || svcport >= IPPORT_MAX)
413				usage();
414			svcport_str = strdup(optarg);
415			break;
416		case 'h':
417			++nhosts;
418			hosts_bak = hosts;
419			hosts_bak = realloc(hosts, nhosts * sizeof(char *));
420			if (hosts_bak == NULL) {
421				if (hosts != NULL) {
422					for (k = 0; k < nhosts; k++)
423						free(hosts[k]);
424					free(hosts);
425					out_of_mem();
426				}
427			}
428			hosts = hosts_bak;
429			hosts[nhosts - 1] = strdup(optarg);
430			if (hosts[nhosts - 1] == NULL) {
431				for (k = 0; k < (nhosts - 1); k++)
432					free(hosts[k]);
433				free(hosts);
434				out_of_mem();
435			}
436			break;
437		case 'S':
438			suspend_nfsd = 1;
439			break;
440		default:
441			usage();
442		}
443
444	if (modfind("nfsd") < 0) {
445		/* Not present in kernel, try loading it */
446		if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
447			errx(1, "NFS server is not available");
448	}
449
450	argc -= optind;
451	argv += optind;
452	grphead = (struct grouplist *)NULL;
453	if (argc > 0)
454		exnames = argv;
455	else
456		exnames = exnames_default;
457	openlog("mountd", LOG_PID, LOG_DAEMON);
458	if (debug)
459		warnx("getting export list");
460	get_exportlist();
461	if (debug)
462		warnx("getting mount list");
463	get_mountlist();
464	if (debug)
465		warnx("here we go");
466	if (debug == 0) {
467		daemon(0, 0);
468		signal(SIGINT, SIG_IGN);
469		signal(SIGQUIT, SIG_IGN);
470	}
471	signal(SIGHUP, huphandler);
472	signal(SIGTERM, terminate);
473	signal(SIGPIPE, SIG_IGN);
474
475	pidfile_write(pfh);
476
477	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
478	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
479	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
480
481	if (!resvport_only) {
482		if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL,
483		    &resvport_only, sizeof(resvport_only)) != 0 &&
484		    errno != ENOENT) {
485			syslog(LOG_ERR, "sysctl: %m");
486			exit(1);
487		}
488	}
489
490	/*
491	 * If no hosts were specified, add a wildcard entry to bind to
492	 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
493	 * list.
494	 */
495	if (nhosts == 0) {
496		hosts = malloc(sizeof(char *));
497		if (hosts == NULL)
498			out_of_mem();
499		hosts[0] = "*";
500		nhosts = 1;
501	} else {
502		hosts_bak = hosts;
503		if (have_v6) {
504			hosts_bak = realloc(hosts, (nhosts + 2) *
505			    sizeof(char *));
506			if (hosts_bak == NULL) {
507				for (k = 0; k < nhosts; k++)
508					free(hosts[k]);
509		    		free(hosts);
510		    		out_of_mem();
511			} else
512				hosts = hosts_bak;
513			nhosts += 2;
514			hosts[nhosts - 2] = "::1";
515		} else {
516			hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
517			if (hosts_bak == NULL) {
518				for (k = 0; k < nhosts; k++)
519					free(hosts[k]);
520				free(hosts);
521				out_of_mem();
522			} else {
523				nhosts += 1;
524				hosts = hosts_bak;
525			}
526		}
527
528		hosts[nhosts - 1] = "127.0.0.1";
529	}
530
531	attempt_cnt = 1;
532	sock_fdcnt = 0;
533	sock_fd = NULL;
534	port_list = NULL;
535	port_len = 0;
536	nc_handle = setnetconfig();
537	while ((nconf = getnetconfig(nc_handle))) {
538		if (nconf->nc_flag & NC_VISIBLE) {
539			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
540			    "inet6") == 0) {
541				/* DO NOTHING */
542			} else {
543				ret = create_service(nconf);
544				if (ret == 1)
545					/* Ignore this call */
546					continue;
547				if (ret < 0) {
548					/*
549					 * Failed to bind port, so close off
550					 * all sockets created and try again
551					 * if the port# was dynamically
552					 * assigned via bind(2).
553					 */
554					clearout_service();
555					if (mallocd_svcport != 0 &&
556					    attempt_cnt < GETPORT_MAXTRY) {
557						free(svcport_str);
558						svcport_str = NULL;
559						mallocd_svcport = 0;
560					} else {
561						errno = EADDRINUSE;
562						syslog(LOG_ERR,
563						    "bindresvport_sa: %m");
564						exit(1);
565					}
566
567					/* Start over at the first service. */
568					free(sock_fd);
569					sock_fdcnt = 0;
570					sock_fd = NULL;
571					nc_handle = setnetconfig();
572					attempt_cnt++;
573				} else if (mallocd_svcport != 0 &&
574				    attempt_cnt == GETPORT_MAXTRY) {
575					/*
576					 * For the last attempt, allow
577					 * different port #s for each nconf
578					 * by saving the svcport_str and
579					 * setting it back to NULL.
580					 */
581					port_list = realloc(port_list,
582					    (port_len + 1) * sizeof(char *));
583					if (port_list == NULL)
584						out_of_mem();
585					port_list[port_len++] = svcport_str;
586					svcport_str = NULL;
587					mallocd_svcport = 0;
588				}
589			}
590		}
591	}
592
593	/*
594	 * Successfully bound the ports, so call complete_service() to
595	 * do the rest of the setup on the service(s).
596	 */
597	sock_fdpos = 0;
598	port_pos = 0;
599	nc_handle = setnetconfig();
600	while ((nconf = getnetconfig(nc_handle))) {
601		if (nconf->nc_flag & NC_VISIBLE) {
602			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
603			    "inet6") == 0) {
604				/* DO NOTHING */
605			} else if (port_list != NULL) {
606				if (port_pos >= port_len) {
607					syslog(LOG_ERR, "too many port#s");
608					exit(1);
609				}
610				complete_service(nconf, port_list[port_pos++]);
611			} else
612				complete_service(nconf, svcport_str);
613		}
614	}
615	endnetconfig(nc_handle);
616	free(sock_fd);
617	if (port_list != NULL) {
618		for (port_pos = 0; port_pos < port_len; port_pos++)
619			free(port_list[port_pos]);
620		free(port_list);
621	}
622
623	if (xcreated == 0) {
624		syslog(LOG_ERR, "could not create any services");
625		exit(1);
626	}
627
628	/* Expand svc_run() here so that we can call get_exportlist(). */
629	for (;;) {
630		if (got_sighup) {
631			get_exportlist();
632			got_sighup = 0;
633		}
634		readfds = svc_fdset;
635		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
636		case -1:
637			if (errno == EINTR)
638                                continue;
639			syslog(LOG_ERR, "mountd died: select: %m");
640			exit(1);
641		case 0:
642			continue;
643		default:
644			svc_getreqset(&readfds);
645		}
646	}
647}
648
649/*
650 * This routine creates and binds sockets on the appropriate
651 * addresses. It gets called one time for each transport.
652 * It returns 0 upon success, 1 for ingore the call and -1 to indicate
653 * bind failed with EADDRINUSE.
654 * Any file descriptors that have been created are stored in sock_fd and
655 * the total count of them is maintained in sock_fdcnt.
656 */
657static int
658create_service(struct netconfig *nconf)
659{
660	struct addrinfo hints, *res = NULL;
661	struct sockaddr_in *sin;
662	struct sockaddr_in6 *sin6;
663	struct __rpc_sockinfo si;
664	int aicode;
665	int fd;
666	int nhostsbak;
667	int one = 1;
668	int r;
669	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
670	int mallocd_res;
671
672	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
673	    (nconf->nc_semantics != NC_TPI_COTS) &&
674	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
675		return (1);	/* not my type */
676
677	/*
678	 * XXX - using RPC library internal functions.
679	 */
680	if (!__rpc_nconf2sockinfo(nconf, &si)) {
681		syslog(LOG_ERR, "cannot get information for %s",
682		    nconf->nc_netid);
683		return (1);
684	}
685
686	/* Get mountd's address on this transport */
687	memset(&hints, 0, sizeof hints);
688	hints.ai_family = si.si_af;
689	hints.ai_socktype = si.si_socktype;
690	hints.ai_protocol = si.si_proto;
691
692	/*
693	 * Bind to specific IPs if asked to
694	 */
695	nhostsbak = nhosts;
696	while (nhostsbak > 0) {
697		--nhostsbak;
698		sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
699		if (sock_fd == NULL)
700			out_of_mem();
701		sock_fd[sock_fdcnt++] = -1;	/* Set invalid for now. */
702		mallocd_res = 0;
703
704		hints.ai_flags = AI_PASSIVE;
705
706		/*
707		 * XXX - using RPC library internal functions.
708		 */
709		if ((fd = __rpc_nconf2fd(nconf)) < 0) {
710			int non_fatal = 0;
711	    		if (errno == EAFNOSUPPORT &&
712			    nconf->nc_semantics != NC_TPI_CLTS)
713				non_fatal = 1;
714
715			syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
716			    "cannot create socket for %s", nconf->nc_netid);
717			if (non_fatal != 0)
718				continue;
719			exit(1);
720		}
721
722		switch (hints.ai_family) {
723		case AF_INET:
724			if (inet_pton(AF_INET, hosts[nhostsbak],
725			    host_addr) == 1) {
726				hints.ai_flags |= AI_NUMERICHOST;
727			} else {
728				/*
729				 * Skip if we have an AF_INET6 address.
730				 */
731				if (inet_pton(AF_INET6, hosts[nhostsbak],
732				    host_addr) == 1) {
733					close(fd);
734					continue;
735				}
736			}
737			break;
738		case AF_INET6:
739			if (inet_pton(AF_INET6, hosts[nhostsbak],
740			    host_addr) == 1) {
741				hints.ai_flags |= AI_NUMERICHOST;
742			} else {
743				/*
744				 * Skip if we have an AF_INET address.
745				 */
746				if (inet_pton(AF_INET, hosts[nhostsbak],
747				    host_addr) == 1) {
748					close(fd);
749					continue;
750				}
751			}
752
753			/*
754			 * We're doing host-based access checks here, so don't
755			 * allow v4-in-v6 to confuse things. The kernel will
756			 * disable it by default on NFS sockets too.
757			 */
758			if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
759			    sizeof one) < 0) {
760				syslog(LOG_ERR,
761				    "can't disable v4-in-v6 on IPv6 socket");
762				exit(1);
763			}
764			break;
765		default:
766			break;
767		}
768
769		/*
770		 * If no hosts were specified, just bind to INADDR_ANY
771		 */
772		if (strcmp("*", hosts[nhostsbak]) == 0) {
773			if (svcport_str == NULL) {
774				res = malloc(sizeof(struct addrinfo));
775				if (res == NULL)
776					out_of_mem();
777				mallocd_res = 1;
778				res->ai_flags = hints.ai_flags;
779				res->ai_family = hints.ai_family;
780				res->ai_protocol = hints.ai_protocol;
781				switch (res->ai_family) {
782				case AF_INET:
783					sin = malloc(sizeof(struct sockaddr_in));
784					if (sin == NULL)
785						out_of_mem();
786					sin->sin_family = AF_INET;
787					sin->sin_port = htons(0);
788					sin->sin_addr.s_addr = htonl(INADDR_ANY);
789					res->ai_addr = (struct sockaddr*) sin;
790					res->ai_addrlen = (socklen_t)
791					    sizeof(struct sockaddr_in);
792					break;
793				case AF_INET6:
794					sin6 = malloc(sizeof(struct sockaddr_in6));
795					if (sin6 == NULL)
796						out_of_mem();
797					sin6->sin6_family = AF_INET6;
798					sin6->sin6_port = htons(0);
799					sin6->sin6_addr = in6addr_any;
800					res->ai_addr = (struct sockaddr*) sin6;
801					res->ai_addrlen = (socklen_t)
802					    sizeof(struct sockaddr_in6);
803					break;
804				default:
805					syslog(LOG_ERR, "bad addr fam %d",
806					    res->ai_family);
807					exit(1);
808				}
809			} else {
810				if ((aicode = getaddrinfo(NULL, svcport_str,
811				    &hints, &res)) != 0) {
812					syslog(LOG_ERR,
813					    "cannot get local address for %s: %s",
814					    nconf->nc_netid,
815					    gai_strerror(aicode));
816					close(fd);
817					continue;
818				}
819			}
820		} else {
821			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
822			    &hints, &res)) != 0) {
823				syslog(LOG_ERR,
824				    "cannot get local address for %s: %s",
825				    nconf->nc_netid, gai_strerror(aicode));
826				close(fd);
827				continue;
828			}
829		}
830
831		/* Store the fd. */
832		sock_fd[sock_fdcnt - 1] = fd;
833
834		/* Now, attempt the bind. */
835		r = bindresvport_sa(fd, res->ai_addr);
836		if (r != 0) {
837			if (errno == EADDRINUSE && mallocd_svcport != 0) {
838				if (mallocd_res != 0) {
839					free(res->ai_addr);
840					free(res);
841				} else
842					freeaddrinfo(res);
843				return (-1);
844			}
845			syslog(LOG_ERR, "bindresvport_sa: %m");
846			exit(1);
847		}
848
849		if (svcport_str == NULL) {
850			svcport_str = malloc(NI_MAXSERV * sizeof(char));
851			if (svcport_str == NULL)
852				out_of_mem();
853			mallocd_svcport = 1;
854
855			if (getnameinfo(res->ai_addr,
856			    res->ai_addr->sa_len, NULL, NI_MAXHOST,
857			    svcport_str, NI_MAXSERV * sizeof(char),
858			    NI_NUMERICHOST | NI_NUMERICSERV))
859				errx(1, "Cannot get port number");
860		}
861		if (mallocd_res != 0) {
862			free(res->ai_addr);
863			free(res);
864		} else
865			freeaddrinfo(res);
866		res = NULL;
867	}
868	return (0);
869}
870
871/*
872 * Called after all the create_service() calls have succeeded, to complete
873 * the setup and registration.
874 */
875static void
876complete_service(struct netconfig *nconf, char *port_str)
877{
878	struct addrinfo hints, *res = NULL;
879	struct __rpc_sockinfo si;
880	struct netbuf servaddr;
881	SVCXPRT	*transp = NULL;
882	int aicode, fd, nhostsbak;
883	int registered = 0;
884
885	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
886	    (nconf->nc_semantics != NC_TPI_COTS) &&
887	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
888		return;	/* not my type */
889
890	/*
891	 * XXX - using RPC library internal functions.
892	 */
893	if (!__rpc_nconf2sockinfo(nconf, &si)) {
894		syslog(LOG_ERR, "cannot get information for %s",
895		    nconf->nc_netid);
896		return;
897	}
898
899	nhostsbak = nhosts;
900	while (nhostsbak > 0) {
901		--nhostsbak;
902		if (sock_fdpos >= sock_fdcnt) {
903			/* Should never happen. */
904			syslog(LOG_ERR, "Ran out of socket fd's");
905			return;
906		}
907		fd = sock_fd[sock_fdpos++];
908		if (fd < 0)
909			continue;
910
911		/*
912		 * Using -1 tells listen(2) to use
913		 * kern.ipc.soacceptqueue for the backlog.
914		 */
915		if (nconf->nc_semantics != NC_TPI_CLTS)
916			listen(fd, -1);
917
918		if (nconf->nc_semantics == NC_TPI_CLTS )
919			transp = svc_dg_create(fd, 0, 0);
920		else
921			transp = svc_vc_create(fd, RPC_MAXDATASIZE,
922			    RPC_MAXDATASIZE);
923
924		if (transp != (SVCXPRT *) NULL) {
925			if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
926			    NULL))
927				syslog(LOG_ERR,
928				    "can't register %s MOUNTVERS service",
929				    nconf->nc_netid);
930			if (!force_v2) {
931				if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
932				    mntsrv, NULL))
933					syslog(LOG_ERR,
934					    "can't register %s MOUNTVERS3 service",
935					    nconf->nc_netid);
936			}
937		} else
938			syslog(LOG_WARNING, "can't create %s services",
939			    nconf->nc_netid);
940
941		if (registered == 0) {
942			registered = 1;
943			memset(&hints, 0, sizeof hints);
944			hints.ai_flags = AI_PASSIVE;
945			hints.ai_family = si.si_af;
946			hints.ai_socktype = si.si_socktype;
947			hints.ai_protocol = si.si_proto;
948
949			if ((aicode = getaddrinfo(NULL, port_str, &hints,
950			    &res)) != 0) {
951				syslog(LOG_ERR, "cannot get local address: %s",
952				    gai_strerror(aicode));
953				exit(1);
954			}
955
956			servaddr.buf = malloc(res->ai_addrlen);
957			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
958			servaddr.len = res->ai_addrlen;
959
960			rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
961			rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
962
963			xcreated++;
964			freeaddrinfo(res);
965		}
966	} /* end while */
967}
968
969/*
970 * Clear out sockets after a failure to bind one of them, so that the
971 * cycle of socket creation/binding can start anew.
972 */
973static void
974clearout_service(void)
975{
976	int i;
977
978	for (i = 0; i < sock_fdcnt; i++) {
979		if (sock_fd[i] >= 0) {
980			shutdown(sock_fd[i], SHUT_RDWR);
981			close(sock_fd[i]);
982		}
983	}
984}
985
986static void
987usage(void)
988{
989	fprintf(stderr,
990		"usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
991		"[-S] [-h <bindip>] [export_file ...]\n");
992	exit(1);
993}
994
995/*
996 * The mount rpc service
997 */
998void
999mntsrv(struct svc_req *rqstp, SVCXPRT *transp)
1000{
1001	struct exportlist *ep;
1002	struct dirlist *dp;
1003	struct fhreturn fhr;
1004	struct stat stb;
1005	struct statfs fsb;
1006	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
1007	int lookup_failed = 1;
1008	struct sockaddr *saddr;
1009	u_short sport;
1010	char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
1011	int bad = 0, defset, hostset;
1012	sigset_t sighup_mask;
1013	int numsecflavors, *secflavorsp;
1014
1015	sigemptyset(&sighup_mask);
1016	sigaddset(&sighup_mask, SIGHUP);
1017	saddr = svc_getrpccaller(transp)->buf;
1018	switch (saddr->sa_family) {
1019	case AF_INET6:
1020		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
1021		break;
1022	case AF_INET:
1023		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
1024		break;
1025	default:
1026		syslog(LOG_ERR, "request from unknown address family");
1027		return;
1028	}
1029	switch (rqstp->rq_proc) {
1030	case MOUNTPROC_MNT:
1031	case MOUNTPROC_UMNT:
1032	case MOUNTPROC_UMNTALL:
1033		lookup_failed = getnameinfo(saddr, saddr->sa_len, host,
1034		    sizeof host, NULL, 0, 0);
1035	}
1036	getnameinfo(saddr, saddr->sa_len, numerichost,
1037	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
1038	switch (rqstp->rq_proc) {
1039	case NULLPROC:
1040		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
1041			syslog(LOG_ERR, "can't send reply");
1042		return;
1043	case MOUNTPROC_MNT:
1044		if (sport >= IPPORT_RESERVED && resvport_only) {
1045			syslog(LOG_NOTICE,
1046			    "mount request from %s from unprivileged port",
1047			    numerichost);
1048			svcerr_weakauth(transp);
1049			return;
1050		}
1051		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1052			syslog(LOG_NOTICE, "undecodable mount request from %s",
1053			    numerichost);
1054			svcerr_decode(transp);
1055			return;
1056		}
1057
1058		/*
1059		 * Get the real pathname and make sure it is a directory
1060		 * or a regular file if the -r option was specified
1061		 * and it exists.
1062		 */
1063		if (realpath(rpcpath, dirpath) == NULL ||
1064		    stat(dirpath, &stb) < 0 ||
1065		    statfs(dirpath, &fsb) < 0) {
1066			chdir("/");	/* Just in case realpath doesn't */
1067			syslog(LOG_NOTICE,
1068			    "mount request from %s for non existent path %s",
1069			    numerichost, dirpath);
1070			if (debug)
1071				warnx("stat failed on %s", dirpath);
1072			bad = ENOENT;	/* We will send error reply later */
1073		}
1074		if (!bad &&
1075		    !S_ISDIR(stb.st_mode) &&
1076		    (dir_only || !S_ISREG(stb.st_mode))) {
1077			syslog(LOG_NOTICE,
1078			    "mount request from %s for non-directory path %s",
1079			    numerichost, dirpath);
1080			if (debug)
1081				warnx("mounting non-directory %s", dirpath);
1082			bad = ENOTDIR;	/* We will send error reply later */
1083		}
1084
1085		/* Check in the exports list */
1086		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1087		if (bad)
1088			ep = NULL;
1089		else
1090			ep = ex_search(&fsb.f_fsid);
1091		hostset = defset = 0;
1092		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
1093		    &numsecflavors, &secflavorsp) ||
1094		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
1095		      chk_host(dp, saddr, &defset, &hostset, &numsecflavors,
1096		       &secflavorsp)) ||
1097		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
1098		     scan_tree(ep->ex_dirl, saddr) == 0))) {
1099			if (bad) {
1100				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1101				    (caddr_t)&bad))
1102					syslog(LOG_ERR, "can't send reply");
1103				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1104				return;
1105			}
1106			if (hostset & DP_HOSTSET) {
1107				fhr.fhr_flag = hostset;
1108				fhr.fhr_numsecflavors = numsecflavors;
1109				fhr.fhr_secflavors = secflavorsp;
1110			} else {
1111				fhr.fhr_flag = defset;
1112				fhr.fhr_numsecflavors = ep->ex_defnumsecflavors;
1113				fhr.fhr_secflavors = ep->ex_defsecflavors;
1114			}
1115			fhr.fhr_vers = rqstp->rq_vers;
1116			/* Get the file handle */
1117			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
1118			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
1119				bad = errno;
1120				syslog(LOG_ERR, "can't get fh for %s", dirpath);
1121				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1122				    (caddr_t)&bad))
1123					syslog(LOG_ERR, "can't send reply");
1124				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1125				return;
1126			}
1127			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
1128			    (caddr_t)&fhr))
1129				syslog(LOG_ERR, "can't send reply");
1130			if (!lookup_failed)
1131				add_mlist(host, dirpath);
1132			else
1133				add_mlist(numerichost, dirpath);
1134			if (debug)
1135				warnx("mount successful");
1136			if (dolog)
1137				syslog(LOG_NOTICE,
1138				    "mount request succeeded from %s for %s",
1139				    numerichost, dirpath);
1140		} else {
1141			if (!bad)
1142				bad = EACCES;
1143			syslog(LOG_NOTICE,
1144			    "mount request denied from %s for %s",
1145			    numerichost, dirpath);
1146		}
1147
1148		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
1149		    (caddr_t)&bad))
1150			syslog(LOG_ERR, "can't send reply");
1151		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1152		return;
1153	case MOUNTPROC_DUMP:
1154		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
1155			syslog(LOG_ERR, "can't send reply");
1156		else if (dolog)
1157			syslog(LOG_NOTICE,
1158			    "dump request succeeded from %s",
1159			    numerichost);
1160		return;
1161	case MOUNTPROC_UMNT:
1162		if (sport >= IPPORT_RESERVED && resvport_only) {
1163			syslog(LOG_NOTICE,
1164			    "umount request from %s from unprivileged port",
1165			    numerichost);
1166			svcerr_weakauth(transp);
1167			return;
1168		}
1169		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1170			syslog(LOG_NOTICE, "undecodable umount request from %s",
1171			    numerichost);
1172			svcerr_decode(transp);
1173			return;
1174		}
1175		if (realpath(rpcpath, dirpath) == NULL) {
1176			syslog(LOG_NOTICE, "umount request from %s "
1177			    "for non existent path %s",
1178			    numerichost, dirpath);
1179		}
1180		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1181			syslog(LOG_ERR, "can't send reply");
1182		if (!lookup_failed)
1183			del_mlist(host, dirpath);
1184		del_mlist(numerichost, dirpath);
1185		if (dolog)
1186			syslog(LOG_NOTICE,
1187			    "umount request succeeded from %s for %s",
1188			    numerichost, dirpath);
1189		return;
1190	case MOUNTPROC_UMNTALL:
1191		if (sport >= IPPORT_RESERVED && resvport_only) {
1192			syslog(LOG_NOTICE,
1193			    "umountall request from %s from unprivileged port",
1194			    numerichost);
1195			svcerr_weakauth(transp);
1196			return;
1197		}
1198		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1199			syslog(LOG_ERR, "can't send reply");
1200		if (!lookup_failed)
1201			del_mlist(host, NULL);
1202		del_mlist(numerichost, NULL);
1203		if (dolog)
1204			syslog(LOG_NOTICE,
1205			    "umountall request succeeded from %s",
1206			    numerichost);
1207		return;
1208	case MOUNTPROC_EXPORT:
1209		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
1210			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
1211			    (caddr_t)NULL))
1212				syslog(LOG_ERR, "can't send reply");
1213		if (dolog)
1214			syslog(LOG_NOTICE,
1215			    "export request succeeded from %s",
1216			    numerichost);
1217		return;
1218	default:
1219		svcerr_noproc(transp);
1220		return;
1221	}
1222}
1223
1224/*
1225 * Xdr conversion for a dirpath string
1226 */
1227static int
1228xdr_dir(XDR *xdrsp, char *dirp)
1229{
1230	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
1231}
1232
1233/*
1234 * Xdr routine to generate file handle reply
1235 */
1236static int
1237xdr_fhs(XDR *xdrsp, caddr_t cp)
1238{
1239	struct fhreturn *fhrp = (struct fhreturn *)cp;
1240	u_long ok = 0, len, auth;
1241	int i;
1242
1243	if (!xdr_long(xdrsp, &ok))
1244		return (0);
1245	switch (fhrp->fhr_vers) {
1246	case 1:
1247		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
1248	case 3:
1249		len = NFSX_V3FH;
1250		if (!xdr_long(xdrsp, &len))
1251			return (0);
1252		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
1253			return (0);
1254		if (fhrp->fhr_numsecflavors) {
1255			if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
1256				return (0);
1257			for (i = 0; i < fhrp->fhr_numsecflavors; i++)
1258				if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
1259					return (0);
1260			return (1);
1261		} else {
1262			auth = AUTH_SYS;
1263			len = 1;
1264			if (!xdr_long(xdrsp, &len))
1265				return (0);
1266			return (xdr_long(xdrsp, &auth));
1267		}
1268	}
1269	return (0);
1270}
1271
1272static int
1273xdr_mlist(XDR *xdrsp, caddr_t cp __unused)
1274{
1275	struct mountlist *mlp;
1276	int true = 1;
1277	int false = 0;
1278	char *strp;
1279
1280	SLIST_FOREACH(mlp, &mlhead, next) {
1281		if (!xdr_bool(xdrsp, &true))
1282			return (0);
1283		strp = &mlp->ml_host[0];
1284		if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
1285			return (0);
1286		strp = &mlp->ml_dirp[0];
1287		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1288			return (0);
1289	}
1290	if (!xdr_bool(xdrsp, &false))
1291		return (0);
1292	return (1);
1293}
1294
1295/*
1296 * Xdr conversion for export list
1297 */
1298static int
1299xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
1300{
1301	struct exportlist *ep;
1302	int false = 0;
1303	int putdef;
1304	sigset_t sighup_mask;
1305
1306	sigemptyset(&sighup_mask);
1307	sigaddset(&sighup_mask, SIGHUP);
1308	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1309
1310	SLIST_FOREACH(ep, &exphead, entries) {
1311		putdef = 0;
1312		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1313			       &putdef, brief))
1314			goto errout;
1315		if (ep->ex_defdir && putdef == 0 &&
1316			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
1317			&putdef, brief))
1318			goto errout;
1319	}
1320	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1321	if (!xdr_bool(xdrsp, &false))
1322		return (0);
1323	return (1);
1324errout:
1325	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1326	return (0);
1327}
1328
1329/*
1330 * Called from xdr_explist() to traverse the tree and export the
1331 * directory paths.
1332 */
1333static int
1334put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1335	int brief)
1336{
1337	struct grouplist *grp;
1338	struct hostlist *hp;
1339	int true = 1;
1340	int false = 0;
1341	int gotalldir = 0;
1342	char *strp;
1343
1344	if (dp) {
1345		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
1346			return (1);
1347		if (!xdr_bool(xdrsp, &true))
1348			return (1);
1349		strp = dp->dp_dirp;
1350		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1351			return (1);
1352		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
1353			gotalldir = 1;
1354			*putdefp = 1;
1355		}
1356		if (brief) {
1357			if (!xdr_bool(xdrsp, &true))
1358				return (1);
1359			strp = "(...)";
1360			if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1361				return (1);
1362		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
1363		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
1364			hp = dp->dp_hosts;
1365			while (hp) {
1366				grp = hp->ht_grp;
1367				if (grp->gr_type == GT_HOST) {
1368					if (!xdr_bool(xdrsp, &true))
1369						return (1);
1370					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
1371					if (!xdr_string(xdrsp, &strp,
1372					    MNTNAMLEN))
1373						return (1);
1374				} else if (grp->gr_type == GT_NET) {
1375					if (!xdr_bool(xdrsp, &true))
1376						return (1);
1377					strp = grp->gr_ptr.gt_net.nt_name;
1378					if (!xdr_string(xdrsp, &strp,
1379					    MNTNAMLEN))
1380						return (1);
1381				}
1382				hp = hp->ht_next;
1383				if (gotalldir && hp == (struct hostlist *)NULL) {
1384					hp = adp->dp_hosts;
1385					gotalldir = 0;
1386				}
1387			}
1388		}
1389		if (!xdr_bool(xdrsp, &false))
1390			return (1);
1391		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
1392			return (1);
1393	}
1394	return (0);
1395}
1396
1397static int
1398xdr_explist(XDR *xdrsp, caddr_t cp)
1399{
1400
1401	return xdr_explist_common(xdrsp, cp, 0);
1402}
1403
1404static int
1405xdr_explist_brief(XDR *xdrsp, caddr_t cp)
1406{
1407
1408	return xdr_explist_common(xdrsp, cp, 1);
1409}
1410
1411static char *line;
1412static size_t linesize;
1413static FILE *exp_file;
1414
1415/*
1416 * Get the export list from one, currently open file
1417 */
1418static void
1419get_exportlist_one(void)
1420{
1421	struct exportlist *ep;
1422	struct grouplist *grp, *tgrp;
1423	struct dirlist *dirhead;
1424	struct statfs fsb;
1425	struct xucred anon;
1426	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1427	int len, has_host, exflags, got_nondir, dirplen, netgrp;
1428
1429	v4root_phase = 0;
1430	dirhead = (struct dirlist *)NULL;
1431	while (get_line()) {
1432		if (debug)
1433			warnx("got line %s", line);
1434		cp = line;
1435		nextfield(&cp, &endcp);
1436		if (*cp == '#')
1437			goto nextline;
1438
1439		/*
1440		 * Set defaults.
1441		 */
1442		has_host = FALSE;
1443		anon = def_anon;
1444		exflags = MNT_EXPORTED;
1445		got_nondir = 0;
1446		opt_flags = 0;
1447		ep = (struct exportlist *)NULL;
1448		dirp = NULL;
1449
1450		/*
1451		 * Handle the V4 root dir.
1452		 */
1453		if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1454			/*
1455			 * V4: just indicates that it is the v4 root point,
1456			 * so skip over that and set v4root_phase.
1457			 */
1458			if (v4root_phase > 0) {
1459				syslog(LOG_ERR, "V4:duplicate line, ignored");
1460				goto nextline;
1461			}
1462			v4root_phase = 1;
1463			cp += 3;
1464			nextfield(&cp, &endcp);
1465		}
1466
1467		/*
1468		 * Create new exports list entry
1469		 */
1470		len = endcp-cp;
1471		tgrp = grp = get_grp();
1472		while (len > 0) {
1473			if (len > MNTNAMLEN) {
1474			    getexp_err(ep, tgrp, "mountpoint too long");
1475			    goto nextline;
1476			}
1477			if (*cp == '-') {
1478			    if (ep == (struct exportlist *)NULL) {
1479				getexp_err(ep, tgrp,
1480				    "flag before export path definition");
1481				goto nextline;
1482			    }
1483			    if (debug)
1484				warnx("doing opt %s", cp);
1485			    got_nondir = 1;
1486			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
1487				&exflags, &anon)) {
1488				getexp_err(ep, tgrp, NULL);
1489				goto nextline;
1490			    }
1491			} else if (*cp == '/') {
1492			    savedc = *endcp;
1493			    *endcp = '\0';
1494			    if (v4root_phase > 1) {
1495				    if (dirp != NULL) {
1496					getexp_err(ep, tgrp, "Multiple V4 dirs");
1497					goto nextline;
1498				    }
1499			    }
1500			    if (check_dirpath(cp) &&
1501				statfs(cp, &fsb) >= 0) {
1502				if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
1503				    syslog(LOG_ERR, "Warning: exporting of "
1504					"automounted fs %s not supported", cp);
1505				if (got_nondir) {
1506				    getexp_err(ep, tgrp, "dirs must be first");
1507				    goto nextline;
1508				}
1509				if (v4root_phase == 1) {
1510				    if (dirp != NULL) {
1511					getexp_err(ep, tgrp, "Multiple V4 dirs");
1512					goto nextline;
1513				    }
1514				    if (strlen(v4root_dirpath) == 0) {
1515					strlcpy(v4root_dirpath, cp,
1516					    sizeof (v4root_dirpath));
1517				    } else if (strcmp(v4root_dirpath, cp)
1518					!= 0) {
1519					syslog(LOG_ERR,
1520					    "different V4 dirpath %s", cp);
1521					getexp_err(ep, tgrp, NULL);
1522					goto nextline;
1523				    }
1524				    dirp = cp;
1525				    v4root_phase = 2;
1526				    got_nondir = 1;
1527				    ep = get_exp();
1528				} else {
1529				    if (ep) {
1530					if (ep->ex_fs.val[0] !=
1531					    fsb.f_fsid.val[0] ||
1532					    ep->ex_fs.val[1] !=
1533					    fsb.f_fsid.val[1]) {
1534						getexp_err(ep, tgrp,
1535						    "fsid mismatch");
1536						goto nextline;
1537					}
1538				    } else {
1539					/*
1540					 * See if this directory is already
1541					 * in the list.
1542					 */
1543					ep = ex_search(&fsb.f_fsid);
1544					if (ep == (struct exportlist *)NULL) {
1545					    ep = get_exp();
1546					    ep->ex_fs = fsb.f_fsid;
1547					    ep->ex_fsdir = strdup(fsb.f_mntonname);
1548					    if (ep->ex_fsdir == NULL)
1549						out_of_mem();
1550					    if (debug)
1551						warnx(
1552						  "making new ep fs=0x%x,0x%x",
1553						  fsb.f_fsid.val[0],
1554						  fsb.f_fsid.val[1]);
1555					} else if (debug)
1556					    warnx("found ep fs=0x%x,0x%x",
1557						fsb.f_fsid.val[0],
1558						fsb.f_fsid.val[1]);
1559				    }
1560
1561				    /*
1562				     * Add dirpath to export mount point.
1563				     */
1564				    dirp = add_expdir(&dirhead, cp, len);
1565				    dirplen = len;
1566				}
1567			    } else {
1568				getexp_err(ep, tgrp,
1569				    "symbolic link in export path or statfs failed");
1570				goto nextline;
1571			    }
1572			    *endcp = savedc;
1573			} else {
1574			    savedc = *endcp;
1575			    *endcp = '\0';
1576			    got_nondir = 1;
1577			    if (ep == (struct exportlist *)NULL) {
1578				getexp_err(ep, tgrp,
1579				    "host(s) before export path definition");
1580				goto nextline;
1581			    }
1582
1583			    /*
1584			     * Get the host or netgroup.
1585			     */
1586			    setnetgrent(cp);
1587			    netgrp = getnetgrent(&hst, &usr, &dom);
1588			    do {
1589				if (has_host) {
1590				    grp->gr_next = get_grp();
1591				    grp = grp->gr_next;
1592				}
1593				if (netgrp) {
1594				    if (hst == 0) {
1595					syslog(LOG_ERR,
1596				"null hostname in netgroup %s, skipping", cp);
1597					grp->gr_type = GT_IGNORE;
1598				    } else if (get_host(hst, grp, tgrp)) {
1599					syslog(LOG_ERR,
1600			"bad host %s in netgroup %s, skipping", hst, cp);
1601					grp->gr_type = GT_IGNORE;
1602				    }
1603				} else if (get_host(cp, grp, tgrp)) {
1604				    syslog(LOG_ERR, "bad host %s, skipping", cp);
1605				    grp->gr_type = GT_IGNORE;
1606				}
1607				has_host = TRUE;
1608			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
1609			    endnetgrent();
1610			    *endcp = savedc;
1611			}
1612			cp = endcp;
1613			nextfield(&cp, &endcp);
1614			len = endcp - cp;
1615		}
1616		if (check_options(dirhead)) {
1617			getexp_err(ep, tgrp, NULL);
1618			goto nextline;
1619		}
1620		if (!has_host) {
1621			grp->gr_type = GT_DEFAULT;
1622			if (debug)
1623				warnx("adding a default entry");
1624
1625		/*
1626		 * Don't allow a network export coincide with a list of
1627		 * host(s) on the same line.
1628		 */
1629		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1630			getexp_err(ep, tgrp, "network/host conflict");
1631			goto nextline;
1632
1633		/*
1634		 * If an export list was specified on this line, make sure
1635		 * that we have at least one valid entry, otherwise skip it.
1636		 */
1637		} else {
1638			grp = tgrp;
1639			while (grp && grp->gr_type == GT_IGNORE)
1640				grp = grp->gr_next;
1641			if (! grp) {
1642			    getexp_err(ep, tgrp, "no valid entries");
1643			    goto nextline;
1644			}
1645		}
1646
1647		if (v4root_phase == 1) {
1648			getexp_err(ep, tgrp, "V4:root, no dirp, ignored");
1649			goto nextline;
1650		}
1651
1652		/*
1653		 * Loop through hosts, pushing the exports into the kernel.
1654		 * After loop, tgrp points to the start of the list and
1655		 * grp points to the last entry in the list.
1656		 */
1657		grp = tgrp;
1658		do {
1659			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
1660			    &fsb)) {
1661				getexp_err(ep, tgrp, NULL);
1662				goto nextline;
1663			}
1664		} while (grp->gr_next && (grp = grp->gr_next));
1665
1666		/*
1667		 * For V4: don't enter in mount lists.
1668		 */
1669		if (v4root_phase > 0 && v4root_phase <= 2) {
1670			/*
1671			 * Since these structures aren't used by mountd,
1672			 * free them up now.
1673			 */
1674			if (ep != NULL)
1675				free_exp(ep);
1676			while (tgrp != NULL) {
1677				grp = tgrp;
1678				tgrp = tgrp->gr_next;
1679				free_grp(grp);
1680			}
1681			goto nextline;
1682		}
1683
1684		/*
1685		 * Success. Update the data structures.
1686		 */
1687		if (has_host) {
1688			hang_dirp(dirhead, tgrp, ep, opt_flags);
1689			grp->gr_next = grphead;
1690			grphead = tgrp;
1691		} else {
1692			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1693				opt_flags);
1694			free_grp(grp);
1695		}
1696		dirhead = (struct dirlist *)NULL;
1697		if ((ep->ex_flag & EX_LINKED) == 0) {
1698			SLIST_INSERT_HEAD(&exphead, ep, entries);
1699
1700			ep->ex_flag |= EX_LINKED;
1701		}
1702nextline:
1703		v4root_phase = 0;
1704		if (dirhead) {
1705			free_dir(dirhead);
1706			dirhead = (struct dirlist *)NULL;
1707		}
1708	}
1709}
1710
1711/*
1712 * Get the export list from all specified files
1713 */
1714static void
1715get_exportlist(void)
1716{
1717	struct exportlist *ep, *ep2;
1718	struct grouplist *grp, *tgrp;
1719	struct export_args export;
1720	struct iovec *iov;
1721	struct statfs *fsp, *mntbufp;
1722	struct xvfsconf vfc;
1723	char errmsg[255];
1724	int num, i;
1725	int iovlen;
1726	int done;
1727	struct nfsex_args eargs;
1728
1729	if (suspend_nfsd != 0)
1730		(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
1731	v4root_dirpath[0] = '\0';
1732	bzero(&export, sizeof(export));
1733	export.ex_flags = MNT_DELEXPORT;
1734	iov = NULL;
1735	iovlen = 0;
1736	bzero(errmsg, sizeof(errmsg));
1737
1738	/*
1739	 * First, get rid of the old list
1740	 */
1741	SLIST_FOREACH_SAFE(ep, &exphead, entries, ep2) {
1742		SLIST_REMOVE(&exphead, ep, exportlist, entries);
1743		free_exp(ep);
1744	}
1745
1746	grp = grphead;
1747	while (grp) {
1748		tgrp = grp;
1749		grp = grp->gr_next;
1750		free_grp(tgrp);
1751	}
1752	grphead = (struct grouplist *)NULL;
1753
1754	/*
1755	 * and the old V4 root dir.
1756	 */
1757	bzero(&eargs, sizeof (eargs));
1758	eargs.export.ex_flags = MNT_DELEXPORT;
1759	if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
1760	    errno != ENOENT)
1761		syslog(LOG_ERR, "Can't delete exports for V4:");
1762
1763	/*
1764	 * and clear flag that notes if a public fh has been exported.
1765	 */
1766	has_publicfh = 0;
1767
1768	/*
1769	 * And delete exports that are in the kernel for all local
1770	 * filesystems.
1771	 * XXX: Should know how to handle all local exportable filesystems.
1772	 */
1773	num = getmntinfo(&mntbufp, MNT_NOWAIT);
1774
1775	if (num > 0) {
1776		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1777		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1778		build_iovec(&iov, &iovlen, "from", NULL, 0);
1779		build_iovec(&iov, &iovlen, "update", NULL, 0);
1780		build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
1781		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1782	}
1783
1784	for (i = 0; i < num; i++) {
1785		fsp = &mntbufp[i];
1786		if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
1787			syslog(LOG_ERR, "getvfsbyname() failed for %s",
1788			    fsp->f_fstypename);
1789			continue;
1790		}
1791
1792		/*
1793		 * We do not need to delete "export" flag from
1794		 * filesystems that do not have it set.
1795		 */
1796		if (!(fsp->f_flags & MNT_EXPORTED))
1797		    continue;
1798		/*
1799		 * Do not delete export for network filesystem by
1800		 * passing "export" arg to nmount().
1801		 * It only makes sense to do this for local filesystems.
1802		 */
1803		if (vfc.vfc_flags & VFCF_NETWORK)
1804			continue;
1805
1806		iov[1].iov_base = fsp->f_fstypename;
1807		iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
1808		iov[3].iov_base = fsp->f_mntonname;
1809		iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
1810		iov[5].iov_base = fsp->f_mntfromname;
1811		iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
1812		errmsg[0] = '\0';
1813
1814		/*
1815		 * EXDEV is returned when path exists but is not a
1816		 * mount point.  May happens if raced with unmount.
1817		 */
1818		if (nmount(iov, iovlen, fsp->f_flags) < 0 &&
1819		    errno != ENOENT && errno != ENOTSUP && errno != EXDEV) {
1820			syslog(LOG_ERR,
1821			    "can't delete exports for %s: %m %s",
1822			    fsp->f_mntonname, errmsg);
1823		}
1824	}
1825
1826	if (iov != NULL) {
1827		/* Free strings allocated by strdup() in getmntopts.c */
1828		free(iov[0].iov_base); /* fstype */
1829		free(iov[2].iov_base); /* fspath */
1830		free(iov[4].iov_base); /* from */
1831		free(iov[6].iov_base); /* update */
1832		free(iov[8].iov_base); /* export */
1833		free(iov[10].iov_base); /* errmsg */
1834
1835		/* free iov, allocated by realloc() */
1836		free(iov);
1837		iovlen = 0;
1838	}
1839
1840	/*
1841	 * Read in the exports file and build the list, calling
1842	 * nmount() as we go along to push the export rules into the kernel.
1843	 */
1844	done = 0;
1845	for (i = 0; exnames[i] != NULL; i++) {
1846		if (debug)
1847			warnx("reading exports from %s", exnames[i]);
1848		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
1849			syslog(LOG_WARNING, "can't open %s", exnames[i]);
1850			continue;
1851		}
1852		get_exportlist_one();
1853		fclose(exp_file);
1854		done++;
1855	}
1856	if (done == 0) {
1857		syslog(LOG_ERR, "can't open any exports file");
1858		exit(2);
1859	}
1860
1861	/*
1862	 * If there was no public fh, clear any previous one set.
1863	 */
1864	if (has_publicfh == 0)
1865		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
1866
1867	/* Resume the nfsd. If they weren't suspended, this is harmless. */
1868	(void)nfssvc(NFSSVC_RESUMENFSD, NULL);
1869}
1870
1871/*
1872 * Allocate an export list element
1873 */
1874static struct exportlist *
1875get_exp(void)
1876{
1877	struct exportlist *ep;
1878
1879	ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
1880	if (ep == (struct exportlist *)NULL)
1881		out_of_mem();
1882	return (ep);
1883}
1884
1885/*
1886 * Allocate a group list element
1887 */
1888static struct grouplist *
1889get_grp(void)
1890{
1891	struct grouplist *gp;
1892
1893	gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
1894	if (gp == (struct grouplist *)NULL)
1895		out_of_mem();
1896	return (gp);
1897}
1898
1899/*
1900 * Clean up upon an error in get_exportlist().
1901 */
1902static void
1903getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason)
1904{
1905	struct grouplist *tgrp;
1906
1907	if (!(opt_flags & OP_QUIET)) {
1908		if (reason != NULL)
1909			syslog(LOG_ERR, "bad exports list line '%s': %s", line,
1910			    reason);
1911		else
1912			syslog(LOG_ERR, "bad exports list line '%s'", line);
1913	}
1914	if (ep && (ep->ex_flag & EX_LINKED) == 0)
1915		free_exp(ep);
1916	while (grp) {
1917		tgrp = grp;
1918		grp = grp->gr_next;
1919		free_grp(tgrp);
1920	}
1921}
1922
1923/*
1924 * Search the export list for a matching fs.
1925 */
1926static struct exportlist *
1927ex_search(fsid_t *fsid)
1928{
1929	struct exportlist *ep;
1930
1931	SLIST_FOREACH(ep, &exphead, entries) {
1932		if (ep->ex_fs.val[0] == fsid->val[0] &&
1933		    ep->ex_fs.val[1] == fsid->val[1])
1934			return (ep);
1935	}
1936
1937	return (ep);
1938}
1939
1940/*
1941 * Add a directory path to the list.
1942 */
1943static char *
1944add_expdir(struct dirlist **dpp, char *cp, int len)
1945{
1946	struct dirlist *dp;
1947
1948	dp = malloc(sizeof (struct dirlist));
1949	if (dp == (struct dirlist *)NULL)
1950		out_of_mem();
1951	dp->dp_left = *dpp;
1952	dp->dp_right = (struct dirlist *)NULL;
1953	dp->dp_flag = 0;
1954	dp->dp_hosts = (struct hostlist *)NULL;
1955	dp->dp_dirp = strndup(cp, len);
1956	if (dp->dp_dirp == NULL)
1957		out_of_mem();
1958	*dpp = dp;
1959	return (dp->dp_dirp);
1960}
1961
1962/*
1963 * Hang the dir list element off the dirpath binary tree as required
1964 * and update the entry for host.
1965 */
1966static void
1967hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
1968	int flags)
1969{
1970	struct hostlist *hp;
1971	struct dirlist *dp2;
1972
1973	if (flags & OP_ALLDIRS) {
1974		if (ep->ex_defdir)
1975			free((caddr_t)dp);
1976		else
1977			ep->ex_defdir = dp;
1978		if (grp == (struct grouplist *)NULL) {
1979			ep->ex_defdir->dp_flag |= DP_DEFSET;
1980			/* Save the default security flavors list. */
1981			ep->ex_defnumsecflavors = ep->ex_numsecflavors;
1982			if (ep->ex_numsecflavors > 0)
1983				memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
1984				    sizeof(ep->ex_secflavors));
1985		} else while (grp) {
1986			hp = get_ht();
1987			hp->ht_grp = grp;
1988			hp->ht_next = ep->ex_defdir->dp_hosts;
1989			ep->ex_defdir->dp_hosts = hp;
1990			/* Save the security flavors list for this host set. */
1991			grp->gr_numsecflavors = ep->ex_numsecflavors;
1992			if (ep->ex_numsecflavors > 0)
1993				memcpy(grp->gr_secflavors, ep->ex_secflavors,
1994				    sizeof(ep->ex_secflavors));
1995			grp = grp->gr_next;
1996		}
1997	} else {
1998
1999		/*
2000		 * Loop through the directories adding them to the tree.
2001		 */
2002		while (dp) {
2003			dp2 = dp->dp_left;
2004			add_dlist(&ep->ex_dirl, dp, grp, flags, ep);
2005			dp = dp2;
2006		}
2007	}
2008}
2009
2010/*
2011 * Traverse the binary tree either updating a node that is already there
2012 * for the new directory or adding the new node.
2013 */
2014static void
2015add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
2016	int flags, struct exportlist *ep)
2017{
2018	struct dirlist *dp;
2019	struct hostlist *hp;
2020	int cmp;
2021
2022	dp = *dpp;
2023	if (dp) {
2024		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
2025		if (cmp > 0) {
2026			add_dlist(&dp->dp_left, newdp, grp, flags, ep);
2027			return;
2028		} else if (cmp < 0) {
2029			add_dlist(&dp->dp_right, newdp, grp, flags, ep);
2030			return;
2031		} else
2032			free((caddr_t)newdp);
2033	} else {
2034		dp = newdp;
2035		dp->dp_left = (struct dirlist *)NULL;
2036		*dpp = dp;
2037	}
2038	if (grp) {
2039
2040		/*
2041		 * Hang all of the host(s) off of the directory point.
2042		 */
2043		do {
2044			hp = get_ht();
2045			hp->ht_grp = grp;
2046			hp->ht_next = dp->dp_hosts;
2047			dp->dp_hosts = hp;
2048			/* Save the security flavors list for this host set. */
2049			grp->gr_numsecflavors = ep->ex_numsecflavors;
2050			if (ep->ex_numsecflavors > 0)
2051				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2052				    sizeof(ep->ex_secflavors));
2053			grp = grp->gr_next;
2054		} while (grp);
2055	} else {
2056		dp->dp_flag |= DP_DEFSET;
2057		/* Save the default security flavors list. */
2058		ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2059		if (ep->ex_numsecflavors > 0)
2060			memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2061			    sizeof(ep->ex_secflavors));
2062	}
2063}
2064
2065/*
2066 * Search for a dirpath on the export point.
2067 */
2068static struct dirlist *
2069dirp_search(struct dirlist *dp, char *dirp)
2070{
2071	int cmp;
2072
2073	if (dp) {
2074		cmp = strcmp(dp->dp_dirp, dirp);
2075		if (cmp > 0)
2076			return (dirp_search(dp->dp_left, dirp));
2077		else if (cmp < 0)
2078			return (dirp_search(dp->dp_right, dirp));
2079		else
2080			return (dp);
2081	}
2082	return (dp);
2083}
2084
2085/*
2086 * Scan for a host match in a directory tree.
2087 */
2088static int
2089chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2090	int *hostsetp, int *numsecflavors, int **secflavorsp)
2091{
2092	struct hostlist *hp;
2093	struct grouplist *grp;
2094	struct addrinfo *ai;
2095
2096	if (dp) {
2097		if (dp->dp_flag & DP_DEFSET)
2098			*defsetp = dp->dp_flag;
2099		hp = dp->dp_hosts;
2100		while (hp) {
2101			grp = hp->ht_grp;
2102			switch (grp->gr_type) {
2103			case GT_HOST:
2104				ai = grp->gr_ptr.gt_addrinfo;
2105				for (; ai; ai = ai->ai_next) {
2106					if (!sacmp(ai->ai_addr, saddr, NULL)) {
2107						*hostsetp =
2108						    (hp->ht_flag | DP_HOSTSET);
2109						if (numsecflavors != NULL) {
2110							*numsecflavors =
2111							    grp->gr_numsecflavors;
2112							*secflavorsp =
2113							    grp->gr_secflavors;
2114						}
2115						return (1);
2116					}
2117				}
2118				break;
2119			case GT_NET:
2120				if (!sacmp(saddr, (struct sockaddr *)
2121				    &grp->gr_ptr.gt_net.nt_net,
2122				    (struct sockaddr *)
2123				    &grp->gr_ptr.gt_net.nt_mask)) {
2124					*hostsetp = (hp->ht_flag | DP_HOSTSET);
2125					if (numsecflavors != NULL) {
2126						*numsecflavors =
2127						    grp->gr_numsecflavors;
2128						*secflavorsp =
2129						    grp->gr_secflavors;
2130					}
2131					return (1);
2132				}
2133				break;
2134			}
2135			hp = hp->ht_next;
2136		}
2137	}
2138	return (0);
2139}
2140
2141/*
2142 * Scan tree for a host that matches the address.
2143 */
2144static int
2145scan_tree(struct dirlist *dp, struct sockaddr *saddr)
2146{
2147	int defset, hostset;
2148
2149	if (dp) {
2150		if (scan_tree(dp->dp_left, saddr))
2151			return (1);
2152		if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
2153			return (1);
2154		if (scan_tree(dp->dp_right, saddr))
2155			return (1);
2156	}
2157	return (0);
2158}
2159
2160/*
2161 * Traverse the dirlist tree and free it up.
2162 */
2163static void
2164free_dir(struct dirlist *dp)
2165{
2166
2167	if (dp) {
2168		free_dir(dp->dp_left);
2169		free_dir(dp->dp_right);
2170		free_host(dp->dp_hosts);
2171		free(dp->dp_dirp);
2172		free(dp);
2173	}
2174}
2175
2176/*
2177 * Parse a colon separated list of security flavors
2178 */
2179static int
2180parsesec(char *seclist, struct exportlist *ep)
2181{
2182	char *cp, savedc;
2183	int flavor;
2184
2185	ep->ex_numsecflavors = 0;
2186	for (;;) {
2187		cp = strchr(seclist, ':');
2188		if (cp) {
2189			savedc = *cp;
2190			*cp = '\0';
2191		}
2192
2193		if (!strcmp(seclist, "sys"))
2194			flavor = AUTH_SYS;
2195		else if (!strcmp(seclist, "krb5"))
2196			flavor = RPCSEC_GSS_KRB5;
2197		else if (!strcmp(seclist, "krb5i"))
2198			flavor = RPCSEC_GSS_KRB5I;
2199		else if (!strcmp(seclist, "krb5p"))
2200			flavor = RPCSEC_GSS_KRB5P;
2201		else {
2202			if (cp)
2203				*cp = savedc;
2204			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2205			return (1);
2206		}
2207		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2208			if (cp)
2209				*cp = savedc;
2210			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2211			return (1);
2212		}
2213		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2214		ep->ex_numsecflavors++;
2215		if (cp) {
2216			*cp = savedc;
2217			seclist = cp + 1;
2218		} else {
2219			break;
2220		}
2221	}
2222	return (0);
2223}
2224
2225/*
2226 * Parse the option string and update fields.
2227 * Option arguments may either be -<option>=<value> or
2228 * -<option> <value>
2229 */
2230static int
2231do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2232	int *has_hostp, int *exflagsp, struct xucred *cr)
2233{
2234	char *cpoptarg, *cpoptend;
2235	char *cp, *endcp, *cpopt, savedc, savedc2;
2236	int allflag, usedarg;
2237
2238	savedc2 = '\0';
2239	cpopt = *cpp;
2240	cpopt++;
2241	cp = *endcpp;
2242	savedc = *cp;
2243	*cp = '\0';
2244	while (cpopt && *cpopt) {
2245		allflag = 1;
2246		usedarg = -2;
2247		if ((cpoptend = strchr(cpopt, ','))) {
2248			*cpoptend++ = '\0';
2249			if ((cpoptarg = strchr(cpopt, '=')))
2250				*cpoptarg++ = '\0';
2251		} else {
2252			if ((cpoptarg = strchr(cpopt, '=')))
2253				*cpoptarg++ = '\0';
2254			else {
2255				*cp = savedc;
2256				nextfield(&cp, &endcp);
2257				**endcpp = '\0';
2258				if (endcp > cp && *cp != '-') {
2259					cpoptarg = cp;
2260					savedc2 = *endcp;
2261					*endcp = '\0';
2262					usedarg = 0;
2263				}
2264			}
2265		}
2266		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
2267			*exflagsp |= MNT_EXRDONLY;
2268		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
2269		    !(allflag = strcmp(cpopt, "mapall")) ||
2270		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
2271			usedarg++;
2272			parsecred(cpoptarg, cr);
2273			if (allflag == 0) {
2274				*exflagsp |= MNT_EXPORTANON;
2275				opt_flags |= OP_MAPALL;
2276			} else
2277				opt_flags |= OP_MAPROOT;
2278		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
2279		    !strcmp(cpopt, "m"))) {
2280			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
2281				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
2282				return (1);
2283			}
2284			usedarg++;
2285			opt_flags |= OP_MASK;
2286		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
2287			!strcmp(cpopt, "n"))) {
2288			if (strchr(cpoptarg, '/') != NULL) {
2289				if (debug)
2290					fprintf(stderr, "setting OP_MASKLEN\n");
2291				opt_flags |= OP_MASKLEN;
2292			}
2293			if (grp->gr_type != GT_NULL) {
2294				syslog(LOG_ERR, "network/host conflict");
2295				return (1);
2296			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
2297				syslog(LOG_ERR, "bad net: %s", cpoptarg);
2298				return (1);
2299			}
2300			grp->gr_type = GT_NET;
2301			*has_hostp = 1;
2302			usedarg++;
2303			opt_flags |= OP_NET;
2304		} else if (!strcmp(cpopt, "alldirs")) {
2305			opt_flags |= OP_ALLDIRS;
2306		} else if (!strcmp(cpopt, "public")) {
2307			*exflagsp |= MNT_EXPUBLIC;
2308		} else if (!strcmp(cpopt, "webnfs")) {
2309			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
2310			opt_flags |= OP_MAPALL;
2311		} else if (cpoptarg && !strcmp(cpopt, "index")) {
2312			ep->ex_indexfile = strdup(cpoptarg);
2313		} else if (!strcmp(cpopt, "quiet")) {
2314			opt_flags |= OP_QUIET;
2315		} else if (cpoptarg && !strcmp(cpopt, "sec")) {
2316			if (parsesec(cpoptarg, ep))
2317				return (1);
2318			opt_flags |= OP_SEC;
2319			usedarg++;
2320		} else {
2321			syslog(LOG_ERR, "bad opt %s", cpopt);
2322			return (1);
2323		}
2324		if (usedarg >= 0) {
2325			*endcp = savedc2;
2326			**endcpp = savedc;
2327			if (usedarg > 0) {
2328				*cpp = cp;
2329				*endcpp = endcp;
2330			}
2331			return (0);
2332		}
2333		cpopt = cpoptend;
2334	}
2335	**endcpp = savedc;
2336	return (0);
2337}
2338
2339/*
2340 * Translate a character string to the corresponding list of network
2341 * addresses for a hostname.
2342 */
2343static int
2344get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
2345{
2346	struct grouplist *checkgrp;
2347	struct addrinfo *ai, *tai, hints;
2348	int ecode;
2349	char host[NI_MAXHOST];
2350
2351	if (grp->gr_type != GT_NULL) {
2352		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
2353		return (1);
2354	}
2355	memset(&hints, 0, sizeof hints);
2356	hints.ai_flags = AI_CANONNAME;
2357	hints.ai_protocol = IPPROTO_UDP;
2358	ecode = getaddrinfo(cp, NULL, &hints, &ai);
2359	if (ecode != 0) {
2360		syslog(LOG_ERR,"can't get address info for host %s", cp);
2361		return 1;
2362	}
2363	grp->gr_ptr.gt_addrinfo = ai;
2364	while (ai != NULL) {
2365		if (ai->ai_canonname == NULL) {
2366			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2367			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
2368				strlcpy(host, "?", sizeof(host));
2369			ai->ai_canonname = strdup(host);
2370			ai->ai_flags |= AI_CANONNAME;
2371		}
2372		if (debug)
2373			fprintf(stderr, "got host %s\n", ai->ai_canonname);
2374		/*
2375		 * Sanity check: make sure we don't already have an entry
2376		 * for this host in the grouplist.
2377		 */
2378		for (checkgrp = tgrp; checkgrp != NULL;
2379		    checkgrp = checkgrp->gr_next) {
2380			if (checkgrp->gr_type != GT_HOST)
2381				continue;
2382			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
2383			    tai = tai->ai_next) {
2384				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
2385					continue;
2386				if (debug)
2387					fprintf(stderr,
2388					    "ignoring duplicate host %s\n",
2389					    ai->ai_canonname);
2390				grp->gr_type = GT_IGNORE;
2391				return (0);
2392			}
2393		}
2394		ai = ai->ai_next;
2395	}
2396	grp->gr_type = GT_HOST;
2397	return (0);
2398}
2399
2400/*
2401 * Free up an exports list component
2402 */
2403static void
2404free_exp(struct exportlist *ep)
2405{
2406
2407	if (ep->ex_defdir) {
2408		free_host(ep->ex_defdir->dp_hosts);
2409		free((caddr_t)ep->ex_defdir);
2410	}
2411	if (ep->ex_fsdir)
2412		free(ep->ex_fsdir);
2413	if (ep->ex_indexfile)
2414		free(ep->ex_indexfile);
2415	free_dir(ep->ex_dirl);
2416	free((caddr_t)ep);
2417}
2418
2419/*
2420 * Free hosts.
2421 */
2422static void
2423free_host(struct hostlist *hp)
2424{
2425	struct hostlist *hp2;
2426
2427	while (hp) {
2428		hp2 = hp;
2429		hp = hp->ht_next;
2430		free((caddr_t)hp2);
2431	}
2432}
2433
2434static struct hostlist *
2435get_ht(void)
2436{
2437	struct hostlist *hp;
2438
2439	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
2440	if (hp == (struct hostlist *)NULL)
2441		out_of_mem();
2442	hp->ht_next = (struct hostlist *)NULL;
2443	hp->ht_flag = 0;
2444	return (hp);
2445}
2446
2447/*
2448 * Out of memory, fatal
2449 */
2450static void
2451out_of_mem(void)
2452{
2453
2454	syslog(LOG_ERR, "out of memory");
2455	exit(2);
2456}
2457
2458/*
2459 * Do the nmount() syscall with the update flag to push the export info into
2460 * the kernel.
2461 */
2462static int
2463do_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2464    struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
2465{
2466	struct statfs fsb1;
2467	struct addrinfo *ai;
2468	struct export_args *eap;
2469	char errmsg[255];
2470	char *cp;
2471	int done;
2472	char savedc;
2473	struct iovec *iov;
2474	int i, iovlen;
2475	int ret;
2476	struct nfsex_args nfsea;
2477
2478	eap = &nfsea.export;
2479
2480	cp = NULL;
2481	savedc = '\0';
2482	iov = NULL;
2483	iovlen = 0;
2484	ret = 0;
2485
2486	bzero(eap, sizeof (struct export_args));
2487	bzero(errmsg, sizeof(errmsg));
2488	eap->ex_flags = exflags;
2489	eap->ex_anon = *anoncrp;
2490	eap->ex_indexfile = ep->ex_indexfile;
2491	if (grp->gr_type == GT_HOST)
2492		ai = grp->gr_ptr.gt_addrinfo;
2493	else
2494		ai = NULL;
2495	eap->ex_numsecflavors = ep->ex_numsecflavors;
2496	for (i = 0; i < eap->ex_numsecflavors; i++)
2497		eap->ex_secflavors[i] = ep->ex_secflavors[i];
2498	if (eap->ex_numsecflavors == 0) {
2499		eap->ex_numsecflavors = 1;
2500		eap->ex_secflavors[0] = AUTH_SYS;
2501	}
2502	done = FALSE;
2503
2504	if (v4root_phase == 0) {
2505		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2506		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2507		build_iovec(&iov, &iovlen, "from", NULL, 0);
2508		build_iovec(&iov, &iovlen, "update", NULL, 0);
2509		build_iovec(&iov, &iovlen, "export", eap,
2510		    sizeof (struct export_args));
2511		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2512	}
2513
2514	while (!done) {
2515		switch (grp->gr_type) {
2516		case GT_HOST:
2517			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
2518				goto skip;
2519			eap->ex_addr = ai->ai_addr;
2520			eap->ex_addrlen = ai->ai_addrlen;
2521			eap->ex_masklen = 0;
2522			break;
2523		case GT_NET:
2524			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
2525			    have_v6 == 0)
2526				goto skip;
2527			eap->ex_addr =
2528			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2529			eap->ex_addrlen =
2530			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2531			eap->ex_mask =
2532			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2533			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
2534			break;
2535		case GT_DEFAULT:
2536			eap->ex_addr = NULL;
2537			eap->ex_addrlen = 0;
2538			eap->ex_mask = NULL;
2539			eap->ex_masklen = 0;
2540			break;
2541		case GT_IGNORE:
2542			ret = 0;
2543			goto error_exit;
2544			break;
2545		default:
2546			syslog(LOG_ERR, "bad grouptype");
2547			if (cp)
2548				*cp = savedc;
2549			ret = 1;
2550			goto error_exit;
2551		}
2552
2553		/*
2554		 * For V4:, use the nfssvc() syscall, instead of mount().
2555		 */
2556		if (v4root_phase == 2) {
2557			nfsea.fspec = v4root_dirpath;
2558			if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
2559				syslog(LOG_ERR, "Exporting V4: failed");
2560				return (2);
2561			}
2562		} else {
2563			/*
2564			 * XXX:
2565			 * Maybe I should just use the fsb->f_mntonname path
2566			 * instead of looping back up the dirp to the mount
2567			 * point??
2568			 * Also, needs to know how to export all types of local
2569			 * exportable filesystems and not just "ufs".
2570			 */
2571			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2572			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2573			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2574			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2575			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2576			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2577			errmsg[0] = '\0';
2578
2579			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2580				if (cp)
2581					*cp-- = savedc;
2582				else
2583					cp = dirp + dirplen - 1;
2584				if (opt_flags & OP_QUIET) {
2585					ret = 1;
2586					goto error_exit;
2587				}
2588				if (errno == EPERM) {
2589					if (debug)
2590						warnx("can't change attributes for %s: %s",
2591						    dirp, errmsg);
2592					syslog(LOG_ERR,
2593					   "can't change attributes for %s: %s",
2594					    dirp, errmsg);
2595					ret = 1;
2596					goto error_exit;
2597				}
2598				if (opt_flags & OP_ALLDIRS) {
2599					if (errno == EINVAL)
2600						syslog(LOG_ERR,
2601		"-alldirs requested but %s is not a filesystem mountpoint",
2602						    dirp);
2603					else
2604						syslog(LOG_ERR,
2605						    "could not remount %s: %m",
2606						    dirp);
2607					ret = 1;
2608					goto error_exit;
2609				}
2610				/* back up over the last component */
2611				while (*cp == '/' && cp > dirp)
2612					cp--;
2613				while (*(cp - 1) != '/' && cp > dirp)
2614					cp--;
2615				if (cp == dirp) {
2616					if (debug)
2617						warnx("mnt unsucc");
2618					syslog(LOG_ERR, "can't export %s %s",
2619					    dirp, errmsg);
2620					ret = 1;
2621					goto error_exit;
2622				}
2623				savedc = *cp;
2624				*cp = '\0';
2625				/*
2626				 * Check that we're still on the same
2627				 * filesystem.
2628				 */
2629				if (statfs(dirp, &fsb1) != 0 ||
2630				    bcmp(&fsb1.f_fsid, &fsb->f_fsid,
2631				    sizeof (fsb1.f_fsid)) != 0) {
2632					*cp = savedc;
2633					syslog(LOG_ERR,
2634					    "can't export %s %s", dirp,
2635					    errmsg);
2636					ret = 1;
2637					goto error_exit;
2638				}
2639			}
2640		}
2641
2642		/*
2643		 * For the experimental server:
2644		 * If this is the public directory, get the file handle
2645		 * and load it into the kernel via the nfssvc() syscall.
2646		 */
2647		if ((exflags & MNT_EXPUBLIC) != 0) {
2648			fhandle_t fh;
2649			char *public_name;
2650
2651			if (eap->ex_indexfile != NULL)
2652				public_name = eap->ex_indexfile;
2653			else
2654				public_name = dirp;
2655			if (getfh(public_name, &fh) < 0)
2656				syslog(LOG_ERR,
2657				    "Can't get public fh for %s", public_name);
2658			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
2659				syslog(LOG_ERR,
2660				    "Can't set public fh for %s", public_name);
2661			else
2662				has_publicfh = 1;
2663		}
2664skip:
2665		if (ai != NULL)
2666			ai = ai->ai_next;
2667		if (ai == NULL)
2668			done = TRUE;
2669	}
2670	if (cp)
2671		*cp = savedc;
2672error_exit:
2673	/* free strings allocated by strdup() in getmntopts.c */
2674	if (iov != NULL) {
2675		free(iov[0].iov_base); /* fstype */
2676		free(iov[2].iov_base); /* fspath */
2677		free(iov[4].iov_base); /* from */
2678		free(iov[6].iov_base); /* update */
2679		free(iov[8].iov_base); /* export */
2680		free(iov[10].iov_base); /* errmsg */
2681
2682		/* free iov, allocated by realloc() */
2683		free(iov);
2684	}
2685	return (ret);
2686}
2687
2688/*
2689 * Translate a net address.
2690 *
2691 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
2692 */
2693static int
2694get_net(char *cp, struct netmsk *net, int maskflg)
2695{
2696	struct netent *np = NULL;
2697	char *name, *p, *prefp;
2698	struct sockaddr_in sin;
2699	struct sockaddr *sa = NULL;
2700	struct addrinfo hints, *ai = NULL;
2701	char netname[NI_MAXHOST];
2702	long preflen;
2703
2704	p = prefp = NULL;
2705	if ((opt_flags & OP_MASKLEN) && !maskflg) {
2706		p = strchr(cp, '/');
2707		*p = '\0';
2708		prefp = p + 1;
2709	}
2710
2711	/*
2712	 * Check for a numeric address first. We wish to avoid
2713	 * possible DNS lookups in getnetbyname().
2714	 */
2715	if (isxdigit(*cp) || *cp == ':') {
2716		memset(&hints, 0, sizeof hints);
2717		/* Ensure the mask and the network have the same family. */
2718		if (maskflg && (opt_flags & OP_NET))
2719			hints.ai_family = net->nt_net.ss_family;
2720		else if (!maskflg && (opt_flags & OP_HAVEMASK))
2721			hints.ai_family = net->nt_mask.ss_family;
2722		else
2723			hints.ai_family = AF_UNSPEC;
2724		hints.ai_flags = AI_NUMERICHOST;
2725		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
2726			sa = ai->ai_addr;
2727		if (sa != NULL && ai->ai_family == AF_INET) {
2728			/*
2729			 * The address in `cp' is really a network address, so
2730			 * use inet_network() to re-interpret this correctly.
2731			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
2732			 */
2733			bzero(&sin, sizeof sin);
2734			sin.sin_family = AF_INET;
2735			sin.sin_len = sizeof sin;
2736			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
2737			if (debug)
2738				fprintf(stderr, "get_net: v4 addr %s\n",
2739				    inet_ntoa(sin.sin_addr));
2740			sa = (struct sockaddr *)&sin;
2741		}
2742	}
2743	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
2744		bzero(&sin, sizeof sin);
2745		sin.sin_family = AF_INET;
2746		sin.sin_len = sizeof sin;
2747		sin.sin_addr = inet_makeaddr(np->n_net, 0);
2748		sa = (struct sockaddr *)&sin;
2749	}
2750	if (sa == NULL)
2751		goto fail;
2752
2753	if (maskflg) {
2754		/* The specified sockaddr is a mask. */
2755		if (checkmask(sa) != 0)
2756			goto fail;
2757		bcopy(sa, &net->nt_mask, sa->sa_len);
2758		opt_flags |= OP_HAVEMASK;
2759	} else {
2760		/* The specified sockaddr is a network address. */
2761		bcopy(sa, &net->nt_net, sa->sa_len);
2762
2763		/* Get a network name for the export list. */
2764		if (np) {
2765			name = np->n_name;
2766		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2767		   NULL, 0, NI_NUMERICHOST) == 0) {
2768			name = netname;
2769		} else {
2770			goto fail;
2771		}
2772		if ((net->nt_name = strdup(name)) == NULL)
2773			out_of_mem();
2774
2775		/*
2776		 * Extract a mask from either a "/<masklen>" suffix, or
2777		 * from the class of an IPv4 address.
2778		 */
2779		if (opt_flags & OP_MASKLEN) {
2780			preflen = strtol(prefp, NULL, 10);
2781			if (preflen < 0L || preflen == LONG_MAX)
2782				goto fail;
2783			bcopy(sa, &net->nt_mask, sa->sa_len);
2784			if (makemask(&net->nt_mask, (int)preflen) != 0)
2785				goto fail;
2786			opt_flags |= OP_HAVEMASK;
2787			*p = '/';
2788		} else if (sa->sa_family == AF_INET &&
2789		    (opt_flags & OP_MASK) == 0) {
2790			in_addr_t addr;
2791
2792			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
2793			if (IN_CLASSA(addr))
2794				preflen = 8;
2795			else if (IN_CLASSB(addr))
2796				preflen = 16;
2797			else if (IN_CLASSC(addr))
2798				preflen = 24;
2799			else if (IN_CLASSD(addr))
2800				preflen = 28;
2801			else
2802				preflen = 32;	/* XXX */
2803
2804			bcopy(sa, &net->nt_mask, sa->sa_len);
2805			makemask(&net->nt_mask, (int)preflen);
2806			opt_flags |= OP_HAVEMASK;
2807		}
2808	}
2809
2810	if (ai)
2811		freeaddrinfo(ai);
2812	return 0;
2813
2814fail:
2815	if (ai)
2816		freeaddrinfo(ai);
2817	return 1;
2818}
2819
2820/*
2821 * Parse out the next white space separated field
2822 */
2823static void
2824nextfield(char **cp, char **endcp)
2825{
2826	char *p;
2827
2828	p = *cp;
2829	while (*p == ' ' || *p == '\t')
2830		p++;
2831	if (*p == '\n' || *p == '\0')
2832		*cp = *endcp = p;
2833	else {
2834		*cp = p++;
2835		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
2836			p++;
2837		*endcp = p;
2838	}
2839}
2840
2841/*
2842 * Get an exports file line. Skip over blank lines and handle line
2843 * continuations.
2844 */
2845static int
2846get_line(void)
2847{
2848	char *p, *cp;
2849	size_t len;
2850	int totlen, cont_line;
2851
2852	/*
2853	 * Loop around ignoring blank lines and getting all continuation lines.
2854	 */
2855	p = line;
2856	totlen = 0;
2857	do {
2858		if ((p = fgetln(exp_file, &len)) == NULL)
2859			return (0);
2860		cp = p + len - 1;
2861		cont_line = 0;
2862		while (cp >= p &&
2863		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
2864			if (*cp == '\\')
2865				cont_line = 1;
2866			cp--;
2867			len--;
2868		}
2869		if (cont_line) {
2870			*++cp = ' ';
2871			len++;
2872		}
2873		if (linesize < len + totlen + 1) {
2874			linesize = len + totlen + 1;
2875			line = realloc(line, linesize);
2876			if (line == NULL)
2877				out_of_mem();
2878		}
2879		memcpy(line + totlen, p, len);
2880		totlen += len;
2881		line[totlen] = '\0';
2882	} while (totlen == 0 || cont_line);
2883	return (1);
2884}
2885
2886/*
2887 * Parse a description of a credential.
2888 */
2889static void
2890parsecred(char *namelist, struct xucred *cr)
2891{
2892	char *name;
2893	int cnt;
2894	char *names;
2895	struct passwd *pw;
2896	struct group *gr;
2897	gid_t groups[XU_NGROUPS + 1];
2898	int ngroups;
2899
2900	cr->cr_version = XUCRED_VERSION;
2901	/*
2902	 * Set up the unprivileged user.
2903	 */
2904	cr->cr_uid = 65534;
2905	cr->cr_groups[0] = 65533;
2906	cr->cr_ngroups = 1;
2907	/*
2908	 * Get the user's password table entry.
2909	 */
2910	names = strsep_quote(&namelist, " \t\n");
2911	name = strsep(&names, ":");
2912	/* Bug?  name could be NULL here */
2913	if (isdigit(*name) || *name == '-')
2914		pw = getpwuid(atoi(name));
2915	else
2916		pw = getpwnam(name);
2917	/*
2918	 * Credentials specified as those of a user.
2919	 */
2920	if (names == NULL) {
2921		if (pw == NULL) {
2922			syslog(LOG_ERR, "unknown user: %s", name);
2923			return;
2924		}
2925		cr->cr_uid = pw->pw_uid;
2926		ngroups = XU_NGROUPS + 1;
2927		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) {
2928			syslog(LOG_ERR, "too many groups");
2929			ngroups = XU_NGROUPS + 1;
2930		}
2931
2932		/*
2933		 * Compress out duplicate.
2934		 */
2935		cr->cr_ngroups = ngroups - 1;
2936		cr->cr_groups[0] = groups[0];
2937		for (cnt = 2; cnt < ngroups; cnt++)
2938			cr->cr_groups[cnt - 1] = groups[cnt];
2939		return;
2940	}
2941	/*
2942	 * Explicit credential specified as a colon separated list:
2943	 *	uid:gid:gid:...
2944	 */
2945	if (pw != NULL)
2946		cr->cr_uid = pw->pw_uid;
2947	else if (isdigit(*name) || *name == '-')
2948		cr->cr_uid = atoi(name);
2949	else {
2950		syslog(LOG_ERR, "unknown user: %s", name);
2951		return;
2952	}
2953	cr->cr_ngroups = 0;
2954	while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
2955		name = strsep(&names, ":");
2956		if (isdigit(*name) || *name == '-') {
2957			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
2958		} else {
2959			if ((gr = getgrnam(name)) == NULL) {
2960				syslog(LOG_ERR, "unknown group: %s", name);
2961				continue;
2962			}
2963			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2964		}
2965	}
2966	if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
2967		syslog(LOG_ERR, "too many groups");
2968}
2969
2970#define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
2971/*
2972 * Routines that maintain the remote mounttab
2973 */
2974static void
2975get_mountlist(void)
2976{
2977	struct mountlist *mlp;
2978	char *host, *dirp, *cp;
2979	char str[STRSIZ];
2980	FILE *mlfile;
2981
2982	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2983		if (errno == ENOENT)
2984			return;
2985		else {
2986			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
2987			return;
2988		}
2989	}
2990	while (fgets(str, STRSIZ, mlfile) != NULL) {
2991		cp = str;
2992		host = strsep(&cp, " \t\n");
2993		dirp = strsep(&cp, " \t\n");
2994		if (host == NULL || dirp == NULL)
2995			continue;
2996		mlp = (struct mountlist *)malloc(sizeof (*mlp));
2997		if (mlp == (struct mountlist *)NULL)
2998			out_of_mem();
2999		strncpy(mlp->ml_host, host, MNTNAMLEN);
3000		mlp->ml_host[MNTNAMLEN] = '\0';
3001		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3002		mlp->ml_dirp[MNTPATHLEN] = '\0';
3003
3004		SLIST_INSERT_HEAD(&mlhead, mlp, next);
3005	}
3006	fclose(mlfile);
3007}
3008
3009static void
3010del_mlist(char *hostp, char *dirp)
3011{
3012	struct mountlist *mlp, *mlp2;
3013	FILE *mlfile;
3014	int fnd = 0;
3015
3016	SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) {
3017		if (!strcmp(mlp->ml_host, hostp) &&
3018		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
3019			fnd = 1;
3020			SLIST_REMOVE(&mlhead, mlp, mountlist, next);
3021			free((caddr_t)mlp);
3022		}
3023	}
3024	if (fnd) {
3025		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
3026			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
3027			return;
3028		}
3029		SLIST_FOREACH(mlp, &mlhead, next) {
3030			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3031		}
3032		fclose(mlfile);
3033	}
3034}
3035
3036static void
3037add_mlist(char *hostp, char *dirp)
3038{
3039	struct mountlist *mlp;
3040	FILE *mlfile;
3041
3042	SLIST_FOREACH(mlp, &mlhead, next) {
3043		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
3044			return;
3045	}
3046
3047	mlp = (struct mountlist *)malloc(sizeof (*mlp));
3048	if (mlp == (struct mountlist *)NULL)
3049		out_of_mem();
3050	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3051	mlp->ml_host[MNTNAMLEN] = '\0';
3052	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3053	mlp->ml_dirp[MNTPATHLEN] = '\0';
3054	SLIST_INSERT_HEAD(&mlhead, mlp, next);
3055	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
3056		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
3057		return;
3058	}
3059	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3060	fclose(mlfile);
3061}
3062
3063/*
3064 * Free up a group list.
3065 */
3066static void
3067free_grp(struct grouplist *grp)
3068{
3069	if (grp->gr_type == GT_HOST) {
3070		if (grp->gr_ptr.gt_addrinfo != NULL)
3071			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
3072	} else if (grp->gr_type == GT_NET) {
3073		if (grp->gr_ptr.gt_net.nt_name)
3074			free(grp->gr_ptr.gt_net.nt_name);
3075	}
3076	free((caddr_t)grp);
3077}
3078
3079#ifdef DEBUG
3080static void
3081SYSLOG(int pri, const char *fmt, ...)
3082{
3083	va_list ap;
3084
3085	va_start(ap, fmt);
3086	vfprintf(stderr, fmt, ap);
3087	va_end(ap);
3088}
3089#endif /* DEBUG */
3090
3091/*
3092 * Check options for consistency.
3093 */
3094static int
3095check_options(struct dirlist *dp)
3096{
3097
3098	if (v4root_phase == 0 && dp == NULL)
3099	    return (1);
3100	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
3101	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
3102	    return (1);
3103	}
3104	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
3105		syslog(LOG_ERR, "-mask requires -network");
3106		return (1);
3107	}
3108	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
3109		syslog(LOG_ERR, "-network requires mask specification");
3110		return (1);
3111	}
3112	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
3113		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
3114		return (1);
3115	}
3116	if (v4root_phase > 0 &&
3117	    (opt_flags &
3118	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3119	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3120	    return (1);
3121	}
3122	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3123	    syslog(LOG_ERR, "-alldirs has multiple directories");
3124	    return (1);
3125	}
3126	return (0);
3127}
3128
3129/*
3130 * Check an absolute directory path for any symbolic links. Return true
3131 */
3132static int
3133check_dirpath(char *dirp)
3134{
3135	char *cp;
3136	int ret = 1;
3137	struct stat sb;
3138
3139	cp = dirp + 1;
3140	while (*cp && ret) {
3141		if (*cp == '/') {
3142			*cp = '\0';
3143			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3144				ret = 0;
3145			*cp = '/';
3146		}
3147		cp++;
3148	}
3149	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3150		ret = 0;
3151	return (ret);
3152}
3153
3154/*
3155 * Make a netmask according to the specified prefix length. The ss_family
3156 * and other non-address fields must be initialised before calling this.
3157 */
3158static int
3159makemask(struct sockaddr_storage *ssp, int bitlen)
3160{
3161	u_char *p;
3162	int bits, i, len;
3163
3164	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
3165		return (-1);
3166	if (bitlen > len * CHAR_BIT)
3167		return (-1);
3168
3169	for (i = 0; i < len; i++) {
3170		bits = MIN(CHAR_BIT, bitlen);
3171		*p++ = (u_char)~0 << (CHAR_BIT - bits);
3172		bitlen -= bits;
3173	}
3174	return 0;
3175}
3176
3177/*
3178 * Check that the sockaddr is a valid netmask. Returns 0 if the mask
3179 * is acceptable (i.e. of the form 1...10....0).
3180 */
3181static int
3182checkmask(struct sockaddr *sa)
3183{
3184	u_char *mask;
3185	int i, len;
3186
3187	if ((mask = sa_rawaddr(sa, &len)) == NULL)
3188		return (-1);
3189
3190	for (i = 0; i < len; i++)
3191		if (mask[i] != 0xff)
3192			break;
3193	if (i < len) {
3194		if (~mask[i] & (u_char)(~mask[i] + 1))
3195			return (-1);
3196		i++;
3197	}
3198	for (; i < len; i++)
3199		if (mask[i] != 0)
3200			return (-1);
3201	return (0);
3202}
3203
3204/*
3205 * Compare two sockaddrs according to a specified mask. Return zero if
3206 * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3207 * If samask is NULL, perform a full comparison.
3208 */
3209static int
3210sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
3211{
3212	unsigned char *p1, *p2, *mask;
3213	int len, i;
3214
3215	if (sa1->sa_family != sa2->sa_family ||
3216	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
3217	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
3218		return (1);
3219
3220	switch (sa1->sa_family) {
3221	case AF_INET6:
3222		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
3223		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
3224			return (1);
3225		break;
3226	}
3227
3228	/* Simple binary comparison if no mask specified. */
3229	if (samask == NULL)
3230		return (memcmp(p1, p2, len));
3231
3232	/* Set up the mask, and do a mask-based comparison. */
3233	if (sa1->sa_family != samask->sa_family ||
3234	    (mask = sa_rawaddr(samask, NULL)) == NULL)
3235		return (1);
3236
3237	for (i = 0; i < len; i++)
3238		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
3239			return (1);
3240	return (0);
3241}
3242
3243/*
3244 * Return a pointer to the part of the sockaddr that contains the
3245 * raw address, and set *nbytes to its length in bytes. Returns
3246 * NULL if the address family is unknown.
3247 */
3248static void *
3249sa_rawaddr(struct sockaddr *sa, int *nbytes) {
3250	void *p;
3251	int len;
3252
3253	switch (sa->sa_family) {
3254	case AF_INET:
3255		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
3256		p = &((struct sockaddr_in *)sa)->sin_addr;
3257		break;
3258	case AF_INET6:
3259		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
3260		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
3261		break;
3262	default:
3263		p = NULL;
3264		len = 0;
3265	}
3266
3267	if (nbytes != NULL)
3268		*nbytes = len;
3269	return (p);
3270}
3271
3272static void
3273huphandler(int sig __unused)
3274{
3275
3276	got_sighup = 1;
3277}
3278
3279static void
3280terminate(int sig __unused)
3281{
3282	pidfile_remove(pfh);
3283	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3284	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
3285	exit (0);
3286}
3287