1/*	$OpenBSD: common.c,v 1.42 2021/01/19 09:04:13 claudio Exp $	*/
2/*	$NetBSD: common.c,v 1.21 2000/08/09 14:28:50 itojun Exp $	*/
3
4/*
5 * Copyright (c) 1983, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 * (c) UNIX System Laboratories, Inc.
8 * All or some portions of this file are derived from material licensed
9 * to the University of California by American Telephone and Telegraph
10 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11 * the permission of UNIX System Laboratories, Inc.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 * 3. 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#include <sys/stat.h>
39#include <sys/time.h>
40
41#include <sys/socket.h>
42#include <netinet/in.h>
43#include <arpa/inet.h>
44#include <netdb.h>
45
46#include <dirent.h>
47#include <errno.h>
48#include <fcntl.h>
49#include <unistd.h>
50#include <limits.h>
51#include <stdlib.h>
52#include <stdio.h>
53#include <string.h>
54#include <signal.h>
55#include <stdarg.h>
56#include <ifaddrs.h>
57#include "lp.h"
58#include "pathnames.h"
59
60/*
61 * Routines and data common to all the line printer functions.
62 */
63
64char	*AF;		/* accounting file */
65long	 BR;		/* baud rate if lp is a tty */
66char	*CF;		/* name of cifplot filter (per job) */
67char	*DF;		/* name of tex filter (per job) */
68long	 DU;		/* daemon user-id */
69char	*FF;		/* form feed string */
70char	*GF;		/* name of graph(1G) filter (per job) */
71long	 HL;		/* print header last */
72char	*IF;		/* name of input filter (created per job) */
73char	*LF;		/* log file for error messages */
74char	*LO;		/* lock file name */
75char	*LP;		/* line printer device name */
76long	 MC;		/* maximum number of copies allowed */
77char	*MS;		/* stty flags to set if lp is a tty */
78long	 MX;		/* maximum number of blocks to copy */
79char	*NF;		/* name of ditroff filter (per job) */
80char	*OF;		/* name of output filter (created once) */
81long	 PL;		/* page length */
82long	 PW;		/* page width */
83long	 PX;		/* page width in pixels */
84long	 PY;		/* page length in pixels */
85char	*RF;		/* name of fortran text filter (per job) */
86char    *RG;		/* restricted group */
87char	*RM;		/* remote machine name */
88char	*RP;		/* remote printer name */
89long	 RS;		/* restricted to those with local accounts */
90long	 RW;		/* open LP for reading and writing */
91long	 SB;		/* short banner instead of normal header */
92long	 SC;		/* suppress multiple copies */
93char	*SD;		/* spool directory */
94long	 SF;		/* suppress FF on each print job */
95long	 SH;		/* suppress header page */
96char	*ST;		/* status file name */
97char	*TF;		/* name of troff filter (per job) */
98char	*TR;		/* trailer string to be output when Q empties */
99char	*VF;		/* name of vplot filter (per job) */
100
101char	line[BUFSIZ];
102int	remote;		/* true if sending files to a remote host */
103
104static int compar(const void *, const void *);
105
106/*
107 * Create a TCP connection to host "rhost" at port "rport".
108 * If rport == 0, then use the printer service port.
109 * Most of this code comes from rcmd.c.
110 */
111int
112getport(char *rhost, int rport)
113{
114	struct addrinfo hints, *res, *r;
115	u_int timo = 1;
116	int s, lport = IPPORT_RESERVED - 1;
117	int error;
118	int refuse, trial;
119	char pbuf[NI_MAXSERV];
120
121	/*
122	 * Get the host address and port number to connect to.
123	 */
124	if (rhost == NULL)
125		fatal("no remote host to connect to");
126	memset(&hints, 0, sizeof(hints));
127	hints.ai_family = PF_UNSPEC;
128	hints.ai_socktype = SOCK_STREAM;
129	if (rport)
130		snprintf(pbuf, sizeof(pbuf), "%d", rport);
131	else
132		snprintf(pbuf, sizeof(pbuf), "printer");
133	siginterrupt(SIGINT, 1);
134	error = getaddrinfo(rhost, pbuf, &hints, &res);
135	siginterrupt(SIGINT, 0);
136	if (error)
137		fatal("printer/tcp: %s", gai_strerror(error));
138
139	/*
140	 * Try connecting to the server.
141	 */
142retry:
143	s = -1;
144	refuse = trial = 0;
145	for (r = res; r; r = r->ai_next) {
146		trial++;
147retryport:
148		PRIV_START;
149		s = rresvport_af(&lport, r->ai_family);
150		PRIV_END;
151		if (s < 0) {
152			/* fall back to non-privileged port */
153			if (errno != EACCES ||
154			    (s = socket(r->ai_family, SOCK_STREAM, 0)) < 0) {
155				freeaddrinfo(res);
156				return(-1);
157			}
158		}
159		siginterrupt(SIGINT, 1);
160		if (connect(s, r->ai_addr, r->ai_addrlen) < 0) {
161			error = errno;
162			siginterrupt(SIGINT, 0);
163			(void)close(s);
164			s = -1;
165			errno = error;
166			if (errno == EADDRINUSE) {
167				lport--;
168				goto retryport;
169			} else if (errno == ECONNREFUSED)
170				refuse++;
171			continue;
172		} else {
173			siginterrupt(SIGINT, 0);
174			break;
175		}
176	}
177	if (s < 0 && trial == refuse && timo <= 16) {
178		sleep(timo);
179		timo *= 2;
180		goto retry;
181	}
182	if (res)
183		freeaddrinfo(res);
184
185	/* Don't worry if we get an error from setsockopt(). */
186	trial = 1;
187	setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &trial, sizeof(trial));
188
189	return(s);
190}
191
192/*
193 * Getline reads a line from the control file cfp, removes tabs, converts
194 *  new-line to null and leaves it in line.
195 * Returns 0 at EOF or the number of characters read.
196 */
197int
198get_line(FILE *cfp)
199{
200	int linel = 0;
201	char *lp = line;
202	int c;
203
204	while ((c = getc(cfp)) != '\n' && linel+1<sizeof(line)) {
205		if (c == EOF)
206			return(0);
207		if (c == '\t') {
208			do {
209				*lp++ = ' ';
210				linel++;
211			} while ((linel & 07) != 0 && linel+1 < sizeof(line));
212			continue;
213		}
214		*lp++ = c;
215		linel++;
216	}
217	*lp++ = '\0';
218	return(linel);
219}
220
221/*
222 * Scan the current directory and make a list of daemon files sorted by
223 * creation time.
224 * Return the number of entries and a pointer to the list.
225 */
226int
227getq(struct queue ***namelist)
228{
229	struct dirent *d;
230	struct queue *q, **queue = NULL;
231	size_t nitems = 0, arraysz;
232	struct stat stbuf;
233	DIR *dirp;
234
235	PRIV_START;
236	dirp = opendir(SD);
237	PRIV_END;
238	if (dirp == NULL)
239		return(-1);
240	if (fstat(dirfd(dirp), &stbuf) < 0)
241		goto errdone;
242
243	/*
244	 * Estimate the array size by taking the size of the directory file
245	 * and dividing it by a multiple of the minimum size entry.
246	 */
247	arraysz = (stbuf.st_size / 24);
248	queue = calloc(arraysz, sizeof(struct queue *));
249	if (queue == NULL)
250		goto errdone;
251
252	while ((d = readdir(dirp)) != NULL) {
253		if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
254			continue;	/* daemon control files only */
255		PRIV_START;
256		if (stat(d->d_name, &stbuf) < 0) {
257			PRIV_END;
258			continue;	/* Doesn't exist */
259		}
260		PRIV_END;
261		q = malloc(sizeof(struct queue));
262		if (q == NULL)
263			goto errdone;
264		q->q_time = stbuf.st_mtime;
265		strlcpy(q->q_name, d->d_name, sizeof(q->q_name));
266
267		/*
268		 * Check to make sure the array has space left and
269		 * realloc the maximum size.
270		 */
271		if (nitems == arraysz) {
272			struct queue **newqueue;
273			newqueue = reallocarray(queue,
274			    arraysz, 2 * sizeof(struct queue *));
275			if (newqueue == NULL) {
276				free(q);
277				goto errdone;
278			}
279			arraysz *= 2;
280			queue = newqueue;
281		}
282		queue[nitems++] = q;
283	}
284	closedir(dirp);
285	if (nitems)
286		qsort(queue, nitems, sizeof(struct queue *), compar);
287	*namelist = queue;
288	return(nitems);
289
290errdone:
291	if (queue != NULL) {
292		while (nitems--)
293			free(queue[nitems]);
294		free(queue);
295	}
296	closedir(dirp);
297	return(-1);
298}
299
300/*
301 * Compare modification times.
302 */
303static int
304compar(const void *v1, const void *v2)
305{
306	struct queue *p1 = *(struct queue **)v1;
307	struct queue *p2 = *(struct queue **)v2;
308
309	return(p1->q_time - p2->q_time);
310}
311
312/*
313 * Figure out whether the local machine is the same
314 * as the remote machine (RM) entry (if it exists).
315 */
316char *
317checkremote(void)
318{
319	char lname[NI_MAXHOST], rname[NI_MAXHOST];
320	struct addrinfo hints, *res, *res0;
321	static char errbuf[128];
322	int error;
323	struct ifaddrs *ifap, *ifa;
324	const int niflags = NI_NUMERICHOST;
325	struct sockaddr *sa;
326#ifdef __KAME__
327	struct sockaddr_in6 sin6;
328	struct sockaddr_in6 *sin6p;
329#endif
330
331	remote = 0;	/* assume printer is local on failure */
332
333	if (RM == NULL || *RM == '\0')
334		return NULL;
335
336	/* get the local interface addresses */
337	siginterrupt(SIGINT, 1);
338	if (getifaddrs(&ifap) < 0) {
339		(void)snprintf(errbuf, sizeof(errbuf),
340		    "unable to get local interface address: %s",
341		    strerror(errno));
342		siginterrupt(SIGINT, 0);
343		return errbuf;
344	}
345	siginterrupt(SIGINT, 0);
346
347	/* get the remote host addresses (RM) */
348	memset(&hints, 0, sizeof(hints));
349	hints.ai_flags = AI_CANONNAME;
350	hints.ai_family = PF_UNSPEC;
351	hints.ai_socktype = SOCK_STREAM;
352	res = NULL;
353	siginterrupt(SIGINT, 1);
354	error = getaddrinfo(RM, NULL, &hints, &res0);
355	siginterrupt(SIGINT, 0);
356	if (error) {
357		(void)snprintf(errbuf, sizeof(errbuf),
358		    "unable to resolve remote machine %s: %s",
359		    RM, gai_strerror(error));
360		freeifaddrs(ifap);
361		return errbuf;
362	}
363
364	remote = 1;	/* assume printer is remote */
365
366	for (res = res0; res; res = res->ai_next) {
367		siginterrupt(SIGINT, 1);
368		error = getnameinfo(res->ai_addr, res->ai_addrlen,
369		    rname, sizeof(rname), NULL, 0, niflags);
370		siginterrupt(SIGINT, 0);
371		if (error != 0)
372			continue;
373		for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
374			sa = ifa->ifa_addr;
375#ifdef __KAME__
376			sin6p = (struct sockaddr_in6 *)sa;
377			if (sa->sa_family == AF_INET6 &&
378			    sa->sa_len == sizeof(sin6) &&
379			    IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr) &&
380			    *(u_int16_t *)&sin6p->sin6_addr.s6_addr[2]) {
381				/* kame scopeid hack */
382				memcpy(&sin6, ifa->ifa_addr, sizeof(sin6));
383				sin6.sin6_scope_id =
384				    ntohs(*(u_int16_t *)&sin6p->sin6_addr.s6_addr[2]);
385				sin6.sin6_addr.s6_addr[2] = 0;
386				sin6.sin6_addr.s6_addr[3] = 0;
387				sa = (struct sockaddr *)&sin6;
388			}
389#endif
390			siginterrupt(SIGINT, 1);
391			error = getnameinfo(sa, sa->sa_len,
392			    lname, sizeof(lname), NULL, 0, niflags);
393			siginterrupt(SIGINT, 0);
394			if (error != 0)
395				continue;
396
397			if (strcmp(rname, lname) == 0) {
398				remote = 0;
399				goto done;
400			}
401		}
402	}
403done:
404	freeaddrinfo(res0);
405	freeifaddrs(ifap);
406	return NULL;
407}
408
409__dead void
410fatal(const char *msg, ...)
411{
412	extern char *__progname;
413	va_list ap;
414
415	va_start(ap, msg);
416	if (from != host)
417		(void)printf("%s: ", host);
418	(void)printf("%s: ", __progname);
419	if (printer)
420		(void)printf("%s: ", printer);
421	(void)vprintf(msg, ap);
422	va_end(ap);
423	(void)putchar('\n');
424	exit(1);
425}
426
427int
428safe_open(const char *path, int flags, mode_t mode)
429{
430	int fd, serrno;
431	struct stat stbuf;
432
433	if ((fd = open(path, flags|O_NONBLOCK, mode)) < 0 ||
434	    fstat(fd, &stbuf) < 0) {
435		if (fd >= 0) {
436			serrno = errno;
437			close(fd);
438			errno = serrno;
439		}
440		return (-1);
441	}
442	if (!S_ISREG(stbuf.st_mode)) {
443		close(fd);
444		errno = EACCES;
445		return (-1);
446	}
447	if (mode)
448		(void)fchmod(fd, mode);
449	return (fd);
450}
451
452/*
453 * Make sure there's some work to do before forking off a child - lpd
454 * Check to see if anything in queue - lpq
455 */
456int
457ckqueue(char *cap)
458{
459	struct dirent *d;
460	DIR *dirp;
461	char *spooldir;
462
463	if (cgetstr(cap, "sd", &spooldir) >= 0) {
464		dirp = opendir(spooldir);
465		free(spooldir);
466	} else
467		dirp = opendir(_PATH_DEFSPOOL);
468
469	if (dirp == NULL)
470		return (-1);
471	while ((d = readdir(dirp)) != NULL) {
472		if (d->d_name[0] == 'c' && d->d_name[1] == 'f') {
473			closedir(dirp);
474			return (1);		/* found a cf file */
475		}
476	}
477	closedir(dirp);
478	return (0);
479}
480