IdSocksServer.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.15 12/2/2004 4:23:58 PM JPMugaas
  18. Adjusted for changes in Core.
  19. Rev 1.14 5/30/2004 7:50:44 PM DSiders
  20. Corrected case in ancestor for TIdCustomSocksServer.
  21. Rev 1.13 2004.03.03 10:28:48 AM czhower
  22. Removed warnings.
  23. Rev 1.12 2004.02.03 5:44:24 PM czhower
  24. Name changes
  25. Rev 1.11 1/21/2004 4:03:44 PM JPMugaas
  26. InitComponent
  27. Rev 1.10 2003.10.21 9:13:14 PM czhower
  28. Now compiles.
  29. Rev 1.9 2003.10.12 7:23:52 PM czhower
  30. Compile todos
  31. Rev 1.8 9/19/2003 04:27:04 PM JPMugaas
  32. Removed IdFTPServer so Indy can compile with Kudzu's new changes.
  33. Rev 1.7 9/16/2003 11:59:16 PM JPMugaas
  34. Should compile.
  35. Rev 1.6 1/20/2003 1:15:38 PM BGooijen
  36. Changed to TIdTCPServer / TIdCmdTCPServer classes
  37. Rev 1.5 1/17/2003 07:10:54 PM JPMugaas
  38. Now compiles under new framework.
  39. Rev 1.4 1/9/2003 06:09:36 AM JPMugaas
  40. Updated for IdContext API change.
  41. Rev 1.3 1/8/2003 05:53:50 PM JPMugaas
  42. Switched stuff to IdContext.
  43. Rev 1.2 12-8-2002 18:08:56 BGooijen
  44. Changed to use TIdIOHandlerStack for the .IPVersion
  45. Rev 1.1 12/7/2002 06:43:26 PM JPMugaas
  46. These should now compile except for Socks server. IPVersion has to be a
  47. property someplace for that.
  48. Rev 1.0 11/13/2002 08:01:18 AM JPMugaas
  49. }
  50. {*****************************************************************************}
  51. {* IdSocksServer.pas *}
  52. {*****************************************************************************}
  53. {*===========================================================================*}
  54. {* DESCRIPTION *}
  55. {*****************************************************************************}
  56. {* PROJECT : Indy 10 *}
  57. {* AUTHOR : Bas Gooijen ([email protected]) *}
  58. {* MAINTAINER : Bas Gooijen *}
  59. {*...........................................................................*}
  60. {* DESCRIPTION *}
  61. {* Indy SOCKS 4/5 Server *}
  62. {* *}
  63. {* QUICK NOTES: *}
  64. {* Socks5 GSSAPI-authentication is NOT supported. *}
  65. {* UDP ASSOCIATE is NOT supported *}
  66. {*...........................................................................*}
  67. {* HISTORY *}
  68. {* DATE VERSION AUTHOR REASONS *}
  69. {* *}
  70. {* 19/05/2002 1.0 Bas Gooijen Initial start *}
  71. {* 24/05/2002 1.0 Bas Gooijen Added socks 5 authentication *}
  72. {* 08/06/2002 1.0 Bas Gooijen Revised code *}
  73. {* 08/09/2002 1.0 Bas Gooijen Added Socks 5 IPv6 Support *}
  74. {*****************************************************************************}
  75. unit IdSocksServer;
  76. interface
  77. {$i IdCompilerDefines.inc}
  78. uses
  79. Classes,
  80. IdAssignedNumbers,
  81. IdContext,
  82. IdCustomTCPServer,
  83. IdException,
  84. IdGlobal,
  85. IdTCPConnection,
  86. IdYarn,
  87. SysUtils;
  88. const
  89. IdSocks4ReplySuccess = 90;
  90. IdSocks4ReplyFailed = 91;
  91. IdSocks4ReplyNoIdent = 92;
  92. IdSocks4ReplyIdentNotConfirmed = 93;
  93. IdSocksAuthNoAuthenticationRequired = 0;
  94. IdSocksAuthGSSApi = 1;
  95. IdSocksAuthUsernamePassword = 2;
  96. IdSocksAuthNoAcceptableMethods = $FF;
  97. IdSocks5ReplySuccess = 0;
  98. IdSocks5ReplyGeneralFailure = 1;
  99. IdSocks5ReplyConnNotAllowed = 2;
  100. IdSocks5ReplyNetworkUnreachable = 3;
  101. IdSocks5ReplyHostUnreachable = 4;
  102. IdSocks5ReplyConnRefused = 5;
  103. IdSocks5ReplyTTLExpired = 6;
  104. IdSocks5ReplyCmdNotSupported = 7;
  105. IdSocks5ReplyAddrNotSupported = 8;
  106. type
  107. EIdSocksSvrException = class(EIdException);
  108. EIdSocksSvrNotSupported = class(EIdSocksSvrException);
  109. EIdSocksSvrInvalidLogin = class(EIdSocksSvrException);
  110. EIdSocksSvrSocks5WrongATYP = class(EIdSocksSvrException);
  111. EIdSocksSvrWrongSocksVer = class(EIdSocksSvrException);
  112. EIdSocksSvrWrongSocksCmd = class(EIdSocksSvrException);
  113. EIdSocksSvrAccessDenied = class(EIdSocksSvrException);
  114. EIdSocksSvrUnexpectedClose = class(EIdSocksSvrException);
  115. EIdSocksSvrPeerMismatch = class(EIdSocksSvrException);
  116. TIdSocksServerContext = class(TIdServerContext)
  117. protected
  118. FIPVersion: TIdIPVersion;
  119. FUsername: string;
  120. FPassword: string;
  121. FSocksVersion: Byte; // either 4 or 5, or 0 when version not known yet
  122. public
  123. constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
  124. //
  125. procedure SendV4Response(const AStatus: Byte; const AIP: String = ''; const APort: TIdPort = 0);
  126. //
  127. procedure SendV5MethodResponse(const AMethod: Byte);
  128. procedure SendV5AuthResponse(const AStatus: Byte);
  129. procedure SendV5Response(const AStatus: Byte; const AIP: String = ''; const APort: TIdPort = 0);
  130. //
  131. property IPVersion: TIdIPVersion read FIPVersion;
  132. property Username: string read FUsername;
  133. property Password: string read FPassword;
  134. property SocksVersion: Byte read FSocksVersion;
  135. end;
  136. TIdOnAuthenticate = procedure(AContext: TIdSocksServerContext; var AAuthenticated: Boolean) of object;
  137. TIdOnBeforeEvent = procedure(AContext: TIdSocksServerContext; var VHost: string; var VPort: TIdPort; var VAllowed: Boolean) of object;
  138. TIdOnVerifyEvent = procedure(AContext: TIdSocksServerContext; const AHost, APeer: string; var VAllowed: Boolean) of object;
  139. TIdCustomSocksServer = class(TIdCustomTCPServer)
  140. protected
  141. FNeedsAuthentication: Boolean;
  142. FAllowSocks4: Boolean;
  143. FAllowSocks5: Boolean;
  144. FOnAuthenticate: TIdOnAuthenticate;
  145. FOnBeforeSocksConnect: TIdOnBeforeEvent;
  146. FOnBeforeSocksBind: TIdOnBeforeEvent;
  147. FOnVerifyBoundPeer: TIdOnVerifyEvent;
  148. function DoExecute(AContext: TIdContext) : Boolean; override;
  149. procedure CommandConnect(AContext: TIdSocksServerContext; const AHost: string; const APort: TIdPort); virtual; abstract;
  150. procedure CommandBind(AContext: TIdSocksServerContext; const AHost: string; const APort: TIdPort); virtual; abstract;
  151. function DoAuthenticate(AContext: TIdSocksServerContext): Boolean; virtual;
  152. function DoBeforeSocksConnect(AContext: TIdSocksServerContext; var VHost: string; var VPort: TIdPort): Boolean; virtual;
  153. function DoBeforeSocksBind(AContext: TIdSocksServerContext; var VHost: string; var VPort: TIdPort): Boolean; virtual;
  154. function DoVerifyBoundPeer(AContext: TIdSocksServerContext; const AExpected, AActual: string): Boolean; virtual;
  155. procedure HandleConnectV4(AContext: TIdSocksServerContext; var VCommand: Byte; var VHost: string; var VPort: TIdPort); virtual;
  156. procedure HandleConnectV5(AContext: TIdSocksServerContext; var VCommand: Byte; var VHost: string; var VPort: TIdPort); virtual;
  157. public
  158. constructor Create(AOwner: TComponent); override;
  159. published
  160. property DefaultPort default IdPORT_SOCKS;
  161. property AllowSocks4: Boolean read FAllowSocks4 write FAllowSocks4;
  162. property AllowSocks5: Boolean read FAllowSocks5 write FAllowSocks5;
  163. property NeedsAuthentication: Boolean read FNeedsAuthentication write FNeedsAuthentication;
  164. property OnAuthenticate: TIdOnAuthenticate read FOnAuthenticate write FOnAuthenticate;
  165. property OnBeforeSocksConnect: TIdOnBeforeEvent read FOnBeforeSocksConnect write FOnBeforeSocksConnect;
  166. property OnBeforeSocksBind: TIdOnBeforeEvent read FOnBeforeSocksBind write FOnBeforeSocksBind;
  167. property OnVerifyBoundPeer: TIdOnVerifyEvent read FOnVerifyBoundPeer write FOnVerifyBoundPeer;
  168. end;
  169. TIdSocksServer = class(TIdCustomSocksServer)
  170. protected
  171. procedure CommandConnect(AContext: TIdSocksServerContext; const AHost: string; const APort: TIdPort); override;
  172. procedure CommandBind(AContext: TIdSocksServerContext; const AHost: string; const APort: TIdPort); override;
  173. end;
  174. TIdOnCommandEvent = procedure(AContext: TIdSocksServerContext; const AHost: string; const APort: TIdPort) of object;
  175. TIdEventSocksServer = class(TIdCustomSocksServer)
  176. protected
  177. FOnCommandConnect: TIdOnCommandEvent;
  178. FOnCommandBind: TIdOnCommandEvent;
  179. procedure CommandConnect(AContext: TIdSocksServerContext; const AHost: string; const APort: TIdPort) ; override;
  180. procedure CommandBind(AContext: TIdSocksServerContext; const AHost: string; const APort: TIdPort); override;
  181. published
  182. property OnCommandConnect: TIdOnCommandEvent read FOnCommandConnect write FOnCommandConnect;
  183. property OnCommandBind: TIdOnCommandEvent read FOnCommandBind write FOnCommandBind;
  184. end;
  185. implementation
  186. uses
  187. IdGlobalProtocols,
  188. IdIOHandlerStack,
  189. IdIPAddress,
  190. IdResourceStringsProtocols,
  191. IdTcpClient,
  192. IdSimpleServer,
  193. IdSocketHandle,
  194. IdStack;
  195. function IPToBytes(AIP: string; const AIPVersion: TIdIPVersion): TIdBytes;
  196. var
  197. LIP: TIdIPAddress;
  198. begin
  199. if AIPVersion = Id_IPv4 then begin
  200. SetLength(Result, 4);
  201. end else begin
  202. SetLength(Result, 16);
  203. end;
  204. LIP := TIdIPAddress.MakeAddressObject(AIP, AIPVersion);
  205. try
  206. if Assigned(LIP) then begin
  207. CopyTIdBytes(LIP.HToNBytes, 0, Result, 0, Length(Result));
  208. end else begin
  209. FillBytes(Result, Length(Result), 0);
  210. end;
  211. finally
  212. LIP.Free;
  213. end;
  214. end;
  215. procedure TransferData(const FromConn, ToConn: TIdTCPConnection);
  216. const
  217. cMaxBufSize: Integer = 4096;
  218. var
  219. LBuffer: TMemoryStream;
  220. LAmount: Integer;
  221. function ReadFrom(AFrom: TIdTCPConnection): Integer;
  222. begin
  223. AFrom.IOHandler.CheckForDataOnSource(25);
  224. Result := IndyMin(AFrom.IOHandler.InputBuffer.Size, cMaxBufSize);
  225. if Result > 0 then begin
  226. LBuffer.Position := 0;
  227. AFrom.IOHandler.InputBuffer.ExtractToStream(LBuffer, Result);
  228. end;
  229. end;
  230. begin
  231. LBuffer := TMemoryStream.Create;
  232. try
  233. LBuffer.Size := cMaxBufSize;
  234. while FromConn.Connected and ToConn.Connected do
  235. begin
  236. // TODO: use TIdSocketList here
  237. LAmount := ReadFrom(FromConn);
  238. if LAmount > 0 then begin
  239. LBuffer.Position := 0;
  240. ToConn.IOHandler.Write(LBuffer, LAmount);
  241. end;
  242. LAmount := ReadFrom(ToConn);
  243. if LAmount > 0 then begin
  244. LBuffer.Position := 0;
  245. FromConn.IOHandler.Write(LBuffer, LAmount);
  246. end;
  247. end;
  248. finally
  249. LBuffer.Free;
  250. end;
  251. end;
  252. function TIdCustomSocksServer.DoAuthenticate(AContext: TIdSocksServerContext): Boolean;
  253. begin
  254. Result := Assigned(OnAuthenticate);
  255. if Result then begin
  256. OnAuthenticate(AContext, Result);
  257. end;
  258. end;
  259. function TIdCustomSocksServer.DoBeforeSocksConnect(AContext: TIdSocksServerContext;
  260. var VHost: string; var VPort: TIdPort): Boolean;
  261. begin
  262. Result := True;
  263. if Assigned(OnBeforeSocksConnect) then begin
  264. OnBeforeSocksConnect(AContext, VHost, VPort, Result);
  265. end;
  266. end;
  267. function TIdCustomSocksServer.DoBeforeSocksBind(AContext: TIdSocksServerContext;
  268. var VHost: string; var VPort: TIdPort): Boolean;
  269. begin
  270. Result := True;
  271. if Assigned(OnBeforeSocksBind) then begin
  272. OnBeforeSocksBind(AContext, VHost, VPort, Result);
  273. end;
  274. end;
  275. function TIdCustomSocksServer.DoVerifyBoundPeer(AContext: TIdSocksServerContext;
  276. const AExpected, AActual: string): Boolean;
  277. begin
  278. Result := True;
  279. if Assigned(OnVerifyBoundPeer) then begin
  280. OnVerifyBoundPeer(AContext, AExpected, AActual, Result);
  281. end;
  282. end;
  283. procedure TIdCustomSocksServer.HandleConnectV4(AContext: TIdSocksServerContext;
  284. var VCommand: Byte; var VHost: string; var VPort: TIdPort);
  285. var
  286. LData: TIdBytes;
  287. LBinding: TIdSocketHandle;
  288. LUserId: string;
  289. begin
  290. AContext.Connection.IOHandler.ReadBytes(LData, 7);
  291. VCommand := LData[0];
  292. VPort := GStack.NetworkToHost(BytesToUInt16(LData, 1));
  293. VHost := BytesToIPv4Str(LData, 3);
  294. LUserId := AContext.Connection.IOHandler.ReadLn(#0);
  295. // According to the Socks 4a spec:
  296. //
  297. // "For version 4A, if the client cannot resolve the destination
  298. // host's domain name to find its IP address, it should set the first
  299. // three bytes of DSTIP to NULL and the last byte to a non-zero value.
  300. //
  301. // In other words, do not check for '0.0.0.1' specifically, but '0.0.0.x' generically.
  302. // if VHost = '0.0.0.1' then begin
  303. if (LData[3] = 0) and (LData[4] = 0) and (LData[5] = 0) and (LData[6] <> 0) then begin
  304. VHost := AContext.Connection.IOHandler.ReadLn(#0);
  305. LBinding := AContext.Binding;
  306. if LBinding <> nil then begin
  307. AContext.FIPVersion := LBinding.IPVersion;
  308. end else begin
  309. AContext.FIPVersion := ID_DEFAULT_IP_VERSION;
  310. end;
  311. end else begin
  312. AContext.FIPVersion := Id_IPv4;
  313. end;
  314. if not NeedsAuthentication then begin
  315. AContext.FUsername := '';
  316. AContext.FPassword := '';
  317. end else begin
  318. AContext.FUsername := LUserId;
  319. AContext.FPassword := '';
  320. if not DoAuthenticate(AContext) then begin
  321. AContext.SendV4Response(IdSocks4ReplyIdentNotConfirmed);
  322. AContext.Connection.Disconnect;
  323. raise EIdSocksSvrInvalidLogin.Create(RSSocksSvrInvalidLogin);
  324. end;
  325. end;
  326. end;
  327. procedure TIdCustomSocksServer.HandleConnectV5(AContext: TIdSocksServerContext;
  328. var VCommand: Byte; var VHost: string; var VPort: TIdPort);
  329. var
  330. LType: Byte;
  331. LMethods, LData: TIdBytes;
  332. LIP6: TIdIPv6Address;
  333. I: Integer;
  334. LBinding: TIdSocketHandle;
  335. begin
  336. AContext.Connection.IOHandler.ReadBytes(LMethods, AContext.Connection.IOHandler.ReadByte);
  337. if not NeedsAuthentication then begin
  338. AContext.FUsername := '';
  339. AContext.FPassword := '';
  340. AContext.SendV5MethodResponse(IdSocksAuthNoAuthenticationRequired);
  341. end else begin
  342. if ByteIndex(IdSocksAuthUsernamePassword, LMethods) = -1 then begin
  343. AContext.SendV5MethodResponse(IdSocksAuthNoAcceptableMethods);
  344. AContext.Connection.Disconnect; // not sure the server has to disconnect
  345. raise EIdSocksSvrNotSupported.Create(RSSocksSvrNotSupported);
  346. end;
  347. AContext.SendV5MethodResponse(IdSocksAuthUsernamePassword);
  348. AContext.Connection.IOHandler.ReadByte; //subversion, we don't need it.
  349. AContext.FUsername := AContext.Connection.IOHandler.ReadString(AContext.Connection.IOHandler.ReadByte);
  350. AContext.FPassword := AContext.Connection.IOHandler.ReadString(AContext.Connection.IOHandler.ReadByte);
  351. if not DoAuthenticate(AContext) then begin
  352. AContext.SendV5AuthResponse(IdSocks5ReplyGeneralFailure);
  353. AContext.Connection.Disconnect;
  354. raise EIdSocksSvrInvalidLogin.Create(RSSocksSvrInvalidLogin);
  355. end;
  356. AContext.SendV5AuthResponse(IdSocks5ReplySuccess);
  357. end;
  358. AContext.Connection.IOHandler.ReadByte; // socks version, should be 5
  359. VCommand := AContext.Connection.IOHandler.ReadByte;
  360. AContext.Connection.IOHandler.ReadByte; // reserved, should be 0
  361. LType := AContext.Connection.IOHandler.ReadByte;
  362. case LType of
  363. 1:
  364. begin
  365. AContext.Connection.IOHandler.ReadBytes(LData, 6);
  366. VHost := BytesToIPv4Str(LData);
  367. VPort := GStack.NetworkToHost(BytesToUInt16(LData, 4));
  368. AContext.FIPVersion := Id_IPv4;
  369. end;
  370. 3:
  371. begin
  372. AContext.Connection.IOHandler.ReadBytes(LData, AContext.Connection.IOHandler.ReadByte+2);
  373. VHost := BytesToString(LData, 0, Length(LData)-2);
  374. VPort := GStack.NetworkToHost(BytesToUInt16(LData, Length(LData)-2));
  375. LBinding := AContext.Binding;
  376. if LBinding <> nil then begin
  377. AContext.FIPVersion := LBinding.IPVersion;
  378. end else begin
  379. AContext.FIPVersion := ID_DEFAULT_IP_VERSION;
  380. end;
  381. end;
  382. 4:
  383. begin
  384. AContext.Connection.IOHandler.ReadBytes(LData, 18);
  385. BytesToIPv6(LData, LIP6);
  386. for I := 0 to 7 do begin
  387. LIP6[I] := GStack.NetworkToHost(LIP6[I]);
  388. end;
  389. VHost := IPv6AddressToStr(LIP6);
  390. VPort := GStack.NetworkToHost(BytesToUInt16(LData, 16));
  391. AContext.FIPVersion := Id_IPv6;
  392. end;
  393. else
  394. begin
  395. AContext.SendV5Response(IdSocks5ReplyAddrNotSupported);
  396. AContext.Connection.Disconnect;
  397. raise EIdSocksSvrSocks5WrongATYP.Create(RSSocksSvrWrongATYP);
  398. end;
  399. end;
  400. end;
  401. function TIdCustomSocksServer.DoExecute(AContext: TIdContext): Boolean;
  402. var
  403. LCommand: Byte;
  404. LHost: string;
  405. LPort: TIdPort;
  406. LContext: TIdSocksServerContext;
  407. begin
  408. //just to keep the compiler happy
  409. Result := True;
  410. LContext := AContext as TIdSocksServerContext;
  411. LContext.FSocksVersion := AContext.Connection.IOHandler.ReadByte;
  412. if not (
  413. ((LContext.SocksVersion = 4) and AllowSocks4) or
  414. ((LContext.SocksVersion = 5) and AllowSocks5)
  415. ) then
  416. begin
  417. case LContext.SocksVersion of
  418. 4: LContext.SendV4Response(IdSocks4ReplyFailed);
  419. 5: LContext.SendV5MethodResponse(IdSocksAuthNoAcceptableMethods);
  420. end;
  421. AContext.Connection.Disconnect;
  422. raise EIdSocksSvrWrongSocksVer.Create(RSSocksSvrWrongSocksVersion);
  423. end;
  424. case LContext.SocksVersion of
  425. 4: HandleConnectV4(LContext, LCommand, LHost, LPort);
  426. 5: HandleConnectV5(LContext, LCommand, LHost, LPort);
  427. end;
  428. case LCommand of
  429. 1: CommandConnect(LContext, LHost, LPort);
  430. 2: CommandBind(LContext, LHost, LPort);
  431. //3: //udp bind
  432. else
  433. begin
  434. case LContext.SocksVersion of
  435. 4: LContext.SendV4Response(IdSocks4ReplyFailed);
  436. 5: LContext.SendV5Response(IdSocks5ReplyCmdNotSupported);
  437. end;
  438. AContext.Connection.Disconnect;
  439. raise EIdSocksSvrWrongSocksCmd.Create(RSSocksSvrWrongSocksCommand);
  440. end;
  441. end;
  442. end;
  443. procedure TIdSocksServer.CommandConnect(AContext: TIdSocksServerContext;
  444. const AHost: string; const APort: TIdPort);
  445. var
  446. LClient: TIdTCPClient;
  447. LHost: String;
  448. LPort: TIdPort;
  449. begin
  450. LHost := AHost;
  451. LPort := APort;
  452. if not DoBeforeSocksConnect(AContext, LHost, LPort) then begin
  453. case AContext.SocksVersion of
  454. 4: AContext.SendV4Response(IdSocks4ReplyFailed);
  455. 5: AContext.SendV5Response(IdSocks5ReplyConnNotAllowed);
  456. end;
  457. AContext.Connection.Disconnect;
  458. raise EIdSocksSvrAccessDenied.Create(RSSocksSvrAccessDenied);
  459. end;
  460. LClient := nil;
  461. try
  462. try
  463. LClient := TIdTCPClient.Create(nil);
  464. LClient.Port := LPort;
  465. LClient.Host := LHost;
  466. LClient.IPVersion := AContext.IPVersion;
  467. LClient.ConnectTimeout := 120000; // 2 minutes
  468. // TODO: fire an event here so the user can customize the LClient as
  469. // needed (enable SSL, etc) before connecting to the target Host...
  470. LClient.Connect;
  471. except
  472. // TODO: for v5, check the socket error and send an appropriate reply
  473. // (Network unreachable, Host unreachable, Connection refused, etc)...
  474. case AContext.SocksVersion of
  475. 4: AContext.SendV4Response(IdSocks4ReplyFailed);
  476. 5: AContext.SendV5Response(IdSocks5ReplyHostUnreachable);
  477. end;
  478. AContext.Connection.Disconnect;
  479. raise;
  480. end;
  481. case AContext.SocksVersion of
  482. 4: AContext.SendV4Response(IdSocks4ReplySuccess, LClient.Socket.Binding.IP, LClient.Socket.Binding.Port);
  483. 5: AContext.SendV5Response(IdSocks5ReplySuccess, LClient.Socket.Binding.IP, LClient.Socket.Binding.Port);
  484. end;
  485. TransferData(AContext.Connection, LClient);
  486. LClient.Disconnect;
  487. AContext.Connection.Disconnect;
  488. finally
  489. LClient.Free;
  490. end;
  491. end;
  492. procedure TIdSocksServer.CommandBind(AContext: TIdSocksServerContext;
  493. const AHost: string; const APort: TIdPort);
  494. var
  495. LServer: TIdSimpleServer;
  496. LHost: String;
  497. LPort: TIdPort;
  498. begin
  499. LHost := AHost;
  500. LPort := APort;
  501. if not DoBeforeSocksBind(AContext, LHost, LPort) then begin
  502. case AContext.SocksVersion of
  503. 4: AContext.SendV4Response(IdSocks4ReplyFailed);
  504. 5: AContext.SendV5Response(IdSocks5ReplyConnNotAllowed);
  505. end;
  506. AContext.Connection.Disconnect;
  507. raise EIdSocksSvrAccessDenied.Create(RSSocksSvrAccessDenied);
  508. end;
  509. LServer := nil;
  510. try
  511. try
  512. LServer := TIdSimpleServer.Create(nil);
  513. LServer.IPVersion := AContext.IPVersion;
  514. LServer.BeginListen;
  515. except
  516. case AContext.SocksVersion of
  517. 4: AContext.SendV4Response(IdSocks4ReplyFailed);
  518. 5: AContext.SendV5Response(IdSocks5ReplyGeneralFailure);
  519. end;
  520. AContext.Connection.Disconnect;
  521. raise;
  522. end;
  523. case AContext.SocksVersion of
  524. 4: AContext.SendV4Response(IdSocks4ReplySuccess, LServer.Binding.IP, LServer.Binding.Port);
  525. 5: AContext.SendV5Response(IdSocks5ReplySuccess, LServer.Binding.IP, LServer.Binding.Port);
  526. end;
  527. try
  528. LServer.Listen(120000); // 2 minutes
  529. except
  530. case AContext.SocksVersion of
  531. 4: AContext.SendV4Response(IdSocks4ReplyFailed);
  532. 5: AContext.SendV5Response(IdSocks5ReplyGeneralFailure);
  533. end;
  534. AContext.Connection.Disconnect;
  535. raise;
  536. end;
  537. // verify that the connected host is the one actually expected
  538. if not DoVerifyBoundPeer(AContext, LHost, LServer.Binding.PeerIP) then begin
  539. LServer.Disconnect;
  540. case AContext.SocksVersion of
  541. 4: AContext.SendV4Response(IdSocks4ReplyFailed);
  542. 5: AContext.SendV5Response(IdSocks5ReplyGeneralFailure);
  543. end;
  544. AContext.Connection.Disconnect;
  545. raise EIdSocksSvrPeerMismatch.Create(RSSocksSvrPeerMismatch);
  546. end;
  547. case AContext.SocksVersion of
  548. 4: AContext.SendV4Response(IdSocks4ReplySuccess, LServer.Binding.PeerIP, LServer.Binding.PeerPort);
  549. 5: AContext.SendV5Response(IdSocks5ReplySuccess, LServer.Binding.PeerIP, LServer.Binding.PeerPort);
  550. end;
  551. TransferData(AContext.Connection, LServer);
  552. LServer.Disconnect;
  553. AContext.Connection.Disconnect;
  554. finally
  555. LServer.Free;
  556. end;
  557. end;
  558. procedure TIdEventSocksServer.CommandConnect(AContext: TIdSocksServerContext;
  559. const AHost: string; const APort: TIdPort);
  560. begin
  561. if Assigned(FOnCommandConnect) then begin
  562. FOnCommandConnect(AContext, AHost, APort);
  563. end;
  564. end;
  565. procedure TIdEventSocksServer.CommandBind(AContext: TIdSocksServerContext;
  566. const AHost: string; const APort: TIdPort);
  567. begin
  568. if Assigned(FOnCommandBind) then begin
  569. FOnCommandBind(AContext, AHost, APort);
  570. end;
  571. end;
  572. { Constructor / Destructors }
  573. constructor TIdCustomSocksServer.Create(AOwner: TComponent);
  574. begin
  575. inherited Create(AOwner);
  576. FContextClass := TIdSocksServerContext;
  577. DefaultPort := IdPORT_SOCKS;
  578. AllowSocks4 := True;
  579. AllowSocks5 := True;
  580. NeedsAuthentication := False;
  581. end;
  582. { TIdSocksServerContext }
  583. constructor TIdSocksServerContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
  584. begin
  585. inherited Create(AConnection, AYarn, AList);
  586. FIPVersion := ID_DEFAULT_IP_VERSION;
  587. FUsername := '';
  588. FPassword := '';
  589. FSocksVersion := 0;
  590. end;
  591. procedure TIdSocksServerContext.SendV4Response(const AStatus: Byte;
  592. const AIP: String = ''; const APort: TIdPort = 0);
  593. var
  594. LResponse: TIdBytes;
  595. begin
  596. SetLength(LResponse, 8);
  597. LResponse[0] := 0; // SOCKS version, null in SOCKS 4/4A
  598. LResponse[1] := AStatus;
  599. CopyTIdUInt16(GStack.HostToNetwork(APort), LResponse, 2);
  600. CopyTIdBytes(IPToBytes(AIP, Id_IPv4), 0, LResponse, 4, 4);
  601. Connection.IOHandler.Write(LResponse);
  602. end;
  603. procedure TIdSocksServerContext.SendV5MethodResponse(const AMethod: Byte);
  604. var
  605. LResponse: TIdBytes;
  606. begin
  607. SetLength(LResponse, 2);
  608. LResponse[0] := 5; // SOCKS version
  609. LResponse[1] := AMethod;
  610. Connection.IOHandler.Write(LResponse);
  611. end;
  612. procedure TIdSocksServerContext.SendV5AuthResponse(const AStatus: Byte);
  613. var
  614. LResponse: TIdBytes;
  615. begin
  616. SetLength(LResponse, 2);
  617. LResponse[0] := 1; // AUTH version
  618. LResponse[1] := AStatus;
  619. Connection.IOHandler.Write(LResponse);
  620. end;
  621. procedure TIdSocksServerContext.SendV5Response(const AStatus: Byte;
  622. const AIP: String = ''; const APort: TIdPort = 0);
  623. const
  624. LTypes: array[TIdIPVersion] of Byte = ($1, $4);
  625. var
  626. LResponse, LIP: TIdBytes;
  627. begin
  628. LIP := IPToBytes(AIP, IPVersion);
  629. SetLength(LResponse, 4 + Length(LIP) + 2);
  630. LResponse[0] := 5; // SOCKS version
  631. LResponse[1] := AStatus;
  632. LResponse[2] := 0;
  633. LResponse[3] := LTypes[IPVersion];
  634. CopyTIdBytes(LIP, 0, LResponse, 4, Length(LIP));
  635. CopyTIdUInt16(GStack.HostToNetwork(APort), LResponse, 4+Length(LIP));
  636. Connection.IOHandler.Write(LResponse);
  637. end;
  638. end.