xref: /dragonfly/usr.sbin/lpr/common_source/net.c (revision 956f4b310cfa8e38b40fc4f9b7232e250bed6b0a)
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
getport(const struct printer * pp,const char * rhost,int rport)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 *
checkremote(struct printer * pp)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
writel(int strm,...)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