Skip to content
Advertisement

Bad File Descriptor on recv from TCP socket

I’ve got a trouble and can’t google out solution, so I hope you’ll be able to help me.

There is a client-server application, something like text chat.
Server accepts client’s connection, makes new socket for client and sends socket descriptor to it’s child process, that broadcasts received messages to all connected clients.

server.c (error handling and tests are cuted out)

/* structure with fd, sockaddr_in and sockaddrlen */
Socket_in listener;
memset(&listener, 0, sizeof(Socket_in));

listener.saddr.sin_addr.s_addr = htonl(INADDR_ANY);
listener.saddr.sin_family = AF_INET;
listener.saddr.sin_port = htons(conf.port);
listener.slen = sizeof(listener.saddr);

listener.fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)

/* Bind socket to port */
bind(listener.fd, (struct sockaddr*)&listener.saddr, listener.slen);

/* socktrans is UNIX-socket for IPC between main process and child */
int pid = fork_receiver(socktrans);

while(1){
    struct sockaddr_in newcliaddr;
    uint nclilen = sizeof(newcliaddr);
    int newsockfd = accept(listener.fd, (struct sockaddr*)&newcliaddr, &nclilen);

    flags = fcntl(newsockfd, F_GETFL, 0);
    fcntl(newsockfd, F_SETFL, flags | O_NONBLOCK)

   /* Sending connected client socket fd to child process */
    sendto(socktrans.fd, &newsockfd, sizeof(newsockfd), 0, (struct 
}

Next, receiver gets fd of new socket and saves it to an array. Receiver’s code looks like that:

int pid = fork();
if(pid < 0)        // fork() failed
    return -1;
if(pid > 0)         // it's parent process -> return child's pid
    return pid;

/* Now we are in child process */
struct fds_s fds;
fds.fds = NULL;
fds.count = 0;

int n, i;
message msg;
int     newclientfd;

while(1){
    memset(&msg, 0, sizeof(msg));
    n = lc_recv_non_block(socktrans.fd, &newclientfd, sizeof(newclientfd), 0);
    if(n > 0){
        add_client(&fds, newclientfd);

    for(i = 0; i < fds.count; i++){
        int fd = fds.fds[i];
        n = lc_recv_non_block(fd, &msg, sizeof(msg), 0);
        if(n > 0){
            broadcast(&bay, &msg, sizeof(msg));
        }
    }
}

exit(0);

Here functions lc_recv_non_block and lc_send_non_block are just normal recv and send with additional error handling for EAGAIN and EWOULDBLOCK (using select() to avoid problems).

For some reason, at this line recv() fails with errno EBADF.

n = lc_recv_non_block(fd, &msg, sizeof(msg), 0);

What did I missed and why file descriptor is broken?

Advertisement

Answer

Look at the sendmsg() API for Unix domain sockets, especially the SCM_RIGHTS message type. Basically you build a message using the SCM_RIGHTS message type and an array of file descriptors. Under the covers the receiving side effectively dup()s the file descriptors enabling access to those files.

This is very Linux-specific.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement