xref: /freebsd/usr.sbin/lpr/common_source/net.c (revision 02e9120893770924227138ba49df1edb3896112a)
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 
41 #include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
42 #include <sys/param.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #include <sys/time.h>
46 #include <sys/uio.h>
47 
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
50 #include <netdb.h>
51 
52 #include <dirent.h>		/* required for lp.h, not used here */
53 #include <err.h>
54 #include <errno.h>
55 #include <stdarg.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 
61 #include "lp.h"
62 #include "lp.local.h"
63 #include "pathnames.h"
64 
65 /*
66  * 'local_host' is always the hostname of the machine which is running
67  * lpr (lpd, whatever), while 'from_host' either points at 'local_host'
68  * or points at a different buffer when receiving a job from a remote
69  * machine (and that buffer has the hostname of that remote machine).
70  */
71 char		 local_host[MAXHOSTNAMELEN];	/* host running lpd/lpr */
72 const char	*from_host = local_host;	/* client's machine name */
73 const char	*from_ip = "";		/* client machine's IP address */
74 
75 #ifdef INET6
76 u_char	family = PF_UNSPEC;
77 #else
78 u_char	family = PF_INET;
79 #endif
80 
81 /*
82  * Create a TCP connection to host "rhost" at port "rport".
83  * If rport == 0, then use the printer service port.
84  * Most of this code comes from rcmd.c.
85  */
86 int
87 getport(const struct printer *pp, const char *rhost, int rport)
88 {
89 	struct addrinfo hints, *res, *ai;
90 	int s, timo = 1, lport = IPPORT_RESERVED - 1;
91 	int error, refused = 0;
92 
93 	/*
94 	 * Get the host address and port number to connect to.
95 	 */
96 	if (rhost == NULL)
97 		fatal(pp, "no remote host to connect to");
98 	memset(&hints, 0, sizeof(hints));
99 	hints.ai_family = family;
100 	hints.ai_socktype = SOCK_STREAM;
101 	hints.ai_protocol = 0;
102 	error = getaddrinfo(rhost, (rport == 0 ? "printer" : NULL),
103 			  &hints, &res);
104 	if (error)
105 		fatal(pp, "%s\n", gai_strerror(error));
106 	if (rport != 0)
107 		((struct sockaddr_in *) res->ai_addr)->sin_port = htons(rport);
108 
109 	/*
110 	 * Try connecting to the server.
111 	 */
112 	ai = res;
113 retry:
114 	PRIV_START
115 	s = rresvport_af(&lport, ai->ai_family);
116 	PRIV_END
117 	if (s < 0) {
118 		if (errno != EAGAIN) {
119 			if (ai->ai_next) {
120 				ai = ai->ai_next;
121 				goto retry;
122 			}
123 			if (refused && timo <= 16) {
124 				sleep(timo);
125 				timo *= 2;
126 				refused = 0;
127 				ai = res;
128 				goto retry;
129 			}
130 		}
131 		freeaddrinfo(res);
132 		return(-1);
133 	}
134 	if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
135 		error = errno;
136 		(void) close(s);
137 		errno = error;
138 		/*
139 		 * This used to decrement lport, but the current semantics
140 		 * of rresvport do not provide such a function (in fact,
141 		 * rresvport should guarantee that the chosen port will
142 		 * never result in an EADDRINUSE).
143 		 */
144 		if (errno == EADDRINUSE) {
145 			goto retry;
146 		}
147 
148 		if (errno == ECONNREFUSED)
149 			refused++;
150 
151 		if (ai->ai_next != NULL) {
152 			ai = ai->ai_next;
153 			goto retry;
154 		}
155 		if (refused && timo <= 16) {
156 			sleep(timo);
157 			timo *= 2;
158 			refused = 0;
159 			ai = res;
160 			goto retry;
161 		}
162 		freeaddrinfo(res);
163 		return(-1);
164 	}
165 	freeaddrinfo(res);
166 	return(s);
167 }
168 
169 /*
170  * Figure out whether the local machine is the same
171  * as the remote machine (RM) entry (if it exists).
172  * We do this by counting the intersection of our
173  * address list and theirs.  This is better than the
174  * old method (comparing the canonical names), as it
175  * allows load-sharing between multiple print servers.
176  * The return value is an error message which must be
177  * free()d.
178  */
179 char *
180 checkremote(struct printer *pp)
181 {
182 	char lclhost[MAXHOSTNAMELEN];
183 	struct addrinfo hints, *local_res, *remote_res, *lr, *rr;
184 	char *error;
185 	int ncommonaddrs, errno;
186 	char h1[NI_MAXHOST], h2[NI_MAXHOST];
187 
188 	if (!pp->rp_matches_local) { /* Remote printer doesn't match local */
189 		pp->remote = 1;
190 		return NULL;
191 	}
192 
193 	pp->remote = 0;	/* assume printer is local */
194 	if (pp->remote_host == NULL)
195 		return NULL;
196 
197 	/* get the addresses of the local host */
198 	gethostname(lclhost, sizeof(lclhost));
199 	lclhost[sizeof(lclhost) - 1] = '\0';
200 
201 	memset(&hints, 0, sizeof(hints));
202 	hints.ai_family = family;
203 	hints.ai_socktype = SOCK_STREAM;
204 	hints.ai_flags = AI_PASSIVE;
205 	if ((errno = getaddrinfo(lclhost, NULL, &hints, &local_res)) != 0) {
206 		asprintf(&error, "unable to get official name "
207 			 "for local machine %s: %s",
208 			 lclhost, gai_strerror(errno));
209 		return error;
210 	}
211 
212 	/* get the official name of RM */
213 	memset(&hints, 0, sizeof(hints));
214 	hints.ai_family = family;
215 	hints.ai_socktype = SOCK_STREAM;
216 	hints.ai_flags = AI_PASSIVE;
217 	if ((errno = getaddrinfo(pp->remote_host, NULL,
218 				 &hints, &remote_res)) != 0) {
219 		asprintf(&error, "unable to get address list for "
220 			 "remote machine %s: %s",
221 			 pp->remote_host, gai_strerror(errno));
222 		freeaddrinfo(local_res);
223 		return error;
224 	}
225 
226 	ncommonaddrs = 0;
227 	for (lr = local_res; lr; lr = lr->ai_next) {
228 		h1[0] = '\0';
229 		if (getnameinfo(lr->ai_addr, lr->ai_addrlen, h1, sizeof(h1),
230 				NULL, 0, NI_NUMERICHOST) != 0)
231 			continue;
232 		for (rr = remote_res; rr; rr = rr->ai_next) {
233 			h2[0] = '\0';
234 			if (getnameinfo(rr->ai_addr, rr->ai_addrlen,
235 					h2, sizeof(h2), NULL, 0,
236 					NI_NUMERICHOST) != 0)
237 				continue;
238 			if (strcmp(h1, h2) == 0)
239 				ncommonaddrs++;
240 		}
241 	}
242 
243 	/*
244 	 * if the two hosts do not share at least one IP address
245 	 * then the printer must be remote.
246 	 */
247 	if (ncommonaddrs == 0)
248 		pp->remote = 1;
249 	freeaddrinfo(local_res);
250 	freeaddrinfo(remote_res);
251 	return NULL;
252 }
253 
254 /*
255  * This isn't really network-related, but it's used here to write
256  * multi-part strings onto sockets without using stdio.  Return
257  * values are as for writev(2).
258  */
259 ssize_t
260 writel(int strm, ...)
261 {
262 	va_list ap;
263 	int i, n;
264 	const char *cp;
265 #define NIOV 12
266 	struct iovec iov[NIOV], *iovp = iov;
267 	ssize_t retval;
268 
269 	/* first count them */
270 	va_start(ap, strm);
271 	n = 0;
272 	do {
273 		cp = va_arg(ap, char *);
274 		n++;
275 	} while (cp);
276 	va_end(ap);
277 	n--;			/* correct for count of trailing null */
278 
279 	if (n > NIOV) {
280 		iovp = malloc(n * sizeof *iovp);
281 		if (iovp == NULL)
282 			return -1;
283 	}
284 
285 	/* now make up iovec and send */
286 	va_start(ap, strm);
287 	for (i = 0; i < n; i++) {
288 		iovp[i].iov_base = va_arg(ap, char *);
289 		iovp[i].iov_len = strlen(iovp[i].iov_base);
290 	}
291 	va_end(ap);
292 	retval = writev(strm, iovp, n);
293 	if (iovp != iov)
294 		free(iovp);
295 	return retval;
296 }
297