PageRenderTime 55ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/ProSnooperFx_src/indy10.0.52_source/Core/IdIPAddress.pas

http://github.com/lookias/ProSnooper
Pascal | 560 lines | 421 code | 29 blank | 110 comment | 57 complexity | 44fe281d848ed61d27d077ebee23bc4c MD5 | raw file
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 52330: IdIPAddress.pas
  11. {
  12. { Rev 1.9 28.09.2004 20:54:32 Andreas Hausladen
  13. { Removed unused functions that were moved to IdGlobal
  14. }
  15. {
  16. Rev 1.8 6/11/2004 8:48:20 AM DSiders
  17. Added "Do not Localize" comments.
  18. }
  19. {
  20. Rev 1.7 5/19/2004 10:44:34 PM DSiders
  21. Corrected spelling for TIdIPAddress.MakeAddressObject method.
  22. }
  23. {
  24. { Rev 1.6 14/04/2004 17:35:38 HHariri
  25. { Removed IP6 for BCB temporarily
  26. }
  27. {
  28. { Rev 1.5 2/11/2004 5:10:40 AM JPMugaas
  29. { Moved IPv6 address definition to System package.
  30. }
  31. {
  32. { Rev 1.4 2004.02.03 4:17:18 PM czhower
  33. { For unit name changes.
  34. }
  35. {
  36. { Rev 1.3 2/2/2004 12:22:24 PM JPMugaas
  37. { Now uses IdGlobal IPVersion Type. Added HToNBytes for things that need
  38. { to export into NetworkOrder for structures used in protocols.
  39. }
  40. {
  41. { Rev 1.2 1/3/2004 2:13:56 PM JPMugaas
  42. { Removed some empty function code that wasn't used.
  43. { Added some value comparison functions.
  44. { Added a function in the IPAddress object for comparing the value with another
  45. { IP address. Note that this comparison is useful as an IP address will take
  46. { several forms (especially common with IPv6).
  47. { Added a property for returning the IP address as a string which works for
  48. { both IPv4 and IPv6 addresses.
  49. }
  50. {
  51. { Rev 1.1 1/3/2004 1:03:14 PM JPMugaas
  52. { Removed Lo as it was not needed and is not safe in NET.
  53. }
  54. {
  55. { Rev 1.0 1/1/2004 4:00:18 PM JPMugaas
  56. { An object for handling both IPv4 and IPv6 addresses. This is a proposal with
  57. { some old code for conversions.
  58. }
  59. unit IdIPAddress;
  60. interface
  61. uses
  62. Classes,
  63. IdGlobal;
  64. type
  65. TIdIPAddress = class(TObject)
  66. protected
  67. FIPv4 : Cardinal;
  68. FIPv6 : TIdIPv6Address;
  69. FAddrType : TIdIPVersion;
  70. class function IPv4MakeCardInRange(const AInt : Int64; const A256Power : Integer) : Cardinal;
  71. //general conversion stuff
  72. class function IPv6ToIdIPv6Address(const AIPAddress : String; var VErr : Boolean) : TIdIPv6Address;
  73. class function IPv4ToCardinal(const AIPAddress : String; var VErr : Boolean) : Cardinal;
  74. class function MakeCanonicalIPv6Address(const AAddr: string): string;
  75. class function MakeCanonicalIPv4Address(const AAddr: string): string;
  76. //property as String Get methods
  77. function GetIPv4AsString : String;
  78. function GetIPv6AsString : String;
  79. function GetIPAddress : String;
  80. public
  81. function GetHToNBytes: TIdBytes;
  82. public
  83. constructor Create; virtual;
  84. class function MakeAddressObject(const AIP : String) : TIdIPAddress;
  85. function CompareAddress(const AIP : String; var Err : Boolean) : Integer;
  86. property IPv4 : Cardinal read FIPv4 write FIPv4;
  87. property IPv4AsString : String read GetIPv4AsString;
  88. {$IFNDEF BCB}
  89. property IPv6 : TIdIPv6Address read FIPv6 write FIPv6;
  90. {$ENDIF}
  91. property IPv6AsString : String read GetIPv6AsString;
  92. property AddrType : TIdIPVersion read FAddrType write FAddrType;
  93. property IPAsString : String read GetIPAddress;
  94. property HToNBytes : TIdBytes read GetHToNBytes;
  95. end;
  96. implementation
  97. uses SysUtils, IdStack;
  98. //The power constants are for processing IP addresses
  99. //They are powers of 255.
  100. const POWER_1 = $000000FF;
  101. POWER_2 = $0000FFFF;
  102. POWER_3 = $00FFFFFF;
  103. POWER_4 = $FFFFFFFF;
  104. //IPv4 address conversion
  105. //Much of this is based on http://www.pc-help.org/obscure.htm
  106. function OctalToInt64(const AValue: string): Int64;
  107. //swiped from:
  108. //http://www.swissdelphicenter.ch/torry/showcode.php?id=711
  109. var
  110. i: Integer;
  111. begin
  112. Result := 0;
  113. for i := 1 to Length(AValue) do
  114. begin
  115. Result := Result * 8 + StrToInt(Copy(AValue, i, 1));
  116. end;
  117. end;
  118. function CompareWord(const AWord1, AWord2 : Word) : Integer;
  119. {
  120. AWord1 > AWord2 > 0
  121. AWord1 < AWord2 < 0
  122. AWord1 = AWord2 = 0
  123. }
  124. begin
  125. Result := 0;
  126. if AWord1 > AWord2 then
  127. begin
  128. Result := 1;
  129. end
  130. else
  131. begin
  132. if AWord1 < AWord2 then
  133. begin
  134. Result := -1;
  135. end;
  136. end;
  137. end;
  138. function CompareCardinal(const ACard1, ACard2 : Cardinal) : Integer;
  139. {
  140. ACard1 > ACard2 > 0
  141. ACard1 < ACard2 < 0
  142. ACard1 = ACard2 = 0
  143. }
  144. begin
  145. Result := 0;
  146. if ACard1 > ACard2 then
  147. begin
  148. Result := 1;
  149. end
  150. else
  151. begin
  152. if ACard1 < ACard2 then
  153. begin
  154. Result := -1;
  155. end;
  156. end;
  157. end;
  158. { TIdIPAddress }
  159. function TIdIPAddress.CompareAddress(const AIP: String;
  160. var Err: Boolean): Integer;
  161. var LIP2 : TIdIPAddress;
  162. i : Integer;
  163. {
  164. Note that the IP address in the object is S1.
  165. S1 > S2 > 0
  166. S1 < S2 < 0
  167. S1 = S2 = 0
  168. }
  169. begin
  170. Result := 0;
  171. //LIP2 may be nil if the IP address is invalid
  172. LIP2 := MakeAddressObject(AIP);
  173. Err := not Assigned(LIP2);
  174. if not Err then
  175. begin
  176. try
  177. //we can't compare an IPv4 address with an IPv6 address
  178. Err := FAddrType <> LIP2.FAddrType;
  179. if not Err then
  180. begin
  181. if FAddrType = Id_IPv4 then
  182. begin
  183. Result := CompareCardinal(FIPv4,LIP2.FIPv4);
  184. end
  185. else
  186. begin
  187. for i := 0 to 7 do
  188. begin
  189. Result := CompareWord(FIPv6[i],LIP2.FIPv6[i]);
  190. if Result <> 0 then
  191. begin
  192. Break;
  193. end;
  194. end;
  195. end;
  196. end;
  197. finally
  198. FreeAndNil(LIP2);
  199. end;
  200. end;
  201. end;
  202. constructor TIdIPAddress.Create;
  203. begin
  204. inherited Create;
  205. FAddrType := Id_IPv4;
  206. FIPv4 := 0; //'0.0.0.0'
  207. end;
  208. function TIdIPAddress.GetHToNBytes: TIdBytes;
  209. var
  210. i : Integer;
  211. begin
  212. SetLength(Result,0);
  213. case Self.FAddrType of
  214. Id_IPv4 :
  215. begin
  216. Result := ToBytes( GStack.HostToNetwork( FIPv4));
  217. end;
  218. Id_IPv6 :
  219. begin
  220. for i := 0 to 7 do begin
  221. AppendBytes(Result, ToBytes(GStack.HostToNetwork(FIPv6[i]) ) );
  222. end;
  223. end;
  224. end;
  225. end;
  226. function TIdIPAddress.GetIPAddress: String;
  227. begin
  228. if FAddrType = Id_IPv4 then
  229. begin
  230. Result := GetIPv4AsString;
  231. end
  232. else
  233. begin
  234. Result := GetIPv6AsString;
  235. end;
  236. end;
  237. function TIdIPAddress.GetIPv4AsString: String;
  238. begin
  239. Result := '';
  240. if FAddrType = Id_IPv4 then
  241. begin
  242. Result := IntToStr((FIPv4 shr 24) and $FF)+'.';
  243. Result := Result + IntToStr((FIPv4 shr 16) and $FF)+'.';
  244. Result := Result + IntToStr((FIPv4 shr 8) and $FF)+'.';
  245. Result := Result + IntToStr(FIPv4 and $FF);
  246. end;
  247. end;
  248. function TIdIPAddress.GetIPv6AsString: String;
  249. var i:integer;
  250. begin
  251. Result := '';
  252. if FAddrType = Id_IPv6 then
  253. begin
  254. Result := IntToHex(FIPv6[0], 4);
  255. for i := 1 to 7 do begin
  256. Result := Result + ':' + IntToHex(FIPv6[i], 4);
  257. end;
  258. end;
  259. end;
  260. class function TIdIPAddress.IPv4MakeCardInRange(const AInt: Int64;
  261. const A256Power: Integer): Cardinal;
  262. begin
  263. case A256Power of
  264. 4 : Result := (AInt and POWER_4);
  265. 3 : Result := (AInt and POWER_3);
  266. 2 : Result := (AInt and POWER_2);
  267. else
  268. Result := (AInt and POWER_1);
  269. end;
  270. end;
  271. class function TIdIPAddress.IPv4ToCardinal(const AIPAddress: String;
  272. var VErr: Boolean): Cardinal;
  273. var
  274. LBuf, LBuf2 : String;
  275. L256Power : Integer;
  276. LParts : Integer; //how many parts should we process at a time
  277. begin
  278. // S.G. 11/8/2003: Added overflow checking disabling and change multiplys by SHLs.
  279. // Locally disable overflow checking so we can safely use SHL and SHR
  280. {$ifopt Q+} // detect previous setting
  281. {$define _QPlusWasEnabled}
  282. {$Q-}
  283. {$endif}
  284. VErr := True;
  285. L256Power := 4;
  286. LBuf2 := AIPAddress;
  287. Result := 0;
  288. repeat
  289. LBuf := Fetch(LBuf2,'.');
  290. if LBuf = '' then
  291. begin
  292. break;
  293. end;
  294. //We do things this way because we have to treat
  295. //IP address parts differently than a whole number
  296. //and sometimes, there can be missing periods.
  297. if (LBuf2='') and (L256Power > 1) then
  298. begin
  299. LParts := L256Power;
  300. Result := Result shl (L256Power SHL 3);
  301. end
  302. else
  303. begin
  304. LParts := 1;
  305. result := result SHL 8;
  306. end;
  307. if (Copy(LBuf,1,2)=HEXPREFIX) then
  308. begin
  309. //this is a hexideciaml number
  310. if IsHexidecimal(Copy(LBuf,3,MaxInt))=False then
  311. begin
  312. Exit;
  313. end
  314. else
  315. begin
  316. Result := Result + IPv4MakeCardInRange(StrToInt64Def(LBuf,0), LParts);
  317. end;
  318. end
  319. else
  320. begin
  321. if IsNumeric(LBuf) then
  322. begin
  323. if (LBuf[1]='0') and IsOctal(LBuf) then
  324. begin
  325. //this is octal
  326. Result := Result + IPv4MakeCardInRange(OctalToInt64(LBuf),LParts);
  327. end
  328. else
  329. begin
  330. //this must be a decimal
  331. Result := Result + IPv4MakeCardInRange(StrToInt64Def(LBuf,0), LParts);
  332. end;
  333. end
  334. else
  335. begin
  336. //There was an error meaning an invalid IP address
  337. Exit;
  338. end;
  339. end;
  340. Dec(L256Power);
  341. until False;
  342. VErr := False;
  343. // Restore overflow checking
  344. {$ifdef _QPlusWasEnabled} // detect previous setting
  345. {$undef _QPlusWasEnabled}
  346. {$Q-}
  347. {$endif}
  348. end;
  349. class function TIdIPAddress.IPv6ToIdIPv6Address(const AIPAddress: String;
  350. var VErr: Boolean): TIdIPv6Address;
  351. var
  352. LAddress:string;
  353. i:integer;
  354. begin
  355. LAddress := MakeCanonicalIPv6Address(AIPAddress);
  356. VErr := (LAddress='');
  357. if not VErr then begin
  358. for i := 0 to 7 do begin
  359. Result[i]:=StrToInt('$'+fetch(LAddress,':'));
  360. end;
  361. end;
  362. end;
  363. class function TIdIPAddress.MakeAddressObject(
  364. const AIP: String): TIdIPAddress;
  365. var LErr : Boolean;
  366. begin
  367. Result := TIdIPAddress.Create;
  368. Result.FIPv6 := Result.IPv6ToIdIPv6Address(AIP,LErr);
  369. if LErr then
  370. begin
  371. Result.FIPv4 := Result.IPv4ToCardinal(AIP,LErr);
  372. if LErr then
  373. begin
  374. //this is not a valid IPv4 address
  375. FreeAndNil(Result);
  376. end
  377. else
  378. begin
  379. Result.FAddrType := Id_IPv4;
  380. end;
  381. end
  382. else
  383. begin
  384. Result.FAddrType := Id_IPv6;
  385. end;
  386. end;
  387. class function TIdIPAddress.MakeCanonicalIPv4Address(
  388. const AAddr: string): string;
  389. var LErr : Boolean;
  390. LIP : Cardinal;
  391. begin
  392. LIP := IPv4ToDWord(AAddr,LErr);
  393. if LErr then
  394. begin
  395. Result := '';
  396. end
  397. else
  398. begin
  399. Result := MakeDWordIntoIPv4Address(LIP);
  400. end;
  401. end;
  402. class function TIdIPAddress.MakeCanonicalIPv6Address(
  403. const AAddr: string): string;
  404. // return an empty string if the address is invalid,
  405. // for easy checking if its an address or not.
  406. var
  407. p, i: integer;
  408. dots, colons: integer;
  409. colonpos: array[1..8] of integer;
  410. dotpos: array[1..3] of integer;
  411. LAddr: string;
  412. num: integer;
  413. haddoublecolon: boolean;
  414. fillzeros: integer;
  415. begin
  416. Result := ''; // error
  417. LAddr := AAddr;
  418. if Length(LAddr) = 0 then exit;
  419. if LAddr[1] = ':' then begin
  420. LAddr := '0'+LAddr;
  421. end;
  422. if LAddr[Length(LAddr)] = ':' then begin
  423. LAddr := LAddr + '0';
  424. end;
  425. dots := 0;
  426. colons := 0;
  427. for p := 1 to Length(LAddr) do begin
  428. case LAddr[p] of
  429. '.' : begin
  430. inc(dots);
  431. if dots < 4 then begin
  432. dotpos[dots] := p;
  433. end else begin
  434. exit; // error in address
  435. end;
  436. end;
  437. ':' : begin
  438. inc(colons);
  439. if colons < 8 then begin
  440. colonpos[colons] := p;
  441. end else begin
  442. exit; // error in address
  443. end;
  444. end;
  445. 'a'..'f',
  446. 'A'..'F': if dots>0 then exit;
  447. // allow only decimal stuff within dotted portion, ignore otherwise
  448. '0'..'9': ; // do nothing
  449. else exit; // error in address
  450. end; // case
  451. end; // for
  452. if not (dots in [0,3]) then begin
  453. exit; // you have to write 0 or 3 dots...
  454. end;
  455. if dots = 3 then begin
  456. if not (colons in [2..6]) then begin
  457. exit; // must not have 7 colons if we have dots
  458. end;
  459. if colonpos[colons] > dotpos[1] then begin
  460. exit; // x:x:x.x:x:x is not valid
  461. end;
  462. end else begin
  463. if not (colons in [2..7]) then begin
  464. exit; // must at least have two colons
  465. end;
  466. end;
  467. // now start :-)
  468. num := StrToIntDef('$'+Copy(LAddr, 1, colonpos[1]-1), -1);
  469. if (num<0) or (num>65535) then begin
  470. exit; // huh? odd number...
  471. end;
  472. Result := IntToHex(num,1)+':';
  473. haddoublecolon := false;
  474. for p := 2 to colons do begin
  475. if colonpos[p-1] = colonpos[p]-1 then begin
  476. if haddoublecolon then begin
  477. Result := '';
  478. exit; // only a single double-dot allowed!
  479. end;
  480. haddoublecolon := true;
  481. fillzeros := 8 - colons;
  482. if dots>0 then dec(fillzeros,2);
  483. for i := 1 to fillzeros do begin
  484. Result := Result + '0:'; {do not localize}
  485. end;
  486. end else begin
  487. num := StrToIntDef('$'+Copy(LAddr, colonpos[p-1]+1, colonpos[p]-colonpos[p-1]-1), -1);
  488. if (num<0) or (num>65535) then begin
  489. Result := '';
  490. exit; // huh? odd number...
  491. end;
  492. Result := Result + IntToHex(num,1)+':';
  493. end;
  494. end; // end of colon separated part
  495. if dots = 0 then begin
  496. num := StrToIntDef('$'+Copy(LAddr, colonpos[colons]+1, MaxInt), -1);
  497. if (num<0) or (num>65535) then begin
  498. Result := '';
  499. exit; // huh? odd number...
  500. end;
  501. Result := Result + IntToHex(num,1)+':';
  502. end;
  503. if dots > 0 then begin
  504. num := StrToIntDef(Copy(LAddr, colonpos[colons]+1, dotpos[1]-colonpos[colons]-1),-1);
  505. if (num < 0) or (num>255) then begin
  506. Result := '';
  507. exit;
  508. end;
  509. Result := Result + IntToHex(num, 2);
  510. num := StrToIntDef(Copy(LAddr, dotpos[1]+1, dotpos[2]-dotpos[1]-1),-1);
  511. if (num < 0) or (num>255) then begin
  512. Result := '';
  513. exit;
  514. end;
  515. Result := Result + IntToHex(num, 2)+':';
  516. num := StrToIntDef(Copy(LAddr, dotpos[2]+1, dotpos[3]-dotpos[2]-1),-1);
  517. if (num < 0) or (num>255) then begin
  518. Result := '';
  519. exit;
  520. end;
  521. Result := Result + IntToHex(num, 2);
  522. num := StrToIntDef(Copy(LAddr, dotpos[3]+1, 3), -1);
  523. if (num < 0) or (num>255) then begin
  524. Result := '';
  525. exit;
  526. end;
  527. Result := Result + IntToHex(num, 2)+':';
  528. end;
  529. SetLength(Result, Length(Result)-1);
  530. end;
  531. end.