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