IdIOHandlerSocket.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  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.38 11/10/2004 8:25:54 AM JPMugaas
  18. Fix for AV caused by short-circut boolean evaluation.
  19. Rev 1.37 27.08.2004 21:58:20 Andreas Hausladen
  20. Speed optimization ("const" for string parameters)
  21. Rev 1.36 8/2/04 5:44:40 PM RLebeau
  22. Moved ConnectTimeout over from TIdIOHandlerStack
  23. Rev 1.35 7/21/2004 12:22:32 PM BGooijen
  24. Fix to .connected
  25. Rev 1.34 6/30/2004 12:31:34 PM BGooijen
  26. Added OnSocketAllocated
  27. Rev 1.33 4/24/04 12:52:52 PM RLebeau
  28. Added setter method to UseNagle property
  29. Rev 1.32 2004.04.18 12:52:02 AM czhower
  30. Big bug fix with server disconnect and several other bug fixed that I found
  31. along the way.
  32. Rev 1.31 2004.02.03 4:16:46 PM czhower
  33. For unit name changes.
  34. Rev 1.30 2/2/2004 11:46:46 AM BGooijen
  35. Dotnet and TransparentProxy
  36. Rev 1.29 2/1/2004 9:44:00 PM JPMugaas
  37. Start on reenabling Transparant proxy.
  38. Rev 1.28 2004.01.20 10:03:28 PM czhower
  39. InitComponent
  40. Rev 1.27 1/2/2004 12:02:16 AM BGooijen
  41. added OnBeforeBind/OnAfterBind
  42. Rev 1.26 12/31/2003 9:51:56 PM BGooijen
  43. Added IPv6 support
  44. Rev 1.25 11/4/2003 10:37:40 PM BGooijen
  45. JP's patch to fix the bound port
  46. Rev 1.24 10/19/2003 5:21:26 PM BGooijen
  47. SetSocketOption
  48. Rev 1.23 10/18/2003 1:44:06 PM BGooijen
  49. Added include
  50. Rev 1.22 2003.10.14 1:26:54 PM czhower
  51. Uupdates + Intercept support
  52. Rev 1.21 10/9/2003 8:09:06 PM SPerry
  53. bug fixes
  54. Rev 1.20 8/10/2003 2:05:50 PM SGrobety
  55. Dotnet
  56. Rev 1.19 2003.10.07 10:18:26 PM czhower
  57. Uncommneted todo code that is now non dotnet.
  58. Rev 1.18 2003.10.02 8:23:42 PM czhower
  59. DotNet Excludes
  60. Rev 1.17 2003.10.01 9:11:18 PM czhower
  61. .Net
  62. Rev 1.16 2003.10.01 5:05:12 PM czhower
  63. .Net
  64. Rev 1.15 2003.10.01 2:46:38 PM czhower
  65. .Net
  66. Rev 1.14 2003.10.01 11:16:32 AM czhower
  67. .Net
  68. Rev 1.13 2003.09.30 1:22:58 PM czhower
  69. Stack split for DotNet
  70. Rev 1.12 7/4/2003 08:26:44 AM JPMugaas
  71. Optimizations.
  72. Rev 1.11 7/1/2003 03:39:44 PM JPMugaas
  73. Started numeric IP function API calls for more efficiency.
  74. Rev 1.10 2003.06.30 5:41:56 PM czhower
  75. -Fixed AV that occurred sometimes when sockets were closed with chains
  76. -Consolidated code that was marked by a todo for merging as it no longer
  77. needed to be separate
  78. -Removed some older code that was no longer necessary
  79. Passes bubble tests.
  80. Rev 1.9 6/3/2003 11:45:58 PM BGooijen
  81. Added .Connected
  82. Rev 1.8 2003.04.22 7:45:34 PM czhower
  83. Rev 1.7 4/2/2003 3:24:56 PM BGooijen
  84. Moved transparantproxy from ..stack to ..socket
  85. Rev 1.6 2/28/2003 9:51:56 PM BGooijen
  86. removed the field: FReadTimeout: Integer, it hided the one in TIdIOHandler
  87. Rev 1.5 2/26/2003 1:15:38 PM BGooijen
  88. FBinding is now freed in IdIOHandlerSocket, instead of in IdIOHandlerStack
  89. Rev 1.4 2003.02.25 1:36:08 AM czhower
  90. Rev 1.3 2002.12.07 12:26:26 AM czhower
  91. Rev 1.2 12-6-2002 20:09:14 BGooijen
  92. Changed SetDestination to search for the last ':', instead of the first
  93. Rev 1.1 12-6-2002 18:54:14 BGooijen
  94. Added IPv6-support
  95. Rev 1.0 11/13/2002 08:45:08 AM JPMugaas
  96. }
  97. unit IdIOHandlerSocket;
  98. interface
  99. {$I IdCompilerDefines.inc}
  100. uses
  101. Classes,
  102. IdCustomTransparentProxy,
  103. IdBaseComponent,
  104. IdGlobal,
  105. IdIOHandler,
  106. IdSocketHandle;
  107. const
  108. IdDefTimeout = 0;
  109. IdBoundPortDefault = 0;
  110. type
  111. {
  112. TIdIOHandlerSocket is the base class for socket IOHandlers that implement a
  113. binding.
  114. Descendants
  115. -TIdIOHandlerStack
  116. -TIdIOHandlerChain
  117. }
  118. TIdIOHandlerSocket = class(TIdIOHandler)
  119. protected
  120. FBinding: TIdSocketHandle;
  121. FBoundIP: string;
  122. FBoundPort: TIdPort;
  123. FBoundPortMax: TIdPort;
  124. FBoundPortMin: TIdPort;
  125. FDefaultPort: TIdPort;
  126. FOnBeforeBind: TNotifyEvent;
  127. FOnAfterBind: TNotifyEvent;
  128. FOnSocketAllocated: TNotifyEvent;
  129. {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FTransparentProxy: TIdCustomTransparentProxy;
  130. FImplicitTransparentProxy: Boolean;
  131. FUseNagle: Boolean;
  132. FReuseSocket: TIdReuseSocket;
  133. FIPVersion: TIdIPVersion;
  134. //
  135. procedure ConnectClient; virtual;
  136. procedure DoBeforeBind; virtual;
  137. procedure DoAfterBind; virtual;
  138. procedure DoSocketAllocated; virtual;
  139. procedure InitComponent; override;
  140. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  141. function GetDestination: string; override;
  142. procedure SetDestination(const AValue: string); override;
  143. function GetReuseSocket: TIdReuseSocket;
  144. procedure SetReuseSocket(AValue: TIdReuseSocket);
  145. function GetTransparentProxy: TIdCustomTransparentProxy; virtual;
  146. procedure SetTransparentProxy(AProxy: TIdCustomTransparentProxy); virtual;
  147. function GetUseNagle: Boolean;
  148. procedure SetUseNagle(AValue: Boolean);
  149. //
  150. function SourceIsAvailable: Boolean; override;
  151. function CheckForError(ALastResult: Integer): Integer; override;
  152. procedure RaiseError(AError: Integer); override;
  153. public
  154. procedure AfterAccept; override;
  155. destructor Destroy; override;
  156. function BindingAllocated: Boolean;
  157. procedure Close; override;
  158. function Connected: Boolean; override;
  159. procedure Open; override;
  160. function WriteFile(const AFile: String; AEnableTransferFile: Boolean = False): Int64; override;
  161. //
  162. property Binding: TIdSocketHandle read FBinding;
  163. property BoundPortMax: TIdPort read FBoundPortMax write FBoundPortMax;
  164. property BoundPortMin: TIdPort read FBoundPortMin write FBoundPortMin;
  165. // events
  166. property OnBeforeBind: TNotifyEvent read FOnBeforeBind write FOnBeforeBind;
  167. property OnAfterBind: TNotifyEvent read FOnAfterBind write FOnAfterBind;
  168. property OnSocketAllocated: TNotifyEvent read FOnSocketAllocated write FOnSocketAllocated;
  169. published
  170. property BoundIP: string read FBoundIP write FBoundIP;
  171. property BoundPort: TIdPort read FBoundPort write FBoundPort default IdBoundPortDefault;
  172. property DefaultPort: TIdPort read FDefaultPort write FDefaultPort;
  173. property IPVersion: TIdIPVersion read FIPVersion write FIPVersion default ID_DEFAULT_IP_VERSION;
  174. property ReuseSocket: TIdReuseSocket read GetReuseSocket write SetReuseSocket default rsOSDependent;
  175. property TransparentProxy: TIdCustomTransparentProxy read GetTransparentProxy write SetTransparentProxy;
  176. property UseNagle: boolean read GetUseNagle write SetUseNagle default True;
  177. end;
  178. implementation
  179. uses
  180. //facilitate inlining only.
  181. {$IFDEF DOTNET}
  182. {$IFDEF USE_INLINE}
  183. System.IO,
  184. {$ENDIF}
  185. {$ENDIF}
  186. {$IFDEF WIN32_OR_WIN64 }
  187. Windows,
  188. {$ENDIF}
  189. SysUtils,
  190. IdStack,
  191. IdStackConsts,
  192. IdSocks;
  193. { TIdIOHandlerSocket }
  194. procedure TIdIOHandlerSocket.AfterAccept;
  195. begin
  196. inherited AfterAccept;
  197. FIPVersion := FBinding.IPVersion;
  198. end;
  199. procedure TIdIOHandlerSocket.Close;
  200. begin
  201. if FBinding <> nil then begin
  202. FBinding.CloseSocket;
  203. end;
  204. inherited Close;
  205. end;
  206. procedure TIdIOHandlerSocket.ConnectClient;
  207. var
  208. LBinding: TIdSocketHandle;
  209. begin
  210. LBinding := Binding;
  211. DoBeforeBind;
  212. // Allocate the socket
  213. LBinding.IPVersion := FIPVersion;
  214. LBinding.AllocateSocket;
  215. DoSocketAllocated;
  216. // Bind the socket
  217. if BoundIP <> '' then begin
  218. LBinding.IP := BoundIP;
  219. end;
  220. LBinding.Port := BoundPort;
  221. LBinding.ClientPortMin := BoundPortMin;
  222. LBinding.ClientPortMax := BoundPortMax;
  223. LBinding.ReuseSocket := FReuseSocket;
  224. // RLebeau 11/15/2014: Using the socket bind() function in a Mac OSX sandbox
  225. // causes the Apple store to reject an app with the following error if it
  226. // uses Indy client(s) and no Indy server(s):
  227. //
  228. // "This app uses one or more entitlements which do not have matching
  229. // functionality within the app. Apps should have only the minimum set of
  230. // entitlements necessary for the app to function properly. Please remove
  231. // all entitlements that are not needed by your app and submit an updated
  232. // binary for review, including the following:
  233. //
  234. // com.apple.security.network.server"
  235. //
  236. // Ideally, TIdSocketHandle.Bind() should not call TryBind() if the IP is
  237. // blank and the Port, ClientPortMin, and ClientPortMax are all 0. However,
  238. // TIdSocketHandle.Bind() is used for both clients and servers, and sometimes
  239. // a server needs to bind to port 0 to get a random ephemeral port, which it
  240. // can then report to clients. So lets do the check here instead, as this
  241. // method is only used for clients...
  242. {$IFDEF DARWIN}
  243. // TODO: remove the DARWIN check and just skip the Bind() on all platforms?
  244. if (LBinding.IP <> '') or (LBinding.Port <> 0) or
  245. ((LBinding.ClientPortMin <> 0) and (LBinding.ClientPortMax <> 0)) then
  246. begin
  247. LBinding.Bind;
  248. end;
  249. {$ELSE}
  250. LBinding.Bind;
  251. {$ENDIF}
  252. // Turn off Nagle if specified
  253. LBinding.UseNagle := FUseNagle;
  254. DoAfterBind;
  255. end;
  256. function TIdIOHandlerSocket.Connected: Boolean;
  257. begin
  258. Result := (BindingAllocated and inherited Connected) or (not InputBufferIsEmpty);
  259. end;
  260. destructor TIdIOHandlerSocket.Destroy;
  261. begin
  262. SetTransparentProxy(nil);
  263. FreeAndNil(FBinding);
  264. inherited Destroy;
  265. end;
  266. procedure TIdIOHandlerSocket.DoBeforeBind;
  267. begin
  268. if Assigned(FOnBeforeBind) then begin
  269. FOnBeforeBind(self);
  270. end;
  271. end;
  272. procedure TIdIOHandlerSocket.DoAfterBind;
  273. begin
  274. if Assigned(FOnAfterBind) then begin
  275. FOnAfterBind(self);
  276. end;
  277. end;
  278. procedure TIdIOHandlerSocket.DoSocketAllocated;
  279. begin
  280. if Assigned(FOnSocketAllocated) then begin
  281. FOnSocketAllocated(self);
  282. end;
  283. end;
  284. function TIdIOHandlerSocket.GetDestination: string;
  285. begin
  286. Result := Host;
  287. if (Port <> DefaultPort) and (Port > 0) then begin
  288. Result := Host + ':' + IntToStr(Port);
  289. end;
  290. end;
  291. procedure TIdIOHandlerSocket.Open;
  292. begin
  293. inherited Open;
  294. if not Assigned(FBinding) then begin
  295. FBinding := TIdSocketHandle.Create(nil);
  296. end else begin
  297. FBinding.Reset(True);
  298. end;
  299. FBinding.ClientPortMin := BoundPortMin;
  300. FBinding.ClientPortMax := BoundPortMax;
  301. //if the IOHandler is used to accept connections then port+host will be empty
  302. if (Host <> '') and (Port > 0) then begin
  303. ConnectClient;
  304. end;
  305. end;
  306. procedure TIdIOHandlerSocket.SetDestination(const AValue: string);
  307. var
  308. LPortStart: integer;
  309. begin
  310. // Bas Gooijen 06-Dec-2002: Changed to search the last ':', instead of the first:
  311. LPortStart := LastDelimiter(':', AValue);
  312. if LPortStart > 0 then begin
  313. Host := Copy(AValue, 1, LPortStart-1);
  314. Port := IndyStrToInt(Trim(Copy(AValue, LPortStart + 1, $FF)), DefaultPort);
  315. end;
  316. end;
  317. function TIdIOHandlerSocket.BindingAllocated: Boolean;
  318. begin
  319. Result := FBinding <> nil;
  320. if Result then begin
  321. Result := FBinding.HandleAllocated;
  322. end;
  323. end;
  324. function TIdIOHandlerSocket.WriteFile(const AFile: String;
  325. AEnableTransferFile: Boolean): Int64;
  326. //TODO: There is a way in linux to dump a file to a socket as well. use it.
  327. {$IFDEF WIN32_OR_WIN64}
  328. var
  329. LOldErrorMode : Integer;
  330. {$ENDIF}
  331. begin
  332. Result := 0;
  333. {$IFDEF WIN32_OR_WIN64}
  334. LOldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
  335. try
  336. {$ENDIF}
  337. if FileExists(AFile) then begin
  338. if Assigned(GServeFileProc) and (not WriteBufferingActive)
  339. {and (Intercept = nil)} and AEnableTransferFile
  340. then begin
  341. Result := GServeFileProc(Binding.Handle, AFile);
  342. Exit;
  343. end
  344. else
  345. begin
  346. Result := inherited WriteFile(AFile, AEnableTransferFile);
  347. end;
  348. end;
  349. {$IFDEF WIN32_OR_WIN64}
  350. finally
  351. SetErrorMode(LOldErrorMode)
  352. end;
  353. {$ENDIF}
  354. end;
  355. function TIdIOHandlerSocket.GetReuseSocket: TIdReuseSocket;
  356. begin
  357. if FBinding <> nil then begin
  358. Result := FBinding.ReuseSocket;
  359. end else begin
  360. Result := FReuseSocket;
  361. end;
  362. end;
  363. procedure TIdIOHandlerSocket.SetReuseSocket(AValue: TIdReuseSocket);
  364. begin
  365. FReuseSocket := AValue;
  366. if FBinding <> nil then begin
  367. FBinding.ReuseSocket := AValue;
  368. end;
  369. end;
  370. procedure TIdIOHandlerSocket.SetTransparentProxy(AProxy : TIdCustomTransparentProxy);
  371. var
  372. LClass: TIdCustomTransparentProxyClass;
  373. // under ARC, convert a weak reference to a strong reference before working with it
  374. LTransparentProxy: TIdCustomTransparentProxy;
  375. begin
  376. LTransparentProxy := FTransparentProxy;
  377. if LTransparentProxy <> AProxy then
  378. begin
  379. // All this is to preserve the compatibility with old version
  380. // In the case when we have SocksInfo as object created in runtime without owner form it is treated as temporary object
  381. // In the case when the ASocks points to an object with owner it is treated as component on form.
  382. // under ARC, all weak references to a freed object get nil'ed automatically
  383. if Assigned(AProxy) then begin
  384. if not Assigned(AProxy.Owner) then begin
  385. if Assigned(LTransparentProxy) and (not FImplicitTransparentProxy) then begin
  386. FTransparentProxy := nil;
  387. {$IFNDEF USE_OBJECT_ARC}
  388. LTransparentProxy.RemoveFreeNotification(Self);
  389. {$ENDIF}
  390. end;
  391. LClass := TIdCustomTransparentProxyClass(AProxy.ClassType);
  392. if Assigned(LTransparentProxy) and (LTransparentProxy.ClassType <> LClass) then begin
  393. FTransparentProxy := nil;
  394. FImplicitTransparentProxy := False;
  395. IdDisposeAndNil(LTransparentProxy);
  396. end;
  397. if not Assigned(LTransparentProxy) then begin
  398. LTransparentProxy := LClass.Create(Self);
  399. FTransparentProxy := LTransparentProxy;
  400. FImplicitTransparentProxy := True;
  401. end;
  402. LTransparentProxy.Assign(AProxy);
  403. end else begin
  404. if Assigned(LTransparentProxy) then begin
  405. if FImplicitTransparentProxy then begin
  406. FTransparentProxy := nil;
  407. FImplicitTransparentProxy := False;
  408. IdDisposeAndNil(LTransparentProxy);
  409. end else begin
  410. {$IFNDEF USE_OBJECT_ARC}
  411. LTransparentProxy.RemoveFreeNotification(Self);
  412. {$ENDIF}
  413. end;
  414. end;
  415. FTransparentProxy := AProxy;
  416. {$IFNDEF USE_OBJECT_ARC}
  417. AProxy.FreeNotification(Self);
  418. {$ENDIF}
  419. end;
  420. end
  421. else if Assigned(LTransparentProxy) then begin
  422. if FImplicitTransparentProxy then begin
  423. FTransparentProxy := nil;
  424. FImplicitTransparentProxy := False;
  425. IdDisposeAndNil(LTransparentProxy);
  426. end else begin
  427. FTransparentProxy := nil;
  428. {$IFNDEF USE_OBJECT_ARC}
  429. LTransparentProxy.RemoveFreeNotification(Self);
  430. {$ENDIF}
  431. end;
  432. end;
  433. end;
  434. end;
  435. function TIdIOHandlerSocket.GetTransparentProxy: TIdCustomTransparentProxy;
  436. var
  437. // under ARC, convert a weak reference to a strong reference before working with it
  438. LTransparentProxy: TIdCustomTransparentProxy;
  439. begin
  440. LTransparentProxy := FTransparentProxy;
  441. // Necessary at design time for Borland SOAP support
  442. if LTransparentProxy = nil then begin
  443. LTransparentProxy := TIdSocksInfo.Create(Self); //default
  444. FTransparentProxy := LTransparentProxy;
  445. FImplicitTransparentProxy := True;
  446. end;
  447. Result := LTransparentProxy;
  448. end;
  449. function TIdIOHandlerSocket.GetUseNagle: Boolean;
  450. begin
  451. if FBinding <> nil then begin
  452. Result := FBinding.UseNagle;
  453. end else begin
  454. Result := FUseNagle;
  455. end;
  456. end;
  457. procedure TIdIOHandlerSocket.SetUseNagle(AValue: Boolean);
  458. begin
  459. FUseNagle := AValue;
  460. if FBinding <> nil then begin
  461. FBinding.UseNagle := AValue;
  462. end;
  463. end;
  464. // under ARC, all weak references to a freed object get nil'ed automatically
  465. // so this is mostly redundant
  466. procedure TIdIOHandlerSocket.Notification(AComponent: TComponent; Operation: TOperation);
  467. begin
  468. if (Operation = opRemove) and (AComponent = FTransparentProxy) then begin
  469. FTransparentProxy := nil;
  470. FImplicitTransparentProxy := False;
  471. end;
  472. inherited Notification(AComponent, Operation);
  473. end;
  474. procedure TIdIOHandlerSocket.InitComponent;
  475. begin
  476. inherited InitComponent;
  477. FUseNagle := True;
  478. FIPVersion := ID_DEFAULT_IP_VERSION;
  479. end;
  480. function TIdIOHandlerSocket.SourceIsAvailable: Boolean;
  481. begin
  482. Result := BindingAllocated;
  483. end;
  484. function TIdIOHandlerSocket.CheckForError(ALastResult: Integer): Integer;
  485. begin
  486. Result := GStack.CheckForSocketError(ALastResult, [Id_WSAESHUTDOWN, Id_WSAECONNABORTED, Id_WSAECONNRESET, Id_WSAETIMEDOUT]);
  487. end;
  488. procedure TIdIOHandlerSocket.RaiseError(AError: Integer);
  489. begin
  490. GStack.RaiseSocketError(AError);
  491. end;
  492. end.