Prueba de script de shell si el puerto TCP remoto está abierto

Estoy buscando un método rápido y simple para probar adecuadamente si un puerto TCP dado está abierto en un servidor remoto, desde dentro de un script de Shell.

He logrado hacerlo con el comando telnet, y funciona bien cuando se abre el puerto, pero parece que no se agota cuando no está y simplemente se cuelga allí …

Aquí hay una muestra:

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 

Necesito una forma mejor, o una forma de forzar a Telnet a que agote el tiempo de espera si no se conecta en menos de 8 segundos, por ejemplo, y devolver algo que pueda capturar en Shell (código de retorno o cadena en stdout).

Conozco el método Perl, que usa el módulo IO :: Socket :: INET y escribió un script exitoso que prueba un puerto, pero preferiría evitar el uso de Perl si fuera posible.

Nota: Esto es lo que está ejecutando mi servidor (donde necesito ejecutar esto)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc

    Como señaló B. Rhodes, nc hará el trabajo. Una forma más compacta de usarlo:

     nc -z   

    De esta forma, nc solo comprobará si el puerto está abierto, saliendo con 0 en caso de éxito, 1 si falla.

    Para una comprobación interactiva rápida (con un tiempo de espera de 5 segundos):

     nc -z -v -w5   

    Es bastante fácil hacer con las opciones -z y -w TIMEOUT a nc , pero no todos los sistemas se han instalado. Si tienes una versión bastante reciente de bash, esto funcionará:

     # 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 

    Lo que sucede aquí es que el timeout ejecutará el subcomando y lo matará si no sale dentro del tiempo de espera especificado (1 segundo en el ejemplo anterior). En este caso, bash es el subcomando y utiliza su manejo especial / dev / tcp para tratar de abrir una conexión al servidor y puerto especificado. Si bash puede abrir la conexión dentro del tiempo de espera, cat simplemente la cerrará inmediatamente (ya que está leyendo desde /dev/null ) y saldrá con un código de estado de 0 que se propagará a través de bash y luego timeout . Si bash obtiene una falla de conexión antes del tiempo de espera especificado, entonces bash saldrá con un código de salida de 1, que también retornará. Y si bash no puede establecer una conexión y expira el tiempo de espera especificado, entonces el timeout matará a bash y saldrá con un estado de 124.

    TOC:

    • Usando bash y timeout
      • Mando
      • Ejemplos
    • Usando nc
      • Mando
      • RHEL 6 (nc-1.84)
        • Instalación
        • Ejemplos
      • RHEL 7 (nmap-ncat-6.40)
        • Instalación
        • Ejemplos
    • Observaciones

    Usando bash y timeout :

    Tenga en cuenta que el timeout debe estar presente con RHEL 6+, o alternativamente se encuentra en GNU coreutils 8.22. En MacOS, instálalo usando brew install coreutils y gtimeout como gtimeout .

    Mando:

     $ timeout $TIMEOUT_SECONDS bash -c " 

    Si parametriza el host y el puerto, asegúrese de especificarlos como ${HOST} y ${PORT} como se muestra arriba. No los especifique simplemente como $HOST y $PORT , es decir, sin llaves; no funcionará en este caso.

    Ejemplo:

    Éxito:

     $ timeout 2 bash -c " 

    Fracaso:

     $ timeout 2 bash -c " 

    Si debe preservar el estado de salida de bash ,

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

    Usando nc :

    Tenga en cuenta que una versión incompatible hacia atrás de nc se instala en RHEL 7.

    Mando:

    Tenga en cuenta que el siguiente comando es único ya que es idéntico para RHEL 6 y 7. Es solo la instalación y la salida las que son diferentes.

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

    RHEL 6 (nc-1.84):

    Instalación:

     $ sudo yum install nc 

    Ejemplos:

    Éxito:

     $ nc -w 2 -v canyouseeme.org 80  

    Fracaso:

     $ nc -w 2 -v canyouseeme.org 81  

    Si el nombre de host se asigna a varias direcciones IP, el comando de falla anterior recorrerá muchos o todos ellos. Por ejemplo:

     $ nc -w 2 -v microsoft.com 81  

    RHEL 7 (nmap-ncat-6.40):

    Instalación:

     $ sudo yum install nmap-ncat 

    Ejemplos:

    Éxito:

     $ nc -w 2 -v canyouseeme.org 80  

    Fracaso:

     $ nc -w 2 -v canyouseeme.org 81  

    Si el nombre de host se asigna a varias direcciones IP, el comando de falla anterior recorrerá muchos o todos ellos. Por ejemplo:

     $ nc -w 2 -v microsoft.com 81  

    Observaciones:

    El argumento -v ( --verbose ) y el echo $? comando son, por supuesto, solo para ilustración.

    Con netcat puede verificar si un puerto está abierto así:

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

    El valor de retorno de nc será exitoso si se abrió el puerto TCP, y la falla (generalmente el código de retorno 1) si no puede hacer la conexión TCP.

    En Bash, el uso de archivos de pseudodispositivos para conexiones TCP / UDP es sencillo. Aquí está el guión:

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

    Pruebas:

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

    Aquí hay una línea (syntax Bash):

      

    Tenga en cuenta que algunos servidores pueden estar protegidos contra firewalls contra ataques de inundación SYN, por lo que puede experimentar un tiempo de espera de conexión TCP (~ 75secs). Para solucionar el problema del tiempo de espera, intente:

     timeout 1 bash -c " 

    Ver: Cómo disminuir el tiempo de espera de la llamada al sistema TCP connect ()?

    Si bien es una vieja pregunta, acabo de tratar con una variante de ella, pero ninguna de las soluciones aquí eran aplicables, así que encontré otra, y la estoy agregando para la posteridad. Sí, sé que OP dijo que estaban al tanto de esta opción y que no les convenía, pero que cualquier persona que la siga podría resultar útil.

    En mi caso, quiero probar la disponibilidad de un servicio apt-cacher-ng local de una comstackción de docker . Eso significa que no se puede instalar absolutamente nada antes de la prueba. No nc , nmap , expect , telnet o python . Sin embargo, perl está presente, junto con las bibliotecas principales, así que utilicé esto:

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

    Si usa ksh o bash , ambos admiten la redirección de IO hacia / desde un socket utilizando la construcción / dev / tcp / IP / PORT . En este ejemplo de shell Korn estoy redireccionando std-in de no-op (:) desde un socket:

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

    El shell imprime un error si el socket no está abierto:

     W$ :  

    Por lo tanto, puede usar esto como prueba en una condición if :

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

    El no-op está en una subcapa, así que puedo tirar std-err si falla la redirección estándar.

    A menudo uso / dev / tcp para verificar la disponibilidad de un recurso a través de 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$ 

    Este "one-liner" abre el descriptor de archivo 9 para leer y escribir en el zócalo, imprime el HTTP GET en el zócalo y usa cat para leer desde el zócalo.

    Necesitaba una solución más flexible para trabajar en múltiples repositorys git, así que escribí el siguiente código sh basado en 1 y 2 . Puede usar su dirección de servidor en lugar de gitlab.com y su puerto en reemplazo de 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 

    En algunos casos en que las herramientas como curl, telnet, nc o nmap no están disponibles, todavía tienes una oportunidad 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 

    Si desea usar nc pero no tiene una versión que admita -z , intente usar --send-only :

     nc --send-only    

    y con tiempo de espera:

     nc -w 1 --send-only    

    y sin búsqueda de DNS si es una IP:

     nc -n -w 1 --send-only    

    Devuelve los códigos como -z función de si se puede conectar o no.

    ¿Qué hay de Netcat o Nmap ?

    Supongo que es demasiado tarde para una respuesta, y esta podría no ser una buena, pero aquí tienes …

    ¿Qué hay de ponerlo dentro de un bucle while con un temporizador de algún tipo? Soy más un tipo Perl que Solaris, pero dependiendo del shell que estés usando, deberías ser capaz de hacer algo como:

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

    Y luego solo agregue una bandera en el ciclo while, de modo que si se agota el tiempo de espera antes de completarla, puede citar el tiempo de espera como motivo de falla.

    Sospecho que el telnet también tiene un interruptor de tiempo de espera, pero por encima de todo, creo que lo anterior funcionará.

    Necesitaba una secuencia de comandos corta que se ejecutó en cron y no se imprimió. Resuelvo mi problema 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 

    Para ejecutarlo Debe instalar nmap porque no es un paquete instalado por defecto.

    Esto utiliza telnet detrás de escena, y parece funcionar bien en mac / linux. No utiliza netcat debido a las diferencias entre las versiones en linux / mac, y esto funciona con una instalación predeterminada de mac.

    Ejemplo:

     $ 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 

    verificar puertos usando bash

    Ejemplo

     $ ./test_port_bash.sh 192.168.7.7 22 

    el puerto 22 está abierto

    Código

     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 

    Basándose en la respuesta más votada, aquí hay una función para esperar a que se abran dos puertos, con un tiempo de espera también. Tenga en cuenta los dos puertos que deben estar abiertos, 8890 y 1111, así como los max_attempts (1 por segundo).

     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 para probar el puerto local que aún no está en uso


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

    La respuesta mintió con Esperar. Escribimos un script simple que envía un telnet al puerto que necesitábamos, con un tiempo de espera de 8 segundos. Hay muchos ejemplos para elegir también.

    Basamos el nuestro en esta publicación: http://www.unix.com/shell-programming-scripting/146568-expect-telnet-testing-tacacs-cisco.html