File:  [DragonFly] / src / usr.sbin / lpr / lpd / lpd.c
Revision 1.4: download - view: text, annotated - select for diffs
Mon Mar 22 22:32:50 2004 UTC (10 years, 8 months ago) by cpressey
Branches: MAIN
CVS tags: HEAD, DragonFly_Snap29Sep2004, DragonFly_Snap13Sep2004, DragonFly_1_0_REL, DragonFly_1_0_RC1, DragonFly_1_0A_REL
Style(9) cleanup.

- Remove ``register'' keywords and adjust a comment referencing them.
- Remove formfeeds.
- *argv[] -> **argv

    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.4 2004/03/22 22:32:50 cpressey 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, (char *)0);
  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: 	(void) 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: 	(void) 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: 	(void) 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: 	(void) umask(0);
  331: 	sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
  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: 			(void) close(funix);
  399: 			if (sflag == 0 && finet) {
  400:                         	for (i = 1; i <= *finet; i++) 
  401: 					(void)close(finet[i]);
  402: 			}
  403: 			dup2(s, 1);
  404: 			(void) 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: 		(void) 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: 			(void) fclose(hostf);
  757: 			goto foundhost;
  758: 		}
  759: 		(void) 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: }