Verifica se la porta TCP remota è aperta da uno script di shell


297

Sto cercando un metodo rapido e semplice per testare correttamente se una determinata porta TCP è aperta su un server remoto, all'interno di uno script Shell.

Sono riuscito a farlo con il comando telnet, e funziona bene quando la porta è aperta, ma non sembra andare in timeout quando non lo è e si blocca solo lì ...

Ecco un esempio:

l_TELNET=`echo "quit" | telnet $SERVER $PORT | grep "Escape character is"`
if [ "$?" -ne 0 ]; then
  echo "Connection to $SERVER on port $PORT failed"
  exit 1
else
  echo "Connection to $SERVER on port $PORT succeeded"
  exit 0
fi

O ho bisogno di un modo migliore, o di un modo per forzare il timeout di telnet se, ad esempio, non si connette in meno di 8 secondi e restituire qualcosa che posso catturare in Shell (codice di ritorno o stringa in stdout).

Conosco il metodo Perl, che utilizza il modulo IO :: Socket :: INET e ha scritto uno script di successo che testa una porta, ma preferirei evitare di usare Perl, se possibile.

Nota: questo è ciò che il mio server è in esecuzione (da dove devo eseguirlo)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc


Che dire di Netcat o Nmap ?
Jeremiah Willcock,

La risposta è stata con Expect. Abbiamo scritto un semplice script che invia un telnet sulla porta di cui avevamo bisogno, con un timeout di 8 secondi. Ci sono anche molti esempi tra cui scegliere. Abbiamo basato il nostro su questo post: unix.com/shell-programming-scripting/…
Yanick Girouard,

1
check_tcp da github.com/monitoring-plugins/monitoring-plugins può farlo, incluso l'inserimento di stringhe e il controllo della risposta prevista.

Risposte:


452

Come indicato da B. Rhodes, ncfarà il lavoro. Un modo più compatto per usarlo:

nc -z <host> <port>

In questo modo nccontrollerà solo se la porta è aperta, uscendo con 0 in caso di successo, 1 in caso di fallimento.

Per un rapido controllo interattivo (con un timeout di 5 secondi):

nc -z -v -w5 <host> <port>

48
centos7 usa nmap netcat di default e non ha l'opzione -z.
Jolestar,

7
Questo non funziona in RHEL / Centos. Per quelle distro devi: nc -vn <host> <porta>
Justin Dennahower,

2
FWIW, ho completamente rivisto la mia risposta con un esempio , applicabile separatamente sia a RHEL 6 che a RHEL 7.
Acumenus,

4
almeno su Mac, potrebbe essere necessario aggiungere -G#per impostare un timeout di connessione separato da / in aggiunta al -w#timeout, che sostanzialmente funziona come un timeout di lettura.
Doktor J,

1
@jolestar È possibile aggiornare manualmente Ncat su Centos 7 per ottenere l' -zopzione. Ti consigliamo di prendere in considerazione: unix.stackexchange.com/questions/393762/…
Damien Ó Ceallaigh,

150

È abbastanza facile da fare con le opzioni -ze , ma non tutti i sistemi sono stati installati. Se hai una versione abbastanza recente di bash, funzionerà:-w TIMEOUTncnc

# Connection successful:
$ timeout 1 bash -c 'cat < /dev/null > /dev/tcp/google.com/80'
$ echo $?
0

# Connection failure prior to the timeout
$ timeout 1 bash -c 'cat < /dev/null > /dev/tcp/sfsfdfdff.com/80'
bash: sfsfdfdff.com: Name or service not known
bash: /dev/tcp/sfsfdfdff.com/80: Invalid argument
$ echo $?
1

# Connection not established by the timeout
$ timeout 1 bash -c 'cat < /dev/null > /dev/tcp/google.com/81'
$ echo $?
124

Quello che sta succedendo qui è che timeouteseguirà il sottocomando e lo ucciderà se non termina entro il timeout specificato (1 secondo nell'esempio sopra). In questo caso bashè il sottocomando e usa la sua speciale gestione / dev / tcp per provare ad aprire una connessione al server e alla porta specificati. Se è bashpossibile aprire la connessione entro il timeout, catla chiuderà immediatamente (poiché sta leggendo da /dev/null) e 0terminerà con un codice di stato che si propagherà attraverso bashe poi timeout. Se bashsi verifica un errore di connessione prima del timeout specificato, ucciderà e uscirà con uno stato di 124.bash verrà chiuso con un codice di uscita pari a 1, che timeoutverrà restituito. E se bash non è in grado di stabilire una connessione e il timeout specificato scade, alloratimeoutbash


1
È /dev/tcpdisponibile su sistemi diversi da Linux? Che dire dei Mac in particolare?
Peter,

8
La cosa / dev / tcp è una caratteristica di bash, quindi sì. Tuttavia sembra che i mac non abbiano timeout ...
solo il

1
@onlynone - Sembra timeoutche non esista anche in FreeBSD, e sulla mia vecchia scatola Ubuntu è un pacchetto installabile, ma non è presente di default. Sarebbe fantastico se ci fosse un modo per farlo da soli in bash, senza strumenti di terze parti come timeout o netcat.
Graham

3
Volevo solo dire che timeoutsembra essere parte di coreutils GNU, e possono essere installati sui Mac con homebrew: brew install coreutils. Sarà quindi disponibile come gtimeout.
solo il

1
@Wildcard Un registro delle modifiche bash suggerisce bash-2.04-devel, anche se potrebbe essere stato aggiunto il supporto per i nomi host bash-2.05-alpha1.
Acumenus,

109

TOC:

  • Usando bash e timeout
    • Comando
    • Esempi
  • utilizzando nc
    • Comando
    • RHEL 6 (nc-1.84)
      • Installazione
      • Esempi
    • RHEL 7 (nmap-ncat-6.40)
      • Installazione
      • Esempi
  • Osservazioni

Usando bash e timeout:

Nota che timeoutdovrebbe essere presente con RHEL 6+ o che si trova in alternativa nei coreutils GNU 8.22. Su MacOS, installalo usando brew install coreutilse usalo come gtimeout.

Comando:

$ timeout $TIMEOUT_SECONDS bash -c "</dev/tcp/${HOST}/${PORT}"; echo $?

Se si parametrizzano l'host e la porta, assicurarsi di specificarli come ${HOST}e ${PORT}come sopra. Non specificarli semplicemente come $HOSTe $PORT, cioè senza le parentesi graffe; non funzionerà in questo caso.

Esempio:

Successo:

$ timeout 2 bash -c "</dev/tcp/canyouseeme.org/80"; echo $?
0

Fallimento:

$ timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?
124

Se è necessario conservare lo stato di uscita di bash,

$ timeout --preserve-status 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?
143

Utilizzando nc:

Si noti che una versione incompatibile con le versioni precedenti ncviene installata su RHEL 7.

Comando:

Si noti che il comando seguente è unico in quanto è identico sia per RHEL 6 che per 7. Sono solo l'installazione e l'output a differire.

$ nc -w $TIMEOUT_SECONDS -v $HOST $PORT </dev/null; echo $?

RHEL 6 (nc-1.84):

Installazione:

$ sudo yum install nc

Esempi:

Successo:
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Connection to canyouseeme.org 80 port [tcp/http] succeeded!
0
Fallimento:
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
nc: connect to canyouseeme.org port 81 (tcp) timed out: Operation now in progress
1

Se il nome host è associato a più IP, il comando sopra fallito scorrerà attraverso molti o tutti. Per esempio:

$ nc -w 2 -v microsoft.com 81 </dev/null; echo $?
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
nc: connect to microsoft.com port 81 (tcp) timed out: Operation now in progress
1

RHEL 7 (nmap-ncat-6.40):

Installazione:

$ sudo yum install nmap-ncat

Esempi:

Successo:
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connected to 52.202.215.126:80.
Ncat: 0 bytes sent, 0 bytes received in 0.22 seconds.
0
Fallimento:
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection timed out.
1

Se il nome host è associato a più IP, il comando sopra fallito scorrerà attraverso molti o tutti. Per esempio:

$ nc -w 2 -v microsoft.com 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection to 104.43.195.251 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection to 23.100.122.175 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection to 23.96.52.53 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection to 191.239.213.197 failed: Connection timed out.
Ncat: Trying next address...
Ncat: Connection timed out.
1

Osservazioni:

L' argomento -v( --verbose) e il echo $?comando sono ovviamente solo a scopo illustrativo.


3
timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?è un ottimo punto!
ipeacocks,

aggiungi 2>&1a not echo status result_test=$(nc -w 2 $ip_addreess 80 </dev/null 2>&1 ; echo $?)
Sanya Snex

55

Con netcatpuoi verificare se una porta è aperta in questo modo:

nc my.example.com 80 < /dev/null

Il valore restituito ncsarà corretto se la porta TCP è stata aperta e errore (in genere il codice restituito 1) se non è stato possibile stabilire la connessione TCP.

Alcune versioni di si ncbloccheranno quando provi questo, perché non chiudono la metà di invio del loro socket anche dopo aver ricevuto la fine del file da /dev/null. Sul mio laptop Ubuntu (18.04), la netcat-openbsdversione di netcat che ho installato offre una soluzione alternativa: l' -Nopzione è necessaria per ottenere un risultato immediato:

nc -N my.example.com 80 < /dev/null

1
Funziona alla grande per sapori ncche non supportano la -wbandiera.
David Dossot,

5
grazie - funziona anche per le versioni di nc senza -zsupporto - funziona su RHEL / CentOS 7, ad esempio
Andrew

1
Sebbene questo approccio sia valido, -wè necessario, in caso contrario il comando quando utilizzato in uno script può bloccarsi per oltre dieci secondi. Ho scritto una risposta che include -w.
Acumenus,

2
+1 questo è fantastico perché nc viene fornito di serie con alpine linux e ubuntu entrambi. probabilmente altri. non è necessario installare extra quando si è remotati e non è possibile. grazie per questo! Potrei aggiungere,nc host port -w 2 && echo it works
std''OrgnlDave,

2
Io preferisco nc -vz localhost 3306. Fornisce un output più dettagliato. Ho provato solo su Mac.
Fatti il

29

In Bash l'uso dei file pseudo-dispositivo per le connessioni TCP / UDP è semplice. Ecco la sceneggiatura:

#!/usr/bin/env bash
SERVER=example.com
PORT=80
</dev/tcp/$SERVER/$PORT
if [ "$?" -ne 0 ]; then
  echo "Connection to $SERVER on port $PORT failed"
  exit 1
else
  echo "Connection to $SERVER on port $PORT succeeded"
  exit 0
fi

test:

$ ./test.sh 
Connection to example.com on port 80 succeeded

Ecco una riga (sintassi di Bash):

</dev/tcp/localhost/11211 && echo Port open. || echo Port closed.

Si noti che alcuni server possono essere protetti da firewall dagli attacchi flood di SYN, pertanto è possibile che si verifichi un timeout della connessione TCP (~ 75 secondi). Per ovviare al problema del timeout, provare:

timeout 1 bash -c "</dev/tcp/stackoverflow.com/81" && echo Port open. || echo Port closed.

Vedi: Come ridurre il timeout delle chiamate di sistema TCP connect ()?


1
@ABB Questo è legato alla configurazione del firewall del server che non fornisce alcuna risposta per prevenire eventuali attacchi alluvione SYN. Anche la corsa telnet canyouseeme.org 81si blocca. Questo è controllato dai limiti di timeout che probabilmente sono codificati in bash. Vedere: ridurre il timeout della chiamata di sistema TCP connect () .
Kenorb,

Per la parametrizzazione, ora sembra necessario specificare $SERVERe $PORTcon parentesi graffe, almeno come ${SERVER}e ${PORT}.
Acumenus,

13

Avevo bisogno di una soluzione più flessibile per lavorare su più repository git, quindi ho scritto il seguente codice sh basato su 1 e 2 . È possibile utilizzare l'indirizzo del server anziché gitlab.com e la porta in sostituzione di 22.

SERVER=gitlab.com
PORT=22
nc -z -v -w5 $SERVER $PORT
result1=$?

#Do whatever you want

if [  "$result1" != 0 ]; then
  echo  'port 22 is closed'
else
  echo 'port 22 is open'
fi

1
Grazie! Mi rende la vita facile per /etc/rc.local
Shahin Khaled il

10

Se stai usando ksh o bash entrambi supportano il reindirizzamento IO verso / da un socket usando il costrutto / dev / tcp / IP / PORT . In questo shell Korn esempio sto reindirizzando no-op ( : ) STD-in da presa:

W$ python -m SimpleHTTPServer &
[1]     16833
Serving HTTP on 0.0.0.0 port 8000 ...
W$ : </dev/tcp/127.0.0.1/8000

La shell stampa un errore se il socket non è aperto:

W$ : </dev/tcp/127.0.0.1/8001
ksh: /dev/tcp/127.0.0.1/8001: cannot open [Connection refused]

È quindi possibile utilizzare questo come test in una condizione if :

SERVER=127.0.0.1 PORT=8000
if (: < /dev/tcp/$SERVER/$PORT) 2>/dev/null
then
    print succeeded
else
    print failed
fi

La no-op è in una subshell, quindi posso buttare via std-err se il reindirizzamento std-in fallisce.

Uso spesso / dev / tcp per verificare la disponibilità di una risorsa su HTTP:

W$ print arghhh > grr.html
W$ python -m SimpleHTTPServer &
[1]     16863
Serving HTTP on 0.0.0.0 port 8000 ...
W$ (print -u9 'GET /grr.html HTTP/1.0\n';cat <&9) 9<>/dev/tcp/127.0.0.1/8000
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/2.6.1
Date: Thu, 14 Feb 2013 12:56:29 GMT
Content-type: text/html
Content-Length: 7
Last-Modified: Thu, 14 Feb 2013 12:55:44 GMT

arghhh
W$ 

Questo one-liner apre il descrittore di file 9 per leggere e scrivere sul socket, stampa l' HTTP GET sul socket e usa catper leggere dal socket.


1
@ABB Penso che intendi che si blocca a lungo se la porta non risponde, come ad esempio quando viene filtrata o se non c'è nulla sull'IP. Una porta chiusa non causa un ritardo se la porta rifiuta attivamente la connessione. Per molti scopi questo è abbastanza accettabile.
MC0e

Questa tecnica era già stata pubblicata prima di questa risposta in una risposta precedente di kenorb . Comunque, il punto più grande è che si blocca comunque a lungo se la porta non risponde. Ad esempio, prova </dev/tcp/canyouseeme.org/80e poi </dev/tcp/canyouseeme.org/81.
Acumenus

9

Mentre una vecchia domanda, ho appena affrontato una variante, ma nessuna delle soluzioni qui era applicabile, quindi ho trovato un'altra e la sto aggiungendo per i posteri. Sì, so che l'OP ha affermato di essere a conoscenza di questa opzione e non era adatta a loro, ma per chiunque lo seguisse in seguito potrebbe rivelarsi utile.

Nel mio caso, voglio verificare la disponibilità di un apt-cacher-ngservizio locale da una dockerbuild. Ciò significa che assolutamente nulla può essere installato prima del test. No nc, nmap, expect, telneto python. perltuttavia è presente, insieme alle librerie di base, quindi ho usato questo:

perl -MIO::Socket::INET -e 'exit(! defined( IO::Socket::INET->new("172.17.42.1:3142")))'

6

In alcuni casi in cui strumenti come curl, telnet, nc o nmap non sono disponibili, hai ancora una possibilità con wget

if [[ $(wget -q -t 1 --spider --dns-timeout 3 --connect-timeout 10  host:port; echo $?) -eq 0 ]]; then echo "OK"; else echo "FAIL"; fi

6

controlla le porte usando bash

Esempio

$ ./test_port_bash.sh 192.168.7.7 22

la porta 22 è aperta

Codice

HOST=$1
PORT=$2
exec 3> /dev/tcp/${HOST}/${PORT}
if [ $? -eq 0 ];then echo "the port $2 is open";else echo "the port $2 is closed";fi

4

Se si desidera utilizzare ncma non si dispone di una versione che supporti -z, provare a utilizzare --send-only:

nc --send-only <IP> <PORT> </dev/null

e con timeout:

nc -w 1 --send-only <IP> <PORT> </dev/null

e senza ricerca DNS se è un IP:

nc -n -w 1 --send-only <IP> <PORT> </dev/null

Restituisce i codici come -zbasato su se può connettersi o meno.


1

Immagino che sia troppo tardi per una risposta, e potrebbe non essere una buona risposta, ma eccoti qui ...

Che ne dici di metterlo in un ciclo while con un timer su di esso in qualche modo. Sono più un tipo Perl che Solaris, ma a seconda della shell che stai usando, dovresti essere in grado di fare qualcosa del tipo:

TIME = 'date +%s' + 15
while TIME != `date +%s'
do whatever

Quindi aggiungi semplicemente un flag nel ciclo while, in modo che se scade prima del completamento, puoi citare il timeout come motivo di errore.

Sospetto che anche il telnet abbia un timeout, ma appena sopra la mia testa, penso che quanto sopra funzionerà.


1

Avevo bisogno di un breve script che fosse eseguito in cron e che non sia stato riprodotto. Risolvo i miei problemi usando nmap

open=`nmap -p $PORT $SERVER | grep "$PORT" | grep open`
if [ -z "$open" ]; then
  echo "Connection to $SERVER on port $PORT failed"
  exit 1
else
  echo "Connection to $SERVER on port $PORT succeeded"
  exit 0
fi

Per eseguirlo È necessario installare nmap perché non è un pacchetto installato predefinito.


nmapL'output grepable potrebbe anche essere utile -oG -per inviarlo a stdout. (il grep avrebbe bisogno di un po 'di modifiche)
Gert van den Berg,

0

Questo utilizza telnet dietro le quinte e sembra funzionare bene su mac / linux. Non utilizza netcat a causa delle differenze tra le versioni su linux / mac e funziona con un'installazione mac predefinita.

Esempio:

$ is_port_open.sh 80 google.com
OPEN

$ is_port_open.sh 8080 google.com
CLOSED

is_port_open.sh

PORT=$1
HOST=$2
TIMEOUT_IN_SEC=${3:-1}
VALUE_IF_OPEN=${4:-"OPEN"}
VALUE_IF_CLOSED=${5:-"CLOSED"}

function eztern()
{
  if [ "$1" == "$2" ]
  then
    echo $3
  else
    echo $4
  fi
}

# cross platform timeout util to support mac mostly
# https://gist.github.com/jaytaylor/6527607
function eztimeout() { perl -e 'alarm shift; exec @ARGV' "$@"; }

function testPort()
{
  OPTS=""

  # find out if port is open using telnet
  # by saving telnet output to temporary file
  # and looking for "Escape character" response
  # from telnet
  FILENAME="/tmp/__port_check_$(uuidgen)"
  RESULT=$(eztimeout $TIMEOUT_IN_SEC telnet $HOST $PORT &> $FILENAME; cat $FILENAME | tail -n1)
  rm -f $FILENAME;
  SUCCESS=$(eztern "$RESULT" "Escape character is '^]'." "$VALUE_IF_OPEN" "$VALUE_IF_CLOSED")

  echo "$SUCCESS"
}

testPort 

0

Sulla base della risposta più votata, ecco una funzione per attendere l'apertura di due porte, anche con un timeout. Nota le due porte che devono essere aperte, 8890 e 1111, nonché i max_attempt (1 al secondo).

function wait_for_server_to_boot()
{
    echo "Waiting for server to boot up..."
    attempts=0
    max_attempts=30
    while ( nc 127.0.0.1 8890 < /dev/null || nc 127.0.0.1 1111 < /dev/null )  && [[ $attempts < $max_attempts ]] ; do
        attempts=$((attempts+1))
        sleep 1;
        echo "waiting... (${attempts}/${max_attempts})"
    done
}

-1

nmap-ncat per verificare la porta locale che non è già in uso


availabletobindon() {
  port="$1"
  nc -w 2 -i 1 localhost "$port" 2>&1 | grep -v -q 'Idle timeout expired'
  return "$?"
}

Non mi dice che la mia porta 80 sia aperta.
totoro,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.