Qual é a maneira mais fácil de encontrar uma porta local não usada?

46

Qual é a maneira mais fácil de encontrar uma porta local não usada?

Atualmente estou usando algo parecido com isso:

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

Parece terrivelmente indireto, então estou pensando se há um caminho mais simples, como um que eu perdi.

    
por mybuddymichael 16.11.2012 / 16:58

13 respostas

22

Se o seu aplicativo oferecer suporte, você pode tentar passar a porta 0 para o aplicativo. Se o seu aplicativo passar isso para o kernel, a porta será alocada dinamicamente no momento da solicitação, e é garantido que não está em uso (a alocação falhará se todas as portas já estiverem em uso).

Caso contrário, você pode fazer isso manualmente. O script em sua resposta tem uma condição de corrida, a única maneira de evitá-lo é verificar atentamente se está aberto, tentando abri-lo. Se a porta estiver em uso, o programa deve ser encerrado com uma falha ao abrir a porta.

Por exemplo, digamos que você esteja tentando ouvir com o GNU netcat.

#!/bin/bash
read lower_port upper_port < /proc/sys/net/ipv4/ip_local_port_range
while :; do
    for (( port = lower_port ; port <= upper_port ; port++ )); do
        nc -l -p "$port" 2>/dev/null && break 2
    done
done
    
por 16.11.2012 / 17:04
50

Minha solução é ligar-se à porta 0, que pede ao kernel para alocar uma porta de sua porta ip_local_port_range. Em seguida, feche o soquete e use esse número de porta em sua configuração.

Isso funciona porque o kernel não parece reutilizar números de porta até que seja absolutamente necessário. Ligações subseqüentes à porta 0 alocarão um número de porta diferente. Código Python:

import socket

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

Isso fornece apenas um número de portas, por exemplo. 60123 .

Execute este programa 10 000 vezes (você deve executá-los simultaneamente) e obterá 10 000 números de porta diferentes. Portanto, acho muito seguro usar as portas.

    
por 29.05.2014 / 11:15
11
#!/bin/bash
read LOWERPORT UPPERPORT < /proc/sys/net/ipv4/ip_local_port_range
while :
do
        PORT="'shuf -i $LOWERPORT-$UPPERPORT -n 1'"
        ss -lpn | grep -q ":$PORT " || break
done
echo $PORT

Créditos para Chris Down

    
por 09.12.2015 / 14:30
8

One-liner

Eu criei um bom one-liner que serve rapidamente ao propósito, permitindo pegar um número arbitrário de portas em um intervalo arbitrário (aqui ele é dividido em 4 linhas para facilitar a leitura):

comm -23 \
<(seq "$FROM" "$TO" | sort) \
<(ss -tan | awk '{print $4}' | cut -d':' -f2 | grep '[0-9]\{1,5\}' | sort -u) \
| shuf | head -n "$HOWMANY"

Linha por linha

comm é um utilitário que compara linhas em dois arquivos que devem aparecer classificados em ordem alfabética. Ele gera três colunas: linhas que aparecem apenas no primeiro arquivo, linhas que aparecem apenas no segundo e linhas comuns. Especificando -23 , suprimimos as últimas colunas e apenas mantemos a primeira. Podemos usar isso para obter a diferença de dois conjuntos, expressos como uma sequência de linhas de texto. Aprendi sobre comm aqui .

O primeiro arquivo é o intervalo de portas que podemos selecionar. seq produz uma sequência classificada de números de $FROM a $TO . O resultado é classificado em ordem alfabética (em vez de numericamente) e canalizado para comm como o primeiro arquivo usando substituição de processos .

O segundo arquivo é a lista classificada de portas, que obtemos chamando o comando ss (com -t significando portas TCP, -a significando todo - estabelecido e escutando - e -n numérico - don ' t tente resolver, digamos, 22 to ssh ). Em seguida, escolhemos apenas a quarta coluna com awk , que contém o endereço e a porta locais. Usamos cut para dividir endereço e porta com o delimitador : e manter apenas o último ( -f2 ). ss também gera um cabeçalho, pelo qual nos livramos de grep ping para sequências não vazias de números que não são maiores do que 5. Então, cumprimos a exigência de comm em sort ing sem duplicatas -u .

Agora, temos uma lista classificada de portas abertas, que podemos usar shuf para, em seguida, pegar as primeiras "$HOWMANY" com head -n .

Exemplo

Pegue as três portas abertas aleatórias no intervalo privado (49152-65535)

comm -23 <(seq 49152 65535 | sort) <(ss -tan | awk '{print $4}' | cut -d':' -f2 | grep "[0-9]\{1,5\}" | sort -u) | shuf | head -n 3

poderia retornar por exemplo

54930
57937
51399

Notas

  • altere -t com -u em ss para obter portas UDP gratuitas.
  • substitua shuf por sort -n se preferir obter portas disponíveis numericamente ordenadas em vez de aleatoriamente
por 09.02.2018 / 15:24
4

Aparentemente, conexões TCP podem ser usadas como descritores de arquivos no linux a partir do com no bash / zsh. A seguinte função usa essa técnica e deve ser mais rápida do que invocar o netcat / telnet.

function EPHYMERAL_PORT(){
    LPORT=32768;
    UPORT=60999;
    while true; do
        MPORT=$[$LPORT + ($RANDOM % $UPORT)];
        (echo "" >/dev/tcp/127.0.0.1/${MPORT}) >/dev/null 2>&1
        if [ $? -ne 0 ]; then
            echo $MPORT;
            return 0;
        fi
    done
}

Instruções de uso: Vincule a saída a uma variável e use em scripts. Testado no Ubuntu 16.04

root@ubuntu:~> EPHYMERAL_PORT
59453
root@ubuntu:~> PORT=$(EPHYMERAL_PORT)
    
por 16.12.2016 / 05:58
3

No Linux, você pode fazer algo como:

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}'

