View Issue Details

IDProjectCategoryView StatusLast Update
0037013FPCRTLpublic2020-05-16 02:29
ReporterNoel Duffy Assigned ToMichael Van Canneyt  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Product Version3.0.4 
Fixed in Version3.3.1 
Summary0037013: StrToHostAddr6 in sockets unit parses invalid IPv6 addresses
DescriptionStrToHostAddr6 does not validate input IPv6 addresses. It splits the input string on colons, then takes each substring, prefixes '$', and passes the result to Val. If Val successfully converts the string, StrToHostAddr6 accepts it.

As an example, the string "fe80ca2f1::906e:9d2f:a520:4172" will be successfully parsed, even though it's not a valid IPv6 address because there are more than 4 characters in the first segment.
Steps To ReproduceThis small program demonstrates the problem:

program ip6bug;

uses
   SysUtils, sockets;

procedure parse_print_ip6(addr: String);
var
   ip6: in6_addr;
   idx: Cardinal;
begin
   ip6 := StrToHostAddr6(addr);
   write('"' + addr+'" ' + '-> ');
   for idx := 0 to 15 do
     write(IntToHex(byte(ip6.s6_addr8[idx]),2)+'-');
   writeln();
end;

const
   addr = 'fe80ca2f1::906e:9d2f:a520:4172';
begin
   parse_print_ip6(addr);
end.

This program uses StrToHostAddr6 to parse an input string, then it writes out the bytes in in6_addr record returned. It produces this output:

"fe80ca2f1::906e:9d2f:a520:4172" -> A2-F1-00-00-00-00-00-00-90-6E-9D-2F-A5-20-41-72-

TagsNo tags attached.
Fixed in Revision45350
FPCOldBugId
FPCTarget4.0.0
Attached Files

Relationships

related to 0037060 resolvedMichael Van Canneyt Add TryStrToHostAddr and TryStrToHostAddr6 to Sockets unit 
related to 0037061 closedMichael Van Canneyt function HostAddrToStr6 in sockets.inc: don't compare a boolean variable to True 

Activities

Noel Duffy

2020-05-12 11:18

reporter   ~0122723

This program demonstrates a number of problems with StrToHostAddr6. It tests over seventy badly formed addresses, and StrToHostAddr6 returns a result for 54. Similarly, 9 different good addresses are tried, and three of these fail. StrToHostAddr6 has no support for IPv4 addresses embedded in IPv6, as specified by RFC4291.
ip6_test.lpr (8,776 bytes)   
program ip6_test;

{$mode objfpc}{$H+}

{
Test StrToHostAddr6 in the sockets unit with lists of known bad and known
good IPv6 addresses.
}

uses Classes, sockets, SysUtils;

procedure BuildBaddAddrList(out bad_addrs: TStringList);
begin
  // start with some obviously bad formats.
  bad_addrs.Add('');
  bad_addrs.Add(':');
  bad_addrs.Add(':::');
  bad_addrs.Add('::.');
  bad_addrs.Add('::::');
  bad_addrs.Add('fe80:');
  bad_addrs.Add('x:');
  bad_addrs.Add('.');
  bad_addrs.Add('....');

  // invalid chars in all 8 hextets.
  bad_addrs.Add('fe@0:b46c:c2a1:a202:9*6e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b4%c:c2a1:a202:9*6e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:c2#1:a202:926e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:c2a1:a2^2:926e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:c2a1:a202:9*6e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9!d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:a=20:4172');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:a520:41+2');

  // $ sign in hextets.
  bad_addrs.Add('$fe80:b46c:c2a1:a202:926e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b$46c:c2a1:a202:926e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:c2a1$:a202:926e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926$e:9d2f:a520:4172');

  // last char is :
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:a520:4172:');

  // first char is :
  bad_addrs.Add(':fe80:b46c:c2a1:a202:926e:9d2f:a520:4172');

  // two sequences of collapsed zeroes. :: is the Highlander
  // sequence. There can be only one.
  bad_addrs.Add('fe80::c2a1:a202::9d2f:a520:4172');

  // 8 hextets plus collapsed zeroes, which means at least 1 hextet
  // of all zeroes, equaling 9 hextets.
  bad_addrs.Add('fe80:b46c:c2a1::a202:926e:9d2f:a520:4172');

  // try the same with the :: at the start.
  bad_addrs.Add('::b46c:c2a1:8fcb:a202:926e:9d2f:a520:4172');

  // and now try the same with the :: at the ned.
  bad_addrs.Add('b46c:a771:8fcb:a202:926e:9d2f:a520:4172::');

  // too many hextets
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:a520:4172:1211');

  // too few hextets
  bad_addrs.Add('fe80:b46c:9d2f:a520:4172:1211');

  // too many digits in each of the 8 hextets
  bad_addrs.Add('fe801:b46c:c2a1:a202:926e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b46cb:c2a1:a202:926e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:0c2ad:a202:926e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:c2a1:a2022:926e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e6:9d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:09d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:a5209:4172');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:a520:04172');

  // math signs in hextets. naive parsing of hextets with
  // math signs produces a positive result, but these are not
  // valid.
  bad_addrs.Add('fe80:-b46c:c2a1:a202:926e:9d2f:a520:4172');
  bad_addrs.Add('fe80:b46c:-c2a1:a202:926e:9d2f:a520:4172');

  // Hybrid 6 and 4 addresses.

  // ipv4 octet can't contain hex
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:19F.168.1.2');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192.1A8.1.2');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192.168.B.2');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192.168.1.C');

  // ipv4 octets can't contain math signs
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:-192.168.1.1');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192.-168.1.1');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192.168.-1.1');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192.168.1.-1');

  // invalid hybrid ipv6/ipv4 address, because there are 7
  // hextets before the ipv4. there can be only 6, as the 4
  // octets make up 2 hextets, and there can be no more than 8
  // hextets in total.
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:a520:192.168.1.2');

  // 5 hextets plus 2 hextets (4 octets) = 7. Must be 8.
  bad_addrs.Add('fe80:b46c:926e:9d2f:a520:192.168.1.2');

  // too few octets in ipv4 bit.
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192.168.1');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192.168');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192');

  // 7 hextets plus two octets of ipv4 = 8 hextets, but still must
  // not be parsed as valid because there must be 4 octets.
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:e2fc:192.168');

  // too many ipv4 octets
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192.168.1.17.32');

  // addr starts with .
  bad_addrs.Add('.fe80:b46c:c2a1:a202:926e:9d2f:192.168.1.17');
  // addr ends with .
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192.168.1.17.');

  // 6 hextets + 2 hextets (4 octets) plus collapsed zero sequence = 9 hextets.
  bad_addrs.Add('fe80:b46c::c2a1:a202:926e:9d2f:72.16.32.1');

  // repeat with :: at start
  bad_addrs.Add('::fe80:b46c:c2a1:a202:926e:9d2f:72.16.32.1');
  // and at end
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f::72.16.32.1');

  // ipv4 octets > 255
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:351.16.32.1');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:72.123216.32.1');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:72.16.9999999999999999999.1');
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:72.16.32.5e21');

  // dot sequence
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192..168.1.17');

  // start with dot
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:.192.168.1.17');

  // end with dot
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:9d2f:192.168.1.17.');

  // ipv4 octets followed by hextet
  bad_addrs.Add('fe80:b46c:c2a1:a202:926e:192.168.1.1:a2c1');

  // all zeroes but for mathematical operator.
  bad_addrs.Add('::-');

  // end on colon, but with earlier collapsed section.
  bad_addrs.Add('fe80::a202:926e:a2c1:');

  // too many digits in ipv4 octet
  bad_addrs.Add('21ac:d349:07c4:198e:6fab:df5a:0192.168.1.17');
  bad_addrs.Add('21ac:d349:07c4:198e:6fab:df5a:192.0168.1.17');
  bad_addrs.Add('21ac:d349:07c4:198e:6fab:df5a:192.168.0001.17');
  bad_addrs.Add('21ac:d349:07c4:198e:6fab:df5a:192.0168.1.0017');

  // just ipv4 address
  bad_addrs.Add('127.0.0.2');
end;

procedure BuildGoodAddrList(out addrlist: TStringList);
begin
  // Each str is two parts, separated by a pipe. The left part is the input
  // address to be parsed, and the right is the expected result of taking the
  // resulting address and converting back to a string. This provides an
  // easy way to verify that the StrToHostAddr6 function parsed the address
  // correctly.
  // The values on the right have been double-checked with libc's inet_pton.
  addrlist.Add('::1|::0001');
  addrlist.Add('::|::');
  addrlist.Add('2001:4860:4000::|2001:4860:4000::');
  addrlist.Add('21ac:d349:07c4:198e:6fab:df5a:192.168.1.17|21AC:D349:07C4:198E:6FAB:DF5A:C0A8:0111');
  addrlist.Add('21ac:d349:07c4:198e:6fab:df5a:0.0.0.0|21AC:D349:07C4:198E:6FAB:DF5A::');
  addrlist.Add('::213.41.35.14|::D529:230E');
  addrlist.Add('fe80:b46c:c2a1:a202:926e:9d2f:a520:4172|FE80:B46C:C2A1:A202:926E:9D2F:A520:4172');
  addrlist.Add('a:b:c:d:e:f:0:1|000A:000B:000C:000D:000E:000F::0001');
  addrlist.Add('a:B:c:D:e:f:9:1|000A:000B:000C:000D:000E:000F:0009:0001');
end;

function TestAddrs(al: TStringList): Cardinal;
var
  bad_addr: String;
  i6: in6_addr;
begin
  Result := 0;
  for bad_addr in al do
  begin
    i6 := StrToHostAddr6(bad_addr);
    if (i6.s6_addr32[0] <> 0) or (i6.s6_addr32[1] <> 0) or
      (i6.s6_addr32[2] <> 0) or (i6.s6_addr32[3] <> 0) then
    begin
      writeln('  [x]"'+bad_addr+'".');
      Inc(Result);
    end;
  end;
end;

function TestGoodAddrs(al: TStringList): Cardinal;
var
  addr,instr,parsed_addr,expected: String;
  i6: in6_addr;
  idx: Cardinal;
begin
  Result := 0;
  for addr in al do
  begin
    idx := Pos('|', addr);
    if idx > 0 then
    begin
      instr := Copy(addr,1,idx-1);
      i6 := StrToHostAddr6(instr);
      expected := Copy(addr, idx+1, Length(addr)-idx);
      parsed_addr := HostAddrToStr6(i6);
      if parsed_addr <> expected then
        writeln(' [x] "'+instr+'" -> '+parsed_addr+'".')
      else
        Inc(Result);
    end;
  end;
end;

var
  addrlist: TStringList;
  count: Cardinal;
begin
  ExitCode := 0;
  addrlist := TStringList.Create;

  BuildBaddAddrList(addrlist);
  count := TestAddrs(addrlist);
  writeln('Got non-zero result for '+inttostr(count)+' out of '+
    inttostr(addrlist.Count)+' bad addresses.');
  writeln();
  // if we successfully parsed any bad addresses
  if count > 0 then ExitCode := 1;

  addrlist.Clear;
  BuildGoodAddrList(addrlist);
  count := TestGoodAddrs(addrlist);
  writeln('Successfully parsed '+inttostr(count)+' out of '+
    inttostr(addrlist.Count)+' good addresses.');

  // if we didn't parse all the good addresses.
  if count < addrlist.Count then
    ExitCode := 1;
  addrlist.Free;
end.

ip6_test.lpr (8,776 bytes)   

Noel Duffy

2020-05-12 11:20

reporter   ~0122724

This is my proposed patch to rewrite StrToHostAddr6 to make it RFC4291 compliant. The patch is based off trunk, in the packages directory, since I didn't check out the entire fpc source.
StrToHostAddr6.patch (9,439 bytes)   
Index: rtl-extra/src/inc/sockets.inc
===================================================================
--- rtl-extra/src/inc/sockets.inc	(revision 45347)
+++ rtl-extra/src/inc/sockets.inc	(working copy)
@@ -442,57 +442,264 @@
     SetLength(HostAddrToStr6, Length(HostAddrToStr6)-1);
 end;
 
+function StrToHostAddr6(IP: String): in6_addr;
+type
+  TCharClass = (cHexDigit, cColon, cDot, cUnknown, cEndStr);
+  TParserMode = (pmIPv6, pmIPv4);
 
-function StrToHostAddr6(IP : String) : TIn6_addr;
-Var Part   : String;
-    IPv6   : TIn6_addr;
-    P,J    : Integer;
-    W      : Word;
-    Index  : Integer;
-    ZeroAt : Integer;
-Begin
-  FillChar(IPv6,SizeOf(IPv6),0);
-  FillChar(StrToHostAddr6,SizeOf(TIn6_addr),0);
-  { Every 16-bit block is converted at its own and stored into Result. When }
-  { the '::' zero-spacer is found, its location is stored. Afterwards the   }
-  { address is shifted and zero-filled.                                     }
-  Index := 0; ZeroAt := -1;
-  J := 0;
-  P := Pos(':',IP);
-  While (P > 0) and (Length(IP) > 0) and (Index < 8) do
-    Begin
-      Part := '$'+Copy(IP,1,P-1);
-      Delete(IP,1,P);
-      if Length(Part) > 1 then  { is there a digit after the '$'? }
-        Val(Part,W,J)
-      else W := 0;
-      IPv6.u6_addr16[Index] := HtoNS(W);
-      if J <> 0 then
-        Begin
-          FillChar(IPv6,SizeOf(IPv6),0);
-          Exit(IPV6);
-        End;
-      if IP[1] = ':' then
-        Begin
-          ZeroAt := Index;
-          Delete(IP,1,1);
-        End;
-      Inc(Index);
-      P := Pos(':',IP); if P = 0 then P := Length(IP)+1;
-    End;
-  { address      a:b:c::f:g:h }
-  { Result now   a : b : c : f : g : h : 0 : 0, ZeroAt = 2, Index = 6 }
-  { Result after a : b : c : 0 : 0 : f : g : h }
-  if ZeroAt >= 0 then
-    Begin
-      Move(IPv6.u6_addr16[ZeroAt+1],IPv6.u6_addr16[(8-Index)+ZeroAt+1],2*(Index-ZeroAt-1));
-      FillChar(IPv6.u6_addr16[ZeroAt+1],2*(8-Index),0);
-    End;
+  TCharRec = record
+    ch: AnsiChar;
+    ctype: TCharClass;
+  end;
 
-  StrToHostAddr6:=IPv6;
-End;
+  TToken = record
+    s: ShortString;
+    tt: TCharClass;
+  end;
 
+  function get_char_class(ch: AnsiChar): TCharClass;
+  begin
+    get_char_class := cUnknown;
+    case ch of
+      'A' .. 'F', 'a' .. 'f', '0' .. '9': get_char_class := cHexDigit;
+      ':': get_char_class := cColon;
+      '.': get_char_class := cDot;
+    else
+      get_char_class := cUnknown;
+    end;
+  end;
 
