1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 3 * 4 * Copyright (c) 1983, 1993 5 * The Regents of the University of California. All rights reserved. 6 * (c) UNIX System Laboratories, Inc. 7 * All or some portions of this file are derived from material licensed 8 * to the University of California by American Telephone and Telegraph 9 * Co. or Unix System Laboratories, Inc. and are reproduced herein with 10 * the permission of UNIX System Laboratories, Inc. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. All advertising materials mentioning features or use of this software 21 * must display the following acknowledgement: 22 * This product includes software developed by the University of 23 * California, Berkeley and its contributors. 24 * 4. Neither the name of the University nor the names of its contributors 25 * may be used to endorse or promote products derived from this software 26 * without specific prior written permission. 27 * 28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 38 * SUCH DAMAGE. 39 * 40 * From: @(#)common.c 8.5 (Berkeley) 4/28/95 41 */ 42 43 #include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */ 44 __FBSDID("$FreeBSD$"); 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 <err.h> 58 #include <errno.h> 59 #include <stdarg.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <unistd.h> 64 65 #include "lp.h" 66 #include "lp.local.h" 67 #include "pathnames.h" 68 69 /* 70 * 'local_host' is always the hostname of the machine which is running 71 * lpr (lpd, whatever), while 'from_host' either points at 'local_host' 72 * or points at a different buffer when receiving a job from a remote 73 * machine (and that buffer has the hostname of that remote machine). 74 */ 75 char local_host[MAXHOSTNAMELEN]; /* host running lpd/lpr */ 76 const char *from_host = local_host; /* client's machine name */ 77 const char *from_ip = ""; /* client machine's IP address */ 78 79 #ifdef INET6 80 u_char family = PF_UNSPEC; 81 #else 82 u_char family = PF_INET; 83 #endif 84 85 /* 86 * Create a TCP connection to host "rhost" at port "rport". 87 * If rport == 0, then use the printer service port. 88 * Most of this code comes from rcmd.c. 89 */ 90 int 91 getport(const struct printer *pp, const char *rhost, int rport) 92 { 93 struct addrinfo hints, *res, *ai; 94 int s, timo = 1, lport = IPPORT_RESERVED - 1; 95 int error, refused = 0; 96 97 /* 98 * Get the host address and port number to connect to. 99 */ 100 if (rhost == NULL) 101 fatal(pp, "no remote host to connect to"); 102 memset(&hints, 0, sizeof(hints)); 103 hints.ai_family = family; 104 hints.ai_socktype = SOCK_STREAM; 105 hints.ai_protocol = 0; 106 error = getaddrinfo(rhost, (rport == 0 ? "printer" : NULL), 107 &hints, &res); 108 if (error) 109 fatal(pp, "%s\n", gai_strerror(error)); 110 if (rport != 0) 111 ((struct sockaddr_in *) res->ai_addr)->sin_port = htons(rport); 112 113 /* 114 * Try connecting to the server. 115 */ 116 ai = res; 117 retry: 118 PRIV_START 119 s = rresvport_af(&lport, ai->ai_family); 120 PRIV_END 121 if (s < 0) { 122 if (errno != EAGAIN) { 123 if (ai->ai_next) { 124 ai = ai->ai_next; 125 goto retry; 126 } 127 if (refused && timo <= 16) { 128 sleep(timo); 129 timo *= 2; 130 refused = 0; 131 ai = res; 132 goto retry; 133 } 134 } 135 freeaddrinfo(res); 136 return(-1); 137 } 138 if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) { 139 error = errno; 140 (void) close(s); 141 errno = error; 142 /* 143 * This used to decrement lport, but the current semantics 144 * of rresvport do not provide such a function (in fact, 145 * rresvport should guarantee that the chosen port will 146 * never result in an EADDRINUSE). 147 */ 148 if (errno == EADDRINUSE) { 149 goto retry; 150 } 151 152 if (errno == ECONNREFUSED) 153 refused++; 154 155 if (ai->ai_next != NULL) { 156 ai = ai->ai_next; 157 goto retry; 158 } 159 if (refused && timo <= 16) { 160 sleep(timo); 161 timo *= 2; 162 refused = 0; 163 ai = res; 164 goto retry; 165 } 166 freeaddrinfo(res); 167 return(-1); 168 } 169 freeaddrinfo(res); 170 return(s); 171 } 172 173 /* 174 * Figure out whether the local machine is the same 175 * as the remote machine (RM) entry (if it exists). 176 * We do this by counting the intersection of our 177 * address list and theirs. This is better than the 178 * old method (comparing the canonical names), as it 179 * allows load-sharing between multiple print servers. 180 * The return value is an error message which must be 181 * free()d. 182 */ 183 char * 184 checkremote(struct printer *pp) 185 { 186 char lclhost[MAXHOSTNAMELEN]; 187 struct addrinfo hints, *local_res, *remote_res, *lr, *rr; 188 char *error; 189 int ncommonaddrs, errno; 190 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 191 192 if (!pp->rp_matches_local) { /* Remote printer doesn't match local */ 193 pp->remote = 1; 194 return NULL; 195 } 196 197 pp->remote = 0; /* assume printer is local */ 198 if (pp->remote_host == NULL) 199 return NULL; 200 201 /* get the addresses of the local host */ 202 gethostname(lclhost, sizeof(lclhost)); 203 lclhost[sizeof(lclhost) - 1] = '\0'; 204 205 memset(&hints, 0, sizeof(hints)); 206 hints.ai_family = family; 207 hints.ai_socktype = SOCK_STREAM; 208 hints.ai_flags = AI_PASSIVE; 209 if ((errno = getaddrinfo(lclhost, NULL, &hints, &local_res)) != 0) { 210 asprintf(&error, "unable to get official name " 211 "for local machine %s: %s", 212 lclhost, gai_strerror(errno)); 213 return error; 214 } 215 216 /* get the official name of RM */ 217 memset(&hints, 0, sizeof(hints)); 218 hints.ai_family = family; 219 hints.ai_socktype = SOCK_STREAM; 220 hints.ai_flags = AI_PASSIVE; 221 if ((errno = getaddrinfo(pp->remote_host, NULL, 222 &hints, &remote_res)) != 0) { 223 asprintf(&error, "unable to get address list for " 224 "remote machine %s: %s", 225 pp->remote_host, gai_strerror(errno)); 226 freeaddrinfo(local_res); 227 return error; 228 } 229 230 ncommonaddrs = 0; 231 for (lr = local_res; lr; lr = lr->ai_next) { 232 h1[0] = '\0'; 233 if (getnameinfo(lr->ai_addr, lr->ai_addrlen, h1, sizeof(h1), 234 NULL, 0, NI_NUMERICHOST) != 0) 235 continue; 236 for (rr = remote_res; rr; rr = rr->ai_next) { 237 h2[0] = '\0'; 238 if (getnameinfo(rr->ai_addr, rr->ai_addrlen, 239 h2, sizeof(h2), NULL, 0, 240 NI_NUMERICHOST) != 0) 241 continue; 242 if (strcmp(h1, h2) == 0) 243 ncommonaddrs++; 244 } 245 } 246 247 /* 248 * if the two hosts do not share at least one IP address 249 * then the printer must be remote. 250 */ 251 if (ncommonaddrs == 0) 252 pp->remote = 1; 253 freeaddrinfo(local_res); 254 freeaddrinfo(remote_res); 255 return NULL; 256 } 257 258 /* 259 * This isn't really network-related, but it's used here to write 260 * multi-part strings onto sockets without using stdio. Return 261 * values are as for writev(2). 262 */ 263 ssize_t 264 writel(int strm, ...) 265 { 266 va_list ap; 267 int i, n; 268 const char *cp; 269 #define NIOV 12 270 struct iovec iov[NIOV], *iovp = iov; 271 ssize_t retval; 272 273 /* first count them */ 274 va_start(ap, strm); 275 n = 0; 276 do { 277 cp = va_arg(ap, char *); 278 n++; 279 } while (cp); 280 va_end(ap); 281 n--; /* correct for count of trailing null */ 282 283 if (n > NIOV) { 284 iovp = malloc(n * sizeof *iovp); 285 if (iovp == NULL) 286 return -1; 287 } 288 289 /* now make up iovec and send */ 290 va_start(ap, strm); 291 for (i = 0; i < n; i++) { 292 iovp[i].iov_base = va_arg(ap, char *); 293 iovp[i].iov_len = strlen(iovp[i].iov_base); 294 } 295 va_end(ap); 296 retval = writev(strm, iovp, n); 297 if (iovp != iov) 298 free(iovp); 299 return retval; 300 } 301