IdNetworkCalculator.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  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.4 10/26/2004 10:33:46 PM JPMugaas
  18. Updated refs.
  19. Rev 1.3 2004.02.03 5:44:08 PM czhower
  20. Name changes
  21. Rev 1.2 24/01/2004 19:27:30 CCostelloe
  22. Cleaned up warnings
  23. Rev 1.1 1/21/2004 2:20:26 PM JPMugaas
  24. InitComponent
  25. Rev 1.0 11/13/2002 07:57:46 AM JPMugaas
  26. }
  27. unit IdNetworkCalculator;
  28. interface
  29. {$i IdCompilerDefines.inc}
  30. uses
  31. Classes,
  32. IdGlobal,
  33. IdBaseComponent;
  34. type
  35. TNetworkClass = (
  36. ID_NET_CLASS_A, ID_NET_CLASS_B, ID_NET_CLASS_C, ID_NET_CLASS_D, ID_NET_CLASS_E
  37. );
  38. const
  39. ID_NC_MASK_LENGTH = 32;
  40. ID_NETWORKCLASS = ID_NET_CLASS_A;
  41. type
  42. TIdIPAddressType = (IPLocalHost, IPLocalNetwork, IPReserved, IPInternetHost,
  43. IPPrivateNetwork, IPLoopback, IPMulticast, IPFutureUse, IPGlobalBroadcast);
  44. TIpProperty = class(TPersistent)
  45. protected
  46. FReadOnly: Boolean;
  47. FBitArray: array[0..31] of Boolean;
  48. FValue: array[0..3] of Byte;
  49. FOnChange: TNotifyEvent;
  50. function GetAddressType: TIdIPAddressType;
  51. function GetAsBinaryString: String;
  52. function GetAsDoubleWord: UInt32;
  53. function GetAsString: String;
  54. function GetBit(Index: Byte): Boolean;
  55. function GetByte(Index: Integer): Byte;
  56. procedure SetAsBinaryString(const Value: String);
  57. procedure SetAsDoubleWord(const Value: UInt32);
  58. procedure SetAsString(const Value: String);
  59. procedure SetBit(Index: Byte; const Value: Boolean);
  60. procedure SetByte(Index: Integer; const Value: Byte);
  61. //
  62. property IsReadOnly: Boolean read FReadOnly write FReadOnly default False;
  63. public
  64. constructor Create; virtual;
  65. destructor Destroy; override;
  66. //
  67. procedure SetAll(One, Two, Three, Four: Byte); virtual;
  68. procedure Assign(Source: TPersistent); override;
  69. //
  70. property Bits[Index: Byte]: Boolean read GetBit write SetBit;
  71. property AddressType: TIdIPAddressType read GetAddressType;
  72. property OnChange: TNotifyEvent read FOnChange write FOnChange;
  73. published
  74. property Byte1: Byte index 0 read GetByte write SetByte stored False;
  75. property Byte2: Byte index 1 read GetByte write SetByte stored False;
  76. property Byte3: Byte index 2 read GetByte write SetByte stored False;
  77. property Byte4: Byte index 3 read GetByte write SetByte stored False;
  78. property AsDoubleWord: UInt32 read GetAsDoubleWord write SetAsDoubleWord stored False;
  79. property AsBinaryString: String read GetAsBinaryString write SetAsBinaryString stored False;
  80. property AsString: String read GetAsString write SetAsString;
  81. end;
  82. TIdNetworkCalculator = class(TIdBaseComponent)
  83. protected
  84. FListIP: TStrings;
  85. FNetworkMaskLength: UInt32;
  86. FNetworkMask: TIpProperty;
  87. FNetworkAddress: TIpProperty;
  88. FNetworkClass: TNetworkClass;
  89. FOnChange: TNotifyEvent;
  90. FOnGenIPList: TNotifyEvent;
  91. procedure FillIPList;
  92. function GetNetworkClassAsString: String;
  93. function GetIsAddressRoutable: Boolean;
  94. function GetListIP: TStrings;
  95. procedure SetNetworkAddress(const Value: TIpProperty);
  96. procedure SetNetworkMask(const Value: TIpProperty);
  97. procedure SetNetworkMaskLength(const Value: UInt32);
  98. procedure NetMaskChanged(Sender: TObject);
  99. procedure NetAddressChanged(Sender: TObject);
  100. procedure InitComponent; override;
  101. public
  102. destructor Destroy; override;
  103. function IsAddressInNetwork(const Address: String): Boolean;
  104. function NumIP: UInt32;
  105. function StartIP: String;
  106. function EndIP: String;
  107. //
  108. property ListIP: TStrings read GetListIP;
  109. property NetworkClass: TNetworkClass read FNetworkClass;
  110. property NetworkClassAsString: String read GetNetworkClassAsString;
  111. property IsAddressRoutable: Boolean read GetIsAddressRoutable;
  112. published
  113. property NetworkAddress: TIpProperty read FNetworkAddress write SetNetworkAddress;
  114. property NetworkMask: TIpProperty read FNetworkMask write SetNetworkMask;
  115. property NetworkMaskLength: UInt32 read FNetworkMaskLength write SetNetworkMaskLength
  116. default ID_NC_MASK_LENGTH;
  117. property OnGenIPList: TNotifyEvent read FOnGenIPList write FOnGenIPList;
  118. property OnChange: TNotifyEvent read FOnChange write FOnChange;
  119. end;
  120. implementation
  121. uses
  122. IdException, IdGlobalProtocols, IdResourceStringsProtocols, IdStack, SysUtils;
  123. function MakeLongWordIP(const One, Two, Three, Four: Byte): UInt32;
  124. begin
  125. Result := (UInt32(One) shl 24) or (UInt32(Two) shl 16) or (UInt32(Three) shl 8) or UInt32(Four);
  126. end;
  127. procedure BreakupLongWordIP(const Value: UInt32; var One, Two, Three, Four: Byte);
  128. begin
  129. One := Byte((Value and $FF000000) shr 24);
  130. Two := Byte((Value and $00FF0000) shr 16);
  131. Three := Byte((Value and $0000FF00) shr 8);
  132. Four := Byte(Value and $000000FF);
  133. end;
  134. function StrToIP(const Value: string): UInt32;
  135. var
  136. strBuffers: Array [0..3] of String;
  137. cardBuffers: Array[0..3] of UInt32;
  138. StrWork: String;
  139. I: Integer;
  140. begin
  141. StrWork := Value;
  142. // Separate the strings
  143. strBuffers[0] := Fetch(StrWork, '.', True); {Do not Localize}
  144. strBuffers[1] := Fetch(StrWork, '.', True); {Do not Localize}
  145. strBuffers[2] := Fetch(StrWork, '.', True); {Do not Localize}
  146. strBuffers[3] := StrWork;
  147. try
  148. for I := 0 to 3 do begin
  149. cardBuffers[I] := IndyStrToInt(strBuffers[I]);
  150. end;
  151. except
  152. IndyRaiseOuterException(EIdException.CreateFmt(RSNETCALInvalidIPString, [Value]));
  153. end;
  154. // range check
  155. for I := 0 to 3 do begin
  156. if not (cardBuffers[I] in [0..255]) then begin
  157. raise EIdException.CreateFmt(RSNETCALInvalidIPString, [Value]); // TODO: create a new Exception class for this
  158. end;
  159. end;
  160. Result := MakeLongWordIP(cardBuffers[0], cardBuffers[1], cardBuffers[2], cardBuffers[3]);
  161. end;
  162. { TIdNetworkCalculator }
  163. procedure TIdNetworkCalculator.InitComponent;
  164. begin
  165. inherited InitComponent;
  166. FNetworkMask := TIpProperty.Create;
  167. FNetworkMask.OnChange := NetMaskChanged;
  168. FNetworkAddress := TIpProperty.Create;
  169. FNetworkAddress.OnChange := NetAddressChanged;
  170. FListIP := TStringList.Create;
  171. FNetworkClass := ID_NETWORKCLASS;
  172. NetworkMaskLength := ID_NC_MASK_LENGTH;
  173. end;
  174. destructor TIdNetworkCalculator.Destroy;
  175. begin
  176. FreeAndNil(FNetworkMask);
  177. FreeAndNil(FNetworkAddress);
  178. FreeAndNil(FListIP);
  179. inherited Destroy;
  180. end;
  181. procedure TIdNetworkCalculator.FillIPList;
  182. var
  183. i: UInt32;
  184. BaseIP: UInt32;
  185. LByte1, LByte2, LByte3, LByte4: Byte;
  186. begin
  187. if FListIP.Count = 0 then
  188. begin
  189. // prevent to start a long loop in the IDE (will lock delphi)
  190. if IsDesignTime and (NumIP > 1024) then begin
  191. FListIP.text := IndyFormat(RSNETCALConfirmLongIPList, [NumIP]);
  192. end else
  193. begin
  194. BaseIP := NetworkAddress.AsDoubleWord and NetworkMask.AsDoubleWord;
  195. // Lock the list so we won't be "repainting" the whole time... {Do not Localize}
  196. FListIP.BeginUpdate;
  197. try
  198. FListIP.Capacity := NumIP;
  199. for i := 1 to (NumIP - 1) do
  200. begin
  201. Inc(BaseIP);
  202. BreakupLongWordIP(BaseIP, LByte1, LByte2, LByte3, LByte4);
  203. FListIP.Append(IndyFormat('%d.%d.%d.%d', [LByte1, LByte2, LByte3, LByte4])); {Do not Localize}
  204. end;
  205. finally
  206. FListIP.EndUpdate;
  207. end;
  208. end;
  209. end;
  210. end;
  211. function TIdNetworkCalculator.GetListIP: TStrings;
  212. begin
  213. FillIPList;
  214. Result := FListIP;
  215. end;
  216. function TIdNetworkCalculator.IsAddressInNetwork(const Address: String): Boolean;
  217. begin
  218. Result := (StrToIP(Address) and NetworkMask.AsDoubleWord) = (NetworkAddress.AsDoubleWord and NetworkMask.AsDoubleWord);
  219. end;
  220. procedure TIdNetworkCalculator.NetAddressChanged(Sender: TObject);
  221. var
  222. sBuffer: String;
  223. begin
  224. FListIP.Clear;
  225. sBuffer := NetworkAddress.AsBinaryString;
  226. // RFC 1365
  227. if TextStartsWith(sBuffer, '0') then begin {Do not Localize}
  228. fNetworkClass := ID_NET_CLASS_A;
  229. end
  230. else if TextStartsWith(sBuffer, '10') then begin {Do not Localize}
  231. fNetworkClass := ID_NET_CLASS_B;
  232. end
  233. else if TextStartsWith(sBuffer, '110') then begin {Do not Localize}
  234. fNetworkClass := ID_NET_CLASS_C;
  235. end
  236. // Network class D is reserved for multicast
  237. else if TextStartsWith(sBuffer, '1110') then begin {Do not Localize}
  238. fNetworkClass := ID_NET_CLASS_D;
  239. end
  240. // network class E is reserved and shouldn't be used {Do not Localize}
  241. else {if TextStartsWith(sBuffer, '1111') then} begin {Do not Localize}
  242. fNetworkClass := ID_NET_CLASS_E;
  243. end;
  244. if Assigned(FOnChange) then begin
  245. FOnChange(Self);
  246. end;
  247. end;
  248. procedure TIdNetworkCalculator.NetMaskChanged(Sender: TObject);
  249. var
  250. sBuffer: string;
  251. InitialMaskLength: UInt32;
  252. begin
  253. FListIP.Clear;
  254. InitialMaskLength := FNetworkMaskLength;
  255. // A network mask MUST NOT contains holes.
  256. sBuffer := FNetworkMask.AsBinaryString;
  257. while TextStartsWith(sBuffer, '1') do begin {Do not Localize}
  258. Delete(sBuffer, 1, 1);
  259. end;
  260. if IndyPos('1', sBuffer) > 0 then {Do not Localize}
  261. begin
  262. NetworkMaskLength := InitialMaskLength;
  263. raise EIdException.Create(RSNETCALCInvalidNetworkMask); // TODO: create a new Exception class for this
  264. end;
  265. // set the net mask length
  266. NetworkMaskLength := 32 - Length(sBuffer);
  267. if Assigned(FOnChange) then begin
  268. FOnChange(Self);
  269. end;
  270. end;
  271. procedure TIdNetworkCalculator.SetNetworkAddress(const Value: TIpProperty);
  272. begin
  273. FNetworkAddress.Assign(Value);
  274. end;
  275. procedure TIdNetworkCalculator.SetNetworkMask(const Value: TIpProperty);
  276. begin
  277. FNetworkMask.Assign(Value);
  278. end;
  279. procedure TIdNetworkCalculator.SetNetworkMaskLength(const Value: UInt32);
  280. var
  281. LBuffer, LValue: UInt32;
  282. begin
  283. if Value <= 32 then begin
  284. LValue := Value;
  285. end else begin
  286. LValue := 32;
  287. end;
  288. if FNetworkMaskLength <> LValue then
  289. begin
  290. FNetworkMaskLength := LValue;
  291. if Value > 0 then begin
  292. LBuffer := High(UInt32) shl (32 - LValue);
  293. end else begin
  294. LBuffer := 0;
  295. end;
  296. FNetworkMask.AsDoubleWord := LBuffer;
  297. end;
  298. end;
  299. function TIdNetworkCalculator.GetNetworkClassAsString: String;
  300. const
  301. sClasses: array[TNetworkClass] of String = ('A', 'B', 'C', 'D','E'); {Do not Localize}
  302. begin
  303. Result := sClasses[FNetworkClass];
  304. end;
  305. function TIdNetworkCalculator.GetIsAddressRoutable: Boolean;
  306. begin
  307. // RFC 1918
  308. Result := not (
  309. (FNetworkAddress.Byte1 = 10) or
  310. ((FNetworkAddress.Byte1 = 172) and (FNetworkAddress.Byte2 in [16..31])) or
  311. ((FNetworkAddress.Byte1 = 192) and (FNetworkAddress.Byte2 = 168))
  312. );
  313. end;
  314. function TIdNetworkCalculator.EndIP: String;
  315. var
  316. IP: UInt32;
  317. LByte1, LByte2, LByte3, LByte4: Byte;
  318. begin
  319. IP := (NetworkAddress.AsDoubleWord and NetworkMask.AsDoubleWord) + (NumIP - 1);
  320. BreakupLongWordIP(IP, LByte1, LByte2, LByte3, LByte4);
  321. Result := IndyFormat('%d.%d.%d.%d', [LByte1, LByte2, LByte3, LByte4]); {Do not Localize}
  322. end;
  323. function TIdNetworkCalculator.NumIP: UInt32;
  324. begin
  325. Result := 1 shl (32 - NetworkMaskLength);
  326. end;
  327. function TIdNetworkCalculator.StartIP: String;
  328. var
  329. IP: UInt32;
  330. LByte1, LByte2, LByte3, LByte4: Byte;
  331. begin
  332. IP := NetworkAddress.AsDoubleWord and NetworkMask.AsDoubleWord;
  333. BreakupLongWordIP(IP, LByte1, LByte2, LByte3, LByte4);
  334. Result := IndyFormat('%d.%d.%d.%d', [LByte1, LByte2, LByte3, LByte4]); {Do not Localize}
  335. end;
  336. { TIpProperty }
  337. constructor TIpProperty.Create;
  338. begin
  339. inherited Create;
  340. FValue[0] := $0;
  341. FValue[1] := $0;
  342. FValue[2] := $0;
  343. FValue[3] := $0;
  344. end;
  345. destructor TIpProperty.Destroy;
  346. begin
  347. inherited Destroy;
  348. end;
  349. procedure TIpProperty.Assign(Source: TPersistent);
  350. var
  351. LSource: TIpProperty;
  352. begin
  353. if Source is TIpProperty then
  354. begin
  355. LSource := TIpProperty(Source);
  356. SetAll(LSource.Byte1, LSource.Byte2, LSource.Byte3, LSource.Byte4);
  357. end else begin
  358. inherited Assign(Source);
  359. end;
  360. end;
  361. function TIpProperty.GetBit(Index: Byte): boolean;
  362. begin
  363. Result := FBitArray[index];
  364. end;
  365. procedure TIpProperty.SetAll(One, Two, Three, Four: Byte);
  366. var
  367. i, j: Integer;
  368. begin
  369. if (FValue[0] <> One) or (FValue[1] <> Two) or (FValue[2] <> Three) or (FValue[3] <> Four) then
  370. begin
  371. FValue[0] := One;
  372. FValue[1] := Two;
  373. FValue[2] := Three;
  374. FValue[3] := Four;
  375. // set the binary array
  376. for i := 0 to 3 do begin
  377. for j := 0 to 7 do begin
  378. FBitArray[(8*i)+j] := (FValue[i] and (1 shl (7-j))) <> 0;
  379. end;
  380. end;
  381. if Assigned(FOnChange) then begin
  382. FOnChange(Self);
  383. end;
  384. end;
  385. end;
  386. function TIpProperty.GetAsBinaryString: String;
  387. var
  388. i : Integer;
  389. {$IFDEF STRING_IS_IMMUTABLE}
  390. LSB: TIdStringBuilder;
  391. {$ENDIF}
  392. begin
  393. // get the binary string
  394. {$IFDEF STRING_IS_IMMUTABLE}
  395. LSB := TIdStringBuilder.Create(32);
  396. {$ELSE}
  397. SetLength(Result, 32);
  398. {$ENDIF}
  399. for i := 1 to 32 do
  400. begin
  401. if FBitArray[i-1] then begin
  402. {$IFDEF STRING_IS_IMMUTABLE}
  403. LSB.Append(Char('1')); {Do not Localize}
  404. {$ELSE}
  405. Result[i] := '1'; {Do not Localize}
  406. {$ENDIF}
  407. end else begin
  408. {$IFDEF STRING_IS_IMMUTABLE}
  409. LSB.Append(Char('0')); {Do not Localize}
  410. {$ELSE}
  411. Result[i] := '0'; {Do not Localize}
  412. {$ENDIF}
  413. end;
  414. end;
  415. {$IFDEF STRING_IS_IMMUTABLE}
  416. Result := LSB.ToString;
  417. {$ENDIF}
  418. end;
  419. function TIpProperty.GetAsDoubleWord: UInt32;
  420. begin
  421. Result := MakeLongWordIP(FValue[0], FValue[1], FValue[2], FValue[3]);
  422. end;
  423. function TIpProperty.GetAsString: String;
  424. begin
  425. // Set the string
  426. Result := IndyFormat('%d.%d.%d.%d', [FValue[0], FValue[1], FValue[2], FValue[3]]); {Do not Localize}
  427. end;
  428. procedure TIpProperty.SetAsBinaryString(const Value: String);
  429. var
  430. i: Integer;
  431. NewIP: UInt32;
  432. begin
  433. if IsReadOnly then begin
  434. Exit;
  435. end;
  436. if Length(Value) <> 32 then begin
  437. raise EIdException.Create(RSNETCALCInvalidValueLength); // TODO: create a new Exception class for this
  438. end;
  439. if not TextIsSame(Value, AsBinaryString) then
  440. begin
  441. NewIP := 0;
  442. for i := 1 to 32 do
  443. begin
  444. if Value[i] <> '0' then begin {Do not Localize}
  445. NewIP := NewIP or (1 shl (32 - i));
  446. end;
  447. end;
  448. SetAsDoubleWord(NewIP);
  449. end;
  450. end;
  451. function TIpProperty.GetByte(Index: Integer): Byte;
  452. begin
  453. Result := FValue[Index];
  454. end;
  455. procedure TIpProperty.SetAsDoubleWord(const Value: UInt32);
  456. var
  457. LByte1, LByte2, LByte3, LByte4: Byte;
  458. begin
  459. if not IsReadOnly then
  460. begin
  461. BreakupLongWordIP(Value, LByte1, LByte2, LByte3, LByte4);
  462. SetAll(LByte1, LByte2, LByte3, LByte4);
  463. end;
  464. end;
  465. procedure TIpProperty.SetAsString(const Value: String);
  466. begin
  467. SetAsDoubleWord(StrToIP(Value));
  468. end;
  469. procedure TIpProperty.SetBit(Index: Byte; const Value: Boolean);
  470. var
  471. ByteIndex: Integer;
  472. BitValue: Byte;
  473. begin
  474. if (not IsReadOnly) and (FBitArray[Index] <> Value) then
  475. begin
  476. FBitArray[Index] := Value;
  477. ByteIndex := Index div 8;
  478. BitValue := Byte(1 shl (7-(Index mod 8)));
  479. if Value then begin
  480. FValue[ByteIndex] := FValue[ByteIndex] or BitValue;
  481. end else begin
  482. FValue[ByteIndex] := FValue[ByteIndex] and not BitValue;
  483. end;
  484. if Assigned(OnChange) then begin
  485. OnChange(Self);
  486. end;
  487. end;
  488. end;
  489. procedure TIpProperty.SetByte(Index: Integer; const Value: Byte);
  490. begin
  491. if (not IsReadOnly) and (GetByte(Index) <> Value) then
  492. begin
  493. case Index of
  494. 0: SetAll(Value, Byte2, Byte3, Byte4);
  495. 1: SetAll(Byte1, Value, Byte3, Byte4);
  496. 2: SetAll(Byte1, Byte2, Value, Byte4);
  497. 3: SetAll(Byte1, Byte2, Byte3, Value);
  498. end;
  499. end;
  500. end;
  501. function TIpProperty.GetAddressType: TIdIPAddressType;
  502. // based on http://www.ora.com/reference/dictionary/terms/I/IP_Address.htm
  503. begin
  504. Result := IPInternetHost;
  505. case Byte1 of
  506. {localhost or local network}
  507. 0 : if AsDoubleWord = 0 then begin
  508. Result := IPLocalHost;
  509. end else begin
  510. Result := IPLocalNetwork;
  511. end;
  512. {Private network allocations}
  513. 10 : Result := IPPrivateNetwork;
  514. 172 : if Byte2 = 16 then begin
  515. Result := IPPrivateNetwork;
  516. end;
  517. 192 : if Byte2 = 168 then begin
  518. Result := IPPrivateNetwork;
  519. end
  520. else if (Byte2 = 0) and (Byte3 = 0) then begin
  521. Result := IPReserved;
  522. end;
  523. {loopback}
  524. 127 : Result := IPLoopback;
  525. 255 : if AsDoubleWord = $FFFFFFFF then begin
  526. Result := IPGlobalBroadcast;
  527. end else begin
  528. Result := IPFutureUse;
  529. end;
  530. {Reserved}
  531. 128 : if Byte2 = 0 then begin
  532. Result := IPReserved;
  533. end;
  534. 191 : if (Byte2 = 255) and (Byte3 = 255) then begin
  535. Result := IPReserved;
  536. end;
  537. 223 : if (Byte2 = 255) and (Byte3 = 255) then begin
  538. Result := IPReserved;
  539. end;
  540. {Multicast}
  541. 224..239: Result := IPMulticast;
  542. {Future Use}
  543. 240..254: Result := IPFutureUse;
  544. end;
  545. end;
  546. end.