IdUDPClient.pas 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  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. {$I IdCompilerDefines.inc}
  55. //Put FPC into Delphi mode
  56. interface
  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;
  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. begin
  325. raise EIdMustUseOpenProxy.Create(RSUDPMustUseProxyOpen);
  326. end;
  327. function TIdUDPClient.ReceiveBuffer(var ABuffer: TIdBytes;
  328. var VPeerIP: string; var VPeerPort: TIdPort; AMSec: Integer): integer;
  329. var
  330. VoidIPVersion: TidIPVersion;
  331. begin
  332. Result := ReceiveBuffer(ABuffer, VPeerIP, VPeerPort, VoidIPVersion, AMSec);
  333. end;
  334. procedure TIdUDPClient.Send(const AData: string; AByteEncoding: IIdTextEncoding = nil
  335. {$IFDEF STRING_IS_ANSI}; ASrcEncoding: IIdTextEncoding = nil{$ENDIF}
  336. );
  337. begin
  338. Send(Host, Port, AData, AByteEncoding{$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF});
  339. end;
  340. procedure TIdUDPClient.SendBuffer(const ABuffer : TIdBytes);
  341. begin
  342. if UseProxy then begin
  343. if not FProxyOpened then begin
  344. RaiseUseProxyError;
  345. end;
  346. FTransparentProxy.SendToUDP(Binding, Host, Port, IPVersion, ABuffer);
  347. end else
  348. begin
  349. if Connected then begin
  350. FBinding.Send(ABuffer, 0, -1);
  351. end else begin
  352. inherited SendBuffer(Host, Port, IPVersion, ABuffer);
  353. end;
  354. end;
  355. end;
  356. procedure TIdUDPClient.SendBuffer(const AHost: string; const APort: TIdPort;
  357. const ABuffer: TIdBytes);
  358. begin
  359. if UseProxy then begin
  360. if not FProxyOpened then begin
  361. RaiseUseProxyError;
  362. end;
  363. FTransparentProxy.SendToUDP(Binding, AHost, APort, IPVersion, ABuffer);
  364. end else begin
  365. inherited SendBuffer(AHost, APort, ABuffer);
  366. end;
  367. end;
  368. procedure TIdUDPClient.SetHost(const AValue: String);
  369. begin
  370. if FHost <> AValue then begin
  371. Disconnect;
  372. end;
  373. inherited SetHost(AValue);
  374. end;
  375. procedure TIdUDPClient.SetIPVersion(const AValue: TIdIPVersion);
  376. begin
  377. if FIPVersion <> AValue then begin
  378. Disconnect;
  379. end;
  380. inherited SetIPVersion(AValue);
  381. end;
  382. procedure TIdUDPClient.SetPort(const AValue: TIdPort);
  383. begin
  384. if FPort <> AValue then begin
  385. Disconnect;
  386. end;
  387. inherited SetPort(AValue);
  388. end;
  389. procedure TIdUDPClient.SetTransparentProxy(AProxy: TIdCustomTransparentProxy);
  390. var
  391. LClass: TIdCustomTransparentProxyClass;
  392. // under ARC, convert a weak reference to a strong reference before working with it
  393. LTransparentProxy: TIdCustomTransparentProxy;
  394. begin
  395. LTransparentProxy := FTransparentProxy;
  396. if LTransparentProxy <> AProxy then
  397. begin
  398. // All this is to preserve the compatibility with old version
  399. // In the case when we have SocksInfo as object created in runtime without owner form it is treated as temporary object
  400. // In the case when the ASocks points to an object with owner it is treated as component on form.
  401. // under ARC, all weak references to a freed object get nil'ed automatically
  402. if Assigned(AProxy) then begin
  403. if not Assigned(AProxy.Owner) then begin
  404. if Assigned(LTransparentProxy) and (not FImplicitTransparentProxy) then begin
  405. {$IFNDEF USE_OBJECT_ARC}
  406. LTransparentProxy.RemoveFreeNotification(Self);
  407. {$ENDIF}
  408. LTransparentProxy := nil;
  409. end;
  410. LClass := TIdCustomTransparentProxyClass(AProxy.ClassType);
  411. if Assigned(LTransparentProxy) and (LTransparentProxy.ClassType <> LClass) then begin
  412. FTransparentProxy := nil;
  413. FImplicitTransparentProxy := False;
  414. IdDisposeAndNil(LTransparentProxy);
  415. end;
  416. if not Assigned(LTransparentProxy) then begin
  417. LTransparentProxy := LClass.Create(Self);
  418. FTransparentProxy := LTransparentProxy;
  419. FImplicitTransparentProxy := True;
  420. end;
  421. LTransparentProxy.Assign(AProxy);
  422. end else begin
  423. if Assigned(LTransparentProxy) then begin
  424. if FImplicitTransparentProxy then begin
  425. FTransparentProxy := nil;
  426. FImplicitTransparentProxy := False;
  427. IdDisposeAndNil(LTransparentProxy);
  428. end else begin
  429. {$IFNDEF USE_OBJECT_ARC}
  430. LTransparentProxy.RemoveFreeNotification(Self);
  431. {$ENDIF}
  432. end;
  433. end;
  434. FTransparentProxy := AProxy;
  435. {$IFNDEF USE_OBJECT_ARC}
  436. AProxy.FreeNotification(Self);
  437. {$ENDIF}
  438. end;
  439. end
  440. else if Assigned(LTransparentProxy) then begin
  441. if FImplicitTransparentProxy then begin
  442. FTransparentProxy := nil;
  443. FImplicitTransparentProxy := False;
  444. IdDisposeAndNil(LTransparentProxy);
  445. end else begin
  446. FTransparentProxy := nil; //remove link
  447. {$IFNDEF USE_OBJECT_ARC}
  448. LTransparentProxy.RemoveFreeNotification(Self);
  449. {$ENDIF}
  450. end;
  451. end;
  452. end;
  453. end;
  454. function TIdUDPClient.UseProxy: Boolean;
  455. var
  456. // under ARC, convert a weak reference to a strong reference before working with it
  457. LTransparentProxy: TIdCustomTransparentProxy;
  458. begin
  459. LTransparentProxy := FTransparentProxy;
  460. Result := Assigned(LTransparentProxy);
  461. if Result then begin
  462. Result := LTransparentProxy.Enabled;
  463. end;
  464. end;
  465. destructor TIdUDPClient.Destroy;
  466. begin
  467. if UseProxy and FProxyOpened then begin
  468. CloseProxy;
  469. end;
  470. if Connected then begin
  471. Disconnect;
  472. end;
  473. inherited Destroy;
  474. end;
  475. function TIdUDPClient.ReceiveBuffer(var ABuffer: TIdBytes;
  476. var VPeerIP: string; var VPeerPort: TIdPort; var VIPVersion: TIdIPVersion;
  477. const AMSec: Integer): integer;
  478. var
  479. LMSec : Integer;
  480. begin
  481. if AMSec = IdTimeoutDefault then begin
  482. if ReceiveTimeout = 0 then begin
  483. LMSec := IdTimeoutInfinite;
  484. end else begin
  485. LMSec := ReceiveTimeout;
  486. end;
  487. end else begin
  488. LMSec := AMSec;
  489. end;
  490. if UseProxy then begin
  491. if not FProxyOpened then begin
  492. RaiseUseProxyError;
  493. end;
  494. Result := FTransparentProxy.RecvFromUDP(Binding, ABuffer, VPeerIP, VPeerPort, VIPVersion, LMSec);
  495. end else begin
  496. Result := inherited ReceiveBuffer(ABuffer, VPeerIP, VPeerPort, VIPVersion, LMSec);
  497. end;
  498. end;
  499. procedure TIdUDPClient.SendBuffer(const AHost: string; const APort: TIdPort;
  500. const AIPVersion: TIdIPVersion; const ABuffer: TIdBytes);
  501. begin
  502. if UseProxy then begin
  503. if not FProxyOpened then begin
  504. RaiseUseProxyError;
  505. end;
  506. FTransparentProxy.SendToUDP(Binding, AHost, APort, AIPVersion, ABuffer);
  507. end else begin
  508. inherited SendBuffer(AHost, APort, AIPVersion, ABuffer);
  509. end;
  510. end;
  511. end.