DatagramChannel::disconnect appears to rebind socket to the wildcard address

Kurt Miller kurt at intricatesoftware.com
Wed Sep 25 18:50:47 UTC 2019


On Wed, 2019-09-25 at 18:38 +0100, Alan Bateman wrote:
> Is this project this active? Are there folks here that work on the 
> xxxBSD ports?
> 
> JDK-8231259 [1] is a bug submitted against the 
> java.nio.channels.DatagramChannel implementation on macOS a few days 
> ago. It looks like a macOS bug, rather than a JDK bug.
> 
> The scenario is a UDP socket is bound to a specific local address, 
> connected, and then disconnected (to dissolve the association). It seems 
> that the disconnect changes the local address (as reported by 
> getsocketname) to the wildcard address (INADDR_ANY or the IPv6 
> equivalent). There are several ways to disconnect (disconnectx, connect 
> with an invalid address, or connect with a family of AF_UNSPEC) and the 
> unexplained behavior arises with all.
> 
> If there are still people here then I'm wondering if the same behavior 
> is observed on other BSD operating systems. That is, does UDP disconnect 
> deliberately change the local address to INADDR_ANY?
> 
> Thanks,
> 
> -Alan
> 
> [1] https://bugs.openjdk.java.net/browse/JDK-8231259
> 

Hi Alan,

The project is active but currently we are using github forks of
AdoptOpenJDK.

Yes, I have observed that bug on OpenBSD as well. It is indeed
a kernel bug on OpenBSD. I have kernel patches to correct it
but haven't pushed them forward through the review process there.

I just did a quick test on FreeBSD 11 on amd64 and it also
doesn't retain the bound address. 

Regards,
-Kurt

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h>
#include <unistd.h> 
#include <errno.h> 
#include <netdb.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <sys/param.h>
#include <arpa/inet.h> 
#include <netinet/in.h>
  
int main() 
{ 
    char hostname[MAXHOSTNAMELEN]; 
    struct hostent *he; 
    int fd;
    struct sockaddr addr;
    struct sockaddr addr1;
    socklen_t socklen;
    char ip6str[INET6_ADDRSTRLEN];

    if (gethostname(hostname, sizeof(hostname)) != 0) {
        perror("gethostname failed"); 
        exit(1); 
    }
  
    if ((he = gethostbyname(hostname)) == NULL) {
        perror("gethostbyname failed"); 
        exit(1); 
    }

    memset(&addr, 0, sizeof(addr));
    addr.sa_family = he->h_addrtype;
    if (addr.sa_family == AF_INET6)  {
        memcpy(&((struct sockaddr_in6 *)&addr)->sin6_addr, he->h_addr, he->h_length);
    } else if (addr.sa_family == AF_INET) {
        memcpy(&((struct sockaddr_in *)&addr)->sin_addr.s_addr, he->h_addr, he->h_length);
    } else {
        perror("unknown address family");
        exit(1);
    }

    if ((fd = socket(addr.sa_family, SOCK_DGRAM, 0)) < 0) {
        perror("socket failed");
        exit(1);
    }

    if (bind(fd, &addr, sizeof(addr)) != 0) {
        perror("bind failed");
        exit(1);
    }

    /* retrive bound address info into addr and print results */

    memset(&addr, 0, sizeof(addr));
    socklen = sizeof(addr);

    if (getsockname(fd, &addr, &socklen) != 0) {
        perror("getsockname failed");
        exit(1);
    }

    if (addr.sa_family == AF_INET6) {
        inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr, ip6str, sizeof(ip6str));
        printf("Bind IP:   %s\n", ip6str); 
        printf("Bind Port: %d\n", ((struct sockaddr_in6 *)&addr)->sin6_port); 
    } else if (addr.sa_family == AF_INET) {
        printf("Bind IP:   %s\n", inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr)); 
        printf("Bind Port: %d\n", ((struct sockaddr_in *)&addr)->sin_port); 
    }

    /* setup addr1 for connect on port 16000 */
    memset(&addr1, 0, sizeof(addr1));
    addr1.sa_family = he->h_addrtype;
    if (addr1.sa_family == AF_INET6)  {
        memcpy(&((struct sockaddr_in6 *)&addr1)->sin6_addr, he->h_addr, he->h_length);
        ((struct sockaddr_in6 *)&addr1)->sin6_port = 16000;
    } else {
        memcpy(&((struct sockaddr_in *)&addr1)->sin_addr.s_addr, he->h_addr, he->h_length);
        ((struct sockaddr_in *)&addr1)->sin_port = 16000;
    }

    if (connect(fd, &addr1, sizeof(addr1)) != 0) {
        perror("connect failed");
        exit(1);
    }
  
    /* disconnect dgram socket */

    memset(&addr1, 0, sizeof(addr1));
    addr1.sa_family = AF_UNSPEC;

    /* disconnecting always returns EAFNOSUPPORT */
    connect(fd, &addr1, sizeof(addr1));

    /* re-retrive bound address info into addr1 */
  
    memset(&addr1, 0, sizeof(addr1));
    if (getsockname(fd, &addr1, &socklen) != 0) {
        perror("getsockname failed");
        exit(1);
    }

    if (addr1.sa_family == AF_INET6) {
        inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr1)->sin6_addr, ip6str, sizeof(ip6str));
        printf("Bind IP:   %s\n", ip6str); 
        printf("Bind Port: %d\n", ((struct sockaddr_in6 *)&addr1)->sin6_port); 
    } else if (addr1.sa_family == AF_INET) {
        printf("Bind IP:   %s\n", inet_ntoa(((struct sockaddr_in *)&addr1)->sin_addr)); 
        printf("Bind Port: %d\n", ((struct sockaddr_in *)&addr1)->sin_port); 
    }

    int ret = memcmp(&addr, &addr1, sizeof(addr1));
    if (ret == 0)
        printf("socket bind was not lost\n");
    else
        printf("socket bind changed\n");
    return ret; 
}


More information about the bsd-port-dev mailing list