12

The OS is Linux. I have a server process that can change its port realtime. However I would like to know in advance if a port is free before binding.

Scenario: Server binds localhost:5000 and receives a request to bind at localhost:6000. The server has to check if the port is free. This question seeks for answers that provide a routine that checks if a port is free or not.

For the record, I am editing my question with a code snippet that checks if a port is free to use. This does not mean that it will be used. The code below answer to the question "if the port is available right now", it does not use it. Opens a socket, check if bind returns EADDRINUSE and closes the socket.

#include <iostream>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>

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

    struct sockaddr_in serv_addr;
    if( argc < 2 )
        return 0;
    int port = atoi(argv[1]);

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if( sockfd < 0 ) {
        printf("socket error\n");
        return 0;
    } else {
        printf("Opened fd %d\n", sockfd);
    }

     bzero((char *) &serv_addr, sizeof(serv_addr));
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(port);
     if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {

        if( errno == EADDRINUSE )
        {
            printf("the port is not available. already to other process\n");
        } else {
            printf("could not bind to process (%d) %s\n", errno, strerror(errno));
        }
    }

    if (close (sockfd) < 0 ) {
        printf("did not close fd: %s\n", strerror(errno));
        return errno;
    }

    return 0;


}

Here are some sample runs (partial outputs)

[bash{1051}{51}]:[~/some_sources/checkbind]::./a.out 41067
the port is not available. already to other process
[bash{1052}{52}]:[~/some_sources/checkbind]::./a.out 22
could not bind to process (13) Permission denied
[bash{1053}{53}]:[~/some_sources/checkbind]::./a.out 22000
Opened fd 3
cateof
  • 6,608
  • 25
  • 79
  • 153

3 Answers3

12

This is an obvious race condition, since other processes on your system might be binding to ports in parallel. So, any solution you find will be imperfect, and you will still need to just write it according to the "try to bind(), if it fails pick a new port number and try again" approach.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • I agree that a race condition might occur, but in my system this scenario is not possible. Also the "pick the next port" approach is not acceptable. – cateof Apr 24 '12 at 09:14
  • @cateof: What is not possible? Is the race condition not possible, or is it not possible to try binding and then see if it fails? – Dietrich Epp Apr 24 '12 at 09:19
  • The race condition is possible if you have at least two processes competing for a resource. In my system only one process tries to bind. – cateof Apr 24 '12 at 09:24
  • 3
    @cateof If only only process tries to bind, doesn't that mean that the other port must be free? – RedX Apr 24 '12 at 09:32
  • @RedX I was referring to processes that are developed by me. sshd binds to a well known port that rarely changes. However if my process tries to bind 22, my process needs to return an error. The use case "sshd might change at the same time with my process" is not applicable. – cateof Apr 24 '12 at 10:07
  • 3
    @cateof Maybe both processes start at the same time, think of `sshd` being restarted by the time your program starts. Always assume a race-condition applies if you cannot control ALL parameters involved. So just try to bind and see if it succeeds. That is by far the best and safest way. – RedX Apr 24 '12 at 10:12
  • Also, if your process is the only one doing this, chances are picking a random number and bind()ing to it will succeed immediately (or with one or two retries at most). So it's not really worth burning too many cycles over. – bdonlan Apr 25 '12 at 07:22
8

I struggled with this myself and have slightly modified your code.

The solution is to set serv_addr.sin_port = 0 (auto assign port).

Note In the bind() and the getsockname() lines there are unclean casts from sockaddr_in to sockaddr. I have seen it done many places and I'm looking for a safer solution.

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

// ... snip ...

int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0) {
    printf("socket error\n");
    return;
}
printf("Opened %d\n", sock);

struct sockaddr_in serv_addr;
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = 0;
if (bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
    if(errno == EADDRINUSE) {
        printf("the port is not available. already to other process\n");
        return;
    } else {
        printf("could not bind to process (%d) %s\n", errno, strerror(errno));
        return;
    }
}

socklen_t len = sizeof(serv_addr);
if (getsockname(sock, (struct sockaddr *)&serv_addr, &len) == -1) {
    perror("getsockname");
    return;
}

printf("port number %d\n", ntohs(serv_addr.sin_port));


if (close (sock) < 0 ) {
    printf("did not close: %s\n", strerror(errno));
    return;
}

Program output

Opened 4
port number 59081
neoneye
  • 50,398
  • 25
  • 166
  • 151
  • 1
    "In the `getsockname(...)` line there is a dangerous cast from `sockaddr_in` to `sockaddr`." In what way do you think it is dangerous? Maybe you want to use `sockaddr_storage` in order to be really safe. – glglgl Dec 31 '13 at 01:52
  • They have the same size 16 bytes. The `sin_len` and `sin_family` are located the same place in both structs. I just don't like reinterpret_cast casts. – neoneye Dec 31 '13 at 02:09
7

If your server was told what port to use, just bind() it. Seriously.

Sure, you could parse /proc/net/tcp and see if the port's in use. But then what? You still need to call bind() now that you know your port is free, and it'll tell you if the port was free then anyway, and so there was no point in groveling through /proc/net/tcp and doing all that (slow!) string production and parsing and extra kernel trips through not-very-well-optimized (read: super slow compared to bind()) diagnostic paths, just to get information that could well be out of date before you even finished parsing it. So just call bind() and be happy.

bdonlan
  • 224,562
  • 31
  • 268
  • 324