1df57947fSPedro F. Giffuni /*-
2df57947fSPedro F. Giffuni * SPDX-License-Identifier: BSD-4-Clause
3df57947fSPedro F. Giffuni *
44a1a0dbeSGarrett Wollman * Copyright (c) 1983, 1993
54a1a0dbeSGarrett Wollman * The Regents of the University of California. All rights reserved.
64a1a0dbeSGarrett Wollman * (c) UNIX System Laboratories, Inc.
74a1a0dbeSGarrett Wollman * All or some portions of this file are derived from material licensed
84a1a0dbeSGarrett Wollman * to the University of California by American Telephone and Telegraph
94a1a0dbeSGarrett Wollman * Co. or Unix System Laboratories, Inc. and are reproduced herein with
104a1a0dbeSGarrett Wollman * the permission of UNIX System Laboratories, Inc.
114a1a0dbeSGarrett Wollman *
124a1a0dbeSGarrett Wollman * Redistribution and use in source and binary forms, with or without
134a1a0dbeSGarrett Wollman * modification, are permitted provided that the following conditions
144a1a0dbeSGarrett Wollman * are met:
154a1a0dbeSGarrett Wollman * 1. Redistributions of source code must retain the above copyright
164a1a0dbeSGarrett Wollman * notice, this list of conditions and the following disclaimer.
174a1a0dbeSGarrett Wollman * 2. Redistributions in binary form must reproduce the above copyright
184a1a0dbeSGarrett Wollman * notice, this list of conditions and the following disclaimer in the
194a1a0dbeSGarrett Wollman * documentation and/or other materials provided with the distribution.
204a1a0dbeSGarrett Wollman * 3. All advertising materials mentioning features or use of this software
214a1a0dbeSGarrett Wollman * must display the following acknowledgement:
224a1a0dbeSGarrett Wollman * This product includes software developed by the University of
234a1a0dbeSGarrett Wollman * California, Berkeley and its contributors.
244a1a0dbeSGarrett Wollman * 4. Neither the name of the University nor the names of its contributors
254a1a0dbeSGarrett Wollman * may be used to endorse or promote products derived from this software
264a1a0dbeSGarrett Wollman * without specific prior written permission.
274a1a0dbeSGarrett Wollman *
284a1a0dbeSGarrett Wollman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
294a1a0dbeSGarrett Wollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
304a1a0dbeSGarrett Wollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
314a1a0dbeSGarrett Wollman * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
324a1a0dbeSGarrett Wollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
334a1a0dbeSGarrett Wollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
344a1a0dbeSGarrett Wollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
354a1a0dbeSGarrett Wollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
364a1a0dbeSGarrett Wollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
374a1a0dbeSGarrett Wollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
384a1a0dbeSGarrett Wollman * SUCH DAMAGE.
394a1a0dbeSGarrett Wollman */
404a1a0dbeSGarrett Wollman
411f589b47SGarance A Drosehn #include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
424a1a0dbeSGarrett Wollman #include <sys/param.h>
434a1a0dbeSGarrett Wollman #include <sys/socket.h>
444a1a0dbeSGarrett Wollman #include <sys/stat.h>
454a1a0dbeSGarrett Wollman #include <sys/time.h>
464a1a0dbeSGarrett Wollman #include <sys/uio.h>
474a1a0dbeSGarrett Wollman
484a1a0dbeSGarrett Wollman #include <netinet/in.h>
494a1a0dbeSGarrett Wollman #include <arpa/inet.h>
504a1a0dbeSGarrett Wollman #include <netdb.h>
514a1a0dbeSGarrett Wollman
524a1a0dbeSGarrett Wollman #include <dirent.h> /* required for lp.h, not used here */
531d1d4a47SEitan Adler #include <err.h>
544a1a0dbeSGarrett Wollman #include <errno.h>
554a1a0dbeSGarrett Wollman #include <stdarg.h>
564a1a0dbeSGarrett Wollman #include <stdio.h>
574a1a0dbeSGarrett Wollman #include <stdlib.h>
584a1a0dbeSGarrett Wollman #include <string.h>
594a1a0dbeSGarrett Wollman #include <unistd.h>
604a1a0dbeSGarrett Wollman
614a1a0dbeSGarrett Wollman #include "lp.h"
624a1a0dbeSGarrett Wollman #include "lp.local.h"
634a1a0dbeSGarrett Wollman #include "pathnames.h"
644a1a0dbeSGarrett Wollman
65cc3fd56fSGarance A Drosehn /*
66cc3fd56fSGarance A Drosehn * 'local_host' is always the hostname of the machine which is running
67cc3fd56fSGarance A Drosehn * lpr (lpd, whatever), while 'from_host' either points at 'local_host'
68cc3fd56fSGarance A Drosehn * or points at a different buffer when receiving a job from a remote
69cc3fd56fSGarance A Drosehn * machine (and that buffer has the hostname of that remote machine).
70cc3fd56fSGarance A Drosehn */
71cc3fd56fSGarance A Drosehn char local_host[MAXHOSTNAMELEN]; /* host running lpd/lpr */
72cc3fd56fSGarance A Drosehn const char *from_host = local_host; /* client's machine name */
73cc3fd56fSGarance A Drosehn const char *from_ip = ""; /* client machine's IP address */
7408829865SHajimu UMEMOTO
7508829865SHajimu UMEMOTO #ifdef INET6
7608829865SHajimu UMEMOTO u_char family = PF_UNSPEC;
7708829865SHajimu UMEMOTO #else
7808829865SHajimu UMEMOTO u_char family = PF_INET;
7908829865SHajimu UMEMOTO #endif
804a1a0dbeSGarrett Wollman
814a1a0dbeSGarrett Wollman /*
824a1a0dbeSGarrett Wollman * Create a TCP connection to host "rhost" at port "rport".
834a1a0dbeSGarrett Wollman * If rport == 0, then use the printer service port.
844a1a0dbeSGarrett Wollman * Most of this code comes from rcmd.c.
854a1a0dbeSGarrett Wollman */
864a1a0dbeSGarrett Wollman int
getport(const struct printer * pp,const char * rhost,int rport)874a1a0dbeSGarrett Wollman getport(const struct printer *pp, const char *rhost, int rport)
884a1a0dbeSGarrett Wollman {
8908829865SHajimu UMEMOTO struct addrinfo hints, *res, *ai;
904a1a0dbeSGarrett Wollman int s, timo = 1, lport = IPPORT_RESERVED - 1;
911d1d4a47SEitan Adler int error, refused = 0;
924a1a0dbeSGarrett Wollman
934a1a0dbeSGarrett Wollman /*
944a1a0dbeSGarrett Wollman * Get the host address and port number to connect to.
954a1a0dbeSGarrett Wollman */
964a1a0dbeSGarrett Wollman if (rhost == NULL)
974a1a0dbeSGarrett Wollman fatal(pp, "no remote host to connect to");
9808829865SHajimu UMEMOTO memset(&hints, 0, sizeof(hints));
9908829865SHajimu UMEMOTO hints.ai_family = family;
10008829865SHajimu UMEMOTO hints.ai_socktype = SOCK_STREAM;
10108829865SHajimu UMEMOTO hints.ai_protocol = 0;
1021d1d4a47SEitan Adler error = getaddrinfo(rhost, (rport == 0 ? "printer" : NULL),
10308829865SHajimu UMEMOTO &hints, &res);
1041d1d4a47SEitan Adler if (error)
1051d1d4a47SEitan Adler fatal(pp, "%s\n", gai_strerror(error));
10608829865SHajimu UMEMOTO if (rport != 0)
10708829865SHajimu UMEMOTO ((struct sockaddr_in *) res->ai_addr)->sin_port = htons(rport);
1084a1a0dbeSGarrett Wollman
1094a1a0dbeSGarrett Wollman /*
1104a1a0dbeSGarrett Wollman * Try connecting to the server.
1114a1a0dbeSGarrett Wollman */
11208829865SHajimu UMEMOTO ai = res;
1134a1a0dbeSGarrett Wollman retry:
1141d1d4a47SEitan Adler PRIV_START
11508829865SHajimu UMEMOTO s = rresvport_af(&lport, ai->ai_family);
1161d1d4a47SEitan Adler PRIV_END
11708829865SHajimu UMEMOTO if (s < 0) {
11808829865SHajimu UMEMOTO if (errno != EAGAIN) {
11908829865SHajimu UMEMOTO if (ai->ai_next) {
12008829865SHajimu UMEMOTO ai = ai->ai_next;
12108829865SHajimu UMEMOTO goto retry;
12208829865SHajimu UMEMOTO }
12308829865SHajimu UMEMOTO if (refused && timo <= 16) {
12408829865SHajimu UMEMOTO sleep(timo);
12508829865SHajimu UMEMOTO timo *= 2;
12608829865SHajimu UMEMOTO refused = 0;
12708829865SHajimu UMEMOTO ai = res;
12808829865SHajimu UMEMOTO goto retry;
12908829865SHajimu UMEMOTO }
13008829865SHajimu UMEMOTO }
13108829865SHajimu UMEMOTO freeaddrinfo(res);
1324a1a0dbeSGarrett Wollman return(-1);
13308829865SHajimu UMEMOTO }
13408829865SHajimu UMEMOTO if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
1351d1d4a47SEitan Adler error = errno;
1364a1a0dbeSGarrett Wollman (void) close(s);
1371d1d4a47SEitan Adler errno = error;
1384a1a0dbeSGarrett Wollman /*
1394a1a0dbeSGarrett Wollman * This used to decrement lport, but the current semantics
1404a1a0dbeSGarrett Wollman * of rresvport do not provide such a function (in fact,
1414a1a0dbeSGarrett Wollman * rresvport should guarantee that the chosen port will
1424a1a0dbeSGarrett Wollman * never result in an EADDRINUSE).
1434a1a0dbeSGarrett Wollman */
14408829865SHajimu UMEMOTO if (errno == EADDRINUSE) {
1454a1a0dbeSGarrett Wollman goto retry;
14608829865SHajimu UMEMOTO }
1474a1a0dbeSGarrett Wollman
14808829865SHajimu UMEMOTO if (errno == ECONNREFUSED)
14908829865SHajimu UMEMOTO refused++;
15008829865SHajimu UMEMOTO
15108829865SHajimu UMEMOTO if (ai->ai_next != NULL) {
15208829865SHajimu UMEMOTO ai = ai->ai_next;
15308829865SHajimu UMEMOTO goto retry;
15408829865SHajimu UMEMOTO }
15508829865SHajimu UMEMOTO if (refused && timo <= 16) {
1564a1a0dbeSGarrett Wollman sleep(timo);
1574a1a0dbeSGarrett Wollman timo *= 2;
15808829865SHajimu UMEMOTO refused = 0;
15908829865SHajimu UMEMOTO ai = res;
1604a1a0dbeSGarrett Wollman goto retry;
1614a1a0dbeSGarrett Wollman }
16208829865SHajimu UMEMOTO freeaddrinfo(res);
1634a1a0dbeSGarrett Wollman return(-1);
1644a1a0dbeSGarrett Wollman }
16508829865SHajimu UMEMOTO freeaddrinfo(res);
1664a1a0dbeSGarrett Wollman return(s);
1674a1a0dbeSGarrett Wollman }
1684a1a0dbeSGarrett Wollman
1694a1a0dbeSGarrett Wollman /*
1704a1a0dbeSGarrett Wollman * Figure out whether the local machine is the same
1714a1a0dbeSGarrett Wollman * as the remote machine (RM) entry (if it exists).
1724a1a0dbeSGarrett Wollman * We do this by counting the intersection of our
1734a1a0dbeSGarrett Wollman * address list and theirs. This is better than the
1744a1a0dbeSGarrett Wollman * old method (comparing the canonical names), as it
1754a1a0dbeSGarrett Wollman * allows load-sharing between multiple print servers.
1764a1a0dbeSGarrett Wollman * The return value is an error message which must be
1774a1a0dbeSGarrett Wollman * free()d.
1784a1a0dbeSGarrett Wollman */
1794a1a0dbeSGarrett Wollman char *
checkremote(struct printer * pp)1804a1a0dbeSGarrett Wollman checkremote(struct printer *pp)
1814a1a0dbeSGarrett Wollman {
182ba7a1ad7SGarance A Drosehn char lclhost[MAXHOSTNAMELEN];
18308829865SHajimu UMEMOTO struct addrinfo hints, *local_res, *remote_res, *lr, *rr;
1841d1d4a47SEitan Adler char *error;
1851d1d4a47SEitan Adler int ncommonaddrs, errno;
18608829865SHajimu UMEMOTO char h1[NI_MAXHOST], h2[NI_MAXHOST];
1874a1a0dbeSGarrett Wollman
188d5483ddfSJordan K. Hubbard if (!pp->rp_matches_local) { /* Remote printer doesn't match local */
189d5483ddfSJordan K. Hubbard pp->remote = 1;
190d5483ddfSJordan K. Hubbard return NULL;
191d5483ddfSJordan K. Hubbard }
192d5483ddfSJordan K. Hubbard
1934a1a0dbeSGarrett Wollman pp->remote = 0; /* assume printer is local */
19408829865SHajimu UMEMOTO if (pp->remote_host == NULL)
19508829865SHajimu UMEMOTO return NULL;
19608829865SHajimu UMEMOTO
1974a1a0dbeSGarrett Wollman /* get the addresses of the local host */
198ba7a1ad7SGarance A Drosehn gethostname(lclhost, sizeof(lclhost));
199ba7a1ad7SGarance A Drosehn lclhost[sizeof(lclhost) - 1] = '\0';
20008829865SHajimu UMEMOTO
20108829865SHajimu UMEMOTO memset(&hints, 0, sizeof(hints));
20208829865SHajimu UMEMOTO hints.ai_family = family;
20308829865SHajimu UMEMOTO hints.ai_socktype = SOCK_STREAM;
20408829865SHajimu UMEMOTO hints.ai_flags = AI_PASSIVE;
2051d1d4a47SEitan Adler if ((errno = getaddrinfo(lclhost, NULL, &hints, &local_res)) != 0) {
2061d1d4a47SEitan Adler asprintf(&error, "unable to get official name "
2074a1a0dbeSGarrett Wollman "for local machine %s: %s",
2081d1d4a47SEitan Adler lclhost, gai_strerror(errno));
2091d1d4a47SEitan Adler return error;
2104a1a0dbeSGarrett Wollman }
2114a1a0dbeSGarrett Wollman
2124a1a0dbeSGarrett Wollman /* get the official name of RM */
21308829865SHajimu UMEMOTO memset(&hints, 0, sizeof(hints));
21408829865SHajimu UMEMOTO hints.ai_family = family;
21508829865SHajimu UMEMOTO hints.ai_socktype = SOCK_STREAM;
21608829865SHajimu UMEMOTO hints.ai_flags = AI_PASSIVE;
2171d1d4a47SEitan Adler if ((errno = getaddrinfo(pp->remote_host, NULL,
21808829865SHajimu UMEMOTO &hints, &remote_res)) != 0) {
2191d1d4a47SEitan Adler asprintf(&error, "unable to get address list for "
2204a1a0dbeSGarrett Wollman "remote machine %s: %s",
2211d1d4a47SEitan Adler pp->remote_host, gai_strerror(errno));
22208829865SHajimu UMEMOTO freeaddrinfo(local_res);
2231d1d4a47SEitan Adler return error;
2244a1a0dbeSGarrett Wollman }
2254a1a0dbeSGarrett Wollman
2264a1a0dbeSGarrett Wollman ncommonaddrs = 0;
22708829865SHajimu UMEMOTO for (lr = local_res; lr; lr = lr->ai_next) {
22808829865SHajimu UMEMOTO h1[0] = '\0';
22908829865SHajimu UMEMOTO if (getnameinfo(lr->ai_addr, lr->ai_addrlen, h1, sizeof(h1),
2309ccf2f38SHajimu UMEMOTO NULL, 0, NI_NUMERICHOST) != 0)
23108829865SHajimu UMEMOTO continue;
23208829865SHajimu UMEMOTO for (rr = remote_res; rr; rr = rr->ai_next) {
23308829865SHajimu UMEMOTO h2[0] = '\0';
23408829865SHajimu UMEMOTO if (getnameinfo(rr->ai_addr, rr->ai_addrlen,
23508829865SHajimu UMEMOTO h2, sizeof(h2), NULL, 0,
2369ccf2f38SHajimu UMEMOTO NI_NUMERICHOST) != 0)
23708829865SHajimu UMEMOTO continue;
23808829865SHajimu UMEMOTO if (strcmp(h1, h2) == 0)
2394a1a0dbeSGarrett Wollman ncommonaddrs++;
2404a1a0dbeSGarrett Wollman }
2414a1a0dbeSGarrett Wollman }
2424a1a0dbeSGarrett Wollman
2434a1a0dbeSGarrett Wollman /*
2444a1a0dbeSGarrett Wollman * if the two hosts do not share at least one IP address
2454a1a0dbeSGarrett Wollman * then the printer must be remote.
2464a1a0dbeSGarrett Wollman */
2474a1a0dbeSGarrett Wollman if (ncommonaddrs == 0)
2484a1a0dbeSGarrett Wollman pp->remote = 1;
24908829865SHajimu UMEMOTO freeaddrinfo(local_res);
25008829865SHajimu UMEMOTO freeaddrinfo(remote_res);
2514a1a0dbeSGarrett Wollman return NULL;
2524a1a0dbeSGarrett Wollman }
2534a1a0dbeSGarrett Wollman
2544a1a0dbeSGarrett Wollman /*
2554a1a0dbeSGarrett Wollman * This isn't really network-related, but it's used here to write
2564a1a0dbeSGarrett Wollman * multi-part strings onto sockets without using stdio. Return
2574a1a0dbeSGarrett Wollman * values are as for writev(2).
2584a1a0dbeSGarrett Wollman */
2594a1a0dbeSGarrett Wollman ssize_t
writel(int strm,...)260ba7a1ad7SGarance A Drosehn writel(int strm, ...)
2614a1a0dbeSGarrett Wollman {
2624a1a0dbeSGarrett Wollman va_list ap;
2634a1a0dbeSGarrett Wollman int i, n;
2644a1a0dbeSGarrett Wollman const char *cp;
2654a1a0dbeSGarrett Wollman #define NIOV 12
2664a1a0dbeSGarrett Wollman struct iovec iov[NIOV], *iovp = iov;
2674a1a0dbeSGarrett Wollman ssize_t retval;
2684a1a0dbeSGarrett Wollman
2694a1a0dbeSGarrett Wollman /* first count them */
270ba7a1ad7SGarance A Drosehn va_start(ap, strm);
2714a1a0dbeSGarrett Wollman n = 0;
2724a1a0dbeSGarrett Wollman do {
2734a1a0dbeSGarrett Wollman cp = va_arg(ap, char *);
2744a1a0dbeSGarrett Wollman n++;
2754a1a0dbeSGarrett Wollman } while (cp);
2764a1a0dbeSGarrett Wollman va_end(ap);
2774a1a0dbeSGarrett Wollman n--; /* correct for count of trailing null */
2784a1a0dbeSGarrett Wollman
2794a1a0dbeSGarrett Wollman if (n > NIOV) {
2804a1a0dbeSGarrett Wollman iovp = malloc(n * sizeof *iovp);
281bc17d12dSPedro F. Giffuni if (iovp == NULL)
2824a1a0dbeSGarrett Wollman return -1;
2834a1a0dbeSGarrett Wollman }
2844a1a0dbeSGarrett Wollman
2854a1a0dbeSGarrett Wollman /* now make up iovec and send */
286ba7a1ad7SGarance A Drosehn va_start(ap, strm);
2874a1a0dbeSGarrett Wollman for (i = 0; i < n; i++) {
2884a1a0dbeSGarrett Wollman iovp[i].iov_base = va_arg(ap, char *);
2894a1a0dbeSGarrett Wollman iovp[i].iov_len = strlen(iovp[i].iov_base);
2904a1a0dbeSGarrett Wollman }
2914a1a0dbeSGarrett Wollman va_end(ap);
292ba7a1ad7SGarance A Drosehn retval = writev(strm, iovp, n);
2934a1a0dbeSGarrett Wollman if (iovp != iov)
2944a1a0dbeSGarrett Wollman free(iovp);
2954a1a0dbeSGarrett Wollman return retval;
2964a1a0dbeSGarrett Wollman }
297