C sockets - Blocked on select

2.1k views Asked by At

I am working on a client server program , that uses select() calls to listen to multiple sockets. But my select call gets blocked, although I have a message in one of those sockets , select() call doesn't recognize it and it's still waits there indefinetly.

There are 2 entities in the program , a master and a client. The master knows the number of clients it will handle and waits for the clients to connect to it. Once it receives a client acknowledgement, it stores its information. Once all the clients are connected, it then sends its neighboring client's information to every client so it can form a network. It is here, I use the select() to monitor many sockets, master has a socket to every child tat is connected to it client has 3 main sockets s1-to speak with master s2-child listens for connection on this socket neighbour-the socket on which its neighbour wait for a connection.(i.e S2 in a neighbour) p- the socket that is results of connection from its neighbour ( accept of s2 - returns ths) I use select to listen to server, its own socket for incoming connections and once.

Initially my server sends a string "hello" to one of the client, which receives this message and passes it on to the neighbour, in this way when the string reaches back to the first child that has received this message from server , it passes it on to its neighbour. But all though all child are in select() waiting for an input. What could cause this ??

void main(int argc, char **argv) {

    int s1, s2, n, server_port, sc1, sc2, rv, rc, left_peer_port;
    int peer_port;
    fd_set writefds, readfds;
    struct timeval tv;
    struct hostent *server_info, *child_info, *left_peer_info;
    int start_flag = 0;

    struct sockaddr_in server, peer, incoming;

    char host_child[64];
    char *left_host = malloc(1);
    char *right_host = malloc(1);
    char buf1[256];
    char buf2[256];

    server_port = atoi(argv[2]);

    //speak to peer using this
    s2 = socket(AF_INET, SOCK_STREAM, 0);
    if (s2 < 0) {
        perror("socket:");
        exit(s2);
    }

    peer_port = server_port + 1;

    gethostname(host_child, sizeof host_child);

    child_info = gethostbyname(host_child);

    if (child_info == NULL) {
        fprintf(stderr, "%s: host not found (%s)\n", argv[0], host_child);
        exit(1);
    }


    peer.sin_family = AF_INET;
    memcpy(&peer.sin_addr, child_info->h_addr_list[0], child_info->h_length);
    int changeport = 0;
    do {
        peer.sin_port = htons(peer_port);
        rc = bind(s2, (struct sockaddr *) &peer, sizeof(peer));

        if (rc < 0) {
            //perror("bind:");
            peer_port++;
            changeport = 1;
            //exit(rc);

        } else {
            changeport = 0;
        }

    } while (changeport == 1);

    if (listen(s2, 100) == -1) {
        perror("listen");
        exit(3);
    }



//Now talk to server

    server_info = gethostbyname(argv[1]);

    if (server_info == NULL) {
        fprintf(stderr, "%s: host not found\n", argv[0]);
        exit(1);
    }


// pretend we've connected both to a server at this point
//speak to server using this
    s1 = socket(AF_INET, SOCK_STREAM, 0);
    if (s1 < 0) {
        perror("socket:");
        exit(s1);
    }


    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    memcpy(&server.sin_addr, server_info->h_addr_list[0], server_info->h_length);

//To talk to the server

    sc1 = connect(s1, (struct sockaddr *) &server, sizeof(server));
    if (sc1 < 0) {
        perror("connect:");
        exit(sc1);
    }

    int send_len;
    char *str = malloc(1);
    sprintf(str, "%d", peer_port);
    printf("\nport-here=%s\n", str);


    send_len = send(s1, str, strlen(str), 0);
    if (send_len != strlen(str)) {
        perror("send");
        exit(1);
    }

    int recv_len;
    char buf[100];
    int ref = 0;
    int recv_stage = 0;
    int start_id;


    recv_len = recv(s1, buf, 34, 0);
    if (recv_len < 0) {
        perror("recv");
        exit(1);
    }
    buf[recv_len] = '\0';
    char *temp_port;

    if (!strcmp("close", buf))
        printf("%s", buf);
            //break;
    else {
        char *temp_buffer = malloc(1);
        char *id = malloc(100);
        char *pp = malloc(1);
        strcpy(temp_buffer, buf);

        char *search = ":";
        temp_port = strtok(temp_buffer, search);
        strcpy(buf, temp_port);
        printf("temp_name%s", temp_port);

        temp_port = strtok(NULL, search);
        strcpy(pp, temp_port);
        printf("temp_port%s", temp_port);
        temp_port = strtok(NULL, search);
        strcpy(id, temp_port);
        printf("id%s", temp_port);

        strcpy(temp_port, pp);
        printf("\nbuf=%s\n", buf);
        printf("\nport=%s\n", temp_port);
        printf("\nid=%s\n", id);
        start_id = atoi(id);
    }

//To send packet to its neighbour
    left_peer_info = gethostbyname(buf);
    printf("\nleft host=%s\n", buf);
    if (left_peer_info == NULL) {
        fprintf(stderr, "%s: host not found\n", left_host);
        exit(1);
    }
    left_peer_port = atoi(temp_port);


    int neighbour_socket;
    struct hostent *neighbour_info;
    struct sockaddr_in neighbour;
    neighbour_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (neighbour_socket < 0) {
        perror("socket:");
        exit(neighbour_socket);
    }

    neighbour_info = left_peer_info;


    neighbour.sin_family = AF_INET;
    neighbour.sin_port = htons(left_peer_port);
    memcpy(&neighbour.sin_addr, neighbour_info->h_addr_list[0], neighbour_info->h_length);

    printf("\nconnected to port %d\n", left_peer_port);
    //To talk to the neighbour
    printf("\ncomes here\n");


//Listen on this socket connection for potato

    int send_peer_len;
    int nfds;

    nfds = MAX(MAX(neighbour_socket, s2), s1);

// clear the set ahead of time
    FD_ZERO(&writefds);

// add our descriptors to the set
    FD_SET(neighbour_socket, &writefds);
    FD_SET(s1, &writefds);
    FD_SET(s2, &writefds);

//FD_SET(s2, &writefds);


    FD_ZERO(&readfds);
    FD_SET(neighbour_socket, &readfds);
    FD_SET(s1, &readfds);
    FD_SET(s2, &readfds);

//select()

// since we got s2 second, it's the "greater", so we use that for
// the n param in select()
//n = s1 + 1;

// wait until either socket has data ready to be recv()d (timeout 10.5 secs)
    tv.tv_sec = 10;
    tv.tv_usec = 500000;

    int fds[3];
    fds[0] = s1;
    fds[1] = s2;
    fds[2] = neighbour_socket;
    int p = 0;
    int p_flag = 0;

    while (1) {
        printf("\n nfds = %d , p = %d \n", nfds, p);
        char buf_msg[64];


        //This is where the error occurs  //

        rv = select(nfds, &readfds, NULL, NULL, 0);

        //This is where the error occurs  //

        if (rv == -1) {
            perror("select"); // error occurred in select()
        } else if (rv == 0) {
            printf("Timeout occurred!  No data after 10.5 seconds.\n");
        } else {
            // one or both of the descriptors have data
            //reading message from server
            int select_fd;
            for (select_fd = 0; select_fd <= nfds; select_fd++) {

                if (FD_ISSET(select_fd, &readfds) != 0) {
                    if (select_fd == s1) {

                        recv_len = 0;
                        recv_len = recv(s1, buf_msg, 34, 0);
                        if (recv_len < 0) {
                            perror("recv");
                            exit(1);
                        }
                        buf_msg[recv_len] = '\0';
                        printf("\nreceived from server = %s\n", buf_msg);
                        //send to neighbour

                        int sc3;
                        sc3 = connect(neighbour_socket, (struct sockaddr *) &neighbour, sizeof(neighbour));
                        if (sc3 < 0) {
                            perror("connect:");
                            exit(sc3);

                        }

                        str = malloc(1);
                        strcpy(str, buf_msg);
                        send_len = send(neighbour_socket, str, strlen(str), 0);
                        printf("\n send - len - s1  - %d\n", send_len);
                        if (send_len != strlen(str)) {
                            perror("send");
                            exit(1);
                        }
                        start_flag = 1;
                        //FD_CLR(s1, &readfds);

                        printf("\ncrossed server\n");

                    } else if (select_fd == s2) {

                        int list_len = sizeof incoming;
                        printf("\ninside client\n");
                        printf("\nWaiting for accept in S2\n");
                        if (p_flag == 0) {
                            p_flag = 1;
                            p = accept(s2, (struct sockaddr *) &incoming, &list_len);
                            printf("\nConnection accepted in S2\n");
                            if (p < 0) {
                                perror("bind:");
                                exit(rc);
                            }
                        }
                        nfds = MAX(nfds, p);
                        recv_len = 0;
                        buf_msg[recv_len] = '\0';
                        recv_len = recv(p, buf_msg, 34, 0);
                        if (recv_len < 0) {
                            perror("recv");
                            exit(1);
                        }
                        buf_msg[recv_len] = '\0';
                        printf("\nreceived from client = %s\n", buf_msg);
                        //send to neighbour

                        //if(start_id!=1){
                        int sc3;
                        sc3 = connect(neighbour_socket, (struct sockaddr *) &neighbour, sizeof(neighbour));
                        if (sc3 < 0) {
                            perror("connect:");
                            //exit(sc3);
                        }
                        //}

                        str = malloc(1);
                        strcpy(str, buf_msg);
                        send_len = send(neighbour_socket, str, strlen(str), 0);
                        printf("\n send - len - s2  - %d\n", send_len);
                        if (send_len != strlen(str)) {
                            perror("send");
                            exit(1);
                        }

                    } else if (select_fd == neighbour_socket) {

                        printf("\ncomes in\n");

                    } else if (select_fd == p && p != 0) {

                        int list_len = sizeof incoming;
                        printf("\ninside p\n");
                        recv_len = 0;
                        buf_msg[recv_len] = '\0';
                        printf("\nwaiting at recv in P\n");

                        recv_len = recv(p, buf_msg, 34, 0);

                        printf("\ncrossed at recv in P\n");
                        if (recv_len < 0) {
                            perror("recv");
                            exit(1);
                        }
                        buf_msg[recv_len] = '\0';
                        printf("\nreceived from client = %s\n", buf_msg);
                        //send to neighbour
                        str = malloc(1);
                        strcpy(str, buf_msg);
                        send_len = send(neighbour_socket, str, strlen(str), 0);
                        printf("\n send - len - neighbour - %d\n", send_len);
                        if (send_len != strlen(str)) {
                            perror("send");
                            exit(1);
                        }
                    }
                }
            }

            FD_ZERO(&readfds);
            //FD_SET(neighbour_socket,&readfds);
            FD_SET(s1, &readfds);
            FD_SET(neighbour_socket, &readfds);

            if (p_flag == 1) {
                printf("\nsetting P\n");
                FD_SET(p, &readfds);
                FD_SET(s2, &readfds);

                p_flag = 0;
            } else {
                printf("\nNot setting P\n");
                FD_SET(s2, &readfds);
            }
        }
    }
    close(s1);
    close(s2);
} 

