I posted this on freebsd-questions also but maybe the expert density isn't that high as here in "hackers". Since I think it may be a design or implementation issue in FreeBSDs' select(), I'm posting it here as well,
hoping to get an experts' answer.

I have written a small server to test TCP/IP roundtrip times of the packets in a proprietary protocol and while compiling and running this server on different platforms (Windows 7/cygwin, UbuntuLinux, FreeBSD 8.0 Release), I found that the server produces an error when the listening socket (on which the accept() is performed) is also
member of the select() fd_set.

On the other platforms the program works without error, just under FreeBSD I'm getting this "invalid argument" error.

Comments appreciated (despite comments about the error checking logic

Here is the code:
/* testsrv.c                   */
/*  gcc -o testsrv testsrv.c   */
/*                             */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>

#include <sys/select.h>

#define USEDBUFSIZ 60
#define MAX_HOSTNAME 256
#define MAXFDS 256
#define CLRBUF  memset(buf,0,sizeof(buf))
#define max(a,b)        (((a) > (b)) ? (a) : (b))
static unsigned char buf[256];
int             array_of_fds[MAXFDS];
static fd_set   clientfds;
#define SOCKET int
void           *memset(void *, int, size_t);
int             enter      (int);
int             remov      (int);
int             invalidip  (char *);
void            exit      (int);
int             getv       (int, unsigned char *, int);
int             getfds     ();

int
main(int argc, char **argv)
{
    int             nfds;
    static fd_set   readfds;
    SOCKET          ListenSocket, newsockfd;
    struct sockaddr_in cli_addr;
    struct sockaddr_in service;
    struct hostent *thisHost;
    int             bOptVal = 0;
    int             bOptLen = sizeof(int);
    char            hostname[256];
    char           *host_addr;
    struct in_addr  addr = {0};
    char           *ip;
    u_short         port;
    int             iResult = 0;
    int             i     , n, m, clilen, dummy, connect = 0;
    struct timeval  tv;
    /*---------------------------------------*/
    /* Create a listening socket             */
        ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ListenSocket == -1) {
        perror("socket creation");
        return 1;
    } else
        printf("ListenSocket=%d\n", ListenSocket);
    /*-----------------------------------------*/
    /* Bind the socket to the local IP address */
    /* and port 3210                           */
        port = 3210;
    if (gethostname(hostname, 256))
        perror("gethostname failed\n"), exit(3);
    printf("%s\n", hostname), fflush(stdout);
    thisHost = gethostbyname(hostname);
    ip = inet_ntoa(*(struct in_addr *)(*thisHost->h_addr_list));


    if (argc == 2) {
        host_addr = argv[1];
        service.sin_addr.s_addr = inet_addr(host_addr);
        thisHost = gethostbyaddr((const char *)&service.sin_addr.s_addr,
sizeof(service.sin_addr.s_addr), AF_INET);
        if (thisHost == 0)
            printf("host unknown\n"), exit(3);
        if (invalidip(host_addr))
            printf("invalid IP\n"), exit(4);
    } else {
        service.sin_addr.s_addr = inet_addr(ip);
    }
    service.sin_port = htons(port);
    service.sin_family = AF_INET;

iResult = bind(ListenSocket, (struct sockaddr *)&service, sizeof(service));
    if (iResult == -1) {
        perror("bind");
        shutdown(ListenSocket, SHUT_RDWR);
        return 1;
    }
    listen(ListenSocket, SOMAXCONN);
    printf("SOMAXCONN=%d %d\n", SOMAXCONN, FD_SETSIZE);
    /* all sockets are put into an own array_of_fs */
/* in the while() loop below the FD_SET id used by looping through the */
    /* array_of_fds to fill the readfds array in the select() */

    enter(ListenSocket);

    /*
     * Wait for connect
     */
    tv.tv_sec = 0;
    tv.tv_usec = 5000000;       /* 5 seconds */

    printf("Server %s listening on port %d\n", thisHost->h_name, port);
memset((void *)array_of_fds, 0, (size_t) MAXFDS * sizeof(*array_of_fds));
    nfds = ListenSocket + 1;
    while (1) {
        FD_ZERO(&readfds);
        FD_SET(ListenSocket, &readfds);
        for (i = 0; i < MAXFDS; i++) {
            if (array_of_fds[i]) {
                nfds = max(nfds, array_of_fds[i]) + 1;
                FD_SET(array_of_fds[i], &readfds);
            }
        }
        n = select(nfds, &readfds,
                   (fd_set *) NULL,     /* not interested in write */
                   (fd_set *) NULL,     /* ...or exceptions */
&tv);        /* timeout */

        switch (n) {

        case 1:
            clilen = sizeof(cli_addr);
            /* first test if a new client has connected */
            if (FD_ISSET(ListenSocket, &readfds)) {
                newsockfd = accept(ListenSocket,
                            (struct sockaddr *)&cli_addr, &clilen);
if (enter(newsockfd)) /* socket of new connection is entered*/
                    printf("\n%d.connect! ", ++connect), fflush(stdout);
                else
                    printf("too many connections"), exit(1);
                sprintf(buf, "ENTERED %d of %d", 1, 10);
send(newsockfd, buf, USEDBUFSIZ, 0); /* send to the client*/
            } else {

                for (i = 0; i < MAXFDS; i++) {
                    int             s;
                    if (FD_ISSET((s = array_of_fds[i]), &readfds)) {

                        m = getv(s, buf, USEDBUFSIZ);
                        if (m <= 0) {
                            shutdown(s, SHUT_RDWR);
                            if (remov(s) == -1)
printf("error on remove(s)\n", s), fflush(stdout
);
                            goto done;
                        } else {
                            printf("*");
                            fflush(stdout);

                            CLRBUF;
                            /* now send some data to the client */
sprintf(buf, "XX-123456789-XX-xx : 0x0600, 0x99999999, \"NNNNN\"");
                            m = send(s, buf, USEDBUFSIZ, 0);
                            if (m < 0 || m != USEDBUFSIZ) {
                                perror("cannot send\n");
                            }
                            CLRBUF;
                            sprintf(buf, "END");
                            m = send(s, buf, USEDBUFSIZ, 0);
                            if (m < 0 || m != USEDBUFSIZ) {
                                perror("cannot send\n");
                            }
                        }       /* else */
                    }           /* if FD_ISSET */
                }               /* for */
                break;

        case 0:
                break;

        case -1:
                perror("select");
                exit(2);

        default:
                printf("more than one descriptor ready!\n");
                fflush(stdout);
                exit(4);
                break;
            }                   /* switch */
        }                       /* while */

    }

done:
    shutdown(ListenSocket, SHUT_RDWR);
    return 0;

}                               /* main */
int
getv(int fd, unsigned char *buf, int count)
{
    int             m     , got = 0;
    int             i = 0;
    while (got < count) {
        m = recv(fd, buf + got, count - got, 0);
        if (m == count)
            return m;
        if (m > 0)
            got = got + m;
        if (m == 0)
            return (0);
        if (m < 0) {
            return -1;
        }
    }
    return -1;
}
int
enter(int i)
{
    int             k = 0;
    while (array_of_fds[k++]);

    if (k > MAXFDS - 1)
        return -1;
    k--;
    array_of_fds[k] = i;
    return i;
}

int
remov(s)
{
    int             k;
    for (k = 0; k < MAXFDS; k++)
        if (array_of_fds[k] == s) {
            array_of_fds[k] = 0;
            return k;
        }
    return -1;
}

/*
* this code taken from code for validating IPv4 address taken from article
 * in http://bytes.com/topic/c/answers/212174-code-validating-ipv4-address
 */
#define INVALID -1
#define VALID 0


int
invalidip(char *ipadd)
{
    unsigned        b1, b2, b3, b4;
    unsigned char   c;
    if (sscanf(ipadd, "%3u.%3u.%3u.%3u%c", &b1, &b2, &b3, &b4, &c) != 4)
        return INVALID;

    if ((b1 | b2 | b3 | b4) > 255)
        return INVALID;
    if (strspn(ipadd, "0123456789.") < strlen(ipadd))
        return INVALID;

    return VALID;
}


_______________________________________________
freebsd-questi...@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-questions
To unsubscribe, send any mail to "freebsd-questions-unsubscr...@freebsd.org"
_______________________________________________
freebsd-hackers@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-hackers
To unsubscribe, send any mail to "freebsd-hackers-unsubscr...@freebsd.org"

Reply via email to