Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 10 Jan 2005 14:09:26 +0200
From:      Maxim Sobolev <sobomax@portaone.com>
To:        hackers@freebsd.org, current@freebsd.org
Subject:   Attempt to invoke connect(2) on already connected unix domaindatagram socket fails with ECONNRESET
Message-ID:  <41E27076.8080904@portaone.com>

next in thread | raw e-mail | index | archive | help
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 <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>

#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--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?41E27076.8080904>