xref: /freebsd/usr.sbin/lpr/common_source/net.c (revision 33b77e2decd50e53798014b70bf7ca3bdc4c0c7e)
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 	"$Id$";
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 	pp->remote = 0;	/* assume printer is local */
164 	if (pp->remote_host != NULL) {
165 		/* get the addresses of the local host */
166 		gethostname(name, sizeof(name));
167 		name[sizeof(name) - 1] = '\0';
168 		hp = gethostbyname2(name, AF_INET);
169 		if (hp == (struct hostent *) NULL) {
170 			asprintf(&err, "unable to get official name "
171 				 "for local machine %s: %s",
172 				 name, hstrerror(h_errno));
173 			return err;
174 		}
175 		for (i = 0; hp->h_addr_list[i]; i++)
176 			;
177 		nlocaladdrs = i;
178 		localaddrs = malloc(i * sizeof(struct in_addr));
179 		if (localaddrs == 0) {
180 			asprintf(&err, "malloc %lu bytes failed",
181 				 (u_long)i * sizeof(struct in_addr));
182 			return err;
183 		}
184 		for (i = 0; hp->h_addr_list[i]; i++)
185 			localaddrs[i] = *(struct in_addr *)hp->h_addr_list[i];
186 
187 		/* get the official name of RM */
188 		hp = gethostbyname2(pp->remote_host, AF_INET);
189 		if (hp == (struct hostent *) NULL) {
190 			asprintf(&err, "unable to get address list for "
191 				 "remote machine %s: %s",
192 				 pp->remote_host, hstrerror(h_errno));
193 			free(localaddrs);
194 			return err;
195 		}
196 
197 		ncommonaddrs = 0;
198 		for (i = 0; i < nlocaladdrs; i++) {
199 			for (j = 0; hp->h_addr_list[j]; j++) {
200 				char *them = hp->h_addr_list[j];
201 				if (localaddrs[i].s_addr ==
202 				    (*(struct in_addr *)them).s_addr)
203 					ncommonaddrs++;
204 			}
205 		}
206 
207 		/*
208 		 * if the two hosts do not share at least one IP address
209 		 * then the printer must be remote.
210 		 */
211 		if (ncommonaddrs == 0)
212 			pp->remote = 1;
213 		free(localaddrs);
214 	}
215 	return NULL;
216 }
217 
218 /*
219  * This isn't really network-related, but it's used here to write
220  * multi-part strings onto sockets without using stdio.  Return
221  * values are as for writev(2).
222  */
223 ssize_t
224 writel(int s, ...)
225 {
226 	va_list ap;
227 	int i, n;
228 	const char *cp;
229 #define NIOV 12
230 	struct iovec iov[NIOV], *iovp = iov;
231 	ssize_t retval;
232 
233 	/* first count them */
234 	va_start(ap, s);
235 	n = 0;
236 	do {
237 		cp = va_arg(ap, char *);
238 		n++;
239 	} while (cp);
240 	va_end(ap);
241 	n--;			/* correct for count of trailing null */
242 
243 	if (n > NIOV) {
244 		iovp = malloc(n * sizeof *iovp);
245 		if (iovp == 0)
246 			return -1;
247 	}
248 
249 	/* now make up iovec and send */
250 	va_start(ap, s);
251 	for (i = 0; i < n; i++) {
252 		iovp[i].iov_base = va_arg(ap, char *);
253 		iovp[i].iov_len = strlen(iovp[i].iov_base);
254 	}
255 	va_end(ap);
256 	retval = writev(s, iovp, n);
257 	if (iovp != iov)
258 		free(iovp);
259 	return retval;
260 }
261