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