IdIOHandlerSocket.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  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. {$IFNDEF TRANSPARENTPROXY_GETTER_BUG}
  146. function GetTransparentProxy: TIdCustomTransparentProxy; virtual;
  147. {$ENDIF}
  148. procedure SetTransparentProxy(AProxy: TIdCustomTransparentProxy); virtual;
  149. function GetUseNagle: Boolean;
  150. procedure SetUseNagle(AValue: Boolean);
  151. //
  152. function SourceIsAvailable: Boolean; override;
  153. function CheckForError(ALastResult: Integer): Integer; override;
  154. procedure RaiseError(AError: Integer); override;
  155. public
  156. procedure AfterAccept; override;
  157. destructor Destroy; override;
  158. function BindingAllocated: Boolean;
  159. procedure Close; override;
  160. function Connected: Boolean; override;
  161. procedure Open; override;
  162. function WriteFile(const AFile: String; AEnableTransferFile: Boolean = False): Int64; override;
  163. //
  164. property Binding: TIdSocketHandle read FBinding;
  165. property BoundPortMax: TIdPort read FBoundPortMax write FBoundPortMax;
  166. property BoundPortMin: TIdPort read FBoundPortMin write FBoundPortMin;
  167. // events
  168. property OnBeforeBind: TNotifyEvent read FOnBeforeBind write FOnBeforeBind;
  169. property OnAfterBind: TNotifyEvent read FOnAfterBind write FOnAfterBind;
  170. property OnSocketAllocated: TNotifyEvent read FOnSocketAllocated write FOnSocketAllocated;
  171. published
  172. property BoundIP: string read FBoundIP write FBoundIP;
  173. property BoundPort: TIdPort read FBoundPort write FBoundPort default IdBoundPortDefault;
  174. property DefaultPort: TIdPort read FDefaultPort write FDefaultPort;
  175. property IPVersion: TIdIPVersion read FIPVersion write FIPVersion default ID_DEFAULT_IP_VERSION;
  176. property ReuseSocket: TIdReuseSocket read GetReuseSocket write SetReuseSocket default rsOSDependent;
  177. property TransparentProxy: TIdCustomTransparentProxy read {$IFDEF TRANSPARENTPROXY_GETTER_BUG}FTransparentProxy{$ELSE}GetTransparentProxy{$ENDIF} write SetTransparentProxy;
  178. property UseNagle: boolean read GetUseNagle write SetUseNagle default True;
  179. end;
  180. implementation
  181. uses
  182. //facilitate inlining only.
  183. {$IFDEF DOTNET}
  184. {$IFDEF USE_INLINE}
  185. System.IO,
  186. {$ENDIF}
  187. {$ENDIF}
  188. {$IFDEF WIN32_OR_WIN64 }
  189. Windows,
  190. {$ENDIF}
  191. SysUtils,
  192. IdStack,
  193. IdStackConsts,
  194. IdSocks;
  195. { TIdIOHandlerSocket }
  196. procedure TIdIOHandlerSocket.AfterAccept;
  197. begin
  198. inherited AfterAccept;
  199. FIPVersion := FBinding.IPVersion;
  200. end;
  201. procedure TIdIOHandlerSocket.Close;
  202. begin
  203. if FBinding <> nil then begin
  204. FBinding.CloseSocket;
  205. end;
  206. inherited Close;
  207. end;
  208. procedure TIdIOHandlerSocket.ConnectClient;
  209. var
  210. LBinding: TIdSocketHandle;
  211. begin
  212. LBinding := Binding;
  213. DoBeforeBind;
  214. // Allocate the socket
  215. LBinding.IPVersion := FIPVersion;
  216. LBinding.AllocateSocket;
  217. DoSocketAllocated;
  218. // Bind the socket
  219. if BoundIP <> '' then begin
  220. LBinding.IP := BoundIP;
  221. end;
  222. LBinding.Port := BoundPort;
  223. LBinding.ClientPortMin := BoundPortMin;
  224. LBinding.ClientPortMax := BoundPortMax;
  225. LBinding.ReuseSocket := FReuseSocket;
  226. // RLebeau 11/15/2014: Using the socket bind() function in a Mac OSX sandbox
  227. // causes the Apple store to reject an app with the following error if it
  228. // uses Indy client(s) and no Indy server(s):
  229. //
  230. // "This app uses one or more entitlements which do not have matching
  231. // functionality within the app. Apps should have only the minimum set of
  232. // entitlements necessary for the app to function properly. Please remove
  233. // all entitlements that are not needed by your app and submit an updated
  234. // binary for review, including the following:
  235. //
  236. // com.apple.security.network.server"
  237. //
  238. // Ideally, TIdSocketHandle.Bind() should not call TryBind() if the IP is
  239. // blank and the Port, ClientPortMin, and ClientPortMax are all 0. However,
  240. // TIdSocketHandle.Bind() is used for both clients and servers, and sometimes
  241. // a server needs to bind to port 0 to get a random ephemeral port, which it
  242. // can then report to clients. So lets do the check here instead, as this
  243. // method is only used for clients...
  244. {$IFDEF DARWIN}
  245. // TODO: remove the DARWIN check and just skip the Bind() on all platforms?
  246. if (LBinding.IP <> '') or (LBinding.Port <> 0) or
  247. ((LBinding.ClientPortMin <> 0) and (LBinding.ClientPortMax <> 0)) then
  248. begin
  249. LBinding.Bind;
  250. end;
  251. {$ELSE}
  252. LBinding.Bind;
  253. {$ENDIF}
  254. // Turn off Nagle if specified
  255. LBinding.UseNagle := FUseNagle;
  256. DoAfterBind;
  257. end;
  258. function TIdIOHandlerSocket.Connected: Boolean;
  259. begin
  260. Result := (BindingAllocated and inherited Connected) or (not InputBufferIsEmpty);
  261. end;
  262. destructor TIdIOHandlerSocket.Destroy;
  263. begin
  264. SetTransparentProxy(nil);
  265. FreeAndNil(FBinding);
  266. inherited Destroy;
  267. end;
  268. procedure TIdIOHandlerSocket.DoBeforeBind;
  269. begin
  270. if Assigned(FOnBeforeBind) then begin
  271. FOnBeforeBind(self);
  272. end;
  273. end;
  274. procedure TIdIOHandlerSocket.DoAfterBind;
  275. begin
  276. if Assigned(FOnAfterBind) then begin
  277. FOnAfterBind(self);
  278. end;
  279. end;
  280. procedure TIdIOHandlerSocket.DoSocketAllocated;
  281. begin
  282. if Assigned(FOnSocketAllocated) then begin
  283. FOnSocketAllocated(self);
  284. end;
  285. end;
  286. function TIdIOHandlerSocket.GetDestination: string;
  287. begin
  288. Result := Host;
  289. if (Port <> DefaultPort) and (Port > 0) then begin
  290. Result := Host + ':' + IntToStr(Port);
  291. end;
  292. end;
  293. procedure TIdIOHandlerSocket.Open;
  294. begin
  295. inherited Open;
  296. if not Assigned(FBinding) then begin
  297. FBinding := TIdSocketHandle.Create(nil);
  298. end else begin
  299. FBinding.Reset(True);
  300. end;
  301. FBinding.ClientPortMin := BoundPortMin;
  302. FBinding.ClientPortMax := BoundPortMax;
  303. //if the IOHandler is used to accept connections then port+host will be empty
  304. if (Host <> '') and (Port > 0) then begin
  305. ConnectClient;
  306. end;
  307. end;
  308. procedure TIdIOHandlerSocket.SetDestination(const AValue: string);
  309. var
  310. LPortStart: integer;
  311. begin
  312. // Bas Gooijen 06-Dec-2002: Changed to search the last ':', instead of the first:
  313. LPortStart := LastDelimiter(':', AValue);
  314. if LPortStart > 0 then begin
  315. Host := Copy(AValue, 1, LPortStart-1);
  316. Port := IndyStrToInt(Trim(Copy(AValue, LPortStart + 1, $FF)), DefaultPort);
  317. end;
  318. end;
  319. function TIdIOHandlerSocket.BindingAllocated: Boolean;
  320. begin
  321. Result := FBinding <> nil;
  322. if Result then begin
  323. Result := FBinding.HandleAllocated;
  324. end;
  325. end;
  326. function TIdIOHandlerSocket.WriteFile(const AFile: String;
  327. AEnableTransferFile: Boolean): Int64;
  328. //TODO: There is a way in linux to dump a file to a socket as well. use it.
  329. {$IFDEF WIN32_OR_WIN64}
  330. var
  331. LOldErrorMode : Integer;
  332. {$ENDIF}
  333. begin
  334. Result := 0;
  335. {$IFDEF WIN32_OR_WIN64}
  336. LOldErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
  337. try
  338. {$ENDIF}
  339. if FileExists(AFile) then begin
  340. if Assigned(GServeFileProc) and (not WriteBufferingActive)
  341. {and (Intercept = nil)} and AEnableTransferFile
  342. then begin
  343. Result := GServeFileProc(Binding.Handle, AFile);
  344. Exit;
  345. end
  346. else
  347. begin
  348. Result := inherited WriteFile(AFile, AEnableTransferFile);
  349. end;
  350. end;
  351. {$IFDEF WIN32_OR_WIN64}
  352. finally
  353. SetErrorMode(LOldErrorMode)
  354. end;
  355. {$ENDIF}
  356. end;
  357. function TIdIOHandlerSocket.GetReuseSocket: TIdReuseSocket;
  358. begin
  359. if FBinding <> nil then begin
  360. Result := FBinding.ReuseSocket;
  361. end else begin
  362. Result := FReuseSocket;
  363. end;
  364. end;
  365. procedure TIdIOHandlerSocket.SetReuseSocket(AValue: TIdReuseSocket);
  366. begin
  367. FReuseSocket := AValue;
  368. if FBinding <> nil then begin
  369. FBinding.ReuseSocket := AValue;
  370. end;
  371. end;
  372. procedure TIdIOHandlerSocket.SetTransparentProxy(AProxy : TIdCustomTransparentProxy);
  373. var
  374. LClass: TIdCustomTransparentProxyClass;
  375. // under ARC, convert a weak reference to a strong reference before working with it
  376. LTransparentProxy: TIdCustomTransparentProxy;
  377. begin
  378. LTransparentProxy := FTransparentProxy;
  379. if LTransparentProxy <> AProxy then
  380. begin
  381. // All this is to preserve the compatibility with old version
  382. // In the case when we have SocksInfo as object created in runtime without owner form it is treated as temporary object
  383. // In the case when the ASocks points to an object with owner it is treated as component on form.
  384. // under ARC, all weak references to a freed object get nil'ed automatically
  385. if Assigned(AProxy) then begin
  386. if not Assigned(AProxy.Owner) then begin
  387. if Assigned(LTransparentProxy) and (not FImplicitTransparentProxy) then begin
  388. FTransparentProxy := nil;
  389. {$IFNDEF USE_OBJECT_ARC}
  390. LTransparentProxy.RemoveFreeNotification(Self);
  391. {$ENDIF}
  392. end;
  393. LClass := TIdCustomTransparentProxyClass(AProxy.ClassType);
  394. if Assigned(LTransparentProxy) and (LTransparentProxy.ClassType <> LClass) then begin
  395. FTransparentProxy := nil;
  396. FImplicitTransparentProxy := False;
  397. IdDisposeAndNil(LTransparentProxy);
  398. end;
  399. if not Assigned(LTransparentProxy) then begin
  400. LTransparentProxy := LClass.Create(Self);
  401. FTransparentProxy := LTransparentProxy;
  402. FImplicitTransparentProxy := True;
  403. end;
  404. LTransparentProxy.Assign(AProxy);
  405. end else begin
  406. if Assigned(LTransparentProxy) then begin
  407. if FImplicitTransparentProxy then begin
  408. FTransparentProxy := nil;
  409. FImplicitTransparentProxy := False;
  410. IdDisposeAndNil(LTransparentProxy);
  411. end else begin
  412. {$IFNDEF USE_OBJECT_ARC}
  413. LTransparentProxy.RemoveFreeNotification(Self);
  414. {$ENDIF}
  415. end;
  416. end;
  417. FTransparentProxy := AProxy;
  418. {$IFNDEF USE_OBJECT_ARC}
  419. AProxy.FreeNotification(Self);
  420. {$ENDIF}
  421. end;
  422. end
  423. else if Assigned(LTransparentProxy) then begin
  424. if FImplicitTransparentProxy then begin
  425. FTransparentProxy := nil;
  426. FImplicitTransparentProxy := False;
  427. IdDisposeAndNil(LTransparentProxy);
  428. end else begin
  429. FTransparentProxy := nil;
  430. {$IFNDEF USE_OBJECT_ARC}
  431. LTransparentProxy.RemoveFreeNotification(Self);
  432. {$ENDIF}
  433. end;
  434. end;
  435. end;
  436. end;
  437. {$IFNDEF TRANSPARENTPROXY_GETTER_BUG}
  438. function TIdIOHandlerSocket.GetTransparentProxy: TIdCustomTransparentProxy;
  439. var
  440. // under ARC, convert a weak reference to a strong reference before working with it
  441. LTransparentProxy: TIdCustomTransparentProxy;
  442. begin
  443. LTransparentProxy := FTransparentProxy;
  444. // Necessary at design time for Borland SOAP support
  445. // TODO: can we remove this getter and just require the user to link up a
  446. // TransparentProxy component manually at design-time? What problem, if any,
  447. // will this cause for SOAP support?
  448. if LTransparentProxy = nil then begin
  449. LTransparentProxy := TIdSocksInfo.Create(Self); //default
  450. {$IFDEF VCL_6_OR_ABOVE}
  451. if IsDesignTime then begin
  452. LTransparentProxy.SetSubComponent(True);
  453. end;
  454. {$ENDIF}
  455. FTransparentProxy := LTransparentProxy;
  456. FImplicitTransparentProxy := True;
  457. end;
  458. Result := LTransparentProxy;
  459. end;
  460. {$ENDIF}
  461. function TIdIOHandlerSocket.GetUseNagle: Boolean;
  462. begin
  463. if FBinding <> nil then begin
  464. Result := FBinding.UseNagle;
  465. end else begin
  466. Result := FUseNagle;
  467. end;
  468. end;
  469. procedure TIdIOHandlerSocket.SetUseNagle(AValue: Boolean);
  470. begin
  471. FUseNagle := AValue;
  472. if FBinding <> nil then begin
  473. FBinding.UseNagle := AValue;
  474. end;
  475. end;
  476. // under ARC, all weak references to a freed object get nil'ed automatically
  477. // so this is mostly redundant
  478. procedure TIdIOHandlerSocket.Notification(AComponent: TComponent; Operation: TOperation);
  479. begin
  480. if (Operation = opRemove) and (AComponent = FTransparentProxy) then begin
  481. FTransparentProxy := nil;
  482. FImplicitTransparentProxy := False;
  483. end;
  484. inherited Notification(AComponent, Operation);
  485. end;
  486. procedure TIdIOHandlerSocket.InitComponent;
  487. begin
  488. inherited InitComponent;
  489. FUseNagle := True;
  490. FIPVersion := ID_DEFAULT_IP_VERSION;
  491. end;
  492. function TIdIOHandlerSocket.SourceIsAvailable: Boolean;
  493. begin
  494. Result := BindingAllocated;
  495. end;
  496. function TIdIOHandlerSocket.CheckForError(ALastResult: Integer): Integer;
  497. begin
  498. Result := GStack.{$IFDEF OVERLOADED_OPENARRAY_BUG}CheckForSocketErrorArr{$ELSE}CheckForSocketError{$ENDIF}(
  499. ALastResult, [Id_WSAESHUTDOWN, Id_WSAECONNABORTED, Id_WSAECONNRESET, Id_WSAETIMEDOUT]);
  500. end;
  501. procedure TIdIOHandlerSocket.RaiseError(AError: Integer);
  502. begin
  503. GStack.RaiseSocketError(AError);
  504. end;
  505. end.