IdUDPClient.pas 18 KB

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