libc rcmd update for IPv6.

A new function bindresvport2(), AF independent version of bindresvport()
is also added.

Reviewed by: sumikawa
Obtained from: KAME project
This commit is contained in:
Yoshinobu Inoue 2000-01-13 15:09:48 +00:00
parent 5d60ed0e69
commit 42b4f28ebd
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=55918
7 changed files with 301 additions and 89 deletions

View file

@ -87,6 +87,7 @@ extern void setrpcent __P((int));
extern void endrpcent __P((void)); extern void endrpcent __P((void));
extern int bindresvport __P((int, struct sockaddr_in *)); extern int bindresvport __P((int, struct sockaddr_in *));
extern int bindresvport2 __P((int, struct sockaddr *, int addrlen));
extern int get_myaddress __P((struct sockaddr_in *)); extern int get_myaddress __P((struct sockaddr_in *));
__END_DECLS __END_DECLS

View file

@ -147,6 +147,7 @@ char *getusershell __P((void));
char *getwd __P((char *)); /* obsoleted by getcwd() */ char *getwd __P((char *)); /* obsoleted by getcwd() */
int initgroups __P((const char *, int)); int initgroups __P((const char *, int));
int iruserok __P((unsigned long, int, const char *, const char *)); int iruserok __P((unsigned long, int, const char *, const char *));
int iruserok_af __P((void *, int, const char *, const char *, int));
int issetugid __P((void)); int issetugid __P((void));
int lchown __P((const char *, uid_t, gid_t)); int lchown __P((const char *, uid_t, gid_t));
int lockf __P((int, int, off_t)); int lockf __P((int, int, off_t));
@ -203,6 +204,9 @@ int unwhiteout __P((const char *));
int usleep __P((unsigned int)); int usleep __P((unsigned int));
void *valloc __P((size_t)); /* obsoleted by malloc() */ void *valloc __P((size_t)); /* obsoleted by malloc() */
pid_t vfork __P((void)); pid_t vfork __P((void));
int rresvport_af __P((int *, int));
int ruserok_af __P((const char *, int, const char *, const char *, int));
int iruserok_af __P((void *, int, const char *, const char *, int));
extern char *suboptarg; /* getsubopt(3) external variable */ extern char *suboptarg; /* getsubopt(3) external variable */
int getsubopt __P((char **, char * const *, char **)); int getsubopt __P((char **, char * const *, char **));

View file

@ -39,7 +39,10 @@
.Nm rcmd , .Nm rcmd ,
.Nm rresvport , .Nm rresvport ,
.Nm iruserok , .Nm iruserok ,
.Nm ruserok .Nm ruserok ,
.Nm rresvport_af ,
.Nm iruserok_af ,
.Nm ruserok_af
.Nd routines for returning a stream to a remote command .Nd routines for returning a stream to a remote command
.Sh SYNOPSIS .Sh SYNOPSIS
.Fd #include <unistd.h> .Fd #include <unistd.h>
@ -51,6 +54,12 @@
.Fn iruserok "u_long raddr" "int superuser" "const char *ruser" "const char *luser" .Fn iruserok "u_long raddr" "int superuser" "const char *ruser" "const char *luser"
.Ft int .Ft int
.Fn ruserok "const char *rhost" "int superuser" "const char *ruser" "const char *luser" .Fn ruserok "const char *rhost" "int superuser" "const char *ruser" "const char *luser"
.Ft int
.Fn rresvport_af "int *port" "int family"
.Ft int
.Fn iruserok_af "void *raddr" "int superuser" "const char *ruser" "const char *luser" "int af"
.Ft int
.Fn ruserok_af "const char *rhost" "int superuser" "const char *ruser" "const char *luser" "int af"
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Fn rcmd .Fn rcmd
@ -173,6 +182,19 @@ function is strongly preferred for security reasons.
It requires trusting the local DNS at most, while the It requires trusting the local DNS at most, while the
.Fn ruserok .Fn ruserok
function requires trusting the entire DNS, which can be spoofed. function requires trusting the entire DNS, which can be spoofed.
.Pp
Functions with ``_af'' suffix, i.e.
.Fn rresvport_af ,
.Fn iruserok_af and
.Fn ruserok_af ,
works just as same as functions without ``_af'', and is capable of
handling both IPv6 port and IPv4 port.
To switch address family,
.Fa af
argument must be filled with
.Dv AF_INET
or
.Dv AF_INET6 .
.Sh DIAGNOSTICS .Sh DIAGNOSTICS
The The
.Fn rcmd .Fn rcmd
@ -198,7 +220,18 @@ is overloaded to mean ``All network ports in use.''
.Xr rexecd 8 , .Xr rexecd 8 ,
.Xr rlogind 8 , .Xr rlogind 8 ,
.Xr rshd 8 .Xr rshd 8
.Pp
W. Stevens and M. Thomas, ``Advanced Socket API for IPv6,''
RFC2292.
.Sh HISTORY .Sh HISTORY
These These
functions appeared in functions appeared in
.Bx 4.2 . .Bx 4.2 .
.Fn rresvport_af
appeared in RFC2292, and implemented by WIDE project
for Hydrangea IPv6 protocol stack kit.
.Fn iruserok_af
and
.Fn rusreok_af
are proposed and implemented by WIDE project
for Hydrangea IPv6 protocol stack kit.

