[Bug 248579] PRoblem with accept(2) and dual IPv4/IPv6 TCP servers

bugzilla-noreply at freebsd.org bugzilla-noreply at freebsd.org
Mon Aug 10 16:55:31 UTC 2020


https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=248579

            Bug ID: 248579
           Summary: PRoblem with accept(2) and dual IPv4/IPv6 TCP servers
           Product: Base System
           Version: 12.1-RELEASE
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Some People
          Priority: ---
         Component: kern
          Assignee: bugs at FreeBSD.org
          Reporter: rec at rcousins.com

While writing a TCP server designed to accept both IPv4 and IPv6 connections,
I've found that the recommended way fails, but the same code runs under Linux
perfectly. The failure is that IPv6 connections succeed and IPv4 connections
always fail.

Way to exercise bug:
- Compile code below (cc -o foo foo.c will work)
- run command ("./foo") in one window
- In another window try to connect to port 5000
  for example: telnet ::1 5000 <-- should work
               telnet 127.0.0.1 5000 <-- should work but fails 100% of the
time.

Repeating this test under modern Linux results in both telnet sessions
connecting.

Code to exercise bug:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int
make_listen(char *port)
{
  struct addrinfo hints, *res,  *res0;
  int error;
  int s;
  const char *cause = NULL;

  memset(&hints, 0, sizeof(hints));
  //  hints.ai_family = PF_UNSPEC;
  hints.ai_family = AF_INET6;  
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = IPPROTO_TCP;
  hints.ai_flags = AI_PASSIVE;

  if ((error = getaddrinfo(NULL, port, &hints, &res0)))
    errx(1, "%s", gai_strerror(error));

  for (res = res0; res; res = res->ai_next) {
    printf("Trying Family=%d, Socktype=%d, Protocol=%d\n",
           res->ai_family, 
           res->ai_socktype,
           res->ai_protocol);
    if ((s = socket(res->ai_family, 
                    res->ai_socktype,
                    res->ai_protocol)) < 0) {
      cause = "socket";
      continue;
    }
    int yes = 1;
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
      errx(1,"Setsockopt(SO_REUSEADDR) failed.");

    if (bind(s,
             res->ai_addr,
             res->ai_addrlen) < 0) {
      cause = "bind";
      close(s);
      continue;
    }
    listen(s, 5);
    freeaddrinfo(res0);
    return s;
  }
  err(1, "%s",  cause);
  freeaddrinfo(res0);
  return -1;
}

int
main(int argc, char *argv[])
{
  int listen_fd = make_listen("5000");

  while (1) {
    char addr[80];
    struct sockaddr_storage client_addr;
    socklen_t client_addr_len = sizeof(client_addr);

    int fc = accept(listen_fd,
                    (struct sockaddr *)&client_addr,
                    &client_addr_len);
    if (fc < 0) {
      warn("Accept()");
      continue;
    }

    if (client_addr.ss_family == AF_INET) {
      struct sockaddr_in *p = (struct sockaddr_in *)&client_addr;
      inet_ntop(p->sin_family, &p->sin_addr,
              addr,sizeof(addr));
    } else {
      struct sockaddr_in6 *p = (struct sockaddr_in6 *)&client_addr;
      inet_ntop(p->sin6_family, &p->sin6_addr,
              addr,sizeof(addr));
    }
    printf("Connection from (%d) %s\n",
           client_addr_len,
           addr);
    (void)write(fc,"Hello\n",6);
    close(fc);
  }
}

-- 
You are receiving this mail because:
You are the assignee for the bug.


More information about the freebsd-bugs mailing list