/ProSnooperFx_src/indy10.0.52_source/Core/IdIPAddress.pas
Pascal | 560 lines | 421 code | 29 blank | 110 comment | 57 complexity | 44fe281d848ed61d27d077ebee23bc4c MD5 | raw file
- { $HDR$}
- {**********************************************************************}
- { Unit archived using Team Coherence }
- { Team Coherence is Copyright 2002 by Quality Software Components }
- { }
- { For further information / comments, visit our WEB site at }
- { http://www.TeamCoherence.com }
- {**********************************************************************}
- {}
- { $Log: 52330: IdIPAddress.pas
- {
- { Rev 1.9 28.09.2004 20:54:32 Andreas Hausladen
- { Removed unused functions that were moved to IdGlobal
- }
- {
- Rev 1.8 6/11/2004 8:48:20 AM DSiders
- Added "Do not Localize" comments.
- }
- {
- Rev 1.7 5/19/2004 10:44:34 PM DSiders
- Corrected spelling for TIdIPAddress.MakeAddressObject method.
- }
- {
- { Rev 1.6 14/04/2004 17:35:38 HHariri
- { Removed IP6 for BCB temporarily
- }
- {
- { Rev 1.5 2/11/2004 5:10:40 AM JPMugaas
- { Moved IPv6 address definition to System package.
- }
- {
- { Rev 1.4 2004.02.03 4:17:18 PM czhower
- { For unit name changes.
- }
- {
- { Rev 1.3 2/2/2004 12:22:24 PM JPMugaas
- { Now uses IdGlobal IPVersion Type. Added HToNBytes for things that need
- { to export into NetworkOrder for structures used in protocols.
- }
- {
- { Rev 1.2 1/3/2004 2:13:56 PM JPMugaas
- { Removed some empty function code that wasn't used.
- { Added some value comparison functions.
- { Added a function in the IPAddress object for comparing the value with another
- { IP address. Note that this comparison is useful as an IP address will take
- { several forms (especially common with IPv6).
- { Added a property for returning the IP address as a string which works for
- { both IPv4 and IPv6 addresses.
- }
- {
- { Rev 1.1 1/3/2004 1:03:14 PM JPMugaas
- { Removed Lo as it was not needed and is not safe in NET.
- }
- {
- { Rev 1.0 1/1/2004 4:00:18 PM JPMugaas
- { An object for handling both IPv4 and IPv6 addresses. This is a proposal with
- { some old code for conversions.
- }
- unit IdIPAddress;
- interface
- uses
- Classes,
- IdGlobal;
- type
- TIdIPAddress = class(TObject)
- protected
- FIPv4 : Cardinal;
- FIPv6 : TIdIPv6Address;
- FAddrType : TIdIPVersion;
- class function IPv4MakeCardInRange(const AInt : Int64; const A256Power : Integer) : Cardinal;
- //general conversion stuff
- class function IPv6ToIdIPv6Address(const AIPAddress : String; var VErr : Boolean) : TIdIPv6Address;
- class function IPv4ToCardinal(const AIPAddress : String; var VErr : Boolean) : Cardinal;
- class function MakeCanonicalIPv6Address(const AAddr: string): string;
- class function MakeCanonicalIPv4Address(const AAddr: string): string;
- //property as String Get methods
- function GetIPv4AsString : String;
- function GetIPv6AsString : String;
- function GetIPAddress : String;
- public
- function GetHToNBytes: TIdBytes;
- public
- constructor Create; virtual;
- class function MakeAddressObject(const AIP : String) : TIdIPAddress;
- function CompareAddress(const AIP : String; var Err : Boolean) : Integer;
- property IPv4 : Cardinal read FIPv4 write FIPv4;
- property IPv4AsString : String read GetIPv4AsString;
- {$IFNDEF BCB}
- property IPv6 : TIdIPv6Address read FIPv6 write FIPv6;
- {$ENDIF}
- property IPv6AsString : String read GetIPv6AsString;
- property AddrType : TIdIPVersion read FAddrType write FAddrType;
- property IPAsString : String read GetIPAddress;
- property HToNBytes : TIdBytes read GetHToNBytes;
- end;
- implementation
- uses SysUtils, IdStack;
- //The power constants are for processing IP addresses
- //They are powers of 255.
- const POWER_1 = $000000FF;
- POWER_2 = $0000FFFF;
- POWER_3 = $00FFFFFF;
- POWER_4 = $FFFFFFFF;
- //IPv4 address conversion
- //Much of this is based on http://www.pc-help.org/obscure.htm
- function OctalToInt64(const AValue: string): Int64;
- //swiped from:
- //http://www.swissdelphicenter.ch/torry/showcode.php?id=711
- var
- i: Integer;
- begin
- Result := 0;
- for i := 1 to Length(AValue) do
- begin
- Result := Result * 8 + StrToInt(Copy(AValue, i, 1));
- end;
- end;
- function CompareWord(const AWord1, AWord2 : Word) : Integer;
- {
- AWord1 > AWord2 > 0
- AWord1 < AWord2 < 0
- AWord1 = AWord2 = 0
- }
- begin
- Result := 0;
- if AWord1 > AWord2 then
- begin
- Result := 1;
- end
- else
- begin
- if AWord1 < AWord2 then
- begin
- Result := -1;
- end;
- end;
- end;
- function CompareCardinal(const ACard1, ACard2 : Cardinal) : Integer;
- {
- ACard1 > ACard2 > 0
- ACard1 < ACard2 < 0
- ACard1 = ACard2 = 0
- }
- begin
- Result := 0;
- if ACard1 > ACard2 then
- begin
- Result := 1;
- end
- else
- begin
- if ACard1 < ACard2 then
- begin
- Result := -1;
- end;
- end;
- end;
- { TIdIPAddress }
- function TIdIPAddress.CompareAddress(const AIP: String;
- var Err: Boolean): Integer;
- var LIP2 : TIdIPAddress;
- i : Integer;
- {
- Note that the IP address in the object is S1.
- S1 > S2 > 0
- S1 < S2 < 0
- S1 = S2 = 0
- }
- begin
- Result := 0;
- //LIP2 may be nil if the IP address is invalid
- LIP2 := MakeAddressObject(AIP);
- Err := not Assigned(LIP2);
- if not Err then
- begin
- try
- //we can't compare an IPv4 address with an IPv6 address
- Err := FAddrType <> LIP2.FAddrType;
- if not Err then
- begin
- if FAddrType = Id_IPv4 then
- begin
- Result := CompareCardinal(FIPv4,LIP2.FIPv4);
- end
- else
- begin
- for i := 0 to 7 do
- begin
- Result := CompareWord(FIPv6[i],LIP2.FIPv6[i]);
- if Result <> 0 then
- begin
- Break;
- end;
- end;
- end;
- end;
- finally
- FreeAndNil(LIP2);
- end;
- end;
- end;
- constructor TIdIPAddress.Create;
- begin
- inherited Create;
- FAddrType := Id_IPv4;
- FIPv4 := 0; //'0.0.0.0'
- end;
- function TIdIPAddress.GetHToNBytes: TIdBytes;
- var
- i : Integer;
- begin
- SetLength(Result,0);
- case Self.FAddrType of
- Id_IPv4 :
- begin
- Result := ToBytes( GStack.HostToNetwork( FIPv4));
- end;
- Id_IPv6 :
- begin
- for i := 0 to 7 do begin
- AppendBytes(Result, ToBytes(GStack.HostToNetwork(FIPv6[i]) ) );
- end;
- end;
- end;
- end;
- function TIdIPAddress.GetIPAddress: String;
- begin
- if FAddrType = Id_IPv4 then
- begin
- Result := GetIPv4AsString;
- end
- else
- begin
- Result := GetIPv6AsString;
- end;
- end;
- function TIdIPAddress.GetIPv4AsString: String;
- begin
- Result := '';
- if FAddrType = Id_IPv4 then
- begin
- Result := IntToStr((FIPv4 shr 24) and $FF)+'.';
- Result := Result + IntToStr((FIPv4 shr 16) and $FF)+'.';
- Result := Result + IntToStr((FIPv4 shr 8) and $FF)+'.';
- Result := Result + IntToStr(FIPv4 and $FF);
- end;
- end;
- function TIdIPAddress.GetIPv6AsString: String;
- var i:integer;
- begin
- Result := '';
- if FAddrType = Id_IPv6 then
- begin
- Result := IntToHex(FIPv6[0], 4);
- for i := 1 to 7 do begin
- Result := Result + ':' + IntToHex(FIPv6[i], 4);
- end;
- end;
- end;
- class function TIdIPAddress.IPv4MakeCardInRange(const AInt: Int64;
- const A256Power: Integer): Cardinal;
- begin
- case A256Power of
- 4 : Result := (AInt and POWER_4);
- 3 : Result := (AInt and POWER_3);
- 2 : Result := (AInt and POWER_2);
- else
- Result := (AInt and POWER_1);
- end;
- end;
- class function TIdIPAddress.IPv4ToCardinal(const AIPAddress: String;
- var VErr: Boolean): Cardinal;
- var
- LBuf, LBuf2 : String;
- L256Power : Integer;
- LParts : Integer; //how many parts should we process at a time
- begin
- // S.G. 11/8/2003: Added overflow checking disabling and change multiplys by SHLs.
- // Locally disable overflow checking so we can safely use SHL and SHR
- {$ifopt Q+} // detect previous setting
- {$define _QPlusWasEnabled}
- {$Q-}
- {$endif}
- VErr := True;
- L256Power := 4;
- LBuf2 := AIPAddress;
- Result := 0;
- repeat
- LBuf := Fetch(LBuf2,'.');
- if LBuf = '' then
- begin
- break;
- end;
- //We do things this way because we have to treat
- //IP address parts differently than a whole number
- //and sometimes, there can be missing periods.
- if (LBuf2='') and (L256Power > 1) then
- begin
- LParts := L256Power;
- Result := Result shl (L256Power SHL 3);
- end
- else
- begin
- LParts := 1;
- result := result SHL 8;
- end;
- if (Copy(LBuf,1,2)=HEXPREFIX) then
- begin
- //this is a hexideciaml number
- if IsHexidecimal(Copy(LBuf,3,MaxInt))=False then
- begin
- Exit;
- end
- else
- begin
- Result := Result + IPv4MakeCardInRange(StrToInt64Def(LBuf,0), LParts);
- end;
- end
- else
- begin
- if IsNumeric(LBuf) then
- begin
- if (LBuf[1]='0') and IsOctal(LBuf) then
- begin
- //this is octal
- Result := Result + IPv4MakeCardInRange(OctalToInt64(LBuf),LParts);
- end
- else
- begin
- //this must be a decimal
- Result := Result + IPv4MakeCardInRange(StrToInt64Def(LBuf,0), LParts);
- end;
- end
- else
- begin
- //There was an error meaning an invalid IP address
- Exit;
- end;
- end;
- Dec(L256Power);
- until False;
- VErr := False;
- // Restore overflow checking
- {$ifdef _QPlusWasEnabled} // detect previous setting
- {$undef _QPlusWasEnabled}
- {$Q-}
- {$endif}
- end;
- class function TIdIPAddress.IPv6ToIdIPv6Address(const AIPAddress: String;
- var VErr: Boolean): TIdIPv6Address;
- var
- LAddress:string;
- i:integer;
- begin
- LAddress := MakeCanonicalIPv6Address(AIPAddress);
- VErr := (LAddress='');
- if not VErr then begin
- for i := 0 to 7 do begin
- Result[i]:=StrToInt('$'+fetch(LAddress,':'));
- end;
- end;
- end;
- class function TIdIPAddress.MakeAddressObject(
- const AIP: String): TIdIPAddress;
- var LErr : Boolean;
- begin
- Result := TIdIPAddress.Create;
- Result.FIPv6 := Result.IPv6ToIdIPv6Address(AIP,LErr);
- if LErr then
- begin
- Result.FIPv4 := Result.IPv4ToCardinal(AIP,LErr);
- if LErr then
- begin
- //this is not a valid IPv4 address
- FreeAndNil(Result);
- end
- else
- begin
- Result.FAddrType := Id_IPv4;
- end;
- end
- else
- begin
- Result.FAddrType := Id_IPv6;
- end;
- end;
- class function TIdIPAddress.MakeCanonicalIPv4Address(
- const AAddr: string): string;
- var LErr : Boolean;
- LIP : Cardinal;
- begin
- LIP := IPv4ToDWord(AAddr,LErr);
- if LErr then
- begin
- Result := '';
- end
- else
- begin
- Result := MakeDWordIntoIPv4Address(LIP);
- end;
- end;
- class function TIdIPAddress.MakeCanonicalIPv6Address(
- const AAddr: string): string;
- // return an empty string if the address is invalid,
- // for easy checking if its an address or not.
- var
- p, i: integer;
- dots, colons: integer;
- colonpos: array[1..8] of integer;
- dotpos: array[1..3] of integer;
- LAddr: string;
- num: integer;
- haddoublecolon: boolean;
- fillzeros: integer;
- begin
- Result := ''; // error
- LAddr := AAddr;
- if Length(LAddr) = 0 then exit;
- if LAddr[1] = ':' then begin
- LAddr := '0'+LAddr;
- end;
- if LAddr[Length(LAddr)] = ':' then begin
- LAddr := LAddr + '0';
- end;
- dots := 0;
- colons := 0;
- for p := 1 to Length(LAddr) do begin
- case LAddr[p] of
- '.' : begin
- inc(dots);
- if dots < 4 then begin
- dotpos[dots] := p;
- end else begin
- exit; // error in address
- end;
- end;
- ':' : begin
- inc(colons);
- if colons < 8 then begin
- colonpos[colons] := p;
- end else begin
- exit; // error in address
- end;
- end;
- 'a'..'f',
- 'A'..'F': if dots>0 then exit;
- // allow only decimal stuff within dotted portion, ignore otherwise
- '0'..'9': ; // do nothing
- else exit; // error in address
- end; // case
- end; // for
- if not (dots in [0,3]) then begin
- exit; // you have to write 0 or 3 dots...
- end;
- if dots = 3 then begin
- if not (colons in [2..6]) then begin
- exit; // must not have 7 colons if we have dots
- end;
- if colonpos[colons] > dotpos[1] then begin
- exit; // x:x:x.x:x:x is not valid
- end;
- end else begin
- if not (colons in [2..7]) then begin
- exit; // must at least have two colons
- end;
- end;
- // now start :-)
- num := StrToIntDef('$'+Copy(LAddr, 1, colonpos[1]-1), -1);
- if (num<0) or (num>65535) then begin
- exit; // huh? odd number...
- end;
- Result := IntToHex(num,1)+':';
- haddoublecolon := false;
- for p := 2 to colons do begin
- if colonpos[p-1] = colonpos[p]-1 then begin
- if haddoublecolon then begin
- Result := '';
- exit; // only a single double-dot allowed!
- end;
- haddoublecolon := true;
- fillzeros := 8 - colons;
- if dots>0 then dec(fillzeros,2);
- for i := 1 to fillzeros do begin
- Result := Result + '0:'; {do not localize}
- end;
- end else begin
- num := StrToIntDef('$'+Copy(LAddr, colonpos[p-1]+1, colonpos[p]-colonpos[p-1]-1), -1);
- if (num<0) or (num>65535) then begin
- Result := '';
- exit; // huh? odd number...
- end;
- Result := Result + IntToHex(num,1)+':';
- end;
- end; // end of colon separated part
- if dots = 0 then begin
- num := StrToIntDef('$'+Copy(LAddr, colonpos[colons]+1, MaxInt), -1);
- if (num<0) or (num>65535) then begin
- Result := '';
- exit; // huh? odd number...
- end;
- Result := Result + IntToHex(num,1)+':';
- end;
- if dots > 0 then begin
- num := StrToIntDef(Copy(LAddr, colonpos[colons]+1, dotpos[1]-colonpos[colons]-1),-1);
- if (num < 0) or (num>255) then begin
- Result := '';
- exit;
- end;
- Result := Result + IntToHex(num, 2);
- num := StrToIntDef(Copy(LAddr, dotpos[1]+1, dotpos[2]-dotpos[1]-1),-1);
- if (num < 0) or (num>255) then begin
- Result := '';
- exit;
- end;
- Result := Result + IntToHex(num, 2)+':';
- num := StrToIntDef(Copy(LAddr, dotpos[2]+1, dotpos[3]-dotpos[2]-1),-1);
- if (num < 0) or (num>255) then begin
- Result := '';
- exit;
- end;
- Result := Result + IntToHex(num, 2);
- num := StrToIntDef(Copy(LAddr, dotpos[3]+1, 3), -1);
- if (num < 0) or (num>255) then begin
- Result := '';
- exit;
- end;
- Result := Result + IntToHex(num, 2)+':';
- end;
- SetLength(Result, Length(Result)-1);
- end;
- end.