+  function is_eos(idx: Cardinal): Boolean;
+  begin
+    is_eos := (idx < 1) or (idx > Length(IP));
+  end;
+
+  function next_char(idx: Cardinal): TCharRec;
+  begin
+    next_char.ctype := cUnknown;
+    if is_eos(idx) then
+    begin
+      next_char.ch := '-';
+      next_char.ctype := cEndStr;
+    end
+    else
+    begin
+      next_char.ch := IP[idx];
+      next_char.ctype := get_char_class(next_char.ch);
+    end;
+  end;
+
+  function next_token(var idx: Cardinal): TToken;
+  var
+    rch: TCharRec;
+    prv: TCharClass;
+  begin
+    next_token.s := '';
+    next_token.tt := cUnknown;
+    rch := next_char(idx);
+    prv := rch.ctype;
+    next_token.tt := rch.ctype;
+    while (rch.ctype <> cEndStr) and (rch.ctype = prv) do
+    begin
+      next_token.s := next_token.s + rch.ch;
+      Inc(idx);
+      rch := next_char(idx);
+    end;
+  end;
+
+  function convert_hextet(const s: ShortString; var res: Word): Boolean;
+  var
+    tmpval,valcode: Word;
+  begin
+    convert_hextet := False;
+    if Length(s) > 4 then exit;
+    Val('0x'+s,tmpval,valcode);
+    if valcode <> 0 then exit;
+    res := htons(tmpval);
+    convert_hextet := True;
+  end;
+
+  function convert_octet(const s: ShortString; var res: Byte): Boolean;
+  var
+    tmpval: Word;
+    valcode: Word;
+  begin
+    convert_octet := False;
+    if Length(s) > 3 then exit;
+    Val(s,tmpval,valcode);
+    if valcode <> 0 then exit;
+    if tmpval > 255 then exit;
+    res := tmpval;
+    convert_octet := True;
+  end;
+
+var
+  tkn, ptkn: TToken;
+  idx: Cardinal;
+  hextet_arr: array[0 .. 7] of Word = (0,0,0,0,0,0,0,0);
+  hextet_idx, octet_idx,coll_start_idx: byte;
+  octet_arr: array[0 .. 3] of byte = (0,0,0,0);
+  coll_zero_seen: Boolean = False;
+  parser_mode: TParserMode = pmIPv6;
+  tmpval: Word = 0;
+  tmpByte: Byte = 0;
+
+begin
+  StrToHostAddr6.s6_addr32[0] := 0;
+  StrToHostAddr6.s6_addr32[1] := 0;
+  StrToHostAddr6.s6_addr32[2] := 0;
+  StrToHostAddr6.s6_addr32[3] := 0;
+
+  if (Length(IP) > 45) or (Length(IP) < 2) then exit;
+
+  hextet_idx := 0;
+  coll_start_idx := 0;
+  octet_idx := 0;
+  idx := 1;
+
+  ptkn.s := '';
+  ptkn.tt := cUnknown;
+
+  tkn := next_token(idx);
+  while (tkn.tt <> cEndStr) do
+  begin
+    case tkn.tt of
+      cHexDigit:
+        begin
+          case parser_mode of
+            pmIPv6:
+              begin
+                if (hextet_idx <= 7) and (convert_hextet(tkn.s, tmpval)) then
+                begin
+                  hextet_arr[hextet_idx] := tmpval;
+                  Inc(hextet_idx);
+                end
+                else
+                  exit; // too many hextets, or invalid hextet.
+              end;
+            pmIPv4:
+              begin
+                if (octet_idx <= 3) and (convert_octet(tkn.s, tmpByte)) then
+                begin
+                  octet_arr[octet_idx] := tmpByte;
+                  Inc(octet_idx);
+                end
+                else
+                  exit; // too many octets, or invalid octet.
+              end;
+          end;
+        end;
+
+      cColon:
+        begin
+          if (parser_mode = pmIPv4) or (Length(tkn.s) > 2) then exit;
+          if Length(tkn.s) = 2 then
+          begin
+            // if we saw a collapsed sequence before, or if we've already
+            // seen 8 hextets.
+            if (coll_zero_seen = True) or (hextet_idx > 7) then exit;
+            coll_zero_seen := True;
+            coll_start_idx := hextet_idx;
+            Inc(hextet_idx);
+          end
+          else if Length(tkn.s) = 1 then
+          begin
+            // is this single colon the first token? if so, address is invalid.
+            // if the prev token is cUnknown, then this must be the first token.
+            if ptkn.tt = cUnknown then exit;
+          end;
+        end;
+
+      cDot:
+        begin
+          if Length(tkn.s) > 1 then exit;
+
+          // By the time we see the first dot, the first octet of the IPv4
+          // address has already been processed as an IPv6 hextet. we have
+          // to backtrack to remove that value from hextet_arr
+          // and reprocess the value as ipv4.
+          if parser_mode = pmIPv6 then
+          begin
+            if ptkn.tt = cHexDigit then
+            begin
+              Dec(hextet_idx);
+              hextet_arr[hextet_idx] := 0;
+
+              if (octet_idx <= 3) and (convert_octet(ptkn.s, tmpByte)) then
+              begin
+                octet_arr[octet_idx] := tmpByte;
+                Inc(octet_idx);
+              end
+              else
+                exit; // too many octets, or invalid octet.
+            end
+            else // dot preceded by something other than digit
+              exit;
+            parser_mode := pmIPv4;
+          end;
+        end;
+
+      cUnknown:
+        exit;
+    end;
+    ptkn := tkn;
+    tkn := next_token(idx);
+  end;
+
+  // if we finished on a . or :, the address is invalid.
+  if (ptkn.tt = cDot) or ((ptkn.tt = cColon) and (Length(ptkn.s) = 1)) then
+    exit;
+
+  // if there's an ipv4 addr, add its octets onto the end
+  // of the ipv6 hextet array. we have to convert the bytes to
+  // words.
+  if (parser_mode = pmIPv4) then
+  begin
+    if (octet_idx = 4) and (hextet_idx <= 6) then
+    begin
+      tmpval := (octet_arr[0] shl 8) + (octet_arr[1]);
+      hextet_arr[hextet_idx] := htons(tmpval);
+      Inc(hextet_idx);
+      tmpval := (octet_arr[2] shl 8) + (octet_arr[3]);
+      hextet_arr[hextet_idx] := htons(tmpval);
+      Inc(hextet_idx);
+    end
+    else
+      exit; // invalid no of ipv4 octets, or not enough room for them.
+  end;
+
+  // finish line is in sight. if we have a collapsed-zeroes sequence
+  // then we must fill that in now.
+  if coll_zero_seen = True then
+  begin
+    for tmpByte := 0 to coll_start_idx do
+      StrToHostAddr6.s6_addr16[tmpByte] := hextet_arr[tmpByte];
+
+    // hextet_idx-1 points to the final byte we processed, in the hextet_arr
+    // array. starting there, reading back to coll_start_idx, we copy these
+    // words to the end of the Result array, with word hextet_idx-1 going at
+    // the end of the Result array, hextet_idx-2 going to the end - 1 of Result,
+    // and so on.
+    // NOTE: optimization note -- a memmove/memcpy equivalent could help here.
+    tmpByte := hextet_idx-1;
+    idx := 7;
+    while tmpByte > coll_start_idx do
+    begin
+      StrToHostAddr6.s6_addr16[idx] := hextet_arr[tmpByte];
+      Dec(tmpByte);
+      Dec(idx);
+    end;
+  end
+  else
+  begin
+    // no collapsed zeroes. we must have exactly 8 words then, or we're short.
+    // NOTE: optimization note: memmove/memcpy equivalent could help here.
+    if hextet_idx < 8 then exit;
+    for tmpByte := 0 to 7 do
+      StrToHostAddr6.s6_addr16[tmpByte] := hextet_arr[tmpByte];
+  end;
+end;
+
 function NetAddrToStr6 (Entry : TIn6_Addr) : ansiString;
 begin
   netaddrtostr6 := HostAddrToStr6((Entry));
