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