📄 psitcpclient.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 + -