Testen Sie, ob der Remote-TCP-Port über ein Shell-Skript geöffnet ist

297

Ich suche nach einer schnellen und einfachen Methode, um ordnungsgemäß zu testen, ob ein bestimmter TCP-Port auf einem Remote-Server in einem Shell-Skript geöffnet ist.

Ich habe es geschafft, es mit dem Telnet-Befehl zu tun, und es funktioniert gut, wenn der Port geöffnet wird, aber es scheint keine Zeitüberschreitung zu geben, wenn es nicht ist und hängt einfach dort ...

Hier ist ein Beispiel:

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

Ich brauche entweder einen besseren Weg oder einen Weg, um Telnet zum Timeout zu zwingen, wenn es keine Verbindung in weniger als 8 Sekunden herstellt, und etwas zurückzugeben, das ich in Shell abfangen kann (Rückkehrcode oder String in stdout).

Ich kenne die Perl-Methode, die das IO :: Socket :: INET-Modul verwendet und ein erfolgreiches Skript geschrieben hat, das einen Port testet, möchte aber die Verwendung von Perl möglichst vermeiden.

Hinweis: Dies ist, was mein Server ausführt (wo ich dies ausführen muss)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc

Yanick Girouard
quelle
Was ist mit Netcat oder Nmap ?
Jeremiah Willcock
Die Antwort lag bei Expect. Wir haben ein einfaches Skript geschrieben, das ein Telnet an den von uns benötigten Port mit einer Zeitüberschreitung von 8 Sekunden sendet. Es gibt auch viele Beispiele zur Auswahl. Wir haben uns auf diesen Beitrag gestützt
Yanick Girouard
1
check_tcp von github.com/monitoring-plugins/monitoring-plugins kann dies tun, einschließlich der Eingabe von Zeichenfolgen und der Suche nach einer erwarteten Antwort.

Antworten:

452

Wie von B. Rhodes gezeigt, ncwird die Arbeit erledigen. Eine kompaktere Art, es zu verwenden:

nc -z <host> <port>

Auf diese Weise ncwird nur überprüft, ob der Port geöffnet ist, und bei Erfolg mit 0 und bei Fehler mit 1 beendet.

Für eine schnelle interaktive Überprüfung (mit einer Zeitüberschreitung von 5 Sekunden):

nc -z -v -w5 <host> <port>
Alessio Gaeta
quelle
48
centos7 verwendet standardmäßig nmap netcat und hat nicht die Option -z.
Jolestar
7
Dies funktioniert nicht in RHEL / Centos. Für diese Distributionen müssen Sie: nc -vn <Host> <Port>
Justin Dennahower
2
FWIW, ich habe meine Antwort mit einem Beispiel komplett überarbeitet , das sowohl für RHEL 6 als auch für RHEL 7 separat gilt.
Acumenus
4
Zumindest auf dem Mac müssen Sie möglicherweise hinzufügen -G#, um ein Verbindungszeitlimit unabhängig von / zusätzlich zum -w#Zeitlimit festzulegen, das im Grunde genommen als Lesezeitlimit fungiert.
Doktor J
1
@jolestar Sie können Ncat unter Centos 7 manuell aktualisieren, um die -zOption zu erhalten. Vielleicht möchten Sie Folgendes
Damien Ó Ceallaigh
150

Es ist einfach genug, mit den -zund -w TIMEOUTOptionen zu arbeiten nc, aber nicht alle Systeme haben ncinstalliert. Wenn Sie eine ausreichend aktuelle Version von Bash haben, funktioniert dies:

# 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

Was hier passiert, ist, dass timeoutder Unterbefehl ausgeführt und beendet wird, wenn er nicht innerhalb des angegebenen Zeitlimits beendet wird (1 Sekunde im obigen Beispiel). In diesem Fall bashhandelt es sich um den Unterbefehl, der mithilfe seiner speziellen Behandlung / dev / tcp versucht, eine Verbindung zu dem angegebenen Server und Port herzustellen. Wenn bashdie Verbindung innerhalb des Timeouts geöffnet werden kann, catwird sie sofort geschlossen (da sie von liest /dev/null) und mit einem Statuscode beendet, der 0sich durch bashund dann ausbreitet timeout. Wenn bashvor dem angegebenen Zeitlimit ein Verbindungsfehler auftritt, bashwird der Exit mit dem Exit-Code 1 beendet, der timeoutebenfalls zurückgegeben wird. Und wenn bash keine Verbindung herstellen kann und das angegebene Zeitlimit abläuft, danntimeoutwird töten bashund mit einem Status von 124 beenden.

