mountd.c revision 349124
1331722Seadler/*
21558Srgrimes * Copyright (c) 1989, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * This code is derived from software contributed to Berkeley by
61558Srgrimes * Herb Hasler and Rick Macklem at The University of Guelph.
71558Srgrimes *
81558Srgrimes * Redistribution and use in source and binary forms, with or without
91558Srgrimes * modification, are permitted provided that the following conditions
101558Srgrimes * are met:
111558Srgrimes * 1. Redistributions of source code must retain the above copyright
121558Srgrimes *    notice, this list of conditions and the following disclaimer.
131558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141558Srgrimes *    notice, this list of conditions and the following disclaimer in the
151558Srgrimes *    documentation and/or other materials provided with the distribution.
161558Srgrimes * 4. Neither the name of the University nor the names of its contributors
171558Srgrimes *    may be used to endorse or promote products derived from this software
181558Srgrimes *    without specific prior written permission.
191558Srgrimes *
201558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301558Srgrimes * SUCH DAMAGE.
311558Srgrimes */
321558Srgrimes
331558Srgrimes#ifndef lint
3437663Scharnierstatic const char copyright[] =
351558Srgrimes"@(#) Copyright (c) 1989, 1993\n\
361558Srgrimes	The Regents of the University of California.  All rights reserved.\n";
372999Swollman#endif /*not lint*/
381558Srgrimes
39105267Scharnier#if 0
401558Srgrimes#ifndef lint
4137663Scharnierstatic char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95";
42105267Scharnier#endif /*not lint*/
4337663Scharnier#endif
441558Srgrimes
45105267Scharnier#include <sys/cdefs.h>
46105267Scharnier__FBSDID("$FreeBSD: stable/11/usr.sbin/mountd/mountd.c 349124 2019-06-17 00:11:46Z rmacklem $");
47105267Scharnier
481558Srgrimes#include <sys/param.h>
49192934Srmacklem#include <sys/fcntl.h>
50192934Srmacklem#include <sys/linker.h>
51192934Srmacklem#include <sys/module.h>
521558Srgrimes#include <sys/mount.h>
53324955Smanu#include <sys/queue.h>
541558Srgrimes#include <sys/stat.h>
55192934Srmacklem#include <sys/sysctl.h>
561558Srgrimes#include <sys/syslog.h>
571558Srgrimes
581558Srgrimes#include <rpc/rpc.h>
59109363Smbr#include <rpc/rpc_com.h>
601558Srgrimes#include <rpc/pmap_clnt.h>
6174462Salfred#include <rpc/pmap_prot.h>
6274462Salfred#include <rpcsvc/mount.h>
639336Sdfr#include <nfs/nfsproto.h>
64192934Srmacklem#include <nfs/nfssvc.h>
6583653Speter#include <nfsserver/nfs.h>
661558Srgrimes
67192934Srmacklem#include <fs/nfs/nfsport.h>
68192934Srmacklem
691558Srgrimes#include <arpa/inet.h>
701558Srgrimes
711558Srgrimes#include <ctype.h>
7237663Scharnier#include <err.h>
731558Srgrimes#include <errno.h>
741558Srgrimes#include <grp.h>
75149433Spjd#include <libutil.h>
76103949Smike#include <limits.h>
771558Srgrimes#include <netdb.h>
781558Srgrimes#include <pwd.h>
791558Srgrimes#include <signal.h>
801558Srgrimes#include <stdio.h>
811558Srgrimes#include <stdlib.h>
821558Srgrimes#include <string.h>
831558Srgrimes#include <unistd.h>
841558Srgrimes#include "pathnames.h"
85158857Srodrigc#include "mntopts.h"
861558Srgrimes
871558Srgrimes#ifdef DEBUG
881558Srgrimes#include <stdarg.h>
891558Srgrimes#endif
901558Srgrimes
911558Srgrimes/*
921558Srgrimes * Structures for keeping the mount list and export list
931558Srgrimes */
941558Srgrimesstruct mountlist {
95194880Sdfr	char	ml_host[MNTNAMLEN+1];
96194880Sdfr	char	ml_dirp[MNTPATHLEN+1];
97324955Smanu
98324955Smanu	SLIST_ENTRY(mountlist)	next;
991558Srgrimes};
1001558Srgrimes
1011558Srgrimesstruct dirlist {
1021558Srgrimes	struct dirlist	*dp_left;
1031558Srgrimes	struct dirlist	*dp_right;
1041558Srgrimes	int		dp_flag;
1051558Srgrimes	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
106324234Smanu	char		*dp_dirp;
1071558Srgrimes};
1081558Srgrimes/* dp_flag bits */
1091558Srgrimes#define	DP_DEFSET	0x1
1109336Sdfr#define DP_HOSTSET	0x2
1111558Srgrimes
1121558Srgrimesstruct exportlist {
1131558Srgrimes	struct dirlist	*ex_dirl;
1141558Srgrimes	struct dirlist	*ex_defdir;
1151558Srgrimes	int		ex_flag;
1161558Srgrimes	fsid_t		ex_fs;
1171558Srgrimes	char		*ex_fsdir;
11827447Sdfr	char		*ex_indexfile;
119184588Sdfr	int		ex_numsecflavors;
120184588Sdfr	int		ex_secflavors[MAXSECFLAVORS];
121240902Srmacklem	int		ex_defnumsecflavors;
122240902Srmacklem	int		ex_defsecflavors[MAXSECFLAVORS];
123324955Smanu
124324955Smanu	SLIST_ENTRY(exportlist) entries;
1251558Srgrimes};
1261558Srgrimes/* ex_flag bits */
1271558Srgrimes#define	EX_LINKED	0x1
1281558Srgrimes
129349124SrmacklemSLIST_HEAD(exportlisthead, exportlist);
130349124Srmacklem
1311558Srgrimesstruct netmsk {
13274462Salfred	struct sockaddr_storage nt_net;
13375801Siedowse	struct sockaddr_storage nt_mask;
13442144Sdfr	char		*nt_name;
1351558Srgrimes};
1361558Srgrimes
1371558Srgrimesunion grouptypes {
13874462Salfred	struct addrinfo *gt_addrinfo;
1391558Srgrimes	struct netmsk	gt_net;
1401558Srgrimes};
1411558Srgrimes
1421558Srgrimesstruct grouplist {
1431558Srgrimes	int gr_type;
1441558Srgrimes	union grouptypes gr_ptr;
1451558Srgrimes	struct grouplist *gr_next;
146240902Srmacklem	int gr_numsecflavors;
147240902Srmacklem	int gr_secflavors[MAXSECFLAVORS];
1481558Srgrimes};
1491558Srgrimes/* Group types */
1501558Srgrimes#define	GT_NULL		0x0
1511558Srgrimes#define	GT_HOST		0x1
1521558Srgrimes#define	GT_NET		0x2
15375641Siedowse#define	GT_DEFAULT	0x3
1547401Swpaul#define GT_IGNORE	0x5
1551558Srgrimes
1561558Srgrimesstruct hostlist {
1579336Sdfr	int		 ht_flag;	/* Uses DP_xx bits */
1581558Srgrimes	struct grouplist *ht_grp;
1591558Srgrimes	struct hostlist	 *ht_next;
1601558Srgrimes};
1611558Srgrimes
1629336Sdfrstruct fhreturn {
1639336Sdfr	int	fhr_flag;
1649336Sdfr	int	fhr_vers;
1659336Sdfr	nfsfh_t	fhr_fh;
166184588Sdfr	int	fhr_numsecflavors;
167184588Sdfr	int	*fhr_secflavors;
1689336Sdfr};
1699336Sdfr
170222623Srmacklem#define	GETPORT_MAXTRY	20	/* Max tries to get a port # */
171222623Srmacklem
1721558Srgrimes/* Global defs */
173285128Straszstatic char	*add_expdir(struct dirlist **, char *, int);
174285128Straszstatic void	add_dlist(struct dirlist **, struct dirlist *,
175285128Strasz		    struct grouplist *, int, struct exportlist *);
176285128Straszstatic void	add_mlist(char *, char *);
177285128Straszstatic int	check_dirpath(char *);
178285128Straszstatic int	check_options(struct dirlist *);
179285128Straszstatic int	checkmask(struct sockaddr *sa);
180285128Straszstatic int	chk_host(struct dirlist *, struct sockaddr *, int *, int *,
181285128Strasz		    int *, int **);
182293305Sjpaetzelstatic char	*strsep_quote(char **stringp, const char *delim);
183222623Srmacklemstatic int	create_service(struct netconfig *nconf);
184222623Srmacklemstatic void	complete_service(struct netconfig *nconf, char *port_str);
185222623Srmacklemstatic void	clearout_service(void);
186285128Straszstatic void	del_mlist(char *hostp, char *dirp);
187285128Straszstatic struct dirlist	*dirp_search(struct dirlist *, char *);
188285128Straszstatic int	do_mount(struct exportlist *, struct grouplist *, int,
189285128Strasz		    struct xucred *, char *, int, struct statfs *);
190285128Straszstatic int	do_opt(char **, char **, struct exportlist *,
191285128Strasz		    struct grouplist *, int *, int *, struct xucred *);
192349124Srmacklemstatic struct exportlist	*ex_search(fsid_t *, struct exportlisthead *);
193285128Straszstatic struct exportlist	*get_exp(void);
194285128Straszstatic void	free_dir(struct dirlist *);
195285128Straszstatic void	free_exp(struct exportlist *);
196285128Straszstatic void	free_grp(struct grouplist *);
197285128Straszstatic void	free_host(struct hostlist *);
198285128Straszstatic void	get_exportlist(void);
199349124Srmacklemstatic void	insert_exports(struct exportlist *, struct exportlisthead *);
200349124Srmacklemstatic void	free_exports(struct exportlisthead *);
201285128Straszstatic int	get_host(char *, struct grouplist *, struct grouplist *);
202285128Straszstatic struct hostlist *get_ht(void);
203285128Straszstatic int	get_line(void);
204285128Straszstatic void	get_mountlist(void);
205285128Straszstatic int	get_net(char *, struct netmsk *, int);
206329392Sbrdstatic void	getexp_err(struct exportlist *, struct grouplist *, const char *);
207285128Straszstatic struct grouplist	*get_grp(void);
208285128Straszstatic void	hang_dirp(struct dirlist *, struct grouplist *,
20992882Simp				struct exportlist *, int);
210285128Straszstatic void	huphandler(int sig);
211285128Straszstatic int	makemask(struct sockaddr_storage *ssp, int bitlen);
212285128Straszstatic void	mntsrv(struct svc_req *, SVCXPRT *);
213285128Straszstatic void	nextfield(char **, char **);
214285128Straszstatic void	out_of_mem(void);
215285128Straszstatic void	parsecred(char *, struct xucred *);
216285128Straszstatic int	parsesec(char *, struct exportlist *);
217285128Straszstatic int	put_exlist(struct dirlist *, XDR *, struct dirlist *,
218285128Strasz		    int *, int);
219285128Straszstatic void	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
220285128Straszstatic int	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
221285128Strasz		    struct sockaddr *samask);
222285128Straszstatic int	scan_tree(struct dirlist *, struct sockaddr *);
223285128Straszstatic void	usage(void);
224285128Straszstatic int	xdr_dir(XDR *, char *);
225285128Straszstatic int	xdr_explist(XDR *, caddr_t);
226285128Straszstatic int	xdr_explist_brief(XDR *, caddr_t);
227285128Straszstatic int	xdr_explist_common(XDR *, caddr_t, int);
228285128Straszstatic int	xdr_fhs(XDR *, caddr_t);
229285128Straszstatic int	xdr_mlist(XDR *, caddr_t);
230285128Straszstatic void	terminate(int);
2311558Srgrimes
232349124Srmacklemstatic struct exportlisthead exphead = SLIST_HEAD_INITIALIZER(&exphead);
233349124Srmacklemstatic SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead);
234285128Straszstatic struct grouplist *grphead;
235285128Straszstatic char *exnames_default[2] = { _PATH_EXPORTS, NULL };
236285128Straszstatic char **exnames;
237285128Straszstatic char **hosts = NULL;
238285128Straszstatic struct xucred def_anon = {
23991354Sdd	XUCRED_VERSION,
24072650Sgreen	(uid_t)-2,
2411558Srgrimes	1,
24272650Sgreen	{ (gid_t)-2 },
24372650Sgreen	NULL
2441558Srgrimes};
245285128Straszstatic int force_v2 = 0;
246285128Straszstatic int resvport_only = 1;
247285128Straszstatic int nhosts = 0;
248285128Straszstatic int dir_only = 1;
249285128Straszstatic int dolog = 0;
250285128Straszstatic int got_sighup = 0;
251285128Straszstatic int xcreated = 0;
25274462Salfred
253285128Straszstatic char *svcport_str = NULL;
254285128Straszstatic int mallocd_svcport = 0;
255285128Straszstatic int *sock_fd;
256285128Straszstatic int sock_fdcnt;
257285128Straszstatic int sock_fdpos;
258285128Straszstatic int suspend_nfsd = 0;
259172827Smatteo
260285128Straszstatic int opt_flags;
26174462Salfredstatic int have_v6 = 1;
26274462Salfred
263285128Straszstatic int v4root_phase = 0;
264285128Straszstatic char v4root_dirpath[PATH_MAX + 1];
265285128Straszstatic int has_publicfh = 0;
266192934Srmacklem
267285128Straszstatic struct pidfh *pfh = NULL;
26875801Siedowse/* Bits for opt_flags above */
2691558Srgrimes#define	OP_MAPROOT	0x01
2701558Srgrimes#define	OP_MAPALL	0x02
27183653Speter/* 0x4 free */
2721558Srgrimes#define	OP_MASK		0x08
2731558Srgrimes#define	OP_NET		0x10
2741558Srgrimes#define	OP_ALLDIRS	0x40
27575801Siedowse#define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
276100336Sjoerg#define	OP_QUIET	0x100
27774462Salfred#define OP_MASKLEN	0x200
278184588Sdfr#define OP_SEC		0x400
2791558Srgrimes
2801558Srgrimes#ifdef DEBUG
281285128Straszstatic int debug = 1;
282285128Straszstatic void	SYSLOG(int, const char *, ...) __printflike(2, 3);
2831558Srgrimes#define syslog SYSLOG
2841558Srgrimes#else
285285128Straszstatic int debug = 0;
2861558Srgrimes#endif
2871558Srgrimes
2881558Srgrimes/*
289293305Sjpaetzel * Similar to strsep(), but it allows for quoted strings
290293305Sjpaetzel * and escaped characters.
291293305Sjpaetzel *
292293305Sjpaetzel * It returns the string (or NULL, if *stringp is NULL),
293293305Sjpaetzel * which is a de-quoted version of the string if necessary.
294293305Sjpaetzel *
295293305Sjpaetzel * It modifies *stringp in place.
296293305Sjpaetzel */
297293305Sjpaetzelstatic char *
298293305Sjpaetzelstrsep_quote(char **stringp, const char *delim)
299293305Sjpaetzel{
300293305Sjpaetzel	char *srcptr, *dstptr, *retval;
301293305Sjpaetzel	char quot = 0;
302293305Sjpaetzel
303293305Sjpaetzel	if (stringp == NULL || *stringp == NULL)
304293305Sjpaetzel		return (NULL);
305293305Sjpaetzel
306293305Sjpaetzel	srcptr = dstptr = retval = *stringp;
307293305Sjpaetzel
308293305Sjpaetzel	while (*srcptr) {
309293305Sjpaetzel		/*
310293305Sjpaetzel		 * We're looking for several edge cases here.
311293305Sjpaetzel		 * First:  if we're in quote state (quot != 0),
312293305Sjpaetzel		 * then we ignore the delim characters, but otherwise
313293305Sjpaetzel		 * process as normal, unless it is the quote character.
314293305Sjpaetzel		 * Second:  if the current character is a backslash,
315293305Sjpaetzel		 * we take the next character as-is, without checking
316293305Sjpaetzel		 * for delim, quote, or backslash.  Exception:  if the
317293305Sjpaetzel		 * next character is a NUL, that's the end of the string.
318293305Sjpaetzel		 * Third:  if the character is a quote character, we toggle
319293305Sjpaetzel		 * quote state.
320293305Sjpaetzel		 * Otherwise:  check the current character for NUL, or
321293305Sjpaetzel		 * being in delim, and end the string if either is true.
322293305Sjpaetzel		 */
323293305Sjpaetzel		if (*srcptr == '\\') {
324293305Sjpaetzel			srcptr++;
325293305Sjpaetzel			/*
326293305Sjpaetzel			 * The edge case here is if the next character
327293305Sjpaetzel			 * is NUL, we want to stop processing.  But if
328293305Sjpaetzel			 * it's not NUL, then we simply want to copy it.
329293305Sjpaetzel			 */
330293305Sjpaetzel			if (*srcptr) {
331293305Sjpaetzel				*dstptr++ = *srcptr++;
332293305Sjpaetzel			}
333293305Sjpaetzel			continue;
334293305Sjpaetzel		}
335293305Sjpaetzel		if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) {
336293305Sjpaetzel			quot = *srcptr++;
337293305Sjpaetzel			continue;
338293305Sjpaetzel		}
339293305Sjpaetzel		if (quot && *srcptr == quot) {
340293305Sjpaetzel			/* End of the quoted part */
341293305Sjpaetzel			quot = 0;
342293305Sjpaetzel			srcptr++;
343293305Sjpaetzel			continue;
344293305Sjpaetzel		}
345293305Sjpaetzel		if (!quot && strchr(delim, *srcptr))
346293305Sjpaetzel			break;
347293305Sjpaetzel		*dstptr++ = *srcptr++;
348293305Sjpaetzel	}
349293305Sjpaetzel
350293305Sjpaetzel	*dstptr = 0; /* Terminate the string */
351293305Sjpaetzel	*stringp = (*srcptr == '\0') ? NULL : srcptr + 1;
352293305Sjpaetzel	return (retval);
353293305Sjpaetzel}
354293305Sjpaetzel
355293305Sjpaetzel/*
3561558Srgrimes * Mountd server for NFS mount protocol as described in:
3571558Srgrimes * NFS: Network File System Protocol Specification, RFC1094, Appendix A
3581558Srgrimes * The optional arguments are the exports file name
3591558Srgrimes * default: _PATH_EXPORTS
3601558Srgrimes * and "-n" to allow nonroot mount.
3611558Srgrimes */
3621558Srgrimesint
363216587Scharniermain(int argc, char **argv)
3641558Srgrimes{
36575754Siedowse	fd_set readfds;
366172827Smatteo	struct netconfig *nconf;
367172827Smatteo	char *endptr, **hosts_bak;
368172827Smatteo	void *nc_handle;
369149433Spjd	pid_t otherpid;
370172827Smatteo	in_port_t svcport;
371172827Smatteo	int c, k, s;
372109363Smbr	int maxrec = RPC_MAXDATASIZE;
373222623Srmacklem	int attempt_cnt, port_len, port_pos, ret;
374222623Srmacklem	char **port_list;
3751558Srgrimes
37674462Salfred	/* Check that another mountd isn't already running. */
377150214Spjd	pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
378149433Spjd	if (pfh == NULL) {
379149433Spjd		if (errno == EEXIST)
380149433Spjd			errx(1, "mountd already running, pid: %d.", otherpid);
381149433Spjd		warn("cannot open or create pidfile");
382149433Spjd	}
38374462Salfred
38474462Salfred	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
38574462Salfred	if (s < 0)
38674462Salfred		have_v6 = 0;
38774462Salfred	else
38874462Salfred		close(s);
3892999Swollman
390282214Strasz	while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1)
3911558Srgrimes		switch (c) {
39225087Sdfr		case '2':
39325087Sdfr			force_v2 = 1;
39425087Sdfr			break;
395192993Srmacklem		case 'e':
396220980Srmacklem			/* now a no-op, since this is the default */
397192934Srmacklem			break;
3989336Sdfr		case 'n':
3999336Sdfr			resvport_only = 0;
4009336Sdfr			break;
4019336Sdfr		case 'r':
4029336Sdfr			dir_only = 0;
4039336Sdfr			break;
4048688Sphk		case 'd':
4058688Sphk			debug = debug ? 0 : 1;
4068688Sphk			break;
40731656Sguido		case 'l':
408121767Speter			dolog = 1;
40931656Sguido			break;
410126572Sbms		case 'p':
411126572Sbms			endptr = NULL;
412126572Sbms			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
413126572Sbms			if (endptr == NULL || *endptr != '\0' ||
414126572Sbms			    svcport == 0 || svcport >= IPPORT_MAX)
415126572Sbms				usage();
416172827Smatteo			svcport_str = strdup(optarg);
417126572Sbms			break;
418172827Smatteo		case 'h':
419172827Smatteo			++nhosts;
420172827Smatteo			hosts_bak = hosts;
421172827Smatteo			hosts_bak = realloc(hosts, nhosts * sizeof(char *));
422172827Smatteo			if (hosts_bak == NULL) {
423172827Smatteo				if (hosts != NULL) {
424172827Smatteo					for (k = 0; k < nhosts; k++)
425172827Smatteo						free(hosts[k]);
426172827Smatteo					free(hosts);
427172827Smatteo					out_of_mem();
428172827Smatteo				}
429172827Smatteo			}
430172827Smatteo			hosts = hosts_bak;
431172827Smatteo			hosts[nhosts - 1] = strdup(optarg);
432172827Smatteo			if (hosts[nhosts - 1] == NULL) {
433172827Smatteo				for (k = 0; k < (nhosts - 1); k++)
434172827Smatteo					free(hosts[k]);
435172827Smatteo				free(hosts);
436172827Smatteo				out_of_mem();
437172827Smatteo			}
438172827Smatteo			break;
439241568Srmacklem		case 'S':
440241568Srmacklem			suspend_nfsd = 1;
441241568Srmacklem			break;
4421558Srgrimes		default:
44337663Scharnier			usage();
444298089Spfg		}
445192934Srmacklem
446282214Strasz	if (modfind("nfsd") < 0) {
447192934Srmacklem		/* Not present in kernel, try loading it */
448282214Strasz		if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
449192934Srmacklem			errx(1, "NFS server is not available");
450192934Srmacklem	}
451192934Srmacklem
4521558Srgrimes	argc -= optind;
4531558Srgrimes	argv += optind;
4541558Srgrimes	grphead = (struct grouplist *)NULL;
455166440Spjd	if (argc > 0)
456166440Spjd		exnames = argv;
457166440Spjd	else
458166440Spjd		exnames = exnames_default;
4591558Srgrimes	openlog("mountd", LOG_PID, LOG_DAEMON);
4601558Srgrimes	if (debug)
46137663Scharnier		warnx("getting export list");
4621558Srgrimes	get_exportlist();
4631558Srgrimes	if (debug)
46437663Scharnier		warnx("getting mount list");
4651558Srgrimes	get_mountlist();
4661558Srgrimes	if (debug)
46737663Scharnier		warnx("here we go");
4681558Srgrimes	if (debug == 0) {
4691558Srgrimes		daemon(0, 0);
4701558Srgrimes		signal(SIGINT, SIG_IGN);
4711558Srgrimes		signal(SIGQUIT, SIG_IGN);
4721558Srgrimes	}
47375754Siedowse	signal(SIGHUP, huphandler);
47474462Salfred	signal(SIGTERM, terminate);
475164394Srodrigc	signal(SIGPIPE, SIG_IGN);
476149433Spjd
477149433Spjd	pidfile_write(pfh);
478149433Spjd
479194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
480194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
481109363Smbr	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
482109363Smbr
48324759Sguido	if (!resvport_only) {
484308449Srmacklem		if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL,
48583687Speter		    &resvport_only, sizeof(resvport_only)) != 0 &&
48683687Speter		    errno != ENOENT) {
48724759Sguido			syslog(LOG_ERR, "sysctl: %m");
48824759Sguido			exit(1);
48924759Sguido		}
49024330Sguido	}
491126572Sbms
492172827Smatteo	/*
493172827Smatteo	 * If no hosts were specified, add a wildcard entry to bind to
494172827Smatteo	 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
495172827Smatteo	 * list.
496172827Smatteo	 */
497172827Smatteo	if (nhosts == 0) {
498292864Suqs		hosts = malloc(sizeof(char *));
499172827Smatteo		if (hosts == NULL)
500172827Smatteo			out_of_mem();
501172827Smatteo		hosts[0] = "*";
502172827Smatteo		nhosts = 1;
503172827Smatteo	} else {
504172827Smatteo		hosts_bak = hosts;
505172827Smatteo		if (have_v6) {
506172827Smatteo			hosts_bak = realloc(hosts, (nhosts + 2) *
507172827Smatteo			    sizeof(char *));
508172827Smatteo			if (hosts_bak == NULL) {
509172827Smatteo				for (k = 0; k < nhosts; k++)
510172827Smatteo					free(hosts[k]);
511172827Smatteo		    		free(hosts);
512172827Smatteo		    		out_of_mem();
513172827Smatteo			} else
514172827Smatteo				hosts = hosts_bak;
515172827Smatteo			nhosts += 2;
516172827Smatteo			hosts[nhosts - 2] = "::1";
517172827Smatteo		} else {
518172827Smatteo			hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
519172827Smatteo			if (hosts_bak == NULL) {
520172827Smatteo				for (k = 0; k < nhosts; k++)
521172827Smatteo					free(hosts[k]);
522172827Smatteo				free(hosts);
523172827Smatteo				out_of_mem();
524172827Smatteo			} else {
525172827Smatteo				nhosts += 1;
526172827Smatteo				hosts = hosts_bak;
527126572Sbms			}
528172827Smatteo		}
52974462Salfred
530172827Smatteo		hosts[nhosts - 1] = "127.0.0.1";
53174462Salfred	}
53274462Salfred
533222623Srmacklem	attempt_cnt = 1;
534222623Srmacklem	sock_fdcnt = 0;
535222623Srmacklem	sock_fd = NULL;
536222623Srmacklem	port_list = NULL;
537222623Srmacklem	port_len = 0;
538172827Smatteo	nc_handle = setnetconfig();
539172827Smatteo	while ((nconf = getnetconfig(nc_handle))) {
540172827Smatteo		if (nconf->nc_flag & NC_VISIBLE) {
541172827Smatteo			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
542172827Smatteo			    "inet6") == 0) {
543172827Smatteo				/* DO NOTHING */
544222623Srmacklem			} else {
545222623Srmacklem				ret = create_service(nconf);
546222623Srmacklem				if (ret == 1)
547222623Srmacklem					/* Ignore this call */
548222623Srmacklem					continue;
549222623Srmacklem				if (ret < 0) {
550222623Srmacklem					/*
551222623Srmacklem					 * Failed to bind port, so close off
552222623Srmacklem					 * all sockets created and try again
553222623Srmacklem					 * if the port# was dynamically
554222623Srmacklem					 * assigned via bind(2).
555222623Srmacklem					 */
556222623Srmacklem					clearout_service();
557222623Srmacklem					if (mallocd_svcport != 0 &&
558222623Srmacklem					    attempt_cnt < GETPORT_MAXTRY) {
559222623Srmacklem						free(svcport_str);
560222623Srmacklem						svcport_str = NULL;
561222623Srmacklem						mallocd_svcport = 0;
562222623Srmacklem					} else {
563222623Srmacklem						errno = EADDRINUSE;
564222623Srmacklem						syslog(LOG_ERR,
565222623Srmacklem						    "bindresvport_sa: %m");
566222623Srmacklem						exit(1);
567222623Srmacklem					}
568222623Srmacklem
569222623Srmacklem					/* Start over at the first service. */
570222623Srmacklem					free(sock_fd);
571222623Srmacklem					sock_fdcnt = 0;
572222623Srmacklem					sock_fd = NULL;
573222623Srmacklem					nc_handle = setnetconfig();
574222623Srmacklem					attempt_cnt++;
575222623Srmacklem				} else if (mallocd_svcport != 0 &&
576222623Srmacklem				    attempt_cnt == GETPORT_MAXTRY) {
577222623Srmacklem					/*
578222623Srmacklem					 * For the last attempt, allow
579222623Srmacklem					 * different port #s for each nconf
580222623Srmacklem					 * by saving the svcport_str and
581222623Srmacklem					 * setting it back to NULL.
582222623Srmacklem					 */
583222623Srmacklem					port_list = realloc(port_list,
584222623Srmacklem					    (port_len + 1) * sizeof(char *));
585222623Srmacklem					if (port_list == NULL)
586222623Srmacklem						out_of_mem();
587222623Srmacklem					port_list[port_len++] = svcport_str;
588222623Srmacklem					svcport_str = NULL;
589222623Srmacklem					mallocd_svcport = 0;
590222623Srmacklem				}
591222623Srmacklem			}
592222623Srmacklem		}
593222623Srmacklem	}
594222623Srmacklem
595222623Srmacklem	/*
596222623Srmacklem	 * Successfully bound the ports, so call complete_service() to
597222623Srmacklem	 * do the rest of the setup on the service(s).
598222623Srmacklem	 */
599222623Srmacklem	sock_fdpos = 0;
600222623Srmacklem	port_pos = 0;
601222623Srmacklem	nc_handle = setnetconfig();
602222623Srmacklem	while ((nconf = getnetconfig(nc_handle))) {
603222623Srmacklem		if (nconf->nc_flag & NC_VISIBLE) {
604222623Srmacklem			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
605222623Srmacklem			    "inet6") == 0) {
606222623Srmacklem				/* DO NOTHING */
607222623Srmacklem			} else if (port_list != NULL) {
608222623Srmacklem				if (port_pos >= port_len) {
609222623Srmacklem					syslog(LOG_ERR, "too many port#s");
610222623Srmacklem					exit(1);
611222623Srmacklem				}
612222623Srmacklem				complete_service(nconf, port_list[port_pos++]);
613172827Smatteo			} else
614222623Srmacklem				complete_service(nconf, svcport_str);
615172827Smatteo		}
61674462Salfred	}
617172827Smatteo	endnetconfig(nc_handle);
618222623Srmacklem	free(sock_fd);
619222623Srmacklem	if (port_list != NULL) {
620222623Srmacklem		for (port_pos = 0; port_pos < port_len; port_pos++)
621222623Srmacklem			free(port_list[port_pos]);
622222623Srmacklem		free(port_list);
623222623Srmacklem	}
62474462Salfred
62574462Salfred	if (xcreated == 0) {
62674462Salfred		syslog(LOG_ERR, "could not create any services");
6271558Srgrimes		exit(1);
6281558Srgrimes	}
62975754Siedowse
63075754Siedowse	/* Expand svc_run() here so that we can call get_exportlist(). */
63175754Siedowse	for (;;) {
63275754Siedowse		if (got_sighup) {
63375754Siedowse			get_exportlist();
63475754Siedowse			got_sighup = 0;
63575754Siedowse		}
63675754Siedowse		readfds = svc_fdset;
63775754Siedowse		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
63875754Siedowse		case -1:
63975754Siedowse			if (errno == EINTR)
64075754Siedowse                                continue;
64175754Siedowse			syslog(LOG_ERR, "mountd died: select: %m");
64275754Siedowse			exit(1);
64375754Siedowse		case 0:
64475754Siedowse			continue;
64575754Siedowse		default:
64675754Siedowse			svc_getreqset(&readfds);
64775754Siedowse		}
64875754Siedowse	}
649172827Smatteo}
650172827Smatteo
651172827Smatteo/*
652172827Smatteo * This routine creates and binds sockets on the appropriate
653222623Srmacklem * addresses. It gets called one time for each transport.
654222623Srmacklem * It returns 0 upon success, 1 for ingore the call and -1 to indicate
655222623Srmacklem * bind failed with EADDRINUSE.
656222623Srmacklem * Any file descriptors that have been created are stored in sock_fd and
657222623Srmacklem * the total count of them is maintained in sock_fdcnt.
658172827Smatteo */
659222623Srmacklemstatic int
660172827Smatteocreate_service(struct netconfig *nconf)
661172827Smatteo{
662172827Smatteo	struct addrinfo hints, *res = NULL;
663172827Smatteo	struct sockaddr_in *sin;
664172827Smatteo	struct sockaddr_in6 *sin6;
665172827Smatteo	struct __rpc_sockinfo si;
666172827Smatteo	int aicode;
667172827Smatteo	int fd;
668172827Smatteo	int nhostsbak;
669172827Smatteo	int one = 1;
670172827Smatteo	int r;
671172827Smatteo	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
672222623Srmacklem	int mallocd_res;
673172827Smatteo
674172827Smatteo	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
675172827Smatteo	    (nconf->nc_semantics != NC_TPI_COTS) &&
676172827Smatteo	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
677222623Srmacklem		return (1);	/* not my type */
678172827Smatteo
679172827Smatteo	/*
680172827Smatteo	 * XXX - using RPC library internal functions.
681172827Smatteo	 */
682172827Smatteo	if (!__rpc_nconf2sockinfo(nconf, &si)) {
683172827Smatteo		syslog(LOG_ERR, "cannot get information for %s",
684172827Smatteo		    nconf->nc_netid);
685222623Srmacklem		return (1);
686172827Smatteo	}
687172827Smatteo
688172827Smatteo	/* Get mountd's address on this transport */
689172827Smatteo	memset(&hints, 0, sizeof hints);
690172827Smatteo	hints.ai_family = si.si_af;
691172827Smatteo	hints.ai_socktype = si.si_socktype;
692172827Smatteo	hints.ai_protocol = si.si_proto;
693172827Smatteo
694172827Smatteo	/*
695172827Smatteo	 * Bind to specific IPs if asked to
696172827Smatteo	 */
697172827Smatteo	nhostsbak = nhosts;
698172827Smatteo	while (nhostsbak > 0) {
699172827Smatteo		--nhostsbak;
700222623Srmacklem		sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
701222623Srmacklem		if (sock_fd == NULL)
702222623Srmacklem			out_of_mem();
703222623Srmacklem		sock_fd[sock_fdcnt++] = -1;	/* Set invalid for now. */
704222623Srmacklem		mallocd_res = 0;
705222623Srmacklem
706277352Srstone		hints.ai_flags = AI_PASSIVE;
707277352Srstone
708172827Smatteo		/*
709172827Smatteo		 * XXX - using RPC library internal functions.
710172827Smatteo		 */
711172827Smatteo		if ((fd = __rpc_nconf2fd(nconf)) < 0) {
712172827Smatteo			int non_fatal = 0;
713244538Skevlo	    		if (errno == EAFNOSUPPORT &&
714172827Smatteo			    nconf->nc_semantics != NC_TPI_CLTS)
715172827Smatteo				non_fatal = 1;
716172827Smatteo
717172827Smatteo			syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
718172827Smatteo			    "cannot create socket for %s", nconf->nc_netid);
719222623Srmacklem			if (non_fatal != 0)
720222623Srmacklem				continue;
721222623Srmacklem			exit(1);
722172827Smatteo		}
723172827Smatteo
724172827Smatteo		switch (hints.ai_family) {
725172827Smatteo		case AF_INET:
726172827Smatteo			if (inet_pton(AF_INET, hosts[nhostsbak],
727172827Smatteo			    host_addr) == 1) {
728222623Srmacklem				hints.ai_flags |= AI_NUMERICHOST;
729172827Smatteo			} else {
730172827Smatteo				/*
731172827Smatteo				 * Skip if we have an AF_INET6 address.
732172827Smatteo				 */
733172827Smatteo				if (inet_pton(AF_INET6, hosts[nhostsbak],
734172827Smatteo				    host_addr) == 1) {
735172827Smatteo					close(fd);
736172827Smatteo					continue;
737172827Smatteo				}
738172827Smatteo			}
739172827Smatteo			break;
740172827Smatteo		case AF_INET6:
741172827Smatteo			if (inet_pton(AF_INET6, hosts[nhostsbak],
742172827Smatteo			    host_addr) == 1) {
743222623Srmacklem				hints.ai_flags |= AI_NUMERICHOST;
744172827Smatteo			} else {
745172827Smatteo				/*
746172827Smatteo				 * Skip if we have an AF_INET address.
747172827Smatteo				 */
748172827Smatteo				if (inet_pton(AF_INET, hosts[nhostsbak],
749172827Smatteo				    host_addr) == 1) {
750172827Smatteo					close(fd);
751172827Smatteo					continue;
752172827Smatteo				}
753172827Smatteo			}
754172827Smatteo
755172827Smatteo			/*
756172827Smatteo			 * We're doing host-based access checks here, so don't
757172827Smatteo			 * allow v4-in-v6 to confuse things. The kernel will
758172827Smatteo			 * disable it by default on NFS sockets too.
759172827Smatteo			 */
760172827Smatteo			if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
761172827Smatteo			    sizeof one) < 0) {
762172827Smatteo				syslog(LOG_ERR,
763172827Smatteo				    "can't disable v4-in-v6 on IPv6 socket");
764172827Smatteo				exit(1);
765172827Smatteo			}
766172827Smatteo			break;
767172827Smatteo		default:
768172827Smatteo			break;
769172827Smatteo		}
770172827Smatteo
771172827Smatteo		/*
772172827Smatteo		 * If no hosts were specified, just bind to INADDR_ANY
773172827Smatteo		 */
774172827Smatteo		if (strcmp("*", hosts[nhostsbak]) == 0) {
775172827Smatteo			if (svcport_str == NULL) {
776172827Smatteo				res = malloc(sizeof(struct addrinfo));
777172827Smatteo				if (res == NULL)
778172827Smatteo					out_of_mem();
779222623Srmacklem				mallocd_res = 1;
780172827Smatteo				res->ai_flags = hints.ai_flags;
781172827Smatteo				res->ai_family = hints.ai_family;
782172827Smatteo				res->ai_protocol = hints.ai_protocol;
783172827Smatteo				switch (res->ai_family) {
784172827Smatteo				case AF_INET:
785172827Smatteo					sin = malloc(sizeof(struct sockaddr_in));
786172827Smatteo					if (sin == NULL)
787172827Smatteo						out_of_mem();
788172827Smatteo					sin->sin_family = AF_INET;
789172827Smatteo					sin->sin_port = htons(0);
790172827Smatteo					sin->sin_addr.s_addr = htonl(INADDR_ANY);
791172827Smatteo					res->ai_addr = (struct sockaddr*) sin;
792172827Smatteo					res->ai_addrlen = (socklen_t)
793222623Srmacklem					    sizeof(struct sockaddr_in);
794172827Smatteo					break;
795172827Smatteo				case AF_INET6:
796172827Smatteo					sin6 = malloc(sizeof(struct sockaddr_in6));
797173056Ssimon					if (sin6 == NULL)
798172827Smatteo						out_of_mem();
799172827Smatteo					sin6->sin6_family = AF_INET6;
800172827Smatteo					sin6->sin6_port = htons(0);
801172827Smatteo					sin6->sin6_addr = in6addr_any;
802172827Smatteo					res->ai_addr = (struct sockaddr*) sin6;
803172827Smatteo					res->ai_addrlen = (socklen_t)
804222623Srmacklem					    sizeof(struct sockaddr_in6);
805222623Srmacklem					break;
806172827Smatteo				default:
807222623Srmacklem					syslog(LOG_ERR, "bad addr fam %d",
808222623Srmacklem					    res->ai_family);
809222623Srmacklem					exit(1);
810172827Smatteo				}
811172827Smatteo			} else {
812172827Smatteo				if ((aicode = getaddrinfo(NULL, svcport_str,
813172827Smatteo				    &hints, &res)) != 0) {
814172827Smatteo					syslog(LOG_ERR,
815172827Smatteo					    "cannot get local address for %s: %s",
816172827Smatteo					    nconf->nc_netid,
817172827Smatteo					    gai_strerror(aicode));
818222623Srmacklem					close(fd);
819172827Smatteo					continue;
820172827Smatteo				}
821172827Smatteo			}
822172827Smatteo		} else {
823172827Smatteo			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
824172827Smatteo			    &hints, &res)) != 0) {
825172827Smatteo				syslog(LOG_ERR,
826172827Smatteo				    "cannot get local address for %s: %s",
827172827Smatteo				    nconf->nc_netid, gai_strerror(aicode));
828222623Srmacklem				close(fd);
829172827Smatteo				continue;
830172827Smatteo			}
831172827Smatteo		}
832172827Smatteo
833222623Srmacklem		/* Store the fd. */
834222623Srmacklem		sock_fd[sock_fdcnt - 1] = fd;
835222623Srmacklem
836222623Srmacklem		/* Now, attempt the bind. */
837172827Smatteo		r = bindresvport_sa(fd, res->ai_addr);
838172827Smatteo		if (r != 0) {
839222623Srmacklem			if (errno == EADDRINUSE && mallocd_svcport != 0) {
840222623Srmacklem				if (mallocd_res != 0) {
841222623Srmacklem					free(res->ai_addr);
842222623Srmacklem					free(res);
843222623Srmacklem				} else
844222623Srmacklem					freeaddrinfo(res);
845222623Srmacklem				return (-1);
846222623Srmacklem			}
847172827Smatteo			syslog(LOG_ERR, "bindresvport_sa: %m");
848172827Smatteo			exit(1);
849172827Smatteo		}
850172827Smatteo
851222623Srmacklem		if (svcport_str == NULL) {
852222623Srmacklem			svcport_str = malloc(NI_MAXSERV * sizeof(char));
853222623Srmacklem			if (svcport_str == NULL)
854222623Srmacklem				out_of_mem();
855222623Srmacklem			mallocd_svcport = 1;
856222623Srmacklem
857222623Srmacklem			if (getnameinfo(res->ai_addr,
858222623Srmacklem			    res->ai_addr->sa_len, NULL, NI_MAXHOST,
859222623Srmacklem			    svcport_str, NI_MAXSERV * sizeof(char),
860222623Srmacklem			    NI_NUMERICHOST | NI_NUMERICSERV))
861222623Srmacklem				errx(1, "Cannot get port number");
862222623Srmacklem		}
863222623Srmacklem		if (mallocd_res != 0) {
864222623Srmacklem			free(res->ai_addr);
865222623Srmacklem			free(res);
866222623Srmacklem		} else
867222623Srmacklem			freeaddrinfo(res);
868222623Srmacklem		res = NULL;
869222623Srmacklem	}
870222623Srmacklem	return (0);
871222623Srmacklem}
872222623Srmacklem
873222623Srmacklem/*
874222623Srmacklem * Called after all the create_service() calls have succeeded, to complete
875222623Srmacklem * the setup and registration.
876222623Srmacklem */
877222623Srmacklemstatic void
878222623Srmacklemcomplete_service(struct netconfig *nconf, char *port_str)
879222623Srmacklem{
880222623Srmacklem	struct addrinfo hints, *res = NULL;
881222623Srmacklem	struct __rpc_sockinfo si;
882222623Srmacklem	struct netbuf servaddr;
883222623Srmacklem	SVCXPRT	*transp = NULL;
884222623Srmacklem	int aicode, fd, nhostsbak;
885222623Srmacklem	int registered = 0;
886222623Srmacklem
887222623Srmacklem	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
888222623Srmacklem	    (nconf->nc_semantics != NC_TPI_COTS) &&
889222623Srmacklem	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
890222623Srmacklem		return;	/* not my type */
891222623Srmacklem
892222623Srmacklem	/*
893222623Srmacklem	 * XXX - using RPC library internal functions.
894222623Srmacklem	 */
895222623Srmacklem	if (!__rpc_nconf2sockinfo(nconf, &si)) {
896222623Srmacklem		syslog(LOG_ERR, "cannot get information for %s",
897222623Srmacklem		    nconf->nc_netid);
898222623Srmacklem		return;
899222623Srmacklem	}
900222623Srmacklem
901222623Srmacklem	nhostsbak = nhosts;
902222623Srmacklem	while (nhostsbak > 0) {
903222623Srmacklem		--nhostsbak;
904222623Srmacklem		if (sock_fdpos >= sock_fdcnt) {
905222623Srmacklem			/* Should never happen. */
906222623Srmacklem			syslog(LOG_ERR, "Ran out of socket fd's");
907222623Srmacklem			return;
908222623Srmacklem		}
909222623Srmacklem		fd = sock_fd[sock_fdpos++];
910222623Srmacklem		if (fd < 0)
911222623Srmacklem			continue;
912222623Srmacklem
913341171Ssef		/*
914341171Ssef		 * Using -1 tells listen(2) to use
915341171Ssef		 * kern.ipc.soacceptqueue for the backlog.
916341171Ssef		 */
917172827Smatteo		if (nconf->nc_semantics != NC_TPI_CLTS)
918341171Ssef			listen(fd, -1);
919172827Smatteo
920172827Smatteo		if (nconf->nc_semantics == NC_TPI_CLTS )
921172827Smatteo			transp = svc_dg_create(fd, 0, 0);
922172827Smatteo		else
923172827Smatteo			transp = svc_vc_create(fd, RPC_MAXDATASIZE,
924172827Smatteo			    RPC_MAXDATASIZE);
925172827Smatteo
926172827Smatteo		if (transp != (SVCXPRT *) NULL) {
927194880Sdfr			if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
928172827Smatteo			    NULL))
929172827Smatteo				syslog(LOG_ERR,
930194880Sdfr				    "can't register %s MOUNTVERS service",
931172827Smatteo				    nconf->nc_netid);
932172827Smatteo			if (!force_v2) {
933194880Sdfr				if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
934172827Smatteo				    mntsrv, NULL))
935172827Smatteo					syslog(LOG_ERR,
936194880Sdfr					    "can't register %s MOUNTVERS3 service",
937172827Smatteo					    nconf->nc_netid);
938172827Smatteo			}
939172827Smatteo		} else
940172827Smatteo			syslog(LOG_WARNING, "can't create %s services",
941172827Smatteo			    nconf->nc_netid);
942172827Smatteo
943172827Smatteo		if (registered == 0) {
944172827Smatteo			registered = 1;
945172827Smatteo			memset(&hints, 0, sizeof hints);
946172827Smatteo			hints.ai_flags = AI_PASSIVE;
947172827Smatteo			hints.ai_family = si.si_af;
948172827Smatteo			hints.ai_socktype = si.si_socktype;
949172827Smatteo			hints.ai_protocol = si.si_proto;
950172827Smatteo
951222623Srmacklem			if ((aicode = getaddrinfo(NULL, port_str, &hints,
952172827Smatteo			    &res)) != 0) {
953172827Smatteo				syslog(LOG_ERR, "cannot get local address: %s",
954172827Smatteo				    gai_strerror(aicode));
955172827Smatteo				exit(1);
956172827Smatteo			}
957172827Smatteo
958172827Smatteo			servaddr.buf = malloc(res->ai_addrlen);
959172827Smatteo			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
960172827Smatteo			servaddr.len = res->ai_addrlen;
961172827Smatteo
962194880Sdfr			rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
963194880Sdfr			rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
964172827Smatteo
965172827Smatteo			xcreated++;
966172827Smatteo			freeaddrinfo(res);
967172827Smatteo		}
968172827Smatteo	} /* end while */
9691558Srgrimes}
9701558Srgrimes
971222623Srmacklem/*
972222623Srmacklem * Clear out sockets after a failure to bind one of them, so that the
973222623Srmacklem * cycle of socket creation/binding can start anew.
974222623Srmacklem */
97537663Scharnierstatic void
976222623Srmacklemclearout_service(void)
977222623Srmacklem{
978222623Srmacklem	int i;
979222623Srmacklem
980222623Srmacklem	for (i = 0; i < sock_fdcnt; i++) {
981222623Srmacklem		if (sock_fd[i] >= 0) {
982222623Srmacklem			shutdown(sock_fd[i], SHUT_RDWR);
983222623Srmacklem			close(sock_fd[i]);
984222623Srmacklem		}
985222623Srmacklem	}
986222623Srmacklem}
987222623Srmacklem
988222623Srmacklemstatic void
989216587Scharnierusage(void)
99037663Scharnier{
99137663Scharnier	fprintf(stderr,
992192993Srmacklem		"usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
993241568Srmacklem		"[-S] [-h <bindip>] [export_file ...]\n");
99437663Scharnier	exit(1);
99537663Scharnier}
99637663Scharnier
9971558Srgrimes/*
9981558Srgrimes * The mount rpc service
9991558Srgrimes */
10001558Srgrimesvoid
1001216587Scharniermntsrv(struct svc_req *rqstp, SVCXPRT *transp)
10021558Srgrimes{
10031558Srgrimes	struct exportlist *ep;
10041558Srgrimes	struct dirlist *dp;
10059336Sdfr	struct fhreturn fhr;
10061558Srgrimes	struct stat stb;
10071558Srgrimes	struct statfs fsb;
100874462Salfred	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
100974462Salfred	int lookup_failed = 1;
101074462Salfred	struct sockaddr *saddr;
10119336Sdfr	u_short sport;
1012194880Sdfr	char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
101328911Sguido	int bad = 0, defset, hostset;
10149336Sdfr	sigset_t sighup_mask;
1015240902Srmacklem	int numsecflavors, *secflavorsp;
10161558Srgrimes
10179336Sdfr	sigemptyset(&sighup_mask);
10189336Sdfr	sigaddset(&sighup_mask, SIGHUP);
101974462Salfred	saddr = svc_getrpccaller(transp)->buf;
102074462Salfred	switch (saddr->sa_family) {
102174462Salfred	case AF_INET6:
102275635Siedowse		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
102374462Salfred		break;
102474462Salfred	case AF_INET:
102575635Siedowse		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
102674462Salfred		break;
102774462Salfred	default:
102874462Salfred		syslog(LOG_ERR, "request from unknown address family");
102974462Salfred		return;
103074462Salfred	}
1031346769Smav	switch (rqstp->rq_proc) {
1032346769Smav	case MOUNTPROC_MNT:
1033346769Smav	case MOUNTPROC_UMNT:
1034346769Smav	case MOUNTPROC_UMNTALL:
1035346769Smav		lookup_failed = getnameinfo(saddr, saddr->sa_len, host,
1036346769Smav		    sizeof host, NULL, 0, 0);
1037346769Smav	}
103874462Salfred	getnameinfo(saddr, saddr->sa_len, numerichost,
103974462Salfred	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
10401558Srgrimes	switch (rqstp->rq_proc) {
10411558Srgrimes	case NULLPROC:
1042121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
104337663Scharnier			syslog(LOG_ERR, "can't send reply");
10441558Srgrimes		return;
1045194880Sdfr	case MOUNTPROC_MNT:
10469336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
104731656Sguido			syslog(LOG_NOTICE,
104831656Sguido			    "mount request from %s from unprivileged port",
104974462Salfred			    numerichost);
10501558Srgrimes			svcerr_weakauth(transp);
10511558Srgrimes			return;
10521558Srgrimes		}
1053121556Speter		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
105431656Sguido			syslog(LOG_NOTICE, "undecodable mount request from %s",
105574462Salfred			    numerichost);
10561558Srgrimes			svcerr_decode(transp);
10571558Srgrimes			return;
10581558Srgrimes		}
10591558Srgrimes
10601558Srgrimes		/*
10611558Srgrimes		 * Get the real pathname and make sure it is a directory
10629336Sdfr		 * or a regular file if the -r option was specified
10639336Sdfr		 * and it exists.
10641558Srgrimes		 */
106551968Salfred		if (realpath(rpcpath, dirpath) == NULL ||
10661558Srgrimes		    stat(dirpath, &stb) < 0 ||
10671558Srgrimes		    statfs(dirpath, &fsb) < 0) {
10681558Srgrimes			chdir("/");	/* Just in case realpath doesn't */
106931656Sguido			syslog(LOG_NOTICE,
107037663Scharnier			    "mount request from %s for non existent path %s",
107174462Salfred			    numerichost, dirpath);
10721558Srgrimes			if (debug)
107337663Scharnier				warnx("stat failed on %s", dirpath);
107428911Sguido			bad = ENOENT;	/* We will send error reply later */
10751558Srgrimes		}
1076330092Srpokala		if (!bad &&
1077330092Srpokala		    !S_ISDIR(stb.st_mode) &&
1078330092Srpokala		    (dir_only || !S_ISREG(stb.st_mode))) {
1079330092Srpokala			syslog(LOG_NOTICE,
1080330092Srpokala			    "mount request from %s for non-directory path %s",
1081330092Srpokala			    numerichost, dirpath);
1082330092Srpokala			if (debug)
1083330092Srpokala				warnx("mounting non-directory %s", dirpath);
1084330092Srpokala			bad = ENOTDIR;	/* We will send error reply later */
1085330092Srpokala		}
10861558Srgrimes
10871558Srgrimes		/* Check in the exports list */
10889336Sdfr		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1089330092Srpokala		if (bad)
1090330092Srpokala			ep = NULL;
1091330092Srpokala		else
1092349124Srmacklem			ep = ex_search(&fsb.f_fsid, &exphead);
10939336Sdfr		hostset = defset = 0;
1094240902Srmacklem		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
1095240902Srmacklem		    &numsecflavors, &secflavorsp) ||
10961558Srgrimes		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
1097240902Srmacklem		      chk_host(dp, saddr, &defset, &hostset, &numsecflavors,
1098240902Srmacklem		       &secflavorsp)) ||
109974462Salfred		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
110074462Salfred		     scan_tree(ep->ex_dirl, saddr) == 0))) {
110128911Sguido			if (bad) {
1102121556Speter				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
110328911Sguido				    (caddr_t)&bad))
110437663Scharnier					syslog(LOG_ERR, "can't send reply");
110528911Sguido				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
110628911Sguido				return;
110728911Sguido			}
1108240902Srmacklem			if (hostset & DP_HOSTSET) {
11099336Sdfr				fhr.fhr_flag = hostset;
1110240902Srmacklem				fhr.fhr_numsecflavors = numsecflavors;
1111240902Srmacklem				fhr.fhr_secflavors = secflavorsp;
1112240902Srmacklem			} else {
11139336Sdfr				fhr.fhr_flag = defset;
1114240902Srmacklem				fhr.fhr_numsecflavors = ep->ex_defnumsecflavors;
1115240902Srmacklem				fhr.fhr_secflavors = ep->ex_defsecflavors;
1116240902Srmacklem			}
11179336Sdfr			fhr.fhr_vers = rqstp->rq_vers;
11181558Srgrimes			/* Get the file handle */
111923681Speter			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
11209336Sdfr			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
11211558Srgrimes				bad = errno;
112237663Scharnier				syslog(LOG_ERR, "can't get fh for %s", dirpath);
1123121556Speter				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
11241558Srgrimes				    (caddr_t)&bad))
112537663Scharnier					syslog(LOG_ERR, "can't send reply");
11269336Sdfr				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
11271558Srgrimes				return;
11281558Srgrimes			}
1129121556Speter			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
1130121556Speter			    (caddr_t)&fhr))
113137663Scharnier				syslog(LOG_ERR, "can't send reply");
113274462Salfred			if (!lookup_failed)
113374462Salfred				add_mlist(host, dirpath);
11341558Srgrimes			else
113574462Salfred				add_mlist(numerichost, dirpath);
11361558Srgrimes			if (debug)
113737663Scharnier				warnx("mount successful");
1138121767Speter			if (dolog)
113931656Sguido				syslog(LOG_NOTICE,
114031656Sguido				    "mount request succeeded from %s for %s",
114174462Salfred				    numerichost, dirpath);
114231656Sguido		} else {
1143330092Srpokala			if (!bad)
1144330092Srpokala				bad = EACCES;
114531656Sguido			syslog(LOG_NOTICE,
114631656Sguido			    "mount request denied from %s for %s",
114774462Salfred			    numerichost, dirpath);
114831656Sguido		}
114928911Sguido
1150121556Speter		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
1151121556Speter		    (caddr_t)&bad))
115237663Scharnier			syslog(LOG_ERR, "can't send reply");
11539336Sdfr		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
11541558Srgrimes		return;
1155194880Sdfr	case MOUNTPROC_DUMP:
1156121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
115737663Scharnier			syslog(LOG_ERR, "can't send reply");
1158121767Speter		else if (dolog)
115931656Sguido			syslog(LOG_NOTICE,
116031656Sguido			    "dump request succeeded from %s",
116174462Salfred			    numerichost);
11621558Srgrimes		return;
1163194880Sdfr	case MOUNTPROC_UMNT:
11649336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
116531656Sguido			syslog(LOG_NOTICE,
116631656Sguido			    "umount request from %s from unprivileged port",
116774462Salfred			    numerichost);
11681558Srgrimes			svcerr_weakauth(transp);
11691558Srgrimes			return;
11701558Srgrimes		}
1171121556Speter		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
117231656Sguido			syslog(LOG_NOTICE, "undecodable umount request from %s",
117374462Salfred			    numerichost);
11741558Srgrimes			svcerr_decode(transp);
11751558Srgrimes			return;
11761558Srgrimes		}
117751968Salfred		if (realpath(rpcpath, dirpath) == NULL) {
117851968Salfred			syslog(LOG_NOTICE, "umount request from %s "
117951968Salfred			    "for non existent path %s",
118074462Salfred			    numerichost, dirpath);
118151968Salfred		}
1182121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
118337663Scharnier			syslog(LOG_ERR, "can't send reply");
118474462Salfred		if (!lookup_failed)
118575635Siedowse			del_mlist(host, dirpath);
118675635Siedowse		del_mlist(numerichost, dirpath);
1187121767Speter		if (dolog)
118831656Sguido			syslog(LOG_NOTICE,
118931656Sguido			    "umount request succeeded from %s for %s",
119074462Salfred			    numerichost, dirpath);
11911558Srgrimes		return;
1192194880Sdfr	case MOUNTPROC_UMNTALL:
11939336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
119431656Sguido			syslog(LOG_NOTICE,
119531656Sguido			    "umountall request from %s from unprivileged port",
119674462Salfred			    numerichost);
11971558Srgrimes			svcerr_weakauth(transp);
11981558Srgrimes			return;
11991558Srgrimes		}
1200121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
120137663Scharnier			syslog(LOG_ERR, "can't send reply");
120274462Salfred		if (!lookup_failed)
120375635Siedowse			del_mlist(host, NULL);
120475635Siedowse		del_mlist(numerichost, NULL);
1205121767Speter		if (dolog)
120631656Sguido			syslog(LOG_NOTICE,
120731656Sguido			    "umountall request succeeded from %s",
120874462Salfred			    numerichost);
12091558Srgrimes		return;
1210194880Sdfr	case MOUNTPROC_EXPORT:
1211121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
1212121556Speter			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
1213121556Speter			    (caddr_t)NULL))
1214100117Salfred				syslog(LOG_ERR, "can't send reply");
1215121767Speter		if (dolog)
121631656Sguido			syslog(LOG_NOTICE,
121731656Sguido			    "export request succeeded from %s",
121874462Salfred			    numerichost);
12191558Srgrimes		return;
12201558Srgrimes	default:
12211558Srgrimes		svcerr_noproc(transp);
12221558Srgrimes		return;
12231558Srgrimes	}
12241558Srgrimes}
12251558Srgrimes
12261558Srgrimes/*
12271558Srgrimes * Xdr conversion for a dirpath string
12281558Srgrimes */
1229285128Straszstatic int
1230216587Scharnierxdr_dir(XDR *xdrsp, char *dirp)
12311558Srgrimes{
1232194880Sdfr	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
12331558Srgrimes}
12341558Srgrimes
12351558Srgrimes/*
12369336Sdfr * Xdr routine to generate file handle reply
12371558Srgrimes */
1238285128Straszstatic int
1239216587Scharnierxdr_fhs(XDR *xdrsp, caddr_t cp)
12401558Srgrimes{
124192806Sobrien	struct fhreturn *fhrp = (struct fhreturn *)cp;
12429336Sdfr	u_long ok = 0, len, auth;
1243184588Sdfr	int i;
12441558Srgrimes
12451558Srgrimes	if (!xdr_long(xdrsp, &ok))
12461558Srgrimes		return (0);
12479336Sdfr	switch (fhrp->fhr_vers) {
12489336Sdfr	case 1:
12499336Sdfr		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
12509336Sdfr	case 3:
12519336Sdfr		len = NFSX_V3FH;
12529336Sdfr		if (!xdr_long(xdrsp, &len))
12539336Sdfr			return (0);
12549336Sdfr		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
12559336Sdfr			return (0);
1256184588Sdfr		if (fhrp->fhr_numsecflavors) {
1257184588Sdfr			if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
1258184588Sdfr				return (0);
1259184588Sdfr			for (i = 0; i < fhrp->fhr_numsecflavors; i++)
1260184588Sdfr				if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
1261184588Sdfr					return (0);
1262184588Sdfr			return (1);
1263184588Sdfr		} else {
1264184588Sdfr			auth = AUTH_SYS;
1265184588Sdfr			len = 1;
1266184588Sdfr			if (!xdr_long(xdrsp, &len))
1267184588Sdfr				return (0);
1268184588Sdfr			return (xdr_long(xdrsp, &auth));
1269184588Sdfr		}
1270298089Spfg	}
12719336Sdfr	return (0);
12721558Srgrimes}
12731558Srgrimes
1274285128Straszstatic int
1275216587Scharnierxdr_mlist(XDR *xdrsp, caddr_t cp __unused)
12761558Srgrimes{
12771558Srgrimes	struct mountlist *mlp;
12781558Srgrimes	int true = 1;
12791558Srgrimes	int false = 0;
12801558Srgrimes	char *strp;
12811558Srgrimes
1282324955Smanu	SLIST_FOREACH(mlp, &mlhead, next) {
12831558Srgrimes		if (!xdr_bool(xdrsp, &true))
12841558Srgrimes			return (0);
12851558Srgrimes		strp = &mlp->ml_host[0];
1286194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
12871558Srgrimes			return (0);
12881558Srgrimes		strp = &mlp->ml_dirp[0];
1289194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
12901558Srgrimes			return (0);
12911558Srgrimes	}
12921558Srgrimes	if (!xdr_bool(xdrsp, &false))
12931558Srgrimes		return (0);
12941558Srgrimes	return (1);
12951558Srgrimes}
12961558Srgrimes
12971558Srgrimes/*
12981558Srgrimes * Xdr conversion for export list
12991558Srgrimes */
1300285128Straszstatic int
1301216587Scharnierxdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
13021558Srgrimes{
13031558Srgrimes	struct exportlist *ep;
13041558Srgrimes	int false = 0;
13059336Sdfr	int putdef;
13069336Sdfr	sigset_t sighup_mask;
13071558Srgrimes
13089336Sdfr	sigemptyset(&sighup_mask);
13099336Sdfr	sigaddset(&sighup_mask, SIGHUP);
13109336Sdfr	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1311324955Smanu
1312324955Smanu	SLIST_FOREACH(ep, &exphead, entries) {
13131558Srgrimes		putdef = 0;
1314100117Salfred		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1315100117Salfred			       &putdef, brief))
13161558Srgrimes			goto errout;
13171558Srgrimes		if (ep->ex_defdir && putdef == 0 &&
13181558Srgrimes			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
1319100117Salfred			&putdef, brief))
13201558Srgrimes			goto errout;
13211558Srgrimes	}
13229336Sdfr	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
13231558Srgrimes	if (!xdr_bool(xdrsp, &false))
13241558Srgrimes		return (0);
13251558Srgrimes	return (1);
13261558Srgrimeserrout:
13279336Sdfr	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
13281558Srgrimes	return (0);
13291558Srgrimes}
13301558Srgrimes
13311558Srgrimes/*
13321558Srgrimes * Called from xdr_explist() to traverse the tree and export the
13331558Srgrimes * directory paths.
13341558Srgrimes */
1335285128Straszstatic int
1336216587Scharnierput_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1337216587Scharnier	int brief)
13381558Srgrimes{
13391558Srgrimes	struct grouplist *grp;
13401558Srgrimes	struct hostlist *hp;
13411558Srgrimes	int true = 1;
13421558Srgrimes	int false = 0;
13431558Srgrimes	int gotalldir = 0;
13441558Srgrimes	char *strp;
13451558Srgrimes
13461558Srgrimes	if (dp) {
1347100117Salfred		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
13481558Srgrimes			return (1);
13491558Srgrimes		if (!xdr_bool(xdrsp, &true))
13501558Srgrimes			return (1);
13511558Srgrimes		strp = dp->dp_dirp;
1352194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
13531558Srgrimes			return (1);
13541558Srgrimes		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
13551558Srgrimes			gotalldir = 1;
13561558Srgrimes			*putdefp = 1;
13571558Srgrimes		}
1358100117Salfred		if (brief) {
1359100117Salfred			if (!xdr_bool(xdrsp, &true))
1360100117Salfred				return (1);
1361100117Salfred			strp = "(...)";
1362194880Sdfr			if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1363100117Salfred				return (1);
1364100117Salfred		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
13651558Srgrimes		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
13661558Srgrimes			hp = dp->dp_hosts;
13671558Srgrimes			while (hp) {
13681558Srgrimes				grp = hp->ht_grp;
13691558Srgrimes				if (grp->gr_type == GT_HOST) {
13701558Srgrimes					if (!xdr_bool(xdrsp, &true))
13711558Srgrimes						return (1);
137274462Salfred					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
13738871Srgrimes					if (!xdr_string(xdrsp, &strp,
1374194880Sdfr					    MNTNAMLEN))
13751558Srgrimes						return (1);
13761558Srgrimes				} else if (grp->gr_type == GT_NET) {
13771558Srgrimes					if (!xdr_bool(xdrsp, &true))
13781558Srgrimes						return (1);
13791558Srgrimes					strp = grp->gr_ptr.gt_net.nt_name;
13808871Srgrimes					if (!xdr_string(xdrsp, &strp,
1381194880Sdfr					    MNTNAMLEN))
13821558Srgrimes						return (1);
13831558Srgrimes				}
13841558Srgrimes				hp = hp->ht_next;
13851558Srgrimes				if (gotalldir && hp == (struct hostlist *)NULL) {
13861558Srgrimes					hp = adp->dp_hosts;
13871558Srgrimes					gotalldir = 0;
13881558Srgrimes				}
13891558Srgrimes			}
13901558Srgrimes		}
13911558Srgrimes		if (!xdr_bool(xdrsp, &false))
13921558Srgrimes			return (1);
1393100117Salfred		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
13941558Srgrimes			return (1);
13951558Srgrimes	}
13961558Srgrimes	return (0);
13971558Srgrimes}
13981558Srgrimes
1399285128Straszstatic int
1400216587Scharnierxdr_explist(XDR *xdrsp, caddr_t cp)
1401100117Salfred{
1402100117Salfred
1403100117Salfred	return xdr_explist_common(xdrsp, cp, 0);
1404100117Salfred}
1405100117Salfred
1406285128Straszstatic int
1407216587Scharnierxdr_explist_brief(XDR *xdrsp, caddr_t cp)
1408100117Salfred{
1409100117Salfred
1410100117Salfred	return xdr_explist_common(xdrsp, cp, 1);
1411100117Salfred}
1412100117Salfred
1413285128Straszstatic char *line;
1414285128Straszstatic size_t linesize;
1415285128Straszstatic FILE *exp_file;
14161558Srgrimes
14171558Srgrimes/*
1418166440Spjd * Get the export list from one, currently open file
14191558Srgrimes */
1420166440Spjdstatic void
1421216587Scharnierget_exportlist_one(void)
14221558Srgrimes{
1423324955Smanu	struct exportlist *ep;
14241558Srgrimes	struct grouplist *grp, *tgrp;
14251558Srgrimes	struct dirlist *dirhead;
1426166440Spjd	struct statfs fsb;
142772650Sgreen	struct xucred anon;
14281558Srgrimes	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1429166440Spjd	int len, has_host, exflags, got_nondir, dirplen, netgrp;
14301558Srgrimes
1431192934Srmacklem	v4root_phase = 0;
14321558Srgrimes	dirhead = (struct dirlist *)NULL;
14331558Srgrimes	while (get_line()) {
14341558Srgrimes		if (debug)
143537663Scharnier			warnx("got line %s", line);
14361558Srgrimes		cp = line;
14371558Srgrimes		nextfield(&cp, &endcp);
14381558Srgrimes		if (*cp == '#')
14391558Srgrimes			goto nextline;
14401558Srgrimes
14411558Srgrimes		/*
14421558Srgrimes		 * Set defaults.
14431558Srgrimes		 */
14441558Srgrimes		has_host = FALSE;
14451558Srgrimes		anon = def_anon;
14461558Srgrimes		exflags = MNT_EXPORTED;
14471558Srgrimes		got_nondir = 0;
14481558Srgrimes		opt_flags = 0;
14491558Srgrimes		ep = (struct exportlist *)NULL;
1450192934Srmacklem		dirp = NULL;
14511558Srgrimes
14521558Srgrimes		/*
1453192934Srmacklem		 * Handle the V4 root dir.
1454192934Srmacklem		 */
1455192934Srmacklem		if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1456192934Srmacklem			/*
1457192934Srmacklem			 * V4: just indicates that it is the v4 root point,
1458192934Srmacklem			 * so skip over that and set v4root_phase.
1459192934Srmacklem			 */
1460192934Srmacklem			if (v4root_phase > 0) {
1461192934Srmacklem				syslog(LOG_ERR, "V4:duplicate line, ignored");
1462192934Srmacklem				goto nextline;
1463192934Srmacklem			}
1464192934Srmacklem			v4root_phase = 1;
1465192934Srmacklem			cp += 3;
1466192934Srmacklem			nextfield(&cp, &endcp);
1467192934Srmacklem		}
1468192934Srmacklem
1469192934Srmacklem		/*
14701558Srgrimes		 * Create new exports list entry
14711558Srgrimes		 */
14721558Srgrimes		len = endcp-cp;
14731558Srgrimes		tgrp = grp = get_grp();
14741558Srgrimes		while (len > 0) {
1475194880Sdfr			if (len > MNTNAMLEN) {
1476329392Sbrd			    getexp_err(ep, tgrp, "mountpoint too long");
14771558Srgrimes			    goto nextline;
14781558Srgrimes			}
14791558Srgrimes			if (*cp == '-') {
14801558Srgrimes			    if (ep == (struct exportlist *)NULL) {
1481329392Sbrd				getexp_err(ep, tgrp,
1482329392Sbrd				    "flag before export path definition");
14831558Srgrimes				goto nextline;
14841558Srgrimes			    }
14851558Srgrimes			    if (debug)
148637663Scharnier				warnx("doing opt %s", cp);
14871558Srgrimes			    got_nondir = 1;
14881558Srgrimes			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
14891558Srgrimes				&exflags, &anon)) {
1490329392Sbrd				getexp_err(ep, tgrp, NULL);
14911558Srgrimes				goto nextline;
14921558Srgrimes			    }
14931558Srgrimes			} else if (*cp == '/') {
14941558Srgrimes			    savedc = *endcp;
14951558Srgrimes			    *endcp = '\0';
1496192934Srmacklem			    if (v4root_phase > 1) {
1497192934Srmacklem				    if (dirp != NULL) {
1498329392Sbrd					getexp_err(ep, tgrp, "Multiple V4 dirs");
1499192934Srmacklem					goto nextline;
1500192934Srmacklem				    }
1501192934Srmacklem			    }
15021558Srgrimes			    if (check_dirpath(cp) &&
15031558Srgrimes				statfs(cp, &fsb) >= 0) {
1504283008Srmacklem				if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
1505283008Srmacklem				    syslog(LOG_ERR, "Warning: exporting of "
1506283008Srmacklem					"automounted fs %s not supported", cp);
15071558Srgrimes				if (got_nondir) {
1508329392Sbrd				    getexp_err(ep, tgrp, "dirs must be first");
15091558Srgrimes				    goto nextline;
15101558Srgrimes				}
1511192934Srmacklem				if (v4root_phase == 1) {
1512192934Srmacklem				    if (dirp != NULL) {
1513329392Sbrd					getexp_err(ep, tgrp, "Multiple V4 dirs");
15141558Srgrimes					goto nextline;
15151558Srgrimes				    }
1516192934Srmacklem				    if (strlen(v4root_dirpath) == 0) {
1517192934Srmacklem					strlcpy(v4root_dirpath, cp,
1518192934Srmacklem					    sizeof (v4root_dirpath));
1519192934Srmacklem				    } else if (strcmp(v4root_dirpath, cp)
1520192934Srmacklem					!= 0) {
1521192934Srmacklem					syslog(LOG_ERR,
1522192934Srmacklem					    "different V4 dirpath %s", cp);
1523329392Sbrd					getexp_err(ep, tgrp, NULL);
1524192934Srmacklem					goto nextline;
1525192934Srmacklem				    }
1526192934Srmacklem				    dirp = cp;
1527192934Srmacklem				    v4root_phase = 2;
1528192934Srmacklem				    got_nondir = 1;
1529192934Srmacklem				    ep = get_exp();
15301558Srgrimes				} else {
1531192934Srmacklem				    if (ep) {
1532192934Srmacklem					if (ep->ex_fs.val[0] !=
1533192934Srmacklem					    fsb.f_fsid.val[0] ||
1534192934Srmacklem					    ep->ex_fs.val[1] !=
1535192934Srmacklem					    fsb.f_fsid.val[1]) {
1536329392Sbrd						getexp_err(ep, tgrp,
1537329392Sbrd						    "fsid mismatch");
1538192934Srmacklem						goto nextline;
1539192934Srmacklem					}
1540192934Srmacklem				    } else {
1541192934Srmacklem					/*
1542192934Srmacklem					 * See if this directory is already
1543192934Srmacklem					 * in the list.
1544192934Srmacklem					 */
1545349124Srmacklem					ep = ex_search(&fsb.f_fsid, &exphead);
1546192934Srmacklem					if (ep == (struct exportlist *)NULL) {
1547192934Srmacklem					    ep = get_exp();
1548192934Srmacklem					    ep->ex_fs = fsb.f_fsid;
1549324234Smanu					    ep->ex_fsdir = strdup(fsb.f_mntonname);
1550324234Smanu					    if (ep->ex_fsdir == NULL)
1551192934Srmacklem						out_of_mem();
1552192934Srmacklem					    if (debug)
1553192934Srmacklem						warnx(
1554192934Srmacklem						  "making new ep fs=0x%x,0x%x",
1555192934Srmacklem						  fsb.f_fsid.val[0],
1556192934Srmacklem						  fsb.f_fsid.val[1]);
1557192934Srmacklem					} else if (debug)
1558192934Srmacklem					    warnx("found ep fs=0x%x,0x%x",
1559192934Srmacklem						fsb.f_fsid.val[0],
1560192934Srmacklem						fsb.f_fsid.val[1]);
1561192934Srmacklem				    }
1562192934Srmacklem
15631558Srgrimes				    /*
1564192934Srmacklem				     * Add dirpath to export mount point.
15651558Srgrimes				     */
1566192934Srmacklem				    dirp = add_expdir(&dirhead, cp, len);
1567192934Srmacklem				    dirplen = len;
15681558Srgrimes				}
15691558Srgrimes			    } else {
1570329392Sbrd				getexp_err(ep, tgrp,
1571329392Sbrd				    "symbolic link in export path or statfs failed");
15721558Srgrimes				goto nextline;
15731558Srgrimes			    }
15741558Srgrimes			    *endcp = savedc;
15751558Srgrimes			} else {
15761558Srgrimes			    savedc = *endcp;
15771558Srgrimes			    *endcp = '\0';
15781558Srgrimes			    got_nondir = 1;
15791558Srgrimes			    if (ep == (struct exportlist *)NULL) {
1580329392Sbrd				getexp_err(ep, tgrp,
1581329392Sbrd				    "host(s) before export path definition");
15821558Srgrimes				goto nextline;
15831558Srgrimes			    }
15841558Srgrimes
15851558Srgrimes			    /*
15861558Srgrimes			     * Get the host or netgroup.
15871558Srgrimes			     */
15881558Srgrimes			    setnetgrent(cp);
15891558Srgrimes			    netgrp = getnetgrent(&hst, &usr, &dom);
15901558Srgrimes			    do {
15911558Srgrimes				if (has_host) {
15921558Srgrimes				    grp->gr_next = get_grp();
15931558Srgrimes				    grp = grp->gr_next;
15941558Srgrimes				}
15951558Srgrimes				if (netgrp) {
159637003Sjoerg				    if (hst == 0) {
159737663Scharnier					syslog(LOG_ERR,
159837663Scharnier				"null hostname in netgroup %s, skipping", cp);
159937004Sjoerg					grp->gr_type = GT_IGNORE;
160037003Sjoerg				    } else if (get_host(hst, grp, tgrp)) {
160137663Scharnier					syslog(LOG_ERR,
160237663Scharnier			"bad host %s in netgroup %s, skipping", hst, cp);
160329317Sjlemon					grp->gr_type = GT_IGNORE;
16041558Srgrimes				    }
16057401Swpaul				} else if (get_host(cp, grp, tgrp)) {
160637663Scharnier				    syslog(LOG_ERR, "bad host %s, skipping", cp);
160729317Sjlemon				    grp->gr_type = GT_IGNORE;
16081558Srgrimes				}
16091558Srgrimes				has_host = TRUE;
16101558Srgrimes			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
16111558Srgrimes			    endnetgrent();
16121558Srgrimes			    *endcp = savedc;
16131558Srgrimes			}
16141558Srgrimes			cp = endcp;
16151558Srgrimes			nextfield(&cp, &endcp);
16161558Srgrimes			len = endcp - cp;
16171558Srgrimes		}
16181558Srgrimes		if (check_options(dirhead)) {
1619329392Sbrd			getexp_err(ep, tgrp, NULL);
16201558Srgrimes			goto nextline;
16211558Srgrimes		}
16221558Srgrimes		if (!has_host) {
162375641Siedowse			grp->gr_type = GT_DEFAULT;
16241558Srgrimes			if (debug)
162537663Scharnier				warnx("adding a default entry");
16261558Srgrimes
16271558Srgrimes		/*
16281558Srgrimes		 * Don't allow a network export coincide with a list of
16291558Srgrimes		 * host(s) on the same line.
16301558Srgrimes		 */
16311558Srgrimes		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1632329392Sbrd			getexp_err(ep, tgrp, "network/host conflict");
16331558Srgrimes			goto nextline;
163429317Sjlemon
163574462Salfred		/*
163674462Salfred		 * If an export list was specified on this line, make sure
163729317Sjlemon		 * that we have at least one valid entry, otherwise skip it.
163829317Sjlemon		 */
163929317Sjlemon		} else {
164029317Sjlemon			grp = tgrp;
164174462Salfred			while (grp && grp->gr_type == GT_IGNORE)
164229317Sjlemon				grp = grp->gr_next;
164329317Sjlemon			if (! grp) {
1644329392Sbrd			    getexp_err(ep, tgrp, "no valid entries");
164529317Sjlemon			    goto nextline;
164629317Sjlemon			}
16471558Srgrimes		}
16481558Srgrimes
1649192934Srmacklem		if (v4root_phase == 1) {
1650329392Sbrd			getexp_err(ep, tgrp, "V4:root, no dirp, ignored");
1651192934Srmacklem			goto nextline;
1652192934Srmacklem		}
1653192934Srmacklem
16541558Srgrimes		/*
16551558Srgrimes		 * Loop through hosts, pushing the exports into the kernel.
16561558Srgrimes		 * After loop, tgrp points to the start of the list and
16571558Srgrimes		 * grp points to the last entry in the list.
16581558Srgrimes		 */
16591558Srgrimes		grp = tgrp;
16601558Srgrimes		do {
166175635Siedowse			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
166275635Siedowse			    &fsb)) {
1663329392Sbrd				getexp_err(ep, tgrp, NULL);
166475635Siedowse				goto nextline;
166575635Siedowse			}
16661558Srgrimes		} while (grp->gr_next && (grp = grp->gr_next));
16671558Srgrimes
16681558Srgrimes		/*
1669192934Srmacklem		 * For V4: don't enter in mount lists.
1670192934Srmacklem		 */
1671194773Srmacklem		if (v4root_phase > 0 && v4root_phase <= 2) {
1672194773Srmacklem			/*
1673194773Srmacklem			 * Since these structures aren't used by mountd,
1674194773Srmacklem			 * free them up now.
1675194773Srmacklem			 */
1676194773Srmacklem			if (ep != NULL)
1677194773Srmacklem				free_exp(ep);
1678194773Srmacklem			while (tgrp != NULL) {
1679194773Srmacklem				grp = tgrp;
1680194773Srmacklem				tgrp = tgrp->gr_next;
1681194773Srmacklem				free_grp(grp);
1682194773Srmacklem			}
1683192934Srmacklem			goto nextline;
1684194773Srmacklem		}
1685192934Srmacklem
1686192934Srmacklem		/*
16871558Srgrimes		 * Success. Update the data structures.
16881558Srgrimes		 */
16891558Srgrimes		if (has_host) {
16909336Sdfr			hang_dirp(dirhead, tgrp, ep, opt_flags);
16911558Srgrimes			grp->gr_next = grphead;
16921558Srgrimes			grphead = tgrp;
16931558Srgrimes		} else {
16941558Srgrimes			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
16959336Sdfr				opt_flags);
16961558Srgrimes			free_grp(grp);
16971558Srgrimes		}
16981558Srgrimes		dirhead = (struct dirlist *)NULL;
16991558Srgrimes		if ((ep->ex_flag & EX_LINKED) == 0) {
1700349124Srmacklem			insert_exports(ep, &exphead);
17011558Srgrimes
17021558Srgrimes			ep->ex_flag |= EX_LINKED;
17031558Srgrimes		}
17041558Srgrimesnextline:
1705192934Srmacklem		v4root_phase = 0;
17061558Srgrimes		if (dirhead) {
17071558Srgrimes			free_dir(dirhead);
17081558Srgrimes			dirhead = (struct dirlist *)NULL;
17091558Srgrimes		}
17101558Srgrimes	}
17111558Srgrimes}
17121558Srgrimes
17131558Srgrimes/*
1714166440Spjd * Get the export list from all specified files
1715166440Spjd */
1716285128Straszstatic void
1717216587Scharnierget_exportlist(void)
1718166440Spjd{
1719166440Spjd	struct grouplist *grp, *tgrp;
1720166440Spjd	struct export_args export;
1721166440Spjd	struct iovec *iov;
1722166440Spjd	struct statfs *fsp, *mntbufp;
1723166440Spjd	struct xvfsconf vfc;
1724166440Spjd	char errmsg[255];
1725230352Seadler	int num, i;
1726166440Spjd	int iovlen;
1727168684Spjd	int done;
1728192934Srmacklem	struct nfsex_args eargs;
1729166440Spjd
1730241568Srmacklem	if (suspend_nfsd != 0)
1731241568Srmacklem		(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
1732192934Srmacklem	v4root_dirpath[0] = '\0';
1733166440Spjd	bzero(&export, sizeof(export));
1734166440Spjd	export.ex_flags = MNT_DELEXPORT;
1735166440Spjd	iov = NULL;
1736166440Spjd	iovlen = 0;
1737166440Spjd	bzero(errmsg, sizeof(errmsg));
1738166440Spjd
1739166440Spjd	/*
1740166440Spjd	 * First, get rid of the old list
1741166440Spjd	 */
1742349124Srmacklem	free_exports(&exphead);
1743166440Spjd
1744166440Spjd	grp = grphead;
1745166440Spjd	while (grp) {
1746166440Spjd		tgrp = grp;
1747166440Spjd		grp = grp->gr_next;
1748166440Spjd		free_grp(tgrp);
1749166440Spjd	}
1750166440Spjd	grphead = (struct grouplist *)NULL;
1751166440Spjd
1752166440Spjd	/*
1753192934Srmacklem	 * and the old V4 root dir.
1754192934Srmacklem	 */
1755192934Srmacklem	bzero(&eargs, sizeof (eargs));
1756192934Srmacklem	eargs.export.ex_flags = MNT_DELEXPORT;
1757282214Strasz	if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
1758192934Srmacklem	    errno != ENOENT)
1759192934Srmacklem		syslog(LOG_ERR, "Can't delete exports for V4:");
1760192934Srmacklem
1761192934Srmacklem	/*
1762192934Srmacklem	 * and clear flag that notes if a public fh has been exported.
1763192934Srmacklem	 */
1764192934Srmacklem	has_publicfh = 0;
1765192934Srmacklem
1766192934Srmacklem	/*
1767166440Spjd	 * And delete exports that are in the kernel for all local
1768166440Spjd	 * filesystems.
1769166440Spjd	 * XXX: Should know how to handle all local exportable filesystems.
1770166440Spjd	 */
1771166440Spjd	num = getmntinfo(&mntbufp, MNT_NOWAIT);
1772166440Spjd
1773166440Spjd	if (num > 0) {
1774166440Spjd		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1775166440Spjd		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1776166440Spjd		build_iovec(&iov, &iovlen, "from", NULL, 0);
1777166440Spjd		build_iovec(&iov, &iovlen, "update", NULL, 0);
1778166440Spjd		build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
1779166440Spjd		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1780166440Spjd	}
1781166440Spjd
1782166440Spjd	for (i = 0; i < num; i++) {
1783166440Spjd		fsp = &mntbufp[i];
1784166440Spjd		if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
1785166440Spjd			syslog(LOG_ERR, "getvfsbyname() failed for %s",
1786166440Spjd			    fsp->f_fstypename);
1787166440Spjd			continue;
1788166440Spjd		}
1789166440Spjd
1790166440Spjd		/*
1791281699Ssjg		 * We do not need to delete "export" flag from
1792281699Ssjg		 * filesystems that do not have it set.
1793281699Ssjg		 */
1794281699Ssjg		if (!(fsp->f_flags & MNT_EXPORTED))
1795281699Ssjg		    continue;
1796281699Ssjg		/*
1797166440Spjd		 * Do not delete export for network filesystem by
1798166440Spjd		 * passing "export" arg to nmount().
1799166440Spjd		 * It only makes sense to do this for local filesystems.
1800166440Spjd		 */
1801166440Spjd		if (vfc.vfc_flags & VFCF_NETWORK)
1802166440Spjd			continue;
1803166440Spjd
1804166440Spjd		iov[1].iov_base = fsp->f_fstypename;
1805166440Spjd		iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
1806166440Spjd		iov[3].iov_base = fsp->f_mntonname;
1807166440Spjd		iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
1808166440Spjd		iov[5].iov_base = fsp->f_mntfromname;
1809166440Spjd		iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
1810270183Sbdrewery		errmsg[0] = '\0';
1811166440Spjd
1812278523Skib		/*
1813278523Skib		 * EXDEV is returned when path exists but is not a
1814278523Skib		 * mount point.  May happens if raced with unmount.
1815278523Skib		 */
1816166440Spjd		if (nmount(iov, iovlen, fsp->f_flags) < 0 &&
1817278523Skib		    errno != ENOENT && errno != ENOTSUP && errno != EXDEV) {
1818166440Spjd			syslog(LOG_ERR,
1819166440Spjd			    "can't delete exports for %s: %m %s",
1820166440Spjd			    fsp->f_mntonname, errmsg);
1821166440Spjd		}
1822166440Spjd	}
1823166440Spjd
1824166440Spjd	if (iov != NULL) {
1825166440Spjd		/* Free strings allocated by strdup() in getmntopts.c */
1826166440Spjd		free(iov[0].iov_base); /* fstype */
1827166440Spjd		free(iov[2].iov_base); /* fspath */
1828166440Spjd		free(iov[4].iov_base); /* from */
1829166440Spjd		free(iov[6].iov_base); /* update */
1830166440Spjd		free(iov[8].iov_base); /* export */
1831166440Spjd		free(iov[10].iov_base); /* errmsg */
1832166440Spjd
1833166440Spjd		/* free iov, allocated by realloc() */
1834166440Spjd		free(iov);
1835166440Spjd		iovlen = 0;
1836166440Spjd	}
1837166440Spjd
1838166440Spjd	/*
1839166440Spjd	 * Read in the exports file and build the list, calling
1840166440Spjd	 * nmount() as we go along to push the export rules into the kernel.
1841166440Spjd	 */
1842168684Spjd	done = 0;
1843166440Spjd	for (i = 0; exnames[i] != NULL; i++) {
1844166440Spjd		if (debug)
1845166440Spjd			warnx("reading exports from %s", exnames[i]);
1846166440Spjd		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
1847168684Spjd			syslog(LOG_WARNING, "can't open %s", exnames[i]);
1848168684Spjd			continue;
1849166440Spjd		}
1850166440Spjd		get_exportlist_one();
1851166440Spjd		fclose(exp_file);
1852168684Spjd		done++;
1853166440Spjd	}
1854168684Spjd	if (done == 0) {
1855168684Spjd		syslog(LOG_ERR, "can't open any exports file");
1856168684Spjd		exit(2);
1857168684Spjd	}
1858192934Srmacklem
1859192934Srmacklem	/*
1860192934Srmacklem	 * If there was no public fh, clear any previous one set.
1861192934Srmacklem	 */
1862282214Strasz	if (has_publicfh == 0)
1863192934Srmacklem		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
1864241568Srmacklem
1865241568Srmacklem	/* Resume the nfsd. If they weren't suspended, this is harmless. */
1866241568Srmacklem	(void)nfssvc(NFSSVC_RESUMENFSD, NULL);
1867166440Spjd}
1868166440Spjd
1869166440Spjd/*
1870349124Srmacklem * Insert an export entry in the appropriate list.
1871349124Srmacklem */
1872349124Srmacklemstatic void
1873349124Srmackleminsert_exports(struct exportlist *ep, struct exportlisthead *exhp)
1874349124Srmacklem{
1875349124Srmacklem
1876349124Srmacklem	SLIST_INSERT_HEAD(exhp, ep, entries);
1877349124Srmacklem}
1878349124Srmacklem
1879349124Srmacklem/*
1880349124Srmacklem * Free up the exports lists passed in as arguments.
1881349124Srmacklem */
1882349124Srmacklemstatic void
1883349124Srmacklemfree_exports(struct exportlisthead *exhp)
1884349124Srmacklem{
1885349124Srmacklem	struct exportlist *ep, *ep2;
1886349124Srmacklem
1887349124Srmacklem	SLIST_FOREACH_SAFE(ep, exhp, entries, ep2) {
1888349124Srmacklem		SLIST_REMOVE(exhp, ep, exportlist, entries);
1889349124Srmacklem		free_exp(ep);
1890349124Srmacklem	}
1891349124Srmacklem	SLIST_INIT(exhp);
1892349124Srmacklem}
1893349124Srmacklem
1894349124Srmacklem/*
18951558Srgrimes * Allocate an export list element
18961558Srgrimes */
1897285128Straszstatic struct exportlist *
1898216587Scharnierget_exp(void)
18991558Srgrimes{
19001558Srgrimes	struct exportlist *ep;
19011558Srgrimes
1902224003Sdelphij	ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
19031558Srgrimes	if (ep == (struct exportlist *)NULL)
19041558Srgrimes		out_of_mem();
19051558Srgrimes	return (ep);
19061558Srgrimes}
19071558Srgrimes
19081558Srgrimes/*
19091558Srgrimes * Allocate a group list element
19101558Srgrimes */
1911285128Straszstatic struct grouplist *
1912216587Scharnierget_grp(void)
19131558Srgrimes{
19141558Srgrimes	struct grouplist *gp;
19151558Srgrimes
1916224003Sdelphij	gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
19171558Srgrimes	if (gp == (struct grouplist *)NULL)
19181558Srgrimes		out_of_mem();
19191558Srgrimes	return (gp);
19201558Srgrimes}
19211558Srgrimes
19221558Srgrimes/*
19231558Srgrimes * Clean up upon an error in get_exportlist().
19241558Srgrimes */
1925285128Straszstatic void
1926329392Sbrdgetexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason)
19271558Srgrimes{
19281558Srgrimes	struct grouplist *tgrp;
19291558Srgrimes
1930329392Sbrd	if (!(opt_flags & OP_QUIET)) {
1931329392Sbrd		if (reason != NULL)
1932329392Sbrd			syslog(LOG_ERR, "bad exports list line '%s': %s", line,
1933329392Sbrd			    reason);
1934329392Sbrd		else
1935329392Sbrd			syslog(LOG_ERR, "bad exports list line '%s'", line);
1936329392Sbrd	}
19371558Srgrimes	if (ep && (ep->ex_flag & EX_LINKED) == 0)
19381558Srgrimes		free_exp(ep);
19391558Srgrimes	while (grp) {
19401558Srgrimes		tgrp = grp;
19411558Srgrimes		grp = grp->gr_next;
19421558Srgrimes		free_grp(tgrp);
19431558Srgrimes	}
19441558Srgrimes}
19451558Srgrimes
19461558Srgrimes/*
19471558Srgrimes * Search the export list for a matching fs.
19481558Srgrimes */
1949285128Straszstatic struct exportlist *
1950349124Srmacklemex_search(fsid_t *fsid, struct exportlisthead *exhp)
19511558Srgrimes{
19521558Srgrimes	struct exportlist *ep;
19531558Srgrimes
1954349124Srmacklem	SLIST_FOREACH(ep, exhp, entries) {
19551558Srgrimes		if (ep->ex_fs.val[0] == fsid->val[0] &&
19561558Srgrimes		    ep->ex_fs.val[1] == fsid->val[1])
19571558Srgrimes			return (ep);
19581558Srgrimes	}
1959324955Smanu
19601558Srgrimes	return (ep);
19611558Srgrimes}
19621558Srgrimes
19631558Srgrimes/*
19641558Srgrimes * Add a directory path to the list.
19651558Srgrimes */
1966285128Straszstatic char *
1967216587Scharnieradd_expdir(struct dirlist **dpp, char *cp, int len)
19681558Srgrimes{
19691558Srgrimes	struct dirlist *dp;
19701558Srgrimes
1971324234Smanu	dp = malloc(sizeof (struct dirlist));
197237663Scharnier	if (dp == (struct dirlist *)NULL)
197337663Scharnier		out_of_mem();
19741558Srgrimes	dp->dp_left = *dpp;
19751558Srgrimes	dp->dp_right = (struct dirlist *)NULL;
19761558Srgrimes	dp->dp_flag = 0;
19771558Srgrimes	dp->dp_hosts = (struct hostlist *)NULL;
1978324234Smanu	dp->dp_dirp = strndup(cp, len);
1979324234Smanu	if (dp->dp_dirp == NULL)
1980324234Smanu		out_of_mem();
19811558Srgrimes	*dpp = dp;
19821558Srgrimes	return (dp->dp_dirp);
19831558Srgrimes}
19841558Srgrimes
19851558Srgrimes/*
19861558Srgrimes * Hang the dir list element off the dirpath binary tree as required
19871558Srgrimes * and update the entry for host.
19881558Srgrimes */
1989285128Straszstatic void
1990216587Scharnierhang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
1991216587Scharnier	int flags)
19921558Srgrimes{
19931558Srgrimes	struct hostlist *hp;
19941558Srgrimes	struct dirlist *dp2;
19951558Srgrimes
19969336Sdfr	if (flags & OP_ALLDIRS) {
19971558Srgrimes		if (ep->ex_defdir)
19981558Srgrimes			free((caddr_t)dp);
19991558Srgrimes		else
20001558Srgrimes			ep->ex_defdir = dp;
20019336Sdfr		if (grp == (struct grouplist *)NULL) {
20021558Srgrimes			ep->ex_defdir->dp_flag |= DP_DEFSET;
2003240902Srmacklem			/* Save the default security flavors list. */
2004240902Srmacklem			ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2005240902Srmacklem			if (ep->ex_numsecflavors > 0)
2006240902Srmacklem				memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2007240902Srmacklem				    sizeof(ep->ex_secflavors));
20089336Sdfr		} else while (grp) {
20091558Srgrimes			hp = get_ht();
20101558Srgrimes			hp->ht_grp = grp;
20111558Srgrimes			hp->ht_next = ep->ex_defdir->dp_hosts;
20121558Srgrimes			ep->ex_defdir->dp_hosts = hp;
2013240902Srmacklem			/* Save the security flavors list for this host set. */
2014240902Srmacklem			grp->gr_numsecflavors = ep->ex_numsecflavors;
2015240902Srmacklem			if (ep->ex_numsecflavors > 0)
2016240902Srmacklem				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2017240902Srmacklem				    sizeof(ep->ex_secflavors));
20181558Srgrimes			grp = grp->gr_next;
20191558Srgrimes		}
20201558Srgrimes	} else {
20211558Srgrimes
20221558Srgrimes		/*
202337663Scharnier		 * Loop through the directories adding them to the tree.
20241558Srgrimes		 */
20251558Srgrimes		while (dp) {
20261558Srgrimes			dp2 = dp->dp_left;
2027240902Srmacklem			add_dlist(&ep->ex_dirl, dp, grp, flags, ep);
20281558Srgrimes			dp = dp2;
20291558Srgrimes		}
20301558Srgrimes	}
20311558Srgrimes}
20321558Srgrimes
20331558Srgrimes/*
20341558Srgrimes * Traverse the binary tree either updating a node that is already there
20351558Srgrimes * for the new directory or adding the new node.
20361558Srgrimes */
2037285128Straszstatic void
2038216587Scharnieradd_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
2039240902Srmacklem	int flags, struct exportlist *ep)
20401558Srgrimes{
20411558Srgrimes	struct dirlist *dp;
20421558Srgrimes	struct hostlist *hp;
20431558Srgrimes	int cmp;
20441558Srgrimes
20451558Srgrimes	dp = *dpp;
20461558Srgrimes	if (dp) {
20471558Srgrimes		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
20481558Srgrimes		if (cmp > 0) {
2049240902Srmacklem			add_dlist(&dp->dp_left, newdp, grp, flags, ep);
20501558Srgrimes			return;
20511558Srgrimes		} else if (cmp < 0) {
2052240902Srmacklem			add_dlist(&dp->dp_right, newdp, grp, flags, ep);
20531558Srgrimes			return;
20541558Srgrimes		} else
20551558Srgrimes			free((caddr_t)newdp);
20561558Srgrimes	} else {
20571558Srgrimes		dp = newdp;
20581558Srgrimes		dp->dp_left = (struct dirlist *)NULL;
20591558Srgrimes		*dpp = dp;
20601558Srgrimes	}
20611558Srgrimes	if (grp) {
20621558Srgrimes
20631558Srgrimes		/*
20641558Srgrimes		 * Hang all of the host(s) off of the directory point.
20651558Srgrimes		 */
20661558Srgrimes		do {
20671558Srgrimes			hp = get_ht();
20681558Srgrimes			hp->ht_grp = grp;
20691558Srgrimes			hp->ht_next = dp->dp_hosts;
20701558Srgrimes			dp->dp_hosts = hp;
2071240902Srmacklem			/* Save the security flavors list for this host set. */
2072240902Srmacklem			grp->gr_numsecflavors = ep->ex_numsecflavors;
2073240902Srmacklem			if (ep->ex_numsecflavors > 0)
2074240902Srmacklem				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2075240902Srmacklem				    sizeof(ep->ex_secflavors));
20761558Srgrimes			grp = grp->gr_next;
20771558Srgrimes		} while (grp);
20789336Sdfr	} else {
20791558Srgrimes		dp->dp_flag |= DP_DEFSET;
2080240902Srmacklem		/* Save the default security flavors list. */
2081240902Srmacklem		ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2082240902Srmacklem		if (ep->ex_numsecflavors > 0)
2083240902Srmacklem			memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2084240902Srmacklem			    sizeof(ep->ex_secflavors));
20859336Sdfr	}
20861558Srgrimes}
20871558Srgrimes
20881558Srgrimes/*
20891558Srgrimes * Search for a dirpath on the export point.
20901558Srgrimes */
2091285128Straszstatic struct dirlist *
2092216587Scharnierdirp_search(struct dirlist *dp, char *dirp)
20931558Srgrimes{
20941558Srgrimes	int cmp;
20951558Srgrimes
20961558Srgrimes	if (dp) {
209774462Salfred		cmp = strcmp(dp->dp_dirp, dirp);
20981558Srgrimes		if (cmp > 0)
209974462Salfred			return (dirp_search(dp->dp_left, dirp));
21001558Srgrimes		else if (cmp < 0)
210174462Salfred			return (dirp_search(dp->dp_right, dirp));
21021558Srgrimes		else
21031558Srgrimes			return (dp);
21041558Srgrimes	}
21051558Srgrimes	return (dp);
21061558Srgrimes}
21071558Srgrimes
21081558Srgrimes/*
21091558Srgrimes * Scan for a host match in a directory tree.
21101558Srgrimes */
2111285128Straszstatic int
2112216587Scharnierchk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2113240902Srmacklem	int *hostsetp, int *numsecflavors, int **secflavorsp)
21141558Srgrimes{
21151558Srgrimes	struct hostlist *hp;
21161558Srgrimes	struct grouplist *grp;
211774462Salfred	struct addrinfo *ai;
21181558Srgrimes
21191558Srgrimes	if (dp) {
21201558Srgrimes		if (dp->dp_flag & DP_DEFSET)
21219336Sdfr			*defsetp = dp->dp_flag;
21221558Srgrimes		hp = dp->dp_hosts;
21231558Srgrimes		while (hp) {
21241558Srgrimes			grp = hp->ht_grp;
21251558Srgrimes			switch (grp->gr_type) {
21261558Srgrimes			case GT_HOST:
212774462Salfred				ai = grp->gr_ptr.gt_addrinfo;
212874462Salfred				for (; ai; ai = ai->ai_next) {
212975801Siedowse					if (!sacmp(ai->ai_addr, saddr, NULL)) {
213074462Salfred						*hostsetp =
213174462Salfred						    (hp->ht_flag | DP_HOSTSET);
2132240902Srmacklem						if (numsecflavors != NULL) {
2133240902Srmacklem							*numsecflavors =
2134240902Srmacklem							    grp->gr_numsecflavors;
2135240902Srmacklem							*secflavorsp =
2136240902Srmacklem							    grp->gr_secflavors;
2137240902Srmacklem						}
213874462Salfred						return (1);
213974462Salfred					}
21409336Sdfr				}
214175801Siedowse				break;
21421558Srgrimes			case GT_NET:
214375801Siedowse				if (!sacmp(saddr, (struct sockaddr *)
214475801Siedowse				    &grp->gr_ptr.gt_net.nt_net,
214575801Siedowse				    (struct sockaddr *)
214675801Siedowse				    &grp->gr_ptr.gt_net.nt_mask)) {
214774462Salfred					*hostsetp = (hp->ht_flag | DP_HOSTSET);
2148240902Srmacklem					if (numsecflavors != NULL) {
2149240902Srmacklem						*numsecflavors =
2150240902Srmacklem						    grp->gr_numsecflavors;
2151240902Srmacklem						*secflavorsp =
2152240902Srmacklem						    grp->gr_secflavors;
2153240902Srmacklem					}
215474462Salfred					return (1);
215574462Salfred				}
215675801Siedowse				break;
215775801Siedowse			}
21581558Srgrimes			hp = hp->ht_next;
21591558Srgrimes		}
21601558Srgrimes	}
21611558Srgrimes	return (0);
21621558Srgrimes}
21631558Srgrimes
21641558Srgrimes/*
21651558Srgrimes * Scan tree for a host that matches the address.
21661558Srgrimes */
2167285128Straszstatic int
2168216587Scharnierscan_tree(struct dirlist *dp, struct sockaddr *saddr)
21691558Srgrimes{
21709336Sdfr	int defset, hostset;
21711558Srgrimes
21721558Srgrimes	if (dp) {
21731558Srgrimes		if (scan_tree(dp->dp_left, saddr))
21741558Srgrimes			return (1);
2175240902Srmacklem		if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
21761558Srgrimes			return (1);
21771558Srgrimes		if (scan_tree(dp->dp_right, saddr))
21781558Srgrimes			return (1);
21791558Srgrimes	}
21801558Srgrimes	return (0);
21811558Srgrimes}
21821558Srgrimes
21831558Srgrimes/*
21841558Srgrimes * Traverse the dirlist tree and free it up.
21851558Srgrimes */
2186285128Straszstatic void
2187216587Scharnierfree_dir(struct dirlist *dp)
21881558Srgrimes{
21891558Srgrimes
21901558Srgrimes	if (dp) {
21911558Srgrimes		free_dir(dp->dp_left);
21921558Srgrimes		free_dir(dp->dp_right);
21931558Srgrimes		free_host(dp->dp_hosts);
2194324234Smanu		free(dp->dp_dirp);
2195324234Smanu		free(dp);
21961558Srgrimes	}
21971558Srgrimes}
21981558Srgrimes
21991558Srgrimes/*
2200184588Sdfr * Parse a colon separated list of security flavors
2201184588Sdfr */
2202285128Straszstatic int
2203216587Scharnierparsesec(char *seclist, struct exportlist *ep)
2204184588Sdfr{
2205184588Sdfr	char *cp, savedc;
2206184588Sdfr	int flavor;
2207184588Sdfr
2208184588Sdfr	ep->ex_numsecflavors = 0;
2209184588Sdfr	for (;;) {
2210184588Sdfr		cp = strchr(seclist, ':');
2211184588Sdfr		if (cp) {
2212184588Sdfr			savedc = *cp;
2213184588Sdfr			*cp = '\0';
2214184588Sdfr		}
2215184588Sdfr
2216184588Sdfr		if (!strcmp(seclist, "sys"))
2217184588Sdfr			flavor = AUTH_SYS;
2218184588Sdfr		else if (!strcmp(seclist, "krb5"))
2219184588Sdfr			flavor = RPCSEC_GSS_KRB5;
2220184588Sdfr		else if (!strcmp(seclist, "krb5i"))
2221184588Sdfr			flavor = RPCSEC_GSS_KRB5I;
2222184588Sdfr		else if (!strcmp(seclist, "krb5p"))
2223184588Sdfr			flavor = RPCSEC_GSS_KRB5P;
2224184588Sdfr		else {
2225184588Sdfr			if (cp)
2226184588Sdfr				*cp = savedc;
2227184588Sdfr			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2228184588Sdfr			return (1);
2229184588Sdfr		}
2230184588Sdfr		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2231184588Sdfr			if (cp)
2232184588Sdfr				*cp = savedc;
2233184588Sdfr			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2234184588Sdfr			return (1);
2235184588Sdfr		}
2236184588Sdfr		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2237184588Sdfr		ep->ex_numsecflavors++;
2238184588Sdfr		if (cp) {
2239184588Sdfr			*cp = savedc;
2240184588Sdfr			seclist = cp + 1;
2241184588Sdfr		} else {
2242184588Sdfr			break;
2243184588Sdfr		}
2244184588Sdfr	}
2245184588Sdfr	return (0);
2246184588Sdfr}
2247184588Sdfr
2248184588Sdfr/*
22491558Srgrimes * Parse the option string and update fields.
22501558Srgrimes * Option arguments may either be -<option>=<value> or
22511558Srgrimes * -<option> <value>
22521558Srgrimes */
2253285128Straszstatic int
2254216587Scharnierdo_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2255216587Scharnier	int *has_hostp, int *exflagsp, struct xucred *cr)
22561558Srgrimes{
22571558Srgrimes	char *cpoptarg, *cpoptend;
22581558Srgrimes	char *cp, *endcp, *cpopt, savedc, savedc2;
22591558Srgrimes	int allflag, usedarg;
22601558Srgrimes
226151968Salfred	savedc2 = '\0';
22621558Srgrimes	cpopt = *cpp;
22631558Srgrimes	cpopt++;
22641558Srgrimes	cp = *endcpp;
22651558Srgrimes	savedc = *cp;
22661558Srgrimes	*cp = '\0';
22671558Srgrimes	while (cpopt && *cpopt) {
22681558Srgrimes		allflag = 1;
22691558Srgrimes		usedarg = -2;
227037663Scharnier		if ((cpoptend = strchr(cpopt, ','))) {
22711558Srgrimes			*cpoptend++ = '\0';
227237663Scharnier			if ((cpoptarg = strchr(cpopt, '=')))
22731558Srgrimes				*cpoptarg++ = '\0';
22741558Srgrimes		} else {
227537663Scharnier			if ((cpoptarg = strchr(cpopt, '=')))
22761558Srgrimes				*cpoptarg++ = '\0';
22771558Srgrimes			else {
22781558Srgrimes				*cp = savedc;
22791558Srgrimes				nextfield(&cp, &endcp);
22801558Srgrimes				**endcpp = '\0';
22811558Srgrimes				if (endcp > cp && *cp != '-') {
22821558Srgrimes					cpoptarg = cp;
22831558Srgrimes					savedc2 = *endcp;
22841558Srgrimes					*endcp = '\0';
22851558Srgrimes					usedarg = 0;
22861558Srgrimes				}
22871558Srgrimes			}
22881558Srgrimes		}
22891558Srgrimes		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
22901558Srgrimes			*exflagsp |= MNT_EXRDONLY;
22911558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
22921558Srgrimes		    !(allflag = strcmp(cpopt, "mapall")) ||
22931558Srgrimes		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
22941558Srgrimes			usedarg++;
22951558Srgrimes			parsecred(cpoptarg, cr);
22961558Srgrimes			if (allflag == 0) {
22971558Srgrimes				*exflagsp |= MNT_EXPORTANON;
22981558Srgrimes				opt_flags |= OP_MAPALL;
22991558Srgrimes			} else
23001558Srgrimes				opt_flags |= OP_MAPROOT;
23011558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
230275801Siedowse		    !strcmp(cpopt, "m"))) {
23031558Srgrimes			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
230437663Scharnier				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
23051558Srgrimes				return (1);
23061558Srgrimes			}
23071558Srgrimes			usedarg++;
23081558Srgrimes			opt_flags |= OP_MASK;
23091558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
23101558Srgrimes			!strcmp(cpopt, "n"))) {
231174462Salfred			if (strchr(cpoptarg, '/') != NULL) {
231274462Salfred				if (debug)
231374462Salfred					fprintf(stderr, "setting OP_MASKLEN\n");
231474462Salfred				opt_flags |= OP_MASKLEN;
231574462Salfred			}
23161558Srgrimes			if (grp->gr_type != GT_NULL) {
231737663Scharnier				syslog(LOG_ERR, "network/host conflict");
23181558Srgrimes				return (1);
23191558Srgrimes			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
232037663Scharnier				syslog(LOG_ERR, "bad net: %s", cpoptarg);
23211558Srgrimes				return (1);
23221558Srgrimes			}
23231558Srgrimes			grp->gr_type = GT_NET;
23241558Srgrimes			*has_hostp = 1;
23251558Srgrimes			usedarg++;
23261558Srgrimes			opt_flags |= OP_NET;
23271558Srgrimes		} else if (!strcmp(cpopt, "alldirs")) {
23281558Srgrimes			opt_flags |= OP_ALLDIRS;
232927447Sdfr		} else if (!strcmp(cpopt, "public")) {
233027447Sdfr			*exflagsp |= MNT_EXPUBLIC;
233127447Sdfr		} else if (!strcmp(cpopt, "webnfs")) {
233227447Sdfr			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
233327447Sdfr			opt_flags |= OP_MAPALL;
233427447Sdfr		} else if (cpoptarg && !strcmp(cpopt, "index")) {
233527447Sdfr			ep->ex_indexfile = strdup(cpoptarg);
2336100336Sjoerg		} else if (!strcmp(cpopt, "quiet")) {
2337100336Sjoerg			opt_flags |= OP_QUIET;
2338247034Spluknet		} else if (cpoptarg && !strcmp(cpopt, "sec")) {
2339184588Sdfr			if (parsesec(cpoptarg, ep))
2340184588Sdfr				return (1);
2341184588Sdfr			opt_flags |= OP_SEC;
2342184588Sdfr			usedarg++;
23431558Srgrimes		} else {
234437663Scharnier			syslog(LOG_ERR, "bad opt %s", cpopt);
23451558Srgrimes			return (1);
23461558Srgrimes		}
23471558Srgrimes		if (usedarg >= 0) {
23481558Srgrimes			*endcp = savedc2;
23491558Srgrimes			**endcpp = savedc;
23501558Srgrimes			if (usedarg > 0) {
23511558Srgrimes				*cpp = cp;
23521558Srgrimes				*endcpp = endcp;
23531558Srgrimes			}
23541558Srgrimes			return (0);
23551558Srgrimes		}
23561558Srgrimes		cpopt = cpoptend;
23571558Srgrimes	}
23581558Srgrimes	**endcpp = savedc;
23591558Srgrimes	return (0);
23601558Srgrimes}
23611558Srgrimes
23621558Srgrimes/*
23631558Srgrimes * Translate a character string to the corresponding list of network
23641558Srgrimes * addresses for a hostname.
23651558Srgrimes */
2366285128Straszstatic int
2367216587Scharnierget_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
23681558Srgrimes{
23697401Swpaul	struct grouplist *checkgrp;
237075635Siedowse	struct addrinfo *ai, *tai, hints;
237174462Salfred	int ecode;
237274462Salfred	char host[NI_MAXHOST];
23731558Srgrimes
237474462Salfred	if (grp->gr_type != GT_NULL) {
237574462Salfred		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
23761558Srgrimes		return (1);
23771558Srgrimes	}
237874462Salfred	memset(&hints, 0, sizeof hints);
237974462Salfred	hints.ai_flags = AI_CANONNAME;
238074462Salfred	hints.ai_protocol = IPPROTO_UDP;
238174462Salfred	ecode = getaddrinfo(cp, NULL, &hints, &ai);
238274462Salfred	if (ecode != 0) {
238375635Siedowse		syslog(LOG_ERR,"can't get address info for host %s", cp);
238474462Salfred		return 1;
238574462Salfred	}
238674462Salfred	grp->gr_ptr.gt_addrinfo = ai;
238774462Salfred	while (ai != NULL) {
238874462Salfred		if (ai->ai_canonname == NULL) {
238974462Salfred			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2390146187Sume			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
239174462Salfred				strlcpy(host, "?", sizeof(host));
239274462Salfred			ai->ai_canonname = strdup(host);
239374462Salfred			ai->ai_flags |= AI_CANONNAME;
239475641Siedowse		}
239574462Salfred		if (debug)
239675635Siedowse			fprintf(stderr, "got host %s\n", ai->ai_canonname);
239775635Siedowse		/*
239875635Siedowse		 * Sanity check: make sure we don't already have an entry
239975635Siedowse		 * for this host in the grouplist.
240075635Siedowse		 */
240175635Siedowse		for (checkgrp = tgrp; checkgrp != NULL;
240275635Siedowse		    checkgrp = checkgrp->gr_next) {
240375635Siedowse			if (checkgrp->gr_type != GT_HOST)
240475635Siedowse				continue;
240575635Siedowse			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
240675635Siedowse			    tai = tai->ai_next) {
240775801Siedowse				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
240875635Siedowse					continue;
240975635Siedowse				if (debug)
241075635Siedowse					fprintf(stderr,
241175635Siedowse					    "ignoring duplicate host %s\n",
241275635Siedowse					    ai->ai_canonname);
241375635Siedowse				grp->gr_type = GT_IGNORE;
241475635Siedowse				return (0);
241575635Siedowse			}
241675635Siedowse		}
241774462Salfred		ai = ai->ai_next;
24181558Srgrimes	}
241975635Siedowse	grp->gr_type = GT_HOST;
24201558Srgrimes	return (0);
24211558Srgrimes}
24221558Srgrimes
24231558Srgrimes/*
24241558Srgrimes * Free up an exports list component
24251558Srgrimes */
2426285128Straszstatic void
2427216587Scharnierfree_exp(struct exportlist *ep)
24281558Srgrimes{
24291558Srgrimes
24301558Srgrimes	if (ep->ex_defdir) {
24311558Srgrimes		free_host(ep->ex_defdir->dp_hosts);
24321558Srgrimes		free((caddr_t)ep->ex_defdir);
24331558Srgrimes	}
24341558Srgrimes	if (ep->ex_fsdir)
24351558Srgrimes		free(ep->ex_fsdir);
243627447Sdfr	if (ep->ex_indexfile)
243727447Sdfr		free(ep->ex_indexfile);
24381558Srgrimes	free_dir(ep->ex_dirl);
24391558Srgrimes	free((caddr_t)ep);
24401558Srgrimes}
24411558Srgrimes
24421558Srgrimes/*
24431558Srgrimes * Free hosts.
24441558Srgrimes */
2445285128Straszstatic void
2446216587Scharnierfree_host(struct hostlist *hp)
24471558Srgrimes{
24481558Srgrimes	struct hostlist *hp2;
24491558Srgrimes
24501558Srgrimes	while (hp) {
24511558Srgrimes		hp2 = hp;
24521558Srgrimes		hp = hp->ht_next;
24531558Srgrimes		free((caddr_t)hp2);
24541558Srgrimes	}
24551558Srgrimes}
24561558Srgrimes
2457285128Straszstatic struct hostlist *
2458216587Scharnierget_ht(void)
24591558Srgrimes{
24601558Srgrimes	struct hostlist *hp;
24611558Srgrimes
24621558Srgrimes	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
24631558Srgrimes	if (hp == (struct hostlist *)NULL)
24641558Srgrimes		out_of_mem();
24651558Srgrimes	hp->ht_next = (struct hostlist *)NULL;
24669336Sdfr	hp->ht_flag = 0;
24671558Srgrimes	return (hp);
24681558Srgrimes}
24691558Srgrimes
24701558Srgrimes/*
24711558Srgrimes * Out of memory, fatal
24721558Srgrimes */
2473285128Straszstatic void
2474216587Scharnierout_of_mem(void)
24751558Srgrimes{
24761558Srgrimes
247737663Scharnier	syslog(LOG_ERR, "out of memory");
24781558Srgrimes	exit(2);
24791558Srgrimes}
24801558Srgrimes
24811558Srgrimes/*
2482158857Srodrigc * Do the nmount() syscall with the update flag to push the export info into
24831558Srgrimes * the kernel.
24841558Srgrimes */
2485285128Straszstatic int
2486158857Srodrigcdo_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2487158857Srodrigc    struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
24881558Srgrimes{
248975841Siedowse	struct statfs fsb1;
249074462Salfred	struct addrinfo *ai;
2491282214Strasz	struct export_args *eap;
2492158857Srodrigc	char errmsg[255];
2493158857Srodrigc	char *cp;
24941558Srgrimes	int done;
2495158857Srodrigc	char savedc;
2496158857Srodrigc	struct iovec *iov;
2497184588Sdfr	int i, iovlen;
2498158857Srodrigc	int ret;
2499192934Srmacklem	struct nfsex_args nfsea;
25001558Srgrimes
2501282214Strasz	eap = &nfsea.export;
2502192934Srmacklem
2503158857Srodrigc	cp = NULL;
2504158857Srodrigc	savedc = '\0';
2505158857Srodrigc	iov = NULL;
2506158857Srodrigc	iovlen = 0;
2507158857Srodrigc	ret = 0;
250875801Siedowse
2509192934Srmacklem	bzero(eap, sizeof (struct export_args));
2510158857Srodrigc	bzero(errmsg, sizeof(errmsg));
2511192934Srmacklem	eap->ex_flags = exflags;
2512192934Srmacklem	eap->ex_anon = *anoncrp;
2513192934Srmacklem	eap->ex_indexfile = ep->ex_indexfile;
251475641Siedowse	if (grp->gr_type == GT_HOST)
251574462Salfred		ai = grp->gr_ptr.gt_addrinfo;
251675641Siedowse	else
251775641Siedowse		ai = NULL;
2518192934Srmacklem	eap->ex_numsecflavors = ep->ex_numsecflavors;
2519192934Srmacklem	for (i = 0; i < eap->ex_numsecflavors; i++)
2520192934Srmacklem		eap->ex_secflavors[i] = ep->ex_secflavors[i];
2521192934Srmacklem	if (eap->ex_numsecflavors == 0) {
2522192934Srmacklem		eap->ex_numsecflavors = 1;
2523192934Srmacklem		eap->ex_secflavors[0] = AUTH_SYS;
2524184588Sdfr	}
25251558Srgrimes	done = FALSE;
2526158857Srodrigc
2527192934Srmacklem	if (v4root_phase == 0) {
2528192934Srmacklem		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2529192934Srmacklem		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2530192934Srmacklem		build_iovec(&iov, &iovlen, "from", NULL, 0);
2531192934Srmacklem		build_iovec(&iov, &iovlen, "update", NULL, 0);
2532192934Srmacklem		build_iovec(&iov, &iovlen, "export", eap,
2533192934Srmacklem		    sizeof (struct export_args));
2534192934Srmacklem		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2535192934Srmacklem	}
2536158857Srodrigc
25371558Srgrimes	while (!done) {
25381558Srgrimes		switch (grp->gr_type) {
25391558Srgrimes		case GT_HOST:
254075641Siedowse			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
254174462Salfred				goto skip;
2542192934Srmacklem			eap->ex_addr = ai->ai_addr;
2543192934Srmacklem			eap->ex_addrlen = ai->ai_addrlen;
2544192934Srmacklem			eap->ex_masklen = 0;
25451558Srgrimes			break;
25461558Srgrimes		case GT_NET:
254775801Siedowse			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
254874462Salfred			    have_v6 == 0)
254974462Salfred				goto skip;
2550192934Srmacklem			eap->ex_addr =
255175801Siedowse			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2552192934Srmacklem			eap->ex_addrlen =
2553158857Srodrigc			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2554192934Srmacklem			eap->ex_mask =
255575801Siedowse			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2556192934Srmacklem			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
25571558Srgrimes			break;
255875641Siedowse		case GT_DEFAULT:
2559192934Srmacklem			eap->ex_addr = NULL;
2560192934Srmacklem			eap->ex_addrlen = 0;
2561192934Srmacklem			eap->ex_mask = NULL;
2562192934Srmacklem			eap->ex_masklen = 0;
256375641Siedowse			break;
25647401Swpaul		case GT_IGNORE:
2565158857Srodrigc			ret = 0;
2566158857Srodrigc			goto error_exit;
25677401Swpaul			break;
25681558Srgrimes		default:
256937663Scharnier			syslog(LOG_ERR, "bad grouptype");
25701558Srgrimes			if (cp)
25711558Srgrimes				*cp = savedc;
2572158857Srodrigc			ret = 1;
2573158857Srodrigc			goto error_exit;
2574298089Spfg		}
25751558Srgrimes
25761558Srgrimes		/*
2577192934Srmacklem		 * For V4:, use the nfssvc() syscall, instead of mount().
25781558Srgrimes		 */
2579192934Srmacklem		if (v4root_phase == 2) {
2580192934Srmacklem			nfsea.fspec = v4root_dirpath;
2581282214Strasz			if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
2582192934Srmacklem				syslog(LOG_ERR, "Exporting V4: failed");
2583192934Srmacklem				return (2);
2584158857Srodrigc			}
2585192934Srmacklem		} else {
2586192934Srmacklem			/*
2587192934Srmacklem			 * XXX:
2588192934Srmacklem			 * Maybe I should just use the fsb->f_mntonname path
2589192934Srmacklem			 * instead of looping back up the dirp to the mount
2590192934Srmacklem			 * point??
2591192934Srmacklem			 * Also, needs to know how to export all types of local
2592192934Srmacklem			 * exportable filesystems and not just "ufs".
2593192934Srmacklem			 */
2594192934Srmacklem			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2595192934Srmacklem			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2596192934Srmacklem			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2597192934Srmacklem			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2598192934Srmacklem			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2599192934Srmacklem			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2600270183Sbdrewery			errmsg[0] = '\0';
2601192934Srmacklem
2602192934Srmacklem			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2603192934Srmacklem				if (cp)
2604192934Srmacklem					*cp-- = savedc;
2605192934Srmacklem				else
2606192934Srmacklem					cp = dirp + dirplen - 1;
2607192934Srmacklem				if (opt_flags & OP_QUIET) {
2608192934Srmacklem					ret = 1;
2609192934Srmacklem					goto error_exit;
2610192934Srmacklem				}
2611192934Srmacklem				if (errno == EPERM) {
2612192934Srmacklem					if (debug)
2613239744Sdelphij						warnx("can't change attributes for %s: %s",
2614239744Sdelphij						    dirp, errmsg);
2615192934Srmacklem					syslog(LOG_ERR,
2616239744Sdelphij					   "can't change attributes for %s: %s",
2617239744Sdelphij					    dirp, errmsg);
2618192934Srmacklem					ret = 1;
2619192934Srmacklem					goto error_exit;
2620192934Srmacklem				}
2621192934Srmacklem				if (opt_flags & OP_ALLDIRS) {
2622192934Srmacklem					if (errno == EINVAL)
2623192934Srmacklem						syslog(LOG_ERR,
2624100336Sjoerg		"-alldirs requested but %s is not a filesystem mountpoint",
2625192934Srmacklem						    dirp);
2626192934Srmacklem					else
2627192934Srmacklem						syslog(LOG_ERR,
2628192934Srmacklem						    "could not remount %s: %m",
2629192934Srmacklem						    dirp);
2630192934Srmacklem					ret = 1;
2631192934Srmacklem					goto error_exit;
2632192934Srmacklem				}
2633192934Srmacklem				/* back up over the last component */
2634192934Srmacklem				while (*cp == '/' && cp > dirp)
2635192934Srmacklem					cp--;
2636192934Srmacklem				while (*(cp - 1) != '/' && cp > dirp)
2637192934Srmacklem					cp--;
2638192934Srmacklem				if (cp == dirp) {
2639192934Srmacklem					if (debug)
2640192934Srmacklem						warnx("mnt unsucc");
2641192934Srmacklem					syslog(LOG_ERR, "can't export %s %s",
2642192934Srmacklem					    dirp, errmsg);
2643192934Srmacklem					ret = 1;
2644192934Srmacklem					goto error_exit;
2645192934Srmacklem				}
2646192934Srmacklem				savedc = *cp;
2647192934Srmacklem				*cp = '\0';
2648192934Srmacklem				/*
2649192934Srmacklem				 * Check that we're still on the same
2650192934Srmacklem				 * filesystem.
2651192934Srmacklem				 */
2652192934Srmacklem				if (statfs(dirp, &fsb1) != 0 ||
2653192934Srmacklem				    bcmp(&fsb1.f_fsid, &fsb->f_fsid,
2654192934Srmacklem				    sizeof (fsb1.f_fsid)) != 0) {
2655192934Srmacklem					*cp = savedc;
2656100336Sjoerg					syslog(LOG_ERR,
2657192934Srmacklem					    "can't export %s %s", dirp,
2658192934Srmacklem					    errmsg);
2659192934Srmacklem					ret = 1;
2660192934Srmacklem					goto error_exit;
2661192934Srmacklem				}
26621558Srgrimes			}
26631558Srgrimes		}
2664192934Srmacklem
2665192934Srmacklem		/*
2666192934Srmacklem		 * For the experimental server:
2667192934Srmacklem		 * If this is the public directory, get the file handle
2668192934Srmacklem		 * and load it into the kernel via the nfssvc() syscall.
2669192934Srmacklem		 */
2670282214Strasz		if ((exflags & MNT_EXPUBLIC) != 0) {
2671192934Srmacklem			fhandle_t fh;
2672192934Srmacklem			char *public_name;
2673192934Srmacklem
2674192934Srmacklem			if (eap->ex_indexfile != NULL)
2675192934Srmacklem				public_name = eap->ex_indexfile;
2676192934Srmacklem			else
2677192934Srmacklem				public_name = dirp;
2678192934Srmacklem			if (getfh(public_name, &fh) < 0)
2679192934Srmacklem				syslog(LOG_ERR,
2680192934Srmacklem				    "Can't get public fh for %s", public_name);
2681192934Srmacklem			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
2682192934Srmacklem				syslog(LOG_ERR,
2683192934Srmacklem				    "Can't set public fh for %s", public_name);
2684192934Srmacklem			else
2685192934Srmacklem				has_publicfh = 1;
2686192934Srmacklem		}
268774462Salfredskip:
268875641Siedowse		if (ai != NULL)
268974462Salfred			ai = ai->ai_next;
269075641Siedowse		if (ai == NULL)
26911558Srgrimes			done = TRUE;
26921558Srgrimes	}
26931558Srgrimes	if (cp)
26941558Srgrimes		*cp = savedc;
2695158857Srodrigcerror_exit:
2696158857Srodrigc	/* free strings allocated by strdup() in getmntopts.c */
2697158857Srodrigc	if (iov != NULL) {
2698158857Srodrigc		free(iov[0].iov_base); /* fstype */
2699158857Srodrigc		free(iov[2].iov_base); /* fspath */
2700158857Srodrigc		free(iov[4].iov_base); /* from */
2701158857Srodrigc		free(iov[6].iov_base); /* update */
2702158857Srodrigc		free(iov[8].iov_base); /* export */
2703158857Srodrigc		free(iov[10].iov_base); /* errmsg */
2704158857Srodrigc
2705158857Srodrigc		/* free iov, allocated by realloc() */
2706158857Srodrigc		free(iov);
2707158857Srodrigc	}
2708158857Srodrigc	return (ret);
27091558Srgrimes}
27101558Srgrimes
27111558Srgrimes/*
27121558Srgrimes * Translate a net address.
271375801Siedowse *
271475801Siedowse * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
27151558Srgrimes */
2716285128Straszstatic int
2717216587Scharnierget_net(char *cp, struct netmsk *net, int maskflg)
27181558Srgrimes{
271975861Siedowse	struct netent *np = NULL;
272074462Salfred	char *name, *p, *prefp;
272175801Siedowse	struct sockaddr_in sin;
272275861Siedowse	struct sockaddr *sa = NULL;
272374462Salfred	struct addrinfo hints, *ai = NULL;
272474462Salfred	char netname[NI_MAXHOST];
272574462Salfred	long preflen;
27261558Srgrimes
272775635Siedowse	p = prefp = NULL;
272874462Salfred	if ((opt_flags & OP_MASKLEN) && !maskflg) {
272974462Salfred		p = strchr(cp, '/');
273074462Salfred		*p = '\0';
273174462Salfred		prefp = p + 1;
273274462Salfred	}
273374462Salfred
273475861Siedowse	/*
273575861Siedowse	 * Check for a numeric address first. We wish to avoid
273675861Siedowse	 * possible DNS lookups in getnetbyname().
273775861Siedowse	 */
273875861Siedowse	if (isxdigit(*cp) || *cp == ':') {
273974462Salfred		memset(&hints, 0, sizeof hints);
274075801Siedowse		/* Ensure the mask and the network have the same family. */
274175801Siedowse		if (maskflg && (opt_flags & OP_NET))
274275801Siedowse			hints.ai_family = net->nt_net.ss_family;
274375801Siedowse		else if (!maskflg && (opt_flags & OP_HAVEMASK))
274475801Siedowse			hints.ai_family = net->nt_mask.ss_family;
274575801Siedowse		else
274675801Siedowse			hints.ai_family = AF_UNSPEC;
274774462Salfred		hints.ai_flags = AI_NUMERICHOST;
274875861Siedowse		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
274975861Siedowse			sa = ai->ai_addr;
275075861Siedowse		if (sa != NULL && ai->ai_family == AF_INET) {
275174462Salfred			/*
275275801Siedowse			 * The address in `cp' is really a network address, so
275375801Siedowse			 * use inet_network() to re-interpret this correctly.
275475801Siedowse			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
275574462Salfred			 */
275675801Siedowse			bzero(&sin, sizeof sin);
275774462Salfred			sin.sin_family = AF_INET;
275874462Salfred			sin.sin_len = sizeof sin;
275975801Siedowse			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
276074462Salfred			if (debug)
276175801Siedowse				fprintf(stderr, "get_net: v4 addr %s\n",
276275801Siedowse				    inet_ntoa(sin.sin_addr));
276374462Salfred			sa = (struct sockaddr *)&sin;
276475861Siedowse		}
276575861Siedowse	}
276675861Siedowse	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
276775861Siedowse		bzero(&sin, sizeof sin);
276875861Siedowse		sin.sin_family = AF_INET;
276975861Siedowse		sin.sin_len = sizeof sin;
277075861Siedowse		sin.sin_addr = inet_makeaddr(np->n_net, 0);
277175861Siedowse		sa = (struct sockaddr *)&sin;
277275861Siedowse	}
277375861Siedowse	if (sa == NULL)
277474462Salfred		goto fail;
277525318Spst
277675801Siedowse	if (maskflg) {
277775801Siedowse		/* The specified sockaddr is a mask. */
277875801Siedowse		if (checkmask(sa) != 0)
277975801Siedowse			goto fail;
278075801Siedowse		bcopy(sa, &net->nt_mask, sa->sa_len);
278175801Siedowse		opt_flags |= OP_HAVEMASK;
278275801Siedowse	} else {
278375801Siedowse		/* The specified sockaddr is a network address. */
278475801Siedowse		bcopy(sa, &net->nt_net, sa->sa_len);
278574462Salfred
278675801Siedowse		/* Get a network name for the export list. */
278775801Siedowse		if (np) {
278875801Siedowse			name = np->n_name;
278975801Siedowse		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2790146187Sume		   NULL, 0, NI_NUMERICHOST) == 0) {
279175801Siedowse			name = netname;
279275801Siedowse		} else {
279375801Siedowse			goto fail;
279475801Siedowse		}
279575801Siedowse		if ((net->nt_name = strdup(name)) == NULL)
279675801Siedowse			out_of_mem();
279775801Siedowse
279875801Siedowse		/*
279975801Siedowse		 * Extract a mask from either a "/<masklen>" suffix, or
280075801Siedowse		 * from the class of an IPv4 address.
280175801Siedowse		 */
280274462Salfred		if (opt_flags & OP_MASKLEN) {
280374462Salfred			preflen = strtol(prefp, NULL, 10);
280475801Siedowse			if (preflen < 0L || preflen == LONG_MAX)
280574462Salfred				goto fail;
280675801Siedowse			bcopy(sa, &net->nt_mask, sa->sa_len);
280775801Siedowse			if (makemask(&net->nt_mask, (int)preflen) != 0)
280875801Siedowse				goto fail;
280975801Siedowse			opt_flags |= OP_HAVEMASK;
281074462Salfred			*p = '/';
281175801Siedowse		} else if (sa->sa_family == AF_INET &&
281275801Siedowse		    (opt_flags & OP_MASK) == 0) {
281375801Siedowse			in_addr_t addr;
281474462Salfred
281575801Siedowse			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
281675801Siedowse			if (IN_CLASSA(addr))
281775801Siedowse				preflen = 8;
281875801Siedowse			else if (IN_CLASSB(addr))
281975801Siedowse				preflen = 16;
282075801Siedowse			else if (IN_CLASSC(addr))
282175801Siedowse				preflen = 24;
282275801Siedowse			else if (IN_CLASSD(addr))
282375801Siedowse				preflen = 28;
282475801Siedowse			else
282575801Siedowse				preflen = 32;	/* XXX */
282675801Siedowse
282775801Siedowse			bcopy(sa, &net->nt_mask, sa->sa_len);
282875801Siedowse			makemask(&net->nt_mask, (int)preflen);
282975801Siedowse			opt_flags |= OP_HAVEMASK;
283074462Salfred		}
283174462Salfred	}
283274462Salfred
283374462Salfred	if (ai)
283474462Salfred		freeaddrinfo(ai);
283574462Salfred	return 0;
283674462Salfred
283774462Salfredfail:
283874462Salfred	if (ai)
283974462Salfred		freeaddrinfo(ai);
284074462Salfred	return 1;
28411558Srgrimes}
28421558Srgrimes
28431558Srgrimes/*
28441558Srgrimes * Parse out the next white space separated field
28451558Srgrimes */
2846285128Straszstatic void
2847216587Scharniernextfield(char **cp, char **endcp)
28481558Srgrimes{
28491558Srgrimes	char *p;
2850347341Smav	char quot = 0;
28511558Srgrimes
28521558Srgrimes	p = *cp;
28531558Srgrimes	while (*p == ' ' || *p == '\t')
28541558Srgrimes		p++;
2855347341Smav	*cp = p;
2856347341Smav	while (*p != '\0') {
2857347341Smav		if (quot) {
2858347341Smav			if (*p == quot)
2859347341Smav				quot = 0;
2860347341Smav		} else {
2861347341Smav			if (*p == '\\' && *(p + 1) != '\0')
2862347341Smav				p++;
2863347341Smav			else if (*p == '\'' || *p == '"')
2864347341Smav				quot = *p;
2865347341Smav			else if (*p == ' ' || *p == '\t')
2866347341Smav				break;
2867347341Smav		}
2868347341Smav		p++;
2869347341Smav	};
2870347341Smav	*endcp = p;
28711558Srgrimes}
28721558Srgrimes
28731558Srgrimes/*
28741558Srgrimes * Get an exports file line. Skip over blank lines and handle line
28751558Srgrimes * continuations.
28761558Srgrimes */
2877285128Straszstatic int
2878216587Scharnierget_line(void)
28791558Srgrimes{
28801558Srgrimes	char *p, *cp;
288196622Siedowse	size_t len;
28821558Srgrimes	int totlen, cont_line;
28831558Srgrimes
28841558Srgrimes	/*
28851558Srgrimes	 * Loop around ignoring blank lines and getting all continuation lines.
28861558Srgrimes	 */
28871558Srgrimes	p = line;
28881558Srgrimes	totlen = 0;
28891558Srgrimes	do {
289096622Siedowse		if ((p = fgetln(exp_file, &len)) == NULL)
28911558Srgrimes			return (0);
28921558Srgrimes		cp = p + len - 1;
28931558Srgrimes		cont_line = 0;
28941558Srgrimes		while (cp >= p &&
28951558Srgrimes		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
28961558Srgrimes			if (*cp == '\\')
28971558Srgrimes				cont_line = 1;
28981558Srgrimes			cp--;
28991558Srgrimes			len--;
29001558Srgrimes		}
290179117Sdd		if (cont_line) {
290279117Sdd			*++cp = ' ';
290379117Sdd			len++;
290479117Sdd		}
290596622Siedowse		if (linesize < len + totlen + 1) {
290696622Siedowse			linesize = len + totlen + 1;
290796622Siedowse			line = realloc(line, linesize);
290896622Siedowse			if (line == NULL)
290996622Siedowse				out_of_mem();
29101558Srgrimes		}
291196622Siedowse		memcpy(line + totlen, p, len);
291296622Siedowse		totlen += len;
291396622Siedowse		line[totlen] = '\0';
29141558Srgrimes	} while (totlen == 0 || cont_line);
29151558Srgrimes	return (1);
29161558Srgrimes}
29171558Srgrimes
29181558Srgrimes/*
29191558Srgrimes * Parse a description of a credential.
29201558Srgrimes */
2921285128Straszstatic void
2922216587Scharnierparsecred(char *namelist, struct xucred *cr)
29231558Srgrimes{
29241558Srgrimes	char *name;
29251558Srgrimes	int cnt;
29261558Srgrimes	char *names;
29271558Srgrimes	struct passwd *pw;
29281558Srgrimes	struct group *gr;
2929194498Sbrooks	gid_t groups[XU_NGROUPS + 1];
2930136051Sstefanf	int ngroups;
29311558Srgrimes
293291354Sdd	cr->cr_version = XUCRED_VERSION;
29331558Srgrimes	/*
293437663Scharnier	 * Set up the unprivileged user.
29351558Srgrimes	 */
29361558Srgrimes	cr->cr_uid = -2;
29371558Srgrimes	cr->cr_groups[0] = -2;
29381558Srgrimes	cr->cr_ngroups = 1;
29391558Srgrimes	/*
29401558Srgrimes	 * Get the user's password table entry.
29411558Srgrimes	 */
2942347341Smav	names = namelist;
2943347341Smav	name = strsep_quote(&names, ":");
2944293305Sjpaetzel	/* Bug?  name could be NULL here */
29451558Srgrimes	if (isdigit(*name) || *name == '-')
29461558Srgrimes		pw = getpwuid(atoi(name));
29471558Srgrimes	else
29481558Srgrimes		pw = getpwnam(name);
29491558Srgrimes	/*
29501558Srgrimes	 * Credentials specified as those of a user.
29511558Srgrimes	 */
29521558Srgrimes	if (names == NULL) {
29531558Srgrimes		if (pw == NULL) {
295437663Scharnier			syslog(LOG_ERR, "unknown user: %s", name);
29551558Srgrimes			return;
29561558Srgrimes		}
29571558Srgrimes		cr->cr_uid = pw->pw_uid;
2958194498Sbrooks		ngroups = XU_NGROUPS + 1;
2959333197Savg		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) {
296037663Scharnier			syslog(LOG_ERR, "too many groups");
2961333197Savg			ngroups = XU_NGROUPS + 1;
2962333197Savg		}
2963333197Savg
29641558Srgrimes		/*
2965136051Sstefanf		 * Compress out duplicate.
29661558Srgrimes		 */
29671558Srgrimes		cr->cr_ngroups = ngroups - 1;
29681558Srgrimes		cr->cr_groups[0] = groups[0];
29691558Srgrimes		for (cnt = 2; cnt < ngroups; cnt++)
29701558Srgrimes			cr->cr_groups[cnt - 1] = groups[cnt];
29711558Srgrimes		return;
29721558Srgrimes	}
29731558Srgrimes	/*
29741558Srgrimes	 * Explicit credential specified as a colon separated list:
29751558Srgrimes	 *	uid:gid:gid:...
29761558Srgrimes	 */
29771558Srgrimes	if (pw != NULL)
29781558Srgrimes		cr->cr_uid = pw->pw_uid;
29791558Srgrimes	else if (isdigit(*name) || *name == '-')
29801558Srgrimes		cr->cr_uid = atoi(name);
29811558Srgrimes	else {
298237663Scharnier		syslog(LOG_ERR, "unknown user: %s", name);
29831558Srgrimes		return;
29841558Srgrimes	}
29851558Srgrimes	cr->cr_ngroups = 0;
2986194498Sbrooks	while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
2987347341Smav		name = strsep_quote(&names, ":");
29881558Srgrimes		if (isdigit(*name) || *name == '-') {
29891558Srgrimes			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
29901558Srgrimes		} else {
29911558Srgrimes			if ((gr = getgrnam(name)) == NULL) {
299237663Scharnier				syslog(LOG_ERR, "unknown group: %s", name);
29931558Srgrimes				continue;
29941558Srgrimes			}
29951558Srgrimes			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
29961558Srgrimes		}
29971558Srgrimes	}
2998194498Sbrooks	if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
299937663Scharnier		syslog(LOG_ERR, "too many groups");
30001558Srgrimes}
30011558Srgrimes
3002194880Sdfr#define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
30031558Srgrimes/*
30041558Srgrimes * Routines that maintain the remote mounttab
30051558Srgrimes */
3006285128Straszstatic void
3007216587Scharnierget_mountlist(void)
30081558Srgrimes{
3009324955Smanu	struct mountlist *mlp;
301023681Speter	char *host, *dirp, *cp;
30111558Srgrimes	char str[STRSIZ];
30121558Srgrimes	FILE *mlfile;
30131558Srgrimes
30141558Srgrimes	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
301553117Sbillf		if (errno == ENOENT)
301653117Sbillf			return;
301753117Sbillf		else {
301853117Sbillf			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
301953117Sbillf			return;
302053117Sbillf		}
30211558Srgrimes	}
30221558Srgrimes	while (fgets(str, STRSIZ, mlfile) != NULL) {
302323681Speter		cp = str;
302423681Speter		host = strsep(&cp, " \t\n");
302523681Speter		dirp = strsep(&cp, " \t\n");
302623681Speter		if (host == NULL || dirp == NULL)
30271558Srgrimes			continue;
30281558Srgrimes		mlp = (struct mountlist *)malloc(sizeof (*mlp));
302937663Scharnier		if (mlp == (struct mountlist *)NULL)
303037663Scharnier			out_of_mem();
3031194880Sdfr		strncpy(mlp->ml_host, host, MNTNAMLEN);
3032194880Sdfr		mlp->ml_host[MNTNAMLEN] = '\0';
3033194880Sdfr		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3034194880Sdfr		mlp->ml_dirp[MNTPATHLEN] = '\0';
3035324955Smanu
3036324955Smanu		SLIST_INSERT_HEAD(&mlhead, mlp, next);
30371558Srgrimes	}
30381558Srgrimes	fclose(mlfile);
30391558Srgrimes}
30401558Srgrimes
3041285128Straszstatic void
304275635Siedowsedel_mlist(char *hostp, char *dirp)
30431558Srgrimes{
3044324955Smanu	struct mountlist *mlp, *mlp2;
30451558Srgrimes	FILE *mlfile;
30461558Srgrimes	int fnd = 0;
30471558Srgrimes
3048324955Smanu	SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) {
30491558Srgrimes		if (!strcmp(mlp->ml_host, hostp) &&
30501558Srgrimes		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
30511558Srgrimes			fnd = 1;
3052324955Smanu			SLIST_REMOVE(&mlhead, mlp, mountlist, next);
3053324955Smanu			free((caddr_t)mlp);
30541558Srgrimes		}
30551558Srgrimes	}
30561558Srgrimes	if (fnd) {
30571558Srgrimes		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
305837663Scharnier			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
30591558Srgrimes			return;
30601558Srgrimes		}
3061324955Smanu		SLIST_FOREACH(mlp, &mlhead, next) {
30621558Srgrimes			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
30631558Srgrimes		}
30641558Srgrimes		fclose(mlfile);
30651558Srgrimes	}
30661558Srgrimes}
30671558Srgrimes
3068285128Straszstatic void
3069216587Scharnieradd_mlist(char *hostp, char *dirp)
30701558Srgrimes{
3071324955Smanu	struct mountlist *mlp;
30721558Srgrimes	FILE *mlfile;
30731558Srgrimes
3074324955Smanu	SLIST_FOREACH(mlp, &mlhead, next) {
30751558Srgrimes		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
30761558Srgrimes			return;
30771558Srgrimes	}
3078324955Smanu
30791558Srgrimes	mlp = (struct mountlist *)malloc(sizeof (*mlp));
308037663Scharnier	if (mlp == (struct mountlist *)NULL)
308137663Scharnier		out_of_mem();
3082194880Sdfr	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3083194880Sdfr	mlp->ml_host[MNTNAMLEN] = '\0';
3084194880Sdfr	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3085194880Sdfr	mlp->ml_dirp[MNTPATHLEN] = '\0';
3086324955Smanu	SLIST_INSERT_HEAD(&mlhead, mlp, next);
30871558Srgrimes	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
308837663Scharnier		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
30891558Srgrimes		return;
30901558Srgrimes	}
30911558Srgrimes	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
30921558Srgrimes	fclose(mlfile);
30931558Srgrimes}
30941558Srgrimes
30951558Srgrimes/*
30961558Srgrimes * Free up a group list.
30971558Srgrimes */
3098285128Straszstatic void
3099216587Scharnierfree_grp(struct grouplist *grp)
31001558Srgrimes{
31011558Srgrimes	if (grp->gr_type == GT_HOST) {
310274462Salfred		if (grp->gr_ptr.gt_addrinfo != NULL)
310374462Salfred			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
31041558Srgrimes	} else if (grp->gr_type == GT_NET) {
31051558Srgrimes		if (grp->gr_ptr.gt_net.nt_name)
31061558Srgrimes			free(grp->gr_ptr.gt_net.nt_name);
31071558Srgrimes	}
31081558Srgrimes	free((caddr_t)grp);
31091558Srgrimes}
31101558Srgrimes
31111558Srgrimes#ifdef DEBUG
3112285128Straszstatic void
31131558SrgrimesSYSLOG(int pri, const char *fmt, ...)
31141558Srgrimes{
31151558Srgrimes	va_list ap;
31161558Srgrimes
31171558Srgrimes	va_start(ap, fmt);
31181558Srgrimes	vfprintf(stderr, fmt, ap);
31191558Srgrimes	va_end(ap);
31201558Srgrimes}
31211558Srgrimes#endif /* DEBUG */
31221558Srgrimes
31231558Srgrimes/*
31241558Srgrimes * Check options for consistency.
31251558Srgrimes */
3126285128Straszstatic int
3127216587Scharniercheck_options(struct dirlist *dp)
31281558Srgrimes{
31291558Srgrimes
3130192934Srmacklem	if (v4root_phase == 0 && dp == NULL)
31311558Srgrimes	    return (1);
313283653Speter	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
313383653Speter	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
31341558Srgrimes	    return (1);
31351558Srgrimes	}
31361558Srgrimes	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
313775801Siedowse		syslog(LOG_ERR, "-mask requires -network");
313875801Siedowse		return (1);
31391558Srgrimes	}
314075801Siedowse	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
314175801Siedowse		syslog(LOG_ERR, "-network requires mask specification");
314275801Siedowse		return (1);
314375801Siedowse	}
314475801Siedowse	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
314575801Siedowse		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
314675801Siedowse		return (1);
314775801Siedowse	}
3148192934Srmacklem	if (v4root_phase > 0 &&
3149192934Srmacklem	    (opt_flags &
3150192934Srmacklem	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3151192934Srmacklem	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3152192934Srmacklem	    return (1);
3153192934Srmacklem	}
3154207689Srmacklem	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3155207689Srmacklem	    syslog(LOG_ERR, "-alldirs has multiple directories");
3156207689Srmacklem	    return (1);
3157207689Srmacklem	}
31581558Srgrimes	return (0);
31591558Srgrimes}
31601558Srgrimes
31611558Srgrimes/*
31621558Srgrimes * Check an absolute directory path for any symbolic links. Return true
31631558Srgrimes */
3164285128Straszstatic int
3165216587Scharniercheck_dirpath(char *dirp)
31661558Srgrimes{
31671558Srgrimes	char *cp;
31681558Srgrimes	int ret = 1;
31691558Srgrimes	struct stat sb;
31701558Srgrimes
31711558Srgrimes	cp = dirp + 1;
31721558Srgrimes	while (*cp && ret) {
31731558Srgrimes		if (*cp == '/') {
31741558Srgrimes			*cp = '\0';
31759336Sdfr			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
31761558Srgrimes				ret = 0;
31771558Srgrimes			*cp = '/';
31781558Srgrimes		}
31791558Srgrimes		cp++;
31801558Srgrimes	}
31819336Sdfr	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
31821558Srgrimes		ret = 0;
31831558Srgrimes	return (ret);
31841558Srgrimes}
31859336Sdfr
318675801Siedowse/*
318775801Siedowse * Make a netmask according to the specified prefix length. The ss_family
318875801Siedowse * and other non-address fields must be initialised before calling this.
318975801Siedowse */
3190285128Straszstatic int
319175801Siedowsemakemask(struct sockaddr_storage *ssp, int bitlen)
319274462Salfred{
319375801Siedowse	u_char *p;
319475801Siedowse	int bits, i, len;
319574462Salfred
319675801Siedowse	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
319775801Siedowse		return (-1);
3198103949Smike	if (bitlen > len * CHAR_BIT)
319975801Siedowse		return (-1);
320074462Salfred
320175801Siedowse	for (i = 0; i < len; i++) {
3202298912Saraujo		bits = MIN(CHAR_BIT, bitlen);
3203219125Sru		*p++ = (u_char)~0 << (CHAR_BIT - bits);
320475801Siedowse		bitlen -= bits;
320574462Salfred	}
320675801Siedowse	return 0;
320774462Salfred}
320874462Salfred
320975801Siedowse/*
321075801Siedowse * Check that the sockaddr is a valid netmask. Returns 0 if the mask
321175801Siedowse * is acceptable (i.e. of the form 1...10....0).
321275801Siedowse */
3213285128Straszstatic int
321475801Siedowsecheckmask(struct sockaddr *sa)
321574462Salfred{
321675801Siedowse	u_char *mask;
321775801Siedowse	int i, len;
321874462Salfred
321975801Siedowse	if ((mask = sa_rawaddr(sa, &len)) == NULL)
322075801Siedowse		return (-1);
322175801Siedowse
322275801Siedowse	for (i = 0; i < len; i++)
322375801Siedowse		if (mask[i] != 0xff)
322475801Siedowse			break;
322575801Siedowse	if (i < len) {
322675801Siedowse		if (~mask[i] & (u_char)(~mask[i] + 1))
322775801Siedowse			return (-1);
322875801Siedowse		i++;
322974462Salfred	}
323075801Siedowse	for (; i < len; i++)
323175801Siedowse		if (mask[i] != 0)
323275801Siedowse			return (-1);
323375801Siedowse	return (0);
323474462Salfred}
323574462Salfred
323675801Siedowse/*
323775801Siedowse * Compare two sockaddrs according to a specified mask. Return zero if
323875801Siedowse * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3239228990Suqs * If samask is NULL, perform a full comparison.
324075801Siedowse */
3241285128Straszstatic int
324275801Siedowsesacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
324374462Salfred{
324475801Siedowse	unsigned char *p1, *p2, *mask;
324575801Siedowse	int len, i;
324674462Salfred
324775801Siedowse	if (sa1->sa_family != sa2->sa_family ||
324875801Siedowse	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
324975801Siedowse	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
325075801Siedowse		return (1);
325175801Siedowse
325275801Siedowse	switch (sa1->sa_family) {
325374462Salfred	case AF_INET6:
325475801Siedowse		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
325575801Siedowse		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
325675801Siedowse			return (1);
325774462Salfred		break;
325874462Salfred	}
325974462Salfred
326075801Siedowse	/* Simple binary comparison if no mask specified. */
326175801Siedowse	if (samask == NULL)
326275801Siedowse		return (memcmp(p1, p2, len));
326374462Salfred
326475801Siedowse	/* Set up the mask, and do a mask-based comparison. */
326575801Siedowse	if (sa1->sa_family != samask->sa_family ||
326675801Siedowse	    (mask = sa_rawaddr(samask, NULL)) == NULL)
326775801Siedowse		return (1);
326874462Salfred
326975801Siedowse	for (i = 0; i < len; i++)
327075801Siedowse		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
327175801Siedowse			return (1);
327275801Siedowse	return (0);
327374462Salfred}
327474462Salfred
327575801Siedowse/*
327675801Siedowse * Return a pointer to the part of the sockaddr that contains the
327775801Siedowse * raw address, and set *nbytes to its length in bytes. Returns
327875801Siedowse * NULL if the address family is unknown.
327975801Siedowse */
3280285128Straszstatic void *
328175801Siedowsesa_rawaddr(struct sockaddr *sa, int *nbytes) {
328275801Siedowse	void *p;
328374462Salfred	int len;
328474462Salfred
328575801Siedowse	switch (sa->sa_family) {
328674462Salfred	case AF_INET:
328775801Siedowse		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
328875801Siedowse		p = &((struct sockaddr_in *)sa)->sin_addr;
328974462Salfred		break;
329074462Salfred	case AF_INET6:
329175801Siedowse		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
329275801Siedowse		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
329374462Salfred		break;
329474462Salfred	default:
329575801Siedowse		p = NULL;
329675801Siedowse		len = 0;
329774462Salfred	}
329874462Salfred
329975801Siedowse	if (nbytes != NULL)
330075801Siedowse		*nbytes = len;
330175801Siedowse	return (p);
330274462Salfred}
330374462Salfred
3304285128Straszstatic void
3305216587Scharnierhuphandler(int sig __unused)
330675754Siedowse{
3307285128Strasz
330875754Siedowse	got_sighup = 1;
330975754Siedowse}
331075754Siedowse
3311285128Straszstatic void
3312285128Straszterminate(int sig __unused)
331374462Salfred{
3314149433Spjd	pidfile_remove(pfh);
3315194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3316194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
331774462Salfred	exit (0);
331874462Salfred}
3319