Перевірте, чи відкритий віддалений порт TCP від ​​сценарію оболонки


297

Я шукаю швидкий і простий метод для правильного тестування, якщо даний порт TCP відкритий на віддаленому сервері, всередині сценарію Shell.

Мені вдалося це зробити за допомогою команди telnet, і вона прекрасно працює, коли порт відкритий, але, здається, не йде час, коли його немає і просто висить там ...

Ось зразок:

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

Мені або потрібен кращий спосіб, або спосіб примусити telnet до тайм-ауту, якщо він, наприклад, не підключиться менше 8 секунд, і повернути щось, що я можу впізнати в Shell (код повернення або рядок в stdout).

Я знаю метод Perl, який використовує модуль IO :: Socket :: INET і написав успішний сценарій, який тестує порт, але скоріше хотів би уникати використання Perl, якщо можливо.

Примітка. Це те, на чому працює мій сервер (звідки мені потрібно запустити це)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc


А як щодо Netcat або Nmap ?
Jeremiah Willcock

Відповідь збрехала з очікуванням. Ми написали простий сценарій, який надсилає телефонну мережу на потрібний нам порт, із затримкою 8 секунд. Прикладів можна також вибрати з багатьох прикладів. Ми базувались на цій публікації: unix.com/shell-programming-scripting/…
Girouard

1
check_tcp з github.com/monitoring-plugins/monitoring-plugins може це зробити, включаючи введення рядків і перевірку очікуваної відповіді.

Відповіді:


452

Як вказував Б. Родос, ncбуде виконувати цю роботу. Більш компактний спосіб його використання:

nc -z <host> <port>

Цей спосіб ncперевірятиме лише, чи порт відкритий, виходячи з 0 при успіху, 1 при відмові.

Для швидкої інтерактивної перевірки (із затримкою 5 секунд):

nc -z -v -w5 <host> <port>

48
centos7 за замовчуванням використовує nmap netcat і не має опції -z.
jolestar

7
Це не працює в RHEL / Centos. Для цих дистрибутивів потрібно: nc -vn <host> <port>
Джастін Деннахауер

2
FWIW, я повністю переробив свою відповідь на прикладі , окремо застосовний як до RHEL 6, так і до RHEL 7.
Acumenus

4
принаймні на Mac, можливо, вам потрібно буде додати, -G#щоб встановити час очікування з'єднання окремо від / на додаток до -w#таймауту, який в основному функціонує як час очікування читання.
Doktor J

1
@jolestar Ви можете вручну оновити Ncat на Centos 7, щоб отримати -zможливість. Ви можете розглянути: unix.stackexchange.com/questions/393762/…
Damien Ó Ceallaigh

150

Це досить легко зробити з -zі -w TIMEOUTопціями nc, але не всі системи ncвстановлені. Якщо у вас є досить недавня версія bash, це спрацює:

# 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

Тут відбувається те, що timeoutвін запустить підкоманду та знищить її, якщо вона не вийде за вказаний час (1 секунда у наведеному вище прикладі). У цьому випадку bashпідкоманда і використовує спеціальну обробку / dev / tcp, щоб спробувати відкрити підключення до вказаного сервера та порту. Якщо ви bashзможете відкрити з’єднання протягом тайм-ауту, catпросто закрийте його негайно (оскільки воно зчитується з /dev/null) та вийдете з кодом статусу, 0який поширюватиметься через bashі потім timeout. Якщо ви отримаєте bashпошкодження з’єднання до вказаного часу, тоді bashвийде з кодом виходу 1, який timeoutтакож повернеться. І якщо bash не в змозі встановити з'єднання і вказаний час очікування закінчується, тодіtimeoutзнищить bashі вийде зі статусом 124.


1
Чи /dev/tcpдоступний в інших системах, ніж Linux? Що з Macs зокрема?
Петро

8
Річ / dev / tcp - це особливість bash, так що так. Однак схоже, що у маків немає таймауту ...
onlynone

1
@onlynone - Схоже, timeoutтакож не існує у FreeBSD, і в моєму старому вікні Ubuntu це встановлений пакет, але його немає за замовчуванням. Було б чудово, якби був спосіб зробити це в башті самостійно, без сторонніх інструментів, таких як тайм-аут або netcat.
Грем

3
Просто хотілося б відзначити , що , як timeoutвидається , бути частиною GNU Coreutils, і може бути встановлений на Маках з доморощеного: brew install coreutils. Потім він буде доступний як gtimeout.
onlynone

1
@Wildcard Баш журнал змін передбачає bash-2.04-devel, хоча підтримка імен хостів можуть бути додані в bash-2.05-alpha1.
Acumenus

