1033 lines
35 KiB
Plaintext
1033 lines
35 KiB
Plaintext
|
{
|
||
|
$Project$
|
||
|
$Workfile$
|
||
|
$Revision$
|
||
|
$DateUTC$
|
||
|
$Id$
|
||
|
|
||
|
This file is part of the Indy (Internet Direct) project, and is offered
|
||
|
under the dual-licensing agreement described on the Indy website.
|
||
|
(http://www.indyproject.org/)
|
||
|
|
||
|
Copyright:
|
||
|
(c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
|
||
|
|
||
|
|
||
|
$Log$
|
||
|
|
||
|
|
||
|
Rev 1.38 11/15/2004 11:59:12 PM JPMugaas
|
||
|
Hopefully, this should handle IPv6 addresses in SOCKS bind and listen.
|
||
|
|
||
|
|
||
|
Rev 1.37 11/12/2004 11:30:18 AM JPMugaas
|
||
|
Expansions for IPv6.
|
||
|
|
||
|
|
||
|
Rev 1.36 11/11/2004 10:25:24 PM JPMugaas
|
||
|
Added OpenProxy and CloseProxy so you can do RecvFrom and SendTo functions
|
||
|
from the UDP client with SOCKS. You must call OpenProxy before using
|
||
|
RecvFrom or SendTo. When you are finished, you must use CloseProxy to close
|
||
|
any connection to the Proxy. Connect and disconnect also call OpenProxy and
|
||
|
CloseProxy.
|
||
|
|
||
|
|
||
|
Rev 1.35 11/11/2004 3:42:50 AM JPMugaas
|
||
|
Moved strings into RS. Socks will now raise an exception if you attempt to
|
||
|
use SOCKS4 and SOCKS4A with UDP. Those protocol versions do not support UDP
|
||
|
at all.
|
||
|
|
||
|
|
||
|
Rev 1.34 11/10/2004 10:55:58 PM JPMugaas
|
||
|
UDP Association bug fix - we now send 0's for IP address and port.
|
||
|
|
||
|
|
||
|
Rev 1.33 11/10/2004 10:38:42 PM JPMugaas
|
||
|
Bug fixes - UDP with SOCKS now works.
|
||
|
|
||
|
|
||
|
Rev 1.32 11/10/2004 9:42:54 PM JPMugaas
|
||
|
1 in a reserved position should be 0 in a UDP request packet.
|
||
|
|
||
|
|
||
|
Rev 1.31 11/9/2004 8:18:00 PM JPMugaas
|
||
|
Attempt to add SOCKS support in UDP.
|
||
|
|
||
|
|
||
|
Rev 1.30 03/07/2004 10:08:22 CCostelloe
|
||
|
Removed spurious code that generates warning
|
||
|
|
||
|
|
||
|
Rev 1.29 6/9/04 7:44:44 PM RLebeau
|
||
|
various ReadBytes() tweaks
|
||
|
|
||
|
updated MakeSocks4Request() to call AIOHandler.WriteBufferCancel() on error.
|
||
|
|
||
|
|
||
|
Rev 1.28 2004.05.20 1:39:58 PM czhower
|
||
|
Last of the IdStream updates
|
||
|
|
||
|
|
||
|
Rev 1.27 2004.05.20 9:19:24 AM czhower
|
||
|
Removed unused var
|
||
|
|
||
|
|
||
|
Rev 1.26 5/19/2004 10:44:42 PM DSiders
|
||
|
Corrected spelling for TIdIPAddress.MakeAddressObject method.
|
||
|
|
||
|
|
||
|
Rev 1.25 5/19/2004 2:44:40 PM JPMugaas
|
||
|
Fixed compiler warnings in TIdSocksInfo.Listen.
|
||
|
|
||
|
|
||
|
Rev 1.24 5/8/2004 3:45:34 PM BGooijen
|
||
|
Listen works in Socks 4 now
|
||
|
|
||
|
|
||
|
Rev 1.23 5/7/2004 4:52:44 PM JPMugaas
|
||
|
Bind in SOCKS4 should work a bit better. There's still some other work that
|
||
|
needs to be done on it.
|
||
|
|
||
|
|
||
|
Rev 1.22 5/7/2004 8:54:54 AM JPMugaas
|
||
|
Attempt to add SOCKS4 bind.
|
||
|
|
||
|
|
||
|
Rev 1.21 5/7/2004 7:43:24 AM JPMugaas
|
||
|
Checked Bas's changes.
|
||
|
|
||
|
|
||
|
Rev 1.20 5/7/2004 5:53:20 AM JPMugaas
|
||
|
Removed some duplicate code to reduce the probability of error.
|
||
|
|
||
|
|
||
|
Rev 1.19 5/7/2004 1:44:12 AM BGooijen
|
||
|
Bind
|
||
|
|
||
|
|
||
|
Rev 1.18 5/6/2004 6:47:04 PM JPMugaas
|
||
|
Attempt to work on bind further.
|
||
|
|
||
|
|
||
|
Rev 1.16 5/6/2004 5:32:58 PM JPMugaas
|
||
|
Port was being mangled because the compiler was assuming you wanted a 4 byte
|
||
|
byte order instead of only a two byte byte order function.
|
||
|
IP addresses are better handled. At least I can connect again.
|
||
|
|
||
|
|
||
|
Rev 1.15 5/5/2004 2:09:40 PM JPMugaas
|
||
|
Attempt to reintroduce bind and listen functionality for FTP.
|
||
|
|
||
|
|
||
|
Rev 1.14 2004.03.07 11:48:44 AM czhower
|
||
|
Flushbuffer fix + other minor ones found
|
||
|
|
||
|
|
||
|
Rev 1.13 2004.02.03 4:16:52 PM czhower
|
||
|
For unit name changes.
|
||
|
|
||
|
|
||
|
Rev 1.12 2/2/2004 2:33:04 PM JPMugaas
|
||
|
Should compile better.
|
||
|
|
||
|
|
||
|
Rev 1.11 2/2/2004 12:23:16 PM JPMugaas
|
||
|
Attempt to fix the last Todo concerning IPv6.
|
||
|
|
||
|
|
||
|
Rev 1.10 2/2/2004 11:43:08 AM BGooijen
|
||
|
DotNet
|
||
|
|
||
|
|
||
|
Rev 1.9 2/2/2004 12:00:08 AM BGooijen
|
||
|
Socks 4 / 4A working again
|
||
|
|
||
|
|
||
|
Rev 1.8 2004.01.20 10:03:34 PM czhower
|
||
|
InitComponent
|
||
|
|
||
|
|
||
|
Rev 1.7 1/11/2004 10:45:56 PM BGooijen
|
||
|
Socks 5 works on D7 now, Socks 4 almost
|
||
|
|
||
|
|
||
|
Rev 1.6 2003.10.11 5:50:34 PM czhower
|
||
|
-VCL fixes for servers
|
||
|
-Chain suport for servers (Super core)
|
||
|
-Scheduler upgrades
|
||
|
-Full yarn support
|
||
|
|
||
|
|
||
|
Rev 1.5 2003.10.01 1:37:34 AM czhower
|
||
|
.Net
|
||
|
|
||
|
|
||
|
Rev 1.4 2003.09.30 7:37:28 PM czhower
|
||
|
Updates for .net
|
||
|
|
||
|
|
||
|
Rev 1.3 4/2/2003 3:23:00 PM BGooijen
|
||
|
fixed and re-enabled
|
||
|
|
||
|
|
||
|
Rev 1.2 2003.01.10 8:21:04 PM czhower
|
||
|
Removed more warnings
|
||
|
|
||
|
|
||
|
Rev 1.1 2003.01.10 7:21:14 PM czhower
|
||
|
Removed warnings
|
||
|
|
||
|
|
||
|
Rev 1.0 11/13/2002 08:58:56 AM JPMugaas
|
||
|
}
|
||
|
unit IdSocks;
|
||
|
|
||
|
interface
|
||
|
|
||
|
{$I IdCompilerDefines.inc}
|
||
|
//we need to put this in Delphi mode to work.
|
||
|
|
||
|
uses
|
||
|
Classes,
|
||
|
IdAssignedNumbers, IdException, IdBaseComponent,
|
||
|
IdComponent, IdCustomTransparentProxy, IdGlobal, IdIOHandler,
|
||
|
IdIOHandlerSocket, IdSocketHandle;
|
||
|
|
||
|
type
|
||
|
EIdSocksUDPNotSupportedBySOCKSVersion = class(EIdException);
|
||
|
TSocksVersion = (svNoSocks, svSocks4, svSocks4A, svSocks5);
|
||
|
TSocksAuthentication = (saNoAuthentication, saUsernamePassword);
|
||
|
|
||
|
const
|
||
|
ID_SOCKS_AUTH = saNoAuthentication;
|
||
|
ID_SOCKS_VER = svNoSocks;
|
||
|
|
||
|
type
|
||
|
TIdSocksInfo = class(TIdCustomTransparentProxy)
|
||
|
protected
|
||
|
FAuthentication: TSocksAuthentication;
|
||
|
FVersion: TSocksVersion;
|
||
|
FUDPSocksAssociation : TIdIOHandlerSocket;
|
||
|
|
||
|
//
|
||
|
function DisasmUDPReplyPacket(const APacket : TIdBytes;
|
||
|
var VHost : String; var VPort : TIdPort; var VIPVersion: TIdIPVersion): TIdBytes;
|
||
|
function MakeUDPRequestPacket(const AData: TIdBytes;
|
||
|
const AHost: String; const APort: TIdPort) : TIdBytes;
|
||
|
function GetEnabled: Boolean; override;
|
||
|
procedure InitComponent; override;
|
||
|
procedure AuthenticateSocks5Connection(AIOHandler: TIdIOHandler);
|
||
|
// This must be defined with an port value that's a word so that we use the 2 byte Network Order byte functions instead
|
||
|
// the 4 byte or 8 byte functions. If we use the wrong byte order functions, we can get a zero port value causing an error.
|
||
|
procedure MakeSocks4Request(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const ARequest : Byte);
|
||
|
procedure MakeSocks5Request(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const ARequest : Byte; var VBuf : TIdBytes; var VLen : Integer);
|
||
|
procedure MakeSocks4Connection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort);
|
||
|
procedure MakeSocks4Bind(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort);
|
||
|
procedure MakeSocks5Connection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
|
||
|
procedure MakeSocks5Bind(AIOHandler: TIdIOHandler; const AHost: string;
|
||
|
const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
|
||
|
procedure MakeConnection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
|
||
|
function MakeSocks4Listen(AIOHandler: TIdIOHandler; const ATimeOut:integer):boolean;
|
||
|
function MakeSocks5Listen(AIOHandler: TIdIOHandler; const ATimeOut:integer):boolean;
|
||
|
|
||
|
//association for UDP
|
||
|
procedure MakeSocks5UDPAssociation(AHandle : TIdSocketHandle);
|
||
|
procedure CloseSocks5UDPAssociation;
|
||
|
public
|
||
|
procedure Assign(ASource: TPersistent); override;
|
||
|
destructor Destroy; override;
|
||
|
procedure Bind(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
|
||
|
function Listen(AIOHandler: TIdIOHandler; const ATimeOut:integer):boolean;override;
|
||
|
procedure OpenUDP(AHandle : TIdSocketHandle; const AHost: string = ''; const APort: TIdPort = 0; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
|
||
|
function RecvFromUDP(AHandle: TIdSocketHandle; var ABuffer : TIdBytes;
|
||
|
var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion;
|
||
|
AMSec: Integer = IdTimeoutDefault): Integer; override;
|
||
|
procedure SendToUDP(AHandle: TIdSocketHandle; const AHost: string;
|
||
|
const APort: TIdPort; const AIPVersion: TIdIPVersion; const ABuffer : TIdBytes); override;
|
||
|
procedure CloseUDP(AHandle: TIdSocketHandle); override;
|
||
|
published
|
||
|
property Authentication: TSocksAuthentication read FAuthentication write FAuthentication default ID_SOCKS_AUTH;
|
||
|
property Host;
|
||
|
property Password;
|
||
|
property Port default IdPORT_SOCKS;
|
||
|
property IPVersion;
|
||
|
property Username;
|
||
|
property Version: TSocksVersion read FVersion write FVersion default ID_SOCKS_VER;
|
||
|
property ChainedProxy;
|
||
|
End;//TIdSocksInfo
|
||
|
|
||
|
implementation
|
||
|
|
||
|
uses
|
||
|
IdResourceStringsCore, IdExceptionCore, IdIPAddress, IdStack,
|
||
|
IdTCPClient,
|
||
|
IdIOHandlerStack, SysUtils;
|
||
|
|
||
|
{ TIdSocksInfo }
|
||
|
|
||
|
procedure TIdSocksInfo.Assign(ASource: TPersistent);
|
||
|
var
|
||
|
LSource: TIdSocksInfo;
|
||
|
begin
|
||
|
if ASource is TIdSocksInfo then begin
|
||
|
LSource := TIdSocksInfo(ASource);
|
||
|
FAuthentication := LSource.Authentication;
|
||
|
FVersion := LSource.Version;
|
||
|
end;
|
||
|
// always allow TIdCustomTransparentProxy to assign its properties as well
|
||
|
inherited Assign(ASource);
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.MakeSocks4Request(AIOHandler: TIdIOHandler; const AHost: string;
|
||
|
const APort: TIdPort; const ARequest : Byte);
|
||
|
var
|
||
|
LIpAddr: String;
|
||
|
LBufferingStarted: Boolean;
|
||
|
begin
|
||
|
LBufferingStarted := not AIOHandler.WriteBufferingActive;
|
||
|
if LBufferingStarted then begin
|
||
|
AIOHandler.WriteBufferOpen;
|
||
|
end;
|
||
|
try
|
||
|
AIOHandler.Write(Byte(4)); // Version
|
||
|
AIOHandler.Write(ARequest); // Opcode
|
||
|
AIOHandler.Write(Word(APort)); // Port
|
||
|
|
||
|
if Version = svSocks4A then begin
|
||
|
LIpAddr := '0.0.0.1'; {Do not Localize}
|
||
|
end else begin
|
||
|
LIpAddr := GStack.ResolveHost(AHost,Id_IPv4);
|
||
|
end;
|
||
|
|
||
|
AIOHandler.Write(Byte(IndyStrToInt(Fetch(LIpAddr,'.'))));// IP
|
||
|
AIOHandler.Write(Byte(IndyStrToInt(Fetch(LIpAddr,'.'))));// IP
|
||
|
AIOHandler.Write(Byte(IndyStrToInt(Fetch(LIpAddr,'.'))));// IP
|
||
|
AIOHandler.Write(Byte(IndyStrToInt(Fetch(LIpAddr,'.'))));// IP
|
||
|
|
||
|
AIOHandler.Write(Username);
|
||
|
AIOHandler.Write(Byte(0));// Username
|
||
|
|
||
|
if Version = svSocks4A then begin
|
||
|
AIOHandler.Write(AHost);
|
||
|
AIOHandler.Write(Byte(0));// Host
|
||
|
end;
|
||
|
|
||
|
if LBufferingStarted then begin
|
||
|
AIOHandler.WriteBufferClose; //flush everything
|
||
|
end;
|
||
|
except
|
||
|
if LBufferingStarted then begin
|
||
|
AIOHandler.WriteBufferCancel; //cancel everything
|
||
|
end;
|
||
|
raise;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.MakeSocks4Connection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort);
|
||
|
var
|
||
|
LResponse: TIdBytes;
|
||
|
begin
|
||
|
MakeSocks4Request(AIOHandler, AHost, APort,$01); //connect
|
||
|
AIOHandler.ReadBytes(LResponse, 8, False);
|
||
|
case LResponse[1] of // OpCode
|
||
|
90: ;// request granted, do nothing
|
||
|
91: raise EIdSocksRequestFailed.Create(RSSocksRequestFailed);
|
||
|
92: raise EIdSocksRequestServerFailed.Create(RSSocksRequestServerFailed);
|
||
|
93: raise EIdSocksRequestIdentFailed.Create(RSSocksRequestIdentFailed);
|
||
|
else raise EIdSocksUnknownError.Create(RSSocksUnknownError);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.MakeSocks5Request(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const ARequest : Byte; var VBuf : TIdBytes; var VLen : Integer);
|
||
|
var
|
||
|
LIP : TIdIPAddress;
|
||
|
LAddr: TIdBytes;
|
||
|
begin
|
||
|
// Connection process
|
||
|
VBuf[0] := $5; // socks version
|
||
|
VBuf[1] := ARequest; //request method
|
||
|
VBuf[2] := $0; // reserved
|
||
|
|
||
|
// address type: IP V4 address: X'01' {Do not Localize}
|
||
|
// DOMAINNAME: X'03' {Do not Localize}
|
||
|
// IP V6 address: X'04' {Do not Localize}
|
||
|
|
||
|
LIP := TIdIPAddress.MakeAddressObject(AHost);
|
||
|
if Assigned(LIP) then
|
||
|
begin
|
||
|
try
|
||
|
if LIP.AddrType = Id_IPv6 then begin
|
||
|
VBuf[3] := $04; //IPv6 address
|
||
|
end else begin
|
||
|
VBuf[3] := $01; //IPv4 address
|
||
|
end;
|
||
|
LAddr := LIP.HToNBytes;
|
||
|
CopyTIdBytes(LAddr, 0, VBuf, 4, Length(LAddr));
|
||
|
VLen := 4 + Length(LAddr);
|
||
|
finally
|
||
|
FreeAndNil(LIP);
|
||
|
end;
|
||
|
end else
|
||
|
begin
|
||
|
LAddr := ToBytes(AHost);
|
||
|
VBuf[3] := $3; // host name
|
||
|
VBuf[4] := IndyMin(Length(LAddr), 255);
|
||
|
if VBuf[4] > 0 then begin
|
||
|
CopyTIdBytes(LAddr, 0, VBuf, 5, VBuf[4]);
|
||
|
end;
|
||
|
VLen := 5 + VBuf[4];
|
||
|
end;
|
||
|
|
||
|
// port
|
||
|
|
||
|
CopyTIdUInt16(GStack.HostToNetwork(APort), VBuf, VLen);
|
||
|
VLen := VLen + 2;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.MakeSocks5Connection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
|
||
|
var
|
||
|
Lpos: Integer;
|
||
|
LBuf: TIdBytes;
|
||
|
begin
|
||
|
AuthenticateSocks5Connection(AIOHandler);
|
||
|
SetLength(LBuf, 255);
|
||
|
MakeSocks5Request(AIOHandler, AHost, APort, $01, LBuf, Lpos);
|
||
|
|
||
|
LBuf := ToBytes(LBuf, Lpos);
|
||
|
AIOHandler.WriteDirect(LBuf); // send the connection packet
|
||
|
try
|
||
|
AIOHandler.ReadBytes(LBuf, 5, False); // Socks server replies on connect, this is the first part
|
||
|
except
|
||
|
IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError));
|
||
|
end;
|
||
|
|
||
|
case LBuf[1] of
|
||
|
0: ;// success, do nothing
|
||
|
1: raise EIdSocksServerGeneralError.Create(RSSocksServerGeneralError);
|
||
|
2: raise EIdSocksServerPermissionError.Create(RSSocksServerPermissionError);
|
||
|
3: raise EIdSocksServerNetUnreachableError.Create(RSSocksServerNetUnreachableError);
|
||
|
4: raise EIdSocksServerHostUnreachableError.Create(RSSocksServerHostUnreachableError);
|
||
|
5: raise EIdSocksServerConnectionRefusedError.Create(RSSocksServerConnectionRefusedError);
|
||
|
6: raise EIdSocksServerTTLExpiredError.Create(RSSocksServerTTLExpiredError);
|
||
|
7: raise EIdSocksServerCommandError.Create(RSSocksServerCommandError);
|
||
|
8: raise EIdSocksServerAddressError.Create(RSSocksServerAddressError);
|
||
|
else
|
||
|
raise EIdSocksUnknownError.Create(RSSocksUnknownError);
|
||
|
end;
|
||
|
|
||
|
// type of destination address is domain name
|
||
|
case LBuf[3] of
|
||
|
// IP V4
|
||
|
1: Lpos := 4 + 2; // 4 is for address and 2 is for port length
|
||
|
// FQDN
|
||
|
3: Lpos := LBuf[4] + 2; // 2 is for port length
|
||
|
// IP V6
|
||
|
4: Lpos := 16 + 2; // 16 is for address and 2 is for port length
|
||
|
end;
|
||
|
|
||
|
try
|
||
|
// Socks server replies on connect, this is the second part
|
||
|
// RLebeau: why -1?
|
||
|
AIOHandler.ReadBytes(LBuf, Lpos-1, False); // just write it over the first part for now
|
||
|
except
|
||
|
IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError));
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.MakeSocks4Bind(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort);
|
||
|
var
|
||
|
LResponse: TIdBytes;
|
||
|
LClient: TIdTcpClient;
|
||
|
begin
|
||
|
LClient := TIdTCPClient.Create(nil);
|
||
|
try
|
||
|
// SetLength(LResponse, 255);
|
||
|
SetLength(LResponse, 8);
|
||
|
TIdIOHandlerSocket(AIOHandler).TransparentProxy := nil;
|
||
|
LClient.IOHandler := AIOHandler;
|
||
|
LClient.Host := Host;
|
||
|
LClient.Port := Port;
|
||
|
LClient.Connect;
|
||
|
TIdIOHandlerSocket(AIOHandler).TransparentProxy := Self;
|
||
|
MakeSocks4Request(AIOHandler, AHost, APort, $02); //bind
|
||
|
AIOHandler.ReadBytes(LResponse, 2, False);
|
||
|
case LResponse[1] of // OpCode
|
||
|
90: ;// request granted, do nothing
|
||
|
91: raise EIdSocksRequestFailed.Create(RSSocksRequestFailed);
|
||
|
92: raise EIdSocksRequestServerFailed.Create(RSSocksRequestServerFailed);
|
||
|
93: raise EIdSocksRequestIdentFailed.Create(RSSocksRequestIdentFailed);
|
||
|
else raise EIdSocksUnknownError.Create(RSSocksUnknownError);
|
||
|
end;
|
||
|
|
||
|
try
|
||
|
// Socks server replies on connect, this is the second part
|
||
|
AIOHandler.ReadBytes(LResponse, 6, False); //overwrite the first part for now
|
||
|
TIdIOHandlerSocket(AIOHandler).Binding.SetBinding(BytesToIPv4Str(LResponse, 2), LResponse[0]*256+LResponse[1]);
|
||
|
except
|
||
|
IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError));
|
||
|
end;
|
||
|
finally
|
||
|
LClient.IOHandler := nil;
|
||
|
FreeAndNil(LClient);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.MakeConnection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
|
||
|
begin
|
||
|
case Version of
|
||
|
svSocks4, svSocks4A: MakeSocks4Connection(AIOHandler, AHost, APort);
|
||
|
svSocks5: MakeSocks5Connection(AIOHandler, AHost, APort);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdSocksInfo.GetEnabled: Boolean;
|
||
|
Begin
|
||
|
Result := Version in [svSocks4, svSocks4A, svSocks5];
|
||
|
End;//
|
||
|
|
||
|
procedure TIdSocksInfo.InitComponent;
|
||
|
begin
|
||
|
inherited InitComponent;
|
||
|
Authentication := ID_SOCKS_AUTH;
|
||
|
Version := ID_SOCKS_VER;
|
||
|
Port := IdPORT_SOCKS;
|
||
|
FIPVersion := ID_DEFAULT_IP_VERSION;
|
||
|
FUDPSocksAssociation := TIdIOHandlerStack.Create;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.AuthenticateSocks5Connection(
|
||
|
AIOHandler: TIdIOHandler);
|
||
|
var
|
||
|
Lpos: Integer;
|
||
|
LBuf,
|
||
|
LUsername,
|
||
|
LPassword : TIdBytes;
|
||
|
LRequestedAuthMethod,
|
||
|
LServerAuthMethod,
|
||
|
LUsernameLen,
|
||
|
LPasswordLen : Byte;
|
||
|
begin
|
||
|
// keep the compiler happy
|
||
|
LUsername := nil;
|
||
|
LPassword := nil;
|
||
|
|
||
|
SetLength(LBuf, 3);
|
||
|
|
||
|
// defined in rfc 1928
|
||
|
if Authentication = saNoAuthentication then begin
|
||
|
LBuf[2] := $0 // No authentication
|
||
|
end else begin
|
||
|
LBuf[2] := $2; // Username password authentication
|
||
|
end;
|
||
|
|
||
|
LRequestedAuthMethod := LBuf[2];
|
||
|
LBuf[0] := $5; // socks version
|
||
|
LBuf[1] := $1; // number of possible authentication methods
|
||
|
AIOHandler.WriteDirect(LBuf);
|
||
|
try
|
||
|
AIOHandler.ReadBytes(LBuf, 2, False); // Socks server sends the selected authentication method
|
||
|
except
|
||
|
IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError));
|
||
|
end;
|
||
|
|
||
|
LServerAuthMethod := LBuf[1];
|
||
|
if (LServerAuthMethod <> LRequestedAuthMethod) or (LServerAuthMethod = $FF) then begin
|
||
|
raise EIdSocksAuthMethodError.Create(RSSocksAuthMethodError);
|
||
|
end;
|
||
|
|
||
|
// Authentication process
|
||
|
if Authentication = saUsernamePassword then begin
|
||
|
LUsername := ToBytes(Username);
|
||
|
LPassword := ToBytes(Password);
|
||
|
LUsernameLen := IndyMin(Length(LUsername), 255);
|
||
|
LPasswordLen := IndyMin(Length(LPassword), 255);
|
||
|
SetLength(LBuf, 3 + LUsernameLen + LPasswordLen);
|
||
|
LBuf[0] := 1; // version of subnegotiation
|
||
|
LBuf[1] := LUsernameLen;
|
||
|
Lpos := 2;
|
||
|
if LUsernameLen > 0 then begin
|
||
|
CopyTIdBytes(LUsername, 0, LBuf, Lpos, LUsernameLen);
|
||
|
Lpos := Lpos + LUsernameLen;
|
||
|
end;
|
||
|
LBuf[Lpos] := LPasswordLen;
|
||
|
Lpos := Lpos + 1;
|
||
|
if LPasswordLen > 0 then begin
|
||
|
CopyTIdBytes(LPassword, 0, LBuf, Lpos, LPasswordLen);
|
||
|
end;
|
||
|
|
||
|
AIOHandler.WriteDirect(LBuf); // send the username and password
|
||
|
try
|
||
|
AIOHandler.ReadBytes(LBuf, 2, False); // Socks server sends the authentication status
|
||
|
except
|
||
|
IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError));
|
||
|
end;
|
||
|
|
||
|
if LBuf[1] <> $0 then begin
|
||
|
raise EIdSocksAuthError.Create(RSSocksAuthError);
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.MakeSocks5Bind(AIOHandler: TIdIOHandler; const AHost: string;
|
||
|
const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
|
||
|
var
|
||
|
Lpos: Integer;
|
||
|
LBuf: TIdBytes;
|
||
|
LClient: TIdTCPClient;
|
||
|
LType : Byte;
|
||
|
LAddress: TIdIPv6Address;
|
||
|
LIPVersion: TIdIPVersion;
|
||
|
begin
|
||
|
LClient := TIdTCPClient.Create(nil);
|
||
|
try
|
||
|
SetLength(LBuf, 255);
|
||
|
TIdIOHandlerSocket(AIOHandler).TransparentProxy := nil;
|
||
|
LClient.IOHandler := AIOHandler;
|
||
|
LClient.Host := Host;
|
||
|
LClient.IPVersion := IPVersion;
|
||
|
LClient.Port := Port;
|
||
|
LClient.Connect;
|
||
|
TIdIOHandlerSocket(AIOHandler).TransparentProxy := Self;
|
||
|
|
||
|
AuthenticateSocks5Connection(AIOHandler);
|
||
|
// Bind process
|
||
|
MakeSocks5Request(AIOHandler, AHost, APort, $02, LBuf, LPos); //bind request
|
||
|
//
|
||
|
AIOHandler.Write(LBuf, LPos); // send the connection packet
|
||
|
try
|
||
|
AIOHandler.ReadBytes(LBuf, 4, False); // Socks server replies on connect, this is the first part
|
||
|
except
|
||
|
IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError));
|
||
|
end;
|
||
|
|
||
|
case LBuf[1] of
|
||
|
0: ;// success, do nothing
|
||
|
1: raise EIdSocksServerGeneralError.Create(RSSocksServerGeneralError);
|
||
|
2: raise EIdSocksServerPermissionError.Create(RSSocksServerPermissionError);
|
||
|
3: raise EIdSocksServerNetUnreachableError.Create(RSSocksServerNetUnreachableError);
|
||
|
4: raise EIdSocksServerHostUnreachableError.Create(RSSocksServerHostUnreachableError);
|
||
|
5: raise EIdSocksServerConnectionRefusedError.Create(RSSocksServerConnectionRefusedError);
|
||
|
6: raise EIdSocksServerTTLExpiredError.Create(RSSocksServerTTLExpiredError);
|
||
|
7: raise EIdSocksServerCommandError.Create(RSSocksServerCommandError);
|
||
|
8: raise EIdSocksServerAddressError.Create(RSSocksServerAddressError);
|
||
|
else
|
||
|
raise EIdSocksUnknownError.Create(RSSocksUnknownError);
|
||
|
end;
|
||
|
LType := LBuf[3];
|
||
|
// type of destination address is domain name
|
||
|
case LType of
|
||
|
// IP V4
|
||
|
1: Lpos := 4 + 2; // 4 is for address and 2 is for port length
|
||
|
// FQDN
|
||
|
3: Lpos := LBuf[4] + 2; // 2 is for port length
|
||
|
// IP V6
|
||
|
4: LPos := 16 + 2; // 16 is for address and 2 is for port length
|
||
|
end;
|
||
|
try
|
||
|
// Socks server replies on connect, this is the second part
|
||
|
AIOHandler.ReadBytes(LBuf, Lpos, False); //overwrite the first part for now
|
||
|
case LType of
|
||
|
1 : begin
|
||
|
//IPv4
|
||
|
TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(BytesToIPv4Str(LBuf), LBuf[4]*256+LBuf[5], Id_IPv4);
|
||
|
end;
|
||
|
3 : begin
|
||
|
LIPVersion := TIdIOHandlerSocket(AIOHandler).IPVersion;
|
||
|
TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(GStack.ResolveHost(BytesToString(LBuf,0,LPos-2), LIPVersion), LBuf[4]*256+LBuf[5], LIPVersion);
|
||
|
end;
|
||
|
4 : begin
|
||
|
BytesToIPv6(LBuf, LAddress);
|
||
|
TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(IPv6AddressToStr(LAddress), LBuf[16]*256+LBuf[17], Id_IPv6);
|
||
|
end;
|
||
|
end;
|
||
|
except
|
||
|
IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError));
|
||
|
end;
|
||
|
finally
|
||
|
LClient.IOHandler := nil;
|
||
|
FreeAndNil(LClient);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.Bind(AIOHandler: TIdIOHandler; const AHost: string;
|
||
|
const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
|
||
|
begin
|
||
|
case Version of
|
||
|
svSocks4, svSocks4A: MakeSocks4Bind(AIOHandler, AHost, APort);
|
||
|
svSocks5: MakeSocks5Bind(AIOHandler, AHost, APort, AIPVersion);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdSocksInfo.Listen(AIOHandler: TIdIOHandler;
|
||
|
const ATimeOut: integer): boolean;
|
||
|
begin
|
||
|
Result := False;
|
||
|
case Version of
|
||
|
svSocks4, svSocks4A: Result := MakeSocks4Listen(AIOHandler, ATimeOut);
|
||
|
svSocks5: Result := MakeSocks5Listen(AIOHandler, ATimeOut);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdSocksInfo.MakeSocks5Listen(AIOHandler: TIdIOHandler;
|
||
|
const ATimeOut: integer): boolean;
|
||
|
var
|
||
|
Lpos: Integer;
|
||
|
LBuf: TIdBytes;
|
||
|
LType : Byte;
|
||
|
LAddress: TIdIPv6Address;
|
||
|
LIPVersion: TIdIPVersion;
|
||
|
begin
|
||
|
SetLength(LBuf, 255);
|
||
|
Result := TIdIOHandlerSocket(AIOHandler).Binding.Readable(ATimeOut);
|
||
|
if Result then begin
|
||
|
AIOHandler.ReadBytes(LBuf, 4, False); // Socks server replies on connect, this is the first part
|
||
|
|
||
|
case LBuf[1] of
|
||
|
0: ;// success, do nothing
|
||
|
1: raise EIdSocksServerGeneralError.Create(RSSocksServerGeneralError);
|
||
|
2: raise EIdSocksServerPermissionError.Create(RSSocksServerPermissionError);
|
||
|
3: raise EIdSocksServerNetUnreachableError.Create(RSSocksServerNetUnreachableError);
|
||
|
4: raise EIdSocksServerHostUnreachableError.Create(RSSocksServerHostUnreachableError);
|
||
|
5: raise EIdSocksServerConnectionRefusedError.Create(RSSocksServerConnectionRefusedError);
|
||
|
6: raise EIdSocksServerTTLExpiredError.Create(RSSocksServerTTLExpiredError);
|
||
|
7: raise EIdSocksServerCommandError.Create(RSSocksServerCommandError);
|
||
|
8: raise EIdSocksServerAddressError.Create(RSSocksServerAddressError);
|
||
|
else
|
||
|
raise EIdSocksUnknownError.Create(RSSocksUnknownError);
|
||
|
end;
|
||
|
LType := LBuf[3];
|
||
|
// type of destination address is domain name
|
||
|
case LType of
|
||
|
// IP V4
|
||
|
1: Lpos := 4 + 2; // 4 is for address and 2 is for port length
|
||
|
// FQDN
|
||
|
3: Lpos := LBuf[4] + 2; // 2 is for port length
|
||
|
// IP V6 - 4:
|
||
|
else
|
||
|
Lpos := 16 + 2; // 16 is for address and 2 is for port length
|
||
|
end;
|
||
|
// Socks server replies on connect, this is the second part
|
||
|
AIOHandler.ReadBytes(LBuf, Lpos, False); // just write it over the first part for now
|
||
|
case LType of
|
||
|
1 : begin
|
||
|
//IPv4
|
||
|
TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(BytesToIPv4Str(LBuf), LBuf[4]*256+LBuf[5], Id_IPv4);
|
||
|
end;
|
||
|
3 : begin
|
||
|
//FQN
|
||
|
LIPVersion := TIdIOHandlerSocket(AIOHandler).IPVersion;
|
||
|
TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(GStack.ResolveHost(BytesToString(LBuf,0,LPos-2), LIPVersion), LBuf[4]*256+LBuf[5], LIPVersion);
|
||
|
end;
|
||
|
else begin
|
||
|
//IPv6
|
||
|
BytesToIPv6(LBuf, LAddress);
|
||
|
TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(IPv6AddressToStr(LAddress), LBuf[16]*256+LBuf[17], Id_IPv6);
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdSocksInfo.MakeSocks4Listen(AIOHandler: TIdIOHandler;
|
||
|
const ATimeOut: integer): boolean;
|
||
|
var
|
||
|
LBuf: TIdBytes;
|
||
|
begin
|
||
|
SetLength(LBuf, 6);
|
||
|
Result := TIdIOHandlerSocket(AIOHandler).Binding.Readable(ATimeOut);
|
||
|
if Result then begin
|
||
|
AIOHandler.ReadBytes(LBuf, 2, False); // Socks server replies on connect, this is the first part
|
||
|
|
||
|
case LBuf[1] of // OpCode
|
||
|
90: ;// request granted, do nothing
|
||
|
91: raise EIdSocksRequestFailed.Create(RSSocksRequestFailed);
|
||
|
92: raise EIdSocksRequestServerFailed.Create(RSSocksRequestServerFailed);
|
||
|
93: raise EIdSocksRequestIdentFailed.Create(RSSocksRequestIdentFailed);
|
||
|
else raise EIdSocksUnknownError.Create(RSSocksUnknownError);
|
||
|
end;
|
||
|
|
||
|
// Socks server replies on connect, this is the second part
|
||
|
AIOHandler.ReadBytes(LBuf, 6, False); // just write it over the first part for now
|
||
|
TIdIOHandlerSocket(AIOHandler).Binding.SetPeer(BytesToIPv4Str(LBuf, 2), LBuf[0]*256+LBuf[1]);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.CloseSocks5UDPAssociation;
|
||
|
begin
|
||
|
if Assigned(FUDPSocksAssociation) then begin
|
||
|
FUDPSocksAssociation.Close;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.MakeSocks5UDPAssociation(AHandle: TIdSocketHandle);
|
||
|
var
|
||
|
Lpos: Integer;
|
||
|
LBuf: TIdBytes;
|
||
|
LIPVersion : TIdIPVersion;
|
||
|
begin
|
||
|
LIPVersion := Self.IPVersion;
|
||
|
FUDPSocksAssociation.Host := Self.Host;
|
||
|
FUDPSocksAssociation.Port := Self.Port;
|
||
|
FUDPSocksAssociation.IPVersion := LIPVersion;
|
||
|
FUDPSocksAssociation.Open;
|
||
|
try
|
||
|
SetLength(LBuf, 255);
|
||
|
AuthenticateSocks5Connection(FUDPSocksAssociation);
|
||
|
// Associate process
|
||
|
//For SOCKS5 Associate, the IP address and port is the client's IP address and port which may
|
||
|
//not be known
|
||
|
if LIPVersion = Id_IPv4 then begin
|
||
|
MakeSocks5Request(FUDPSocksAssociation, '0.0.0.0', 0, $03, LBuf, LPos); //associate request
|
||
|
end else begin
|
||
|
MakeSocks5Request(FUDPSocksAssociation, '::0', 0, $03, LBuf, LPos); //associate request
|
||
|
end;
|
||
|
//
|
||
|
FUDPSocksAssociation.Write(LBuf, LPos); // send the connection packet
|
||
|
try
|
||
|
FUDPSocksAssociation.ReadBytes(LBuf, 2, False); // Socks server replies on connect, this is the first part )VER and RSP
|
||
|
except
|
||
|
IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError));
|
||
|
end;
|
||
|
|
||
|
case LBuf[1] of
|
||
|
0: ;// success, do nothing
|
||
|
1: raise EIdSocksServerGeneralError.Create(RSSocksServerGeneralError);
|
||
|
2: raise EIdSocksServerPermissionError.Create(RSSocksServerPermissionError);
|
||
|
3: raise EIdSocksServerNetUnreachableError.Create(RSSocksServerNetUnreachableError);
|
||
|
4: raise EIdSocksServerHostUnreachableError.Create(RSSocksServerHostUnreachableError);
|
||
|
5: raise EIdSocksServerConnectionRefusedError.Create(RSSocksServerConnectionRefusedError);
|
||
|
6: raise EIdSocksServerTTLExpiredError.Create(RSSocksServerTTLExpiredError);
|
||
|
7: raise EIdSocksServerCommandError.Create(RSSocksServerCommandError);
|
||
|
8: raise EIdSocksServerAddressError.Create(RSSocksServerAddressError);
|
||
|
else
|
||
|
raise EIdSocksUnknownError.Create(RSSocksUnknownError);
|
||
|
end;
|
||
|
FUDPSocksAssociation.ReadBytes(LBuf, 2, False); //Now get RSVD and ATYPE feilds
|
||
|
// type of destination address is domain name
|
||
|
case LBuf[1] of
|
||
|
// IP V4
|
||
|
1: begin
|
||
|
Lpos := 4 + 2; // 4 is for address and 2 is for port length
|
||
|
LIPVersion := Id_IPv4;
|
||
|
end;
|
||
|
// FQDN
|
||
|
3: Lpos := LBuf[4] + 2; // 2 is for port length
|
||
|
// IP V6
|
||
|
4: begin
|
||
|
LPos := 16 + 2; // 16 is for address and 2 is for port length
|
||
|
LIPVersion := Id_IPv6;
|
||
|
end;
|
||
|
end;
|
||
|
try
|
||
|
// Socks server replies on connect, this is the second part
|
||
|
FUDPSocksAssociation.ReadBytes(LBuf, Lpos, False); //overwrite the first part for now
|
||
|
AHandle.SetPeer( (FUDPSocksAssociation as TIdIOHandlerStack).Binding.PeerIP ,LBuf[4]*256+LBuf[5],LIPVersion);
|
||
|
AHandle.Connect;
|
||
|
except
|
||
|
IndyRaiseOuterException(EIdSocksServerRespondError.Create(RSSocksServerRespondError));
|
||
|
end;
|
||
|
except
|
||
|
FUDPSocksAssociation.Close;
|
||
|
raise;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.CloseUDP(AHandle: TIdSocketHandle);
|
||
|
begin
|
||
|
case Version of
|
||
|
svSocks4, svSocks4A: raise EIdSocksUDPNotSupportedBySOCKSVersion.Create(RSSocksUDPNotSupported);
|
||
|
svSocks5: CloseSocks5UDPAssociation;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.OpenUDP(AHandle: TIdSocketHandle;
|
||
|
const AHost: string=''; const APort: TIdPort=0; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
|
||
|
begin
|
||
|
case Version of
|
||
|
svSocks4, svSocks4A: raise EIdSocksUDPNotSupportedBySOCKSVersion.Create(RSSocksUDPNotSupported);
|
||
|
svSocks5: MakeSocks5UDPAssociation(AHandle);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdSocksInfo.DisasmUDPReplyPacket(const APacket : TIdBytes;
|
||
|
var VHost : String; var VPort : TIdPort; var VIPVersion: TIdIPVersion): TIdBytes;
|
||
|
{
|
||
|
|
||
|
|
||
|
+----+------+------+----------+----------+----------+
|
||
|
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
|
||
|
+----+------+------+----------+----------+----------+
|
||
|
| 2 | 1 | 1 | Variable | 2 | Variable |
|
||
|
+----+------+------+----------+----------+----------+
|
||
|
01 2 3
|
||
|
The fields in the UDP request header are:
|
||
|
|
||
|
o RSV Reserved X'0000'
|
||
|
o FRAG Current fragment number
|
||
|
o ATYP address type of following addresses:
|
||
|
o IP V4 address: X'01'
|
||
|
o DOMAINNAME: X'03'
|
||
|
o IP V6 address: X'04'
|
||
|
o DST.ADDR desired destination address
|
||
|
o DST.PORT desired destination port
|
||
|
o DATA user data
|
||
|
}
|
||
|
var
|
||
|
LLen : Integer;
|
||
|
LIP6 : TIdIPv6Address;
|
||
|
i : Integer;
|
||
|
begin
|
||
|
if Length(APacket) < 5 then begin
|
||
|
Exit;
|
||
|
end;
|
||
|
// type of destination address is domain name
|
||
|
case APacket[3] of
|
||
|
// IP V4
|
||
|
1: begin
|
||
|
LLen := 4 + 4; //4 IPv4 address len, 4- 2 reserved, 1 frag, 1 atype
|
||
|
VHost := BytesToIPv4Str(APacket, 4);
|
||
|
VIPVersion := Id_IPv4;
|
||
|
end;
|
||
|
// FQDN
|
||
|
3: begin
|
||
|
LLen := APacket[4] +4; // 2 is for port length, 4 - 2 reserved, 1 frag, 1 atype
|
||
|
if Length(APacket)< (5+LLen) then begin
|
||
|
Exit;
|
||
|
end;
|
||
|
VHost := BytesToString(APacket, 5, APacket[4]);
|
||
|
// VIPVersion is pre-initialized by the receiving socket before DisasmUDPReplyPacket() is called
|
||
|
end;
|
||
|
// IP V6 - 4:
|
||
|
else begin
|
||
|
LLen := 16 + 4; // 16 is for address, 2 is for port length, 4 - 2 reserved, 1 frag, 1 atype
|
||
|
BytesToIPv6(APacket, LIP6, 5);
|
||
|
for i := 0 to 7 do begin
|
||
|
LIP6[i] := GStack.NetworkToHost(LIP6[i]);
|
||
|
end;
|
||
|
VHost := IPv6AddressToStr(LIP6);
|
||
|
VIPVersion := Id_IPv6;
|
||
|
end;
|
||
|
end;
|
||
|
VPort := APacket[LLen]*256 + APacket[LLen+1];
|
||
|
LLen := LLen + 2;
|
||
|
SetLength(Result, Length(APacket)-LLen);
|
||
|
CopyTIdBytes(APacket, LLen, Result, 0, Length(APacket)-LLen);
|
||
|
end;
|
||
|
|
||
|
function TIdSocksInfo.MakeUDPRequestPacket(const AData: TIdBytes;
|
||
|
const AHost : String; const APort : TIdPort) : TIdBytes;
|
||
|
{
|
||
|
|
||
|
|
||
|
+----+------+------+----------+----------+----------+
|
||
|
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
|
||
|
+----+------+------+----------+----------+----------+
|
||
|
| 2 | 1 | 1 | Variable | 2 | Variable |
|
||
|
+----+------+------+----------+----------+----------+
|
||
|
01 2 3
|
||
|
The fields in the UDP request header are:
|
||
|
|
||
|
o RSV Reserved X'0000'
|
||
|
o FRAG Current fragment number
|
||
|
o ATYP address type of following addresses:
|
||
|
o IP V4 address: X'01'
|
||
|
o DOMAINNAME: X'03'
|
||
|
o IP V6 address: X'04'
|
||
|
o DST.ADDR desired destination address
|
||
|
o DST.PORT desired destination port
|
||
|
o DATA user data
|
||
|
}
|
||
|
var
|
||
|
LLen : Integer;
|
||
|
LIP : TIdIPAddress;
|
||
|
LAddr: TIdBytes;
|
||
|
begin
|
||
|
SetLength(Result, 1024);
|
||
|
Result[0] := 0;
|
||
|
Result[1] := 0;
|
||
|
Result[2] := 0; //no fragmentation - too lazy to implement it
|
||
|
|
||
|
// address type: IP V4 address: X'01' {Do not Localize}
|
||
|
// DOMAINNAME: X'03' {Do not Localize}
|
||
|
// IP V6 address: X'04' {Do not Localize}
|
||
|
|
||
|
LIP := TIdIPAddress.MakeAddressObject(AHost);
|
||
|
if Assigned(LIP) then
|
||
|
begin
|
||
|
try
|
||
|
if LIP.AddrType = Id_IPv6 then begin
|
||
|
Result[3] := $04; //IPv6 address
|
||
|
end else begin
|
||
|
Result[3] := $01; //IPv4 address
|
||
|
end;
|
||
|
LLen := 4;
|
||
|
LAddr := LIP.HToNBytes;
|
||
|
CopyTIdBytes(LAddr, 0, Result, 4, Length(LAddr));
|
||
|
LLen := LLen + Length(LAddr);
|
||
|
finally
|
||
|
FreeAndNil(LIP);
|
||
|
end;
|
||
|
end else
|
||
|
begin
|
||
|
LAddr := ToBytes(AHost);
|
||
|
Result[3] := $3; // host name
|
||
|
Result[4] := IndyMin(Length(LAddr), 255);
|
||
|
if Result[4] > 0 then begin
|
||
|
CopyTIdBytes(LAddr, 0, Result, 5, Result[4]);
|
||
|
end;
|
||
|
LLen := 5 + Result[4];
|
||
|
end;
|
||
|
|
||
|
// port
|
||
|
CopyTIdUInt16(GStack.HostToNetwork(APort), Result, LLen);
|
||
|
LLen := LLen + 2;
|
||
|
|
||
|
//now do the rest of the packet
|
||
|
SetLength(Result, LLen + Length(AData));
|
||
|
CopyTIdBytes(AData, 0, Result, LLen, Length(AData));
|
||
|
end;
|
||
|
|
||
|
function TIdSocksInfo.RecvFromUDP(AHandle: TIdSocketHandle;
|
||
|
var ABuffer : TIdBytes; var VPeerIP: string; var VPeerPort: TIdPort;
|
||
|
var VIPVersion: TIdIPVersion; AMSec: Integer = IdTimeoutDefault): Integer;
|
||
|
var
|
||
|
LBuf : TIdBytes;
|
||
|
begin
|
||
|
case Version of
|
||
|
svSocks4, svSocks4A: raise EIdSocksUDPNotSupportedBySOCKSVersion.Create(RSSocksUDPNotSupported);
|
||
|
end;
|
||
|
SetLength(LBuf, Length(ABuffer)+200);
|
||
|
|
||
|
if not AHandle.Readable(AMSec) then begin
|
||
|
Result := 0;
|
||
|
VPeerIP := ''; {Do not Localize}
|
||
|
VPeerPort := 0;
|
||
|
VIPVersion := ID_DEFAULT_IP_VERSION;
|
||
|
Exit;
|
||
|
end;
|
||
|
Result := AHandle.RecvFrom(LBuf, VPeerIP, VPeerPort, VIPVersion);
|
||
|
SetLength(LBuf, Result);
|
||
|
LBuf := DisasmUDPReplyPacket(LBuf, VPeerIP, VPeerPort, VIPVersion);
|
||
|
Result := Length(LBuf);
|
||
|
CopyTIdBytes(LBuf, 0, ABuffer, 0, Result);
|
||
|
end;
|
||
|
|
||
|
procedure TIdSocksInfo.SendToUDP(AHandle: TIdSocketHandle; const AHost: string;
|
||
|
const APort: TIdPort; const AIPVersion: TIdIPVersion; const ABuffer : TIdBytes);
|
||
|
var
|
||
|
LBuf : TIdBytes;
|
||
|
begin
|
||
|
case Version of
|
||
|
svSocks4, svSocks4A: raise EIdSocksUDPNotSupportedBySOCKSVersion.Create(RSSocksUDPNotSupported);
|
||
|
end;
|
||
|
LBuf := MakeUDPRequestPacket(ABuffer, AHost, APort);
|
||
|
AHandle.Send(LBuf, 0);
|
||
|
end;
|
||
|
|
||
|
destructor TIdSocksInfo.Destroy;
|
||
|
begin
|
||
|
FreeAndNil(FUDPSocksAssociation);
|
||
|
inherited Destroy;
|
||
|
end;
|
||
|
|
||
|
end.
|