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