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