Thanks in advance.

3

There are 3 answers

1
moshbear On

Have you considered using poll() instead of select()? It's easier to debug and scales elegantly to however many you need.

4
Mat On

The first parameter to select must be the maximum file descriptor plus one. As far as I can tell in that huge lump of code you posted, you forgot that "plus one".

1
sarnold On

I believe Mat has found the underlying problem. But I think there is a much larger problem here:

int send_len;
char *str=malloc(1);
sprintf(str,"%d",peer_port);
printf("\nport-here=%s\n",str);

You have corrupted your heap with your sprintf(3) call. Maybe it isn't important data you've overwritten, and maybe malloc(3) won't ever actually allocate one byte, but that is a bad assumption to make. You need to allocate at least six bytes for a port number: five for the digits in 65535 and one for the trailing ASCII NUL \0 byte.

  buf[recv_len] = '\0';
  char *temp_port;
  //printf("\n-%s\n",buf);
  if ( !strcmp("close", buf) )
        printf("%s",buf);
    //break;
  else{
    char *temp_buffer=malloc(1);
    char *id=malloc(100);
    char *pp=malloc(1);
    strcpy(temp_buffer,buf);

In the preceding selection, you have stored a \0 into the end of buf, so you're presumably working with a string of some sort. But in a few lines, you allocate a single byte and then proceed to copy the contents of buf into that single byte. The ASCII NUL will use that byte entirely, leaving no space for the string you received. But strcpy(3) doesn't work that way -- it will copy the contents of buf, until that '\0' character, into the memory starting with your single byte. You've again destroyed your heap. But this time it can overwrite significantly more than five bytes -- and all under the control of the remote peer.

Heap overflows are extremely dangerous. I found over 350 references to exploitable bugs in programs that derive directly from heap overflows in an old archive I have from the Common Vulnerabilities and Exposures project.

Do not deploy this program on publicly-accessible machines until you have fixed these problems. It represents a significant security flaw. Find every instance of malloc(1) and replace it with the correct amount of memory that must be allocated. Once you've done this, please run your program with MALLOC_CHECK_=1 or under control of valgrind(1) to help you find further memory allocation problems.