View file

@ -59,12 +59,19 @@ static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94";
#include <rpcsvc/ypclnt.h> #include <rpcsvc/ypclnt.h>
#endif #endif
/* wrapper for KAME-special getnameinfo() */
#ifndef NI_WITHSCOPEID
#define NI_WITHSCOPEID 0
#endif
extern int innetgr __P(( const char *, const char *, const char *, const char * )); extern int innetgr __P(( const char *, const char *, const char *, const char * ));
#define max(a, b) ((a > b) ? a : b) #define max(a, b) ((a > b) ? a : b)
int __ivaliduser __P((FILE *, u_int32_t, const char *, const char *)); int __ivaliduser __P((FILE *, u_int32_t, const char *, const char *));
static int __icheckhost __P((u_int32_t, char *)); static int __icheckhost __P((void *, char *, int, int));
char paddr[INET6_ADDRSTRLEN];
int int
rcmd(ahost, rport, locuser, remuser, cmd, fd2p) rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
@ -73,24 +80,40 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
const char *locuser, *remuser, *cmd; const char *locuser, *remuser, *cmd;
int *fd2p; int *fd2p;
{ {
struct hostent *hp; struct addrinfo hints, *res, *ai;
struct sockaddr_in sin, from; struct sockaddr_storage from;
fd_set reads; fd_set reads;
long oldmask; long oldmask;
pid_t pid; pid_t pid;
int s, lport, timo; int s, aport, lport, timo, error;
char c; char c;
int refused;
char num[8];
pid = getpid(); pid = getpid();
hp = gethostbyname(*ahost);
if (hp == NULL) { memset(&hints, 0, sizeof(hints));
herror(*ahost); hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
(void)snprintf(num, sizeof(num), "%d", ntohs(rport));
error = getaddrinfo(*ahost, num, &hints, &res);
if (error) {
fprintf(stderr, "rcmd: getaddrinfo: %s\n",
gai_strerror(error));
if (error == EAI_SYSTEM)
fprintf(stderr, "rcmd: getaddrinfo: %s\n",
strerror(errno));
return (-1); return (-1);
} }
*ahost = hp->h_name; if (res->ai_canonname)
*ahost = res->ai_canonname;
ai = res;
refused = 0;
oldmask = sigblock(sigmask(SIGURG)); oldmask = sigblock(sigmask(SIGURG));
for (timo = 1, lport = IPPORT_RESERVED - 1;;) { for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
s = rresvport(&lport); s = rresvport_af(&lport, ai->ai_family);
if (s < 0) { if (s < 0) {
if (errno == EAGAIN) if (errno == EAGAIN)
(void)fprintf(stderr, (void)fprintf(stderr,
@ -99,40 +122,47 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
(void)fprintf(stderr, "rcmd: socket: %s\n", (void)fprintf(stderr, "rcmd: socket: %s\n",
strerror(errno)); strerror(errno));
sigsetmask(oldmask); sigsetmask(oldmask);
freeaddrinfo(res);
return (-1); return (-1);
} }
_libc_fcntl(s, F_SETOWN, pid); _libc_fcntl(s, F_SETOWN, pid);
bzero(&sin, sizeof sin); if (connect(s, ai->ai_addr, ai->ai_addrlen) >= 0)
sin.sin_len = sizeof(struct sockaddr_in);
sin.sin_family = hp->h_addrtype;
sin.sin_port = rport;
bcopy(hp->h_addr_list[0], &sin.sin_addr, MIN(hp->h_length, sizeof sin.sin_addr));
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0)
break; break;
(void)_libc_close(s); (void)_libc_close(s);
if (errno == EADDRINUSE) { if (errno == EADDRINUSE) {
lport--; lport--;
continue; continue;
} }
if (errno == ECONNREFUSED && timo <= 16) { if (errno == ECONNREFUSED)
(void)_libc_sleep(timo); refused = 1;
timo *= 2; if (ai->ai_next != NULL) {
continue;
}
if (hp->h_addr_list[1] != NULL) {
int oerrno = errno; int oerrno = errno;
getnameinfo(ai->ai_addr, ai->ai_addrlen,
paddr, sizeof(paddr),
NULL, 0,
NI_NUMERICHOST|NI_WITHSCOPEID);
(void)fprintf(stderr, "connect to address %s: ", (void)fprintf(stderr, "connect to address %s: ",
inet_ntoa(sin.sin_addr)); paddr);
errno = oerrno; errno = oerrno;
perror(0); perror(0);
hp->h_addr_list++; ai = ai->ai_next;
bcopy(hp->h_addr_list[0], &sin.sin_addr, MIN(hp->h_length, sizeof sin.sin_addr)); getnameinfo(ai->ai_addr, ai->ai_addrlen,
(void)fprintf(stderr, "Trying %s...\n", paddr, sizeof(paddr),
inet_ntoa(sin.sin_addr)); NULL, 0,
NI_NUMERICHOST|NI_WITHSCOPEID);
fprintf(stderr, "Trying %s...\n", paddr);
continue; continue;
} }
(void)fprintf(stderr, "%s: %s\n", hp->h_name, strerror(errno)); if (refused && timo <= 16) {
(void)_libc_sleep(timo);
timo *= 2;
ai = res;
refused = 0;
continue;
}
freeaddrinfo(res);
(void)fprintf(stderr, "%s: %s\n", *ahost, strerror(errno));
sigsetmask(oldmask); sigsetmask(oldmask);
return (-1); return (-1);
} }
@ -142,8 +172,8 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
lport = 0; lport = 0;
} else { } else {
char num[8]; char num[8];
int s2 = rresvport(&lport), s3; int s2 = rresvport_af(&lport, ai->ai_family), s3;
int len = sizeof(from); int len = ai->ai_addrlen;
int nfds; int nfds;
if (s2 < 0) if (s2 < 0)
@ -180,11 +210,24 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
goto bad; goto bad;
} }
s3 = accept(s2, (struct sockaddr *)&from, &len); s3 = accept(s2, (struct sockaddr *)&from, &len);
switch (from.ss_family) {
case AF_INET:
aport = ntohs(((struct sockaddr_in *)&from)->sin_port);
break;
#ifdef INET6
case AF_INET6:
aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port);
break;
#endif
default:
aport = 0; /* error */
break;
}
/* /*
* XXX careful for ftp bounce attacks. If discovered, shut them * XXX careful for ftp bounce attacks. If discovered, shut them
* down and check for the real auxiliary channel to connect. * down and check for the real auxiliary channel to connect.
*/ */
if (from.sin_family == AF_INET && from.sin_port == htons(20)) { if (aport == 20) {
_libc_close(s3); _libc_close(s3);
goto again; goto again;
} }
@ -196,10 +239,7 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
goto bad; goto bad;
} }
*fd2p = s3; *fd2p = s3;
from.sin_port = ntohs((u_short)from.sin_port); if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) {
if (from.sin_family != AF_INET ||
from.sin_port >= IPPORT_RESERVED ||
from.sin_port < IPPORT_RESERVED / 2) {
(void)fprintf(stderr, (void)fprintf(stderr,
"socket: protocol failure in circuit setup.\n"); "socket: protocol failure in circuit setup.\n");
goto bad2; goto bad2;
@ -222,6 +262,7 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
goto bad2; goto bad2;
} }
sigsetmask(oldmask); sigsetmask(oldmask);
freeaddrinfo(res);
return (s); return (s);
bad2: bad2:
if (lport) if (lport)
@ -229,21 +270,46 @@ rcmd(ahost, rport, locuser, remuser, cmd, fd2p)
bad: bad:
(void)_libc_close(s); (void)_libc_close(s);
sigsetmask(oldmask); sigsetmask(oldmask);
freeaddrinfo(res);
return (-1); return (-1);
} }
int int
rresvport(alport) rresvport(port)
int *alport; int *port;
{ {
struct sockaddr_in sin; return rresvport_af(port, AF_INET);
int s; }
bzero(&sin, sizeof sin); int
sin.sin_len = sizeof(struct sockaddr_in); rresvport_af(alport, family)
sin.sin_family = AF_INET; int *alport, family;
sin.sin_addr.s_addr = INADDR_ANY; {
s = socket(AF_INET, SOCK_STREAM, 0); int i, s, len, err;
struct sockaddr_storage ss;
u_short *sport;
memset(&ss, 0, sizeof(ss));
ss.ss_family = family;
switch (family) {
case AF_INET:
((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in);
sport = &((struct sockaddr_in *)&ss)->sin_port;
((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY;
break;
#ifdef INET6
case AF_INET6:
((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6);
sport = &((struct sockaddr_in6 *)&ss)->sin6_port;
((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any;
break;
#endif
default:
errno = EAFNOSUPPORT;
return -1;
}
s = socket(ss.ss_family, SOCK_STREAM, 0);
if (s < 0) if (s < 0)
return (-1); return (-1);
#if 0 /* compat_exact_traditional_rresvport_semantics */ #if 0 /* compat_exact_traditional_rresvport_semantics */
@ -255,12 +321,13 @@ rresvport(alport)
return (-1); return (-1);
} }
#endif #endif
sin.sin_port = 0; *sport = 0;
if (bindresvport(s, &sin) == -1) { if (bindresvport2(s, (struct sockaddr *)&ss,
((struct sockaddr *)&ss)->sa_len) == -1) {
(void)_libc_close(s); (void)_libc_close(s);
return (-1); return (-1);
} }
*alport = (int)ntohs(sin.sin_port); *alport = (int)ntohs(*sport);
return (s); return (s);
} }
@ -272,18 +339,34 @@ ruserok(rhost, superuser, ruser, luser)
const char *rhost, *ruser, *luser; const char *rhost, *ruser, *luser;
int superuser; int superuser;
{ {
struct hostent *hp; return ruserok_af(rhost, superuser, ruser, luser, AF_INET);
u_int32_t addr; }
char **ap;
if ((hp = gethostbyname(rhost)) == NULL) int
ruserok_af(rhost, superuser, ruser, luser, af)
const char *rhost, *ruser, *luser;
int superuser, af;
{
struct hostent *hp;
union {
struct in_addr addr_in;
struct in6_addr addr_in6;
} addr;
char **ap;
int ret, h_error;
if ((hp = getipnodebyname(rhost, af, AI_DEFAULT, &h_error)) == NULL)
return (-1); return (-1);
ret = -1;
for (ap = hp->h_addr_list; *ap; ++ap) { for (ap = hp->h_addr_list; *ap; ++ap) {
bcopy(*ap, &addr, sizeof(addr)); bcopy(*ap, &addr, hp->h_length);
if (iruserok(addr, superuser, ruser, luser) == 0) if (iruserok_af(&addr, superuser, ruser, luser, af) == 0) {
return (0); ret = 0;
break;
}
} }
return (-1); freehostent(hp);
return (ret);
} }
/* /*
@ -300,6 +383,16 @@ iruserok(raddr, superuser, ruser, luser)
unsigned long raddr; unsigned long raddr;
int superuser; int superuser;
const char *ruser, *luser; const char *ruser, *luser;
{
return iruserok_af(&raddr, superuser, ruser, luser, AF_INET);
}
int
iruserok_af(raddr, superuser, ruser, luser, af)
void *raddr;
int superuser;
const char *ruser, *luser;
int af;
{ {
register char *cp; register char *cp;
struct stat sbuf; struct stat sbuf;
@ -308,12 +401,25 @@ iruserok(raddr, superuser, ruser, luser)
uid_t uid; uid_t uid;
int first; int first;
char pbuf[MAXPATHLEN]; char pbuf[MAXPATHLEN];
int len = 0;
switch (af) {
case AF_INET:
len = sizeof(struct in_addr);
break;
#ifdef INET6
case AF_INET6:
len = sizeof(struct in6_addr);
break;
#endif
}
first = 1; first = 1;
hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r"); hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "r");
again: again:
if (hostf) { if (hostf) {
if (__ivaliduser(hostf, (u_int32_t)raddr, luser, ruser) == 0) { if (__ivaliduser_af(hostf, raddr, luser, ruser, af, len)
== 0) {
(void)fclose(hostf); (void)fclose(hostf);
return (0); return (0);
} }
@ -375,6 +481,17 @@ __ivaliduser(hostf, raddr, luser, ruser)
FILE *hostf; FILE *hostf;
u_int32_t raddr; u_int32_t raddr;
const char *luser, *ruser; const char *luser, *ruser;
{
return __ivaliduser_af(hostf, &raddr, luser, ruser, AF_INET,
sizeof(raddr));
}
int
__ivaliduser_af(hostf, raddr, luser, ruser, af, len)
FILE *hostf;
void *raddr;
const char *luser, *ruser;
int af, len;
{ {
register char *user, *p; register char *user, *p;
int ch; int ch;
@ -383,6 +500,7 @@ __ivaliduser(hostf, raddr, luser, ruser)
struct hostent *hp; struct hostent *hp;
/* Presumed guilty until proven innocent. */ /* Presumed guilty until proven innocent. */
int userok = 0, hostok = 0; int userok = 0, hostok = 0;
int h_error;
#ifdef YP #ifdef YP
char *ypdomain; char *ypdomain;
@ -392,11 +510,11 @@ __ivaliduser(hostf, raddr, luser, ruser)
#define ypdomain NULL #define ypdomain NULL
#endif #endif
/* We need to get the damn hostname back for netgroup matching. */ /* We need to get the damn hostname back for netgroup matching. */
if ((hp = gethostbyaddr((char *)&raddr, sizeof(u_int32_t), if ((hp = getipnodebyaddr((char *)raddr, len, af, &h_error)) == NULL)
AF_INET)) == NULL)
return (-1); return (-1);
strncpy(hname, hp->h_name, sizeof(hname)); strncpy(hname, hp->h_name, sizeof(hname));
hname[sizeof(hname) - 1] = '\0'; hname[sizeof(hname) - 1] = '\0';
freehostent(hp);
while (fgets(buf, sizeof(buf), hostf)) { while (fgets(buf, sizeof(buf), hostf)) {
p = buf; p = buf;
@ -438,7 +556,8 @@ __ivaliduser(hostf, raddr, luser, ruser)
hostok = innetgr((char *)&buf[2], hostok = innetgr((char *)&buf[2],
(char *)&hname, NULL, ypdomain); (char *)&hname, NULL, ypdomain);
else /* match a host by addr */ else /* match a host by addr */
hostok = __icheckhost(raddr,(char *)&buf[1]); hostok = __icheckhost(raddr,(char *)&buf[1],
af, len);
break; break;
case '-': /* reject '-' hosts and all their users */ case '-': /* reject '-' hosts and all their users */
if (buf[1] == '@') { if (buf[1] == '@') {
@ -446,12 +565,12 @@ __ivaliduser(hostf, raddr, luser, ruser)
(char *)&hname, NULL, ypdomain)) (char *)&hname, NULL, ypdomain))
return(-1); return(-1);
} else { } else {
if (__icheckhost(raddr,(char *)&buf[1])) if (__icheckhost(raddr,(char *)&buf[1],af,len))
return(-1); return(-1);
} }
break; break;
default: /* if no '+' or '-', do a simple match */ default: /* if no '+' or '-', do a simple match */
hostok = __icheckhost(raddr, buf); hostok = __icheckhost(raddr, buf, af, len);
break; break;
} }
switch(*user) { switch(*user) {
@ -494,27 +613,37 @@ __ivaliduser(hostf, raddr, luser, ruser)
* Returns "true" if match, 0 if no match. * Returns "true" if match, 0 if no match.
*/ */
static int static int
__icheckhost(raddr, lhost) __icheckhost(raddr, lhost, af, len)
u_int32_t raddr; void *raddr;
register char *lhost; register char *lhost;
int af, len;
{ {
register struct hostent *hp; register struct hostent *hp;
register u_int32_t laddr; char laddr[BUFSIZ]; /* xxx */
register char **pp; register char **pp;
int h_error;
int match;
/* Try for raw ip address first. */ /* Try for raw ip address first. */
if (isdigit((unsigned char)*lhost) && (u_int32_t)(laddr = inet_addr(lhost)) != -1) if (inet_pton(af, lhost, laddr) == 1) {
return (raddr == laddr); if (memcmp(raddr, laddr, len) == 0)
return (1);
else
return (0);
}
/* Better be a hostname. */ /* Better be a hostname. */
if ((hp = gethostbyname(lhost)) == NULL) if ((hp = getipnodebyname(lhost, af, AI_DEFAULT, &h_error)) == NULL)
return (0); return (0);
/* Spin through ip addresses. */ /* Spin through ip addresses. */
match = 0;
for (pp = hp->h_addr_list; *pp; ++pp) for (pp = hp->h_addr_list; *pp; ++pp)
if (!bcmp(&raddr, *pp, sizeof(u_int32_t))) if (!bcmp(raddr, *pp, len)) {
return (1); match = 1;
break;
}
/* No match. */ freehostent(hp);
return (0); return (match);
} }

View file

@ -44,7 +44,8 @@ crypt.h: ${RPCDIR}/crypt.x
# #
# MAN1+= rstat.1 # MAN1+= rstat.1
MAN3+= bindresvport.3 des_crypt.3 getrpcent.3 getrpcport.3 publickey.3 rpc.3 \ MAN3+= bindresvport.3 bindresvport2.3 des_crypt.3 getrpcent.3 getrpcport.3 \
publickey.3 rpc.3 \
rpc_secure.3 rtime.3 rpc_secure.3 rtime.3
MAN5+= publickey.5 rpc.5 MAN5+= publickey.5 rpc.5
# MAN8+= rstat_svc.8 # MAN8+= rstat_svc.8

View file

@ -30,3 +30,5 @@ If the value of sin->sin_port is non-zero
.Fn bindresvport .Fn bindresvport
will attempt to use that specific port. If it fails, it chooses another will attempt to use that specific port. If it fails, it chooses another
privileged port automatically. privileged port automatically.
.Sh "SEE ALSO"
.Xr bindresvport2 3

View file

@ -55,7 +55,6 @@ bindresvport(sd, sin)
int sd; int sd;
struct sockaddr_in *sin; struct sockaddr_in *sin;
{ {
int on, old, error;
struct sockaddr_in myaddr; struct sockaddr_in myaddr;
int sinlen = sizeof(struct sockaddr_in); int sinlen = sizeof(struct sockaddr_in);
@ -69,39 +68,82 @@ bindresvport(sd, sin)
return (-1); return (-1);
} }
if (sin->sin_port == 0) { return (bindresvport2(sd, sin, sinlen));
}
int
bindresvport2(sd, sa, addrlen)
int sd;
struct sockaddr *sa;
socklen_t addrlen;
{
int on, old, error, level, optname;
u_short port;
if (sa == NULL) {
errno = EINVAL;
return (-1);
}
switch (sa->sa_family) {
case AF_INET:
port = ntohs(((struct sockaddr_in *)sa)->sin_port);
level = IPPROTO_IP;
optname = IP_PORTRANGE;
on = IP_PORTRANGE_LOW;
break;
#ifdef INET6
case AF_INET6:
port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
level = IPPROTO_IPV6;
optname = IPV6_PORTRANGE;
on = IPV6_PORTRANGE_LOW;
break;
#endif
default:
errno = EAFNOSUPPORT;
return (-1);
}
if (port == 0) {
int oldlen = sizeof(old); int oldlen = sizeof(old);
error = getsockopt(sd, IPPROTO_IP, IP_PORTRANGE, error = getsockopt(sd, level, optname, &old, &oldlen);
&old, &oldlen);
if (error < 0) if (error < 0)
return(error); return(error);
on = IP_PORTRANGE_LOW; error = setsockopt(sd, level, optname, &on, sizeof(on));
error = setsockopt(sd, IPPROTO_IP, IP_PORTRANGE,
&on, sizeof(on));
if (error < 0) if (error < 0)
return(error); return(error);
} }
error = bind(sd, (struct sockaddr *)sin, sinlen); error = bind(sd, sa, addrlen);
if (sin->sin_port == 0) { switch (sa->sa_family) {
case AF_INET:
port = ntohs(((struct sockaddr_in *)sa)->sin_port);
break;
#ifdef INET6
case AF_INET6:
port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
break;
#endif
default: /* shoud not match here */
errno = EAFNOSUPPORT;
return (-1);
}
if (port == 0) {
int saved_errno = errno; int saved_errno = errno;
if (error) { if (error) {
if (setsockopt(sd, IPPROTO_IP, IP_PORTRANGE, if (setsockopt(sd, level, optname,
&old, sizeof(old)) < 0) &old, sizeof(old)) < 0)
errno = saved_errno; errno = saved_errno;
return (error); return (error);
} }
if (sin != &myaddr) { /* Hmm, what did the kernel assign... */
/* Hmm, what did the kernel assign... */ if (getsockname(sd, (struct sockaddr *)sa, &addrlen) < 0)
if (getsockname(sd, (struct sockaddr *)sin, errno = saved_errno;
&sinlen) < 0) return (error);
errno = saved_errno;
return (error);
}
} }
return (error); return (error);
} }