131492Swollman/*
231492Swollman * Copyright (c) 1983, 1993
331492Swollman *	The Regents of the University of California.  All rights reserved.
431492Swollman * (c) UNIX System Laboratories, Inc.
531492Swollman * All or some portions of this file are derived from material licensed
631492Swollman * to the University of California by American Telephone and Telegraph
731492Swollman * Co. or Unix System Laboratories, Inc. and are reproduced herein with
831492Swollman * the permission of UNIX System Laboratories, Inc.
931492Swollman *
1031492Swollman * Redistribution and use in source and binary forms, with or without
1131492Swollman * modification, are permitted provided that the following conditions
1231492Swollman * are met:
1331492Swollman * 1. Redistributions of source code must retain the above copyright
1431492Swollman *    notice, this list of conditions and the following disclaimer.
1531492Swollman * 2. Redistributions in binary form must reproduce the above copyright
1631492Swollman *    notice, this list of conditions and the following disclaimer in the
1731492Swollman *    documentation and/or other materials provided with the distribution.
1831492Swollman * 3. All advertising materials mentioning features or use of this software
1931492Swollman *    must display the following acknowledgement:
2031492Swollman *	This product includes software developed by the University of
2131492Swollman *	California, Berkeley and its contributors.
2231492Swollman * 4. Neither the name of the University nor the names of its contributors
2331492Swollman *    may be used to endorse or promote products derived from this software
2431492Swollman *    without specific prior written permission.
2531492Swollman *
2631492Swollman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2731492Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2831492Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2931492Swollman * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3031492Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3131492Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3231492Swollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3331492Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3431492Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3531492Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3631492Swollman * SUCH DAMAGE.
3731492Swollman *
3831492Swollman *	From: @(#)common.c	8.5 (Berkeley) 4/28/95
3931492Swollman */
4031492Swollman
41117541Sgad#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
42117541Sgad__FBSDID("$FreeBSD$");
4331492Swollman
4431492Swollman#include <sys/param.h>
4531492Swollman#include <sys/socket.h>
4631492Swollman#include <sys/stat.h>
4731492Swollman#include <sys/time.h>
4831492Swollman#include <sys/uio.h>
4931492Swollman
5031492Swollman#include <netinet/in.h>
5131492Swollman#include <arpa/inet.h>
5231492Swollman#include <netdb.h>
5331492Swollman
5431492Swollman#include <dirent.h>		/* required for lp.h, not used here */
55241852Seadler#include <err.h>
5631492Swollman#include <errno.h>
5731492Swollman#include <stdarg.h>
5831492Swollman#include <stdio.h>
5931492Swollman#include <stdlib.h>
6031492Swollman#include <string.h>
6131492Swollman#include <unistd.h>
6231492Swollman
6331492Swollman#include "lp.h"
6431492Swollman#include "lp.local.h"
6531492Swollman#include "pathnames.h"
6631492Swollman
6778300Sgad/*
6878300Sgad * 'local_host' is always the hostname of the machine which is running
6978300Sgad * lpr (lpd, whatever), while 'from_host' either points at 'local_host'
7078300Sgad * or points at a different buffer when receiving a job from a remote
7178300Sgad * machine (and that buffer has the hostname of that remote machine).
7278300Sgad */
7378300Sgadchar		 local_host[MAXHOSTNAMELEN];	/* host running lpd/lpr */
7478300Sgadconst char	*from_host = local_host;	/* client's machine name */
7578300Sgadconst char	*from_ip = "";		/* client machine's IP address */
7631492Swollman
7770098Sume#ifdef INET6
7870098Sumeu_char	family = PF_UNSPEC;
7970098Sume#else
8070098Sumeu_char	family = PF_INET;
8170098Sume#endif
8270098Sume
8331492Swollman/*
8431492Swollman * Create a TCP connection to host "rhost" at port "rport".
8531492Swollman * If rport == 0, then use the printer service port.
8631492Swollman * Most of this code comes from rcmd.c.
8731492Swollman */
8831492Swollmanint
8931492Swollmangetport(const struct printer *pp, const char *rhost, int rport)
9031492Swollman{
9170098Sume	struct addrinfo hints, *res, *ai;
9231492Swollman	int s, timo = 1, lport = IPPORT_RESERVED - 1;
93241852Seadler	int error, refused = 0;
9431492Swollman
9531492Swollman	/*
9631492Swollman	 * Get the host address and port number to connect to.
9731492Swollman	 */
9831492Swollman	if (rhost == NULL)
9931492Swollman		fatal(pp, "no remote host to connect to");
10070098Sume	memset(&hints, 0, sizeof(hints));
10170098Sume	hints.ai_family = family;
10270098Sume	hints.ai_socktype = SOCK_STREAM;
10370098Sume	hints.ai_protocol = 0;
104241852Seadler	error = getaddrinfo(rhost, (rport == 0 ? "printer" : NULL),
10570098Sume			  &hints, &res);
106241852Seadler	if (error)
107241852Seadler		fatal(pp, "%s\n", gai_strerror(error));
10870098Sume	if (rport != 0)
10970098Sume		((struct sockaddr_in *) res->ai_addr)->sin_port = htons(rport);
11031492Swollman
11131492Swollman	/*
11231492Swollman	 * Try connecting to the server.
11331492Swollman	 */
11470098Sume	ai = res;
11531492Swollmanretry:
116241852Seadler	PRIV_START
11770098Sume	s = rresvport_af(&lport, ai->ai_family);
118241852Seadler	PRIV_END
11970098Sume	if (s < 0) {
12070098Sume		if (errno != EAGAIN) {
12170098Sume			if (ai->ai_next) {
12270098Sume				ai = ai->ai_next;
12370098Sume				goto retry;
12470098Sume			}
12570098Sume			if (refused && timo <= 16) {
12670098Sume				sleep(timo);
12770098Sume				timo *= 2;
12870098Sume				refused = 0;
12970098Sume				ai = res;
13070098Sume				goto retry;
13170098Sume			}
13270098Sume		}
13370098Sume		freeaddrinfo(res);
13431492Swollman		return(-1);
13570098Sume	}
13670098Sume	if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
137241852Seadler		error = errno;
13831492Swollman		(void) close(s);
139241852Seadler		errno = error;
14031492Swollman		/*
14131492Swollman		 * This used to decrement lport, but the current semantics
14231492Swollman		 * of rresvport do not provide such a function (in fact,
14331492Swollman		 * rresvport should guarantee that the chosen port will
14431492Swollman		 * never result in an EADDRINUSE).
14531492Swollman		 */
14670098Sume		if (errno == EADDRINUSE) {
14731492Swollman			goto retry;
14870098Sume		}
14931492Swollman
15070098Sume		if (errno == ECONNREFUSED)
15170098Sume			refused++;
15270098Sume
15370098Sume		if (ai->ai_next != NULL) {
15470098Sume			ai = ai->ai_next;
15570098Sume			goto retry;
15670098Sume		}
15770098Sume		if (refused && timo <= 16) {
15831492Swollman			sleep(timo);
15931492Swollman			timo *= 2;
16070098Sume			refused = 0;
16170098Sume			ai = res;
16231492Swollman			goto retry;
16331492Swollman		}
16470098Sume		freeaddrinfo(res);
16531492Swollman		return(-1);
16631492Swollman	}
16770098Sume	freeaddrinfo(res);
16831492Swollman	return(s);
16931492Swollman}
17031492Swollman
17131492Swollman/*
17231492Swollman * Figure out whether the local machine is the same
17331492Swollman * as the remote machine (RM) entry (if it exists).
17431492Swollman * We do this by counting the intersection of our
17531492Swollman * address list and theirs.  This is better than the
17631492Swollman * old method (comparing the canonical names), as it
17731492Swollman * allows load-sharing between multiple print servers.
17831492Swollman * The return value is an error message which must be
17931492Swollman * free()d.
18031492Swollman */
18131492Swollmanchar *
18231492Swollmancheckremote(struct printer *pp)
18331492Swollman{
18478146Sgad	char lclhost[MAXHOSTNAMELEN];
18570098Sume	struct addrinfo hints, *local_res, *remote_res, *lr, *rr;
186241852Seadler	char *error;
187241852Seadler	int ncommonaddrs, errno;
18870098Sume	char h1[NI_MAXHOST], h2[NI_MAXHOST];
18931492Swollman
19046110Sjkh	if (!pp->rp_matches_local) { /* Remote printer doesn't match local */
19146110Sjkh		pp->remote = 1;
19246110Sjkh		return NULL;
19346110Sjkh	}
19446110Sjkh
19531492Swollman	pp->remote = 0;	/* assume printer is local */
19670098Sume	if (pp->remote_host == NULL)
19770098Sume		return NULL;
19831492Swollman
19970098Sume	/* get the addresses of the local host */
20078146Sgad	gethostname(lclhost, sizeof(lclhost));
20178146Sgad	lclhost[sizeof(lclhost) - 1] = '\0';
20231492Swollman
20370098Sume	memset(&hints, 0, sizeof(hints));
20470098Sume	hints.ai_family = family;
20570098Sume	hints.ai_socktype = SOCK_STREAM;
20670098Sume	hints.ai_flags = AI_PASSIVE;
207241852Seadler	if ((errno = getaddrinfo(lclhost, NULL, &hints, &local_res)) != 0) {
208241852Seadler		asprintf(&error, "unable to get official name "
20970098Sume			 "for local machine %s: %s",
210241852Seadler			 lclhost, gai_strerror(errno));
211241852Seadler		return error;
21270098Sume	}
21370098Sume
21470098Sume	/* get the official name of RM */
21570098Sume	memset(&hints, 0, sizeof(hints));
21670098Sume	hints.ai_family = family;
21770098Sume	hints.ai_socktype = SOCK_STREAM;
21870098Sume	hints.ai_flags = AI_PASSIVE;
219241852Seadler	if ((errno = getaddrinfo(pp->remote_host, NULL,
22070098Sume				 &hints, &remote_res)) != 0) {
221241852Seadler		asprintf(&error, "unable to get address list for "
22270098Sume			 "remote machine %s: %s",
223241852Seadler			 pp->remote_host, gai_strerror(errno));
22470098Sume		freeaddrinfo(local_res);
225241852Seadler		return error;
22670098Sume	}
22770098Sume
22870098Sume	ncommonaddrs = 0;
22970098Sume	for (lr = local_res; lr; lr = lr->ai_next) {
23070098Sume		h1[0] = '\0';
23170098Sume		if (getnameinfo(lr->ai_addr, lr->ai_addrlen, h1, sizeof(h1),
232146188Sume				NULL, 0, NI_NUMERICHOST) != 0)
23370098Sume			continue;
23470098Sume		for (rr = remote_res; rr; rr = rr->ai_next) {
23570098Sume			h2[0] = '\0';
23670098Sume			if (getnameinfo(rr->ai_addr, rr->ai_addrlen,
23770098Sume					h2, sizeof(h2), NULL, 0,
238146188Sume					NI_NUMERICHOST) != 0)
23970098Sume				continue;
24070098Sume			if (strcmp(h1, h2) == 0)
24170098Sume				ncommonaddrs++;
24231492Swollman		}
24370098Sume	}
24431492Swollman
24570098Sume	/*
24670098Sume	 * if the two hosts do not share at least one IP address
24770098Sume	 * then the printer must be remote.
24870098Sume	 */
24970098Sume	if (ncommonaddrs == 0)
25070098Sume		pp->remote = 1;
25170098Sume	freeaddrinfo(local_res);
25270098Sume	freeaddrinfo(remote_res);
25331492Swollman	return NULL;
25431492Swollman}
25531492Swollman
25631492Swollman/*
25731492Swollman * This isn't really network-related, but it's used here to write
25831492Swollman * multi-part strings onto sockets without using stdio.  Return
25931492Swollman * values are as for writev(2).
26031492Swollman */
26131492Swollmanssize_t
26278146Sgadwritel(int strm, ...)
26331492Swollman{
26431492Swollman	va_list ap;
26531492Swollman	int i, n;
26631492Swollman	const char *cp;
26731492Swollman#define NIOV 12
26831492Swollman	struct iovec iov[NIOV], *iovp = iov;
26931492Swollman	ssize_t retval;
27031492Swollman
27131492Swollman	/* first count them */
27278146Sgad	va_start(ap, strm);
27331492Swollman	n = 0;
27431492Swollman	do {
27531492Swollman		cp = va_arg(ap, char *);
27631492Swollman		n++;
27731492Swollman	} while (cp);
27831492Swollman	va_end(ap);
27931492Swollman	n--;			/* correct for count of trailing null */
28031492Swollman
28131492Swollman	if (n > NIOV) {
28231492Swollman		iovp = malloc(n * sizeof *iovp);
28331492Swollman		if (iovp == 0)
28431492Swollman			return -1;
28531492Swollman	}
28631492Swollman
28731492Swollman	/* now make up iovec and send */
28878146Sgad	va_start(ap, strm);
28931492Swollman	for (i = 0; i < n; i++) {
29031492Swollman		iovp[i].iov_base = va_arg(ap, char *);
29131492Swollman		iovp[i].iov_len = strlen(iovp[i].iov_base);
29231492Swollman	}
29331492Swollman	va_end(ap);
29478146Sgad	retval = writev(strm, iovp, n);
29531492Swollman	if (iovp != iov)
29631492Swollman		free(iovp);
29731492Swollman	return retval;
29831492Swollman}
299