mountd.c revision 298912
11558Srgrimes/*
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: head/usr.sbin/mountd/mountd.c 298912 2016-05-02 01:49:42Z araujo $");
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>
531558Srgrimes#include <sys/stat.h>
54192934Srmacklem#include <sys/sysctl.h>
551558Srgrimes#include <sys/syslog.h>
561558Srgrimes
571558Srgrimes#include <rpc/rpc.h>
58109363Smbr#include <rpc/rpc_com.h>
591558Srgrimes#include <rpc/pmap_clnt.h>
6074462Salfred#include <rpc/pmap_prot.h>
6174462Salfred#include <rpcsvc/mount.h>
629336Sdfr#include <nfs/nfsproto.h>
63192934Srmacklem#include <nfs/nfssvc.h>
6483653Speter#include <nfsserver/nfs.h>
651558Srgrimes
66192934Srmacklem#include <fs/nfs/nfsport.h>
67192934Srmacklem
681558Srgrimes#include <arpa/inet.h>
691558Srgrimes
701558Srgrimes#include <ctype.h>
7137663Scharnier#include <err.h>
721558Srgrimes#include <errno.h>
731558Srgrimes#include <grp.h>
74149433Spjd#include <libutil.h>
75103949Smike#include <limits.h>
761558Srgrimes#include <netdb.h>
771558Srgrimes#include <pwd.h>
781558Srgrimes#include <signal.h>
791558Srgrimes#include <stdio.h>
801558Srgrimes#include <stdlib.h>
811558Srgrimes#include <string.h>
821558Srgrimes#include <unistd.h>
831558Srgrimes#include "pathnames.h"
84158857Srodrigc#include "mntopts.h"
851558Srgrimes
861558Srgrimes#ifdef DEBUG
871558Srgrimes#include <stdarg.h>
881558Srgrimes#endif
891558Srgrimes
901558Srgrimes/*
911558Srgrimes * Structures for keeping the mount list and export list
921558Srgrimes */
931558Srgrimesstruct mountlist {
941558Srgrimes	struct mountlist *ml_next;
95194880Sdfr	char	ml_host[MNTNAMLEN+1];
96194880Sdfr	char	ml_dirp[MNTPATHLEN+1];
971558Srgrimes};
981558Srgrimes
991558Srgrimesstruct dirlist {
1001558Srgrimes	struct dirlist	*dp_left;
1011558Srgrimes	struct dirlist	*dp_right;
1021558Srgrimes	int		dp_flag;
1031558Srgrimes	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
1041558Srgrimes	char		dp_dirp[1];	/* Actually malloc'd to size of dir */
1051558Srgrimes};
1061558Srgrimes/* dp_flag bits */
1071558Srgrimes#define	DP_DEFSET	0x1
1089336Sdfr#define DP_HOSTSET	0x2
1091558Srgrimes
1101558Srgrimesstruct exportlist {
1111558Srgrimes	struct exportlist *ex_next;
1121558Srgrimes	struct dirlist	*ex_dirl;
1131558Srgrimes	struct dirlist	*ex_defdir;
1141558Srgrimes	int		ex_flag;
1151558Srgrimes	fsid_t		ex_fs;
1161558Srgrimes	char		*ex_fsdir;
11727447Sdfr	char		*ex_indexfile;
118184588Sdfr	int		ex_numsecflavors;
119184588Sdfr	int		ex_secflavors[MAXSECFLAVORS];
120240902Srmacklem	int		ex_defnumsecflavors;
121240902Srmacklem	int		ex_defsecflavors[MAXSECFLAVORS];
1221558Srgrimes};
1231558Srgrimes/* ex_flag bits */
1241558Srgrimes#define	EX_LINKED	0x1
1251558Srgrimes
1261558Srgrimesstruct netmsk {
12774462Salfred	struct sockaddr_storage nt_net;
12875801Siedowse	struct sockaddr_storage nt_mask;
12942144Sdfr	char		*nt_name;
1301558Srgrimes};
1311558Srgrimes
1321558Srgrimesunion grouptypes {
13374462Salfred	struct addrinfo *gt_addrinfo;
1341558Srgrimes	struct netmsk	gt_net;
1351558Srgrimes};
1361558Srgrimes
1371558Srgrimesstruct grouplist {
1381558Srgrimes	int gr_type;
1391558Srgrimes	union grouptypes gr_ptr;
1401558Srgrimes	struct grouplist *gr_next;
141240902Srmacklem	int gr_numsecflavors;
142240902Srmacklem	int gr_secflavors[MAXSECFLAVORS];
1431558Srgrimes};
1441558Srgrimes/* Group types */
1451558Srgrimes#define	GT_NULL		0x0
1461558Srgrimes#define	GT_HOST		0x1
1471558Srgrimes#define	GT_NET		0x2
14875641Siedowse#define	GT_DEFAULT	0x3
1497401Swpaul#define GT_IGNORE	0x5
1501558Srgrimes
1511558Srgrimesstruct hostlist {
1529336Sdfr	int		 ht_flag;	/* Uses DP_xx bits */
1531558Srgrimes	struct grouplist *ht_grp;
1541558Srgrimes	struct hostlist	 *ht_next;
1551558Srgrimes};
1561558Srgrimes
1579336Sdfrstruct fhreturn {
1589336Sdfr	int	fhr_flag;
1599336Sdfr	int	fhr_vers;
1609336Sdfr	nfsfh_t	fhr_fh;
161184588Sdfr	int	fhr_numsecflavors;
162184588Sdfr	int	*fhr_secflavors;
1639336Sdfr};
1649336Sdfr
165222623Srmacklem#define	GETPORT_MAXTRY	20	/* Max tries to get a port # */
166222623Srmacklem
1671558Srgrimes/* Global defs */
168285128Straszstatic char	*add_expdir(struct dirlist **, char *, int);
169285128Straszstatic void	add_dlist(struct dirlist **, struct dirlist *,
170285128Strasz		    struct grouplist *, int, struct exportlist *);
171285128Straszstatic void	add_mlist(char *, char *);
172285128Straszstatic int	check_dirpath(char *);
173285128Straszstatic int	check_options(struct dirlist *);
174285128Straszstatic int	checkmask(struct sockaddr *sa);
175285128Straszstatic int	chk_host(struct dirlist *, struct sockaddr *, int *, int *,
176285128Strasz		    int *, int **);
177293305Sjpaetzelstatic char	*strsep_quote(char **stringp, const char *delim);
178222623Srmacklemstatic int	create_service(struct netconfig *nconf);
179222623Srmacklemstatic void	complete_service(struct netconfig *nconf, char *port_str);
180222623Srmacklemstatic void	clearout_service(void);
181285128Straszstatic void	del_mlist(char *hostp, char *dirp);
182285128Straszstatic struct dirlist	*dirp_search(struct dirlist *, char *);
183285128Straszstatic int	do_mount(struct exportlist *, struct grouplist *, int,
184285128Strasz		    struct xucred *, char *, int, struct statfs *);
185285128Straszstatic int	do_opt(char **, char **, struct exportlist *,
186285128Strasz		    struct grouplist *, int *, int *, struct xucred *);
187285128Straszstatic struct exportlist	*ex_search(fsid_t *);
188285128Straszstatic struct exportlist	*get_exp(void);
189285128Straszstatic void	free_dir(struct dirlist *);
190285128Straszstatic void	free_exp(struct exportlist *);
191285128Straszstatic void	free_grp(struct grouplist *);
192285128Straszstatic void	free_host(struct hostlist *);
193285128Straszstatic void	get_exportlist(void);
194285128Straszstatic int	get_host(char *, struct grouplist *, struct grouplist *);
195285128Straszstatic struct hostlist *get_ht(void);
196285128Straszstatic int	get_line(void);
197285128Straszstatic void	get_mountlist(void);
198285128Straszstatic int	get_net(char *, struct netmsk *, int);
199285128Straszstatic void	getexp_err(struct exportlist *, struct grouplist *);
200285128Straszstatic struct grouplist	*get_grp(void);
201285128Straszstatic void	hang_dirp(struct dirlist *, struct grouplist *,
20292882Simp				struct exportlist *, int);
203285128Straszstatic void	huphandler(int sig);
204285128Straszstatic int	makemask(struct sockaddr_storage *ssp, int bitlen);
205285128Straszstatic void	mntsrv(struct svc_req *, SVCXPRT *);
206285128Straszstatic void	nextfield(char **, char **);
207285128Straszstatic void	out_of_mem(void);
208285128Straszstatic void	parsecred(char *, struct xucred *);
209285128Straszstatic int	parsesec(char *, struct exportlist *);
210285128Straszstatic int	put_exlist(struct dirlist *, XDR *, struct dirlist *,
211285128Strasz		    int *, int);
212285128Straszstatic void	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
213285128Straszstatic int	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
214285128Strasz		    struct sockaddr *samask);
215285128Straszstatic int	scan_tree(struct dirlist *, struct sockaddr *);
216285128Straszstatic void	usage(void);
217285128Straszstatic int	xdr_dir(XDR *, char *);
218285128Straszstatic int	xdr_explist(XDR *, caddr_t);
219285128Straszstatic int	xdr_explist_brief(XDR *, caddr_t);
220285128Straszstatic int	xdr_explist_common(XDR *, caddr_t, int);
221285128Straszstatic int	xdr_fhs(XDR *, caddr_t);
222285128Straszstatic int	xdr_mlist(XDR *, caddr_t);
223285128Straszstatic void	terminate(int);
2241558Srgrimes
225285128Straszstatic struct exportlist *exphead;
226285128Straszstatic struct mountlist *mlhead;
227285128Straszstatic struct grouplist *grphead;
228285128Straszstatic char *exnames_default[2] = { _PATH_EXPORTS, NULL };
229285128Straszstatic char **exnames;
230285128Straszstatic char **hosts = NULL;
231285128Straszstatic struct xucred def_anon = {
23291354Sdd	XUCRED_VERSION,
23372650Sgreen	(uid_t)-2,
2341558Srgrimes	1,
23572650Sgreen	{ (gid_t)-2 },
23672650Sgreen	NULL
2371558Srgrimes};
238285128Straszstatic int force_v2 = 0;
239285128Straszstatic int resvport_only = 1;
240285128Straszstatic int nhosts = 0;
241285128Straszstatic int dir_only = 1;
242285128Straszstatic int dolog = 0;
243285128Straszstatic int got_sighup = 0;
244285128Straszstatic int xcreated = 0;
24574462Salfred
246285128Straszstatic char *svcport_str = NULL;
247285128Straszstatic int mallocd_svcport = 0;
248285128Straszstatic int *sock_fd;
249285128Straszstatic int sock_fdcnt;
250285128Straszstatic int sock_fdpos;
251285128Straszstatic int suspend_nfsd = 0;
252172827Smatteo
253285128Straszstatic int opt_flags;
25474462Salfredstatic int have_v6 = 1;
25574462Salfred
256285128Straszstatic int v4root_phase = 0;
257285128Straszstatic char v4root_dirpath[PATH_MAX + 1];
258285128Straszstatic int has_publicfh = 0;
259192934Srmacklem
260285128Straszstatic struct pidfh *pfh = NULL;
26175801Siedowse/* Bits for opt_flags above */
2621558Srgrimes#define	OP_MAPROOT	0x01
2631558Srgrimes#define	OP_MAPALL	0x02
26483653Speter/* 0x4 free */
2651558Srgrimes#define	OP_MASK		0x08
2661558Srgrimes#define	OP_NET		0x10
2671558Srgrimes#define	OP_ALLDIRS	0x40
26875801Siedowse#define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
269100336Sjoerg#define	OP_QUIET	0x100
27074462Salfred#define OP_MASKLEN	0x200
271184588Sdfr#define OP_SEC		0x400
2721558Srgrimes
2731558Srgrimes#ifdef DEBUG
274285128Straszstatic int debug = 1;
275285128Straszstatic void	SYSLOG(int, const char *, ...) __printflike(2, 3);
2761558Srgrimes#define syslog SYSLOG
2771558Srgrimes#else
278285128Straszstatic int debug = 0;
2791558Srgrimes#endif
2801558Srgrimes
2811558Srgrimes/*
282293305Sjpaetzel * Similar to strsep(), but it allows for quoted strings
283293305Sjpaetzel * and escaped characters.
284293305Sjpaetzel *
285293305Sjpaetzel * It returns the string (or NULL, if *stringp is NULL),
286293305Sjpaetzel * which is a de-quoted version of the string if necessary.
287293305Sjpaetzel *
288293305Sjpaetzel * It modifies *stringp in place.
289293305Sjpaetzel */
290293305Sjpaetzelstatic char *
291293305Sjpaetzelstrsep_quote(char **stringp, const char *delim)
292293305Sjpaetzel{
293293305Sjpaetzel	char *srcptr, *dstptr, *retval;
294293305Sjpaetzel	char quot = 0;
295293305Sjpaetzel
296293305Sjpaetzel	if (stringp == NULL || *stringp == NULL)
297293305Sjpaetzel		return (NULL);
298293305Sjpaetzel
299293305Sjpaetzel	srcptr = dstptr = retval = *stringp;
300293305Sjpaetzel
301293305Sjpaetzel	while (*srcptr) {
302293305Sjpaetzel		/*
303293305Sjpaetzel		 * We're looking for several edge cases here.
304293305Sjpaetzel		 * First:  if we're in quote state (quot != 0),
305293305Sjpaetzel		 * then we ignore the delim characters, but otherwise
306293305Sjpaetzel		 * process as normal, unless it is the quote character.
307293305Sjpaetzel		 * Second:  if the current character is a backslash,
308293305Sjpaetzel		 * we take the next character as-is, without checking
309293305Sjpaetzel		 * for delim, quote, or backslash.  Exception:  if the
310293305Sjpaetzel		 * next character is a NUL, that's the end of the string.
311293305Sjpaetzel		 * Third:  if the character is a quote character, we toggle
312293305Sjpaetzel		 * quote state.
313293305Sjpaetzel		 * Otherwise:  check the current character for NUL, or
314293305Sjpaetzel		 * being in delim, and end the string if either is true.
315293305Sjpaetzel		 */
316293305Sjpaetzel		if (*srcptr == '\\') {
317293305Sjpaetzel			srcptr++;
318293305Sjpaetzel			/*
319293305Sjpaetzel			 * The edge case here is if the next character
320293305Sjpaetzel			 * is NUL, we want to stop processing.  But if
321293305Sjpaetzel			 * it's not NUL, then we simply want to copy it.
322293305Sjpaetzel			 */
323293305Sjpaetzel			if (*srcptr) {
324293305Sjpaetzel				*dstptr++ = *srcptr++;
325293305Sjpaetzel			}
326293305Sjpaetzel			continue;
327293305Sjpaetzel		}
328293305Sjpaetzel		if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) {
329293305Sjpaetzel			quot = *srcptr++;
330293305Sjpaetzel			continue;
331293305Sjpaetzel		}
332293305Sjpaetzel		if (quot && *srcptr == quot) {
333293305Sjpaetzel			/* End of the quoted part */
334293305Sjpaetzel			quot = 0;
335293305Sjpaetzel			srcptr++;
336293305Sjpaetzel			continue;
337293305Sjpaetzel		}
338293305Sjpaetzel		if (!quot && strchr(delim, *srcptr))
339293305Sjpaetzel			break;
340293305Sjpaetzel		*dstptr++ = *srcptr++;
341293305Sjpaetzel	}
342293305Sjpaetzel
343293305Sjpaetzel	*dstptr = 0; /* Terminate the string */
344293305Sjpaetzel	*stringp = (*srcptr == '\0') ? NULL : srcptr + 1;
345293305Sjpaetzel	return (retval);
346293305Sjpaetzel}
347293305Sjpaetzel
348293305Sjpaetzel/*
3491558Srgrimes * Mountd server for NFS mount protocol as described in:
3501558Srgrimes * NFS: Network File System Protocol Specification, RFC1094, Appendix A
3511558Srgrimes * The optional arguments are the exports file name
3521558Srgrimes * default: _PATH_EXPORTS
3531558Srgrimes * and "-n" to allow nonroot mount.
3541558Srgrimes */
3551558Srgrimesint
356216587Scharniermain(int argc, char **argv)
3571558Srgrimes{
35875754Siedowse	fd_set readfds;
359172827Smatteo	struct netconfig *nconf;
360172827Smatteo	char *endptr, **hosts_bak;
361172827Smatteo	void *nc_handle;
362149433Spjd	pid_t otherpid;
363172827Smatteo	in_port_t svcport;
364172827Smatteo	int c, k, s;
365109363Smbr	int maxrec = RPC_MAXDATASIZE;
366222623Srmacklem	int attempt_cnt, port_len, port_pos, ret;
367222623Srmacklem	char **port_list;
3681558Srgrimes
36974462Salfred	/* Check that another mountd isn't already running. */
370150214Spjd	pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
371149433Spjd	if (pfh == NULL) {
372149433Spjd		if (errno == EEXIST)
373149433Spjd			errx(1, "mountd already running, pid: %d.", otherpid);
374149433Spjd		warn("cannot open or create pidfile");
375149433Spjd	}
37674462Salfred
37774462Salfred	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
37874462Salfred	if (s < 0)
37974462Salfred		have_v6 = 0;
38074462Salfred	else
38174462Salfred		close(s);
3822999Swollman
383282214Strasz	while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1)
3841558Srgrimes		switch (c) {
38525087Sdfr		case '2':
38625087Sdfr			force_v2 = 1;
38725087Sdfr			break;
388192993Srmacklem		case 'e':
389220980Srmacklem			/* now a no-op, since this is the default */
390192934Srmacklem			break;
3919336Sdfr		case 'n':
3929336Sdfr			resvport_only = 0;
3939336Sdfr			break;
3949336Sdfr		case 'r':
3959336Sdfr			dir_only = 0;
3969336Sdfr			break;
3978688Sphk		case 'd':
3988688Sphk			debug = debug ? 0 : 1;
3998688Sphk			break;
40031656Sguido		case 'l':
401121767Speter			dolog = 1;
40231656Sguido			break;
403126572Sbms		case 'p':
404126572Sbms			endptr = NULL;
405126572Sbms			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
406126572Sbms			if (endptr == NULL || *endptr != '\0' ||
407126572Sbms			    svcport == 0 || svcport >= IPPORT_MAX)
408126572Sbms				usage();
409172827Smatteo			svcport_str = strdup(optarg);
410126572Sbms			break;
411172827Smatteo		case 'h':
412172827Smatteo			++nhosts;
413172827Smatteo			hosts_bak = hosts;
414172827Smatteo			hosts_bak = realloc(hosts, nhosts * sizeof(char *));
415172827Smatteo			if (hosts_bak == NULL) {
416172827Smatteo				if (hosts != NULL) {
417172827Smatteo					for (k = 0; k < nhosts; k++)
418172827Smatteo						free(hosts[k]);
419172827Smatteo					free(hosts);
420172827Smatteo					out_of_mem();
421172827Smatteo				}
422172827Smatteo			}
423172827Smatteo			hosts = hosts_bak;
424172827Smatteo			hosts[nhosts - 1] = strdup(optarg);
425172827Smatteo			if (hosts[nhosts - 1] == NULL) {
426172827Smatteo				for (k = 0; k < (nhosts - 1); k++)
427172827Smatteo					free(hosts[k]);
428172827Smatteo				free(hosts);
429172827Smatteo				out_of_mem();
430172827Smatteo			}
431172827Smatteo			break;
432241568Srmacklem		case 'S':
433241568Srmacklem			suspend_nfsd = 1;
434241568Srmacklem			break;
4351558Srgrimes		default:
43637663Scharnier			usage();
437298089Spfg		}
438192934Srmacklem
439282214Strasz	if (modfind("nfsd") < 0) {
440192934Srmacklem		/* Not present in kernel, try loading it */
441282214Strasz		if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
442192934Srmacklem			errx(1, "NFS server is not available");
443192934Srmacklem	}
444192934Srmacklem
4451558Srgrimes	argc -= optind;
4461558Srgrimes	argv += optind;
4471558Srgrimes	grphead = (struct grouplist *)NULL;
4481558Srgrimes	exphead = (struct exportlist *)NULL;
4491558Srgrimes	mlhead = (struct mountlist *)NULL;
450166440Spjd	if (argc > 0)
451166440Spjd		exnames = argv;
452166440Spjd	else
453166440Spjd		exnames = exnames_default;
4541558Srgrimes	openlog("mountd", LOG_PID, LOG_DAEMON);
4551558Srgrimes	if (debug)
45637663Scharnier		warnx("getting export list");
4571558Srgrimes	get_exportlist();
4581558Srgrimes	if (debug)
45937663Scharnier		warnx("getting mount list");
4601558Srgrimes	get_mountlist();
4611558Srgrimes	if (debug)
46237663Scharnier		warnx("here we go");
4631558Srgrimes	if (debug == 0) {
4641558Srgrimes		daemon(0, 0);
4651558Srgrimes		signal(SIGINT, SIG_IGN);
4661558Srgrimes		signal(SIGQUIT, SIG_IGN);
4671558Srgrimes	}
46875754Siedowse	signal(SIGHUP, huphandler);
46974462Salfred	signal(SIGTERM, terminate);
470164394Srodrigc	signal(SIGPIPE, SIG_IGN);
471149433Spjd
472149433Spjd	pidfile_write(pfh);
473149433Spjd
474194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
475194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
476109363Smbr	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
477109363Smbr
47824759Sguido	if (!resvport_only) {
47983687Speter		if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL,
48083687Speter		    &resvport_only, sizeof(resvport_only)) != 0 &&
48183687Speter		    errno != ENOENT) {
48224759Sguido			syslog(LOG_ERR, "sysctl: %m");
48324759Sguido			exit(1);
48424759Sguido		}
48524330Sguido	}
486126572Sbms
487172827Smatteo	/*
488172827Smatteo	 * If no hosts were specified, add a wildcard entry to bind to
489172827Smatteo	 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
490172827Smatteo	 * list.
491172827Smatteo	 */
492172827Smatteo	if (nhosts == 0) {
493292864Suqs		hosts = malloc(sizeof(char *));
494172827Smatteo		if (hosts == NULL)
495172827Smatteo			out_of_mem();
496172827Smatteo		hosts[0] = "*";
497172827Smatteo		nhosts = 1;
498172827Smatteo	} else {
499172827Smatteo		hosts_bak = hosts;
500172827Smatteo		if (have_v6) {
501172827Smatteo			hosts_bak = realloc(hosts, (nhosts + 2) *
502172827Smatteo			    sizeof(char *));
503172827Smatteo			if (hosts_bak == NULL) {
504172827Smatteo				for (k = 0; k < nhosts; k++)
505172827Smatteo					free(hosts[k]);
506172827Smatteo		    		free(hosts);
507172827Smatteo		    		out_of_mem();
508172827Smatteo			} else
509172827Smatteo				hosts = hosts_bak;
510172827Smatteo			nhosts += 2;
511172827Smatteo			hosts[nhosts - 2] = "::1";
512172827Smatteo		} else {
513172827Smatteo			hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
514172827Smatteo			if (hosts_bak == NULL) {
515172827Smatteo				for (k = 0; k < nhosts; k++)
516172827Smatteo					free(hosts[k]);
517172827Smatteo				free(hosts);
518172827Smatteo				out_of_mem();
519172827Smatteo			} else {
520172827Smatteo				nhosts += 1;
521172827Smatteo				hosts = hosts_bak;
522126572Sbms			}
523172827Smatteo		}
52474462Salfred
525172827Smatteo		hosts[nhosts - 1] = "127.0.0.1";
52674462Salfred	}
52774462Salfred
528222623Srmacklem	attempt_cnt = 1;
529222623Srmacklem	sock_fdcnt = 0;
530222623Srmacklem	sock_fd = NULL;
531222623Srmacklem	port_list = NULL;
532222623Srmacklem	port_len = 0;
533172827Smatteo	nc_handle = setnetconfig();
534172827Smatteo	while ((nconf = getnetconfig(nc_handle))) {
535172827Smatteo		if (nconf->nc_flag & NC_VISIBLE) {
536172827Smatteo			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
537172827Smatteo			    "inet6") == 0) {
538172827Smatteo				/* DO NOTHING */
539222623Srmacklem			} else {
540222623Srmacklem				ret = create_service(nconf);
541222623Srmacklem				if (ret == 1)
542222623Srmacklem					/* Ignore this call */
543222623Srmacklem					continue;
544222623Srmacklem				if (ret < 0) {
545222623Srmacklem					/*
546222623Srmacklem					 * Failed to bind port, so close off
547222623Srmacklem					 * all sockets created and try again
548222623Srmacklem					 * if the port# was dynamically
549222623Srmacklem					 * assigned via bind(2).
550222623Srmacklem					 */
551222623Srmacklem					clearout_service();
552222623Srmacklem					if (mallocd_svcport != 0 &&
553222623Srmacklem					    attempt_cnt < GETPORT_MAXTRY) {
554222623Srmacklem						free(svcport_str);
555222623Srmacklem						svcport_str = NULL;
556222623Srmacklem						mallocd_svcport = 0;
557222623Srmacklem					} else {
558222623Srmacklem						errno = EADDRINUSE;
559222623Srmacklem						syslog(LOG_ERR,
560222623Srmacklem						    "bindresvport_sa: %m");
561222623Srmacklem						exit(1);
562222623Srmacklem					}
563222623Srmacklem
564222623Srmacklem					/* Start over at the first service. */
565222623Srmacklem					free(sock_fd);
566222623Srmacklem					sock_fdcnt = 0;
567222623Srmacklem					sock_fd = NULL;
568222623Srmacklem					nc_handle = setnetconfig();
569222623Srmacklem					attempt_cnt++;
570222623Srmacklem				} else if (mallocd_svcport != 0 &&
571222623Srmacklem				    attempt_cnt == GETPORT_MAXTRY) {
572222623Srmacklem					/*
573222623Srmacklem					 * For the last attempt, allow
574222623Srmacklem					 * different port #s for each nconf
575222623Srmacklem					 * by saving the svcport_str and
576222623Srmacklem					 * setting it back to NULL.
577222623Srmacklem					 */
578222623Srmacklem					port_list = realloc(port_list,
579222623Srmacklem					    (port_len + 1) * sizeof(char *));
580222623Srmacklem					if (port_list == NULL)
581222623Srmacklem						out_of_mem();
582222623Srmacklem					port_list[port_len++] = svcport_str;
583222623Srmacklem					svcport_str = NULL;
584222623Srmacklem					mallocd_svcport = 0;
585222623Srmacklem				}
586222623Srmacklem			}
587222623Srmacklem		}
588222623Srmacklem	}
589222623Srmacklem
590222623Srmacklem	/*
591222623Srmacklem	 * Successfully bound the ports, so call complete_service() to
592222623Srmacklem	 * do the rest of the setup on the service(s).
593222623Srmacklem	 */
594222623Srmacklem	sock_fdpos = 0;
595222623Srmacklem	port_pos = 0;
596222623Srmacklem	nc_handle = setnetconfig();
597222623Srmacklem	while ((nconf = getnetconfig(nc_handle))) {
598222623Srmacklem		if (nconf->nc_flag & NC_VISIBLE) {
599222623Srmacklem			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
600222623Srmacklem			    "inet6") == 0) {
601222623Srmacklem				/* DO NOTHING */
602222623Srmacklem			} else if (port_list != NULL) {
603222623Srmacklem				if (port_pos >= port_len) {
604222623Srmacklem					syslog(LOG_ERR, "too many port#s");
605222623Srmacklem					exit(1);
606222623Srmacklem				}
607222623Srmacklem				complete_service(nconf, port_list[port_pos++]);
608172827Smatteo			} else
609222623Srmacklem				complete_service(nconf, svcport_str);
610172827Smatteo		}
61174462Salfred	}
612172827Smatteo	endnetconfig(nc_handle);
613222623Srmacklem	free(sock_fd);
614222623Srmacklem	if (port_list != NULL) {
615222623Srmacklem		for (port_pos = 0; port_pos < port_len; port_pos++)
616222623Srmacklem			free(port_list[port_pos]);
617222623Srmacklem		free(port_list);
618222623Srmacklem	}
61974462Salfred
62074462Salfred	if (xcreated == 0) {
62174462Salfred		syslog(LOG_ERR, "could not create any services");
6221558Srgrimes		exit(1);
6231558Srgrimes	}
62475754Siedowse
62575754Siedowse	/* Expand svc_run() here so that we can call get_exportlist(). */
62675754Siedowse	for (;;) {
62775754Siedowse		if (got_sighup) {
62875754Siedowse			get_exportlist();
62975754Siedowse			got_sighup = 0;
63075754Siedowse		}
63175754Siedowse		readfds = svc_fdset;
63275754Siedowse		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
63375754Siedowse		case -1:
63475754Siedowse			if (errno == EINTR)
63575754Siedowse                                continue;
63675754Siedowse			syslog(LOG_ERR, "mountd died: select: %m");
63775754Siedowse			exit(1);
63875754Siedowse		case 0:
63975754Siedowse			continue;
64075754Siedowse		default:
64175754Siedowse			svc_getreqset(&readfds);
64275754Siedowse		}
64375754Siedowse	}
644172827Smatteo}
645172827Smatteo
646172827Smatteo/*
647172827Smatteo * This routine creates and binds sockets on the appropriate
648222623Srmacklem * addresses. It gets called one time for each transport.
649222623Srmacklem * It returns 0 upon success, 1 for ingore the call and -1 to indicate
650222623Srmacklem * bind failed with EADDRINUSE.
651222623Srmacklem * Any file descriptors that have been created are stored in sock_fd and
652222623Srmacklem * the total count of them is maintained in sock_fdcnt.
653172827Smatteo */
654222623Srmacklemstatic int
655172827Smatteocreate_service(struct netconfig *nconf)
656172827Smatteo{
657172827Smatteo	struct addrinfo hints, *res = NULL;
658172827Smatteo	struct sockaddr_in *sin;
659172827Smatteo	struct sockaddr_in6 *sin6;
660172827Smatteo	struct __rpc_sockinfo si;
661172827Smatteo	int aicode;
662172827Smatteo	int fd;
663172827Smatteo	int nhostsbak;
664172827Smatteo	int one = 1;
665172827Smatteo	int r;
666172827Smatteo	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
667222623Srmacklem	int mallocd_res;
668172827Smatteo
669172827Smatteo	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
670172827Smatteo	    (nconf->nc_semantics != NC_TPI_COTS) &&
671172827Smatteo	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
672222623Srmacklem		return (1);	/* not my type */
673172827Smatteo
674172827Smatteo	/*
675172827Smatteo	 * XXX - using RPC library internal functions.
676172827Smatteo	 */
677172827Smatteo	if (!__rpc_nconf2sockinfo(nconf, &si)) {
678172827Smatteo		syslog(LOG_ERR, "cannot get information for %s",
679172827Smatteo		    nconf->nc_netid);
680222623Srmacklem		return (1);
681172827Smatteo	}
682172827Smatteo
683172827Smatteo	/* Get mountd's address on this transport */
684172827Smatteo	memset(&hints, 0, sizeof hints);
685172827Smatteo	hints.ai_family = si.si_af;
686172827Smatteo	hints.ai_socktype = si.si_socktype;
687172827Smatteo	hints.ai_protocol = si.si_proto;
688172827Smatteo
689172827Smatteo	/*
690172827Smatteo	 * Bind to specific IPs if asked to
691172827Smatteo	 */
692172827Smatteo	nhostsbak = nhosts;
693172827Smatteo	while (nhostsbak > 0) {
694172827Smatteo		--nhostsbak;
695222623Srmacklem		sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
696222623Srmacklem		if (sock_fd == NULL)
697222623Srmacklem			out_of_mem();
698222623Srmacklem		sock_fd[sock_fdcnt++] = -1;	/* Set invalid for now. */
699222623Srmacklem		mallocd_res = 0;
700222623Srmacklem
701277352Srstone		hints.ai_flags = AI_PASSIVE;
702277352Srstone
703172827Smatteo		/*
704172827Smatteo		 * XXX - using RPC library internal functions.
705172827Smatteo		 */
706172827Smatteo		if ((fd = __rpc_nconf2fd(nconf)) < 0) {
707172827Smatteo			int non_fatal = 0;
708244538Skevlo	    		if (errno == EAFNOSUPPORT &&
709172827Smatteo			    nconf->nc_semantics != NC_TPI_CLTS)
710172827Smatteo				non_fatal = 1;
711172827Smatteo
712172827Smatteo			syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
713172827Smatteo			    "cannot create socket for %s", nconf->nc_netid);
714222623Srmacklem			if (non_fatal != 0)
715222623Srmacklem				continue;
716222623Srmacklem			exit(1);
717172827Smatteo		}
718172827Smatteo
719172827Smatteo		switch (hints.ai_family) {
720172827Smatteo		case AF_INET:
721172827Smatteo			if (inet_pton(AF_INET, hosts[nhostsbak],
722172827Smatteo			    host_addr) == 1) {
723222623Srmacklem				hints.ai_flags |= AI_NUMERICHOST;
724172827Smatteo			} else {
725172827Smatteo				/*
726172827Smatteo				 * Skip if we have an AF_INET6 address.
727172827Smatteo				 */
728172827Smatteo				if (inet_pton(AF_INET6, hosts[nhostsbak],
729172827Smatteo				    host_addr) == 1) {
730172827Smatteo					close(fd);
731172827Smatteo					continue;
732172827Smatteo				}
733172827Smatteo			}
734172827Smatteo			break;
735172827Smatteo		case AF_INET6:
736172827Smatteo			if (inet_pton(AF_INET6, hosts[nhostsbak],
737172827Smatteo			    host_addr) == 1) {
738222623Srmacklem				hints.ai_flags |= AI_NUMERICHOST;
739172827Smatteo			} else {
740172827Smatteo				/*
741172827Smatteo				 * Skip if we have an AF_INET address.
742172827Smatteo				 */
743172827Smatteo				if (inet_pton(AF_INET, hosts[nhostsbak],
744172827Smatteo				    host_addr) == 1) {
745172827Smatteo					close(fd);
746172827Smatteo					continue;
747172827Smatteo				}
748172827Smatteo			}
749172827Smatteo
750172827Smatteo			/*
751172827Smatteo			 * We're doing host-based access checks here, so don't
752172827Smatteo			 * allow v4-in-v6 to confuse things. The kernel will
753172827Smatteo			 * disable it by default on NFS sockets too.
754172827Smatteo			 */
755172827Smatteo			if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
756172827Smatteo			    sizeof one) < 0) {
757172827Smatteo				syslog(LOG_ERR,
758172827Smatteo				    "can't disable v4-in-v6 on IPv6 socket");
759172827Smatteo				exit(1);
760172827Smatteo			}
761172827Smatteo			break;
762172827Smatteo		default:
763172827Smatteo			break;
764172827Smatteo		}
765172827Smatteo
766172827Smatteo		/*
767172827Smatteo		 * If no hosts were specified, just bind to INADDR_ANY
768172827Smatteo		 */
769172827Smatteo		if (strcmp("*", hosts[nhostsbak]) == 0) {
770172827Smatteo			if (svcport_str == NULL) {
771172827Smatteo				res = malloc(sizeof(struct addrinfo));
772172827Smatteo				if (res == NULL)
773172827Smatteo					out_of_mem();
774222623Srmacklem				mallocd_res = 1;
775172827Smatteo				res->ai_flags = hints.ai_flags;
776172827Smatteo				res->ai_family = hints.ai_family;
777172827Smatteo				res->ai_protocol = hints.ai_protocol;
778172827Smatteo				switch (res->ai_family) {
779172827Smatteo				case AF_INET:
780172827Smatteo					sin = malloc(sizeof(struct sockaddr_in));
781172827Smatteo					if (sin == NULL)
782172827Smatteo						out_of_mem();
783172827Smatteo					sin->sin_family = AF_INET;
784172827Smatteo					sin->sin_port = htons(0);
785172827Smatteo					sin->sin_addr.s_addr = htonl(INADDR_ANY);
786172827Smatteo					res->ai_addr = (struct sockaddr*) sin;
787172827Smatteo					res->ai_addrlen = (socklen_t)
788222623Srmacklem					    sizeof(struct sockaddr_in);
789172827Smatteo					break;
790172827Smatteo				case AF_INET6:
791172827Smatteo					sin6 = malloc(sizeof(struct sockaddr_in6));
792173056Ssimon					if (sin6 == NULL)
793172827Smatteo						out_of_mem();
794172827Smatteo					sin6->sin6_family = AF_INET6;
795172827Smatteo					sin6->sin6_port = htons(0);
796172827Smatteo					sin6->sin6_addr = in6addr_any;
797172827Smatteo					res->ai_addr = (struct sockaddr*) sin6;
798172827Smatteo					res->ai_addrlen = (socklen_t)
799222623Srmacklem					    sizeof(struct sockaddr_in6);
800222623Srmacklem					break;
801172827Smatteo				default:
802222623Srmacklem					syslog(LOG_ERR, "bad addr fam %d",
803222623Srmacklem					    res->ai_family);
804222623Srmacklem					exit(1);
805172827Smatteo				}
806172827Smatteo			} else {
807172827Smatteo				if ((aicode = getaddrinfo(NULL, svcport_str,
808172827Smatteo				    &hints, &res)) != 0) {
809172827Smatteo					syslog(LOG_ERR,
810172827Smatteo					    "cannot get local address for %s: %s",
811172827Smatteo					    nconf->nc_netid,
812172827Smatteo					    gai_strerror(aicode));
813222623Srmacklem					close(fd);
814172827Smatteo					continue;
815172827Smatteo				}
816172827Smatteo			}
817172827Smatteo		} else {
818172827Smatteo			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
819172827Smatteo			    &hints, &res)) != 0) {
820172827Smatteo				syslog(LOG_ERR,
821172827Smatteo				    "cannot get local address for %s: %s",
822172827Smatteo				    nconf->nc_netid, gai_strerror(aicode));
823222623Srmacklem				close(fd);
824172827Smatteo				continue;
825172827Smatteo			}
826172827Smatteo		}
827172827Smatteo
828222623Srmacklem		/* Store the fd. */
829222623Srmacklem		sock_fd[sock_fdcnt - 1] = fd;
830222623Srmacklem
831222623Srmacklem		/* Now, attempt the bind. */
832172827Smatteo		r = bindresvport_sa(fd, res->ai_addr);
833172827Smatteo		if (r != 0) {
834222623Srmacklem			if (errno == EADDRINUSE && mallocd_svcport != 0) {
835222623Srmacklem				if (mallocd_res != 0) {
836222623Srmacklem					free(res->ai_addr);
837222623Srmacklem					free(res);
838222623Srmacklem				} else
839222623Srmacklem					freeaddrinfo(res);
840222623Srmacklem				return (-1);
841222623Srmacklem			}
842172827Smatteo			syslog(LOG_ERR, "bindresvport_sa: %m");
843172827Smatteo			exit(1);
844172827Smatteo		}
845172827Smatteo
846222623Srmacklem		if (svcport_str == NULL) {
847222623Srmacklem			svcport_str = malloc(NI_MAXSERV * sizeof(char));
848222623Srmacklem			if (svcport_str == NULL)
849222623Srmacklem				out_of_mem();
850222623Srmacklem			mallocd_svcport = 1;
851222623Srmacklem
852222623Srmacklem			if (getnameinfo(res->ai_addr,
853222623Srmacklem			    res->ai_addr->sa_len, NULL, NI_MAXHOST,
854222623Srmacklem			    svcport_str, NI_MAXSERV * sizeof(char),
855222623Srmacklem			    NI_NUMERICHOST | NI_NUMERICSERV))
856222623Srmacklem				errx(1, "Cannot get port number");
857222623Srmacklem		}
858222623Srmacklem		if (mallocd_res != 0) {
859222623Srmacklem			free(res->ai_addr);
860222623Srmacklem			free(res);
861222623Srmacklem		} else
862222623Srmacklem			freeaddrinfo(res);
863222623Srmacklem		res = NULL;
864222623Srmacklem	}
865222623Srmacklem	return (0);
866222623Srmacklem}
867222623Srmacklem
868222623Srmacklem/*
869222623Srmacklem * Called after all the create_service() calls have succeeded, to complete
870222623Srmacklem * the setup and registration.
871222623Srmacklem */
872222623Srmacklemstatic void
873222623Srmacklemcomplete_service(struct netconfig *nconf, char *port_str)
874222623Srmacklem{
875222623Srmacklem	struct addrinfo hints, *res = NULL;
876222623Srmacklem	struct __rpc_sockinfo si;
877222623Srmacklem	struct netbuf servaddr;
878222623Srmacklem	SVCXPRT	*transp = NULL;
879222623Srmacklem	int aicode, fd, nhostsbak;
880222623Srmacklem	int registered = 0;
881222623Srmacklem
882222623Srmacklem	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
883222623Srmacklem	    (nconf->nc_semantics != NC_TPI_COTS) &&
884222623Srmacklem	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
885222623Srmacklem		return;	/* not my type */
886222623Srmacklem
887222623Srmacklem	/*
888222623Srmacklem	 * XXX - using RPC library internal functions.
889222623Srmacklem	 */
890222623Srmacklem	if (!__rpc_nconf2sockinfo(nconf, &si)) {
891222623Srmacklem		syslog(LOG_ERR, "cannot get information for %s",
892222623Srmacklem		    nconf->nc_netid);
893222623Srmacklem		return;
894222623Srmacklem	}
895222623Srmacklem
896222623Srmacklem	nhostsbak = nhosts;
897222623Srmacklem	while (nhostsbak > 0) {
898222623Srmacklem		--nhostsbak;
899222623Srmacklem		if (sock_fdpos >= sock_fdcnt) {
900222623Srmacklem			/* Should never happen. */
901222623Srmacklem			syslog(LOG_ERR, "Ran out of socket fd's");
902222623Srmacklem			return;
903222623Srmacklem		}
904222623Srmacklem		fd = sock_fd[sock_fdpos++];
905222623Srmacklem		if (fd < 0)
906222623Srmacklem			continue;
907222623Srmacklem
908172827Smatteo		if (nconf->nc_semantics != NC_TPI_CLTS)
909172827Smatteo			listen(fd, SOMAXCONN);
910172827Smatteo
911172827Smatteo		if (nconf->nc_semantics == NC_TPI_CLTS )
912172827Smatteo			transp = svc_dg_create(fd, 0, 0);
913172827Smatteo		else
914172827Smatteo			transp = svc_vc_create(fd, RPC_MAXDATASIZE,
915172827Smatteo			    RPC_MAXDATASIZE);
916172827Smatteo
917172827Smatteo		if (transp != (SVCXPRT *) NULL) {
918194880Sdfr			if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
919172827Smatteo			    NULL))
920172827Smatteo				syslog(LOG_ERR,
921194880Sdfr				    "can't register %s MOUNTVERS service",
922172827Smatteo				    nconf->nc_netid);
923172827Smatteo			if (!force_v2) {
924194880Sdfr				if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
925172827Smatteo				    mntsrv, NULL))
926172827Smatteo					syslog(LOG_ERR,
927194880Sdfr					    "can't register %s MOUNTVERS3 service",
928172827Smatteo					    nconf->nc_netid);
929172827Smatteo			}
930172827Smatteo		} else
931172827Smatteo			syslog(LOG_WARNING, "can't create %s services",
932172827Smatteo			    nconf->nc_netid);
933172827Smatteo
934172827Smatteo		if (registered == 0) {
935172827Smatteo			registered = 1;
936172827Smatteo			memset(&hints, 0, sizeof hints);
937172827Smatteo			hints.ai_flags = AI_PASSIVE;
938172827Smatteo			hints.ai_family = si.si_af;
939172827Smatteo			hints.ai_socktype = si.si_socktype;
940172827Smatteo			hints.ai_protocol = si.si_proto;
941172827Smatteo
942222623Srmacklem			if ((aicode = getaddrinfo(NULL, port_str, &hints,
943172827Smatteo			    &res)) != 0) {
944172827Smatteo				syslog(LOG_ERR, "cannot get local address: %s",
945172827Smatteo				    gai_strerror(aicode));
946172827Smatteo				exit(1);
947172827Smatteo			}
948172827Smatteo
949172827Smatteo			servaddr.buf = malloc(res->ai_addrlen);
950172827Smatteo			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
951172827Smatteo			servaddr.len = res->ai_addrlen;
952172827Smatteo
953194880Sdfr			rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
954194880Sdfr			rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
955172827Smatteo
956172827Smatteo			xcreated++;
957172827Smatteo			freeaddrinfo(res);
958172827Smatteo		}
959172827Smatteo	} /* end while */
9601558Srgrimes}
9611558Srgrimes
962222623Srmacklem/*
963222623Srmacklem * Clear out sockets after a failure to bind one of them, so that the
964222623Srmacklem * cycle of socket creation/binding can start anew.
965222623Srmacklem */
96637663Scharnierstatic void
967222623Srmacklemclearout_service(void)
968222623Srmacklem{
969222623Srmacklem	int i;
970222623Srmacklem
971222623Srmacklem	for (i = 0; i < sock_fdcnt; i++) {
972222623Srmacklem		if (sock_fd[i] >= 0) {
973222623Srmacklem			shutdown(sock_fd[i], SHUT_RDWR);
974222623Srmacklem			close(sock_fd[i]);
975222623Srmacklem		}
976222623Srmacklem	}
977222623Srmacklem}
978222623Srmacklem
979222623Srmacklemstatic void
980216587Scharnierusage(void)
98137663Scharnier{
98237663Scharnier	fprintf(stderr,
983192993Srmacklem		"usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
984241568Srmacklem		"[-S] [-h <bindip>] [export_file ...]\n");
98537663Scharnier	exit(1);
98637663Scharnier}
98737663Scharnier
9881558Srgrimes/*
9891558Srgrimes * The mount rpc service
9901558Srgrimes */
9911558Srgrimesvoid
992216587Scharniermntsrv(struct svc_req *rqstp, SVCXPRT *transp)
9931558Srgrimes{
9941558Srgrimes	struct exportlist *ep;
9951558Srgrimes	struct dirlist *dp;
9969336Sdfr	struct fhreturn fhr;
9971558Srgrimes	struct stat stb;
9981558Srgrimes	struct statfs fsb;
99974462Salfred	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
100074462Salfred	int lookup_failed = 1;
100174462Salfred	struct sockaddr *saddr;
10029336Sdfr	u_short sport;
1003194880Sdfr	char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
100428911Sguido	int bad = 0, defset, hostset;
10059336Sdfr	sigset_t sighup_mask;
1006240902Srmacklem	int numsecflavors, *secflavorsp;
10071558Srgrimes
10089336Sdfr	sigemptyset(&sighup_mask);
10099336Sdfr	sigaddset(&sighup_mask, SIGHUP);
101074462Salfred	saddr = svc_getrpccaller(transp)->buf;
101174462Salfred	switch (saddr->sa_family) {
101274462Salfred	case AF_INET6:
101375635Siedowse		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
101474462Salfred		break;
101574462Salfred	case AF_INET:
101675635Siedowse		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
101774462Salfred		break;
101874462Salfred	default:
101974462Salfred		syslog(LOG_ERR, "request from unknown address family");
102074462Salfred		return;
102174462Salfred	}
102274462Salfred	lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
102374462Salfred	    NULL, 0, 0);
102474462Salfred	getnameinfo(saddr, saddr->sa_len, numerichost,
102574462Salfred	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
10261558Srgrimes	switch (rqstp->rq_proc) {
10271558Srgrimes	case NULLPROC:
1028121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
102937663Scharnier			syslog(LOG_ERR, "can't send reply");
10301558Srgrimes		return;
1031194880Sdfr	case MOUNTPROC_MNT:
10329336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
103331656Sguido			syslog(LOG_NOTICE,
103431656Sguido			    "mount request from %s from unprivileged port",
103574462Salfred			    numerichost);
10361558Srgrimes			svcerr_weakauth(transp);
10371558Srgrimes			return;
10381558Srgrimes		}
1039121556Speter		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
104031656Sguido			syslog(LOG_NOTICE, "undecodable mount request from %s",
104174462Salfred			    numerichost);
10421558Srgrimes			svcerr_decode(transp);
10431558Srgrimes			return;
10441558Srgrimes		}
10451558Srgrimes
10461558Srgrimes		/*
10471558Srgrimes		 * Get the real pathname and make sure it is a directory
10489336Sdfr		 * or a regular file if the -r option was specified
10499336Sdfr		 * and it exists.
10501558Srgrimes		 */
105151968Salfred		if (realpath(rpcpath, dirpath) == NULL ||
10521558Srgrimes		    stat(dirpath, &stb) < 0 ||
10539336Sdfr		    (!S_ISDIR(stb.st_mode) &&
105474462Salfred		    (dir_only || !S_ISREG(stb.st_mode))) ||
10551558Srgrimes		    statfs(dirpath, &fsb) < 0) {
10561558Srgrimes			chdir("/");	/* Just in case realpath doesn't */
105731656Sguido			syslog(LOG_NOTICE,
105837663Scharnier			    "mount request from %s for non existent path %s",
105974462Salfred			    numerichost, dirpath);
10601558Srgrimes			if (debug)
106137663Scharnier				warnx("stat failed on %s", dirpath);
106228911Sguido			bad = ENOENT;	/* We will send error reply later */
10631558Srgrimes		}
10641558Srgrimes
10651558Srgrimes		/* Check in the exports list */
10669336Sdfr		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
10671558Srgrimes		ep = ex_search(&fsb.f_fsid);
10689336Sdfr		hostset = defset = 0;
1069240902Srmacklem		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
1070240902Srmacklem		    &numsecflavors, &secflavorsp) ||
10711558Srgrimes		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
1072240902Srmacklem		      chk_host(dp, saddr, &defset, &hostset, &numsecflavors,
1073240902Srmacklem		       &secflavorsp)) ||
107474462Salfred		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
107574462Salfred		     scan_tree(ep->ex_dirl, saddr) == 0))) {
107628911Sguido			if (bad) {
1077121556Speter				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
107828911Sguido				    (caddr_t)&bad))
107937663Scharnier					syslog(LOG_ERR, "can't send reply");
108028911Sguido				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
108128911Sguido				return;
108228911Sguido			}
1083240902Srmacklem			if (hostset & DP_HOSTSET) {
10849336Sdfr				fhr.fhr_flag = hostset;
1085240902Srmacklem				fhr.fhr_numsecflavors = numsecflavors;
1086240902Srmacklem				fhr.fhr_secflavors = secflavorsp;
1087240902Srmacklem			} else {
10889336Sdfr				fhr.fhr_flag = defset;
1089240902Srmacklem				fhr.fhr_numsecflavors = ep->ex_defnumsecflavors;
1090240902Srmacklem				fhr.fhr_secflavors = ep->ex_defsecflavors;
1091240902Srmacklem			}
10929336Sdfr			fhr.fhr_vers = rqstp->rq_vers;
10931558Srgrimes			/* Get the file handle */
109423681Speter			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
10959336Sdfr			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
10961558Srgrimes				bad = errno;
109737663Scharnier				syslog(LOG_ERR, "can't get fh for %s", dirpath);
1098121556Speter				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
10991558Srgrimes				    (caddr_t)&bad))
110037663Scharnier					syslog(LOG_ERR, "can't send reply");
11019336Sdfr				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
11021558Srgrimes				return;
11031558Srgrimes			}
1104121556Speter			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
1105121556Speter			    (caddr_t)&fhr))
110637663Scharnier				syslog(LOG_ERR, "can't send reply");
110774462Salfred			if (!lookup_failed)
110874462Salfred				add_mlist(host, dirpath);
11091558Srgrimes			else
111074462Salfred				add_mlist(numerichost, dirpath);
11111558Srgrimes			if (debug)
111237663Scharnier				warnx("mount successful");
1113121767Speter			if (dolog)
111431656Sguido				syslog(LOG_NOTICE,
111531656Sguido				    "mount request succeeded from %s for %s",
111674462Salfred				    numerichost, dirpath);
111731656Sguido		} else {
11181558Srgrimes			bad = EACCES;
111931656Sguido			syslog(LOG_NOTICE,
112031656Sguido			    "mount request denied from %s for %s",
112174462Salfred			    numerichost, dirpath);
112231656Sguido		}
112328911Sguido
1124121556Speter		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
1125121556Speter		    (caddr_t)&bad))
112637663Scharnier			syslog(LOG_ERR, "can't send reply");
11279336Sdfr		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
11281558Srgrimes		return;
1129194880Sdfr	case MOUNTPROC_DUMP:
1130121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
113137663Scharnier			syslog(LOG_ERR, "can't send reply");
1132121767Speter		else if (dolog)
113331656Sguido			syslog(LOG_NOTICE,
113431656Sguido			    "dump request succeeded from %s",
113574462Salfred			    numerichost);
11361558Srgrimes		return;
1137194880Sdfr	case MOUNTPROC_UMNT:
11389336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
113931656Sguido			syslog(LOG_NOTICE,
114031656Sguido			    "umount request from %s from unprivileged port",
114174462Salfred			    numerichost);
11421558Srgrimes			svcerr_weakauth(transp);
11431558Srgrimes			return;
11441558Srgrimes		}
1145121556Speter		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
114631656Sguido			syslog(LOG_NOTICE, "undecodable umount request from %s",
114774462Salfred			    numerichost);
11481558Srgrimes			svcerr_decode(transp);
11491558Srgrimes			return;
11501558Srgrimes		}
115151968Salfred		if (realpath(rpcpath, dirpath) == NULL) {
115251968Salfred			syslog(LOG_NOTICE, "umount request from %s "
115351968Salfred			    "for non existent path %s",
115474462Salfred			    numerichost, dirpath);
115551968Salfred		}
1156121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
115737663Scharnier			syslog(LOG_ERR, "can't send reply");
115874462Salfred		if (!lookup_failed)
115975635Siedowse			del_mlist(host, dirpath);
116075635Siedowse		del_mlist(numerichost, dirpath);
1161121767Speter		if (dolog)
116231656Sguido			syslog(LOG_NOTICE,
116331656Sguido			    "umount request succeeded from %s for %s",
116474462Salfred			    numerichost, dirpath);
11651558Srgrimes		return;
1166194880Sdfr	case MOUNTPROC_UMNTALL:
11679336Sdfr		if (sport >= IPPORT_RESERVED && resvport_only) {
116831656Sguido			syslog(LOG_NOTICE,
116931656Sguido			    "umountall request from %s from unprivileged port",
117074462Salfred			    numerichost);
11711558Srgrimes			svcerr_weakauth(transp);
11721558Srgrimes			return;
11731558Srgrimes		}
1174121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
117537663Scharnier			syslog(LOG_ERR, "can't send reply");
117674462Salfred		if (!lookup_failed)
117775635Siedowse			del_mlist(host, NULL);
117875635Siedowse		del_mlist(numerichost, NULL);
1179121767Speter		if (dolog)
118031656Sguido			syslog(LOG_NOTICE,
118131656Sguido			    "umountall request succeeded from %s",
118274462Salfred			    numerichost);
11831558Srgrimes		return;
1184194880Sdfr	case MOUNTPROC_EXPORT:
1185121556Speter		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
1186121556Speter			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
1187121556Speter			    (caddr_t)NULL))
1188100117Salfred				syslog(LOG_ERR, "can't send reply");
1189121767Speter		if (dolog)
119031656Sguido			syslog(LOG_NOTICE,
119131656Sguido			    "export request succeeded from %s",
119274462Salfred			    numerichost);
11931558Srgrimes		return;
11941558Srgrimes	default:
11951558Srgrimes		svcerr_noproc(transp);
11961558Srgrimes		return;
11971558Srgrimes	}
11981558Srgrimes}
11991558Srgrimes
12001558Srgrimes/*
12011558Srgrimes * Xdr conversion for a dirpath string
12021558Srgrimes */
1203285128Straszstatic int
1204216587Scharnierxdr_dir(XDR *xdrsp, char *dirp)
12051558Srgrimes{
1206194880Sdfr	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
12071558Srgrimes}
12081558Srgrimes
12091558Srgrimes/*
12109336Sdfr * Xdr routine to generate file handle reply
12111558Srgrimes */
1212285128Straszstatic int
1213216587Scharnierxdr_fhs(XDR *xdrsp, caddr_t cp)
12141558Srgrimes{
121592806Sobrien	struct fhreturn *fhrp = (struct fhreturn *)cp;
12169336Sdfr	u_long ok = 0, len, auth;
1217184588Sdfr	int i;
12181558Srgrimes
12191558Srgrimes	if (!xdr_long(xdrsp, &ok))
12201558Srgrimes		return (0);
12219336Sdfr	switch (fhrp->fhr_vers) {
12229336Sdfr	case 1:
12239336Sdfr		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
12249336Sdfr	case 3:
12259336Sdfr		len = NFSX_V3FH;
12269336Sdfr		if (!xdr_long(xdrsp, &len))
12279336Sdfr			return (0);
12289336Sdfr		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
12299336Sdfr			return (0);
1230184588Sdfr		if (fhrp->fhr_numsecflavors) {
1231184588Sdfr			if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
1232184588Sdfr				return (0);
1233184588Sdfr			for (i = 0; i < fhrp->fhr_numsecflavors; i++)
1234184588Sdfr				if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
1235184588Sdfr					return (0);
1236184588Sdfr			return (1);
1237184588Sdfr		} else {
1238184588Sdfr			auth = AUTH_SYS;
1239184588Sdfr			len = 1;
1240184588Sdfr			if (!xdr_long(xdrsp, &len))
1241184588Sdfr				return (0);
1242184588Sdfr			return (xdr_long(xdrsp, &auth));
1243184588Sdfr		}
1244298089Spfg	}
12459336Sdfr	return (0);
12461558Srgrimes}
12471558Srgrimes
1248285128Straszstatic int
1249216587Scharnierxdr_mlist(XDR *xdrsp, caddr_t cp __unused)
12501558Srgrimes{
12511558Srgrimes	struct mountlist *mlp;
12521558Srgrimes	int true = 1;
12531558Srgrimes	int false = 0;
12541558Srgrimes	char *strp;
12551558Srgrimes
12561558Srgrimes	mlp = mlhead;
12571558Srgrimes	while (mlp) {
12581558Srgrimes		if (!xdr_bool(xdrsp, &true))
12591558Srgrimes			return (0);
12601558Srgrimes		strp = &mlp->ml_host[0];
1261194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
12621558Srgrimes			return (0);
12631558Srgrimes		strp = &mlp->ml_dirp[0];
1264194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
12651558Srgrimes			return (0);
12661558Srgrimes		mlp = mlp->ml_next;
12671558Srgrimes	}
12681558Srgrimes	if (!xdr_bool(xdrsp, &false))
12691558Srgrimes		return (0);
12701558Srgrimes	return (1);
12711558Srgrimes}
12721558Srgrimes
12731558Srgrimes/*
12741558Srgrimes * Xdr conversion for export list
12751558Srgrimes */
1276285128Straszstatic int
1277216587Scharnierxdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
12781558Srgrimes{
12791558Srgrimes	struct exportlist *ep;
12801558Srgrimes	int false = 0;
12819336Sdfr	int putdef;
12829336Sdfr	sigset_t sighup_mask;
12831558Srgrimes
12849336Sdfr	sigemptyset(&sighup_mask);
12859336Sdfr	sigaddset(&sighup_mask, SIGHUP);
12869336Sdfr	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
12871558Srgrimes	ep = exphead;
12881558Srgrimes	while (ep) {
12891558Srgrimes		putdef = 0;
1290100117Salfred		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1291100117Salfred			       &putdef, brief))
12921558Srgrimes			goto errout;
12931558Srgrimes		if (ep->ex_defdir && putdef == 0 &&
12941558Srgrimes			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
1295100117Salfred			&putdef, brief))
12961558Srgrimes			goto errout;
12971558Srgrimes		ep = ep->ex_next;
12981558Srgrimes	}
12999336Sdfr	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
13001558Srgrimes	if (!xdr_bool(xdrsp, &false))
13011558Srgrimes		return (0);
13021558Srgrimes	return (1);
13031558Srgrimeserrout:
13049336Sdfr	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
13051558Srgrimes	return (0);
13061558Srgrimes}
13071558Srgrimes
13081558Srgrimes/*
13091558Srgrimes * Called from xdr_explist() to traverse the tree and export the
13101558Srgrimes * directory paths.
13111558Srgrimes */
1312285128Straszstatic int
1313216587Scharnierput_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1314216587Scharnier	int brief)
13151558Srgrimes{
13161558Srgrimes	struct grouplist *grp;
13171558Srgrimes	struct hostlist *hp;
13181558Srgrimes	int true = 1;
13191558Srgrimes	int false = 0;
13201558Srgrimes	int gotalldir = 0;
13211558Srgrimes	char *strp;
13221558Srgrimes
13231558Srgrimes	if (dp) {
1324100117Salfred		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
13251558Srgrimes			return (1);
13261558Srgrimes		if (!xdr_bool(xdrsp, &true))
13271558Srgrimes			return (1);
13281558Srgrimes		strp = dp->dp_dirp;
1329194880Sdfr		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
13301558Srgrimes			return (1);
13311558Srgrimes		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
13321558Srgrimes			gotalldir = 1;
13331558Srgrimes			*putdefp = 1;
13341558Srgrimes		}
1335100117Salfred		if (brief) {
1336100117Salfred			if (!xdr_bool(xdrsp, &true))
1337100117Salfred				return (1);
1338100117Salfred			strp = "(...)";
1339194880Sdfr			if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1340100117Salfred				return (1);
1341100117Salfred		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
13421558Srgrimes		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
13431558Srgrimes			hp = dp->dp_hosts;
13441558Srgrimes			while (hp) {
13451558Srgrimes				grp = hp->ht_grp;
13461558Srgrimes				if (grp->gr_type == GT_HOST) {
13471558Srgrimes					if (!xdr_bool(xdrsp, &true))
13481558Srgrimes						return (1);
134974462Salfred					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
13508871Srgrimes					if (!xdr_string(xdrsp, &strp,
1351194880Sdfr					    MNTNAMLEN))
13521558Srgrimes						return (1);
13531558Srgrimes				} else if (grp->gr_type == GT_NET) {
13541558Srgrimes					if (!xdr_bool(xdrsp, &true))
13551558Srgrimes						return (1);
13561558Srgrimes					strp = grp->gr_ptr.gt_net.nt_name;
13578871Srgrimes					if (!xdr_string(xdrsp, &strp,
1358194880Sdfr					    MNTNAMLEN))
13591558Srgrimes						return (1);
13601558Srgrimes				}
13611558Srgrimes				hp = hp->ht_next;
13621558Srgrimes				if (gotalldir && hp == (struct hostlist *)NULL) {
13631558Srgrimes					hp = adp->dp_hosts;
13641558Srgrimes					gotalldir = 0;
13651558Srgrimes				}
13661558Srgrimes			}
13671558Srgrimes		}
13681558Srgrimes		if (!xdr_bool(xdrsp, &false))
13691558Srgrimes			return (1);
1370100117Salfred		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
13711558Srgrimes			return (1);
13721558Srgrimes	}
13731558Srgrimes	return (0);
13741558Srgrimes}
13751558Srgrimes
1376285128Straszstatic int
1377216587Scharnierxdr_explist(XDR *xdrsp, caddr_t cp)
1378100117Salfred{
1379100117Salfred
1380100117Salfred	return xdr_explist_common(xdrsp, cp, 0);
1381100117Salfred}
1382100117Salfred
1383285128Straszstatic int
1384216587Scharnierxdr_explist_brief(XDR *xdrsp, caddr_t cp)
1385100117Salfred{
1386100117Salfred
1387100117Salfred	return xdr_explist_common(xdrsp, cp, 1);
1388100117Salfred}
1389100117Salfred
1390285128Straszstatic char *line;
1391285128Straszstatic size_t linesize;
1392285128Straszstatic FILE *exp_file;
13931558Srgrimes
13941558Srgrimes/*
1395166440Spjd * Get the export list from one, currently open file
13961558Srgrimes */
1397166440Spjdstatic void
1398216587Scharnierget_exportlist_one(void)
13991558Srgrimes{
14001558Srgrimes	struct exportlist *ep, *ep2;
14011558Srgrimes	struct grouplist *grp, *tgrp;
14021558Srgrimes	struct exportlist **epp;
14031558Srgrimes	struct dirlist *dirhead;
1404166440Spjd	struct statfs fsb;
140572650Sgreen	struct xucred anon;
14061558Srgrimes	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1407166440Spjd	int len, has_host, exflags, got_nondir, dirplen, netgrp;
14081558Srgrimes
1409192934Srmacklem	v4root_phase = 0;
14101558Srgrimes	dirhead = (struct dirlist *)NULL;
14111558Srgrimes	while (get_line()) {
14121558Srgrimes		if (debug)
141337663Scharnier			warnx("got line %s", line);
14141558Srgrimes		cp = line;
14151558Srgrimes		nextfield(&cp, &endcp);
14161558Srgrimes		if (*cp == '#')
14171558Srgrimes			goto nextline;
14181558Srgrimes
14191558Srgrimes		/*
14201558Srgrimes		 * Set defaults.
14211558Srgrimes		 */
14221558Srgrimes		has_host = FALSE;
14231558Srgrimes		anon = def_anon;
14241558Srgrimes		exflags = MNT_EXPORTED;
14251558Srgrimes		got_nondir = 0;
14261558Srgrimes		opt_flags = 0;
14271558Srgrimes		ep = (struct exportlist *)NULL;
1428192934Srmacklem		dirp = NULL;
14291558Srgrimes
14301558Srgrimes		/*
1431192934Srmacklem		 * Handle the V4 root dir.
1432192934Srmacklem		 */
1433192934Srmacklem		if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1434192934Srmacklem			/*
1435192934Srmacklem			 * V4: just indicates that it is the v4 root point,
1436192934Srmacklem			 * so skip over that and set v4root_phase.
1437192934Srmacklem			 */
1438192934Srmacklem			if (v4root_phase > 0) {
1439192934Srmacklem				syslog(LOG_ERR, "V4:duplicate line, ignored");
1440192934Srmacklem				goto nextline;
1441192934Srmacklem			}
1442192934Srmacklem			v4root_phase = 1;
1443192934Srmacklem			cp += 3;
1444192934Srmacklem			nextfield(&cp, &endcp);
1445192934Srmacklem		}
1446192934Srmacklem
1447192934Srmacklem		/*
14481558Srgrimes		 * Create new exports list entry
14491558Srgrimes		 */
14501558Srgrimes		len = endcp-cp;
14511558Srgrimes		tgrp = grp = get_grp();
14521558Srgrimes		while (len > 0) {
1453194880Sdfr			if (len > MNTNAMLEN) {
14541558Srgrimes			    getexp_err(ep, tgrp);
14551558Srgrimes			    goto nextline;
14561558Srgrimes			}
14571558Srgrimes			if (*cp == '-') {
14581558Srgrimes			    if (ep == (struct exportlist *)NULL) {
14591558Srgrimes				getexp_err(ep, tgrp);
14601558Srgrimes				goto nextline;
14611558Srgrimes			    }
14621558Srgrimes			    if (debug)
146337663Scharnier				warnx("doing opt %s", cp);
14641558Srgrimes			    got_nondir = 1;
14651558Srgrimes			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
14661558Srgrimes				&exflags, &anon)) {
14671558Srgrimes				getexp_err(ep, tgrp);
14681558Srgrimes				goto nextline;
14691558Srgrimes			    }
14701558Srgrimes			} else if (*cp == '/') {
14711558Srgrimes			    savedc = *endcp;
14721558Srgrimes			    *endcp = '\0';
1473192934Srmacklem			    if (v4root_phase > 1) {
1474192934Srmacklem				    if (dirp != NULL) {
1475192934Srmacklem					syslog(LOG_ERR, "Multiple V4 dirs");
1476192934Srmacklem					getexp_err(ep, tgrp);
1477192934Srmacklem					goto nextline;
1478192934Srmacklem				    }
1479192934Srmacklem			    }
14801558Srgrimes			    if (check_dirpath(cp) &&
14811558Srgrimes				statfs(cp, &fsb) >= 0) {
1482283008Srmacklem				if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
1483283008Srmacklem				    syslog(LOG_ERR, "Warning: exporting of "
1484283008Srmacklem					"automounted fs %s not supported", cp);
14851558Srgrimes				if (got_nondir) {
148637663Scharnier				    syslog(LOG_ERR, "dirs must be first");
14871558Srgrimes				    getexp_err(ep, tgrp);
14881558Srgrimes				    goto nextline;
14891558Srgrimes				}
1490192934Srmacklem				if (v4root_phase == 1) {
1491192934Srmacklem				    if (dirp != NULL) {
1492192934Srmacklem					syslog(LOG_ERR, "Multiple V4 dirs");
14931558Srgrimes					getexp_err(ep, tgrp);
14941558Srgrimes					goto nextline;
14951558Srgrimes				    }
1496192934Srmacklem				    if (strlen(v4root_dirpath) == 0) {
1497192934Srmacklem					strlcpy(v4root_dirpath, cp,
1498192934Srmacklem					    sizeof (v4root_dirpath));
1499192934Srmacklem				    } else if (strcmp(v4root_dirpath, cp)
1500192934Srmacklem					!= 0) {
1501192934Srmacklem					syslog(LOG_ERR,
1502192934Srmacklem					    "different V4 dirpath %s", cp);
1503192934Srmacklem					getexp_err(ep, tgrp);
1504192934Srmacklem					goto nextline;
1505192934Srmacklem				    }
1506192934Srmacklem				    dirp = cp;
1507192934Srmacklem				    v4root_phase = 2;
1508192934Srmacklem				    got_nondir = 1;
1509192934Srmacklem				    ep = get_exp();
15101558Srgrimes				} else {
1511192934Srmacklem				    if (ep) {
1512192934Srmacklem					if (ep->ex_fs.val[0] !=
1513192934Srmacklem					    fsb.f_fsid.val[0] ||
1514192934Srmacklem					    ep->ex_fs.val[1] !=
1515192934Srmacklem					    fsb.f_fsid.val[1]) {
1516192934Srmacklem						getexp_err(ep, tgrp);
1517192934Srmacklem						goto nextline;
1518192934Srmacklem					}
1519192934Srmacklem				    } else {
1520192934Srmacklem					/*
1521192934Srmacklem					 * See if this directory is already
1522192934Srmacklem					 * in the list.
1523192934Srmacklem					 */
1524192934Srmacklem					ep = ex_search(&fsb.f_fsid);
1525192934Srmacklem					if (ep == (struct exportlist *)NULL) {
1526192934Srmacklem					    ep = get_exp();
1527192934Srmacklem					    ep->ex_fs = fsb.f_fsid;
1528192934Srmacklem					    ep->ex_fsdir = (char *)malloc
1529192934Srmacklem					        (strlen(fsb.f_mntonname) + 1);
1530192934Srmacklem					    if (ep->ex_fsdir)
1531192934Srmacklem						strcpy(ep->ex_fsdir,
1532192934Srmacklem						    fsb.f_mntonname);
1533192934Srmacklem					    else
1534192934Srmacklem						out_of_mem();
1535192934Srmacklem					    if (debug)
1536192934Srmacklem						warnx(
1537192934Srmacklem						  "making new ep fs=0x%x,0x%x",
1538192934Srmacklem						  fsb.f_fsid.val[0],
1539192934Srmacklem						  fsb.f_fsid.val[1]);
1540192934Srmacklem					} else if (debug)
1541192934Srmacklem					    warnx("found ep fs=0x%x,0x%x",
1542192934Srmacklem						fsb.f_fsid.val[0],
1543192934Srmacklem						fsb.f_fsid.val[1]);
1544192934Srmacklem				    }
1545192934Srmacklem
15461558Srgrimes				    /*
1547192934Srmacklem				     * Add dirpath to export mount point.
15481558Srgrimes				     */
1549192934Srmacklem				    dirp = add_expdir(&dirhead, cp, len);
1550192934Srmacklem				    dirplen = len;
15511558Srgrimes				}
15521558Srgrimes			    } else {
15531558Srgrimes				getexp_err(ep, tgrp);
15541558Srgrimes				goto nextline;
15551558Srgrimes			    }
15561558Srgrimes			    *endcp = savedc;
15571558Srgrimes			} else {
15581558Srgrimes			    savedc = *endcp;
15591558Srgrimes			    *endcp = '\0';
15601558Srgrimes			    got_nondir = 1;
15611558Srgrimes			    if (ep == (struct exportlist *)NULL) {
15621558Srgrimes				getexp_err(ep, tgrp);
15631558Srgrimes				goto nextline;
15641558Srgrimes			    }
15651558Srgrimes
15661558Srgrimes			    /*
15671558Srgrimes			     * Get the host or netgroup.
15681558Srgrimes			     */
15691558Srgrimes			    setnetgrent(cp);
15701558Srgrimes			    netgrp = getnetgrent(&hst, &usr, &dom);
15711558Srgrimes			    do {
15721558Srgrimes				if (has_host) {
15731558Srgrimes				    grp->gr_next = get_grp();
15741558Srgrimes				    grp = grp->gr_next;
15751558Srgrimes				}
15761558Srgrimes				if (netgrp) {
157737003Sjoerg				    if (hst == 0) {
157837663Scharnier					syslog(LOG_ERR,
157937663Scharnier				"null hostname in netgroup %s, skipping", cp);
158037004Sjoerg					grp->gr_type = GT_IGNORE;
158137003Sjoerg				    } else if (get_host(hst, grp, tgrp)) {
158237663Scharnier					syslog(LOG_ERR,
158337663Scharnier			"bad host %s in netgroup %s, skipping", hst, cp);
158429317Sjlemon					grp->gr_type = GT_IGNORE;
15851558Srgrimes				    }
15867401Swpaul				} else if (get_host(cp, grp, tgrp)) {
158737663Scharnier				    syslog(LOG_ERR, "bad host %s, skipping", cp);
158829317Sjlemon				    grp->gr_type = GT_IGNORE;
15891558Srgrimes				}
15901558Srgrimes				has_host = TRUE;
15911558Srgrimes			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
15921558Srgrimes			    endnetgrent();
15931558Srgrimes			    *endcp = savedc;
15941558Srgrimes			}
15951558Srgrimes			cp = endcp;
15961558Srgrimes			nextfield(&cp, &endcp);
15971558Srgrimes			len = endcp - cp;
15981558Srgrimes		}
15991558Srgrimes		if (check_options(dirhead)) {
16001558Srgrimes			getexp_err(ep, tgrp);
16011558Srgrimes			goto nextline;
16021558Srgrimes		}
16031558Srgrimes		if (!has_host) {
160475641Siedowse			grp->gr_type = GT_DEFAULT;
16051558Srgrimes			if (debug)
160637663Scharnier				warnx("adding a default entry");
16071558Srgrimes
16081558Srgrimes		/*
16091558Srgrimes		 * Don't allow a network export coincide with a list of
16101558Srgrimes		 * host(s) on the same line.
16111558Srgrimes		 */
16121558Srgrimes		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
161375801Siedowse			syslog(LOG_ERR, "network/host conflict");
16141558Srgrimes			getexp_err(ep, tgrp);
16151558Srgrimes			goto nextline;
161629317Sjlemon
161774462Salfred		/*
161874462Salfred		 * If an export list was specified on this line, make sure
161929317Sjlemon		 * that we have at least one valid entry, otherwise skip it.
162029317Sjlemon		 */
162129317Sjlemon		} else {
162229317Sjlemon			grp = tgrp;
162374462Salfred			while (grp && grp->gr_type == GT_IGNORE)
162429317Sjlemon				grp = grp->gr_next;
162529317Sjlemon			if (! grp) {
162629317Sjlemon			    getexp_err(ep, tgrp);
162729317Sjlemon			    goto nextline;
162829317Sjlemon			}
16291558Srgrimes		}
16301558Srgrimes
1631192934Srmacklem		if (v4root_phase == 1) {
1632192934Srmacklem			syslog(LOG_ERR, "V4:root, no dirp, ignored");
1633192934Srmacklem			getexp_err(ep, tgrp);
1634192934Srmacklem			goto nextline;
1635192934Srmacklem		}
1636192934Srmacklem
16371558Srgrimes		/*
16381558Srgrimes		 * Loop through hosts, pushing the exports into the kernel.
16391558Srgrimes		 * After loop, tgrp points to the start of the list and
16401558Srgrimes		 * grp points to the last entry in the list.
16411558Srgrimes		 */
16421558Srgrimes		grp = tgrp;
16431558Srgrimes		do {
164475635Siedowse			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
164575635Siedowse			    &fsb)) {
164675635Siedowse				getexp_err(ep, tgrp);
164775635Siedowse				goto nextline;
164875635Siedowse			}
16491558Srgrimes		} while (grp->gr_next && (grp = grp->gr_next));
16501558Srgrimes
16511558Srgrimes		/*
1652192934Srmacklem		 * For V4: don't enter in mount lists.
1653192934Srmacklem		 */
1654194773Srmacklem		if (v4root_phase > 0 && v4root_phase <= 2) {
1655194773Srmacklem			/*
1656194773Srmacklem			 * Since these structures aren't used by mountd,
1657194773Srmacklem			 * free them up now.
1658194773Srmacklem			 */
1659194773Srmacklem			if (ep != NULL)
1660194773Srmacklem				free_exp(ep);
1661194773Srmacklem			while (tgrp != NULL) {
1662194773Srmacklem				grp = tgrp;
1663194773Srmacklem				tgrp = tgrp->gr_next;
1664194773Srmacklem				free_grp(grp);
1665194773Srmacklem			}
1666192934Srmacklem			goto nextline;
1667194773Srmacklem		}
1668192934Srmacklem
1669192934Srmacklem		/*
16701558Srgrimes		 * Success. Update the data structures.
16711558Srgrimes		 */
16721558Srgrimes		if (has_host) {
16739336Sdfr			hang_dirp(dirhead, tgrp, ep, opt_flags);
16741558Srgrimes			grp->gr_next = grphead;
16751558Srgrimes			grphead = tgrp;
16761558Srgrimes		} else {
16771558Srgrimes			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
16789336Sdfr				opt_flags);
16791558Srgrimes			free_grp(grp);
16801558Srgrimes		}
16811558Srgrimes		dirhead = (struct dirlist *)NULL;
16821558Srgrimes		if ((ep->ex_flag & EX_LINKED) == 0) {
16831558Srgrimes			ep2 = exphead;
16841558Srgrimes			epp = &exphead;
16851558Srgrimes
16861558Srgrimes			/*
16871558Srgrimes			 * Insert in the list in alphabetical order.
16881558Srgrimes			 */
16891558Srgrimes			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
16901558Srgrimes				epp = &ep2->ex_next;
16911558Srgrimes				ep2 = ep2->ex_next;
16921558Srgrimes			}
16931558Srgrimes			if (ep2)
16941558Srgrimes				ep->ex_next = ep2;
16951558Srgrimes			*epp = ep;
16961558Srgrimes			ep->ex_flag |= EX_LINKED;
16971558Srgrimes		}
16981558Srgrimesnextline:
1699192934Srmacklem		v4root_phase = 0;
17001558Srgrimes		if (dirhead) {
17011558Srgrimes			free_dir(dirhead);
17021558Srgrimes			dirhead = (struct dirlist *)NULL;
17031558Srgrimes		}
17041558Srgrimes	}
17051558Srgrimes}
17061558Srgrimes
17071558Srgrimes/*
1708166440Spjd * Get the export list from all specified files
1709166440Spjd */
1710285128Straszstatic void
1711216587Scharnierget_exportlist(void)
1712166440Spjd{
1713166440Spjd	struct exportlist *ep, *ep2;
1714166440Spjd	struct grouplist *grp, *tgrp;
1715166440Spjd	struct export_args export;
1716166440Spjd	struct iovec *iov;
1717166440Spjd	struct statfs *fsp, *mntbufp;
1718166440Spjd	struct xvfsconf vfc;
1719166440Spjd	char errmsg[255];
1720230352Seadler	int num, i;
1721166440Spjd	int iovlen;
1722168684Spjd	int done;
1723192934Srmacklem	struct nfsex_args eargs;
1724166440Spjd
1725241568Srmacklem	if (suspend_nfsd != 0)
1726241568Srmacklem		(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
1727192934Srmacklem	v4root_dirpath[0] = '\0';
1728166440Spjd	bzero(&export, sizeof(export));
1729166440Spjd	export.ex_flags = MNT_DELEXPORT;
1730166440Spjd	iov = NULL;
1731166440Spjd	iovlen = 0;
1732166440Spjd	bzero(errmsg, sizeof(errmsg));
1733166440Spjd
1734166440Spjd	/*
1735166440Spjd	 * First, get rid of the old list
1736166440Spjd	 */
1737166440Spjd	ep = exphead;
1738166440Spjd	while (ep) {
1739166440Spjd		ep2 = ep;
1740166440Spjd		ep = ep->ex_next;
1741166440Spjd		free_exp(ep2);
1742166440Spjd	}
1743166440Spjd	exphead = (struct exportlist *)NULL;
1744166440Spjd
1745166440Spjd	grp = grphead;
1746166440Spjd	while (grp) {
1747166440Spjd		tgrp = grp;
1748166440Spjd		grp = grp->gr_next;
1749166440Spjd		free_grp(tgrp);
1750166440Spjd	}
1751166440Spjd	grphead = (struct grouplist *)NULL;
1752166440Spjd
1753166440Spjd	/*
1754192934Srmacklem	 * and the old V4 root dir.
1755192934Srmacklem	 */
1756192934Srmacklem	bzero(&eargs, sizeof (eargs));
1757192934Srmacklem	eargs.export.ex_flags = MNT_DELEXPORT;
1758282214Strasz	if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
1759192934Srmacklem	    errno != ENOENT)
1760192934Srmacklem		syslog(LOG_ERR, "Can't delete exports for V4:");
1761192934Srmacklem
1762192934Srmacklem	/*
1763192934Srmacklem	 * and clear flag that notes if a public fh has been exported.
1764192934Srmacklem	 */
1765192934Srmacklem	has_publicfh = 0;
1766192934Srmacklem
1767192934Srmacklem	/*
1768166440Spjd	 * And delete exports that are in the kernel for all local
1769166440Spjd	 * filesystems.
1770166440Spjd	 * XXX: Should know how to handle all local exportable filesystems.
1771166440Spjd	 */
1772166440Spjd	num = getmntinfo(&mntbufp, MNT_NOWAIT);
1773166440Spjd
1774166440Spjd	if (num > 0) {
1775166440Spjd		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1776166440Spjd		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1777166440Spjd		build_iovec(&iov, &iovlen, "from", NULL, 0);
1778166440Spjd		build_iovec(&iov, &iovlen, "update", NULL, 0);
1779166440Spjd		build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
1780166440Spjd		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1781166440Spjd	}
1782166440Spjd
1783166440Spjd	for (i = 0; i < num; i++) {
1784166440Spjd		fsp = &mntbufp[i];
1785166440Spjd		if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
1786166440Spjd			syslog(LOG_ERR, "getvfsbyname() failed for %s",
1787166440Spjd			    fsp->f_fstypename);
1788166440Spjd			continue;
1789166440Spjd		}
1790166440Spjd
1791166440Spjd		/*
1792281699Ssjg		 * We do not need to delete "export" flag from
1793281699Ssjg		 * filesystems that do not have it set.
1794281699Ssjg		 */
1795281699Ssjg		if (!(fsp->f_flags & MNT_EXPORTED))
1796281699Ssjg		    continue;
1797281699Ssjg		/*
1798166440Spjd		 * Do not delete export for network filesystem by
1799166440Spjd		 * passing "export" arg to nmount().
1800166440Spjd		 * It only makes sense to do this for local filesystems.
1801166440Spjd		 */
1802166440Spjd		if (vfc.vfc_flags & VFCF_NETWORK)
1803166440Spjd			continue;
1804166440Spjd
1805166440Spjd		iov[1].iov_base = fsp->f_fstypename;
1806166440Spjd		iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
1807166440Spjd		iov[3].iov_base = fsp->f_mntonname;
1808166440Spjd		iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
1809166440Spjd		iov[5].iov_base = fsp->f_mntfromname;
1810166440Spjd		iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
1811270183Sbdrewery		errmsg[0] = '\0';
1812166440Spjd
1813278523Skib		/*
1814278523Skib		 * EXDEV is returned when path exists but is not a
1815278523Skib		 * mount point.  May happens if raced with unmount.
1816278523Skib		 */
1817166440Spjd		if (nmount(iov, iovlen, fsp->f_flags) < 0 &&
1818278523Skib		    errno != ENOENT && errno != ENOTSUP && errno != EXDEV) {
1819166440Spjd			syslog(LOG_ERR,
1820166440Spjd			    "can't delete exports for %s: %m %s",
1821166440Spjd			    fsp->f_mntonname, errmsg);
1822166440Spjd		}
1823166440Spjd	}
1824166440Spjd
1825166440Spjd	if (iov != NULL) {
1826166440Spjd		/* Free strings allocated by strdup() in getmntopts.c */
1827166440Spjd		free(iov[0].iov_base); /* fstype */
1828166440Spjd		free(iov[2].iov_base); /* fspath */
1829166440Spjd		free(iov[4].iov_base); /* from */
1830166440Spjd		free(iov[6].iov_base); /* update */
1831166440Spjd		free(iov[8].iov_base); /* export */
1832166440Spjd		free(iov[10].iov_base); /* errmsg */
1833166440Spjd
1834166440Spjd		/* free iov, allocated by realloc() */
1835166440Spjd		free(iov);
1836166440Spjd		iovlen = 0;
1837166440Spjd	}
1838166440Spjd
1839166440Spjd	/*
1840166440Spjd	 * Read in the exports file and build the list, calling
1841166440Spjd	 * nmount() as we go along to push the export rules into the kernel.
1842166440Spjd	 */
1843168684Spjd	done = 0;
1844166440Spjd	for (i = 0; exnames[i] != NULL; i++) {
1845166440Spjd		if (debug)
1846166440Spjd			warnx("reading exports from %s", exnames[i]);
1847166440Spjd		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
1848168684Spjd			syslog(LOG_WARNING, "can't open %s", exnames[i]);
1849168684Spjd			continue;
1850166440Spjd		}
1851166440Spjd		get_exportlist_one();
1852166440Spjd		fclose(exp_file);
1853168684Spjd		done++;
1854166440Spjd	}
1855168684Spjd	if (done == 0) {
1856168684Spjd		syslog(LOG_ERR, "can't open any exports file");
1857168684Spjd		exit(2);
1858168684Spjd	}
1859192934Srmacklem
1860192934Srmacklem	/*
1861192934Srmacklem	 * If there was no public fh, clear any previous one set.
1862192934Srmacklem	 */
1863282214Strasz	if (has_publicfh == 0)
1864192934Srmacklem		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
1865241568Srmacklem
1866241568Srmacklem	/* Resume the nfsd. If they weren't suspended, this is harmless. */
1867241568Srmacklem	(void)nfssvc(NFSSVC_RESUMENFSD, NULL);
1868166440Spjd}
1869166440Spjd
1870166440Spjd/*
18711558Srgrimes * Allocate an export list element
18721558Srgrimes */
1873285128Straszstatic struct exportlist *
1874216587Scharnierget_exp(void)
18751558Srgrimes{
18761558Srgrimes	struct exportlist *ep;
18771558Srgrimes
1878224003Sdelphij	ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
18791558Srgrimes	if (ep == (struct exportlist *)NULL)
18801558Srgrimes		out_of_mem();
18811558Srgrimes	return (ep);
18821558Srgrimes}
18831558Srgrimes
18841558Srgrimes/*
18851558Srgrimes * Allocate a group list element
18861558Srgrimes */
1887285128Straszstatic struct grouplist *
1888216587Scharnierget_grp(void)
18891558Srgrimes{
18901558Srgrimes	struct grouplist *gp;
18911558Srgrimes
1892224003Sdelphij	gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
18931558Srgrimes	if (gp == (struct grouplist *)NULL)
18941558Srgrimes		out_of_mem();
18951558Srgrimes	return (gp);
18961558Srgrimes}
18971558Srgrimes
18981558Srgrimes/*
18991558Srgrimes * Clean up upon an error in get_exportlist().
19001558Srgrimes */
1901285128Straszstatic void
1902216587Scharniergetexp_err(struct exportlist *ep, struct grouplist *grp)
19031558Srgrimes{
19041558Srgrimes	struct grouplist *tgrp;
19051558Srgrimes
1906100336Sjoerg	if (!(opt_flags & OP_QUIET))
1907100336Sjoerg		syslog(LOG_ERR, "bad exports list line %s", line);
19081558Srgrimes	if (ep && (ep->ex_flag & EX_LINKED) == 0)
19091558Srgrimes		free_exp(ep);
19101558Srgrimes	while (grp) {
19111558Srgrimes		tgrp = grp;
19121558Srgrimes		grp = grp->gr_next;
19131558Srgrimes		free_grp(tgrp);
19141558Srgrimes	}
19151558Srgrimes}
19161558Srgrimes
19171558Srgrimes/*
19181558Srgrimes * Search the export list for a matching fs.
19191558Srgrimes */
1920285128Straszstatic struct exportlist *
1921216587Scharnierex_search(fsid_t *fsid)
19221558Srgrimes{
19231558Srgrimes	struct exportlist *ep;
19241558Srgrimes
19251558Srgrimes	ep = exphead;
19261558Srgrimes	while (ep) {
19271558Srgrimes		if (ep->ex_fs.val[0] == fsid->val[0] &&
19281558Srgrimes		    ep->ex_fs.val[1] == fsid->val[1])
19291558Srgrimes			return (ep);
19301558Srgrimes		ep = ep->ex_next;
19311558Srgrimes	}
19321558Srgrimes	return (ep);
19331558Srgrimes}
19341558Srgrimes
19351558Srgrimes/*
19361558Srgrimes * Add a directory path to the list.
19371558Srgrimes */
1938285128Straszstatic char *
1939216587Scharnieradd_expdir(struct dirlist **dpp, char *cp, int len)
19401558Srgrimes{
19411558Srgrimes	struct dirlist *dp;
19421558Srgrimes
19431558Srgrimes	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
194437663Scharnier	if (dp == (struct dirlist *)NULL)
194537663Scharnier		out_of_mem();
19461558Srgrimes	dp->dp_left = *dpp;
19471558Srgrimes	dp->dp_right = (struct dirlist *)NULL;
19481558Srgrimes	dp->dp_flag = 0;
19491558Srgrimes	dp->dp_hosts = (struct hostlist *)NULL;
19501558Srgrimes	strcpy(dp->dp_dirp, cp);
19511558Srgrimes	*dpp = dp;
19521558Srgrimes	return (dp->dp_dirp);
19531558Srgrimes}
19541558Srgrimes
19551558Srgrimes/*
19561558Srgrimes * Hang the dir list element off the dirpath binary tree as required
19571558Srgrimes * and update the entry for host.
19581558Srgrimes */
1959285128Straszstatic void
1960216587Scharnierhang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
1961216587Scharnier	int flags)
19621558Srgrimes{
19631558Srgrimes	struct hostlist *hp;
19641558Srgrimes	struct dirlist *dp2;
19651558Srgrimes
19669336Sdfr	if (flags & OP_ALLDIRS) {
19671558Srgrimes		if (ep->ex_defdir)
19681558Srgrimes			free((caddr_t)dp);
19691558Srgrimes		else
19701558Srgrimes			ep->ex_defdir = dp;
19719336Sdfr		if (grp == (struct grouplist *)NULL) {
19721558Srgrimes			ep->ex_defdir->dp_flag |= DP_DEFSET;
1973240902Srmacklem			/* Save the default security flavors list. */
1974240902Srmacklem			ep->ex_defnumsecflavors = ep->ex_numsecflavors;
1975240902Srmacklem			if (ep->ex_numsecflavors > 0)
1976240902Srmacklem				memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
1977240902Srmacklem				    sizeof(ep->ex_secflavors));
19789336Sdfr		} else while (grp) {
19791558Srgrimes			hp = get_ht();
19801558Srgrimes			hp->ht_grp = grp;
19811558Srgrimes			hp->ht_next = ep->ex_defdir->dp_hosts;
19821558Srgrimes			ep->ex_defdir->dp_hosts = hp;
1983240902Srmacklem			/* Save the security flavors list for this host set. */
1984240902Srmacklem			grp->gr_numsecflavors = ep->ex_numsecflavors;
1985240902Srmacklem			if (ep->ex_numsecflavors > 0)
1986240902Srmacklem				memcpy(grp->gr_secflavors, ep->ex_secflavors,
1987240902Srmacklem				    sizeof(ep->ex_secflavors));
19881558Srgrimes			grp = grp->gr_next;
19891558Srgrimes		}
19901558Srgrimes	} else {
19911558Srgrimes
19921558Srgrimes		/*
199337663Scharnier		 * Loop through the directories adding them to the tree.
19941558Srgrimes		 */
19951558Srgrimes		while (dp) {
19961558Srgrimes			dp2 = dp->dp_left;
1997240902Srmacklem			add_dlist(&ep->ex_dirl, dp, grp, flags, ep);
19981558Srgrimes			dp = dp2;
19991558Srgrimes		}
20001558Srgrimes	}
20011558Srgrimes}
20021558Srgrimes
20031558Srgrimes/*
20041558Srgrimes * Traverse the binary tree either updating a node that is already there
20051558Srgrimes * for the new directory or adding the new node.
20061558Srgrimes */
2007285128Straszstatic void
2008216587Scharnieradd_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
2009240902Srmacklem	int flags, struct exportlist *ep)
20101558Srgrimes{
20111558Srgrimes	struct dirlist *dp;
20121558Srgrimes	struct hostlist *hp;
20131558Srgrimes	int cmp;
20141558Srgrimes
20151558Srgrimes	dp = *dpp;
20161558Srgrimes	if (dp) {
20171558Srgrimes		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
20181558Srgrimes		if (cmp > 0) {
2019240902Srmacklem			add_dlist(&dp->dp_left, newdp, grp, flags, ep);
20201558Srgrimes			return;
20211558Srgrimes		} else if (cmp < 0) {
2022240902Srmacklem			add_dlist(&dp->dp_right, newdp, grp, flags, ep);
20231558Srgrimes			return;
20241558Srgrimes		} else
20251558Srgrimes			free((caddr_t)newdp);
20261558Srgrimes	} else {
20271558Srgrimes		dp = newdp;
20281558Srgrimes		dp->dp_left = (struct dirlist *)NULL;
20291558Srgrimes		*dpp = dp;
20301558Srgrimes	}
20311558Srgrimes	if (grp) {
20321558Srgrimes
20331558Srgrimes		/*
20341558Srgrimes		 * Hang all of the host(s) off of the directory point.
20351558Srgrimes		 */
20361558Srgrimes		do {
20371558Srgrimes			hp = get_ht();
20381558Srgrimes			hp->ht_grp = grp;
20391558Srgrimes			hp->ht_next = dp->dp_hosts;
20401558Srgrimes			dp->dp_hosts = hp;
2041240902Srmacklem			/* Save the security flavors list for this host set. */
2042240902Srmacklem			grp->gr_numsecflavors = ep->ex_numsecflavors;
2043240902Srmacklem			if (ep->ex_numsecflavors > 0)
2044240902Srmacklem				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2045240902Srmacklem				    sizeof(ep->ex_secflavors));
20461558Srgrimes			grp = grp->gr_next;
20471558Srgrimes		} while (grp);
20489336Sdfr	} else {
20491558Srgrimes		dp->dp_flag |= DP_DEFSET;
2050240902Srmacklem		/* Save the default security flavors list. */
2051240902Srmacklem		ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2052240902Srmacklem		if (ep->ex_numsecflavors > 0)
2053240902Srmacklem			memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2054240902Srmacklem			    sizeof(ep->ex_secflavors));
20559336Sdfr	}
20561558Srgrimes}
20571558Srgrimes
20581558Srgrimes/*
20591558Srgrimes * Search for a dirpath on the export point.
20601558Srgrimes */
2061285128Straszstatic struct dirlist *
2062216587Scharnierdirp_search(struct dirlist *dp, char *dirp)
20631558Srgrimes{
20641558Srgrimes	int cmp;
20651558Srgrimes
20661558Srgrimes	if (dp) {
206774462Salfred		cmp = strcmp(dp->dp_dirp, dirp);
20681558Srgrimes		if (cmp > 0)
206974462Salfred			return (dirp_search(dp->dp_left, dirp));
20701558Srgrimes		else if (cmp < 0)
207174462Salfred			return (dirp_search(dp->dp_right, dirp));
20721558Srgrimes		else
20731558Srgrimes			return (dp);
20741558Srgrimes	}
20751558Srgrimes	return (dp);
20761558Srgrimes}
20771558Srgrimes
20781558Srgrimes/*
20791558Srgrimes * Scan for a host match in a directory tree.
20801558Srgrimes */
2081285128Straszstatic int
2082216587Scharnierchk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2083240902Srmacklem	int *hostsetp, int *numsecflavors, int **secflavorsp)
20841558Srgrimes{
20851558Srgrimes	struct hostlist *hp;
20861558Srgrimes	struct grouplist *grp;
208774462Salfred	struct addrinfo *ai;
20881558Srgrimes
20891558Srgrimes	if (dp) {
20901558Srgrimes		if (dp->dp_flag & DP_DEFSET)
20919336Sdfr			*defsetp = dp->dp_flag;
20921558Srgrimes		hp = dp->dp_hosts;
20931558Srgrimes		while (hp) {
20941558Srgrimes			grp = hp->ht_grp;
20951558Srgrimes			switch (grp->gr_type) {
20961558Srgrimes			case GT_HOST:
209774462Salfred				ai = grp->gr_ptr.gt_addrinfo;
209874462Salfred				for (; ai; ai = ai->ai_next) {
209975801Siedowse					if (!sacmp(ai->ai_addr, saddr, NULL)) {
210074462Salfred						*hostsetp =
210174462Salfred						    (hp->ht_flag | DP_HOSTSET);
2102240902Srmacklem						if (numsecflavors != NULL) {
2103240902Srmacklem							*numsecflavors =
2104240902Srmacklem							    grp->gr_numsecflavors;
2105240902Srmacklem							*secflavorsp =
2106240902Srmacklem							    grp->gr_secflavors;
2107240902Srmacklem						}
210874462Salfred						return (1);
210974462Salfred					}
21109336Sdfr				}
211175801Siedowse				break;
21121558Srgrimes			case GT_NET:
211375801Siedowse				if (!sacmp(saddr, (struct sockaddr *)
211475801Siedowse				    &grp->gr_ptr.gt_net.nt_net,
211575801Siedowse				    (struct sockaddr *)
211675801Siedowse				    &grp->gr_ptr.gt_net.nt_mask)) {
211774462Salfred					*hostsetp = (hp->ht_flag | DP_HOSTSET);
2118240902Srmacklem					if (numsecflavors != NULL) {
2119240902Srmacklem						*numsecflavors =
2120240902Srmacklem						    grp->gr_numsecflavors;
2121240902Srmacklem						*secflavorsp =
2122240902Srmacklem						    grp->gr_secflavors;
2123240902Srmacklem					}
212474462Salfred					return (1);
212574462Salfred				}
212675801Siedowse				break;
212775801Siedowse			}
21281558Srgrimes			hp = hp->ht_next;
21291558Srgrimes		}
21301558Srgrimes	}
21311558Srgrimes	return (0);
21321558Srgrimes}
21331558Srgrimes
21341558Srgrimes/*
21351558Srgrimes * Scan tree for a host that matches the address.
21361558Srgrimes */
2137285128Straszstatic int
2138216587Scharnierscan_tree(struct dirlist *dp, struct sockaddr *saddr)
21391558Srgrimes{
21409336Sdfr	int defset, hostset;
21411558Srgrimes
21421558Srgrimes	if (dp) {
21431558Srgrimes		if (scan_tree(dp->dp_left, saddr))
21441558Srgrimes			return (1);
2145240902Srmacklem		if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
21461558Srgrimes			return (1);
21471558Srgrimes		if (scan_tree(dp->dp_right, saddr))
21481558Srgrimes			return (1);
21491558Srgrimes	}
21501558Srgrimes	return (0);
21511558Srgrimes}
21521558Srgrimes
21531558Srgrimes/*
21541558Srgrimes * Traverse the dirlist tree and free it up.
21551558Srgrimes */
2156285128Straszstatic void
2157216587Scharnierfree_dir(struct dirlist *dp)
21581558Srgrimes{
21591558Srgrimes
21601558Srgrimes	if (dp) {
21611558Srgrimes		free_dir(dp->dp_left);
21621558Srgrimes		free_dir(dp->dp_right);
21631558Srgrimes		free_host(dp->dp_hosts);
21641558Srgrimes		free((caddr_t)dp);
21651558Srgrimes	}
21661558Srgrimes}
21671558Srgrimes
21681558Srgrimes/*
2169184588Sdfr * Parse a colon separated list of security flavors
2170184588Sdfr */
2171285128Straszstatic int
2172216587Scharnierparsesec(char *seclist, struct exportlist *ep)
2173184588Sdfr{
2174184588Sdfr	char *cp, savedc;
2175184588Sdfr	int flavor;
2176184588Sdfr
2177184588Sdfr	ep->ex_numsecflavors = 0;
2178184588Sdfr	for (;;) {
2179184588Sdfr		cp = strchr(seclist, ':');
2180184588Sdfr		if (cp) {
2181184588Sdfr			savedc = *cp;
2182184588Sdfr			*cp = '\0';
2183184588Sdfr		}
2184184588Sdfr
2185184588Sdfr		if (!strcmp(seclist, "sys"))
2186184588Sdfr			flavor = AUTH_SYS;
2187184588Sdfr		else if (!strcmp(seclist, "krb5"))
2188184588Sdfr			flavor = RPCSEC_GSS_KRB5;
2189184588Sdfr		else if (!strcmp(seclist, "krb5i"))
2190184588Sdfr			flavor = RPCSEC_GSS_KRB5I;
2191184588Sdfr		else if (!strcmp(seclist, "krb5p"))
2192184588Sdfr			flavor = RPCSEC_GSS_KRB5P;
2193184588Sdfr		else {
2194184588Sdfr			if (cp)
2195184588Sdfr				*cp = savedc;
2196184588Sdfr			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2197184588Sdfr			return (1);
2198184588Sdfr		}
2199184588Sdfr		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2200184588Sdfr			if (cp)
2201184588Sdfr				*cp = savedc;
2202184588Sdfr			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2203184588Sdfr			return (1);
2204184588Sdfr		}
2205184588Sdfr		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2206184588Sdfr		ep->ex_numsecflavors++;
2207184588Sdfr		if (cp) {
2208184588Sdfr			*cp = savedc;
2209184588Sdfr			seclist = cp + 1;
2210184588Sdfr		} else {
2211184588Sdfr			break;
2212184588Sdfr		}
2213184588Sdfr	}
2214184588Sdfr	return (0);
2215184588Sdfr}
2216184588Sdfr
2217184588Sdfr/*
22181558Srgrimes * Parse the option string and update fields.
22191558Srgrimes * Option arguments may either be -<option>=<value> or
22201558Srgrimes * -<option> <value>
22211558Srgrimes */
2222285128Straszstatic int
2223216587Scharnierdo_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2224216587Scharnier	int *has_hostp, int *exflagsp, struct xucred *cr)
22251558Srgrimes{
22261558Srgrimes	char *cpoptarg, *cpoptend;
22271558Srgrimes	char *cp, *endcp, *cpopt, savedc, savedc2;
22281558Srgrimes	int allflag, usedarg;
22291558Srgrimes
223051968Salfred	savedc2 = '\0';
22311558Srgrimes	cpopt = *cpp;
22321558Srgrimes	cpopt++;
22331558Srgrimes	cp = *endcpp;
22341558Srgrimes	savedc = *cp;
22351558Srgrimes	*cp = '\0';
22361558Srgrimes	while (cpopt && *cpopt) {
22371558Srgrimes		allflag = 1;
22381558Srgrimes		usedarg = -2;
223937663Scharnier		if ((cpoptend = strchr(cpopt, ','))) {
22401558Srgrimes			*cpoptend++ = '\0';
224137663Scharnier			if ((cpoptarg = strchr(cpopt, '=')))
22421558Srgrimes				*cpoptarg++ = '\0';
22431558Srgrimes		} else {
224437663Scharnier			if ((cpoptarg = strchr(cpopt, '=')))
22451558Srgrimes				*cpoptarg++ = '\0';
22461558Srgrimes			else {
22471558Srgrimes				*cp = savedc;
22481558Srgrimes				nextfield(&cp, &endcp);
22491558Srgrimes				**endcpp = '\0';
22501558Srgrimes				if (endcp > cp && *cp != '-') {
22511558Srgrimes					cpoptarg = cp;
22521558Srgrimes					savedc2 = *endcp;
22531558Srgrimes					*endcp = '\0';
22541558Srgrimes					usedarg = 0;
22551558Srgrimes				}
22561558Srgrimes			}
22571558Srgrimes		}
22581558Srgrimes		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
22591558Srgrimes			*exflagsp |= MNT_EXRDONLY;
22601558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
22611558Srgrimes		    !(allflag = strcmp(cpopt, "mapall")) ||
22621558Srgrimes		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
22631558Srgrimes			usedarg++;
22641558Srgrimes			parsecred(cpoptarg, cr);
22651558Srgrimes			if (allflag == 0) {
22661558Srgrimes				*exflagsp |= MNT_EXPORTANON;
22671558Srgrimes				opt_flags |= OP_MAPALL;
22681558Srgrimes			} else
22691558Srgrimes				opt_flags |= OP_MAPROOT;
22701558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
227175801Siedowse		    !strcmp(cpopt, "m"))) {
22721558Srgrimes			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
227337663Scharnier				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
22741558Srgrimes				return (1);
22751558Srgrimes			}
22761558Srgrimes			usedarg++;
22771558Srgrimes			opt_flags |= OP_MASK;
22781558Srgrimes		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
22791558Srgrimes			!strcmp(cpopt, "n"))) {
228074462Salfred			if (strchr(cpoptarg, '/') != NULL) {
228174462Salfred				if (debug)
228274462Salfred					fprintf(stderr, "setting OP_MASKLEN\n");
228374462Salfred				opt_flags |= OP_MASKLEN;
228474462Salfred			}
22851558Srgrimes			if (grp->gr_type != GT_NULL) {
228637663Scharnier				syslog(LOG_ERR, "network/host conflict");
22871558Srgrimes				return (1);
22881558Srgrimes			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
228937663Scharnier				syslog(LOG_ERR, "bad net: %s", cpoptarg);
22901558Srgrimes				return (1);
22911558Srgrimes			}
22921558Srgrimes			grp->gr_type = GT_NET;
22931558Srgrimes			*has_hostp = 1;
22941558Srgrimes			usedarg++;
22951558Srgrimes			opt_flags |= OP_NET;
22961558Srgrimes		} else if (!strcmp(cpopt, "alldirs")) {
22971558Srgrimes			opt_flags |= OP_ALLDIRS;
229827447Sdfr		} else if (!strcmp(cpopt, "public")) {
229927447Sdfr			*exflagsp |= MNT_EXPUBLIC;
230027447Sdfr		} else if (!strcmp(cpopt, "webnfs")) {
230127447Sdfr			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
230227447Sdfr			opt_flags |= OP_MAPALL;
230327447Sdfr		} else if (cpoptarg && !strcmp(cpopt, "index")) {
230427447Sdfr			ep->ex_indexfile = strdup(cpoptarg);
2305100336Sjoerg		} else if (!strcmp(cpopt, "quiet")) {
2306100336Sjoerg			opt_flags |= OP_QUIET;
2307247034Spluknet		} else if (cpoptarg && !strcmp(cpopt, "sec")) {
2308184588Sdfr			if (parsesec(cpoptarg, ep))
2309184588Sdfr				return (1);
2310184588Sdfr			opt_flags |= OP_SEC;
2311184588Sdfr			usedarg++;
23121558Srgrimes		} else {
231337663Scharnier			syslog(LOG_ERR, "bad opt %s", cpopt);
23141558Srgrimes			return (1);
23151558Srgrimes		}
23161558Srgrimes		if (usedarg >= 0) {
23171558Srgrimes			*endcp = savedc2;
23181558Srgrimes			**endcpp = savedc;
23191558Srgrimes			if (usedarg > 0) {
23201558Srgrimes				*cpp = cp;
23211558Srgrimes				*endcpp = endcp;
23221558Srgrimes			}
23231558Srgrimes			return (0);
23241558Srgrimes		}
23251558Srgrimes		cpopt = cpoptend;
23261558Srgrimes	}
23271558Srgrimes	**endcpp = savedc;
23281558Srgrimes	return (0);
23291558Srgrimes}
23301558Srgrimes
23311558Srgrimes/*
23321558Srgrimes * Translate a character string to the corresponding list of network
23331558Srgrimes * addresses for a hostname.
23341558Srgrimes */
2335285128Straszstatic int
2336216587Scharnierget_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
23371558Srgrimes{
23387401Swpaul	struct grouplist *checkgrp;
233975635Siedowse	struct addrinfo *ai, *tai, hints;
234074462Salfred	int ecode;
234174462Salfred	char host[NI_MAXHOST];
23421558Srgrimes
234374462Salfred	if (grp->gr_type != GT_NULL) {
234474462Salfred		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
23451558Srgrimes		return (1);
23461558Srgrimes	}
234774462Salfred	memset(&hints, 0, sizeof hints);
234874462Salfred	hints.ai_flags = AI_CANONNAME;
234974462Salfred	hints.ai_protocol = IPPROTO_UDP;
235074462Salfred	ecode = getaddrinfo(cp, NULL, &hints, &ai);
235174462Salfred	if (ecode != 0) {
235275635Siedowse		syslog(LOG_ERR,"can't get address info for host %s", cp);
235374462Salfred		return 1;
235474462Salfred	}
235574462Salfred	grp->gr_ptr.gt_addrinfo = ai;
235674462Salfred	while (ai != NULL) {
235774462Salfred		if (ai->ai_canonname == NULL) {
235874462Salfred			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2359146187Sume			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
236074462Salfred				strlcpy(host, "?", sizeof(host));
236174462Salfred			ai->ai_canonname = strdup(host);
236274462Salfred			ai->ai_flags |= AI_CANONNAME;
236375641Siedowse		}
236474462Salfred		if (debug)
236575635Siedowse			fprintf(stderr, "got host %s\n", ai->ai_canonname);
236675635Siedowse		/*
236775635Siedowse		 * Sanity check: make sure we don't already have an entry
236875635Siedowse		 * for this host in the grouplist.
236975635Siedowse		 */
237075635Siedowse		for (checkgrp = tgrp; checkgrp != NULL;
237175635Siedowse		    checkgrp = checkgrp->gr_next) {
237275635Siedowse			if (checkgrp->gr_type != GT_HOST)
237375635Siedowse				continue;
237475635Siedowse			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
237575635Siedowse			    tai = tai->ai_next) {
237675801Siedowse				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
237775635Siedowse					continue;
237875635Siedowse				if (debug)
237975635Siedowse					fprintf(stderr,
238075635Siedowse					    "ignoring duplicate host %s\n",
238175635Siedowse					    ai->ai_canonname);
238275635Siedowse				grp->gr_type = GT_IGNORE;
238375635Siedowse				return (0);
238475635Siedowse			}
238575635Siedowse		}
238674462Salfred		ai = ai->ai_next;
23871558Srgrimes	}
238875635Siedowse	grp->gr_type = GT_HOST;
23891558Srgrimes	return (0);
23901558Srgrimes}
23911558Srgrimes
23921558Srgrimes/*
23931558Srgrimes * Free up an exports list component
23941558Srgrimes */
2395285128Straszstatic void
2396216587Scharnierfree_exp(struct exportlist *ep)
23971558Srgrimes{
23981558Srgrimes
23991558Srgrimes	if (ep->ex_defdir) {
24001558Srgrimes		free_host(ep->ex_defdir->dp_hosts);
24011558Srgrimes		free((caddr_t)ep->ex_defdir);
24021558Srgrimes	}
24031558Srgrimes	if (ep->ex_fsdir)
24041558Srgrimes		free(ep->ex_fsdir);
240527447Sdfr	if (ep->ex_indexfile)
240627447Sdfr		free(ep->ex_indexfile);
24071558Srgrimes	free_dir(ep->ex_dirl);
24081558Srgrimes	free((caddr_t)ep);
24091558Srgrimes}
24101558Srgrimes
24111558Srgrimes/*
24121558Srgrimes * Free hosts.
24131558Srgrimes */
2414285128Straszstatic void
2415216587Scharnierfree_host(struct hostlist *hp)
24161558Srgrimes{
24171558Srgrimes	struct hostlist *hp2;
24181558Srgrimes
24191558Srgrimes	while (hp) {
24201558Srgrimes		hp2 = hp;
24211558Srgrimes		hp = hp->ht_next;
24221558Srgrimes		free((caddr_t)hp2);
24231558Srgrimes	}
24241558Srgrimes}
24251558Srgrimes
2426285128Straszstatic struct hostlist *
2427216587Scharnierget_ht(void)
24281558Srgrimes{
24291558Srgrimes	struct hostlist *hp;
24301558Srgrimes
24311558Srgrimes	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
24321558Srgrimes	if (hp == (struct hostlist *)NULL)
24331558Srgrimes		out_of_mem();
24341558Srgrimes	hp->ht_next = (struct hostlist *)NULL;
24359336Sdfr	hp->ht_flag = 0;
24361558Srgrimes	return (hp);
24371558Srgrimes}
24381558Srgrimes
24391558Srgrimes/*
24401558Srgrimes * Out of memory, fatal
24411558Srgrimes */
2442285128Straszstatic void
2443216587Scharnierout_of_mem(void)
24441558Srgrimes{
24451558Srgrimes
244637663Scharnier	syslog(LOG_ERR, "out of memory");
24471558Srgrimes	exit(2);
24481558Srgrimes}
24491558Srgrimes
24501558Srgrimes/*
2451158857Srodrigc * Do the nmount() syscall with the update flag to push the export info into
24521558Srgrimes * the kernel.
24531558Srgrimes */
2454285128Straszstatic int
2455158857Srodrigcdo_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2456158857Srodrigc    struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
24571558Srgrimes{
245875841Siedowse	struct statfs fsb1;
245974462Salfred	struct addrinfo *ai;
2460282214Strasz	struct export_args *eap;
2461158857Srodrigc	char errmsg[255];
2462158857Srodrigc	char *cp;
24631558Srgrimes	int done;
2464158857Srodrigc	char savedc;
2465158857Srodrigc	struct iovec *iov;
2466184588Sdfr	int i, iovlen;
2467158857Srodrigc	int ret;
2468192934Srmacklem	struct nfsex_args nfsea;
24691558Srgrimes
2470282214Strasz	eap = &nfsea.export;
2471192934Srmacklem
2472158857Srodrigc	cp = NULL;
2473158857Srodrigc	savedc = '\0';
2474158857Srodrigc	iov = NULL;
2475158857Srodrigc	iovlen = 0;
2476158857Srodrigc	ret = 0;
247775801Siedowse
2478192934Srmacklem	bzero(eap, sizeof (struct export_args));
2479158857Srodrigc	bzero(errmsg, sizeof(errmsg));
2480192934Srmacklem	eap->ex_flags = exflags;
2481192934Srmacklem	eap->ex_anon = *anoncrp;
2482192934Srmacklem	eap->ex_indexfile = ep->ex_indexfile;
248375641Siedowse	if (grp->gr_type == GT_HOST)
248474462Salfred		ai = grp->gr_ptr.gt_addrinfo;
248575641Siedowse	else
248675641Siedowse		ai = NULL;
2487192934Srmacklem	eap->ex_numsecflavors = ep->ex_numsecflavors;
2488192934Srmacklem	for (i = 0; i < eap->ex_numsecflavors; i++)
2489192934Srmacklem		eap->ex_secflavors[i] = ep->ex_secflavors[i];
2490192934Srmacklem	if (eap->ex_numsecflavors == 0) {
2491192934Srmacklem		eap->ex_numsecflavors = 1;
2492192934Srmacklem		eap->ex_secflavors[0] = AUTH_SYS;
2493184588Sdfr	}
24941558Srgrimes	done = FALSE;
2495158857Srodrigc
2496192934Srmacklem	if (v4root_phase == 0) {
2497192934Srmacklem		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2498192934Srmacklem		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2499192934Srmacklem		build_iovec(&iov, &iovlen, "from", NULL, 0);
2500192934Srmacklem		build_iovec(&iov, &iovlen, "update", NULL, 0);
2501192934Srmacklem		build_iovec(&iov, &iovlen, "export", eap,
2502192934Srmacklem		    sizeof (struct export_args));
2503192934Srmacklem		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2504192934Srmacklem	}
2505158857Srodrigc
25061558Srgrimes	while (!done) {
25071558Srgrimes		switch (grp->gr_type) {
25081558Srgrimes		case GT_HOST:
250975641Siedowse			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
251074462Salfred				goto skip;
2511192934Srmacklem			eap->ex_addr = ai->ai_addr;
2512192934Srmacklem			eap->ex_addrlen = ai->ai_addrlen;
2513192934Srmacklem			eap->ex_masklen = 0;
25141558Srgrimes			break;
25151558Srgrimes		case GT_NET:
251675801Siedowse			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
251774462Salfred			    have_v6 == 0)
251874462Salfred				goto skip;
2519192934Srmacklem			eap->ex_addr =
252075801Siedowse			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2521192934Srmacklem			eap->ex_addrlen =
2522158857Srodrigc			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2523192934Srmacklem			eap->ex_mask =
252475801Siedowse			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2525192934Srmacklem			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
25261558Srgrimes			break;
252775641Siedowse		case GT_DEFAULT:
2528192934Srmacklem			eap->ex_addr = NULL;
2529192934Srmacklem			eap->ex_addrlen = 0;
2530192934Srmacklem			eap->ex_mask = NULL;
2531192934Srmacklem			eap->ex_masklen = 0;
253275641Siedowse			break;
25337401Swpaul		case GT_IGNORE:
2534158857Srodrigc			ret = 0;
2535158857Srodrigc			goto error_exit;
25367401Swpaul			break;
25371558Srgrimes		default:
253837663Scharnier			syslog(LOG_ERR, "bad grouptype");
25391558Srgrimes			if (cp)
25401558Srgrimes				*cp = savedc;
2541158857Srodrigc			ret = 1;
2542158857Srodrigc			goto error_exit;
2543298089Spfg		}
25441558Srgrimes
25451558Srgrimes		/*
2546192934Srmacklem		 * For V4:, use the nfssvc() syscall, instead of mount().
25471558Srgrimes		 */
2548192934Srmacklem		if (v4root_phase == 2) {
2549192934Srmacklem			nfsea.fspec = v4root_dirpath;
2550282214Strasz			if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
2551192934Srmacklem				syslog(LOG_ERR, "Exporting V4: failed");
2552192934Srmacklem				return (2);
2553158857Srodrigc			}
2554192934Srmacklem		} else {
2555192934Srmacklem			/*
2556192934Srmacklem			 * XXX:
2557192934Srmacklem			 * Maybe I should just use the fsb->f_mntonname path
2558192934Srmacklem			 * instead of looping back up the dirp to the mount
2559192934Srmacklem			 * point??
2560192934Srmacklem			 * Also, needs to know how to export all types of local
2561192934Srmacklem			 * exportable filesystems and not just "ufs".
2562192934Srmacklem			 */
2563192934Srmacklem			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2564192934Srmacklem			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2565192934Srmacklem			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2566192934Srmacklem			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2567192934Srmacklem			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2568192934Srmacklem			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2569270183Sbdrewery			errmsg[0] = '\0';
2570192934Srmacklem
2571192934Srmacklem			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2572192934Srmacklem				if (cp)
2573192934Srmacklem					*cp-- = savedc;
2574192934Srmacklem				else
2575192934Srmacklem					cp = dirp + dirplen - 1;
2576192934Srmacklem				if (opt_flags & OP_QUIET) {
2577192934Srmacklem					ret = 1;
2578192934Srmacklem					goto error_exit;
2579192934Srmacklem				}
2580192934Srmacklem				if (errno == EPERM) {
2581192934Srmacklem					if (debug)
2582239744Sdelphij						warnx("can't change attributes for %s: %s",
2583239744Sdelphij						    dirp, errmsg);
2584192934Srmacklem					syslog(LOG_ERR,
2585239744Sdelphij					   "can't change attributes for %s: %s",
2586239744Sdelphij					    dirp, errmsg);
2587192934Srmacklem					ret = 1;
2588192934Srmacklem					goto error_exit;
2589192934Srmacklem				}
2590192934Srmacklem				if (opt_flags & OP_ALLDIRS) {
2591192934Srmacklem					if (errno == EINVAL)
2592192934Srmacklem						syslog(LOG_ERR,
2593100336Sjoerg		"-alldirs requested but %s is not a filesystem mountpoint",
2594192934Srmacklem						    dirp);
2595192934Srmacklem					else
2596192934Srmacklem						syslog(LOG_ERR,
2597192934Srmacklem						    "could not remount %s: %m",
2598192934Srmacklem						    dirp);
2599192934Srmacklem					ret = 1;
2600192934Srmacklem					goto error_exit;
2601192934Srmacklem				}
2602192934Srmacklem				/* back up over the last component */
2603192934Srmacklem				while (*cp == '/' && cp > dirp)
2604192934Srmacklem					cp--;
2605192934Srmacklem				while (*(cp - 1) != '/' && cp > dirp)
2606192934Srmacklem					cp--;
2607192934Srmacklem				if (cp == dirp) {
2608192934Srmacklem					if (debug)
2609192934Srmacklem						warnx("mnt unsucc");
2610192934Srmacklem					syslog(LOG_ERR, "can't export %s %s",
2611192934Srmacklem					    dirp, errmsg);
2612192934Srmacklem					ret = 1;
2613192934Srmacklem					goto error_exit;
2614192934Srmacklem				}
2615192934Srmacklem				savedc = *cp;
2616192934Srmacklem				*cp = '\0';
2617192934Srmacklem				/*
2618192934Srmacklem				 * Check that we're still on the same
2619192934Srmacklem				 * filesystem.
2620192934Srmacklem				 */
2621192934Srmacklem				if (statfs(dirp, &fsb1) != 0 ||
2622192934Srmacklem				    bcmp(&fsb1.f_fsid, &fsb->f_fsid,
2623192934Srmacklem				    sizeof (fsb1.f_fsid)) != 0) {
2624192934Srmacklem					*cp = savedc;
2625100336Sjoerg					syslog(LOG_ERR,
2626192934Srmacklem					    "can't export %s %s", dirp,
2627192934Srmacklem					    errmsg);
2628192934Srmacklem					ret = 1;
2629192934Srmacklem					goto error_exit;
2630192934Srmacklem				}
26311558Srgrimes			}
26321558Srgrimes		}
2633192934Srmacklem
2634192934Srmacklem		/*
2635192934Srmacklem		 * For the experimental server:
2636192934Srmacklem		 * If this is the public directory, get the file handle
2637192934Srmacklem		 * and load it into the kernel via the nfssvc() syscall.
2638192934Srmacklem		 */
2639282214Strasz		if ((exflags & MNT_EXPUBLIC) != 0) {
2640192934Srmacklem			fhandle_t fh;
2641192934Srmacklem			char *public_name;
2642192934Srmacklem
2643192934Srmacklem			if (eap->ex_indexfile != NULL)
2644192934Srmacklem				public_name = eap->ex_indexfile;
2645192934Srmacklem			else
2646192934Srmacklem				public_name = dirp;
2647192934Srmacklem			if (getfh(public_name, &fh) < 0)
2648192934Srmacklem				syslog(LOG_ERR,
2649192934Srmacklem				    "Can't get public fh for %s", public_name);
2650192934Srmacklem			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
2651192934Srmacklem				syslog(LOG_ERR,
2652192934Srmacklem				    "Can't set public fh for %s", public_name);
2653192934Srmacklem			else
2654192934Srmacklem				has_publicfh = 1;
2655192934Srmacklem		}
265674462Salfredskip:
265775641Siedowse		if (ai != NULL)
265874462Salfred			ai = ai->ai_next;
265975641Siedowse		if (ai == NULL)
26601558Srgrimes			done = TRUE;
26611558Srgrimes	}
26621558Srgrimes	if (cp)
26631558Srgrimes		*cp = savedc;
2664158857Srodrigcerror_exit:
2665158857Srodrigc	/* free strings allocated by strdup() in getmntopts.c */
2666158857Srodrigc	if (iov != NULL) {
2667158857Srodrigc		free(iov[0].iov_base); /* fstype */
2668158857Srodrigc		free(iov[2].iov_base); /* fspath */
2669158857Srodrigc		free(iov[4].iov_base); /* from */
2670158857Srodrigc		free(iov[6].iov_base); /* update */
2671158857Srodrigc		free(iov[8].iov_base); /* export */
2672158857Srodrigc		free(iov[10].iov_base); /* errmsg */
2673158857Srodrigc
2674158857Srodrigc		/* free iov, allocated by realloc() */
2675158857Srodrigc		free(iov);
2676158857Srodrigc	}
2677158857Srodrigc	return (ret);
26781558Srgrimes}
26791558Srgrimes
26801558Srgrimes/*
26811558Srgrimes * Translate a net address.
268275801Siedowse *
268375801Siedowse * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
26841558Srgrimes */
2685285128Straszstatic int
2686216587Scharnierget_net(char *cp, struct netmsk *net, int maskflg)
26871558Srgrimes{
268875861Siedowse	struct netent *np = NULL;
268974462Salfred	char *name, *p, *prefp;
269075801Siedowse	struct sockaddr_in sin;
269175861Siedowse	struct sockaddr *sa = NULL;
269274462Salfred	struct addrinfo hints, *ai = NULL;
269374462Salfred	char netname[NI_MAXHOST];
269474462Salfred	long preflen;
26951558Srgrimes
269675635Siedowse	p = prefp = NULL;
269774462Salfred	if ((opt_flags & OP_MASKLEN) && !maskflg) {
269874462Salfred		p = strchr(cp, '/');
269974462Salfred		*p = '\0';
270074462Salfred		prefp = p + 1;
270174462Salfred	}
270274462Salfred
270375861Siedowse	/*
270475861Siedowse	 * Check for a numeric address first. We wish to avoid
270575861Siedowse	 * possible DNS lookups in getnetbyname().
270675861Siedowse	 */
270775861Siedowse	if (isxdigit(*cp) || *cp == ':') {
270874462Salfred		memset(&hints, 0, sizeof hints);
270975801Siedowse		/* Ensure the mask and the network have the same family. */
271075801Siedowse		if (maskflg && (opt_flags & OP_NET))
271175801Siedowse			hints.ai_family = net->nt_net.ss_family;
271275801Siedowse		else if (!maskflg && (opt_flags & OP_HAVEMASK))
271375801Siedowse			hints.ai_family = net->nt_mask.ss_family;
271475801Siedowse		else
271575801Siedowse			hints.ai_family = AF_UNSPEC;
271674462Salfred		hints.ai_flags = AI_NUMERICHOST;
271775861Siedowse		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
271875861Siedowse			sa = ai->ai_addr;
271975861Siedowse		if (sa != NULL && ai->ai_family == AF_INET) {
272074462Salfred			/*
272175801Siedowse			 * The address in `cp' is really a network address, so
272275801Siedowse			 * use inet_network() to re-interpret this correctly.
272375801Siedowse			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
272474462Salfred			 */
272575801Siedowse			bzero(&sin, sizeof sin);
272674462Salfred			sin.sin_family = AF_INET;
272774462Salfred			sin.sin_len = sizeof sin;
272875801Siedowse			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
272974462Salfred			if (debug)
273075801Siedowse				fprintf(stderr, "get_net: v4 addr %s\n",
273175801Siedowse				    inet_ntoa(sin.sin_addr));
273274462Salfred			sa = (struct sockaddr *)&sin;
273375861Siedowse		}
273475861Siedowse	}
273575861Siedowse	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
273675861Siedowse		bzero(&sin, sizeof sin);
273775861Siedowse		sin.sin_family = AF_INET;
273875861Siedowse		sin.sin_len = sizeof sin;
273975861Siedowse		sin.sin_addr = inet_makeaddr(np->n_net, 0);
274075861Siedowse		sa = (struct sockaddr *)&sin;
274175861Siedowse	}
274275861Siedowse	if (sa == NULL)
274374462Salfred		goto fail;
274425318Spst
274575801Siedowse	if (maskflg) {
274675801Siedowse		/* The specified sockaddr is a mask. */
274775801Siedowse		if (checkmask(sa) != 0)
274875801Siedowse			goto fail;
274975801Siedowse		bcopy(sa, &net->nt_mask, sa->sa_len);
275075801Siedowse		opt_flags |= OP_HAVEMASK;
275175801Siedowse	} else {
275275801Siedowse		/* The specified sockaddr is a network address. */
275375801Siedowse		bcopy(sa, &net->nt_net, sa->sa_len);
275474462Salfred
275575801Siedowse		/* Get a network name for the export list. */
275675801Siedowse		if (np) {
275775801Siedowse			name = np->n_name;
275875801Siedowse		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2759146187Sume		   NULL, 0, NI_NUMERICHOST) == 0) {
276075801Siedowse			name = netname;
276175801Siedowse		} else {
276275801Siedowse			goto fail;
276375801Siedowse		}
276475801Siedowse		if ((net->nt_name = strdup(name)) == NULL)
276575801Siedowse			out_of_mem();
276675801Siedowse
276775801Siedowse		/*
276875801Siedowse		 * Extract a mask from either a "/<masklen>" suffix, or
276975801Siedowse		 * from the class of an IPv4 address.
277075801Siedowse		 */
277174462Salfred		if (opt_flags & OP_MASKLEN) {
277274462Salfred			preflen = strtol(prefp, NULL, 10);
277375801Siedowse			if (preflen < 0L || preflen == LONG_MAX)
277474462Salfred				goto fail;
277575801Siedowse			bcopy(sa, &net->nt_mask, sa->sa_len);
277675801Siedowse			if (makemask(&net->nt_mask, (int)preflen) != 0)
277775801Siedowse				goto fail;
277875801Siedowse			opt_flags |= OP_HAVEMASK;
277974462Salfred			*p = '/';
278075801Siedowse		} else if (sa->sa_family == AF_INET &&
278175801Siedowse		    (opt_flags & OP_MASK) == 0) {
278275801Siedowse			in_addr_t addr;
278374462Salfred
278475801Siedowse			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
278575801Siedowse			if (IN_CLASSA(addr))
278675801Siedowse				preflen = 8;
278775801Siedowse			else if (IN_CLASSB(addr))
278875801Siedowse				preflen = 16;
278975801Siedowse			else if (IN_CLASSC(addr))
279075801Siedowse				preflen = 24;
279175801Siedowse			else if (IN_CLASSD(addr))
279275801Siedowse				preflen = 28;
279375801Siedowse			else
279475801Siedowse				preflen = 32;	/* XXX */
279575801Siedowse
279675801Siedowse			bcopy(sa, &net->nt_mask, sa->sa_len);
279775801Siedowse			makemask(&net->nt_mask, (int)preflen);
279875801Siedowse			opt_flags |= OP_HAVEMASK;
279974462Salfred		}
280074462Salfred	}
280174462Salfred
280274462Salfred	if (ai)
280374462Salfred		freeaddrinfo(ai);
280474462Salfred	return 0;
280574462Salfred
280674462Salfredfail:
280774462Salfred	if (ai)
280874462Salfred		freeaddrinfo(ai);
280974462Salfred	return 1;
28101558Srgrimes}
28111558Srgrimes
28121558Srgrimes/*
28131558Srgrimes * Parse out the next white space separated field
28141558Srgrimes */
2815285128Straszstatic void
2816216587Scharniernextfield(char **cp, char **endcp)
28171558Srgrimes{
28181558Srgrimes	char *p;
28191558Srgrimes
28201558Srgrimes	p = *cp;
28211558Srgrimes	while (*p == ' ' || *p == '\t')
28221558Srgrimes		p++;
28231558Srgrimes	if (*p == '\n' || *p == '\0')
28241558Srgrimes		*cp = *endcp = p;
28251558Srgrimes	else {
28261558Srgrimes		*cp = p++;
28271558Srgrimes		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
28281558Srgrimes			p++;
28291558Srgrimes		*endcp = p;
28301558Srgrimes	}
28311558Srgrimes}
28321558Srgrimes
28331558Srgrimes/*
28341558Srgrimes * Get an exports file line. Skip over blank lines and handle line
28351558Srgrimes * continuations.
28361558Srgrimes */
2837285128Straszstatic int
2838216587Scharnierget_line(void)
28391558Srgrimes{
28401558Srgrimes	char *p, *cp;
284196622Siedowse	size_t len;
28421558Srgrimes	int totlen, cont_line;
28431558Srgrimes
28441558Srgrimes	/*
28451558Srgrimes	 * Loop around ignoring blank lines and getting all continuation lines.
28461558Srgrimes	 */
28471558Srgrimes	p = line;
28481558Srgrimes	totlen = 0;
28491558Srgrimes	do {
285096622Siedowse		if ((p = fgetln(exp_file, &len)) == NULL)
28511558Srgrimes			return (0);
28521558Srgrimes		cp = p + len - 1;
28531558Srgrimes		cont_line = 0;
28541558Srgrimes		while (cp >= p &&
28551558Srgrimes		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
28561558Srgrimes			if (*cp == '\\')
28571558Srgrimes				cont_line = 1;
28581558Srgrimes			cp--;
28591558Srgrimes			len--;
28601558Srgrimes		}
286179117Sdd		if (cont_line) {
286279117Sdd			*++cp = ' ';
286379117Sdd			len++;
286479117Sdd		}
286596622Siedowse		if (linesize < len + totlen + 1) {
286696622Siedowse			linesize = len + totlen + 1;
286796622Siedowse			line = realloc(line, linesize);
286896622Siedowse			if (line == NULL)
286996622Siedowse				out_of_mem();
28701558Srgrimes		}
287196622Siedowse		memcpy(line + totlen, p, len);
287296622Siedowse		totlen += len;
287396622Siedowse		line[totlen] = '\0';
28741558Srgrimes	} while (totlen == 0 || cont_line);
28751558Srgrimes	return (1);
28761558Srgrimes}
28771558Srgrimes
28781558Srgrimes/*
28791558Srgrimes * Parse a description of a credential.
28801558Srgrimes */
2881285128Straszstatic void
2882216587Scharnierparsecred(char *namelist, struct xucred *cr)
28831558Srgrimes{
28841558Srgrimes	char *name;
28851558Srgrimes	int cnt;
28861558Srgrimes	char *names;
28871558Srgrimes	struct passwd *pw;
28881558Srgrimes	struct group *gr;
2889194498Sbrooks	gid_t groups[XU_NGROUPS + 1];
2890136051Sstefanf	int ngroups;
28911558Srgrimes
289291354Sdd	cr->cr_version = XUCRED_VERSION;
28931558Srgrimes	/*
289437663Scharnier	 * Set up the unprivileged user.
28951558Srgrimes	 */
28961558Srgrimes	cr->cr_uid = -2;
28971558Srgrimes	cr->cr_groups[0] = -2;
28981558Srgrimes	cr->cr_ngroups = 1;
28991558Srgrimes	/*
29001558Srgrimes	 * Get the user's password table entry.
29011558Srgrimes	 */
2902293305Sjpaetzel	names = strsep_quote(&namelist, " \t\n");
29031558Srgrimes	name = strsep(&names, ":");
2904293305Sjpaetzel	/* Bug?  name could be NULL here */
29051558Srgrimes	if (isdigit(*name) || *name == '-')
29061558Srgrimes		pw = getpwuid(atoi(name));
29071558Srgrimes	else
29081558Srgrimes		pw = getpwnam(name);
29091558Srgrimes	/*
29101558Srgrimes	 * Credentials specified as those of a user.
29111558Srgrimes	 */
29121558Srgrimes	if (names == NULL) {
29131558Srgrimes		if (pw == NULL) {
291437663Scharnier			syslog(LOG_ERR, "unknown user: %s", name);
29151558Srgrimes			return;
29161558Srgrimes		}
29171558Srgrimes		cr->cr_uid = pw->pw_uid;
2918194498Sbrooks		ngroups = XU_NGROUPS + 1;
29191558Srgrimes		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
292037663Scharnier			syslog(LOG_ERR, "too many groups");
29211558Srgrimes		/*
2922136051Sstefanf		 * Compress out duplicate.
29231558Srgrimes		 */
29241558Srgrimes		cr->cr_ngroups = ngroups - 1;
29251558Srgrimes		cr->cr_groups[0] = groups[0];
29261558Srgrimes		for (cnt = 2; cnt < ngroups; cnt++)
29271558Srgrimes			cr->cr_groups[cnt - 1] = groups[cnt];
29281558Srgrimes		return;
29291558Srgrimes	}
29301558Srgrimes	/*
29311558Srgrimes	 * Explicit credential specified as a colon separated list:
29321558Srgrimes	 *	uid:gid:gid:...
29331558Srgrimes	 */
29341558Srgrimes	if (pw != NULL)
29351558Srgrimes		cr->cr_uid = pw->pw_uid;
29361558Srgrimes	else if (isdigit(*name) || *name == '-')
29371558Srgrimes		cr->cr_uid = atoi(name);
29381558Srgrimes	else {
293937663Scharnier		syslog(LOG_ERR, "unknown user: %s", name);
29401558Srgrimes		return;
29411558Srgrimes	}
29421558Srgrimes	cr->cr_ngroups = 0;
2943194498Sbrooks	while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
29441558Srgrimes		name = strsep(&names, ":");
29451558Srgrimes		if (isdigit(*name) || *name == '-') {
29461558Srgrimes			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
29471558Srgrimes		} else {
29481558Srgrimes			if ((gr = getgrnam(name)) == NULL) {
294937663Scharnier				syslog(LOG_ERR, "unknown group: %s", name);
29501558Srgrimes				continue;
29511558Srgrimes			}
29521558Srgrimes			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
29531558Srgrimes		}
29541558Srgrimes	}
2955194498Sbrooks	if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
295637663Scharnier		syslog(LOG_ERR, "too many groups");
29571558Srgrimes}
29581558Srgrimes
2959194880Sdfr#define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
29601558Srgrimes/*
29611558Srgrimes * Routines that maintain the remote mounttab
29621558Srgrimes */
2963285128Straszstatic void
2964216587Scharnierget_mountlist(void)
29651558Srgrimes{
29661558Srgrimes	struct mountlist *mlp, **mlpp;
296723681Speter	char *host, *dirp, *cp;
29681558Srgrimes	char str[STRSIZ];
29691558Srgrimes	FILE *mlfile;
29701558Srgrimes
29711558Srgrimes	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
297253117Sbillf		if (errno == ENOENT)
297353117Sbillf			return;
297453117Sbillf		else {
297553117Sbillf			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
297653117Sbillf			return;
297753117Sbillf		}
29781558Srgrimes	}
29791558Srgrimes	mlpp = &mlhead;
29801558Srgrimes	while (fgets(str, STRSIZ, mlfile) != NULL) {
298123681Speter		cp = str;
298223681Speter		host = strsep(&cp, " \t\n");
298323681Speter		dirp = strsep(&cp, " \t\n");
298423681Speter		if (host == NULL || dirp == NULL)
29851558Srgrimes			continue;
29861558Srgrimes		mlp = (struct mountlist *)malloc(sizeof (*mlp));
298737663Scharnier		if (mlp == (struct mountlist *)NULL)
298837663Scharnier			out_of_mem();
2989194880Sdfr		strncpy(mlp->ml_host, host, MNTNAMLEN);
2990194880Sdfr		mlp->ml_host[MNTNAMLEN] = '\0';
2991194880Sdfr		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
2992194880Sdfr		mlp->ml_dirp[MNTPATHLEN] = '\0';
29931558Srgrimes		mlp->ml_next = (struct mountlist *)NULL;
29941558Srgrimes		*mlpp = mlp;
29951558Srgrimes		mlpp = &mlp->ml_next;
29961558Srgrimes	}
29971558Srgrimes	fclose(mlfile);
29981558Srgrimes}
29991558Srgrimes
3000285128Straszstatic void
300175635Siedowsedel_mlist(char *hostp, char *dirp)
30021558Srgrimes{
30031558Srgrimes	struct mountlist *mlp, **mlpp;
30041558Srgrimes	struct mountlist *mlp2;
30051558Srgrimes	FILE *mlfile;
30061558Srgrimes	int fnd = 0;
30071558Srgrimes
30081558Srgrimes	mlpp = &mlhead;
30091558Srgrimes	mlp = mlhead;
30101558Srgrimes	while (mlp) {
30111558Srgrimes		if (!strcmp(mlp->ml_host, hostp) &&
30121558Srgrimes		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
30131558Srgrimes			fnd = 1;
30141558Srgrimes			mlp2 = mlp;
30151558Srgrimes			*mlpp = mlp = mlp->ml_next;
30161558Srgrimes			free((caddr_t)mlp2);
30171558Srgrimes		} else {
30181558Srgrimes			mlpp = &mlp->ml_next;
30191558Srgrimes			mlp = mlp->ml_next;
30201558Srgrimes		}
30211558Srgrimes	}
30221558Srgrimes	if (fnd) {
30231558Srgrimes		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
302437663Scharnier			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
30251558Srgrimes			return;
30261558Srgrimes		}
30271558Srgrimes		mlp = mlhead;
30281558Srgrimes		while (mlp) {
30291558Srgrimes			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
30301558Srgrimes			mlp = mlp->ml_next;
30311558Srgrimes		}
30321558Srgrimes		fclose(mlfile);
30331558Srgrimes	}
30341558Srgrimes}
30351558Srgrimes
3036285128Straszstatic void
3037216587Scharnieradd_mlist(char *hostp, char *dirp)
30381558Srgrimes{
30391558Srgrimes	struct mountlist *mlp, **mlpp;
30401558Srgrimes	FILE *mlfile;
30411558Srgrimes
30421558Srgrimes	mlpp = &mlhead;
30431558Srgrimes	mlp = mlhead;
30441558Srgrimes	while (mlp) {
30451558Srgrimes		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
30461558Srgrimes			return;
30471558Srgrimes		mlpp = &mlp->ml_next;
30481558Srgrimes		mlp = mlp->ml_next;
30491558Srgrimes	}
30501558Srgrimes	mlp = (struct mountlist *)malloc(sizeof (*mlp));
305137663Scharnier	if (mlp == (struct mountlist *)NULL)
305237663Scharnier		out_of_mem();
3053194880Sdfr	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3054194880Sdfr	mlp->ml_host[MNTNAMLEN] = '\0';
3055194880Sdfr	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3056194880Sdfr	mlp->ml_dirp[MNTPATHLEN] = '\0';
30571558Srgrimes	mlp->ml_next = (struct mountlist *)NULL;
30581558Srgrimes	*mlpp = mlp;
30591558Srgrimes	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
306037663Scharnier		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
30611558Srgrimes		return;
30621558Srgrimes	}
30631558Srgrimes	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
30641558Srgrimes	fclose(mlfile);
30651558Srgrimes}
30661558Srgrimes
30671558Srgrimes/*
30681558Srgrimes * Free up a group list.
30691558Srgrimes */
3070285128Straszstatic void
3071216587Scharnierfree_grp(struct grouplist *grp)
30721558Srgrimes{
30731558Srgrimes	if (grp->gr_type == GT_HOST) {
307474462Salfred		if (grp->gr_ptr.gt_addrinfo != NULL)
307574462Salfred			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
30761558Srgrimes	} else if (grp->gr_type == GT_NET) {
30771558Srgrimes		if (grp->gr_ptr.gt_net.nt_name)
30781558Srgrimes			free(grp->gr_ptr.gt_net.nt_name);
30791558Srgrimes	}
30801558Srgrimes	free((caddr_t)grp);
30811558Srgrimes}
30821558Srgrimes
30831558Srgrimes#ifdef DEBUG
3084285128Straszstatic void
30851558SrgrimesSYSLOG(int pri, const char *fmt, ...)
30861558Srgrimes{
30871558Srgrimes	va_list ap;
30881558Srgrimes
30891558Srgrimes	va_start(ap, fmt);
30901558Srgrimes	vfprintf(stderr, fmt, ap);
30911558Srgrimes	va_end(ap);
30921558Srgrimes}
30931558Srgrimes#endif /* DEBUG */
30941558Srgrimes
30951558Srgrimes/*
30961558Srgrimes * Check options for consistency.
30971558Srgrimes */
3098285128Straszstatic int
3099216587Scharniercheck_options(struct dirlist *dp)
31001558Srgrimes{
31011558Srgrimes
3102192934Srmacklem	if (v4root_phase == 0 && dp == NULL)
31031558Srgrimes	    return (1);
310483653Speter	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
310583653Speter	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
31061558Srgrimes	    return (1);
31071558Srgrimes	}
31081558Srgrimes	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
310975801Siedowse		syslog(LOG_ERR, "-mask requires -network");
311075801Siedowse		return (1);
31111558Srgrimes	}
311275801Siedowse	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
311375801Siedowse		syslog(LOG_ERR, "-network requires mask specification");
311475801Siedowse		return (1);
311575801Siedowse	}
311675801Siedowse	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
311775801Siedowse		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
311875801Siedowse		return (1);
311975801Siedowse	}
3120192934Srmacklem	if (v4root_phase > 0 &&
3121192934Srmacklem	    (opt_flags &
3122192934Srmacklem	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3123192934Srmacklem	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3124192934Srmacklem	    return (1);
3125192934Srmacklem	}
3126207689Srmacklem	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3127207689Srmacklem	    syslog(LOG_ERR, "-alldirs has multiple directories");
3128207689Srmacklem	    return (1);
3129207689Srmacklem	}
31301558Srgrimes	return (0);
31311558Srgrimes}
31321558Srgrimes
31331558Srgrimes/*
31341558Srgrimes * Check an absolute directory path for any symbolic links. Return true
31351558Srgrimes */
3136285128Straszstatic int
3137216587Scharniercheck_dirpath(char *dirp)
31381558Srgrimes{
31391558Srgrimes	char *cp;
31401558Srgrimes	int ret = 1;
31411558Srgrimes	struct stat sb;
31421558Srgrimes
31431558Srgrimes	cp = dirp + 1;
31441558Srgrimes	while (*cp && ret) {
31451558Srgrimes		if (*cp == '/') {
31461558Srgrimes			*cp = '\0';
31479336Sdfr			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
31481558Srgrimes				ret = 0;
31491558Srgrimes			*cp = '/';
31501558Srgrimes		}
31511558Srgrimes		cp++;
31521558Srgrimes	}
31539336Sdfr	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
31541558Srgrimes		ret = 0;
31551558Srgrimes	return (ret);
31561558Srgrimes}
31579336Sdfr
315875801Siedowse/*
315975801Siedowse * Make a netmask according to the specified prefix length. The ss_family
316075801Siedowse * and other non-address fields must be initialised before calling this.
316175801Siedowse */
3162285128Straszstatic int
316375801Siedowsemakemask(struct sockaddr_storage *ssp, int bitlen)
316474462Salfred{
316575801Siedowse	u_char *p;
316675801Siedowse	int bits, i, len;
316774462Salfred
316875801Siedowse	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
316975801Siedowse		return (-1);
3170103949Smike	if (bitlen > len * CHAR_BIT)
317175801Siedowse		return (-1);
317274462Salfred
317375801Siedowse	for (i = 0; i < len; i++) {
3174298912Saraujo		bits = MIN(CHAR_BIT, bitlen);
3175219125Sru		*p++ = (u_char)~0 << (CHAR_BIT - bits);
317675801Siedowse		bitlen -= bits;
317774462Salfred	}
317875801Siedowse	return 0;
317974462Salfred}
318074462Salfred
318175801Siedowse/*
318275801Siedowse * Check that the sockaddr is a valid netmask. Returns 0 if the mask
318375801Siedowse * is acceptable (i.e. of the form 1...10....0).
318475801Siedowse */
3185285128Straszstatic int
318675801Siedowsecheckmask(struct sockaddr *sa)
318774462Salfred{
318875801Siedowse	u_char *mask;
318975801Siedowse	int i, len;
319074462Salfred
319175801Siedowse	if ((mask = sa_rawaddr(sa, &len)) == NULL)
319275801Siedowse		return (-1);
319375801Siedowse
319475801Siedowse	for (i = 0; i < len; i++)
319575801Siedowse		if (mask[i] != 0xff)
319675801Siedowse			break;
319775801Siedowse	if (i < len) {
319875801Siedowse		if (~mask[i] & (u_char)(~mask[i] + 1))
319975801Siedowse			return (-1);
320075801Siedowse		i++;
320174462Salfred	}
320275801Siedowse	for (; i < len; i++)
320375801Siedowse		if (mask[i] != 0)
320475801Siedowse			return (-1);
320575801Siedowse	return (0);
320674462Salfred}
320774462Salfred
320875801Siedowse/*
320975801Siedowse * Compare two sockaddrs according to a specified mask. Return zero if
321075801Siedowse * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3211228990Suqs * If samask is NULL, perform a full comparison.
321275801Siedowse */
3213285128Straszstatic int
321475801Siedowsesacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
321574462Salfred{
321675801Siedowse	unsigned char *p1, *p2, *mask;
321775801Siedowse	int len, i;
321874462Salfred
321975801Siedowse	if (sa1->sa_family != sa2->sa_family ||
322075801Siedowse	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
322175801Siedowse	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
322275801Siedowse		return (1);
322375801Siedowse
322475801Siedowse	switch (sa1->sa_family) {
322574462Salfred	case AF_INET6:
322675801Siedowse		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
322775801Siedowse		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
322875801Siedowse			return (1);
322974462Salfred		break;
323074462Salfred	}
323174462Salfred
323275801Siedowse	/* Simple binary comparison if no mask specified. */
323375801Siedowse	if (samask == NULL)
323475801Siedowse		return (memcmp(p1, p2, len));
323574462Salfred
323675801Siedowse	/* Set up the mask, and do a mask-based comparison. */
323775801Siedowse	if (sa1->sa_family != samask->sa_family ||
323875801Siedowse	    (mask = sa_rawaddr(samask, NULL)) == NULL)
323975801Siedowse		return (1);
324074462Salfred
324175801Siedowse	for (i = 0; i < len; i++)
324275801Siedowse		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
324375801Siedowse			return (1);
324475801Siedowse	return (0);
324574462Salfred}
324674462Salfred
324775801Siedowse/*
324875801Siedowse * Return a pointer to the part of the sockaddr that contains the
324975801Siedowse * raw address, and set *nbytes to its length in bytes. Returns
325075801Siedowse * NULL if the address family is unknown.
325175801Siedowse */
3252285128Straszstatic void *
325375801Siedowsesa_rawaddr(struct sockaddr *sa, int *nbytes) {
325475801Siedowse	void *p;
325574462Salfred	int len;
325674462Salfred
325775801Siedowse	switch (sa->sa_family) {
325874462Salfred	case AF_INET:
325975801Siedowse		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
326075801Siedowse		p = &((struct sockaddr_in *)sa)->sin_addr;
326174462Salfred		break;
326274462Salfred	case AF_INET6:
326375801Siedowse		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
326475801Siedowse		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
326574462Salfred		break;
326674462Salfred	default:
326775801Siedowse		p = NULL;
326875801Siedowse		len = 0;
326974462Salfred	}
327074462Salfred
327175801Siedowse	if (nbytes != NULL)
327275801Siedowse		*nbytes = len;
327375801Siedowse	return (p);
327474462Salfred}
327574462Salfred
3276285128Straszstatic void
3277216587Scharnierhuphandler(int sig __unused)
327875754Siedowse{
3279285128Strasz
328075754Siedowse	got_sighup = 1;
328175754Siedowse}
328275754Siedowse
3283285128Straszstatic void
3284285128Straszterminate(int sig __unused)
328574462Salfred{
3286149433Spjd	pidfile_remove(pfh);
3287194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3288194880Sdfr	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
328974462Salfred	exit (0);
329074462Salfred}
3291