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