NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

kern/58874: nonblocking connect() and getsockname() error conditions



>Number:         58874
>Category:       kern
>Synopsis:       nonblocking connect() and getsockname() error conditions
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Dec 05 00:30:01 +0000 2024
>Originator:     Taylor R Campbell
>Release:        10
>Organization:
The NetBSD EINPROGRESS
>Environment:
>Description:
NetBSD and Linux disagree on some error conditions of nonblocking connect() and getsockname() when the connection is refused, illustrated by the following program.

Maybe both are sensible and compatible with POSIX -- I don't know, I haven't assessed; I just stumbled upon the difference while running git-cinnabar tests: https://github.com/glandium/git-cinnabar/issues/340  So I'm filing this PR to make a record of the difference and any determination about it.

#include <sys/socket.h>

#include <err.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
	struct addrinfo *ai0, *ai, hints;
	int error;

	if (argc != 3)
		errx(1, "Usage: %s <host> <service>", argv[0]);

	memset(&hints, 0, sizeof(hints));
	hints.ai_socktype = SOCK_STREAM;
	error = getaddrinfo(argv[1], argv[2], &hints, &ai0);
	if (error)
		errx(1, "getaddrinfo: %s", gai_strerror(error));

	for (ai = ai0; ai != NULL; ai = ai->ai_next) {
		int fd;
		struct sockaddr_storage ss;
		socklen_t namelen;
		socklen_t optlen;
		socklen_t i;

		fd = socket(ai->ai_family, ai->ai_socktype|SOCK_NONBLOCK,
		    ai->ai_protocol);
		if (fd == -1) {
			warn("socket");
			continue;
		}
		if (connect(fd, ai->ai_addr, ai->ai_addrlen) != -1) {
			warnx("connect succeeded");
			(void)close(fd);
			continue;
		}
		if (errno != EINPROGRESS)
			err(1, "connect failed unexpectedly");
		warn("first connect");

		if (connect(fd, ai->ai_addr, ai->ai_addrlen) == -1)
			warn("second connect");
		else
			warnx("second connect succeeded!");

		if (getsockname(fd, (struct sockaddr *)&ss, &namelen) == -1) {
			warn("getsockname");
		} else {
			for (i = 0; i < namelen; i++) {
				fprintf(stderr, "%02hhx",
				    ((const char *)&ss)[i]);
			}
			fprintf(stderr, "\n");
		}
		optlen = sizeof(error);
		if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &optlen) ==
		    -1)
			err(1, "getsockopt(SO_ERROR)");
		errno = error;
		warn("connect SO_ERROR");
		if (connect(fd, ai->ai_addr, ai->ai_addrlen) == -1)
			warn("third connect");
		else
			warnx("third connect succeeded!");
		return 0;
	}
	errx(1, "no addresses");
}

>How-To-Repeat:
linux$ ./sn localhost 12345
sn: first connect: Operation now in progress
sn: second connect: Connection refused
00000000000000000000000000000000000000000000000000000000
sn: connect SO_ERROR: Success
sn: third connect: Operation now in progress

netbsd$ $ ./sn localhost 12345
sn: first connect: Operation now in progress
sn: second connect: Invalid argument
sn: getsockname: Invalid argument
sn: connect SO_ERROR: Connection refused
sn: third connect: Invalid argument
>Fix:
1. determine whether there is a problem here
2. maybe make a change



Home | Main Index | Thread Index | Old Index