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