Bug#164768: libc: IPv6 still not correct.
Package: libc6
Version: 2.2.5-11.2
Severity: important
Tags: patch
Hi,
This bug was in fact once #82468 (now archived), but having checked
the libc code (and retried the test case), it still exists (and so
really needs fixing.
Executive summary: libc uses an incorrectly-sized sockaddr_in6
structure, which causes programs running under 2.2 kernels to get
error messages when in fact they are behving correctly. Hence the
important severity.
A 2.2 kernel uses:
struct sockaddr_in6 {
unsigned short int sin6_family; /* AF_INET6 */
__u16 sin6_port; /* Transport layer port # */
__u32 sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
};
While 2.4 (and glibc complies) use:
struct sockaddr_in6 {
unsigned short int sin6_family; /* AF_INET6 */
__u16 sin6_port; /* Transport layer port # */
__u32 sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
__u32 sin6_scope_id; /* scope id (new in RFC2553) */
};
getpeername returns the kernel's idea of a sockaddr_in6 when using an
ipv6 socket. Now getnameinfo has the following code
case AF_INET6:
if (addrlen < sizeof (struct sockaddr_in6))
return EAI_FAMILY;
in which the sizeof uses the libc idea of an sockaddr_in6...
Test code:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
int
main(int argc, char **argv) {
int sockfd;
struct addrinfo req,*ans;
struct sockaddr_storage tmp;
int len = sizeof(struct sockaddr_storage);
memset(&req,0,sizeof(req));
req.ai_flags = 0;
req.ai_family = PF_INET6;
req.ai_socktype = SOCK_STREAM;
req.ai_protocol =IPPROTO_TCP;
if (getaddrinfo("spacelabs.nl","smtp",&req,&ans) != 0) {
printf("getaddrinfo failed\n");
return -1;
}
sockfd = socket(ans->ai_family,ans->ai_socktype,ans->ai_protocol);
if (connect(sockfd,ans->ai_addr,ans->ai_addrlen) < 0) {
printf("connect failed\n");
return -1;
}
if (getpeername(sockfd,(struct sockaddr *) &tmp,&len) < 0) {
printf("getpeernamed failed\n");
return -1;
}
printf("getpeername len -> %d, sizeof(sockaddr_in6) -> %d\n",
len, sizeof(struct sockaddr_in6));
exit(0);
}
Running this on a current stable libc with 2.2 kernel gives:
getpeername len -> 24, sizeof(sockaddr_in6) -> 28
whilst with a 2.4 kernel, one gets:
getpeername len -> 28, sizeof(sockaddr_in6) -> 28
Now this is a problem if you do, for example:
int
get_sock_port(int sock, int local)
{
struct sockaddr_storage from;
socklen_t fromlen;
char strport[NI_MAXSERV];
/* Get IP address of client. */
fromlen = sizeof(from);
memset(&from, 0, sizeof(from));
if (local) {
if (getsockname(sock, (struct sockaddr *)&from, &fromlen) < 0) {
error("getsockname failed: %.100s", strerror(errno));
return 0;
}
} else {
if (getpeername(sock, (struct sockaddr *) & from, &fromlen) < 0)
{
debug("getpeername failed: %.100s", strerror(errno));
fatal_cleanup();
}
}
/* Return port number. */
if (getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0,
strport, sizeof(strport), NI_NUMERICSERV) != 0)
fatal("get_sock_port: getnameinfo NI_NUMERICSERV failed");
return atoi(strport);
}
[code from SSH] getnameinfo barfs at this point on a 2.2 kernel.
Now, a fix to libc might be:
--- glibc-2.2.5/inet/getnameinfo.c Tue Jan 30 00:23:05 2001
+++ glibc-2.2.5-fix/inet/getnameinfo.c Sun Aug 4 01:15:42 2002
@@ -62,6 +62,13 @@
# define min(x,y) (((x) > (y)) ? (y) : (x))
#endif /* min */
+struct __sockaddr_in6_rfc2133
+ {
+ __SOCKADDR_COMMON (__sin6_);
+ in_port_t __sin6_port;
+ uint32_t __sin6_flowinfo;
+ struct in6_addr __sin6_addr;
+ };
static char *
internal_function
@@ -193,7 +200,7 @@
return EAI_FAMILY;
break;
case AF_INET6:
- if (addrlen < sizeof (struct sockaddr_in6))
+ if (addrlen < sizeof (struct __sockaddr_in6_rfc2133))
return EAI_FAMILY;
break;
default:
@@ -298,7 +305,8 @@
c = inet_ntop (AF_INET6,
(const void *) &sin6p->sin6_addr, host, hostlen);
- scopeid = sin6p->sin6_scope_id;
+ scopeid = (addrlen >= sizeof(struct sockaddr_in6)) ?
+ sin6p->sin6_scope_id : 0;
if (scopeid != 0)
{
/* Buffer is >= IFNAMSIZ+1. */
For more information on this bug, please see
http://bugs.debian.org/82468
Thanks,
Matthew
--
Rapun.sel - outermost outpost of the Pick Empire
http://www.pick.ucam.org
Reply to: