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