odd behavior on select() after shutdown()

Yoshihiko Sarumaru ysarumaru at gmail.com
Mon May 17 16:35:01 UTC 2010


Hi all,

Select(2) has three arguments to get socket status for read, write and except.
After upgrading to 8.0-RELEASE, select() after shutdown(SHUT_WR) returns with
the status exceptfds is set. It means out-of-bound data can be read
from the socket,
but recv() with OOB flag returns ECONNRESET, and no packets with urgent flag
was observed by tcpdump.
It seems strange for me, but is it an intentional change on 8.x ?

This behavior breaks net/stone on 8.0-RELEASE.
http://www.freebsd.org/cgi/query-pr.cgi?pr=ports/141103
The continuous recv() error on PR might lead by incorrectly setted
exceptfds on every recv()
and it should be fixed, but it doesn't matter if above behavior of
select() doesn't occur.

You can reproduce this by following example:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>

int main() {
        int ret;
        int s;
        s = socket(PF_INET, SOCK_STREAM, 0);

        struct sockaddr_in sa;
        sa.sin_family = AF_INET;
        sa.sin_addr.s_addr = inet_addr("127.0.0.1");
        sa.sin_port = htons(22);
        ret = connect(s, (struct sockaddr*)&sa, sizeof(sa));
        if (ret) perror("connect");

        /* get OpenSSH greetings */
        char buf[BUFSIZ];
        memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
        ret = recv(s, buf, sizeof(buf), 0);
        if (ret < 0) perror("recv");
        printf("recv: %s\n", buf);

        /* send something incorrect */
        printf("send: \\r\\n\n");
        ret = send(s, "\r\n", 2, 0);
        if (ret < 0) perror("send");

        /* receive "Protocol mismatch" */
        memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
        ret = recv(s, buf, sizeof(buf), 0);
        if (ret < 0) perror("recv");
        printf("recv: %s\n", buf);

        /* shutdown */
        ret = shutdown(s, SHUT_WR); /* SHUT_RD doesn't make problem. */
        if (ret) perror("shutdown");

        /* select */
        fd_set readfds, exceptfds;
        FD_ZERO(&readfds);
        FD_ZERO(&exceptfds);
        FD_SET(s, &readfds);
        FD_SET(s, &exceptfds);
        ret = select(s+1, &readfds, NULL, &exceptfds, NULL);
        if (ret < 1) perror("select");
        printf("select: read:%d except:%d\n",
                FD_ISSET(s, &readfds), FD_ISSET(s, &exceptfds));

        if (FD_ISSET(s, &exceptfds)) {
                printf("read OOB data\n");
                memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
                ret = recv(s, buf, sizeof(buf), MSG_OOB);
                if (ret < 0) perror("recv");
                printf("recv(OOB): %s\n", buf);
        }

        FD_ZERO(&readfds);
        FD_SET(s, &readfds);
        ret = select(s+1, &readfds, NULL, NULL, NULL);
        if (ret < 1) perror("select");
        printf("select: read:%d\n", FD_ISSET(s, &readfds));

        memset(buf, 0, sizeof(buf) / sizeof(buf[0]));
        ret = recv(s, buf, sizeof(buf), 0);
        if (ret < 0) perror("recv");
        printf("recv: %s\n", buf);

        close(s);
}

Thanks in advance
- Yoshihiko


More information about the freebsd-net mailing list