Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get An Unused TCP/IP (or UDP) Port #500

Closed
xcthulhu opened this issue Nov 25, 2018 · 3 comments
Closed

Get An Unused TCP/IP (or UDP) Port #500

xcthulhu opened this issue Nov 25, 2018 · 3 comments

Comments

@xcthulhu
Copy link

This is a duplicate of #123 , but I was hoping for a slightly different solution than the one provided there.

I was hoping to get a free port so I can make a temporary postgres database listen on it while I am running database tests.

I found a nice answer on this blog: https://elliotekj.com/2017/07/25/find-available-tcp-port-rust/

Here is how I look for available ports:

fn port_is_available(port: u16) -> bool {
    match TcpListener::bind(("127.0.0.1", port)) {
        Ok(_) => true,
        Err(_) => false,
    }
}

fn get_available_port() -> Option<u16> {
    (1025..65535).find(|port| port_is_available(*port))
}

I have made one slight change; get_available_port checks all possible user-assignable ports.

@MightyPork
Copy link

This works, but it can fail due to TOCTOU - someone else can snatch the port before you open the real socket. A solution would be to return the socket you used for probing, e.g. in Option, or to use retries.

@dzervas
Copy link

dzervas commented Jan 25, 2020

This works only due to SO_REUSEPORT that was introduced in Linux 3.9 (search here). This flag allows you to bind to a port and then rebind to it without closing the file descriptor (from what I understand). The regular behavior after you bind to a port (either if you use it or not) is for it to become unavailable for a minute or two (until the kernel does some kind of cleanup). And yes, rust, uses SO_REUSEPORT by default when calling libc's bind - check with strace.

For example this does not work on OS X and I have no idea if this will work on Windows. A better approach that works on all OSes (solving the TOCTOU problem as well) is the following (not tested though!!):

fn listen_available_port() -> Option<TcpListener> {
    for port in (1025..65535) {
         match TcpListener::bind(("127.0.0.1", port)) {
             Ok(l) => return Some(l),
             _ => {}
         }
    }

    None
}

Still this approach does not take into account other TcpListener errors. The loop should continue only for port unavailable error and return a Result (not an Option) indicating the exact error or all ports unavailable error.

@ghost
Copy link

ghost commented Sep 2, 2023

Binding with a port number of 0 will request that the OS assigns a port to this listener. The port allocated can be queried via the TcpListener::local_addr method.

docs
https://doc.rust-lang.org/std/net/struct.TcpListener.html#method.bind

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants