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