Prova dallo script della shell se la porta TCP remota è aperta

Sto cercando un metodo semplice e veloce per verificare 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 scadere quando non lo è e si blocca 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 telnet a timeout se non si collega in meno di 8 secondi per esempio, e restituire qualcosa che posso catturare in Shell (codice di ritorno o stringa in stdout).

Conosco il metodo Perl, che usa il modulo IO :: Socket :: INET e scrive uno script riuscito che testa una porta, ma preferisce evitare di usare Perl se ansible.

    Nota: questo è ciò che il mio server è in esecuzione (dove ho bisogno di eseguire questo da)

    SunOS 5.10 Generic_139556-08 i86pc i386 i86pc

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

     nc -z   

    In questo modo nc controllerà 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   

    È abbastanza facile fare con le opzioni -z e -w TIMEOUT su nc , ma non tutti i sistemi hanno installato nc . Se hai una versione abbastanza recente di bash, questo funzionerà:

     # 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 il timeout eseguirà il sottocomando e lo ucciderà se non esce entro il timeout specificato (1 secondo nell’esempio sopra). In questo caso bash è il sottocomando e utilizza la sua gestione speciale / dev / tcp per provare ad aprire una connessione al server e alla porta specificati. Se bash può aprire la connessione entro il timeout, cat lo chiuderà immediatamente (poiché sta leggendo da /dev/null ) e uscirà con un codice di stato di 0 che si propagherà attraverso bash e poi timeout . Se bash ottiene un errore di connessione prima del timeout specificato, bash uscirà con un codice di uscita 1 che verrà restituito anche in timeout . E se bash non è in grado di stabilire una connessione e il timeout specificato scade, allora timeout ucciderà bash e uscirà con uno stato di 124.

    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

    Utilizzo di bash e timeout :

    Si noti che il timeout deve essere presente con RHEL 6+, o che si trova alternativamente in coreutils GNU 8.22. Su MacOS, installalo usando brew install coreutils e gtimeout come gtimeout .

    Comando:

     $ timeout $TIMEOUT_SECONDS bash -c " 

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

    Esempio:

    Successo:

     $ timeout 2 bash -c " 

    Fallimento:

     $ timeout 2 bash -c " 

    Se è necessario preservare lo stato di uscita di bash ,

     $ timeout --preserve-status 2 bash -c " 

    Utilizzando nc :

    Si noti che una versione backward incompatibile di nc viene installata su RHEL 7.

    Comando:

    Nota che il comando qui sotto è unico in quanto è identico sia per RHEL 6 che per 7. È solo l'installazione e l'output che sono diversi.

     $ nc -w $TIMEOUT_SECONDS -v $HOST $PORT  

    RHEL 6 (nc-1.84):

    Installazione:

     $ sudo yum install nc 

    Esempi:

    Successo:

     $ nc -w 2 -v canyouseeme.org 80  

    Fallimento:

     $ nc -w 2 -v canyouseeme.org 81  

    Se il nome host si associa a più IP, il comando di errore sopra riportato scorrerà tutti o molti di loro. Per esempio:

     $ nc -w 2 -v microsoft.com 81  

    RHEL 7 (nmap-ncat-6.40):

    Installazione:

     $ sudo yum install nmap-ncat 

    Esempi:

    Successo:

     $ nc -w 2 -v canyouseeme.org 80  

    Fallimento:

     $ nc -w 2 -v canyouseeme.org 81  

    Se il nome host si associa a più IP, il comando di errore sopra riportato scorrerà tutti o molti di loro. Per esempio:

     $ nc -w 2 -v microsoft.com 81  

    Osservazioni:

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

    Con netcat puoi verificare se una porta è aperta in questo modo:

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

    Il valore restituito da nc sarà positivo se la porta TCP è stata aperta e l'errore (in genere il codice di ritorno 1) se non è riuscito a stabilire la connessione TCP.

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

     #!/usr/bin/env bash SERVER=example.com PORT=80  

    test:

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

    Ecco one-liner (syntax di Bash):

      

    Tieni presente che alcuni server possono essere protetti dal firewall dagli attacchi flood di SYN, pertanto potresti riscontrare un timeout della connessione TCP (~ 75secs). Per risolvere il problema di timeout, prova:

     timeout 1 bash -c " 

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

    Mentre ho una vecchia domanda, ne ho appena trattato una variante, ma nessuna delle soluzioni qui era applicabile, quindi ne ho trovata un’altra e la sto aggiungendo per i posteri. Sì, so che l’OP ha detto di essere a conoscenza di questa opzione e che non gli è valsa la pena, ma per chi segue in seguito potrebbe rivelarsi utile.

    Nel mio caso, voglio testare la disponibilità di un servizio apt-cacher-ng locale da una build docker . Ciò significa che assolutamente nulla può essere installato prima del test. No nc , nmap , expect , telnet o python . tuttavia perl è 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")))' 

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

     W$ python -m SimpleHTTPServer & [1] 16833 Serving HTTP on 0.0.0.0 port 8000 ... W$ :  

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

     W$ :  

    È quindi ansible utilizzarlo 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 

    Il no-op è in una sotto-shell, quindi posso lanciare std-err via se il reindirizzamento std-in fallisce.

    Io 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 cat per leggere dal socket.

    Avevo bisogno di una soluzione più flessibile per lavorare su più repository git, così ho scritto il seguente codice sh basato su 1 e 2 . Puoi utilizzare il tuo indirizzo del server invece di gitlab.com e la tua porta in sostituzione del 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 

    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 

    Se vuoi usare nc ma non hai una versione che supporti -z , prova a usare --send-only :

     nc --send-only    

    e con timeout:

     nc -w 1 --send-only    

    e senza ricerca DNS se si tratta di un IP:

     nc -n -w 1 --send-only    

    Restituisce i codici come -z basato se è ansible connettersi o meno.

    Che ne dici di Netcat o Nmap ?

    Suppongo che sia troppo tardi per una risposta, e questo potrebbe non essere buono, ma qui vai …

    Che dire di metterlo all’interno di un ciclo while con un timer su di esso di qualche tipo. Sono più un ragazzo Perl di 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 

    E quindi basta aggiungere un flag nel ciclo while, in modo che se scade prima del completamento, è ansible citare il timeout come motivo di errore.

    Sospetto che il telnet abbia anche un interruttore di timeout, ma appena in cima alla mia testa, penso che quanto sopra funzionerà.

    Avevo bisogno di un breve script che fosse eseguito in cron e non fosse in uscita. 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 Dovresti installare nmap perché non è un pacchetto installato di default.

    Questo utilizza il telnet dietro le quinte e sembra funzionare bene su mac / linux. Non usa netcat a causa delle differenze tra le versioni su linux / mac e questo 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' "[email protected]"; } 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 

    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 

    Basandosi sulla risposta più votata, ecco una funzione per aspettare che due porte siano aperte, con un timeout. Nota le due porte che devono essere aperte, 8890 e 1111, nonché i max_attempts (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 } 

    nmap-ncat per testare 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 "$?" } 

    La risposta ha mentito 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 un sacco di esempi tra cui scegliere.

    Abbiamo basato la nostra su questo post: http://www.unix.com/shell-programming-scripting/146568-expect-telnet-testing-tacacs-cisco.html