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