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