2OF1 · the depletionmode zineLinkedIn · X
← back to the issue

08 June 2010

Good socket()s

Originally published on the old depletionmode / 2of1 blog (archived copy).

Two things I think you should know about linux sockets:

bind()s and SO_REUSEADDR

Firstly, the linux kernel will automatically lock up a port on bind() – even if the process is killed.
Well actually the kernel will close the socket when the process ends, however it stays in the 2MSL (2 x Max. Segment Lifetime) state for a few (2) minutes.

That means that the next time you try to bind() the port, you could get a message saying that it is already in use.

This is VERY annoying – especially if you’re trying to debug a TCP server for example.

The solution for this is to have your program instruct the kernel to allow a new socket to be bound to the same port. You can do this by setting the SO_REUSEADDR sockopt as follows:

listing text
int opt = 1;
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt))

O_NONBLOCKing socket()s

The second thing that you should be aware of is that if you try to connect() to a server, there is no direct way to define a timeout for that connection attempt – meaning that you could be waiting for a LONG time.

There are two main solutions:

You could use alarm() and set up a relevant signal handler – but that is only really good for non-threaded applications.

The other way is to set the O_NONBLOCK flag on the socket file device. This will cause the connect() to always return a value pretty much immediately – usually EINPROGRESS. You can then use either select() or poll() (which is the much better option) to define a timeout for the connection.

Take a look at the following example:

listing text
#define POLLRDNORM 0x0040
#define POLLWRNORM 0x0100
#define TIMEOUT_SEC 5
 
int _nonblock_connect(struct sockaddr *addr, int addr_size) {
    int socket_fd, flags, err;
 
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    flags = fcntl(socket_fd, F_GETFL, NULL);
    fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK);
 
    err = connect(socket_fd, addr, addr_size);
 
    if ((errno == EINPROGRESS) || (err == 0)) {
        struct pollfd pfd;
 
        pfd.fd = socket_fd;
        pfd.events = POLLWRNORM | POLLRDNORM;
 
        if (poll(&pfd, 1, TIMEOUT_SEC * 1000) == 0) {
            fcntl(socket_fd, F_SETFL, flags);  // restore blocking mode
            return socket_fd;  // connection successful
        } else
            err = -1;  // connection timed out after TIMEOUT_SEC
    }
 
    close(socket_fd);
    return err;
}

→ end of entry

2/1
@depletionmode
security research & systems engineering