This repository has been archived by the owner on Nov 27, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
/
EvilWorks.Web.AsyncSockets.pas
2368 lines (2040 loc) · 66.3 KB
/
EvilWorks.Web.AsyncSockets.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//
// EvilLibrary by Vedran Vuk 2010-2012
//
// Name: EvilWorks.Web.AsyncSockets
// Description: WSAAsyncSelect type of sockets. Windows only. Solid, lightweight, easy to use.
// Base sockets for other clients in EvilWorks.Web.* portion of EvilLibrary.
// File last change date: October 19th. 2012
// File version: Dev 0.0.0
// Licence: Free.
//
{ TODO: FIX fast Connect/Disconnect lockup with resolve thread. }
{ TODO: Tweak TBuffer.Consume with ReallocMem. }
{ TODO: Bandwidth limiting. }
{ TODO: SSL context setup, loading keyfiles etc. }
{ TODO: Handle EventDisconnect better in TCPClient. }
{ TODO: Fix SSL for servers. }
{ TODO: Finish TCP server. }
unit EvilWorks.Web.AsyncSockets platform;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.Classes,
System.SysUtils,
EvilWorks.Api.Winsock2,
EvilWorks.Api.OpenSSL,
EvilWorks.System.SysUtils,
EvilWorks.System.StrUtils,
EvilWorks.Web.Base64,
EvilWorks.Web.Utils,
EvilWorks.Web.SSLFilter;
type
{ Forward declarations }
TAsyncSocket = class;
TProxyConnector = class;
TProxyItem = class;
TProxyChain = class;
TCustomAsyncTCPClient = class;
TCustomAsyncTCPServer = class;
TAsyncTCPClient = class;
TAsyncTCPServer = class;
{ Shared events }
TOnSocketLog = procedure(aSender: TObject; const aText: string) of object;
{ TProxyConnector Events }
TOnProxyEvent = procedure(aSender: TProxyConnector) of object;
TOnProxyConnecting = procedure(aSender: TProxyConnector; const aHost, aPort: string; const aProxyType: TProxyType) of object;
TOnProxyDataForSocket = procedure(aSender: TProxyConnector; const aData: pByte; const aSize: integer; var aResult: integer) of object;
TOnProxyNeedDetails = procedure(aSender: TProxyConnector; var aHost, aPort, aUser, aPass: ansistring) of object;
TOnProxyError = procedure(aSender: TProxyConnector; const aError: string) of object;
{ TProxyChainer Events }
TOnProxyChainConnected = procedure(aSender: TProxyChain) of object;
{ TAsyncSocket Events }
TOnSocketEvent = procedure(aSender: TAsyncSocket) of object;
TOnSocketClientEvent = procedure(aSender: TAsyncSocket; const aClient: TSocket) of object;
TOnSocketResolving = procedure(aSender: TAsyncSocket; const aHost, aPort: string; const aBindAddr: boolean) of object;
TOnSocketResolved = procedure(aSender: TAsyncSocket; const aCount: integer; const aBindAddr: boolean) of object;
TOnSocketAccept = procedure(aSender: TAsyncSocket; aClient: TAsyncTCPClient) of object;
TOnSocketDataAvailable = procedure(aSender: TAsyncSocket; const aDataAvailable: integer) of object;
TOnSocketClientDataAvailable = procedure(aSender: TAsyncSocket; const aClient: TSocket; const aDataAvailable: integer) of object;
TOnSocketError = procedure(aSender: TAsyncSocket; const aErrorText: string) of object;
{ TSocketList }
{ Always sorted socket list for fast lookups of TAsyncSocket instances from TAsyncSocket.WndProc. }
TSocketList = record
private
FItems: array of TAsyncSocket;
FCount: integer;
function Find(const aHandle: TSocket): integer;
function GetSocket(const aIndex: integer): TAsyncSocket;
public
procedure Clear; // Constructor/Destructor!
procedure Add(aSocket: TAsyncSocket);
function Get(const aHandle: TSocket): TAsyncSocket; overload;
procedure Del(aSocket: TAsyncSocket);
property Items[const aIndex: integer]: TAsyncSocket read GetSocket;
property Count: integer read FCount;
end;
{ TProxyConnector }
{ Abstract class for proxy handshake methods. }
TProxyConnector = class
private
FProxyItem: TProxyItem;
FOnDataForSocket: TOnProxyDataForSocket;
FOnNeedDetails : TOnProxyNeedDetails;
FOnConnecting : TonProxyConnecting;
FOnConnected : TOnProxyEvent;
FOnLog : TOnSocketLog;
FOnError : TOnProxyError;
// Error handling.
function HandleError(const aErr, aNeed: integer; const aError: string): boolean;
protected
// Descendants implement this perform the proxy handshake.
procedure RunConnector(const aData: pByte; const aSize: integer); virtual; abstract;
// Event handler callers
procedure EventConnecting(const aHost, aPort: string; const aProxyType: TProxyType);
procedure EventDataForSocket(const aData: pByte; const aSize: integer; var aResult: integer);
procedure EventNeedDetails(aSender: TProxyConnector; var aHost, aPort, aUser, aPass: ansistring);
procedure EventConnected;
procedure EventLog(const aText: string);
procedure EventError(const aError: string);
public
constructor Create(aProxyItem: TProxyItem);
procedure Assign(aSource: TProxyConnector);
// Handshake methods.
procedure ResetHandshake; virtual; abstract;
procedure StartHandshake;
procedure DataForProxy(const aData: pByte; const aSize: integer);
// Helpers.
function Index: integer;
// Events.
property OnConnecting: TOnProxyConnecting read FOnConnecting write FOnConnecting;
property OnDataForSocket: TOnProxyDataForSocket read FOnDataForSocket write FOnDataForSocket;
property OnOnNeedDetails: TOnProxyNeedDetails read FOnNeedDetails write FOnNeedDetails;
property OnConnected: TOnProxyEvent read FOnConnected write FOnConnected;
property OnLog: TOnSocketLog read FOnLog write FOnLog;
property OnError: TOnProxyError read FOnError write FOnError;
end;
{ THTTPProxyConnector }
{ Implements HTTP tunnel proxy handshake. }
THTTPProxyConnector = class(TProxyConnector)
type
THTTPHandshake = (hpStart, hpGetReply, hpDone);
private
FStage: THTTPHandshake;
protected
procedure RunConnector(const aData: pByte; const aSize: integer); override;
public
procedure ResetHandshake; override;
end;
{ TSocks4Connector }
{ Implements socks4/4a handshake. }
TSocks4Connector = class(TProxyConnector)
type
TSocks4Handshake = (h4Start, h4Done);
private
FStage: TSocks4Handshake;
protected
procedure RunConnector(const aData: pByte; const aSize: integer); override;
public
procedure ResetHandshake; override;
end;
{ TSocks5Connector }
{ Implements socks5 handshake. }
TSocks5Connector = class(TProxyConnector)
type
TSocks5Handshake = (h5Start, h5GetAuthSelReply, h5GetAuthReqReply, h5GetConnReqReply, h5Done);
private
FStage: TSocks5Handshake;
protected
procedure RunConnector(const aData: pByte; const aSize: integer); override;
public
procedure ResetHandshake; override;
end;
{ TProxyItem }
{ TCollectionItem container for TProxyConnector designtime. }
TProxyItem = class(TCollectionItem)
private
FProxyType: TProxyType;
FPort : string;
FPass : string;
FHost : string;
FUser : string;
procedure SetProxyType(const Value: TProxyType);
protected
FConnector: TProxyConnector;
function GetDisplayName: string; override;
public
constructor Create(aCollection: TCollection); override;
destructor Destroy; override;
procedure Assign(aSource: TPersistent); override;
property Connector: TProxyConnector read FConnector write FConnector;
published
property ProxyType: TProxyType read FProxyType write SetProxyType;
property Host : string read FHost write FHost;
property Port : string read FPort write FPort;
property User : string read FUser write FUser;
property Pass : string read FPass write FPass;
end;
{ TProxyChain. }
{ Container for a list of TProxyItem. Defines and manages a proxy chain and the handshakes. }
TProxyChain = class(TCollection)
private
FOwner : TCustomAsyncTCPClient;
FOnLog : TOnSocketLog;
FOnDataForSocket : TOnProxyDataForSocket;
FOnConnecting : TOnProxyConnecting;
FOnConnected : TOnProxyEvent;
FOnChainConnected: TOnProxyChainConnected;
FOnError : TOnProxyError;
// TProxyConnector handshake cycling stuff
FChainIndex: integer;
FFinished : boolean;
procedure BindCurrentProxy;
// Property getters/setters.
function GetItem(const aIndex: integer): TProxyItem;
procedure SetItem(const aIndex: integer; const aValue: TProxyItem);
protected
// TCollection overrides
function GetOwner: TPersistent; override;
// TProxyConnector event handlers
procedure EventConnecting(aSender: TProxyConnector; const aHost, aPort: string; const aProxyType: TProxyType);
procedure EventDataForSocket(aSender: TProxyConnector; const aData: pByte; const aSize: integer; var aResult: integer);
procedure EventNeedDetails(aSender: TProxyConnector; var aHost, aPort, aUser, aPass: ansistring);
procedure EventConnected(aSender: TProxyConnector);
procedure EventLog(aSender: TObject; const aText: string);
procedure EventError(aSender: TProxyConnector; const aError: string);
public
constructor Create(aSocket: TCustomAsyncTCPClient);
procedure Assign(aSource: TPersistent); override;
// TCollection overrides
function Add: TProxyItem;
function Insert(aIndex: integer): TProxyItem;
property Items[const aIndex: integer]: TProxyItem read GetItem write SetItem; default;
// Handshake methods
procedure ResetHandshake;
function NeedsHandshake: boolean;
procedure StartHandshake;
function GetTCPConnectAddr(var aHost, aPort: string): boolean;
procedure DataForProxy(const aData: pByte; const aSize: integer);
// Introduced events
property OnDataForSocket: TOnProxyDataForSocket read FOnDataForSocket write FOnDataForSocket;
property OnConnecting: TOnProxyConnecting read FOnConnecting write FOnConnecting;
property OnConnected: TOnProxyEvent read FOnConnected write FOnConnected;
property OnChainConnected: TOnProxyChainConnected read FOnChainConnected write FOnChainConnected;
property OnLog: TOnSocketLog read FOnLog write FOnLog;
property OnError: TOnProxyError read FOnError write FOnError;
end;
{ TAsyncSocket }
{ Base AsyncSocket class that manages messages and contains base functionality. }
TAsyncSocket = class(TComponent)
private const
CMsgWndCls = 'EvilAsyncSocketWndCls'; { Do not localize. }
public type
{ TSocketState }
TSocketState = (
ssResolvingBind, // Resolving address for binding the socket.
ssSocketCreated, // Socket created [and bound].
ssResolvingConnect, // Resolving target for connecting the socket.
ssResolvingListen, // Resolving listen address.
ssResolvedListen, // Resolved listen address, just before bind().
ssConnecting, // Connecting the socket.
ssConnected, // Socket connected.
ssDisconnecting, // Socket is disconnecting.
ssDisconnected, // Socket disconnected.
ssListening // Socket listening.
);
private
class var
// Class variables for managing message window and socket messages.
FWSData : TWSAData; // Winsock startup structure.
FWndCls : TWndClass; // Message receiver window class.
FClsAtm : ATOM; // Message Window class atom.
FMsgWnd : HWND; // Message receiver window handle.
FSckLst : TSocketList; // List of TAsyncSocket instances.
FSckMsg : integer; // Windows message that forwards socket messages.
FInitialized: boolean; // If class vars and objects are initialized properly, will be true.
FInitError : string; // Initialization string stored because we must not error in constructor.
private
var
// Socket variables.
FAddressFamily: TAddressFamily; // Address family used with RemoteHost:RemotePort
FBindPort : string; // Bind/Listen address.
FBindHost : string; // Bind/Listen port.
FSocket : TSocket; // Socket handle.
FSocketState : TSocketState; // State of the socket.
FResolveData : TGetAddrPoolAsyncData; // ResolveAddress() data.
FMutex : TMutex; // Messages mutex.
// Events
FOnResolving: TOnSocketResolving;
FOnResolved : TOnSocketResolved;
FOnLog : TOnSocketLog;
FOnError : TOnSocketError;
procedure EventSocketCreated(const aError: integer); virtual;
procedure EventBindAddressResolved(const aAddrPool: PAddrInfo; const aError: integer);
// Socket message management functions and events.
procedure MsgAccept(const aError: word); virtual;
procedure MsgConnect(const aError: word); virtual;
procedure MsgRead(const aError: word); virtual;
procedure MsgWrite(const aError: word); virtual;
procedure MsgClose(const aError: word); virtual;
procedure MsgTimer(const aTimerID: cardinal); virtual;
class function WndProc(aHwnd: HWND; aMsg: UINT; aWPar: WPARAM; aLPar: LPARAM): LRESULT; stdcall; static;
protected
// Mutex.
procedure Lock;
procedure Unlock;
// Timer utility
procedure TimerStart(const aTimerID, aElapse: cardinal);
procedure TimerStop(const aTimerID: cardinal);
// Socket management and class var access.
function CheckInitialized: boolean;
function MsgWnd: HWND;
function SetSocketMessages(const aMask: cardinal): integer;
procedure CreateSocket(const aSockType: TSocketType; const aListenSocket: boolean = False); virtual;
procedure DestroySocket; virtual;
procedure SetSocket(const aSocket: TSocket);
procedure SetSocketState(const aState: TSocketState);
procedure ResolveAddress(const aHost, aPort: string; const aAf: TAddressFamily; const aSt: TSocketType; aOnDone: TOnResolved; const aPassive: boolean = False);
property Socket: TSocket read FSocket write FSocket;
// Events.
procedure EventResolving(const aHost, aPort: string; const aBindAddr: boolean);
procedure EventResolved(const aCount: integer; const aBindAddr: boolean);
procedure EventLog(aSender: TObject; const aText: string);
procedure EventError(const aError: string);
// Error handling.
function HandleError(const aErr: integer; const aError: string): boolean; virtual;
// Properties for lowering in descendants.
property AddressFamily: TAddressFamily read FAddressFamily write FAddressFamily;
property SocketState: TSocketState read FSocketState;
property BindHost: string read FBindHost write FBindHost;
property BindPort: string read FBindPort write FBindPort;
property OnResolving: TOnSocketResolving read FOnResolving write FOnResolving;
property OnResolved: TOnSocketResolved read FOnResolved write FOnResolved;
property OnLog: TOnSocketLog read FOnLog write FOnLog;
property OnError: TOnSocketError read FOnError write FOnError;
public
class constructor Create;
class destructor Destroy;
constructor Create(aOwner: TComponent); overload; override;
destructor Destroy; override;
procedure Assign(aSource: TPersistent); override;
end;
{ TCustomAsyncTCPClient }
{ TCP client. }
TCustomAsyncTCPClient = class(TAsyncSocket)
type
TTimerMode = (tmOff, tmWaitRetry, tmConnectTimeout);
private
// Properties
FRemoteHost : string;
FRemotePort : string;
FConnectTimeout: cardinal;
// Connection variables.
FProxyChain : TProxyChain;
FSSL : TSSLFilter;
FAddress : PAddrInfo; // Holds resolved address pool.
FCurrentAddress: PAddrInfo; // Current address we are trying to connect to in address pool.
FTimerMode : TTimerMode; // Marks what EventTimer means when received.
// Events
FOnConnected : TOnSocketEvent;
FOnProxyConnecting : TOnProxyConnecting;
FOnProxyConnected : TOnProxyEvent;
FOnProxyChainConnected: TOnSocketEvent;
FOnDisconnected : TOnSocketEvent;
FOnConnecting : TOnSocketEvent;
FOnDataAvailable : TOnSocketDataAvailable;
FOnConnectTimeout : TOnSocketEvent;
// Misc handlers.
procedure EventSocketCreated(const aError: integer); override;
procedure EventTargetAddressResolved(const aAddrPool: PAddrInfo; const aError: integer);
// ProxyChain event handlers.
procedure EventProxyHave(aSender: TProxyConnector; const aData: pByte; const aSize: integer; var aResult: integer);
procedure EventProxyConnecting(aSender: TProxyConnector; const aHost, aPort: string; const aProxyType: TProxyType);
procedure EventProxyConnected(aSender: TProxyConnector);
procedure EventProxyChainConnected(aSender: TProxyChain);
procedure EventProxyLog(aSender: TObject; const aText: string);
procedure EventProxyError(aSender: TProxyConnector; const aError: string);
// TSSLFilter event handlers.
procedure EventSSLDataEncrypted(aSender: TSSLFilter; const aDataSize: integer);
procedure EventSSLDataDecrypted(aSender: TSSLFilter; const aDataSize: integer);
procedure EventSSLLog(aSender: TObject; const aText: string);
procedure EventSSLError(aSender: TSSLFilter; const aErrorText: string);
// FD_* messages from WndProc
// Overriden to handle TCP connections.
procedure MsgConnect(const aError: word); override;
procedure MsgRead(const aError: word); override;
procedure MsgClose(const aError: word); override;
procedure MsgTimer(const aTimerID: cardinal); override;
// Connect() procedure helpers.
function ConnectCycleAddressPool: boolean;
procedure ConnectToCurrentAddress;
// Getters/Setters
procedure SetProxyChain(const aValue: TProxyChain);
procedure SetSSL(const aValue: TSSLFilter);
procedure SetConnectTimeout(const Value: cardinal);
procedure SetRemoteHost(const Value: string);
procedure SetRemotePort(const Value: string);
protected
// For descendants to override.
procedure EventConnecting; virtual;
procedure EventConnect; virtual;
procedure EventDataAvailable(const aSize: integer); virtual;
procedure EventDisconnect; virtual;
// Error handling.
function HandleError(const aErr: integer; const aError: string): boolean; override;
// Properties for lowering in descendants.
property RemoteHost: string read FRemoteHost write SetRemoteHost;
property RemotePort: string read FRemotePort write SetRemotePort;
property ProxyChain: TProxyChain read FProxyChain write SetProxyChain;
property SSL: TSSLFilter read FSSL write SetSSL;
// If not connected in under this time (milliseconds), fail.
// For connecting over proxy chain, the value is applied to each proxy in chain.
// Default and maximum is 60 seconds (60000) - which is also a hard coded value in Winsock.
property ConnectTimeout: cardinal read FConnectTimeout write SetConnectTimeout;
// Events for lowering in descendants.
property OnConnecting: TOnSocketEvent read FOnConnecting write FOnConnecting;
property OnProxyConnecting: TOnProxyConnecting read FOnProxyConnecting write FOnProxyConnecting;
property OnProxyConnected: TOnProxyEvent read FOnProxyConnected write FOnProxyConnected;
property OnProxyChainConnected: TOnSocketEvent read FOnProxyChainConnected write FOnProxyChainConnected;
property OnConnectTimeout: TOnSocketEvent read FOnConnectTimeout write FOnConnectTimeout;
property OnConnected: TOnSocketEvent read FOnConnected write FOnConnected;
property OnDataAvailable: TOnSocketDataAvailable read FOnDataAvailable write FOnDataAvailable;
property OnDisconnected: TOnSocketEvent read FOnDisconnected write FOnDisconnected;
public
constructor Create(aOwner: TComponent); override;
destructor Destroy; override;
procedure Assign(aSource: TPersistent); override;
// TCP connect/disconnect.
procedure Connect; overload; virtual;
procedure Connect(const aHost, aPort: string); overload;
procedure Disconnect; virtual;
// Send/Recv functions.
function Send(const aData: pointer; const aSize: integer): integer;
function SendString(const aString: string): integer;
function SendLine(const aLine: string): integer;
function Recv(const aData: pointer; const aSize: integer): integer;
function RecvString: string;
property Socket;
end;
{ TAsyncTCPClient }
TAsyncTCPClient = class(TCustomAsyncTCPClient)
published
property AddressFamily;
property BindHost;
property BindPort;
property ConnectTimeout;
property ProxyChain;
property RemoteHost;
property RemotePort;
property SocketState;
property SSL;
property OnResolving;
property OnResolved;
property OnConnecting;
property OnProxyConnecting;
property OnProxyConnected;
property OnProxyChainConnected;
property OnConnectTimeout;
property OnConnected;
property OnDataAvailable;
property OnDisconnected;
property OnError;
property OnLog;
end;
implementation
{ =========== }
{ TSocketList }
{ =========== }
{ Get index of item, -1 if not found. }
function TSocketList.Find(const aHandle: TSocket): integer;
var
loIdx, hiIdx, res, i: integer;
begin
loIdx := 0;
hiIdx := (FCount - 1);
while (loIdx <= hiIdx) do
begin
i := ((loIdx + hiIdx) shr 1);
if (FItems[i].Socket < aHandle) then
res := - 1
else if (FItems[i].Socket > aHandle) then
res := 1
else
res := 0;
if (res < 0) then
loIdx := (i + 1)
else
begin
if (res = 0) then
Exit(i);
hiIdx := (i - 1);
end;
end;
Result := - 1;
end;
{ Bisect-right insertion. List is always sorted. }
procedure TSocketList.Add(aSocket: TAsyncSocket);
var
loIdx, hiIdx, i: integer;
begin
if (FCount <> 0) then
begin
loIdx := 0;
hiIdx := FCount;
while (loIdx < hiIdx) do
begin
i := ((loIdx + hiIdx) shr 1);
if (aSocket.Socket < FItems[i].Socket) then
hiIdx := i
else
loIdx := i + 1;
end;
i := loIdx;
end
else
i := 0;
SetLength(FItems, FCount + 1);
if (i < FCount) then
Move(FItems[i], FItems[i + 1], (FCount - i) * SizeOf(TAsyncSocket));
Inc(FCount);
FItems[i] := aSocket;
end;
{ Get item at aIndex. }
function TSocketList.Get(const aHandle: TSocket): TAsyncSocket;
var
idx: integer;
begin
idx := Find(aHandle);
if (idx < 0) then
Exit(nil);
Result := FItems[idx];
end;
{ Removes a TAsyncSocket instance from the list. }
procedure TSocketList.Del(aSocket: TAsyncSocket);
var
idx: integer;
begin
idx := Find(aSocket.Socket);
if (idx < 0) then
Exit;
Dec(FCount);
if (idx < FCount) then
Move(FItems[idx + 1], FItems[idx], (FCount - idx) * SizeOf(TAsyncSocket));
SetLength(FItems, FCount);
end;
{ Clears/creates/initializes the list. }
procedure TSocketList.Clear;
begin
SetLength(FItems, 0);
FCount := 0;
end;
{ Items getter. }
function TSocketList.GetSocket(const aIndex: integer): TAsyncSocket;
begin
if (aIndex < 0) or (aIndex >= FCount) then
Exit(nil);
Result := FItems[aIndex];
end;
{ =============== }
{ TProxyConnector }
{ =============== }
{ Constructor. }
constructor TProxyConnector.Create(aProxyItem: TProxyItem);
begin
FProxyItem := aProxyItem;
end;
{ Assign. }
procedure TProxyConnector.Assign(aSource: TProxyConnector);
begin
OnDataForSocket := aSource.OnDataForSocket;
OnConnected := aSource.OnConnected;
OnError := aSource.OnError;
end;
{ Starts the handshake. }
procedure TProxyConnector.StartHandshake;
begin
ResetHandshake;
RunConnector(nil, 0);
end;
{ aData of aSize available for proxy for reading. }
procedure TProxyConnector.DataForProxy(const aData: pByte; const aSize: integer);
begin
RunConnector(aData, aSize);
end;
{ Gets index in proxy chain. }
function TProxyConnector.Index: integer;
begin
Result := FProxyItem.Index;
end;
{ Checks aErr, returns True if handled, False otherwise. Returns False if (aErr != aNeed) as well. }
function TProxyConnector.HandleError(const aErr, aNeed: integer; const aError: string): boolean;
begin
if (aErr <> aNeed) or (aErr < 0) then
begin
Result := False;
ResetHandshake;
EventError(aError);
end
else
Result := True;
end;
{ Calls OnConnecting handler. }
procedure TProxyConnector.EventConnecting(const aHost, aPort: string; const aProxyType: TProxyType);
begin
if (Assigned(FOnConnecting)) then
FOnConnecting(Self, aHost, aPort, aProxyType);
end;
{ Calls OnProxyHave handler. This tells socket to send aData of aSize to peer and return send() result to aResult. }
procedure TProxyConnector.EventDataForSocket(const aData: pByte; const aSize: integer; var aResult: integer);
begin
if (Assigned(FOnDataForSocket)) then
FOnDataForSocket(Self, aData, aSize, aResult);
end;
{ Calls OnNeedNexInChain which returns connect details from next proxy in chain. }
procedure TProxyConnector.EventNeedDetails(aSender: TProxyConnector; var aHost, aPort, aUser, aPass: ansistring);
begin
if (Assigned(FOnNeedDetails)) then
FOnNeedDetails(aSender, aHost, aPort, aUser, aPass);
end;
{ Calls OnConnected handler. Proxy negotiation successfully completed and connected. }
procedure TProxyConnector.EventConnected;
begin
if (Assigned(FOnConnected)) then
FOnConnected(Self);
end;
{ Calls OnLog handler. }
procedure TProxyConnector.EventLog(const aText: string);
begin
if (Assigned(FOnLog)) then
FOnLog(Self, aText);
end;
{ Calls OnError handler. Negotiation error occured. Disconnect socket! }
procedure TProxyConnector.EventError(const aError: string);
begin
if (Assigned(FOnError)) then
FOnError(Self, Format('[%s]: %s', [Self.ClassName, aError]));
end;
{ =================== }
{ THTTPProxyConnector }
{ =================== }
{ Resets handshake state, gets ready to start new negotiation. }
procedure THTTPProxyConnector.ResetHandshake;
begin
inherited;
FStage := hpStart;
end;
{ Implements HTTP proxy handshake. }
procedure THTTPProxyConnector.RunConnector(const aData: pByte; const aSize: integer);
var
ret: integer;
procedure SendConnectRequest;
var
host, port, user, pass, data: ansistring;
begin
EventNeedDetails(Self, host, port, user, pass);
data := 'CONNECT ' + ansistring(host + ':' + port) + ' HTTP/1.1' + CCrLf;
if (user <> CEmpty) and (pass <> CEmpty) then
data := data + 'Authorization: Basic ' + ansistring(Base64Encode(user + ':' + pass)) + CCrLf;
data := data + CCrLf;
EventDataForSocket(@data[1], Length(data), ret);
if (HandleError(ret, Length(data), 'SendConnectRequest() failed.') = False) then
Exit;
FStage := hpGetReply;
end;
procedure RecvConnectReqResponse;
var
data: string;
begin
data := string(pansichar(aData));
data := TextToken(data, 1);
if (TextToInt(data, - 1) = - 1) then
begin
HandleError(0, 1, 'RecvConnectReqResponse(): Invalid reply.');
Exit;
end;
if (data <> '200') then
begin
HandleError(0, 1, 'RecvConnectReqResponse(): Connect failed: ' + data);
Exit;
end;
FStage := hpDone;
EventConnected;
end;
begin
case FStage of
hpStart:
SendConnectRequest;
hpGetReply:
RecvConnectReqResponse;
end; { case }
end;
{ ================ }
{ TSocks4Connector }
{ ================ }
{ Resets handshake state, gets ready to start new negotiation. }
procedure TSocks4Connector.ResetHandshake;
begin
inherited;
FStage := h4Start;
end;
{ Implements Socks4/4a proxy handshake. }
procedure TSocks4Connector.RunConnector(const aData: pByte; const aSize: integer);
begin
end;
{ ================ }
{ TSocks5Connector }
{ ================ }
{ Resets handshake state, gets ready to start new negotiation. }
procedure TSocks5Connector.ResetHandshake;
begin
inherited;
FStage := h5Start;
end;
{ Implements Socks5 proxy handshake. }
procedure TSocks5Connector.RunConnector(const aData: pByte; const aSize: integer);
var
ret : integer;
p : PByte;
Buff : array of Byte;
host, port, user, pass: ansistring;
procedure SendSupportedAuthMethods;
begin
// Send auth methods.
SetLength(Buff, 4);
Buff[0] := $05; // Socks version, must be $05.
Buff[1] := $02; // Num of methods supported.
Buff[2] := $00; // Method 1 - No auth.
Buff[3] := $02; // Method 2 - Username/Password.
EventDataForSocket(@Buff[0], 4, ret);
if (HandleError(ret, 4, 'SendSupportedAuthMethods(): Send failed.') = False) then
Exit;
FStage := h5GetAuthSelReply;
end;
procedure SendConnectRequest;
var
w: word;
begin
EventNeedDetails(Self, host, port, user, pass);
// Send connect request.
ServToPortNum(string(port), w);
SetLength(Buff, 7 + Length(host));
Buff[0] := $05; // Socks version.
Buff[1] := $01; // Establish TCP connection.
Buff[2] := $00; // Reserved.
Buff[3] := $03; // Destination type: Domain name.
Buff[4] := Length(host); // Length of hostname
Move(host[1], Buff[5], Buff[4]); // Hostname
Move(w, Buff[Buff[4] + 5], SizeOf(w)); // Port
EventDataForSocket(@Buff[0], Length(Buff), ret);
if (HandleError(ret, Length(Buff), 'SendConnectRequest(): Send failed.') = False) then
Exit;
FStage := h5GetConnReqReply;
end;
procedure RecvSelAuthMethod;
begin
// Recieve selected Auth method.
p := aData;
if (HandleError(p^, $05, 'RecvSelAuthMethod(): Invalid reply version.') = False) then
Exit;
Inc(p);
// User:pass auth requested, send user:pass.
if (p^ = 2) then
begin
EventNeedDetails(Self, host, port, user, pass);
// Send Username/Password
SetLength(Buff, 3 + Length(user) + Length(pass));
Buff[0] := $01; // Sending user:pass auth.
// Put username
Buff[1] := Length(user);
if (Buff[1] > 0) then
Move(user[1], Buff[2], Length(user));
// Put password
Buff[2 + Length(user) + 1] := Length(pass);
if (Buff[2 + Length(user) + 1] > 0) then
Move(pass[1], Buff[2 + Length(user) + 1], Length(pass));
// Send Username:Password auth.
EventDataForSocket(@Buff[0], Length(Buff), ret);
if (HandleError(ret, Length(Buff), 'RecvSelAuthMethod(): Send failed.') = False) then
Exit;
FStage := h5GetAuthReqReply;
end
else if (p^ <> 0) then // Some unsupported auth method requested.
HandleError(0, 1, 'RecvSelAuthMethod(): No supported auth methods.')
else
SendConnectRequest; // No auth requested, do connect request.
end;
procedure RecvUserPassResponse;
begin
// Get auth response.
p := aData;
if (HandleError(p^, $05, 'RecvSelAuthMethod(): Invalid reply version.') = False) then
Exit;
Inc(p);
if (p^ <> 0) then
begin
HandleError(0, 1, 'RecvSelAuthMethod(): Authentication failed.');
Exit;
end;
SendConnectRequest;
end;
procedure RecvConnectReqResponse;
begin
// Recieve connect response.
p := aData;
if (HandleError(p^, $05, 'RecvConnectReqResponse(): Invalid reply version.') = False) then
Exit;
Inc(p);
// Check for final "OK!".
if (p^ <> $00) then
begin
HandleError(0, 1, 'RecvConnectReqResponse(): Connect failed: ' + GetSocks5ErrorText(p^));
Exit;
end;
// Done with this proxy in chain.
FStage := h5Done;
EventConnected;
end;
begin
case FStage of
h5Start:
SendSupportedAuthMethods;
h5GetAuthSelReply:
RecvSelAuthMethod;
h5GetAuthReqReply:
RecvUserPassResponse;
h5GetConnReqReply:
RecvConnectReqResponse;
end; { case }
if (Length(Buff) <> 0) then
SetLength(Buff, 0);
end;
{ ========== }
{ TProxyItem }
{ ========== }
{ Constructor. }
constructor TProxyItem.Create(aCollection: TCollection);
begin
inherited Create(aCollection);
FProxyType := ptNone;
FConnector := nil;
end;
{ Destructor. }
destructor TProxyItem.Destroy;
begin
if (FConnector <> nil) then
FConnector.Free;
inherited;
end;
{ Assign. }
procedure TProxyItem.Assign(aSource: TPersistent);
begin
inherited;
if (aSource is TProxyItem) then
begin
ProxyType := TProxyItem(aSource).ProxyType;
Host := TProxyItem(aSource).Host;
Port := TProxyItem(aSource).Port;
User := TProxyItem(aSource).User;
Pass := TProxyItem(aSource).Pass;
end;
end;
{ GetDisplayName override. }
function TProxyItem.GetDisplayName: string;
begin
case FProxyType of
ptNone:
Result := '<Proxy disabled>';
ptHTTP:
Result := 'proxy://';
ptSocks4:
Result := 'socks4://';
ptSocks5:
Result := 'socks5://';
end;
if (Host <> '') and (Port <> '') then
Result := Result + Host + ':' + Port;
end;
{ ProxyType setter. }
procedure TProxyItem.SetProxyType(const Value: TProxyType);
begin
if (FProxyType = Value) then
Exit;
FProxyType := Value;
if (FConnector <> nil) then
FConnector.Free;
case FProxyType of
ptHTTP:
FConnector := THTTPProxyConnector.Create(Self);
ptSocks4:
FConnector := TSocks4Connector.Create(Self);
ptSocks5:
FConnector := TSocks5Connector.Create(Self);
end;