109

TOC:

  • Використання bash і timeout
    • Командування
    • Приклади
  • Використання nc
    • Командування
    • RHEL 6 (nc-1,84)
      • Установка
      • Приклади
    • RHEL 7 (nmap-ncat-6.40)
      • Установка
      • Приклади
  • Зауваження

Використання bash та timeout:

Зауважте, що він timeoutповинен бути присутнім у RHEL 6+ або, як альтернатива, знайдений у GNU coreutils 8.22. На MacOS встановіть його за допомогою brew install coreutilsта використовуйте як gtimeout.

Команда:

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

Якщо параметризуєте хост і порт, обов'язково вкажіть їх як ${HOST}і ${PORT}як зазначено вище. Не вказуйте їх просто як $HOSTі $PORT, тобто без дужок; у цьому випадку це не спрацює.

Приклад:

Успіх:

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

Збій:

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

Якщо вам потрібно зберегти статус виходу bash,

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

Використання nc:

Зауважте, що ncна RHEL 7 встановлюється назад несумісна версія .

Команда:

Зауважте, що команда нижче унікальна тим, що вона ідентична як для RHEL 6, так і для 7. Просто встановлення та вихід відрізняються.

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

RHEL 6 (nc-1,84):

Установка:

$ 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

Якщо ім'я хоста відображається на декількох IP-адресах, вищезазначена команда, що відмовляється, буде проходити через багато або всі з них. Наприклад:

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

Установка:

$ 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

Якщо ім'я хоста відображається на декількох IP-адресах, вищезазначена команда, що відмовляється, буде проходити через багато або всі з них. Наприклад:

$ 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

Зауваження:

Аргумент -v( --verbose) і echo $?команда, звичайно, лише для ілюстрації.


3
timeout 2 bash -c "</dev/tcp/canyouseeme.org/81"; echo $?чудовий момент!
ipeacocks

додати 2>&1не статус ехо result_test=$(nc -w 2 $ip_addreess 80 </dev/null 2>&1 ; echo $?)
Sanya Snex

55

За допомогою netcatвас можна перевірити, чи порт відкритий так:

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

Повернене значення ncбуде успішним, якщо порт TCP було відкрито, і невдача (як правило, код повернення 1), якщо він не зміг зробити TCP-з'єднання.

Деякі версії програми ncвисітимуть, якщо ви спробуєте це, оскільки вони не закривають половину свого сокета, навіть не отримуючи кінець файлу від /dev/null. На моєму власному ноутбуці Ubuntu (18.04) netcat-openbsdверсія Netcat, яку я встановив, пропонує вирішення: -Nпараметр необхідний для отримання негайного результату:

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

1
Чудово підходить для ароматів nc, які не підтримують -wпрапор.
Девід Доссот

5
спасибі - також працює для версій nc без -zпідтримки - працює на RHEL / CentOS 7, наприклад
Андрій

1
Хоча такий підхід є хорошим, -wнеобхідний, якщо не вдасться, що команда при використанні в сценарії може зависати більше десяти секунд. Я написав відповідь, яка включає -w.
Acumenus

2
+1 це чудово, тому що nc є стандартним для альпійського Linux та ubuntu. напевно, інші. немає необхідності встановлювати додаткові, коли ви віддалені та не можете. спасибі за це! Можу додати,nc host port -w 2 && echo it works
std''OrgnlDave

2
Я віддаю перевагу nc -vz localhost 3306. Це дає більш багатослівний вихід. Спробував це тільки на mac.
EFreak

29

У Bash використання файлів псевдопристроїв для підключення TCP / UDP прямо вперед. Ось сценарій:

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

Тестування:

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

Ось однолінійний (синтаксис Баша):

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

Зверніть увагу, що деякі сервери можуть бути захищені брандмауером від нападу SYN потоку, тому у вас може виникнути час очікування з'єднання TCP (~ 75 сек). Щоб вирішити проблему з очікуванням часу, спробуйте:

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

Див.: Як зменшити час очікування системного виклику TCP connect ()?


1
@ABB Це пов'язано з конфігурацією брандмауера сервера, яка не дає жодної відповіді, щоб запобігти будь-яким атакам потоку SYN. Біг telnet canyouseeme.org 81також висить. Це контролюється вашими обмеженнями на час очікування, які, ймовірно, жорстко закодовані в баш. Див: зменшення TCP підключення () системи тайм - аут виклику .
kenorb

Для параметризації тепер, здається, потрібно вказати $SERVERі $PORTдужки, принаймні як ${SERVER}і ${PORT}.
Acumenus

13

Мені потрібно було гнучкіше рішення для роботи над декількома сховищами git, тому я написав наступний код sh на основі 1 і 2 . Ви можете використовувати свою адресу сервера замість gitlab.com та свій порт замість 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

1
Дякую! Це полегшує моє життя для /etc/rc.local
Шахін Халед

10

Якщо ви використовуєте ksh або bash, вони обидва підтримують перенаправлення IO в сокет з / за допомогою конструкції / dev / tcp / IP / PORT . У цьому Korn оболонки прикладу я перенаправляти НЕ-оп - х ( : ) НЕ Std-в з сокета:

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

Оболонка друкує помилку, якщо сокет не відкритий:

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

Тому ви можете використовувати це як тест у випадку if :

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

Неопераційний режим знаходиться в нижній частині, тому я можу скинути std-err, якщо перенаправлення std-in не вдасться.

Я часто використовую / dev / tcp для перевірки наявності ресурсу через 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$ 

Цей однолінійний відкриває дескриптор 9 файлів для читання з сокету та запису в нього, друкує HTTP GET в сокет і використовує catдля читання з сокета.


1
@ABB Я думаю, ви маєте на увазі, що він довго зависає, якщо порт не реагує, як, наприклад, при фільтруванні, або нічого немає в IP. Закритий порт не викликає затримки, якщо порт активно відмовляється від з'єднання. Для багатьох цілей це цілком прийнятно.
mc0e

Ця методика була вже розміщена перед цією відповіддю в попередній відповіді від kenorb . У будь-якому разі, важливішим є те, що він, однак, зависає довго, якщо порт не реагує. Наприклад, спробуйте </dev/tcp/canyouseeme.org/80і потім </dev/tcp/canyouseeme.org/81.
Акумен

9

Хоча старе питання, я щойно розбирався з його варіантом, але жодне з рішень тут не було застосовно, тому я знайшов інше і додаю його для нащадків. Так, я знаю, що ОП сказали, що вони знають про цей варіант, і він їм не підходить, але для тих, хто наступний після цього може виявитися корисним.

У моєму випадку я хочу перевірити наявність локальної apt-cacher-ngпослуги у складі docker. Це означає, що перед тестом абсолютно нічого не можна встановити. Ні nc, nmap, expect, telnetабо python. perlоднак присутні разом із основними бібліотеками, тому я використав це:

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

6

У деяких випадках, коли такі інструменти, як curl, telnet, nc o nmap недоступні, ви все ще маєте шанс за допомогою 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

6

перевірити порти за допомогою bash

Приклад

$ ./test_port_bash.sh 192.168.7.7 22

порт 22 відкритий

Код

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

4

Якщо ви хочете використовувати, ncале не маєте версії, яка підтримує -z, спробуйте скористатися --send-only:

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

та з таймаутом:

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

і без пошуку DNS, якщо це IP-адреса:

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

Він повертає коди як -zоснову, може він підключитися чи ні.


1

Я здогадуюсь, що відповідь вже пізно, і це може бути не гарним, але ось ви йдете ...

Що з того, щоб поставити його всередину певного циклу з тимчасовим таймером на ньому. Я скоріше хлопець Perl, ніж Solaris, але залежно від оболонки, яку ви використовуєте, ви повинні вміти робити щось на кшталт:

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

А потім просто додайте прапор у циклі while, так що якщо час закінчиться перед завершенням, ви можете навести час очікування як причину відмови.

Я підозрюю, що в telnet є і перемикач тайм-аута, але якраз у верхній частині голови, я думаю, що вищезгадане спрацює.


1

Мені потрібен був короткий сценарій, який запускався в cron і не виводив. Я вирішую свою проблему за допомогою 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

Для його запуску Вам слід встановити nmap, оскільки це не встановлений за замовчуванням пакет.


nmapgrepable-вихід може також бути корисним -oG -для надсилання в stdout. (греп потребує трохи модифікації)
Герт ван ден Берг

0

Це використовує telnet за кадром, і, здається, працює добре на mac / linux. Він не використовує netcat через відмінності між версіями на Linux / mac, і це працює при встановленні mac за замовчуванням.

Приклад:

$ 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 

0

Опираючись на найбільш голосовану відповідь, тут є функція чекати, коли два порти будуть відкриті, а також тайм-аут. Зверніть увагу на два порти, які мають бути відкритими, 8890 та 1111, а також max_attempts (1 в секунду).

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
}

-1

nmap-ncat для перевірки на локальний порт, який вже не використовується


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

Це не говорить мені, що мій порт 80 відкритий.
totoro
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.