Источник: Как открыть TCP-/UDP-сокет средствами командной оболочки bash
Представьте, что вам по какой-либо причине необходимо открыть TCP-/UDP-сокет на работающем под управлением Linux сервере. Например, вам нужно проверить доступность определенного порта на удаленном узле с известным адресом. Также у вас может возникнуть потребность в загрузке определенной веб-страницы с удаленного веб-сервера или задействования restful API для тестирования сетевого сервиса. Кроме того, вы можете просто захотеть соединиться с сервером IRC или каким-либо другим удаленным сервером. Но что делать в том случае, если на вашем сервере имеется лишь очень ограниченное в плане функций рабочее окружение? Так, в нем могут быть просто не установлены такие стандартные инструменты для работы с сетью, как netcat , curl и wget , при этом в вашем распоряжении может иметься лишь командная оболочка bash.
Фактически, одной из встроенных функций командной оболочки bash является функция открытия TCP-/UDP-сокетов с помощью файлов устройств /dev/tcp и /dev/udp соответственно. В данном руководстве мы постараемся разобраться в том, как открывать TCP-/UDP-сокеты, а также осуществлять посредством них прием и передачу данных в процессе работы с командной оболочкой bash.
По сути, вы можете открыть TCP-/UDP-сокет средствами командной оболочки bash, воспользовавшись следующей синтаксической конструкцией:
$ exec {дескриптор-файла}<>/dev/{протокол}/{адрес-узла}/{номер-порта}Вместо строки "дескриптор-файла" следует использовать уникальные неотрицательные целочисленные идентификаторы (дескрипторы), которые будут ассоциированы с каждым из создаваемых сокетов. Файловые дескрипторы 0, 1 и 2 зарезервированы за стандартными потоками ввода ( stdin ), вывода ( stdout ) и ошибок ( stderr ) соответственно. Исходя из этого, вы должны использовать значения, начиная с 3 (которые не используются) в качестве файловых дескрипторов.
Сочетание символов "<>" указывает на то, что сокет должен быть открыт для чтения и записи. В зависимости от ваших потребностей, вы можете открыть сокет только для чтения ( "<" ) или только для записи ( ">" ).
В поле "протокол" может располагаться строка "tcp", либо "udp". Назначение полей "адрес-узла" и "номер-порта" не требует особых комментариев.
Например, для открытия двунаправленного TCP-сокета, соединенного с основным портом HTTP-сервера ресурса xmodulo.com, с файловым дескриптором 3 достаточно выполнить следующую команду:
$ exec 3<>/dev/tcp/xmodulo.com/80После открытия сокет для чтения/записи может быть закрыт с помощью следующей синтаксической конструкции. Первая команда предназначена для закрытия входящего соединения, вторая — исходящего.
$ exec {дескриптор-файла}<&-
$ exec {дескриптор-файла}>&-После открытия сокета вы можете использовать его для приема или передачи сообщений.
Для передачи сообщения, хранящегося в переменной $MESSAGE , через сокет следует использовать одну из следующих команд:
$ echo -ne $MESSAGE >&3
$ printf $MESSAGE >&3Для чтения сообщения из сокета и сохранения его в переменной $MESSAGE — одну из следующих команд (в переменных $NUM_BYTES и $COUNT должны храниться числовые значения, равные количеству байт сообщения и количеству сообщений соответственно — прим.пер.):
$ read -r -u -n $MESSAGE <&3
$ MESSAGE=$(dd bs=$NUM\_BYTES count=$COUNT <&3 2> /dev/null)Ниже я привел несколько примеров сценариев, в рамках которых создаются и используются TCP-сокеты.
1. Получение веб-страницы с удаленного сервера и вывод ее исходного кода
#!/bin/bash
exec 3<>/dev/tcp/xmodulo.com/80
echo -e "GET / HTTP/1.1\\r\\nhost: xmodulo.com\\r\\nConnection: close\\r\\n\\r\\n" >&3
cat <&32. Вывод информации о версии установленного на удаленном узле SSH-сервера
#!/bin/bash
exec 3</dev/tcp/192.168.0.10/22
timeout 1 cat <&3Фактически, приведенный выше сценарий может быть сокращен до следующего однострочного сценария:
#!/bin/bash
timeout 1 cat </dev/tcp/192.168.0.10/223. Вывод информации о текущем времени, полученной с веб-сайта nist.gov
#!/bin/bash
cat </dev/tcp/time.nist.gov/134. Проверка работоспособности соединения с сетью Интернет
#!/bin/bash
HOST=www.mit.edu
PORT=80
(echo >/dev/tcp/${HOST}/${PORT}) &>/dev/null
if \[ $? -eq 0 \]; then
echo "Соединение успешно установлено"
else
echo "Не удалось установить соединение"
fi5. Сканирование TCP-портов удаленного узла
#!/bin/bash
host=$1
port\_first=1
port\_last=65535
for ((port=$port\_first; port<=$port\_last; port++))
do
(echo >/dev/tcp/$host/$port) >/dev/null 2>&1 && echo "Порт $port открыт"
doneДля получения возможности открытия сокетов средствами командной оболочки bash необходима активация механизма поддержки виртуальных файлов устройств сокетов на этапе сборки бинарного файла этой командной оболочки из исходных кодов (т.е., его сборки с активацией возможности "--enable-net-redirections" ). В устаревших версиях дистрибутивов данная возможность bash может быть деактивирована, причем в этом случае вы будете получать следующее сообщение об ошибке после каждой попытки открытия сокета:
/dev/tcp/xmodulo.com/80: No such file or directory
Помимо командной оболочки bash, виртуальные устройства сокетов поддерживаются такими командными оболочками, как ksh и zsh.