FreeBSD-5.3/usr.sbin/lpr/common_source/net.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 1983, 1993
 *	The Regents of the University of California.  All rights reserved.
 * (c) UNIX System Laboratories, Inc.
 * All or some portions of this file are derived from material licensed
 * to the University of California by American Telephone and Telegraph
 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
 * the permission of UNIX System Laboratories, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	From: @(#)common.c	8.5 (Berkeley) 4/28/95
 */

#include "lp.cdefs.h"		/* A cross-platform version of <sys/cdefs.h> */
__FBSDID("$FreeBSD: src/usr.sbin/lpr/common_source/net.c,v 1.8 2003/07/14 05:15:21 gad Exp $");

#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/uio.h>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <dirent.h>		/* required for lp.h, not used here */
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "lp.h"
#include "lp.local.h"
#include "pathnames.h"

/*
 * 'local_host' is always the hostname of the machine which is running
 * lpr (lpd, whatever), while 'from_host' either points at 'local_host'
 * or points at a different buffer when receiving a job from a remote
 * machine (and that buffer has the hostname of that remote machine).
 */
char		 local_host[MAXHOSTNAMELEN];	/* host running lpd/lpr */
const char	*from_host = local_host;	/* client's machine name */
const char	*from_ip = "";		/* client machine's IP address */

#ifdef INET6
u_char	family = PF_UNSPEC;
#else
u_char	family = PF_INET;
#endif

extern uid_t	uid, euid;

/*
 * Create a TCP connection to host "rhost" at port "rport".
 * If rport == 0, then use the printer service port.
 * Most of this code comes from rcmd.c.
 */
int
getport(const struct printer *pp, const char *rhost, int rport)
{
	struct addrinfo hints, *res, *ai;
	int s, timo = 1, lport = IPPORT_RESERVED - 1;
	int err, refused = 0;

	/*
	 * Get the host address and port number to connect to.
	 */
	if (rhost == NULL)
		fatal(pp, "no remote host to connect to");
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = family;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = 0;
	err = getaddrinfo(rhost, (rport == 0 ? "printer" : NULL),
			  &hints, &res);
	if (err)
		fatal(pp, "%s\n", gai_strerror(err));
	if (rport != 0)
		((struct sockaddr_in *) res->ai_addr)->sin_port = htons(rport);

	/*
	 * Try connecting to the server.
	 */
	ai = res;
retry:
	seteuid(euid);
	s = rresvport_af(&lport, ai->ai_family);
	seteuid(uid);
	if (s < 0) {
		if (errno != EAGAIN) {
			if (ai->ai_next) {
				ai = ai->ai_next;
				goto retry;
			}
			if (refused && timo <= 16) {
				sleep(timo);
				timo *= 2;
				refused = 0;
				ai = res;
				goto retry;
			}
		}
		freeaddrinfo(res);
		return(-1);
	}
	if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
		err = errno;
		(void) close(s);
		errno = err;
		/*
		 * This used to decrement lport, but the current semantics
		 * of rresvport do not provide such a function (in fact,
		 * rresvport should guarantee that the chosen port will
		 * never result in an EADDRINUSE).
		 */
		if (errno == EADDRINUSE) {
			goto retry;
		}

		if (errno == ECONNREFUSED)
			refused++;

		if (ai->ai_next != NULL) {
			ai = ai->ai_next;
			goto retry;
		}
		if (refused && timo <= 16) {
			sleep(timo);
			timo *= 2;
			refused = 0;
			ai = res;
			goto retry;
		}
		freeaddrinfo(res);
		return(-1);
	}
	freeaddrinfo(res);
	return(s);
}

/*
 * Figure out whether the local machine is the same
 * as the remote machine (RM) entry (if it exists).
 * We do this by counting the intersection of our
 * address list and theirs.  This is better than the
 * old method (comparing the canonical names), as it
 * allows load-sharing between multiple print servers.
 * The return value is an error message which must be
 * free()d.
 */
char *
checkremote(struct printer *pp)
{
	char lclhost[MAXHOSTNAMELEN];
	struct addrinfo hints, *local_res, *remote_res, *lr, *rr;
	char *err;
	int ncommonaddrs, error;
	char h1[NI_MAXHOST], h2[NI_MAXHOST];

	if (!pp->rp_matches_local) { /* Remote printer doesn't match local */
		pp->remote = 1;
		return NULL;
	}

	pp->remote = 0;	/* assume printer is local */
	if (pp->remote_host == NULL)
		return NULL;

	/* get the addresses of the local host */
	gethostname(lclhost, sizeof(lclhost));
	lclhost[sizeof(lclhost) - 1] = '\0';

	memset(&hints, 0, sizeof(hints));
	hints.ai_family = family;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE;
	if ((error = getaddrinfo(lclhost, NULL, &hints, &local_res)) != 0) {
		asprintf(&err, "unable to get official name "
			 "for local machine %s: %s",
			 lclhost, gai_strerror(error));
		return err;
	}

	/* get the official name of RM */
	memset(&hints, 0, sizeof(hints));
	hints.ai_family = family;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_flags = AI_PASSIVE;
	if ((error = getaddrinfo(pp->remote_host, NULL,
				 &hints, &remote_res)) != 0) {
		asprintf(&err, "unable to get address list for "
			 "remote machine %s: %s",
			 pp->remote_host, gai_strerror(error));
		freeaddrinfo(local_res);
		return err;
	}

	ncommonaddrs = 0;
	for (lr = local_res; lr; lr = lr->ai_next) {
		h1[0] = '\0';
		if (getnameinfo(lr->ai_addr, lr->ai_addrlen, h1, sizeof(h1),
				NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID) != 0)
			continue;
		for (rr = remote_res; rr; rr = rr->ai_next) {
			h2[0] = '\0';
			if (getnameinfo(rr->ai_addr, rr->ai_addrlen,
					h2, sizeof(h2), NULL, 0,
					NI_NUMERICHOST | NI_WITHSCOPEID) != 0)
				continue;
			if (strcmp(h1, h2) == 0)
				ncommonaddrs++;
		}
	}
			
	/*
	 * if the two hosts do not share at least one IP address
	 * then the printer must be remote.
	 */
	if (ncommonaddrs == 0)
		pp->remote = 1;
	freeaddrinfo(local_res);
	freeaddrinfo(remote_res);
	return NULL;
}

/*
 * This isn't really network-related, but it's used here to write
 * multi-part strings onto sockets without using stdio.  Return
 * values are as for writev(2).
 */
ssize_t
writel(int strm, ...)
{
	va_list ap;
	int i, n;
	const char *cp;
#define NIOV 12
	struct iovec iov[NIOV], *iovp = iov;
	ssize_t retval;

	/* first count them */
	va_start(ap, strm);
	n = 0;
	do {
		cp = va_arg(ap, char *);
		n++;
	} while (cp);
	va_end(ap);
	n--;			/* correct for count of trailing null */

	if (n > NIOV) {
		iovp = malloc(n * sizeof *iovp);
		if (iovp == 0)
			return -1;
	}

	/* now make up iovec and send */
	va_start(ap, strm);
	for (i = 0; i < n; i++) {
		iovp[i].iov_base = va_arg(ap, char *);
		iovp[i].iov_len = strlen(iovp[i].iov_base);
	}
	va_end(ap);
	retval = writev(strm, iovp, n);
	if (iovp != iov)
		free(iovp);
	return retval;
}