Skip to content
Snippets Groups Projects
Select Git revision
  • a23b99eaeac4369f23689cea204b1aa7edf57835
  • master default protected
  • 9.0
  • 8.0
  • 7.8
  • 7.6
  • 7.4
  • 7.2
  • 7.0
  • 0.6
  • rosuav/latex-markdown-renderer
  • rxnpatch/rxnpatch
  • marcus/gobject-introspection
  • rxnpatch/8.0
  • rosuav/pre-listening-ports
  • nt-tools
  • rosuav/async-annotations
  • rosuav/pgsql-ssl
  • rxnpatch/rxnpatch-broken/2023-10-06T094250
  • grubba/fdlib
  • grubba/wip/sakura/8.0
  • v8.0.2004
  • v8.0.2002
  • v8.0.2000
  • v8.0.1998
  • v8.0.1996
  • v8.0.1994
  • v8.0.1992
  • v8.0.1990
  • v8.0.1988
  • v8.0.1986
  • rxnpatch/clusters/8.0/2025-04-29T124414
  • rxnpatch/2025-04-29T124414
  • v8.0.1984
  • v8.0.1982
  • v8.0.1980
  • v8.0.1978
  • v8.0.1976
  • v8.0.1974
  • v8.0.1972
  • v8.0.1970
41 results

basic.pike

