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