Subject: Should connect() be able to to be called again after ECONNREFUSED?
To: None <tech-net@netbsd.org>
From: Sean Boudreau <seanb@qnx.com>
List: tech-net
Date: 06/17/2003 16:35:28
Hello:
Should one be able to call connect() again on a tcp
socket after a failed attempt. The problem that
arises is that the RST handling calls tcp_drop() which
calls tcp_close() which in turn disassociates the socket
from the protocol (in_pcbdetach() etc.). As such one
currently has to close() and create a new socket.
FWIW, I'm told Linux allows this (haven't confirmed).
Here's a diff on a quick fix that allows this. I'm not
sure it's the best solution or even if it's worth allowing
at all. I added the PR_CONNATTCH flag to restrict it
to tcp sockets for now but that may not be required.
Index: kern/uipc_socket.c
===================================================================
RCS file: /cvsroot/src/sys/kern/uipc_socket.c,v
retrieving revision 1.66.4.1
diff -c -r1.66.4.1 uipc_socket.c
*** kern/uipc_socket.c 2002/06/11 01:59:49 1.66.4.1
--- kern/uipc_socket.c 2003/06/17 20:27:12
***************
*** 518,523 ****
--- 518,532 ----
if (so->so_options & SO_ACCEPTCONN)
return (EOPNOTSUPP);
s = splsoftnet();
+
+ if (so->so_pcb == NULL && (so->so_proto->pr_flags & PR_CONATTCH)) {
+ if (error = (*so->so_proto->pr_usrreq)(so, PRU_ATTACH,
+ (struct mbuf *)0, (struct mbuf *)(long)so->so_proto->pr_protocol,
+ (struct mbuf *)0, p)) {
+ splx(s);
+ return (error);
+ }
+ }
/*
* If protocol is connection-based, can only connect once.
* Otherwise, if connected, try to disconnect first.
To demonstrate the issue:
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
int
main(void)
{
int s;
struct sockaddr_in s_in;
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
return 1;
}
memset(&s_in, 0x00, sizeof(s_in));
s_in.sin_family = AF_INET;
s_in.sin_len = sizeof(s_in);
s_in.sin_addr.s_addr = inet_addr("127.0.0.1");
s_in.sin_port = htons(1234);
if (connect(s, (struct sockaddr *)&s_in, sizeof(s_in)) != -1) {
fprintf(stderr, "disable port 1234\n");
return 1;
}
/* Assume discard is working but doesn't matter as it never gets that far */
s_in.sin_port = htons(9);
if (connect(s, (struct sockaddr *)&s_in, sizeof(s_in)) == -1) {
fprintf(stderr, "should this connect() have worked? errno = %d\n", errno);
return 1;
}
printf("Success?\n");
return 0;
}