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