nur keiner
quelle
1
Ist /dev/tcpauf anderen Systemen als Linux verfügbar? Was ist insbesondere mit Macs?
Peter
8
Das / dev / tcp-Ding ist ein Feature von bash, also ja. Es sieht jedoch so aus, als hätten Macs keine Zeitüberschreitung ...
nur
1
@onlynone - Sieht so aus, als ob es timeoutauch in FreeBSD nicht existiert, und auf meiner alten Ubuntu-Box ist es ein installierbares Paket, aber standardmäßig nicht vorhanden. Wäre großartig, wenn es eine Möglichkeit gäbe, dies allein in Bash zu tun, ohne Tools von Drittanbietern wie Timeout oder Netcat.
Graham
3
Ich wollte nur erwähnen, dass timeoutdies Teil von GNU Coreutils zu sein scheint und auf Macs mit Homebrew installiert werden kann : brew install coreutils. Es wird dann als verfügbar sein gtimeout.
Nur am
1
@Wildcard Ein Bash- Änderungsprotokoll schlägt vor bash-2.04-devel, obwohl möglicherweise Unterstützung für Hostnamen hinzugefügt wurde bash-2.05-alpha1.
Acumenus
109

Inhaltsverzeichnis:

  • Mit Bash und timeout
    • Befehl
    • Beispiele
  • Verwenden von nc
    • Befehl
    • RHEL 6 (nc-1,84)
      • Installation
      • Beispiele
    • RHEL 7 (nmap-ncat-6.40)
      • Installation
      • Beispiele
  • Bemerkungen

Mit Bash und timeout:

Beachten Sie, dass timeoutRHEL 6+ vorhanden sein sollte oder alternativ in GNU Coreutils 8.22 enthalten ist. Installieren Sie es unter MacOS mit brew install coreutilsund verwenden Sie es als gtimeout.

Befehl:

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

Wenn der Host und Port Parametrisierung, sollten Sie sie angeben , wie ${HOST}und ${PORT}wie oben ist. Geben Sie sie nicht nur als $HOSTund an $PORT, dh ohne die geschweiften Klammern. In diesem Fall funktioniert es nicht.

Beispiel:

Erfolg:

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

Fehler:

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

Wenn Sie den Exit-Status von beibehalten müssen bash,

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

Verwenden von nc:

Beachten Sie, dass eine abwärts inkompatible Version von ncauf RHEL 7 installiert wird.

Befehl:

Beachten Sie, dass der folgende Befehl insofern einzigartig ist, als er für RHEL 6 und 7 identisch ist. Nur die Installation und die Ausgabe unterscheiden sich.

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

RHEL 6 (nc-1,84):

Installation:

$ sudo yum install nc

Beispiele:

Erfolg:
$ nc -w 2 -v canyouseeme.org 80 </dev/null; echo $?
Connection to canyouseeme.org 80 port [tcp/http] succeeded!
0
Fehler:
$ 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

Wenn der Hostname mehreren IPs zugeordnet ist, durchläuft der obige fehlgeschlagene Befehl viele oder alle. Zum Beispiel:

$ 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):

Installation:

$ sudo yum install nmap-ncat

Beispiele:

Erfolg:
$ 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
Fehler:
$ nc -w 2 -v canyouseeme.org 81 </dev/null; echo $?
Ncat: Version 6.40 ( http://nmap.org/ncat )
Ncat: Connection timed out.
1

Wenn der Hostname mehreren IPs zugeordnet ist, durchläuft der obige fehlgeschlagene Befehl viele oder alle. Zum Beispiel:

$ 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

Bemerkungen:

Das Argument -v( --verbose) und der echo $?Befehl dienen natürlich nur zur Veranschaulichung.

Scharfsinn
quelle
3
timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?ist ein toller Punkt!
ipeacocks
Hinzufügen 2>&1zu nicht Echo Status result_test=$(nc -w 2 $ip_addreess 80 </dev/null 2>&1 ; echo $?)
Sanya Snex
55

Mit können netcatSie überprüfen, ob ein Port wie folgt geöffnet ist:

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

Der Rückgabewert von ncist erfolgreich, wenn der TCP-Port geöffnet wurde, und ein Fehler (normalerweise der Rückkehrcode 1), wenn die TCP-Verbindung nicht hergestellt werden konnte.

Einige Versionen von nchängen, wenn Sie dies versuchen, da sie die sendende Hälfte ihres Sockets auch nach dem Empfang des Dateiende von nicht schließen /dev/null. Auf meinem eigenen Ubuntu-Laptop (18.04) bietet die netcat-openbsdvon mir installierte Version von netcat eine Problemumgehung: Die -NOption ist erforderlich, um ein sofortiges Ergebnis zu erzielen:

nc -N my.example.com 80 < /dev/null
Brandon Rhodes
quelle
1
Funktioniert hervorragend für Aromen nc, die die -wFlagge nicht unterstützen .
David Dossot
5
danke - funktioniert auch für Versionen von nc ohne -zUnterstützung - funktioniert zum Beispiel unter RHEL / CentOS 7
Andrew
1
Obwohl dieser Ansatz gut ist, -wist er erforderlich. Andernfalls kann der Befehl bei Verwendung in einem Skript länger als zehn Sekunden hängen bleiben. Ich habe eine Antwort geschrieben , die enthält -w.
Acumenus
2
+1 das ist großartig, da nc standardmäßig mit alpine linux und ubuntu geliefert wird. wahrscheinlich andere. Sie müssen keine zusätzliche Installation durchführen, wenn Sie remote sind und dies nicht können. Danke dafür! Ich könnte hinzufügen,nc host port -w 2 && echo it works
std''OrgnlDave
2
Ich bevorzuge nc -vz localhost 3306. Es gibt eine ausführlichere Ausgabe. Versuchte es nur auf dem Mac.
EFreak
29

In Bash ist die Verwendung von Pseudogerätedateien für TCP / UDP-Verbindungen unkompliziert. Hier ist das Skript:

#!/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

Testen:

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

Hier ist ein Einzeiler (Bash-Syntax):

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

Beachten Sie, dass einige Server durch eine Firewall vor SYN-Flood-Angriffen geschützt werden können, sodass möglicherweise ein TCP-Verbindungszeitlimit (~ 75 Sekunden) auftritt. Versuchen Sie Folgendes, um das Timeout-Problem zu umgehen:

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

Siehe: Wie kann das Zeitlimit für TCP connect () -Systemaufrufe verringert werden?

Kenorb
quelle
1
@ABB Dies hängt mit der Firewall-Konfiguration des Servers zusammen, die keine Antwort gibt, um SYN-Flood-Angriffe zu verhindern. Laufen telnet canyouseeme.org 81hängt auch. Dies wird durch Ihre Timeout-Limits gesteuert, die wahrscheinlich in Bash fest codiert sind. Siehe: Zeitlimit für TCP connect () -Systemaufrufe verringern .
Kenorb
Für Parametrisierung, so scheint es nun notwendig zu sein , um anzugeben , $SERVERund $PORTmit Klammern, zumindest ${SERVER}und ${PORT}.
Acumenus
13

Ich brauchte eine flexiblere Lösung für die Arbeit an mehreren Git-Repositorys, also schrieb ich den folgenden Sh-Code basierend auf 1 und 2 . Sie können Ihre Serveradresse anstelle von gitlab.com und Ihren Port anstelle von 22 verwenden.

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
Ahmad Yoosofan
quelle
1
Vielen Dank! Es macht mein Leben für /etc/rc.local
Shahin Khaled
10

Wenn Sie ksh oder bash verwenden , unterstützen beide die E / A-Umleitung zu / von einem Socket mithilfe des Konstrukts / dev / tcp / IP / PORT . In dieser Korn - Shell Beispiel ich Umleiten no-op ( : ) STD-in aus der Steckdose:

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

Die Shell gibt einen Fehler aus, wenn der Socket nicht geöffnet ist:

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

Sie können dies daher als Test in einer if- Bedingung verwenden:

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

Das No-Op befindet sich in einer Subshell, sodass ich std-err wegwerfen kann, wenn die std-in-Umleitung fehlschlägt.

Ich benutze oft / dev / tcp, um die Verfügbarkeit einer Ressource über HTTP zu überprüfen:

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$ 

Dieser Einzeiler öffnet den Dateideskriptor 9 zum Lesen und Schreiben in den Socket, druckt das HTTP-GET in den Socket und verwendet catzum Lesen aus dem Socket.

merebagatelle
quelle
1
@ABB Ich denke du meinst, es hängt für eine lange Zeit, wenn der Port nicht reagiert, wie zum Beispiel wenn er gefiltert wird oder nichts auf der IP ist. Ein geschlossener Port verursacht keine Verzögerung, wenn der Port die Verbindung aktiv ablehnt. Für viele Zwecke ist dies durchaus akzeptabel.
mc0e
Diese Technik wurde bereits vor dieser Antwort in einer vorherigen Antwort von Kenorb veröffentlicht . Der größere Punkt ist jedoch, dass es jedoch lange hängt, wenn der Port nicht reagiert. Versuchen Sie es zum Beispiel </dev/tcp/canyouseeme.org/80und dann </dev/tcp/canyouseeme.org/81.
Acumenus
9

Während eine alte Frage, habe ich mich gerade mit einer Variante davon befasst, aber keine der Lösungen hier war anwendbar, also habe ich eine andere gefunden und füge sie für die Nachwelt hinzu. Ja, ich weiß, dass das OP sagte, dass sie sich dieser Option bewusst waren und sie nicht zu ihnen passte, aber für jeden, der danach folgt, könnte sie sich als nützlich erweisen.

In meinem Fall möchte ich die Verfügbarkeit eines lokalen apt-cacher-ngDienstes aus einem dockerBuild testen . Das heißt, vor dem Test kann absolut nichts installiert werden. Nein nc, nmap, expect, telnetoder python. perlist jedoch zusammen mit den Kernbibliotheken vorhanden, daher habe ich Folgendes verwendet:

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

In einigen Fällen, in denen Tools wie Curl, Telnet, NCONMAP nicht verfügbar sind, haben Sie mit wget immer noch eine Chance

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

Überprüfen Sie die Ports mit Bash

Beispiel

$ ./test_port_bash.sh 192.168.7.7 22

Der Port 22 ist offen

Code

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
krbu
quelle
4

Wenn Sie nceine Version verwenden möchten, die diese unterstützt -z, versuchen Sie Folgendes --send-only:

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

und mit Timeout:

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

und ohne DNS-Suche, wenn es sich um eine IP handelt:

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

Es gibt die Codes als -zbasierend darauf zurück, ob eine Verbindung hergestellt werden kann oder nicht.

Chris Mendez
quelle
1

Ich vermute, dass es für eine Antwort zu spät ist, und dies ist vielleicht keine gute, aber los geht's ...

Wie wäre es, wenn Sie es in eine while-Schleife mit einem Timer stecken? Ich bin eher ein Perl-Typ als Solaris, aber abhängig von der verwendeten Shell sollten Sie in der Lage sein, Folgendes zu tun:

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

Fügen Sie dann einfach ein Flag in die while-Schleife ein, damit Sie das Zeitlimit als Grund für den Fehler angeben können, wenn das Zeitlimit vor dem Abschluss abgelaufen ist.

Ich vermute, dass das Telnet auch einen Timeout-Schalter hat, aber ich denke, dass das oben genannte funktionieren wird.

Roter Thomas
quelle
1

Ich brauchte ein kurzes Skript, das in cron ausgeführt wurde und nicht ausgegeben wurde. Ich löse meine Probleme mit 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

So führen Sie es aus Sie sollten nmap installieren, da es sich nicht um ein standardmäßig installiertes Paket handelt.

Krystian Kulasza
quelle
nmapDie grepable Ausgabe kann auch nützlich sein -oG -, um sie an stdout zu senden. (Der Grep müsste ein wenig modifiziert werden)
Gert van den Berg
0

Dies verwendet Telnet hinter den Kulissen und scheint unter Mac / Linux gut zu funktionieren. Netcat wird aufgrund der Unterschiede zwischen den Versionen unter Linux / Mac nicht verwendet, und dies funktioniert mit einer Standard-Mac-Installation.

Beispiel:

$ 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 
Brad Parks
quelle
0

Aufbauend auf der am höchsten bewerteten Antwort können Sie hier warten, bis zwei Ports geöffnet sind, mit einer Zeitüberschreitung. Beachten Sie die beiden Ports, die geöffnet sein müssen, 8890 und 1111, sowie die max_attempts (1 pro Sekunde).

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
}
João Rocha da Silva
quelle
-1

nmap-ncat zum Testen auf einen lokalen Port, der noch nicht verwendet wird


availabletobindon() {
  port="$1"
  nc -w 2 -i 1 localhost "$port" 2>&1 | grep -v -q 'Idle timeout expired'
  return "$?"
}
isleofgray
quelle
Es sagt mir nicht, dass mein Port 80 offen ist.
Totoro