[Delphi] SOCKS4 Server

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by Kandi, 23 Feb 2013.

  1. Kandi

    Kandi Member

    Joined:
    18 Nov 2009
    Messages:
    344
    Likes Received:
    17
    Reputations:
    0
    Решил написать собственный прокси сервер SOCKS4. Написал два потока:
    Code:
    type
      TSocksMethodSel = record
        Version   : Byte;
        nMethods  : Byte;
        Methods   : array [0..255] of Byte;
      end;
    
    type
      TSocks4Request = record
        ucVersion : Byte;
        ucCommand : Byte;
        wDestPort : Word;
        dwDestIp  : DWORD;
      end;
    

    Listener:
    Code:
    function ListenerExecute(Parametr: Pointer): Integer;
    var
      _listenSock, _clientSock  : TSocket;
      _listenAddr, _clientAddr  : sockaddr_in;
      _size                     : Integer;
      _ExeceuteHandle           : DWORD;
      _ClientIP                 : String;
    begin
      Result := 0;
      try
        _listenSock := socket(AF_INET, SOCK_STREAM, 0);
        try
          if _listenSock <> INVALID_SOCKET then
            begin
              ZeroMemory(@_listenAddr, SizeOf(_listenAddr));
              _listenAddr.sin_family := AF_INET;
              _listenAddr.sin_port := htons(CONTROL_PORT);
              _listenAddr.sin_addr.S_addr := htonl(INADDR_ANY);
              if (bind    (_listenSock, _listenAddr, SizeOf(_listenAddr)) <> INVALID_SOCKET) AND
                 (listen  (_listenSock, SOMAXCONN)                        <> INVALID_SOCKET) then
                begin
                  while 1 = 1 do
                    begin
                      _size := SizeOf(_clientAddr);
                      _clientSock := accept(_listenSock, @_clientAddr, @_size);
                      if _clientSock <> INVALID_SOCKET then
                        begin
                                _ExeceuteHandle := BeginThread(nil, 0, @ConnectExecute, Pointer(_clientSock), 0, _ExeceuteHandle);
                                if _ExeceuteHandle <> 0 then
                                  CloseHandle(_ExeceuteHandle);
                        end; // _clientSock <> INVALID_SOCKET
                      Sleep(300);
                    end; // while 1 = 1
                end; // bind, listen
            end; // _listenSock <> INVALID_SOCKET
        finally
          closesocket(_listenSock);
        end;
      finally
        ExitThread(0);
      end;
    end;
    


    Connect:
    Code:
    function ConnectExecute(Parametr: Pointer): Integer;
    var
      _m                        : TSocksMethodSel;
      _req4                     : TSocks4Request;
      _clientSock, _tunnelSock  : TSocket;
      _tunnelAddr               : sockaddr_in;
      _tv                       : TTimeVal;
      _fset                     : TFDSet;
      _recv_len                 : Integer;
      _buffer                   : array [0..8191] of Byte; // 8Kb
      _mode                     : Integer;
    begin
      Result := 0;
      _clientSock := Integer(Parametr);
      try
        if recv(_clientSock, _m, 2, MSG_PEEK) > 0 then
          begin
            if _m.Version = 4 then
              begin
                recv(_clientSock, _req4, SizeOf(TSocks4Request), 0);
                if _req4.ucCommand = 1 then
                  begin
                    ZeroMemory(@_tunnelAddr, SizeOf(_tunnelAddr));
                    _tunnelAddr.sin_port := _req4.wDestPort;
                    Move(_req4.dwDestIp, _tunnelAddr.sin_addr, SizeOf(_tunnelAddr.sin_addr));
                    _tunnelAddr.sin_family := AF_INET;
                    _tunnelSock := socket(AF_INET, SOCK_STREAM, 0);
                    // Connect _tunnelSock
                    if connect(_tunnelSock, _tunnelAddr, SizeOf(_tunnelAddr)) <> INVALID_SOCKET Then
                      begin // success
                        _req4.ucCommand := $5A;  // success
                      end
                      else
                      begin
                        _req4.ucCommand := $5B; // General Failure reporting in
                      end;
                    _req4.ucVersion := $00; // null-байт
                    _req4.wDestPort := $00;
                    _req4.dwDestIp := $00;
                    // поле 1: null-байт, 1 байт
                    // поле 2: код ответа, 1 байт:
                    //  - 0x5a = запрос предоставлен
                    //  - 0x5b = запрос отклонён или ошибочен
                    // поле 3: 2 произвольных байта, должны быть проигнорированы
                    // поле 4: 4 произвольных байта, должны быть проигнорированы
                    send(_clientSock, _req4, SizeOf(TSocks4Request), 0); // 8 byte return
    
                    _mode := 1;
                    setsockopt(_tunnelSock, IPPROTO_IP, TCP_NODELAY, @_mode, SizeOf(Integer));
                    // now tunneling everythink
                    _tv.tv_sec := 5000;
                    _tv.tv_usec := 0;
                    try
                      while 1 = 1 do
                        begin
                          Sleep(100); // zzzzz....
                          // waiting for incoming data
                          FD_ZERO(_fset);
                          FD_SET(_clientSock, _fset);
                          FD_SET(_tunnelSock, _fset);
                          if select(0, @_fset, nil, nil, nil) <> SOCKET_ERROR then
                            begin
                              if FD_ISSET(_tunnelSock, _fset) then
                                begin // data on the recvsock
                                  ZeroMemory(@_buffer, SizeOf(_buffer));
                                  _recv_len := recv(_tunnelSock, _buffer, SizeOf(_buffer), 0);
                                  if _recv_len = SOCKET_ERROR then
                                    break; // error ?
                                  if (_recv_len <> SOCKET_ERROR) and (_recv_len > 0) then
                                    send(_clientSock, _buffer, _recv_len, 0)
                                  else
                                    break;
                                end;
                              if FD_ISSET(_clientSock, _fset) then
                                begin //data on the recvsock
                                  ZeroMemory(@_buffer, SizeOf(_buffer));
                                  _recv_len := recv(_clientSock, _buffer, SizeOf(_buffer), 0);
                                  if _recv_len = SOCKET_ERROR then
                                    break; // error ?
                                  if (_recv_len <> SOCKET_ERROR) and (_recv_len > 1) then
                                    if _buffer[0] = $00 then
                                      send(_tunnelSock, _buffer[1], _recv_len - 1, 0)
                                    else
                                      send(_tunnelSock, _buffer, _recv_len, 0)
                                  else
                                    break;
                                end;
                            end; // if select(0...
                        end;
                    finally
                      if _req4.ucCommand = 90 then
                        begin
                          _mode := 0;
                          setsockopt(_tunnelSock, IPPROTO_TCP, TCP_NODELAY, @_mode, sizeof(integer));
                          // close
                          closesocket(_tunnelSock);
                        end;
                    end;
                  end; // if _req4.unCommand = 1
              end; // if _m.Version = 4 -> SOCKS4
          end; // if recv(_clientSock....) > 0
      finally
        ZeroMemory(@_buffer, SizeOf(_buffer));
        closesocket(_clientSock);
        ExitThread(0);
      end;
    end;
    
    Всё вроде бы ничего, только работает как то странно. Сам сервер под debug работает вроде бы нормально, отсылает байты клиенту, но например клиент почему-то их не получает. Клиент создал на Overbytes ICS. Очень конечно интересует как бы выявить этот баг!?
    Так же интересует вопрос по утечке памяти, как бы проверить его на утечку памяти?!
    P.S. Вообще хотелось бы послушать замечания. Может кто-то подскажет где я накосячил здесь?! Где-то может неправильно работаю с Socket.