IdUDPClient.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  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. {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FTransparentProxy: TIdCustomTransparentProxy;
  83. FImplicitTransparentProxy: Boolean;
  84. function UseProxy : Boolean;
  85. procedure RaiseUseProxyError;
  86. procedure DoOnConnected; virtual;
  87. procedure DoOnDisconnected; virtual;
  88. procedure InitComponent; override;
  89. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  90. //property methods
  91. procedure SetIPVersion(const AValue: TIdIPVersion); override;
  92. procedure SetHost(const AValue : String); override;
  93. procedure SetPort(const AValue : TIdPort); override;
  94. procedure SetTransparentProxy(AProxy : TIdCustomTransparentProxy);
  95. function GetBinding: TIdSocketHandle; override;
  96. function GetTransparentProxy: TIdCustomTransparentProxy;
  97. public
  98. destructor Destroy; override;
  99. procedure OpenProxy;
  100. procedure CloseProxy;
  101. procedure Connect; virtual;
  102. procedure Disconnect; virtual;
  103. function Connected: Boolean;
  104. function ReceiveBuffer(var ABuffer : TIdBytes;
  105. const AMSec: Integer = IdTimeoutDefault): Integer; overload; override;
  106. function ReceiveBuffer(var ABuffer : TIdBytes;
  107. var VPeerIP: string; var VPeerPort: TIdPort;
  108. AMSec: Integer = IdTimeoutDefault): integer; overload; override;
  109. function ReceiveBuffer(var ABuffer : TIdBytes;
  110. var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion;
  111. const AMSec: Integer = IdTimeoutDefault): integer; overload; override;
  112. procedure Send(const AData: string; AByteEncoding: IIdTextEncoding = nil
  113. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  114. ); overload;
  115. procedure SendBuffer(const AHost: string; const APort: TIdPort; const ABuffer : TIdBytes); overload; override;
  116. procedure SendBuffer(const ABuffer: TIdBytes); reintroduce; overload;
  117. procedure SendBuffer(const AHost: string; const APort: TIdPort;
  118. const AIPVersion: TIdIPVersion; const ABuffer: TIdBytes);overload; override;
  119. published
  120. property BoundIP: string read FBoundIP write FBoundIP;
  121. property BoundPort: TIdPort read FBoundPort write FBoundPort default DEF_PORT_ANY;
  122. property BoundPortMin: TIdPort read FBoundPortMin write FBoundPortMin default DEF_PORT_ANY;
  123. property BoundPortMax: TIdPort read FBoundPortMax write FBoundPortMax default DEF_PORT_ANY;
  124. property IPVersion default ID_DEFAULT_IP_VERSION;
  125. property Host;
  126. property Port;
  127. property ReceiveTimeout;
  128. property ReuseSocket;
  129. property TransparentProxy: TIdCustomTransparentProxy read GetTransparentProxy write SetTransparentProxy;
  130. property OnConnected: TNotifyEvent read FOnConnected write FOnConnected;
  131. property OnDisconnected: TNotifyEvent read FOnDisconnected write FOnDisconnected;
  132. end;
  133. implementation
  134. uses
  135. IdComponent, IdResourceStringsCore, IdSocks, IdStack, IdStackConsts,
  136. SysUtils;
  137. { TIdUDPClient }
  138. procedure TIdUDPClient.CloseProxy;
  139. begin
  140. if UseProxy and FProxyOpened then begin
  141. FTransparentProxy.CloseUDP(Binding);
  142. FProxyOpened := False;
  143. end;
  144. end;
  145. procedure TIdUDPClient.Connect;
  146. var
  147. LIP : String;
  148. // under ARC, convert a weak reference to a strong reference before working with it
  149. LTransparentProxy: TIdCustomTransparentProxy;
  150. begin
  151. if Connected then begin
  152. Disconnect;
  153. end;
  154. LTransparentProxy := FTransparentProxy;
  155. if Assigned(LTransparentProxy) then begin
  156. if LTransparentProxy.Enabled then begin
  157. //we don't use proxy open because we want to pass a peer's hostname and port
  158. //in case a proxy type in the future requires this.
  159. LTransparentProxy.OpenUDP(Binding, Host, Port);
  160. FProxyOpened := True;
  161. FConnected := True;
  162. Exit; //we're done, the transparentProxy takes care of the work.
  163. end;
  164. end;
  165. if FIPVersion = Id_IPv4 then
  166. begin
  167. if not GStack.IsIP(Host) then begin
  168. if Assigned(OnStatus) then begin
  169. DoStatus(hsResolving, [Host]);
  170. end;
  171. LIP := GStack.ResolveHost(Host, FIPVersion);
  172. end else begin
  173. LIP := Host;
  174. end;
  175. end
  176. else
  177. begin //IPv6
  178. LIP := MakeCanonicalIPv6Address(Host);
  179. if LIP = '' then begin //if MakeCanonicalIPv6Address failed, we have a hostname
  180. if Assigned(OnStatus) then begin
  181. DoStatus(hsResolving, [Host]);
  182. end;
  183. LIP := GStack.ResolveHost(Host, FIPVersion);
  184. end else begin
  185. LIP := Host;
  186. end;
  187. end;
  188. Binding.SetPeer(LIP, Port, FIPVersion);
  189. Binding.Connect;
  190. DoStatus(hsConnected, [Host]);
  191. DoOnConnected;
  192. FConnected := True;
  193. end;
  194. function TIdUDPClient.Connected: Boolean;
  195. begin
  196. Result := FConnected;
  197. if Result then begin
  198. Result := Assigned(FBinding);
  199. if Result then begin
  200. Result := FBinding.HandleAllocated;
  201. end;
  202. end;
  203. end;
  204. procedure TIdUDPClient.Disconnect;
  205. begin
  206. if Connected then begin
  207. DoStatus(hsDisconnecting);
  208. if UseProxy and FProxyOpened then begin
  209. CloseProxy;
  210. end;
  211. FBinding.CloseSocket;
  212. DoOnDisconnected;
  213. DoStatus(hsDisconnected);
  214. FConnected := False;
  215. end;
  216. end;
  217. procedure TIdUDPClient.DoOnConnected;
  218. begin
  219. if Assigned(OnConnected) then begin
  220. OnConnected(Self);
  221. end;
  222. end;
  223. procedure TIdUDPClient.DoOnDisconnected;
  224. begin
  225. if Assigned(OnDisconnected) then begin
  226. OnDisconnected(Self);
  227. end;
  228. end;
  229. function TIdUDPClient.GetBinding: TIdSocketHandle;
  230. begin
  231. if FBinding = nil then begin
  232. FBinding := TIdSocketHandle.Create(nil);
  233. end;
  234. if not FBinding.HandleAllocated then begin
  235. FBinding.IPVersion := FIPVersion;
  236. FBinding.AllocateSocket(Id_SOCK_DGRAM);
  237. FBinding.IP := FBoundIP;
  238. FBinding.Port := FBoundPort;
  239. FBinding.ClientPortMin := FBoundPortMin;
  240. FBinding.ClientPortMax := FBoundPortMax;
  241. FBinding.ReuseSocket := FReuseSocket;
  242. FBinding.Bind;
  243. BroadcastEnabledChanged;
  244. end;
  245. Result := FBinding;
  246. end;
  247. function TIdUDPClient.GetTransparentProxy: TIdCustomTransparentProxy;
  248. var
  249. // under ARC, convert a weak reference to a strong reference before working with it
  250. LTransparentProxy: TIdCustomTransparentProxy;
  251. begin
  252. LTransparentProxy := FTransparentProxy;
  253. // Necessary at design time for Borland SOAP support
  254. if LTransparentProxy = nil then begin
  255. LTransparentProxy := TIdSocksInfo.Create(Self); //default
  256. FTransparentProxy := LTransparentProxy;
  257. FImplicitTransparentProxy := True;
  258. end;
  259. Result := LTransparentProxy;
  260. end;
  261. procedure TIdUDPClient.InitComponent;
  262. begin
  263. inherited InitComponent;
  264. FProxyOpened := False;
  265. FConnected := False;
  266. FBoundPort := DEF_PORT_ANY;
  267. FBoundPortMin := DEF_PORT_ANY;
  268. FBoundPortMax := DEF_PORT_ANY;
  269. end;
  270. // under ARC, all weak references to a freed object get nil'ed automatically
  271. // so this is mostly redundant
  272. procedure TIdUDPClient.Notification(AComponent: TComponent; Operation: TOperation);
  273. begin
  274. if (Operation = opRemove) and (AComponent = FTransparentProxy) then begin
  275. FTransparentProxy := nil;
  276. FImplicitTransparentProxy := False;
  277. end;
  278. inherited Notification(AComponent, Operation);
  279. end;
  280. procedure TIdUDPClient.OpenProxy;
  281. begin
  282. if UseProxy and (not FProxyOpened) then begin
  283. FTransparentProxy.OpenUDP(Binding);
  284. FProxyOpened := True;
  285. end;
  286. end;
  287. function TIdUDPClient.ReceiveBuffer(var ABuffer: TIdBytes;
  288. const AMSec: Integer): Integer;
  289. var
  290. LMSec : Integer;
  291. LHost : String;
  292. LPort : TIdPort;
  293. LIPVersion: TIdIPVersion;
  294. begin
  295. Result := 0;
  296. if AMSec = IdTimeoutDefault then begin
  297. if ReceiveTimeout = 0 then begin
  298. LMSec := IdTimeoutInfinite;
  299. end else begin
  300. LMSec := ReceiveTimeout;
  301. end;
  302. end else begin
  303. LMSec := AMSec;
  304. end;
  305. if UseProxy then begin
  306. if not FProxyOpened then begin
  307. RaiseUseProxyError;
  308. end;
  309. Result := FTransparentProxy.RecvFromUDP(Binding, ABuffer, LHost, LPort, LIPVersion, LMSec);
  310. end else
  311. begin
  312. if Connected then begin
  313. if FBinding.Readable(LMSec) then begin //Select(LMSec) then
  314. Result := FBinding.Receive(ABuffer);
  315. end;
  316. end else begin
  317. Result := inherited ReceiveBuffer(ABuffer, LMSec);
  318. end;
  319. end;
  320. end;
  321. procedure TIdUDPClient.RaiseUseProxyError;
  322. begin
  323. raise EIdMustUseOpenProxy.Create(RSUDPMustUseProxyOpen);
  324. end;
  325. function TIdUDPClient.ReceiveBuffer(var ABuffer: TIdBytes;
  326. var VPeerIP: string; var VPeerPort: TIdPort; AMSec: Integer): integer;
  327. var
  328. VoidIPVersion: TidIPVersion;
  329. begin
  330. Result := ReceiveBuffer(ABuffer, VPeerIP, VPeerPort, VoidIPVersion, AMSec);
  331. end;
  332. procedure TIdUDPClient.Send(const AData: string; AByteEncoding: IIdTextEncoding = nil
  333. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  334. );
  335. begin
  336. Send(Host, Port, AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
  337. end;
  338. procedure TIdUDPClient.SendBuffer(const ABuffer : TIdBytes);
  339. begin
  340. if UseProxy then begin
  341. if not FProxyOpened then begin
  342. RaiseUseProxyError;
  343. end;
  344. FTransparentProxy.SendToUDP(Binding, Host, Port, IPVersion, ABuffer);
  345. end else
  346. begin
  347. if Connected then begin
  348. FBinding.Send(ABuffer, 0, -1);
  349. end else begin
  350. inherited SendBuffer(Host, Port, IPVersion, ABuffer);
  351. end;
  352. end;
  353. end;
  354. procedure TIdUDPClient.SendBuffer(const AHost: string; const APort: TIdPort;
  355. const ABuffer: TIdBytes);
  356. begin
  357. if UseProxy then begin
  358. if not FProxyOpened then begin
  359. RaiseUseProxyError;
  360. end;
  361. FTransparentProxy.SendToUDP(Binding, AHost, APort, IPVersion, ABuffer);
  362. end else begin
  363. inherited SendBuffer(AHost, APort, ABuffer);
  364. end;
  365. end;
  366. procedure TIdUDPClient.SetHost(const AValue: String);
  367. begin
  368. if FHost <> AValue then begin
  369. Disconnect;
  370. end;
  371. inherited SetHost(AValue);
  372. end;
  373. procedure TIdUDPClient.SetIPVersion(const AValue: TIdIPVersion);
  374. begin
  375. if FIPVersion <> AValue then begin
  376. Disconnect;
  377. end;
  378. inherited SetIPVersion(AValue);
  379. end;
  380. procedure TIdUDPClient.SetPort(const AValue: TIdPort);
  381. begin
  382. if FPort <> AValue then begin
  383. Disconnect;
  384. end;
  385. inherited SetPort(AValue);
  386. end;
  387. procedure TIdUDPClient.SetTransparentProxy(AProxy: TIdCustomTransparentProxy);
  388. var
  389. LClass: TIdCustomTransparentProxyClass;
  390. // under ARC, convert a weak reference to a strong reference before working with it
  391. LTransparentProxy: TIdCustomTransparentProxy;
  392. begin
  393. LTransparentProxy := FTransparentProxy;
  394. if LTransparentProxy <> AProxy then
  395. begin
  396. // All this is to preserve the compatibility with old version
  397. // In the case when we have SocksInfo as object created in runtime without owner form it is treated as temporary object
  398. // In the case when the ASocks points to an object with owner it is treated as component on form.
  399. // under ARC, all weak references to a freed object get nil'ed automatically
  400. if Assigned(AProxy) then begin
  401. if not Assigned(AProxy.Owner) then begin
  402. if Assigned(LTransparentProxy) and (not FImplicitTransparentProxy) then begin
  403. {$IFNDEF USE_OBJECT_ARC}
  404. LTransparentProxy.RemoveFreeNotification(Self);
  405. {$ENDIF}
  406. LTransparentProxy := nil;
  407. end;
  408. LClass := TIdCustomTransparentProxyClass(AProxy.ClassType);
  409. if Assigned(LTransparentProxy) and (LTransparentProxy.ClassType <> LClass) then begin
  410. FTransparentProxy := nil;
  411. FImplicitTransparentProxy := False;
  412. IdDisposeAndNil(LTransparentProxy);
  413. end;
  414. if not Assigned(LTransparentProxy) then begin
  415. LTransparentProxy := LClass.Create(Self);
  416. FTransparentProxy := LTransparentProxy;
  417. FImplicitTransparentProxy := True;
  418. end;
  419. LTransparentProxy.Assign(AProxy);
  420. end else begin
  421. if Assigned(LTransparentProxy) then begin
  422. if FImplicitTransparentProxy then begin
  423. FTransparentProxy := nil;
  424. FImplicitTransparentProxy := False;
  425. IdDisposeAndNil(LTransparentProxy);
  426. end else begin
  427. {$IFNDEF USE_OBJECT_ARC}
  428. LTransparentProxy.RemoveFreeNotification(Self);
  429. {$ENDIF}
  430. end;
  431. end;
  432. FTransparentProxy := AProxy;
  433. {$IFNDEF USE_OBJECT_ARC}
  434. AProxy.FreeNotification(Self);
  435. {$ENDIF}
  436. end;
  437. end
  438. else if Assigned(LTransparentProxy) then begin
  439. if FImplicitTransparentProxy then begin
  440. FTransparentProxy := nil;
  441. FImplicitTransparentProxy := False;
  442. IdDisposeAndNil(LTransparentProxy);
  443. end else begin
  444. FTransparentProxy := nil; //remove link
  445. {$IFNDEF USE_OBJECT_ARC}
  446. LTransparentProxy.RemoveFreeNotification(Self);
  447. {$ENDIF}
  448. end;
  449. end;
  450. end;
  451. end;
  452. function TIdUDPClient.UseProxy: Boolean;
  453. var
  454. // under ARC, convert a weak reference to a strong reference before working with it
  455. LTransparentProxy: TIdCustomTransparentProxy;
  456. begin
  457. LTransparentProxy := FTransparentProxy;
  458. Result := Assigned(LTransparentProxy);
  459. if Result then begin
  460. Result := LTransparentProxy.Enabled;
  461. end;
  462. end;
  463. destructor TIdUDPClient.Destroy;
  464. begin
  465. if UseProxy and FProxyOpened then begin
  466. CloseProxy;
  467. end;
  468. if Connected then begin
  469. Disconnect;
  470. end;
  471. inherited Destroy;
  472. end;
  473. function TIdUDPClient.ReceiveBuffer(var ABuffer: TIdBytes;
  474. var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion;
  475. const AMSec: Integer): integer;
  476. var
  477. LMSec : Integer;
  478. begin
  479. if AMSec = IdTimeoutDefault then begin
  480. if ReceiveTimeout = 0 then begin
  481. LMSec := IdTimeoutInfinite;
  482. end else begin
  483. LMSec := ReceiveTimeout;
  484. end;
  485. end else begin
  486. LMSec := AMSec;
  487. end;
  488. if UseProxy then begin
  489. if not FProxyOpened then begin
  490. RaiseUseProxyError;
  491. end;
  492. Result := FTransparentProxy.RecvFromUDP(Binding, ABuffer, VPeerIP, VPeerPort, VIPVersion, LMSec);
  493. end else begin
  494. Result := inherited ReceiveBuffer(ABuffer, VPeerIP, VPeerPort, VIPVersion, LMSec);
  495. end;
  496. end;
  497. procedure TIdUDPClient.SendBuffer(const AHost: string; const APort: TIdPort;
  498. const AIPVersion: TIdIPVersion; const ABuffer: TIdBytes);
  499. begin
  500. if UseProxy then begin
  501. if not FProxyOpened then begin
  502. RaiseUseProxyError;
  503. end;
  504. FTransparentProxy.SendToUDP(Binding, AHost, APort, AIPVersion, ABuffer);
  505. end else begin
  506. inherited SendBuffer(AHost, APort, AIPVersion, ABuffer);
  507. end;
  508. end;
  509. end.