Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 18 May 2010 01:08:50 +0900
From:      Yoshihiko Sarumaru <ysarumaru@gmail.com>
To:        freebsd-net@FreeBSD.org, freebsd-stable@FreeBSD.org
Subject:   odd behavior on select() after shutdown()
Message-ID:  <AANLkTilQexT6ZobWYe2MuPcsD5RnTsx3SL0Zru_01Q9C@mail.gmail.com>

next in thread | raw e-mail | index | archive | help
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



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