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