xref: /freebsd/usr.sbin/lpr/lpd/lpd.c (revision 99e8005137088aafb1350e23b113d69b01b0820f)
1 /*
2  * Copyright (c) 1983, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by the University of
17  *	California, Berkeley and its contributors.
18  * 4. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #ifndef lint
36 static const char copyright[] =
37 "@(#) Copyright (c) 1983, 1993, 1994\n\
38 	The Regents of the University of California.  All rights reserved.\n";
39 #endif /* not lint */
40 
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)lpd.c	8.7 (Berkeley) 5/10/95";
44 #endif
45 static const char rcsid[] =
46   "$FreeBSD$";
47 #endif /* not lint */
48 
49 /*
50  * lpd -- line printer daemon.
51  *
52  * Listen for a connection and perform the requested operation.
53  * Operations are:
54  *	\1printer\n
55  *		check the queue for jobs and print any found.
56  *	\2printer\n
57  *		receive a job from another machine and queue it.
58  *	\3printer [users ...] [jobs ...]\n
59  *		return the current state of the queue (short form).
60  *	\4printer [users ...] [jobs ...]\n
61  *		return the current state of the queue (long form).
62  *	\5printer person [users ...] [jobs ...]\n
63  *		remove jobs from the queue.
64  *
65  * Strategy to maintain protected spooling area:
66  *	1. Spooling area is writable only by daemon and spooling group
67  *	2. lpr runs setuid root and setgrp spooling group; it uses
68  *	   root to access any file it wants (verifying things before
69  *	   with an access call) and group id to know how it should
70  *	   set up ownership of files in the spooling area.
71  *	3. Files in spooling area are owned by root, group spooling
72  *	   group, with mode 660.
73  *	4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
74  *	   access files and printer.  Users can't get to anything
75  *	   w/o help of lpq and lprm programs.
76  */
77 
78 #include <sys/param.h>
79 #include <sys/wait.h>
80 #include <sys/types.h>
81 #include <sys/socket.h>
82 #include <sys/un.h>
83 #include <sys/stat.h>
84 #include <sys/file.h>
85 #include <netinet/in.h>
86 #include <arpa/inet.h>
87 
88 #include <netdb.h>
89 #include <unistd.h>
90 #include <syslog.h>
91 #include <signal.h>
92 #include <err.h>
93 #include <errno.h>
94 #include <fcntl.h>
95 #include <dirent.h>
96 #include <stdio.h>
97 #include <stdlib.h>
98 #include <string.h>
99 #include <sysexits.h>
100 #include <ctype.h>
101 #include "lp.h"
102 #include "lp.local.h"
103 #include "pathnames.h"
104 #include "extern.h"
105 
106 int	lflag;				/* log requests flag */
107 int	pflag;				/* no incoming port flag */
108 int	from_remote;			/* from remote socket */
109 
110 int		  main __P((int, char **));
111 static void       reapchild __P((int));
112 static void       mcleanup __P((int));
113 static void       doit __P((void));
114 static void       startup __P((void));
115 static void       chkhost __P((struct sockaddr *));
116 static int	  ckqueue __P((struct printer *));
117 static void	  usage __P((void));
118 static int	  *socksetup __P((int, int));
119 
120 /* XXX from libc/net/rcmd.c */
121 extern int __ivaliduser_sa __P((FILE *, struct sockaddr *, socklen_t,
122 				const char *, const char *));
123 
124 uid_t	uid, euid;
125 
126 int
127 main(argc, argv)
128 	int argc;
129 	char **argv;
130 {
131 	int errs, f, funix, *finet, fromlen, i, options, socket_debug;
132 	fd_set defreadfds;
133 	struct sockaddr_un un, fromunix;
134 	struct sockaddr_storage frominet;
135 	int lfd;
136 	sigset_t omask, nmask;
137 	struct servent *sp, serv;
138 	int inet_flag = 0, inet6_flag = 0;
139 
140 	euid = geteuid();	/* these shouldn't be different */
141 	uid = getuid();
142 	socket_debug = 0;
143 	gethostname(host, sizeof(host));
144 
145 	name = "lpd";
146 
147 	if (euid != 0)
148 		errx(EX_NOPERM,"must run as root");
149 
150 	errs = 0;
151 	while ((i = getopt(argc, argv, "dlp46")) != -1)
152 		switch (i) {
153 		case 'd':
154 			socket_debug++;
155 			break;
156 		case 'l':
157 			lflag++;
158 			break;
159 		case 'p':
160 			pflag++;
161 			break;
162 		case '4':
163 			family = PF_INET;
164 			inet_flag++;
165 			break;
166 		case '6':
167 #ifdef INET6
168 			family = PF_INET6;
169 			inet6_flag++;
170 #else
171 			errx(EX_USAGE, "lpd compiled sans INET6 (IPv6 support)");
172 #endif
173 			break;
174 		default:
175 			errs++;
176 		}
177 	if (inet_flag && inet6_flag)
178 		family = PF_UNSPEC;
179 	argc -= optind;
180 	argv += optind;
181 	if (errs)
182 		usage();
183 
184 	if (argc == 1) {
185 		if ((i = atoi(argv[0])) == 0)
186 			usage();
187 		if (i < 0 || i > USHRT_MAX)
188 			errx(EX_USAGE, "port # %d is invalid", i);
189 
190 		serv.s_port = htons(i);
191 		sp = &serv;
192 		argc--;
193 	} else {
194 		sp = getservbyname("printer", "tcp");
195 		if (sp == NULL)
196 			errx(EX_OSFILE, "printer/tcp: unknown service");
197 	}
198 
199 	if (argc != 0)
200 		usage();
201 
202 	/*
203 	 * We run chkprintcap right away to catch any errors and blat them
204 	 * to stderr while we still have it open, rather than sending them
205 	 * to syslog and leaving the user wondering why lpd started and
206 	 * then stopped.  There should probably be a command-line flag to
207 	 * ignore errors from chkprintcap.
208 	 */
209 	{
210 		pid_t pid;
211 		int status;
212 		pid = fork();
213 		if (pid < 0) {
214 			err(EX_OSERR, "cannot fork");
215 		} else if (pid == 0) {	/* child */
216 			execl(_PATH_CHKPRINTCAP, _PATH_CHKPRINTCAP, (char *)0);
217 			err(EX_OSERR, "cannot execute %s", _PATH_CHKPRINTCAP);
218 		}
219 		if (waitpid(pid, &status, 0) < 0) {
220 			err(EX_OSERR, "cannot wait");
221 		}
222 		if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
223 			errx(EX_OSFILE, "%d errors in printcap file, exiting",
224 			     WEXITSTATUS(status));
225 	}
226 
227 #ifndef DEBUG
228 	/*
229 	 * Set up standard environment by detaching from the parent.
230 	 */
231 	daemon(0, 0);
232 #endif
233 
234 	openlog("lpd", LOG_PID, LOG_LPR);
235 	syslog(LOG_INFO, "lpd startup: logging=%d%s", lflag,
236 	    socket_debug ? " dbg" : "");
237 	(void) umask(0);
238 	/*
239 	 * NB: This depends on O_NONBLOCK semantics doing the right thing;
240 	 * i.e., applying only to the O_EXLOCK and not to the rest of the
241 	 * open/creation.  As of 1997-12-02, this is the case for commonly-
242 	 * used filesystems.  There are other places in this code which
243 	 * make the same assumption.
244 	 */
245 	lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
246 		   LOCK_FILE_MODE);
247 	if (lfd < 0) {
248 		if (errno == EWOULDBLOCK)	/* active deamon present */
249 			exit(0);
250 		syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
251 		exit(1);
252 	}
253 	fcntl(lfd, F_SETFL, 0);	/* turn off non-blocking mode */
254 	ftruncate(lfd, 0);
255 	/*
256 	 * write process id for others to know
257 	 */
258 	sprintf(line, "%u\n", getpid());
259 	f = strlen(line);
260 	if (write(lfd, line, f) != f) {
261 		syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
262 		exit(1);
263 	}
264 	signal(SIGCHLD, reapchild);
265 	/*
266 	 * Restart all the printers.
267 	 */
268 	startup();
269 	(void) unlink(_PATH_SOCKETNAME);
270 	funix = socket(AF_UNIX, SOCK_STREAM, 0);
271 	if (funix < 0) {
272 		syslog(LOG_ERR, "socket: %m");
273 		exit(1);
274 	}
275 
276 	sigemptyset(&nmask);
277 	sigaddset(&nmask, SIGHUP);
278 	sigaddset(&nmask, SIGINT);
279 	sigaddset(&nmask, SIGQUIT);
280 	sigaddset(&nmask, SIGTERM);
281 	sigprocmask(SIG_BLOCK, &nmask, &omask);
282 
283 	(void) umask(07);
284 	signal(SIGHUP, mcleanup);
285 	signal(SIGINT, mcleanup);
286 	signal(SIGQUIT, mcleanup);
287 	signal(SIGTERM, mcleanup);
288 	memset(&un, 0, sizeof(un));
289 	un.sun_family = AF_UNIX;
290 	strcpy(un.sun_path, _PATH_SOCKETNAME);
291 #ifndef SUN_LEN
292 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
293 #endif
294 	if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) {
295 		syslog(LOG_ERR, "ubind: %m");
296 		exit(1);
297 	}
298 	(void) umask(0);
299 	sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
300 	FD_ZERO(&defreadfds);
301 	FD_SET(funix, &defreadfds);
302 	listen(funix, 5);
303 	if (pflag == 0) {
304 		options = SO_REUSEADDR;
305 		if (socket_debug)
306 			options |= SO_DEBUG;
307 		finet = socksetup(family, options);
308 	} else
309 		finet = NULL;	/* pretend we couldn't open TCP socket. */
310 	if (finet) {
311 		for (i = 1; i <= *finet; i++) {
312 			FD_SET(finet[i], &defreadfds);
313 			listen(finet[i], 5);
314 		}
315 	}
316 	/*
317 	 * Main loop: accept, do a request, continue.
318 	 */
319 	memset(&frominet, 0, sizeof(frominet));
320 	memset(&fromunix, 0, sizeof(fromunix));
321 	if (lflag)
322 		syslog(LOG_INFO, "lpd startup: ready to accept requests");
323 	/*
324 	 * XXX - should be redone for multi-protocol
325 	 */
326 	for (;;) {
327 		int domain = -1, nfds, s = -1;
328 		fd_set readfds;
329 
330 		FD_COPY(&defreadfds, &readfds);
331 		nfds = select(20, &readfds, 0, 0, 0);
332 		if (nfds <= 0) {
333 			if (nfds < 0 && errno != EINTR)
334 				syslog(LOG_WARNING, "select: %m");
335 			continue;
336 		}
337 		domain = 0;			/* avoid compile-time warning */
338 		s = 0;				/* avoid compile-time warning */
339 		if (FD_ISSET(funix, &readfds)) {
340 			domain = AF_UNIX, fromlen = sizeof(fromunix);
341 			s = accept(funix,
342 			    (struct sockaddr *)&fromunix, &fromlen);
343  		} else {
344                         for (i = 1; i <= *finet; i++)
345 				if (FD_ISSET(finet[i], &readfds)) {
346 					domain = AF_INET;
347 					fromlen = sizeof(frominet);
348 					s = accept(finet[i],
349 					    (struct sockaddr *)&frominet,
350 					    &fromlen);
351 				}
352 		}
353 		if (s < 0) {
354 			if (errno != EINTR)
355 				syslog(LOG_WARNING, "accept: %m");
356 			continue;
357 		}
358 		if (fork() == 0) {
359 			signal(SIGCHLD, SIG_IGN);
360 			signal(SIGHUP, SIG_IGN);
361 			signal(SIGINT, SIG_IGN);
362 			signal(SIGQUIT, SIG_IGN);
363 			signal(SIGTERM, SIG_IGN);
364 			(void) close(funix);
365 			if (pflag == 0 && finet) {
366                         	for (i = 1; i <= *finet; i++)
367 					(void)close(finet[i]);
368 			}
369 			dup2(s, 1);
370 			(void) close(s);
371 			if (domain == AF_INET) {
372 				/* for both AF_INET and AF_INET6 */
373 				from_remote = 1;
374  				chkhost((struct sockaddr *)&frominet);
375 			} else
376 				from_remote = 0;
377 			doit();
378 			exit(0);
379 		}
380 		(void) close(s);
381 	}
382 }
383 
384 static void
385 reapchild(signo)
386 	int signo;
387 {
388 	union wait status;
389 
390 	while (wait3((int *)&status, WNOHANG, 0) > 0)
391 		;
392 }
393 
394 static void
395 mcleanup(signo)
396 	int signo;
397 {
398 	/*
399 	 * XXX syslog(3) is not signal-safe.
400 	 */
401 	if (lflag) {
402 		if (signo)
403 			syslog(LOG_INFO, "exiting on signal %d", signo);
404 		else
405 			syslog(LOG_INFO, "exiting");
406 	}
407 	unlink(_PATH_SOCKETNAME);
408 	exit(0);
409 }
410 
411 /*
412  * Stuff for handling job specifications
413  */
414 char	*user[MAXUSERS];	/* users to process */
415 int	users;			/* # of users in user array */
416 int	requ[MAXREQUESTS];	/* job number of spool entries */
417 int	requests;		/* # of spool requests */
418 char	*person;		/* name of person doing lprm */
419 
420 char	fromb[MAXHOSTNAMELEN];	/* buffer for client's machine name */
421 char	cbuf[BUFSIZ];		/* command line buffer */
422 char	*cmdnames[] = {
423 	"null",
424 	"printjob",
425 	"recvjob",
426 	"displayq short",
427 	"displayq long",
428 	"rmjob"
429 };
430 
431 static void
432 doit()
433 {
434 	char *cp, *printer;
435 	int n;
436 	int status;
437 	struct printer myprinter, *pp = &myprinter;
438 
439 	init_printer(&myprinter);
440 
441 	for (;;) {
442 		cp = cbuf;
443 		do {
444 			if (cp >= &cbuf[sizeof(cbuf) - 1])
445 				fatal(0, "Command line too long");
446 			if ((n = read(1, cp, 1)) != 1) {
447 				if (n < 0)
448 					fatal(0, "Lost connection");
449 				return;
450 			}
451 		} while (*cp++ != '\n');
452 		*--cp = '\0';
453 		cp = cbuf;
454 		if (lflag) {
455 			if (*cp >= '\1' && *cp <= '\5')
456 				syslog(LOG_INFO, "%s requests %s %s",
457 					from, cmdnames[(u_char)*cp], cp+1);
458 			else
459 				syslog(LOG_INFO, "bad request (%d) from %s",
460 					*cp, from);
461 		}
462 		switch (*cp++) {
463 		case CMD_CHECK_QUE: /* check the queue, print any jobs there */
464 			startprinting(cp);
465 			break;
466 		case CMD_TAKE_THIS: /* receive files to be queued */
467 			if (!from_remote) {
468 				syslog(LOG_INFO, "illegal request (%d)", *cp);
469 				exit(1);
470 			}
471 			recvjob(cp);
472 			break;
473 		case CMD_SHOWQ_SHORT: /* display the queue (short form) */
474 		case CMD_SHOWQ_LONG: /* display the queue (long form) */
475 			/* XXX - this all needs to be redone. */
476 			printer = cp;
477 			while (*cp) {
478 				if (*cp != ' ') {
479 					cp++;
480 					continue;
481 				}
482 				*cp++ = '\0';
483 				while (isspace(*cp))
484 					cp++;
485 				if (*cp == '\0')
486 					break;
487 				if (isdigit(*cp)) {
488 					if (requests >= MAXREQUESTS)
489 						fatal(0, "Too many requests");
490 					requ[requests++] = atoi(cp);
491 				} else {
492 					if (users >= MAXUSERS)
493 						fatal(0, "Too many users");
494 					user[users++] = cp;
495 				}
496 			}
497 			status = getprintcap(printer, pp);
498 			if (status < 0)
499 				fatal(pp, pcaperr(status));
500 			displayq(pp, cbuf[0] == CMD_SHOWQ_LONG);
501 			exit(0);
502 		case CMD_RMJOB:	/* remove a job from the queue */
503 			if (!from_remote) {
504 				syslog(LOG_INFO, "illegal request (%d)", *cp);
505 				exit(1);
506 			}
507 			printer = cp;
508 			while (*cp && *cp != ' ')
509 				cp++;
510 			if (!*cp)
511 				break;
512 			*cp++ = '\0';
513 			person = cp;
514 			while (*cp) {
515 				if (*cp != ' ') {
516 					cp++;
517 					continue;
518 				}
519 				*cp++ = '\0';
520 				while (isspace(*cp))
521 					cp++;
522 				if (*cp == '\0')
523 					break;
524 				if (isdigit(*cp)) {
525 					if (requests >= MAXREQUESTS)
526 						fatal(0, "Too many requests");
527 					requ[requests++] = atoi(cp);
528 				} else {
529 					if (users >= MAXUSERS)
530 						fatal(0, "Too many users");
531 					user[users++] = cp;
532 				}
533 			}
534 			rmjob(printer);
535 			break;
536 		}
537 		fatal(0, "Illegal service request");
538 	}
539 }
540 
541 /*
542  * Make a pass through the printcap database and start printing any
543  * files left from the last time the machine went down.
544  */
545 static void
546 startup()
547 {
548 	int pid, status, more;
549 	struct printer myprinter, *pp = &myprinter;
550 
551 	more = firstprinter(pp, &status);
552 	if (status)
553 		goto errloop;
554 	while (more) {
555 		if (ckqueue(pp) <= 0) {
556 			goto next;
557 		}
558 		if (lflag)
559 			syslog(LOG_INFO, "lpd startup: work for %s",
560 			    pp->printer);
561 		if ((pid = fork()) < 0) {
562 			syslog(LOG_WARNING, "lpd startup: cannot fork for %s",
563 			    pp->printer);
564 			mcleanup(0);
565 		}
566 		if (pid == 0) {
567 			lastprinter();
568 			printjob(pp);
569 			/* NOTREACHED */
570 		}
571 		do {
572 next:
573 			more = nextprinter(pp, &status);
574 errloop:
575 			if (status)
576 				syslog(LOG_WARNING,
577 				    "lpd startup: printcap entry for %s has errors, skipping",
578 				    pp->printer ? pp->printer : "<???>");
579 		} while (more && status);
580 	}
581 }
582 
583 /*
584  * Make sure there's some work to do before forking off a child
585  */
586 static int
587 ckqueue(pp)
588 	struct printer *pp;
589 {
590 	register struct dirent *d;
591 	DIR *dirp;
592 	char *spooldir;
593 
594 	spooldir = pp->spool_dir;
595 	if ((dirp = opendir(spooldir)) == NULL)
596 		return (-1);
597 	while ((d = readdir(dirp)) != NULL) {
598 		if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
599 			continue;	/* daemon control files only */
600 		closedir(dirp);
601 		return (1);		/* found something */
602 	}
603 	closedir(dirp);
604 	return (0);
605 }
606 
607 #define DUMMY ":nobody::"
608 
609 /*
610  * Check to see if the from host has access to the line printer.
611  */
612 static void
613 chkhost(f)
614 	struct sockaddr *f;
615 {
616 	struct addrinfo hints, *res, *r;
617 	register FILE *hostf;
618 	int first = 1;
619 	int good = 0;
620 	char host[NI_MAXHOST], ip[NI_MAXHOST];
621 	char serv[NI_MAXSERV];
622 	int error, addrlen;
623 	caddr_t addr;
624 
625 	error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv),
626 			    NI_NUMERICSERV);
627 	if (error || atoi(serv) >= IPPORT_RESERVED)
628 		fatal(0, "Malformed from address");
629 
630 	/* Need real hostname for temporary filenames */
631 	error = getnameinfo(f, f->sa_len, host, sizeof(host), NULL, 0,
632 			    NI_NAMEREQD);
633 	if (error) {
634 		error = getnameinfo(f, f->sa_len, host, sizeof(host), NULL, 0,
635 				    NI_NUMERICHOST | NI_WITHSCOPEID);
636 		if (error)
637 			fatal(0, "Host name for your address unknown");
638 		else
639 			fatal(0, "Host name for your address (%s) unknown",
640 			      host);
641 	}
642 
643 	(void)strncpy(fromb, host, sizeof(fromb) - 1);
644 	fromb[sizeof(fromb) - 1] = '\0';
645 	from = fromb;
646 
647 	/* Need address in stringform for comparison (no DNS lookup here) */
648 	error = getnameinfo(f, f->sa_len, host, sizeof(host), NULL, 0,
649 			    NI_NUMERICHOST | NI_WITHSCOPEID);
650 	if (error)
651 		fatal(0, "Cannot print address");
652 	strncpy(from_ip, host, NI_MAXHOST);
653 	from_ip[sizeof(from_ip) - 1] = '\0';
654 
655 	/* Reject numeric addresses */
656 	memset(&hints, 0, sizeof(hints));
657 	hints.ai_family = family;
658 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
659 	hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
660 	if (getaddrinfo(fromb, NULL, &hints, &res) == 0) {
661 		freeaddrinfo(res);
662 		fatal(0, "reverse lookup results in non-FQDN %s", fromb);
663 	}
664 
665 	/* Check for spoof, ala rlogind */
666 	memset(&hints, 0, sizeof(hints));
667 	hints.ai_family = family;
668 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
669 	error = getaddrinfo(fromb, NULL, &hints, &res);
670 	if (error) {
671 		fatal(0, "hostname for your address (%s) unknown: %s", from_ip,
672 		      gai_strerror(error));
673 	}
674 	good = 0;
675 	for (r = res; good == 0 && r; r = r->ai_next) {
676 		error = getnameinfo(r->ai_addr, r->ai_addrlen, ip, sizeof(ip),
677 				    NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
678 		if (!error && !strcmp(from_ip, ip))
679 			good = 1;
680 	}
681 	if (res)
682 		freeaddrinfo(res);
683 	if (good == 0)
684 		fatal(0, "address for your hostname (%s) not matched",
685 		    from_ip);
686 
687 	hostf = fopen(_PATH_HOSTSEQUIV, "r");
688 again:
689 	if (hostf) {
690 		if (__ivaliduser_sa(hostf, f, f->sa_len, DUMMY, DUMMY) == 0) {
691 			(void) fclose(hostf);
692 			return;
693 		}
694 		(void) fclose(hostf);
695 	}
696 	if (first == 1) {
697 		first = 0;
698 		hostf = fopen(_PATH_HOSTSLPD, "r");
699 		goto again;
700 	}
701 	fatal(0, "Your host does not have line printer access");
702 	/*NOTREACHED*/
703 }
704 
705 /* setup server socket for specified address family */
706 /* if af is PF_UNSPEC more than one socket may be returned */
707 /* the returned list is dynamically allocated, so caller needs to free it */
708 static int *
709 socksetup(af, options)
710         int af, options;
711 {
712 	struct addrinfo hints, *res, *r;
713 	int error, maxs, *s, *socks;
714 	const int on = 1;
715 
716 	memset(&hints, 0, sizeof(hints));
717 	hints.ai_flags = AI_PASSIVE;
718 	hints.ai_family = af;
719 	hints.ai_socktype = SOCK_STREAM;
720 	error = getaddrinfo(NULL, "printer", &hints, &res);
721 	if (error) {
722 		syslog(LOG_ERR, "%s", gai_strerror(error));
723 		mcleanup(0);
724 	}
725 
726 	/* Count max number of sockets we may open */
727 	for (maxs = 0, r = res; r; r = r->ai_next, maxs++)
728 		;
729 	socks = malloc((maxs + 1) * sizeof(int));
730 	if (!socks) {
731 		syslog(LOG_ERR, "couldn't allocate memory for sockets");
732 		mcleanup(0);
733 	}
734 
735 	*socks = 0;   /* num of sockets counter at start of array */
736 	s = socks + 1;
737 	for (r = res; r; r = r->ai_next) {
738 		*s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
739 		if (*s < 0) {
740 			syslog(LOG_DEBUG, "socket(): %m");
741 			continue;
742 		}
743 		if (options & SO_REUSEADDR)
744 			if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, &on,
745 				       sizeof(on)) < 0) {
746 				syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m");
747 				close(*s);
748 				continue;
749 			}
750 		if (options & SO_DEBUG)
751 			if (setsockopt(*s, SOL_SOCKET, SO_DEBUG,
752 				       &on, sizeof(on)) < 0) {
753 				syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
754 				close(*s);
755 				continue;
756 			}
757 #ifdef IPV6_BINDV6ONLY
758 		if (r->ai_family == AF_INET6) {
759 			if (setsockopt(*s, IPPROTO_IPV6, IPV6_BINDV6ONLY,
760 				       &on, sizeof(on)) < 0) {
761 				syslog(LOG_ERR,
762 				       "setsockopt (IPV6_BINDV6ONLY): %m");
763 				close(*s);
764 				continue;
765 			}
766 		}
767 #endif
768 		if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
769 			syslog(LOG_DEBUG, "bind(): %m");
770 			close(*s);
771 			continue;
772 		}
773 		(*socks)++;
774 		s++;
775 	}
776 
777 	if (res)
778 		freeaddrinfo(res);
779 
780 	if (*socks == 0) {
781 		syslog(LOG_ERR, "Couldn't bind to any socket");
782 		free(socks);
783 		mcleanup(0);
784 	}
785 	return(socks);
786 }
787 
788 static void
789 usage()
790 {
791 #ifdef INET6
792 	fprintf(stderr, "usage: lpd [-dlp46] [port#]\n");
793 #else
794 	fprintf(stderr, "usage: lpd [-dlp] [port#]\n");
795 #endif
796 	exit(EX_USAGE);
797 }
798