Sprawdź, czy zdalny port TCP jest otwarty ze skryptu powłoki

297

Szukam szybkiej i prostej metody prawidłowego testowania, czy dany port TCP jest otwarty na zdalnym serwerze z poziomu skryptu Shell.

Udało mi się to zrobić za pomocą polecenia telnet i działa dobrze, gdy port jest otwarty, ale wydaje się, że nie upłynął limit czasu, gdy nie jest i po prostu się tam zawiesza ...

Oto próbka:

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

Potrzebuję lepszego sposobu lub zmusić telnet do przekroczenia limitu czasu, jeśli nie połączy się on na przykład w czasie krótszym niż 8 sekund, i zwrócę coś, co mogę złapać w powłoce (kod powrotu lub ciąg znaków na standardowe wyjście).

Znam metodę Perla, która wykorzystuje moduł IO :: Socket :: INET i napisała udany skrypt, który testuje port, ale wolałby unikać używania Perla, jeśli to możliwe.

Uwaga: z tego działa mój serwer (z którego muszę to uruchomić)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc

Yanick Girouard
źródło
Co z Netcat lub Nmap ?
Jeremiah Willcock
Odpowiedź leżała w oczekiwaniu. Napisaliśmy prosty skrypt, który wysyła telnet na potrzebny port z limitem czasu wynoszącym 8 sekund. Istnieje również wiele przykładów do wyboru. Oparliśmy
Yanick Girouard,
1
check_tcp z github.com/monitoring-plugins/monitoring-plugins może to zrobić, włączając wprowadzanie ciągów i sprawdzanie oczekiwanej odpowiedzi.

Odpowiedzi:

452

Jak wskazał B. Rhodes, ncwykona zadanie. Bardziej kompaktowy sposób użycia:

nc -z <host> <port>

W ten sposób ncsprawdzi tylko, czy port jest otwarty, wychodząc z 0 w przypadku sukcesu, 1 w przypadku niepowodzenia.

Aby szybko sprawdzić interaktywnie (z 5-sekundowym limitem czasu):

nc -z -v -w5 <host> <port>
Alessio Gaeta
źródło
48
Domyślnie centos7 używa nmap netcat i nie ma opcji -z.
jolestar
7
To nie działa w RHEL / Centos. W przypadku tych dystrybucji musisz: nc -vn <host> <port>
Justin Dennahower
2
FWIW, całkowicie zrewidowałem moją odpowiedź przykładem , osobno odnoszącym się zarówno do RHEL 6, jak i RHEL 7.
Acumenus,
4
przynajmniej na Macu może być konieczne dodanie, -G#aby ustawić limit czasu połączenia oddzielnie od limitu / oprócz -w#limitu czasu, który zasadniczo działa jako limit czasu odczytu.
Doktor J
1
@jolestar Możesz ręcznie zaktualizować Ncat na Centos 7, aby uzyskać tę -zopcję. Możesz rozważyć: unix.stackexchange.com/questions/393762/...
Damien Ó Ceallaigh,
150

Łatwo jest zrobić z opcjami -zi , ale nie wszystkie systemy mają zainstalowane. Jeśli masz wystarczająco najnowszą wersję bash, zadziała to:-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

To, co się tutaj dzieje, timeouturuchomi podkomendę i zabije ją, jeśli nie zakończy działania w określonym czasie (1 sekunda w powyższym przykładzie). W tym przypadku bashjest podkomenda i używa specjalnej obsługi / dev / tcp, aby spróbować otworzyć połączenie z serwerem i określonym portem. Jeśli bashmożna otworzyć połączenie w ramach limitu czasu, catpo prostu zamknie je natychmiast (ponieważ odczytuje z /dev/null) i zakończy działanie z kodem statusu, 0który będzie propagowany przez, basha następnie timeout. Jeśli bashotrzyma awarię połączenia przed upływem określonego limitu czasu,bash nastąpi zakończenie z kodem wyjścia 1, który timeoutrównież zwróci. A jeśli bash nie będzie w stanie nawiązać połączenia i upłynie określony limit czasu, totimeoutzabije bashi wyjdzie ze statusem 124.

tylko nikt
źródło
1
Czy jest /dev/tcpdostępny na systemach innych niż Linux? Co szczególnie z komputerami Mac?
Peter
8
Opcja / dev / tcp jest cechą bash, więc tak. Wygląda jednak na to, że
Mac
1
@onlynone - Wygląda na to, że timeoutrównież nie istnieje we FreeBSD, a na moim starym Ubuntu jest to pakiet do zainstalowania, ale domyślnie go nie ma. Byłoby wspaniale, gdyby istniał sposób na zrobienie tego sam w bash, bez narzędzi innych firm, takich jak timeout czy netcat.
Graham
3
Po prostu chciałem wspomnieć, że timeoutwydaje się być częścią coreutils GNU i mogą być instalowane na komputerach Mac z homebrew: brew install coreutils. Będzie wtedy dostępny jako gtimeout.
tylko
1
@Wildcard Sugeruje dziennik zmian bash bash-2.04-devel, chociaż może zostać dodana obsługa nazw hostów bash-2.05-alpha1.
Acumenus,
109

Spis treści:

  • Korzystanie z bash i timeout
    • Komenda
    • Przykłady
  • Za pomocą nc
    • Komenda
    • RHEL 6 (nc-1.84)
      • Instalacja
      • Przykłady
    • RHEL 7 (nmap-ncat-6.40)
      • Instalacja
      • Przykłady
  • Uwagi

Używanie bash i timeout:

Zauważ, że timeoutpowinien być obecny z RHEL 6+ lub alternatywnie można go znaleźć w jądrach GNU 8.22. W systemie MacOS zainstaluj go, używając brew install coreutilsi używaj go jako gtimeout.

Komenda:

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

Jeśli parametryzujesz host i port, pamiętaj, aby podać je tak jak ${HOST}i ${PORT}powyżej. Nie określaj ich jedynie jako $HOSTi $PORT, tj. Bez nawiasów klamrowych; w tym przypadku nie zadziała.

Przykład:

Sukces:

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

Niepowodzenie:

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

Jeśli musisz zachować status wyjścia bash,

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

Używanie nc:

Zauważ, że wsteczna niekompatybilna wersja ncinstalowana jest na RHEL 7.

Komenda:

Zauważ, że poniższe polecenie jest unikalne, ponieważ jest identyczne zarówno dla RHEL 6, jak i 7. To tylko instalacja i wyjście są różne.

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

RHEL 6 (nc-1.84):

Instalacja:

$ sudo yum install nc

Przykłady:

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

Jeśli nazwa hosta zostanie odwzorowana na wiele adresów IP, powyższe nieudane polecenie przejdzie przez wiele lub wszystkie z nich. Na przykład:

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

Instalacja:

$ sudo yum install nmap-ncat

Przykłady:

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

Jeśli nazwa hosta zostanie odwzorowana na wiele adresów IP, powyższe nieudane polecenie przejdzie przez wiele lub wszystkie z nich. Na przykład:

$ 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

Uwagi:

Argument -v( --verbose) i echo $?polecenie służą oczywiście wyłącznie do ilustracji.

Acumenus
źródło
3
timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?jest świetny punkt!
ipeacocks 18.04.17
dodaj 2>&1do statusu „brak echa” result_test=$(nc -w 2 $ip_addreess 80 </dev/null 2>&1 ; echo $?)
Sanya Snex,
55

Za pomocą netcatmożesz sprawdzić, czy port jest otwarty w ten sposób:

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

Zwracana wartość ncto sukces, jeśli port TCP został otwarty, a awaria (zazwyczaj kod powrotu 1), jeśli nie może nawiązać połączenia TCP.

Niektóre wersje ncbędą się zawieszać, gdy spróbujesz tego, ponieważ nie zamykają wysyłającej połowy swojego gniazda nawet po otrzymaniu od końca pliku /dev/null. Na moim laptopie Ubuntu (18.04) zainstalowana netcat-openbsdwersja programu netcat oferuje obejście: -Nopcja jest niezbędna, aby uzyskać natychmiastowy wynik:

nc -N my.example.com 80 < /dev/null
Brandon Rhodes
źródło
1
Działa świetnie dla smaków nc, które nie obsługują -wflagi.
David Dossot,
5
dzięki - działa również na wersje nc bez -zwsparcia - działa na RHEL / CentOS 7, na przykład
Andrew
1
Chociaż takie podejście jest dobre, -wjest konieczne, ponieważ w przeciwnym razie polecenie użyte w skrypcie może zawiesić się na ponad dziesięć sekund. Napisałem odpowiedź, która zawiera -w.
Acumenus,
2
+1 to jest świetne, ponieważ nc jest standardowo wyposażony w alpine linux i ubuntu. prawdopodobnie inni. nie ma potrzeby instalowania dodatkowych, gdy jesteś zdalny i nie możesz. dzięki za to! Mógłbym dodaćnc host port -w 2 && echo it works
std''OrgnlDave
2
Wolę nc -vz localhost 3306. Daje bardziej pełny wynik. Wypróbowałem to tylko na komputerze Mac.
EFreak
29

W Bash korzystanie z plików pseudo-urządzeń dla połączeń TCP / UDP jest proste. Oto skrypt:

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

Testowanie:

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

Oto jednowierszowa (składnia Bash):

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

Należy pamiętać, że niektóre serwery mogą być chronione zaporą ogniową przed atakami SYN flood, dlatego może wystąpić limit czasu połączenia TCP (~ 75 sekund). Aby obejść problem z przekroczeniem limitu czasu, spróbuj:

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

Zobacz: Jak skrócić limit czasu połączenia systemowego TCP connect ()?

kenorb
źródło
1
@ABB Jest to związane z konfiguracją zapory ogniowej na serwerze, która nie daje żadnej odpowiedzi, aby zapobiec atakom powodziowym SYN. Bieganie telnet canyouseeme.org 81również się zawiesza. Jest to kontrolowane przez limity czasu, które prawdopodobnie są zakodowane na sztywno. Zobacz: zmniejsz limit czasu połączenia systemowego TCP connect () .
kenorb
Do parametryzacji wydaje się teraz konieczne określenie $SERVERi $PORTnawiasy klamrowe, przynajmniej jako ${SERVER}i ${PORT}.
Acumenus,
13

Potrzebowałem bardziej elastycznego rozwiązania do pracy z wieloma repozytoriami git, więc napisałem następujący kod sh na podstawie 1 i 2 . Możesz użyć adresu serwera zamiast gitlab.com i swojego portu zamiast 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
Ahmad Yoosofan
źródło
1
Dzięki! Ułatwia moje życie dla /etc/rc.local
Shahin Khaled
10

Jeśli używasz ksh lub bash , oba obsługują przekierowanie IO do / z gniazda za pomocą konstrukcji / dev / tcp / IP / PORT . W tym Korn przykład ja przekierowanie nie-OP ( : ) STD z gniazda:

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

Powłoka drukuje błąd, jeśli gniazdo nie jest otwarte:

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

Dlatego możesz użyć tego jako testu w warunku if :

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

Brak operacji jest w podpowłoce, więc mogę wyrzucić std-err, jeśli przekierowanie std-in zawiedzie.

Często używam / dev / tcp do sprawdzania dostępności zasobu przez 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$ 

Ten jednowierszowy otwiera deskryptor pliku 9 do odczytu i zapisu do gniazda, drukuje HTTP GET do gniazda i używa catdo odczytu z gniazda.

merebagatelle
źródło
1
@ABB Myślę, że masz na myśli, że zawiesza się przez długi czas, jeśli port nie odpowiada, na przykład gdy jest filtrowany lub nic nie znajduje się w adresie IP. Zamknięty port nie powoduje opóźnienia, jeśli port aktywnie odrzuca połączenie. Z wielu powodów jest to całkiem do przyjęcia.
mc0e,
Ta technika została już opublikowana przed tą odpowiedzią we wcześniejszej odpowiedzi kenorb . W każdym razie większą kwestią jest to, że jednak zawiesza się przez długi czas, jeśli port nie odpowiada. Na przykład, spróbuj </dev/tcp/canyouseeme.org/80i wtedy </dev/tcp/canyouseeme.org/81.
Acumenus
9

Choć to stare pytanie, właśnie poradziłem sobie z jego wariantem, ale żadne z rozwiązań tutaj nie miało zastosowania, więc znalazłem inne i dodaję je dla potomności. Tak, wiem, że OP powiedział, że byli świadomi tej opcji i nie pasowało to do nich, ale dla każdego, kto po niej pójdzie, może się to przydać.

W moim przypadku chcę przetestować dostępność apt-cacher-ngusługi lokalnej z dockerkompilacji. Oznacza to, że absolutnie nic nie można zainstalować przed testem. No nc, nmap, expect, telnetlub python. perljednak jest obecny wraz z bibliotekami podstawowymi, więc użyłem tego:

perl -MIO::Socket::INET -e 'exit(! defined( IO::Socket::INET->new("172.17.42.1:3142")))'
Mc0e
źródło
6

W niektórych przypadkach, gdy narzędzia takie jak curl, telnet, nc o nmap są niedostępne, nadal masz szansę z 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
PRF
źródło
6

sprawdź porty za pomocą bash

Przykład

$ ./test_port_bash.sh 192.168.7.7 22

port 22 jest otwarty

Kod

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
źródło
4

Jeśli chcesz używać, ncale nie masz wersji obsługującej -z, spróbuj użyć --send-only:

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

i po upływie limitu czasu:

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

i bez wyszukiwania DNS, jeśli jest to adres IP:

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

Zwraca kody jako -zoparte na tym, czy można się połączyć, czy nie.

Chris Mendez
źródło
1

Domyślam się, że jest już za późno na odpowiedź, i to może nie być dobra, ale proszę bardzo ...

Co powiesz na umieszczenie go w pętli while z jakimś zegarem. Jestem bardziej facetem od Perla niż Solaris, ale w zależności od używanej powłoki powinieneś być w stanie zrobić coś takiego:

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

A następnie po prostu dodaj flagę w pętli while, aby jeśli upłynął limit czasu przed ukończeniem, możesz podać limit czasu jako przyczynę niepowodzenia.

Podejrzewam, że telnet ma również wyłącznik czasowy, ale myślę, że powyższe zadziała.

Red Thomas
źródło
1

Potrzebowałem krótkiego skryptu, który został uruchomiony w cronie i nie ma danych wyjściowych. Rozwiązuję problemy za pomocą 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

Aby go uruchomić powinieneś zainstalować nmap, ponieważ nie jest to domyślnie zainstalowany pakiet.

Krystian Kulasza
źródło
nmapgrepowalne wyjście może być również przydatne -oG -do wysłania go na standardowe wyjście. (grep potrzebowałby trochę modyfikacji)
Gert van den Berg
0

To używa telnet za kulisami i wydaje się działać dobrze na Mac / Linux. Nie używa netcata ze względu na różnice między wersjami na Linux / Mac, i działa to z domyślną instalacją Mac.

Przykład:

$ 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
źródło
0

Opierając się na najczęściej głosowanej odpowiedzi, tutaj jest funkcja czekania na otwarcie dwóch portów, z limitem czasu. Zwróć uwagę na dwa porty, które muszą być otwarte, 8890 i 1111, a także na max_attempts (1 na sekundę).

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
źródło
-1

nmap-ncat, aby przetestować port lokalny, który nie jest już używany


availabletobindon() {
  port="$1"
  nc -w 2 -i 1 localhost "$port" 2>&1 | grep -v -q 'Idle timeout expired'
  return "$?"
}
isleofgray
źródło
Nie mówi mi to, że mój port 80 jest otwarty.
totoro 19.04.16