Предохраняйся носками! Докучаев Дмитрий aka Forb Xakep, номер #066, стр. 066-118-3 exit unless ($shake); ($lport, $sdata)=parse_logon($shake); if ($lport eq 0) { $sid->send("-ERR\n"); if ($logpw) { $ip=getip($sid); logging("access.log", "Password incorrect from $ip\n"); } close($sid); exit; } $sid->send("+OK $lport $sdata\n"); skipped close($sid); unless(fork) { listen_gate($lport, $sdata) } return 0; } Разберемся, что конкретно делает эта действительно маленькая саба. Во-первых, в ней запоминается идентификатор сокета, на который успешно подцепились. Затем происходит чтение из этого самого идентификатора информации для «рукопожатия». Для забывчивых повторяю: клиент передаст строку вида «pass:host:port». Затем зарисовывается новая процедура parse_logon(), от которой мы получаем рандомно выбранный локальный порт (на котором будем сокет мутить), а также $sdata (переменная имеет вид «host:port»). Если был получен нулевой результат, вызываем функцию определения IP и логируем запрос (для дальнейшего изучения). Наконец, закроем сокет и корректно завершим процесс. В случае же если logon_parse() вернул ненулевое значение, напишем клиенту, что он прав :), а также наградим его значением локального порта (смотри скриншот). На всякий случай определяем сетевой адрес и логируем запрос (вдруг это был хакер с забугорья?). И в виде отдельного процесса переходим к процедуре listen_gate(). Она очень проста, поэтому не привожу ее синтаксиса. Единственная сложность - в коде используется таймер. Он нужен, если клиент раздумал цепляться на выданный ему порт. Посуди сам, в этом случае сценарий создаст лишний сокет, который никогда не будет закрыт (он уничтожится после завершения главного процесса). Чтобы оговорить этот вариант, после открытия порта запускается таймер на 300 секунд. Если соединения за это время не произошло - процессу посылается сигнал ALRM, который обработается специальной процедурой timer() (ее ты обнаружишь в сорцах). В этой нехитрой сабе происходит корректное закрытие сокета и завершение подпроцесса. Если же соединение удалось - таймер деактивируется и интерпретатор переходит к обработке процедуры forward(). Она является самой главной и масштабной. Если тебе удастся понять принцип ее реализации - считай, что ты научился работать с сокетами. Механизм туннелирования На самом деле я чуть-чуть приврал, говоря о масштабности. Процедура весьма компактна и содержит только необходимые вещи. Не веришь - сам посмотри на ее листинг. Работа с двумя сокетами sub forward { my($sid, $sdata)=@_; $select = IO::Select->new(); $select->add($sid); binmode($sid); my($remote)=IO::Socket::INET->new("$sdata") || die print "$!\n"; $select->add($remote); binmode($remote); while(1) { foreach $n ($select->can_read) { if ($n eq $sid) { $sid->recv($buff, 1024); if ($buff) { $remote->send($buff); logging("sniff.log", "-> $buff") if $sniff; } else { $select->remove($sid); logging("sniff.log", "-> EOF\n") if $sniff; close($sid); close($remote); return 0; }} if ($n eq $remote) { $remote->recv($buff, 1024); ***skipped*** }} return 0; } В этом куске кода происходит работа в основном с модулем IO::Select. Он служит для слежения за сокетом и имеет несколько нужных методов. Нас интересует can_read, так как именно он определяет - имеются ли в сокете непрочитанные данные. После того как был получен идентификатор подключенного клиента, происходит его добавление в «список» Select’а. Затем порождаем соединение с удаленным узлом и также добавляем его для контроля Select’ом. |