1331722Seadler/* 21553Srgrimes * Copyright (c) 1983, 1993, 1994 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * 61553Srgrimes * Redistribution and use in source and binary forms, with or without 71553Srgrimes * modification, are permitted provided that the following conditions 81553Srgrimes * are met: 91553Srgrimes * 1. Redistributions of source code must retain the above copyright 101553Srgrimes * notice, this list of conditions and the following disclaimer. 111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121553Srgrimes * notice, this list of conditions and the following disclaimer in the 131553Srgrimes * documentation and/or other materials provided with the distribution. 141553Srgrimes * 4. Neither the name of the University nor the names of its contributors 151553Srgrimes * may be used to endorse or promote products derived from this software 161553Srgrimes * without specific prior written permission. 171553Srgrimes * 181553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 191553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 201553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 211553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 221553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 231553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 241553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 251553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 261553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 271553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 281553Srgrimes * SUCH DAMAGE. 291553Srgrimes */ 301553Srgrimes 311553Srgrimes#ifndef lint 3229780Scharnierstatic const char copyright[] = 331553Srgrimes"@(#) Copyright (c) 1983, 1993, 1994\n\ 341553Srgrimes The Regents of the University of California. All rights reserved.\n"; 351553Srgrimes#endif /* not lint */ 361553Srgrimes 37117280Scharnier#if 0 38117587Sgad#ifndef lint 3915637Sjoergstatic char sccsid[] = "@(#)lpd.c 8.7 (Berkeley) 5/10/95"; 40117587Sgad#endif /* not lint */ 4129780Scharnier#endif 421553Srgrimes 43117554Sgad#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 44117280Scharnier__FBSDID("$FreeBSD$"); 45117280Scharnier 461553Srgrimes/* 471553Srgrimes * lpd -- line printer daemon. 481553Srgrimes * 491553Srgrimes * Listen for a connection and perform the requested operation. 501553Srgrimes * Operations are: 511553Srgrimes * \1printer\n 521553Srgrimes * check the queue for jobs and print any found. 531553Srgrimes * \2printer\n 541553Srgrimes * receive a job from another machine and queue it. 551553Srgrimes * \3printer [users ...] [jobs ...]\n 561553Srgrimes * return the current state of the queue (short form). 571553Srgrimes * \4printer [users ...] [jobs ...]\n 581553Srgrimes * return the current state of the queue (long form). 591553Srgrimes * \5printer person [users ...] [jobs ...]\n 601553Srgrimes * remove jobs from the queue. 611553Srgrimes * 621553Srgrimes * Strategy to maintain protected spooling area: 631553Srgrimes * 1. Spooling area is writable only by daemon and spooling group 641553Srgrimes * 2. lpr runs setuid root and setgrp spooling group; it uses 651553Srgrimes * root to access any file it wants (verifying things before 661553Srgrimes * with an access call) and group id to know how it should 671553Srgrimes * set up ownership of files in the spooling area. 681553Srgrimes * 3. Files in spooling area are owned by root, group spooling 691553Srgrimes * group, with mode 660. 701553Srgrimes * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to 711553Srgrimes * access files and printer. Users can't get to anything 721553Srgrimes * w/o help of lpq and lprm programs. 731553Srgrimes */ 741553Srgrimes 751553Srgrimes#include <sys/param.h> 761553Srgrimes#include <sys/wait.h> 771553Srgrimes#include <sys/types.h> 781553Srgrimes#include <sys/socket.h> 791553Srgrimes#include <sys/un.h> 801553Srgrimes#include <sys/stat.h> 8115637Sjoerg#include <sys/file.h> 821553Srgrimes#include <netinet/in.h> 8315703Sjoerg#include <arpa/inet.h> 841553Srgrimes 851553Srgrimes#include <netdb.h> 861553Srgrimes#include <unistd.h> 871553Srgrimes#include <syslog.h> 881553Srgrimes#include <signal.h> 8928621Sjoerg#include <err.h> 901553Srgrimes#include <errno.h> 911553Srgrimes#include <fcntl.h> 921553Srgrimes#include <dirent.h> 931553Srgrimes#include <stdio.h> 941553Srgrimes#include <stdlib.h> 951553Srgrimes#include <string.h> 9628621Sjoerg#include <sysexits.h> 971553Srgrimes#include <ctype.h> 981553Srgrimes#include "lp.h" 991553Srgrimes#include "lp.local.h" 1001553Srgrimes#include "pathnames.h" 1011553Srgrimes#include "extern.h" 1021553Srgrimes 1031553Srgrimesint lflag; /* log requests flag */ 10495070Sgadint sflag; /* no incoming port flag */ 1051553Srgrimesint from_remote; /* from remote socket */ 1061553Srgrimes 10778146Sgadint main(int argc, char **_argv); 10878146Sgadstatic void reapchild(int _signo); 10978146Sgadstatic void mcleanup(int _signo); 11078146Sgadstatic void doit(void); 11178146Sgadstatic void startup(void); 11278749Sgadstatic void chkhost(struct sockaddr *_f, int _ch_opts); 11378146Sgadstatic int ckqueue(struct printer *_pp); 11498776Sgadstatic void fhosterr(int _ch_opts, char *_sysmsg, char *_usermsg); 11578302Sgadstatic int *socksetup(int _af, int _debuglvl); 11678146Sgadstatic void usage(void); 1171553Srgrimes 11870098Sume/* XXX from libc/net/rcmd.c */ 119173412Skevloextern int __ivaliduser_sa(FILE *, struct sockaddr *, socklen_t, 120173412Skevlo const char *, const char *); 12170098Sume 12227618Simpuid_t uid, euid; 12327618Simp 12478749Sgad#define LPD_NOPORTCHK 0001 /* skip reserved-port check */ 12578749Sgad#define LPD_LOGCONNERR 0002 /* (sys)log connection errors */ 12698776Sgad#define LPD_ADDFROMLINE 0004 /* just used for fhosterr() */ 12778749Sgad 1281553Srgrimesint 12978146Sgadmain(int argc, char **argv) 1301553Srgrimes{ 13180170Sgad int ch_options, errs, f, funix, *finet, i, lfd, socket_debug; 1321553Srgrimes fd_set defreadfds; 1331553Srgrimes struct sockaddr_un un, fromunix; 13470098Sume struct sockaddr_storage frominet; 13580170Sgad socklen_t fromlen; 13631492Swollman sigset_t omask, nmask; 13728621Sjoerg struct servent *sp, serv; 13870098Sume int inet_flag = 0, inet6_flag = 0; 1391553Srgrimes 14027618Simp euid = geteuid(); /* these shouldn't be different */ 14127618Simp uid = getuid(); 14278749Sgad 14378749Sgad ch_options = 0; 14468742Sgad socket_debug = 0; 14578300Sgad gethostname(local_host, sizeof(local_host)); 1461553Srgrimes 14778280Sgad progname = "lpd"; 14827618Simp 14928621Sjoerg if (euid != 0) 15028621Sjoerg errx(EX_NOPERM,"must run as root"); 15127618Simp 15228621Sjoerg errs = 0; 15395070Sgad while ((i = getopt(argc, argv, "cdlpswW46")) != -1) 15428621Sjoerg switch (i) { 15578749Sgad case 'c': 15678749Sgad /* log all kinds of connection-errors to syslog */ 15778749Sgad ch_options |= LPD_LOGCONNERR; 15878749Sgad break; 15928621Sjoerg case 'd': 16068742Sgad socket_debug++; 16128621Sjoerg break; 16228621Sjoerg case 'l': 16328621Sjoerg lflag++; 16428621Sjoerg break; 16595070Sgad case 'p': /* letter initially used for -s */ 16695070Sgad /* 16795070Sgad * This will probably be removed with 5.0-release. 16895070Sgad */ 16995070Sgad /* FALLTHROUGH */ 17095070Sgad case 's': /* secure (no inet) */ 17195070Sgad sflag++; 17258777Ssheldonh break; 17380122Sgad case 'w': /* netbsd uses -w for maxwait */ 17480122Sgad /* 17580122Sgad * This will be removed after the release of 4.4, as 17680122Sgad * it conflicts with -w in netbsd's lpd. For now it 17780122Sgad * is just a warning, so we won't suddenly break lpd 17880122Sgad * for anyone who is currently using the option. 17980122Sgad */ 18080122Sgad syslog(LOG_WARNING, 18180122Sgad "NOTE: the -w option has been renamed -W"); 18280122Sgad syslog(LOG_WARNING, 18380122Sgad "NOTE: please change your lpd config to use -W"); 18480122Sgad /* FALLTHROUGH */ 18580122Sgad case 'W': 18678749Sgad /* allow connections coming from a non-reserved port */ 18778749Sgad /* (done by some lpr-implementations for MS-Windows) */ 18878749Sgad ch_options |= LPD_NOPORTCHK; 18978749Sgad break; 19070098Sume case '4': 19170098Sume family = PF_INET; 19270098Sume inet_flag++; 19370098Sume break; 19470098Sume case '6': 19574127Sgad#ifdef INET6 19670098Sume family = PF_INET6; 19770098Sume inet6_flag++; 19874127Sgad#else 19974127Sgad errx(EX_USAGE, "lpd compiled sans INET6 (IPv6 support)"); 20074127Sgad#endif 20170098Sume break; 20280122Sgad /* 20380122Sgad * The following options are not in FreeBSD (yet?), but are 20480122Sgad * listed here to "reserve" them, because the option-letters 20580122Sgad * are used by either NetBSD or OpenBSD (as of July 2001). 20680122Sgad */ 20780122Sgad case 'b': /* set bind-addr */ 20880122Sgad case 'n': /* set max num of children */ 20980122Sgad case 'r': /* allow 'of' for remote ptrs */ 21080122Sgad /* ...[not needed in freebsd] */ 21180122Sgad /* FALLTHROUGH */ 21228621Sjoerg default: 21328621Sjoerg errs++; 21428621Sjoerg } 21570098Sume if (inet_flag && inet6_flag) 21670098Sume family = PF_UNSPEC; 21728621Sjoerg argc -= optind; 21828621Sjoerg argv += optind; 21928621Sjoerg if (errs) 22028621Sjoerg usage(); 22128621Sjoerg 22228621Sjoerg if (argc == 1) { 22328621Sjoerg if ((i = atoi(argv[0])) == 0) 22428621Sjoerg usage(); 22528621Sjoerg if (i < 0 || i > USHRT_MAX) 22628621Sjoerg errx(EX_USAGE, "port # %d is invalid", i); 22728621Sjoerg 22828621Sjoerg serv.s_port = htons(i); 22928621Sjoerg sp = &serv; 23028621Sjoerg argc--; 23128621Sjoerg } else { 23228621Sjoerg sp = getservbyname("printer", "tcp"); 23328621Sjoerg if (sp == NULL) 23428621Sjoerg errx(EX_OSFILE, "printer/tcp: unknown service"); 2351553Srgrimes } 2361553Srgrimes 23728621Sjoerg if (argc != 0) 23828621Sjoerg usage(); 23928621Sjoerg 24031492Swollman /* 24131492Swollman * We run chkprintcap right away to catch any errors and blat them 24231492Swollman * to stderr while we still have it open, rather than sending them 24331492Swollman * to syslog and leaving the user wondering why lpd started and 24431492Swollman * then stopped. There should probably be a command-line flag to 24531492Swollman * ignore errors from chkprintcap. 24631492Swollman */ 24731492Swollman { 24831492Swollman pid_t pid; 24931492Swollman int status; 25031492Swollman pid = fork(); 25131492Swollman if (pid < 0) { 25231492Swollman err(EX_OSERR, "cannot fork"); 25331492Swollman } else if (pid == 0) { /* child */ 25431492Swollman execl(_PATH_CHKPRINTCAP, _PATH_CHKPRINTCAP, (char *)0); 25531492Swollman err(EX_OSERR, "cannot execute %s", _PATH_CHKPRINTCAP); 25631492Swollman } 25731492Swollman if (waitpid(pid, &status, 0) < 0) { 25831492Swollman err(EX_OSERR, "cannot wait"); 25931492Swollman } 26031492Swollman if (WIFEXITED(status) && WEXITSTATUS(status) != 0) 26131492Swollman errx(EX_OSFILE, "%d errors in printcap file, exiting", 26231492Swollman WEXITSTATUS(status)); 26331492Swollman } 26431492Swollman 2651553Srgrimes#ifndef DEBUG 2661553Srgrimes /* 2671553Srgrimes * Set up standard environment by detaching from the parent. 2681553Srgrimes */ 2691553Srgrimes daemon(0, 0); 2701553Srgrimes#endif 2711553Srgrimes 2721553Srgrimes openlog("lpd", LOG_PID, LOG_LPR); 27395070Sgad syslog(LOG_INFO, "lpd startup: logging=%d%s%s", lflag, 27495070Sgad socket_debug ? " dbg" : "", sflag ? " net-secure" : ""); 2751553Srgrimes (void) umask(0); 27631492Swollman /* 27731492Swollman * NB: This depends on O_NONBLOCK semantics doing the right thing; 27831492Swollman * i.e., applying only to the O_EXLOCK and not to the rest of the 27931492Swollman * open/creation. As of 1997-12-02, this is the case for commonly- 28031492Swollman * used filesystems. There are other places in this code which 28131492Swollman * make the same assumption. 28231492Swollman */ 28331492Swollman lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK, 28431492Swollman LOCK_FILE_MODE); 2851553Srgrimes if (lfd < 0) { 28695068Sgad if (errno == EWOULDBLOCK) /* active daemon present */ 2871553Srgrimes exit(0); 2881553Srgrimes syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); 2891553Srgrimes exit(1); 2901553Srgrimes } 29131492Swollman fcntl(lfd, F_SETFL, 0); /* turn off non-blocking mode */ 2921553Srgrimes ftruncate(lfd, 0); 2931553Srgrimes /* 2941553Srgrimes * write process id for others to know 2951553Srgrimes */ 2961553Srgrimes sprintf(line, "%u\n", getpid()); 2971553Srgrimes f = strlen(line); 2981553Srgrimes if (write(lfd, line, f) != f) { 2991553Srgrimes syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK); 3001553Srgrimes exit(1); 3011553Srgrimes } 3021553Srgrimes signal(SIGCHLD, reapchild); 3031553Srgrimes /* 3041553Srgrimes * Restart all the printers. 3051553Srgrimes */ 3061553Srgrimes startup(); 3071553Srgrimes (void) unlink(_PATH_SOCKETNAME); 3081553Srgrimes funix = socket(AF_UNIX, SOCK_STREAM, 0); 3091553Srgrimes if (funix < 0) { 3101553Srgrimes syslog(LOG_ERR, "socket: %m"); 3111553Srgrimes exit(1); 3121553Srgrimes } 31331492Swollman 31431492Swollman sigemptyset(&nmask); 31531492Swollman sigaddset(&nmask, SIGHUP); 31631492Swollman sigaddset(&nmask, SIGINT); 31731492Swollman sigaddset(&nmask, SIGQUIT); 31831492Swollman sigaddset(&nmask, SIGTERM); 31931492Swollman sigprocmask(SIG_BLOCK, &nmask, &omask); 32031492Swollman 32119202Simp (void) umask(07); 3221553Srgrimes signal(SIGHUP, mcleanup); 3231553Srgrimes signal(SIGINT, mcleanup); 3241553Srgrimes signal(SIGQUIT, mcleanup); 3251553Srgrimes signal(SIGTERM, mcleanup); 3261553Srgrimes memset(&un, 0, sizeof(un)); 3271553Srgrimes un.sun_family = AF_UNIX; 3281553Srgrimes strcpy(un.sun_path, _PATH_SOCKETNAME); 3291553Srgrimes#ifndef SUN_LEN 3301553Srgrimes#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2) 3311553Srgrimes#endif 3321553Srgrimes if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) { 3331553Srgrimes syslog(LOG_ERR, "ubind: %m"); 3341553Srgrimes exit(1); 3351553Srgrimes } 33619202Simp (void) umask(0); 33731492Swollman sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0); 3381553Srgrimes FD_ZERO(&defreadfds); 3391553Srgrimes FD_SET(funix, &defreadfds); 3401553Srgrimes listen(funix, 5); 34195070Sgad if (sflag == 0) { 34278302Sgad finet = socksetup(family, socket_debug); 34370098Sume } else 34470098Sume finet = NULL; /* pretend we couldn't open TCP socket. */ 34570098Sume if (finet) { 34670098Sume for (i = 1; i <= *finet; i++) { 34770098Sume FD_SET(finet[i], &defreadfds); 34870098Sume listen(finet[i], 5); 3491553Srgrimes } 3501553Srgrimes } 3511553Srgrimes /* 3521553Srgrimes * Main loop: accept, do a request, continue. 3531553Srgrimes */ 3541553Srgrimes memset(&frominet, 0, sizeof(frominet)); 3551553Srgrimes memset(&fromunix, 0, sizeof(fromunix)); 35668742Sgad if (lflag) 35768742Sgad syslog(LOG_INFO, "lpd startup: ready to accept requests"); 35831492Swollman /* 35931492Swollman * XXX - should be redone for multi-protocol 36031492Swollman */ 3611553Srgrimes for (;;) { 36278302Sgad int domain, nfds, s; 3631553Srgrimes fd_set readfds; 3641553Srgrimes 3651553Srgrimes FD_COPY(&defreadfds, &readfds); 3661553Srgrimes nfds = select(20, &readfds, 0, 0, 0); 3671553Srgrimes if (nfds <= 0) { 3681553Srgrimes if (nfds < 0 && errno != EINTR) 3691553Srgrimes syslog(LOG_WARNING, "select: %m"); 3701553Srgrimes continue; 3711553Srgrimes } 37278302Sgad domain = -1; /* avoid compile-time warning */ 37378302Sgad s = -1; /* avoid compile-time warning */ 3741553Srgrimes if (FD_ISSET(funix, &readfds)) { 3751553Srgrimes domain = AF_UNIX, fromlen = sizeof(fromunix); 3761553Srgrimes s = accept(funix, 3771553Srgrimes (struct sockaddr *)&fromunix, &fromlen); 37870098Sume } else { 37970098Sume for (i = 1; i <= *finet; i++) 38070098Sume if (FD_ISSET(finet[i], &readfds)) { 38170098Sume domain = AF_INET; 38270098Sume fromlen = sizeof(frominet); 38370098Sume s = accept(finet[i], 38470098Sume (struct sockaddr *)&frominet, 38570098Sume &fromlen); 38670098Sume } 3871553Srgrimes } 3881553Srgrimes if (s < 0) { 3891553Srgrimes if (errno != EINTR) 3901553Srgrimes syslog(LOG_WARNING, "accept: %m"); 3911553Srgrimes continue; 3921553Srgrimes } 3931553Srgrimes if (fork() == 0) { 39479735Sgad /* 39579735Sgad * Note that printjob() also plays around with 39679735Sgad * signal-handling routines, and may need to be 39779735Sgad * changed when making changes to signal-handling. 39879735Sgad */ 39979735Sgad signal(SIGCHLD, SIG_DFL); 4001553Srgrimes signal(SIGHUP, SIG_IGN); 4011553Srgrimes signal(SIGINT, SIG_IGN); 4021553Srgrimes signal(SIGQUIT, SIG_IGN); 4031553Srgrimes signal(SIGTERM, SIG_IGN); 4041553Srgrimes (void) close(funix); 40595070Sgad if (sflag == 0 && finet) { 40670098Sume for (i = 1; i <= *finet; i++) 40770098Sume (void)close(finet[i]); 40858777Ssheldonh } 409118881Sgad dup2(s, STDOUT_FILENO); 4101553Srgrimes (void) close(s); 4111553Srgrimes if (domain == AF_INET) { 41270098Sume /* for both AF_INET and AF_INET6 */ 4131553Srgrimes from_remote = 1; 41478749Sgad chkhost((struct sockaddr *)&frominet, 41578749Sgad ch_options); 4161553Srgrimes } else 4171553Srgrimes from_remote = 0; 4181553Srgrimes doit(); 4191553Srgrimes exit(0); 4201553Srgrimes } 4211553Srgrimes (void) close(s); 4221553Srgrimes } 4231553Srgrimes} 4241553Srgrimes 4251553Srgrimesstatic void 42678146Sgadreapchild(int signo __unused) 4271553Srgrimes{ 42897781Sgad int status; 4291553Srgrimes 43097781Sgad while (wait3(&status, WNOHANG, 0) > 0) 4311553Srgrimes ; 4321553Srgrimes} 4331553Srgrimes 4341553Srgrimesstatic void 43578146Sgadmcleanup(int signo) 4361553Srgrimes{ 43766414Swollman /* 43866414Swollman * XXX syslog(3) is not signal-safe. 43966414Swollman */ 44066414Swollman if (lflag) { 44166414Swollman if (signo) 44266414Swollman syslog(LOG_INFO, "exiting on signal %d", signo); 44366414Swollman else 44466414Swollman syslog(LOG_INFO, "exiting"); 44566414Swollman } 4461553Srgrimes unlink(_PATH_SOCKETNAME); 4471553Srgrimes exit(0); 4481553Srgrimes} 4491553Srgrimes 4501553Srgrimes/* 4511553Srgrimes * Stuff for handling job specifications 4521553Srgrimes */ 4531553Srgrimeschar *user[MAXUSERS]; /* users to process */ 4541553Srgrimesint users; /* # of users in user array */ 4551553Srgrimesint requ[MAXREQUESTS]; /* job number of spool entries */ 4561553Srgrimesint requests; /* # of spool requests */ 4571553Srgrimeschar *person; /* name of person doing lprm */ 4581553Srgrimes 45978300Sgad /* buffer to hold the client's machine-name */ 46078300Sgadstatic char frombuf[MAXHOSTNAMELEN]; 4611553Srgrimeschar cbuf[BUFSIZ]; /* command line buffer */ 46278146Sgadconst char *cmdnames[] = { 4631553Srgrimes "null", 4641553Srgrimes "printjob", 4651553Srgrimes "recvjob", 4661553Srgrimes "displayq short", 4671553Srgrimes "displayq long", 4681553Srgrimes "rmjob" 4691553Srgrimes}; 4701553Srgrimes 4711553Srgrimesstatic void 47278146Sgaddoit(void) 4731553Srgrimes{ 47431492Swollman char *cp, *printer; 47531492Swollman int n; 47631492Swollman int status; 47731492Swollman struct printer myprinter, *pp = &myprinter; 4781553Srgrimes 47931492Swollman init_printer(&myprinter); 48031492Swollman 4811553Srgrimes for (;;) { 4821553Srgrimes cp = cbuf; 4831553Srgrimes do { 4841553Srgrimes if (cp >= &cbuf[sizeof(cbuf) - 1]) 48531492Swollman fatal(0, "Command line too long"); 48680113Sgad if ((n = read(STDOUT_FILENO, cp, 1)) != 1) { 4871553Srgrimes if (n < 0) 48831492Swollman fatal(0, "Lost connection"); 4891553Srgrimes return; 4901553Srgrimes } 4911553Srgrimes } while (*cp++ != '\n'); 4921553Srgrimes *--cp = '\0'; 4931553Srgrimes cp = cbuf; 4941553Srgrimes if (lflag) { 4951553Srgrimes if (*cp >= '\1' && *cp <= '\5') 4961553Srgrimes syslog(LOG_INFO, "%s requests %s %s", 49778300Sgad from_host, cmdnames[(u_char)*cp], cp+1); 4981553Srgrimes else 4991553Srgrimes syslog(LOG_INFO, "bad request (%d) from %s", 50078300Sgad *cp, from_host); 5011553Srgrimes } 5021553Srgrimes switch (*cp++) { 50331492Swollman case CMD_CHECK_QUE: /* check the queue, print any jobs there */ 50431492Swollman startprinting(cp); 5051553Srgrimes break; 50631492Swollman case CMD_TAKE_THIS: /* receive files to be queued */ 5071553Srgrimes if (!from_remote) { 5081553Srgrimes syslog(LOG_INFO, "illegal request (%d)", *cp); 5091553Srgrimes exit(1); 5101553Srgrimes } 51131492Swollman recvjob(cp); 5121553Srgrimes break; 51331492Swollman case CMD_SHOWQ_SHORT: /* display the queue (short form) */ 51431492Swollman case CMD_SHOWQ_LONG: /* display the queue (long form) */ 51531492Swollman /* XXX - this all needs to be redone. */ 5161553Srgrimes printer = cp; 5171553Srgrimes while (*cp) { 5181553Srgrimes if (*cp != ' ') { 5191553Srgrimes cp++; 5201553Srgrimes continue; 5211553Srgrimes } 5221553Srgrimes *cp++ = '\0'; 5231553Srgrimes while (isspace(*cp)) 5241553Srgrimes cp++; 5251553Srgrimes if (*cp == '\0') 5261553Srgrimes break; 5271553Srgrimes if (isdigit(*cp)) { 5281553Srgrimes if (requests >= MAXREQUESTS) 52931492Swollman fatal(0, "Too many requests"); 5301553Srgrimes requ[requests++] = atoi(cp); 5311553Srgrimes } else { 5321553Srgrimes if (users >= MAXUSERS) 53331492Swollman fatal(0, "Too many users"); 5341553Srgrimes user[users++] = cp; 5351553Srgrimes } 5361553Srgrimes } 53731492Swollman status = getprintcap(printer, pp); 53831492Swollman if (status < 0) 53979739Sgad fatal(pp, "%s", pcaperr(status)); 54031492Swollman displayq(pp, cbuf[0] == CMD_SHOWQ_LONG); 5411553Srgrimes exit(0); 54231492Swollman case CMD_RMJOB: /* remove a job from the queue */ 5431553Srgrimes if (!from_remote) { 5441553Srgrimes syslog(LOG_INFO, "illegal request (%d)", *cp); 5451553Srgrimes exit(1); 5461553Srgrimes } 5471553Srgrimes printer = cp; 5481553Srgrimes while (*cp && *cp != ' ') 5491553Srgrimes cp++; 5501553Srgrimes if (!*cp) 5511553Srgrimes break; 5521553Srgrimes *cp++ = '\0'; 5531553Srgrimes person = cp; 5541553Srgrimes while (*cp) { 5551553Srgrimes if (*cp != ' ') { 5561553Srgrimes cp++; 5571553Srgrimes continue; 5581553Srgrimes } 5591553Srgrimes *cp++ = '\0'; 5601553Srgrimes while (isspace(*cp)) 5611553Srgrimes cp++; 5621553Srgrimes if (*cp == '\0') 5631553Srgrimes break; 5641553Srgrimes if (isdigit(*cp)) { 5651553Srgrimes if (requests >= MAXREQUESTS) 56631492Swollman fatal(0, "Too many requests"); 5671553Srgrimes requ[requests++] = atoi(cp); 5681553Srgrimes } else { 5691553Srgrimes if (users >= MAXUSERS) 57031492Swollman fatal(0, "Too many users"); 5711553Srgrimes user[users++] = cp; 5721553Srgrimes } 5731553Srgrimes } 57431492Swollman rmjob(printer); 5751553Srgrimes break; 5761553Srgrimes } 57731492Swollman fatal(0, "Illegal service request"); 5781553Srgrimes } 5791553Srgrimes} 5801553Srgrimes 5811553Srgrimes/* 5821553Srgrimes * Make a pass through the printcap database and start printing any 5831553Srgrimes * files left from the last time the machine went down. 5841553Srgrimes */ 5851553Srgrimesstatic void 58678146Sgadstartup(void) 5871553Srgrimes{ 58831492Swollman int pid, status, more; 58931492Swollman struct printer myprinter, *pp = &myprinter; 5901553Srgrimes 59131492Swollman more = firstprinter(pp, &status); 59231492Swollman if (status) 59331492Swollman goto errloop; 59431492Swollman while (more) { 59531492Swollman if (ckqueue(pp) <= 0) { 59631492Swollman goto next; 59725856Sbrian } 59815637Sjoerg if (lflag) 59968742Sgad syslog(LOG_INFO, "lpd startup: work for %s", 60068742Sgad pp->printer); 6011553Srgrimes if ((pid = fork()) < 0) { 60268742Sgad syslog(LOG_WARNING, "lpd startup: cannot fork for %s", 60368742Sgad pp->printer); 6041553Srgrimes mcleanup(0); 6051553Srgrimes } 60631492Swollman if (pid == 0) { 60731492Swollman lastprinter(); 60831492Swollman printjob(pp); 60915637Sjoerg /* NOTREACHED */ 6101553Srgrimes } 61131492Swollman do { 61231492Swollmannext: 61331492Swollman more = nextprinter(pp, &status); 61431492Swollmanerrloop: 61531492Swollman if (status) 61631492Swollman syslog(LOG_WARNING, 61768742Sgad "lpd startup: printcap entry for %s has errors, skipping", 61878317Sgad pp->printer ? pp->printer : "<noname?>"); 61931492Swollman } while (more && status); 6201553Srgrimes } 6211553Srgrimes} 6221553Srgrimes 62315637Sjoerg/* 62415637Sjoerg * Make sure there's some work to do before forking off a child 62515637Sjoerg */ 62615637Sjoergstatic int 62778146Sgadckqueue(struct printer *pp) 62815637Sjoerg{ 62915637Sjoerg register struct dirent *d; 63015637Sjoerg DIR *dirp; 63115637Sjoerg char *spooldir; 63215637Sjoerg 63331492Swollman spooldir = pp->spool_dir; 63415637Sjoerg if ((dirp = opendir(spooldir)) == NULL) 63515637Sjoerg return (-1); 63615637Sjoerg while ((d = readdir(dirp)) != NULL) { 63715637Sjoerg if (d->d_name[0] != 'c' || d->d_name[1] != 'f') 63815637Sjoerg continue; /* daemon control files only */ 63915637Sjoerg closedir(dirp); 64015637Sjoerg return (1); /* found something */ 64115637Sjoerg } 64215637Sjoerg closedir(dirp); 64315637Sjoerg return (0); 64415637Sjoerg} 64515637Sjoerg 6461553Srgrimes#define DUMMY ":nobody::" 6471553Srgrimes 6481553Srgrimes/* 64978749Sgad * Check to see if the host connecting to this host has access to any 65078749Sgad * lpd services on this host. 6511553Srgrimes */ 6521553Srgrimesstatic void 65378749Sgadchkhost(struct sockaddr *f, int ch_opts) 6541553Srgrimes{ 65570098Sume struct addrinfo hints, *res, *r; 6561553Srgrimes register FILE *hostf; 65778300Sgad char hostbuf[NI_MAXHOST], ip[NI_MAXHOST]; 65870098Sume char serv[NI_MAXSERV]; 65998776Sgad char *syserr, *usererr; 660298592Sgad int error, errsav, fpass, good; 6611553Srgrimes 66278749Sgad from_host = ".na."; 66378749Sgad 6641553Srgrimes /* Need real hostname for temporary filenames */ 66578300Sgad error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, 66678300Sgad NI_NAMEREQD); 66770098Sume if (error) { 66878749Sgad errsav = error; 66978300Sgad error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), 670146188Sume NULL, 0, NI_NUMERICHOST); 67198776Sgad if (error) { 67298776Sgad asprintf(&syserr, 67398776Sgad "can not determine hostname for remote host (%d,%d)", 67498776Sgad errsav, error); 67598776Sgad asprintf(&usererr, 67698776Sgad "Host name for your address is not known"); 67798776Sgad fhosterr(ch_opts, syserr, usererr); 67898776Sgad /* NOTREACHED */ 67998776Sgad } 68098776Sgad asprintf(&syserr, 68198776Sgad "Host name for remote host (%s) not known (%d)", 68298776Sgad hostbuf, errsav); 68398776Sgad asprintf(&usererr, 68498776Sgad "Host name for your address (%s) is not known", 68598776Sgad hostbuf); 68698776Sgad fhosterr(ch_opts, syserr, usererr); 68798776Sgad /* NOTREACHED */ 68870098Sume } 6891553Srgrimes 69078300Sgad strlcpy(frombuf, hostbuf, sizeof(frombuf)); 69178300Sgad from_host = frombuf; 69298776Sgad ch_opts |= LPD_ADDFROMLINE; 69370098Sume 69470098Sume /* Need address in stringform for comparison (no DNS lookup here) */ 69578300Sgad error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0, 696146188Sume NI_NUMERICHOST); 69798776Sgad if (error) { 69898776Sgad asprintf(&syserr, "Cannot print IP address (error %d)", 69998776Sgad error); 70098776Sgad asprintf(&usererr, "Cannot print IP address for your host"); 70198776Sgad fhosterr(ch_opts, syserr, usererr); 70298776Sgad /* NOTREACHED */ 70398776Sgad } 70478300Sgad from_ip = strdup(hostbuf); 7051553Srgrimes 70670098Sume /* Reject numeric addresses */ 70770098Sume memset(&hints, 0, sizeof(hints)); 70870098Sume hints.ai_family = family; 70970098Sume hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 71070098Sume hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; 71178300Sgad if (getaddrinfo(from_host, NULL, &hints, &res) == 0) { 71270098Sume freeaddrinfo(res); 71398776Sgad /* This syslog message already includes from_host */ 71498776Sgad ch_opts &= ~LPD_ADDFROMLINE; 71598776Sgad asprintf(&syserr, "reverse lookup results in non-FQDN %s", 71678749Sgad from_host); 71798776Sgad /* same message to both syslog and remote user */ 71898776Sgad fhosterr(ch_opts, syserr, syserr); 71998776Sgad /* NOTREACHED */ 72070098Sume } 72170098Sume 72219202Simp /* Check for spoof, ala rlogind */ 72370098Sume memset(&hints, 0, sizeof(hints)); 72470098Sume hints.ai_family = family; 72570098Sume hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 72678300Sgad error = getaddrinfo(from_host, NULL, &hints, &res); 72770098Sume if (error) { 72898776Sgad asprintf(&syserr, "dns lookup for address %s failed: %s", 72998776Sgad from_ip, gai_strerror(error)); 73098776Sgad asprintf(&usererr, "hostname for your address (%s) unknown: %s", 73198776Sgad from_ip, gai_strerror(error)); 73298776Sgad fhosterr(ch_opts, syserr, usererr); 73398776Sgad /* NOTREACHED */ 73470098Sume } 73570098Sume good = 0; 73670098Sume for (r = res; good == 0 && r; r = r->ai_next) { 73770098Sume error = getnameinfo(r->ai_addr, r->ai_addrlen, ip, sizeof(ip), 738146188Sume NULL, 0, NI_NUMERICHOST); 73970098Sume if (!error && !strcmp(from_ip, ip)) 74019202Simp good = 1; 74119202Simp } 74270098Sume if (res) 74370098Sume freeaddrinfo(res); 74498776Sgad if (good == 0) { 74598776Sgad asprintf(&syserr, "address for remote host (%s) not matched", 74698776Sgad from_ip); 74798776Sgad asprintf(&usererr, 74878749Sgad "address for your hostname (%s) not matched", from_ip); 74998776Sgad fhosterr(ch_opts, syserr, usererr); 75098776Sgad /* NOTREACHED */ 75198776Sgad } 75219202Simp 75378749Sgad fpass = 1; 7541553Srgrimes hostf = fopen(_PATH_HOSTSEQUIV, "r"); 7551553Srgrimesagain: 7561553Srgrimes if (hostf) { 75770098Sume if (__ivaliduser_sa(hostf, f, f->sa_len, DUMMY, DUMMY) == 0) { 7581553Srgrimes (void) fclose(hostf); 75978749Sgad goto foundhost; 7601553Srgrimes } 7611553Srgrimes (void) fclose(hostf); 7621553Srgrimes } 76378749Sgad if (fpass == 1) { 76478749Sgad fpass = 2; 7651553Srgrimes hostf = fopen(_PATH_HOSTSLPD, "r"); 7661553Srgrimes goto again; 7671553Srgrimes } 76898776Sgad /* This syslog message already includes from_host */ 76998776Sgad ch_opts &= ~LPD_ADDFROMLINE; 77098776Sgad asprintf(&syserr, "refused connection from %s, sip=%s", from_host, 77178749Sgad from_ip); 77298776Sgad asprintf(&usererr, 77398776Sgad "Print-services are not available to your host (%s).", from_host); 77498776Sgad fhosterr(ch_opts, syserr, usererr); 77598776Sgad /* NOTREACHED */ 77678749Sgad 77778749Sgadfoundhost: 77878749Sgad if (ch_opts & LPD_NOPORTCHK) 77978749Sgad return; /* skip the reserved-port check */ 78078749Sgad 78178749Sgad error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv), 78278749Sgad NI_NUMERICSERV); 78398776Sgad if (error) { 78498776Sgad /* same message to both syslog and remote user */ 78598776Sgad asprintf(&syserr, "malformed from-address (%d)", error); 78698776Sgad fhosterr(ch_opts, syserr, syserr); 78798776Sgad /* NOTREACHED */ 78898776Sgad } 78978749Sgad 79098776Sgad if (atoi(serv) >= IPPORT_RESERVED) { 79198776Sgad /* same message to both syslog and remote user */ 79298776Sgad asprintf(&syserr, "connected from invalid port (%s)", serv); 79398776Sgad fhosterr(ch_opts, syserr, syserr); 79498776Sgad /* NOTREACHED */ 79598776Sgad } 7961553Srgrimes} 79728621Sjoerg 79878749Sgad/* 79998776Sgad * Handle fatal errors in chkhost. The first message will optionally be 80098776Sgad * sent to syslog, the second one is sent to the connecting host. 80178749Sgad * 80278749Sgad * The idea is that the syslog message is meant for an administrator of a 80378749Sgad * print server (the host receiving connections), while the usermsg is meant 80478749Sgad * for a remote user who may or may not be clueful, and may or may not be 80578749Sgad * doing something nefarious. Some remote users (eg, MS-Windows...) may not 80678749Sgad * even see whatever message is sent, which is why there's the option to 80778749Sgad * start 'lpd' with the connection-errors also sent to syslog. 80878749Sgad * 80978749Sgad * Given that hostnames can theoretically be fairly long (well, over 250 81078749Sgad * bytes), it would probably be helpful to have the 'from_host' field at 81178749Sgad * the end of any error messages which include that info. 81298776Sgad * 81398776Sgad * These are Fatal host-connection errors, so this routine does not return. 81478749Sgad */ 81598776Sgadstatic void 81698776Sgadfhosterr(int ch_opts, char *sysmsg, char *usermsg) 81778749Sgad{ 81878749Sgad 81998776Sgad /* 82098776Sgad * If lpd was started up to print connection errors, then write 82198776Sgad * the syslog message before the user message. 82298776Sgad * And for many of the syslog messages, it is helpful to first 82398776Sgad * write the from_host (if it is known) as a separate syslog 82498776Sgad * message, since the hostname may be so long. 82598776Sgad */ 82698776Sgad if (ch_opts & LPD_LOGCONNERR) { 82798776Sgad if (ch_opts & LPD_ADDFROMLINE) { 82878749Sgad syslog(LOG_WARNING, "for connection from %s:", from_host); 82978749Sgad } 83098776Sgad syslog(LOG_WARNING, "%s", sysmsg); 83178749Sgad } 83278749Sgad 83398776Sgad /* 83498776Sgad * Now send the error message to the remote host which is trying 83598776Sgad * to make the connection. 83698776Sgad */ 83798776Sgad printf("%s [@%s]: %s\n", progname, local_host, usermsg); 83878749Sgad fflush(stdout); 83978749Sgad 84078749Sgad /* 84178749Sgad * Add a minimal delay before exiting (and disconnecting from the 84278749Sgad * sending-host). This is just in case that machine responds by 84378749Sgad * INSTANTLY retrying (and instantly re-failing...). This may also 84478749Sgad * give the other side more time to read the error message. 84578749Sgad */ 84678749Sgad sleep(2); /* a paranoid throttling measure */ 84778749Sgad exit(1); 84878749Sgad} 84978749Sgad 85070098Sume/* setup server socket for specified address family */ 85170098Sume/* if af is PF_UNSPEC more than one socket may be returned */ 85270098Sume/* the returned list is dynamically allocated, so caller needs to free it */ 85370098Sumestatic int * 85478302Sgadsocksetup(int af, int debuglvl) 85570098Sume{ 85670098Sume struct addrinfo hints, *res, *r; 85770098Sume int error, maxs, *s, *socks; 85870098Sume const int on = 1; 85970098Sume 86070098Sume memset(&hints, 0, sizeof(hints)); 86170098Sume hints.ai_flags = AI_PASSIVE; 86270098Sume hints.ai_family = af; 86370098Sume hints.ai_socktype = SOCK_STREAM; 86470098Sume error = getaddrinfo(NULL, "printer", &hints, &res); 86570098Sume if (error) { 86670098Sume syslog(LOG_ERR, "%s", gai_strerror(error)); 86770098Sume mcleanup(0); 86870098Sume } 86970098Sume 87070098Sume /* Count max number of sockets we may open */ 87170098Sume for (maxs = 0, r = res; r; r = r->ai_next, maxs++) 87270098Sume ; 87370098Sume socks = malloc((maxs + 1) * sizeof(int)); 87470098Sume if (!socks) { 87570098Sume syslog(LOG_ERR, "couldn't allocate memory for sockets"); 87670098Sume mcleanup(0); 87770098Sume } 87870098Sume 87970098Sume *socks = 0; /* num of sockets counter at start of array */ 88070098Sume s = socks + 1; 88170098Sume for (r = res; r; r = r->ai_next) { 88270098Sume *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol); 88370098Sume if (*s < 0) { 88470098Sume syslog(LOG_DEBUG, "socket(): %m"); 88570098Sume continue; 88670098Sume } 88778302Sgad if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) 88878302Sgad < 0) { 88978302Sgad syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m"); 89078302Sgad close(*s); 89178302Sgad continue; 89278302Sgad } 89378302Sgad if (debuglvl) 89478302Sgad if (setsockopt(*s, SOL_SOCKET, SO_DEBUG, &debuglvl, 89578302Sgad sizeof(debuglvl)) < 0) { 89670098Sume syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m"); 89770098Sume close(*s); 89870098Sume continue; 89970098Sume } 90070098Sume if (r->ai_family == AF_INET6) { 901100522Sume if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY, 90270098Sume &on, sizeof(on)) < 0) { 90370098Sume syslog(LOG_ERR, 904100522Sume "setsockopt (IPV6_V6ONLY): %m"); 90570098Sume close(*s); 90670098Sume continue; 90770098Sume } 90870098Sume } 90970098Sume if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) { 91070098Sume syslog(LOG_DEBUG, "bind(): %m"); 91170098Sume close(*s); 91270098Sume continue; 91370098Sume } 91470098Sume (*socks)++; 91570098Sume s++; 91670098Sume } 91770098Sume 91870098Sume if (res) 91970098Sume freeaddrinfo(res); 92070098Sume 92170098Sume if (*socks == 0) { 92270098Sume syslog(LOG_ERR, "Couldn't bind to any socket"); 92370098Sume free(socks); 92470098Sume mcleanup(0); 92570098Sume } 92670098Sume return(socks); 92770098Sume} 92874127Sgad 92974127Sgadstatic void 93078146Sgadusage(void) 93174127Sgad{ 93274127Sgad#ifdef INET6 93395070Sgad fprintf(stderr, "usage: lpd [-cdlsW46] [port#]\n"); 93474127Sgad#else 93595070Sgad fprintf(stderr, "usage: lpd [-cdlsW] [port#]\n"); 93674127Sgad#endif 93774127Sgad exit(EX_USAGE); 93874127Sgad} 939