IdUDPClient.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  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.11 11/12/2004 11:30:20 AM JPMugaas
  18. Expansions for IPv6.
  19. Rev 1.10 11/11/2004 10:25:26 PM JPMugaas
  20. Added OpenProxy and CloseProxy so you can do RecvFrom and SendTo functions
  21. from the UDP client with SOCKS. You must call OpenProxy before using
  22. RecvFrom or SendTo. When you are finished, you must use CloseProxy to close
  23. any connection to the Proxy. Connect and disconnect also call OpenProxy and
  24. CloseProxy.
  25. Rev 1.9 11/10/2004 9:40:42 PM JPMugaas
  26. Timeout error fix. Thanks Bas.
  27. Rev 1.8 11/9/2004 8:18:00 PM JPMugaas
  28. Attempt to add SOCKS support in UDP.
  29. Rev 1.7 11/8/2004 5:03:00 PM JPMugaas
  30. Eliminated Socket property because we probably do not need it after all.
  31. Binding should work just as well. I also made some minor refinements to
  32. Disconnect and Connect.
  33. Rev 1.6 11/7/2004 11:50:36 PM JPMugaas
  34. Fixed a Send method I broke. If FSocket is not assigned, it will call the
  35. inherited SendBuffer method. That should prevent code breakage. The connect
  36. method should be OPTIONAL because UDP may be used for simple one-packet
  37. query/response protocols.
  38. Rev 1.5 11/7/2004 11:33:30 PM JPMugaas
  39. Now uses Connect, Disconnect, Send, and Receive similarly to the TCP Clients.
  40. This should prevent unneeded DNS name to IP address conversions that SendTo
  41. was doing.
  42. Rev 1.4 2004.02.03 4:17:02 PM czhower
  43. For unit name changes.
  44. Rev 1.3 2004.01.21 2:35:40 PM czhower
  45. Removed illegal characters from file.
  46. Rev 1.2 21.1.2004 ã. 12:31:02 DBondzhev
  47. Fix for Indy source. Workaround for dccil bug
  48. now it can be compiled using Compile instead of build
  49. Rev 1.1 10/22/2003 04:41:00 PM JPMugaas
  50. Should compile with some restored functionality. Still not finished.
  51. Rev 1.0 11/13/2002 09:02:16 AM JPMugaas
  52. }
  53. unit IdUDPClient;
  54. interface
  55. {$I IdCompilerDefines.inc}
  56. //Put FPC into Delphi mode
  57. uses
  58. Classes,
  59. IdUDPBase,
  60. IdGlobal,
  61. IdSocketHandle,
  62. IdCustomTransparentProxy;
  63. (*$HPPEMIT '#if defined(_VCL_ALIAS_RECORDS)' *)
  64. (*$HPPEMIT '#if !defined(UNICODE)' *)
  65. (*$HPPEMIT '#pragma alias "@Idudpclient@TIdUDPClient@SetPortA$qqrxus"="@Idudpclient@TIdUDPClient@SetPort$qqrxus"' *)
  66. (*$HPPEMIT '#else' *)
  67. (*$HPPEMIT '#pragma alias "@Idudpclient@TIdUDPClient@SetPortW$qqrxus"="@Idudpclient@TIdUDPClient@SetPort$qqrxus"' *)
  68. (*$HPPEMIT '#endif' *)
  69. (*$HPPEMIT '#endif' *)
  70. type
  71. EIdMustUseOpenProxy = class(EIdUDPException);
  72. TIdUDPClient = class(TIdUDPBase)
  73. protected
  74. FBoundIP: string;
  75. FBoundPort: TIdPort;
  76. FBoundPortMin: TIdPort;
  77. FBoundPortMax: TIdPort;
  78. FProxyOpened : Boolean;
  79. FOnConnected : TNotifyEvent;
  80. FOnDisconnected: TNotifyEvent;
  81. FConnected : Boolean;
  82. {$IF DEFINED(HAS_UNSAFE_OBJECT_REF)}[Unsafe]
  83. {$ELSEIF DEFINED(HAS_WEAK_OBJECT_REF)}[Weak]
  84. {$IFENDIF} FTransparentProxy: TIdCustomTransparentProxy;
  85. function UseProxy : Boolean;
  86. procedure RaiseUseProxyError;
  87. procedure DoOnConnected; virtual;
  88. procedure DoOnDisconnected; virtual;
  89. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  90. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  91. {$ENDIF}
  92. //property methods
  93. procedure SetIPVersion(const AValue: TIdIPVersion); override;
  94. procedure SetHost(const AValue : String); override;
  95. procedure SetPort(const AValue : TIdPort); override;
  96. procedure SetTransparentProxy(AProxy : TIdCustomTransparentProxy);
  97. function GetBinding: TIdSocketHandle; override;
  98. function GetTransparentProxy: TIdCustomTransparentProxy;
  99. public
  100. constructor Create(AOwner: TComponent); override;
  101. destructor Destroy; override;
  102. procedure OpenProxy;
  103. procedure CloseProxy;
  104. procedure Connect; virtual;
  105. procedure Disconnect; virtual;
  106. function Connected: Boolean;
  107. function ReceiveBuffer(var ABuffer : TIdBytes;
  108. const AMSec: Integer = IdTimeoutDefault): Integer; overload; override;
  109. function ReceiveBuffer(var ABuffer : TIdBytes;
  110. var VPeerIP: string; var VPeerPort: TIdPort;
  111. AMSec: Integer = IdTimeoutDefault): integer; overload; override;
  112. function ReceiveBuffer(var ABuffer : TIdBytes;
  113. var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion;
  114. const AMSec: Integer = IdTimeoutDefault): integer; overload; override;
  115. procedure Send(const AData: string; AByteEncoding: IIdTextEncoding = nil); overload;
  116. procedure SendBuffer(const AHost: string; const APort: TIdPort; const ABuffer : TIdBytes); overload; override;
  117. procedure SendBuffer(const ABuffer: TIdBytes); reintroduce; overload;
  118. procedure SendBuffer(const AHost: string; const APort: TIdPort;
  119. const AIPVersion: TIdIPVersion; const ABuffer: TIdBytes);overload; override;
  120. published
  121. property BoundIP: string read FBoundIP write FBoundIP;
  122. property BoundPort: TIdPort read FBoundPort write FBoundPort default DEF_PORT_ANY;
  123. property BoundPortMin: TIdPort read FBoundPortMin write FBoundPortMin default DEF_PORT_ANY;
  124. property BoundPortMax: TIdPort read FBoundPortMax write FBoundPortMax default DEF_PORT_ANY;
  125. property IPVersion default ID_DEFAULT_IP_VERSION;
  126. property Host;
  127. property Port;
  128. property ReceiveTimeout;
  129. property ReuseSocket;
  130. property TransparentProxy: TIdCustomTransparentProxy read GetTransparentProxy write SetTransparentProxy;
  131. property OnConnected: TNotifyEvent read FOnConnected write FOnConnected;
  132. property OnDisconnected: TNotifyEvent read FOnDisconnected write FOnDisconnected;
  133. end;
  134. implementation
  135. uses
  136. IdComponent, IdResourceStringsCore, IdSocks, IdStack, IdStackConsts,
  137. SysUtils;
  138. { TIdUDPClient }
  139. constructor TIdUDPClient.Create(AOwner: TComponent);
  140. begin
  141. inherited Create(AOwner);
  142. FProxyOpened := False;
  143. FConnected := False;
  144. FBoundPort := DEF_PORT_ANY;
  145. FBoundPortMin := DEF_PORT_ANY;
  146. FBoundPortMax := DEF_PORT_ANY;
  147. end;
  148. destructor TIdUDPClient.Destroy;
  149. begin
  150. if UseProxy and FProxyOpened then begin
  151. CloseProxy;
  152. end;
  153. if Connected then begin
  154. Disconnect;
  155. end;
  156. inherited Destroy;
  157. end;
  158. procedure TIdUDPClient.CloseProxy;
  159. begin
  160. if UseProxy and FProxyOpened then begin
  161. FTransparentProxy.CloseUDP(Binding);
  162. FProxyOpened := False;
  163. end;
  164. end;
  165. procedure TIdUDPClient.Connect;
  166. var
  167. LIP : String;
  168. // under ARC, convert a weak reference to a strong reference before working with it
  169. LTransparentProxy: TIdCustomTransparentProxy;
  170. begin
  171. if Connected then begin
  172. Disconnect;
  173. end;
  174. LTransparentProxy := FTransparentProxy;
  175. if Assigned(LTransparentProxy) then begin
  176. if LTransparentProxy.Enabled then begin
  177. //we don't use proxy open because we want to pass a peer's hostname and port
  178. //in case a proxy type in the future requires this.
  179. LTransparentProxy.OpenUDP(Binding, Host, Port);
  180. FProxyOpened := True;
  181. FConnected := True;
  182. Exit; //we're done, the transparentProxy takes care of the work.
  183. end;
  184. end;
  185. if FIPVersion = Id_IPv4 then
  186. begin
  187. if not GStack.IsIP(Host) then begin
  188. if Assigned(OnStatus) then begin
  189. DoStatus(hsResolving, [Host]);
  190. end;
  191. LIP := GStack.ResolveHost(Host, FIPVersion);
  192. end else begin
  193. LIP := Host;
  194. end;
  195. end
  196. else
  197. begin //IPv6
  198. LIP := MakeCanonicalIPv6Address(Host);
  199. if LIP = '' then begin //if MakeCanonicalIPv6Address failed, we have a hostname
  200. if Assigned(OnStatus) then begin
  201. DoStatus(hsResolving, [Host]);
  202. end;
  203. LIP := GStack.ResolveHost(Host, FIPVersion);
  204. end else begin
  205. LIP := Host;
  206. end;
  207. end;
  208. Binding.SetPeer(LIP, Port, FIPVersion);
  209. Binding.Connect;
  210. DoStatus(hsConnected, [Host]);
  211. DoOnConnected;
  212. FConnected := True;
  213. end;
  214. function TIdUDPClient.Connected: Boolean;
  215. begin
  216. Result := FConnected;
  217. if Result then begin
  218. Result := Assigned(FBinding);
  219. if Result then begin
  220. Result := FBinding.HandleAllocated;
  221. end;
  222. end;
  223. end;
  224. procedure TIdUDPClient.Disconnect;
  225. begin
  226. if Connected then begin
  227. DoStatus(hsDisconnecting);
  228. if UseProxy and FProxyOpened then begin
  229. CloseProxy;
  230. end;
  231. FBinding.CloseSocket;
  232. DoOnDisconnected;
  233. DoStatus(hsDisconnected);
  234. FConnected := False;
  235. end;
  236. end;
  237. procedure TIdUDPClient.DoOnConnected;
  238. begin
  239. if Assigned(OnConnected) then begin
  240. OnConnected(Self);
  241. end;
  242. end;
  243. procedure TIdUDPClient.DoOnDisconnected;
  244. begin
  245. if Assigned(OnDisconnected) then begin
  246. OnDisconnected(Self);
  247. end;
  248. end;
  249. function TIdUDPClient.GetBinding: TIdSocketHandle;
  250. begin
  251. if FBinding = nil then begin
  252. FBinding := TIdSocketHandle.Create(nil);
  253. end;
  254. if not FBinding.HandleAllocated then begin
  255. FBinding.IPVersion := FIPVersion;
  256. FBinding.AllocateSocket(Id_SOCK_DGRAM);
  257. FBinding.IP := FBoundIP;
  258. FBinding.Port := FBoundPort;
  259. FBinding.ClientPortMin := FBoundPortMin;
  260. FBinding.ClientPortMax := FBoundPortMax;
  261. FBinding.ReuseSocket := FReuseSocket;
  262. FBinding.Bind;
  263. BroadcastEnabledChanged;
  264. end;
  265. Result := FBinding;
  266. end;
  267. function TIdUDPClient.GetTransparentProxy: TIdCustomTransparentProxy;
  268. var
  269. // under ARC, convert a weak reference to a strong reference before working with it
  270. LTransparentProxy: TIdCustomTransparentProxy;
  271. begin
  272. LTransparentProxy := FTransparentProxy;
  273. // Necessary at design time for Borland SOAP support
  274. if LTransparentProxy = nil then begin
  275. LTransparentProxy := TIdSocksInfo.Create(Self); //default
  276. FTransparentProxy := LTransparentProxy;
  277. end;
  278. Result := LTransparentProxy;
  279. end;
  280. // under ARC, all weak references to a freed object get nil'ed automatically
  281. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  282. procedure TIdUDPClient.Notification(AComponent: TComponent; Operation: TOperation);
  283. begin
  284. if (Operation = opRemove) and (AComponent = FTransparentProxy) then begin
  285. FTransparentProxy := nil;
  286. end;
  287. inherited Notification(AComponent, Operation);
  288. end;
  289. {$ENDIF}
  290. procedure TIdUDPClient.OpenProxy;
  291. begin
  292. if UseProxy and (not FProxyOpened) then begin
  293. FTransparentProxy.OpenUDP(Binding);
  294. FProxyOpened := True;
  295. end;
  296. end;
  297. function TIdUDPClient.ReceiveBuffer(var ABuffer: TIdBytes;
  298. const AMSec: Integer): Integer;
  299. var
  300. LMSec : Integer;
  301. LHost : String;
  302. LPort : TIdPort;
  303. LIPVersion: TIdIPVersion;
  304. begin
  305. Result := 0;
  306. if AMSec = IdTimeoutDefault then begin
  307. if ReceiveTimeout = 0 then begin
  308. LMSec := IdTimeoutInfinite;
  309. end else begin
  310. LMSec := ReceiveTimeout;
  311. end;
  312. end else begin
  313. LMSec := AMSec;
  314. end;
  315. if UseProxy then begin
  316. if not FProxyOpened then begin
  317. RaiseUseProxyError;
  318. end;
  319. Result := FTransparentProxy.RecvFromUDP(Binding, ABuffer, LHost, LPort, LIPVersion, LMSec);
  320. end else
  321. begin
  322. if Connected then begin
  323. if FBinding.Readable(LMSec) then begin //Select(LMSec) then
  324. Result := FBinding.Receive(ABuffer);
  325. end;
  326. end else begin
  327. Result := inherited ReceiveBuffer(ABuffer, LMSec);
  328. end;
  329. end;
  330. end;
  331. procedure TIdUDPClient.RaiseUseProxyError;
  332. begin
  333. raise EIdMustUseOpenProxy.Create(RSUDPMustUseProxyOpen);
  334. end;
  335. function TIdUDPClient.ReceiveBuffer(var ABuffer: TIdBytes;
  336. var VPeerIP: string; var VPeerPort: TIdPort; AMSec: Integer): integer;
  337. var
  338. VoidIPVersion: TidIPVersion;
  339. begin
  340. Result := ReceiveBuffer(ABuffer, VPeerIP, VPeerPort, VoidIPVersion, AMSec);
  341. end;
  342. procedure TIdUDPClient.Send(const AData: string; AByteEncoding: IIdTextEncoding = nil);
  343. begin
  344. Send(Host, Port, AData, AByteEncoding);
  345. end;
  346. procedure TIdUDPClient.SendBuffer(const ABuffer : TIdBytes);
  347. begin
  348. if UseProxy then begin
  349. if not FProxyOpened then begin
  350. RaiseUseProxyError;
  351. end;
  352. FTransparentProxy.SendToUDP(Binding, Host, Port, IPVersion, ABuffer);
  353. end else
  354. begin
  355. if Connected then begin
  356. FBinding.Send(ABuffer, 0, -1);
  357. end else begin
  358. inherited SendBuffer(Host, Port, IPVersion, ABuffer);
  359. end;
  360. end;
  361. end;
  362. procedure TIdUDPClient.SendBuffer(const AHost: string; const APort: TIdPort;
  363. const ABuffer: TIdBytes);
  364. begin
  365. if UseProxy then begin
  366. if not FProxyOpened then begin
  367. RaiseUseProxyError;
  368. end;
  369. FTransparentProxy.SendToUDP(Binding, AHost, APort, IPVersion, ABuffer);
  370. end else begin
  371. inherited SendBuffer(AHost, APort, ABuffer);
  372. end;
  373. end;
  374. procedure TIdUDPClient.SetHost(const AValue: String);
  375. begin
  376. if FHost <> AValue then begin
  377. Disconnect;
  378. end;
  379. inherited SetHost(AValue);
  380. end;
  381. procedure TIdUDPClient.SetIPVersion(const AValue: TIdIPVersion);
  382. begin
  383. if FIPVersion <> AValue then begin
  384. Disconnect;
  385. end;
  386. inherited SetIPVersion(AValue);
  387. end;
  388. procedure TIdUDPClient.SetPort(const AValue: TIdPort);
  389. begin
  390. if FPort <> AValue then begin
  391. Disconnect;
  392. end;
  393. inherited SetPort(AValue);
  394. end;
  395. procedure TIdUDPClient.SetTransparentProxy(AProxy: TIdCustomTransparentProxy);
  396. var
  397. LClass: TIdCustomTransparentProxyClass;
  398. // under ARC, convert a weak reference to a strong reference before working with it
  399. LTransparentProxy: TIdCustomTransparentProxy;
  400. begin
  401. LTransparentProxy := FTransparentProxy;
  402. if LTransparentProxy <> AProxy then
  403. begin
  404. // All this is to preserve the compatibility with old version
  405. // In the case when we have SocksInfo as object created in runtime without owner form it is treated as temporary object
  406. // In the case when the ASocks points to an object with owner it is treated as component on form.
  407. // under ARC, all weak references to a freed object get nil'ed automatically
  408. if Assigned(AProxy) then begin
  409. if not Assigned(AProxy.Owner) then begin
  410. if Assigned(LTransparentProxy) and (LTransparentProxy.Owner <> Self) then begin
  411. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  412. LTransparentProxy.RemoveFreeNotification(Self);
  413. {$ENDIF}
  414. LTransparentProxy := nil;
  415. end;
  416. LClass := TIdCustomTransparentProxyClass(AProxy.ClassType);
  417. if Assigned(LTransparentProxy) and (LTransparentProxy.ClassType <> LClass) then begin
  418. FTransparentProxy := nil;
  419. IdDisposeAndNil(LTransparentProxy);
  420. end;
  421. if not Assigned(LTransparentProxy) then begin
  422. LTransparentProxy := LClass.Create(Self);
  423. FTransparentProxy := LTransparentProxy;
  424. end;
  425. LTransparentProxy.Assign(AProxy);
  426. end else begin
  427. if Assigned(LTransparentProxy) then begin
  428. if LTransparentProxy.Owner = Self then begin
  429. FTransparentProxy := nil;
  430. IdDisposeAndNil(LTransparentProxy);
  431. end
  432. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  433. else begin
  434. LTransparentProxy.RemoveFreeNotification(Self);
  435. end
  436. {$ENDIF}
  437. ;
  438. end;
  439. FTransparentProxy := AProxy;
  440. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  441. AProxy.FreeNotification(Self);
  442. {$ENDIF}
  443. end;
  444. end
  445. else if Assigned(LTransparentProxy) then begin
  446. if LTransparentProxy.Owner = Self then begin
  447. FTransparentProxy := nil;
  448. IdDisposeAndNil(LTransparentProxy);
  449. end else begin
  450. FTransparentProxy := nil; //remove link
  451. {$IFDEF USE_OBJECT_REF_FREENOTIF}
  452. LTransparentProxy.RemoveFreeNotification(Self);
  453. {$ENDIF}
  454. end;
  455. end;
  456. end;
  457. end;
  458. function TIdUDPClient.UseProxy: Boolean;
  459. var
  460. // under ARC, convert a weak reference to a strong reference before working with it
  461. LTransparentProxy: TIdCustomTransparentProxy;
  462. begin
  463. LTransparentProxy := FTransparentProxy;
  464. Result := Assigned(LTransparentProxy);
  465. if Result then begin
  466. Result := LTransparentProxy.Enabled;
  467. end;
  468. end;
  469. function TIdUDPClient.ReceiveBuffer(var ABuffer: TIdBytes;
  470. var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion;
  471. const AMSec: Integer): integer;
  472. var
  473. LMSec : Integer;
  474. begin
  475. if AMSec = IdTimeoutDefault then begin
  476. if ReceiveTimeout = 0 then begin
  477. LMSec := IdTimeoutInfinite;
  478. end else begin
  479. LMSec := ReceiveTimeout;
  480. end;
  481. end else begin
  482. LMSec := AMSec;
  483. end;
  484. if UseProxy then begin
  485. if not FProxyOpened then begin
  486. RaiseUseProxyError;
  487. end;
  488. Result := FTransparentProxy.RecvFromUDP(Binding, ABuffer, VPeerIP, VPeerPort, VIPVersion, LMSec);
  489. end else begin
  490. Result := inherited ReceiveBuffer(ABuffer, VPeerIP, VPeerPort, VIPVersion, LMSec);
  491. end;
  492. end;
  493. procedure TIdUDPClient.SendBuffer(const AHost: string; const APort: TIdPort;
  494. const AIPVersion: TIdIPVersion; const ABuffer: TIdBytes);
  495. begin
  496. if UseProxy then begin
  497. if not FProxyOpened then begin
  498. RaiseUseProxyError;
  499. end;
  500. FTransparentProxy.SendToUDP(Binding, AHost, APort, AIPVersion, ABuffer);
  501. end else begin
  502. inherited SendBuffer(AHost, APort, AIPVersion, ABuffer);
  503. end;
  504. end;
  505. end.