Para encontrar a primeira porta livre acima de 1080. Observe que ssh -D seria ligado na interface de loopback, portanto, em teoria, você poderia reutilizar a porta 1080 se um soquete a vinculasse em outro endereço. Outra maneira seria realmente tentar ligá-lo:

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'
    
por 16.11.2012 / 21:26
2

Aqui está um "oneliner" de plataforma cruzada que absorve todas as portas em uso e oferece a primeira disponível a partir de 3000:

netstat -aln | awk '
  $6 == "LISTEN" {
    if ($4 ~ "[.:][0-9]+$") {
      split($4, a, /[:.]/);
      port = a[length(a)];
      p[port] = 1
    }
  }
  END {
    for (i = 3000; i < 65000 && p[i]; i++){};
    if (i == 65000) {exit 1};
    print i
  }
'

Você pode simplesmente juntar todas as linhas para tê-lo em uma linha. Se você deseja obter o primeiro disponível a partir de um número de porta diferente, altere a atribuição para i no loop for .

Funciona tanto no Mac quanto no Linux, e é por isso que o [:.] regex é necessário.

    
por 10.04.2017 / 14:17
1

Isso faz parte de uma função que tenho em meu .bashrc, que cria dinamicamente túneis SSH e tenta usar qualquer porta em um intervalo:

   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

    
por 16.11.2012 / 20:48
1

Mais uma corrida neste velho cavalo de passatempo:

function random_free_tcp_port {
  local ports="${1:-1}" interim="${2:-2048}" spacing=32
  local free_ports=( )
  local taken_ports=( $( netstat -aln | egrep ^tcp | fgrep LISTEN |
                         awk '{print $4}' | egrep -o '[0-9]+$' |
                         sort -n | uniq ) )
  interim=$(( interim + (RANDOM % spacing) ))

  for taken in "${taken_ports[@]}" 65535
  do
    while [[ $interim -lt $taken && ${#free_ports[@]} -lt $ports ]]
    do
      free_ports+=( $interim )
      interim=$(( interim + spacing + (RANDOM % spacing) ))
    done
    interim=$(( interim > taken + spacing
                ? interim
                : taken + spacing + (RANDOM % spacing) ))
  done

  [[ ${#free_ports[@]} -ge $ports ]] || return 2

  printf '%d\n' "${free_ports[@]}"
}

Este código faz uso puramente portátil de netstat , egrep , awk , & al. Observe que somente a chamada é emitida para comandos externos, para obter uma lista de portas tomadas no início. Pode-se solicitar uma ou mais portas livres:

:;  random_free_tcp_port
2070
:;  random_free_tcp_port 2
2073
2114

e inicie em uma porta arbitrária:

:;  random_free_tcp_port 2 10240
10245
10293
    
por 19.07.2017 / 08:25
1

Esta é a versão que eu uso:

while
  port=$(shuf -n 1 -i 49152-65535)
  netstat -atun | grep -q "$port"
do
  continue
done

echo "$port"

O comando shuf -n 1 -i 49152-65535 fornece uma porta "aleatória" no intervalo dinâmico. Se já for usado, outra porta nesse intervalo é tentada.

O comando netstat -atun lista todas as portas (-a) TCP (-t) e UDP (-u) sem perder tempo para determinar nomes de host (-n).

    
por 04.06.2018 / 13:36
0

Eu escrevi este pequeno script em Python2 / 3 que pode ajudá-lo da próxima vez:

link

    
por 09.08.2018 / 11:03
0
while port=$(shuf -n1 -i $(cat /proc/sys/net/ipv4/ip_local_port_range | tr '1' '-'))
netstat -atun | grep -q ":$port\s" ; do
    continue
done
echo $port

Minha combinação de outras respostas acima. Obtê-lo:

Com shuf-n1 pegamos um número aleatório do intervalo (-i) em / proc / sys / net / ipv4 / ip_local_port_range. shuf precisa da sintaxe com traço, então usamos tr para alterar a tabulação em um traço.

Em seguida, use o netstat para nos mostrar todas as conexões (-a) tcp e udp (-u -t) em números (-n), se encontrarmos nossa porta $ port aleatória neste (inicie com a: e termine com w whitespace (\ s) então precisamos de um outro Port e assim continue Else (grep -q tem um returncode > 0 nós deixamos o while loop e $ port é setado.

    
por 03.10.2018 / 00:14
0

Se você tem python por aí, eu faria isso:

port="$(python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1])')";
echo "Unused Port: $port"
    
por 29.10.2018 / 22:04