⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 psitcpclient.pas

📁 一个delphi的p2p控件的源代码
💻 PAS
字号:
unit PsiTCPClient;

//******************************************************************************
// The original software is under
// Copyright (c) 1993 - 2000, Chad Z. Hower (Kudzu)
//   and the Indy Pit Crew - http://www.nevrona.com/Indy/
//
// Amended : November 2000, by Michael M. Michalak MACS for use with
// MorphTek.com Inc Peer to Peer Open Source Components - http://www.morphtek.com
//
//******************************************************************************

interface

uses
  Classes,
  PsiGlobal, PsiSocks, PsiTCPConnection;

type
  TProceduralEvent = procedure of object;

  TPsiTCPClient = class(TPsiTCPConnection)
  protected
    FHost: string;
    FOnConnected: TNotifyEvent;
    FPort: Integer;
    FSocksInfo: TSocksInfo;
    FUseNagle: boolean;
    //
    procedure DoOnConnected; virtual;
    procedure SetSocksInfo(ASocks : TSocksInfo);
  public
    procedure Connect; virtual;
    function ConnectAndGetAll: string; virtual;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Host: string read FHost write FHost;
    property OnConnected: TNotifyEvent read FOnConnected write FOnConnected;
    property Port: integer read FPort write FPort;
    property UseNagle: boolean read FUseNagle write FUseNagle default True;
    property SocksInfo: TSocksInfo read FSocksInfo write SetSocksInfo;
  end;

implementation

uses
  PsiComponent, PsiException, PsiStack, PsiResourceStrings, PsiStackConsts,
  SysUtils;

{ TPsiTCPClient }

procedure TPsiTCPClient.Connect;
var
  req: TPsiSocksRequest;
  res: TPsiSocksResponse;
  len, pos: Integer;
  tempBuffer: Array [0..255] of Byte;
  ReqestedAuthMethod,
  ServerAuthMethod: Byte;
  tempPort: Word;
begin
  // Do not call Connected here, it will call CheckDisconnect
  if Binding.HandleAllocated then begin
    raise EPsiAlreadyConnected.Create(RSAlreadyConnected);
  end;

  ResetConnection;
  Binding.AllocateSocket; try
    // socks support
    case SocksInfo.Version of
      svSocks4, svSocks4A, svSocks5: begin
        Binding.Port := SocksInfo.Port;
        {We put this GStack.IsIP test in twice because for socks, we do not
        resolve the Host at all and for non-socks support, we resolve the host directly}
        if not GStack.IsIP(SocksInfo.Host) then begin
          DoStatus(hsResolving, [SocksInfo.Host]);
        end;
        Binding.IP := GStack.ResolveHost(SocksInfo.Host);
        //fsPeerAddress := '';
      end else begin
     	  Binding.Port := Port;
        if not GStack.IsIP(Host) then begin
          DoStatus(hsResolving, [Host]);
        end;
        Binding.IP := GStack.ResolveHost(Host);
      end;
    end;
    if not UseNagle then begin
      GStack.CheckForSocketError(Binding.SetSockOpt(Psi_IPPROTO_TCP, Psi_TCP_NODELAY, '1111', 4));
    end;
    GStack.CheckForSocketError(Binding.Connect);
  except
    On E: Exception do begin
      // Need to mask, so a new error wont kick out of this
      try
        Disconnect;
      except end;
      raise;
    end;
  end;
  Binding.SetPeer(Binding.IP,Binding.Port);
  DoStatus(hsConnecting, [Binding.IP]);

  case SocksInfo.Version of
    svSocks4, svSocks4A: begin
      req.Version := 4;
      req.OpCode  := 1;
      req.Port := GStack.WSHToNs(Port);
      if SocksInfo.Version = svSocks4A then begin
        req.IpAddr := GStack.StringToTInAddr('0.0.0.1');
      end else begin
        req.IpAddr := GStack.StringToTInAddr(GStack.ResolveHost(Host));
      end;
      req.UserId  := SocksInfo.UserID;
      len := Length(req.UserId); // calc the len of username
      req.UserId[len + 1] := #0;
      if SocksInfo.Version = svSocks4A then begin
        Move(Host[1], req.UserId[len + 2], Length(Host));
        len := len + 1 + Length(Host);
        req.UserId[len + 1] := #0;
      end;
      len := 8 + len + 1;        // calc the len of request
      WriteBuffer(req, len);
      try
        ReadBuffer(res, 8);
