xref: /openbsd-src/usr.sbin/lpr/lpd/lpd.c (revision 3a50f0a93a2072911d0ba6ababa815fb04bf9a71)
1*3a50f0a9Sjmc /*	$OpenBSD: lpd.c,v 1.66 2022/12/28 21:30:17 jmc Exp $	*/
2a7643117Smillert /*	$NetBSD: lpd.c,v 1.33 2002/01/21 14:42:29 wiz Exp $	*/
32701d70cSderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1983, 1993, 1994
6df930be7Sderaadt  *	The Regents of the University of California.  All rights reserved.
7df930be7Sderaadt  *
8df930be7Sderaadt  *
9df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
10df930be7Sderaadt  * modification, are permitted provided that the following conditions
11df930be7Sderaadt  * are met:
12df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
13df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
14df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
15df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
16df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
1729295d1cSmillert  * 3. Neither the name of the University nor the names of its contributors
18df930be7Sderaadt  *    may be used to endorse or promote products derived from this software
19df930be7Sderaadt  *    without specific prior written permission.
20df930be7Sderaadt  *
21df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22df930be7Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23df930be7Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24df930be7Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25df930be7Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26df930be7Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27df930be7Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28df930be7Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29df930be7Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30df930be7Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31df930be7Sderaadt  * SUCH DAMAGE.
32df930be7Sderaadt  */
33df930be7Sderaadt 
34df930be7Sderaadt /*
35df930be7Sderaadt  * lpd -- line printer daemon.
36df930be7Sderaadt  *
37df930be7Sderaadt  * Listen for a connection and perform the requested operation.
38df930be7Sderaadt  * Operations are:
39df930be7Sderaadt  *	\1printer\n
40df930be7Sderaadt  *		check the queue for jobs and print any found.
41df930be7Sderaadt  *	\2printer\n
42df930be7Sderaadt  *		receive a job from another machine and queue it.
43df930be7Sderaadt  *	\3printer [users ...] [jobs ...]\n
44df930be7Sderaadt  *		return the current state of the queue (short form).
45df930be7Sderaadt  *	\4printer [users ...] [jobs ...]\n
46df930be7Sderaadt  *		return the current state of the queue (long form).
47df930be7Sderaadt  *	\5printer person [users ...] [jobs ...]\n
48df930be7Sderaadt  *		remove jobs from the queue.
49df930be7Sderaadt  *
50df930be7Sderaadt  * Strategy to maintain protected spooling area:
516468ba68Smillert  *	1. Spooling area is writable only by root and the group daemon.
526468ba68Smillert  *	2. Files in spooling area are owned by user daemon, group daemon,
536468ba68Smillert  *	   and are mode 660.
546468ba68Smillert  *	3. lpd runs as root but spends most of its time with its effective
556468ba68Smillert  *	   uid and gid set to the uid/gid specified in the passwd entry for
566468ba68Smillert  *	   DEFUID (1, aka daemon).
5747481f8aSmillert  *	4. lpr and lprm run setuid daemon and setgrp daemon.  lpr opens
586468ba68Smillert  *	   files to be printed with its real uid/gid and writes to
596468ba68Smillert  *	   the spool dir with its effective uid/gid (i.e. daemon).
6047481f8aSmillert  *	   lprm need to run as user daemon so it can kill lpd.
6147481f8aSmillert  *	5. lpc and lpq run setgrp daemon.
626468ba68Smillert  *
636468ba68Smillert  * Users can't touch the spool w/o the help of one of the lp* programs.
64df930be7Sderaadt  */
65df930be7Sderaadt 
66df930be7Sderaadt #include <sys/types.h>
67f4147939Sguenther #include <sys/wait.h>
68df930be7Sderaadt #include <sys/socket.h>
69df930be7Sderaadt #include <sys/un.h>
70df930be7Sderaadt #include <sys/stat.h>
71f4147939Sguenther 
72df930be7Sderaadt #include <netinet/in.h>
73c1624b2fSmillert #include <arpa/inet.h>
74df930be7Sderaadt 
75a7643117Smillert #include <ctype.h>
76a7643117Smillert #include <dirent.h>
77a7643117Smillert #include <err.h>
78df930be7Sderaadt #include <errno.h>
79df930be7Sderaadt #include <fcntl.h>
80a7643117Smillert #include <netdb.h>
816468ba68Smillert #include <pwd.h>
82a7643117Smillert #include <signal.h>
83df930be7Sderaadt #include <stdio.h>
84df930be7Sderaadt #include <stdlib.h>
85df930be7Sderaadt #include <string.h>
86a7643117Smillert #include <syslog.h>
87a7643117Smillert #include <unistd.h>
88b9fc9a72Sderaadt #include <limits.h>
89a7643117Smillert 
90df930be7Sderaadt #include "lp.h"
91df930be7Sderaadt #include "lp.local.h"
92df930be7Sderaadt #include "pathnames.h"
93df930be7Sderaadt #include "extern.h"
94df930be7Sderaadt 
95df930be7Sderaadt int	lflag;				/* log requests flag */
96a7643117Smillert int	rflag;				/* allow 'of' for remote printers */
97a7643117Smillert int	sflag;				/* secure (no inet) flag */
98df930be7Sderaadt int	from_remote;			/* from remote socket */
99a7643117Smillert char	**blist;			/* list of addresses to bind(2) to */
100a7643117Smillert int	blist_size;
101a7643117Smillert int	blist_addrs;
102a7643117Smillert 
103a7643117Smillert volatile sig_atomic_t child_count;	/* number of kids forked */
104df930be7Sderaadt 
105c72b5b24Smillert static void		reapchild(int);
106c72b5b24Smillert static void		mcleanup(int);
107c72b5b24Smillert static void		doit(void);
108c72b5b24Smillert static void		startup(void);
1098d66a5d9Sstevesk static void		chkhost(struct sockaddr *);
110a7643117Smillert static __dead void	usage(void);
111a7643117Smillert static int		*socksetup(int, int, const char *);
112a7643117Smillert 
1130122b1e5Sderaadt /* unused, needed for lpc */
1140122b1e5Sderaadt volatile sig_atomic_t gotintr;
1150122b1e5Sderaadt 
116df930be7Sderaadt int
main(int argc,char ** argv)117a7643117Smillert main(int argc, char **argv)
118df930be7Sderaadt {
119df930be7Sderaadt 	fd_set defreadfds;
1206468ba68Smillert 	struct passwd *pw;
121df930be7Sderaadt 	struct sockaddr_un un, fromunix;
122a7643117Smillert 	struct sockaddr_storage frominet;
1239a7fa6a3Smillert 	sigset_t mask, omask;
124ebada818Smillert 	int i, funix, *finet;
1258d66a5d9Sstevesk 	int options, maxfd;
126a7643117Smillert 	long l;
1276957a4a4Sjmc 	long child_max = 32;	/* more than enough to hose the system */
128a7643117Smillert 	struct servent *sp;
129a7643117Smillert 	const char *port = "printer";
130a7643117Smillert 	char *cp;
131df930be7Sderaadt 
1326468ba68Smillert 	if (geteuid() != 0)
133a7643117Smillert 		errx(1, "must run as root");
134df930be7Sderaadt 
1356468ba68Smillert 	/*
1366468ba68Smillert 	 * We want to run with euid of daemon most of the time.
1376468ba68Smillert 	 */
1386468ba68Smillert 	if ((pw = getpwuid(DEFUID)) == NULL)
139b0ca7b2aSderaadt 		errx(1, "daemon uid (%u) not in password file", DEFUID);
1406468ba68Smillert 	real_uid = pw->pw_uid;
1416468ba68Smillert 	real_gid = pw->pw_gid;
1426468ba68Smillert 	effective_uid = 0;
1436468ba68Smillert 	effective_gid = getegid();
1446468ba68Smillert 	PRIV_END;	/* run as daemon for most things */
1456468ba68Smillert 
1466468ba68Smillert 	options = 0;
1476468ba68Smillert 	gethostname(host, sizeof(host));
1486468ba68Smillert 
14967e491c4Saaron 	while ((i = getopt(argc, argv, "b:dln:rsw:W")) != -1) {
150a7643117Smillert 		switch (i) {
151a7643117Smillert 		case 'b':
152a7643117Smillert 			if (blist_addrs >= blist_size) {
153f733074bSpvalchev 				char **newblist;
154f733074bSpvalchev 				int newblist_size = blist_size +
155f733074bSpvalchev 				    sizeof(char *) * 4;
156f733074bSpvalchev 				newblist = realloc(blist, newblist_size);
157f733074bSpvalchev 				if (newblist == NULL) {
158f733074bSpvalchev 					free(blist);
159f733074bSpvalchev 					blist_size = 0;
160f733074bSpvalchev 					blist = NULL;
161f733074bSpvalchev 				}
162f733074bSpvalchev 				blist = newblist;
163f733074bSpvalchev 				blist_size = newblist_size;
164a7643117Smillert 				if (blist == NULL)
165a7643117Smillert 					err(1, "cant allocate bind addr list");
166a7643117Smillert 			}
167a7643117Smillert 			blist[blist_addrs] = strdup(optarg);
168a7643117Smillert 			if (blist[blist_addrs++] == NULL)
169a7643117Smillert 				err(1, NULL);
170a7643117Smillert 			break;
171df930be7Sderaadt 		case 'd':
172df930be7Sderaadt 			options |= SO_DEBUG;
173df930be7Sderaadt 			break;
174df930be7Sderaadt 		case 'l':
17528056f30Sderaadt 			lflag = 1;
176df930be7Sderaadt 			break;
177a7643117Smillert 		case 'n':
178a7643117Smillert 			child_max = strtol(optarg, &cp, 10);
179a7643117Smillert 			if (*cp != '\0' || child_max < 0 || child_max > 1024)
180a7643117Smillert 				errx(1, "invalid number of children: %s",
181a7643117Smillert 				    optarg);
182a7643117Smillert 			break;
183a7643117Smillert 		case 'r':
18428056f30Sderaadt 			rflag = 1;
185a7643117Smillert 			break;
186a7643117Smillert 		case 's':
18728056f30Sderaadt 			sflag = 1;
188a7643117Smillert 			break;
189a7643117Smillert 		case 'w':
190a7643117Smillert 			l = strtol(optarg, &cp, 10);
191a7643117Smillert 			if (*cp != '\0' || l < 0 || l >= INT_MAX)
192*3a50f0a9Sjmc 				errx(1, "wait time must be positive integer: %s",
193a7643117Smillert 				    optarg);
194a7643117Smillert 			wait_time = (u_int)l;
195a7643117Smillert 			if (wait_time < 30)
196a7643117Smillert 				warnx("warning: wait time less than 30 seconds");
197a7643117Smillert 			break;
1988d66a5d9Sstevesk 		case 'W':	/* XXX deprecate */
199a7643117Smillert 			break;
200a7643117Smillert 		default:
201a7643117Smillert 			usage();
202a7643117Smillert 			break;
203df930be7Sderaadt 		}
204df930be7Sderaadt 	}
205a7643117Smillert 	argc -= optind;
206a7643117Smillert 	argv += optind;
207a7643117Smillert 
208a7643117Smillert 	switch (argc) {
209a7643117Smillert 	case 1:
210a7643117Smillert 		port = argv[0];
211a7643117Smillert 		l = strtol(port, &cp, 10);
212a7643117Smillert 		if (*cp != '\0' || l <= 0 || l > USHRT_MAX)
213a7643117Smillert 			errx(1, "port # %s is invalid", port);
214a7643117Smillert 		break;
215a7643117Smillert 	case 0:
216a7643117Smillert 		sp = getservbyname(port, "tcp");
217a7643117Smillert 		if (sp == NULL)
218a7643117Smillert 			errx(1, "%s/tcp: unknown service", port);
219a7643117Smillert 		break;
220a7643117Smillert 	default:
221a7643117Smillert 		usage();
222a7643117Smillert 	}
223df930be7Sderaadt 
224ebada818Smillert 	funix = socket(AF_UNIX, SOCK_STREAM, 0);
225ebada818Smillert 	if (funix < 0)
226ebada818Smillert 		err(1, "socket");
227ebada818Smillert 	memset(&un, 0, sizeof(un));
228ebada818Smillert 	un.sun_family = AF_UNIX;
229ebada818Smillert 	strlcpy(un.sun_path, _PATH_SOCKETNAME, sizeof(un.sun_path));
230ebada818Smillert 	PRIV_START;
231ebada818Smillert 	if (connect(funix, (struct sockaddr *)&un, sizeof(un)) == 0)
232ebada818Smillert 		errx(1, "already running");
233ebada818Smillert 	if (errno != ENOENT)
234ebada818Smillert 		(void)unlink(un.sun_path);
235ebada818Smillert 	if (bind(funix, (struct sockaddr *)&un, sizeof(un)) < 0)
236ebada818Smillert 		err(1, "bind %s", un.sun_path);
237ebada818Smillert 	chmod(_PATH_SOCKETNAME, 0660);
238ebada818Smillert 	chown(_PATH_SOCKETNAME, -1, real_gid);
239ebada818Smillert 	PRIV_END;
240ebada818Smillert 
241df930be7Sderaadt #ifndef DEBUG
242df930be7Sderaadt 	/*
243df930be7Sderaadt 	 * Set up standard environment by detaching from the parent.
244df930be7Sderaadt 	 */
245df930be7Sderaadt 	daemon(0, 0);
246df930be7Sderaadt #endif
247df930be7Sderaadt 
248df930be7Sderaadt 	openlog("lpd", LOG_PID, LOG_LPR);
249df930be7Sderaadt 	syslog(LOG_INFO, "restarted");
250df930be7Sderaadt 	(void)umask(0);
251df930be7Sderaadt 	signal(SIGCHLD, reapchild);
252df930be7Sderaadt 	/*
253df930be7Sderaadt 	 * Restart all the printers.
254df930be7Sderaadt 	 */
255df930be7Sderaadt 	startup();
256a7643117Smillert 
2579a7fa6a3Smillert 	sigemptyset(&mask);
2589a7fa6a3Smillert 	sigaddset(&mask, SIGHUP);
2599a7fa6a3Smillert 	sigaddset(&mask, SIGINT);
2609a7fa6a3Smillert 	sigaddset(&mask, SIGQUIT);
2619a7fa6a3Smillert 	sigaddset(&mask, SIGTERM);
2629a7fa6a3Smillert 	sigprocmask(SIG_BLOCK, &mask, &omask);
263a7643117Smillert 
264df930be7Sderaadt 	signal(SIGHUP, mcleanup);
265df930be7Sderaadt 	signal(SIGINT, mcleanup);
266df930be7Sderaadt 	signal(SIGQUIT, mcleanup);
267df930be7Sderaadt 	signal(SIGTERM, mcleanup);
2689a7fa6a3Smillert 	sigprocmask(SIG_SETMASK, &omask, NULL);
269df930be7Sderaadt 	FD_ZERO(&defreadfds);
270df930be7Sderaadt 	FD_SET(funix, &defreadfds);
271df930be7Sderaadt 	listen(funix, 5);
272a7643117Smillert 	if (!sflag || blist_addrs)
273a7643117Smillert 		finet = socksetup(PF_UNSPEC, options, port);
274a7643117Smillert 	else
275a7643117Smillert 		finet = NULL;	/* pretend we couldn't open TCP socket. */
276df930be7Sderaadt 
277a7643117Smillert 	if (blist != NULL) {
278a7643117Smillert 		for (i = 0; i < blist_addrs; i++)
279a7643117Smillert 			free(blist[i]);
280a7643117Smillert 		free(blist);
281df930be7Sderaadt 	}
282a7643117Smillert 
2830de90af5Smillert 	maxfd = funix;
284a7643117Smillert 	if (finet) {
285a7643117Smillert 		for (i = 1; i <= *finet; i++) {
286a7643117Smillert 			FD_SET(finet[i], &defreadfds);
287a7643117Smillert 			listen(finet[i], 5);
2880de90af5Smillert 			if (finet[i] > maxfd)
2890de90af5Smillert 				maxfd = finet[i];
29059e77ed9Smillert 		}
291df930be7Sderaadt 	}
292df930be7Sderaadt 	/*
293df930be7Sderaadt 	 * Main loop: accept, do a request, continue.
294df930be7Sderaadt 	 */
295df930be7Sderaadt 	memset(&frominet, 0, sizeof(frominet));
296df930be7Sderaadt 	memset(&fromunix, 0, sizeof(fromunix));
297df930be7Sderaadt 	for (;;) {
29808e0da89Sderaadt 		int domain, nfds, s;
29908e0da89Sderaadt 		socklen_t fromlen;
300df930be7Sderaadt 		fd_set readfds;
301a7643117Smillert 		short sleeptime = 10;	/* overflows in about 2 hours */
302a7643117Smillert 
303a7643117Smillert 		while (child_max < child_count) {
304a7643117Smillert 			syslog(LOG_WARNING,
305a7643117Smillert 			    "too many children, sleeping for %d seconds",
306a7643117Smillert 			    sleeptime);
307a7643117Smillert 			sleep(sleeptime);
308a7643117Smillert 			sleeptime <<= 1;
309a7643117Smillert 			if (sleeptime < 0) {
310a7643117Smillert 				syslog(LOG_CRIT, "sleeptime overflowed! help!");
311a7643117Smillert 				sleeptime = 10;
312a7643117Smillert 			}
313a7643117Smillert 		}
314df930be7Sderaadt 
315df930be7Sderaadt 		FD_COPY(&defreadfds, &readfds);
3160de90af5Smillert 		nfds = select(maxfd + 1, &readfds, NULL, NULL, NULL);
317df930be7Sderaadt 		if (nfds <= 0) {
318df930be7Sderaadt 			if (nfds < 0 && errno != EINTR)
319df930be7Sderaadt 				syslog(LOG_WARNING, "select: %m");
320df930be7Sderaadt 			continue;
321df930be7Sderaadt 		}
322df930be7Sderaadt 		if (FD_ISSET(funix, &readfds)) {
323e5ffb51bSmillert 			domain = AF_UNIX;
324a7643117Smillert 			fromlen = sizeof(fromunix);
325df930be7Sderaadt 			s = accept(funix,
326df930be7Sderaadt 			    (struct sockaddr *)&fromunix, &fromlen);
327a7643117Smillert 		} else {
328a7643117Smillert 			domain = AF_INET;
329a7643117Smillert 			s = -1;
330a7643117Smillert 			for (i = 1; i <= *finet; i++)
331a7643117Smillert 				if (FD_ISSET(finet[i], &readfds)) {
332a7643117Smillert 					in_port_t port;
333a7643117Smillert 
334a7643117Smillert 					fromlen = sizeof(frominet);
335a7643117Smillert 					s = accept(finet[i],
336a7643117Smillert 					    (struct sockaddr *)&frominet,
337a7643117Smillert 					    &fromlen);
338a7643117Smillert 					switch (frominet.ss_family) {
339a7643117Smillert 					case AF_INET:
340a7643117Smillert 						port = ((struct sockaddr_in *)
341a7643117Smillert 						    &frominet)->sin_port;
342a7643117Smillert 						break;
343a7643117Smillert 					case AF_INET6:
344a7643117Smillert 						port = ((struct sockaddr_in6 *)
345a7643117Smillert 						    &frominet)->sin6_port;
346a7643117Smillert 						break;
347a7643117Smillert 					default:
348a7643117Smillert 						port = 0;
349a7643117Smillert 					}
350a7643117Smillert 					/* check for ftp bounce attack */
351a7643117Smillert 					if (port == htons(20)) {
35264cd2c7eSderaadt 						close(s);
35364cd2c7eSderaadt 						continue;
35464cd2c7eSderaadt 					}
355df930be7Sderaadt 				}
356a7643117Smillert 		}
357df930be7Sderaadt 		if (s < 0) {
35862e3c252Sderaadt 			if (errno != EINTR && errno != EWOULDBLOCK &&
35962e3c252Sderaadt 			    errno != ECONNABORTED)
360df930be7Sderaadt 				syslog(LOG_WARNING, "accept: %m");
361df930be7Sderaadt 			continue;
362df930be7Sderaadt 		}
363a7643117Smillert 
364a7643117Smillert 		switch (fork()) {
365a7643117Smillert 		case 0:
3664472945eSmillert 			signal(SIGCHLD, SIG_DFL);
367df930be7Sderaadt 			signal(SIGHUP, SIG_IGN);
368df930be7Sderaadt 			signal(SIGINT, SIG_IGN);
369df930be7Sderaadt 			signal(SIGQUIT, SIG_IGN);
370df930be7Sderaadt 			signal(SIGTERM, SIG_IGN);
371df930be7Sderaadt 			(void)close(funix);
372a7643117Smillert 			if (!sflag && finet)
373a7643117Smillert 				for (i = 1; i <= *finet; i++)
374a7643117Smillert 					(void)close(finet[i]);
3754bc554dcSmillert 			if (s != STDOUT_FILENO) {
3764bc554dcSmillert 				dup2(s, STDOUT_FILENO);
377df930be7Sderaadt 				(void)close(s);
3784bc554dcSmillert 			}
379df930be7Sderaadt 			if (domain == AF_INET) {
380a7643117Smillert 				/* for both AF_INET and AF_INET6 */
381df930be7Sderaadt 				from_remote = 1;
3828d66a5d9Sstevesk 				chkhost((struct sockaddr *)&frominet);
383df930be7Sderaadt 			} else
384df930be7Sderaadt 				from_remote = 0;
385df930be7Sderaadt 			doit();
386df930be7Sderaadt 			exit(0);
387a7643117Smillert 		case -1:
388a7643117Smillert 			syslog(LOG_WARNING, "fork: %m, sleeping for 10 seconds...");
389a7643117Smillert 			sleep(10);
390a7643117Smillert 			continue;
391a7643117Smillert 		default:
392a7643117Smillert 			child_count++;
393df930be7Sderaadt 		}
394df930be7Sderaadt 		(void)close(s);
395df930be7Sderaadt 	}
396df930be7Sderaadt }
397df930be7Sderaadt 
398df930be7Sderaadt static void
reapchild(int signo)399a7643117Smillert reapchild(int signo)
400df930be7Sderaadt {
4016dccf94eSderaadt 	int save_errno = errno;
4027248be3bSderaadt 	int status;
403df930be7Sderaadt 
40420a848d0Smillert 	while (waitpid((pid_t)-1, &status, WNOHANG) > 0)
405a7643117Smillert 		child_count--;
4066dccf94eSderaadt 	errno = save_errno;
407df930be7Sderaadt }
408df930be7Sderaadt 
409df930be7Sderaadt static void
mcleanup(int signo)410a7643117Smillert mcleanup(int signo)
411df930be7Sderaadt {
4127248be3bSderaadt 	struct syslog_data sdata = SYSLOG_DATA_INIT;
4137248be3bSderaadt 
414df930be7Sderaadt 	if (lflag)
4157248be3bSderaadt 		syslog_r(LOG_INFO, &sdata, "exiting");
4166468ba68Smillert 	PRIV_START;
417df930be7Sderaadt 	unlink(_PATH_SOCKETNAME);
4187248be3bSderaadt 	_exit(0);
419df930be7Sderaadt }
420df930be7Sderaadt 
421df930be7Sderaadt /*
422df930be7Sderaadt  * Stuff for handling job specifications
423df930be7Sderaadt  */
424df930be7Sderaadt char	*user[MAXUSERS];	/* users to process */
425df930be7Sderaadt int	users;			/* # of users in user array */
426df930be7Sderaadt int	requ[MAXREQUESTS];	/* job number of spool entries */
427df930be7Sderaadt int	requests;		/* # of spool requests */
428df930be7Sderaadt char	*person;		/* name of person doing lprm */
429df930be7Sderaadt 
430a7643117Smillert char	fromb[NI_MAXHOST];	/* buffer for client's machine name */
431df930be7Sderaadt char	cbuf[BUFSIZ];		/* command line buffer */
432df930be7Sderaadt char	*cmdnames[] = {
433df930be7Sderaadt 	"null",
434df930be7Sderaadt 	"printjob",
435df930be7Sderaadt 	"recvjob",
436df930be7Sderaadt 	"displayq short",
437df930be7Sderaadt 	"displayq long",
438df930be7Sderaadt 	"rmjob"
439df930be7Sderaadt };
440df930be7Sderaadt 
441df930be7Sderaadt static void
doit(void)442a7643117Smillert doit(void)
443df930be7Sderaadt {
444c1624b2fSmillert 	char *cp;
445c1624b2fSmillert 	int n;
446df930be7Sderaadt 
447df930be7Sderaadt 	for (;;) {
448df930be7Sderaadt 		cp = cbuf;
449df930be7Sderaadt 		do {
450df930be7Sderaadt 			if (cp >= &cbuf[sizeof(cbuf) - 1])
451df930be7Sderaadt 				fatal("Command line too long");
452a7643117Smillert 			if ((n = read(STDOUT_FILENO, cp, 1)) != 1) {
453df930be7Sderaadt 				if (n < 0)
454df930be7Sderaadt 					fatal("Lost connection");
455df930be7Sderaadt 				return;
456df930be7Sderaadt 			}
457df930be7Sderaadt 		} while (*cp++ != '\n');
458df930be7Sderaadt 		*--cp = '\0';
459df930be7Sderaadt 		cp = cbuf;
460df930be7Sderaadt 		if (lflag) {
461a7643117Smillert 			if (*cp >= '\1' && *cp <= '\5') {
462df930be7Sderaadt 				syslog(LOG_INFO, "%s requests %s %s",
463705d4ab5Sderaadt 					from, cmdnames[(int)*cp], cp+1);
464a7643117Smillert 				setproctitle("serving %s: %s %s", from,
465a7643117Smillert 				    cmdnames[(int)*cp], cp+1);
466a7643117Smillert 			} else
467df930be7Sderaadt 				syslog(LOG_INFO, "bad request (%d) from %s",
468df930be7Sderaadt 					*cp, from);
469df930be7Sderaadt 		}
470df930be7Sderaadt 		switch (*cp++) {
471df930be7Sderaadt 		case '\1':	/* check the queue and print any jobs there */
472df930be7Sderaadt 			printer = cp;
473a7643117Smillert 			if (*printer == '\0')
474a7643117Smillert 				printer = DEFLP;
475df930be7Sderaadt 			printjob();
476df930be7Sderaadt 			break;
477df930be7Sderaadt 		case '\2':	/* receive files to be queued */
478df930be7Sderaadt 			if (!from_remote) {
479df930be7Sderaadt 				syslog(LOG_INFO, "illegal request (%d)", *cp);
480df930be7Sderaadt 				exit(1);
481df930be7Sderaadt 			}
482df930be7Sderaadt 			printer = cp;
483a7643117Smillert 			if (*printer == '\0')
484a7643117Smillert 				printer = DEFLP;
485df930be7Sderaadt 			recvjob();
486df930be7Sderaadt 			break;
487df930be7Sderaadt 		case '\3':	/* display the queue (short form) */
488df930be7Sderaadt 		case '\4':	/* display the queue (long form) */
489df930be7Sderaadt 			printer = cp;
490a7643117Smillert 			if (*printer == '\0')
491a7643117Smillert 				printer = DEFLP;
492df930be7Sderaadt 			while (*cp) {
493df930be7Sderaadt 				if (*cp != ' ') {
494df930be7Sderaadt 					cp++;
495df930be7Sderaadt 					continue;
496df930be7Sderaadt 				}
497df930be7Sderaadt 				*cp++ = '\0';
4980c4db8c1Sderaadt 				while (isspace((unsigned char)*cp))
499df930be7Sderaadt 					cp++;
500df930be7Sderaadt 				if (*cp == '\0')
501df930be7Sderaadt 					break;
5020c4db8c1Sderaadt 				if (isdigit((unsigned char)*cp)) {
503df930be7Sderaadt 					if (requests >= MAXREQUESTS)
504df930be7Sderaadt 						fatal("Too many requests");
505df930be7Sderaadt 					requ[requests++] = atoi(cp);
506df930be7Sderaadt 				} else {
507df930be7Sderaadt 					if (users >= MAXUSERS)
508df930be7Sderaadt 						fatal("Too many users");
509df930be7Sderaadt 					user[users++] = cp;
510df930be7Sderaadt 				}
511df930be7Sderaadt 			}
512df930be7Sderaadt 			displayq(cbuf[0] - '\3');
513df930be7Sderaadt 			exit(0);
514df930be7Sderaadt 		case '\5':	/* remove a job from the queue */
515df930be7Sderaadt 			if (!from_remote) {
516df930be7Sderaadt 				syslog(LOG_INFO, "illegal request (%d)", *cp);
517df930be7Sderaadt 				exit(1);
518df930be7Sderaadt 			}
519df930be7Sderaadt 			printer = cp;
520a7643117Smillert 			if (*printer == '\0')
521a7643117Smillert 				printer = DEFLP;
522df930be7Sderaadt 			while (*cp && *cp != ' ')
523df930be7Sderaadt 				cp++;
524df930be7Sderaadt 			if (!*cp)
525df930be7Sderaadt 				break;
526df930be7Sderaadt 			*cp++ = '\0';
527df930be7Sderaadt 			person = cp;
528df930be7Sderaadt 			while (*cp) {
529df930be7Sderaadt 				if (*cp != ' ') {
530df930be7Sderaadt 					cp++;
531df930be7Sderaadt 					continue;
532df930be7Sderaadt 				}
533df930be7Sderaadt 				*cp++ = '\0';
5340c4db8c1Sderaadt 				while (isspace((unsigned char)*cp))
535df930be7Sderaadt 					cp++;
536df930be7Sderaadt 				if (*cp == '\0')
537df930be7Sderaadt 					break;
5380c4db8c1Sderaadt 				if (isdigit((unsigned char)*cp)) {
539df930be7Sderaadt 					if (requests >= MAXREQUESTS)
540df930be7Sderaadt 						fatal("Too many requests");
541df930be7Sderaadt 					requ[requests++] = atoi(cp);
542df930be7Sderaadt 				} else {
543df930be7Sderaadt 					if (users >= MAXUSERS)
544df930be7Sderaadt 						fatal("Too many users");
545df930be7Sderaadt 					user[users++] = cp;
546df930be7Sderaadt 				}
547df930be7Sderaadt 			}
548df930be7Sderaadt 			rmjob();
549df930be7Sderaadt 			break;
550df930be7Sderaadt 		}
551df930be7Sderaadt 		fatal("Illegal service request");
552df930be7Sderaadt 	}
553df930be7Sderaadt }
554df930be7Sderaadt 
555df930be7Sderaadt /*
556df930be7Sderaadt  * Make a pass through the printcap database and start printing any
557df930be7Sderaadt  * files left from the last time the machine went down.
558df930be7Sderaadt  */
559df930be7Sderaadt static void
startup(void)560a7643117Smillert startup(void)
561df930be7Sderaadt {
5626468ba68Smillert 	char *buf, *cp;
563df930be7Sderaadt 
564df930be7Sderaadt 	/*
565df930be7Sderaadt 	 * Restart the daemons.
566df930be7Sderaadt 	 */
567df930be7Sderaadt 	while (cgetnext(&buf, printcapdb) > 0) {
5683b62b2c3Stholo 		if (ckqueue(buf) <= 0) {
5693b62b2c3Stholo 			free(buf);
5703b62b2c3Stholo 			continue;	/* no work to do for this printer */
5713b62b2c3Stholo 		}
572df930be7Sderaadt 		for (cp = buf; *cp; cp++)
573df930be7Sderaadt 			if (*cp == '|' || *cp == ':') {
574df930be7Sderaadt 				*cp = '\0';
575df930be7Sderaadt 				break;
576df930be7Sderaadt 			}
5773b62b2c3Stholo 		if (lflag)
5783b62b2c3Stholo 			syslog(LOG_INFO, "work for %s", buf);
579a7643117Smillert 		switch (fork()) {
580a7643117Smillert 		case -1:
581df930be7Sderaadt 			syslog(LOG_WARNING, "startup: cannot fork");
582df930be7Sderaadt 			mcleanup(0);
583a7643117Smillert 			/* NOTREACHED */
584a7643117Smillert 		case 0:
585df930be7Sderaadt 			printer = buf;
586a7643117Smillert 			setproctitle("working on printer %s", printer);
587df930be7Sderaadt 			cgetclose();
588df930be7Sderaadt 			printjob();
5893b62b2c3Stholo 			/* NOTREACHED */
590a7643117Smillert 		default:
591a7643117Smillert 			child_count++;
592a7643117Smillert 			free(buf);
5933b62b2c3Stholo 		}
594df930be7Sderaadt 	}
595df930be7Sderaadt }
5963b62b2c3Stholo 
5973b62b2c3Stholo /*
598df930be7Sderaadt  * Check to see if the from host has access to the line printer.
599df930be7Sderaadt  */
600df930be7Sderaadt static void
chkhost(struct sockaddr * f)6018d66a5d9Sstevesk chkhost(struct sockaddr *f)
602df930be7Sderaadt {
603a7643117Smillert 	struct addrinfo hints, *res, *r;
604c1624b2fSmillert 	FILE *hostf;
60564cd2c7eSderaadt 	int good = 0;
606a7643117Smillert 	char host[NI_MAXHOST], ip[NI_MAXHOST];
607a7643117Smillert 	char serv[NI_MAXSERV];
608a7643117Smillert 	int error;
609a7643117Smillert 
610a7643117Smillert 	error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv),
611a7643117Smillert 	    NI_NUMERICSERV);
612a7643117Smillert 	if (error)
613a7643117Smillert 		fatal("Malformed from address");
614a7643117Smillert 
615df930be7Sderaadt 	/* Need real hostname for temporary filenames */
616a7643117Smillert 	error = getnameinfo(f, f->sa_len, host, sizeof(host), NULL, 0,
617a7643117Smillert 	    NI_NAMEREQD);
618a7643117Smillert 	if (error) {
619a7643117Smillert 		error = getnameinfo(f, f->sa_len, host, sizeof(host), NULL, 0,
620a7643117Smillert 		    NI_NUMERICHOST);
621a7643117Smillert 		if (error)
622a7643117Smillert 			fatal("Host name for your address unknown");
623a7643117Smillert 		else
624a7643117Smillert 			fatal("Host name for your address (%s) unknown", host);
625a7643117Smillert 	}
626df930be7Sderaadt 
627a7643117Smillert 	(void)strlcpy(fromb, host, sizeof(fromb));
628df930be7Sderaadt 	from = fromb;
629df930be7Sderaadt 
630a7643117Smillert 	/* need address in stringform for comparison (no DNS lookup here) */
631a7643117Smillert 	error = getnameinfo(f, f->sa_len, host, sizeof(host), NULL, 0,
632a7643117Smillert 	    NI_NUMERICHOST);
633a7643117Smillert 	if (error)
634a7643117Smillert 		fatal("Cannot print address");
635a7643117Smillert 
63664cd2c7eSderaadt 	/* Check for spoof, ala rlogind */
637a7643117Smillert 	memset(&hints, 0, sizeof(hints));
638a7643117Smillert 	hints.ai_family = PF_UNSPEC;
639a7643117Smillert 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
640a7643117Smillert 	error = getaddrinfo(fromb, NULL, &hints, &res);
641a7643117Smillert 	if (error) {
642a7643117Smillert 		fatal("hostname for your address (%s) unknown: %s", host,
643a7643117Smillert 		    gai_strerror(error));
644a7643117Smillert 	}
645a7643117Smillert 	for (good = 0, r = res; good == 0 && r; r = r->ai_next) {
646a7643117Smillert 		error = getnameinfo(r->ai_addr, r->ai_addrlen, ip, sizeof(ip),
647a7643117Smillert 		    NULL, 0, NI_NUMERICHOST);
648a7643117Smillert 		if (!error && !strcmp(host, ip))
64964cd2c7eSderaadt 			good = 1;
65064cd2c7eSderaadt 	}
651a7643117Smillert 	if (res)
652a7643117Smillert 		freeaddrinfo(res);
65364cd2c7eSderaadt 	if (good == 0)
654a7643117Smillert 		fatal("address for your hostname (%s) not matched", host);
655a7643117Smillert 	setproctitle("serving %s", from);
6566468ba68Smillert 	PRIV_START;
657acafcefcSajacoutot 	hostf = fopen(_PATH_HOSTSLPD, "r");
6586468ba68Smillert 	PRIV_END;
659df930be7Sderaadt 	if (hostf) {
6601e18bebcSmillert 		if (allowedhost(hostf, f, f->sa_len) == 0) {
661df930be7Sderaadt 			(void)fclose(hostf);
662df930be7Sderaadt 			return;
663df930be7Sderaadt 		}
664df930be7Sderaadt 		(void)fclose(hostf);
6658785ab1eSderaadt 		fatal("Your host does not have line printer access (/etc/hosts.lpd)");
6668785ab1eSderaadt 	} else
6678785ab1eSderaadt 		fatal("Your host does not have line printer access (no /etc/hosts.lpd)");
668df930be7Sderaadt }
669a7643117Smillert 
670a7643117Smillert static __dead void
usage(void)671a7643117Smillert usage(void)
672a7643117Smillert {
673a7643117Smillert 	extern char *__progname;
674a7643117Smillert 
6758d66a5d9Sstevesk 	fprintf(stderr, "usage: %s [-dlrs] [-b bind-address] [-n maxchild] "
676a7643117Smillert 	    "[-w maxwait] [port]\n", __progname);
677a7643117Smillert 	exit(1);
678a7643117Smillert }
679a7643117Smillert 
680a7643117Smillert /*
681a7643117Smillert  * Setup server socket for specified address family.
682a7643117Smillert  * If af is PF_UNSPEC more than one socket may be returned.
683a7643117Smillert  * The returned list is dynamically allocated, so the caller needs to free it.
684a7643117Smillert  */
685a7643117Smillert int *
socksetup(int af,int options,const char * port)686a7643117Smillert socksetup(int af, int options, const char *port)
687a7643117Smillert {
688a7643117Smillert 	struct addrinfo hints, *res, *r;
68932a5de2eSitojun 	int error, maxs = 0, *s, *socks = NULL, *newsocks, blidx = 0;
690a7643117Smillert 	const int on = 1;
691a7643117Smillert 
692a7643117Smillert 	do {
693a7643117Smillert 		memset(&hints, 0, sizeof(hints));
694a7643117Smillert 		hints.ai_flags = AI_PASSIVE;
695a7643117Smillert 		hints.ai_family = af;
696a7643117Smillert 		hints.ai_socktype = SOCK_STREAM;
697a7643117Smillert 		error = getaddrinfo((blist_addrs == 0) ? NULL : blist[blidx],
698a7643117Smillert 		    port ? port : "printer", &hints, &res);
699a7643117Smillert 		if (error) {
700a7643117Smillert 			if (blist_addrs)
701a7643117Smillert 				syslog(LOG_ERR, "%s: %s", blist[blidx],
702a7643117Smillert 				    gai_strerror(error));
703a7643117Smillert 			else
704a7643117Smillert 				syslog(LOG_ERR, "%s", gai_strerror(error));
705a7643117Smillert 			mcleanup(0);
706a7643117Smillert 		}
707a7643117Smillert 
708a7643117Smillert 		/* Count max number of sockets we may open */
709a7643117Smillert 		for (r = res; r; r = r->ai_next, maxs++)
710a7643117Smillert 			;
711a7643117Smillert 		if (socks == NULL) {
7121ed98fdfSderaadt 			socks = calloc(maxs + 1, sizeof(int));
713a7643117Smillert 			if (socks)
714a7643117Smillert 				*socks = 0; /* num of sockets ctr at start */
71532a5de2eSitojun 		} else {
716a6d758a8Sdoug 			newsocks = reallocarray(socks, maxs + 1, sizeof(int));
71732a5de2eSitojun 			if (newsocks)
71832a5de2eSitojun 				socks = newsocks;
71932a5de2eSitojun 			else {
72032a5de2eSitojun 				free(socks);
72132a5de2eSitojun 				socks = NULL;
72232a5de2eSitojun 			}
72332a5de2eSitojun 		}
724a7643117Smillert 		if (!socks) {
725a7643117Smillert 			syslog(LOG_ERR, "couldn't allocate memory for sockets");
726a7643117Smillert 			mcleanup(0);
727a7643117Smillert 		}
728a7643117Smillert 
729a7643117Smillert 		s = socks + *socks + 1;
730a7643117Smillert 		for (r = res; r; r = r->ai_next) {
731a7643117Smillert 			*s = socket(r->ai_family, r->ai_socktype,
732a7643117Smillert 			            r->ai_protocol);
733a7643117Smillert 			if (*s < 0) {
734a7643117Smillert 				syslog(LOG_DEBUG, "socket(): %m");
735a7643117Smillert 				continue;
736a7643117Smillert 			}
737a7643117Smillert 			if (options & SO_DEBUG)
738a7643117Smillert 				if (setsockopt(*s, SOL_SOCKET, SO_DEBUG,
739a7643117Smillert 					       &on, sizeof(on)) < 0) {
740a7643117Smillert 					syslog(LOG_ERR,
741a7643117Smillert 					       "setsockopt (SO_DEBUG): %m");
742a7643117Smillert 					close (*s);
743a7643117Smillert 					continue;
744a7643117Smillert 				}
7456468ba68Smillert 			PRIV_START;
7466468ba68Smillert 			error = bind(*s, r->ai_addr, r->ai_addrlen);
7476468ba68Smillert 			PRIV_END;
7486468ba68Smillert 			if (error < 0) {
749a7643117Smillert 				syslog(LOG_DEBUG, "bind(): %m");
750a7643117Smillert 				close (*s);
751a7643117Smillert 				continue;
752a7643117Smillert 			}
753a7643117Smillert 			*socks = *socks + 1;
754a7643117Smillert 			s++;
755a7643117Smillert 		}
756a7643117Smillert 
757a7643117Smillert 		if (res)
758a7643117Smillert 			freeaddrinfo(res);
759a7643117Smillert 	} while (++blidx < blist_addrs);
760a7643117Smillert 
761a7643117Smillert 	if (socks == NULL || *socks == 0) {
762a7643117Smillert 		syslog(LOG_ERR, "Couldn't bind to any socket");
763a7643117Smillert 		free(socks);
764a7643117Smillert 		mcleanup(0);
765a7643117Smillert 	}
766a7643117Smillert 	return(socks);
767a7643117Smillert }
768