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