StrToHostAddr6.patch (9,439 bytes)   

Bart Broersma

2020-05-12 15:50

reporter   ~0122729

This is a bit ugly:
if (coll_zero_seen = True) (occurs twice)
No need to compare a boolean against either True or False

Michael Van Canneyt

2020-05-13 09:20

administrator   ~0122757

Applied, added test program as tw37013.pp. Thank you very much !

Jonas Maebe

2020-05-14 19:03

manager   ~0122799

There seem to be range errors in the code: https://www.freepascal.org/testsuite/cgi-bin/testsuite.cgi?action=3&run1id=497745&run2id=497919&testfileid=6037

NoName

2020-05-16 02:29

reporter   ~0122837

for tmpByte := 0 to 7 do
      StrToHostAddr6.s6_addr16[tmpByte] := hextet_arr[tmpByte];
could be replaced by
    move(hextet_arr[0], StrToHostAddr6.s6_addr16[0], sizeof(hextet_arr));

But maybe the compiler is smart enough to create the same code for it already.

Issue History

Date Modified Username Field Change
2020-05-03 22:49 Noel Duffy New Issue
2020-05-12 11:18 Noel Duffy Note Added: 0122723
2020-05-12 11:18 Noel Duffy File Added: ip6_test.lpr
2020-05-12 11:20 Noel Duffy Note Added: 0122724
2020-05-12 11:20 Noel Duffy File Added: StrToHostAddr6.patch
2020-05-12 15:50 Bart Broersma Note Added: 0122729
2020-05-13 09:20 Michael Van Canneyt Assigned To => Michael Van Canneyt
2020-05-13 09:20 Michael Van Canneyt Status new => resolved
2020-05-13 09:20 Michael Van Canneyt Resolution open => fixed
2020-05-13 09:20 Michael Van Canneyt Fixed in Version => 3.3.1
2020-05-13 09:20 Michael Van Canneyt Fixed in Revision => 45350
2020-05-13 09:20 Michael Van Canneyt FPCTarget => 4.0.0
2020-05-13 09:20 Michael Van Canneyt Note Added: 0122757
2020-05-13 10:35 Michael Van Canneyt Relationship added related to 0037060
2020-05-13 12:11 Michael Van Canneyt Relationship added related to 0037061
2020-05-14 19:03 Jonas Maebe Note Added: 0122799
2020-05-16 02:29 NoName Note Added: 0122837