//        s := ExtractXBytesFromBuffer(1);
      except
        On E: Exception do begin
          raise;
        end;
      end;
      case res.OpCode of
        90: ;// request granted, do nothing
        91: raise EPsiException.Create(RSSocksRequestFailed);
        92: raise EPsiException.Create(RSSocksRequestServerFailed);
        93: raise EPsiException.Create(RSSocksRequestIdentFailed);
        else
            raise EPsiException.Create(RSSocksUnknownError);
      end;
    end;
    svSocks5: begin
      // defined in rfc 1928
      if SocksInfo.Authentication = saNoAuthentication then
        tempBuffer[2] := $0   // No authentication
      else
        tempBuffer[2] := $2;  // Username password authentication

      ReqestedAuthMethod := tempBuffer[2];
      tempBuffer[0] := $5;     // socks version
      tempBuffer[1] := $1;     // number of possible authentication methods

      len := 2 + tempBuffer[1];
      WriteBuffer(tempBuffer, len);

      try
        ReadBuffer(tempBuffer, 2);  // Socks server sends the selected authentication method
      except
        On E: Exception do begin
          raise EPsiException.Create(RSSocksServerRespondError);
        end;
      end;

      ServerAuthMethod := tempBuffer[1];
      if (ServerAuthMethod <> ReqestedAuthMethod) or (ServerAuthMethod = $FF) then
        raise EPsiException.Create(RSSocksAuthMethodError);

      // Authentication process
      if SocksInfo.Authentication = saUsernamePassword then begin
        tempBuffer[0] := 1; // version of subnegotiation
        tempBuffer[1] := Length(SocksInfo.UserID);
        pos := 2;
        if Length(SocksInfo.UserID) > 0 then
          Move(SocksInfo.UserID[1], tempBuffer[pos], Length(SocksInfo.UserID));
        pos := pos + Length(SocksInfo.UserID);
        tempBuffer[pos] := Length(SocksInfo.Password);
        pos := pos + 1;
        if Length(SocksInfo.Password) > 0 then
          Move(SocksInfo.Password[1], tempBuffer[pos], Length(SocksInfo.Password));
        pos := pos + Length(SocksInfo.Password);

        WriteBuffer(tempBuffer, pos); // send the username and password
        try
          ReadBuffer(tempBuffer, 2);    // Socks server sends the authentication status
        except
          On E: Exception do begin
            raise EPsiException.Create(RSSocksServerRespondError);
          end;
        end;

        if tempBuffer[1] <> $0 then
          raise EPsiException.Create(RSSocksAuthError);
      end;

      // Connection process
      tempBuffer[0] := $5;   // socks version
      tempBuffer[1] := $1;   // connect method
      tempBuffer[2] := $0;   // reserved
      // for now we stick with domain name, must ask Chad how to detect
      // address type
      tempBuffer[3] := $3;   // address type: IP V4 address: X'01'
                             //               DOMAINNAME:    X'03'                             //               IP V6 address: X'04'
      // host name
      tempBuffer[4] := Length(Host);
      pos := 5;
      if Length(Host) > 0 then
        Move(Host[1], tempBuffer[pos], Length(Host));
      pos := pos + Length(Host);
      // port
      tempPort := GStack.WSHToNs(Port);
      Move(tempPort, tempBuffer[pos], SizeOf(tempPort));
      pos := pos + 2;

      WriteBuffer(tempBuffer, pos); // send the connection packet
      try
        ReadBuffer(tempBuffer, 5);    // Socks server replies on connect, this is the first part
      except
        On E: Exception do begin
          raise EPsiException.Create(RSSocksServerRespondError);
        end;
      end;

      case tempBuffer[1] of
        0: ;// success, do nothing
        1: raise EPsiException.Create(RSSocksServerGeneralError);
        2: raise EPsiException.Create(RSSocksServerPermissionError);
        3: raise EPsiException.Create(RSSocksServerNetUnreachableError);
        4: raise EPsiException.Create(RSSocksServerHostUnreachableError);
        5: raise EPsiException.Create(RSSocksServerConnectionRefusedError);
        6: raise EPsiException.Create(RSSocksServerTTLExpiredError);
        7: raise EPsiException.Create(RSSocksServerCommandError);
        8: raise EPsiException.Create(RSSocksServerAddressError);
        else
           raise EPsiException.Create(RSSocksUnknownError);
      end;

      // type of destiantion address is domain name
      case tempBuffer[3] of
        // IP V4
        1: len := 4 + 2; // 4 is for address and 2 is for port length
        // FQDN
        3: len := tempBuffer[4] + 2; // 2 is for port length
        // IP V6
        4: len := 16 + 2; // 16 is for address and 2 is for port length
      end;

      try
        ReadBuffer(tempBuffer[5], len-1);    // Socks server replies on connect, this is the seconf part
      except
        On E: Exception do begin
          raise EPsiException.Create(RSSocksServerRespondError);
        end;
      end;
    end;
  end;
  DoStatus(hsConnected,[Binding.IP]);
  DoOnConnected;
end;

function TPsiTCPClient.ConnectAndGetAll: string;
begin
  Connect; try
    result := AllData;
  finally Disconnect; end;
end;

constructor TPsiTCPClient.Create(AOwner: TComponent);
begin
  inherited;
  FSocksInfo := TSocksInfo.Create;
	FUseNagle := True;
end;

destructor TPsiTCPClient.Destroy;
begin
  FreeAndNil(FSocksInfo);
  inherited;
end;

procedure TPsiTCPClient.DoOnConnected;
begin
  if assigned(OnConnected) then begin
    OnConnected(Self);
  end;
end;

procedure TPsiTCPClient.SetSocksInfo(ASocks: TSocksInfo);
begin
  FSocksInfo.Assign(ASocks);
end;

end.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -