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