Subject: kern/22522: connect(2) may fail with EINVAL which isn't undocumented.
To: None <gnats-bugs@gnats.netbsd.org>
From: Takahiro Kambe <taca@back-street.net>
List: netbsd-bugs
Date: 08/18/2003 12:16:03
>Number: 22522
>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 UTC 2003
>Closed-Date:
>Last-Modified:
>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: