Uji apakah port TCP jarak jauh terbuka dari skrip shell

297

Saya mencari metode cepat dan sederhana untuk pengujian dengan benar jika port TCP yang diberikan terbuka pada server jarak jauh, dari dalam skrip Shell.

Saya sudah berhasil melakukannya dengan perintah telnet, dan berfungsi dengan baik ketika port dibuka, tetapi sepertinya tidak timeout ketika tidak dan hanya hang di sana ...

Berikut ini contohnya:

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

Saya juga membutuhkan cara yang lebih baik, atau cara untuk memaksa telnet ke timeout jika tidak terhubung di bawah 8 detik misalnya, dan mengembalikan sesuatu yang dapat saya tangkap di Shell (kode pengembalian, atau string di stdout).

Saya tahu metode Perl, yang menggunakan modul IO :: Socket :: INET dan menulis skrip yang berhasil yang menguji port, tetapi lebih suka menghindari menggunakan Perl jika mungkin.

Catatan: Inilah yang dijalankan server saya (dari mana saya harus menjalankan ini)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc

Yanick Girouard
sumber
Bagaimana dengan Netcat atau Nmap ?
Jeremiah Willcock
Jawabannya berbohong dengan Harapkan. Kami menulis skrip sederhana yang mengirimkan telnet pada port yang kami butuhkan, dengan batas waktu 8 detik. Ada banyak contoh untuk dipilih. Kami mendasarkan kami pada posting ini: unix.com/shell-programming-scripting/…
Yanick Girouard
1
check_tcp dari github.com/monitoring-plugins/monitoring-plugins dapat melakukan ini, termasuk memasukkan string dan memeriksa jawaban yang diharapkan.

Jawaban:

452

Seperti yang ditunjukkan oleh B. Rhodes, ncakan melakukan pekerjaan. Cara yang lebih ringkas untuk menggunakannya:

nc -z <host> <port>

Dengan cara ncitu hanya akan memeriksa apakah port terbuka, keluar dengan 0 pada keberhasilan, 1 pada kegagalan.

Untuk pemeriksaan interaktif cepat (dengan batas waktu 5 detik):

nc -z -v -w5 <host> <port>
Alessio Gaeta
sumber
48
centos7 default menggunakan nmap netcat dan tidak memiliki opsi -z.
jolestar
7
Ini tidak berfungsi di RHEL / Centos. Untuk distro-distro itu, Anda perlu: nc -vn <host> <port>
Justin Dennahower
2
FWIW, saya telah merombak jawaban saya sepenuhnya dengan sebuah contoh , secara terpisah berlaku untuk RHEL 6 dan RHEL 7.
Acumenus
4
setidaknya pada Mac, Anda mungkin perlu menambahkan -G#untuk mengatur batas waktu koneksi terpisah dari / selain -w#batas waktu, yang pada dasarnya berfungsi sebagai batas waktu baca.
Doktor J
1
@ jolestar Anda dapat memutakhirkan Ncat secara manual di Centos 7 untuk mendapatkan -zopsi. Anda mungkin ingin mempertimbangkan: unix.stackexchange.com/questions/393762/…
Damien Ó Ceallaigh
150

Ini cukup mudah untuk dilakukan dengan opsi -zdan , tetapi tidak semua sistem telah diinstal. Jika Anda memiliki versi bash yang cukup baru, ini akan berfungsi:-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

Apa yang terjadi di sini adalah timeoutmenjalankan sub-perintah dan membunuhnya jika tidak keluar dalam batas waktu yang ditentukan (1 detik dalam contoh di atas). Dalam hal ini bashadalah sub-perintah dan menggunakan penanganan khusus / dev / tcp untuk mencoba dan membuka koneksi ke server dan port yang ditentukan. Jika bashdapat membuka koneksi dalam batas waktu, cathanya akan segera menutupnya (karena itu membaca dari /dev/null) dan keluar dengan kode status 0yang akan menyebar melalui bashdan kemudian timeout. Jika bashmendapat kegagalan koneksi sebelum batas waktu yang ditentukan, maka bashakan keluar dengan kode keluar 1 yang timeoutjuga akan kembali. Dan jika bash tidak dapat membuat koneksi dan batas waktu yang ditentukan berakhir, makatimeoutakan membunuh bashdan keluar dengan status 124.

hanya tidak ada
sumber
1
Apakah /dev/tcptersedia di sistem selain Linux? Bagaimana dengan Mac khususnya?
Peter
8
Hal / dev / tcp adalah fitur bash, jadi ya. Namun sepertinya mac tidak memiliki batas waktu ...
onlynone
1
@onlynone - Sepertinya timeoutjuga tidak ada di FreeBSD, dan pada kotak Ubuntu lama saya ini adalah paket yang dapat diinstal, tetapi tidak ada di sana secara default. Akan lebih bagus jika ada cara untuk melakukan ini di bash sendirian, tanpa alat pihak ketiga seperti batas waktu atau netcat.
Graham
3
Hanya ingin menyebutkan bahwa timeoutmuncul untuk menjadi bagian dari coreutils GNU, dan dapat diinstal pada Mac dengan homebrew: brew install coreutils. Ini akan tersedia sebagai gtimeout.
onlynone
1
@ Kartu Memori Log perubahan bash menyarankan bash-2.04-devel, meskipun dukungan untuk nama host mungkin telah ditambahkan bash-2.05-alpha1.
Acumenus
109

TOC:

  • Menggunakan bash dan timeout
    • Perintah
    • Contohnya
  • Menggunakan nc
    • Perintah
    • RHEL 6 (nc-1.84)
      • Instalasi
      • Contohnya
    • RHEL 7 (nmap-ncat-6.40)
      • Instalasi
      • Contohnya
  • Catatan

Menggunakan bash dan timeout:

Catatan yang timeoutharus hadir dengan RHEL 6+, atau alternatifnya ditemukan di GNU coreutils 8.22. Di MacOS, instal menggunakan brew install coreutilsdan gunakan sebagai gtimeout.

Perintah:

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

Jika parametrizing host dan port, pastikan untuk menentukannya seperti ${HOST}dan ${PORT}seperti di atas. Jangan menentukan mereka hanya sebagai $HOSTdan $PORT, yaitu tanpa kawat gigi; itu tidak akan berfungsi dalam kasus ini.

Contoh:

Keberhasilan:

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

Kegagalan:

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

Jika Anda harus mempertahankan status keluar dari bash,

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

Menggunakan nc:

Perhatikan bahwa versi mundur yang tidak kompatibel ncterinstal di RHEL 7.

Perintah:

Perhatikan bahwa perintah di bawah ini unik karena identik untuk RHEL 6 dan 7. Hanya instalasi dan output yang berbeda.

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

RHEL 6 (nc-1.84):

Instalasi:

$ sudo yum install nc

Contoh:

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

Jika nama host dipetakan ke beberapa IP, perintah gagal di atas akan menggilir banyak atau semua dari mereka. Sebagai contoh:

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

Instalasi:

$ sudo yum install nmap-ncat

Contoh:

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

Jika nama host dipetakan ke beberapa IP, perintah gagal di atas akan menggilir banyak atau semua dari mereka. Sebagai contoh:

$ 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

Catatan:

The -v( --verbose) argumen dan echo $?perintah tentu saja hanya untuk ilustrasi.

Acumenus
sumber
3
timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?adalah poin yang bagus!
ipeacocks
tambahkan 2>&1ke tidak status gema result_test=$(nc -w 2 $ip_addreess 80 </dev/null 2>&1 ; echo $?)
Sanya Snex
55

Dengan netcatAnda dapat memeriksa apakah port terbuka seperti ini:

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

Nilai balik ncakan berhasil jika port TCP dibuka, dan kegagalan (biasanya kode pengembalian 1) jika tidak dapat membuat koneksi TCP.

Beberapa versi ncakan hang ketika Anda mencoba ini, karena mereka tidak menutup setengah pengiriman soket mereka bahkan setelah menerima file akhir dari /dev/null. Di laptop Ubuntu saya sendiri (18.04), netcat-openbsdversi netcat yang saya instal menawarkan solusi: -Nopsi diperlukan untuk mendapatkan hasil langsung:

nc -N my.example.com 80 < /dev/null
Brandon Rhodes
sumber
1
Berfungsi bagus untuk rasa ncyang tidak mendukung -wbendera.
David Dossot
5
terima kasih - juga berfungsi untuk versi nc tanpa -zdukungan - berfungsi di RHEL / CentOS 7, misalnya
Andrew
1
Meskipun pendekatan ini baik, -wperlu, gagal dimana perintah ketika digunakan dalam skrip dapat bertahan selama lebih dari sepuluh detik. Saya telah menulis jawaban yang mencakup -w.
Acumenus
2
+1 ini bagus karena nc hadir standar dengan alpine linux dan ubuntu keduanya. mungkin yang lain. tidak perlu menginstal ekstra saat Anda sedang dikirim dan tidak bisa. Terima kasih untuk ini! Saya mungkin menambahkan,nc host port -w 2 && echo it works
std''OrgnlDave
2
Saya lebih suka nc -vz localhost 3306. Ini memberikan output yang lebih verbose. Mencobanya di mac saja.
EFreak
29

Di Bash menggunakan file perangkat semu untuk koneksi TCP / UDP langsung. Ini skripnya:

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

Pengujian:

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

Berikut ini adalah satu-liner (sintaks Bash):

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

Perhatikan bahwa beberapa server dapat dilindungi firewall dari serangan banjir SYN, sehingga Anda mungkin mengalami batas waktu koneksi TCP (~ 75secs). Untuk mengatasi masalah batas waktu, coba:

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

Lihat: Bagaimana cara mengurangi batas waktu panggilan sistem TCP connect ()?

kenorb
sumber
1
@ ABB Ini terkait dengan konfigurasi firewall server yang tidak memberikan respons apa pun untuk mencegah serangan banjir SYN. Berlari telnet canyouseeme.org 81juga hang. Ini dikendalikan oleh batas waktu habis Anda yang mungkin hardcoded di bash. Lihat: mengurangi batas waktu panggilan sistem TCP connect () .
kenorb
Untuk parametrization, sekarang tampaknya perlu untuk menentukan $SERVERdan $PORTdengan kawat gigi, setidaknya sebagai${SERVER} dan ${PORT}.
Acumenus
13

Saya membutuhkan solusi yang lebih fleksibel untuk mengerjakan beberapa repositori git jadi saya menulis kode sh berikut berdasarkan pada 1 dan 2 . Anda dapat menggunakan alamat server Anda alih-alih gitlab.com dan port Anda menggantikan 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
sumber
1
Terima kasih! Itu membuat hidup saya mudah untuk /etc/rc.local
Shahin Khaled
10

Jika Anda menggunakan ksh atau bash, keduanya mendukung pengalihan IO ke / dari soket menggunakan konstruksi / dev / tcp / IP / PORT . Di shell Korn ini contoh saya mengarahkan no-op ( : ) std-in dari soket:

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

Shell mencetak kesalahan jika soket tidak terbuka:

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

Oleh karena itu Anda dapat menggunakan ini sebagai tes di jika kondisi :

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

No-op berada dalam subkulit sehingga saya dapat membuang std-err jika pengalihan std-in gagal.

Saya sering menggunakan / dev / tcp untuk memeriksa ketersediaan sumber daya melalui 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$ 

One-liner ini membuka file descriptor 9 untuk membaca dari dan menulis ke soket, mencetak GET HTTP ke soket dan digunakan catuntuk membaca dari soket.

merebagatelle
sumber
1
@ ABB Saya pikir maksud Anda hang untuk waktu yang lama jika port tidak merespons, seperti misalnya ketika difilter, atau tidak ada yang ada di IP. Port yang tertutup tidak menyebabkan penundaan jika port secara aktif menolak koneksi. Untuk banyak tujuan ini cukup dapat diterima.
mc0e
Teknik ini sudah diposting sebelum jawaban ini di jawaban sebelumnya oleh kenorb . Pokoknya, poin yang lebih besar adalah bahwa itu hang untuk waktu yang lama jika port tidak merespons. Misalnya, coba </dev/tcp/canyouseeme.org/80dan kemudian </dev/tcp/canyouseeme.org/81.
Acumenus
9

Sementara pertanyaan lama, saya baru saja berurusan dengan varian itu, tetapi tidak ada solusi di sini yang berlaku, jadi saya menemukan yang lain, dan saya menambahkannya untuk anak cucu. Ya, saya tahu OP mengatakan mereka mengetahui opsi ini dan itu tidak cocok untuk mereka, tetapi bagi siapa pun yang mengikuti setelah itu mungkin terbukti bermanfaat.

Dalam kasus saya, saya ingin menguji ketersediaan apt-cacher-nglayanan lokal dari dockerbuild. Itu berarti sama sekali tidak ada yang dapat diinstal sebelum tes. Tidak nc, nmap, expect, telnetatau python. perlNamun hadir, bersama dengan perpustakaan inti, jadi saya menggunakan ini:

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

Dalam beberapa kasus di mana alat-alat seperti curl, telnet, nc o nmap tidak tersedia, Anda masih memiliki peluang dengan 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
sumber
6

periksa port menggunakan bash

Contoh

$ ./test_port_bash.sh 192.168.7.7 22

port 22 terbuka

Kode

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
sumber
4

Jika Anda ingin menggunakan nctetapi tidak memiliki versi yang mendukung -z, coba gunakan --send-only:

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

dan dengan batas waktu:

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

dan tanpa pencarian DNS jika itu IP:

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

Ini mengembalikan kode sebagai -zberdasarkan apakah dapat terhubung atau tidak.

Chris Mendez
sumber
1

Saya menduga sudah terlambat untuk menjawab, dan ini mungkin bukan jawaban yang bagus, tapi begini ...

Bagaimana dengan meletakkannya di dalam loop sementara dengan timer di atasnya. Saya lebih menyukai Perl daripada Solaris, tetapi tergantung pada shell yang Anda gunakan, Anda harus bisa melakukan sesuatu seperti:

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

Dan kemudian tambahkan bendera di loop sementara, sehingga jika habis sebelum selesai, Anda dapat mengutip batas waktu sebagai alasan kegagalan.

Saya menduga bahwa telnet memiliki saklar batas waktu juga, tetapi hanya dari atas kepala saya, saya pikir di atas akan berfungsi.

Thomas Merah
sumber
1

Saya membutuhkan skrip pendek yang dijalankan di cron dan belum menghasilkan. Saya memecahkan masalah saya menggunakan 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

Untuk menjalankannya, Anda harus menginstal nmap karena itu bukan paket bawaan yang diinstal.

Krystian Kulasza
sumber
nmapOutput grepable mungkin juga berguna -oG -untuk mengirimkannya ke stdout. (grep akan membutuhkan sedikit modifikasi)
Gert van den Berg
0

Ini menggunakan telnet di belakang layar, dan tampaknya berfungsi dengan baik di mac / linux. Itu tidak menggunakan netcat karena perbedaan antara versi di linux / mac, dan ini berfungsi dengan instalasi mac default.

Contoh:

$ 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 
Taman Brad
sumber
0

Berdasarkan jawaban yang paling banyak dipilih, berikut adalah fungsi untuk menunggu dua port terbuka, dengan batas waktu juga. Perhatikan dua port yang terbuka, 8890 dan 1111, serta maks_attempts (1 per detik).

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
sumber
-1

nmap-ncat untuk menguji port lokal yang belum digunakan


availabletobindon() {
  port="$1"
  nc -w 2 -i 1 localhost "$port" 2>&1 | grep -v -q 'Idle timeout expired'
  return "$?"
}
isleofgray
sumber
Itu tidak memberi tahu saya bahwa port 80 saya terbuka.
totoro