diff options
author | ume <ume@FreeBSD.org> | 2000-12-16 18:06:09 +0000 |
---|---|---|
committer | ume <ume@FreeBSD.org> | 2000-12-16 18:06:09 +0000 |
commit | bd199e398300ffe7763db31037bf1a6d19b2d407 (patch) | |
tree | 3a4830f8c7df7ed9495f11908a846633c967ffaa /usr.sbin/lpr | |
parent | 5b8ad91da1233aca24afce2d8c4fe1d0966b3c50 (diff) | |
download | FreeBSD-src-bd199e398300ffe7763db31037bf1a6d19b2d407.zip FreeBSD-src-bd199e398300ffe7763db31037bf1a6d19b2d407.tar.gz |
IPv6 support for lpr.
Reviewed by: freebsd-current (no objection)
Obtained from: KAME
Diffstat (limited to 'usr.sbin/lpr')
-rw-r--r-- | usr.sbin/lpr/Makefile.inc | 1 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/lp.h | 5 | ||||
-rw-r--r-- | usr.sbin/lpr/common_source/net.c | 186 | ||||
-rw-r--r-- | usr.sbin/lpr/lpd/lpd.8 | 8 | ||||
-rw-r--r-- | usr.sbin/lpr/lpd/lpd.c | 245 |
5 files changed, 303 insertions, 142 deletions
diff --git a/usr.sbin/lpr/Makefile.inc b/usr.sbin/lpr/Makefile.inc index 141373e..9d01167 100644 --- a/usr.sbin/lpr/Makefile.inc +++ b/usr.sbin/lpr/Makefile.inc @@ -1,5 +1,6 @@ # $FreeBSD$ +CFLAGS+=-DINET6 CWARNFLAGS= -Wall -Wnested-externs -Wmissing-prototypes -Wno-unused -Wredundant-decls -Wstrict-prototypes .if exists(${.OBJDIR}/../common_source) diff --git a/usr.sbin/lpr/common_source/lp.h b/usr.sbin/lpr/common_source/lp.h index 4e4e4b6..309a770 100644 --- a/usr.sbin/lpr/common_source/lp.h +++ b/usr.sbin/lpr/common_source/lp.h @@ -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. diff --git a/usr.sbin/lpr/common_source/net.c b/usr.sbin/lpr/common_source/net.c index 4cfabfa..2f30d85 100644 --- a/usr.sbin/lpr/common_source/net.c +++ b/usr.sbin/lpr/common_source/net.c @@ -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) + if (errno == EADDRINUSE) { goto retry; + } - if (errno == ECONNREFUSED && timo <= 16) { + 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++; - } + 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; - free(localaddrs); } + + /* + * 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; } diff --git a/usr.sbin/lpr/lpd/lpd.8 b/usr.sbin/lpr/lpd/lpd.8 index 48e2af3..0c48f7c 100644 --- a/usr.sbin/lpr/lpd/lpd.8 +++ b/usr.sbin/lpr/lpd/lpd.8 @@ -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 diff --git a/usr.sbin/lpr/lpd/lpd.c b/usr.sbin/lpr/lpd/lpd.c index ea505ae..a890826 100644 --- a/usr.sbin/lpr/lpd/lpd.c +++ b/usr.sbin/lpr/lpd/lpd.c @@ -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); +} |