Blame
  • IPv6.pmod 6.80 KiB
    #pike __REAL_VERSION__
    
    // Some IPv6 utilities.
    
    protected array(int) parse_ipv6_seq (string seq)
    {
      if (has_value (seq, "-"))
        // "-" could be interpreted as a minus sign.
        return 0;
    
      if (seq == "")
        return ({});
      if (sscanf (seq, "%4x%{:%4x%}%*c",
    	      int first, array(array(int)) rest) == 2)
        return ({first}) + (rest * ({}));
    }
    
    protected array(int) parse_ipv6_seq_with_ipv4_suffix (string seq)
    // seq is assumed to contain at least one ".".
    {
      array(int) segments = ({});
      array(string) split = seq / ":";
    
      foreach (split[..<1], string part) {
        int val;
        if (sscanf (part, "%4x%*c", val) != 1) return 0;
        segments += ({val});
      }
    
      array(string|int) sub_segs = split[-1] / ".";
    
      foreach (sub_segs; int i; string|int v) {
        if (sscanf (v, "%D%*c", v) != 1) return 0;
        sub_segs[i] = v;
      }
    
      switch(sizeof(sub_segs)) {
        case 4:
          if (sub_segs[0] > 0xff ||
    	  sub_segs[1] > 0xff ||
    	  sub_segs[2] > 0xff ||
    	  sub_segs[3] > 0xff) return 0;
          segments += ({ sub_segs[0]*256+sub_segs[1],
    		     sub_segs[2]*256+sub_segs[3] });
          break;
    
        case 3:
          // From inet(3): "When a three part address is specified, the
          // last part is interpreted as a 16-bit quantity and placed in
          // the rightmost two bytes of the network address. This makes
          // the three part address format convenient for specifying Class
          // B network addresses as 128.net.host."
          if (sub_segs[0] > 0xff ||
    	  sub_segs[1] > 0xff ||
    	  sub_segs[2] > 0xffff) return 0;
          segments += ({ sub_segs[0]*256+sub_segs[1],
    		     sub_segs[2] });
          break;
    
        case 2:
          // From inet(3): "When a two part address is supplied, the last
          // part is interpreted as a 24-bit quantity and placed in the
          // rightmost three bytes of the network address. This makes the
          // two part address format convenient for specifying Class A
          // network addresses as net.host."
          if (sub_segs[0] > 0xff ||
    	  sub_segs[1] > 0xffffff) return 0;
          segments += ({ sub_segs[0]*256 + (sub_segs[1] >> 16),
    		     sub_segs[1]&0xffff });
          break;
    
        default:
          return 0;
      }
    
      return segments;
    }
    
    array(int(0..65535)) parse_addr (string addr)
    //! Parses an IPv6 address on the formatted hexadecimal
    //! @expr{"x:x:x:x:x:x:x:x"@} form, or any of the shorthand varieties
    //! (see RFC 2373, section 2.2).
    //!
    //! The address is returned as an 8-element array where each element
    //! is the value of the corresponding field. Zero is returned if @[addr]
    //! is incorrectly formatted.
    //!
    //! @seealso
    //! @[format_addr_short]
    {
      sscanf (addr, "%s::%s", string part1, string part2);
      array(int) sections;
    
      if (!part1) {
        sections = has_value (addr, ".") ?
          parse_ipv6_seq_with_ipv4_suffix (addr) : parse_ipv6_seq (addr);
        if (!sections) return 0;
      }
    
      else {
        sections = parse_ipv6_seq (part1);
        if (!sections) return 0;
        array(int) tail = has_value (part2, ".") ?
          parse_ipv6_seq_with_ipv4_suffix (part2) : parse_ipv6_seq (part2);
        if (!tail) return 0;
        int pad = 8 - sizeof(sections) - sizeof(tail);
        if (pad >= 0)
          sections += allocate (pad) + tail;
        else
          sections += tail;
      }
    
      return sizeof (sections) == 8 && sections;
    }
    
    string format_addr_short (array(int(0..65535)) bin_addr)
    //! Formats an IPv6 address to the colon-separated hexadecimal form as
    //! defined in RFC 2373, section 2.2. @[bin_addr] must be an 8-element
    //! array containing the 16-bit fields.
    //!
    //! The returned address is on a canonical shortest form as follows:
    //! The longest sequence of zeroes is shortened using @tt{"::"@}. If
    //! there are several of equal length then the leftmost is shortened.
    //! All hexadecimal letters are lower-case. There are no superfluous
    //! leading zeroes in the fields.
    //!
    //! @seealso
    //! @[parse_addr]
    {
      string wide_addr = (string) bin_addr;
    
      // Optimize some common cases.
      if (wide_addr == "\0\0\0\0\0\0\0\0") return "::"; // ANY
      if (wide_addr == "\0\0\0\0\0\0\0\1") return "::1"; // loopback
    
      // Compress the longest sequence of zeros and format back to a
      // string with colon separated hex numbers.
      int maxlen, maxpos = -1;
      array(array(string)) split = array_sscanf (wide_addr, "%{%[\0]%[^\0]%}")[0];
      foreach (split; int pos; [string zeroes, string others]) {
        if (sizeof (zeroes) > maxlen) {
          maxlen = sizeof (zeroes);
          maxpos = pos;
        }
        split[pos][0] = "0:" * sizeof (zeroes);
        split[pos][1] = sprintf ("%{%x:%}", (array(int)) others);
      }
    
      switch (maxpos) {
        case -1:
          break;
    
        case 0:
          // Note: "::" special case above guarantees there's some later
          // field that isn't "".
          split[0][0] = "::";
          break;
    
        default:
          // Replace longest entry with a single colon since it gets
          // padded with the trailing one of the previous entry. We know
          // there's at least one earlier field that isn't "".
          split[maxpos][0] = ":";
    
          if (maxpos == sizeof (split) - 1 && split[maxpos][1] == "")
    	// Only case when we've got no later field that isn't "" is
    	// when maxpos is the last entry and the following non-zero
    	// field is empty. Don't shave off a trailing ":" then.
    	return (split * ({})) * "";
          break;
      }
    
      return ((split * ({})) * "")[..<1]; // Get rid of the trailing ":".
    }
    
    string normalize_addr_basic (string addr)
    //! Normalizes a formatted IPv6 address to a string with eight
    //! hexadecimal numbers separated by @tt{":"@}. @[addr] is given on
    //! the same form, or any of the shorthand varieties as specified in
    //! RFC 2373, section 2.2.
    //!
    //! All hexadecimal letters in the returned address are lower-case,
    //! and there are no superfluous leading zeroes in the fields.
    //!
    //! Zero is returned if @[addr] is incorrectly formatted.
    //!
    //! @seealso
    //! @[normalize_addr_short]
    {
      // Shortcuts.
      if (addr == "::") return "0:0:0:0:0:0:0:0"; // ANY
      if (addr == "::1") return "0:0:0:0:0:0:0:1"; // loopback
    
      array(int(0..65535)) bin_addr = parse_addr (addr);
      if (!bin_addr) return 0;
    
      return sprintf ("%{%x:%}", bin_addr)[..<1];
    }
    
    string normalize_addr_short (string addr)
    //! Normalizes a formatted IPv6 address to a canonical shortest form.
    //! @[addr] is parsed according to the hexadecimal
    //! @expr{"x:x:x:x:x:x:x:x"@} syntax or any of its shorthand varieties
    //! (see RFC 2373, section 2.2).
    //!
    //! The returned address is normalized as follows: The longest
    //! sequence of zeroes is shortened using @tt{"::"@}. If there are
    //! several of equal length then the leftmost is shortened. All
    //! hexadecimal letters are lower-case. There are no superfluous
    //! leading zeroes in the fields.
    //!
    //! Zero is returned if @[addr] is incorrectly formatted.
    //!
    //! @seealso
    //! @[normalize_addr_basic]
    {
      if (addr == "::" || addr == "::1") return addr; // ANY and loopback shortcuts.
    
      array(int(0..65535)) bin_addr = parse_addr (addr);
      if (!bin_addr) return 0;
    
      return format_addr_short (bin_addr);
    }