TCP-сервер. UDP-сервер
TCP-сервер
#подключаем Perl
#! /usr/bin/perl
#Подключаем наш компонент для работы с сокетом
use IO:: Socket;
$Server_port = 80;
#создаемсокет
$server = IO:: Socket:: INET-> new(
LocalPort => $Server_port,
Type => SOCK_STREAM,
Reuse => 1,
Listen => 10)
or die " Couldn't start TCP-server\n";
#пока есть подключение, сделать что-то с этим подключением
while ( $client = $server -> accept() ) {
#клиент и есть подключение, берём от него сообщение и печатаем
$sub = < $client>;
print $sub;
}
#закрываемсокет
close($server);
Комментарий:
Сначала, как всегда вызываем конструктор и задаемсамые необходимые параметры и создаем этим же конструкторомсокет. После чего, функцией accept() последовательнопринимаем все приходящие подключения, при выполненииэтой функции дальнейшая часть программы блокируется, до техпор пока не поступит предложение на соединение, и послесоединения мы спокойно обрабатываем $client, как обычный сокет.
Теперь рассмотрим UDP-сервер:
UDP-сервер
#подключаем Perl
#! /usr/bin/perl
#Подключаем наш компонент для работы с сокетом
use IO:: Socket;
$Server_port = 1000;
#создаемсокет
$server = IO:: Socket:: INET-> new(
LocalPort => $Server_port,
Proto => " udp" )
or die " Couldn't start UDP-server\n";
$MAX_TO_READ = 1024;
#если общее количество принятой информации не достигло 1024 байта, то
#читаемв $datagram
while ( $server -> recv($datagram, $MAX_TO_READ, $flags) ) {
#печатаем сообщения
print $datagram;
}
#закрываем сокет
close($server);
Комментарий:
С UDP всё намного проще, мы не устанавливаем никакогосоединения, а просто принимаем сообщения по мере поступленияс помощью функции recv(), где первый параметр само принятоесообщение, второй – целое число, максимальная длина сообщения, ну и параметры. Дальше печатаем сообщение и закрываем сокет.
Теперь ты с уверенностью можешь сказать, что ты знаешь, какработать с сокетами в Perl: ). Все программы, написанные выше, будут работать на любой ОС с установленным Perl в базовой комплектации.
А теперь… мы поговорим оболее сложных вещах, чем сокеты: )
Давай попробуем собрать пакет с нуля. Но прежде, чем это сделать нам потребуются некоторые элементарные знания по протоколам.
Собирать будем пакет семейства протоколов TCP/IP, так как это самое сложное, а с UDP я думаю, ты разберёшься и самбез проблем по докам (в сети есть полное описание компонента Net:: RawIP).
Я дам только самую необходимую информацию по TCP/IP. Итак, TCP/IP – это не один протокол, это целых два протокола в одном флаконе IP и TCP.
Каждый из них выполняет свою функцию и не лезет в дела другого.
Так IP нужен для того чтобы сообщение дошло по адресу, то есть это иесть адрес в обычном понимании. В заголовке IP самое главное содержатьсяIP-адреса отправителя и получателя, а также контрольная сумма cheksum, гарантирующая целостность всего пакета, но IP не гарантирует, что пакет вообще дойдёт – этим занимается протокол TCP.
Заголовок протоколаTCP содержит информацию обо всём остальном, именно он устанавливаетсоединение и создаёт иллюзию туннеля при соединении. Итак, в заголовкесодержаться информация о порте отправителя и получателя и куча флагов –seq, ack_seq, urg, ack, psh, rst, syn, fin, doff.
Давай обо всём по порядку:
Seq – 32-битное поле - номер последовательности, служит для контроляполучателя отравителя, чтобы понять с тем ли нужным нам пакетом имеемдело – гарантирует правильную последовательность передачи пакетов –задаём в самом начале случайное значение.
Ack_seq – 32-битное поле, содержимое которого косвенно определяетколичество принятых данных, служит опять же для уверенности в том, что всё хорошо – высчитывается нами.
Urg – Флаг, единичное значение которого говорит о том, что мы имеемдело с так называемыми «важными» данными, которые обслуживаются вне очереди что это за данные сказано в urg_ptr.
Ack – флаг, если единичное значение, то TCP-пакет содержит вполе ack_seq верные данные.
Psh – флаг, если единица, то данные, содержащиеся в пакете должныбыть немедленно переданы прикладной программе, для которой ониадресованы.
RST –флаг, устанавливающийся в единицу в TCP-пакете, отправляемомв ответ на получение неверного TCP-пакета. Так же запрос на переустановку TCP-соединения.
SYN – флаг, если единица, значит просим об установлении соединения.
FIN – если единица, значит запрос на закрытие соединения.
Doff – смещение данных – 4-битное поле, содержащие длину TCP-заголовка, нужно для определения начала самих данных – выщипывается автоматомнашим модулем.
Так же в TCP-заголовке есть своя checksum для полного контроля над пакетом.
Вот основные параметры TCPIP заголовков – ведь просто, правда?: )
Теперь рассмотрим процесс установления соединения между получателеми отправителем. Итак, пусть хост A хочет активно слиться в едином экстазес хостом B. Что же ему поможет в его нелёгком деле, а поможет ему флаг SYN, вот схема:
A ---> { SYN = 1, seq = 1000 (random значение 32-бита), ldata (длинна данных) = 0} ------> B
Если всё в порядке и наша дама B узнала хахаля A и он ей
понравился (порт открыт – давай стыковку: )), то идёт пакет:
B ---> SYN = 1 (даёт разрешение на посадку), ACK = 1(вас поняла, даю подтверждение правильности SEQ), ack_seq = 1000 + 1(это чтобы потом тебя милый узнать), seq = 3000 (ещё один random, так же для якоря на нашего хахаля), ldata = 0 ----> A
И чтобы закончить слияние нужно что сделать?
Правильно само действие – закончить, то есть начать: ):
A ---> ACK = 1(всё верно – даю стыковку), ack_seq = 3000 + 1(подтверждение на правильность места, туда ли стыкуемся? ), seq = 1001 (номер последовательности – да, это я милая: )), ldata = 0 ---> B
Всё стыковка прошла нормально – есть контакт! Теперь нужно
сделать самое главное - влить в крошку данные и принять от
неё их, вот так это происходит:
A ----> ACK = 1 (всё нормально, понял вас, продолжаю полёт), seq = 1001, ack_seq = 3001, ldata (длинна данных в сообщение) = 50 ---> B
Обрати внимание на длину данных и в следующей схеме на
номер подтверждения и номер последовательности:
B ----> ACK = 1(всё впорядке), seq = 3001 (предыдущий номер подтверждения), ack_seq = 1001 + ldata = 1051(это теперь номер подтверждения), ldata = 80 (вечно ни болтают, когда не нужно: )) ---> A
И, наконец, ещё одна итерация, чтобы всё понять окончательно:
A ---> ACK = 1, seq = 1051, ack_seq = 3001 + 80(ldata)= 3081, ldata = 0 ----> B
Данные перелили и осталось только закончить эту оргию, делается это так:
A ----> ACK = 1, FIN = 1, seq = 1051, ack_seq = 3081, ldata = 0 ---> B
Теперь обязательно обрати внимание на поле ack_seq в следующем
пакете – оно увеличилось на 1, хотя данных не было:
B ---> ACK = 1, seq = 3081, ack_seq = 1051 + 1 = 1052, ldata = 0 ---> A
Всё я этих самых пор A не имеет глосса и сказать ничего не может, однако
B ещё имеет пару претензий по качеству проведенного разговора и хочет
поболтать и после чего заткнуться, окончательно послав флаг FIN, однако
B может не сказать ничего, но рассмотрим всё таки случай по сложнее:
B ----> seq = 3081, ack_seq =1052, ack = 1, ldata = 40 ----> A
A ----> ack = 1, seq = 1052, ack_seq = 3081 + 40 = 3121, ldata = 0(приёмпрошёлнормально – ятебяуслышалдетка: )) ---> B
B ---> seq = 3121, ack_seq = 1052, ack = 1, FIN = 1, ldata = 0 (запросназакрытие) --> A
A ----> seq = 1052, ack_seq = 3121 + 1 = 3122, ACK = 1, ldata = 0(всё - закончили окончательно)
Вот такое нелёгкое оказывается дело – TCPIP: )) – это вам не сокеты юзать.
Всё осталось теперь всё это дело накодить, чем и займёмся.
Например, рассмотрим запрос на соединение хоста
A на хост B, используя компонент Net:: RawIP:
Запрос на соединение
#! /usr/bin/perl
use Net:: RawIP;
# это A, может быть и имя типа www. cdswom. net
$client = " 10. 4. 2. 32";
# это B
$server = " 10. 4. 2. 19";
#создаём новый объект
$packet = new Net:: RawIP;
#формируем в объекте нужные поля
$packet-> set({ip => {saddr => $client, daddr => $server},
tcp => {source => 3000, dest => 1000, seq => 1234567890, psh => 1, syn => 1}});
#посылаем объект
$packet-> send;
Комментарии:
Ну что, какой ещё язык сможет собрать пакет в10 строчек? Вот и я говорю Perl рулит: ). Итак, первое
что мы делаем – это инициализируем пакет $packetстандартным методом, потом вызываем конструктор set, о нём поподробней:
Сначала вызываем конструктор для заголовка ip, где:
Saddr – ip-адрес отправителя, да, дорогой мой, можно сделатьспуфинг – это же гайка, просто скажи кого ты хочешь подставить: )
Daddr – адрес получателя, куда шлём собственно.
Всё больше нам в этом пакете ничего не надо –всё остальное за нас сделает модуль.
Следующим параметром идёт tcp и конструктор для него, но может с таким же успехом идти и udp, но мы всё жеговорим о tcp:
Source – порт от куда отправляют сообщение, туда же придётответ, так что снифер надо настраивать именно на этот порт.
Dest – удалённый порт куда идёт пакет
Seq – наш начальный номер последовательности, как я сказал любой.
Syn – устанавливаем флаг в еденицу, то есть говорим, что хотимдобиться подключения и флаг psh - говорит о том, что немедленнои не фиг ставить в очередь: ) – необязательный параметр.
Всё после этого сервер должен послать ответ, формат ты уже знаешьосталось поймать ответ это делаеться очень просто с помощью функции pcapinit:
$filter = " src port 3000";
$device = “eth0”; # идентификатор сетевой карточки в системе, если одна это и есть
$psize = 1500; # размер пакета на приём(максимальный)
$timeout = 500; # сколько ждать ответа, привета
$ans = new Net:: RawIP; #инициализируем новый объект
my $pcap = $ans-> pcapinit($device, $filter1, $psize, $timeout); #вызываемфункцию
После того как прошёл тайм аут и мы получили пакет необходимовыбить из него какие-то параметры делается это с помощьюметода get:
($tos, $urg, $ack, $psh, $rst, $syn, $fin) = $ans-> get({
ip => [qw(tos)],
tcp => [qw(psh syn urg ack rst fin)]
});
Порядок не важен, главное названия. Ну и всё – соответствующиепеременные содержат соответствующие значения и делай с ними, что хочешь: ).
|