NetBSD Problem Report #22522

Received: (qmail 1529 invoked by uid 605); 18 Aug 2003 03:16:16 -0000
Message-Id: <200308180316.h7I3G3pv004465@edge.back-street.net>
Date: Mon, 18 Aug 2003 12:16:03 +0900 (JST)
From: Takahiro Kambe <taca@back-street.net>
Sender: gnats-bugs-owner@NetBSD.org
Reply-To: taca@back-street.net
To: gnats-bugs@gnats.netbsd.org
Subject: connect(2) may fail with EINVAL which isn't undocumented.
X-Send-Pr-Version: 3.95

>Number:         22522
>Notify-List:    gson@gson.org
>Category:       kern
>Synopsis:       connect(2) may fail with EINVAL which isn't undocumented.
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Mon Aug 18 03:17:00 +0000 2003
>Closed-Date:    
>Last-Modified:  Sat Jan 17 14:50:21 +0000 2015
>Originator:     Takahiro Kambe
>Release:        NetBSD 1.6W
>Organization:
Takahiro Kambe
>Environment:


System: NetBSD edge.back-street.net 1.6W NetBSD 1.6W (CF-R1) #0: Tue Aug 12 22:19:46 JST 2003 taca@edge.back-street.net:/var/obj/sys/arch/i386/compile.i386/CF-R1 i386
Architecture: i386
Machine: i386
>Description:
	connect(2) may fail with EINVAL when using non-blocking I/O, but it
	isn't documented connect(2).

	When using non-blocking I/O, connect(2) may return EINPROGRESS and
	after that repeating connect(2) will is expected to return with::

		1. Still in inprogress as EALREADY error.
		2. Already connected as EISCONN error.

	But when remote host refused connection, following connect(2)
	may returns EINVAL. And in this case, real error can't fetch with
	getsockopt(2).

>How-To-Repeat:
	Here is test program and specify host and unused (not listened) port
	as argument.

		% cc test.c
		% ./a.out 192.168.32.10 12345
		connect to family = 2, port = 234, addr = 192.168.32.10
		connect: Operation now in progress
		getsockopt => Connection refused
		connect to family = 2, port = 234, addr = 192.168.32.10
		connect: Invalid argument
		getsockopt => 0

	Don't test with localhost.

-------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

static int connect_test(int, struct sockaddr_in *, int, int);

#define GETSOCKOPT_AFTER_EINPROGRESS	1
#define GETSOCKOPT_AFTER_EINVAL		1

static int exit_after_getsockopt = 0;

int
main(int argc, char **argv)
{
	char *host, *port;
	struct hostent *ht;
	unsigned short p;
	int fd, status, n;
	struct sockaddr_in sin;

	if (argc == 3) {
		host = argv[1];
		port = argv[2];
	} else {
		fputs("Need host and port as parameter.\n", stderr);
		exit(1);
	}
	n = atoi(port);
	if (n <= 0 || n > 65535) {
		fputs("Invalid port number.\n", stderr);
		exit(1);
	}
	p = n;

	ht = gethostbyname(host);
	if (ht == NULL) {
		herror(host);
	}
	memset(&sin, '\0', sizeof sin);
	sin.sin_family = AF_INET;
	sin.sin_port = htons(p);
	memcpy(&sin.sin_addr, ht->h_addr_list[0], ht->h_length);

	fd = socket(PF_INET, SOCK_STREAM, 0);
	status = connect_test(fd, &sin, sizeof sin, 0);
	return(status);
}

static int
connect_test(fd, sockaddr, len, socks)
    int fd;
    struct sockaddr_in *sockaddr;
    int len;
    int socks;
{
    int status;
    int mode;
#if (GETSOCKOPT_AFTER_EINPROGRESS == 1) || (GETSOCKOPT_AFTER_EINVAL == 1)
    int value;
#endif

    mode = fcntl(fd, F_GETFL, 0);

    fcntl(fd, F_SETFL, mode | O_NONBLOCK);

    for (;;) {
	printf("connect to family = %d, port = %d, addr = %s\n",
	       sockaddr->sin_family, ntohs(sockaddr->sin_port),
	       inet_ntoa(sockaddr->sin_addr));

	status = connect(fd, (struct sockaddr *)sockaddr, len);
	if (status < 0) {
		perror("connect");
		switch (errno) {
		case EAGAIN:
		case EINPROGRESS:
#if (GETSOCKOPT_AFTER_EINPROGRESS == 1)
			len = sizeof(value);
			getsockopt(fd, SOL_SOCKET, SO_ERROR, &value, &len);
			printf("getsockopt => %s\n",
				(value != 0) ? strerror(value): "0");
			if (exit_after_getsockopt)
				goto error;
#endif
			continue;
		case EISCONN:
			status = 0;
			break;
		case EINVAL:
#if (GETSOCKOPT_AFTER_EINVAL == 1)
			len = sizeof(value);
			getsockopt(fd, SOL_SOCKET, SO_ERROR, &value, &len);
			printf("getsockopt => %s\n",
				(value != 0) ? strerror(value): "0");
			if (exit_after_getsockopt)
				goto error;
#endif
			break;
		default:
			break;
		}
	}
#if (GETSOCKOPT_AFTER_EINPROGRESS == 1) || (GETSOCKOPT_AFTER_EINVAL == 1)
    error: ;
#endif
	fcntl(fd, F_SETFL, mode);
	return status;
    }
}
-------------------------------------------------------------------------

>Fix:
	Though I don't know EINVAL is really valid errno for connect(2),
	etheir modification will be needed.

	o Document EINVAL to connect(2).
	o Fix connect(2) not to return EINVAL.


>Release-Note:
>Audit-Trail:
>Unformatted:

 Man pages rarely document all possible errno values for a system call.

 In this case, the error EINVAL comes from calling connect() with bad
 arguments, because the test loop trashes the len parameter.

 There are other errors that connect() may return, like
 ENOBUFS or EAFNOSUPPORT. All of them are documented on
 intro(2).

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.