I'm looking for a way in Delphi to validate and manipulate IP Addresses. Some of the things it should be able to do is...
String or Array[0..3] of Byte
The basic reason is that I want to see if these things are already out there before I go ahead and reinvent them.
I also once wrote a IPv4 and IPv6 conversion unit including a custom variant type for both types of IP addresses. This answer shows a few examples of its capabilities. Originally, it was designed to visualize values of various types in scale on some slider control 1). The requirements then were such that default existing libraries weren't sufficient, but I agree with the comments here that you probably will be helped with just Indy (10!) or alike.
Answering your list of questions with a few code snippets from that unit:
Q4: Storage type for IP types:
  const
    IPv4BitSize = SizeOf(Byte) * 4 * 8;
    IPv6BitSize = SizeOf(Word) * 8 * 8;
  type
    T4 = 0..3;
    T8 = 0..7;
    TIPv4ByteArray = array[T4] of Byte;
    TIPv6WordArray = array[T8] of Word;
    TIPv4 = packed record
      case Integer of
        0: (D, C, B, A: Byte);
        1: (Groups: TIPv4ByteArray);
        2: (Value: Cardinal);
    end;
    TIPv6 = packed record
      case Integer of
        0: (H, G, F, E, D, C, B, A: Word);
        1: (Groups: TIPv6WordArray);
    end;
Q5: Conversion of IP address strings to these record or array types:
  function StrToIPv4(const S: String): TIPv4;
  var
    SIP: String;
    Start: Integer;
    I: T4;
    Index: Integer;
    Count: Integer;
    SGroup: String;
    G: Integer;
  begin
    SIP := S + '.';
    Start := 1;
    for I := High(T4) downto Low(T4) do
    begin
      Index := PosEx('.', SIP, Start);
      if Index = 0 then
        IPv4ErrorFmt(SInvalidIPv4Value, S);
      Count := Index - Start + 1;
      SGroup := Copy(SIP, Start, Count - 1);
      if TryStrToInt(SGroup, G) and (G >= Low(Word)) and (G <= High(Word)) then
          Result.Groups[I] := G
        else
          Result.Groups[I] := 0;
      Inc(Start, Count);
    end;
  end;
  function StrToIPv6(const S: String): TIPv6;
  { Valid examples for S:
    2001:0db8:85a3:0000:0000:8a2e:0370:7334
    2001:db8:85a3:0:0:8a2e:370:7334
    2001:db8:85a3::8a2e:370:7334
    ::8a2e:370:7334
    2001:db8:85a3::
    ::1
    ::
    ::ffff:c000:280
    ::ffff:192.0.2.128 }
  var
    ZeroPos: Integer;
    DotPos: Integer;
    SIP: String;
    Start: Integer;
    Index: Integer;
    Count: Integer;
    SGroup: String;
    G: Integer;
    procedure NormalNotation;
    var
      I: T8;
    begin
      SIP := S + ':';
      Start := 1;
      for I := High(T8) downto Low(T8) do
      begin
        Index := PosEx(':', SIP, Start);
        if Index = 0 then
          IPv6ErrorFmt(SInvalidIPv6Value, S);
        Count := Index - Start + 1;
        SGroup := '$' + Copy(SIP, Start, Count - 1);
        if not TryStrToInt(SGroup, G) or (G > High(Word)) or (G < 0) then
          IPv6ErrorFmt(SInvalidIPv6Value, S);
        Result.Groups[I] := G;
        Inc(Start, Count);
      end;
    end;
    procedure CompressedNotation;
    var
      I: T8;
      A: array of Word;
    begin
      SIP := S + ':';
      Start := 1;
      I := High(T8);
      while Start < ZeroPos do
      begin
        Index := PosEx(':', SIP, Start);
        if Index = 0 then
          IPv6ErrorFmt(SInvalidIPv6Value, S);
        Count := Index - Start + 1;
        SGroup := '$' + Copy(SIP, Start, Count - 1);
        if not TryStrToInt(SGroup, G) or (G > High(Word)) or (G < 0) then
          IPv6ErrorFmt(SInvalidIPv6Value, S);
        Result.Groups[I] := G;
        Inc(Start, Count);
        Dec(I);
      end;
      FillChar(Result.H, (I + 1) * SizeOf(Word), 0);
      if ZeroPos < (Length(S) - 1) then
      begin
        SetLength(A, I + 1);
        Start := ZeroPos + 2;
        repeat
          Index := PosEx(':', SIP, Start);
          if Index > 0 then
          begin
            Count := Index - Start + 1;
            SGroup := '$' + Copy(SIP, Start, Count - 1);
            if not TryStrToInt(SGroup, G) or (G > High(Word)) or (G < 0) then
              IPv6ErrorFmt(SInvalidIPv6Value, S);
            A[I] := G;
            Inc(Start, Count);
            Dec(I);
          end;
        until Index = 0;
        Inc(I);
        Count := Length(A) - I;
        Move(A[I], Result.H, Count * SizeOf(Word));
      end;
    end;
    procedure DottedQuadNotation;
    var
      I: T4;
    begin
      if UpperCase(Copy(S, ZeroPos + 2, 4)) <> 'FFFF' then
        IPv6ErrorFmt(SInvalidIPv6Value, S);
      FillChar(Result.E, 5 * SizeOf(Word), 0);
      Result.F := $FFFF;
      SIP := S + '.';
      Start := ZeroPos + 7;
      for I := Low(T4) to High(T4) do
      begin
        Index := PosEx('.', SIP, Start);
        if Index = 0 then
          IPv6ErrorFmt(SInvalidIPv6Value, S);
        Count := Index - Start + 1;
        SGroup := Copy(SIP, Start, Count - 1);
        if not TryStrToInt(SGroup, G) or (G > High(Byte)) or (G < 0) then
          IPv6ErrorFmt(SInvalidIPv6Value, S);
        case I of
          0: Result.G := G shl 8;
          1: Inc(Result.G, G);
          2: Result.H := G shl 8;
          3: Inc(Result.H, G);
        end;
        Inc(Start, Count);
      end;
    end;
  begin
    ZeroPos := Pos('::', S);
    if ZeroPos = 0 then
      NormalNotation
    else
    begin
      DotPos := Pos('.', S);
      if DotPos = 0 then
        CompressedNotation
      else
        DottedQuadNotation;
    end;
  end;
For Q1 to Q3 you have to derive some routines yourself, but that should not be any problem.
1) For those interested, it's this slider control and this topic served as initiation of this unit.
I have already written all the functions you require but I'm afraid I'm not in a position to share the code.
However, the Synapse library contains quite a few functions in the synaip unit. e.g.
function IsIP(const Value: string): Boolean;
function IsIP6(const Value: string): Boolean;
function IPToID(Host: string): Ansistring;
function StrToIp6(value: string): TIp6Bytes;
function Ip6ToStr(value: TIp6Bytes): string;
function StrToIp(value: string): integer;
function IpToStr(value: integer): string;
function ReverseIP(Value: AnsiString): AnsiString;
function ReverseIP6(Value: AnsiString): AnsiString;
When I tried the functions a few years ago, the IPv6 functions were a bit buggy, especially when dealing with compressed IPv6 addresses.
If you want to roll your own, here a few pointers:
If you have any specific questions I can try to answer them too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With