Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

What's the easiest way to find an unused local port?

Currently I'm using something similar to this:

port=$RANDOM
quit=0

while [ "$quit" -ne 1 ]; do
  netstat -a | grep $port >> /dev/null
  if [ $? -gt 0 ]; then
    quit=1
  else
    port=`expr $port + 1`
  fi
done

It feels awfully roundabout, so I'm wondering if there's a more simple path such as a builtin that I've missed.

share|improve this question
2  
Why do you want to do that? It's inherently racy (and inefficient - and least add -n to netstat and a more selective grep). The way to do it is to try and open a port in whatever mode you need, and try another one if it's not available. –  Mat Nov 16 '12 at 16:04
    
@Mat I'm trying to automatically find an open port to use with ssh -D as a SOCKS server. –  mybuddymichael Nov 16 '12 at 16:08

4 Answers 4

up vote 4 down vote accepted

The script in your answer has a race condition, the only way to avoid it is to atomically check if it is open by trying to open it. If the port is in use, the program should quit with a failure to open the port.

For example, say you're trying to listen with netcat.

#!/bin/bash
read lowerPort upperPort < /proc/sys/net/ipv4/ip_local_port_range
while :; do
    for (( port = lowerPort ; port <= upperPort ; port++ )); do
        nc -l -p "$port" 2>/dev/null && break 2
    done
done
share|improve this answer

My solution is to bind to port 0, which asks the kernel to allocate a port from it's ip_local_port_range. Then, close the socket and use that port number in your configuration.

This works because the kernel doesn't seem to reuse port numbers until it absolutely has to. Subsequent binds to port 0 will allocate a different port number. Python code:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 0))
addr = s.getsockname()
print addr[1]
s.close()

This gives just a number of a port, eg. 60123.

Run this program 10 000 times, and you'll get 10 000 different port numbers. Therefore, I think it's pretty safe to use the ports.

share|improve this answer

On Linux, you could do something like:

ss -tln | 
  awk 'NR > 1{gsub(/.*:/,"",$4); print $4}' |
  sort -un |
  awk -v n=1080 '$0 < n {next}; $0 == n {n++; next}; {exit}; END {print n}'

To find the first free port above 1080. Note that ss -D would bind on the loopback interface, so in theory you could reuse port 1080 if a socket has it bound on another address. Another way would be to actually try and bind it:

perl -MSocket -le 'socket S, PF_INET, SOCK_STREAM,getprotobyname("tcp");
  $port = 1080;
  ++$port until bind S, sockaddr_in($port,inet_aton("127.1"));
  print $port'
share|improve this answer
    
This does, however, involve a race condition between attempting to open the port and actually using it. –  Chris Down Nov 17 '12 at 0:02
    
@ChrisDown, Indeed, but with ssh -D, I can't see any better option. The -O forward option of ssh doesn't return an error when the forward fail. –  Stéphane Chazelas Nov 17 '12 at 9:00

This is part of a function I have in my .bashrc, which dynamically creates SSH tunnels and tries to use any port in a range:

   lps=( 7002 7003 7004 7005 7006 7007 7008 7009 7010 7011 )
   lp=null

   # find a free listening port
   for port in ${lps[@]}; do
      lsof -i -n -P |grep LISTEN |grep -q ":${port}"
      [ $? -eq 1 ] && { lp=$port; break; }
   done
   [ "$lp" = "null" ] && { echo "no free local ports available"; return 2; }
   return $port

YMMV

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.