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