1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 1983, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 * (c) UNIX System Laboratories, Inc.
7 * All or some portions of this file are derived from material licensed
8 * to the University of California by American Telephone and Telegraph
9 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
10 * the permission of UNIX System Laboratories, Inc.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 *    must display the following acknowledgement:
22 *	This product includes software developed by the University of
23 *	California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 *    may be used to endorse or promote products derived from this software
26 *    without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 *	From: @(#)common.c	8.5 (Berkeley) 4/28/95
41 */
42
43#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
44__FBSDID("$FreeBSD$");
45
46#include <sys/param.h>
47#include <sys/socket.h>
48#include <sys/stat.h>
49#include <sys/time.h>
50#include <sys/uio.h>
51
52#include <netinet/in.h>
53#include <arpa/inet.h>
54#include <netdb.h>
55
56#include <dirent.h>		/* required for lp.h, not used here */
57#include <err.h>
58#include <errno.h>
59#include <stdarg.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <string.h>
63#include <unistd.h>
64
65#include "lp.h"
66#include "lp.local.h"
67#include "pathnames.h"
68
69/*
70 * 'local_host' is always the hostname of the machine which is running
71 * lpr (lpd, whatever), while 'from_host' either points at 'local_host'
72 * or points at a different buffer when receiving a job from a remote
73 * machine (and that buffer has the hostname of that remote machine).
74 */
75char		 local_host[MAXHOSTNAMELEN];	/* host running lpd/lpr */
76const char	*from_host = local_host;	/* client's machine name */
77const char	*from_ip = "";		/* client machine's IP address */
78
79#ifdef INET6
80u_char	family = PF_UNSPEC;
81#else
82u_char	family = PF_INET;
83#endif
84
85/*
86 * Create a TCP connection to host "rhost" at port "rport".
87 * If rport == 0, then use the printer service port.
88 * Most of this code comes from rcmd.c.
89 */
90int
91getport(const struct printer *pp, const char *rhost, int rport)
92{
93	struct addrinfo hints, *res, *ai;
94	int s, timo = 1, lport = IPPORT_RESERVED - 1;
95	int error, refused = 0;
96
97	/*
98	 * Get the host address and port number to connect to.
99	 */
100	if (rhost == NULL)
101		fatal(pp, "no remote host to connect to");
102	memset(&hints, 0, sizeof(hints));
103	hints.ai_family = family;
104	hints.ai_socktype = SOCK_STREAM;
105	hints.ai_protocol = 0;
106	error = getaddrinfo(rhost, (rport == 0 ? "printer" : NULL),
107			  &hints, &res);
108	if (error)
109		fatal(pp, "%s\n", gai_strerror(error));
110	if (rport != 0)
111		((struct sockaddr_in *) res->ai_addr)->sin_port = htons(rport);
112
113	/*
114	 * Try connecting to the server.
115	 */
116	ai = res;
117retry:
118	PRIV_START
119	s = rresvport_af(&lport, ai->ai_family);
120	PRIV_END
121	if (s < 0) {
122		if (errno != EAGAIN) {
123			if (ai->ai_next) {
124				ai = ai->ai_next;
125				goto retry;
126			}
127			if (refused && timo <= 16) {
128				sleep(timo);
129				timo *= 2;
130				refused = 0;
131				ai = res;
132				goto retry;
133			}
134		}
135		freeaddrinfo(res);
136		return(-1);
137	}
138	if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
139		error = errno;
140		(void) close(s);
141		errno = error;
142		/*
143		 * This used to decrement lport, but the current semantics
144		 * of rresvport do not provide such a function (in fact,
145		 * rresvport should guarantee that the chosen port will
146		 * never result in an EADDRINUSE).
147		 */
148		if (errno == EADDRINUSE) {
149			goto retry;
150		}
151
152		if (errno == ECONNREFUSED)
153			refused++;
154
155		if (ai->ai_next != NULL) {
156			ai = ai->ai_next;
157			goto retry;
158		}
159		if (refused && timo <= 16) {
160			sleep(timo);
161			timo *= 2;
162			refused = 0;
163			ai = res;
164			goto retry;
165		}
166		freeaddrinfo(res);
167		return(-1);
168	}
169	freeaddrinfo(res);
170	return(s);
171}
172
173/*
174 * Figure out whether the local machine is the same
175 * as the remote machine (RM) entry (if it exists).
176 * We do this by counting the intersection of our
177 * address list and theirs.  This is better than the
178 * old method (comparing the canonical names), as it
179 * allows load-sharing between multiple print servers.
180 * The return value is an error message which must be
181 * free()d.
182 */
183char *
184checkremote(struct printer *pp)
185{
186	char lclhost[MAXHOSTNAMELEN];
187	struct addrinfo hints, *local_res, *remote_res, *lr, *rr;
188	char *error;
189	int ncommonaddrs, errno;
190	char h1[NI_MAXHOST], h2[NI_MAXHOST];
191
192	if (!pp->rp_matches_local) { /* Remote printer doesn't match local */
193		pp->remote = 1;
194		return NULL;
195	}
196
197	pp->remote = 0;	/* assume printer is local */
198	if (pp->remote_host == NULL)
199		return NULL;
200
201	/* get the addresses of the local host */
202	gethostname(lclhost, sizeof(lclhost));
203	lclhost[sizeof(lclhost) - 1] = '\0';
204
205	memset(&hints, 0, sizeof(hints));
206	hints.ai_family = family;
207	hints.ai_socktype = SOCK_STREAM;
208	hints.ai_flags = AI_PASSIVE;
209	if ((errno = getaddrinfo(lclhost, NULL, &hints, &local_res)) != 0) {
210		asprintf(&error, "unable to get official name "
211			 "for local machine %s: %s",
212			 lclhost, gai_strerror(errno));
213		return error;
214	}
215
216	/* get the official name of RM */
217	memset(&hints, 0, sizeof(hints));
218	hints.ai_family = family;
219	hints.ai_socktype = SOCK_STREAM;
220	hints.ai_flags = AI_PASSIVE;
221	if ((errno = getaddrinfo(pp->remote_host, NULL,
222				 &hints, &remote_res)) != 0) {
223		asprintf(&error, "unable to get address list for "
224			 "remote machine %s: %s",
225			 pp->remote_host, gai_strerror(errno));
226		freeaddrinfo(local_res);
227		return error;
228	}
229
230	ncommonaddrs = 0;
231	for (lr = local_res; lr; lr = lr->ai_next) {
232		h1[0] = '\0';
233		if (getnameinfo(lr->ai_addr, lr->ai_addrlen, h1, sizeof(h1),
234				NULL, 0, NI_NUMERICHOST) != 0)
235			continue;
236		for (rr = remote_res; rr; rr = rr->ai_next) {
237			h2[0] = '\0';
238			if (getnameinfo(rr->ai_addr, rr->ai_addrlen,
239					h2, sizeof(h2), NULL, 0,
240					NI_NUMERICHOST) != 0)
241				continue;
242			if (strcmp(h1, h2) == 0)
243				ncommonaddrs++;
244		}
245	}
246
247	/*
248	 * if the two hosts do not share at least one IP address
249	 * then the printer must be remote.
250	 */
251	if (ncommonaddrs == 0)
252		pp->remote = 1;
253	freeaddrinfo(local_res);
254	freeaddrinfo(remote_res);
255	return NULL;
256}
257
258/*
259 * This isn't really network-related, but it's used here to write
260 * multi-part strings onto sockets without using stdio.  Return
261 * values are as for writev(2).
262 */
263ssize_t
264writel(int strm, ...)
265{
266	va_list ap;
267	int i, n;
268	const char *cp;
269#define NIOV 12
270	struct iovec iov[NIOV], *iovp = iov;
271	ssize_t retval;
272
273	/* first count them */
274	va_start(ap, strm);
275	n = 0;
276	do {
277		cp = va_arg(ap, char *);
278		n++;
279	} while (cp);
280	va_end(ap);
281	n--;			/* correct for count of trailing null */
282
283	if (n > NIOV) {
284		iovp = malloc(n * sizeof *iovp);
285		if (iovp == NULL)
286			return -1;
287	}
288
289	/* now make up iovec and send */
290	va_start(ap, strm);
291	for (i = 0; i < n; i++) {
292		iovp[i].iov_base = va_arg(ap, char *);
293		iovp[i].iov_len = strlen(iovp[i].iov_base);
294	}
295	va_end(ap);
296	retval = writev(strm, iovp, n);
297	if (iovp != iov)
298		free(iovp);
299	return retval;
300}
301