Je recherche une méthode simple et rapide pour tester correctement si un port donné TCP est ouvert sur un serveur distant, à partir d'un script Shell.
J'ai réussi à le faire avec la commande telnet, et cela fonctionne bien lorsque le port est ouvert, mais il ne semble pas expirer quand il ne l'est pas et se bloque juste là ...
Voici un exemple:
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
J'ai besoin d'un meilleur moyen ou d'un moyen de forcer le délai d'expiration de telnet s'il ne se connecte pas en moins de 8 secondes, par exemple, et de renvoyer quelque chose que je peux attraper dans Shell (code de retour ou chaîne in stdout).
Je connais la méthode Perl, qui utilise le module IO :: Socket :: INET et a écrit un script réussi qui teste un port, mais préfère plutôt éviter d'utiliser Perl si possible.
Remarque: Voici ce que mon serveur est en cours d'exécution (où je dois l'exécuter à partir de)
SunOS 5.10 Generic_139556-08 i86pc i386 i86pc
Comme l'a souligné B. Rhodes, nc
fera le travail. Une façon plus compacte de l'utiliser:
nc -z <Host> <port>
De cette façon, nc
ne vérifiera que si le port est ouvert, quitte avec 0 en cas de succès, 1 en cas d'échec.
Pour un contrôle interactif rapide (avec un délai d'attente de 5 secondes):
nc -z -v -w5 <Host> <port>
C'est assez facile à faire avec les options _-z
_ et _-w TIMEOUT
_ sur nc
, mais tous les systèmes n'ont pas nc
installé. Si vous avez une version assez récente de bash, cela fonctionnera:
_# 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
_
Ce qui se passe ici, c'est que timeout
exécutera la sous-commande et la tuera si elle ne se ferme pas dans le délai imparti (1 seconde dans l'exemple ci-dessus). Dans ce cas, bash
est la sous-commande et utilise sa commande spéciale / dev/tcp pour essayer d'ouvrir une connexion au serveur et au port spécifiés. Si bash
peut ouvrir la connexion dans le délai imparti, cat
la fermera immédiatement (car il lit _/dev/null
_) et se termine avec un code d’état de _0
_ qui se propage. à travers bash
puis timeout
. Si bash
obtient un échec de connexion avant le délai spécifié, alors bash
sortira avec un code de sortie de 1 que timeout
retournera également. Et si bash ne parvient pas à établir une connexion et que le délai spécifié expire, alors timeout
va tuer bash
et quitter avec un statut de 124.
timeout
nc
timeout
:Notez que timeout
devrait être présent avec RHEL 6+, ou se trouve également dans GNU coreutils 8.22. Sous MacOS, installez-le avec brew install coreutils
et utilisez-le comme gtimeout
.
$ timeout $TIMEOUT_SECONDS bash -c "</dev/tcp/${Host}/${PORT}"; echo $?
Si vous paramétrez l'hôte et le port, veillez à les spécifier comme suit: ${Host}
et ${PORT}
comme ci-dessus. Ne les spécifiez pas simplement comme $Host
et $PORT
, c'est-à-dire sans les accolades; cela ne fonctionnera pas dans ce cas.
$ timeout 2 bash -c "</dev/tcp/canyouseeme.org/80"; echo $?
0
$ timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?
124
Si vous devez conserver le statut de sortie de bash
,
$ timeout --preserve-status 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?
143
nc
:Notez qu'une version incompatible avec les versions antérieures de nc
est installée sur RHEL 7.
Notez que la commande ci-dessous est unique en ce sens qu'elle est identique pour RHEL 6 et 7. Seules l'installation et la sortie sont différentes.
$ nc -w $TIMEOUT_SECONDS -v $Host $PORT </dev/null; echo $?
$ Sudo yum install nc
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Connection to canyouseeme.org 80 port [tcp/http] succeeded!
0
$ 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
Si le nom d'hôte correspond à plusieurs adresses IP, la commande défaillante ci-dessus parcourt la totalité ou la totalité d'entre elles. Par exemple:
$ 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
$ Sudo yum install nmap-ncat
$ 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
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection timed out.
1
Si le nom d'hôte correspond à plusieurs adresses IP, la commande défaillante ci-dessus parcourt la totalité ou la totalité d'entre elles. Par exemple:
$ 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
L'argument -v
(--verbose
) et la commande echo $?
ne sont bien sûr qu'illustrés.
Avec netcat
, vous pouvez vérifier si un port est ouvert comme ceci:
nc my.example.com 80 < /dev/null
La valeur de retour de nc
sera réussie si le port TCP est ouvert et échoue (généralement le code de retour 1) s'il ne peut pas établir la connexion TCP.
Certaines versions de nc
seront bloquées lorsque vous tenterez ceci, car elles ne fermeront pas la moitié d'envoi de leur socket, même après avoir reçu la fin du fichier de /dev/null
. Sur mon propre ordinateur portable Ubuntu (18.04), la version netcat-openbsd
de netcat que j'ai installée offre une solution de contournement: l'option -N
est nécessaire pour obtenir un résultat immédiat:
nc -N my.example.com 80 < /dev/null
Dans Bash, l'utilisation de fichiers de pseudo-périphériques pour les connexions TCP/UDP est simple. Voici le script:
#!/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
Essai:
$ ./test.sh
Connection to example.com on port 80 succeeded
Voici one-liner (syntaxe Bash):
</dev/tcp/localhost/11211 && echo Port open. || echo Port closed.
Notez que certains serveurs peuvent être protégés par un pare-feu contre les attaques par saturation SYN. Vous risquez donc de rencontrer un délai de connexion TCP (environ 75 secondes). Pour contourner le problème de délai d'attente, essayez:
timeout 1 bash -c "</dev/tcp/stackoverflow.com/81" && echo Port open. || echo Port closed.
Voir: Comment diminuer TCP connect () délai d’appel système?
J'avais besoin d'une solution plus flexible pour travailler sur plusieurs référentiels git. J'ai donc écrit le code sh suivant basé sur 1 et 2 . Vous pouvez utiliser votre adresse de serveur au lieu de gitlab.com et votre port au lieu 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
Si vous utilisez ksh ou bash, ils prennent tous les deux en charge la redirection IO vers/depuis un socket à l'aide de la commande / dev/tcp/IP/PORT construire. Dans cet exemple Korn Shell, je redirige le std-in (:) no-op depuis un socket:
W$ python -m SimpleHTTPServer &
[1] 16833
Serving HTTP on 0.0.0.0 port 8000 ...
W$ : </dev/tcp/127.0.0.1/8000
Le shell imprime une erreur si le socket n’est pas ouvert:
W$ : </dev/tcp/127.0.0.1/8001
ksh: /dev/tcp/127.0.0.1/8001: cannot open [Connection refused]
Vous pouvez donc l'utiliser comme test dans une condition if:
SERVER=127.0.0.1 PORT=8000
if (: < /dev/tcp/$SERVER/$PORT) 2>/dev/null
then
print succeeded
else
print failed
fi
Le no-op est dans un sous-shell afin que je puisse jeter std-err si la redirection std-in échoue.
J'utilise souvent / dev/tcp pour vérifier la disponibilité d'une ressource sur 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$
Cette ligne s'ouvre descripteur de fichier 9 pour lire et écrire dans le socket, affiche le HTTP GET dans le socket et utilise cat
pour lire de la prise.
Bien qu’il s’agisse d’une vieille question, j’ai abordé une variante de celle-ci, mais aucune des solutions ici n’était applicable, j’en ai donc trouvé une autre et je l’ajoute pour la postérité. Oui, je sais que le PO a dit qu'il était au courant de cette option et que cela ne lui convenait pas, mais cela pourrait s'avérer utile pour les personnes suivantes.
Dans mon cas, je veux tester la disponibilité d'un service apt-cacher-ng
local à partir d'une version docker
. Cela signifie que rien ne peut être installé avant le test. Non nc
, nmap
, expect
, telnet
ou python
. Perl
cependant est présent, avec les librairies principales, donc j'ai utilisé:
Perl -MIO::Socket::INET -e 'exit(! defined( IO::Socket::INET->new("172.17.42.1:3142")))'
Dans certains cas où des outils tels que curl, telnet, nc o nmap sont indisponibles, vous avez encore une chance avec 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
$ ./test_port_bash.sh 192.168.7.7 22
le port 22 est ouvert
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
Si vous souhaitez utiliser nc
mais que vous n'avez pas de version prenant en charge -z
, essayez d'utiliser --send-only
:
nc --send-only <IP> <PORT> </dev/null
et avec timeout:
nc -w 1 --send-only <IP> <PORT> </dev/null
et sans recherche DNS s'il s'agit d'une adresse IP:
nc -n -w 1 --send-only <IP> <PORT> </dev/null
Il renvoie les codes sous forme de -z
selon qu'il peut ou non se connecter.
J'imagine qu'il est trop tard pour une réponse et que ce n'est peut-être pas une bonne réponse, mais voilà ...
Qu'en est-il de le mettre dans une boucle while avec une minuterie dessus? Je suis plus un gars de Perl que Solaris, mais selon le shell que vous utilisez, vous devriez pouvoir faire quelque chose comme:
TIME = 'date +%s' + 15
while TIME != `date +%s'
do whatever
Ajoutez ensuite un indicateur dans la boucle while afin que, si le délai expire avant la fin, vous pouvez indiquer le délai comme motif d'échec.
Je soupçonne que le telnet a également un commutateur de délai d’attente, mais je pense que ce qui précède va fonctionner.
J'avais besoin d'un court script qui a été exécuté dans cron et qui n'a pas été généré. Je résous mes problèmes en utilisant 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
Pour l'exécuter, vous devez installer nmap car ce n'est pas le paquet installé par défaut.
Cela utilise telnet dans les coulisses et semble fonctionner correctement sous mac/linux. Il n'utilise pas netcat en raison des différences entre les versions de linux/mac, et cela fonctionne avec une installation mac par défaut.
$ is_port_open.sh 80 google.com
OPEN
$ is_port_open.sh 8080 google.com
CLOSED
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
Sur la base de la réponse la plus votée, voici une fonction qui permet d’attendre que deux ports soient ouverts, avec également un délai d’attente. Notez les deux ports qui doivent être ouverts, 8890 et 1111, ainsi que le max_attempts (1 par seconde).
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
}