| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # Net::CIDR | 
| 2 |  |  |  |  |  |  | # | 
| 3 |  |  |  |  |  |  | # Copyright 2001-2019 Sam Varshavchik. | 
| 4 |  |  |  |  |  |  | # | 
| 5 |  |  |  |  |  |  | # with contributions from David Cantrell. | 
| 6 |  |  |  |  |  |  | # | 
| 7 |  |  |  |  |  |  | # This program is free software; you can redistribute it | 
| 8 |  |  |  |  |  |  | # and/or modify it under the same terms as Perl itself. | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | package Net::CIDR; | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | require 5.000; | 
| 13 |  |  |  |  |  |  | #use strict; | 
| 14 |  |  |  |  |  |  | #use warnings; | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | require Exporter; | 
| 17 |  |  |  |  |  |  | # use AutoLoader qw(AUTOLOAD); | 
| 18 | 1 |  |  | 1 |  | 588 | use Carp; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 5635 |  | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | @ISA = qw(Exporter); | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | # Items to export into callers namespace by default. Note: do not export | 
| 23 |  |  |  |  |  |  | # names by default without a very good reason. Use EXPORT_OK instead. | 
| 24 |  |  |  |  |  |  | # Do not simply export all your public functions/methods/constants. | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | # This allows declaration	use Net::CIDR ':all'; | 
| 27 |  |  |  |  |  |  | # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK | 
| 28 |  |  |  |  |  |  | # will save memory. | 
| 29 |  |  |  |  |  |  | %EXPORT_TAGS = ( 'all' => [ qw( range2cidr | 
| 30 |  |  |  |  |  |  | cidr2range | 
| 31 |  |  |  |  |  |  | cidr2octets | 
| 32 |  |  |  |  |  |  | cidradd | 
| 33 |  |  |  |  |  |  | cidrlookup | 
| 34 |  |  |  |  |  |  | cidrvalidate | 
| 35 |  |  |  |  |  |  | addr2cidr | 
| 36 |  |  |  |  |  |  | addrandmask2cidr | 
| 37 |  |  |  |  |  |  | ) ] ); | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | @EXPORT_OK = ( qw( range2cidr | 
| 40 |  |  |  |  |  |  | cidr2range | 
| 41 |  |  |  |  |  |  | cidr2octets | 
| 42 |  |  |  |  |  |  | cidradd | 
| 43 |  |  |  |  |  |  | cidrlookup | 
| 44 |  |  |  |  |  |  | cidrvalidate | 
| 45 |  |  |  |  |  |  | addr2cidr | 
| 46 |  |  |  |  |  |  | addrandmask2cidr | 
| 47 |  |  |  |  |  |  | )); | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | @EXPORT = qw( | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | ); | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | $VERSION = "0.20"; | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | 1; | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | =pod | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | =head1 NAME | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | Net::CIDR - Manipulate IPv4/IPv6 netblocks in CIDR notation | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | use Net::CIDR; | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | use Net::CIDR ':all'; | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | print join("\n", | 
| 71 |  |  |  |  |  |  | Net::CIDR::range2cidr("192.168.0.0-192.168.255.255", | 
| 72 |  |  |  |  |  |  | "10.0.0.0-10.3.255.255")) | 
| 73 |  |  |  |  |  |  | . "\n"; | 
| 74 |  |  |  |  |  |  | # | 
| 75 |  |  |  |  |  |  | # Output from above: | 
| 76 |  |  |  |  |  |  | # | 
| 77 |  |  |  |  |  |  | # 192.168.0.0/16 | 
| 78 |  |  |  |  |  |  | # 10.0.0.0/14 | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | print join("\n", | 
| 81 |  |  |  |  |  |  | Net::CIDR::range2cidr( | 
| 82 |  |  |  |  |  |  | "dead:beef::-dead:beef:ffff:ffff:ffff:ffff:ffff:ffff")) | 
| 83 |  |  |  |  |  |  | . "\n"; | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | # | 
| 86 |  |  |  |  |  |  | # Output from above: | 
| 87 |  |  |  |  |  |  | # | 
| 88 |  |  |  |  |  |  | # dead:beef::/32 | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | print join("\n", | 
| 91 |  |  |  |  |  |  | Net::CIDR::range2cidr("192.168.1.0-192.168.2.255")) | 
| 92 |  |  |  |  |  |  | . "\n"; | 
| 93 |  |  |  |  |  |  | # | 
| 94 |  |  |  |  |  |  | # Output from above: | 
| 95 |  |  |  |  |  |  | # | 
| 96 |  |  |  |  |  |  | # 192.168.1.0/24 | 
| 97 |  |  |  |  |  |  | # 192.168.2.0/24 | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | print join("\n", Net::CIDR::cidr2range("192.168.0.0/16")) . "\n"; | 
| 100 |  |  |  |  |  |  | # | 
| 101 |  |  |  |  |  |  | # Output from above: | 
| 102 |  |  |  |  |  |  | # | 
| 103 |  |  |  |  |  |  | # 192.168.0.0-192.168.255.255 | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | print join("\n", Net::CIDR::cidr2range("dead::beef::/46")) . "\n"; | 
| 106 |  |  |  |  |  |  | # | 
| 107 |  |  |  |  |  |  | # Output from above: | 
| 108 |  |  |  |  |  |  | # | 
| 109 |  |  |  |  |  |  | # dead:beef::-dead:beef:3:ffff:ffff:ffff:ffff:ffff | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | @list=("192.168.0.0/24"); | 
| 112 |  |  |  |  |  |  | @list=Net::CIDR::cidradd("192.168.1.0-192.168.1.255", @list); | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | print join("\n", @list) . "\n"; | 
| 115 |  |  |  |  |  |  | # | 
| 116 |  |  |  |  |  |  | # Output from above: | 
| 117 |  |  |  |  |  |  | # | 
| 118 |  |  |  |  |  |  | # 192.168.0.0/23 | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | print join("\n", Net::CIDR::cidr2octets("192.168.0.0/22")) . "\n"; | 
| 121 |  |  |  |  |  |  | # | 
| 122 |  |  |  |  |  |  | # Output from above: | 
| 123 |  |  |  |  |  |  | # | 
| 124 |  |  |  |  |  |  | # 192.168.0 | 
| 125 |  |  |  |  |  |  | # 192.168.1 | 
| 126 |  |  |  |  |  |  | # 192.168.2 | 
| 127 |  |  |  |  |  |  | # 192.168.3 | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | print join("\n", Net::CIDR::cidr2octets("dead::beef::/46")) . "\n"; | 
| 130 |  |  |  |  |  |  | # | 
| 131 |  |  |  |  |  |  | # Output from above: | 
| 132 |  |  |  |  |  |  | # | 
| 133 |  |  |  |  |  |  | # dead:beef:0000 | 
| 134 |  |  |  |  |  |  | # dead:beef:0001 | 
| 135 |  |  |  |  |  |  | # dead:beef:0002 | 
| 136 |  |  |  |  |  |  | # dead:beef:0003 | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | @list=("192.168.0.0/24"); | 
| 139 |  |  |  |  |  |  | print Net::CIDR::cidrlookup("192.168.0.12", @list); | 
| 140 |  |  |  |  |  |  | # | 
| 141 |  |  |  |  |  |  | # Output from above: | 
| 142 |  |  |  |  |  |  | # | 
| 143 |  |  |  |  |  |  | # 1 | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | @list = Net::CIDR::addr2cidr("192.168.0.31"); | 
| 146 |  |  |  |  |  |  | print join("\n", @list); | 
| 147 |  |  |  |  |  |  | # | 
| 148 |  |  |  |  |  |  | # Output from above: | 
| 149 |  |  |  |  |  |  | # | 
| 150 |  |  |  |  |  |  | # 192.168.0.31/32 | 
| 151 |  |  |  |  |  |  | # 192.168.0.30/31 | 
| 152 |  |  |  |  |  |  | # 192.168.0.28/30 | 
| 153 |  |  |  |  |  |  | # 192.168.0.24/29 | 
| 154 |  |  |  |  |  |  | # 192.168.0.16/28 | 
| 155 |  |  |  |  |  |  | # 192.168.0.0/27 | 
| 156 |  |  |  |  |  |  | # 192.168.0.0/26 | 
| 157 |  |  |  |  |  |  | # 192.168.0.0/25 | 
| 158 |  |  |  |  |  |  | # 192.168.0.0/24 | 
| 159 |  |  |  |  |  |  | # 192.168.0.0/23 | 
| 160 |  |  |  |  |  |  | # [and so on] | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | print Net::CIDR::addrandmask2cidr("195.149.50.61", "255.255.255.248")."\n"; | 
| 163 |  |  |  |  |  |  | # | 
| 164 |  |  |  |  |  |  | # Output from above: | 
| 165 |  |  |  |  |  |  | # | 
| 166 |  |  |  |  |  |  | # 195.149.50.56/29 | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | The Net::CIDR package contains functions that manipulate lists of IP | 
| 171 |  |  |  |  |  |  | netblocks expressed in CIDR notation. | 
| 172 |  |  |  |  |  |  | The Net::CIDR functions handle both IPv4 and IPv6 addresses. | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | =head2 @cidr_list=Net::CIDR::range2cidr(@range_list); | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | Each element in the @range_list is a string "start-finish", where | 
| 177 |  |  |  |  |  |  | "start" is the first IP address and "finish" is the last IP address. | 
| 178 |  |  |  |  |  |  | range2cidr() converts each range into an equivalent CIDR netblock. | 
| 179 |  |  |  |  |  |  | It returns a list of netblocks except in the case where it is given | 
| 180 |  |  |  |  |  |  | only one parameter and is called in scalar context. | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | For example: | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | @a=Net::CIDR::range2cidr("192.168.0.0-192.168.255.255"); | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | The result is a one-element array, with $a[0] being "192.168.0.0/16". | 
| 187 |  |  |  |  |  |  | range2cidr() processes each "start-finish" element in @range_list separately. | 
| 188 |  |  |  |  |  |  | But if invoked like so: | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | $a=Net::CIDR::range2cidr("192.168.0.0-192.168.255.255"); | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | The result is a scalar "192.168.0.0/16". | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | Where each element cannot be expressed as a single CIDR netblock | 
| 195 |  |  |  |  |  |  | range2cidr() will generate as many CIDR netblocks as are necessary to cover | 
| 196 |  |  |  |  |  |  | the full range of IP addresses.  Example: | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | @a=Net::CIDR::range2cidr("192.168.1.0-192.168.2.255"); | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | The result is a two element array: ("192.168.1.0/24","192.168.2.0/24"); | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | @a=Net::CIDR::range2cidr( | 
| 203 |  |  |  |  |  |  | "d08c:43::-d08c:43:ffff:ffff:ffff:ffff:ffff:ffff"); | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | The result is an one element array: ("d08c:43::/32") that reflects this | 
| 206 |  |  |  |  |  |  | IPv6 netblock in CIDR notation. | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | range2cidr() does not merge adjacent or overlapping netblocks in | 
| 209 |  |  |  |  |  |  | @range_list. | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | =head2 @range_list=Net::CIDR::cidr2range(@cidr_list); | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | The cidr2range() functions converts a netblock list in CIDR notation | 
| 214 |  |  |  |  |  |  | to a list of "start-finish" IP address ranges: | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | @a=Net::CIDR::cidr2range("10.0.0.0/14", "192.168.0.0/24"); | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | The result is a two-element array: | 
| 219 |  |  |  |  |  |  | ("10.0.0.0-10.3.255.255", "192.168.0.0-192.168.0.255"). | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | @a=Net::CIDR::cidr2range("d08c:43::/32"); | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | The result is a one-element array: | 
| 224 |  |  |  |  |  |  | ("d08c:43::-d08c:43:ffff:ffff:ffff:ffff:ffff:ffff"). | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | cidr2range() does not merge adjacent or overlapping netblocks in | 
| 227 |  |  |  |  |  |  | @cidr_list. | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | =head2 @netblock_list = Net::CIDR::addr2cidr($address); | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | The addr2cidr function takes an IP address and returns a list of all | 
| 232 |  |  |  |  |  |  | the CIDR netblocks it might belong to: | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | @a=Net::CIDR::addr2cidr('192.168.0.31'); | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | The result is a thirtythree-element array: | 
| 237 |  |  |  |  |  |  | ('192.168.0.31/32', '192.168.0.30/31', '192.168.0.28/30', '192.168.0.24/29', | 
| 238 |  |  |  |  |  |  | [and so on]) | 
| 239 |  |  |  |  |  |  | consisting of all the possible subnets containing this address from | 
| 240 |  |  |  |  |  |  | 0.0.0.0/0 to address/32. | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | Any addresses supplied to addr2cidr after the first will be ignored. | 
| 243 |  |  |  |  |  |  | It works similarly for IPv6 addresses, returning a list of one hundred | 
| 244 |  |  |  |  |  |  | and twenty nine elements. | 
| 245 |  |  |  |  |  |  |  | 
| 246 |  |  |  |  |  |  | =head2 $cidr=Net::CIDR::addrandmask2cidr($address, $netmask); | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | The addrandmask2cidr function takes an IP address and a netmask, and | 
| 249 |  |  |  |  |  |  | returns the CIDR range whose size fits the netmask and which contains | 
| 250 |  |  |  |  |  |  | the address.  It is an error to supply one parameter in IPv4-ish | 
| 251 |  |  |  |  |  |  | format and the other in IPv6-ish format, and it is an error to supply | 
| 252 |  |  |  |  |  |  | a netmask which does not consist solely of 1 bits followed by 0 bits. | 
| 253 |  |  |  |  |  |  | For example, '255.255.248.192' is an invalid netmask, as is | 
| 254 |  |  |  |  |  |  | '255.255.255.32' because both contain 0 bits in between 1 bits. | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | Technically speaking both of those *are* valid netmasks, but a) you'd | 
| 257 |  |  |  |  |  |  | have to be insane to use them, and b) there's no corresponding CIDR | 
| 258 |  |  |  |  |  |  | range. | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | =cut | 
| 261 |  |  |  |  |  |  |  | 
| 262 |  |  |  |  |  |  | # CIDR to start-finish | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | sub cidr2range { | 
| 265 | 12 |  |  | 12 | 1 | 72 | my @cidr=@_; | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 12 |  |  |  |  | 23 | my @r; | 
| 268 |  |  |  |  |  |  |  | 
| 269 | 12 |  |  |  |  | 28 | while ($#cidr >= 0) | 
| 270 |  |  |  |  |  |  | { | 
| 271 | 32 |  |  |  |  | 43 | my $cidr=shift @cidr; | 
| 272 |  |  |  |  |  |  |  | 
| 273 | 32 |  |  |  |  | 63 | $cidr =~ s/\s//g; | 
| 274 |  |  |  |  |  |  |  | 
| 275 | 32 | 100 |  |  |  | 100 | unless ($cidr =~ /(.*)\/(.*)/) | 
| 276 |  |  |  |  |  |  | { | 
| 277 | 10 |  |  |  |  | 19 | push @r, $cidr; | 
| 278 | 10 |  |  |  |  | 23 | next; | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  |  | 
| 281 | 22 |  |  |  |  | 64 | my ($ip, $pfix)=($1, $2); | 
| 282 |  |  |  |  |  |  |  | 
| 283 | 22 |  |  |  |  | 33 | my $isipv6; | 
| 284 |  |  |  |  |  |  |  | 
| 285 | 22 |  |  |  |  | 33 | my @ips=_iptoipa($ip); | 
| 286 |  |  |  |  |  |  |  | 
| 287 | 22 |  |  |  |  | 34 | $isipv6=shift @ips; | 
| 288 |  |  |  |  |  |  |  | 
| 289 | 22 | 50 | 33 |  |  | 140 | croak "$pfix, as in '$cidr', does not make sense" | 
|  |  |  | 33 |  |  |  |  | 
| 290 |  |  |  |  |  |  | unless $pfix >= 0 && $pfix <= ($#ips+1) * 8 && $pfix =~ /^[0-9]+$/; | 
| 291 |  |  |  |  |  |  |  | 
| 292 | 22 |  |  |  |  | 51 | my @rr=_cidr2iprange($pfix, @ips); | 
| 293 |  |  |  |  |  |  |  | 
| 294 | 22 |  |  |  |  | 44 | while ($#rr >= 0) | 
| 295 |  |  |  |  |  |  | { | 
| 296 | 22 |  |  |  |  | 31 | my $a=shift @rr; | 
| 297 | 22 |  |  |  |  | 33 | my $b=shift @rr; | 
| 298 |  |  |  |  |  |  |  | 
| 299 | 22 |  |  |  |  | 37 | $a =~ s/\.$//; | 
| 300 | 22 |  |  |  |  | 29 | $b =~ s/\.$//; | 
| 301 |  |  |  |  |  |  |  | 
| 302 | 22 | 100 |  |  |  | 42 | if ($isipv6) | 
| 303 |  |  |  |  |  |  | { | 
| 304 | 11 |  |  |  |  | 19 | $a=_ipv4to6($a); | 
| 305 | 11 |  |  |  |  | 22 | $b=_ipv4to6($b); | 
| 306 |  |  |  |  |  |  | } | 
| 307 |  |  |  |  |  |  |  | 
| 308 | 22 |  |  |  |  | 104 | push @r, "$a-$b"; | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  | } | 
| 311 |  |  |  |  |  |  |  | 
| 312 | 12 |  |  |  |  | 36 | return @r; | 
| 313 |  |  |  |  |  |  | } | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | # | 
| 316 |  |  |  |  |  |  | # If the input is an IPv6-formatted address, convert it to an IPv4 decimal | 
| 317 |  |  |  |  |  |  | # format, since the other functions know how to deal with it.  The hexadecimal | 
| 318 |  |  |  |  |  |  | # IPv6 address is represented in dotted-decimal form, like IPv4. | 
| 319 |  |  |  |  |  |  | # | 
| 320 |  |  |  |  |  |  |  | 
| 321 |  |  |  |  |  |  | sub _ipv6to4 { | 
| 322 | 87 |  |  | 87 |  | 112 | my $ipv6=shift; | 
| 323 |  |  |  |  |  |  |  | 
| 324 | 87 | 100 |  |  |  | 228 | return (undef, $ipv6) unless $ipv6 =~ /:/; | 
| 325 |  |  |  |  |  |  |  | 
| 326 | 42 | 50 |  |  |  | 129 | croak "Syntax error: $ipv6" | 
| 327 |  |  |  |  |  |  | unless $ipv6 =~ /^[a-fA-F0-9:\.]+$/; | 
| 328 |  |  |  |  |  |  |  | 
| 329 | 42 |  |  |  |  | 67 | my $ip4_suffix=""; | 
| 330 |  |  |  |  |  |  |  | 
| 331 | 42 | 50 |  |  |  | 132 | ($ipv6, $ip4_suffix)=($1, $2) | 
| 332 |  |  |  |  |  |  | if $ipv6 =~ /^(.*:)([0-9]+\.[0-9\.]+)$/; | 
| 333 |  |  |  |  |  |  |  | 
| 334 | 42 |  |  |  |  | 131 | $ipv6 =~ s/([a-fA-F0-9]+)/_h62d($1)/ge; | 
|  | 158 |  |  |  |  | 258 |  | 
| 335 |  |  |  |  |  |  |  | 
| 336 | 42 |  |  |  |  | 83 | my $ipv6_suffix=""; | 
| 337 |  |  |  |  |  |  |  | 
| 338 | 42 | 100 |  |  |  | 140 | if ($ipv6 =~ /(.*)::(.*)/) | 
| 339 |  |  |  |  |  |  | { | 
| 340 | 41 |  |  |  |  | 108 | ($ipv6, $ipv6_suffix)=($1, $2); | 
| 341 | 41 |  |  |  |  | 60 | $ipv6_suffix .= ".$ip4_suffix"; | 
| 342 |  |  |  |  |  |  | } | 
| 343 |  |  |  |  |  |  | else | 
| 344 |  |  |  |  |  |  | { | 
| 345 | 1 |  |  |  |  | 4 | $ipv6 .= ".$ip4_suffix"; | 
| 346 |  |  |  |  |  |  | } | 
| 347 |  |  |  |  |  |  |  | 
| 348 | 42 |  |  |  |  | 282 | my @p=grep (/./, split (/[^0-9]+/, $ipv6)); | 
| 349 |  |  |  |  |  |  |  | 
| 350 | 42 |  |  |  |  | 182 | my @s=grep (/./, split (/[^0-9]+/, $ipv6_suffix)); | 
| 351 |  |  |  |  |  |  |  | 
| 352 | 42 |  |  |  |  | 344 | push @p, 0 while $#p + $#s < 14; | 
| 353 |  |  |  |  |  |  |  | 
| 354 | 42 |  |  |  |  | 158 | my $n=join(".", @p, @s); | 
| 355 |  |  |  |  |  |  |  | 
| 356 |  |  |  |  |  |  | #    return (undef, $1) | 
| 357 |  |  |  |  |  |  | #	if $n =~ /^0\.0\.0\.0\.0\.0\.0\.0\.0\.0\.255\.255\.(.*)$/; | 
| 358 |  |  |  |  |  |  |  | 
| 359 | 42 |  |  |  |  | 154 | return (1, $n); | 
| 360 |  |  |  |  |  |  | } | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | # Let's go the other way around | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | sub _ipv4to6 { | 
| 365 | 796 |  |  | 796 |  | 5470 | my @octets=split(/[^0-9]+/, shift); | 
| 366 |  |  |  |  |  |  |  | 
| 367 | 796 | 50 |  |  |  | 1517 | croak "Internal error in _ipv4to6" | 
| 368 |  |  |  |  |  |  | unless $#octets == 15; | 
| 369 |  |  |  |  |  |  |  | 
| 370 | 796 |  |  |  |  | 2782 | my @dummy=@octets; | 
| 371 |  |  |  |  |  |  |  | 
| 372 | 796 | 50 |  |  |  | 2146 | return ("::ffff:" . join(".", $octets[12], $octets[13], $octets[14], $octets[15])) | 
| 373 |  |  |  |  |  |  | if join(".", splice(@dummy, 0, 12)) eq "0.0.0.0.0.0.0.0.0.0.255.255"; | 
| 374 |  |  |  |  |  |  |  | 
| 375 | 796 |  |  |  |  | 1575 | my @words; | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | my $i; | 
| 378 |  |  |  |  |  |  |  | 
| 379 | 796 |  |  |  |  | 1532 | for ($i=0; $i < 8; $i++) | 
| 380 |  |  |  |  |  |  | { | 
| 381 | 6368 |  |  |  |  | 18036 | $words[$i]=sprintf("%x", $octets[$i*2] * 256 + $octets[$i*2+1]); | 
| 382 |  |  |  |  |  |  | } | 
| 383 |  |  |  |  |  |  |  | 
| 384 | 796 |  |  |  |  | 998 | my $ind= -1; | 
| 385 | 796 |  |  |  |  | 910 | my $indlen= -1; | 
| 386 |  |  |  |  |  |  |  | 
| 387 | 796 |  |  |  |  | 1250 | for ($i=0; $i < 8; $i++) | 
| 388 |  |  |  |  |  |  | { | 
| 389 | 2644 | 100 |  |  |  | 4846 | next unless $words[$i] eq "0"; | 
| 390 |  |  |  |  |  |  |  | 
| 391 | 868 |  |  |  |  | 984 | my $j; | 
| 392 |  |  |  |  |  |  |  | 
| 393 | 868 |  |  |  |  | 1398 | for ($j=$i; $j < 8; $j++) | 
| 394 |  |  |  |  |  |  | { | 
| 395 | 4691 | 100 |  |  |  | 8696 | last if $words[$j] ne "0"; | 
| 396 |  |  |  |  |  |  | } | 
| 397 |  |  |  |  |  |  |  | 
| 398 | 868 | 50 |  |  |  | 1413 | if ($j - $i > $indlen) | 
| 399 |  |  |  |  |  |  | { | 
| 400 | 868 |  |  |  |  | 1023 | $indlen= $j-$i; | 
| 401 | 868 |  |  |  |  | 1308 | $ind=$i; | 
| 402 | 868 |  |  |  |  | 1501 | $i=$j-1; | 
| 403 |  |  |  |  |  |  | } | 
| 404 |  |  |  |  |  |  | } | 
| 405 |  |  |  |  |  |  |  | 
| 406 | 796 | 100 |  |  |  | 1223 | return "::" if $indlen == 8; | 
| 407 |  |  |  |  |  |  |  | 
| 408 | 775 | 100 |  |  |  | 1334 | return join(":", @words) if $ind < 0; | 
| 409 |  |  |  |  |  |  |  | 
| 410 | 758 |  |  |  |  | 1101 | my @s=splice (@words, $ind+$indlen); | 
| 411 |  |  |  |  |  |  |  | 
| 412 | 758 |  |  |  |  | 4325 | return join(":", splice (@words, 0, $ind)) . "::" | 
| 413 |  |  |  |  |  |  | . join(":", @s); | 
| 414 |  |  |  |  |  |  | } | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | # An IP address to an octet list. | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | # Returns a list. First element, flag: true if it was an IPv6 flag. Remaining | 
| 419 |  |  |  |  |  |  | # values are octets. | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | sub _iptoipa { | 
| 422 | 39 |  |  | 39 |  | 57 | my $iparg=shift; | 
| 423 |  |  |  |  |  |  |  | 
| 424 | 39 |  |  |  |  | 56 | my $isipv6; | 
| 425 |  |  |  |  |  |  | my $ip; | 
| 426 |  |  |  |  |  |  |  | 
| 427 | 39 |  |  |  |  | 65 | ($isipv6, $ip)=_ipv6to4($iparg); | 
| 428 |  |  |  |  |  |  |  | 
| 429 | 39 |  |  |  |  | 254 | my @ips= split (/\.+/, $ip); | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | grep { | 
| 432 | 39 | 50 | 33 |  |  | 70 | croak "$_, in $iparg, is not a byte" unless $_ >= 0 && $_ <= 255 && $_ =~ /^[0-9]+$/; | 
|  | 372 |  | 33 |  |  | 1848 |  | 
| 433 |  |  |  |  |  |  | } @ips; | 
| 434 |  |  |  |  |  |  |  | 
| 435 | 39 |  |  |  |  | 138 | return ($isipv6, @ips); | 
| 436 |  |  |  |  |  |  | } | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | sub _h62d { | 
| 439 | 158 |  |  | 158 |  | 244 | my $h=shift; | 
| 440 |  |  |  |  |  |  |  | 
| 441 | 158 |  |  |  |  | 257 | $h=hex("0x$h"); | 
| 442 |  |  |  |  |  |  |  | 
| 443 | 158 |  |  |  |  | 560 | return ( int($h / 256) . "." . ($h % 256)); | 
| 444 |  |  |  |  |  |  | } | 
| 445 |  |  |  |  |  |  |  | 
| 446 |  |  |  |  |  |  | sub _cidr2iprange { | 
| 447 | 139 |  |  | 139 |  | 334 | my @ips=@_; | 
| 448 | 139 |  |  |  |  | 174 | my $pfix=shift @ips; | 
| 449 |  |  |  |  |  |  |  | 
| 450 | 139 | 100 |  |  |  | 217 | if ($pfix == 0) | 
| 451 |  |  |  |  |  |  | { | 
| 452 | 21 |  |  |  |  | 33 | grep { $_=0 } @ips; | 
|  | 92 |  |  |  |  | 130 |  | 
| 453 |  |  |  |  |  |  |  | 
| 454 | 21 |  |  |  |  | 39 | my @ips2=@ips; | 
| 455 |  |  |  |  |  |  |  | 
| 456 | 21 |  |  |  |  | 23 | grep { $_=255 } @ips2; | 
|  | 92 |  |  |  |  | 118 |  | 
| 457 |  |  |  |  |  |  |  | 
| 458 | 21 |  |  |  |  | 104 | return ( join(".", @ips), join(".", @ips2)); | 
| 459 |  |  |  |  |  |  | } | 
| 460 |  |  |  |  |  |  |  | 
| 461 | 118 | 100 |  |  |  | 226 | if ($pfix >= 8) | 
| 462 |  |  |  |  |  |  | { | 
| 463 | 117 |  |  |  |  | 138 | my $octet=shift @ips; | 
| 464 |  |  |  |  |  |  |  | 
| 465 | 117 |  |  |  |  | 240 | @ips=_cidr2iprange($pfix - 8, @ips); | 
| 466 |  |  |  |  |  |  |  | 
| 467 | 117 |  |  |  |  | 190 | grep { $_="$octet.$_"; } @ips; | 
|  | 234 |  |  |  |  | 420 |  | 
| 468 | 117 |  |  |  |  | 248 | return @ips; | 
| 469 |  |  |  |  |  |  | } | 
| 470 |  |  |  |  |  |  |  | 
| 471 | 1 |  |  |  |  | 4 | my $octet=shift @ips; | 
| 472 |  |  |  |  |  |  |  | 
| 473 | 1 |  |  |  |  | 2 | grep { $_=0 } @ips; | 
|  | 10 |  |  |  |  | 14 |  | 
| 474 |  |  |  |  |  |  |  | 
| 475 | 1 |  |  |  |  | 3 | my @ips2=@ips; | 
| 476 |  |  |  |  |  |  |  | 
| 477 | 1 |  |  |  |  | 2 | grep { $_=255 } @ips2; | 
|  | 10 |  |  |  |  | 14 |  | 
| 478 |  |  |  |  |  |  |  | 
| 479 | 1 |  |  |  |  | 5 | my @r= _cidr2range8(($octet, $pfix)); | 
| 480 |  |  |  |  |  |  |  | 
| 481 | 1 |  |  |  |  | 16 | $r[0] = join (".", ($r[0], @ips)); | 
| 482 | 1 |  |  |  |  | 6 | $r[1] = join (".", ($r[1], @ips2)); | 
| 483 |  |  |  |  |  |  |  | 
| 484 | 1 |  |  |  |  | 4 | return @r; | 
| 485 |  |  |  |  |  |  | } | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  | # | 
| 488 |  |  |  |  |  |  | # ADDRESS to list of CIDR netblocks | 
| 489 |  |  |  |  |  |  | # | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | sub addr2cidr { | 
| 492 | 14 |  |  | 14 | 1 | 158 | my @ips=_iptoipa(shift); | 
| 493 |  |  |  |  |  |  |  | 
| 494 | 14 |  |  |  |  | 22 | my $isipv6=shift @ips; | 
| 495 |  |  |  |  |  |  |  | 
| 496 | 14 |  |  |  |  | 20 | my $nbits; | 
| 497 |  |  |  |  |  |  |  | 
| 498 | 14 | 100 |  |  |  | 25 | if ($isipv6) | 
| 499 |  |  |  |  |  |  | { | 
| 500 | 6 | 50 |  |  |  | 12 | croak "An IPv6 address is 16 bytes long" unless $#ips == 15; | 
| 501 | 6 |  |  |  |  | 10 | $nbits=128; | 
| 502 |  |  |  |  |  |  | } | 
| 503 |  |  |  |  |  |  | else | 
| 504 |  |  |  |  |  |  | { | 
| 505 | 8 | 50 |  |  |  | 26 | croak "An IPv4 address is 4 bytes long" unless $#ips == 3; | 
| 506 | 8 |  |  |  |  | 9 | $nbits=32; | 
| 507 |  |  |  |  |  |  | } | 
| 508 |  |  |  |  |  |  |  | 
| 509 | 14 |  |  |  |  | 18 | my @blocks; | 
| 510 |  |  |  |  |  |  |  | 
| 511 | 14 |  |  |  |  | 49 | foreach my $bits (reverse 0..$nbits) | 
| 512 |  |  |  |  |  |  | { | 
| 513 | 1038 |  |  |  |  | 2497 | my @ipcpy=@ips; | 
| 514 |  |  |  |  |  |  |  | 
| 515 | 1038 |  |  |  |  | 1233 | my $n=$bits; | 
| 516 |  |  |  |  |  |  |  | 
| 517 | 1038 |  |  |  |  | 1704 | while ($n < $nbits) | 
| 518 |  |  |  |  |  |  | { | 
| 519 | 7168 |  |  |  |  | 10573 | @ipcpy[$n / 8] &= (0xFF00 >> ($n % 8)); | 
| 520 |  |  |  |  |  |  |  | 
| 521 | 7168 |  |  |  |  | 8095 | $n += 8; | 
| 522 |  |  |  |  |  |  |  | 
| 523 | 7168 |  |  |  |  | 10565 | $n &= 0xF8; | 
| 524 |  |  |  |  |  |  | } | 
| 525 |  |  |  |  |  |  |  | 
| 526 | 1038 |  |  |  |  | 2715 | my $s=join(".", @ipcpy); | 
| 527 |  |  |  |  |  |  |  | 
| 528 | 1038 | 100 |  |  |  | 2183 | push @blocks, ($isipv6 ? _ipv4to6($s):$s) . "/$bits"; | 
| 529 |  |  |  |  |  |  | } | 
| 530 | 14 |  |  |  |  | 203 | return @blocks; | 
| 531 |  |  |  |  |  |  | } | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | # Address and netmask to CIDR | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | sub addrandmask2cidr { | 
| 536 | 0 |  |  | 0 | 1 | 0 | my $address = shift; | 
| 537 | 0 |  |  |  |  | 0 | my($a_isIPv6) = _ipv6to4($address); | 
| 538 | 0 |  |  |  |  | 0 | my($n_isIPv6, $netmask) = _ipv6to4(shift); | 
| 539 | 0 | 0 | 0 |  |  | 0 | die("Both address and netmask must be the same type") | 
|  |  |  | 0 |  |  |  |  | 
| 540 |  |  |  |  |  |  | if( defined($a_isIPv6) && defined($n_isIPv6) && $a_isIPv6 != $n_isIPv6); | 
| 541 | 0 |  |  |  |  | 0 | my $bitsInNetmask = 0; | 
| 542 | 0 |  |  |  |  | 0 | my $previousNMoctet = 255; | 
| 543 | 0 |  |  |  |  | 0 | foreach my $octet (split/\./, $netmask) { | 
| 544 | 0 | 0 | 0 |  |  | 0 | die("Invalid netmask") if($previousNMoctet != 255 && $octet != 0); | 
| 545 | 0 |  |  |  |  | 0 | $previousNMoctet = $octet; | 
| 546 | 0 | 0 |  |  |  | 0 | $bitsInNetmask += | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 547 |  |  |  |  |  |  | ($octet == 255) ? 8 : | 
| 548 |  |  |  |  |  |  | ($octet == 254) ? 7 : | 
| 549 |  |  |  |  |  |  | ($octet == 252) ? 6 : | 
| 550 |  |  |  |  |  |  | ($octet == 248) ? 5 : | 
| 551 |  |  |  |  |  |  | ($octet == 240) ? 4 : | 
| 552 |  |  |  |  |  |  | ($octet == 224) ? 3 : | 
| 553 |  |  |  |  |  |  | ($octet == 192) ? 2 : | 
| 554 |  |  |  |  |  |  | ($octet == 128) ? 1 : | 
| 555 |  |  |  |  |  |  | ($octet == 0) ? 0 : | 
| 556 |  |  |  |  |  |  | die("Invalid netmask"); | 
| 557 |  |  |  |  |  |  | } | 
| 558 | 0 |  |  |  |  | 0 | return (grep { /\/$bitsInNetmask$/ } addr2cidr($address))[0]; | 
|  | 0 |  |  |  |  | 0 |  | 
| 559 |  |  |  |  |  |  | } | 
| 560 |  |  |  |  |  |  |  | 
| 561 |  |  |  |  |  |  | # | 
| 562 |  |  |  |  |  |  | # START-FINISH to CIDR list | 
| 563 |  |  |  |  |  |  | # | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  | sub range2cidr { | 
| 566 | 0 |  |  | 0 | 1 | 0 | my @r=@_; | 
| 567 |  |  |  |  |  |  |  | 
| 568 | 0 |  |  |  |  | 0 | my $i; | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | my @c; | 
| 571 |  |  |  |  |  |  |  | 
| 572 | 0 |  |  |  |  | 0 | for ($i=0; $i <= $#r; $i++) | 
| 573 |  |  |  |  |  |  | { | 
| 574 | 0 |  |  |  |  | 0 | $r[$i] =~ s/\s//g; | 
| 575 |  |  |  |  |  |  |  | 
| 576 | 0 | 0 |  |  |  | 0 | if ($r[$i] =~ /\//) | 
| 577 |  |  |  |  |  |  | { | 
| 578 | 0 |  |  |  |  | 0 | push @c, $r[$i]; | 
| 579 | 0 |  |  |  |  | 0 | next; | 
| 580 |  |  |  |  |  |  | } | 
| 581 |  |  |  |  |  |  |  | 
| 582 | 0 | 0 |  |  |  | 0 | $r[$i]="$r[$i]-$r[$i]" unless $r[$i] =~ /(.*)-(.*)/; | 
| 583 |  |  |  |  |  |  |  | 
| 584 | 0 |  |  |  |  | 0 | $r[$i] =~ /(.*)-(.*)/; | 
| 585 |  |  |  |  |  |  |  | 
| 586 | 0 |  |  |  |  | 0 | my ($a,$b)=($1,$2); | 
| 587 |  |  |  |  |  |  |  | 
| 588 | 0 |  |  |  |  | 0 | my $isipv6_1; | 
| 589 |  |  |  |  |  |  | my $isipv6_2; | 
| 590 |  |  |  |  |  |  |  | 
| 591 | 0 |  |  |  |  | 0 | ($isipv6_1, $a)=_ipv6to4($a); | 
| 592 | 0 |  |  |  |  | 0 | ($isipv6_2, $b)=_ipv6to4($b); | 
| 593 |  |  |  |  |  |  |  | 
| 594 | 0 | 0 | 0 |  |  | 0 | if ($isipv6_1 || $isipv6_2) | 
| 595 |  |  |  |  |  |  | { | 
| 596 | 0 | 0 | 0 |  |  | 0 | croak "Invalid netblock range: $r[$i]" | 
| 597 |  |  |  |  |  |  | unless $isipv6_1 && $isipv6_2; | 
| 598 |  |  |  |  |  |  | } | 
| 599 |  |  |  |  |  |  |  | 
| 600 | 0 |  |  |  |  | 0 | my @a=split(/\.+/, $a); | 
| 601 | 0 |  |  |  |  | 0 | my @b=split(/\.+/, $b); | 
| 602 |  |  |  |  |  |  |  | 
| 603 | 0 | 0 |  |  |  | 0 | croak unless $#a == $#b; | 
| 604 |  |  |  |  |  |  |  | 
| 605 | 0 |  |  |  |  | 0 | my @cc=_range2cidr(\@a, \@b); | 
| 606 |  |  |  |  |  |  |  | 
| 607 | 0 |  |  |  |  | 0 | while ($#cc >= 0) | 
| 608 |  |  |  |  |  |  | { | 
| 609 | 0 |  |  |  |  | 0 | $a=shift @cc; | 
| 610 | 0 |  |  |  |  | 0 | $b=shift @cc; | 
| 611 |  |  |  |  |  |  |  | 
| 612 | 0 | 0 |  |  |  | 0 | $a=_ipv4to6($a) if $isipv6_1; | 
| 613 |  |  |  |  |  |  |  | 
| 614 | 0 |  |  |  |  | 0 | push @c, "$a/$b"; | 
| 615 |  |  |  |  |  |  | } | 
| 616 |  |  |  |  |  |  | } | 
| 617 | 0 | 0 | 0 |  |  | 0 | return @c unless(1==@r && 1==@c && !wantarray()); | 
|  |  |  | 0 |  |  |  |  | 
| 618 | 0 |  |  |  |  | 0 | return $c[0]; | 
| 619 |  |  |  |  |  |  | } | 
| 620 |  |  |  |  |  |  |  | 
| 621 |  |  |  |  |  |  | sub _range2cidr { | 
| 622 | 0 |  |  | 0 |  | 0 | my $a=shift; | 
| 623 | 0 |  |  |  |  | 0 | my $b=shift; | 
| 624 |  |  |  |  |  |  |  | 
| 625 | 0 |  |  |  |  | 0 | my @a=@$a; | 
| 626 | 0 |  |  |  |  | 0 | my @b=@$b; | 
| 627 |  |  |  |  |  |  |  | 
| 628 | 0 |  |  |  |  | 0 | $a=shift @a; | 
| 629 | 0 |  |  |  |  | 0 | $b=shift @b; | 
| 630 |  |  |  |  |  |  |  | 
| 631 | 0 | 0 |  |  |  | 0 | return _range2cidr8($a, $b) if $#a < 0; # Least significant octet pair. | 
| 632 |  |  |  |  |  |  |  | 
| 633 | 0 | 0 | 0 |  |  | 0 | croak "Bad starting address\n" unless $a >= 0 && $a <= 255 && $a =~ /^[0-9]+$/; | 
|  |  |  | 0 |  |  |  |  | 
| 634 | 0 | 0 | 0 |  |  | 0 | croak "Bad ending address\n" unless $b >= 0 && $b <= 255 && $b =~ /^[0-9]+$/ && $b >= $a; | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 635 |  |  |  |  |  |  |  | 
| 636 | 0 |  |  |  |  | 0 | my @c; | 
| 637 |  |  |  |  |  |  |  | 
| 638 | 0 | 0 |  |  |  | 0 | if ($a == $b) # Same start/end octet | 
| 639 |  |  |  |  |  |  | { | 
| 640 | 0 |  |  |  |  | 0 | my @cc= _range2cidr(\@a, \@b); | 
| 641 |  |  |  |  |  |  |  | 
| 642 | 0 |  |  |  |  | 0 | while ($#cc >= 0) | 
| 643 |  |  |  |  |  |  | { | 
| 644 | 0 |  |  |  |  | 0 | my $c=shift @cc; | 
| 645 |  |  |  |  |  |  |  | 
| 646 | 0 |  |  |  |  | 0 | push @c, "$a.$c"; | 
| 647 |  |  |  |  |  |  |  | 
| 648 | 0 |  |  |  |  | 0 | $c=shift @cc; | 
| 649 | 0 |  |  |  |  | 0 | push @c, $c+8; | 
| 650 |  |  |  |  |  |  | } | 
| 651 | 0 |  |  |  |  | 0 | return @c; | 
| 652 |  |  |  |  |  |  | } | 
| 653 |  |  |  |  |  |  |  | 
| 654 | 0 |  |  |  |  | 0 | my $start0=1; | 
| 655 | 0 |  |  |  |  | 0 | my $end255=1; | 
| 656 |  |  |  |  |  |  |  | 
| 657 | 0 | 0 |  |  |  | 0 | grep { $start0=0 unless $_ == 0; } @a; | 
|  | 0 |  |  |  |  | 0 |  | 
| 658 | 0 | 0 |  |  |  | 0 | grep { $end255=0 unless $_ == 255; } @b; | 
|  | 0 |  |  |  |  | 0 |  | 
| 659 |  |  |  |  |  |  |  | 
| 660 | 0 | 0 |  |  |  | 0 | if ( ! $start0 ) | 
| 661 |  |  |  |  |  |  | { | 
| 662 | 0 |  |  |  |  | 0 | my @bcopy=@b; | 
| 663 |  |  |  |  |  |  |  | 
| 664 | 0 |  |  |  |  | 0 | grep { $_=255 } @bcopy; | 
|  | 0 |  |  |  |  | 0 |  | 
| 665 |  |  |  |  |  |  |  | 
| 666 | 0 |  |  |  |  | 0 | my @cc= _range2cidr(\@a, \@bcopy); | 
| 667 |  |  |  |  |  |  |  | 
| 668 | 0 |  |  |  |  | 0 | while ($#cc >= 0) | 
| 669 |  |  |  |  |  |  | { | 
| 670 | 0 |  |  |  |  | 0 | my $c=shift @cc; | 
| 671 |  |  |  |  |  |  |  | 
| 672 | 0 |  |  |  |  | 0 | push @c, "$a.$c"; | 
| 673 |  |  |  |  |  |  |  | 
| 674 | 0 |  |  |  |  | 0 | $c=shift @cc; | 
| 675 | 0 |  |  |  |  | 0 | push @c, $c + 8; | 
| 676 |  |  |  |  |  |  | } | 
| 677 |  |  |  |  |  |  |  | 
| 678 | 0 |  |  |  |  | 0 | ++$a; | 
| 679 |  |  |  |  |  |  | } | 
| 680 |  |  |  |  |  |  |  | 
| 681 | 0 | 0 |  |  |  | 0 | if ( ! $end255 ) | 
| 682 |  |  |  |  |  |  | { | 
| 683 | 0 |  |  |  |  | 0 | my @acopy=@a; | 
| 684 |  |  |  |  |  |  |  | 
| 685 | 0 |  |  |  |  | 0 | grep { $_=0 } @acopy; | 
|  | 0 |  |  |  |  | 0 |  | 
| 686 |  |  |  |  |  |  |  | 
| 687 | 0 |  |  |  |  | 0 | my @cc= _range2cidr(\@acopy, \@b); | 
| 688 |  |  |  |  |  |  |  | 
| 689 | 0 |  |  |  |  | 0 | while ($#cc >= 0) | 
| 690 |  |  |  |  |  |  | { | 
| 691 | 0 |  |  |  |  | 0 | my $c=shift @cc; | 
| 692 |  |  |  |  |  |  |  | 
| 693 | 0 |  |  |  |  | 0 | push @c, "$b.$c"; | 
| 694 |  |  |  |  |  |  |  | 
| 695 | 0 |  |  |  |  | 0 | $c=shift @cc; | 
| 696 | 0 |  |  |  |  | 0 | push @c, $c + 8; | 
| 697 |  |  |  |  |  |  | } | 
| 698 |  |  |  |  |  |  |  | 
| 699 | 0 |  |  |  |  | 0 | --$b; | 
| 700 |  |  |  |  |  |  | } | 
| 701 |  |  |  |  |  |  |  | 
| 702 | 0 | 0 |  |  |  | 0 | if ($a <= $b) | 
| 703 |  |  |  |  |  |  | { | 
| 704 | 0 |  |  |  |  | 0 | grep { $_=0 } @a; | 
|  | 0 |  |  |  |  | 0 |  | 
| 705 |  |  |  |  |  |  |  | 
| 706 | 0 |  |  |  |  | 0 | my $pfix=join(".", @a); | 
| 707 |  |  |  |  |  |  |  | 
| 708 | 0 |  |  |  |  | 0 | my @cc= _range2cidr8($a, $b); | 
| 709 |  |  |  |  |  |  |  | 
| 710 | 0 |  |  |  |  | 0 | while ($#cc >= 0) | 
| 711 |  |  |  |  |  |  | { | 
| 712 | 0 |  |  |  |  | 0 | my $c=shift @cc; | 
| 713 |  |  |  |  |  |  |  | 
| 714 | 0 |  |  |  |  | 0 | push @c, "$c.$pfix"; | 
| 715 |  |  |  |  |  |  |  | 
| 716 | 0 |  |  |  |  | 0 | $c=shift @cc; | 
| 717 | 0 |  |  |  |  | 0 | push @c, $c; | 
| 718 |  |  |  |  |  |  | } | 
| 719 |  |  |  |  |  |  | } | 
| 720 | 0 |  |  |  |  | 0 | return @c; | 
| 721 |  |  |  |  |  |  | } | 
| 722 |  |  |  |  |  |  |  | 
| 723 |  |  |  |  |  |  | sub _range2cidr8 { | 
| 724 |  |  |  |  |  |  |  | 
| 725 | 0 |  |  | 0 |  | 0 | my @c; | 
| 726 |  |  |  |  |  |  |  | 
| 727 | 0 |  |  |  |  | 0 | my @r=@_; | 
| 728 |  |  |  |  |  |  |  | 
| 729 | 0 |  |  |  |  | 0 | while ($#r >= 0) | 
| 730 |  |  |  |  |  |  | { | 
| 731 | 0 |  |  |  |  | 0 | my $a=shift @r; | 
| 732 | 0 |  |  |  |  | 0 | my $b=shift @r; | 
| 733 |  |  |  |  |  |  |  | 
| 734 | 0 | 0 | 0 |  |  | 0 | croak "Bad starting address\n" unless $a >= 0 && $a <= 255 && $a =~ /^[0-9]+$/; | 
|  |  |  | 0 |  |  |  |  | 
| 735 | 0 | 0 | 0 |  |  | 0 | croak "Bad ending address\n" unless $b >= 0 && $b <= 255 && $b =~ /^[0-9]+$/ && $b >= $a; | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 736 |  |  |  |  |  |  |  | 
| 737 | 0 |  |  |  |  | 0 | ++$b; | 
| 738 |  |  |  |  |  |  |  | 
| 739 | 0 |  |  |  |  | 0 | while ($a < $b) | 
| 740 |  |  |  |  |  |  | { | 
| 741 | 0 |  |  |  |  | 0 | my $i=0; | 
| 742 | 0 |  |  |  |  | 0 | my $n=1; | 
| 743 |  |  |  |  |  |  |  | 
| 744 | 0 |  |  |  |  | 0 | while ( ($n & $a) == 0) | 
| 745 |  |  |  |  |  |  | { | 
| 746 | 0 |  |  |  |  | 0 | ++$i; | 
| 747 | 0 |  |  |  |  | 0 | $n <<= 1; | 
| 748 | 0 | 0 |  |  |  | 0 | last if $i >= 8; | 
| 749 |  |  |  |  |  |  | } | 
| 750 |  |  |  |  |  |  |  | 
| 751 | 0 |  | 0 |  |  | 0 | while ($i && $n + $a > $b) | 
| 752 |  |  |  |  |  |  | { | 
| 753 | 0 |  |  |  |  | 0 | --$i; | 
| 754 | 0 |  |  |  |  | 0 | $n >>= 1; | 
| 755 |  |  |  |  |  |  | } | 
| 756 |  |  |  |  |  |  |  | 
| 757 | 0 |  |  |  |  | 0 | push @c, $a; | 
| 758 | 0 |  |  |  |  | 0 | push @c, 8-$i; | 
| 759 |  |  |  |  |  |  |  | 
| 760 | 0 |  |  |  |  | 0 | $a += $n; | 
| 761 |  |  |  |  |  |  | } | 
| 762 |  |  |  |  |  |  | } | 
| 763 |  |  |  |  |  |  |  | 
| 764 | 0 |  |  |  |  | 0 | return @c; | 
| 765 |  |  |  |  |  |  | } | 
| 766 |  |  |  |  |  |  |  | 
| 767 |  |  |  |  |  |  | sub _cidr2range8 { | 
| 768 |  |  |  |  |  |  |  | 
| 769 | 3 |  |  | 3 |  | 7 | my @c=@_; | 
| 770 |  |  |  |  |  |  |  | 
| 771 | 3 |  |  |  |  | 4 | my @r; | 
| 772 |  |  |  |  |  |  |  | 
| 773 | 3 |  |  |  |  | 7 | while ($#c >= 0) | 
| 774 |  |  |  |  |  |  | { | 
| 775 | 3 |  |  |  |  | 4 | my $a=shift @c; | 
| 776 | 3 |  |  |  |  | 5 | my $b=shift @c; | 
| 777 |  |  |  |  |  |  |  | 
| 778 | 3 | 50 | 33 |  |  | 20 | croak "Bad starting address" unless $a >= 0 && $a <= 255 && $a =~ /^[0-9]+$/; | 
|  |  |  | 33 |  |  |  |  | 
| 779 | 3 | 50 | 33 |  |  | 31 | croak "Bad ending address" unless $b >= 0 && $b <= 8 && $b =~ /^[0-9]+$/; | 
|  |  |  | 33 |  |  |  |  | 
| 780 |  |  |  |  |  |  |  | 
| 781 | 3 |  |  |  |  | 7 | my $n= 1 << (8-$b); | 
| 782 |  |  |  |  |  |  |  | 
| 783 | 3 |  |  |  |  | 24 | $a &= ($n-1) ^ 255; | 
| 784 |  |  |  |  |  |  |  | 
| 785 | 3 |  |  |  |  | 8 | push @r, $a; | 
| 786 | 3 |  |  |  |  | 8 | push @r, $a + ($n-1); | 
| 787 |  |  |  |  |  |  | } | 
| 788 | 3 |  |  |  |  | 9 | return @r; | 
| 789 |  |  |  |  |  |  | } | 
| 790 |  |  |  |  |  |  |  | 
| 791 |  |  |  |  |  |  | sub _ipcmp { | 
| 792 | 24 |  |  | 24 |  | 32 | my $aa=shift; | 
| 793 | 24 |  |  |  |  | 31 | my $bb=shift; | 
| 794 |  |  |  |  |  |  |  | 
| 795 | 24 |  |  |  |  | 31 | my $isipv6_1; | 
| 796 |  |  |  |  |  |  | my $isipv6_2; | 
| 797 |  |  |  |  |  |  |  | 
| 798 | 24 |  |  |  |  | 40 | ($isipv6_1, $aa)=_ipv6to4($aa); | 
| 799 | 24 |  |  |  |  | 42 | ($isipv6_2, $bb)=_ipv6to4($bb); | 
| 800 |  |  |  |  |  |  |  | 
| 801 | 24 |  |  |  |  | 70 | my @a=split (/\./, $aa); | 
| 802 | 24 |  |  |  |  | 62 | my @b=split (/\./, $bb); | 
| 803 |  |  |  |  |  |  |  | 
| 804 | 24 | 100 |  |  |  | 58 | unshift @a, (0,0,0,0,0,0,0,0,0,0,255,255) | 
| 805 |  |  |  |  |  |  | unless $isipv6_1; | 
| 806 |  |  |  |  |  |  |  | 
| 807 | 24 | 100 |  |  |  | 44 | unshift @b, (0,0,0,0,0,0,0,0,0,0,255,255) | 
| 808 |  |  |  |  |  |  | unless $isipv6_2; | 
| 809 |  |  |  |  |  |  |  | 
| 810 | 24 | 50 |  |  |  | 46 | croak "Different number of octets in IP addresses" unless $#a == $#b; | 
| 811 |  |  |  |  |  |  |  | 
| 812 | 24 |  | 66 |  |  | 95 | while ($#a >= 0 && $a[0] == $b[0]) | 
| 813 |  |  |  |  |  |  | { | 
| 814 | 163 |  |  |  |  | 183 | shift @a; | 
| 815 | 163 |  |  |  |  | 413 | shift @b; | 
| 816 |  |  |  |  |  |  | } | 
| 817 |  |  |  |  |  |  |  | 
| 818 | 24 | 50 |  |  |  | 45 | return 0 if $#a < 0; | 
| 819 |  |  |  |  |  |  |  | 
| 820 | 24 |  |  |  |  | 100 | return $a[0] <=> $b[0]; | 
| 821 |  |  |  |  |  |  | } | 
| 822 |  |  |  |  |  |  |  | 
| 823 |  |  |  |  |  |  |  | 
| 824 |  |  |  |  |  |  | =pod | 
| 825 |  |  |  |  |  |  |  | 
| 826 |  |  |  |  |  |  | =head2 @octet_list=Net::CIDR::cidr2octets(@cidr_list); | 
| 827 |  |  |  |  |  |  |  | 
| 828 |  |  |  |  |  |  | cidr2octets() takes @cidr_list and returns a list of leading octets | 
| 829 |  |  |  |  |  |  | representing those netblocks.  Example: | 
| 830 |  |  |  |  |  |  |  | 
| 831 |  |  |  |  |  |  | @octet_list=Net::CIDR::cidr2octets("10.0.0.0/14", "192.168.0.0/24"); | 
| 832 |  |  |  |  |  |  |  | 
| 833 |  |  |  |  |  |  | The result is the following five-element array: | 
| 834 |  |  |  |  |  |  | ("10.0", "10.1", "10.2", "10.3", "192.168.0"). | 
| 835 |  |  |  |  |  |  |  | 
| 836 |  |  |  |  |  |  | For IPv6 addresses, the hexadecimal words in the resulting list are | 
| 837 |  |  |  |  |  |  | zero-padded: | 
| 838 |  |  |  |  |  |  |  | 
| 839 |  |  |  |  |  |  | @octet_list=Net::CIDR::cidr2octets("::dead:beef:0:0/110"); | 
| 840 |  |  |  |  |  |  |  | 
| 841 |  |  |  |  |  |  | The result is a four-element array: | 
| 842 |  |  |  |  |  |  | ("0000:0000:0000:0000:dead:beef:0000", | 
| 843 |  |  |  |  |  |  | "0000:0000:0000:0000:dead:beef:0001", | 
| 844 |  |  |  |  |  |  | "0000:0000:0000:0000:dead:beef:0002", | 
| 845 |  |  |  |  |  |  | "0000:0000:0000:0000:dead:beef:0003"). | 
| 846 |  |  |  |  |  |  | Prefixes of IPv6 CIDR blocks should be even multiples of 16 bits, otherwise | 
| 847 |  |  |  |  |  |  | they can potentially expand out to a 32,768-element array, each! | 
| 848 |  |  |  |  |  |  |  | 
| 849 |  |  |  |  |  |  | =cut | 
| 850 |  |  |  |  |  |  |  | 
| 851 |  |  |  |  |  |  | sub cidr2octets { | 
| 852 | 2 |  |  | 2 | 1 | 61 | my @cidr=@_; | 
| 853 |  |  |  |  |  |  |  | 
| 854 | 2 |  |  |  |  | 4 | my @r; | 
| 855 |  |  |  |  |  |  |  | 
| 856 | 2 |  |  |  |  | 7 | while ($#cidr >= 0) | 
| 857 |  |  |  |  |  |  | { | 
| 858 | 3 |  |  |  |  | 6 | my $cidr=shift @cidr; | 
| 859 |  |  |  |  |  |  |  | 
| 860 | 3 |  |  |  |  | 9 | $cidr =~ s/\s//g; | 
| 861 |  |  |  |  |  |  |  | 
| 862 | 3 | 50 |  |  |  | 14 | croak "CIDR doesn't look like a CIDR\n" unless ($cidr =~ /(.*)\/(.*)/); | 
| 863 |  |  |  |  |  |  |  | 
| 864 | 3 |  |  |  |  | 12 | my ($ip, $pfix)=($1, $2); | 
| 865 |  |  |  |  |  |  |  | 
| 866 | 3 |  |  |  |  | 5 | my $isipv6; | 
| 867 |  |  |  |  |  |  |  | 
| 868 | 3 |  |  |  |  | 6 | my @ips=_iptoipa($ip); | 
| 869 |  |  |  |  |  |  |  | 
| 870 | 3 |  |  |  |  | 6 | $isipv6=shift @ips; | 
| 871 |  |  |  |  |  |  |  | 
| 872 | 3 | 50 | 33 |  |  | 38 | croak "$pfix, as in '$cidr', does not make sense" | 
|  |  |  | 33 |  |  |  |  | 
| 873 |  |  |  |  |  |  | unless $pfix >= 0 && $pfix <= ($#ips+1) * 8 && $pfix =~ /^[0-9]+$/; | 
| 874 |  |  |  |  |  |  |  | 
| 875 | 3 |  |  |  |  | 6 | my $i; | 
| 876 |  |  |  |  |  |  |  | 
| 877 | 3 |  |  |  |  | 7 | for ($i=0; $i <= $#ips; $i++) | 
| 878 |  |  |  |  |  |  | { | 
| 879 | 20 | 100 |  |  |  | 44 | last if $pfix - $i * 8 < 8; | 
| 880 |  |  |  |  |  |  | } | 
| 881 |  |  |  |  |  |  |  | 
| 882 | 3 |  |  |  |  | 8 | my @msb=splice @ips, 0, $i; | 
| 883 |  |  |  |  |  |  |  | 
| 884 | 3 |  |  |  |  | 5 | my $bitsleft= $pfix - $i * 8; | 
| 885 |  |  |  |  |  |  |  | 
| 886 | 3 | 100 | 66 |  |  | 12 | if ($#ips < 0 || $bitsleft == 0) | 
| 887 |  |  |  |  |  |  | { | 
| 888 | 1 | 50 | 33 |  |  | 5 | if ($pfix == 0 && $bitsleft == 0) | 
|  |  | 50 |  |  |  |  |  | 
| 889 |  |  |  |  |  |  | { | 
| 890 | 0 |  |  |  |  | 0 | foreach (0..255) | 
| 891 |  |  |  |  |  |  | { | 
| 892 | 0 |  |  |  |  | 0 | my @n=($_); | 
| 893 |  |  |  |  |  |  |  | 
| 894 | 0 | 0 |  |  |  | 0 | if ($isipv6) | 
| 895 |  |  |  |  |  |  | { | 
| 896 | 0 |  |  |  |  | 0 | _push_ipv6_octets(\@r, \@n); | 
| 897 |  |  |  |  |  |  | } | 
| 898 |  |  |  |  |  |  | else | 
| 899 |  |  |  |  |  |  | { | 
| 900 | 0 |  |  |  |  | 0 | push @r, $n[0]; | 
| 901 |  |  |  |  |  |  | } | 
| 902 |  |  |  |  |  |  | } | 
| 903 |  |  |  |  |  |  | } | 
| 904 |  |  |  |  |  |  | elsif ($isipv6) | 
| 905 |  |  |  |  |  |  | { | 
| 906 | 0 |  |  |  |  | 0 | _push_ipv6_octets(\@r, \@msb); | 
| 907 |  |  |  |  |  |  | } | 
| 908 |  |  |  |  |  |  | else | 
| 909 |  |  |  |  |  |  | { | 
| 910 | 1 |  |  |  |  | 4 | push @r, join(".", @msb); | 
| 911 |  |  |  |  |  |  | } | 
| 912 | 1 |  |  |  |  | 3 | next; | 
| 913 |  |  |  |  |  |  | } | 
| 914 |  |  |  |  |  |  |  | 
| 915 | 2 |  |  |  |  | 6 | my @rr=_cidr2range8(($ips[0], $bitsleft)); | 
| 916 |  |  |  |  |  |  |  | 
| 917 | 2 |  |  |  |  | 5 | while ($#rr >= 0) | 
| 918 |  |  |  |  |  |  | { | 
| 919 | 2 |  |  |  |  | 4 | my $a=shift @rr; | 
| 920 | 2 |  |  |  |  | 2 | my $b=shift @rr; | 
| 921 |  |  |  |  |  |  |  | 
| 922 |  |  |  |  |  |  | grep { | 
| 923 | 2 | 100 |  |  |  | 5 | if ($isipv6) | 
|  | 8 |  |  |  |  | 13 |  | 
| 924 |  |  |  |  |  |  | { | 
| 925 | 4 |  |  |  |  | 6 | push @msb, $_; | 
| 926 | 4 |  |  |  |  | 10 | _push_ipv6_octets(\@r, \@msb); | 
| 927 | 4 |  |  |  |  | 10 | pop @msb; | 
| 928 |  |  |  |  |  |  | } | 
| 929 |  |  |  |  |  |  | else | 
| 930 |  |  |  |  |  |  | { | 
| 931 | 4 |  |  |  |  | 14 | push @r, join(".", (@msb, $_)); | 
| 932 |  |  |  |  |  |  | } | 
| 933 |  |  |  |  |  |  | } ($a .. $b); | 
| 934 |  |  |  |  |  |  | } | 
| 935 |  |  |  |  |  |  | } | 
| 936 |  |  |  |  |  |  |  | 
| 937 | 2 |  |  |  |  | 10 | return @r; | 
| 938 |  |  |  |  |  |  | } | 
| 939 |  |  |  |  |  |  |  | 
| 940 |  |  |  |  |  |  | sub _push_ipv6_octets { | 
| 941 | 4 |  |  | 4 |  | 5 | my $ary_ref=shift; | 
| 942 | 4 |  |  |  |  | 5 | my $octets=shift; | 
| 943 |  |  |  |  |  |  |  | 
| 944 | 4 | 50 |  |  |  | 5 | if ( ($#{$octets} % 2) == 0)	# Odd number of octets | 
|  | 4 |  |  |  |  | 10 |  | 
| 945 |  |  |  |  |  |  | { | 
| 946 | 0 |  |  |  |  | 0 | foreach (0 .. 255) | 
| 947 |  |  |  |  |  |  | { | 
| 948 | 0 |  |  |  |  | 0 | push @$octets, $_; | 
| 949 | 0 |  |  |  |  | 0 | _push_ipv6_octets($ary_ref, $octets); | 
| 950 | 0 |  |  |  |  | 0 | pop @$octets; | 
| 951 |  |  |  |  |  |  | } | 
| 952 | 0 |  |  |  |  | 0 | return; | 
| 953 |  |  |  |  |  |  | } | 
| 954 |  |  |  |  |  |  |  | 
| 955 | 4 |  |  |  |  | 4 | my $i; | 
| 956 | 4 |  |  |  |  | 6 | my $s=""; | 
| 957 |  |  |  |  |  |  |  | 
| 958 | 4 |  |  |  |  | 8 | for ($i=0; $i <= $#{$octets}; $i += 2) | 
|  | 32 |  |  |  |  | 56 |  | 
| 959 |  |  |  |  |  |  | { | 
| 960 | 28 | 100 |  |  |  | 43 | $s .= ":" if $s ne ""; | 
| 961 | 28 |  |  |  |  | 62 | $s .= sprintf("%02x%02x", $$octets[$i], $$octets[$i+1]); | 
| 962 |  |  |  |  |  |  | } | 
| 963 | 4 |  |  |  |  | 8 | push @$ary_ref, $s; | 
| 964 |  |  |  |  |  |  | } | 
| 965 |  |  |  |  |  |  |  | 
| 966 |  |  |  |  |  |  | =pod | 
| 967 |  |  |  |  |  |  |  | 
| 968 |  |  |  |  |  |  | =head2 @cidr_list=Net::CIDR::cidradd($block, @cidr_list); | 
| 969 |  |  |  |  |  |  |  | 
| 970 |  |  |  |  |  |  | The cidradd() functions allows a CIDR list to be built one CIDR netblock | 
| 971 |  |  |  |  |  |  | at a time, merging adjacent and overlapping ranges. | 
| 972 |  |  |  |  |  |  | $block is a single netblock, expressed as either "start-finish", or | 
| 973 |  |  |  |  |  |  | "address/prefix". | 
| 974 |  |  |  |  |  |  | Example: | 
| 975 |  |  |  |  |  |  |  | 
| 976 |  |  |  |  |  |  | @cidr_list=Net::CIDR::range2cidr("192.168.0.0-192.168.0.255"); | 
| 977 |  |  |  |  |  |  | @cidr_list=Net::CIDR::cidradd("10.0.0.0/8", @cidr_list); | 
| 978 |  |  |  |  |  |  | @cidr_list=Net::CIDR::cidradd("192.168.1.0-192.168.1.255", @cidr_list); | 
| 979 |  |  |  |  |  |  |  | 
| 980 |  |  |  |  |  |  | The result is a two-element array: ("10.0.0.0/8", "192.168.0.0/23"). | 
| 981 |  |  |  |  |  |  | IPv6 addresses are handled in an analogous fashion. | 
| 982 |  |  |  |  |  |  |  | 
| 983 |  |  |  |  |  |  | =cut | 
| 984 |  |  |  |  |  |  |  | 
| 985 |  |  |  |  |  |  | sub cidradd { | 
| 986 | 0 |  |  | 0 | 1 | 0 | my @cidr=@_; | 
| 987 |  |  |  |  |  |  |  | 
| 988 | 0 |  |  |  |  | 0 | my $ip=shift @cidr; | 
| 989 |  |  |  |  |  |  |  | 
| 990 | 0 | 0 |  |  |  | 0 | $ip="$ip-$ip" unless $ip =~ /[-\/]/; | 
| 991 |  |  |  |  |  |  |  | 
| 992 | 0 |  |  |  |  | 0 | unshift @cidr, $ip; | 
| 993 |  |  |  |  |  |  |  | 
| 994 | 0 |  |  |  |  | 0 | @cidr=cidr2range(@cidr); | 
| 995 |  |  |  |  |  |  |  | 
| 996 | 0 |  |  |  |  | 0 | my @a; | 
| 997 |  |  |  |  |  |  | my @b; | 
| 998 |  |  |  |  |  |  |  | 
| 999 |  |  |  |  |  |  | grep { | 
| 1000 | 0 | 0 |  |  |  | 0 | croak "This doesn't look like start-end\n" unless /(.*)-(.*)/; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1001 | 0 |  |  |  |  | 0 | push @a, $1; | 
| 1002 | 0 |  |  |  |  | 0 | push @b, $2; | 
| 1003 |  |  |  |  |  |  | } @cidr; | 
| 1004 |  |  |  |  |  |  |  | 
| 1005 | 0 |  |  |  |  | 0 | my $lo=shift @a; | 
| 1006 | 0 |  |  |  |  | 0 | my $hi=shift @b; | 
| 1007 |  |  |  |  |  |  |  | 
| 1008 | 0 |  |  |  |  | 0 | my $i; | 
| 1009 |  |  |  |  |  |  |  | 
| 1010 | 0 |  |  |  |  | 0 | for ($i=0; $i <= $#a; $i++) | 
| 1011 |  |  |  |  |  |  | { | 
| 1012 | 0 | 0 |  |  |  | 0 | last if _ipcmp($lo, $hi) > 0; | 
| 1013 |  |  |  |  |  |  |  | 
| 1014 | 0 | 0 |  |  |  | 0 | next if _ipcmp($b[$i], $lo) < 0; | 
| 1015 | 0 | 0 |  |  |  | 0 | next if _ipcmp($hi, $a[$i]) < 0; | 
| 1016 |  |  |  |  |  |  |  | 
| 1017 | 0 | 0 | 0 |  |  | 0 | if (_ipcmp($a[$i],$lo) <= 0 && _ipcmp($hi, $b[$i]) <= 0) | 
| 1018 |  |  |  |  |  |  | { | 
| 1019 | 0 |  |  |  |  | 0 | $lo=_add1($hi); | 
| 1020 | 0 |  |  |  |  | 0 | last; | 
| 1021 |  |  |  |  |  |  | } | 
| 1022 |  |  |  |  |  |  |  | 
| 1023 | 0 | 0 |  |  |  | 0 | if (_ipcmp($a[$i],$lo) <= 0) | 
| 1024 |  |  |  |  |  |  | { | 
| 1025 | 0 |  |  |  |  | 0 | $lo=_add1($b[$i]); | 
| 1026 | 0 |  |  |  |  | 0 | next; | 
| 1027 |  |  |  |  |  |  | } | 
| 1028 |  |  |  |  |  |  |  | 
| 1029 | 0 | 0 |  |  |  | 0 | if (_ipcmp($hi, $b[$i]) <= 0) | 
| 1030 |  |  |  |  |  |  | { | 
| 1031 | 0 |  |  |  |  | 0 | $hi=_sub1($a[$i]); | 
| 1032 | 0 |  |  |  |  | 0 | next; | 
| 1033 |  |  |  |  |  |  | } | 
| 1034 |  |  |  |  |  |  |  | 
| 1035 | 0 |  |  |  |  | 0 | $a[$i]=undef; | 
| 1036 | 0 |  |  |  |  | 0 | $b[$i]=undef; | 
| 1037 |  |  |  |  |  |  | } | 
| 1038 |  |  |  |  |  |  |  | 
| 1039 | 0 | 0 | 0 |  |  | 0 | unless ((! defined $lo) || (! defined $hi) || _ipcmp($lo, $hi) > 0) | 
|  |  |  | 0 |  |  |  |  | 
| 1040 |  |  |  |  |  |  | { | 
| 1041 | 0 |  |  |  |  | 0 | push @a, $lo; | 
| 1042 | 0 |  |  |  |  | 0 | push @b, $hi; | 
| 1043 |  |  |  |  |  |  | } | 
| 1044 |  |  |  |  |  |  |  | 
| 1045 | 0 |  |  |  |  | 0 | @cidr=(); | 
| 1046 |  |  |  |  |  |  |  | 
| 1047 | 0 |  |  |  |  | 0 | @a=grep ( (defined $_), @a); | 
| 1048 | 0 |  |  |  |  | 0 | @b=grep ( (defined $_), @b); | 
| 1049 |  |  |  |  |  |  |  | 
| 1050 | 0 |  |  |  |  | 0 | for ($i=0; $i <= $#a; $i++) | 
| 1051 |  |  |  |  |  |  | { | 
| 1052 | 0 |  |  |  |  | 0 | push @cidr, "$a[$i]-$b[$i]"; | 
| 1053 |  |  |  |  |  |  | } | 
| 1054 |  |  |  |  |  |  |  | 
| 1055 |  |  |  |  |  |  | @cidr=sort { | 
| 1056 | 0 |  |  |  |  | 0 | $a =~ /(.*)-/; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1057 |  |  |  |  |  |  |  | 
| 1058 | 0 |  |  |  |  | 0 | my $c=$1; | 
| 1059 |  |  |  |  |  |  |  | 
| 1060 | 0 |  |  |  |  | 0 | $b =~ /(.*)-/; | 
| 1061 |  |  |  |  |  |  |  | 
| 1062 | 0 |  |  |  |  | 0 | my $d=$1; | 
| 1063 |  |  |  |  |  |  |  | 
| 1064 | 0 |  |  |  |  | 0 | my $e=_ipcmp($c, $d); | 
| 1065 | 0 |  |  |  |  | 0 | return $e; | 
| 1066 |  |  |  |  |  |  | } @cidr; | 
| 1067 |  |  |  |  |  |  |  | 
| 1068 | 0 |  |  |  |  | 0 | $i=0; | 
| 1069 |  |  |  |  |  |  |  | 
| 1070 | 0 |  |  |  |  | 0 | while ($i < $#cidr) | 
| 1071 |  |  |  |  |  |  | { | 
| 1072 | 0 |  |  |  |  | 0 | $cidr[$i] =~ /(.*)-(.*)/; | 
| 1073 |  |  |  |  |  |  |  | 
| 1074 | 0 |  |  |  |  | 0 | my ($k, $l)=($1, $2); | 
| 1075 |  |  |  |  |  |  |  | 
| 1076 | 0 |  |  |  |  | 0 | $cidr[$i+1] =~ /(.*)-(.*)/; | 
| 1077 |  |  |  |  |  |  |  | 
| 1078 | 0 |  |  |  |  | 0 | my ($m, $n)=($1, $2); | 
| 1079 |  |  |  |  |  |  |  | 
| 1080 | 0 | 0 |  |  |  | 0 | if (_ipcmp( _add1($l), $m) == 0) | 
| 1081 |  |  |  |  |  |  | { | 
| 1082 | 0 |  |  |  |  | 0 | splice @cidr, $i, 2, "$k-$n"; | 
| 1083 | 0 |  |  |  |  | 0 | next; | 
| 1084 |  |  |  |  |  |  | } | 
| 1085 | 0 |  |  |  |  | 0 | ++$i; | 
| 1086 |  |  |  |  |  |  | } | 
| 1087 |  |  |  |  |  |  |  | 
| 1088 | 0 |  |  |  |  | 0 | return range2cidr(@cidr); | 
| 1089 |  |  |  |  |  |  | } | 
| 1090 |  |  |  |  |  |  |  | 
| 1091 |  |  |  |  |  |  |  | 
| 1092 |  |  |  |  |  |  | sub _add1 { | 
| 1093 | 0 |  |  | 0 |  | 0 | my $n=shift; | 
| 1094 |  |  |  |  |  |  |  | 
| 1095 | 0 |  |  |  |  | 0 | my $isipv6; | 
| 1096 |  |  |  |  |  |  |  | 
| 1097 | 0 |  |  |  |  | 0 | ($isipv6, $n)=_ipv6to4($n); | 
| 1098 |  |  |  |  |  |  |  | 
| 1099 | 0 |  |  |  |  | 0 | my @ip=split(/\./, $n); | 
| 1100 |  |  |  |  |  |  |  | 
| 1101 | 0 |  |  |  |  | 0 | my $i=$#ip; | 
| 1102 |  |  |  |  |  |  |  | 
| 1103 | 0 |  |  |  |  | 0 | while ($i >= 0) | 
| 1104 |  |  |  |  |  |  | { | 
| 1105 | 0 | 0 |  |  |  | 0 | last if ++$ip[$i] < 256; | 
| 1106 | 0 |  |  |  |  | 0 | $ip[$i]=0; | 
| 1107 | 0 |  |  |  |  | 0 | --$i; | 
| 1108 |  |  |  |  |  |  | } | 
| 1109 |  |  |  |  |  |  |  | 
| 1110 | 0 | 0 |  |  |  | 0 | return undef if $i < 0; | 
| 1111 |  |  |  |  |  |  |  | 
| 1112 | 0 |  |  |  |  | 0 | $i=join(".", @ip); | 
| 1113 | 0 | 0 |  |  |  | 0 | $i=_ipv4to6($i) if $isipv6; | 
| 1114 | 0 |  |  |  |  | 0 | return $i; | 
| 1115 |  |  |  |  |  |  |  | 
| 1116 |  |  |  |  |  |  | } | 
| 1117 |  |  |  |  |  |  |  | 
| 1118 |  |  |  |  |  |  | sub _sub1 { | 
| 1119 | 0 |  |  | 0 |  | 0 | my $n=shift; | 
| 1120 |  |  |  |  |  |  |  | 
| 1121 | 0 |  |  |  |  | 0 | my $isipv6; | 
| 1122 |  |  |  |  |  |  |  | 
| 1123 | 0 |  |  |  |  | 0 | ($isipv6, $n)=_ipv6to4($n); | 
| 1124 |  |  |  |  |  |  |  | 
| 1125 | 0 |  |  |  |  | 0 | my @ip=split(/\./, $n); | 
| 1126 |  |  |  |  |  |  |  | 
| 1127 | 0 |  |  |  |  | 0 | my $i=$#ip; | 
| 1128 |  |  |  |  |  |  |  | 
| 1129 | 0 |  |  |  |  | 0 | while ($i >= 0) | 
| 1130 |  |  |  |  |  |  | { | 
| 1131 | 0 | 0 |  |  |  | 0 | last if --$ip[$i] >= 0; | 
| 1132 | 0 |  |  |  |  | 0 | $ip[$i]=255; | 
| 1133 | 0 |  |  |  |  | 0 | --$i; | 
| 1134 |  |  |  |  |  |  | } | 
| 1135 |  |  |  |  |  |  |  | 
| 1136 | 0 | 0 |  |  |  | 0 | return undef if $i < 0; | 
| 1137 |  |  |  |  |  |  |  | 
| 1138 | 0 |  |  |  |  | 0 | $i=join(".", @ip); | 
| 1139 | 0 | 0 |  |  |  | 0 | $i=_ipv4to6($i) if $isipv6; | 
| 1140 | 0 |  |  |  |  | 0 | return $i; | 
| 1141 |  |  |  |  |  |  | } | 
| 1142 |  |  |  |  |  |  |  | 
| 1143 |  |  |  |  |  |  | =pod | 
| 1144 |  |  |  |  |  |  |  | 
| 1145 |  |  |  |  |  |  | =head2 $found=Net::CIDR::cidrlookup($ip, @cidr_list); | 
| 1146 |  |  |  |  |  |  |  | 
| 1147 |  |  |  |  |  |  | Search for $ip in @cidr_list.  $ip can be a single IP address, or a | 
| 1148 |  |  |  |  |  |  | netblock in CIDR or start-finish notation. | 
| 1149 |  |  |  |  |  |  | lookup() returns 1 if $ip overlaps any netblock in @cidr_list, 0 if not. | 
| 1150 |  |  |  |  |  |  |  | 
| 1151 |  |  |  |  |  |  | =cut | 
| 1152 |  |  |  |  |  |  |  | 
| 1153 |  |  |  |  |  |  | sub cidrlookup { | 
| 1154 | 10 |  |  | 10 | 1 | 204 | my @cidr=@_; | 
| 1155 |  |  |  |  |  |  |  | 
| 1156 | 10 |  |  |  |  | 22 | my $ip=shift @cidr; | 
| 1157 |  |  |  |  |  |  |  | 
| 1158 | 10 | 50 |  |  |  | 38 | $ip="$ip-$ip" unless $ip =~ /[-\/]/; | 
| 1159 |  |  |  |  |  |  |  | 
| 1160 | 10 |  |  |  |  | 20 | unshift @cidr, $ip; | 
| 1161 |  |  |  |  |  |  |  | 
| 1162 | 10 |  |  |  |  | 21 | @cidr=cidr2range(@cidr); | 
| 1163 |  |  |  |  |  |  |  | 
| 1164 | 10 |  |  |  |  | 13 | my @a; | 
| 1165 |  |  |  |  |  |  | my @b; | 
| 1166 |  |  |  |  |  |  |  | 
| 1167 |  |  |  |  |  |  | grep { | 
| 1168 | 10 | 50 |  |  |  | 19 | croak "This doesn't look like start-end\n" unless /(.*)-(.*)/; | 
|  | 30 |  |  |  |  | 94 |  | 
| 1169 | 30 |  |  |  |  | 60 | push @a, $1; | 
| 1170 | 30 |  |  |  |  | 56 | push @b, $2; | 
| 1171 |  |  |  |  |  |  | } @cidr; | 
| 1172 |  |  |  |  |  |  |  | 
| 1173 | 10 |  |  |  |  | 20 | my $lo=shift @a; | 
| 1174 | 10 |  |  |  |  | 16 | my $hi=shift @b; | 
| 1175 |  |  |  |  |  |  |  | 
| 1176 | 10 |  |  |  |  | 18 | my $i; | 
| 1177 |  |  |  |  |  |  |  | 
| 1178 | 10 |  |  |  |  | 23 | for ($i=0; $i <= $#a; $i++) | 
| 1179 |  |  |  |  |  |  | { | 
| 1180 | 17 | 100 |  |  |  | 44 | next if _ipcmp($b[$i], $lo) < 0; | 
| 1181 | 7 | 100 |  |  |  | 19 | next if _ipcmp($hi, $a[$i]) < 0; | 
| 1182 | 4 |  |  |  |  | 23 | return 1; | 
| 1183 |  |  |  |  |  |  | } | 
| 1184 |  |  |  |  |  |  |  | 
| 1185 | 6 |  |  |  |  | 29 | return 0; | 
| 1186 |  |  |  |  |  |  | } | 
| 1187 |  |  |  |  |  |  |  | 
| 1188 |  |  |  |  |  |  | =pod | 
| 1189 |  |  |  |  |  |  |  | 
| 1190 |  |  |  |  |  |  | =head2 $ip=Net::CIDR::cidrvalidate($ip); | 
| 1191 |  |  |  |  |  |  |  | 
| 1192 |  |  |  |  |  |  | Validate whether $ip is a valid IPv4 or IPv6 address, or a CIDR. | 
| 1193 |  |  |  |  |  |  | Returns its argument or undef. | 
| 1194 |  |  |  |  |  |  | Spaces are removed, and IPv6 hexadecimal address are converted to lowercase. | 
| 1195 |  |  |  |  |  |  |  | 
| 1196 |  |  |  |  |  |  | $ip with less than four octets gets filled out with additional octets, and | 
| 1197 |  |  |  |  |  |  | the modified value gets returned. This turns "192.168/16" into a proper | 
| 1198 |  |  |  |  |  |  | "192.168.0.0/16". | 
| 1199 |  |  |  |  |  |  |  | 
| 1200 |  |  |  |  |  |  | If $ip contains a "/", it must be a valid CIDR, otherwise it must be a valid | 
| 1201 |  |  |  |  |  |  | IPv4 or an IPv6 address. | 
| 1202 |  |  |  |  |  |  |  | 
| 1203 |  |  |  |  |  |  | A technically invalid CIDR, such as "192.168.0.1/24" fails validation, returning | 
| 1204 |  |  |  |  |  |  | undef. | 
| 1205 |  |  |  |  |  |  |  | 
| 1206 |  |  |  |  |  |  | =cut | 
| 1207 |  |  |  |  |  |  |  | 
| 1208 |  |  |  |  |  |  | sub cidrvalidate { | 
| 1209 | 12 |  |  | 12 | 1 | 315 | my $v=shift; | 
| 1210 |  |  |  |  |  |  |  | 
| 1211 | 12 |  |  |  |  | 30 | $v =~ s/\s//g; | 
| 1212 |  |  |  |  |  |  |  | 
| 1213 | 12 |  |  |  |  | 31 | $v=lc($v); | 
| 1214 |  |  |  |  |  |  |  | 
| 1215 | 12 |  |  |  |  | 15 | my $suffix; | 
| 1216 |  |  |  |  |  |  |  | 
| 1217 | 12 | 100 |  |  |  | 72 | ($v, $suffix)=($1, $2) if $v =~ m@(.*)/(.*)@; | 
| 1218 |  |  |  |  |  |  |  | 
| 1219 | 12 | 100 |  |  |  | 24 | if (defined $suffix) | 
| 1220 |  |  |  |  |  |  | { | 
| 1221 | 8 | 50 | 33 |  |  | 61 | return undef unless $suffix =~ /^\d+$/ && | 
|  |  |  | 33 |  |  |  |  | 
| 1222 |  |  |  |  |  |  | ($suffix eq "0" || $suffix =~ /^[123456789]/); | 
| 1223 |  |  |  |  |  |  | } | 
| 1224 |  |  |  |  |  |  |  | 
| 1225 | 12 | 100 | 100 |  |  | 72 | if ($v =~ /^([0-9\.]+)$/ || $v =~ /^::ffff:([0-9\.]+)$/ || | 
|  |  |  | 66 |  |  |  |  | 
| 1226 |  |  |  |  |  |  | $v =~ /^:([0-9\.]+)$/) | 
| 1227 |  |  |  |  |  |  | { | 
| 1228 | 6 |  |  |  |  | 13 | my $n=$1; | 
| 1229 |  |  |  |  |  |  |  | 
| 1230 | 6 | 50 | 33 |  |  | 30 | return undef if $n =~ /^\./ || $n =~ /\.$/ || $n =~ /\.\./; | 
|  |  |  | 33 |  |  |  |  | 
| 1231 |  |  |  |  |  |  |  | 
| 1232 | 6 |  |  |  |  | 17 | my @o= split(/\./, $n); | 
| 1233 |  |  |  |  |  |  |  | 
| 1234 | 6 |  |  |  |  | 18 | while ($#o < 3) | 
| 1235 |  |  |  |  |  |  | { | 
| 1236 | 0 |  |  |  |  | 0 | push @o, "0"; | 
| 1237 |  |  |  |  |  |  | } | 
| 1238 |  |  |  |  |  |  |  | 
| 1239 | 6 |  |  |  |  | 15 | $n=join(".", @o); | 
| 1240 |  |  |  |  |  |  |  | 
| 1241 | 6 | 50 |  |  |  | 13 | return undef if $#o != 3; | 
| 1242 |  |  |  |  |  |  |  | 
| 1243 | 6 |  |  |  |  | 9 | foreach (@o) | 
| 1244 |  |  |  |  |  |  | { | 
| 1245 | 24 | 50 |  |  |  | 41 | return undef if /^0./; | 
| 1246 | 24 | 50 | 33 |  |  | 82 | return undef if $_ < 0 || $_ > 255; | 
| 1247 |  |  |  |  |  |  | } | 
| 1248 |  |  |  |  |  |  |  | 
| 1249 | 6 | 100 |  |  |  | 15 | if ($v =~ /^::ffff/) | 
| 1250 |  |  |  |  |  |  | { | 
| 1251 | 3 | 100 |  |  |  | 6 | $suffix=128 unless defined $suffix; | 
| 1252 |  |  |  |  |  |  |  | 
| 1253 | 3 | 50 |  |  |  | 10 | return undef if $suffix < 128-32; | 
| 1254 |  |  |  |  |  |  |  | 
| 1255 | 3 |  |  |  |  | 11 | $suffix -= 128-32; | 
| 1256 |  |  |  |  |  |  | } | 
| 1257 |  |  |  |  |  |  | else | 
| 1258 |  |  |  |  |  |  | { | 
| 1259 | 3 | 100 |  |  |  | 6 | $suffix=32 unless defined $suffix; | 
| 1260 |  |  |  |  |  |  | } | 
| 1261 |  |  |  |  |  |  |  | 
| 1262 | 6 |  |  |  |  | 15 | foreach (addr2cidr($n)) | 
| 1263 |  |  |  |  |  |  | { | 
| 1264 | 86 | 100 |  |  |  | 159 | return $_ if $_ eq "$n/$suffix"; | 
| 1265 |  |  |  |  |  |  | } | 
| 1266 | 2 |  |  |  |  | 9 | return undef; | 
| 1267 |  |  |  |  |  |  | } | 
| 1268 |  |  |  |  |  |  |  | 
| 1269 | 6 | 50 |  |  |  | 20 | return undef unless $v =~ /^[0-9a-f:]+$/; | 
| 1270 |  |  |  |  |  |  |  | 
| 1271 | 6 | 50 | 33 |  |  | 46 | return undef if $v =~ /:::/ || $v =~ /^:[^:]/ || $v =~ /[^:]:$/ | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 1272 |  |  |  |  |  |  | || $v =~ /::.*::/; | 
| 1273 |  |  |  |  |  |  |  | 
| 1274 | 6 |  |  |  |  | 42 | my @o=grep (/./, split(/:/, $v)); | 
| 1275 |  |  |  |  |  |  |  | 
| 1276 | 6 | 50 | 66 |  |  | 35 | return undef if ($#o >= 8 || ($#o<7 && $v !~ /::/)); | 
|  |  |  | 33 |  |  |  |  | 
| 1277 |  |  |  |  |  |  |  | 
| 1278 | 6 |  |  |  |  | 18 | foreach (@o) | 
| 1279 |  |  |  |  |  |  | { | 
| 1280 | 19 | 50 |  |  |  | 50 | return undef if length ($_) > 4; | 
| 1281 |  |  |  |  |  |  | } | 
| 1282 |  |  |  |  |  |  |  | 
| 1283 | 6 | 100 |  |  |  | 14 | $suffix=128 unless defined $suffix; | 
| 1284 |  |  |  |  |  |  |  | 
| 1285 | 6 |  |  |  |  | 26 | $v =~ s/([0-9A-Fa-f]+)/_triml0($1)/ge; | 
|  | 19 |  |  |  |  | 34 |  | 
| 1286 |  |  |  |  |  |  |  | 
| 1287 | 6 |  |  |  |  | 16 | foreach (addr2cidr($v)) | 
| 1288 |  |  |  |  |  |  | { | 
| 1289 | 322 | 100 |  |  |  | 565 | return $_ if $_ eq "$v/$suffix"; | 
| 1290 |  |  |  |  |  |  | } | 
| 1291 | 1 |  |  |  |  | 16 | return undef; | 
| 1292 |  |  |  |  |  |  | } | 
| 1293 |  |  |  |  |  |  |  | 
| 1294 |  |  |  |  |  |  | sub _triml0 { | 
| 1295 | 19 |  |  | 19 |  | 35 | my ($a) = @_; | 
| 1296 |  |  |  |  |  |  |  | 
| 1297 | 19 |  |  |  |  | 35 | $a =~ s/^0+//g; | 
| 1298 | 19 | 100 |  |  |  | 35 | $a = "0" if $a eq ''; | 
| 1299 | 19 |  |  |  |  | 57 | return $a | 
| 1300 |  |  |  |  |  |  | } | 
| 1301 |  |  |  |  |  |  |  | 
| 1302 |  |  |  |  |  |  | =pod | 
| 1303 |  |  |  |  |  |  |  | 
| 1304 |  |  |  |  |  |  | =head1 BUGS | 
| 1305 |  |  |  |  |  |  |  | 
| 1306 |  |  |  |  |  |  | Garbage in, garbage out. | 
| 1307 |  |  |  |  |  |  | Always use cidrvalidate() before doing anything with untrusted input. | 
| 1308 |  |  |  |  |  |  | Otherwise, | 
| 1309 |  |  |  |  |  |  | "slightly" invalid input will work (extraneous whitespace | 
| 1310 |  |  |  |  |  |  | is generally OK), | 
| 1311 |  |  |  |  |  |  | but the functions will croak if you're totally off the wall. | 
| 1312 |  |  |  |  |  |  |  | 
| 1313 |  |  |  |  |  |  | =head1 AUTHOR | 
| 1314 |  |  |  |  |  |  |  | 
| 1315 |  |  |  |  |  |  | Sam Varshavchik | 
| 1316 |  |  |  |  |  |  |  | 
| 1317 |  |  |  |  |  |  | With some contributions from David Cantrell | 
| 1318 |  |  |  |  |  |  |  | 
| 1319 |  |  |  |  |  |  | =cut | 
| 1320 |  |  |  |  |  |  |  | 
| 1321 |  |  |  |  |  |  | __END__ |