IdIOHandlerSocket.pas 17 KB

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