From owner-freebsd-hackers@FreeBSD.ORG Mon Jan 10 12:09:43 2005 Return-Path: Delivered-To: freebsd-hackers@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id A28C116A4CE; Mon, 10 Jan 2005 12:09:43 +0000 (GMT) Received: from www.portaone.com (support.portaone.com [195.70.151.35]) by mx1.FreeBSD.org (Postfix) with ESMTP id AEF8643D31; Mon, 10 Jan 2005 12:09:42 +0000 (GMT) (envelope-from sobomax@portaone.com) Received: from [192.168.0.128] (sobohome.portaone.com [193.28.87.24]) (authenticated bits=0) by www.portaone.com (8.12.11/8.12.11) with ESMTP id j0AC9YGq058850 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Mon, 10 Jan 2005 13:09:39 +0100 (CET) (envelope-from sobomax@portaone.com) Message-ID: <41E27076.8080904@portaone.com> Date: Mon, 10 Jan 2005 14:09:26 +0200 From: Maxim Sobolev Organization: Porta Software Ltd User-Agent: Mozilla Thunderbird 1.0 (Windows/20041206) X-Accept-Language: en-us, en MIME-Version: 1.0 To: hackers@freebsd.org, current@freebsd.org Content-Type: multipart/mixed; boundary="------------040104060604090102080808" X-Virus-Scanned: ClamAV 0.80/589/Wed Nov 17 13:38:41 2004 clamav-milter version 0.80j on www.portaone.com X-Virus-Status: Clean X-Mailman-Approved-At: Mon, 10 Jan 2005 12:57:31 +0000 Subject: Attempt to invoke connect(2) on already connected unix domaindatagram socket fails with ECONNRESET X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 10 Jan 2005 12:09:43 -0000 This is a multi-part message in MIME format. --------------040104060604090102080808 Content-Type: text/plain; charset=KOI8-U; format=flowed Content-Transfer-Encoding: 7bit Folks, I've discovered very strange behaviour of the connect(2) system call when it's called on already connected unix domain datagram socket. In this case connect(2) fails with ECONNRESET, which is weird. ECONNRESET is not even listed among possible return values of connect(2). I've confirmed this behaviour at 4.10 and 5.3 systems. Linux doesn't exhibit this (mis?)behaviour. As long as I can tell, this behaviour contradicts documentation, connect(2) manpage says: Generally, stream sockets may successfully connect() only once; datagram sockets may use connect() multiple times to change their association. Attached please find small test program which illustrates the problem. It forks itsels at the start, child becomes a server, while parent a client. After each transaction server closes unix domain socket and opens its again, while the client attempts to re-connect() to that unix domain socket using already created socket object. This mimics real-world scenario in which I've encountered the problem. In this scenarion, there are two distinct processes communicating using unix domain socket. Client uses connect() on already connected socket object for performance reasons to avoid calling socket(2) for each transaction. Everything works just fine until server is restarted. After that any attempts to send command from the client to the server fails with ECONNRESET until the client is restarted as well. -Maxim --------------040104060604090102080808 Content-Type: text/plain; name="socket_test.c" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="socket_test.c" #include #include #include #include #include #include #include #include #include #include #include #define UDS_NAME "/tmp/uds_test.sock" #define sstosa(ss) ((struct sockaddr *)(ss)) static pid_t pid_kill; void prepare_ifsun(struct sockaddr_un *ifsun) { static char ch = '1' * 2; memset(ifsun, '\0', sizeof(*ifsun)); #if !defined(__linux__) && !defined(__solaris__) ifsun->sun_len = strlen(UDS_NAME); #endif ifsun->sun_family = AF_LOCAL; strcpy(ifsun->sun_path, UDS_NAME); //ifsun->sun_path[ifsun->sun_len - 1] = ch / 2; ch++; } int create_uds_server(void) { struct sockaddr_un ifsun; int sock; prepare_ifsun(&ifsun); unlink(ifsun.sun_path); sock = socket(PF_LOCAL, SOCK_DGRAM, 0); if (sock == -1) err(1, "server: can't create socket"); setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &sock, sizeof(sock)); if (bind(sock, sstosa(&ifsun), sizeof(ifsun)) < 0) err(1, "server: can't bind to a socket"); return sock; } void connect_uds_server(int sock) { struct sockaddr_un ifsun; prepare_ifsun(&ifsun); if (connect(sock, sstosa(&ifsun), sizeof(ifsun)) < 0) err(1, "client: can't connect to a socket"); } static void cleanup(void) { kill(pid_kill, SIGKILL); } int main() { int sock, len; pid_t pid; pid = fork(); if (pid < 0) err(1, "can't fork"); pid_kill = getpid(); if (pid != 0) { /* Parent */ pid_kill = pid; atexit(cleanup); sock = socket(PF_LOCAL, SOCK_DGRAM, 0); if (sock < 0) err(1, "client: can't create socket"); for (;;) { sleep(1); connect_uds_server(sock); len = write(sock, &pid, sizeof(pid)); if (len < 0) err(1, "client: can't write to a socket"); printf("client: wrote %d bytes to the socket\n", len); } } else { /* Child */ atexit(cleanup); for (;;) { sock = create_uds_server(); len = recvfrom(sock, &pid, sizeof(pid), 0, NULL, NULL); if (len < 0) err(1, "server: can't read from a socket"); printf("server: read %d bytes from the socket\n", len); close(sock); } } exit (1); } --------------040104060604090102080808--