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