1
0
mirror of https://git.FreeBSD.org/src.git synced 2026-06-02 11:24:32 +00:00

IPv6 support for lpr.

Reviewed by:	freebsd-current (no objection)
Obtained from:	KAME
This commit is contained in:
Hajimu UMEMOTO
2000-12-16 18:06:09 +00:00
parent b600010a5b
commit 08829865f6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=70098
5 changed files with 308 additions and 147 deletions
+1
View File
@@ -1,5 +1,6 @@
# $FreeBSD$
CFLAGS+=-DINET6
CWARNFLAGS= -Wall -Wnested-externs -Wmissing-prototypes -Wno-unused -Wredundant-decls -Wstrict-prototypes
.if exists(${.OBJDIR}/../common_source)
+3 -2
View File
@@ -36,6 +36,7 @@
#include <sys/queue.h>
#include <time.h>
#include <netdb.h>
/*
* All this information used to be in global static variables shared
@@ -156,14 +157,14 @@ extern char *name; /* program name */
/* host machine name */
extern char host[MAXHOSTNAMELEN];
extern char *from; /* client's machine name */
#define MAXIPSTRLEN 32 /* maxlen of an IP-address as a string */
extern char from_ip[MAXIPSTRLEN]; /* client machine's IP address */
extern char from_ip[NI_MAXHOST]; /* client machine's IP address */
extern int requ[]; /* job number of spool entries */
extern int requests; /* # of spool requests */
extern char *user[]; /* users to process */
extern int users; /* # of users in user array */
extern char *person; /* name of person doing lprm */
extern u_char family; /* address family */
/*
* Structure used for building a sorted list of control files.
+113 -83
View File
@@ -67,7 +67,13 @@ static const char rcsid[] =
char host[MAXHOSTNAMELEN]; /* host machine name */
char *from = host; /* client's machine name */
char from_ip[MAXIPSTRLEN] = ""; /* client machine's IP address */
char from_ip[NI_MAXHOST] = ""; /* client machine's IP address */
#ifdef INET6
u_char family = PF_UNSPEC;
#else
u_char family = PF_INET;
#endif
extern uid_t uid, euid;
@@ -79,46 +85,52 @@ extern uid_t uid, euid;
int
getport(const struct printer *pp, const char *rhost, int rport)
{
struct hostent *hp;
struct servent *sp;
struct sockaddr_in sin;
struct addrinfo hints, *res, *ai;
int s, timo = 1, lport = IPPORT_RESERVED - 1;
int err;
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");
bzero((char *)&sin, sizeof(sin));
sin.sin_len = sizeof sin;
sin.sin_family = AF_INET;
if (inet_aton(rhost, &sin.sin_addr) == 0) {
hp = gethostbyname2(rhost, AF_INET);
if (hp == NULL)
fatal(pp, "cannot resolve %s: %s", rhost,
hstrerror(h_errno));
/* XXX - should deal with more addresses */
sin.sin_addr = *(struct in_addr *)hp->h_addr_list[0];
}
if (rport == 0) {
sp = getservbyname("printer", "tcp");
if (sp == NULL)
fatal(pp, "printer/tcp: unknown service");
sin.sin_port = sp->s_port;
} else
sin.sin_port = htons(rport);
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(&lport);
s = rresvport_af(&lport, ai->ai_family);
seteuid(uid);
if (s < 0)
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, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
}
if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
err = errno;
(void) close(s);
errno = err;
@@ -128,16 +140,28 @@ retry:
* rresvport should guarantee that the chosen port will
* never result in an EADDRINUSE).
*/
if (errno == EADDRINUSE)
goto retry;
if (errno == ECONNREFUSED && timo <= 16) {
sleep(timo);
timo *= 2;
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);
}
@@ -155,10 +179,10 @@ char *
checkremote(struct printer *pp)
{
char name[MAXHOSTNAMELEN];
register struct hostent *hp;
struct addrinfo hints, *local_res, *remote_res, *lr, *rr;
char *err;
struct in_addr *localaddrs;
int i, j, nlocaladdrs, ncommonaddrs;
int ncommonaddrs, error;
char h1[NI_MAXHOST], h2[NI_MAXHOST];
if (!pp->rp_matches_local) { /* Remote printer doesn't match local */
pp->remote = 1;
@@ -166,57 +190,63 @@ checkremote(struct printer *pp)
}
pp->remote = 0; /* assume printer is local */
if (pp->remote_host != NULL) {
/* get the addresses of the local host */
gethostname(name, sizeof(name));
name[sizeof(name) - 1] = '\0';
hp = gethostbyname2(name, AF_INET);
if (hp == (struct hostent *) NULL) {
asprintf(&err, "unable to get official name "
"for local machine %s: %s",
name, hstrerror(h_errno));
return err;
}
for (i = 0; hp->h_addr_list[i]; i++)
;
nlocaladdrs = i;
localaddrs = malloc(i * sizeof(struct in_addr));
if (localaddrs == 0) {
asprintf(&err, "malloc %lu bytes failed",
(u_long)i * sizeof(struct in_addr));
return err;
}
for (i = 0; hp->h_addr_list[i]; i++)
localaddrs[i] = *(struct in_addr *)hp->h_addr_list[i];
if (pp->remote_host == NULL)
return NULL;
/* get the official name of RM */
hp = gethostbyname2(pp->remote_host, AF_INET);
if (hp == (struct hostent *) NULL) {
asprintf(&err, "unable to get address list for "
"remote machine %s: %s",
pp->remote_host, hstrerror(h_errno));
free(localaddrs);
return err;
}
/* get the addresses of the local host */
gethostname(name, sizeof(name));
name[sizeof(name) - 1] = '\0';
ncommonaddrs = 0;
for (i = 0; i < nlocaladdrs; i++) {
for (j = 0; hp->h_addr_list[j]; j++) {
char *them = hp->h_addr_list[j];
if (localaddrs[i].s_addr ==
(*(struct in_addr *)them).s_addr)
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;
free(localaddrs);
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((error = getaddrinfo(name, NULL, &hints, &local_res)) != 0) {
asprintf(&err, "unable to get official name "
"for local machine %s: %s",
name, 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;
}
+7 -1
View File
@@ -40,7 +40,7 @@
.Nd line printer spooler daemon
.Sh SYNOPSIS
.Nm
.Op Fl dlp
.Op Fl dlp46
.Op Ar port#
.Sh DESCRIPTION
.Nm Lpd
@@ -81,6 +81,12 @@ The
flag causes
.Nm
not to open an Internet listening socket.
.It Fl 4
Inet only.
.It Fl 6
Inet6 only.
.It Fl 46
Inet and inet6 (default).
.It Ar "port#"
The Internet port number used to rendezvous
with other processes is normally obtained with
+184 -61
View File
@@ -112,12 +112,14 @@ static void reapchild __P((int));
static void mcleanup __P((int));
static void doit __P((void));
static void startup __P((void));
static void chkhost __P((struct sockaddr_in *));
static void chkhost __P((struct sockaddr *));
static int ckqueue __P((struct printer *));
static void usage __P((void));
/* From rcmd.c: */
int __ivaliduser __P((FILE *, u_long, const char *,
const char *));
static int *socksetup __P((int, int));
/* XXX from libc/net/rcmd.c */
extern int __ivaliduser_sa __P((FILE *, struct sockaddr *, socklen_t,
const char *, const char *));
uid_t uid, euid;
@@ -126,13 +128,14 @@ main(argc, argv)
int argc;
char **argv;
{
int errs, f, funix, finet, fromlen, i, socket_debug;
int errs, f, funix, *finet, fromlen, i, options, socket_debug;
fd_set defreadfds;
struct sockaddr_un un, fromunix;
struct sockaddr_in sin, frominet;
struct sockaddr_storage frominet;
int lfd;
sigset_t omask, nmask;
struct servent *sp, serv;
int inet_flag = 0, inet6_flag = 0;
euid = geteuid(); /* these shouldn't be different */
uid = getuid();
@@ -145,7 +148,7 @@ main(argc, argv)
errx(EX_NOPERM,"must run as root");
errs = 0;
while ((i = getopt(argc, argv, "dlp")) != -1)
while ((i = getopt(argc, argv, "dlp46")) != -1)
switch (i) {
case 'd':
socket_debug++;
@@ -156,9 +159,19 @@ main(argc, argv)
case 'p':
pflag++;
break;
case '4':
family = PF_INET;
inet_flag++;
break;
case '6':
family = PF_INET6;
inet6_flag++;
break;
default:
errs++;
}
if (inet_flag && inet6_flag)
family = PF_UNSPEC;
argc -= optind;
argv += optind;
if (errs)
@@ -283,32 +296,17 @@ main(argc, argv)
FD_ZERO(&defreadfds);
FD_SET(funix, &defreadfds);
listen(funix, 5);
finet = -1;
if (pflag == 0) {
finet = socket(AF_INET, SOCK_STREAM, 0);
if (finet >= 0) {
i = 1;
if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR, &i,
sizeof i) < 0) {
syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m");
mcleanup(0);
}
if (socket_debug &&
setsockopt(finet, SOL_SOCKET, SO_DEBUG,
&socket_debug, sizeof(socket_debug)) < 0) {
syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
mcleanup(0);
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = sp->s_port;
if (bind(finet, (struct sockaddr *)&sin,
sizeof(sin)) < 0) {
syslog(LOG_ERR, "bind: %m");
mcleanup(0);
}
FD_SET(finet, &defreadfds);
listen(finet, 5);
options = SO_REUSEADDR;
if (socket_debug)
options |= SO_DEBUG;
finet = socksetup(family, options);
} else
finet = NULL; /* pretend we couldn't open TCP socket. */
if (finet) {
for (i = 1; i <= *finet; i++) {
FD_SET(finet[i], &defreadfds);
listen(finet[i], 5);
}
}
/*
@@ -322,7 +320,7 @@ main(argc, argv)
* XXX - should be redone for multi-protocol
*/
for (;;) {
int domain, nfds, s;
int domain = -1, nfds, s = -1;
fd_set readfds;
FD_COPY(&defreadfds, &readfds);
@@ -338,14 +336,15 @@ main(argc, argv)
domain = AF_UNIX, fromlen = sizeof(fromunix);
s = accept(funix,
(struct sockaddr *)&fromunix, &fromlen);
} else if (pflag == 0) /* if (FD_ISSET(finet, &readfds)) */ {
domain = AF_INET, fromlen = sizeof(frominet);
s = accept(finet,
(struct sockaddr *)&frominet, &fromlen);
if (frominet.sin_port == htons(20)) {
close(s);
continue;
}
} else {
for (i = 1; i <= *finet; i++)
if (FD_ISSET(finet[i], &readfds)) {
domain = AF_INET;
fromlen = sizeof(frominet);
s = accept(finet[i],
(struct sockaddr *)&frominet,
&fromlen);
}
}
if (s < 0) {
if (errno != EINTR)
@@ -359,14 +358,16 @@ main(argc, argv)
signal(SIGQUIT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
(void) close(funix);
if (pflag == 0) {
(void) close(finet);
if (pflag == 0 && finet) {
for (i = 1; i <= *finet; i++)
(void)close(finet[i]);
}
dup2(s, 1);
(void) close(s);
if (domain == AF_INET) {
/* for both AF_INET and AF_INET6 */
from_remote = 1;
chkhost(&frominet);
chkhost((struct sockaddr *)&frominet);
} else
from_remote = 0;
doit();
@@ -606,35 +607,75 @@ ckqueue(pp)
*/
static void
chkhost(f)
struct sockaddr_in *f;
struct sockaddr *f;
{
register struct hostent *hp;
struct addrinfo hints, *res, *r;
register FILE *hostf;
int first = 1;
int good = 0;
char host[NI_MAXHOST], ip[NI_MAXHOST];
char serv[NI_MAXSERV];
int error, addrlen;
caddr_t addr;
error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv),
NI_NUMERICSERV);
if (error || atoi(serv) >= IPPORT_RESERVED)
fatal(0, "Malformed from address");
/* Need real hostname for temporary filenames */
hp = gethostbyaddr((char *)&f->sin_addr,
sizeof(struct in_addr), f->sin_family);
if (hp == NULL)
fatal(0, "Host name for your address (%s) unknown",
inet_ntoa(f->sin_addr));
error = getnameinfo(f, f->sa_len, host, sizeof(host), NULL, 0,
NI_NAMEREQD);
if (error) {
error = getnameinfo(f, f->sa_len, host, sizeof(host), NULL, 0,
NI_NUMERICHOST | NI_WITHSCOPEID);
if (error)
fatal(0, "Host name for your address unknown");
else
fatal(0, "Host name for your address (%s) unknown",
host);
}
(void) strncpy(fromb, hp->h_name, sizeof(fromb) - 1);
(void)strncpy(fromb, host, sizeof(fromb) - 1);
fromb[sizeof(fromb) - 1] = '\0';
from = fromb;
strncpy(from_ip, inet_ntoa(f->sin_addr), MAXIPSTRLEN);
/* Need address in stringform for comparison (no DNS lookup here) */
error = getnameinfo(f, f->sa_len, host, sizeof(host), NULL, 0,
NI_NUMERICHOST | NI_WITHSCOPEID);
if (error)
fatal(0, "Cannot print address");
strncpy(from_ip, host, NI_MAXHOST);
from_ip[sizeof(from_ip) - 1] = '\0';
/* Reject numeric addresses */
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
if (getaddrinfo(fromb, NULL, &hints, &res) == 0) {
freeaddrinfo(res);
fatal(0, "reverse lookup results in non-FQDN %s", fromb);
}
/* Check for spoof, ala rlogind */
hp = gethostbyname(fromb);
if (!hp)
fatal(0, "hostname for your address (%s) unknown", from_ip);
for (; good == 0 && hp->h_addr_list[0] != NULL; hp->h_addr_list++) {
if (!bcmp(hp->h_addr_list[0], (caddr_t)&f->sin_addr,
sizeof(f->sin_addr)))
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM; /*dummy*/
error = getaddrinfo(fromb, NULL, &hints, &res);
if (error) {
fatal(0, "hostname for your address (%s) unknown: %s", from_ip,
gai_strerror(error));
}
good = 0;
for (r = res; good == 0 && r; r = r->ai_next) {
error = getnameinfo(r->ai_addr, r->ai_addrlen, ip, sizeof(ip),
NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
if (!error && !strcmp(from_ip, ip))
good = 1;
}
if (res)
freeaddrinfo(res);
if (good == 0)
fatal(0, "address for your hostname (%s) not matched",
from_ip);
@@ -642,8 +683,7 @@ chkhost(f)
hostf = fopen(_PATH_HOSTSEQUIV, "r");
again:
if (hostf) {
if (__ivaliduser(hostf, f->sin_addr.s_addr,
DUMMY, DUMMY) == 0) {
if (__ivaliduser_sa(hostf, f, f->sa_len, DUMMY, DUMMY) == 0) {
(void) fclose(hostf);
return;
}
@@ -664,3 +704,86 @@ usage()
fprintf(stderr, "usage: lpd [-dlp] [port#]\n");
exit(EX_USAGE);
}
/* setup server socket for specified address family */
/* if af is PF_UNSPEC more than one socket may be returned */
/* the returned list is dynamically allocated, so caller needs to free it */
static int *
socksetup(af, options)
int af, options;
{
struct addrinfo hints, *res, *r;
int error, maxs, *s, *socks;
const int on = 1;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = af;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(NULL, "printer", &hints, &res);
if (error) {
syslog(LOG_ERR, "%s", gai_strerror(error));
mcleanup(0);
}
/* Count max number of sockets we may open */
for (maxs = 0, r = res; r; r = r->ai_next, maxs++)
;
socks = malloc((maxs + 1) * sizeof(int));
if (!socks) {
syslog(LOG_ERR, "couldn't allocate memory for sockets");
mcleanup(0);
}
*socks = 0; /* num of sockets counter at start of array */
s = socks + 1;
for (r = res; r; r = r->ai_next) {
*s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
if (*s < 0) {
syslog(LOG_DEBUG, "socket(): %m");
continue;
}
if (options & SO_REUSEADDR)
if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, &on,
sizeof(on)) < 0) {
syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m");
close(*s);
continue;
}
if (options & SO_DEBUG)
if (setsockopt(*s, SOL_SOCKET, SO_DEBUG,
&on, sizeof(on)) < 0) {
syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
close(*s);
continue;
}
#ifdef IPV6_BINDV6ONLY
if (r->ai_family == AF_INET6) {
if (setsockopt(*s, IPPROTO_IPV6, IPV6_BINDV6ONLY,
&on, sizeof(on)) < 0) {
syslog(LOG_ERR,
"setsockopt (IPV6_BINDV6ONLY): %m");
close(*s);
continue;
}
}
#endif
if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
syslog(LOG_DEBUG, "bind(): %m");
close(*s);
continue;
}
(*socks)++;
s++;
}
if (res)
freeaddrinfo(res);
if (*socks == 0) {
syslog(LOG_ERR, "Couldn't bind to any socket");
free(socks);
mcleanup(0);
}
return(socks);
}