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