synapse/blcksock.pas
geby 9f400a899b Release 30
git-svn-id: https://svn.code.sf.net/p/synalist/code/trunk@66 7c85be65-684b-0410-a082-b2ed4fbef004
2008-04-24 07:22:17 +00:00

2305 lines
63 KiB
ObjectPascal

{==============================================================================|
| Project : Delphree - Synapse | 006.006.001 |
|==============================================================================|
| Content: Library base |
|==============================================================================|
| Copyright (c)1999-2003, Lukas Gebauer |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions are met: |
| |
| Redistributions of source code must retain the above copyright notice, this |
| list of conditions and the following disclaimer. |
| |
| Redistributions in binary form must reproduce the above copyright notice, |
| this list of conditions and the following disclaimer in the documentation |
| and/or other materials provided with the distribution. |
| |
| Neither the name of Lukas Gebauer nor the names of its contributors may |
| be used to endorse or promote products derived from this software without |
| specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR |
| ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| DAMAGE. |
|==============================================================================|
| The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
| Portions created by Lukas Gebauer are Copyright (c)1999-2003. |
| All Rights Reserved. |
|==============================================================================|
| Contributor(s): |
|==============================================================================|
| History: see HISTORY.HTM from distribution package |
| (Found at URL: http://www.ararat.cz/synapse/) |
|==============================================================================}
{
Special thanks to Gregor Ibic <gregor.ibic@intelicom.si>
(Intelicom d.o.o., http://www.intelicom.si)
for good inspiration about SSL programming.
}
{$Q-}
{$DEFINE ONCEWINSOCK}
{Note about define ONCEWINSOCK:
If you remove this compiler directive, then socket interface is loaded and
initialized on constructor of TBlockSocket class for each socket separately.
Socket interface is used only if your need it.
If you leave this directive here, then socket interface is loaded and
initialized only once at start of your program! It boost performace on high
count of created and destroyed sockets. It eliminate possible small resource
leak on Windows systems too.
}
unit blcksock;
interface
uses
SysUtils, Classes,
{$IFDEF LINUX}
Libc, kernelioctl,
{$ELSE}
Windows, WinSock,
{$ENDIF}
synsock, SynaUtil, SynaCode, SynaSSL;
const
cLocalhost = 'localhost';
cAnyHost = '0.0.0.0';
cBroadcast = '255.255.255.255';
cAnyPort = '0';
CR = #$0d;
LF = #$0a;
CRLF = CR + LF;
type
ESynapseError = class(Exception)
private
FErrorCode: Integer;
FErrorMessage: string;
published
property ErrorCode: Integer read FErrorCode Write FErrorCode;
property ErrorMessage: string read FErrorMessage Write FErrorMessage;
end;
THookSocketReason = (
HR_ResolvingBegin,
HR_ResolvingEnd,
HR_SocketCreate,
HR_SocketClose,
HR_Bind,
HR_Connect,
HR_CanRead,
HR_CanWrite,
HR_Listen,
HR_Accept,
HR_ReadCount,
HR_WriteCount,
HR_Wait
);
THookSocketStatus = procedure(Sender: TObject; Reason: THookSocketReason;
const Value: string) of object;
THookDataFilter = procedure(Sender: TObject; var Value: string) of object;
TBlockSocket = class(TObject)
private
FOnStatus: THookSocketStatus;
FOnReadFilter: THookDataFilter;
FOnWriteFilter: THookDataFilter;
FWsaData: TWSADATA;
FLocalSin: TSockAddrIn;
FRemoteSin: TSockAddrIn;
FLastError: Integer;
FLastErrorDesc: string;
FBuffer: string;
FRaiseExcept: Boolean;
FNonBlockMode: Boolean;
FMaxLineLength: Integer;
FMaxSendBandwidth: Integer;
FNextSend: Cardinal;
FMaxRecvBandwidth: Integer;
FNextRecv: Cardinal;
FConvertLineEnd: Boolean;
FLastCR: Boolean;
FLastLF: Boolean;
FBinded: Boolean;
function GetSizeRecvBuffer: Integer;
procedure SetSizeRecvBuffer(Size: Integer);
function GetSizeSendBuffer: Integer;
procedure SetSizeSendBuffer(Size: Integer);
procedure SetNonBlockMode(Value: Boolean);
procedure SetTTL(TTL: integer);
function GetTTL:integer;
protected
FSocket: TSocket;
FProtocol: Integer;
procedure CreateSocket; virtual;
procedure AutoCreateSocket;
procedure SetSin(var Sin: TSockAddrIn; IP, Port: string);
function GetSinIP(Sin: TSockAddrIn): string;
function GetSinPort(Sin: TSockAddrIn): Integer;
procedure DoStatus(Reason: THookSocketReason; const Value: string);
procedure DoReadFilter(Buffer: Pointer; var Length: Integer);
procedure DoWriteFilter(Buffer: Pointer; var Length: Integer);
procedure LimitBandwidth(Length: Integer; MaxB: integer; var Next: Cardinal);
procedure SetBandwidth(Value: Integer);
public
constructor Create;
constructor CreateAlternate(Stub: string);
destructor Destroy; override;
procedure CloseSocket; virtual;
procedure Bind(IP, Port: string);
procedure Connect(IP, Port: string); virtual;
function SendBuffer(Buffer: Pointer; Length: Integer): Integer; virtual;
procedure SendByte(Data: Byte); virtual;
procedure SendString(const Data: string); virtual;
function RecvBuffer(Buffer: Pointer; Length: Integer): Integer; virtual;
function RecvBufferEx(Buffer: Pointer; Length: Integer;
Timeout: Integer): Integer; virtual;
function RecvBufferStr(Length: Integer; Timeout: Integer): String; virtual;
function RecvByte(Timeout: Integer): Byte; virtual;
function RecvString(Timeout: Integer): string; virtual;
function RecvTerminated(Timeout: Integer; const Terminator: string): string; virtual;
function RecvPacket(Timeout: Integer): string; virtual;
function PeekBuffer(Buffer: Pointer; Length: Integer): Integer; virtual;
function PeekByte(Timeout: Integer): Byte; virtual;
function WaitingData: Integer; virtual;
function WaitingDataEx: Integer;
procedure SetLinger(Enable: Boolean; Linger: Integer);
procedure GetSins;
function SockCheck(SockResult: Integer): Integer;
procedure ExceptCheck;
function LocalName: string;
procedure ResolveNameToIP(Name: string; IPList: TStrings);
function ResolveName(Name: string): string;
function ResolvePort(Port: string): Word;
procedure SetRemoteSin(IP, Port: string);
function GetLocalSinIP: string; virtual;
function GetRemoteSinIP: string; virtual;
function GetLocalSinPort: Integer; virtual;
function GetRemoteSinPort: Integer; virtual;
function CanRead(Timeout: Integer): Boolean;
function CanReadEx(Timeout: Integer): Boolean;
function CanWrite(Timeout: Integer): Boolean;
function SendBufferTo(Buffer: Pointer; Length: Integer): Integer; virtual;
function RecvBufferFrom(Buffer: Pointer; Length: Integer): Integer; virtual;
function GroupCanRead(const SocketList: TList; Timeout: Integer;
const CanReadList: TList): Boolean;
function EnableReuse(Value: Boolean): Boolean;
//See 'winsock2.txt' file in distribute package!
function SetTimeout(Timeout: Integer): Boolean;
function SetSendTimeout(Timeout: Integer): Boolean;
function SetRecvTimeout(Timeout: Integer): Boolean;
property LocalSin: TSockAddrIn read FLocalSin;
property RemoteSin: TSockAddrIn read FRemoteSin;
published
class function GetErrorDesc(ErrorCode: Integer): string;
property Socket: TSocket read FSocket write FSocket;
property LastError: Integer read FLastError;
property LastErrorDesc: string read FLastErrorDesc;
property Protocol: Integer read FProtocol;
property LineBuffer: string read FBuffer write FBuffer;
property RaiseExcept: Boolean read FRaiseExcept write FRaiseExcept;
property SizeRecvBuffer: Integer read GetSizeRecvBuffer write SetSizeRecvBuffer;
property SizeSendBuffer: Integer read GetSizeSendBuffer write SetSizeSendBuffer;
property WSAData: TWSADATA read FWsaData;
property NonBlockMode: Boolean read FNonBlockMode Write SetNonBlockMode;
property MaxLineLength: Integer read FMaxLineLength Write FMaxLineLength;
property MaxSendBandwidth: Integer read FMaxSendBandwidth Write FMaxSendBandwidth;
property MaxRecvBandwidth: Integer read FMaxRecvBandwidth Write FMaxRecvBandwidth;
property MaxBandwidth: Integer Write SetBandwidth;
property ConvertLineEnd: Boolean read FConvertLineEnd Write FConvertLineEnd;
property TTL: Integer read GetTTL Write SetTTL;
property OnStatus: THookSocketStatus read FOnStatus write FOnStatus;
property OnReadFilter: THookDataFilter read FOnReadFilter write FOnReadFilter;
property OnWriteFilter: THookDataFilter read FOnWriteFilter write FOnWriteFilter;
end;
TSocksBlockSocket = class(TBlockSocket)
protected
FSocksIP: string;
FSocksPort: string;
FSocksTimeout: integer;
FSocksUsername: string;
FSocksPassword: string;
FUsingSocks: Boolean;
FSocksResolver: Boolean;
FSocksLastError: integer;
FSocksResponseIP: string;
FSocksResponsePort: string;
FSocksLocalIP: string;
FSocksLocalPort: string;
FSocksRemoteIP: string;
FSocksRemotePort: string;
FBypassFlag: Boolean;
function SocksCode(IP, Port: string): string;
function SocksDecode(Value: string): integer;
public
constructor Create;
function SocksOpen: Boolean;
function SocksRequest(Cmd: Byte; const IP, Port: string): Boolean;
function SocksResponse: Boolean;
published
property SocksIP: string read FSocksIP write FSocksIP;
property SocksPort: string read FSocksPort write FSocksPort;
property SocksUsername: string read FSocksUsername write FSocksUsername;
property SocksPassword: string read FSocksPassword write FSocksPassword;
property UsingSocks: Boolean read FUsingSocks;
property SocksResolver: Boolean read FSocksResolver write FSocksResolver;
property SocksLastError: integer read FSocksLastError;
end;
TTCPBlockSocket = class(TSocksBlockSocket)
protected
FSslEnabled: Boolean;
FSslBypass: Boolean;
FSsl: PSSL;
Fctx: PSSL_CTX;
FSSLPassword: string;
FSSLCiphers: string;
FSSLCertificateFile: string;
FSSLPrivateKeyFile: string;
FSSLCertCAFile: string;
FSSLLastError: integer;
FSSLLastErrorDesc: string;
FSSLverifyCert: Boolean;
FHTTPTunnelIP: string;
FHTTPTunnelPort: string;
FHTTPTunnel: Boolean;
FHTTPTunnelRemoteIP: string;
FHTTPTunnelRemotePort: string;
FHTTPTunnelUser: string;
FHTTPTunnelPass: string;
procedure SetSslEnabled(Value: Boolean);
function SetSslKeys: boolean;
procedure SocksDoConnect(IP, Port: string);
procedure HTTPTunnelDoConnect(IP, Port: string);
public
constructor Create;
destructor Destroy; override;
procedure CreateSocket; override;
procedure CloseSocket; override;
function WaitingData: Integer; override;
procedure Listen;
function Accept: TSocket;
procedure Connect(IP, Port: string); override;
procedure SSLDoConnect;
procedure SSLDoShutdown;
function SSLAcceptConnection: Boolean;
function GetLocalSinIP: string; override;
function GetRemoteSinIP: string; override;
function GetLocalSinPort: Integer; override;
function GetRemoteSinPort: Integer; override;
function SendBuffer(Buffer: Pointer; Length: Integer): Integer; override;
function RecvBuffer(Buffer: Pointer; Length: Integer): Integer; override;
function SSLGetSSLVersion: string;
function SSLGetPeerSubject: string;
function SSLGetPeerIssuer: string;
function SSLGetPeerSubjectHash: Cardinal;
function SSLGetPeerIssuerHash: Cardinal;
function SSLGetPeerFingerprint: string;
function SSLCheck: Boolean;
published
property SSLEnabled: Boolean read FSslEnabled write SetSslEnabled;
property SSLBypass: Boolean read FSslBypass write FSslBypass;
property SSLPassword: string read FSSLPassword write FSSLPassword;
property SSLCiphers: string read FSSLCiphers write FSSLCiphers;
property SSLCertificateFile: string read FSSLCertificateFile write FSSLCertificateFile;
property SSLPrivateKeyFile: string read FSSLPrivateKeyFile write FSSLPrivateKeyFile;
property SSLCertCAFile: string read FSSLCertCAFile write FSSLCertCAFile;
property SSLLastError: integer read FSSLLastError;
property SSLLastErrorDesc: string read FSSLLastErrorDesc;
property SSLverifyCert: Boolean read FSSLverifyCert write FSSLverifyCert;
property HTTPTunnelIP: string read FHTTPTunnelIP Write FHTTPTunnelIP;
property HTTPTunnelPort: string read FHTTPTunnelPort Write FHTTPTunnelPort;
property HTTPTunnel: Boolean read FHTTPTunnel;
property HTTPTunnelUser: string read FHTTPTunnelUser Write FHTTPTunnelUser;
property HTTPTunnelPass: string read FHTTPTunnelPass Write FHTTPTunnelPass;
end;
TUDPBlockSocket = class(TSocksBlockSocket)
protected
FSocksControlSock: TTCPBlockSocket;
function UdpAssociation: Boolean;
procedure SetMulticastTTL(TTL: integer);
function GetMulticastTTL:integer;
public
destructor Destroy; override;
procedure CreateSocket; override;
function EnableBroadcast(Value: Boolean): Boolean;
procedure Connect(IP, Port: string); override;
function SendBuffer(Buffer: Pointer; Length: Integer): Integer; override;
function RecvBuffer(Buffer: Pointer; Length: Integer): Integer; override;
function SendBufferTo(Buffer: Pointer; Length: Integer): Integer; override;
function RecvBufferFrom(Buffer: Pointer; Length: Integer): Integer; override;
procedure AddMulticast(MCastIP:string);
procedure DropMulticast(MCastIP:string);
function EnableMulticastLoop(Value: Boolean): Boolean;
published
property MulticastTTL: Integer read GetMulticastTTL Write SetMulticastTTL;
end;
//See 'winsock2.txt' file in distribute package!
TICMPBlockSocket = class(TBlockSocket)
public
procedure CreateSocket; override;
end;
//See 'winsock2.txt' file in distribute package!
TRAWBlockSocket = class(TBlockSocket)
public
procedure CreateSocket; override;
end;
TIPHeader = record
VerLen: Byte;
TOS: Byte;
TotalLen: Word;
Identifer: Word;
FragOffsets: Word;
TTL: Byte;
Protocol: Byte;
CheckSum: Word;
SourceIp: DWORD;
DestIp: DWORD;
Options: DWORD;
end;
TSynaClient = Class(TObject)
protected
FTargetHost: string;
FTargetPort: string;
FIPInterface: string;
FTimeout: integer;
public
constructor Create;
published
property TargetHost: string read FTargetHost Write FTargetHost;
property TargetPort: string read FTargetPort Write FTargetPort;
property IPInterface: string read FIPInterface Write FIPInterface;
property Timeout: integer read FTimeout Write FTimeout;
end;
implementation
type
TMulticast = record
MCastAddr : u_long;
MCastIfc : u_long;
end;
{$IFDEF ONCEWINSOCK}
var
WsaDataOnce: TWSADATA;
e: ESynapseError;
{$ENDIF}
constructor TBlockSocket.Create;
{$IFNDEF ONCEWINSOCK}
var
e: ESynapseError;
{$ENDIF}
begin
inherited Create;
FRaiseExcept := False;
FSocket := INVALID_SOCKET;
FProtocol := IPPROTO_IP;
FBuffer := '';
FLastCR := False;
FLastLF := False;
FBinded := False;
FNonBlockMode := False;
FMaxLineLength := 0;
FMaxSendBandwidth := 0;
FNextSend := 0;
FMaxRecvBandwidth := 0;
FNextRecv := 0;
FConvertLineEnd := False;
{$IFDEF ONCEWINSOCK}
FWsaData := WsaDataOnce;
{$ELSE}
if not InitSocketInterface('') then
begin
e := ESynapseError.Create('Error loading Winsock DLL!');
e.ErrorCode := 0;
e.ErrorMessage := 'Error loading Winsock DLL!';
raise e;
end;
SockCheck(synsock.WSAStartup($101, FWsaData));
ExceptCheck;
{$ENDIF}
end;
constructor TBlockSocket.CreateAlternate(Stub: string);
{$IFNDEF ONCEWINSOCK}
var
e: ESynapseError;
{$ENDIF}
begin
inherited Create;
FRaiseExcept := False;
FSocket := INVALID_SOCKET;
FProtocol := IPPROTO_IP;
FBuffer := '';
FNonBlockMode := False;
FMaxLineLength := 0;
FMaxSendBandwidth := 0;
FNextSend := 0;
FMaxRecvBandwidth := 0;
FNextRecv := 0;
FConvertLineEnd := False;
{$IFDEF ONCEWINSOCK}
FWsaData := WsaDataOnce;
{$ELSE}
if not InitSocketInterface(Stub) then
begin
e := ESynapseError.Create('Error loading alternate Winsock DLL (' + Stub + ')!');
e.ErrorCode := 0;
e.ErrorMessage := 'Error loading Winsock DLL (' + Stub + ')!';
raise e;
end;
SockCheck(synsock.WSAStartup($101, FWsaData));
ExceptCheck;
{$ENDIF}
end;
destructor TBlockSocket.Destroy;
begin
CloseSocket;
{$IFNDEF ONCEWINSOCK}
synsock.WSACleanup;
DestroySocketInterface;
{$ENDIF}
inherited Destroy;
end;
procedure TBlockSocket.SetSin(var Sin: TSockAddrIn; IP, Port: string);
type
pu_long = ^u_long;
var
ProtoEnt: PProtoEnt;
ServEnt: PServEnt;
HostEnt: PHostEnt;
begin
DoStatus(HR_ResolvingBegin, IP + ':' + Port);
SynSockCS.Enter;
try
FillChar(Sin, Sizeof(Sin), 0);
Sin.sin_family := AF_INET;
ProtoEnt := synsock.GetProtoByNumber(FProtocol);
ServEnt := nil;
if ProtoEnt <> nil then
ServEnt := synsock.GetServByName(PChar(Port), ProtoEnt^.p_name);
if ServEnt = nil then
Sin.sin_port := synsock.htons(StrToIntDef(Port, 0))
else
Sin.sin_port := ServEnt^.s_port;
if IP = cBroadcast then
Sin.sin_addr.s_addr := u_long(INADDR_BROADCAST)
else
begin
Sin.sin_addr.s_addr := synsock.inet_addr(PChar(IP));
if SIn.sin_addr.s_addr = u_long(INADDR_NONE) then
begin
HostEnt := synsock.GetHostByName(PChar(IP));
if HostEnt <> nil then
SIn.sin_addr.S_addr := u_long(Pu_long(HostEnt^.h_addr_list^)^);
end;
end;
finally
SynSockCS.Leave;
end;
DoStatus(HR_ResolvingEnd, IP + ':' + Port);
end;
function TBlockSocket.GetSinIP(Sin: TSockAddrIn): string;
var
p: PChar;
begin
p := synsock.inet_ntoa(Sin.sin_addr);
if p = nil then
Result := ''
else
Result := p;
end;
function TBlockSocket.GetSinPort(Sin: TSockAddrIn): Integer;
begin
Result := synsock.ntohs(Sin.sin_port);
end;
procedure TBlockSocket.CreateSocket;
begin
FBuffer := '';
FBinded := False;
if FSocket = INVALID_SOCKET then
FLastError := synsock.WSAGetLastError
else
FLastError := 0;
ExceptCheck;
DoStatus(HR_SocketCreate, '');
end;
procedure TBlockSocket.AutoCreateSocket;
begin
if FSocket = INVALID_SOCKET then
CreateSocket;
end;
procedure TBlockSocket.CloseSocket;
begin
synsock.CloseSocket(FSocket);
FSocket := INVALID_SOCKET;
DoStatus(HR_SocketClose, '');
end;
procedure TBlockSocket.Bind(IP, Port: string);
var
Sin: TSockAddrIn;
Len: Integer;
begin
AutoCreateSocket;
SetSin(Sin, IP, Port);
SockCheck(synsock.Bind(FSocket, Sin, SizeOf(Sin)));
Len := SizeOf(FLocalSin);
synsock.GetSockName(FSocket, FLocalSin, Len);
FBuffer := '';
FBinded := True;
ExceptCheck;
DoStatus(HR_Bind, IP + ':' + Port);
end;
procedure TBlockSocket.Connect(IP, Port: string);
var
Sin: TSockAddrIn;
begin
AutoCreateSocket;
SetSin(Sin, IP, Port);
SockCheck(synsock.Connect(FSocket, Sin, SizeOf(Sin)));
GetSins;
FBuffer := '';
FLastCR := False;
FLastLF := False;
ExceptCheck;
DoStatus(HR_Connect, IP + ':' + Port);
end;
procedure TBlockSocket.GetSins;
var
Len: Integer;
begin
Len := SizeOf(FLocalSin);
synsock.GetSockName(FSocket, FLocalSin, Len);
Len := SizeOf(FRemoteSin);
synsock.GetPeerName(FSocket, FremoteSin, Len);
end;
procedure TBlockSocket.SetBandwidth(Value: Integer);
begin
MaxSendBandwidth := Value;
MaxRecvBandwidth := Value;
end;
procedure TBlockSocket.LimitBandwidth(Length: Integer; MaxB: integer; var Next: Cardinal);
var
x: Cardinal;
y: Cardinal;
begin
if MaxB > 0 then
begin
y := GetTick;
if Next > y then
begin
x := Next - y;
if x > 0 then
begin
DoStatus(HR_Wait, IntToStr(x));
sleep(x);
end;
end;
Next := y + Trunc((Length / MaxB) * 1000);
end;
end;
function TBlockSocket.SendBuffer(Buffer: Pointer; Length: Integer): Integer;
begin
LimitBandwidth(Length, FMaxSendBandwidth, FNextsend);
DoWriteFilter(Buffer, Length);
Result := synsock.Send(FSocket, Buffer^, Length, 0);
SockCheck(Result);
ExceptCheck;
DoStatus(HR_WriteCount, IntToStr(Result));
end;
procedure TBlockSocket.SendByte(Data: Byte);
begin
SendBuffer(@Data, 1);
end;
procedure TBlockSocket.SendString(const Data: string);
begin
SendBuffer(PChar(Data), Length(Data));
end;
function TBlockSocket.RecvBuffer(Buffer: Pointer; Length: Integer): Integer;
begin
LimitBandwidth(Length, FMaxRecvBandwidth, FNextRecv);
Result := synsock.Recv(FSocket, Buffer^, Length, 0);
if Result = 0 then
FLastError := WSAECONNRESET
else
SockCheck(Result);
ExceptCheck;
DoStatus(HR_ReadCount, IntToStr(Result));
DoReadFilter(Buffer, Result);
end;
function TBlockSocket.RecvBufferEx(Buffer: Pointer; Length: Integer;
Timeout: Integer): Integer;
var
s: string;
rl, l: integer;
begin
FLastError := 0;
rl := 0;
repeat
s := RecvPacket(Timeout);
l := System.Length(s);
if (rl + l) > Length then
l := Length - rl;
Move(Pointer(s)^, IncPoint(Buffer, rl)^, l);
rl := rl + l;
if FLastError <> 0 then
Break;
until rl >= Length;
delete(s, 1, l);
FBuffer := s;
Result := rl;
end;
function TBlockSocket.RecvBufferStr(Length: Integer; Timeout: Integer): string;
var
x: integer;
begin
Result := '';
if Length > 0 then
begin
Setlength(Result, Length);
x := RecvBufferEx(PChar(Result), Length , Timeout);
if FLastError = 0 then
SetLength(Result, x)
else
Result := '';
end;
end;
function TBlockSocket.RecvPacket(Timeout: Integer): string;
var
x: integer;
begin
Result := '';
FLastError := 0;
if FBuffer <> '' then
begin
Result := FBuffer;
FBuffer := '';
end
else
begin
Sleep(0);
if CanRead(Timeout) then
begin
x := WaitingData;
if x = 0 then
FLastError := WSAECONNRESET;
if x > 0 then
begin
SetLength(Result, x);
x := RecvBuffer(Pointer(Result), x);
if x >= 0 then
SetLength(Result, x);
end;
end
else
FLastError := WSAETIMEDOUT;
end;
ExceptCheck;
end;
function TBlockSocket.RecvByte(Timeout: Integer): Byte;
begin
Result := 0;
FLastError := 0;
if FBuffer = '' then
FBuffer := RecvPacket(Timeout);
if (FLastError = 0) and (FBuffer <> '') then
begin
Result := Ord(FBuffer[1]);
System.Delete(FBuffer, 1, 1);
end;
ExceptCheck;
end;
function TBlockSocket.RecvTerminated(Timeout: Integer; const Terminator: string): string;
var
x: Integer;
s: string;
l: Integer;
CorCRLF: Boolean;
t: string;
tl: integer;
begin
FLastError := 0;
Result := '';
l := system.Length(Terminator);
if l = 0 then
Exit;
tl := l;
CorCRLF := FConvertLineEnd and (Terminator = CRLF);
s := '';
x := 0;
repeat
//get rest of FBuffer or incomming new data...
s := s + RecvPacket(Timeout);
if FLastError <> 0 then
Break;
x := 0;
if Length(s) > 0 then
if CorCRLF then
begin
if FLastCR and (s[1] = LF) then
Delete(s, 1, 1);
if FLastLF and (s[1] = CR) then
Delete(s, 1, 1);
FLastCR := False;
FLastLF := False;
t := '';
x := PosCRLF(s, t);
tl := system.Length(t);
if t = CR then
FLastCR := True;
if t = LF then
FLastLF := True;
end
else
begin
x := pos(Terminator, s);
tl := l;
end;
if (FMaxLineLength <> 0) and (system.Length(s) > FMaxLineLength) then
begin
FLastError := WSAENOBUFS;
Break;
end;
until x > 0;
if x > 0 then
begin
Result := Copy(s, 1, x - 1);
System.Delete(s, 1, x + tl - 1);
end;
FBuffer := s;
ExceptCheck;
end;
function TBlockSocket.RecvString(Timeout: Integer): string;
var
s: string;
begin
Result := '';
s := RecvTerminated(Timeout, CRLF);
if FLastError = 0 then
Result := s;
end;
function TBlockSocket.PeekBuffer(Buffer: Pointer; Length: Integer): Integer;
begin
Result := synsock.Recv(FSocket, Buffer^, Length, MSG_PEEK);
SockCheck(Result);
ExceptCheck;
end;
function TBlockSocket.PeekByte(Timeout: Integer): Byte;
var
s: string;
begin
Result := 0;
if CanRead(Timeout) then
begin
SetLength(s, 1);
PeekBuffer(Pointer(s), 1);
if s <> '' then
Result := Ord(s[1]);
end
else
FLastError := WSAETIMEDOUT;
ExceptCheck;
end;
function TBlockSocket.SockCheck(SockResult: Integer): Integer;
begin
FLastErrorDesc := '';
if SockResult = integer(SOCKET_ERROR) then
begin
Result := synsock.WSAGetLastError;
FLastErrorDesc := GetErrorDesc(Result);
end
else
Result := 0;
FLastError := Result;
end;
procedure TBlockSocket.ExceptCheck;
var
e: ESynapseError;
s: string;
begin
if FRaiseExcept and (LastError <> 0) and (LastError <> WSAEINPROGRESS)
and (LastError <> WSAEWOULDBLOCK) then
begin
s := GetErrorDesc(LastError);
e := ESynapseError.CreateFmt('TCP/IP Socket error %d: %s', [LastError, s]);
e.ErrorCode := LastError;
e.ErrorMessage := s;
raise e;
end;
end;
function TBlockSocket.WaitingData: Integer;
var
x: Integer;
begin
synsock.IoctlSocket(FSocket, FIONREAD, u_long(x));
Result := x;
end;
function TBlockSocket.WaitingDataEx: Integer;
begin
if FBuffer <> '' then
Result := Length(FBuffer)
else
Result := WaitingData;
end;
procedure TBlockSocket.SetLinger(Enable: Boolean; Linger: Integer);
var
li: TLinger;
begin
li.l_onoff := Ord(Enable);
li.l_linger := Linger div 1000;
SockCheck(synsock.SetSockOpt(FSocket, SOL_SOCKET, SO_LINGER, @li, SizeOf(li)));
ExceptCheck;
end;
function TBlockSocket.LocalName: string;
var
buf: array[0..255] of Char;
BufPtr: PChar;
RemoteHost: PHostEnt;
begin
BufPtr := buf;
Result := '';
synsock.GetHostName(BufPtr, SizeOf(buf));
if BufPtr[0] <> #0 then
begin
// try get Fully Qualified Domain Name
SynSockCS.Enter;
try
RemoteHost := synsock.GetHostByName(BufPtr);
if RemoteHost <> nil then
Result := PChar(RemoteHost^.h_name);
finally
SynSockCS.Leave;
end;
end;
if Result = '' then
Result := '127.0.0.1';
end;
procedure TBlockSocket.ResolveNameToIP(Name: string; IPList: TStrings);
type
TaPInAddr = array[0..250] of PInAddr;
PaPInAddr = ^TaPInAddr;
var
RemoteHost: PHostEnt;
IP: u_long;
PAdrPtr: PaPInAddr;
i: Integer;
s: string;
InAddr: TInAddr;
begin
IPList.Clear;
IP := synsock.inet_addr(PChar(Name));
if IP = u_long(INADDR_NONE) then
begin
SynSockCS.Enter;
try
RemoteHost := synsock.GetHostByName(PChar(Name));
if RemoteHost <> nil then
begin
PAdrPtr := PAPInAddr(RemoteHost^.h_addr_list);
i := 0;
while PAdrPtr^[i] <> nil do
begin
InAddr := PAdrPtr^[i]^;
with InAddr.S_un_b do
s := Format('%d.%d.%d.%d',
[Ord(s_b1), Ord(s_b2), Ord(s_b3), Ord(s_b4)]);
IPList.Add(s);
Inc(i);
end;
end;
if IPList.Count = 0 then
IPList.Add('0.0.0.0');
finally
SynSockCS.Leave;
end;
end
else
IPList.Add(Name);
end;
function TBlockSocket.ResolveName(Name: string): string;
var
l: TStringList;
begin
l := TStringList.Create;
try
ResolveNameToIP(Name, l);
Result := l[0];
finally
l.Free;
end;
end;
function TBlockSocket.ResolvePort(Port: string): Word;
var
ProtoEnt: PProtoEnt;
ServEnt: PServEnt;
begin
SynSockCS.Enter;
try
ProtoEnt := synsock.GetProtoByNumber(FProtocol);
ServEnt := nil;
if ProtoEnt <> nil then
ServEnt := synsock.GetServByName(PChar(Port), ProtoEnt^.p_name);
if ServEnt = nil then
Result := synsock.htons(StrToIntDef(Port, 0))
else
Result := ServEnt^.s_port;
finally
SynSockCS.Leave;
end;
end;
procedure TBlockSocket.SetRemoteSin(IP, Port: string);
begin
SetSin(FRemoteSin, IP, Port);
end;
function TBlockSocket.GetLocalSinIP: string;
begin
Result := GetSinIP(FLocalSin);
end;
function TBlockSocket.GetRemoteSinIP: string;
begin
Result := GetSinIP(FRemoteSin);
end;
function TBlockSocket.GetLocalSinPort: Integer;
begin
Result := GetSinPort(FLocalSin);
end;
function TBlockSocket.GetRemoteSinPort: Integer;
begin
Result := GetSinPort(FRemoteSin);
end;
function TBlockSocket.CanRead(Timeout: Integer): Boolean;
var
FDSet: TFDSet;
TimeVal: PTimeVal;
TimeV: TTimeVal;
x: Integer;
begin
TimeV.tv_usec := (Timeout mod 1000) * 1000;
TimeV.tv_sec := Timeout div 1000;
TimeVal := @TimeV;
if Timeout = -1 then
TimeVal := nil;
FD_ZERO(FDSet);
FD_SET(FSocket, FDSet);
x := synsock.Select(FSocket + 1, @FDSet, nil, nil, TimeVal);
SockCheck(x);
if FLastError <> 0 then
x := 0;
Result := x > 0;
ExceptCheck;
if Result then
DoStatus(HR_CanRead, '');
end;
function TBlockSocket.CanWrite(Timeout: Integer): Boolean;
var
FDSet: TFDSet;
TimeVal: PTimeVal;
TimeV: TTimeVal;
x: Integer;
begin
TimeV.tv_usec := (Timeout mod 1000) * 1000;
TimeV.tv_sec := Timeout div 1000;
TimeVal := @TimeV;
if Timeout = -1 then
TimeVal := nil;
FD_ZERO(FDSet);
FD_SET(FSocket, FDSet);
x := synsock.Select(FSocket + 1, nil, @FDSet, nil, TimeVal);
SockCheck(x);
if FLastError <> 0 then
x := 0;
Result := x > 0;
ExceptCheck;
if Result then
DoStatus(HR_CanWrite, '');
end;
function TBlockSocket.CanReadEx(Timeout: Integer): Boolean;
begin
if FBuffer <> '' then
Result := True
else
Result := CanRead(Timeout);
end;
function TBlockSocket.SendBufferTo(Buffer: Pointer; Length: Integer): Integer;
var
Len: Integer;
begin
LimitBandwidth(Length, FMaxSendBandwidth, FNextsend);
Len := SizeOf(FRemoteSin);
Result := synsock.SendTo(FSocket, Buffer^, Length, 0, FRemoteSin, Len);
SockCheck(Result);
ExceptCheck;
DoStatus(HR_WriteCount, IntToStr(Result));
end;
function TBlockSocket.RecvBufferFrom(Buffer: Pointer; Length: Integer): Integer;
var
Len: Integer;
begin
LimitBandwidth(Length, FMaxRecvBandwidth, FNextRecv);
Len := SizeOf(FRemoteSin);
Result := synsock.RecvFrom(FSocket, Buffer^, Length, 0, FRemoteSin, Len);
SockCheck(Result);
ExceptCheck;
DoStatus(HR_ReadCount, IntToStr(Result));
end;
function TBlockSocket.GetSizeRecvBuffer: Integer;
var
l: Integer;
begin
l := SizeOf(Result);
SockCheck(synsock.GetSockOpt(FSocket, SOL_SOCKET, SO_RCVBUF, @Result, l));
if FLastError <> 0 then
Result := 1024;
ExceptCheck;
end;
procedure TBlockSocket.SetSizeRecvBuffer(Size: Integer);
begin
SockCheck(synsock.SetSockOpt(FSocket, SOL_SOCKET, SO_RCVBUF, @Size, SizeOf(Size)));
ExceptCheck;
end;
function TBlockSocket.GetSizeSendBuffer: Integer;
var
l: Integer;
begin
l := SizeOf(Result);
SockCheck(synsock.GetSockOpt(FSocket, SOL_SOCKET, SO_SNDBUF, @Result, l));
if FLastError <> 0 then
Result := 1024;
ExceptCheck;
end;
procedure TBlockSocket.SetSizeSendBuffer(Size: Integer);
begin
SockCheck(synsock.SetSockOpt(FSocket, SOL_SOCKET, SO_SNDBUF, @Size, SizeOf(Size)));
ExceptCheck;
end;
procedure TBlockSocket.SetNonBlockMode(Value: Boolean);
var
x: integer;
begin
FNonBlockMode := Value;
if Value then
x := 1
else
x := 0;
synsock.IoctlSocket(FSocket, FIONBIO, u_long(x));
end;
//See 'winsock2.txt' file in distribute package!
function TBlockSocket.SetTimeout(Timeout: Integer): Boolean;
begin
Result := SetSendTimeout(Timeout) and SetRecvTimeout(Timeout);
end;
//See 'winsock2.txt' file in distribute package!
function TBlockSocket.SetSendTimeout(Timeout: Integer): Boolean;
begin
Result := synsock.SetSockOpt(FSocket, SOL_SOCKET, SO_SNDTIMEO,
@Timeout, SizeOf(Timeout)) <> SOCKET_ERROR;
end;
//See 'winsock2.txt' file in distribute package!
function TBlockSocket.SetRecvTimeout(Timeout: Integer): Boolean;
begin
Result := synsock.SetSockOpt(FSocket, SOL_SOCKET, SO_RCVTIMEO,
@Timeout, SizeOf(Timeout)) <> SOCKET_ERROR;
end;
function TBlockSocket.GroupCanRead(const SocketList: TList; Timeout: Integer;
const CanReadList: TList): boolean;
var
FDSet: TFDSet;
TimeVal: PTimeVal;
TimeV: TTimeVal;
x, n: Integer;
Max: Integer;
begin
TimeV.tv_usec := (Timeout mod 1000) * 1000;
TimeV.tv_sec := Timeout div 1000;
TimeVal := @TimeV;
if Timeout = -1 then
TimeVal := nil;
FD_ZERO(FDSet);
Max := 0;
for n := 0 to SocketList.Count - 1 do
if TObject(SocketList.Items[n]) is TBlockSocket then
begin
if TBlockSocket(SocketList.Items[n]).Socket > Max then
Max := TBlockSocket(SocketList.Items[n]).Socket;
FD_SET(TBlockSocket(SocketList.Items[n]).Socket, FDSet);
end;
x := synsock.Select(Max + 1, @FDSet, nil, nil, TimeVal);
SockCheck(x);
ExceptCheck;
if FLastError <> 0 then
x := 0;
Result := x > 0;
CanReadList.Clear;
if Result then
for n := 0 to SocketList.Count - 1 do
if TObject(SocketList.Items[n]) is TBlockSocket then
if FD_ISSET(TBlockSocket(SocketList.Items[n]).Socket, FDSet) then
CanReadList.Add(TBlockSocket(SocketList.Items[n]));
end;
function TBlockSocket.EnableReuse(Value: Boolean): Boolean;
var
Opt: Integer;
Res: Integer;
begin
opt := Ord(Value);
Res := synsock.SetSockOpt(FSocket, SOL_SOCKET, SO_REUSEADDR, @Opt, SizeOf(opt));
SockCheck(Res);
Result := res = 0;
ExceptCheck;
end;
procedure TBlockSocket.SetTTL(TTL: integer);
var
Res: Integer;
begin
Res := synsock.SetSockOpt(FSocket, IPPROTO_IP, IP_TTL, @TTL, SizeOf(TTL));
SockCheck(Res);
ExceptCheck;
end;
function TBlockSocket.GetTTL:integer;
var
l: Integer;
begin
l := SizeOf(Result);
SockCheck(synsock.GetSockOpt(FSocket, IPPROTO_IP, IP_TTL, @Result, l));
ExceptCheck;
end;
procedure TBlockSocket.DoStatus(Reason: THookSocketReason; const Value: string);
begin
if assigned(OnStatus) then
OnStatus(Self, Reason, Value);
end;
procedure TBlockSocket.DoReadFilter(Buffer: Pointer; var Length: Integer);
var
s: string;
begin
if assigned(OnReadFilter) then
if Length > 0 then
begin
SetLength(s, Length);
Move(Buffer^, Pointer(s)^, Length);
OnReadFilter(Self, s);
if System.Length(s) > Length then
SetLength(s, Length);
Length := System.Length(s);
Move(Pointer(s)^, Buffer^, Length);
end;
end;
procedure TBlockSocket.DoWriteFilter(Buffer: Pointer; var Length: Integer);
var
s: string;
begin
if assigned(OnWriteFilter) then
if Length > 0 then
begin
SetLength(s, Length);
Move(Buffer^, Pointer(s)^, Length);
OnWriteFilter(Self, s);
if System.Length(s) > Length then
SetLength(s, Length);
Length := System.Length(s);
Move(Pointer(s)^, Buffer^, Length);
end;
end;
class function TBlockSocket.GetErrorDesc(ErrorCode: Integer): string;
begin
case ErrorCode of
0:
Result := 'OK';
WSAEINTR: {10004}
Result := 'Interrupted system call';
WSAEBADF: {10009}
Result := 'Bad file number';
WSAEACCES: {10013}
Result := 'Permission denied';
WSAEFAULT: {10014}
Result := 'Bad address';
WSAEINVAL: {10022}
Result := 'Invalid argument';
WSAEMFILE: {10024}
Result := 'Too many open files';
WSAEWOULDBLOCK: {10035}
Result := 'Operation would block';
WSAEINPROGRESS: {10036}
Result := 'Operation now in progress';
WSAEALREADY: {10037}
Result := 'Operation already in progress';
WSAENOTSOCK: {10038}
Result := 'Socket operation on nonsocket';
WSAEDESTADDRREQ: {10039}
Result := 'Destination address required';
WSAEMSGSIZE: {10040}
Result := 'Message too long';
WSAEPROTOTYPE: {10041}
Result := 'Protocol wrong type for Socket';
WSAENOPROTOOPT: {10042}
Result := 'Protocol not available';
WSAEPROTONOSUPPORT: {10043}
Result := 'Protocol not supported';
WSAESOCKTNOSUPPORT: {10044}
Result := 'Socket not supported';
WSAEOPNOTSUPP: {10045}
Result := 'Operation not supported on Socket';
WSAEPFNOSUPPORT: {10046}
Result := 'Protocol family not supported';
WSAEAFNOSUPPORT: {10047}
Result := 'Address family not supported';
WSAEADDRINUSE: {10048}
Result := 'Address already in use';
WSAEADDRNOTAVAIL: {10049}
Result := 'Can''t assign requested address';
WSAENETDOWN: {10050}
Result := 'Network is down';
WSAENETUNREACH: {10051}
Result := 'Network is unreachable';
WSAENETRESET: {10052}
Result := 'Network dropped connection on reset';
WSAECONNABORTED: {10053}
Result := 'Software caused connection abort';
WSAECONNRESET: {10054}
Result := 'Connection reset by peer';
WSAENOBUFS: {10055}
Result := 'No Buffer space available';
WSAEISCONN: {10056}
Result := 'Socket is already connected';
WSAENOTCONN: {10057}
Result := 'Socket is not connected';
WSAESHUTDOWN: {10058}
Result := 'Can''t send after Socket shutdown';
WSAETOOMANYREFS: {10059}
Result := 'Too many references:can''t splice';
WSAETIMEDOUT: {10060}
Result := 'Connection timed out';
WSAECONNREFUSED: {10061}
Result := 'Connection refused';
WSAELOOP: {10062}
Result := 'Too many levels of symbolic links';
WSAENAMETOOLONG: {10063}
Result := 'File name is too long';
WSAEHOSTDOWN: {10064}
Result := 'Host is down';
WSAEHOSTUNREACH: {10065}
Result := 'No route to host';
WSAENOTEMPTY: {10066}
Result := 'Directory is not empty';
WSAEPROCLIM: {10067}
Result := 'Too many processes';
WSAEUSERS: {10068}
Result := 'Too many users';
WSAEDQUOT: {10069}
Result := 'Disk quota exceeded';
WSAESTALE: {10070}
Result := 'Stale NFS file handle';
WSAEREMOTE: {10071}
Result := 'Too many levels of remote in path';
WSASYSNOTREADY: {10091}
Result := 'Network subsystem is unusable';
WSAVERNOTSUPPORTED: {10092}
Result := 'Winsock DLL cannot support this application';
WSANOTINITIALISED: {10093}
Result := 'Winsock not initialized';
WSAEDISCON: {10101}
Result := 'Disconnect';
WSAHOST_NOT_FOUND: {11001}
Result := 'Host not found';
WSATRY_AGAIN: {11002}
Result := 'Non authoritative - host not found';
WSANO_RECOVERY: {11003}
Result := 'Non recoverable error';
WSANO_DATA: {11004}
Result := 'Valid name, no data record of requested type'
else
Result := 'Not a Winsock error (' + IntToStr(ErrorCode) + ')';
end;
end;
{======================================================================}
constructor TSocksBlockSocket.Create;
begin
inherited Create;
FSocksIP:= '';
FSocksPort:= '1080';
FSocksTimeout:= 300000;
FSocksUsername:= '';
FSocksPassword:= '';
FUsingSocks := False;
FSocksResolver := True;
FSocksLastError := 0;
FSocksResponseIP := '';
FSocksResponsePort := '';
FSocksLocalIP := '';
FSocksLocalPort := '';
FSocksRemoteIP := '';
FSocksRemotePort := '';
FBypassFlag := False;
end;
function TSocksBlockSocket.SocksOpen: boolean;
var
Buf: string;
n: integer;
begin
Result := False;
FUsingSocks := False;
FBypassFlag := True;
try
if FSocksUsername = '' then
Buf := #5 + #1 + #0
else
Buf := #5 + #2 + #2 +#0;
SendString(Buf);
Buf := RecvBufferStr(2, FSocksTimeout);
if Length(Buf) < 2 then
Exit;
if Buf[1] <> #5 then
Exit;
n := Ord(Buf[2]);
case n of
0: //not need authorisation
;
2:
begin
Buf := #1 + char(Length(FSocksUsername)) + FSocksUsername
+ char(Length(FSocksPassword)) + FSocksPassword;
SendString(Buf);
Buf := RecvBufferStr(2, FSocksTimeout);
if Length(Buf) < 2 then
Exit;
if Buf[2] <> #0 then
Exit;
end;
else
Exit;
end;
FUsingSocks := True;
Result := True;
finally
FBypassFlag := False;
end;
end;
function TSocksBlockSocket.SocksRequest(Cmd: Byte;
const IP, Port: string): Boolean;
var
Buf: string;
begin
FBypassFlag := True;
try
Buf := #5 + char(Cmd) + #0 + SocksCode(IP, Port);
SendString(Buf);
Result := FLastError = 0;
finally
FBypassFlag := False;
end;
end;
function TSocksBlockSocket.SocksResponse: Boolean;
var
Buf, s: string;
x: integer;
begin
Result := False;
FBypassFlag := True;
try
FSocksResponseIP := '';
FSocksResponsePort := '';
Buf := RecvBufferStr(4, FSocksTimeout);
if FLastError <> 0 then
Exit;
if Buf[1] <> #5 then
Exit;
case Ord(Buf[4]) of
1:
s := RecvBufferStr(4, FSocksTimeout);
3:
begin
x := RecvByte(FSocksTimeout);
if FLastError <> 0 then
Exit;
s := char(x) + RecvBufferStr(x, FSocksTimeout);
end;
else
Exit;
end;
Buf := Buf + s + RecvBufferStr(2, FSocksTimeout);
if FLastError <> 0 then
Exit;
FSocksLastError := Ord(Buf[2]);
if FSocksLastError <> 0 then
Exit;
SocksDecode(Buf);
Result := True;
finally
FBypassFlag := False;
end;
end;
function TSocksBlockSocket.SocksCode(IP, Port: string): string;
begin
if IsIP(IP) then
Result := #1 + IPToID(IP)
else
if FSocksResolver then
Result := #3 + char(Length(IP)) + IP
else
Result := #1 + IPToID(ResolveName(IP));
Result := Result + CodeInt(synsock.htons(ResolvePort(Port)));
end;
function TSocksBlockSocket.SocksDecode(Value: string): integer;
var
Atyp: Byte;
y, n: integer;
w: Word;
begin
FSocksResponsePort := '0';
if Length(Value) < 4 then
Exit;
Atyp := Ord(Value[4]);
Result := 5;
case Atyp of
1:
begin
if Length(Value) < 10 then
Exit;
FSocksResponseIP := Format('%d.%d.%d.%d',
[Ord(Value[5]), Ord(Value[6]), Ord(Value[7]), Ord(Value[8])]);
Result := 9;
end;
3:
begin
y := Ord(Value[5]);
if Length(Value) < (5 + y + 2) then
Exit;
for n := 6 to 6 + y - 1 do
FSocksResponseIP := FSocksResponseIP + Value[n];
Result := 5 + y + 1;
end;
else
Exit;
end;
w := DecodeInt(Value, Result);
FSocksResponsePort := IntToStr(w);
Result := Result + 2;
end;
{======================================================================}
destructor TUDPBlockSocket.Destroy;
begin
if Assigned(FSocksControlSock) then
FSocksControlSock.Free;
inherited;
end;
procedure TUDPBlockSocket.CreateSocket;
begin
FSocket := synsock.Socket(PF_INET, Integer(SOCK_DGRAM), IPPROTO_UDP);
FProtocol := IPPROTO_UDP;
inherited CreateSocket;
end;
function TUDPBlockSocket.EnableBroadcast(Value: Boolean): Boolean;
var
Opt: Integer;
Res: Integer;
begin
opt := Ord(Value);
Res := synsock.SetSockOpt(FSocket, SOL_SOCKET, SO_BROADCAST, @Opt, SizeOf(opt));
SockCheck(Res);
Result := res = 0;
ExceptCheck;
end;
procedure TUDPBlockSocket.Connect(IP, Port: string);
begin
AutoCreateSocket;
SetRemoteSin(IP, Port);
FBuffer := '';
DoStatus(HR_Connect, IP + ':' + Port);
end;
function TUDPBlockSocket.RecvBuffer(Buffer: Pointer; Length: Integer): Integer;
begin
Result := RecvBufferFrom(Buffer, Length);
end;
function TUDPBlockSocket.SendBuffer(Buffer: Pointer; Length: Integer): Integer;
begin
Result := SendBufferTo(Buffer, Length);
end;
function TUDPBlockSocket.UdpAssociation: Boolean;
var
b: Boolean;
begin
Result := True;
FUsingSocks := False;
if FSocksIP <> '' then
begin
Result := False;
if not Assigned(FSocksControlSock) then
FSocksControlSock := TTCPBlockSocket.Create;
FSocksControlSock.CloseSocket;
FSocksControlSock.CreateSocket;
FSocksControlSock.Connect(FSocksIP, FSocksPort);
if FSocksControlSock.LastError <> 0 then
Exit;
// if not assigned local port, assign it!
if not FBinded then
Bind('0.0.0.0', '0');
//open control TCP connection to SOCKS
b := FSocksControlSock.SocksOpen;
if b then
b := FSocksControlSock.SocksRequest(3, GetLocalSinIP, IntToStr(GetLocalSinPort));
if b then
b := FSocksControlSock.SocksResponse;
if not b and (FLastError = 0) then
FLastError := WSANO_RECOVERY;
FUsingSocks :=FSocksControlSock.UsingSocks;
FSocksRemoteIP := FSocksControlSock.FSocksResponseIP;
FSocksRemotePort := FSocksControlSock.FSocksResponsePort;
Result := b and (FLastError = 0);
end;
end;
function TUDPBlockSocket.SendBufferTo(Buffer: Pointer; Length: Integer): Integer;
var
SIp: string;
SPort: integer;
Buf: string;
begin
FUsingSocks := False;
if (FSocksIP <> '') and (not UdpAssociation) then
FLastError := WSANO_RECOVERY
else
begin
if FUsingSocks then
begin
Sip := GetRemoteSinIp;
SPort := GetRemoteSinPort;
SetRemoteSin(FSocksRemoteIP, FSocksRemotePort);
SetLength(Buf,Length);
Move(Buffer^, PChar(Buf)^, Length);
Buf := #0 + #0 + #0 + SocksCode(Sip, IntToStr(SPort)) + Buf;
Result := inherited SendBufferTo(PChar(Buf), System.Length(buf));
SetRemoteSin(Sip, IntToStr(SPort));
end
else
begin
Result := inherited SendBufferTo(Buffer, Length);
GetSins;
end;
end;
end;
function TUDPBlockSocket.RecvBufferFrom(Buffer: Pointer; Length: Integer): Integer;
var
Buf: string;
x: integer;
begin
Result := inherited RecvBufferFrom(Buffer, Length);
if FUsingSocks then
begin
SetLength(Buf, Result);
Move(Buffer^, PChar(Buf)^, Result);
x := SocksDecode(Buf);
Result := Result - x + 1;
Buf := Copy(Buf, x, Result);
Move(PChar(Buf)^, Buffer^, Result);
SetRemoteSin(FSocksResponseIP, FSocksResponsePort);
end;
end;
procedure TUDPBlockSocket.AddMulticast(MCastIP: string);
var
Multicast: TMulticast;
begin
Multicast.MCastAddr := synsock.inet_addr(PChar(MCastIP));
Multicast.MCastIfc := u_long(INADDR_ANY);
SockCheck(synsock.SetSockOpt(FSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP,
pchar(@Multicast), SizeOf(Multicast)));
ExceptCheck;
end;
procedure TUDPBlockSocket.DropMulticast(MCastIP: string);
var
Multicast: TMulticast;
begin
Multicast.MCastAddr := synsock.inet_addr(PChar(MCastIP));
Multicast.MCastIfc := u_long(INADDR_ANY);
SockCheck(synsock.SetSockOpt(FSocket, IPPROTO_IP, IP_DROP_MEMBERSHIP,
pchar(@Multicast), SizeOf(Multicast)));
ExceptCheck;
end;
procedure TUDPBlockSocket.SetMulticastTTL(TTL: integer);
var
Res: Integer;
begin
Res := synsock.SetSockOpt(FSocket, IPPROTO_IP, IP_MULTICAST_TTL, @TTL, SizeOf(TTL));
SockCheck(Res);
ExceptCheck;
end;
function TUDPBlockSocket.GetMulticastTTL:integer;
var
l: Integer;
begin
l := SizeOf(Result);
SockCheck(synsock.GetSockOpt(FSocket, IPPROTO_IP, IP_MULTICAST_TTL, @Result, l));
ExceptCheck;
end;
function TUDPBlockSocket.EnableMulticastLoop(Value: Boolean): Boolean;
var
Opt: Integer;
Res: Integer;
begin
opt := Ord(Value);
Res := synsock.SetSockOpt(FSocket, IPPROTO_IP, IP_MULTICAST_LOOP, @Opt, SizeOf(opt));
SockCheck(Res);
Result := res = 0;
ExceptCheck;
end;
{======================================================================}
function PasswordCallback(buf:PChar; size:Integer; rwflag:Integer; userdata: Pointer):Integer; cdecl;
var
Password: String;
begin
Password := '';
if TTCPBlockSocket(userdata) is TTCPBlockSocket then
Password := TTCPBlockSocket(userdata).SSLPassword;
if Length(Password) > (Size - 1) then
SetLength(Password, Size - 1);
Result := Length(Password);
StrLCopy(buf, PChar(Password + #0), Result + 1);
end;
constructor TTCPBlockSocket.Create;
begin
inherited Create;
FSslEnabled := False;
FSslBypass := False;
FSSLCiphers := 'DEFAULT';
FSSLCertificateFile := '';
FSSLPrivateKeyFile := '';
FSSLPassword := '';
FSsl := nil;
Fctx := nil;
FSSLLastError := 0;
FSSLLastErrorDesc := '';
FSSLverifyCert := False;
FHTTPTunnelIP := '';
FHTTPTunnelPort := '';
FHTTPTunnel := False;
FHTTPTunnelRemoteIP := '';
FHTTPTunnelRemotePort := '';
FHTTPTunnelUser := '';
FHTTPTunnelPass := '';
end;
destructor TTCPBlockSocket.Destroy;
begin
if FSslEnabled then
SslEnabled := False;
inherited;
end;
procedure TTCPBlockSocket.CreateSocket;
begin
FSocket := synsock.Socket(PF_INET, Integer(SOCK_STREAM), IPPROTO_TCP);
FProtocol := IPPROTO_TCP;
inherited CreateSocket;
end;
procedure TTCPBlockSocket.CloseSocket;
begin
if FSocket <> INVALID_SOCKET then
synsock.Shutdown(FSocket, 1);
inherited CloseSocket;
end;
function TTCPBlockSocket.WaitingData: Integer;
begin
Result := 0;
if FSslEnabled and not(FSslBypass) and not(FBypassFlag) then
Result := sslpending(Fssl);
if Result = 0 then
Result := inherited WaitingData;
end;
procedure TTCPBlockSocket.Listen;
var
b: Boolean;
Sip,SPort: string;
begin
if FSocksIP = '' then
begin
SockCheck(synsock.Listen(FSocket, SOMAXCONN));
GetSins;
end
else
begin
Sip := GetLocalSinIP;
if Sip = '0.0.0.0' then
Sip := LocalName;
SPort := IntToStr(GetLocalSinPort);
inherited Connect(FSocksIP, FSocksPort);
b := SocksOpen;
if b then
b := SocksRequest(2, Sip, SPort);
if b then
b := SocksResponse;
if not b and (FLastError = 0) then
FLastError := WSANO_RECOVERY;
FSocksLocalIP := FSocksResponseIP;
if FSocksLocalIP = '0.0.0.0' then
FSocksLocalIP := FSocksIP;
FSocksLocalPort := FSocksResponsePort;
FSocksRemoteIP := '';
FSocksRemotePort := '';
end;
ExceptCheck;
DoStatus(HR_Listen, '');
end;
function TTCPBlockSocket.Accept: TSocket;
var
Len: Integer;
begin
if FUsingSocks then
begin
if not SocksResponse and (FLastError = 0) then
FLastError := WSANO_RECOVERY;
FSocksRemoteIP := FSocksResponseIP;
FSocksRemotePort := FSocksResponsePort;
Result := FSocket;
end
else
begin
Len := SizeOf(FRemoteSin);
Result := synsock.Accept(FSocket, @FRemoteSin, @Len);
SockCheck(Result);
end;
ExceptCheck;
DoStatus(HR_Accept, '');
end;
procedure TTCPBlockSocket.Connect(IP, Port: string);
begin
AutoCreateSocket;
if FSocksIP <> '' then
SocksDoConnect(IP, Port)
else
if FHTTPTunnelIP <> '' then
HTTPTunnelDoConnect(IP, Port)
else
inherited Connect(IP, Port);
if FSslEnabled then
SSLDoConnect;
end;
procedure TTCPBlockSocket.SocksDoConnect(IP, Port: string);
var
b: Boolean;
begin
inherited Connect(FSocksIP, FSocksPort);
if FLastError = 0 then
begin
b := SocksOpen;
if b then
b := SocksRequest(1, IP, Port);
if b then
b := SocksResponse;
if not b and (FLastError = 0) then
FLastError := WSASYSNOTREADY;
FSocksLocalIP := FSocksResponseIP;
FSocksLocalPort := FSocksResponsePort;
FSocksRemoteIP := IP;
FSocksRemotePort := Port;
end;
ExceptCheck;
DoStatus(HR_Connect, IP + ':' + Port);
end;
procedure TTCPBlockSocket.HTTPTunnelDoConnect(IP, Port: string);
//bugfixed by Mike Green (mgreen@emixode.com)
var
s: string;
begin
try
FBypassFlag := True;
inherited Connect(FHTTPTunnelIP, FHTTPTunnelPort);
if FLastError <> 0 then
Exit;
FHTTPTunnel := False;
SendString('CONNECT ' + IP + ':' + Port + ' HTTP/1.0' + CRLF);
if FHTTPTunnelUser <> '' then
Sendstring('Proxy-Authorization: Basic ' +
EncodeBase64(FHTTPTunnelUser + ':' + FHTTPTunnelPass) + CRLF);
SendString(CRLF);
repeat
s := RecvTerminated(30000, #$0a);
if FLastError <> 0 then
Break;
if (Pos('HTTP/', s) = 1) and (Length(s) > 11) then
FHTTPTunnel := s[10] = '2';
until (s = '') or (s = #$0d);
if (FLasterror = 0) and not FHTTPTunnel then
FLastError := WSASYSNOTREADY;
FHTTPTunnelRemoteIP := IP;
FHTTPTunnelRemotePort := Port;
finally
FBypassFlag := False;
end;
ExceptCheck;
end;
procedure TTCPBlockSocket.SSLDoConnect;
begin
FLastError := 0;
if not FSSLEnabled then
SSLEnabled := True;
if (FLastError = 0) then
if sslsetfd(FSsl, FSocket) < 1 then
begin
FLastError := WSASYSNOTREADY;
SSLCheck;
end;
if (FLastError = 0) then
if sslconnect(FSsl) < 1 then
FLastError := WSASYSNOTREADY;
ExceptCheck;
end;
procedure TTCPBlockSocket.SSLDoShutdown;
begin
FLastError := 0;
if sslshutdown(FSsl) < 0 then
FLastError := WSASYSNOTREADY;
ExceptCheck;
SSLEnabled := False;
end;
function TTCPBlockSocket.GetLocalSinIP: string;
begin
if FUsingSocks then
Result := FSocksLocalIP
else
Result := inherited GetLocalSinIP;
end;
function TTCPBlockSocket.GetRemoteSinIP: string;
begin
if FUsingSocks then
Result := FSocksRemoteIP
else
if FHTTPTunnel then
Result := FHTTPTunnelRemoteIP
else
Result := inherited GetRemoteSinIP;
end;
function TTCPBlockSocket.GetLocalSinPort: Integer;
begin
if FUsingSocks then
Result := StrToIntDef(FSocksLocalPort, 0)
else
Result := inherited GetLocalSinPort;
end;
function TTCPBlockSocket.GetRemoteSinPort: Integer;
begin
if FUsingSocks then
Result := StrToIntDef(FSocksRemotePort, 0)
else
if FHTTPTunnel then
Result := StrToIntDef(FHTTPTunnelRemotePort, 0)
else
Result := inherited GetRemoteSinPort;
end;
function TTCPBlockSocket.SSLCheck: Boolean;
var
ErrBuf: array[0..255] of Char;
begin
Result := true;
FSSLLastErrorDesc := '';
FSSLLastError := ErrGetError;
ErrClearError;
if FSSLLastError <> 0 then
begin
Result := False;
ErrErrorString(FSSLLastError, ErrBuf);
FSSLLastErrorDesc := ErrBuf;
end;
end;
function TTCPBlockSocket.SetSslKeys: boolean;
begin
Result := True;
if FSSLCertificateFile <> '' then
if SslCtxUseCertificateChainFile(FCtx, PChar(FSSLCertificateFile)) <> 1 then
begin
Result := False;
SSLCheck;
Exit;
end;
if FSSLPrivateKeyFile <> '' then
if SslCtxUsePrivateKeyFile(FCtx, PChar(FSSLPrivateKeyFile), 1) <> 1 then
begin
Result := False;
SSLCheck;
Exit;
end;
if FSSLCertCAFile <> '' then
if SslCtxLoadVerifyLocations(FCtx, PChar(FSSLCertCAFile), nil) <> 1 then
begin
Result := False;
SSLCheck;
end;
end;
procedure TTCPBlockSocket.SetSslEnabled(Value: Boolean);
var
err: Boolean;
begin
FLastError := 0;
if Value <> FSslEnabled then
if Value then
begin
FBuffer := '';
FSSLLastErrorDesc := '';
FSSLLastError := 0;
if InitSSLInterface then
begin
SslLibraryInit;
SslLoadErrorStrings;
err := False;
Fctx := nil;
Fctx := SslCtxNew(SslMethodV23);
if Fctx = nil then
begin
SSLCheck;
FlastError := WSAEPROTONOSUPPORT;
err := True;
end
else
begin
SslCtxSetCipherList(Fctx, PChar(FSSLCiphers));
if FSSLverifyCert then
SslCtxSetVerify(FCtx, SSL_VERIFY_PEER, nil)
else
SslCtxSetVerify(FCtx, SSL_VERIFY_NONE, nil);
SslCtxSetDefaultPasswdCb(FCtx, @PasswordCallback);
SslCtxSetDefaultPasswdCbUserdata(FCtx, self);
if not SetSSLKeys then
FLastError := WSAEINVAL
else
begin
Fssl := nil;
Fssl := SslNew(Fctx);
if Fssl = nil then
begin
SSLCheck;
FlastError := WSAEPROTONOSUPPORT;
err := True;
end;
end;
end;
if err then
DestroySSLInterface
else
FSslEnabled := True;
end
else
begin
DestroySSLInterface;
FlastError := WSAEPROTONOSUPPORT;
end;
end
else
begin
FBuffer := '';
sslfree(Fssl);
SslCtxFree(Fctx);
DestroySSLInterface;
FSslEnabled := False;
end;
ExceptCheck;
end;
function TTCPBlockSocket.RecvBuffer(Buffer: Pointer; Length: Integer): Integer;
var
err: integer;
begin
if FSslEnabled and not(FSslBypass) and not(FBypassFlag) then
begin
FLastError := 0;
repeat
Result := SslRead(FSsl, Buffer, Length);
err := SslGetError(FSsl, Result);
until (err <> SSL_ERROR_WANT_READ) and (err <> SSL_ERROR_WANT_WRITE);
if err = SSL_ERROR_ZERO_RETURN then
Result := 0
else
if (err <> 0) then
FLastError := WSASYSNOTREADY;
ExceptCheck;
DoStatus(HR_ReadCount, IntToStr(Result));
DoReadFilter(Buffer, Result);
end
else
Result := inherited RecvBuffer(Buffer, Length);
end;
function TTCPBlockSocket.SendBuffer(Buffer: Pointer; Length: Integer): Integer;
var
err: integer;
begin
if FSslEnabled and not(FSslBypass) and not(FBypassFlag) then
begin
FLastError := 0;
DoWriteFilter(Buffer, Length);
repeat
Result := SslWrite(FSsl, Buffer, Length);
err := SslGetError(FSsl, Result);
until (err <> SSL_ERROR_WANT_READ) and (err <> SSL_ERROR_WANT_WRITE);
if err = SSL_ERROR_ZERO_RETURN then
Result := 0
else
if (err <> 0) then
FLastError := WSASYSNOTREADY;
ExceptCheck;
DoStatus(HR_WriteCount, IntToStr(Result));
end
else
Result := inherited SendBuffer(Buffer, Length);
end;
function TTCPBlockSocket.SSLAcceptConnection: Boolean;
begin
FLastError := 0;
if not FSSLEnabled then
SSLEnabled := True;
if (FLastError = 0) then
if sslsetfd(FSsl, FSocket) < 1 then
begin
FLastError := WSASYSNOTREADY;
SSLCheck;
end;
if (FLastError = 0) then
if sslAccept(FSsl) < 1 then
FLastError := WSASYSNOTREADY;
ExceptCheck;
Result := FLastError = 0;
end;
function TTCPBlockSocket.SSLGetSSLVersion: string;
begin
Result := SSlGetVersion(FSsl);
end;
function TTCPBlockSocket.SSLGetPeerSubject: string;
var
cert: PX509;
s: string;
begin
cert := SSLGetPeerCertificate(Fssl);
setlength(s, 4096);
Result := SslX509NameOneline(SslX509GetSubjectName(cert), PChar(s), length(s));
SslX509Free(cert);
end;
function TTCPBlockSocket.SSLGetPeerIssuer: string;
var
cert: PX509;
s: string;
begin
cert := SSLGetPeerCertificate(Fssl);
setlength(s, 4096);
Result := SslX509NameOneline(SslX509GetIssuerName(cert), PChar(s), length(s));
SslX509Free(cert);
end;
function TTCPBlockSocket.SSLGetPeerSubjectHash: Cardinal;
var
cert: PX509;
begin
cert := SSLGetPeerCertificate(Fssl);
Result := SslX509NameHash(SslX509GetSubjectName(cert));
SslX509Free(cert);
end;
function TTCPBlockSocket.SSLGetPeerIssuerHash: Cardinal;
var
cert: PX509;
begin
cert := SSLGetPeerCertificate(Fssl);
Result := SslX509NameHash(SslX509GetIssuerName(cert));
SslX509Free(cert);
end;
function TTCPBlockSocket.SSLGetPeerFingerprint: string;
var
cert: PX509;
x: integer;
begin
cert := SSLGetPeerCertificate(Fssl);
setlength(Result, EVP_MAX_MD_SIZE);
SslX509Digest(cert, SslEvpMd5, PChar(Result), @x);
SetLength(Result, x);
SslX509Free(cert);
end;
{======================================================================}
//See 'winsock2.txt' file in distribute package!
procedure TICMPBlockSocket.CreateSocket;
begin
FSocket := synsock.Socket(PF_INET, Integer(SOCK_RAW), IPPROTO_ICMP);
FProtocol := IPPROTO_ICMP;
inherited CreateSocket;
end;
{======================================================================}
//See 'winsock2.txt' file in distribute package!
procedure TRAWBlockSocket.CreateSocket;
begin
FSocket := synsock.Socket(PF_INET, Integer(SOCK_RAW), IPPROTO_RAW);
FProtocol := IPPROTO_RAW;
inherited CreateSocket;
end;
{======================================================================}
constructor TSynaClient.Create;
begin
inherited Create;
FIPInterface := cAnyHost;
FTargetHost := cLocalhost;
FTargetPort := cAnyPort;
FTimeout := 5000;
end;
{======================================================================}
{$IFDEF ONCEWINSOCK}
initialization
begin
if not InitSocketInterface('') then
begin
e := ESynapseError.Create('Error loading Winsock DLL!');
e.ErrorCode := 0;
e.ErrorMessage := 'Error loading Winsock DLL!';
raise e;
end;
synsock.WSAStartup($101, WsaDataOnce);
end;
finalization
begin
synsock.WSACleanup;
DestroySocketInterface;
end;
{$ENDIF}
end.