DNS-туннелинг или халявный dialup

Дмитрий Докучаев aka Forb

Xakep, номер #051, стр. 051-058-3


%locate - хеш, хранящий полный адрес сокета (необходим для точной отправки udp-датаграммы).

@sockets - массив, хранящий идентификаторы сокетов.

@pings - массив, необходимый для процедуры обработки пинга сокета.

$timeout - таймаут, по умолчанию три минуты (значение * 3, в дальнейшем я расскажу про эту переменную).

$sockets - подсчет количества открытых сокетов.

$proxy – прокси-сервер, для tcp-соединения.

После объявления этих важных идентификаторов, мы создадим udp-сокет, который будет ловить пакеты на 53-ем порту, а также добавим его в объект модуля IO::Select, чтобы следить за данными в этом сокете. Затем порождаем бесконечный цикл, в котором читаем данные с udp-сокета следующей конструкцией:

foreach $n ($udp->can_read) { ## Смотрим все идентификаторы сокетов, в которых есть данные

if ($n eq $sockudp) { ## Если идентификатор - $sockudp, то есть наш сокет

$sockudp->recv($msg,1024); ## Получаем из него сообщение

my($port,$addr)=sockaddr_in($sockudp->peername); ## Запоминаем порт и адрес

$flag=0,$i=0,$nsock=0; ## Обнуляем временные переменные

foreach $in (values %ports) {

$i++;

if ($in ne -1) {

if ($in eq $port) {

$flag=1,$nsock=$i; ## Ищем этот порт в хеше портов, если он есть, устанавливаем $flag 1, иначе - 0

}

}

}

unless ($flag) {

$threads++;

$res=tcpsock($port,$threads); ## Если порт новый, создаем новый IRC-сокет

..............

} else {

## Оперируем над старыми udp-датаграммами

}

Обзор утилиты

В этой части сервера все просто. Следим за приходящими на udp-порт пакетами. Как известно, этот пакет может быть доставлен с любого ip-адреса и порта, так как фактического соединения с подтверждением не происходит (такой уж он - udp). Если в хеше %ports нет локального порта, откуда пришел этот пакет, считаем его новым, и делаем для него туннель через tcp-сокет. В противном случае переходим в малоинтересную часть кода - прием сообщений со всех порожденных (в данный момент времени) tcp-сокетов, с последующим возвратом пакетов клиенту через udp. Интереснее будет рассказать про процедуру отключения сокета по таймауту (при завершении сессии весь туннель должен быть корректно завершенным, с отключением tcp-соединения для него). Это делается с помощью сигнала ALRM, посылаемому серверу через определенный промежуток времени (этот промежуток и есть $timeout). Так как при передаче пакетов через udp возможна их потеря, контролировать которую крайне медленно и неудобно, мы обходимся тройной передачей сообщения "KEEP" клиенту. Если клиент хотя бы один раз возвращает "ALIVE", значит он все еще с нами =), и мы не убиваем его. Иначе закрываем сокет и присваиваем идентификатору значение "-1", впоследствии считая его мертвым. Они все могут быть "оживлены" заново при новом подключении. Таким образом количество элементов массива сводится к минимальному, что существенно улучшает производительность скрипта.

Назад на стр. 051-058-2  Содержание  Вперед на стр. 051-058-4