Тест из оболочки script, если открыт удаленный порт TCP

Я ищу быстрый и простой метод для правильного тестирования, если данный порт TCP открыт на удаленном сервере, изнутри оболочки script.

Мне удалось сделать это с помощью команды 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 (код возврата или строка в стандартном режиме).

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

Примечание. Это то, что работает мой сервер (где мне нужно его запустить)

SunOS 5.10 Generic_139556-08 i86pc i386 i86pc

Ответ 2

Как указал Б. Роудс, nc выполнит эту работу. Более компактный способ его использования:

nc -z <host> <port>

Таким образом, nc будет проверять, открыт ли порт, завершение с успехом 0, 1 при сбое.

Для быстрой интерактивной проверки (с тайм-аутом 5 секунд):

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

Ответ 3

Это легко сделать с параметрами -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.

Ответ 4

TOC:

  • Использование bash и timeout
    • Command
    • Примеры
  • Использование nc
    • Command
    • 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 $?, конечно, предназначены только для иллюстрации.

Ответ 5

С помощью netcat вы можете проверить, открыт ли этот порт:

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

Возвращаемое значение nc будет успешным, если порт TCP был открыт, и сбой (как правило, код возврата 1), если он не смог выполнить TCP-соединение.

Ответ 6

В Bash с использованием файлов псевдо-устройств для соединений TCP/UDP выполняется прямо. Вот 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

Тестирование:

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

Вот однострочный синтаксис (Bash):

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

Обратите внимание, что некоторые серверы могут быть брандмауэрами, защищенными от атак SYN flood, поэтому может возникнуть тайм-аут соединения TCP (~ 75 секунд). Чтобы обойти проблему тайм-аута, попробуйте:

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

Смотрите: Как уменьшить тайм-аут системного вызова TCP connect()

Ответ 7

Если вы используете ksh или bash, они поддерживают IO перенаправление в/из сокета с помощью /dev/tcp/IP/PORT. В этом примере оболочки Korn я перенаправляю std-in no-op (:) из сокета:

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]

Поэтому вы можете использовать это как test в if:

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

Нет-op находится в подоболочке, поэтому я могу сбросить 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 для чтения из сокета.

Ответ 8

В то время как старый вопрос, я только что рассмотрел его вариант, но ни одно из решений здесь не применимо, поэтому я нашел другое, и добавляю его для потомков. Да, я знаю, что ОП сказал, что они знают об этом варианте, и это им не устраивало, но для тех, кто последует впоследствии, это может оказаться полезным.

В моем случае я хочу проверить наличие локальной службы 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")))'

Ответ 9

Мне нужно было более гибкое решение для работы с несколькими репозиториями 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

Ответ 10

Я предполагаю, что слишком поздно для ответа, и это может быть не очень хорошо, но здесь вы идете...

Как насчет того, чтобы вставить его внутри цикла while с каким-то таймером. Я больше похож на Perl, чем на Solaris, но в зависимости от используемой оболочки вы должны сделать что-то вроде:

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

А затем просто добавьте флаг во время цикла while, так что, если он истечет до завершения, вы можете указать время ожидания как причину сбоя.

Я подозреваю, что у telnet есть также тайм-аут, но, как мне кажется, выше всего будет работать.

Ответ 11

Если вы хотите использовать 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 на основе, если он может подключиться или нет.

Ответ 12

В некоторых случаях, когда инструменты, такие как 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

Ответ 13

Как насчет Netcat или Nmap

Ответ 14

Мне понадобился короткий script, который запускался в 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, поскольку он не является установленным по умолчанию пакетом.

Ответ 15

Это использует 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' "[email protected]"; }

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 

Ответ 16

проверить порты с помощью 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

Ответ 17

nmap-ncat для проверки локального порта, который еще не используется


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