xref: /dragonfly/usr.sbin/lpr/lpd/lpd.c (revision 36a3d1d6)
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  * @(#) Copyright (c) 1983, 1993, 1994 The Regents of the University of California.  All rights reserved.
35  * @(#)lpd.c	8.7 (Berkeley) 5/10/95
36  * $FreeBSD: src/usr.sbin/lpr/lpd/lpd.c,v 1.12.2.22 2002/06/30 04:09:11 gad Exp $
37  * $DragonFly: src/usr.sbin/lpr/lpd/lpd.c,v 1.5 2004/12/18 22:48:03 swildner Exp $
38  */
39 
40 /*
41  * lpd -- line printer daemon.
42  *
43  * Listen for a connection and perform the requested operation.
44  * Operations are:
45  *	\1printer\n
46  *		check the queue for jobs and print any found.
47  *	\2printer\n
48  *		receive a job from another machine and queue it.
49  *	\3printer [users ...] [jobs ...]\n
50  *		return the current state of the queue (short form).
51  *	\4printer [users ...] [jobs ...]\n
52  *		return the current state of the queue (long form).
53  *	\5printer person [users ...] [jobs ...]\n
54  *		remove jobs from the queue.
55  *
56  * Strategy to maintain protected spooling area:
57  *	1. Spooling area is writable only by daemon and spooling group
58  *	2. lpr runs setuid root and setgrp spooling group; it uses
59  *	   root to access any file it wants (verifying things before
60  *	   with an access call) and group id to know how it should
61  *	   set up ownership of files in the spooling area.
62  *	3. Files in spooling area are owned by root, group spooling
63  *	   group, with mode 660.
64  *	4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
65  *	   access files and printer.  Users can't get to anything
66  *	   w/o help of lpq and lprm programs.
67  */
68 
69 #include <sys/param.h>
70 #include <sys/wait.h>
71 #include <sys/types.h>
72 #include <sys/socket.h>
73 #include <sys/un.h>
74 #include <sys/stat.h>
75 #include <sys/file.h>
76 #include <netinet/in.h>
77 #include <arpa/inet.h>
78 
79 #include <netdb.h>
80 #include <unistd.h>
81 #include <syslog.h>
82 #include <signal.h>
83 #include <err.h>
84 #include <errno.h>
85 #include <fcntl.h>
86 #include <dirent.h>
87 #include <stdio.h>
88 #include <stdlib.h>
89 #include <string.h>
90 #include <sysexits.h>
91 #include <ctype.h>
92 #include "lp.h"
93 #include "lp.local.h"
94 #include "pathnames.h"
95 #include "extern.h"
96 
97 int	lflag;				/* log requests flag */
98 int	sflag;				/* no incoming port flag */
99 int	from_remote;			/* from remote socket */
100 
101 int		 main(int argc, char **_argv);
102 static void	 reapchild(int _signo);
103 static void	 mcleanup(int _signo);
104 static void	 doit(void);
105 static void	 startup(void);
106 static void	 chkhost(struct sockaddr *_f, int _ch_opts);
107 static int	 ckqueue(struct printer *_pp);
108 static void	 fhosterr(int _ch_opts, char *_sysmsg, char *_usermsg);
109 static int	*socksetup(int _af, int _debuglvl);
110 static void	 usage(void);
111 
112 /* XXX from libc/net/rcmd.c */
113 extern int __ivaliduser_sa(FILE *, struct sockaddr *, socklen_t,
114 				const char *, const char *);
115 
116 uid_t	uid, euid;
117 
118 #define LPD_NOPORTCHK	0001		/* skip reserved-port check */
119 #define LPD_LOGCONNERR	0002		/* (sys)log connection errors */
120 #define LPD_ADDFROMLINE	0004		/* just used for fhosterr() */
121 
122 int
123 main(int argc, char **argv)
124 {
125 	int ch_options, errs, f, funix, *finet, i, lfd, socket_debug;
126 	fd_set defreadfds;
127 	struct sockaddr_un un, fromunix;
128 	struct sockaddr_storage frominet;
129 	socklen_t fromlen;
130 	sigset_t omask, nmask;
131 	struct servent *sp, serv;
132 	int inet_flag = 0, inet6_flag = 0;
133 
134 	euid = geteuid();	/* these shouldn't be different */
135 	uid = getuid();
136 
137 	ch_options = 0;
138 	socket_debug = 0;
139 	gethostname(local_host, sizeof(local_host));
140 
141 	progname = "lpd";
142 
143 	if (euid != 0)
144 		errx(EX_NOPERM,"must run as root");
145 
146 	errs = 0;
147 	while ((i = getopt(argc, argv, "cdlpswW46")) != -1)
148 		switch (i) {
149 		case 'c':
150 			/* log all kinds of connection-errors to syslog */
151 			ch_options |= LPD_LOGCONNERR;
152 			break;
153 		case 'd':
154 			socket_debug++;
155 			break;
156 		case 'l':
157 			lflag++;
158 			break;
159 		case 'p':		/* letter initially used for -s */
160 			/*
161 			 * This will probably be removed with 5.0-release.
162 			 */
163 			/* FALLTHROUGH */
164 		case 's':		/* secure (no inet) */
165 			sflag++;
166 			break;
167 		case 'w':		/* netbsd uses -w for maxwait */
168 			/*
169 			 * This will be removed after the release of 4.4, as
170 			 * it conflicts with -w in netbsd's lpd.  For now it
171 			 * is just a warning, so we won't suddenly break lpd
172 			 * for anyone who is currently using the option.
173 			 */
174 			syslog(LOG_WARNING,
175 			    "NOTE: the -w option has been renamed -W");
176 			syslog(LOG_WARNING,
177 			    "NOTE: please change your lpd config to use -W");
178 			/* FALLTHROUGH */
179 		case 'W':
180 			/* allow connections coming from a non-reserved port */
181 			/* (done by some lpr-implementations for MS-Windows) */
182 			ch_options |= LPD_NOPORTCHK;
183 			break;
184 		case '4':
185 			family = PF_INET;
186 			inet_flag++;
187 			break;
188 		case '6':
189 #ifdef INET6
190 			family = PF_INET6;
191 			inet6_flag++;
192 #else
193 			errx(EX_USAGE, "lpd compiled sans INET6 (IPv6 support)");
194 #endif
195 			break;
196 		/*
197 		 * The following options are not in FreeBSD (yet?), but are
198 		 * listed here to "reserve" them, because the option-letters
199 		 * are used by either NetBSD or OpenBSD (as of July 2001).
200 		 */
201 		case 'b':		/* set bind-addr */
202 		case 'n':		/* set max num of children */
203 		case 'r':		/* allow 'of' for remote ptrs */
204 					/* ...[not needed in freebsd] */
205 			/* FALLTHROUGH */
206 		default:
207 			errs++;
208 		}
209 	if (inet_flag && inet6_flag)
210 		family = PF_UNSPEC;
211 	argc -= optind;
212 	argv += optind;
213 	if (errs)
214 		usage();
215 
216 	if (argc == 1) {
217 		if ((i = atoi(argv[0])) == 0)
218 			usage();
219 		if (i < 0 || i > USHRT_MAX)
220 			errx(EX_USAGE, "port # %d is invalid", i);
221 
222 		serv.s_port = htons(i);
223 		sp = &serv;
224 		argc--;
225 	} else {
226 		sp = getservbyname("printer", "tcp");
227 		if (sp == NULL)
228 			errx(EX_OSFILE, "printer/tcp: unknown service");
229 	}
230 
231 	if (argc != 0)
232 		usage();
233 
234 	/*
235 	 * We run chkprintcap right away to catch any errors and blat them
236 	 * to stderr while we still have it open, rather than sending them
237 	 * to syslog and leaving the user wondering why lpd started and
238 	 * then stopped.  There should probably be a command-line flag to
239 	 * ignore errors from chkprintcap.
240 	 */
241 	{
242 		pid_t pid;
243 		int status;
244 		pid = fork();
245 		if (pid < 0) {
246 			err(EX_OSERR, "cannot fork");
247 		} else if (pid == 0) {	/* child */
248 			execl(_PATH_CHKPRINTCAP, _PATH_CHKPRINTCAP, NULL);
249 			err(EX_OSERR, "cannot execute %s", _PATH_CHKPRINTCAP);
250 		}
251 		if (waitpid(pid, &status, 0) < 0) {
252 			err(EX_OSERR, "cannot wait");
253 		}
254 		if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
255 			errx(EX_OSFILE, "%d errors in printcap file, exiting",
256 			     WEXITSTATUS(status));
257 	}
258 
259 #ifndef DEBUG
260 	/*
261 	 * Set up standard environment by detaching from the parent.
262 	 */
263 	daemon(0, 0);
264 #endif
265 
266 	openlog("lpd", LOG_PID, LOG_LPR);
267 	syslog(LOG_INFO, "lpd startup: logging=%d%s%s", lflag,
268 	    socket_debug ? " dbg" : "", sflag ? " net-secure" : "");
269 	umask(0);
270 	/*
271 	 * NB: This depends on O_NONBLOCK semantics doing the right thing;
272 	 * i.e., applying only to the O_EXLOCK and not to the rest of the
273 	 * open/creation.  As of 1997-12-02, this is the case for commonly-
274 	 * used filesystems.  There are other places in this code which
275 	 * make the same assumption.
276 	 */
277 	lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
278 		   LOCK_FILE_MODE);
279 	if (lfd < 0) {
280 		if (errno == EWOULDBLOCK)	/* active daemon present */
281 			exit(0);
282 		syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
283 		exit(1);
284 	}
285 	fcntl(lfd, F_SETFL, 0);	/* turn off non-blocking mode */
286 	ftruncate(lfd, 0);
287 	/*
288 	 * write process id for others to know
289 	 */
290 	sprintf(line, "%u\n", getpid());
291 	f = strlen(line);
292 	if (write(lfd, line, f) != f) {
293 		syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
294 		exit(1);
295 	}
296 	signal(SIGCHLD, reapchild);
297 	/*
298 	 * Restart all the printers.
299 	 */
300 	startup();
301 	unlink(_PATH_SOCKETNAME);
302 	funix = socket(AF_UNIX, SOCK_STREAM, 0);
303 	if (funix < 0) {
304 		syslog(LOG_ERR, "socket: %m");
305 		exit(1);
306 	}
307 
308 	sigemptyset(&nmask);
309 	sigaddset(&nmask, SIGHUP);
310 	sigaddset(&nmask, SIGINT);
311 	sigaddset(&nmask, SIGQUIT);
312 	sigaddset(&nmask, SIGTERM);
313 	sigprocmask(SIG_BLOCK, &nmask, &omask);
314 
315 	umask(07);
316 	signal(SIGHUP, mcleanup);
317 	signal(SIGINT, mcleanup);
318 	signal(SIGQUIT, mcleanup);
319 	signal(SIGTERM, mcleanup);
320 	memset(&un, 0, sizeof(un));
321 	un.sun_family = AF_UNIX;
322 	strcpy(un.sun_path, _PATH_SOCKETNAME);
323 #ifndef SUN_LEN
324 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
325 #endif
326 	if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) {
327 		syslog(LOG_ERR, "ubind: %m");
328 		exit(1);
329 	}
330 	umask(0);
331 	sigprocmask(SIG_SETMASK, &omask, NULL);
332 	FD_ZERO(&defreadfds);
333 	FD_SET(funix, &defreadfds);
334 	listen(funix, 5);
335 	if (sflag == 0) {
336 		finet = socksetup(family, socket_debug);
337 	} else
338 		finet = NULL;	/* pretend we couldn't open TCP socket. */
339 	if (finet) {
340 		for (i = 1; i <= *finet; i++) {
341 			FD_SET(finet[i], &defreadfds);
342 			listen(finet[i], 5);
343 		}
344 	}
345 	/*
346 	 * Main loop: accept, do a request, continue.
347 	 */
348 	memset(&frominet, 0, sizeof(frominet));
349 	memset(&fromunix, 0, sizeof(fromunix));
350 	if (lflag)
351 		syslog(LOG_INFO, "lpd startup: ready to accept requests");
352 	/*
353 	 * XXX - should be redone for multi-protocol
354 	 */
355 	for (;;) {
356 		int domain, nfds, s;
357 		fd_set readfds;
358 
359 		FD_COPY(&defreadfds, &readfds);
360 		nfds = select(20, &readfds, 0, 0, 0);
361 		if (nfds <= 0) {
362 			if (nfds < 0 && errno != EINTR)
363 				syslog(LOG_WARNING, "select: %m");
364 			continue;
365 		}
366 		domain = -1;		    /* avoid compile-time warning */
367 		s = -1;			    /* avoid compile-time warning */
368 		if (FD_ISSET(funix, &readfds)) {
369 			domain = AF_UNIX, fromlen = sizeof(fromunix);
370 			s = accept(funix,
371 			    (struct sockaddr *)&fromunix, &fromlen);
372  		} else {
373                         for (i = 1; i <= *finet; i++)
374 				if (FD_ISSET(finet[i], &readfds)) {
375 					domain = AF_INET;
376 					fromlen = sizeof(frominet);
377 					s = accept(finet[i],
378 					    (struct sockaddr *)&frominet,
379 					    &fromlen);
380 				}
381 		}
382 		if (s < 0) {
383 			if (errno != EINTR)
384 				syslog(LOG_WARNING, "accept: %m");
385 			continue;
386 		}
387 		if (fork() == 0) {
388 			/*
389 			 * Note that printjob() also plays around with
390 			 * signal-handling routines, and may need to be
391 			 * changed when making changes to signal-handling.
392 			 */
393 			signal(SIGCHLD, SIG_DFL);
394 			signal(SIGHUP, SIG_IGN);
395 			signal(SIGINT, SIG_IGN);
396 			signal(SIGQUIT, SIG_IGN);
397 			signal(SIGTERM, SIG_IGN);
398 			close(funix);
399 			if (sflag == 0 && finet) {
400                         	for (i = 1; i <= *finet; i++)
401 					close(finet[i]);
402 			}
403 			dup2(s, 1);
404 			close(s);
405 			if (domain == AF_INET) {
406 				/* for both AF_INET and AF_INET6 */
407 				from_remote = 1;
408  				chkhost((struct sockaddr *)&frominet,
409 				    ch_options);
410 			} else
411 				from_remote = 0;
412 			doit();
413 			exit(0);
414 		}
415 		close(s);
416 	}
417 }
418 
419 static void
420 reapchild(int signo __unused)
421 {
422 	int status;
423 
424 	while (wait3(&status, WNOHANG, 0) > 0)
425 		;
426 }
427 
428 static void
429 mcleanup(int signo)
430 {
431 	/*
432 	 * XXX syslog(3) is not signal-safe.
433 	 */
434 	if (lflag) {
435 		if (signo)
436 			syslog(LOG_INFO, "exiting on signal %d", signo);
437 		else
438 			syslog(LOG_INFO, "exiting");
439 	}
440 	unlink(_PATH_SOCKETNAME);
441 	exit(0);
442 }
443 
444 /*
445  * Stuff for handling job specifications
446  */
447 char	*user[MAXUSERS];	/* users to process */
448 int	users;			/* # of users in user array */
449 int	requ[MAXREQUESTS];	/* job number of spool entries */
450 int	requests;		/* # of spool requests */
451 char	*person;		/* name of person doing lprm */
452 
453 		 /* buffer to hold the client's machine-name */
454 static char	 frombuf[MAXHOSTNAMELEN];
455 char	cbuf[BUFSIZ];		/* command line buffer */
456 const char	*cmdnames[] = {
457 	"null",
458 	"printjob",
459 	"recvjob",
460 	"displayq short",
461 	"displayq long",
462 	"rmjob"
463 };
464 
465 static void
466 doit(void)
467 {
468 	char *cp, *printer;
469 	int n;
470 	int status;
471 	struct printer myprinter, *pp = &myprinter;
472 
473 	init_printer(&myprinter);
474 
475 	for (;;) {
476 		cp = cbuf;
477 		do {
478 			if (cp >= &cbuf[sizeof(cbuf) - 1])
479 				fatal(0, "Command line too long");
480 			if ((n = read(STDOUT_FILENO, cp, 1)) != 1) {
481 				if (n < 0)
482 					fatal(0, "Lost connection");
483 				return;
484 			}
485 		} while (*cp++ != '\n');
486 		*--cp = '\0';
487 		cp = cbuf;
488 		if (lflag) {
489 			if (*cp >= '\1' && *cp <= '\5')
490 				syslog(LOG_INFO, "%s requests %s %s",
491 					from_host, cmdnames[(u_char)*cp], cp+1);
492 			else
493 				syslog(LOG_INFO, "bad request (%d) from %s",
494 					*cp, from_host);
495 		}
496 		switch (*cp++) {
497 		case CMD_CHECK_QUE: /* check the queue, print any jobs there */
498 			startprinting(cp);
499 			break;
500 		case CMD_TAKE_THIS: /* receive files to be queued */
501 			if (!from_remote) {
502 				syslog(LOG_INFO, "illegal request (%d)", *cp);
503 				exit(1);
504 			}
505 			recvjob(cp);
506 			break;
507 		case CMD_SHOWQ_SHORT: /* display the queue (short form) */
508 		case CMD_SHOWQ_LONG: /* display the queue (long form) */
509 			/* XXX - this all needs to be redone. */
510 			printer = cp;
511 			while (*cp) {
512 				if (*cp != ' ') {
513 					cp++;
514 					continue;
515 				}
516 				*cp++ = '\0';
517 				while (isspace(*cp))
518 					cp++;
519 				if (*cp == '\0')
520 					break;
521 				if (isdigit(*cp)) {
522 					if (requests >= MAXREQUESTS)
523 						fatal(0, "Too many requests");
524 					requ[requests++] = atoi(cp);
525 				} else {
526 					if (users >= MAXUSERS)
527 						fatal(0, "Too many users");
528 					user[users++] = cp;
529 				}
530 			}
531 			status = getprintcap(printer, pp);
532 			if (status < 0)
533 				fatal(pp, "%s", pcaperr(status));
534 			displayq(pp, cbuf[0] == CMD_SHOWQ_LONG);
535 			exit(0);
536 		case CMD_RMJOB:	/* remove a job from the queue */
537 			if (!from_remote) {
538 				syslog(LOG_INFO, "illegal request (%d)", *cp);
539 				exit(1);
540 			}
541 			printer = cp;
542 			while (*cp && *cp != ' ')
543 				cp++;
544 			if (!*cp)
545 				break;
546 			*cp++ = '\0';
547 			person = cp;
548 			while (*cp) {
549 				if (*cp != ' ') {
550 					cp++;
551 					continue;
552 				}
553 				*cp++ = '\0';
554 				while (isspace(*cp))
555 					cp++;
556 				if (*cp == '\0')
557 					break;
558 				if (isdigit(*cp)) {
559 					if (requests >= MAXREQUESTS)
560 						fatal(0, "Too many requests");
561 					requ[requests++] = atoi(cp);
562 				} else {
563 					if (users >= MAXUSERS)
564 						fatal(0, "Too many users");
565 					user[users++] = cp;
566 				}
567 			}
568 			rmjob(printer);
569 			break;
570 		}
571 		fatal(0, "Illegal service request");
572 	}
573 }
574 
575 /*
576  * Make a pass through the printcap database and start printing any
577  * files left from the last time the machine went down.
578  */
579 static void
580 startup(void)
581 {
582 	int pid, status, more;
583 	struct printer myprinter, *pp = &myprinter;
584 
585 	more = firstprinter(pp, &status);
586 	if (status)
587 		goto errloop;
588 	while (more) {
589 		if (ckqueue(pp) <= 0) {
590 			goto next;
591 		}
592 		if (lflag)
593 			syslog(LOG_INFO, "lpd startup: work for %s",
594 			    pp->printer);
595 		if ((pid = fork()) < 0) {
596 			syslog(LOG_WARNING, "lpd startup: cannot fork for %s",
597 			    pp->printer);
598 			mcleanup(0);
599 		}
600 		if (pid == 0) {
601 			lastprinter();
602 			printjob(pp);
603 			/* NOTREACHED */
604 		}
605 		do {
606 next:
607 			more = nextprinter(pp, &status);
608 errloop:
609 			if (status)
610 				syslog(LOG_WARNING,
611 				    "lpd startup: printcap entry for %s has errors, skipping",
612 				    pp->printer ? pp->printer : "<noname?>");
613 		} while (more && status);
614 	}
615 }
616 
617 /*
618  * Make sure there's some work to do before forking off a child
619  */
620 static int
621 ckqueue(struct printer *pp)
622 {
623 	struct dirent *d;
624 	DIR *dirp;
625 	char *spooldir;
626 
627 	spooldir = pp->spool_dir;
628 	if ((dirp = opendir(spooldir)) == NULL)
629 		return (-1);
630 	while ((d = readdir(dirp)) != NULL) {
631 		if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
632 			continue;	/* daemon control files only */
633 		closedir(dirp);
634 		return (1);		/* found something */
635 	}
636 	closedir(dirp);
637 	return (0);
638 }
639 
640 #define DUMMY ":nobody::"
641 
642 /*
643  * Check to see if the host connecting to this host has access to any
644  * lpd services on this host.
645  */
646 static void
647 chkhost(struct sockaddr *f, int ch_opts)
648 {
649 	struct addrinfo hints, *res, *r;
650 	FILE *hostf;
651 	char hostbuf[NI_MAXHOST], ip[NI_MAXHOST];
652 	char serv[NI_MAXSERV];
653 	char *syserr, *usererr;
654 	int error, errsav, fpass, good, wantsl;
655 
656 	wantsl = 0;
657 	if (ch_opts & LPD_LOGCONNERR)
658 		wantsl = 1;			/* also syslog the errors */
659 
660 	from_host = ".na.";
661 
662 	/* Need real hostname for temporary filenames */
663 	error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0,
664 	    NI_NAMEREQD);
665 	if (error) {
666 		errsav = error;
667 		error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf),
668 		    NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
669 		if (error) {
670 			asprintf(&syserr,
671 			    "can not determine hostname for remote host (%d,%d)",
672 			    errsav, error);
673 			asprintf(&usererr,
674 			    "Host name for your address is not known");
675 			fhosterr(ch_opts, syserr, usererr);
676 			/* NOTREACHED */
677 		}
678 		asprintf(&syserr,
679 		    "Host name for remote host (%s) not known (%d)",
680 		    hostbuf, errsav);
681 		asprintf(&usererr,
682 		    "Host name for your address (%s) is not known",
683 		    hostbuf);
684 		fhosterr(ch_opts, syserr, usererr);
685 		/* NOTREACHED */
686 	}
687 
688 	strlcpy(frombuf, hostbuf, sizeof(frombuf));
689 	from_host = frombuf;
690 	ch_opts |= LPD_ADDFROMLINE;
691 
692 	/* Need address in stringform for comparison (no DNS lookup here) */
693 	error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0,
694 	    NI_NUMERICHOST | NI_WITHSCOPEID);
695 	if (error) {
696 		asprintf(&syserr, "Cannot print IP address (error %d)",
697 		    error);
698 		asprintf(&usererr, "Cannot print IP address for your host");
699 		fhosterr(ch_opts, syserr, usererr);
700 		/* NOTREACHED */
701 	}
702 	from_ip = strdup(hostbuf);
703 
704 	/* Reject numeric addresses */
705 	memset(&hints, 0, sizeof(hints));
706 	hints.ai_family = family;
707 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
708 	hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
709 	if (getaddrinfo(from_host, NULL, &hints, &res) == 0) {
710 		freeaddrinfo(res);
711 		/* This syslog message already includes from_host */
712 		ch_opts &= ~LPD_ADDFROMLINE;
713 		asprintf(&syserr, "reverse lookup results in non-FQDN %s",
714 		    from_host);
715 		/* same message to both syslog and remote user */
716 		fhosterr(ch_opts, syserr, syserr);
717 		/* NOTREACHED */
718 	}
719 
720 	/* Check for spoof, ala rlogind */
721 	memset(&hints, 0, sizeof(hints));
722 	hints.ai_family = family;
723 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
724 	error = getaddrinfo(from_host, NULL, &hints, &res);
725 	if (error) {
726 		asprintf(&syserr, "dns lookup for address %s failed: %s",
727 		    from_ip, gai_strerror(error));
728 		asprintf(&usererr, "hostname for your address (%s) unknown: %s",
729 		    from_ip, gai_strerror(error));
730 		fhosterr(ch_opts, syserr, usererr);
731 		/* NOTREACHED */
732 	}
733 	good = 0;
734 	for (r = res; good == 0 && r; r = r->ai_next) {
735 		error = getnameinfo(r->ai_addr, r->ai_addrlen, ip, sizeof(ip),
736 		    NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
737 		if (!error && !strcmp(from_ip, ip))
738 			good = 1;
739 	}
740 	if (res)
741 		freeaddrinfo(res);
742 	if (good == 0) {
743 		asprintf(&syserr, "address for remote host (%s) not matched",
744 		    from_ip);
745 		asprintf(&usererr,
746 		    "address for your hostname (%s) not matched", from_ip);
747 		fhosterr(ch_opts, syserr, usererr);
748 		/* NOTREACHED */
749 	}
750 
751 	fpass = 1;
752 	hostf = fopen(_PATH_HOSTSEQUIV, "r");
753 again:
754 	if (hostf) {
755 		if (__ivaliduser_sa(hostf, f, f->sa_len, DUMMY, DUMMY) == 0) {
756 			fclose(hostf);
757 			goto foundhost;
758 		}
759 		fclose(hostf);
760 	}
761 	if (fpass == 1) {
762 		fpass = 2;
763 		hostf = fopen(_PATH_HOSTSLPD, "r");
764 		goto again;
765 	}
766 	/* This syslog message already includes from_host */
767 	ch_opts &= ~LPD_ADDFROMLINE;
768 	asprintf(&syserr, "refused connection from %s, sip=%s", from_host,
769 	    from_ip);
770 	asprintf(&usererr,
771 	    "Print-services are not available to your host (%s).", from_host);
772 	fhosterr(ch_opts, syserr, usererr);
773 	/* NOTREACHED */
774 
775 foundhost:
776 	if (ch_opts & LPD_NOPORTCHK)
777 		return;			/* skip the reserved-port check */
778 
779 	error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv),
780 	    NI_NUMERICSERV);
781 	if (error) {
782 		/* same message to both syslog and remote user */
783 		asprintf(&syserr, "malformed from-address (%d)", error);
784 		fhosterr(ch_opts, syserr, syserr);
785 		/* NOTREACHED */
786 	}
787 
788 	if (atoi(serv) >= IPPORT_RESERVED) {
789 		/* same message to both syslog and remote user */
790 		asprintf(&syserr, "connected from invalid port (%s)", serv);
791 		fhosterr(ch_opts, syserr, syserr);
792 		/* NOTREACHED */
793 	}
794 }
795 
796 /*
797  * Handle fatal errors in chkhost.  The first message will optionally be
798  * sent to syslog, the second one is sent to the connecting host.
799  *
800  * The idea is that the syslog message is meant for an administrator of a
801  * print server (the host receiving connections), while the usermsg is meant
802  * for a remote user who may or may not be clueful, and may or may not be
803  * doing something nefarious.  Some remote users (eg, MS-Windows...) may not
804  * even see whatever message is sent, which is why there's the option to
805  * start 'lpd' with the connection-errors also sent to syslog.
806  *
807  * Given that hostnames can theoretically be fairly long (well, over 250
808  * bytes), it would probably be helpful to have the 'from_host' field at
809  * the end of any error messages which include that info.
810  *
811  * These are Fatal host-connection errors, so this routine does not return.
812  */
813 static void
814 fhosterr(int ch_opts, char *sysmsg, char *usermsg)
815 {
816 
817 	/*
818 	 * If lpd was started up to print connection errors, then write
819 	 * the syslog message before the user message.
820 	 * And for many of the syslog messages, it is helpful to first
821 	 * write the from_host (if it is known) as a separate syslog
822 	 * message, since the hostname may be so long.
823 	 */
824 	if (ch_opts & LPD_LOGCONNERR) {
825 		if (ch_opts & LPD_ADDFROMLINE) {
826 		    syslog(LOG_WARNING, "for connection from %s:", from_host);
827 		}
828 		syslog(LOG_WARNING, "%s", sysmsg);
829 	}
830 
831 	/*
832 	 * Now send the error message to the remote host which is trying
833 	 * to make the connection.
834 	 */
835 	printf("%s [@%s]: %s\n", progname, local_host, usermsg);
836 	fflush(stdout);
837 
838 	/*
839 	 * Add a minimal delay before exiting (and disconnecting from the
840 	 * sending-host).  This is just in case that machine responds by
841 	 * INSTANTLY retrying (and instantly re-failing...).  This may also
842 	 * give the other side more time to read the error message.
843 	 */
844 	sleep(2);			/* a paranoid throttling measure */
845 	exit(1);
846 }
847 
848 /* setup server socket for specified address family */
849 /* if af is PF_UNSPEC more than one socket may be returned */
850 /* the returned list is dynamically allocated, so caller needs to free it */
851 static int *
852 socksetup(int af, int debuglvl)
853 {
854 	struct addrinfo hints, *res, *r;
855 	int error, maxs, *s, *socks;
856 	const int on = 1;
857 
858 	memset(&hints, 0, sizeof(hints));
859 	hints.ai_flags = AI_PASSIVE;
860 	hints.ai_family = af;
861 	hints.ai_socktype = SOCK_STREAM;
862 	error = getaddrinfo(NULL, "printer", &hints, &res);
863 	if (error) {
864 		syslog(LOG_ERR, "%s", gai_strerror(error));
865 		mcleanup(0);
866 	}
867 
868 	/* Count max number of sockets we may open */
869 	for (maxs = 0, r = res; r; r = r->ai_next, maxs++)
870 		;
871 	socks = malloc((maxs + 1) * sizeof(int));
872 	if (!socks) {
873 		syslog(LOG_ERR, "couldn't allocate memory for sockets");
874 		mcleanup(0);
875 	}
876 
877 	*socks = 0;   /* num of sockets counter at start of array */
878 	s = socks + 1;
879 	for (r = res; r; r = r->ai_next) {
880 		*s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
881 		if (*s < 0) {
882 			syslog(LOG_DEBUG, "socket(): %m");
883 			continue;
884 		}
885 		if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))
886 		    < 0) {
887 			syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m");
888 			close(*s);
889 			continue;
890 		}
891 		if (debuglvl)
892 			if (setsockopt(*s, SOL_SOCKET, SO_DEBUG, &debuglvl,
893 			    sizeof(debuglvl)) < 0) {
894 				syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
895 				close(*s);
896 				continue;
897 			}
898 #ifdef IPV6_BINDV6ONLY
899 		if (r->ai_family == AF_INET6) {
900 			if (setsockopt(*s, IPPROTO_IPV6, IPV6_BINDV6ONLY,
901 				       &on, sizeof(on)) < 0) {
902 				syslog(LOG_ERR,
903 				       "setsockopt (IPV6_BINDV6ONLY): %m");
904 				close(*s);
905 				continue;
906 			}
907 		}
908 #endif
909 		if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
910 			syslog(LOG_DEBUG, "bind(): %m");
911 			close(*s);
912 			continue;
913 		}
914 		(*socks)++;
915 		s++;
916 	}
917 
918 	if (res)
919 		freeaddrinfo(res);
920 
921 	if (*socks == 0) {
922 		syslog(LOG_ERR, "Couldn't bind to any socket");
923 		free(socks);
924 		mcleanup(0);
925 	}
926 	return(socks);
927 }
928 
929 static void
930 usage(void)
931 {
932 #ifdef INET6
933 	fprintf(stderr, "usage: lpd [-cdlsW46] [port#]\n");
934 #else
935 	fprintf(stderr, "usage: lpd [-cdlsW] [port#]\n");
936 #endif
937 	exit(EX_USAGE);
938 }
939