IdSocksServer.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  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. procedure InitComponent; override;
  158. published
  159. property DefaultPort default IdPORT_SOCKS;
  160. property AllowSocks4: Boolean read FAllowSocks4 write FAllowSocks4;
  161. property AllowSocks5: Boolean read FAllowSocks5 write FAllowSocks5;
  162. property NeedsAuthentication: Boolean read FNeedsAuthentication write FNeedsAuthentication;
  163. property OnAuthenticate: TIdOnAuthenticate read FOnAuthenticate write FOnAuthenticate;
  164. property OnBeforeSocksConnect: TIdOnBeforeEvent read FOnBeforeSocksConnect write FOnBeforeSocksConnect;
  165. property OnBeforeSocksBind: TIdOnBeforeEvent read FOnBeforeSocksBind write FOnBeforeSocksBind;
  166. property OnVerifyBoundPeer: TIdOnVerifyEvent read FOnVerifyBoundPeer write FOnVerifyBoundPeer;
  167. end;
  168. TIdSocksServer = class(TIdCustomSocksServer)
  169. protected
  170. procedure CommandConnect(AContext: TIdSocksServerContext; const AHost: string; const APort: TIdPort); override;
  171. procedure CommandBind(AContext: TIdSocksServerContext; const AHost: string; const APort: TIdPort); override;
  172. end;
  173. TIdOnCommandEvent = procedure(AContext: TIdSocksServerContext; const AHost: string; const APort: TIdPort) of object;
  174. TIdEventSocksServer = class(TIdCustomSocksServer)
  175. protected
  176. FOnCommandConnect: TIdOnCommandEvent;
  177. FOnCommandBind: TIdOnCommandEvent;
  178. procedure CommandConnect(AContext: TIdSocksServerContext; const AHost: string; const APort: TIdPort) ; override;
  179. procedure CommandBind(AContext: TIdSocksServerContext; const AHost: string; const APort: TIdPort); override;
  180. published
  181. property OnCommandConnect: TIdOnCommandEvent read FOnCommandConnect write FOnCommandConnect;
  182. property OnCommandBind: TIdOnCommandEvent read FOnCommandBind write FOnCommandBind;
  183. end;
  184. implementation
  185. uses
  186. IdGlobalProtocols,
  187. IdIOHandlerStack,
  188. IdIPAddress,
  189. IdResourceStringsProtocols,
  190. IdTcpClient,
  191. IdSimpleServer,
  192. IdSocketHandle,
  193. IdStack;
  194. function IPToBytes(AIP: string; const AIPVersion: TIdIPVersion): TIdBytes;
  195. var
  196. LIP: TIdIPAddress;
  197. begin
  198. if AIPVersion = Id_IPv4 then begin
  199. SetLength(Result, 4);
  200. end else begin
  201. SetLength(Result, 16);
  202. end;
  203. LIP := TIdIPAddress.MakeAddressObject(AIP, AIPVersion);
  204. try
  205. if Assigned(LIP) then begin
  206. CopyTIdBytes(LIP.HToNBytes, 0, Result, 0, Length(Result));
  207. end else begin
  208. FillBytes(Result, Length(Result), 0);
  209. end;
  210. finally
  211. FreeAndNil(LIP);
  212. end;
  213. end;
  214. procedure TransferData(const FromConn, ToConn: TIdTCPConnection);
  215. const
  216. cMaxBufSize: Integer = 4096;
  217. var
  218. LBuffer: TMemoryStream;
  219. LAmount: Integer;
  220. function ReadFrom(AFrom: TIdTCPConnection): Integer;
  221. begin
  222. AFrom.IOHandler.CheckForDataOnSource(25);
  223. Result := IndyMin(AFrom.IOHandler.InputBuffer.Size, cMaxBufSize);
  224. if Result > 0 then begin
  225. LBuffer.Position := 0;
  226. AFrom.IOHandler.InputBuffer.ExtractToStream(LBuffer, Result);
  227. end;
  228. end;
  229. begin
  230. LBuffer := TMemoryStream.Create;
  231. try
  232. LBuffer.Size := cMaxBufSize;
  233. while FromConn.Connected and ToConn.Connected do
  234. begin
  235. // TODO: use TIdSocketList here
  236. LAmount := ReadFrom(FromConn);
  237. if LAmount > 0 then begin
  238. LBuffer.Position := 0;
  239. ToConn.IOHandler.Write(LBuffer, LAmount);
  240. end;
  241. LAmount := ReadFrom(ToConn);
  242. if LAmount > 0 then begin
  243. LBuffer.Position := 0;
  244. FromConn.IOHandler.Write(LBuffer, LAmount);
  245. end;
  246. end;
  247. finally
  248. FreeAndNil(LBuffer);
  249. end;
  250. end;
  251. function TIdCustomSocksServer.DoAuthenticate(AContext: TIdSocksServerContext): Boolean;
  252. begin
  253. Result := Assigned(OnAuthenticate);
  254. if Result then begin
  255. OnAuthenticate(AContext, Result);
  256. end;
  257. end;
  258. function TIdCustomSocksServer.DoBeforeSocksConnect(AContext: TIdSocksServerContext;
  259. var VHost: string; var VPort: TIdPort): Boolean;
  260. begin
  261. Result := True;
  262. if Assigned(OnBeforeSocksConnect) then begin
  263. OnBeforeSocksConnect(AContext, VHost, VPort, Result);
  264. end;
  265. end;
  266. function TIdCustomSocksServer.DoBeforeSocksBind(AContext: TIdSocksServerContext;
  267. var VHost: string; var VPort: TIdPort): Boolean;
  268. begin
  269. Result := True;
  270. if Assigned(OnBeforeSocksBind) then begin
  271. OnBeforeSocksBind(AContext, VHost, VPort, Result);
  272. end;
  273. end;
  274. function TIdCustomSocksServer.DoVerifyBoundPeer(AContext: TIdSocksServerContext;
  275. const AExpected, AActual: string): Boolean;
  276. begin
  277. Result := True;
  278. if Assigned(OnVerifyBoundPeer) then begin
  279. OnVerifyBoundPeer(AContext, AExpected, AActual, Result);
  280. end;
  281. end;
  282. procedure TIdCustomSocksServer.HandleConnectV4(AContext: TIdSocksServerContext;
  283. var VCommand: Byte; var VHost: string; var VPort: TIdPort);
  284. var
  285. LData: TIdBytes;
  286. LBinding: TIdSocketHandle;
  287. LUserId: string;
  288. begin
  289. AContext.Connection.IOHandler.ReadBytes(LData, 7);
  290. VCommand := LData[0];
  291. VPort := GStack.NetworkToHost(BytesToUInt16(LData, 1));
  292. VHost := BytesToIPv4Str(LData, 3);
  293. LUserId := AContext.Connection.IOHandler.ReadLn(#0);
  294. // According to the Socks 4a spec:
  295. //
  296. // "For version 4A, if the client cannot resolve the destination
  297. // host's domain name to find its IP address, it should set the first
  298. // three bytes of DSTIP to NULL and the last byte to a non-zero value.
  299. //
  300. // In other words, do not check for '0.0.0.1' specifically, but '0.0.0.x' generically.
  301. // if VHost = '0.0.0.1' then begin
  302. if (LData[3] = 0) and (LData[4] = 0) and (LData[5] = 0) and (LData[6] <> 0) then begin
  303. VHost := AContext.Connection.IOHandler.ReadLn(#0);
  304. LBinding := AContext.Binding;
  305. if LBinding <> nil then begin
  306. AContext.FIPVersion := LBinding.IPVersion;
  307. end else begin
  308. AContext.FIPVersion := ID_DEFAULT_IP_VERSION;
  309. end;
  310. end else begin
  311. AContext.FIPVersion := Id_IPv4;
  312. end;
  313. if not NeedsAuthentication then begin
  314. AContext.FUsername := '';
  315. AContext.FPassword := '';
  316. end else begin
  317. AContext.FUsername := LUserId;
  318. AContext.FPassword := '';
  319. if not DoAuthenticate(AContext) then begin
  320. AContext.SendV4Response(IdSocks4ReplyIdentNotConfirmed);
  321. AContext.Connection.Disconnect;
  322. raise EIdSocksSvrInvalidLogin.Create(RSSocksSvrInvalidLogin);
  323. end;
  324. end;
  325. end;
  326. procedure TIdCustomSocksServer.HandleConnectV5(AContext: TIdSocksServerContext;
  327. var VCommand: Byte; var VHost: string; var VPort: TIdPort);
  328. var
  329. LType: Byte;
  330. LMethods, LData: TIdBytes;
  331. LIP6: TIdIPv6Address;
  332. I: Integer;
  333. LBinding: TIdSocketHandle;
  334. begin
  335. AContext.Connection.IOHandler.ReadBytes(LMethods, AContext.Connection.IOHandler.ReadByte);
  336. if not NeedsAuthentication then begin
  337. AContext.FUsername := '';
  338. AContext.FPassword := '';
  339. AContext.SendV5MethodResponse(IdSocksAuthNoAuthenticationRequired);
  340. end else begin
  341. if ByteIndex(IdSocksAuthUsernamePassword, LMethods) = -1 then begin
  342. AContext.SendV5MethodResponse(IdSocksAuthNoAcceptableMethods);
  343. AContext.Connection.Disconnect; // not sure the server has to disconnect
  344. raise EIdSocksSvrNotSupported.Create(RSSocksSvrNotSupported);
  345. end;
  346. AContext.SendV5MethodResponse(IdSocksAuthUsernamePassword);
  347. AContext.Connection.IOHandler.ReadByte; //subversion, we don't need it.
  348. AContext.FUsername := AContext.Connection.IOHandler.ReadString(AContext.Connection.IOHandler.ReadByte);
  349. AContext.FPassword := AContext.Connection.IOHandler.ReadString(AContext.Connection.IOHandler.ReadByte);
  350. if not DoAuthenticate(AContext) then begin
  351. AContext.SendV5AuthResponse(IdSocks5ReplyGeneralFailure);
  352. AContext.Connection.Disconnect;
  353. raise EIdSocksSvrInvalidLogin.Create(RSSocksSvrInvalidLogin);
  354. end;
  355. AContext.SendV5AuthResponse(IdSocks5ReplySuccess);
  356. end;
  357. AContext.Connection.IOHandler.ReadByte; // socks version, should be 5
  358. VCommand := AContext.Connection.IOHandler.ReadByte;
  359. AContext.Connection.IOHandler.ReadByte; // reserved, should be 0
  360. LType := AContext.Connection.IOHandler.ReadByte;
  361. case LType of
  362. 1:
  363. begin
  364. AContext.Connection.IOHandler.ReadBytes(LData, 6);
  365. VHost := BytesToIPv4Str(LData);
  366. VPort := GStack.NetworkToHost(BytesToUInt16(LData, 4));
  367. AContext.FIPVersion := Id_IPv4;
  368. end;
  369. 3:
  370. begin
  371. AContext.Connection.IOHandler.ReadBytes(LData, AContext.Connection.IOHandler.ReadByte+2);
  372. VHost := BytesToString(LData, 0, Length(LData)-2);
  373. VPort := GStack.NetworkToHost(BytesToUInt16(LData, Length(LData)-2));
  374. LBinding := AContext.Binding;
  375. if LBinding <> nil then begin
  376. AContext.FIPVersion := LBinding.IPVersion;
  377. end else begin
  378. AContext.FIPVersion := ID_DEFAULT_IP_VERSION;
  379. end;
  380. end;
  381. 4:
  382. begin
  383. AContext.Connection.IOHandler.ReadBytes(LData, 18);
  384. BytesToIPv6(LData, LIP6);
  385. for I := 0 to 7 do begin
  386. LIP6[I] := GStack.NetworkToHost(LIP6[I]);
  387. end;
  388. VHost := IPv6AddressToStr(LIP6);
  389. VPort := GStack.NetworkToHost(BytesToUInt16(LData, 16));
  390. AContext.FIPVersion := Id_IPv6;
  391. end;
  392. else
  393. begin
  394. AContext.SendV5Response(IdSocks5ReplyAddrNotSupported);
  395. AContext.Connection.Disconnect;
  396. raise EIdSocksSvrSocks5WrongATYP.Create(RSSocksSvrWrongATYP);
  397. end;
  398. end;
  399. end;
  400. function TIdCustomSocksServer.DoExecute(AContext: TIdContext): Boolean;
  401. var
  402. LCommand: Byte;
  403. LHost: string;
  404. LPort: TIdPort;
  405. LContext: TIdSocksServerContext;
  406. begin
  407. //just to keep the compiler happy
  408. Result := True;
  409. LContext := AContext as TIdSocksServerContext;
  410. LContext.FSocksVersion := AContext.Connection.IOHandler.ReadByte;
  411. if not (
  412. ((LContext.SocksVersion = 4) and AllowSocks4) or
  413. ((LContext.SocksVersion = 5) and AllowSocks5)
  414. ) then
  415. begin
  416. case LContext.SocksVersion of
  417. 4: LContext.SendV4Response(IdSocks4ReplyFailed);
  418. 5: LContext.SendV5MethodResponse(IdSocksAuthNoAcceptableMethods);
  419. end;
  420. AContext.Connection.Disconnect;
  421. raise EIdSocksSvrWrongSocksVer.Create(RSSocksSvrWrongSocksVersion);
  422. end;
  423. case LContext.SocksVersion of
  424. 4: HandleConnectV4(LContext, LCommand, LHost, LPort);
  425. 5: HandleConnectV5(LContext, LCommand, LHost, LPort);
  426. end;
  427. case LCommand of
  428. 1: CommandConnect(LContext, LHost, LPort);
  429. 2: CommandBind(LContext, LHost, LPort);
  430. //3: //udp bind
  431. else
  432. begin
  433. case LContext.SocksVersion of
  434. 4: LContext.SendV4Response(IdSocks4ReplyFailed);
  435. 5: LContext.SendV5Response(IdSocks5ReplyCmdNotSupported);
  436. end;
  437. AContext.Connection.Disconnect;
  438. raise EIdSocksSvrWrongSocksCmd.Create(RSSocksSvrWrongSocksCommand);
  439. end;
  440. end;
  441. end;
  442. procedure TIdSocksServer.CommandConnect(AContext: TIdSocksServerContext;
  443. const AHost: string; const APort: TIdPort);
  444. var
  445. LClient: TIdTCPClient;
  446. LHost: String;
  447. LPort: TIdPort;
  448. begin
  449. LHost := AHost;
  450. LPort := APort;
  451. if not DoBeforeSocksConnect(AContext, LHost, LPort) then begin
  452. case AContext.SocksVersion of
  453. 4: AContext.SendV4Response(IdSocks4ReplyFailed);
  454. 5: AContext.SendV5Response(IdSocks5ReplyConnNotAllowed);
  455. end;
  456. AContext.Connection.Disconnect;
  457. raise EIdSocksSvrAccessDenied.Create(RSSocksSvrAccessDenied);
  458. end;
  459. LClient := nil;
  460. try
  461. try
  462. LClient := TIdTCPClient.Create(nil);
  463. LClient.Port := LPort;
  464. LClient.Host := LHost;
  465. LClient.IPVersion := AContext.IPVersion;
  466. LClient.ConnectTimeout := 120000; // 2 minutes
  467. // TODO: fire an event here so the user can customize the LClient as
  468. // needed (enable SSL, etc) before connecting to the target Host...
  469. LClient.Connect;
  470. except
  471. // TODO: for v5, check the socket error and send an appropriate reply
  472. // (Network unreachable, Host unreachable, Connection refused, etc)...
  473. case AContext.SocksVersion of
  474. 4: AContext.SendV4Response(IdSocks4ReplyFailed);
  475. 5: AContext.SendV5Response(IdSocks5ReplyHostUnreachable);
  476. end;
  477. AContext.Connection.Disconnect;
  478. raise;
  479. end;
  480. case AContext.SocksVersion of
  481. 4: AContext.SendV4Response(IdSocks4ReplySuccess, LClient.Socket.Binding.IP, LClient.Socket.Binding.Port);
  482. 5: AContext.SendV5Response(IdSocks5ReplySuccess, LClient.Socket.Binding.IP, LClient.Socket.Binding.Port);
  483. end;
  484. TransferData(AContext.Connection, LClient);
  485. LClient.Disconnect;
  486. AContext.Connection.Disconnect;
  487. finally
  488. FreeAndNil(LClient);
  489. end;
  490. end;
  491. procedure TIdSocksServer.CommandBind(AContext: TIdSocksServerContext;
  492. const AHost: string; const APort: TIdPort);
  493. var
  494. LServer: TIdSimpleServer;
  495. LHost: String;
  496. LPort: TIdPort;
  497. begin
  498. LHost := AHost;
  499. LPort := APort;
  500. if not DoBeforeSocksBind(AContext, LHost, LPort) then begin
  501. case AContext.SocksVersion of
  502. 4: AContext.SendV4Response(IdSocks4ReplyFailed);
  503. 5: AContext.SendV5Response(IdSocks5ReplyConnNotAllowed);
  504. end;
  505. AContext.Connection.Disconnect;
  506. raise EIdSocksSvrAccessDenied.Create(RSSocksSvrAccessDenied);
  507. end;
  508. LServer := nil;
  509. try
  510. try
  511. LServer := TIdSimpleServer.Create(nil);
  512. LServer.IPVersion := AContext.IPVersion;
  513. LServer.BeginListen;
  514. except
  515. case AContext.SocksVersion of
  516. 4: AContext.SendV4Response(IdSocks4ReplyFailed);
  517. 5: AContext.SendV5Response(IdSocks5ReplyGeneralFailure);
  518. end;
  519. AContext.Connection.Disconnect;
  520. raise;
  521. end;
  522. case AContext.SocksVersion of
  523. 4: AContext.SendV4Response(IdSocks4ReplySuccess, LServer.Binding.IP, LServer.Binding.Port);
  524. 5: AContext.SendV5Response(IdSocks5ReplySuccess, LServer.Binding.IP, LServer.Binding.Port);
  525. end;
  526. try
  527. LServer.Listen(120000); // 2 minutes
  528. except
  529. case AContext.SocksVersion of
  530. 4: AContext.SendV4Response(IdSocks4ReplyFailed);
  531. 5: AContext.SendV5Response(IdSocks5ReplyGeneralFailure);
  532. end;
  533. AContext.Connection.Disconnect;
  534. raise;
  535. end;
  536. // verify that the connected host is the one actually expected
  537. if not DoVerifyBoundPeer(AContext, LHost, LServer.Binding.PeerIP) then begin
  538. LServer.Disconnect;
  539. case AContext.SocksVersion of
  540. 4: AContext.SendV4Response(IdSocks4ReplyFailed);
  541. 5: AContext.SendV5Response(IdSocks5ReplyGeneralFailure);
  542. end;
  543. AContext.Connection.Disconnect;
  544. raise EIdSocksSvrPeerMismatch.Create(RSSocksSvrPeerMismatch);
  545. end;
  546. case AContext.SocksVersion of
  547. 4: AContext.SendV4Response(IdSocks4ReplySuccess, LServer.Binding.PeerIP, LServer.Binding.PeerPort);
  548. 5: AContext.SendV5Response(IdSocks5ReplySuccess, LServer.Binding.PeerIP, LServer.Binding.PeerPort);
  549. end;
  550. TransferData(AContext.Connection, LServer);
  551. LServer.Disconnect;
  552. AContext.Connection.Disconnect;
  553. finally
  554. FreeAndNil(LServer);
  555. end;
  556. end;
  557. procedure TIdEventSocksServer.CommandConnect(AContext: TIdSocksServerContext;
  558. const AHost: string; const APort: TIdPort);
  559. begin
  560. if Assigned(FOnCommandConnect) then begin
  561. FOnCommandConnect(AContext, AHost, APort);
  562. end;
  563. end;
  564. procedure TIdEventSocksServer.CommandBind(AContext: TIdSocksServerContext;
  565. const AHost: string; const APort: TIdPort);
  566. begin
  567. if Assigned(FOnCommandBind) then begin
  568. FOnCommandBind(AContext, AHost, APort);
  569. end;
  570. end;
  571. { Constructor / Destructors }
  572. procedure TIdCustomSocksServer.InitComponent;
  573. begin
  574. inherited InitComponent;
  575. FContextClass := TIdSocksServerContext;
  576. DefaultPort := IdPORT_SOCKS;
  577. AllowSocks4 := True;
  578. AllowSocks5 := True;
  579. NeedsAuthentication := False;
  580. end;
  581. { TIdSocksServerContext }
  582. constructor TIdSocksServerContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
  583. begin
  584. inherited Create(AConnection, AYarn, AList);
  585. FIPVersion := ID_DEFAULT_IP_VERSION;
  586. FUsername := '';
  587. FPassword := '';
  588. FSocksVersion := 0;
  589. end;
  590. procedure TIdSocksServerContext.SendV4Response(const AStatus: Byte;
  591. const AIP: String = ''; const APort: TIdPort = 0);
  592. var
  593. LResponse: TIdBytes;
  594. begin
  595. SetLength(LResponse, 8);
  596. LResponse[0] := 0; // SOCKS version, null in SOCKS 4/4A
  597. LResponse[1] := AStatus;
  598. CopyTIdUInt16(GStack.HostToNetwork(APort), LResponse, 2);
  599. CopyTIdBytes(IPToBytes(AIP, Id_IPv4), 0, LResponse, 4, 4);
  600. Connection.IOHandler.Write(LResponse);
  601. end;
  602. procedure TIdSocksServerContext.SendV5MethodResponse(const AMethod: Byte);
  603. var
  604. LResponse: TIdBytes;
  605. begin
  606. SetLength(LResponse, 2);
  607. LResponse[0] := 5; // SOCKS version
  608. LResponse[1] := AMethod;
  609. Connection.IOHandler.Write(LResponse);
  610. end;
  611. procedure TIdSocksServerContext.SendV5AuthResponse(const AStatus: Byte);
  612. var
  613. LResponse: TIdBytes;
  614. begin
  615. SetLength(LResponse, 2);
  616. LResponse[0] := 1; // AUTH version
  617. LResponse[1] := AStatus;
  618. Connection.IOHandler.Write(LResponse);
  619. end;
  620. procedure TIdSocksServerContext.SendV5Response(const AStatus: Byte;
  621. const AIP: String = ''; const APort: TIdPort = 0);
  622. const
  623. LTypes: array[TIdIPVersion] of Byte = ($1, $4);
  624. var
  625. LResponse, LIP: TIdBytes;
  626. begin
  627. LIP := IPToBytes(AIP, IPVersion);
  628. SetLength(LResponse, 4 + Length(LIP) + 2);
  629. LResponse[0] := 5; // SOCKS version
  630. LResponse[1] := AStatus;
  631. LResponse[2] := 0;
  632. LResponse[3] := LTypes[IPVersion];
  633. CopyTIdBytes(LIP, 0, LResponse, 4, Length(LIP));
  634. CopyTIdUInt16(GStack.HostToNetwork(APort), LResponse, 4+Length(LIP));
  635. Connection.IOHandler.Write(LResponse);
  636. end;
  637. end.