IP Address String Routines in Delphi?

被刻印的时光 ゝ 提交于 2020-01-22 12:58:11

问题


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...

  • Verify that a string is a valid IP address
  • Verify that a string is a valid subnet mask
  • Verify that an IP address is within a given Subnet
  • Some type (record or string or whatever) which is meant for storing an IP address
  • Basic conversion of an IP address types, such as String or Array[0..3] of Byte
  • Any other IP address routines that can make IP manipulation easier

The basic reason is that I want to see if these things are already out there before I go ahead and reinvent them.


回答1:


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.




回答2:


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:

  • Manipulating IPv4 addresses is fairly simple as they only consist of 32 bits which can be stored in a standard integer type. IPv6 addresses are harder as they need 128 bits and no native type has that many bits.
  • Before trying to manipulate an IP address, first convert it into an integer (IPv4 only of course, IPv6 will require a different storage method). To do this, split the IP address using '.' as a delimiter. Check each individual value only contains numbers and falls between 0 and 255 then use these values to generate the final integer.
  • Once the IP address has been converted into an integer, you can manipulate it however you like. For example, given an IP address and a subnet mask you can find the subnet that the IP address belongs to e.g. IPtoInt(IPAddress) AND NOT(IPToInt(SubnetMask)) = The integer of the subnet. Now, you can comaare an integer IP address to the integer subnet to see if the IP falls within the subnet.
  • Finally, convert the integer IP address back into a string.

If you have any specific questions I can try to answer them too.



来源:https://stackoverflow.com/questions/8525314/ip-address-string-routines-in-delphi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!