1 /*        $NetBSD: lpd.c,v 1.60 2023/04/26 18:25:02 kre Exp $         */
2 
3 /*
4  * Copyright (c) 1983, 1993, 1994
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 
35 #ifndef lint
36 __COPYRIGHT("@(#) Copyright (c) 1983, 1993, 1994\
37  The Regents of the University of California.  All rights reserved.");
38 #endif /* not lint */
39 
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)lpd.c       8.7 (Berkeley) 5/10/95";
43 #else
44 __RCSID("$NetBSD: lpd.c,v 1.60 2023/04/26 18:25:02 kre Exp $");
45 #endif
46 #endif /* not lint */
47 
48 /*
49  * lpd -- line printer daemon.
50  *
51  * Listen for a connection and perform the requested operation.
52  * Operations are:
53  *        \1printer\n
54  *                  check the queue for jobs and print any found.
55  *        \2printer\n
56  *                  receive a job from another machine and queue it.
57  *        \3printer [users ...] [jobs ...]\n
58  *                  return the current state of the queue (short form).
59  *        \4printer [users ...] [jobs ...]\n
60  *                  return the current state of the queue (long form).
61  *        \5printer person [users ...] [jobs ...]\n
62  *                  remove jobs from the queue.
63  *
64  * Strategy to maintain protected spooling area:
65  *        1. Spooling area is writable only by daemon and spooling group
66  *        2. lpr runs setuid root and setgrp spooling group; it uses
67  *           root to access any file it wants (verifying things before
68  *           with an access call) and group id to know how it should
69  *           set up ownership of files in the spooling area.
70  *        3. Files in spooling area are owned by root, group spooling
71  *           group, with mode 660.
72  *        4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
73  *           access files and printer.  Users can't get to anything
74  *           w/o help of lpq and lprm programs.
75  */
76 
77 #include <sys/param.h>
78 #include <sys/wait.h>
79 #include <sys/types.h>
80 #include <sys/socket.h>
81 #include <sys/un.h>
82 #include <sys/stat.h>
83 #include <sys/file.h>
84 #include <sys/poll.h>
85 #include <netinet/in.h>
86 
87 #include <err.h>
88 #include <netdb.h>
89 #include <unistd.h>
90 #include <syslog.h>
91 #include <signal.h>
92 #include <errno.h>
93 #include <fcntl.h>
94 #include <dirent.h>
95 #include <stdarg.h>
96 #include <stdio.h>
97 #include <stdlib.h>
98 #include <string.h>
99 #include <ctype.h>
100 #include <arpa/inet.h>
101 
102 #ifdef LIBWRAP
103 #include <tcpd.h>
104 #endif
105 
106 #include "lp.h"
107 #include "lp.local.h"
108 #include "pathnames.h"
109 #include "extern.h"
110 
111 /* XXX from libc/net/rcmd.c */
112 extern int __ivaliduser_sa(FILE *, const struct sockaddr *, socklen_t,
113                                  const char *, const char *);
114 
115 #ifdef LIBWRAP
116 int allow_severity = LOG_AUTH|LOG_INFO;
117 int deny_severity = LOG_AUTH|LOG_WARNING;
118 #endif
119 
120 int       lflag;                                  /* log requests flag */
121 int       rflag;                                  /* allow of for remote printers */
122 int       sflag;                                  /* secure (no inet) flag */
123 int       from_remote;                            /* from remote socket */
124 char      **blist;                      /* list of addresses to bind(2) to */
125 int       blist_size;
126 int       blist_addrs;
127 
128 static void                   reapchild(int);
129 __dead static void  mcleanup(int);
130 static void                   doit(void);
131 static void                   startup(void);
132 static void                   chkhost(struct sockaddr *, int);
133 __dead static void  usage(void);
134 static struct pollfd          *socksetup(int, int, const char *, int *);
135 static void                   chkplushost(int, FILE *, char*);
136 
137 uid_t     uid, euid;
138 int child_count;
139 
140 #define LPD_NOPORTCHK         0001                /* skip reserved-port check */
141 
142 int
main(int argc,char ** argv)143 main(int argc, char **argv)
144 {
145           struct sockaddr_storage frm;
146           socklen_t frmlen;
147           sigset_t nmask, omask;
148           int lfd, errs, i, f, nfds;
149           struct pollfd *socks;
150           int child_max = 32; /* more than enough to hose the system */
151           int options = 0, check_options = 0;
152           struct servent *sp;
153           const char *port = "printer";
154           char **newblist;
155 
156           euid = geteuid();   /* these shouldn't be different */
157           uid = getuid();
158           gethostname(host, sizeof(host));
159           host[sizeof(host) - 1] = '\0';
160           setprogname(*argv);
161 
162           errs = 0;
163           while ((i = getopt(argc, argv, "b:dln:srw:W")) != -1)
164                     switch (i) {
165                     case 'b':
166                               if (blist_addrs >= blist_size) {
167                                         newblist = realloc(blist,
168                                             blist_size + sizeof(char *) * 4);
169                                         if (newblist == NULL)
170                                                   err(1, "cant allocate bind addr list");
171                                         blist = newblist;
172                                         blist_size += sizeof(char *) * 4;
173                               }
174                               blist[blist_addrs++] = strdup(optarg);
175                               break;
176                     case 'd':
177                               options |= SO_DEBUG;
178                               break;
179                     case 'l':
180                               lflag++;
181                               break;
182                     case 'n':
183                               child_max = atoi(optarg);
184                               if (child_max < 0 || child_max > 1024)
185                                         errx(1, "invalid number of children: %s",
186                                             optarg);
187                               break;
188                     case 'r':
189                               rflag++;
190                               break;
191                     case 's':
192                               sflag++;
193                               break;
194                     case 'w':
195                               wait_time = atoi(optarg);
196                               if (wait_time < 0)
197                                         errx(1, "wait time must be positive: %s",
198                                             optarg);
199                               if (wait_time < 30)
200                                   warnx("warning: wait time less than 30 seconds");
201                               break;
202                     case 'W':/* allow connections coming from a non-reserved port */
203                                /* (done by some lpr-implementations for MS-Windows) */
204                               check_options |= LPD_NOPORTCHK;
205                               break;
206                     default:
207                               errs++;
208                     }
209           argc -= optind;
210           argv += optind;
211           if (errs)
212                     usage();
213 
214           switch (argc) {
215           case 1:
216                     if ((i = atoi(argv[0])) == 0)
217                               usage();
218                     if (i < 0 || i > USHRT_MAX)
219                               errx(1, "port # %d is invalid", i);
220 
221                     port = argv[0];
222                     break;
223           case 0:
224                     sp = getservbyname(port, "tcp");
225                     if (sp == NULL)
226                               errx(1, "%s/tcp: unknown service", port);
227                     break;
228           default:
229                     usage();
230           }
231 
232 #ifndef DEBUG
233           /*
234            * Set up standard environment by detaching from the parent.
235            */
236           daemon(0, 0);
237 #endif
238 
239           openlog("lpd", LOG_PID, LOG_LPR);
240           syslog(LOG_INFO, "restarted");
241           (void)umask(0);
242           lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT, 0644);
243           if (lfd < 0) {
244                     syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
245                     exit(1);
246           }
247           if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
248                     if (errno == EWOULDBLOCK) {   /* active daemon present */
249                               syslog(LOG_ERR, "%s is locked; another lpd is running",
250                                   _PATH_MASTERLOCK);
251                               exit(0);
252                     }
253                     syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
254                     exit(1);
255           }
256           ftruncate(lfd, 0);
257           /*
258            * write process id for others to know
259            */
260           (void)snprintf(line, sizeof(line), "%u\n", getpid());
261           f = strlen(line);
262           if (write(lfd, line, f) != f) {
263                     syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
264                     exit(1);
265           }
266           signal(SIGCHLD, reapchild);
267           /*
268            * Restart all the printers.
269            */
270           startup();
271 
272           sigemptyset(&nmask);
273           sigaddset(&nmask, SIGHUP);
274           sigaddset(&nmask, SIGINT);
275           sigaddset(&nmask, SIGQUIT);
276           sigaddset(&nmask, SIGTERM);
277           sigprocmask(SIG_BLOCK, &nmask, &omask);
278 
279           signal(SIGHUP, mcleanup);
280           signal(SIGINT, mcleanup);
281           signal(SIGQUIT, mcleanup);
282           signal(SIGTERM, mcleanup);
283 
284           socks = socksetup(PF_UNSPEC, options, port, &nfds);
285 
286           sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
287 
288           if (blist != NULL) {
289                     for (i = 0; i < blist_addrs; i++)
290                               free(blist[i]);
291                     free(blist);
292           }
293 
294           /*
295            * Main loop: accept, do a request, continue.
296            */
297           memset(&frm, 0, sizeof(frm));
298           for (;;) {
299                     int rv, s;
300                     /* "short" so it overflows in about 2 hours */
301                     struct timespec sleeptime = {10, 0};
302 
303                     while (child_max < child_count) {
304                               syslog(LOG_WARNING,
305                                   "too many children, sleeping for %ld seconds",
306                                         (long)sleeptime.tv_sec);
307                               nanosleep(&sleeptime, NULL);
308                               sleeptime.tv_sec <<= 1;
309                               if (sleeptime.tv_sec <= 0) {
310                                         syslog(LOG_CRIT, "sleeptime overflowed! help!");
311                                         sleeptime.tv_sec = 10;
312                               }
313                     }
314 
315                     rv = poll(socks, nfds, INFTIM);
316                     if (rv <= 0) {
317                               if (rv < 0 && errno != EINTR)
318                                         syslog(LOG_WARNING, "poll: %m");
319                               continue;
320                     }
321                     s = -1;
322                 for (i = 0; i < nfds; i++)
323                               if (socks[i].revents & POLLIN) {
324                                         frmlen = sizeof(frm);
325                                         s = accept(socks[i].fd,
326                                             (struct sockaddr *)&frm, &frmlen);
327                                         break;
328                               }
329                     if (s < 0) {
330                               if (errno != EINTR)
331                                         syslog(LOG_WARNING, "accept: %m");
332                               continue;
333                     }
334 
335                     switch (fork()) {
336                     case 0:
337                               signal(SIGCHLD, SIG_DFL);
338                               signal(SIGHUP, SIG_IGN);
339                               signal(SIGINT, SIG_IGN);
340                               signal(SIGQUIT, SIG_IGN);
341                               signal(SIGTERM, SIG_IGN);
342                               for (i = 0; i < nfds; i++)
343                                         (void)close(socks[i].fd);
344                               dup2(s, STDOUT_FILENO);
345                               (void)close(s);
346                               if (frm.ss_family != AF_LOCAL) {
347                                         /* for both AF_INET and AF_INET6 */
348                                         from_remote = 1;
349                                         chkhost((struct sockaddr *)&frm, check_options);
350                               } else
351                                         from_remote = 0;
352                               doit();
353                               exit(0);
354                     case -1:
355                               syslog(LOG_WARNING, "fork: %m, sleeping for 10 seconds...");
356                               sleep(10);
357                               continue;
358                     default:
359                               child_count++;
360                     }
361                     (void)close(s);
362           }
363 }
364 
365 /*
366  * If there was a forward/backward name resolution mismatch, check
367  * that there's a '+' entry in fhost.
368  */
369 
370 void
chkplushost(int good,FILE * fhost,char * hst)371 chkplushost(int good, FILE *fhost, char *hst)
372 {
373           int c1, c2, c3;
374 
375           if (good) {
376                     return;
377           }
378 
379           rewind(fhost);
380           while (EOF != (c1 = fgetc(fhost))) {
381                     if (c1 == '+') {
382                               c2 = fgetc(fhost);
383                               if (c2 == ' ' || c2 == '\t' || c2 == '\n') {
384                                         return;
385                               }
386                     }
387                     do {
388                               c3 = fgetc(fhost);
389                     } while (c3 != EOF && c3 != '\n');
390           }
391           fatal("address for your hostname (%s) not matched", hst);
392 }
393 
394 static void
reapchild(int signo)395 reapchild(int signo)
396 {
397           int status;
398 
399           while (wait3(&status, WNOHANG, 0) > 0)
400                     child_count--;
401 }
402 
403 static void
mcleanup(int signo)404 mcleanup(int signo)
405 {
406           if (lflag)
407                     syslog(LOG_INFO, "exiting");
408           unlink(_PATH_SOCKETNAME);
409           exit(0);
410 }
411 
412 /*
413  * Stuff for handling job specifications
414  */
415 char      *user[MAXUSERS];    /* users to process */
416 int       users;                        /* # of users in user array */
417 int       requ[MAXREQUESTS];  /* job number of spool entries */
418 int       requests;           /* # of spool requests */
419 char      *person;            /* name of person doing lprm */
420 
421 char      fromb[NI_MAXHOST];  /* buffer for client's machine name */
422 char      cbuf[BUFSIZ];                 /* command line buffer */
423 const char *cmdnames[] = {
424           "null",
425           "printjob",
426           "recvjob",
427           "displayq short",
428           "displayq long",
429           "rmjob"
430 };
431 
432 static void
doit(void)433 doit(void)
434 {
435           char *cp;
436           int n;
437 
438           for (;;) {
439                     cp = cbuf;
440                     do {
441                               if (cp >= &cbuf[sizeof(cbuf) - 1])
442                                         fatal("Command line too long");
443                               if ((n = read(STDOUT_FILENO, cp, 1)) != 1) {
444                                         if (n < 0)
445                                                   fatal("Lost connection");
446                                         return;
447                               }
448                     } while (*cp++ != '\n');
449                     *--cp = '\0';
450                     cp = cbuf;
451                     if (lflag) {
452                               if (*cp >= '\1' && *cp <= '\5') {
453                                         syslog(LOG_INFO, "%s requests %s %s",
454                                                   from, cmdnames[(int)*cp], cp+1);
455                                         setproctitle("serving %s: %s %s", from,
456                                             cmdnames[(int)*cp], cp+1);
457                               }
458                               else
459                                         syslog(LOG_INFO, "bad request (%d) from %s",
460                                                   *cp, from);
461                     }
462                     switch (*cp++) {
463                     case '\1':          /* check the queue and print any jobs there */
464                               printer = cp;
465                               if (*printer == '\0')
466                                         printer = DEFLP;
467                               printjob();
468                               break;
469                     case '\2':          /* receive files to be queued */
470                               if (!from_remote) {
471                                         syslog(LOG_INFO, "illegal request (%d)", *cp);
472                                         exit(1);
473                               }
474                               printer = cp;
475                               if (*printer == '\0')
476                                         printer = DEFLP;
477                               recvjob();
478                               break;
479                     case '\3':          /* display the queue (short form) */
480                     case '\4':          /* display the queue (long form) */
481                               printer = cp;
482                               if (*printer == '\0')
483                                         printer = DEFLP;
484                               while (*cp) {
485                                         if (*cp != ' ') {
486                                                   cp++;
487                                                   continue;
488                                         }
489                                         *cp++ = '\0';
490                                         while (isspace((unsigned char)*cp))
491                                                   cp++;
492                                         if (*cp == '\0')
493                                                   break;
494                                         if (isdigit((unsigned char)*cp)) {
495                                                   if (requests >= MAXREQUESTS)
496                                                             fatal("Too many requests");
497                                                   requ[requests++] = atoi(cp);
498                                         } else {
499                                                   if (users >= MAXUSERS)
500                                                             fatal("Too many users");
501                                                   user[users++] = cp;
502                                         }
503                               }
504                               displayq(cbuf[0] - '\3');
505                               exit(0);
506                     case '\5':          /* remove a job from the queue */
507                               if (!from_remote) {
508                                         syslog(LOG_INFO, "illegal request (%d)", *cp);
509                                         exit(1);
510                               }
511                               printer = cp;
512                               if (*printer == '\0')
513                                         printer = DEFLP;
514                               while (*cp && *cp != ' ')
515                                         cp++;
516                               if (!*cp)
517                                         break;
518                               *cp++ = '\0';
519                               person = cp;
520                               while (*cp) {
521                                         if (*cp != ' ') {
522                                                   cp++;
523                                                   continue;
524                                         }
525                                         *cp++ = '\0';
526                                         while (isspace((unsigned char)*cp))
527                                                   cp++;
528                                         if (*cp == '\0')
529                                                   break;
530                                         if (isdigit((unsigned char)*cp)) {
531                                                   if (requests >= MAXREQUESTS)
532                                                             fatal("Too many requests");
533                                                   requ[requests++] = atoi(cp);
534                                         } else {
535                                                   if (users >= MAXUSERS)
536                                                             fatal("Too many users");
537                                                   user[users++] = cp;
538                                         }
539                               }
540                               rmjob();
541                               break;
542                     }
543                     fatal("Illegal service request");
544           }
545 }
546 
547 /*
548  * Make a pass through the printcap database and start printing any
549  * files left from the last time the machine went down.
550  */
551 static void
startup(void)552 startup(void)
553 {
554           char *buf;
555           char *cp;
556 
557           /*
558            * Restart the daemons.
559            */
560           while (cgetnext(&buf, printcapdb) > 0) {
561                     if (ckqueue(buf) <= 0) {
562                               free(buf);
563                               continue; /* no work to do for this printer */
564                     }
565                     for (cp = buf; *cp; cp++)
566                               if (*cp == '|' || *cp == ':') {
567                                         *cp = '\0';
568                                         break;
569                               }
570                     if (lflag)
571                               syslog(LOG_INFO, "work for %s", buf);
572                     switch (fork()) {
573                     case -1:
574                               syslog(LOG_WARNING, "startup: cannot fork");
575                               mcleanup(0);
576                     case 0:
577                               printer = buf;
578                               setproctitle("working on printer %s", printer);
579                               cgetclose();
580                               printjob();
581                               /* NOTREACHED */
582                     default:
583                               child_count++;
584                               free(buf);
585                     }
586           }
587 }
588 
589 #define DUMMY ":nobody::"
590 
591 /*
592  * Check to see if the from host has access to the line printer.
593  */
594 static void
chkhost(struct sockaddr * f,int check_opts)595 chkhost(struct sockaddr *f, int check_opts)
596 {
597           struct addrinfo hints, *res, *r;
598           FILE *hostf;
599           int good = 0;
600           char hst[NI_MAXHOST], ip[NI_MAXHOST];
601           char serv[NI_MAXSERV];
602           int error;
603 #ifdef LIBWRAP
604           struct request_info req;
605 #endif
606 
607           error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv),
608                                   NI_NUMERICSERV);
609           if (error)
610                     fatal("Malformed from address: %s", gai_strerror(error));
611 
612          if (!(check_opts & LPD_NOPORTCHK) &&
613                  atoi(serv) >= IPPORT_RESERVED)
614                     fatal("Connect from invalid port (%s)", serv);
615 
616           /* Need real hostname for temporary filenames */
617           error = getnameinfo(f, f->sa_len, hst, sizeof(hst), NULL, 0,
618                                   NI_NAMEREQD);
619           if (error) {
620                     error = getnameinfo(f, f->sa_len, hst, sizeof(hst), NULL, 0,
621                                             NI_NUMERICHOST);
622                     if (error)
623                               fatal("Host name for your address unknown");
624                     else
625                               fatal("Host name for your address (%s) unknown", hst);
626           }
627 
628           (void)strlcpy(fromb, hst, sizeof(fromb));
629           from = fromb;
630 
631           /* need address in stringform for comparison (no DNS lookup here) */
632           error = getnameinfo(f, f->sa_len, hst, sizeof(hst), NULL, 0,
633                                   NI_NUMERICHOST);
634           if (error)
635                     fatal("Cannot print address");
636 
637           /* Check for spoof, ala rlogind */
638           good = 0;
639           memset(&hints, 0, sizeof(hints));
640           hints.ai_family = PF_UNSPEC;
641           hints.ai_socktype = SOCK_DGRAM;         /*dummy*/
642           error = getaddrinfo(fromb, NULL, &hints, &res);
643           if (!error) {
644                     for (r = res; good == 0 && r; r = r->ai_next) {
645                               error = getnameinfo(r->ai_addr, r->ai_addrlen,
646                                             ip, sizeof(ip), NULL, 0, NI_NUMERICHOST);
647                               if (!error && !strcmp(hst, ip))
648                                         good = 1;
649                     }
650                     if (res)
651                               freeaddrinfo(res);
652           }
653 
654           /* complain about !good later in chkplushost if needed. */
655 
656           setproctitle("serving %s", from);
657 
658 #ifdef LIBWRAP
659           request_init(&req, RQ_DAEMON, "lpd", RQ_CLIENT_SIN, f,
660               RQ_FILE, STDOUT_FILENO, NULL);
661           fromhost(&req);
662           if (!hosts_access(&req))
663                     goto denied;
664 #endif
665 
666           hostf = fopen(_PATH_HOSTSEQUIV, "r");
667           if (hostf) {
668                     if (__ivaliduser_sa(hostf, f, f->sa_len, DUMMY, DUMMY) == 0) {
669                               chkplushost(good, hostf, hst);
670                               (void)fclose(hostf);
671                               return;
672                     }
673                     (void)fclose(hostf);
674           }
675           hostf = fopen(_PATH_HOSTSLPD, "r");
676           if (hostf) {
677                     if (__ivaliduser_sa(hostf, f, f->sa_len, DUMMY, DUMMY) == 0) {
678                               chkplushost(good, hostf, hst);
679                               (void)fclose(hostf);
680                               return;
681                     }
682                     (void)fclose(hostf);
683           }
684 #ifdef LIBWRAP
685   denied:
686 #endif
687           fatal("Your host does not have line printer access");
688           /*NOTREACHED*/
689 }
690 
691 
692 static void
usage(void)693 usage(void)
694 {
695 
696           (void)fprintf(stderr,
697               "Usage: %s [-dlrsW] [-b bind-address] [-n maxchild] "
698               "[-w maxwait] [port]\n", getprogname());
699           exit(1);
700 }
701 
702 /* setup server socket for specified address family */
703 /* if af is PF_UNSPEC more than one socket may be returned */
704 /* the returned list is dynamically allocated, so caller needs to free it */
705 struct pollfd *
socksetup(int af,int options,const char * port,int * nfds)706 socksetup(int af, int options, const char *port, int *nfds)
707 {
708           struct sockaddr_un un;
709           struct addrinfo hints, *res, *r;
710           int error, s, blidx = 0, n;
711           struct pollfd *socks, *newsocks;
712           const int on = 1;
713 
714           *nfds = 0;
715 
716           socks = malloc(1 * sizeof(socks[0]));
717           if (!socks) {
718                     syslog(LOG_ERR, "couldn't allocate memory for sockets");
719                     mcleanup(0);
720           }
721 
722           s = socket(AF_LOCAL, SOCK_STREAM, 0);
723           if (s < 0) {
724                     syslog(LOG_ERR, "socket(): %m");
725                     exit(1);
726           }
727           memset(&un, 0, sizeof(un));
728           un.sun_family = AF_LOCAL;
729           strncpy(un.sun_path, _PATH_SOCKETNAME, sizeof(un.sun_path) - 1);
730           un.sun_len = SUN_LEN(&un);
731           (void)umask(07);
732           (void)unlink(_PATH_SOCKETNAME);
733           if (bind(s, (struct sockaddr *)&un, un.sun_len) < 0) {
734                     syslog(LOG_ERR, "bind(): %m");
735                     exit(1);
736           }
737           (void)umask(0);
738           listen(s, 5);
739           socks[*nfds].fd = s;
740           socks[*nfds].events = POLLIN;
741           (*nfds)++;
742 
743           if (sflag && !blist_addrs)
744                     return (socks);
745 
746           do {
747                     memset(&hints, 0, sizeof(hints));
748                     hints.ai_flags = AI_PASSIVE;
749                     hints.ai_family = af;
750                     hints.ai_socktype = SOCK_STREAM;
751                     error = getaddrinfo((blist_addrs == 0) ? NULL : blist[blidx],
752                         port ? port : "printer", &hints, &res);
753                     if (error) {
754                               if (blist_addrs)
755                                         syslog(LOG_ERR, "%s: %s", blist[blidx],
756                                             gai_strerror(error));
757                               else
758                                         syslog(LOG_ERR, "%s", gai_strerror(error));
759                               mcleanup(0);
760                     }
761 
762                     /* Count max number of sockets we may open */
763                     for (r = res, n = 0; r; r = r->ai_next, n++)
764                               ;
765                     newsocks = realloc(socks, (*nfds + n) * sizeof(socks[0]));
766                     if (!newsocks) {
767                               syslog(LOG_ERR, "couldn't allocate memory for sockets");
768                               mcleanup(0);
769                     }
770                     socks = newsocks;
771 
772                     for (r = res; r; r = r->ai_next) {
773                               s = socket(r->ai_family, r->ai_socktype,
774                                   r->ai_protocol);
775                               if (s < 0) {
776                                         syslog(LOG_DEBUG, "socket(): %m");
777                                         continue;
778                               }
779                               if (options & SO_DEBUG)
780                                         if (setsockopt(s, SOL_SOCKET, SO_DEBUG,
781                                                          &on, sizeof(on)) < 0) {
782                                                   syslog(LOG_ERR,
783                                                          "setsockopt (SO_DEBUG): %m");
784                                                   close(s);
785                                                   continue;
786                                         }
787                               if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &on,
788                                   sizeof(on)) < 0) {
789                                         syslog(LOG_ERR,
790                                             "setsockopt (SO_REUSEPORT): %m");
791                                         close(s);
792                                         continue;
793                               }
794                               if (r->ai_family == AF_INET6 && setsockopt(s,
795                                   IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
796                                         syslog(LOG_ERR,
797                                             "setsockopt (IPV6_V6ONLY): %m");
798                                         close(s);
799                                         continue;
800                               }
801                               if (bind(s, r->ai_addr, r->ai_addrlen) < 0) {
802                                         syslog(LOG_DEBUG, "bind(): %m");
803                                         close(s);
804                                         continue;
805                               }
806                               listen(s, 5);
807                               socks[*nfds].fd = s;
808                               socks[*nfds].events = POLLIN;
809                               (*nfds)++;
810                     }
811 
812                     if (res)
813                               freeaddrinfo(res);
814           } while (++blidx < blist_addrs);
815 
816           return (socks);
817 }
818