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