Changes for version 4.080_04 - 2026-10-09 (TRIAL RELEASE)

  • Fix GH#109: The wrap check in the Util EXAMPLES compared addresses as strings, so text order stood in for address order. Two snippets stepped an address with the overloaded ++ and decided whether the host part had wrapped by comparing the string forms before and after. As text 192.0.2.10 sorts before 192.0.2.9, so the step from .9 to .10 read as a wrap, and the real wrap from .11 back to .8 sorted after, so it read as no wrap. Both snippets happened to print what the page said: the first never wrapped, so its addconst branch never ran, and every address in the loop had three digits in the last octet, where text order and address order agree.
  • Stepping every aligned subnet of 192.0.2.0/24 from /24 to /31, 255 nets and 2048 steps, each net wrapping once, the string test reported 13 false wraps and missed 6 of the 255 real ones, in 192.0.2.64/26, 192.0.2.96/27, 192.0.2.96/28, 192.0.2.8/29, 192.0.2.96/29 and 192.0.2.8/30. Six IPv6 nets in 2001:db8::/32 gave 3 false wraps and no missed ones. Comparing the objects got all 2048 IPv4 steps and all 4648 IPv6 steps right on both builds.
  • Both snippets now take $before = $ip->copy and test $ip < $before, the overloaded comparison, which subtracts the two 128 bit addresses and is address order for either family. A step inside the net raises the address and a wrap drops it to the network address, so the new address is the lower one only at a wrap. The first snippet starts at 192.0.2.127/26 and steps by 64, so it takes the wrap branch and shows the addconst list assignment the paragraph above it is about; it prints 192.0.2.128/26, where the string test prints 192.0.2.64/26. The loop starts at 192.0.2.8/30, which passes both failure points, and its output follows as result comments. The paragraph between them had called the overloaded < the wrong test, when it is the right one, and had said comparing string forms is what detects the wrap; the only fault in the original test was $ip-copy(), which is not Perl. No code outside the examples changes.
  • Fix GH#111: Build XS by default where a compiler works, and delete the checkout Makefile.PL. Three edits in 4f61b57 combined to disable the compiler test: $useXS was initialised to 0 rather than left undefined, which also made the new "!defined $useXS" test in the Windows rule permanently false; the probe condition became "if ($useXS)" rather than "unless (defined $useXS)", so it ran only when --xs was given; and a successful probe no longer set $useXS = 1. So a plain "perl Makefile.PL" wrote the pure Perl Makefile everywhere. That is the command cpan and cpanm run, so an install from the next release would have built pure Perl even where a compiler works, and the Linux CI job meant to test the XS has been testing pure Perl while staying green. The same commit also dropped the closing brace of an "if ($useXS)" block, so the checked-out Makefile.PL no longer compiled at all.
    • header.pl leaves $useXS undefined until an option or the platform rule sets it, runs the probe when neither --xs nor -noxs was given, and sets $useXS = 1 when a compiler works. That restores the 4.079 default while keeping the 4f61b57 change that lets --xs override the Windows rule. With --xs the probe is skipped and the build is XS, as in 4.079; a broken compiler now fails at make instead of falling back to pure Perl, which is what asking for the XS explicitly should mean.
    • Each of the four build jobs now checks the mode it got rather than passing as whichever build it happened to produce. t/033-util-mode.t cannot do this: it derives its expectation from Util_IS->not_pure, so it agrees with the build either way. The macOS job asserts Pure Perl, which is the point of pinning it down, since it gets there by the accident that "darwin" contains "win" and so matches the /win/i platform rule.
    • The checked-out Makefile.PL is deleted rather than repaired. It is excluded from the distribution, nothing regenerates it, and it had drifted from inc/MakeMaker: it still used the older %makeparms spelling instead of @mm_args, and carried a realclean naming xs/config.h, xs/config.log and xs/config.status, which GH#101 stopped generating, plus a clean override protecting lib/NetAddr/IP/Util.c, which GH#96 established is not tracked in git. README.md now says so and gives the dzil build path.
    • Restoring the XS build exposed a fault in the clean list that the pure Perl default had been hiding. make clean did not remove lib/NetAddr/IP/Util.o, because ExtUtils::MakeMaker only knows about C_FILES and OBJECT when the XS branch fills them in, so the file survived a clean, then a later perl Makefile.PL -noxs in the same tree copied it into blib as though it were a module, which dragged the postamble's xsubpp rule into a pure Perl build where $(XSUBPP) is unset. That failed on "-ypemap". The clean list now names Util.c, Util.o and Util.c.xsc as well. All three are generated, and in a pure Perl tree naming absent files costs nothing.
    • Fix GH#114: The XS build had not compiled since GH#106, so CI never noticed: its Linux XS job builds pure Perl, see GH#111. The file carried two definitions of _num_arg and two of have128 with no body. It was missing _128x10plusbcd, which _bcdn2bin still called, and the unused _isipv4. It used k and nv in comp128 and bcd2bin without declaring them, and read the packed argument with SvPV rather than SvPVbyte in seven functions. Restored GH#106 as filed, tests included: the duplicate is gone, have128 keeps its GH#102 unaligned access fix, the missing helpers are back, the packed argument is read with SvPVbyte while the digit text in bcd2bin and simple_pack stays SvPV, and a count or constant is read before a pointer into the address is taken. A fifth defect in the same area: a packed address reached through get magic looked undefined. _packed_ok runs the magic once and reports whether the value is then defined, _packed_bytes reads the bytes without running the magic again and through a downgraded copy when the argument is upgraded. SvPVbyte_nomg would do this in one call but needs perl 5.31.4, so this uses only calls available from 5.9.2.
    • Fix GH#101: Remove the autoconf/configure step. The XS build ran one of two autoconf scripts to write xs/config.h, and the C code takes exactly two facts from that file: the host byte order and a pair of 32-bit integer types. Both have been in perl.h since at least 5.005_03. The configure step can go: four files, 31 percent of the compressed tarball, about 60 lines of the Makefile.PL header, and the reason the module has historically given for building pure Perl on Windows.
    • Fix GH#102: Four helpers in xs/Util.xs read the caller's packed address as 32-bit words through a cast of the Perl string buffer. That is undefined behaviour in C whenever the buffer is not 4-byte aligned, and Perl hands the XSUBs unaligned buffers in ordinary use. A string whose leading bytes were removed in place keeps its allocation and moves its start. On x86 and arm64 the loads still give the right answer, which is why nothing has failed; on hardware that traps misaligned loads, SPARC above all, the process dies with SIGBUS. The fix copies the bytes into local words with
  • memcpy, which compiles to the same instructions where unaligned loads are cheap.
    • Fix GH#7: Document that uppercase is the effective default, not lowercase. InetBase said "This module defaults to lowercase", which holds only when it is loaded on its own. NetAddr::IP::Util loads it with :upper, so every program that uses NetAddr::IP prints uppercase. The case setting is process wide and the last import or call wins, so a lower() anywhere changes what every object prints. InetBase, IP.pm and Lite.pm now say so.
    • Fix GH#12: The :aton packed input is corrupted by lc for bytes 0x41 to 0x5A. The _xnew() function in Lite.pm ran $ip = lc $ip; before checking whether the input was a packed binary address, so bytes 0x41-0x5A (A-Z) became 0x61-0x7a (a-z). Fixed by preserving the original input in $raw before lowercasing, and using it for the binary IP detection path.
    • Fix GH#21: ipv6_aton accepts a single leading or trailing colon, which RFC 4291 section 2.2 does not allow. The pure-Perl build shares this code and behaves the same way. Fixed by adding explicit checks for a single leading or trailing colon before processing.
    • Final polish: constructor entry cleanup and import tag refinement.
    • Unify the argument names and finish the import tag tables (GH#100, C5 and C7), the last two items from the structure list.
    • C5 named one concept six ways across the three function pages. For a 16 byte string there were $ipv6naddr, $ipv6addr, $pv6naddr, $bits128 and $netaddr. Now $bits128 is the 16 byte string, $ipv6naddr an address being converted, and $netaddr a 4 byte one:
      • $bits128 52 -> 60 uses, after the sweep $ipv6naddr 48 -> 50 $netaddr 29 $ipv6addr 8 -> 0 $pv6naddr 2 -> 0
    • $pv6naddr was a typo for $ipv6naddr, in two places. Checked first that every occurrence of both names was POD or POD inside a comment, so no code was renamed:
      • $ipv6addr in non-POD code 0 lines in each of the three files
    • C7, three pages carried an import tag section and Lite.pm carried none, and none of them said what happens when :lower and :upper are both given. Verified rather than assumed, in both classes:
      • NetAddr::IP :lower 2001:db8:0:0:0:0:0:1 NetAddr::IP :upper 2001:DB8:0:0:0:0:0:1 NetAddr::IP :lower :upper 2001:DB8:0:0:0:0:0:1 NetAddr::IP :upper :lower 2001:DB8:0:0:0:0:0:1
    • :upper wins either way, because :lower is handled first in import. Lite.pm now has an IMPORT TAGS section covering the five tags it accepts, each marked process-wide, and the precedence stated. Which is the five was measured, since :old_storable and :rfc3021 belong to NetAddr::IP alone:
      • :old_storable rejected by Lite :rfc3021 rejected by Lite :aton accepted :old_nth accepted :nofqdn accepted
    • Documentation only, no code change.
    • Group the remaining method and function entries (GH#100, C2 and the grouped layout). IP.pm was grouped in the previous commit; this does the other four.
      • Util.pm 34 entries into Text to binary, Binary to text, Case, Family tests, Widening and narrowing, Arithmetic, Decimal strings, Resolver, Build mode InetBase.pm 21 entries into Text to binary, Binary to text, Family tests, Address family, Short IPv4 text, Case UtilPP.pm 13 entries into Family test and shift, Arithmetic, Widening and narrowing, Decimal strings Lite.pm 27 entries into the six groups it has, the same names IP.pm uses where they overlap
    • Each entry block was moved whole and checked to have survived verbatim, and both the definition count and the POD item count are unchanged in all six modules:
      • IP.pm 31 subs, 67 items Lite.pm 50 subs, 39 items Util.pm 7 subs, 41 items InetBase.pm 29 subs, 25 items UtilPP.pm 28 subs, 13 items Constants.pm 0 subs, 14 items
    • Two things worth recording about how this went.
    • podchecker needs =head2 outside an =over, so each group is its own list rather than headings inside one list. It also needs a blank line before =back, or the =back is swallowed as ordinary text and the list reads as unclosed. That only bit one group, because the group whose entries happened to be last in the old order had no trailing blank line to inherit. The error pointed at the next =head2, which sent the first two attempts looking for an imbalance that was not there; the cause only showed up when the block was reproduced in three lines.
    • UtilPP.pm had two =head1 DESCRIPTION sections, one of them a single line left over from the step 2 rewrite, and no FUNCTIONS heading at all. Both fixed here.
    • C3 finished at the same time: INSTALLATION is now gone from every module. It was already gone from Util.pm, InetBase.pm, UtilPP.pm and Constants.pm; IP.pm and Lite.pm still had it, so the build steps live in README.md alone, and the two pages point at it from DESCRIPTION. Nothing linked to the removed sections.
    • Documentation only, no code change.
    • Fix README.md (GH#100, step 4, the last one). It had errors of its own, and three of them meant a reader's first attempt at the advanced methods died.
    • The four calling conventions in the advanced section were all wrong. They were written as functions called on the class name, where the arguments land in $_[0] where an object is expected:
      • NetAddr::IP->split($base,$bits) dies: Can't use string ("NetAddr::IP") as a HASH ref NetAddr::IP->rsplit($base,$target) dies: Can't use string ("NetAddr::IP") as a HASH ref NetAddr::IP->hostenum($ips) dies: Can't use string ("NetAddr::IP") as a HASH ref NetAddr::IP->compact(@ips) dies: Can't use string ("NetAddr::IP") as a HASH ref NetAddr::IP->coalesce(@ips) returns one value, silently
    • Every one of them is a method on an object:
      • $ip->split(26) $ip->rsplit(28,29,28,29,26) $ip->splitref(26) $ip->rsplitref(28,29,28,29,26) $ip->hostenum $ip->hostenumref $ip->compact(@other) $ip->compactref(\@other) $ip->coalesce($masklen,$number,@other)
    • The coalesce case is the nastiest, because it returns rather than dying, which is the same silent-wrong-answer shape as GH#39.
    • The examples used 192.168.1.123 and fe80::1. Replaced with the RFC 5737 and RFC 3849 documentation addresses, so nothing in the README points at a range someone might own, and the IPv6 outputs are the real ones, which are not what a reader would guess:
      • new('2001:db8::1/64')->network 2001:DB8:0:0:0:0:0:0/64 new('2001:db8::1/64')->broadcast 2001:DB8:0:0:0:FFFF:FFFF:FFFF:FFFF/64
    • InetBase was described as "low-level inet address conversion (mostly XS)". It is pure Perl on every host; UtilPP is the pure Perl half and Util is the XS one. The module list also said four modules and gave no mention of UtilPP or Constants, so all six are now listed, with NetAddr::IP::Util_IS noted as generated.
    • is_local was described as "true for 127.x.x.x or ::1", which misses the two IPv6 forms the code accepts:
      • 127.0.0.1=1 ::1=1 ::127.0.0.1=1 ::ffff:127.0.0.1=1
    • The export tag table omitted :old_storable, :aton and :rfc3021. All three are added and marked deprecated, and the entry for :rfc3021 says why it is no longer needed.
    • Added what was missing rather than only fixing: the mask-argument trap, the array deref overload and the two croaking operators, the comparison order, the /31 and /127 host count, the split croak, a worked example showing split and rsplitref actually differing on the same plan, and a Functions section for Compact, Coalesce and netlimit, which had no home at all.
    • The Authors section listed two of the three, and gave an address for Michael Robinton that is not the one in dist.ini. It now matches META.json.
    • Documentation only. No module was touched, checked by counting definitions before and after: IP.pm 31/31, Lite.pm 50/50, Util.pm 7/7, InetBase.pm 29/29, UtilPP.pm 28/28, Constants.pm 0/0.
    • Group the IP.pm methods, rewrite its SYNOPSIS, and document netlimit (GH#100, step 3 continued). The Methods section was one flat =over of forty-eight entries, so a reader looking for how to enumerate hosts scanned forty-eight headings. It is now ten =head2 groups, each its own =over:
      • Constructors new new6 new_no new_from_aton new_cis new_cis6 Address and mask addr mask masklen bits version cidr aton Boundaries network broadcast first last range Numeric forms numeric bigint Text forms short canon full full6 full6m prefix nprefix wildcard Containment contains within is_rfc1918 is_local Splitting split splitref rsplit rsplitref Set operations compact compactref coalesce Hosts num nth hostenum hostenumref Regular expressions re re6
    • That reorders entries, so each block was moved whole and checked to have survived verbatim; 48 of 48 did, and the POD item count is unchanged at 63. podchecker needs =head2 outside an =over, so each group is its own list rather than one list with headings inside it.
    • The SYNOPSIS was 113 lines: an import list, every signature, the case policy, the FQDN note, Storable, Compact, Coalesce and netlimit, all of it mixed together. It is now fifteen pasteable lines with results in comments, and the material that was not a synopsis moved to its own IMPORT TAGS section:
      • print $ip->addr, "\n"; # 192.0.2.1 print $ip->mask, "\n"; # 255.255.255.0 print $ip->network, "\n"; # 192.0.2.0/24 print $ip->broadcast, "\n"; # 192.0.2.255/24 print "$ip\n"; # 192.0.2.1/24 my $next = $ip + 5; # 192.0.2.6/24 my @halves = $ip->split(25); # 192.0.2.0/25, 192.0.2.128/25
    • B11, netlimit was documented only inside the SYNOPSIS, which is where the code lived too, and it returns the new limit rather than "true on success". It has a FUNCTIONS section now, and the values are the measured ones:
      • netlimit(20) 1048576 netlimit(16) 65536 netlimit(24) 16777216 netlimit(10) undef, below the default netlimit(25) undef, above the maximum
    • B4 through B7 are documented under the constructor, where the abbreviated accepted-forms list is, with the mask-argument trap spelled out since it is the one that surprises:
      • new('10.1') 10.0.0.1/32 new('10.1', 8) 10.1.0.0/8 new('10.1.2') 10.1.0.2/32 new('10.1.2', 24) 10.1.2.0/24
      • new('192.0.2.4-7') 192.0.2.4/30 new('192.0-3.') 192.0.0.0/14 new('[2001:db8::1]/64') 2001:DB8:0:0:0:0:0:1/64 new('[192.0.2.1]/24') undef
      • new('broadcast') 255.255.255.255/32 new6('broadcast') undef, IPv4 keywords only new('loopback') 127.0.0.1/8, not /32
    • And one correction to what I wrote in the previous commit for this step: moving the SYNOPSIS deleted the netlimit sub and its $_netlimit default, because the code sat between that heading and INSTALLATION and the replacement spanned both. perl -c did not catch it and the suite still passed 2535 tests, since nothing calls netlimit. What caught it was documenting the function and finding NetAddr::IP->can("netlimit") was undef. Restored, in the FUNCTIONS section this time. Every edited file was then checked for lost definitions:
      • IP.pm 31 subs before, 31 after, identical set Lite.pm 50/50, Util.pm 7/7, InetBase.pm 29/29, UtilPP.pm 28/28, Constants.pm 0/0
    • plus the glob aliases, which also match.
    • Fix NetAddr::IP and NetAddr::IP::Lite (GH#100, step 3). The two pages carry the same object API and have drifted, so they go in one commit and the merged text is written once.
    • A4, both said splitref "returns undef when bits ... will not fit". It croaks:
      • NetAddr::IP->new('192.0.2.0/24')->splitref(16);
      • netmask error: overrange or spurious bits
    • A6, the IP.pm SYNOPSIS listed inet_any2d and inetanyto6. Neither exists; they are inet_any2n and ipanyto6, both of which Util has.
    • A11, both new() entries said 'See "STRINGIFICATION" below.' There is no such section on either page.
    • A12, "The first two methods create..." followed by "The third method new_no", after six constructors had been listed. new6FFFF was absent from IP.pm's list altogether and works through inheritance, so the entry now names it and shows the object it makes:
      • NetAddr::IP->new6FFFF('192.0.2.1');
      • 0:0:0:0:0:FFFF:C000:201/128
    • A13 is Util.pm, A17 is below. A19, IP.pm's DESCRIPTION promised NetAddr::IP "will work with older versions of Perl". META.json says 5.014, so that is now stated.
    • A20 and A22 are the drift between the two pages. IP.pm listed two is_local forms where Lite.pm listed four, and Lite.pm's within() carried a sentence about comparing addr() that IP.pm lacked. Both are merged, verified against the code:
      • 127.0.0.1 is_local=1 ::1 is_local=1 ::127.0.0.1 is_local=1 ::ffff:127.0.0.1 is_local=1 ::ffff:10.0.0.1 is_local=0 128.0.0.1 is_local=0
    • The entries are now byte-identical between the two files, which is checkable:
      • is_local() identical mask() identical first() identical within() identical bits() identical last() identical
    • A21, Lite.pm carried the numeric() float-comparison warning and IP.pm did not. It is on both now, with the hazard shown rather than described, because it is silent:
      • $x->numeric == $y->numeric same for 2001:db8::1 and ::2 $x == $y different $x->bigint == $y->bigint different
    • A23, both new_cis item headings had an unbalanced quote.
    • A25, prefix() was documented as including the broadcast address. The range starts at first(), not network():
      • 192.0.2.4/30 -> 192.0.2.5-7 192.0.2.0/24 -> 192.0.2. 192.0.2.0/20 -> 192.0.0-15. 192.0.2.9/32 -> 192.0.2.9
    • A27, both pages warned that comparing objects of differing CIDR "may produce indeterminate - unexpected results". It is deterministic: address first, then the mask as a number. The warning was wrong, and the real caveat is that the order is not the one most people mean by "bigger", so it says that instead and shows the sort order.
    • A28, both listed "a netaddr as returned by 'inet_aton'" among the accepted forms without saying it needs the deprecated :aton tag. Without it a packed string returns undef, which is now stated. The RFC 1884 citations became RFC 4291 s2.2 on both pages.
    • A17, the IP.pm NOTES / BUGS section said in the first person that the author lacked the resources to get the configure step working on Windows and asked for volunteers. It now states the fact and where it comes from, which is that header.pl selects pure Perl when $^O matches /win/i, and points at README.md for the build steps.
    • B1 and B2, the @{} overload and the croaking neg and abs, were undocumented on both pages:
      • scalar @{$ip} 14 for a /28 -$ip cannot negate a NetAddr::IP object abs $ip cannot take the absolute value of a NetAddr::IP object
    • B3, the sort order is now given as part of the comparison entry.
    • C4, version history inside method text is gone from nth() and num() on both pages. Both entries carry measured output instead:
      • /32 nth(0) is the address itself, num 1 /31 nth(0) is the network address, num 2 /30 nth(0) is the first host, num 2 /28 num 14
    • and the C<:old_nth> tag still restores the old behaviour, verified:
      • old_nth 192.0.2.0/30 nth 0 undef, 1..3 the hosts, num 3
    • C9, IP.pm's HISTORY section, which said only "See the Changes file", is removed and the internal link now points at DEPRECATED.
    • Also fixed in passing: "array contest" for "array context" in IP.pm and Lite.pm numeric(), a doubled space in Lite.pm bigint(), and the C3.02 version note in compact().
    • POD only, no code change. Still outstanding from this step: the =head2 method groups, the SYNOPSIS rewrite, and B4 through B12 for the constructor entry, which is where the accepted-forms list lives and is long enough to need more than a correction.
    • Fix NetAddr::IP::InetBase and NetAddr::IP::UtilPP (GH#100, step 2, continued). The two carry copies of the entries fixed in Util.pm, so they go in the same step.
    • A7, the InetBase SYNOPSIS said "use NetAddr::IP::Base qw(...)", and the module is NetAddr::IP::InetBase, so the line as printed could not run. It was also followed by a NetAddr::IP::Util line pasted from Util.pm showing Util's tags, which InetBase does not have. Both are gone; the SYNOPSIS is now InetBase's own nineteen names with measured output, and its four tags are listed with their real contents, recovered by importing each into a scratch package:
      • :ipv4 fillIPv4 inet_aton inet_ntoa, three names :ipv6 eleven names
    • The old list for :ipv6 was missing four of those.
    • A8, InetBase documented ipv4to6. It is neither defined nor exported there; the function belongs to NetAddr::IP::Util, and InetBase->can("ipv4to6") is undef. The entry is removed.
    • A9, InetBase said lower() "is the default" and Util.pm said upper() "is the default", and both were right about themselves and wrong about the reader's program. Measured:
      • InetBase alone: ipv6_n2x = 2001:db8:0:0:0:0:0:1 after InetBase::upper() ipv6_n2x = 2001:DB8:0:0:0:0:0:1 Util loaded: ipv6_n2x = 2001:DB8:0:0:0:0:0:1
    • InetBase defaults to lowercase, and loading Util, Lite or NetAddr::IP imports :upper for you, so in practice uppercase is the default for nearly every program. There is now one section, THE CASE POLICY, that says so once, shows the process-wide behaviour where an unrelated package importing :lower changes the output for everyone else, and points at GH#7 for the open question of what the policy should be. ipv6_ntoa and inet_ntop are called out as always lowercase.
    • Worth recording, since the old entries implied otherwise: neither lower nor upper is importable from either module.
      • NetAddr::IP::InetBase->can("lower") undef NetAddr::IP::Util->can("lower") undef
    • They must be called fully qualified, NetAddr::IP::InetBase::upper(), and the entries now say so.
    • fillIPv4 was documented as converting "127.1 to the long form 127.0.0.1", which is right, but nothing said what it does with anything else, and it is not what a reader would guess. Measured:
      • fillIPv4('192.0.2.1') 192.0.2.1 fillIPv4('192.0.2') 192.0.0.2 fillIPv4('192.0') 192.0.0.0 fillIPv4('10') 0.0.0.10 fillIPv4('example.com') example.com, unchanged fillIPv4('256.1.1.1') undef
    • The argument is text, not a packed address, which is the trap: a packed string does not match, so it comes back unchanged. It is also the BSD inet_aton convention and not RFC 791, which is the note table D asks for, and which belongs here rather than on inet_aton.
    • A14, the missing paren in the hasbits example was in this file too, at UtilPP.pm:111, so it is fixed in both.
    • A15, the UtilPP EXPORT_OK list ended with "threads", which is not a function of that module nor of NetAddr::IP::Util; it never existed. Removed, and the list now cross references NetAddr::IP::Util instead of being a bare list of eighteen names.
    • A18's rfc1844 typo was here as well as in Util.pm, and the two rfc1884 citations became RFC 4291 s2.2.
    • UtilPP gets a DESCRIPTION, which it did not have beyond one sentence. It explains when the module is selected, that the two implementations are meant to be interchangeable and agree down to the error message wording, and that README.md carries the build steps, so no page discusses building.
    • INSTALLATION is removed from InetBase too, per C3.
    • A new section on InetBase, SOCKET6 SUBSTITUTION, names the three symbols that come from Socket6 when it is installed, says the choice is made at load time, and points at GH#99 and GH#21 for where the two paths disagree.
    • POD only, no code change. The grouped =head2 sections the issue asks for across all three function pages, and the table D note about inet_aton short forms, are still outstanding.
    • Fix NetAddr::IP::Util (GH#100, step 2 of the POD overhaul). The SYNOPSIS was a 118 line import list followed by every signature, a table of contents rather than a synopsis; it is now about forty lines that a reader can paste, and every value in it is the measured output rather than a placeholder. INSTALLATION is removed, per the rules, and README.md keeps the build steps. The EXAMPLES section did not compile at all and now runs on both builds.
    • The four defects the issue found in the examples, and what each was:
      • $addr = addconst($addr,$const) scalar context stores the carry, not the sum return ($network, $broadcast) $broadcast never declared, the local is $bcast my $addr = $nip->{addr} missing semicolon my $lastip = $ip-copy() not Perl; stringification copies, "$ip"
    • plus an unbalanced paren in (undef, $ip->{addr} = addconst(. The wrap is now detected with "$ip" lt $before, since the comparison is on the string forms rather than the overloaded operators, and the list context form of addconst takes the second element. The loop is shown wrapping a /30, so the branch is actually exercised:
      • step 3: 192.0.2.252/30 wrapped: 1
    • A1, shiftleft with no count. Util.pm said "a single shift is performed if $n is not specified" and UtilPP.pm said it returns the input. UtilPP was right and both builds agree:
      • shiftleft($bits128) eq $bits128 yes, on XS and pure Perl
    • Both entries now say so, with an example, and note that $n above $MAX_SHIFTLEFT croaks.
    • A2, naip_gethostbyname documented IPv4 answers as ::FFFF:FFFF:d.d.d.d, which is not an address. The answer for 127.0.0.1 is:
      • 0:0:0:0:0:FFFF:7F00:1
    • so the form is ::FFFF:d.d.d.d, the RFC 4291 s2.5.5.2 mapped prefix, which is what the function actually produces on both builds.
    • A3, three entries wrote ::ffff::d.d.d.d, which has one colon too many. Corrected to ::ffff:d.d.d.d, and the isNewIPv4 and isAnyIPv4 entries now name the prefixes they test, s2.5.5.2 and the union of s2.5.5.1 with it.
    • A14, "if (hasbits($bits128 & $mask128) {" was missing its closing paren here and in UtilPP.pm.
    • A16, the EXPORT_OK list omitted seven names that are in @EXPORT_OK: AF_INET, AF_INET6, bin2bcdn, bcdn2txt, bcdn2bin, simple_pack and comp128. All seven are now listed, the last five under a heading that says they are exported for the test suite and are not stable API, which is what the code comments about comp128 said all along. The import tags are now listed with their real contents, measured by importing each into a scratch package, which turned up that :ipv6 is :inet without the two IPv4 names and nothing else, and that the original list for it was missing four.
    • A18, inet_n2ad was documented as returning x:x:x:x:x:x:ddd.ddd.ddd.dd, which has a truncated octet. The real form is x:x:x:x:x:x:ddd.ddd.ddd.ddd, and inet_n2ad(2001:db8::1) is 2001:DB8:0:0:0:0:0.0.0.1. Two rfc1844 typos became RFC 4291 s2.2.
    • A24, "=item $ipv6naddr = inet_4map6($netaddr or $ipv6naddr" was missing its closing paren in the item heading.
    • B9, the :noSock6 import tag, which forces havegethostbyname2 to report false so the resolver fallback can be tested on a host that has Socket6, was undocumented. It has its own section now, labelled a test hook. C7's point that there is no single place listing the tags that change behaviour is partly answered; the case tags live in NetAddr::IP::InetBase and are cross referenced.
    • B10, the split between the functions that return undef on bad input and the ones that croak on a wrong length, now stated in the entries: ipv6_aton, inet_aton and inet_4map6 return undef, while hasbits and isIPv4 croak with "Bad arg length ... length is N, should be 128". B11's netlimit is a NetAddr::IP function rather than one of these, and is left to step 3.
    • The RFC 1884 citations become RFC 4291 s2.2 for the accepted forms and RFC 5952 s4 for the output, with packzeros documented against s4.2.1, s4.2.2 and s4.2.3 separately and an example for each, including that a single zero group is never shortened. Table D's note that inet_aton short forms are the BSD convention rather than RFC 791 is still outstanding; inet_aton('127.1') is 127.0.0.1 and inet_aton('192.168.1') is 192.168.0.1, and inet_aton croaks on '0x7f.1', so it belongs with a rewritten inet_aton entry.
    • Five names for one concept were unified to $bits128 for a 16 byte string and $ipv6naddr for an address, per C5, though the full sweep across all three function pages is part of finishing this step.
    • POD only, no code change. InetBase.pm and UtilPP.pm carry copies of several of these entries and are corrected in the commit that follows.
    • Fix NetAddr::IP::Constants (GH#100, step 1 of the POD overhaul). Unindent the item bodies, which rendered as verbatim blocks under pod2text, group the constants by the RFC they come from, and drop the claim that importing one "ensures that each constant resolves at compile time".
    • That claim is false and the opposite is worth saying plainly, since the constants are read in the hot path of the 128 bit arithmetic. They are our variables, so an assignment writes through to the module's own copy, which is what the rest of the distribution reads:
      • use NetAddr::IP::Constants qw($IPV6_BITS); print "$IPV6_BITS\n"; # 128 $IPV6_BITS = 999; print "$IPV6_BITS\n"; # 999 print "$NetAddr::IP::Constants::IPV6_BITS\n"; # 999
    • A caller who has already declared a name the import wants is not overwritten, because Exporter only installs the alias when the name is free, so overriding is reachable and order dependent. InetBase, Util and UtilPP each import these and read their own copy, so a caller's reassignment changes nothing inside the library. Documented as reachable but unsupported rather than as a supported feature.
    • One more correction the issue did not list. $MAX_BCD_DIGITS was documented as "40 (i.e. ceil(128 * log10(2)))", and that ceiling is 39, not 40:
      • ceil(128*log10(2)) = 39 decimal digits in 2**128 = 39 bin2bcd of all ones = 39 digits, unpadded
    • 40 is right for a different reason. BCD holds one digit per nibble, and a packed string has to be a whole number of bytes, so 39 nibbles round up to 40, which is the 20 bytes $PACKED_BCD_BYTES holds, and simple_pack is what pads to 40 before pack("H40"). The entry now gives that reasoning instead of the false equivalence.
    • $IPV4_OFFSET gained the two section numbers it covers, RFC 4291 s2.5.5.1 for the deprecated compatible prefix and s2.5.5.2 for the mapped prefix, since both sit at that offset and the entry had only the umbrella citation. Table D's note that inet_aton short forms such as 127.1 are the BSD convention and not RFC 791 is left for the Util.pm commit that rewrites the function entries.
    • POD only, no code change. Every example run on both builds.
    • Narrow what canon() claims to follow, and document what it does with an IPv4 address in the low 32 bits (GH#98). The POD said ipV6 output is "as per RFC5952", which overclaims. Sections 4.1 to 4.3 hold: leading zeros dropped, the longest run of zero groups shortened to ::, the first of two equal runs shortened, lowercase, and canon is the lower case of short() on every address tried. Section 5, the mixed form for an embedded IPv4 address, is not applied:
      • canon ->new('::ffff:192.0.2.1') ::ffff:c000:201 canon ->new6FFFF('192.0.2.1') ::ffff:c000:201 canon ->new6('1.2.3.4') ::102:304
    • Deferred to a future decision, GH#98, and this entry takes neither path. Mixed notation for the mapped prefix alone is the narrowest reading of section 5, the one prefix that is both well known and current, and is what Python does; the test is isNewIPv4, which is what distinguishes the mapped prefix from the compatible one, since isIPv4 is also true of ::1 and its mixed form would be ::0.0.0.1. It would change canon for mapped addresses and nothing else, leave addr, short, full and stringification in hex since none of them claims the RFC, and stop canon and short agreeing for those addresses, which the POD would then have to say. Mixed notation for the compatible prefix as well would agree with glibc, and therefore with ipv6_ntoa where Socket6 binds libc, but it would make canon stop agreeing with the ::102:304 that new6 documents, and RFC 4291 deprecates the form. Either way t/114-canon.t gains the mapped cases and keeps ::1 and :: unchanged, and anyone comparing stored canon strings of mapped addresses sees a new value, so this belongs in the series that already changed re, re6 and new(undef).
    • A comment at canon() records the section 5 omission where it happens and points at GH#98.
    • Document that ipv6_ntoa and inet_ntop render an address with an IPv4 address in the low 32 bits differently depending on Socket6 (GH#99). inet_ntop is bound to the platform's libc when Socket6 is installed and to the module's own _inet_ntop otherwise, and Socket6 is a recommendation rather than a requirement. Two hosts differing only in that one optional module therefore render ::ffff:192.0.2.1 as ::ffff:192.0.2.1 and as ::ffff:c000:201. The POD said these functions "come from the Socket6 library if it is present on this host" without saying what follows from it.
    • The table now in the POD is measured, not reasoned. Over 8,040 addresses, a structured set plus random ones weighted towards zero groups, every disagreement between libc and the fallback fell in the mapped prefix ::ffff:0:0/96 or the deprecated compatible prefix ::/96, and nowhere else. Zero run compression, which of two equal runs is shortened, single zero groups, leading zeros and case agree throughout, including on 2001:0:0:1:0:0:0:1, where both shorten the second run.
    • Also correct "takes and IP address" to "an" in the inet_ntop entry.
    • POD only. No method of NetAddr::IP or NetAddr::IP::Lite calls either function; stringification goes through ipv6_n2x and packzeros, which are pure Perl on every host.
    • Deferred to a future decision, GH#99. Two ways out, and this entry takes neither. Bind inet_ntop to the module's own implementation always, and teach the fallback the mixed form for the mapped prefix. That makes the text the same on every host, defined by this module rather than by whichever libc is underneath, and stops a libc rendering it differently from breaking the suite, which is what GH#33 was. The cost is that on a host with Socket6 today ::192.0.2.1 comes back as ::c000:201, so it moves output for existing callers. Or keep libc and teach the fallback to match, which moves nothing today but leaves the text defined by the host's libc, leaves the GH#33 class of failure in place, and forces the compatible prefix to glibc's rendering. That is the opposite of Path B of GH#98, so canon and ipv6_ntoa would disagree on ::192.0.2.1 unless GH#98 takes the same path. Whichever is chosen, the four tests that pin these functions should gain the mapped and compatible cases with one expected value each, true with and without Socket6.
    • A comment at the fallback binding in InetBase.pm now records the divergence where the choice is made, and points at GH#99 and GH#21.
    • Drop docs/rfc1884.txt (GH#97). The full text of RFC 1884, "IP Version 6 Addressing Architecture", December 1995, shipped with 4.001 in August 2006 and moved to docs/ by 4.004 a fortnight later, and nothing has touched it since. It is 37,854 bytes, 3.98% of the unpacked distribution, and the only thing under docs/.
    • Nothing read it. No POD links to it, no test opened it, no code names the path, and the only reference to docs/ anywhere in the tree was the skip line in dist.ini that existed to keep the EOL test away from it. The generated t/author-eol.t lists 185 files and none under docs/, so removing the file and the skip line leaves that test at the same 185 files.
    • RFC 1884 was obsoleted by RFC 2373 in July 1998, which RFC 3513 replaced in April 2003, which RFC 4291 replaced in February 2006. The addressing architecture is RFC 4291, and the text representation rules that :lower and canon() follow are RFC 5952 from August 2010. Shipping the 1995 text beside code that implements the 2006 rules invites a reader to take it for the reference.
    • The ten mentions of rfc1884 in the POD name the specification, not the file, and are unchanged here. They are being replaced with RFC 4291 and RFC 5952 citations in the POD work.
    • The text remains at https://www.rfc-editor.org/rfc/rfc1884 for anyone who wants the history.
    • Give the shipped Makefile.PL a clean and realclean list again (GH#96). The checkout Makefile.PL has listed the generated files since 4.079, but header.pl has not, so the shipped one has had no list at all since 0da215c, and six files survived make realclean: lib/NetAddr/IP/Util.c, lib/NetAddr/IP/Util_IS.pm, xs/config.h, xs/config.log, xs/config.status and xs/localperl.h. Pure Perl left Util_IS.pm behind.
    • The stale xs/config.h was the part that mattered. Configure runs only when that file is missing, so the next perl Makefile.PL in the same tree reused the old results and never probed again, whatever had changed in between.
    • Restore the 4.079 split: make clean removes what Makefile.PL rewrites on every run, which is Util_IS.pm in both builds and localperl.h in the XS build, and make realclean removes the configure results. make clean moves the Makefile aside, so perl Makefile.PL runs again before the next build and puts those three back; the configure results survive until realclean.
    • That required dropping the sub clean override from the footer, which kept lib/NetAddr/IP/Util.c out of make clean on the grounds that "we need this file tracked in git for CPAN distribution". It stopped being tracked at 335f5eb, which untracked it, and 4.080_03 does not ship it, so the override was protecting nothing. The file is not needed in the tree either: the postamble regenerates it from xs/Util.xs through xsubpp, and a make clean followed by perl Makefile.PL and make builds and tests clean.
    • The checkout Makefile.PL loses xs/config.h, xs/config.log and xs/config.status from its clean list for the same reason, so the two copies do not disagree about what a clean build reuses.
    • Verified: XS build then make realclean leaves none of the six; pure Perl then make realclean leaves none; after make clean, Util.c, Util_IS.pm and localperl.h are gone and the three configure results are kept; Util.c comes back on the next make, and the next perl Makefile.PL --xs after a realclean probes again. Both builds 161 files and 2535 tests, all pass.
    • Correct the :nofqdn example in NetAddr::IP's POD (GH#92). The FQDN paragraph under new() told the reader to load NetAddr::IP::Lite, which works, since the flag lives there and both modules read it, but every other tag example on that page imports through NetAddr::IP and a reader of the NetAddr::IP page has no reason to load Lite. Both forms set the same $NetAddr::IP::Lite::NoFQDN, checked here, and t/124-nofqdn.t already covers the tag arriving through NetAddr::IP. POD only.
    • Fix LIBS from configure reaching ExtUtils::MakeMaker by the wrong name (GH#95). header.pl read xs/config.h and then replaced @mm_args[-1], which is depend's hash ref rather than LIBS', so the libraries turned depend into an array ref, LIBS stayed empty, and EUMM died before writing a Makefile: WARNING: depend takes a HASH reference not a ARRAY reference Not a HASH reference at ExtUtils/MakeMaker.pm line 837 Any library configure had to add, which is libnsl or libsocket on illumos and older Solaris, was enough to do it. config.h is only rewritten when it is missing, so the tree then stayed broken until it was deleted by hand. Read the file first and pass the libraries by name now.
    • Stop emitting the xs/config.h make rule, and stop referring to $begin to do it (GH#93). The rule ran xs/configure, a script this distribution has not shipped since 4.069, so it could never succeed; it only stayed out of the Makefile by accident, because the footer referred to $main::begin, which is the package variable of a different thing and is always unset. Every build warned Name "main::begin" used only once: possible typo Use of uninitialized value $begin in concatenation (.) or string and then carried on. No perl makes those fatal. The generated Makefile.PL enables warnings with a plain "use warnings", which [MakeMaker::Awesome] puts there, so both messages printed, the Makefile was written and the suite passed, on every perl in the matrix. Only a caller that imposes fatal warnings on the whole process, as PERL5OPT=-Mwarnings=FATAL does, turns the first one into a build that stops. Remove the reference and the warnings go; remove the rule and the generated Makefile is byte for byte what it was before, and a missing xs/config.h stops the build with make's own No rule to make target 'xs/config.h', needed by 'Util.c' rather than a failed script. The depend entry still lists it as a prerequisite. Makefile.PL itself probes configure.gcc, then configure.cc, then falls back to pure Perl before writing anything, which a make rule cannot repeat.
    • Re-enable the Windows test job, pure Perl only, so the runner needs no C toolchain. It had been disabled long enough that the reason was forgotten, and the reason was three separate things stacked on top of each other:
      • the job ran under a POSIX shell, where "perl" is the Cygwin perl that Git Bash puts on PATH, and it has no ExtUtils::MakeMaker, so the installer reported a core module as missing
      • the flavour of make ExtUtils::MakeMaker writes a Makefile in depends on the perl, 5.24 saying "dmake-style" and 5.26 saying "gmake-style", and neither make works across the matrix
      • the dependency install did not request the develop phase, which the author tests in the distribution need
    • It now installs through cpm via the setup action, which also gets all five author tests' prerequisites, and asks for gmake explicitly with MAKE=gmake so the Makefile flavour does not vary with the perl.
    • The Windows job does not set AUTHOR_TESTING, unlike the others. On that platform the setup action installs dependencies into local/lib/perl5 inside the project directory, and Test::EOF walks the working directory, so the check was reading third-party modules and failing on Data::Binary's trailing newlines. Test::EOF takes no file list and no finder, so it cannot be pointed elsewhere. The five author tests skip themselves without the variable. The four release tests do not, and still run; they need the develop phase, which is still installed, and they pass on Windows.
    • Skip local/ in the other line-ending check as well, the one built on the EOLFiles finder, for the same reason. Both checks walk the tree and neither can be given a file list from dist.ini.
    • Verified on Windows, pure Perl, perl 5.24: 160 files and 3676 tests with the one failure above, and 2531 with it gone and the author tests skipped. t/013-util-binet_pton.t and t/025-util-inet_pton.t, which hard-code the output of inet_ntop, both pass, so the difference in GH#33 is glibc against BSD and does not reach Windows. GH#33 is closed as unreproduced: all forty macOS jobs pass, ten perls from 5.24 across four images, with Socket6 built into each so libc, not the pure Perl fallback, is under test. The tests still assert the rendered string, so a libc rendering these three addresses differently would fail them again.
    • Stop the SYNOPSIS of both modules importing behaviour-changing tags (GH#91). A SYNOPSIS invites pasting, and both lists pulled in every tag the module has. Pasted as written, NetAddr::IP's list warned twice, for :aton and :rfc3021, and NetAddr::IP::Lite's warned once, for :aton. Under "use warnings FATAL => 'all'" both died at compile time, in a program that had only asked for Compact and Zeros.
    • Worse, the tags change behaviour for the whole program rather than returning something. A paste of the NetAddr::IP list also switched on the legacy Storable and nth() behaviour, and its :lower did nothing at all, because import() handles :lower before :upper, so :upper wins whichever order they appear in.
    • Each list now names functions only. NetAddr::IP points at where the tags are documented: :lower, :upper, :old_storable and :nofqdn each have a one-line example where they apply, and :aton and :rfc3021 are under DEPRECATED. Lite.pm is the smaller module and gets no sentence, and its EXPORT_OK drops the tags to match NetAddr::IP's, which lists functions only.
    • The tags themselves are unchanged and still import as before; this is POD only. No test asserted the SYNOPSIS list, and none needed changing.
    • Verified that each list now loads clean under both plain and FATAL warnings, that all twelve tags across the two modules still import, and podchecker is clean on both files.

Changes for version 4.080_03 - 2026-09-30 (TRIAL RELEASE)

  • Reformat the test files with perltidy's default profile. Nine of them were indented with tabs while the other 140 used spaces, and .editorconfig asks for four columns of spaces, so t/ is now internally consistent for the first time. Whitespace is the only thing that moved, except for four semicolons perltidy deletes by default, being ones that are simply empty statements: the one after "sub BASE_LEN () { 7 };" in t/043, and one standing before a closing brace in each of t/087, t/096 and t/097. None of them changes what the code does.
  • Tabs survive in six files, inside comments that use them to line up columns in a table of test vectors. perltidy does not rewrite comment text, which is the right call, so those stay.
  • Checked by stripping all whitespace from each file before and after, which is the only guarantee perltidy actually makes, so those four semicolons are the whole of the difference. The suite passes either way: 160 files and 2927 tests from dzil test --all, and 160 files and 2531 tests in each of the XS and pure Perl built distributions.
  • No .perltidyrc is checked in, so this records the result rather than the settings that produced it, and nothing stops the files drifting apart again.
  • Name an undefined packed argument in the error, instead of warning first and then reporting a length of zero. The guards read their argument with length() and then did arithmetic or a comparison on the result, so an undef reached an operator first and perl warned about a value inside the library, at a line the caller cannot act on, before the check that names the function could run: inet_ntoa(undef) Use of uninitialized value in numeric eq (==), at InetBase.pm line 332 Bad arg length for ...::inet_ntoa, length is should be 4 hasbits(undef) Use of uninitialized value in numeric ne (!=), at UtilPP.pm line 137 Bad arg length for ...::hasbits, length is 0, should be 128 Both halves were wrong. The zero described a length nobody passed, and inet_ntoa interpolated an empty string because length(undef) is the empty string, so its message named the function and the expected width but no length at all. The message now says the length is undefined, and nothing warns on the way to it.
  • Fixed in both implementations, which have to agree: NetAddr::IP::Util is native by default and falls back to UtilPP, and the XS half got there differently. It declares its arguments as SV *, and SvPV turned an undefined argument into an empty string with a length of zero without complaining, so it reported the same misleading zero and perl's "Use of uninitialized value in subroutine entry" warning fired from the caller's line. Each XS function now tests SvOK before it calls SvPV.
  • inet_ntoa and ipv6_ntoa are pure perl with no XS counterpart, so they only needed the guard. ipv6_ntoa was also handing an undefined argument to Socket6::inet_ntop, which reported a length of 0 for something that had none.
  • A wrong but defined length is still measured and reported as before, so "ab" still gives 2 and not undefined.
  • Fix addition and subtraction accepting a constant that is not a number (GH#89). The guard compared $const == int($const), and perl numifies both sides first, so a string passed as its leading digits or as zero:
    • '5abc' added or subtracted 5
    • '0x10' and '0b11' added 0, which reads as no change at all
  • looks_like_number now runs ahead of the guard, and subtraction negates only what is a number so the guard sees the original otherwise. Hex, binary, 0o and empty strings are rejected. Padded and exponent strings, and a Math::BigInt constant, are unaffected. The POD in both IP.pm and Lite.pm said only that the constant must be an integer, and now says a non-numeric string croaks.
  • Keep the documented behaviour for a missing or zero constant, which returns a copy. Nothing tested it before, so the new tests pin it: undef and the empty string still return a copy for both operators, where subtraction previously died inside the negation and addition returned a copy.
  • Fix both SYNOPSIS use lists, which carried their annotations as qw() members, so each module tried to import a symbol named DEPRECATED and Lite.pm one named ! as well (GH#83)
  • Consolidate the scattered deprecation notes in IP.pm and Lite.pm into a DEPRECATED section under EXPORT_OK, and state there that everything listed is going in version 5:
    • the promise was only ever attached to a markdown ###### banner in a POD verbatim block, and in Lite.pm that sat 58 lines into the new_cis6 item body
    • :rfc3021 said only "a future version", and new_cis and new_cis6 carried no removal version at all
    • :rfc3021, new_cis and new_cis6 are now covered by the promise
  • Each item names its replacement. :old_nth is left out, being a compatibility switch that has never been marked deprecated and has no replacement.
  • Warn in the deprecated category when :aton, new_cis or new_cis6 is used, naming the replacement in the warning (GH#49). None of the three warned before, though all were marked deprecated:
    • :aton warns on import in both IP.pm and Lite.pm
    • new_cis and new_cis6 warn on call; they are defined once in Lite.pm and reached from IP.pm via AUTOLOAD
  • Behaviour of all three is unchanged, except under use warnings FATAL => 'all', where the new warning dies; no warnings 'deprecated' silences it. For an import like :aton that silence has to come before the use, since the import runs at BEGIN time.
  • Require Math::BigInt 1.99, the last release before perl 5.14 (GH#44)
  • Remove duplicate NAME section from the Constants.pm POD
  • Adjust Constants.pm POD: name each constant with its sigil and indent descriptions consistently
  • Convert SEE ALSO sections to L<...> links and drop the (3) suffixes
  • Uniform code formatting across lib/, following UtilPP.pm:
    • 4 spaces per indent level, no tabs
    • space after commas, my (...) rather than my(...)
    • =item rather than =item *
    • else and elsif on their own line, not cuddled to the brace
  • POD is unchanged by the reindent; verbatim examples keep their two-space first level.
  • Fix {1,3} regex quantifiers broken by a stray space, which had disabled the mixed hex/dot-quad branch of InetBase::ipv6_aton and the masklen check in coalesce
  • Write the shipped Makefile.PL source as inc/MakeMaker/header.pl and reapply the style pass there, so the tarball no longer ships the old use vars, indent and =item * forms (GH#85)
  • Drop AutoLoader from the prerequisites; nothing loads it since GH#70
  • Drop vars from the prerequisites; generated Util_IS.pm now declares its version with our $VERSION
  • Remove the Math::BigInt emulation switch from Lite.pm (GH#84):
    • _force_bi_emu and $no_mbi_emu are gone, and _loadMBI always runs the require, so bigint() no longer croaks on a host that has Math::BigInt installed
    • drop the stale note on the three known Math::BigInt flavors
  • Document the numeric() comparison hazard in the Lite.pm POD: an ipV6 value has more digits than a Perl number holds, so ==, <=> and sort treat distinct addresses as equal (GH#84)

Changes for version 4.080_02 - 2026-09-29 (TRIAL RELEASE)

  • Fix re6() to support compressed IPv6 addresses (::) and shortened groups (GH#14)
  • Fix bcd2bin/bcdn2bin error messages to show correct function name (GH#78)
  • Add :all export tag to NetAddr::IP::Constants (GH#81)
  • Remove broken _bi_fake fallback in bigint(): Math::BigInt now required for bigint() support, added to RuntimeRecommends (GH#44)
  • Fix SYNOPSIS in IP.pm and Lite.pm (GH#74 items 3-9):
    • Fix within() example, consolidate my $ip declarations
    • Fix prose indentation, typo "Domanin" -> "Domain"
    • Fix unbalanced parenthesis, fix Zeros/Ones/V4mask/V4net description
  • Fix comment in Lite.pm: 0xfffffffe is 2**32 - 2, not 2**32 - 1 (GH#79)
  • Fix module load without resolver: replace inet_aton('255.0.0.0') with pack() (GH#69)
  • Fix Storable hooks: install at file scope, :old_storable is sticky (GH#8)
  • Re-enable tail-call goto dispatches removed in commit 9743c94:
    • IP.pm: splitref/rsplitref -> goto &_splitref
    • UtilPP.pm: addconst/add128/sub128 -> goto &slowadd128
    • UtilPP.pm: bcd2bin -> &_bcdcheck; goto &_bcd2bin
    • UtilPP.pm: bin2bcdn -> goto &_bin2bcdn
    • UtilPP.pm: comp128 restored to return ~ $_[0]
  • Convert @ISA + require Exporter to use parent in all modules
  • Replace use vars with our declarations, consolidate declaration + init
  • Convert tabs to spaces and fix POD indentation in IP.pm and Lite.pm

Changes for version 4.080_01 - 2026-09-22 (TRIAL RELEASE)

  • Validate masklen and number in coalesce, croak on bad arguments (GH#40)
  • Fix coalesce on subclass objects (GH#38)
  • Fix coalesce to count subnet addresses, not usable hosts (GH#37)
  • new6() returns compatible form for IPv4-only hosts, not mapped form (GH#64)
  • Add neg and abs overloads with clear error messages (GH#67)
  • Anchor bracket notation match to reject text outside brackets (GH#68)
  • Fix licence metadata and restore Artistic text (GH#56)
  • new6FFFF returns undef on invalid input instead of unblessed hash ref (GH#65)
  • BREAKING: re() on IPv6 objects in IPv4 space no longer emits a broken regex (GH#34)
  • BREAKING: re6() now returns a non-capturing group (?:...) (GH#50)
  • new6() now prefers AAAA over A for dual-stack hostnames, RT#119858 (GH#32)
  • Deprecate :rfc3021 tag; hostenum/hostenumref return 2 hosts for /31 and /127 unconditionally (GH#26)
  • BREAKING: new(undef) returns undef instead of the default route (GH#29)
  • Fix is_local for IPv4 loopback held in IPv6 objects (GH#27)
  • Fix pure-Perl _bcdcheck error messages (GH#23)
  • Remove dead branch in _splitplan, use named loop variable (GH#18)
  • Add BCD boundary input test suite (GH#51)
  • Preserve caller $@ and $SIG{__DIE__} on oversized decimal in new() (GH#59)
  • Localize $SIG{__DIE__} in module load eval probes, RT#130949 (GH#30)
  • Fix POD spelling mistakes: complier, alway, netowrk, occurence, RT#114239 (GH#47)
  • Prevent re() from matching addresses embedded in longer dotted strings (GH#35)
  • Reject cross-family and non-object arguments in within() and contains() (GH#28)
  • Remove faulty _splitplan pre-check that rejected valid short plans (GH#58)
  • Fix Compact merging v4 with v6 and mutating arguments (GH#15)
  • Fix compactref on subclasses (GH#16)
  • Reject bracket-enclosed IPv4 literals (GH#60)
  • Assert mode() matches Makefile.PL build choice (GH#61)
  • Reject empty string mask in constructor (GH#3)
  • Croak on swapped subtraction constant - object (GH#24)
  • Fix plus() alias for zero and silent no-op outside 32-bit range (GH#25)
  • Detect overflow in decimal integers, return undef in constructor (GH#19)
  • Support [addr]/mask URI bracket notation (GH#2)
  • Fix _splitplan netlimit pre-check and croak (GH#17)
  • Reject non-integer indices in nth() (GH#10)
  • Fix 0b/0x literal parsing using oct() instead of string eval (GH#4)
  • Reject non-ASCII digits in IP parsing
  • Fix version() for large decimal integers, RT#117995 (GH#31)
  • Document coalesce() behavior for equal-length masks (GH#45)
  • Fix bcdn2bin length check to report digits not bytes (GH#53)
  • Make Socket6 an optional recommends, not hard prereq (GH#54)
  • Modernize POD: indirect object syntax and RFC 5737 doc addresses
  • Bump perl requirement to 5.014 and fix Test2 imports (GH#57)
  • Remove Perl 5.10 and 5.12 from CI matrix (GH#57)
  • Consolidate sub-distributions into conventional Dist::Zilla layout
  • Convert all test files to Test2::V1
  • Fix :nofqdn import tag to actually set the flag, RT#111301 (GH#1)

Modules

Manages IPv4 and IPv6 addresses and subnets
Magic number constants for NetAddr::IP
IPv4 and IPv6 address parsing and formatting utilities
Manages IPv4 and IPv6 addresses and subnets (lightweight)
Native C and pure perl implementations of IPv4 and IPv6 address utilities
Pure perl implementations of IPv4 and IPv6 address utilities

Provides

in lib/NetAddr/IP/Util.pm