pax_global_header00006660000000000000000000000064144025064160014514gustar00rootroot0000000000000052 comment=1359b30c776e730b4806714ce979a3dbee27ad49 ipaddress-go-1.5.4/000077500000000000000000000000001440250641600141045ustar00rootroot00000000000000ipaddress-go-1.5.4/.gitignore000066400000000000000000000004271440250641600160770ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out notes.go # Dependency directories (remove the comment below to include it) # vendor/ ipaddress-go-1.5.4/.idea/000077500000000000000000000000001440250641600150645ustar00rootroot00000000000000ipaddress-go-1.5.4/.idea/.gitignore000066400000000000000000000002601440250641600170520ustar00rootroot00000000000000# Default ignored files /shelf/ /workspace.xml # Datasource local storage ignored files /dataSources/ /dataSources.local.xml # Editor-based HTTP Client requests /httpRequests/ ipaddress-go-1.5.4/.idea/ipaddress-go.iml000066400000000000000000000005021440250641600201450ustar00rootroot00000000000000 ipaddress-go-1.5.4/.idea/modules.xml000066400000000000000000000004241440250641600172560ustar00rootroot00000000000000 ipaddress-go-1.5.4/.idea/vcs.xml000066400000000000000000000002641440250641600164030ustar00rootroot00000000000000 ipaddress-go-1.5.4/LICENSE000066400000000000000000000261351440250641600151200ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ipaddress-go-1.5.4/README.md000066400000000000000000000042041440250641600153630ustar00rootroot00000000000000# ipaddress-go [Go](https://golang.org/) library for handling IP addresses and subnets, both IPv4 and IPv6 IP address and network manipulation, CIDR, operations, iterations, containment checks, longest prefix match, subnetting, and data structures, with polymorphic code [View Project Page](https://seancfoley.github.io/IPAddress/) [View Godoc](https://pkg.go.dev/github.com/seancfoley/ipaddress-go/ipaddr) [![Go Reference](https://pkg.go.dev/badge/github.com/seancfoley/ipaddress-go/ipaddr.svg)](https://pkg.go.dev/github.com/seancfoley/ipaddress-go/ipaddr) [View Code Examples](https://github.com/seancfoley/ipaddress-go/wiki/Code-Examples) [View List of Users](https://github.com/seancfoley/ipaddress-go/wiki) | Version | Notes | | ------- | ------------- | | [1.2.1](https://github.com/seancfoley/ipaddress-go/releases/tag/v1.2.1) | Requires Go 1.12 or higher | | [1.4.1](https://github.com/seancfoley/ipaddress-go/releases/tag/v1.4.1) | Requires Go 1.13 or higher | | [1.5.4](https://github.com/seancfoley/ipaddress-go/releases/tag/v1.5.4) | Requires Go 1.18 or higher | In your go.mod file:\ require github.com/seancfoley/ipaddress-go v1.5.4 In your source file:\ import "github.com/seancfoley/ipaddress-go/ipaddr" Also available as a [Java](https://www.oracle.com/java/) library from the [IPAddress repository](https://github.com/seancfoley/IPAddress) ## Getting Started starting with address or subnet strings ```go import "github.com/seancfoley/ipaddress-go/ipaddr" ipv6AddrStr := ipaddr.NewIPAddressString("a:b:c:d::a:b/64") if ipAddr, err := ipv6AddrStr.ToAddress(); err != nil { // err.Error() has validation error } else { // use the address } ``` ...or avoid errors, checking for nil: ```go str := ipaddr.NewIPAddressString("a:b:c:d:e-f:f:1.2-3.3.4/64") addr := str.GetAddress() if addr != nil { // use address } ``` starting with host name strings ```go hostStr := "[::1]" host := ipaddr.NewHostName(hostStr) err := host.Validate() if err == nil { if host.IsAddress() { fmt.Println("address: " + host.AsAddress().String()) } else { fmt.Println("host name: " + host.String()) } // use host } else { fmt.Println(err.Error()) } ``` ipaddress-go-1.5.4/go.mod000066400000000000000000000012721440250641600152140ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // module github.com/seancfoley/ipaddress-go go 1.18 require github.com/seancfoley/bintree v1.2.1 ipaddress-go-1.5.4/go.sum000066400000000000000000000002611440250641600152360ustar00rootroot00000000000000github.com/seancfoley/bintree v1.2.1 h1:Z/iNjRKkXnn0CTW7jDQYtjW5fz2GH1yWvOTJ4MrMvdo= github.com/seancfoley/bintree v1.2.1/go.mod h1:hIUabL8OFYyFVTQ6azeajbopogQc2l5C/hiXMcemWNU= ipaddress-go-1.5.4/ipaddr/000077500000000000000000000000001440250641600153475ustar00rootroot00000000000000ipaddress-go-1.5.4/ipaddr/IPAddressResources.properties000066400000000000000000000273171440250641600232100ustar00rootroot00000000000000# # Copyright 2016-2022 Sean C Foley # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ipaddress.error.zero.not.allowed=a non-zero address is required ipaddress.error.only.ipv6.has.zone=only ipv6 can have a zone specified ipaddress.error.ipv6.has.zone=no ipv6 zone allowed ipaddress.error.only.ipv6.square.brackets=only ipv6 can be enclosed in square brackets ipaddress.error.special.ip=a special IP address with first segment larger than 255 cannot be used here ipaddress.error.ipv6.segment.format=invalid segment ipaddress.error.empty=you must specify an address ipaddress.error.all=the universal address is not allowed ipaddress.error.single.segment=validation options do not allow you to specify a non-segmented single value ipaddress.error.ipv4=validation options do not allow IPv4 ipaddress.error.ipv6=validation options do not allow IPv6 ipaddress.error.prefix.only=a prefix-only address is not allowed ipaddress.error.ip.format=invalid format of IP address, whether IPv4 (255.255.255.255) or IPv6 (ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff) or other supported format ipaddress.error.ipv6.format=invalid format of IPv6 (ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff) address ipaddress.error.ipv4.format=invalid format of IPv4 (255.255.255.255) address ipaddress.error.ipv4.segment.too.large=IPv4 segment too large ipaddress.error.address.too.large=address too large ipaddress.error.segment.too.long=segment too long ipaddress.error.segment.too.long.at.index=segment too long at index ipaddress.error.segment.too.short.at.index=segment too short at index ipaddress.error.segment.leading.zeros=segment value starts with zero ipaddress.error.ipv4.prefix.leading.zeros=IPv4 CIDR prefix length starts with zero ipaddress.error.ipv6.prefix.leading.zeros=IPv6 CIDR prefix length starts with zero ipaddress.error.ipv4.segment.hex=IPv4 segment contains hexadecimal value ipaddress.error.invalid.joined.ranges=range of joined segments cannot be divided into individual ranges ipaddress.error.too.few.segments=address has too few segments ipaddress.error.too.few.segments.digit.count=address has too few segments or an invalid digit count ipaddress.error.front.digit.count=front address in range has an invalid digit count ipaddress.error.back.digit.count=back address in range has an invalid digit count ipaddress.error.ipv4.too.few.segments=options do not allow IPv4 address with less than four segments ipaddress.error.too.many.segments=address has too many segments ipaddress.error.ipv4.too.many.segments=IPv4 address has too many segments ipaddress.error.ipv4.invalid.segment.count=IPv4 address has invalid segment count ipaddress.error.ipv4.invalid.byte.count=IPv4 address has invalid byte count ipaddress.error.mac.invalid.segment.count=MAC address has invalid segment count ipaddress.error.mac.invalid.byte.count=MAC address has invalid byte count ipaddress.error.ipv6.invalid.segment.count=IPv6 address has invalid segment count ipaddress.error.ipv6.invalid.byte.count=IPv6 address has invalid byte count ipaddress.error.ipv6.ambiguous=IPv6 compressed address is ambiguous ipaddress.error.ipv6.cannot.start.with.single.separator=An IPv6 address cannot start with a single colon, it must start with either two colons or with the first segment ipaddress.error.cannot.end.with.single.separator=An IPv6 address cannot end with a single colon, it must end with either two colons or with the last segment ipaddress.error.unavailable.numeric=No numeric value available for this address ipaddress.error.invalidRange=in segment range, lower value must precede upper value ipaddress.error.invalid.character.combination=invalid combination of characters in segment ipaddress.error.invalid.character.combination.at.index=invalid combination with earlier character at character number ipaddress.error.invalid.character.at.index=invalid character number ipaddress.error.invalid.character=invalid character in segment ipaddress.error.empty.start.of.range=range start missing ipaddress.error.empty.segment.at.index=segment value missing at index ipaddress.error.single.wildcard.order=single wildcards can appear only as the end of segment values ipaddress.error.no.mixed=validation options do no allow mixed IPv6 ipaddress.error.no.wildcard=validation options do no allow wildcard segments ipaddress.error.no.single.wildcard=validation options do no allow single character wildcard segments ipaddress.error.no.range=validation options do not allow range segments ipaddress.error.ipv4.invalid.binary.digit=invalid binary digit ipaddress.error.ipv4.invalid.octal.digit=invalid octal digit ipaddress.error.ipv4.invalid.decimal.digit=invalid decimal digit ipaddress.error.ipv6.separator=invalid position of IPv6 separator ipaddress.error.invalidCIDRPrefixOrMask=A mask must be a single IP address, while a CIDR prefix length must indicate the count of subnet bits, between 0 and 32 for IP version 4 addresses and between 0 and 128 for IP version 6 addresses ipaddress.error.invalidCIDRPrefix=CIDR prefix must indicate the count of subnet bits, between 0 and 32 subnet bits for IP version 4 addresses and between 0 and 128 subnet bits for IP version 6 addresses ipaddress.error.invalid.mask.empty=mask is empty ipaddress.error.invalid.mask.address.empty=mask with empty address ipaddress.error.invalid.mask.wildcard=wildcard in mask ipaddress.error.invalid.mask.extra.chars=invalid chars following mask at index: ipaddress.host.error.invalidService.no.letter=service name must have at least one letter ipaddress.host.error.invalidService.too.long=service name too long ipaddress.host.error.invalidService.no.chars=service name is empty ipaddress.host.error.invalidPort.too.large=port number too large ipaddress.host.error.invalidPort.no.digits=port value is empty ipaddress.host.error.invalid.service.hyphen.end=service name cannot end in a hyphen ipaddress.host.error.invalid.service.hyphen.start=service name cannot start with a hyphen ipaddress.host.error.invalid.service.hyphen.consecutive=service name cannot have consecutive hyphens ipaddress.host.error.invalid.port.service=invalid port or service name character at index: ipaddress.error.zoneAndCIDRPrefix=zone and prefix combined ipaddress.error.zone=IPv6 zone not allowed ipaddress.error.CIDRNotAllowed=CIDR prefix or mask not allowed for this address ipaddress.error.wildcardOrRangeIPv6=Wildcards and ranges are not supported for IPv6 addresses ipaddress.error.mixedVersions=Please specify either IPv4 or IPv6 addresses, but not both ipaddress.error.mixedNetworks=Address components have different networks ipaddress.error.nullNetwork=network is nil ipaddress.error.maskMismatch=applying the mask results in a segment that is not a sequential range ipaddress.error.segmentMismatch=joining segments results in a joined segment that is not a sequential range ipaddress.error.reverseRange=reversing a range of values does not result in a sequential range ipaddress.error.splitMismatch=splitting digits in range segments results in an invalid string (eg 12-22 becomes 1-2.2-2 which is 12 and 22 and nothing in between) ipaddress.error.splitSeg=cannot split ranged segment into smaller ranged segments spanning the same values ipaddress.error.invalidMultipleMask=mask must specify a single IP address ipaddress.error.invalidMixedRange=IPv4 segment ranges cannot be converted to IPv6 segment ranges ipaddress.error.invalidMACIPv6Range=MAC segment ranges cannot be converted to IPv6 segment ranges ipaddress.error.ipMismatch=IP version of address must match IP version of mask ipaddress.error.version.mismatch=Unable to convert version of argument address ipaddress.error.ipVersionMismatch=the IP version must match ipaddress.error.ipVersionIndeterminate=requested version is indeterminate ipaddress.error.sizeMismatch=the number of segments must match ipaddress.error.prefixSize=the network prefix bit-length is negative or exceeds the address bit-length ipaddress.error.separatePrefixFromAddress=specify the IP address separately from the mask or prefix ipaddress.error.separatePrefixFromMask=specify a mask or prefix but not both ipaddress.error.notNetworkMask=mask is not a network mask ipaddress.error.url=please supply an address, not a full URL ipaddress.error.address.is.ipv4=address is IPv4 ipaddress.error.address.is.ipv6=address is IPv6 ipaddress.error.only.zone=with a zone you must specify an address ipaddress.error.invalid.zone=invalid zone or scope id character at index: ipaddress.error.invalid.zone.encoding=invalid encoding in zone at index: ipaddress.error.mask.single.segment=mask with single segment not allowed by validation options ipaddress.error.exceeds.size=exceeds address size ipaddress.error.negative=negative address value ipaddress.error.invalid.size=invalid address size ipaddress.error.index.exceeds.prefix.length=index exceeds prefix length ipaddress.error.null.segment=Section or grouping array contains a nil value ipaddress.error.inconsistent.prefixes=Segments invalid due to inconsistent prefix values ipaddress.error.invalid.position=Invalid index into address ipaddress.error.incompatible.position=Incompatible positions in address ipaddress.error.address.not.block=Address is neither a CIDR prefix block nor an individual address ipaddress.error.address.out.of.range=Address not within the assigned range ipaddress.error.address.lower.exceeds.upper=invalid address range, lower bound exceeds upper: ipaddress.error.lower.below.range=below range: ipaddress.error.lower.above.range=above range: ipaddress.error.no.iterator.element.to.remove=no iterator element to remove ipaddress.error.mismatched.bit.size=mismatched address bit size ipaddress.address.error=IP Address error: ipaddress.host.error.cidrprefixonly=please supply an address, not a CIDR prefix length only ipaddress.host.error.invalid.type=invalid IP address type ipaddress.host.error=Host error: ipaddress.host.error.invalid.length=invalid host length ipaddress.host.error.invalid=invalid host ipaddress.host.error.invalid.mechanism=address mechanism not supported ipaddress.host.error.bracketed.not.ipv6=bracketed address must be IPv6 ipaddress.host.error.bracketed.missing.end=bracketed address missing end bracket ipaddress.host.error.bracketed.conflicting.prefix.length=conflicting prefix lengths inside and outside of bracketed address ipaddress.host.error.bracketed.conflicting.mask=conflicting masks inside and outside of bracketed address ipaddress.host.error.host.resolve=host cannot be resolved ipaddress.host.error.empty.host.resolve=empty host cannot be resolved ipaddress.host.error.host.brackets=ipv6 addresses must be surrounded by square brackets [] in host names ipaddress.host.error.url=please supply a host, not a full URL ipaddress.host.error.all.numeric=host cannot be all numeric ipaddress.host.error.too.many.segments=too many segments ipaddress.host.error.invalid.character.at.index=invalid character at index ipaddress.host.error.segment.too.short=zero-length segment ipaddress.host.error.empty=validation options do no allow empty string for host ipaddress.host.error.port=validation options do no allow for port ipaddress.host.error.service=validation options do no allow for service name ipaddress.host.error.ipaddress=validation options do no allow IP address ipaddress.mac.error.not.eui.convertible=MAC address cannot be converted to EUI 64 ipaddress.mac.error.mix.format.characters.at.index=invalid mix of mac address format characters at index ipaddress.mac.error.format=validation options do no allow this mac format ipaddress-go-1.5.4/ipaddr/LICENSE000066400000000000000000000261351440250641600163630ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ipaddress-go-1.5.4/ipaddr/addr.go000066400000000000000000002351231440250641600166160ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math/big" "reflect" "unsafe" "github.com/seancfoley/bintree/tree" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) const ( HexPrefix = "0x" OctalPrefix = "0" BinaryPrefix = "0b" RangeSeparator = '-' RangeSeparatorStr = "-" AlternativeRangeSeparator = '\u00bb' AlternativeRangeSeparatorStr = "\u00bb" // '»' ExtendedDigitsRangeSeparatorStr = AlternativeRangeSeparatorStr SegmentWildcard = '*' SegmentWildcardStr = "*" SegmentSqlWildcard = '%' SegmentSqlWildcardStr = "%" SegmentSqlSingleWildcard = '_' SegmentSqlSingleWildcardStr = "_" //ExtendedDigitsRangeSeparator = '\u00bb' //AlternativeSegmentWildcard = '¿' ) var segmentWildcardStr = SegmentWildcardStr func createAddress(section *AddressSection, zone Zone) *Address { res := &Address{ addressInternal{ section: section, zone: zone, cache: &addressCache{}, }, } return res } // SegmentValueProvider provides values for segments. // Values that fall outside the segment value type range will be truncated using standard golang integer type conversions https://golang.org/ref/spec#Conversions type SegmentValueProvider func(segmentIndex int) SegInt // AddressValueProvider provides values for addresses. type AddressValueProvider interface { GetSegmentCount() int GetValues() SegmentValueProvider GetUpperValues() SegmentValueProvider } type addrsCache struct { lower, upper *Address } // identifierStr is a string representation of an address or host name. type identifierStr struct { idStr HostIdentifierString // MACAddressString or IPAddressString or HostName } type addressCache struct { addrsCache *addrsCache stringCache *stringCache // only used by IPv6 when there is a zone identifierStr *identifierStr } type addressInternal struct { section *AddressSection zone Zone cache *addressCache } // GetBitCount returns the number of bits comprising this address, // or each address in the range if a subnet. func (addr *addressInternal) GetBitCount() BitCount { section := addr.section if section == nil { return 0 } return section.GetBitCount() } // GetByteCount returns the number of bytes required for this address, // or each address in the range if a subnet. func (addr *addressInternal) GetByteCount() int { section := addr.section if section == nil { return 0 } return section.GetByteCount() } func (addr *addressInternal) getBytes() []byte { return addr.section.getBytes() } func (addr *addressInternal) getUpperBytes() []byte { return addr.section.getUpperBytes() } func (addr *addressInternal) getTrailingBitCount(ones bool) BitCount { return addr.section.GetTrailingBitCount(ones) } func (addr *addressInternal) getLeadingBitCount(ones bool) BitCount { return addr.section.GetLeadingBitCount(ones) } func (addr *addressInternal) getCount() *big.Int { section := addr.section if section == nil { return bigOne() } return section.GetCount() } // GetPrefixCount returns the count of prefixes in this address or subnet. // // The prefix length is given by GetPrefixLen. // // If this has a non-nil prefix length, returns the count of the range of values in the prefix. // // If this has a nil prefix length, returns the same value as GetCount. func (addr *addressInternal) GetPrefixCount() *big.Int { section := addr.section if section == nil { return bigOne() } return section.GetPrefixCount() } // GetPrefixCountLen returns the count of prefixes in this address or subnet for the given prefix length. // // If not a subnet of multiple addresses, or a subnet with just single prefix of the given length, returns 1. func (addr *addressInternal) GetPrefixCountLen(prefixLen BitCount) *big.Int { section := addr.section if section == nil { return bigOne() } return section.GetPrefixCountLen(prefixLen) } // GetBlockCount returns the count of distinct values in the given number of initial (more significant) segments. func (addr *addressInternal) GetBlockCount(segments int) *big.Int { section := addr.section if section == nil { return bigOne() } return section.GetBlockCount(segments) } // testBit returns true if the bit in the lower value of this address at the given index is 1, where index 0 refers to the least significant bit. // In other words, it computes (bits & (1 << n)) != 0), using the lower value of this address. // TestBit will panic if n < 0, or if it matches or exceeds the bit count of this item. func (addr *addressInternal) testBit(n BitCount) bool { return addr.section.TestBit(n) } // isOneBit returns true if the bit in the lower value of this address at the given index is 1, where index 0 refers to the most significant bit. // isOneBit will panic if bitIndex is less than zero or larger than the bit count of this item. func (addr *addressInternal) isOneBit(bitIndex BitCount) bool { return addr.section.IsOneBit(bitIndex) } // isMultiple returns true if this address represents more than a single individual address, whether it is a subnet of multiple addresses. func (addr *addressInternal) isMultiple() bool { return addr.section != nil && addr.section.isMultiple() } // isPrefixed returns whether this address has an associated prefix length. func (addr *addressInternal) isPrefixed() bool { return addr.section != nil && addr.section.IsPrefixed() } // GetPrefixLen returns the prefix length, or nil if there is no prefix length. // // A prefix length indicates the number of bits in the initial part (most significant bits) of the address that comprise the prefix. // // A prefix is a part of the address that is not specific to that address but common amongst a group of addresses, such as a CIDR prefix block subnet. // // For IP addresses, the prefix is explicitly defined when the address is created. For example, "1.2.0.0/16" has a prefix length of 16, while "1.2.*.*" has no prefix length, // even though they both represent the same set of addresses and are considered equal. Prefixes can be considered variable for a given IP address and can depend on routing. // // The methods GetMinPrefixLenForBlock and GetPrefixLenForSingleBlock can help you to obtain or define a prefix length if one does not exist already. // The method ToPrefixBlockLen allows you to create the subnet consisting of the block of addresses for any given prefix length. // // For MAC addresses, the prefix is initially inferred from the range, so "1:2:3:*:*:*" has a prefix length of 24. // MAC addresses derived from an address with a prefix length may retain the prefix length regardless of their own range of values. func (addr *addressInternal) GetPrefixLen() PrefixLen { return addr.getPrefixLen().copy() } func (addr *addressInternal) getPrefixLen() PrefixLen { if addr.section == nil { return nil } return addr.section.getPrefixLen() } // IsSinglePrefixBlock returns whether the address range matches the block of values for a single prefix identified by the prefix length of this address. // This is similar to IsPrefixBlock except that it returns false when the subnet has multiple prefixes. // // What distinguishes this method from ContainsSinglePrefixBlock is that this method returns // false if the series does not have a prefix length assigned to it, // or a prefix length that differs from the prefix length for which ContainsSinglePrefixBlock returns true. // // It is similar to IsPrefixBlock but returns false when there are multiple prefixes. // // For instance, "1.*.*.* /16" returns false from this method and returns true from IsPrefixBlock. func (addr *addressInternal) IsSinglePrefixBlock() bool { prefLen := addr.getPrefixLen() return prefLen != nil && addr.section.IsSinglePrefixBlock() } // IsPrefixBlock returns whether the address has a prefix length and the address range includes the block of values for that prefix length. // If the prefix length matches the bit count, this returns true. // // To create a prefix block from any address, use ToPrefixBlock. // // This is different from ContainsPrefixBlock in that this method returns // false if the series has no prefix length, or a prefix length that differs from a prefix length for which ContainsPrefixBlock returns true. func (addr *addressInternal) IsPrefixBlock() bool { prefLen := addr.getPrefixLen() return prefLen != nil && addr.section.ContainsPrefixBlock(prefLen.bitCount()) } // ContainsPrefixBlock returns whether the range of this address or subnet contains the block of addresses for the given prefix length. // // Unlike ContainsSinglePrefixBlock, whether there are multiple prefix values in this item for the given prefix length makes no difference. // // Use GetMinPrefixLenForBlock to determine the smallest prefix length for which this method returns true. func (addr *addressInternal) ContainsPrefixBlock(prefixLen BitCount) bool { return addr.section == nil || addr.section.ContainsPrefixBlock(prefixLen) } // ContainsSinglePrefixBlock returns whether this address contains a single prefix block for the given prefix length. // // This means there is only one prefix value for the given prefix length, and it also contains the full prefix block for that prefix, all addresses with that prefix. // // Use GetPrefixLenForSingleBlock to determine whether there is a prefix length for which this method returns true. func (addr *addressInternal) ContainsSinglePrefixBlock(prefixLen BitCount) bool { return addr.section == nil || addr.section.ContainsSinglePrefixBlock(prefixLen) } // GetMinPrefixLenForBlock returns the smallest prefix length such that this includes the block of addresses for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this represents just a single address, returns the bit length of this address. func (addr *addressInternal) GetMinPrefixLenForBlock() BitCount { section := addr.section if section == nil { return 0 } return section.GetMinPrefixLenForBlock() } // GetPrefixLenForSingleBlock returns a prefix length for which the range of this address subnet matches exactly the block of addresses for that prefix. // // If the range can be described this way, then this method returns the same value as GetMinPrefixLenForBlock. // // If no such prefix exists, returns nil. // // If this segment grouping represents a single value, returns the bit length of this address. // // IP address examples: // - 1.2.3.4 returns 32 // - 1.2.3.4/16 returns 32 // - 1.2.*.* returns 16 // - 1.2.*.0/24 returns 16 // - 1.2.0.0/16 returns 16 // - 1.2.*.4 returns nil // - 1.2.252-255.* returns 22 func (addr *addressInternal) GetPrefixLenForSingleBlock() PrefixLen { section := addr.section if section == nil { return cacheBitCount(0) } return section.GetPrefixLenForSingleBlock() } // In callers, we always need to ensure init is called, otherwise a nil section will be zero-size instead of having size one. func (addr *addressInternal) compareSize(other AddressItem) int { return addr.section.compareSize(other) } func (addr *addressInternal) trieCompare(other *Address) int { if addr.toAddress() == other { return 0 } segmentCount := addr.getDivisionCount() bitsPerSegment := addr.GetBitsPerSegment() o1Pref := addr.GetPrefixLen() o2Pref := other.GetPrefixLen() bitsMatchedSoFar := 0 i := 0 for { segment1 := addr.getSegment(i) segment2 := other.getSegment(i) pref1 := getSegmentPrefLen(addr.toAddress(), o1Pref, bitsPerSegment, bitsMatchedSoFar, segment1) pref2 := getSegmentPrefLen(other, o2Pref, bitsPerSegment, bitsMatchedSoFar, segment2) if pref1 != nil { segmentPref1 := pref1.Len() segmentPref2 := pref2.Len() if pref2 != nil && segmentPref2 <= segmentPref1 { matchingBits := getMatchingBits(segment1, segment2, segmentPref2, bitsPerSegment) if matchingBits >= segmentPref2 { if segmentPref2 == segmentPref1 { // same prefix block return 0 } // segmentPref2 is shorter prefix, prefix bits match, so depends on bit at index segmentPref2 if segment1.IsOneBit(segmentPref2) { return 1 } return -1 } return compareSegInt(segment1.GetSegmentValue(), segment2.GetSegmentValue()) } else { matchingBits := getMatchingBits(segment1, segment2, segmentPref1, bitsPerSegment) if matchingBits >= segmentPref1 { if segmentPref1 < bitsPerSegment { if segment2.IsOneBit(segmentPref1) { return -1 } return 1 } else { i++ if i == segmentCount { return 1 // o1 with prefix length matching bit count is the bigger } // else must check the next segment } } else { return compareSegInt(segment1.GetSegmentValue(), segment2.GetSegmentValue()) } } } else if pref2 != nil { segmentPref2 := pref2.Len() matchingBits := getMatchingBits(segment1, segment2, segmentPref2, bitsPerSegment) if matchingBits >= segmentPref2 { if segmentPref2 < bitsPerSegment { if segment1.IsOneBit(segmentPref2) { return 1 } return -1 } else { i++ if i == segmentCount { return -1 // o2 with prefix length matching bit count is the bigger } // else must check the next segment } } else { return compareSegInt(segment1.GetSegmentValue(), segment2.GetSegmentValue()) } } else { matchingBits := getMatchingBits(segment1, segment2, bitsPerSegment, bitsPerSegment) if matchingBits < bitsPerSegment { // no match - the current subnet/address is not here return compareSegInt(segment1.GetSegmentValue(), segment2.GetSegmentValue()) } else { i++ if i == segmentCount { // same address return 0 } // else must check the next segment } } bitsMatchedSoFar += bitsPerSegment } } func trieIncrement[T TrieKeyConstraint[T]](addr T) (t T, ok bool) { if res, ok := tree.TrieIncrement(trieKey[T]{addr}); ok { return res.address, true } return } func trieDecrement[T TrieKeyConstraint[T]](addr T) (t T, ok bool) { if res, ok := tree.TrieDecrement(trieKey[T]{addr}); ok { return res.address, true } return } func (addr *addressInternal) toString() string { section := addr.section if section == nil { return nilSection() // note no zone possible since a zero-address like Address{} or IPAddress{} cannot have a zone } else if addr.isMAC() { return addr.toNormalizedString() } return addr.toCanonicalString() } // IsSequential returns whether the address or subnet represents a range of addresses that are sequential. // // Generally, for a subnet this means that any segment covering a range of values must be followed by segments that are full range, covering all values. // // Individual addresses are sequential and CIDR prefix blocks are sequential. // The subnet "1.2.3-4.5" is not sequential, since the two addresses it represents, "1.2.3.5" and "1.2.4.5", are not ("1.2.3.6" is in-between the two but not in the subnet). // // With any IP address subnet, you can use SequentialBlockIterator to convert any subnet to a collection of sequential subnets. func (addr *addressInternal) IsSequential() bool { section := addr.section if section == nil { return true } return section.IsSequential() } func (addr *addressInternal) getSegment(index int) *AddressSegment { return addr.section.GetSegment(index) } // GetBitsPerSegment returns the number of bits comprising each segment in this address or subnet. Segments in the same address are equal length. func (addr *addressInternal) GetBitsPerSegment() BitCount { section := addr.section if section == nil { return 0 } return section.GetBitsPerSegment() } // GetBytesPerSegment returns the number of bytes comprising each segment in this address or subnet. Segments in the same address are equal length. func (addr *addressInternal) GetBytesPerSegment() int { section := addr.section if section == nil { return 0 } return section.GetBytesPerSegment() } func (addr *addressInternal) getMaxSegmentValue() SegInt { return addr.section.GetMaxSegmentValue() } func (addr *addressInternal) checkIdentity(section *AddressSection) *Address { if section == nil { return nil } else if section == addr.section { return addr.toAddress() } return createAddress(section, addr.zone) } func (addr *addressInternal) getLower() *Address { lower, _ := addr.getLowestHighestAddrs() return lower } func (addr *addressInternal) getUpper() *Address { _, upper := addr.getLowestHighestAddrs() return upper } func (addr *addressInternal) getLowestHighestAddrs() (lower, upper *Address) { if !addr.isMultiple() { lower = addr.toAddress() upper = lower return } cache := addr.cache if cache == nil { return addr.createLowestHighestAddrs() } cached := (*addrsCache)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.addrsCache)))) if cached == nil { cached = &addrsCache{} cached.lower, cached.upper = addr.createLowestHighestAddrs() dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.addrsCache)) atomicStorePointer(dataLoc, unsafe.Pointer(cached)) } lower, upper = cached.lower, cached.upper return } func (addr *addressInternal) createLowestHighestAddrs() (lower, upper *Address) { lower = addr.checkIdentity(addr.section.GetLower()) upper = addr.checkIdentity(addr.section.GetUpper()) return } func (addr *addressInternal) toMaxLower() *Address { section := addr.section if section == nil { return addr.toAddress() } return addr.checkIdentity(addr.section.toMaxLower()) } func (addr *addressInternal) toMinUpper() *Address { section := addr.section if section == nil { return addr.toAddress() } return addr.checkIdentity(addr.section.toMinUpper()) } // IsZero returns whether this address matches exactly the value of zero. func (addr *addressInternal) IsZero() bool { section := addr.section if section == nil { return true } return section.IsZero() } // IncludesZero returns whether this address includes the zero address within its range. func (addr *addressInternal) IncludesZero() bool { section := addr.section if section == nil { return true } return section.IncludesZero() } // IsFullRange returns whether this address covers the entire address space of this address version or type. // // This is true if and only if both IncludesZero and IncludesMax return true. func (addr *addressInternal) IsFullRange() bool { section := addr.section if section == nil { // when no bits, the only value 0 is the max value too return true } return section.IsFullRange() } func (addr *addressInternal) toAddress() *Address { return (*Address)(unsafe.Pointer(addr)) } func (addr *addressInternal) getDivision(index int) *AddressDivision { return addr.section.getDivision(index) } func (addr *addressInternal) getDivisionCount() int { if addr.section == nil { return 0 } return addr.section.GetDivisionCount() } func (addr *addressInternal) getDivisionsInternal() []*AddressDivision { return addr.section.getDivisionsInternal() } func (addr *addressInternal) toPrefixBlock() *Address { return addr.checkIdentity(addr.section.toPrefixBlock()) } func (addr *addressInternal) toPrefixBlockLen(prefLen BitCount) *Address { return addr.checkIdentity(addr.section.toPrefixBlockLen(prefLen)) } func (addr *addressInternal) toBlock(segmentIndex int, lower, upper SegInt) *Address { return addr.checkIdentity(addr.section.toBlock(segmentIndex, lower, upper)) } func (addr *addressInternal) reverseBytes() (*Address, addrerr.IncompatibleAddressError) { sect, err := addr.section.ReverseBytes() if err != nil { return nil, err } return addr.checkIdentity(sect), nil } func (addr *addressInternal) reverseBits(perByte bool) (*Address, addrerr.IncompatibleAddressError) { sect, err := addr.section.ReverseBits(perByte) if err != nil { return nil, err } return addr.checkIdentity(sect), nil } // reverseSegments returns a new address with the segments reversed. func (addr *addressInternal) reverseSegments() *Address { return addr.checkIdentity(addr.section.ReverseSegments()) } // isIPv4 returns whether this matches an IPv4 address. // we allow nil receivers to allow this to be called following a failed conversion like ToIP() func (addr *addressInternal) isIPv4() bool { return addr.section != nil && addr.section.matchesIPv4AddressType() } // isIPv6 returns whether this matches an IPv6 address. // we allow nil receivers to allow this to be called following a failed conversion like ToIP() func (addr *addressInternal) isIPv6() bool { return addr.section != nil && addr.section.matchesIPv6AddressType() } // isIPv6 returns whether this matches an IPv6 address. // we allow nil receivers to allow this to be called following a failed conversion like ToIP() func (addr *addressInternal) isMAC() bool { return addr.section != nil && addr.section.matchesMACAddressType() } // isIP returns whether this matches an IP address. // It must be IPv4, IPv6, or the zero IPAddress which has no segments // we allow nil receivers to allow this to be called following a failed conversion like ToIP() func (addr *addressInternal) isIP() bool { return addr.section == nil /* zero addr */ || addr.section.matchesIPAddressType() } func (addr *addressInternal) prefixEquals(other AddressType) bool { otherAddr := other.ToAddressBase() if addr.toAddress() == otherAddr { return true } otherSection := otherAddr.GetSection() if addr.section == nil { return otherSection.GetSegmentCount() == 0 } return addr.section.PrefixEqual(otherSection) && // if it is IPv6 and has a zone, then it does not contain addresses from other zones addr.isSameZone(otherAddr) } func (addr *addressInternal) prefixContains(other AddressType) bool { otherAddr := other.ToAddressBase() if addr.toAddress() == otherAddr { return true } otherSection := otherAddr.GetSection() if addr.section == nil { return otherSection.GetSegmentCount() == 0 } return addr.section.PrefixContains(otherSection) && // if it is IPv6 and has a zone, then it does not contain addresses from other zones addr.isSameZone(otherAddr) } func (addr *addressInternal) contains(other AddressType) bool { if other == nil { return true } otherAddr := other.ToAddressBase() if addr.toAddress() == otherAddr || otherAddr == nil { return true } otherSection := otherAddr.GetSection() if addr.section == nil { return otherSection.GetSegmentCount() == 0 } return addr.section.Contains(otherSection) && // if it is IPv6 and has a zone, then it does not contain addresses from other zones addr.isSameZone(otherAddr) } func (addr *addressInternal) equals(other AddressType) bool { if other == nil { return false } otherAddr := other.ToAddressBase() if addr.toAddress() == otherAddr { return true } else if otherAddr == nil { return false } otherSection := otherAddr.GetSection() if addr.section == nil { return otherSection.GetSegmentCount() == 0 } return addr.section.Equal(otherSection) && // if it it is IPv6 and has a zone, then it does not equal addresses from other zones addr.isSameZone(otherAddr) } func (addr *addressInternal) equalsSameVersion(other AddressType) bool { otherAddr := other.ToAddressBase() if addr.toAddress() == otherAddr { return true } else if otherAddr == nil { return false } otherSection := otherAddr.GetSection() return addr.section.sameCountTypeEquals(otherSection) && // if it it is IPv6 and has a zone, then it does not equal addresses from other zones addr.isSameZone(otherAddr) } // withoutPrefixLen returns the same address but with no associated prefix length. func (addr *addressInternal) withoutPrefixLen() *Address { return addr.checkIdentity(addr.section.withoutPrefixLen()) } func (addr *addressInternal) adjustPrefixLen(prefixLen BitCount) *Address { return addr.checkIdentity(addr.section.adjustPrefixLen(prefixLen)) } func (addr *addressInternal) adjustPrefixLenZeroed(prefixLen BitCount) (res *Address, err addrerr.IncompatibleAddressError) { section, err := addr.section.adjustPrefixLenZeroed(prefixLen) if err == nil { res = addr.checkIdentity(section) } return } func (addr *addressInternal) setPrefixLen(prefixLen BitCount) *Address { return addr.checkIdentity(addr.section.setPrefixLen(prefixLen)) } func (addr *addressInternal) setPrefixLenZeroed(prefixLen BitCount) (res *Address, err addrerr.IncompatibleAddressError) { section, err := addr.section.setPrefixLenZeroed(prefixLen) if err == nil { res = addr.checkIdentity(section) } return } func (addr *addressInternal) assignPrefixForSingleBlock() *Address { newPrefix := addr.GetPrefixLenForSingleBlock() if newPrefix == nil { return nil } return addr.checkIdentity(addr.section.setPrefixLen(newPrefix.bitCount())) } // assignMinPrefixForBlock constructs an equivalent address section with the smallest CIDR prefix possible (largest network), // such that the range of values are a set of subnet blocks for that prefix. func (addr *addressInternal) assignMinPrefixForBlock() *Address { return addr.setPrefixLen(addr.GetMinPrefixLenForBlock()) } // toSingleBlockOrAddress converts to a single prefix block or address. // If the given address is a single prefix block, it is returned. // If it can be converted to a single prefix block by assigning a prefix length, the converted block is returned. // If it is a single address, any prefix length is removed and the address is returned. // Otherwise, nil is returned. func (addr *addressInternal) toSinglePrefixBlockOrAddr() *Address { if !addr.isMultiple() { if !addr.isPrefixed() { return addr.toAddress() } return addr.withoutPrefixLen() //} else if addr.IsSinglePrefixBlock() { // return addr.toAddress() } else { series := addr.assignPrefixForSingleBlock() if series != nil { return series } } return nil } func (addr *addressInternal) isSameZone(other *Address) bool { return addr.zone == other.ToAddressBase().zone } func (addr *addressInternal) getAddrType() addrType { if addr.section == nil { return zeroType } return addr.section.addrType } // equivalent to section.sectionIterator func (addr *addressInternal) addrIterator(excludeFunc func([]*AddressDivision) bool) Iterator[*Address] { useOriginal := !addr.isMultiple() original := addr.toAddress() var iterator Iterator[[]*AddressDivision] if useOriginal { if excludeFunc != nil && excludeFunc(addr.getDivisionsInternal()) { original = nil // the single-valued iterator starts out empty } } else { address := addr.toAddress() iterator = allSegmentsIterator( addr.getDivisionCount(), nil, func(index int) Iterator[*AddressSegment] { return address.getSegment(index).iterator() }, excludeFunc) } return addrIterator( useOriginal, original, original.getPrefixLen(), false, iterator) } func (addr *addressInternal) prefixIterator(isBlockIterator bool) Iterator[*Address] { prefLen := addr.getPrefixLen() if prefLen == nil { return addr.addrIterator(nil) } var useOriginal bool if isBlockIterator { useOriginal = addr.IsSinglePrefixBlock() } else { useOriginal = bigIsOne(addr.GetPrefixCount()) } prefLength := prefLen.bitCount() bitsPerSeg := addr.GetBitsPerSegment() bytesPerSeg := addr.GetBytesPerSegment() networkSegIndex := getNetworkSegmentIndex(prefLength, bytesPerSeg, bitsPerSeg) hostSegIndex := getHostSegmentIndex(prefLength, bytesPerSeg, bitsPerSeg) segCount := addr.getDivisionCount() var iterator Iterator[[]*AddressDivision] address := addr.toAddress() if !useOriginal { var hostSegIteratorProducer func(index int) Iterator[*AddressSegment] if isBlockIterator { hostSegIteratorProducer = func(index int) Iterator[*AddressSegment] { seg := address.getSegment(index) if seg.isPrefixed() { // IP address segments know their own prefix, MAC segments do not return seg.prefixBlockIterator() } segPref := getPrefixedSegmentPrefixLength(bitsPerSeg, prefLength, index) return seg.prefixedBlockIterator(segPref.bitCount()) } } else { hostSegIteratorProducer = func(index int) Iterator[*AddressSegment] { seg := address.getSegment(index) if seg.isPrefixed() { // IP address segments know their own prefix, MACS segments do not return seg.prefixIterator() } segPref := getPrefixedSegmentPrefixLength(bitsPerSeg, prefLength, index) return seg.prefixedIterator(segPref.bitCount()) } } iterator = segmentsIterator( segCount, nil, //when no prefix we defer to other iterator, when there is one we use the whole original section in the encompassing iterator and not just the original segments func(index int) Iterator[*AddressSegment] { return address.getSegment(index).iterator() }, nil, networkSegIndex, hostSegIndex, hostSegIteratorProducer) } if isBlockIterator { return addrIterator( useOriginal, address, address.getPrefixLen(), prefLength < addr.GetBitCount(), iterator) } return prefixAddrIterator( useOriginal, address, address.getPrefixLen(), iterator) } func (addr *addressInternal) blockIterator(segmentCount int) Iterator[*Address] { if segmentCount < 0 { segmentCount = 0 } allSegsCount := addr.getDivisionCount() if segmentCount >= allSegsCount { return addr.addrIterator(nil) } useOriginal := !addr.section.isMultipleTo(segmentCount) address := addr.toAddress() var iterator Iterator[[]*AddressDivision] if !useOriginal { var hostSegIteratorProducer func(index int) Iterator[*AddressSegment] hostSegIteratorProducer = func(index int) Iterator[*AddressSegment] { return address.getSegment(index).identityIterator() } segIteratorProducer := func(index int) Iterator[*AddressSegment] { return address.getSegment(index).iterator() } iterator = segmentsIterator( allSegsCount, nil, //when no prefix we defer to other iterator, when there is one we use the whole original section in the encompassing iterator and not just the original segments segIteratorProducer, nil, segmentCount-1, segmentCount, hostSegIteratorProducer) } return addrIterator( useOriginal, address, address.getPrefixLen(), addr.section.isMultipleFrom(segmentCount), iterator) } // sequentialBlockIterator iterates through the minimal number of maximum-sized blocks comprising this subnet // a block is sequential if given any two addresses in the block, any intervening address between the two is also in the block func (addr *addressInternal) sequentialBlockIterator() Iterator[*Address] { return addr.blockIterator(addr.getSequentialBlockIndex()) } func (addr *addressInternal) getSequentialBlockIndex() int { if addr.section == nil { return 0 } return addr.section.GetSequentialBlockIndex() } func (addr *addressInternal) getSequentialBlockCount() *big.Int { if addr.section == nil { return bigOne() } return addr.section.GetSequentialBlockCount() } func (addr *addressInternal) hasZone() bool { return addr.zone != NoZone } func (addr *addressInternal) increment(increment int64) *Address { return addr.checkIdentity(addr.section.increment(increment)) } func (addr *addressInternal) incrementBoundary(increment int64) *Address { return addr.checkIdentity(addr.section.incrementBoundary(increment)) } func (addr *addressInternal) getStringCache() *stringCache { cache := addr.cache if cache == nil { return nil } return addr.cache.stringCache } func (addr *addressInternal) getSegmentStrings() []string { return addr.section.getSegmentStrings() } func (addr *addressInternal) toCanonicalString() string { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.ToIPv6().toCanonicalString(addr.zone) } return cacheStr(&cache.canonicalString, func() string { return addr.section.ToIPv6().toCanonicalString(addr.zone) }) } return addr.section.ToCanonicalString() } func (addr *addressInternal) toNormalizedString() string { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.ToIPv6().toNormalizedString(addr.zone) } return cacheStr(&cache.normalizedIPv6String, func() string { return addr.section.ToIPv6().toNormalizedString(addr.zone) }) } return addr.section.ToNormalizedString() } func (addr *addressInternal) toNormalizedWildcardString() string { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.ToIPv6().toNormalizedWildcardStringZoned(addr.zone) } return cacheStr(&cache.normalizedIPv6String, func() string { return addr.section.ToIPv6().toNormalizedWildcardStringZoned(addr.zone) }) } return addr.section.ToNormalizedWildcardString() } func (addr *addressInternal) toCompressedString() string { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.ToIPv6().toCompressedString(addr.zone) } return cacheStr(&cache.compressedIPv6String, func() string { return addr.section.ToIPv6().toCompressedString(addr.zone) }) } return addr.section.ToCompressedString() } func (addr *addressInternal) toOctalString(with0Prefix bool) (string, addrerr.IncompatibleAddressError) { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.toOctalStringZoned(with0Prefix, addr.zone) } var cacheField **string if with0Prefix { cacheField = &cache.octalStringPrefixed } else { cacheField = &cache.octalString } return cacheStrErr(cacheField, func() (string, addrerr.IncompatibleAddressError) { return addr.section.toOctalStringZoned(with0Prefix, addr.zone) }) } return addr.section.ToOctalString(with0Prefix) } func (addr *addressInternal) toBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.toBinaryStringZoned(with0bPrefix, addr.zone) } var cacheField **string if with0bPrefix { cacheField = &cache.binaryStringPrefixed } else { cacheField = &cache.binaryString } return cacheStrErr(cacheField, func() (string, addrerr.IncompatibleAddressError) { return addr.section.toBinaryStringZoned(with0bPrefix, addr.zone) }) } return addr.section.ToBinaryString(with0bPrefix) } func (addr *addressInternal) toHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.toHexStringZoned(with0xPrefix, addr.zone) } var cacheField **string if with0xPrefix { cacheField = &cache.hexStringPrefixed } else { cacheField = &cache.hexString } return cacheStrErr(cacheField, func() (string, addrerr.IncompatibleAddressError) { return addr.section.toHexStringZoned(with0xPrefix, addr.zone) }) } return addr.section.ToHexString(with0xPrefix) } func (addr *addressInternal) format(state fmt.State, verb rune) { section := addr.section section.format(state, verb, addr.zone, addr.isIP()) } var zeroAddr = createAddress(zeroSection, NoZone) // Address represents a single address, or a collection of multiple addresses, such as with an IP subnet or a set of MAC addresses. // // Addresses consist of a sequence of segments, each of equal bit-size. // The number of such segments and the bit-size are determined by the underlying version or type of the address, whether IPv4, IPv6, MAC, or other. // Each segment can represent a single value or a sequential range of values. Addresses can also have an associated prefix length, // which is the number of consecutive bits comprising the prefix, the most significant bits of an address. // // To construct one from a string, use // NewIPAddressString or NewMACAddressString, // then use the ToAddress or GetAddress methods to get an [IPAddress] or [MACAddress], // and then you can convert to this type using the ToAddressBase method. // // Any given specific address types can be converted to Address with the ToAddressBase method, // and then back again to their original types with methods like ToIPv6, ToIP, ToIPv4, and ToMAC. // When calling such a method on a given address, if the address was not originally constructed as the type returned from the method, // then the method will return nil. Conversion methods work with nil pointers (returning nil) so that they can be chained together safely. // // This allows for polymorphic code that works with all addresses, such as with the address trie code in this library, // while still allowing for methods and code specific to each address version or type. // // You can also use the methods IsIPv6, IsIP, IsIPv4, and IsMAC, // which will return true if and only if the corresponding method ToIPv6, ToIP, ToIPv4, and ToMAC returns non-nil, respectively. // // The zero value for an Address is an address with no segments and no associated address version or type, also known as the adaptive zero. type Address struct { addressInternal } func (addr *Address) init() *Address { if addr.section == nil { return zeroAddr // this has a zero section rather that a nil section } return addr } // GetCount returns the count of addresses that this address or subnet represents. // // If just a single address, not a collection nor subnet of multiple addresses, returns 1. // // For instance, the IP address subnet "2001:db8::/64" has the count of 2 to the power of 64. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (addr *Address) GetCount() *big.Int { if addr == nil { return bigZero() } return addr.getCount() } // IsMultiple returns true if this represents more than a single individual address, whether it is a collection or subnet of multiple addresses. func (addr *Address) IsMultiple() bool { return addr != nil && addr.isMultiple() } // IsPrefixed returns whether this address has an associated prefix length. func (addr *Address) IsPrefixed() bool { return addr != nil && addr.isPrefixed() } // PrefixEqual determines if the given address matches this address up to the prefix length of this address. // It returns whether the two addresses share the same range of prefix values. func (addr *Address) PrefixEqual(other AddressType) bool { return addr.init().prefixEquals(other) } // PrefixContains returns whether the prefix values in the given address or subnet // are prefix values in this address or subnet, using the prefix length of this address or subnet. // If this address has no prefix length, the entire address is compared. // // It returns whether the prefix of this address contains all values of the same prefix length in the given address. func (addr *Address) PrefixContains(other AddressType) bool { return addr.init().prefixContains(other) } // Contains returns whether this is the same type and version as the given address or subnet and whether it contains all addresses in the given address or subnet. func (addr *Address) Contains(other AddressType) bool { if addr == nil { return other == nil || other.ToAddressBase() == nil } return addr.init().contains(other) } // Compare returns a negative integer, zero, or a positive integer if this address or subnet is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (addr *Address) Compare(item AddressItem) int { return CountComparator.Compare(addr, item) } // Equal returns whether the given address or subnet is equal to this address or subnet. // Two address instances are equal if they represent the same set of addresses. func (addr *Address) Equal(other AddressType) bool { if addr == nil { return other == nil || other.ToAddressBase() == nil } else if other.ToAddressBase() == nil { return false } return addr.init().equals(other) } // CompareSize compares the counts of two subnets or addresses or other address items, the number of individual items within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether one subnet or collection represents more individual items than another. // // CompareSize returns a positive integer if this address or subnet has a larger count than the item given, zero if they are the same, or a negative integer if the other has a larger count. func (addr *Address) CompareSize(other AddressItem) int { if addr == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return addr.init().compareSize(other) } // TrieCompare compares two addresses according to address trie ordering. // It returns a number less than zero, zero, or a number greater than zero if the first address argument is less than, equal to, or greater than the second. // // The comparison is intended for individual addresses and CIDR prefix blocks. // If an address is neither an individual address nor a prefix block, it is treated like one: // // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *Address) TrieCompare(other *Address) (int, addrerr.IncompatibleAddressError) { if thisAddr := addr.ToIPv4(); thisAddr != nil { if oth := other.ToIPv4(); oth != nil { return thisAddr.TrieCompare(oth), nil } } else if thisAddr := addr.ToIPv6(); thisAddr != nil { if oth := other.ToIPv6(); oth != nil { return thisAddr.TrieCompare(oth), nil } } else if thisAddr := addr.ToMAC(); thisAddr != nil { if oth := other.ToMAC(); oth != nil { return thisAddr.TrieCompare(oth) } } if segmentCount, otherSegmentCount := addr.getDivisionCount(), other.getDivisionCount(); segmentCount == otherSegmentCount { if bitsPerSegment, otherBitsPerSegment := addr.GetBitsPerSegment(), other.GetBitsPerSegment(); bitsPerSegment == otherBitsPerSegment { return addr.trieCompare(other), nil } } return 0, &incompatibleAddressError{addressError{key: "ipaddress.error.mismatched.bit.size"}} } // TrieIncrement returns the next address or block according to address trie ordering. // // If an address is neither an individual address nor a prefix block, it is treated like one: // // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *Address) TrieIncrement() *Address { if res, ok := trieIncrement(addr); ok { return res } return nil } // TrieDecrement returns the previous or block address according to address trie ordering. // // If an address is neither an individual address nor a prefix block, it is treated like one: // // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *Address) TrieDecrement() *Address { if res, ok := trieDecrement(addr); ok { return res } return nil } // GetSection returns the backing section for this address or subnet, comprising all segments. func (addr *Address) GetSection() *AddressSection { return addr.init().section } // GetTrailingSection gets the subsection from the series starting from the given index. // The first segment is at index 0. func (addr *Address) GetTrailingSection(index int) *AddressSection { return addr.GetSection().GetTrailingSection(index) } // GetSubSection gets the subsection from the series starting from the given index and ending just before the give endIndex. // The first segment is at index 0. func (addr *Address) GetSubSection(index, endIndex int) *AddressSection { return addr.GetSection().GetSubSection(index, endIndex) } // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (addr *Address) CopySubSegments(start, end int, segs []*AddressSegment) (count int) { return addr.GetSection().CopySubSegments(start, end, segs) } // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. func (addr *Address) CopySegments(segs []*AddressSegment) (count int) { return addr.GetSection().CopySegments(segs) } // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as this section. func (addr *Address) GetSegments() []*AddressSegment { return addr.GetSection().GetSegments() } // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. func (addr *Address) GetSegment(index int) *AddressSegment { return addr.getSegment(index) } // GetSegmentCount returns the segment count, the number of segments in this address. // For example, IPv4 addresses have 4, IPv6 addresses have 8. func (addr *Address) GetSegmentCount() int { return addr.getDivisionCount() } // ForEachSegment visits each segment in order from most-significant to least, the most significant with index 0, calling the given function for each, terminating early if the function returns true. // Returns the number of visited segments. func (addr *Address) ForEachSegment(consumer func(segmentIndex int, segment *AddressSegment) (stop bool)) int { return addr.GetSection().ForEachSegment(consumer) } // GetGenericDivision returns the segment at the given index as a DivisionType. // The first segment is at index 0. // GetGenericDivision will panic given a negative index or index larger than the division count. func (addr *Address) GetGenericDivision(index int) DivisionType { return addr.getDivision(index) } // GetGenericSegment returns the segment at the given index as an AddressSegmentType. // The first segment is at index 0. // GetGenericSegment will panic given a negative index or an index matching or larger than the segment count. func (addr *Address) GetGenericSegment(index int) AddressSegmentType { return addr.getSegment(index) } // GetDivisionCount returns the division count, which is the same as the segment count, since the divisions of an address are the segments. func (addr *Address) GetDivisionCount() int { return addr.getDivisionCount() } // TestBit returns true if the bit in the lower value of this address at the given index is 1, where index 0 refers to the least significant bit. // In other words, it computes (bits & (1 << n)) != 0), using the lower value of this address. // TestBit will panic if n < 0, or if it matches or exceeds the bit count of this item. func (addr *Address) TestBit(n BitCount) bool { return addr.init().testBit(n) } // IsOneBit returns true if the bit in the lower value of this address at the given index is 1, where index 0 refers to the most significant bit. // IsOneBit will panic if bitIndex is less than zero, or if it is larger than the bit count of this item. func (addr *Address) IsOneBit(bitIndex BitCount) bool { return addr.init().isOneBit(bitIndex) } // GetLower returns the address in the subnet or address collection with the lowest numeric value, // which will be the receiver if it represents a single address. // For example, for "1.2-3.4.5-6", the series "1.2.4.5" is returned. func (addr *Address) GetLower() *Address { return addr.init().getLower() } // GetUpper returns the address in the subnet or address collection with the highest numeric value, // which will be the receiver if it represents a single address. // For example, for the subnet "1.2-3.4.5-6", the address "1.3.4.6" is returned. func (addr *Address) GetUpper() *Address { return addr.init().getUpper() } // GetValue returns the lowest address in this subnet or address collection as an integer value. func (addr *Address) GetValue() *big.Int { return addr.init().section.GetValue() } // GetUpperValue returns the highest address in this subnet or address collection as an integer value. func (addr *Address) GetUpperValue() *big.Int { return addr.init().section.GetUpperValue() } // Bytes returns the lowest address in this subnet or address collection as a byte slice. func (addr *Address) Bytes() []byte { return addr.init().section.Bytes() } // UpperBytes returns the highest address in this subnet or address collection as a byte slice. func (addr *Address) UpperBytes() []byte { return addr.init().section.UpperBytes() } // CopyBytes copies the value of the lowest individual address in the subnet into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *Address) CopyBytes(bytes []byte) []byte { return addr.init().section.CopyBytes(bytes) } // CopyUpperBytes copies the value of the highest individual address in the subnet into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *Address) CopyUpperBytes(bytes []byte) []byte { return addr.init().section.CopyUpperBytes(bytes) } // IsMax returns whether this address matches exactly the maximum possible value, the address whose bits are all ones. func (addr *Address) IsMax() bool { return addr.init().section.IsMax() } // IncludesMax returns whether this address includes the max address, the address whose bits are all ones, within its range. func (addr *Address) IncludesMax() bool { return addr.init().section.IncludesMax() } // ToPrefixBlock returns the address collection associated with the prefix of this address or address collection, // the address whose prefix matches the prefix of this address, and the remaining bits span all values. // If this address has no prefix length, this address is returned. // // The returned address collection will include all addresses with the same prefix as this one, the prefix "block". func (addr *Address) ToPrefixBlock() *Address { return addr.init().toPrefixBlock() } // ToPrefixBlockLen returns the address associated with the prefix length provided, // the address collection whose prefix of that length matches the prefix of this address, and the remaining bits span all values. // // The returned address will include all addresses with the same prefix as this one, the prefix "block". func (addr *Address) ToPrefixBlockLen(prefLen BitCount) *Address { return addr.init().toPrefixBlockLen(prefLen) } // ToBlock creates a new block of addresses by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (addr *Address) ToBlock(segmentIndex int, lower, upper SegInt) *Address { return addr.init().toBlock(segmentIndex, lower, upper) } // WithoutPrefixLen provides the same address but with no prefix length. The values remain unchanged. func (addr *Address) WithoutPrefixLen() *Address { if !addr.IsPrefixed() { return addr } return addr.init().withoutPrefixLen() } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address. // The provided prefix length will be adjusted to these boundaries if necessary. func (addr *Address) SetPrefixLen(prefixLen BitCount) *Address { return addr.init().setPrefixLen(prefixLen) } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this address has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this address has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr *Address) SetPrefixLenZeroed(prefixLen BitCount) (*Address, addrerr.IncompatibleAddressError) { return addr.init().setPrefixLenZeroed(prefixLen) } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address. // // If this address has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (addr *Address) AdjustPrefixLen(prefixLen BitCount) *Address { return addr.adjustPrefixLen(prefixLen).ToAddressBase() } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address. // // If this address has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // For example, "1.2.0.0/16" adjusted by -8 becomes "1.0.0.0/8". // "1.2.0.0/16" adjusted by 8 becomes "1.2.0.0/24". // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr *Address) AdjustPrefixLenZeroed(prefixLen BitCount) (*Address, addrerr.IncompatibleAddressError) { res, err := addr.adjustPrefixLenZeroed(prefixLen) return res.ToAddressBase(), err } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this address. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such address - it is required that the range of values match the range of a prefix block. // If there is no such address, then nil is returned. // // Examples: // - 1.2.3.4 returns 1.2.3.4/32 // - 1.2.*.* returns 1.2.0.0/16 // - 1.2.*.0/24 returns 1.2.0.0/16 // - 1.2.*.4 returns nil // - 1.2.0-1.* returns 1.2.0.0/23 // - 1.2.1-2.* returns nil // - 1.2.252-255.* returns 1.2.252.0/22 // - 1.2.3.4/16 returns 1.2.3.4/32 func (addr *Address) AssignPrefixForSingleBlock() *Address { return addr.init().assignPrefixForSingleBlock() } // AssignMinPrefixForBlock returns an equivalent subnet, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this subnet. // // In other words, this method assigns a prefix length to this subnet matching the largest prefix block in this subnet. // // Examples: // - 1.2.3.4 returns 1.2.3.4/32 // - 1.2.*.* returns 1.2.0.0/16 // - 1.2.*.0/24 returns 1.2.0.0/16 // - 1.2.*.4 returns 1.2.*.4/32 // - 1.2.0-1.* returns 1.2.0.0/23 // - 1.2.1-2.* returns 1.2.1-2.0/24 // - 1.2.252-255.* returns 1.2.252.0/22 // - 1.2.3.4/16 returns 1.2.3.4/32 func (addr *Address) AssignMinPrefixForBlock() *Address { return addr.init().assignMinPrefixForBlock() } // ToSinglePrefixBlockOrAddress converts to a single prefix block or address. // If the given address is a single prefix block, it is returned. // If it can be converted to a single prefix block by assigning a prefix length, the converted block is returned. // If it is a single address, any prefix length is removed and the address is returned. // Otherwise, nil is returned. // This method provides the address formats used by tries. // ToSinglePrefixBlockOrAddress is quite similar to AssignPrefixForSingleBlock, which always returns prefixed addresses, while this does not. func (addr *Address) ToSinglePrefixBlockOrAddress() *Address { return addr.init().toSinglePrefixBlockOrAddr() } func (addr *Address) toSinglePrefixBlockOrAddress() (*Address, addrerr.IncompatibleAddressError) { if addr == nil { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.address.not.block"}} } res := addr.ToSinglePrefixBlockOrAddress() if res == nil { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.address.not.block"}} } return res, nil } // GetMaxSegmentValue returns the maximum possible segment value for this type of address. // // Note this is not the maximum of the range of segment values in this specific address, // this is the maximum value of any segment for this address type and version, determined by the number of bits per segment. func (addr *Address) GetMaxSegmentValue() SegInt { return addr.init().getMaxSegmentValue() } // Iterator provides an iterator to iterate through the individual addresses of this address or subnet. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual addresses. // // Call IsMultiple to determine if this instance represents multiple addresses, or GetCount for the count. func (addr *Address) Iterator() Iterator[*Address] { if addr == nil { return nilAddrIterator() } return addr.addrIterator(nil) } // PrefixIterator provides an iterator to iterate through the individual prefixes of this subnet, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this subnet. // // If the subnet has no prefix length, then this is equivalent to Iterator. func (addr *Address) PrefixIterator() Iterator[*Address] { return addr.prefixIterator(false) } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address or subnet. // Each iterated address or subnet will be a prefix block with the same prefix length as this address or subnet. // // If this address has no prefix length, then this is equivalent to Iterator. func (addr *Address) PrefixBlockIterator() Iterator[*Address] { return addr.prefixIterator(true) } // BlockIterator iterates through the addresses that can be obtained by iterating through all the upper segments up to the given segment count. // The segments following remain the same in all iterated addresses. // // For instance, given the IPv4 subnet "1-2.3-4.5-6.7" and the count argument 2, // BlockIterator will iterate through "1.3.5-6.7", "1.4.5-6.7", "2.3.5-6.7" and "2.4.5-6.7". func (addr *Address) BlockIterator(segmentCount int) Iterator[*Address] { return addr.init().blockIterator(segmentCount) } // SequentialBlockIterator iterates through the sequential subnets or addresses that make up this address or subnet. // // Practically, this means finding the count of segments for which the segments that follow are not full range, and then using BlockIterator with that segment count. // // For instance, given the IPv4 subnet "1-2.3-4.5-6.7-8", it will iterate through "1.3.5.7-8", "1.3.6.7-8", "1.4.5.7-8", "1.4.6.7-8", "2.3.5.7-8", "2.3.6.7-8", "2.4.6.7-8" and "2.4.6.7-8". // // Use GetSequentialBlockCount to get the number of iterated elements. func (addr *Address) SequentialBlockIterator() Iterator[*Address] { return addr.init().sequentialBlockIterator() } // GetSequentialBlockIndex gets the minimal segment index for which all following segments are full-range blocks. // // The segment at this index is not a full-range block itself, unless all segments are full-range. // The segment at this index and all following segments form a sequential range. // For the full subnet to be sequential, the preceding segments must be single-valued. func (addr *Address) GetSequentialBlockIndex() int { return addr.getSequentialBlockIndex() } // GetSequentialBlockCount provides the count of elements from the sequential block iterator, the minimal number of sequential subnets that comprise this subnet. func (addr *Address) GetSequentialBlockCount() *big.Int { return addr.getSequentialBlockCount() } // IncrementBoundary returns the address that is the given increment from the range boundaries of this subnet or address collection. // // If the given increment is positive, adds the value to the upper address (GetUpper) in the range to produce a new address. // If the given increment is negative, adds the value to the lower address (GetLower) in the range to produce a new address. // If the increment is zero, returns this address. // // If this is a single address value, that address is simply incremented by the given increment value, positive or negative. // // On address overflow or underflow, IncrementBoundary returns nil. func (addr *Address) IncrementBoundary(increment int64) *Address { return addr.init().IncrementBoundary(increment) } // Increment returns the address from the subnet that is the given increment upwards into the subnet range, // with the increment of 0 returning the first address in the range. // // If the increment i matches or exceeds the subnet size count c, then i - c + 1 // is added to the upper address of the range. // An increment matching the subnet count gives you the address just above the highest address in the subnet. // // If the increment is negative, it is added to the lower address of the range. // To get the address just below the lowest address of the subnet, use the increment -1. // // If this is just a single address value, the address is simply incremented by the given increment, positive or negative. // // If this is a subnet with multiple values, a positive increment i is equivalent i + 1 values from the subnet iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the subnet count is equivalent to the same number of iterator values preceding the upper bound of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On address overflow or underflow, Increment returns nil. func (addr *Address) Increment(increment int64) *Address { return addr.init().increment(increment) } // ReverseBytes returns a new address with the bytes reversed. Any prefix length is dropped. // // If each segment is more than 1 byte long, and the bytes within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, then this returns an error. // // In practice this means that to be reversible, a segment range must include all values except possibly the largest and/or smallest, which reverse to themselves. func (addr *Address) ReverseBytes() (*Address, addrerr.IncompatibleAddressError) { return addr.init().reverseBytes() } // ReverseBits returns a new address with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a segment range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (addr *Address) ReverseBits(perByte bool) (*Address, addrerr.IncompatibleAddressError) { return addr.init().reverseBits(perByte) } // ReverseSegments returns a new address with the segments reversed. func (addr *Address) ReverseSegments() *Address { return addr.init().reverseSegments() } // IsMulticast returns whether this address is multicast. func (addr *Address) IsMulticast() bool { if thisAddr := addr.ToIPv4(); thisAddr != nil { return thisAddr.IsMulticast() } else if thisAddr := addr.ToIPv6(); thisAddr != nil { return thisAddr.IsMulticast() } else if thisAddr := addr.ToMAC(); thisAddr != nil { return thisAddr.IsMulticast() } return false } // IsLocal returns whether the address can be considered a local address (as opposed to a global one). func (addr *Address) IsLocal() bool { if thisAddr := addr.ToIPv4(); thisAddr != nil { return thisAddr.IsLocal() } else if thisAddr := addr.ToIPv6(); thisAddr != nil { return thisAddr.IsLocal() } else if thisAddr := addr.ToMAC(); thisAddr != nil { return thisAddr.IsLocal() } return false } // GetLeadingBitCount returns the number of consecutive leading one or zero bits. // If ones is true, returns the number of consecutive leading one bits. // Otherwise, returns the number of consecutive leading zero bits. // // This method applies to the lower address of the range if this is a subnet representing multiple values. func (addr *Address) GetLeadingBitCount(ones bool) BitCount { return addr.init().getLeadingBitCount(ones) } // GetTrailingBitCount returns the number of consecutive trailing one or zero bits. // If ones is true, returns the number of consecutive trailing zero bits. // Otherwise, returns the number of consecutive trailing one bits. // // This method applies to the lower value of the range if this is a subnet representing multiple values. func (addr *Address) GetTrailingBitCount(ones bool) BitCount { return addr.init().getTrailingBitCount(ones) } // Format implements [fmt.Formatter] interface. It accepts the formats // - 'v' for the default address and section format (either the normalized or canonical string), // - 's' (string) for the same, // - 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix), // - 'd' (decimal), 'x' (lowercase hexadecimal), and // - 'X' (uppercase hexadecimal). // Also supported are some of fmt's format flags for integral types. // Sign control is not supported since addresses and sections are never negative. // '#' for an alternate format is supported, which adds a leading zero for octal, and for hexadecimal it adds // a leading "0x" or "0X" for "%#x" and "%#X" respectively. // Also supported is specification of minimum digits precision, output field width, // space or zero padding, and '-' for left or right justification. func (addr Address) Format(state fmt.State, verb rune) { addr.init().format(state, verb) } // String implements the [fmt.Stringer] interface, returning the canonical string provided by ToCanonicalString, or "" if the receiver is a nil pointer. func (addr *Address) String() string { if addr == nil { return nilString() } return addr.init().toString() } // GetSegmentStrings returns a slice with the string for each segment being the string that is normalized with wildcards. func (addr *Address) GetSegmentStrings() []string { if addr == nil { return nil } return addr.init().getSegmentStrings() } // ToCanonicalString produces a canonical string for the address. // // For IPv4, dotted octet format, also known as dotted decimal format, is used. // https://datatracker.ietf.org/doc/html/draft-main-ipaddr-text-rep-00#section-2.1 // // For IPv6, RFC 5952 describes canonical string representation. // https://en.wikipedia.org/wiki/IPv6_address#Representation // http://tools.ietf.org/html/rfc5952 // // For MAC, it uses the canonical standardized IEEE 802 MAC address representation of xx-xx-xx-xx-xx-xx. An example is "01-23-45-67-89-ab". // For range segments, '|' is used: "11-22-33|44-55-66". // // Each address has a unique canonical string, not counting the prefix length. // With IP addresses, the prefix length is included in the string, and the prefix length can cause two equal addresses to have different strings, for example "1.2.3.4/16" and "1.2.3.4". // It can also cause two different addresses to have the same string, such as "1.2.0.0/16" for the individual address "1.2.0.0" and also the prefix block "1.2.*.*". // Use the IPAddress method ToCanonicalWildcardString for a unique string for each IP address and subnet. func (addr *Address) ToCanonicalString() string { if addr == nil { return nilString() } return addr.init().toCanonicalString() } // ToNormalizedString produces a normalized string for the address. // // For IPv4, it is the same as the canonical string. // // For IPv6, it differs from the canonical string. Zero-segments are not compressed. // // For MAC, it differs from the canonical string. It uses the most common representation of MAC addresses: "xx:xx:xx:xx:xx:xx". An example is "01:23:45:67:89:ab". // For range segments, '-' is used: "11:22:33-44:55:66". // // Each address has a unique normalized string, not counting the prefix length. // With IP addresses, the prefix length can cause two equal addresses to have different strings, for example "1.2.3.4/16" and "1.2.3.4". // It can also cause two different addresses to have the same string, such as "1.2.0.0/16" for the individual address "1.2.0.0" and also the prefix block "1.2.*.*". // Use the IPAddress method ToNormalizedWildcardString for a unique string for each IP address and subnet. func (addr *Address) ToNormalizedString() string { if addr == nil { return nilString() } return addr.init().toNormalizedString() } // ToNormalizedWildcardString produces a string similar to the normalized string but avoids the CIDR prefix length in IP addresses. // Multi-valued segments will be shown with wildcards and ranges (denoted by '*' and '-'). func (addr *Address) ToNormalizedWildcardString() string { if addr == nil { return nilString() } return addr.init().toNormalizedWildcardString() } // ToCompressedString produces a short representation of this address while remaining within the confines of standard representation(s) of the address. // // For IPv4, it is the same as the canonical string. // // For IPv6, it differs from the canonical string. It compresses the maximum number of zeros and/or host segments with the IPv6 compression notation '::'. // // For MAC, it differs from the canonical string. It produces a shorter string for the address that has no leading zeros. func (addr *Address) ToCompressedString() string { if addr == nil { return nilString() } return addr.init().toCompressedString() } // ToHexString writes this address as a single hexadecimal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // If an address collection cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *Address) ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toHexString(with0xPrefix) } // ToOctalString writes this address as a single octal value (possibly two values if a range), // the number of digits according to the bit count, with or without a preceding "0" prefix. // // If an address collection cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *Address) ToOctalString(with0Prefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toOctalString(with0Prefix) } // ToBinaryString writes this address as a single binary value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0b" prefix. // // If a subnet cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *Address) ToBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toBinaryString(with0bPrefix) } // ToCustomString creates a customized string from this address or subnet according to the given string option parameters. func (addr *Address) ToCustomString(stringOptions addrstr.StringOptions) string { if addr == nil { return nilString() } return addr.GetSection().toCustomStringZoned(stringOptions, addr.zone) } // ToAddressString retrieves or generates a HostIdentifierString instance for this Address object. // // This same Address instance can be retrieved from the resulting HostIdentifierString object using the GetAddress method. // // In general, users create Address instances from IPAddressString or MACAddressString instances, // while the reverse direction is generally not common and not useful. // // However, the reverse direction can be useful under certain circumstances, such as when maintaining a collection of HostIdentifierString instances. func (addr *Address) ToAddressString() HostIdentifierString { if addr.isIP() { return addr.ToIP().ToAddressString() } else if addr.isMAC() { return addr.ToMAC().ToAddressString() } return nil } // IsIPv4 returns true if this address or subnet originated as an IPv4 address or subnet. If so, use ToIPv4 to convert back to the IPv4-specific type. func (addr *Address) IsIPv4() bool { return addr != nil && addr.isIPv4() } // IsIPv6 returns true if this address or subnet originated as an IPv6 address or subnet. If so, use ToIPv6 to convert back to the IPv6-specific type. func (addr *Address) IsIPv6() bool { return addr != nil && addr.isIPv6() } // IsIP returns true if this address or subnet originated as an IPv4 or IPv6 address or subnet, or an implicitly zero-valued IP. If so, use ToIP to convert back to the IP-specific type. func (addr *Address) IsIP() bool { return addr != nil && addr.isIP() } // IsMAC returns true if this address or address collection originated as a MAC address or address collection. If so, use ToMAC to convert back to the MAC-specific type. func (addr *Address) IsMAC() bool { return addr != nil && addr.isMAC() } // ToAddressBase is an identity method. // // ToAddressBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *Address) ToAddressBase() *Address { return addr } // ToIP converts to an IPAddress if this address or subnet originated as an IPv4 or IPv6 address or subnet, or an implicitly zero-valued IP. // If not, ToIP returns nil. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *Address) ToIP() *IPAddress { if addr.IsIP() { return (*IPAddress)(unsafe.Pointer(addr)) } return nil } // ToIPv6 converts to an IPv6Address if this address or subnet originated as an IPv6 address or subnet. // If not, ToIPv6 returns nil. // // ToIPv6 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *Address) ToIPv6() *IPv6Address { if addr.IsIPv6() { return (*IPv6Address)(unsafe.Pointer(addr)) } return nil } // ToIPv4 converts to an IPv4Address if this address or subnet originated as an IPv4 address or subnet. // If not, ToIPv4 returns nil. // // ToIPv4 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *Address) ToIPv4() *IPv4Address { if addr.IsIPv4() { return (*IPv4Address)(unsafe.Pointer(addr)) } return nil } // ToMAC converts to a MACAddress if this address or subnet originated as a MAC address or subnet. // If not, ToMAC returns nil. // // ToMAC can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *Address) ToMAC() *MACAddress { if addr.IsMAC() { return (*MACAddress)(addr) } return nil } // Wrap wraps this address, returning a WrappedAddress, an implementation of ExtendedSegmentSeries, // which can be used to write code that works with both addresses and address sections. func (addr *Address) Wrap() WrappedAddress { return wrapAddress(addr.init()) } // ToKey creates the associated address key. // While addresses can be compared with the Compare, TrieCompare or Equal methods as well as various provided instances of AddressComparator, // they are not comparable with Go operators. // However, AddressKey instances are comparable with Go operators, and thus can be used as map keys. func (addr *Address) ToKey() Key[*Address] { key := Key[*Address]{} contents := &key.keyContents if thisAddr := addr.ToIPv4(); thisAddr != nil { key.scheme = ipv4Scheme thisAddr.toIPv4Key(contents) } else if thisAddr := addr.ToIPv6(); thisAddr != nil { key.scheme = ipv6Scheme thisAddr.toIPv6Key(contents) } else if thisAddr := addr.ToMAC(); thisAddr != nil { if addr.GetSegmentCount() == ExtendedUniqueIdentifier64SegmentCount { key.scheme = eui64Scheme } else { key.scheme = mac48Scheme } thisAddr.toMACKey(contents) } // else key.scheme == adaptiveZeroScheme return key } // ToGenericKey produces a generic Key[*Address] that can be used with generic code working with [Address], [IPAddress], [IPv4Address], [IPv6Address] and [MACAddress]. func (addr *Address) ToGenericKey() Key[*Address] { return addr.ToKey() } func (addr *Address) fromKey(scheme addressScheme, key *keyContents) *Address { if scheme == ipv4Scheme { ipv4Addr := fromIPv4IPKey(key) return ipv4Addr.ToAddressBase() } else if scheme == ipv6Scheme { ipv6Addr := fromIPv6IPKey(key) return ipv6Addr.ToAddressBase() } else if scheme == eui64Scheme || scheme == mac48Scheme { macAddr := fromMACAddrKey(scheme, key) return macAddr.ToAddressBase() } // scheme == adaptiveZeroScheme zeroAddr := Address{} return zeroAddr.init() } // AddrsMatchUnordered checks if the two slices share the same list of addresses, subnets, or address collections, in any order, using address equality. // The function can handle duplicates and nil addresses. func AddrsMatchUnordered[T, U AddressType](addrs1 []T, addrs2 []U) (result bool) { len1 := len(addrs1) len2 := len(addrs2) sameLen := len1 == len2 if len1 == 0 || len2 == 0 { result = sameLen } else if len1 == 1 && sameLen { result = addrs1[0].Equal(addrs2[0]) } else if len1 == 2 && sameLen { if addrs1[0].Equal(addrs2[0]) { result = addrs1[1].Equal(addrs2[1]) } else if result = addrs1[0].Equal(addrs2[1]); result { result = addrs1[1].Equal(addrs2[0]) } } else { result = reflect.DeepEqual(asMap(addrs1), asMap(addrs2)) } return } // AddrsMatchOrdered checks if the two slices share the same ordered list of addresses, subnets, or address collections, using address equality. // Duplicates and nil addresses are allowed. func AddrsMatchOrdered[T, U AddressType](addrs1 []T, addrs2 []U) (result bool) { len1 := len(addrs1) len2 := len(addrs2) if len1 != len2 { return } for i, addr := range addrs1 { if !addr.Equal(addrs2[i]) { return } } return true } func asMap[T AddressType](addrs []T) (result map[string]struct{}) { if addrLen := len(addrs); addrLen > 0 { result = make(map[string]struct{}) for _, addr := range addrs { result[addr.ToAddressBase().ToNormalizedWildcardString()] = struct{}{} } } return } ipaddress-go-1.5.4/ipaddr/addrerr/000077500000000000000000000000001440250641600167725ustar00rootroot00000000000000ipaddress-go-1.5.4/ipaddr/addrerr/err.go000066400000000000000000000077111440250641600201170ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package addrerr /* Error hierarchy: AddressError -IncompatibleAddressError - SizeMismatchError - HostIdentifierError - HostNameError - AddressStringError - AddressValueError unused, but present in Java: NetworkMismatchException InconsistentPrefixException AddressPositionException AddressConversionException PrefixLenException PositionMismatchException */ // AddressError is a type used by all library errors in order to be able to provide internationalized error messages. type AddressError interface { error // GetKey allows users to implement their own i18n error messages. // The keys and mappings are listed in IPAddressResources.properties, // so users of this library need only provide translations and implement // their own method of i18n to incorporate those translations, // such as the method provided by golang.org/x/text GetKey() string } // HostIdentifierError represents errors in string formats used to identify hosts. type HostIdentifierError interface { AddressError } // AddressStringError represents errors in address string formats used to identify addresses. type AddressStringError interface { HostIdentifierError } // HostNameError represents errors in host name string formats used to identify hosts. type HostNameError interface { HostIdentifierError // GetAddrError returns the underlying address error, or nil if none. GetAddrError() AddressError } // IncompatibleAddressError represents situations when an address, address section, address segment, or address string represents a valid type or format but // that type does not match the required type or format for a given operation. // // All such occurrences occur only from subnet addresses and sections. These occurrences cannot happen with single-valued address objects. // These occurrences cannot happen when using a standard prefix block subnet with standard masks. // // Examples include: // // • producing non-segmented hex, octal or base 85 strings from a subnet with a range that cannot be represented as a single range of values, // • masking subnets in a way that produces a non-contiguous range of values in a segment, // • reversing values that are not reversible, // • producing strings that are single-segment ranges from subnets which cannot be represented that way, // • producing new formats for which the range of values are incompatible with the new segments (EUI-64, IPv4 inet_aton formats, IPv4 embedded within IPv6, dotted MAC addresses from standard mac addresses, reverse-DNS strings), or // • using a subnet for an operation that requires a single address, such as with ToCanonicalHostName in IPAddress type IncompatibleAddressError interface { AddressError } // SizeMismatchError is an error that results from attempting an operation that requires address items of equal size, // but the supplied arguments were not equal in size. type SizeMismatchError interface { IncompatibleAddressError } // AddressValueError results from supplying an invalid value to an address operation. // Used when an address or address component would be too large or small, // when a prefix length is too large or small, or when prefixes across segments are inconsistent. // Not used when constructing new address components. // Not used when parsing strings to construct new address components, in which case AddressStringError is used instead. type AddressValueError interface { AddressError } ipaddress-go-1.5.4/ipaddr/addriterator.go000066400000000000000000000113251440250641600203640ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr // Iterator iterates collections, such as subnets and sequential address ranges. type Iterator[T any] interface { // HasNext returns true if there is another item to iterate, false otherwise. HasNext() bool // Next returns the next item, or the zero value for T if there is none left. Next() T } // IteratorWithRemove is an iterator that provides a removal operation. type IteratorWithRemove[T any] interface { Iterator[T] // Remove removes the last iterated item from the underlying data structure or collection, and returns that element. // If there is no such element, it returns the zero value for T. Remove() T } // type singleIterator[T any] struct { empty bool original T } func (it *singleIterator[T]) HasNext() bool { return !it.empty } func (it *singleIterator[T]) Next() (res T) { if it.HasNext() { res = it.original it.empty = true } return } // type multiAddrIterator struct { Iterator[*AddressSection] zone Zone } func (it multiAddrIterator) Next() (res *Address) { if it.HasNext() { sect := it.Iterator.Next() res = createAddress(sect, it.zone) } return } func nilAddrIterator() Iterator[*Address] { return &singleIterator[*Address]{} } func nilIterator[T any]() Iterator[T] { return &singleIterator[T]{} } func addrIterator( single bool, original *Address, prefixLen PrefixLen, valsAreMultiple bool, iterator Iterator[[]*AddressDivision]) Iterator[*Address] { if single { return &singleIterator[*Address]{original: original} } return multiAddrIterator{ Iterator: &multiSectionIterator{ original: original.section, iterator: iterator, valsAreMultiple: valsAreMultiple, prefixLen: prefixLen, }, zone: original.zone, } } func prefixAddrIterator( single bool, original *Address, prefixLen PrefixLen, iterator Iterator[[]*AddressDivision]) Iterator[*Address] { if single { return &singleIterator[*Address]{original: original} } var zone Zone if original != nil { zone = original.zone } return multiAddrIterator{ Iterator: &prefixSectionIterator{ original: original.section, iterator: iterator, prefixLen: prefixLen, }, zone: zone, } } // this one is used by the sequential ranges func rangeAddrIterator( single bool, original *Address, prefixLen PrefixLen, valsAreMultiple bool, iterator Iterator[[]*AddressDivision]) Iterator[*Address] { return addrIterator(single, original, prefixLen, valsAreMultiple, iterator) } type ipAddrIterator struct { Iterator[*Address] } func (iter ipAddrIterator) Next() *IPAddress { return iter.Iterator.Next().ToIP() } // type sliceIterator[T any] struct { elements []T } func (iter *sliceIterator[T]) HasNext() bool { return len(iter.elements) > 0 } func (iter *sliceIterator[T]) Next() (res T) { if iter.HasNext() { res = iter.elements[0] iter.elements = iter.elements[1:] } return } // type ipv4AddressIterator struct { Iterator[*Address] } func (iter ipv4AddressIterator) Next() *IPv4Address { return iter.Iterator.Next().ToIPv4() } // type ipv6AddressIterator struct { Iterator[*Address] } func (iter ipv6AddressIterator) Next() *IPv6Address { return iter.Iterator.Next().ToIPv6() } // type macAddressIterator struct { Iterator[*Address] } func (iter macAddressIterator) Next() *MACAddress { return iter.Iterator.Next().ToMAC() } // type addressSeriesIterator struct { Iterator[*Address] } func (iter addressSeriesIterator) Next() ExtendedSegmentSeries { if !iter.HasNext() { return nil } return wrapAddress(iter.Iterator.Next()) } // type ipaddressSeriesIterator struct { Iterator[*IPAddress] } func (iter ipaddressSeriesIterator) Next() ExtendedIPSegmentSeries { if !iter.HasNext() { return nil } return iter.Iterator.Next().Wrap() } // type sectionSeriesIterator struct { Iterator[*AddressSection] } func (iter sectionSeriesIterator) Next() ExtendedSegmentSeries { if !iter.HasNext() { return nil } return wrapSection(iter.Iterator.Next()) } // type ipSectionSeriesIterator struct { Iterator[*IPAddressSection] } func (iter ipSectionSeriesIterator) Next() ExtendedIPSegmentSeries { if !iter.HasNext() { return nil } return wrapIPSection(iter.Iterator.Next()) } ipaddress-go-1.5.4/ipaddr/addrstr/000077500000000000000000000000001440250641600170125ustar00rootroot00000000000000ipaddress-go-1.5.4/ipaddr/addrstr/stringopts.go000066400000000000000000001207641440250641600215670ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // /* Package addrstr provides interfaces for specifying how to create specific strings from addresses and address sections, as well as builder types to construct instances of those interfaces. For example, StringOptionsBuilder produces instances implementing StringOptions for specifying generic strings. More specific builders and corresponding interface types exist for more specific address versions and types. Each instance produced by a builders is immutable. */ package addrstr var ( falseVal = false trueVal = true ) const ( ipv6SegmentSeparator = ':' ipv6ZoneSeparatorStr = "%" ipv4SegmentSeparator = '.' macColonSegmentSeparator = ':' rangeSeparatorStr = "-" segmentWildcardStr = "*" ) // Wildcards specifies the wildcards to use when constructing an address string. // WildcardsBuilder can be used to build an instance of Wildcards. type Wildcards interface { // GetRangeSeparator returns the wildcard used to separate the lower and upper boundary (inclusive) of a range of values. // If not set, then the default separator RangeSeparatorStr is used, which is the hyphen '-'. GetRangeSeparator() string // GetWildcard returns the wildcard used for representing any legitimate value, which is the asterisk '*' by default. GetWildcard() string // GetSingleWildcard returns the wildcard used for representing any single digit, which is the underscore '_' by default. GetSingleWildcard() string } type wildcards struct { rangeSeparator, wildcard, singleWildcard string //rangeSeparator cannot be empty, the other two can } // GetRangeSeparator returns the wildcard used to separate the lower and upper boundary (inclusive) of a range of values. // If not set, then the default separator RangeSeparatorStr is used, which is the hyphen '-' func (wildcards *wildcards) GetRangeSeparator() string { return wildcards.rangeSeparator } // GetWildcard returns the wildcard used for representing any legitimate value, which is the asterisk '*' by default. func (wildcards *wildcards) GetWildcard() string { return wildcards.wildcard } // GetSingleWildcard returns the wildcard used for representing any single digit, which is the underscore '_' by default. func (wildcards *wildcards) GetSingleWildcard() string { return wildcards.singleWildcard } // DefaultWildcards is the default Wildcards instance, using '-' and '*' as range separator and wildcard. var DefaultWildcards Wildcards = &wildcards{rangeSeparator: rangeSeparatorStr, wildcard: segmentWildcardStr} // WildcardsBuilder builds an instance of Wildcards type WildcardsBuilder struct { wildcards } // SetRangeSeparator sets the wildcard used to separate the lower and upper boundary (inclusive) of a range of values. // If not set, then the default separator RangeSeparatorStr is used, which is the hyphen '-' func (wildcards *WildcardsBuilder) SetRangeSeparator(str string) *WildcardsBuilder { wildcards.rangeSeparator = str return wildcards } // SetWildcard sets the wildcard used for representing any legitimate value, which is the asterisk '*' by default. func (wildcards *WildcardsBuilder) SetWildcard(str string) *WildcardsBuilder { wildcards.wildcard = str return wildcards } // SetSingleWildcard sets the wildcard used for representing any single digit, which is the underscore '_' by default. func (wildcards *WildcardsBuilder) SetSingleWildcard(str string) *WildcardsBuilder { wildcards.singleWildcard = str return wildcards } // ToWildcards returns an immutable Wildcards instance built by this builder. func (wildcards *WildcardsBuilder) ToWildcards() Wildcards { res := wildcards.wildcards if res.rangeSeparator == "" { //rangeSeparator cannot be empty res.rangeSeparator = rangeSeparatorStr } return &res } // StringOptions represents a clear way to create a specific type of string. type StringOptions interface { // GetWildcards returns the wildcards specified for use in the string. GetWildcards() Wildcards // IsReverse indicates whether the string segments should be printed in reverse from the usual order, the usual order being most to least significant. IsReverse() bool // IsUppercase indicates whether to use uppercase for hexadecimal or other radices with alphabetic characters. IsUppercase() bool // IsExpandedSegments returns whether segments should be expanded to maximal width, typically by using leading zeros. IsExpandedSegments() bool // GetRadix returns the radix to be used. The default is hexadecimal unless built using an IPv4 options builder in which case the default is decimal. GetRadix() int // GetSeparator returns the separator that separates the divisions of the address, typically ':' or '.'. HasSeparator indicates if this method should be called. // the default is to have no separator, unless built using a MAC, IPv6 or IPv4 options builder in which case the separator is ':' for MAC and IPv6 and '.' for IPv4. GetSeparator() byte // HasSeparator indicates whether there is a separator. // The default is false, no separator, unless built using a MAC, IPv6 or IPv4 options builder in which case there is a default separator. HasSeparator() bool // GetAddressLabel returns a string to prepend to the entire address string, such as an octal, hex, or binary prefix. GetAddressLabel() string // GetSegmentStrPrefix returns the string prefix (if any) to prepend to each segment's values, such as an octal, hex or binary prefix. GetSegmentStrPrefix() string } type stringOptions struct { wildcards Wildcards base int // default is hex //the segment separator and in the case of split digits, the digit separator separator byte // default is ' ', but it's typically either '.' or ':' segmentStrPrefix, addrLabel string expandSegments, reverse, uppercase bool hasSeparator *bool // if not set, the default is false, no separator } // GetWildcards returns the wildcards specified for use in the string. func (opts *stringOptions) GetWildcards() Wildcards { return opts.wildcards } // IsReverse indicates whether the string segments should be printed in reverse from the usual order, the usual order being most to least significant. func (opts *stringOptions) IsReverse() bool { return opts.reverse } // IsUppercase indicates whether to use uppercase for hexadecimal or other radices with alphabetic characters. func (opts *stringOptions) IsUppercase() bool { return opts.uppercase } // IsExpandedSegments returns whether segments should be expanded to maximal width, typically by using leading zeros. func (opts *stringOptions) IsExpandedSegments() bool { return opts.expandSegments } // GetRadix returns the radix to be used. The default is hexadecimal unless built using an IPv4 options builder in which case the default is decimal. func (opts *stringOptions) GetRadix() int { return opts.base } // GetSeparator returns the separator that separates the divisions of the address, typically ':' or '.'. HasSeparator indicates if this method should be called. // the default is to have no separator, unless built using a MAC, IPv6 or IPv4 options builder in which case the separator is ':' for MAC and IPv6 and '.' for IPv4. func (opts *stringOptions) GetSeparator() byte { return opts.separator } // HasSeparator indicates whether there is a separator. // The default is false, no separator, unless built using a MAC, IPv6 or IPv4 options builder in which case there is a default separator. func (opts *stringOptions) HasSeparator() bool { if opts.hasSeparator == nil { return false } return *opts.hasSeparator } // GetAddressLabel returns a string to prepend to the entire address string, such as an octal, hex, or binary prefix. func (opts *stringOptions) GetAddressLabel() string { return opts.addrLabel } // GetSegmentStrPrefix returns the string prefix (if any) to prepend to each segment's values, such as an octal, hex or binary prefix. func (opts *stringOptions) GetSegmentStrPrefix() string { return opts.segmentStrPrefix } var _ StringOptions = &stringOptions{} func getDefaults(radix int, wildcards Wildcards, separator byte) (int, Wildcards, byte) { if radix == 0 { radix = 16 } if wildcards == nil { wildcards = DefaultWildcards } if separator == 0 { separator = ' ' } return radix, wildcards, separator } func getIPDefaults(zoneSeparator string) string { if len(zoneSeparator) == 0 { zoneSeparator = ipv6ZoneSeparatorStr } return zoneSeparator } func getIPv6Defaults(hasSeparator *bool, separator byte) (*bool, byte) { if hasSeparator == nil { hasSeparator = &trueVal } if separator == 0 { separator = ipv6SegmentSeparator } return hasSeparator, separator } func getIPv4Defaults(hasSeparator *bool, separator byte, radix int) (*bool, byte, int) { if hasSeparator == nil { hasSeparator = &trueVal } if radix == 0 { radix = 10 } if separator == 0 { separator = ipv4SegmentSeparator } return hasSeparator, separator, radix } func getMACDefaults(hasSeparator *bool, separator byte) (*bool, byte) { if hasSeparator == nil { hasSeparator = &trueVal } if separator == 0 { separator = macColonSegmentSeparator } return hasSeparator, separator } // StringOptionsBuilder is used to build an immutable StringOptions instance. type StringOptionsBuilder struct { stringOptions } // SetWildcards specifies the wildcards for use in the string. func (builder *StringOptionsBuilder) SetWildcards(wildcards Wildcards) *StringOptionsBuilder { builder.wildcards = wildcards return builder } // SetReverse dictates whether the string segments should be printed in reverse from the usual order, the usual order being most to least significant. func (builder *StringOptionsBuilder) SetReverse(reverse bool) *StringOptionsBuilder { builder.reverse = reverse return builder } // SetUppercase dictates whether to use uppercase for hexadecimal or other radices with alphabetic characters. func (builder *StringOptionsBuilder) SetUppercase(uppercase bool) *StringOptionsBuilder { builder.uppercase = uppercase return builder } // SetExpandedSegments dictates whether segments should be expanded to maximal width, typically by using leading zeros. func (builder *StringOptionsBuilder) SetExpandedSegments(expandSegments bool) *StringOptionsBuilder { builder.expandSegments = expandSegments return builder } // SetRadix sets the radix to be used. func (builder *StringOptionsBuilder) SetRadix(base int) *StringOptionsBuilder { builder.base = base return builder } // SetHasSeparator dictates whether there is a separator. // The default is false, no separator, unless using a MAC, IPv6 or IPv4 options builder in which case there is a default separator. func (builder *StringOptionsBuilder) SetHasSeparator(has bool) *StringOptionsBuilder { if has { builder.hasSeparator = &trueVal } else { builder.hasSeparator = &falseVal } return builder } // SetSeparator dictates the separator to separate the divisions of the address, typically ':' or '.'. // HasSeparator indicates if this separator should be used or not. func (builder *StringOptionsBuilder) SetSeparator(separator byte) *StringOptionsBuilder { builder.separator = separator builder.SetHasSeparator(true) return builder } // SetAddressLabel dictates a string to prepend to the entire address string, such as an octal, hex, or binary prefix. func (builder *StringOptionsBuilder) SetAddressLabel(label string) *StringOptionsBuilder { builder.addrLabel = label return builder } // SetSegmentStrPrefix dictates a string prefix to prepend to each segment's values, such as an octal, hex or binary prefix. func (builder *StringOptionsBuilder) SetSegmentStrPrefix(prefix string) *StringOptionsBuilder { builder.segmentStrPrefix = prefix return builder } // ToOptions returns an immutable StringOptions instance built by this builder. func (builder *StringOptionsBuilder) ToOptions() StringOptions { res := builder.stringOptions res.base, res.wildcards, res.separator = getDefaults(res.base, res.wildcards, res.separator) return &res } // MACStringOptionsBuilder is used to build an immutable StringOptions instance for MAC address strings. type MACStringOptionsBuilder struct { StringOptionsBuilder } // SetWildcards specifies the wildcards for use in the string. func (builder *MACStringOptionsBuilder) SetWildcards(wildcards Wildcards) *MACStringOptionsBuilder { builder.StringOptionsBuilder.SetWildcards(wildcards) return builder } // SetReverse dictates whether the string segments should be printed in reverse from the usual order, the usual order being most to least significant. func (builder *MACStringOptionsBuilder) SetReverse(reverse bool) *MACStringOptionsBuilder { builder.StringOptionsBuilder.SetReverse(reverse) return builder } // SetUppercase dictates whether to use uppercase for hexadecimal or other radices with alphabetic characters. func (builder *MACStringOptionsBuilder) SetUppercase(uppercase bool) *MACStringOptionsBuilder { builder.StringOptionsBuilder.SetUppercase(uppercase) return builder } // SetExpandedSegments dictates whether segments should be expanded to maximal width, typically by using leading zeros. func (builder *MACStringOptionsBuilder) SetExpandedSegments(expandSegments bool) *MACStringOptionsBuilder { builder.StringOptionsBuilder.SetExpandedSegments(expandSegments) return builder } // SetRadix sets the radix to be used. func (builder *MACStringOptionsBuilder) SetRadix(base int) *MACStringOptionsBuilder { builder.StringOptionsBuilder.SetRadix(base) return builder } // SetHasSeparator dictates whether there is a separator. // The default for MAC is true. func (builder *MACStringOptionsBuilder) SetHasSeparator(has bool) *MACStringOptionsBuilder { builder.StringOptionsBuilder.SetHasSeparator(has) return builder } // SetSeparator dictates the separator to separate the divisions of the address, for MAC the default is ':'. // HasSeparator indicates if this separator should be used or not. func (builder *MACStringOptionsBuilder) SetSeparator(separator byte) *MACStringOptionsBuilder { builder.StringOptionsBuilder.SetSeparator(separator) return builder } // SetAddressLabel dictates a string to prepend to the entire address string, such as an octal, hex, or binary prefix. func (builder *MACStringOptionsBuilder) SetAddressLabel(label string) *MACStringOptionsBuilder { builder.StringOptionsBuilder.SetAddressLabel(label) return builder } // SetSegmentStrPrefix dictates a string prefix to prepend to each segment's values, such as an octal, hex or binary prefix. func (builder *MACStringOptionsBuilder) SetSegmentStrPrefix(prefix string) *MACStringOptionsBuilder { builder.StringOptionsBuilder.SetSegmentStrPrefix(prefix) return builder } // ToOptions returns an immutable StringOptions instance built by this builder. func (builder *MACStringOptionsBuilder) ToOptions() StringOptions { b := &builder.StringOptionsBuilder b.hasSeparator, b.separator = getMACDefaults(b.hasSeparator, b.separator) return builder.StringOptionsBuilder.ToOptions() } // WildcardOption indicates options indicating when and where to use wildcards. type WildcardOption string const ( // WildcardsNetworkOnly prints wildcards that are part of the network portion (only possible with subnet address notation, otherwise this option is ignored). WildcardsNetworkOnly WildcardOption = "" // WildcardsAll prints wildcards for any visible (non-compressed) segments. WildcardsAll WildcardOption = "allType" ) // WildcardOptions indicates options indicating when and where to use wildcards, and what wildcards to use. type WildcardOptions interface { // GetWildcardOption returns the WildcardOption to use. GetWildcardOption() WildcardOption // GetWildcards returns the wildcards to use. GetWildcards() Wildcards } type wildcardOptions struct { wildcardOption WildcardOption wildcards Wildcards } // GetWildcardOption returns the WildcardOption to use. func (opts *wildcardOptions) GetWildcardOption() WildcardOption { return opts.wildcardOption } // GetWildcards returns the wildcards to use. func (opts *wildcardOptions) GetWildcards() Wildcards { return opts.wildcards } var _ WildcardOptions = &wildcardOptions{} // WildcardOptionsBuilder is used to build an immutable WildcardOptions instance for address strings. type WildcardOptionsBuilder struct { wildcardOptions } // SetWildcardOptions dictates the WildcardOption to use. func (builder *WildcardOptionsBuilder) SetWildcardOptions(wildcardOption WildcardOption) *WildcardOptionsBuilder { builder.wildcardOption = wildcardOption return builder } // SetWildcards dictates the wildcards to use. func (builder *WildcardOptionsBuilder) SetWildcards(wildcards Wildcards) *WildcardOptionsBuilder { builder.wildcards = wildcards return builder } // ToOptions returns an immutable WildcardOptions instance built by this builder. func (builder *WildcardOptionsBuilder) ToOptions() WildcardOptions { cpy := builder.wildcardOptions if builder.wildcards == nil { builder.wildcards = DefaultWildcards } return &cpy } // IPStringOptions represents a clear way to create a specific type of IP address or subnet string. type IPStringOptions interface { StringOptions // GetAddressSuffix returns a suffix to be appended to the string. // .in-addr.arpa, .ip6.arpa, .ipv6-literal.net are examples of suffixes tacked onto the end of address strings. GetAddressSuffix() string // GetWildcardOption returns the WildcardOption to use. GetWildcardOption() WildcardOption // GetZoneSeparator indicates the delimiter that separates the zone from the address, the default being '%'. GetZoneSeparator() string } type ipStringOptions struct { stringOptions addrSuffix string wildcardOption WildcardOption // default is WildcardsNetworkOnly zoneSeparator string // default is IPv6ZoneSeparator } // GetAddressSuffix returns a suffix to be appended to the string. // .in-addr.arpa, .ip6.arpa, .ipv6-literal.net are examples of suffixes tacked onto the end of address strings. func (opts *ipStringOptions) GetAddressSuffix() string { return opts.addrSuffix } // GetWildcardOptions returns the WildcardOptions to use. func (opts *ipStringOptions) GetWildcardOptions() WildcardOptions { options := &wildcardOptions{ opts.wildcardOption, opts.GetWildcards(), } return options } // GetWildcardOption returns the WildcardOption to use. func (opts *ipStringOptions) GetWildcardOption() WildcardOption { return opts.wildcardOption } // GetZoneSeparator returns the delimiter that separates the address from the zone, the default being '%'. func (opts *ipStringOptions) GetZoneSeparator() string { return opts.zoneSeparator } var _ IPStringOptions = &ipStringOptions{} // IPStringOptionsBuilder is used to build an immutable IPStringOptions instance for IP address strings. type IPStringOptionsBuilder struct { StringOptionsBuilder ipStringOptions ipStringOptions } // SetAddressSuffix dictates a suffix to be appended to the string. // .in-addr.arpa, .ip6.arpa, .ipv6-literal.net are examples of suffixes tacked onto the end of address strings. func (builder *IPStringOptionsBuilder) SetAddressSuffix(suffix string) *IPStringOptionsBuilder { builder.ipStringOptions.addrSuffix = suffix return builder } // SetWildcardOptions is a convenience method for setting both the WildcardOption and the Wildcards at the same time. // It overrides previous calls to SetWildcardOption and SetWildcards, // and is overridden by subsequent calls to those methods. func (builder *IPStringOptionsBuilder) SetWildcardOptions(wildcardOptions WildcardOptions) *IPStringOptionsBuilder { builder.SetWildcards(wildcardOptions.GetWildcards()) return builder.SetWildcardOption(wildcardOptions.GetWildcardOption()) } // SetWildcardOption specifies the WildcardOption for use in the string. func (builder *IPStringOptionsBuilder) SetWildcardOption(wildcardOption WildcardOption) *IPStringOptionsBuilder { builder.ipStringOptions.wildcardOption = wildcardOption return builder } // SetWildcards specifies the wildcards for use in the string. func (builder *IPStringOptionsBuilder) SetWildcards(wildcards Wildcards) *IPStringOptionsBuilder { builder.StringOptionsBuilder.SetWildcards(wildcards) return builder } // SetZoneSeparator dictates the separator to separate the zone from the address, the default being '%' // Zones apply to IPv6 addresses only, not IPv4. func (builder *IPStringOptionsBuilder) SetZoneSeparator(separator string) *IPStringOptionsBuilder { builder.ipStringOptions.zoneSeparator = separator return builder } // SetReverse dictates whether the string segments should be printed in reverse from the usual order, the usual order being most to least significant. func (builder *IPStringOptionsBuilder) SetReverse(reverse bool) *IPStringOptionsBuilder { builder.StringOptionsBuilder.SetReverse(reverse) return builder } // SetUppercase dictates whether to use uppercase for hexadecimal or other radices with alphabetic characters. func (builder *IPStringOptionsBuilder) SetUppercase(uppercase bool) *IPStringOptionsBuilder { builder.StringOptionsBuilder.SetUppercase(uppercase) return builder } // SetExpandedSegments dictates whether segments should be expanded to maximal width, typically by using leading zeros. func (builder *IPStringOptionsBuilder) SetExpandedSegments(expandSegments bool) *IPStringOptionsBuilder { builder.StringOptionsBuilder.SetExpandedSegments(expandSegments) return builder } // SetRadix sets the radix to be used. func (builder *IPStringOptionsBuilder) SetRadix(base int) *IPStringOptionsBuilder { builder.StringOptionsBuilder.SetRadix(base) return builder } // SetHasSeparator dictates whether there is a separator. // The default for IPStringOptionsBuilder is false. func (builder *IPStringOptionsBuilder) SetHasSeparator(has bool) *IPStringOptionsBuilder { builder.StringOptionsBuilder.SetHasSeparator(has) return builder } // SetSeparator dictates the separator to separate the divisions of the address. // HasSeparator indicates if this separator should be used or not. func (builder *IPStringOptionsBuilder) SetSeparator(separator byte) *IPStringOptionsBuilder { builder.StringOptionsBuilder.SetSeparator(separator) return builder } // SetAddressLabel dictates a string to prepend to the entire address string, such as an octal, hex, or binary prefix. func (builder *IPStringOptionsBuilder) SetAddressLabel(label string) *IPStringOptionsBuilder { builder.StringOptionsBuilder.SetAddressLabel(label) return builder } // SetSegmentStrPrefix dictates a string prefix to prepend to each segment's values, such as an octal, hex or binary prefix. func (builder *IPStringOptionsBuilder) SetSegmentStrPrefix(prefix string) *IPStringOptionsBuilder { builder.StringOptionsBuilder.SetSegmentStrPrefix(prefix) return builder } // ToOptions returns an immutable IPStringOptions instance built by this builder. func (builder *IPStringOptionsBuilder) ToOptions() IPStringOptions { builder.ipStringOptions.zoneSeparator = getIPDefaults(builder.ipStringOptions.zoneSeparator) res := builder.ipStringOptions res.stringOptions = *builder.StringOptionsBuilder.ToOptions().(*stringOptions) return &res } // IPv4StringOptionsBuilder is used to build an immutable IPStringOptions instance for IPv4 address strings. type IPv4StringOptionsBuilder struct { IPStringOptionsBuilder } // SetAddressSuffix dictates a suffix to be appended to the string. // .in-addr.arpa, .ip6.arpa, .ipv6-literal.net are examples of suffixes tacked onto the end of address strings. func (builder *IPv4StringOptionsBuilder) SetAddressSuffix(suffix string) *IPv4StringOptionsBuilder { builder.IPStringOptionsBuilder.SetAddressSuffix(suffix) return builder } // SetWildcardOptions is a convenience method for setting both the WildcardOption and the Wildcards at the same time. // It overrides previous calls to SetWildcardOption and SetWildcards, // and is overridden by subsequent calls to those methods. func (builder *IPv4StringOptionsBuilder) SetWildcardOptions(wildcardOptions WildcardOptions) *IPv4StringOptionsBuilder { builder.IPStringOptionsBuilder.SetWildcardOptions(wildcardOptions) return builder.SetWildcardOption(wildcardOptions.GetWildcardOption()) } // SetWildcardOption specifies the WildcardOption for use in the string. func (builder *IPv4StringOptionsBuilder) SetWildcardOption(wildcardOption WildcardOption) *IPv4StringOptionsBuilder { builder.IPStringOptionsBuilder.SetWildcardOption(wildcardOption) return builder } // SetWildcards specifies the wildcards for use in the string. func (builder *IPv4StringOptionsBuilder) SetWildcards(wildcards Wildcards) *IPv4StringOptionsBuilder { builder.IPStringOptionsBuilder.SetWildcards(wildcards) return builder } // SetReverse dictates whether the string segments should be printed in reverse from the usual order, the usual order being most to least significant. func (builder *IPv4StringOptionsBuilder) SetReverse(reverse bool) *IPv4StringOptionsBuilder { builder.IPStringOptionsBuilder.SetReverse(reverse) return builder } // SetUppercase dictates whether to use uppercase for hexadecimal or other radices with alphabetic characters. func (builder *IPv4StringOptionsBuilder) SetUppercase(uppercase bool) *IPv4StringOptionsBuilder { builder.IPStringOptionsBuilder.SetUppercase(uppercase) return builder } // SetExpandedSegments dictates whether segments should be expanded to maximal width, typically by using leading zeros. func (builder *IPv4StringOptionsBuilder) SetExpandedSegments(expandSegments bool) *IPv4StringOptionsBuilder { builder.IPStringOptionsBuilder.SetExpandedSegments(expandSegments) return builder } // SetRadix sets the radix to be used. func (builder *IPv4StringOptionsBuilder) SetRadix(base int) *IPv4StringOptionsBuilder { builder.IPStringOptionsBuilder.SetRadix(base) return builder } // SetHasSeparator dictates whether there is a separator. // The default for IPv4 is true. func (builder *IPv4StringOptionsBuilder) SetHasSeparator(has bool) *IPv4StringOptionsBuilder { builder.IPStringOptionsBuilder.SetHasSeparator(has) return builder } // SetSeparator dictates the separator to separate the divisions of the address, for IPv4 the default is '.'. // HasSeparator indicates if this separator should be used or not. func (builder *IPv4StringOptionsBuilder) SetSeparator(separator byte) *IPv4StringOptionsBuilder { builder.IPStringOptionsBuilder.SetSeparator(separator) return builder } // SetAddressLabel dictates a string to prepend to the entire address string, such as an octal, hex, or binary prefix. func (builder *IPv4StringOptionsBuilder) SetAddressLabel(label string) *IPv4StringOptionsBuilder { builder.IPStringOptionsBuilder.SetAddressLabel(label) return builder } // SetSegmentStrPrefix dictates a string prefix to prepend to each segment's values, such as an octal, hex or binary prefix. func (builder *IPv4StringOptionsBuilder) SetSegmentStrPrefix(prefix string) *IPv4StringOptionsBuilder { builder.IPStringOptionsBuilder.SetSegmentStrPrefix(prefix) return builder } // ToOptions returns an immutable IPStringOptions instance built by this builder. func (builder *IPv4StringOptionsBuilder) ToOptions() IPStringOptions { b := &builder.StringOptionsBuilder b.hasSeparator, b.separator, b.base = getIPv4Defaults(b.hasSeparator, b.separator, b.base) return builder.IPStringOptionsBuilder.ToOptions() } // IPv6StringOptions provides a clear way to create a specific type of IPv6 address string. type IPv6StringOptions interface { IPStringOptions // GetIPv4Opts returns the options used for creating the embedded IPv4 address string in a mixed IPv6 address, // which comes from the last 32 bits of the IPv6 address. // For example: "a:b:c:d:e:f:1.2.3.4" GetIPv4Opts() IPStringOptions // GetCompressOptions returns the CompressOptions which specify how to compress zero-segments in the IPv6 address or subnet string. GetCompressOptions() CompressOptions // IsSplitDigits indicates whether every digit is separated from every other by separators. If mixed, this option is ignored. IsSplitDigits() bool // can produce addrerr.IncompatibleAddressError for ranged series // IsMixed specifies that the last two segments of the IPv6 address should be printed as an IPv4 address, resulting in a mixed IPv6/v4 string. IsMixed() bool // can produce addrerr.IncompatibleAddressError for ranges in the IPv4 part of the series } type ipv6StringOptions struct { ipStringOptions ipv4Opts IPStringOptions //can be nil, which means no compression compressOptions CompressOptions splitDigits bool } // IsSplitDigits indicates whether every digit is separated from every other by separators. If mixed, this option is ignored. func (opts *ipv6StringOptions) IsSplitDigits() bool { return opts.splitDigits } // GetIPv4Opts returns the IPv4 string options to be used on the IPv4 address section in a mixed IPv6/v4 string. func (opts *ipv6StringOptions) GetIPv4Opts() IPStringOptions { return opts.ipv4Opts } // GetCompressOptions returns the CompressOptions which specify how to compress zero-segments in the IPv6 address or subnet string. func (opts *ipv6StringOptions) GetCompressOptions() CompressOptions { return opts.compressOptions } // IsMixed specifies that the last two segments of the IPv6 address should be printed as an IPv4 address, resulting in a mixed IPv6/v4 string. func (opts *ipv6StringOptions) IsMixed() bool { return opts.ipv4Opts != nil } var _ IPv6StringOptions = &ipv6StringOptions{} // IPv6StringOptionsBuilder is used to build an immutable IPv6StringOptions instance for IPv6 address strings. type IPv6StringOptionsBuilder struct { opts ipv6StringOptions IPStringOptionsBuilder makeMixed bool } // IsMixed specifies whether the last two segments of the IPv6 address should be printed as an IPv4 address, resulting in a mixed IPv6/v4 string. func (builder *IPv6StringOptionsBuilder) IsMixed() bool { return builder.makeMixed } // GetIPv4Opts returns the IPv4 string options to be used on the IPv4 address section in a mixed IPv6/v4 string. func (builder *IPv6StringOptionsBuilder) GetIPv4Opts() IPStringOptions { return builder.opts.ipv4Opts } // GetCompressOptions returns the CompressOptions which specify how to compress zero-segments in the IPv6 address or subnet string. func (builder *IPv6StringOptionsBuilder) GetCompressOptions() CompressOptions { return builder.opts.compressOptions } // SetSplitDigits dictates whether every digit is separated from every other by separators. If mixed, this option is ignored. func (builder *IPv6StringOptionsBuilder) SetSplitDigits(splitDigits bool) *IPv6StringOptionsBuilder { builder.opts.splitDigits = splitDigits return builder } // SetCompressOptions sets the CompressOptions which specify how to compress zero-segments in the IPv6 address or subnet string. func (builder *IPv6StringOptionsBuilder) SetCompressOptions(compressOptions CompressOptions) *IPv6StringOptionsBuilder { builder.opts.compressOptions = compressOptions return builder } // SetMixed dictates whether the string should be a mixed IPv6/v4 string, in which the last two segments of the IPv6 address should be printed as an IPv4 address. func (builder *IPv6StringOptionsBuilder) SetMixed(makeMixed bool) *IPv6StringOptionsBuilder { builder.makeMixed = makeMixed return builder } // SetMixedOptions supplies the IPv4 options to be used on the IPv4 section of a mixed string. Calling this method sets the string to be a mixed IPv6/v4 string. func (builder *IPv6StringOptionsBuilder) SetMixedOptions(ipv4Options IPStringOptions) *IPv6StringOptionsBuilder { builder.makeMixed = true builder.opts.ipv4Opts = ipv4Options return builder } // SetWildcardOptions is a convenience method for setting both the WildcardOption and the Wildcards at the same time // It overrides previous calls to SetWildcardOption and SetWildcards, // and is overridden by subsequent calls to those methods. func (builder *IPv6StringOptionsBuilder) SetWildcardOptions(wildcardOptions WildcardOptions) *IPv6StringOptionsBuilder { builder.IPStringOptionsBuilder.SetWildcardOptions(wildcardOptions) return builder } // SetWildcardOption specifies the WildcardOption for use in the string. func (builder *IPv6StringOptionsBuilder) SetWildcardOption(wildcardOption WildcardOption) *IPv6StringOptionsBuilder { builder.IPStringOptionsBuilder.SetWildcardOption(wildcardOption) return builder } // SetWildcards specifies the wildcards for use in the string. func (builder *IPv6StringOptionsBuilder) SetWildcards(wildcards Wildcards) *IPv6StringOptionsBuilder { builder.IPStringOptionsBuilder.SetWildcards(wildcards) return builder } // SetExpandedSegments dictates whether segments should be expanded to maximal width, typically by using leading zeros. func (builder *IPv6StringOptionsBuilder) SetExpandedSegments(expandSegments bool) *IPv6StringOptionsBuilder { builder.IPStringOptionsBuilder.SetExpandedSegments(expandSegments) return builder } // SetRadix sets the radix to be used. func (builder *IPv6StringOptionsBuilder) SetRadix(base int) *IPv6StringOptionsBuilder { builder.IPStringOptionsBuilder.SetRadix(base) return builder } // SetHasSeparator dictates whether there is a separator. // The default for IPv6 is true. func (builder *IPv6StringOptionsBuilder) SetHasSeparator(has bool) *IPv6StringOptionsBuilder { builder.IPStringOptionsBuilder.SetHasSeparator(has) return builder } // SetSeparator dictates the separator to separate the divisions of the address, for IPv6 the default is ':'. // HasSeparator indicates if this separator should be used or not. func (builder *IPv6StringOptionsBuilder) SetSeparator(separator byte) *IPv6StringOptionsBuilder { builder.IPStringOptionsBuilder.SetSeparator(separator) return builder } // SetZoneSeparator dictates the separator to separate the zone from the address, the default being '%'. func (builder *IPv6StringOptionsBuilder) SetZoneSeparator(separator string) *IPv6StringOptionsBuilder { builder.IPStringOptionsBuilder.SetZoneSeparator(separator) return builder } // SetAddressSuffix dictates a suffix to be appended to the string. // .in-addr.arpa, .ip6.arpa, .ipv6-literal.net are examples of suffixes tacked onto the end of address strings. func (builder *IPv6StringOptionsBuilder) SetAddressSuffix(suffix string) *IPv6StringOptionsBuilder { builder.IPStringOptionsBuilder.SetAddressSuffix(suffix) return builder } // SetSegmentStrPrefix dictates a string prefix to prepend to each segment's values, such as an octal, hex or binary prefix. func (builder *IPv6StringOptionsBuilder) SetSegmentStrPrefix(prefix string) *IPv6StringOptionsBuilder { builder.IPStringOptionsBuilder.SetSegmentStrPrefix(prefix) return builder } // SetReverse dictates whether the string segments should be printed in reverse from the usual order, the usual order being most to least significant. func (builder *IPv6StringOptionsBuilder) SetReverse(reverse bool) *IPv6StringOptionsBuilder { builder.IPStringOptionsBuilder.SetReverse(reverse) return builder } // SetUppercase dictates whether to use uppercase for hexadecimal or other radices with alphabetic characters. func (builder *IPv6StringOptionsBuilder) SetUppercase(upper bool) *IPv6StringOptionsBuilder { builder.IPStringOptionsBuilder.SetUppercase(upper) return builder } // ToOptions returns an immutable IPv6StringOptions instance built by this builder. func (builder *IPv6StringOptionsBuilder) ToOptions() IPv6StringOptions { if builder.makeMixed { if builder.opts.ipv4Opts == nil { builder.opts.ipv4Opts = new(IPv4StringOptionsBuilder).SetExpandedSegments(builder.expandSegments). SetWildcardOption(builder.ipStringOptions.wildcardOption). SetWildcards(builder.wildcards).ToOptions() } } else { builder.opts.ipv4Opts = nil } b := &builder.IPStringOptionsBuilder.StringOptionsBuilder b.hasSeparator, b.separator = getIPv6Defaults(b.hasSeparator, b.separator) res := builder.opts res.ipStringOptions = *builder.IPStringOptionsBuilder.ToOptions().(*ipStringOptions) return &res } // CompressionChoiceOptions specify which zero-segments should be compressed. type CompressionChoiceOptions string const ( // HostPreferred - if there is a host section, compress the host along with any adjoining zero-segments, otherwise compress a range of zero-segments. HostPreferred CompressionChoiceOptions = "host preferred" // MixedPreferred - if there is a mixed section that is compressible according to the MixedCompressionOptions, compress the mixed section along with any adjoining zero-segments, otherwise compress a range of zero-segments. MixedPreferred CompressionChoiceOptions = "mixed preferred" // ZerosOrHost - compress the largest range of zero or host segments. ZerosOrHost CompressionChoiceOptions = "" // ZerosCompression - compress the largest range of zero-segments. ZerosCompression CompressionChoiceOptions = "zeros" ) // CompressHost indicates if a host of a prefixed address should be compressed. func (choice CompressionChoiceOptions) CompressHost() bool { return choice != ZerosCompression } // MixedCompressionOptions specify which zero-segments should be compressed in mixed IPv6/v4 strings. type MixedCompressionOptions string const ( // NoMixedCompression - do not allow compression of an IPv4 section. NoMixedCompression MixedCompressionOptions = "no mixed compression" // MixedCompressionNoHost - allow compression of the IPv4 section when there is no host of the prefixed address. MixedCompressionNoHost MixedCompressionOptions = "no host" // MixedCompressionCoveredByHost - compress the IPv4 section if it is part of the host of the prefixed address. MixedCompressionCoveredByHost MixedCompressionOptions = "covered by host" // AllowMixedCompression - allow compression of a the IPv4 section. AllowMixedCompression MixedCompressionOptions = "" ) // CompressOptions specifies how to compress the zero-segments in an address or subnet string. type CompressOptions interface { // GetCompressionChoiceOptions provides the CompressionChoiceOptions which specify which zero-segments should be compressed. GetCompressionChoiceOptions() CompressionChoiceOptions // GetMixedCompressionOptions provides the MixedCompressionOptions which specify which zero-segments should be compressed in mixed IPv6/v4 strings. GetMixedCompressionOptions() MixedCompressionOptions // CompressSingle indicates if a single zero-segment should be compressed on its own when there are no other segments to compress. CompressSingle() bool } type compressOptions struct { compressSingle bool rangeSelection CompressionChoiceOptions //options for addresses with an ipv4 section compressMixedOptions MixedCompressionOptions } // GetCompressionChoiceOptions provides the CompressionChoiceOptions which specify which zero-segments should be compressed. func (opts *compressOptions) GetCompressionChoiceOptions() CompressionChoiceOptions { return opts.rangeSelection } // GetMixedCompressionOptions provides the MixedCompressionOptions which specify which zero-segments should be compressed in mixed IPv6/v4 strings. func (opts *compressOptions) GetMixedCompressionOptions() MixedCompressionOptions { return opts.compressMixedOptions } // CompressSingle indicates if a single zero-segment should be compressed on its own when there are no other segments to compress. func (opts *compressOptions) CompressSingle() bool { return opts.compressSingle } var _ CompressOptions = &compressOptions{} // CompressOptionsBuilder is used to build an immutable CompressOptions instance for IPv6 address strings. type CompressOptionsBuilder struct { compressOptions } // SetCompressSingle dictates whether a single zero-segment should be compressed on its own when there are no other segments to compress func (builder *CompressOptionsBuilder) SetCompressSingle(compressSingle bool) *CompressOptionsBuilder { builder.compressSingle = compressSingle return builder } // SetCompressionChoiceOptions sets the CompressionChoiceOptions which specify which zero-segments should be compressed func (builder *CompressOptionsBuilder) SetCompressionChoiceOptions(rangeSelection CompressionChoiceOptions) *CompressOptionsBuilder { builder.rangeSelection = rangeSelection return builder } // SetMixedCompressionOptions sets the MixedCompressionOptions which specify which zero-segments should be compressed in mixed IPv6/v4 strings func (builder *CompressOptionsBuilder) SetMixedCompressionOptions(compressMixedOptions MixedCompressionOptions) *CompressOptionsBuilder { builder.compressMixedOptions = compressMixedOptions return builder } // ToOptions returns an immutable CompressOptions instance built by this builder func (builder *CompressOptionsBuilder) ToOptions() CompressOptions { res := builder.compressOptions return &res } ipaddress-go-1.5.4/ipaddr/addrstrparam/000077500000000000000000000000001440250641600200335ustar00rootroot00000000000000ipaddress-go-1.5.4/ipaddr/addrstrparam/addrstrparams.go000066400000000000000000000342731440250641600232420ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package addrstrparam type AddressStringFormatParams interface { // AllowsWildcardedSeparator controls whether the wildcard '*' or '%' can replace the segment separators '.' and ':'. // If so, then you can write addresses like "*.*" or "*:*". AllowsWildcardedSeparator() bool // AllowsLeadingZeros indicates whether you allow addresses with segments that have leasing zeros like "001.2.3.004" or "1:000a::". // For IPV4, this option overrides inet_aton octal. // // Single segment addresses that must have the requisite length to be parsed are not affected by this flag. AllowsLeadingZeros() bool // AllowsUnlimitedLeadingZeros determines if you allow leading zeros that extend segments // beyond the usual segment length, which is 3 for IPv4 dotted-decimal and 4 for IPv6. // However, this only takes effect if leading zeros are allowed, which is when // AllowsLeadingZeros is true or the address is IPv4 and Allows_inet_aton_octal is true. // // For example, this determines whether you allow "0001.0002.0003.0004". AllowsUnlimitedLeadingZeros() bool // GetRangeParams returns the RangeParams describing whether ranges of values are allowed and what wildcards are allowed. GetRangeParams() RangeParams } type AddressStringParams interface { // AllowsEmpty indicates whether it allows zero-length address strings: "" AllowsEmpty() bool // AllowsSingleSegment allows an address to be specified as a single value, eg ffffffff, without the standard use of segments like "1.2.3.4" or "1:2:4:3:5:6:7:8" AllowsSingleSegment() bool // AllowsAll indicates if we allow the string of just the wildcard "*" to denote all addresses of all version. // If false, then for IP addresses we check the preferred version with GetPreferredVersion(), and then check AllowsWildcardedSeparator, // to determine if the string represents all addresses of that version. AllowsAll() bool } // RangeParams indicates what wildcards and ranges are allowed in the string. type RangeParams interface { // AllowsWildcard indicates whether '*' is allowed to denote segments covering all possible segment values AllowsWildcard() bool // AllowsRangeSeparator indicates whether '-' (or the expected range separator for the address) is allowed to denote a range from lower to higher, like 1-10 AllowsRangeSeparator() bool // AllowsSingleWildcard indicates whether to allow a segment terminating with '_' characters, which represent any digit AllowsSingleWildcard() bool // AllowsReverseRange indicates whether '-' (or the expected range separator for the address) is allowed to denote a range from higher to lower, like 10-1 AllowsReverseRange() bool // AllowsInferredBoundary indicates whether a missing range value before or after a '-' is allowed to denote the mininum or maximum potential value AllowsInferredBoundary() bool } var _ AddressStringFormatParams = &addressStringFormatParameters{} var _ AddressStringParams = &addressStringParameters{} var _ RangeParams = &rangeParameters{} type rangeParameters struct { noWildcard, noValueRange, noReverseRange, noSingleWildcard, noInferredBoundary bool } var ( // NoRange - use no wildcards nor range separators NoRange RangeParams = &rangeParameters{ noWildcard: true, noValueRange: true, noReverseRange: true, noSingleWildcard: true, noInferredBoundary: true, } // WildcardOnly - use this to support addresses like "1.*.3.4" or "1::*:3" or "1.2_.3.4" or "1::a__:3" WildcardOnly RangeParams = &rangeParameters{ noValueRange: true, noReverseRange: true, } // WildcardAndRange - use this to support addresses supported by the default wildcard options and also addresses like "1.2-3.3.4" or "1:0-ff::". WildcardAndRange RangeParams = &rangeParameters{} ) // AllowsWildcard indicates whether '*' is allowed to denote segments covering all possible segment values. func (builder *rangeParameters) AllowsWildcard() bool { return !builder.noWildcard } // AllowsRangeSeparator indicates whether '-' (or the expected range separator for the address) is allowed to denote a range from lower to higher, like 1-10. func (builder *rangeParameters) AllowsRangeSeparator() bool { return !builder.noValueRange } // AllowsReverseRange indicates whether '-' (or the expected range separator for the address) is allowed to denote a range from higher to lower, like 10-1. func (builder *rangeParameters) AllowsReverseRange() bool { return !builder.noReverseRange } // AllowsInferredBoundary indicates whether a missing range value before or after a '-' is allowed to denote the mininum or maximum potential value. func (builder *rangeParameters) AllowsInferredBoundary() bool { return !builder.noInferredBoundary } // AllowsSingleWildcard indicates whether to allow a segment terminating with '_' characters, which represent any digit. func (builder *rangeParameters) AllowsSingleWildcard() bool { return !builder.noSingleWildcard } // RangeParamsBuilder is used to build an immutable RangeParams for parsing address strings. type RangeParamsBuilder struct { rangeParameters parent interface{} } // ToParams returns an immutable RangeParams instance built by this builder. func (builder *RangeParamsBuilder) ToParams() RangeParams { return &builder.rangeParameters } // Set initializes this builder with the values from the given RangeParams. func (builder *RangeParamsBuilder) Set(rangeParams RangeParams) *RangeParamsBuilder { if rp, ok := rangeParams.(*rangeParameters); ok { builder.rangeParameters = *rp } else { builder.rangeParameters = rangeParameters{ noWildcard: !rangeParams.AllowsWildcard(), noValueRange: !rangeParams.AllowsRangeSeparator(), noReverseRange: !rangeParams.AllowsReverseRange(), noSingleWildcard: !rangeParams.AllowsSingleWildcard(), noInferredBoundary: !rangeParams.AllowsInferredBoundary(), } } return builder } // GetIPv4ParentBuilder returns the IPv4AddressStringParamsBuilder if this builder was obtained by a call to IPv4AddressStringParamsBuilder.GetRangeParamsBuilder. func (builder *RangeParamsBuilder) GetIPv4ParentBuilder() *IPv4AddressStringParamsBuilder { parent := builder.parent if p, ok := parent.(*IPv4AddressStringParamsBuilder); ok { return p } return nil } // GetIPv6ParentBuilder returns the IPv6AddressStringParamsBuilder if this builder was obtained by a call to IPv6AddressStringParamsBuilder.GetRangeParamsBuilder. func (builder *RangeParamsBuilder) GetIPv6ParentBuilder() *IPv6AddressStringParamsBuilder { parent := builder.parent if p, ok := parent.(*IPv6AddressStringParamsBuilder); ok { return p } return nil } // GetMACParentBuilder returns the IPv6AddressStringParamsBuilder if this builder was obtained by a call to IPv6AddressStringParamsBuilder.GetRangeParamsBuilder. func (builder *RangeParamsBuilder) GetMACParentBuilder() *MACAddressStringFormatParamsBuilder { parent := builder.parent if p, ok := parent.(*MACAddressStringFormatParamsBuilder); ok { return p } return nil } // AllowWildcard dictates whether '*' is allowed to denote segments covering all possible segment values. func (builder *RangeParamsBuilder) AllowWildcard(allow bool) *RangeParamsBuilder { builder.noWildcard = !allow return builder } // AllowRangeSeparator dictates whether '-' (or the expected range separator for the address) is allowed to denote a range from lower to higher, like 1-10. func (builder *RangeParamsBuilder) AllowRangeSeparator(allow bool) *RangeParamsBuilder { builder.noValueRange = !allow return builder } // AllowReverseRange dictates whether '-' (or the expected range separator for the address) is allowed to denote a range from higher to lower, like 10-1. func (builder *RangeParamsBuilder) AllowReverseRange(allow bool) *RangeParamsBuilder { builder.noReverseRange = !allow return builder } // AllowInferredBoundary dictates whether a missing range value before or after a '-' is allowed to denote the mininum or maximum potential value. func (builder *RangeParamsBuilder) AllowInferredBoundary(allow bool) *RangeParamsBuilder { builder.noInferredBoundary = !allow return builder } // AllowSingleWildcard dictates whether to allow a segment terminating with '_' characters, which represent any digit. func (builder *RangeParamsBuilder) AllowSingleWildcard(allow bool) *RangeParamsBuilder { builder.noSingleWildcard = !allow return builder } type addressStringParameters struct { noEmpty, noAll, noSingleSegment bool } // AllowsEmpty indicates whether it allows zero-length address strings: "". func (params *addressStringParameters) AllowsEmpty() bool { return !params.noEmpty } // AllowsSingleSegment allows an address to be specified as a single value, eg ffffffff, without the standard use of segments like "1.2.3.4" or "1:2:4:3:5:6:7:8". func (params *addressStringParameters) AllowsSingleSegment() bool { return !params.noSingleSegment } // AllowsAll indicates if we allow the string of just the wildcard "*" to denote all addresses of all version. // If false, then for IP addresses we check the preferred version with GetPreferredVersion(), and then check AllowsWildcardedSeparator(), // to determine if the string represents all addresses of that version. func (params *addressStringParameters) AllowsAll() bool { return !params.noAll } // AddressStringParamsBuilder builds an AddressStringParams. type AddressStringParamsBuilder struct { addressStringParameters } func (builder *AddressStringParamsBuilder) set(params AddressStringParams) { if p, ok := params.(*addressStringParameters); ok { builder.addressStringParameters = *p } else { builder.addressStringParameters = addressStringParameters{ noEmpty: !params.AllowsEmpty(), noAll: !params.AllowsAll(), noSingleSegment: !params.AllowsSingleSegment(), } } } // ToParams returns an immutable AddressStringParams instance built by this builder. func (builder *AddressStringParamsBuilder) ToParams() AddressStringParams { return &builder.addressStringParameters } func (builder *AddressStringParamsBuilder) allowEmpty(allow bool) { builder.noEmpty = !allow } func (builder *AddressStringParamsBuilder) allowAll(allow bool) { builder.noAll = !allow } func (builder *AddressStringParamsBuilder) allowSingleSegment(allow bool) { builder.noSingleSegment = !allow } // // // AddressStringFormatParams are parameters specific to a given address type or version that is supplied. type addressStringFormatParameters struct { rangeParams rangeParameters noWildcardedSeparator, noLeadingZeros, noUnlimitedLeadingZeros bool } // AllowsWildcardedSeparator controls whether the wildcard '*' or '%' can replace the segment separators '.' and ':'. // If so, then you can write addresses like *.* or *:* func (params *addressStringFormatParameters) AllowsWildcardedSeparator() bool { return !params.noWildcardedSeparator } // AllowsLeadingZeros indicates whether you allow addresses with segments that have leasing zeros like "001.2.3.004" or "1:000a::". // For IPV4, this option overrides inet_aton octal. // // Single segment addresses that must have the requisite length to be parsed are not affected by this flag. func (params *addressStringFormatParameters) AllowsLeadingZeros() bool { return !params.noLeadingZeros } // AllowsUnlimitedLeadingZeros determines if you allow leading zeros that extend segments // beyond the usual segment length, which is 3 for IPv4 dotted-decimal and 4 for IPv6. // However, this only takes effect if leading zeros are allowed, which is when // AllowsLeadingZeros is true or the address is IPv4 and Allows_inet_aton_octal is true. // // For example, this determines whether you allow "0001.0002.0003.0004". func (params *addressStringFormatParameters) AllowsUnlimitedLeadingZeros() bool { return !params.noUnlimitedLeadingZeros } // GetRangeParams returns the RangeParams describing whether ranges of values are allowed and what wildcards are allowed. func (params *addressStringFormatParameters) GetRangeParams() RangeParams { return ¶ms.rangeParams } // // // AddressStringFormatParamsBuilder creates parameters for parsing a specific address type or address version. type AddressStringFormatParamsBuilder struct { addressStringFormatParameters rangeParamsBuilder RangeParamsBuilder } // ToParams returns an immutable AddressStringFormatParams instance built by this builder. func (builder *AddressStringFormatParamsBuilder) ToParams() AddressStringFormatParams { result := &builder.addressStringFormatParameters result.rangeParams = *builder.rangeParamsBuilder.ToParams().(*rangeParameters) return result } func (builder *AddressStringFormatParamsBuilder) set(parms AddressStringFormatParams) { if p, ok := parms.(*addressStringFormatParameters); ok { builder.addressStringFormatParameters = *p } else { builder.addressStringFormatParameters = addressStringFormatParameters{ noWildcardedSeparator: !parms.AllowsWildcardedSeparator(), noLeadingZeros: !parms.AllowsLeadingZeros(), noUnlimitedLeadingZeros: !parms.AllowsUnlimitedLeadingZeros(), } } builder.rangeParamsBuilder.Set(parms.GetRangeParams()) } func (builder *AddressStringFormatParamsBuilder) setRangeParameters(rangeParams RangeParams) { builder.rangeParamsBuilder.Set(rangeParams) } // GetRangeParamsBuilder returns a builder that builds the range parameters for these address string format parameters. func (builder *AddressStringFormatParamsBuilder) GetRangeParamsBuilder() RangeParams { return &builder.rangeParamsBuilder } func (builder *AddressStringFormatParamsBuilder) allowWildcardedSeparator(allow bool) { builder.noWildcardedSeparator = !allow } func (builder *AddressStringFormatParamsBuilder) allowLeadingZeros(allow bool) { builder.noLeadingZeros = !allow } func (builder *AddressStringFormatParamsBuilder) allowUnlimitedLeadingZeros(allow bool) { builder.noUnlimitedLeadingZeros = !allow } ipaddress-go-1.5.4/ipaddr/addrstrparam/hostnameparams.go000066400000000000000000000241011440250641600234020ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package addrstrparam // CopyHostNameParams produces an immutable copy of the original HostNameParams. // Copying a HostNameParams created by a HostNameParamsBuilder is unnecessary since it is already immutable. func CopyHostNameParams(orig HostNameParams) HostNameParams { if p, ok := orig.(*hostNameParameters); ok { return p } return new(HostNameParamsBuilder).Set(orig).ToParams() } // HostNameParams provides parameters for parsing host name strings. // // This allows you to control the validation performed by HostName. // // HostName uses a default permissive HostNameParams object when you do not specify one. // // If you wish to use parameters different from the default, then use this interface. Immutable instances can be constructed with HostNameParamsBuilder. type HostNameParams interface { // AllowsEmpty determines if an empty host string is considered valid. // The parser will first parse as an empty address, if allowed by the nested IPAddressStringParams. // Otherwise, it will be considered an empty host if this returns true, or an invalid host if it returns false. AllowsEmpty() bool // GetPreferredVersion indicates the version to prefer when resolving host names. GetPreferredVersion() IPVersion // AllowsBracketedIPv4 allows bracketed IPv4 addresses like "[1.2.3.4]". AllowsBracketedIPv4() bool // AllowsBracketedIPv6 allows bracketed IPv6 addresses like "[1::2]". AllowsBracketedIPv6() bool // NormalizesToLowercase indicates whether to normalize the host name to lowercase characters when parsing. NormalizesToLowercase() bool // AllowsIPAddress allows a host name to specify an IP address or subnet. AllowsIPAddress() bool // AllowsPort allows a host name to specify a port. AllowsPort() bool // AllowsService allows a host name to specify a service, which typically maps to a port. AllowsService() bool // ExpectsPort indicates whether a port should be inferred from a host like 1:2:3:4::80 that is ambiguous if a port might have been appended. // The final segment would normally be considered part of the address, but can be interpreted as a port instead. ExpectsPort() bool // GetIPAddressParams returns the parameters that apply specifically to IP addresses and subnets, whenever a host name specifies an IP addresses or subnet. GetIPAddressParams() IPAddressStringParams } // hostNameParameters has parameters for parsing host name strings. // They are immutable and can be constructed using an HostNameParamsBuilder. type hostNameParameters struct { ipParams ipAddressStringParameters preferredVersion IPVersion noEmpty, noBracketedIPv4, noBracketedIPv6, noNormalizeToLower, noIPAddress, noPort, noService, expectPort bool } // AllowsEmpty determines if an empty host string is considered valid. // The parser will first parse as an empty address, if allowed by the nested IPAddressStringParams. // Otherwise, it will be considered an empty host if this returns true, or an invalid host if it returns false. func (params *hostNameParameters) AllowsEmpty() bool { return !params.noEmpty } // GetPreferredVersion indicates the version to prefer when resolving host names. func (params *hostNameParameters) GetPreferredVersion() IPVersion { return params.preferredVersion } // AllowsBracketedIPv4 allows bracketed IPv4 addresses like "[1.2.3.4]". func (params *hostNameParameters) AllowsBracketedIPv4() bool { return !params.noBracketedIPv4 } // AllowsBracketedIPv6 allows bracketed IPv6 addresses like "[1::2]". func (params *hostNameParameters) AllowsBracketedIPv6() bool { return !params.noBracketedIPv6 } // NormalizesToLowercase indicates whether to normalize the host name to lowercase characters when parsing. func (params *hostNameParameters) NormalizesToLowercase() bool { return !params.noNormalizeToLower } // AllowsIPAddress allows a host name to specify an IP address or subnet. func (params *hostNameParameters) AllowsIPAddress() bool { return !params.noIPAddress } // AllowsPort allows a host name to specify a port. func (params *hostNameParameters) AllowsPort() bool { return !params.noPort } // AllowsService allows a host name to specify a service, which typically maps to a port. func (params *hostNameParameters) AllowsService() bool { return !params.noService } // ExpectsPort indicates whether a port should be inferred from a host like 1:2:3:4::80 that is ambiguous if a port might have been appended. // The final segment would normally be considered part of the address, but can be interpreted as a port instead. func (params *hostNameParameters) ExpectsPort() bool { return params.expectPort } // GetIPAddressParams returns the parameters that apply specifically to IP addresses and subnets, whenever a host name specifies an IP addresses or subnet. func (params *hostNameParameters) GetIPAddressParams() IPAddressStringParams { return ¶ms.ipParams } // HostNameParamsBuilder builds an immutable HostNameParams for controlling parsing of host names. type HostNameParamsBuilder struct { hostNameParameters ipAddressBuilder IPAddressStringParamsBuilder } // ToParams returns an immutable HostNameParams instance built by this builder. func (builder *HostNameParamsBuilder) ToParams() HostNameParams { // We do not return a pointer to builder.hostNameParameters because that would make it possible to change params // by continuing to use the same builder, // and we want immutable objects for concurrency-safety, // so we cannot allow it result := builder.hostNameParameters result.ipParams = *builder.ipAddressBuilder.ToParams().(*ipAddressStringParameters) return &result } // GetIPAddressParamsBuilder returns a builder that builds the IPAddressStringParams for the HostNameParams being built by this builder. func (builder *HostNameParamsBuilder) GetIPAddressParamsBuilder() (result *IPAddressStringParamsBuilder) { result = &builder.ipAddressBuilder result.parent = builder return } // Set populates this builder with the values from the given HostNameParams. func (builder *HostNameParamsBuilder) Set(params HostNameParams) *HostNameParamsBuilder { if p, ok := params.(*hostNameParameters); ok { builder.hostNameParameters = *p } else { builder.hostNameParameters = hostNameParameters{ preferredVersion: params.GetPreferredVersion(), noEmpty: !params.AllowsEmpty(), noBracketedIPv4: !params.AllowsBracketedIPv4(), noBracketedIPv6: !params.AllowsBracketedIPv6(), noNormalizeToLower: !params.NormalizesToLowercase(), noIPAddress: !params.AllowsIPAddress(), noPort: !params.AllowsPort(), noService: !params.AllowsService(), expectPort: params.ExpectsPort(), } } builder.SetIPAddressParams(params.GetIPAddressParams()) return builder } // SetIPAddressParams populates this builder with the values from the given IPAddressStringParams. func (builder *HostNameParamsBuilder) SetIPAddressParams(params IPAddressStringParams) *HostNameParamsBuilder { //builder.ipAddressBuilder = *ToIPAddressStringParamsBuilder(params) builder.ipAddressBuilder.Set(params) return builder } // AllowEmpty dictates whether an empty host string is considered valid. // The parser will first parse as an empty address, if allowed by the nested IPAddressStringParams. // Otherwise, this setting dictates whether it will be considered an invalid host. func (builder *HostNameParamsBuilder) AllowEmpty(allow bool) *HostNameParamsBuilder { builder.hostNameParameters.noEmpty = !allow return builder } // SetPreferredVersion dictates the version to prefer when resolving host names. func (builder *HostNameParamsBuilder) SetPreferredVersion(version IPVersion) *HostNameParamsBuilder { builder.hostNameParameters.preferredVersion = version return builder } // AllowBracketedIPv4 dictates whether to allow bracketed IPv4 addresses like "[1.2.3.4]". func (builder *HostNameParamsBuilder) AllowBracketedIPv4(allow bool) *HostNameParamsBuilder { builder.hostNameParameters.noBracketedIPv4 = !allow return builder } // AllowBracketedIPv6 dictates whether to allow bracketed IPv6 addresses like "[1::2]". func (builder *HostNameParamsBuilder) AllowBracketedIPv6(allow bool) *HostNameParamsBuilder { builder.hostNameParameters.noBracketedIPv6 = !allow return builder } // NormalizeToLowercase dictates whether to normalize the host name to lowercase characters when parsing. func (builder *HostNameParamsBuilder) NormalizeToLowercase(allow bool) *HostNameParamsBuilder { builder.hostNameParameters.noNormalizeToLower = !allow return builder } // AllowIPAddress dictates whether to allow a host name to specify an IP address or subnet. func (builder *HostNameParamsBuilder) AllowIPAddress(allow bool) *HostNameParamsBuilder { builder.hostNameParameters.noIPAddress = !allow return builder } // AllowPort dictates whether to allow a host name to specify a port. func (builder *HostNameParamsBuilder) AllowPort(allow bool) *HostNameParamsBuilder { builder.hostNameParameters.noPort = !allow return builder } // AllowService dictates whether to allow a host name to specify a service, which typically maps to a port. func (builder *HostNameParamsBuilder) AllowService(allow bool) *HostNameParamsBuilder { builder.hostNameParameters.noService = !allow return builder } // ExpectPort dictates whether a port should be inferred from a host like 1:2:3:4::80 that is ambiguous if a port might have been appended. // The final segment would normally be considered part of the address, but can be interpreted as a port instead. func (builder *HostNameParamsBuilder) ExpectPort(expect bool) *HostNameParamsBuilder { builder.hostNameParameters.expectPort = expect return builder } ipaddress-go-1.5.4/ipaddr/addrstrparam/ipaddrstrparams.go000066400000000000000000001231721440250641600235700ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package addrstrparam import "strings" // CopyIPAddressStringParams produces an immutable copy of the original IPAddressStringParams. // Copying an IPAddressStringParams created by an IPAddressStringParamsBuilder is unnecessary since it is already immutable. func CopyIPAddressStringParams(orig IPAddressStringParams) IPAddressStringParams { if p, ok := orig.(*ipAddressStringParameters); ok { return p } return new(IPAddressStringParamsBuilder).Set(orig).ToParams() } // IPAddressStringParams provides parameters for parsing IP address strings, // indicating what to allow, what to disallow, and other options. // // This allows you to control the validation performed by IPAddressString. // // IPAddressString uses a default permissive IPAddressStringParams instance when you do not specify one. // // If you wish to use parameters different from the default, then use this interface. Immutable instances can be constructed with IPAddressStringParamsBuilder. type IPAddressStringParams interface { AddressStringParams // AllowsPrefix indicates whether addresses with prefix length like 1.2.0.0/16 are allowed. AllowsPrefix() bool // EmptyStrParsedAs determines how a zero-length empty string is translated to an address. // If the option is ZeroAddressOption or LoopbackOption, then if defers to GetPreferredVersion() for the version. EmptyStrParsedAs() EmptyStrOption // AllStrParsedAs determines how the "all" string "*" is translated to addresses. // If the option is AllPreferredIPVersion, then it defers to GetPreferredVersion() for the version. AllStrParsedAs() AllStrOption // AllowsMask allows masks to follow valid addresses, such as 1.2.3.4/255.255.0.0 which has the mask 255.255.0.0 // If the mask is the mask for a network prefix length, this is interpreted as the subnet for that network prefix length. // Otherwise the address is simply masked by the mask. // For instance, 1.2.3.4/255.0.255.0 is 1.0.3.0, while 1.2.3.4/255.255.0.0 is 1.2.0.0/16. AllowsMask() bool // GetPreferredVersion indicates the version to use for ambiguous addresses strings, // like prefix lengths less than 32 bits which are translated to masks, // the "all" address or the "empty" address. // The default is IPv6. // // If either of AllowsIPv4() or AllowsIPv6() returns false, then those settings take precedence over this setting. GetPreferredVersion() IPVersion // AllowsIPv4 allows IPv4 addresses and subnets. AllowsIPv4() bool // AllowsIPv6 allows IPv6 addresses and subnets. AllowsIPv6() bool // GetIPv4Params returns the parameters that apply specifically to IPv4 addresses and subnets. GetIPv4Params() IPv4AddressStringParams // GetIPv6Params returns the parameters that apply specifically to IPv6 addresses and subnets. GetIPv6Params() IPv6AddressStringParams } // EmptyStrOption is an option indicating how to translate an empty address string to an address. type EmptyStrOption string const ( // NoAddressOption indicates that empty strings are not translated to addresses. NoAddressOption EmptyStrOption = "none" // ZeroAddressOption is the default, which means empty strings are translated to zero addresses. ZeroAddressOption EmptyStrOption = "" // LoopbackOption indicates empty strings are translated to loopback addresses. LoopbackOption EmptyStrOption = "loopback" ) // AllStrOption is an option indicating how to translate an all address string, such as "*", to an address. type AllStrOption string const ( // AllAddresses is the default, indicating the all address string refers to all addresses of all IP versions. AllAddresses AllStrOption = "" // the default // AllPreferredIPVersion indicates the all address string refers to all addresses of the preferred IP version. AllPreferredIPVersion AllStrOption = "preferred" ) var _ IPAddressStringParams = &ipAddressStringParameters{} // IPv4AddressStringParams provides parameters specific to IPv4 addresses and subnets type IPv4AddressStringParams interface { IPAddressStringFormatParams // Allows_inet_aton_hex allows IPv4 inet_aton hexadecimal format "0xa.0xb.0xc.0cd". Allows_inet_aton_hex() bool // Allows_inet_aton_octal allows IPv4 inet_aton octal format, "04.05.06.07" being an example. // Can be overridden by allowLeadingZeros Allows_inet_aton_octal() bool // Allows_inet_aton_joinedSegments allows IPv4 joined segments like "1.2.3", "1.2", or just "1". // // For the case of just 1 segment, the behaviour is controlled by allowSingleSegment. Allows_inet_aton_joinedSegments() bool // Allows_inet_aton_single_segment_mask indicates whether you allow a mask that looks like a prefix length when you allow IPv4 joined segments: "1.2.3.5/255". Allows_inet_aton_single_segment_mask() bool // Allows_inet_aton_leading_zeros allows IPv4 inet_aton hexadecimal or octal to have leading zeros, such as in the first two segments of "0x0a.00b.c.d". // The first 0 is not considered a leading zero, it either denotes octal or hex depending on whether it is followed by an 'x'. // Zeros that appear afterwards are inet_aton leading zeros. Allows_inet_aton_leading_zeros() bool } var _ IPv4AddressStringParams = &ipv4AddressStringParameters{} // IPv6AddressStringParams provides parameters specific to IPv6 addresses and subnets. type IPv6AddressStringParams interface { IPAddressStringFormatParams // AllowsMixed allows mixed-in embedded IPv4 like "a:b:c:d:e:f:1.2.3.4". AllowsMixed() bool // AllowsZone allows zones like "a:b:c:d:e:f:a:b%zone". AllowsZone() bool // AllowsEmptyZone allows the zone character % with no following zone. AllowsEmptyZone() bool // AllowsBase85 allows IPv6 single-segment base 85 addresses. AllowsBase85() bool // GetMixedParams provides the IP parameters that for parsing the embedded IPv4 section of a mixed IPv6/v4 address, if AllowsMixed is true. GetMixedParams() IPAddressStringParams // GetEmbeddedIPv4AddressParams returns the IPv4 parameters for parsing the embedded IPv4 section of a mixed IPv6/v4 address. GetEmbeddedIPv4AddressParams() IPv4AddressStringParams } var _ IPv6AddressStringParams = &ipv6AddressStringParameters{} // IPAddressStringFormatParams provides format parameters that apply to all IP addresses, but can be different for IPv4 or IPv6, // allowing for cases where you may wish to allow something for one version but not the same for the other version. type IPAddressStringFormatParams interface { AddressStringFormatParams // AllowsPrefixesBeyondAddressSize allows prefix length values greater than 32 for IPv4 or greater than 128 for IPv6. AllowsPrefixesBeyondAddressSize() bool // AllowsPrefixLenLeadingZeros allows leading zeros in the prefix length like "1.2.3.4/016". AllowsPrefixLenLeadingZeros() bool // AllowsBinary allows binary addresses like "11111111.0.1.0" or "1111111111111111::". AllowsBinary() bool } func init() { defaultEmbeddedBuilder. AllowEmpty(false). AllowPrefix(false). AllowMask(false). AllowAll(false). AllowIPv6(false). GetIPv6AddressParamsBuilder(). AllowZone(true). AllowEmptyZone(true) defaultEmbeddedParams = defaultEmbeddedBuilder. ToParams().(*ipAddressStringParameters) } var defaultEmbeddedBuilder IPAddressStringParamsBuilder var defaultEmbeddedParams *ipAddressStringParameters // ipAddressStringParameters has parameters for parsing IP address strings. // They are immutable and can be constructed using an IPAddressStringParamsBuilder. type ipAddressStringParameters struct { addressStringParameters ipv4Params ipv4AddressStringParameters ipv6Params ipv6AddressStringParameters emptyStringOption EmptyStrOption allStringOption AllStrOption preferredVersion IPVersion //emptyIsNotLoopback, //noPrefixOnly, noPrefix, noMask, noIPv6, noIPv4 bool } // AllowsPrefix indicates whether addresses with prefix length like 1.2.0.0/16 are allowed. func (params *ipAddressStringParameters) AllowsPrefix() bool { return !params.noPrefix } // EmptyStrParsedAs determines how a zero-length empty string is translated to an address. // If the option is ZeroAddressOption or LoopbackOption, then if defers to GetPreferredVersion() for the version. func (params *ipAddressStringParameters) EmptyStrParsedAs() EmptyStrOption { return params.emptyStringOption } // AllStrParsedAs determines how the "all" string "*" is translated to addresses. // If the option is AllPreferredIPVersion, then it defers to GetPreferredVersion() for the version. func (params *ipAddressStringParameters) AllStrParsedAs() AllStrOption { return params.allStringOption } // GetPreferredVersion indicates the version to use for ambiguous addresses strings, // like prefix lengths less than 32 bits which are translated to masks, // the "all" address or the "empty" address. // The default is IPv6. // // If either of AllowsIPv4() or AllowsIPv6() returns false, then those settings take precedence over this setting. func (params *ipAddressStringParameters) GetPreferredVersion() IPVersion { return params.preferredVersion } // AllowsMask allows masks to follow valid addresses, such as 1.2.3.4/255.255.0.0 which has the mask 255.255.0.0 // If the mask is the mask for a network prefix length, this is interpreted as the subnet for that network prefix length. // Otherwise the address is simply masked by the mask. // For instance, 1.2.3.4/255.0.255.0 is 1.0.3.0, while 1.2.3.4/255.255.0.0 is 1.2.0.0/16. func (params *ipAddressStringParameters) AllowsMask() bool { return !params.noMask } // AllowsIPv4 allows IPv4 addresses and subnets. func (params *ipAddressStringParameters) AllowsIPv4() bool { return !params.noIPv4 } // AllowsIPv6 allows IPv6 addresses and subnets. func (params *ipAddressStringParameters) AllowsIPv6() bool { return !params.noIPv6 } // GetIPv4Params returns the parameters that apply specifically to IPv4 addresses and subnets. func (params *ipAddressStringParameters) GetIPv4Params() IPv4AddressStringParams { return ¶ms.ipv4Params } // GetIPv6Params returns the parameters that apply specifically to IPv6 addresses and subnets. func (params *ipAddressStringParameters) GetIPv6Params() IPv6AddressStringParams { return ¶ms.ipv6Params } // IPAddressStringParamsBuilder builds an immutable IPAddressStringParameters for controlling parsing of IP address strings. type IPAddressStringParamsBuilder struct { params ipAddressStringParameters AddressStringParamsBuilder ipv4Builder IPv4AddressStringParamsBuilder ipv6Builder IPv6AddressStringParamsBuilder parent *HostNameParamsBuilder } // GetParentBuilder returns the original HostNameParamsBuilder builder that this was obtained from, if this builder was obtained from a HostNameParamsBuilder. func (builder *IPAddressStringParamsBuilder) GetParentBuilder() *HostNameParamsBuilder { return builder.parent } // ToParams returns an immutable IPAddressStringParams instance built by this builder. func (builder *IPAddressStringParamsBuilder) ToParams() IPAddressStringParams { // We do not return a pointer to builder.params because that would make it possible to change a ipAddressStringParameters // by continuing to use the same builder, // and we want immutable objects for concurrency-safety, // so we cannot allow it result := builder.params result.addressStringParameters = *builder.AddressStringParamsBuilder.ToParams().(*addressStringParameters) result.ipv4Params = *builder.ipv4Builder.ToParams().(*ipv4AddressStringParameters) result.ipv6Params = *builder.ipv6Builder.ToParams().(*ipv6AddressStringParameters) return &result } // GetIPv6AddressParamsBuilder returns a builder that builds the IPv6AddressStringParams for the IPAddressStringParams being built by this builder. func (builder *IPAddressStringParamsBuilder) GetIPv6AddressParamsBuilder() (result *IPv6AddressStringParamsBuilder) { result = &builder.ipv6Builder result.parent = builder return } // GetIPv4AddressParamsBuilder returns a builder that builds the IPv4AddressStringParams for the IPAddressStringParams being built by this builder. func (builder *IPAddressStringParamsBuilder) GetIPv4AddressParamsBuilder() (result *IPv4AddressStringParamsBuilder) { result = &builder.ipv4Builder result.parent = builder return } // Set populates this builder with the values from the given IPAddressStringParams. func (builder *IPAddressStringParamsBuilder) Set(params IPAddressStringParams) *IPAddressStringParamsBuilder { return builder.set(params, false) } func (builder *IPAddressStringParamsBuilder) set(params IPAddressStringParams, isMixed bool) *IPAddressStringParamsBuilder { if p, ok := params.(*ipAddressStringParameters); ok { builder.params = *p } else { builder.params = ipAddressStringParameters{ preferredVersion: params.GetPreferredVersion(), emptyStringOption: params.EmptyStrParsedAs(), allStringOption: params.AllStrParsedAs(), noPrefix: !params.AllowsPrefix(), noMask: !params.AllowsMask(), noIPv6: !params.AllowsIPv6(), noIPv4: !params.AllowsIPv4(), } } builder.AddressStringParamsBuilder.set(params) builder.ipv4Builder.Set(params.GetIPv4Params()) builder.ipv6Builder.set(params.GetIPv6Params(), isMixed) return builder } // AllowEmpty dictates whether to allow empty zero-length address strings. func (builder *IPAddressStringParamsBuilder) AllowEmpty(allow bool) *IPAddressStringParamsBuilder { builder.allowEmpty(allow) return builder } // AllowSingleSegment dictates whether to allow an address to be specified as a single value, eg "ffffffff", without the standard use of segments like "1.2.3.4" or "1:2:4:3:5:6:7:8". func (builder *IPAddressStringParamsBuilder) AllowSingleSegment(allow bool) *IPAddressStringParamsBuilder { builder.allowSingleSegment(allow) return builder } // AllowAll dictates whether to alloww the string of just the wildcard "*" to denote all addresses of all version. // If false, then for IP addresses we check the preferred version with GetPreferredVersion, and then check AllowsWildcardedSeparator, // to determine if the string represents all addresses of that version. func (builder *IPAddressStringParamsBuilder) AllowAll(allow bool) *IPAddressStringParamsBuilder { builder.allowAll(allow) return builder } // ParseEmptyStrAs dictates how a zero-length empty string is translated to an address. // If the option is ZeroAddressOption or LoopbackOption, then if defers to GetPreferredVersion for the version. func (builder *IPAddressStringParamsBuilder) ParseEmptyStrAs(option EmptyStrOption) *IPAddressStringParamsBuilder { builder.params.emptyStringOption = option builder.AllowEmpty(true) return builder } // ParseAllStrAs dictates how the "all" string "*" is translated to addresses. //// If the option is AllPreferredIPVersion, then it defers to GetPreferredVersion for the version. func (builder *IPAddressStringParamsBuilder) ParseAllStrAs(option AllStrOption) *IPAddressStringParamsBuilder { builder.params.allStringOption = option return builder } // SetPreferredVersion dictates the version to use for ambiguous addresses strings, // like prefix lengths less than 32 bits which are translated to masks, // the "all" address or the "empty" address. // The default is IPv6. // // If either of AllowsIPv4 or AllowsIPv6 returns false, then those settings take precedence over this setting. func (builder *IPAddressStringParamsBuilder) SetPreferredVersion(version IPVersion) *IPAddressStringParamsBuilder { builder.params.preferredVersion = version return builder } // AllowPrefix dictates whether to allow addresses with prefix length like "1.2.0.0/16" are allowed. func (builder *IPAddressStringParamsBuilder) AllowPrefix(allow bool) *IPAddressStringParamsBuilder { builder.params.noPrefix = !allow return builder } // AllowMask dictates whether to allow masks to follow valid addresses, such as "1.2.3.4/255.255.0.0" which has the mask "255.255.0.0". // If the mask is the mask for a network prefix length, this is interpreted as the subnet for that network prefix length. // Otherwise the address is simply masked by the mask. // For instance, "1.2.3.4/255.0.255.0" is "1.0.3.0", while "1.2.3.4/255.255.0.0" is "1.2.0.0/16". func (builder *IPAddressStringParamsBuilder) AllowMask(allow bool) *IPAddressStringParamsBuilder { builder.params.noMask = !allow return builder } // AllowIPv4 dictates whether to allow IPv4 addresses and subnets func (builder *IPAddressStringParamsBuilder) AllowIPv4(allow bool) *IPAddressStringParamsBuilder { builder.params.noIPv4 = !allow return builder } // AllowIPv6 dictates whether to allow IPv6 addresses and subnets func (builder *IPAddressStringParamsBuilder) AllowIPv6(allow bool) *IPAddressStringParamsBuilder { builder.params.noIPv6 = !allow return builder } // AllowWildcardedSeparator dictates whether the wildcard '*' or '%' can replace the segment separators '.' and ':'. // If so, then you can write addresses like *.* or *:* func (builder *IPAddressStringParamsBuilder) AllowWildcardedSeparator(allow bool) *IPAddressStringParamsBuilder { builder.GetIPv4AddressParamsBuilder().AllowWildcardedSeparator(allow) builder.GetIPv6AddressParamsBuilder().AllowWildcardedSeparator(allow) return builder } // SetRangeParams populates this builder with the values from the given RangeParams. func (builder *IPAddressStringParamsBuilder) SetRangeParams(rangeParams RangeParams) *IPAddressStringParamsBuilder { builder.GetIPv4AddressParamsBuilder().SetRangeParams(rangeParams) builder.GetIPv6AddressParamsBuilder().SetRangeParams(rangeParams) return builder } // Allow_inet_aton dictates whether to allow any IPv4 inet_aton format, whether hex, octal, or joined segments. func (builder *IPAddressStringParamsBuilder) Allow_inet_aton(allow bool) *IPAddressStringParamsBuilder { builder.GetIPv4AddressParamsBuilder().Allow_inet_aton(allow) builder.GetIPv6AddressParamsBuilder().Allow_mixed_inet_aton(allow) return builder } type ipAddressStringFormatParameters struct { addressStringFormatParameters allowPrefixesBeyondAddrSize, noPrefixLengthLeadingZeros, noBinary bool } // AllowsPrefixesBeyondAddressSize allows prefix length values greater than 32 for IPv4 or greater than 128 for IPv6. func (params *ipAddressStringFormatParameters) AllowsPrefixesBeyondAddressSize() bool { return params.allowPrefixesBeyondAddrSize } // AllowsPrefixLenLeadingZeros allows leading zeros in the prefix length like "1.2.3.4/016". func (params *ipAddressStringFormatParameters) AllowsPrefixLenLeadingZeros() bool { return !params.noPrefixLengthLeadingZeros } // AllowsBinary allows binary addresses like "11111111.0.1.0" or "1111111111111111::". func (params *ipAddressStringFormatParameters) AllowsBinary() bool { return !params.noBinary } // IPAddressStringFormatParamsBuilder builds an immutable IPAddressStringFormatParams for controlling parsing of IP address strings. type IPAddressStringFormatParamsBuilder struct { AddressStringFormatParamsBuilder ipParams ipAddressStringFormatParameters parent *IPAddressStringParamsBuilder } // GetParentBuilder returns the original IPAddressStringParamsBuilder builder that this was obtained from, if this builder was obtained from a IPAddressStringParamsBuilder. func (builder *IPAddressStringFormatParamsBuilder) GetParentBuilder() *IPAddressStringParamsBuilder { return builder.parent } // ToParams returns an immutable IPAddressStringFormatParams instance built by this builder func (builder *IPAddressStringFormatParamsBuilder) ToParams() IPAddressStringFormatParams { result := &builder.ipParams result.addressStringFormatParameters = *builder.AddressStringFormatParamsBuilder.ToParams().(*addressStringFormatParameters) return result } func (builder *IPAddressStringFormatParamsBuilder) set(params IPAddressStringFormatParams) { if p, ok := params.(*ipAddressStringFormatParameters); ok { builder.ipParams = *p } else { builder.ipParams = ipAddressStringFormatParameters{ allowPrefixesBeyondAddrSize: params.AllowsPrefixesBeyondAddressSize(), noPrefixLengthLeadingZeros: !params.AllowsPrefixLenLeadingZeros(), noBinary: !params.AllowsBinary(), } } builder.AddressStringFormatParamsBuilder.set(params) } // AllowsPrefixesBeyondAddressSize allows prefix length values greater than 32 for IPv4 or greater than 128 for IPv6. func (builder *IPAddressStringFormatParamsBuilder) AllowsPrefixesBeyondAddressSize() bool { return builder.ipParams.AllowsPrefixesBeyondAddressSize() } // AllowsPrefixLenLeadingZeros allows leading zeros in the prefix length like "1.2.3.4/016". func (builder *IPAddressStringFormatParamsBuilder) AllowsPrefixLenLeadingZeros() bool { return builder.ipParams.AllowsPrefixLenLeadingZeros() } // AllowsBinary allows binary addresses like 11111111.0.1.0 or 1111111111111111:: func (builder *IPAddressStringFormatParamsBuilder) AllowsBinary() bool { return builder.ipParams.AllowsBinary() } func (builder *IPAddressStringFormatParamsBuilder) allowBinary(allow bool) { builder.ipParams.noBinary = !allow } func (builder *IPAddressStringFormatParamsBuilder) allowPrefixesBeyondAddressSize(allow bool) { builder.ipParams.allowPrefixesBeyondAddrSize = allow } func (builder *IPAddressStringFormatParamsBuilder) allowPrefixLengthLeadingZeros(allow bool) { builder.ipParams.noPrefixLengthLeadingZeros = !allow } type ipv6AddressStringParameters struct { ipAddressStringFormatParameters noMixed, noZone, noBase85, noEmptyZone bool embeddedParams *ipAddressStringParameters } // AllowsMixed allows mixed-in embedded IPv4 like "a:b:c:d:e:f:1.2.3.4". func (params *ipv6AddressStringParameters) AllowsMixed() bool { return !params.noMixed } // AllowsZone allows zones like "a:b:c:d:e:f:a:b%zone". func (params *ipv6AddressStringParameters) AllowsZone() bool { return !params.noZone } // AllowsEmptyZone allows the zone character % with no following zone' func (params *ipv6AddressStringParameters) AllowsEmptyZone() bool { return !params.noEmptyZone } // AllowsBase85 allows IPv6 single-segment base 85 addresses' func (params *ipv6AddressStringParameters) AllowsBase85() bool { return !params.noBase85 } // GetMixedParams provides the parameters that for parsing the embedded IPv4 section of a mixed IPv6/v4 address, if AllowsMixed is true' func (params *ipv6AddressStringParameters) GetMixedParams() IPAddressStringParams { result := params.embeddedParams if result == nil { result = defaultEmbeddedParams } return result } // GetEmbeddedIPv4AddressParams returns the IPv4 parameters for parsing the embedded IPv4 section of a mixed IPv6/v4 address' func (params *ipv6AddressStringParameters) GetEmbeddedIPv4AddressParams() IPv4AddressStringParams { return params.embeddedParams.GetIPv4Params() } // IPv6AddressStringParamsBuilder builds an immutable IPv6AddressStringParams for controlling parsing of IPv6 address strings' type IPv6AddressStringParamsBuilder struct { // This is not anonymous since it clashes with IPAddressStringFormatParamsBuilder, // both have ipAddressStringFormatParameters and AddressStringFormatParams // and thee builder IPAddressStringFormatParamsBuilder takes precedence params ipv6AddressStringParameters embeddedBuilder *IPAddressStringParamsBuilder IPAddressStringFormatParamsBuilder } // ToParams returns an immutable IPv6AddressStringParams instance built by this builder' func (builder *IPv6AddressStringParamsBuilder) ToParams() IPv6AddressStringParams { result := &builder.params result.ipAddressStringFormatParameters = *builder.IPAddressStringFormatParamsBuilder.ToParams().(*ipAddressStringFormatParameters) if emb := builder.embeddedBuilder; emb == nil { result.embeddedParams = defaultEmbeddedParams } else { result.embeddedParams = emb.ToParams().(*ipAddressStringParameters) } return result } // GetRangeParamsBuilder returns a builder that builds the range parameters for these IPv6 address string parameters. func (builder *IPv6AddressStringParamsBuilder) GetRangeParamsBuilder() *RangeParamsBuilder { result := &builder.rangeParamsBuilder result.parent = builder return result } // AllowsMixed allows mixed-in embedded IPv4 like "a:b:c:d:e:f:1.2.3.4". func (builder *IPv6AddressStringParamsBuilder) AllowsMixed() bool { return builder.params.AllowsMixed() } // AllowsZone allows zones like "a:b:c:d:e:f:a:b%zone". func (builder *IPv6AddressStringParamsBuilder) AllowsZone() bool { return builder.params.AllowsZone() } // AllowsEmptyZone allows the zone character % with no following zone. func (builder *IPv6AddressStringParamsBuilder) AllowsEmptyZone() bool { return builder.params.AllowsEmptyZone() } // AllowsBase85 allows IPv6 single-segment base 85 addresses. func (builder *IPv6AddressStringParamsBuilder) AllowsBase85() bool { return builder.params.AllowsBase85() } // AllowBase85 dictates whether to allow IPv6 single-segment base 85 addresses. func (builder *IPv6AddressStringParamsBuilder) AllowBase85(allow bool) *IPv6AddressStringParamsBuilder { builder.params.noBase85 = !allow return builder } // Set populates this builder with the values from the given IPv6AddressStringParams. func (builder *IPv6AddressStringParamsBuilder) Set(params IPv6AddressStringParams) *IPv6AddressStringParamsBuilder { return builder.set(params, false) } func (builder *IPv6AddressStringParamsBuilder) set(params IPv6AddressStringParams, isMixed bool) *IPv6AddressStringParamsBuilder { if p, ok := params.(*ipv6AddressStringParameters); ok { builder.params = *p } else { builder.params = ipv6AddressStringParameters{ noMixed: !params.AllowsMixed(), noZone: !params.AllowsZone(), noEmptyZone: !params.AllowsEmptyZone(), noBase85: !params.AllowsBase85(), } } builder.IPAddressStringFormatParamsBuilder.set(params) if !isMixed { builder.getEmbeddedIPv4ParametersBuilder().ipv4Builder.Set(params.GetEmbeddedIPv4AddressParams()) } return builder } // AllowZone dictates whether to allow zones like "a:b:c:d:e:f:a:b%zone". func (builder *IPv6AddressStringParamsBuilder) AllowZone(allow bool) *IPv6AddressStringParamsBuilder { builder.params.noZone = !allow //we must decide whether to treat the % character as a zone when parsing the mixed part //if considered zone, then the zone character is actually part of the encompassing IPv6 address //otherwise, the zone character is an sql wildcard that is part of the mixed address //So whether we consider the % character a zone must match the same setting for the encompassing address // ipv4Builder can be nil when builder == &defaultEmbeddedBuilder.ipv6Builder, see getEmbeddedIPv4ParametersBuilder() if ipv4Builder := builder.getEmbeddedIPv4ParametersBuilder(); ipv4Builder != nil { ipv4Builder.GetIPv6AddressParamsBuilder().params.noZone = !allow } return builder } // AllowEmptyZone dictates whether to allow the zone character % with no following zone func (builder *IPv6AddressStringParamsBuilder) AllowEmptyZone(allow bool) *IPv6AddressStringParamsBuilder { builder.params.noEmptyZone = !allow if ipv4Builder := builder.getEmbeddedIPv4ParametersBuilder(); ipv4Builder != nil { ipv4Builder.GetIPv6AddressParamsBuilder().params.noEmptyZone = !allow } return builder } // AllowMixed dictates whether to allow mixed-in embedded IPv4 like "a:b:c:d:e:f:1.2.3.4". func (builder *IPv6AddressStringParamsBuilder) AllowMixed(allow bool) *IPv6AddressStringParamsBuilder { builder.params.noMixed = !allow return builder } func (builder *IPv6AddressStringParamsBuilder) getEmbeddedIPv4ParametersBuilder() (result *IPAddressStringParamsBuilder) { if builder == &defaultEmbeddedBuilder.ipv6Builder { return nil } if result = builder.embeddedBuilder; result == nil { result = &IPAddressStringParamsBuilder{} // copy in proper default values for embedded IPv4 addresses, which differ from defaults for typical ipv4AddrType addresses *result = defaultEmbeddedBuilder builder.embeddedBuilder = result } result.GetIPv4AddressParamsBuilder().mixedParent = builder return } // GetEmbeddedIPv4AddressParamsBuilder returns a builder to build the IPv4 parameters that controls parsing of the embedded IPv4 section of a mixed IPv6/v4 address. func (builder *IPv6AddressStringParamsBuilder) GetEmbeddedIPv4AddressParamsBuilder() (result *IPv4AddressStringParamsBuilder) { return builder.getEmbeddedIPv4ParametersBuilder().GetIPv4AddressParamsBuilder() } // Allow_mixed_inet_aton dictates whether to allow inet_aton style formats, whether hex, octal, or joined segments, in the embedded IPv4 section of a mixed IPv6/v4 address. func (builder *IPv6AddressStringParamsBuilder) Allow_mixed_inet_aton(allow bool) *IPv6AddressStringParamsBuilder { builder.getEmbeddedIPv4ParametersBuilder().GetIPv4AddressParamsBuilder().Allow_inet_aton(allow) if allow { // if we allow inet_aton in the mixed part, then of course that insinuates that we allow the mixed part builder.AllowMixed(allow) } return builder } // AllowBinary dictates whether to allow binary addresses like "11111111.0.1.0" or "1111111111111111::". func (builder *IPv6AddressStringParamsBuilder) AllowBinary(allow bool) *IPv6AddressStringParamsBuilder { builder.GetEmbeddedIPv4AddressParamsBuilder().AllowBinary(allow) builder.allowBinary(allow) return builder } // AllowWildcardedSeparator dictates whether the wildcard '*' or '%' can replace the segment separators '.' and ':'. // If so, then you can write addresses like *.* or *:* func (builder *IPv6AddressStringParamsBuilder) AllowWildcardedSeparator(allow bool) *IPv6AddressStringParamsBuilder { builder.GetEmbeddedIPv4AddressParamsBuilder().AllowWildcardedSeparator(allow) builder.allowWildcardedSeparator(allow) return builder } // AllowLeadingZeros dictates whether to allow addresses with segments that have leasing zeros like "001.2.3.004" or "1:000a::". // For IPV4, this option overrides inet_aton octal. // // Single segment addresses that must have the requisite length to be parsed are not affected by this flag. func (builder *IPv6AddressStringParamsBuilder) AllowLeadingZeros(allow bool) *IPv6AddressStringParamsBuilder { builder.GetEmbeddedIPv4AddressParamsBuilder().allowLeadingZeros(allow) builder.allowLeadingZeros(allow) return builder } // AllowUnlimitedLeadingZeros dictates whether to allow leading zeros that extend segments // beyond the usual segment length, which is 3 for IPv4 dotted-decimal and 4 for IPv6. // However, this only takes effect if leading zeros are allowed, which is when // AllowsLeadingZeros is true or the address is IPv4 and Allows_inet_aton_octal is true. // // For example, this determines whether you allow 0001.0002.0003.0004 func (builder *IPv6AddressStringParamsBuilder) AllowUnlimitedLeadingZeros(allow bool) *IPv6AddressStringParamsBuilder { builder.GetEmbeddedIPv4AddressParamsBuilder().AllowUnlimitedLeadingZeros(allow) builder.allowUnlimitedLeadingZeros(allow) return builder } // SetRangeParams populates this builder with the values from the given RangeParams. func (builder *IPv6AddressStringParamsBuilder) SetRangeParams(rangeParams RangeParams) *IPv6AddressStringParamsBuilder { builder.GetEmbeddedIPv4AddressParamsBuilder().SetRangeParams(rangeParams) builder.setRangeParameters(rangeParams) return builder } // AllowPrefixesBeyondAddressSize dictates whether to allow prefix length values greater than 32 for IPv4 or greater than 128 for IPv6. func (builder *IPv6AddressStringParamsBuilder) AllowPrefixesBeyondAddressSize(allow bool) *IPv6AddressStringParamsBuilder { builder.allowPrefixesBeyondAddressSize(allow) return builder } // AllowPrefixLenLeadingZeros dictates whether to allow leading zeros in the prefix length like "1.2.3.4/016". func (builder *IPv6AddressStringParamsBuilder) AllowPrefixLenLeadingZeros(allow bool) *IPv6AddressStringParamsBuilder { builder.allowPrefixLengthLeadingZeros(allow) return builder } type ipv4AddressStringParameters struct { ipAddressStringFormatParameters no_inet_aton_hex, no_inet_aton_octal, no_inet_aton_joinedSegments, inet_aton_single_segment_mask, no_inet_aton_leading_zeros bool } // Allows_inet_aton_hex allows IPv4 inet_aton hexadecimal format "0xa.0xb.0xc.0cd". func (params *ipv4AddressStringParameters) Allows_inet_aton_hex() bool { return !params.no_inet_aton_hex } // Allows_inet_aton_octal allows IPv4 inet_aton octal format, "04.05.06.07" being an example. // Can be overridden by AllowLeadingZeros func (params *ipv4AddressStringParameters) Allows_inet_aton_octal() bool { return !params.no_inet_aton_octal } // Allows_inet_aton_joinedSegments allows IPv4 joined segments like "1.2.3", "1.2', or just "1". // // For the case of just 1 segment, the behaviour is controlled by allowSingleSegment func (params *ipv4AddressStringParameters) Allows_inet_aton_joinedSegments() bool { return !params.no_inet_aton_joinedSegments } // Allows_inet_aton_single_segment_mask indicates whether you allow a mask that looks like a prefix length when you allow IPv4 joined segments: "1.2.3.5/255". func (params *ipv4AddressStringParameters) Allows_inet_aton_single_segment_mask() bool { return params.inet_aton_single_segment_mask } // Allows_inet_aton_leading_zeros allows IPv4 inet_aton hexadecimal or octal to have leading zeros, such as in the first two segments of "0x0a.00b.c.d". // The first 0 is not considered a leading zero, it either denotes octal or hex depending on whether it is followed by an 'x'. // Zeros that appear afterwards are inet_aton leading zeros. func (params *ipv4AddressStringParameters) Allows_inet_aton_leading_zeros() bool { return !params.no_inet_aton_leading_zeros } // IPv4AddressStringParamsBuilder builds an immutable IPv4AddressStringParams for controlling parsing of IPv4 address strings. type IPv4AddressStringParamsBuilder struct { // This is not anonymous since it clashes with IPAddressStringFormatParamsBuilder, // both have ipAddressStringFormatParameters and AddressStringFormatParams // IPAddressStringFormatParamsBuilder takes precedence params ipv4AddressStringParameters IPAddressStringFormatParamsBuilder mixedParent *IPv6AddressStringParamsBuilder } // ToParams returns an immutable IPv4AddressStringParams instance built by this builder. func (builder *IPv4AddressStringParamsBuilder) ToParams() IPv4AddressStringParams { result := &builder.params result.ipAddressStringFormatParameters = *builder.IPAddressStringFormatParamsBuilder.ToParams().(*ipAddressStringFormatParameters) return result } // GetEmbeddedIPv4AddressParentBuilder the parent IPv6AddressStringParamsBuilder, // if this builder was obtained by a call to getEmbeddedIPv4ParamsBuilder() from IPv6AddressStringParamsBuilder. func (builder *IPv4AddressStringParamsBuilder) GetEmbeddedIPv4AddressParentBuilder() *IPv6AddressStringParamsBuilder { return builder.mixedParent } // GetRangeParamsBuilder returns a builder that builds the range parameters for these IPv4 address string parameters. func (builder *IPv4AddressStringParamsBuilder) GetRangeParamsBuilder() *RangeParamsBuilder { result := &builder.rangeParamsBuilder result.parent = builder return result } // Set populates this builder with the values from the given IPv4AddressStringParams. func (builder *IPv4AddressStringParamsBuilder) Set(params IPv4AddressStringParams) *IPv4AddressStringParamsBuilder { if p, ok := params.(*ipv4AddressStringParameters); ok { builder.params = *p } else { builder.params = ipv4AddressStringParameters{ no_inet_aton_hex: !params.Allows_inet_aton_hex(), no_inet_aton_octal: !params.Allows_inet_aton_octal(), no_inet_aton_joinedSegments: !params.Allows_inet_aton_joinedSegments(), inet_aton_single_segment_mask: params.Allows_inet_aton_single_segment_mask(), no_inet_aton_leading_zeros: !params.Allows_inet_aton_leading_zeros(), } } builder.IPAddressStringFormatParamsBuilder.set(params) return builder } // Allow_inet_aton dictates whether to allow any IPv4 inet_aton format, whether hex, octal, or joined segments. func (builder *IPv4AddressStringParamsBuilder) Allow_inet_aton(allow bool) *IPv4AddressStringParamsBuilder { builder.params.no_inet_aton_joinedSegments = !allow builder.params.no_inet_aton_octal = !allow builder.params.no_inet_aton_hex = !allow builder.allowUnlimitedLeadingZeros(allow) return builder } // Allow_inet_aton_hex dictates whether to allow IPv4 inet_aton hexadecimal format "0xa.0xb.0xc.0cd". func (builder *IPv4AddressStringParamsBuilder) Allow_inet_aton_hex(allow bool) *IPv4AddressStringParamsBuilder { builder.params.no_inet_aton_hex = !allow return builder } // Allow_inet_aton_octal dictates whether to allow IPv4 inet_aton octal format, "04.05.06.07" being an example. func (builder *IPv4AddressStringParamsBuilder) Allow_inet_aton_octal(allow bool) *IPv4AddressStringParamsBuilder { builder.params.no_inet_aton_octal = !allow return builder } // Allow_inet_aton_leading_zeros dictates whether to allow IPv4 inet_aton hexadecimal or octal to have leading zeros, such as in the first two segments of "0x0a.00b.c.d". // The first 0 is not considered a leading zero, it either denotes octal or hex depending on whether it is followed by an 'x'. // Zeros that appear afterwards are inet_aton leading zeros. func (builder *IPv4AddressStringParamsBuilder) Allow_inet_aton_leading_zeros(allow bool) *IPv4AddressStringParamsBuilder { builder.params.no_inet_aton_leading_zeros = !allow return builder } // Allow_inet_aton_joinedSegments dictates whether to allow IPv4 joined segments like "1.2.3", "1.2", or just "1". // // For the case of just 1 segment, the behaviour is controlled by AllowSingleSegment. func (builder *IPv4AddressStringParamsBuilder) Allow_inet_aton_joinedSegments(allow bool) *IPv4AddressStringParamsBuilder { builder.params.no_inet_aton_joinedSegments = !allow return builder } // Allow_inet_aton_single_segment_mask dictates whether to allow a mask that looks like a prefix length when you allow IPv4 joined segments: "1.2.3.5/255". func (builder *IPv4AddressStringParamsBuilder) Allow_inet_aton_single_segment_mask(allow bool) *IPv4AddressStringParamsBuilder { builder.params.inet_aton_single_segment_mask = allow return builder } // AllowWildcardedSeparator dictates whether the wildcard '*' or '%' can replace the segment separators '.' and ':'. // If so, then you can write addresses like *.* or *:* func (builder *IPv4AddressStringParamsBuilder) AllowWildcardedSeparator(allow bool) *IPv4AddressStringParamsBuilder { builder.allowWildcardedSeparator(allow) return builder } // AllowLeadingZeros dictates whether to allow addresses with segments that have leasing zeros like "001.2.3.004" or "1:000a::". // For IPV4, this option overrides inet_aton octal. // // Single segment addresses that must have the requisite length to be parsed are not affected by this flag. func (builder *IPv4AddressStringParamsBuilder) AllowLeadingZeros(allow bool) *IPv4AddressStringParamsBuilder { builder.allowLeadingZeros(allow) return builder } // AllowUnlimitedLeadingZeros dictates whether to allow leading zeros that extend segments // beyond the usual segment length, which is 3 for IPv4 dotted-decimal and 4 for IPv6. // However, this only takes effect if leading zeros are allowed, which is when // AllowsLeadingZeros is true or the address is IPv4 and Allows_inet_aton_octal is true. // // For example, this determines whether you allow "0001.0002.0003.0004"> func (builder *IPv4AddressStringParamsBuilder) AllowUnlimitedLeadingZeros(allow bool) *IPv4AddressStringParamsBuilder { builder.allowUnlimitedLeadingZeros(allow) return builder } // SetRangeParams populates this builder with the values from the given RangeParams. func (builder *IPv4AddressStringParamsBuilder) SetRangeParams(rangeParams RangeParams) *IPv4AddressStringParamsBuilder { builder.setRangeParameters(rangeParams) return builder } // AllowPrefixesBeyondAddressSize dictates whether to allow prefix length values greater than 32 for IPv4 or greater than 128 for IPv6. func (builder *IPv4AddressStringParamsBuilder) AllowPrefixesBeyondAddressSize(allow bool) *IPv4AddressStringParamsBuilder { builder.allowPrefixesBeyondAddressSize(allow) return builder } // AllowPrefixLenLeadingZeros dictates whether to allow leading zeros in the prefix length like "1.2.3.4/016". func (builder *IPv4AddressStringParamsBuilder) AllowPrefixLenLeadingZeros(allow bool) *IPv4AddressStringParamsBuilder { builder.allowPrefixLengthLeadingZeros(allow) return builder } // AllowBinary dictates whether to allow binary addresses like "11111111.0.1.0" or "1111111111111111::". func (builder *IPv4AddressStringParamsBuilder) AllowBinary(allow bool) *IPv4AddressStringParamsBuilder { builder.allowBinary(allow) return builder } // IPVersion is the version type used by IP string parameters. // It is interchangeable with the ipaddr.Version, the more generic version type used by the library as a whole. type IPVersion string const ( // IndeterminateIPVersion represents an unspecified IP address version. IndeterminateIPVersion IPVersion = "" // IPv4 represents Internet Protocol version 4. IPv4 IPVersion = "IPv4" // IPv6 represents Internet Protocol version 6. IPv6 IPVersion = "IPv6" ) // IsIPv6 returns true if this represents version 6. func (version IPVersion) IsIPv6() bool { return strings.EqualFold(string(version), string(IPv6)) } // IsIPv4 returns true if this represents version 4. func (version IPVersion) IsIPv4() bool { return strings.EqualFold(string(version), string(IPv4)) } // IsIndeterminate returns true if this represents an unspecified IP address version. func (version IPVersion) IsIndeterminate() bool { if len(version) == 4 { // we allow mixed case in the event code is converted a string to IPVersion dig := version[3] return (dig != '4' && dig != '6') || !strings.EqualFold(string(version[:3]), "IPv") } return true } // String returns "IPv4", "IPv6", or the zero-value "" representing an indeterminate version. func (version IPVersion) String() string { return string(version) } ipaddress-go-1.5.4/ipaddr/addrstrparam/macaddrstrparams.go000066400000000000000000000333201440250641600237130ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package addrstrparam // MACAddressLen is an option indicating a MAC address length. type MACAddressLen string const ( // MAC48Len indicates 48-bit MAC addresses. MAC48Len MACAddressLen = "MAC48" // EUI64Len indicates 64-bit MAC addresses. EUI64Len MACAddressLen = "EUI64" // UnspecifiedMACLen indicates unspecified bit-length MAC addresses. UnspecifiedMACLen MACAddressLen = "" ) // CopyMACAddressStringParams produces an immutable copy of the original MACAddressStringParams. // Copying a MACAddressStringParams created by a MACAddressStringParamsBuilder is unnecessary since it is already immutable. func CopyMACAddressStringParams(orig MACAddressStringParams) MACAddressStringParams { if p, ok := orig.(*macAddressStringParameters); ok { return p } return new(MACAddressStringParamsBuilder).Set(orig).ToParams() } // MACAddressStringParams provides parameters for parsing MAC address strings. // // This allows you to control the validation performed by the MACAddressString. // // MACAddressString uses a default permissive MACAddressStringParams instance when you do not specify one. // // If you wish to use parameters different from the default, then use this interface. Immutable instances can be constructed with MACAddressStringParamsBuilder. type MACAddressStringParams interface { AddressStringParams // GetPreferredLen indicates whether an ambiguous address like * is considered to be MAC 6 bytes, EUI-64 8 bytes, or either one. GetPreferredLen() MACAddressLen // AllowsDashed allows addresses like "aa-bb-cc-dd-ee-ff". AllowsDashed() bool // AllowsSingleDashed allows addresses like "aabbcc-ddeeff". AllowsSingleDashed() bool // AllowsColonDelimited allows addresses like "aa:bb:cc:dd:ee:ff". AllowsColonDelimited() bool // AllowsDotted allows addresses like "aaa.bbb.ccc.ddd". AllowsDotted() bool // AllowsSpaceDelimited allows addresses like "aa bb cc dd ee ff". AllowsSpaceDelimited() bool // GetFormatParams returns the parameters that apply to formatting of the address segments. GetFormatParams() MACAddressStringFormatParams } var _ MACAddressStringParams = &macAddressStringParameters{} // MACAddressStringFormatParams provides format parameters for MAC addresses, indicating what formatting is allowed. type MACAddressStringFormatParams interface { AddressStringFormatParams // AllowsShortSegments allows segments that are just a single hex digit and not two. AllowsShortSegments() bool } var _ MACAddressStringFormatParams = &macAddressStringFormatParameters{} // macAddressStringParameters has parameters for parsing MAC address strings // They are immutable and must be constructed using an IPAddressStringParamsBuilder type macAddressStringParameters struct { addressStringParameters formatParams macAddressStringFormatParameters noAllowDashed, noAllowSingleDashed, noAllowColonDelimited, noAllowDotted, noAllowSpaceDelimited bool allAddresses MACAddressLen } // GetPreferredLen indicates whether an ambiguous address like * is considered to be MAC 6 bytes, EUI-64 8 bytes, or either one. func (params *macAddressStringParameters) GetPreferredLen() MACAddressLen { return params.allAddresses } // AllowsDashed allows addresses like "aa-bb-cc-dd-ee-ff". func (params *macAddressStringParameters) AllowsDashed() bool { return !params.noAllowDashed } // AllowsSingleDashed allows addresses like "aabbcc-ddeeff". func (params *macAddressStringParameters) AllowsSingleDashed() bool { return !params.noAllowSingleDashed } // AllowsColonDelimited allows addresses like "aa:bb:cc:dd:ee:ff". func (params *macAddressStringParameters) AllowsColonDelimited() bool { return !params.noAllowColonDelimited } // AllowsDotted allows addresses like "aaa.bbb.ccc.ddd". func (params *macAddressStringParameters) AllowsDotted() bool { return !params.noAllowDotted } // AllowsSpaceDelimited allows addresses like "aa bb cc dd ee ff". func (params *macAddressStringParameters) AllowsSpaceDelimited() bool { return !params.noAllowSpaceDelimited } // GetFormatParams returns the parameters that apply to formatting of the address segments. func (params *macAddressStringParameters) GetFormatParams() MACAddressStringFormatParams { return ¶ms.formatParams } // MACAddressStringParamsBuilder builds an immutable MACAddressStringParameters for controlling parsing of MAC address strings. type MACAddressStringParamsBuilder struct { params macAddressStringParameters AddressStringParamsBuilder formatBuilder MACAddressStringFormatParamsBuilder } // ToParams returns an immutable MACAddressStringParams instance built by this builder. func (builder *MACAddressStringParamsBuilder) ToParams() MACAddressStringParams { // We do not return a pointer to builder.params because that would make it possible to change a macAddressStringParameters // by continuing to use the same builder, // and we want immutable objects for concurrency-safety, // so we cannot allow it result := builder.params result.addressStringParameters = *builder.AddressStringParamsBuilder.ToParams().(*addressStringParameters) result.formatParams = *builder.formatBuilder.ToParams().(*macAddressStringFormatParameters) return &result } // GetFormatParamsBuilder returns a builder that builds the MACAddressStringFormatParams for the MACAddressStringParams being built by this builder. func (builder *MACAddressStringParamsBuilder) GetFormatParamsBuilder() (result *MACAddressStringFormatParamsBuilder) { result = &builder.formatBuilder result.parent = builder return } // Set populates this builder with the values from the given MACAddressStringParams. func (builder *MACAddressStringParamsBuilder) Set(params MACAddressStringParams) *MACAddressStringParamsBuilder { if p, ok := params.(*macAddressStringParameters); ok { builder.params = *p } else { builder.params = macAddressStringParameters{ noAllowDashed: !params.AllowsDashed(), noAllowSingleDashed: !params.AllowsSingleDashed(), noAllowColonDelimited: !params.AllowsColonDelimited(), noAllowDotted: !params.AllowsDotted(), noAllowSpaceDelimited: !params.AllowsSpaceDelimited(), allAddresses: params.GetPreferredLen(), } } builder.AddressStringParamsBuilder.set(params) builder.formatBuilder.Set(params.GetFormatParams()) return builder } // AllowEmpty dictates whether to allow empty zero-length address strings. func (builder *MACAddressStringParamsBuilder) AllowEmpty(allow bool) *MACAddressStringParamsBuilder { builder.allowEmpty(allow) return builder } // AllowSingleSegment dictates whether to allow an address to be specified as a single value, eg "ffffffff", without the standard use of segments like "1.2.3.4" or "1:2:4:3:5:6:7:8". func (builder *MACAddressStringParamsBuilder) AllowSingleSegment(allow bool) *MACAddressStringParamsBuilder { builder.allowSingleSegment(allow) return builder } // AllowAll dictates whether to allow the string of just the wildcard "*" to denote all MAC addresses. func (builder *MACAddressStringParamsBuilder) AllowAll(allow bool) *MACAddressStringParamsBuilder { builder.allowAll(allow) return builder } // SetPreferredLen indicates the length for an ambiguous address like *, whether it is considered to be MAC 6 bytes, EUI-64 8 bytes, or either one. func (builder *MACAddressStringParamsBuilder) SetPreferredLen(size MACAddressLen) *MACAddressStringParamsBuilder { builder.params.allAddresses = size return builder } // AllowDashed dictates whether to allow addresses like "aa-bb-cc-dd-ee-ff". func (builder *MACAddressStringParamsBuilder) AllowDashed(allow bool) *MACAddressStringParamsBuilder { builder.params.noAllowDashed = !allow return builder } // AllowSingleDashed dictates whether to allow addresses like "aabbcc-ddeeff". func (builder *MACAddressStringParamsBuilder) AllowSingleDashed(allow bool) *MACAddressStringParamsBuilder { builder.params.noAllowSingleDashed = !allow return builder } // AllowColonDelimited dictates whether to allow addresses like "aa:bb:cc:dd:ee:ff". func (builder *MACAddressStringParamsBuilder) AllowColonDelimited(allow bool) *MACAddressStringParamsBuilder { builder.params.noAllowColonDelimited = !allow return builder } // AllowDotted dictates whether to allow addresses like "aaa.bbb.ccc.ddd". func (builder *MACAddressStringParamsBuilder) AllowDotted(allow bool) *MACAddressStringParamsBuilder { builder.params.noAllowDotted = !allow return builder } // AllowSpaceDelimited dictates whether to allow addresses like "aa bb cc dd ee ff". func (builder *MACAddressStringParamsBuilder) AllowSpaceDelimited(allow bool) *MACAddressStringParamsBuilder { builder.params.noAllowSpaceDelimited = !allow return builder } // these two are just for convenience // AllowWildcardedSeparator dictates whether the wildcard '*' or '%' can replace the segment separators '.', '-' and ':'. // If so, then you can write addresses like "*.*" or "*:*". func (builder *MACAddressStringParamsBuilder) AllowWildcardedSeparator(allow bool) *MACAddressStringParamsBuilder { builder.GetFormatParamsBuilder().AllowWildcardedSeparator(allow) return builder } // SetRangeParams populates this builder with the values from the given RangeParams. func (builder *MACAddressStringParamsBuilder) SetRangeParams(rangeParams RangeParams) *MACAddressStringParamsBuilder { builder.GetFormatParamsBuilder().SetRangeParams(rangeParams) return builder } type macAddressStringFormatParameters struct { addressStringFormatParameters noShortSegments bool } // AllowsShortSegments allows segments that are just a single hex digit and not two. func (params *macAddressStringFormatParameters) AllowsShortSegments() bool { return !params.noShortSegments } // MACAddressStringFormatParamsBuilder builds an immutable MACAddressStringFormatParams for controlling parsing of MAC address strings. type MACAddressStringFormatParamsBuilder struct { // This is not anonymous since it clashes with AddressStringFormatParamsBuilder, // both have AddressStringFormatParams // AddressStringFormatParamsBuilder takes precedence params macAddressStringFormatParameters AddressStringFormatParamsBuilder parent *MACAddressStringParamsBuilder } // GetParentBuilder returns the original MACAddressStringParamsBuilder builder that this was obtained from, if this builder was obtained from a MACAddressStringParamsBuilder. func (builder *MACAddressStringFormatParamsBuilder) GetParentBuilder() *MACAddressStringParamsBuilder { return builder.parent } // ToParams returns an immutable MACAddressStringFormatParams instance built by this builder. func (builder *MACAddressStringFormatParamsBuilder) ToParams() MACAddressStringFormatParams { result := &builder.params result.addressStringFormatParameters = *builder.AddressStringFormatParamsBuilder.ToParams().(*addressStringFormatParameters) return result } // GetRangeParamsBuilder returns a builder that builds the range parameters for these MAC address string format parameters. func (builder *MACAddressStringFormatParamsBuilder) GetRangeParamsBuilder() *RangeParamsBuilder { result := &builder.rangeParamsBuilder result.parent = builder return result } // Set populates this builder with the values from the given MACAddressStringFormatParams. func (builder *MACAddressStringFormatParamsBuilder) Set(parms MACAddressStringFormatParams) *MACAddressStringFormatParamsBuilder { if p, ok := parms.(*macAddressStringFormatParameters); ok { builder.params = *p } else { builder.params = macAddressStringFormatParameters{ noShortSegments: !parms.AllowsShortSegments(), } } builder.AddressStringFormatParamsBuilder.set(parms) return builder } // AllowWildcardedSeparator dictates whether the wildcard '*' or '%' can replace the segment separators '.', '-' and ':'. // If so, then you can write addresses like "*.*" or "*:*". func (builder *MACAddressStringFormatParamsBuilder) AllowWildcardedSeparator(allow bool) *MACAddressStringFormatParamsBuilder { builder.allowWildcardedSeparator(allow) return builder } // AllowLeadingZeros dictates whether to allow addresses with segments that have leasing zeros like "01:02:03:04:05:06". // // Single segment addresses that must have the requisite length to be parsed are not affected by this flag. func (builder *MACAddressStringFormatParamsBuilder) AllowLeadingZeros(allow bool) *MACAddressStringFormatParamsBuilder { builder.allowLeadingZeros(allow) return builder } // AllowUnlimitedLeadingZeros dictates whether to allow leading zeros that extend segments // beyond the usual segment length of 2 hex digits. // However, this only takes effect if leading zeros are allowed, which is when AllowsLeadingZeros is true. func (builder *MACAddressStringFormatParamsBuilder) AllowUnlimitedLeadingZeros(allow bool) *MACAddressStringFormatParamsBuilder { builder.allowUnlimitedLeadingZeros(allow) return builder } // SetRangeParams populates this builder with the values from the given RangeParams. func (builder *MACAddressStringFormatParamsBuilder) SetRangeParams(rangeParams RangeParams) *MACAddressStringFormatParamsBuilder { builder.setRangeParameters(rangeParams) return builder } // AllowShortSegments dictates whether to allow segments that are just a single hex digit and not two. func (builder *MACAddressStringFormatParamsBuilder) AllowShortSegments(allow bool) *MACAddressStringFormatParamsBuilder { builder.params.noShortSegments = !allow return builder } ipaddress-go-1.5.4/ipaddr/addrtrie.go000066400000000000000000002165711440250641600175100ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "github.com/seancfoley/bintree/tree" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "unsafe" ) type trieBase[T TrieKeyConstraint[T], V any] struct { trie tree.BinTrie[trieKey[T], V] } // clear removes all added nodes from the trie, after which IsEmpty will return true. func (trie *trieBase[T, V]) clear() { trie.trie.Clear() } // getRoot returns the root node of this trie, which can be nil for an implicitly zero-valued uninitialized trie, but not for any other trie. func (trie *trieBase[T, V]) getRoot() *tree.BinTrieNode[trieKey[T], V] { return trie.trie.GetRoot() } func (trie *trieBase[T, V]) add(addr T) bool { addr = mustBeBlockOrAddress(addr) return trie.trie.Add(trieKey[T]{addr}) } func (trie *trieBase[T, V]) addNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return trie.trie.AddNode(trieKey[T]{addr}) } // constructAddedNodesTree constructs an associative trie in which the root and each added node are mapped to a list of their respective direct added sub-nodes. // This trie provides an alternative non-binary tree structure of the added nodes. // It is used by ToAddedNodesTreeString to produce a string showing the alternative structure. // If there are no non-added nodes in this trie, then the alternative tree structure provided by this method is the same as the original trie. func (trie *trieBase[T, V]) constructAddedNodesTree() trieBase[T, tree.AddedSubnodeMapping] { return trieBase[T, tree.AddedSubnodeMapping]{trie.trie.ConstructAddedNodesTree()} // BinTrie[E, AddedSubnodeMapping] } func (trie *trieBase[T, V]) addTrie(added *trieNode[T, V]) *tree.BinTrieNode[trieKey[T], V] { return trie.trie.AddTrie(added.toBinTrieNode()) } func (trie *trieBase[T, V]) contains(addr T) bool { addr = mustBeBlockOrAddress(addr) return trie.trie.Contains(trieKey[T]{addr}) } func (trie *trieBase[T, V]) remove(addr T) bool { addr = mustBeBlockOrAddress(addr) return trie.trie.Remove(trieKey[T]{addr}) } func (trie *trieBase[T, V]) removeElementsContainedBy(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return trie.trie.RemoveElementsContainedBy(trieKey[T]{addr}) } func (trie *trieBase[T, V]) elementsContainedBy(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return trie.trie.ElementsContainedBy(trieKey[T]{addr}) } func (trie *trieBase[T, V]) elementsContaining(addr T) *containmentPath[T, V] { addr = mustBeBlockOrAddress(addr) return toContainmentPath[T, V](trie.trie.ElementsContaining(trieKey[T]{addr})) } func (trie *trieBase[T, V]) longestPrefixMatch(addr T) (t T) { addr = mustBeBlockOrAddress(addr) key, _ := trie.trie.LongestPrefixMatch(trieKey[T]{addr}) return key.address } // only added nodes are added to the linked list func (trie *trieBase[T, V]) longestPrefixMatchNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return trie.trie.LongestPrefixMatchNode(trieKey[T]{addr}) } func (trie *trieBase[T, V]) elementContains(addr T) bool { addr = mustBeBlockOrAddress(addr) return trie.trie.ElementContains(trieKey[T]{addr}) } func (trie *trieBase[T, V]) getNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return trie.trie.GetNode(trieKey[T]{addr}) } func (trie *trieBase[T, V]) getAddedNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return trie.trie.GetAddedNode(trieKey[T]{addr}) } func (trie *trieBase[T, V]) iterator() Iterator[T] { if trie == nil { return nilAddressIterator[T]() } return addressKeyIterator[T]{trie.trie.Iterator()} } func (trie *trieBase[T, V]) descendingIterator() Iterator[T] { if trie == nil { return nilAddressIterator[T]() } return addressKeyIterator[T]{trie.trie.DescendingIterator()} } func (trie *trieBase[T, V]) nodeIterator(forward bool) tree.TrieNodeIteratorRem[trieKey[T], V] { return trie.toTrie().NodeIterator(forward) } func (trie *trieBase[T, V]) allNodeIterator(forward bool) tree.TrieNodeIteratorRem[trieKey[T], V] { return trie.toTrie().AllNodeIterator(forward) } // Iterates the added nodes in the trie, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order func (trie *trieBase[T, V]) blockSizeNodeIterator(lowerSubNodeFirst bool) tree.TrieNodeIteratorRem[trieKey[T], V] { return trie.toTrie().BlockSizeNodeIterator(lowerSubNodeFirst) } // Iterates all nodes in the trie, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order func (trie *trieBase[T, V]) blockSizeAllNodeIterator(lowerSubNodeFirst bool) tree.TrieNodeIteratorRem[trieKey[T], V] { return trie.toTrie().BlockSizeAllNodeIterator(lowerSubNodeFirst) } // Iterates all nodes, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. func (trie *trieBase[T, V]) blockSizeCachingAllNodeIterator() tree.CachingTrieNodeIterator[trieKey[T], V] { return trie.toTrie().BlockSizeCachingAllNodeIterator() } // Iterates all nodes, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. func (trie *trieBase[T, V]) containingFirstIterator(forwardSubNodeOrder bool) tree.CachingTrieNodeIterator[trieKey[T], V] { return trie.toTrie().ContainingFirstIterator(forwardSubNodeOrder) } func (trie *trieBase[T, V]) containingFirstAllNodeIterator(forwardSubNodeOrder bool) tree.CachingTrieNodeIterator[trieKey[T], V] { return trie.toTrie().ContainingFirstAllNodeIterator(forwardSubNodeOrder) } func (trie *trieBase[T, V]) containedFirstIterator(forwardSubNodeOrder bool) tree.TrieNodeIteratorRem[trieKey[T], V] { return trie.toTrie().ContainedFirstIterator(forwardSubNodeOrder) } func (trie *trieBase[T, V]) containedFirstAllNodeIterator(forwardSubNodeOrder bool) tree.TrieNodeIterator[trieKey[T], V] { return trie.toTrie().ContainedFirstAllNodeIterator(forwardSubNodeOrder) } func (trie *trieBase[T, V]) lowerAddedNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return trie.trie.LowerAddedNode(trieKey[T]{addr}) } func (trie *trieBase[T, V]) floorAddedNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return trie.trie.FloorAddedNode(trieKey[T]{addr}) } func (trie *trieBase[T, V]) higherAddedNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return trie.trie.HigherAddedNode(trieKey[T]{addr}) } func (trie *trieBase[T, V]) ceilingAddedNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return trie.trie.CeilingAddedNode(trieKey[T]{addr}) } func (trie *trieBase[T, V]) clone() *tree.BinTrie[trieKey[T], V] { return trie.toTrie().Clone() } func (trie *trieBase[T, V]) toTrie() *tree.BinTrie[trieKey[T], V] { return (*tree.BinTrie[trieKey[T], V])(unsafe.Pointer(trie)) } // // Trie is a compact binary trie (aka compact binary prefix tree, or binary radix trie), for addresses and/or CIDR prefix block subnets. // The prefixes in used by the prefix trie are the CIDR prefixes, or the full address in the case of individual addresses with no prefix length. // The elements of the trie are CIDR prefix blocks or addresses. // // For the generic type T, you can choose *Address, *IPAddress, *IPv4Address, *IPv6Address, or *MACAddress. // // The zero-value of an AddressTrie is a trie ready for use. Its root will be nil until an element is added to it. // Once any subnet or address is added to the trie, it will have an assigned root, and any further addition to the trie must match the type and version of the root, // in addition to the generic type of the trie's keys. // Once there is a root, the root cannot be removed. // // So, for instance, an instance of ipaddr.Trie[*ipaddr.IPAddress] can contain either IPv4 or IPv6 keys, but not both. // Once it has been populated with the first key, all remaining additions must have the same IP version, even if the trie is cleared. // // Any trie can be copied. If a trie has no root, a copy produces a new zero-valued trie with no root. // If a trie has a root, a copy produces a reference to the same trie, much like copying a map or slice. // // The trie data structure allows you to check an address for containment in many subnets at once, in constant time. // The trie allows you to check a subnet for containment of many smaller subnets or addresses at once, in constant time. // The trie allows you to check for equality of a subnet or address with a large number of subnets or addresses at once. // // There is only a single possible trie for any given set of address and subnets. For one thing, this means they are automatically balanced. // Also, this makes access to subtries and to the nodes themselves more useful, allowing for many of the same operations performed on the original trie. // // Each node has either a prefix block or a single address as its key. // Each prefix block node can have two sub-nodes, each sub-node a prefix block or address contained by the node. // // There are more nodes in the trie than elements added to the trie. // A node is considered "added" if it was explicitly added to the trie and is included as an element when viewed as a set. // There are non-added prefix block nodes that are generated in the trie as well. // When two or more added addresses share the same prefix up until they differ with the bit at index x, // then a prefix block node is generated (if not already added to the trie) for the common prefix of length x, // with the nodes for those addresses to be found following the lower // or upper sub-nodes according to the bit at index x + 1 in each address. // If that bit is 1, the node can be found by following the upper sub-node, // and when it is 0, the lower sub-node. // // Nodes that were generated as part of the trie structure only // because of other added elements are not elements of the represented set of addresses and subnets. // The set elements are the elements that were explicitly added. // // You can work with parts of the trie, starting from any node in the trie, // calling methods that start with any given node, such as iterating the subtrie, // finding the first or last in the subtrie, doing containment checks with the subtrie, and so on. // // The binary trie structure defines a natural ordering of the trie elements. // Addresses of equal prefix length are sorted by prefix value. Addresses with no prefix length are sorted by address value. // Addresses of differing prefix length are sorted according to the bit that follows the shorter prefix length in the address with the longer prefix length, // whether that bit is 0 or 1 determines if that address is ordered before or after the address of shorter prefix length. // // The unique and pre-defined structure for a trie means that different means of traversing the trie can be more meaningful. // This trie implementation provides 8 different ways of iterating through the trie: // - 1, 2: the natural sorted trie order, forward and reverse (spliterating is also an option for these two orders). Use the methods NodeIterator, Iterator or DescendingIterator. Functions for incrementing and decrementing keys, or comparing keys, is also provided for this order. // - 3, 4: pre-order tree traversal, in which parent node is visited before sub-nodes, with sub-nodes visited in forward or reverse order // - 5, 6: post-order tree traversal, in which sub-nodes are visited before parent nodes, with sub-nodes visited in forward or reverse order // - 7, 8: prefix-block order, in which larger prefix blocks are visited before smaller, and blocks of equal size are visited in forward or reverse sorted order // // All of these orderings are useful in specific contexts. // // If you create an iterator, then that iterator can no longer be advanced following any further modification to the trie. // Any call to Next or Remove will panic if the trie was changed following creation of the iterator. // // You can do lookup and containment checks on all the subnets and addresses in the trie at once, in constant time. // A generic trie data structure lookup is O(m) where m is the entry length. // For this trie, which operates on address bits, entry length is capped at 128 bits for IPv6 and 32 bits for IPv4. // That makes lookup a constant time operation. // Subnet containment or equality checks are also constant time since they work the same way as lookup, by comparing prefix bits. // // For a generic trie data structure, construction is O(m * n) where m is entry length and n is the number of addresses, // but for this trie, since entry length is capped at 128 bits for IPv6 and 32 bits for IPv4, construction is O(n), // in linear proportion to the number of added elements. // // This trie also allows for constant time size queries (count of added elements, not node count), by storing sub-trie size in each node. // It works by updating the size of every node in the path to any added or removed node. // This does not change insertion or deletion operations from being constant time (because tree-depth is limited to address bit count). // At the same this makes size queries constant time, rather than being O(n) time. // // A single trie can use just a single address type or version, since it works with bits alone, // and cannot distinguish between different versions and types in the trie structure. // // Instead, you could aggregate multiple subtries to create a collection of multiple address types or versions. // You can use the method ToString for a String that represents multiple tries as a single tree. // // Tries are concurrency-safe when not being modified (elements added or removed), but are not concurrency-safe when any goroutine is modifying the trie. type Trie[T TrieKeyConstraint[T]] struct { trieBase[T, emptyValue] } func (trie *Trie[T]) tobase() *trieBase[T, emptyValue] { return (*trieBase[T, emptyValue])(unsafe.Pointer(trie)) } // GetRoot returns the root node of this trie, which can be nil for an implicitly zero-valued uninitialized trie, but not for any other trie. func (trie *Trie[T]) GetRoot() *TrieNode[T] { return toAddressTrieNode[T](trie.getRoot()) } // Size returns the number of elements in the trie. // It does not return the number of nodes, it returns the number of added nodes. // Only nodes for which IsAdded returns true are counted (those nodes corresponding to added addresses and prefix blocks). // When zero is returned, IsEmpty returns true. func (trie *Trie[T]) Size() int { return trie.toTrie().Size() } // NodeSize returns the number of nodes in the trie, which is always more than the number of elements. func (trie *Trie[T]) NodeSize() int { return trie.toTrie().NodeSize() } // Clear removes all added nodes from the trie, after which IsEmpty will return true. func (trie *Trie[T]) Clear() { trie.clear() } // IsEmpty returns true if there are not any added nodes within this trie. func (trie *Trie[T]) IsEmpty() bool { return trie.Size() == 0 } // TreeString returns a visual representation of the trie with one node per line, with or without the non-added keys. func (trie *Trie[T]) TreeString(withNonAddedKeys bool) string { return trie.toTrie().TreeString(withNonAddedKeys) } // String returns a visual representation of the trie with one node per line. func (trie *Trie[T]) String() string { return trie.toTrie().String() } // AddedNodesTreeString provides a flattened version of the trie showing only the contained added nodes and their containment structure, which is non-binary. // The root node is included, which may or may not be added. func (trie *Trie[T]) AddedNodesTreeString() string { return trie.toTrie().AddedNodesTreeString() } // Add adds the address to this trie. // The address must match the same type and version of any existing addresses already in the trie. // Returns true if the address did not already exist in the trie. func (trie *Trie[T]) Add(addr T) bool { return trie.add(addr) } // AddNode adds the address to this trie. // The address must match the same type and version of any existing addresses already in the trie. // The new or existing node for the address is returned. func (trie *Trie[T]) AddNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](trie.addNode(addr)) } // AddTrie adds nodes for the keys in the trie with the root node as the passed in node. // AddTrie returns the sub-node in the trie where the added trie begins, where the first node of the added trie is located. func (trie *Trie[T]) AddTrie(added *TrieNode[T]) *TrieNode[T] { return toAddressTrieNode[T](trie.addTrie(added.tobase())) } // ConstructAddedNodesTree constructs an associative trie in which the root and each added node have been mapped to a slice of their respective direct added sub-nodes. // This trie provides an alternative non-binary tree structure of the added nodes. // It is used by ToAddedNodesTreeString to produce a string showing the alternative structure. // The returned AddedTree instance wraps the associative trie, presenting it as a non-binary tree with the alternative tree structure, // the structure in which each node's child nodes are the list of direct and indirect added child nodes in the original trie. // If there are no non-added nodes in this trie, then the alternative tree structure provided by this method is the same as the original trie. func (trie *Trie[T]) ConstructAddedNodesTree() AddedTree[T] { var t trieBase[T, tree.AddedSubnodeMapping] = trie.constructAddedNodesTree() return AddedTree[T]{AssociativeTrie[T, tree.AddedSubnodeMapping]{t}} } // Contains returns whether the given address or prefix block subnet is in the trie as an added element. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns true if the prefix block or address exists already in the trie, false otherwise. // // Use GetAddedNode to get the node for the address rather than just checking for its existence. func (trie *Trie[T]) Contains(addr T) bool { return trie.contains(addr) } // Remove removes the given single address or prefix block subnet from the trie. // // Removing an element will not remove contained elements (nodes for contained blocks and addresses). // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns true if the prefix block or address was removed, false if not already in the trie. // // You can also remove by calling GetAddedNode to get the node and then calling Remove on the node. // // When an address is removed, the corresponding node may remain in the trie if it remains a subnet block for two sub-nodes. // If the corresponding node can be removed from the trie, it will be removed. func (trie *Trie[T]) Remove(addr T) bool { return trie.remove(addr) } // RemoveElementsContainedBy removes any single address or prefix block subnet from the trie that is contained in the given individual address or prefix block subnet. // // This goes further than Remove, not requiring a match to an inserted node, and also removing all the sub-nodes of any removed node or sub-node. // // For example, after inserting 1.2.3.0 and 1.2.3.1, passing 1.2.3.0/31 to RemoveElementsContainedBy will remove them both, // while the Remove method will remove nothing. // After inserting 1.2.3.0/31, then Remove will remove 1.2.3.0/31, but will leave 1.2.3.0 and 1.2.3.1 in the trie. // // It cannot partially delete a node, such as deleting a single address from a prefix block represented by a node. // It can only delete the whole node if the whole address or block represented by that node is contained in the given address or block. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // //Returns the root node of the sub-trie that was removed from the trie, or nil if nothing was removed. func (trie *Trie[T]) RemoveElementsContainedBy(addr T) *TrieNode[T] { return toAddressTrieNode[T](trie.removeElementsContainedBy(addr)) } // ElementsContainedBy checks if a part of this trie is contained by the given prefix block subnet or individual address. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns the root node of the contained sub-trie, or nil if no sub-trie is contained. // The node returned need not be an "added" node, see IsAdded for more details on added nodes. // The returned sub-trie is backed by this trie, so changes in this trie are reflected in those nodes and vice-versa. func (trie *Trie[T]) ElementsContainedBy(addr T) *TrieNode[T] { return toAddressTrieNode[T](trie.elementsContainedBy(addr)) } // ElementsContaining finds the trie nodes in the trie containing the given key and returns them as a linked list. // Only added nodes are added to the linked list. // // If the argument is not a single address nor prefix block, this method will panic. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. func (trie *Trie[T]) ElementsContaining(addr T) *ContainmentPath[T] { return &ContainmentPath[T]{*trie.elementsContaining(addr)} } // LongestPrefixMatch returns the address added to the trie with the longest matching prefix compared to the provided address, or nil if no matching address. func (trie *Trie[T]) LongestPrefixMatch(addr T) T { return trie.longestPrefixMatch(addr) } // only added nodes are added to the linked list // LongestPrefixMatchNode returns the node of address added to the trie with the longest matching prefix compared to the provided address, or nil if no matching address. func (trie *Trie[T]) LongestPrefixMatchNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](trie.longestPrefixMatchNode(addr)) } // ElementContains checks if a prefix block subnet or address in the trie contains the given subnet or address. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns true if the subnet or address is contained by a trie element, false otherwise. // // To get all the containing addresses, use ElementsContaining. func (trie *Trie[T]) ElementContains(addr T) bool { return trie.elementContains(addr) } // GetNode gets the node in the trie corresponding to the given address, // or returns nil if not such element exists. // // It returns any node, whether added or not, // including any prefix block node that was not added. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. func (trie *Trie[T]) GetNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](trie.getNode(addr)) } // GetAddedNode gets trie nodes representing added elements. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Use Contains to check for the existence of a given address in the trie, // as well as GetNode to search for all nodes including those not-added but also auto-generated nodes for subnet blocks. func (trie *Trie[T]) GetAddedNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](trie.getAddedNode(addr)) } // Iterator returns an iterator that iterates through the added addresses and prefix blocks in the trie. // The iteration is in sorted element order. func (trie *Trie[T]) Iterator() Iterator[T] { return trie.tobase().iterator() } // DescendingIterator returns an iterator that iterates through the added addresses and prefix blocks in the trie. // The iteration is in reverse sorted element order. func (trie *Trie[T]) DescendingIterator() Iterator[T] { return trie.tobase().descendingIterator() } // NodeIterator returns an iterator that iterates through all the added nodes in the trie in forward or reverse trie order. func (trie *Trie[T]) NodeIterator(forward bool) IteratorWithRemove[*TrieNode[T]] { return addrTrieNodeIteratorRem[T, emptyValue]{trie.tobase().nodeIterator(forward)} } // AllNodeIterator returns an iterator that iterates through all the nodes in the trie in forward or reverse trie order. func (trie *Trie[T]) AllNodeIterator(forward bool) IteratorWithRemove[*TrieNode[T]] { return addrTrieNodeIteratorRem[T, emptyValue]{trie.tobase().allNodeIterator(forward)} } // BlockSizeNodeIterator returns an iterator that iterates the added nodes in the trie, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order func (trie *Trie[T]) BlockSizeNodeIterator(lowerSubNodeFirst bool) IteratorWithRemove[*TrieNode[T]] { return addrTrieNodeIteratorRem[T, emptyValue]{trie.tobase().blockSizeNodeIterator(lowerSubNodeFirst)} } // BlockSizeAllNodeIterator returns an iterator that iterates all nodes in the trie, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order func (trie *Trie[T]) BlockSizeAllNodeIterator(lowerSubNodeFirst bool) IteratorWithRemove[*TrieNode[T]] { return addrTrieNodeIteratorRem[T, emptyValue]{trie.tobase().blockSizeAllNodeIterator(lowerSubNodeFirst)} } // BlockSizeCachingAllNodeIterator returns an iterator that iterates all nodes, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. func (trie *Trie[T]) BlockSizeCachingAllNodeIterator() CachingTrieIterator[*TrieNode[T]] { return cachingAddressTrieNodeIterator[T, emptyValue]{trie.tobase().blockSizeCachingAllNodeIterator()} } // ContainingFirstIterator returns an iterator that does a pre-order binary tree traversal of the added nodes. // All added nodes will be visited before their added sub-nodes. // For an address trie this means added containing subnet blocks will be visited before their added contained addresses and subnet blocks. // // Once a given node is visited, the iterator allows you to cache an object corresponding to the // lower or upper sub-node that can be retrieved when you later visit that sub-node. // // Objects are cached only with nodes to be visited. // So for this iterator that means an object will be cached with the first added lower or upper sub-node, // the next lower or upper sub-node to be visited, // which is not necessarily the direct lower or upper sub-node of a given node. // // The caching allows you to provide iteration context from a parent to its sub-nodes when iterating. // The caching and retrieval is done in constant-time. func (trie *Trie[T]) ContainingFirstIterator(forwardSubNodeOrder bool) CachingTrieIterator[*TrieNode[T]] { return cachingAddressTrieNodeIterator[T, emptyValue]{trie.tobase().containingFirstIterator(forwardSubNodeOrder)} } // ContainingFirstAllNodeIterator returns an iterator that does a pre-order binary tree traversal. // All nodes will be visited before their sub-nodes. // For an address trie this means containing subnet blocks will be visited before their contained addresses and subnet blocks. // // Once a given node is visited, the iterator allows you to cache an object corresponding to the // lower or upper sub-node that can be retrieved when you later visit that sub-node. // That allows you to provide iteration context from a parent to its sub-nodes when iterating. // The caching and retrieval is done in constant time. func (trie *Trie[T]) ContainingFirstAllNodeIterator(forwardSubNodeOrder bool) CachingTrieIterator[*TrieNode[T]] { return cachingAddressTrieNodeIterator[T, emptyValue]{trie.tobase().containingFirstAllNodeIterator(forwardSubNodeOrder)} } // ContainedFirstIterator returns an iterator that does a post-order binary tree traversal of the added nodes. // All added sub-nodes will be visited before their parent nodes. // For an address trie this means contained addresses and subnets will be visited before their containing subnet blocks. func (trie *Trie[T]) ContainedFirstIterator(forwardSubNodeOrder bool) IteratorWithRemove[*TrieNode[T]] { return addrTrieNodeIteratorRem[T, emptyValue]{trie.tobase().containedFirstIterator(forwardSubNodeOrder)} } // ContainedFirstAllNodeIterator returns an iterator that does a post-order binary tree traversal. // All sub-nodes will be visited before their parent nodes. // For an address trie this means contained addresses and subnets will be visited before their containing subnet blocks. func (trie *Trie[T]) ContainedFirstAllNodeIterator(forwardSubNodeOrder bool) Iterator[*TrieNode[T]] { return addrTrieNodeIterator[T, emptyValue]{trie.tobase().containedFirstAllNodeIterator(forwardSubNodeOrder)} } // FirstNode returns the first (lowest valued) node in the trie. func (trie *Trie[T]) FirstNode() *TrieNode[T] { return toAddressTrieNode[T](trie.trieBase.trie.FirstNode()) } // FirstAddedNode returns the first (lowest valued) added node in this trie, // or nil if there are no added entries in this trie or sub-trie. func (trie *Trie[T]) FirstAddedNode() *TrieNode[T] { return toAddressTrieNode[T](trie.trieBase.trie.FirstAddedNode()) } // LastNode returns the last (highest valued) node in this trie. func (trie *Trie[T]) LastNode() *TrieNode[T] { return toAddressTrieNode[T](trie.trieBase.trie.LastNode()) } // LastAddedNode returns the last (highest valued) added node in the trie, // or nil if there are no added entries in this tree or sub-tree. func (trie *Trie[T]) LastAddedNode() *TrieNode[T] { return toAddressTrieNode[T](trie.trieBase.trie.LastAddedNode()) } // LowerAddedNode returns the added node whose address is the highest address strictly less than the given address. func (trie *Trie[T]) LowerAddedNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](trie.lowerAddedNode(addr)) } // FloorAddedNode returns the added node whose address is the highest address less than or equal to the given address. func (trie *Trie[T]) FloorAddedNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](trie.floorAddedNode(addr)) } // HigherAddedNode returns the added node whose address is the lowest address strictly greater than the given address. func (trie *Trie[T]) HigherAddedNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](trie.higherAddedNode(addr)) } // CeilingAddedNode returns the added node whose address is the lowest address greater than or equal to the given address. func (trie *Trie[T]) CeilingAddedNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](trie.ceilingAddedNode(addr)) } // Clone clones this trie. func (trie *Trie[T]) Clone() *Trie[T] { return toAddressTrie[T](trie.tobase().clone()) } // Equal returns whether the given argument is a trie with a set of nodes with the same keys as in this trie. func (trie *Trie[T]) Equal(other *Trie[T]) bool { return trie.toTrie().Equal(other.toTrie()) } // For some reason Format must be here and not in addressTrieNode for nil node. // It panics in fmt code either way, but if in here then it is handled by a recover() call in fmt properly. // Seems to be a problem only in the debugger. // Format implements the [fmt.Formatter] interface. func (trie Trie[T]) Format(state fmt.State, verb rune) { // without this, prints like {{{{}}}} or {{{{0xc00014ca50}}}} // which is done by printValue in print.go of fmt trie.trieBase.trie.Format(state, verb) } // TreesString merges the tree strings (as shown by the TreeString method) of multiple tries into a single merged tree string. func TreesString[T TrieKeyConstraint[T]](withNonAddedKeys bool, tries ...*Trie[T]) string { binTries := make([]*tree.BinTrie[trieKey[T], emptyValue], 0, len(tries)) for _, trie := range tries { binTries = append(binTries, toBinTrie[T](trie)) } return tree.TreesString(withNonAddedKeys, binTries...) } func toBinTrie[T TrieKeyConstraint[T]](trie *Trie[T]) *tree.BinTrie[trieKey[T], emptyValue] { return (*tree.BinTrie[trieKey[T], emptyValue])(unsafe.Pointer(trie)) } func toAddressTrie[T TrieKeyConstraint[T]](trie *tree.BinTrie[trieKey[T], emptyValue]) *Trie[T] { return (*Trie[T])(unsafe.Pointer(trie)) } //////// //////// //////// //////// //////// //////// //////// //////// // AssociativeTrie represents a binary address trie in which each added node can be associated with a value. // It is an instance of [Trie] that can also function as a key-value map. The keys are addresses or prefix blocks. // Each can be mapped to a value with type specified by the generic type V. // // For the generic type T, you can choose *Address, *IPAddress, *IPv4Address, *IPv6Address, or *MACAddress. // The generic value type V can be any type of your choosing. // // All the characteristics of Trie are common to AssociativeTrie. // // The zero value is a binary trie ready for use. type AssociativeTrie[T TrieKeyConstraint[T], V any] struct { trieBase[T, V] } func (trie *AssociativeTrie[T, V]) tobase() *trieBase[T, V] { return (*trieBase[T, V])(unsafe.Pointer(trie)) } // GetRoot returns the root node of this trie, which can be nil for an implicitly zero-valued uninitialized trie, but not for any other trie. func (trie *AssociativeTrie[T, V]) GetRoot() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.getRoot()) } // Size returns the number of elements in the tree. // It does not return the number of nodes. // Only nodes for which IsAdded returns true are counted (those nodes corresponding to added addresses and prefix blocks). // When zero is returned, IsEmpty returns true. func (trie *AssociativeTrie[T, V]) Size() int { return trie.toTrie().Size() } // NodeSize returns the number of nodes in the tree, which is always more than the number of elements. func (trie *AssociativeTrie[T, V]) NodeSize() int { return trie.toTrie().NodeSize() } // Clear removes all added nodes from the trie, after which IsEmpty will return true. func (trie *AssociativeTrie[T, V]) Clear() { trie.clear() } // IsEmpty returns true if there are not any added nodes within this tree. func (trie *AssociativeTrie[T, V]) IsEmpty() bool { return trie.Size() == 0 } // TreeString returns a visual representation of the tree with one node per line, with or without the non-added keys. func (trie *AssociativeTrie[T, V]) TreeString(withNonAddedKeys bool) string { return trie.toTrie().TreeString(withNonAddedKeys) } // String returns a visual representation of the tree with one node per line. func (trie *AssociativeTrie[T, V]) String() string { return trie.toTrie().String() } // AddedNodesTreeString provides a flattened version of the trie showing only the contained added nodes and their containment structure, which is non-binary. // The root node is included, which may or may not be added. func (trie *AssociativeTrie[T, V]) AddedNodesTreeString() string { return trie.toTrie().AddedNodesTreeString() } // Add adds the address to this trie. // Returns true if the address did not already exist in the trie. func (trie *AssociativeTrie[T, V]) Add(addr T) bool { return trie.add(addr) } // AddNode adds the address key to this trie. // The new or existing node for the address is returned. func (trie *AssociativeTrie[T, V]) AddNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.addNode(addr)) } // AddTrie adds nodes for the keys in the trie with the root node as the passed in node. To add both keys and values, use PutTrie. // AddTrie returns the sub-node in the trie where the added trie begins, where the first node of the added trie is located. func (trie *AssociativeTrie[T, V]) AddTrie(added *AssociativeTrieNode[T, V]) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.addTrie(added.toBase())) } // ConstructAddedNodesTree provides an associative trie in which the root and each added node are mapped to a list of their respective direct added sub-nodes. // This trie provides an alternative non-binary tree structure of the added nodes. // It is used by ToAddedNodesTreeString to produce a string showing the alternative structure. // The returned AddedTree instance wraps the associative trie, presenting it as a non-binary tree with the alternative tree structure, // the structure in which each node's child nodes are the list of direct and indirect added child nodes in the original trie. // If there are no non-added nodes in this trie, then the alternative tree structure provided by this method is the same as the original trie. func (trie *AssociativeTrie[T, V]) ConstructAddedNodesTree() AssociativeAddedTree[T, V] { var t trieBase[T, tree.AddedSubnodeMapping] = trie.constructAddedNodesTree() return AssociativeAddedTree[T, V]{AssociativeTrie[T, tree.AddedSubnodeMapping]{t}} } // Contains returns whether the given address or prefix block subnet is in the trie as an added element. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns true if the prefix block or address exists already in the trie, false otherwise. // // Use GetAddedNode to get the node for the address rather than just checking for its existence. func (trie *AssociativeTrie[T, V]) Contains(addr T) bool { return trie.contains(addr) } // Remove removes the given single address or prefix block subnet from the trie. // // Removing an element will not remove contained elements (nodes for contained blocks and addresses). // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns true if the prefix block or address was removed, false if not already in the trie. // // You can also remove by calling GetAddedNode to get the node and then calling Remove on the node. // // When an address is removed, the corresponding node may remain in the trie if it remains a subnet block for two sub-nodes. // If the corresponding node can be removed from the trie, it will be removed. func (trie *AssociativeTrie[T, V]) Remove(addr T) bool { return trie.remove(addr) } // RemoveElementsContainedBy removes any single address or prefix block subnet from the trie that is contained in the given individual address or prefix block subnet. // // This goes further than Remove, not requiring a match to an inserted node, and also removing all the sub-nodes of any removed node or sub-node. // // For example, after inserting 1.2.3.0 and 1.2.3.1, passing 1.2.3.0/31 to RemoveElementsContainedBy will remove them both, // while the Remove method will remove nothing. // After inserting 1.2.3.0/31, then Remove will remove 1.2.3.0/31, but will leave 1.2.3.0 and 1.2.3.1 in the trie. // // It cannot partially delete a node, such as deleting a single address from a prefix block represented by a node. // It can only delete the whole node if the whole address or block represented by that node is contained in the given address or block. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // //Returns the root node of the sub-trie that was removed from the trie, or nil if nothing was removed. func (trie *AssociativeTrie[T, V]) RemoveElementsContainedBy(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.removeElementsContainedBy(addr)) } // ElementsContainedBy checks if a part of this trie is contained by the given prefix block subnet or individual address. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns the root node of the contained sub-trie, or nil if no sub-trie is contained. // The node returned need not be an "added" node, see IsAdded for more details on added nodes. // The returned sub-trie is backed by this trie, so changes in this trie are reflected in those nodes and vice-versa. func (trie *AssociativeTrie[T, V]) ElementsContainedBy(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.elementsContainedBy(addr)) } // ElementsContaining finds the trie nodes in the trie containing the given key and returns them as a linked list. // Only added nodes are added to the linked list. // // If the argument is not a single address nor prefix block, this method will panic. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. func (trie *AssociativeTrie[T, V]) ElementsContaining(addr T) *ContainmentValuesPath[T, V] { return &ContainmentValuesPath[T, V]{*trie.elementsContaining(addr)} } // LongestPrefixMatch returns the address with the longest matching prefix compared to the provided address. func (trie *AssociativeTrie[T, V]) LongestPrefixMatch(addr T) T { return trie.longestPrefixMatch(addr) } // only added nodes are added to the linked list // LongestPrefixMatchNode returns the node of the address with the longest matching prefix compared to the provided address. func (trie *AssociativeTrie[T, V]) LongestPrefixMatchNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.longestPrefixMatchNode(addr)) } // ElementContains checks if a prefix block subnet or address in the trie contains the given subnet or address. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns true if the subnet or address is contained by a trie element, false otherwise. // // To get all the containing addresses, use ElementsContaining. func (trie *AssociativeTrie[T, V]) ElementContains(addr T) bool { return trie.elementContains(addr) } // GetNode gets the node in the trie corresponding to the given address, // or returns nil if not such element exists. // // It returns any node, whether added or not, // including any prefix block node that was not added. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. func (trie *AssociativeTrie[T, V]) GetNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.getNode(addr)) } // GetAddedNode gets trie nodes representing added elements. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Use Contains to check for the existence of a given address in the trie, // as well as GetNode to search for all nodes including those not added but also auto-generated nodes for subnet blocks. func (trie *AssociativeTrie[T, V]) GetAddedNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.getAddedNode(addr)) } // Iterator returns an iterator that iterates through the added addresses and prefix blocks in the trie. // The iteration is in sorted element order. func (trie *AssociativeTrie[T, V]) Iterator() Iterator[T] { return trie.tobase().iterator() } // DescendingIterator returns an iterator that iterates through the added addresses and prefix blocks in the trie. // The iteration is in reverse sorted element order. func (trie *AssociativeTrie[T, V]) DescendingIterator() Iterator[T] { return trie.tobase().descendingIterator() } // NodeIterator returns an iterator that iterates through all the added nodes in the trie in forward or reverse tree order. func (trie *AssociativeTrie[T, V]) NodeIterator(forward bool) IteratorWithRemove[*AssociativeTrieNode[T, V]] { return associativeAddressTrieNodeIteratorRem[T, V]{trie.tobase().nodeIterator(forward)} } // AllNodeIterator returns an iterator that iterates through all the nodes in the trie in forward or reverse tree order. func (trie *AssociativeTrie[T, V]) AllNodeIterator(forward bool) IteratorWithRemove[*AssociativeTrieNode[T, V]] { return associativeAddressTrieNodeIteratorRem[T, V]{trie.tobase().allNodeIterator(forward)} } // BlockSizeNodeIterator returns an iterator that iterates the added nodes in the trie, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order func (trie *AssociativeTrie[T, V]) BlockSizeNodeIterator(lowerSubNodeFirst bool) IteratorWithRemove[*AssociativeTrieNode[T, V]] { return associativeAddressTrieNodeIteratorRem[T, V]{trie.tobase().blockSizeNodeIterator(lowerSubNodeFirst)} } // BlockSizeAllNodeIterator returns an iterator that iterates all nodes in the trie, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order func (trie *AssociativeTrie[T, V]) BlockSizeAllNodeIterator(lowerSubNodeFirst bool) IteratorWithRemove[*AssociativeTrieNode[T, V]] { return associativeAddressTrieNodeIteratorRem[T, V]{trie.tobase().blockSizeAllNodeIterator(lowerSubNodeFirst)} } // BlockSizeCachingAllNodeIterator returns an iterator that iterates all nodes, ordered by keys from largest prefix blocks to smallest, and then to individual addresses. func (trie *AssociativeTrie[T, V]) BlockSizeCachingAllNodeIterator() CachingTrieIterator[*AssociativeTrieNode[T, V]] { return cachingAssociativeAddressTrieNodeIteratorX[T, V]{trie.tobase().blockSizeCachingAllNodeIterator()} } // ContainingFirstIterator returns an iterator that does a pre-order binary trie traversal of the added nodes. // All added nodes will be visited before their added sub-nodes. // For an address trie this means added containing subnet blocks will be visited before their added contained addresses and subnet blocks. // // Once a given node is visited, the iterator allows you to cache an object corresponding to the // lower or upper sub-node that can be retrieved when you later visit that sub-node. // // Objects are cached only with nodes to be visited. // So for this iterator that means an object will be cached with the first added lower or upper sub-node, // the next lower or upper sub-node to be visited, // which is not necessarily the direct lower or upper sub-node of a given node. // // The caching allows you to provide iteration context from a parent to its sub-nodes when iterating. // The caching and retrieval is done in constant-time. func (trie *AssociativeTrie[T, V]) ContainingFirstIterator(forwardSubNodeOrder bool) CachingTrieIterator[*AssociativeTrieNode[T, V]] { return cachingAssociativeAddressTrieNodeIteratorX[T, V]{trie.tobase().containingFirstIterator(forwardSubNodeOrder)} } // ContainingFirstAllNodeIterator returns an iterator that does a pre-order binary trie traversal. // All nodes will be visited before their sub-nodes. // For an address trie this means containing subnet blocks will be visited before their contained addresses and subnet blocks. // // Once a given node is visited, the iterator allows you to cache an object corresponding to the // lower or upper sub-node that can be retrieved when you later visit that sub-node. // That allows you to provide iteration context from a parent to its sub-nodes when iterating. // The caching and retrieval is done in constant-time. func (trie *AssociativeTrie[T, V]) ContainingFirstAllNodeIterator(forwardSubNodeOrder bool) CachingTrieIterator[*AssociativeTrieNode[T, V]] { return cachingAssociativeAddressTrieNodeIteratorX[T, V]{trie.tobase().containingFirstAllNodeIterator(forwardSubNodeOrder)} } // ContainedFirstIterator returns an iterator that does a post-order binary trie traversal of the added nodes. // All added sub-nodes will be visited before their parent nodes. // For an address trie this means contained addresses and subnets will be visited before their containing subnet blocks. func (trie *AssociativeTrie[T, V]) ContainedFirstIterator(forwardSubNodeOrder bool) IteratorWithRemove[*AssociativeTrieNode[T, V]] { return associativeAddressTrieNodeIteratorRem[T, V]{trie.tobase().containedFirstIterator(forwardSubNodeOrder)} } // ContainedFirstAllNodeIterator returns an iterator that does a post-order binary trie traversal. // All sub-nodes will be visited before their parent nodes. // For an address trie this means contained addresses and subnets will be visited before their containing subnet blocks. func (trie *AssociativeTrie[T, V]) ContainedFirstAllNodeIterator(forwardSubNodeOrder bool) Iterator[*AssociativeTrieNode[T, V]] { return associativeAddressTrieNodeIterator[T, V]{trie.tobase().containedFirstAllNodeIterator(forwardSubNodeOrder)} } // FirstNode returns the first (lowest-valued) node in the trie or nil if the trie is empty. func (trie *AssociativeTrie[T, V]) FirstNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.trieBase.trie.FirstNode()) } // FirstAddedNode returns the first (lowest-valued) added node in the trie // or nil if there are no added entries in this trie. func (trie *AssociativeTrie[T, V]) FirstAddedNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.trieBase.trie.FirstAddedNode()) } // LastNode returns the last (highest-valued) node in the trie or nil if the trie is empty. func (trie *AssociativeTrie[T, V]) LastNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.trieBase.trie.LastNode()) } // LastAddedNode returns the last (highest-valued) added node in the sub-trie originating from this node, // or nil if there are no added entries in this trie. func (trie *AssociativeTrie[T, V]) LastAddedNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.trieBase.trie.LastAddedNode()) } // LowerAddedNode returns the added node whose address is the highest address strictly less than the given address, // or nil if there are no added entries in this trie. func (trie *AssociativeTrie[T, V]) LowerAddedNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.lowerAddedNode(addr)) } // FloorAddedNode returns the added node whose address is the highest address less than or equal to the given address, // or nil if there are no added entries in this trie. func (trie *AssociativeTrie[T, V]) FloorAddedNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.floorAddedNode(addr)) } // HigherAddedNode returns the added node whose address is the lowest address strictly greater than the given address, // or nil if there are no added entries in this trie. func (trie *AssociativeTrie[T, V]) HigherAddedNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.higherAddedNode(addr)) } // CeilingAddedNode returns the added node whose address is the lowest address greater than or equal to the given address, // or nil if there are no added entries in this trie. func (trie *AssociativeTrie[T, V]) CeilingAddedNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.ceilingAddedNode(addr)) } // Clone clones this trie. func (trie *AssociativeTrie[T, V]) Clone() *AssociativeTrie[T, V] { return toAssociativeTrie[T, V](trie.tobase().clone()) } // Equal returns whether the given argument is a trie with a set of nodes with the same keys as in this trie. func (trie *AssociativeTrie[T, V]) Equal(other *AssociativeTrie[T, V]) bool { return trie.toTrie().Equal(other.toTrie()) } // DeepEqual returns whether the given argument is a trie with a set of nodes with the same keys and values as in this trie, // the values being compared with reflect.DeepEqual. func (trie *AssociativeTrie[T, V]) DeepEqual(other *AssociativeTrie[T, V]) bool { return trie.toTrie().DeepEqual(other.toTrie()) } // Put associates the specified value with the specified key in this map. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // If this map previously contained a mapping for a key, // the old value is replaced by the specified value, and false is returned along with the old value. // If this map did not previously contain a mapping for the key, true is returned along with a nil value. // The boolean return value allows you to distinguish whether the address was previously mapped to nil or not mapped at all. func (trie *AssociativeTrie[T, V]) Put(addr T, value V) (V, bool) { addr = mustBeBlockOrAddress(addr) return trie.trie.Put(trieKey[T]{addr}, value) } // PutTrie adds nodes for the address keys and values in the trie with the root node as the passed in node. To add only the keys, use AddTrie. // // For each added in the given node that does not exist in the trie, a copy of each node will be made, // the copy including the associated value, and the copy will be inserted into the trie. // // The address type/version of the keys must match. // // When adding one trie to another, this method is more efficient than adding each node of the first trie individually. // When using this method, searching for the location to add sub-nodes starts from the inserted parent node. // // Returns the node corresponding to the given sub-root node, whether it was already in the trie or not. func (trie *AssociativeTrie[T, V]) PutTrie(added *AssociativeTrieNode[T, V]) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](trie.trie.PutTrie(added.toBinTrieNode())) //return trie.putTrie(added.toBase()) } // PutNode associates the specified value with the specified key in this map. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns the node for the added address, whether it was already in the trie or not. // // If you wish to know whether the node was already there when adding, use PutNew, or before adding you can use GetAddedNode. func (trie *AssociativeTrie[T, V]) PutNode(addr T, value V) *AssociativeTrieNode[T, V] { addr = mustBeBlockOrAddress(addr) return toAssociativeTrieNode[T, V](trie.trie.PutNode(trieKey[T]{addr}, value)) } // Remap remaps node values in the trie. // // This will look up the node corresponding to the given key. // It will call the remapping function, regardless of whether the node is found or not. // // If the node is not found, or the node is not an "added" node, the existingValue argument will be the zero value. // If the node is found, the existingValue argument will be the node's value, which can also be the zero value. // The boolean "found" argument will be true if the node was found and it is an "added" node. // If the node was not found or was not an "added" node, then the boolean "found" argument will be false. // // If the remapping function returns false as the "mapIt" argument, then the matched node will be removed or converted to a "non-added" node, if any. // If it returns true, then either the existing node will be set to an "added" node with the "mapped" value given as the first argument, // or if there was no matched node, it will create a new added node with the "mapped" value. // // The method will return the node involved, which is either the matched node, or the newly created node, // or nil if there was no matched node nor newly created node. // // If the remapping function modifies the trie during its computation, // and the returned values from the remapper requires changes to be made to the trie, // then the trie will not be changed as required by the remapper, and Remap will panic. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. func (trie *AssociativeTrie[T, V]) Remap(addr T, remapper func(existingValue V, found bool) (mapped V, mapIt bool)) *AssociativeTrieNode[T, V] { addr = mustBeBlockOrAddress(addr) return toAssociativeTrieNode[T, V](trie.trieBase.trie.Remap(trieKey[T]{addr}, remapper)) } // RemapIfAbsent remaps node values in the trie, but only for nodes that do not exist or are not "added". // // This will look up the node corresponding to the given key. // If the node is not found or not "added", then RemapIfAbsent will call the supplier function. // It will create a new node with the value returned from the supplier function. // If the node is found and "added", then RemapIfAbsent will not call the supplier function. // // The method will return the node involved, which is either the matched node, the newly created node, or nil if there was no matched node nor newly created node. // // If the supplier function modifies the trie during its computation, // then the trie will not be changed and RemapIfAbsent will panic. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. func (trie *AssociativeTrie[T, V]) RemapIfAbsent(addr T, supplier func() V) *AssociativeTrieNode[T, V] { addr = mustBeBlockOrAddress(addr) return toAssociativeTrieNode[T, V](trie.trieBase.trie.RemapIfAbsent(trieKey[T]{addr}, supplier)) } // Get gets the value for the specified key in this mapped trie or sub-trie. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns the value for the given key. // Returns nil if the contains no mapping for that key or if the mapped value is nil. func (trie *AssociativeTrie[T, V]) Get(addr T) (V, bool) { addr = mustBeBlockOrAddress(addr) return trie.trie.Get(trieKey[T]{addr}) } // For some reason Format must be here and not in addressTrieNode for nil node. // It panics in fmt code either way, but if in here then it is handled by a recover() call in fmt properly in the debugger. // Format implements the [fmt.Formatter] interface. func (trie AssociativeTrie[T, V]) Format(state fmt.State, verb rune) { trie.trieBase.trie.Format(state, verb) } // AssociativeTreesString merges the trie strings (as shown by the TreeString method) of multiple associative tries into a single merged trie string. func AssociativeTreesString[T TrieKeyConstraint[T], V any](withNonAddedKeys bool, tries ...*AssociativeTrie[T, V]) string { binTries := make([]*tree.BinTrie[trieKey[T], V], 0, len(tries)) for _, trie := range tries { binTries = append(binTries, toAssocBinTrie[T, V](trie)) } return tree.TreesString(withNonAddedKeys, binTries...) } func toAssocBinTrie[T TrieKeyConstraint[T], V any](trie *AssociativeTrie[T, V]) *tree.BinTrie[trieKey[T], V] { return (*tree.BinTrie[trieKey[T], V])(unsafe.Pointer(trie)) } func toAssociativeTrie[T TrieKeyConstraint[T], V any](trie *tree.BinTrie[trieKey[T], V]) *AssociativeTrie[T, V] { return (*AssociativeTrie[T, V])(unsafe.Pointer(trie)) } // Ensures the address is either an individual address or a prefix block subnet. func mustBeBlockOrAddress[T TrieKeyConstraint[T]](addr T) T { res, err := checkBlockOrAddress(addr) if err != nil { panic(err) } return res } // Ensures the address is either an individual address or a prefix block subnet. // Returns a normalized address which has no prefix length if it is a single address, // or has a prefix length matching the prefix block size if it is a prefix block. func checkBlockOrAddress[T TrieKeyConstraint[T]](addr T) (res T, err addrerr.IncompatibleAddressError) { return addr.toSinglePrefixBlockOrAddress() } // NewTrie constructs an address trie for the given type, without a root. For the generic type T, // you can choose *Address, *IPAddress, *IPv4Address, *IPv6Address, or *MACAddress. func NewTrie[T TrieKeyConstraint[T]]() *Trie[T] { return &Trie[T]{} } // NewAssociativeTrie constructs an associative address trie for the given types, without a root. func NewAssociativeTrie[T TrieKeyConstraint[T], V any]() *AssociativeTrie[T, V] { return &AssociativeTrie[T, V]{} } type ( AssociativeAddressTrie = AssociativeTrie[*Address, any] AddressTrie = Trie[*Address] IPv4AddressAssociativeTrie = AssociativeTrie[*IPv4Address, any] IPv4AddressTrie = Trie[*IPv4Address] IPv6AddressAssociativeTrie = AssociativeTrie[*IPv6Address, any] IPv6AddressTrie = Trie[*IPv6Address] // NodeValue is an alias for the generic node value type for AssociativeAddressTrie, IPv4AddressAssociativeTrie, and IPv6AddressAssociativeTrie NodeValue = any ) // NewIPv4AddressTrie constructs an IPv4 address trie with the root as the 0.0.0.0/0 prefix block // This is here for backwards compatibility. Using NewTrie is recommended instead. func NewIPv4AddressTrie() *Trie[*IPv4Address] { // for backwards compatibility return &Trie[*IPv4Address]{trieBase[*IPv4Address, emptyValue]{tree.NewBinTrie[trieKey[*IPv4Address], emptyValue](trieKey[*IPv4Address]{ipv4All})}} } // NewIPv4AddressAssociativeTrie constructs an IPv4 associative address trie with the root as the 0.0.0.0/0 prefix block // This is here for backwards compatibility. Using NewAssociativeTrie is recommended instead. func NewIPv4AddressAssociativeTrie() *AssociativeTrie[*IPv4Address, any] { // for backwards compatibility return &AssociativeTrie[*IPv4Address, any]{trieBase[*IPv4Address, any]{tree.NewBinTrie[trieKey[*IPv4Address], any](trieKey[*IPv4Address]{ipv4All})}} } // NewIPv6AddressTrie constructs an IPv6 address trie with the root as the ::/0 prefix block // This is here for backwards compatibility. Using NewTrie is recommended instead. func NewIPv6AddressTrie() *Trie[*IPv6Address] { // for backwards compatibility return &Trie[*IPv6Address]{trieBase[*IPv6Address, emptyValue]{tree.NewBinTrie[trieKey[*IPv6Address], emptyValue](trieKey[*IPv6Address]{ipv6All})}} } // NewIPv6AddressAssociativeTrie constructs an IPv6 associative address trie with the root as the ::/0 prefix block // This is here for backwards compatibility. Using NewAssociativeTrie is recommended instead. func NewIPv6AddressAssociativeTrie() *AssociativeTrie[*IPv6Address, any] { // for backwards compatibility return &AssociativeTrie[*IPv6Address, any]{trieBase[*IPv6Address, any]{tree.NewBinTrie[trieKey[*IPv6Address], any](trieKey[*IPv6Address]{ipv6All})}} } // NewMACAddressTrie constructs a MAC address trie with the root as the zero-prefix block // If extended is true, the trie will consist of 64-bit EUI addresses, otherwise the addresses will be 48-bit. // If you wish to construct a trie in which the address size is determined by the first added address, // use the zero-value MACAddressTrie{} // This is here for backwards compatibility. Using NewTrie is recommended instead. func NewMACAddressTrie(extended bool) *Trie[*MACAddress] { var rootAddr *MACAddress if extended { rootAddr = macAllExtended } else { rootAddr = macAll } return &Trie[*MACAddress]{trieBase[*MACAddress, emptyValue]{tree.NewBinTrie[trieKey[*MACAddress], emptyValue](trieKey[*MACAddress]{rootAddr})}} } // NewMACAddressAssociativeTrie constructs a MAC associative address trie with the root as the zero-prefix prefix block // This is here for backwards compatibility. Using NewAssociativeTrie is recommended instead. func NewMACAddressAssociativeTrie(extended bool) *AssociativeTrie[*MACAddress, any] { var rootAddr *MACAddress if extended { rootAddr = macAllExtended } else { rootAddr = macAll } return &AssociativeTrie[*MACAddress, any]{trieBase[*MACAddress, any]{tree.NewBinTrie[trieKey[*MACAddress], any](trieKey[*MACAddress]{rootAddr})}} } // AddedTree is an alternative non-binary tree data structure originating from a binary trie // in which the nodes of this tree are the "added" nodes of the original trie, // with the possible exception of the root, which matches the root node of the original. // The root may or may not be an added node from the original trie. // This tree is also read-only and is generated from the originating trie, // but does not change in concert with changes to the original trie. type AddedTree[T TrieKeyConstraint[T]] struct { wrapped AssociativeTrie[T, tree.AddedSubnodeMapping] } // GetRoot returns the root of this tree, which corresponds to the root of the originating trie. func (atree AddedTree[T]) GetRoot() AddedTreeNode[T] { return AddedTreeNode[T]{atree.wrapped.getRoot()} } // String returns a string representation of the tree, which is the same as the string obtained from // the AddedNodesTreeString method of the originating trie. func (atree AddedTree[T]) String() string { return tree.AddedNodesTreeString[trieKey[T], emptyValue](atree.wrapped.trie.GetRoot()) } // AddedTreeNode represents a node in an AddedTree type AddedTreeNode[T TrieKeyConstraint[T]] struct { wrapped *tree.BinTrieNode[trieKey[T], tree.AddedSubnodeMapping] } // GetSubNodes returns the sub-nodes of this node, which are not the same as the 0, 1 or 2 direct sub-nodes of the originating binary trie. // Instead, these are all the direct or indirect added sub-nodes of the node in the originating trie. // If you can traverse from this node to another node in the originating trie, using a sequence of sub-nodes, without any intervening sub-node being an added node, then that other node will appear as a sub-node here. func (node AddedTreeNode[T]) GetSubNodes() []AddedTreeNode[T] { val := node.wrapped.GetValue() if val == nil { return nil } subnodeMapping := val.(tree.SubNodesMapping[trieKey[T], emptyValue]) var subNodes []*tree.BinTrieNode[trieKey[T], tree.AddedSubnodeMapping] subNodes = subnodeMapping.SubNodes if len(subNodes) == 0 { return nil } res := make([]AddedTreeNode[T], len(subNodes)) for i, subNode := range subNodes { res[i] = AddedTreeNode[T]{subNode} } return res } // IsAdded returns if the node was an added node in the original trie. // This returns true for all nodes except possibly the root, since only added nodes are added to this tree, apart from the root. func (node AddedTreeNode[T]) IsAdded() bool { return node.wrapped.IsAdded() } // GetKey returns the key of this node, which is the same as the key of the corresponding node in the originating trie. func (node AddedTreeNode[T]) GetKey() T { return node.wrapped.GetKey().address } // String returns a visual representation of this node including the key. // If this is the root, it will have an open circle if the root is not an added node. // Otherwise, the node will have a closed circle. func (node AddedTreeNode[T]) String() string { return tree.NodeString[trieKey[T], emptyValue](printWrapper[T, emptyValue]{node.wrapped}) } // TreeString returns a visual representation of the sub-tree originating from this node, with one node per line. func (node AddedTreeNode[T]) TreeString() string { return tree.AddedNodesTreeString[trieKey[T], emptyValue](node.wrapped) } type printWrapper[T TrieKeyConstraint[T], V any] struct { *tree.BinTrieNode[trieKey[T], tree.AddedSubnodeMapping] } func (p printWrapper[T, V]) GetValue() V { var nodeValue tree.AddedSubnodeMapping = p.BinTrieNode.GetValue() if nodeValue == nil { var v V return v } return nodeValue.(tree.SubNodesMapping[trieKey[T], V]).Value } // AssociativeAddedTree is similar to AddedTree but originates from an AssociativeTrie. // The nodes of this tree have the same values as the corresponding nodes in the original trie. type AssociativeAddedTree[T TrieKeyConstraint[T], V any] struct { wrapped AssociativeTrie[T, tree.AddedSubnodeMapping] } // GetRoot returns the root of this tree, which corresponds to the root of the originating trie. func (atree AssociativeAddedTree[T, V]) GetRoot() AssociativeAddedTreeNode[T, V] { return AssociativeAddedTreeNode[T, V]{atree.wrapped.getRoot()} } // String returns a string representation of the tree, which is the same as the string obtained from // the AddedNodesTreeString method of the originating trie. func (atree AssociativeAddedTree[T, V]) String() string { return tree.AddedNodesTreeString[trieKey[T], V](atree.wrapped.trie.GetRoot()) } // AssociativeAddedTreeNode represents a node in an AssociativeAddedTree. type AssociativeAddedTreeNode[T TrieKeyConstraint[T], V any] struct { // T wrapped *tree.BinTrieNode[trieKey[T], tree.AddedSubnodeMapping] } // GetSubNodes returns the sub-nodes of this node, which are not the same as the 0, 1 or 2 direct sub-nodes of the originating binary trie. // Instead, these are all direct or indirect added sub-nodes of the node. // If you can traverse from this node to another node in the originating trie, using a sequence of sub-nodes, without any intervening sub-node being an added node, then that other node will appear as a sub-node here. func (node AssociativeAddedTreeNode[T, V]) GetSubNodes() []AssociativeAddedTreeNode[T, V] { val := node.wrapped.GetValue() if val == nil { return nil } subnodeMapping := val.(tree.SubNodesMapping[trieKey[T], V]) var subNodes []*tree.BinTrieNode[trieKey[T], tree.AddedSubnodeMapping] subNodes = subnodeMapping.SubNodes if len(subNodes) == 0 { return nil } res := make([]AssociativeAddedTreeNode[T, V], len(subNodes)) for i, subNode := range subNodes { res[i] = AssociativeAddedTreeNode[T, V]{subNode} } return res } // IsAdded returns if the node was an added node in the original trie. // This returns true for all nodes except possibly the root, since only added nodes are added to this tree, apart from the root. func (node AssociativeAddedTreeNode[T, V]) IsAdded() bool { return node.wrapped.IsAdded() } // GetKey returns the key of this node, which is the same as the key of the corresponding node in the originating trie. func (node AssociativeAddedTreeNode[T, V]) GetKey() T { return node.wrapped.GetKey().address } // GetValue returns the value of this node, which is the same as the value of the corresponding node in the originating trie. func (node AssociativeAddedTreeNode[T, V]) GetValue() V { val := node.wrapped.GetValue() if val == nil { var v V return v } subnodeMapping := val.(tree.SubNodesMapping[trieKey[T], V]) return subnodeMapping.Value } // String returns a visual representation of this node including the key and the value. // If this is the root, it will have an open circle if the root is not an added node. // Otherwise, the node will have a closed circle. func (node AssociativeAddedTreeNode[T, V]) String() string { return tree.NodeString[trieKey[T], V](printWrapper[T, V]{node.wrapped}) } // TreeString returns a visual representation of the sub-tree originating from this node, with one node per line. func (node AssociativeAddedTreeNode[T, V]) TreeString() string { return tree.AddedNodesTreeString[trieKey[T], V](node.wrapped) } ipaddress-go-1.5.4/ipaddr/addrtrieiterator.go000066400000000000000000000140031440250641600212440ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import "github.com/seancfoley/bintree/tree" type Cached = tree.C // CachingTrieIterator is an iterator of a tree that allows you to cache an object with the // lower or upper sub-node of the currently visited node. // The cached object can be retrieved later when iterating the sub-node. // That allows you to provide iteration context from a parent to its sub-nodes when iterating, // but can only be provided with iterators in which parent nodes are visited before their sub-nodes. // The caching and retrieval is done in constant-time. type CachingTrieIterator[T any] interface { IteratorWithRemove[T] // Note: We could theoretically try to make the cached type generic. // But the problem with that is that the iterator methods that return them cannot be generic on their own, the whole type would need to specify the cache type. // The other problem is that even if we could, some callers would not care about the caching behaviour and thus would not want to have to specify a cache type. // GetCached returns an object previously cached with the current iterated node. // After Next has returned a node, // if an object was cached by a call to CacheWithLowerSubNode or CacheWithUpperSubNode // was called when that node's parent was previously returned by Next, // then this returns that cached object. GetCached() Cached // CacheWithLowerSubNode caches an object with the lower sub-node of the current iterated node. // After Next has returned a node, // calling this method caches the provided object with the lower sub-node so that it can // be retrieved with GetCached when the lower sub-node is visited later. // // Returns false if it could not be cached, either because the node has since been removed with a call to Remove, // because Next has not been called yet, or because there is no lower sub node for the node previously returned by Next. // // The caching and retrieval is done in constant time. CacheWithLowerSubNode(Cached) bool // CacheWithUpperSubNode caches an object with the upper sub-node of the current iterated node. // After Next has returned a node, // calling this method caches the provided object with the upper sub-node so that it can // be retrieved with GetCached when the upper sub-node is visited later. // // Returns false if it could not be cached, either because the node has since been removed with a call to Remove, // because Next has not been called yet, or because there is no upper sub node for the node previously returned by Next. // // The caching and retrieval is done in constant time. CacheWithUpperSubNode(Cached) bool } // addressKeyIterator implements the address key iterator for tries type addressKeyIterator[T TrieKeyConstraint[T]] struct { tree.TrieKeyIterator[trieKey[T]] } func (iter addressKeyIterator[T]) Next() (t T) { return iter.TrieKeyIterator.Next().address } func (iter addressKeyIterator[T]) Remove() (t T) { return iter.TrieKeyIterator.Remove().address } // type addrTrieNodeIteratorRem[T TrieKeyConstraint[T], V any] struct { tree.TrieNodeIteratorRem[trieKey[T], V] } func (iter addrTrieNodeIteratorRem[T, V]) Next() *TrieNode[T] { return toAddressTrieNode[T](iter.TrieNodeIteratorRem.Next()) } func (iter addrTrieNodeIteratorRem[T, V]) Remove() *TrieNode[T] { return toAddressTrieNode[T](iter.TrieNodeIteratorRem.Remove()) } // type addrTrieNodeIterator[T TrieKeyConstraint[T], V any] struct { tree.TrieNodeIterator[trieKey[T], V] } func (iter addrTrieNodeIterator[T, V]) Next() *TrieNode[T] { return toAddressTrieNode[T](iter.TrieNodeIterator.Next()) } // type cachingAddressTrieNodeIterator[T TrieKeyConstraint[T], V any] struct { tree.CachingTrieNodeIterator[trieKey[T], V] } func (iter cachingAddressTrieNodeIterator[T, V]) Next() *TrieNode[T] { return toAddressTrieNode[T](iter.CachingTrieNodeIterator.Next()) } func (iter cachingAddressTrieNodeIterator[T, V]) Remove() *TrieNode[T] { return toAddressTrieNode[T](iter.CachingTrieNodeIterator.Remove()) } ////////////////////////////////////////////////////////////////// ////// type associativeAddressTrieNodeIteratorRem[T TrieKeyConstraint[T], V any] struct { tree.TrieNodeIteratorRem[trieKey[T], V] } func (iter associativeAddressTrieNodeIteratorRem[T, V]) Next() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](iter.TrieNodeIteratorRem.Next()) } func (iter associativeAddressTrieNodeIteratorRem[T, V]) Remove() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](iter.TrieNodeIteratorRem.Remove()) } // type associativeAddressTrieNodeIterator[T TrieKeyConstraint[T], V any] struct { tree.TrieNodeIterator[trieKey[T], V] } func (iter associativeAddressTrieNodeIterator[T, V]) Next() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](iter.TrieNodeIterator.Next()) } // type cachingAssociativeAddressTrieNodeIteratorX[T TrieKeyConstraint[T], V any] struct { tree.CachingTrieNodeIterator[trieKey[T], V] } func (iter cachingAssociativeAddressTrieNodeIteratorX[T, V]) Next() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](iter.CachingTrieNodeIterator.Next()) } func (iter cachingAssociativeAddressTrieNodeIteratorX[T, V]) Remove() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](iter.CachingTrieNodeIterator.Remove()) } // type emptyIterator[T any] struct{} func (it emptyIterator[T]) HasNext() bool { return false } func (it emptyIterator[T]) Next() (t T) { return } func nilAddressIterator[T any]() Iterator[T] { return emptyIterator[T]{} } ipaddress-go-1.5.4/ipaddr/addrtrienode.go000066400000000000000000002233601440250641600203500ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package ipaddr import ( "fmt" "github.com/seancfoley/bintree/tree" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "unsafe" ) // TrieKeyConstraint is the generic type constraint used for tree keys, which are individual addresses and prefix block subnets. type TrieKeyConstraint[T any] interface { comparable BitItem fmt.Stringer PrefixedConstraint[T] IsOneBit(index BitCount) bool // AddressComponent ToAddressBase() *Address // AddressType - used by MatchBits toMaxLower() T toMinUpper() T trieCompare(other *Address) int getTrailingBitCount(ones bool) BitCount toSinglePrefixBlockOrAddress() (T, addrerr.IncompatibleAddressError) } type trieKey[T TrieKeyConstraint[T]] struct { address T } func (a trieKey[T]) GetBitCount() tree.BitCount { return a.address.GetBitCount() } func (a trieKey[T]) String() string { return a.address.String() } func (a trieKey[T]) IsOneBit(bitIndex tree.BitCount) bool { return a.address.IsOneBit(bitIndex) } func (a trieKey[T]) GetTrailingBitCount(ones bool) tree.BitCount { return a.address.getTrailingBitCount(ones) } func (a trieKey[T]) GetPrefixLen() tree.PrefixLen { return tree.PrefixLen(a.address.GetPrefixLen()) } // ToPrefixBlockLen returns the address key associated with the prefix length provided, // the address key whose prefix of that length matches the prefix of this address key, and the remaining bits span all values. // // The returned address key will represent all addresses with the same prefix as this one, the prefix "block". func (a trieKey[T]) ToPrefixBlockLen(bitCount BitCount) trieKey[T] { return trieKey[T]{a.address.ToPrefixBlockLen(bitCount)} } // Compare compares to provide the same ordering used by the trie, // an ordering that works with prefix block subnets and individual addresses. // The comparator is consistent with the equality of address instances // and can be used in other contexts. However, it only works with prefix blocks and individual addresses, // not with addresses like 1-2.3.4.5-6 which cannot be differentiated using this comparator from 1.3.4.5 // and is thus not consistent with equality for subnets that are not CIDR prefix blocks. // // The comparator first compares the prefix of addresses, with the full address value considered the prefix when // there is no prefix length, ie when it is a single address. It takes the minimum m of the two prefix lengths and // compares those m prefix bits in both addresses. The ordering is determined by which of those two values is smaller or larger. // // If both prefix lengths match then both addresses are equal. // Otherwise it looks at bit m in the address with larger prefix. If 1 it is larger and if 0 it is smaller than the other. // // When comparing an address with a prefix p and an address without, the first p bits in both are compared, and if equal, // the bit at index p in the non-prefixed address determines the ordering, if 1 it is larger and if 0 it is smaller than the other. // // When comparing an address with prefix length matching the bit count to an address with no prefix, they are considered equal if the bits match. // For instance, 1.2.3.4/32 is equal to 1.2.3.4, and thus the trie does not allow 1.2.3.4/32 in the trie since it is indistinguishable from 1.2.3.4, // instead 1.2.3.4/32 is converted to 1.2.3.4 when inserted into the trie. // // When comparing 0.0.0.0/0, which has no prefix, to other addresses, the first bit in the other address determines the ordering. // If 1 it is larger and if 0 it is smaller than 0.0.0.0/0. func (a trieKey[T]) Compare(other trieKey[T]) int { return a.address.trieCompare(other.address.ToAddressBase()) } // MatchBits returns false if we need to keep going and try to match sub-nodes. // MatchBits returns true if the bits do not match, or the bits match to the very end. func (a trieKey[T]) MatchBits(key trieKey[T], bitIndex int, handleMatch tree.KeyCompareResult) bool { existingAddr := key.address.ToAddressBase() bitsPerSegment := existingAddr.GetBitsPerSegment() bytesPerSegment := existingAddr.GetBytesPerSegment() newAddr := a.address.ToAddressBase() segmentIndex := getHostSegmentIndex(bitIndex, bytesPerSegment, bitsPerSegment) segmentCount := existingAddr.GetSegmentCount() // the caller already checks total bits, so we only need to check either bitsPerSegment or segmentCount, but not both if /* newAddr.GetSegmentCount() != segmentCount || */ bitsPerSegment != newAddr.GetBitsPerSegment() { panic("mismatched bit length between address trie keys") } existingPref := existingAddr.GetPrefixLen() newPrefLen := newAddr.GetPrefixLen() // this block handles cases like matching ::ffff:102:304 to ::ffff:102:304/127, // and we found a subnode to match, but we know the final bit is a match due to the subnode being lower or upper, // so there is actually not more bits to match if segmentIndex >= segmentCount { // all the bits match handleMatch.BitsMatch() return true } bitsMatchedSoFar := segmentIndex * bitsPerSegment for { existingSegment := existingAddr.getSegment(segmentIndex) newSegment := newAddr.getSegment(segmentIndex) segmentPref := getSegmentPrefLen(existingAddr, existingPref, bitsPerSegment, bitsMatchedSoFar, existingSegment) newSegmentPref := getSegmentPrefLen(newAddr, newPrefLen, bitsPerSegment, bitsMatchedSoFar, newSegment) if segmentPref != nil { segmentPrefLen := segmentPref.Len() newPrefixLen := newSegmentPref.Len() if newSegmentPref != nil && newPrefixLen <= segmentPrefLen { matchingBits := getMatchingBits(existingSegment, newSegment, newPrefixLen, bitsPerSegment) if matchingBits >= newPrefixLen { handleMatch.BitsMatch() } else { // no match - the bits don't match // matchingBits < newPrefLen < segmentPrefLen handleMatch.BitsDoNotMatch(bitsMatchedSoFar + matchingBits) } } else { matchingBits := getMatchingBits(existingSegment, newSegment, segmentPrefLen, bitsPerSegment) if matchingBits >= segmentPrefLen { // match - the current subnet/address is a match so far, and we must go further to check smaller subnets return false } // matchingBits < segmentPrefLen - no match - the bits in current prefix do not match the prefix of the existing address handleMatch.BitsDoNotMatch(bitsMatchedSoFar + matchingBits) } return true } else if newSegmentPref != nil { newPrefixLen := newSegmentPref.Len() matchingBits := getMatchingBits(existingSegment, newSegment, newPrefixLen, bitsPerSegment) if matchingBits >= newPrefixLen { // the current bits match the current prefix, but the existing has no prefix handleMatch.BitsMatch() } else { // no match - the current subnet does not match the existing address handleMatch.BitsDoNotMatch(bitsMatchedSoFar + matchingBits) } return true } else { matchingBits := getMatchingBits(existingSegment, newSegment, bitsPerSegment, bitsPerSegment) if matchingBits < bitsPerSegment { // no match - the current subnet/address is not here handleMatch.BitsDoNotMatch(bitsMatchedSoFar + matchingBits) return true } else { segmentIndex++ if segmentIndex == segmentCount { // match - the current subnet/address is a match // note that "added" is already true here, we can only be here if explicitly inserted already since it is a non-prefixed full address handleMatch.BitsMatch() return true } } bitsMatchedSoFar += bitsPerSegment } } } // ToMaxLower changes this key to a new key with a 0 at the first bit beyond the prefix, followed by all ones, and with no prefix length. func (a trieKey[T]) ToMaxLower() trieKey[T] { return trieKey[T]{a.address.toMaxLower()} } // ToMinUpper changes this key to a new key with a 1 at the first bit beyond the prefix, followed by all zeros, and with no prefix length. func (a trieKey[T]) ToMinUpper() trieKey[T] { return trieKey[T]{a.address.toMinUpper()} } var ( _ tree.BinTrieNode[trieKey[*Address], any] _ tree.BinTrieNode[trieKey[*IPAddress], any] _ tree.BinTrieNode[trieKey[*IPv4Address], any] _ tree.BinTrieNode[trieKey[*IPv6Address], any] _ tree.BinTrieNode[trieKey[*MACAddress], any] ) // // // // type trieNode[T TrieKeyConstraint[T], V any] struct { binNode tree.BinTrieNode[trieKey[T], V] } // getKey gets the key used for placing the node in the trie. func (node *trieNode[T, V]) getKey() (t T) { return node.toBinTrieNode().GetKey().address } func (node *trieNode[T, V]) get(addr T) (V, bool) { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().Get(trieKey[T]{addr}) } func (node *trieNode[T, V]) lowerAddedNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().LowerAddedNode(trieKey[T]{addr}) } func (node *trieNode[T, V]) floorAddedNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().FloorAddedNode(trieKey[T]{addr}) } func (node *trieNode[T, V]) higherAddedNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().HigherAddedNode(trieKey[T]{addr}) } func (node *trieNode[T, V]) ceilingAddedNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().CeilingAddedNode(trieKey[T]{addr}) } // iterator returns an iterator that iterates through the elements of the sub-trie with this node as the root. // The iteration is in sorted element order. func (node *trieNode[T, V]) iterator() Iterator[T] { return addressKeyIterator[T]{node.toBinTrieNode().Iterator()} } // descendingIterator returns an iterator that iterates through the elements of the subtrie with this node as the root. // The iteration is in reverse sorted element order. func (node *trieNode[T, V]) descendingIterator() Iterator[T] { return addressKeyIterator[T]{node.toBinTrieNode().DescendingIterator()} } // nodeIterator iterates through the added nodes of the sub-trie with this node as the root, in forward or reverse tree order. func (node *trieNode[T, V]) nodeIterator(forward bool) tree.TrieNodeIteratorRem[trieKey[T], V] { return node.toBinTrieNode().NodeIterator(forward) } // allNodeIterator iterates through all the nodes of the sub-trie with this node as the root, in forward or reverse tree order. func (node *trieNode[T, V]) allNodeIterator(forward bool) tree.TrieNodeIteratorRem[trieKey[T], V] { return node.toBinTrieNode().AllNodeIterator(forward) } // blockSizeNodeIterator iterates the added nodes, ordered by keys from the largest prefix blocks to smallest and then to individual addresses, // in the sub-trie with this node as the root. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order is taken. func (node *trieNode[T, V]) blockSizeNodeIterator(lowerSubNodeFirst bool) tree.TrieNodeIteratorRem[trieKey[T], V] { return node.toBinTrieNode().BlockSizeNodeIterator(lowerSubNodeFirst) } // blockSizeAllNodeIterator iterates all the nodes, ordered by keys from the largest prefix blocks to smallest and then to individual addresses, // in the sub-trie with this node as the root. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order func (node *trieNode[T, V]) blockSizeAllNodeIterator(lowerSubNodeFirst bool) tree.TrieNodeIteratorRem[trieKey[T], V] { return node.toBinTrieNode().BlockSizeAllNodeIterator(lowerSubNodeFirst) } // blockSizeCachingAllNodeIterator iterates all nodes, ordered by keys from the largest prefix blocks to smallest and then to individual addresses, // in the sub-trie with this node as the root. func (node *trieNode[T, V]) blockSizeCachingAllNodeIterator() tree.CachingTrieNodeIterator[trieKey[T], V] { return node.toBinTrieNode().BlockSizeCachingAllNodeIterator() } func (node *trieNode[T, V]) containingFirstIterator(forwardSubNodeOrder bool) tree.CachingTrieNodeIterator[trieKey[T], V] { return node.toBinTrieNode().ContainingFirstIterator(forwardSubNodeOrder) } func (node *trieNode[T, V]) containingFirstAllNodeIterator(forwardSubNodeOrder bool) tree.CachingTrieNodeIterator[trieKey[T], V] { return node.toBinTrieNode().ContainingFirstAllNodeIterator(forwardSubNodeOrder) } func (node *trieNode[T, V]) containedFirstIterator(forwardSubNodeOrder bool) tree.TrieNodeIteratorRem[trieKey[T], V] { return node.toBinTrieNode().ContainedFirstIterator(forwardSubNodeOrder) } func (node *trieNode[T, V]) containedFirstAllNodeIterator(forwardSubNodeOrder bool) tree.TrieNodeIterator[trieKey[T], V] { return node.toBinTrieNode().ContainedFirstAllNodeIterator(forwardSubNodeOrder) } func (node *trieNode[T, V]) contains(addr T) bool { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().Contains(trieKey[T]{addr}) } func (node *trieNode[T, V]) removeNode(addr T) bool { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().RemoveNode(trieKey[T]{addr}) } func (node *trieNode[T, V]) removeElementsContainedBy(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().RemoveElementsContainedBy(trieKey[T]{addr}) } func (node *trieNode[T, V]) elementsContainedBy(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().ElementsContainedBy(trieKey[T]{addr}) } func (node *trieNode[T, V]) elementsContaining(addr T) *containmentPath[T, V] { addr = mustBeBlockOrAddress(addr) return toContainmentPath[T, V](node.toBinTrieNode().ElementsContaining(trieKey[T]{addr})) } func (node *trieNode[T, V]) longestPrefixMatch(addr T) (t T) { addr = mustBeBlockOrAddress(addr) key, _ := node.toBinTrieNode().LongestPrefixMatch(trieKey[T]{addr}) return key.address } func (node *trieNode[T, V]) longestPrefixMatchNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().LongestPrefixMatchNode(trieKey[T]{addr}) } func (node *trieNode[T, V]) elementContains(addr T) bool { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().ElementContains(trieKey[T]{addr}) } func (node *trieNode[T, V]) getNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().GetNode(trieKey[T]{addr}) } func (node *trieNode[T, V]) getAddedNode(addr T) *tree.BinTrieNode[trieKey[T], V] { addr = mustBeBlockOrAddress(addr) return node.toBinTrieNode().GetAddedNode(trieKey[T]{addr}) } func (node *trieNode[T, V]) toBinTrieNode() *tree.BinTrieNode[trieKey[T], V] { return (*tree.BinTrieNode[trieKey[T], V])(unsafe.Pointer(node)) } func toAddressTrieNode[T TrieKeyConstraint[T], V any](node *tree.BinTrieNode[trieKey[T], V]) *TrieNode[T] { return (*TrieNode[T])(unsafe.Pointer(node)) } func toAssociativeTrieNode[T TrieKeyConstraint[T], V any](node *tree.BinTrieNode[trieKey[T], V]) *AssociativeTrieNode[T, V] { return (*AssociativeTrieNode[T, V])(unsafe.Pointer(node)) } // // // // // // // // // // // // using EmptyValueType alters how values are printed in strings type emptyValue = tree.EmptyValueType // TrieNode is a node in a compact binary prefix trie whose elements (keys) are prefix block subnets or addresses. type TrieNode[T TrieKeyConstraint[T]] struct { trieNode[T, emptyValue] } func (node *TrieNode[T]) toBinTrieNode() *tree.BinTrieNode[trieKey[T], emptyValue] { return (*tree.BinTrieNode[trieKey[T], emptyValue])(unsafe.Pointer(node)) } // tobase is used to convert the pointer rather than doing a field dereference, so that nil pointer handling can be done in *addressTrieNode func (node *TrieNode[T]) tobase() *trieNode[T, emptyValue] { return (*trieNode[T, emptyValue])(unsafe.Pointer(node)) } // GetKey gets the key used to place the node in the trie. func (node *TrieNode[T]) GetKey() T { return node.tobase().getKey() } // IsRoot returns whether this node is the root of the trie. func (node *TrieNode[T]) IsRoot() bool { return node.toBinTrieNode().IsRoot() } // IsAdded returns whether the node was "added". // Some binary trie nodes are considered "added" and others are not. // Those nodes created for key elements added to the trie are "added" nodes. // Those that are not added are those nodes created to serve as junctions for the added nodes. // Only added elements contribute to the size of a trie. // When removing nodes, non-added nodes are removed automatically whenever they are no longer needed, // which is when an added node has less than two added sub-nodes. func (node *TrieNode[T]) IsAdded() bool { return node.toBinTrieNode().IsAdded() } // SetAdded makes this node an added node, which is equivalent to adding the corresponding key to the trie. // If the node is already an added node, this method has no effect. // You cannot set an added node to non-added, for that you should Remove the node from the trie by calling Remove. // A non-added node will only remain in the trie if it needs to be in the trie. func (node *TrieNode[T]) SetAdded() { node.toBinTrieNode().SetAdded() } // Clear removes this node and all sub-nodes from the trie, after which isEmpty will return true. func (node *TrieNode[T]) Clear() { node.toBinTrieNode().Clear() } // IsLeaf returns whether this node is in the trie (a node for which IsAdded is true) // and there are no elements in the sub-trie with this node as the root. func (node *TrieNode[T]) IsLeaf() bool { return node.toBinTrieNode().IsLeaf() } // GetUpperSubNode gets the direct child node whose key is largest in value. func (node *TrieNode[T]) GetUpperSubNode() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().GetUpperSubNode()) } // GetLowerSubNode gets the direct child node whose key is smallest in value. func (node *TrieNode[T]) GetLowerSubNode() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().GetLowerSubNode()) } // GetParent gets the node from which this node is a direct child node, or nil if this is the root. func (node *TrieNode[T]) GetParent() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().GetParent()) } // PreviousAddedNode returns the previous node in the trie that is an added node, following the trie order in reverse, // or nil if there is no such node. func (node *TrieNode[T]) PreviousAddedNode() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().PreviousAddedNode()) } // NextAddedNode returns the next node in the trie that is an added node, following the trie order, // or nil if there is no such node. func (node *TrieNode[T]) NextAddedNode() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().NextAddedNode()) } // NextNode returns the node that follows this node following the trie order. func (node *TrieNode[T]) NextNode() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().NextNode()) } // PreviousNode eturns the node that precedes this node following the trie order. func (node *TrieNode[T]) PreviousNode() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().PreviousNode()) } // FirstNode returns the first (the lowest valued) node in the sub-trie originating from this node. func (node *TrieNode[T]) FirstNode() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().FirstNode()) } // FirstAddedNode returns the first (the lowest valued) added node in the sub-trie originating from this node, // or nil if there are no added entries in this trie or sub-trie. func (node *TrieNode[T]) FirstAddedNode() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().FirstAddedNode()) } // LastNode returns the last (the highest valued) node in the sub-trie originating from this node. func (node *TrieNode[T]) LastNode() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().LastNode()) } // LastAddedNode returns the last (the highest valued) added node in the sub-trie originating from this node, // or nil if there are no added entries in this trie or sub-trie. func (node *TrieNode[T]) LastAddedNode() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().LastAddedNode()) } // LowerAddedNode returns the added node, in this sub-trie with this node as the root, whose address is the highest address strictly less than the given address. func (node *TrieNode[T]) LowerAddedNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](node.tobase().lowerAddedNode(addr)) } // FloorAddedNode returns the added node, in this sub-trie with this node as the root, whose address is the highest address less than or equal to the given address. func (node *TrieNode[T]) FloorAddedNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](node.tobase().floorAddedNode(addr)) } // HigherAddedNode returns the added node, in this sub-trie with this node as the root, whose address is the lowest address strictly greater than the given address. func (node *TrieNode[T]) HigherAddedNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](node.tobase().higherAddedNode(addr)) } // CeilingAddedNode returns the added node, in this sub-trie with this node as the root, whose address is the lowest address greater than or equal to the given address. func (node *TrieNode[T]) CeilingAddedNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](node.tobase().ceilingAddedNode(addr)) } // Iterator returns an iterator that iterates through the elements of the sub-trie with this node as the root. // The iteration is in sorted element order. func (node *TrieNode[T]) Iterator() Iterator[T] { return node.tobase().iterator() } // DescendingIterator returns an iterator that iterates through the elements of the subtrie with this node as the root. // The iteration is in reverse sorted element order. func (node *TrieNode[T]) DescendingIterator() Iterator[T] { return node.tobase().descendingIterator() } // NodeIterator returns an iterator that iterates through the added nodes of the sub-trie with this node as the root, in forward or reverse trie order. func (node *TrieNode[T]) NodeIterator(forward bool) IteratorWithRemove[*TrieNode[T]] { return addrTrieNodeIteratorRem[T, emptyValue]{node.tobase().nodeIterator(forward)} } // AllNodeIterator returns an iterator that iterates through all the nodes of the sub-trie with this node as the root, in forward or reverse trie order. func (node *TrieNode[T]) AllNodeIterator(forward bool) IteratorWithRemove[*TrieNode[T]] { return addrTrieNodeIteratorRem[T, emptyValue]{node.tobase().allNodeIterator(forward)} } // BlockSizeNodeIterator returns an iterator that iterates the added nodes, ordered by keys from largest prefix blocks to smallest and then to individual addresses, // in the sub-trie with this node as the root. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order is taken. func (node *TrieNode[T]) BlockSizeNodeIterator(lowerSubNodeFirst bool) IteratorWithRemove[*TrieNode[T]] { return addrTrieNodeIteratorRem[T, emptyValue]{node.tobase().blockSizeNodeIterator(lowerSubNodeFirst)} } // BlockSizeAllNodeIterator returns an iterator that iterates all the nodes, ordered by keys from largest prefix blocks to smallest and then to individual addresses, // in the sub-trie with this node as the root. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order. func (node *TrieNode[T]) BlockSizeAllNodeIterator(lowerSubNodeFirst bool) IteratorWithRemove[*TrieNode[T]] { return addrTrieNodeIteratorRem[T, emptyValue]{node.tobase().blockSizeAllNodeIterator(lowerSubNodeFirst)} } // BlockSizeCachingAllNodeIterator returns an iterator that iterates all nodes, ordered by keys from largest prefix blocks to smallest and then to individual addresses, // in the sub-trie with this node as the root. func (node *TrieNode[T]) BlockSizeCachingAllNodeIterator() CachingTrieIterator[*TrieNode[T]] { return cachingAddressTrieNodeIterator[T, emptyValue]{node.tobase().blockSizeCachingAllNodeIterator()} } // ContainingFirstIterator returns an iterator that does a pre-order binary trie traversal of the added nodes // of the sub-trie with this node as the root. // // All added nodes will be visited before their added sub-nodes. // For an address trie this means added containing subnet blocks will be visited before their added contained addresses and subnet blocks. // // Once a given node is visited, the iterator allows you to cache an object corresponding to the // lower or upper sub-node that can be retrieved when you later visit that sub-node. // // Objects are cached only with nodes to be visited. // So for this iterator that means an object will be cached with the first added lower or upper sub-node, // the next lower or upper sub-node to be visited, // which is not necessarily the direct lower or upper sub-node of a given node. // // The caching allows you to provide iteration context from a parent to its sub-nodes when iterating. // The caching and retrieval is done in constant-time. func (node *TrieNode[T]) ContainingFirstIterator(forwardSubNodeOrder bool) CachingTrieIterator[*TrieNode[T]] { return cachingAddressTrieNodeIterator[T, emptyValue]{node.tobase().containingFirstIterator(forwardSubNodeOrder)} } // ContainingFirstAllNodeIterator returns an iterator that does a pre-order binary trie traversal of all the nodes // of the sub-trie with this node as the root. // // All nodes will be visited before their sub-nodes. // For an address trie this means containing subnet blocks will be visited before their contained addresses and subnet blocks. // // Once a given node is visited, the iterator allows you to cache an object corresponding to the // lower or upper sub-node that can be retrieved when you later visit that sub-node. // That allows you to provide iteration context from a parent to its sub-nodes when iterating. // The caching and retrieval is done in constant-time. func (node *TrieNode[T]) ContainingFirstAllNodeIterator(forwardSubNodeOrder bool) CachingTrieIterator[*TrieNode[T]] { return cachingAddressTrieNodeIterator[T, emptyValue]{node.tobase().containingFirstAllNodeIterator(forwardSubNodeOrder)} } // ContainedFirstIterator returns an iterator that does a post-order binary trie traversal of the added nodes // of the sub-trie with this node as the root. // All added sub-nodes will be visited before their parent nodes. // For an address trie this means contained addresses and subnets will be visited before their containing subnet blocks. func (node *TrieNode[T]) ContainedFirstIterator(forwardSubNodeOrder bool) IteratorWithRemove[*TrieNode[T]] { return addrTrieNodeIteratorRem[T, emptyValue]{node.tobase().containedFirstIterator(forwardSubNodeOrder)} } // ContainedFirstAllNodeIterator returns an iterator that does a post-order binary trie traversal of all the nodes // of the sub-trie with this node as the root. // All sub-nodes will be visited before their parent nodes. // For an address trie this means contained addresses and subnets will be visited before their containing subnet blocks. func (node *TrieNode[T]) ContainedFirstAllNodeIterator(forwardSubNodeOrder bool) Iterator[*TrieNode[T]] { return addrTrieNodeIterator[T, emptyValue]{node.tobase().containedFirstAllNodeIterator(forwardSubNodeOrder)} } // Clone clones the node. // Keys remain the same, but the parent node and the lower and upper sub-nodes are all set to nil. func (node *TrieNode[T]) Clone() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().Clone()) } // CloneTree clones the sub-trie starting with this node as the root. // The nodes are cloned, but their keys and values are not cloned. func (node *TrieNode[T]) CloneTree() *TrieNode[T] { return toAddressTrieNode[T](node.toBinTrieNode().CloneTree()) } // AsNewTrie creates a new sub-trie, copying the nodes starting with this node as the root. // The nodes are copies of the nodes in this sub-trie, but their keys and values are not copies. func (node *TrieNode[T]) AsNewTrie() *Trie[T] { return toAddressTrie[T](node.toBinTrieNode().AsNewTrie()) } // Compare returns a negative integer, zero, or a positive integer if this node is less than, equal, or greater than the other, according to the key and the trie order. func (node *TrieNode[T]) Compare(other *TrieNode[T]) int { return node.toBinTrieNode().Compare(other.toBinTrieNode()) } // Equal returns whether the address and and mapped value match those of the given node. func (node *TrieNode[T]) Equal(other *TrieNode[T]) bool { return node.toBinTrieNode().Equal(other.toBinTrieNode()) } // TreeEqual returns whether the sub-tree represented by this node as the root node matches the given sub-trie. func (node *TrieNode[T]) TreeEqual(other *TrieNode[T]) bool { return node.toBinTrieNode().TreeEqual(other.toBinTrieNode()) } // Remove removes this node from the collection of added nodes, and also from the trie if possible. // If it has two sub-nodes, it cannot be removed from the trie, in which case it is marked as not "added", // nor is it counted in the trie size. // Only added nodes can be removed from the trie. If this node is not added, this method does nothing. func (node *TrieNode[T]) Remove() { node.toBinTrieNode().Remove() } // Contains returns whether the given address or prefix block subnet is in the sub-trie, as an added element, with this node as the root. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns true if the prefix block or address address exists already in the trie, false otherwise. // // Use GetAddedNode to get the node for the address rather than just checking for its existence. func (node *TrieNode[T]) Contains(addr T) bool { return node.tobase().contains(addr) } // RemoveNode removes the given single address or prefix block subnet from the trie with this node as the root. // // Removing an element will not remove contained elements (nodes for contained blocks and addresses). // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns true if the prefix block or address was removed, false if not already in the trie. // // You can also remove by calling GetAddedNode to get the node and then calling Remove on the node. // // When an address is removed, the corresponding node may remain in the trie if it remains a subnet block for two sub-nodes. // If the corresponding node can be removed from the trie, it will be removed. func (node *TrieNode[T]) RemoveNode(addr T) bool { return node.tobase().removeNode(addr) } // RemoveElementsContainedBy removes any single address or prefix block subnet from the trie, with this node as the root, that is contained in the given individual address or prefix block subnet. // // Goes further than Remove, not requiring a match to an inserted node, and also removing all the sub-nodes of any removed node or sub-node. // // For example, after inserting 1.2.3.0 and 1.2.3.1, passing 1.2.3.0/31 to RemoveElementsContainedBy will remove them both, // while the Remove method will remove nothing. // After inserting 1.2.3.0/31, then Remove(Address) will remove 1.2.3.0/31, but will leave 1.2.3.0 and 1.2.3.1 in the trie. // // It cannot partially delete a node, such as deleting a single address from a prefix block represented by a node. // It can only delete the whole node if the whole address or block represented by that node is contained in the given address or block. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // //Returns the root node of the subtrie that was removed from the trie, or nil if nothing was removed. func (node *TrieNode[T]) RemoveElementsContainedBy(addr T) *TrieNode[T] { return toAddressTrieNode[T](node.tobase().removeElementsContainedBy(addr)) } // ElementsContainedBy checks if a part of this trie, with this node as the root, is contained by the given prefix block subnet or individual address. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns the root node of the contained subtrie, or nil if no subtrie is contained. // The node returned need not be an "added" node, see IsAdded for more details on added nodes. // The returned subtrie is backed by this trie, so changes in this trie are reflected in those nodes and vice-versa. func (node *TrieNode[T]) ElementsContainedBy(addr T) *TrieNode[T] { return toAddressTrieNode[T](node.tobase().elementsContainedBy(addr)) } // ElementsContaining finds the trie nodes in the trie, with this sub-node as the root, // containing the given key and returns them as a linked list. // Only added nodes are added to the linked list // // If the argument is not a single address nor prefix block, this method will panic. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. func (node *TrieNode[T]) ElementsContaining(addr T) *ContainmentPath[T] { return &ContainmentPath[T]{*node.tobase().elementsContaining(addr)} } // LongestPrefixMatch returns the address or subnet with the longest prefix of all the added subnets and addresses whose prefix matches the given address. // This is equivalent to finding the containing subnet or address with the smallest subnet size. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // The second returned argument is false if no added subnet or address contains the given argument. // // Use ElementContains to check for the existence of a containing address. // To get all the containing addresses (subnets with matching prefix), use ElementsContaining. // To get the node corresponding to the result of this method, use LongestPrefixMatchNode. func (node *TrieNode[T]) LongestPrefixMatch(addr T) T { return node.tobase().longestPrefixMatch(addr) } // LongestPrefixMatchNode finds the containing subnet or address in the trie with the smallest subnet size, // which is equivalent to finding the subnet or address with the longest matching prefix. // Returns the node corresponding to that subnet. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns nil if no added subnet or address contains the given argument. // // Use ElementContains to check for the existence of a containing address. // To get all the containing addresses, use ElementsContaining. // Use LongestPrefixMatch to get only the address corresponding to the result of this method. func (node *TrieNode[T]) LongestPrefixMatchNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](node.tobase().longestPrefixMatchNode(addr)) } // ElementContains checks if a prefix block subnet or address in the trie, with this node as the root, contains the given subnet or address. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns true if the subnet or address is contained by a trie element, false otherwise. // // To get all the containing addresses, use ElementsContaining. func (node *TrieNode[T]) ElementContains(addr T) bool { return node.tobase().elementContains(addr) } // GetNode gets the node in the trie, with this subnode as the root, corresponding to the given address, // or returns nil if not such element exists. // // It returns any node, whether added or not, // including any prefix block node that was not added. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. func (node *TrieNode[T]) GetNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](node.tobase().getNode(addr)) } // GetAddedNode gets trie nodes representing added elements. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Use Contains to check for the existence of a given address in the trie, // as well as GetNode to search for all nodes including those not-added but also auto-generated nodes for subnet blocks. func (node *TrieNode[T]) GetAddedNode(addr T) *TrieNode[T] { return toAddressTrieNode[T](node.tobase().getAddedNode(addr)) } // NodeSize returns the number of nodes in the trie with this node as the root, which is more than the number of added addresses or blocks. func (node *TrieNode[T]) NodeSize() int { return node.toBinTrieNode().NodeSize() } // Size returns the number of elements in the sub-trie with this node as the root. // Only nodes for which IsAdded returns true are counted. // When zero is returned, IsEmpty returns true. func (node *TrieNode[T]) Size() int { return node.toBinTrieNode().Size() } // IsEmpty returns whether the size is zero. func (node *TrieNode[T]) IsEmpty() bool { return node.Size() == 0 } // TreeString returns a visual representation of the sub-trie with this node as the root, with one node per line. // // - withNonAddedKeys: whether to show nodes that are not added nodes. // - withSizes: whether to include the counts of added nodes in each sub-trie. func (node *TrieNode[T]) TreeString(withNonAddedKeys, withSizes bool) string { return node.toBinTrieNode().TreeString(withNonAddedKeys, withSizes) } // String returns a visual representation of this node including the key, with an open circle indicating this node is not an added node, // a closed circle indicating this node is an added node. func (node *TrieNode[T]) String() string { return node.toBinTrieNode().String() } // For some reason Format must be here and not in addressTrieNode for nil node. // It panics in fmt code either way, but if in here then it is handled by a recover() call in fmt properly in the debugger. // Format implements the [fmt.Formatter] interface. func (node TrieNode[T]) Format(state fmt.State, verb rune) { node.toBinTrieNode().Format(state, verb) } // // // // // // // // // // // // // // // // // // AssociativeTrieNode represents a node of an associative compact binary prefix trie. // Each key is a prefix block subnet or address. Each node also has an associated value. type AssociativeTrieNode[T TrieKeyConstraint[T], V any] struct { trieNode[T, V] } func (node *AssociativeTrieNode[T, V]) toBinTrieNode() *tree.BinTrieNode[trieKey[T], V] { return (*tree.BinTrieNode[trieKey[T], V])(unsafe.Pointer(node)) } func (node *AssociativeTrieNode[T, V]) toBase() *trieNode[T, V] { return (*trieNode[T, V])(unsafe.Pointer(node)) } // GetKey gets the key used for placing the node in the trie. func (node *AssociativeTrieNode[T, V]) GetKey() T { return node.toBase().getKey() } // IsRoot returns whether this is the root of the backing trie. func (node *AssociativeTrieNode[T, V]) IsRoot() bool { return node.toBinTrieNode().IsRoot() } // IsAdded returns whether the node was "added". // Some binary trie nodes are considered "added" and others are not. // Those nodes created for key elements added to the trie are "added" nodes. // Those that are not added are those nodes created to serve as junctions for the added nodes. // Only added elements contribute to the size of a trie. // When removing nodes, non-added nodes are removed automatically whenever they are no longer needed, // which is when an added node has less than two added sub-nodes. func (node *AssociativeTrieNode[T, V]) IsAdded() bool { return node.toBinTrieNode().IsAdded() } // SetAdded makes this node an added node, which is equivalent to adding the corresponding key to the trie. // If the node is already an added node, this method has no effect. // You cannot set an added node to non-added, for that you should Remove the node from the trie by calling Remove. // A non-added node will only remain in the trie if it needs to be in the trie. func (node *AssociativeTrieNode[T, V]) SetAdded() { node.toBinTrieNode().SetAdded() } // Clear removes this node and all sub-nodes from the tree, after which isEmpty will return true. func (node *AssociativeTrieNode[T, V]) Clear() { node.toBinTrieNode().Clear() } // IsLeaf returns whether this node is in the tree (a node for which IsAdded is true) // and there are no elements in the sub-tree with this node as the root. func (node *AssociativeTrieNode[T, V]) IsLeaf() bool { return node.toBinTrieNode().IsLeaf() } // ClearValue makes the value associated with this node the zero-value of V. func (node *AssociativeTrieNode[T, V]) ClearValue() { node.toBinTrieNode().ClearValue() } // SetValue sets the value associated with this node. func (node *AssociativeTrieNode[T, V]) SetValue(val V) { node.toBinTrieNode().SetValue(val) } // GetValue returns whather there is a value associated with the node, and returns that value. func (node *AssociativeTrieNode[T, V]) GetValue() V { return node.toBinTrieNode().GetValue() } // GetUpperSubNode gets the direct child node whose key is largest in value. func (node *AssociativeTrieNode[T, V]) GetUpperSubNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().GetUpperSubNode()) } // GetLowerSubNode gets the direct child node whose key is smallest in value. func (node *AssociativeTrieNode[T, V]) GetLowerSubNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().GetLowerSubNode()) } // GetParent gets the node from which this node is a direct child node, or nil if this is the root. func (node *AssociativeTrieNode[T, V]) GetParent() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().GetParent()) } // PreviousAddedNode returns the first added node that precedes this node following the trie order. func (node *AssociativeTrieNode[T, V]) PreviousAddedNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().PreviousAddedNode()) } // NextAddedNode returns the first added node that follows this node following the trie order. func (node *AssociativeTrieNode[T, V]) NextAddedNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().NextAddedNode()) } // NextNode returns the node that follows this node following the trie order. func (node *AssociativeTrieNode[T, V]) NextNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().NextNode()) } // PreviousNode returns the node that precedes this node following the trie order. func (node *AssociativeTrieNode[T, V]) PreviousNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().PreviousNode()) } // FirstNode returns the first (the lowest valued) node in the sub-trie originating from this node. func (node *AssociativeTrieNode[T, V]) FirstNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().FirstNode()) } // FirstAddedNode returns the first (the lowest valued) added node in the sub-trie originating from this node, // or nil if there are no added entries in this trie or sub-trie. func (node *AssociativeTrieNode[T, V]) FirstAddedNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().FirstAddedNode()) } // LastNode returns the last (the highest valued) node in the sub-trie originating from this node. func (node *AssociativeTrieNode[T, V]) LastNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().LastNode()) } // LastAddedNode returns the last (the highest valued) added node in the sub-trie originating from this node, // or nil if there are no added entries in this trie or sub-trie. func (node *AssociativeTrieNode[T, V]) LastAddedNode() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().LastAddedNode()) } // LowerAddedNode returns the added node, in this sub-trie with this node as the root, whose address is the highest address strictly less than the given address. func (node *AssociativeTrieNode[T, V]) LowerAddedNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBase().lowerAddedNode(addr)) } // FloorAddedNode returns the added node, in this sub-trie with this node as the root, whose address is the highest address less than or equal to the given address. func (node *AssociativeTrieNode[T, V]) FloorAddedNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBase().floorAddedNode(addr)) } // HigherAddedNode returns the added node, in this sub-trie with this node as the root, whose address is the lowest address strictly greater than the given address. func (node *AssociativeTrieNode[T, V]) HigherAddedNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBase().higherAddedNode(addr)) } // CeilingAddedNode returns the added node, in this sub-trie with this node as the root, whose address is the lowest address greater than or equal to the given address. func (node *AssociativeTrieNode[T, V]) CeilingAddedNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBase().ceilingAddedNode(addr)) } // Iterator returns an iterator that iterates through the elements of the sub-trie with this node as the root. // The iteration is in sorted element order. func (node *AssociativeTrieNode[T, V]) Iterator() Iterator[T] { return node.toBase().iterator() } // DescendingIterator returns an iterator that iterates through the elements of the subtrie with this node as the root. // The iteration is in reverse sorted element order. func (node *AssociativeTrieNode[T, V]) DescendingIterator() Iterator[T] { return node.toBase().descendingIterator() } // NodeIterator returns an iterator that iterates through the added nodes of the sub-trie with this node as the root, in forward or reverse trie order. func (node *AssociativeTrieNode[T, V]) NodeIterator(forward bool) IteratorWithRemove[*AssociativeTrieNode[T, V]] { return associativeAddressTrieNodeIteratorRem[T, V]{node.toBase().nodeIterator(forward)} } // AllNodeIterator returns an iterator that iterates through all the nodes of the sub-trie with this node as the root, in forward or reverse trie order. func (node *AssociativeTrieNode[T, V]) AllNodeIterator(forward bool) IteratorWithRemove[*AssociativeTrieNode[T, V]] { return associativeAddressTrieNodeIteratorRem[T, V]{node.toBase().allNodeIterator(forward)} } // BlockSizeNodeIterator returns an iterator that iterates the added nodes, ordered by keys from largest prefix blocks to smallest and then to individual addresses, // in the sub-trie with this node as the root. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order is taken. func (node *AssociativeTrieNode[T, V]) BlockSizeNodeIterator(lowerSubNodeFirst bool) IteratorWithRemove[*AssociativeTrieNode[T, V]] { return associativeAddressTrieNodeIteratorRem[T, V]{node.toBase().blockSizeNodeIterator(lowerSubNodeFirst)} } // BlockSizeAllNodeIterator returns an iterator that iterates all the nodes, ordered by keys from largest prefix blocks to smallest and then to individual addresses, // in the sub-trie with this node as the root. // // If lowerSubNodeFirst is true, for blocks of equal size the lower is first, otherwise the reverse order. func (node *AssociativeTrieNode[T, V]) BlockSizeAllNodeIterator(lowerSubNodeFirst bool) IteratorWithRemove[*AssociativeTrieNode[T, V]] { return associativeAddressTrieNodeIteratorRem[T, V]{node.toBase().blockSizeAllNodeIterator(lowerSubNodeFirst)} } // BlockSizeCachingAllNodeIterator returns an iterator that iterates all nodes, ordered by keys from largest prefix blocks to smallest and then to individual addresses, // in the sub-trie with this node as the root. func (node *AssociativeTrieNode[T, V]) BlockSizeCachingAllNodeIterator() CachingTrieIterator[*AssociativeTrieNode[T, V]] { return cachingAssociativeAddressTrieNodeIteratorX[T, V]{node.toBase().blockSizeCachingAllNodeIterator()} } // ContainingFirstIterator returns an iterator that does a pre-order binary trie traversal of the added nodes // of the sub-trie with this node as the root. // // All added nodes will be visited before their added sub-nodes. // For an address trie this means added containing subnet blocks will be visited before their added contained addresses and subnet blocks. // // Once a given node is visited, the iterator allows you to cache an object corresponding to the // lower or upper sub-node that can be retrieved when you later visit that sub-node. // // Objects are cached only with nodes to be visited. // So for this iterator that means an object will be cached with the first added lower or upper sub-node, // the next lower or upper sub-node to be visited, // which is not necessarily the direct lower or upper sub-node of a given node. // // The caching allows you to provide iteration context from a parent to its sub-nodes when iterating. // The caching and retrieval is done in constant-time. func (node *AssociativeTrieNode[T, V]) ContainingFirstIterator(forwardSubNodeOrder bool) CachingTrieIterator[*AssociativeTrieNode[T, V]] { return cachingAssociativeAddressTrieNodeIteratorX[T, V]{node.toBase().containingFirstIterator(forwardSubNodeOrder)} } // ContainingFirstAllNodeIterator returns an iterator that does a pre-order binary trie traversal of all the nodes // of the sub-trie with this node as the root. // // All nodes will be visited before their sub-nodes. // For an address trie this means containing subnet blocks will be visited before their contained addresses and subnet blocks. // // Once a given node is visited, the iterator allows you to cache an object corresponding to the // lower or upper sub-node that can be retrieved when you later visit that sub-node. // That allows you to provide iteration context from a parent to its sub-nodes when iterating. // The caching and retrieval is done in constant-time. func (node *AssociativeTrieNode[T, V]) ContainingFirstAllNodeIterator(forwardSubNodeOrder bool) CachingTrieIterator[*AssociativeTrieNode[T, V]] { return cachingAssociativeAddressTrieNodeIteratorX[T, V]{node.toBase().containingFirstAllNodeIterator(forwardSubNodeOrder)} } // ContainedFirstIterator returns an iterator that does a post-order binary trie traversal of the added nodes // of the sub-trie with this node as the root. // All added sub-nodes will be visited before their parent nodes. // For an address trie this means contained addresses and subnets will be visited before their containing subnet blocks. func (node *AssociativeTrieNode[T, V]) ContainedFirstIterator(forwardSubNodeOrder bool) IteratorWithRemove[*AssociativeTrieNode[T, V]] { return associativeAddressTrieNodeIteratorRem[T, V]{node.toBase().containedFirstIterator(forwardSubNodeOrder)} } // ContainedFirstAllNodeIterator returns an iterator that does a post-order binary trie traversal of all the nodes // of the sub-trie with this node as the root. // All sub-nodes will be visited before their parent nodes. // For an address trie this means contained addresses and subnets will be visited before their containing subnet blocks. func (node *AssociativeTrieNode[T, V]) ContainedFirstAllNodeIterator(forwardSubNodeOrder bool) Iterator[*AssociativeTrieNode[T, V]] { return associativeAddressTrieNodeIterator[T, V]{node.toBase().containedFirstAllNodeIterator(forwardSubNodeOrder)} } // Clone clones the node. // Keys remain the same, but the parent node and the lower and upper sub-nodes are all set to nil. func (node *AssociativeTrieNode[T, V]) Clone() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().Clone()) } // CloneTree clones the sub-trie starting with this node as the root. // The nodes are cloned, but their keys and values are not cloned. func (node *AssociativeTrieNode[T, V]) CloneTree() *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBinTrieNode().CloneTree()) } // AsNewTrie creates a new sub-trie, copying the nodes starting with this node as the root. // The nodes are copies of the nodes in this sub-trie, but their keys and values are not copies. func (node *AssociativeTrieNode[T, V]) AsNewTrie() *AssociativeTrie[T, V] { return toAssociativeTrie[T, V](node.toBinTrieNode().AsNewTrie()) } // Compare returns a negative integer, zero, or a positive integer if this node is less than, equal, or greater than the other, according to the key and the trie order. func (node *AssociativeTrieNode[T, V]) Compare(other *AssociativeTrieNode[T, V]) int { return node.toBinTrieNode().Compare(other.toBinTrieNode()) } // Equal returns whether the key and mapped value match those of the given node. func (node *AssociativeTrieNode[T, V]) Equal(other *AssociativeTrieNode[T, V]) bool { return node.toBinTrieNode().Equal(other.toBinTrieNode()) } // TreeEqual returns whether the sub-trie represented by this node as the root node matches the given sub-trie. func (node *AssociativeTrieNode[T, V]) TreeEqual(other *AssociativeTrieNode[T, V]) bool { return node.toBinTrieNode().TreeEqual(other.toBinTrieNode()) } // DeepEqual returns whether the key is equal to that of the given node and the value is deep equal to that of the given node. func (node *AssociativeTrieNode[T, V]) DeepEqual(other *AssociativeTrieNode[T, V]) bool { return node.toBinTrieNode().DeepEqual(other.toBinTrieNode()) } // TreeDeepEqual returns whether the sub-trie represented by this node as the root node matches the given sub-trie, matching with Compare on the keys and reflect.DeepEqual on the values. func (node *AssociativeTrieNode[T, V]) TreeDeepEqual(other *AssociativeTrieNode[T, V]) bool { return node.toBinTrieNode().TreeDeepEqual(other.toBinTrieNode()) } ///////////////////////////////////////////////////////////////////////////// // Remove removes this node from the collection of added nodes, and also from the trie if possible. // If it has two sub-nodes, it cannot be removed from the trie, in which case it is marked as not "added", // nor is it counted in the trie size. // Only added nodes can be removed from the trie. If this node is not added, this method does nothing. func (node *AssociativeTrieNode[T, V]) Remove() { node.toBinTrieNode().Remove() } // Contains returns whether the given address or prefix block subnet is in the sub-trie, as an added element, with this node as the root. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns true if the prefix block or address address exists already in the trie, false otherwise. // // Use GetAddedNode to get the node for the address rather than just checking for its existence. func (node *AssociativeTrieNode[T, V]) Contains(addr T) bool { return node.toBase().contains(addr) } // RemoveNode removes the given single address or prefix block subnet from the trie with this node as the root. // // Removing an element will not remove contained elements (nodes for contained blocks and addresses). // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns true if the prefix block or address was removed, false if not already in the trie. // // You can also remove by calling GetAddedNode to get the node and then calling Remove on the node. // // When an address is removed, the corresponding node may remain in the trie if it remains a subnet block for two sub-nodes. // If the corresponding node can be removed from the trie, it will be removed. func (node *AssociativeTrieNode[T, V]) RemoveNode(addr T) bool { return node.toBase().removeNode(addr) } // RemoveElementsContainedBy removes any single address or prefix block subnet from the trie, with this node as the root, that is contained in the given individual address or prefix block subnet. // // Goes further than Remove, not requiring a match to an inserted node, and also removing all the sub-nodes of any removed node or sub-node. // // For example, after inserting 1.2.3.0 and 1.2.3.1, passing 1.2.3.0/31 to RemoveElementsContainedBy will remove them both, // while the Remove method will remove nothing. // After inserting 1.2.3.0/31, then Remove(Address) will remove 1.2.3.0/31, but will leave 1.2.3.0 and 1.2.3.1 in the trie. // // It cannot partially delete a node, such as deleting a single address from a prefix block represented by a node. // It can only delete the whole node if the whole address or block represented by that node is contained in the given address or block. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // //Returns the root node of the subtrie that was removed from the trie, or nil if nothing was removed. func (node *AssociativeTrieNode[T, V]) RemoveElementsContainedBy(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBase().removeElementsContainedBy(addr)) } // ElementsContainedBy checks if a part of this trie, with this node as the root, is contained by the given prefix block subnet or individual address. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns the root node of the contained subtrie, or nil if no subtrie is contained. // The node returned need not be an "added" node, see IsAdded for more details on added nodes. // The returned subtrie is backed by this trie, so changes in this trie are reflected in those nodes and vice-versa. func (node *AssociativeTrieNode[T, V]) ElementsContainedBy(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBase().elementsContainedBy(addr)) } // ElementsContaining finds the trie nodes in the trie, with this sub-node as the root, // containing the given key and returns them as a linked list. // Only added nodes are added to the linked list. // // If the argument is not a single address nor prefix block, this method will panic. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. func (node *AssociativeTrieNode[T, V]) ElementsContaining(addr T) *ContainmentValuesPath[T, V] { return &ContainmentValuesPath[T, V]{*node.toBase().elementsContaining(addr)} } // LongestPrefixMatch returns the address or subnet with the longest prefix of all the added subnets or the address whose prefix matches the given address. // This is equivalent to finding the containing subnet or address with the smallest subnet size. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns nil if no added subnet or address contains the given argument. // // Use ElementContains to check for the existence of a containing address. // To get all the containing addresses (subnets with matching prefix), use ElementsContaining. // To get the node corresponding to the result of this method, use LongestPrefixMatchNode. func (node *AssociativeTrieNode[T, V]) LongestPrefixMatch(addr T) T { return node.toBase().longestPrefixMatch(addr) } // LongestPrefixMatchNode finds the containing subnet or address in the trie with the smallest subnet size, // which is equivalent to finding the subnet or address with the longest matching prefix. // Returns the node corresponding to that subnet. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns nil if no added subnet or address contains the given argument. // // Use ElementContains to check for the existence of a containing address. // To get all the containing addresses, use ElementsContaining. // Use LongestPrefixMatch to get only the address corresponding to the result of this method. func (node *AssociativeTrieNode[T, V]) LongestPrefixMatchNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBase().longestPrefixMatchNode(addr)) } // ElementContains checks if a prefix block subnet or address in the trie, with this node as the root, contains the given subnet or address. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns true if the subnet or address is contained by a trie element, false otherwise. // // To get all the containing addresses, use ElementsContaining. func (node *AssociativeTrieNode[T, V]) ElementContains(addr T) bool { return node.toBase().elementContains(addr) } // GetNode gets the node in the trie, with this subnode as the root, corresponding to the given address, // or returns nil if not such element exists. // // It returns any node, whether added or not, // including any prefix block node that was not added. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. func (node *AssociativeTrieNode[T, V]) GetNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBase().getNode(addr)) } // GetAddedNode gets trie nodes representing added elements. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Use Contains to check for the existence of a given address in the trie, // as well as GetNode to search for all nodes including those not-added but also auto-generated nodes for subnet blocks. func (node *AssociativeTrieNode[T, V]) GetAddedNode(addr T) *AssociativeTrieNode[T, V] { return toAssociativeTrieNode[T, V](node.toBase().getAddedNode(addr)) } // Get gets the value for the specified key in this mapped trie or subtrie. // // If the argument is not a single address nor prefix block, this method will panic. // The [Partition] type can be used to convert the argument to single addresses and prefix blocks before calling this method. // // Returns the value for the given key. // Returns nil if the contains no mapping for that key or if the mapped value is nil. func (node *AssociativeTrieNode[T, V]) Get(addr T) (V, bool) { return node.toBase().get(addr) } // NodeSize returns the number of nodes in the trie with this node as the root, which is more than the number of added addresses or blocks. func (node *AssociativeTrieNode[T, V]) NodeSize() int { return node.toBinTrieNode().NodeSize() } // Size returns the number of elements in the trie. // Only nodes for which IsAdded returns true are counted. // When zero is returned, IsEmpty returns true. func (node *AssociativeTrieNode[T, V]) Size() int { return node.toBinTrieNode().Size() } // IsEmpty returns whether the size is zero. func (node *AssociativeTrieNode[T, V]) IsEmpty() bool { return node.Size() == 0 } // TreeString returns a visual representation of the sub-trie with this node as the root, with one node per line. // // - withNonAddedKeys: whether to show nodes that are not added nodes // - withSizes: whether to include the counts of added nodes in each sub-trie func (node *AssociativeTrieNode[T, V]) TreeString(withNonAddedKeys, withSizes bool) string { return node.toBinTrieNode().TreeString(withNonAddedKeys, withSizes) } // String returns a visual representation of this node including the key, with an open circle indicating this node is not an added node, // a closed circle indicating this node is an added node. func (node *AssociativeTrieNode[T, V]) String() string { return node.toBinTrieNode().String() } // For some reason Format must be here and not in addressTrieNode for nil node. // It panics in fmt code either way, but if in here then it is handled by a recover() call in fmt properly in the debugger. // Format implements the [fmt.Formatter] interface. func (node AssociativeTrieNode[T, V]) Format(state fmt.State, verb rune) { node.toBase().binNode.Format(state, verb) } // // // // // // // // ContainmentPath represents a path through the trie of containing subnets, // each node in the path contained by the previous node, // the first node corresponding to the shortest prefix match, the last element corresponding to the longest prefix match. type containmentPath[T TrieKeyConstraint[T], V any] struct { path tree.Path[trieKey[T], V] } // Count returns the count of containing subnets in the path of containing subnets, starting from this node and moving downwards to sub-nodes. // This is a constant-time operation since the size is maintained in each node and adjusted with each add and Remove operation in the sub-tree. func (path *containmentPath[T, V]) count() int { if path == nil { return 0 } return path.path.Size() } // String returns a visual representation of the Path with one node per line. func (path *containmentPath[T, V]) string() string { if path == nil { return nilString() } return path.path.String() } func toContainmentPath[T TrieKeyConstraint[T], V any](path *tree.Path[trieKey[T], V]) *containmentPath[T, V] { return (*containmentPath[T, V])(unsafe.Pointer(path)) } // // // // // // ContainmentPath represents a path through the trie of containing subnets, // each node in the path contained by the previous node, // the first node corresponding to the shortest prefix match, the last element corresponding to the longest prefix match. type ContainmentPath[T TrieKeyConstraint[T]] struct { containmentPath[T, emptyValue] } // Count returns the count of containing subnets in the path of containing subnets, starting from this node and moving downwards to sub-nodes. // This is a constant-time operation since the size is maintained in each node and adjusted with each add and Remove operation in the sub-tree. func (path *ContainmentPath[T]) Count() int { return path.count() } // String returns a visual representation of the Path with one node per line. func (path *ContainmentPath[T]) String() string { return path.string() } // ShortestPrefixMatch returns the beginning of the Path of containing subnets, which may or may not match the tree root of the originating tree. // If there are no containing elements (prefix matches) this returns nil. func (path *ContainmentPath[T]) ShortestPrefixMatch() *ContainmentPathNode[T] { return toContainmentPathNode[T](path.path.GetRoot()) } // LongestPrefixMatch returns the end of the Path of containing subnets, which may or may not match a leaf in the originating tree. // If there are no containing elements (prefix matches) this returns nil. func (path *ContainmentPath[T]) LongestPrefixMatch() *ContainmentPathNode[T] { return toContainmentPathNode[T](path.path.GetLeaf()) } // // // // // // // // // // ContainmentValuesPath represents a path through the associative trie of containing subnets, // each node in the path contained by the previous node, // the first node corresponding to the shortest prefix match, the last element corresponding to the longest prefix match. type ContainmentValuesPath[T TrieKeyConstraint[T], V any] struct { containmentPath[T, V] } // Count returns the count of containing subnets in the path of containing subnets, starting from this node and moving downwards to sub-nodes. // This is a constant-time operation since the size is maintained in each node and adjusted with each add and Remove operation in the sub-tree. func (path *ContainmentValuesPath[T, V]) Count() int { return path.count() } // String returns a visual representation of the Path with one node per line. func (path *ContainmentValuesPath[T, V]) String() string { return path.string() } // ShortestPrefixMatch returns the beginning of the Path of containing subnets, which may or may not match the tree root of the originating tree. // If there are no containing elements (prefix matches) this returns nil. func (path *ContainmentValuesPath[T, V]) ShortestPrefixMatch() *ContainmentValuesPathNode[T, V] { return toContainmentValuesPathNode[T, V](path.path.GetRoot()) } // LongestPrefixMatch returns the end of the Path of containing subnets, which may or may not match a leaf in the originating tree. // If there are no containing elements (prefix matches) this returns nil. func (path *ContainmentValuesPath[T, V]) LongestPrefixMatch() *ContainmentValuesPathNode[T, V] { return toContainmentValuesPathNode[T, V](path.path.GetLeaf()) } // // // // // // // // // // ContainmentPathNode is a node in a ContainmentPath type containmentPathNode[T TrieKeyConstraint[T], V any] struct { pathNode tree.PathNode[trieKey[T], V] } // getKey gets the containing block or matching address corresponding to this node func (node *containmentPathNode[T, V]) getKey() T { return node.pathNode.GetKey().address } // Count returns the count of containing subnets in the path of containing subnets, starting from this node and moving downwards to sub-nodes. // This is a constant-time operation since the size is maintained in each node and adjusted with each add and Remove operation in the sub-tree. func (node *containmentPathNode[T, V]) count() int { if node == nil { return 0 } return node.pathNode.Size() } // String returns a visual representation of this node including the address key func (node *containmentPathNode[T, V]) string() string { if node == nil { return nilString() } return node.pathNode.String() } // ListString returns a visual representation of the containing subnets starting from this node and moving downwards to sub-nodes. func (node *containmentPathNode[T, V]) listString() string { return node.pathNode.ListString(true, true) } // // // // // // // ContainmentPathNode is a node in a ContainmentPath type ContainmentPathNode[T TrieKeyConstraint[T]] struct { containmentPathNode[T, emptyValue] } // GetKey gets the containing block or matching address corresponding to this node func (node *ContainmentPathNode[T]) GetKey() T { return node.getKey() } // Count returns the count of containing subnets in the path of containing subnets, starting from this node and moving downwards to sub-nodes. // This is a constant-time operation since the size is maintained in each node and adjusted with each add and Remove operation in the sub-tree. func (node *ContainmentPathNode[T]) Count() int { return node.count() } // String returns a visual representation of this node including the address key func (node *ContainmentPathNode[T]) String() string { return node.string() } // ListString returns a visual representation of the containing subnets starting from this node and moving downwards to sub-nodes. func (node *ContainmentPathNode[T]) ListString() string { return node.listString() } // Next gets the node contained by this node func (node *ContainmentPathNode[T]) Next() *ContainmentPathNode[T] { return toContainmentPathNode[T](node.pathNode.Next()) } // Previous gets the node containing this node func (node *ContainmentPathNode[T]) Previous() *ContainmentPathNode[T] { return toContainmentPathNode[T](node.pathNode.Previous()) } func toContainmentPathNode[T TrieKeyConstraint[T]](node *tree.PathNode[trieKey[T], emptyValue]) *ContainmentPathNode[T] { return (*ContainmentPathNode[T])(unsafe.Pointer(node)) } // // // // // // // ContainmentValuesPathNode is a node in a ContainmentPath type ContainmentValuesPathNode[T TrieKeyConstraint[T], V any] struct { containmentPathNode[T, V] } // GetKey gets the containing block or matching address corresponding to this node func (node *ContainmentValuesPathNode[T, V]) GetKey() T { return node.getKey() } // Count returns the count of containing subnets in the path of containing subnets, starting from this node and moving downwards to sub-nodes. // This is a constant-time operation since the size is maintained in each node and adjusted with each add and Remove operation in the sub-tree. func (node *ContainmentValuesPathNode[T, V]) Count() int { return node.count() } // String returns a visual representation of this node including the address key func (node *ContainmentValuesPathNode[T, V]) String() string { return node.string() } // ListString returns a visual representation of the containing subnets starting from this node and moving downwards to sub-nodes. func (node *ContainmentValuesPathNode[T, V]) ListString() string { return node.listString() } // Next gets the node contained by this node func (node *ContainmentValuesPathNode[T, V]) Next() *ContainmentValuesPathNode[T, V] { return toContainmentValuesPathNode[T, V](node.pathNode.Next()) } // Previous gets the node containing this node func (node *ContainmentValuesPathNode[T, V]) Previous() *ContainmentValuesPathNode[T, V] { return toContainmentValuesPathNode[T, V](node.pathNode.Previous()) } // GetValue returns the value assigned to the block or address, if the node was an associative node from an associative trie. // Otherwise, it returns the zero value. func (node *ContainmentValuesPathNode[T, V]) GetValue() V { return node.pathNode.GetValue() } func toContainmentValuesPathNode[T TrieKeyConstraint[T], V any](node *tree.PathNode[trieKey[T], V]) *ContainmentValuesPathNode[T, V] { return (*ContainmentValuesPathNode[T, V])(unsafe.Pointer(node)) } ipaddress-go-1.5.4/ipaddr/addrtype.go000066400000000000000000000033321440250641600175130ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr // addrType tracks which address division and address division groupings can be upscaled to higher-level types type addrType byte const ( zeroType addrType = 0 // no segments ipv4Type addrType = 1 // ipv4 segments ipv6Type addrType = 2 // ipv6 segments ipv6v4MixedType addrType = 3 // ipv6-v4 mixed segments macType addrType = 4 // mac segments ) func (a addrType) isZeroSegments() bool { return a == zeroType } func (a addrType) isIPv4() bool { return a == ipv4Type } func (a addrType) isIPv6() bool { return a == ipv6Type } func (a addrType) isIPv6v4Mixed() bool { return a == ipv6v4MixedType } func (a addrType) isIP() bool { return a.isIPv4() || a.isIPv6() } func (a addrType) isMAC() bool { return a == macType } func (a addrType) getIPNetwork() (network IPAddressNetwork) { if a.isIPv6() { network = ipv6Network } else if a.isIPv4() { network = ipv4Network } return } func (a addrType) getNetwork() (network addressNetwork) { if a.isIPv6() { network = ipv6Network } else if a.isIPv4() { network = ipv4Network } else if a.isMAC() { network = macNetwork } return } ipaddress-go-1.5.4/ipaddr/allocator.go000066400000000000000000000264311440250641600176640ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math" "math/big" "sort" "strings" ) // PrefixBlockConstraint is the generic type constraint used for a prefix block allocator. type PrefixBlockConstraint[T any] interface { SequentialRangeConstraint[T] MergeToPrefixBlocks(...T) []T PrefixBlockIterator() Iterator[T] } var ( _ = PrefixBlockAllocator[*IPAddress]{} _ = PrefixBlockAllocator[*IPv4Address]{} _ = PrefixBlockAllocator[*IPv6Address]{} ) // PrefixBlockAllocator allocates blocks of the desired size from a set of seed blocks provided to it previously for allocation. // // The generic type T can be *IPAddress, *IPv4Address or *IPv6Address. // // Once a prefix block allocator of generic type *IPAddress has been provided with either an IPv4 or IPv6 address or subnet for allocation, // it can only be used with the same address version from that point onwards. In other words, it can allocate either IPv4 or IPv6 blocks, but not both. // // The zero value of a PrefixBlockAllocator is an allocator ready for use. type PrefixBlockAllocator[T PrefixBlockConstraint[T]] struct { version IPVersion blocks [][]T reservedCount, totalBlockCount int } // GetBlockCount returns the count of available blocks in this allocator. func (alloc *PrefixBlockAllocator[T]) GetBlockCount() int { return alloc.totalBlockCount } // GetVersion returns the IP version of the available blocks in the allocator, // which is determined by the version of the first block made available to the allocator. func (alloc *PrefixBlockAllocator[T]) GetVersion() IPVersion { return alloc.version } // GetTotalCount returns the total of the count of all individual addresses available in this allocator, // which is the total number of individual addresses in all the blocks. func (alloc *PrefixBlockAllocator[T]) GetTotalCount() *big.Int { if alloc.GetBlockCount() == 0 { return bigZero() } result := bigZero() version := alloc.version for i := len(alloc.blocks) - 1; i >= 0; i-- { if blockCount := len(alloc.blocks[i]); blockCount != 0 { hostBitCount := HostBitCount(version.GetBitCount() - i) size := hostBitCount.BlockSize() size.Mul(size, big.NewInt(int64(blockCount))) result.Add(result, size) } } return result } // SetReserved sets the additional number of addresses to be included in any size allocation. // Any request for a block of a given size will adjust that size by the given number. // This can be useful when the size requests do not include the count of additional addresses that must be included in every block. // For IPv4, it is common to reserve two addresses, the network and broadcast addresses. // If the reservedCount is negative, then every request will be shrunk by that number, useful for cases where // insufficient space requires that all subnets be reduced in size by an equal number. func (alloc *PrefixBlockAllocator[T]) SetReserved(reservedCount int) { alloc.reservedCount = reservedCount } // GetReserved returns the reserved count. Use SetReserved to change the reserved count. func (alloc *PrefixBlockAllocator[T]) GetReserved() (reservedCount int) { return alloc.reservedCount } // AddAvailable provides the given blocks to the allocator for allocating. func (alloc *PrefixBlockAllocator[T]) AddAvailable(blocks ...T) { if len(blocks) == 0 { return } version := alloc.version for _, block := range blocks { if version.IsIndeterminate() { version = block.GetIPVersion() alloc.version = version } else if !version.Equal(block.GetIPVersion()) { panic(lookupStr("ipaddress.error.ipVersionMismatch")) } } if alloc.blocks == nil { size := alloc.version.GetBitCount() + 1 alloc.blocks = make([][]T, size) } else if alloc.totalBlockCount > 0 { for i, existingBlocks := range alloc.blocks { blocks = append(blocks, existingBlocks...) alloc.blocks[i] = nil } } blocks = blocks[0].MergeToPrefixBlocks(blocks...) alloc.insertBlocks(blocks) } func (alloc *PrefixBlockAllocator[T]) insertBlocks(blocks []T) { for _, block := range blocks { prefLen := block.GetPrefixLen().bitCount() alloc.blocks[prefLen] = append(alloc.blocks[prefLen], block) alloc.totalBlockCount++ } } // GetAvailable returns a list of all the blocks available for allocating in the allocator. func (alloc *PrefixBlockAllocator[T]) GetAvailable() (blocks []T) { for _, block := range alloc.blocks { blocks = append(blocks, block...) } return } // AllocateSize returns a block of sufficient size, // the size indicating the number of distinct addresses required in the block. // AllocateSize returns nil if no such block is available in the allocator, // or if the size required is zero. // The returned block will be able to accommodate sizeRequired hosts as well as the reserved count, if any. func (alloc *PrefixBlockAllocator[T]) AllocateSize(sizeRequired uint64) T { var bitsRequired HostBitCount if alloc.reservedCount < 0 { adjustment := uint64(-alloc.reservedCount) if adjustment >= sizeRequired { var t T return t } sizeRequired -= adjustment bitsRequired = *BitsForCount(sizeRequired) } else if math.MaxUint64-uint64(alloc.reservedCount) < sizeRequired { // 64 bits holds MaxUint64 + 1 addresses sizeRequired += uint64(alloc.reservedCount) // overflow bitsRequired = *BitsForCount(sizeRequired) + 64 } else { sizeRequired += uint64(alloc.reservedCount) bRequired := BitsForCount(sizeRequired) if bRequired == nil { var t T return t } bitsRequired = *bRequired } return alloc.AllocateBitLen(BitCount(bitsRequired)) } // AllocateSizes returns multiple blocks of sufficient size for the given size required, // or nil if there is insufficient space in the allocator. // The reserved count, if any, will be added to the required sizes. func (alloc *PrefixBlockAllocator[T]) AllocateSizes(blockSizes ...uint64) []AllocatedBlock[T] { sizes := append(make([]uint64, 0, len(blockSizes)), blockSizes...) // sort required subnets by size, largest first sort.Slice(sizes, func(i, j int) bool { return sizes[i] > sizes[j] }) result := make([]AllocatedBlock[T], 0, len(sizes)) for _, blockSize := range sizes { if alloc.reservedCount < 0 && uint64(-alloc.reservedCount) >= blockSize { // size zero continue } allocated := alloc.AllocateSize(blockSize) if allocated.IsMultiple() || bigIsOne(allocated.GetCount()) { // count is non-zero result = append(result, AllocatedBlock[T]{ blockSize: new(big.Int).SetUint64(blockSize), reservedCount: alloc.reservedCount, block: allocated, }) } else { return nil } } return result } // AllocateBitLen allocates a block with the given bit-length, // the bit-length being the number of bits extending beyond the prefix length, // or nil if no such block is available in the allocator. // The reserved count is ignored when allocating by bit-length. func (alloc *PrefixBlockAllocator[T]) AllocateBitLen(bitLength BitCount) T { if alloc.totalBlockCount == 0 { var t T return t // nil } newPrefixBitCount := alloc.version.GetBitCount() - bitLength var block T i := newPrefixBitCount for ; i >= 0; i-- { blockRow := alloc.blocks[i] if len(blockRow) > 0 { block = blockRow[0] var t T blockRow[0] = t // just for GC alloc.blocks[i] = blockRow[1:] alloc.totalBlockCount-- break } } if !block.IsMultiple() || i == newPrefixBitCount { return block } // block is larger than needed, adjust it adjustedBlock := block.SetPrefixLen(newPrefixBitCount) blockIterator := adjustedBlock.PrefixBlockIterator() result := blockIterator.Next() // now we add the remaining from the block iterator back into the list alloc.insertBlocks(newSequRangeUnchecked(blockIterator.Next().GetLower(), block.GetUpper(), true).SpanWithPrefixBlocks()) return result } // AllocateMultiBitLens returns multiple blocks of the given bit-lengths, // or nil if there is insufficient space in the allocator. // The reserved count is ignored when allocating by bit-length. func (alloc *PrefixBlockAllocator[T]) AllocateMultiBitLens(bitLengths ...BitCount) []AllocatedBlock[T] { lengths := append(make([]BitCount, 0, len(bitLengths)), bitLengths...) // sort required subnets by size, largest first sort.Slice(lengths, func(i, j int) bool { return lengths[i] > lengths[j] }) result := make([]AllocatedBlock[T], 0, len(lengths)) for _, bitLength := range lengths { allocated := alloc.AllocateBitLen(bitLength) if allocated.IsMultiple() || bigIsOne(allocated.GetCount()) { hostBitCount := HostBitCount(bitLength) result = append(result, AllocatedBlock[T]{ blockSize: hostBitCount.BlockSize(), block: allocated, }) } else { return nil } } return result } // String returns a string showing the counts of available blocks for each prefix size in the allocator. func (alloc PrefixBlockAllocator[T]) String() string { var builder strings.Builder version := alloc.version hasBlocks := false builder.WriteString("available blocks:\n") for i := len(alloc.blocks) - 1; i >= 0; i-- { if blockCount := len(alloc.blocks[i]); blockCount != 0 { hostBitCount := HostBitCount(version.GetBitCount() - i) size := hostBitCount.BlockSize() builder.WriteString(fmt.Sprint(blockCount)) if blockCount == 1 { builder.WriteString(" block") } else { builder.WriteString(" blocks") } builder.WriteString(" with prefix length ") builder.WriteString(fmt.Sprint(i)) builder.WriteString(" size ") builder.WriteString(fmt.Sprint(size)) builder.WriteString("\n") hasBlocks = true } } if !hasBlocks { builder.WriteString("none\n") } return builder.String() } type ( IPPrefixBlockAllocator = PrefixBlockAllocator[*IPAddress] IPv4PrefixBlockAllocator = PrefixBlockAllocator[*IPv4Address] IPv6PrefixBlockAllocator = PrefixBlockAllocator[*IPv6Address] ) // AllocatedBlock represents a block of addresses allocated for assignment to hosts. type AllocatedBlock[T AddressType] struct { blockSize *big.Int block T reservedCount int } // GetAddress returns the block. func (alloc AllocatedBlock[T]) GetAddress() T { return alloc.block } // GetSize returns the number of hosts for which this block was allocated. func (alloc AllocatedBlock[T]) GetSize() *big.Int { return alloc.blockSize } // GetCount returns the total number of addresses within the block. func (alloc AllocatedBlock[T]) GetCount() *big.Int { return alloc.block.GetCount() } // GetReservedCount returns the number of reserved addresses with the block. func (alloc AllocatedBlock[T]) GetReservedCount() int { return alloc.reservedCount } // String returns a string representation of the allocated block. func (alloc AllocatedBlock[T]) String() string { if alloc.reservedCount > 0 { return fmt.Sprint(alloc.block, " for ", alloc.blockSize, " hosts and ", alloc.reservedCount, " reserved addresses") } return fmt.Sprint(alloc.block, " for ", alloc.blockSize, " hosts") } ipaddress-go-1.5.4/ipaddr/clonearrays.go000066400000000000000000000077731440250641600202360ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr func cloneIPv4Sections(sect *IPv4AddressSection, orig []*IPv4AddressSection) []ExtendedIPSegmentSeries { converter := func(a *IPv4AddressSection) ExtendedIPSegmentSeries { return wrapIPSection(a.ToIP()) } if sect == nil { return cloneTo(orig, converter) } return cloneToExtra(sect, orig, converter) // return types matter with interfaces - https://play.golang.org/p/HZR8FSp42a9 ) } func cloneIPv6Sections(sect *IPv6AddressSection, orig []*IPv6AddressSection) []ExtendedIPSegmentSeries { converter := func(a *IPv6AddressSection) ExtendedIPSegmentSeries { return wrapIPSection(a.ToIP()) } if sect == nil { return cloneTo(orig, converter) } return cloneToExtra(sect, orig, converter) } // returns a slice of addresses that match the same IP version as the given func filterCloneIPAddrs(addr *IPAddress, orig []*IPAddress) []ExtendedIPSegmentSeries { addrType := addr.getAddrType() result := make([]ExtendedIPSegmentSeries, 0, len(orig)+1) result = append(result, wrapIPAddress(addr)) for _, a := range orig { if addrType == a.getAddrType() { result = append(result, a.Wrap()) } } return result } func cloneIPv4Addrs(sect *IPv4Address, orig []*IPv4Address) []ExtendedIPSegmentSeries { converter := func(a *IPv4Address) ExtendedIPSegmentSeries { return wrapIPAddress(a.ToIP()) } if sect == nil { return cloneTo(orig, converter) } return cloneToExtra(sect, orig, converter) } func cloneIPv6Addrs(sect *IPv6Address, orig []*IPv6Address) []ExtendedIPSegmentSeries { converter := func(a *IPv6Address) ExtendedIPSegmentSeries { return wrapIPAddress(a.ToIP()) } if sect == nil { return cloneTo(orig, converter) } return cloneToExtra(sect, orig, converter) } func cloneToIPSections(orig []ExtendedIPSegmentSeries) []*IPAddressSection { return cloneTo(orig, func(a ExtendedIPSegmentSeries) *IPAddressSection { return a.(WrappedIPAddressSection).IPAddressSection }) } func cloneToIPv4Sections(orig []ExtendedIPSegmentSeries) []*IPv4AddressSection { return cloneTo(orig, func(a ExtendedIPSegmentSeries) *IPv4AddressSection { return a.(WrappedIPAddressSection).IPAddressSection.ToIPv4() }) } func cloneToIPv6Sections(orig []ExtendedIPSegmentSeries) []*IPv6AddressSection { return cloneTo(orig, func(a ExtendedIPSegmentSeries) *IPv6AddressSection { return a.(WrappedIPAddressSection).IPAddressSection.ToIPv6() }) } func cloneToIPAddrs(orig []ExtendedIPSegmentSeries) []*IPAddress { return cloneTo(orig, func(a ExtendedIPSegmentSeries) *IPAddress { return a.(WrappedIPAddress).IPAddress }) } func cloneToIPv4Addrs(orig []ExtendedIPSegmentSeries) []*IPv4Address { return cloneTo(orig, func(a ExtendedIPSegmentSeries) *IPv4Address { return a.(WrappedIPAddress).IPAddress.ToIPv4() }) } func cloneToIPv6Addrs(orig []ExtendedIPSegmentSeries) []*IPv6Address { return cloneTo(orig, func(a ExtendedIPSegmentSeries) *IPv6Address { return a.(WrappedIPAddress).IPAddress.ToIPv6() }) } func cloneTo[T any, U any](orig []T, conv func(T) U) []U { result := make([]U, len(orig)) for i := range orig { result[i] = conv(orig[i]) } return result } func cloneToExtra[T any, U any](sect T, orig []T, conv func(T) U) []U { origCount := len(orig) result := make([]U, origCount+1) result[origCount] = conv(sect) for i := range orig { result[i] = conv(orig[i]) } return result } func copyTo[T any, U any](dest []U, orig []T, conv func(T) U) { for i := range orig { if i == len(dest) { break } dest[i] = conv(orig[i]) } return } ipaddress-go-1.5.4/ipaddr/cmd/000077500000000000000000000000001440250641600161125ustar00rootroot00000000000000ipaddress-go-1.5.4/ipaddr/cmd/main.go000066400000000000000000001216751440250641600174010ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package main import ( "fmt" "math" "net" "reflect" "strings" //"go/ast" "go/doc" "go/parser" "go/token" "os" "github.com/seancfoley/ipaddress-go/ipaddr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) // this is just a test program used for trying out code func main() { zeroipaddressString := ipaddr.IPAddressString{} fmt.Println(zeroipaddressString.GetAddress()) fmt.Println(ipaddr.IPv4Address{}) seg := ipaddr.IPv4AddressSegment{} seg.GetSegmentValue() fmt.Printf("%v\n", seg.GetBitCount()) fmt.Printf("%v\n", seg.GetByteCount()) grouping := ipaddr.IPv4AddressSection{} grouping.GetSegmentCount() builder := addrstrparam.IPAddressStringParamsBuilder{} params := builder.AllowAll(false).ToParams() fmt.Printf("%+v\n", params) //params := ipaddr.ipAddressStringParameters{} ////fmt.Printf("%+v\n", params) //init := ipaddr.IPAddressStringParamsBuilder{} //params2 := init.AllowAll(false).ToParams() //params = *params2 //_ = params ////fmt.Printf("%+v\n", params) i := -1 b := byte(i) fmt.Printf("byte is %+v\n", b) var slc []int fmt.Printf("%+v\n", slc) // expecting [] fmt.Printf("%v\n", slc) // expecting [] fmt.Printf("%v\n", slc) // expecting [] addr := ipaddr.IPv6Address{} fmt.Printf("zero addr is %+v\n", addr) fmt.Printf("zero addr is %+v\n", &addr) addr4 := ipaddr.IPv4Address{} fmt.Printf("zero addr is %+v\n", addr4) addr2 := addr4.ToIP() fmt.Printf("zero addr is %+v\n", addr2) _ = addr2.String() _ = addr2.GetSection() fmt.Printf("zero addr is %+v\n", addr2.String()) //fmt.Printf("%+v\n", &addr2) ipv4Prefixed := addr4.ToPrefixBlockLen(16) fmt.Printf("16 block is %+v\n", ipv4Prefixed) fmt.Printf("lower is %+v\n", ipv4Prefixed.GetLower()) fmt.Printf("upper is %+v\n", ipv4Prefixed.GetUpper()) fmt.Printf("lower is %+v\n", ipv4Prefixed.GetLower()) fmt.Printf("upper is %+v\n", ipv4Prefixed.GetUpper()) _ = addr.GetPrefixCount() // an inherited method addr5 := ipaddr.IPAddress{} // expecting [] fmt.Printf("%+v\n", addr5) addr5Upper := addr5.GetUpper() fmt.Printf("%+v\n", addr5Upper) // expecting [] addr6 := addr5Upper.ToIPv4() fmt.Printf("%+v\n", addr6) // expecting addrSection := ipaddr.AddressSection{} fmt.Printf("%+v\n", addrSection) // expecting [] or ipAddrSection := ipaddr.IPAddressSection{} fmt.Printf("%+v\n", ipAddrSection) // expecting [] or ipv4AddrSection := ipaddr.IPv4AddressSection{} fmt.Printf("%+v\n", ipv4AddrSection) // expecting [] or //addrStr := ipaddr.IPAddressString{} addrStr := ipaddr.NewIPAddressString("1.2.3.4") pAddr := addrStr.GetAddress() fmt.Printf("%+v\n", *pAddr) fmt.Printf("%+v\n", pAddr) //fmt.Printf("All the formats: %v %x %X %o %O %b %d %#x %#o %#b\n", // pAddr, pAddr, pAddr, pAddr, pAddr, pAddr, pAddr, pAddr, pAddr, pAddr) fmt.Printf("All the formats: default %v\nstring %s\nquoted %q\nquoted backtick %#q\nlowercase hex %x\nuppercase hex %X\nlower hex prefixed %#x\nupper hex prefixed %#X\noctal no prefix %o\noctal prefixed %O\noctal 0 prefix %#o\nbinary %b\nbinary prefixed %#b\ndecimal %d\n\n", pAddr, pAddr, pAddr, pAddr, pAddr, pAddr, pAddr, pAddr, pAddr, pAddr, pAddr, pAddr, pAddr, pAddr) //fmt.Printf("All the formats: %v %x %X %o %O %b %d %#x %#o %#b\n", // *pAddr, *pAddr, *pAddr, *pAddr, *pAddr, *pAddr, *pAddr, *pAddr, *pAddr, *pAddr) //fmt.Printf("octal no prefix %o\n", *pAddr) //fmt.Printf("octal prefixed %O\n", *pAddr) //fmt.Printf("octal 0 prefix %#o\n", *pAddr) //fmt.Printf("binary no prefix %b\n", *pAddr) //fmt.Printf("binary prefixed %#b\n", *pAddr) pAddr = addrStr.GetAddress() // test getting it a second time from the cache fmt.Printf("%+v\n", *pAddr) fmt.Printf("%+v\n", pAddr) cidrStr := ipaddr.NewIPAddressString("255.2.0.0/16") cidr := cidrStr.GetAddress() fmt.Printf("All the formats: default %v\nstring %s\nquoted %q\nquoted backtick %#q\nlowercase hex %x\nuppercase hex %X\nlower hex prefixed %#x\nupper hex prefixed %#X\noctal no prefix %o\noctal prefixed %O\noctal 0 prefix %#o\nbinary %b\nbinary prefixed %#b\ndecimal %d\n\n", cidr, cidr, cidr, cidr, cidr, cidr, cidr, cidr, cidr, cidr, cidr, cidr, cidr, cidr) pZeroSec := ipaddr.IPv4AddressSection{} //fmt.Printf("octal no prefix %o\noctal prefixed %O\noctal 0 prefix %#o\ndecimal %d\n\n", // pZeroSec, pZeroSec, pZeroSec, pZeroSec) fmt.Printf("All the formats for zero section: default %v\nstring %s\nquoted %q\nquoted backtick %#q\nlowercase hex %x\nuppercase hex %X\nlower hex prefixed %#x\nupper hex prefixed %#X\noctal no prefix %o\noctal prefixed %O\noctal 0 prefix %#o\nbinary %b\nbinary prefixed %#b\ndecimal %d\n\n", pZeroSec, pZeroSec, pZeroSec, pZeroSec, pZeroSec, pZeroSec, pZeroSec, pZeroSec, pZeroSec, pZeroSec, pZeroSec, pZeroSec, pZeroSec, pZeroSec) addrStr = ipaddr.NewIPAddressString("abc.2.3.4") noAddr, err := addrStr.ToAddress() fmt.Printf("invalid string abc.2.3.4 is %v with err %v\n", noAddr, err) ipv4Prefixed2 := pAddr.ToPrefixBlockLen(19) fmt.Printf("19 block is %+v\n", ipv4Prefixed2) addrStr = ipaddr.NewIPAddressString("a:b:c:d:e:f:a:b") pAddr = addrStr.GetAddress() fmt.Printf("%+v\n", *pAddr) fmt.Printf("%+v\n", pAddr) addrStr = ipaddr.NewIPAddressString("a:b:c:d:e:f:a:b%eth0") pAddr = addrStr.GetAddress() fmt.Printf("%+v\n", *pAddr) fmt.Printf("%+v\n", pAddr) addrStr = ipaddr.NewIPAddressString("a:b:c:d:e:f:1.2.3.4") pAddr = addrStr.GetAddress() fmt.Printf("%+v\n", *pAddr) fmt.Printf("%+v\n", pAddr) ipv4Addr, _ := ipaddr.NewIPv4AddressFromBytes([]byte{1, 0, 1, 0}) fmt.Printf("%+v\n", ipv4Addr) fmt.Printf("%+v\n", *ipv4Addr) ipv4Addr, ipv4Err := ipaddr.NewIPv4AddressFromBytes([]byte{1, 1, 0, 1, 0}) fmt.Printf("%+v %+v\n", ipv4Addr, ipv4Err) ipv6Addr, ipv6Err := ipaddr.NewIPv6AddressFromBytes(net.IP{1, 0, 1, 0, 0xff, 0xa, 0xb, 0xc, 1, 0, 1, 0, 0xff, 0xa, 0xb, 0xc}) fmt.Printf("%+v %+v\n", ipv6Addr, ipv6Err) fmt.Printf("%+v\n", *ipv6Addr) fmt.Printf("All the formats: default %v\nstring %s\nlowercase hex %x\nuppercase hex %X\nlower hex prefixed %#x\nupper hex prefixed %#X\noctal no prefix %o\noctal prefixed %O\noctal 0 prefix %#o\nbinary %b\nbinary prefixed %#b\ndecimal %d\n\n", ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr) //ipv6Addr = nil //fmt.Printf("All the formats: %v %x %X %o %O %b %#x %#o %#b\n", // ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr, ipv6Addr) fmt.Println(ipv6Addr) ipv6Addr.ForEachSegment(func(i int, seg *ipaddr.IPv6AddressSegment) bool { fmt.Printf("visiting %d seg %s\n", i, seg) return false }) base85Str, _ := ipv6Addr.ToBase85String() fmt.Println("Base 85 string is", base85Str, "for", ipv6Addr) ipv4Addr, _ = ipaddr.NewIPv4AddressFromBytes([]byte{1, 0, 1, 0}) fmt.Println() fmt.Println(ipv4Addr) ipv4Addr.ForEachSegment(func(i int, seg *ipaddr.IPv4AddressSegment) bool { fmt.Printf("visiting %d seg %s\n", i, seg) return false }) fmt.Println() fmt.Println(cidr) cidr.ForEachSegment(func(i int, seg *ipaddr.IPAddressSegment) bool { fmt.Printf("visiting %d seg %s\n", i, seg) return false }) fmt.Println() ipv6Prefixed := ipv6Addr.ToPrefixBlockLen(32) fmt.Printf("32 block is %+v\n", ipv6Prefixed) ipv6Prefixed = ipv6Addr.ToPrefixBlockLen(40) fmt.Printf("40 block is %+v\n", ipv6Prefixed) mixedGrouping, _ := ipv6Addr.GetMixedAddressGrouping() fmt.Printf("mixed grouping of %v is %v and again %s\n", ipv6Addr, mixedGrouping.String(), mixedGrouping) mixedGrouping, _ = ipv6Prefixed.GetMixedAddressGrouping() fmt.Printf("mixed grouping of 40 block %v is %v and again %s\n", ipv6Prefixed, mixedGrouping.String(), mixedGrouping) addrDown := ipv6Prefixed.ToAddressBase() fmt.Printf("addr down converted 40 block is %+v\n", addrDown) addrUp := addrDown.ToIPv6() fmt.Printf("addr up converted 40 block is %+v\n", addrUp) addrUpNil := addrDown.ToIPv4() fmt.Printf("addr up converted nil is %+v\n", addrUpNil) ht := ipaddr.NewHostName("bla.com") fmt.Printf("%v\n", ht.ToNormalizedString()) fmt.Printf("%v\n", ht.GetHost()) //ip := net.IP{1, 0, 1, 0, 0xff, 0xa, 0xb, 0xc, 1, 0, 1, 0, 0xff, 0xa, 0xb, 0xc} //foo(ip) //foo2(ip) //foo3(net.IPAddr{IP: ip}) //bytes := []byte{1, 0, 1, 0, 0xff, 0xa, 0xb, 0xc, 1, 0, 1, 0, 0xff, 0xa, 0xb, 0xc} //foo(bytes) //foo2(bytes) //foo3(net.IPAddr{IP: bytes}) fmt.Printf("iterate a segment:\n") iter := addrUp.GetSegment(ipaddr.IPv6SegmentCount - 1).PrefixedBlockIterator(5) for iter.HasNext() { fmt.Printf("%v ", iter.Next()) } fmt.Printf("\niterate another segment:\n") iter = addrUp.GetSegment(ipaddr.IPv6SegmentCount - 1).PrefixedBlockIterator(0) for iter.HasNext() { fmt.Printf("%v ", iter.Next()) } addrStrPref := ipaddr.NewIPAddressString("1.2-11.0.0/15") pAddr = addrStrPref.GetAddress() newIter := pAddr.GetSection().PrefixBlockIterator() fmt.Println() fmt.Printf("to iterate: %+v", pAddr) fmt.Println("iterate prefix blocks (prefix len 15):") for newIter.HasNext() { fmt.Printf("%v ", newIter.Next()) } addrStrPref = ipaddr.NewIPAddressString("1.2-11.0.0/16") pAddr = addrStrPref.GetAddress() fmt.Println() fmt.Printf("to iterate: %+v", pAddr) newIter = pAddr.GetSection().BlockIterator(2) fmt.Println("iterate a section's first two blocks:") for newIter.HasNext() { fmt.Printf("%v ", newIter.Next()) } newIter = pAddr.GetSection().SequentialBlockIterator() fmt.Printf("\nsequential block iterator:\n") for newIter.HasNext() { fmt.Printf("%v ", newIter.Next()) } addrStrPref1 := ipaddr.NewIPAddressString("1.2.3.4") addrStrPref2 := ipaddr.NewIPAddressString("1.2.4.1") rng := addrStrPref1.GetAddress().ToIPv4().SpanWithRange(addrStrPref2.GetAddress().ToIPv4()) riter := rng.Iterator() fmt.Printf("\nsequential range iterator:\n") for riter.HasNext() { fmt.Printf("%v ", riter.Next()) } riter = rng.PrefixBlockIterator(28) fmt.Printf("\nsequential range pref block iterator:\n") for riter.HasNext() { fmt.Printf("%v ", riter.Next()) } sect := addrStrPref1.GetAddress().ToIPv4().GetSection() str := sect.ToCanonicalString() fmt.Printf("\nString is %s", str) addrStrPref6 := ipaddr.NewIPAddressString("1.2.3.4/16") sect = addrStrPref6.GetAddress().ToIPv4().GetSection() str = sect.ToCanonicalString() fmt.Printf("\nString with prefix length is %s", str) ipv4Addr = addrStrPref6.GetAddress().ToIPv4() str, _ = ipv4Addr.ToInetAtonJoinedString(ipaddr.Inet_aton_radix_hex, 2) fmt.Printf("\nInet Aton string with prefix length is %s", str) str, _ = ipv4Addr.ToInetAtonJoinedString(ipaddr.Inet_aton_radix_hex, 1) fmt.Printf("\nInet Aton string with prefix length is %s", str) str, _ = ipv4Addr.ToInetAtonJoinedString(ipaddr.Inet_aton_radix_hex, 0) fmt.Printf("\nInet Aton string with prefix length is %s", str) addrStrPref7 := ipaddr.NewIPAddressString("1:2:3:4::/64") ipv6Sect := addrStrPref7.GetAddress().ToIPv6().GetSection() str = ipv6Sect.ToCanonicalString() fmt.Printf("\nIPv6 string with prefix length is %s", str) str, _ = addrStrPref7.GetAddress().ToIPv6().ToMixedString() fmt.Printf("\nIPv6 mixed string with prefix length is %s", str) str, _ = addrStrPref7.GetAddress().ToBinaryString(true) fmt.Printf("\nIPv6 binary string is %s", str) str = addrStrPref7.GetAddress().ToSegmentedBinaryString() fmt.Printf("\nIPv6 segmented binary string is %s", str) addrStrPref8 := ipaddr.NewIPAddressString("1::4:5:6:7:8fff/64") ipv6Sect = addrStrPref8.GetAddress().ToIPv6().GetSection() str = ipv6Sect.ToCanonicalString() fmt.Printf("\nIPv6 string with prefix length is %s", str) str, _ = addrStrPref8.GetAddress().ToIPv6().ToMixedString() fmt.Printf("\nIPv6 mixed string with prefix length is %s", str) rangiter := rng.PrefixIterator(28) fmt.Printf("\nsequential range pref iterator:\n") for rangiter.HasNext() { fmt.Printf("%v ", rangiter.Next()) } addrStrIPv6Pref1 := ipaddr.NewIPAddressString("1:2:3:4::") addrStrIPv6Pref2 := ipaddr.NewIPAddressString("1:2:4:1::") rng2 := addrStrIPv6Pref1.GetAddress().ToIPv6().SpanWithRange(addrStrIPv6Pref2.GetAddress().ToIPv6()) rangeres := rng.Join(rng) fmt.Printf("\n\njoined ranges: %v\n", rangeres) rangeres2 := rng.ToIP().Join(rng2.ToIP()) fmt.Printf("\n\njoined ranges: %v\n", rangeres2) rangeres3 := rng2.Join(rng2) fmt.Printf("\n\njoined ranges: %v\n", rangeres3) rangeres4 := rng2.ToIP().Join(rng.ToIP()) fmt.Printf("\n\njoined ranges: %v\n", rangeres4) addrStrPref3 := ipaddr.NewIPAddressString("1-4::1/125") addrIter := addrStrPref3.GetAddress().PrefixBlockIterator() fmt.Printf("\naddress pref block iterator:\n") for addrIter.HasNext() { fmt.Printf("%v ", addrIter.Next()) } addrStrPref4 := ipaddr.NewIPAddressString("1::1/125") addrIter = addrStrPref4.GetAddress().Iterator() fmt.Printf("\naddress iterator:\n") for addrIter.HasNext() { fmt.Printf("%v ", addrIter.Next()) } addrStrPref5 := ipaddr.NewIPAddressString("1::/125") addrIter = addrStrPref5.GetAddress().Iterator() fmt.Printf("\naddress iterator:\n") for addrIter.HasNext() { fmt.Printf("%v ", addrIter.Next()) } macStrPref1 := ipaddr.NewMACAddressString("1:2:3:4:5:6") mAddr := macStrPref1.GetAddress() fmt.Printf("\nmac addr is %+v\n", mAddr) macStrPref1 = ipaddr.NewMACAddressString("1:2:3:4:5:*") mAddr = macStrPref1.GetAddress() fmt.Printf("\nmac addr is %+v\n", mAddr) mAddrIter := mAddr.Iterator() fmt.Printf("\nmac address iterator:\n") for mAddrIter.HasNext() { fmt.Printf("%v ", mAddrIter.Next()) } fmt.Printf("\nincremented by 1 mac addr %+v is %+v\n", mAddr, mAddr.Increment(1)) fmt.Printf("\nincremented by -1 mac addr %+v is %+v\n", mAddr, mAddr.Increment(-1)) fmt.Printf("\nincremented by -1 and then by +1 mac addr %+v is %+v\n", mAddr, mAddr.Increment(-1).Increment(1)) fmt.Printf("\nincremented by +1 and then by -1 mac addr %+v is %+v\n", mAddr, mAddr.Increment(1).Increment(-1)) splitIntoBlocks("0.0.0.0", "0.0.0.254") splitIntoBlocks("0.0.0.1", "0.0.0.254") splitIntoBlocks("0.0.0.0", "0.0.0.254") // 16 8 4 2 1 splitIntoBlocks("0.0.0.10", "0.0.0.21") splitIntoBlocks("1.2.3.4", "1.2.3.3-5") splitIntoBlocks("1.2-3.4.5-6", "2.0.0.0") splitIntoBlocks("1.2.3.4", "1.2.4.4") // 16 8 4 2 1 splitIntoBlocks("0.0.0.0", "255.0.0.0") fmt.Printf("\n\n") splitIntoBlocksSeq("0.0.0.0", "0.0.0.254") splitIntoBlocksSeq("0.0.0.1", "0.0.0.254") splitIntoBlocksSeq("0.0.0.0", "0.0.0.254") // 16 8 4 2 1 splitIntoBlocksSeq("0.0.0.10", "0.0.0.21") splitIntoBlocksSeq("1.2.3.4", "1.2.3.3-5") splitIntoBlocksSeq("1.2-3.4.5-6", "2.0.0.0") splitIntoBlocksSeq("1.2-3.4.5-6", "1.3.4.6") splitIntoBlocksSeq("1.2.3.4", "1.2.4.4") // 16 8 4 2 1 splitIntoBlocksSeq("0.0.0.0", "255.0.0.0") ipZero := &ipaddr.IPAddress{} ipZeroAgain := &ipaddr.IPAddress{} merged := ipZero.MergeToPrefixBlocks(ipZeroAgain, ipZero) //mergedOld := ipZero.MergeToPrefixBlocksOld(ipZeroAgain, ipZero) //fmt.Printf("new %v len %d\nold %v len %d", merged, len(merged), mergedOld, len(mergedOld)) fmt.Printf("new %v len %d\n", merged, len(merged)) merged = ipZero.MergeToPrefixBlocks(ipZeroAgain, ipZero, addrStrIPv6Pref1.GetAddress().ToIP()) fmt.Printf("new %v len %d\n", merged, len(merged)) fmt.Printf("%v\n\n", merge("209.152.214.112/30", "209.152.214.116/31", "209.152.214.118/31")) fmt.Printf("%v\n\n", merge("209.152.214.112/30", "209.152.214.116/32", "209.152.214.118/31")) fmt.Printf("%v\n\n", merge("1:2:3:4:8000::/65", "1:2:3:4::/66", "1:2:3:4:4000::/66", "1:2:3:5:4000::/66", "1:2:3:5::/66", "1:2:3:5:8000::/65")) delim := "1:2,3,4:3:6:4:5,6fff,7,8,99:6:8" delims := ipaddr.DelimitedAddressString(delim).ParseDelimitedSegments() delimCount := ipaddr.DelimitedAddressString(delim).CountDelimitedAddresses() i = 0 for delims.HasNext() { i++ fmt.Printf("%d of %d is %v, from %v\n", i, delimCount, delims.Next(), delim) } fmt.Println() delim = "1:3:6:4:5,6fff,7,8,99:6:2,3,4:8" delims = ipaddr.DelimitedAddressString(delim).ParseDelimitedSegments() delimCount = ipaddr.DelimitedAddressString(delim).CountDelimitedAddresses() //delims = ipaddr.ParseDelimitedSegments(delim) //delimCount = ipaddr.CountDelimitedAddresses(delim) i = 0 for delims.HasNext() { i++ fmt.Printf("%d of %d is %v, from %v\n", i, delimCount, delims.Next(), delim) } //bitsPerSegment := 8 //prefBits := 7 //maxVal := ^ipaddr.DivInt(0) //mask := ^(maxVal << (bitsPerSegment - prefBits)) //masker := ipaddr.TestMaskRange(0, 4, mask, maxVal) //fmt.Printf("masked vals 0 to 4 masked with %v (should be 0 to 1): %v %v\n", mask, masker.GetMaskedLower(0, mask), masker.GetMaskedUpper(4, mask)) // //prefBits = 4 //mask = ^(maxVal << (bitsPerSegment - prefBits)) //masker = ipaddr.TestMaskRange(17, 32, mask, maxVal) //fmt.Printf("masked vals 17 to 32 masked with %v (should be 0 to 15): %v %v\n", mask, masker.GetMaskedLower(17, mask), masker.GetMaskedUpper(32, mask)) // //masker = ipaddr.TestMaskRange(16, 32, mask, maxVal) //fmt.Printf("masked vals 16 to 32 masked with %v (should be 0 to 15): %v %v\n", mask, masker.GetMaskedLower(16, mask), masker.GetMaskedUpper(32, mask)) // iterate on nil - just checking what happens. it panics, not surprisingly. //var niladdr *ipaddr.IPAddress //itr := niladdr.Iterator() //for itr.hasNext() { // fmt.Printf("%v ", itr.Next()) //} s := ipaddr.IPv4AddressSegment{} res := s.PrefixContains(&s, 6) fmt.Printf("Zero seg pref contains %v\n", res) // check is we need to "override" methods like ToHexString str, _ = ipaddr.NewIPv4Segment(3).ToHexString(true) fmt.Println("leading zeros? Hope not: " + str) str, _ = (&ipaddr.IPv4AddressSegment{}).ToHexString(true) fmt.Println("leading zeros? Hope not: " + str) // check is we need to "override" methods like ToNormalizedString str = ipaddr.NewIPv4Segment(3).ToNormalizedString() fmt.Println("leading zeros? Hope not: " + str) str = (&ipaddr.IPv4AddressSegment{}).ToNormalizedString() fmt.Println("leading zeros? Hope not: " + str) sega := ipaddr.NewIPv4Segment(128) segb := ipaddr.NewIPv4Segment(127) seg1 := ipaddr.NewIPv4Segment(3) seg2 := ipaddr.NewIPv4Segment(0) seg3 := &ipaddr.IPv4AddressSegment{} fmt.Printf("compare values: 1? %v nil? %v nil? %v 0? %v 0? %v nil? %v 1? %v 6? %v 8? %v 8? %v\n", sega.GetBlockMaskPrefixLen(true), // should be 1 segb.GetBlockMaskPrefixLen(true), // should be nil seg1.GetBlockMaskPrefixLen(true), // should be nil seg2.GetBlockMaskPrefixLen(true), // should be 0 - either 0 or nil seg3.GetBlockMaskPrefixLen(true), // should be 0 - either 0 or nil sega.GetBlockMaskPrefixLen(false), // should be nil segb.GetBlockMaskPrefixLen(false), // should be 1 seg1.GetBlockMaskPrefixLen(false), // should be 6 seg2.GetBlockMaskPrefixLen(false), // should be 8 - either 8 or nil seg3.GetBlockMaskPrefixLen(false), // should be 8 - either 8 or nil ) ToPrefixLen := func(i ipaddr.PrefixBitCount) ipaddr.PrefixLen { return &i } p1 := ToPrefixLen(1) p2 := ToPrefixLen(2) fmt.Printf("%v %v\n", p1, p2) *p1 = *p2 fmt.Printf("%v %v\n", p1, p2) p1 = ToPrefixLen(1) p2 = ToPrefixLen(2) fmt.Printf("%v %v\n", p1, p2) ToPort := func(i ipaddr.PortNum) ipaddr.Port { return &i } pr1 := ToPort(3) pr2 := ToPort(4) fmt.Printf("%p %p %v %v\n", pr1, pr2, pr1, pr2) *pr1 = *pr2 fmt.Printf("%p %p %v %v\n", pr1, pr2, pr1, pr2) pr1 = ToPort(3) pr2 = ToPort(4) fmt.Printf("%v %v\n", pr1, pr2) fmt.Printf("\n\n") // _ = getDoc() bn := NewAddressTrieNode() _ = bn addrStr = ipaddr.NewIPAddressString("1.2.0.0/32") pAddr = addrStr.GetAddress() fmt.Printf("bit count pref len is pref block: %t\n", pAddr.IsPrefixBlock()) trie := NewIPv4AddressTrie() addrStr = ipaddr.NewIPAddressString("1.2.0.0/16") trie.Add(pAddr.ToIPv4()) addrStr = ipaddr.NewIPAddressString("1.2.3.4") pAddr = addrStr.GetAddress() fmt.Printf("no pref len is pref block: %t\n", pAddr.IsPrefixBlock()) trie.Add(pAddr.ToIPv4()) str = trie.String() fmt.Printf("%s", str) fmt.Printf("trie default: %v", trie) fmt.Printf("decimal: %d\n", trie) fmt.Printf("hex: %#x\n", trie) fmt.Printf("node default: %v\n", *trie.GetRoot()) fmt.Printf("node decimal: %d\n", *trie.GetRoot()) fmt.Printf("node hex: %#x\n", *trie.GetRoot()) trie2 := ipaddr.IPv4AddressTrie{} fmt.Println(ipaddr.TreesString[*ipaddr.IPv4Address](true, &trie, &trie2, &trie)) fmt.Println("zero trie\n", trie2) var ptraddr *ipaddr.IPv4Address fmt.Printf("nil addr %s\n", ptraddr) var trie3 *ipaddr.IPv4AddressTrie fmt.Printf("nil trie %s\n", trie3) fmt.Println("nil trie\n", trie3) fmt.Println(ipaddr.TreesString(true, &trie, &trie2, &trie, trie3, &trie)) trie = ipaddr.IPv4AddressTrie{} fmt.Printf("%v %d %d %t %t", trie, trie.Size(), trie.NodeSize(), trie.BlockSizeAllNodeIterator(true).HasNext(), trie.ContainedFirstAllNodeIterator(true).HasNext()) //fmt.Printf("%v %d %d %t %v", // trie, // trie.Size(), // trie.NodeSize(), // trie.BlockSizeAllNodeIterator(true).hasNext(), // trie.BlockSizeAllNodeIterator(true).Next()) fmt.Printf("%v %d %d %v %v", trie, trie.Size(), trie.NodeSize(), trie.BlockSizeAllNodeIterator(true).Next(), trie.ContainedFirstAllNodeIterator(true).Next()) testers := []string{ "1.2.3.4", "1.2.*.*", "1.2.*.0/24", "1.2.*.4", "1.2.0-1.*", "1.2.1-2.*", "1.2.252-255.*", "1.2.3.4/16", } fmt.Println() fmt.Println() fmt.Println("AssignPrefixForSingleBlock") for _, t := range testers { addr := ipaddr.NewIPAddressString(t).GetAddress() fmt.Printf("%s\n", addr.AssignPrefixForSingleBlock()) } fmt.Println() fmt.Println("AssignMinPrefixForBlock") for _, t := range testers { addr := ipaddr.NewIPAddressString(t).GetAddress() fmt.Printf("%s\n", addr.AssignMinPrefixForBlock()) } p4 := ToPrefixLen(4) segp := ipaddr.NewIPv4PrefixedSegment(1, p4) segp2 := ipaddr.NewIPv4Segment(2) p12 := ToPrefixLen(12) newSec := ipaddr.NewIPv4PrefixedSection([]*ipaddr.IPv4AddressSegment{segp, segp2, segp2, segp2}, p12) fmt.Println("the section is", newSec) // should be 1.2.2.2/4 sgs := newSec.GetSegments() fmt.Println("the segs are", sgs) sg := sgs[0] fmt.Println("the first seg is", sg, "with prefix", sg.GetSegmentPrefixLen()) sg = sgs[1] fmt.Println("the second seg is", sg, "with prefix", sg.GetSegmentPrefixLen()) sg = sgs[2] fmt.Println("the third seg is", sg, "with prefix", sg.GetSegmentPrefixLen()) newSec = ipaddr.NewIPv4PrefixedSection([]*ipaddr.IPv4AddressSegment{segp2, segp2, segp2, segp2}, p12) fmt.Println("the section is", newSec) // should be 1.2.2.2/12 sgs = newSec.GetSegments() fmt.Println("the segs are", sgs) sg = sgs[0] fmt.Println("the first seg is", sg, "with prefix", sg.GetSegmentPrefixLen()) sg = sgs[1] fmt.Println("the second seg is", sg, "with prefix", sg.GetSegmentPrefixLen()) sg = sgs[2] fmt.Println("the third seg is", sg, "with prefix", sg.GetSegmentPrefixLen()) fmt.Printf("decimal IPv4 address: %d\n", pAddr) fmt.Printf("decimal IPv4 address: %d\n", ipaddr.NewIPAddressString("255.255.255.255").GetAddress()) fmt.Printf("decimal IPv6 address: %d\n", ipv6Addr) fmt.Printf("decimal IPv6 address: %d\n", ipaddr.NewIPAddressString("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").GetAddress()) allocator := ipaddr.IPPrefixBlockAllocator{} fmt.Println(allocator) allocator.AddAvailable(ipaddr.NewIPAddressString("192.168.10.0/24").GetAddress()) fmt.Println(allocator) allocator.SetReserved(2) blocks := allocator.AllocateSizes(50, 30, 20, 2, 2, 2) fmt.Println("allocated blocks are:", blocks) fmt.Println(allocator) allocator = ipaddr.IPPrefixBlockAllocator{} fmt.Println(allocator) allocator.AddAvailable(ipaddr.NewIPAddressString("192.168.10.0/24").GetAddress()) fmt.Println(allocator) allocator.SetReserved(2) blocks = allocator.AllocateSizes(60, 12, 12, 28) fmt.Println("allocated blocks are:", blocks) fmt.Println(allocator) //ipaddr.NewIPAddressString("1.2.3.16/28") //almostBlockStr := "1.2.3.17-31/28" xxxx // when switching from /30 to /28, we get 1.2.3.0/28 1.2.3.16/28 1.2.3.32/28 1.2.3.48/28 almostBlockStr := "1.2.3.16-48/28" fmt.Println("Splitting " + almostBlockStr + " to range then back to iterator") almostBlock := ipaddr.NewIPAddressString(almostBlockStr).GetAddress() almostBlockRng := almostBlock.ToSequentialRange() fmt.Println("Range is " + almostBlockRng.String()) fmt.Println("Range lower is " + almostBlockRng.GetLower().ToSegmentedBinaryString()) fmt.Println("Range upper is " + almostBlockRng.GetUpper().ToSegmentedBinaryString()) almostBlockIterRng := almostBlockRng.PrefixBlockIterator(almostBlockRng.GetMinPrefixLenForBlock()) for almostBlockIterRng.HasNext() { fmt.Println(almostBlockIterRng.Next()) } // Same as above, but instead of starting from "1.2.3.16-48/28", starts from "1.2.3.0/26" fmt.Println("and again") block := ipaddr.NewIPAddressString("1.2.3.0/26").GetAddress().SetPrefixLen(28) fmt.Println("count of prefixes of " + block.String() + " is " + block.GetPrefixCount().String()) almostBlockIter := block.PrefixBlockIterator() almostBlockIter.Next() almostBlockRng = almostBlockIter.Next().GetLower().SpanWithRange(block.GetUpper()) fmt.Println("Range is " + almostBlockRng.String()) almostBlockIterRng = almostBlockRng.PrefixBlockIterator(almostBlockRng.GetMinPrefixLenForBlock()) for almostBlockIterRng.HasNext() { fmt.Println(almostBlockIterRng.Next()) } // the above shows we can take an iterator, the 1.2.3.16/28 prefix block iterator, peel off the first, then convert to sequential range, // then from the sequential range recover that iterator using almostBlockRng.PrefixBlockIterator(almostBlockRng.GetMinPrefixLenForBlock()) // but we can get large blocks instead, by spanning again: fmt.Println(almostBlockRng.SpanWithPrefixBlocks()) // Let's try this with IPv6 originalBlock := ipaddr.NewIPAddressString("::/64").GetAddress() shrinkIt := originalBlock.SetPrefixLen(126) shrinkIter := shrinkIt.PrefixBlockIterator() shrinkIter.Next() low := shrinkIter.Next().GetLower() up := originalBlock.GetUpper() shrunkRange := low.SpanWithRange(up) fmt.Println("low " + low.String() + " to " + up.String() + " size " + shrunkRange.GetCount().String()) fmt.Println(shrunkRange.SpanWithPrefixBlocks()) //fmt.Println(almostBlockRng.SpanWithSequentialBlocks()) alloc := ipaddr.IPPrefixBlockAllocator{} fmt.Println(alloc) alloc.AddAvailable(ipaddr.NewIPAddressString("192.168.10.0/24").GetAddress()) fmt.Println(alloc) alloc.SetReserved(2) blocks = alloc.AllocateSizes(50, 30, 20, 2, 2, 2) fmt.Println("allocated blocks are:", blocks) fmt.Println(alloc) // put em back and see what happens for _, allocated := range blocks { alloc.AddAvailable(allocated.GetAddress()) //fmt.Println(alloc) } fmt.Println(alloc) myaddr := ipaddr.IPAddress{} addr1Lower := myaddr.GetLower() fmt.Println("one is " + addr1Lower.String()) //fmt.Println("one to bytes is ", addr1Lower.Bytes()) naddr := addr1Lower.GetNetIPAddr() fmt.Println("one to ipaddr is " + naddr.String()) faddr, _ := ipaddr.NewIPAddressFromNetIPAddr(naddr) fmt.Println("and back is " + faddr.String()) //log2() addedTree := ipaddr.AddedTree[*ipaddr.IPv4Address]{} fmt.Println("\nzero tree is " + addedTree.String()) fmt.Println("root is " + addedTree.GetRoot().String()) fmt.Println("root key is " + addedTree.GetRoot().GetKey().String()) fmt.Println("root subnodes are ", addedTree.GetRoot().GetSubNodes()) fmt.Println("root tree string is " + addedTree.GetRoot().TreeString()) addedTreeNode := ipaddr.AddedTreeNode[*ipaddr.IPv4Address]{} fmt.Println("node is " + addedTreeNode.String()) fmt.Println("node key is " + addedTreeNode.GetKey().String()) fmt.Println("node subnodes are ", addedTreeNode.GetSubNodes()) fmt.Println("node tree string is " + addedTreeNode.TreeString()) assocAddedTree := ipaddr.AssociativeAddedTree[*ipaddr.IPv4Address, int]{} fmt.Println("\nassoc zero tree is " + assocAddedTree.String()) fmt.Println("root is " + assocAddedTree.GetRoot().String()) fmt.Println("root key is " + assocAddedTree.GetRoot().GetKey().String()) fmt.Println("root value is ", assocAddedTree.GetRoot().GetValue()) fmt.Println("root subnodes are ", assocAddedTree.GetRoot().GetSubNodes()) fmt.Println("root tree string is " + assocAddedTree.GetRoot().TreeString()) assocAddedTreeNode := ipaddr.AssociativeAddedTreeNode[*ipaddr.IPAddress, float64]{} fmt.Println("assoc node is " + assocAddedTreeNode.String()) fmt.Println("assoc node key is " + assocAddedTreeNode.GetKey().String()) fmt.Println("assoc node value is ", assocAddedTreeNode.GetValue()) fmt.Println("assoc node subnodes are ", assocAddedTreeNode.GetSubNodes()) fmt.Println("assoc node tree string is " + assocAddedTreeNode.TreeString()) fmt.Println() zeros() } func splitIntoBlocks(one, two string) { blocks := split(one, two) fmt.Printf("%v from splitting %v and %v: %v\n", len(blocks), one, two, blocks) } func splitIntoBlocksSeq(one, two string) { blocks := splitSeq(one, two) fmt.Printf("%v from splitting %v and %v: %v\n", len(blocks), one, two, blocks) } func split(oneStr, twoStr string) []*ipaddr.IPv4Address { one := ipaddr.NewIPAddressString(oneStr) two := ipaddr.NewIPAddressString(twoStr) return one.GetAddress().ToIPv4().SpanWithPrefixBlocksTo(two.GetAddress().ToIPv4()) } func splitSeq(oneStr, twoStr string) []*ipaddr.IPv4Address { one := ipaddr.NewIPAddressString(oneStr) two := ipaddr.NewIPAddressString(twoStr) return one.GetAddress().ToIPv4().SpanWithSequentialBlocksTo(two.GetAddress().ToIPv4()) } /* 8 from splitting 0.0.0.0 and 0.0.0.254: [0.0.0.0/25, 0.0.0.128/26, 0.0.0.192/27, 0.0.0.224/28, 0.0.0.240/29, 0.0.0.248/30, 0.0.0.252/31, 0.0.0.254/32] 14 from splitting 0.0.0.1 and 0.0.0.254: [0.0.0.1/32, 0.0.0.2/31, 0.0.0.4/30, 0.0.0.8/29, 0.0.0.16/28, 0.0.0.32/27, 0.0.0.64/26, 0.0.0.128/26, 0.0.0.192/27, 0.0.0.224/28, 0.0.0.240/29, 0.0.0.248/30, 0.0.0.252/31, 0.0.0.254/32] 8 from splitting 0.0.0.0 and 0.0.0.254: [0.0.0.0/25, 0.0.0.128/26, 0.0.0.192/27, 0.0.0.224/28, 0.0.0.240/29, 0.0.0.248/30, 0.0.0.252/31, 0.0.0.254/32] 4 from splitting 0.0.0.10 and 0.0.0.21: [0.0.0.10/31, 0.0.0.12/30, 0.0.0.16/30, 0.0.0.20/31] 1 from splitting 1.2.3.4 and 1.2.3.3-5: [1.2.3.3-5] 4 from splitting 1.2-3.4.5-6 and 2.0.0.0: [1.2.4.5-255, 1.2.5-255.*, 1.3-255.*.*, 2.0.0.0] 2 from splitting 1.2.3.4 and 1.2.4.4: [1.2.3.4-255, 1.2.4.0-4] 2 from splitting 0.0.0.0 and 255.0.0.0: [0-254.*.*.*, 255.0.0.0] */ func merge(strs ...string) []*ipaddr.IPAddress { first := ipaddr.NewIPAddressString(strs[0]).GetAddress() var remaining = make([]*ipaddr.IPAddress, len(strs)) for i := range strs { remaining[i] = ipaddr.NewIPAddressString(strs[i]).GetAddress() } return first.MergeToPrefixBlocks(remaining...) } //func foo(bytes []byte) { // fmt.Printf("%v\n", bytes) //} //func foo2(bytes net.IP) { // fmt.Printf("%v\n", bytes) //} //func foo3(bytes net.IPAddr) { // fmt.Printf("%v\n", bytes) //} // go install golang.org/x/tools/cmd/godoc // cd /Users/scfoley@us.ibm.com/goworkspace/bin // ./godoc -http=localhost:6060 // http://localhost:6060/pkg/github.com/seancfoley/ipaddress/ipaddress-go/ipaddr/ // src/golang.org/x/tools/godoc/static/ has the templates, specifically godoc.html // godoc cheat sheet //https://godoc.org/github.com/fluhus/godoc-tricks#Links // gdb tips https://gist.github.com/danisfermi/17d6c0078a2fd4c6ee818c954d2de13c func getDoc() error { // Create the AST by parsing src. fset := token.NewFileSet() // positions are relative to fset pkgs, err := parser.ParseDir( fset, //"/Users/scfoley@us.ibm.com/goworkspace/src/github.com/seancfoley/ipaddress/ipaddress-go/ipaddr", "/Users/scfoley/go/src/github.com/seancfoley/ipaddress/ipaddress-go/ipaddr", func(f os.FileInfo) bool { return true }, parser.ParseComments) if err != nil { fmt.Printf("%s", err.Error()) return err //panic(err) } for keystr, valuePkg := range pkgs { pkage := doc.New(valuePkg, keystr, 0) //pkage := doc.New(valuePkg, keystr, doc.AllMethods) //pkage := doc.New(valuePkg, keystr, doc.AllDecls) //fmt.Printf("\n%+v", pkage) // Print the AST. // ast.Print(fset, pkage) for _, t := range pkage.Types { fmt.Printf("\n%s", t.Name) for _, m := range t.Methods { //fmt.Printf("bool %v", doc.AllMethods&doc.AllMethods != 0) //https: //golang.org/src/go/doc/doc.go //https://golang.org/src/go/doc/reader.go sortedTypes sortedFuncs show how they are filtered fmt.Printf("\n%+v", m) } } } return nil } var faillog2, failceillog2, faillogbx, faililogbx, failBitsFor, ilogbShift, total int func log2() { bitsFor := func(x uint64, expected uint64) { total++ fmt.Printf("trying %x, want %d\n", x, expected) res := math.Log2(float64(x)) if uint64(res) != expected { faillog2++ } fmt.Println("log2", res) res = math.Ceil(math.Log2(float64(x))) if uint64(res) != expected { failceillog2++ } fmt.Println("ceil log2", res) //fmt.Println("logb", math.Logb(float64(x))) //fmt.Println("ilogb", math.Ilogb(float64(x))) res = math.Logb(float64(2*x - 1)) if uint64(res) != expected { faillogbx++ } fmt.Println("logb x * 2 - 1", res) resi := math.Ilogb(float64(2*x - 1)) if uint64(resi) != expected { faililogbx++ } fmt.Println("ilogb x * 2 - 1", resi) //fmt.Println("ceil logb x * 2 - 1", math.Ceil(math.Logb(float64(2*x-1)))) // https://janmr.com/blog/2010/09/computing-the-integer-binary-logarithm/ // OR combo of that with ILogb // subtract 1 then shift, then we have 1 which needs to add 1 to result //I think I need to add 1 back limit := uint(53) const mask = 0xfff0000000000000 BitsFor := func(x uint64) (result int) { if ((x - 1) & mask) != 0 { // conversion to float64 will fail x = ((x - 1) >> limit) + 1 result = int(limit) } result += math.Ilogb(float64((x << 1) - 1)) return } resi = BitsFor(x) if uint64(resi) != expected { failBitsFor++ } fmt.Println("BitsFor", resi) //maintissa is 52 bits I think //var extra int //y := (x - 1) >> 52 //if y != 0 { // equivalent to x > (1 << 52) or (x - 1) & 0x fff0000000000000 != 0 // fmt.Println("in extra block") // x = ((x - 1) >> 52) + 1 // extra += 52 //} var extra int if ((x - 1) & mask) != 0 { // equivalent to x > (1 << 52) or (x - 1) & 0xfffffffffffff != 0 //fmt.Println("in extra block") x = ((x - 1) >> limit) + 1 extra += int(limit) } resi = extra + math.Ilogb(float64((x<<1)-1)) if uint64(resi) != expected { ilogbShift++ } //fmt.Println("ilogb with shift", result+math.Ilogb(float64(2*x-1))) fmt.Println("ilogb with shift", resi) fmt.Println() } // x bits holds 2 power x values, the largest being 2 power x - 1 bitsFor(1, 0) bitsFor(2, 1) bitsFor(4, 2) bitsFor(5, 3) bitsFor(6, 3) bitsFor(7, 3) bitsFor(8, 3) bitsFor(9, 4) bitsFor(0x4, 2) bitsFor(0x5, 3) bitsFor(0x8, 3) bitsFor(0x9, 4) bitsFor(0x10, 4) bitsFor(0x10+1, 5) bitsFor(0x100, 8) bitsFor(0x100+1, 9) bitsFor(0x1000000000000, 48) bitsFor(0x1000000000000+1, 49) bitsFor(0x4000000000000, 50) bitsFor(0x4000000000000+1, 51) bitsFor(0x8000000000000-1, 51) bitsFor(0x8000000000000, 51) bitsFor(0x8000000000000+1, 52) bitsFor(0x10000000000000-1, 52) bitsFor(0x10000000000000, 52) bitsFor(0x10000000000000+1, 53) bitsFor(0x20000000000000-1, 53) bitsFor(0x20000000000000, 53) bitsFor(0x20000000000000+1, 54) bitsFor(0x40000000000000-1, 54) bitsFor(0x40000000000000, 54) bitsFor(0x40000000000000+1, 55) bitsFor(0x100000000000000, 56) bitsFor(0x100000000000000+1, 57) bitsFor(0x1000000000000000, 60) bitsFor(0x1000000000000000+1, 61) bitsFor(0x8000000000000000, 63) bitsFor(0x8000000000000000+1, 64) bitsFor(0x8000000000000000+2, 64) bitsFor(0x10000000000000000-1, 64) fmt.Printf("fail counts %d %d %d %d %d %d total:%d\n", faillog2, failceillog2, faillogbx, faililogbx, failBitsFor, ilogbShift, total) //fmt.Printf("%x\n", uint64(float64(0x10000000000000))) //fmt.Printf("%x\n", uint64(float64(0x10000000000001))) //fmt.Printf("10000000000000\n") //fmt.Printf("10000000000001\n\n") //fmt.Printf("%x\n", uint64(float64(0x100000000000000))) //fmt.Printf("%x\n", uint64(float64(0x100000000000001))) //fmt.Printf("100000000000000\n") //fmt.Printf("100000000000001\n\n") //fmt.Printf("%x\n", uint64(float64(0x1000000000000000))) //fmt.Printf("%x\n", uint64(float64(0x1000000000000001))) //fmt.Printf("1000000000000000\n") //fmt.Printf("1000000000000001\n\n") //fmt.Printf("%x\n", uint64(float64(0x800000000000000))) //fmt.Printf("%x\n", uint64(float64(0x800000000000001))) //fmt.Printf("800000000000000\n") //fmt.Printf("800000000000001\n\n") //fmt.Printf("%x\n", uint64(float64(0x400000000000000))) //fmt.Printf("%x\n", uint64(float64(0x400000000000001))) //fmt.Printf("400000000000000\n") //fmt.Printf("400000000000001\n\n") //fmt.Printf("%x\n", uint64(float64(0x200000000000000))) //fmt.Printf("%x\n", uint64(float64(0x200000000000001))) //fmt.Printf("200000000000000\n") //fmt.Printf("200000000000001\n\n") x := -1 fmt.Println(uint64(x)) fmt.Println(uint64(x - 1)) var y uint64 = 0xffffffffffffffff var z uint = 2 fmt.Println(y + uint64(z)) } func NewIPv4AddressTrie() ipaddr.IPv4AddressTrie { return ipaddr.IPv4AddressTrie{} } func NewAddressTrieNode() ipaddr.TrieNode[*ipaddr.Address] { return ipaddr.TrieNode[*ipaddr.Address]{} } func zeros() { strip := func(s string) string { return strings.ReplaceAll(strings.ReplaceAll(s, "ipaddr.", ""), "github.com/seancfoley/ipaddress-go/", "") } typeName := func(i any) string { return strip(reflect.ValueOf(i).Elem().Type().Name()) } interfaceTypeName := func(i any) string { return strip(reflect.TypeOf(i).String()) } truncateIndent := func(s, indent string) string { if boundary := len(indent) - (len(s) >> 3); boundary >= 0 { return indent[:boundary] + "\t" // every 8 chars eliminates a tab } return "" } baseIndent := "\t\t\t" title := "Address item zero values" fmt.Printf("%s%sint\tbits\tcount\tstring\n", title, truncateIndent(title, baseIndent)) vars := []ipaddr.AddressItem{ &ipaddr.Address{}, &ipaddr.IPAddress{}, &ipaddr.IPv4Address{}, &ipaddr.IPv6Address{}, &ipaddr.MACAddress{}, &ipaddr.AddressSection{}, &ipaddr.IPAddressSection{}, &ipaddr.IPv4AddressSection{}, &ipaddr.IPv6AddressSection{}, &ipaddr.MACAddressSection{}, &ipaddr.EmbeddedIPv6AddressSection{}, &ipaddr.AddressDivisionGrouping{}, &ipaddr.IPAddressLargeDivisionGrouping{}, &ipaddr.IPv6v4MixedAddressGrouping{}, &ipaddr.AddressSegment{}, &ipaddr.IPAddressSegment{}, &ipaddr.IPv4AddressSegment{}, &ipaddr.IPv6AddressSegment{}, &ipaddr.MACAddressSegment{}, &ipaddr.AddressDivision{}, &ipaddr.IPAddressLargeDivision{}, &ipaddr.IPAddressSeqRange{}, &ipaddr.IPv4AddressSeqRange{}, &ipaddr.IPv6AddressSeqRange{}, } for _, v := range vars { name := typeName(v) + "{}" indent := truncateIndent(name, baseIndent) fmt.Printf("%s%s%v\t%v\t%v\t\"%v\"\n", name, indent, v.GetValue(), v.GetBitCount(), v.GetCount(), v) } title = "Address item nil pointers" fmt.Printf("\n%s%scount\tstring\n", title, truncateIndent(title, baseIndent+"\t\t")) nilPtrItems := []ipaddr.AddressItem{ (*ipaddr.Address)(nil), (*ipaddr.IPAddress)(nil), (*ipaddr.IPv4Address)(nil), (*ipaddr.IPv6Address)(nil), (*ipaddr.MACAddress)(nil), (*ipaddr.AddressSection)(nil), (*ipaddr.IPAddressSection)(nil), (*ipaddr.IPv4AddressSection)(nil), (*ipaddr.IPv6AddressSection)(nil), (*ipaddr.MACAddressSection)(nil), (*ipaddr.AddressSegment)(nil), (*ipaddr.IPAddressSegment)(nil), (*ipaddr.IPv4AddressSegment)(nil), (*ipaddr.IPv6AddressSegment)(nil), (*ipaddr.MACAddressSegment)(nil), (*ipaddr.IPAddressSeqRange)(nil), (*ipaddr.IPv4AddressSeqRange)(nil), (*ipaddr.IPv6AddressSeqRange)(nil), } for _, v := range nilPtrItems { name := "(" + interfaceTypeName(v) + ")(nil)" indent := truncateIndent(name, baseIndent+"\t\t") fmt.Printf("%s%s%v\t\"%v\"\n", name, indent, v.GetCount(), v) } title = "Address key zero values" fmt.Printf("\n%s%sstring\n", title, truncateIndent(title, baseIndent+"\t\t\t")) keys := []fmt.Stringer{ &ipaddr.AddressKey{}, &ipaddr.IPAddressKey{}, &ipaddr.IPv4AddressKey{}, &ipaddr.IPv6AddressKey{}, &ipaddr.MACAddressKey{}, &ipaddr.IPAddressSeqRangeKey{}, &ipaddr.IPv4AddressSeqRangeKey{}, &ipaddr.IPv6AddressSeqRangeKey{}, } for _, k := range keys { name := typeName(k) + "{}" indent := truncateIndent(name, baseIndent+"\t\t\t") fmt.Printf("%s%s\"%v\"\n", name, indent, k) } title = "Host id zero values" fmt.Printf("\n%s%sstring\n", title, truncateIndent(title, baseIndent+"\t\t\t")) hostids := []ipaddr.HostIdentifierString{ &ipaddr.HostName{}, &ipaddr.IPAddressString{}, &ipaddr.MACAddressString{}, } for _, k := range hostids { name := typeName(k) + "{}" indent := truncateIndent(name, baseIndent+"\t\t\t") fmt.Printf("%s%s\"%v\"\n", name, indent, k) } title = "Host id nil pointers" fmt.Printf("\n%s%sstring\n", title, truncateIndent(title, baseIndent+"\t\t\t")) nilPtrIds := []ipaddr.HostIdentifierString{ (*ipaddr.HostName)(nil), (*ipaddr.IPAddressString)(nil), (*ipaddr.MACAddressString)(nil), } for _, v := range nilPtrIds { name := "(" + interfaceTypeName(v) + ")(nil)" indent := truncateIndent(name, baseIndent+"\t\t\t") fmt.Printf("%s%s\"%v\"\n", name, indent, v) } } ipaddress-go-1.5.4/ipaddr/cmd/test.go000066400000000000000000000014721440250641600174240ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package main import ( "flag" "github.com/seancfoley/ipaddress-go/ipaddr/test" ) func main() { isLimitedPtr := flag.Bool("limited", false, "exclude caching and threading tests") flag.Parse() test.Test(*isLimitedPtr) } ipaddress-go-1.5.4/ipaddr/compare.go000066400000000000000000001110731440250641600173270ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import "math/big" var ( // CountComparator compares by count first, then by value. CountComparator = AddressComparator{countComparator{}} // HighValueComparator compares by high value first, then low, then count. HighValueComparator = AddressComparator{valueComparator{compareHighValue: true}} // LowValueComparator compares by low value first, then high, then count. LowValueComparator = AddressComparator{valueComparator{}} // With the reverse comparators, ordering with the secondary values (higher or lower) follow a reverse ordering than the primary values (lower or higher) // ReverseHighValueComparator is like HighValueComparator but when comparing the low value, reverses the comparison. ReverseHighValueComparator = AddressComparator{valueComparator{compareHighValue: true, flipSecond: true}} // ReverseLowValueComparator is like LowValueComparator but when comparing the high value, reverses the comparison. ReverseLowValueComparator = AddressComparator{valueComparator{flipSecond: true}} ) type componentComparator interface { compareSectionParts(one, two *AddressSection) int compareParts(one, two AddressDivisionSeries) int compareSegValues(oneUpper, oneLower, twoUpper, twoLower SegInt) int compareValues(oneUpper, oneLower, twoUpper, twoLower uint64) int compareLargeValues(oneUpper, oneLower, twoUpper, twoLower *big.Int) int } type groupingType int const ( ipv6sectype groupingType = 7 ipv4sectype groupingType = 6 ipsectype groupingType = 5 macsectype groupingType = 4 sectype groupingType = 3 ipv6v4groupingtype groupingType = 2 largegroupingtype groupingType = -2 standardgroupingtype groupingType = -3 adaptivezerotype groupingType = -4 unknowntype groupingType = -5 ) type divType int const ( ipv6segtype divType = 6 ipv4segtype divType = 5 ipsegtype divType = 4 macsegtype divType = 3 segtype divType = 1 standarddivtype divType = 0 largedivtype divType = -2 unknowndivtype divType = -3 ) type rangeType int const ( ipv6rangetype rangeType = 2 ipv4rangetype rangeType = 1 iprangetype rangeType = 0 unknownrangetype rangeType = -1 ) func checkSegmentType(div AddressSegmentType) (isNil bool, divType divType) { if isNil = div == nil; isNil { divType = unknowndivtype } else { seg := div.ToSegmentBase() if isNil = seg == nil; !isNil { if seg.IsIPv6() { divType = ipv6segtype } else if seg.IsIPv4() { divType = ipv4segtype } else if seg.IsMAC() { divType = macsegtype } else if seg.IsIP() { divType = ipsegtype } else { divType = segtype } } else { divType = unknowndivtype } } return } func checkDivisionType(genericDiv DivisionType) (isNil, isStandard bool, divType divType, standardDiv StandardDivisionType) { // Note: https://go.dev/play/p/4cHtDqDxpjp shows the behaviour or type-checking if standardDiv, isStandard = genericDiv.(StandardDivisionType); isStandard { div := standardDiv.ToDiv() if isNil = div == nil; !isNil { if div.IsIPv6() { divType = ipv6segtype } else if div.IsIPv4() { divType = ipv4segtype } else if div.IsMAC() { divType = macsegtype } else if div.IsIP() { divType = ipsegtype } else if div.IsSegmentBase() { divType = segtype } else { divType = standarddivtype } } else { divType = unknowndivtype } } else if largeDiv, isLarge := genericDiv.(*IPAddressLargeDivision); isLarge { if isNil = largeDiv.isNil(); !isNil { divType = largedivtype } } else { isNil = genericDiv == nil // it could still have some external type, so not a nil interface but a nil value with that type, but we have no way to know divType = unknowndivtype } return } func checkSectionType(sect AddressSectionType) (isNil bool, groupingType groupingType) { if isNil = sect == nil; isNil { groupingType = unknowntype } else { section := sect.ToSectionBase() if isNil = section == nil; !isNil { if section.IsAdaptiveZero() { // The zero grouping can represent a zero-length section of any address type. // This is necessary because sections and groupings have no init() method to ensure zero-sections are always assigned an address type. // We would need the zero grouping to be less than everything else or more than everything else for comparison consistency. // Empty sections or groupings that have an address type are not considered equal. They can represent only one address type. // This is similar to the fact that a MAC section and an IPv4 section can be structurally identical but not equal due to the type. // // See IsAdaptiveZero() method for more details. groupingType = adaptivezerotype } else if section.IsIPv6() { groupingType = ipv6sectype } else if section.IsIPv4() { groupingType = ipv4sectype } else if section.IsMAC() { groupingType = macsectype } else if section.IsIP() { groupingType = ipsectype } else { groupingType = sectype } } else { groupingType = unknowntype } } return } func checkGroupingType(series AddressDivisionSeries) ( isNil bool, groupingType groupingType) { // Note: https://go.dev/play/p/4cHtDqDxpjp shows the behaviour or type-checking if sgrouping, isStandard := series.(StandardDivGroupingType); isStandard { group := sgrouping.ToDivGrouping() if isNil = group == nil; !isNil { if group.IsAdaptiveZero() { // The zero grouping can represent a zero-length section of any address type. // This is necessary because sections and groupings have no init() method to ensure zero-sections are always assigned an address type. // We would need the zero grouping to be less than everything else or more than everything else for comparison consistency. // Empty sections or groupings that have an address type are not considered equal. They can represent only one address type. // This is similar to the fact that a MAC section and an IPv4 section can be structurally identical but not equal due to the type. // // See IsAdaptiveZero() method for more details. groupingType = adaptivezerotype } else if group.IsIPv6() { groupingType = ipv6sectype } else if group.IsMixedIPv6v4() { groupingType = ipv6v4groupingtype } else if group.IsIPv4() { groupingType = ipv4sectype } else if group.IsMAC() { groupingType = macsectype } else if group.IsIP() { // Currently the ipsectype result is impossible, because a zero IP section has type adaptivezerotype, // while a non-zero IP section can only be ipv6sectype or ipv4sectype groupingType = ipsectype } else if group.isAddressSection() { // Currently the sectype result is impossible, because a zero section has type adaptivezerotype, // while a non-zero IP section can only be ipv6sectype or ipv4sectype groupingType = sectype } else { groupingType = standardgroupingtype } } else { groupingType = unknowntype } } else if lgrouping, isLarge := series.(*IPAddressLargeDivisionGrouping); isLarge { if isNil = lgrouping.isNil(); !isNil { groupingType = largegroupingtype } else { groupingType = unknowntype } } else { isNil = series == nil // it could still have some external type, so not a nil interface but a nil value with that type, but we have no way to know groupingType = unknowntype } return } func checkRangeTypeX(r IPAddressSeqRangeType) (isNil bool, rngType rangeType, rng *SequentialRange[*IPAddress]) { if isNil = r == nil; isNil { rngType = unknownrangetype } else { rng = r.ToIP() if isNil = rng == nil; !isNil { version := r.GetIPVersion() if version.IsIPv4() { rngType = ipv4rangetype } else if version.IsIPv6() { rngType = ipv6rangetype } else { rngType = iprangetype } } else { rngType = unknownrangetype } } return } // AddressComparator has methods to compare addresses, or sections, or division series, or segments, or divisions, or sequential ranges. // AddressComparator also allows you to compare any two instances of any such address items, using the Compare method. // The zero value acts like CountComparator, the default comparator. type AddressComparator struct { componentComparator componentComparator } // CompareAddresses compares any two addresses (including different versions or address types) // It returns a negative integer, zero, or a positive integer if address item one is less than, equal, or greater than address item two. func (comp AddressComparator) CompareAddresses(one, two AddressType) int { if one == nil || one.ToAddressBase() == nil { if two == nil || two.ToAddressBase() == nil { return 0 } return -1 } else if two == nil || two.ToAddressBase() == nil { return 1 } oneAddr := one.ToAddressBase() twoAddr := two.ToAddressBase() result := comp.CompareAddressSections(oneAddr.GetSection(), twoAddr.GetSection()) if result == 0 { if oneIPv6 := oneAddr.ToIPv6(); oneIPv6 != nil { twoIPv6 := twoAddr.ToIPv6() oneZone := oneIPv6.zone twoZone := twoIPv6.zone if oneZone == twoZone { return 0 } else if oneZone < twoZone { return -1 } return 1 } } return result } // CompareAddressSections compares any two address sections (including from different versions or address types). // It returns a negative integer, zero, or a positive integer if address item one is less than, equal, or greater than address item two. func (comp AddressComparator) CompareAddressSections(one, two AddressSectionType) int { oneIsNil, oneGroupingType := checkSectionType(one) twoIsNil, twoGroupingType := checkSectionType(two) if oneIsNil { if twoIsNil { return 0 } return -1 } else if twoIsNil { return 1 } else if result := oneGroupingType - twoGroupingType; result != 0 { return int(result) } else if result := int(one.GetBitCount() - two.GetBitCount()); result != 0 { return result } return comp.getCompComp().compareSectionParts(one.ToSectionBase(), two.ToSectionBase()) } func (comp AddressComparator) getCompComp() componentComparator { compComp := comp.componentComparator if compComp == nil { return countComparator{} } return compComp } func unwrapWrapper(item AddressDivisionSeries) AddressDivisionSeries { if wrapper, ok := item.(ExtendedIPSegmentSeries); ok { return wrapper.Unwrap() } return item } // CompareSeries compares any two address division series (including from different versions or address types). // It returns a negative integer, zero, or a positive integer if address item one is less than, equal, or greater than address item two. func (comp AddressComparator) CompareSeries(one, two AddressDivisionSeries) int { one = unwrapWrapper(one) two = unwrapWrapper(two) if addrSeries1, ok := one.(AddressType); ok { if addrSeries2, ok := two.(AddressType); ok { return comp.CompareAddresses(addrSeries1, addrSeries2) } return 1 } else if _, ok := two.(AddressType); ok { return -1 } // at this point they must be both groupings if not nil if addrSection1, ok := one.(AddressSectionType); ok { if addrSection2, ok := two.(AddressSectionType); ok { return comp.CompareAddressSections(addrSection1, addrSection2) } } oneIsNil, oneGroupingType := checkGroupingType(one) twoIsNil, twoGroupingType := checkGroupingType(two) // All nils are equivalent. We decided that a nil interface should be equivalent to an interface with a nil value (standard or large) // But if nil interface == nil standard, and nil interface == nil large, then nil standard == nil large, by transitive condition. // And in fact, if you attempted to categorize the 3 nil types, it would get quite confusing perhaps. if oneIsNil { if twoIsNil { return 0 } return -1 } else if twoIsNil { return 1 } else if result := oneGroupingType - twoGroupingType; result != 0 { return int(result) } else if result := int(one.GetBitCount() - two.GetBitCount()); result != 0 { return result } return comp.getCompComp().compareParts(one, two) } // CompareSegments compares any two address segments (including from different versions or address types). // It returns a negative integer, zero, or a positive integer if address item one is less than, equal, or greater than address item two. func (comp AddressComparator) CompareSegments(one, two AddressSegmentType) int { oneIsNil, oneDivType := checkSegmentType(one) twoIsNil, twoDivType := checkSegmentType(two) // All nils are equivalent. We decided that a nil interface should be equivalent to an interface with a nil value (standard or large) // But if nil interface == nil standard, and nil interface == nil large, then nil standard == nil large, by transitive condition. // And in fact, if you attempted to categorize the 3 nil types, it would get quite confusing perhaps. if oneIsNil { if twoIsNil { return 0 } return -1 } else if twoIsNil { return 1 } else if result := oneDivType - twoDivType; result != 0 { return int(result) } else if result := int(one.GetBitCount() - two.GetBitCount()); result != 0 { return result } oneSeg := one.ToSegmentBase() twoSeg := two.ToSegmentBase() return comp.getCompComp().compareSegValues(oneSeg.GetUpperSegmentValue(), oneSeg.GetSegmentValue(), twoSeg.GetUpperSegmentValue(), twoSeg.GetSegmentValue()) } // CompareDivisions compares any two address divisions (including from different versions or address types). // It returns a negative integer, zero, or a positive integer if address item one is less than, equal, or greater than address item two. func (comp AddressComparator) CompareDivisions(one, two DivisionType) int { if addrSeg1, ok := one.(AddressSegmentType); ok { if addrSeg2, ok := two.(AddressSegmentType); ok { return comp.CompareSegments(addrSeg1, addrSeg2) } } oneIsNil, oneIsStandard, oneDivType, oneStandardDiv := checkDivisionType(one) twoIsNil, twoIsStandard, twoDivType, twoStandardDiv := checkDivisionType(two) // All nils are equivalent. We decided that a nil interface should be equivalent to an interface with a nil value (standard or large) // But if nil interface == nil standard, and nil interface == nil large, then nil standard == nil large, by transitive condition. // And in fact, if you attempted to categorize the 3 nil types, it would get quite confusing perhaps. if oneIsNil { if twoIsNil { return 0 } return -1 } else if twoIsNil { return 1 } else if result := oneDivType - twoDivType; result != 0 { return int(result) } else if result := int(one.GetBitCount() - two.GetBitCount()); result != 0 { return result } compComp := comp.getCompComp() if oneIsStandard { if twoIsStandard { div1, div2 := oneStandardDiv.ToDiv(), twoStandardDiv.ToDiv() return compComp.compareValues(div1.GetUpperDivisionValue(), div1.GetDivisionValue(), div2.GetUpperDivisionValue(), div2.GetDivisionValue()) } } return compComp.compareLargeValues(one.GetUpperValue(), one.GetValue(), two.GetUpperValue(), two.GetValue()) } // CompareRanges compares any two IP address sequential ranges (including from different IP versions). // It returns a negative integer, zero, or a positive integer if address item one is less than, equal, or greater than address item two. func (comp AddressComparator) CompareRanges(one, two IPAddressSeqRangeType) int { oneIsNil, r1Type, r1 := checkRangeTypeX(one) twoIsNil, r2Type, r2 := checkRangeTypeX(two) if oneIsNil { if twoIsNil { return 0 } return -1 } else if twoIsNil { return 1 } result := r1Type - r2Type if result != 0 { return int(result) } compComp := comp.getCompComp() if r1Type == ipv4rangetype { // avoid using the large values r1ipv4 := r1.ToIPv4() r2ipv4 := r2.ToIPv4() return compComp.compareValues(uint64(r1ipv4.GetUpper().Uint32Value()), uint64(r1ipv4.GetLower().Uint32Value()), uint64(r2ipv4.GetUpper().Uint32Value()), uint64(r2ipv4.GetLower().Uint32Value())) } return compComp.compareLargeValues(one.GetUpperValue(), one.GetValue(), two.GetUpperValue(), two.GetValue()) } // Compare returns a negative integer, zero, or a positive integer if address item one is less than, equal, or greater than address item two. // Any address item is comparable to any other. func (comp AddressComparator) Compare(one, two AddressItem) int { if one == nil { if two == nil { return 0 } return -1 } else if two == nil { return 1 } if divSeries1, ok := one.(AddressDivisionSeries); ok { if divSeries2, ok := two.(AddressDivisionSeries); ok { return comp.CompareSeries(divSeries1, divSeries2) } else { return 1 } } else if div1, ok := one.(DivisionType); ok { if div2, ok := two.(DivisionType); ok { return comp.CompareDivisions(div1, div2) } else { return -1 } } else if rng1, ok := one.(IPAddressSeqRangeType); ok { if rng2, ok := two.(IPAddressSeqRangeType); ok { return comp.CompareRanges(rng1, rng2) } else if _, ok := two.(AddressDivisionSeries); ok { return -1 } return 1 } // we've covered all known address items for 'one', so check 'two' if _, ok := two.(AddressDivisionSeries); ok { return -1 } else if _, ok := two.(DivisionType); ok { return 1 //} else if _, ok := two.(IPAddressSeqRangeType); ok { // return -1 } else if _, ok := two.(IPAddressSeqRangeType); ok { return -1 } // neither are a known AddressItem type res := int(one.GetBitCount() - two.GetBitCount()) if res == 0 { return res } return comp.getCompComp().compareLargeValues(one.GetUpperValue(), one.GetValue(), two.GetUpperValue(), two.GetValue()) } type valueComparator struct { compareHighValue, flipSecond bool } func (comp valueComparator) compareSectionParts(one, two *AddressSection) int { compareHigh := comp.compareHighValue for { segCount := one.GetSegmentCount() for i := 0; i < segCount; i++ { segOne := one.GetSegment(i) segTwo := two.GetSegment(i) var s1, s2 SegInt if compareHigh { s1 = segOne.GetUpperSegmentValue() s2 = segTwo.GetUpperSegmentValue() } else { s1 = segOne.GetSegmentValue() s2 = segTwo.GetSegmentValue() } if s1 != s2 { var result int if s1 > s2 { result = 1 } else { result = -1 } if comp.flipSecond && compareHigh != comp.compareHighValue { result = -result } return result } } compareHigh = !compareHigh if compareHigh == comp.compareHighValue { break } } return 0 } func (comp valueComparator) compareParts(oneSeries, twoSeries AddressDivisionSeries) int { sizeResult := int(oneSeries.GetBitCount() - twoSeries.GetBitCount()) if sizeResult != 0 { return sizeResult } result := compareDivBitCounts(oneSeries, twoSeries) if result != 0 { return result } compareHigh := comp.compareHighValue var one, two *AddressDivisionGrouping if o, ok := oneSeries.(StandardDivGroupingType); ok { if t, ok := twoSeries.(StandardDivGroupingType); ok { one = o.ToDivGrouping() two = t.ToDivGrouping() } } oneSeriesByteCount := oneSeries.GetByteCount() twoSeriesByteCount := twoSeries.GetByteCount() oneBytes := make([]byte, oneSeriesByteCount) twoBytes := make([]byte, twoSeriesByteCount) for { var oneByteCount, twoByteCount, oneByteIndex, twoByteIndex, oneIndex, twoIndex int var oneBitCount, twoBitCount, oneTotalBitCount, twoTotalBitCount BitCount var oneValue, twoValue uint64 for oneIndex < oneSeries.GetDivisionCount() || twoIndex < twoSeries.GetDivisionCount() { if one != nil { if oneBitCount == 0 { oneCombo := one.GetDivision(oneIndex) oneIndex++ oneBitCount = oneCombo.GetBitCount() if compareHigh { oneValue = oneCombo.GetUpperDivisionValue() } else { oneValue = oneCombo.GetDivisionValue() } } if twoBitCount == 0 { twoCombo := two.GetDivision(twoIndex) twoIndex++ twoBitCount = twoCombo.GetBitCount() if compareHigh { twoValue = twoCombo.GetUpperDivisionValue() } else { twoValue = twoCombo.GetDivisionValue() } } } else { if oneBitCount == 0 { if oneByteCount == 0 { oneCombo := oneSeries.GetGenericDivision(oneIndex) oneIndex++ if compareHigh { oneBytes = oneCombo.CopyUpperBytes(oneBytes) } else { oneBytes = oneCombo.CopyBytes(oneBytes) } oneTotalBitCount = oneCombo.GetBitCount() oneByteCount = oneCombo.GetByteCount() oneByteIndex = 0 } //put some or all of the bytes into a long count := 8 oneValue = 0 if count < oneByteCount { oneBitCount = BitCount(count) << 3 oneTotalBitCount -= oneBitCount oneByteCount -= count for count > 0 { count-- oneValue = (oneValue << 8) | uint64(oneBytes[oneByteIndex]) oneByteIndex++ } } else { shortCount := oneByteCount - 1 lastBitsCount := oneTotalBitCount - (BitCount(shortCount) << 3) for shortCount > 0 { shortCount-- oneValue = (oneValue << 8) | uint64(oneBytes[oneByteIndex]) oneByteIndex++ } oneValue = (oneValue << uint64(lastBitsCount)) | uint64(oneBytes[oneByteIndex]>>uint64(8-lastBitsCount)) oneByteIndex++ oneBitCount = oneTotalBitCount oneTotalBitCount = 0 oneByteCount = 0 } } if twoBitCount == 0 { if twoByteCount == 0 { twoCombo := twoSeries.GetGenericDivision(twoIndex) twoIndex++ if compareHigh { twoBytes = twoCombo.CopyUpperBytes(twoBytes) } else { twoBytes = twoCombo.CopyBytes(twoBytes) } twoTotalBitCount = twoCombo.GetBitCount() twoByteCount = twoCombo.GetByteCount() twoByteIndex = 0 } //put some or all of the bytes into a long count := 8 twoValue = 0 if count < twoByteCount { twoBitCount = BitCount(count) << 3 twoTotalBitCount -= twoBitCount twoByteCount -= count for count > 0 { count-- twoValue = (twoValue << 8) | uint64(twoBytes[twoByteIndex]) twoByteIndex++ } } else { shortCount := twoByteCount - 1 lastBitsCount := twoTotalBitCount - (BitCount(shortCount) << 3) for shortCount > 0 { shortCount-- twoValue = (twoValue << 8) | uint64(twoBytes[twoByteIndex]) twoByteIndex++ } twoValue = (twoValue << uint(lastBitsCount)) | uint64(twoBytes[twoByteIndex]>>uint(8-lastBitsCount)) twoByteIndex++ twoBitCount = twoTotalBitCount twoTotalBitCount = 0 twoByteCount = 0 } } } oneResultValue := oneValue twoResultValue := twoValue if twoBitCount == oneBitCount { //no adjustment required, compare the values straight up oneBitCount = 0 twoBitCount = 0 } else { diffBits := twoBitCount - oneBitCount if diffBits > 0 { twoResultValue >>= uint(diffBits) twoValue &= ^(^uint64(0) << uint(diffBits)) twoBitCount = diffBits oneBitCount = 0 } else { diffBits = -diffBits oneResultValue >>= uint(diffBits) oneValue &= ^(^uint64(0) << uint(diffBits)) oneBitCount = diffBits twoBitCount = 0 } } if oneResultValue != twoResultValue { if comp.flipSecond && compareHigh != comp.compareHighValue { if oneResultValue > twoResultValue { return -1 } return 1 } if oneResultValue > twoResultValue { return 1 } return -1 } } compareHigh = !compareHigh if compareHigh == comp.compareHighValue { break } } return 0 } func (comp valueComparator) compareSegValues(oneUpper, oneLower, twoUpper, twoLower SegInt) int { if comp.compareHighValue { if oneUpper == twoUpper { if oneLower == twoLower { return 0 } else if oneLower > twoLower { if !comp.flipSecond { return 1 } } } else if oneUpper > twoUpper { return 1 } } else { if oneLower == twoLower { if oneUpper == twoUpper { return 0 } else if oneUpper > twoUpper { if !comp.flipSecond { return 1 } } } else if oneLower > twoLower { return 1 } } return -1 } func (comp valueComparator) compareValues(oneUpper, oneLower, twoUpper, twoLower uint64) int { if comp.compareHighValue { if oneUpper == twoUpper { if oneLower == twoLower { return 0 } else if oneLower > twoLower { if !comp.flipSecond { return 1 } } } else if oneUpper > twoUpper { return 1 } } else { if oneLower == twoLower { if oneUpper == twoUpper { return 0 } else if oneUpper > twoUpper { if !comp.flipSecond { return 1 } } } else if oneLower > twoLower { return 1 } } return -1 } func (comp valueComparator) compareLargeValues(oneUpper, oneLower, twoUpper, twoLower *big.Int) int { var result int if comp.compareHighValue { result = oneUpper.CmpAbs(twoUpper) if result == 0 { result = oneLower.CmpAbs(twoLower) if comp.flipSecond { result = -result } } } else { result = oneLower.CmpAbs(twoLower) if result == 0 { result = oneUpper.CmpAbs(twoUpper) if comp.flipSecond { result = -result } } } return result } type countComparator struct{} func (comp countComparator) compareSectionParts(one, two *AddressSection) int { //result := int(one.GetBitCount() - two.GetBitCount()) //if result == 0 { //result := compareSectionCount(one, two) result := compareCount(one, two) if result == 0 { result = comp.compareEqualSizedSections(one, two) } //} return result } func (comp countComparator) compareEqualSizedSections(one, two *AddressSection) int { segCount := one.GetSegmentCount() for i := 0; i < segCount; i++ { segOne := one.GetSegment(i) segTwo := two.GetSegment(i) oneUpper := segOne.GetUpperSegmentValue() twoUpper := segTwo.GetUpperSegmentValue() oneLower := segOne.GetSegmentValue() twoLower := segTwo.GetSegmentValue() result := comp.compareSegValues(oneUpper, oneLower, twoUpper, twoLower) if result != 0 { return result } } return 0 } func (comp countComparator) compareParts(one, two AddressDivisionSeries) int { result := int(one.GetBitCount() - two.GetBitCount()) if result == 0 { result = compareCount(one, two) if result == 0 { result = comp.compareDivisionGroupings(one, two) } } return result } func (comp countComparator) compareDivisionGroupings(oneSeries, twoSeries AddressDivisionSeries) int { var one, two *AddressDivisionGrouping if o, ok := oneSeries.(StandardDivGroupingType); ok { if t, ok := twoSeries.(StandardDivGroupingType); ok { one = o.ToDivGrouping() two = t.ToDivGrouping() } } result := compareDivBitCounts(oneSeries, twoSeries) if result != 0 { return result } oneSeriesByteCount := oneSeries.GetByteCount() twoSeriesByteCount := twoSeries.GetByteCount() oneUpperBytes := make([]byte, oneSeriesByteCount) oneLowerBytes := make([]byte, oneSeriesByteCount) twoUpperBytes := make([]byte, twoSeriesByteCount) twoLowerBytes := make([]byte, twoSeriesByteCount) var oneByteCount, twoByteCount, oneByteIndex, twoByteIndex, oneIndex, twoIndex int var oneBitCount, twoBitCount, oneTotalBitCount, twoTotalBitCount BitCount var oneUpper, oneLower, twoUpper, twoLower uint64 for oneIndex < oneSeries.GetDivisionCount() || twoIndex < twoSeries.GetDivisionCount() { if one != nil { if oneBitCount == 0 { oneCombo := one.getDivision(oneIndex) oneIndex++ oneBitCount = oneCombo.GetBitCount() oneUpper = oneCombo.GetUpperDivisionValue() oneLower = oneCombo.GetDivisionValue() } if twoBitCount == 0 { twoCombo := two.getDivision(twoIndex) twoIndex++ twoBitCount = twoCombo.GetBitCount() twoUpper = twoCombo.GetUpperDivisionValue() twoLower = twoCombo.GetDivisionValue() } } else { if oneBitCount == 0 { if oneByteCount == 0 { oneCombo := oneSeries.GetGenericDivision(oneIndex) oneIndex++ oneUpperBytes = oneCombo.CopyUpperBytes(oneUpperBytes) oneLowerBytes = oneCombo.CopyBytes(oneLowerBytes) oneTotalBitCount = oneCombo.GetBitCount() oneByteCount = oneCombo.GetByteCount() oneByteIndex = 0 } //put some or all of the bytes into a uint64 count := 8 oneUpper = 0 oneLower = 0 if count < oneByteCount { oneBitCount = BitCount(count << 3) oneTotalBitCount -= oneBitCount oneByteCount -= count for count > 0 { count-- upperByte := oneUpperBytes[oneByteIndex] lowerByte := oneLowerBytes[oneByteIndex] oneByteIndex++ oneUpper = (oneUpper << 8) | uint64(upperByte) oneLower = (oneLower << 8) | uint64(lowerByte) } } else { shortCount := oneByteCount - 1 lastBitsCount := oneTotalBitCount - (BitCount(shortCount) << 3) for shortCount > 0 { shortCount-- upperByte := oneUpperBytes[oneByteIndex] lowerByte := oneLowerBytes[oneByteIndex] oneByteIndex++ oneUpper = (oneUpper << 8) | uint64(upperByte) oneLower = (oneLower << 8) | uint64(lowerByte) } upperByte := oneUpperBytes[oneByteIndex] lowerByte := oneLowerBytes[oneByteIndex] oneByteIndex++ oneUpper = (oneUpper << uint(lastBitsCount)) | uint64(upperByte>>uint(8-lastBitsCount)) oneLower = (oneLower << uint(lastBitsCount)) | uint64(lowerByte>>uint(8-lastBitsCount)) oneBitCount = oneTotalBitCount oneTotalBitCount = 0 oneByteCount = 0 } } if twoBitCount == 0 { if twoByteCount == 0 { twoCombo := twoSeries.GetGenericDivision(twoIndex) twoIndex++ twoUpperBytes = twoCombo.CopyUpperBytes(twoUpperBytes) twoLowerBytes = twoCombo.CopyBytes(twoLowerBytes) twoTotalBitCount = twoCombo.GetBitCount() twoByteCount = twoCombo.GetByteCount() twoByteIndex = 0 } //put some or all of the bytes into a long count := 8 twoUpper = 0 twoLower = 0 if count < twoByteCount { twoBitCount = BitCount(count << 3) twoTotalBitCount -= twoBitCount twoByteCount -= count for count > 0 { count-- upperByte := twoUpperBytes[twoByteIndex] lowerByte := twoLowerBytes[twoByteIndex] twoByteIndex++ twoUpper = (twoUpper << 8) | uint64(upperByte) twoLower = (twoLower << 8) | uint64(lowerByte) } } else { shortCount := twoByteCount - 1 lastBitsCount := twoTotalBitCount - (BitCount(shortCount) << 3) for shortCount > 0 { shortCount-- upperByte := twoUpperBytes[twoByteIndex] lowerByte := twoLowerBytes[twoByteIndex] twoByteIndex++ twoUpper = (twoUpper << 8) | uint64(upperByte) twoLower = (twoLower << 8) | uint64(lowerByte) } upperByte := twoUpperBytes[twoByteIndex] lowerByte := twoLowerBytes[twoByteIndex] twoByteIndex++ twoUpper = (twoUpper << uint(lastBitsCount)) | uint64(upperByte>>uint(8-lastBitsCount)) twoLower = (twoLower << uint(lastBitsCount)) | uint64(lowerByte>>uint(8-lastBitsCount)) twoBitCount = twoTotalBitCount twoTotalBitCount = 0 twoByteCount = 0 } } } oneResultUpper := oneUpper oneResultLower := oneLower twoResultUpper := twoUpper twoResultLower := twoLower if twoBitCount == oneBitCount { //no adjustment required, compare the values straight up oneBitCount = 0 twoBitCount = 0 } else { diffBits := twoBitCount - oneBitCount if diffBits > 0 { twoResultUpper >>= uint(diffBits) //look at the high bits only (we are comparing left to right, high to low) twoResultLower >>= uint(diffBits) mask := ^(^uint64(0) << uint(diffBits)) twoUpper &= mask twoLower &= mask twoBitCount = diffBits oneBitCount = 0 } else { diffBits = -diffBits oneResultUpper >>= uint(diffBits) oneResultLower >>= uint(diffBits) mask := ^(^uint64(0) << uint(diffBits)) oneUpper &= mask oneLower &= mask oneBitCount = diffBits twoBitCount = 0 } } result := comp.compareValues(oneResultUpper, oneResultLower, twoResultUpper, twoResultLower) if result != 0 { return result } } return 0 } func (countComparator) compareSegValues(oneUpper, oneLower, twoUpper, twoLower SegInt) int { size1 := oneUpper - oneLower size2 := twoUpper - twoLower if size1 == size2 { //the size of the range is the same, so just compare either upper or lower values if oneLower == twoLower { return 0 } else if oneLower > twoLower { return 1 } } else if size1 > size2 { return 1 } return -1 } func (countComparator) compareValues(oneUpper, oneLower, twoUpper, twoLower uint64) int { size1 := oneUpper - oneLower size2 := twoUpper - twoLower if size1 == size2 { //the size of the range is the same, so just compare either upper or lower values if oneLower == twoLower { return 0 } else if oneLower > twoLower { return 1 } } else if size1 > size2 { return 1 } return -1 } func (countComparator) compareLargeValues(oneUpper, oneLower, twoUpper, twoLower *big.Int) (result int) { oneUpper.Sub(oneUpper, oneLower) twoUpper.Sub(twoUpper, twoLower) result = oneUpper.CmpAbs(twoUpper) if result == 0 { //the size of the range is the same, so just compare either upper or lower values result = oneLower.CmpAbs(twoLower) } return } func compareDivBitCounts(oneSeries, twoSeries AddressDivisionSeries) int { //when this is called we know the two series have the same bit-size, we want to check that the divisions //also have the same bit size (which of course also implies that there are the same number of divisions) count := oneSeries.GetDivisionCount() result := count - twoSeries.GetDivisionCount() if result == 0 { for i := 0; i < count; i++ { result = int(oneSeries.GetGenericDivision(i).GetBitCount() - twoSeries.GetGenericDivision(i).GetBitCount()) if result != 0 { break } } } return result } func isNilItem(item AddressItem) bool { if divSeries, ok := item.(AddressDivisionSeries); ok { if addr, ok := divSeries.(AddressType); ok { return addr.ToAddressBase() == nil } else if grouping, ok := divSeries.(StandardDivGroupingType); ok { return grouping.ToDivGrouping() == nil } else if largeGrouping, ok := divSeries.(*IPAddressLargeDivisionGrouping); ok { return largeGrouping.isNil() } // else a type external to this library, which we cannot test for nil //} else if rng, ok := item.(IPAddressSeqRangeType); ok { // return rng.ToIP() == nil } else if rng, ok := item.(IPAddressSeqRangeType); ok { return rng.ToIP() == nil } else if div, ok := item.(DivisionType); ok { if sdiv, ok := div.(StandardDivisionType); ok { return sdiv.ToDiv() == nil } else if ldiv, ok := div.(*IPAddressLargeDivision); ok { return ldiv.isNil() } // else a type external to this library, which we cannot test for nil } return item == nil } // Note: never called with an address instance, never called with an instance of AddressType func compareCount(one, two AddressItem) int { if !one.IsMultiple() { if two.IsMultiple() { return -1 } return 0 } else if !two.IsMultiple() { return 1 } b1, u1 := getCount(one) b2, u2 := getCount(two) if b1 == nil { if b2 != nil { if b2.IsUint64() { u2 = b2.Uint64() } else { return -1 } } if u1 < u2 { return -1 } else if u1 == u2 { return 0 } return 1 } else if b2 == nil { if b1.IsUint64() { u1 = b1.Uint64() if u1 < u2 { return -1 } else if u1 == u2 { return 0 } } return 1 } return b1.CmpAbs(b2) } // Note: never called with an address instance, never called with an instance of AddressType func getCount(item AddressItem) (b *big.Int, u uint64) { if sect, ok := item.(StandardDivGroupingType); ok { grouping := sect.ToDivGrouping() if grouping != nil { b = grouping.getCachedCount() } //} else if rng, ok := item.(IPAddressSeqRangeType); ok { // //ipRange := rng.ToIP() // //if ipRange != nil { // b = rng.GetCount() // //b = rng.getCachedCount(false) // //} } else if rng, ok := item.(IPAddressSeqRangeType); ok { //ipRange := rng.ToIP() //if ipRange != nil { b = rng.GetCount() //b = rng.getCachedCount(false) //} } else if div, ok := item.(StandardDivisionType); ok { base := div.ToDiv() if base != nil { if segBase := base.ToSegmentBase(); segBase != nil { u = uint64((segBase.getUpperSegmentValue() - base.getSegmentValue()) + 1) } else { r := base.getUpperDivisionValue() - base.getDivisionValue() if r == 0xffffffffffffffff { b = bigZero().SetUint64(0xffffffffffffffff) b.Add(b, bigOneConst()) return } u = r + 1 } } } else if lgrouping, ok := item.(*IPAddressLargeDivisionGrouping); ok { if lgrouping != nil { b = lgrouping.getCachedCount() } } else if ldiv, ok := item.(*IPAddressLargeDivision); ok { if ldiv != nil { b = ldiv.getCount() } } else { b = item.GetCount() } return } ipaddress-go-1.5.4/ipaddr/converter.go000066400000000000000000000100551440250641600177060ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr // IPv6AddressConverter converts IP addresses to IPv6. type IPv6AddressConverter interface { // ToIPv6 converts to IPv6. If the given address is IPv6, or can be converted to IPv6, returns that IPv6Address. Otherwise, returns nil. ToIPv6(address *IPAddress) *IPv6Address } // IPv4AddressConverter converts IP addresses to IPv4. type IPv4AddressConverter interface { // ToIPv4 converts to IPv4. If the given address is IPv4, or can be converted to IPv4, returns that IPv4Address. Otherwise, returns nil. ToIPv4(address *IPAddress) *IPv4Address } // IPAddressConverter converts IP addresses to either IPv4 or IPv6. type IPAddressConverter interface { IPv4AddressConverter IPv6AddressConverter // IsIPv4Convertible returns whether the address is IPv4 or can be converted to IPv4. If true, ToIPv4 returns non-nil. IsIPv4Convertible(address *IPAddress) bool // IsIPv6Convertible returns whether the address is IPv6 or can be converted to IPv6. If true, ToIPv6 returns non-nil. IsIPv6Convertible(address *IPAddress) bool } // DefaultAddressConverter converts to/from IPv4-mapped addresses, which maps IPv4 "a.b.c.d" to/from the IPv4-mapped IPv6 "::ffff:a.b.c.d". // Converting from IPv6 to IPv4 requires that the IPV6 address have the prefix "0:0:0:0:0:ffff". // // Note that with some subnets, the mapping is not possible due to the range of values in segments. // For example, "::ffff:0-100:0" cannot be mapped to an IPv4 address because the range 0-0x100 cannot be split into two smaller ranges. // Similarly, "1-2.0.0.0" cannot be converted to an IPv4-mapped IPv6 address, // because the two segments "1-2.0" cannot be joined into a single IPv6 segment with the same range of values, namely the two values 0x100 and 0x200. type DefaultAddressConverter struct{} var _ IPAddressConverter = DefaultAddressConverter{} // ToIPv4 converts IPv4-mapped IPv6 addresses to IPv4, or returns the original address if IPv4 already, or returns nil if the address cannot be converted. func (converter DefaultAddressConverter) ToIPv4(address *IPAddress) *IPv4Address { if addr := address.ToIPv4(); addr != nil { return addr } else if addr := address.ToIPv6(); addr != nil { if ipv4Addr, err := addr.GetEmbeddedIPv4Address(); err == nil { return ipv4Addr } } return nil } // ToIPv6 converts to an IPv4-mapped IPv6 address or returns the original address if IPv6 already. func (converter DefaultAddressConverter) ToIPv6(address *IPAddress) *IPv6Address { if addr := address.ToIPv6(); addr != nil { return addr } else if addr := address.ToIPv4(); addr != nil { if ipv6Addr, err := addr.GetIPv4MappedAddress(); err == nil { return ipv6Addr } } return nil } // IsIPv4Convertible returns true if ToIPv4 returns non-nil. func (converter DefaultAddressConverter) IsIPv4Convertible(address *IPAddress) bool { if addr := address.ToIPv6(); addr != nil { if addr.IsIPv4Mapped() { if _, _, _, _, err := addr.GetSegment(IPv6SegmentCount - 1).splitSegValues(); err != nil { return false } else if _, _, _, _, err := addr.GetSegment(IPv6SegmentCount - 2).splitSegValues(); err != nil { return false } return true } } return address.IsIPv4() } // IsIPv6Convertible returns true if ToIPv6 returns non-nil. func (converter DefaultAddressConverter) IsIPv6Convertible(address *IPAddress) bool { if addr := address.ToIPv4(); addr != nil { return addr.GetSegment(0).isJoinableTo(addr.GetSegment(1)) && addr.GetSegment(2).isJoinableTo(addr.GetSegment(3)) } return address.IsIPv6() } ipaddress-go-1.5.4/ipaddr/cover.go000066400000000000000000000041121440250641600170120ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import "math/bits" func getCoveringPrefixBlock( first, other ExtendedIPSegmentSeries) ExtendedIPSegmentSeries { result := checkPrefixBlockContainment(first, other) if result != nil { return result } return applyOperatorToLowerUpper(first, other, false, coverWithPrefixBlockWrapped)[0] } func coverWithPrefixBlockWrapped( lower, upper ExtendedIPSegmentSeries) []ExtendedIPSegmentSeries { return []ExtendedIPSegmentSeries{coverWithPrefixBlock(lower, upper)} } func coverWithPrefixBlock( lower, upper ExtendedIPSegmentSeries) ExtendedIPSegmentSeries { segCount := lower.GetSegmentCount() bitsPerSegment := lower.GetBitsPerSegment() var currentSegment int var previousSegmentBits BitCount for ; currentSegment < segCount; currentSegment++ { lowerSeg := lower.GetGenericSegment(currentSegment) upperSeg := upper.GetGenericSegment(currentSegment) var lowerValue, upperValue SegInt lowerValue = lowerSeg.GetSegmentValue() //these are single addresses, so lower or upper value no different here upperValue = upperSeg.GetSegmentValue() differing := lowerValue ^ upperValue if differing != 0 { highestDifferingBitInRange := BitCount(bits.LeadingZeros32(differing)) - (SegIntSize - bitsPerSegment) differingBitPrefixLen := highestDifferingBitInRange + previousSegmentBits return lower.ToPrefixBlockLen(differingBitPrefixLen) } previousSegmentBits += bitsPerSegment } //all bits match, it's just a single address return lower.ToPrefixBlockLen(lower.GetBitCount()) } ipaddress-go-1.5.4/ipaddr/creators.go000066400000000000000000000270621440250641600175270ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr type addressSegmentCreator interface { createRangeSegment(lower, upper SegInt) *AddressDivision createSegment(lower, upper SegInt, segmentPrefixLength PrefixLen) *AddressDivision createSegmentInternal(value SegInt, segmentPrefixLength PrefixLen, addressStr string, originalVal SegInt, isStandardString bool, lowerStringStartIndex, lowerStringEndIndex int) *AddressDivision createRangeSegmentInternal(lower, upper SegInt, segmentPrefixLength PrefixLen, addressStr string, originalLower, originalUpper SegInt, isStandardString, isStandardRangeString bool, lowerStringStartIndex, lowerStringEndIndex, upperStringEndIndex int) *AddressDivision createPrefixSegment(value SegInt, segmentPrefixLength PrefixLen) *AddressDivision getMaxValuePerSegment() SegInt } type parsedAddressCreator interface { addressSegmentCreator createSectionInternal(segments []*AddressDivision, isMultiple bool) *AddressSection createAddressInternal(section *AddressSection, identifier HostIdentifierString) *Address } type parsedIPAddressCreator interface { createPrefixedSectionInternalSingle(segments []*AddressDivision, isMultiple bool, prefixLength PrefixLen) *IPAddressSection createPrefixedSectionInternal(segments []*AddressDivision, isMultiple bool, prefixLength PrefixLen) *IPAddressSection createAddressInternalFromSection(*IPAddressSection, Zone, HostIdentifierString) *IPAddress } type ipAddressCreator interface { parsedAddressCreator parsedIPAddressCreator createAddressInternalFromBytes(bytes []byte, zone Zone) *IPAddress } type ipv6AddressCreator struct{} func (creator *ipv6AddressCreator) getMaxValuePerSegment() SegInt { return IPv6MaxValuePerSegment } func (creator *ipv6AddressCreator) createSegment(lower, upper SegInt, segmentPrefixLength PrefixLen) *AddressDivision { return NewIPv6RangePrefixedSegment(IPv6SegInt(lower), IPv6SegInt(upper), segmentPrefixLength).ToDiv() } func (creator *ipv6AddressCreator) createRangeSegment(lower, upper SegInt) *AddressDivision { return NewIPv6RangeSegment(IPv6SegInt(lower), IPv6SegInt(upper)).ToDiv() } func (creator *ipv6AddressCreator) createSegmentInternal(value SegInt, segmentPrefixLength PrefixLen, addressStr string, originalVal SegInt, isStandardString bool, lowerStringStartIndex, lowerStringEndIndex int) *AddressDivision { seg := NewIPv6PrefixedSegment(IPv6SegInt(value), segmentPrefixLength) seg.setStandardString(addressStr, isStandardString, lowerStringStartIndex, lowerStringEndIndex, originalVal) seg.setWildcardString(addressStr, isStandardString, lowerStringStartIndex, lowerStringEndIndex, originalVal) return seg.ToDiv() } func (creator *ipv6AddressCreator) createRangeSegmentInternal(lower, upper SegInt, segmentPrefixLength PrefixLen, addressStr string, originalLower, originalUpper SegInt, isStandardString, isStandardRangeString bool, lowerStringStartIndex, lowerStringEndIndex, upperStringEndIndex int) *AddressDivision { seg := NewIPv6RangePrefixedSegment(IPv6SegInt(lower), IPv6SegInt(upper), segmentPrefixLength) seg.setRangeStandardString(addressStr, isStandardString, isStandardRangeString, lowerStringStartIndex, lowerStringEndIndex, upperStringEndIndex, originalLower, originalUpper) seg.setRangeWildcardString(addressStr, isStandardRangeString, lowerStringStartIndex, upperStringEndIndex, originalLower, originalUpper) return seg.ToDiv() } func (creator *ipv6AddressCreator) createPrefixSegment(value SegInt, segmentPrefixLength PrefixLen) *AddressDivision { return NewIPv6PrefixedSegment(IPv6SegInt(value), segmentPrefixLength).ToDiv() } func (creator *ipv6AddressCreator) createPrefixedSectionInternal(segments []*AddressDivision, isMultiple bool, prefixLength PrefixLen) *IPAddressSection { return newPrefixedIPv6SectionParsed(segments, isMultiple, prefixLength, false).ToIP() } func (creator *ipv6AddressCreator) createPrefixedSectionInternalSingle(segments []*AddressDivision, isMultiple bool, prefixLength PrefixLen) *IPAddressSection { return newPrefixedIPv6SectionParsed(segments, isMultiple, prefixLength, true).ToIP() } func (creator *ipv6AddressCreator) createSectionInternal(segments []*AddressDivision, isMultiple bool) *AddressSection { return newIPv6SectionParsed(segments, isMultiple).ToSectionBase() } func (creator *ipv6AddressCreator) createAddressInternalFromBytes(bytes []byte, zone Zone) *IPAddress { addr, _ := NewIPv6AddressFromZonedBytes(bytes, string(zone)) return addr.ToIP() } func (creator *ipv6AddressCreator) createAddressInternalFromSection(section *IPAddressSection, zone Zone, originator HostIdentifierString) *IPAddress { res := newIPv6AddressZoned(section.ToIPv6(), string(zone)).ToIP() if originator != nil { // the originator is assigned to a parsedIPAddress struct in validateHostName or validateIPAddressStr cache := res.cache if cache != nil { cache.identifierStr = &identifierStr{originator} } } return res } func (creator *ipv6AddressCreator) createAddressInternal(section *AddressSection, originator HostIdentifierString) *Address { res := newIPv6Address(section.ToIPv6()).ToAddressBase() if originator != nil { // the originator is assigned to a parsedIPAddress struct in validateHostName or validateIPAddressStr cache := res.cache if cache != nil { cache.identifierStr = &identifierStr{originator} } } return res } type ipv4AddressCreator struct{} func (creator *ipv4AddressCreator) getMaxValuePerSegment() SegInt { return IPv4MaxValuePerSegment } func (creator *ipv4AddressCreator) createSegment(lower, upper SegInt, segmentPrefixLength PrefixLen) *AddressDivision { return NewIPv4RangePrefixedSegment(IPv4SegInt(lower), IPv4SegInt(upper), segmentPrefixLength).ToDiv() } func (creator *ipv4AddressCreator) createRangeSegment(lower, upper SegInt) *AddressDivision { return NewIPv4RangeSegment(IPv4SegInt(lower), IPv4SegInt(upper)).ToDiv() } func (creator *ipv4AddressCreator) createSegmentInternal(value SegInt, segmentPrefixLength PrefixLen, addressStr string, originalVal SegInt, isStandardString bool, lowerStringStartIndex, lowerStringEndIndex int) *AddressDivision { seg := NewIPv4PrefixedSegment(IPv4SegInt(value), segmentPrefixLength) seg.setStandardString(addressStr, isStandardString, lowerStringStartIndex, lowerStringEndIndex, originalVal) seg.setWildcardString(addressStr, isStandardString, lowerStringStartIndex, lowerStringEndIndex, originalVal) return seg.toAddressDivision() } func (creator *ipv4AddressCreator) createRangeSegmentInternal(lower, upper SegInt, segmentPrefixLength PrefixLen, addressStr string, originalLower, originalUpper SegInt, isStandardString, isStandardRangeString bool, lowerStringStartIndex, lowerStringEndIndex, upperStringEndIndex int) *AddressDivision { seg := NewIPv4RangePrefixedSegment(IPv4SegInt(lower), IPv4SegInt(upper), segmentPrefixLength) seg.setRangeStandardString(addressStr, isStandardString, isStandardRangeString, lowerStringStartIndex, lowerStringEndIndex, upperStringEndIndex, originalLower, originalUpper) seg.setRangeWildcardString(addressStr, isStandardRangeString, lowerStringStartIndex, upperStringEndIndex, originalLower, originalUpper) return seg.ToDiv() } func (creator *ipv4AddressCreator) createPrefixSegment(value SegInt, segmentPrefixLength PrefixLen) *AddressDivision { return NewIPv4PrefixedSegment(IPv4SegInt(value), segmentPrefixLength).ToDiv() } func (creator *ipv4AddressCreator) createPrefixedSectionInternal(segments []*AddressDivision, isMultiple bool, prefixLength PrefixLen) *IPAddressSection { return newPrefixedIPv4SectionParsed(segments, isMultiple, prefixLength, false).ToIP() } func (creator *ipv4AddressCreator) createPrefixedSectionInternalSingle(segments []*AddressDivision, isMultiple bool, prefixLength PrefixLen) *IPAddressSection { return newPrefixedIPv4SectionParsed(segments, isMultiple, prefixLength, true).ToIP() } func (creator *ipv4AddressCreator) createSectionInternal(segments []*AddressDivision, isMultiple bool) *AddressSection { return newIPv4SectionParsed(segments, isMultiple).ToSectionBase() } func (creator *ipv4AddressCreator) createAddressInternalFromBytes(bytes []byte, _ Zone) *IPAddress { addr, _ := NewIPv4AddressFromBytes(bytes) return addr.ToIP() } func (creator *ipv4AddressCreator) createAddressInternalFromSection(section *IPAddressSection, _ Zone, originator HostIdentifierString) *IPAddress { res := newIPv4Address(section.ToIPv4()).ToIP() if originator != nil { cache := res.cache if cache != nil { cache.identifierStr = &identifierStr{originator} } } return res } func (creator *ipv4AddressCreator) createAddressInternal(section *AddressSection, originator HostIdentifierString) *Address { res := newIPv4Address(section.ToIPv4()).ToAddressBase() if originator != nil { cache := res.cache if cache != nil { cache.identifierStr = &identifierStr{originator} } } return res } // // // // // type macAddressCreator struct{} func (creator *macAddressCreator) getMaxValuePerSegment() SegInt { return MACMaxValuePerSegment } func (creator *macAddressCreator) createSegment(lower, upper SegInt, _ PrefixLen) *AddressDivision { return NewMACRangeSegment(MACSegInt(lower), MACSegInt(upper)).ToDiv() } func (creator *macAddressCreator) createRangeSegment(lower, upper SegInt) *AddressDivision { return NewMACRangeSegment(MACSegInt(lower), MACSegInt(upper)).ToDiv() } func (creator *macAddressCreator) createSegmentInternal(value SegInt, _ PrefixLen, addressStr string, originalVal SegInt, isStandardString bool, lowerStringStartIndex, lowerStringEndIndex int) *AddressDivision { seg := NewMACSegment(MACSegInt(value)) seg.setString(addressStr, isStandardString, lowerStringStartIndex, lowerStringEndIndex, originalVal) return seg.ToDiv() } func (creator *macAddressCreator) createRangeSegmentInternal(lower, upper SegInt, _ PrefixLen, addressStr string, originalLower, originalUpper SegInt, isStandardString, isStandardRangeString bool, lowerStringStartIndex, lowerStringEndIndex, upperStringEndIndex int) *AddressDivision { seg := NewMACRangeSegment(MACSegInt(lower), MACSegInt(upper)) seg.setRangeString(addressStr, isStandardRangeString, lowerStringStartIndex, upperStringEndIndex, originalLower, originalUpper) return seg.ToDiv() } func (creator *macAddressCreator) createPrefixSegment(value SegInt, _ PrefixLen) *AddressDivision { return NewMACSegment(MACSegInt(value)).ToDiv() } func (creator *macAddressCreator) createSectionInternal(segments []*AddressDivision, isMultiple bool) *AddressSection { return newMACSectionParsed(segments, isMultiple).ToSectionBase() } func (creator *macAddressCreator) createAddressInternal(section *AddressSection, originator HostIdentifierString) *Address { res := newMACAddress(section.ToMAC()).ToAddressBase() if originator != nil { cache := res.cache if cache != nil { cache.identifierStr = &identifierStr{originator} } } return res } func (creator *macAddressCreator) createAddressInternalFromSection(section *MACAddressSection, originator HostIdentifierString) *MACAddress { res := newMACAddress(section) if originator != nil { cache := res.cache if cache != nil { cache.identifierStr = &identifierStr{originator} } } return res } ipaddress-go-1.5.4/ipaddr/delimitedaddrstrs.go000066400000000000000000000153431440250641600214130ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import "strings" const SegmentValueDelimiter = ',' type DelimitedAddressString string // CountDelimitedAddresses will count the possible combinations, given a string with comma delimiters separating segment elements. // It is a counterpart to ParseDelimitedSegments, indicating the number of iterated elements from ParseDelimitedSegments. // // For example, given "1,2.3.4,5.6" this method will return 4 for the possible combinations: "1.3.4.6", "1.3.5.6", "2.3.4.6" and "2.3.5.6". func (str DelimitedAddressString) CountDelimitedAddresses() int { segDelimitedCount := 0 result := 1 strlen := len(str) for i := 0; i < strlen; i++ { c := str[i] if isDelimitedBoundary(c) { if segDelimitedCount > 0 { result *= segDelimitedCount + 1 segDelimitedCount = 0 } } else if c == SegmentValueDelimiter { segDelimitedCount++ } } if segDelimitedCount > 0 { result *= segDelimitedCount + 1 } return result } func isDelimitedBoundary(c byte) bool { return c == IPv4SegmentSeparator || c == IPv6SegmentSeparator || c == RangeSeparator || c == MacDashedSegmentRangeSeparator } // ParseDelimitedSegments will provide an iterator to iterate through the possible combinations, given a string with comma delimiters to denote segment elements, // // For example, given "1,2.3.4,5.6" this will iterate through "1.3.4.6", "1.3.5.6", "2.3.4.6" and "2.3.5.6" // // Another example: "1-2,3.4.5.6" will iterate through "1-2.4.5.6" and "1-3.4.5.6". // // This method will not validate strings. Each string produced can be validated using an instance of [IPAddressString]. // Use CountDelimitedAddresses for the count of elements in the iterator. func (str DelimitedAddressString) ParseDelimitedSegments() Iterator[string] { var parts [][]string var lastSegmentStartIndex, lastPartIndex, lastDelimiterIndex int anyDelimited := false var delimitedList []string strlen := len(str) s := string(str) for i := 0; i < strlen; i++ { c := str[i] if isDelimitedBoundary(c) { // end of segment or range boundary if len(delimitedList) > 0 { if parts == nil { parts = make([][]string, 0, IPv6SegmentCount) } parts, _ = addParts(s, parts, lastSegmentStartIndex, lastPartIndex, lastDelimiterIndex, delimitedList, i) lastPartIndex = i delimitedList = delimitedList[:0] } lastDelimiterIndex = i + 1 lastSegmentStartIndex = lastDelimiterIndex } else if c == SegmentValueDelimiter { anyDelimited = true if delimitedList == nil { delimitedList = make([]string, 0, 4) } sub := str[lastDelimiterIndex:i] delimitedList = append(delimitedList, string(sub)) lastDelimiterIndex = i + 1 } } if anyDelimited { if len(delimitedList) > 0 { if parts == nil { parts = make([][]string, 0, IPv6SegmentCount) } parts, _ = addParts(s, parts, lastSegmentStartIndex, lastPartIndex, lastDelimiterIndex, delimitedList, len(str)) } else { parts = append(parts, []string{s[lastPartIndex:]}) } return newDelimitedStringsIterator(parts) } return newSingleStrIterator(s) } // ParseDelimitedIPAddrSegments will provide an iterator to iterate through the possible combinations, given a string with comma delimiters to denote segment elements. func (str DelimitedAddressString) ParseDelimitedIPAddrSegments() Iterator[*IPAddressString] { return ipAddressStringIterator{str.ParseDelimitedSegments()} } func addParts(str string, parts [][]string, lastSegmentStartIndex, lastPartIndex, lastDelimiterIndex int, delimitedList []string, i int) (newParts [][]string, newDelimitedList []string) { sub := str[lastDelimiterIndex:i] delimitedList = append(delimitedList, sub) if lastPartIndex != lastSegmentStartIndex { parts = append(parts, []string{str[lastPartIndex:lastSegmentStartIndex]}) } parts = append(parts, delimitedList) return parts, delimitedList } func newDelimitedStringsIterator(parts [][]string) Iterator[string] { partCount := len(parts) it := &delimitedStringsIterator{ parts: parts, variations: make([]Iterator[string], partCount), nextSet: make([]string, partCount), } it.updateVariations(0) return it } type delimitedStringsIterator struct { parts [][]string done bool variations []Iterator[string] nextSet []string } func (it *delimitedStringsIterator) updateVariations(start int) { variationLen := len(it.variations) variations := it.variations parts := it.parts nextSet := it.nextSet for i := start; i < variationLen; i++ { strSlice := parts[i] if len(strSlice) > 1 { variations[i] = newStrSliceIterator(strSlice) } else { variations[i] = newSingleStrIterator(strSlice[0]) } nextSet[i] = variations[i].Next() } } func (it *delimitedStringsIterator) HasNext() bool { return !it.done } func (it *delimitedStringsIterator) Next() (res string) { if !it.done { result := strings.Builder{} nextSet := it.nextSet nextSetLen := len(nextSet) for i := 0; i < nextSetLen; i++ { result.WriteString(nextSet[i]) } it.increment() res = result.String() } return } func (it *delimitedStringsIterator) increment() { variations := it.variations variationsLen := len(variations) nextSet := it.nextSet for j := variationsLen - 1; j >= 0; j-- { if variations[j].HasNext() { nextSet[j] = variations[j].Next() it.updateVariations(j + 1) return } } it.done = true } func newStrSliceIterator(strs []string) Iterator[string] { return &stringIterator{strs: strs} } type stringIterator struct { strs []string } func (it *stringIterator) HasNext() bool { return len(it.strs) > 0 } func (it *stringIterator) Next() (res string) { if it.HasNext() { strs := it.strs res = strs[0] it.strs = strs[1:] } return } // type singleStringIterator struct { str string done bool } func (it *singleStringIterator) HasNext() bool { return !it.done } func (it *singleStringIterator) Next() (res string) { if it.HasNext() { it.done = true res = it.str } return } func newSingleStrIterator(str string) Iterator[string] { return &singleStringIterator{str: str} } // type ipAddressStringIterator struct { Iterator[string] } func (iter ipAddressStringIterator) Next() *IPAddressString { if !iter.HasNext() { return nil } return NewIPAddressString(iter.Iterator.Next()) } ipaddress-go-1.5.4/ipaddr/divframework.go000066400000000000000000000065031440250641600204020ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr // DivisionType serves as a common interface to all divisions type DivisionType interface { AddressItem getAddrType() addrType // getStringAsLower caches the string from getDefaultLowerString. getStringAsLower() string // GetString produces a string that avoids wildcards when a prefix length is part of the string. Equivalent to GetWildcardString when the prefix length is not part of the string. GetString() string // GetWildcardString produces a string that uses wildcards and avoids prefix length. GetWildcardString() string // IsSinglePrefix determines if the division has a single prefix for the given prefix length. You can call GetPrefixCountLen to get the count of prefixes. IsSinglePrefix(BitCount) bool // methods for string generation used by the string params and string writer. divStringProvider } var _ DivisionType = &IPAddressLargeDivision{} // StandardDivisionType represents any standard address division, which is a division of size 64 bits or less. // All can be converted to/from [AddressDivision]. type StandardDivisionType interface { DivisionType // ToDiv converts to an AddressDivision, a polymorphic type usable with all address segments and divisions. // // ToDiv implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. ToDiv() *AddressDivision } var _ StandardDivisionType = &AddressDivision{} // AddressSegmentType serves as a common interface to all segments, including [AddressSegment], [IPAddressSegment], [IPv6AddressSegment], [IPv4AddressSegment] and [MACAddressSegment]. type AddressSegmentType interface { AddressComponent StandardDivisionType // Equal returns whether the given segment is equal to this segment. // Two segments are equal if they match: // - type/version (IPv4, IPv6, MAC) // - value range // Prefix lengths are ignored. Equal(AddressSegmentType) bool // Contains returns whether this segment is same type and version as the given segment and whether it contains all values in the given segment. Contains(AddressSegmentType) bool // GetSegmentValue returns the lower value of the segment value range as a SegInt. GetSegmentValue() SegInt // GetUpperSegmentValue returns the upper value of the segment value range as a SegInt. GetUpperSegmentValue() SegInt // ToSegmentBase converts to an AddressSegment, a polymorphic type usable with all address segments. // // ToSegmentBase implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. ToSegmentBase() *AddressSegment } var _, _, _, _, _ AddressSegmentType = &AddressSegment{}, &IPAddressSegment{}, &IPv6AddressSegment{}, &IPv4AddressSegment{}, &MACAddressSegment{} ipaddress-go-1.5.4/ipaddr/division.go000066400000000000000000001372311440250641600175310ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math/big" "math/bits" "strings" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) // DivInt is an integer type for holding generic division values, which can be larger than segment values type DivInt = uint64 const DivIntSize = 64 type divderiver interface { // deriveNew produces a new division with the same bit count as the old, // but with the new values and prefix length deriveNew(val, upperVal DivInt, prefLen PrefixLen) divisionValues // derivePrefixed produces a new division with the same bit count and values as the old, // but with the new prefix length derivePrefixed(prefLen PrefixLen) divisionValues } type divIntVals interface { // getDivisionValue gets the lower value for a division getDivisionValue() DivInt // getUpperDivisionValue gets the upper value for a division getUpperDivisionValue() DivInt } func newDivValues(value, upperValue DivInt, prefLen PrefixLen, bitCount BitCount) *divIntValues { if value > upperValue { value, upperValue = upperValue, value } if bitCount <= 0 { value = 0 upperValue = 0 } else if (1 << uint(bitCount)) <= upperValue { // upperValue too big max := ^(^DivInt(0) << uint(bitCount)) value &= max upperValue &= max } prefLen = checkPrefLen(prefLen, bitCount) return newDivValuesUnchecked(value, upperValue, prefLen, bitCount) } func newDivValuesUnchecked(value, upperValue DivInt, prefLen PrefixLen, bitCount BitCount) *divIntValues { return &divIntValues{ value: value, upperValue: upperValue, prefLen: prefLen, bitCount: bitCount, } } // divIntValues are used by AddressDivision type divIntValues struct { bitCount BitCount value, upperValue DivInt prefLen PrefixLen cache divCache } func (div *divIntValues) getBitCount() BitCount { return div.bitCount } func (div *divIntValues) getByteCount() int { return (int(div.getBitCount()) + 7) >> 3 } func (div *divIntValues) getDivisionPrefixLength() PrefixLen { return div.prefLen } func (div *divIntValues) getValue() *BigDivInt { return big.NewInt(int64(div.value)) } func (div *divIntValues) getUpperValue() *BigDivInt { return big.NewInt(int64(div.upperValue)) } func (div *divIntValues) includesZero() bool { return div.value == 0 } func (div *divIntValues) includesMax() bool { return div.upperValue == ^((^DivInt(0)) << uint(div.getBitCount())) } func (div *divIntValues) isMultiple() bool { return div.value != div.upperValue } func (div *divIntValues) getCount() *big.Int { res := new(big.Int) return res.SetUint64(uint64(div.upperValue-div.value)).Add(res, bigOneConst()) } func (div *divIntValues) calcBytesInternal() (bytes, upperBytes []byte) { return calcBytesInternal(div.getByteCount(), div.getDivisionValue(), div.getUpperDivisionValue()) } func (div *divIntValues) bytesInternal(upper bool) []byte { if upper { return calcSingleBytes(div.getByteCount(), div.getUpperDivisionValue()) } return calcSingleBytes(div.getByteCount(), div.getDivisionValue()) } func calcBytesInternal(byteCount int, val, upperVal DivInt) (bytes, upperBytes []byte) { byteIndex := byteCount - 1 isMultiple := val != upperVal bytes = make([]byte, byteCount) if isMultiple { upperBytes = make([]byte, byteCount) } else { upperBytes = bytes } for { bytes[byteIndex] |= byte(val) val >>= 8 if isMultiple { upperBytes[byteIndex] |= byte(upperVal) upperVal >>= 8 } if byteIndex == 0 { return bytes, upperBytes } byteIndex-- } } func calcSingleBytes(byteCount int, val DivInt) (bytes []byte) { byteIndex := byteCount - 1 bytes = make([]byte, byteCount) for { bytes[byteIndex] |= byte(val) val >>= 8 if byteIndex == 0 { return bytes } byteIndex-- } } func (div *divIntValues) getCache() *divCache { return &div.cache } func (div *divIntValues) getAddrType() addrType { return zeroType } func (div *divIntValues) getDivisionValue() DivInt { return div.value } func (div *divIntValues) getUpperDivisionValue() DivInt { return div.upperValue } func (div *divIntValues) getSegmentValue() SegInt { return SegInt(div.value) } func (div *divIntValues) getUpperSegmentValue() SegInt { return SegInt(div.upperValue) } func (div *divIntValues) deriveNew(val, upperVal DivInt, prefLen PrefixLen) divisionValues { return newDivValuesUnchecked(val, upperVal, prefLen, div.bitCount) } func (div *divIntValues) derivePrefixed(prefLen PrefixLen) divisionValues { return newDivValuesUnchecked(div.value, div.upperValue, prefLen, div.bitCount) } func (div *divIntValues) deriveNewMultiSeg(val, upperVal SegInt, prefLen PrefixLen) divisionValues { return newDivValuesUnchecked(DivInt(val), DivInt(upperVal), prefLen, div.bitCount) } func (div *divIntValues) deriveNewSeg(val SegInt, prefLen PrefixLen) divisionValues { value := DivInt(val) return newDivValuesUnchecked(value, value, prefLen, div.bitCount) } var _ divisionValues = &divIntValues{} func createAddressDivision(vals divisionValues) *AddressDivision { return &AddressDivision{ addressDivisionInternal{ addressDivisionBase: addressDivisionBase{vals}, }, } } type addressDivisionInternal struct { addressDivisionBase } var ( // wildcards differ, here we use only range since div size not implicit octalParamsDiv = new(addrstr.IPStringOptionsBuilder).SetRadix(8).SetSegmentStrPrefix(OctalPrefix).SetWildcards(rangeWildcard).ToOptions() hexParamsDiv = new(addrstr.IPStringOptionsBuilder).SetRadix(16).SetSegmentStrPrefix(HexPrefix).SetWildcards(rangeWildcard).ToOptions() decimalParamsDiv = new(addrstr.IPStringOptionsBuilder).SetRadix(10).SetWildcards(rangeWildcard).ToOptions() ) // String produces a string that is useful when a division string is provided with no context. // If the division was originally constructed as an address segment, uses the default radix for that segment, which is decimal for IPv4 and hexadecimal for IPv6, MAC or other. // It uses a string prefix for octal or hex ("0" or "0x"), and does not use the wildcard '*', because division size is variable, so '*' is ambiguous. // GetWildcardString is more appropriate in context with other segments or divisions. It does not use a string prefix and uses '*' for full-range segments. // GetString is more appropriate in context with prefix lengths, it uses zeros instead of wildcards for prefix block ranges. func (div *addressDivisionInternal) String() string { return div.toString() } // toString produces a string that is useful when a division string is provided with no context. // It uses a string prefix for octal or hex ("0" or "0x"), and does not use the wildcard '*', because division size is variable, so '*' is ambiguous. // GetWildcardString() is more appropriate in context with other segments or divisions. It does not use a string prefix and uses '*' for full-range segments. // GetString() is more appropriate in context with prefix lengths, it uses zeros instead of wildcards for prefix block ranges. func (div *addressDivisionInternal) toString() string { // this can be moved to addressDivisionBase when we have ContainsPrefixBlock and similar methods implemented for big.Int in the base. return toString(div.toAddressDivision()) } // Format implements [fmt.Formatter] interface. It accepts the formats // - 'v' for the default address and section format (either the normalized or canonical string), // - 's' (string) for the same, // - 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix), // - 'd' (decimal), 'x' (lowercase hexadecimal), and // - 'X' (uppercase hexadecimal). // Also supported are some of fmt's format flags for integral types. // Sign control is not supported since addresses and sections are never negative. // '#' for an alternate format is supported, which adds a leading zero for octal, and for hexadecimal it adds // a leading "0x" or "0X" for "%#x" and "%#X" respectively. // Also supported is specification of minimum digits precision, output field width, // space or zero padding, and '-' for left or right justification. func (div addressDivisionInternal) Format(state fmt.State, verb rune) { switch verb { case 's', 'v': _, _ = state.Write([]byte(div.toString())) return } // we try to filter through the flags provided to the DivInt values, as if the fmt string were applied to the int(s) directly formatStr := flagsFromState(state, verb) if div.isMultiple() { formatStr = fmt.Sprintf("%s%c%s", formatStr, RangeSeparator, formatStr) _, _ = state.Write([]byte(fmt.Sprintf(formatStr, div.getDivisionValue(), div.getUpperDivisionValue()))) } else { _, _ = state.Write([]byte(fmt.Sprintf(formatStr, div.getDivisionValue()))) } } func (div *addressDivisionInternal) toStringOpts(opts addrstr.StringOptions) string { return toStringOpts(opts, div.toAddressDivision()) } func (div *addressDivisionInternal) isPrefixed() bool { return div.getDivisionPrefixLength() != nil } // return whether the division range includes the block of values for the given prefix length. func (div *addressDivisionInternal) containsPrefixBlock(divisionPrefixLen BitCount) bool { return div.isPrefixBlockVals(div.getDivisionValue(), div.getUpperDivisionValue(), divisionPrefixLen) } // Returns whether the division range includes the block of values for its prefix length. func (div *addressDivisionInternal) isPrefixBlockVals(divisionValue, upperValue DivInt, divisionPrefixLen BitCount) bool { return isPrefixBlockVals(divisionValue, upperValue, divisionPrefixLen, div.GetBitCount()) } func isPrefixBlockVals(divisionValue, upperValue DivInt, divisionPrefixLen, divisionBitCount BitCount) bool { if divisionPrefixLen <= 0 { if divisionValue != 0 { return false } maxValue := ^(^DivInt(0) << uint(divisionBitCount)) return upperValue == maxValue } if divisionPrefixLen >= divisionBitCount { return true } var ones = ^DivInt(0) divisionBitMask := ^(ones << uint(divisionBitCount)) divisionPrefixMask := ones << uint(divisionBitCount-divisionPrefixLen) var divisionNonPrefixMask = ^divisionPrefixMask return testRange(divisionValue, upperValue, upperValue, divisionPrefixMask&divisionBitMask, divisionNonPrefixMask) } // Returns whether the given range of divisionValue to upperValue is equivalent to the range of segmentValue with the prefix of divisionPrefixLen func (div *addressDivisionInternal) isSinglePrefix(divisionValue, upperValue DivInt, divisionPrefixLen BitCount) bool { bitCount := div.GetBitCount() divisionPrefixLen = checkBitCount(divisionPrefixLen, bitCount) shift := uint(bitCount - divisionPrefixLen) return (divisionValue >> shift) == (upperValue >> shift) } // Returns whether the given range of divisionValue to upperValue is equivalent to the range of segmentValue with the prefix of divisionPrefixLen func (div *addressDivisionInternal) isSinglePrefixBlock(divisionValue, upperValue DivInt, divisionPrefixLen BitCount) bool { if divisionPrefixLen == 0 { return divisionValue == 0 && upperValue == div.getMaxValue() } bitCount := div.GetBitCount() ones := ^DivInt(0) divisionBitMask := ^(ones << uint(bitCount)) divisionPrefixMask := ones << uint(bitCount-divisionPrefixLen) divisionHostMask := ^divisionPrefixMask return testRange(divisionValue, divisionValue, upperValue, divisionPrefixMask&divisionBitMask, divisionHostMask) } // ContainsPrefixBlock returns whether the division range includes the block of values for the given prefix length. func (div *addressDivisionInternal) ContainsPrefixBlock(prefixLen BitCount) bool { return div.isPrefixBlockVals(div.getDivisionValue(), div.getUpperDivisionValue(), prefixLen) } // ContainsSinglePrefixBlock returns whether the division range matches exactly the block of values for the given prefix length and has just a single prefix for that prefix length. func (div *addressDivisionInternal) ContainsSinglePrefixBlock(prefixLen BitCount) bool { prefixLen = checkDiv(div.toAddressDivision(), prefixLen) return div.isSinglePrefixBlock(div.getDivisionValue(), div.getUpperDivisionValue(), prefixLen) } // GetMinPrefixLenForBlock returns the smallest prefix length such that this division includes the block of all values for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this division represents a single value, this returns the bit count. func (div *addressDivisionInternal) GetMinPrefixLenForBlock() BitCount { return getMinPrefixLenForBlock(div.getDivisionValue(), div.getUpperDivisionValue(), div.GetBitCount()) } // GetPrefixLenForSingleBlock returns a prefix length for which there is only one prefix in this division, // and the range of values in this division matches the block of all values for that prefix. // // If the range of division values can be described this way, then this method returns the same value as GetMinPrefixLenForBlock. // // If no such prefix length exists, returns nil. // // If this division represents a single value, this returns the bit count of the segment. func (div *addressDivisionInternal) GetPrefixLenForSingleBlock() PrefixLen { return getPrefixLenForSingleBlock(div.getDivisionValue(), div.getUpperDivisionValue(), div.GetBitCount()) } // return whether the division range includes the block of values for the division prefix length, // or false if the division has no prefix length. func (div *addressDivisionInternal) isPrefixBlock() bool { prefLen := div.getDivisionPrefixLength() return prefLen != nil && div.containsPrefixBlock(prefLen.bitCount()) } func (div *addressDivisionInternal) getMaxValue() DivInt { return ^(^DivInt(0) << uint(div.GetBitCount())) } func (div *addressDivisionInternal) getDivisionValue() DivInt { vals := div.divisionValues if vals == nil { return 0 } return vals.getDivisionValue() } func (div *addressDivisionInternal) getUpperDivisionValue() DivInt { vals := div.divisionValues if vals == nil { return 0 } return vals.getUpperDivisionValue() } func (div *addressDivisionInternal) matches(value DivInt) bool { return !div.isMultiple() && value == div.getDivisionValue() } func (div *addressDivisionInternal) matchesWithMask(value, mask DivInt) bool { if div.isMultiple() { //we want to ensure that any of the bits that can change from value to upperValue is masked out (zeroed) by the mask. //In other words, when masked we need all values represented by this segment to become just a single value diffBits := div.getDivisionValue() ^ div.getUpperDivisionValue() leadingZeros := bits.LeadingZeros64(diffBits) //the bits that can change are all bits following the first leadingZero bits //all the bits that follow must be zeroed out by the mask fullMask := ^DivInt(0) >> uint(leadingZeros) if (fullMask & mask) != 0 { return false } //else we know that the mask zeros out all the bits that can change from value to upperValue, so now we just compare with either one } return value == (div.getDivisionValue() & mask) } // matchesWithMask returns whether masking with the given mask results in a valid contiguous range for this segment, // and if it does, if the result matches the range of lowerValue to upperValue. func (div *addressDivisionInternal) matchesValsWithMask(lowerValue, upperValue, mask DivInt) bool { if lowerValue == upperValue { return div.matchesWithMask(lowerValue, mask) } if !div.isMultiple() { // the values to match, lowerValue and upperValue, are not the same, so impossible to match those two values with a single value from this segment return false } thisValue := div.getDivisionValue() thisUpperValue := div.getUpperDivisionValue() masker := MaskRange(thisValue, thisUpperValue, mask, div.getMaxValue()) if !masker.IsSequential() { return false } return lowerValue == masker.GetMaskedLower(thisValue, mask) && upperValue == masker.GetMaskedUpper(thisUpperValue, mask) } func (div *addressDivisionInternal) toPrefixedNetworkDivision(divPrefixLength PrefixLen) *AddressDivision { return div.toNetworkDivision(divPrefixLength, true) } func (div *addressDivisionInternal) toNetworkDivision(divPrefixLength PrefixLen, withPrefixLength bool) *AddressDivision { vals := div.divisionValues if vals == nil { return div.toAddressDivision() } lower := div.getDivisionValue() upper := div.getUpperDivisionValue() var newLower, newUpper DivInt hasPrefLen := divPrefixLength != nil if hasPrefLen { prefBits := divPrefixLength.bitCount() bitCount := div.GetBitCount() prefBits = checkBitCount(prefBits, bitCount) mask := ^DivInt(0) << uint(bitCount-prefBits) newLower = lower & mask newUpper = upper | ^mask if !withPrefixLength { divPrefixLength = nil } if divsSame(divPrefixLength, div.getDivisionPrefixLength(), newLower, lower, newUpper, upper) { return div.toAddressDivision() } } else { divPrefixLength = nil if div.getDivisionPrefixLength() == nil { return div.toAddressDivision() } } newVals := div.deriveNew(newLower, newUpper, divPrefixLength) return createAddressDivision(newVals) } func (div *addressDivisionInternal) toPrefixedHostDivision(divPrefixLength PrefixLen) *AddressDivision { return div.toHostDivision(divPrefixLength, true) } func (div *addressDivisionInternal) toHostDivision(divPrefixLength PrefixLen, withPrefixLength bool) *AddressDivision { vals := div.divisionValues if vals == nil { return div.toAddressDivision() } lower := div.getDivisionValue() upper := div.getUpperDivisionValue() //var newLower, newUpper DivInt hasPrefLen := divPrefixLength != nil var mask SegInt if hasPrefLen { prefBits := divPrefixLength.bitCount() bitCount := div.GetBitCount() prefBits = checkBitCount(prefBits, bitCount) mask = ^(^SegInt(0) << uint(bitCount-prefBits)) } divMask := uint64(mask) maxVal := uint64(^SegInt(0)) masker := MaskRange(lower, upper, divMask, maxVal) newLower, newUpper := masker.GetMaskedLower(lower, divMask), masker.GetMaskedUpper(upper, divMask) if !withPrefixLength { divPrefixLength = nil } if divsSame(divPrefixLength, div.getDivisionPrefixLength(), newLower, lower, newUpper, upper) { return div.toAddressDivision() } newVals := div.deriveNew(newLower, newUpper, divPrefixLength) return createAddressDivision(newVals) } func (div *addressDivisionInternal) toPrefixedDivision(divPrefixLength PrefixLen) *AddressDivision { hasPrefLen := divPrefixLength != nil bitCount := div.GetBitCount() if hasPrefLen { prefBits := divPrefixLength.bitCount() prefBits = checkBitCount(prefBits, bitCount) if div.isPrefixed() && prefBits == div.getDivisionPrefixLength().bitCount() { return div.toAddressDivision() } } else { return div.toAddressDivision() } lower := div.getDivisionValue() upper := div.getUpperDivisionValue() newVals := div.deriveNew(lower, upper, divPrefixLength) return createAddressDivision(newVals) } func (div *addressDivisionInternal) getCount() *big.Int { if !div.isMultiple() { return bigOne() } if div.IsFullRange() { res := bigZero() return res.SetUint64(0xffffffffffffffff).Add(res, bigOneConst()) } return bigZero().SetUint64((div.getUpperDivisionValue() - div.getDivisionValue()) + 1) } // IsSinglePrefix returns true if the division value range spans just a single prefix value for the given prefix length. func (div *addressDivisionInternal) IsSinglePrefix(divisionPrefixLength BitCount) bool { return div.isSinglePrefix(div.getDivisionValue(), div.getUpperDivisionValue(), divisionPrefixLength) } // GetPrefixCountLen returns the number of distinct prefixes in the division value range for the given prefix length. func (div *addressDivisionInternal) GetPrefixCountLen(divisionPrefixLength BitCount) *big.Int { if div.IsFullRange() { return bigZero().Add(bigOneConst(), bigZero().SetUint64(div.getMaxValue())) } bitCount := div.GetBitCount() divisionPrefixLength = checkBitCount(divisionPrefixLength, bitCount) shiftAdjustment := bitCount - divisionPrefixLength count := ((div.getUpperDivisionValue() >> uint(shiftAdjustment)) - (div.getDivisionValue() >> uint(shiftAdjustment))) + 1 return bigZero().SetUint64(count) } func (div *addressDivisionInternal) matchesIPSegment() bool { return div.divisionValues == nil || div.getAddrType().isIP() } func (div *addressDivisionInternal) matchesIPv4Segment() bool { // the init() methods ensure even zero-IPv4 segments (IPv4Segment{}) have addr type IPv4 return div.divisionValues != nil && div.getAddrType().isIPv4() } func (div *addressDivisionInternal) matchesIPv6Segment() bool { // the init() methods ensure even zero IPv6 segments (IPv6Segment{}) have addr type IPv6 return div.divisionValues != nil && div.getAddrType().isIPv6() } func (div *addressDivisionInternal) matchesMACSegment() bool { // the init() methods ensure even zero MAC segments (MACSegment{}) have addr type MAC return div.divisionValues != nil && div.getAddrType().isMAC() } func (div *addressDivisionInternal) matchesSegment() bool { return div.GetBitCount() <= SegIntSize } func (div *addressDivisionInternal) toAddressDivision() *AddressDivision { return (*AddressDivision)(unsafe.Pointer(div)) } func (div *addressDivisionInternal) toAddressSegment() *AddressSegment { if div.matchesSegment() { return (*AddressSegment)(unsafe.Pointer(div)) } return nil } func (div *addressDivisionInternal) getStringAsLower() string { if seg := div.toAddressDivision().ToIP(); seg != nil { return seg.getStringAsLower() } return div.getStringFromStringer(div.getDefaultLowerString) } func (div *addressDivisionInternal) getDivString() string { if !div.isMultiple() { return div.getStringFromStringer(div.getDefaultLowerString) } else { return div.getStringFromStringer(div.getDefaultRangeString) } } func (div *addressDivisionInternal) getStringFromStringer(stringer func() string) string { if div.divisionValues != nil { if cache := div.getCache(); cache != nil { return cacheStr(&cache.cachedString, stringer) } } return stringer() } func (div *addressDivisionInternal) getString() string { if seg := div.toAddressDivision().ToIP(); seg != nil { return seg.GetString() } return div.getDivString() } func (div *addressDivisionInternal) getWildcardString() string { if seg := div.toAddressDivision().ToIP(); seg != nil { return seg.GetWildcardString() } return div.getDivString() // same string as GetString() when not an IP segment } func (div *addressDivisionInternal) getDefaultRangeStringVals(val1, val2 uint64, radix int) string { return getDefaultRangeStringVals(div, val1, val2, radix) } func (div *addressDivisionInternal) buildDefaultRangeString(radix int) string { return buildDefaultRangeString(div, radix) } func (div *addressDivisionInternal) getLowerStringLength(radix int) int { return toUnsignedStringLength(div.getDivisionValue(), radix) } func (div *addressDivisionInternal) getUpperStringLength(radix int) int { return toUnsignedStringLength(div.getUpperDivisionValue(), radix) } func (div *addressDivisionInternal) getLowerString(radix int, uppercase bool, appendable *strings.Builder) { toUnsignedStringCased(div.getDivisionValue(), radix, 0, uppercase, appendable) } func (div *addressDivisionInternal) getLowerStringChopped(radix int, choppedDigits int, uppercase bool, appendable *strings.Builder) { toUnsignedStringCased(div.getDivisionValue(), radix, choppedDigits, uppercase, appendable) } func (div *addressDivisionInternal) getUpperString(radix int, uppercase bool, appendable *strings.Builder) { toUnsignedStringCased(div.getUpperDivisionValue(), radix, 0, uppercase, appendable) } func (div *addressDivisionInternal) getUpperStringMasked(radix int, uppercase bool, appendable *strings.Builder) { if seg := div.toAddressDivision().ToIP(); seg != nil { seg.getUpperStringMasked(radix, uppercase, appendable) } else if div.isPrefixed() { upperValue := div.getUpperDivisionValue() mask := ^DivInt(0) << uint(div.GetBitCount()-div.getDivisionPrefixLength().bitCount()) upperValue &= mask toUnsignedStringCased(upperValue, radix, 0, uppercase, appendable) } else { div.getUpperString(radix, uppercase, appendable) } } func (div *addressDivisionInternal) getSplitLowerString(radix int, choppedDigits int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string, appendable *strings.Builder) { toSplitUnsignedString(div.getDivisionValue(), radix, choppedDigits, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, appendable) } func (div *addressDivisionInternal) getSplitRangeString(rangeSeparator string, wildcard string, radix int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string, appendable *strings.Builder) addrerr.IncompatibleAddressError { return toUnsignedSplitRangeString( div.getDivisionValue(), div.getUpperDivisionValue(), rangeSeparator, wildcard, radix, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, appendable) } func (div *addressDivisionInternal) getSplitRangeStringLength(rangeSeparator string, wildcard string, leadingZeroCount int, radix int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string) int { return toUnsignedSplitRangeStringLength( div.getDivisionValue(), div.getUpperDivisionValue(), rangeSeparator, wildcard, leadingZeroCount, radix, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix) } func (div *addressDivisionInternal) getRangeDigitCount(radix int) int { if !div.isMultiple() { return 0 } if radix == 16 { prefix := div.GetMinPrefixLenForBlock() bitCount := div.GetBitCount() if prefix < bitCount && div.ContainsSinglePrefixBlock(prefix) { bitsPerCharacter := BitCount(4) if prefix%bitsPerCharacter == 0 { return int((bitCount - prefix) / bitsPerCharacter) } } return 0 } value := div.getDivisionValue() upperValue := div.getUpperDivisionValue() maxValue := div.getMaxValue() factorRadix := DivInt(radix) factor := factorRadix numDigits := 1 for { lowerRemainder := value % factor if lowerRemainder == 0 { //Consider in ipv4 the segment 24_ //what does this mean? It means 240 to 249 (not 240 to 245) //Consider 25_. It means 250-255. //so the last digit ranges between 0-5 or 0-9 depending on whether the front matches the max possible front of 25. //If the front matches, the back ranges from 0 to the highest value of 255. //if the front does not match, the back must range across all values for the radix (0-9) var max DivInt if maxValue/factor == upperValue/factor { max = maxValue % factor } else { max = factor - 1 } upperRemainder := upperValue % factor if upperRemainder == max { //whatever range there is must be accounted entirely by range digits, otherwise the range digits is 0 //so here we check if that is the case if upperValue-upperRemainder == value { return numDigits } else { numDigits++ factor *= factorRadix continue } } } return 0 } } // if leadingZeroCount is -1, returns the number of leading zeros for maximum width, based on the width of the value func (div *addressDivisionInternal) adjustLowerLeadingZeroCount(leadingZeroCount int, radix int) int { return div.adjustLeadingZeroCount(leadingZeroCount, div.getDivisionValue(), radix) } // if leadingZeroCount is -1, returns the number of leading zeros for maximum width, based on the width of the value func (div *addressDivisionInternal) adjustUpperLeadingZeroCount(leadingZeroCount int, radix int) int { return div.adjustLeadingZeroCount(leadingZeroCount, div.getUpperDivisionValue(), radix) } func (div *addressDivisionInternal) adjustLeadingZeroCount(leadingZeroCount int, value DivInt, radix int) int { if leadingZeroCount < 0 { width := getDigitCount(value, radix) num := div.getMaxDigitCountRadix(radix) - width if num < 0 { return 0 } return num } return leadingZeroCount } func (div *addressDivisionInternal) getDigitCount(radix int) int { if !div.isMultiple() && radix == div.getDefaultTextualRadix() { //optimization - just get the string, which is cached, which speeds up further calls to this or getString() return len(div.getWildcardString()) } return getDigitCount(div.getUpperDivisionValue(), radix) } func (div *addressDivisionInternal) getMaxDigitCountRadix(radix int) int { return getMaxDigitCount(radix, div.GetBitCount(), div.getMaxValue()) } // returns the number of digits for the maximum possible value of the division when using the default radix func (div *addressDivisionInternal) getMaxDigitCount() int { return div.getMaxDigitCountRadix(div.getDefaultTextualRadix()) } // returns the default radix for textual representations of addresses (10 for IPv4, 16 for IPv6, MAC and other) func (div *addressDivisionInternal) getDefaultTextualRadix() int { addrType := div.getAddrType() if addrType.isIPv4() { return IPv4DefaultTextualRadix } return 16 } // A simple string using just the lower value and the default radix. func (div *addressDivisionInternal) getDefaultLowerString() string { return toDefaultString(div.getDivisionValue(), div.getDefaultTextualRadix()) } // A simple string using just the lower and upper values and the default radix, separated by the default range character. func (div *addressDivisionInternal) getDefaultRangeString() string { return div.getDefaultRangeStringVals(div.getDivisionValue(), div.getUpperDivisionValue(), div.getDefaultTextualRadix()) } // getDefaultSegmentWildcardString() is the wildcard string to be used when producing the default strings with getString() or getWildcardString() // // Since no parameters for the string are provided, default settings are used, but they must be consistent with the address. // // For instance, generally the '*' is used as a wildcard to denote all possible values for a given segment, // but in some cases that character is used for a segment separator. // // Note that this only applies to "default" settings, there are additional string methods that allow you to specify these separator characters. // Those methods must be aware of the defaults as well, to know when they can defer to the defaults and when they cannot. func (div *addressDivisionInternal) getDefaultSegmentWildcardString() string { if seg := div.toAddressDivision().ToSegmentBase(); seg != nil { return seg.getDefaultSegmentWildcardString() } return "" // for divisions, the width is variable and max values can change, so using wildcards make no sense } // getDefaultRangeSeparatorString() is the wildcard string to be used when producing the default strings with getString() or getWildcardString() // // Since no parameters for the string are provided, default settings are used, but they must be consistent with the address. // //For instance, generally the '-' is used as a range separator, but in some cases that character is used for a segment separator. // // Note that this only applies to "default" settings, there are additional string methods that allow you to specify these separator characters. // Those methods must be aware of the defaults as well, to know when they can defer to the defaults and when they cannot. func (div *addressDivisionInternal) getDefaultRangeSeparatorString() string { return "-" } //// only needed for godoc / pkgsite // GetBitCount returns the number of bits in each value comprising this address item. func (div *addressDivisionInternal) GetBitCount() BitCount { return div.addressDivisionBase.GetBitCount() } // GetByteCount returns the number of bytes required for each value comprising this address item, // rounding up if the bit count is not a multiple of 8. func (div *addressDivisionInternal) GetByteCount() int { return div.addressDivisionBase.GetByteCount() } // GetValue returns the lowest value in the address division range as a big integer. func (div *addressDivisionInternal) GetValue() *BigDivInt { return div.addressDivisionBase.GetValue() } // GetUpperValue returns the highest value in the address division range as a big integer. func (div *addressDivisionInternal) GetUpperValue() *BigDivInt { return div.addressDivisionBase.GetUpperValue() } // Bytes returns the lowest value in the address division range as a byte slice. func (div *addressDivisionInternal) Bytes() []byte { return div.addressDivisionBase.Bytes() } // UpperBytes returns the highest value in the address division range as a byte slice. func (div *addressDivisionInternal) UpperBytes() []byte { return div.addressDivisionBase.UpperBytes() } // CopyBytes copies the lowest value in the address division range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (div *addressDivisionInternal) CopyBytes(bytes []byte) []byte { return div.addressDivisionBase.CopyBytes(bytes) } // CopyUpperBytes copies the highest value in the address division range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (div *addressDivisionInternal) CopyUpperBytes(bytes []byte) []byte { return div.addressDivisionBase.CopyUpperBytes(bytes) } // IsZero returns whether this division matches exactly the value of zero. func (div *addressDivisionInternal) IsZero() bool { return div.addressDivisionBase.IsZero() } // IncludesZero returns whether this item includes the value of zero within its range. func (div *addressDivisionInternal) IncludesZero() bool { return div.addressDivisionBase.IncludesZero() } // IsMax returns whether this division matches exactly the maximum possible value, the value whose bits are all ones. func (div *addressDivisionInternal) IsMax() bool { return div.addressDivisionBase.IsMax() } // IncludesMax returns whether this division includes the max value, the value whose bits are all ones, within its range. func (div *addressDivisionInternal) IncludesMax() bool { return div.addressDivisionBase.IncludesMax() } // IsFullRange returns whether the division range includes all possible values for its bit length. // // This is true if and only if both IncludesZero and IncludesMax return true. func (div *addressDivisionInternal) IsFullRange() bool { return div.addressDivisionBase.IsFullRange() } func (div *addressDivisionInternal) compareSize(other AddressItem) int { return compareCount(div.toAddressDivision(), other) } //// end needed for godoc / pkgsite // NewDivision creates a division of the given bit length, assigning it the given value. // If the value's bit length exceeds the given bit length, it is truncated. func NewDivision(val DivInt, bitCount BitCount) *AddressDivision { return NewRangePrefixDivision(val, val, nil, bitCount) } // NewRangeDivision creates a division of the given bit length, assigning it the given value range. // If a value's bit length exceeds the given bit length, it is truncated. func NewRangeDivision(val, upperVal DivInt, bitCount BitCount) *AddressDivision { return NewRangePrefixDivision(val, upperVal, nil, bitCount) } // NewPrefixDivision creates a division of the given bit length, assigning it the given value and prefix length. // If the value's bit length exceeds the given bit length, it is truncated. // If the prefix length exceeds the bit length, it is adjusted to the bit length. If the prefix length is negative, it is adjusted to zero. func NewPrefixDivision(val DivInt, prefixLen PrefixLen, bitCount BitCount) *AddressDivision { return NewRangePrefixDivision(val, val, prefixLen, bitCount) } // NewRangePrefixDivision creates a division of the given bit length, assigning it the given value range and prefix length. // If a value's bit length exceeds the given bit length, it is truncated. // If the prefix length exceeds the bit length, it is adjusted to the bit length. If the prefix length is negative, it is adjusted to zero. func NewRangePrefixDivision(val, upperVal DivInt, prefixLen PrefixLen, bitCount BitCount) *AddressDivision { return createAddressDivision(newDivValues(val, upperVal, prefixLen, bitCount)) } // The following avoid the prefix length checks, value to BitCount checks, and low to high check inside newDivValues func newDivision(val DivInt, bitCount BitCount) *AddressDivision { return newRangePrefixDivision(val, val, nil, bitCount) } func newRangeDivision(val, upperVal DivInt, bitCount BitCount) *AddressDivision { return newRangePrefixDivision(val, upperVal, nil, bitCount) } func newPrefixDivision(val DivInt, prefixLen PrefixLen, bitCount BitCount) *AddressDivision { return newRangePrefixDivision(val, val, prefixLen, bitCount) } func newRangePrefixDivision(val, upperVal DivInt, prefixLen PrefixLen, bitCount BitCount) *AddressDivision { return createAddressDivision(newDivValuesUnchecked(val, upperVal, prefixLen, bitCount)) } // AddressDivision represents an arbitrary division in an address or address division grouping. // It can contain a single value or a range of sequential values and it has an assigned bit length. // Like all address components, it is immutable. // Divisions that were converted from IPv4, IPv6 or MAC segments can be converted back to the same segment type and version. // Divisions that were not converted from IPv4, IPv6 or MAC cannot be converted to segments. type AddressDivision struct { addressDivisionInternal } //Note: many of the methods below are not public to addressDivisionInternal because segments have corresponding methods using segment values // GetDivisionValue returns the lower division value in the range. func (div *AddressDivision) GetDivisionValue() DivInt { return div.getDivisionValue() } // GetUpperDivisionValue returns the upper division value in the range. func (div *AddressDivision) GetUpperDivisionValue() DivInt { return div.getUpperDivisionValue() } // IsMultiple returns whether this division represents a sequential range of values, vs a single value. func (div *AddressDivision) IsMultiple() bool { return div != nil && div.isMultiple() } // GetCount returns the count of possible distinct values for this division. // If not representing multiple values, the count is 1. // // For instance, a division with the value range of 3-7 has count 5. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (div *AddressDivision) GetCount() *big.Int { if div == nil { return bigZero() } return div.getCount() } // Compare returns a negative integer, zero, or a positive integer if this address division is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (div *AddressDivision) Compare(item AddressItem) int { return CountComparator.Compare(div, item) } // CompareSize compares the counts of two items, the number of individual values within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether one represents more individual values than another. // // CompareSize returns a positive integer if this division has a larger count than the one given, zero if they are the same, or a negative integer if the other has a larger count. func (div *AddressDivision) CompareSize(other AddressItem) int { if div == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return div.compareSize(other) } // Matches returns true if the division range matches the given single value. func (div *AddressDivision) Matches(value DivInt) bool { return div.matches(value) } // MatchesWithMask applies the mask to this division and then compares the result with the given value, // returning true if the range of the resulting division matches that single value. func (div *AddressDivision) MatchesWithMask(value, mask DivInt) bool { return div.matchesWithMask(value, mask) } // MatchesValsWithMask applies the mask to this division and then compares the result with the given values, // returning true if the range of the resulting division matches the given range. func (div *AddressDivision) MatchesValsWithMask(lowerValue, upperValue, mask DivInt) bool { return div.matchesValsWithMask(lowerValue, upperValue, mask) } // GetMaxValue gets the maximum possible value for this type of division, determined by the number of bits. // // For the highest range value of this particular segment, use GetUpperDivisionValue. func (div *AddressDivision) GetMaxValue() DivInt { return div.getMaxValue() } // IsSegmentBase returns true if this division originated as an address segment, and this can be converted back with ToSegmentBase. func (div *AddressDivision) IsSegmentBase() bool { return div != nil && div.matchesSegment() } // IsIP returns true if this division originated as an IPv4 or IPv6 segment, or an implicitly zero-valued IP segment. If so, use ToIP to convert back to the IP-specific type. func (div *AddressDivision) IsIP() bool { return div != nil && div.matchesIPSegment() } // IsIPv4 returns true if this division originated as an IPv4 segment. If so, use ToIPv4 to convert back to the IPv4-specific type. func (div *AddressDivision) IsIPv4() bool { return div != nil && div.matchesIPv4Segment() } // IsIPv6 returns true if this division originated as an IPv6 segment. If so, use ToIPv6 to convert back to the IPv6-specific type. func (div *AddressDivision) IsIPv6() bool { return div != nil && div.matchesIPv6Segment() } // IsMAC returns true if this division originated as a MAC segment. If so, use ToMAC to convert back to the MAC-specific type. func (div *AddressDivision) IsMAC() bool { return div != nil && div.matchesMACSegment() } // ToIP converts to an IPAddressSegment if this division originated as an IPv4 or IPv6 segment, or an implicitly zero-valued IP segment. // If not, ToIP returns nil. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (div *AddressDivision) ToIP() *IPAddressSegment { if div.IsIP() { return (*IPAddressSegment)(unsafe.Pointer(div)) } return nil } // ToIPv4 converts to an IPv4AddressSegment if this division originated as an IPv4 segment. // If not, ToIPv4 returns nil. // // ToIPv4 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (div *AddressDivision) ToIPv4() *IPv4AddressSegment { if div.IsIPv4() { return (*IPv4AddressSegment)(unsafe.Pointer(div)) } return nil } // ToIPv6 converts to an IPv6AddressSegment if this division originated as an IPv6 segment. // If not, ToIPv6 returns nil. // // ToIPv6 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (div *AddressDivision) ToIPv6() *IPv6AddressSegment { if div.IsIPv6() { return (*IPv6AddressSegment)(unsafe.Pointer(div)) } return nil } // ToMAC converts to a MACAddressSegment if this division originated as a MAC segment. // If not, ToMAC returns nil. // // ToMAC can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (div *AddressDivision) ToMAC() *MACAddressSegment { if div.IsMAC() { return (*MACAddressSegment)(unsafe.Pointer(div)) } return nil } // ToSegmentBase converts to an AddressSegment if this division originated as a segment. // If not, ToSegmentBase returns nil. // // ToSegmentBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (div *AddressDivision) ToSegmentBase() *AddressSegment { if div.IsSegmentBase() { return (*AddressSegment)(unsafe.Pointer(div)) } return nil } // ToDiv is an identity method. // // ToDiv can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (div *AddressDivision) ToDiv() *AddressDivision { return div } // GetString produces a normalized string to represent the segment. // If the segment is an IP segment string with CIDR network prefix block for its prefix length, then the string contains only the lower value of the block range. // Otherwise, the explicit range will be printed. // If the segment is not an IP segment, then the string is the same as that produced by GetWildcardString. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (div *AddressDivision) GetString() string { if div == nil { return nilString() } return div.getString() } // GetWildcardString produces a normalized string to represent the segment, favouring wildcards and range characters regardless of any network prefix length. // The explicit range of a range-valued segment will be printed. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and the bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (div *AddressDivision) GetWildcardString() string { if div == nil { return nilString() } return div.getWildcardString() } // String produces a string that is useful when a division string is provided with no context. // It uses a string prefix for octal or hex ("0" or "0x"), and does not use the wildcard '*', because division size is variable, and so '*' is ambiguous. // GetWildcardString is more appropriate in context with other segments or divisions. It does not use a string prefix and uses '*' for full-range segments. // GetString is more appropriate in context with prefix lengths, it uses zeros instead of wildcards for prefix block ranges. func (div *AddressDivision) String() string { if div == nil { return nilString() } return div.toString() } func testRange(lowerValue, upperValue, finalUpperValue, networkMask, hostMask DivInt) bool { return lowerValue == (lowerValue&networkMask) && finalUpperValue == (upperValue|hostMask) } func divsSame(onePref, twoPref PrefixLen, oneVal, twoVal, oneUpperVal, twoUpperVal DivInt) bool { return onePref.Equal(twoPref) && oneVal == twoVal && oneUpperVal == twoUpperVal } func divValsSame(oneVal, twoVal, oneUpperVal, twoUpperVal DivInt) bool { return oneVal == twoVal && oneUpperVal == twoUpperVal } func divValSame(oneVal, twoVal DivInt) bool { return oneVal == twoVal } func cacheStrPtr(cachedString **string, strPtr *string) { cachedVal := (*string)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(cachedString)))) if cachedVal == nil { dataLoc := (*unsafe.Pointer)(unsafe.Pointer(cachedString)) atomicStorePointer(dataLoc, unsafe.Pointer(strPtr)) } return } func cacheStr(cachedString **string, stringer func() string) (str string) { cachedVal := (*string)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(cachedString)))) if cachedVal == nil { str = stringer() dataLoc := (*unsafe.Pointer)(unsafe.Pointer(cachedString)) atomicStorePointer(dataLoc, unsafe.Pointer(&str)) } else { str = *cachedVal } return } func cacheStrErr(cachedString **string, stringer func() (string, addrerr.IncompatibleAddressError)) (str string, err addrerr.IncompatibleAddressError) { cachedVal := (*string)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(cachedString)))) if cachedVal == nil { str, err = stringer() if err == nil { dataLoc := (*unsafe.Pointer)(unsafe.Pointer(cachedString)) atomicStorePointer(dataLoc, unsafe.Pointer(&str)) } } else { str = *cachedVal } return } ipaddress-go-1.5.4/ipaddr/divisionbase.go000066400000000000000000000241761440250641600203670ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math/big" "strings" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) // divisionValuesBase provides an interface for divisions of any bit-size. // It is shared by standard and large divisions. // All the methods can be called for any division. type divisionValuesBase interface { getBitCount() BitCount getByteCount() int // getDivisionPrefixLength provides the prefix length // if is aligned is true and the prefix is non-nil, any divisions that follow in the same grouping have a zero-length prefix getDivisionPrefixLength() PrefixLen // getValue gets the lower value as a BigDivInt getValue() *BigDivInt // getValue gets the upper value as a BigDivInt getUpperValue() *BigDivInt includesZero() bool includesMax() bool isMultiple() bool getCount() *big.Int // convert lower and upper values to byte arrays calcBytesInternal() (bytes, upperBytes []byte) bytesInternal(upper bool) (bytes []byte) // getCache returns a divCache for those divisions which cache their values, or nil otherwise getCache() *divCache getAddrType() addrType } // divisionValues provides methods to provide the values from divisions, // and to create new divisions from values. // Values may be truncated if the stored values in the interface implementation // have larger bit-size than the return values. // Similarly, values may be truncated if the supplied values have greater bit-size // than the returned types. type divisionValues interface { divisionValuesBase divIntVals divderiver segderiver segmentValues } type divCache struct { cachedString, cachedWildcardString, cached0xHexString, cachedHexString, cachedNormalizedString *string isSinglePrefBlock *bool } // addressDivisionBase is a division of any bit-size. // It is shared by standard and large divisions types. // Large divisions must not use the methods of divisionValues and use only the methods in divisionValuesBase. type addressDivisionBase struct { // I've looked into making this divisionValuesBase. // If you do that, then to get access to the methods in divisionValues, you can either do type assertions like divisionValuesBase.(divisionValiues), // or you can add a method getDivisionValues to divisionValuesBase. // But in the end, either way you are assuming you know that divisionValuesBase is a divisionValues. So no point. // Instead, each division type like IPAddressSegment and LargeDivision will know which value methods apply to that type. divisionValues // The field could possibly be generic. However, since we aggregate implementations of divisionValues, what we have may be better } func (div *addressDivisionBase) getDivisionPrefixLength() PrefixLen { vals := div.divisionValues if vals == nil { return nil } return vals.getDivisionPrefixLength() } // GetBitCount returns the number of bits in each value comprising this address item. func (div *addressDivisionBase) GetBitCount() BitCount { vals := div.divisionValues if vals == nil { return 0 } return vals.getBitCount() } // GetByteCount returns the number of bytes required for each value comprising this address item, // rounding up if the bit count is not a multiple of 8. func (div *addressDivisionBase) GetByteCount() int { vals := div.divisionValues if vals == nil { return 0 } return vals.getByteCount() } // GetValue returns the lowest value in the address division range as a big integer. func (div *addressDivisionBase) GetValue() *BigDivInt { vals := div.divisionValues if vals == nil { return bigZero() } return vals.getValue() } // GetUpperValue returns the highest value in the address division range as a big integer. func (div *addressDivisionBase) GetUpperValue() *BigDivInt { vals := div.divisionValues if vals == nil { return bigZero() } return vals.getUpperValue() } // Bytes returns the lowest value in the address division range as a byte slice. func (div *addressDivisionBase) Bytes() []byte { if div.divisionValues == nil { return emptyBytes } return div.getBytes() } // UpperBytes returns the highest value in the address division range as a byte slice. func (div *addressDivisionBase) UpperBytes() []byte { if div.divisionValues == nil { return emptyBytes } return div.getUpperBytes() } // CopyBytes copies the lowest value in the address division range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (div *addressDivisionBase) CopyBytes(bytes []byte) []byte { if div.divisionValues == nil { if bytes != nil { return bytes } return emptyBytes } cached := div.getBytes() return getBytesCopy(bytes, cached) } // CopyUpperBytes copies the highest value in the address division range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (div *addressDivisionBase) CopyUpperBytes(bytes []byte) []byte { if div.divisionValues == nil { if bytes != nil { return bytes } return emptyBytes } cached := div.getUpperBytes() return getBytesCopy(bytes, cached) } func (div *addressDivisionBase) getBytes() (bytes []byte) { return div.bytesInternal(false) } func (div *addressDivisionBase) getUpperBytes() (bytes []byte) { return div.bytesInternal(true) } func (div *addressDivisionBase) getCount() *big.Int { if !div.isMultiple() { return bigOne() } return div.divisionValues.getCount() } func (div *addressDivisionBase) isMultiple() bool { vals := div.divisionValues if vals == nil { return false } return vals.isMultiple() } // GetPrefixCountLen returns the count of the number of distinct values within the prefix part of the address item, the bits that appear within the prefix length. func (div *addressDivisionBase) GetPrefixCountLen(prefixLength BitCount) *big.Int { if prefixLength < 0 { return bigOne() } bitCount := div.GetBitCount() if prefixLength >= bitCount { return div.getCount() } ushiftAdjustment := uint(bitCount - prefixLength) lower := div.GetValue() upper := div.GetUpperValue() upper.Rsh(upper, ushiftAdjustment) lower.Rsh(lower, ushiftAdjustment) upper.Sub(upper, lower).Add(upper, bigOneConst()) return upper } // IsZero returns whether this division matches exactly the value of zero. func (div *addressDivisionBase) IsZero() bool { return !div.isMultiple() && div.IncludesZero() } // IncludesZero returns whether this item includes the value of zero within its range. func (div *addressDivisionBase) IncludesZero() bool { vals := div.divisionValues if vals == nil { return true } return vals.includesZero() } // IsMax returns whether this address matches exactly the maximum possible value, the value whose bits are all ones. func (div *addressDivisionBase) IsMax() bool { return !div.isMultiple() && div.includesMax() } // IncludesMax returns whether this division includes the max value, the value whose bits are all ones, within its range. func (div *addressDivisionBase) IncludesMax() bool { vals := div.divisionValues if vals == nil { return false } return vals.includesMax() } // IsFullRange returns whether the division range includes all possible values for its bit length. // // This is true if and only if both IncludesZero and IncludesMax return true. func (div *addressDivisionBase) IsFullRange() bool { return div.includesZero() && div.includesMax() } func (div *addressDivisionBase) getAddrType() addrType { vals := div.divisionValues if vals == nil { return zeroType } return vals.getAddrType() } func (div *addressDivisionBase) matchesStructure(other DivisionType) (res bool, addrType addrType) { addrType = div.getAddrType() if addrType != other.getAddrType() || (addrType.isZeroSegments() && (div.GetBitCount() != other.GetBitCount())) { return } res = true return } // toString produces a string that is useful when a division string is provided with no context. // It uses a string prefix for octal or hex ("0" or "0x"), and does not use the wildcard '*', because division size is variable, so '*' is ambiguous. // GetWildcardString() is more appropriate in context with other segments or divisions. It does not use a string prefix and uses '*' for full-range segments. // GetString() is more appropriate in context with prefix lengths, it uses zeros instead of wildcards for prefix block ranges. func toString(div DivisionType) string { // this can be moved to addressDivisionBase when we have ContainsPrefixBlock and similar methods implemented for big.Int in the base. radix := div.getDefaultTextualRadix() var opts addrstr.IPStringOptions switch radix { case 16: opts = hexParamsDiv case 10: opts = decimalParamsDiv case 8: opts = octalParamsDiv default: opts = new(addrstr.IPStringOptionsBuilder).SetRadix(radix).SetWildcards(rangeWildcard).ToOptions() } return toStringOpts(opts, div) } func toStringOpts(opts addrstr.StringOptions, div DivisionType) string { builder := strings.Builder{} params := toParams(opts) builder.Grow(params.getDivisionStringLength(div)) params.appendDivision(&builder, div) return builder.String() } func bigDivsSame(onePref, twoPref PrefixLen, oneVal, twoVal, oneUpperVal, twoUpperVal *BigDivInt) bool { return onePref.Equal(twoPref) && oneVal.CmpAbs(twoVal) == 0 && oneUpperVal.CmpAbs(twoUpperVal) == 0 } func bigDivValsSame(oneVal, twoVal, oneUpperVal, twoUpperVal *BigDivInt) bool { return oneVal.CmpAbs(twoVal) == 0 && oneUpperVal.CmpAbs(twoUpperVal) == 0 } func bigDivValSame(oneVal, twoVal *big.Int) bool { return oneVal.CmpAbs(twoVal) == 0 } ipaddress-go-1.5.4/ipaddr/doc.go000066400000000000000000000133201440250641600164420ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // /* IPAddress is a library for handling IP addresses and subnets, both IPv4 and IPv6. # Benefits of this Library The primary goals are: - Parse all IPv4 and IPv6 address formats and host name formats in common usage, plus some additional formats. - Parse and represent subnets, either those specified by network prefix length or those specified with ranges of segment values. - Allow the separation of address parsing from host parsing. - Allow control over which formats are allowed when parsing, whether IPv4, IPv6, subnet formats, inet_aton formats, or other. - Produce all common address strings of different formats for a given IPv4 or IPv6 address and produce collections of such strings. - Parse all common MAC Address formats in usage and produce all common MAC address strings of different formats. - Integrate MAC addresses with IPv6 with standard conversions. - Integrate IPv4 Addresses with IPv6 through common address conversions. - Polymorphism is a key goal. The library maintains an address framework of interfaces that allow most library functionality to be independent of address type or version, whether IPv4, IPv6 or MAC. This allows for code which supports both IPv4 and IPv6 transparently. - Thread-safety and immutability. The core types (host names, address strings, addresses, address sections, address segments, address ranges) are all immutable. They do not change their underlying value. For sharing amongst goroutines this is valuable. - Modify addresses, such as altering prefix lengths, masking, splitting into sections and segments, splitting into network and host sections, reconstituting from sections and segments. - Provide address operations and subnetting, such as obtaining the prefix block subnet for a prefixed address, iterating through subnets, iterating through prefixes, prefix blocks, or segment blocks of subnets, incrementing and decrementing addresses by integer values, reversing address bits for endianness or DNS lookup, set-subtracting subnets from other subnets, subnetting, intersections of subnets, merging subnets, checking containment of addresses in subnets, listing subnets covering a span of addresses. - Sorting and comparison of host names, addresses, address strings and subnets. All the address component types are compararable. - Integrate with the Go language primitive types and the standard library types [net.IP], [net.IPAddr], [net.IPMask], [net.IPNet], [net.TCPAddr], [net.UDPAddr], [net/netip.Addr], [net/netip.Prefix], [net/netip.AddrPort], and [math/big.Int]. - Make address manipulations easy, so you do not have to worry about longs/ints/shorts/bytes/bits, signed/unsigned, sign extension, ipv4/v6, masking, iterating, and other implementation details. # Design Overview The core types are [IPAddressString], [HostName], and [MACAddressString] along with the [Address] base type and its associated types [IPAddress], [IPv4Address], [IPv6Address], and [MACAddress], as well as the sequential address type [SequentialRange]. If you have a textual representation of an IP address, then start with [IPAddressString] or [HostName]. If you have a textual representation of a MAC address, then start with [MACAddressString]. Note that address instances can represent either a single address or a subnet. If you have either an address or host name, or you have something with a port or service name, then use [HostName]. If you have numeric bytes or integers, then start with [IPv4Address], [IPv6Address], [IPAddress], or [MACAddress]. This library allows you to scale down from more specific address types to more generic address types, and then to scale back up again. The polymorphism is useful for IP-version ambiguous code. The most-specific types allow for method sets tailored to the address version or type. You can only scale up to a specific version or address type if the lower level instance was originally derived from an instance of the specific type. So, for instance, an [IPv6Address] can be converted to an [IPAddress] using [IPv6Address.ToIP], or to an [Address] using [IPv6Address.ToAddressBase], which can then be converted back to [IPAddress] or an [IPv6Address] using [Address.ToIP] or [Address.ToIPv6]. But that [IPv6Address] cannot be scaled back to IPv4. If you wish to convert that [IPv6Address] to IPv4, you would need to use an implementation of [IPv4AddressConverter]. This library has similarities in design to the [Java IPAddress library]. Notable divergences derive from the differences between the Java and Go languages, such as the differences in error handling and the lack of inheritance in Go, amongst many other differences. Other divergences derive from common Go language idioms and practices which differ from standard Java idioms and practices. # Code Examples For common use-cases, you may wish to go straight to the [wiki code examples] which cover a wide breadth of common use-cases. # Further Documentation You can read [further documentation] with more depth. [Java IPAddress library]: https://github.com/seancfoley/IPAddress [wiki code examples]: https://github.com/seancfoley/ipaddress-go/wiki/Code-Examples [further documentation]: https://seancfoley.github.io/IPAddress/ */ package ipaddr ipaddress-go-1.5.4/ipaddr/err.go000066400000000000000000000146251440250641600164760ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "errors" "fmt" "strconv" "strings" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" ) type addressError struct { // key to look up the error message key string // an optional string with the address str string } func (a *addressError) Error() string { return getStr(a.str) + lookupStr("ipaddress.address.error") + " " + lookupStr(a.key) } func getStr(str string) (res string) { if len(str) > 0 { res = str + " " } return } // GetKey can be used to internationalize the error strings in the IPAddress library. // The list of keys and their English translations are listed in IPAddressResources.properties. // Use your own preferred method to map the key to your translations. // One such option is golang.org/x/text which provides language tags (https://pkg.go.dev/golang.org/x/text/language?utm_source=godoc#Tag), // which can then be mapped to catalogs, each catalog a list of translations for the set of keys provided here. // In the code you supply a language key to use the right catalog. // You can use the gotext tool to integrate those translations with your application. func (a *addressError) GetKey() string { return a.key } type mergedError struct { addrerr.AddressError merged []addrerr.AddressError } func (a *mergedError) GetMerged() []addrerr.AddressError { return a.merged } type addressStringError struct { addressError } type addressStringNestedError struct { addressStringError nested addrerr.AddressStringError } func (a *addressStringNestedError) Error() string { return a.addressError.Error() + ": " + a.nested.Error() } type addressStringIndexError struct { addressStringError // byte index location in string of the error index int } func (a *addressStringIndexError) Error() string { return lookupStr("ipaddress.address.error") + " " + lookupStr(a.key) + " " + strconv.Itoa(a.index) } type hostNameError struct { addressError } // GetAddrError returns the nested address error which is nil for a host name error func (a *hostNameError) GetAddrError() addrerr.AddressError { return nil } func (a *hostNameError) Error() string { return getStr(a.str) + lookupStr("ipaddress.host.error") + " " + lookupStr(a.key) } type hostNameNestedError struct { hostNameError nested error } type hostAddressNestedError struct { hostNameIndexError nested addrerr.AddressError } // GetAddrError returns the nested address error func (a *hostAddressNestedError) GetAddrError() addrerr.AddressError { return a.nested } func (a *hostAddressNestedError) Error() string { if a.hostNameIndexError.key != "" { return getStr(a.str) + lookupStr("ipaddress.host.error") + " " + a.hostNameIndexError.Error() + " " + a.nested.Error() } return getStr(a.str) + lookupStr("ipaddress.host.error") + " " + a.nested.Error() } type hostNameIndexError struct { hostNameError // byte index location in string of the error index int } func (a *hostNameIndexError) Error() string { return getStr(a.str) + lookupStr("ipaddress.host.error") + " " + lookupStr(a.key) + " " + strconv.Itoa(a.index) } type incompatibleAddressError struct { addressError } type sizeMismatchError struct { incompatibleAddressError } type addressValueError struct { addressError // the value val int } /////////////////////////////////////////////// type wrappedErr struct { // root cause cause error // wrapper err error str string } func (wrappedErr *wrappedErr) Error() string { str := wrappedErr.str if len(str) > 0 { return str } str = wrappedErr.err.Error() + ": " + wrappedErr.cause.Error() wrappedErr.str = str return str } func newError(str string) error { return errors.New(str) } // errorF returns a formatted error func errorF(format string, a ...interface{}) error { return errors.New(fmt.Sprintf(format, a...)) } // wrapErrf wraps the given error, but only if it is not nil. func wrapErrf(err error, format string, a ...interface{}) error { return wrapper(true, err, format, a...) } // wrapToErrf is like wrapErrf but always returns an error func wrapToErrf(err error, format string, a ...interface{}) error { return wrapper(false, err, format, a...) } func wrapper(nilIfFirstNil bool, err error, format string, a ...interface{}) error { if err == nil { if nilIfFirstNil { return nil } return errorF(format, a...) } return &wrappedErr{ cause: err, err: errorF(format, a...), } } type mergedErr struct { mergedErrs []error str string } func (merged *mergedErr) Error() (str string) { str = merged.str if len(str) > 0 { return } mergedErrs := merged.mergedErrs errLen := len(mergedErrs) strs := make([]string, errLen) totalLen := 0 for i, err := range mergedErrs { str := err.Error() strs[i] = str totalLen += len(str) } format := strings.Builder{} format.Grow(totalLen + errLen*2) format.WriteString(strs[0]) for _, str := range strs[1:] { format.WriteString(", ") format.WriteString(str) } str = format.String() merged.str = str return } // mergeErrs merges an existing error with a new one func mergeErrs(err error, format string, a ...interface{}) error { newErr := errorF(format, a...) if err == nil { return newErr } var merged []error if merge, isMergedErr := err.(*mergedErr); isMergedErr { merged = append(append([]error(nil), merge.mergedErrs...), newErr) } else { merged = []error{err, newErr} } return &mergedErr{mergedErrs: merged} } // mergeErrors merges multiple errors func mergeAllErrs(errs ...error) error { var all []error allLen := len(errs) if allLen <= 1 { if allLen == 0 { return nil } return errs[0] } for _, err := range errs { if err != nil { if merge, isMergedErr := err.(*mergedErr); isMergedErr { all = append(all, merge.mergedErrs...) } else { all = append(all, err) } } } allLen = len(all) if allLen <= 1 { if allLen == 0 { return nil } return all[0] } return &mergedErr{mergedErrs: all} } ipaddress-go-1.5.4/ipaddr/filterediterator.go000066400000000000000000000040031440250641600212430ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr type filteredAddrIterator struct { skip func(*Address) bool iter Iterator[*Address] next *Address } func (it *filteredAddrIterator) Next() (res *Address) { res = it.next for { next := it.iter.Next() if next == nil || !it.skip(next) { it.next = next break } } return res } func (it *filteredAddrIterator) HasNext() bool { return it.next != nil } // NewFilteredAddrIterator modifies an address iterator to skip certain addresses, // skipping those elements for which the "skip" function returns true func NewFilteredAddrIterator(iter Iterator[*Address], skip func(*Address) bool) Iterator[*Address] { res := &filteredAddrIterator{skip: skip, iter: iter} res.Next() return res } type filteredIPAddrIterator struct { skip func(*IPAddress) bool iter Iterator[*IPAddress] next *IPAddress } func (it *filteredIPAddrIterator) Next() (res *IPAddress) { res = it.next for { next := it.iter.Next() if next == nil || !it.skip(next) { it.next = next break } } return res } func (it *filteredIPAddrIterator) HasNext() bool { return it.next != nil } // NewFilteredIPAddrIterator returns an iterator similar to the passed in iterator, // but skipping those elements for which the "skip" function returns true func NewFilteredIPAddrIterator(iter Iterator[*IPAddress], skip func(*IPAddress) bool) Iterator[*IPAddress] { res := &filteredIPAddrIterator{skip: skip, iter: iter} res.Next() return res } ipaddress-go-1.5.4/ipaddr/framework.go000066400000000000000000001251351440250641600177020ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math/big" "net" "net/netip" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" ) type BitItem interface { // GetByteCount returns the number of bytes required for each value comprising this address item, // rounding up if the bit count is not a multiple of 8. GetByteCount() int // GetBitCount returns the number of bits in each value comprising this address item. GetBitCount() BitCount } // AddressItem represents all addresses, division groupings, divisions, and sequential ranges. // Any address item can be compared to any other. type AddressItem interface { BitItem // GetValue returns the lowest individual address item in the address item range as an integer value. GetValue() *big.Int // GetUpperValue returns the highest individual address item in the address item range as an integer value. GetUpperValue() *big.Int // CopyBytes copies the value of the lowest individual address item in this address item range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. CopyBytes(bytes []byte) []byte // CopyUpperBytes copies the value of the highest individual address item in this address item range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. CopyUpperBytes(bytes []byte) []byte // Bytes returns the lowest individual address item in the address item range as a byte slice. Bytes() []byte // UpperBytes returns the highest individual address item in the address item range as a byte slice. UpperBytes() []byte // GetCount provides the number of address items represented by this AddressItem, for example the subnet size for IP addresses GetCount() *big.Int // IsMultiple returns whether this item represents multiple values (the count is larger than 1) IsMultiple() bool // IsFullRange returns whether this address item represents all possible values attainable by an address item of this type. // // This is true if and only if both IncludesZero and IncludesMax return true. IsFullRange() bool // IncludesZero returns whether this item includes the value of zero within its range. IncludesZero() bool // IncludesMax returns whether this item includes the max value, the value whose bits are all ones, within its range. IncludesMax() bool // IsZero returns whether this address item matches exactly the value of zero. IsZero() bool // IsMax returns whether this address item matches exactly the maximum possible value, the value whose bits are all ones. IsMax() bool // ContainsPrefixBlock returns whether the values of this item contains the prefix block for the given prefix length. // Unlike ContainsSinglePrefixBlock, whether there are multiple prefix values for the given prefix length makes no difference. ContainsPrefixBlock(BitCount) bool // ContainsSinglePrefixBlock returns whether the values of this series contains a single prefix block for the given prefix length. // This means there is only one prefix of the given length in this item, and this item contains the prefix block for that given prefix, all items with that same prefix. ContainsSinglePrefixBlock(BitCount) bool // GetPrefixLenForSingleBlock returns a prefix length for which there is only one prefix of that length in this item, // and the range of this item matches the block of all values for that prefix. // // If the entire range can be described this way, then this method returns the same value as GetMinPrefixLenForBlock. // // If no such prefix length exists, returns nil. // // If this item represents a single value, this returns the bit count. GetPrefixLenForSingleBlock() PrefixLen // GetMinPrefixLenForBlock returns the smallest prefix length possible such that this item includes the block of all values for that prefix length. // // If the entire range can be dictated this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this item represents a single value, this returns the bit count. GetMinPrefixLenForBlock() BitCount // GetPrefixCountLen returns the count of the number of distinct values within the prefix part of the range of values for this item GetPrefixCountLen(BitCount) *big.Int // Compare returns a negative integer, zero, or a positive integer if this address item is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. Compare(AddressItem) int // CompareSize compares the counts of two address items, // whether addresses in the subnet or address range, whether individual sections in the collection of sections, whether individual segments in the segment's range. // It compares the number of individual elements within each. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether one item represents more individual addresses than another. // // CompareSize returns a positive integer if this item has a larger count than the one given, zero if they are the same, or a negative integer if the other has a larger count. CompareSize(AddressItem) int fmt.Stringer fmt.Formatter } type Prefixed interface { // IsPrefixed returns whether this item has an associated prefix length. IsPrefixed() bool // GetPrefixLen returns the prefix length, or nil if there is no prefix length. // // A prefix length indicates the number of bits in the initial part (most significant bits) of the series that comprise the prefix. // // A prefix is a part of the series that is not specific to that series but common amongst a group, such as a CIDR prefix block subnet. GetPrefixLen() PrefixLen // IsPrefixBlock returns whether this item has a prefix length and includes the block associated with that prefix length. // If the prefix length matches the bit count, this returns true. // // This is different from ContainsPrefixBlock in that this method returns // false if this item has no prefix length, or it has a prefix length that differs from a prefix length for which ContainsPrefixBlock returns true. IsPrefixBlock() bool // IsSinglePrefixBlock returns whether the range of values matches a single subnet block for the prefix length. // // This is different from ContainsSinglePrefixBlock in that this method returns // false if this series has no prefix length or a prefix length that differs from a prefix length for which ContainsSinglePrefixBlock returns true. IsSinglePrefixBlock() bool } type PrefixedConstraint[T any] interface { Prefixed // WithoutPrefixLen provides the same item but with no prefix length. The values remain unchanged. WithoutPrefixLen() T // ToPrefixBlock returns the item whose prefix matches the prefix of this item, while the remaining bits span all values. // If this item has no prefix length, then this item is returned. // // The returned item will include all items with the same prefix as this item, known as the prefix "block". ToPrefixBlock() T // ToPrefixBlockLen returns the item associated with the prefix length provided, // the item whose prefix of that length matches the prefix of that length in this item, and the remaining bits span all values. // // The returned address will include all items with the same prefix as this one, known as the prefix "block". ToPrefixBlockLen(BitCount) T // SetPrefixLen sets the prefix length, returning a new item with the same values but with the new prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the item. // The provided prefix length will be adjusted to these boundaries if necessary. SetPrefixLen(BitCount) T } // AddressDivisionSeries serves as a common interface to all division groupings, address sections, and addresses. type AddressDivisionSeries interface { AddressItem // GetDivisionCount returns the number of divisions. GetDivisionCount() int // GetPrefixCount returns the count of prefixes in this series for its prefix length, or the total count if it has no prefix length GetPrefixCount() *big.Int // GetBlockCount returns the count of distinct values in the given number of initial (more significant) segments. GetBlockCount(divisionCount int) *big.Int // GetSequentialBlockIndex gets the minimal division index for which all following divisions are full-range blocks. // // The division at this index is not a full-range block unless all divisions are full-range. // The division at this index and all following divisions form a sequential range. // For the full series to be sequential, the preceding divisions must be single-valued. GetSequentialBlockIndex() int // GetSequentialBlockCount provides the count of elements from the sequential block iterator, the minimal number of sequential address division series that comprise this address division series. GetSequentialBlockCount() *big.Int // IsSequential returns whether the series represents a range of values that are sequential. // // Generally, this means that any division covering a range of values must be followed by divisions that are full range, covering all values. IsSequential() bool Prefixed // GetGenericDivision returns the division at the given index as a DivisionType. // The first division is at index 0. // GetGenericDivision will panic given a negative index or index larger than the division count. GetGenericDivision(index int) DivisionType // useful for comparisons } var _ AddressDivisionSeries = &IPAddressLargeDivisionGrouping{} // StandardDivGroupingType represents any standard division grouping (division groupings or address sections where all divisions are 64 bits or less) // including [AddressSection], [IPAddressSection], [IPv4AddressSection], [IPv6AddressSection], [MACAddressSection], and [AddressDivisionGrouping] type StandardDivGroupingType interface { AddressDivisionSeries // IsAdaptiveZero returns true if the division grouping was originally created as an implicitly zero-valued section or grouping (e.g. IPv4AddressSection{}), // meaning it was not constructed using a constructor function. // Such a grouping, which has no divisions or segments, is convertible to an implicitly zero-valued grouping of any type or version, whether IPv6, IPv4, MAC, or other. // In other words, when a section or grouping is the zero-value, then it is equivalent and convertible to the zero value of any other section or grouping type. IsAdaptiveZero() bool // ToDivGrouping converts to an AddressDivisionGrouping, a polymorphic type usable with all address sections and division groupings. // // ToDivGrouping implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. ToDivGrouping() *AddressDivisionGrouping } var _, _ StandardDivGroupingType = &AddressDivisionGrouping{}, &IPv6v4MixedAddressGrouping{} // AddressComponent represents all addresses, address sections, and address segments. type AddressComponent interface { //AddressSegment and above, AddressSegmentSeries and above // TestBit returns true if the bit in the lower value of the address component at the given index is 1, where index 0 refers to the least significant bit. // In other words, it computes (bits & (1 << n)) != 0), using the lower value of this address component. // TestBit will panic if n < 0, or if it matches or exceeds the bit count of this address component. TestBit(index BitCount) bool // IsOneBit returns true if the bit in the lower value of this address component at the given index is 1, where index 0 refers to the most significant bit. // IsOneBit will panic if bitIndex is less than zero, or if it is larger than the bit count of this address component. IsOneBit(index BitCount) bool // ToHexString writes this address component as a single hexadecimal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // If a multiple-valued component cannot be written as a single prefix block or a range of two values, an error is returned. ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) // ToNormalizedString produces a string that is consistent for all address components of the same type and version. ToNormalizedString() string } // AddressSegmentSeries serves as a common interface to all address sections and addresses. type AddressSegmentSeries interface { // Address and above, AddressSection and above, IPAddressSegmentSeries, ExtendedIPSegmentSeries AddressComponent AddressDivisionSeries // GetMaxSegmentValue returns the maximum possible segment value for this type of series. // // Note this is not the maximum of the range of segment values in this specific series, // this is the maximum value of any segment for this series type and version, determined by the number of bits per segment. GetMaxSegmentValue() SegInt // GetSegmentCount returns the number of segments, which is the same as the division count since the segments are also the divisions GetSegmentCount() int // GetBitsPerSegment returns the number of bits comprising each segment in this series. Segments in the same series are equal length. GetBitsPerSegment() BitCount // GetBytesPerSegment returns the number of bytes comprising each segment in this series. Segments in the same series are equal length. GetBytesPerSegment() int // ToCanonicalString produces a canonical string for the address series. // // For IPv4, dotted octet format, also known as dotted decimal format, is used. // https://datatracker.ietf.org/doc/html/draft-main-ipaddr-text-rep-00#section-2.1 // // For IPv6, RFC 5952 describes the canonical string representation. // https://en.wikipedia.org/wiki/IPv6_address#Representation // http://tools.ietf.org/html/rfc5952 // // For MAC, it uses the canonical standardized IEEE 802 MAC address representation of xx-xx-xx-xx-xx-xx. An example is "01-23-45-67-89-ab". // For range segments, '|' is used: "11-22-33|44-55-66". // // Each address has a unique canonical string, not counting the prefix length. // With IP addresses and sections, the prefix length is included in the string, and the prefix length can cause two equal addresses to have different strings, for example "1.2.3.4/16" and "1.2.3.4". // It can also cause two different addresses to have the same string, such as "1.2.0.0/16" for the individual address "1.2.0.0" and also the prefix block "1.2.*.*". ToCanonicalString() string // ToNormalizedWildcardString produces a string similar to the normalized string but avoids the CIDR prefix length in the case of IP addresses. // Multiple-valued segments will be shown with wildcards and ranges (denoted by '*' and '-'). ToNormalizedWildcardString() string // ToCompressedString produces a short representation of this series while remaining within the confines of standard representation(s) of the series. // // For IPv4, it is the same as the canonical string. // // For IPv6, it differs from the canonical string. It compresses the maximum number of zeros and/or host segments with the IPv6 compression notation '::'. // // For MAC, it differs from the canonical string. It produces a shorter string for the address that has no leading zeros. ToCompressedString() string // ToBinaryString writes this address series as a single binary value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0b" prefix. // // If a multiple-valued series cannot be written as a single prefix block or a range of two values, an error is returned. ToBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) // ToOctalString writes this address series as a single octal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0" prefix. // // If a multiple-valued series cannot be written as a single prefix block or a range of two values, an error is returned. ToOctalString(withPrefix bool) (string, addrerr.IncompatibleAddressError) // GetSegmentStrings returns a slice with the string for each segment being the string that is normalized with wildcards. GetSegmentStrings() []string // GetGenericSegment returns the segment at the given index as an AddressSegmentType. // The first segment is at index 0. // GetGenericSegment will panic given a negative index or an index matching or larger than the segment count. GetGenericSegment(index int) AddressSegmentType } var _, _ AddressSegmentSeries = &Address{}, &AddressSection{} // IPAddressSegmentSeries serves as a common interface to all IP address sections and IP addresses. type IPAddressSegmentSeries interface { // IPAddress and above, IPAddressSection and above, ExtendedIPSegmentSeries AddressSegmentSeries // IncludesZeroHost returns whether the series contains an individual series with a host of zero. If the series has no prefix length it returns false. // If the prefix length matches the bit count, then it returns true. // // Otherwise, it checks whether it contains an individual series for which all bits past the prefix are zero. IncludesZeroHost() bool // IncludesZeroHostLen returns whether the series contains an individual series with a host of zero, a series for which all bits past the given prefix length are zero. IncludesZeroHostLen(prefLen BitCount) bool // IncludesMaxHost returns whether the series contains an individual series with a host of all one-bits. If the series has no prefix length it returns false. // If the prefix length matches the bit count, then it returns true. // // Otherwise, it checks whether it contains an individual series for which all bits past the prefix are one. IncludesMaxHost() bool // IncludesMaxHostLen returns whether the series contains an individual series with a host of all one-bits, a series for which all bits past the given prefix length are all ones. IncludesMaxHostLen(prefLen BitCount) bool // IsZeroHost returns whether this series has a prefix length and if so, // whether the host section is always zero for all individual series in this subnet or address section. // // If the host section is zero length (there are zero host bits), IsZeroHost returns true. IsZeroHost() bool // IsZeroHostLen returns whether the host section is always zero for all individual series in this address or address section, // for the given prefix length. // // If the host section is zero length (there are zero host bits), IsZeroHostLen returns true. IsZeroHostLen(BitCount) bool // IsMaxHost returns whether this address or address section has a prefix length and if so, // whether the host section is always all one-bits, the max value, for all individual series in this address or address section, //the host being the bits following the prefix. // // If the host section is zero length (there are zero host bits), IsMaxHost returns true. IsMaxHost() bool // IsMaxHostLen returns whether the host is all one-bits, the max value, for all individual series in this address or address section, // for the given prefix length, the host being the bits following the prefix. // // If the host is zero length (there are zero host bits), IsMaxHostLen returns true. IsMaxHostLen(BitCount) bool // IsSingleNetwork returns whether the network section of the IP address series, the prefix, consists of a single value. // // If it has no prefix length, it returns true if not multiple, if it contains only a single individual series. IsSingleNetwork() bool // GetIPVersion returns the IP version of this IP address or IP address section. GetIPVersion() IPVersion // GetBlockMaskPrefixLen returns the prefix length if this IP address or IP address section is equivalent to the mask for a CIDR prefix block. // Otherwise, it returns nil. // A CIDR network mask is a series with all ones in the network section and then all zeros in the host section. // A CIDR host mask is a series with all zeros in the network section and then all ones in the host section. // The prefix length is the bit-length of the network section. // // Also, keep in mind that the prefix length returned by this method is not equivalent to the prefix length of this instance, // indicating the network and host section of this series. // The prefix length returned here indicates the whether the value of this series can be used as a mask for the network and host // section of any other series. Therefore, the two values can be different values, or one can be nil while the other is not. // // This method applies only to the lower value of the range if this series represents multiple values. GetBlockMaskPrefixLen(network bool) PrefixLen // GetLeadingBitCount returns the number of consecutive leading one or zero-bits. // If ones is true, returns the number of consecutive leading one-bits. // Otherwise, returns the number of consecutive leading zero bits. // // This method applies to the lower value of the range if this series represents multiple values. GetLeadingBitCount(ones bool) BitCount // GetTrailingBitCount returns the number of consecutive trailing one or zero-bits. // If ones is true, returns the number of consecutive trailing zero bits. // Otherwise, returns the number of consecutive trailing one-bits. // // This method applies to the lower value of the range if this series represents multiple values. GetTrailingBitCount(ones bool) BitCount // ToFullString produces a string with no compressed segments and all segments of full length with leading zeros. ToFullString() string // ToPrefixLenString returns a string with a CIDR network prefix length if this address has a network prefix length. // For IPv6, a zero host section will be compressed with "::". For IPv4 the string is equivalent to the canonical string. ToPrefixLenString() string // ToSubnetString produces a string with specific formats for subnets. // The subnet string looks like "1.2.*.*" or "1:2::/16". // // In the case of IPv4, this means that wildcards are used instead of a network prefix when a network prefix has been supplied. // In the case of IPv6, when a network prefix has been supplied, the prefix will be shown and the host section will be compressed with "::". ToSubnetString() string // ToCanonicalWildcardString produces a string similar to the canonical string but avoids the CIDR prefix length. // Series with a network prefix length will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix length notation. // IPv6 series will be compressed according to the canonical representation. ToCanonicalWildcardString() string // ToCompressedWildcardString produces a string similar to ToNormalizedWildcardString, avoiding the CIDR prefix, but with full IPv6 segment compression as well, including single zero-segments. // For IPv4 it is the same as ToNormalizedWildcardString. ToCompressedWildcardString() string // ToSegmentedBinaryString writes this IP address segment series as segments of binary values preceded by the "0b" prefix. ToSegmentedBinaryString() string // ToSQLWildcardString create a string similar to that from toNormalizedWildcardString except that // it uses SQL wildcards. It uses '%' instead of '*' and also uses the wildcard '_'. ToSQLWildcardString() string // ToReverseDNSString generates the reverse-DNS lookup string, // returning an error if this address series is an IPv6 multiple-valued section for which the range cannot be represented. // For "8.255.4.4" it is "4.4.255.8.in-addr.arpa". // For "2001:db8::567:89ab" it is "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa". ToReverseDNSString() (string, addrerr.IncompatibleAddressError) } var _, _ IPAddressSegmentSeries = &IPAddress{}, &IPAddressSection{} // IPv6AddressSegmentSeries serves as a common interface to all IPv6 address sections and IPv6 addresses. type IPv6AddressSegmentSeries interface { IPAddressSegmentSeries // GetTrailingSection returns an ending subsection of the full address or address section GetTrailingSection(index int) *IPv6AddressSection // GetSubSection returns a subsection of the full address or address section GetSubSection(index, endIndex int) *IPv6AddressSection // GetNetworkSection returns an address section containing the segments with the network of the series, the prefix bits. // The returned section will have only as many segments as needed as determined by the existing CIDR network prefix length. // // If this series has no CIDR prefix length, the returned network section will // be the entire series as a prefixed section with prefix length matching the address bit length. GetNetworkSection() *IPv6AddressSection // GetHostSection returns a section containing the segments with the host of the series, the bits beyond the CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. // // If this series has no prefix length, the returned host section will be the full section. GetHostSection() *IPv6AddressSection // GetNetworkSectionLen returns a section containing the segments with the network of the series, the prefix bits according to the given prefix length. // The returned section will have only as many segments as needed to contain the network. // // The new section will be assigned the given prefix length, // unless the existing prefix length is smaller, in which case the existing prefix length will be retained. GetNetworkSectionLen(BitCount) *IPv6AddressSection // GetHostSectionLen returns a section containing the segments with the host of the series, the bits beyond the given CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. GetHostSectionLen(BitCount) *IPv6AddressSection // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as the receiver. GetSegments() []*IPv6AddressSegment // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. CopySegments(segs []*IPv6AddressSegment) (count int) // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. CopySubSegments(start, end int, segs []*IPv6AddressSegment) (count int) // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. GetSegment(index int) *IPv6AddressSegment } var _, _, _ IPv6AddressSegmentSeries = &IPv6Address{}, &IPv6AddressSection{}, &EmbeddedIPv6AddressSection{} // IPv4AddressSegmentSeries serves as a common interface to all IPv4 address sections and IPv4 addresses. type IPv4AddressSegmentSeries interface { IPAddressSegmentSeries // GetTrailingSection returns an ending subsection of the full address section. GetTrailingSection(index int) *IPv4AddressSection // GetSubSection returns a subsection of the full address section. GetSubSection(index, endIndex int) *IPv4AddressSection // GetNetworkSection returns an address section containing the segments with the network of the series, the prefix bits. // The returned section will have only as many segments as needed as determined by the existing CIDR network prefix length. // // If this series has no CIDR prefix length, the returned network section will // be the entire series as a prefixed section with prefix length matching the address bit length. GetNetworkSection() *IPv4AddressSection // GetHostSection returns a section containing the segments with the host of the series, the bits beyond the CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. // // If this series has no prefix length, the returned host section will be the full section. GetHostSection() *IPv4AddressSection // GetNetworkSectionLen returns a section containing the segments with the network of the series, the prefix bits according to the given prefix length. // The returned section will have only as many segments as needed to contain the network. // // The new section will be assigned the given prefix length, // unless the existing prefix length is smaller, in which case the existing prefix length will be retained. GetNetworkSectionLen(BitCount) *IPv4AddressSection // GetHostSectionLen returns a section containing the segments with the host of the series, the bits beyond the given CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. GetHostSectionLen(BitCount) *IPv4AddressSection // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as the receiver. GetSegments() []*IPv4AddressSegment // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. CopySegments(segs []*IPv4AddressSegment) (count int) // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. CopySubSegments(start, end int, segs []*IPv4AddressSegment) (count int) // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. GetSegment(index int) *IPv4AddressSegment } var _, _ IPv4AddressSegmentSeries = &IPv4Address{}, &IPv4AddressSection{} // MACAddressSegmentSeries serves as a common interface to all MAC address sections and MAC addresses. type MACAddressSegmentSeries interface { AddressSegmentSeries // GetTrailingSection returns an ending subsection of the full address section. GetTrailingSection(index int) *MACAddressSection // GetSubSection returns a subsection of the full address section. GetSubSection(index, endIndex int) *MACAddressSection // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as the receiver. GetSegments() []*MACAddressSegment // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. CopySegments(segs []*MACAddressSegment) (count int) // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. CopySubSegments(start, end int, segs []*MACAddressSegment) (count int) // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. GetSegment(index int) *MACAddressSegment } var _, _ MACAddressSegmentSeries = &MACAddress{}, &MACAddressSection{} // AddressSectionType represents any address section // that can be converted to/from the base type AddressSection, // including [AddressSection], [IPAddressSection], [IPv4AddressSection], [IPv6AddressSection], and [MACAddressSection]. type AddressSectionType interface { StandardDivGroupingType // Equal returns whether the given address section is equal to this address section. // Two address sections are equal if they represent the same set of sections. // They must match: // - type/version (IPv4, IPv6, MAC, etc.) // - segment counts // - bits per segment // - segment value ranges // Prefix lengths are ignored. Equal(AddressSectionType) bool // Contains returns whether this is same type and version as the given address section and whether it contains all values in the given section. // // Sections must also have the same number of segments to be comparable, otherwise false is returned. Contains(AddressSectionType) bool // PrefixEqual determines if the given section matches this section up to the prefix length of this section. // It returns whether the argument section has the same address section prefix values as this. // // The entire prefix of this section must be present in the other section to be comparable. PrefixEqual(AddressSectionType) bool // PrefixContains returns whether the prefix values in the given address section // are prefix values in this address section, using the prefix length of this section. // If this address section has no prefix length, the entire address is compared. // // It returns whether the prefix of this address contains all values of the same prefix length in the given address. // // All prefix bits of this section must be present in the other section to be comparable. PrefixContains(AddressSectionType) bool // ToSectionBase converts to an AddressSection, a polymorphic type usable with all address sections. // // ToSectionBase implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. ToSectionBase() *AddressSection } //Note: if we had an IPAddressSectionType we could add Wrap() WrappedIPAddressSection to it, but I guess not much else. var _, _, _, _, _ AddressSectionType = &AddressSection{}, &IPAddressSection{}, &IPv4AddressSection{}, &IPv6AddressSection{}, &MACAddressSection{} // AddressType represents any address, all of which can be represented by the base type [Address]. // This includes [IPAddress], [IPv4Address], [IPv6Address], and [MACAddress]. // You must use the pointer types *Address, *IPAddress, *IPv4Address, *IPv6Address, and *MACAddress when implementing AddressType. // It can be useful as a parameter for functions to take any address type, while inside the function you can convert to [Address] using ToAddressBase. type AddressType interface { AddressSegmentSeries // Equal returns whether the given address or subnet is equal to this address or subnet. // Two address instances are equal if they represent the same set of addresses. Equal(AddressType) bool // Contains returns whether this is same type and version as the given address or subnet and whether it contains all addresses in the given address or subnet. Contains(AddressType) bool // PrefixEqual determines if the given address matches this address up to the prefix length of this address. // If this address has no prefix length, the entire address is compared. // // It returns whether the two addresses share the same range of prefix values. PrefixEqual(AddressType) bool // PrefixContains returns whether the prefix values in the given address or subnet // are prefix values in this address or subnet, using the prefix length of this address or subnet. // If this address has no prefix length, the entire address is compared. // // It returns whether the prefix of this address contains all values of the same prefix length in the given address. PrefixContains(AddressType) bool // ToAddressBase converts to an Address, a polymorphic type usable with all addresses and subnets. // // ToAddressBase implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. ToAddressBase() *Address } var _, _ AddressType = &Address{}, &MACAddress{} // IPAddressRange represents all IPAddress instances and all IPAddress sequential range instances. type IPAddressRange interface { // GetIPVersion returns the IP version of this IP address range GetIPVersion() IPVersion // GetLowerIPAddress returns the address in the subnet or address range with the lowest numeric value, // which will be the receiver if it represents a single address. // For example, for "1.2-3.4.5-6", the series "1.2.4.5" is returned. GetLowerIPAddress() *IPAddress // GetUpperIPAddress returns the address in the subnet or address range with the highest numeric value, // which will be the receiver if it represents a single address. // For example, for the subnet "1.2-3.4.5-6", the address "1.3.4.6" is returned. GetUpperIPAddress() *IPAddress // CopyNetIP copies the value of the lowest individual address in the subnet or address range into a net.IP. // // If the value can fit in the given net.IP slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. CopyNetIP(bytes net.IP) net.IP // CopyUpperNetIP copies the value of the highest individual address in the subnet or address range into a net.IP. // // If the value can fit in the given net.IP slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. CopyUpperNetIP(bytes net.IP) net.IP // GetNetIP returns the lowest address in this subnet or address range as a net.IP. GetNetIP() net.IP // GetUpperNetIP returns the highest address in this subnet or address range as a net.IP. GetUpperNetIP() net.IP // GetNetNetIPAddr returns the lowest address in this subnet or address range as a netip.Addr. GetNetNetIPAddr() netip.Addr // GetUpperNetNetIPAddr returns the highest address in this subnet or address range as a netip.Addr. GetUpperNetNetIPAddr() netip.Addr // IsSequential returns whether the address item represents a range of addresses that are sequential. // // IP Address sequential ranges are sequential by definition. // // Generally, for a subnet this means that any segment covering a range of values must be followed by segments that are full range, covering all values. // // Individual addresses are sequential and CIDR prefix blocks are sequential. // The subnet "1.2.3-4.5" is not sequential, since the two addresses it represents, "1.2.3.5" and "1.2.4.5", are not ("1.2.3.6" is in-between the two but not in the subnet). IsSequential() bool } var _, _, _, _, _, _ IPAddressRange = &IPAddress{}, &IPv4Address{}, &IPv6Address{}, &SequentialRange[*IPAddress]{}, &SequentialRange[*IPv4Address]{}, &SequentialRange[*IPv6Address]{} // IPAddressType represents any IP address, all of which can be represented by the base type [IPAddress]. // This includes [IPv4Address] and [IPv6Address]. // You must use the pointer types *IPAddress, *IPv4Address, and *IPv6Address when implementing IPAddressType. type IPAddressType interface { AddressType IPAddressRange // Wrap wraps this IP address, returning a WrappedIPAddress, an implementation of ExtendedIPSegmentSeries, // which can be used to write code that works with both IP addresses and IP address sections. Wrap() WrappedIPAddress // ToIP converts to an IPAddress, a polymorphic type usable with all IP addresses and subnets. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. ToIP() *IPAddress // ToAddressString retrieves or generates an IPAddressString instance for this IP address. // This may be the IPAddressString this instance was generated from, if it was generated from an IPAddressString. // // In general, users are intended to create IP address instances from IPAddressString instances, // while the reverse direction, calling this method, is generally not encouraged and not useful, except under specific circumstances. // // Those specific circumstances may include when maintaining a collection of HostIdentifierString or IPAddressString instances. ToAddressString() *IPAddressString } var _, _, _ IPAddressType = &IPAddress{}, &IPv4Address{}, &IPv6Address{} // IPAddressSeqRangeType represents any IP address sequential range, all of which can be represented by the base type IPAddressSeqRange. // This includes IPv4AddressSeqRange and IPv6AddressSeqRange. type IPAddressSeqRangeType interface { AddressItem IPAddressRange // ContainsRange returns whether all the addresses in the given sequential range are also contained in this sequential range. ContainsRange(IPAddressSeqRangeType) bool // Contains returns whether this range contains all IP addresses in the given address or subnet. Contains(IPAddressType) bool // Equal returns whether the given sequential address range is equal to this sequential address range. // Two sequential address ranges are equal if their lower and upper range boundaries are equal. Equal(IPAddressSeqRangeType) bool // ToCanonicalString produces a canonical string for the address range. // It has the format "lower -> upper" where lower and upper are the canonical strings for the lowest and highest addresses in the range, given by GetLower and GetUpper. ToCanonicalString() string // ToNormalizedString produces a normalized string for the address range. // It has the format "lower -> upper" where lower and upper are the normalized strings for the lowest and highest addresses in the range, given by GetLower and GetUpper. ToNormalizedString() string // ToIP converts to an IPAddressSeqRange, a polymorphic type usable with all IP address sequential ranges. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. ToIP() *SequentialRange[*IPAddress] } var _, _, _ IPAddressSeqRangeType = &SequentialRange[*IPAddress]{}, &SequentialRange[*IPv4Address]{}, &SequentialRange[*IPv6Address]{} // HostIdentifierString represents a string that is used to identify a host. type HostIdentifierString interface { // ToNormalizedString provides a normalized String representation for the host identified by this HostIdentifierString instance. ToNormalizedString() string // IsValid returns whether the wrapped string is a valid identifier for a host. IsValid() bool // Wrap wraps the identifier string into the extended type that is polymorphic with other identifier strings. Wrap() ExtendedIdentifierString fmt.Stringer fmt.Formatter } var _, _, _ HostIdentifierString = &IPAddressString{}, &MACAddressString{}, &HostName{} ipaddress-go-1.5.4/ipaddr/frameworkipwrappers.go000066400000000000000000001661571440250641600220300ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) // ExtendedIPSegmentSeries wraps either an [IPAddress] or [IPAddressSection]. // ExtendedIPSegmentSeries can be used to write code that works with both IP addresses and IP address sections, // going further than [IPAddressSegmentSeries] to offer additional methods, methods with the series types in their signature. type ExtendedIPSegmentSeries interface { IPAddressSegmentSeries // Unwrap returns the wrapped IP address or IP address section as an interface, IPAddressSegmentSeries. Unwrap() IPAddressSegmentSeries // Equal returns whether the given address series is equal to this address series. // Two address series are equal if they represent the same set of series. // Both must be equal addresses or both must be equal sections. Equal(ExtendedIPSegmentSeries) bool // Contains returns whether this is same type and version as the given address series and whether it contains all values in the given series. // // Series must also have the same number of segments to be comparable, otherwise false is returned. Contains(ExtendedIPSegmentSeries) bool // GetSection returns the backing section for this series, comprising all segments. GetSection() *IPAddressSection // GetTrailingSection returns an ending subsection of the full address section. GetTrailingSection(index int) *IPAddressSection // GetSubSection returns a subsection of the full address section. GetSubSection(index, endIndex int) *IPAddressSection // GetNetworkSection returns an address section containing the segments with the network of the series, the prefix bits. // The returned section will have only as many segments as needed as determined by the existing CIDR network prefix length. // // If this series has no CIDR prefix length, the returned network section will // be the entire series as a prefixed section with prefix length matching the address bit length. GetNetworkSection() *IPAddressSection // GetHostSection returns a section containing the segments with the host of the series, the bits beyond the CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. // // If this series has no prefix length, the returned host section will be the full section. GetHostSection() *IPAddressSection // GetNetworkSectionLen returns a section containing the segments with the network of the series, the prefix bits according to the given prefix length. // The returned section will have only as many segments as needed to contain the network. // // The new section will be assigned the given prefix length, // unless the existing prefix length is smaller, in which case the existing prefix length will be retained. GetNetworkSectionLen(BitCount) *IPAddressSection // GetHostSectionLen returns a section containing the segments with the host of the series, the bits beyond the given CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. GetHostSectionLen(BitCount) *IPAddressSection // GetNetworkMask returns the network mask associated with the CIDR network prefix length of this series. // If this series has no prefix length, then the all-ones mask is returned. GetNetworkMask() ExtendedIPSegmentSeries // GetHostMask returns the host mask associated with the CIDR network prefix length of this series. // If this series has no prefix length, then the all-ones mask is returned. GetHostMask() ExtendedIPSegmentSeries // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. GetSegment(index int) *IPAddressSegment // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as this section. GetSegments() []*IPAddressSegment // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. CopySegments(segs []*IPAddressSegment) (count int) // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. CopySubSegments(start, end int, segs []*IPAddressSegment) (count int) // IsIPv4 returns true if this series originated as an IPv4 series. If so, use ToIPv4 to convert back to the IPv4-specific type. IsIPv4() bool // IsIPv6 returns true if this series originated as an IPv6 series. If so, use ToIPv6 to convert back to the IPv6-specific type. IsIPv6() bool // ToIPv4 converts to an IPv4AddressSegmentSeries if this series originated as an IPv4 series. // If not, ToIPv4 returns nil. // // ToIPv4 implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. ToIPv4() IPv4AddressSegmentSeries // ToIPv6 converts to an IPv4AddressSegmentSeries if this series originated as an IPv6 series. // If not, ToIPv6 returns nil. // // ToIPv6 implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. ToIPv6() IPv6AddressSegmentSeries // ToBlock creates a new series block by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. ToBlock(segmentIndex int, lower, upper SegInt) ExtendedIPSegmentSeries // ToPrefixBlock returns the series with the same prefix as this series while the remaining bits span all values. // The series will be the block of all series with the same prefix. // // If this series has no prefix, this series is returned. ToPrefixBlock() ExtendedIPSegmentSeries // ToPrefixBlockLen returns the series with the same prefix of the given length as this series while the remaining bits span all values. // The returned series will be the block of all series with the same prefix. ToPrefixBlockLen(BitCount) ExtendedIPSegmentSeries // ToZeroHostLen converts the series to one in which all individual series have a host of zero, // the host being the bits following the given prefix length. // If this series has the same prefix length, then the returned one will too, otherwise the returned series will have no prefix length. // // This returns an error if the series is a range which cannot be converted to a range in which all series have zero hosts, // because the conversion results in a segment that is not a sequential range of values. ToZeroHostLen(BitCount) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) // ToZeroHost converts the series to one in which all individual series have a host of zero, // the host being the bits following the prefix length. // If the series has no prefix length, then it returns an all-zero series. // // The returned series will have the same prefix length. // // For instance, the zero host of "1.2.3.4/16" is the individual address "1.2.0.0/16". // // This returns an error if the series is a range which cannot be converted to a range in which all individual elements have zero hosts, // because the conversion results in a series segment that is not a sequential range of values. ToZeroHost() (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) // ToMaxHostLen converts the series to one in which all individual series have a host of all one-bits, the max host, // the host being the bits following the given prefix length. // If this series has the same prefix length, then the resulting series will too, otherwise the resulting series will have no prefix length. // // For instance, the zero host of "1.2.3.4" for the prefix length of 16 is the address "1.2.255.255". // // This returns an error if the series is a range which cannot be converted to a range in which all individual elements have max hosts, // because the conversion results in a series segment that is not a sequential range of values. ToMaxHostLen(BitCount) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) // ToMaxHost converts the series to one in which all individual series have a host of all one-bits, the max value, // the host being the bits following the prefix length. // If the series has no prefix length, then it returns an all-ones series, the max series. // // The returned series will have the same prefix length. // // For instance, the max host of "1.2.3.4/16" gives the broadcast address "1.2.255.255/16". // // This returns an error if the series is a range which cannot be converted to a range in which all individual elements have max hosts, // because the conversion results in a series segment that is not a sequential range of values. ToMaxHost() (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) // ToZeroNetwork converts the series to one in which all individual addresses or address sections have a network of zero, // the network being the bits within the prefix length. // If the series has no prefix length, then it returns an all-zero series. // // The returned series will have the same prefix length. ToZeroNetwork() ExtendedIPSegmentSeries // Increment returns the item that is the given increment upwards into the range, // with the increment of 0 returning the first in the range. // // If the increment i matches or exceeds the range count c, then i - c + 1 // is added to the upper item of the range. // An increment matching the count gives you the item just above the highest in the range. // // If the increment is negative, it is added to the lowest of the range. // To get the item just below the lowest of the range, use the increment -1. // // If this represents just a single value, the item is simply incremented by the given increment, positive or negative. // // If this item represents multiple values, a positive increment i is equivalent i + 1 values from the iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the count is equivalent to the same number of iterator values preceding the last value of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On overflow or underflow, Increment returns nil. Increment(int64) ExtendedIPSegmentSeries // IncrementBoundary returns the item that is the given increment from the range boundaries of this item. // // If the given increment is positive, adds the value to the highest (GetUpper) in the range to produce a new item. // If the given increment is negative, adds the value to the lowest (GetLower) in the range to produce a new item. // If the increment is zero, returns this. // // If this represents just a single value, this item is simply incremented by the given increment value, positive or negative. // // On overflow or underflow, IncrementBoundary returns nil. IncrementBoundary(int64) ExtendedIPSegmentSeries // GetLower returns the series in the range with the lowest numeric value, // which will be the same series if it represents a single value. // For example, for "1.2-3.4.5-6", the series "1.2.4.5" is returned. GetLower() ExtendedIPSegmentSeries // GetUpper returns the series in the range with the highest numeric value, // which will be the same series if it represents a single value. // For example, for the subnet "1.2-3.4.5-6", the address "1.3.4.6" is returned. GetUpper() ExtendedIPSegmentSeries // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this series. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such series - it is required that the range of values match the range of a prefix block. // If there is no such series, then nil is returned. AssignPrefixForSingleBlock() ExtendedIPSegmentSeries // AssignMinPrefixForBlock returns an equivalent series, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this series. // // In other words, this method assigns a prefix length to this series matching the largest prefix block in this series. AssignMinPrefixForBlock() ExtendedIPSegmentSeries // Iterator provides an iterator to iterate through the individual series of this series. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual series. // // Call IsMultiple to determine if this instance represents multiple series, or GetCount for the count. Iterator() Iterator[ExtendedIPSegmentSeries] // PrefixIterator provides an iterator to iterate through the individual prefixes of this series, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this series. // // If the series has no prefix length, then this is equivalent to Iterator. PrefixIterator() Iterator[ExtendedIPSegmentSeries] // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this series. // Each iterated series will be a prefix block with the same prefix length as this series. // // If this series has no prefix length, then this is equivalent to Iterator. PrefixBlockIterator() Iterator[ExtendedIPSegmentSeries] // SequentialBlockIterator iterates through the sequential series that make up this series. // // Practically, this means finding the count of segments for which the segments that follow are not full range, and then using BlockIterator with that segment count. // // Use GetSequentialBlockCount to get the number of iterated elements. SequentialBlockIterator() Iterator[ExtendedIPSegmentSeries] // BlockIterator Iterates through the series that can be obtained by iterating through all the upper segments up to the given segment count. // The segments following remain the same in all iterated series. BlockIterator(segmentCount int) Iterator[ExtendedIPSegmentSeries] // SpanWithPrefixBlocks returns an array of prefix blocks that spans the same set of individual series as this address series. SpanWithPrefixBlocks() []ExtendedIPSegmentSeries // SpanWithSequentialBlocks produces the smallest slice of sequential blocks that cover the same set of individual series as this series. // // This slice can be shorter than that produced by SpanWithPrefixBlocks and is never longer. SpanWithSequentialBlocks() []ExtendedIPSegmentSeries // CoverWithPrefixBlock returns the minimal-size prefix block that covers all the values in this series. // The resulting block will have a larger series count than this, unless this series is already a prefix block. CoverWithPrefixBlock() ExtendedIPSegmentSeries // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the series. // // If this series has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. AdjustPrefixLen(BitCount) ExtendedIPSegmentSeries // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the series. // // If this series has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. AdjustPrefixLenZeroed(BitCount) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the series. // The provided prefix length will be adjusted to these boundaries if necessary. SetPrefixLen(BitCount) ExtendedIPSegmentSeries // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the series. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this series has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this series has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. SetPrefixLenZeroed(BitCount) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) // WithoutPrefixLen provides the same address series but with no prefix length. The values remain unchanged. WithoutPrefixLen() ExtendedIPSegmentSeries // ReverseBytes returns a new segment series with the bytes reversed. Any prefix length is dropped. // // If each segment is more than 1 byte long, and the bytes within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, then this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. ReverseBytes() (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) // ReverseBits returns a new segment series with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. ReverseBits(perByte bool) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) // ReverseSegments returns a new series with the segments reversed. ReverseSegments() ExtendedIPSegmentSeries // ToCustomString creates a customized string from this series according to the given string option parameters. ToCustomString(stringOptions addrstr.IPStringOptions) string } // WrappedIPAddress is the implementation of ExtendedIPSegmentSeries for IP addresses. type WrappedIPAddress struct { *IPAddress } // Unwrap returns the wrapped address as an interface, IPAddressSegmentSeries. func (addr WrappedIPAddress) Unwrap() IPAddressSegmentSeries { res := addr.IPAddress if res == nil { return nil } return res } // ToIPv4 converts to an IPv4AddressSegmentSeries if this address originated as an IPv4 section. // If not, ToIPv4 returns nil. // // ToIPv4 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr WrappedIPAddress) ToIPv4() IPv4AddressSegmentSeries { return addr.IPAddress.ToIPv4() } // ToIPv6 converts to an IPv6AddressSegmentSeries if this address originated as an IPv6 section. // If not, ToIPv6 returns nil. // // ToIPv6 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr WrappedIPAddress) ToIPv6() IPv6AddressSegmentSeries { return addr.IPAddress.ToIPv6() } // GetNetworkMask returns the network mask associated with the CIDR network prefix length of this address or subnet. // If this series has no prefix length, then the all-ones mask is returned. func (addr WrappedIPAddress) GetNetworkMask() ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.GetNetworkMask()) } // GetHostMask returns the host mask associated with the CIDR network prefix length of this address or subnet. // If this series has no prefix length, then the all-ones mask is returned. func (addr WrappedIPAddress) GetHostMask() ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.GetHostMask()) } // SequentialBlockIterator iterates through the sequential series that make up this series. // // Practically, this means finding the count of segments for which the segments that follow are not full range, and then using BlockIterator with that segment count. // // Use GetSequentialBlockCount to get the number of iterated elements. func (addr WrappedIPAddress) SequentialBlockIterator() Iterator[ExtendedIPSegmentSeries] { return ipaddressSeriesIterator{addr.IPAddress.SequentialBlockIterator()} } // BlockIterator Iterates through the series that can be obtained by iterating through all the upper segments up to the given segment count. // The segments following remain the same in all iterated series. func (addr WrappedIPAddress) BlockIterator(segmentCount int) Iterator[ExtendedIPSegmentSeries] { return ipaddressSeriesIterator{addr.IPAddress.BlockIterator(segmentCount)} } // Iterator provides an iterator to iterate through the individual series of this series. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual series. // // Call IsMultiple to determine if this instance represents multiple series, or GetCount for the count. func (addr WrappedIPAddress) Iterator() Iterator[ExtendedIPSegmentSeries] { return ipaddressSeriesIterator{addr.IPAddress.Iterator()} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this series, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this series. // // If the series has no prefix length, then this is equivalent to Iterator. func (addr WrappedIPAddress) PrefixIterator() Iterator[ExtendedIPSegmentSeries] { return ipaddressSeriesIterator{addr.IPAddress.PrefixIterator()} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this series. // Each iterated series will be a prefix block with the same prefix length as this series. // // If this series has no prefix length, then this is equivalent to Iterator. func (addr WrappedIPAddress) PrefixBlockIterator() Iterator[ExtendedIPSegmentSeries] { return ipaddressSeriesIterator{addr.IPAddress.PrefixBlockIterator()} } // ToBlock creates a new series block by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (addr WrappedIPAddress) ToBlock(segmentIndex int, lower, upper SegInt) ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.ToBlock(segmentIndex, lower, upper)) } // ToPrefixBlockLen returns the series with the same prefix of the given length as this series while the remaining bits span all values. // The returned series will be the block of all series with the same prefix. func (addr WrappedIPAddress) ToPrefixBlockLen(bitCount BitCount) ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.ToPrefixBlockLen(bitCount)) } // ToPrefixBlock returns the series with the same prefix as this series while the remaining bits span all values. // The series will be the block of all series with the same prefix. // // If this series has no prefix, this series is returned. func (addr WrappedIPAddress) ToPrefixBlock() ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.ToPrefixBlock()) } // ToZeroHostLen converts the subnet to one in which all individual addresses have a host of zero, // the host being the bits following the given prefix length. // If this address has the same prefix length, then the returned one will too, otherwise the returned series will have no prefix length. // // This returns an error if the subnet is a range which cannot be converted to a range in which all addresses have zero hosts, // because the conversion results in a segment that is not a sequential range of values. func (addr WrappedIPAddress) ToZeroHostLen(bitCount BitCount) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPAddrWithErr(addr.IPAddress.ToZeroHostLen(bitCount)) //in IPAddress/Section } // ToZeroHost converts the subnet to one in which all individual addresses have a host of zero, // the host being the bits following the prefix length. // If the subnet has no prefix length, then it returns an all-zero series. // // The returned series will have the same prefix length. // // For instance, the zero host of "1.2.3.4/16" is the individual address "1.2.0.0/16". // // This returns an error if the series is a range which cannot be converted to a range in which all individual elements have zero hosts, // because the conversion results in a series segment that is not a sequential range of values. func (addr WrappedIPAddress) ToZeroHost() (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPAddrWithErr(addr.IPAddress.ToZeroHost()) // in IPAddress/Section/Segment } // ToMaxHostLen converts the address or subnet to one in which all individual addresses have a host of all one-bits, the max host, // the host being the bits following the given prefix length. // If this address or subnet has the same prefix length, then the resulting one will too, otherwise the resulting series will have no prefix length. // // For instance, the zero host of "1.2.3.4" for the prefix length of 16 is the address "1.2.255.255". // // This returns an error if the address or subnet is a range which cannot be converted to a range in which all individual addresses have max hosts, // because the conversion results in a series segment that is not a sequential range of values. func (addr WrappedIPAddress) ToMaxHostLen(bitCount BitCount) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPAddrWithErr(addr.IPAddress.ToMaxHostLen(bitCount)) } // ToMaxHost converts the subnet to one in which all individual addresses have a host of all one-bits, the max value, // the host being the bits following the prefix length. // If the subnet has no prefix length, then it returns an all-ones address, the max address. // // The returned series will have the same prefix length. // // For instance, the max host of "1.2.3.4/16" gives the broadcast address "1.2.255.255/16". // // This returns an error if the series is a range which cannot be converted to a range in which all individual elements have max hosts, // because the conversion results in a series segment that is not a sequential range of values. func (addr WrappedIPAddress) ToMaxHost() (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPAddrWithErr(addr.IPAddress.ToMaxHost()) } // ToZeroNetwork converts the address or subnet to one in which all individual addresses have a network of zero, // the network being the bits within the prefix length. // If the address or subnet has no prefix length, then it returns an all-zero address. // // The returned address or subnet will have the same prefix length. func (addr WrappedIPAddress) ToZeroNetwork() ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.ToZeroNetwork()) //IPAddress/Section. ToZeroHost() is in IPAddress/Section/Segment } // Increment returns the item that is the given increment upwards into the range, // with the increment of 0 returning the first in the range. // // If the increment i matches or exceeds the range count c, then i - c + 1 // is added to the upper item of the range. // An increment matching the count gives you the item just above the highest in the range. // // If the increment is negative, it is added to the lowest of the range. // To get the item just below the lowest of the range, use the increment -1. // // If this represents just a single value, the item is simply incremented by the given increment, positive or negative. // // If this item represents multiple values, a positive increment i is equivalent i + 1 values from the iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the count is equivalent to the same number of iterator values preceding the last value of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On overflow or underflow, Increment returns nil. func (addr WrappedIPAddress) Increment(i int64) ExtendedIPSegmentSeries { return convIPAddrToIntf(addr.IPAddress.Increment(i)) } // IncrementBoundary returns the item that is the given increment from the range boundaries of this item. // // If the given increment is positive, adds the value to the highest (GetUpper) in the range to produce a new item. // If the given increment is negative, adds the value to the lowest (GetLower) in the range to produce a new item. // If the increment is zero, returns this. // // If this represents just a single value, this item is simply incremented by the given increment value, positive or negative. // // On overflow or underflow, IncrementBoundary returns nil. func (addr WrappedIPAddress) IncrementBoundary(i int64) ExtendedIPSegmentSeries { return convIPAddrToIntf(addr.IPAddress.IncrementBoundary(i)) } // GetLower returns the series in the range with the lowest numeric value, // which will be the same series if it represents a single value. // For example, for "1.2-3.4.5-6", the series "1.2.4.5" is returned. func (addr WrappedIPAddress) GetLower() ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.GetLower()) } // GetUpper returns the series in the range with the highest numeric value, // which will be the same series if it represents a single value. // For example, for the subnet "1.2-3.4.5-6", the address "1.3.4.6" is returned. func (addr WrappedIPAddress) GetUpper() ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.GetUpper()) } // GetSection returns the backing section for this series, comprising all segments. func (addr WrappedIPAddress) GetSection() *IPAddressSection { return addr.IPAddress.GetSection() } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this series. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such series - it is required that the range of values match the range of a prefix block. // If there is no such series, then nil is returned. func (addr WrappedIPAddress) AssignPrefixForSingleBlock() ExtendedIPSegmentSeries { return convIPAddrToIntf(addr.IPAddress.AssignPrefixForSingleBlock()) } // AssignMinPrefixForBlock returns an equivalent series, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this series. // // In other words, this method assigns a prefix length to this series matching the largest prefix block in this series. func (addr WrappedIPAddress) AssignMinPrefixForBlock() ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.AssignMinPrefixForBlock()) } // WithoutPrefixLen provides the same address series but with no prefix length. The values remain unchanged. func (addr WrappedIPAddress) WithoutPrefixLen() ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.WithoutPrefixLen()) } // SpanWithPrefixBlocks returns an array of prefix blocks that spans the same set of individual series as this subnet. func (addr WrappedIPAddress) SpanWithPrefixBlocks() []ExtendedIPSegmentSeries { return addr.IPAddress.spanWithPrefixBlocks() } // SpanWithSequentialBlocks produces the smallest slice of sequential blocks that cover the same set of individual addresses as this subnet. // // This slice can be shorter than that produced by SpanWithPrefixBlocks and is never longer. func (addr WrappedIPAddress) SpanWithSequentialBlocks() []ExtendedIPSegmentSeries { return addr.IPAddress.spanWithSequentialBlocks() } // CoverWithPrefixBlock returns the minimal-size prefix block that covers all the addresses in this subnet. // The resulting block will have a larger subnet size than this, unless this series is already a prefix block. func (addr WrappedIPAddress) CoverWithPrefixBlock() ExtendedIPSegmentSeries { return addr.IPAddress.coverSeriesWithPrefixBlock() } // Contains returns whether this is same type and version as the given address series and whether it contains all values in the given series. // // Series must also have the same number of segments to be comparable, otherwise false is returned. func (addr WrappedIPAddress) Contains(other ExtendedIPSegmentSeries) bool { a, ok := other.Unwrap().(AddressType) return ok && addr.IPAddress.Contains(a) } // Equal returns whether the given address series is equal to this address series. // Two address series are equal if they represent the same set of series. // Both must be equal addresses. func (addr WrappedIPAddress) Equal(other ExtendedIPSegmentSeries) bool { a, ok := other.Unwrap().(AddressType) return ok && addr.IPAddress.Equal(a) } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the series. // The provided prefix length will be adjusted to these boundaries if necessary. func (addr WrappedIPAddress) SetPrefixLen(prefixLen BitCount) ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.SetPrefixLen(prefixLen)) } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the series. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this series has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this series has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr WrappedIPAddress) SetPrefixLenZeroed(prefixLen BitCount) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPAddrWithErr(addr.IPAddress.SetPrefixLenZeroed(prefixLen)) } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the series. // // If this series has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (addr WrappedIPAddress) AdjustPrefixLen(prefixLen BitCount) ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.AdjustPrefixLen(prefixLen)) } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the series. // // If this series has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr WrappedIPAddress) AdjustPrefixLenZeroed(prefixLen BitCount) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPAddrWithErr(addr.IPAddress.AdjustPrefixLenZeroed(prefixLen)) } // ReverseBytes returns a new segment series with the bytes reversed. Any prefix length is dropped. // // If each segment is more than 1 byte long, and the bytes within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, then this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. func (addr WrappedIPAddress) ReverseBytes() (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPAddrWithErr(addr.IPAddress.ReverseBytes()) } // ReverseBits returns a new segment series with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (addr WrappedIPAddress) ReverseBits(perByte bool) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPAddrWithErr(addr.IPAddress.ReverseBits(perByte)) } // ReverseSegments returns a new series with the segments reversed. func (addr WrappedIPAddress) ReverseSegments() ExtendedIPSegmentSeries { return wrapIPAddress(addr.IPAddress.ReverseSegments()) } // WrappedIPAddressSection is the implementation of ExtendedIPSegmentSeries for IP address sections. type WrappedIPAddressSection struct { *IPAddressSection } // Unwrap returns the wrapped address section as an interface, IPAddressSegmentSeries. func (section WrappedIPAddressSection) Unwrap() IPAddressSegmentSeries { res := section.IPAddressSection if res == nil { return nil } return res } // ToIPv4 converts to an IPv4AddressSegmentSeries if this section originated as an IPv4 section. // If not, ToIPv4 returns nil. // // ToIPv4 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section WrappedIPAddressSection) ToIPv4() IPv4AddressSegmentSeries { return section.IPAddressSection.ToIPv4() } // ToIPv6 converts to an IPv6AddressSegmentSeries if this section originated as an IPv6 section. // If not, ToIPv6 returns nil. // // ToIPv6 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section WrappedIPAddressSection) ToIPv6() IPv6AddressSegmentSeries { return section.IPAddressSection.ToIPv6() } // GetNetworkMask returns the network mask associated with the CIDR network prefix length of this address section. // If this series has no prefix length, then the all-ones mask is returned. func (section WrappedIPAddressSection) GetNetworkMask() ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.GetNetworkMask()) } // GetHostMask returns the host mask associated with the CIDR network prefix length of this address section. // If this series has no prefix length, then the all-ones mask is returned. func (section WrappedIPAddressSection) GetHostMask() ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.GetHostMask()) } // SequentialBlockIterator iterates through the sequential series that make up this series. // // Practically, this means finding the count of segments for which the segments that follow are not full range, and then using BlockIterator with that segment count. // // Use GetSequentialBlockCount to get the number of iterated elements. func (section WrappedIPAddressSection) SequentialBlockIterator() Iterator[ExtendedIPSegmentSeries] { return ipSectionSeriesIterator{section.IPAddressSection.SequentialBlockIterator()} } // BlockIterator Iterates through the series that can be obtained by iterating through all the upper segments up to the given segment count. // The segments following remain the same in all iterated series. func (section WrappedIPAddressSection) BlockIterator(segmentCount int) Iterator[ExtendedIPSegmentSeries] { return ipSectionSeriesIterator{section.IPAddressSection.BlockIterator(segmentCount)} } // Iterator provides an iterator to iterate through the individual series of this series. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual series. // // Call IsMultiple to determine if this instance represents multiple series, or GetCount for the count. func (section WrappedIPAddressSection) Iterator() Iterator[ExtendedIPSegmentSeries] { return ipSectionSeriesIterator{section.IPAddressSection.Iterator()} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this series, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this series. // // If the series has no prefix length, then this is equivalent to Iterator. func (section WrappedIPAddressSection) PrefixIterator() Iterator[ExtendedIPSegmentSeries] { return ipSectionSeriesIterator{section.IPAddressSection.PrefixIterator()} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this series. // Each iterated series will be a prefix block with the same prefix length as this series. // // If this series has no prefix length, then this is equivalent to Iterator. func (section WrappedIPAddressSection) PrefixBlockIterator() Iterator[ExtendedIPSegmentSeries] { return ipSectionSeriesIterator{section.IPAddressSection.PrefixBlockIterator()} } // ToBlock creates a new series block by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (section WrappedIPAddressSection) ToBlock(segmentIndex int, lower, upper SegInt) ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.ToBlock(segmentIndex, lower, upper)) } // ToPrefixBlockLen returns the series with the same prefix of the given length as this series while the remaining bits span all values. // The returned series will be the block of all series with the same prefix. func (section WrappedIPAddressSection) ToPrefixBlockLen(bitCount BitCount) ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.ToPrefixBlockLen(bitCount)) } // ToPrefixBlock returns the series with the same prefix as this series while the remaining bits span all values. // The series will be the block of all series with the same prefix. // // If this series has no prefix, this series is returned. func (section WrappedIPAddressSection) ToPrefixBlock() ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.ToPrefixBlock()) } // ToZeroHostLen converts the section to one in which all individual sections have a host of zero, // the host being the bits following the given prefix length. // If this section has the same prefix length, then the returned one will too, otherwise the returned series will have no prefix length. // // This returns an error if the section is a range which cannot be converted to a range in which all individual sections have zero hosts, // because the conversion results in a segment that is not a sequential range of values. func (section WrappedIPAddressSection) ToZeroHostLen(bitCount BitCount) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPSectWithErr(section.IPAddressSection.ToZeroHostLen(bitCount)) } // ToZeroHost converts the section to one in which all individual sections have a host of zero, // the host being the bits following the prefix length. // If the section has no prefix length, then it returns an all-zero section. // // The returned series will have the same prefix length. // // This returns an error if the section is a range which cannot be converted to a range in which all individual elements have zero hosts, // because the conversion results in a segment that is not a sequential range of values. func (section WrappedIPAddressSection) ToZeroHost() (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPSectWithErr(section.IPAddressSection.ToZeroHost()) } // ToMaxHostLen converts the address section to one in which all individual address sections have a host of all one-bits, the max host, // the host being the bits following the given prefix length. // If this address section has the same prefix length, then the resulting series will too, otherwise the resulting series will have no prefix length. // // This returns an error if the address section is a range which cannot be converted to a range in which all individual address sections have max hosts, // because the conversion results in a series segment that is not a sequential range of values. func (section WrappedIPAddressSection) ToMaxHostLen(bitCount BitCount) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPSectWithErr(section.IPAddressSection.ToMaxHostLen(bitCount)) } // ToMaxHost converts the address section to one in which all individual address sections have a host of all one-bits, the max value, // the host being the bits following the prefix length. // If the section has no prefix length, then it returns an all-ones section, the max address section. // // The returned series will have the same prefix length. // // This returns an error if the series is a range which cannot be converted to a range in which all individual elements have max hosts, // because the conversion results in a series segment that is not a sequential range of values. func (section WrappedIPAddressSection) ToMaxHost() (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPSectWithErr(section.IPAddressSection.ToMaxHost()) } // ToZeroNetwork converts the address section to one in which all individual address sections have a network of zero, // the network being the bits within the prefix length. // If the section has no prefix length, then it returns an all-zero series. // // The returned address section will have the same prefix length. func (section WrappedIPAddressSection) ToZeroNetwork() ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.ToZeroNetwork()) } // Increment returns the item that is the given increment upwards into the range, // with the increment of 0 returning the first in the range. // // If the increment i matches or exceeds the range count c, then i - c + 1 // is added to the upper item of the range. // An increment matching the count gives you the item just above the highest in the range. // // If the increment is negative, it is added to the lowest of the range. // To get the item just below the lowest of the range, use the increment -1. // // If this represents just a single value, the item is simply incremented by the given increment, positive or negative. // // If this item represents multiple values, a positive increment i is equivalent i + 1 values from the iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the count is equivalent to the same number of iterator values preceding the last value of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On overflow or underflow, Increment returns nil. func (section WrappedIPAddressSection) Increment(i int64) ExtendedIPSegmentSeries { return convIPSectToIntf(section.IPAddressSection.Increment(i)) } // IncrementBoundary returns the item that is the given increment from the range boundaries of this item. // // If the given increment is positive, adds the value to the highest (GetUpper) in the range to produce a new item. // If the given increment is negative, adds the value to the lowest (GetLower) in the range to produce a new item. // If the increment is zero, returns this. // // If this represents just a single value, this item is simply incremented by the given increment value, positive or negative. // // On overflow or underflow, IncrementBoundary returns nil. func (section WrappedIPAddressSection) IncrementBoundary(i int64) ExtendedIPSegmentSeries { return convIPSectToIntf(section.IPAddressSection.IncrementBoundary(i)) } // GetLower returns the series in the range with the lowest numeric value, // which will be the same series if it represents a single value. // For example, for "1.2-3.4.5-6", the series "1.2.4.5" is returned. func (section WrappedIPAddressSection) GetLower() ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.GetLower()) } // GetUpper returns the series in the range with the highest numeric value, // which will be the same series if it represents a single value. // For example, for the subnet "1.2-3.4.5-6", the address "1.3.4.6" is returned. func (section WrappedIPAddressSection) GetUpper() ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.GetUpper()) } // GetSection returns the backing section for this series, comprising all segments. func (section WrappedIPAddressSection) GetSection() *IPAddressSection { return section.IPAddressSection } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this series. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such series - it is required that the range of values match the range of a prefix block. // If there is no such series, then nil is returned. func (section WrappedIPAddressSection) AssignPrefixForSingleBlock() ExtendedIPSegmentSeries { return convIPSectToIntf(section.IPAddressSection.AssignPrefixForSingleBlock()) } // AssignMinPrefixForBlock returns an equivalent series, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this series. // // In other words, this method assigns a prefix length to this series matching the largest prefix block in this series. func (section WrappedIPAddressSection) AssignMinPrefixForBlock() ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.AssignMinPrefixForBlock()) } // WithoutPrefixLen provides the same address series but with no prefix length. The values remain unchanged. func (section WrappedIPAddressSection) WithoutPrefixLen() ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.WithoutPrefixLen()) } // SpanWithPrefixBlocks returns an array of prefix blocks that spans the same set of individual series as this subnet section. func (section WrappedIPAddressSection) SpanWithPrefixBlocks() []ExtendedIPSegmentSeries { return section.IPAddressSection.spanWithPrefixBlocks() } // SpanWithSequentialBlocks produces the smallest slice of sequential blocks that cover the same set of individual address sections as this series. // // This slice can be shorter than that produced by SpanWithPrefixBlocks and is never longer. func (section WrappedIPAddressSection) SpanWithSequentialBlocks() []ExtendedIPSegmentSeries { return section.IPAddressSection.spanWithSequentialBlocks() } // CoverWithPrefixBlock returns the minimal-size prefix block that covers all the individual address sections in this section. // The resulting block will have a larger count than this, unless this section is already a prefix block. func (section WrappedIPAddressSection) CoverWithPrefixBlock() ExtendedIPSegmentSeries { return section.IPAddressSection.coverSeriesWithPrefixBlock() } // Contains returns whether this is same type and version as the given address series and whether it contains all values in the given series. // // Series must also have the same number of segments to be comparable, otherwise false is returned. func (section WrappedIPAddressSection) Contains(other ExtendedIPSegmentSeries) bool { s, ok := other.Unwrap().(AddressSectionType) return ok && section.IPAddressSection.Contains(s) } // Equal returns whether the given address series is equal to this address series. // Two address series are equal if they represent the same set of series. // Both must be equal sections. func (section WrappedIPAddressSection) Equal(other ExtendedIPSegmentSeries) bool { s, ok := other.Unwrap().(AddressSectionType) return ok && section.IPAddressSection.Equal(s) } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the series. // The provided prefix length will be adjusted to these boundaries if necessary. func (section WrappedIPAddressSection) SetPrefixLen(prefixLen BitCount) ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.SetPrefixLen(prefixLen)) } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the series. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this series has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this series has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section WrappedIPAddressSection) SetPrefixLenZeroed(prefixLen BitCount) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPSectWithErr(section.IPAddressSection.SetPrefixLenZeroed(prefixLen)) } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the series. // // If this series has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (section WrappedIPAddressSection) AdjustPrefixLen(prefixLen BitCount) ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.AdjustPrefixLen(prefixLen)) } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the series. // // If this series has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section WrappedIPAddressSection) AdjustPrefixLenZeroed(prefixLen BitCount) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPSectWithErr(section.IPAddressSection.AdjustPrefixLenZeroed(prefixLen)) } // ReverseBytes returns a new segment series with the bytes reversed. Any prefix length is dropped. // // If each segment is more than 1 byte long, and the bytes within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, then this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. func (section WrappedIPAddressSection) ReverseBytes() (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPSectWithErr(section.IPAddressSection.ReverseBytes()) } // ReverseBits returns a new segment series with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (section WrappedIPAddressSection) ReverseBits(perByte bool) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { return wrapIPSectWithErr(section.IPAddressSection.ReverseBits(perByte)) } // ReverseSegments returns a new series with the segments reversed. func (section WrappedIPAddressSection) ReverseSegments() ExtendedIPSegmentSeries { return wrapIPSection(section.IPAddressSection.ReverseSegments()) } var _ ExtendedIPSegmentSeries = WrappedIPAddress{} var _ ExtendedIPSegmentSeries = WrappedIPAddressSection{} // In go, a nil value is not converted to a nil interface, it is converted to a non-nil interface instance with underlying value nil func convIPAddrToIntf(addr *IPAddress) ExtendedIPSegmentSeries { if addr == nil { return nil } return wrapIPAddress(addr) } func convIPSectToIntf(sect *IPAddressSection) ExtendedIPSegmentSeries { if sect == nil { return nil } return wrapIPSection(sect) } func wrapIPSectWithErr(section *IPAddressSection, err addrerr.IncompatibleAddressError) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { if err == nil { return wrapIPSection(section), nil } return nil, err } func wrapIPAddrWithErr(addr *IPAddress, err addrerr.IncompatibleAddressError) (ExtendedIPSegmentSeries, addrerr.IncompatibleAddressError) { if err == nil { return wrapIPAddress(addr), nil } return nil, err } func wrapIPAddress(addr *IPAddress) WrappedIPAddress { return WrappedIPAddress{addr} } func wrapIPSection(section *IPAddressSection) WrappedIPAddressSection { return WrappedIPAddressSection{section} } ipaddress-go-1.5.4/ipaddr/frameworkstrwrappers.go000066400000000000000000000071551440250641600222200ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr // ExtendedIdentifierString is a common interface for strings that identify hosts, namely [IPAddressString], [MACAddressString], and [HostName]. type ExtendedIdentifierString interface { HostIdentifierString // GetAddress returns the identified address or nil if none. GetAddress() AddressType // ToAddress returns the identified address or an error. ToAddress() (AddressType, error) // Unwrap returns the wrapped IPAddressString, MACAddressString or HostName as an interface, HostIdentifierString. Unwrap() HostIdentifierString } // WrappedIPAddressString wraps an IPAddressString to get an ExtendedIdentifierString, an extended polymorphic type. type WrappedIPAddressString struct { *IPAddressString } // Unwrap returns the wrapped IPAddressString as an interface, HostIdentifierString. func (str WrappedIPAddressString) Unwrap() HostIdentifierString { res := str.IPAddressString if res == nil { return nil } return res } // ToAddress returns the identified address or an error. func (str WrappedIPAddressString) ToAddress() (AddressType, error) { addr, err := str.IPAddressString.ToAddress() if err != nil { return nil, err } return addr, nil } // GetAddress returns the identified address or nil if none. func (str WrappedIPAddressString) GetAddress() AddressType { if addr := str.IPAddressString.GetAddress(); addr != nil { return addr } return nil } // WrappedMACAddressString wraps a MACAddressString to get an ExtendedIdentifierString. type WrappedMACAddressString struct { *MACAddressString } // Unwrap returns the wrapped MACAddressString as an interface, HostIdentifierString. func (str WrappedMACAddressString) Unwrap() HostIdentifierString { res := str.MACAddressString if res == nil { return nil } return res } // ToAddress returns the identified address or an error. func (str WrappedMACAddressString) ToAddress() (AddressType, error) { addr, err := str.MACAddressString.ToAddress() if err != nil { return nil, err } return addr, nil } // GetAddress returns the identified address or nil if none. func (str WrappedMACAddressString) GetAddress() AddressType { if addr := str.MACAddressString.GetAddress(); addr != nil { return addr } return nil } // WrappedHostName wraps a HostName to get an ExtendedIdentifierString. type WrappedHostName struct { *HostName } // Unwrap returns the wrapped HostName as an interface, HostIdentifierString. func (host WrappedHostName) Unwrap() HostIdentifierString { res := host.HostName if res == nil { return nil } return res } // ToAddress returns the identified address or an error. func (host WrappedHostName) ToAddress() (AddressType, error) { addr, err := host.HostName.ToAddress() if err != nil { return nil, err } return addr, nil } // GetAddress returns the identified address or nil if none. func (host WrappedHostName) GetAddress() AddressType { if addr := host.HostName.GetAddress(); addr != nil { return addr } return nil } var ( _, _, _ ExtendedIdentifierString = WrappedIPAddressString{}, WrappedMACAddressString{}, WrappedHostName{} ) ipaddress-go-1.5.4/ipaddr/frameworkwrappers.go000066400000000000000000001222611440250641600214630ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) // ExtendedSegmentSeries wraps either an Address or AddressSection. // ExtendedSegmentSeries can be used to write code that works with both addresses and address sections, // going further than AddressSegmentSeries to offer additional methods with the series types in their signature. type ExtendedSegmentSeries interface { AddressSegmentSeries // Unwrap returns the wrapped address or address section as an interface, AddressSegmentSeries. Unwrap() AddressSegmentSeries // Equal returns whether the given address series is equal to this address series. // Two address series are equal if they represent the same set of series. // Both must be equal addresses or both must be equal sections. Equal(ExtendedSegmentSeries) bool // Contains returns whether this is same type and version as the given address series and whether it contains all values in the given series. // // Series must also have the same number of segments to be comparable, otherwise false is returned. Contains(ExtendedSegmentSeries) bool // GetSection returns the backing section for this series, comprising all segments. GetSection() *AddressSection // GetTrailingSection returns an ending subsection of the full address section. GetTrailingSection(index int) *AddressSection // GetSubSection returns a subsection of the full address section. GetSubSection(index, endIndex int) *AddressSection // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. GetSegment(index int) *AddressSegment // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as this section. GetSegments() []*AddressSegment // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. CopySegments(segs []*AddressSegment) (count int) // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. CopySubSegments(start, end int, segs []*AddressSegment) (count int) // IsIP returns true if this series originated as an IPv4 or IPv6 series, or a zero-length IP series. If so, use ToIP to convert back to the IP-specific type. IsIP() bool // IsIPv4 returns true if this series originated as an IPv4 series. If so, use ToIPv4 to convert back to the IPv4-specific type. IsIPv4() bool // IsIPv6 returns true if this series originated as an IPv6 series. If so, use ToIPv6 to convert back to the IPv6-specific type. IsIPv6() bool // IsMAC returns true if this series originated as a MAC series. If so, use ToMAC to convert back to the MAC-specific type. IsMAC() bool // ToIP converts to an IPAddressSegmentSeries if this series originated as IPv4 or IPv6, or an implicitly zero-valued IP. // If not, ToIP returns nil. ToIP() IPAddressSegmentSeries // ToIPv4 converts to an IPv4AddressSegmentSeries if this series originated as an IPv4 series. // If not, ToIPv4 returns nil. // // ToIPv4 implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. ToIPv4() IPv4AddressSegmentSeries // ToIPv6 converts to an IPv4AddressSegmentSeries if this series originated as an IPv6 series. // If not, ToIPv6 returns nil. // // ToIPv6 implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. ToIPv6() IPv6AddressSegmentSeries // ToMAC converts to a MACAddressSegmentSeries if this series originated as a MAC series. // If not, ToMAC returns nil. // // ToMAC implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. ToMAC() MACAddressSegmentSeries // ToBlock creates a new series block by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. ToBlock(segmentIndex int, lower, upper SegInt) ExtendedSegmentSeries // ToPrefixBlock returns the series with the same prefix as this series while the remaining bits span all values. // The series will be the block of all series with the same prefix. // // If this series has no prefix, this series is returned. ToPrefixBlock() ExtendedSegmentSeries // ToPrefixBlockLen returns the series with the same prefix of the given length as this series while the remaining bits span all values. // The returned series will be the block of all series with the same prefix. ToPrefixBlockLen(prefLen BitCount) ExtendedSegmentSeries // Increment returns the item that is the given increment upwards into the range, // with the increment of 0 returning the first in the range. // // If the increment i matches or exceeds the range count c, then i - c + 1 // is added to the upper item of the range. // An increment matching the count gives you the item just above the highest in the range. // // If the increment is negative, it is added to the lowest of the range. // To get the item just below the lowest of the range, use the increment -1. // // If this represents just a single value, the item is simply incremented by the given increment, positive or negative. // // If this item represents multiple values, a positive increment i is equivalent i + 1 values from the iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the count is equivalent to the same number of iterator values preceding the last value of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On overflow or underflow, Increment returns nil. Increment(int64) ExtendedSegmentSeries // IncrementBoundary returns the item that is the given increment from the range boundaries of this item. // // If the given increment is positive, adds the value to the highest (GetUpper) in the range to produce a new item. // If the given increment is negative, adds the value to the lowest (GetLower) in the range to produce a new item. // If the increment is zero, returns this. // // If this represents just a single value, this item is simply incremented by the given increment value, positive or negative. // // On overflow or underflow, IncrementBoundary returns nil. IncrementBoundary(int64) ExtendedSegmentSeries // GetLower returns the series in the range with the lowest numeric value, // which will be the same series if it represents a single value. GetLower() ExtendedSegmentSeries // GetUpper returns the series in the range with the highest numeric value, // which will be the same series if it represents a single value. GetUpper() ExtendedSegmentSeries // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this series. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such series - it is required that the range of values match the range of a prefix block. // If there is no such series, then nil is returned. AssignPrefixForSingleBlock() ExtendedSegmentSeries // AssignMinPrefixForBlock returns an equivalent series, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this series. // // In other words, this method assigns a prefix length to this series matching the largest prefix block in this series. AssignMinPrefixForBlock() ExtendedSegmentSeries // Iterator provides an iterator to iterate through the individual series of this series. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual series. // // Call IsMultiple to determine if this instance represents multiple series, or GetCount for the count. Iterator() Iterator[ExtendedSegmentSeries] // PrefixIterator provides an iterator to iterate through the individual prefixes of this series, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this series. // // If the series has no prefix length, then this is equivalent to Iterator. PrefixIterator() Iterator[ExtendedSegmentSeries] // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this series. // Each iterated series will be a prefix block with the same prefix length as this series. // // If this series has no prefix length, then this is equivalent to Iterator. PrefixBlockIterator() Iterator[ExtendedSegmentSeries] // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the series. // // If this series has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. AdjustPrefixLen(BitCount) ExtendedSegmentSeries // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the series. // // If this series has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. AdjustPrefixLenZeroed(BitCount) (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the series. // The provided prefix length will be adjusted to these boundaries if necessary. SetPrefixLen(BitCount) ExtendedSegmentSeries // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the series. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this series has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this series has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. SetPrefixLenZeroed(BitCount) (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) // WithoutPrefixLen provides the same address series but with no prefix length. The values remain unchanged. WithoutPrefixLen() ExtendedSegmentSeries // ReverseBytes returns a new segment series with the bytes reversed. Any prefix length is dropped. // // If each segment is more than 1 byte long, and the bytes within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, then this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. ReverseBytes() (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) // ReverseBits returns a new segment series with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. ReverseBits(perByte bool) (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) // ReverseSegments returns a new series with the segments reversed. ReverseSegments() ExtendedSegmentSeries // ToCustomString creates a customized string from this series according to the given string option parameters. ToCustomString(stringOptions addrstr.StringOptions) string } // WrappedAddress is the implementation of ExtendedSegmentSeries for addresses. type WrappedAddress struct { *Address } // Unwrap returns the wrapped address as an interface, AddressSegmentSeries. func (addr WrappedAddress) Unwrap() AddressSegmentSeries { res := addr.Address if res == nil { return nil } return res } // ToIPv4 converts to an IPv4AddressSegmentSeries if this series originated as an IPv4 series. // If not, ToIPv4 returns nil. // // ToIPv4 implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr WrappedAddress) ToIPv4() IPv4AddressSegmentSeries { return addr.Address.ToIPv4() } // ToIPv6 converts to an IPv4AddressSegmentSeries if this series originated as an IPv6 series. // If not, ToIPv6 returns nil. // // ToIPv6 implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr WrappedAddress) ToIPv6() IPv6AddressSegmentSeries { return addr.Address.ToIPv6() } // ToIP converts to an IP address if this originated as IPv4 or IPv6, or an implicitly zero-valued IP. // If not, ToIP returns nil. func (addr WrappedAddress) ToIP() IPAddressSegmentSeries { return addr.Address.ToIP() } // ToMAC converts to a MACAddressSegmentSeries if this series originated as a MAC series. // If not, ToMAC returns nil. // // ToMAC implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr WrappedAddress) ToMAC() MACAddressSegmentSeries { return addr.Address.ToMAC() } // Iterator provides an iterator to iterate through the individual series of this series. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual series. // // Call IsMultiple to determine if this instance represents multiple series, or GetCount for the count. func (addr WrappedAddress) Iterator() Iterator[ExtendedSegmentSeries] { return addressSeriesIterator{addr.Address.Iterator()} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this series, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this series. // // If the series has no prefix length, then this is equivalent to Iterator. func (addr WrappedAddress) PrefixIterator() Iterator[ExtendedSegmentSeries] { return addressSeriesIterator{addr.Address.PrefixIterator()} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this series. // Each iterated series will be a prefix block with the same prefix length as this series. // // If this series has no prefix length, then this is equivalent to Iterator. func (addr WrappedAddress) PrefixBlockIterator() Iterator[ExtendedSegmentSeries] { return addressSeriesIterator{addr.Address.PrefixBlockIterator()} } // ToBlock creates a new series block by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (addr WrappedAddress) ToBlock(segmentIndex int, lower, upper SegInt) ExtendedSegmentSeries { return wrapAddress(addr.Address.ToBlock(segmentIndex, lower, upper)) } // ToPrefixBlock returns the series with the same prefix as this series while the remaining bits span all values. // The series will be the block of all series with the same prefix. // // If this series has no prefix, this series is returned. func (addr WrappedAddress) ToPrefixBlock() ExtendedSegmentSeries { return wrapAddress(addr.Address.ToPrefixBlock()) } // ToPrefixBlockLen returns the series with the same prefix of the given length as this series while the remaining bits span all values. // The returned series will be the block of all series with the same prefix. func (addr WrappedAddress) ToPrefixBlockLen(prefLen BitCount) ExtendedSegmentSeries { return wrapAddress(addr.Address.ToPrefixBlockLen(prefLen)) } // Increment returns the item that is the given increment upwards into the range, // with the increment of 0 returning the first in the range. // // If the increment i matches or exceeds the range count c, then i - c + 1 // is added to the upper item of the range. // An increment matching the count gives you the item just above the highest in the range. // // If the increment is negative, it is added to the lowest of the range. // To get the item just below the lowest of the range, use the increment -1. // // If this represents just a single value, the item is simply incremented by the given increment, positive or negative. // // If this item represents multiple values, a positive increment i is equivalent i + 1 values from the iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the count is equivalent to the same number of iterator values preceding the last value of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On overflow or underflow, Increment returns nil. func (addr WrappedAddress) Increment(i int64) ExtendedSegmentSeries { return convAddrToIntf(addr.Address.Increment(i)) } // IncrementBoundary returns the item that is the given increment from the range boundaries of this item. // // If the given increment is positive, adds the value to the highest (GetUpper) in the range to produce a new item. // If the given increment is negative, adds the value to the lowest (GetLower) in the range to produce a new item. // If the increment is zero, returns this. // // If this represents just a single value, this item is simply incremented by the given increment value, positive or negative. // // On overflow or underflow, IncrementBoundary returns nil. func (addr WrappedAddress) IncrementBoundary(i int64) ExtendedSegmentSeries { return convAddrToIntf(addr.Address.IncrementBoundary(i)) } // GetLower returns the series in the range with the lowest numeric value, // which will be the same series if it represents a single value. func (addr WrappedAddress) GetLower() ExtendedSegmentSeries { return wrapAddress(addr.Address.GetLower()) } // GetUpper returns the series in the range with the highest numeric value, // which will be the same series if it represents a single value. func (addr WrappedAddress) GetUpper() ExtendedSegmentSeries { return wrapAddress(addr.Address.GetUpper()) } // GetSection returns the backing section for this series, comprising all segments. func (addr WrappedAddress) GetSection() *AddressSection { return addr.Address.GetSection() } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this series. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such series - it is required that the range of values match the range of a prefix block. // If there is no such series, then nil is returned. func (addr WrappedAddress) AssignPrefixForSingleBlock() ExtendedSegmentSeries { return convAddrToIntf(addr.Address.AssignPrefixForSingleBlock()) } // AssignMinPrefixForBlock returns an equivalent series, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this series. // // In other words, this method assigns a prefix length to this series matching the largest prefix block in this series. func (addr WrappedAddress) AssignMinPrefixForBlock() ExtendedSegmentSeries { return wrapAddress(addr.Address.AssignMinPrefixForBlock()) } // WithoutPrefixLen provides the same address series but with no prefix length. The values remain unchanged. func (addr WrappedAddress) WithoutPrefixLen() ExtendedSegmentSeries { return wrapAddress(addr.Address.WithoutPrefixLen()) } // Contains returns whether this is same type and version as the given address series and whether it contains all values in the given series. // // Series must also have the same number of segments to be comparable, otherwise false is returned. func (addr WrappedAddress) Contains(other ExtendedSegmentSeries) bool { a, ok := other.Unwrap().(AddressType) return ok && addr.Address.Contains(a) } // Equal returns whether the given address series is equal to this address series. // Two address series are equal if they represent the same set of series. // Both must be equal addresses. func (addr WrappedAddress) Equal(other ExtendedSegmentSeries) bool { a, ok := other.Unwrap().(AddressType) return ok && addr.Address.Equal(a) } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the series. // The provided prefix length will be adjusted to these boundaries if necessary. func (addr WrappedAddress) SetPrefixLen(prefixLen BitCount) ExtendedSegmentSeries { return wrapAddress(addr.Address.SetPrefixLen(prefixLen)) } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the series. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this series has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this series has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr WrappedAddress) SetPrefixLenZeroed(prefixLen BitCount) (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) { return wrapAddrWithErr(addr.Address.SetPrefixLenZeroed(prefixLen)) } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the series. // // If this series has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (addr WrappedAddress) AdjustPrefixLen(prefixLen BitCount) ExtendedSegmentSeries { return wrapAddress(addr.Address.AdjustPrefixLen(prefixLen)) } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the series. // // If this series has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. func (addr WrappedAddress) AdjustPrefixLenZeroed(prefixLen BitCount) (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) { return wrapAddrWithErr(addr.Address.AdjustPrefixLenZeroed(prefixLen)) } // ReverseBytes returns a new segment series with the bytes reversed. Any prefix length is dropped. // // If each segment is more than 1 byte long, and the bytes within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, then this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. func (addr WrappedAddress) ReverseBytes() (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) { return wrapAddrWithErr(addr.Address.ReverseBytes()) } // ReverseBits returns a new segment series with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (addr WrappedAddress) ReverseBits(perByte bool) (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) { a, err := addr.Address.ReverseBits(perByte) if err != nil { return nil, err } return wrapAddress(a), nil } // ReverseSegments returns a new series with the segments reversed. func (addr WrappedAddress) ReverseSegments() ExtendedSegmentSeries { return wrapAddress(addr.Address.ReverseSegments()) } // WrappedAddressSection is the implementation of ExtendedSegmentSeries for address sections. type WrappedAddressSection struct { *AddressSection } // Unwrap returns the wrapped address section as an interface, AddressSegmentSeries. func (section WrappedAddressSection) Unwrap() AddressSegmentSeries { res := section.AddressSection if res == nil { return nil } return res } // ToIPv4 converts to an IPv4AddressSegmentSeries if this series originated as an IPv4 series. // If not, ToIPv4 returns nil. // // ToIPv4 implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section WrappedAddressSection) ToIPv4() IPv4AddressSegmentSeries { return section.AddressSection.ToIPv4() } // ToIPv6 converts to an IPv4AddressSegmentSeries if this series originated as an IPv6 series. // If not, ToIPv6 returns nil. // // ToIPv6 implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section WrappedAddressSection) ToIPv6() IPv6AddressSegmentSeries { return section.AddressSection.ToIPv6() } // ToIP converts to an IP address section if this originated as IPv4 or IPv6, or an implicitly zero-valued IP. // If not, ToIP returns nil. func (section WrappedAddressSection) ToIP() IPAddressSegmentSeries { return section.AddressSection.ToIP() } // ToMAC converts to a MACAddressSegmentSeries if this series originated as a MAC series. // If not, ToMAC returns nil. // // ToMAC implementations can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section WrappedAddressSection) ToMAC() MACAddressSegmentSeries { return section.AddressSection.ToMAC() } // Iterator provides an iterator to iterate through the individual series of this series. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual series. // // Call IsMultiple to determine if this instance represents multiple series, or GetCount for the count. func (section WrappedAddressSection) Iterator() Iterator[ExtendedSegmentSeries] { return sectionSeriesIterator{section.AddressSection.Iterator()} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this series, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this series. // // If the series has no prefix length, then this is equivalent to Iterator. func (section WrappedAddressSection) PrefixIterator() Iterator[ExtendedSegmentSeries] { return sectionSeriesIterator{section.AddressSection.PrefixIterator()} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this series. // Each iterated series will be a prefix block with the same prefix length as this series. // // If this series has no prefix length, then this is equivalent to Iterator. func (section WrappedAddressSection) PrefixBlockIterator() Iterator[ExtendedSegmentSeries] { return sectionSeriesIterator{section.AddressSection.PrefixBlockIterator()} } // ToBlock creates a new series block by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (section WrappedAddressSection) ToBlock(segmentIndex int, lower, upper SegInt) ExtendedSegmentSeries { return wrapSection(section.AddressSection.ToBlock(segmentIndex, lower, upper)) } // ToPrefixBlock returns the series with the same prefix as this series while the remaining bits span all values. // The series will be the block of all series with the same prefix. // // If this series has no prefix, this series is returned. func (section WrappedAddressSection) ToPrefixBlock() ExtendedSegmentSeries { return wrapSection(section.AddressSection.ToPrefixBlock()) } // ToPrefixBlockLen returns the series with the same prefix of the given length as this series while the remaining bits span all values. // The returned series will be the block of all series with the same prefix. func (section WrappedAddressSection) ToPrefixBlockLen(prefLen BitCount) ExtendedSegmentSeries { return wrapSection(section.AddressSection.ToPrefixBlockLen(prefLen)) } // Increment returns the item that is the given increment upwards into the range, // with the increment of 0 returning the first in the range. // // If the increment i matches or exceeds the range count c, then i - c + 1 // is added to the upper item of the range. // An increment matching the count gives you the item just above the highest in the range. // // If the increment is negative, it is added to the lowest of the range. // To get the item just below the lowest of the range, use the increment -1. // // If this represents just a single value, the item is simply incremented by the given increment, positive or negative. // // If this item represents multiple values, a positive increment i is equivalent i + 1 values from the iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the count is equivalent to the same number of iterator values preceding the last value of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On overflow or underflow, Increment returns nil. func (section WrappedAddressSection) Increment(i int64) ExtendedSegmentSeries { return convSectToIntf(section.AddressSection.Increment(i)) } // IncrementBoundary returns the item that is the given increment from the range boundaries of this item. // // If the given increment is positive, adds the value to the highest (GetUpper) in the range to produce a new item. // If the given increment is negative, adds the value to the lowest (GetLower) in the range to produce a new item. // If the increment is zero, returns this. // // If this represents just a single value, this item is simply incremented by the given increment value, positive or negative. // // On overflow or underflow, IncrementBoundary returns nil. func (section WrappedAddressSection) IncrementBoundary(i int64) ExtendedSegmentSeries { return convSectToIntf(section.AddressSection.IncrementBoundary(i)) } // GetLower returns the series in the range with the lowest numeric value, // which will be the same series if it represents a single value. func (section WrappedAddressSection) GetLower() ExtendedSegmentSeries { return wrapSection(section.AddressSection.GetLower()) } // GetUpper returns the series in the range with the highest numeric value, // which will be the same series if it represents a single value. func (section WrappedAddressSection) GetUpper() ExtendedSegmentSeries { return wrapSection(section.AddressSection.GetUpper()) } // GetSection returns the backing section for this series, comprising all segments. func (section WrappedAddressSection) GetSection() *AddressSection { return section.AddressSection } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this series. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such series - it is required that the range of values match the range of a prefix block. // If there is no such series, then nil is returned. func (section WrappedAddressSection) AssignPrefixForSingleBlock() ExtendedSegmentSeries { return convSectToIntf(section.AddressSection.AssignPrefixForSingleBlock()) } // AssignMinPrefixForBlock returns an equivalent series, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this series. // // In other words, this method assigns a prefix length to this series matching the largest prefix block in this series. func (section WrappedAddressSection) AssignMinPrefixForBlock() ExtendedSegmentSeries { return wrapSection(section.AddressSection.AssignMinPrefixForBlock()) } // WithoutPrefixLen provides the same address series but with no prefix length. The values remain unchanged. func (section WrappedAddressSection) WithoutPrefixLen() ExtendedSegmentSeries { return wrapSection(section.AddressSection.WithoutPrefixLen()) } // Contains returns whether this is same type and version as the given address series and whether it contains all values in the given series. // // Series must also have the same number of segments to be comparable, otherwise false is returned. func (section WrappedAddressSection) Contains(other ExtendedSegmentSeries) bool { s, ok := other.Unwrap().(AddressSectionType) return ok && section.AddressSection.Contains(s) } // Equal returns whether the given address series is equal to this address series. // Two address series are equal if they represent the same set of series. // Both must be equal sections. func (section WrappedAddressSection) Equal(other ExtendedSegmentSeries) bool { s, ok := other.Unwrap().(AddressSectionType) return ok && section.AddressSection.Equal(s) } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the series. // The provided prefix length will be adjusted to these boundaries if necessary. func (section WrappedAddressSection) SetPrefixLen(prefixLen BitCount) ExtendedSegmentSeries { return wrapSection(section.AddressSection.SetPrefixLen(prefixLen)) } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the series. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this series has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this series has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section WrappedAddressSection) SetPrefixLenZeroed(prefixLen BitCount) (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) { return wrapSectWithErr(section.AddressSection.SetPrefixLenZeroed(prefixLen)) } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the series. // // If this series has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (section WrappedAddressSection) AdjustPrefixLen(adjustment BitCount) ExtendedSegmentSeries { return wrapSection(section.AddressSection.AdjustPrefixLen(adjustment)) } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the series. // // If this series has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. func (section WrappedAddressSection) AdjustPrefixLenZeroed(adjustment BitCount) (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) { return wrapSectWithErr(section.AddressSection.AdjustPrefixLenZeroed(adjustment)) } // ReverseBytes returns a new segment series with the bytes reversed. Any prefix length is dropped. // // If each segment is more than 1 byte long, and the bytes within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, then this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. func (section WrappedAddressSection) ReverseBytes() (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) { return wrapSectWithErr(section.AddressSection.ReverseBytes()) } // ReverseBits returns a new segment series with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (section WrappedAddressSection) ReverseBits(perByte bool) (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) { return wrapSectWithErr(section.AddressSection.ReverseBits(perByte)) } // ReverseSegments returns a new series with the segments reversed. func (section WrappedAddressSection) ReverseSegments() ExtendedSegmentSeries { return wrapSection(section.AddressSection.ReverseSegments()) } var _ ExtendedSegmentSeries = WrappedAddress{} var _ ExtendedSegmentSeries = WrappedAddressSection{} // In go, a nil value is not converted to a nil interface, it is converted to a non-nil interface instance with underlying value nil. func convAddrToIntf(addr *Address) ExtendedSegmentSeries { if addr == nil { return nil } return wrapAddress(addr) } func convSectToIntf(sect *AddressSection) ExtendedSegmentSeries { if sect == nil { return nil } return wrapSection(sect) } func wrapSectWithErr(section *AddressSection, err addrerr.IncompatibleAddressError) (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) { if err == nil { return wrapSection(section), nil } return nil, err } func wrapAddrWithErr(addr *Address, err addrerr.IncompatibleAddressError) (ExtendedSegmentSeries, addrerr.IncompatibleAddressError) { if err == nil { return wrapAddress(addr), nil } return nil, err } func wrapAddress(addr *Address) WrappedAddress { return WrappedAddress{addr} } func wrapSection(section *AddressSection) WrappedAddressSection { return WrappedAddressSection{section} } ipaddress-go-1.5.4/ipaddr/grouping.go000066400000000000000000001367461440250641600175510ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math/big" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" ) func createGrouping(divs []*AddressDivision, prefixLength PrefixLen, addrType addrType) *AddressDivisionGrouping { grouping := &AddressDivisionGrouping{ addressDivisionGroupingInternal{ addressDivisionGroupingBase: addressDivisionGroupingBase{ divisions: standardDivArray(divs), prefixLength: prefixLength, addrType: addrType, cache: &valueCache{}, }, }, } assignStringCache(&grouping.addressDivisionGroupingBase, addrType) return grouping } // callers to this function have segments/divisions with prefix length consistent with the supplied prefix length func createGroupingMultiple(divs []*AddressDivision, prefixLength PrefixLen, isMultiple bool) *AddressDivisionGrouping { result := createGrouping(divs, prefixLength, zeroType) result.isMult = isMultiple return result } // callers to this function have segments/divisions with prefix length consistent with the supplied prefix length func createInitializedGrouping(divs []*AddressDivision, prefixLength PrefixLen) *AddressDivisionGrouping { result := createGrouping(divs, prefixLength, zeroType) result.initMultiple() // assigns isMultiple return result } var ( emptyBytes = make([]byte, 0, 0) ) type addressDivisionGroupingInternal struct { addressDivisionGroupingBase // TODO LATER refactor to support infiniband, which will involve multiple types. // But that will be a joint effort with Java and will wait to later. } func createSegmentArray(length int) []*AddressDivision { return make([]*AddressDivision, length) } func (grouping *addressDivisionGroupingInternal) initMultiple() { divCount := grouping.getDivisionCount() for i := divCount - 1; i >= 0; i-- { div := grouping.getDivision(i) if div.isMultiple() { grouping.isMult = true return } } return } func (grouping *addressDivisionGroupingInternal) getDivArray() standardDivArray { if divsArray := grouping.divisions; divsArray != nil { return divsArray.(standardDivArray) } return nil } // getDivision returns the division or panics if the index is negative or too large func (grouping *addressDivisionGroupingInternal) getDivision(index int) *AddressDivision { return grouping.getDivArray()[index] } // getDivisionsInternal returns the divisions slice, only to be used internally func (grouping *addressDivisionGroupingInternal) getDivisionsInternal() []*AddressDivision { return grouping.getDivArray() } func (grouping *addressDivisionGroupingInternal) getDivisionCount() int { if divArray := grouping.getDivArray(); divArray != nil { return divArray.getDivisionCount() } return 0 } func (grouping *addressDivisionGroupingInternal) forEachSubDivision(start, end int, target func(index int, div *AddressDivision), targetLen int) (count int) { divArray := grouping.getDivArray() if divArray != nil { // if not enough space in target, adjust if targetEnd := start + targetLen; end > targetEnd { end = targetEnd } divArray = divArray[start:end] for i, div := range divArray { target(i, div) } } return len(divArray) } func adjust1To1StartIndices(sourceStart, sourceEnd, sourceCount, targetCount int) (newSourceStart, newSourceEnd, newTargetStart int) { // both sourceCount and targetCount are lengths of their respective slices, so never negative targetStart := 0 if sourceStart < 0 { targetStart -= sourceStart sourceStart = 0 if targetStart > targetCount || targetStart < 0 /* overflow */ { targetStart = targetCount } } else if sourceStart > sourceCount { sourceStart = sourceCount } // how many to copy? if sourceEnd > sourceCount { // end index exceeds available sourceEnd = sourceCount } else if sourceEnd < sourceStart { sourceEnd = sourceStart } return sourceStart, sourceEnd, targetStart } func adjust1To1Indices(sourceStart, sourceEnd, sourceCount, targetCount int) (newSourceStart, newSourceEnd, newTargetStart int) { var targetStart int sourceStart, sourceEnd, targetStart = adjust1To1StartIndices(sourceStart, sourceEnd, sourceCount, targetCount) if limitEnd := sourceStart + (targetCount - targetStart); sourceEnd > limitEnd { sourceEnd = limitEnd } return sourceStart, sourceEnd, targetStart } func adjustIndices( startIndex, endIndex, sourceCount, replacementStartIndex, replacementEndIndex, replacementSegmentCount int) (int, int, int, int) { if startIndex < 0 { startIndex = 0 } else if startIndex > sourceCount { startIndex = sourceCount } if endIndex < startIndex { endIndex = startIndex } else if endIndex > sourceCount { endIndex = sourceCount } if replacementStartIndex < 0 { replacementStartIndex = 0 } else if replacementStartIndex > replacementSegmentCount { replacementStartIndex = replacementSegmentCount } if replacementEndIndex < replacementStartIndex { replacementEndIndex = replacementStartIndex } else if replacementEndIndex > replacementSegmentCount { replacementEndIndex = replacementSegmentCount } return startIndex, endIndex, replacementStartIndex, replacementEndIndex } // copySubDivisions copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (grouping *addressDivisionGroupingInternal) copySubDivisions(start, end int, divs []*AddressDivision) (count int) { if divArray := grouping.getDivArray(); divArray != nil { start, end, targetIndex := adjust1To1Indices(start, end, grouping.GetDivisionCount(), len(divs)) return divArray.copySubDivisions(start, end, divs[targetIndex:]) } return } // copyDivisions copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (grouping *addressDivisionGroupingInternal) copyDivisions(divs []*AddressDivision) (count int) { if divArray := grouping.getDivArray(); divArray != nil { return divArray.copyDivisions(divs) } return } func (grouping *addressDivisionGroupingInternal) getSubDivisions(start, end int) []*AddressDivision { divArray := grouping.getDivArray() if divArray != nil { return divArray.getSubDivisions(start, end) } else if start != 0 || end != 0 { panic("invalid subslice") } return make([]*AddressDivision, 0) } func (grouping *addressDivisionGroupingInternal) isAddressSection() bool { return grouping != nil && grouping.matchesAddrSectionType() } func (grouping *addressDivisionGroupingInternal) compareSize(other AddressItem) int { // the getCount() is optimized which is why we do not defer to the method in addressDivisionGroupingBase return compareCount(grouping.toAddressDivisionGrouping(), other) } func (grouping *addressDivisionGroupingInternal) getCount() *big.Int { if !grouping.isMultiple() { return bigOne() } else { g := grouping.toAddressDivisionGrouping() if sect := g.ToIPv4(); sect != nil { return sect.GetCount() } else if sect := g.ToIPv6(); sect != nil { return sect.GetCount() } else if sect := g.ToMAC(); sect != nil { return sect.GetCount() } } return grouping.addressDivisionGroupingBase.getCount() } func (grouping *addressDivisionGroupingInternal) getCachedCount() *big.Int { if !grouping.isMultiple() { return bigOne() } else { g := grouping.toAddressDivisionGrouping() if sect := g.ToIPv4(); sect != nil { return sect.getCachedCount() } else if sect := g.ToIPv6(); sect != nil { return sect.getCachedCount() } else if sect := g.ToMAC(); sect != nil { return sect.getCachedCount() } } return grouping.addressDivisionGroupingBase.getCachedCount() } // GetPrefixCount returns the number of distinct prefix values in this item. // // The prefix length is given by GetPrefixLen. // // If this has a non-nil prefix length, returns the number of distinct prefix values. // // If this has a nil prefix length, returns the same value as GetCount. func (grouping *addressDivisionGroupingInternal) GetPrefixCount() *big.Int { if section := grouping.toAddressSection(); section != nil { return section.GetPrefixCount() } return grouping.addressDivisionGroupingBase.GetPrefixCount() } // GetPrefixCountLen returns the number of distinct prefix values in this item for the given prefix length. func (grouping *addressDivisionGroupingInternal) GetPrefixCountLen(prefixLen BitCount) *big.Int { if section := grouping.toAddressSection(); section != nil { return section.GetPrefixCountLen(prefixLen) } return grouping.addressDivisionGroupingBase.GetPrefixCountLen(prefixLen) } func (grouping *addressDivisionGroupingInternal) getDivisionStrings() []string { if grouping.hasNoDivisions() { return []string{} } result := make([]string, grouping.GetDivisionCount()) for i := range result { result[i] = grouping.getDivision(i).String() } return result } func (grouping *addressDivisionGroupingInternal) getSegmentStrings() []string { if grouping.hasNoDivisions() { return []string{} } result := make([]string, grouping.GetDivisionCount()) for i := range result { result[i] = grouping.getDivision(i).GetWildcardString() } return result } func (grouping *addressDivisionGroupingInternal) toAddressDivisionGrouping() *AddressDivisionGrouping { return (*AddressDivisionGrouping)(unsafe.Pointer(grouping)) } func (grouping *addressDivisionGroupingInternal) toAddressSection() *AddressSection { return grouping.toAddressDivisionGrouping().ToSectionBase() } func (grouping *addressDivisionGroupingInternal) matchesIPv6AddressType() bool { return grouping.getAddrType().isIPv6() // no need to check segment count because addresses cannot be constructed with incorrect segment count } func (grouping *addressDivisionGroupingInternal) matchesIPv4AddressType() bool { return grouping.getAddrType().isIPv4() // no need to check segment count because addresses cannot be constructed with incorrect segment count } func (grouping *addressDivisionGroupingInternal) matchesIPAddressType() bool { return grouping.matchesIPSectionType() // no need to check segment count because addresses cannot be constructed with incorrect segment count (note the zero IPAddress has zero-segments) } func (grouping *addressDivisionGroupingInternal) matchesMACAddressType() bool { return grouping.getAddrType().isMAC() } // The adaptive zero grouping, produced by zero sections like IPv4AddressSection{} or AddressDivisionGrouping{}, can represent a zero-length section of any address type, // It is not considered equal to constructions of specific zero length sections of groupings like NewIPv4Section(nil) which can only represent a zero-length section of a single address type. func (grouping *addressDivisionGroupingInternal) matchesZeroGrouping() bool { addrType := grouping.getAddrType() return addrType.isZeroSegments() && grouping.hasNoDivisions() } func (grouping *addressDivisionGroupingInternal) matchesAddrSectionType() bool { addrType := grouping.getAddrType() // because there are no init() conversions for IPv6/IPV4/MAC sections, an implicitly zero-valued IPv6/IPV4/MAC or zero IP section has addr type nil return addrType.isIP() || addrType.isMAC() || grouping.matchesZeroGrouping() } func (grouping *addressDivisionGroupingInternal) matchesIPv6SectionType() bool { // because there are no init() conversions for IPv6 sections, an implicitly zero-valued IPV6 section has addr type nil return grouping.getAddrType().isIPv6() || grouping.matchesZeroGrouping() } func (grouping *addressDivisionGroupingInternal) matchesIPv6v4MixedGroupingType() bool { // because there are no init() conversions for IPv6v4MixedGrouping groupings, an implicitly zero-valued IPv6v4MixedGrouping has addr type nil return grouping.getAddrType().isIPv6v4Mixed() || grouping.matchesZeroGrouping() } func (grouping *addressDivisionGroupingInternal) matchesIPv4SectionType() bool { // because there are no init() conversions for IPV4 sections, an implicitly zero-valued IPV4 section has addr type nil return grouping.getAddrType().isIPv4() || grouping.matchesZeroGrouping() } func (grouping *addressDivisionGroupingInternal) matchesIPSectionType() bool { // because there are no init() conversions for IPv6 or IPV4 sections, an implicitly zero-valued IPv4, IPv6 or IP section has addr type nil return grouping.getAddrType().isIP() || grouping.matchesZeroGrouping() } func (grouping *addressDivisionGroupingInternal) matchesMACSectionType() bool { // because there are no init() conversions for MAC sections, an implicitly zero-valued MAC section has addr type nil return grouping.getAddrType().isMAC() || grouping.matchesZeroGrouping() } // Format implements the [fmt.Formatter] interface. It accepts the formats // - 'v' for the default address and section format (either the normalized or canonical string), // - 's' (string) for the same, // - 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix), // - 'd' (decimal), 'x' (lowercase hexadecimal), and // - 'X' (uppercase hexadecimal). // Also supported are some of fmt's format flags for integral types. // Sign control is not supported since addresses and sections are never negative. // '#' for an alternate format is supported, which adds a leading zero for octal, and for hexadecimal it adds // a leading "0x" or "0X" for "%#x" and "%#X" respectively. // Also supported is specification of minimum digits precision, output field width, // space or zero padding, and '-' for left or right justification. func (grouping addressDivisionGroupingInternal) Format(state fmt.State, verb rune) { if sect := grouping.toAddressSection(); sect != nil { sect.Format(state, verb) return } else if mixed := grouping.toAddressDivisionGrouping().ToMixedIPv6v4(); mixed != nil { mixed.Format(state, verb) return } // divisions are printed like slices of *AddressDivision (which are Stringers) with division separated by spaces and enclosed in square brackets, // sections are printed like addresses with segments separated by segment separators grouping.defaultFormat(state, verb) } func (grouping addressDivisionGroupingInternal) defaultFormat(state fmt.State, verb rune) { s := flagsFromState(state, verb) _, _ = state.Write([]byte(fmt.Sprintf(s, grouping.initDivs().getDivArray()))) } func (grouping *addressDivisionGroupingInternal) toString() string { if sect := grouping.toAddressSection(); sect != nil { return sect.ToNormalizedString() } return fmt.Sprint(grouping.initDivs().getDivArray()) } func (grouping *addressDivisionGroupingInternal) initDivs() *addressDivisionGroupingInternal { if grouping.divisions == nil { return &zeroSection.addressDivisionGroupingInternal } return grouping } // ContainsPrefixBlock returns whether the values of this item contains the block of values for the given prefix length. // // Unlike ContainsSinglePrefixBlock, whether there are multiple prefix values in this item for the given prefix length makes no difference. // // Use GetMinPrefixLenForBlock to determine the smallest prefix length for which this method returns true. func (grouping *addressDivisionGroupingInternal) ContainsPrefixBlock(prefixLen BitCount) bool { if section := grouping.toAddressSection(); section != nil { return section.ContainsPrefixBlock(prefixLen) } prefixLen = checkSubnet(grouping, prefixLen) divisionCount := grouping.GetDivisionCount() var prevBitCount BitCount for i := 0; i < divisionCount; i++ { division := grouping.getDivision(i) bitCount := division.GetBitCount() totalBitCount := bitCount + prevBitCount if prefixLen < totalBitCount { divPrefixLen := prefixLen - prevBitCount if !division.containsPrefixBlock(divPrefixLen) { return false } for i++; i < divisionCount; i++ { division = grouping.getDivision(i) if !division.IsFullRange() { return false } } return true } prevBitCount = totalBitCount } return true } // ContainsSinglePrefixBlock returns whether the values of this grouping contains a single prefix block for the given prefix length. // // This means there is only one prefix of the given length in this item, and this item contains the prefix block for that given prefix. // // Use GetPrefixLenForSingleBlock to determine whether there is a prefix length for which this method returns true. func (grouping *addressDivisionGroupingInternal) ContainsSinglePrefixBlock(prefixLen BitCount) bool { prefixLen = checkSubnet(grouping, prefixLen) divisionCount := grouping.GetDivisionCount() var prevBitCount BitCount for i := 0; i < divisionCount; i++ { division := grouping.getDivision(i) bitCount := division.getBitCount() totalBitCount := bitCount + prevBitCount if prefixLen >= totalBitCount { if division.isMultiple() { return false } } else { divPrefixLen := prefixLen - prevBitCount if !division.ContainsSinglePrefixBlock(divPrefixLen) { return false } for i++; i < divisionCount; i++ { division = grouping.getDivision(i) if !division.IsFullRange() { return false } } return true } prevBitCount = totalBitCount } return true } // IsPrefixBlock returns whether this address division series has a prefix length and includes the block associated with its prefix length. // If the prefix length matches the bit count, this returns true. // // This is different from ContainsPrefixBlock in that this method returns // false if the series has no prefix length, or a prefix length that differs from a prefix length for which ContainsPrefixBlock returns true. func (grouping *addressDivisionGroupingInternal) IsPrefixBlock() bool { //Note for any given prefix length you can compare with GetMinPrefixLenForBlock prefLen := grouping.getPrefixLen() return prefLen != nil && grouping.ContainsPrefixBlock(prefLen.bitCount()) } // IsSinglePrefixBlock returns whether the range of values matches a single subnet block for the prefix length. // // What distinguishes this method with ContainsSinglePrefixBlock is that this method returns // false if the series does not have a prefix length assigned to it, // or a prefix length that differs from the prefix length for which ContainsSinglePrefixBlock returns true. // // It is similar to IsPrefixBlock but returns false when there are multiple prefixes. func (grouping *addressDivisionGroupingInternal) IsSinglePrefixBlock() bool { //Note for any given prefix length you can compare with GetPrefixLenForSingleBlock calc := func() bool { prefLen := grouping.getPrefixLen() return prefLen != nil && grouping.ContainsSinglePrefixBlock(prefLen.bitCount()) } return cacheIsSinglePrefixBlock(grouping.cache, grouping.getPrefixLen(), calc) } func cacheIsSinglePrefixBlock(cache *valueCache, prefLen PrefixLen, calc func() bool) bool { if cache == nil { return calc() } res := (*bool)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.isSinglePrefixBlock)))) if res == nil { if calc() { res = &trueVal // we can also set related cache fields dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.equivalentPrefix)) equivPref := cachePrefix(prefLen.bitCount()) atomicStorePointer(dataLoc, unsafe.Pointer(equivPref)) dataLoc = (*unsafe.Pointer)(unsafe.Pointer(&cache.minPrefix)) atomicStorePointer(dataLoc, unsafe.Pointer(prefLen)) } else { res = &falseVal } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.isSinglePrefixBlock)) atomicStorePointer(dataLoc, unsafe.Pointer(res)) } return *res } // GetMinPrefixLenForBlock returns the smallest prefix length such that this grouping includes the block of all values for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this grouping represents a single value, this returns the bit count. func (grouping *addressDivisionGroupingInternal) GetMinPrefixLenForBlock() BitCount { calc := func() BitCount { count := grouping.GetDivisionCount() totalPrefix := grouping.GetBitCount() for i := count - 1; i >= 0; i-- { div := grouping.getDivision(i) segBitCount := div.getBitCount() segPrefix := div.GetMinPrefixLenForBlock() if segPrefix == segBitCount { break } else { totalPrefix -= segBitCount if segPrefix != 0 { totalPrefix += segPrefix break } } } return totalPrefix } return cacheMinPrefix(grouping.cache, calc) } func cacheMinPrefix(cache *valueCache, calc func() BitCount) BitCount { if cache == nil { return calc() } res := (PrefixLen)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.minPrefix)))) if res == nil { val := calc() res = cacheBitCount(val) dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.minPrefix)) atomicStorePointer(dataLoc, unsafe.Pointer(res)) } return res.bitCount() } // GetPrefixLenForSingleBlock returns a prefix length for which the range of this division grouping matches the block of addresses for that prefix. // // If no such prefix exists, GetPrefixLenForSingleBlock returns nil. // // If this division grouping represents a single value, returns the bit length. func (grouping *addressDivisionGroupingInternal) GetPrefixLenForSingleBlock() PrefixLen { calc := func() *PrefixLen { count := grouping.GetDivisionCount() var totalPrefix BitCount for i := 0; i < count; i++ { div := grouping.getDivision(i) divPrefix := div.GetPrefixLenForSingleBlock() if divPrefix == nil { return cacheNilPrefix() } divPrefLen := divPrefix.bitCount() totalPrefix += divPrefLen if divPrefLen < div.GetBitCount() { //remaining segments must be full range or we return nil for i++; i < count; i++ { laterDiv := grouping.getDivision(i) if !laterDiv.IsFullRange() { return cacheNilPrefix() } } } } return cachePrefix(totalPrefix) } return cachePrefLenSingleBlock(grouping.cache, grouping.getPrefixLen(), calc) } func cachePrefLenSingleBlock(cache *valueCache, prefLen PrefixLen, calc func() *PrefixLen) PrefixLen { if cache == nil { return *calc() } res := (*PrefixLen)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.equivalentPrefix)))) if res == nil { res = calc() if *res == nil { // we can also set related cache fields dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.isSinglePrefixBlock)) atomicStorePointer(dataLoc, unsafe.Pointer(&falseVal)) } else { // we can also set related cache fields var isSingleBlock *bool if prefLen != nil && (*res).Equal(prefLen) { isSingleBlock = &trueVal } else { isSingleBlock = &falseVal } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.isSinglePrefixBlock)) atomicStorePointer(dataLoc, unsafe.Pointer(isSingleBlock)) dataLoc = (*unsafe.Pointer)(unsafe.Pointer(&cache.minPrefix)) atomicStorePointer(dataLoc, unsafe.Pointer(*res)) } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.equivalentPrefix)) atomicStorePointer(dataLoc, unsafe.Pointer(res)) } return *res } // GetValue returns the lowest individual address division grouping in this address division grouping as an integer value. func (grouping *addressDivisionGroupingInternal) GetValue() *big.Int { if grouping.hasNoDivisions() { return bigZero() } return bigZero().SetBytes(grouping.getBytes()) } // GetUpperValue returns the highest individual address division grouping in this address division grouping as an integer value. func (grouping *addressDivisionGroupingInternal) GetUpperValue() *big.Int { if grouping.hasNoDivisions() { return bigZero() } return bigZero().SetBytes(grouping.getUpperBytes()) } // Bytes returns the lowest individual division grouping in this grouping as a byte slice. func (grouping *addressDivisionGroupingInternal) Bytes() []byte { if grouping.hasNoDivisions() { return emptyBytes } return cloneBytes(grouping.getBytes()) } // UpperBytes returns the highest individual division grouping in this grouping as a byte slice. func (grouping *addressDivisionGroupingInternal) UpperBytes() []byte { if grouping.hasNoDivisions() { return emptyBytes } return cloneBytes(grouping.getUpperBytes()) } // CopyBytes copies the value of the lowest division grouping in the range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. // // You can use GetByteCount to determine the required array length for the bytes. func (grouping *addressDivisionGroupingInternal) CopyBytes(bytes []byte) []byte { if grouping.hasNoDivisions() { if bytes != nil { return bytes[:0] } return emptyBytes } return getBytesCopy(bytes, grouping.getBytes()) } // CopyUpperBytes copies the value of the highest division grouping in the range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. // // You can use GetByteCount to determine the required array length for the bytes. func (grouping *addressDivisionGroupingInternal) CopyUpperBytes(bytes []byte) []byte { if grouping.hasNoDivisions() { if bytes != nil { return bytes[:0] } return emptyBytes } return getBytesCopy(bytes, grouping.getUpperBytes()) } func (grouping *addressDivisionGroupingInternal) getBytes() (bytes []byte) { bytes, _ = grouping.getCachedBytes(grouping.calcBytes) return } func (grouping *addressDivisionGroupingInternal) getUpperBytes() (bytes []byte) { _, bytes = grouping.getCachedBytes(grouping.calcBytes) return } func (grouping *addressDivisionGroupingInternal) calcBytes() (bytes, upperBytes []byte) { addrType := grouping.getAddrType() divisionCount := grouping.GetDivisionCount() isMultiple := grouping.isMultiple() if addrType.isIPv4() || addrType.isMAC() { bytes = make([]byte, divisionCount) if isMultiple { upperBytes = make([]byte, divisionCount) } else { upperBytes = bytes } for i := 0; i < divisionCount; i++ { seg := grouping.getDivision(i).ToSegmentBase() bytes[i] = byte(seg.GetSegmentValue()) if isMultiple { upperBytes[i] = byte(seg.GetUpperSegmentValue()) } } } else if addrType.isIPv6() { byteCount := divisionCount << 1 bytes = make([]byte, byteCount) if isMultiple { upperBytes = make([]byte, byteCount) } else { upperBytes = bytes } for i := 0; i < divisionCount; i++ { seg := grouping.getDivision(i).ToSegmentBase() byteIndex := i << 1 val := seg.GetSegmentValue() bytes[byteIndex] = byte(val >> 8) var upperVal SegInt if isMultiple { upperVal = seg.GetUpperSegmentValue() upperBytes[byteIndex] = byte(upperVal >> 8) } nextByteIndex := byteIndex + 1 bytes[nextByteIndex] = byte(val) if isMultiple { upperBytes[nextByteIndex] = byte(upperVal) } } } else { byteCount := grouping.GetByteCount() bytes = make([]byte, byteCount) if isMultiple { upperBytes = make([]byte, byteCount) } else { upperBytes = bytes } for k, byteIndex, bitIndex := divisionCount-1, byteCount-1, BitCount(8); k >= 0; k-- { div := grouping.getDivision(k) val := div.GetDivisionValue() var upperVal DivInt if isMultiple { upperVal = div.GetUpperDivisionValue() } divBits := div.GetBitCount() for divBits > 0 { rbi := 8 - bitIndex bytes[byteIndex] |= byte(val << uint(rbi)) val >>= uint(bitIndex) if isMultiple { upperBytes[byteIndex] |= byte(upperVal << uint(rbi)) upperVal >>= uint(bitIndex) } if divBits < bitIndex { bitIndex -= divBits break } else { divBits -= bitIndex bitIndex = 8 byteIndex-- } } } } return } func (grouping *addressDivisionGroupingInternal) createNewDivisions(bitsPerDigit BitCount) ([]*AddressDivision, addrerr.IncompatibleAddressError) { return grouping.createNewPrefixedDivisions(bitsPerDigit, nil) } func (grouping *addressDivisionGroupingInternal) createNewPrefixedDivisions(bitsPerDigit BitCount, networkPrefixLength PrefixLen) ([]*AddressDivision, addrerr.IncompatibleAddressError) { bitCount := grouping.GetBitCount() var bitDivs []BitCount // here we divide into divisions, each with an exact number of digits. // Each digit takes 3 bits. So the division bit-sizes are a multiple of 3 until the last one. //ipv6 octal: //seg bit counts: 63, 63, 2 //ipv4 octal: //seg bit counts: 30, 2 largestBitCount := BitCount(64) // uint64, size of DivInt largestBitCount -= largestBitCount % bitsPerDigit // round off to a multiple of 3 bits for { if bitCount <= largestBitCount { mod := bitCount % bitsPerDigit secondLast := bitCount - mod if secondLast > 0 { bitDivs = append(bitDivs, secondLast) } if mod > 0 { bitDivs = append(bitDivs, mod) } break } else { bitCount -= largestBitCount bitDivs = append(bitDivs, largestBitCount) } } // at this point bitDivs has our division sizes divCount := len(bitDivs) divs := make([]*AddressDivision, divCount) if divCount > 0 { //S divs[] = groupingArrayCreator.apply(divCount); currentSegmentIndex := 0 seg := grouping.getDivision(currentSegmentIndex) segLowerVal := seg.GetDivisionValue() segUpperVal := seg.GetUpperDivisionValue() segBits := seg.GetBitCount() bitsSoFar := BitCount(0) // 2 to the x is all ones shift left x, then not, then add 1 // so, for x == 1, 1111111 -> 1111110 -> 0000001 -> 0000010 //radix := ^(^(0) << uint(bitsPerDigit)) + 1 //fill up our new divisions, one by one for i := divCount - 1; i >= 0; i-- { divBitSize := bitDivs[i] originalDivBitSize := divBitSize var divLowerValue, divUpperValue uint64 for { if segBits >= divBitSize { // this segment fills the remainder of this division diff := uint(segBits - divBitSize) segBits = BitCount(diff) segL := segLowerVal >> diff segU := segUpperVal >> diff // if the division upper bits are multiple, then the lower bits inserted must be full range if divLowerValue != divUpperValue { if segL != 0 || segU != ^(^uint64(0)< 0 { //get next seg currentSegmentIndex++ seg = grouping.getDivision(currentSegmentIndex) segLowerVal = seg.getDivisionValue() segUpperVal = seg.getUpperDivisionValue() segBits = seg.getBitCount() } break } else { // if the division upper bits are multiple, then the lower bits inserted must be full range if divLowerValue != divUpperValue { if segLowerVal != 0 || segUpperVal != ^(^uint64(0)< 0 { // normalize boundaries by looking back lastDiv := newDivs[len(newDivs)-1] if !lastDiv.isPrefixed() { newDivs[len(newDivs)-1] = createAddressDivision( lastDiv.derivePrefixed(cacheBitCount(lastDiv.GetBitCount()))) } } newPref = cacheBitCount(bits + divPrefix.bitCount()) previousDivPrefixed = true } newDiv = div } newDivs = append(newDivs, newDiv) bits += newDiv.GetBitCount() isMultiple = isMultiple || newDiv.isMultiple() } return } // AddressDivisionGrouping objects consist of a series of AddressDivision objects, each division containing a sequential range of values. // // AddressDivisionGrouping objects are immutable. This also makes them concurrency-safe. // // AddressDivision objects use uint64 to represent their values, so this places a cap on the size of the divisions in AddressDivisionGrouping. // // AddressDivisionGrouping objects are similar to address sections and addresses, except that groupings can have divisions of differing bit-length, // including divisions that are not an exact number of bytes, whereas all segments in an address or address section must be equal bit size and an exact number of bytes. type AddressDivisionGrouping struct { addressDivisionGroupingInternal } // Compare returns a negative integer, zero, or a positive integer if this address division grouping is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (grouping *AddressDivisionGrouping) Compare(item AddressItem) int { return CountComparator.Compare(grouping, item) } // CompareSize compares the counts of two items, the number of individual items represented in each. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether this grouping represents more individual items than another. // // CompareSize returns a positive integer if this address division grouping has a larger count than the item given, zero if they are the same, or a negative integer if the other has a larger count. func (grouping *AddressDivisionGrouping) CompareSize(other AddressItem) int { if grouping == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return grouping.compareSize(other) } // GetCount returns the count of possible distinct values for this division grouping. // If not representing multiple values, the count is 1, // unless this is a division grouping with no divisions, or an address section with no segments, in which case it is 0. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (grouping *AddressDivisionGrouping) GetCount() *big.Int { if grouping == nil { return bigZero() } return grouping.getCount() } // IsMultiple returns whether this grouping represents multiple values rather than a single value func (grouping *AddressDivisionGrouping) IsMultiple() bool { return grouping != nil && grouping.isMultiple() } // IsPrefixed returns whether this grouping has an associated prefix length. func (grouping *AddressDivisionGrouping) IsPrefixed() bool { if grouping == nil { return false } return grouping.isPrefixed() } // CopySubDivisions copies the existing divisions from the given start index until but not including the division at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of divisions copied. func (grouping *AddressDivisionGrouping) CopySubDivisions(start, end int, divs []*AddressDivision) (count int) { return grouping.copySubDivisions(start, end, divs) } // CopyDivisions copies the existing divisions from the given start index until but not including the division at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of divisions copied. func (grouping *AddressDivisionGrouping) CopyDivisions(divs []*AddressDivision) (count int) { return grouping.copyDivisions(divs) } // GetDivisionStrings returns a slice containing each string returned from the String method of each division in the grouping. func (grouping *AddressDivisionGrouping) GetDivisionStrings() []string { if grouping == nil { return nil } return grouping.getDivisionStrings() } // IsAdaptiveZero returns true if this is an adaptive zero grouping. // The adaptive zero grouping, produced by zero sections like IPv4AddressSection{} or AddressDivisionGrouping{}, can represent a zero-length section of any address type. // It is not considered equal to constructions of specific zero length sections or groupings like NewIPv4Section(nil) which can only represent a zero-length section of a single address type. func (grouping *AddressDivisionGrouping) IsAdaptiveZero() bool { return grouping != nil && grouping.matchesZeroGrouping() } // IsSectionBase returns true if this address division grouping originated as an address section. If so, use ToSectionBase to convert back to the section type. func (grouping *AddressDivisionGrouping) IsSectionBase() bool { return grouping != nil && grouping.isAddressSection() } // IsIP returns true if this address division grouping originated as an IPv4 or IPv6 section, or a zero-length IP section. If so, use ToIP to convert back to the IP-specific type. func (grouping *AddressDivisionGrouping) IsIP() bool { return grouping.ToSectionBase().IsIP() } // IsIPv4 returns true if this grouping originated as an IPv4 section. If so, use ToIPv4 to convert back to the IPv4-specific type. func (grouping *AddressDivisionGrouping) IsIPv4() bool { return grouping.ToSectionBase().IsIPv4() } // IsIPv6 returns true if this grouping originated as an IPv6 section. If so, use ToIPv6 to convert back to the IPv6-specific type. func (grouping *AddressDivisionGrouping) IsIPv6() bool { return grouping.ToSectionBase().IsIPv6() } // IsMixedIPv6v4 returns true if this grouping originated as a mixed IPv6-IPv4 grouping. If so, use ToMixedIPv6v4 to convert back to the more specific grouping type. func (grouping *AddressDivisionGrouping) IsMixedIPv6v4() bool { return grouping != nil && grouping.matchesIPv6v4MixedGroupingType() } // IsMAC returns true if this grouping originated as a MAC section. If so, use ToMAC to convert back to the MAC-specific type. func (grouping *AddressDivisionGrouping) IsMAC() bool { return grouping.ToSectionBase().IsMAC() } // ToSectionBase converts to an address section if this grouping originated as an address section. // Otherwise, the result will be nil. // // ToSectionBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (grouping *AddressDivisionGrouping) ToSectionBase() *AddressSection { if grouping == nil || !grouping.isAddressSection() { return nil } return (*AddressSection)(unsafe.Pointer(grouping)) } // ToMixedIPv6v4 converts to a mixed IPv6/4 address section if this grouping originated as a mixed IPv6/4 address section. // Otherwise, the result will be nil. // // ToMixedIPv6v4 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (grouping *AddressDivisionGrouping) ToMixedIPv6v4() *IPv6v4MixedAddressGrouping { if grouping.matchesIPv6v4MixedGroupingType() { return (*IPv6v4MixedAddressGrouping)(grouping) } return nil } // ToIP converts to an IPAddressSection if this grouping originated as an IPv4 or IPv6 section, or an implicitly zero-valued IP section. // If not, ToIP returns nil. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (grouping *AddressDivisionGrouping) ToIP() *IPAddressSection { return grouping.ToSectionBase().ToIP() } // ToIPv6 converts to an IPv6AddressSection if this grouping originated as an IPv6 section. // If not, ToIPv6 returns nil. // // ToIPv6 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (grouping *AddressDivisionGrouping) ToIPv6() *IPv6AddressSection { return grouping.ToSectionBase().ToIPv6() } // ToIPv4 converts to an IPv4AddressSection if this grouping originated as an IPv4 section. // If not, ToIPv4 returns nil. // // ToIPv4 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (grouping *AddressDivisionGrouping) ToIPv4() *IPv4AddressSection { return grouping.ToSectionBase().ToIPv4() } // ToMAC converts to a MACAddressSection if this grouping originated as a MAC section. // If not, ToMAC returns nil. // // ToMAC can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (grouping *AddressDivisionGrouping) ToMAC() *MACAddressSection { return grouping.ToSectionBase().ToMAC() } // ToDivGrouping is an identity method. // // ToDivGrouping can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (grouping *AddressDivisionGrouping) ToDivGrouping() *AddressDivisionGrouping { return grouping } // GetDivision returns the division at the given index. func (grouping *AddressDivisionGrouping) GetDivision(index int) *AddressDivision { return grouping.getDivision(index) } // ForEachDivision visits each segment in order from most-significant to least, the most significant with index 0, calling the given function for each, terminating early if the function returns true. // ForEachDivision returns the number of visited segments. func (grouping *AddressDivisionGrouping) ForEachDivision(consumer func(divisionIndex int, division *AddressDivision) (stop bool)) int { divArray := grouping.getDivArray() if divArray != nil { for i, div := range divArray { if consumer(i, div) { return i + 1 } } } return len(divArray) } // String implements the [fmt.Stringer] interface. // It returns "" if the receiver is a nil pointer. // It returns the normalized string provided by ToNormalizedString if this grouping originated as an address section. // Otherwise, the string is printed like a slice, with each division converted to a string by its own String method (like "[ div0 div1 ... ]"). func (grouping *AddressDivisionGrouping) String() string { if grouping == nil { return nilString() } return grouping.toString() } ipaddress-go-1.5.4/ipaddr/groupingbase.go000066400000000000000000000472071440250641600203750ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math/big" "unsafe" ) type addressDivisionGroupingBase struct { // the non-cacheBitCount elements are assigned at creation and are immutable divisions divArray // either standard or large prefixLength PrefixLen // must align with the divisions if they store prefix lengths isMult bool // When a top-level section is created, it is assigned an address type, IPv4, IPv6, or MAC, // and determines if an *AddressDivisionGrouping can be converted back to a section of the original type. // // Type-specific functions in IPAddressSection and lower levels, such as functions returning strings, // can rely on this field. addrType addrType // assigned on creation only; for zero-value groupings it is never assigned, but in that case it is not needed since there is nothing to cache cache *valueCache } // this is used by methods that are used by both mac and ipv4/6, even though the prefix length assignment does not apply to MAC. It is also used by large division groupings. func (grouping *addressDivisionGroupingBase) initMultAndPrefLen() { segCount := grouping.GetDivisionCount() bitsSoFar := 0 if segCount != 0 { var previousSegmentPrefix PrefixLen isMultiple := false //bitsPerSegment := grouping.getBitsPerSegment() for i := 0; i < segCount; i++ { segment := grouping.getDivision(i) if !isMultiple && segment.isMultiple() { isMultiple = true grouping.isMult = true if grouping.prefixLength != nil { // nothing left to do break } } //Calculate the segment-level prefix // //Across an address prefixes are: //IPv6: (nil):...:(nil):(1 to 16):(0):...:(0) //or IPv4: ...(nil).(1 to 8).(0)... //For MAC, all segs have nil prefix since prefix is not segment-level segPrefix := segment.getDivisionPrefixLength() if previousSegmentPrefix == nil { if segPrefix != nil { newPref := bitsSoFar + segPrefix.bitCount() //newPref := getNetworkPrefixLen(bitsPerSegment, segPrefix.bitCount(), i) grouping.prefixLength = cacheBitCount(newPref) if isMultiple { // nothing left to do break } } } previousSegmentPrefix = segPrefix bitsSoFar += segment.GetBitCount() } } return } func (grouping *addressDivisionGroupingBase) getAddrType() addrType { return grouping.addrType } func (grouping *addressDivisionGroupingBase) isPrefixed() bool { return grouping.prefixLength != nil } func (grouping *addressDivisionGroupingBase) getPrefixLen() PrefixLen { return grouping.prefixLength } // GetPrefixLen returns the prefix length, or nil if there is no prefix length. // // A prefix length indicates the number of bits in the initial part of the address item that comprises the prefix. // // A prefix is a part of the address item that is not specific to that address but common amongst a group of such items, such as a CIDR prefix block subnet. func (grouping *addressDivisionGroupingBase) GetPrefixLen() PrefixLen { return grouping.getPrefixLen().copy() } // isMultiple returns whether this address or grouping represents more than one address or grouping. // Such addresses include CIDR/IP addresses (eg 1.2.3.4/11) or wildcard addresses (eg 1.2.*.4) or range addresses (eg 1.2.3-4.5) func (grouping *addressDivisionGroupingBase) isMultiple() bool { return grouping.isMult } // hasNoDivisions() returns whether this grouping is the zero grouping, // which is what you get when constructing a grouping or section with no divisions func (grouping *addressDivisionGroupingBase) hasNoDivisions() bool { divisions := grouping.divisions return divisions == nil || divisions.getDivisionCount() == 0 } // GetBitCount returns the total number of bits across all divisions func (grouping *addressDivisionGroupingBase) GetBitCount() (res BitCount) { for i := 0; i < grouping.GetDivisionCount(); i++ { res += grouping.getDivision(i).GetBitCount() } return } // GetByteCount returns the total number of bytes across all divisions (rounded up) func (grouping *addressDivisionGroupingBase) GetByteCount() int { return (int(grouping.GetBitCount()) + 7) >> 3 } // getDivision returns the division or panics if the index is negative or it is too large func (grouping *addressDivisionGroupingBase) getDivision(index int) *addressDivisionBase { return grouping.divisions.getDivision(index) } // GetGenericDivision returns the division as a DivisionType, // allowing all division types and aggregated division types to be represented by a single type, // useful for comparisons and other common uses. func (grouping *addressDivisionGroupingBase) GetGenericDivision(index int) DivisionType { return grouping.divisions.getGenericDivision(index) } // GetDivisionCount returns the number of divisions in this grouping. func (grouping *addressDivisionGroupingBase) GetDivisionCount() int { divisions := grouping.divisions if divisions != nil { return divisions.getDivisionCount() } return 0 } // IsZero returns whether this grouping matches exactly the value of zero. func (grouping *addressDivisionGroupingBase) IsZero() bool { divCount := grouping.GetDivisionCount() for i := 0; i < divCount; i++ { if !grouping.getDivision(i).IsZero() { return false } } return true } // IncludesZero returns whether this grouping includes the value of zero within its range. func (grouping *addressDivisionGroupingBase) IncludesZero() bool { divCount := grouping.GetDivisionCount() for i := 0; i < divCount; i++ { if !grouping.getDivision(i).IncludesZero() { return false } } return true } // IsMax returns whether this grouping matches exactly the maximum possible value, the value whose bits are all ones. func (grouping *addressDivisionGroupingBase) IsMax() bool { divCount := grouping.GetDivisionCount() for i := 0; i < divCount; i++ { if !grouping.getDivision(i).IsMax() { return false } } return true } // IncludesMax returns whether this grouping includes the max value, the value whose bits are all ones, within its range. func (grouping *addressDivisionGroupingBase) IncludesMax() bool { divCount := grouping.GetDivisionCount() for i := 0; i < divCount; i++ { if !grouping.getDivision(i).IncludesMax() { return false } } return true } // IsFullRange returns whether this address item represents all possible values attainable by an address item of this type. // // This is true if and only if both IncludesZero and IncludesMax return true. func (grouping *addressDivisionGroupingBase) IsFullRange() bool { divCount := grouping.GetDivisionCount() for i := 0; i < divCount; i++ { if !grouping.getDivision(i).IsFullRange() { return false } } return true } // GetSequentialBlockIndex gets the minimal division index for which all following divisions are full-range blocks. // // The division at this index is not a full-range block unless all divisions are full-range. // The division at this index and all following divisions form a sequential range. // For the full grouping to be sequential, the preceding divisions must be single-valued. func (grouping *addressDivisionGroupingBase) GetSequentialBlockIndex() int { divCount := grouping.GetDivisionCount() if divCount > 0 { for divCount--; divCount > 0 && grouping.getDivision(divCount).IsFullRange(); divCount-- { } } return divCount } // GetSequentialBlockCount provides the count of elements from the sequential block iterator, the minimal number of sequential address division groupings that comprise this address division grouping. func (grouping *addressDivisionGroupingBase) GetSequentialBlockCount() *big.Int { sequentialSegCount := grouping.GetSequentialBlockIndex() prefixLen := BitCount(0) for i := 0; i < sequentialSegCount; i++ { prefixLen += grouping.getDivision(i).GetBitCount() } return grouping.GetPrefixCountLen(prefixLen) // 0-1.0-1.*.* gives 1 as seq block index, and then you count only previous segments } func (grouping *addressDivisionGroupingBase) getCountBig() *big.Int { res := bigOne() count := grouping.GetDivisionCount() if count > 0 { for i := 0; i < count; i++ { div := grouping.getDivision(i) if div.isMultiple() { res.Mul(res, div.getCount()) } } } return res } func (grouping *addressDivisionGroupingBase) getPrefixCountBig() *big.Int { prefixLen := grouping.prefixLength if prefixLen == nil { return grouping.getCountBig() } return grouping.getPrefixCountLenBig(prefixLen.bitCount()) } func (grouping *addressDivisionGroupingBase) getPrefixCountLenBig(prefixLen BitCount) *big.Int { if prefixLen <= 0 { return bigOne() } else if prefixLen >= grouping.GetBitCount() { return grouping.getCountBig() } res := bigOne() if grouping.isMultiple() { divisionCount := grouping.GetDivisionCount() divPrefixLength := prefixLen for i := 0; i < divisionCount; i++ { div := grouping.getDivision(i) divBitCount := div.getBitCount() if div.isMultiple() { var divCount *big.Int if divPrefixLength < divBitCount { divCount = div.GetPrefixCountLen(divPrefixLength) } else { divCount = div.getCount() } res.Mul(res, divCount) } if divPrefixLength <= divBitCount { break } divPrefixLength -= divBitCount } } return res } func (grouping *addressDivisionGroupingBase) getBlockCountBig(segmentCount int) *big.Int { if segmentCount <= 0 { return bigOne() } divCount := grouping.GetDivisionCount() if segmentCount >= divCount { return grouping.getCountBig() } res := bigOne() if grouping.isMultiple() { for i := 0; i < segmentCount; i++ { division := grouping.getDivision(i) if division.isMultiple() { res.Mul(res, division.getCount()) } } } return res } func (grouping *addressDivisionGroupingBase) getCount() *big.Int { return grouping.cacheCount(grouping.getCountBig) } func (grouping *addressDivisionGroupingBase) getCachedCount() *big.Int { return grouping.cachedCount(grouping.getCountBig) } // GetPrefixCount returns the number of distinct prefix values in this item. // // The prefix length is given by GetPrefixLen. // // If this has a non-nil prefix length, returns the number of distinct prefix values. // // If this has a nil prefix length, returns the same value as GetCount. func (grouping *addressDivisionGroupingBase) GetPrefixCount() *big.Int { return grouping.cachePrefixCount(grouping.getPrefixCountBig) } // GetPrefixCountLen returns the number of distinct prefix values in this item for the given prefix length. func (grouping *addressDivisionGroupingBase) GetPrefixCountLen(prefixLen BitCount) *big.Int { return grouping.calcCount(func() *big.Int { return grouping.getPrefixCountLenBig(prefixLen) }) } // GetBlockCount returns the count of distinct values in the given number of initial (more significant) divisions. func (grouping *addressDivisionGroupingBase) GetBlockCount(divisionCount int) *big.Int { return grouping.calcCount(func() *big.Int { return grouping.getBlockCountBig(divisionCount) }) } func (grouping *addressDivisionGroupingBase) cacheCount(counter func() *big.Int) *big.Int { cache := grouping.cache if cache == nil { return grouping.calcCount(counter) } count := (*big.Int)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.cachedCount)))) if count == nil { count = grouping.calcCount(counter) dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.cachedCount)) atomicStorePointer(dataLoc, unsafe.Pointer(count)) } return new(big.Int).Set(count) } // cachedCount returns the cached count value, not a duplicate func (grouping *addressDivisionGroupingBase) cachedCount(counter func() *big.Int) *big.Int { cache := grouping.cache if cache == nil { return grouping.calcCount(counter) } count := (*big.Int)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.cachedCount)))) if count == nil { count = grouping.calcCount(counter) dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.cachedCount)) atomicStorePointer(dataLoc, unsafe.Pointer(count)) } return count } func (grouping *addressDivisionGroupingBase) calcCount(counter func() *big.Int) *big.Int { if grouping != nil && !grouping.isMultiple() { return bigOne() } return counter() } func (grouping *addressDivisionGroupingBase) calcUint64Count(counter func() uint64) uint64 { if grouping != nil && !grouping.isMultiple() { return 1 } return counter() } func (grouping *addressDivisionGroupingBase) cacheUint64PrefixCount(counter func() uint64) uint64 { cache := grouping.cache // isMultiple checks prior to this ensures cache not nil here if cache == nil { return grouping.calcUint64PrefixCount(counter) } count := (*big.Int)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.cachedPrefixCount)))) if count == nil { count64 := grouping.calcUint64PrefixCount(counter) count = new(big.Int).SetUint64(count64) dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.cachedPrefixCount)) atomicStorePointer(dataLoc, unsafe.Pointer(count)) return count64 } return count.Uint64() } func (grouping *addressDivisionGroupingBase) cachePrefixCount(counter func() *big.Int) *big.Int { cache := grouping.cache // isMultiple checks prior to this ensures cache not nil here if cache == nil { return grouping.calcPrefixCount(counter) } count := (*big.Int)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.cachedPrefixCount)))) if count == nil { count = grouping.calcPrefixCount(counter) dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.cachedPrefixCount)) atomicStorePointer(dataLoc, unsafe.Pointer(count)) } return new(big.Int).Set(count) } func (grouping *addressDivisionGroupingBase) calcPrefixCount(counter func() *big.Int) *big.Int { if !grouping.isMultiple() { return bigOne() } prefixLen := grouping.prefixLength if prefixLen == nil || prefixLen.bitCount() >= grouping.GetBitCount() { return grouping.getCount() } return counter() } func (grouping *addressDivisionGroupingBase) calcUint64PrefixCount(counter func() uint64) uint64 { if !grouping.isMultiple() { return 1 } //prefixLen := grouping.prefixLength //if prefixLen == nil || prefixLen.bitCount() >= grouping.GetBitCount() { // return grouping.getCount() //} return counter() } func (grouping *addressDivisionGroupingBase) getCachedBytes(calcBytes func() (bytes, upperBytes []byte)) (bytes, upperBytes []byte) { cache := grouping.cache if cache == nil { return calcBytes() } cached := (*bytesCache)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.bytesCache)))) if cached == nil { bytes, upperBytes = calcBytes() cached = &bytesCache{ lowerBytes: bytes, upperBytes: upperBytes, } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.bytesCache)) atomicStorePointer(dataLoc, unsafe.Pointer(cached)) } bytes = cached.lowerBytes upperBytes = cached.upperBytes return } // IsSequential returns whether the grouping represents a range of values that are sequential. // // Generally, this means that any division covering a range of values must be followed by divisions that are full range, covering all values. func (grouping *addressDivisionGroupingBase) IsSequential() bool { count := grouping.GetDivisionCount() if count > 1 { for i := 0; i < count; i++ { if grouping.getDivision(i).isMultiple() { for i++; i < count; i++ { if !grouping.getDivision(i).IsFullRange() { return false } } return true } } } return true } type bytesCache struct { lowerBytes, upperBytes []byte } type mixedCache struct { defaultMixedAddressSection *IPv6v4MixedAddressGrouping embeddedIPv4Section *IPv4AddressSection embeddedIPv6Section *EmbeddedIPv6AddressSection } type valueCache struct { cachedCount, cachedPrefixCount *big.Int cachedMaskLens *maskLenSetting bytesCache *bytesCache stringCache stringCache sectionCache *groupingCache mixed *mixedCache minPrefix PrefixLen equivalentPrefix *PrefixLen isSinglePrefixBlock *bool } type ipStringCache struct { normalizedWildcardString, fullString, sqlWildcardString, reverseDNSString, segmentedBinaryString *string } type ipv4StringCache struct { inetAtonOctalString, inetAtonHexString *string } type ipv6StringCache struct { normalizedIPv6String, compressedIPv6String, mixedString, compressedWildcardString, canonicalWildcardString, networkPrefixLengthString, base85String, uncString *string } type macStringCache struct { normalizedMACString, compressedMACString, dottedString, spaceDelimitedString *string } type stringCache struct { canonicalString *string octalString, octalStringPrefixed, binaryString, binaryStringPrefixed, hexString, hexStringPrefixed *string *ipv6StringCache *ipv4StringCache *ipStringCache *macStringCache } var zeroStringCache = stringCache{ ipv6StringCache: &ipv6StringCache{}, ipv4StringCache: &ipv4StringCache{}, ipStringCache: &ipStringCache{}, macStringCache: &macStringCache{}, } type groupingCache struct { lower, upper *AddressSection } type maskLenSetting struct { networkMaskLen, hostMaskLen PrefixLen } type divArray interface { getDivision(index int) *addressDivisionBase getGenericDivision(index int) DivisionType getDivisionCount() int fmt.Stringer } var zeroDivs = make([]*AddressDivision, 0) var zeroStandardDivArray = standardDivArray(zeroDivs) type standardDivArray []*AddressDivision func (grouping standardDivArray) getDivisionCount() int { return len(grouping) } func (grouping standardDivArray) getDivision(index int) *addressDivisionBase { return (*addressDivisionBase)(unsafe.Pointer(grouping[index])) } func (grouping standardDivArray) getGenericDivision(index int) DivisionType { return grouping[index] } func (grouping standardDivArray) copyDivisions(divs []*AddressDivision) (count int) { return copy(divs, grouping) } func (grouping standardDivArray) copySubDivisions(start, end int, divs []*AddressDivision) (count int) { return copy(divs, grouping[start:end]) } func (grouping standardDivArray) getSubDivisions(index, endIndex int) (divs []*AddressDivision) { return grouping[index:endIndex] } func (grouping standardDivArray) init() standardDivArray { if grouping == nil { return zeroStandardDivArray } return grouping } func (grouping standardDivArray) String() string { return fmt.Sprint([]*AddressDivision(grouping.init())) } var zeroLargeDivs = make([]*IPAddressLargeDivision, 0) var zeroLargeDivArray = largeDivArray(zeroLargeDivs) type largeDivArray []*IPAddressLargeDivision func (grouping largeDivArray) getDivisionCount() int { return len(grouping) } func (grouping largeDivArray) getDivision(index int) *addressDivisionBase { return (*addressDivisionBase)(unsafe.Pointer(grouping[index])) } func (grouping largeDivArray) getGenericDivision(index int) DivisionType { return grouping[index] } func (grouping largeDivArray) copyDivisions(divs []*IPAddressLargeDivision) (count int) { return copy(divs, grouping) } func (grouping largeDivArray) copySubDivisions(start, end int, divs []*IPAddressLargeDivision) (count int) { return copy(divs, grouping[start:end]) } func (grouping largeDivArray) getSubDivisions(index, endIndex int) (divs []*IPAddressLargeDivision) { return grouping[index:endIndex] } func (grouping largeDivArray) init() largeDivArray { if grouping == nil { return zeroLargeDivArray } return grouping } func (grouping largeDivArray) String() string { return fmt.Sprint([]*IPAddressLargeDivision(grouping.init())) } var _, _ divArray = standardDivArray{}, largeDivArray{} ipaddress-go-1.5.4/ipaddr/hostname.go000066400000000000000000001004561440250641600175220ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "net" "net/netip" "strings" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) const ( PortSeparator = ':' LabelSeparator = '.' IPv6StartBracket = '[' IPv6EndBracket = ']' ) func parseHostName(str string, params addrstrparam.HostNameParams) *HostName { str = strings.TrimSpace(str) res := &HostName{ str: str, hostCache: &hostCache{}, } res.validate(params) return res } // NewHostName constructs a HostName that will parse the given string according to the default parameters. func NewHostName(str string) *HostName { return parseHostName(str, defaultHostParameters) } // NewHostNameParams constructs a HostName that will parse the given string according to the given parameters. func NewHostNameParams(str string, params addrstrparam.HostNameParams) *HostName { var prms addrstrparam.HostNameParams if params == nil { prms = defaultHostParameters } else { prms = addrstrparam.CopyHostNameParams(params) } return parseHostName(str, prms) } // NewHostNameFromAddrPort constructs a HostName from an IP address and a port. func NewHostNameFromAddrPort(addr *IPAddress, port uint16) *HostName { portVal := PortInt(port) hostStr := toNormalizedAddrPortString(addr, portVal) parsedHost := parsedHost{ originalStr: hostStr, embeddedAddress: embeddedAddress{addressProvider: addr.getProvider()}, labelsQualifier: parsedHostIdentifierStringQualifier{port: cachePorts(portVal)}, } return &HostName{ str: hostStr, hostCache: &hostCache{normalizedString: &hostStr}, parsedHost: &parsedHost, } } // NewHostNameFromAddr constructs a HostName from an IP address. func NewHostNameFromAddr(addr *IPAddress) *HostName { hostStr := addr.ToNormalizedString() return newHostNameFromAddr(hostStr, addr) } func newHostNameFromAddr(hostStr string, addr *IPAddress) *HostName { // same as HostName(String hostStr, ParsedHost parsed) { parsedHost := parsedHost{ originalStr: hostStr, embeddedAddress: embeddedAddress{addressProvider: addr.getProvider()}, } return &HostName{ str: hostStr, hostCache: &hostCache{normalizedString: &hostStr}, parsedHost: &parsedHost, } } // NewHostNameFromNetTCPAddr constructs a HostName from a net.TCPAddr. func NewHostNameFromNetTCPAddr(addr *net.TCPAddr) (*HostName, addrerr.AddressValueError) { return newHostNameFromSocketAddr(addr.IP, addr.Port, addr.Zone) } // NewHostNameFromNetUDPAddr constructs a HostName from a net.UDPAddr. func NewHostNameFromNetUDPAddr(addr *net.UDPAddr) (*HostName, addrerr.AddressValueError) { return newHostNameFromSocketAddr(addr.IP, addr.Port, addr.Zone) } func newHostNameFromSocketAddr(ip net.IP, port int, zone string) (hostName *HostName, err addrerr.AddressValueError) { var ipAddr *IPAddress ipAddr, err = NewIPAddressFromNetIPAddr(&net.IPAddr{IP: ip, Zone: zone}) if err != nil { return } else if ipAddr == nil { err = &addressValueError{addressError: addressError{key: "ipaddress.error.exceeds.size"}} return } portVal := PortInt(port) hostStr := toNormalizedAddrPortString(ipAddr, portVal) parsedHost := parsedHost{ originalStr: hostStr, embeddedAddress: embeddedAddress{addressProvider: ipAddr.getProvider()}, labelsQualifier: parsedHostIdentifierStringQualifier{port: cachePorts(portVal)}, } hostName = &HostName{ str: hostStr, hostCache: &hostCache{normalizedString: &hostStr}, parsedHost: &parsedHost, } return } // NewHostNameFromNetIP constructs a HostName from a net.IP. func NewHostNameFromNetIP(bytes net.IP) (hostName *HostName, err addrerr.AddressValueError) { var addr *IPAddress addr, err = NewIPAddressFromNetIP(bytes) if err != nil { return } else if addr == nil { err = &addressValueError{addressError: addressError{key: "ipaddress.error.exceeds.size"}} return } hostName = NewHostNameFromAddr(addr) return } // NewHostNameFromPrefixedNetIP constructs a HostName from a net.IP paired with a prefix length. func NewHostNameFromPrefixedNetIP(bytes net.IP, prefixLen PrefixLen) (hostName *HostName, err addrerr.AddressValueError) { var addr *IPAddress addr, err = NewIPAddressFromPrefixedNetIP(bytes, prefixLen) if err != nil { return } else if addr == nil { err = &addressValueError{addressError: addressError{key: "ipaddress.error.exceeds.size"}} return } hostName = NewHostNameFromAddr(addr) return } // NewHostNameFromNetIPAddr constructs a HostName from a net.IPAddr. func NewHostNameFromNetIPAddr(addr *net.IPAddr) (hostName *HostName, err addrerr.AddressValueError) { var ipAddr *IPAddress ipAddr, err = NewIPAddressFromNetIPAddr(addr) if err != nil { return } else if ipAddr == nil { err = &addressValueError{addressError: addressError{key: "ipaddress.error.exceeds.size"}} return } hostName = NewHostNameFromAddr(ipAddr) return } // NewHostNameFromPrefixedNetIPAddr constructs a HostName from a net.IPAddr paired with a prefix length. func NewHostNameFromPrefixedNetIPAddr(addr *net.IPAddr, prefixLen PrefixLen) (hostName *HostName, err addrerr.AddressValueError) { var ipAddr *IPAddress ipAddr, err = NewIPAddressFromPrefixedNetIPAddr(addr, prefixLen) if err != nil { return } else if ipAddr == nil { err = &addressValueError{addressError: addressError{key: "ipaddress.error.exceeds.size"}} return } hostName = NewHostNameFromAddr(ipAddr) return } // NewHostNameFromNetNetIPAddr constructs a host name from a netip.Addr. func NewHostNameFromNetNetIPAddr(addr netip.Addr) *HostName { ipAddr := NewIPAddressFromNetNetIPAddr(addr) return NewHostNameFromAddr(ipAddr) } // NewHostNameFromNetNetIPPrefix constructs a host name from a netip.Prefix. func NewHostNameFromNetNetIPPrefix(addr netip.Prefix) (hostName *HostName, err addrerr.AddressValueError) { var ipAddr *IPAddress ipAddr, err = NewIPAddressFromNetNetIPPrefix(addr) if err == nil { hostName = NewHostNameFromAddr(ipAddr) } return } // NewHostNameFromNetNetIPAddrPort constructs a host name from a netip.AddrPort. func NewHostNameFromNetNetIPAddrPort(addrPort netip.AddrPort) *HostName { port := addrPort.Port() addr := addrPort.Addr() ipAddr := NewIPAddressFromNetNetIPAddr(addr) return NewHostNameFromAddrPort(ipAddr, port) } var defaultHostParameters = new(addrstrparam.HostNameParamsBuilder).ToParams() var zeroHost = NewHostName("") type resolveData struct { resolvedAddrs []*IPAddress err error } type hostCache struct { resolveData *resolveData normalizedString *string } // HostName represents an internet host name. Can be a fully qualified domain name, a simple host name, or an ip address string. // It can also include a port number or service name (which maps to a port). // It can include a prefix length or mask for either an ipaddress or host name string. An IPv6 address can have an IPv6 zone. // // Supported Formats // // You can use all host or address formats supported by nmap and all address formats supported by IPAddressString. // All manners of domain names are supported. When adding a prefix length or mask to a host name string, it is to denote the subnet of the resolved address. // // Validation is done separately from DNS resolution to avoid unnecessary DNS lookups. // // See RFC 3513, RFC 2181, RFC 952, RFC 1035, RFC 1034, RFC 1123, RFC 5890 or the list of rfcs for IPAddress. For IPv6 addresses in host, see RFC 2732 specifying "[]" notation // and RFC 3986 and RFC 4038 (combining IPv6 "[]" notation with prefix or zone) and SMTP RFC 2821 for alternative uses of "[]" notation for both IPv4 and IPv6. type HostName struct { str string parsedHost *parsedHost validateError addrerr.HostNameError *hostCache } func (host *HostName) init() *HostName { if host.parsedHost == nil && host.validateError == nil { // the only way params can be nil is when str == "" as well return zeroHost } return host } // GetValidationOptions returns the validation options supplied when constructing the HostName, or the default validation options if none were supplied. // It returns nil if no options were used to construct. func (host *HostName) GetValidationOptions() addrstrparam.HostNameParams { return host.init().parsedHost.params } func (host *HostName) validate(validationOptions addrstrparam.HostNameParams) { parsed, validateError := validator.validateHostName(host, validationOptions) if validateError != nil && parsed == nil { parsed = &parsedHost{originalStr: host.str, params: validationOptions} } host.parsedHost, host.validateError = parsed, validateError } // Validate validates that this string is a valid address, and if not, returns an error with a descriptive message indicating why it is not. func (host *HostName) Validate() addrerr.HostNameError { return host.init().validateError } // String implements the [fmt.Stringer] interface, // returning the original string used to create this HostName (altered by strings.TrimSpace if a host name and not an address), // or "" if the receiver is a nil pointer. func (host *HostName) String() string { if host == nil { return nilString() } return host.str } // Format implements the [fmt.Formatter] interface. // It accepts the verbs hat are applicable to strings, // namely the verbs %s, %q, %x and %X. func (addrStr HostName) Format(state fmt.State, verb rune) { s := flagsFromState(state, verb) _, _ = state.Write([]byte(fmt.Sprintf(s, addrStr.str))) } // IsAddressString returns whether this host name is a string representing an IP address or subnet. func (host *HostName) IsAddressString() bool { host = host.init() return host.IsValid() && host.parsedHost.isAddressString() } // IsAddress returns whether this host name is a string representing a valid specific IP address or subnet. func (host *HostName) IsAddress() bool { if host.IsAddressString() { addr, _ := host.init().parsedHost.asAddress() return addr != nil } return false } // AsAddress returns the address if this host name represents an ip address. Otherwise, this returns nil. // Note that the translation includes prefix lengths and IPv6 zones. // // This does not resolve addresses or return resolved addresses. // Call ToAddress or GetAddress to get the resolved address. // // In cases such as IPv6 literals and reverse-DNS hosts, you can check the relevant methods isIpv6Literal or isReverseDNS, // in which case this method should return the associated address. func (host *HostName) AsAddress() *IPAddress { if host.IsAddress() { addr, _ := host.parsedHost.asAddress() return addr } return nil } // IsAllAddresses returns whether this is an IP address that represents the set all all valid IP addresses (as opposed to an empty string, a specific address, or an invalid format). func (host *HostName) IsAllAddresses() bool { host = host.init() return host.IsValid() && host.parsedHost.getAddressProvider().isProvidingAllAddresses() } // IsEmpty returns true if the host name is empty (zero-length). func (host *HostName) IsEmpty() bool { host = host.init() return host.IsValid() && ((host.IsAddressString() && host.parsedHost.getAddressProvider().isProvidingEmpty()) || len(host.GetNormalizedLabels()) == 0) } // GetAddress attempts to convert this host name to an IP address. // If this represents an ip address, returns that address. // If this represents a host, returns the resolved ip address of that host. // Otherwise, returns nil. // GetAddress is similar to ToAddress but does not return any errors. // // If you wish to get the represented address while avoiding DNS resolution, use AsAddress or AsAddressString. func (host *HostName) GetAddress() *IPAddress { addr, _ := host.ToAddress() return addr } // ToAddress resolves to an address. // This method can potentially return a list of resolved addresses and an error as well, if some resolved addresses were invalid. func (host *HostName) ToAddress() (addr *IPAddress, err addrerr.AddressError) { addresses, err := host.ToAddresses() if len(addresses) > 0 { addr = addresses[0] } return } // ToAddresses resolves to one or more addresses. // The error can be addrerr.AddressStringError,addrerr.IncompatibleAddressError, or addrerr.HostNameError. // This method can potentially return a list of resolved addresses and an error as well if some resolved addresses were invalid. func (host *HostName) ToAddresses() (addrs []*IPAddress, err addrerr.AddressError) { host = host.init() data := (*resolveData)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&host.resolveData)))) if data == nil { //note that validation handles empty address resolution err = host.Validate() //addrerr.HostNameError if err != nil { return } // http://networkbit.ch/golang-dns-lookup/ parsedHost := host.parsedHost if parsedHost.isAddressString() { addr, addrErr := parsedHost.asAddress() //addrerr.IncompatibleAddressError addrs, err = []*IPAddress{addr}, addrErr //note there is no need to apply prefix or mask here, it would have been applied to the address already } else { strHost := parsedHost.getHost() validationOptions := host.GetValidationOptions() if len(strHost) == 0 { addrs = []*IPAddress{} } else { var ips []net.IP ips, lookupErr := net.LookupIP(strHost) if lookupErr != nil { //Note we do not set resolveData, so we will attempt to resolve again err = &hostNameNestedError{nested: lookupErr, hostNameError: hostNameError{addressError{str: strHost, key: "ipaddress.host.error.host.resolve"}}} return } count := len(ips) addrs = make([]*IPAddress, 0, count) var errs []addrerr.AddressError for j := 0; j < count; j++ { ip := ips[j] if ipv4 := ip.To4(); ipv4 != nil { ip = ipv4 } networkPrefixLength := parsedHost.getNetworkPrefixLen() byteLen := len(ip) if networkPrefixLength == nil { mask := parsedHost.getMask() if mask != nil { maskBytes := mask.Bytes() if len(maskBytes) == byteLen { for i := 0; i < byteLen; i++ { ip[i] &= maskBytes[i] } networkPrefixLength = mask.GetBlockMaskPrefixLen(true) } } } ipAddr, addrErr := NewIPAddressFromPrefixedNetIP(ip, networkPrefixLength) if addrErr != nil { errs = append(errs, addrErr) } else { cache := ipAddr.cache if cache != nil { cache.identifierStr = &identifierStr{host} } addrs = append(addrs, ipAddr) } } if len(errs) > 0 { err = &mergedError{AddressError: &hostNameError{addressError{str: strHost, key: "ipaddress.host.error.host.resolve"}}, merged: errs} } count = len(addrs) if count > 0 { // sort by preferred version preferredVersion := IPVersion(validationOptions.GetPreferredVersion()) if !preferredVersion.IsIndeterminate() { preferredAddrType := preferredVersion.toType() boundaryCase := 8 // we sort differently based on list size if count > boundaryCase { c := 0 newAddrs := make([]*IPAddress, count) for _, val := range addrs { if val.getAddrType() == preferredAddrType { newAddrs[c] = val c++ } } for i := 0; c < count; i++ { val := addrs[i] if val.getAddrType() != preferredAddrType { newAddrs[c] = val c++ } } addrs = newAddrs } else { preferredIndex := 0 top: for i := 0; i < count; i++ { val := addrs[i] if val.getAddrType() != preferredAddrType { var j int if preferredIndex == 0 { j = i + 1 } else { j = preferredIndex } for ; j < len(addrs); j++ { if addrs[j].getAddrType() == preferredAddrType { // move the preferred into the non-preferred's spot addrs[i] = addrs[j] // don't swap so the non-preferred order is preserved, // instead shift each upwards by one spot k := i + 1 for ; k < j; k++ { addrs[k], val = val, addrs[k] } addrs[k] = val preferredIndex = j + 1 continue top } } // no more preferred so nothing more to do break } } } } } } } data = &resolveData{addrs, err} dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&host.resolveData)) atomicStorePointer(dataLoc, unsafe.Pointer(data)) } return data.resolvedAddrs, nil } // IsValid returns whether this represents a valid host name or IP address format. func (host *HostName) IsValid() bool { return host.init().Validate() == nil } // AsAddressString returns the address string if this host name represents an ip address or an ip address string. Otherwise, this returns nil. // Note that translation includes prefix lengths and IPv6 zones. // This does not resolve host names. Call ToAddress or GetAddress to get the resolved address. func (host *HostName) AsAddressString() *IPAddressString { host = host.init() if host.IsAddressString() { return host.parsedHost.asGenericAddressString() } return nil } // GetPort returns the port if a port was supplied, otherwise it returns nil. func (host *HostName) GetPort() Port { host = host.init() if host.IsValid() { return host.parsedHost.getPort().copy() } return nil } // GetService returns the service name if a service name was supplied (which is typically mapped to a port), otherwise it returns an empty string. func (host *HostName) GetService() string { host = host.init() if host.IsValid() { return host.parsedHost.getService() } return "" } // ToNormalizedString provides a normalized string which is lowercase for host strings, and which is the normalized string for addresses. func (host *HostName) ToNormalizedString() string { if str := host.normalizedString; str != nil { return *str } return host.toNormalizedString(false, false) } // ToNormalizedWildcardString provides a normalized string which is lowercase for host strings, and which is a normalized string for addresses. func (host *HostName) ToNormalizedWildcardString() string { return host.toNormalizedString(false, false) } // ToQualifiedString provides a normalized string which is lowercase for host strings, and which is a normalized string for addresses. func (host *HostName) ToQualifiedString() string { return host.toNormalizedString(false, true) } func (host *HostName) toNormalizedString(wildcard, addTrailingDot bool) string { if host.IsValid() { var builder strings.Builder if host.IsAddress() { toNormalizedHostString(host.AsAddress(), wildcard, &builder) } else if host.IsAddressString() { builder.WriteString(host.AsAddressString().ToNormalizedString()) } else { builder.WriteString(host.parsedHost.getHost()) if addTrailingDot { builder.WriteByte(LabelSeparator) } /* * If prefix or mask is supplied and there is an address, it is applied directly to the address provider, so * we need only check for those things here * * Also note that ports and prefix/mask cannot appear at the same time, so this does not interfere with the port code below. */ networkPrefixLength := host.parsedHost.getEquivalentPrefixLen() if networkPrefixLength != nil { builder.WriteByte(PrefixLenSeparator) toUnsignedString(uint64(networkPrefixLength.bitCount()), 10, &builder) } else { mask := host.parsedHost.getMask() if mask != nil { builder.WriteByte(PrefixLenSeparator) builder.WriteString(mask.ToNormalizedString()) } } } port := host.parsedHost.getPort() if port != nil { toNormalizedPortString(port.portNum(), &builder) } else { service := host.parsedHost.getService() if service != "" { builder.WriteByte(PortSeparator) builder.WriteString(service) } } return builder.String() } return host.str } func toNormalizedPortString(port PortInt, builder *strings.Builder) { builder.WriteByte(PortSeparator) toUnsignedString(uint64(port), 10, builder) } func toNormalizedHostString(addr *IPAddress, wildcard bool, builder *strings.Builder) { if addr.isIPv6() { if !wildcard && addr.IsPrefixed() { // prefix needs to be outside the brackets normalized := addr.ToNormalizedString() index := strings.IndexByte(normalized, PrefixLenSeparator) builder.WriteByte(IPv6StartBracket) translateReserved(addr.ToIPv6(), normalized[:index], builder) builder.WriteByte(IPv6EndBracket) builder.WriteString(normalized[index:]) } else { normalized := addr.ToNormalizedWildcardString() builder.WriteByte(IPv6StartBracket) translateReserved(addr.ToIPv6(), normalized, builder) builder.WriteByte(IPv6EndBracket) } } else { if wildcard { builder.WriteString(addr.ToNormalizedWildcardString()) } else { builder.WriteString(addr.ToNormalizedString()) } } } func toNormalizedAddrPortString(addr *IPAddress, port PortInt) string { builder := strings.Builder{} toNormalizedHostString(addr, false, &builder) toNormalizedPortString(port, &builder) return builder.String() } // Equal returns true if the given host name matches this one. // For hosts to match, they must represent the same addresses or have the same host names. // Hosts are not resolved when matching. Also, hosts must have the same port or service. // They must have the same masks if they are host names. // Even if two hosts are invalid, they match if they have the same invalid string. func (host *HostName) Equal(other *HostName) bool { if host == nil { return other == nil } else if other == nil { return false } host = host.init() other = other.init() if host == other { return true } if host.IsValid() { if other.IsValid() { parsedHost := host.parsedHost otherParsedHost := other.parsedHost if parsedHost.isAddressString() { return otherParsedHost.isAddressString() && parsedHost.asGenericAddressString().Equal(otherParsedHost.asGenericAddressString()) && parsedHost.getPort().Equal(otherParsedHost.getPort()) && parsedHost.getService() == otherParsedHost.getService() } if otherParsedHost.isAddressString() { return false } thisHost := parsedHost.getHost() otherHost := otherParsedHost.getHost() if thisHost != otherHost { return false } return parsedHost.getEquivalentPrefixLen().Equal(otherParsedHost.getEquivalentPrefixLen()) && parsedHost.getMask().Equal(otherParsedHost.getMask()) && parsedHost.getPort().Equal(otherParsedHost.getPort()) && parsedHost.getService() == otherParsedHost.getService() } return false } return !other.IsValid() && host.String() == other.String() } // GetNormalizedLabels returns an array of normalized strings for this host name instance. // // If this represents an IP address, the address segments are separated into the returned array. // If this represents a host name string, the domain name segments are separated into the returned array, // with the top-level domain name (right-most segment) as the last array element. // // The individual segment strings are normalized in the same way as ToNormalizedString. // // Ports, service name strings, prefix lengths, and masks are all omitted from the returned array. func (host *HostName) GetNormalizedLabels() []string { host = host.init() if host.IsValid() { return host.parsedHost.getNormalizedLabels() } else { str := host.str if len(str) == 0 { return []string{} } return []string{str} } } // GetHost returns the host string normalized but without port, service, prefix or mask. // // If an address, returns the address string normalized, but without port, service, prefix, mask, or brackets for IPv6. // // To get a normalized string encompassing all details, use ToNormalizedString. // // If not a valid host, returns the zero string. func (host *HostName) GetHost() string { host = host.init() if host.IsValid() { return host.parsedHost.getHost() } return "" } // IsUncIPv6Literal returns whether this host name is an Uniform Naming Convention IPv6 literal host name. func (host *HostName) IsUncIPv6Literal() bool { host = host.init() return host.IsValid() && host.parsedHost.isUNCIPv6Literal() } // IsReverseDNS returns whether this host name is a reverse-DNS string host name. func (host *HostName) IsReverseDNS() bool { host = host.init() return host.IsValid() && host.parsedHost.isReverseDNS() } // GetNetworkPrefixLen returns the prefix length, if a prefix length was supplied, // either as part of an address or as part of a domain (in which case the prefix applies to any resolved address). // Otherwise, GetNetworkPrefixLen returns nil. func (host *HostName) GetNetworkPrefixLen() PrefixLen { if host.IsAddress() { addr, err := host.parsedHost.asAddress() if err == nil { return addr.getNetworkPrefixLen().copy() } } else if host.IsAddressString() { return host.parsedHost.asGenericAddressString().getNetworkPrefixLen().copy() } else if host.IsValid() { return host.parsedHost.getEquivalentPrefixLen().copy() } return nil } // GetMask returns the resulting mask value if a mask was provided with this host name. func (host *HostName) GetMask() *IPAddress { if host.IsValid() { if host.parsedHost.isAddressString() { return host.parsedHost.getAddressProvider().getProviderMask() } return host.parsedHost.getMask() } return nil } // ResolvesToSelf returns whether this represents, or resolves to, // a host or address representing the same host. func (host *HostName) ResolvesToSelf() bool { if host.IsSelf() { return true } else if host.GetAddress() != nil { host.resolveData.resolvedAddrs[0].IsLoopback() } return false } // IsSelf returns whether this represents a host or address representing the same host. // Also see IsLocalHost and IsLoopback. func (host *HostName) IsSelf() bool { return host.IsLocalHost() || host.IsLoopback() } // IsLocalHost returns whether this host is "localhost". func (host *HostName) IsLocalHost() bool { return host.IsValid() && strings.EqualFold(host.str, "localhost") } // IsLoopback returns whether this host has the loopback address, such as "::1" or "127.0.0.1". // // Also see IsSelf. func (host *HostName) IsLoopback() bool { return host.IsAddress() && host.AsAddress().IsLoopback() } // ToNetTCPAddrService returns the TCPAddr if this HostName both resolves to an address and has an associated service or port, otherwise returns nil. func (host *HostName) ToNetTCPAddrService(serviceMapper func(string) Port) *net.TCPAddr { if host.IsValid() { port := host.GetPort() if port == nil && serviceMapper != nil { service := host.GetService() if service != "" { port = serviceMapper(service) } } if port != nil { if addr := host.GetAddress(); addr != nil { return &net.TCPAddr{ IP: addr.GetNetIP(), Port: port.portNum(), Zone: string(addr.zone), } } } } return nil } // ToNetTCPAddr returns the TCPAddr if this HostName both resolves to an address and has an associated port. // Otherwise, it returns nil. func (host *HostName) ToNetTCPAddr() *net.TCPAddr { return host.ToNetTCPAddrService(nil) } // ToNetUDPAddrService returns the UDPAddr if this HostName both resolves to an address and has an associated service or port. func (host *HostName) ToNetUDPAddrService(serviceMapper func(string) Port) *net.UDPAddr { tcpAddr := host.ToNetTCPAddrService(serviceMapper) if tcpAddr != nil { return &net.UDPAddr{ IP: tcpAddr.IP, Port: tcpAddr.Port, Zone: tcpAddr.Zone, } } return nil } // ToNetUDPAddr returns the UDPAddr if this HostName both resolves to an address and has an associated port. func (host *HostName) ToNetUDPAddr(serviceMapper func(string) Port) *net.UDPAddr { return host.ToNetUDPAddrService(serviceMapper) } // ToNetIP is similar to ToAddress but returns the resulting address as a net.IP. func (host *HostName) ToNetIP() net.IP { if addr, err := host.ToAddress(); addr != nil && err == nil { return addr.GetNetIP() } return nil } // ToNetIPAddr is similar to ToAddress but returns the resulting address as a net.IPAddr. func (host *HostName) ToNetIPAddr() *net.IPAddr { if addr, err := host.ToAddress(); addr != nil && err == nil { return &net.IPAddr{ IP: addr.GetNetIP(), Zone: string(addr.zone), } } return nil } // Compare returns a negative integer, zero, or a positive integer if this host name is less than, equal, or greater than the given host name. // Any address item is comparable to any other. func (host *HostName) Compare(other *HostName) int { if host == other { return 0 } else if host == nil { return -1 } else if other == nil { return 1 } if host.IsValid() { if other.IsValid() { parsedHost := host.parsedHost otherParsedHost := other.parsedHost if parsedHost.isAddressString() { if otherParsedHost.isAddressString() { result := parsedHost.asGenericAddressString().Compare(otherParsedHost.asGenericAddressString()) if result != 0 { return result } //fall through to compare ports } else { return -1 } } else if otherParsedHost.isAddressString() { return 1 } else { //both are non-address hosts normalizedLabels := parsedHost.getNormalizedLabels() otherNormalizedLabels := otherParsedHost.getNormalizedLabels() oneLen := len(normalizedLabels) twoLen := len(otherNormalizedLabels) var minLen int if oneLen < twoLen { minLen = oneLen } else { minLen = twoLen } for i := 1; i <= minLen; i++ { one := normalizedLabels[oneLen-i] two := otherNormalizedLabels[twoLen-i] result := strings.Compare(one, two) if result != 0 { return result } } if oneLen != twoLen { return oneLen - twoLen } //keep in mind that hosts can has masks/prefixes or ports, but not both networkPrefixLength := parsedHost.getEquivalentPrefixLen() otherPrefixLength := otherParsedHost.getEquivalentPrefixLen() if networkPrefixLength != nil { if otherPrefixLength != nil { if *networkPrefixLength != *otherPrefixLength { return otherPrefixLength.bitCount() - networkPrefixLength.bitCount() } //fall through to compare ports } else { return 1 } } else { if otherPrefixLength != nil { return -1 } mask := parsedHost.getMask() otherMask := otherParsedHost.getMask() if mask != nil { if otherMask != nil { ret := mask.Compare(otherMask) if ret != 0 { return ret } //fall through to compare ports } else { return 1 } } else { if otherMask != nil { return -1 } //fall through to compare ports } } //end non-address host compare } //two equivalent address strings or two equivalent hosts, now check port and service names portOne := parsedHost.getPort() portTwo := otherParsedHost.getPort() portRet := portOne.Compare(portTwo) if portRet != 0 { return portRet } serviceOne := parsedHost.getService() serviceTwo := otherParsedHost.getService() if serviceOne != "" { if serviceTwo != "" { ret := strings.Compare(serviceOne, serviceTwo) if ret != 0 { return ret } } else { return 1 } } else if serviceTwo != "" { return -1 } return 0 } else { return 1 } } else if other.IsValid() { return -1 } return strings.Compare(host.String(), other.String()) } // Wrap wraps this host name, returning a WrappedHostName, an implementation of ExtendedIdentifierString, // which can be used to write code that works with a host identifier string including [IPAddressString], [MACAddressString], and [HostName]. func (host *HostName) Wrap() ExtendedIdentifierString { return WrappedHostName{host} } func translateReserved(addr *IPv6Address, str string, builder *strings.Builder) { //This is particularly targeted towards the zone if !addr.HasZone() { builder.WriteString(str) return } index := strings.IndexByte(str, IPv6ZoneSeparator) var translated = builder translated.WriteString(str[0:index]) translated.WriteString("%25") for i := index + 1; i < len(str); i++ { c := str[i] if isReserved(c) { translated.WriteByte('%') toUnsignedString(uint64(c), 16, translated) } else { translated.WriteByte(c) } } } ipaddress-go-1.5.4/ipaddr/increment.go000066400000000000000000000167771440250641600177040ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math" "math/big" ) // returns true for overflow func checkOverflow( // used by IPv4 and MAC increment int64, lowerValue, upperValue, countMinus1 uint64, maxValue uint64) bool { if increment < 0 { if lowerValue < uint64(-increment) { return true } } else { uIncrement := uint64(increment) if uIncrement > countMinus1 { if countMinus1 > 0 { uIncrement -= countMinus1 } room := maxValue - upperValue if uIncrement > room { return true } } } return false } func checkOverflowBig( // used by MAC and IPv6 increment int64, bigIncrement, lowerValue, upperValue, count *big.Int, maxValue func() *big.Int) bool { isMultiple := count.CmpAbs(bigOneConst()) > 0 if increment < 0 { if lowerValue.CmpAbs(bigIncrement.Neg(bigIncrement)) < 0 { return true } } else { if isMultiple { bigIncrement.Sub(bigIncrement, count.Sub(count, bigOneConst())) } maxVal := maxValue() if bigIncrement.CmpAbs(maxVal.Sub(maxVal, upperValue)) > 0 { return true } } return false } // Handles the cases in which we can use longs rather than BigInteger func fastIncrement( // used by IPv6 section *AddressSection, inc int64, creator addressSegmentCreator, lowerProducer, upperProducer func() *AddressSection, prefixLength PrefixLen) *AddressSection { if inc >= 0 { count := section.GetCount() uincrement := uint64(inc) var maxUint64 big.Int maxUint64.SetUint64(math.MaxUint64) countMinus1 := count.Sub(count, bigOneConst()) if countMinus1.CmpAbs(&maxUint64) <= 0 { longCount := count.Uint64() if longCount > uincrement { if longCount == uincrement+1 { return upperProducer() } return incrementRange(section, inc, lowerProducer, prefixLength) } upperValue := section.GetUpperValue() if upperValue.CmpAbs(&maxUint64) <= 0 { value := section.GetValue() return increment( section, inc, creator, countMinus1.Uint64(), value.Uint64(), upperValue.Uint64(), lowerProducer, upperProducer, prefixLength) } } } else { var maxUint64 big.Int maxUint64.SetUint64(math.MaxUint64) value := section.GetValue() if value.CmpAbs(&maxUint64) <= 0 { return add(lowerProducer(), value.Uint64(), inc, creator, prefixLength) } } return nil } //this does not handle overflow, overflow should be checked before calling this func increment( // used by IPv4 and MAC section *AddressSection, increment int64, creator addressSegmentCreator, countMinus1 uint64, lowerValue, upperValue uint64, lowerProducer, upperProducer func() *AddressSection, prefixLength PrefixLen) *AddressSection { if !section.isMultiple() { return add(section, lowerValue, increment, creator, prefixLength) } isDecrement := increment <= 0 if isDecrement { //we know lowerValue + increment >= 0 because we already did an overflow check return add(lowerProducer(), lowerValue, increment, creator, prefixLength) } uIncrement := uint64(increment) if countMinus1 >= uIncrement { if countMinus1 == uIncrement { return upperProducer() } return incrementRange(section, increment, lowerProducer, prefixLength) } if uIncrement <= math.MaxUint64-upperValue { return add(upperProducer(), upperValue, int64(uIncrement-countMinus1), creator, prefixLength) } return addBig(upperProducer(), new(big.Int).SetUint64(uIncrement-countMinus1), creator, prefixLength) } //this does not handle overflow, overflow should be checked before calling this func incrementBig( // used by MAC and IPv6 section *AddressSection, increment int64, bigIncrement *big.Int, creator addressSegmentCreator, lowerProducer, upperProducer func() *AddressSection, prefixLength PrefixLen) *AddressSection { if !section.isMultiple() { return addBig(section, bigIncrement, creator, prefixLength) } isDecrement := increment <= 0 if isDecrement { return addBig(lowerProducer(), bigIncrement, creator, prefixLength) } count := section.GetCount() incrementPlus1 := bigIncrement.Add(bigIncrement, bigOneConst()) countCompare := count.CmpAbs(incrementPlus1) if countCompare <= 0 { if countCompare == 0 { return upperProducer() } return addBig(upperProducer(), incrementPlus1.Sub(incrementPlus1, count), creator, prefixLength) } return incrementRange(section, increment, lowerProducer, prefixLength) } // rangeIncrement the positive value of the number of increments through the range (0 means take lower or upper value in range) func incrementRange( section *AddressSection, increment int64, lowerProducer func() *AddressSection, prefixLength PrefixLen) *AddressSection { if increment == 0 { return lowerProducer() } segCount := section.GetSegmentCount() newSegments := make([]*AddressDivision, segCount) for i := segCount - 1; i >= 0; i-- { seg := section.GetSegment(i) segRange := seg.GetValueCount() segRange64 := int64(segRange) revolutions := increment / segRange64 remainder := increment % segRange64 val := seg.getSegmentValue() + SegInt(remainder) segPrefixLength := getSegmentPrefixLength(section.GetBitsPerSegment(), prefixLength, i) newSegment := createAddressDivision(seg.deriveNewMultiSeg(val, val, segPrefixLength)) newSegments[i] = newSegment if revolutions == 0 { for i--; i >= 0; i-- { original := section.GetSegment(i) val = original.getSegmentValue() segPrefixLength = getSegmentPrefixLength(section.GetBitsPerSegment(), prefixLength, i) newSegment = createAddressDivision(seg.deriveNewMultiSeg(val, val, segPrefixLength)) newSegments[i] = newSegment } break } else { increment = revolutions } } return createSection(newSegments, prefixLength, section.getAddrType()) } //this does not handle overflow, overflow should be checked before calling this func addBig(section *AddressSection, increment *big.Int, creator addressSegmentCreator, prefixLength PrefixLen) *AddressSection { segCount := section.GetSegmentCount() fullValue := section.GetValue() fullValue.Add(fullValue, increment) expectedByteCount := section.GetByteCount() bytes := fullValue.Bytes() // could use FillBytes but that only came with 1.15 segments, _ := toSegments( bytes, segCount, section.GetBytesPerSegment(), section.GetBitsPerSegment(), //expectedByteCount, creator, prefixLength) res := createSection(segments, prefixLength, section.getAddrType()) if expectedByteCount == len(bytes) && res.cache != nil { res.cache.bytesCache = &bytesCache{ lowerBytes: bytes, upperBytes: bytes, } } return res } func add(section *AddressSection, fullValue uint64, increment int64, creator addressSegmentCreator, prefixLength PrefixLen) *AddressSection { segCount := section.GetSegmentCount() var val uint64 if increment < 0 { val = fullValue - uint64(-increment) } else { val = fullValue + uint64(increment) } newSegs := createSegmentsUint64( segCount, 0, val, section.GetBytesPerSegment(), section.GetBitsPerSegment(), creator, prefixLength) return createSection(newSegs, prefixLength, section.getAddrType()) } ipaddress-go-1.5.4/ipaddr/ipaddr.go000066400000000000000000003564051440250641600171560ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" "math/big" "net" "net/netip" "strings" "unsafe" ) const ( PrefixLenSeparator = '/' PrefixLenSeparatorStr = "/" ) // IPVersion is the version type used by IP address types. type IPVersion string const ( // IndeterminateIPVersion represents an unspecified IP address version IndeterminateIPVersion IPVersion = "" // IPv4 represents Internet Protocol version 4 IPv4 IPVersion = "IPv4" // IPv6 represents Internet Protocol version 6 IPv6 IPVersion = "IPv6" ) // IsIPv6 returns true if this represents version 6 func (version IPVersion) IsIPv6() bool { return len(version) == 4 && strings.EqualFold(string(version), string(IPv6)) } // IsIPv4 returns true if this represents version 4 func (version IPVersion) IsIPv4() bool { return len(version) == 4 && strings.EqualFold(string(version), string(IPv4)) } // IsIndeterminate returns true if this represents an unspecified IP address version func (version IPVersion) IsIndeterminate() bool { if len(version) == 4 { // we allow mixed case in the event code is converted a string to IPVersion dig := version[3] if dig != '4' && dig != '6' { return true } dig = version[0] if dig != 'I' && dig != 'i' { return true } dig = version[1] if dig != 'P' && dig != 'p' { return true } dig = version[2] if dig != 'v' && dig != 'V' { return true } return false } return true } // index returns an index starting from 0 with IndeterminateIPVersion being the highest func (version IPVersion) index() int { if version.IsIPv4() { return 0 } else if version.IsIPv6() { return 1 } return 2 } // Equal returns whether the given version matches this version. Two indeterminate versions always match, even if their associated strings do not. func (version IPVersion) Equal(other IPVersion) bool { return strings.EqualFold(string(version), string(other)) || (version.IsIndeterminate() && other.IsIndeterminate()) } // String returns "IPv4", "IPv6", or the zero-value "" representing an indeterminate version func (version IPVersion) String() string { return string(version) } func (version IPVersion) GetNetwork() (network IPAddressNetwork) { if version.IsIPv6() { network = ipv6Network } else if version.IsIPv4() { network = ipv4Network } return } func (version IPVersion) toType() (t addrType) { if version.IsIPv6() { t = ipv6Type } else if version.IsIPv4() { t = ipv4Type } return } // GetMaxSegmentValue returns the maximum possible segment value for this IP version, determined by the number of bits per segment. func (version IPVersion) GetMaxSegmentValue() SegInt { if version.IsIPv4() { return IPv4MaxValuePerSegment } else if version.IsIPv6() { return IPv6MaxValuePerSegment } return 0 } // GetBytesPerSegment returns the number of bytes comprising each segment in this address or subnet. Segments in the same address are equal length. func (version IPVersion) GetBytesPerSegment() int { if version.IsIPv4() { return IPv4BytesPerSegment } else if version.IsIPv6() { return IPv6BytesPerSegment } return 0 } // GetBitsPerSegment returns the number of bits comprising each segment for this address version, either 8 or 16 for IPv4 and IPv6 respectively. Segments in the same address are equal length. func (version IPVersion) GetBitsPerSegment() BitCount { if version.IsIPv4() { return IPv4BitsPerSegment } else if version.IsIPv6() { return IPv6BitsPerSegment } return 0 } // GetByteCount returns the number of bytes comprising an address of this IP Version. func (version IPVersion) GetByteCount() int { if version.IsIPv4() { return IPv4ByteCount } else if version.IsIPv6() { return IPv6ByteCount } return 0 } // GetSegmentCount returns the number of segments comprising an address of this IP Version: 4 for IPv4 and 8 for IPv6. func (version IPVersion) GetSegmentCount() int { if version.IsIPv4() { return IPv4SegmentCount } else if version.IsIPv6() { return IPv6SegmentCount } return 0 } // GetBitCount returns the number of bits comprising an address of this IP Version. func (version IPVersion) GetBitCount() BitCount { if version.IsIPv4() { return IPv4BitCount } else if version.IsIPv6() { return IPv6BitCount } return 0 } func createIPAddress(section *AddressSection, zone Zone) *IPAddress { return &IPAddress{ ipAddressInternal{ addressInternal{ section: section, zone: zone, cache: &addressCache{}, }, }, } } func newIPAddressZoned(section *IPAddressSection, zone Zone) *IPAddress { result := createIPAddress(section.ToSectionBase(), zone) if zone != NoZone { // will need to cache its own strings result.cache.stringCache = &stringCache{} } return result } // necessary to avoid direct access to IPAddress type ipAddressInternal struct { addressInternal } func (addr *ipAddressInternal) toIPAddress() *IPAddress { return (*IPAddress)(unsafe.Pointer(addr)) } func (addr *ipAddressInternal) getIPVersion() IPVersion { if addr.isIPv4() { return IPv4 } else if addr.isIPv6() { return IPv6 } return IndeterminateIPVersion } func (addr *ipAddressInternal) getNetworkPrefixLen() PrefixLen { section := addr.section if section == nil { return nil } return section.ToIP().getNetworkPrefixLen() } // GetNetworkPrefixLen returns the prefix length, or nil if there is no prefix length. // GetNetworkPrefixLen is equivalent to the method GetPrefixLen. func (addr *ipAddressInternal) GetNetworkPrefixLen() PrefixLen { return addr.getNetworkPrefixLen().copy() } func (addr *ipAddressInternal) getNetNetIPAddr() netip.Addr { netAddr, _ := netip.AddrFromSlice(addr.getBytes()) return netAddr } func (addr *ipAddressInternal) getUpperNetNetIPAddr() netip.Addr { netAddr, _ := netip.AddrFromSlice(addr.getUpperBytes()) return netAddr } // IncludesZeroHost returns whether the subnet contains an individual address with a host of zero. If the subnet has no prefix length it returns false. // If the prefix length matches the bit count, then it returns true. // // Otherwise, it checks whether it contains an individual address for which all bits past the prefix are zero. func (addr *ipAddressInternal) IncludesZeroHost() bool { section := addr.section if section == nil { return false } return section.ToIP().IncludesZeroHost() } func (addr *ipAddressInternal) includesZeroHostLen(networkPrefixLength BitCount) bool { return addr.getSection().IncludesZeroHostLen(networkPrefixLength) } // IncludesMaxHost returns whether the subnet contains an individual address with a host of all one-bits. If the subnet has no prefix length it returns false. // If the prefix length matches the bit count, then it returns true. // // Otherwise, it checks whether it contains an individual address for which all bits past the prefix are one. func (addr *ipAddressInternal) IncludesMaxHost() bool { section := addr.section if section == nil { return false } return section.ToIP().IncludesMaxHost() } func (addr *ipAddressInternal) includesMaxHostLen(networkPrefixLength BitCount) bool { return addr.getSection().IncludesMaxHostLen(networkPrefixLength) } // IsSingleNetwork returns whether the network section of the address, the prefix, consists of a single value. // // If it has no prefix length, it returns true if not multiple, if it contains only a single individual address. func (addr *ipAddressInternal) IsSingleNetwork() bool { section := addr.section return section == nil || section.ToIP().IsSingleNetwork() } // IsMaxHost returns whether this section has a prefix length and if so, // whether the host section is always all one-bits, the max value, for all individual addresses in this subnet. // // If the host section is zero length (there are zero host bits), IsMaxHost returns true. func (addr *ipAddressInternal) IsMaxHost() bool { section := addr.section return section != nil && section.ToIP().IsMaxHost() } // IsMaxHostLen returns whether the host section is always one-bits, the max value, for all individual addresses in this subnet, // for the given prefix length. // // If the host section is zero length (there are zero host bits), IsMaxHostLen returns true. func (addr *ipAddressInternal) isMaxHostLen(prefLen BitCount) bool { return addr.getSection().IsMaxHostLen(prefLen) } // IsZeroHost returns whether this subnet has a prefix length and if so, // whether the host section is always zero for all individual addresses in this subnet. // // If the host section is zero length (there are zero host bits), IsZeroHost returns true. func (addr *ipAddressInternal) IsZeroHost() bool { section := addr.section return section != nil && section.ToIP().IsZeroHost() } // IsZeroHostLen returns whether the host section is always zero for all individual sections in this address section, // for the given prefix length. // // If the host section is zero length (there are zero host bits), IsZeroHostLen returns true. func (addr *ipAddressInternal) isZeroHostLen(prefLen BitCount) bool { return addr.getSection().IsZeroHostLen(prefLen) } // when boundariesOnly is true, there will be no error func (addr *ipAddressInternal) toZeroHost(boundariesOnly bool) (res *IPAddress, err addrerr.IncompatibleAddressError) { section, err := addr.section.toIPAddressSection().toZeroHost(boundariesOnly) if err == nil { res = addr.checkIdentity(section) } return } func (addr *ipAddressInternal) toZeroHostLen(prefixLength BitCount) (res *IPAddress, err addrerr.IncompatibleAddressError) { section, err := addr.getSection().toZeroHostLen(prefixLength) if err == nil { res = addr.checkIdentity(section) } return } func (addr *ipAddressInternal) toZeroNetwork() *IPAddress { return addr.checkIdentity(addr.getSection().toZeroNetwork()) } func (addr *ipAddressInternal) toMaxHost() (res *IPAddress, err addrerr.IncompatibleAddressError) { section, err := addr.section.toIPAddressSection().toMaxHost() if err == nil { res = addr.checkIdentity(section) } return } func (addr *ipAddressInternal) toMaxHostLen(prefixLength BitCount) (res *IPAddress, err addrerr.IncompatibleAddressError) { section, err := addr.getSection().toMaxHostLen(prefixLength) if err == nil { res = addr.checkIdentity(section) } return } func (addr *ipAddressInternal) checkIdentity(section *IPAddressSection) *IPAddress { if section == nil { return nil } sect := section.ToSectionBase() if sect == addr.section { return addr.toIPAddress() } return createIPAddress(sect, addr.zone) } func (addr *ipAddressInternal) getSection() *IPAddressSection { return addr.section.ToIP() } func (addr *ipAddressInternal) adjustPrefixLen(prefixLen BitCount) *IPAddress { return addr.checkIdentity(addr.getSection().adjustPrefixLen(prefixLen)) } func (addr *ipAddressInternal) adjustPrefixLenZeroed(prefixLen BitCount) (res *IPAddress, err addrerr.IncompatibleAddressError) { section, err := addr.getSection().adjustPrefixLenZeroed(prefixLen) if err == nil { res = addr.checkIdentity(section) } return } // GetBlockMaskPrefixLen returns the prefix length if this address is equivalent to the mask for a CIDR prefix block. // Otherwise, it returns nil. // A CIDR network mask is an address with all ones in the network section and then all zeros in the host section. // A CIDR host mask is an address with all zeros in the network section and then all ones in the host section. // The prefix length is the bit-length of the network section. // // Also, keep in mind that the prefix length returned by this method is not equivalent to the prefix length of this instance, // indicating the network and host section of this address. // The prefix length returned here indicates the whether the value of this address can be used as a mask for the network and host // section of any other address. Therefore, the two values can be different values, or one can be nil while the other is not. // // This method applies only to the lower value of the range if this address represents multiple values. func (addr *ipAddressInternal) GetBlockMaskPrefixLen(network bool) PrefixLen { section := addr.section if section == nil { return nil } return section.ToIP().GetBlockMaskPrefixLen(network) } func (addr *ipAddressInternal) spanWithPrefixBlocks() []ExtendedIPSegmentSeries { wrapped := addr.toIPAddress().Wrap() if addr.IsSequential() { if addr.IsSinglePrefixBlock() { return []ExtendedIPSegmentSeries{wrapped} } return getSpanningPrefixBlocks(wrapped, wrapped) } return spanWithPrefixBlocks(wrapped) } func (addr *ipAddressInternal) spanWithSequentialBlocks() []ExtendedIPSegmentSeries { wrapped := addr.toIPAddress().Wrap() if addr.IsSequential() { return []ExtendedIPSegmentSeries{wrapped} } return spanWithSequentialBlocks(wrapped) } func (addr *ipAddressInternal) coverSeriesWithPrefixBlock() ExtendedIPSegmentSeries { // call from wrapper if addr.IsSinglePrefixBlock() { return addr.toIPAddress().Wrap() } return coverWithPrefixBlock( addr.getLower().ToIP().Wrap(), addr.getUpper().ToIP().Wrap(), ) } func (addr *ipAddressInternal) coverWithPrefixBlock() *IPAddress { // call from ip ipv4 ipv6 if addr.IsSinglePrefixBlock() { return addr.toIPAddress() } res := coverWithPrefixBlock( addr.getLower().ToIP().Wrap(), addr.getUpper().ToIP().Wrap(), ) return res.(WrappedIPAddress).IPAddress } func (addr *ipAddressInternal) coverWithPrefixBlockTo(other *IPAddress) *IPAddress { res := getCoveringPrefixBlock( addr.toIPAddress().Wrap(), other.Wrap(), ) return res.(WrappedIPAddress).IPAddress } func (addr *ipAddressInternal) getNetworkMask(network IPAddressNetwork) *IPAddress { var prefLen BitCount if addr.isPrefixed() { prefLen = addr.getNetworkPrefixLen().bitCount() } else { prefLen = addr.GetBitCount() } return network.GetNetworkMask(prefLen) } func (addr *ipAddressInternal) getHostMask(network IPAddressNetwork) *IPAddress { var prefLen BitCount if addr.isPrefixed() { prefLen = addr.getNetworkPrefixLen().bitCount() } return network.GetHostMask(prefLen) } func (addr *ipAddressInternal) toCanonicalWildcardString() string { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.ToIPv6().toCanonicalWildcardStringZoned(addr.zone) } return cacheStr(&cache.canonicalWildcardString, func() string { return addr.section.ToIPv6().toCanonicalWildcardStringZoned(addr.zone) }) } return addr.getSection().ToCanonicalWildcardString() } func (addr *ipAddressInternal) toNormalizedWildcardString() string { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.ToIPv6().toNormalizedWildcardStringZoned(addr.zone) } return cacheStr(&cache.normalizedWildcardString, func() string { return addr.section.ToIPv6().toNormalizedWildcardStringZoned(addr.zone) }) } return addr.getSection().ToNormalizedWildcardString() } func (addr *ipAddressInternal) toSegmentedBinaryString() string { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.ToIPv6().toSegmentedBinaryStringZoned(addr.zone) } return cacheStr(&cache.segmentedBinaryString, func() string { return addr.section.ToIPv6().toSegmentedBinaryStringZoned(addr.zone) }) } return addr.getSection().ToSegmentedBinaryString() } func (addr *ipAddressInternal) toSQLWildcardString() string { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.ToIPv6().toSQLWildcardStringZoned(addr.zone) } return cacheStr(&cache.sqlWildcardString, func() string { return addr.section.ToIPv6().toSQLWildcardStringZoned(addr.zone) }) } return addr.getSection().ToSQLWildcardString() } func (addr *ipAddressInternal) toFullString() string { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.ToIPv6().toFullStringZoned(addr.zone) } return cacheStr(&cache.fullString, func() string { return addr.section.ToIPv6().toFullStringZoned(addr.zone) }) } return addr.getSection().ToFullString() } func (addr *ipAddressInternal) toReverseDNSString() (string, addrerr.IncompatibleAddressError) { return addr.getSection().ToReverseDNSString() } func (addr *ipAddressInternal) toPrefixLenString() string { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.ToIPv6().toPrefixLenStringZoned(addr.zone) } return cacheStr(&cache.networkPrefixLengthString, func() string { return addr.section.ToIPv6().toPrefixLenStringZoned(addr.zone) }) } return addr.getSection().ToPrefixLenString() } func (addr *ipAddressInternal) toSubnetString() string { if addr.hasZone() { return addr.toPrefixLenString() } return addr.getSection().ToSubnetString() } func (addr *ipAddressInternal) toCompressedWildcardString() string { if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.section.ToIPv6().toCompressedWildcardStringZoned(addr.zone) } return cacheStr(&cache.compressedWildcardString, func() string { return addr.section.ToIPv6().toCompressedWildcardStringZoned(addr.zone) }) } return addr.getSection().ToCompressedWildcardString() } func (addr *ipAddressInternal) getNetwork() IPAddressNetwork { return addr.getSection().getNetwork() } //// only needed for godoc / pkgsite // GetPrefixCount returns the count of prefixes in this address or subnet. // // The prefix length is given by GetPrefixLen. // // If this has a non-nil prefix length, returns the count of the range of values in the prefix. // // If this has a nil prefix length, returns the same value as GetCount. func (addr *ipAddressInternal) GetPrefixCount() *big.Int { return addr.addressInternal.GetPrefixCount() } // GetPrefixCountLen returns the count of prefixes in this address or subnet for the given prefix length. // // If not a subnet of multiple addresses, or a subnet with just single prefix of the given length, returns 1. func (addr *ipAddressInternal) GetPrefixCountLen(prefixLen BitCount) *big.Int { return addr.addressInternal.GetPrefixCountLen(prefixLen) } // GetBlockCount returns the count of distinct values in the given number of initial (more significant) segments. func (addr *ipAddressInternal) GetBlockCount(segments int) *big.Int { return addr.addressInternal.GetBlockCount(segments) } // GetPrefixLen returns the prefix length, or nil if there is no prefix length. // // A prefix length indicates the number of bits in the initial part of the address that comprise the prefix. // // A prefix is a part of the address that is not specific to that address but common amongst a group of addresses, such as a CIDR prefix block subnet. // // For IP addresses, the prefix is explicitly defined when the address is created. For example, "1.2.0.0/16" has a prefix length of 16, while "1.2.*.*" has no prefix length, // even though they both represent the same set of addresses and are considered equal. Prefixes can be considered variable for a given IP address and can depend on routing. // // The methods GetMinPrefixLenForBlock and GetPrefixLenForSingleBlock can help you to obtain or define a prefix length if one does not exist already. // The method ToPrefixBlockLen allows you to create the subnet consisting of the block of addresses for any given prefix length. func (addr *ipAddressInternal) GetPrefixLen() PrefixLen { return addr.addressInternal.GetPrefixLen() } // IsSinglePrefixBlock returns whether the address range matches the block of values for a single prefix identified by the prefix length of this address. // This is similar to IsPrefixBlock except that it returns false when the subnet has multiple prefixes. // // What distinguishes this method from ContainsSinglePrefixBlock is that this method returns // false if the series does not have a prefix length assigned to it, // or a prefix length that differs from the prefix length for which ContainsSinglePrefixBlock returns true. // // It is similar to IsPrefixBlock but returns false when there are multiple prefixes. // // For instance, "1.*.*.* /16" returns false from this method and returns true from IsPrefixBlock. func (addr *ipAddressInternal) IsSinglePrefixBlock() bool { return addr.addressInternal.IsSinglePrefixBlock() } // IsPrefixBlock returns whether the address has a prefix length and the address range includes the block of values for that prefix length. // If the prefix length matches the bit count, this returns true. // // To create a prefix block from any address, use ToPrefixBlock. // // This is different from ContainsPrefixBlock in that this method returns // false if the series has no prefix length, or a prefix length that differs from a prefix length for which ContainsPrefixBlock returns true. func (addr *ipAddressInternal) IsPrefixBlock() bool { return addr.addressInternal.IsPrefixBlock() } // ContainsPrefixBlock returns whether the range of this address or subnet contains the block of addresses for the given prefix length. // // Unlike ContainsSinglePrefixBlock, whether there are multiple prefix values in this item for the given prefix length makes no difference. // // Use GetMinPrefixLenForBlock to determine the smallest prefix length for which this method returns true. func (addr *ipAddressInternal) ContainsPrefixBlock(prefixLen BitCount) bool { return addr.addressInternal.ContainsPrefixBlock(prefixLen) } // ContainsSinglePrefixBlock returns whether this address contains a single prefix block for the given prefix length. // // This means there is only one prefix value for the given prefix length, and it also contains the full prefix block for that prefix, all addresses with that prefix. // // Use GetPrefixLenForSingleBlock to determine whether there is a prefix length for which this method returns true. func (addr *ipAddressInternal) ContainsSinglePrefixBlock(prefixLen BitCount) bool { return addr.addressInternal.ContainsSinglePrefixBlock(prefixLen) } // GetMinPrefixLenForBlock returns the smallest prefix length such that this includes the block of addresses for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this represents just a single address, returns the bit length of this address. // // See AssignMinPrefixForBlock for some examples. func (addr *ipAddressInternal) GetMinPrefixLenForBlock() BitCount { return addr.addressInternal.GetMinPrefixLenForBlock() } // GetPrefixLenForSingleBlock returns a prefix length for which the range of this address subnet matches exactly the block of addresses for that prefix. // // If the range can be described this way, then this method returns the same value as GetMinPrefixLenForBlock. // // If no such prefix exists, returns nil. // // If this segment grouping represents a single value, returns the bit length of this address division series. // // IP address examples: // - 1.2.3.4 returns 32 // - 1.2.3.4/16 returns 32 // - 1.2.*.* returns 16 // - 1.2.*.0/24 returns 16 // - 1.2.0.0/16 returns 16 // - 1.2.*.4 returns nil // - 1.2.252-255.* returns 22 func (addr *ipAddressInternal) GetPrefixLenForSingleBlock() PrefixLen { return addr.addressInternal.GetPrefixLenForSingleBlock() } func (addr *ipAddressInternal) rangeIterator( //creator parsedAddressCreator, /* nil for zero sections */ upper *IPAddress, valsAreMultiple bool, prefixLen PrefixLen, segProducer func(addr *IPAddress, index int) *IPAddressSegment, segmentIteratorProducer func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment], segValueComparator func(seg1, seg2 *IPAddress, index int) bool, networkSegmentIndex, hostSegmentIndex int, prefixedSegIteratorProducer func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment], ) Iterator[*Address] { //lower := rng.lower //upper := rng.upper lower := addr.toIPAddress() divCount := lower.GetSegmentCount() // at any given point in time, this list provides an iterator for the segment at each index segIteratorProducerList := make([]func() Iterator[*IPAddressSegment], divCount) // at any given point in time, finalValue[i] is true if and only if we have reached the very last value for segment i - 1 // when that happens, the next iterator for the segment at index i will be the last finalValue := make([]bool, divCount+1) // here is how the segment iterators will work: // the low and high values of the range at each segment are low, high // the maximum possible values for any segment are min, max // we first find the first k >= 0 such that low != high for the segment at index k // the initial set of iterators at each index are as follows: // for i < k finalValue[i] is set to true right away. // we create an iterator from seg = new Seg(low) // for i == k we create a wrapped iterator from Seg(low, high), wrapper will set finalValue[i] once we reach the final value of the iterator // for i > k we create an iterator from Seg(low, max) // // after the initial iterator has been supplied, any further iterator supplied for the same segment is as follows: // for i <= k, there was only one iterator, there will be no further iterator // for i > k, // if i == 0 or of if flagged[i - 1] is true, we create a wrapped iterator from Seg(low, high), wrapper will set finalValue[i] once we reach the final value of the iterator // otherwise we create an iterator from Seg(min, max) // // By following these rules, we iterate through all possible addresses notDiffering := true finalValue[0] = true var allSegShared *IPAddressSegment for i := 0; i < divCount; i++ { var segIteratorProducer func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment] if prefixedSegIteratorProducer != nil && i >= networkSegmentIndex { segIteratorProducer = prefixedSegIteratorProducer } else { segIteratorProducer = segmentIteratorProducer } lowerSeg := segProducer(lower, i) indexi := i if notDiffering { notDiffering = segValueComparator(lower, upper, i) if notDiffering { // there is only one iterator and it produces only one value finalValue[i+1] = true iterator := segIteratorProducer(lowerSeg, i) segIteratorProducerList[i] = func() Iterator[*IPAddressSegment] { return iterator } } else { // in the first differing segment the only iterator will go from segment value of lower address to segment value of upper address iterator := segIteratorProducer( createAddressDivision(lowerSeg.deriveNewMultiSeg(lowerSeg.getSegmentValue(), upper.GetGenericSegment(i).GetSegmentValue(), nil)).ToIP(), i) wrappedFinalIterator := &wrappedIterator{ iterator: iterator, finalValue: finalValue, indexi: indexi, } segIteratorProducerList[i] = func() Iterator[*IPAddressSegment] { return wrappedFinalIterator } } } else { // in the second and all following differing segments, rather than go from segment value of lower address to segment value of upper address // we go from segment value of lower address to the max seg value the first time through // then we go from the min value of the seg to the max seg value each time until the final time, // the final time we go from the min value to the segment value of upper address // we know it is the final time through when the previous iterator has reached its final value, which we track // the first iterator goes from the segment value of lower address to the max value of the segment firstIterator := segIteratorProducer( createAddressDivision(lowerSeg.deriveNewMultiSeg(lowerSeg.getSegmentValue(), lower.GetMaxSegmentValue(), nil)).ToIP(), i) // the final iterator goes from 0 to the segment value of our upper address finalIterator := segIteratorProducer( createAddressDivision(lowerSeg.deriveNewMultiSeg(0, upper.GetGenericSegment(i).GetSegmentValue(), nil)).ToIP(), i) // the wrapper iterator detects when the final iterator has reached its final value wrappedFinalIterator := &wrappedIterator{ iterator: finalIterator, finalValue: finalValue, indexi: indexi, } if allSegShared == nil { allSegShared = createAddressDivision(lowerSeg.deriveNewMultiSeg(0, lower.GetMaxSegmentValue(), nil)).ToIP() } // all iterators after the first iterator and before the final iterator go from 0 the max segment value, // and there will be many such iterators finalIteratorProducer := func() Iterator[*IPAddressSegment] { if finalValue[indexi] { return wrappedFinalIterator } return segIteratorProducer(allSegShared, indexi) } segIteratorProducerList[i] = func() Iterator[*IPAddressSegment] { //the first time through, we replace the iterator producer so the first iterator used only once (ie we remove this function from the list) segIteratorProducerList[indexi] = finalIteratorProducer return firstIterator } } } iteratorProducer := func(iteratorIndex int) Iterator[*AddressSegment] { iter := segIteratorProducerList[iteratorIndex]() return wrappedSegmentIterator[*IPAddressSegment]{iter} } return rangeAddrIterator( false, lower.ToAddressBase(), prefixLen, valsAreMultiple, rangeSegmentsIterator( divCount, iteratorProducer, networkSegmentIndex, hostSegmentIndex, iteratorProducer, ), ) } //// end needed for godoc / pkgsite var zeroIPAddr = createIPAddress(zeroSection, NoZone) // IPAddress represents an IP address or subnet, either IPv4 or IPv6 (except for the zero-valued IPAddress which is neither). // An IP address is composed of range-valued segments and can optionally have an associated prefix length. // The zero value IPAddress has no segments, neither IPv4 nor IPv6, which is not compatible with zero value for IPv4 or IPv6, those being 0.0.0.0 and :: respectively. // The zero value is also known as the adaptive zero. // // To construct one from a string, use NewIPAddressString, // then use the ToAddress or GetAddress method of [IPAddressString]. type IPAddress struct { ipAddressInternal } func (addr *IPAddress) init() *IPAddress { if addr.section == nil { return zeroIPAddr // this has a zero section } return addr } func (addr *IPAddress) getProvider() ipAddressProvider { if addr.IsPrefixed() { if !addr.IsPrefixBlock() { return getProviderFor(addr, addr.WithoutPrefixLen()) } zeroedAddr, _ := addr.toZeroHost(true) return getProviderFor(addr, zeroedAddr.WithoutPrefixLen()) } return getProviderFor(addr, addr) } // GetCount returns the count of addresses that this address or subnet represents. // // If just a single address, not a subnet of multiple addresses, returns 1. // // For instance, the IP address subnet "2001:db8::/64" has the count of 2 to the power of 64. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (addr *IPAddress) GetCount() *big.Int { if addr == nil { return bigZero() } return addr.getCount() } // IsMultiple returns true if this represents more than a single individual address, whether it is a subnet of multiple addresses. func (addr *IPAddress) IsMultiple() bool { return addr != nil && addr.isMultiple() } // Format implements [fmt.Formatter] interface. It accepts the formats // - 'v' for the default address and section format (either the normalized or canonical string), // - 's' (string) for the same, // - 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix), // - 'd' (decimal), 'x' (lowercase hexadecimal), and // - 'X' (uppercase hexadecimal). // Also supported are some of fmt's format flags for integral types. // Sign control is not supported since addresses and sections are never negative. // '#' for an alternate format is supported, which adds a leading zero for octal, and for hexadecimal it adds // a leading "0x" or "0X" for "%#x" and "%#X" respectively. // Also supported is specification of minimum digits precision, output field width, // space or zero padding, and '-' for left or right justification. func (addr IPAddress) Format(state fmt.State, verb rune) { addr.init().format(state, verb) } // String implements the [fmt.Stringer] interface, returning the canonical string provided by ToCanonicalString, or "" if the receiver is a nil pointer. func (addr *IPAddress) String() string { if addr == nil { return nilString() } return addr.init().ipAddressInternal.toString() } // GetSection returns the backing section for this address or subnet, comprising all segments. func (addr *IPAddress) GetSection() *IPAddressSection { return addr.init().section.ToIP() } // GetTrailingSection gets the subsection from the series starting from the given index. // The first segment is at index 0. func (addr *IPAddress) GetTrailingSection(index int) *IPAddressSection { return addr.GetSection().GetTrailingSection(index) } // GetSubSection gets the subsection from the series starting from the given index and ending just before the give endIndex. // The first segment is at index 0. func (addr *IPAddress) GetSubSection(index, endIndex int) *IPAddressSection { return addr.GetSection().GetSubSection(index, endIndex) } // GetNetworkSection returns an address section containing the segments with the network of the address or subnet, the prefix bits. // The returned section will have only as many segments as needed as determined by the existing CIDR network prefix length. // // If this series has no CIDR prefix length, the returned network section will // be the entire series as a prefixed section with prefix length matching the address bit length. func (addr *IPAddress) GetNetworkSection() *IPAddressSection { return addr.GetSection().GetNetworkSection() } // GetNetworkSectionLen returns a section containing the segments with the network of the address or subnet, the prefix bits according to the given prefix length. // The returned section will have only as many segments as needed to contain the network. // // The new section will be assigned the given prefix length, // unless the existing prefix length is smaller, in which case the existing prefix length will be retained. func (addr *IPAddress) GetNetworkSectionLen(prefLen BitCount) *IPAddressSection { return addr.GetSection().GetNetworkSectionLen(prefLen) } // GetHostSection returns a section containing the segments with the host of the address or subnet, the bits beyond the CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. // // If this series has no prefix length, the returned host section will be the full section. func (addr *IPAddress) GetHostSection() *IPAddressSection { return addr.GetSection().GetHostSection() } // GetHostSectionLen returns a section containing the segments with the host of the address or subnet, the bits beyond the given CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. func (addr *IPAddress) GetHostSectionLen(prefLen BitCount) *IPAddressSection { return addr.GetSection().GetHostSectionLen(prefLen) } // GetNetworkMask returns the network mask associated with the CIDR network prefix length of this address or subnet. // If this address or subnet has no prefix length, then the all-ones mask is returned. func (addr *IPAddress) GetNetworkMask() *IPAddress { return addr.getNetworkMask(addr.getNetwork()) } // GetHostMask returns the host mask associated with the CIDR network prefix length of this address or subnet. // If this address or subnet has no prefix length, then the all-ones mask is returned. func (addr *IPAddress) GetHostMask() *IPAddress { return addr.getHostMask(addr.getNetwork()) } // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (addr *IPAddress) CopySubSegments(start, end int, segs []*IPAddressSegment) (count int) { return addr.GetSection().CopySubSegments(start, end, segs) } // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. func (addr *IPAddress) CopySegments(segs []*IPAddressSegment) (count int) { return addr.GetSection().CopySegments(segs) } // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as this section. func (addr *IPAddress) GetSegments() []*IPAddressSegment { return addr.GetSection().GetSegments() } // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. func (addr *IPAddress) GetSegment(index int) *IPAddressSegment { return addr.getSegment(index).ToIP() } // GetSegmentCount returns the segment count, the number of segments in this address. func (addr *IPAddress) GetSegmentCount() int { return addr.getDivisionCount() } // ForEachSegment visits each segment in order from most-significant to least, the most significant with index 0, calling the given function for each, terminating early if the function returns true. // Returns the number of visited segments. func (addr *IPAddress) ForEachSegment(consumer func(segmentIndex int, segment *IPAddressSegment) (stop bool)) int { return addr.GetSection().ForEachSegment(consumer) } // GetGenericDivision returns the segment at the given index as a DivisionType. func (addr *IPAddress) GetGenericDivision(index int) DivisionType { return addr.getDivision(index) } // GetGenericSegment returns the segment at the given index as an AddressSegmentType. // The first segment is at index 0. // GetGenericSegment will panic given a negative index or an index matching or larger than the segment count. func (addr *IPAddress) GetGenericSegment(index int) AddressSegmentType { return addr.getSegment(index) } // GetDivisionCount returns the segment count. func (addr *IPAddress) GetDivisionCount() int { return addr.getDivisionCount() } // GetBitCount returns the number of bits comprising this address, // or each address in the range if a subnet, which is 32 for IPv4 and 128 for IPv6. func (addr *IPAddress) GetBitCount() BitCount { if address := addr.ToIPv4(); address != nil { return address.GetBitCount() } else if address := addr.ToIPv6(); address != nil { return address.GetBitCount() } return addr.addressInternal.GetBitCount() } // GetByteCount returns the number of bytes required for this address, // or each address in the range if a subnet, which is 4 for IPv4 and 16 for IPv6. func (addr *IPAddress) GetByteCount() int { if address := addr.ToIPv4(); address != nil { return address.GetByteCount() } else if address := addr.ToIPv6(); address != nil { return address.GetByteCount() } return addr.addressInternal.GetByteCount() } // GetLowerIPAddress returns the address in the subnet or address collection with the lowest numeric value, // which will be the receiver if it represents a single address. // For example, for "1.2-3.4.5-6", the series "1.2.4.5" is returned. // GetLowerIPAddress implements the IPAddressRange interface, and is equivalent to GetLower. func (addr *IPAddress) GetLowerIPAddress() *IPAddress { return addr.GetLower() } // GetUpperIPAddress returns the address in the subnet or address collection with the highest numeric value, // which will be the receiver if it represents a single address. // For example, for the subnet "1.2-3.4.5-6", the address "1.3.4.6" is returned. // GetUpperIPAddress implements the IPAddressRange interface, and is equivalent to GetUpper. func (addr *IPAddress) GetUpperIPAddress() *IPAddress { return addr.GetUpper() } // GetLower returns the lowest address in the subnet range, // which will be the receiver if it represents a single address. // For example, for the subnet "1.2-3.4.5-6", the address "1.2.4.5" is returned. func (addr *IPAddress) GetLower() *IPAddress { return addr.init().getLower().ToIP() } // GetUpper returns the highest address in the subnet range, // which will be the receiver if it represents a single address. // For example, for "1.2-3.4.5-6", the series "1.3.4.6" is returned. func (addr *IPAddress) GetUpper() *IPAddress { return addr.init().getUpper().ToIP() } // IsZeroHostLen returns whether the host section is always zero for all individual addresses in this subnet, // for the given prefix length. // // If the host section is zero length (there are zero host bits), IsZeroHostLen returns true. func (addr *IPAddress) IsZeroHostLen(prefLen BitCount) bool { return addr.init().isZeroHostLen(prefLen) } // ToZeroHost converts the address or subnet to one in which all individual addresses have a host of zero, // the host being the bits following the prefix length. // If the address or subnet has no prefix length, then it returns an all-zero address. // // The returned address or subnet will have the same prefix and prefix length. // // For instance, the zero host of "1.2.3.4/16" is the individual address "1.2.0.0/16". // // This returns an error if the subnet is a range of addresses which cannot be converted to a range in which all addresses have zero hosts, // because the conversion results in a subnet segment that is not a sequential range of values. func (addr *IPAddress) ToZeroHost() (*IPAddress, addrerr.IncompatibleAddressError) { return addr.init().toZeroHost(false) } // ToZeroHostLen converts the address or subnet to one in which all individual addresses have a host of zero, // the host being the bits following the given prefix length. // If this address or subnet has the same prefix length, then the returned one will too, otherwise the returned series will have no prefix length. // // For instance, the zero host of "1.2.3.4" for the prefix length of 16 is the address "1.2.0.0". // // This returns an error if the subnet is a range of addresses which cannot be converted to a range in which all addresses have zero hosts, // because the conversion results in a subnet segment that is not a sequential range of values. func (addr *IPAddress) ToZeroHostLen(prefixLength BitCount) (*IPAddress, addrerr.IncompatibleAddressError) { return addr.init().toZeroHostLen(prefixLength) } // ToZeroNetwork converts the address or subnet to one in which all individual addresses have a network of zero, // the network being the bits within the prefix length. // If the address or subnet has no prefix length, then it returns an all-zero address. // // The returned address or subnet will have the same prefix length. func (addr *IPAddress) ToZeroNetwork() *IPAddress { return addr.init().toZeroNetwork() } // IsMaxHostLen returns whether the host is all one-bits, the max value, for all individual addresses in this subnet, // for the given prefix length, the host being the bits following the prefix. // // If the host section is zero length (there are zero host bits), IsMaxHostLen returns true. func (addr *IPAddress) IsMaxHostLen(prefLen BitCount) bool { return addr.init().isMaxHostLen(prefLen) } // ToMaxHost converts the address or subnet to one in which all individual addresses have a host of all one-bits, the max value, // the host being the bits following the prefix length. // If the address or subnet has no prefix length, then it returns an all-ones address, the max address. // // The returned address or subnet will have the same prefix and prefix length. // // For instance, the max host of "1.2.3.4/16" gives the broadcast address "1.2.255.255/16". // // This returns an error if the subnet is a range of addresses which cannot be converted to a range in which all addresses have max hosts, // because the conversion results in a subnet segment that is not a sequential range of values. func (addr *IPAddress) ToMaxHost() (*IPAddress, addrerr.IncompatibleAddressError) { return addr.init().toMaxHost() } // ToMaxHostLen converts the address or subnet to one in which all individual addresses have a host of all one-bits, the max host, // the host being the bits following the given prefix length. // If this address or subnet has the same prefix length, then the resulting one will too, otherwise the resulting address or subnet will have no prefix length. // // For instance, the zero host of "1.2.3.4" for the prefix length of 16 is the address "1.2.255.255". // // This returns an error if the subnet is a range of addresses which cannot be converted to a range in which all addresses have max hosts, // because the conversion results in a subnet segment that is not a sequential range of values. func (addr *IPAddress) ToMaxHostLen(prefixLength BitCount) (*IPAddress, addrerr.IncompatibleAddressError) { return addr.init().toMaxHostLen(prefixLength) } // ToPrefixBlock returns the subnet associated with the prefix length of this address. // If this address has no prefix length, this address is returned. // // The subnet will include all addresses with the same prefix as this one, the prefix "block". // The network prefix will match the prefix of this address or subnet, and the host values will span all values. // // For example, if the address is "1.2.3.4/16" it returns the subnet "1.2.0.0/16", which can also be written as "1.2.*.*/16". func (addr *IPAddress) ToPrefixBlock() *IPAddress { return addr.init().toPrefixBlock().ToIP() } // ToPrefixBlockLen returns the subnet associated with the given prefix length. // // The subnet will include all addresses with the same prefix as this one, the prefix "block" for that prefix length. // The network prefix will match the prefix of this address or subnet, and the host values will span all values. // // For example, if the address is "1.2.3.4" and the prefix length provided is 16, it returns the subnet "1.2.0.0/16", which can also be written as "1.2.*.*/16". func (addr *IPAddress) ToPrefixBlockLen(prefLen BitCount) *IPAddress { return addr.init().toPrefixBlockLen(prefLen).ToIP() } // ToBlock creates a new block of addresses by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (addr *IPAddress) ToBlock(segmentIndex int, lower, upper SegInt) *IPAddress { return addr.init().toBlock(segmentIndex, lower, upper).ToIP() } // IsPrefixed returns whether this address has an associated prefix length. func (addr *IPAddress) IsPrefixed() bool { return addr != nil && addr.isPrefixed() } // WithoutPrefixLen provides the same address but with no prefix length. The values remain unchanged. func (addr *IPAddress) WithoutPrefixLen() *IPAddress { if !addr.IsPrefixed() { return addr } return addr.withoutPrefixLen().ToIP() } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address. // The provided prefix length will be adjusted to these boundaries if necessary. func (addr *IPAddress) SetPrefixLen(prefixLen BitCount) *IPAddress { return addr.init().setPrefixLen(prefixLen).ToIP() } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this address has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this address has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr *IPAddress) SetPrefixLenZeroed(prefixLen BitCount) (*IPAddress, addrerr.IncompatibleAddressError) { res, err := addr.init().setPrefixLenZeroed(prefixLen) return res.ToIP(), err } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address. // // If this address has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (addr *IPAddress) AdjustPrefixLen(prefixLen BitCount) *IPAddress { return addr.init().adjustPrefixLen(prefixLen).ToIP() } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address. // // If this address has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // For example, "1.2.0.0/16" adjusted by -8 becomes "1.0.0.0/8". // "1.2.0.0/16" adjusted by 8 becomes "1.2.0.0/24". // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr *IPAddress) AdjustPrefixLenZeroed(prefixLen BitCount) (*IPAddress, addrerr.IncompatibleAddressError) { res, err := addr.init().adjustPrefixLenZeroed(prefixLen) return res.ToIP(), err } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this address. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such address - it is required that the range of values match the range of a prefix block. // If there is no such address, then nil is returned. // // Examples: // - 1.2.3.4 returns 1.2.3.4/32 // - 1.2.*.* returns 1.2.0.0/16 // - 1.2.*.0/24 returns 1.2.0.0/16 // - 1.2.*.4 returns nil // - 1.2.0-1.* returns 1.2.0.0/23 // - 1.2.1-2.* returns nil // - 1.2.252-255.* returns 1.2.252.0/22 // - 1.2.3.4/16 returns 1.2.3.4/32 func (addr *IPAddress) AssignPrefixForSingleBlock() *IPAddress { return addr.init().assignPrefixForSingleBlock().ToIP() } // AssignMinPrefixForBlock returns an equivalent subnet, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this subnet. // // In other words, this method assigns a prefix length to this subnet matching the largest prefix block in this subnet. // // Examples: // - 1.2.3.4 returns 1.2.3.4/32 // - 1.2.*.* returns 1.2.0.0/16 // - 1.2.*.0/24 returns 1.2.0.0/16 // - 1.2.*.4 returns 1.2.*.4/32 // - 1.2.0-1.* returns 1.2.0.0/23 // - 1.2.1-2.* returns 1.2.1-2.0/24 // - 1.2.252-255.* returns 1.2.252.0/22 // - 1.2.3.4/16 returns 1.2.3.4/32 func (addr *IPAddress) AssignMinPrefixForBlock() *IPAddress { return addr.init().assignMinPrefixForBlock().ToIP() } // ToSinglePrefixBlockOrAddress converts to a single prefix block or address. // If the given address is a single prefix block, it is returned. // If it can be converted to a single prefix block by assigning a prefix length, the converted block is returned. // If it is a single address, any prefix length is removed and the address is returned. // Otherwise, nil is returned. // This method provides the address formats used by tries. // ToSinglePrefixBlockOrAddress is quite similar to AssignPrefixForSingleBlock, which always returns prefixed addresses, while this does not. func (addr *IPAddress) ToSinglePrefixBlockOrAddress() *IPAddress { return addr.init().toSinglePrefixBlockOrAddr().ToIP() } func (addr *IPAddress) toSinglePrefixBlockOrAddress() (*IPAddress, addrerr.IncompatibleAddressError) { if addr == nil { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.address.not.block"}} } res := addr.ToSinglePrefixBlockOrAddress() if res == nil { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.address.not.block"}} } return res, nil } // GetValue returns the lowest address in this subnet or address as an integer value. func (addr *IPAddress) GetValue() *big.Int { return addr.init().section.GetValue() } // GetUpperValue returns the highest address in this subnet or address as an integer value. func (addr *IPAddress) GetUpperValue() *big.Int { return addr.init().section.GetUpperValue() } // GetNetIPAddr returns the lowest address in this subnet or address as a net.IPAddr. func (addr *IPAddress) GetNetIPAddr() *net.IPAddr { return &net.IPAddr{ IP: addr.GetNetIP(), Zone: string(addr.zone), } } // GetUpperNetIPAddr returns the highest address in this subnet or address as a net.IPAddr. func (addr *IPAddress) GetUpperNetIPAddr() *net.IPAddr { return &net.IPAddr{ IP: addr.GetUpperNetIP(), Zone: string(addr.zone), } } // GetNetIP returns the lowest address in this subnet or address as a net.IP. func (addr *IPAddress) GetNetIP() net.IP { return addr.Bytes() } // GetUpperNetIP returns the highest address in this subnet or address as a net.IP. func (addr *IPAddress) GetUpperNetIP() net.IP { return addr.UpperBytes() } // GetNetNetIPAddr returns the lowest address in this subnet or address range as a netip.Addr. func (addr *IPAddress) GetNetNetIPAddr() netip.Addr { res := addr.init().getNetNetIPAddr() if addr.hasZone() { res = res.WithZone(string(addr.zone)) } return res } // GetUpperNetNetIPAddr returns the highest address in this subnet or address range as a netip.Addr. func (addr *IPAddress) GetUpperNetNetIPAddr() netip.Addr { return addr.init().getUpperNetNetIPAddr() } // CopyNetIP copies the value of the lowest individual address in the subnet into a net.IP. // // If the value can fit in the given net.IP slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *IPAddress) CopyNetIP(ip net.IP) net.IP { if ipv4Addr := addr.ToIPv4(); ipv4Addr != nil { return ipv4Addr.CopyNetIP(ip) // this shrinks the arg to 4 bytes if it was 16, we need only 4 } return addr.CopyBytes(ip) } // CopyUpperNetIP copies the value of the highest individual address in the subnet into a net.IP. // // If the value can fit in the given net.IP slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *IPAddress) CopyUpperNetIP(ip net.IP) net.IP { if ipv4Addr := addr.ToIPv4(); ipv4Addr != nil { return ipv4Addr.CopyUpperNetIP(ip) // this shrinks the arg to 4 bytes if it was 16, we need only 4 } return addr.CopyUpperBytes(ip) } // Bytes returns the lowest address in this subnet or address as a byte slice. func (addr *IPAddress) Bytes() []byte { return addr.init().section.Bytes() } // UpperBytes returns the highest address in this subnet or address as a byte slice. func (addr *IPAddress) UpperBytes() []byte { return addr.init().section.UpperBytes() } // CopyBytes copies the value of the lowest individual address in the subnet into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *IPAddress) CopyBytes(bytes []byte) []byte { return addr.init().section.CopyBytes(bytes) } // CopyUpperBytes copies the value of the highest individual address in the subnet into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *IPAddress) CopyUpperBytes(bytes []byte) []byte { return addr.init().section.CopyUpperBytes(bytes) } // IsMax returns whether this address matches exactly the maximum possible value, the address whose bits are all ones. func (addr *IPAddress) IsMax() bool { return addr.init().section.IsMax() } // IncludesMax returns whether this address includes the max address, the address whose bits are all ones, within its range. func (addr *IPAddress) IncludesMax() bool { return addr.init().section.IncludesMax() } // TestBit returns true if the bit in the lower value of this address at the given index is 1, where index 0 refers to the least significant bit. // In other words, it computes (bits & (1 << n)) != 0), using the lower value of this address. // TestBit will panic if n < 0, or if it matches or exceeds the bit count of this item. func (addr *IPAddress) TestBit(n BitCount) bool { return addr.init().testBit(n) } // IsOneBit returns true if the bit in the lower value of this address at the given index is 1, where index 0 refers to the most significant bit. // IsOneBit will panic if bitIndex is less than zero, or if it is larger than the bit count of this item. func (addr *IPAddress) IsOneBit(bitIndex BitCount) bool { return addr.init().isOneBit(bitIndex) } // PrefixEqual determines if the given address matches this address up to the prefix length of this address. // It returns whether the two addresses share the same range of prefix values. func (addr *IPAddress) PrefixEqual(other AddressType) bool { return addr.init().prefixEquals(other) } // PrefixContains returns whether the prefix values in the given address or subnet // are prefix values in this address or subnet, using the prefix length of this address or subnet. // If this address has no prefix length, the entire address is compared. // // It returns whether the prefix of this address contains all values of the same prefix length in the given address. func (addr *IPAddress) PrefixContains(other AddressType) bool { return addr.init().prefixContains(other) } // Contains returns whether this is the same type and version as the given address or subnet and whether it contains all addresses in the given address or subnet. func (addr *IPAddress) Contains(other AddressType) bool { if addr == nil { return other == nil || other.ToAddressBase() == nil } return addr.init().contains(other) } // Compare returns a negative integer, zero, or a positive integer if this address or subnet is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (addr *IPAddress) Compare(item AddressItem) int { return CountComparator.Compare(addr, item) } // Equal returns whether the given address or subnet is equal to this address or subnet. // Two address instances are equal if they represent the same set of addresses. func (addr *IPAddress) Equal(other AddressType) bool { if addr == nil { return other == nil || other.ToAddressBase() == nil } return addr.init().equals(other) } // CompareSize compares the counts of two subnets or addresses or other items, the number of individual items within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether one subnet represents more individual addresses than another. // // CompareSize returns a positive integer if this address or subnet has a larger count than the one given, zero if they are the same, or a negative integer if the other has a larger count. func (addr *IPAddress) CompareSize(other AddressItem) int { // this is here to take advantage of the CompareSize in IPAddressSection if addr == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return addr.init().compareSize(other) } // TrieCompare compares two addresses according to address trie ordering. // It returns a number less than zero, zero, or a number greater than zero if the first address argument is less than, equal to, or greater than the second. // // The comparison is intended for individual addresses and CIDR prefix blocks. // If an address is neither an individual address nor a prefix block, it is treated like one: // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *IPAddress) TrieCompare(other *IPAddress) (int, addrerr.IncompatibleAddressError) { if thisAddr := addr.ToIPv4(); thisAddr != nil { if oth := other.ToIPv4(); oth != nil { return thisAddr.TrieCompare(oth), nil } } else if thisAddr := addr.ToIPv6(); thisAddr != nil { if oth := other.ToIPv6(); oth != nil { return thisAddr.TrieCompare(oth), nil } } return 0, &incompatibleAddressError{addressError{key: "ipaddress.error.mismatched.bit.size"}} } // TrieIncrement returns the next address or block according to address trie ordering // // If an address is neither an individual address nor a prefix block, it is treated like one: // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *IPAddress) TrieIncrement() *IPAddress { if res, ok := trieIncrement(addr); ok { return res } return nil } // TrieDecrement returns the previous address or block according to address trie ordering // // If an address is neither an individual address nor a prefix block, it is treated like one: // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *IPAddress) TrieDecrement() *IPAddress { if res, ok := trieDecrement(addr); ok { return res } return nil } // MatchesWithMask applies the mask to this address and then compares the result with the given address, // returning true if they match, false otherwise. func (addr *IPAddress) MatchesWithMask(other *IPAddress, mask *IPAddress) bool { if thisAddr := addr.ToIPv4(); thisAddr != nil { if oth := other.ToIPv4(); oth != nil { if msk := mask.ToIPv4(); mask != nil { return thisAddr.MatchesWithMask(oth, msk) } } } else if thisAddr := addr.ToIPv6(); thisAddr != nil { if oth := other.ToIPv6(); oth != nil { if msk := mask.ToIPv6(); mask != nil { return thisAddr.MatchesWithMask(oth, msk) } } } return false } // IsIPv4 returns true if this address or subnet originated as an IPv4 address or subnet. If so, use ToIPv4 to convert back to the IPv4-specific type. func (addr *IPAddress) IsIPv4() bool { return addr != nil && addr.isIPv4() } // IsIPv6 returns true if this address or subnet originated as an IPv6 address or subnet. If so, use ToIPv6 to convert back to the IPv6-specific type. func (addr *IPAddress) IsIPv6() bool { return addr != nil && addr.isIPv6() } // GetIPVersion returns the IP version of this IP address. func (addr *IPAddress) GetIPVersion() IPVersion { if addr == nil { return IndeterminateIPVersion } return addr.getIPVersion() } // ToAddressBase converts to an Address, a polymorphic type usable with all addresses and subnets. // Afterwards, you can convert back with ToIP. // // ToAddressBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *IPAddress) ToAddressBase() *Address { if addr != nil { addr = addr.init() } return (*Address)(unsafe.Pointer(addr)) } // ToIP is an identity method. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *IPAddress) ToIP() *IPAddress { return addr } // ToIPv6 converts to an IPv6Address if this address or subnet originated as an IPv6 address or subnet. // If not, ToIPv6 returns nil. // // ToIPv6 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *IPAddress) ToIPv6() *IPv6Address { if addr.IsIPv6() { return (*IPv6Address)(addr) } return nil } // ToIPv4 converts to an IPv4Address if this address or subnet originated as an IPv4 address or subnet. // If not, ToIPv4 returns nil. // // ToIPv4 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *IPAddress) ToIPv4() *IPv4Address { if addr.IsIPv4() { return (*IPv4Address)(addr) } return nil } // Wrap wraps this IP address, returning a WrappedIPAddress, an implementation of ExtendedIPSegmentSeries, // which can be used to write code that works with both IP addresses and IP address sections. // Wrap can be called with a nil receiver, wrapping a nil address. func (addr *IPAddress) Wrap() WrappedIPAddress { return wrapIPAddress(addr) } // WrapAddress wraps this IP address, returning a WrappedAddress, an implementation of ExtendedSegmentSeries, // which can be used to write code that works with both addresses and address sections. // WrapAddress can be called with a nil receiver, wrapping a nil address. func (addr *IPAddress) WrapAddress() WrappedAddress { return wrapAddress(addr.ToAddressBase()) } // GetMaxSegmentValue returns the maximum possible segment value for this type of address. // // Note this is not the maximum of the range of segment values in this specific address, // this is the maximum value of any segment for this address type and version, determined by the number of bits per segment. func (addr *IPAddress) GetMaxSegmentValue() SegInt { return addr.init().getMaxSegmentValue() } // Iterator provides an iterator to iterate through the individual addresses of this address or subnet. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual addresses. // // Call IsMultiple to determine if this instance represents multiple addresses, or GetCount for the count. func (addr *IPAddress) Iterator() Iterator[*IPAddress] { if addr == nil { return ipAddrIterator{nilAddrIterator()} } return ipAddrIterator{addr.init().addrIterator(nil)} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this subnet, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this subnet. // // If the subnet has no prefix length, then this is equivalent to Iterator. func (addr *IPAddress) PrefixIterator() Iterator[*IPAddress] { return ipAddrIterator{addr.init().prefixIterator(false)} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address or subnet. // Each iterated address or subnet will be a prefix block with the same prefix length as this address or subnet. // // If this address has no prefix length, then this is equivalent to Iterator. func (addr *IPAddress) PrefixBlockIterator() Iterator[*IPAddress] { return ipAddrIterator{addr.init().prefixIterator(true)} } // BlockIterator iterates through the addresses that can be obtained by iterating through all the upper segments up to the given segment count. // The segments following remain the same in all iterated addresses. // // For instance, given the IPv4 subnet "1-2.3-4.5-6.7" and the count argument 2, // BlockIterator will iterate through "1.3.5-6.7", "1.4.5-6.7", "2.3.5-6.7" and "2.4.5-6.7". func (addr *IPAddress) BlockIterator(segmentCount int) Iterator[*IPAddress] { return ipAddrIterator{addr.init().blockIterator(segmentCount)} } // SequentialBlockIterator iterates through the sequential subnets or addresses that make up this address or subnet. // // Practically, this means finding the count of segments for which the segments that follow are not full range, and then using BlockIterator with that segment count. // // For instance, given the IPv4 subnet "1-2.3-4.5-6.7-8", it will iterate through "1.3.5.7-8", "1.3.6.7-8", "1.4.5.7-8", "1.4.6.7-8", "2.3.5.7-8", "2.3.6.7-8", "2.4.6.7-8" and "2.4.6.7-8". // // Use GetSequentialBlockCount to get the number of iterated elements. func (addr *IPAddress) SequentialBlockIterator() Iterator[*IPAddress] { return ipAddrIterator{addr.init().sequentialBlockIterator()} } // GetSequentialBlockIndex gets the minimal segment index for which all following segments are full-range blocks. // // The segment at this index is not a full-range block itself, unless all segments are full-range. // The segment at this index and all following segments form a sequential range. // For the full subnet to be sequential, the preceding segments must be single-valued. func (addr *IPAddress) GetSequentialBlockIndex() int { return addr.getSequentialBlockIndex() } // GetSequentialBlockCount provides the count of elements from the sequential block iterator, the minimal number of sequential subnets that comprise this subnet. func (addr *IPAddress) GetSequentialBlockCount() *big.Int { return addr.getSequentialBlockCount() } func (addr *IPAddress) rangeIterator( upper *IPAddress, valsAreMultiple bool, prefixLen PrefixLen, segProducer func(addr *IPAddress, index int) *IPAddressSegment, segmentIteratorProducer func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment], segValueComparator func(seg1, seg2 *IPAddress, index int) bool, networkSegmentIndex, hostSegmentIndex int, prefixedSegIteratorProducer func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment], ) Iterator[*IPAddress] { return ipAddrIterator{addr.ipAddressInternal.rangeIterator(upper.ToIP(), valsAreMultiple, prefixLen, segProducer, segmentIteratorProducer, segValueComparator, networkSegmentIndex, hostSegmentIndex, prefixedSegIteratorProducer)} } // ToSequentialRange creates a sequential range instance from the lowest and highest addresses in this subnet. // // The two will represent the same set of individual addresses if and only if IsSequential is true. // To get a series of ranges that represent the same set of individual addresses use the SequentialBlockIterator (or PrefixIterator), // and apply this method to each iterated subnet. // // If this represents just a single address then the returned instance covers just that single address as well. func (addr *IPAddress) ToSequentialRange() *SequentialRange[*IPAddress] { if addr != nil { addr = addr.init().WithoutPrefixLen() return newSequRangeUnchecked( addr.GetLower(), addr.GetUpper(), addr.isMultiple()) } return nil } func (addr *IPAddress) getLowestHighestAddrs() (lower, upper *IPAddress) { l, u := addr.ipAddressInternal.getLowestHighestAddrs() return l.ToIP(), u.ToIP() } // IncrementBoundary returns the address that is the given increment from the range boundaries of this subnet. // // If the given increment is positive, adds the value to the upper address (GetUpper) in the subnet range to produce a new address. // If the given increment is negative, adds the value to the lower address (GetLower) in the subnet range to produce a new address. // If the increment is zero, returns this address. // // If this is a single address value, that address is simply incremented by the given increment value, positive or negative. // // On address overflow or underflow, IncrementBoundary returns nil. func (addr *IPAddress) IncrementBoundary(increment int64) *IPAddress { return addr.init().incrementBoundary(increment).ToIP() } // Increment returns the address from the subnet that is the given increment upwards into the subnet range, // with the increment of 0 returning the first address in the range. // // If the increment i matches or exceeds the subnet size count c, then i - c + 1 // is added to the upper address of the range. // An increment matching the subnet count gives you the address just above the highest address in the subnet. // // If the increment is negative, it is added to the lower address of the range. // To get the address just below the lowest address of the subnet, use the increment -1. // // If this is just a single address value, the address is simply incremented by the given increment, positive or negative. // // If this is a subnet with multiple values, a positive increment i is equivalent i + 1 values from the subnet iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the subnet count is equivalent to the same number of iterator values preceding the upper bound of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On address overflow or underflow, Increment returns nil. func (addr *IPAddress) Increment(increment int64) *IPAddress { return addr.init().increment(increment).ToIP() } // SpanWithRange returns an IPAddressSeqRange instance that spans this subnet to the given subnet. // If the other address is a different version than this, then the other is ignored, and the result is equivalent to calling ToSequentialRange. func (addr *IPAddress) SpanWithRange(other *IPAddress) *SequentialRange[*IPAddress] { return NewSequentialRange(addr.init(), other) } // Mask applies the given mask to all addresses represented by this IPAddress. // The mask is applied to all individual addresses. // // If the mask is a different version than this, then an error is returned. // // If this represents multiple addresses, and applying the mask to all addresses creates a set of addresses // that cannot be represented as a sequential range within each segment, then an error is returned. func (addr *IPAddress) Mask(other *IPAddress) (masked *IPAddress, err addrerr.IncompatibleAddressError) { return addr.maskPrefixed(other, true) } func (addr *IPAddress) maskPrefixed(other *IPAddress, retainPrefix bool) (*IPAddress, addrerr.IncompatibleAddressError) { if thisAddr := addr.ToIPv4(); thisAddr != nil { if oth := other.ToIPv4(); oth != nil { result, err := thisAddr.maskPrefixed(oth, retainPrefix) return result.ToIP(), err } } else if thisAddr := addr.ToIPv6(); thisAddr != nil { if oth := other.ToIPv6(); oth != nil { result, err := thisAddr.maskPrefixed(oth, retainPrefix) return result.ToIP(), err } } return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.ipMismatch"}} } // BitwiseOr does the bitwise disjunction with this address or subnet, useful when subnetting. // It is similar to Mask which does the bitwise conjunction. // // The operation is applied to all individual addresses and the result is returned. // // If the given address is a different version than this, then an error is returned. // // If this is a subnet representing multiple addresses, and applying the operations to all addresses creates a set of addresses // that cannot be represented as a sequential range within each segment, then an error is returned. func (addr *IPAddress) BitwiseOr(other *IPAddress) (masked *IPAddress, err addrerr.IncompatibleAddressError) { return addr.bitwiseOrPrefixed(other, true) } func (addr *IPAddress) bitwiseOrPrefixed(other *IPAddress, retainPrefix bool) (*IPAddress, addrerr.IncompatibleAddressError) { if thisAddr := addr.ToIPv4(); thisAddr != nil { if oth := other.ToIPv4(); oth != nil { result, err := thisAddr.bitwiseOrPrefixed(oth, retainPrefix) return result.ToIP(), err } } else if thisAddr := addr.ToIPv6(); thisAddr != nil { if oth := other.ToIPv6(); oth != nil { result, err := thisAddr.bitwiseOrPrefixed(oth, retainPrefix) return result.ToIP(), err } } return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.ipMismatch"}} } // Intersect returns the subnet whose addresses are found in both this and the given subnet argument, or nil if no such addresses exist. // // This is also known as the conjunction of the two sets of addresses. func (addr *IPAddress) Intersect(other *IPAddress) *IPAddress { if thisAddr := addr.ToIPv4(); thisAddr != nil { if oth := other.ToIPv4(); oth != nil { return thisAddr.Intersect(oth).ToIP() } } else if thisAddr := addr.ToIPv6(); thisAddr != nil { if oth := other.ToIPv6(); oth != nil { return thisAddr.Intersect(oth).ToIP() } } return nil } // Subtract subtracts the given subnet from this subnet, returning an array of subnets for the result (the subnets will not be contiguous so an array is required). // Subtract computes the subnet difference, the set of addresses in this address subnet but not in the provided subnet. // This is also known as the relative complement of the given argument in this subnet. // This is set subtraction, not subtraction of address values (use Increment for the latter). We have a subnet of addresses and we are removing those addresses found in the argument subnet. // If there are no remaining addresses, nil is returned. func (addr *IPAddress) Subtract(other *IPAddress) []*IPAddress { if !versionsMatch(addr, other) { return []*IPAddress{addr} } addr = addr.init() sects, _ := addr.GetSection().subtract(other.GetSection()) sectLen := len(sects) if sectLen == 0 { return nil } else if sectLen == 1 { sec := sects[0] if sec.ToSectionBase() == addr.section { return []*IPAddress{addr} } } res := make([]*IPAddress, sectLen) for i, sect := range sects { res[i] = newIPAddressZoned(sect, addr.zone) } return res } // IsLinkLocal returns whether the address or subnet is entirely link local, whether unicast or multicast. func (addr *IPAddress) IsLinkLocal() bool { if thisAddr := addr.ToIPv4(); thisAddr != nil { return thisAddr.IsLinkLocal() } else if thisAddr := addr.ToIPv6(); thisAddr != nil { return thisAddr.IsLinkLocal() } return false } // IsLocal returns true if the address is link local, site local, organization local, administered locally, or unspecified. // This includes both unicast and multicast. func (addr *IPAddress) IsLocal() bool { if thisAddr := addr.ToIPv4(); thisAddr != nil { return thisAddr.IsLocal() } else if thisAddr := addr.ToIPv6(); thisAddr != nil { return thisAddr.IsLocal() } return false } // IsUnspecified returns true if exactly zero. The unspecified address is the address that is all zeros. func (addr *IPAddress) IsUnspecified() bool { return addr.section != nil && addr.IsZero() } // IsAnyLocal returns whether this address is the address which binds to any address on the local host. // This is the address that has the value of 0, aka the unspecified address. func (addr *IPAddress) IsAnyLocal() bool { return addr.section != nil && addr.IsZero() } // IsLoopback returns whether this address is a loopback address, such as "::1" or "127.0.0.1". func (addr *IPAddress) IsLoopback() bool { if thisAddr := addr.ToIPv4(); thisAddr != nil { return thisAddr.IsLoopback() } else if thisAddr := addr.ToIPv6(); thisAddr != nil { return thisAddr.IsLoopback() } return false } // IsMulticast returns whether this address or subnet is entirely multicast. func (addr *IPAddress) IsMulticast() bool { if thisAddr := addr.ToIPv4(); thisAddr != nil { return thisAddr.IsMulticast() } else if thisAddr := addr.ToIPv6(); thisAddr != nil { return thisAddr.IsMulticast() } return false } func versionsMatch(one, two *IPAddress) bool { return one.getAddrType() == two.getAddrType() } //func allVersionsMatch(one *IPAddress, two []*IPAddress) bool { // addrType := one.getAddrType() // for _, addr := range two { // if addr.getAddrType() != addrType { // return false // } // } // return true //} // MergeToSequentialBlocks merges this with the list of addresses to produce the smallest array of sequential blocks. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. // Arguments that are not the same IP version are ignored. func (addr *IPAddress) MergeToSequentialBlocks(addrs ...*IPAddress) []*IPAddress { series := filterCloneIPAddrs(addr, addrs) blocks := getMergedSequentialBlocks(series) return cloneToIPAddrs(blocks) } // MergeToPrefixBlocks merges this subnet with the list of subnets to produce the smallest array of prefix blocks. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. // Arguments that are not the same IP version are ignored. func (addr *IPAddress) MergeToPrefixBlocks(addrs ...*IPAddress) []*IPAddress { series := filterCloneIPAddrs(addr, addrs) blocks := getMergedPrefixBlocks(series) return cloneToIPAddrs(blocks) } // SpanWithPrefixBlocks returns an array of prefix blocks that cover the same set of addresses as this subnet. // // Unlike SpanWithPrefixBlocksTo, the result only includes addresses that are a part of this subnet. func (addr *IPAddress) SpanWithPrefixBlocks() []*IPAddress { addr = addr.init() if addr.IsSequential() { if addr.IsSinglePrefixBlock() { return []*IPAddress{addr} } wrapped := addr.Wrap() spanning := getSpanningPrefixBlocks(wrapped, wrapped) return cloneToIPAddrs(spanning) } wrapped := addr.Wrap() return cloneToIPAddrs(spanWithPrefixBlocks(wrapped)) } // SpanWithPrefixBlocksTo returns the smallest slice of prefix block subnets that span from this subnet to the given subnet. // // If the given address is a different version than this, then the given address is ignored, and the result is equivalent to calling SpanWithPrefixBlocks. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. // // From the list of returned subnets you can recover the original range (this to other) by converting each to IPAddressRange with ToSequentialRange // and them joining them into a single range with the Join method of IPAddressSeqRange. func (addr *IPAddress) SpanWithPrefixBlocksTo(other *IPAddress) []*IPAddress { if !versionsMatch(addr, other) { return addr.SpanWithPrefixBlocks() } return cloneToIPAddrs( getSpanningPrefixBlocks( addr.init().Wrap(), other.init().Wrap(), ), ) } // CoverWithPrefixBlockTo returns the minimal-size prefix block that covers all the addresses spanning from this subnet to the given subnet. // // If the argument is not the same IP version as the receiver, the argument is ignored, and the result is the same as CoverWithPrefixBlock. func (addr *IPAddress) CoverWithPrefixBlockTo(other *IPAddress) *IPAddress { if !versionsMatch(addr, other) { return addr.CoverWithPrefixBlock() } return addr.init().coverWithPrefixBlockTo(other) } // CoverWithPrefixBlock returns the minimal-size prefix block that covers all the addresses in this subnet. // The resulting block will have a larger subnet size than this, unless this subnet is already a prefix block. func (addr *IPAddress) CoverWithPrefixBlock() *IPAddress { return addr.init().coverWithPrefixBlock() } // SpanWithSequentialBlocks produces the smallest slice of sequential blocks that cover the same set of addresses as this subnet. // // This slice can be shorter than that produced by SpanWithPrefixBlocks and is never longer. // // Unlike SpanWithSequentialBlocksTo, this method only includes addresses that are a part of this subnet. func (addr *IPAddress) SpanWithSequentialBlocks() []*IPAddress { addr = addr.init() if addr.IsSequential() { return []*IPAddress{addr} } return cloneToIPAddrs(spanWithSequentialBlocks(addr.Wrap())) } // SpanWithSequentialBlocksTo produces the smallest slice of sequential block subnets that span all values from this subnet to the given subnet. // The span will cover all addresses in both subnets and everything in between. // // Individual block subnets come in the form "1-3.1-4.5.6-8", however that particular subnet is not sequential since address "1.1.5.8" is in the subnet, // the next sequential address "1.1.5.9" is not in the subnet, and a higher address "1.2.5.6" is in the subnet. // Blocks are sequential when the first segment with a range of values is followed by segments that span all values. // // If the other address is a different version than this, then this is equivalent to calling SpanWithSequentialBlocks on this subnet. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. func (addr *IPAddress) SpanWithSequentialBlocksTo(other *IPAddress) []*IPAddress { if !versionsMatch(addr, other) { return addr.SpanWithSequentialBlocks() } return cloneToIPAddrs( getSpanningSequentialBlocks( addr.init().Wrap(), other.init().Wrap(), ), ) } // ReverseBytes returns a new address with the bytes reversed. Any prefix length is dropped. // // If each segment is more than 1 byte long, and the bytes within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, then this returns an error. // // In practice this means that to be reversible, a segment range must include all values except possibly the largest and/or smallest, which reverse to themselves. func (addr *IPAddress) ReverseBytes() (*IPAddress, addrerr.IncompatibleAddressError) { res, err := addr.init().reverseBytes() return res.ToIP(), err } // ReverseBits returns a new address with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a segment range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (addr *IPAddress) ReverseBits(perByte bool) (*IPAddress, addrerr.IncompatibleAddressError) { res, err := addr.init().reverseBits(perByte) return res.ToIP(), err } // ReverseSegments returns a new address with the segments reversed. func (addr *IPAddress) ReverseSegments() *IPAddress { return addr.init().reverseSegments().ToIP() } // GetSegmentStrings returns a slice with the string for each segment being the string that is normalized with wildcards. func (addr *IPAddress) GetSegmentStrings() []string { if addr == nil { return nil } return addr.init().getSegmentStrings() } //I considered changing to uppercase, see https://www.ieee802.org/1/files/public/docs2020/yangsters-smansfield-mac-address-format-0420-v01.pdf //and https://standards.ieee.org/wp-content/uploads/import/documents/tutorials/macgrp.pdf and https://en.wikipedia.org/wiki/MAC_address //canonicalParams = new MACStringOptions.Builder().setSeparator(MACAddress.DASH_SEGMENT_SEPARATOR).setUppercase(true).setExpandedSegments(true).setWildcards(new Wildcards(MACAddress.DASHED_SEGMENT_RANGE_SEPARATOR_STR, Address.SEGMENT_WILDCARD_STR, null)).toOptions(); // Search docs for: An example is "01-23-45-67-89-ab" // But ACTUALLY, in the ends I decided not to: https://www.mef.net/wp-content/uploads/MEF-89.pdf // ToCanonicalString produces a canonical string for the address. // // For IPv4, dotted octet format, also known as dotted decimal format, is used. // https://datatracker.ietf.org/doc/html/draft-main-ipaddr-text-rep-00#section-2.1 // // For IPv6, RFC 5952 describes canonical string representation. // https://en.wikipedia.org/wiki/IPv6_address#Representation // http://tools.ietf.org/html/rfc5952 // // For MAC, it uses the canonical standardized IEEE 802 MAC address representation of xx-xx-xx-xx-xx-xx. An example is "01-23-45-67-89-ab". // For range segments, '|' is used: "11-22-33|44-55-66". // // Each address has a unique canonical string, not counting the prefix length. // With IP addresses, the prefix length is included in the string, and the prefix length can cause two equal addresses to have different strings, for example "1.2.3.4/16" and "1.2.3.4". // It can also cause two different addresses to have the same string, such as "1.2.0.0/16" for the individual address "1.2.0.0" and also the prefix block "1.2.*.*". // Use ToCanonicalWildcardString for a unique string for each IP address and subnet. func (addr *IPAddress) ToCanonicalString() string { if addr == nil { return nilString() } return addr.init().toCanonicalString() } // ToCanonicalWildcardString produces a string similar to the canonical string and avoids the CIDR prefix length. // Addresses and subnets with a network prefix length will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix length notation. // IPv6 addresses will be compressed according to the canonical representation. // For IPv4 it is the same as ToNormalizedWildcardString. func (addr *IPAddress) ToCanonicalWildcardString() string { if addr == nil { return nilString() } return addr.init().toCanonicalWildcardString() } // ToNormalizedString produces a normalized string for the address. // // For IPv4, it is the same as the canonical string. // // For IPv6, it differs from the canonical string. Zero-segments are not compressed. // // Each address has a unique normalized string, not counting the prefix length. // With IP addresses, the prefix length can cause two equal addresses to have different strings, for example "1.2.3.4/16" and "1.2.3.4". // It can also cause two different addresses to have the same string, such as "1.2.0.0/16" for the individual address "1.2.0.0" and also the prefix block "1.2.*.*". // Use the method ToNormalizedWildcardString for a unique string for each IP address and subnet. func (addr *IPAddress) ToNormalizedString() string { if addr == nil { return nilString() } return addr.init().toNormalizedString() } // ToCompressedString produces a short representation of this address while remaining within the confines of standard representation(s) of the address. // // For IPv4, it is the same as the canonical string. // // For IPv6, it differs from the canonical string. It compresses the maximum number of zeros and/or host segments with the IPv6 compression notation '::'. func (addr *IPAddress) ToCompressedString() string { if addr == nil { return nilString() } return addr.init().toCompressedString() } // ToNormalizedWildcardString produces a string similar to the normalized string but avoids the CIDR prefix length. // CIDR addresses will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix notation. func (addr *IPAddress) ToNormalizedWildcardString() string { if addr == nil { return nilString() } return addr.init().toNormalizedWildcardString() } // ToSegmentedBinaryString writes this IP address segment series as segments of binary values preceded by the "0b" prefix. func (addr *IPAddress) ToSegmentedBinaryString() string { if addr == nil { return nilString() } return addr.init().toSegmentedBinaryString() } // ToSQLWildcardString create a string similar to that from toNormalizedWildcardString except that // it uses SQL wildcards. It uses '%' instead of '*' and also uses the wildcard '_'. func (addr *IPAddress) ToSQLWildcardString() string { if addr == nil { return nilString() } return addr.init().toSQLWildcardString() } // ToFullString produces a string with no compressed segments and all segments of full length with leading zeros, // which is 4 characters for IPv6 segments and 3 characters for IPv4 segments. func (addr *IPAddress) ToFullString() string { if addr == nil { return nilString() } return addr.init().toFullString() } // ToReverseDNSString generates the reverse-DNS lookup string, // returning an error if this address is an IPv6 multiple-valued subnet for which the range cannot be represented. // For "8.255.4.4" it is "4.4.255.8.in-addr.arpa". // For "2001:db8::567:89ab" it is "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa". func (addr *IPAddress) ToReverseDNSString() (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toReverseDNSString() } // ToPrefixLenString returns a string with a CIDR network prefix length if this address has a network prefix length. // For IPv6, a zero host section will be compressed with "::". For IPv4 the string is equivalent to the canonical string. func (addr *IPAddress) ToPrefixLenString() string { if addr == nil { return nilString() } return addr.init().toPrefixLenString() } // ToSubnetString produces a string with specific formats for subnets. // The subnet string looks like "1.2.*.*" or "1:2::/16". // // In the case of IPv4, this means that wildcards are used instead of a network prefix when a network prefix has been supplied. // In the case of IPv6, when a network prefix has been supplied, the prefix will be shown and the host section will be compressed with "::". func (addr *IPAddress) ToSubnetString() string { if addr == nil { return nilString() } return addr.init().toSubnetString() } // ToCompressedWildcardString produces a string similar to ToNormalizedWildcardString, avoiding the CIDR prefix, but with full IPv6 segment compression as well, including single zero-segments. // For IPv4 it is the same as ToNormalizedWildcardString. func (addr *IPAddress) ToCompressedWildcardString() string { if addr == nil { return nilString() } return addr.init().toCompressedWildcardString() } // ToHexString writes this address as a single hexadecimal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // If a subnet cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *IPAddress) ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toHexString(with0xPrefix) } // ToOctalString writes this address as a single octal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0" prefix. // // If a subnet cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *IPAddress) ToOctalString(with0Prefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toOctalString(with0Prefix) } // ToBinaryString writes this address as a single binary value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0b" prefix. // // If a subnet cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *IPAddress) ToBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toBinaryString(with0bPrefix) } // ToUNCHostName Generates the Microsoft UNC path component for this address. See https://ipv6-literal.com/ // // For IPv4 it is the canonical string. // For IPv6, it is the canonical string but with colons replaced by dashes, percent signs with the letter “s”, and then appended with the root domain ".ipv6-literal.net". func (addr *IPAddress) ToUNCHostName() string { if addr == nil { return nilString() } else if thisAddr := addr.ToIPv4(); thisAddr != nil { return thisAddr.ToUNCHostName() } else if thisAddr := addr.ToIPv6(); thisAddr != nil { return thisAddr.ToUNCHostName() } return addr.ToCanonicalString() } // ToCustomString creates a customized string from this address or subnet according to the given string option parameters. func (addr *IPAddress) ToCustomString(stringOptions addrstr.IPStringOptions) string { if addr == nil { return nilString() } return addr.GetSection().toCustomZonedString(stringOptions, addr.zone) } // ToAddressString retrieves or generates an IPAddressString instance for this IPAddress instance. // This may be the IPAddressString this instance was generated from, if it was generated from an IPAddressString. // // In general, users are intended to create IPAddress instances from IPAddressString instances, // while the reverse direction is generally not common and not useful, except under specific circumstances. // // However, the reverse direction can be useful under certain circumstances, // such as when maintaining a collection of HostIdentifierString or IPAddressString instances. func (addr *IPAddress) ToAddressString() *IPAddressString { addr = addr.init() cache := addr.cache if cache != nil { res := cache.identifierStr if res != nil { hostIdStr := res.idStr if str, ok := hostIdStr.(*IPAddressString); ok { return str } } } return newIPAddressStringFromAddr(addr.toCanonicalString(), addr) } // ToHostName returns the HostName used to resolve, if this address was resolved from a host. // Otherwise, if this address represents a subnet of multiple addresses, returns a HostName for that subnet. // Otherwise, it does a reverse name lookup to obtain the proper HostName. func (addr *IPAddress) ToHostName() *HostName { addr = addr.init() cache := addr.cache if cache != nil { res := cache.identifierStr if res != nil { hostIdStr := res.idStr if h, ok := hostIdStr.(*HostName); ok { return h } } } var h *HostName if !addr.isMultiple() { h, _ = addr.ToCanonicalHostName() } if h == nil { h = NewHostNameFromAddr(addr) } return h } // ToCanonicalHostName does a reverse name lookup to get the canonical host name. // Note that the canonical host name may differ on different systems. // // This returns an error if this address is a subnet multiple values. func (addr *IPAddress) ToCanonicalHostName() (*HostName, error) { if addr.isMultiple() { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.unavailable.numeric"}} } return addr.init().lookupAddr() } func (addr *IPAddress) lookupAddr() (*HostName, error) { names, err := net.LookupAddr(addr.ToNormalizedWildcardString()) if err != nil { return nil, err } else if len(names) == 0 { return nil, nil } else if names[0] == "" { return nil, nil } return NewHostName(names[0]), nil } // IncludesZeroHostLen returns whether the subnet contains an individual address with a host of zero, an individual address for which all bits past the given prefix length are zero. func (addr *IPAddress) IncludesZeroHostLen(networkPrefixLength BitCount) bool { return addr.init().includesZeroHostLen(networkPrefixLength) } // IncludesMaxHostLen returns whether the subnet contains an individual address with a host of all one-bits, an individual address for which all bits past the given prefix length are all ones. func (addr *IPAddress) IncludesMaxHostLen(networkPrefixLength BitCount) bool { return addr.init().includesMaxHostLen(networkPrefixLength) } // GetLeadingBitCount returns the number of consecutive leading one or zero bits. // If ones is true, returns the number of consecutive leading one bits. // Otherwise, returns the number of consecutive leading zero bits. // // This method applies to the lower value of the range if this is a subnet representing multiple values. func (addr *IPAddress) GetLeadingBitCount(ones bool) BitCount { return addr.init().getLeadingBitCount(ones) } // GetTrailingBitCount returns the number of consecutive trailing one or zero bits. // If ones is true, returns the number of consecutive trailing zero bits. // Otherwise, returns the number of consecutive trailing one bits. // // This method applies to the lower value of the range if this is a subnet representing multiple values. func (addr *IPAddress) GetTrailingBitCount(ones bool) BitCount { return addr.init().getTrailingBitCount(ones) } // GetNetwork returns the singleton network instance for the IP version of this address or subnet. func (addr *IPAddress) GetNetwork() IPAddressNetwork { return addr.getNetwork() } func (addr *IPAddress) toMaxLower() *IPAddress { return addr.init().addressInternal.toMaxLower().ToIP() } func (addr *IPAddress) toMinUpper() *IPAddress { return addr.init().addressInternal.toMinUpper().ToIP() } // ToKey creates the associated address key. // While addresses can be compared with the Compare, TrieCompare or Equal methods as well as various provided instances of AddressComparator, // they are not comparable with Go operators. // However, AddressKey instances are comparable with Go operators, and thus can be used as map keys. func (addr *IPAddress) ToKey() Key[*IPAddress] { key := Key[*IPAddress]{} contents := &key.keyContents if thisAddr := addr.ToIPv4(); thisAddr != nil { key.scheme = ipv4Scheme thisAddr.toIPv4Key(contents) } else if thisAddr := addr.ToIPv6(); thisAddr != nil { key.scheme = ipv6Scheme thisAddr.toIPv6Key(contents) } // else key.scheme == anySchemeX return key } // ToGenericKey produces a generic Key[*IPAddress] that can be used with generic code working with [Address], [IPAddress], [IPv4Address], [IPv6Address] and [MACAddress]. func (addr *IPAddress) ToGenericKey() Key[*IPAddress] { return addr.ToKey() } func (addr *IPAddress) fromKey(scheme addressScheme, key *keyContents) *IPAddress { if scheme == ipv4Scheme { ipv4Addr := fromIPv4IPKey(key) return ipv4Addr.ToIP() } else if scheme == ipv6Scheme { ipv6Addr := fromIPv6IPKey(key) return ipv6Addr.ToIP() } // scheme == adaptiveZeroScheme zeroAddr := IPAddress{} return zeroAddr.init() } // IPAddressValueProvider supplies all the values that incorporate an IPAddress instance. type IPAddressValueProvider interface { AddressValueProvider GetPrefixLen() PrefixLen // return nil if none GetIPVersion() IPVersion // should not return IndeterminateVersion GetZone() string // return "" or NoZone if none } func addrFromIP(ip net.IP) (addr *IPAddress, err addrerr.AddressValueError) { if ipv4 := ip.To4(); ipv4 != nil { ip = ipv4 } return addrFromBytes(ip) } func addrFromBytes(ip []byte) (addr *IPAddress, err addrerr.AddressValueError) { addrLen := len(ip) if len(ip) == 0 { return &IPAddress{}, nil } else if addrLen <= IPv4ByteCount { var addr4 *IPv4Address addr4, err = NewIPv4AddressFromBytes(ip) addr = addr4.ToIP() } else if addrLen <= IPv6ByteCount { var addr6 *IPv6Address addr6, err = NewIPv6AddressFromBytes(ip) addr = addr6.ToIP() } else { extraCount := len(ip) - IPv6ByteCount if isAllZeros(ip[:extraCount]) { var addr6 *IPv6Address addr6, err = NewIPv6AddressFromBytes(ip[extraCount:]) addr = addr6.ToIP() } else { err = &addressValueError{addressError: addressError{key: "ipaddress.error.exceeds.size"}} } } return } func addrFromPrefixedIP(ip net.IP, prefixLen PrefixLen) (addr *IPAddress, err addrerr.AddressValueError) { if ipv4 := ip.To4(); ipv4 != nil { ip = ipv4 } return addrFromPrefixedBytes(ip, prefixLen) } func addrFromPrefixedBytes(ip []byte, prefixLen PrefixLen) (addr *IPAddress, err addrerr.AddressValueError) { addrLen := len(ip) if len(ip) == 0 { return &IPAddress{}, nil } else if addrLen <= IPv4ByteCount { var addr4 *IPv4Address addr4, err = NewIPv4AddressFromPrefixedBytes(ip, prefixLen) addr = addr4.ToIP() } else if addrLen <= IPv6ByteCount { var addr6 *IPv6Address addr6, err = NewIPv6AddressFromPrefixedBytes(ip, prefixLen) addr = addr6.ToIP() } else { extraCount := len(ip) - IPv6ByteCount if isAllZeros(ip[:extraCount]) { var addr6 *IPv6Address addr6, err = NewIPv6AddressFromPrefixedBytes(ip[extraCount:], prefixLen) addr = addr6.ToIP() } else { err = &addressValueError{addressError: addressError{key: "ipaddress.error.exceeds.size"}} } } return } func addrFromZonedIP(addr *net.IPAddr) (*IPAddress, addrerr.AddressValueError) { ip := addr.IP if ipv4 := ip.To4(); ipv4 != nil { ip = ipv4 } if len(ip) == 0 { return &IPAddress{}, nil } else if len(ip) <= IPv4ByteCount { res, err := NewIPv4AddressFromBytes(ip) return res.ToIP(), err } else if len(ip) <= IPv6ByteCount { res, err := NewIPv6AddressFromZonedBytes(ip, addr.Zone) return res.ToIP(), err } else { extraCount := len(ip) - IPv6ByteCount if isAllZeros(ip[:extraCount]) { var addr6 *IPv6Address addr6, err := NewIPv6AddressFromZonedBytes(ip[extraCount:], addr.Zone) res := addr6.ToIP() return res, err } } return nil, &addressValueError{addressError: addressError{key: "ipaddress.error.exceeds.size"}} } func addrFromPrefixedZonedIP(addr *net.IPAddr, prefixLen PrefixLen) (*IPAddress, addrerr.AddressValueError) { ip := addr.IP if ipv4 := ip.To4(); ipv4 != nil { ip = ipv4 } if len(ip) == 0 { return &IPAddress{}, nil } else if len(ip) <= IPv4ByteCount { res, err := NewIPv4AddressFromPrefixedBytes(ip, prefixLen) return res.ToIP(), err } else if len(ip) <= IPv6ByteCount { res, err := NewIPv6AddressFromPrefixedZonedBytes(ip, prefixLen, addr.Zone) return res.ToIP(), err } else { extraCount := len(ip) - IPv6ByteCount if isAllZeros(ip[:extraCount]) { var addr6 *IPv6Address addr6, err := NewIPv6AddressFromPrefixedZonedBytes(ip[extraCount:], prefixLen, addr.Zone) res := addr6.ToIP() return res, err } } return nil, &addressValueError{addressError: addressError{key: "ipaddress.error.exceeds.size"}} } func isAllZeros(byts []byte) bool { for _, b := range byts { if b != 0 { return false } } return true } // IPAddressCreator is a polymporphic type providing constructor methods to construct IP addresses corresponding to its contained IP version type IPAddressCreator struct { IPVersion } // CreateSegment creates an IPv4 or IPv6 segment depending on the IP version assigned to this IPAddressCreator instance. // If the IP version is indeterminate, then nil is returned. func (creator IPAddressCreator) CreateSegment(lower, upper SegInt, segmentPrefixLength PrefixLen) *IPAddressSegment { if creator.IsIPv4() { return NewIPv4RangePrefixedSegment(IPv4SegInt(lower), IPv4SegInt(upper), segmentPrefixLength).ToIP() } else if creator.IsIPv6() { return NewIPv6RangePrefixedSegment(IPv6SegInt(lower), IPv6SegInt(upper), segmentPrefixLength).ToIP() } return nil } // CreateRangeSegment creates an IPv4 or IPv6 range-valued segment depending on the IP version assigned to this IPAddressCreator instance. // If the IP version is indeterminate, then nil is returned. func (creator IPAddressCreator) CreateRangeSegment(lower, upper SegInt) *IPAddressSegment { if creator.IsIPv4() { return NewIPv4RangeSegment(IPv4SegInt(lower), IPv4SegInt(upper)).ToIP() } else if creator.IsIPv6() { return NewIPv6RangeSegment(IPv6SegInt(lower), IPv6SegInt(upper)).ToIP() } return nil } // CreatePrefixSegment creates an IPv4 or IPv6 segment with a prefix length depending on the IP version assigned to this IPAddressCreator instance. // If the IP version is indeterminate, then nil is returned. func (creator IPAddressCreator) CreatePrefixSegment(value SegInt, segmentPrefixLength PrefixLen) *IPAddressSegment { if creator.IsIPv4() { return NewIPv4PrefixedSegment(IPv4SegInt(value), segmentPrefixLength).ToIP() } else if creator.IsIPv6() { return NewIPv6PrefixedSegment(IPv6SegInt(value), segmentPrefixLength).ToIP() } return nil } // NewIPSectionFromBytes creates an address section from the given bytes, It is IPv4 or IPv6 depending on the IP version assigned to this IPAddressCreator instance. // The number of segments is determined by the length of the byte array. // If the IP version is indeterminate, then nil is returned. func (creator IPAddressCreator) NewIPSectionFromBytes(bytes []byte) *IPAddressSection { if creator.IsIPv4() { return NewIPv4SectionFromBytes(bytes).ToIP() } else if creator.IsIPv6() { return NewIPv6SectionFromBytes(bytes).ToIP() } return nil } // NewIPSectionFromSegmentedBytes creates an address section from the given bytes. It is IPv4 or IPv6 depending on the IP version assigned to this IPAddressCreator instance. // The number of segments is given. An error is returned when the byte slice has too many bytes to match the segment count. // IPv4 should have 4 bytes or less, IPv6 16 bytes or less, although extra leading zeros are tolerated. // If the IP version is indeterminate, then nil is returned. func (creator IPAddressCreator) NewIPSectionFromSegmentedBytes(bytes []byte, segmentCount int) (*IPAddressSection, addrerr.AddressValueError) { if creator.IsIPv4() { addr, err := NewIPv4SectionFromSegmentedBytes(bytes, segmentCount) return addr.ToIP(), err } else if creator.IsIPv6() { addr, err := NewIPv6SectionFromSegmentedBytes(bytes, segmentCount) return addr.ToIP(), err } return nil, &addressValueError{addressError: addressError{key: "ipaddress.error.ipVersionIndeterminate"}} } // NewIPSectionFromPrefixedBytes creates an address section from the given bytes and prefix length. It is IPv4 or IPv6 depending on the IP version assigned to this IPAddressCreator instance. // The number of segments is given. An error is returned when the byte slice has too many bytes to match the segment count. // IPv4 should have 4 bytes or less, IPv6 16 bytes or less, although extra leading zeros are tolerated. // If the IP version is indeterminate, then nil is returned. func (creator IPAddressCreator) NewIPSectionFromPrefixedBytes(bytes []byte, segmentCount int, prefLen PrefixLen) (*IPAddressSection, addrerr.AddressValueError) { if creator.IsIPv4() { addr, err := NewIPv4SectionFromPrefixedBytes(bytes, segmentCount, prefLen) return addr.ToIP(), err } else if creator.IsIPv6() { addr, err := NewIPv4SectionFromPrefixedBytes(bytes, segmentCount, prefLen) return addr.ToIP(), err } return nil, &addressValueError{addressError: addressError{key: "ipaddress.error.ipVersionIndeterminate"}} } // NewIPAddressFromVals constructs an IPAddress from the provided segment values. // If the IP version of this IPAddressCreator is indeterminate, then nil is returned. func (creator IPAddressCreator) NewIPAddressFromVals(lowerValueProvider SegmentValueProvider) *IPAddress { return NewIPAddressFromVals(creator.IPVersion, lowerValueProvider) } // NewIPAddressFromPrefixedVals constructs an IPAddress from the provided segment values and prefix length. // If the IP version of this IPAddressCreator is indeterminate, then nil is returned. // The prefix length is adjusted to 0 if negative or to the bit count if larger. func (creator IPAddressCreator) NewIPAddressFromPrefixedVals(lowerValueProvider, upperValueProvider SegmentValueProvider, prefixLength PrefixLen) *IPAddress { return NewIPAddressFromPrefixedVals(creator.IPVersion, lowerValueProvider, upperValueProvider, prefixLength) } // NewIPAddressFromPrefixedZonedVals constructs an IPAddress from the provided segment values, prefix length, and zone. // If the IP version of this IPAddressCreator is indeterminate, then nil is returned. // If the version is IPv4, then the zone is ignored. // The prefix length is adjusted to 0 if negative or to the bit count if larger. func (creator IPAddressCreator) NewIPAddressFromPrefixedZonedVals(lowerValueProvider, upperValueProvider SegmentValueProvider, prefixLength PrefixLen, zone string) *IPAddress { return NewIPAddressFromPrefixedZonedVals(creator.IPVersion, lowerValueProvider, upperValueProvider, prefixLength, zone) } // NewIPAddressFromNetIPMask constructs an address from a net.IPMask. // An error is returned when the mask has an invalid number of bytes. IPv4 should have 4 bytes or less, IPv6 16 bytes or less, although extra leading zeros are tolerated. func NewIPAddressFromNetIPMask(ip net.IPMask) (*IPAddress, addrerr.AddressValueError) { return addrFromBytes(ip) } // NewIPAddressFromBytes constructs an address from a slice of bytes. // An error is returned when the IP has an invalid number of bytes. IPv4 should have 4 bytes or less, IPv6 16 bytes or less, although extra leading zeros are tolerated. func NewIPAddressFromBytes(ip net.IP) (*IPAddress, addrerr.AddressValueError) { return addrFromBytes(ip) } // NewIPAddressFromNetIP constructs an address from a net.IP. // An error is returned when the IP has an invalid number of bytes. IPv4 should have 4 bytes or less, IPv6 16 bytes or less, although extra leading zeros are tolerated. func NewIPAddressFromNetIP(ip net.IP) (*IPAddress, addrerr.AddressValueError) { return addrFromIP(ip) } // NewIPAddressFromPrefixedNetIP constructs an address or subnet from a net.IP with a prefix length. // An error is returned when the IP has an invalid number of bytes. IPv4 should have 4 bytes or less, IPv6 16 bytes or less, although extra leading zeros are tolerated. func NewIPAddressFromPrefixedNetIP(ip net.IP, prefixLength PrefixLen) (*IPAddress, addrerr.AddressValueError) { return addrFromPrefixedIP(ip, prefixLength) } // NewIPAddressFromNetIPAddr constructs an address or subnet from a net.IPAddr. // An error is returned when the IP has an invalid number of bytes. IPv4 should have 4 bytes or less, IPv6 16 bytes or less, although extra leading zeros are tolerated. func NewIPAddressFromNetIPAddr(addr *net.IPAddr) (*IPAddress, addrerr.AddressValueError) { return addrFromZonedIP(addr) } // NewIPAddressFromPrefixedNetIPAddr constructs an address or subnet from a net.IPAddr with a prefix length. // An error is returned when the IP has an invalid number of bytes. IPv4 should have 4 bytes or less, IPv6 16 bytes or less, although extra leading zeros are tolerated. func NewIPAddressFromPrefixedNetIPAddr(addr *net.IPAddr, prefixLength PrefixLen) (*IPAddress, addrerr.AddressValueError) { return addrFromPrefixedZonedIP(addr, prefixLength) } // NewIPAddressFromNetIPNet constructs a subnet from a net.IPNet. // The error can be either addrerr.AddressValueError, when the net.IPNet IP or mask has an invalid number of bytes, // or addrerr.IncompatibleAddressError when the mask and the IP from net.IPNet are different IP versions. func NewIPAddressFromNetIPNet(ipnet *net.IPNet) (*IPAddress, addrerr.AddressError) { ip := ipnet.IP maskIp := ipnet.Mask if ipv4 := ip.To4(); ipv4 != nil { ip = ipv4 if len(maskIp) == net.IPv6len { maskIp = maskIp[IPv6MixedOriginalByteCount:] } } addr, err := addrFromBytes(ip) if err != nil { return nil, err } else if addr == nil { return nil, &addressValueError{addressError: addressError{key: "ipaddress.error.exceeds.size"}} } mask, err := NewIPAddressFromNetIPMask(maskIp) if err != nil { return nil, err } else if mask == nil { return nil, &addressValueError{addressError: addressError{key: "ipaddress.error.exceeds.size"}} } else if addr.getAddrType() != mask.getAddrType() { //} else if !addr.GetIPVersion().Equal(mask.GetIPVersion()) { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.ipMismatch"}} } prefLen := mask.GetBlockMaskPrefixLen(true) if prefLen == nil { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.notNetworkMask"}} } return addr.ToPrefixBlockLen(prefLen.bitCount()), nil } func NewIPAddressFromNetNetIPAddr(addr netip.Addr) *IPAddress { if res := addr.AsSlice(); res != nil { if addr.Is6() { if zone := addr.Zone(); zone != "" { addr, _ := NewIPv6AddressFromZonedBytes(res, zone) return addr.ToIP() } } addr, _ := addrFromBytes(res) return addr.ToIP() } // the zero addr return &IPAddress{} } func NewIPAddressFromNetNetIPPrefix(prefixedAddr netip.Prefix) (*IPAddress, addrerr.AddressError) { prefixLen := prefixedAddr.Bits() if prefixLen < 0 { return nil, &addressValueError{addressError: addressError{key: "ipaddress.error.invalidCIDRPrefix"}} } addr := prefixedAddr.Addr() if res := addr.AsSlice(); res != nil { var p PrefixBitCount = PrefixBitCount(prefixLen) if addr.Is6() { if zone := addr.Zone(); zone != "" { addr, _ := NewIPv6AddressFromPrefixedZonedBytes(res, &p, zone) return addr.ToIP(), nil } } addr, _ := addrFromPrefixedBytes(res, &p) return addr.ToIP(), nil } return nil, &addressValueError{addressError: addressError{key: "ipaddress.error.ipVersionIndeterminate"}} } // NewIPAddressFromVals constructs an IPAddress from the provided segment values. // If the given version is indeterminate, then nil is returned. func NewIPAddressFromVals(version IPVersion, lowerValueProvider SegmentValueProvider) *IPAddress { if version.IsIPv4() { return NewIPv4AddressFromVals(WrapSegmentValueProviderForIPv4(lowerValueProvider)).ToIP() } else if version.IsIPv6() { return NewIPv6AddressFromVals(WrapSegmentValueProviderForIPv6(lowerValueProvider)).ToIP() } return nil } // NewIPAddressFromPrefixedVals constructs an IPAddress from the provided segment values and prefix length. // If the given version is indeterminate, then nil is returned. // The prefix length is adjusted to 0 if negative or to the bit count if larger. func NewIPAddressFromPrefixedVals(version IPVersion, lowerValueProvider, upperValueProvider SegmentValueProvider, prefixLength PrefixLen) *IPAddress { return NewIPAddressFromPrefixedZonedVals(version, lowerValueProvider, upperValueProvider, prefixLength, "") } // NewIPAddressFromPrefixedZonedVals constructs an IPAddress from the provided segment values, prefix length, and zone. // If the given version is indeterminate, then nil is returned. // If the version is IPv4, then the zone is ignored. // The prefix length is adjusted to 0 if negative or to the bit count if larger. func NewIPAddressFromPrefixedZonedVals(version IPVersion, lowerValueProvider, upperValueProvider SegmentValueProvider, prefixLength PrefixLen, zone string) *IPAddress { if version.IsIPv4() { return NewIPv4AddressFromPrefixedRange( WrapSegmentValueProviderForIPv4(lowerValueProvider), WrapSegmentValueProviderForIPv4(upperValueProvider), prefixLength).ToIP() } else if version.IsIPv6() { return NewIPv6AddressFromPrefixedZonedRange( WrapSegmentValueProviderForIPv6(lowerValueProvider), WrapSegmentValueProviderForIPv6(upperValueProvider), prefixLength, zone).ToIP() } return nil } // NewIPAddressFromSegs constructs an address from the given segments. // If the segments are not consistently IPv4 or IPv6, or if there is not the correct number of segments for the IP version (4 for IPv4, 8 for IPv6), // then an error is returned. func NewIPAddressFromSegs(segments []*IPAddressSegment) (res *IPAddress, err addrerr.AddressValueError) { return NewIPAddressFromPrefixedSegments(segments, nil) } // NewIPAddressFromPrefixedSegments constructs an address from the given segments and prefix length. // If the segments are not consistently IPv4 or IPv6, or if there is not the correct number of segments for the IP version (4 for IPv4, 8 for IPv6), // then an error is returned. func NewIPAddressFromPrefixedSegments(segs []*IPAddressSegment, prefixLength PrefixLen) (res *IPAddress, err addrerr.AddressValueError) { if len(segs) > 0 { if segs[0].IsIPv4() { for _, seg := range segs[1:] { if !seg.IsIPv4() { err = &addressValueError{addressError: addressError{key: "ipaddress.error.ipVersionMismatch"}} return } } sect := createIPSectionFromSegs(true, segs, prefixLength) addr, addrErr := NewIPv4Address(sect.ToIPv4()) res, err = addr.ToIP(), addrErr } else if segs[0].IsIPv6() { for _, seg := range segs[1:] { if !seg.IsIPv6() { err = &addressValueError{addressError: addressError{key: "ipaddress.error.ipVersionMismatch"}} return } } sect := createIPSectionFromSegs(false, segs, prefixLength) addr, addrErr := NewIPv6Address(sect.ToIPv6()) res, err = addr.ToIP(), addrErr } else { err = &addressValueError{addressError: addressError{key: "ipaddress.error.invalid.size"}} } } else { err = &addressValueError{addressError: addressError{key: "ipaddress.error.invalid.size"}} } return } // NewIPAddressFromValueProvider constructs an IPAddress from the provided segment values, prefix length, and zone, // all of which are supplied by the implementation of IPAddressValueProvider. // If the given version is indeterminate, then nil is returned. // If the version is IPv4, then the zone is ignored. // The prefix length is adjusted to 0 if negative or to the bit count if larger. func NewIPAddressFromValueProvider(valueProvider IPAddressValueProvider) *IPAddress { if valueProvider.GetIPVersion().IsIPv4() { return NewIPv4AddressFromPrefixedRange( WrapSegmentValueProviderForIPv4(valueProvider.GetValues()), WrapSegmentValueProviderForIPv4(valueProvider.GetUpperValues()), valueProvider.GetPrefixLen()).ToIP() } else if valueProvider.GetIPVersion().IsIPv6() { return NewIPv6AddressFromPrefixedZonedRange( WrapSegmentValueProviderForIPv6(valueProvider.GetValues()), WrapSegmentValueProviderForIPv6(valueProvider.GetUpperValues()), valueProvider.GetPrefixLen(), valueProvider.GetZone()).ToIP() } return nil } ipaddress-go-1.5.4/ipaddr/ipaddressprovider.go000066400000000000000000000641631440250641600214410ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) // All IP address strings corresponds to exactly one of these types. // In cases where there is no corresponding default IPAddress value (invalidType, allType, and possibly emptyType), these types can be used for comparison. // emptyType means a zero-length string (useful for validation, we can set validation to allow empty strings) that has no corresponding IPAddress value (validation options allow you to map empty to the loopback) // invalidType means it is known that it is not any of the other allowed types (validation options can restrict the allowed types) // allType means it is wildcard(s) with no separators, like "*", which represents all addresses, whether IPv4, IPv6 or other, and thus has no corresponding IPAddress value // These constants are ordered by address space size, from smallest to largest, and the ordering affects comparisons type ipType int func fromVersion(version IPVersion) ipType { switch version { case IPv4: return ipv4AddrType case IPv6: return ipv6AddrType default: } return uninitializedType } func (t ipType) isUnknown() bool { return t == uninitializedType } const ( uninitializedType ipType = iota invalidType emptyType ipv4AddrType ipv6AddrType allType ) type ipAddressProvider interface { getType() ipType getProviderHostAddress() (*IPAddress, addrerr.IncompatibleAddressError) getProviderAddress() (*IPAddress, addrerr.IncompatibleAddressError) getVersionedAddress(version IPVersion) (*IPAddress, addrerr.IncompatibleAddressError) isSequential() bool getProviderSeqRange() *SequentialRange[*IPAddress] getProviderMask() *IPAddress // TODO LATER getDivisionGrouping //default IPAddressDivisionSeries getDivisionGrouping() throwsaddrerr.IncompatibleAddressError { // return getProviderAddress(); //} providerCompare(ipAddressProvider) (int, addrerr.IncompatibleAddressError) providerEquals(ipAddressProvider) (bool, addrerr.IncompatibleAddressError) getProviderIPVersion() IPVersion isProvidingIPAddress() bool isProvidingIPv4() bool isProvidingIPv6() bool // providing **all** addresses of any IP version, ie "*", not "*.*" or "*:*" isProvidingAllAddresses() bool isProvidingEmpty() bool isProvidingMixedIPv6() bool isProvidingBase85IPv6() bool getProviderNetworkPrefixLen() PrefixLen isInvalid() bool // If the address was created by parsing, this provides the parameters used when creating the address, // otherwise nil getParameters() addrstrparam.IPAddressStringParams // containsProvider is an optimized contains that does not need to create address objects to return an answer. // Unconventional addresses may require that the address objects are created, in such cases nil is returned. // // Addresses constructed from canonical or normalized representations with no wildcards will not return null. containsProvider(ipAddressProvider) boolSetting // prefixEqualsProvider is an optimized prefix comparison that does not need to create addresses to return an answer. // // Unconventional addresses may require the address objects, in such cases null is returned. prefixEqualsProvider(ipAddressProvider) boolSetting // prefixContainsProvider is an optimized prefix comparison that does not need to create addresses to return an answer. // // Unconventional addresses may require the address objects, in such cases null is returned. prefixContainsProvider(ipAddressProvider) boolSetting // parsedEquals is an optimized equality comparison that does not need to create addresses to return an answer. // // Unconventional addresses may require the address objects, in such cases null is returned. parsedEquals(ipAddressProvider) boolSetting } type ipAddrProvider struct{} func (p *ipAddrProvider) getType() ipType { return uninitializedType } func (p *ipAddrProvider) isSequential() bool { return false } func (p *ipAddrProvider) getProviderHostAddress() (*IPAddress, addrerr.IncompatibleAddressError) { return nil, nil } func (p *ipAddrProvider) getProviderAddress() (*IPAddress, addrerr.IncompatibleAddressError) { return nil, nil } func (p *ipAddrProvider) getProviderSeqRange() *SequentialRange[*IPAddress] { return nil } func (p *ipAddrProvider) getVersionedAddress(_ IPVersion) (*IPAddress, addrerr.IncompatibleAddressError) { return nil, nil } func (p *ipAddrProvider) getProviderMask() *IPAddress { return nil } func (p *ipAddrProvider) getProviderIPVersion() IPVersion { return IndeterminateIPVersion } func (p *ipAddrProvider) isProvidingIPAddress() bool { return false } func (p *ipAddrProvider) isProvidingIPv4() bool { return false } func (p *ipAddrProvider) isProvidingIPv6() bool { return false } func (p *ipAddrProvider) isProvidingAllAddresses() bool { return false } func (p *ipAddrProvider) isProvidingEmpty() bool { return false } func (p *ipAddrProvider) isInvalid() bool { return false } func (p *ipAddrProvider) isProvidingMixedIPv6() bool { return false } func (p *ipAddrProvider) isProvidingBase85IPv6() bool { return false } func (p *ipAddrProvider) getProviderNetworkPrefixLen() PrefixLen { return nil } func (p *ipAddrProvider) getParameters() addrstrparam.IPAddressStringParams { return nil } func (p *ipAddrProvider) containsProvider(ipAddressProvider) boolSetting { return boolSetting{} } func (p *ipAddrProvider) prefixEqualsProvider(ipAddressProvider) boolSetting { return boolSetting{} } func (p *ipAddrProvider) prefixContainsProvider(ipAddressProvider) boolSetting { return boolSetting{} } func (p *ipAddrProvider) parsedEquals(ipAddressProvider) boolSetting { return boolSetting{} } func providerCompare(p, other ipAddressProvider) (res int, err addrerr.IncompatibleAddressError) { if p == other { return } value, err := p.getProviderAddress() if err != nil { return } if value != nil { var otherValue *IPAddress otherValue, err = other.getProviderAddress() if err != nil { return } if otherValue != nil { res = value.Compare(otherValue) return } } var thisType, otherType = p.getType(), other.getType() res = int(thisType - otherType) return } /** * When a value provider produces no value, equality and comparison are based on the enum ipType, * which can by null. * @param o * @return */ func providerEquals(p, other ipAddressProvider) (res bool, err addrerr.IncompatibleAddressError) { if p == other { res = true return } value, err := p.getProviderAddress() if err != nil { return } if value != nil { var otherValue *IPAddress otherValue, err = other.getProviderAddress() if err != nil { return } if otherValue != nil { res = value.Equal(otherValue) return } else { return // returns false } } res = p.getType() == other.getType() return } type nullProvider struct { ipAddrProvider ipType ipType isInvalidVal, isEmpty bool params addrstrparam.IPAddressStringParams } func (p *nullProvider) isInvalid() bool { return p.isInvalidVal } func (p *nullProvider) isProvidingEmpty() bool { return p.isEmpty } func (p *nullProvider) getType() ipType { return p.ipType } func (p *nullProvider) providerCompare(other ipAddressProvider) (int, addrerr.IncompatibleAddressError) { return providerCompare(p, other) } func (p *nullProvider) providerEquals(other ipAddressProvider) (bool, addrerr.IncompatibleAddressError) { return providerEquals(p, other) } var ( invalidProvider = &nullProvider{isInvalidVal: true, ipType: invalidType} emptyProvider = &nullProvider{isEmpty: true, ipType: emptyType} ) // Wraps an IPAddress for IPAddressString in the cases where no parsing is provided, the address exists already func getProviderFor(address, hostAddress *IPAddress) ipAddressProvider { return &cachedAddressProvider{addresses: &addressResult{address: address, hostAddress: hostAddress}} } type addressResult struct { address, hostAddress *IPAddress // addrErr applies to address, hostErr to hostAddress addrErr, hostErr addrerr.IncompatibleAddressError // only used when no address can be obtained rng *SequentialRange[*IPAddress] } type cachedAddressProvider struct { ipAddrProvider // addressCreator creates two addresses, the host address and address with prefix/mask, at the same time addressCreator func() (address, hostAddress *IPAddress, addrErr, hosterr addrerr.IncompatibleAddressError) addresses *addressResult } func (cached *cachedAddressProvider) providerCompare(other ipAddressProvider) (int, addrerr.IncompatibleAddressError) { return providerCompare(cached, other) } func (cached *cachedAddressProvider) providerEquals(other ipAddressProvider) (bool, addrerr.IncompatibleAddressError) { return providerEquals(cached, other) } func (cached *cachedAddressProvider) isProvidingIPAddress() bool { return true } func (cached *cachedAddressProvider) getVersionedAddress(version IPVersion) (*IPAddress, addrerr.IncompatibleAddressError) { thisVersion := cached.getProviderIPVersion() if version != thisVersion { return nil, nil } return cached.getProviderAddress() } func (cached *cachedAddressProvider) getProviderSeqRange() *SequentialRange[*IPAddress] { addr, _ := cached.getProviderAddress() if addr != nil { return addr.ToSequentialRange() } return nil } func (cached *cachedAddressProvider) isSequential() bool { addr, _ := cached.getProviderAddress() if addr != nil { return addr.IsSequential() } return false } func (cached *cachedAddressProvider) getProviderHostAddress() (res *IPAddress, err addrerr.IncompatibleAddressError) { _, res, _, err = cached.getCachedAddresses() return } func (cached *cachedAddressProvider) getProviderAddress() (res *IPAddress, err addrerr.IncompatibleAddressError) { res, _, err, _ = cached.getCachedAddresses() return } func (cached *cachedAddressProvider) getCachedAddresses() (address, hostAddress *IPAddress, addrErr, hostErr addrerr.IncompatibleAddressError) { addrs := (*addressResult)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cached.addresses)))) if addrs == nil { if cached.addressCreator != nil { address, hostAddress, addrErr, hostErr = cached.addressCreator() addresses := &addressResult{ address: address, hostAddress: hostAddress, addrErr: addrErr, hostErr: hostErr, } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cached.addresses)) atomicStorePointer(dataLoc, unsafe.Pointer(addresses)) } } else { address, hostAddress, addrErr, hostErr = addrs.address, addrs.hostAddress, addrs.addrErr, addrs.hostErr } return } func (cached *cachedAddressProvider) getProviderNetworkPrefixLen() (p PrefixLen) { if addr, _ := cached.getProviderAddress(); addr != nil { p = addr.getNetworkPrefixLen() } return } func (cached *cachedAddressProvider) getProviderIPVersion() IPVersion { if addr, _ := cached.getProviderAddress(); addr != nil { return addr.getIPVersion() } return IndeterminateIPVersion } func (cached *cachedAddressProvider) getType() ipType { return fromVersion(cached.getProviderIPVersion()) } func (cached *cachedAddressProvider) isProvidingIPv4() bool { addr, _ := cached.getProviderAddress() return addr.IsIPv4() } func (cached *cachedAddressProvider) isProvidingIPv6() bool { addr, _ := cached.getProviderAddress() return addr.IsIPv6() } type versionedAddressCreator struct { cachedAddressProvider adjustedVersion IPVersion versionedAddressCreatorFunc func(IPVersion) (*IPAddress, addrerr.IncompatibleAddressError) versionedValues [2]*IPAddress parameters addrstrparam.IPAddressStringParams } func (versioned *versionedAddressCreator) getParameters() addrstrparam.IPAddressStringParams { return versioned.parameters } func (versioned *versionedAddressCreator) isProvidingIPAddress() bool { return versioned.adjustedVersion != IndeterminateIPVersion } func (versioned *versionedAddressCreator) isProvidingIPv4() bool { return versioned.adjustedVersion == IPv4 } func (versioned *versionedAddressCreator) isProvidingIPv6() bool { return versioned.adjustedVersion == IPv6 } func (versioned *versionedAddressCreator) getProviderIPVersion() IPVersion { return versioned.adjustedVersion } func (versioned *versionedAddressCreator) getType() ipType { return fromVersion(versioned.adjustedVersion) } func (versioned *versionedAddressCreator) getVersionedAddress(version IPVersion) (addr *IPAddress, err addrerr.IncompatibleAddressError) { index := version.index() if index >= IndeterminateIPVersion.index() { return } if versioned.versionedAddressCreatorFunc != nil { addr = (*IPAddress)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&versioned.versionedValues[index])))) if addr == nil { addr, err = versioned.versionedAddressCreatorFunc(version) if err == nil { dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&versioned.versionedValues[index])) atomicStorePointer(dataLoc, unsafe.Pointer(addr)) } } return } addr = versioned.versionedValues[index] return } func emptyAddressCreator(emptyStrOption addrstrparam.EmptyStrOption, version IPVersion, zone Zone) (addrCreator func() (address, hostAddress *IPAddress), versionedCreator func() *IPAddress) { preferIPv6 := version.IsIPv6() double := func(one *IPAddress) (address, hostAddress *IPAddress) { return one, one } if emptyStrOption == addrstrparam.NoAddressOption { addrCreator = func() (*IPAddress, *IPAddress) { return double(nil) } versionedCreator = func() *IPAddress { return nil } } else if emptyStrOption == addrstrparam.LoopbackOption { if preferIPv6 { if len(zone) > 0 { ipv6WithZoneLoop := func() *IPAddress { network := ipv6Network creator := network.getIPAddressCreator() return creator.createAddressInternalFromBytes(network.GetLoopback().Bytes(), zone) } versionedCreator = ipv6WithZoneLoop addrCreator = func() (*IPAddress, *IPAddress) { return double(ipv6WithZoneLoop()) } } else { ipv6Loop := func() *IPAddress { return ipv6Network.GetLoopback() } versionedCreator = ipv6Loop addrCreator = func() (*IPAddress, *IPAddress) { return double(ipv6Loop()) } } } else { ipv4Loop := func() *IPAddress { return ipv4Network.GetLoopback() } addrCreator = func() (*IPAddress, *IPAddress) { return double(ipv4Loop()) } versionedCreator = ipv4Loop } } else { // EmptyStrParsedAs() == ZeroAddressOption if preferIPv6 { if len(zone) > 0 { ipv6WithZoneZero := func() *IPAddress { network := ipv6Network creator := network.getIPAddressCreator() return creator.createAddressInternalFromBytes(zeroIPv6.Bytes(), zone) } versionedCreator = ipv6WithZoneZero addrCreator = func() (*IPAddress, *IPAddress) { return double(ipv6WithZoneZero()) } } else { ipv6Zero := func() *IPAddress { return zeroIPv6.ToIP() } versionedCreator = ipv6Zero addrCreator = func() (*IPAddress, *IPAddress) { return double(ipv6Zero()) } } } else { ipv4Zero := func() *IPAddress { return zeroIPv4.ToIP() } addrCreator = func() (*IPAddress, *IPAddress) { return double(ipv4Zero()) } versionedCreator = ipv4Zero } } return } func newEmptyAddrCreator(options addrstrparam.IPAddressStringParams, zone Zone) *emptyAddrCreator { var version = IPVersion(options.GetPreferredVersion()) // EmptyStrParsedAs chooses whether to produce loopbacks, zero addresses, or nothing for the empty string "" addrCreator, versionedCreator := emptyAddressCreator(options.EmptyStrParsedAs(), version, zone) cached := cachedAddressProvider{ addressCreator: func() (address, hostAddress *IPAddress, addrErr, hosterr addrerr.IncompatibleAddressError) { address, hostAddress = addrCreator() return }, } versionedCreatorFunc := func(v IPVersion) *IPAddress { addresses := cached.addresses if addresses != nil { addr := addresses.address if v == addr.GetIPVersion() { return addr } } if v.IsIndeterminate() { return versionedCreator() } _, vCreator := emptyAddressCreator(options.EmptyStrParsedAs(), v, zone) return vCreator() } versionedAddressCreatorFunc := func(version IPVersion) (*IPAddress, addrerr.IncompatibleAddressError) { return versionedCreatorFunc(version), nil } return &emptyAddrCreator{ versionedAddressCreator: versionedAddressCreator{ adjustedVersion: version, parameters: options, cachedAddressProvider: cached, versionedAddressCreatorFunc: versionedAddressCreatorFunc, }, zone: zone, } } type emptyAddrCreator struct { versionedAddressCreator zone Zone } func (loop *emptyAddrCreator) providerCompare(other ipAddressProvider) (int, addrerr.IncompatibleAddressError) { return providerCompare(loop, other) } func (loop *emptyAddrCreator) providerEquals(other ipAddressProvider) (bool, addrerr.IncompatibleAddressError) { return providerEquals(loop, other) } func (loop *emptyAddrCreator) getProviderNetworkPrefixLen() PrefixLen { return nil } type adjustedAddressCreator struct { versionedAddressCreator networkPrefixLength PrefixLen } func (adjusted *adjustedAddressCreator) getProviderNetworkPrefixLen() PrefixLen { return adjusted.networkPrefixLength } func (adjusted *adjustedAddressCreator) getProviderAddress() (*IPAddress, addrerr.IncompatibleAddressError) { if !adjusted.isProvidingIPAddress() { return nil, nil } return adjusted.versionedAddressCreator.getProviderAddress() } func (adjusted *adjustedAddressCreator) getProviderHostAddress() (*IPAddress, addrerr.IncompatibleAddressError) { if !adjusted.isProvidingIPAddress() { return nil, nil } return adjusted.versionedAddressCreator.getProviderHostAddress() } func newMaskCreator(options addrstrparam.IPAddressStringParams, adjustedVersion IPVersion, networkPrefixLength PrefixLen) *maskCreator { if adjustedVersion == IndeterminateIPVersion { adjustedVersion = IPVersion(options.GetPreferredVersion()) } createVersionedMask := func(version IPVersion, prefLen PrefixLen, withPrefixLength bool) *IPAddress { if version == IPv4 { network := ipv4Network return network.GetNetworkMask(prefLen.bitCount()) } else if version == IPv6 { network := ipv6Network return network.GetNetworkMask(prefLen.bitCount()) } return nil } versionedAddressCreatorFunc := func(version IPVersion) (*IPAddress, addrerr.IncompatibleAddressError) { return createVersionedMask(version, networkPrefixLength, true), nil } maskCreatorFunc := func() (address, hostAddress *IPAddress) { prefLen := networkPrefixLength return createVersionedMask(adjustedVersion, prefLen, true), createVersionedMask(adjustedVersion, prefLen, false) } addrCreator := func() (address, hostAddress *IPAddress, addrErr, hosterr addrerr.IncompatibleAddressError) { address, hostAddress = maskCreatorFunc() return } cached := cachedAddressProvider{addressCreator: addrCreator} return &maskCreator{ adjustedAddressCreator{ networkPrefixLength: networkPrefixLength, versionedAddressCreator: versionedAddressCreator{ adjustedVersion: adjustedVersion, parameters: options, cachedAddressProvider: cached, versionedAddressCreatorFunc: versionedAddressCreatorFunc, }, }, } } type maskCreator struct { adjustedAddressCreator } func newAllCreator(qualifier *parsedHostIdentifierStringQualifier, adjustedVersion IPVersion, originator HostIdentifierString, options addrstrparam.IPAddressStringParams) ipAddressProvider { result := &allCreator{ adjustedAddressCreator: adjustedAddressCreator{ networkPrefixLength: qualifier.getEquivalentPrefixLen(), versionedAddressCreator: versionedAddressCreator{ adjustedVersion: adjustedVersion, parameters: options, }, }, originator: originator, qualifier: *qualifier, } result.addressCreator = result.createAddrs result.versionedAddressCreatorFunc = result.versionedCreate return result } type allCreator struct { adjustedAddressCreator originator HostIdentifierString qualifier parsedHostIdentifierStringQualifier } func (all *allCreator) getType() ipType { if !all.adjustedVersion.IsIndeterminate() { return fromVersion(all.adjustedVersion) } return allType } func (all *allCreator) providerCompare(other ipAddressProvider) (int, addrerr.IncompatibleAddressError) { return providerCompare(all, other) } func (all *allCreator) providerEquals(other ipAddressProvider) (bool, addrerr.IncompatibleAddressError) { return providerEquals(all, other) } // providing **all** addresses of any IP version, ie "*", not "*.*" or "*:*" func (all *allCreator) isProvidingAllAddresses() bool { return all.adjustedVersion == IndeterminateIPVersion } func (all *allCreator) getProviderNetworkPrefixLen() PrefixLen { return all.qualifier.getEquivalentPrefixLen() } func (all *allCreator) getProviderMask() *IPAddress { return all.qualifier.getMaskLower() } func (all *allCreator) isSequential() bool { addr, _ := all.getProviderAddress() if addr != nil { return addr.IsSequential() } return false } func (all *allCreator) getProviderHostAddress() (res *IPAddress, err addrerr.IncompatibleAddressError) { if !all.isProvidingIPAddress() { return nil, nil } _, res, _, err = all.createAddrs() return } func (all *allCreator) getProviderAddress() (res *IPAddress, err addrerr.IncompatibleAddressError) { if !all.isProvidingIPAddress() { return } res, _, err, _ = all.createAddrs() return } func (all *allCreator) getProviderSeqRange() *SequentialRange[*IPAddress] { if all.isProvidingAllAddresses() { return nil } return all.createRange() } func (all *allCreator) createRange() (rng *SequentialRange[*IPAddress]) { rng, _, _, _, _ = all.createAll() return } func (all *allCreator) createAddrs() (addr *IPAddress, hostAddr *IPAddress, addrErr, hostErr addrerr.IncompatibleAddressError) { _, addr, hostAddr, addrErr, hostErr = all.createAll() return } func (all *allCreator) createAll() (rng *SequentialRange[*IPAddress], addr *IPAddress, hostAddr *IPAddress, addrErr, hostErr addrerr.IncompatibleAddressError) { addrs := (*addressResult)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&all.addresses)))) if addrs == nil { var lower, upper *IPAddress addr, hostAddr, lower, upper, addrErr = createAllAddress( all.adjustedVersion, &all.qualifier, all.originator) rng = lower.SpanWithRange(upper) addresses := &addressResult{ address: addr, hostAddress: hostAddr, addrErr: addrErr, rng: rng, } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&all.addresses)) atomicStorePointer(dataLoc, unsafe.Pointer(addresses)) } else { rng, addr, hostAddr, addrErr, hostErr = addrs.rng, addrs.address, addrs.hostAddress, addrs.addrErr, addrs.hostErr } return } func (all *allCreator) versionedCreate(version IPVersion) (addr *IPAddress, addrErr addrerr.IncompatibleAddressError) { if version == all.adjustedVersion { return all.getProviderAddress() } else if all.adjustedVersion != IndeterminateIPVersion { return nil, nil } addr, _, _, _, addrErr = createAllAddress( version, &all.qualifier, all.originator) return } func (all *allCreator) prefixContainsProvider(otherProvider ipAddressProvider) boolSetting { return all.containsProviderFunc(otherProvider, (*IPAddress).prefixContains) } func (all *allCreator) containsProvider(otherProvider ipAddressProvider) (res boolSetting) { return all.containsProviderFunc(otherProvider, (*IPAddress).contains) } func (all *allCreator) containsProviderFunc(otherProvider ipAddressProvider, functor func(*IPAddress, AddressType) bool) (res boolSetting) { if otherProvider.isInvalid() { return boolSetting{true, false} } else if all.adjustedVersion == IndeterminateIPVersion { return boolSetting{true, true} } else if all.adjustedVersion != otherProvider.getProviderIPVersion() { return boolSetting{true, false} } else if all.qualifier.getMaskLower() == nil && all.qualifier.getZone() == NoZone { return boolSetting{true, true} } else if addr, err := all.getProviderAddress(); err != nil { return boolSetting{true, false} } else if otherAddr, err := all.getProviderAddress(); err != nil { return boolSetting{true, false} } else { return boolSetting{true, functor(addr, otherAddr)} //return boolSetting{true, addr.Contains(otherAddr)} } } // TODO LATER getDivisionGrouping() // // @Override // public IPAddressDivisionSeries getDivisionGrouping() throwsaddrerr.IncompatibleAddressError { // if(isProvidingAllAddresses()) { // return null; // } // IPAddressNetwork network = adjustedVersion.IsIPv4() ? // options.getIPv4Parameters().getNetwork() : options.getIPv6Parameters().getNetwork(); // IPAddress mask = getProviderMask(); // if(mask != null && mask.getBlockMaskPrefixLen(true) == null) { // // there is a mask // Integer hostMaskPrefixLen = mask.getBlockMaskPrefixLen(false); // if(hostMaskPrefixLen == null) { // not a host mask // throw newaddrerr.IncompatibleAddressError(getProviderAddress(), mask, "ipaddress.error.maskMismatch"); // } // IPAddress hostMask = network.getHostMask(hostMaskPrefixLen); // return hostMask.toPrefixBlock(); // } // IPAddressDivisionSeries grouping; // if(adjustedVersion.IsIPv4()) { // grouping = new IPAddressDivisionGrouping(new IPAddressBitsDivision[] { // new IPAddressBitsDivision(0, IPv4Address.MAX_VALUE, IPv4Address.BIT_COUNT, IPv4Address.DEFAULT_TEXTUAL_RADIX, network, qualifier.getEquivalentPrefixLength()) // }, network); // } else if(adjustedVersion.IsIPv6()) { // byte upperBytes[] = new byte[16]; // Arrays.fill(upperBytes, (byte) 0xff); // grouping = new IPAddressLargeDivisionGrouping(new IPAddressLargeDivision[] {new IPAddressLargeDivision(new byte[IPv6Address.BYTE_COUNT], upperBytes, IPv6Address.BIT_COUNT, IPv6Address.DEFAULT_TEXTUAL_RADIX, network, qualifier.getEquivalentPrefixLength())}, network); // } else { // grouping = null; // } // return grouping; // } // } ipaddress-go-1.5.4/ipaddr/ipaddressresources.go000066400000000000000000000435421440250641600216170ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Code generated by running convert.go from the go workspace directory (the one containing the src folder). Do not edit. package ipaddr var keyStrMap = map[string]int{ `ipaddress.error.invalidMACIPv6Range`: 8, `ipaddress.error.address.out.of.range`: 29, `ipaddress.error.single.segment`: 141, `ipaddress.host.error.invalidService.no.letter`: 143, `ipaddress.host.error.invalid.service.hyphen.consecutive`: 144, `ipaddress.error.mac.invalid.segment.count`: 45, `ipaddress.error.address.too.large`: 52, `ipaddress.host.error.invalid.service.hyphen.start`: 68, `ipaddress.error.ipv6.has.zone`: 85, `ipaddress.error.cannot.end.with.single.separator`: 136, `ipaddress.error.ipMismatch`: 13, `ipaddress.host.error.invalidService.too.long`: 74, `ipaddress.error.special.ip`: 81, `ipaddress.error.back.digit.count`: 106, `ipaddress.error.mixedVersions`: 111, `ipaddress.error.ipv6.invalid.segment.count`: 135, `ipaddress.host.error.empty.host.resolve`: 4, `ipaddress.error.ipv6.format`: 5, `ipaddress.error.segmentMismatch`: 41, `ipaddress.error.zero.not.allowed`: 48, `ipaddress.error.invalid.character.combination`: 101, `ipaddress.error.segment.leading.zeros`: 112, `ipaddress.error.exceeds.size`: 70, `ipaddress.host.error.cidrprefixonly`: 104, `ipaddress.host.error.invalid.type`: 124, `ipaddress.error.ipv6.segment.format`: 100, `ipaddress.host.error.invalid.length`: 3, `ipaddress.error.ip.format`: 15, `ipaddress.error.mask.single.segment`: 42, `ipaddress.host.error.service`: 43, `ipaddress.error.ipv4`: 58, `ipaddress.error.segment.too.short.at.index`: 9, `ipaddress.error.ipv4.segment.hex`: 16, `ipaddress.error.incompatible.position`: 95, `ipaddress.host.error.invalid.character.at.index`: 96, `ipaddress.error.mixedNetworks`: 98, `ipaddress.error.too.many.segments`: 127, `ipaddress.error.ipv4.invalid.octal.digit`: 10, `ipaddress.error.ipv4.invalid.segment.count`: 28, `ipaddress.host.error.empty`: 40, `ipaddress.host.error.bracketed.missing.end`: 62, `ipaddress.error.wildcardOrRangeIPv6`: 76, `ipaddress.error.sizeMismatch`: 87, `ipaddress.host.error.too.many.segments`: 129, `ipaddress.error.invalid.position`: 130, `ipaddress.error.too.few.segments`: 6, `ipaddress.host.error.all.numeric`: 55, `ipaddress.error.invalid.character.combination.at.index`: 53, `ipaddress.error.lower.below.range`: 66, `ipaddress.host.error.bracketed.conflicting.prefix.length`: 93, `ipaddress.error.nullNetwork`: 102, `ipaddress.error.only.ipv6.square.brackets`: 105, `ipaddress.error.invalid.mask.extra.chars`: 110, `ipaddress.host.error`: 99, `ipaddress.error.splitSeg`: 84, `ipaddress.mac.error.not.eui.convertible`: 89, `ipaddress.error.invalidRange`: 128, `ipaddress.error.invalid.character.at.index`: 63, `ipaddress.error.CIDRNotAllowed`: 120, `ipaddress.error.address.is.ipv4`: 2, `ipaddress.error.prefix.only`: 59, `ipaddress.error.zoneAndCIDRPrefix`: 75, `ipaddress.error.notNetworkMask`: 92, `ipaddress.error.address.lower.exceeds.upper`: 30, `ipaddress.host.error.port`: 32, `ipaddress.error.mismatched.bit.size`: 57, `ipaddress.error.ipv6.separator`: 61, `ipaddress.error.ipv4.invalid.byte.count`: 67, `ipaddress.error.ipv4.format`: 134, `ipaddress.error.invalidMultipleMask`: 108, `ipaddress.error.mac.invalid.byte.count`: 109, `ipaddress.error.prefixSize`: 50, `ipaddress.error.only.zone`: 65, `ipaddress.error.too.few.segments.digit.count`: 71, `ipaddress.error.ipv4.invalid.decimal.digit`: 73, `ipaddress.error.no.single.wildcard`: 83, `ipaddress.error.invalid.mask.wildcard`: 90, `ipaddress.error.ipv6.invalid.byte.count`: 113, `ipaddress.error.ipv4.too.few.segments`: 33, `ipaddress.error.separatePrefixFromMask`: 78, `ipaddress.error.no.range`: 118, `ipaddress.error.invalid.joined.ranges`: 126, `ipaddress.error.all`: 22, `ipaddress.error.version.mismatch`: 37, `ipaddress.error.segment.too.long.at.index`: 60, `ipaddress.error.maskMismatch`: 91, `ipaddress.host.error.bracketed.not.ipv6`: 116, `ipaddress.error.invalid.character`: 117, `ipaddress.error.address.not.block`: 131, `ipaddress.host.error.invalid.mechanism`: 132, `ipaddress.error.ipv4.too.many.segments`: 18, `ipaddress.error.ipv4.prefix.leading.zeros`: 27, `ipaddress.host.error.url`: 77, `ipaddress.error.inconsistent.prefixes`: 123, `ipaddress.error.address.is.ipv6`: 139, `ipaddress.host.error.ipaddress`: 140, `ipaddress.error.splitMismatch`: 1, `ipaddress.error.no.wildcard`: 23, `ipaddress.error.only.ipv6.has.zone`: 125, `ipaddress.error.separatePrefixFromAddress`: 137, `ipaddress.host.error.invalidService.no.chars`: 0, `ipaddress.error.index.exceeds.prefix.length`: 12, `ipaddress.error.ipv4.segment.too.large`: 44, `ipaddress.error.ipVersionIndeterminate`: 20, `ipaddress.error.ipv6.ambiguous`: 72, `ipaddress.error.ipv6`: 86, `ipaddress.error.invalid.zone.encoding`: 94, `ipaddress.error.lower.above.range`: 103, `ipaddress.error.null.segment`: 114, `ipaddress.error.invalidMixedRange`: 24, `ipaddress.error.no.iterator.element.to.remove`: 31, `ipaddress.error.ipv6.prefix.leading.zeros`: 49, `ipaddress.error.empty.start.of.range`: 19, `ipaddress.host.error.invalid.service.hyphen.end`: 47, `ipaddress.error.url`: 79, `ipaddress.host.error.invalidPort.too.large`: 122, `ipaddress.error.invalidCIDRPrefixOrMask`: 142, `ipaddress.error.empty`: 14, `ipaddress.error.ipv6.cannot.start.with.single.separator`: 17, `ipaddress.address.error`: 39, `ipaddress.host.error.host.brackets`: 80, `ipaddress.error.segment.too.long`: 26, `ipaddress.error.zone`: 119, `ipaddress.error.invalidCIDRPrefix`: 121, `ipaddress.mac.error.mix.format.characters.at.index`: 21, `ipaddress.error.unavailable.numeric`: 34, `ipaddress.error.negative`: 56, `ipaddress.error.invalid.mask.empty`: 11, `ipaddress.error.ipv4.invalid.binary.digit`: 35, `ipaddress.host.error.invalidPort.no.digits`: 46, `ipaddress.error.single.wildcard.order`: 107, `ipaddress.host.error.host.resolve`: 69, `ipaddress.error.no.mixed`: 82, `ipaddress.error.front.digit.count`: 7, `ipaddress.mac.error.format`: 36, `ipaddress.error.ipVersionMismatch`: 38, `ipaddress.host.error.bracketed.conflicting.mask`: 51, `ipaddress.error.invalid.zone`: 54, `ipaddress.error.invalid.mask.address.empty`: 64, `ipaddress.host.error.segment.too.short`: 88, `ipaddress.error.empty.segment.at.index`: 97, `ipaddress.error.reverseRange`: 115, `ipaddress.host.error.invalid`: 133, `ipaddress.host.error.invalid.port.service`: 138, `ipaddress.error.invalid.size`: 25, } var strIndices = []int{ 0, 21, 153, 168, 187, 216, 288, 316, 365, 426, 452, 471, 484, 511, 562, 589, 725, 764, 876, 910, 929, 963, 1016, 1052, 1100, 1162, 1182, 1198, 1238, 1276, 1313, 1362, 1391, 1430, 1492, 1535, 1555, 1601, 1646, 1671, 1688, 1740, 1815, 1873, 1920, 1942, 1979, 1998, 2033, 2063, 2103, 2178, 2235, 2252, 2314, 2358, 2384, 2406, 2433, 2469, 2505, 2530, 2564, 2601, 2625, 2648, 2687, 2699, 2734, 2773, 2796, 2816, 2870, 2906, 2927, 2948, 2972, 3029, 3065, 3102, 3142, 3211, 3286, 3327, 3392, 3473, 3493, 3529, 3562, 3581, 3622, 3638, 3707, 3733, 3799, 3833, 3866, 3892, 3922, 3964, 3975, 3990, 4034, 4048, 4060, 4115, 4159, 4207, 4268, 4305, 4339, 4377, 4435, 4465, 4500, 4546, 4611, 4641, 4669, 4715, 4736, 4784, 4952, 4973, 5023, 5046, 5081, 5146, 5175, 5229, 5246, 5272, 5336, 5367, 5379, 5427, 5465, 5572, 5629, 5677, 5692, 5733, 5808, 6003, 6045, } var strVals = `service name is empty` + `splitting digits in range segments results in an invalid string (eg 12-22 becomes 1-2.2-2 which is 12 and 22 and nothing in between)` + `address is IPv4` + `invalid host length` + `empty host cannot be resolved` + `invalid format of IPv6 (ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff) address` + `address has too few segments` + `front address in range has an invalid digit count` + `MAC segment ranges cannot be converted to IPv6 segment ranges` + `segment too short at index` + `invalid octal digit` + `mask is empty` + `index exceeds prefix length` + `IP version of address must match IP version of mask` + `you must specify an address` + `invalid format of IP address, whether IPv4 (255.255.255.255) or IPv6 (ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff) or other supported format` + `IPv4 segment contains hexadecimal value` + `An IPv6 address cannot start with a single colon, it must start with either two colons or with the first segment` + `IPv4 address has too many segments` + `range start missing` + `requested version is indeterminate` + `invalid mix of mac address format characters at index` + `the universal address is not allowed` + `validation options do no allow wildcard segments` + `IPv4 segment ranges cannot be converted to IPv6 segment ranges` + `invalid address size` + `segment too long` + `IPv4 CIDR prefix length starts with zero` + `IPv4 address has invalid segment count` + `Address not within the assigned range` + `invalid address range, lower bound exceeds upper:` + `no iterator element to remove` + `validation options do no allow for port` + `options do not allow IPv4 address with less than four segments` + `No numeric value available for this address` + `invalid binary digit` + `validation options do no allow this mac format` + `Unable to convert version of argument address` + `the IP version must match` + `IP Address error:` + `validation options do no allow empty string for host` + `joining segments results in a joined segment that is not a sequential range` + `mask with single segment not allowed by validation options` + `validation options do no allow for service name` + `IPv4 segment too large` + `MAC address has invalid segment count` + `port value is empty` + `service name cannot end in a hyphen` + `a non-zero address is required` + `IPv6 CIDR prefix length starts with zero` + `the network prefix bit-length is negative or exceeds the address bit-length` + `conflicting masks inside and outside of bracketed address` + `address too large` + `invalid combination with earlier character at character number` + `invalid zone or scope id character at index:` + `host cannot be all numeric` + `negative address value` + `mismatched address bit size` + `validation options do not allow IPv4` + `a prefix-only address is not allowed` + `segment too long at index` + `invalid position of IPv6 separator` + `bracketed address missing end bracket` + `invalid character number` + `mask with empty address` + `with a zone you must specify an address` + `below range:` + `IPv4 address has invalid byte count` + `service name cannot start with a hyphen` + `host cannot be resolved` + `exceeds address size` + `address has too few segments or an invalid digit count` + `IPv6 compressed address is ambiguous` + `invalid decimal digit` + `service name too long` + `zone and prefix combined` + `Wildcards and ranges are not supported for IPv6 addresses` + `please supply a host, not a full URL` + `specify a mask or prefix but not both` + `please supply an address, not a full URL` + `ipv6 addresses must be surrounded by square brackets [] in host names` + `a special IP address with first segment larger than 255 cannot be used here` + `validation options do no allow mixed IPv6` + `validation options do no allow single character wildcard segments` + `cannot split ranged segment into smaller ranged segments spanning the same values` + `no ipv6 zone allowed` + `validation options do not allow IPv6` + `the number of segments must match` + `zero-length segment` + `MAC address cannot be converted to EUI 64` + `wildcard in mask` + `applying the mask results in a segment that is not a sequential range` + `mask is not a network mask` + `conflicting prefix lengths inside and outside of bracketed address` + `invalid encoding in zone at index:` + `Incompatible positions in address` + `invalid character at index` + `segment value missing at index` + `Address components have different networks` + `Host error:` + `invalid segment` + `invalid combination of characters in segment` + `network is nil` + `above range:` + `please supply an address, not a CIDR prefix length only` + `only ipv6 can be enclosed in square brackets` + `back address in range has an invalid digit count` + `single wildcards can appear only as the end of segment values` + `mask must specify a single IP address` + `MAC address has invalid byte count` + `invalid chars following mask at index:` + `Please specify either IPv4 or IPv6 addresses, but not both` + `segment value starts with zero` + `IPv6 address has invalid byte count` + `Section or grouping array contains a nil value` + `reversing a range of values does not result in a sequential range` + `bracketed address must be IPv6` + `invalid character in segment` + `validation options do not allow range segments` + `IPv6 zone not allowed` + `CIDR prefix or mask not allowed for this address` + `CIDR prefix must indicate the count of subnet bits, between 0 and 32 subnet bits for IP version 4 addresses and between 0 and 128 subnet bits for IP version 6 addresses` + `port number too large` + `Segments invalid due to inconsistent prefix values` + `invalid IP address type` + `only ipv6 can have a zone specified` + `range of joined segments cannot be divided into individual ranges` + `address has too many segments` + `in segment range, lower value must precede upper value` + `too many segments` + `Invalid index into address` + `Address is neither a CIDR prefix block nor an individual address` + `address mechanism not supported` + `invalid host` + `invalid format of IPv4 (255.255.255.255) address` + `IPv6 address has invalid segment count` + `An IPv6 address cannot end with a single colon, it must end with either two colons or with the last segment` + `specify the IP address separately from the mask or prefix` + `invalid port or service name character at index:` + `address is IPv6` + `validation options do no allow IP address` + `validation options do not allow you to specify a non-segmented single value` + `A mask must be a single IP address, while a CIDR prefix length must indicate the count of subnet bits, between 0 and 32 for IP version 4 addresses and between 0 and 128 for IP version 6 addresses` + `service name must have at least one letter` + `service name cannot have consecutive hyphens` func lookupStr(key string) (result string) { if index, ok := keyStrMap[key]; ok { start, end := strIndices[index], strIndices[index+1] result = strVals[start:end] } return } ipaddress-go-1.5.4/ipaddr/ipaddrstr.go000066400000000000000000001034271440250641600177010ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "strings" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) // NewIPAddressStringParams constructs an IPAddressString that will parse the given string according to the given parameters. func NewIPAddressStringParams(str string, params addrstrparam.IPAddressStringParams) *IPAddressString { var p addrstrparam.IPAddressStringParams if params == nil { p = defaultIPAddrParameters } else { p = addrstrparam.CopyIPAddressStringParams(params) } return parseIPAddressString(str, p) } // NewIPAddressString constructs an IPAddressString. func NewIPAddressString(str string) *IPAddressString { return parseIPAddressString(str, defaultIPAddrParameters) } func newIPAddressStringFromAddr(str string, addr *IPAddress) *IPAddressString { return &IPAddressString{ str: str, addressProvider: addr.getProvider(), } } func parseIPAddressString(str string, params addrstrparam.IPAddressStringParams) *IPAddressString { str = strings.TrimSpace(str) res := &IPAddressString{ str: str, } res.validate(params) return res } var validator hostIdentifierStringValidator = strValidator{} var defaultIPAddrParameters = new(addrstrparam.IPAddressStringParamsBuilder).ToParams() // // IPAddressString parses the string representation of an IP address. Such a string can represent just a single address like "1.2.3.4" or "1:2:3:4:6:7:8", or a subnet like "1.2.0.0/16" or "1.*.1-3.1-4" or "1111:222::/64". // // This supports a wide range of address string formats. It supports subnet formats, provides specific error messages, and allows more specific configuration. // // You can control all the supported formats using an IPAddressStringParamsBuilder to build a parameters instance of IPAddressStringParams. // When no IPAddressStringParams is supplied, a default instance of IPAddressStringParams is used that is generally permissive. // // Supported Formats // // Both IPv4 and IPv6 are supported. // // Subnets are supported: // • wildcards '*' and ranges '-' (for example "1.*.2-3.4"), useful for working with subnets // • the wildcard '*' can span multiple segments, so you can represent all addresses with '*', all IPv4 with '*.*', or all IPv6 with '*:*' // • SQL wildcards '%' and '_', although '%' is considered an SQL wildcard only when it is not considered an IPv6 zone indicator // • CIDR network prefix length addresses, like "1.2.0.0/16", which is equivalent to "1.2.*.*" (all-zero hosts are the full subnet, non-zero hosts are single addresses) // • address/mask pairs, in which the mask is applied to the address, like "1.2.3.4/255.255.0.0", which is also equivalent to "1.2.*.*" // // // You can combine these variations, such as "1.*.2-3.4/255.255.255.0". // // IPv6 is fully supported: // • IPv6 addresses like "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" // • IPv6 zones or scope identifiers, like "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff%zone" // • IPv6 mixed addresses are supported, which are addresses for which the last two IPv6 segments are represented as IPv4, like "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255" // • IPv6 compressed addresses like "::1" // • A single value of 32 hex digits like "00aa00bb00cc00dd00ee00ff00aa00bb" with or without a preceding hex delimiter "0x" // • A base 85 address comprising 20 base 85 digits like "4)+k&C#VzJ4br>0wv%Yp" as in RFC 1924 https://tools.ietf.org/html/rfc1924 // • Binary, preceded by "0b", either with binary segments that comprise all 16 bits like "::0b0000111100001111" or a single segment address of "0b" followed by 128 binary bits. // // // All of the above subnet variations work for IPv6, whether network prefix lengths, masks, ranges or wildcards. // Similarly, all the above subnet variations work for any supported IPv4 format, such as the standard dotted-decimal IPv4 format as well as the inet_aton formats listed below. // // This type support all address formats of the C routine inet_pton and the Java method java.net.InetAddress.getByName. // This type supports all IPv4 address formats of the C routine inet_aton as follows: // • IPv4 hex: "0x1.0x2.0x3.0x4" ("0x" prefix) // • IPv4 octal: "01.02.03.0234". Note this clashes with the same address interpreted as dotted decimal // • 3-part IPv4: "1.2.3" (which is interpreted as "1.2.0.3" (ie the third part covers the last two) // • 2-part IPv4: "1.2" (which is interpreted as "1.0.0.2" (ie the 2nd part covers the last 3) // • 1-part IPv4: "1" (which is interpreted as "0.0.0.1" (ie the number represents all 4 segments, and can be any number of digits less than the 32 digits which would be interpreted as IPv6) // • hex or octal variants of 1, 2, and 3 part, such as "0xffffffff" (which is interpreted as "255.255.255.255") // // Also supported are binary segments of a "0b" followed by binary digits like "0b1.0b1010.2.3", or a single segment address of "0b" followed by all 32 bits. // // inet_aton (and this type) allows mixing octal, hex and decimal (e.g. "0xa.11.013.11" which is equivalent to "11.11.11.11"). // String variations using prefixes, masks, ranges, and wildcards also work for inet_aton style. // The same can be said of binary segments, they can be mixed with all other formats. // // Note that there is ambiguity when supporting both inet_aton octal and dotted-decimal leading zeros, like "010.010.010.010" which can // be interpreted as octal or decimal, thus it can be either "8.8.8.8" or "10.10.10.10", with the default behaviour using the former interpretation. // This behaviour can be controlled by IPAddressStringParamsBuilder.GetIPv4AddressParamsBuilder and // IPv4AddressStringParametersBuilder.allowLeadingZeros(boolean) // // Some Additional Formats: // • empty strings are interpreted as the zero-address or the loopback // • as noted previously, the single wildcard address "*" represents all addresses both ipv4 and ipv6, // although you need to give it some help when converting to [IPAddress] by specifying the IP version in GetVersionedAddress(IPVersion) or ToVersionedAddress(IPVersion). // // If you have an address in which segments have been delimited with commas, such as "1,2.3.4,5.6", you can parse this with ParseDelimitedSegments(string) // which gives an iterator of strings. For "1,2.3.4,5.6" you will iterate through "1.3.4.6", "1.3.5.6", "2.3.4.6" and "2.3.5.6". // You can count the number of elements in such an iterator with CountDelimitedAddresses(String). // Each string can then be used to construct an IPAddressString. // // Usage // // Once you have constructed an IPAddressString object, you can convert it to an [IPAddress] object with various methods. // // Most address strings can be converted to an [IPAddress] object using GetAddress or ToAddress. In most cases the IP version is determined by the string itself. // // There are a few exceptions, cases in which the version is unknown or ambiguous, for which GetAddress returns nil: // • strings which do not represent valid addresses (eg "bla") // • the "all" address "*" which represents all IPv4 and IPv6 addresses. For this string you can provide the IPv4/IPv6 version to GetVersionedAddress to get an address representing either all IPv4 or all IPv6 addresses. // • empty string "" is interpreted as the zero-address, or optionally the default loopback address. You can provide the IPv4/IPv6 version to GetVersionedAddress to get the version of your choice. // // // The other exception is a subnet in which the range of values in a segment of the subnet are not sequential, for which ToAddress returns IncompatibleAddressError because there is no single [IPAddress] value, there would be many. // An [IPAddress] instance requires that all segments can be represented as a range of values. // // There are only two unusual circumstances when this can occur: // • using masks on subnets specified with wildcard or range characters causing non-sequential segments such as the final IPv4 segment of "0.0.0.*" with mask "0.0.0.128", // this example translating to the two addresses "0.0.0.0" and "0.0.0.128", so the last IPv4 segment cannot be represented as a sequential range of values. // • using wildcards or range characters in the IPv4 section of an IPv6 mixed address causing non-sequential segments such as the last IPv6 segment of "::ffff:0.0.*.0", // this example translating to the addresses "::ffff:0:100", "::ffff:0:200", "::ffff:0:300", ..., so the last IPv6 segment cannot be represented as a sequential range of values. // // These exceptions do not occur with non-subnets (ie individual addresses), nor can they occur with standard CIDR prefix-based subnets. // // This type is concurrency-safe. In fact, IPAddressString objects are immutable. // An IPAddressString object represents a single IP address representation that cannot be changed after construction. // Some derived state is created upon demand and cached, such as the derived [IPAddress] instances. // // This type has a few methods with analogs in [IPAddress], such as Contains, GetSequentialRange, // PrefixEqual, IsIPv4, and IsIPv6. // Such methods are provided to make creating the [IPAddress] instance unnecessary when no such [IPAddress] instance is needed for other reasons. type IPAddressString struct { str string addressProvider ipAddressProvider validateError addrerr.AddressStringError } func (addrStr *IPAddressString) init() *IPAddressString { if addrStr.addressProvider == nil && addrStr.validateError == nil { return zeroIPAddressString } return addrStr } // GetValidationOptions returns the validation options supplied when constructing this address string, // or the default options if no options were supplied. It returns nil if no options were used to construct. func (addrStr *IPAddressString) GetValidationOptions() addrstrparam.IPAddressStringParams { provider, _ := addrStr.getAddressProvider() if provider != nil { return provider.getParameters() } return nil } // IsPrefixed returns whether this address string has an associated prefix length. // If so, the prefix length is given by GetNetworkPrefixLen. func (addrStr *IPAddressString) IsPrefixed() bool { return addrStr.getNetworkPrefixLen() != nil } // GetNetworkPrefixLen returns the associated network prefix length. // // If this address is a valid address with an associated network prefix length then this returns that prefix length, otherwise returns nil. // The prefix length may be expressed explicitly with the notation "/xx" where xx is a decimal value, or it may be expressed implicitly as a network mask such as "/255.255.0.0". func (addrStr *IPAddressString) GetNetworkPrefixLen() PrefixLen { return addrStr.getNetworkPrefixLen().copy() } // If this address is a valid address with an associated network prefix length then this returns that prefix length, otherwise returns nil. // The prefix length may be expressed explicitly with the notation "/xx" where xx is a decimal value, or it may be expressed implicitly as a network mask such as "/255.255.0.0". func (addrStr *IPAddressString) getNetworkPrefixLen() PrefixLen { addrStr = addrStr.init() if addrStr.IsValid() { return addrStr.addressProvider.getProviderNetworkPrefixLen() } return nil } // GetMask returns the mask, if any, that was provided with this address string. func (addrStr *IPAddressString) GetMask() *IPAddress { addrStr = addrStr.init() if addrStr.IsValid() { return addrStr.addressProvider.getProviderMask() } return nil } // IsAllAddresses returns true if the string represents all IP addresses, such as the string "*". // You can denote all IPv4 addresses with *.*, or all IPv6 addresses with *:*. func (addrStr *IPAddressString) IsAllAddresses() bool { addrStr = addrStr.init() return addrStr.IsValid() && addrStr.addressProvider.isProvidingAllAddresses() } // IsEmpty returns true if the address string is empty (zero-length). func (addrStr *IPAddressString) IsEmpty() bool { addrStr = addrStr.init() return addrStr.IsValid() && addrStr.addressProvider.isProvidingEmpty() } // IsIPv4 returns true if the address is IPv4. func (addrStr *IPAddressString) IsIPv4() bool { addrStr = addrStr.init() return addrStr.IsValid() && addrStr.addressProvider.isProvidingIPv4() } // IsIPv6 returns true if the address is IPv6. func (addrStr *IPAddressString) IsIPv6() bool { addrStr = addrStr.init() return addrStr.IsValid() && addrStr.addressProvider.isProvidingIPv6() } // IsMixedIPv6 returns whether the lower 4 bytes of the address string are represented as IPv4, if this address string represents an IPv6 address. func (addrStr *IPAddressString) IsMixedIPv6() bool { addrStr = addrStr.init() return addrStr.IsIPv6() && addrStr.addressProvider.isProvidingMixedIPv6() } // IsBase85IPv6 returns whether this address string represents an IPv6 address, returns whether the string was base 85. func (addrStr *IPAddressString) IsBase85IPv6() bool { return addrStr.IsIPv6() && addrStr.addressProvider.isProvidingBase85IPv6() } // IsIPv4Mapped returns true if the address is an IPv4-mapped IPv6 address. func (addrStr *IPAddressString) IsIPv4Mapped() bool { return addrStr.IsIPv6() && ipv4MappedPrefix.PrefixEqual(addrStr) } // GetIPVersion returns the IP address version if this represents a valid IP address, otherwise it returns nil. func (addrStr *IPAddressString) GetIPVersion() IPVersion { if addrStr.IsValid() { return addrStr.addressProvider.getProviderIPVersion() } return IndeterminateIPVersion } // IsLoopback returns whether this address string represents a loopback address, such as "::1" or "127.0.0.1". func (addrStr *IPAddressString) IsLoopback() bool { val := addrStr.GetAddress() return val != nil && val.IsLoopback() } // IsZero returns whether this string represents an IP address whose value is exactly zero. func (addrStr *IPAddressString) IsZero() bool { value := addrStr.GetAddress() return value != nil && value.IsZero() } // String implements the [fmt.Stringer] interface, // returning the original string used to create this IPAddressString (altered by strings.TrimSpace), // or "" if the receiver is a nil pointer. func (addrStr *IPAddressString) String() string { if addrStr == nil { return nilString() } return addrStr.str } // Format implements the [fmt.Formatter] interface. // It accepts the verbs hat are applicable to strings, // namely the verbs %s, %q, %x and %X. func (addrStr IPAddressString) Format(state fmt.State, verb rune) { s := flagsFromState(state, verb) _, _ = state.Write([]byte(fmt.Sprintf(s, addrStr.str))) } // ToNormalizedString produces a normalized string for the address. // // For IPv4, it is the same as the canonical string. // // For IPv6, it differs from the canonical string. Zero-segments are not compressed. // // If the address has a prefix length, it will be included in the string. // // If the original string is not a valid address string, the original string is used. func (addrStr *IPAddressString) ToNormalizedString() string { addrStr = addrStr.init() if addrStr.IsValid() { if str, err := addrStr.toNormalizedString(addrStr.addressProvider); err == nil { return str } } return addrStr.String() } func (addrStr *IPAddressString) toNormalizedString(addressProvider ipAddressProvider) (result string, err addrerr.IncompatibleAddressError) { if addressProvider.isProvidingAllAddresses() { result = SegmentWildcardStr } else if addressProvider.isProvidingEmpty() { result = "" } else if addressProvider.isProvidingIPAddress() { var addr *IPAddress if addr, err = addressProvider.getProviderAddress(); err == nil { result = addr.ToNormalizedString() } } return } // IsValid returns whether this is a valid IP address string format. // The accepted IP address formats are: // an IPv4 address or subnet, an IPv6 address or subnet, the address representing all addresses of both versions, or an empty string. // If this method returns false, and you want more details, call Validate and examine the error. func (addrStr *IPAddressString) IsValid() bool { return addrStr.Validate() == nil } // GetAddress returns the IP address if this IPAddressString is a valid string representing an IP address or subnet. Otherwise, it returns nil. // // Use ToAddress for an equivalent method that returns an error when the format is invalid. // // If you have a prefixed address and you wish to get only the host without the prefix, use GetHostAddress. func (addrStr *IPAddressString) GetAddress() *IPAddress { addr, _ := addrStr.ToAddress() return addr } // ToAddress produces the IPAddress corresponding to this IPAddressString. // // If this object does not represent a specific IPAddress or a subnet, nil is returned. // // If the string used to construct this object is not a known format (empty string, address, or range of addresses) then this method returns an error. // // An equivalent method that does not return the error is GetAddress. // // If you have a prefixed address and you wish to get only the host rather than the address with the prefix, use ToHostAddress. // // The error can be addrerr.AddressStringError or addrerr.IncompatibleAddressError func (addrStr *IPAddressString) ToAddress() (*IPAddress, addrerr.AddressError) { provider, err := addrStr.getAddressProvider() if err != nil { return nil, err } return provider.getProviderAddress() } // GetVersionedAddress is similar to ToVersionedAddress, but returns nil rather than an error when the address is invalid or does not match the supplied version. func (addrStr *IPAddressString) GetVersionedAddress(version IPVersion) *IPAddress { addr, _ := addrStr.ToVersionedAddress(version) return addr } // ToVersionedAddress Produces the IPAddress of the specified address version corresponding to this IPAddressString. // // In most cases the string indicates the address version and calling ToAddress() is sufficient, with a few exceptions. // // When this object represents only a network prefix length, // specifying the address version allows the conversion to take place to the associated mask for that prefix length. // // When this object represents all addresses, specifying the address version allows the conversion to take place // to the associated representation of all IPv4 or all IPv6 addresses. // // When this object represents the empty string and that string is interpreted as a loopback or zero address, then it returns // the corresponding address for the given version. // // When this object represents an ipv4 or ipv6 address, it returns that address if and only if that address matches the provided version. // // If the string used to construct this object is an invalid format, // or a format that does not match the provided version, then an error is returned. func (addrStr *IPAddressString) ToVersionedAddress(version IPVersion) (*IPAddress, addrerr.AddressError) { provider, err := addrStr.getAddressProvider() if err != nil { return nil, err } return provider.getVersionedAddress(version) } // GetHostAddress parses the address while ignoring the prefix length or mask. // GetHostAddress returns nil for an invalid string. If you wish to receive an error instead, use ToHostAddress. func (addrStr *IPAddressString) GetHostAddress() *IPAddress { addr, _ := addrStr.ToHostAddress() return addr } // ToHostAddress parses the address while ignoring the prefix length or mask. // The error can be addrerr.AddressStringError for invalid strings or addrerr.IncompatibleAddressError. // GetHostAddress is similar but does not return errors. // Standard address formats do not result in errors. func (addrStr *IPAddressString) ToHostAddress() (*IPAddress, addrerr.AddressError) { provider, err := addrStr.getAddressProvider() if err != nil { return nil, err } return provider.getProviderHostAddress() } // TODO getDivisionGrouping: allows for isSequential //// IsSequential returns whether the addresses returned by this IPAddressString are sequential, //// meaning that if any address has a numerical value that lies in between the numerical values of two addresses represented by this IPAddressString, //// then that address is also represented by this IPAddressString. In other words, the range of addresses is sequential. //// //// When the IPAddressString is sequential, it can be represented exactly by the IPAddressSeqRange returned from GetSequentialRange. //// In some cases, no IPAddress instance can be obtained from GetAddress or ToAddress, //// but if the IPAddressString is sequential, you can obtain a IPAddressSeqRange to represent the IPAddressString instead. //func (addrStr *IPAddressString) IsSequential() bool { // addrStr = addrStr.init() // return addrStr.IsValid() && addrStr.addressProvider.isSequential() //} // //// Also restore this part of the godoc for GetSequentialRange below once IsSequential is back: //// //// The sequential range matches the same set of addresses as the address string or the address when IsSequential is true. //// Otherwise, the range includes addresses not specified by the address string. //// // GetSequentialRange returns the range of sequential addresses from the lowest address specified in this address string to the highest. // // Since not all IPAddressString instances describe a sequential series of addresses, // this does not necessarily match the exact set of addresses specified by the string. // For example, "1-2.3.4.1-2" produces the sequential range "1.3.4.1" to "2.3.4.2" that includes the address "1.255.255.2" not specified by the string. // // This method can also produce a range for a string for which no IPAddress instance can be created, // those cases where IsValid returns true but ToAddress returns addrerr.IncompatibleAddressError and GetAddress returns nil. // The range cannot be produced for the other cases where GetAddress returns nil // // This is similar to ToSequentialRange except that ToSequentialRange provides a descriptive error when nil is returned. func (addrStr *IPAddressString) GetSequentialRange() (res *IPAddressSeqRange) { res, _ = addrStr.ToSequentialRange() return } // ToSequentialRange returns the range of sequential addresses from the lowest address specified in this address string to the highest. // // This is similar to GetSequentialRange except that this method provides a descriptive error when nil is returned. See GetSequentialRange for more details. func (addrStr *IPAddressString) ToSequentialRange() (*IPAddressSeqRange, addrerr.AddressStringError) { provider, err := addrStr.getAddressProvider() if err != nil { return nil, err } return provider.getProviderSeqRange(), nil } // ValidateIPv4 validates that this string is a valid IPv4 address, returning nil, and if not, returns an error with a descriptive message indicating why it is not. func (addrStr *IPAddressString) ValidateIPv4() addrerr.AddressStringError { return addrStr.ValidateVersion(IPv4) } // ValidateIPv6 validates that this string is a valid IPv6 address, returning nil, and if not, returns an error with a descriptive message indicating why it is not. func (addrStr *IPAddressString) ValidateIPv6() addrerr.AddressStringError { return addrStr.ValidateVersion(IPv6) } func (addrStr *IPAddressString) getAddressProvider() (ipAddressProvider, addrerr.AddressStringError) { addrStr = addrStr.init() err := addrStr.Validate() return addrStr.addressProvider, err } func (addrStr *IPAddressString) validate(validationOptions addrstrparam.IPAddressStringParams) { addrStr.addressProvider, addrStr.validateError = validator.validateIPAddressStr(addrStr, validationOptions) } // Validate validates that this string is a valid IP address, returning nil, and if not, returns an error with a descriptive message indicating why it is not. func (addrStr *IPAddressString) Validate() addrerr.AddressStringError { return addrStr.init().validateError } // ValidateVersion validates that this string is a valid IP address of the given version. // If it is, it returns nil, otherwise it returns an error with a descriptive message indicating why it is not. func (addrStr *IPAddressString) ValidateVersion(version IPVersion) addrerr.AddressStringError { addrStr = addrStr.init() err := addrStr.Validate() if err != nil { return err } else if version.IsIndeterminate() { return &addressStringError{addressError{str: addrStr.str, key: "ipaddress.error.ipVersionIndeterminate"}} } else { addrVersion := addrStr.addressProvider.getProviderIPVersion() if version.IsIPv4() { if addrVersion.IsIPv6() { return &addressStringError{addressError{str: addrStr.str, key: "ipaddress.error.address.is.ipv6"}} } } else if version.IsIPv6() { if addrVersion.IsIPv4() { return &addressStringError{addressError{str: addrStr.str, key: "ipaddress.error.address.is.ipv4"}} } } } return nil } // Compare compares this address string with another, // returning a negative number, zero, or a positive number if this address string is less than, equal to, or greater than the other. // // All address strings are comparable. If two address strings are invalid, their strings are compared. // Otherwise, address strings are compared according to which type or version of string, and then within each type or version // they are compared using the comparison rules for addresses. func (addrStr *IPAddressString) Compare(other *IPAddressString) int { if addrStr == other { return 0 } else if addrStr == nil { return -1 } else if other == nil { return 1 } addrStr = addrStr.init() other = other.init() if addrStr == other { return 0 } if addrStr.IsValid() { if other.IsValid() { if res, err := addrStr.addressProvider.providerCompare(other.addressProvider); err == nil { return res } // one or the other is nil, either empty or IncompatibleAddressException return strings.Compare(addrStr.String(), other.String()) } return 1 } else if other.IsValid() { return -1 } return strings.Compare(addrStr.String(), other.String()) } // PrefixEqual is similar to Equal, but instead returns whether the prefix of this address string matches the same of the given address string, // using the prefix length of this address string. It returns whether the argument address string has the same address prefix values as this. // // In other words, it determines if the other address has the same prefix subnet using the prefix length of this address. // // If an address has no prefix length, the whole address is compared. // // If this address string or the given address string is invalid, it returns false. func (addrStr *IPAddressString) PrefixEqual(other *IPAddressString) bool { // getting the prefix addrStr = addrStr.init() other = other.init() if other == addrStr { return true } else if !addrStr.IsValid() { return false } else if other.IsValid() { directResult := addrStr.addressProvider.prefixEqualsProvider(other.addressProvider) if directResult.isSet { return directResult.val } thisAddress := addrStr.GetAddress() if thisAddress != nil { otherAddress := other.GetAddress() if otherAddress != nil { return thisAddress.prefixEquals(otherAddress) } } // one or both addresses are nil, so there is no prefix to speak of } return false } // PrefixContains is similar to PrefixEqual, but instead returns whether the prefix of this address contains the same of the given address, // using the prefix length of this address. It returns whether the argument address string prefix values of that length are also prefix values in this address string. // // In other words, determines if the other address is in one of the same prefix subnets using the prefix length of this address. // // If an address has no prefix length, the whole address is used as the prefix. // // If this address string or the given address string is invalid, it returns false. func (addrStr *IPAddressString) PrefixContains(other *IPAddressString) bool { addrStr = addrStr.init() other = other.init() if other == addrStr { return true } else if !addrStr.IsValid() { return false } else if other.IsValid() { directResult := addrStr.addressProvider.prefixContainsProvider(other.addressProvider) if directResult.isSet { return directResult.val } thisAddress := addrStr.GetAddress() if thisAddress != nil { otherAddress := other.GetAddress() if otherAddress != nil { return thisAddress.prefixContains(otherAddress) } } // one or both addresses are nil, so there is no prefix to speak of } return false } // Contains returns whether the address or subnet identified by this address string contains the address or subnet identified by the given string. // If this address string or the given address string is invalid then Contains returns false. func (addrStr *IPAddressString) Contains(other *IPAddressString) bool { addrStr = addrStr.init() other = other.init() if addrStr.IsValid() { if other == addrStr { return true } if other.IsValid() { // note the quick result also handles the case of "all addresses" directResult := addrStr.addressProvider.containsProvider(other.addressProvider) if directResult.isSet { return directResult.val } // defer to the constructed addresses addr := addrStr.GetAddress() if addr != nil { otherAddress := other.GetAddress() if otherAddress != nil { return addr.Contains(otherAddress) } } } } return false } // Equal compares two IP address strings for equality. // Two IPAddressString objects are equal if they represent the same set of addresses. // Whether one or the other has an associated network prefix length is not considered. // // If an IPAddressString is invalid, it is equal to another address only if the other address was constructed from the same string. func (addrStr *IPAddressString) Equal(other *IPAddressString) bool { if addrStr == nil { return other == nil } else if other == nil { return false } addrStr = addrStr.init() other = other.init() if other == addrStr { return true } // if they have the same string, they must be the same, // but the converse is not true, if they have different strings, they can // still be the same because IPv6 addresses have many representations // and additional things like leading zeros can have an effect for IPv4 // Also note that we do not call equals() on the validation options, this is intended as an optimization, // and probably better to avoid going through all the validation objects here stringsMatch := addrStr.String() == other.String() if stringsMatch && addrStr.GetValidationOptions() == other.GetValidationOptions() { return true } if addrStr.IsValid() { if other.IsValid() { directResult := addrStr.addressProvider.parsedEquals(other.addressProvider) if directResult.isSet { return directResult.val } // When a value provider produces no value, equality and comparison are based on the enum ipType var err addrerr.AddressError addrProvider, err := addrStr.getAddressProvider() if err != nil { return stringsMatch } equals, err := addrProvider.providerEquals(other.addressProvider) if err != nil { return stringsMatch } return equals } } else if !other.IsValid() { return stringsMatch // Two invalid addresses are not equal unless strings match, regardless of validation options } return false } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // If the address string has prefix length 0 and represents all addresses of the same version, // and the prefix length is being decreased, then the address representing all addresses of any version is returned. // // When there is an associated address value and the prefix length is increased, the bits moved within the prefix become zero, // and if prefix length is extended beyond the segment series boundary, it is removed. // When there is an associated address value and the prefix length is decreased, the bits moved outside the prefix become zero. // // If the address string represents a prefix block, then the result will also represent a prefix block. func (addrStr *IPAddressString) AdjustPrefixLen(adjustment BitCount) (*IPAddressString, addrerr.IncompatibleAddressError) { address := addrStr.GetAddress() if address == nil { return nil, nil } if adjustment == 0 && addrStr.IsPrefixed() { return addrStr, nil } prefix := address.getNetworkPrefixLen() isPrefBlock := address.IsPrefixBlock() var addr *IPAddress var err addrerr.IncompatibleAddressError if adjustment < 0 && isPrefBlock { if prefix != nil && prefix.bitCount()+adjustment < 0 { return NewIPAddressStringParams(SegmentWildcardStr, addrStr.GetValidationOptions()), nil } addr, err = address.AdjustPrefixLenZeroed(adjustment) if err != nil { return nil, err } addr = addr.ToPrefixBlock() } else { addr, err = address.AdjustPrefixLenZeroed(adjustment) if err != nil { return nil, err } } return addr.ToAddressString(), nil } // Wrap wraps this address string, returning a WrappedIPAddressString as an implementation of ExtendedIdentifierString, // which can be used to write code that works with different host identifier types polymorphically, including IPAddressString, MACAddressString, and HostName. func (addrStr *IPAddressString) Wrap() ExtendedIdentifierString { return WrappedIPAddressString{addrStr} } // ValidatePrefixLenStr validates that the string represents a valid prefix length, such as "24". // The string should not include a beginning '/' character. // If invalid, it returns an error with an appropriate message. // You can specify the IP version or IndeterminateIPVersion if unknown. // An error is returned if the format is invalid. func ValidatePrefixLenStr(str string, version IPVersion) (prefixLen PrefixLen, err addrerr.AddressStringError) { return validator.validatePrefixLenStr(str, version) } ipaddress-go-1.5.4/ipaddr/ipsection.go000066400000000000000000003207601440250641600177030ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math/big" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) func createIPSection(segments []*AddressDivision, prefixLength PrefixLen, addrType addrType) *IPAddressSection { sect := &IPAddressSection{ ipAddressSectionInternal{ addressSectionInternal{ addressDivisionGroupingInternal{ addressDivisionGroupingBase: addressDivisionGroupingBase{ divisions: standardDivArray(segments), addrType: addrType, cache: &valueCache{}, prefixLength: prefixLength, }, }, }, }, } assignStringCache(§.addressDivisionGroupingBase, addrType) return sect } func createIPSectionFromSegs(isIPv4 bool, orig []*IPAddressSegment, prefLen PrefixLen) (result *IPAddressSection) { segProvider := func(index int) *IPAddressSegment { return orig[index] } var divs []*AddressDivision var newPref PrefixLen var isMultiple bool if isIPv4 { divs, newPref, isMultiple = createDivisionsFromSegs( segProvider, len(orig), ipv4BitsToSegmentBitshift, IPv4BitsPerSegment, IPv4BytesPerSegment, IPv4MaxValuePerSegment, zeroIPv4Seg.ToIP(), zeroIPv4SegZeroPrefix.ToIP(), zeroIPv4SegPrefixBlock.ToIP(), prefLen) result = createIPv4Section(divs).ToIP() } else { divs, newPref, isMultiple = createDivisionsFromSegs( segProvider, len(orig), ipv6BitsToSegmentBitshift, IPv6BitsPerSegment, IPv6BytesPerSegment, IPv6MaxValuePerSegment, zeroIPv6Seg.ToIP(), zeroIPv6SegZeroPrefix.ToIP(), zeroIPv6SegPrefixBlock.ToIP(), prefLen) result = createIPv6Section(divs).ToIP() } result.prefixLength = newPref result.isMult = isMultiple return result } // Callers to this function have already initialized the segments to have consistent prefix lengths, // but in here we need to determine what that prefix length might be. func deriveIPAddressSection(from *IPAddressSection, segments []*AddressDivision) (res *IPAddressSection) { res = createIPSection(segments, nil, from.getAddrType()) res.initMultAndPrefLen() return } // Callers to this function have already initialized the segments to have prefix lengths corresponding to the supplied argument prefixLength // So we need only check if multiple and assign the prefix length. func deriveIPAddressSectionPrefLen(from *IPAddressSection, segments []*AddressDivision, prefixLength PrefixLen) (res *IPAddressSection) { res = createIPSection(segments, prefixLength, from.getAddrType()) res.initMultiple() return } // // // // type ipAddressSectionInternal struct { addressSectionInternal } // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. func (section *ipAddressSectionInternal) GetSegment(index int) *IPAddressSegment { return section.getDivision(index).ToIP() } // ForEachSegment visits each segment in order from most-significant to least, the most significant with index 0, calling the given function for each, terminating early if the function returns true. // Returns the number of visited segments. func (section *ipAddressSectionInternal) ForEachSegment(consumer func(segmentIndex int, segment *IPAddressSegment) (stop bool)) int { divArray := section.getDivArray() if divArray != nil { for i, div := range divArray { if consumer(i, div.ToIP()) { return i + 1 } } } return len(divArray) } // GetIPVersion returns the IP version of this IP address section. func (section *ipAddressSectionInternal) GetIPVersion() IPVersion { addrType := section.getAddrType() if addrType.isIPv4() { return IPv4 } else if addrType.isIPv6() { return IPv6 } return IndeterminateIPVersion } func (section *ipAddressSectionInternal) getNetworkPrefixLen() PrefixLen { return section.prefixLength } // GetNetworkPrefixLen returns the prefix length, or nil if there is no prefix length. It is equivalent to GetPrefixLen. // // A prefix length indicates the number of bits in the initial part of the address item that comprises the prefix. // // A prefix is a part of the address item that is not specific to that address but common amongst a group of such items, such as a CIDR prefix block subnet. func (section *ipAddressSectionInternal) GetNetworkPrefixLen() PrefixLen { return section.getNetworkPrefixLen().copy() } // GetBlockMaskPrefixLen returns the prefix length if this address section is equivalent to the mask for a CIDR prefix block. // Otherwise, it returns nil. // A CIDR network mask is an address section with all ones in the network section and then all zeros in the host section. // A CIDR host mask is an address section with all zeros in the network section and then all ones in the host section. // The prefix length is the bit-length of the network section. // // Also, keep in mind that the prefix length returned by this method is not equivalent to the prefix length of this instance, // indicating the network and host section of this address section. // The prefix length returned here indicates the whether the value of this address can be used as a mask for the network and host // section of any other address. Therefore the two values can be different values, or one can be nil while the other is not. // // This method applies only to the lower value of the range if this section represents multiple values. func (section *ipAddressSectionInternal) GetBlockMaskPrefixLen(network bool) PrefixLen { cache := section.cache if cache == nil { return nil // no prefix } cachedMaskLens := (*maskLenSetting)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.cachedMaskLens)))) if cachedMaskLens == nil { networkMaskLen, hostMaskLen := section.checkForPrefixMask() cachedMaskLens = &maskLenSetting{networkMaskLen, hostMaskLen} dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.cachedMaskLens)) atomicStorePointer(dataLoc, unsafe.Pointer(cachedMaskLens)) } if network { return cachedMaskLens.networkMaskLen } return cachedMaskLens.hostMaskLen } func (section *ipAddressSectionInternal) checkForPrefixMask() (networkMaskLen, hostMaskLen PrefixLen) { count := section.GetSegmentCount() if count == 0 { return } firstSeg := section.GetSegment(0) checkingNetworkFront, checkingHostFront := true, true var checkingNetworkBack, checkingHostBack bool var prefixedSeg int prefixedSegPrefixLen := BitCount(0) maxVal := firstSeg.GetMaxValue() for i := 0; i < count; i++ { seg := section.GetSegment(i) val := seg.GetSegmentValue() if val == 0 { if checkingNetworkFront { prefixedSeg = i checkingNetworkFront, checkingNetworkBack = false, true } else if !checkingHostFront && !checkingNetworkBack { return } checkingHostBack = false } else if val == maxVal { if checkingHostFront { prefixedSeg = i checkingHostFront, checkingHostBack = false, true } else if !checkingHostBack && !checkingNetworkFront { return } checkingNetworkBack = false } else { segNetworkMaskLen, segHostMaskLen := seg.checkForPrefixMask() if segNetworkMaskLen != nil { if checkingNetworkFront { prefixedSegPrefixLen = segNetworkMaskLen.bitCount() checkingNetworkBack = true checkingHostBack = false prefixedSeg = i } else { return } } else if segHostMaskLen != nil { if checkingHostFront { prefixedSegPrefixLen = segHostMaskLen.bitCount() checkingHostBack = true checkingNetworkBack = false prefixedSeg = i } else { return } } else { return } checkingNetworkFront, checkingHostFront = false, false } } if checkingNetworkFront { // all ones networkMaskLen = cacheBitCount(section.GetBitCount()) hostMaskLen = cacheBitCount(0) } else if checkingHostFront { // all zeros hostMaskLen = cacheBitCount(section.GetBitCount()) networkMaskLen = cacheBitCount(0) } else if checkingNetworkBack { // ending in zeros, network mask networkMaskLen = getNetworkPrefixLen(firstSeg.GetBitCount(), prefixedSegPrefixLen, prefixedSeg) } else if checkingHostBack { // ending in ones, host mask hostMaskLen = getNetworkPrefixLen(firstSeg.GetBitCount(), prefixedSegPrefixLen, prefixedSeg) } return } // IncludesZeroHost returns whether the address section contains an individual address section with a host of zero. If the address section has no prefix length it returns false. // If the prefix length matches the bit count, then it returns true. // // Otherwise, it checks whether it contains an individual address section for which all bits past the prefix are zero. func (section *ipAddressSectionInternal) IncludesZeroHost() bool { networkPrefixLength := section.getPrefixLen() return networkPrefixLength != nil && section.IncludesZeroHostLen(networkPrefixLength.bitCount()) } // IncludesZeroHostLen returns whether the address section contains an individual section with a host of zero, a section for which all bits past the given prefix length are zero. func (section *ipAddressSectionInternal) IncludesZeroHostLen(networkPrefixLength BitCount) bool { networkPrefixLength = checkSubnet(section, networkPrefixLength) bitsPerSegment := section.GetBitsPerSegment() bytesPerSegment := section.GetBytesPerSegment() prefixedSegmentIndex := getHostSegmentIndex(networkPrefixLength, bytesPerSegment, bitsPerSegment) divCount := section.GetSegmentCount() for i := prefixedSegmentIndex; i < divCount; i++ { div := section.GetSegment(i) segmentPrefixLength := getPrefixedSegmentPrefixLength(bitsPerSegment, networkPrefixLength, i) mask := div.GetSegmentHostMask(segmentPrefixLength.bitCount()) if (mask & div.GetSegmentValue()) != 0 { return false } for i++; i < divCount; i++ { div = section.GetSegment(i) if !div.includesZero() { return false } } } return true } // IncludesMaxHost returns whether the address section contains an individual address section with a host of all one-bits. If the address section has no prefix length it returns false. // If the prefix length matches the bit count, then it returns true. // // Otherwise, it checks whether it contains an individual address section for which all bits past the prefix are one. func (section *ipAddressSectionInternal) IncludesMaxHost() bool { networkPrefixLength := section.getPrefixLen() return networkPrefixLength != nil && section.IncludesMaxHostLen(networkPrefixLength.bitCount()) } // IncludesMaxHostLen returns whether the address section contains an individual address section with a host of all one-bits, an address section for which all bits past the given prefix length are all ones. func (section *ipAddressSectionInternal) IncludesMaxHostLen(networkPrefixLength BitCount) bool { networkPrefixLength = checkSubnet(section, networkPrefixLength) bitsPerSegment := section.GetBitsPerSegment() bytesPerSegment := section.GetBytesPerSegment() prefixedSegmentIndex := getHostSegmentIndex(networkPrefixLength, bytesPerSegment, bitsPerSegment) divCount := section.GetSegmentCount() for i := prefixedSegmentIndex; i < divCount; i++ { div := section.GetSegment(i) segmentPrefixLength := getPrefixedSegmentPrefixLength(bitsPerSegment, networkPrefixLength, i) if segmentPrefixLength != nil { mask := div.GetSegmentHostMask(segmentPrefixLength.bitCount()) if (mask & div.getUpperSegmentValue()) != mask { return false } for i++; i < divCount; i++ { div = section.GetSegment(i) if !div.includesMax() { return false } } } } return true } func (section *ipAddressSectionInternal) toZeroHost(boundariesOnly bool) (res *IPAddressSection, err addrerr.IncompatibleAddressError) { segmentCount := section.GetSegmentCount() if segmentCount == 0 { return section.toIPAddressSection(), nil } var prefLen BitCount if section.isPrefixed() { prefLen = section.getPrefixLen().bitCount() } if section.IsZeroHostLen(prefLen) { return section.toIPAddressSection(), nil } if section.IncludesZeroHost() && section.IsSingleNetwork() { res = section.getLower().ToIP() //cached return } if !section.isPrefixed() { mask := section.addrType.getIPNetwork().GetPrefixedNetworkMask(0) res = mask.GetSubSection(0, segmentCount) return } return section.createZeroHost(prefLen, boundariesOnly) } // boundariesOnly: whether we care if the masking works for all values in a range. // For instance, 1.2.3.2-4/31 cannot be zero-hosted, because applyng to the boundaries results in 1.2.3.2-4/31, // and that includes 1.2.3.3/31 which does not have host of zero. // So in that case, we'd normally haveaddrerr.IncompatibleAddressError. boundariesOnly as true avoids the exception, // if we are really just interested in getting the zero-host boundaries, // and we don't care about the remaining values in-between. func (section *ipAddressSectionInternal) createZeroHost(prefLen BitCount, boundariesOnly bool) (*IPAddressSection, addrerr.IncompatibleAddressError) { mask := section.addrType.getIPNetwork().GetNetworkMask(prefLen) return section.getSubnetSegments( getNetworkSegmentIndex(prefLen, section.GetBytesPerSegment(), section.GetBitsPerSegment()), cacheBitCount(prefLen), !boundariesOnly, //verifyMask section.getDivision, func(i int) SegInt { return mask.GetSegment(i).GetSegmentValue() }) } func (section *ipAddressSectionInternal) toZeroHostLen(prefixLength BitCount) (*IPAddressSection, addrerr.IncompatibleAddressError) { var minIndex int if section.isPrefixed() { existingPrefLen := section.getNetworkPrefixLen().bitCount() if prefixLength == existingPrefLen { return section.toZeroHost(false) } if prefixLength < existingPrefLen { minIndex = getNetworkSegmentIndex(prefixLength, section.GetBytesPerSegment(), section.GetBitsPerSegment()) } else { minIndex = getNetworkSegmentIndex(existingPrefLen, section.GetBytesPerSegment(), section.GetBitsPerSegment()) } } else { minIndex = getNetworkSegmentIndex(prefixLength, section.GetBytesPerSegment(), section.GetBitsPerSegment()) } mask := section.addrType.getIPNetwork().GetNetworkMask(prefixLength) return section.getSubnetSegments( minIndex, nil, // intentionally no prefix length true, section.getDivision, func(i int) SegInt { return mask.GetSegment(i).GetSegmentValue() }) } func (section *ipAddressSectionInternal) toZeroNetwork() *IPAddressSection { segmentCount := section.GetSegmentCount() if segmentCount == 0 { return section.toIPAddressSection() } if !section.isPrefixed() { mask := section.addrType.getIPNetwork().GetHostMask(section.GetBitCount()) return mask.GetSubSection(0, segmentCount) } return section.createZeroNetwork() } func (section *ipAddressSectionInternal) createZeroNetwork() *IPAddressSection { prefixLength := section.getNetworkPrefixLen() // we know it is prefixed here so no panic on the derefence mask := section.addrType.getIPNetwork().GetHostMask(prefixLength.bitCount()) res, _ := section.getSubnetSegments( 0, prefixLength, false, section.getDivision, func(i int) SegInt { return mask.GetSegment(i).GetSegmentValue() }) return res } func (section *ipAddressSectionInternal) toMaxHost() (res *IPAddressSection, err addrerr.IncompatibleAddressError) { segmentCount := section.GetSegmentCount() if segmentCount == 0 { return section.toIPAddressSection(), nil } if !section.isPrefixed() { mask := section.addrType.getIPNetwork().GetPrefixedHostMask(0) res = mask.GetSubSection(0, segmentCount) return } if section.IsMaxHostLen(section.getPrefixLen().bitCount()) { return section.toIPAddressSection(), nil } if section.IncludesMaxHost() && section.IsSingleNetwork() { return section.getUpper().ToIP(), nil // cached } return section.createMaxHost() } func (section *ipAddressSectionInternal) createMaxHost() (*IPAddressSection, addrerr.IncompatibleAddressError) { prefixLength := section.getNetworkPrefixLen() // we know it is prefixed here so no panic on the derefence mask := section.addrType.getIPNetwork().GetHostMask(prefixLength.bitCount()) return section.getOredSegments( prefixLength, true, section.getDivision, func(i int) SegInt { return mask.GetSegment(i).GetSegmentValue() }) } func (section *ipAddressSectionInternal) toMaxHostLen(prefixLength BitCount) (*IPAddressSection, addrerr.IncompatibleAddressError) { if section.isPrefixed() && prefixLength == section.getNetworkPrefixLen().bitCount() { return section.toMaxHost() } mask := section.addrType.getIPNetwork().GetHostMask(prefixLength) return section.getOredSegments( nil, true, section.getDivision, func(i int) SegInt { return mask.GetSegment(i).GetSegmentValue() }) } // IsSingleNetwork returns whether the network section of the address, the prefix, consists of a single value. // // If it has no prefix length, it returns true if not multiple, if it contains only a single individual address section. func (section *ipAddressSectionInternal) IsSingleNetwork() bool { networkPrefixLength := section.getNetworkPrefixLen() if networkPrefixLength == nil { return !section.isMultiple() } prefLen := networkPrefixLength.bitCount() if prefLen >= section.GetBitCount() { return !section.isMultiple() } bitsPerSegment := section.GetBitsPerSegment() prefixedSegmentIndex := getNetworkSegmentIndex(prefLen, section.GetBytesPerSegment(), bitsPerSegment) if prefixedSegmentIndex < 0 { return true } for i := 0; i < prefixedSegmentIndex; i++ { if section.getDivision(i).isMultiple() { return false } } div := section.GetSegment(prefixedSegmentIndex) divPrefLen := getPrefixedSegmentPrefixLength(bitsPerSegment, prefLen, prefixedSegmentIndex) shift := bitsPerSegment - divPrefLen.bitCount() return (div.GetSegmentValue() >> uint(shift)) == (div.GetUpperSegmentValue() >> uint(shift)) } // IsMaxHost returns whether this section has a prefix length and if so, // whether the host is all all one-bits, the max value, for all individual sections in this address section. // // If the host section is zero length (there are zero host bits), IsMaxHost returns true. func (section *ipAddressSectionInternal) IsMaxHost() bool { if !section.isPrefixed() { return false } return section.IsMaxHostLen(section.getNetworkPrefixLen().bitCount()) } // IsMaxHostLen returns whether the host host is all one-bits, the max value, for all individual sections in this address section, // for the given prefix length, the host being the bits following the prefix. // // If the host section is zero length (there are zero host bits), IsMaxHostLen returns true. func (section *ipAddressSectionInternal) IsMaxHostLen(prefLen BitCount) bool { divCount := section.GetSegmentCount() if divCount == 0 { return true } else if prefLen < 0 { prefLen = 0 } bytesPerSegment := section.GetBytesPerSegment() bitsPerSegment := section.GetBitsPerSegment() // Note: 1.2.3.4/32 has a max host prefixedSegmentIndex := getHostSegmentIndex(prefLen, bytesPerSegment, bitsPerSegment) if prefixedSegmentIndex < divCount { segmentPrefixLength := getPrefixedSegmentPrefixLength(bitsPerSegment, prefLen, prefixedSegmentIndex) i := prefixedSegmentIndex div := section.GetSegment(i) mask := div.GetSegmentHostMask(segmentPrefixLength.bitCount()) if div.isMultiple() || (mask&div.getSegmentValue()) != mask { return false } i++ for ; i < divCount; i++ { div = section.GetSegment(i) if !div.IsMax() { return false } } } return true } // IsZeroHost returns whether this section has a prefix length and if so, // whether the host section is always zero for all individual sections in this address section. // // If the host section is zero length (there are zero host bits), IsZeroHost returns true. func (section *ipAddressSectionInternal) IsZeroHost() bool { if !section.isPrefixed() { return false } return section.IsZeroHostLen(section.getNetworkPrefixLen().bitCount()) } // IsZeroHostLen returns whether the host section is always zero for all individual sections in this address section, // for the given prefix length. // // If the host section is zero length (there are zero host bits), IsZeroHostLen returns true. func (section *ipAddressSectionInternal) IsZeroHostLen(prefLen BitCount) bool { segmentCount := section.GetSegmentCount() if segmentCount == 0 { return true } else if prefLen < 0 { prefLen = 0 } bitsPerSegment := section.GetBitsPerSegment() // Note: 1.2.3.4/32 has a zero host prefixedSegmentIndex := getHostSegmentIndex(prefLen, section.GetBytesPerSegment(), bitsPerSegment) if prefixedSegmentIndex < segmentCount { segmentPrefixLength := getPrefixedSegmentPrefixLength(bitsPerSegment, prefLen, prefixedSegmentIndex) i := prefixedSegmentIndex div := section.GetSegment(i) if div.isMultiple() || (div.GetSegmentHostMask(segmentPrefixLength.bitCount())&div.getSegmentValue()) != 0 { return false } for i++; i < segmentCount; i++ { div := section.GetSegment(i) if !div.IsZero() { return false } } } return true } func (section *ipAddressSectionInternal) adjustPrefixLength(adjustment BitCount, withZeros bool) (*IPAddressSection, addrerr.IncompatibleAddressError) { if adjustment == 0 && section.isPrefixed() { return section.toIPAddressSection(), nil } prefix := section.getAdjustedPrefix(adjustment) sec, err := section.setPrefixLength(prefix, withZeros) return sec.ToIP(), err } func (section *ipAddressSectionInternal) adjustPrefixLen(adjustment BitCount) *IPAddressSection { // no zeroing res, _ := section.adjustPrefixLength(adjustment, false) return res } func (section *ipAddressSectionInternal) adjustPrefixLenZeroed(adjustment BitCount) (*IPAddressSection, addrerr.IncompatibleAddressError) { return section.adjustPrefixLength(adjustment, true) } func (section *ipAddressSectionInternal) withoutPrefixLen() *IPAddressSection { if !section.isPrefixed() { return section.toIPAddressSection() } if section.hasNoDivisions() { return createIPSection(section.getDivisionsInternal(), nil, section.getAddrType()) } existingPrefixLength := section.getPrefixLen().bitCount() maxVal := section.GetMaxSegmentValue() var startIndex int if existingPrefixLength > 0 { bitsPerSegment := section.GetBitsPerSegment() bytesPerSegment := section.GetBytesPerSegment() startIndex = getNetworkSegmentIndex(existingPrefixLength, bytesPerSegment, bitsPerSegment) } res, _ := section.getSubnetSegments( startIndex, nil, false, func(i int) *AddressDivision { return section.getDivision(i) }, func(i int) SegInt { return maxVal }, ) return res } func (section *ipAddressSectionInternal) checkSectionCount(other *IPAddressSection) addrerr.SizeMismatchError { if other.GetSegmentCount() < section.GetSegmentCount() { return &sizeMismatchError{incompatibleAddressError{addressError{key: "ipaddress.error.sizeMismatch"}}} } return nil } // error can be addrerr.IncompatibleAddressError or addrerr.SizeMismatchError func (section *ipAddressSectionInternal) mask(msk *IPAddressSection, retainPrefix bool) (*IPAddressSection, addrerr.IncompatibleAddressError) { if err := section.checkSectionCount(msk); err != nil { return nil, err } var prefLen PrefixLen if retainPrefix { prefLen = section.getPrefixLen() } return section.getSubnetSegments( 0, prefLen, true, section.getDivision, func(i int) SegInt { return msk.GetSegment(i).GetSegmentValue() }) } // error can be addrerr.IncompatibleAddressError or addrerr.SizeMismatchError func (section *ipAddressSectionInternal) bitwiseOr(msk *IPAddressSection, retainPrefix bool) (*IPAddressSection, addrerr.IncompatibleAddressError) { if err := section.checkSectionCount(msk); err != nil { return nil, err } var prefLen PrefixLen if retainPrefix { prefLen = section.getPrefixLen() } return section.getOredSegments( prefLen, true, section.getDivision, func(i int) SegInt { return msk.GetSegment(i).GetSegmentValue() }) } func (section *ipAddressSectionInternal) matchesWithMask(other *IPAddressSection, mask *IPAddressSection) bool { if err := section.checkSectionCount(other); err != nil { return false } else if err := section.checkSectionCount(mask); err != nil { return false } divCount := section.GetSegmentCount() for i := 0; i < divCount; i++ { seg := section.GetSegment(i) maskSegment := mask.GetSegment(i) otherSegment := other.GetSegment(i) if !seg.MatchesValsWithMask( otherSegment.getSegmentValue(), otherSegment.getUpperSegmentValue(), maskSegment.getSegmentValue()) { return false } } return true } func (section *ipAddressSectionInternal) intersect(other *IPAddressSection) (res *IPAddressSection, err addrerr.SizeMismatchError) { //check if they are comparable section. We only check segment count, we do not care about start index. err = section.checkSectionCount(other) if err != nil { return } //larger prefix length should prevail? hmmmmm... I would say that is true, choose the larger prefix pref := section.getNetworkPrefixLen() otherPref := other.getNetworkPrefixLen() if pref != nil { if otherPref != nil { if otherPref.bitCount() > pref.bitCount() { pref = otherPref } } else { pref = nil } } if other.Contains(section.toIPAddressSection()) { if pref.Equal(section.getNetworkPrefixLen()) { res = section.toIPAddressSection() return } } else if !section.isMultiple() { // no intersection, for single valued section, any intersection would have to be containment return } if section.contains(other) { if pref.Equal(other.getNetworkPrefixLen()) { res = other.toIPAddressSection() return } } else if !other.isMultiple() { // no intersection, for single valued section, any intersection would have to be containment return } segCount := section.GetSegmentCount() for i := 0; i < segCount; i++ { seg := section.GetSegment(i) otherSeg := other.GetSegment(i) lower := seg.GetSegmentValue() higher := seg.getUpperSegmentValue() otherLower := otherSeg.GetSegmentValue() otherHigher := otherSeg.getUpperSegmentValue() if otherLower > higher || lower > otherHigher { //no overlap in this segment means no overlap at all return } } // all segments have overlap segs := createSegmentArray(segCount) for i := 0; i < segCount; i++ { seg := section.GetSegment(i) otherSeg := other.GetSegment(i) segPref := getSegmentPrefixLength(seg.getBitCount(), pref, i) if seg.Contains(otherSeg) { if segPref.Equal(otherSeg.GetSegmentPrefixLen()) { segs[i] = otherSeg.ToDiv() continue } } if otherSeg.Contains(seg) { if segPref.Equal(seg.GetSegmentPrefixLen()) { segs[i] = seg.ToDiv() continue } } lower := seg.GetSegmentValue() higher := seg.getUpperSegmentValue() otherLower := otherSeg.GetSegmentValue() otherHigher := otherSeg.getUpperSegmentValue() lower = maxSegInt(lower, otherLower) higher = minSegInt(higher, otherHigher) segs[i] = createAddressDivision(seg.deriveNewMultiSeg(lower, higher, segPref)) } res = deriveIPAddressSectionPrefLen(section.toIPAddressSection(), segs, pref) return } func (section *ipAddressSectionInternal) subtract(other *IPAddressSection) (res []*IPAddressSection, err addrerr.SizeMismatchError) { //check if they are comparable section err = section.checkSectionCount(other) if err != nil { return } //Since this is only called from IPv4 and IPv6, we need not check section versions or types here if !section.isMultiple() { if other.Contains(section.toIPAddressSection()) { return } res = []*IPAddressSection{section.toIPAddressSection()} return } //getDifference: same as removing the intersection // section you confirm there is an intersection in each segment. // Then you remove each intersection, one at a time, leaving the other segments the same, since only one segment needs to differ. // To prevent adding the same section twice, use only the intersection (ie the relative complement of the diff) // of segments already handled and not the whole segment. // For example: 0-3.0-3.2.4 subtracting 1-4.1-3.2.4, the intersection is 1-3.1-3.2.4 // The diff of the section segment is just 0, giving 0.0-3.2.4 (subtract the section segment, leave the others the same) // The diff of the second segment is also 0, but for the section segment we use the intersection since we handled the section already, giving 1-3.0.2.4 // (take the intersection of the section segment, subtract the second segment, leave remaining segments the same) segCount := section.GetSegmentCount() for i := 0; i < segCount; i++ { seg := section.GetSegment(i) otherSeg := other.GetSegment(i) lower := seg.GetSegmentValue() higher := seg.getUpperSegmentValue() otherLower := otherSeg.GetSegmentValue() otherHigher := otherSeg.getUpperSegmentValue() if otherLower > higher || lower > otherHigher { //no overlap in this segment means no overlap at all res = []*IPAddressSection{section.toIPAddressSection()} return } } // As we create each section, the initial segments created by us will have no prefix, // the trailing segments come from the original section, so the resulting section will have prefix-consistent segments. // We do not care what that prefix length is, because at the end of this method we assign a new prefix. // We just need to be sure that we create a valid section, one with prefix-consistent segments. intersections := createSegmentArray(segCount) sections := make([]*IPAddressSection, 0, segCount<<1) for i := 0; i < segCount; i++ { seg := section.GetSegment(i) otherSeg := other.GetSegment(i) lower := seg.GetSegmentValue() higher := seg.getUpperSegmentValue() otherLower := otherSeg.GetSegmentValue() otherHigher := otherSeg.getUpperSegmentValue() if lower >= otherLower { if higher <= otherHigher { //this segment is contained in the other if seg.isPrefixed() { intersections[i] = createAddressDivision(seg.deriveNewMultiSeg(lower, higher, nil)) } else { intersections[i] = seg.ToDiv() } continue } //otherLower <= lower <= otherHigher < higher intersections[i] = createAddressDivision(seg.deriveNewMultiSeg(lower, otherHigher, nil)) section := section.createDiffSection(seg, otherHigher+1, higher, i, intersections) sections = append(sections, section) } else { //lower < otherLower <= otherHigher section := section.createDiffSection(seg, lower, otherLower-1, i, intersections) sections = append(sections, section) if higher <= otherHigher { intersections[i] = createAddressDivision(seg.deriveNewMultiSeg(otherLower, higher, nil)) } else { //lower < otherLower <= otherHigher < higher intersections[i] = createAddressDivision(seg.deriveNewMultiSeg(otherLower, otherHigher, nil)) section = section.createDiffSection(seg, otherHigher+1, higher, i, intersections) sections = append(sections, section) } } } if len(sections) == 0 { return } //apply the prefix to the sections //for each section, we figure out what each prefix length should be if section.isPrefixed() { thisPrefix := section.getNetworkPrefixLen().bitCount() for i := 0; i < len(sections); i++ { section := sections[i] bitCount := section.GetBitCount() totalPrefix := bitCount for j := section.GetSegmentCount() - 1; j >= 0; j-- { seg := section.GetSegment(j) segBitCount := seg.GetBitCount() segPrefix := seg.GetMinPrefixLenForBlock() if segPrefix == segBitCount { break } else { totalPrefix -= segBitCount if segPrefix != 0 { totalPrefix += segPrefix break } } } if totalPrefix != bitCount { if totalPrefix < thisPrefix { totalPrefix = thisPrefix } section = section.SetPrefixLen(totalPrefix) sections[i] = section } } } res = sections return } func (section *ipAddressSectionInternal) createDiffSection( seg *IPAddressSegment, lower, upper SegInt, diffIndex int, intersectingValues []*AddressDivision) *IPAddressSection { segCount := section.GetSegmentCount() segments := createSegmentArray(segCount) for j := 0; j < diffIndex; j++ { segments[j] = intersectingValues[j] } diff := createAddressDivision(seg.deriveNewMultiSeg(lower, upper, nil)) segments[diffIndex] = diff for j := diffIndex + 1; j < segCount; j++ { segments[j] = section.getDivision(j) } return deriveIPAddressSection(section.toIPAddressSection(), segments) } func (section *ipAddressSectionInternal) spanWithPrefixBlocks() []ExtendedIPSegmentSeries { wrapped := wrapIPSection(section.toIPAddressSection()) if section.IsSequential() { if section.IsSinglePrefixBlock() { return []ExtendedIPSegmentSeries{wrapped} } return getSpanningPrefixBlocks(wrapped, wrapped) } return spanWithPrefixBlocks(wrapped) } func (section *ipAddressSectionInternal) spanWithSequentialBlocks() []ExtendedIPSegmentSeries { wrapped := wrapIPSection(section.toIPAddressSection()) if section.IsSequential() { return []ExtendedIPSegmentSeries{wrapped} } return spanWithSequentialBlocks(wrapped) } func (section *ipAddressSectionInternal) coverSeriesWithPrefixBlock() ExtendedIPSegmentSeries { if section.IsSinglePrefixBlock() { return wrapIPSection(section.toIPAddressSection()) } return coverWithPrefixBlock( wrapIPSection(section.getLower().ToIP()), wrapIPSection(section.getUpper().ToIP())) } func (section *ipAddressSectionInternal) coverWithPrefixBlock() *IPAddressSection { if section.IsSinglePrefixBlock() { return section.toIPAddressSection() } res := coverWithPrefixBlock( wrapIPSection(section.getLower().ToIP()), wrapIPSection(section.getUpper().ToIP())) return res.(WrappedIPAddressSection).IPAddressSection } func (section *ipAddressSectionInternal) coverWithPrefixBlockTo(other *IPAddressSection) (*IPAddressSection, addrerr.SizeMismatchError) { if err := section.checkSectionCount(other); err != nil { return nil, err } res := getCoveringPrefixBlock( wrapIPSection(section.toIPAddressSection()), wrapIPSection(other)) return res.(WrappedIPAddressSection).IPAddressSection, nil } func (section *ipAddressSectionInternal) getNetworkSection() *IPAddressSection { var prefLen BitCount if section.isPrefixed() { prefLen = section.getPrefixLen().bitCount() } else { prefLen = section.GetBitCount() } return section.getNetworkSectionLen(prefLen) } func (section *ipAddressSectionInternal) getNetworkSectionLen(networkPrefixLength BitCount) *IPAddressSection { segmentCount := section.GetSegmentCount() if segmentCount == 0 { return section.toIPAddressSection() } networkPrefixLength = checkBitCount(networkPrefixLength, section.GetBitCount()) bitsPerSegment := section.GetBitsPerSegment() prefixedSegmentIndex := getNetworkSegmentIndex(networkPrefixLength, section.GetBytesPerSegment(), bitsPerSegment) var newSegments []*AddressDivision if prefixedSegmentIndex >= 0 { segPrefLength := getPrefixedSegmentPrefixLength(bitsPerSegment, networkPrefixLength, prefixedSegmentIndex) // prefixedSegmentIndex of -1 already handled lastSeg := section.GetSegment(prefixedSegmentIndex) prefBits := segPrefLength.bitCount() mask := ^SegInt(0) << uint(bitsPerSegment-prefBits) lower, upper := lastSeg.getSegmentValue()&mask, lastSeg.getUpperSegmentValue()|^mask networkSegmentCount := prefixedSegmentIndex + 1 if networkSegmentCount == segmentCount && segsSame(segPrefLength, lastSeg.GetSegmentPrefixLen(), lower, lastSeg.getSegmentValue(), upper, lastSeg.getUpperSegmentValue()) { // the segment count and prefixed segment matches return section.toIPAddressSection() } newSegments = createSegmentArray(networkSegmentCount) section.copySubDivisions(0, prefixedSegmentIndex, newSegments) newSegments[prefixedSegmentIndex] = createAddressDivision(lastSeg.deriveNewMultiSeg(lower, upper, segPrefLength)) } else { newSegments = createSegmentArray(0) } return deriveIPAddressSectionPrefLen(section.toIPAddressSection(), newSegments, cacheBitCount(networkPrefixLength)) } func (section *ipAddressSectionInternal) getHostSection() *IPAddressSection { var prefLen BitCount if section.isPrefixed() { prefLen = section.getPrefixLen().bitCount() } return section.getHostSectionLen(prefLen) } func (section *ipAddressSectionInternal) getHostSectionLen(networkPrefixLength BitCount) *IPAddressSection { segmentCount := section.GetSegmentCount() if segmentCount == 0 { return section.toIPAddressSection() } networkPrefixLength = checkBitCount(networkPrefixLength, section.GetBitCount()) bitsPerSegment := section.GetBitsPerSegment() bytesPerSegment := section.GetBytesPerSegment() prefixedSegmentIndex := getHostSegmentIndex(networkPrefixLength, bytesPerSegment, bitsPerSegment) var prefLen PrefixLen var newSegments []*AddressDivision if prefixedSegmentIndex < segmentCount { firstSeg := section.GetSegment(prefixedSegmentIndex) segPrefLength := getPrefixedSegmentPrefixLength(bitsPerSegment, networkPrefixLength, prefixedSegmentIndex) prefLen = segPrefLength prefBits := segPrefLength.bitCount() //mask the boundary segment mask := ^(^SegInt(0) << uint(bitsPerSegment-prefBits)) divLower := uint64(firstSeg.getDivisionValue()) divUpper := uint64(firstSeg.getUpperDivisionValue()) divMask := uint64(mask) maxVal := uint64(^SegInt(0)) masker := MaskRange(divLower, divUpper, divMask, maxVal) lower, upper := masker.GetMaskedLower(divLower, divMask), masker.GetMaskedUpper(divUpper, divMask) segLower, segUpper := SegInt(lower), SegInt(upper) if prefixedSegmentIndex == 0 && segsSame(segPrefLength, firstSeg.GetSegmentPrefixLen(), segLower, firstSeg.getSegmentValue(), segUpper, firstSeg.getUpperSegmentValue()) { // the segment count and prefixed segment matches return section.toIPAddressSection() } hostSegmentCount := segmentCount - prefixedSegmentIndex newSegments = createSegmentArray(hostSegmentCount) newSegments[0] = createAddressDivision(firstSeg.deriveNewMultiSeg(segLower, segUpper, segPrefLength)) // the remaining segments each must have zero-segment prefix length var zeroPrefixIndex int if section.isPrefixed() { zeroPrefixIndex = getNetworkSegmentIndex(section.GetPrefixLen().bitCount(), bytesPerSegment, bitsPerSegment) + 1 } else { zeroPrefixIndex = segmentCount } zeroPrefixIndex -= prefixedSegmentIndex zeroPrefixIndex = max(zeroPrefixIndex, 1) for i := 1; i < zeroPrefixIndex; i++ { seg := section.GetSegment(prefixedSegmentIndex + i) newSegments[i] = createAddressDivision(seg.derivePrefixed(cacheBitCount(0))) } // the rest already have zero-segment prefix length, just copy them section.copySubDivisions(prefixedSegmentIndex+zeroPrefixIndex, prefixedSegmentIndex+hostSegmentCount, newSegments[zeroPrefixIndex:]) } else { prefLen = cacheBitCount(0) newSegments = createSegmentArray(0) } return deriveIPAddressSectionPrefLen(section.toIPAddressSection(), newSegments, prefLen) } func (section *ipAddressSectionInternal) getSubnetSegments( // called by methods to adjust/remove/set prefix length, masking methods, zero host and zero network methods startIndex int, networkPrefixLength PrefixLen, verifyMask bool, segProducer func(int) *AddressDivision, segmentMaskProducer func(int) SegInt, ) (*IPAddressSection, addrerr.IncompatibleAddressError) { newSect, err := section.addressSectionInternal.getSubnetSegments(startIndex, networkPrefixLength, verifyMask, segProducer, segmentMaskProducer) return newSect.ToIP(), err } func (section *ipAddressSectionInternal) getOredSegments( networkPrefixLength PrefixLen, verifyMask bool, segProducer func(int) *AddressDivision, segmentMaskProducer func(int) SegInt) (res *IPAddressSection, err addrerr.IncompatibleAddressError) { networkPrefixLength = checkPrefLen(networkPrefixLength, section.GetBitCount()) bitsPerSegment := section.GetBitsPerSegment() count := section.GetSegmentCount() for i := 0; i < count; i++ { segmentPrefixLength := getSegmentPrefixLength(bitsPerSegment, networkPrefixLength, i) seg := segProducer(i) //note that the mask can represent a range (for example a CIDR mask), //but we use the lowest value (maskSegment.value) in the range when masking (ie we discard the range) maskValue := segmentMaskProducer(i) origValue, origUpperValue := seg.getSegmentValue(), seg.getUpperSegmentValue() value, upperValue := origValue, origUpperValue if verifyMask { mask64 := uint64(maskValue) val64 := uint64(value) upperVal64 := uint64(upperValue) masker := bitwiseOrRange(val64, upperVal64, mask64, seg.GetMaxValue()) if !masker.IsSequential() { err = &incompatibleAddressError{addressError{key: "ipaddress.error.maskMismatch"}} return } value = SegInt(masker.GetOredLower(val64, mask64)) upperValue = SegInt(masker.GetOredUpper(upperVal64, mask64)) } else { value |= maskValue upperValue |= maskValue } if !segsSame(segmentPrefixLength, seg.getDivisionPrefixLength(), value, origValue, upperValue, origUpperValue) { newSegments := createSegmentArray(count) section.copySubDivisions(0, i, newSegments) newSegments[i] = createAddressDivision(seg.deriveNewMultiSeg(value, upperValue, segmentPrefixLength)) for i++; i < count; i++ { segmentPrefixLength = getSegmentPrefixLength(bitsPerSegment, networkPrefixLength, i) seg = segProducer(i) maskValue = segmentMaskProducer(i) value = seg.getSegmentValue() upperValue = seg.getUpperSegmentValue() if verifyMask { mask64 := uint64(maskValue) val64 := uint64(value) upperVal64 := uint64(upperValue) masker := bitwiseOrRange(val64, upperVal64, mask64, seg.GetMaxValue()) if !masker.IsSequential() { err = &incompatibleAddressError{addressError{key: "ipaddress.error.maskMismatch"}} return } value = SegInt(masker.GetOredLower(val64, mask64)) upperValue = SegInt(masker.GetOredUpper(upperVal64, mask64)) } else { value |= maskValue upperValue |= maskValue } if !segsSame(segmentPrefixLength, seg.getDivisionPrefixLength(), value, origValue, upperValue, origUpperValue) { newSegments[i] = createAddressDivision(seg.deriveNewMultiSeg(value, upperValue, segmentPrefixLength)) } else { newSegments[i] = seg } } res = deriveIPAddressSectionPrefLen(section.toIPAddressSection(), newSegments, networkPrefixLength) return } } res = section.toIPAddressSection() return } func (section *ipAddressSectionInternal) getNetwork() IPAddressNetwork { if addrType := section.getAddrType(); addrType.isIPv4() { return ipv4Network } else if addrType.isIPv6() { return ipv6Network } return nil } func (section *ipAddressSectionInternal) getNetworkMask(network IPAddressNetwork) *IPAddressSection { var prefLen BitCount if section.isPrefixed() { prefLen = section.getNetworkPrefixLen().bitCount() } else { prefLen = section.GetBitCount() } return network.GetNetworkMask(prefLen).GetSubSection(0, section.GetSegmentCount()) } func (section *ipAddressSectionInternal) getHostMask(network IPAddressNetwork) *IPAddressSection { var prefLen BitCount if section.isPrefixed() { prefLen = section.getNetworkPrefixLen().bitCount() } return network.GetHostMask(prefLen).GetSubSection(0, section.GetSegmentCount()) } func (section *ipAddressSectionInternal) insert(index int, other *IPAddressSection, segmentToBitsShift uint) *IPAddressSection { return section.replaceLen(index, index, other, 0, other.GetSegmentCount(), segmentToBitsShift) } // Replaces segments starting from startIndex and ending before endIndex with the segments starting at replacementStartIndex and // ending before replacementEndIndex from the replacement section. func (section *ipAddressSectionInternal) replaceLen( startIndex, endIndex int, replacement *IPAddressSection, replacementStartIndex, replacementEndIndex int, segmentToBitsShift uint) *IPAddressSection { segmentCount := section.GetSegmentCount() startIndex, endIndex, replacementStartIndex, replacementEndIndex = adjustIndices(startIndex, endIndex, segmentCount, replacementStartIndex, replacementEndIndex, replacement.GetSegmentCount()) replacedCount := endIndex - startIndex replacementCount := replacementEndIndex - replacementStartIndex thizz := section.toAddressSection() if replacementCount == 0 && replacedCount == 0 { //keep in mind for ipvx, empty sections cannot have prefix lengths return section.toIPAddressSection() } else if segmentCount == replacedCount { //keep in mind for ipvx, empty sections cannot have prefix lengths return replacement } var newPrefixLen PrefixLen prefixLength := section.getPrefixLen() startBits := BitCount(startIndex << segmentToBitsShift) if prefixLength != nil && prefixLength.bitCount() <= startBits { newPrefixLen = prefixLength replacement = replacement.SetPrefixLen(0) } else { replacementEndBits := BitCount(replacementEndIndex << segmentToBitsShift) replacementPrefLen := replacement.getPrefixLen() endIndexBits := BitCount(endIndex << segmentToBitsShift) if replacementPrefLen != nil && replacementPrefLen.bitCount() <= replacementEndBits { var replacementPrefixLen BitCount replacementStartBits := BitCount(replacementStartIndex << segmentToBitsShift) replacementPrefLenIsZero := replacementPrefLen.bitCount() <= replacementStartBits if !replacementPrefLenIsZero { replacementPrefixLen = replacementPrefLen.bitCount() - replacementStartBits } newPrefixLen = cacheBitCount(startBits + replacementPrefixLen) if endIndex < segmentCount && (prefixLength == nil || prefixLength.bitCount() > endIndexBits) { if replacedCount > 0 || replacementPrefLenIsZero { thizz = section.setPrefixLen(endIndexBits) } else { // this covers the case of a:5:6:7:8 is getting b:c:d/47 at index 1 to 1 // We need "a" to have no prefix, and "5" to get prefix len 0 // But setting "5" to have prefix len 0 gives "a" the prefix len 16 // This is not a problem if any segments are getting replaced or the replacement segments have prefix length 0 // // we move the non-replaced host segments from the end of this to the end of the replacement segments // and we also remove the prefix length from this additionalSegs := segmentCount - endIndex thizz = section.getSubSection(0, startIndex) replacement = replacement.insert( replacementEndIndex, section.getSubSection(endIndex, segmentCount).ToIP(), segmentToBitsShift) replacementEndIndex += additionalSegs } } } else if prefixLength != nil { replacementBits := BitCount(replacementCount << segmentToBitsShift) var endPrefixBits BitCount if prefixLength.bitCount() > endIndexBits { endPrefixBits = prefixLength.bitCount() - endIndexBits } newPrefixLen = cacheBitCount(startBits + replacementBits + endPrefixBits) } // else newPrefixLen is nil } return thizz.replace(startIndex, endIndex, replacement.ToSectionBase(), replacementStartIndex, replacementEndIndex, newPrefixLen).ToIP() } func (section *ipAddressSectionInternal) toNormalizedWildcardString() string { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToNormalizedWildcardString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToNormalizedWildcardString() } return nilSection() } func (section *ipAddressSectionInternal) toCanonicalWildcardString() string { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToCanonicalWildcardString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToCanonicalWildcardString() } return nilSection() } func (section *ipAddressSectionInternal) toSegmentedBinaryString() string { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToSegmentedBinaryString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToSegmentedBinaryString() } return nilSection() } func (section *ipAddressSectionInternal) toSQLWildcardString() string { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToSQLWildcardString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToSQLWildcardString() } return nilSection() } func (section *ipAddressSectionInternal) toFullString() string { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToFullString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToFullString() } return nilSection() } func (section *ipAddressSectionInternal) toReverseDNSString() (string, addrerr.IncompatibleAddressError) { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToReverseDNSString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToReverseDNSString() } return nilSection(), nil } func (section *ipAddressSectionInternal) toPrefixLenString() string { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToPrefixLenString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToPrefixLenString() } return nilSection() } func (section *ipAddressSectionInternal) toSubnetString() string { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToNormalizedWildcardString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToPrefixLenString() } return nilSection() } func (section *ipAddressSectionInternal) toCompressedWildcardString() string { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToCompressedWildcardString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToCompressedWildcardString() } return nilSection() } func (section *ipAddressSectionInternal) toCustomString(stringOptions addrstr.IPStringOptions) string { return toNormalizedIPZonedString(stringOptions, section.toIPAddressSection(), NoZone) } func (section *ipAddressSectionInternal) toCustomZonedString(stringOptions addrstr.IPStringOptions, zone Zone) string { return toNormalizedIPZonedString(stringOptions, section.toIPAddressSection(), zone) } // Wrap wraps this IP address section, returning a WrappedIPAddressSection, an implementation of ExtendedIPSegmentSeries, // which can be used to write code that works with both IP addresses and IP address sections. // Wrap can be called with a nil receiver, wrapping a nil address section. func (section *ipAddressSectionInternal) Wrap() WrappedIPAddressSection { return wrapIPSection(section.toIPAddressSection()) } // WrapSection wraps this IP address section, returning a WrappedAddressSection, an implementation of ExtendedSegmentSeries, // which can be used to write code that works with both addresses and address sections. // WrapSection can be called with a nil receiver, wrapping a nil address section. func (section *ipAddressSectionInternal) WrapSection() WrappedAddressSection { return wrapSection(section.toAddressSection()) } func (section *ipAddressSectionInternal) toIPAddressSection() *IPAddressSection { return (*IPAddressSection)(unsafe.Pointer(section)) } //// only needed for godoc / pkgsite // GetBitCount returns the number of bits in each value comprising this address item. func (section *ipAddressSectionInternal) GetBitCount() BitCount { return section.addressSectionInternal.GetBitCount() } // GetByteCount returns the number of bytes required for each value comprising this address item. func (section *ipAddressSectionInternal) GetByteCount() int { return section.addressSectionInternal.GetByteCount() } //IPv6v4, Div, Not needed Addr because of GetGenericSegment //func (grouping *addressDivisionGroupingBase) GetGenericDivision(index int) DivisionType { // //IPv6v4, Div, Not needed Addr //func (grouping *addressDivisionGroupingBase) GetDivisionCount() int { // IsZero returns whether this section matches exactly the value of zero. func (section *ipAddressSectionInternal) IsZero() bool { return section.addressSectionInternal.IsZero() } // IncludesZero returns whether this section includes the value of zero within its range. func (section *ipAddressSectionInternal) IncludesZero() bool { return section.addressSectionInternal.IncludesZero() } // IsMax returns whether this section matches exactly the maximum possible value, the value whose bits are all ones. func (section *ipAddressSectionInternal) IsMax() bool { return section.addressSectionInternal.IsMax() } // IncludesMax returns whether this section includes the max value, the value whose bits are all ones, within its range. func (section *ipAddressSectionInternal) IncludesMax() bool { return section.addressSectionInternal.IncludesMax() } // IsFullRange returns whether this address item represents all possible values attainable by an address item of this type. // // This is true if and only if both IncludesZero and IncludesMax return true. func (section *ipAddressSectionInternal) IsFullRange() bool { return section.addressSectionInternal.IsFullRange() } // GetSequentialBlockIndex gets the minimal segment index for which all following segments are full-range blocks. // // The segment at this index is not a full-range block itself, unless all segments are full-range. // The segment at this index and all following segments form a sequential range. // For the full address section to be sequential, the preceding segments must be single-valued. func (section *ipAddressSectionInternal) GetSequentialBlockIndex() int { return section.addressSectionInternal.GetSequentialBlockIndex() } // GetSequentialBlockCount provides the count of elements from the sequential block iterator, the minimal number of sequential address sections that comprise this address section. func (section *ipAddressSectionInternal) GetSequentialBlockCount() *big.Int { return section.addressSectionInternal.GetSequentialBlockCount() } // ContainsPrefixBlock returns whether the values of this item contains the block of values for the given prefix length. // // Unlike ContainsSinglePrefixBlock, whether there are multiple prefix values in this item for the given prefix length makes no difference. // // Use GetMinPrefixLenForBlock to determine the smallest prefix length for which this method returns true. func (section *ipAddressSectionInternal) ContainsPrefixBlock(prefixLen BitCount) bool { return section.addressSectionInternal.ContainsPrefixBlock(prefixLen) } // ContainsSinglePrefixBlock returns whether the values of this section contains a single prefix block for the given prefix length. // // This means there is only one prefix of the given length in this item, and this item contains the prefix block for that given prefix. // // Use GetPrefixLenForSingleBlock to determine whether there is a prefix length for which this method returns true. func (section *ipAddressSectionInternal) ContainsSinglePrefixBlock(prefixLen BitCount) bool { return section.addressSectionInternal.ContainsSinglePrefixBlock(prefixLen) } // IsPrefixBlock returns whether this address segment series has a prefix length and includes the block associated with its prefix length. // If the prefix length matches the bit count, this returns true. // // This is different from ContainsPrefixBlock in that this method returns // false if the series has no prefix length, or a prefix length that differs from a prefix length for which ContainsPrefixBlock returns true. func (section *ipAddressSectionInternal) IsPrefixBlock() bool { return section.addressSectionInternal.IsPrefixBlock() } // IsSinglePrefixBlock returns whether the range matches the block of values for a single prefix identified by the prefix length of this address. // This is similar to IsPrefixBlock except that it returns false when the subnet has multiple prefixes. // // What distinguishes this method from ContainsSinglePrefixBlock is that this method returns // false if the series does not have a prefix length assigned to it, // or a prefix length that differs from the prefix length for which ContainsSinglePrefixBlock returns true. // // It is similar to IsPrefixBlock but returns false when there are multiple prefixes. func (section *ipAddressSectionInternal) IsSinglePrefixBlock() bool { return section.addressSectionInternal.IsSinglePrefixBlock() } // GetMinPrefixLenForBlock returns the smallest prefix length such that this section includes the block of all values for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this section represents a single value, this returns the bit count. func (section *ipAddressSectionInternal) GetMinPrefixLenForBlock() BitCount { return section.addressSectionInternal.GetMinPrefixLenForBlock() } // GetPrefixLenForSingleBlock returns a prefix length for which the range of this address section matches the block of addresses for that prefix. // // If no such prefix exists, GetPrefixLenForSingleBlock returns nil. // // If this address section represents a single value, returns the bit length. func (section *ipAddressSectionInternal) GetPrefixLenForSingleBlock() PrefixLen { return section.addressSectionInternal.GetPrefixLenForSingleBlock() } // GetValue returns the lowest individual address section in this address section as an integer value. func (section *ipAddressSectionInternal) GetValue() *big.Int { return section.addressSectionInternal.GetValue() } // GetUpperValue returns the highest individual address section in this address section as an integer value. func (section *ipAddressSectionInternal) GetUpperValue() *big.Int { return section.addressSectionInternal.GetUpperValue() } // Bytes returns the lowest individual address section in this address section as a byte slice. func (section *ipAddressSectionInternal) Bytes() []byte { return section.addressSectionInternal.Bytes() } // UpperBytes returns the highest individual address section in this address section as a byte slice. func (section *ipAddressSectionInternal) UpperBytes() []byte { return section.addressSectionInternal.UpperBytes() } // CopyBytes copies the value of the lowest individual address section in the section into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (section *ipAddressSectionInternal) CopyBytes(bytes []byte) []byte { return section.addressSectionInternal.CopyBytes(bytes) } // CopyUpperBytes copies the value of the highest individual address section in the section into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (section *ipAddressSectionInternal) CopyUpperBytes(bytes []byte) []byte { return section.addressSectionInternal.CopyUpperBytes(bytes) } // IsSequential returns whether the section represents a range of values that are sequential. // // Generally, this means that any segment covering a range of values must be followed by segment that are full range, covering all values. func (section *ipAddressSectionInternal) IsSequential() bool { return section.addressSectionInternal.IsSequential() } // GetBitsPerSegment returns the number of bits comprising each segment in this section. Segments in the same address section are equal length. func (section *ipAddressSectionInternal) GetBitsPerSegment() BitCount { return section.addressSectionInternal.GetBitsPerSegment() } // GetBytesPerSegment returns the number of bytes comprising each segment in this section. Segments in the same address section are equal length. func (section *ipAddressSectionInternal) GetBytesPerSegment() int { return section.addressSectionInternal.GetBytesPerSegment() } // GetGenericSegment returns the segment at the given index as an AddressSegmentType. // The first segment is at index 0. // GetGenericSegment will panic given a negative index or an index matching or larger than the segment count. func (section *ipAddressSectionInternal) GetGenericSegment(index int) AddressSegmentType { return section.addressSectionInternal.GetGenericSegment(index) } // GetSegmentCount returns the segment/division count. func (section *ipAddressSectionInternal) GetSegmentCount() int { return section.addressSectionInternal.GetSegmentCount() } // GetMaxSegmentValue returns the maximum possible segment value for this type of address. // // Note this is not the maximum of the range of segment values in this specific address, // this is the maximum value of any segment for this address type and version, determined by the number of bits per segment. func (section *ipAddressSectionInternal) GetMaxSegmentValue() SegInt { return section.addressSectionInternal.GetMaxSegmentValue() } // TestBit returns true if the bit in the lower value of this section at the given index is 1, where index 0 refers to the least significant bit. // In other words, it computes (bits & (1 << n)) != 0), using the lower value of this section. // TestBit will panic if n < 0, or if it matches or exceeds the bit count of this item. func (section *ipAddressSectionInternal) TestBit(n BitCount) bool { return section.addressSectionInternal.TestBit(n) } // IsOneBit returns true if the bit in the lower value of this section at the given index is 1, where index 0 refers to the most significant bit. // IsOneBit will panic if bitIndex is less than zero, or if it is larger than the bit count of this item. func (section *ipAddressSectionInternal) IsOneBit(prefixBitIndex BitCount) bool { return section.addressSectionInternal.IsOneBit(prefixBitIndex) } // PrefixEqual determines if the given section matches this section up to the prefix length of this section. // It returns whether the argument section has the same address section prefix values as this. // // All prefix bits of this section must be present in the other section to be comparable, otherwise false is returned. func (section *ipAddressSectionInternal) PrefixEqual(other AddressSectionType) bool { return section.addressSectionInternal.PrefixEqual(other) } // PrefixContains returns whether the prefix values in the given address section // are prefix values in this address section, using the prefix length of this section. // If this address section has no prefix length, the entire address is compared. // // It returns whether the prefix of this address contains all values of the same prefix length in the given address. // // All prefix bits of this section must be present in the other section to be comparable. func (section *ipAddressSectionInternal) PrefixContains(other AddressSectionType) bool { return section.addressSectionInternal.PrefixContains(other) } //// end needed for godoc / pkgsite // An IPAddressSection is an address section of an IP address, containing a certain number of consecutive segments of an IP address. // // It is a series of individual address segments. Each segment has equal bit-length. Each address is backed by an address section that contains all the segments of the address. // // IPAddressSection objects are immutable. This also makes them concurrency-safe. // // Most operations that can be performed on IPAddress instances can also be performed on IPAddressSection instances and vice-versa. type IPAddressSection struct { ipAddressSectionInternal } // Contains returns whether this is same type and version as the given address section and whether it contains all values in the given section. // // Sections must also have the same number of segments to be comparable, otherwise false is returned. func (section *IPAddressSection) Contains(other AddressSectionType) bool { if section == nil { return other == nil || other.ToSectionBase() == nil } return section.contains(other) } // Equal returns whether the given address section is equal to this address section. // Two address sections are equal if they represent the same set of sections. // They must match: // - type/version: IPv4, IPv6 // - segment counts // - segment value ranges // Prefix lengths are ignored. func (section *IPAddressSection) Equal(other AddressSectionType) bool { if section == nil { return other == nil || other.ToSectionBase() == nil } return section.equal(other) } // Compare returns a negative integer, zero, or a positive integer if this address section is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (section *IPAddressSection) Compare(item AddressItem) int { return CountComparator.Compare(section, item) } // CompareSize compares the counts of two address sections or other items, the number of individual items represented. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether this section represents more individual address sections than another item. // // CompareSize returns a positive integer if this address section has a larger count than the item given, zero if they are the same, or a negative integer if the other has a larger count. func (section *IPAddressSection) CompareSize(other AddressItem) int { if section == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return section.compareSize(other) } // GetCount returns the count of possible distinct values for this item. // If not representing multiple values, the count is 1, // unless this is a division grouping with no divisions, or an address section with no segments, in which case it is 0. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (section *IPAddressSection) GetCount() *big.Int { if section == nil { return bigZero() } else if sect := section.ToIPv4(); sect != nil { return sect.GetCount() } else if sect := section.ToIPv6(); sect != nil { return sect.GetCount() } return section.addressDivisionGroupingBase.getCount() } // IsMultiple returns whether this section represents multiple values. func (section *IPAddressSection) IsMultiple() bool { return section != nil && section.isMultiple() } // IsPrefixed returns whether this section has an associated prefix length. func (section *IPAddressSection) IsPrefixed() bool { return section != nil && section.isPrefixed() } // GetPrefixCount returns the number of distinct prefix values in this item. // // The prefix length is given by GetPrefixLen. // // If this has a non-nil prefix length, returns the number of distinct prefix values. // // If this has a nil prefix length, it returns the same value as GetCount. func (section *IPAddressSection) GetPrefixCount() *big.Int { if sect := section.ToIPv4(); sect != nil { return sect.GetPrefixCount() } else if sect := section.ToIPv6(); sect != nil { return sect.GetPrefixCount() } return section.addressDivisionGroupingBase.GetPrefixCount() } // GetPrefixCountLen returns the number of distinct prefix values in this item for the given prefix length. func (section *IPAddressSection) GetPrefixCountLen(prefixLen BitCount) *big.Int { if sect := section.ToIPv4(); sect != nil { return sect.GetPrefixCountLen(prefixLen) } else if sect := section.ToIPv6(); sect != nil { return sect.GetPrefixCountLen(prefixLen) } return section.addressDivisionGroupingBase.GetPrefixCountLen(prefixLen) } // GetBlockCount returns the count of distinct values in the given number of initial (more significant) segments. func (section *IPAddressSection) GetBlockCount(segments int) *big.Int { if sect := section.ToIPv4(); sect != nil { return sect.GetBlockCount(segments) } else if sect := section.ToIPv6(); sect != nil { return sect.GetBlockCount(segments) } return section.addressDivisionGroupingBase.GetBlockCount(segments) } // IsAdaptiveZero returns true if the division grouping was originally created as an implicitly zero-valued section or grouping (e.g. IPv4AddressSection{}), // meaning it was not constructed using a constructor function. // Such a grouping, which has no divisions or segments, is convertible to an implicitly zero-valued grouping of any type or version, whether IPv6, IPv4, MAC, or other. // In other words, when a section or grouping is the zero-value, then it is equivalent and convertible to the zero value of any other section or grouping type. func (section *IPAddressSection) IsAdaptiveZero() bool { return section != nil && section.matchesZeroGrouping() } // ToDivGrouping converts to an AddressDivisionGrouping, a polymorphic type usable with all address sections and division groupings. // Afterwards, you can convert back with ToIP. // // ToDivGrouping can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *IPAddressSection) ToDivGrouping() *AddressDivisionGrouping { return section.ToSectionBase().ToDivGrouping() } // ToSectionBase converts to an AddressSection, a polymorphic type usable with all address sections. // Afterwards, you can convert back with ToIP. // // ToSectionBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *IPAddressSection) ToSectionBase() *AddressSection { return (*AddressSection)(unsafe.Pointer(section)) } // ToIPv6 converts to an IPv6AddressSection if this section originated as an IPv6 section. // If not, ToIPv6 returns nil. // // ToIPv6 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *IPAddressSection) ToIPv6() *IPv6AddressSection { if section.IsIPv6() { return (*IPv6AddressSection)(section) } return nil } // ToIPv4 converts to an IPv4AddressSection if this section originated as an IPv4 section. // If not, ToIPv4 returns nil. // // ToIPv4 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *IPAddressSection) ToIPv4() *IPv4AddressSection { if section.IsIPv4() { return (*IPv4AddressSection)(section) } return nil } // IsIPv4 returns true if this address section originated as an IPv4 section. If so, use ToIPv4 to convert back to the IPv4-specific type. func (section *IPAddressSection) IsIPv4() bool { // we allow nil receivers to allow this to be called following a failed converion like ToIP() return section != nil && section.matchesIPv4SectionType() } // IsIPv6 returns true if this address section originated as an IPv6 section. If so, use ToIPv6 to convert back to the IPv6-specific type. func (section *IPAddressSection) IsIPv6() bool { return section != nil && section.matchesIPv6SectionType() } // GetTrailingSection gets the subsection from the series starting from the given index. // The first segment is at index 0. func (section *IPAddressSection) GetTrailingSection(index int) *IPAddressSection { return section.GetSubSection(index, section.GetSegmentCount()) } // GetSubSection gets the subsection from the series starting from the given index and ending just before the give endIndex. // The first segment is at index 0. func (section *IPAddressSection) GetSubSection(index, endIndex int) *IPAddressSection { return section.getSubSection(index, endIndex).ToIP() } // GetNetworkSection returns a subsection containing the segments with the network bits of the address section. // The returned section will have only as many segments as needed as determined by the existing CIDR network prefix length. // // If this series has no CIDR prefix length, the returned network section will // be the entire series as a prefixed section with prefix length matching the address bit length. func (section *IPAddressSection) GetNetworkSection() *IPAddressSection { return section.getNetworkSection() } // GetNetworkSectionLen returns a subsection containing the segments with the network of the address section, the prefix bits according to the given prefix length. // The returned section will have only as many segments as needed to contain the network. // // The new section will be assigned the given prefix length, // unless the existing prefix length is smaller, in which case the existing prefix length will be retained. func (section *IPAddressSection) GetNetworkSectionLen(prefLen BitCount) *IPAddressSection { return section.getNetworkSectionLen(prefLen) } // GetHostSection returns a subsection containing the segments with the host of the address section, the bits beyond the CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. // // If this series has no prefix length, the returned host section will be the full section. func (section *IPAddressSection) GetHostSection() *IPAddressSection { return section.getHostSection() } // GetHostSectionLen returns a subsection containing the segments with the host of the address section, the bits beyond the given CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. // The returned section will have an assigned prefix length indicating the beginning of the host. func (section *IPAddressSection) GetHostSectionLen(prefLen BitCount) *IPAddressSection { return section.getHostSectionLen(prefLen) } // GetNetworkMask returns the network mask associated with the CIDR network prefix length of this address section. // If this section has no prefix length, then the all-ones mask is returned. func (section *IPAddressSection) GetNetworkMask() *IPAddressSection { return section.getNetworkMask(section.getNetwork()) } // GetHostMask returns the host mask associated with the CIDR network prefix length of this address section. // If this section has no prefix length, then the all-ones mask is returned. func (section *IPAddressSection) GetHostMask() *IPAddressSection { return section.getHostMask(section.getNetwork()) } // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (section *IPAddressSection) CopySubSegments(start, end int, segs []*IPAddressSegment) (count int) { start, end, targetStart := adjust1To1StartIndices(start, end, section.GetDivisionCount(), len(segs)) segs = segs[targetStart:] return section.forEachSubDivision(start, end, func(index int, div *AddressDivision) { segs[index] = div.ToIP() }, len(segs)) } // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. func (section *IPAddressSection) CopySegments(segs []*IPAddressSegment) (count int) { return section.ForEachSegment(func(index int, seg *IPAddressSegment) (stop bool) { if stop = index >= len(segs); !stop { segs[index] = seg } return }) } // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as this section. func (section *IPAddressSection) GetSegments() (res []*IPAddressSegment) { res = make([]*IPAddressSegment, section.GetSegmentCount()) section.CopySegments(res) return } // GetLower returns the section in the range with the lowest numeric value, // which will be the same section if it represents a single value. // For example, for "1.2-3.4.5-6", the section "1.2.4.5" is returned. func (section *IPAddressSection) GetLower() *IPAddressSection { return section.getLower().ToIP() } // GetUpper returns the section in the range with the highest numeric value, // which will be the same section if it represents a single value. // For example, for "1.2-3.4.5-6", the section "1.3.4.6" is returned. func (section *IPAddressSection) GetUpper() *IPAddressSection { return section.getUpper().ToIP() } // ToZeroHost converts the address section to one in which all individual address sections have a host of zero, // the host being the bits following the prefix length. // If the address section has no prefix length, then it returns an all-zero address section. // // The returned section will have the same prefix and prefix length. // // This returns an error if the section is a range of address sections which cannot be converted to a range in which all sections have zero hosts, // because the conversion results in a segment that is not a sequential range of values. func (section *IPAddressSection) ToZeroHost() (res *IPAddressSection, err addrerr.IncompatibleAddressError) { return section.toZeroHost(false) } // ToZeroHostLen converts the address section to one in which all individual sections have a host of zero, // the host being the bits following the given prefix length. // If this address section has the same prefix length, then the returned one will too, otherwise the returned section will have no prefix length. // // This returns an error if the section is a range of which cannot be converted to a range in which all sections have zero hosts, // because the conversion results in a segment that is not a sequential range of values. func (section *IPAddressSection) ToZeroHostLen(prefixLength BitCount) (*IPAddressSection, addrerr.IncompatibleAddressError) { return section.ToZeroHostLen(prefixLength) } // ToZeroNetwork converts the address section to one in which all individual address sections have a network of zero, // the network being the bits within the prefix length. // If the address section has no prefix length, then it returns an all-zero address section. // // The returned address section will have the same prefix length. func (section *IPAddressSection) ToZeroNetwork() *IPAddressSection { return section.toZeroNetwork() } // ToMaxHost converts the address section to one in which all individual address sections have a host of all one-bits, the max value, // the host being the bits following the prefix length. // If the address section has no prefix length, then it returns an all-ones section, the max address section. // // The returned address section will have the same prefix and prefix length. // // This returns an error if the address section is a range of address sections which cannot be converted to a range in which all sections have max hosts, // because the conversion results in a segment that is not a sequential range of values. func (section *IPAddressSection) ToMaxHost() (res *IPAddressSection, err addrerr.IncompatibleAddressError) { return section.toMaxHost() } // ToMaxHostLen converts the address section to one in which all individual address sections have a host of all one-bits, the max host, // the host being the bits following the given prefix length. // If this section has the same prefix length, then the resulting section will too, otherwise the resulting section will have no prefix length. // // This returns an error if the section is a range of address sections which cannot be converted to a range in which all address sections have max hosts, // because the conversion results in a segment that is not a sequential range of values. func (section *IPAddressSection) ToMaxHostLen(prefixLength BitCount) (*IPAddressSection, addrerr.IncompatibleAddressError) { return section.toMaxHostLen(prefixLength) } // WithoutPrefixLen provides the same address section but with no prefix length. The values remain unchanged. func (section *IPAddressSection) WithoutPrefixLen() *IPAddressSection { if !section.IsPrefixed() { return section } return section.withoutPrefixLen() } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address section. // The provided prefix length will be adjusted to these boundaries if necessary. func (section *IPAddressSection) SetPrefixLen(prefixLen BitCount) *IPAddressSection { return section.setPrefixLen(prefixLen).ToIP() } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address section. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this address section has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this address section has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section *IPAddressSection) SetPrefixLenZeroed(prefixLen BitCount) (*IPAddressSection, addrerr.IncompatibleAddressError) { res, err := section.setPrefixLenZeroed(prefixLen) return res.ToIP(), err } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address section. // // If this address section has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (section *IPAddressSection) AdjustPrefixLen(prefixLen BitCount) *IPAddressSection { return section.adjustPrefixLen(prefixLen) } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address section. // // If this address section has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section *IPAddressSection) AdjustPrefixLenZeroed(prefixLen BitCount) (*IPAddressSection, addrerr.IncompatibleAddressError) { return section.adjustPrefixLenZeroed(prefixLen) } // ToPrefixBlock returns the section with the same prefix as this section while the remaining bits span all values. // The returned section will be the block of all sections with the same prefix. // // If this section has no prefix, this section is returned. func (section *IPAddressSection) ToPrefixBlock() *IPAddressSection { return section.toPrefixBlock().ToIP() } // ToPrefixBlockLen returns the section with the same prefix of the given length as this section while the remaining bits span all values. // The returned section will be the block of all sections with the same prefix. func (section *IPAddressSection) ToPrefixBlockLen(prefLen BitCount) *IPAddressSection { return section.toPrefixBlockLen(prefLen).ToIP() } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this address section. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such address section - it is required that the range of values match the range of a prefix block. // If there is no such address section, then nil is returned. func (section *IPAddressSection) AssignPrefixForSingleBlock() *IPAddressSection { return section.assignPrefixForSingleBlock().ToIP() } // AssignMinPrefixForBlock returns an equivalent address section, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this address section. // // In other words, this method assigns a prefix length to this address section matching the largest prefix block in this address section. func (section *IPAddressSection) AssignMinPrefixForBlock() *IPAddressSection { return section.assignMinPrefixForBlock().ToIP() } // ToBlock creates a new block of address sections by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (section *IPAddressSection) ToBlock(segmentIndex int, lower, upper SegInt) *IPAddressSection { return section.toBlock(segmentIndex, lower, upper).ToIP() } // Iterator provides an iterator to iterate through the individual address sections of this address section. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual address sections. // // Call IsMultiple to determine if this instance represents multiple address sections, or GetCount for the count. func (section *IPAddressSection) Iterator() Iterator[*IPAddressSection] { if section == nil { return ipSectionIterator{nilSectIterator()} } return ipSectionIterator{section.sectionIterator(nil)} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this address section, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this address section. // // If the series has no prefix length, then this is equivalent to Iterator. func (section *IPAddressSection) PrefixIterator() Iterator[*IPAddressSection] { return ipSectionIterator{section.prefixIterator(false)} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address section. // Each iterated address section will be a prefix block with the same prefix length as this address section. // // If this address section has no prefix length, then this is equivalent to Iterator. func (section *IPAddressSection) PrefixBlockIterator() Iterator[*IPAddressSection] { return ipSectionIterator{section.prefixIterator(true)} } // BlockIterator Iterates through the address sections that can be obtained by iterating through all the upper segments up to the given segment count. // The segments following remain the same in all iterated sections. func (section *IPAddressSection) BlockIterator(segmentCount int) Iterator[*IPAddressSection] { return ipSectionIterator{section.blockIterator(segmentCount)} } // SequentialBlockIterator iterates through the sequential address sections that make up this address section. // // Practically, this means finding the count of segments for which the segments that follow are not full range, and then using BlockIterator with that segment count. // // Use GetSequentialBlockCount to get the number of iterated elements. func (section *IPAddressSection) SequentialBlockIterator() Iterator[*IPAddressSection] { return ipSectionIterator{section.sequentialBlockIterator()} } // IncrementBoundary returns the item that is the given increment from the range boundaries of this item. // // If the given increment is positive, adds the value to the highest (GetUpper) in the range to produce a new item. // If the given increment is negative, adds the value to the lowest (GetLower) in the range to produce a new item. // If the increment is zero, returns this. // // If this represents just a single value, this item is simply incremented by the given increment value, positive or negative. // // On overflow or underflow, IncrementBoundary returns nil. func (section *IPAddressSection) IncrementBoundary(increment int64) *IPAddressSection { return section.incrementBoundary(increment).ToIP() } // Increment returns the item that is the given increment upwards into the range, // with the increment of 0 returning the first in the range. // // If the increment i matches or exceeds the range count c, then i - c + 1 // is added to the upper item of the range. // An increment matching the count gives you the item just above the highest in the range. // // If the increment is negative, it is added to the lowest of the range. // To get the item just below the lowest of the range, use the increment -1. // // If this represents just a single value, the item is simply incremented by the given increment, positive or negative. // // If this item represents multiple values, a positive increment i is equivalent i + 1 values from the iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the count is equivalent to the same number of iterator values preceding the last value of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On overflow or underflow, Increment returns nil. func (section *IPAddressSection) Increment(increment int64) *IPAddressSection { return section.increment(increment).ToIP() } // SpanWithPrefixBlocks returns an array of prefix blocks that spans the same set of individual address sections as this section. // // Unlike SpanWithPrefixBlocksTo, the result only includes blocks that are a part of this section. func (section *IPAddressSection) SpanWithPrefixBlocks() []*IPAddressSection { if section.IsSequential() { if section.IsSinglePrefixBlock() { return []*IPAddressSection{section} } wrapped := wrapIPSection(section) spanning := getSpanningPrefixBlocks(wrapped, wrapped) return cloneToIPSections(spanning) } wrapped := wrapIPSection(section) return cloneToIPSections(spanWithPrefixBlocks(wrapped)) } // SpanWithSequentialBlocks produces the smallest slice of sequential blocks that cover the same set of sections as this. // // This slice can be shorter than that produced by SpanWithPrefixBlocks and is never longer. func (section *IPAddressSection) SpanWithSequentialBlocks() []*IPAddressSection { if section.IsSequential() { return []*IPAddressSection{section} } wrapped := wrapIPSection(section) return cloneToIPSections(spanWithSequentialBlocks(wrapped)) } // CoverWithPrefixBlock returns the minimal-size prefix block that covers all the individual address sections in this section. // The resulting block will have a larger count than this, unless this section is already a prefix block. func (section *IPAddressSection) CoverWithPrefixBlock() *IPAddressSection { return section.coverWithPrefixBlock() } // ReverseBits returns a new section with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (section *IPAddressSection) ReverseBits(perByte bool) (*IPAddressSection, addrerr.IncompatibleAddressError) { res, err := section.reverseBits(perByte) return res.ToIP(), err } // ReverseBytes returns a new section with the bytes reversed. Any prefix length is dropped. // // If each segment is more than 1 byte long, and the bytes within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, then this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. func (section *IPAddressSection) ReverseBytes() (*IPAddressSection, addrerr.IncompatibleAddressError) { res, err := section.reverseBytes(false) return res.ToIP(), err } // ReverseSegments returns a new section with the segments reversed. func (section *IPAddressSection) ReverseSegments() *IPAddressSection { if section.GetSegmentCount() <= 1 { if section.IsPrefixed() { return section.WithoutPrefixLen() } return section } res, _ := section.reverseSegments( func(i int) (*AddressSegment, addrerr.IncompatibleAddressError) { return section.GetSegment(i).withoutPrefixLen().ToSegmentBase(), nil }, ) return res.ToIP() } // String implements the [fmt.Stringer] interface, returning the normalized string provided by ToNormalizedString, or "" if the receiver is a nil pointer. func (section *IPAddressSection) String() string { if section == nil { return nilString() } return section.toString() } // ToCanonicalString produces a canonical string for the address section. // // For IPv4, dotted octet format, also known as dotted decimal format, is used. // https://datatracker.ietf.org/doc/html/draft-main-ipaddr-text-rep-00#section-2.1 // // For IPv6, RFC 5952 describes canonical string representation. // https://en.wikipedia.org/wiki/IPv6_address#Representation // http://tools.ietf.org/html/rfc5952 // // With IP addresses, the prefix length is included in the string. func (section *IPAddressSection) ToCanonicalString() string { if section == nil { return nilString() } return section.toCanonicalString() } // ToNormalizedString produces a normalized string for the address section. // // For IPv4, it is the same as the canonical string. // // For IPv6, it differs from the canonical string. Zero-segments are not compressed. // // With IP addresses, the prefix length is included in the string. func (section *IPAddressSection) ToNormalizedString() string { if section == nil { return nilString() } return section.toNormalizedString() } // ToCompressedString produces a short representation of this address section while remaining within the confines of standard representation(s) of the address. // // For IPv4, it is the same as the canonical string. // // For IPv6, it differs from the canonical string. It compresses the maximum number of zeros and/or host segments with the IPv6 compression notation '::'. func (section *IPAddressSection) ToCompressedString() string { if section == nil { return nilString() } return section.toCompressedString() } // ToHexString writes this address section as a single hexadecimal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *IPAddressSection) ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toHexString(with0xPrefix) } // ToOctalString writes this address section as a single octal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *IPAddressSection) ToOctalString(with0Prefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toOctalString(with0Prefix) } // ToBinaryString writes this address section as a single binary value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0b" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *IPAddressSection) ToBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toBinaryString(with0bPrefix) } // ToNormalizedWildcardString produces a string similar to the normalized string but avoids the CIDR prefix length. // CIDR addresses will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix notation. func (section *IPAddressSection) ToNormalizedWildcardString() string { if section == nil { return nilString() } return section.toNormalizedWildcardString() } // ToCanonicalWildcardString produces a string similar to the canonical string but avoids the CIDR prefix length. // Address sections with a network prefix length will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix length notation. // IPv6 sections will be compressed according to the canonical representation. // For IPv4 it is the same as ToNormalizedWildcardString. func (section *IPAddressSection) ToCanonicalWildcardString() string { if section == nil { return nilString() } return section.toCanonicalWildcardString() } // ToSegmentedBinaryString writes this IP address section as segments of binary values preceded by the "0b" prefix. func (section *IPAddressSection) ToSegmentedBinaryString() string { if section == nil { return nilString() } return section.toSegmentedBinaryString() } // ToSQLWildcardString create a string similar to that from toNormalizedWildcardString except that // it uses SQL wildcards. It uses '%' instead of '*' and also uses the wildcard '_'. func (section *IPAddressSection) ToSQLWildcardString() string { if section == nil { return nilString() } return section.toSQLWildcardString() } // ToFullString produces a string with no compressed segments and all segments of full length with leading zeros, // which is 4 characters for IPv6 segments and 3 characters for IPv4 segments. func (section *IPAddressSection) ToFullString() string { if section == nil { return nilString() } return section.toFullString() } // ToReverseDNSString generates the reverse-DNS lookup string, // returning an error if this address section is an IPv6 multiple-valued section for which the range cannot be represented. // For "8.255.4.4" it is "4.4.255.8.in-addr.arpa". // For "2001:db8::567:89ab" it is "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa". func (section *IPAddressSection) ToReverseDNSString() (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toReverseDNSString() } // ToPrefixLenString returns a string with a CIDR network prefix length if this address has a network prefix length. // For IPv6, a zero host section will be compressed with "::". For IPv4 the string is equivalent to the canonical string. func (section *IPAddressSection) ToPrefixLenString() string { if section == nil { return nilString() } return section.toPrefixLenString() } // ToSubnetString produces a string with specific formats for subnets. // The subnet string looks like "1.2.*.*" or "1:2::/16". // // In the case of IPv4, this means that wildcards are used instead of a network prefix when a network prefix has been supplied. // In the case of IPv6, when a network prefix has been supplied, the prefix will be shown and the host section will be compressed with "::". func (section *IPAddressSection) ToSubnetString() string { if section == nil { return nilString() } return section.toSubnetString() } // ToCompressedWildcardString produces a string similar to ToNormalizedWildcardString, avoiding the CIDR prefix, but with full IPv6 segment compression as well, including single zero-segments. // For IPv4 it is the same as ToNormalizedWildcardString. func (section *IPAddressSection) ToCompressedWildcardString() string { if section == nil { return nilString() } return section.toCompressedWildcardString() } // ToCustomString creates a customized string from this address section according to the given string option parameters. func (section *IPAddressSection) ToCustomString(stringOptions addrstr.IPStringOptions) string { if section == nil { return nilString() } return section.toCustomString(stringOptions) } // GetSegmentStrings returns a slice with the string for each segment being the string that is normalized with wildcards. func (section *IPAddressSection) GetSegmentStrings() []string { if section == nil { return nil } return section.getSegmentStrings() } var ( rangeWildcard = new(addrstr.WildcardsBuilder).ToWildcards() allWildcards = new(addrstr.WildcardOptionsBuilder).SetWildcardOptions(addrstr.WildcardsAll).ToOptions() wildcardsRangeOnlyNetworkOnly = new(addrstr.WildcardOptionsBuilder).SetWildcards(rangeWildcard).ToOptions() allSQLWildcards = new(addrstr.WildcardOptionsBuilder).SetWildcardOptions(addrstr.WildcardsAll).SetWildcards( new(addrstr.WildcardsBuilder).SetWildcard(SegmentSqlWildcardStr).SetSingleWildcard(SegmentSqlSingleWildcardStr).ToWildcards()).ToOptions() ) // handles prefix block subnets, and ensures segment prefixes match the section prefix func assignPrefix(prefixLength PrefixLen, segments []*AddressDivision, res *IPAddressSection, singleOnly, checkPrefixes bool, boundaryBits BitCount) { prefLen := prefixLength.bitCount() if prefLen < 0 { prefLen = 0 } else if prefLen > boundaryBits { prefLen = boundaryBits prefixLength = cacheBitCount(boundaryBits) } else { prefixLength = cachePrefixLen(prefixLength) // use our own cache of prefix lengths so callers cannot overwrite a section's prefix length } segLen := len(segments) if segLen > 0 { var segProducer func(*AddressDivision, PrefixLen) *AddressDivision applyPrefixSubnet := !singleOnly && isPrefixSubnetDivs(segments, prefLen) if applyPrefixSubnet || checkPrefixes { if applyPrefixSubnet { segProducer = (*AddressDivision).toPrefixedNetworkDivision } else { segProducer = (*AddressDivision).toPrefixedDivision } applyPrefixToSegments( prefLen, segments, res.GetBitsPerSegment(), res.GetBytesPerSegment(), segProducer) if applyPrefixSubnet { res.isMult = res.isMult || res.GetSegment(segLen-1).isMultiple() } } } res.prefixLength = prefixLength return } // Starting from the first host bit according to the prefix, if the section is a sequence of zeros in both low and high values, // followed by a sequence where low values are zero and high values are 1, then the section is a subnet prefix. // // Note that this includes sections where hosts are all zeros, or sections where hosts are full range of values, // so the sequence of zeros can be empty and the sequence of where low values are zero and high values are 1 can be empty as well. // However, if they are both empty, then this returns false, there must be at least one bit in the sequence. func isPrefixSubnetDivs(sectionSegments []*AddressDivision, networkPrefixLength BitCount) bool { segmentCount := len(sectionSegments) if segmentCount == 0 { return false } seg := sectionSegments[0] return isPrefixSubnet( func(segmentIndex int) SegInt { return sectionSegments[segmentIndex].ToSegmentBase().GetSegmentValue() }, func(segmentIndex int) SegInt { return sectionSegments[segmentIndex].ToSegmentBase().GetUpperSegmentValue() }, segmentCount, seg.GetByteCount(), seg.GetBitCount(), seg.ToSegmentBase().GetMaxValue(), networkPrefixLength, zerosOnly) } func applyPrefixToSegments( sectionPrefixBits BitCount, segments []*AddressDivision, segmentBitCount BitCount, segmentByteCount int, segProducer func(*AddressDivision, PrefixLen) *AddressDivision) { var i int if sectionPrefixBits != 0 { i = getNetworkSegmentIndex(sectionPrefixBits, segmentByteCount, segmentBitCount) } for ; i < len(segments); i++ { pref := getPrefixedSegmentPrefixLength(segmentBitCount, sectionPrefixBits, i) if pref != nil { segments[i] = segProducer(segments[i], pref) } } } // called prior to check for prefix subnet. The segments must be created first before that can happen. func createSegmentsUint64( segLen int, highBytes, lowBytes uint64, bytesPerSegment int, bitsPerSegment BitCount, creator addressSegmentCreator, assignedPrefixLength PrefixLen) []*AddressDivision { segmentMask := ^(^SegInt(0) << uint(bitsPerSegment)) lowSegCount := getHostSegmentIndex(64, bytesPerSegment, bitsPerSegment) newSegs := make([]*AddressDivision, segLen) lowIndex := segLen - lowSegCount if lowIndex < 0 { lowIndex = 0 } segmentIndex := segLen - 1 bytes := lowBytes for { for { segmentPrefixLength := getSegmentPrefixLength(bitsPerSegment, assignedPrefixLength, segmentIndex) value := segmentMask & SegInt(bytes) seg := creator.createSegment(value, value, segmentPrefixLength) newSegs[segmentIndex] = seg segmentIndex-- if segmentIndex < lowIndex { break } bytes >>= uint(bitsPerSegment) } if lowIndex == 0 { break } lowIndex = 0 bytes = highBytes } return newSegs } // called prior to check for prefix subnet. The segments must be created first before that can happen. func createSegments( lowerValueProvider, upperValueProvider SegmentValueProvider, segmentCount int, bitsPerSegment BitCount, creator addressSegmentCreator, prefixLength PrefixLen) (segments []*AddressDivision, isMultiple bool) { segments = createSegmentArray(segmentCount) for segmentIndex := 0; segmentIndex < segmentCount; segmentIndex++ { segmentPrefixLength := getSegmentPrefixLength(bitsPerSegment, prefixLength, segmentIndex) var value, value2 SegInt if lowerValueProvider == nil { value = upperValueProvider(segmentIndex) value2 = value } else { value = lowerValueProvider(segmentIndex) if upperValueProvider != nil { value2 = upperValueProvider(segmentIndex) if !isMultiple && value2 != value { isMultiple = true } } else { value2 = value } } seg := creator.createSegment(value, value2, segmentPrefixLength) segments[segmentIndex] = seg } return } ipaddress-go-1.5.4/ipaddr/ipsegment.go000066400000000000000000001026371440250641600177020ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math/big" "strings" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" ) type ipAddressSegmentInternal struct { addressSegmentInternal } func (seg *ipAddressSegmentInternal) isPrefixed() bool { return seg.GetSegmentPrefixLen() != nil } // IsPrefixBlock returns whether the segment has a prefix length and the segment range includes the block of values for that prefix length. // If the prefix length matches the bit count, this returns true. func (seg *ipAddressSegmentInternal) IsPrefixBlock() bool { return seg.isPrefixBlock() } // IsSinglePrefixBlock returns whether the range matches the block of values for a single prefix identified by the prefix length of this address. // This is similar to IsPrefixBlock except that it returns false when the subnet has multiple prefixes. // // What distinguishes this method from ContainsSinglePrefixBlock is that this method returns // false if the series does not have a prefix length assigned to it, // or a prefix length that differs from the prefix length for which ContainsSinglePrefixBlock returns true. // // It is similar to IsPrefixBlock but returns false when there are multiple prefixes. func (seg *ipAddressSegmentInternal) IsSinglePrefixBlock() bool { cache := seg.getCache() if cache != nil { res := cache.isSinglePrefBlock if res != nil { return *res } } if prefLen := seg.GetSegmentPrefixLen(); prefLen != nil { return seg.isSinglePrefixBlock(seg.getDivisionValue(), seg.getUpperDivisionValue(), prefLen.bitCount()) } return false } func (seg *ipAddressSegmentInternal) withoutPrefixLen() *IPAddressSegment { if seg.isPrefixed() { return createAddressDivision(seg.derivePrefixed(nil)).ToIP() } return seg.toIPAddressSegment() } // GetPrefixValueCount returns the count of prefixes in this segment for its prefix length, or the total count if it has no prefix length. func (seg *ipAddressSegmentInternal) GetPrefixValueCount() SegIntCount { prefixLength := seg.GetSegmentPrefixLen() if prefixLength == nil { return seg.GetValueCount() } return getPrefixValueCount(seg.toAddressSegment(), prefixLength.bitCount()) } // GetSegmentPrefixLen returns the network prefix for the segment. // // The network prefix is 16 for an address like "1.2.0.0/16". // // When it comes to each address division or segment, the prefix for the division is the // prefix obtained when applying the address or section prefix. // // For instance, consider the address "1.2.0.0/20". // The first segment has no prefix because the address prefix 20 extends beyond the 8 bits in the first segment, it does not even apply to the segment. // The second segment has no prefix because the address prefix extends beyond bits 9 to 16 which lie in the second segment, it does not apply to that segment either. // The third segment has the prefix 4 because the address prefix 20 corresponds to the first 4 bits in the 3rd segment, // which means that the first 4 bits are part of the network section of the address or segment. // The last segment has the prefix 0 because not a single bit is in the network section of the address or segment // // The division prefixes applied across the address are: nil ... nil (1 to segment bit length) 0 ... 0. // // If the segment has no prefix then nil is returned. func (seg *ipAddressSegmentInternal) GetSegmentPrefixLen() PrefixLen { return seg.getDivisionPrefixLength() } // MatchesWithPrefixMask applies the network mask of the given bit-length to this segment and then compares the result with the given value masked by the same mask, //returning true if the resulting range matches the given single value. func (seg *ipAddressSegmentInternal) MatchesWithPrefixMask(value SegInt, networkBits BitCount) bool { mask := seg.GetSegmentNetworkMask(networkBits) matchingValue := value & mask return matchingValue == (seg.GetSegmentValue()&mask) && matchingValue == (seg.GetUpperSegmentValue()&mask) } func (seg *ipAddressSegmentInternal) checkForPrefixMask() (networkMaskLen, hostMaskLen PrefixLen) { val := seg.GetSegmentValue() if val == 0 { networkMaskLen, hostMaskLen = cacheBitCount(0), cacheBitCount(seg.GetBitCount()) } else { maxVal := seg.GetMaxValue() if val == maxVal { networkMaskLen, hostMaskLen = cacheBitCount(seg.GetBitCount()), cacheBitCount(0) } else { var shifted SegInt trailingOnes := seg.GetTrailingBitCount(true) if trailingOnes == 0 { // can only be 11110000 and not 00000000 trailingZeros := seg.GetTrailingBitCount(false) shifted = (^val & maxVal) >> uint(trailingZeros) if shifted == 0 { networkMaskLen = cacheBitCount(seg.GetBitCount() - trailingZeros) } } else { // can only be 00001111 and not 11111111 shifted = val >> uint(trailingOnes) if shifted == 0 { hostMaskLen = cacheBitCount(seg.GetBitCount() - trailingOnes) } } } } return } // GetBlockMaskPrefixLen returns the prefix length if this address segment is equivalent to the mask for a CIDR prefix block. // Otherwise, it returns nil. // A CIDR network mask is a segment with all ones in the network bits and then all zeros in the host bits. // A CIDR host mask is a segment with all zeros in the network bits and then all ones in the host bits. // The prefix length is the bit-length of the network bits. // // Also, keep in mind that the prefix length returned by this method is not equivalent to the prefix length of this segment. // The prefix length returned here indicates the whether the value of this segment can be used as a mask for the network and host // bits of any other segment. Therefore, the two values can be different values, or one can be nil while the other is not. // // This method applies only to the lower value of the range if this segment represents multiple values. func (seg *ipAddressSegmentInternal) GetBlockMaskPrefixLen(network bool) PrefixLen { hostLength := seg.GetTrailingBitCount(!network) var shifted SegInt val := seg.GetSegmentValue() if network { maxVal := seg.GetMaxValue() shifted = (^val & maxVal) >> uint(hostLength) } else { shifted = val >> uint(hostLength) } if shifted == 0 { return cacheBitCount(seg.GetBitCount() - hostLength) } return nil } func (seg *ipAddressSegmentInternal) getUpperStringMasked(radix int, uppercase bool, appendable *strings.Builder) { if seg.isPrefixed() { upperValue := seg.GetUpperSegmentValue() mask := seg.GetSegmentNetworkMask(seg.GetSegmentPrefixLen().bitCount()) upperValue &= mask toUnsignedStringCased(DivInt(upperValue), radix, 0, uppercase, appendable) } else { seg.getUpperString(radix, uppercase, appendable) } } func (seg *ipAddressSegmentInternal) getStringAsLower() string { if seg.divisionValues != nil { if cache := seg.getCache(); cache != nil { return cacheStr(&cache.cachedString, seg.getDefaultLowerString) } } return seg.getDefaultLowerString() } func (seg *ipAddressSegmentInternal) getString() string { stringer := func() string { if !seg.isMultiple() || seg.IsSinglePrefixBlock() { //covers the case of !isMultiple, ie single addresses, when there is no prefix or the prefix is the bit count return seg.getDefaultLowerString() } else if seg.IsFullRange() { return seg.getDefaultSegmentWildcardString() } upperValue := seg.getUpperSegmentValue() if seg.IsPrefixBlock() { upperValue &= seg.GetSegmentNetworkMask(seg.getDivisionPrefixLength().bitCount()) } return seg.getDefaultRangeStringVals(seg.getDivisionValue(), DivInt(upperValue), seg.getDefaultTextualRadix()) } if seg.divisionValues != nil { if cache := seg.getCache(); cache != nil { return cacheStr(&cache.cachedString, stringer) } } return stringer() } func (seg *ipAddressSegmentInternal) getWildcardString() string { stringer := func() string { if !seg.isPrefixed() || !seg.isMultiple() { return seg.getString() } else if seg.IsFullRange() { return seg.getDefaultSegmentWildcardString() } return seg.getDefaultRangeString() } if seg.divisionValues != nil { if cache := seg.getCache(); cache != nil { return cacheStr(&cache.cachedWildcardString, stringer) } } return stringer() } func (seg *ipAddressSegmentInternal) setStandardString( addressStr string, isStandardString bool, lowerStringStartIndex, lowerStringEndIndex int, originalLowerValue SegInt) { if cache := seg.getCache(); cache != nil { if isStandardString && originalLowerValue == seg.getSegmentValue() { cacheStr(&cache.cachedString, func() string { return addressStr[lowerStringStartIndex:lowerStringEndIndex] }) } } } func (seg *ipAddressSegmentInternal) setWildcardString( addressStr string, isStandardString bool, lowerStringStartIndex, lowerStringEndIndex int, lowerValue SegInt) { if cache := seg.getCache(); cache != nil { if isStandardString && lowerValue == seg.getSegmentValue() && lowerValue == seg.getUpperSegmentValue() { cacheStr(&cache.cachedWildcardString, func() string { return addressStr[lowerStringStartIndex:lowerStringEndIndex] }) } } } func (seg *ipAddressSegmentInternal) setRangeStandardString( addressStr string, isStandardString, isStandardRangeString bool, lowerStringStartIndex, lowerStringEndIndex, upperStringEndIndex int, rangeLower, rangeUpper SegInt) { if cache := seg.getCache(); cache != nil { if seg.IsSinglePrefixBlock() { if isStandardString && rangeLower == seg.getSegmentValue() { cacheStr(&cache.cachedString, func() string { return addressStr[lowerStringStartIndex:lowerStringEndIndex] }) } } else if seg.IsFullRange() { cacheStrPtr(&cache.cachedString, &segmentWildcardStr) } else if isStandardRangeString && rangeLower == seg.getSegmentValue() { upper := seg.getUpperSegmentValue() if seg.isPrefixed() { upper &= seg.GetSegmentNetworkMask(seg.getDivisionPrefixLength().bitCount()) } if rangeUpper == upper { cacheStr(&cache.cachedString, func() string { return addressStr[lowerStringStartIndex:upperStringEndIndex] }) } } } } func (seg *ipAddressSegmentInternal) setRangeWildcardString( addressStr string, isStandardRangeString bool, lowerStringStartIndex, upperStringEndIndex int, rangeLower, rangeUpper SegInt) { if cache := seg.getCache(); cache != nil { if seg.IsFullRange() { cacheStrPtr(&cache.cachedWildcardString, &segmentWildcardStr) } else if isStandardRangeString && rangeLower == seg.getSegmentValue() && rangeUpper == seg.getUpperSegmentValue() { cacheStr(&cache.cachedString, func() string { return addressStr[lowerStringStartIndex:upperStringEndIndex] }) } } } func (seg *ipAddressSegmentInternal) toIPAddressSegment() *IPAddressSegment { return (*IPAddressSegment)(unsafe.Pointer(seg)) } //// only needed for godoc / pkgsite // GetBitCount returns the number of bits in each value comprising this address item. func (seg *ipAddressSegmentInternal) GetBitCount() BitCount { return seg.addressSegmentInternal.GetBitCount() } // GetByteCount returns the number of bytes required for each value comprising this address item. func (seg *ipAddressSegmentInternal) GetByteCount() int { return seg.addressSegmentInternal.GetByteCount() } // GetValue returns the lowest value in the address segment range as a big integer. func (seg *ipAddressSegmentInternal) GetValue() *BigDivInt { return seg.addressSegmentInternal.GetValue() } // GetUpperValue returns the highest value in the address segment range as a big integer. func (seg *ipAddressSegmentInternal) GetUpperValue() *BigDivInt { return seg.addressSegmentInternal.GetUpperValue() } // Bytes returns the lowest value in the address segment range as a byte slice. func (seg *ipAddressSegmentInternal) Bytes() []byte { return seg.addressSegmentInternal.Bytes() } // UpperBytes returns the highest value in the address segment range as a byte slice. func (seg *ipAddressSegmentInternal) UpperBytes() []byte { return seg.addressSegmentInternal.UpperBytes() } // CopyBytes copies the lowest value in the address segment range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (seg *ipAddressSegmentInternal) CopyBytes(bytes []byte) []byte { return seg.addressSegmentInternal.CopyBytes(bytes) } // CopyUpperBytes copies the highest value in the address segment range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (seg *ipAddressSegmentInternal) CopyUpperBytes(bytes []byte) []byte { return seg.addressSegmentInternal.CopyUpperBytes(bytes) } // IsZero returns whether this segment matches exactly the value of zero. func (seg *ipAddressSegmentInternal) IsZero() bool { return seg.addressSegmentInternal.IsZero() } // IncludesZero returns whether this segment includes the value of zero within its range. func (seg *ipAddressSegmentInternal) IncludesZero() bool { return seg.addressSegmentInternal.IncludesZero() } // IsMax returns whether this segment matches exactly the maximum possible value, the value whose bits are all ones. func (seg *ipAddressSegmentInternal) IsMax() bool { return seg.addressSegmentInternal.IsMax() } // IncludesMax returns whether this segment includes the max value, the value whose bits are all ones, within its range. func (seg *ipAddressSegmentInternal) IncludesMax() bool { return seg.addressSegmentInternal.IncludesMax() } // IsFullRange returns whether the segment range includes all possible values for its bit length. // // This is true if and only if both IncludesZero and IncludesMax return true. func (seg *ipAddressSegmentInternal) IsFullRange() bool { return seg.addressSegmentInternal.IsFullRange() } // ContainsPrefixBlock returns whether the division range includes the block of values for the given prefix length. func (seg *ipAddressSegmentInternal) ContainsPrefixBlock(prefixLen BitCount) bool { return seg.addressSegmentInternal.ContainsPrefixBlock(prefixLen) } // ContainsSinglePrefixBlock returns whether the segment range matches exactly the block of values for the given prefix length and has just a single prefix for that prefix length. func (seg *ipAddressSegmentInternal) ContainsSinglePrefixBlock(prefixLen BitCount) bool { return seg.addressSegmentInternal.ContainsSinglePrefixBlock(prefixLen) } // GetMinPrefixLenForBlock returns the smallest prefix length such that this segment includes the block of all values for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this segment represents a single value, this returns the bit count. func (seg *ipAddressSegmentInternal) GetMinPrefixLenForBlock() BitCount { return seg.addressSegmentInternal.GetMinPrefixLenForBlock() } // GetPrefixLenForSingleBlock returns a prefix length for which there is only one prefix in this segment, // and the range of values in this segment matches the block of all values for that prefix. // // If the range of segment values can be described this way, then this method returns the same value as GetMinPrefixLenForBlock. // // If no such prefix length exists, returns nil. // // If this segment represents a single value, this returns the bit count of the segment. func (seg *ipAddressSegmentInternal) GetPrefixLenForSingleBlock() PrefixLen { return seg.addressSegmentInternal.GetPrefixLenForSingleBlock() } // IsSinglePrefix determines if the segment has a single prefix value for the given prefix length. You can call GetPrefixCountLen to get the count of prefixes. func (seg *ipAddressSegmentInternal) IsSinglePrefix(divisionPrefixLength BitCount) bool { return seg.addressSegmentInternal.IsSinglePrefix(divisionPrefixLength) } // PrefixContains returns whether the prefix values in the prefix of the given segment are also prefix values in this segment. // It returns whether the prefix of this segment contains the prefix of the given segment. func (seg *ipAddressSegmentInternal) PrefixContains(other AddressSegmentType, prefixLength BitCount) bool { return seg.addressSegmentInternal.PrefixContains(other, prefixLength) } // PrefixEqual returns whether the prefix bits of this segment match the same bits of the given segment. // It returns whether the two segments share the same range of prefix values using the given prefix length. func (seg *ipAddressSegmentInternal) PrefixEqual(other AddressSegmentType, prefixLength BitCount) bool { return seg.addressSegmentInternal.PrefixEqual(other, prefixLength) } // GetSegmentValue returns the lower value of the segment value range. func (seg *ipAddressSegmentInternal) GetSegmentValue() SegInt { return seg.addressSegmentInternal.GetSegmentValue() } // GetUpperSegmentValue returns the upper value of the segment value range. func (seg *ipAddressSegmentInternal) GetUpperSegmentValue() SegInt { return seg.addressSegmentInternal.GetUpperSegmentValue() } // Matches returns true if the segment range matches the given single value. func (seg *ipAddressSegmentInternal) Matches(value SegInt) bool { return seg.addressSegmentInternal.Matches(value) } // MatchesWithMask applies the mask to this segment and then compares the result with the given value, // returning true if the range of the resulting segment matches that single value. func (seg *ipAddressSegmentInternal) MatchesWithMask(value, mask SegInt) bool { return seg.addressSegmentInternal.MatchesWithMask(value, mask) } // MatchesValsWithMask applies the mask to this segment and then compares the result with the given values, // returning true if the range of the resulting segment matches the given range. func (seg *ipAddressSegmentInternal) MatchesValsWithMask(lowerValue, upperValue, mask SegInt) bool { return seg.addressSegmentInternal.MatchesValsWithMask(lowerValue, upperValue, mask) } // GetPrefixCountLen returns the count of the number of distinct prefix values for the given prefix length in the range of values of this segment. func (seg *ipAddressSegmentInternal) GetPrefixCountLen(segmentPrefixLength BitCount) *big.Int { return seg.addressSegmentInternal.GetPrefixCountLen(segmentPrefixLength) } // GetPrefixValueCountLen returns the same value as GetPrefixCountLen as an integer. func (seg *ipAddressSegmentInternal) GetPrefixValueCountLen(segmentPrefixLength BitCount) SegIntCount { return seg.addressSegmentInternal.GetPrefixValueCountLen(segmentPrefixLength) } // GetValueCount returns the same value as GetCount as an integer. func (seg *ipAddressSegmentInternal) GetValueCount() SegIntCount { return seg.addressSegmentInternal.GetValueCount() } // GetMaxValue gets the maximum possible value for this type or version of segment, determined by the number of bits. // // For the highest range value of this particular segment, use GetUpperSegmentValue. func (seg *ipAddressSegmentInternal) GetMaxValue() SegInt { return seg.addressSegmentInternal.GetMaxValue() } // TestBit returns true if the bit in the lower value of this segment at the given index is 1, where index 0 refers to the least significant bit. // In other words, it computes (bits & (1 << n)) != 0), using the lower value of this section. // TestBit will panic if n < 0, or if it matches or exceeds the bit count of this item. func (seg *ipAddressSegmentInternal) TestBit(n BitCount) bool { return seg.addressSegmentInternal.TestBit(n) } // IsOneBit returns true if the bit in the lower value of this segment at the given index is 1, where index 0 refers to the most significant bit. // IsOneBit will panic if bitIndex is less than zero, or if it is larger than the bit count of this item. func (seg *ipAddressSegmentInternal) IsOneBit(segmentBitIndex BitCount) bool { return seg.addressSegmentInternal.IsOneBit(segmentBitIndex) } // ToNormalizedString produces a string that is consistent for all address segments of the same type and version. // IPv4 segments use base 10, while IPv6 segments use base 16. func (seg *ipAddressSegmentInternal) ToNormalizedString() string { return seg.addressSegmentInternal.ToNormalizedString() } // ToHexString writes this address segment as a single hexadecimal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // For segments, the error is always nil. func (seg *ipAddressSegmentInternal) ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { return seg.addressSegmentInternal.ToHexString(with0xPrefix) } // ReverseBits returns a segment with the bits reversed. // // If this segment represents a range of values that cannot be reversed, then this returns an error. // // To be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // Otherwise the result is not contiguous and thus cannot be represented by a sequential range of values. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (seg *ipAddressSegmentInternal) ReverseBits(perByte bool) (res *AddressSegment, err addrerr.IncompatibleAddressError) { return seg.addressSegmentInternal.ReverseBits(perByte) } // ReverseBytes returns a segment with the bytes reversed. // // If this segment represents a range of values that cannot be reversed, then this returns an error. // // To be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // Otherwise the result is not contiguous and thus cannot be represented by a sequential range of values. func (seg *ipAddressSegmentInternal) ReverseBytes() (res *AddressSegment, err addrerr.IncompatibleAddressError) { return seg.addressSegmentInternal.ReverseBytes() } //// end needed for godoc / pkgsite // IPAddressSegment represents a single segment of an IP address. An IP segment contains a single value or a range of sequential values, a prefix length, and it has an assigned bit length. // // For IPv4, segments are 1 byte. For IPv6, they are two bytes. // // IPAddressSegment objects are immutable and thus also concurrency-safe. // // See AddressSegment for more details regarding segments. type IPAddressSegment struct { ipAddressSegmentInternal } // GetLower returns a segment representing just the lowest value in the range, which will be the same segment if it represents a single value. func (seg *IPAddressSegment) GetLower() *IPAddressSegment { return seg.getLower().ToIP() } // GetUpper returns a segment representing just the highest value in the range, which will be the same segment if it represents a single value. func (seg *IPAddressSegment) GetUpper() *IPAddressSegment { return seg.getUpper().ToIP() } // IsMultiple returns whether this segment represents multiple values. func (seg *IPAddressSegment) IsMultiple() bool { return seg != nil && seg.isMultiple() } // GetCount returns the count of possible distinct values for this item. // If not representing multiple values, the count is 1. // // For instance, a segment with the value range of 3-7 has count 5. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (seg *IPAddressSegment) GetCount() *big.Int { if seg == nil { return bigZero() } return seg.getCount() } // Contains returns whether this is same type and version as the given segment and whether it contains all values in the given segment. func (seg *IPAddressSegment) Contains(other AddressSegmentType) bool { if seg == nil { return other == nil || other.ToSegmentBase() == nil } return seg.contains(other) } // Equal returns whether the given segment is equal to this segment. // Two segments are equal if they match: // - type/version IPv4, IPv6 // - value range // Prefix lengths are ignored. func (seg *IPAddressSegment) Equal(other AddressSegmentType) bool { if seg == nil { return other == nil || other.ToDiv() == nil //return seg.getAddrType() == zeroType && other.(StandardDivisionType).ToDiv() == nil } return seg.equal(other) } // Compare returns a negative integer, zero, or a positive integer if this address segment is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (seg *IPAddressSegment) Compare(item AddressItem) int { return CountComparator.Compare(seg, item) } // CompareSize compares the counts of two items, the number of individual values within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether this represents more individual values than another. // // CompareSize returns a positive integer if this segment has a larger count than the item given, zero if they are the same, or a negative integer if the other has a larger count. func (seg *IPAddressSegment) CompareSize(other AddressItem) int { if seg == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return seg.compareSize(other) } // ContainsPrefixBlock returns whether the division range includes the block of values for the given prefix length. func (seg *IPAddressSegment) ContainsPrefixBlock(divisionPrefixLen BitCount) bool { return seg.containsPrefixBlock(divisionPrefixLen) } // ToPrefixedNetworkSegment returns a segment with the network bits matching this segment but the host bits converted to zero. // The new segment will be assigned the given prefix length. func (seg *IPAddressSegment) ToPrefixedNetworkSegment(segmentPrefixLength PrefixLen) *IPAddressSegment { return seg.toPrefixedNetworkDivision(segmentPrefixLength).ToIP() } // ToNetworkSegment returns a segment with the network bits matching this segment but the host bits converted to zero. // The new segment will have no assigned prefix length. func (seg *IPAddressSegment) ToNetworkSegment(segmentPrefixLength PrefixLen) *IPAddressSegment { return seg.toNetworkDivision(segmentPrefixLength, false).ToIP() } // ToPrefixedHostSegment returns a segment with the host bits matching this segment but the network bits converted to zero. // The new segment will be assigned the given prefix length. func (seg *IPAddressSegment) ToPrefixedHostSegment(segmentPrefixLength PrefixLen) *IPAddressSegment { return seg.toPrefixedHostDivision(segmentPrefixLength).ToIP() } // ToHostSegment returns a segment with the host bits matching this segment but the network bits converted to zero. // The new segment will have no assigned prefix length. func (seg *IPAddressSegment) ToHostSegment(segmentPrefixLength PrefixLen) *IPAddressSegment { return seg.toHostDivision(segmentPrefixLength, false).ToIP() } // Iterator provides an iterator to iterate through the individual address segments of this address segment. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual address segments. // // Call IsMultiple to determine if this instance represents multiple address segments, or GetValueCount for the count. func (seg *IPAddressSegment) Iterator() Iterator[*IPAddressSegment] { if seg == nil { return ipSegmentIterator{nilSegIterator()} } return ipSegmentIterator{seg.iterator()} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address segment. // Each iterated address segment will be a prefix block with the same prefix length as this address segment. // // If this address segment has no prefix length, then this is equivalent to Iterator. func (seg *IPAddressSegment) PrefixBlockIterator() Iterator[*IPAddressSegment] { return ipSegmentIterator{seg.prefixBlockIterator()} } // PrefixedBlockIterator provides an iterator to iterate through the individual prefix blocks of the given prefix length in this segment, // one for each prefix of this address or subnet. // // It is similar to PrefixBlockIterator except that this method allows you to specify the prefix length. func (seg *IPAddressSegment) PrefixedBlockIterator(segmentPrefixLen BitCount) Iterator[*IPAddressSegment] { return ipSegmentIterator{seg.prefixedBlockIterator(segmentPrefixLen)} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this segment, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this segment. // // If this address segment has no prefix length, then this is equivalent to Iterator. func (seg *IPAddressSegment) PrefixIterator() Iterator[*IPAddressSegment] { return ipSegmentIterator{seg.prefixIterator()} } // IsPrefixed returns whether this section has an associated prefix length. func (seg *IPAddressSegment) IsPrefixed() bool { return seg != nil && seg.isPrefixed() } // WithoutPrefixLen returns a segment with the same value range but without a prefix length. func (seg *IPAddressSegment) WithoutPrefixLen() *IPAddressSegment { if !seg.IsPrefixed() { return seg } return seg.withoutPrefixLen() } // IsIPv4 returns true if this segment originated as an IPv4 segment. If so, use ToIPv4 to convert back to the IPv4-specific type. func (seg *IPAddressSegment) IsIPv4() bool { return seg != nil && seg.matchesIPv4Segment() } // IsIPv6 returns true if this segment originated as an IPv6 segment. If so, use ToIPv6 to convert back to the IPv6-specific type. func (seg *IPAddressSegment) IsIPv6() bool { return seg != nil && seg.matchesIPv6Segment() } // ToDiv converts to an AddressDivision, a polymorphic type usable with all address segments and divisions. // Afterwards, you can convert back with ToIP. // // ToDiv can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *IPAddressSegment) ToDiv() *AddressDivision { return seg.ToSegmentBase().ToDiv() } // ToSegmentBase converts to an AddressSegment, a polymorphic type usable with all address segments. // Afterwards, you can convert back with ToIP. // // ToSegmentBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *IPAddressSegment) ToSegmentBase() *AddressSegment { return (*AddressSegment)(unsafe.Pointer(seg)) } // ToIPv4 converts to an IPv4AddressSegment if this segment originated as an IPv4 segment. // If not, ToIPv4 returns nil. // // ToIPv4 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *IPAddressSegment) ToIPv4() *IPv4AddressSegment { if seg.IsIPv4() { return (*IPv4AddressSegment)(seg) } return nil } // ToIPv6 converts to an IPv6AddressSegment if this segment originated as an IPv6 segment. // If not, ToIPv6 returns nil. // // ToIPv6 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *IPAddressSegment) ToIPv6() *IPv6AddressSegment { if seg.IsIPv6() { return (*IPv6AddressSegment)(seg) } return nil } // GetString produces a normalized string to represent the segment. // If the segment is a CIDR network prefix block for its prefix length, then the string contains only the lower value of the block range. // Otherwise, the explicit range will be printed. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (seg *IPAddressSegment) GetString() string { if seg == nil { return nilString() } return seg.getString() } // GetWildcardString produces a normalized string to represent the segment, favouring wildcards and range characters while ignoring any network prefix length. // The explicit range of a range-valued segment will be printed. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and the bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (seg *IPAddressSegment) GetWildcardString() string { if seg == nil { return nilString() } return seg.getWildcardString() } // String produces a string that is useful when a segment string is provided with no context. // If the segment was originally constructed as an IPv4 address segment it uses decimal, otherwise hexadecimal. // It uses a string prefix for hex ("0x"), and does not use the wildcard '*', because division size is variable, so '*' is ambiguous. // GetWildcardString is more appropriate in context with other segments or divisions. It does not use a string prefix and uses '*' for full-range segments. // GetString is more appropriate in context with prefix lengths, it uses zeros instead of wildcards with full prefix block ranges alongside prefix lengths. func (seg *IPAddressSegment) String() string { if seg == nil { return nilString() } return seg.toString() } ipaddress-go-1.5.4/ipaddr/ipseqrange.go000066400000000000000000001305461440250641600200450ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math/big" "math/bits" "net" "net/netip" "sort" "strings" "unsafe" ) // DefaultSeqRangeSeparator is the low to high value separator used when creating strings for IP ranges. const DefaultSeqRangeSeparator = " -> " type rangeCache struct { cachedCount *big.Int } // SequentialRangeConstraint is the generic type constraint for an IP address sequential range. type SequentialRangeConstraint[T any] interface { AddressType // cannot use IPAddressType here because ToAddressString() results in a circular dependency, SequentialRangeConstraint -> IPAddressType -> IPAddressString -> SequentialRange -> SequentialRangeConstraint IPAddressRange comparable ToIP() *IPAddress PrefixedConstraint[T] Increment(int64) T GetLower() T GetUpper() T CoverWithPrefixBlockTo(T) T SpanWithPrefixBlocksTo(T) []T SpanWithSequentialBlocksTo(T) []T SpanWithPrefixBlocks() []T IncludesZeroHostLen(BitCount) bool IncludesMaxHostLen(BitCount) bool Format(state fmt.State, verb rune) rangeIterator(upper T, valsAreMultiple bool, prefixLen PrefixLen, segProducer func(addr *IPAddress, index int) *IPAddressSegment, segmentIteratorProducer func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment], segValueComparator func(seg1, seg2 *IPAddress, index int) bool, networkSegmentIndex, hostSegmentIndex int, prefixedSegIteratorProducer func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment], ) Iterator[T] equalsSameVersion(AddressType) bool getLowestHighestAddrs() (lower, upper T) getAddrType() addrType } var ( _ SequentialRange[*IPAddress] _ SequentialRange[*IPv4Address] _ SequentialRange[*IPv6Address] ) // SequentialRange represents an arbitrary range of consecutive IP addresses, from a lower address to an upper address, inclusive. // // For the generic type T you can choose *IPAddress, *IPv4Address, or *IPv6Address. // // This type allows the representation of any sequential address range, including those that cannot be represented by [IPAddress] or [IPAddressString]. // // [IPAddress] and [IPAddressString] allow you to specify a range of values for each segment, allowing // for single addresses, any address CIDR prefix subnet (for example, "1.2.0.0/16" or "1:2:3:4::/64") or any subnet that can be represented with segment ranges (for example, "1.2.0-255.*" or "1:2:3:4:*"). // See [IPAddressString] for details. // [IPAddressString] and [IPAddress] cover all potential subnets and addresses that can be represented by a single address string of 4 or less segments for IPv4, and 8 or less segments for IPv6. // In contrast, this type covers any sequential address range. // // String representations of this type include the full address for both the lower and upper bounds of the range. // // The zero value is a range from the zero-valued [IPAddress] to itself. // // For a range of type SequentialRange[*IPAddress], the range spans from an IPv4 address to another IPv4 address, // or from an IPv6 address to another IPv6 address. A sequential range cannot include both IPv4 and IPv6 addresses. type SequentialRange[T SequentialRangeConstraint[T]] struct { lower, upper T isMultiple bool // set on construction, even for zero values cache *rangeCache } func nilConvert[T SequentialRangeConstraint[T]]() (t T) { anyt := any(t) if _, ok := anyt.(*IPv6Address); ok { t = any(zeroIPv6).(T) } else if _, ok := anyt.(*IPv4Address); ok { t = any(zeroIPv4).(T) } else if _, ok := anyt.(*IPAddress); ok { t = any(zeroIPAddr).(T) } return } func (rng *SequentialRange[T]) init() *SequentialRange[T] { var t T if rng.lower == t { // nil for pointers t = nilConvert[T]() zeroSeqRange := newSequRange(t, t) return zeroSeqRange } return rng } // GetIPVersion returns the IP version of this IP address sequential range func (rng *SequentialRange[T]) GetIPVersion() IPVersion { return rng.init().lower.GetIPVersion() } func (rng *SequentialRange[T]) getCachedCount(copy bool) (res *big.Int) { cache := rng.cache count := (*big.Int)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.cachedCount)))) if count == nil { if !rng.IsMultiple() { count = bigOne() } else { lower := rng.lower upper := rng.upper if ipv4Lower, ok := any(lower).(*IPv4Address); ok { ipv4Upper := any(upper).(*IPv4Address) val := int64(ipv4Upper.Uint32Value()) - int64(ipv4Lower.Uint32Value()) + 1 count = new(big.Int).SetInt64(val) } else { count = upper.GetValue() res = lower.GetValue() count.Sub(count, res).Add(count, bigOneConst()) res.Set(count) } } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.cachedCount)) atomicStorePointer(dataLoc, unsafe.Pointer(count)) } if res == nil { if copy { res = new(big.Int).Set(count) } else { res = count } } return } // GetPrefixCountLen returns the count of the number of distinct values within the prefix part of the range of addresses. func (rng *SequentialRange[T]) GetPrefixCountLen(prefixLen BitCount) *big.Int { if !rng.IsMultiple() { // also checks for zero-ranges return bigOne() } bitCount := rng.lower.GetBitCount() if prefixLen <= 0 { return bigOne() } else if prefixLen >= bitCount { return rng.GetCount() } shiftAdjustment := bitCount - prefixLen lower := rng.lower if ipv4Lower, ok := any(lower).(*IPv4Address); ok { ipv4Upper := any(rng.upper).(*IPv4Address) upperAdjusted := ipv4Upper.Uint32Value() >> uint(shiftAdjustment) lowerAdjusted := ipv4Lower.Uint32Value() >> uint(shiftAdjustment) result := int64(upperAdjusted) - int64(lowerAdjusted) + 1 return new(big.Int).SetInt64(result) } upperVal := rng.upper.GetValue() ushiftAdjustment := uint(shiftAdjustment) upperVal.Rsh(upperVal, ushiftAdjustment) lowerVal := lower.GetValue() lowerVal.Rsh(lowerVal, ushiftAdjustment) upperVal.Sub(upperVal, lowerVal).Add(upperVal, bigOneConst()) return upperVal } // IsSequential returns whether the address or subnet represents a range of values that are sequential. // // IP address sequential ranges are sequential by definition, so this returns true. func (rng *SequentialRange[T]) IsSequential() bool { return true } // ContainsPrefixBlock returns whether the range contains the block of addresses for the given prefix length. // // Unlike ContainsSinglePrefixBlock, whether there are multiple prefix values for the given prefix length makes no difference. // // Use GetMinPrefixLenForBlock to determine whether there is a prefix length for which this method returns true. func (rng *SequentialRange[T]) ContainsPrefixBlock(prefixLen BitCount) bool { lower := rng.lower upper := rng.upper if lower == upper { // also handles zero-value case nil lower and upper return true } prefixLen = checkSubnet(lower, prefixLen) divCount := lower.GetDivisionCount() bitsPerSegment := lower.GetBitsPerSegment() i := getHostSegmentIndex(prefixLen, lower.GetBytesPerSegment(), bitsPerSegment) if i < divCount { div := lower.GetGenericSegment(i) upperDiv := upper.GetGenericSegment(i) segmentPrefixLength := getPrefixedSegmentPrefixLength(bitsPerSegment, prefixLen, i) if !isPrefixBlockVals(DivInt(div.GetSegmentValue()), DivInt(upperDiv.GetSegmentValue()), segmentPrefixLength.bitCount(), div.GetBitCount()) { return false } for i++; i < divCount; i++ { div = lower.GetGenericSegment(i) upperDiv = upper.GetGenericSegment(i) //is full range? if !div.IncludesZero() || !upperDiv.IncludesMax() { return false } } } return true } // ContainsSinglePrefixBlock returns whether this address range contains a single prefix block for the given prefix length. // // This means there is only one prefix value for the given prefix length, and it also contains the full prefix block for that prefix, all addresses with that prefix. // // Use GetPrefixLenForSingleBlock to determine whether there is a prefix length for which this method returns true. func (rng *SequentialRange[T]) ContainsSinglePrefixBlock(prefixLen BitCount) bool { lower := rng.lower upper := rng.upper if lower == upper { // also handles zero-value case nil lower and upper return true } prefixLen = checkSubnet(lower, prefixLen) var prevBitCount BitCount divCount := lower.GetDivisionCount() for i := 0; i < divCount; i++ { div := lower.GetGenericSegment(i) upperDiv := upper.GetGenericSegment(i) bitCount := div.GetBitCount() totalBitCount := bitCount + prevBitCount if prefixLen >= totalBitCount { if !segValSame(div.GetSegmentValue(), upperDiv.GetSegmentValue()) { return false } } else { divPrefixLen := prefixLen - prevBitCount if !isPrefixBlockVals(DivInt(div.GetSegmentValue()), DivInt(upperDiv.GetSegmentValue()), divPrefixLen, div.GetBitCount()) { return false } for i++; i < divCount; i++ { div = lower.GetGenericSegment(i) upperDiv = upper.GetGenericSegment(i) if !div.IncludesZero() || !upperDiv.IncludesMax() { return false } } return true } prevBitCount = totalBitCount } return true } // GetPrefixLenForSingleBlock returns a prefix length for which there is only one prefix in this range, // and the range of values in this range matches the block of all values for that prefix. // // If the range can be described this way, then this method returns the same value as GetMinPrefixLenForBlock. // // If no such prefix length exists, returns nil. // // If this item represents a single value, this returns the bit count. func (rng *SequentialRange[T]) GetPrefixLenForSingleBlock() PrefixLen { rng = rng.init() lower := rng.lower upper := rng.upper count := lower.GetSegmentCount() segBitCount := lower.GetBitsPerSegment() maxSegValue := ^(^SegInt(0) << uint(segBitCount)) totalPrefix := BitCount(0) for i := 0; i < count; i++ { lowerSeg := lower.GetGenericSegment(i) upperSeg := upper.GetGenericSegment(i) segPrefix := getPrefixLenForSingleBlock(DivInt(lowerSeg.GetSegmentValue()), DivInt(upperSeg.GetSegmentValue()), segBitCount) if segPrefix == nil { return nil } dabits := segPrefix.bitCount() totalPrefix += dabits if dabits < segBitCount { //remaining segments must be full range or we return nil for i++; i < count; i++ { lowerSeg = lower.GetGenericSegment(i) upperSeg = upper.GetGenericSegment(i) if lowerSeg.GetSegmentValue() != 0 { return nil } else if upperSeg.GetSegmentValue() != maxSegValue { return nil } } } } return cacheBitCount(totalPrefix) } // GetMinPrefixLenForBlock returns the smallest prefix length such that this includes the block of addresses for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. func (rng *SequentialRange[T]) GetMinPrefixLenForBlock() BitCount { rng = rng.init() lower := rng.lower upper := rng.upper count := lower.GetSegmentCount() totalPrefix := lower.GetBitCount() segBitCount := lower.GetBitsPerSegment() for i := count - 1; i >= 0; i-- { lowerSeg := lower.GetGenericSegment(i) upperSeg := upper.GetGenericSegment(i) segPrefix := getMinPrefixLenForBlock(DivInt(lowerSeg.GetSegmentValue()), DivInt(upperSeg.GetSegmentValue()), segBitCount) if segPrefix == segBitCount { break } else { totalPrefix -= segBitCount if segPrefix != 0 { totalPrefix += segPrefix break } } } return totalPrefix } // IsZero returns whether this sequential range spans from the zero address to itself. func (rng *SequentialRange[T]) IsZero() bool { return rng.IncludesZero() && !rng.IsMultiple() } // IncludesZero returns whether this sequential range's lower value is the zero address. func (rng *SequentialRange[T]) IncludesZero() bool { return rng.init().lower.IsZero() } // IsMax returns whether this sequential range spans from the max address, the address whose bits are all ones, to itself. func (rng *SequentialRange[T]) IsMax() bool { return rng.IncludesMax() && !rng.IsMultiple() } // IncludesMax returns whether this sequential range's upper value is the max value, the value whose bits are all ones. func (rng *SequentialRange[T]) IncludesMax() bool { return rng.init().upper.IsMax() } // IsFullRange returns whether this address range covers the entire address space of this IP address version. // // This is true if and only if both IncludesZero and IncludesMax return true. func (rng *SequentialRange[T]) IsFullRange() bool { return rng.IncludesZero() && rng.IncludesMax() } // GetCount returns the count of addresses that this sequential range spans. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (rng *SequentialRange[T]) GetCount() *big.Int { if rng == nil { return bigZero() } return rng.init().getCachedCount(true) } // IsMultiple returns whether this range represents a range of multiple addresses. func (rng *SequentialRange[T]) IsMultiple() bool { return rng != nil && rng.isMultiple } // String implements the [fmt.Stringer] interface, // returning the lower address canonical string, followed by the default separator " -> ", // followed by the upper address canonical string. // It returns "" if the receiver is a nil pointer. func (rng *SequentialRange[T]) String() string { if rng == nil { return nilString() } return rng.ToString(T.String, DefaultSeqRangeSeparator, T.String) } // Format implements [fmt.Formatter] interface. // // It prints the string as "lower -> upper" where lower and upper are the formatted strings for the lowest and highest addresses in the range, given by GetLower and GetUpper. // The formats, flags, and other specifications supported are those supported by Format in IPAddress. func (rng SequentialRange[T]) Format(state fmt.State, verb rune) { rngPtr := rng.init() rngPtr.lower.Format(state, verb) _, _ = state.Write([]byte(DefaultSeqRangeSeparator)) rngPtr.upper.Format(state, verb) } // ToString produces a customized string for the address range. func (rng *SequentialRange[T]) ToString(lowerStringer func(T) string, separator string, upperStringer func(T) string) string { if rng == nil { return nilString() } rng = rng.init() builder := strings.Builder{} str1, str2, str3 := lowerStringer(rng.lower), separator, upperStringer(rng.upper) builder.Grow(len(str1) + len(str2) + len(str3)) builder.WriteString(str1) builder.WriteString(str2) builder.WriteString(str3) return builder.String() } // ToNormalizedString produces a normalized string for the address range. // It has the format "lower -> upper" where lower and upper are the normalized strings for the lowest and highest addresses in the range, given by GetLower and GetUpper. func (rng *SequentialRange[T]) ToNormalizedString() string { return rng.ToString(T.ToNormalizedString, DefaultSeqRangeSeparator, T.ToNormalizedString) } // ToCanonicalString produces a canonical string for the address range. // It has the format "lower -> upper" where lower and upper are the canonical strings for the lowest and highest addresses in the range, given by GetLower and GetUpper. func (rng *SequentialRange[T]) ToCanonicalString() string { return rng.ToString(T.ToCanonicalString, DefaultSeqRangeSeparator, T.ToCanonicalString) } // GetLowerIPAddress satisfies the IPAddressRange interface, returning the lower address in the range, same as GetLower. func (rng *SequentialRange[T]) GetLowerIPAddress() *IPAddress { return rng.GetLower().ToIP() } // GetUpperIPAddress satisfies the IPAddressRange interface, returning the upper address in the range, same as GetUpper. func (rng *SequentialRange[T]) GetUpperIPAddress() *IPAddress { return rng.GetUpper().ToIP() } // GetLower returns the lowest address in the range, the one with the lowest numeric value. func (rng *SequentialRange[T]) GetLower() T { return rng.init().lower } // GetUpper returns the highest address in the range, the one with the highest numeric value. func (rng *SequentialRange[T]) GetUpper() T { return rng.init().upper } // GetBitCount returns the number of bits in each address in the range. func (rng *SequentialRange[T]) GetBitCount() BitCount { return rng.GetLower().GetBitCount() } // GetByteCount returns the number of bytes in each address in the range. func (rng *SequentialRange[T]) GetByteCount() int { return rng.GetLower().GetByteCount() } // GetNetIP returns the lower IP address in the range as a net.IP. func (rng *SequentialRange[T]) GetNetIP() net.IP { return rng.GetLower().GetNetIP() } // GetUpperNetIP returns the upper IP address in the range as a net.IP. func (rng *SequentialRange[T]) GetUpperNetIP() net.IP { return rng.GetUpper().GetUpperNetIP() } // GetNetNetIPAddr returns the lowest address in this address range as a netip.Addr. func (rng *SequentialRange[T]) GetNetNetIPAddr() netip.Addr { return rng.GetLower().GetNetNetIPAddr() } // GetUpperNetNetIPAddr returns the highest address in this address range as a netip.Addr. func (rng *SequentialRange[T]) GetUpperNetNetIPAddr() netip.Addr { return rng.GetUpper().GetUpperNetNetIPAddr() } // CopyNetIP copies the value of the lower IP address in the range into a net.IP. // // If the value can fit in the given net.IP slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (rng *SequentialRange[T]) CopyNetIP(bytes net.IP) net.IP { return rng.GetLower().CopyNetIP(bytes) // this changes the arg to 4 bytes if 16 bytes and ipv4 } // CopyUpperNetIP copies the upper IP address in the range into a net.IP. // // If the value can fit in the given net.IP slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (rng *SequentialRange[T]) CopyUpperNetIP(bytes net.IP) net.IP { return rng.GetUpper().CopyUpperNetIP(bytes) // this changes the arg to 4 bytes if 16 bytes and ipv4 } // Bytes returns the lowest address in the range, the one with the lowest numeric value, as a byte slice. func (rng *SequentialRange[T]) Bytes() []byte { return rng.GetLower().Bytes() } // CopyBytes copies the value of the lowest address in the range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (rng *SequentialRange[T]) CopyBytes(bytes []byte) []byte { return rng.GetLower().CopyBytes(bytes) } // UpperBytes returns the highest address in the range, the one with the highest numeric value, as a byte slice. func (rng *SequentialRange[T]) UpperBytes() []byte { return rng.GetUpper().UpperBytes() } // CopyUpperBytes copies the value of the highest address in the range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (rng *SequentialRange[T]) CopyUpperBytes(bytes []byte) []byte { return rng.GetUpper().CopyUpperBytes(bytes) } // Contains returns whether this range contains all addresses in the given address or subnet. func (rng *SequentialRange[T]) Contains(other IPAddressType) bool { if rng == nil { return other == nil || other.ToAddressBase() == nil } else if other == nil { return true } otherAddr := other.ToIP() if otherAddr == nil { return true } rng = rng.init() return compareLowIPAddressValues(otherAddr.GetLower(), rng.lower) >= 0 && compareLowIPAddressValues(otherAddr.GetUpper(), rng.upper) <= 0 } // ContainsRange returns whether all the addresses in the given sequential range are also contained in this sequential range. func (rng *SequentialRange[T]) ContainsRange(other IPAddressSeqRangeType) bool { if rng == nil { return other == nil || other.ToIP() == nil } else if other == nil { return true } rng = rng.init() otherRange := other.ToIP() if otherRange == nil { return true } return compareLowIPAddressValues(otherRange.GetLower(), rng.lower) >= 0 && compareLowIPAddressValues(otherRange.GetUpper(), rng.upper) <= 0 } // Equal returns whether the given sequential address range is equal to this sequential address range. // Two sequential address ranges are equal if their lower and upper range boundaries are equal. func (rng *SequentialRange[T]) Equal(other IPAddressSeqRangeType) bool { if rng == nil { return other == nil || other.ToIP() == nil } else if other == nil { return false } rng = rng.init() otherRange := other.ToIP() if otherRange == nil { return false } return rng.lower.Equal(otherRange.GetLower()) && rng.upper.Equal(otherRange.GetUpper()) } // Compare returns a negative integer, zero, or a positive integer if this sequential address range is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (rng *SequentialRange[T]) Compare(item AddressItem) int { if rng != nil { rng = rng.init() } return CountComparator.Compare(rng, item) } // CompareSize compares the counts of two address ranges or items, the number of individual addresses or items within each. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether this range spans more individual addresses than another item. // // CompareSize returns a positive integer if this range has a larger count than the item given, zero if they are the same, or a negative integer if the other has a larger count. func (rng *SequentialRange[T]) CompareSize(other AddressItem) int { if rng == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return compareCount(rng, other) } // GetValue returns the lowest address in the range, the one with the lowest numeric value, as an integer. func (rng *SequentialRange[T]) GetValue() *big.Int { return rng.GetLower().GetValue() } // GetUpperValue returns the highest address in the range, the one with the highest numeric value, as an integer. func (rng *SequentialRange[T]) GetUpperValue() *big.Int { return rng.GetUpper().GetValue() } // Iterator provides an iterator to iterate through the individual addresses of this address range. // // Call GetCount for the count. func (rng *SequentialRange[T]) Iterator() Iterator[T] { if rng == nil { return nilIterator[T]() } rng = rng.init() lower := rng.lower if !rng.isMultiple { return &singleIterator[T]{original: lower} } divCount := lower.GetSegmentCount() return lower.rangeIterator( rng.upper, false, nil, (*IPAddress).GetSegment, func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment] { return seg.Iterator() }, func(addr1, addr2 *IPAddress, index int) bool { return addr1.getSegment(index).getSegmentValue() == addr2.getSegment(index).getSegmentValue() }, divCount-1, divCount, nil) } type segPrefData struct { prefLen PrefixLen shift BitCount } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks of the given prefix length, // one for each prefix of that length in the address range. func (rng *SequentialRange[T]) PrefixBlockIterator(prefLength BitCount) Iterator[T] { rng = rng.init() lower := rng.lower if !rng.isMultiple { return &singleIterator[T]{original: lower.ToPrefixBlockLen(prefLength)} } prefLength = checkSubnet(lower, prefLength) bitsPerSegment := lower.GetBitsPerSegment() bytesPerSegment := lower.GetBytesPerSegment() segCount := lower.GetSegmentCount() segPrefs := make([]segPrefData, segCount) networkSegIndex := getNetworkSegmentIndex(prefLength, bytesPerSegment, bitsPerSegment) for i := networkSegIndex; i < segCount; i++ { segPrefLength := getPrefixedSegmentPrefixLength(bitsPerSegment, prefLength, i) segPrefs[i] = segPrefData{segPrefLength, bitsPerSegment - segPrefLength.bitCount()} } hostSegIndex := getHostSegmentIndex(prefLength, bytesPerSegment, bitsPerSegment) return lower.rangeIterator( rng.upper, true, cacheBitCount(prefLength), (*IPAddress).GetSegment, func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment] { return seg.Iterator() }, func(addr1, addr2 *IPAddress, index int) bool { segPref := segPrefs[index] if segPref.prefLen == nil { return addr1.GetSegment(index).GetSegmentValue() == addr2.GetSegment(index).GetSegmentValue() } shift := segPref.shift return addr1.GetSegment(index).GetSegmentValue()>>uint(shift) == addr2.GetSegment(index).GetSegmentValue()>>uint(shift) }, networkSegIndex, hostSegIndex, func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment] { segPref := segPrefs[index] segPrefLen := segPref.prefLen if segPrefLen == nil { return seg.Iterator() } return seg.PrefixedBlockIterator(segPrefLen.bitCount()) }, ) } // PrefixIterator provides an iterator to iterate through the individual prefixes of the given prefix length in this address range, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this range. // // Since a range between two arbitrary addresses cannot always be represented with a single IPAddress instance, // the returned iterator iterates through SequentialRange instances. // // For instance, if iterating from "1.2.3.4" to "1.2.4.5" with prefix 8, the range shares the same prefix of value 1, // but the range cannot be represented by the address "1.2.3-4.4-5" which does not include "1.2.3.255" or "1.2.4.0" both of which are in the original range. // Nor can the range be represented by "1.2.3-4.0-255" which includes "1.2.4.6" and "1.2.3.3", both of which were not in the original range. // A SequentialRange is thus required to represent that prefixed range. func (rng *SequentialRange[T]) PrefixIterator(prefLength BitCount) Iterator[*SequentialRange[T]] { rng = rng.init() lower := rng.lower if !rng.isMultiple { return &singleIterator[*SequentialRange[T]]{original: rng} } prefLength = checkSubnet(lower, prefLength) return &sequRangeIterator[T]{ rng: rng, creator: newSequRange[T], prefixBlockIterator: rng.PrefixBlockIterator(prefLength), prefixLength: prefLength, } } // Overlaps returns true if this sequential range overlaps with the given sequential range. func (rng *SequentialRange[T]) Overlaps(other *SequentialRange[T]) bool { rng = rng.init() return compareLowIPAddressValues(other.GetLower(), rng.upper) <= 0 && compareLowIPAddressValues(other.GetUpper(), rng.lower) >= 0 } // Intersect returns the intersection of this range with the given range, a range which includes those addresses found in both. func (rng *SequentialRange[T]) Intersect(other *SequentialRange[T]) *SequentialRange[T] { rng = rng.init() other = other.init() otherLower, otherUpper := other.GetLower(), other.GetUpper() lower, upper := rng.lower, rng.upper if compareLowIPAddressValues(lower, otherLower) <= 0 { if compareLowIPAddressValues(upper, otherUpper) >= 0 { // l, ol, ou, u return other } comp := compareLowIPAddressValues(upper, otherLower) if comp < 0 { // l, u, ol, ou return nil } return newSequRangeUnchecked(otherLower, upper, comp != 0) // l, ol, u, ou } else if compareLowIPAddressValues(otherUpper, upper) >= 0 { return rng } comp := compareLowIPAddressValues(otherUpper, lower) if comp < 0 { return nil } return newSequRangeUnchecked(lower, otherUpper, comp != 0) } // CoverWithPrefixBlock returns the minimal-size prefix block that covers all the addresses in this range. // The resulting block will have a larger count than this, unless this range already directly corresponds to a prefix block. func (rng *SequentialRange[T]) CoverWithPrefixBlock() T { return rng.GetLower().CoverWithPrefixBlockTo(rng.GetUpper()) } // SpanWithPrefixBlocks returns an array of prefix blocks that spans the same set of addresses as this range. func (rng *SequentialRange[T]) SpanWithPrefixBlocks() []T { return rng.GetLower().SpanWithPrefixBlocksTo(rng.GetUpper()) } // SpanWithSequentialBlocks produces the smallest slice of sequential blocks that cover the same set of addresses as this range. // This slice can be shorter than that produced by SpanWithPrefixBlocks and is never longer. func (rng *SequentialRange[T]) SpanWithSequentialBlocks() []T { res := rng.GetLower().SpanWithSequentialBlocksTo(rng.GetUpper()) return res } // Join joins the receiver with the given ranges into the fewest number of ranges. // The returned array will be sorted by ascending lowest range value. // Nil ranges are tolerated, and ignored. func (rng *SequentialRange[T]) Join(ranges ...*SequentialRange[T]) []*SequentialRange[T] { ranges = append(append(make([]*SequentialRange[T], 0, len(ranges)+1), ranges...), rng) return joinRanges(ranges) } // JoinTo joins this range to the other if they are contiguous. If this range overlaps with the given range, // or if the highest value of the lower range is one below the lowest value of the higher range, // then the two are joined into a new larger range that is returned. // Otherwise, nil is returned. func (rng *SequentialRange[T]) JoinTo(other *SequentialRange[T]) *SequentialRange[T] { rng = rng.init() other = other.init() otherLower, otherUpper := other.GetLower(), other.GetUpper() lower, upper := rng.lower, rng.upper lowerComp := compareLowIPAddressValues(lower, otherLower) if !rng.Overlaps(other) { if lowerComp >= 0 { if otherUpper.Increment(1).Equal(lower) { return newSequRangeUnchecked[T](otherLower, upper, true) } } else { if upper.Increment(1).Equal(otherLower) { return newSequRangeUnchecked[T](lower, otherUpper, true) } } return nil } upperComp := compareLowIPAddressValues(upper, otherUpper) var lowestLower, highestUpper T if lowerComp >= 0 { if lowerComp == 0 && upperComp == 0 { return rng } lowestLower = otherLower } else { lowestLower = lower } if upperComp >= 0 { highestUpper = upper } else { highestUpper = otherUpper } return newSequRangeUnchecked(lowestLower, highestUpper, true) } // Extend extends this sequential range to include all address in the given range. // If the argument has a different IP version than this, nil is returned. // Otherwise, this method returns the range that includes this range, the given range, and all addresses in-between. func (rng *SequentialRange[T]) Extend(other *SequentialRange[T]) *SequentialRange[T] { rng = rng.init() other = other.init() if !rng.lower.GetIPVersion().Equal(other.lower.GetIPVersion()) { return nil } otherLower, otherUpper := other.GetLower(), other.GetUpper() lower, upper := rng.lower, rng.upper lowerComp := compareLowIPAddressValues(lower, otherLower) upperComp := compareLowIPAddressValues(upper, otherUpper) if lowerComp > 0 { // if upperComp <= 0 { // ol l u ou return other } // ol l ou u or ol ou l u return newSequRangeUnchecked(otherLower, upper, true) } // lowerComp <= 0 if upperComp >= 0 { // l ol ou u return rng } return newSequRangeUnchecked(lower, otherUpper, true) // l ol u ou or l u ol ou } // Subtract subtracts the given range from the receiver range, to produce either zero, one, or two address ranges that contain the addresses in the receiver range and not in the given range. // If the result has length 2, the two ranges are ordered by ascending lowest range value. func (rng *SequentialRange[T]) Subtract(other *SequentialRange[T]) []*SequentialRange[T] { rng = rng.init() other = other.init() otherLower, otherUpper := other.GetLower(), other.GetUpper() lower, upper := rng.lower, rng.upper if compareLowIPAddressValues(lower, otherLower) < 0 { if compareLowIPAddressValues(upper, otherUpper) > 0 { // l ol ou u return []*SequentialRange[T]{ newSequRangeCheckSize(lower, otherLower.Increment(-1)), newSequRangeCheckSize(otherUpper.Increment(1), upper), } } else { comp := compareLowIPAddressValues(upper, otherLower) if comp < 0 { // l u ol ou return []*SequentialRange[T]{rng} } else if comp == 0 { // l u == ol ou return []*SequentialRange[T]{newSequRangeCheckSize(lower, upper.Increment(-1))} } return []*SequentialRange[T]{newSequRangeCheckSize(lower, otherLower.Increment(-1))} // l ol u ou } } else if compareLowIPAddressValues(otherUpper, upper) >= 0 { // ol l u ou return make([]*SequentialRange[T], 0, 0) } else { comp := compareLowIPAddressValues(otherUpper, lower) if comp < 0 { return []*SequentialRange[T]{rng} // ol ou l u } else if comp == 0 { return []*SequentialRange[T]{newSequRangeCheckSize(lower.Increment(1), upper)} // ol ou == l u } return []*SequentialRange[T]{newSequRangeCheckSize(otherUpper.Increment(1), upper)} // ol l ou u } } // ToKey creates the associated address range key. // While address ranges can be compared with the Compare or Equal methods as well as various provided instances of AddressComparator, // they are not comparable with Go operators. // However, SequentialRangeKey instances are comparable with Go operators, and thus can be used as map keys. func (rng *SequentialRange[T]) ToKey() SequentialRangeKey[T] { return newSequentialRangeKey(rng.init()) } // IsIPv4 returns true if this sequential address range is an IPv4 sequential address range. If so, use ToIPv4 to convert to the IPv4-specific type. func (rng *SequentialRange[T]) IsIPv4() bool { // returns false when lower is nil if rng != nil { t := any(rng.GetLower()) if _, ok := t.(*IPv4Address); ok { return true } else if addr, ok := t.(*IPAddress); ok { return addr.IsIPv4() } } return false } // IsIPv6 returns true if this sequential address range is an IPv6 sequential address range. If so, use ToIPv6 to convert to the IPv6-specific type. func (rng *SequentialRange[T]) IsIPv6() bool { // returns false when lower is nil if rng != nil { t := any(rng.GetLower()) if _, ok := t.(*IPv6Address); ok { return true } else if addr, ok := t.(*IPAddress); ok { return addr.IsIPv6() } } return false } // ToIPv4 converts to a SequentialRange[*IPv4Address] if this address range is an IPv4 address range. // If not, ToIPv4 returns nil. // // ToIPv4 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (rng *SequentialRange[T]) ToIPv4() *SequentialRange[*IPv4Address] { if rng != nil { if ipv4, ok := any(rng).(*SequentialRange[*IPv4Address]); ok { return ipv4 } else { t := any(rng.GetLower()) if addr, ok := t.(*IPAddress); ok && addr.IsIPv4() { t = any(rng.GetUpper()) return newSequRangeUnchecked(addr.ToIPv4(), t.(*IPAddress).ToIPv4(), rng.isMultiple) } } } return nil } // ToIPv6 converts to a SequentialRange[*IPv6Address] if this address range is an IPv6 address range. // If not, ToIPv6 returns nil. // // ToIPv6 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (rng *SequentialRange[T]) ToIPv6() *SequentialRange[*IPv6Address] { if rng != nil { if ipv6, ok := any(rng).(*SequentialRange[*IPv6Address]); ok { return ipv6 } else { t := any(rng.GetLower()) if addr, ok := t.(*IPAddress); ok && addr.IsIPv6() { t = any(rng.GetUpper()) return newSequRangeUnchecked(addr.ToIPv6(), t.(*IPAddress).ToIPv6(), rng.isMultiple) } } } return nil } // ToIP converts to a SequentialRange[*IPAddress], a polymorphic type usable with all IP address sequential ranges. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (rng *SequentialRange[T]) ToIP() *SequentialRange[*IPAddress] { if rng != nil { if ip, ok := any(rng).(*SequentialRange[*IPAddress]); ok { return ip } return newSequRangeUnchecked(rng.GetLower().ToIP(), rng.GetUpper().ToIP(), rng.isMultiple) } return nil } func newSequRangeUnchecked[T SequentialRangeConstraint[T]](lower, upper T, isMult bool) *SequentialRange[T] { return &SequentialRange[T]{ lower: lower, upper: upper, isMultiple: isMult, cache: &rangeCache{}, } } func newSequRangeCheckSize[T SequentialRangeConstraint[T]](lower, upper T) *SequentialRange[T] { return newSequRangeUnchecked(lower, upper, !lower.equalsSameVersion(upper)) } func newSequRange[T SequentialRangeConstraint[T]](first, other T) *SequentialRange[T] { var lower, upper T var isMult bool if f := first.Contains(other); f || other.Contains(first) { var addr T if f { addr = first.WithoutPrefixLen() } else { addr = other.WithoutPrefixLen() } lower = addr.GetLower() if isMult = addr.IsMultiple(); isMult { upper = addr.GetUpper() } else { upper = lower } } else { // We find the lowest and the highest from both supplied addresses firstLower := first.GetLower() otherLower := other.GetLower() firstUpper := first.GetUpper() otherUpper := other.GetUpper() if comp := compareLowIPAddressValues(firstLower, otherLower); comp > 0 { isMult = true lower = otherLower } else { isMult = comp < 0 lower = firstLower } if comp := compareLowIPAddressValues(firstUpper, otherUpper); comp < 0 { isMult = true upper = otherUpper } else { isMult = isMult || comp > 0 upper = firstUpper } if isMult = isMult || compareLowIPAddressValues(lower, upper) != 0; isMult { lower = lower.WithoutPrefixLen() upper = upper.WithoutPrefixLen() } else { if lower.IsPrefixed() { if upper.IsPrefixed() { lower = lower.WithoutPrefixLen() upper = lower } else { lower = upper } } else { upper = lower } } } return newSequRangeUnchecked(lower, upper, isMult) } // NewSequentialRange creates a sequential range from the given addresses. // If the type of T is *IPAddress and the versions of lower and upper do not match (one is IPv4, one IPv6), then nil is returned. // Otherwise, the range is returned. func NewSequentialRange[T SequentialRangeConstraint[T]](lower, upper T) *SequentialRange[T] { var t T if lower == t && upper == t { // nil for pointers lower = nilConvert[T]() } else if lower != t && upper != t { // this check only matters when T is *IPAddress if lower.getAddrType() != upper.getAddrType() { // when both are zero-type, we do not go in here // but if only one is, we return nil. zero-type is "indeterminate", so we cannot "infer" a different version for it // However, nil is the absence of a version/type so we can and do return nil } } return newSequRange(lower, upper) } // NewIPSeqRange creates an IP sequential range from the given addresses. // It is here for backwards compatibility. NewSequentialRange is recommended instead. // If the type of T is *IPAddress and the versions of lower and upper do not match (one is IPv4, one IPv6), then nil is returned. // Otherwise, the range is returned. func NewIPSeqRange(lower, upper *IPAddress) *SequentialRange[*IPAddress] { // for backwards compatibility if lower == nil && upper == nil { lower = zeroIPAddr } else if lower != nil && upper != nil { if lower.getAddrType() != upper.getAddrType() { // when both are zero-type, we do not go in here // but if only one is, we return nil. zero-type is "indeterminate", so we cannot "infer" a different version for it // However, nil is the absence of a version/type so we can and do return nil } } return newSequRange(lower, upper) } // NewIPv4SeqRange creates an IPv4 sequential range from the given addresses. // It is here for backwards compatibility. NewSequentialRange is recommended instead. func NewIPv4SeqRange(lower, upper *IPv4Address) *SequentialRange[*IPv4Address] { // for backwards compatibility if lower == nil && upper == nil { lower = zeroIPv4 } return newSequRange(lower, upper) } // NewIPv6SeqRange creates an IPv6 sequential range from the given addresses. // It is here for backwards compatibility. NewSequentialRange is recommended instead. func NewIPv6SeqRange(lower, upper *IPv6Address) *SequentialRange[*IPv6Address] { // for backwards compatibility if lower == nil && upper == nil { lower = zeroIPv6 } return newSequRange(lower, upper) } func joinRanges[T SequentialRangeConstraint[T]](ranges []*SequentialRange[T]) []*SequentialRange[T] { // nil entries are automatic joins joinedCount := 0 rangesLen := len(ranges) for i, j := 0, rangesLen-1; i <= j; i++ { if ranges[i] == nil { joinedCount++ for ranges[j] == nil && j > i { j-- joinedCount++ } if j > i { ranges[i] = ranges[j] ranges[j] = nil j-- } } } rangesLen = rangesLen - joinedCount ranges = ranges[:rangesLen] joinedCount = 0 sort.Slice(ranges, func(i, j int) bool { return LowValueComparator.CompareRanges(ranges[i], ranges[j]) < 0 }) for i := 0; i < rangesLen; { rng := ranges[i] currentLower, currentUpper := rng.GetLower(), rng.GetUpper() var isMultiJoin, didJoin bool j := i + 1 for ; j < rangesLen; j++ { rng2 := ranges[j] nextLower := rng2.GetLower() doJoin := compareLowIPAddressValues(currentUpper, nextLower) >= 0 if !doJoin && nextLower.GetIPVersion().Equal(currentUpper.GetIPVersion()) { doJoin = currentUpper.Increment(1).Equal(nextLower) isMultiJoin = true } if doJoin { //Join them joinedCount++ nextUpper := rng2.GetUpper() if compareLowIPAddressValues(currentUpper, nextUpper) < 0 { currentUpper = nextUpper } ranges[j] = nil isMultiJoin = isMultiJoin || rng.isMultiple || rng2.isMultiple didJoin = true } else { break } } if didJoin { ranges[i] = newSequRangeUnchecked(currentLower, currentUpper, isMultiJoin) } i = j } finalLen := rangesLen - joinedCount if finalLen > 0 { for i, j := 0, 0; ; i++ { rng := ranges[i] if rng == nil { continue } ranges[j] = rng j++ if j >= finalLen { break } } } ret := ranges[:finalLen] return ret } func compareLowIPAddressValues(one, two AddressType) int { return LowValueComparator.CompareAddresses(one, two) } // getMinPrefixLenForBlock returns the smallest prefix length such that the upper and lower values span the block of values for that prefix length. // The given bit count indicates the bits that matter in the two values, the remaining bits are ignored. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use getPrefixLenForSingleBlock to avoid the case of multiple prefix values. func getMinPrefixLenForBlock(lower, upper DivInt, bitCount BitCount) BitCount { if lower == upper { return bitCount } else if lower == 0 { maxValue := ^(^DivInt(0) << uint(bitCount)) if upper == maxValue { return 0 } } result := bitCount lowerZeros := bits.TrailingZeros64(lower) if lowerZeros != 0 { upperOnes := bits.TrailingZeros64(^upper) if upperOnes != 0 { var prefixedBitCount int if lowerZeros < upperOnes { prefixedBitCount = lowerZeros } else { prefixedBitCount = upperOnes } result -= BitCount(prefixedBitCount) } } return result } // getPrefixLenForSingleBlock returns a prefix length for which the given lower and upper values share the same prefix, // and the range spanned by those values matches exactly the block of all values for that prefix. // The given bit count indicates the bits that matter in the two values, the remaining bits are ignored. // // If the range can be described this way, then this method returns the same value as GetMinPrefixLenForBlock. // // If no such prefix length exists, returns nil. // // If lower and upper values are the same, this returns the bit count. func getPrefixLenForSingleBlock(lower, upper DivInt, bitCount BitCount) PrefixLen { prefixLen := getMinPrefixLenForBlock(lower, upper, bitCount) if prefixLen == bitCount { if lower == upper { return cacheBitCount(prefixLen) } } else { shift := bitCount - prefixLen if lower>>uint(shift) == upper>>uint(shift) { return cacheBitCount(prefixLen) } } return nil } type ( IPAddressSeqRange = SequentialRange[*IPAddress] IPv4AddressSeqRange = SequentialRange[*IPv4Address] IPv6AddressSeqRange = SequentialRange[*IPv6Address] ) ipaddress-go-1.5.4/ipaddr/ipv4addr.go000066400000000000000000002525611440250641600174260ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math/big" "net" "net/netip" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) const ( IPv4SegmentSeparator = '.' IPv4SegmentSeparatorStr = "." IPv4BitsPerSegment = 8 IPv4BytesPerSegment = 1 IPv4SegmentCount = 4 IPv4ByteCount = 4 IPv4BitCount = 32 IPv4DefaultTextualRadix = 10 IPv4MaxValuePerSegment = 0xff IPv4MaxValue = 0xffffffff IPv4ReverseDnsSuffix = ".in-addr.arpa" IPv4SegmentMaxChars = 3 ipv4BitsToSegmentBitshift = 3 ) func newIPv4Address(section *IPv4AddressSection) *IPv4Address { return createAddress(section.ToSectionBase(), NoZone).ToIPv4() } // NewIPv4Address constructs an IPv4 address or subnet from the given address section. // If the section does not have 4 segments, an error is returned. func NewIPv4Address(section *IPv4AddressSection) (*IPv4Address, addrerr.AddressValueError) { if section == nil { return zeroIPv4, nil } segCount := section.GetSegmentCount() if segCount != IPv4SegmentCount { return nil, &addressValueError{ addressError: addressError{key: "ipaddress.error.invalid.size"}, val: segCount, } } return createAddress(section.ToSectionBase(), NoZone).ToIPv4(), nil } // NewIPv4AddressFromSegs constructs an IPv4 address or subnet from the given segments. // If the given slice does not have 4 segments, an error is returned. func NewIPv4AddressFromSegs(segments []*IPv4AddressSegment) (*IPv4Address, addrerr.AddressValueError) { segCount := len(segments) if segCount != IPv4SegmentCount { return nil, &addressValueError{ addressError: addressError{key: "ipaddress.error.invalid.size"}, val: segCount, } } section := NewIPv4Section(segments) return createAddress(section.ToSectionBase(), NoZone).ToIPv4(), nil } // NewIPv4AddressFromPrefixedSegs constructs an IPv4 address or subnet from the given segments and prefix length. // If the given slice does not have 4 segments, an error is returned. // If the address has a zero host for its prefix length, the returned address will be the prefix block. func NewIPv4AddressFromPrefixedSegs(segments []*IPv4AddressSegment, prefixLength PrefixLen) (*IPv4Address, addrerr.AddressValueError) { segCount := len(segments) if segCount != IPv4SegmentCount { return nil, &addressValueError{ addressError: addressError{key: "ipaddress.error.invalid.size"}, val: segCount, } } section := NewIPv4PrefixedSection(segments, prefixLength) return createAddress(section.ToSectionBase(), NoZone).ToIPv4(), nil } // NewIPv4AddressFromBytes constructs an IPv4 address from the given byte slice. // An error is returned when the byte slice has too many bytes to match the IPv4 segment count of 4. // There should be 4 bytes or less, although extra leading zeros are tolerated. func NewIPv4AddressFromBytes(bytes []byte) (addr *IPv4Address, err addrerr.AddressValueError) { if ipv4 := net.IP(bytes).To4(); ipv4 != nil { bytes = ipv4 } section, err := NewIPv4SectionFromSegmentedBytes(bytes, IPv4SegmentCount) if err == nil { addr = newIPv4Address(section) } return } // NewIPv4AddressFromPrefixedBytes constructs an IPv4 address or prefix block from the given byte slice and prefix length. // An error is returned when the byte slice has too many bytes to match the IPv4 segment count of 4. // There should be 4 bytes or less, although extra leading zeros are tolerated. // If the address has a zero host for the given prefix length, the returned address will be the prefix block. func NewIPv4AddressFromPrefixedBytes(bytes []byte, prefixLength PrefixLen) (addr *IPv4Address, err addrerr.AddressValueError) { if ipv4 := net.IP(bytes).To4(); ipv4 != nil { bytes = ipv4 } section, err := NewIPv4SectionFromPrefixedBytes(bytes, IPv4SegmentCount, prefixLength) if err == nil { addr = newIPv4Address(section) } return } // NewIPv4AddressFromUint32 constructs an IPv4 address from the given value. func NewIPv4AddressFromUint32(val uint32) *IPv4Address { section := NewIPv4SectionFromUint32(val, IPv4SegmentCount) return createAddress(section.ToSectionBase(), NoZone).ToIPv4() } // NewIPv4AddressFromPrefixedUint32 constructs an IPv4 address or prefix block from the given value and prefix length. // If the address has a zero host for the given prefix length, the returned address will be the prefix block. func NewIPv4AddressFromPrefixedUint32(val uint32, prefixLength PrefixLen) *IPv4Address { section := NewIPv4SectionFromPrefixedUint32(val, IPv4SegmentCount, prefixLength) return createAddress(section.ToSectionBase(), NoZone).ToIPv4() } // NewIPv4AddressFromVals constructs an IPv4 address from the given values. func NewIPv4AddressFromVals(vals IPv4SegmentValueProvider) *IPv4Address { section := NewIPv4SectionFromVals(vals, IPv4SegmentCount) return newIPv4Address(section) } // NewIPv4AddressFromPrefixedVals constructs an IPv4 address or prefix block from the given values and prefix length. // If the address has a zero host for the given prefix length, the returned address will be the prefix block. func NewIPv4AddressFromPrefixedVals(vals IPv4SegmentValueProvider, prefixLength PrefixLen) *IPv4Address { section := NewIPv4SectionFromPrefixedVals(vals, IPv4SegmentCount, prefixLength) return newIPv4Address(section) } // NewIPv4AddressFromRange constructs an IPv4 subnet from the given values. func NewIPv4AddressFromRange(vals, upperVals IPv4SegmentValueProvider) *IPv4Address { section := NewIPv4SectionFromRange(vals, upperVals, IPv4SegmentCount) return newIPv4Address(section) } // NewIPv4AddressFromPrefixedRange constructs an IPv4 subnet from the given values and prefix length. // If the address has a zero host for the given prefix length, the returned address will be the prefix block. func NewIPv4AddressFromPrefixedRange(vals, upperVals IPv4SegmentValueProvider, prefixLength PrefixLen) *IPv4Address { section := NewIPv4SectionFromPrefixedRange(vals, upperVals, IPv4SegmentCount, prefixLength) return newIPv4Address(section) } func newIPv4AddressFromPrefixedSingle(vals, upperVals IPv4SegmentValueProvider, prefixLength PrefixLen) *IPv4Address { section := newIPv4SectionFromPrefixedSingle(vals, upperVals, IPv4SegmentCount, prefixLength, true) return newIPv4Address(section) } var zeroIPv4 = initZeroIPv4() var ipv4All = zeroIPv4.ToPrefixBlockLen(0) func initZeroIPv4() *IPv4Address { div := zeroIPv4Seg segs := []*IPv4AddressSegment{div, div, div, div} section := NewIPv4Section(segs) return newIPv4Address(section) } // // // IPv4Address is an IPv4 address, or a subnet of multiple IPv4 addresses. // An IPv4 address is composed of 4 1-byte segments and can optionally have an associated prefix length. // Each segment can represent a single value or a range of values. // The zero value is "0.0.0.0". // // To construct one from a string, use NewIPAddressString, then use the ToAddress or GetAddress method of [IPAddressString], // and then use ToIPv4 to get an IPv4Address, assuming the string had an IPv4 format. // // For other inputs, use one of the multiple constructor functions like NewIPv4Address. // You can also use one of the multiple constructors for [IPAddress] like NewIPAddress and then convert using ToIPv4. type IPv4Address struct { ipAddressInternal } func (addr *IPv4Address) init() *IPv4Address { if addr.section == nil { return zeroIPv4 } return addr } // GetCount returns the count of addresses that this address or subnet represents. // // If just a single address, not a subnet of multiple addresses, returns 1. // // For instance, the IP address subnet "1.2.0.0/15" has the count of 2 to the power of 17. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (addr *IPv4Address) GetCount() *big.Int { if addr == nil { return bigZero() } return addr.getCount() } // GetIPv4Count returns the count of possible distinct values for this section. // It is the same as GetCount but returns the value as a uint64 instead of a big integer. // If not representing multiple values, the count is 1. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (addr *IPv4Address) GetIPv4Count() uint64 { if addr == nil { return 0 } return addr.GetSection().GetIPv4Count() } // GetIPv4PrefixCount returns the number of distinct prefix values in this section. // It is the same as GetPrefixCount but returns the value as a uint64 instead of a big integer. // // The prefix length is given by GetPrefixLen. // // If this has a non-nil prefix length, returns the number of distinct prefix values. // // If this has a nil prefix length, returns the same value as GetIPv4Count. func (addr *IPv4Address) GetIPv4PrefixCount() uint64 { return addr.GetSection().GetIPv4PrefixCount() } // GetIPv4PrefixCountLen gives count available as a uint64 instead of big.Int. // // It is the similar to GetPrefixCountLen but returns a uint64, not a *big.Int func (addr *IPv4Address) GetIPv4PrefixCountLen(prefixLength BitCount) uint64 { return addr.GetSection().GetIPv4PrefixCountLen(prefixLength) } // GetIPv4BlockCount returns the count of distinct values in the given number of initial (more significant) segments. // // It is similar to GetBlockCount but returns a uint64 instead of a big integer. func (addr *IPv4Address) GetIPv4BlockCount(segmentCount int) uint64 { return addr.GetSection().GetIPv4BlockCount(segmentCount) } // IsMultiple returns true if this represents more than a single individual address, whether it is a subnet of multiple addresses. func (addr *IPv4Address) IsMultiple() bool { return addr != nil && addr.isMultiple() } // IsPrefixed returns whether this address has an associated prefix length. func (addr *IPv4Address) IsPrefixed() bool { return addr != nil && addr.isPrefixed() } // IsFullRange returns whether this address covers the entire IPv4 address space. // // This is true if and only if both IncludesZero and IncludesMax return true. func (addr *IPv4Address) IsFullRange() bool { return addr.GetSection().IsFullRange() } // GetBitCount returns the number of bits comprising this address, // or each address in the range if a subnet, which is 32. func (addr *IPv4Address) GetBitCount() BitCount { return IPv4BitCount } // GetByteCount returns the number of bytes required for this address, // or each address in the range if a subnet, which is 4. func (addr *IPv4Address) GetByteCount() int { return IPv4ByteCount } // GetBitsPerSegment returns the number of bits comprising each segment in this address. Segments in the same address are equal length. func (addr *IPv4Address) GetBitsPerSegment() BitCount { return IPv4BitsPerSegment } // GetBytesPerSegment returns the number of bytes comprising each segment in this address or subnet. Segments in the same address are equal length. func (addr *IPv4Address) GetBytesPerSegment() int { return IPv4BytesPerSegment } // GetSection returns the backing section for this address or subnet, comprising all segments. func (addr *IPv4Address) GetSection() *IPv4AddressSection { return addr.init().section.ToIPv4() } // GetTrailingSection gets the subsection from the series starting from the given index. // The first segment is at index 0. func (addr *IPv4Address) GetTrailingSection(index int) *IPv4AddressSection { return addr.GetSection().GetTrailingSection(index) } // GetSubSection gets the subsection from the series starting from the given index and ending just before the give endIndex. // The first segment is at index 0. func (addr *IPv4Address) GetSubSection(index, endIndex int) *IPv4AddressSection { return addr.GetSection().GetSubSection(index, endIndex) } // GetNetworkSection returns an address section containing the segments with the network of the address or subnet, the prefix bits. // The returned section will have only as many segments as needed as determined by the existing CIDR network prefix length. // // If this series has no CIDR prefix length, the returned network section will // be the entire series as a prefixed section with prefix length matching the address bit length. func (addr *IPv4Address) GetNetworkSection() *IPv4AddressSection { return addr.GetSection().GetNetworkSection() } // GetNetworkSectionLen returns a section containing the segments with the network of the address or subnet, the prefix bits according to the given prefix length. // The returned section will have only as many segments as needed to contain the network. // // The new section will be assigned the given prefix length, // unless the existing prefix length is smaller, in which case the existing prefix length will be retained. func (addr *IPv4Address) GetNetworkSectionLen(prefLen BitCount) *IPv4AddressSection { return addr.GetSection().GetNetworkSectionLen(prefLen) } // GetHostSection returns a section containing the segments with the host of the address or subnet, the bits beyond the CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. // // If this series has no prefix length, the returned host section will be the full section. func (addr *IPv4Address) GetHostSection() *IPv4AddressSection { return addr.GetSection().GetHostSection() } // GetHostSectionLen returns a section containing the segments with the host of the address or subnet, the bits beyond the given CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. func (addr *IPv4Address) GetHostSectionLen(prefLen BitCount) *IPv4AddressSection { return addr.GetSection().GetHostSectionLen(prefLen) } // GetNetworkMask returns the network mask associated with the CIDR network prefix length of this address or subnet. // If this address or subnet has no prefix length, then the all-ones mask is returned. func (addr *IPv4Address) GetNetworkMask() *IPv4Address { return addr.getNetworkMask(ipv4Network).ToIPv4() } // GetHostMask returns the host mask associated with the CIDR network prefix length of this address or subnet. // If this address or subnet has no prefix length, then the all-ones mask is returned. func (addr *IPv4Address) GetHostMask() *IPv4Address { return addr.getHostMask(ipv4Network).ToIPv4() } // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (addr *IPv4Address) CopySubSegments(start, end int, segs []*IPv4AddressSegment) (count int) { return addr.GetSection().CopySubSegments(start, end, segs) } // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. func (addr *IPv4Address) CopySegments(segs []*IPv4AddressSegment) (count int) { return addr.GetSection().CopySegments(segs) } // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as this address. func (addr *IPv4Address) GetSegments() []*IPv4AddressSegment { return addr.GetSection().GetSegments() } // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. func (addr *IPv4Address) GetSegment(index int) *IPv4AddressSegment { return addr.init().getSegment(index).ToIPv4() } // GetSegmentCount returns the segment count, the number of segments in this address, which is 4. func (addr *IPv4Address) GetSegmentCount() int { return addr.GetDivisionCount() } // ForEachSegment visits each segment in order from most-significant to least, the most significant with index 0, calling the given function for each, terminating early if the function returns true. // Returns the number of visited segments. func (addr *IPv4Address) ForEachSegment(consumer func(segmentIndex int, segment *IPv4AddressSegment) (stop bool)) int { return addr.GetSection().ForEachSegment(consumer) } // GetGenericDivision returns the segment at the given index as a DivisionType. func (addr *IPv4Address) GetGenericDivision(index int) DivisionType { return addr.init().getDivision(index) } // GetGenericSegment returns the segment at the given index as an AddressSegmentType. // The first segment is at index 0. // GetGenericSegment will panic given a negative index or an index matching or larger than the segment count. func (addr *IPv4Address) GetGenericSegment(index int) AddressSegmentType { return addr.init().getSegment(index) } // GetDivisionCount returns the segment count. func (addr *IPv4Address) GetDivisionCount() int { return addr.init().getDivisionCount() } // GetIPVersion returns IPv4, the IP version of this address. func (addr *IPv4Address) GetIPVersion() IPVersion { return IPv4 } func (addr *IPv4Address) checkIdentity(section *IPv4AddressSection) *IPv4Address { if section == nil { return nil } sec := section.ToSectionBase() if sec == addr.section { return addr } return newIPv4Address(section) } // Mask applies the given mask to all addresses represented by this IPv4Address. // The mask is applied to all individual addresses. // // If this represents multiple addresses, and applying the mask to all addresses creates a set of addresses // that cannot be represented as a sequential range within each segment, then an error is returned. func (addr *IPv4Address) Mask(other *IPv4Address) (masked *IPv4Address, err addrerr.IncompatibleAddressError) { return addr.maskPrefixed(other, true) } func (addr *IPv4Address) maskPrefixed(other *IPv4Address, retainPrefix bool) (masked *IPv4Address, err addrerr.IncompatibleAddressError) { addr = addr.init() sect, err := addr.GetSection().maskPrefixed(other.GetSection(), retainPrefix) if err == nil { masked = addr.checkIdentity(sect) } return } // BitwiseOr does the bitwise disjunction with this address or subnet, useful when subnetting. // It is similar to Mask which does the bitwise conjunction. // // The operation is applied to all individual addresses and the result is returned. // // If this is a subnet representing multiple addresses, and applying the operation to all addresses creates a set of addresses // that cannot be represented as a sequential range within each segment, then an error is returned. func (addr *IPv4Address) BitwiseOr(other *IPv4Address) (masked *IPv4Address, err addrerr.IncompatibleAddressError) { return addr.bitwiseOrPrefixed(other, true) } func (addr *IPv4Address) bitwiseOrPrefixed(other *IPv4Address, retainPrefix bool) (masked *IPv4Address, err addrerr.IncompatibleAddressError) { addr = addr.init() sect, err := addr.GetSection().bitwiseOrPrefixed(other.GetSection(), retainPrefix) if err == nil { masked = addr.checkIdentity(sect) } return } // Subtract subtracts the given subnet from this subnet, returning an array of subnets for the result (the subnets will not be contiguous so an array is required). // Subtract computes the subnet difference, the set of addresses in this address subnet but not in the provided subnet. // This is also known as the relative complement of the given argument in this subnet. // This is set subtraction, not subtraction of address values (use Increment for the latter). We have a subnet of addresses and we are removing those addresses found in the argument subnet. // If there are no remaining addresses, nil is returned. func (addr *IPv4Address) Subtract(other *IPv4Address) []*IPv4Address { addr = addr.init() sects, _ := addr.GetSection().Subtract(other.GetSection()) sectLen := len(sects) if sectLen == 0 { return nil } else if sectLen == 1 { sec := sects[0] if sec.ToSectionBase() == addr.section { return []*IPv4Address{addr} } } res := make([]*IPv4Address, sectLen) for i, sect := range sects { res[i] = newIPv4Address(sect) } return res } // Intersect returns the subnet whose addresses are found in both this and the given subnet argument, or nil if no such addresses exist. // // This is also known as the conjunction of the two sets of addresses. func (addr *IPv4Address) Intersect(other *IPv4Address) *IPv4Address { addr = addr.init() section, _ := addr.GetSection().Intersect(other.GetSection()) if section == nil { return nil } return addr.checkIdentity(section) } // SpanWithRange returns an IPv4AddressSeqRange instance that spans this subnet to the given subnet. // If the other address is a different version than this, then the other is ignored, and the result is equivalent to calling ToSequentialRange. func (addr *IPv4Address) SpanWithRange(other *IPv4Address) *SequentialRange[*IPv4Address] { return NewSequentialRange(addr.init(), other) } // GetLower returns the lowest address in the subnet range, // which will be the receiver if it represents a single address. // For example, for "1.2-3.4.5-6", the series "1.2.4.5" is returned. func (addr *IPv4Address) GetLower() *IPv4Address { return addr.init().getLower().ToIPv4() } // GetUpper returns the highest address in the subnet range, // which will be the receiver if it represents a single address. // For example, for "1.2-3.4.5-6", the address "1.3.4.6" is returned. func (addr *IPv4Address) GetUpper() *IPv4Address { return addr.init().getUpper().ToIPv4() } // GetLowerIPAddress returns the address in the subnet or address collection with the lowest numeric value, // which will be the receiver if it represents a single address. // For example, for "1.2-3.4.5-6", the series "1.2.4.5" is returned. // GetLowerIPAddress implements the IPAddressRange interface func (addr *IPv4Address) GetLowerIPAddress() *IPAddress { return addr.GetLower().ToIP() } // GetUpperIPAddress returns the address in the subnet or address collection with the highest numeric value, // which will be the receiver if it represents a single address. // For example, for the subnet "1.2-3.4.5-6", the address "1.3.4.6" is returned. // GetUpperIPAddress implements the IPAddressRange interface func (addr *IPv4Address) GetUpperIPAddress() *IPAddress { return addr.GetUpper().ToIP() } // IsZeroHostLen returns whether the host section is always zero for all individual addresses in this subnet, // for the given prefix length. // // If the host section is zero length (there are zero host bits), IsZeroHostLen returns true. func (addr *IPv4Address) IsZeroHostLen(prefLen BitCount) bool { return addr.init().isZeroHostLen(prefLen) } // ToZeroHost converts the address or subnet to one in which all individual addresses have a host of zero, // the host being the bits following the prefix length. // If the address or subnet has no prefix length, then it returns an all-zero address. // // The returned address or subnet will have the same prefix and prefix length. // // For instance, the zero host of "1.2.3.4/16" is the individual address "1.2.0.0/16". // // This returns an error if the subnet is a range of addresses which cannot be converted to a range in which all addresses have zero hosts, // because the conversion results in a subnet segment that is not a sequential range of values. func (addr *IPv4Address) ToZeroHost() (*IPv4Address, addrerr.IncompatibleAddressError) { res, err := addr.init().toZeroHost(false) return res.ToIPv4(), err } // ToZeroHostLen converts the address or subnet to one in which all individual addresses have a host of zero, // the host being the bits following the given prefix length. // If this address or subnet has the same prefix length, then the returned one will too, otherwise the returned series will have no prefix length. // // For instance, the zero host of "1.2.3.4" for the prefix length of 16 is the address "1.2.0.0". // // This returns an error if the subnet is a range of addresses which cannot be converted to a range in which all addresses have zero hosts, // because the conversion results in a subnet segment that is not a sequential range of values. func (addr *IPv4Address) ToZeroHostLen(prefixLength BitCount) (*IPv4Address, addrerr.IncompatibleAddressError) { res, err := addr.init().toZeroHostLen(prefixLength) return res.ToIPv4(), err } // ToZeroNetwork converts the address or subnet to one in which all individual addresses have a network of zero, // the network being the bits within the prefix length. // If the address or subnet has no prefix length, then it returns an all-zero address. // // The returned address or subnet will have the same prefix length. func (addr *IPv4Address) ToZeroNetwork() *IPv4Address { return addr.init().toZeroNetwork().ToIPv4() } // IsMaxHostLen returns whether the host is all one-bits, the max value, for all individual addresses in this subnet, // for the given prefix length, the host being the bits following the prefix. // // If the host section is zero length (there are zero host bits), IsMaxHostLen returns true. func (addr *IPv4Address) IsMaxHostLen(prefLen BitCount) bool { return addr.init().isMaxHostLen(prefLen) } // ToMaxHost converts the address or subnet to one in which all individual addresses have a host of all one-bits, the max value, // the host being the bits following the prefix length. // If the address or subnet has no prefix length, then it returns an all-ones address, the max address. // // The returned address or subnet will have the same prefix and prefix length. // // For instance, the max host of "1.2.3.4/16" gives the broadcast address "1.2.255.255/16". // // This returns an error if the subnet is a range of addresses which cannot be converted to a range in which all addresses have max hosts, // because the conversion results in a subnet segment that is not a sequential range of values. func (addr *IPv4Address) ToMaxHost() (*IPv4Address, addrerr.IncompatibleAddressError) { res, err := addr.init().toMaxHost() return res.ToIPv4(), err } // ToMaxHostLen converts the address or subnet to one in which all individual addresses have a host of all one-bits, the max host, // the host being the bits following the given prefix length. // If this address or subnet has the same prefix length, then the resulting one will too, otherwise the resulting address or subnet will have no prefix length. // // For instance, the zero host of "1.2.3.4" for the prefix length of 16 is the address "1.2.255.255". // // This returns an error if the subnet is a range of addresses which cannot be converted to a range in which all addresses have max hosts, // because the conversion results in a subnet segment that is not a sequential range of values. func (addr *IPv4Address) ToMaxHostLen(prefixLength BitCount) (*IPv4Address, addrerr.IncompatibleAddressError) { res, err := addr.init().toMaxHostLen(prefixLength) return res.ToIPv4(), err } // Uint32Value returns the lowest address in the subnet range as a uint32. func (addr *IPv4Address) Uint32Value() uint32 { return addr.GetSection().Uint32Value() } // UpperUint32Value returns the highest address in the subnet range as a uint32. func (addr *IPv4Address) UpperUint32Value() uint32 { return addr.GetSection().UpperUint32Value() } // ToPrefixBlock returns the subnet associated with the prefix length of this address. // If this address has no prefix length, this address is returned. // // The subnet will include all addresses with the same prefix as this one, the prefix "block". // The network prefix will match the prefix of this address or subnet, and the host values will span all values. // // For example, if the address is "1.2.3.4/16" it returns the subnet "1.2.0.0/16", which can also be written as "1.2.*.*/16". func (addr *IPv4Address) ToPrefixBlock() *IPv4Address { return addr.init().toPrefixBlock().ToIPv4() } // ToPrefixBlockLen returns the subnet associated with the given prefix length. // // The subnet will include all addresses with the same prefix as this one, the prefix "block" for that prefix length. // The network prefix will match the prefix of this address or subnet, and the host values will span all values. // // For example, if the address is "1.2.3.4" and the prefix length provided is 16, it returns the subnet "1.2.0.0/16", which can also be written as "1.2.*.*/16". func (addr *IPv4Address) ToPrefixBlockLen(prefLen BitCount) *IPv4Address { return addr.init().toPrefixBlockLen(prefLen).ToIPv4() } // ToBlock creates a new block of addresses by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (addr *IPv4Address) ToBlock(segmentIndex int, lower, upper SegInt) *IPv4Address { return addr.init().toBlock(segmentIndex, lower, upper).ToIPv4() } // WithoutPrefixLen provides the same address but with no prefix length. The values remain unchanged. func (addr *IPv4Address) WithoutPrefixLen() *IPv4Address { if !addr.IsPrefixed() { return addr } return addr.init().withoutPrefixLen().ToIPv4() } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address. // The provided prefix length will be adjusted to these boundaries if necessary. func (addr *IPv4Address) SetPrefixLen(prefixLen BitCount) *IPv4Address { return addr.init().setPrefixLen(prefixLen).ToIPv4() } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this address has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this address has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr *IPv4Address) SetPrefixLenZeroed(prefixLen BitCount) (*IPv4Address, addrerr.IncompatibleAddressError) { res, err := addr.init().setPrefixLenZeroed(prefixLen) return res.ToIPv4(), err } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address. // // If this address has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (addr *IPv4Address) AdjustPrefixLen(prefixLen BitCount) *IPv4Address { return addr.init().adjustPrefixLen(prefixLen).ToIPv4() } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address. // // If this address has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // For example, "1.2.0.0/16" adjusted by -8 becomes "1.0.0.0/8". // "1.2.0.0/16" adjusted by 8 becomes "1.2.0.0/24". // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr *IPv4Address) AdjustPrefixLenZeroed(prefixLen BitCount) (*IPv4Address, addrerr.IncompatibleAddressError) { res, err := addr.init().adjustPrefixLenZeroed(prefixLen) return res.ToIPv4(), err } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this address. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such address - it is required that the range of values match the range of a prefix block. // If there is no such address, then nil is returned. // // Examples: // - 1.2.3.4 returns 1.2.3.4/32 // - 1.2.*.* returns 1.2.0.0/16 // - 1.2.*.0/24 returns 1.2.0.0/16 // - 1.2.*.4 returns nil // - 1.2.0-1.* returns 1.2.0.0/23 // - 1.2.1-2.* returns nil // - 1.2.252-255.* returns 1.2.252.0/22 // - 1.2.3.4/16 returns 1.2.3.4/32 func (addr *IPv4Address) AssignPrefixForSingleBlock() *IPv4Address { return addr.init().assignPrefixForSingleBlock().ToIPv4() } // AssignMinPrefixForBlock returns an equivalent subnet, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this subnet. // // In other words, this method assigns a prefix length to this subnet matching the largest prefix block in this subnet. // // Examples: // - 1.2.3.4 returns 1.2.3.4/32 // - 1.2.*.* returns 1.2.0.0/16 // - 1.2.*.0/24 returns 1.2.0.0/16 // - 1.2.*.4 returns 1.2.*.4/32 // - 1.2.0-1.* returns 1.2.0.0/23 // - 1.2.1-2.* returns 1.2.1-2.0/24 // - 1.2.252-255.* returns 1.2.252.0/22 // - 1.2.3.4/16 returns 1.2.3.4/32 func (addr *IPv4Address) AssignMinPrefixForBlock() *IPv4Address { return addr.init().assignMinPrefixForBlock().ToIPv4() } // ToSinglePrefixBlockOrAddress converts to a single prefix block or address. // If the given address is a single prefix block, it is returned. // If it can be converted to a single prefix block by assigning a prefix length, the converted block is returned. // If it is a single address, any prefix length is removed and the address is returned. // Otherwise, nil is returned. // This method provides the address formats used by tries. // ToSinglePrefixBlockOrAddress is quite similar to AssignPrefixForSingleBlock, which always returns prefixed addresses, while this does not. func (addr *IPv4Address) ToSinglePrefixBlockOrAddress() *IPv4Address { return addr.init().toSinglePrefixBlockOrAddr().ToIPv4() } func (addr *IPv4Address) toSinglePrefixBlockOrAddress() (*IPv4Address, addrerr.IncompatibleAddressError) { if addr == nil { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.address.not.block"}} } res := addr.ToSinglePrefixBlockOrAddress() if res == nil { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.address.not.block"}} } return res, nil } // ContainsPrefixBlock returns whether the range of this address or subnet contains the block of addresses for the given prefix length. // // Unlike ContainsSinglePrefixBlock, whether there are multiple prefix values in this item for the given prefix length makes no difference. // // Use GetMinPrefixLenForBlock to determine the smallest prefix length for which this method returns true. func (addr *IPv4Address) ContainsPrefixBlock(prefixLen BitCount) bool { return addr.init().ipAddressInternal.ContainsPrefixBlock(prefixLen) } // ContainsSinglePrefixBlock returns whether this address contains a single prefix block for the given prefix length. // // This means there is only one prefix value for the given prefix length, and it also contains the full prefix block for that prefix, all addresses with that prefix. // // Use GetPrefixLenForSingleBlock to determine whether there is a prefix length for which this method returns true. func (addr *IPv4Address) ContainsSinglePrefixBlock(prefixLen BitCount) bool { return addr.init().ipAddressInternal.ContainsSinglePrefixBlock(prefixLen) } // GetMinPrefixLenForBlock returns the smallest prefix length such that this includes the block of addresses for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this represents just a single address, returns the bit length of this address. func (addr *IPv4Address) GetMinPrefixLenForBlock() BitCount { return addr.init().ipAddressInternal.GetMinPrefixLenForBlock() } // GetPrefixLenForSingleBlock returns a prefix length for which the range of this address subnet matches exactly the block of addresses for that prefix. // // If the range can be described this way, then this method returns the same value as GetMinPrefixLenForBlock. // // If no such prefix exists, returns nil. // // If this segment grouping represents a single value, returns the bit length of this address division series. // // Examples: // - 1.2.3.4 returns 32 // - 1.2.3.4/16 returns 32 // - 1.2.*.* returns 16 // - 1.2.*.0/24 returns 16 // - 1.2.0.0/16 returns 16 // - 1.2.*.4 returns nil // - 1.2.252-255.* returns 22 func (addr *IPv4Address) GetPrefixLenForSingleBlock() PrefixLen { return addr.init().ipAddressInternal.GetPrefixLenForSingleBlock() } // GetValue returns the lowest address in this subnet or address as an integer value. func (addr *IPv4Address) GetValue() *big.Int { return addr.init().section.GetValue() } // GetUpperValue returns the highest address in this subnet or address as an integer value. func (addr *IPv4Address) GetUpperValue() *big.Int { return addr.init().section.GetUpperValue() } // GetNetIPAddr returns the lowest address in this subnet or address as a net.IPAddr. func (addr *IPv4Address) GetNetIPAddr() *net.IPAddr { return &net.IPAddr{ IP: addr.GetNetIP(), } } // GetUpperNetIPAddr returns the highest address in this subnet or address as a net.IPAddr. func (addr *IPv4Address) GetUpperNetIPAddr() *net.IPAddr { return &net.IPAddr{ IP: addr.GetUpperNetIP(), } } // GetNetIP returns the lowest address in this subnet or address as a net.IP. func (addr *IPv4Address) GetNetIP() net.IP { return addr.Bytes() } // GetUpperNetIP returns the highest address in this subnet or address as a net.IP. func (addr *IPv4Address) GetUpperNetIP() net.IP { return addr.UpperBytes() } // GetNetNetIPAddr returns the lowest address in this subnet or address range as a netip.Addr. func (addr *IPv4Address) GetNetNetIPAddr() netip.Addr { return addr.init().getNetNetIPAddr() } // GetUpperNetNetIPAddr returns the highest address in this subnet or address range as a netip.Addr. func (addr *IPv4Address) GetUpperNetNetIPAddr() netip.Addr { return addr.init().getUpperNetNetIPAddr() } // CopyNetIP copies the value of the lowest individual address in the subnet into a net.IP. // // If the value can fit in the given net.IP slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *IPv4Address) CopyNetIP(ip net.IP) net.IP { if ipv4 := ip.To4(); ipv4 != nil { // this shrinks the arg to 4 bytes if it was 16 ip = ipv4 } return addr.CopyBytes(ip) } // CopyUpperNetIP copies the value of the highest individual address in the subnet into a net.IP. // // If the value can fit in the given net.IP slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *IPv4Address) CopyUpperNetIP(ip net.IP) net.IP { if ipv4 := ip.To4(); ipv4 != nil { // this shrinks the arg to 4 bytes if it was 16 ip = ipv4 } return addr.CopyUpperBytes(ip) } // Bytes returns the lowest address in this subnet or address as a byte slice. func (addr *IPv4Address) Bytes() []byte { return addr.init().section.Bytes() } // UpperBytes returns the highest address in this subnet or address as a byte slice. func (addr *IPv4Address) UpperBytes() []byte { return addr.init().section.UpperBytes() } // CopyBytes copies the value of the lowest individual address in the subnet into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *IPv4Address) CopyBytes(bytes []byte) []byte { return addr.init().section.CopyBytes(bytes) } // CopyUpperBytes copies the value of the highest individual address in the subnet into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *IPv4Address) CopyUpperBytes(bytes []byte) []byte { return addr.init().section.CopyUpperBytes(bytes) } // IsMax returns whether this address matches exactly the maximum possible value, the address whose bits are all ones. func (addr *IPv4Address) IsMax() bool { return addr.init().section.IsMax() } // IncludesMax returns whether this address includes the max address, the address whose bits are all ones, within its range. func (addr *IPv4Address) IncludesMax() bool { return addr.init().section.IncludesMax() } // TestBit returns true if the bit in the lower value of this address at the given index is 1, where index 0 refers to the least significant bit. // In other words, it computes (bits & (1 << n)) != 0), using the lower value of this address. // TestBit will panic if n < 0, or if it matches or exceeds the bit count of this item. func (addr *IPv4Address) TestBit(n BitCount) bool { return addr.init().testBit(n) } // IsOneBit returns true if the bit in the lower value of this address at the given index is 1, where index 0 refers to the most significant bit. // IsOneBit will panic if bitIndex is less than zero, or if it is larger than the bit count of this item. func (addr *IPv4Address) IsOneBit(bitIndex BitCount) bool { return addr.init().isOneBit(bitIndex) } // PrefixEqual determines if the given address matches this address up to the prefix length of this address. // It returns whether the two addresses share the same range of prefix values. func (addr *IPv4Address) PrefixEqual(other AddressType) bool { return addr.init().prefixEquals(other) } // PrefixContains returns whether the prefix values in the given address or subnet // are prefix values in this address or subnet, using the prefix length of this address or subnet. // If this address has no prefix length, the entire address is compared. // // It returns whether the prefix of this address contains all values of the same prefix length in the given address. func (addr *IPv4Address) PrefixContains(other AddressType) bool { return addr.init().prefixContains(other) } // Contains returns whether this is the same type and version as the given address or subnet and whether it contains all addresses in the given address or subnet. func (addr *IPv4Address) Contains(other AddressType) bool { if other == nil || other.ToAddressBase() == nil { return true } else if addr == nil { return false } addr = addr.init() otherAddr := other.ToAddressBase() if addr.ToAddressBase() == otherAddr { return true } return otherAddr.getAddrType() == ipv4Type && addr.section.sameCountTypeContains(otherAddr.GetSection()) } // Compare returns a negative integer, zero, or a positive integer if this address or subnet is less than, equal, or greater than the given item. // Any address item is comparable to any other. func (addr *IPv4Address) Compare(item AddressItem) int { return CountComparator.Compare(addr, item) } // Equal returns whether the given address or subnet is equal to this address or subnet. // Two address instances are equal if they represent the same set of addresses. func (addr *IPv4Address) Equal(other AddressType) bool { if addr == nil { return other == nil || other.ToAddressBase() == nil } else if other.ToAddressBase() == nil { return false } return other.ToAddressBase().getAddrType() == ipv4Type && addr.init().section.sameCountTypeEquals(other.ToAddressBase().GetSection()) } // CompareSize compares the counts of two subnets or addresses or other items, the number of individual addresses or items within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether this subnet represents more individual addresses than another item. // // CompareSize returns a positive integer if this address or subnet has a larger count than the one given, zero if they are the same, or a negative integer if the other has a larger count. func (addr *IPv4Address) CompareSize(other AddressItem) int { if addr == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return addr.init().compareSize(other) } // TrieCompare compares two addresses according to address trie ordering. // It returns a number less than zero, zero, or a number greater than zero if the first address argument is less than, equal to, or greater than the second. // // The comparison is intended for individual addresses and CIDR prefix blocks. // If an address is neither an individual address nor a prefix block, it is treated like one: // // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *IPv4Address) TrieCompare(other *IPv4Address) int { return addr.init().trieCompare(other.ToAddressBase()) } // TrieIncrement returns the next address or block according to address trie ordering // // If an address is neither an individual address nor a prefix block, it is treated like one: // // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *IPv4Address) TrieIncrement() *IPv4Address { if res, ok := trieIncrement(addr); ok { return res } return nil } // TrieDecrement returns the previous address or block according to address trie ordering // // If an address is neither an individual address nor a prefix block, it is treated like one: // // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *IPv4Address) TrieDecrement() *IPv4Address { if res, ok := trieDecrement(addr); ok { return res } return nil } // MatchesWithMask applies the mask to this address and then compares the result with the given address, // returning true if they match, false otherwise. func (addr *IPv4Address) MatchesWithMask(other *IPv4Address, mask *IPv4Address) bool { return addr.init().GetSection().MatchesWithMask(other.GetSection(), mask.GetSection()) } // GetMaxSegmentValue returns the maximum possible segment value for this type of address. // // Note this is not the maximum of the range of segment values in this specific address, // this is the maximum value of any segment for this address type and version, determined by the number of bits per segment. func (addr *IPv4Address) GetMaxSegmentValue() SegInt { return addr.init().getMaxSegmentValue() } // ToSequentialRange creates a sequential range instance from the lowest and highest addresses in this subnet. // // The two will represent the same set of individual addresses if and only if IsSequential is true. // To get a series of ranges that represent the same set of individual addresses use the SequentialBlockIterator (or PrefixIterator), // and apply this method to each iterated subnet. // // If this represents just a single address then the returned instance covers just that single address as well. func (addr *IPv4Address) ToSequentialRange() *SequentialRange[*IPv4Address] { if addr == nil { return nil } addr = addr.init().WithoutPrefixLen() return newSequRangeUnchecked( addr.GetLower(), addr.GetUpper(), addr.isMultiple()) } func (addr *IPv4Address) getLowestHighestAddrs() (lower, upper *IPv4Address) { l, u := addr.ipAddressInternal.getLowestHighestAddrs() return l.ToIPv4(), u.ToIPv4() } // ToBroadcastAddress returns the IPv4 broadcast address. // The broadcast address has the same prefix but a host that is all 1 bits. // If this address or subnet is not prefixed, this returns the address of all 1 bits, the "max" address. // This returns an error if a prefixed and ranged-valued segment cannot be converted to a host of all ones and remain a range of consecutive values. func (addr *IPv4Address) ToBroadcastAddress() (*IPv4Address, addrerr.IncompatibleAddressError) { return addr.ToMaxHost() } // ToNetworkAddress returns the IPv4 network address. // The network address has the same prefix but a zero host. // If this address or subnet is not prefixed, this returns the zero "any" address. // This returns an error if a prefixed and ranged-valued segment cannot be converted to a host of all zeros and remain a range of consecutive values. func (addr *IPv4Address) ToNetworkAddress() (*IPv4Address, addrerr.IncompatibleAddressError) { return addr.ToZeroHost() } // ToAddressString retrieves or generates an IPAddressString instance for this IPAddress instance. // This may be the IPAddressString this instance was generated from, if it was generated from an IPAddressString. // // In general, users are intended to create IPAddress instances from IPAddressString instances, // while the reverse direction is generally not common and not useful, except under specific circumstances. // // However, the reverse direction can be useful under certain circumstances, // such as when maintaining a collection of HostIdentifierString instances. func (addr *IPv4Address) ToAddressString() *IPAddressString { return addr.init().ToIP().ToAddressString() } // IncludesZeroHostLen returns whether the subnet contains an individual address with a host of zero, an individual address for which all bits past the given prefix length are zero. func (addr *IPv4Address) IncludesZeroHostLen(networkPrefixLength BitCount) bool { return addr.init().includesZeroHostLen(networkPrefixLength) } // IncludesMaxHostLen returns whether the subnet contains an individual address with a host of all one-bits, an individual address for which all bits past the given prefix length are all ones. func (addr *IPv4Address) IncludesMaxHostLen(networkPrefixLength BitCount) bool { return addr.init().includesMaxHostLen(networkPrefixLength) } // IsLinkLocal returns whether the address is link local, whether unicast or multicast. func (addr *IPv4Address) IsLinkLocal() bool { if addr.IsMulticast() { //224.0.0.252 Link-local Multicast Name Resolution [RFC4795] return addr.GetSegment(0).Matches(224) && addr.GetSegment(1).IsZero() && addr.GetSegment(2).IsZero() && addr.GetSegment(3).Matches(252) } return addr.GetSegment(0).Matches(169) && addr.GetSegment(1).Matches(254) } // IsPrivate returns whether this is a unicast addresses allocated for private use, // as defined by RFC 1918. func (addr *IPv4Address) IsPrivate() bool { // refer to RFC 1918 // 10/8 prefix // 172.16/12 prefix (172.16.0.0 – 172.31.255.255) // 192.168/16 prefix seg0, seg1 := addr.GetSegment(0), addr.GetSegment(1) return seg0.Matches(10) || (seg0.Matches(172) && seg1.MatchesWithPrefixMask(16, 4)) || (seg0.Matches(192) && seg1.Matches(168)) } // IsMulticast returns whether this address or subnet is entirely multicast. func (addr *IPv4Address) IsMulticast() bool { // 1110... //224.0.0.0/4 return addr.GetSegment(0).MatchesWithPrefixMask(0xe0, 4) } // IsLocal returns true if the address is link local, site local, organization local, administered locally, or unspecified. // This includes both unicast and multicast. func (addr *IPv4Address) IsLocal() bool { if addr.IsMulticast() { //1110... seg0 := addr.GetSegment(0) //http://www.tcpipguide.com/free/t_IPMulticastAddressing.htm //RFC 4607 and https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml //239.0.0.0-239.255.255.255 organization local if seg0.matches(239) { return true } seg1, seg2 := addr.GetSegment(1), addr.GetSegment(2) // 224.0.0.0 to 224.0.0.255 local // includes link local multicast name resolution https://tools.ietf.org/html/rfc4795 224.0.0.252 return (seg0.matches(224) && seg1.IsZero() && seg2.IsZero()) || //232.0.0.1 - 232.0.0.255 Reserved for IANA allocation [RFC4607] //232.0.1.0 - 232.255.255.255 Reserved for local host allocation [RFC4607] (seg0.matches(232) && !(seg1.IsZero() && seg2.IsZero())) } return addr.IsLinkLocal() || addr.IsPrivate() || addr.IsAnyLocal() } // IsUnspecified returns whether this is the unspecified address. The unspecified address is the address that is all zeros. func (addr *IPv4Address) IsUnspecified() bool { return addr.section == nil || addr.IsZero() } // IsAnyLocal returns whether this address is the address which binds to any address on the local host. // This is the address that has the value of 0, aka the unspecified address. func (addr *IPv4Address) IsAnyLocal() bool { return addr.section == nil || addr.IsZero() } // IsLoopback returns whether this address is a loopback address, such as "127.0.0.1". func (addr *IPv4Address) IsLoopback() bool { return addr.section != nil && addr.GetSegment(0).Matches(127) } // Iterator provides an iterator to iterate through the individual addresses of this address or subnet. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual addresses. // // Call IsMultiple to determine if this instance represents multiple addresses, or GetCount for the count. func (addr *IPv4Address) Iterator() Iterator[*IPv4Address] { if addr == nil { return ipv4AddressIterator{nilAddrIterator()} } return ipv4AddressIterator{addr.init().addrIterator(nil)} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this subnet, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this subnet. // // If the subnet has no prefix length, then this is equivalent to Iterator. func (addr *IPv4Address) PrefixIterator() Iterator[*IPv4Address] { return ipv4AddressIterator{addr.init().prefixIterator(false)} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address or subnet. // Each iterated address or subnet will be a prefix block with the same prefix length as this address or subnet. // // If this address has no prefix length, then this is equivalent to Iterator. func (addr *IPv4Address) PrefixBlockIterator() Iterator[*IPv4Address] { return ipv4AddressIterator{addr.init().prefixIterator(true)} } // BlockIterator iterates through the addresses that can be obtained by iterating through all the upper segments up to the given segment count. // The segments following remain the same in all iterated addresses. // // For instance, given the IPv4 subnet "1-2.3-4.5-6.7" and the count argument 2, // BlockIterator will iterate through "1.3.5-6.7", "1.4.5-6.7", "2.3.5-6.7" and "2.4.5-6.7". func (addr *IPv4Address) BlockIterator(segmentCount int) Iterator[*IPv4Address] { return ipv4AddressIterator{addr.init().blockIterator(segmentCount)} } // SequentialBlockIterator iterates through the sequential subnets or addresses that make up this address or subnet. // // Practically, this means finding the count of segments for which the segments that follow are not full range, and then using BlockIterator with that segment count. // // For instance, given the IPv4 subnet "1-2.3-4.5-6.7-8", it will iterate through "1.3.5.7-8", "1.3.6.7-8", "1.4.5.7-8", "1.4.6.7-8", "2.3.5.7-8", "2.3.6.7-8", "2.4.6.7-8" and "2.4.6.7-8". // // Use GetSequentialBlockCount to get the number of iterated elements. func (addr *IPv4Address) SequentialBlockIterator() Iterator[*IPv4Address] { return ipv4AddressIterator{addr.init().sequentialBlockIterator()} } // GetSequentialBlockIndex gets the minimal segment index for which all following segments are full-range blocks. // // The segment at this index is not a full-range block itself, unless all segments are full-range. // The segment at this index and all following segments form a sequential range. // For the full subnet to be sequential, the preceding segments must be single-valued. func (addr *IPv4Address) GetSequentialBlockIndex() int { return addr.init().getSequentialBlockIndex() } // GetSequentialBlockCount provides the count of elements from the sequential block iterator, the minimal number of sequential subnets that comprise this subnet. func (addr *IPv4Address) GetSequentialBlockCount() *big.Int { return addr.getSequentialBlockCount() } func (addr *IPv4Address) rangeIterator( upper *IPv4Address, valsAreMultiple bool, prefixLen PrefixLen, segProducer func(addr *IPAddress, index int) *IPAddressSegment, segmentIteratorProducer func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment], segValueComparator func(seg1, seg2 *IPAddress, index int) bool, networkSegmentIndex, hostSegmentIndex int, prefixedSegIteratorProducer func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment], ) Iterator[*IPv4Address] { return ipv4AddressIterator{addr.ipAddressInternal.rangeIterator(upper.ToIP(), valsAreMultiple, prefixLen, segProducer, segmentIteratorProducer, segValueComparator, networkSegmentIndex, hostSegmentIndex, prefixedSegIteratorProducer)} } // IncrementBoundary returns the address that is the given increment from the range boundaries of this subnet. // // If the given increment is positive, adds the value to the upper address (GetUpper) in the subnet range to produce a new address. // If the given increment is negative, adds the value to the lower address (GetLower) in the subnet range to produce a new address. // If the increment is zero, returns this address. // // If this is a single address value, that address is simply incremented by the given increment value, positive or negative. // // On address overflow or underflow, IncrementBoundary returns nil. func (addr *IPv4Address) IncrementBoundary(increment int64) *IPv4Address { return addr.init().incrementBoundary(increment).ToIPv4() } // Increment returns the address from the subnet that is the given increment upwards into the subnet range, // with the increment of 0 returning the first address in the range. // // If the increment i matches or exceeds the subnet size count c, then i - c + 1 // is added to the upper address of the range. // An increment matching the subnet count gives you the address just above the highest address in the subnet. // // If the increment is negative, it is added to the lower address of the range. // To get the address just below the lowest address of the subnet, use the increment -1. // // If this is just a single address value, the address is simply incremented by the given increment, positive or negative. // // If this is a subnet with multiple values, a positive increment i is equivalent i + 1 values from the subnet iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the subnet count is equivalent to the same number of iterator values preceding the upper bound of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On address overflow or underflow, Increment returns nil. func (addr *IPv4Address) Increment(increment int64) *IPv4Address { return addr.init().increment(increment).ToIPv4() } // SpanWithPrefixBlocks returns an array of prefix blocks that cover the same set of addresses as this subnet. // // Unlike SpanWithPrefixBlocksTo, the result only includes addresses that are a part of this subnet. func (addr *IPv4Address) SpanWithPrefixBlocks() []*IPv4Address { if addr.IsSequential() { if addr.IsSinglePrefixBlock() { return []*IPv4Address{addr} } wrapped := wrapIPAddress(addr.ToIP()) spanning := getSpanningPrefixBlocks(wrapped, wrapped) return cloneToIPv4Addrs(spanning) } wrapped := wrapIPAddress(addr.ToIP()) return cloneToIPv4Addrs(spanWithPrefixBlocks(wrapped)) } // SpanWithPrefixBlocksTo returns the smallest slice of prefix block subnets that span from this subnet to the given subnet. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. // // From the list of returned subnets you can recover the original range (this to other) by converting each to IPAddressRange with ToSequentialRange // and them joining them into a single range with the Join method of IPAddressSeqRange. func (addr *IPv4Address) SpanWithPrefixBlocksTo(other *IPv4Address) []*IPv4Address { return cloneToIPv4Addrs( getSpanningPrefixBlocks( wrapIPAddress(addr.ToIP()), wrapIPAddress(other.ToIP()), ), ) } // SpanWithSequentialBlocks produces the smallest slice of sequential blocks that cover the same set of addresses as this subnet. // // This slice can be shorter than that produced by SpanWithPrefixBlocks and is never longer. // // Unlike SpanWithSequentialBlocksTo, this method only includes addresses that are a part of this subnet. func (addr *IPv4Address) SpanWithSequentialBlocks() []*IPv4Address { if addr.IsSequential() { return []*IPv4Address{addr} } wrapped := wrapIPAddress(addr.ToIP()) return cloneToIPv4Addrs(spanWithSequentialBlocks(wrapped)) } // SpanWithSequentialBlocksTo produces the smallest slice of sequential block subnets that span all values from this subnet to the given subnet. // The span will cover all addresses in both subnets and everything in between. // // Individual block subnets come in the form "1-3.1-4.5.6-8", however that particular subnet is not sequential since address "1.1.5.8" is in the subnet, // the next sequential address "1.1.5.9" is not in the subnet, and a higher address "1.2.5.6" is in the subnet. // Blocks are sequential when the first segment with a range of values is followed by segments that span all values. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. func (addr *IPv4Address) SpanWithSequentialBlocksTo(other *IPv4Address) []*IPv4Address { return cloneToIPv4Addrs( getSpanningSequentialBlocks( wrapIPAddress(addr.ToIP()), wrapIPAddress(other.ToIP()), ), ) } // CoverWithPrefixBlockTo returns the minimal-size prefix block that covers all the addresses spanning from this subnet to the given subnet. func (addr *IPv4Address) CoverWithPrefixBlockTo(other *IPv4Address) *IPv4Address { return addr.init().coverWithPrefixBlockTo(other.ToIP()).ToIPv4() } // CoverWithPrefixBlock returns the minimal-size prefix block that covers all the addresses in this subnet. // The resulting block will have a larger subnet size than this, unless this subnet is already a prefix block. func (addr *IPv4Address) CoverWithPrefixBlock() *IPv4Address { return addr.init().coverWithPrefixBlock().ToIPv4() } // // MergeToSequentialBlocks merges this with the list of addresses to produce the smallest array of sequential blocks. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. func (addr *IPv4Address) MergeToSequentialBlocks(addrs ...*IPv4Address) []*IPv4Address { series := cloneIPv4Addrs(addr, addrs) blocks := getMergedSequentialBlocks(series) return cloneToIPv4Addrs(blocks) } // MergeToPrefixBlocks merges this subnet with the list of subnets to produce the smallest array of CIDR prefix blocks. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. func (addr *IPv4Address) MergeToPrefixBlocks(addrs ...*IPv4Address) []*IPv4Address { series := cloneIPv4Addrs(addr, addrs) blocks := getMergedPrefixBlocks(series) return cloneToIPv4Addrs(blocks) } // ReverseBytes returns a new address with the bytes reversed. Any prefix length is dropped. func (addr *IPv4Address) ReverseBytes() *IPv4Address { addr = addr.init() return addr.checkIdentity(addr.GetSection().ReverseBytes()) } // ReverseBits returns a new address with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a segment range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (addr *IPv4Address) ReverseBits(perByte bool) (*IPv4Address, addrerr.IncompatibleAddressError) { addr = addr.init() res, err := addr.GetSection().ReverseBits(perByte) if err != nil { return nil, err } return addr.checkIdentity(res), nil } // ReverseSegments returns a new address with the segments reversed. func (addr *IPv4Address) ReverseSegments() *IPv4Address { addr = addr.init() return addr.checkIdentity(addr.GetSection().ReverseSegments()) } // ReplaceLen replaces segments starting from startIndex and ending before endIndex with the same number of segments starting at replacementStartIndex from the replacement section. // Mappings to or from indices outside the range of this or the replacement address are skipped. func (addr *IPv4Address) ReplaceLen(startIndex, endIndex int, replacement *IPv4Address, replacementIndex int) *IPv4Address { if replacementIndex <= 0 { startIndex -= replacementIndex replacementIndex = 0 } else if replacementIndex >= IPv4SegmentCount { return addr } // We must do a 1 to 1 adjustment of indices before calling the section replace which would do an adjustment of indices not 1 to 1. // Here we assume replacementIndex is 0 and working on the subsection starting at that index. // In other words, a replacementIndex of x on the whole section is equivalent to replacementIndex of 0 on the shorter subsection starting at x. // Then afterwards we use the original replacement index to work on the whole section again, adjusting as needed. startIndex, endIndex, replacementIndexAdjustment := adjust1To1Indices(startIndex, endIndex, IPv4SegmentCount, IPv4SegmentCount-replacementIndex) if startIndex == endIndex { return addr } replacementIndex += replacementIndexAdjustment count := endIndex - startIndex return addr.init().checkIdentity(addr.GetSection().ReplaceLen(startIndex, endIndex, replacement.GetSection(), replacementIndex, replacementIndex+count)) } // Replace replaces segments starting from startIndex with segments from the replacement section. // Mappings to or from indices outside the range of this address or the replacement section are skipped. func (addr *IPv4Address) Replace(startIndex int, replacement *IPv4AddressSection) *IPv4Address { // We must do a 1 to 1 adjustment of indices before calling the section replace which would do an adjustment of indices not 1 to 1. startIndex, endIndex, replacementIndex := adjust1To1Indices(startIndex, startIndex+replacement.GetSegmentCount(), IPv4SegmentCount, replacement.GetSegmentCount()) count := endIndex - startIndex return addr.init().checkIdentity(addr.GetSection().ReplaceLen(startIndex, endIndex, replacement, replacementIndex, replacementIndex+count)) } // GetLeadingBitCount returns the number of consecutive leading one or zero bits. // If ones is true, returns the number of consecutive leading one bits. // Otherwise, returns the number of consecutive leading zero bits. // // This method applies to the lower value of the range if this is a subnet representing multiple values. func (addr *IPv4Address) GetLeadingBitCount(ones bool) BitCount { return addr.init().getLeadingBitCount(ones) } // GetTrailingBitCount returns the number of consecutive trailing one or zero bits. // If ones is true, returns the number of consecutive trailing zero bits. // Otherwise, returns the number of consecutive trailing one bits. // // This method applies to the lower value of the range if this is a subnet representing multiple values. func (addr *IPv4Address) GetTrailingBitCount(ones bool) BitCount { return addr.init().getTrailingBitCount(ones) } // GetNetwork returns the singleton IPv4 network instance. func (addr *IPv4Address) GetNetwork() IPAddressNetwork { return ipv4Network } // GetIPv6Address creates an IPv6 mixed address using the given ipv6 segments and using this address for the embedded IPv4 segments func (addr *IPv4Address) GetIPv6Address(section *IPv6AddressSection) (*IPv6Address, addrerr.AddressError) { if section.GetSegmentCount() < IPv6MixedOriginalSegmentCount { return nil, &addressValueError{addressError: addressError{key: "ipaddress.mac.error.not.eui.convertible"}} } newSegs := createSegmentArray(IPv6SegmentCount) section = section.WithoutPrefixLen() section.copyDivisions(newSegs) sect, err := createMixedSection(newSegs, addr) if err != nil { return nil, err } return newIPv6Address(sect), nil } // GetIPv4MappedAddress returns the IPv4-mapped IPv6 address corresponding to this IPv4 address. // The IPv4-mapped IPv6 address is all zeros in the first 12 bytes, with the last 4 bytes matching the bytes of this IPv4 address. // See rfc 5156 for details. // If this is a subnet with segment ranges which cannot be converted to two IPv6 segment ranges, than an error is returned. func (addr *IPv4Address) GetIPv4MappedAddress() (*IPv6Address, addrerr.IncompatibleAddressError) { zero := zeroIPv6Seg.ToDiv() segs := createSegmentArray(IPv6SegmentCount) segs[0], segs[1], segs[2], segs[3], segs[4] = zero, zero, zero, zero, zero segs[5] = NewIPv6Segment(IPv6MaxValuePerSegment).ToDiv() var sect *IPv6AddressSection sect, err := createMixedSection(segs, addr.WithoutPrefixLen()) if err != nil { return nil, err } return newIPv6Address(sect), nil } // returns an error if the first or 3rd segments have a range of values that cannot be combined with their neighbouting segments into IPv6 segments func (addr *IPv4Address) getIPv6Address(ipv6Segs []*AddressDivision) (*IPv6Address, addrerr.IncompatibleAddressError) { newSegs := createSegmentArray(IPv6SegmentCount) copy(newSegs, ipv6Segs) sect, err := createMixedSection(newSegs, addr) if err != nil { return nil, err } return newIPv6Address(sect), nil } func createMixedSection(newIPv6Divisions []*AddressDivision, mixedSection *IPv4Address) (res *IPv6AddressSection, err addrerr.IncompatibleAddressError) { ipv4Section := mixedSection.GetSection().WithoutPrefixLen() var seg *IPv6AddressSegment if seg, err = ipv4Section.GetSegment(0).Join(ipv4Section.GetSegment(1)); err == nil { newIPv6Divisions[6] = seg.ToDiv() if seg, err = ipv4Section.GetSegment(2).Join(ipv4Section.GetSegment(3)); err == nil { newIPv6Divisions[7] = seg.ToDiv() res = newIPv6SectionFromMixed(newIPv6Divisions) if res.cache != nil { nonMixedSection := res.createNonMixedSection() mixedGrouping := newIPv6v4MixedGrouping( nonMixedSection, ipv4Section, ) mixed := &mixedCache{ defaultMixedAddressSection: mixedGrouping, embeddedIPv6Section: nonMixedSection, embeddedIPv4Section: ipv4Section, } res.cache.mixed = mixed } } } return } // Format implements [fmt.Formatter] interface. It accepts the formats // - 'v' for the default address and section format (either the normalized or canonical string), // - 's' (string) for the same, // - 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix), // - 'd' (decimal), 'x' (lowercase hexadecimal), and // - 'X' (uppercase hexadecimal). // Also supported are some of fmt's format flags for integral types. // Sign control is not supported since addresses and sections are never negative. // '#' for an alternate format is supported, which adds a leading zero for octal, and for hexadecimal it adds // a leading "0x" or "0X" for "%#x" and "%#X" respectively. // Also supported is specification of minimum digits precision, output field width, // space or zero padding, and '-' for left or right justification. func (addr IPv4Address) Format(state fmt.State, verb rune) { addr.init().format(state, verb) } // String implements the [fmt.Stringer] interface, returning the canonical string provided by ToCanonicalString, or "" if the receiver is a nil pointer. func (addr *IPv4Address) String() string { if addr == nil { return nilString() } return addr.init().toString() } // GetSegmentStrings returns a slice with the string for each segment being the string that is normalized with wildcards. func (addr *IPv4Address) GetSegmentStrings() []string { if addr == nil { return nil } return addr.init().getSegmentStrings() } // ToCanonicalString produces a canonical string for the address. // // For IPv4, dotted octet format, also known as dotted decimal format, is used. // https://datatracker.ietf.org/doc/html/draft-main-ipaddr-text-rep-00#section-2.1 // // Each address has a unique canonical string, not counting the prefix length. // With IP addresses, the prefix length can cause two equal addresses to have different strings, for example "1.2.3.4/16" and "1.2.3.4". // It can also cause two different addresses to have the same string, such as "1.2.0.0/16" for the individual address "1.2.0.0" and also the prefix block "1.2.*.*". // Use ToCanonicalWildcardString for a unique string for each IP address and subnet. func (addr *IPv4Address) ToCanonicalString() string { if addr == nil { return nilString() } return addr.init().toCanonicalString() } // ToNormalizedString produces a normalized string for the address. // // For IPv4, it is the same as the canonical string. // // Each address has a unique normalized string, not counting the prefix length. // With IP addresses, the prefix length can cause two equal addresses to have different strings, for example "1.2.3.4/16" and "1.2.3.4". // It can also cause two different addresses to have the same string, such as "1.2.0.0/16" for the individual address "1.2.0.0" and also the prefix block "1.2.*.*". // Use the method ToNormalizedWildcardString for a unique string for each IP address and subnet. func (addr *IPv4Address) ToNormalizedString() string { if addr == nil { return nilString() } return addr.init().toNormalizedString() } // ToCompressedString produces a short representation of this address while remaining within the confines of standard representation(s) of the address. // // For IPv4, it is the same as the canonical string. func (addr *IPv4Address) ToCompressedString() string { if addr == nil { return nilString() } return addr.init().toCompressedString() } // ToCanonicalWildcardString produces a string similar to the canonical string and avoids the CIDR prefix length. // Addresses and subnets with a network prefix length will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix length notation. // For IPv4 it is the same as ToNormalizedWildcardString. func (addr *IPv4Address) ToCanonicalWildcardString() string { if addr == nil { return nilString() } return addr.init().toCanonicalWildcardString() } // ToNormalizedWildcardString produces a string similar to the normalized string but avoids the CIDR prefix length. // CIDR addresses will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix notation. func (addr *IPv4Address) ToNormalizedWildcardString() string { if addr == nil { return nilString() } return addr.init().toNormalizedWildcardString() } // ToSegmentedBinaryString writes this address as segments of binary values preceded by the "0b" prefix. func (addr *IPv4Address) ToSegmentedBinaryString() string { if addr == nil { return nilString() } return addr.init().toSegmentedBinaryString() } // ToSQLWildcardString create a string similar to that from toNormalizedWildcardString except that // it uses SQL wildcards. It uses '%' instead of '*' and also uses the wildcard '_'. func (addr *IPv4Address) ToSQLWildcardString() string { if addr == nil { return nilString() } return addr.init().toSQLWildcardString() } // ToFullString produces a string with no compressed segments and all segments of full length with leading zeros, // which is 3 characters for IPv4 segments. func (addr *IPv4Address) ToFullString() string { if addr == nil { return nilString() } return addr.init().toFullString() } // ToReverseDNSString generates the reverse-DNS lookup string. // For IPV4, the error is always nil. // For "8.255.4.4" it is "4.4.255.8.in-addr.arpa". func (addr *IPv4Address) ToReverseDNSString() (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } str, _ := addr.init().toReverseDNSString() return str, nil } // ToPrefixLenString returns a string with a CIDR network prefix length if this address has a network prefix length. // For IPv6, a zero host section will be compressed with "::". For IPv4 the string is equivalent to the canonical string. func (addr *IPv4Address) ToPrefixLenString() string { if addr == nil { return nilString() } return addr.init().toPrefixLenString() } // ToSubnetString produces a string with specific formats for subnets. // The subnet string looks like "1.2.*.*" or "1:2::/16". // // In the case of IPv4, this means that wildcards are used instead of a network prefix when a network prefix has been supplied. func (addr *IPv4Address) ToSubnetString() string { if addr == nil { return nilString() } return addr.init().toSubnetString() } // ToCompressedWildcardString produces a string similar to ToNormalizedWildcardString, and in fact // for IPv4 it is the same as ToNormalizedWildcardString. func (addr *IPv4Address) ToCompressedWildcardString() string { if addr == nil { return nilString() } return addr.init().toCompressedWildcardString() } // ToHexString writes this address as a single hexadecimal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // If a subnet cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *IPv4Address) ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toHexString(with0xPrefix) } // ToOctalString writes this address as a single octal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0" prefix. // // If a subnet cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *IPv4Address) ToOctalString(with0Prefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toOctalString(with0Prefix) } // ToBinaryString writes this address as a single binary value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0b" prefix. // // If a subnet cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *IPv4Address) ToBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toBinaryString(with0bPrefix) } // ToUNCHostName Generates the Microsoft UNC path component for this address. // // For IPv4 it is the canonical string. func (addr *IPv4Address) ToUNCHostName() string { return addr.ToCanonicalString() } // ToInetAtonString returns a string with a format that is styled from the inet_aton routine. // The string can have an octal or hexadecimal radix rather than decimal. // When using octal, the octal segments each have a leading zero prefix of "0", and when using hex, a prefix of "0x". func (addr *IPv4Address) ToInetAtonString(radix Inet_aton_radix) string { if addr == nil { return nilString() } return addr.GetSection().ToInetAtonString(radix) } // ToInetAtonJoinedString returns a string with a format that is styled from the inet_aton routine. // The string can have an octal or hexadecimal radix rather than decimal, // and can have less than the typical four IPv4 segments by joining the least significant segments together, // resulting in a string which just 1, 2 or 3 divisions. // // When using octal, the octal segments each have a leading zero prefix of "0", and when using hex, a prefix of "0x". // // If this represents a subnet section, this returns an error when unable to join two or more segments // into a division of a larger bit-length that represents the same set of values. func (addr *IPv4Address) ToInetAtonJoinedString(radix Inet_aton_radix, joinedCount int) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.GetSection().ToInetAtonJoinedString(radix, joinedCount) } // ToCustomString creates a customized string from this address or subnet according to the given string option parameters. func (addr *IPv4Address) ToCustomString(stringOptions addrstr.IPStringOptions) string { if addr == nil { return nilString() } return addr.GetSection().toCustomString(stringOptions) } // ToAddressBase converts to an Address, a polymorphic type usable with all addresses and subnets. // Afterwards, you can convert back with ToIPv4. // // ToAddressBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *IPv4Address) ToAddressBase() *Address { return addr.ToIP().ToAddressBase() } // ToIP converts to an IPAddress, a polymorphic type usable with all IP addresses and subnets. // Afterwards, you can convert back with ToIPv4. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *IPv4Address) ToIP() *IPAddress { if addr != nil { addr = addr.init() } return (*IPAddress)(addr) } // Wrap wraps this IP address, returning a WrappedIPAddress, an implementation of ExtendedIPSegmentSeries, // which can be used to write code that works with both IP addresses and IP address sections. // Wrap can be called with a nil receiver, wrapping a nil address. func (addr *IPv4Address) Wrap() WrappedIPAddress { return wrapIPAddress(addr.ToIP()) } // WrapAddress wraps this IP address, returning a WrappedAddress, an implementation of ExtendedSegmentSeries, // which can be used to write code that works with both addresses and address sections. // WrapAddress can be called with a nil receiver, wrapping a nil address. func (addr *IPv4Address) WrapAddress() WrappedAddress { return wrapAddress(addr.ToAddressBase()) } func (addr *IPv4Address) toMaxLower() *IPv4Address { return addr.init().addressInternal.toMaxLower().ToIPv4() } func (addr *IPv4Address) toMinUpper() *IPv4Address { return addr.init().addressInternal.toMinUpper().ToIPv4() } // ToKey creates the associated address key. // While addresses can be compared with the Compare, TrieCompare or Equal methods as well as various provided instances of AddressComparator, // they are not comparable with Go operators. // However, AddressKey instances are comparable with Go operators, and thus can be used as map keys. func (addr *IPv4Address) ToKey() IPv4AddressKey { addr = addr.init() key := IPv4AddressKey{} section := addr.GetSection() divs := section.getDivArray() var newVal uint64 if addr.IsMultiple() { for _, div := range divs { seg := div.ToIPv4() newVal = (newVal << IPv4BitsPerSegment) | uint64(seg.GetIPv4SegmentValue()) | (uint64(seg.GetIPv4UpperSegmentValue()) << IPv4BitCount) } } else { for _, div := range divs { seg := div.ToIPv4() newVal = (newVal << IPv4BitsPerSegment) | uint64(seg.GetIPv4SegmentValue()) } newVal |= newVal << IPv4BitCount } key.vals = newVal return key } func fromIPv4Key(key IPv4AddressKey) *IPv4Address { keyVal := key.vals return NewIPv4AddressFromRange( func(segmentIndex int) IPv4SegInt { segIndex := (IPv4SegmentCount - 1) - segmentIndex return IPv4SegInt(keyVal >> (segIndex << ipv4BitsToSegmentBitshift)) }, func(segmentIndex int) IPv4SegInt { segIndex := ((IPv4SegmentCount << 1) - 1) - segmentIndex return IPv4SegInt(keyVal >> (segIndex << ipv4BitsToSegmentBitshift)) }, ) } // ToGenericKey produces a generic Key[*IPv4Address] that can be used with generic code working with [Address], [IPAddress], [IPv4Address], [IPv6Address] and [MACAddress]. // ToKey produces a more compact key for code that is IPv4-specific. func (addr *IPv4Address) ToGenericKey() Key[*IPv4Address] { // Note: We intentionally do not populate the "scheme" field, which is used with Key[*Address] and Key[*IPAddress] and 64-bit Key[*MACAddress]. // With Key[*IPv4Address], by leaving the scheme zero, the zero Key[*IPv4Address] matches up with the key produced here by the zero address. // We do not need the scheme field for Key[*IPv4Address] since the generic type indicates IPv4. key := Key[*IPv4Address]{} addr.init().toIPv4Key(&key.keyContents) return key } func (addr *IPv4Address) fromKey(_ addressScheme, key *keyContents) *IPv4Address { return fromIPv4IPKey(key) } func (addr *IPv4Address) toIPv4Key(contents *keyContents) { section := addr.GetSection() divs := section.getDivArray() val := &contents.vals[0] if addr.IsMultiple() { for _, div := range divs { seg := div.ToIPv4() val.lower = (val.lower << IPv4BitsPerSegment) | uint64(seg.GetIPv4SegmentValue()) val.upper = (val.upper << IPv4BitsPerSegment) | uint64(seg.GetIPv4UpperSegmentValue()) } } else { for _, div := range divs { seg := div.ToIPv4() val.lower = (val.lower << IPv4BitsPerSegment) | uint64(seg.GetIPv4SegmentValue()) val.upper = val.lower } } } func fromIPv4IPKey(key *keyContents) *IPv4Address { return NewIPv4AddressFromRange( func(segmentIndex int) IPv4SegInt { segIndex := (IPv4SegmentCount - 1) - segmentIndex return IPv4SegInt(key.vals[0].lower >> (segIndex << ipv4BitsToSegmentBitshift)) }, func(segmentIndex int) IPv4SegInt { segIndex := (IPv4SegmentCount - 1) - segmentIndex return IPv4SegInt(key.vals[0].upper >> (segIndex << ipv4BitsToSegmentBitshift)) }, ) } ipaddress-go-1.5.4/ipaddr/ipv4section.go000066400000000000000000002016551440250641600201560ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math/big" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) func createIPv4Section(segments []*AddressDivision) *IPv4AddressSection { return &IPv4AddressSection{ ipAddressSectionInternal{ addressSectionInternal{ addressDivisionGroupingInternal{ addressDivisionGroupingBase: addressDivisionGroupingBase{ divisions: standardDivArray(segments), addrType: ipv4Type, cache: &valueCache{ stringCache: stringCache{ ipStringCache: &ipStringCache{}, ipv4StringCache: &ipv4StringCache{}, }, }, }, }, }, }, } } func newIPv4SectionParsed(segments []*AddressDivision, isMultiple bool) (res *IPv4AddressSection) { res = createIPv4Section(segments) res.isMult = isMultiple return } // this one is used by that parsing code when there are prefix lengths to be applied func newPrefixedIPv4SectionParsed(segments []*AddressDivision, isMultiple bool, prefixLength PrefixLen, singleOnly bool) (res *IPv4AddressSection) { res = createIPv4Section(segments) res.isMult = isMultiple if prefixLength != nil { assignPrefix(prefixLength, segments, res.ToIP(), singleOnly, false, BitCount(len(segments)< 0 { bytes = cloneBytes(bytes) res.cache.bytesCache = &bytesCache{lowerBytes: bytes} if !res.isMult { // not a prefix block res.cache.bytesCache.upperBytes = bytes } } } return } // NewIPv4SectionFromVals constructs an IPv4 address section of the given segment count from the given values. func NewIPv4SectionFromVals(vals IPv4SegmentValueProvider, segmentCount int) (res *IPv4AddressSection) { res = NewIPv4SectionFromPrefixedRange(vals, nil, segmentCount, nil) return } // NewIPv4SectionFromPrefixedVals constructs an IPv4 address or prefix block section of the given segment count from the given values and prefix length. func NewIPv4SectionFromPrefixedVals(vals IPv4SegmentValueProvider, segmentCount int, prefixLength PrefixLen) (res *IPv4AddressSection) { return NewIPv4SectionFromPrefixedRange(vals, nil, segmentCount, prefixLength) } // NewIPv4SectionFromRange constructs an IPv4 subnet section of the given segment count from the given values. func NewIPv4SectionFromRange(vals, upperVals IPv4SegmentValueProvider, segmentCount int) (res *IPv4AddressSection) { res = NewIPv4SectionFromPrefixedRange(vals, upperVals, segmentCount, nil) return } // NewIPv4SectionFromPrefixedRange constructs an IPv4 subnet section of the given segment count from the given values and prefix length. func NewIPv4SectionFromPrefixedRange(vals, upperVals IPv4SegmentValueProvider, segmentCount int, prefixLength PrefixLen) (res *IPv4AddressSection) { return newIPv4SectionFromPrefixedSingle(vals, upperVals, segmentCount, prefixLength, false) } func newIPv4SectionFromPrefixedSingle(vals, upperVals IPv4SegmentValueProvider, segmentCount int, prefixLength PrefixLen, singleOnly bool) (res *IPv4AddressSection) { if segmentCount < 0 { segmentCount = 0 } segments, isMultiple := createSegments( WrapIPv4SegmentValueProvider(vals), WrapIPv4SegmentValueProvider(upperVals), segmentCount, IPv4BitsPerSegment, ipv4Network.getIPAddressCreator(), prefixLength) res = createIPv4Section(segments) res.isMult = isMultiple if prefixLength != nil { assignPrefix(prefixLength, segments, res.ToIP(), singleOnly, false, BitCount(segmentCount<= 1 return -1 } return section.compareSize(other) } // GetBitsPerSegment returns the number of bits comprising each segment in this section. Segments in the same address section are equal length. func (section *IPv4AddressSection) GetBitsPerSegment() BitCount { return IPv4BitsPerSegment } // GetBytesPerSegment returns the number of bytes comprising each segment in this section. Segments in the same address section are equal length. func (section *IPv4AddressSection) GetBytesPerSegment() int { return IPv4BytesPerSegment } // GetIPVersion returns IPv4, the IP version of this address section. func (section *IPv4AddressSection) GetIPVersion() IPVersion { return IPv4 } // IsMultiple returns whether this section represents multiple values. func (section *IPv4AddressSection) IsMultiple() bool { return section != nil && section.isMultiple() } // IsPrefixed returns whether this section has an associated prefix length. func (section *IPv4AddressSection) IsPrefixed() bool { return section != nil && section.isPrefixed() } // GetCount returns the count of possible distinct values for this section. // It is the same as GetIPv4Count but returns the value as a big integer instead of a uint64. // If not representing multiple values, the count is 1, // unless this is a division grouping with no divisions, or an address section with no segments, in which case it is 0. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (section *IPv4AddressSection) GetCount() *big.Int { if section == nil { return bigZero() } return section.cacheCount(func() *big.Int { return bigZero().SetUint64(section.getIPv4Count()) }) } func (section *IPv4AddressSection) getCachedCount() *big.Int { if section == nil { return bigZero() } return section.cachedCount(func() *big.Int { return bigZero().SetUint64(section.getIPv4Count()) }) } // GetIPv4Count returns the count of possible distinct values for this section. // It is the same as GetCount but returns the value as a uint64 instead of a big integer. // If not representing multiple values, the count is 1, // unless this is a division grouping with no divisions, or an address section with no segments, in which case it is 0. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (section *IPv4AddressSection) GetIPv4Count() uint64 { if section == nil { return 0 } return section.getCachedCount().Uint64() } func (section *IPv4AddressSection) getIPv4Count() uint64 { if !section.isMultiple() { return 1 } return longCount(section.ToSectionBase(), section.GetSegmentCount()) } // GetPrefixCount returns the number of distinct prefix values in this item. // // The prefix length is given by GetPrefixLen. // // If this has a non-nil prefix length, returns the number of distinct prefix values. // // If this has a nil prefix length, returns the same value as GetCount. func (section *IPv4AddressSection) GetPrefixCount() *big.Int { return section.cachePrefixCount(func() *big.Int { return bigZero().SetUint64(section.getIPv4PrefixCount()) }) } // GetIPv4PrefixCount returns the number of distinct prefix values in this section. // It is similar to GetPrefixCount but returns a uint64. // // The prefix length is given by GetPrefixLen. // // If this has a non-nil prefix length, returns the number of distinct prefix values. // // If this has a nil prefix length, returns the same value as GetIPv4Count. func (section *IPv4AddressSection) GetIPv4PrefixCount() uint64 { return section.cacheUint64PrefixCount(func() uint64 { return section.getIPv4PrefixCount() }) } func (section *IPv4AddressSection) getIPv4PrefixCount() uint64 { prefixLength := section.getPrefixLen() if prefixLength == nil { return section.GetIPv4Count() } return section.GetIPv4PrefixCountLen(prefixLength.bitCount()) } // GetPrefixCountLen returns the number of distinct prefix values in this item for the given prefix length. func (section *IPv4AddressSection) GetPrefixCountLen(prefixLen BitCount) *big.Int { if prefixLen <= 0 { return bigOne() } else if bc := section.GetBitCount(); prefixLen > bc { prefixLen = bc } return section.calcCount(func() *big.Int { return new(big.Int).SetUint64(section.GetIPv4PrefixCountLen(prefixLen)) }) } // GetIPv4PrefixCountLen returns the number of distinct prefix values in this item for the given prefix length. // // It is the same as GetPrefixCountLen but returns a uint64, not a *big.Int. func (section *IPv4AddressSection) GetIPv4PrefixCountLen(prefixLength BitCount) uint64 { if !section.isMultiple() { return 1 } else if prefixLength >= section.GetBitCount() { return section.GetIPv4Count() } else if prefixLength < 0 { prefixLength = 0 } return longPrefixCount(section.ToSectionBase(), prefixLength) } // GetIPv4BlockCount returns the count of distinct values in the given number of initial (more significant) segments. // It is similar to GetBlockCount but returns a uint64 instead of a big integer. func (section *IPv4AddressSection) GetIPv4BlockCount(segmentCount int) uint64 { if !section.isMultiple() { return 1 } return longCount(section.ToSectionBase(), segmentCount) } // GetBlockCount returns the count of distinct values in the given number of initial (more significant) segments. // It is similar to GetIPv4BlockCount but returns a big integer instead of a uint64. func (section *IPv4AddressSection) GetBlockCount(segmentCount int) *big.Int { if segmentCount <= 0 { return bigOne() } return section.calcCount(func() *big.Int { return new(big.Int).SetUint64(section.GetIPv4BlockCount(segmentCount)) }) } // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. func (section *IPv4AddressSection) GetSegment(index int) *IPv4AddressSegment { return section.getDivision(index).ToIPv4() } // ForEachSegment visits each segment in order from most-significant to least, the most significant with index 0, calling the given function for each, terminating early if the function returns true. // Returns the number of visited segments. func (section *IPv4AddressSection) ForEachSegment(consumer func(segmentIndex int, segment *IPv4AddressSegment) (stop bool)) int { divArray := section.getDivArray() if divArray != nil { for i, div := range divArray { if consumer(i, div.ToIPv4()) { return i + 1 } } } return len(divArray) } // GetTrailingSection gets the subsection from the series starting from the given index. // The first segment is at index 0. func (section *IPv4AddressSection) GetTrailingSection(index int) *IPv4AddressSection { return section.GetSubSection(index, section.GetSegmentCount()) } // GetSubSection gets the subsection from the series starting from the given index and ending just before the give endIndex. // The first segment is at index 0. func (section *IPv4AddressSection) GetSubSection(index, endIndex int) *IPv4AddressSection { return section.getSubSection(index, endIndex).ToIPv4() } // GetNetworkSection returns a subsection containing the segments with the network bits of the section. // The returned section will have only as many segments as needed as determined by the existing CIDR network prefix length. // // If this series has no CIDR prefix length, the returned network section will // be the entire series as a prefixed section with prefix length matching the address bit length. func (section *IPv4AddressSection) GetNetworkSection() *IPv4AddressSection { return section.getNetworkSection().ToIPv4() } // GetNetworkSectionLen returns a subsection containing the segments with the network of the section, the prefix bits according to the given prefix length. // The returned section will have only as many segments as needed to contain the network. // // The new section will be assigned the given prefix length, // unless the existing prefix length is smaller, in which case the existing prefix length will be retained. func (section *IPv4AddressSection) GetNetworkSectionLen(prefLen BitCount) *IPv4AddressSection { return section.getNetworkSectionLen(prefLen).ToIPv4() } // GetHostSection returns a subsection containing the segments with the host of the address section, the bits beyond the CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. // // If this series has no prefix length, the returned host section will be the full section. func (section *IPv4AddressSection) GetHostSection() *IPv4AddressSection { return section.getHostSection().ToIPv4() } // GetHostSectionLen returns a subsection containing the segments with the host of the address section, the bits beyond the given CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. // The returned section will have an assigned prefix length indicating the beginning of the host. func (section *IPv4AddressSection) GetHostSectionLen(prefLen BitCount) *IPv4AddressSection { return section.getHostSectionLen(prefLen).ToIPv4() } // GetNetworkMask returns the network mask associated with the CIDR network prefix length of this address section. // If this section has no prefix length, then the all-ones mask is returned. func (section *IPv4AddressSection) GetNetworkMask() *IPv4AddressSection { return section.getNetworkMask(ipv4Network).ToIPv4() } // GetHostMask returns the host mask associated with the CIDR network prefix length of this address section. // If this section has no prefix length, then the all-ones mask is returned. func (section *IPv4AddressSection) GetHostMask() *IPv4AddressSection { return section.getHostMask(ipv4Network).ToIPv4() } // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (section *IPv4AddressSection) CopySubSegments(start, end int, segs []*IPv4AddressSegment) (count int) { start, end, targetStart := adjust1To1StartIndices(start, end, section.GetDivisionCount(), len(segs)) segs = segs[targetStart:] return section.forEachSubDivision(start, end, func(index int, div *AddressDivision) { segs[index] = div.ToIPv4() }, len(segs)) } // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. func (section *IPv4AddressSection) CopySegments(segs []*IPv4AddressSegment) (count int) { return section.ForEachSegment(func(index int, seg *IPv4AddressSegment) (stop bool) { if stop = index >= len(segs); !stop { segs[index] = seg } return }) } // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as this section. func (section *IPv4AddressSection) GetSegments() (res []*IPv4AddressSegment) { res = make([]*IPv4AddressSegment, section.GetSegmentCount()) section.CopySegments(res) return } // Mask applies the given mask to all address sections represented by this secction, returning the result. // // If the sections do not have a comparable number of segments, an error is returned. // // If this represents multiple addresses, and applying the mask to all addresses creates a set of addresses // that cannot be represented as a sequential range within each segment, then an error is returned. func (section *IPv4AddressSection) Mask(other *IPv4AddressSection) (res *IPv4AddressSection, err addrerr.IncompatibleAddressError) { return section.maskPrefixed(other, true) } func (section *IPv4AddressSection) maskPrefixed(other *IPv4AddressSection, retainPrefix bool) (res *IPv4AddressSection, err addrerr.IncompatibleAddressError) { sec, err := section.mask(other.ToIP(), retainPrefix) if err == nil { res = sec.ToIPv4() } return } // BitwiseOr does the bitwise disjunction with this address section, useful when subnetting. // It is similar to Mask which does the bitwise conjunction. // // The operation is applied to all individual addresses and the result is returned. // // If this represents multiple address sections, and applying the operation to all sections creates a set of sections // that cannot be represented as a sequential range within each segment, then an error is returned. func (section *IPv4AddressSection) BitwiseOr(other *IPv4AddressSection) (res *IPv4AddressSection, err addrerr.IncompatibleAddressError) { return section.bitwiseOrPrefixed(other, true) } func (section *IPv4AddressSection) bitwiseOrPrefixed(other *IPv4AddressSection, retainPrefix bool) (res *IPv4AddressSection, err addrerr.IncompatibleAddressError) { sec, err := section.bitwiseOr(other.ToIP(), retainPrefix) if err == nil { res = sec.ToIPv4() } return } // MatchesWithMask applies the mask to this address section and then compares the result with the given address section, // returning true if they match, false otherwise. To match, both the given section and mask must have the same number of segments as this section. func (section *IPv4AddressSection) MatchesWithMask(other *IPv4AddressSection, mask *IPv4AddressSection) bool { return section.matchesWithMask(other.ToIP(), mask.ToIP()) } // Subtract subtracts the given subnet sections from this subnet section, returning an array of sections for the result (the subnet sections will not be contiguous so an array is required). // // Subtract computes the subnet difference, the set of address sections in this address section but not in the provided section. // This is also known as the relative complement of the given argument in this subnet section. // // This is set subtraction, not subtraction of values. func (section *IPv4AddressSection) Subtract(other *IPv4AddressSection) (res []*IPv4AddressSection, err addrerr.SizeMismatchError) { sections, err := section.subtract(other.ToIP()) if err == nil { res = cloneTo(sections, (*IPAddressSection).ToIPv4) } return } // Intersect returns the subnet sections whose individual sections are found in both this and the given subnet section argument, or nil if no such sections exist. // // This is also known as the conjunction of the two sets of address sections. // // If the two sections have different segment counts, an error is returned. func (section *IPv4AddressSection) Intersect(other *IPv4AddressSection) (res *IPv4AddressSection, err addrerr.SizeMismatchError) { sec, err := section.intersect(other.ToIP()) if err == nil { res = sec.ToIPv4() } return } // GetLower returns the section in the range with the lowest numeric value, // which will be the same section if it represents a single value. // For example, for "1.2-3.4.5-6", the section "1.2.4.5" is returned. func (section *IPv4AddressSection) GetLower() *IPv4AddressSection { return section.getLower().ToIPv4() } // GetUpper returns the section in the range with the highest numeric value, // which will be the same section if it represents a single value. // For example, for "1.2-3.4.5-6", the section "1.3.4.6" is returned. func (section *IPv4AddressSection) GetUpper() *IPv4AddressSection { return section.getUpper().ToIPv4() } // ToZeroHost converts the address section to one in which all individual address sections have a host of zero, // the host being the bits following the prefix length. // If the address section has no prefix length, then it returns an all-zero address section. // // The returned section will have the same prefix and prefix length. // // This returns an error if the section is a range of address sections which cannot be converted to a range in which all sections have zero hosts, // because the conversion results in a segment that is not a sequential range of values. func (section *IPv4AddressSection) ToZeroHost() (*IPv4AddressSection, addrerr.IncompatibleAddressError) { res, err := section.toZeroHost(false) return res.ToIPv4(), err } // ToZeroHostLen converts the address section to one in which all individual sections have a host of zero, // the host being the bits following the given prefix length. // If this address section has the same prefix length, then the returned one will too, otherwise the returned section will have no prefix length. // // This returns an error if the section is a range of which cannot be converted to a range in which all sections have zero hosts, // because the conversion results in a segment that is not a sequential range of values. func (section *IPv4AddressSection) ToZeroHostLen(prefixLength BitCount) (*IPv4AddressSection, addrerr.IncompatibleAddressError) { res, err := section.toZeroHostLen(prefixLength) return res.ToIPv4(), err } // ToZeroNetwork converts the address section to one in which all individual address sections have a network of zero, // the network being the bits within the prefix length. // If the address section has no prefix length, then it returns an all-zero address section. // // The returned address section will have the same prefix length. func (section *IPv4AddressSection) ToZeroNetwork() *IPv4AddressSection { return section.toZeroNetwork().ToIPv4() } // ToMaxHost converts the address section to one in which all individual address sections have a host of all one-bits, the max value, // the host being the bits following the prefix length. // If the address section has no prefix length, then it returns an all-ones section, the max address section. // // The returned address section will have the same prefix and prefix length. // // This returns an error if the address section is a range of address sections which cannot be converted to a range in which all sections have max hosts, // because the conversion results in a segment that is not a sequential range of values. func (section *IPv4AddressSection) ToMaxHost() (*IPv4AddressSection, addrerr.IncompatibleAddressError) { res, err := section.toMaxHost() return res.ToIPv4(), err } // ToMaxHostLen converts the address section to one in which all individual address sections have a host of all one-bits, the max host, // the host being the bits following the given prefix length. // If this section has the same prefix length, then the resulting section will too, otherwise the resulting section will have no prefix length. // // This returns an error if the section is a range of address sections which cannot be converted to a range in which all address sections have max hosts, // because the conversion results in a segment that is not a sequential range of values. func (section *IPv4AddressSection) ToMaxHostLen(prefixLength BitCount) (*IPv4AddressSection, addrerr.IncompatibleAddressError) { res, err := section.toMaxHostLen(prefixLength) return res.ToIPv4(), err } // Uint32Value returns the lowest address in the address section range as a uint32. func (section *IPv4AddressSection) Uint32Value() uint32 { segCount := section.GetSegmentCount() if segCount == 0 { return 0 } arr := section.getDivArray() val := uint32(arr[0].getDivisionValue()) bitsPerSegment := section.GetBitsPerSegment() for i := 1; i < segCount; i++ { val = (val << uint(bitsPerSegment)) | uint32(arr[i].getDivisionValue()) } return val } // UpperUint32Value returns the highest address in the address section range as a uint32. func (section *IPv4AddressSection) UpperUint32Value() uint32 { segCount := section.GetSegmentCount() if segCount == 0 { return 0 } arr := section.getDivArray() val := uint32(arr[0].getUpperDivisionValue()) bitsPerSegment := section.GetBitsPerSegment() for i := 1; i < segCount; i++ { val = (val << uint(bitsPerSegment)) | uint32(arr[i].getUpperDivisionValue()) } return val } // ToPrefixBlock returns the section with the same prefix as this section while the remaining bits span all values. // The returned section will be the block of all sections with the same prefix. // // If this section has no prefix, this section is returned. func (section *IPv4AddressSection) ToPrefixBlock() *IPv4AddressSection { return section.toPrefixBlock().ToIPv4() } // ToPrefixBlockLen returns the section with the same prefix of the given length as this section while the remaining bits span all values. // The returned section will be the block of all sections with the same prefix. func (section *IPv4AddressSection) ToPrefixBlockLen(prefLen BitCount) *IPv4AddressSection { return section.toPrefixBlockLen(prefLen).ToIPv4() } // ToBlock creates a new block of address sections by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (section *IPv4AddressSection) ToBlock(segmentIndex int, lower, upper SegInt) *IPv4AddressSection { return section.toBlock(segmentIndex, lower, upper).ToIPv4() } // WithoutPrefixLen provides the same address section but with no prefix length. The values remain unchanged. func (section *IPv4AddressSection) WithoutPrefixLen() *IPv4AddressSection { if !section.IsPrefixed() { return section } return section.withoutPrefixLen().ToIPv4() } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address section. // The provided prefix length will be adjusted to these boundaries if necessary. func (section *IPv4AddressSection) SetPrefixLen(prefixLen BitCount) *IPv4AddressSection { return section.setPrefixLen(prefixLen).ToIPv4() } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address section. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this address section has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this address section has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section *IPv4AddressSection) SetPrefixLenZeroed(prefixLen BitCount) (*IPv4AddressSection, addrerr.IncompatibleAddressError) { res, err := section.setPrefixLenZeroed(prefixLen) return res.ToIPv4(), err } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address section. // // If this address section has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (section *IPv4AddressSection) AdjustPrefixLen(prefixLen BitCount) *IPv4AddressSection { return section.adjustPrefixLen(prefixLen).ToIPv4() } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address section. // // If this address section has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section *IPv4AddressSection) AdjustPrefixLenZeroed(prefixLen BitCount) (*IPv4AddressSection, addrerr.IncompatibleAddressError) { res, err := section.adjustPrefixLenZeroed(prefixLen) return res.ToIPv4(), err } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this address section. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such address section - it is required that the range of values match the range of a prefix block. // If there is no such address section, then nil is returned. func (section *IPv4AddressSection) AssignPrefixForSingleBlock() *IPv4AddressSection { return section.assignPrefixForSingleBlock().ToIPv4() } // AssignMinPrefixForBlock returns an equivalent address section, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this address section. // // In other words, this method assigns a prefix length to this address section matching the largest prefix block in this address section. func (section *IPv4AddressSection) AssignMinPrefixForBlock() *IPv4AddressSection { return section.assignMinPrefixForBlock().ToIPv4() } // Iterator provides an iterator to iterate through the individual address sections of this address section. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual address sections. // // Call IsMultiple to determine if this instance represents multiple address sections, or GetCount for the count. func (section *IPv4AddressSection) Iterator() Iterator[*IPv4AddressSection] { if section == nil { return ipv4SectionIterator{nilSectIterator()} } return ipv4SectionIterator{section.sectionIterator(nil)} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this address section, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this address section. // // If the series has no prefix length, then this is equivalent to Iterator. func (section *IPv4AddressSection) PrefixIterator() Iterator[*IPv4AddressSection] { return ipv4SectionIterator{section.prefixIterator(false)} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address section. // Each iterated address section will be a prefix block with the same prefix length as this address section. // // If this address section has no prefix length, then this is equivalent to Iterator. func (section *IPv4AddressSection) PrefixBlockIterator() Iterator[*IPv4AddressSection] { return ipv4SectionIterator{section.prefixIterator(true)} } // BlockIterator Iterates through the address sections that can be obtained by iterating through all the upper segments up to the given segment count. // The segments following remain the same in all iterated sections. func (section *IPv4AddressSection) BlockIterator(segmentCount int) Iterator[*IPv4AddressSection] { return ipv4SectionIterator{section.blockIterator(segmentCount)} } // SequentialBlockIterator iterates through the sequential address sections that make up this address section. // // Practically, this means finding the count of segments for which the segments that follow are not full range, and then using BlockIterator with that segment count. // // Use GetSequentialBlockCount to get the number of iterated elements. func (section *IPv4AddressSection) SequentialBlockIterator() Iterator[*IPv4AddressSection] { return ipv4SectionIterator{section.sequentialBlockIterator()} } // ToDivGrouping converts to an AddressDivisionGrouping, a polymorphic type usable with all address sections and division groupings. // Afterwards, you can convert back with ToIPv4. // // ToDivGrouping can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *IPv4AddressSection) ToDivGrouping() *AddressDivisionGrouping { return section.ToSectionBase().ToDivGrouping() } // ToSectionBase converts to an AddressSection, a polymorphic type usable with all address sections. // Afterwards, you can convert back with ToIPv4. // // ToSectionBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *IPv4AddressSection) ToSectionBase() *AddressSection { return section.ToIP().ToSectionBase() } // ToIP converts to an IPAddressSection, a polymorphic type usable with all IP address sections. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *IPv4AddressSection) ToIP() *IPAddressSection { return (*IPAddressSection)(section) } // IncrementBoundary returns the item that is the given increment from the range boundaries of this item. // // If the given increment is positive, adds the value to the highest (GetUpper) in the range to produce a new item. // If the given increment is negative, adds the value to the lowest (GetLower) in the range to produce a new item. // If the increment is zero, returns this. // // If this represents just a single value, this item is simply incremented by the given increment value, positive or negative. // // On overflow or underflow, IncrementBoundary returns nil. func (section *IPv4AddressSection) IncrementBoundary(increment int64) *IPv4AddressSection { return section.incrementBoundary(increment).ToIPv4() } func getIPv4MaxValueLong(segmentCount int) uint64 { return macMaxValues[segmentCount] } // Increment returns the item that is the given increment upwards into the range, // with the increment of 0 returning the first in the range. // // If the increment i matches or exceeds the range count c, then i - c + 1 // is added to the upper item of the range. // An increment matching the count gives you the item just above the highest in the range. // // If the increment is negative, it is added to the lowest of the range. // To get the item just below the lowest of the range, use the increment -1. // // If this represents just a single value, the item is simply incremented by the given increment, positive or negative. // // If this item represents multiple values, a positive increment i is equivalent i + 1 values from the iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the count is equivalent to the same number of iterator values preceding the last value of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On overflow or underflow, Increment returns nil. func (section *IPv4AddressSection) Increment(inc int64) *IPv4AddressSection { if inc == 0 && !section.isMultiple() { return section } lowerValue := uint64(section.Uint32Value()) upperValue := uint64(section.UpperUint32Value()) count := section.GetIPv4Count() isOverflow := checkOverflow(inc, lowerValue, upperValue, count-1, getIPv4MaxValueLong(section.GetSegmentCount())) if isOverflow { return nil } return increment( section.ToSectionBase(), inc, ipv4Network.getIPAddressCreator(), count-1, lowerValue, upperValue, section.getLower, section.getUpper, section.getPrefixLen()).ToIPv4() } // SpanWithPrefixBlocks returns an array of prefix blocks that spans the same set of individual address sections as this section. // // Unlike SpanWithPrefixBlocksTo, the result only includes blocks that are a part of this section. func (section *IPv4AddressSection) SpanWithPrefixBlocks() []*IPv4AddressSection { if section.IsSequential() { if section.IsSinglePrefixBlock() { return []*IPv4AddressSection{section} } wrapped := wrapIPSection(section.ToIP()) spanning := getSpanningPrefixBlocks(wrapped, wrapped) return cloneToIPv4Sections(spanning) } wrapped := wrapIPSection(section.ToIP()) return cloneToIPv4Sections(spanWithPrefixBlocks(wrapped)) } // SpanWithPrefixBlocksTo returns the smallest slice of prefix block subnet sections that span from this section to the given section. // // If the given section has a different segment count, an error is returned. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. func (section *IPv4AddressSection) SpanWithPrefixBlocksTo(other *IPv4AddressSection) ([]*IPv4AddressSection, addrerr.SizeMismatchError) { if err := section.checkSectionCount(other.ToIP()); err != nil { return nil, err } return cloneToIPv4Sections( getSpanningPrefixBlocks( wrapIPSection(section.ToIP()), wrapIPSection(other.ToIP()), ), ), nil } // SpanWithSequentialBlocks produces the smallest slice of sequential blocks that cover the same set of sections as this. // // This slice can be shorter than that produced by SpanWithPrefixBlocks and is never longer. // // Unlike SpanWithSequentialBlocksTo, this method only includes values that are a part of this section. func (section *IPv4AddressSection) SpanWithSequentialBlocks() []*IPv4AddressSection { if section.IsSequential() { return []*IPv4AddressSection{section} } wrapped := wrapIPSection(section.ToIP()) return cloneToIPv4Sections(spanWithSequentialBlocks(wrapped)) } // SpanWithSequentialBlocksTo produces the smallest slice of sequential block address sections that span from this section to the given section. func (section *IPv4AddressSection) SpanWithSequentialBlocksTo(other *IPv4AddressSection) ([]*IPv4AddressSection, addrerr.SizeMismatchError) { if err := section.checkSectionCount(other.ToIP()); err != nil { return nil, err } return cloneToIPv4Sections( getSpanningSequentialBlocks( wrapIPSection(section.ToIP()), wrapIPSection(other.ToIP()), ), ), nil } // CoverWithPrefixBlockTo returns the minimal-size prefix block section that covers all the address sections spanning from this to the given section. // // If the other section has a different segment count, an error is returned. func (section *IPv4AddressSection) CoverWithPrefixBlockTo(other *IPv4AddressSection) (*IPv4AddressSection, addrerr.SizeMismatchError) { res, err := section.coverWithPrefixBlockTo(other.ToIP()) return res.ToIPv4(), err } // CoverWithPrefixBlock returns the minimal-size prefix block that covers all the individual address sections in this section. // The resulting block will have a larger count than this, unless this section is already a prefix block. func (section *IPv4AddressSection) CoverWithPrefixBlock() *IPv4AddressSection { return section.coverWithPrefixBlock().ToIPv4() } func (section *IPv4AddressSection) checkSectionCounts(sections []*IPv4AddressSection) addrerr.SizeMismatchError { segCount := section.GetSegmentCount() length := len(sections) for i := 0; i < length; i++ { section2 := sections[i] if section2 == nil { continue } if section2.GetSegmentCount() != segCount { return &sizeMismatchError{incompatibleAddressError{addressError{key: "ipaddress.error.sizeMismatch"}}} } } return nil } // // MergeToSequentialBlocks merges this with the list of sections to produce the smallest array of sequential blocks. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. func (section *IPv4AddressSection) MergeToSequentialBlocks(sections ...*IPv4AddressSection) ([]*IPv4AddressSection, addrerr.SizeMismatchError) { if err := section.checkSectionCounts(sections); err != nil { return nil, err } series := cloneIPv4Sections(section, sections) blocks := getMergedSequentialBlocks(series) return cloneToIPv4Sections(blocks), nil } // // MergeToPrefixBlocks merges this section with the list of sections to produce the smallest array of prefix blocks. // // The resulting slice is sorted from lowest value to highest, regardless of the size of each prefix block. func (section *IPv4AddressSection) MergeToPrefixBlocks(sections ...*IPv4AddressSection) ([]*IPv4AddressSection, addrerr.SizeMismatchError) { if err := section.checkSectionCounts(sections); err != nil { return nil, err } series := cloneIPv4Sections(section, sections) blocks := getMergedPrefixBlocks(series) return cloneToIPv4Sections(blocks), nil } // ReverseBits returns a new section with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (section *IPv4AddressSection) ReverseBits(perByte bool) (*IPv4AddressSection, addrerr.IncompatibleAddressError) { res, err := section.reverseBits(perByte) return res.ToIPv4(), err } // ReverseBytes returns a new section with the bytes reversed. Any prefix length is dropped. func (section *IPv4AddressSection) ReverseBytes() *IPv4AddressSection { return section.ReverseSegments() } // ReverseSegments returns a new section with the segments reversed. func (section *IPv4AddressSection) ReverseSegments() *IPv4AddressSection { if section.GetSegmentCount() <= 1 { if section.IsPrefixed() { return section.WithoutPrefixLen() } return section } res, _ := section.reverseSegments( func(i int) (*AddressSegment, addrerr.IncompatibleAddressError) { return section.GetSegment(i).WithoutPrefixLen().ToSegmentBase(), nil }, ) return res.ToIPv4() } // Append creates a new section by appending the given section to this section. func (section *IPv4AddressSection) Append(other *IPv4AddressSection) *IPv4AddressSection { count := section.GetSegmentCount() return section.ReplaceLen(count, count, other, 0, other.GetSegmentCount()) } // Insert creates a new section by inserting the given section into this section at the given index. func (section *IPv4AddressSection) Insert(index int, other *IPv4AddressSection) *IPv4AddressSection { return section.insert(index, other.ToIP(), ipv4BitsToSegmentBitshift).ToIPv4() } // Replace replaces the segments of this section starting at the given index with the given replacement segments. func (section *IPv4AddressSection) Replace(index int, replacement *IPv4AddressSection) *IPv4AddressSection { return section.ReplaceLen(index, index+replacement.GetSegmentCount(), replacement, 0, replacement.GetSegmentCount()) } // ReplaceLen replaces segments starting from startIndex and ending before endIndex with the segments starting at replacementStartIndex and // ending before replacementEndIndex from the replacement section. func (section *IPv4AddressSection) ReplaceLen(startIndex, endIndex int, replacement *IPv4AddressSection, replacementStartIndex, replacementEndIndex int) *IPv4AddressSection { return section.replaceLen(startIndex, endIndex, replacement.ToIP(), replacementStartIndex, replacementEndIndex, ipv4BitsToSegmentBitshift).ToIPv4() } // IsAdaptiveZero returns true if the division grouping was originally created as an implicitly zero-valued section or grouping (e.g. IPv4AddressSection{}), // meaning it was not constructed using a constructor function. // Such a grouping, which has no divisions or segments, is convertible to an implicitly zero-valued grouping of any type or version, whether IPv6, IPv4, MAC, or other. // In other words, when a section or grouping is the zero-value, then it is equivalent and convertible to the zero value of any other section or grouping type. func (section *IPv4AddressSection) IsAdaptiveZero() bool { return section != nil && section.matchesZeroGrouping() } var ( ipv4CanonicalParams = new(addrstr.IPv4StringOptionsBuilder).ToOptions() ipv4FullParams = new(addrstr.IPv4StringOptionsBuilder).SetExpandedSegments(true).SetWildcardOptions(wildcardsRangeOnlyNetworkOnly).ToOptions() ipv4NormalizedWildcardParams = new(addrstr.IPv4StringOptionsBuilder).SetWildcardOptions(allWildcards).ToOptions() ipv4SqlWildcardParams = new(addrstr.IPv4StringOptionsBuilder).SetWildcardOptions(allSQLWildcards).ToOptions() inetAtonOctalParams = new(addrstr.IPv4StringOptionsBuilder).SetRadix(Inet_aton_radix_octal.GetRadix()).SetSegmentStrPrefix(Inet_aton_radix_octal.GetSegmentStrPrefix()).ToOptions() inetAtonHexParams = new(addrstr.IPv4StringOptionsBuilder).SetRadix(Inet_aton_radix_hex.GetRadix()).SetSegmentStrPrefix(Inet_aton_radix_hex.GetSegmentStrPrefix()).ToOptions() ipv4ReverseDNSParams = new(addrstr.IPv4StringOptionsBuilder).SetWildcardOptions(allWildcards).SetReverse(true).SetAddressSuffix(IPv4ReverseDnsSuffix).ToOptions() ipv4SegmentedBinaryParams = new(addrstr.IPStringOptionsBuilder).SetRadix(2).SetSeparator(IPv4SegmentSeparator).SetSegmentStrPrefix(BinaryPrefix).ToOptions() ) // ToHexString writes this address section as a single hexadecimal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *IPv4AddressSection) ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toHexString(with0xPrefix) } // ToOctalString writes this address section as a single octal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *IPv4AddressSection) ToOctalString(with0Prefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toOctalString(with0Prefix) } // ToBinaryString writes this address section as a single binary value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0b" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *IPv4AddressSection) ToBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toBinaryString(with0bPrefix) } // ToCanonicalString produces a canonical string for the address section. // // For IPv4, dotted octet format, also known as dotted decimal format, is used. // https://datatracker.ietf.org/doc/html/draft-main-ipaddr-text-rep-00#section-2.1 // // For IPv6, RFC 5952 describes canonical string representation. // https://en.wikipedia.org/wiki/IPv6_address#Representation // http://tools.ietf.org/html/rfc5952 // //If this section has a prefix length, it will be included in the string. func (section *IPv4AddressSection) ToCanonicalString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toNormalizedString(ipv4CanonicalParams) } return cacheStr(&cache.canonicalString, func() string { return section.toNormalizedString(ipv4CanonicalParams) }) } // ToNormalizedString produces a normalized string for the address section. // // For IPv4, it is the same as the canonical string. // // If this section has a prefix length, it will be included in the string. func (section *IPv4AddressSection) ToNormalizedString() string { if section == nil { return nilString() } return section.ToCanonicalString() } // ToCompressedString produces a short representation of this address section while remaining within the confines of standard representation(s) of the address. // // For IPv4, it is the same as the canonical string. func (section *IPv4AddressSection) ToCompressedString() string { if section == nil { return nilString() } return section.ToCanonicalString() } // ToNormalizedWildcardString produces a string similar to the normalized string but avoids the CIDR prefix length. // CIDR addresses will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix notation. func (section *IPv4AddressSection) ToNormalizedWildcardString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toNormalizedString(ipv4NormalizedWildcardParams) } return cacheStr(&cache.normalizedWildcardString, func() string { return section.toNormalizedString(ipv4NormalizedWildcardParams) }) } // ToCanonicalWildcardString produces a string similar to the canonical string but avoids the CIDR prefix length. // Address sections with a network prefix length will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix length notation. // For IPv4 it is the same as ToNormalizedWildcardString. func (section *IPv4AddressSection) ToCanonicalWildcardString() string { if section == nil { return nilString() } return section.ToNormalizedWildcardString() } // ToSegmentedBinaryString writes this address section as segments of binary values preceded by the "0b" prefix. func (section *IPv4AddressSection) ToSegmentedBinaryString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toNormalizedString(ipv4SegmentedBinaryParams) } return cacheStr(&cache.segmentedBinaryString, func() string { return section.toNormalizedString(ipv4SegmentedBinaryParams) }) } // ToSQLWildcardString create a string similar to that from toNormalizedWildcardString except that // it uses SQL wildcards. It uses '%' instead of '*' and also uses the wildcard '_'. func (section *IPv4AddressSection) ToSQLWildcardString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toNormalizedString(ipv4SqlWildcardParams) } return cacheStr(&cache.sqlWildcardString, func() string { return section.toNormalizedString(ipv4SqlWildcardParams) }) } // ToFullString produces a string with no compressed segments and all segments of full length with leading zeros, // which is 3 characters for IPv4 segments. func (section *IPv4AddressSection) ToFullString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toNormalizedString(ipv4FullParams) } return cacheStr(&cache.fullString, func() string { return section.toNormalizedString(ipv4FullParams) }) } // ToReverseDNSString generates the reverse-DNS lookup string. // For IPV4, the error is always nil. // For "8.255.4.4" it is "4.4.255.8.in-addr.arpa". func (section *IPv4AddressSection) ToReverseDNSString() (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } cache := section.getStringCache() if cache == nil { return section.toNormalizedString(ipv4ReverseDNSParams), nil } return cacheStr(&cache.reverseDNSString, func() string { return section.toNormalizedString(ipv4ReverseDNSParams) }), nil } // ToPrefixLenString returns a string with a CIDR network prefix length if this address has a network prefix length. // For IPv4 the string is equivalent to the canonical string. func (section *IPv4AddressSection) ToPrefixLenString() string { if section == nil { return nilString() } return section.ToCanonicalString() } // ToSubnetString produces a string with specific formats for subnets. // The subnet string looks like "1.2.*.*" or "1:2::/16". // // In the case of IPv4, this means that wildcards are used instead of a network prefix when a network prefix has been supplied. func (section *IPv4AddressSection) ToSubnetString() string { if section == nil { return nilString() } return section.ToNormalizedWildcardString() } // ToCompressedWildcardString produces a string similar to ToNormalizedWildcardString, and in fact // for IPv4 it is the same as ToNormalizedWildcardString. func (section *IPv4AddressSection) ToCompressedWildcardString() string { if section == nil { return nilString() } return section.ToNormalizedWildcardString() } // ToInetAtonString returns a string with a format that is styled from the inet_aton routine. // The string can have an octal or hexadecimal radix rather than decimal. // When using octal, the octal segments each have a leading zero prefix of "0", and when using hex, a prefix of "0x". func (section *IPv4AddressSection) ToInetAtonString(radix Inet_aton_radix) string { if section == nil { return nilString() } cache := section.getStringCache() if radix == Inet_aton_radix_octal { if cache == nil { return section.toNormalizedString(inetAtonOctalParams) } return cacheStr(&cache.inetAtonOctalString, func() string { return section.toNormalizedString(inetAtonOctalParams) }) } else if radix == Inet_aton_radix_hex { if cache == nil { return section.toNormalizedString(inetAtonHexParams) } return cacheStr(&cache.inetAtonHexString, func() string { return section.toNormalizedString(inetAtonHexParams) }) } else { return section.ToCanonicalString() } } // ToInetAtonJoinedString returns a string with a format that is styled from the inet_aton routine. // The string can have an octal or hexadecimal radix rather than decimal, // and can have less than the typical four IPv4 segments by joining the least significant segments together, // resulting in a string which just 1, 2 or 3 divisions. // // When using octal, the octal segments each have a leading zero prefix of "0", and when using hex, a prefix of "0x". // // If this represents a subnet section, this returns an error when unable to join two or more segments // into a division of a larger bit-length that represents the same set of values. func (section *IPv4AddressSection) ToInetAtonJoinedString(radix Inet_aton_radix, joinedCount int) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } if joinedCount <= 0 { return section.ToInetAtonString(radix), nil } var stringParams addrstr.IPStringOptions if radix == Inet_aton_radix_octal { stringParams = inetAtonOctalParams } else if radix == Inet_aton_radix_hex { stringParams = inetAtonHexParams } else { stringParams = ipv4CanonicalParams } return section.ToNormalizedJoinedString(stringParams, joinedCount) } // ToNormalizedJoinedString returns a string with a format that is styled from the inet_aton routine. // The string can have less than the typical four IPv4 segments by joining the least significant segments together, // resulting in a string which just 1, 2 or 3 divisions. // // The method accepts an argument of string options as well, allowing callers to customize the string in other ways as well. // // If this represents a subnet section, this returns an error when unable to join two or more segments // into a division of a larger bit-length that represents the same set of values. func (section *IPv4AddressSection) ToNormalizedJoinedString(stringParams addrstr.IPStringOptions, joinedCount int) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } if joinedCount <= 0 || section.GetSegmentCount() <= 1 { return section.toNormalizedString(stringParams), nil } equivalentPart, err := section.ToJoinedSegments(joinedCount) // AddressDivisionSeries if err != nil { return "", err } return toNormalizedIPString(stringParams, equivalentPart), nil } // ToJoinedSegments returns an AddressDivisionSeries which organizes the address section by joining the least significant segments together. // If joined count is not a positive number, or this section has less than 2 segments, then this returns the original receiver section. // Otherwise this returns an AddressDivisionGrouping in which the last division is the division created by joining two or more segments. // // If this represents a subnet section, this returns an error when unable to join address segments, // one of the first with a range of values, into a division of the larger bit-length that represents the same set of values. func (section *IPv4AddressSection) ToJoinedSegments(joinCount int) (AddressDivisionSeries, addrerr.IncompatibleAddressError) { thisCount := section.GetSegmentCount() if joinCount <= 0 || thisCount <= 1 { return section, nil } var totalCount int if joinCount >= thisCount { joinCount = thisCount - 1 totalCount = 1 } else { totalCount = thisCount - joinCount } joinedSegment, err := section.joinSegments(joinCount) //IPv4JoinedSegments if err != nil { return nil, err } notJoinedCount := totalCount - 1 segs := make([]*AddressDivision, totalCount) section.copySubDivisions(0, notJoinedCount, segs) segs[notJoinedCount] = joinedSegment equivalentPart := createInitializedGrouping(segs, section.getPrefixLen()) return equivalentPart, nil } func (section *IPv4AddressSection) joinSegments(joinCount int) (*AddressDivision, addrerr.IncompatibleAddressError) { var lower, upper DivInt var prefix PrefixLen var networkPrefixLength BitCount var firstRange *IPv4AddressSegment firstJoinedIndex := section.GetSegmentCount() - 1 - joinCount bitsPerSeg := section.GetBitsPerSegment() for j := 0; j <= joinCount; j++ { thisSeg := section.GetSegment(firstJoinedIndex + j) if firstRange != nil { if !thisSeg.IsFullRange() { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.invalidMixedRange"}} } } else if thisSeg.isMultiple() { firstRange = thisSeg } lower = (lower << uint(bitsPerSeg)) | DivInt(thisSeg.getSegmentValue()) upper = (upper << uint(bitsPerSeg)) | DivInt(thisSeg.getUpperSegmentValue()) if prefix == nil { thisSegPrefix := thisSeg.getDivisionPrefixLength() if thisSegPrefix != nil { prefix = cacheBitCount(networkPrefixLength + thisSegPrefix.bitCount()) } else { networkPrefixLength += thisSeg.getBitCount() } } } return newRangePrefixDivision(lower, upper, prefix, (BitCount(joinCount)+1)<<3), nil } func (section *IPv4AddressSection) toNormalizedString(stringOptions addrstr.IPStringOptions) string { return toNormalizedIPString(stringOptions, section) } // String implements the [fmt.Stringer] interface, returning the normalized string provided by ToNormalizedString, or "" if the receiver is a nil pointer. func (section *IPv4AddressSection) String() string { if section == nil { return nilString() } return section.toString() } // GetSegmentStrings returns a slice with the string for each segment being the string that is normalized with wildcards. func (section *IPv4AddressSection) GetSegmentStrings() []string { if section == nil { return nil } return section.getSegmentStrings() } // Inet_aton_radix represents a radix for printing an address string. type Inet_aton_radix int // GetRadix converts the radix to an int. func (rad Inet_aton_radix) GetRadix() int { return int(rad) } // GetSegmentStrPrefix returns the string prefix used to identify the radix. func (rad Inet_aton_radix) GetSegmentStrPrefix() string { if rad == Inet_aton_radix_octal { return OctalPrefix } else if rad == Inet_aton_radix_hex { return HexPrefix } return "" } // String returns the name of the radix. func (rad Inet_aton_radix) String() string { if rad == Inet_aton_radix_octal { return "octal" } else if rad == Inet_aton_radix_hex { return "hexadecimal" } return "decimal" } const ( Inet_aton_radix_octal Inet_aton_radix = 8 Inet_aton_radix_hex Inet_aton_radix = 16 Inet_aton_radix_decimal Inet_aton_radix = 10 ) ipaddress-go-1.5.4/ipaddr/ipv4segment.go000066400000000000000000000762351440250641600201600ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math/big" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" ) type IPv4SegInt = uint8 type IPv4SegmentValueProvider func(segmentIndex int) IPv4SegInt // WrapIPv4SegmentValueProvider converts the given IPv4SegmentValueProvider to a SegmentValueProvider. func WrapIPv4SegmentValueProvider(f IPv4SegmentValueProvider) SegmentValueProvider { if f == nil { return nil } return func(segmentIndex int) SegInt { return SegInt(f(segmentIndex)) } } // WrapSegmentValueProviderForIPv4 converts the given SegmentValueProvider to an IPv4SegmentValueProvider. // Values that do not fit IPv4SegInt are truncated. func WrapSegmentValueProviderForIPv4(f SegmentValueProvider) IPv4SegmentValueProvider { if f == nil { return nil } return func(segmentIndex int) IPv4SegInt { return IPv4SegInt(f(segmentIndex)) } } const useIPv4SegmentCache = true type ipv4SegmentValues struct { value IPv4SegInt upperValue IPv4SegInt prefLen PrefixLen cache divCache } func (seg *ipv4SegmentValues) getAddrType() addrType { return ipv4Type } func (seg *ipv4SegmentValues) includesZero() bool { return seg.value == 0 } func (seg *ipv4SegmentValues) includesMax() bool { return seg.upperValue == 0xff } func (seg *ipv4SegmentValues) isMultiple() bool { return seg.value != seg.upperValue } func (seg *ipv4SegmentValues) getCount() *big.Int { return big.NewInt(int64(seg.upperValue-seg.value) + 1) } func (seg *ipv4SegmentValues) getBitCount() BitCount { return IPv4BitsPerSegment } func (seg *ipv4SegmentValues) getByteCount() int { return IPv4BytesPerSegment } func (seg *ipv4SegmentValues) getValue() *BigDivInt { return big.NewInt(int64(seg.value)) } func (seg *ipv4SegmentValues) getUpperValue() *BigDivInt { return big.NewInt(int64(seg.upperValue)) } func (seg *ipv4SegmentValues) getDivisionValue() DivInt { return DivInt(seg.value) } func (seg *ipv4SegmentValues) getUpperDivisionValue() DivInt { return DivInt(seg.upperValue) } func (seg *ipv4SegmentValues) getDivisionPrefixLength() PrefixLen { return seg.prefLen } func (seg *ipv4SegmentValues) deriveNew(val, upperVal DivInt, prefLen PrefixLen) divisionValues { return newIPv4SegmentPrefixedValues(IPv4SegInt(val), IPv4SegInt(upperVal), prefLen) } func (seg *ipv4SegmentValues) derivePrefixed(prefLen PrefixLen) divisionValues { return newIPv4SegmentPrefixedValues(seg.value, seg.upperValue, prefLen) } func (seg *ipv4SegmentValues) deriveNewSeg(val SegInt, prefLen PrefixLen) divisionValues { return newIPv4SegmentPrefixedVal(IPv4SegInt(val), prefLen) } func (seg *ipv4SegmentValues) deriveNewMultiSeg(val, upperVal SegInt, prefLen PrefixLen) divisionValues { return newIPv4SegmentPrefixedValues(IPv4SegInt(val), IPv4SegInt(upperVal), prefLen) } func (seg *ipv4SegmentValues) getCache() *divCache { return &seg.cache } func (seg *ipv4SegmentValues) getSegmentValue() SegInt { return SegInt(seg.value) } func (seg *ipv4SegmentValues) getUpperSegmentValue() SegInt { return SegInt(seg.upperValue) } func (seg *ipv4SegmentValues) calcBytesInternal() (bytes, upperBytes []byte) { bytes = []byte{byte(seg.value)} if seg.isMultiple() { upperBytes = []byte{byte(seg.upperValue)} } else { upperBytes = bytes } return } func (seg *ipv4SegmentValues) bytesInternal(upper bool) []byte { if upper { return []byte{byte(seg.upperValue)} } return []byte{byte(seg.value)} } var _ divisionValues = &ipv4SegmentValues{} var zeroIPv4Seg = NewIPv4Segment(0) var zeroIPv4SegZeroPrefix = NewIPv4PrefixedSegment(0, cacheBitCount(0)) var zeroIPv4SegPrefixBlock = NewIPv4RangePrefixedSegment(0, IPv4MaxValuePerSegment, cacheBitCount(0)) // IPv4AddressSegment represents a segment of an IPv4 address. // An IPv4 segment contains a single value or a range of sequential values, a prefix length, and it has bit length of 8 bits. // // Like strings, segments are immutable, which also makes them concurrency-safe. // // See AddressSegment for more details regarding segments. type IPv4AddressSegment struct { ipAddressSegmentInternal } func (seg *IPv4AddressSegment) init() *IPv4AddressSegment { if seg.divisionValues == nil { return zeroIPv4Seg } return seg } // GetIPv4SegmentValue returns the lower value. Same as GetSegmentValue but returned as a IPv4SegInt. func (seg *IPv4AddressSegment) GetIPv4SegmentValue() IPv4SegInt { return IPv4SegInt(seg.GetSegmentValue()) } // GetIPv4UpperSegmentValue returns the lower value. Same as GetUpperSegmentValue but returned as a IPv4SegInt. func (seg *IPv4AddressSegment) GetIPv4UpperSegmentValue() IPv4SegInt { return IPv4SegInt(seg.GetUpperSegmentValue()) } // Contains returns whether this is same type and version as the given segment and whether it contains all values in the given segment. func (seg *IPv4AddressSegment) Contains(other AddressSegmentType) bool { if seg == nil { return other == nil || other.ToSegmentBase() == nil } return seg.init().contains(other) } // Equal returns whether the given segment is equal to this segment. // Two segments are equal if they match: // - type/version: IPv4 // - value range // Prefix lengths are ignored. func (seg *IPv4AddressSegment) Equal(other AddressSegmentType) bool { if seg == nil { return other == nil || other.ToDiv() == nil } return seg.init().equal(other) } // Compare returns a negative integer, zero, or a positive integer if this address segment is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (seg *IPv4AddressSegment) Compare(item AddressItem) int { if seg != nil { seg = seg.init() } return CountComparator.Compare(seg, item) } // CompareSize compares the counts of two segments, the number of individual values within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether one represents more individual values than another. // // CompareSize returns a positive integer if this segment has a larger count than the one given, zero if they are the same, or a negative integer if the other has a larger count. func (seg *IPv4AddressSegment) CompareSize(other AddressItem) int { if seg == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return seg.init().compareSize(other) } // PrefixContains returns whether the prefix values in the prefix of the given segment are also prefix values in this segment. // It returns whether the prefix of this segment contains the prefix of the given segment. func (seg *IPv4AddressSegment) PrefixContains(other AddressSegmentType, prefixLength BitCount) bool { return seg.init().ipAddressSegmentInternal.PrefixContains(other, prefixLength) } // PrefixEqual returns whether the prefix bits of this segment match the same bits of the given segment. // It returns whether the two segments share the same range of prefix values using the given prefix length. func (seg *IPv4AddressSegment) PrefixEqual(other AddressSegmentType, prefixLength BitCount) bool { return seg.init().ipAddressSegmentInternal.PrefixEqual(other, prefixLength) } // GetBitCount returns the number of bits in each value comprising this address item, which is 8. func (seg *IPv4AddressSegment) GetBitCount() BitCount { return IPv4BitsPerSegment } // GetByteCount returns the number of bytes required for each value comprising this address item, which is 1. func (seg *IPv4AddressSegment) GetByteCount() int { return IPv4BytesPerSegment } // GetMaxValue gets the maximum possible value for this type or version of segment, determined by the number of bits. // // For the highest range value of this particular segment, use GetUpperSegmentValue. func (seg *IPv4AddressSegment) GetMaxValue() IPv4SegInt { return 0xff } // GetLower returns a segment representing just the lowest value in the range, which will be the same segment if it represents a single value. func (seg *IPv4AddressSegment) GetLower() *IPv4AddressSegment { return seg.init().getLower().ToIPv4() } // GetUpper returns a segment representing just the highest value in the range, which will be the same segment if it represents a single value. func (seg *IPv4AddressSegment) GetUpper() *IPv4AddressSegment { return seg.init().getUpper().ToIPv4() } // IsMultiple returns whether this segment represents multiple values. func (seg *IPv4AddressSegment) IsMultiple() bool { return seg != nil && seg.isMultiple() } // GetCount returns the count of possible distinct values for this item. // If not representing multiple values, the count is 1. // // For instance, a segment with the value range of 3-7 has count 5. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (seg *IPv4AddressSegment) GetCount() *big.Int { if seg == nil { return bigZero() } return seg.getCount() } // GetPrefixCountLen returns the count of the number of distinct prefix values for the given prefix length in the range of values of this segment. func (seg *IPv4AddressSegment) GetPrefixCountLen(segmentPrefixLength BitCount) *big.Int { return seg.init().ipAddressSegmentInternal.GetPrefixCountLen(segmentPrefixLength) } // GetPrefixValueCountLen returns the same value as GetPrefixCountLen as an integer. func (seg *IPv4AddressSegment) GetPrefixValueCountLen(segmentPrefixLength BitCount) SegIntCount { return seg.init().ipAddressSegmentInternal.GetPrefixValueCountLen(segmentPrefixLength) } // IsOneBit returns true if the bit in the lower value of this segment at the given index is 1, where index 0 is the most significant bit. func (seg *IPv4AddressSegment) IsOneBit(segmentBitIndex BitCount) bool { return seg.init().ipAddressSegmentInternal.IsOneBit(segmentBitIndex) } // Bytes returns the lowest value in the address segment range as a byte slice. func (seg *IPv4AddressSegment) Bytes() []byte { return seg.init().ipAddressSegmentInternal.Bytes() } // UpperBytes returns the highest value in the address segment range as a byte slice. func (seg *IPv4AddressSegment) UpperBytes() []byte { return seg.init().ipAddressSegmentInternal.UpperBytes() } // CopyBytes copies the lowest value in the address segment range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (seg *IPv4AddressSegment) CopyBytes(bytes []byte) []byte { return seg.init().ipAddressSegmentInternal.CopyBytes(bytes) } // CopyUpperBytes copies the highest value in the address segment range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (seg *IPv4AddressSegment) CopyUpperBytes(bytes []byte) []byte { return seg.init().ipAddressSegmentInternal.CopyUpperBytes(bytes) } // GetPrefixValueCount returns the count of prefixes in this segment for its prefix length, or the total count if it has no prefix length. func (seg *IPv4AddressSegment) GetPrefixValueCount() SegIntCount { return seg.init().ipAddressSegmentInternal.GetPrefixValueCount() } // MatchesWithPrefixMask applies the network mask of the given bit-length to this segment and then compares the result with the given value masked by the same mask, //returning true if the resulting range matches the given single value. func (seg *IPv4AddressSegment) MatchesWithPrefixMask(value IPv4SegInt, networkBits BitCount) bool { return seg.init().ipAddressSegmentInternal.MatchesWithPrefixMask(SegInt(value), networkBits) } // GetBlockMaskPrefixLen returns the prefix length if this address segment is equivalent to the mask for a CIDR prefix block. // Otherwise, it returns nil. // A CIDR network mask is a segment with all ones in the network bits and then all zeros in the host bits. // A CIDR host mask is a segment with all zeros in the network bits and then all ones in the host bits. // The prefix length is the bit-length of the network bits. // // Also, keep in mind that the prefix length returned by this method is not equivalent to the prefix length of this segment. // The prefix length returned here indicates the whether the value of this segment can be used as a mask for the network and host // bits of any other segment. Therefore, the two values can be different values, or one can be nil while the other is not. // // This method applies only to the lower value of the range if this segment represents multiple values. func (seg *IPv4AddressSegment) GetBlockMaskPrefixLen(network bool) PrefixLen { return seg.init().ipAddressSegmentInternal.GetBlockMaskPrefixLen(network) } // GetTrailingBitCount returns the number of consecutive trailing one or zero bits. // If ones is true, returns the number of consecutive trailing zero bits. // Otherwise, returns the number of consecutive trailing one bits. // // This method applies only to the lower value of the range if this segment represents multiple values. func (seg *IPv4AddressSegment) GetTrailingBitCount(ones bool) BitCount { return seg.init().ipAddressSegmentInternal.GetTrailingBitCount(ones) } // GetLeadingBitCount returns the number of consecutive leading one or zero bits. // If ones is true, returns the number of consecutive leading one bits. // Otherwise, returns the number of consecutive leading zero bits. // // This method applies only to the lower value of the range if this segment represents multiple values. func (seg *IPv4AddressSegment) GetLeadingBitCount(ones bool) BitCount { return seg.init().ipAddressSegmentInternal.GetLeadingBitCount(ones) } // ToPrefixedNetworkSegment returns a segment with the network bits matching this segment but the host bits converted to zero. // The new segment will be assigned the given prefix length. func (seg *IPv4AddressSegment) ToPrefixedNetworkSegment(segmentPrefixLength PrefixLen) *IPv4AddressSegment { return seg.init().toPrefixedNetworkDivision(segmentPrefixLength).ToIPv4() } // ToNetworkSegment returns a segment with the network bits matching this segment but the host bits converted to zero. // The new segment will have no assigned prefix length. func (seg *IPv4AddressSegment) ToNetworkSegment(segmentPrefixLength PrefixLen) *IPv4AddressSegment { return seg.init().toNetworkDivision(segmentPrefixLength, false).ToIPv4() } // ToPrefixedHostSegment returns a segment with the host bits matching this segment but the network bits converted to zero. // The new segment will be assigned the given prefix length. func (seg *IPv4AddressSegment) ToPrefixedHostSegment(segmentPrefixLength PrefixLen) *IPv4AddressSegment { return seg.init().toPrefixedHostDivision(segmentPrefixLength).ToIPv4() } // ToHostSegment returns a segment with the host bits matching this segment but the network bits converted to zero. // The new segment will have no assigned prefix length. func (seg *IPv4AddressSegment) ToHostSegment(segmentPrefixLength PrefixLen) *IPv4AddressSegment { return seg.init().toHostDivision(segmentPrefixLength, false).ToIPv4() } // Iterator provides an iterator to iterate through the individual address segments of this address segment. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual address segments. // // Call IsMultiple to determine if this instance represents multiple address segments, or GetValueCount for the count. func (seg *IPv4AddressSegment) Iterator() Iterator[*IPv4AddressSegment] { if seg == nil { return ipv4SegmentIterator{nilSegIterator()} } return ipv4SegmentIterator{seg.init().iterator()} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address segment. // Each iterated address segment will be a prefix block with the same prefix length as this address segment. // // If this address segment has no prefix length, then this is equivalent to Iterator. func (seg *IPv4AddressSegment) PrefixBlockIterator() Iterator[*IPv4AddressSegment] { return ipv4SegmentIterator{seg.init().prefixBlockIterator()} } // PrefixedBlockIterator provides an iterator to iterate through the individual prefix blocks of the given prefix length in this segment, // one for each prefix of this address or subnet. // // It is similar to PrefixBlockIterator except that this method allows you to specify the prefix length. func (seg *IPv4AddressSegment) PrefixedBlockIterator(segmentPrefixLen BitCount) Iterator[*IPv4AddressSegment] { return ipv4SegmentIterator{seg.init().prefixedBlockIterator(segmentPrefixLen)} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this segment, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this segment. // // If this address segment has no prefix length, then this is equivalent to Iterator. func (seg *IPv4AddressSegment) PrefixIterator() Iterator[*IPv4AddressSegment] { return ipv4SegmentIterator{seg.init().prefixIterator()} } // IsPrefixed returns whether this segment has an associated prefix length. func (seg *IPv4AddressSegment) IsPrefixed() bool { return seg != nil && seg.isPrefixed() } // WithoutPrefixLen returns a segment with the same value range but without a prefix length. func (seg *IPv4AddressSegment) WithoutPrefixLen() *IPv4AddressSegment { if !seg.IsPrefixed() { return seg } return seg.withoutPrefixLen().ToIPv4() } // ReverseBits returns a segment with the bits reversed. // // If this segment represents a range of values that cannot be reversed, then this returns an error. // // To be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // Otherwise the result is not contiguous and thus cannot be represented by a sequential range of values. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (seg *IPv4AddressSegment) ReverseBits(_ bool) (res *IPv4AddressSegment, err addrerr.IncompatibleAddressError) { if seg.divisionValues == nil { res = seg return } if seg.isMultiple() { if isReversible := seg.isReversibleRange(false); isReversible { res = seg.WithoutPrefixLen() return } err = &incompatibleAddressError{addressError{key: "ipaddress.error.reverseRange"}} return } oldVal := IPv4SegInt(seg.GetSegmentValue()) val := IPv4SegInt(reverseUint8(uint8(oldVal))) if oldVal == val && !seg.isPrefixed() { res = seg } else { res = NewIPv4Segment(val) } return } // ReverseBytes returns a segment with the bytes reversed, which for an IPv4 segment is always the original segment. func (seg *IPv4AddressSegment) ReverseBytes() (*IPv4AddressSegment, addrerr.IncompatibleAddressError) { return seg, nil } func (seg *IPv4AddressSegment) isJoinableTo(low *IPv4AddressSegment) bool { // if the high segment has a range, the low segment must match the full range, // otherwise it is not possible to create an equivalent range when joining return !seg.isMultiple() || low.IsFullRange() } // Join joins this segment with another IPv4 segment to produce an IPv6 segment. func (seg *IPv4AddressSegment) Join(low *IPv4AddressSegment) (*IPv6AddressSegment, addrerr.IncompatibleAddressError) { prefixLength := seg.getJoinedSegmentPrefixLen(low.GetSegmentPrefixLen()) if !seg.isJoinableTo(low) { return nil, &incompatibleAddressError{addressError: addressError{key: "ipaddress.error.invalidMixedRange"}} } return NewIPv6RangePrefixedSegment( IPv6SegInt((seg.GetSegmentValue()<<8)|low.getSegmentValue()), IPv6SegInt((seg.GetUpperSegmentValue()<<8)|low.getUpperSegmentValue()), prefixLength), nil } func (seg *IPv4AddressSegment) getJoinedSegmentPrefixLen(lowBits PrefixLen) PrefixLen { highBits := seg.GetSegmentPrefixLen() if lowBits == nil { return nil } lowBitCount := lowBits.bitCount() if lowBitCount == 0 { return highBits } return cacheBitCount(lowBitCount + IPv4BitsPerSegment) } // ToDiv converts to an AddressDivision, a polymorphic type usable with all address segments and divisions. // Afterwards, you can convert back with ToIPv4. // // ToDiv can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *IPv4AddressSegment) ToDiv() *AddressDivision { return seg.ToIP().ToDiv() } // ToSegmentBase converts to an AddressSegment, a polymorphic type usable with all address segments. // Afterwards, you can convert back with ToIPv4. // // ToSegmentBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *IPv4AddressSegment) ToSegmentBase() *AddressSegment { return seg.ToIP().ToSegmentBase() } // ToIP converts to an IPAddressSegment, a polymorphic type usable with all IP address segments. // Afterwards, you can convert back with ToIPv4. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *IPv4AddressSegment) ToIP() *IPAddressSegment { if seg == nil { return nil } return (*IPAddressSegment)(seg.init()) } // GetString produces a normalized string to represent the segment. // If the segment is a CIDR network prefix block for its prefix length, then the string contains only the lower value of the block range. // Otherwise, the explicit range will be printed. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (seg *IPv4AddressSegment) GetString() string { if seg == nil { return nilString() } return seg.init().getString() } // GetWildcardString produces a normalized string to represent the segment, favouring wildcards and range characters while ignoring any network prefix length. // The explicit range of a range-valued segment will be printed. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and the bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (seg *IPv4AddressSegment) GetWildcardString() string { if seg == nil { return nilString() } return seg.init().getWildcardString() } // Format implements [fmt.Formatter] interface. It accepts the formats // - 'v' for the default address and section format (either the normalized or canonical string), // - 's' (string) for the same, // - 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix), // - 'd' (decimal), 'x' (lowercase hexadecimal), and // - 'X' (uppercase hexadecimal). // Also supported are some of fmt's format flags for integral types. // Sign control is not supported since addresses and sections are never negative. // '#' for an alternate format is supported, which adds a leading zero for octal, and for hexadecimal it adds // a leading "0x" or "0X" for "%#x" and "%#X" respectively. // Also supported is specification of minimum digits precision, output field width, // space or zero padding, and '-' for left or right justification. func (seg IPv4AddressSegment) Format(state fmt.State, verb rune) { seg.init().ipAddressSegmentInternal.Format(state, verb) } // String produces a string that is useful when a segment string is provided with no context. It uses the decimal radix. // GetWildcardString is more appropriate in context with other segments or divisions. It does not use a string prefix and uses '*' for full-range segments. // GetString is more appropriate in context with prefix lengths, it uses zeros instead of wildcards with full prefix block ranges alongside prefix lengths. func (seg *IPv4AddressSegment) String() string { if seg == nil { return nilString() } return seg.init().toString() } // NewIPv4Segment constructs a segment of an IPv4 address with the given value. func NewIPv4Segment(val IPv4SegInt) *IPv4AddressSegment { return newIPv4Segment(newIPv4SegmentVal(val)) } // NewIPv4RangeSegment constructs a segment of an IPv4 subnet with the given range of sequential values. func NewIPv4RangeSegment(val, upperVal IPv4SegInt) *IPv4AddressSegment { return newIPv4Segment(newIPv4SegmentPrefixedValues(val, upperVal, nil)) } // NewIPv4PrefixedSegment constructs a segment of an IPv4 address with the given value and assigned prefix length. func NewIPv4PrefixedSegment(val IPv4SegInt, prefixLen PrefixLen) *IPv4AddressSegment { return newIPv4Segment(newIPv4SegmentPrefixedVal(val, prefixLen)) } // NewIPv4RangePrefixedSegment constructs a segment of an IPv4 subnet with the given range of sequential values and assigned prefix length. func NewIPv4RangePrefixedSegment(val, upperVal IPv4SegInt, prefixLen PrefixLen) *IPv4AddressSegment { return newIPv4Segment(newIPv4SegmentPrefixedValues(val, upperVal, prefixLen)) } func newIPv4Segment(vals *ipv4SegmentValues) *IPv4AddressSegment { return &IPv4AddressSegment{ ipAddressSegmentInternal{ addressSegmentInternal{ addressDivisionInternal{ addressDivisionBase{ vals, }, }, }, }, } } type ipv4DivsBlock struct { block []ipv4SegmentValues } var ( allRangeValsIPv4 = &ipv4SegmentValues{ upperValue: IPv4MaxValuePerSegment, cache: divCache{ isSinglePrefBlock: &falseVal, }, } allPrefixedCacheIPv4 = makePrefixCache() segmentCacheIPv4 = makeSegmentCache() segmentPrefixCacheIPv4 = makeDivsBlock() prefixBlocksCacheIPv4 = makeDivsBlock() ) func makeDivsBlock() []*ipv4DivsBlock { if useIPv4SegmentCache { return make([]*ipv4DivsBlock, IPv4BitsPerSegment+1) } return nil } func makePrefixCache() (allPrefixedCacheIPv4 []ipv4SegmentValues) { if useIPv4SegmentCache { allPrefixedCacheIPv4 = make([]ipv4SegmentValues, IPv4BitsPerSegment+1) for i := range allPrefixedCacheIPv4 { vals := &allPrefixedCacheIPv4[i] vals.upperValue = IPv4MaxValuePerSegment vals.prefLen = cacheBitCount(i) vals.cache.isSinglePrefBlock = &falseVal } allPrefixedCacheIPv4[0].cache.isSinglePrefBlock = &trueVal } return } func makeSegmentCache() (segmentCacheIPv4 []ipv4SegmentValues) { if useIPv4SegmentCache { segmentCacheIPv4 = make([]ipv4SegmentValues, IPv4MaxValuePerSegment+1) for i := range segmentCacheIPv4 { vals := &segmentCacheIPv4[i] segi := IPv4SegInt(i) vals.value = segi vals.upperValue = segi vals.cache.isSinglePrefBlock = &falseVal } } return } func newIPv4SegmentVal(value IPv4SegInt) *ipv4SegmentValues { if useIPv4SegmentCache { result := &segmentCacheIPv4[value] return result } return &ipv4SegmentValues{ value: value, upperValue: value, cache: divCache{ isSinglePrefBlock: &falseVal, }, } } func newIPv4SegmentPrefixedVal(value IPv4SegInt, prefLen PrefixLen) (result *ipv4SegmentValues) { if prefLen == nil { return newIPv4SegmentVal(value) } segmentPrefixLength := prefLen.bitCount() if segmentPrefixLength < 0 { segmentPrefixLength = 0 } else if segmentPrefixLength > IPv4BitsPerSegment { segmentPrefixLength = IPv4BitsPerSegment } prefLen = cacheBitCount(segmentPrefixLength) // this ensures we use the prefix length cache for all segments if useIPv4SegmentCache { prefixIndex := segmentPrefixLength cache := segmentPrefixCacheIPv4 block := (*ipv4DivsBlock)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache[prefixIndex])))) if block == nil { block = &ipv4DivsBlock{make([]ipv4SegmentValues, IPv4MaxValuePerSegment+1)} vals := block.block var isSinglePrefBlock *bool if prefixIndex == IPv4BitsPerSegment { isSinglePrefBlock = &trueVal } else { isSinglePrefBlock = &falseVal } for i := range vals { value := &vals[i] segi := IPv4SegInt(i) value.value = segi value.upperValue = segi value.prefLen = prefLen value.cache.isSinglePrefBlock = isSinglePrefBlock } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache[prefixIndex])) atomicStorePointer(dataLoc, unsafe.Pointer(block)) } result = &block.block[value] return result } var isSinglePrefBlock *bool if segmentPrefixLength == IPv4BitsPerSegment { isSinglePrefBlock = &trueVal } else { isSinglePrefBlock = &falseVal } return &ipv4SegmentValues{ value: value, upperValue: value, prefLen: prefLen, cache: divCache{ isSinglePrefBlock: isSinglePrefBlock, }, } } func newIPv4SegmentPrefixedValues(value, upperValue IPv4SegInt, prefLen PrefixLen) *ipv4SegmentValues { var isSinglePrefBlock *bool if prefLen == nil { if value == upperValue { return newIPv4SegmentVal(value) } else if value > upperValue { value, upperValue = upperValue, value } if useIPv4SegmentCache && value == 0 && upperValue == IPv4MaxValuePerSegment { return allRangeValsIPv4 } isSinglePrefBlock = &falseVal } else { if value == upperValue { return newIPv4SegmentPrefixedVal(value, prefLen) } else if value > upperValue { value, upperValue = upperValue, value } segmentPrefixLength := prefLen.bitCount() if segmentPrefixLength < 0 { segmentPrefixLength = 0 } else if segmentPrefixLength > IPv4BitsPerSegment { segmentPrefixLength = IPv4BitsPerSegment } prefLen = cacheBitCount(segmentPrefixLength) // this ensures we use the prefix length cache for all segments if useIPv4SegmentCache { // cache is the prefix block for any prefix length shiftBits := uint(IPv4BitsPerSegment - segmentPrefixLength) nmask := ^IPv4SegInt(0) << shiftBits prefixBlockLower := value & nmask hmask := ^nmask prefixBlockUpper := value | hmask if value == prefixBlockLower && upperValue == prefixBlockUpper { valueIndex := value >> shiftBits cache := prefixBlocksCacheIPv4 prefixIndex := segmentPrefixLength block := (*ipv4DivsBlock)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache[prefixIndex])))) var result *ipv4SegmentValues if block == nil { block = &ipv4DivsBlock{make([]ipv4SegmentValues, 1< 0 { zoneStr = string(zone) } prefixSection := prefix.GetSection() return newIPv6AddressFromMAC(prefixSection, suffix.GetSection(), zoneStr) } // when this is called, we know the sections are sufficient length func newIPv6AddressFromMAC(prefixSection *IPv6AddressSection, suffix *MACAddressSection, zone string) (*IPv6Address, addrerr.IncompatibleAddressError) { prefixLen := prefixSection.getPrefixLen() if prefixLen != nil && prefixLen.bitCount() > getNetworkPrefixLen(IPv6BitsPerSegment, 0, 4).bitCount() { prefixLen = nil } segments := createSegmentArray(8) if err := toIPv6SegmentsFromEUI(segments, 4, suffix, prefixLen); err != nil { return nil, err } prefixSection.copySubDivisions(0, 4, segments) res := createIPv6Section(segments) res.prefixLength = prefixLen res.isMult = suffix.isMultiple() || prefixSection.isMultipleTo(4) return newIPv6AddressZoned(res, zone), nil } // NewIPv6AddressFromMACSection constructs an IPv6 address from a modified EUI-64 (Extended Unique Identifier) MAC address section and an IPv6 address section network prefix. // // If the supplied MAC address section is an 8-byte EUI-64, then it must match the required EUI-64 format of "xx-xx-ff-fe-xx-xx" // with the "ff-fe" section in the middle. // // If the supplied MAC address section is a 6-byte MAC-48 or EUI-48, then the "ff-fe" pattern will be inserted when converting to IPv6. // // The constructor will toggle the MAC U/L (universal/local) bit as required with EUI-64. // // The IPv6 address section must be at least 8 bytes (4 segments) in length. // // Any prefix length in the MAC address is ignored, while a prefix length in the IPv6 address is preserved but only up to the first 4 segments. // // The error is either an AddressValueError for sections that are of insufficient segment count, // or IncompatibleAddressError when unable to join two MAC segments, at least one with ranged values, into an equivalent IPV6 segment range. func NewIPv6AddressFromMACSection(prefix *IPv6AddressSection, suffix *MACAddressSection) (*IPv6Address, addrerr.AddressError) { return newIPv6AddressFromZonedMAC(prefix, suffix, NoZone) } // NewIPv6AddressFromZonedMACSection constructs an IPv6 address from a modified EUI-64 (Extended Unique Identifier) MAC address section, an IPv6 address section network prefix, and a zone. // // It is similar to NewIPv6AddressFromMACSection but also allows you to specify a zone. // // It is similar to NewIPv6AddressFromMAC, which can supply a zone with the IPv6Address argument. func NewIPv6AddressFromZonedMACSection(prefix *IPv6AddressSection, suffix *MACAddressSection, zone string) (*IPv6Address, addrerr.AddressError) { return newIPv6AddressFromZonedMAC(prefix, suffix, zone) } func newIPv6AddressFromZonedMAC(prefix *IPv6AddressSection, suffix *MACAddressSection, zone string) (*IPv6Address, addrerr.AddressError) { suffixSegCount := suffix.GetSegmentCount() if prefix.GetSegmentCount() < 4 || (suffixSegCount != ExtendedUniqueIdentifier48SegmentCount && suffixSegCount != ExtendedUniqueIdentifier64SegmentCount) { return nil, &addressValueError{addressError: addressError{key: "ipaddress.mac.error.not.eui.convertible"}} } return newIPv6AddressFromMAC(prefix, suffix, zone) } var zeroIPv6 = initZeroIPv6() var ipv6All = zeroIPv6.ToPrefixBlockLen(0) func initZeroIPv6() *IPv6Address { div := zeroIPv6Seg segs := []*IPv6AddressSegment{div, div, div, div, div, div, div, div} section := NewIPv6Section(segs) return newIPv6Address(section) } // // // IPv6Address is an IPv6 address, or a subnet of multiple IPv6 addresses. // An IPv6 address is composed of 8 2-byte segments and can optionally have an associated prefix length. // Each segment can represent a single value or a range of values. // The zero value is "::". // // To construct one from a string, use NewIPAddressString, then use the ToAddress or GetAddress method of [IPAddressString], // and then use ToIPv6 to get an IPv6Address, assuming the string had an IPv6 format. // // For other inputs, use one of the multiple constructor functions like NewIPv6Address. // You can also use one of the multiple constructors for [IPAddress] like NewIPAddress and then convert using ToIPv6. type IPv6Address struct { ipAddressInternal } func (addr *IPv6Address) init() *IPv6Address { if addr.section == nil { return zeroIPv6 } return addr } // GetCount returns the count of addresses that this address or subnet represents. // // If just a single address, not a subnet of multiple addresses, returns 1. // // For instance, the IP address subnet "2001:db8::/64" has the count of 2 to the power of 64. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (addr *IPv6Address) GetCount() *big.Int { if addr == nil { return bigZero() } return addr.getCount() } // IsMultiple returns true if this represents more than a single individual address, whether it is a subnet of multiple addresses. func (addr *IPv6Address) IsMultiple() bool { return addr != nil && addr.isMultiple() } // IsPrefixed returns whether this address has an associated prefix length. func (addr *IPv6Address) IsPrefixed() bool { return addr != nil && addr.isPrefixed() } // IsFullRange returns whether this address covers the entire IPv6 address space. // // This is true if and only if both IncludesZero and IncludesMax return true. func (addr *IPv6Address) IsFullRange() bool { return addr.GetSection().IsFullRange() } // GetBitCount returns the number of bits comprising this address, // or each address in the range if a subnet, which is 128. func (addr *IPv6Address) GetBitCount() BitCount { return IPv6BitCount } // GetByteCount returns the number of bytes required for this address, // or each address in the range if a subnet, which is 16. func (addr *IPv6Address) GetByteCount() int { return IPv6ByteCount } // GetBitsPerSegment returns the number of bits comprising each segment in this address. Segments in the same address are equal length. func (addr *IPv6Address) GetBitsPerSegment() BitCount { return IPv6BitsPerSegment } // GetBytesPerSegment returns the number of bytes comprising each segment in this address or subnet. Segments in the same address are equal length. func (addr *IPv6Address) GetBytesPerSegment() int { return IPv6BytesPerSegment } // HasZone returns whether this IPv6 address includes a zone or scope. func (addr *IPv6Address) HasZone() bool { return addr != nil && addr.zone != NoZone } // GetZone returns the zone it it has one, otherwise it returns NoZone, which is an empty string. func (addr *IPv6Address) GetZone() Zone { if addr == nil { return NoZone } return addr.zone } // GetSection returns the backing section for this address or subnet, comprising all segments. func (addr *IPv6Address) GetSection() *IPv6AddressSection { return addr.init().section.ToIPv6() } // GetTrailingSection gets the subsection from the series starting from the given index. // The first segment is at index 0. func (addr *IPv6Address) GetTrailingSection(index int) *IPv6AddressSection { return addr.GetSection().GetTrailingSection(index) } // GetSubSection gets the subsection from the series starting from the given index and ending just before the give endIndex. // The first segment is at index 0. func (addr *IPv6Address) GetSubSection(index, endIndex int) *IPv6AddressSection { return addr.GetSection().GetSubSection(index, endIndex) } // GetNetworkSection returns an address section containing the segments with the network of the address or subnet, the prefix bits. // The returned section will have only as many segments as needed as determined by the existing CIDR network prefix length. // // If this series has no CIDR prefix length, the returned network section will // be the entire series as a prefixed section with prefix length matching the address bit length. func (addr *IPv6Address) GetNetworkSection() *IPv6AddressSection { return addr.GetSection().GetNetworkSection() } // GetNetworkSectionLen returns a section containing the segments with the network of the address or subnet, the prefix bits according to the given prefix length. // The returned section will have only as many segments as needed to contain the network. // // The new section will be assigned the given prefix length, // unless the existing prefix length is smaller, in which case the existing prefix length will be retained. func (addr *IPv6Address) GetNetworkSectionLen(prefLen BitCount) *IPv6AddressSection { return addr.GetSection().GetNetworkSectionLen(prefLen) } // GetHostSection returns a section containing the segments with the host of the address or subnet, the bits beyond the CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. // // If this series has no prefix length, the returned host section will be the full section. func (addr *IPv6Address) GetHostSection() *IPv6AddressSection { return addr.GetSection().GetHostSection() } // GetHostSectionLen returns a section containing the segments with the host of the address or subnet, the bits beyond the given CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. func (addr *IPv6Address) GetHostSectionLen(prefLen BitCount) *IPv6AddressSection { return addr.GetSection().GetHostSectionLen(prefLen) } // GetNetworkMask returns the network mask associated with the CIDR network prefix length of this address or subnet. // If this address or subnet has no prefix length, then the all-ones mask is returned. func (addr *IPv6Address) GetNetworkMask() *IPv6Address { return addr.getNetworkMask(ipv6Network).ToIPv6() } // GetHostMask returns the host mask associated with the CIDR network prefix length of this address or subnet. // If this address or subnet has no prefix length, then the all-ones mask is returned. func (addr *IPv6Address) GetHostMask() *IPv6Address { return addr.getHostMask(ipv6Network).ToIPv6() } // GetMixedAddressGrouping creates a grouping by combining an IPv6 address section comprising the first six segments (most significant) in this address // with the IPv4 section corresponding to the lowest (least-significant) two segments in this address, as produced by GetEmbeddedIPv4Address. func (addr *IPv6Address) GetMixedAddressGrouping() (*IPv6v4MixedAddressGrouping, addrerr.IncompatibleAddressError) { return addr.init().GetSection().getMixedAddressGrouping() } // GetEmbeddedIPv4AddressSection gets the IPv4 section corresponding to the lowest (least-significant) 2 segments (4 bytes) in this address. // Many IPv4 to IPv6 mapping schemes (but not all) use these 4 bytes for a mapped IPv4 address. // An error can result when one of the associated IPv6 segments has a range of values that cannot be split into two ranges. func (addr *IPv6Address) GetEmbeddedIPv4AddressSection() (*IPv4AddressSection, addrerr.IncompatibleAddressError) { return addr.init().GetSection().getEmbeddedIPv4AddressSection() } // GetEmbeddedIPv4Address gets the IPv4 address corresponding to the lowest (least-significant) 2 segments (4 bytes) in this address. // Many IPv4 to IPv6 mapping schemes (but not all) use these 4 bytes for a mapped IPv4 address. // An error can result when one of the associated IPv6 segments has a range of values that cannot be split into two ranges. func (addr *IPv6Address) GetEmbeddedIPv4Address() (*IPv4Address, addrerr.IncompatibleAddressError) { section, err := addr.GetEmbeddedIPv4AddressSection() if err != nil { return nil, err } return newIPv4Address(section), nil } // GetIPv4AddressSection produces an IPv4 address section corresponding to any sequence of bytes in this IPv6 address section func (addr *IPv6Address) GetIPv4AddressSection(startIndex, endIndex int) (*IPv4AddressSection, addrerr.IncompatibleAddressError) { return addr.init().GetSection().GetIPv4AddressSection(startIndex, endIndex) } // Get6To4IPv4Address Returns the second and third segments as an IPv4Address. func (addr *IPv6Address) Get6To4IPv4Address() (*IPv4Address, addrerr.IncompatibleAddressError) { return addr.GetEmbeddedIPv4AddressAt(2) } // GetEmbeddedIPv4AddressAt produces an IPv4 address corresponding to any sequence of 4 bytes in this IPv6 address, starting at the given index. func (addr *IPv6Address) GetEmbeddedIPv4AddressAt(byteIndex int) (*IPv4Address, addrerr.IncompatibleAddressError) { if byteIndex == IPv6MixedOriginalSegmentCount*IPv6BytesPerSegment { return addr.GetEmbeddedIPv4Address() } if byteIndex > IPv6ByteCount-IPv4ByteCount { return nil, &addressValueError{ addressError: addressError{key: "ipaddress.error.invalid.size"}, val: byteIndex, } } section, err := addr.init().GetSection().GetIPv4AddressSection(byteIndex, byteIndex+IPv4ByteCount) if err != nil { return nil, err } return newIPv4Address(section), nil } // GetIPv6Address creates an IPv6 mixed address using the given address for the trailing embedded IPv4 segments func (addr *IPv6Address) GetIPv6Address(embedded IPv4Address) (*IPv6Address, addrerr.IncompatibleAddressError) { return embedded.getIPv6Address(addr.WithoutPrefixLen().getDivisionsInternal()) } // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (addr *IPv6Address) CopySubSegments(start, end int, segs []*IPv6AddressSegment) (count int) { return addr.GetSection().CopySubSegments(start, end, segs) } // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. func (addr *IPv6Address) CopySegments(segs []*IPv6AddressSegment) (count int) { return addr.GetSection().CopySegments(segs) } // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as this address. func (addr *IPv6Address) GetSegments() []*IPv6AddressSegment { return addr.GetSection().GetSegments() } // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. func (addr *IPv6Address) GetSegment(index int) *IPv6AddressSegment { return addr.init().getSegment(index).ToIPv6() } // GetSegmentCount returns the segment count, the number of segments in this address, which is 8 func (addr *IPv6Address) GetSegmentCount() int { return addr.GetDivisionCount() } // ForEachSegment visits each segment in order from most-significant to least, the most significant with index 0, calling the given function for each, terminating early if the function returns true. // Returns the number of visited segments. func (addr *IPv6Address) ForEachSegment(consumer func(segmentIndex int, segment *IPv6AddressSegment) (stop bool)) int { return addr.GetSection().ForEachSegment(consumer) } // GetGenericDivision returns the segment at the given index as a DivisionType. func (addr *IPv6Address) GetGenericDivision(index int) DivisionType { return addr.init().getDivision(index) } // GetGenericSegment returns the segment at the given index as an AddressSegmentType. // The first segment is at index 0. // GetGenericSegment will panic given a negative index or an index matching or larger than the segment count. func (addr *IPv6Address) GetGenericSegment(index int) AddressSegmentType { return addr.init().getSegment(index) } // GetDivisionCount returns the segment count. func (addr *IPv6Address) GetDivisionCount() int { return addr.init().getDivisionCount() } // GetIPVersion returns IPv6, the IP version of this address. func (addr *IPv6Address) GetIPVersion() IPVersion { return IPv6 } func (addr *IPv6Address) checkIdentity(section *IPv6AddressSection) *IPv6Address { if section == nil { return nil } sec := section.ToSectionBase() if sec == addr.section { return addr } return newIPv6AddressZoned(section, string(addr.zone)) } // Mask applies the given mask to all addresses represented by this IPv6Address. // The mask is applied to all individual addresses. // // If this represents multiple addresses, and applying the mask to all addresses creates a set of addresses // that cannot be represented as a sequential range within each segment, then an error is returned. func (addr *IPv6Address) Mask(other *IPv6Address) (masked *IPv6Address, err addrerr.IncompatibleAddressError) { return addr.maskPrefixed(other, true) } func (addr *IPv6Address) maskPrefixed(other *IPv6Address, retainPrefix bool) (masked *IPv6Address, err addrerr.IncompatibleAddressError) { addr = addr.init() sect, err := addr.GetSection().maskPrefixed(other.GetSection(), retainPrefix) if err == nil { masked = addr.checkIdentity(sect) } return } // BitwiseOr does the bitwise disjunction with this address or subnet, useful when subnetting. // It is similar to Mask which does the bitwise conjunction. // // The operation is applied to all individual addresses and the result is returned. // // If this is a subnet representing multiple addresses, and applying the operation to all addresses creates a set of addresses // that cannot be represented as a sequential range within each segment, then an error is returned. func (addr *IPv6Address) BitwiseOr(other *IPv6Address) (masked *IPv6Address, err addrerr.IncompatibleAddressError) { return addr.bitwiseOrPrefixed(other, true) } func (addr *IPv6Address) bitwiseOrPrefixed(other *IPv6Address, retainPrefix bool) (masked *IPv6Address, err addrerr.IncompatibleAddressError) { addr = addr.init() sect, err := addr.GetSection().bitwiseOrPrefixed(other.GetSection(), retainPrefix) if err == nil { masked = addr.checkIdentity(sect) } return } // Subtract subtracts the given subnet from this subnet, returning an array of subnets for the result (the subnets will not be contiguous so an array is required). // Subtract computes the subnet difference, the set of addresses in this address subnet but not in the provided subnet. // This is also known as the relative complement of the given argument in this subnet. // This is set subtraction, not subtraction of address values (use Increment for the latter). We have a subnet of addresses and we are removing those addresses found in the argument subnet. // If there are no remaining addresses, nil is returned. func (addr *IPv6Address) Subtract(other *IPv6Address) []*IPv6Address { addr = addr.init() sects, _ := addr.GetSection().Subtract(other.GetSection()) sectLen := len(sects) if sectLen == 0 { return nil } else if sectLen == 1 { sec := sects[0] if sec.ToSectionBase() == addr.section { return []*IPv6Address{addr} } } res := make([]*IPv6Address, sectLen) for i, sect := range sects { res[i] = newIPv6AddressZoned(sect, string(addr.zone)) } return res } // Intersect returns the subnet whose addresses are found in both this and the given subnet argument, or nil if no such addresses exist. // // This is also known as the conjunction of the two sets of addresses. func (addr *IPv6Address) Intersect(other *IPv6Address) *IPv6Address { addr = addr.init() section, _ := addr.GetSection().Intersect(other.GetSection()) if section == nil { return nil } return addr.checkIdentity(section) } // SpanWithRange returns an IPv6AddressSeqRange instance that spans this subnet to the given subnet. // If the other address is a different version than this, then the other is ignored, and the result is equivalent to calling ToSequentialRange. func (addr *IPv6Address) SpanWithRange(other *IPv6Address) *SequentialRange[*IPv6Address] { return NewSequentialRange(addr.init(), other) } // GetLower returns the lowest address in the subnet range, // which will be the receiver if it represents a single address. // For example, for "1::1:2-3:4:5-6", the series "1::1:2:4:5" is returned. func (addr *IPv6Address) GetLower() *IPv6Address { return addr.init().getLower().ToIPv6() } // GetUpper returns the highest address in the subnet range, // which will be the receiver if it represents a single address. // For example, for "1::1:2-3:4:5-6", the series "1::1:3:4:6" is returned. func (addr *IPv6Address) GetUpper() *IPv6Address { return addr.init().getUpper().ToIPv6() } // GetLowerIPAddress returns the address in the subnet or address collection with the lowest numeric value, // which will be the receiver if it represents a single address. // GetLowerIPAddress implements the IPAddressRange interface func (addr *IPv6Address) GetLowerIPAddress() *IPAddress { return addr.GetLower().ToIP() } // GetUpperIPAddress returns the address in the subnet or address collection with the highest numeric value, // which will be the receiver if it represents a single address. // GetUpperIPAddress implements the IPAddressRange interface func (addr *IPv6Address) GetUpperIPAddress() *IPAddress { return addr.GetUpper().ToIP() } // IsZeroHostLen returns whether the host section is always zero for all individual addresses in this subnet, // for the given prefix length. // // If the host section is zero length (there are zero host bits), IsZeroHostLen returns true. func (addr *IPv6Address) IsZeroHostLen(prefLen BitCount) bool { return addr.init().isZeroHostLen(prefLen) } // ToZeroHost converts the address or subnet to one in which all individual addresses have a host of zero, // the host being the bits following the prefix length. // If the address or subnet has no prefix length, then it returns an all-zero address. // // The returned address or subnet will have the same prefix and prefix length. // // For instance, the zero host of "1.2.3.4/16" is the individual address "1.2.0.0/16". // // This returns an error if the subnet is a range of addresses which cannot be converted to a range in which all addresses have zero hosts, // because the conversion results in a subnet segment that is not a sequential range of values. func (addr *IPv6Address) ToZeroHost() (*IPv6Address, addrerr.IncompatibleAddressError) { res, err := addr.init().toZeroHost(false) return res.ToIPv6(), err } // ToZeroHostLen converts the address or subnet to one in which all individual addresses have a host of zero, // the host being the bits following the given prefix length. // If this address or subnet has the same prefix length, then the returned one will too, otherwise the returned series will have no prefix length. // // For instance, the zero host of "1.2.3.4" for the prefix length of 16 is the address "1.2.0.0". // // This returns an error if the subnet is a range of addresses which cannot be converted to a range in which all addresses have zero hosts, // because the conversion results in a subnet segment that is not a sequential range of values. func (addr *IPv6Address) ToZeroHostLen(prefixLength BitCount) (*IPv6Address, addrerr.IncompatibleAddressError) { res, err := addr.init().toZeroHostLen(prefixLength) return res.ToIPv6(), err } // ToZeroNetwork converts the address or subnet to one in which all individual addresses have a network of zero, // the network being the bits within the prefix length. // If the address or subnet has no prefix length, then it returns an all-zero address. // // The returned address or subnet will have the same prefix length. func (addr *IPv6Address) ToZeroNetwork() *IPv6Address { return addr.init().toZeroNetwork().ToIPv6() } // IsMaxHostLen returns whether the host is all one-bits, the max value, for all individual addresses in this subnet, // for the given prefix length, the host being the bits following the prefix. // // If the host section is zero length (there are zero host bits), IsMaxHostLen returns true. func (addr *IPv6Address) IsMaxHostLen(prefLen BitCount) bool { return addr.init().isMaxHostLen(prefLen) } // ToMaxHost converts the address or subnet to one in which all individual addresses have a host of all one-bits, the max value, // the host being the bits following the prefix length. // If the address or subnet has no prefix length, then it returns an all-ones address, the max address. // // The returned address or subnet will have the same prefix and prefix length. // // This returns an error if the subnet is a range of addresses which cannot be converted to a range in which all addresses have max hosts, // because the conversion results in a subnet segment that is not a sequential range of values. func (addr *IPv6Address) ToMaxHost() (*IPv6Address, addrerr.IncompatibleAddressError) { res, err := addr.init().toMaxHost() return res.ToIPv6(), err } // ToMaxHostLen converts the address or subnet to one in which all individual addresses have a host of all one-bits, the max host, // the host being the bits following the given prefix length. // If this address or subnet has the same prefix length, then the resulting one will too, otherwise the resulting address or subnet will have no prefix length. // // For instance, the zero host of "1.2.3.4" for the prefix length of 16 is the address "1.2.255.255". // // This returns an error if the subnet is a range of addresses which cannot be converted to a range in which all addresses have max hosts, // because the conversion results in a subnet segment that is not a sequential range of values. func (addr *IPv6Address) ToMaxHostLen(prefixLength BitCount) (*IPv6Address, addrerr.IncompatibleAddressError) { res, err := addr.init().toMaxHostLen(prefixLength) return res.ToIPv6(), err } // ToPrefixBlock returns the subnet associated with the prefix length of this address. // If this address has no prefix length, this address is returned. // // The subnet will include all addresses with the same prefix as this one, the prefix "block". // The network prefix will match the prefix of this address or subnet, and the host values will span all values. // // For example, if the address is "1:2:3:4:5:6:7:8/64" it returns the subnet "1:2:3:4::/64" which can also be written as "1:2:3:4:*:*:*:*/64". func (addr *IPv6Address) ToPrefixBlock() *IPv6Address { return addr.init().toPrefixBlock().ToIPv6() } // ToPrefixBlockLen returns the subnet associated with the given prefix length. // // The subnet will include all addresses with the same prefix as this one, the prefix "block" for that prefix length. // The network prefix will match the prefix of this address or subnet, and the host values will span all values. // // For example, if the address is "1:2:3:4:5:6:7:8" and the prefix length provided is 64, it returns the subnet "1:2:3:4::/64" which can also be written as "1:2:3:4:*:*:*:*/64". func (addr *IPv6Address) ToPrefixBlockLen(prefLen BitCount) *IPv6Address { return addr.init().toPrefixBlockLen(prefLen).ToIPv6() } // ToBlock creates a new block of addresses by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (addr *IPv6Address) ToBlock(segmentIndex int, lower, upper SegInt) *IPv6Address { return addr.init().toBlock(segmentIndex, lower, upper).ToIPv6() } // WithoutPrefixLen provides the same address but with no prefix length. The values remain unchanged. func (addr *IPv6Address) WithoutPrefixLen() *IPv6Address { if !addr.IsPrefixed() { return addr } return addr.init().withoutPrefixLen().ToIPv6() } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address. // The provided prefix length will be adjusted to these boundaries if necessary. func (addr *IPv6Address) SetPrefixLen(prefixLen BitCount) *IPv6Address { return addr.init().setPrefixLen(prefixLen).ToIPv6() } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this address has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this address has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr *IPv6Address) SetPrefixLenZeroed(prefixLen BitCount) (*IPv6Address, addrerr.IncompatibleAddressError) { res, err := addr.init().setPrefixLenZeroed(prefixLen) return res.ToIPv6(), err } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address. // // If this address has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (addr *IPv6Address) AdjustPrefixLen(prefixLen BitCount) *IPv6Address { return addr.init().adjustPrefixLen(prefixLen).ToIPv6() } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address. // // If this address has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr *IPv6Address) AdjustPrefixLenZeroed(prefixLen BitCount) (*IPv6Address, addrerr.IncompatibleAddressError) { res, err := addr.init().adjustPrefixLenZeroed(prefixLen) return res.ToIPv6(), err } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this address. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such address - it is required that the range of values match the range of a prefix block. // If there is no such address, then nil is returned. func (addr *IPv6Address) AssignPrefixForSingleBlock() *IPv6Address { return addr.init().assignPrefixForSingleBlock().ToIPv6() } // AssignMinPrefixForBlock returns an equivalent subnet, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this subnet. // // In other words, this method assigns a prefix length to this subnet matching the largest prefix block in this subnet. func (addr *IPv6Address) AssignMinPrefixForBlock() *IPv6Address { return addr.init().assignMinPrefixForBlock().ToIPv6() } // ToSinglePrefixBlockOrAddress converts to a single prefix block or address. // If the given address is a single prefix block, it is returned. // If it can be converted to a single prefix block by assigning a prefix length, the converted block is returned. // If it is a single address, any prefix length is removed and the address is returned. // Otherwise, nil is returned. // This method provides the address formats used by tries. // ToSinglePrefixBlockOrAddress is quite similar to AssignPrefixForSingleBlock, which always returns prefixed addresses, while this does not. func (addr *IPv6Address) ToSinglePrefixBlockOrAddress() *IPv6Address { return addr.init().toSinglePrefixBlockOrAddr().ToIPv6() } func (addr *IPv6Address) toSinglePrefixBlockOrAddress() (*IPv6Address, addrerr.IncompatibleAddressError) { if addr == nil { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.address.not.block"}} } res := addr.ToSinglePrefixBlockOrAddress() if res == nil { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.address.not.block"}} } return res, nil } // ContainsPrefixBlock returns whether the range of this address or subnet contains the block of addresses for the given prefix length. // // Unlike ContainsSinglePrefixBlock, whether there are multiple prefix values in this item for the given prefix length makes no difference. // // Use GetMinPrefixLenForBlock to determine the smallest prefix length for which this method returns true. func (addr *IPv6Address) ContainsPrefixBlock(prefixLen BitCount) bool { return addr.init().ipAddressInternal.ContainsPrefixBlock(prefixLen) } // ContainsSinglePrefixBlock returns whether this address contains a single prefix block for the given prefix length. // // This means there is only one prefix value for the given prefix length, and it also contains the full prefix block for that prefix, all addresses with that prefix. // // Use GetPrefixLenForSingleBlock to determine whether there is a prefix length for which this method returns true. func (addr *IPv6Address) ContainsSinglePrefixBlock(prefixLen BitCount) bool { return addr.init().ipAddressInternal.ContainsSinglePrefixBlock(prefixLen) } // GetMinPrefixLenForBlock returns the smallest prefix length such that this includes the block of addresses for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this represents just a single address, returns the bit length of this address. func (addr *IPv6Address) GetMinPrefixLenForBlock() BitCount { return addr.init().ipAddressInternal.GetMinPrefixLenForBlock() } // GetPrefixLenForSingleBlock returns a prefix length for which the range of this address subnet matches exactly the block of addresses for that prefix. // // If the range can be described this way, then this method returns the same value as GetMinPrefixLenForBlock. // // If no such prefix exists, returns nil. // // If this segment grouping represents a single value, returns the bit length of this address division series. func (addr *IPv6Address) GetPrefixLenForSingleBlock() PrefixLen { return addr.init().ipAddressInternal.GetPrefixLenForSingleBlock() } // GetValue returns the lowest address in this subnet or address as an integer value. func (addr *IPv6Address) GetValue() *big.Int { return addr.init().section.GetValue() } // GetUpperValue returns the highest address in this subnet or address as an integer value. func (addr *IPv6Address) GetUpperValue() *big.Int { return addr.init().section.GetUpperValue() } // GetNetIPAddr returns the lowest address in this subnet or address as a net.IPAddr. func (addr *IPv6Address) GetNetIPAddr() *net.IPAddr { return addr.ToIP().GetNetIPAddr() } // GetUpperNetIPAddr returns the highest address in this subnet or address as a net.IPAddr. func (addr *IPv6Address) GetUpperNetIPAddr() *net.IPAddr { return addr.ToIP().GetUpperNetIPAddr() } // GetNetIP returns the lowest address in this subnet or address as a net.IP. func (addr *IPv6Address) GetNetIP() net.IP { return addr.Bytes() } // GetUpperNetIP returns the highest address in this subnet or address as a net.IP. func (addr *IPv6Address) GetUpperNetIP() net.IP { return addr.UpperBytes() } // GetNetNetIPAddr returns the lowest address in this subnet or address range as a netip.Addr. func (addr *IPv6Address) GetNetNetIPAddr() netip.Addr { res := addr.init().getNetNetIPAddr() if addr.hasZone() { res = res.WithZone(string(addr.zone)) } return res } // GetUpperNetNetIPAddr returns the highest address in this subnet or address range as a netip.Addr. func (addr *IPv6Address) GetUpperNetNetIPAddr() netip.Addr { return addr.init().getUpperNetNetIPAddr() } // CopyNetIP copies the value of the lowest individual address in the subnet into a net.IP. // // If the value can fit in the given net.IP slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *IPv6Address) CopyNetIP(bytes net.IP) net.IP { return addr.CopyBytes(bytes) } // CopyUpperNetIP copies the value of the highest individual address in the subnet into a net.IP. // // If the value can fit in the given net.IP slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *IPv6Address) CopyUpperNetIP(bytes net.IP) net.IP { return addr.CopyUpperBytes(bytes) } // Bytes returns the lowest address in this subnet or address as a byte slice. func (addr *IPv6Address) Bytes() []byte { return addr.init().section.Bytes() } // UpperBytes returns the highest address in this subnet or address as a byte slice. func (addr *IPv6Address) UpperBytes() []byte { return addr.init().section.UpperBytes() } // CopyBytes copies the value of the lowest individual address in the subnet into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *IPv6Address) CopyBytes(bytes []byte) []byte { return addr.init().section.CopyBytes(bytes) } // CopyUpperBytes copies the value of the highest individual address in the subnet into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *IPv6Address) CopyUpperBytes(bytes []byte) []byte { return addr.init().section.CopyUpperBytes(bytes) } // IsMax returns whether this address matches exactly the maximum possible value, the address whose bits are all ones. func (addr *IPv6Address) IsMax() bool { return addr.init().section.IsMax() } // IncludesMax returns whether this address includes the max address, the address whose bits are all ones, within its range. func (addr *IPv6Address) IncludesMax() bool { return addr.init().section.IncludesMax() } // TestBit returns true if the bit in the lower value of this address at the given index is 1, where index 0 refers to the least significant bit. // In other words, it computes (bits & (1 << n)) != 0), using the lower value of this address. // TestBit will panic if n < 0, or if it matches or exceeds the bit count of this item. func (addr *IPv6Address) TestBit(n BitCount) bool { return addr.init().testBit(n) } // IsOneBit returns true if the bit in the lower value of this address at the given index is 1, where index 0 refers to the most significant bit. // IsOneBit will panic if bitIndex is less than zero, or if it is larger than the bit count of this item. func (addr *IPv6Address) IsOneBit(bitIndex BitCount) bool { return addr.init().isOneBit(bitIndex) } // PrefixEqual determines if the given address matches this address up to the prefix length of this address. // It returns whether the two addresses share the same range of prefix values. func (addr *IPv6Address) PrefixEqual(other AddressType) bool { return addr.init().prefixEquals(other) } // PrefixContains returns whether the prefix values in the given address or subnet // are prefix values in this address or subnet, using the prefix length of this address or subnet. // If this address has no prefix length, the entire address is compared. // // It returns whether the prefix of this address contains all values of the same prefix length in the given address. func (addr *IPv6Address) PrefixContains(other AddressType) bool { return addr.init().prefixContains(other) } // Contains returns whether this is the same type and version as the given address or subnet and whether it contains all addresses in the given address or subnet. func (addr *IPv6Address) Contains(other AddressType) bool { if other == nil || other.ToAddressBase() == nil { return true } else if addr == nil { return false } addr = addr.init() otherAddr := other.ToAddressBase() if addr.ToAddressBase() == otherAddr { return true } return otherAddr.getAddrType() == ipv6Type && addr.section.sameCountTypeContains(otherAddr.GetSection()) && addr.isSameZone(other.ToAddressBase()) } // Compare returns a negative integer, zero, or a positive integer if this address or subnet is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (addr *IPv6Address) Compare(item AddressItem) int { return CountComparator.Compare(addr, item) } // Equal returns whether the given address or subnet is equal to this address or subnet. // Two address instances are equal if they represent the same set of addresses. func (addr *IPv6Address) Equal(other AddressType) bool { if addr == nil { return other == nil || other.ToAddressBase() == nil } else if other.ToAddressBase() == nil { return false } return other.ToAddressBase().getAddrType() == ipv6Type && addr.init().section.sameCountTypeEquals(other.ToAddressBase().GetSection()) && addr.isSameZone(other.ToAddressBase()) } // CompareSize compares the counts of two subnets or addresses or items, the number of individual addresses or items within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether this subnet represents more individual addresses or items than another. // // CompareSize returns a positive integer if this address or subnet has a larger count than the one given, zero if they are the same, or a negative integer if the other has a larger count. func (addr *IPv6Address) CompareSize(other AddressItem) int { if addr == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return addr.init().compareSize(other) } // TrieCompare compares two addresses according to address trie ordering. // It returns a number less than zero, zero, or a number greater than zero if the first address argument is less than, equal to, or greater than the second. // // The comparison is intended for individual addresses and CIDR prefix blocks. // If an address is neither an individual address nor a prefix block, it is treated like one: // // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *IPv6Address) TrieCompare(other *IPv6Address) int { return addr.init().trieCompare(other.ToAddressBase()) } // TrieIncrement returns the next address or block according to address trie ordering // // If an address is neither an individual address nor a prefix block, it is treated like one: // // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *IPv6Address) TrieIncrement() *IPv6Address { if res, ok := trieIncrement(addr); ok { return res } return nil } // TrieDecrement returns the previous address or block according to address trie ordering // // If an address is neither an individual address nor a prefix block, it is treated like one: // // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *IPv6Address) TrieDecrement() *IPv6Address { if res, ok := trieDecrement(addr); ok { return res } return nil } // MatchesWithMask applies the mask to this address and then compares the result with the given address, // returning true if they match, false otherwise. func (addr *IPv6Address) MatchesWithMask(other *IPv6Address, mask *IPv6Address) bool { return addr.init().GetSection().MatchesWithMask(other.GetSection(), mask.GetSection()) } // GetMaxSegmentValue returns the maximum possible segment value for this type of address. // // Note this is not the maximum of the range of segment values in this specific address, // this is the maximum value of any segment for this address type and version, determined by the number of bits per segment. func (addr *IPv6Address) GetMaxSegmentValue() SegInt { return addr.init().getMaxSegmentValue() } // WithoutZone returns the same address but with no zone. func (addr *IPv6Address) WithoutZone() *IPv6Address { if addr.HasZone() { return newIPv6Address(addr.GetSection()) } return addr } // SetZone returns the same address associated with the given zone, The existing zone, if any, is replaced. func (addr *IPv6Address) SetZone(zone string) *IPv6Address { if Zone(zone) == addr.GetZone() { return addr } return newIPv6AddressZoned(addr.GetSection(), zone) } // ToSequentialRange creates a sequential range instance from the lowest and highest addresses in this subnet. // // The two will represent the same set of individual addresses if and only if IsSequential is true. // To get a series of ranges that represent the same set of individual addresses use the SequentialBlockIterator (or PrefixIterator), // and apply this method to each iterated subnet. // // If this represents just a single address then the returned instance covers just that single address as well. func (addr *IPv6Address) ToSequentialRange() *SequentialRange[*IPv6Address] { if addr == nil { return nil } addr = addr.init().WithoutPrefixLen().WithoutZone() return newSequRangeUnchecked( addr.GetLower(), addr.GetUpper(), addr.isMultiple()) } func (addr *IPv6Address) getLowestHighestAddrs() (lower, upper *IPv6Address) { l, u := addr.ipAddressInternal.getLowestHighestAddrs() return l.ToIPv6(), u.ToIPv6() } // ToAddressString retrieves or generates an IPAddressString instance for this IPAddress instance. // This may be the IPAddressString this instance was generated from, if it was generated from an IPAddressString. // // In general, users are intended to create IPAddress instances from IPAddressString instances, // while the reverse direction is generally not common and not useful, except under specific circumstances. // // However, the reverse direction can be useful under certain circumstances, // such as when maintaining a collection of HostIdentifierString instances. func (addr *IPv6Address) ToAddressString() *IPAddressString { return addr.init().ToIP().ToAddressString() } // IncludesZeroHostLen returns whether the subnet contains an individual address with a host of zero, an individual address for which all bits past the given prefix length are zero. func (addr *IPv6Address) IncludesZeroHostLen(networkPrefixLength BitCount) bool { return addr.init().includesZeroHostLen(networkPrefixLength) } // IncludesMaxHostLen returns whether the subnet contains an individual address with a host of all one-bits, an individual address for which all bits past the given prefix length are all ones. func (addr *IPv6Address) IncludesMaxHostLen(networkPrefixLength BitCount) bool { return addr.init().includesMaxHostLen(networkPrefixLength) } // IsLinkLocal returns whether the address is link local, whether unicast or multicast. func (addr *IPv6Address) IsLinkLocal() bool { firstSeg := addr.GetSegment(0) return (addr.IsMulticast() && firstSeg.matchesWithMask(2, 0xf)) || // ffx2::/16 //1111 1110 10 .... fe8x currently only in use firstSeg.MatchesWithPrefixMask(0xfe80, 10) } // IsLocal returns true if the address is link local, site local, organization local, administered locally, or unspecified. // This includes both unicast and multicast. func (addr *IPv6Address) IsLocal() bool { if addr.IsMulticast() { /* [RFC4291][RFC7346] 11111111|flgs|scop scope 4 bits 1 Interface-Local scope 2 Link-Local scope 3 Realm-Local scope 4 Admin-Local scope 5 Site-Local scope 8 Organization-Local scope E Global scope */ firstSeg := addr.GetSegment(0) if firstSeg.matchesWithMask(8, 0xf) { return true } if firstSeg.GetValueCount() <= 5 && (firstSeg.getSegmentValue()&0xf) >= 1 && (firstSeg.getUpperSegmentValue()&0xf) <= 5 { //all values fall within the range from interface local to site local return true } //source specific multicast //rfc4607 and https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml //FF3X::8000:0 - FF3X::FFFF:FFFF Reserved for local host allocation [RFC4607] if firstSeg.MatchesWithPrefixMask(0xff30, 12) && addr.GetSegment(6).MatchesWithPrefixMask(0x8000, 1) { return true } } return addr.IsLinkLocal() || addr.IsSiteLocal() || addr.IsUniqueLocal() || addr.IsAnyLocal() } // IsUnspecified returns whether this is the unspecified address. The unspecified address is the address that is all zeros. func (addr *IPv6Address) IsUnspecified() bool { return addr.section == nil || addr.IsZero() } // IsAnyLocal returns whether this address is the address which binds to any address on the local host. // This is the address that has the value of 0, aka the unspecified address. func (addr *IPv6Address) IsAnyLocal() bool { return addr.section == nil || addr.IsZero() } // IsSiteLocal returns true if the address is site-local, or all addresses in the subnet are site-local, see rfc 3513, 3879, and 4291. func (addr *IPv6Address) IsSiteLocal() bool { firstSeg := addr.GetSegment(0) return (addr.IsMulticast() && firstSeg.matchesWithMask(5, 0xf)) || // ffx5::/16 //1111 1110 11 ... firstSeg.MatchesWithPrefixMask(0xfec0, 10) // deprecated RFC 3879 } // IsUniqueLocal returns true if the address is unique-local, or all addresses in the subnet are unique-local, see RFC 4193. func (addr *IPv6Address) IsUniqueLocal() bool { //RFC 4193 return addr.GetSegment(0).MatchesWithPrefixMask(0xfc00, 7) } // IsIPv4Mapped returns whether the address or all addresses in the subnet are IPv4-mapped. // // "::ffff:x:x/96" indicates an IPv6 address mapped to IPv4. func (addr *IPv6Address) IsIPv4Mapped() bool { //::ffff:x:x/96 indicates IPv6 address mapped to IPv4 if addr.GetSegment(5).Matches(IPv6MaxValuePerSegment) { for i := 0; i < 5; i++ { if !addr.GetSegment(i).IsZero() { return false } } return true } return false } // IsIPv4Compatible returns whether the address or all addresses in the subnet are IPv4-compatible. func (addr *IPv6Address) IsIPv4Compatible() bool { return addr.GetSegment(0).IsZero() && addr.GetSegment(1).IsZero() && addr.GetSegment(2).IsZero() && addr.GetSegment(3).IsZero() && addr.GetSegment(4).IsZero() && addr.GetSegment(5).IsZero() } // Is6To4 returns whether the address or subnet is IPv6 to IPv4 relay. func (addr *IPv6Address) Is6To4() bool { //2002::/16 return addr.GetSegment(0).Matches(0x2002) } // Is6Over4 returns whether the address or all addresses in the subnet are 6over4. func (addr *IPv6Address) Is6Over4() bool { return addr.GetSegment(0).Matches(0xfe80) && addr.GetSegment(1).IsZero() && addr.GetSegment(2).IsZero() && addr.GetSegment(3).IsZero() && addr.GetSegment(4).IsZero() && addr.GetSegment(5).IsZero() } // IsTeredo returns whether the address or all addresses in the subnet are Teredo. func (addr *IPv6Address) IsTeredo() bool { //2001::/32 return addr.GetSegment(0).Matches(0x2001) && addr.GetSegment(1).IsZero() } // IsIsatap returns whether the address or all addresses in the subnet are ISATAP. func (addr *IPv6Address) IsIsatap() bool { // 0,1,2,3 is fe80:: // 4 can be 0200 return addr.GetSegment(0).Matches(0xfe80) && addr.GetSegment(1).IsZero() && addr.GetSegment(2).IsZero() && addr.GetSegment(3).IsZero() && (addr.GetSegment(4).IsZero() || addr.GetSegment(4).Matches(0x200)) && addr.GetSegment(5).Matches(0x5efe) } // IsIPv4Translatable returns whether the address or subnet is IPv4 translatable as in RFC 2765. func (addr *IPv6Address) IsIPv4Translatable() bool { //rfc 2765 //::ffff:0:x:x/96 indicates IPv6 addresses translated from IPv4 return addr.GetSegment(4).Matches(0xffff) && addr.GetSegment(5).IsZero() && addr.GetSegment(0).IsZero() && addr.GetSegment(1).IsZero() && addr.GetSegment(2).IsZero() && addr.GetSegment(3).IsZero() } // IsWellKnownIPv4Translatable returns whether the address has the well-known prefix for IPv4-translatable addresses as in RFC 6052 and RFC 6144. func (addr *IPv6Address) IsWellKnownIPv4Translatable() bool { //rfc 6052 rfc 6144 //64:ff9b::/96 prefix for auto ipv4/ipv6 translation if addr.GetSegment(0).Matches(0x64) && addr.GetSegment(1).Matches(0xff9b) { for i := 2; i <= 5; i++ { if !addr.GetSegment(i).IsZero() { return false } } return true } return false } // IsMulticast returns whether this address or subnet is entirely multicast. func (addr *IPv6Address) IsMulticast() bool { // 11111111... return addr.GetSegment(0).MatchesWithPrefixMask(0xff00, 8) } // IsLoopback returns whether this address is a loopback address, namely "::1". func (addr *IPv6Address) IsLoopback() bool { if addr.section == nil { return false } //::1 i := 0 lim := addr.GetSegmentCount() - 1 for ; i < lim; i++ { if !addr.GetSegment(i).IsZero() { return false } } return addr.GetSegment(i).Matches(1) } // Iterator provides an iterator to iterate through the individual addresses of this address or subnet. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual addresses. // // Call IsMultiple to determine if this instance represents multiple addresses, or GetCount for the count. func (addr *IPv6Address) Iterator() Iterator[*IPv6Address] { if addr == nil { return ipv6AddressIterator{nilAddrIterator()} } return ipv6AddressIterator{addr.init().addrIterator(nil)} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this subnet, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this subnet. // // If the subnet has no prefix length, then this is equivalent to Iterator. func (addr *IPv6Address) PrefixIterator() Iterator[*IPv6Address] { return ipv6AddressIterator{addr.init().prefixIterator(false)} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address or subnet. // Each iterated address or subnet will be a prefix block with the same prefix length as this address or subnet. // // If this address has no prefix length, then this is equivalent to Iterator. func (addr *IPv6Address) PrefixBlockIterator() Iterator[*IPv6Address] { return ipv6AddressIterator{addr.init().prefixIterator(true)} } // BlockIterator iterates through the addresses that can be obtained by iterating through all the upper segments up to the given segment count. // The segments following remain the same in all iterated addresses. func (addr *IPv6Address) BlockIterator(segmentCount int) Iterator[*IPv6Address] { return ipv6AddressIterator{addr.init().blockIterator(segmentCount)} } // SequentialBlockIterator iterates through the sequential subnets or addresses that make up this address or subnet. // // Practically, this means finding the count of segments for which the segments that follow are not full range, and then using BlockIterator with that segment count. // // For instance, given the IPv4 subnet "1-2.3-4.5-6.7-8", it will iterate through "1.3.5.7-8", "1.3.6.7-8", "1.4.5.7-8", "1.4.6.7-8", "2.3.5.7-8", "2.3.6.7-8", "2.4.6.7-8" and "2.4.6.7-8". // // Use GetSequentialBlockCount to get the number of iterated elements. func (addr *IPv6Address) SequentialBlockIterator() Iterator[*IPv6Address] { return ipv6AddressIterator{addr.init().sequentialBlockIterator()} } // GetSequentialBlockIndex gets the minimal segment index for which all following segments are full-range blocks. // // The segment at this index is not a full-range block itself, unless all segments are full-range. // The segment at this index and all following segments form a sequential range. // For the full subnet to be sequential, the preceding segments must be single-valued. func (addr *IPv6Address) GetSequentialBlockIndex() int { return addr.init().getSequentialBlockIndex() } // GetSequentialBlockCount provides the count of elements from the sequential block iterator, the minimal number of sequential subnets that comprise this subnet. func (addr *IPv6Address) GetSequentialBlockCount() *big.Int { return addr.getSequentialBlockCount() } func (addr *IPv6Address) rangeIterator( upper *IPv6Address, valsAreMultiple bool, prefixLen PrefixLen, segProducer func(addr *IPAddress, index int) *IPAddressSegment, segmentIteratorProducer func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment], segValueComparator func(seg1, seg2 *IPAddress, index int) bool, networkSegmentIndex, hostSegmentIndex int, prefixedSegIteratorProducer func(seg *IPAddressSegment, index int) Iterator[*IPAddressSegment], ) Iterator[*IPv6Address] { return ipv6AddressIterator{addr.ipAddressInternal.rangeIterator(upper.ToIP(), valsAreMultiple, prefixLen, segProducer, segmentIteratorProducer, segValueComparator, networkSegmentIndex, hostSegmentIndex, prefixedSegIteratorProducer)} } // IncrementBoundary returns the address that is the given increment from the range boundaries of this subnet. // // If the given increment is positive, adds the value to the upper address (GetUpper) in the subnet range to produce a new address. // If the given increment is negative, adds the value to the lower address (GetLower) in the subnet range to produce a new address. // If the increment is zero, returns this address. // // If this is a single address value, that address is simply incremented by the given increment value, positive or negative. // // On address overflow or underflow, IncrementBoundary returns nil. func (addr *IPv6Address) IncrementBoundary(increment int64) *IPv6Address { return addr.init().incrementBoundary(increment).ToIPv6() } // Increment returns the address from the subnet that is the given increment upwards into the subnet range, // with the increment of 0 returning the first address in the range. // // If the increment i matches or exceeds the subnet size count c, then i - c + 1 // is added to the upper address of the range. // An increment matching the subnet count gives you the address just above the highest address in the subnet. // // If the increment is negative, it is added to the lower address of the range. // To get the address just below the lowest address of the subnet, use the increment -1. // // If this is just a single address value, the address is simply incremented by the given increment, positive or negative. // // If this is a subnet with multiple values, a positive increment i is equivalent i + 1 values from the subnet iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the subnet count is equivalent to the same number of iterator values preceding the upper bound of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On address overflow or underflow, Increment returns nil. func (addr *IPv6Address) Increment(increment int64) *IPv6Address { return addr.init().increment(increment).ToIPv6() } // SpanWithPrefixBlocks returns an array of prefix blocks that cover the same set of addresses as this subnet. // // Unlike SpanWithPrefixBlocksTo, the result only includes addresses that are a part of this subnet. func (addr *IPv6Address) SpanWithPrefixBlocks() []*IPv6Address { if addr.IsSequential() { if addr.IsSinglePrefixBlock() { return []*IPv6Address{addr} } wrapped := wrapIPAddress(addr.ToIP()) spanning := getSpanningPrefixBlocks(wrapped, wrapped) return cloneToIPv6Addrs(spanning) } wrapped := wrapIPAddress(addr.ToIP()) return cloneToIPv6Addrs(spanWithPrefixBlocks(wrapped)) } // SpanWithPrefixBlocksTo returns the smallest slice of prefix block subnets that span from this subnet to the given subnet. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. // // From the list of returned subnets you can recover the original range (from this to other) by converting each to [SequentialRange] with ToSequentialRange // and them joining them into a single range with the Join method of [SequentialRange]. func (addr *IPv6Address) SpanWithPrefixBlocksTo(other *IPv6Address) []*IPv6Address { return cloneToIPv6Addrs( getSpanningPrefixBlocks( wrapIPAddress(addr.ToIP()), wrapIPAddress(other.ToIP()), ), ) } // SpanWithSequentialBlocks produces the smallest slice of sequential blocks that cover the same set of addresses as this subnet. // // This slice can be shorter than that produced by SpanWithPrefixBlocks and is never longer. // // Unlike SpanWithSequentialBlocksTo, this method only includes addresses that are a part of this subnet. func (addr *IPv6Address) SpanWithSequentialBlocks() []*IPv6Address { if addr.IsSequential() { return []*IPv6Address{addr} } wrapped := wrapIPAddress(addr.ToIP()) return cloneToIPv6Addrs(spanWithSequentialBlocks(wrapped)) } // SpanWithSequentialBlocksTo produces the smallest slice of sequential block subnets that span all values from this subnet to the given subnet. // The span will cover all addresses in both subnets and everything in between. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. func (addr *IPv6Address) SpanWithSequentialBlocksTo(other *IPv6Address) []*IPv6Address { return cloneToIPv6Addrs( getSpanningSequentialBlocks( wrapIPAddress(addr.ToIP()), wrapIPAddress(other.ToIP()), ), ) } // CoverWithPrefixBlockTo returns the minimal-size prefix block that covers all the addresses spanning from this subnet to the given subnet. func (addr *IPv6Address) CoverWithPrefixBlockTo(other *IPv6Address) *IPv6Address { return addr.init().coverWithPrefixBlockTo(other.ToIP()).ToIPv6() } // CoverWithPrefixBlock returns the minimal-size prefix block that covers all the addresses in this subnet. // The resulting block will have a larger subnet size than this, unless this subnet is already a prefix block. func (addr *IPv6Address) CoverWithPrefixBlock() *IPv6Address { return addr.init().coverWithPrefixBlock().ToIPv6() } // MergeToSequentialBlocks merges this with the list of addresses to produce the smallest array of sequential blocks. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. func (addr *IPv6Address) MergeToSequentialBlocks(addrs ...*IPv6Address) []*IPv6Address { series := cloneIPv6Addrs(addr, addrs) blocks := getMergedSequentialBlocks(series) return cloneToIPv6Addrs(blocks) } // MergeToPrefixBlocks merges this subnet with the list of subnets to produce the smallest array of prefix blocks. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. func (addr *IPv6Address) MergeToPrefixBlocks(addrs ...*IPv6Address) []*IPv6Address { series := cloneIPv6Addrs(addr, addrs) blocks := getMergedPrefixBlocks(series) return cloneToIPv6Addrs(blocks) } // ReverseBytes returns a new address with the bytes reversed. Any prefix length is dropped. // // If the bytes within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, then this returns an error. // // In practice this means that to be reversible, a segment range must include all values except possibly the largest and/or smallest, which reverse to themselves. func (addr *IPv6Address) ReverseBytes() (*IPv6Address, addrerr.IncompatibleAddressError) { res, err := addr.GetSection().ReverseBytes() if err != nil { return nil, err } return addr.checkIdentity(res), nil } // ReverseBits returns a new address with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a segment range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (addr *IPv6Address) ReverseBits(perByte bool) (*IPv6Address, addrerr.IncompatibleAddressError) { res, err := addr.GetSection().ReverseBits(perByte) if err != nil { return nil, err } return addr.checkIdentity(res), nil } // ReverseSegments returns a new address with the segments reversed. func (addr *IPv6Address) ReverseSegments() *IPv6Address { return addr.checkIdentity(addr.GetSection().ReverseSegments()) } // ReplaceLen replaces segments starting from startIndex and ending before endIndex with the same number of segments starting at replacementStartIndex from the replacement section. // Mappings to or from indices outside the range of this or the replacement address are skipped. func (addr *IPv6Address) ReplaceLen(startIndex, endIndex int, replacement *IPv6Address, replacementIndex int) *IPv6Address { if replacementIndex <= 0 { startIndex -= replacementIndex replacementIndex = 0 } else if replacementIndex >= IPv6SegmentCount { return addr } // We must do a 1 to 1 adjustment of indices before calling the section replace which would do an adjustment of indices not 1 to 1. // Here we assume replacementIndex is 0 and working on the subsection starting at that index. // In other words, a replacementIndex of x on the whole section is equivalent to replacementIndex of 0 on the shorter subsection starting at x. // Then afterwards we use the original replacement index to work on the whole section again, adjusting as needed. startIndex, endIndex, replacementIndexAdjustment := adjust1To1Indices(startIndex, endIndex, IPv6SegmentCount, IPv6SegmentCount-replacementIndex) if startIndex == endIndex { return addr } replacementIndex += replacementIndexAdjustment count := endIndex - startIndex return addr.init().checkIdentity(addr.GetSection().ReplaceLen(startIndex, endIndex, replacement.GetSection(), replacementIndex, replacementIndex+count)) } // Replace replaces segments starting from startIndex with segments from the replacement section. func (addr *IPv6Address) Replace(startIndex int, replacement *IPv6AddressSection) *IPv6Address { // We must do a 1 to 1 adjustment of indices before calling the section replace which would do an adjustment of indices not 1 to 1. startIndex, endIndex, replacementIndex := adjust1To1Indices(startIndex, startIndex+replacement.GetSegmentCount(), IPv6SegmentCount, replacement.GetSegmentCount()) count := endIndex - startIndex return addr.init().checkIdentity(addr.GetSection().ReplaceLen(startIndex, endIndex, replacement, replacementIndex, replacementIndex+count)) } // GetLeadingBitCount returns the number of consecutive leading one or zero bits. // If ones is true, returns the number of consecutive leading one bits. // Otherwise, returns the number of consecutive leading zero bits. // // This method applies to the lower value of the range if this is a subnet representing multiple values. func (addr *IPv6Address) GetLeadingBitCount(ones bool) BitCount { return addr.init().getLeadingBitCount(ones) } // GetTrailingBitCount returns the number of consecutive trailing one or zero bits. // If ones is true, returns the number of consecutive trailing zero bits. // Otherwise, returns the number of consecutive trailing one bits. // // This method applies to the lower value of the range if this is a subnet representing multiple values. func (addr *IPv6Address) GetTrailingBitCount(ones bool) BitCount { return addr.init().getTrailingBitCount(ones) } // GetNetwork returns the singleton IPv6 network instance. func (addr *IPv6Address) GetNetwork() IPAddressNetwork { return ipv6Network } // IsEUI64 returns whether this address is consistent with EUI64, // which means the 12th and 13th bytes of the address match 0xff and 0xfe. func (addr *IPv6Address) IsEUI64() bool { return addr.GetSegment(6).MatchesWithPrefixMask(0xfe00, 8) && addr.GetSegment(5).MatchesWithMask(0xff, 0xff) } // ToEUI converts to the associated MACAddress. // An error is returned if the 0xfffe pattern is missing in segments 5 and 6, // or if an IPv6 segment's range of values cannot be split into two ranges of values. func (addr *IPv6Address) ToEUI(extended bool) (*MACAddress, addrerr.IncompatibleAddressError) { segs, err := addr.toEUISegments(extended) if err != nil { return nil, err } sect := newMACSectionEUI(segs) return newMACAddress(sect), nil } //prefix length in this section is ignored when converting to MAC. func (addr *IPv6Address) toEUISegments(extended bool) ([]*AddressDivision, addrerr.IncompatibleAddressError) { seg1 := addr.GetSegment(5) seg2 := addr.GetSegment(6) if !seg1.MatchesWithMask(0xff, 0xff) || !seg2.MatchesWithPrefixMask(0xfe00, 8) { return nil, &incompatibleAddressError{addressError{key: "ipaddress.mac.error.not.eui.convertible"}} } macStartIndex := 0 var macSegCount int if extended { macSegCount = ExtendedUniqueIdentifier64SegmentCount } else { macSegCount = ExtendedUniqueIdentifier48SegmentCount } newSegs := createSegmentArray(macSegCount) seg0 := addr.GetSegment(4) if err := seg0.splitIntoMACSegments(newSegs, macStartIndex); err != nil { return nil, err } //toggle the u/l bit macSegment0 := newSegs[0].ToMAC() lower0 := macSegment0.GetSegmentValue() upper0 := macSegment0.GetUpperSegmentValue() mask2ndBit := SegInt(0x2) if !macSegment0.MatchesWithMask(mask2ndBit&lower0, mask2ndBit) { // ensures that bit remains constant return nil, &incompatibleAddressError{addressError{key: "ipaddress.mac.error.not.eui.convertible"}} } lower0 ^= mask2ndBit //flip the universal/local bit upper0 ^= mask2ndBit newSegs[0] = NewMACRangeSegment(MACSegInt(lower0), MACSegInt(upper0)).ToDiv() macStartIndex += 2 if err := seg1.splitIntoMACSegments(newSegs, macStartIndex); err != nil { //a ff fe b return nil, err } if extended { macStartIndex += 2 if err := seg2.splitIntoMACSegments(newSegs, macStartIndex); err != nil { return nil, err } } else { first := newSegs[macStartIndex] if err := seg2.splitIntoMACSegments(newSegs, macStartIndex); err != nil { return nil, err } newSegs[macStartIndex] = first } macStartIndex += 2 seg3 := addr.GetSegment(7) if err := seg3.splitIntoMACSegments(newSegs, macStartIndex); err != nil { return nil, err } return newSegs, nil } // Format implements [fmt.Formatter] interface. It accepts the formats // - 'v' for the default address and section format (either the normalized or canonical string), // - 's' (string) for the same, // - 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix), // - 'd' (decimal), 'x' (lowercase hexadecimal), and // - 'X' (uppercase hexadecimal). // Also supported are some of fmt's format flags for integral types. // Sign control is not supported since addresses and sections are never negative. // '#' for an alternate format is supported, which adds a leading zero for octal, and for hexadecimal it adds // a leading "0x" or "0X" for "%#x" and "%#X" respectively. // Also supported is specification of minimum digits precision, output field width, // space or zero padding, and '-' for left or right justification. func (addr IPv6Address) Format(state fmt.State, verb rune) { addr.init().format(state, verb) } // String implements the [fmt.Stringer] interface, returning the canonical string provided by ToCanonicalString, or "" if the receiver is a nil pointer. func (addr *IPv6Address) String() string { if addr == nil { return nilString() } return addr.init().addressInternal.toString() } // GetSegmentStrings returns a slice with the string for each segment being the string that is normalized with wildcards. func (addr *IPv6Address) GetSegmentStrings() []string { if addr == nil { return nil } return addr.init().getSegmentStrings() } // ToCanonicalString produces a canonical string for the address. // // For IPv6, RFC 5952 describes canonical string representation. // https://en.wikipedia.org/wiki/IPv6_address#Representation // http://tools.ietf.org/html/rfc5952 // // Each address has a unique canonical string, not counting the prefix length. // With IP addresses, the prefix length can cause two equal addresses to have different strings, for example "1.2.3.4/16" and "1.2.3.4". // It can also cause two different addresses to have the same string, such as "1.2.0.0/16" for the individual address "1.2.0.0" and also the prefix block "1.2.*.*". // Use ToCanonicalWildcardString for a unique string for each IP address and subnet. func (addr *IPv6Address) ToCanonicalString() string { if addr == nil { return nilString() } return addr.init().toCanonicalString() } // ToNormalizedString produces a normalized string for the address. // // For IPv6, it differs from the canonical string. Zero-segments are not compressed. // // Each address has a unique normalized string, not counting the prefix length. // With IP addresses, the prefix length can cause two equal addresses to have different strings, for example "1.2.3.4/16" and "1.2.3.4". // It can also cause two different addresses to have the same string, such as "1.2.0.0/16" for the individual address "1.2.0.0" and also the prefix block "1.2.*.*". // Use the method ToNormalizedWildcardString for a unique string for each IP address and subnet. func (addr *IPv6Address) ToNormalizedString() string { if addr == nil { return nilString() } return addr.init().toNormalizedString() } // ToCompressedString produces a short representation of this address while remaining within the confines of standard representation(s) of the address. // // For IPv6, it differs from the canonical string. It compresses the maximum number of zeros and/or host segments with the IPv6 compression notation '::'. func (addr *IPv6Address) ToCompressedString() string { if addr == nil { return nilString() } return addr.init().toCompressedString() } // ToCanonicalWildcardString produces a string similar to the canonical string and avoids the CIDR prefix length. // Addresses and subnets with a network prefix length will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix length notation. // IPv6 addresses will be compressed according to the canonical representation. func (addr *IPv6Address) ToCanonicalWildcardString() string { if addr == nil { return nilString() } return addr.init().toCanonicalWildcardString() } // ToNormalizedWildcardString produces a string similar to the normalized string but avoids the CIDR prefix length. // CIDR addresses will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix notation. func (addr *IPv6Address) ToNormalizedWildcardString() string { if addr == nil { return nilString() } return addr.init().toNormalizedWildcardString() } // ToSegmentedBinaryString writes this address as segments of binary values preceded by the "0b" prefix. func (addr *IPv6Address) ToSegmentedBinaryString() string { if addr == nil { return nilString() } return addr.init().toSegmentedBinaryString() } // ToSQLWildcardString create a string similar to that from toNormalizedWildcardString except that // it uses SQL wildcards. It uses '%' instead of '*' and also uses the ending single-digit wildcard '_'. func (addr *IPv6Address) ToSQLWildcardString() string { if addr == nil { return nilString() } return addr.init().toSQLWildcardString() } // ToFullString produces a string with no compressed segments and all segments of full length with leading zeros, // which is 4 characters for IPv6 segments. func (addr *IPv6Address) ToFullString() string { if addr == nil { return nilString() } return addr.init().toFullString() } // ToPrefixLenString returns a string with a CIDR network prefix length if this address has a network prefix length. // For IPv6, a zero host section will be compressed with "::". For IPv4 the string is equivalent to the canonical string. func (addr *IPv6Address) ToPrefixLenString() string { if addr == nil { return nilString() } return addr.init().toPrefixLenString() } // ToSubnetString produces a string with specific formats for subnets. // The subnet string looks like "1.2.*.*" or "1:2::/16". // // In the case of IPv6, when a network prefix has been supplied, the prefix will be shown and the host section will be compressed with "::". func (addr *IPv6Address) ToSubnetString() string { if addr == nil { return nilString() } return addr.init().toSubnetString() } // ToCompressedWildcardString produces a string similar to ToNormalizedWildcardString, avoiding the CIDR prefix, but with full IPv6 segment compression as well, including single zero-segments. func (addr *IPv6Address) ToCompressedWildcardString() string { if addr == nil { return nilString() } return addr.init().toCompressedWildcardString() } // ToReverseDNSString generates the reverse-DNS lookup string, // returning an error if this address is a multiple-valued subnet for which the range cannot be represented. // For "2001:db8::567:89ab" it is "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa". func (addr *IPv6Address) ToReverseDNSString() (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toReverseDNSString() } // ToHexString writes this address as a single hexadecimal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // If a subnet cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *IPv6Address) ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toHexString(with0xPrefix) } // ToOctalString writes this address as a single octal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0" prefix. // // If a subnet cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *IPv6Address) ToOctalString(with0Prefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toOctalString(with0Prefix) } // ToBinaryString writes this address as a single binary value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0b" prefix. // // If a subnet cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *IPv6Address) ToBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toBinaryString(with0bPrefix) } // ToUNCHostName Generates the Microsoft UNC path component for this address. For examples see https://ipv6-literal.com/ // // For IPv6, it is the canonical string but with colons replaced by dashes, percent signs with the letter “s”, and then appended with the root domain ".ipv6-literal.net". func (addr *IPv6Address) ToUNCHostName() string { if addr == nil { return nilString() } cache := addr.getStringCache() if cache == nil { res, _ := addr.GetSection().toCustomString(uncParams, addr.zone) return res } var cacheField **string cacheField = &cache.uncString return cacheStr(cacheField, func() string { res, _ := addr.GetSection().toCustomString(uncParams, addr.zone) return res }) } // ToBase85String creates the base 85 string, which is described by RFC 1924, "A Compact Representation of IPv6 Addresses". // See https://www.rfc-editor.org/rfc/rfc1924.html // // It may be written as a range of two values if a range that is not a prefixed block. // // If a subnet cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *IPv6Address) ToBase85String() (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.GetSection().toBase85String(addr.zone) } var cacheField **string cacheField = &cache.base85String return cacheStrErr(cacheField, func() (string, addrerr.IncompatibleAddressError) { return addr.GetSection().toBase85String(addr.zone) }) } return addr.GetSection().ToBase85String() } // ToMixedString produces the mixed IPv6/IPv4 string. It is the shortest such string (ie fully compressed). // For some address sections with ranges of values in the IPv4 part of the address, there is not mixed string, and an error is returned. func (addr *IPv6Address) ToMixedString() (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } if addr.hasZone() { cache := addr.getStringCache() if cache == nil { return addr.GetSection().toMixedStringZoned(addr.zone) } var cacheField **string cacheField = &cache.mixedString return cacheStrErr(cacheField, func() (string, addrerr.IncompatibleAddressError) { return addr.GetSection().toMixedStringZoned(addr.zone) }) } return addr.GetSection().toMixedString() } // ToCustomString creates a customized string from this address or subnet according to the given string option parameters. // // Errors can result from split digits with ranged values, or mixed IPv4/v6 with ranged values, when a range cannot be split up. // Options without split digits or mixed addresses do not produce errors. // Single addresses do not produce errors. func (addr *IPv6Address) ToCustomString(stringOptions addrstr.IPv6StringOptions) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.GetSection().toCustomString(stringOptions, addr.zone) } func (addr *IPv6Address) toMaxLower() *IPv6Address { return addr.init().addressInternal.toMaxLower().ToIPv6() } func (addr *IPv6Address) toMinUpper() *IPv6Address { return addr.init().addressInternal.toMinUpper().ToIPv6() } // ToAddressBase converts to an Address, a polymorphic type usable with all addresses and subnets. // Afterwards, you can convert back with ToIPv6. // // ToAddressBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *IPv6Address) ToAddressBase() *Address { return addr.ToIP().ToAddressBase() } // ToIP converts to an IPAddress, a polymorphic type usable with all IP addresses and subnets. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *IPv6Address) ToIP() *IPAddress { if addr != nil { addr = addr.init() } return (*IPAddress)(addr) } // Wrap wraps this IP address, returning a WrappedIPAddress, an implementation of ExtendedIPSegmentSeries, // which can be used to write code that works with both IP addresses and IP address sections. // Wrap can be called with a nil receiver, wrapping a nil address. func (addr *IPv6Address) Wrap() WrappedIPAddress { return wrapIPAddress(addr.ToIP()) } // WrapAddress wraps this IP address, returning a WrappedAddress, an implementation of ExtendedSegmentSeries, // which can be used to write code that works with both addresses and address sections. // WrapAddress can be called with a nil receiver, wrapping a nil address. func (addr *IPv6Address) WrapAddress() WrappedAddress { return wrapAddress(addr.ToAddressBase()) } // ToKey creates the associated address key. // While addresses can be compared with the Compare, TrieCompare or Equal methods as well as various provided instances of AddressComparator, // they are not comparable with Go operators. // However, AddressKey instances are comparable with Go operators, and thus can be used as map keys. func (addr *IPv6Address) ToKey() IPv6AddressKey { addr = addr.init() key := IPv6AddressKey{} addr.toIPv6Key(&key.keyContents) return key } // ToGenericKey produces a generic Key[*IPv6Address] that can be used with generic code working with [Address], [IPAddress], [IPv4Address], [IPv6Address] and [MACAddress]. // ToKey produces a more compact key for code that is IPv6-specific. func (addr *IPv6Address) ToGenericKey() Key[*IPv6Address] { // Note: We intentionally do not populate the "scheme" field, which is used with Key[*Address] and Key[*IPAddress] and 64-bit Key[*MACAddress]. // With Key[*IPv6Address], by leaving the scheme zero, the zero Key[*IPv6Address] matches up with the key produced here by the zero address. // We do not need the scheme field for Key[*IPv6Address] since the generic type indicates IPv6. key := Key[*IPv6Address]{} addr.init().toIPv6Key(&key.keyContents) return key } func (addr *IPv6Address) fromKey(_ addressScheme, key *keyContents) *IPv6Address { // See ToGenericKey for details such as the fact that the scheme is ignored return fromIPv6IPKey(key) } func (addr *IPv6Address) toIPv6Key(contents *keyContents) { contents.zone = addr.GetZone() section := addr.GetSection() divs := section.getDivArray() if addr.IsMultiple() { for i, div := range divs { seg := div.ToIPv6() val := &contents.vals[i>>2] val.lower = (val.lower << IPv6BitsPerSegment) | uint64(seg.GetIPv6SegmentValue()) val.upper = (val.upper << IPv6BitsPerSegment) | uint64(seg.GetIPv6UpperSegmentValue()) } } else { for i, div := range divs { seg := div.ToIPv6() val := &contents.vals[i>>2] newLower := (val.lower << IPv6BitsPerSegment) | uint64(seg.GetIPv6SegmentValue()) val.lower = newLower val.upper = newLower } } } func fromIPv6Key(key IPv6AddressKey) *IPv6Address { return fromIPv6IPKey(&key.keyContents) } func fromIPv6IPKey(contents *keyContents) *IPv6Address { return NewIPv6AddressFromZonedRange( func(segmentIndex int) IPv6SegInt { valsIndex := segmentIndex >> 2 segIndex := ((IPv6SegmentCount - 1) - segmentIndex) & 0x3 return IPv6SegInt(contents.vals[valsIndex].lower >> (segIndex << ipv6BitsToSegmentBitshift)) }, func(segmentIndex int) IPv6SegInt { valsIndex := segmentIndex >> 2 segIndex := ((IPv6SegmentCount - 1) - segmentIndex) & 0x3 return IPv6SegInt(contents.vals[valsIndex].upper >> (segIndex << ipv6BitsToSegmentBitshift)) }, string(contents.zone)) } ipaddress-go-1.5.4/ipaddr/ipv6section.go000066400000000000000000002671261440250641600201650ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math/big" "math/bits" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) func createIPv6Section(segments []*AddressDivision) *IPv6AddressSection { return &IPv6AddressSection{ ipAddressSectionInternal{ addressSectionInternal{ addressDivisionGroupingInternal{ addressDivisionGroupingBase: addressDivisionGroupingBase{ divisions: standardDivArray(segments), addrType: ipv6Type, cache: &valueCache{ stringCache: stringCache{ ipv6StringCache: &ipv6StringCache{}, ipStringCache: &ipStringCache{}, }, }, }, }, }, }, } } func newIPv6Section(segments []*AddressDivision) *IPv6AddressSection { return createIPv6Section(segments) } func newIPv6SectionParsed(segments []*AddressDivision, isMultiple bool) (res *IPv6AddressSection) { res = createIPv6Section(segments) res.isMult = isMultiple return } func newIPv6SectionFromMixed(segments []*AddressDivision) (res *IPv6AddressSection) { res = createIPv6Section(segments) res.initMultiple() return } func newPrefixedIPv6SectionParsed(segments []*AddressDivision, isMultiple bool, prefixLength PrefixLen, singleOnly bool) (res *IPv6AddressSection) { res = createIPv6Section(segments) res.isMult = isMultiple if prefixLength != nil { assignPrefix(prefixLength, segments, res.ToIP(), singleOnly, false, BitCount(len(segments)<>1, nil, false) return res } // NewIPv6SectionFromSegmentedBytes constructs an IPv6 address from the given byte slice. // It allows you to specify the segment count for the supplied bytes. // If the slice is too large for the given number of segments, an error is returned, although leading zeros are tolerated. func NewIPv6SectionFromSegmentedBytes(bytes []byte, segmentCount int) (res *IPv6AddressSection, err addrerr.AddressValueError) { return newIPv6SectionFromBytes(bytes, segmentCount, nil, false) } // NewIPv6SectionFromPrefixedBytes constructs an IPv6 address or prefix block from the given byte slice and prefix length. // It allows you to specify the segment count for the supplied bytes. // If the slice is too large for the given number of segments, an error is returned, although leading zeros are tolerated. func NewIPv6SectionFromPrefixedBytes(bytes []byte, segmentCount int, prefixLength PrefixLen) (res *IPv6AddressSection, err addrerr.AddressValueError) { return newIPv6SectionFromBytes(bytes, segmentCount, prefixLength, false) } func newIPv6SectionFromBytes(bytes []byte, segmentCount int, prefixLength PrefixLen, singleOnly bool) (res *IPv6AddressSection, err addrerr.AddressValueError) { if segmentCount < 0 { segmentCount = (len(bytes) + 1) >> 1 } expectedByteCount := segmentCount << 1 segments, err := toSegments( bytes, segmentCount, IPv6BytesPerSegment, IPv6BitsPerSegment, ipv6Network.getIPAddressCreator(), prefixLength) if err == nil { res = createIPv6Section(segments) if prefixLength != nil { assignPrefix(prefixLength, segments, res.ToIP(), singleOnly, false, BitCount(segmentCount< 0 { bytes = cloneBytes(bytes) res.cache.bytesCache = &bytesCache{lowerBytes: bytes} if !res.isMult { // not a prefix block res.cache.bytesCache.upperBytes = bytes } } } return } func newIPv6SectionFromWords(words []big.Word, segmentCount int, prefixLength PrefixLen, singleOnly bool) (res *IPv6AddressSection, err addrerr.AddressValueError) { if segmentCount < 0 { wordBitSize := bits.UintSize segmentCount = (len(words) * wordBitSize) >> 4 } segments, err := toSegmentsFromWords( words, segmentCount, prefixLength) if err == nil { res = createIPv6Section(segments) if prefixLength != nil { assignPrefix(prefixLength, segments, res.ToIP(), singleOnly, false, BitCount(segmentCount<> ipv6BitsToSegmentBitshift segments = createSegmentArray(segmentCount) var currentWord big.Word if wordLen > 0 { currentWord = words[0] } // start with little end for wordIndex, wordSegmentIndex, segmentIndex := 0, 0, segmentCount-1; ; segmentIndex-- { var value IPv6SegInt if wordIndex < wordLen { value = IPv6SegInt(currentWord) currentWord >>= uint(IPv6BitsPerSegment) wordSegmentIndex++ } segmentPrefixLength := getSegmentPrefixLength(IPv6BitsPerSegment, prefixLength, segmentIndex) seg := NewIPv6PrefixedSegment(value, segmentPrefixLength) segments[segmentIndex] = seg.ToDiv() if wordSegmentIndex == segmentsPerWord { wordSegmentIndex = 0 wordIndex++ if wordIndex < wordLen { currentWord = words[wordIndex] } } if segmentIndex == 0 { // any remaining words should be zero var isErr bool if isErr = currentWord != 0; !isErr { for wordIndex++; wordIndex < wordLen; wordIndex++ { if isErr = words[wordIndex] != 0; isErr { break } } } if isErr { err = &addressValueError{ addressError: addressError{key: "ipaddress.error.exceeds.size"}, val: int(words[wordIndex]), } } break } } return } // NewIPv6SectionFromUint64 constructs an IPv6 address section of the given segment count from the given values. func NewIPv6SectionFromUint64(highBytes, lowBytes uint64, segmentCount int) (res *IPv6AddressSection) { return NewIPv6SectionFromPrefixedUint64(highBytes, lowBytes, segmentCount, nil) } // NewIPv6SectionFromPrefixedUint64 constructs an IPv6 address or prefix block section of the given segment count from the given values and prefix length. func NewIPv6SectionFromPrefixedUint64(highBytes, lowBytes uint64, segmentCount int, prefixLength PrefixLen) (res *IPv6AddressSection) { if segmentCount < 0 { segmentCount = IPv6SegmentCount } segments := createSegmentsUint64( segmentCount, highBytes, lowBytes, IPv6BytesPerSegment, IPv6BitsPerSegment, ipv6Network.getIPAddressCreator(), prefixLength) res = createIPv6Section(segments) if prefixLength != nil { assignPrefix(prefixLength, segments, res.ToIP(), false, false, BitCount(segmentCount<= 1 return -1 } return section.compareSize(other) } // GetIPVersion returns IPv6, the IP version of this address section. func (section *IPv6AddressSection) GetIPVersion() IPVersion { return IPv6 } // GetBitsPerSegment returns the number of bits comprising each segment in this section. Segments in the same address section are equal length. func (section *IPv6AddressSection) GetBitsPerSegment() BitCount { return IPv6BitsPerSegment } // GetBytesPerSegment returns the number of bytes comprising each segment in this section. Segments in the same address section are equal length. func (section *IPv6AddressSection) GetBytesPerSegment() int { return IPv6BytesPerSegment } // GetCount returns the count of possible distinct values for this item. // If not representing multiple values, the count is 1, // unless this is a division grouping with no divisions, or an address section with no segments, in which case it is 0. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (section *IPv6AddressSection) GetCount() *big.Int { if section == nil { return bigZero() } return section.cacheCount(func() *big.Int { return count(func(index int) uint64 { return section.GetSegment(index).GetValueCount() }, section.GetSegmentCount(), 2, 0x7fffffffffff) }) } func (section *IPv6AddressSection) getCachedCount() *big.Int { if section == nil { return bigZero() } return section.cachedCount(func() *big.Int { return count(func(index int) uint64 { return section.GetSegment(index).GetValueCount() }, section.GetSegmentCount(), 2, 0x7fffffffffff) }) } // IsMultiple returns whether this section represents multiple values. func (section *IPv6AddressSection) IsMultiple() bool { return section != nil && section.isMultiple() } // IsPrefixed returns whether this section has an associated prefix length. func (section *IPv6AddressSection) IsPrefixed() bool { return section != nil && section.isPrefixed() } // GetBlockCount returns the count of distinct values in the given number of initial (more significant) segments. func (section *IPv6AddressSection) GetBlockCount(segments int) *big.Int { return section.calcCount(func() *big.Int { return count(func(index int) uint64 { return section.GetSegment(index).GetValueCount() }, segments, 2, 0x7fffffffffff) }) } // GetPrefixCount returns the number of distinct prefix values in this item. // // The prefix length is given by GetPrefixLen. // // If this has a non-nil prefix length, returns the number of distinct prefix values. // // If this has a nil prefix length, returns the same value as GetCount. func (section *IPv6AddressSection) GetPrefixCount() *big.Int { return section.cachePrefixCount(func() *big.Int { return section.GetPrefixCountLen(section.getPrefixLen().bitCount()) }) } // GetPrefixCountLen returns the number of distinct prefix values in this item for the given prefix length. func (section *IPv6AddressSection) GetPrefixCountLen(prefixLen BitCount) *big.Int { if prefixLen <= 0 { return bigOne() } else if bc := section.GetBitCount(); prefixLen >= bc { return section.GetCount() } networkSegmentIndex := getNetworkSegmentIndex(prefixLen, section.GetBytesPerSegment(), section.GetBitsPerSegment()) hostSegmentIndex := getHostSegmentIndex(prefixLen, section.GetBytesPerSegment(), section.GetBitsPerSegment()) return section.calcCount(func() *big.Int { return count(func(index int) uint64 { if (networkSegmentIndex == hostSegmentIndex) && index == networkSegmentIndex { return section.GetSegment(index).GetPrefixValueCount() } return section.GetSegment(index).GetValueCount() }, networkSegmentIndex+1, 2, 0x7fffffffffff) }) } // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. func (section *IPv6AddressSection) GetSegment(index int) *IPv6AddressSegment { return section.getDivision(index).ToIPv6() } // ForEachSegment visits each segment in order from most-significant to least, the most significant with index 0, calling the given function for each, terminating early if the function returns true. // Returns the number of visited segments. func (section *IPv6AddressSection) ForEachSegment(consumer func(segmentIndex int, segment *IPv6AddressSegment) (stop bool)) int { divArray := section.getDivArray() if divArray != nil { for i, div := range divArray { if consumer(i, div.ToIPv6()) { return i + 1 } } } return len(divArray) } // GetTrailingSection gets the subsection from the series starting from the given index. // The first segment is at index 0. func (section *IPv6AddressSection) GetTrailingSection(index int) *IPv6AddressSection { return section.GetSubSection(index, section.GetSegmentCount()) } // GetSubSection gets the subsection from the series starting from the given index and ending just before the give endIndex. // The first segment is at index 0. func (section *IPv6AddressSection) GetSubSection(index, endIndex int) *IPv6AddressSection { return section.getSubSection(index, endIndex).ToIPv6() } // GetNetworkSection returns a subsection containing the segments with the network bits of the section. // The returned section will have only as many segments as needed as determined by the existing CIDR network prefix length. // // If this series has no CIDR prefix length, the returned network section will // be the entire series as a prefixed section with prefix length matching the address bit length. func (section *IPv6AddressSection) GetNetworkSection() *IPv6AddressSection { return section.getNetworkSection().ToIPv6() } // GetNetworkSectionLen returns a subsection containing the segments with the network of the address section, the prefix bits according to the given prefix length. // The returned section will have only as many segments as needed to contain the network. // // The new section will be assigned the given prefix length, // unless the existing prefix length is smaller, in which case the existing prefix length will be retained. func (section *IPv6AddressSection) GetNetworkSectionLen(prefLen BitCount) *IPv6AddressSection { return section.getNetworkSectionLen(prefLen).ToIPv6() } // GetHostSection returns a subsection containing the segments with the host of the address section, the bits beyond the CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. // // If this series has no prefix length, the returned host section will be the full section. func (section *IPv6AddressSection) GetHostSection() *IPv6AddressSection { return section.getHostSection().ToIPv6() } // GetHostSectionLen returns a subsection containing the segments with the host of the address section, the bits beyond the given CIDR network prefix length. // The returned section will have only as many segments as needed to contain the host. // The returned section will have an assigned prefix length indicating the beginning of the host. func (section *IPv6AddressSection) GetHostSectionLen(prefLen BitCount) *IPv6AddressSection { return section.getHostSectionLen(prefLen).ToIPv6() } // GetNetworkMask returns the network mask associated with the CIDR network prefix length of this address section. // If this section has no prefix length, then the all-ones mask is returned. func (section *IPv6AddressSection) GetNetworkMask() *IPv6AddressSection { return section.getNetworkMask(ipv6Network).ToIPv6() } // GetHostMask returns the host mask associated with the CIDR network prefix length of this address section. // If this section has no prefix length, then the all-ones mask is returned. func (section *IPv6AddressSection) GetHostMask() *IPv6AddressSection { return section.getHostMask(ipv6Network).ToIPv6() } // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (section *IPv6AddressSection) CopySubSegments(start, end int, segs []*IPv6AddressSegment) (count int) { start, end, targetStart := adjust1To1StartIndices(start, end, section.GetDivisionCount(), len(segs)) segs = segs[targetStart:] return section.forEachSubDivision(start, end, func(index int, div *AddressDivision) { segs[index] = div.ToIPv6() }, len(segs)) } // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. func (section *IPv6AddressSection) CopySegments(segs []*IPv6AddressSegment) (count int) { return section.ForEachSegment(func(index int, seg *IPv6AddressSegment) (stop bool) { if stop = index >= len(segs); !stop { segs[index] = seg } return }) } // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as this section. func (section *IPv6AddressSection) GetSegments() (res []*IPv6AddressSegment) { res = make([]*IPv6AddressSegment, section.GetSegmentCount()) section.CopySegments(res) return } // Mask applies the given mask to all address sections represented by this secction, returning the result. // // If the sections do not have a comparable number of segments, an error is returned. // // If this represents multiple addresses, and applying the mask to all addresses creates a set of addresses // that cannot be represented as a sequential range within each segment, then an error is returned. func (section *IPv6AddressSection) Mask(other *IPv6AddressSection) (res *IPv6AddressSection, err addrerr.IncompatibleAddressError) { return section.maskPrefixed(other, true) } func (section *IPv6AddressSection) maskPrefixed(other *IPv6AddressSection, retainPrefix bool) (res *IPv6AddressSection, err addrerr.IncompatibleAddressError) { sec, err := section.mask(other.ToIP(), retainPrefix) if err == nil { res = sec.ToIPv6() } return } // BitwiseOr does the bitwise disjunction with this address section, useful when subnetting. // It is similar to Mask which does the bitwise conjunction. // // The operation is applied to all individual addresses and the result is returned. // // If this represents multiple address sections, and applying the operation to all sections creates a set of sections // that cannot be represented as a sequential range within each segment, then an error is returned. func (section *IPv6AddressSection) BitwiseOr(other *IPv6AddressSection) (res *IPv6AddressSection, err addrerr.IncompatibleAddressError) { return section.bitwiseOrPrefixed(other, true) } func (section *IPv6AddressSection) bitwiseOrPrefixed(other *IPv6AddressSection, retainPrefix bool) (res *IPv6AddressSection, err addrerr.IncompatibleAddressError) { sec, err := section.bitwiseOr(other.ToIP(), retainPrefix) if err == nil { res = sec.ToIPv6() } return } // MatchesWithMask applies the mask to this address section and then compares the result with the given address section, // returning true if they match, false otherwise. To match, both the given section and mask must have the same number of segments as this section. func (section *IPv6AddressSection) MatchesWithMask(other *IPv6AddressSection, mask *IPv6AddressSection) bool { return section.matchesWithMask(other.ToIP(), mask.ToIP()) } // Subtract subtracts the given subnet sections from this subnet section, returning an array of sections for the result (the subnet sections will not be contiguous so an array is required). // // Subtract computes the subnet difference, the set of address sections in this address section but not in the provided section. // This is also known as the relative complement of the given argument in this subnet section. // // This is set subtraction, not subtraction of values. func (section *IPv6AddressSection) Subtract(other *IPv6AddressSection) (res []*IPv6AddressSection, err addrerr.SizeMismatchError) { sections, err := section.subtract(other.ToIP()) if err == nil { res = cloneTo(sections, (*IPAddressSection).ToIPv6) } return } // Intersect returns the subnet sections whose individual sections are found in both this and the given subnet section argument, or nil if no such sections exist. // // This is also known as the conjunction of the two sets of address sections. // // If the two sections have different segment counts, an error is returned. func (section *IPv6AddressSection) Intersect(other *IPv6AddressSection) (res *IPv6AddressSection, err addrerr.SizeMismatchError) { sec, err := section.intersect(other.ToIP()) if err == nil { res = sec.ToIPv6() } return } // GetLower returns the section in the range with the lowest numeric value, // which will be the same section if it represents a single value. // For example, for "1::1:2-3:4:5-6", the section "1::1:2:4:5" is returned. func (section *IPv6AddressSection) GetLower() *IPv6AddressSection { return section.getLower().ToIPv6() } // GetUpper returns the section in the range with the highest numeric value, // which will be the same section if it represents a single value. // For example, for "1::1:2-3:4:5-6", the section "1::1:3:4:6" is returned. func (section *IPv6AddressSection) GetUpper() *IPv6AddressSection { return section.getUpper().ToIPv6() } // ToZeroHost converts the address section to one in which all individual address sections have a host of zero, // the host being the bits following the prefix length. // If the address section has no prefix length, then it returns an all-zero address section. // // The returned section will have the same prefix and prefix length. // // This returns an error if the section is a range of address sections which cannot be converted to a range in which all sections have zero hosts, // because the conversion results in a segment that is not a sequential range of values. func (section *IPv6AddressSection) ToZeroHost() (*IPv6AddressSection, addrerr.IncompatibleAddressError) { res, err := section.toZeroHost(false) return res.ToIPv6(), err } // ToZeroHostLen converts the address section to one in which all individual sections have a host of zero, // the host being the bits following the given prefix length. // If this address section has the same prefix length, then the returned one will too, otherwise the returned section will have no prefix length. // // This returns an error if the section is a range of which cannot be converted to a range in which all sections have zero hosts, // because the conversion results in a segment that is not a sequential range of values. func (section *IPv6AddressSection) ToZeroHostLen(prefixLength BitCount) (*IPv6AddressSection, addrerr.IncompatibleAddressError) { res, err := section.toZeroHostLen(prefixLength) return res.ToIPv6(), err } // ToZeroNetwork converts the address section to one in which all individual address sections have a network of zero, // the network being the bits within the prefix length. // If the address section has no prefix length, then it returns an all-zero address section. // // The returned address section will have the same prefix length. func (section *IPv6AddressSection) ToZeroNetwork() *IPv6AddressSection { return section.toZeroNetwork().ToIPv6() } // ToMaxHost converts the address section to one in which all individual address sections have a host of all one-bits, the max value, // the host being the bits following the prefix length. // If the address section has no prefix length, then it returns an all-ones section, the max address section. // // The returned address section will have the same prefix and prefix length. // // This returns an error if the address section is a range of address sections which cannot be converted to a range in which all sections have max hosts, // because the conversion results in a segment that is not a sequential range of values. func (section *IPv6AddressSection) ToMaxHost() (*IPv6AddressSection, addrerr.IncompatibleAddressError) { res, err := section.toMaxHost() return res.ToIPv6(), err } // ToMaxHostLen converts the address section to one in which all individual address sections have a host of all one-bits, the max host, // the host being the bits following the given prefix length. // If this section has the same prefix length, then the resulting section will too, otherwise the resulting section will have no prefix length. // // For instance, the zero host of "1.2.3.4" for the prefix length of 16 is the address "1.2.255.255". // // This returns an error if the section is a range of address sections which cannot be converted to a range in which all address sections have max hosts, // because the conversion results in a segment that is not a sequential range of values. func (section *IPv6AddressSection) ToMaxHostLen(prefixLength BitCount) (*IPv6AddressSection, addrerr.IncompatibleAddressError) { res, err := section.toMaxHostLen(prefixLength) return res.ToIPv6(), err } // ToPrefixBlock returns the section with the same prefix as this section while the remaining bits span all values. // The returned section will be the block of all sections with the same prefix. // // If this section has no prefix, this section is returned. func (section *IPv6AddressSection) ToPrefixBlock() *IPv6AddressSection { return section.toPrefixBlock().ToIPv6() } // ToPrefixBlockLen returns the section with the same prefix of the given length as this section while the remaining bits span all values. // The returned section will be the block of all sections with the same prefix. func (section *IPv6AddressSection) ToPrefixBlockLen(prefLen BitCount) *IPv6AddressSection { return section.toPrefixBlockLen(prefLen).ToIPv6() } // ToBlock creates a new block of address sections by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (section *IPv6AddressSection) ToBlock(segmentIndex int, lower, upper SegInt) *IPv6AddressSection { return section.toBlock(segmentIndex, lower, upper).ToIPv6() } // WithoutPrefixLen provides the same address section but with no prefix length. The values remain unchanged. func (section *IPv6AddressSection) WithoutPrefixLen() *IPv6AddressSection { if !section.IsPrefixed() { return section } return section.withoutPrefixLen().ToIPv6() } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address section. // The provided prefix length will be adjusted to these boundaries if necessary. func (section *IPv6AddressSection) SetPrefixLen(prefixLen BitCount) *IPv6AddressSection { return section.setPrefixLen(prefixLen).ToIPv6() } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address section. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this address section has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this address section has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section *IPv6AddressSection) SetPrefixLenZeroed(prefixLen BitCount) (*IPv6AddressSection, addrerr.IncompatibleAddressError) { res, err := section.setPrefixLenZeroed(prefixLen) return res.ToIPv6(), err } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address section. // // If this address section has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (section *IPv6AddressSection) AdjustPrefixLen(prefixLen BitCount) *IPv6AddressSection { return section.adjustPrefixLen(prefixLen).ToIPv6() } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address section. // // If this address section has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section *IPv6AddressSection) AdjustPrefixLenZeroed(prefixLen BitCount) (*IPv6AddressSection, addrerr.IncompatibleAddressError) { res, err := section.adjustPrefixLenZeroed(prefixLen) return res.ToIPv6(), err } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this address section. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such address section - it is required that the range of values match the range of a prefix block. // If there is no such address section, then nil is returned. func (section *IPv6AddressSection) AssignPrefixForSingleBlock() *IPv6AddressSection { return section.assignPrefixForSingleBlock().ToIPv6() } // AssignMinPrefixForBlock returns an equivalent address section, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this address section. // // In other words, this method assigns a prefix length to this address section matching the largest prefix block in this address section. func (section *IPv6AddressSection) AssignMinPrefixForBlock() *IPv6AddressSection { return section.assignMinPrefixForBlock().ToIPv6() } // Iterator provides an iterator to iterate through the individual address sections of this address section. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual address sections. // // Call IsMultiple to determine if this instance represents multiple address sections, or GetCount for the count. func (section *IPv6AddressSection) Iterator() Iterator[*IPv6AddressSection] { if section == nil { return ipv6SectionIterator{nilSectIterator()} } return ipv6SectionIterator{section.sectionIterator(nil)} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this address section, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this address section. // // If the series has no prefix length, then this is equivalent to Iterator. func (section *IPv6AddressSection) PrefixIterator() Iterator[*IPv6AddressSection] { return ipv6SectionIterator{section.prefixIterator(false)} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address section. // Each iterated address section will be a prefix block with the same prefix length as this address section. // // If this address section has no prefix length, then this is equivalent to Iterator. func (section *IPv6AddressSection) PrefixBlockIterator() Iterator[*IPv6AddressSection] { return ipv6SectionIterator{section.prefixIterator(true)} } // BlockIterator Iterates through the address sections that can be obtained by iterating through all the upper segments up to the given segment count. // The segments following remain the same in all iterated sections. func (section *IPv6AddressSection) BlockIterator(segmentCount int) Iterator[*IPv6AddressSection] { return ipv6SectionIterator{section.blockIterator(segmentCount)} } // SequentialBlockIterator iterates through the sequential address sections that make up this address section. // // Practically, this means finding the count of segments for which the segments that follow are not full range, and then using BlockIterator with that segment count. // // Use GetSequentialBlockCount to get the number of iterated elements. func (section *IPv6AddressSection) SequentialBlockIterator() Iterator[*IPv6AddressSection] { return ipv6SectionIterator{section.sequentialBlockIterator()} } // GetZeroSegments returns the list of consecutive zero-segments. // Each element in the list will be an segment index and a total segment count for which // that count of consecutive segments starting from that index are all zero. func (section *IPv6AddressSection) GetZeroSegments() SegmentSequenceList { return section.getZeroSegments(false) } // GetZeroRangeSegments returns the list of consecutive zero and zero prefix block segments. // Each element in the list will be an segment index and a total segment count for which // that count of consecutive segments starting from that index are all zero or a prefix block segment with lowest segment value zero. func (section *IPv6AddressSection) GetZeroRangeSegments() SegmentSequenceList { if section.IsPrefixed() { return section.getZeroSegments(true) } return section.getZeroSegments(false) } // GetCompressIndexAndCount chooses a single segment to be compressed in an IPv6 string. If no segment could be chosen then count is 0. // If options is nil, no segment will be chosen. If createMixed is true, will assume the address string will be mixed IPv6/v4. func (section *IPv6AddressSection) getCompressIndexAndCount(options addrstr.CompressOptions, createMixed bool) (maxIndex, maxCount int) { if options != nil { rangeSelection := options.GetCompressionChoiceOptions() var compressibleSegs SegmentSequenceList if rangeSelection.CompressHost() { compressibleSegs = section.GetZeroRangeSegments() } else { compressibleSegs = section.GetZeroSegments() } maxCount = 0 segmentCount := section.GetSegmentCount() //compressMixed := createMixed && options.GetMixedCompressionOptions().compressMixed(section) compressMixed := createMixed && compressMixedSect(options.GetMixedCompressionOptions(), section) preferHost := rangeSelection == addrstr.HostPreferred preferMixed := createMixed && (rangeSelection == addrstr.MixedPreferred) for i := compressibleSegs.size() - 1; i >= 0; i-- { rng := compressibleSegs.getRange(i) index := rng.index count := rng.length if createMixed { //so here we shorten the range to exclude the mixed part if necessary mixedIndex := IPv6MixedOriginalSegmentCount if !compressMixed || index > mixedIndex || index+count < segmentCount { //range does not include entire mixed part. We never compress only part of a mixed part. //the compressible range must stop at the mixed part if val := mixedIndex - index; val < count { count = val } } } //select this range if is the longest if count > 0 && count >= maxCount && (options.CompressSingle() || count > 1) { maxIndex = index maxCount = count } if preferHost && section.IsPrefixed() && (BitCount(index+count)*section.GetBitsPerSegment()) > section.getNetworkPrefixLen().bitCount() { //this range contains the host //Since we are going backwards, this means we select as the maximum any zero-segment that includes the host break } if preferMixed && index+count >= segmentCount { //this range contains the mixed section //Since we are going backwards, this means we select to compress the mixed segment break } } } return } func compressMixedSect(m addrstr.MixedCompressionOptions, addressSection *IPv6AddressSection) bool { switch m { case addrstr.AllowMixedCompression: return true case addrstr.NoMixedCompression: return false case addrstr.MixedCompressionNoHost: return !addressSection.IsPrefixed() case addrstr.MixedCompressionCoveredByHost: if addressSection.IsPrefixed() { mixedDistance := IPv6MixedOriginalSegmentCount mixedCount := addressSection.GetSegmentCount() - mixedDistance if mixedCount > 0 { return (BitCount(mixedDistance) * addressSection.GetBitsPerSegment()) >= addressSection.getNetworkPrefixLen().bitCount() } } return true default: return true } } func (section *IPv6AddressSection) getZeroSegments(includeRanges bool) SegmentSequenceList { divisionCount := section.GetSegmentCount() includeRanges = includeRanges && section.IsPrefixBlock() && section.GetPrefixLen().bitCount() < section.GetBitCount() var currentIndex, currentCount, rangeCount int var ranges [IPv6SegmentCount >> 1]SegmentSequence if includeRanges { bitsPerSegment := section.GetBitsPerSegment() networkIndex := getNetworkSegmentIndex(section.getPrefixLen().bitCount(), section.GetBytesPerSegment(), bitsPerSegment) i := 0 for ; i <= networkIndex; i++ { division := section.GetSegment(i) isCompressible := division.IsZero() || (includeRanges && division.IsPrefixed() && division.isSinglePrefixBlock(0, division.getUpperDivisionValue(), division.getDivisionPrefixLength().bitCount())) if isCompressible { currentCount++ if currentCount == 1 { currentIndex = i } } else if currentCount > 0 { ranges[rangeCount] = SegmentSequence{index: currentIndex, length: currentCount} rangeCount++ currentCount = 0 } } if currentCount > 0 { // add all segments past the network segment index to the current sequence ranges[rangeCount] = SegmentSequence{index: currentIndex, length: currentCount + divisionCount - i} rangeCount++ } else if i < divisionCount { // all segments past the network segment index are a new sequence ranges[rangeCount] = SegmentSequence{index: i, length: divisionCount - i} rangeCount++ } // else the very last segment was a network segment, and a prefix block segment, but the lowest segment value is not zero, eg ::100/120 } else { for i := 0; i < divisionCount; i++ { division := section.GetSegment(i) if division.IsZero() { currentCount++ if currentCount == 1 { currentIndex = i } } else if currentCount > 0 { ranges[rangeCount] = SegmentSequence{index: currentIndex, length: currentCount} rangeCount++ currentCount = 0 } } if currentCount > 0 { ranges[rangeCount] = SegmentSequence{index: currentIndex, length: currentCount} rangeCount++ } else if rangeCount == 0 { return SegmentSequenceList{} } } return SegmentSequenceList{ranges[:rangeCount]} } // IncrementBoundary returns the item that is the given increment from the range boundaries of this item. // // If the given increment is positive, adds the value to the highest (GetUpper) in the range to produce a new item. // If the given increment is negative, adds the value to the lowest (GetLower) in the range to produce a new item. // If the increment is zero, returns this. // // If this represents just a single value, this item is simply incremented by the given increment value, positive or negative. // // On overflow or underflow, IncrementBoundary returns nil. func (section *IPv6AddressSection) IncrementBoundary(increment int64) *IPv6AddressSection { return section.incrementBoundary(increment).ToIPv6() } func getIPv6MaxValue(segmentCount int) *big.Int { return new(big.Int).Set(ipv6MaxValues[segmentCount]) } var ipv6MaxValues = []*big.Int{ bigZero(), new(big.Int).SetUint64(IPv6MaxValuePerSegment), new(big.Int).SetUint64(0xffffffff), new(big.Int).SetUint64(0xffffffffffff), maxInt(4), maxInt(5), maxInt(6), maxInt(7), maxInt(8), } func maxInt(segCount int) *big.Int { res := new(big.Int).SetUint64(1) return res.Lsh(res, 16*uint(segCount)).Sub(res, bigOneConst()) } // Increment returns the item that is the given increment upwards into the range, // with the increment of 0 returning the first in the range. // // If the increment i matches or exceeds the range count c, then i - c + 1 // is added to the upper item of the range. // An increment matching the count gives you the item just above the highest in the range. // // If the increment is negative, it is added to the lowest of the range. // To get the item just below the lowest of the range, use the increment -1. // // If this represents just a single value, the item is simply incremented by the given increment, positive or negative. // // If this item represents multiple values, a positive increment i is equivalent i + 1 values from the iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the count is equivalent to the same number of iterator values preceding the last value of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On overflow or underflow, Increment returns nil. func (section *IPv6AddressSection) Increment(increment int64) *IPv6AddressSection { if increment == 0 && !section.isMultiple() { return section } lowerValue := section.GetValue() upperValue := section.GetUpperValue() count := section.GetCount() var bigIncrement big.Int bigIncrement.SetInt64(increment) isOverflow := checkOverflowBig(increment, &bigIncrement, lowerValue, upperValue, count, func() *big.Int { return getIPv6MaxValue(section.GetSegmentCount()) }) if isOverflow { return nil } prefixLength := section.getPrefixLen() result := fastIncrement( section.ToSectionBase(), increment, ipv6Network.getIPAddressCreator(), section.getLower, section.getUpper, prefixLength) if result != nil { return result.ToIPv6() } bigIncrement.SetInt64(increment) return incrementBig( section.ToSectionBase(), increment, &bigIncrement, ipv6Network.getIPAddressCreator(), section.getLower, section.getUpper, prefixLength).ToIPv6() } // SpanWithPrefixBlocks returns an array of prefix blocks that spans the same set of individual address sections as this section. // // Unlike SpanWithPrefixBlocksTo, the result only includes blocks that are a part of this section. func (section *IPv6AddressSection) SpanWithPrefixBlocks() []*IPv6AddressSection { if section.IsSequential() { if section.IsSinglePrefixBlock() { return []*IPv6AddressSection{section} } wrapped := wrapIPSection(section.ToIP()) spanning := getSpanningPrefixBlocks(wrapped, wrapped) return cloneToIPv6Sections(spanning) } wrapped := wrapIPSection(section.ToIP()) return cloneToIPv6Sections(spanWithPrefixBlocks(wrapped)) } // SpanWithPrefixBlocksTo returns the smallest slice of prefix block subnet sections that span from this section to the given section. // // If the given section has a different segment count, an error is returned. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. func (section *IPv6AddressSection) SpanWithPrefixBlocksTo(other *IPv6AddressSection) ([]*IPv6AddressSection, addrerr.SizeMismatchError) { if err := section.checkSectionCount(other.ToIP()); err != nil { return nil, err } return cloneToIPv6Sections( getSpanningPrefixBlocks( wrapIPSection(section.ToIP()), wrapIPSection(other.ToIP()), ), ), nil } // SpanWithSequentialBlocks produces the smallest slice of sequential blocks that cover the same set of sections as this. // // This slice can be shorter than that produced by SpanWithPrefixBlocks and is never longer. // // Unlike SpanWithSequentialBlocksTo, this method only includes values that are a part of this section. func (section *IPv6AddressSection) SpanWithSequentialBlocks() []*IPv6AddressSection { if section.IsSequential() { return []*IPv6AddressSection{section} } wrapped := wrapIPSection(section.ToIP()) return cloneToIPv6Sections(spanWithSequentialBlocks(wrapped)) } // SpanWithSequentialBlocksTo produces the smallest slice of sequential block address sections that span from this section to the given section. func (section *IPv6AddressSection) SpanWithSequentialBlocksTo(other *IPv6AddressSection) ([]*IPv6AddressSection, addrerr.SizeMismatchError) { if err := section.checkSectionCount(other.ToIP()); err != nil { return nil, err } return cloneToIPv6Sections( getSpanningSequentialBlocks( wrapIPSection(section.ToIP()), wrapIPSection(other.ToIP()), ), ), nil } // CoverWithPrefixBlockTo returns the minimal-size prefix block section that covers all the address sections spanning from this to the given section. // // If the other section has a different segment count, an error is returned. func (section *IPv6AddressSection) CoverWithPrefixBlockTo(other *IPv6AddressSection) (*IPv6AddressSection, addrerr.SizeMismatchError) { res, err := section.coverWithPrefixBlockTo(other.ToIP()) return res.ToIPv6(), err } // CoverWithPrefixBlock returns the minimal-size prefix block that covers all the individual address sections in this section. // The resulting block will have a larger count than this, unless this section is already a prefix block. func (section *IPv6AddressSection) CoverWithPrefixBlock() *IPv6AddressSection { return section.coverWithPrefixBlock().ToIPv6() } func (section *IPv6AddressSection) checkSectionCounts(sections []*IPv6AddressSection) addrerr.SizeMismatchError { segCount := section.GetSegmentCount() length := len(sections) for i := 0; i < length; i++ { section2 := sections[i] if section2 == nil { continue } if section2.GetSegmentCount() != segCount { return &sizeMismatchError{incompatibleAddressError{addressError{key: "ipaddress.error.sizeMismatch"}}} } } return nil } // // MergeToSequentialBlocks merges this with the list of sections to produce the smallest array of sequential blocks. // // The resulting slice is sorted from lowest address value to highest, regardless of the size of each prefix block. func (section *IPv6AddressSection) MergeToSequentialBlocks(sections ...*IPv6AddressSection) ([]*IPv6AddressSection, addrerr.SizeMismatchError) { if err := section.checkSectionCounts(sections); err != nil { return nil, err } series := cloneIPv6Sections(section, sections) blocks := getMergedSequentialBlocks(series) return cloneToIPv6Sections(blocks), nil } // // MergeToPrefixBlocks merges this section with the list of sections to produce the smallest array of prefix blocks. // // The resulting slice is sorted from lowest value to highest, regardless of the size of each prefix block. func (section *IPv6AddressSection) MergeToPrefixBlocks(sections ...*IPv6AddressSection) ([]*IPv6AddressSection, addrerr.SizeMismatchError) { if err := section.checkSectionCounts(sections); err != nil { return nil, err } series := cloneIPv6Sections(section, sections) blocks := getMergedPrefixBlocks(series) return cloneToIPv6Sections(blocks), nil } // ReverseBits returns a new section with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (section *IPv6AddressSection) ReverseBits(perByte bool) (*IPv6AddressSection, addrerr.IncompatibleAddressError) { res, err := section.reverseBits(perByte) return res.ToIPv6(), err } // ReverseBytes returns a new section with the bytes reversed. Any prefix length is dropped. // // If the bytes within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, then this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. func (section *IPv6AddressSection) ReverseBytes() (*IPv6AddressSection, addrerr.IncompatibleAddressError) { res, err := section.reverseBytes(false) return res.ToIPv6(), err } // ReverseSegments returns a new section with the segments reversed. func (section *IPv6AddressSection) ReverseSegments() *IPv6AddressSection { if section.GetSegmentCount() <= 1 { if section.IsPrefixed() { return section.WithoutPrefixLen() } return section } res, _ := section.reverseSegments( func(i int) (*AddressSegment, addrerr.IncompatibleAddressError) { return section.GetSegment(i).WithoutPrefixLen().ToSegmentBase(), nil }, ) return res.ToIPv6() } // Append creates a new section by appending the given section to this section. func (section *IPv6AddressSection) Append(other *IPv6AddressSection) *IPv6AddressSection { count := section.GetSegmentCount() return section.ReplaceLen(count, count, other, 0, other.GetSegmentCount()) } // Insert creates a new section by inserting the given section into this section at the given index. func (section *IPv6AddressSection) Insert(index int, other *IPv6AddressSection) *IPv6AddressSection { return section.insert(index, other.ToIP(), ipv6BitsToSegmentBitshift).ToIPv6() } // Replace replaces the segments of this section starting at the given index with the given replacement segments. func (section *IPv6AddressSection) Replace(index int, replacement *IPv6AddressSection) *IPv6AddressSection { return section.ReplaceLen(index, index+replacement.GetSegmentCount(), replacement, 0, replacement.GetSegmentCount()) } // ReplaceLen replaces the segments starting from startIndex and ending before endIndex with the segments starting at replacementStartIndex and // ending before replacementEndIndex from the replacement section. func (section *IPv6AddressSection) ReplaceLen(startIndex, endIndex int, replacement *IPv6AddressSection, replacementStartIndex, replacementEndIndex int) *IPv6AddressSection { return section.replaceLen(startIndex, endIndex, replacement.ToIP(), replacementStartIndex, replacementEndIndex, ipv6BitsToSegmentBitshift).ToIPv6() } // IsAdaptiveZero returns true if the division grouping was originally created as an implicitly zero-valued section or grouping (e.g. IPv4AddressSection{}), // meaning it was not constructed using a constructor function. // Such a grouping, which has no divisions or segments, is convertible to an implicitly zero-valued grouping of any type or version, whether IPv6, IPv4, MAC, or other. // In other words, when a section or grouping is the zero-value, then it is equivalent and convertible to the zero value of any other section or grouping type. func (section *IPv6AddressSection) IsAdaptiveZero() bool { return section != nil && section.matchesZeroGrouping() } var ( compressAll = new(addrstr.CompressOptionsBuilder).SetCompressSingle(true).SetCompressionChoiceOptions(addrstr.ZerosOrHost).ToOptions() compressMixed = new(addrstr.CompressOptionsBuilder).SetCompressSingle(true).SetCompressionChoiceOptions(addrstr.MixedPreferred).ToOptions() compressAllNoSingles = new(addrstr.CompressOptionsBuilder).SetCompressionChoiceOptions(addrstr.ZerosOrHost).ToOptions() compressHostPreferred = new(addrstr.CompressOptionsBuilder).SetCompressSingle(true).SetCompressionChoiceOptions(addrstr.HostPreferred).ToOptions() compressZeros = new(addrstr.CompressOptionsBuilder).SetCompressSingle(true).SetCompressionChoiceOptions(addrstr.ZerosCompression).ToOptions() compressZerosNoSingles = new(addrstr.CompressOptionsBuilder).SetCompressionChoiceOptions(addrstr.ZerosCompression).ToOptions() uncWildcards = new(addrstr.WildcardOptionsBuilder).SetWildcardOptions(addrstr.WildcardsNetworkOnly).SetWildcards( new(addrstr.WildcardsBuilder).SetRangeSeparator(IPv6UncRangeSeparatorStr).SetWildcard(SegmentWildcardStr).ToWildcards()).ToOptions() base85Wildcards = new(addrstr.WildcardsBuilder).SetRangeSeparator(AlternativeRangeSeparatorStr).ToWildcards() mixedParams = new(addrstr.IPv6StringOptionsBuilder).SetMixed(true).SetCompressOptions(compressMixed).ToOptions() ipv6FullParams = new(addrstr.IPv6StringOptionsBuilder).SetExpandedSegments(true).SetWildcardOptions(wildcardsRangeOnlyNetworkOnly).ToOptions() ipv6CanonicalParams = new(addrstr.IPv6StringOptionsBuilder).SetCompressOptions(compressAllNoSingles).ToOptions() uncParams = new(addrstr.IPv6StringOptionsBuilder).SetSeparator(IPv6UncSegmentSeparator).SetZoneSeparator(IPv6UncZoneSeparatorStr). SetAddressSuffix(IPv6UncSuffix).SetWildcardOptions(uncWildcards).ToOptions() ipv6CompressedParams = new(addrstr.IPv6StringOptionsBuilder).SetCompressOptions(compressAll).ToOptions() ipv6normalizedParams = new(addrstr.IPv6StringOptionsBuilder).ToOptions() canonicalWildcardParams = new(addrstr.IPv6StringOptionsBuilder).SetWildcardOptions(allWildcards).SetCompressOptions(compressZerosNoSingles).ToOptions() ipv6NormalizedWildcardParams = new(addrstr.IPv6StringOptionsBuilder).SetWildcardOptions(allWildcards).ToOptions() //no compression ipv6SqlWildcardParams = new(addrstr.IPv6StringOptionsBuilder).SetWildcardOptions(allSQLWildcards).ToOptions() //no compression wildcardCompressedParams = new(addrstr.IPv6StringOptionsBuilder).SetWildcardOptions(allWildcards).SetCompressOptions(compressZeros).ToOptions() networkPrefixLengthParams = new(addrstr.IPv6StringOptionsBuilder).SetCompressOptions(compressHostPreferred).ToOptions() ipv6ReverseDNSParams = new(addrstr.IPv6StringOptionsBuilder).SetReverse(true).SetAddressSuffix(IPv6ReverseDnsSuffix). SetSplitDigits(true).SetExpandedSegments(true).SetSeparator('.').ToOptions() base85Params = new(addrstr.IPStringOptionsBuilder).SetRadix(85).SetExpandedSegments(true). SetWildcards(base85Wildcards).SetZoneSeparator(IPv6AlternativeZoneSeparatorStr).ToOptions() ipv6SegmentedBinaryParams = new(addrstr.IPStringOptionsBuilder).SetRadix(2).SetSeparator(IPv6SegmentSeparator).SetSegmentStrPrefix(BinaryPrefix). SetExpandedSegments(true).ToOptions() ) // String implements the [fmt.Stringer] interface, returning the normalized string provided by ToNormalizedString, or "" if the receiver is a nil pointer. func (section *IPv6AddressSection) String() string { if section == nil { return nilString() } return section.toString() } // ToHexString writes this address section as a single hexadecimal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *IPv6AddressSection) ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toHexString(with0xPrefix) } // ToOctalString writes this address section as a single octal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *IPv6AddressSection) ToOctalString(with0Prefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toOctalString(with0Prefix) } // ToBinaryString writes this address section as a single binary value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0b" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *IPv6AddressSection) ToBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toBinaryString(with0bPrefix) } // ToBase85String creates the base 85 string, which is described by RFC 1924, "A Compact Representation of IPv6 Addresses". // See https://www.rfc-editor.org/rfc/rfc1924.html // It may be written as a range of two values if a range that is not a prefixed block. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *IPv6AddressSection) ToBase85String() (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } cache := section.getStringCache() if cache == nil { return section.toBase85String(NoZone) } cacheField := &cache.base85String return cacheStrErr(cacheField, func() (string, addrerr.IncompatibleAddressError) { return section.toBase85String(NoZone) }) } func (section *IPv6AddressSection) toBase85String(zone Zone) (string, addrerr.IncompatibleAddressError) { if isDual, err := section.isDualString(); err != nil { return "", err } else { var largeGrouping *IPAddressLargeDivisionGrouping if section.hasNoDivisions() { largeGrouping = NewIPAddressLargeDivGrouping(nil) } else { bytes := section.getBytes() prefLen := section.getNetworkPrefixLen() bitCount := section.GetBitCount() var div *IPAddressLargeDivision if isDual { div = NewIPAddressLargeRangePrefixDivision(bytes, section.getUpperBytes(), prefLen, bitCount, 85) } else { div = NewIPAddressLargePrefixDivision(bytes, prefLen, bitCount, 85) } largeGrouping = NewIPAddressLargeDivGrouping([]*IPAddressLargeDivision{div}) } return toNormalizedIPZonedString(base85Params, largeGrouping, zone), nil } } // ToCanonicalString produces a canonical string for the address section. // // For IPv6, RFC 5952 describes canonical string representation. // https://en.wikipedia.org/wiki/IPv6_address#Representation // http://tools.ietf.org/html/rfc5952 // //If this section has a prefix length, it will be included in the string. func (section *IPv6AddressSection) ToCanonicalString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toCanonicalString(NoZone) } return cacheStr(&cache.canonicalString, func() string { return section.toCanonicalString(NoZone) }) } // ToNormalizedString produces a normalized string for the address section. // // For IPv6, it differs from the canonical string. Zero-segments are not compressed. // // If this section has a prefix length, it will be included in the string. func (section *IPv6AddressSection) ToNormalizedString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toNormalizedString(NoZone) } return cacheStr(&cache.normalizedIPv6String, func() string { return section.toNormalizedString(NoZone) }) } // ToCompressedString produces a short representation of this address section while remaining within the confines of standard representation(s) of the address. // // For IPv6, it differs from the canonical string. It compresses the maximum number of zeros and/or host segments with the IPv6 compression notation '::'. func (section *IPv6AddressSection) ToCompressedString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toCompressedString(NoZone) } return cacheStr(&cache.compressedIPv6String, func() string { return section.toCompressedString(NoZone) }) } // This produces the mixed IPv6/IPv4 string. It is the shortest such string (ie fully compressed). // For some address sections with ranges of values in the IPv4 part of the address, there is no mixed string, and an error is returned. func (section *IPv6AddressSection) toMixedString() (string, addrerr.IncompatibleAddressError) { cache := section.getStringCache() if cache == nil { return section.toMixedStringZoned(NoZone) } return cacheStrErr(&cache.mixedString, func() (string, addrerr.IncompatibleAddressError) { return section.toMixedStringZoned(NoZone) }) } // ToNormalizedWildcardString produces a string similar to the normalized string but avoids the CIDR prefix length. // CIDR addresses will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix notation. func (section *IPv6AddressSection) ToNormalizedWildcardString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toNormalizedWildcardStringZoned(NoZone) } return cacheStr(&cache.normalizedWildcardString, func() string { return section.toNormalizedWildcardStringZoned(NoZone) }) } // ToCanonicalWildcardString produces a string similar to the canonical string but avoids the CIDR prefix length. // Address sections with a network prefix length will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix length notation. // IPv6 sections will be compressed according to the canonical representation. func (section *IPv6AddressSection) ToCanonicalWildcardString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toCanonicalWildcardStringZoned(NoZone) } return cacheStr(&cache.canonicalWildcardString, func() string { return section.toCanonicalWildcardStringZoned(NoZone) }) } // ToSegmentedBinaryString writes this address section as segments of binary values preceded by the "0b" prefix. func (section *IPv6AddressSection) ToSegmentedBinaryString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toSegmentedBinaryStringZoned(NoZone) } return cacheStr(&cache.segmentedBinaryString, func() string { return section.toSegmentedBinaryStringZoned(NoZone) }) } // ToSQLWildcardString create a string similar to that from toNormalizedWildcardString except that // it uses SQL wildcards. It uses '%' instead of '*' and also uses the wildcard '_'. func (section *IPv6AddressSection) ToSQLWildcardString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toSQLWildcardStringZoned(NoZone) } return cacheStr(&cache.sqlWildcardString, func() string { return section.toSQLWildcardStringZoned(NoZone) }) } // ToFullString produces a string with no compressed segments and all segments of full length with leading zeros, // which is 4 characters for IPv6 segments. func (section *IPv6AddressSection) ToFullString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toFullStringZoned(NoZone) } return cacheStr(&cache.fullString, func() string { return section.toFullStringZoned(NoZone) }) } // ToReverseDNSString generates the reverse-DNS lookup string, // returning an error if this address section is a multiple-valued section for which the range cannot be represented. // For "2001:db8::567:89ab" it is "b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa". func (section *IPv6AddressSection) ToReverseDNSString() (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } cache := section.getStringCache() if cache == nil { return section.toReverseDNSStringZoned(NoZone) } return cacheStrErr(&cache.reverseDNSString, func() (string, addrerr.IncompatibleAddressError) { return section.toReverseDNSStringZoned(NoZone) }) } // ToPrefixLenString returns a string with a CIDR network prefix length if this address has a network prefix length. // For IPv6, a zero host section will be compressed with "::". For IPv4 the string is equivalent to the canonical string. func (section *IPv6AddressSection) ToPrefixLenString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toPrefixLenStringZoned(NoZone) } return cacheStr(&cache.networkPrefixLengthString, func() string { return section.toPrefixLenStringZoned(NoZone) }) } // ToSubnetString produces a string with specific formats for subnets. // The subnet string looks like "1.2.*.*" or "1:2::/16". // // In the case of IPv6, when a network prefix has been supplied, the prefix will be shown and the host section will be compressed with "::". func (section *IPv6AddressSection) ToSubnetString() string { if section == nil { return nilString() } return section.ToPrefixLenString() } // ToCompressedWildcardString produces a string similar to ToNormalizedWildcardString, avoiding the CIDR prefix, but with full IPv6 segment compression as well, including single zero-segments. func (section *IPv6AddressSection) ToCompressedWildcardString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toCompressedWildcardStringZoned(NoZone) } return cacheStr(&cache.compressedWildcardString, func() string { return section.toCompressedWildcardStringZoned(NoZone) }) } func (section *IPv6AddressSection) toCanonicalString(zone Zone) string { return section.toNormalizedZonedString(ipv6CanonicalParams, zone) } func (section *IPv6AddressSection) toNormalizedString(zone Zone) string { return section.toNormalizedZonedString(ipv6normalizedParams, zone) } func (section *IPv6AddressSection) toCompressedString(zone Zone) string { return section.toNormalizedZonedString(ipv6CompressedParams, zone) } func (section *IPv6AddressSection) toMixedStringZoned(zone Zone) (string, addrerr.IncompatibleAddressError) { return section.toNormalizedMixedZonedString(mixedParams, zone) } func (section *IPv6AddressSection) toNormalizedWildcardStringZoned(zone Zone) string { return section.toNormalizedZonedString(ipv6NormalizedWildcardParams, zone) } func (section *IPv6AddressSection) toCanonicalWildcardStringZoned(zone Zone) string { return section.toNormalizedZonedString(canonicalWildcardParams, zone) } func (section *IPv6AddressSection) toSegmentedBinaryStringZoned(zone Zone) string { return section.ipAddressSectionInternal.toCustomZonedString(ipv6SegmentedBinaryParams, zone) } func (section *IPv6AddressSection) toSQLWildcardStringZoned(zone Zone) string { return section.toNormalizedZonedString(ipv6SqlWildcardParams, zone) } func (section *IPv6AddressSection) toFullStringZoned(zone Zone) string { return section.toNormalizedZonedString(ipv6FullParams, zone) } func (section *IPv6AddressSection) toReverseDNSStringZoned(zone Zone) (string, addrerr.IncompatibleAddressError) { return section.toNormalizedSplitZonedString(ipv6ReverseDNSParams, zone) } func (section *IPv6AddressSection) toPrefixLenStringZoned(zone Zone) string { return section.toNormalizedZonedString(networkPrefixLengthParams, zone) } func (section *IPv6AddressSection) toCompressedWildcardStringZoned(zone Zone) string { return section.toNormalizedZonedString(wildcardCompressedParams, zone) } // ToCustomString creates a customized string from this address section according to the given string option parameters. // // Errors can result from split digits with ranged values, or mixed IPv4/v6 with ranged values, when the segment ranges are incompatible. func (section *IPv6AddressSection) ToCustomString(stringOptions addrstr.IPv6StringOptions) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toCustomString(stringOptions, NoZone) } func (section *IPv6AddressSection) toCustomString(stringOptions addrstr.IPv6StringOptions, zone Zone) (string, addrerr.IncompatibleAddressError) { if stringOptions.IsMixed() { return section.toNormalizedMixedZonedString(stringOptions, zone) } else if stringOptions.IsSplitDigits() { return section.toNormalizedSplitZonedString(stringOptions, zone) } return section.toNormalizedZonedString(stringOptions, zone), nil } func (section *IPv6AddressSection) toNormalizedMixedZonedString(options addrstr.IPv6StringOptions, zone Zone) (string, addrerr.IncompatibleAddressError) { stringParams := from(options, section) if stringParams.nextUncompressedIndex <= IPv6MixedOriginalSegmentCount { //the mixed section is not compressed mixedParams := &ipv6v4MixedParams{ ipv6Params: stringParams, ipv4Params: toIPParams(options.GetIPv4Opts()), } return section.toNormalizedMixedString(mixedParams, zone) } // the mixed section is compressed return stringParams.toZonedString(section, zone), nil } func (section *IPv6AddressSection) toNormalizedZonedString(options addrstr.IPv6StringOptions, zone Zone) string { return from(options, section).toZonedString(section, zone) } func (section *IPv6AddressSection) toNormalizedSplitZonedString(options addrstr.IPv6StringOptions, zone Zone) (string, addrerr.IncompatibleAddressError) { return from(options, section).toZonedSplitString(section, zone) } func (section *IPv6AddressSection) toNormalizedMixedString(mixedParams *ipv6v4MixedParams, zone Zone) (string, addrerr.IncompatibleAddressError) { mixed, err := section.getMixedAddressGrouping() if err != nil { return "", err } return mixedParams.toZonedString(mixed, zone), nil } // GetSegmentStrings returns a slice with the string for each segment being the string that is normalized with wildcards. func (section *IPv6AddressSection) GetSegmentStrings() []string { if section == nil { return nil } return section.getSegmentStrings() } // ToDivGrouping converts to an AddressDivisionGrouping, a polymorphic type usable with all address sections and division groupings. // Afterwards, you can convert back with ToIPv6. // // ToDivGrouping can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *IPv6AddressSection) ToDivGrouping() *AddressDivisionGrouping { return section.ToSectionBase().ToDivGrouping() } // ToSectionBase converts to an AddressSection, a polymorphic type usable with all address sections. // Afterwards, you can convert back with ToIPv6. // // ToSectionBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *IPv6AddressSection) ToSectionBase() *AddressSection { return section.ToIP().ToSectionBase() } // ToIP converts to an IPAddressSection, a polymorphic type usable with all IP address sections. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *IPv6AddressSection) ToIP() *IPAddressSection { return (*IPAddressSection)(section) } func (section *IPv6AddressSection) getMixedAddressGrouping() (*IPv6v4MixedAddressGrouping, addrerr.IncompatibleAddressError) { cache := section.cache var sect *IPv6v4MixedAddressGrouping var mCache *mixedCache if cache != nil { mCache = (*mixedCache)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.mixed)))) if mCache != nil { sect = mCache.defaultMixedAddressSection } } if sect == nil { mixedSect, err := section.createEmbeddedIPv4AddressSection() if err != nil { return nil, err } sect = newIPv6v4MixedGrouping( section.createNonMixedSection(), mixedSect, ) if cache != nil { mixed := &mixedCache{ defaultMixedAddressSection: sect, embeddedIPv6Section: sect.GetIPv6AddressSection(), embeddedIPv4Section: sect.GetIPv4AddressSection(), } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.mixed)) atomicStorePointer(dataLoc, unsafe.Pointer(mixed)) } } return sect, nil } // Gets the IPv4 section corresponding to the lowest (least-significant) 4 bytes in the original address, // which will correspond to between 0 and 4 bytes in this address. Many IPv4 to IPv6 mapping schemes (but not all) use these 4 bytes for a mapped IPv4 address. func (section *IPv6AddressSection) getEmbeddedIPv4AddressSection() (*IPv4AddressSection, addrerr.IncompatibleAddressError) { cache := section.cache if cache == nil { return section.createEmbeddedIPv4AddressSection() } sect, err := section.getMixedAddressGrouping() if err != nil { return nil, err } return sect.GetIPv4AddressSection(), nil } // GetIPv4AddressSection produces an IPv4 address section from a sequence of bytes in this IPv6 address section. func (section *IPv6AddressSection) GetIPv4AddressSection(startByteIndex, endByteIndex int) (*IPv4AddressSection, addrerr.IncompatibleAddressError) { if startByteIndex == IPv6MixedOriginalSegmentCount<<1 && endByteIndex == (section.GetSegmentCount()<<1) { return section.getEmbeddedIPv4AddressSection() } segments := make([]*AddressDivision, endByteIndex-startByteIndex) i := startByteIndex j := 0 bytesPerSegment := section.GetBytesPerSegment() if i%bytesPerSegment == 1 { ipv6Segment := section.GetSegment(i >> 1) i++ if err := ipv6Segment.splitIntoIPv4Segments(segments, j-1); err != nil { return nil, err } j++ } for ; i < endByteIndex; i, j = i+bytesPerSegment, j+bytesPerSegment { ipv6Segment := section.GetSegment(i >> 1) if err := ipv6Segment.splitIntoIPv4Segments(segments, j); err != nil { return nil, err } } res := createIPv4Section(segments) res.initMultAndPrefLen() return res, nil } func (section *IPv6AddressSection) createNonMixedSection() *EmbeddedIPv6AddressSection { nonMixedCount := IPv6MixedOriginalSegmentCount mixedCount := section.GetSegmentCount() - nonMixedCount var result *IPv6AddressSection if mixedCount <= 0 { result = section } else { nonMixed := make([]*AddressDivision, nonMixedCount) section.copySubDivisions(0, nonMixedCount, nonMixed) result = createIPv6Section(nonMixed) result.initMultAndPrefLen() } return &EmbeddedIPv6AddressSection{ embeddedIPv6AddressSection: embeddedIPv6AddressSection{*result}, encompassingSection: section, } } type embeddedIPv6AddressSection struct { IPv6AddressSection } // EmbeddedIPv6AddressSection represents the initial IPv6 section of an IPv6v4MixedAddressGrouping. type EmbeddedIPv6AddressSection struct { embeddedIPv6AddressSection encompassingSection *IPv6AddressSection } // IsPrefixBlock returns whether this address segment series has a prefix length and includes the block associated with its prefix length. // If the prefix length matches the bit count, this returns true. // // This is different from ContainsPrefixBlock in that this method returns // false if the series has no prefix length, or a prefix length that differs from a prefix length for which ContainsPrefixBlock returns true. func (section *EmbeddedIPv6AddressSection) IsPrefixBlock() bool { ipv6Sect := section.encompassingSection if ipv6Sect == nil { ipv6Sect = zeroIPv6AddressSection } return ipv6Sect.IsPrefixBlock() } func (section *IPv6AddressSection) createEmbeddedIPv4AddressSection() (sect *IPv4AddressSection, err addrerr.IncompatibleAddressError) { nonMixedCount := IPv6MixedOriginalSegmentCount segCount := section.GetSegmentCount() mixedCount := segCount - nonMixedCount lastIndex := segCount - 1 var mixed []*AddressDivision if mixedCount == 0 { mixed = []*AddressDivision{} } else if mixedCount == 1 { mixed = make([]*AddressDivision, section.GetBytesPerSegment()) last := section.GetSegment(lastIndex) if err := last.splitIntoIPv4Segments(mixed, 0); err != nil { return nil, err } } else { bytesPerSeg := section.GetBytesPerSegment() mixed = make([]*AddressDivision, bytesPerSeg<<1) low := section.GetSegment(lastIndex) high := section.GetSegment(lastIndex - 1) if err := high.splitIntoIPv4Segments(mixed, 0); err != nil { return nil, err } if err := low.splitIntoIPv4Segments(mixed, bytesPerSeg); err != nil { return nil, err } } sect = createIPv4Section(mixed) sect.initMultAndPrefLen() return } func createMixedAddressGrouping(divisions []*AddressDivision, mixedCache *mixedCache) *IPv6v4MixedAddressGrouping { grouping := &IPv6v4MixedAddressGrouping{ addressDivisionGroupingInternal: addressDivisionGroupingInternal{ addressDivisionGroupingBase: addressDivisionGroupingBase{ divisions: standardDivArray(divisions), addrType: ipv6v4MixedType, cache: &valueCache{mixed: mixedCache}, }, }, } ipv6Section := mixedCache.embeddedIPv6Section ipv4Section := mixedCache.embeddedIPv4Section grouping.isMult = ipv6Section.isMultiple() || ipv4Section.isMultiple() if ipv6Section.IsPrefixed() { grouping.prefixLength = ipv6Section.getPrefixLen() } else if ipv4Section.IsPrefixed() { grouping.prefixLength = cacheBitCount(ipv6Section.GetBitCount() + ipv4Section.getPrefixLen().bitCount()) } return grouping } func newIPv6v4MixedGrouping(ipv6Section *EmbeddedIPv6AddressSection, ipv4Section *IPv4AddressSection) *IPv6v4MixedAddressGrouping { ipv6Len := ipv6Section.GetSegmentCount() ipv4Len := ipv4Section.GetSegmentCount() allSegs := make([]*AddressDivision, ipv6Len+ipv4Len) ipv6Section.copySubDivisions(0, ipv6Len, allSegs) ipv4Section.copySubDivisions(0, ipv4Len, allSegs[ipv6Len:]) grouping := createMixedAddressGrouping(allSegs, &mixedCache{ embeddedIPv6Section: ipv6Section, embeddedIPv4Section: ipv4Section, }) return grouping } // IPv6v4MixedAddressGrouping has divisions which are a mix of IPv6 and IPv4 divisions. // It has an initial IPv6 section followed by an IPv4 section. type IPv6v4MixedAddressGrouping struct { addressDivisionGroupingInternal } // Compare returns a negative integer, zero, or a positive integer if this address division grouping is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (grouping *IPv6v4MixedAddressGrouping) Compare(item AddressItem) int { return CountComparator.Compare(grouping, item) } // CompareSize compares the counts of two items, the number of individual items represented in each. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether this grouping represents more individual address groupings than another item. // // CompareSize returns a positive integer if this address division grouping has a larger count than the item given, zero if they are the same, or a negative integer if the other has a larger count. func (grouping *IPv6v4MixedAddressGrouping) CompareSize(other AddressItem) int { if grouping == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return grouping.compareSize(other) } // GetCount returns the count of possible distinct values for this item. // If not representing multiple values, the count is 1, // unless this is a division grouping with no divisions, or an address section with no segments, in which case it is 0. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (grouping *IPv6v4MixedAddressGrouping) GetCount() *big.Int { if grouping == nil { return bigZero() } cnt := grouping.GetIPv6AddressSection().GetCount() return cnt.Mul(cnt, grouping.GetIPv4AddressSection().GetCount()) } // IsMultiple returns whether this grouping represents multiple values. func (grouping *IPv6v4MixedAddressGrouping) IsMultiple() bool { return grouping != nil && grouping.isMultiple() } // IsPrefixed returns whether this grouping has an associated prefix length. func (grouping *IPv6v4MixedAddressGrouping) IsPrefixed() bool { return grouping != nil && grouping.isPrefixed() } // IsAdaptiveZero returns true if the division grouping was originally created as an implicitly zero-valued section or grouping (e.g. IPv4AddressSection{}), // meaning it was not constructed using a constructor function. // Such a grouping, which has no divisions or segments, is convertible to an implicitly zero-valued grouping of any type or version, whether IPv6, IPv4, MAC, or other. // In other words, when a section or grouping is the zero-value, then it is equivalent and convertible to the zero value of any other section or grouping type. func (grouping *IPv6v4MixedAddressGrouping) IsAdaptiveZero() bool { return grouping != nil && grouping.matchesZeroGrouping() } // ToDivGrouping converts to an AddressDivisionGrouping, a polymorphic type usable with all address sections and division groupings. // Afterwards, you can convert back with ToMixedIPv6v4. // // ToDivGrouping can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (grouping *IPv6v4MixedAddressGrouping) ToDivGrouping() *AddressDivisionGrouping { return (*AddressDivisionGrouping)(grouping) } // by using cached sections for zero values, we will return the same section // pointer for repeated calls to the same zero-valued containing section var ( zeroEmbeddedIPv6AddressSection = &EmbeddedIPv6AddressSection{} zeroIPv4AddressSection = &IPv4AddressSection{} zeroIPv6AddressSection = &IPv6AddressSection{} ) // GetIPv6AddressSection returns the initial IPv6 section of the grouping. func (grouping *IPv6v4MixedAddressGrouping) GetIPv6AddressSection() *EmbeddedIPv6AddressSection { if grouping == nil { return nil } cache := grouping.cache if cache == nil { // zero-valued return zeroEmbeddedIPv6AddressSection } return cache.mixed.embeddedIPv6Section } // GetIPv4AddressSection returns the ending IPv4 section of the grouping. func (grouping *IPv6v4MixedAddressGrouping) GetIPv4AddressSection() *IPv4AddressSection { if grouping == nil { return nil } cache := grouping.cache if cache == nil { // zero-valued return zeroIPv4AddressSection } return cache.mixed.embeddedIPv4Section } // String implements the [fmt.Stringer] interface, // as a slice string with each division converted to a string by String ( ie "[ div0 div1 ...]"), // or "" if the receiver is a nil pointer. func (grouping *IPv6v4MixedAddressGrouping) String() string { if grouping == nil { return nilString() } // used to use grouping.toString() but decided to use a mixed string instead parms := mixedParams ipv6Sect := grouping.GetIPv6AddressSection().encompassingSection if ipv6Sect == nil { ipv6Sect = zeroIPv6AddressSection } // using ipv6Sect here instead of grouping.GetIPv6AddressSection() affects whether compression is used stringParams := from(parms, ipv6Sect) // I guess this would have to be on the ipv6 section - but no compress host params := &ipv6v4MixedParams{ ipv6Params: stringParams, ipv4Params: toIPParams(parms.GetIPv4Opts()), } // using grouping.GetIPv6AddressSection() here affects the results of IsPrefixBlock to account for the IPv4 section that follows the IPv6 section result := params.toZonedString(grouping, NoZone) return result } // Format is intentionally the only method with non-pointer receivers. It is not intended to be called directly, it is intended for use by the fmt package. // When called by a function in the fmt package, nil values are detected before this method is called, avoiding a panic when calling this method. // Format implements [fmt.Formatter] interface. It accepts the formats // - 'v' for the default address and section format (either the normalized or canonical string), // - 's' (string) for the same // - 'q' for a quoted string func (grouping IPv6v4MixedAddressGrouping) Format(state fmt.State, verb rune) { var str string switch verb { case 's', 'v', 'q': str = grouping.String() if verb == 'q' { if state.Flag('#') { str = "`" + str + "`" } else { str = `"` + str + `"` } } default: // format not supported _, _ = fmt.Fprintf(state, "%%!%c(address=%s)", verb, grouping.String()) return } _, _ = state.Write([]byte(str)) } var ffMACSeg, feMACSeg = NewMACSegment(0xff), NewMACSegment(0xfe) func toIPv6SegmentsFromEUI( segments []*AddressDivision, ipv6StartIndex int, // the index into the IPv6 segment array to put the MAC-based IPv6 segments eui *MACAddressSection, // must be full 6 or 8 mac sections prefixLength PrefixLen) addrerr.IncompatibleAddressError { euiSegmentIndex := 0 var seg3, seg4 *MACAddressSegment var err addrerr.IncompatibleAddressError seg0 := eui.GetSegment(euiSegmentIndex) euiSegmentIndex++ seg1 := eui.GetSegment(euiSegmentIndex) euiSegmentIndex++ seg2 := eui.GetSegment(euiSegmentIndex) euiSegmentIndex++ isExtended := eui.GetSegmentCount() == ExtendedUniqueIdentifier64SegmentCount if isExtended { seg3 = eui.GetSegment(euiSegmentIndex) euiSegmentIndex++ if !seg3.matches(0xff) { return &incompatibleAddressError{addressError{key: "ipaddress.mac.error.not.eui.convertible"}} } seg4 = eui.GetSegment(euiSegmentIndex) euiSegmentIndex++ if !seg4.matches(0xfe) { return &incompatibleAddressError{addressError{key: "ipaddress.mac.error.not.eui.convertible"}} } } else { seg3 = ffMACSeg seg4 = feMACSeg } seg5 := eui.GetSegment(euiSegmentIndex) euiSegmentIndex++ seg6 := eui.GetSegment(euiSegmentIndex) euiSegmentIndex++ seg7 := eui.GetSegment(euiSegmentIndex) var currentPrefix PrefixLen if prefixLength != nil { //since the prefix comes from the ipv6 section and not the MAC section, any segment prefix for the MAC section is 0 or nil //prefixes across segments have the pattern: nil, nil, ..., nil, 0-16, 0, 0, ..., 0 //So if the overall prefix is 0, then the prefix of every segment is 0 currentPrefix = cacheBitCount(0) } var seg *IPv6AddressSegment if seg, err = seg0.JoinAndFlip2ndBit(seg1, currentPrefix); /* only this first one gets the flipped bit */ err == nil { segments[ipv6StartIndex] = seg.ToDiv() ipv6StartIndex++ if seg, err = seg2.Join(seg3, currentPrefix); err == nil { segments[ipv6StartIndex] = seg.ToDiv() ipv6StartIndex++ if seg, err = seg4.Join(seg5, currentPrefix); err == nil { segments[ipv6StartIndex] = seg.ToDiv() ipv6StartIndex++ if seg, err = seg6.Join(seg7, currentPrefix); err == nil { segments[ipv6StartIndex] = seg.ToDiv() return nil } } } } return err } // SegmentSequence represents a sequence of consecutive segments with the given length starting from the given segment index. type SegmentSequence struct { index, length int } // SegmentSequenceList represents a list of SegmentSequence instances. type SegmentSequenceList struct { ranges []SegmentSequence } func (list SegmentSequenceList) size() int { return len(list.ranges) } func (list SegmentSequenceList) getRange(index int) SegmentSequence { return list.ranges[index] } ipaddress-go-1.5.4/ipaddr/ipv6segment.go000066400000000000000000001105161440250641600201510ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math/big" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" ) type IPv6SegInt = uint16 type IPv6SegmentValueProvider func(segmentIndex int) IPv6SegInt // WrapIPv6SegmentValueProvider converts the given IPv6SegmentValueProvider to a SegmentValueProvider. func WrapIPv6SegmentValueProvider(f IPv6SegmentValueProvider) SegmentValueProvider { if f == nil { return nil } return func(segmentIndex int) SegInt { return SegInt(f(segmentIndex)) } } // WrapSegmentValueProviderForIPv6 converts the given SegmentValueProvider to an IPv6SegmentValueProvider. // Values that do not fit IPv6SegInt are truncated. func WrapSegmentValueProviderForIPv6(f SegmentValueProvider) IPv6SegmentValueProvider { if f == nil { return nil } return func(segmentIndex int) IPv6SegInt { return IPv6SegInt(f(segmentIndex)) } } const useIPv6SegmentCache = true type ipv6SegmentValues struct { value IPv6SegInt upperValue IPv6SegInt prefLen PrefixLen cache divCache } func (seg *ipv6SegmentValues) getAddrType() addrType { return ipv6Type } func (seg *ipv6SegmentValues) includesZero() bool { return seg.value == 0 } func (seg *ipv6SegmentValues) includesMax() bool { return seg.upperValue == 0xffff } func (seg *ipv6SegmentValues) isMultiple() bool { return seg.value != seg.upperValue } func (seg *ipv6SegmentValues) getCount() *big.Int { return big.NewInt(int64(seg.upperValue-seg.value) + 1) } func (seg *ipv6SegmentValues) getBitCount() BitCount { return IPv6BitsPerSegment } func (seg *ipv6SegmentValues) getByteCount() int { return IPv6BytesPerSegment } func (seg *ipv6SegmentValues) getValue() *BigDivInt { return big.NewInt(int64(seg.value)) } func (seg *ipv6SegmentValues) getUpperValue() *BigDivInt { return big.NewInt(int64(seg.upperValue)) } func (seg *ipv6SegmentValues) getDivisionValue() DivInt { return DivInt(seg.value) } func (seg *ipv6SegmentValues) getUpperDivisionValue() DivInt { return DivInt(seg.upperValue) } func (seg *ipv6SegmentValues) getDivisionPrefixLength() PrefixLen { return seg.prefLen } func (seg *ipv6SegmentValues) getSegmentValue() SegInt { return SegInt(seg.value) } func (seg *ipv6SegmentValues) getUpperSegmentValue() SegInt { return SegInt(seg.upperValue) } func (seg *ipv6SegmentValues) calcBytesInternal() (bytes, upperBytes []byte) { bytes = []byte{byte(seg.value >> 8), byte(seg.value)} if seg.isMultiple() { upperBytes = []byte{byte(seg.upperValue >> 8), byte(seg.upperValue)} } else { upperBytes = bytes } return } func (seg *ipv6SegmentValues) bytesInternal(upper bool) []byte { var val IPv6SegInt if upper { val = seg.upperValue } else { val = seg.value } return []byte{byte(val >> 8), byte(val)} } func (seg *ipv6SegmentValues) deriveNew(val, upperVal DivInt, prefLen PrefixLen) divisionValues { return newIPv6SegmentPrefixedValues(IPv6SegInt(val), IPv6SegInt(upperVal), prefLen) } func (seg *ipv6SegmentValues) derivePrefixed(prefLen PrefixLen) divisionValues { return newIPv6SegmentPrefixedValues(seg.value, seg.upperValue, prefLen) } func (seg *ipv6SegmentValues) deriveNewSeg(val SegInt, prefLen PrefixLen) divisionValues { return newIPv6SegmentPrefixedVal(IPv6SegInt(val), prefLen) } func (seg *ipv6SegmentValues) deriveNewMultiSeg(val, upperVal SegInt, prefLen PrefixLen) divisionValues { return newIPv6SegmentPrefixedValues(IPv6SegInt(val), IPv6SegInt(upperVal), prefLen) } func (seg *ipv6SegmentValues) getCache() *divCache { return &seg.cache } var _ divisionValues = &ipv6SegmentValues{} var zeroIPv6Seg = NewIPv6Segment(0) var zeroIPv6SegZeroPrefix = NewIPv6PrefixedSegment(0, cacheBitCount(0)) var zeroIPv6SegPrefixBlock = NewIPv6RangePrefixedSegment(0, IPv6MaxValuePerSegment, cacheBitCount(0)) // IPv6AddressSegment represents a segment of an IPv6 address. // An IPv6 segment contains a single value or a range of sequential values, a prefix length, and it has bit length of 16 bits. // // Like strings, segments are immutable, which also makes them concurrency-safe. // // See AddressSegment for more details regarding segments. type IPv6AddressSegment struct { ipAddressSegmentInternal } func (seg *IPv6AddressSegment) init() *IPv6AddressSegment { if seg.divisionValues == nil { return zeroIPv6Seg } return seg } // GetIPv6SegmentValue returns the lower value. Same as GetSegmentValue but returned as a IPv6SegInt. func (seg *IPv6AddressSegment) GetIPv6SegmentValue() IPv6SegInt { return IPv6SegInt(seg.GetSegmentValue()) } // GetIPv6UpperSegmentValue returns the lower value. Same as GetUpperSegmentValue but returned as a IPv6SegInt. func (seg *IPv6AddressSegment) GetIPv6UpperSegmentValue() IPv6SegInt { return IPv6SegInt(seg.GetUpperSegmentValue()) } // Contains returns whether this is same type and version as the given segment and whether it contains all values in the given segment. func (seg *IPv6AddressSegment) Contains(other AddressSegmentType) bool { if seg == nil { return other == nil || other.ToSegmentBase() == nil } return seg.init().contains(other) } // Equal returns whether the given segment is equal to this segment. // Two segments are equal if they match: // - type/version: IPv6 // - value range // Prefix lengths are ignored. func (seg *IPv6AddressSegment) Equal(other AddressSegmentType) bool { if seg == nil { return other == nil || other.ToDiv() == nil } return seg.init().equal(other) } // PrefixContains returns whether the prefix values in the prefix of the given segment are also prefix values in this segment. // It returns whether the prefix of this segment contains the prefix of the given segment. func (seg *IPv6AddressSegment) PrefixContains(other AddressSegmentType, prefixLength BitCount) bool { return seg.init().ipAddressSegmentInternal.PrefixContains(other, prefixLength) } // PrefixEqual returns whether the prefix bits of this segment match the same bits of the given segment. // It returns whether the two segments share the same range of prefix values using the given prefix length. func (seg *IPv6AddressSegment) PrefixEqual(other AddressSegmentType, prefixLength BitCount) bool { return seg.init().ipAddressSegmentInternal.PrefixEqual(other, prefixLength) } // Compare returns a negative integer, zero, or a positive integer if this address segment is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (seg *IPv6AddressSegment) Compare(item AddressItem) int { if seg != nil { seg = seg.init() } return CountComparator.Compare(seg, item) } // CompareSize compares the counts of two items, the number of individual values within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether one represents more individual values than another. // // CompareSize returns a positive integer if this segment has a larger count than the one given, zero if they are the same, or a negative integer if the other has a larger count. func (seg *IPv6AddressSegment) CompareSize(other AddressItem) int { if seg == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return seg.init().compareSize(other) } // GetBitCount returns the number of bits in each value comprising this address item, which is 16. func (seg *IPv6AddressSegment) GetBitCount() BitCount { return IPv6BitsPerSegment } // GetByteCount returns the number of bytes required for each value comprising this address item, which is 2. func (seg *IPv6AddressSegment) GetByteCount() int { return IPv6BytesPerSegment } // GetMaxValue gets the maximum possible value for this type or version of segment, determined by the number of bits. // // For the highest range value of this particular segment, use GetUpperSegmentValue. func (seg *IPv6AddressSegment) GetMaxValue() IPv6SegInt { return 0xffff } // GetLower returns a segment representing just the lowest value in the range, which will be the same segment if it represents a single value. func (seg *IPv6AddressSegment) GetLower() *IPv6AddressSegment { return seg.init().getLower().ToIPv6() } // GetUpper returns a segment representing just the highest value in the range, which will be the same segment if it represents a single value. func (seg *IPv6AddressSegment) GetUpper() *IPv6AddressSegment { return seg.init().getUpper().ToIPv6() } // IsMultiple returns whether this segment represents multiple values. func (seg *IPv6AddressSegment) IsMultiple() bool { return seg != nil && seg.isMultiple() } // GetCount returns the count of possible distinct values for this item. // If not representing multiple values, the count is 1. // // For instance, a segment with the value range of 3-7 has count 5. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (seg *IPv6AddressSegment) GetCount() *big.Int { if seg == nil { return bigZero() } return seg.getCount() } // GetPrefixCountLen returns the count of the number of distinct prefix values for the given prefix length in the range of values of this segment. func (seg *IPv6AddressSegment) GetPrefixCountLen(segmentPrefixLength BitCount) *big.Int { return seg.init().ipAddressSegmentInternal.GetPrefixCountLen(segmentPrefixLength) } // GetPrefixValueCountLen returns the same value as GetPrefixCountLen as an integer. func (seg *IPv6AddressSegment) GetPrefixValueCountLen(segmentPrefixLength BitCount) SegIntCount { return seg.init().ipAddressSegmentInternal.GetPrefixValueCountLen(segmentPrefixLength) } // IsOneBit returns true if the bit in the lower value of this segment at the given index is 1, where index 0 is the most significant bit. func (seg *IPv6AddressSegment) IsOneBit(segmentBitIndex BitCount) bool { return seg.init().ipAddressSegmentInternal.IsOneBit(segmentBitIndex) } // Bytes returns the lowest value in the address segment range as a byte slice. func (seg *IPv6AddressSegment) Bytes() []byte { return seg.init().ipAddressSegmentInternal.Bytes() } // UpperBytes returns the highest value in the address segment range as a byte slice. func (seg *IPv6AddressSegment) UpperBytes() []byte { return seg.init().ipAddressSegmentInternal.UpperBytes() } // CopyBytes copies the lowest value in the address segment range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (seg *IPv6AddressSegment) CopyBytes(bytes []byte) []byte { return seg.init().ipAddressSegmentInternal.CopyBytes(bytes) } // CopyUpperBytes copies the highest value in the address segment range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (seg *IPv6AddressSegment) CopyUpperBytes(bytes []byte) []byte { return seg.init().ipAddressSegmentInternal.CopyUpperBytes(bytes) } // GetPrefixValueCount returns the count of prefixes in this segment for its prefix length, or the total count if it has no prefix length. func (seg *IPv6AddressSegment) GetPrefixValueCount() SegIntCount { return seg.init().ipAddressSegmentInternal.GetPrefixValueCount() } // MatchesWithPrefixMask applies the network mask of the given bit-length to this segment and then compares the result with the given value masked by the same mask, // returning true if the resulting range matches the given single value. func (seg *IPv6AddressSegment) MatchesWithPrefixMask(value IPv6SegInt, networkBits BitCount) bool { return seg.init().ipAddressSegmentInternal.MatchesWithPrefixMask(SegInt(value), networkBits) } // GetBlockMaskPrefixLen returns the prefix length if this address segment is equivalent to the mask for a CIDR prefix block. // Otherwise, it returns nil. // A CIDR network mask is a segment with all ones in the network bits and then all zeros in the host bits. // A CIDR host mask is a segment with all zeros in the network bits and then all ones in the host bits. // The prefix length is the bit-length of the network bits. // // Also, keep in mind that the prefix length returned by this method is not equivalent to the prefix length of this segment. // The prefix length returned here indicates the whether the value of this segment can be used as a mask for the network and host // bits of any other segment. Therefore, the two values can be different values, or one can be nil while the other is not. // // This method applies only to the lower value of the range if this segment represents multiple values. func (seg *IPv6AddressSegment) GetBlockMaskPrefixLen(network bool) PrefixLen { return seg.init().ipAddressSegmentInternal.GetBlockMaskPrefixLen(network) } // GetTrailingBitCount returns the number of consecutive trailing one or zero bits. // If ones is true, returns the number of consecutive trailing zero bits. // Otherwise, returns the number of consecutive trailing one bits. // // This method applies only to the lower value of the range if this segment represents multiple values. func (seg *IPv6AddressSegment) GetTrailingBitCount(ones bool) BitCount { return seg.init().ipAddressSegmentInternal.GetTrailingBitCount(ones) } // GetLeadingBitCount returns the number of consecutive leading one or zero bits. // If ones is true, returns the number of consecutive leading one bits. // Otherwise, returns the number of consecutive leading zero bits. // // This method applies only to the lower value of the range if this segment represents multiple values. func (seg *IPv6AddressSegment) GetLeadingBitCount(ones bool) BitCount { return seg.init().ipAddressSegmentInternal.GetLeadingBitCount(ones) } // ToPrefixedNetworkSegment returns a segment with the network bits matching this segment but the host bits converted to zero. // The new segment will be assigned the given prefix length. func (seg *IPv6AddressSegment) ToPrefixedNetworkSegment(segmentPrefixLength PrefixLen) *IPv6AddressSegment { return seg.init().toPrefixedNetworkDivision(segmentPrefixLength).ToIPv6() } // ToNetworkSegment returns a segment with the network bits matching this segment but the host bits converted to zero. // The new segment will have no assigned prefix length. func (seg *IPv6AddressSegment) ToNetworkSegment(segmentPrefixLength PrefixLen) *IPv6AddressSegment { return seg.init().toNetworkDivision(segmentPrefixLength, false).ToIPv6() } // ToPrefixedHostSegment returns a segment with the host bits matching this segment but the network bits converted to zero. // The new segment will be assigned the given prefix length. func (seg *IPv6AddressSegment) ToPrefixedHostSegment(segmentPrefixLength PrefixLen) *IPv6AddressSegment { return seg.init().toPrefixedHostDivision(segmentPrefixLength).ToIPv6() } // ToHostSegment returns a segment with the host bits matching this segment but the network bits converted to zero. // The new segment will have no assigned prefix length. func (seg *IPv6AddressSegment) ToHostSegment(segmentPrefixLength PrefixLen) *IPv6AddressSegment { return seg.init().toHostDivision(segmentPrefixLength, false).ToIPv6() } // Iterator provides an iterator to iterate through the individual address segments of this address segment. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual address segments. // // Call IsMultiple to determine if this instance represents multiple address segments, or GetValueCount for the count. func (seg *IPv6AddressSegment) Iterator() Iterator[*IPv6AddressSegment] { if seg == nil { return ipv6SegmentIterator{nilSegIterator()} } return ipv6SegmentIterator{seg.init().iterator()} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address segment. // Each iterated address segment will be a prefix block with the same prefix length as this address segment. // // If this address segment has no prefix length, then this is equivalent to Iterator. func (seg *IPv6AddressSegment) PrefixBlockIterator() Iterator[*IPv6AddressSegment] { return ipv6SegmentIterator{seg.init().prefixBlockIterator()} } // PrefixedBlockIterator provides an iterator to iterate through the individual prefix blocks of the given prefix length in this segment, // one for each prefix of this address or subnet. // // It is similar to PrefixBlockIterator except that this method allows you to specify the prefix length. func (seg *IPv6AddressSegment) PrefixedBlockIterator(segmentPrefixLen BitCount) Iterator[*IPv6AddressSegment] { return ipv6SegmentIterator{seg.init().prefixedBlockIterator(segmentPrefixLen)} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this segment, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this segment. // // If this address segment has no prefix length, then this is equivalent to Iterator. func (seg *IPv6AddressSegment) PrefixIterator() Iterator[*IPv6AddressSegment] { return ipv6SegmentIterator{seg.init().prefixIterator()} } // IsPrefixed returns whether this segment has an associated prefix length. func (seg *IPv6AddressSegment) IsPrefixed() bool { return seg != nil && seg.isPrefixed() } // WithoutPrefixLen returns a segment with the same value range but without a prefix length. func (seg *IPv6AddressSegment) WithoutPrefixLen() *IPv6AddressSegment { if !seg.IsPrefixed() { return seg } return seg.withoutPrefixLen().ToIPv6() } // Converts this IPv6 address segment into smaller segments, // copying them into the given array starting at the given index. // // If a segment does not fit into the array because the segment index in the array is out of bounds of the array, // then it is not copied. // // It is used to create both IPv4 and MAC segments. func (seg *IPv6AddressSegment) visitSplitSegments(creator func(index int, value, upperValue SegInt, prefLen PrefixLen)) addrerr.IncompatibleAddressError { if seg.isMultiple() { return seg.visitSplitSegmentsMultiple(creator) } else { index := 0 bitSizeSplit := IPv6BitsPerSegment >> 1 myPrefix := seg.GetSegmentPrefixLen() val := seg.highByte() highPrefixBits := getSegmentPrefixLength(bitSizeSplit, myPrefix, 0) creator(index, val, val, highPrefixBits) index++ val = seg.lowByte() lowPrefixBits := getSegmentPrefixLength(bitSizeSplit, myPrefix, 1) creator(index, val, val, lowPrefixBits) return nil } } func (seg *IPv6AddressSegment) splitSegValues() (highLower, highUpper, lowLower, lowUpper SegInt, err addrerr.IncompatibleAddressError) { val := seg.GetSegmentValue() upperVal := seg.GetUpperSegmentValue() highLower = highByteIpv6(val) highUpper = highByteIpv6(upperVal) lowLower = lowByteIpv6(val) lowUpper = lowByteIpv6(upperVal) if (highLower != highUpper) && (lowLower != 0 || lowUpper != 0xff) { err = &incompatibleAddressError{addressError{key: "ipaddress.error.splitSeg"}} } return } // Used to create both IPv4 and MAC segments func (seg *IPv6AddressSegment) visitSplitSegmentsMultiple(creator func(index int, value, upperValue SegInt, prefLen PrefixLen)) addrerr.IncompatibleAddressError { myPrefix := seg.GetSegmentPrefixLen() bitSizeSplit := BitCount(IPv6BitsPerSegment >> 1) highLower, highUpper, lowLower, lowUpper, err := seg.splitSegValues() if err != nil { return err } highPrefixBits := getSegmentPrefixLength(bitSizeSplit, myPrefix, 0) lowPrefixBits := getSegmentPrefixLength(bitSizeSplit, myPrefix, 1) creator(0, highLower, highUpper, highPrefixBits) creator(1, lowLower, lowUpper, lowPrefixBits) return nil } func (seg *IPv6AddressSegment) highByte() SegInt { return highByteIpv6(seg.GetSegmentValue()) } func (seg *IPv6AddressSegment) lowByte() SegInt { return lowByteIpv6(seg.GetSegmentValue()) } func highByteIpv6(value SegInt) SegInt { return value >> 8 } func lowByteIpv6(value SegInt) SegInt { return value & 0xff } // Converts this IPv6 address segment into smaller segments, // copying them into the given array starting at the given index. // // If a segment does not fit into the array because the segment index in the array is out of bounds of the array, // then it is not copied. func (seg *IPv6AddressSegment) getSplitSegments(segs []*IPv4AddressSegment, startIndex int) addrerr.IncompatibleAddressError { return seg.visitSplitSegments(func(index int, value, upperValue SegInt, prefLen PrefixLen) { if ind := startIndex + index; ind < len(segs) { segs[ind] = NewIPv4RangePrefixedSegment(IPv4SegInt(value), IPv4SegInt(upperValue), prefLen) } }) } func (seg *IPv6AddressSegment) splitIntoIPv4Segments(segs []*AddressDivision, startIndex int) addrerr.IncompatibleAddressError { return seg.visitSplitSegments(func(index int, value, upperValue SegInt, prefLen PrefixLen) { if ind := startIndex + index; ind < len(segs) { segs[ind] = NewIPv4RangePrefixedSegment(IPv4SegInt(value), IPv4SegInt(upperValue), prefLen).ToDiv() } }) } func (seg *IPv6AddressSegment) splitIntoMACSegments(segs []*AddressDivision, startIndex int) addrerr.IncompatibleAddressError { return seg.visitSplitSegments(func(index int, value, upperValue SegInt, prefLen PrefixLen) { if ind := startIndex + index; ind < len(segs) { segs[ind] = NewMACRangeSegment(MACSegInt(value), MACSegInt(upperValue)).ToDiv() } }) } // ReverseBits returns a segment with the bits reversed. // // If this segment represents a range of values that cannot be reversed, then this returns an error. // // To be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // Otherwise the result is not contiguous and thus cannot be represented by a sequential range of values. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (seg *IPv6AddressSegment) ReverseBits(perByte bool) (res *IPv6AddressSegment, err addrerr.IncompatibleAddressError) { if seg.divisionValues == nil { res = seg return } if seg.isMultiple() { var addrSeg *AddressSegment addrSeg, err = seg.reverseMultiValSeg(perByte) res = addrSeg.ToIPv6() return } oldVal := IPv6SegInt(seg.GetSegmentValue()) val := IPv6SegInt(reverseUint16(uint16(oldVal))) if perByte { val = ((val & 0xff) << 8) | (val >> 8) } if oldVal == val && !seg.isPrefixed() { res = seg } else { res = NewIPv6Segment(val) } return } // ReverseBytes returns a segment with the bytes reversed. // // If this segment represents a range of values that cannot be reversed, then this returns an error. // // To be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // Otherwise the result is not contiguous and thus cannot be represented by a sequential range of values. func (seg *IPv6AddressSegment) ReverseBytes() (res *IPv6AddressSegment, err addrerr.IncompatibleAddressError) { if seg.divisionValues == nil { res = seg return } if seg.isMultiple() { var addrSeg *AddressSegment addrSeg, err = seg.reverseMultiValSeg(false) res = addrSeg.ToIPv6() return } oldVal := IPv6SegInt(seg.GetSegmentValue()) val := IPv6SegInt(reverseUint16(uint16(oldVal))) if oldVal == val && !seg.isPrefixed() { res = seg } else { res = NewIPv6Segment(val) } return } // ToDiv converts to an AddressDivision, a polymorphic type usable with all address segments and divisions. // Afterwards, you can convert back with ToIPv6. // // ToDiv can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *IPv6AddressSegment) ToDiv() *AddressDivision { return seg.ToIP().ToDiv() } // ToSegmentBase converts to an AddressSegment, a polymorphic type usable with all address segments. // Afterwards, you can convert back with ToIPv6. // // ToSegmentBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *IPv6AddressSegment) ToSegmentBase() *AddressSegment { return seg.ToIP().ToSegmentBase() } // ToIP converts to an IPAddressSegment, a polymorphic type usable with all IP address segments. // Afterwards, you can convert back with ToIPv6. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *IPv6AddressSegment) ToIP() *IPAddressSegment { if seg == nil { return nil } return (*IPAddressSegment)(seg.init()) } // GetString produces a normalized string to represent the segment. // If the segment is a CIDR network prefix block for its prefix length, then the string contains only the lower value of the block range. // Otherwise, the explicit range will be printed. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (seg *IPv6AddressSegment) GetString() string { if seg == nil { return nilString() } return seg.init().getString() } // GetWildcardString produces a normalized string to represent the segment, favouring wildcards and range characters while ignoring any network prefix length. // The explicit range of a range-valued segment will be printed. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and the bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (seg *IPv6AddressSegment) GetWildcardString() string { if seg == nil { return nilString() } return seg.init().getWildcardString() } // String produces a string that is useful when a segment is provided with no context. It uses the hexadecimal radix with the string prefix for hex ("0x"). // GetWildcardString is more appropriate in context with other segments or divisions. It does not use a string prefix and uses '*' for full-range segments. // GetString is more appropriate in context with prefix lengths, it uses zeros instead of wildcards with full prefix block ranges alongside prefix lengths. func (seg *IPv6AddressSegment) String() string { if seg == nil { return nilString() } return seg.init().toString() } // NewIPv6Segment constructs a segment of an IPv6 address with the given value. func NewIPv6Segment(val IPv6SegInt) *IPv6AddressSegment { return newIPv6Segment(newIPv6SegmentVal(val)) } // NewIPv6RangeSegment constructs a segment of an IPv6 subnet with the given range of sequential values. func NewIPv6RangeSegment(val, upperVal IPv6SegInt) *IPv6AddressSegment { return newIPv6Segment(newIPv6SegmentPrefixedValues(val, upperVal, nil)) } // NewIPv6PrefixedSegment constructs a segment of an IPv6 address with the given value and assigned prefix length. func NewIPv6PrefixedSegment(val IPv6SegInt, prefixLen PrefixLen) *IPv6AddressSegment { return newIPv6Segment(newIPv6SegmentPrefixedVal(val, prefixLen)) } // NewIPv6RangePrefixedSegment constructs a segment of an IPv6 subnet with the given range of sequential values and assigned prefix length. func NewIPv6RangePrefixedSegment(val, upperVal IPv6SegInt, prefixLen PrefixLen) *IPv6AddressSegment { return newIPv6Segment(newIPv6SegmentPrefixedValues(val, upperVal, prefixLen)) } func newIPv6Segment(vals *ipv6SegmentValues) *IPv6AddressSegment { return &IPv6AddressSegment{ ipAddressSegmentInternal{ addressSegmentInternal{ addressDivisionInternal{ addressDivisionBase{vals}, }, }, }, } } type ipv6DivsBlock struct { block []ipv6SegmentValues } type ipv6DivsPartition struct { block []*ipv6DivsBlock } var ( allRangeValsIPv6 = &ipv6SegmentValues{ upperValue: IPv6MaxValuePerSegment, cache: divCache{ isSinglePrefBlock: &falseVal, }, } allPrefixedCacheIPv6 = makePrefixCacheIPv6() // single-valued no-prefix cache. // there are 0x10000 (ie 0xffff + 1 or 64k) possible segment values in IPv6. We break the cache into 0x100 blocks of size 0x100 segmentCacheIPv6 = make([]*ipv6DivsBlock, (IPv6MaxValuePerSegment>>8)+1) // single-valued cache for each prefix. segmentPrefixCacheIPv6 = make([]*ipv6DivsPartition, IPv6BitsPerSegment+1) // for each prefix, all segment values, 0x100 blocks of size 0x100 // prefix-block cache: all the prefix blocks for each prefix. // for each prefix, all prefix blocks. // For a given prefix, you shift left by 8 bits for the blocks of size 0x100, the remaining bits to the left are the number of blocks. // // For prefix of size 8, 1 block of size 0x100 // For prefix of size < 8, 1 block of size (1 << prefix) // For prefix of size > 8, (1 << (prefix - 8)) blocks of size 0x100. // // So, you start with the prefix to get the right ipv6DivsPartition. // Then, you use the formula above to look up the block index. // For the first two above, the whole prefix finds the index into the single block. // For the third, the 8 rightmost bits in the prefix give the index into the block of size ff, // while the leftmost bits in the prefix select that block. prefixBlocksCacheIPv6 = make([]*ipv6DivsPartition, IPv6BitsPerSegment+1) ) func makePrefixCacheIPv6() (allPrefixedCacheIPv6 []ipv6SegmentValues) { if useIPv6SegmentCache { allPrefixedCacheIPv6 = make([]ipv6SegmentValues, IPv6BitsPerSegment+1) for i := range allPrefixedCacheIPv6 { vals := &allPrefixedCacheIPv6[i] vals.upperValue = IPv6MaxValuePerSegment vals.prefLen = cacheBitCount(i) vals.cache.isSinglePrefBlock = &falseVal } allPrefixedCacheIPv6[0].cache.isSinglePrefBlock = &trueVal } return } func newIPv6SegmentVal(value IPv6SegInt) *ipv6SegmentValues { if useIPv6SegmentCache { cache := segmentCacheIPv6 blockIndex := value >> 8 // divide by 0x100 firstBlockVal := blockIndex << 8 resultIndex := value - firstBlockVal // mod 0x100 block := (*ipv6DivsBlock)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache[blockIndex])))) //block := cache[blockIndex] if block == nil { block = &ipv6DivsBlock{make([]ipv6SegmentValues, 0x100)} vals := block.block for i := range vals { item := &vals[i] itemVal := firstBlockVal | IPv6SegInt(i) item.value = itemVal item.upperValue = itemVal item.cache.isSinglePrefBlock = &falseVal } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache[blockIndex])) atomicStorePointer(dataLoc, unsafe.Pointer(block)) } result := &block.block[resultIndex] return result } return &ipv6SegmentValues{ value: value, upperValue: value, cache: divCache{ isSinglePrefBlock: &falseVal, }, } } func newIPv6SegmentPrefixedVal(value IPv6SegInt, prefLen PrefixLen) (result *ipv6SegmentValues) { if prefLen == nil { return newIPv6SegmentVal(value) } prefixIndex := prefLen.bitCount() if prefixIndex < 0 { prefixIndex = 0 } else if prefixIndex > IPv6BitsPerSegment { prefixIndex = IPv6BitsPerSegment } prefLen = cacheBitCount(prefixIndex) // this ensures we use the prefix length cache for all segments if useIPv6SegmentCache { cache := segmentPrefixCacheIPv6 prefixCache := (*ipv6DivsPartition)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache[prefixIndex])))) if prefixCache == nil { prefixCache = &ipv6DivsPartition{make([]*ipv6DivsBlock, (IPv6MaxValuePerSegment>>8)+1)} dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache[prefixIndex])) atomicStorePointer(dataLoc, unsafe.Pointer(prefixCache)) } blockIndex := value >> 8 // divide by 0x100 firstBlockVal := blockIndex << 8 resultIndex := value - (firstBlockVal) // mod 0x100 blockCache := (*ipv6DivsBlock)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&prefixCache.block[blockIndex])))) if blockCache == nil { blockCache = &ipv6DivsBlock{make([]ipv6SegmentValues, (IPv6MaxValuePerSegment>>8)+1)} vals := blockCache.block var isSinglePrefBlock *bool if prefixIndex == IPv6BitsPerSegment { isSinglePrefBlock = &trueVal } else { isSinglePrefBlock = &falseVal } for i := range vals { item := &vals[i] itemVal := firstBlockVal | IPv6SegInt(i) item.value = itemVal item.upperValue = itemVal item.prefLen = prefLen item.cache.isSinglePrefBlock = isSinglePrefBlock } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&prefixCache.block[blockIndex])) atomicStorePointer(dataLoc, unsafe.Pointer(blockCache)) } result := &blockCache.block[resultIndex] return result } var isSinglePrefBlock *bool if prefixIndex == IPv6BitsPerSegment { isSinglePrefBlock = &trueVal } else { isSinglePrefBlock = &falseVal } return &ipv6SegmentValues{ value: value, upperValue: value, prefLen: prefLen, cache: divCache{ isSinglePrefBlock: isSinglePrefBlock, }, } } func newIPv6SegmentPrefixedValues(value, upperValue IPv6SegInt, prefLen PrefixLen) *ipv6SegmentValues { var isSinglePrefBlock *bool if prefLen == nil { if value == upperValue { return newIPv6SegmentVal(value) } else if value > upperValue { value, upperValue = upperValue, value } if useIPv6SegmentCache && value == 0 && upperValue == IPv6MaxValuePerSegment { return allRangeValsIPv6 } isSinglePrefBlock = &falseVal } else { if value == upperValue { return newIPv6SegmentPrefixedVal(value, prefLen) } else if value > upperValue { value, upperValue = upperValue, value } prefixIndex := prefLen.bitCount() if prefixIndex < 0 { prefixIndex = 0 } else if prefixIndex > IPv6BitsPerSegment { prefixIndex = IPv6BitsPerSegment } prefLen = cacheBitCount(prefixIndex) // this ensures we use the prefix length cache for all segments if useIPv6SegmentCache { shiftBits := uint(IPv6BitsPerSegment - prefixIndex) nmask := ^IPv6SegInt(0) << shiftBits prefixBlockLower := value & nmask hmask := ^nmask prefixBlockUpper := value | hmask if value == prefixBlockLower && upperValue == prefixBlockUpper { // cache is the prefix block for any prefix length cache := prefixBlocksCacheIPv6 prefixCache := (*ipv6DivsPartition)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache[prefixIndex])))) if prefixCache == nil { if prefixIndex <= 8 { // 1 block of size (1 << prefix) prefixCache = &ipv6DivsPartition{make([]*ipv6DivsBlock, 1)} } else { // (1 << (prefix - 8)) blocks of size 0x100. prefixCache = &ipv6DivsPartition{make([]*ipv6DivsBlock, 1<> shiftBits blockIndex := valueIndex >> 8 // divide by 0x100 firstBlockVal := blockIndex << 8 resultIndex := valueIndex - (firstBlockVal) // mod 0x100 blockCache := (*ipv6DivsBlock)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&prefixCache.block[blockIndex])))) if blockCache == nil { if prefixIndex <= 8 { // 1 block of size (1 << prefix) blockCache = &ipv6DivsBlock{make([]ipv6SegmentValues, 1<>2] newLower := (val.lower << IPv6BitsPerSegment) | uint64(seg.GetIPv6SegmentValue()) val.lower = newLower } section = upperIp.GetSection() divs = section.getDivArray() for i, div := range divs { seg := div.ToIPv6() val := &key.vals[i>>2] newUpper := (val.upper << IPv6BitsPerSegment) | uint64(seg.GetIPv6SegmentValue()) val.upper = newUpper } if isIP { key.addrType = ipv6Type } } else { // nether IPv4 nor IPv6, the zero IP address // key.addrType is zeroType } return } // SequentialRangeKey is a representation of SequentialRange that is comparable as defined by the language specification. // See https://go.dev/ref/spec#Comparison_operators // // It can be used as a map key. // The zero value is a range from a zero-length address to itself. type SequentialRangeKey[T SequentialRangeConstraint[T]] struct { vals [2]struct { lower, upper uint64 } addrType addrType // only used when T is *IPAddress to indicate version for non-zero valued address } // ToSeqRange converts back to a sequential range instance. func (key SequentialRangeKey[T]) ToSeqRange() *SequentialRange[T] { var lower, upper T var isMult bool isIP, isIPv4, isIPv6 := false, false, false anyt := any(lower) if _, isIPv4 = anyt.(*IPv4Address); !isIPv4 { if _, isIPv6 = anyt.(*IPv6Address); !isIPv6 { if _, isIP = anyt.(*IPAddress); isIP { addressType := key.addrType if isIPv4 = addressType.isIPv4(); !isIPv4 { if isIPv6 = addressType.isIPv6(); !isIPv6 { if isNeither := addressType.isZeroSegments(); isNeither { lower = any(zeroIPAddr).(T) upper = lower } else { panic("supports only IP addresses") } } } } else { panic("supports only IP addresses") } } } if isIPv6 { lower6 := NewIPv6AddressFromVals( func(segmentIndex int) IPv6SegInt { valsIndex := segmentIndex >> 2 segIndex := ((IPv6SegmentCount - 1) - segmentIndex) & 0x3 return IPv6SegInt(key.vals[valsIndex].lower >> (segIndex << ipv6BitsToSegmentBitshift)) }) upper6 := NewIPv6AddressFromVals( func(segmentIndex int) IPv6SegInt { valsIndex := segmentIndex >> 2 segIndex := ((IPv6SegmentCount - 1) - segmentIndex) & 0x3 return IPv6SegInt(key.vals[valsIndex].upper >> (segIndex << ipv6BitsToSegmentBitshift)) }) isMult = key.vals[1].lower != key.vals[1].upper || key.vals[0].lower != key.vals[0].upper if isIP { lower = any(lower6.ToIP()).(T) upper = any(upper6.ToIP()).(T) } else { lower = any(lower6).(T) upper = any(upper6).(T) } } else if isIPv4 { l := uint32(key.vals[0].lower) u := uint32(key.vals[0].upper) lower4 := NewIPv4AddressFromUint32(l) upper4 := NewIPv4AddressFromUint32(u) isMult = l != u if isIP { lower = any(lower4.ToIP()).(T) upper = any(upper4.ToIP()).(T) } else { lower = any(lower4).(T) upper = any(upper4).(T) } } return newSequRangeUnchecked(lower, upper, isMult) } // String calls the String method in the corresponding sequential range. func (key SequentialRangeKey[T]) String() string { return key.ToSeqRange().String() } // IPv4AddressKey is a representation of an IPv4 address that is comparable as defined by the language specification. // See https://go.dev/ref/spec#Comparison_operators // // It can be used as a map key. It can be obtained from its originating address instances. // The zero value corresponds to the zero-value for IPv4Address. // Keys do not incorporate prefix length to ensure that all equal addresses have equal keys. // To create a key that has prefix length, combine into a struct with the PrefixKey obtained by passing the address into PrefixKeyFrom. // IPv4Address can be compared using the Compare or Equal methods, or using an AddressComparator. type IPv4AddressKey struct { vals uint64 // upper and lower combined into one uint64 } // ToAddress converts back to an address instance. func (key IPv4AddressKey) ToAddress() *IPv4Address { return fromIPv4Key(key) } // String calls the String method in the corresponding address. func (key IPv4AddressKey) String() string { return key.ToAddress().String() } type testComparableConstraint[T comparable] struct{} var ( // ensure our 5 key types are indeed comparable _ testComparableConstraint[IPv4AddressKey] _ testComparableConstraint[IPv6AddressKey] _ testComparableConstraint[MACAddressKey] _ testComparableConstraint[Key[*IPAddress]] _ testComparableConstraint[Key[*Address]] //_ testComparableConstraint[RangeBoundaryKey[*IPv4Address]] // does not compile, as expected, because it has an interface field. But it is still go-comparable. ) // IPv6AddressKey is a representation of an IPv6 address that is comparable as defined by the language specification. // See https://go.dev/ref/spec#Comparison_operators // // It can be used as a map key. It can be obtained from its originating address instances. // The zero value corresponds to the zero-value for IPv6Address. // Keys do not incorporate prefix length to ensure that all equal addresses have equal keys. // To create a key that has prefix length, combine into a struct with the PrefixKey obtained by passing the address into PrefixKeyFrom. // IPv6Address can be compared using the Compare or Equal methods, or using an AddressComparator. type IPv6AddressKey struct { keyContents } // ToAddress converts back to an address instance. func (key IPv6AddressKey) ToAddress() *IPv6Address { return fromIPv6Key(key) } // String calls the String method in the corresponding address. func (key IPv6AddressKey) String() string { return key.ToAddress().String() } // MACAddressKey is a representation of a MAC address that is comparable as defined by the language specification. // See https://go.dev/ref/spec#Comparison_operators // // It can be used as a map key. It can be obtained from its originating address instances. // The zero value corresponds to the zero-value for MACAddress. // Keys do not incorporate prefix length to ensure that all equal addresses have equal keys. // To create a key that has prefix length, combine into a struct with the PrefixKey obtained by passing the address into PrefixKeyFrom. // MACAddress can be compared using the Compare or Equal methods, or using an AddressComparator. type MACAddressKey struct { vals struct { lower, upper uint64 } additionalByteCount uint8 // 0 for MediaAccessControlSegmentCount or 2 for ExtendedUniqueIdentifier64SegmentCount } // ToAddress converts back to an address instance. func (key MACAddressKey) ToAddress() *MACAddress { return fromMACKey(key) } // String calls the String method in the corresponding address. func (key MACAddressKey) String() string { return key.ToAddress().String() } // KeyConstraint is the generic type constraint for an address type that can be generated from a generic address key. type KeyConstraint[T any] interface { fmt.Stringer fromKey(addressScheme, *keyContents) T // implemented by IPAddress and Address } type addressScheme byte const ( adaptiveZeroScheme addressScheme = 0 // adaptiveZeroScheme needs to be zero, to coincide with the zero value for Address and IPAddress, which is a zero-length address ipv4Scheme addressScheme = 1 ipv6Scheme addressScheme = 2 mac48Scheme addressScheme = 3 eui64Scheme addressScheme = 4 ) // KeyGeneratorConstraint is the generic type constraint for an address type that can generate a generic address key. type KeyGeneratorConstraint[T KeyConstraint[T]] interface { ToGenericKey() Key[T] } // GenericKeyConstraint is the generic type constraint for an address type that can generate and be generated from a generic address key. type GenericKeyConstraint[T KeyConstraint[T]] interface { KeyGeneratorConstraint[T] KeyConstraint[T] } // Key is a representation of an address that is comparable as defined by the language specification. // See https://go.dev/ref/spec#Comparison_operators // // It can be used as a map key. It can be obtained from its originating address instances. // The zero value corresponds to the zero-value for its generic address type. // Keys do not incorporate prefix length to ensure that all equal addresses have equal keys. // To create a key that has prefix length, combine into a struct with the PrefixKey obtained by passing the address into PrefixKeyFrom. type Key[T KeyConstraint[T]] struct { scheme addressScheme keyContents } // ToAddress converts back to an address instance. func (key Key[T]) ToAddress() T { var t T return t.fromKey(key.scheme, &key.keyContents) } // String calls the String method in the corresponding address. func (key Key[T]) String() string { return key.ToAddress().String() } type keyContents struct { vals [2]struct { lower, upper uint64 } zone Zone } type ( AddressKey = Key[*Address] IPAddressKey = Key[*IPAddress] IPAddressSeqRangeKey = SequentialRangeKey[*IPAddress] IPv4AddressSeqRangeKey = SequentialRangeKey[*IPv4Address] IPv6AddressSeqRangeKey = SequentialRangeKey[*IPv6Address] ) var ( _ Key[*IPv4Address] _ Key[*IPv6Address] _ Key[*MACAddress] _ AddressKey _ IPAddressKey _ IPv4AddressKey _ IPv6AddressKey _ MACAddressKey _ IPAddressSeqRangeKey _ IPv4AddressSeqRangeKey _ IPv6AddressSeqRangeKey ) // PrefixKey is a representation of a prefix length that is comparable as defined by the language specification. // See https://go.dev/ref/spec#Comparison_operators // // It can be used as a map key. // The zero value is the absence of a prefix length. type PrefixKey struct { // If true, the prefix length is indicated by PrefixLen. // If false, this indicates no prefix length for the associated address or subnet. IsPrefixed bool // If IsPrefixed is true, this holds the prefix length. // Otherwise, this should be zero if you wish that each address has a unique key. PrefixLen PrefixBitCount } // ToPrefixLen converts this key to its corresponding prefix length. func (pref PrefixKey) ToPrefixLen() PrefixLen { if pref.IsPrefixed { return &pref.PrefixLen } return nil } func PrefixKeyFrom(addr AddressType) PrefixKey { if addr.IsPrefixed() { return PrefixKey{ IsPrefixed: true, PrefixLen: *addr.ToAddressBase().getPrefixLen(), // doing this instead of calling GetPrefixLen() on AddressType avoids the prefix len copy } } return PrefixKey{} } ipaddress-go-1.5.4/ipaddr/largedivision.go000066400000000000000000001141311440250641600205360ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "bytes" "fmt" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "math/big" "strings" "unsafe" ) // BigDivInt is an unsigned integer type for unlimited size division values. type BigDivInt = big.Int func newLargeDivValue(value []byte, bitCount BitCount) *largeDivValues { result := &largeDivValues{cache: divCache{}} result.value, bitCount, result.maxValue = setVal(value, bitCount) result.bitCount = bitCount result.upperValue = result.value result.upperValueMasked = result.upperValue result.cache.isSinglePrefBlock = &falseVal return result } func newLargeDivPrefixedValue(value []byte, prefLen PrefixLen, bitCount BitCount) *largeDivValues { result := &largeDivValues{cache: divCache{}} result.value, bitCount, result.maxValue = setVal(value, bitCount) result.bitCount = bitCount result.upperValue = result.value prefLen = checkPrefLen(prefLen, bitCount) result.prefLen = prefLen if prefLen != nil { if result.isPrefixBlock = prefLen.Len() == bitCount; result.isPrefixBlock { result.cache.isSinglePrefBlock = &trueVal result.upperValueMasked = result.upperValue } else { result.cache.isSinglePrefBlock = &falseVal result.upperValueMasked = setUpperValueMasked(result.value, result.upperValue, prefLen, bitCount) } } else { result.upperValueMasked = result.upperValue result.cache.isSinglePrefBlock = &falseVal } return result } func newLargeDivValues(value, upperValue []byte, bitCount BitCount) *largeDivValues { result := &largeDivValues{cache: divCache{}} result.value, result.upperValue, bitCount, result.maxValue = setVals(value, upperValue, bitCount) result.bitCount = bitCount result.isMult = result.value != result.upperValue result.upperValueMasked = result.upperValue result.cache.isSinglePrefBlock = &falseVal return result } func newLargeDivPrefixedValues(value, upperValue []byte, prefLen PrefixLen, bitCount BitCount) *largeDivValues { result := &largeDivValues{cache: divCache{}} result.value, result.upperValue, bitCount, result.maxValue = setVals(value, upperValue, bitCount) result.bitCount = bitCount prefLen = checkPrefLen(prefLen, bitCount) result.prefLen = prefLen result.isMult = result.value != result.upperValue var isSinglePrefBlock bool result.isPrefixBlock, isSinglePrefBlock, result.upperValueMasked = setCachedPrefixValues(result.value, result.upperValue, result.maxValue, prefLen, bitCount) if isSinglePrefBlock { result.cache.isSinglePrefBlock = &trueVal } else { result.cache.isSinglePrefBlock = &falseVal } return result } func newLargeDivValuesDivIntUnchecked(value, upperValue DivInt, prefLen PrefixLen, bitCount BitCount) *largeDivValues { result := &largeDivValues{ prefLen: prefLen, bitCount: bitCount, } val := new(big.Int).SetUint64(uint64(value)) if value == upperValue { result.value, result.upperValue = val, val } else { result.isMult = true result.value, result.upperValue = val, new(big.Int).SetUint64(uint64(upperValue)) } result.maxValue = setMax(result.upperValue, bitCount) var isSinglePrefBlock bool result.isPrefixBlock, isSinglePrefBlock, result.upperValueMasked = setCachedPrefixValues(result.value, result.upperValue, result.maxValue, prefLen, bitCount) if isSinglePrefBlock { result.cache.isSinglePrefBlock = &trueVal } else { result.cache.isSinglePrefBlock = &falseVal } return result } func newLargeDivValuesUnchecked(value, upperValue, maxValue *BigDivInt, isMult bool, prefLen PrefixLen, bitCount BitCount) *largeDivValues { result := &largeDivValues{ prefLen: prefLen, bitCount: bitCount, value: value, upperValue: upperValue, maxValue: maxValue, isMult: isMult, } var isSinglePrefBlock bool result.isPrefixBlock, isSinglePrefBlock, result.upperValueMasked = setCachedPrefixValues(result.value, result.upperValue, result.maxValue, prefLen, bitCount) if isSinglePrefBlock { result.cache.isSinglePrefBlock = &trueVal } else { result.cache.isSinglePrefBlock = &falseVal } return result } type largeDivValues struct { bitCount BitCount value *BigDivInt upperValue *BigDivInt // always points to value when single-valued maxValue *BigDivInt upperValueMasked *BigDivInt isPrefixBlock bool // note that isSinglePrefBlock is in the divCache isMult bool prefLen PrefixLen cache divCache } func (div *largeDivValues) getBitCount() BitCount { return div.bitCount } func (div *largeDivValues) getByteCount() int { return (int(div.getBitCount()) + 7) >> 3 } func (div *largeDivValues) getDivisionPrefixLength() PrefixLen { return div.prefLen } // for internal usage, this returns the cached value, so it cannot be changed nor returned to outside callers // the only place we need to clone is the methods GetValue() and GetUpperValue() that return to elsewhere func (div *largeDivValues) getValue() *BigDivInt { return div.value } // for internal usage, this returns the cached value, so it cannot be changed nor returned to outside callers // the only place we need to clone is the methods GetValue() and GetUpperValue() that return to elsewhere func (div *largeDivValues) getUpperValue() *BigDivInt { return div.upperValue } func (div *largeDivValues) includesZero() bool { return bigIsZero(div.value) } func (div *largeDivValues) includesMax() bool { return div.upperValue.Cmp(div.maxValue) == 0 } func (div *largeDivValues) isMultiple() bool { return div.isMult } func (div *largeDivValues) getCount() *big.Int { var res big.Int return res.Sub(div.upperValue, div.value).Add(&res, bigOneConst()) } func (div *largeDivValues) calcBytesInternal() (bytes, upperBytes []byte) { return div.value.Bytes(), div.upperValue.Bytes() } func (div *largeDivValues) bytesInternal(upper bool) (bytes []byte) { if upper { return div.upperValue.Bytes() } return div.value.Bytes() } func (div *largeDivValues) getCache() *divCache { return &div.cache } func (div *largeDivValues) getAddrType() addrType { return zeroType } func (div *largeDivValues) getDivisionValue() DivInt { return DivInt(div.value.Uint64()) } func (div *largeDivValues) getUpperDivisionValue() DivInt { return DivInt(div.upperValue.Uint64()) } func (div *largeDivValues) getSegmentValue() SegInt { return SegInt(div.value.Uint64()) } func (div *largeDivValues) getUpperSegmentValue() SegInt { return SegInt(div.upperValue.Uint64()) } func (div *largeDivValues) deriveNew(val, upperVal DivInt, prefLen PrefixLen) divisionValues { return newLargeDivValuesDivIntUnchecked(val, upperVal, prefLen, div.bitCount) } func (div *largeDivValues) derivePrefixed(prefLen PrefixLen) divisionValues { return newLargeDivValuesUnchecked(div.value, div.upperValue, div.maxValue, div.isMult, prefLen, div.bitCount) } func (div *largeDivValues) deriveNewMultiSeg(val, upperVal SegInt, prefLen PrefixLen) divisionValues { return newLargeDivValuesDivIntUnchecked(DivInt(val), DivInt(upperVal), prefLen, div.bitCount) } func (div *largeDivValues) deriveNewSeg(val SegInt, prefLen PrefixLen) divisionValues { return newLargeDivValuesDivIntUnchecked(DivInt(val), DivInt(val), prefLen, div.bitCount) } var _ divisionValues = &largeDivValues{} func createLargeAddressDiv(vals divisionValues, defaultRadix int) *IPAddressLargeDivision { res := &IPAddressLargeDivision{ addressLargeDivInternal{ addressDivisionBase: addressDivisionBase{vals}, }, } if defaultRadix >= 2 { res.defaultRadix = new(big.Int).SetInt64(int64(defaultRadix)) } return res } type addressLargeDivInternal struct { addressDivisionBase defaultRadix *BigDivInt } func (div *addressLargeDivInternal) getDefaultRadix() int { rad := div.defaultRadix if rad == nil { return 16 // use same default as other divisions when zero div } return int(rad.Int64()) } func (div *addressLargeDivInternal) toLargeAddressDivision() *IPAddressLargeDivision { return (*IPAddressLargeDivision)(unsafe.Pointer(div)) } func (div *addressLargeDivInternal) getLargeDivValues() *largeDivValues { vals := div.divisionValues if vals == nil { return nil } return vals.(*largeDivValues) } // returns the default radix for textual representations of divisions func (div *addressLargeDivInternal) getBigDefaultTextualRadix() *big.Int { if div.divisionValues == nil || div.defaultRadix == nil { return bigSixteen() // use same default as other divisions when zero div } return div.defaultRadix } // returns the default radix for textual representations of divisions func (div *addressLargeDivInternal) getDefaultTextualRadix() int { if div.divisionValues == nil || div.defaultRadix == nil { return 16 // use same default as other divisions when zero div } return int(div.defaultRadix.Int64()) } // toString produces a string that is useful when a division string is provided with no context. // It uses a string prefix for octal or hex ("0" or "0x"), and does not use the wildcard '*', because division size is variable, so '*' is ambiguous. // GetWildcardString() is more appropriate in context with other segments or divisions. It does not use a string prefix and uses '*' for full-range segments. // GetString() is more appropriate in context with prefix lengths, it uses zeros instead of wildcards for prefix block ranges. func (div *addressLargeDivInternal) toString() string { // this can be moved to addressDivisionBase when we have ContainsPrefixBlock and similar methods implemented for big.Int in the base return toString(div.toLargeAddressDivision()) } // Format implements [fmt.Formatter] interface. It accepts the formats // - 'v' for the default address and section format (either the normalized or canonical string), // - 's' (string) for the same, // - 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix), // - 'd' (decimal), 'x' (lowercase hexadecimal), and // - 'X' (uppercase hexadecimal). // Also supported are some of fmt's format flags for integral types. // Sign control is not supported since addresses and sections are never negative. // '#' for an alternate format is supported, which adds a leading zero for octal, and for hexadecimal it adds // a leading "0x" or "0X" for "%#x" and "%#X" respectively. // Also supported is specification of minimum digits precision, output field width, // space or zero padding, and '-' for left or right justification. func (div addressLargeDivInternal) Format(state fmt.State, verb rune) { switch verb { case 's', 'v': _, _ = state.Write([]byte(div.toString())) return } // we try to filter through the flags provided to the DivInt values, as if the fmt string were applied to the int(s) directly formatStr := flagsFromState(state, verb) if div.isMultiple() { formatStr = fmt.Sprintf("%s%c%s", formatStr, RangeSeparator, formatStr) _, _ = state.Write([]byte(fmt.Sprintf(formatStr, div.getValue(), div.getUpperValue()))) } else { _, _ = state.Write([]byte(fmt.Sprintf(formatStr, div.getValue()))) } } // NewIPAddressLargeDivision creates a division of the given arbitrary bit-length, assigning it the given value. // If the value's bit length exceeds the given bit length, it is truncated. func NewIPAddressLargeDivision(val []byte, bitCount BitCount, defaultRadix int) *IPAddressLargeDivision { return createLargeAddressDiv(newLargeDivValue(val, bitCount), defaultRadix) } // NewIPAddressLargeRangeDivision creates a division of the given arbitrary bit-length, assigning it the given value range. // If a value's bit length exceeds the given bit length, it is truncated. func NewIPAddressLargeRangeDivision(val, upperVal []byte, bitCount BitCount, defaultRadix int) *IPAddressLargeDivision { return createLargeAddressDiv(newLargeDivValues(val, upperVal, bitCount), defaultRadix) } // NewIPAddressLargePrefixDivision creates a division of the given arbitrary bit-length, assigning it the given value and prefix length. // If the value's bit length exceeds the given bit length, it is truncated. // If the prefix length exceeds the bit length, it is adjusted to the bit length. If the prefix length is negative, it is adjusted to zero. func NewIPAddressLargePrefixDivision(val []byte, prefixLen PrefixLen, bitCount BitCount, defaultRadix int) *IPAddressLargeDivision { return createLargeAddressDiv(newLargeDivPrefixedValue(val, prefixLen, bitCount), defaultRadix) } // NewIPAddressLargeRangePrefixDivision creates a division of the given arbitrary bit-length, assigning it the given value range and prefix length. // If a value's bit length exceeds the given bit length, it is truncated. // If the prefix length exceeds the bit length, it is adjusted to the bit length. If the prefix length is negative, it is adjusted to zero. func NewIPAddressLargeRangePrefixDivision(val, upperVal []byte, prefixLen PrefixLen, bitCount BitCount, defaultRadix int) *IPAddressLargeDivision { return createLargeAddressDiv(newLargeDivPrefixedValues(val, upperVal, prefixLen, bitCount), defaultRadix) } // IPAddressLargeDivision represents an arbitrary division of arbitrary bit-size in an address or address division grouping. // It can contain a single value or a range of sequential values and it has an assigned bit length. // Like all address components, it is immutable. type IPAddressLargeDivision struct { addressLargeDivInternal } // GetValue returns the lowest value in the address division range as a big integer. func (div *IPAddressLargeDivision) GetValue() *BigDivInt { return new(big.Int).Set(div.addressLargeDivInternal.GetValue()) } // GetUpperValue returns the highest value in the address division range as a big integer. func (div *IPAddressLargeDivision) GetUpperValue() *BigDivInt { return new(big.Int).Set(div.addressLargeDivInternal.GetUpperValue()) } // GetDivisionPrefixLen returns the network prefix for the division. // // The network prefix is 16 for an address like "1.2.0.0/16". // // When it comes to each address division or segment, the prefix for the division is the // prefix obtained when applying the address or section prefix. // // For instance, consider the address "1.2.0.0/20". // The first segment has no prefix because the address prefix 20 extends beyond the 8 bits in the first segment, it does not even apply to the segment. // The second segment has no prefix because the address prefix extends beyond bits 9 to 16 which lie in the second segment, it does not apply to that segment either. // The third segment has the prefix 4 because the address prefix 20 corresponds to the first 4 bits in the 3rd segment, // which means that the first 4 bits are part of the network section of the address or segment. // The last segment has the prefix 0 because not a single bit is in the network section of the address or segment // // The division prefixes applied across the address are: nil ... nil (1 to segment bit length) 0 ... 0. // // If the division has no prefix then nil is returned. func (div *IPAddressLargeDivision) GetDivisionPrefixLen() PrefixLen { return div.getDivisionPrefixLength() } // GetCount returns the count of possible distinct values for this division. // If not representing multiple values, the count is 1. // // For instance, a division with the value range of 3-7 has count 5. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (div *IPAddressLargeDivision) GetCount() *big.Int { if div == nil { return bigZero() } return div.getCount() } // IsMultiple returns whether this division represents a sequential range of values, vs a single value func (div *IPAddressLargeDivision) IsMultiple() bool { return div != nil && div.isMultiple() } func testBigRangeMasks(lowerValue, upperValue, finalUpperValue, networkMask, hostMask *BigDivInt) bool { var one, two big.Int return lowerValue.CmpAbs(one.And(lowerValue, networkMask)) == 0 && finalUpperValue.CmpAbs(two.Or(upperValue, hostMask)) == 0 } func testBigRange(lowerValue, upperValue, finalUpperValue *BigDivInt, bitCount, divisionPrefixLen BitCount) bool { var networkMask, hostMask big.Int networkMask.Lsh(bigMinusOneConst(), uint(bitCount-divisionPrefixLen)) hostMask.Not(&networkMask) return testBigRangeMasks(lowerValue, upperValue, finalUpperValue, &networkMask, &hostMask) } // ContainsPrefixBlock returns whether the division range includes the block of values for the given prefix length. func (div *IPAddressLargeDivision) ContainsPrefixBlock(prefixLen BitCount) bool { bitCount := div.GetBitCount() if prefixLen <= 0 { return div.IsFullRange() } else if prefixLen >= bitCount { return true } lower, upper := div.getValue(), div.getUpperValue() return testBigRange(lower, upper, upper, bitCount, prefixLen) } // ContainsSinglePrefixBlock returns whether the division range matches exactly the block of values for the given prefix length and has just a single prefix for that prefix length. func (div *IPAddressLargeDivision) ContainsSinglePrefixBlock(prefixLen BitCount) bool { bitCount := div.GetBitCount() prefixLen = checkBitCount(prefixLen, bitCount) if prefixLen == 0 { return div.IsFullRange() } lower, upper := div.getValue(), div.getUpperValue() return testBigRange(lower, lower, upper, bitCount, prefixLen) } // GetPrefixLenForSingleBlock returns a prefix length for which there is only one prefix in this division, // and the range of values in this division matches the block of all values for that prefix. // // If the range of division values can be described this way, then this method returns the same value as GetMinPrefixLenForBlock. // // If no such prefix length exists, returns nil. // // If this division represents a single value, this returns the bit count of the segment. func (div *IPAddressLargeDivision) GetPrefixLenForSingleBlock() PrefixLen { prefLen := div.GetMinPrefixLenForBlock() bitCount := div.GetBitCount() if prefLen == bitCount { if !div.IsMultiple() { result := PrefixBitCount(prefLen) return &result } } else { lower, upper := div.getValue(), div.getUpperValue() shift := uint(bitCount - prefLen) var one, two big.Int if one.Rsh(lower, shift).Cmp(two.Rsh(upper, shift)) == 0 { result := PrefixBitCount(prefLen) return &result } } return nil } // GetMinPrefixLenForBlock returns the smallest prefix length such that this division includes the block of all values for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this division represents a single value, this returns the bit count. func (div *IPAddressLargeDivision) GetMinPrefixLenForBlock() BitCount { result := div.GetBitCount() if div.IsMultiple() { lower, upper := div.getValue(), div.getUpperValue() lowerZeros := lower.TrailingZeroBits() if lowerZeros != 0 { var upperNot big.Int upperOnes := upperNot.Not(upper).TrailingZeroBits() if upperOnes != 0 { prefixedBitCount := BitCount(umin(lowerZeros, upperOnes)) result -= prefixedBitCount } } } return result } // Compare returns a negative integer, zero, or a positive integer if this address division is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (div *IPAddressLargeDivision) Compare(item AddressItem) int { return CountComparator.Compare(div, item) } // CompareSize compares the counts of two items, the number of individual values within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether one represents more individual values than another. // // CompareSize returns a positive integer if this division has a larger count than the item given, zero if they are the same, or a negative integer if the other has a larger count. func (div *IPAddressLargeDivision) CompareSize(other AddressItem) int { if div == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return compareCount(div, other) } // String produces a string that is useful when a division string is provided with no context. // It uses a string prefix for octal or hex ("0" or "0x"), and does not use the wildcard '*', because division size is variable, so '*' is ambiguous. // GetWildcardString is more appropriate in context with other segments or divisions. It does not use a string prefix and uses '*' for full-range segments. // GetString is more appropriate in context with prefix lengths, it uses zeros instead of wildcards for prefix block ranges. func (div *IPAddressLargeDivision) String() string { if div == nil { return nilString() } return div.toString() } func (div *IPAddressLargeDivision) getStringAsLower() string { stringer := div.getDefaultLowerString if div.divisionValues != nil { if cache := div.getCache(); cache != nil { return cacheStr(&cache.cachedString, stringer) } } return stringer() } // GetString produces a normalized string to represent the segment. // If the segment is an IP segment string with CIDR network prefix block for its prefix length, then the string contains only the lower value of the block range. // Otherwise, the explicit range will be printed. // If the segment is not an IP segment, then the string is the same as that produced by GetWildcardString. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (div *IPAddressLargeDivision) GetString() string { stringer := func() string { if div.IsSinglePrefixBlock() || !div.isMultiple() { //covers the case of single addresses, when there is no prefix or the prefix is the bit count return div.getDefaultLowerString() } else { if div.IsPrefixBlock() { return div.getDefaultMaskedRangeString() } return div.getDefaultRangeString() } } if div.divisionValues != nil { if cache := div.getCache(); cache != nil { return cacheStr(&cache.cachedString, stringer) } } return stringer() } // GetWildcardString produces a normalized string to represent the segment, favouring wildcards and range characters regardless of any network prefix length. // The explicit range of a range-valued segment will be printed. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and the bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (div *IPAddressLargeDivision) GetWildcardString() string { stringer := func() string { if !div.IsPrefixed() || !div.isMultiple() { return div.GetString() } return div.getDefaultRangeString() } if div.divisionValues != nil { if cache := div.getCache(); cache != nil { return cacheStr(&cache.cachedWildcardString, stringer) } } return stringer() } // IsSinglePrefix returns true if the division value range spans just a single prefix value for the given prefix length. func (div *IPAddressLargeDivision) IsSinglePrefix(divisionPrefixLen BitCount) bool { lower, upper := div.getValue(), div.getUpperValue() bitCount := div.GetBitCount() divisionPrefixLen = checkBitCount(divisionPrefixLen, bitCount) shift := uint(bitCount - divisionPrefixLen) var one, two big.Int return one.Rsh(lower, shift).Cmp(two.Rsh(upper, shift)) == 0 } func (div *IPAddressLargeDivision) getLowerStringLength(radix int) int { return getBigDigitCount(div.getValue(), div.getBigRadix(radix)) } func (div *IPAddressLargeDivision) getUpperStringLength(radix int) int { return getBigDigitCount(div.getUpperValue(), div.getBigRadix(radix)) } func (div *IPAddressLargeDivision) getLowerString(radix int, uppercase bool, appendable *strings.Builder) { appendable.WriteString(div.toDefaultString(div.getValue(), radix, uppercase, 0)) } func (div *IPAddressLargeDivision) getLowerStringChopped(radix int, choppedDigits int, uppercase bool, appendable *strings.Builder) { appendable.WriteString(div.toDefaultString(div.getValue(), radix, uppercase, choppedDigits)) } func (div *IPAddressLargeDivision) getUpperString(radix int, uppercase bool, appendable *strings.Builder) { appendable.WriteString(div.toDefaultString(div.getUpperValue(), radix, uppercase, 0)) } func (div *IPAddressLargeDivision) getUpperStringMasked(radix int, uppercase bool, appendable *strings.Builder) { appendable.WriteString(div.toDefaultString(div.getLargeDivValues().upperValueMasked, radix, uppercase, 0)) } func (div *IPAddressLargeDivision) toDefaultString(val *BigDivInt, radix int, uppercase bool, choppedDigits int) string { return toDefaultBigString(val, div.getBigRadix(radix), uppercase, choppedDigits, getBigMaxDigitCount(radix, div.GetBitCount(), div.getLargeDivValues().maxValue)) } func (div *IPAddressLargeDivision) getBigRadix(radix int) *big.Int { defaultRadix := div.getDefaultTextualRadix() if defaultRadix == radix { return div.getBigDefaultTextualRadix() } return big.NewInt(int64(radix)) } func (div *IPAddressLargeDivision) getSplitLowerString(radix int, choppedDigits int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string, appendable *strings.Builder) { var builder strings.Builder div.getLowerStringChopped(radix, choppedDigits, uppercase, &builder) str := builder.String() length := len(str) prefLen := len(stringPrefix) for i := 0; i < length; i++ { if i > 0 { appendable.WriteByte(splitDigitSeparator) } if prefLen > 0 { appendable.WriteString(stringPrefix) } if reverseSplitDigits { appendable.WriteByte(str[length-i-1]) } else { appendable.WriteByte(str[i]) } } } func (div *IPAddressLargeDivision) getSplitRangeString(rangeSeparator string, wildcard string, radix int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string, appendable *strings.Builder) addrerr.IncompatibleAddressError { var lowerBuilder, upperBuilder strings.Builder div.getLowerString(radix, uppercase, &lowerBuilder) div.getUpperString(radix, uppercase, &upperBuilder) diff := upperBuilder.Len() - lowerBuilder.Len() if diff > 0 { lowerStr := lowerBuilder.String() lowerBuilder.Reset() for ; diff > 0; diff-- { lowerBuilder.WriteByte('0') } lowerBuilder.WriteString(lowerStr) } previousWasFull, nextMustBeFull := true, false dig := getDigits(uppercase, radix) zeroDigit := dig[0] highestDigit := dig[radix-1] lowerStr := lowerBuilder.String() upperStr := upperBuilder.String() length := len(lowerStr) prefLen := len(stringPrefix) for i := 0; i < length; i++ { var index int if reverseSplitDigits { index = length - i - 1 } else { index = 1 } lower := lowerStr[index] upper := upperStr[index] if i > 0 { appendable.WriteByte(splitDigitSeparator) } if lower == upper { if nextMustBeFull { return &incompatibleAddressError{addressError{key: "ipaddress.error.splitMismatch"}} } if prefLen > 0 { appendable.WriteString(stringPrefix) } appendable.WriteByte(lower) } else { isFullRange := (lower == zeroDigit) && (upper == highestDigit) if isFullRange { appendable.WriteString(wildcard) } else { if nextMustBeFull { return &incompatibleAddressError{addressError{key: "ipaddress.error.splitMismatch"}} } if prefLen > 0 { appendable.WriteString(stringPrefix) } appendable.WriteByte(lower) appendable.WriteString(rangeSeparator) appendable.WriteByte(upper) } if reverseSplitDigits { if !previousWasFull { return &incompatibleAddressError{addressError{key: "ipaddress.error.splitMismatch"}} } previousWasFull = isFullRange } else { nextMustBeFull = true } } } return nil } func (div *IPAddressLargeDivision) getSplitRangeStringLength(rangeSeparator string, wildcard string, leadingZeroCount int, radix int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string) int { _, _, _ = rangeSeparator, splitDigitSeparator, reverseSplitDigits digitsLength := -1 stringPrefixLength := len(stringPrefix) var lowerBuilder, upperBuilder strings.Builder div.getLowerString(radix, uppercase, &lowerBuilder) div.getUpperString(radix, uppercase, &upperBuilder) dig := getDigits(uppercase, radix) zeroDigit := dig[0] highestDigit := dig[radix-1] remainingAfterLoop := leadingZeroCount lowerStr := lowerBuilder.String() upperStr := upperBuilder.String() upperLength := len(upperStr) lowerLength := len(lowerStr) for i := 1; i < upperLength; i++ { var lower byte if i <= lowerLength { lower = lowerStr[lowerLength-i] } upperIndex := upperLength - i upper := upperStr[upperIndex] isFullRange := (lower == zeroDigit) && (upper == highestDigit) if isFullRange { digitsLength += len(wildcard) + 1 } else if lower != upper { digitsLength += (stringPrefixLength << 1) + 4 //1 for each digit, 1 for range separator, 1 for split digit separator } else { //this and any remaining must be singles remainingAfterLoop += upperIndex + 1 break } } if remainingAfterLoop > 0 { digitsLength += remainingAfterLoop * (stringPrefixLength + 2) // one for each splitDigitSeparator, 1 for each digit } return digitsLength } func (div *IPAddressLargeDivision) getRangeDigitCount(radix int) int { if !div.IsMultiple() { return 0 } val, upperVal := div.getValue(), div.getUpperValue() count := 1 bigRadix := big.NewInt(int64(radix)) bigUpperDigit := big.NewInt(int64(radix - 1)) var quotient, upperQuotient, remainder big.Int for { quotient.QuoRem(val, bigRadix, &remainder) if bigIsZero(&remainder) { upperQuotient.QuoRem(upperVal, bigRadix, &remainder) if remainder.CmpAbs(bigUpperDigit) == 0 { val, upperVal = "ient, &upperQuotient if val.CmpAbs(upperVal) == 0 { return count } else { count++ continue } } } return 0 } } func (div *IPAddressLargeDivision) adjustLowerLeadingZeroCount(leadingZeroCount int, radix int) int { return div.adjustLeadingZeroCount(leadingZeroCount, div.getValue(), radix) } func (div *IPAddressLargeDivision) adjustUpperLeadingZeroCount(leadingZeroCount int, radix int) int { return div.adjustLeadingZeroCount(leadingZeroCount, div.getUpperValue(), radix) } func (div *IPAddressLargeDivision) adjustLeadingZeroCount(leadingZeroCount int, value *BigDivInt, radix int) int { if leadingZeroCount < 0 { width := div.getDigitCount(value, radix) return max(0, div.getMaxDigitCountRadix(radix)-width) } return leadingZeroCount } func (div *IPAddressLargeDivision) getDigitCount(val *BigDivInt, radix int) int { vals := div.divisionValues if vals == nil { return 1 } var bigRadix *big.Int if div.getDefaultTextualRadix() == radix { bigRadix = div.getBigDefaultTextualRadix() } else { bigRadix = big.NewInt(int64(radix)) } return getBigDigitCount(val, bigRadix) } func (div *IPAddressLargeDivision) getMaxDigitCountRadix(radix int) int { bc := div.GetBitCount() vals := div.getLargeDivValues() var maxValue *BigDivInt if vals == nil { maxValue = bigZeroConst() } else { maxValue = vals.maxValue } return getBigMaxDigitCount(radix, bc, maxValue) } func (div *IPAddressLargeDivision) getMaxDigitCount() int { rad := div.getDefaultTextualRadix() bc := div.GetBitCount() vals := div.getLargeDivValues() var maxValue *BigDivInt if vals == nil { maxValue = bigZeroConst() } else { maxValue = vals.maxValue } return getBigMaxDigitCount(rad, bc, maxValue) } func (div *IPAddressLargeDivision) getDefaultLowerString() string { val := div.GetValue() rad := div.getBigDefaultTextualRadix() mdg := div.getMaxDigitCount() return toDefaultBigString(val, rad, false, 0, mdg) } func (div *IPAddressLargeDivision) getDefaultRangeString() string { maxDigitCount := div.getMaxDigitCount() radix := div.getBigDefaultTextualRadix() return toDefaultBigString(div.getValue(), radix, false, 0, maxDigitCount) + div.getDefaultRangeSeparatorString() + toDefaultBigString(div.getUpperValue(), radix, false, 0, maxDigitCount) } func (div *IPAddressLargeDivision) getDefaultMaskedRangeString() string { maxDigitCount := div.getMaxDigitCount() radix := div.getBigDefaultTextualRadix() return toDefaultBigString(div.getValue(), radix, false, 0, maxDigitCount) + div.getDefaultRangeSeparatorString() + toDefaultBigString(div.getLargeDivValues().upperValueMasked, radix, false, 0, maxDigitCount) } func (div *IPAddressLargeDivision) isExtendedDigits() bool { return isExtendedDigits(div.getDefaultTextualRadix()) } func (div *IPAddressLargeDivision) getDefaultRangeSeparatorString() string { if div.isExtendedDigits() { return ExtendedDigitsRangeSeparatorStr } return RangeSeparatorStr } // IsPrefixBlock returns whether the division has a prefix length and the division range includes the block of values for that prefix length. // If the prefix length matches the bit count, this returns true. func (div *IPAddressLargeDivision) IsPrefixBlock() bool { return div.getLargeDivValues().isPrefixBlock } // IsSinglePrefixBlock returns whether the division range matches the block of values for its prefix length func (div *IPAddressLargeDivision) IsSinglePrefixBlock() bool { return *div.getLargeDivValues().cache.isSinglePrefBlock } // IsPrefixed returns whether this division has an associated prefix length. // If so, the prefix length is given by GetDivisionPrefixLen() func (div *IPAddressLargeDivision) IsPrefixed() bool { return div.GetDivisionPrefixLen() != nil } // GetPrefixLen returns the network prefix for the division. // // The network prefix is 16 for an address like "1.2.0.0/16". // // When it comes to each address division or segment, the prefix for the division is the // prefix obtained when applying the address or section prefix. // // For instance, consider the address "1.2.0.0/20". // The first segment has no prefix because the address prefix 20 extends beyond the 8 bits in the first segment, it does not even apply to the segment. // The second segment has no prefix because the address prefix extends beyond bits 9 to 16 which lie in the second segment, it does not apply to that segment either. // The third segment has the prefix 4 because the address prefix 20 corresponds to the first 4 bits in the 3rd segment, // which means that the first 4 bits are part of the network section of the address or segment. // The last segment has the prefix 0 because not a single bit is in the network section of the address or segment // // The division prefixes applied across the address are: nil ... nil (1 to segment bit length) 0 ... 0. // // If the segment has no prefix then nil is returned. func (div *IPAddressLargeDivision) GetPrefixLen() PrefixLen { return div.getDivisionPrefixLength() } func (div *IPAddressLargeDivision) isNil() bool { return div == nil } func setVal(valueBytes []byte, bitCount BitCount) (assignedValue *BigDivInt, assignedBitCount BitCount, maxVal *BigDivInt) { if bitCount < 0 { bitCount = 0 } assignedBitCount = bitCount maxLen := (bitCount + 7) >> 3 if len(valueBytes) >= maxLen { valueBytes = valueBytes[:maxLen] } assignedValue = new(big.Int).SetBytes(valueBytes) maxVal = setMax(assignedValue, bitCount) return } func setVals(valueBytes []byte, upperBytes []byte, bitCount BitCount) (assignedValue, assignedUpper *BigDivInt, assignedBitCount BitCount, maxVal *BigDivInt) { if bitCount < 0 { bitCount = 0 } assignedBitCount = bitCount maxLen := (bitCount + 7) >> 3 if len(valueBytes) >= maxLen || len(upperBytes) >= maxLen { extraBits := bitCount & 7 mask := byte(0xff) if extraBits > 0 { mask = ^(mask << uint(8-extraBits)) } if len(valueBytes) >= maxLen { valueBytes = valueBytes[len(valueBytes)-maxLen:] b := valueBytes[0] if b&mask != b { valueBytes = cloneBytes(valueBytes) valueBytes[0] &= mask } } if len(upperBytes) >= maxLen { upperBytes = upperBytes[len(upperBytes)-maxLen:] b := upperBytes[0] if b&mask != b { upperBytes = cloneBytes(upperBytes) upperBytes[0] &= mask } } } assignedValue = new(big.Int).SetBytes(valueBytes) if upperBytes == nil || bytes.Compare(valueBytes, upperBytes) == 0 { assignedUpper = assignedValue } else { assignedUpper = new(big.Int).SetBytes(upperBytes) cmp := assignedValue.CmpAbs(assignedUpper) if cmp == 0 { assignedUpper = assignedValue } else if cmp > 0 { // flip them assignedValue, assignedUpper = assignedUpper, assignedValue } } maxVal = setMax(assignedUpper, bitCount) return } func setMax(assignedUpper *BigDivInt, bitCount BitCount) (max *BigDivInt) { var maxVal big.Int max = maxVal.Lsh(bigOneConst(), uint(bitCount)).Sub(&maxVal, bigOneConst()) if max.CmpAbs(assignedUpper) == 0 { max = assignedUpper } return } func setUpperValueMasked(value, upperValue *BigDivInt, prefLen PrefixLen, bitCount BitCount) *BigDivInt { var networkMask big.Int networkMask.Lsh(bigMinusOneConst(), uint(bitCount-prefLen.Len())).And(upperValue, &networkMask) if networkMask.Cmp(upperValue) == 0 { return upperValue } else if networkMask.Cmp(value) == 0 { return value } return &networkMask } func setCachedPrefixValues(value, upperValue, maxValue *BigDivInt, prefLen PrefixLen, bitCount BitCount) (isPrefixBlock, isSinglePrefBlock bool, upperValueMasked *BigDivInt) { if prefLen != nil { if prefLen.Len() == bitCount { isPrefixBlock = true isSinglePrefBlock = value == upperValue upperValueMasked = upperValue } else if prefLen.Len() == 0 { valIsZero := bigIsZero(value) isFullRange := valIsZero && upperValue == maxValue isPrefixBlock = isFullRange isSinglePrefBlock = isFullRange if valIsZero { upperValueMasked = value } else { upperValueMasked = bigZeroConst() } } else { prefixLen := prefLen.Len() isPrefixBlock = testBigRange(value, upperValue, upperValue, bitCount, prefixLen) isSinglePrefBlock = testBigRange(value, value, upperValue, bitCount, prefixLen) upperValueMasked = setUpperValueMasked(value, upperValue, prefLen, bitCount) } } else { upperValueMasked = upperValue } return } ipaddress-go-1.5.4/ipaddr/largegrouping.go000066400000000000000000000505521440250641600205520ustar00rootroot00000000000000package ipaddr import ( "fmt" "math/big" ) func createLargeGrouping(divs []*IPAddressLargeDivision) *IPAddressLargeDivisionGrouping { addrType := zeroType grouping := &IPAddressLargeDivisionGrouping{ largeDivisionGroupingInternal{ addressDivisionGroupingBase: addressDivisionGroupingBase{ divisions: largeDivArray(divs), addrType: addrType, cache: &valueCache{}, }, }, } assignStringCache(&grouping.addressDivisionGroupingBase, addrType) return grouping } type largeDivisionGroupingInternal struct { addressDivisionGroupingBase } func (grouping *largeDivisionGroupingInternal) getDivArray() largeDivArray { if divsArray := grouping.divisions; divsArray != nil { return divsArray.(largeDivArray) } return nil } func (grouping *largeDivisionGroupingInternal) getDivisionCount() int { if divArray := grouping.getDivArray(); divArray != nil { return divArray.getDivisionCount() } return 0 } // getDivision returns the division or panics if the index is negative or too large func (grouping *largeDivisionGroupingInternal) getDivision(index int) *IPAddressLargeDivision { return grouping.getDivArray()[index] } func (grouping *largeDivisionGroupingInternal) initMultiple() { divCount := grouping.getDivisionCount() for i := divCount - 1; i >= 0; i-- { div := grouping.getDivision(i) if div.isMultiple() { grouping.isMult = true return } } return } // divisions are printed like slices of *IPAddressLargeDivision (which are Stringers) with division separated by spaces and enclosed in square brackets, // sections are printed like addresses with segments separated by segment separators func (grouping largeDivisionGroupingInternal) Format(state fmt.State, verb rune) { arr := grouping.initDivs().getDivArray() if len(arr) == 0 { return } s := flagsFromState(state, verb) _, _ = state.Write([]byte(fmt.Sprintf(s, arr))) } func (grouping *largeDivisionGroupingInternal) toString() string { return fmt.Sprint(grouping.initDivs().getDivArray()) } var zeroLargeGrouping = createLargeGrouping(zeroLargeDivs) func (grouping *largeDivisionGroupingInternal) initDivs() *largeDivisionGroupingInternal { if grouping.divisions == nil { return &zeroLargeGrouping.largeDivisionGroupingInternal } return grouping } func (grouping *largeDivisionGroupingInternal) getBytes() (bytes []byte) { bytes, _ = grouping.getCachedBytes(grouping.calcBytes) return } func (grouping *largeDivisionGroupingInternal) getUpperBytes() (bytes []byte) { _, bytes = grouping.getCachedBytes(grouping.calcBytes) return } func (grouping *largeDivisionGroupingInternal) calcBytes() (bytes, upperBytes []byte) { divisionCount := grouping.GetDivisionCount() isMultiple := grouping.isMultiple() byteCount := grouping.GetByteCount() bytes = make([]byte, byteCount) if isMultiple { upperBytes = make([]byte, byteCount) } else { upperBytes = bytes } // for each division in reverse order for k, byteIndex, bitIndex := divisionCount-1, byteCount-1, BitCount(8); k >= 0; k-- { div := grouping.getDivision(k) bigBytes := div.getValue().Bytes() var bigUpperBytes []byte if isMultiple { bigUpperBytes = div.getUpperValue().Bytes() } // for each 64 bits of the division in reverse order for totalDivBits := div.GetBitCount(); totalDivBits > 0; totalDivBits -= 64 { // grab those 64 bits (from bigBytes and bigUpperBytes) and put them in val and upperVal divBits := min(totalDivBits, 64) var divBytes []byte var val, upperVal uint64 if len(bigBytes) > 8 { byteLen := len(bigBytes) - 8 divBytes = bigBytes[byteLen:] bigBytes = bigBytes[:byteLen] } else { divBytes = bigBytes bigBytes = nil } for _, b := range divBytes { val = (val << 8) | uint64(b) } if isMultiple { var divUpperBytes []byte if len(upperBytes) > 8 { byteLen := len(bigUpperBytes) - 8 divUpperBytes = bigBytes[byteLen:] bigUpperBytes = bigBytes[:byteLen] } else { divUpperBytes = bigUpperBytes bigUpperBytes = nil } for _, b := range divUpperBytes { upperVal = (upperVal << 8) | uint64(b) } } // insert the 64 bits into the bytes slice for divBits > 0 { rbi := 8 - bitIndex bytes[byteIndex] |= byte(val << uint(rbi)) val >>= uint(bitIndex) if isMultiple { upperBytes[byteIndex] |= byte(upperVal << uint(rbi)) upperVal >>= uint(bitIndex) } if divBits < bitIndex { // bitIndex is the index into the last copied byte that was already occupied previously // so here we were able to copy all the bits and there was still space left over bitIndex -= divBits break } else { // we used up all the space available // if we also copied all the bits, then divBits will be assigned zero // otherwise it will have the number of bits still left to copy divBits -= bitIndex bitIndex = 8 byteIndex-- } } } } return } // CopyBytes copies the value of the lowest division grouping in the range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. // // You can use GetByteCount to determine the required array length for the bytes. func (grouping *largeDivisionGroupingInternal) CopyBytes(bytes []byte) []byte { if grouping.hasNoDivisions() { if bytes != nil { return bytes[:0] } return emptyBytes } return getBytesCopy(bytes, grouping.getBytes()) } // CopyUpperBytes copies the value of the highest division grouping in the range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. // // You can use GetByteCount to determine the required array length for the bytes. func (grouping *largeDivisionGroupingInternal) CopyUpperBytes(bytes []byte) []byte { if grouping.hasNoDivisions() { if bytes != nil { return bytes[:0] } return emptyBytes } return getBytesCopy(bytes, grouping.getUpperBytes()) } // Bytes returns the lowest individual division grouping in this grouping as a byte slice. func (grouping *largeDivisionGroupingInternal) Bytes() []byte { if grouping.hasNoDivisions() { return emptyBytes } return cloneBytes(grouping.getBytes()) } // UpperBytes returns the highest individual division grouping in this grouping as a byte slice. func (grouping *largeDivisionGroupingInternal) UpperBytes() []byte { if grouping.hasNoDivisions() { return emptyBytes } return cloneBytes(grouping.getUpperBytes()) } // GetValue returns the lowest individual address division grouping in this address division grouping as an integer value. func (grouping *largeDivisionGroupingInternal) GetValue() *big.Int { res := big.Int{} if grouping.hasNoDivisions() { return &res } return res.SetBytes(grouping.getBytes()) } // GetUpperValue returns the highest individual address division grouping in this address division grouping as an integer value. func (grouping *largeDivisionGroupingInternal) GetUpperValue() *big.Int { res := big.Int{} if grouping.hasNoDivisions() { return &res } return res.SetBytes(grouping.getUpperBytes()) } // GetPrefixLenForSingleBlock returns a prefix length for which the range of this division grouping matches the block of addresses for that prefix. // // If no such prefix exists, GetPrefixLenForSingleBlock returns nil. // // If this division grouping represents a single value, returns the bit length. func (grouping *largeDivisionGroupingInternal) GetPrefixLenForSingleBlock() PrefixLen { calc := func() *PrefixLen { count := grouping.GetDivisionCount() var totalPrefix BitCount for i := 0; i < count; i++ { div := grouping.getDivision(i) divPrefix := div.GetPrefixLenForSingleBlock() if divPrefix == nil { return cacheNilPrefix() } divPrefLen := divPrefix.bitCount() totalPrefix += divPrefLen if divPrefLen < div.GetBitCount() { //remaining segments must be full range or we return nil for i++; i < count; i++ { laterDiv := grouping.getDivision(i) if !laterDiv.IsFullRange() { return cacheNilPrefix() } } } } return cachePrefix(totalPrefix) } return cachePrefLenSingleBlock(grouping.cache, grouping.getPrefixLen(), calc) } // GetMinPrefixLenForBlock returns the smallest prefix length such that this grouping includes the block of all values for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this grouping represents a single value, this returns the bit count. func (grouping *largeDivisionGroupingInternal) GetMinPrefixLenForBlock() BitCount { calc := func() BitCount { count := grouping.GetDivisionCount() totalPrefix := grouping.GetBitCount() for i := count - 1; i >= 0; i-- { div := grouping.getDivision(i) segBitCount := div.getBitCount() segPrefix := div.GetMinPrefixLenForBlock() if segPrefix == segBitCount { break } else { totalPrefix -= segBitCount if segPrefix != 0 { totalPrefix += segPrefix break } } } return totalPrefix } return cacheMinPrefix(grouping.cache, calc) } // IsPrefixBlock returns whether this division grouping has a prefix length and includes the block associated with its prefix length. // If the prefix length matches the bit count, this returns true. // // This is different from ContainsPrefixBlock in that this method returns // false if the series has no prefix length, or a prefix length that differs from a prefix length for which ContainsPrefixBlock returns true. func (grouping *largeDivisionGroupingInternal) IsPrefixBlock() bool { prefLen := grouping.getPrefixLen() return prefLen != nil && grouping.ContainsPrefixBlock(prefLen.bitCount()) } // IsSinglePrefixBlock returns whether the range of values matches a single subnet block for the prefix length. // // What distinguishes this method with ContainsSinglePrefixBlock is that this method returns // false if the series does not have a prefix length assigned to it, // or a prefix length that differs from the prefix length for which ContainsSinglePrefixBlock returns true. // // It is similar to IsPrefixBlock but returns false when there are multiple prefixes. func (grouping *largeDivisionGroupingInternal) IsSinglePrefixBlock() bool { calc := func() bool { prefLen := grouping.getPrefixLen() return prefLen != nil && grouping.ContainsSinglePrefixBlock(prefLen.bitCount()) } return cacheIsSinglePrefixBlock(grouping.cache, grouping.getPrefixLen(), calc) } // ContainsPrefixBlock returns whether the values of this item contains the block of values for the given prefix length. // // Unlike ContainsSinglePrefixBlock, whether there are multiple prefix values in this item for the given prefix length makes no difference. // // Use GetMinPrefixLenForBlock to determine the smallest prefix length for which this method returns true. func (grouping *largeDivisionGroupingInternal) ContainsPrefixBlock(prefixLen BitCount) bool { prefixLen = checkSubnet(grouping, prefixLen) divisionCount := grouping.GetDivisionCount() var prevBitCount BitCount for i := 0; i < divisionCount; i++ { division := grouping.getDivision(i) bitCount := division.GetBitCount() totalBitCount := bitCount + prevBitCount if prefixLen < totalBitCount { divPrefixLen := prefixLen - prevBitCount if !division.ContainsPrefixBlock(divPrefixLen) { return false } for i++; i < divisionCount; i++ { division = grouping.getDivision(i) if !division.IsFullRange() { return false } } return true } prevBitCount = totalBitCount } return true } // ContainsSinglePrefixBlock returns whether the values of this grouping contains a single prefix block for the given prefix length. // // This means there is only one prefix of the given length in this item, and this item contains the prefix block for that given prefix. // // Use GetPrefixLenForSingleBlock to determine whether there is a prefix length for which this method returns true. func (grouping *largeDivisionGroupingInternal) ContainsSinglePrefixBlock(prefixLen BitCount) bool { prefixLen = checkSubnet(grouping, prefixLen) divisionCount := grouping.GetDivisionCount() var prevBitCount BitCount for i := 0; i < divisionCount; i++ { division := grouping.getDivision(i) bitCount := division.getBitCount() totalBitCount := bitCount + prevBitCount if prefixLen >= totalBitCount { if division.isMultiple() { return false } } else { divPrefixLen := prefixLen - prevBitCount if !division.ContainsSinglePrefixBlock(divPrefixLen) { return false } for i++; i < divisionCount; i++ { division = grouping.getDivision(i) if !division.IsFullRange() { return false } } return true } prevBitCount = totalBitCount } return true } // copySubDivisions copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (grouping *largeDivisionGroupingInternal) copySubDivisions(start, end int, divs []*IPAddressLargeDivision) (count int) { if divArray := grouping.getDivArray(); divArray != nil { start, end, targetIndex := adjust1To1Indices(start, end, grouping.GetDivisionCount(), len(divs)) return divArray.copySubDivisions(start, end, divs[targetIndex:]) } return } // copyDivisions copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (grouping *largeDivisionGroupingInternal) copyDivisions(divs []*IPAddressLargeDivision) (count int) { if divArray := grouping.getDivArray(); divArray != nil { return divArray.copyDivisions(divs) } return } // NewIPAddressLargeDivGrouping creates an arbitrary grouping of divisions of arbitrary size, each division can have an arbitrarily large bit-length. // To create address sections or addresses, use the constructors that are specific to the address version or type. // The IPAddressLargeDivision instances can be created with the NewLargeIPDivision, NewLargeIPRangeDivision, NewLargeIPPrefixDivision, NewLargeIPRangePrefixDivision functions. func NewIPAddressLargeDivGrouping(divs []*IPAddressLargeDivision) *IPAddressLargeDivisionGrouping { // We do not check for prefix subnet because an explicit prefix length must be supplied for that newDivs, newPref, isMult := normalizeLargeDivisions(divs) result := createLargeGrouping(newDivs) result.isMult = isMult result.prefixLength = newPref return result } func normalizeLargeDivisions(divs []*IPAddressLargeDivision) (newDivs []*IPAddressLargeDivision, newPref PrefixLen, isMultiple bool) { divCount := len(divs) newDivs = make([]*IPAddressLargeDivision, 0, divCount) var previousDivPrefixed bool var bits BitCount for _, div := range divs { if div == nil || div.GetBitCount() == 0 { // nil divisions are divisions with zero bit-length, which we ignore continue } var newDiv *IPAddressLargeDivision // The final prefix length is the minimum amongst the divisions' own prefixes divPrefix := div.getDivisionPrefixLength() divIsPrefixed := divPrefix != nil if previousDivPrefixed { if !divIsPrefixed || divPrefix.bitCount() != 0 { newDiv = createLargeAddressDiv(div.derivePrefixed(cacheBitCount(0)), div.getDefaultRadix()) // change prefix to 0 } else { newDiv = div // div prefix is already 0 } } else { if divIsPrefixed { if divPrefix.bitCount() == 0 && len(newDivs) > 0 { // normalize boundaries by looking back lastDiv := newDivs[len(newDivs)-1] if !lastDiv.IsPrefixed() { newDivs[len(newDivs)-1] = createLargeAddressDiv( lastDiv.derivePrefixed(cacheBitCount(lastDiv.GetBitCount())), div.getDefaultRadix()) } } newPref = cacheBitCount(bits + divPrefix.bitCount()) previousDivPrefixed = true } newDiv = div } newDivs = append(newDivs, newDiv) bits += newDiv.GetBitCount() isMultiple = isMultiple || newDiv.isMultiple() } return } type IPAddressLargeDivisionGrouping struct { largeDivisionGroupingInternal } // GetCount returns the count of possible distinct values for this division grouping. // If not representing multiple values, the count is 1, // unless this is a division grouping with no divisions, or an address section with no segments, in which case it is 0. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (grouping *IPAddressLargeDivisionGrouping) GetCount() *big.Int { if !grouping.isMultiple() { return bigOne() } return grouping.addressDivisionGroupingBase.getCount() } // IsMultiple returns whether this grouping represents multiple values rather than a single value. func (grouping *IPAddressLargeDivisionGrouping) IsMultiple() bool { return grouping != nil && grouping.isMultiple() } // Compare returns a negative integer, zero, or a positive integer if this address division grouping is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (grouping *IPAddressLargeDivisionGrouping) Compare(item AddressItem) int { return CountComparator.Compare(grouping, item) } // CompareSize compares the counts of two items, the number of individual values within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether one represents more individual values than another. // // CompareSize returns a positive integer if this division has a larger count than the item given, zero if they are the same, or a negative integer if the other has a larger count. func (grouping *IPAddressLargeDivisionGrouping) CompareSize(other AddressItem) int { if grouping == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return compareCount(grouping, other) //return grouping.compareSize(other) } // String implements the [fmt.Stringer] interface. // It returns "" if the receiver is a nil pointer. // Otherwise, the string is printed like a slice, with each division converted to a string by its own String method (like "[ div0 div1 ... ]"). func (grouping *IPAddressLargeDivisionGrouping) String() string { if grouping == nil { return nilString() } return grouping.toString() } // IsPrefixed returns whether this division grouping has an associated prefix length. // If so, the prefix length is given by GetPrefixLen. func (grouping *IPAddressLargeDivisionGrouping) IsPrefixed() bool { if grouping == nil { return false } return grouping.isPrefixed() } // GetDivision returns the division at the given index. func (grouping *IPAddressLargeDivisionGrouping) GetDivision(index int) *IPAddressLargeDivision { return grouping.getDivision(index) } // ForEachDivision visits each segment in order from most-significant to least, the most significant with index 0, calling the given function for each, terminating early if the function returns true. // ForEachDivision returns the number of visited segments. func (grouping *IPAddressLargeDivisionGrouping) ForEachDivision(consumer func(divisionIndex int, division *IPAddressLargeDivision) (stop bool)) int { divArray := grouping.getDivArray() if divArray != nil { for i, div := range divArray { if consumer(i, div) { return i + 1 } } } return len(divArray) } func (grouping *IPAddressLargeDivisionGrouping) isNil() bool { return grouping == nil } // CopySubDivisions copies the existing divisions from the given start index until but not including the division at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of divisions copied. func (grouping *IPAddressLargeDivisionGrouping) CopySubDivisions(start, end int, divs []*IPAddressLargeDivision) (count int) { return grouping.copySubDivisions(start, end, divs) } // CopyDivisions copies the existing divisions from the given start index until but not including the division at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of divisions copied. func (grouping *IPAddressLargeDivisionGrouping) CopyDivisions(divs []*IPAddressLargeDivision) (count int) { return grouping.copyDivisions(divs) } ipaddress-go-1.5.4/ipaddr/macaddr.go000066400000000000000000001641211440250641600172760ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math/big" "net" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) const ( MACBitsPerSegment = 8 MACBytesPerSegment = 1 MACDefaultTextualRadix = 16 MACMaxValuePerSegment = 0xff MACMaxValuePerDottedSegment = 0xffff MediaAccessControlSegmentCount = 6 MediaAccessControlDottedSegmentCount = 3 MediaAccessControlDotted64SegmentCount = 4 ExtendedUniqueIdentifier48SegmentCount = MediaAccessControlSegmentCount ExtendedUniqueIdentifier64SegmentCount = 8 MACOrganizationalUniqueIdentifierSegmentCount = 3 MACSegmentMaxChars = 2 MACDashSegmentSeparator = '-' MACColonSegmentSeparator = ':' MacSpaceSegmentSeparator = ' ' MacDottedSegmentSeparator = '.' MacDashedSegmentRangeSeparator = '|' MacDashedSegmentRangeSeparatorStr = "|" macBitsToSegmentBitshift = 3 ) func newMACAddress(section *MACAddressSection) *MACAddress { return createAddress(section.ToSectionBase(), NoZone).ToMAC() } // NewMACAddress constructs a MAC address or address collection from the given segments. func NewMACAddress(section *MACAddressSection) (*MACAddress, addrerr.AddressValueError) { segCount := section.GetSegmentCount() if segCount != MediaAccessControlSegmentCount && segCount != ExtendedUniqueIdentifier64SegmentCount { return nil, &addressValueError{ addressError: addressError{key: "ipaddress.error.invalid.size"}, val: segCount, } } return createAddress(section.ToSectionBase(), NoZone).ToMAC(), nil } // NewMACAddressFromBytes constructs a MAC address from the given byte slice. // An error is returned when the byte slice has too many bytes to match the maximum MAC segment count of 8. // There should be 8 bytes or less, although extra leading zeros are tolerated. func NewMACAddressFromBytes(bytes net.HardwareAddr) (*MACAddress, addrerr.AddressValueError) { section, err := createMACSectionFromBytes(bytes) if err != nil { return nil, err } segCount := section.GetSegmentCount() if segCount != MediaAccessControlSegmentCount && segCount != ExtendedUniqueIdentifier64SegmentCount { return nil, &addressValueError{ addressError: addressError{key: "ipaddress.error.invalid.size"}, val: segCount, } } return createAddress(section.ToSectionBase(), NoZone).ToMAC(), nil } // NewMACAddressFromUint64Ext constructs a 6 or 8-byte MAC address from the given value. // If isExtended is true, it is an 8-byte address, 6 otherwise. // If 6 bytes, then the bytes are taken from the lower 48 bits of the uint64. func NewMACAddressFromUint64Ext(val uint64, isExtended bool) *MACAddress { section := NewMACSectionFromUint64(val, getMacSegCount(isExtended)) return createAddress(section.ToSectionBase(), NoZone).ToMAC() } // NewMACAddressFromSegs constructs a MAC address or address collection from the given segments. // If the given slice does not have either 6 or 8 segments, an error is returned. func NewMACAddressFromSegs(segments []*MACAddressSegment) (*MACAddress, addrerr.AddressValueError) { segsLen := len(segments) if segsLen != MediaAccessControlSegmentCount && segsLen != ExtendedUniqueIdentifier64SegmentCount { return nil, &addressValueError{val: segsLen, addressError: addressError{key: "ipaddress.error.mac.invalid.segment.count"}} } section := NewMACSection(segments) return createAddress(section.ToSectionBase(), NoZone).ToMAC(), nil } // NewMACAddressFromVals constructs a 6-byte MAC address from the given values. func NewMACAddressFromVals(vals MACSegmentValueProvider) (addr *MACAddress) { return NewMACAddressFromValsExt(vals, false) } // NewMACAddressFromValsExt constructs a 6 or 8-byte MAC address from the given values. // If isExtended is true, it will be 8 bytes. func NewMACAddressFromValsExt(vals MACSegmentValueProvider, isExtended bool) (addr *MACAddress) { section := NewMACSectionFromVals(vals, getMacSegCount(isExtended)) addr = newMACAddress(section) return } // NewMACAddressFromRange constructs a 6-byte MAC address collection from the given values. func NewMACAddressFromRange(vals, upperVals MACSegmentValueProvider) (addr *MACAddress) { return NewMACAddressFromRangeExt(vals, upperVals, false) } // NewMACAddressFromRangeExt constructs a 6 or 8-byte MAC address collection from the given values. // If isExtended is true, it will be 8 bytes. func NewMACAddressFromRangeExt(vals, upperVals MACSegmentValueProvider, isExtended bool) (addr *MACAddress) { section := NewMACSectionFromRange(vals, upperVals, getMacSegCount(isExtended)) addr = newMACAddress(section) return } func createMACSectionFromBytes(bytes []byte) (*MACAddressSection, addrerr.AddressValueError) { var segCount int length := len(bytes) //We round down the bytes to 6 bytes if we can. Otherwise, we round up. if length < ExtendedUniqueIdentifier64SegmentCount { segCount = MediaAccessControlSegmentCount if length > MediaAccessControlSegmentCount { for i := 0; ; i++ { if bytes[i] != 0 { segCount = ExtendedUniqueIdentifier64SegmentCount break } length-- if length <= MediaAccessControlSegmentCount { break } } } } else { segCount = ExtendedUniqueIdentifier64SegmentCount } return NewMACSectionFromBytes(bytes, segCount) } func getMacSegCount(isExtended bool) (segmentCount int) { if isExtended { segmentCount = ExtendedUniqueIdentifier64SegmentCount } else { segmentCount = MediaAccessControlSegmentCount } return } var zeroMAC = createMACZero(false) var macAll = zeroMAC.SetPrefixLen(0).ToPrefixBlock() var macAllExtended = createMACZero(true).SetPrefixLen(0).ToPrefixBlock() func createMACZero(extended bool) *MACAddress { segs := []*MACAddressSegment{zeroMACSeg, zeroMACSeg, zeroMACSeg, zeroMACSeg, zeroMACSeg, zeroMACSeg} if extended { segs = append(segs, zeroMACSeg, zeroMACSeg) } section := NewMACSection(segs) return newMACAddress(section) } // MACAddress represents a MAC address, or a collection of multiple individual MAC addresses. // Each segment can represent a single byte value or a range of byte values. // // You can construct a MAC address from a byte slice, from a uint64, from a SegmentValueProvider, // from a MACAddressSection of 6 or 8 segments, or from an array of 6 or 8 MACAddressSegment instances. // // To construct one from a string, use NewMACAddressString, then use the ToAddress or GetAddress method of [MACAddressString]. type MACAddress struct { addressInternal } // GetCount returns the count of addresses that this address or address collection represents. // // If just a single address, not a collection of multiple addresses, returns 1. func (addr *MACAddress) init() *MACAddress { if addr.section == nil { return zeroMAC } return addr } // GetCount returns the count of addresses that this address or address collection represents. // // If just a single address, not a collection of multiple addresses, returns 1. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (addr *MACAddress) GetCount() *big.Int { if addr == nil { return bigZero() } return addr.getCount() } // IsMultiple returns true if this represents more than a single individual address, whether it is a collection of multiple addresses. func (addr *MACAddress) IsMultiple() bool { return addr != nil && addr.isMultiple() } // IsPrefixed returns whether this address has an associated prefix length. func (addr *MACAddress) IsPrefixed() bool { return addr != nil && addr.isPrefixed() } // IsFullRange returns whether this address covers the entire MAC address space for its MAC bit length. // // This is true if and only if both IncludesZero and IncludesMax return true. func (addr *MACAddress) IsFullRange() bool { return addr.GetSection().IsFullRange() } // GetBitCount returns the number of bits comprising this address, // or each address in the range. func (addr *MACAddress) GetBitCount() BitCount { return addr.init().addressInternal.GetBitCount() } // GetByteCount returns the number of bytes required for this address, // or each address in the range. func (addr *MACAddress) GetByteCount() int { return addr.init().addressInternal.GetByteCount() } // GetBitsPerSegment returns the number of bits comprising each segment in this address. Segments in the same address are equal length. func (addr *MACAddress) GetBitsPerSegment() BitCount { return MACBitsPerSegment } // GetBytesPerSegment returns the number of bytes comprising each segment in this address. Segments in the same address are equal length. func (addr *MACAddress) GetBytesPerSegment() int { return MACBytesPerSegment } func (addr *MACAddress) checkIdentity(section *MACAddressSection) *MACAddress { if section == nil { return nil } sec := section.ToSectionBase() if sec == addr.section { return addr } return newMACAddress(section) } // GetValue returns the lowest address in this subnet or address as an integer value. func (addr *MACAddress) GetValue() *big.Int { return addr.init().section.GetValue() } // GetUpperValue returns the highest address in this subnet or address as an integer value. func (addr *MACAddress) GetUpperValue() *big.Int { return addr.init().section.GetUpperValue() } // GetLower returns the address in the collection with the lowest numeric value, // which will be the receiver if it represents a single address. // For example, for "1:1:1:2-3:4:5-6", the series "1:1:1:2:4:5" is returned. func (addr *MACAddress) GetLower() *MACAddress { return addr.init().getLower().ToMAC() } // GetUpper returns the address in the collection with the highest numeric value, // which will be the receiver if it represents a single address. // For example, for "1:1:1:2-3:4:5-6", the series "1:1:1:3:4:6" is returned. func (addr *MACAddress) GetUpper() *MACAddress { return addr.init().getUpper().ToMAC() } // Uint64Value returns the lowest address in the address collection as a uint64. func (addr *MACAddress) Uint64Value() uint64 { return addr.GetSection().Uint64Value() } // UpperUint64Value returns the highest address in the address collection as a uint64. func (addr *MACAddress) UpperUint64Value() uint64 { return addr.GetSection().UpperUint64Value() } // GetHardwareAddr returns the lowest address in this address or address collection as a net.HardwareAddr. func (addr *MACAddress) GetHardwareAddr() net.HardwareAddr { return addr.Bytes() } // CopyHardwareAddr copies the value of the lowest individual address in the address collection into a net.HardwareAddr. // // If the value can fit in the given net.HardwareAddr, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new net.HardwareAddr is created and returned with the value. func (addr *MACAddress) CopyHardwareAddr(bytes net.HardwareAddr) net.HardwareAddr { return addr.CopyBytes(bytes) } // GetUpperHardwareAddr returns the highest address in this address or address collection as a net.HardwareAddr. func (addr *MACAddress) GetUpperHardwareAddr() net.HardwareAddr { return addr.UpperBytes() } // CopyUpperHardwareAddr copies the value of the highest individual address in the address collection into a net.HardwareAddr. // // If the value can fit in the given net.HardwareAddr, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new net.HardwareAddr is created and returned with the value. func (addr *MACAddress) CopyUpperHardwareAddr(bytes net.HardwareAddr) net.HardwareAddr { return addr.CopyUpperBytes(bytes) } // Bytes returns the lowest address in this address or address collection as a byte slice. func (addr *MACAddress) Bytes() []byte { return addr.init().section.Bytes() } // UpperBytes returns the highest address in this address or address collection as a byte slice. func (addr *MACAddress) UpperBytes() []byte { return addr.init().section.UpperBytes() } // CopyBytes copies the value of the lowest individual address in the address collection into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *MACAddress) CopyBytes(bytes []byte) []byte { return addr.init().section.CopyBytes(bytes) } // CopyUpperBytes copies the value of the highest individual address in the address collection into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (addr *MACAddress) CopyUpperBytes(bytes []byte) []byte { return addr.init().section.CopyUpperBytes(bytes) } // GetSection returns the backing section for this address or address collection, comprising all segments. func (addr *MACAddress) GetSection() *MACAddressSection { return addr.init().section.ToMAC() } // GetTrailingSection gets the subsection from the series starting from the given index. // The first segment is at index 0. func (addr *MACAddress) GetTrailingSection(index int) *MACAddressSection { return addr.GetSection().GetTrailingSection(index) } // GetSubSection gets the subsection from the series starting from the given index and ending just before the give endIndex. // The first segment is at index 0. func (addr *MACAddress) GetSubSection(index, endIndex int) *MACAddressSection { return addr.GetSection().GetSubSection(index, endIndex) } // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (addr *MACAddress) CopySubSegments(start, end int, segs []*MACAddressSegment) (count int) { return addr.GetSection().CopySubSegments(start, end, segs) } // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. func (addr *MACAddress) CopySegments(segs []*MACAddressSegment) (count int) { return addr.GetSection().CopySegments(segs) } // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as this address. func (addr *MACAddress) GetSegments() []*MACAddressSegment { return addr.GetSection().GetSegments() } // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. func (addr *MACAddress) GetSegment(index int) *MACAddressSegment { return addr.init().getSegment(index).ToMAC() } // GetSegmentCount returns the segment/division count func (addr *MACAddress) GetSegmentCount() int { return addr.GetDivisionCount() } // ForEachSegment visits each segment in order from most-significant to least, the most significant with index 0, calling the given function for each, terminating early if the function returns true. // Returns the number of visited segments. func (addr *MACAddress) ForEachSegment(consumer func(segmentIndex int, segment *MACAddressSegment) (stop bool)) int { return addr.GetSection().ForEachSegment(consumer) } // GetGenericDivision returns the segment at the given index as a DivisionType. func (addr *MACAddress) GetGenericDivision(index int) DivisionType { return addr.init().getDivision(index) } // GetGenericSegment returns the segment at the given index as an AddressSegmentType. // The first segment is at index 0. // GetGenericSegment will panic given a negative index or an index matching or larger than the segment count. func (addr *MACAddress) GetGenericSegment(index int) AddressSegmentType { return addr.init().getSegment(index) } // TestBit returns true if the bit in the lower value of this address at the given index is 1, where index 0 refers to the least significant bit. // In other words, it computes (bits & (1 << n)) != 0), using the lower value of this address. // TestBit will panic if n < 0, or if it matches or exceeds the bit count of this item. func (addr *MACAddress) TestBit(n BitCount) bool { return addr.init().testBit(n) } // IsOneBit returns true if the bit in the lower value of this address at the given index is 1, where index 0 refers to the most significant bit. // IsOneBit will panic if bitIndex is less than zero, or if it is larger than the bit count of this item. func (addr *MACAddress) IsOneBit(bitIndex BitCount) bool { return addr.init().isOneBit(bitIndex) } // IsMax returns whether this address matches exactly the maximum possible value, the address whose bits are all ones. func (addr *MACAddress) IsMax() bool { return addr.init().section.IsMax() } // IncludesMax returns whether this address includes the max address, the address whose bits are all ones, within its range. func (addr *MACAddress) IncludesMax() bool { return addr.init().section.IncludesMax() } // GetDivisionCount returns the segment count, implementing the interface AddressDivisionSeries. func (addr *MACAddress) GetDivisionCount() int { return addr.init().getDivisionCount() } // ToPrefixBlock returns the address associated with the prefix of this address or address collection, // the address whose prefix matches the prefix of this address, and the remaining bits span all values. // If this address has no prefix length, this address is returned. // // The returned address collection will include all addresses with the same prefix as this one, the prefix "block". func (addr *MACAddress) ToPrefixBlock() *MACAddress { return addr.init().toPrefixBlock().ToMAC() } // ToPrefixBlockLen returns the address associated with the prefix length provided, // the address collection whose prefix of that length matches the prefix of this address, and the remaining bits span all values. // // The returned address will include all addresses with the same prefix as this one, the prefix "block". func (addr *MACAddress) ToPrefixBlockLen(prefLen BitCount) *MACAddress { return addr.init().toPrefixBlockLen(prefLen).ToMAC() } // ToBlock creates a new block of addresses by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (addr *MACAddress) ToBlock(segmentIndex int, lower, upper SegInt) *MACAddress { return addr.init().toBlock(segmentIndex, lower, upper).ToMAC() } // WithoutPrefixLen provides the same address but with no prefix length. The values remain unchanged. func (addr *MACAddress) WithoutPrefixLen() *MACAddress { if !addr.IsPrefixed() { return addr } return addr.init().withoutPrefixLen().ToMAC() } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address. // The provided prefix length will be adjusted to these boundaries if necessary. func (addr *MACAddress) SetPrefixLen(prefixLen BitCount) *MACAddress { return addr.init().setPrefixLen(prefixLen).ToMAC() } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this address has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this address has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr *MACAddress) SetPrefixLenZeroed(prefixLen BitCount) (*MACAddress, addrerr.IncompatibleAddressError) { res, err := addr.init().setPrefixLenZeroed(prefixLen) return res.ToMAC(), err } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address. // // If this address has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (addr *MACAddress) AdjustPrefixLen(prefixLen BitCount) *MACAddress { return addr.init().adjustPrefixLen(prefixLen).ToMAC() } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address. // // If this address has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (addr *MACAddress) AdjustPrefixLenZeroed(prefixLen BitCount) (*MACAddress, addrerr.IncompatibleAddressError) { res, err := addr.init().adjustPrefixLenZeroed(prefixLen) return res.ToMAC(), err } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this address. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such address - it is required that the range of values match the range of a prefix block. // If there is no such address, then nil is returned. func (addr *MACAddress) AssignPrefixForSingleBlock() *MACAddress { return addr.init().assignPrefixForSingleBlock().ToMAC() } // AssignMinPrefixForBlock returns an equivalent subnet, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this subnet. // // In other words, this method assigns a prefix length to this subnet matching the largest prefix block in this subnet. func (addr *MACAddress) AssignMinPrefixForBlock() *MACAddress { return addr.init().assignMinPrefixForBlock().ToMAC() } // ToSinglePrefixBlockOrAddress converts to a single prefix block or address. // If the given address is a single prefix block, it is returned. // If it can be converted to a single prefix block by assigning a prefix length, the converted block is returned. // If it is a single address, any prefix length is removed and the address is returned. // Otherwise, nil is returned. // This method provides the address formats used by tries. // ToSinglePrefixBlockOrAddress is quite similar to AssignPrefixForSingleBlock, which always returns prefixed addresses, while this does not. func (addr *MACAddress) ToSinglePrefixBlockOrAddress() *MACAddress { return addr.init().toSinglePrefixBlockOrAddr().ToMAC() } func (addr *MACAddress) toSinglePrefixBlockOrAddress() (*MACAddress, addrerr.IncompatibleAddressError) { if addr == nil { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.address.not.block"}} } res := addr.ToSinglePrefixBlockOrAddress() if res == nil { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.address.not.block"}} } return res, nil } // ContainsPrefixBlock returns whether the range of this address or address collection contains the block of addresses for the given prefix length. // // Unlike ContainsSinglePrefixBlock, whether there are multiple prefix values in this item for the given prefix length makes no difference. // // Use GetMinPrefixLenForBlock to determine the smallest prefix length for which this method returns true. func (addr *MACAddress) ContainsPrefixBlock(prefixLen BitCount) bool { return addr.init().addressInternal.ContainsPrefixBlock(prefixLen) } // ContainsSinglePrefixBlock returns whether this address contains a single prefix block for the given prefix length. // // This means there is only one prefix value for the given prefix length, and it also contains the full prefix block for that prefix, all addresses with that prefix. // // Use GetPrefixLenForSingleBlock to determine whether there is a prefix length for which this method returns true. func (addr *MACAddress) ContainsSinglePrefixBlock(prefixLen BitCount) bool { return addr.init().addressInternal.ContainsSinglePrefixBlock(prefixLen) } // GetMinPrefixLenForBlock returns the smallest prefix length such that this includes the block of addresses for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this represents just a single address, returns the bit length of this address. func (addr *MACAddress) GetMinPrefixLenForBlock() BitCount { return addr.init().addressInternal.GetMinPrefixLenForBlock() } // GetPrefixLenForSingleBlock returns a prefix length for which the range of this address collection matches the block of addresses for that prefix. // // If the range can be described this way, then this method returns the same value as GetMinPrefixLenForBlock. // // If no such prefix exists, returns nil. // // If this segment grouping represents a single value, this returns the bit length of this address. func (addr *MACAddress) GetPrefixLenForSingleBlock() PrefixLen { return addr.init().addressInternal.GetPrefixLenForSingleBlock() } // Compare returns a negative integer, zero, or a positive integer if this address or address collection is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (addr *MACAddress) Compare(item AddressItem) int { return CountComparator.Compare(addr, item) } // PrefixEqual determines if the given address matches this address up to the prefix length of this address. // It returns whether the two addresses share the same range of prefix values. func (addr *MACAddress) PrefixEqual(other AddressType) bool { return addr.init().prefixEquals(other) } // PrefixContains returns whether the prefix values in the given address // are prefix values in this address, using the prefix length of this address. // If this address has no prefix length, the entire address is compared. // // It returns whether the prefix of this address contains all values of the same prefix length in the given address. func (addr *MACAddress) PrefixContains(other AddressType) bool { return addr.init().prefixContains(other) } // Contains returns whether this is the same type and version as the given address or subnet and whether it contains all addresses in the given address or subnet. func (addr *MACAddress) Contains(other AddressType) bool { if addr == nil { return other == nil || other.ToAddressBase() == nil } // note: we don't use the same optimization as in IPv4/6 because we do need to check segment count with MAC return addr.init().contains(other) } // Equal returns whether the given address or address collection is equal to this address or address collection. // Two address instances are equal if they represent the same set of addresses. func (addr *MACAddress) Equal(other AddressType) bool { if addr == nil { return other == nil || other.ToAddressBase() == nil } // note: we don't use the same optimization as in IPv4/6 because we do need to check segment count with MAC return addr.init().equals(other) } // CompareSize compares the counts of two addresses or address collections or address items, the number of individual addresses or items within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether one address collection represents more individual addresses than another. // // CompareSize returns a positive integer if this address or address collection has a larger count than the one given, zero if they are the same, or a negative integer if the other has a larger count. func (addr *MACAddress) CompareSize(other AddressItem) int { // this is here to take advantage of the CompareSize in IPAddressSection if addr == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return addr.init().compareSize(other) } // TrieCompare compares two addresses according to address trie ordering. // It returns a number less than zero, zero, or a number greater than zero if the first address argument is less than, equal to, or greater than the second. // // The comparison is intended for individual addresses and CIDR prefix blocks. // If an address is neither an individual address nor a prefix block, it is treated like one: // // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *MACAddress) TrieCompare(other *MACAddress) (int, addrerr.IncompatibleAddressError) { if addr.GetSegmentCount() != other.GetSegmentCount() { return 0, &incompatibleAddressError{addressError{key: "ipaddress.error.mismatched.bit.size"}} } return addr.init().trieCompare(other.ToAddressBase()), nil } // TrieIncrement returns the next address or block according to address trie ordering // // If an address is neither an individual address nor a prefix block, it is treated like one: // // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *MACAddress) TrieIncrement() *MACAddress { if res, ok := trieIncrement(addr); ok { return res } return nil } // TrieDecrement returns the previous address or block according to address trie ordering // // If an address is neither an individual address nor a prefix block, it is treated like one: // // - ranges that occur inside the prefix length are ignored, only the lower value is used. // - ranges beyond the prefix length are assumed to be the full range across all hosts for that prefix length. func (addr *MACAddress) TrieDecrement() *MACAddress { if res, ok := trieDecrement(addr); ok { return res } return nil } // GetMaxSegmentValue returns the maximum possible segment value for this type of address. // // Note this is not the maximum of the range of segment values in this specific address, // this is the maximum value of any segment for this address type and version, determined by the number of bits per segment. func (addr *MACAddress) GetMaxSegmentValue() SegInt { return addr.init().getMaxSegmentValue() } // IsMulticast returns whether this address or collection of addresses is entirely multicast. // Multicast MAC addresses have the least significant bit of the first octet set to 1. func (addr *MACAddress) IsMulticast() bool { return addr.GetSegment(0).MatchesWithMask(1, 0x1) } // IsUnicast returns whether this address or collection of addresses is entirely unicast. // Unicast MAC addresses have the least significant bit of the first octet set to 0. func (addr *MACAddress) IsUnicast() bool { return !addr.IsMulticast() } // IsUniversal returns whether this is a universal address. // Universal MAC addresses have second the least significant bit of the first octet set to 0. func (addr *MACAddress) IsUniversal() bool { return !addr.IsLocal() } // IsLocal returns whether this is a local address. // Local MAC addresses have the second least significant bit of the first octet set to 1. func (addr *MACAddress) IsLocal() bool { return addr.GetSegment(0).MatchesWithMask(2, 0x2) } // Iterator provides an iterator to iterate through the individual addresses of this address or subnet. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual addresses. // // Call IsMultiple to determine if this instance represents multiple addresses, or GetCount for the count. func (addr *MACAddress) Iterator() Iterator[*MACAddress] { if addr == nil { return macAddressIterator{nilAddrIterator()} } return macAddressIterator{addr.init().addrIterator(nil)} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this subnet, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this subnet. // // If the subnet has no prefix length, then this is equivalent to Iterator. func (addr *MACAddress) PrefixIterator() Iterator[*MACAddress] { return macAddressIterator{addr.init().prefixIterator(false)} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address or subnet. // Each iterated address or subnet will be a prefix block with the same prefix length as this address or subnet. // // If this address has no prefix length, then this is equivalent to Iterator. func (addr *MACAddress) PrefixBlockIterator() Iterator[*MACAddress] { return macAddressIterator{addr.init().prefixIterator(true)} } // BlockIterator iterates through the addresses that can be obtained by iterating through all the upper segments up to the given segment count. // The segments following remain the same in all iterated addresses. func (addr *MACAddress) BlockIterator(segmentCount int) Iterator[*MACAddress] { return macAddressIterator{addr.init().blockIterator(segmentCount)} } // SequentialBlockIterator iterates through the sequential subnets or addresses that make up this address or subnet. // // Practically, this means finding the count of segments for which the segments that follow are not full range, and then using BlockIterator with that segment count. // // For instance, given the IPv4 subnet "1-2.3-4.5-6.7-8", it will iterate through "1.3.5.7-8", "1.3.6.7-8", "1.4.5.7-8", "1.4.6.7-8", "2.3.5.7-8", "2.3.6.7-8", "2.4.6.7-8" and "2.4.6.7-8". // // Use GetSequentialBlockCount to get the number of iterated elements. func (addr *MACAddress) SequentialBlockIterator() Iterator[*MACAddress] { return macAddressIterator{addr.init().sequentialBlockIterator()} } // GetSequentialBlockIndex gets the minimal segment index for which all following segments are full-range blocks. // // The segment at this index is not a full-range block itself, unless all segments are full-range. // The segment at this index and all following segments form a sequential range. // For the full address collection to be sequential, the preceding segments must be single-valued. func (addr *MACAddress) GetSequentialBlockIndex() int { return addr.init().getSequentialBlockIndex() } // GetSequentialBlockCount provides the count of elements from the sequential block iterator, the minimal number of sequential address ranges that comprise this address collection. func (addr *MACAddress) GetSequentialBlockCount() *big.Int { return addr.init().getSequentialBlockCount() } // IncrementBoundary returns the address that is the given increment from the range boundaries of this address collection. // // If the given increment is positive, adds the value to the upper address (GetUpper) in the range to produce a new address. // If the given increment is negative, adds the value to the lower address (GetLower) in the range to produce a new address. // If the increment is zero, returns this address. // // If this is a single address value, that address is simply incremented by the given increment value, positive or negative. // // On address overflow or underflow, IncrementBoundary returns nil. func (addr *MACAddress) IncrementBoundary(increment int64) *MACAddress { return addr.init().incrementBoundary(increment).ToMAC() } // Increment returns the address from the address collection that is the given increment upwards into the address range, // with the increment of 0 returning the first address in the range. // // If the increment i matches or exceeds the size count c, then i - c + 1 // is added to the upper address of the range. // An increment matching the range count gives you the address just above the highest address in the range. // // If the increment is negative, it is added to the lower address of the range. // To get the address just below the lowest address of the address range, use the increment -1. // // If this is just a single address value, the address is simply incremented by the given increment, positive or negative. // // If this is an address range with multiple values, a positive increment i is equivalent i + 1 values from the iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the range count is equivalent to the same number of iterator values preceding the upper bound of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On address overflow or underflow, Increment returns nil. func (addr *MACAddress) Increment(increment int64) *MACAddress { return addr.init().increment(increment).ToMAC() } // ReverseBytes returns a new address with the bytes reversed. Any prefix length is dropped. func (addr *MACAddress) ReverseBytes() *MACAddress { return addr.checkIdentity(addr.GetSection().ReverseBytes()) } // ReverseBits returns a new address with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a segment range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (addr *MACAddress) ReverseBits(perByte bool) (*MACAddress, addrerr.IncompatibleAddressError) { res, err := addr.GetSection().ReverseBits(perByte) if err != nil { return nil, err } return addr.checkIdentity(res), nil } // ReverseSegments returns a new address with the segments reversed. func (addr *MACAddress) ReverseSegments() *MACAddress { return addr.checkIdentity(addr.GetSection().ReverseSegments()) } // ReplaceLen replaces segments starting from startIndex and ending before endIndex with the same number of segments starting at replacementStartIndex from the replacement section. // Mappings to or from indices outside the range of this or the replacement address are skipped. func (addr *MACAddress) ReplaceLen(startIndex, endIndex int, replacement *MACAddress, replacementIndex int) *MACAddress { replacementSegCount := replacement.GetSegmentCount() if replacementIndex <= 0 { startIndex -= replacementIndex replacementIndex = 0 } else if replacementIndex >= replacementSegCount { return addr } // We must do a 1 to 1 adjustment of indices before calling the section replace which would do an adjustment of indices not 1 to 1. // Here we assume replacementIndex is 0 and working on the subsection starting at that index. // In other words, a replacementIndex of x on the whole section is equivalent to replacementIndex of 0 on the shorter subsection starting at x. // Then afterwards we use the original replacement index to work on the whole section again, adjusting as needed. startIndex, endIndex, replacementIndexAdjustment := adjust1To1Indices(startIndex, endIndex, addr.GetSegmentCount(), replacementSegCount-replacementIndex) if startIndex == endIndex { return addr } replacementIndex += replacementIndexAdjustment count := endIndex - startIndex return addr.init().checkIdentity(addr.GetSection().ReplaceLen(startIndex, endIndex, replacement.GetSection(), replacementIndex, replacementIndex+count)) } // Replace replaces segments starting from startIndex with segments from the replacement section. func (addr *MACAddress) Replace(startIndex int, replacement *MACAddressSection) *MACAddress { // We must do a 1 to 1 adjustment of indices before calling the section replace which would do an adjustment of indices not 1 to 1. startIndex, endIndex, replacementIndex := adjust1To1Indices(startIndex, startIndex+replacement.GetSegmentCount(), addr.GetSegmentCount(), replacement.GetSegmentCount()) count := endIndex - startIndex return addr.init().checkIdentity(addr.GetSection().ReplaceLen(startIndex, endIndex, replacement, replacementIndex, replacementIndex+count)) } // GetOUISection returns a section with the first 3 segments, the organizational unique identifier func (addr *MACAddress) GetOUISection() *MACAddressSection { return addr.GetSubSection(0, MACOrganizationalUniqueIdentifierSegmentCount) } // GetODISection returns a section with the segments following the first 3 segments, the organizational distinct identifier func (addr *MACAddress) GetODISection() *MACAddressSection { return addr.GetTrailingSection(MACOrganizationalUniqueIdentifierSegmentCount) } // ToOUIPrefixBlock returns a section in which the range of values match the full block for the OUI (organizationally unique identifier) bytes func (addr *MACAddress) ToOUIPrefixBlock() *MACAddress { segmentCount := addr.GetSegmentCount() currentPref := addr.getPrefixLen() newPref := BitCount(MACOrganizationalUniqueIdentifierSegmentCount) << 3 //ouiSegmentCount * MACAddress.BITS_PER_SEGMENT createNew := currentPref == nil || currentPref.bitCount() > newPref if !createNew { newPref = currentPref.bitCount() for i := MACOrganizationalUniqueIdentifierSegmentCount; i < segmentCount; i++ { segment := addr.GetSegment(i) if !segment.IsFullRange() { createNew = true break } } } if !createNew { return addr } segmentIndex := MACOrganizationalUniqueIdentifierSegmentCount newSegs := createSegmentArray(segmentCount) addr.GetSection().copySubDivisions(0, segmentIndex, newSegs) allRangeSegment := allRangeMACSeg.ToDiv() for i := segmentIndex; i < segmentCount; i++ { newSegs[i] = allRangeSegment } newSect := createSectionMultiple(newSegs, cacheBitCount(newPref), addr.getAddrType(), true).ToMAC() return newMACAddress(newSect) } var IPv6LinkLocalPrefix = createLinkLocalPrefix() func createLinkLocalPrefix() *IPv6AddressSection { zeroSeg := zeroIPv6Seg.ToDiv() segs := []*AddressDivision{ NewIPv6Segment(0xfe80).ToDiv(), zeroSeg, zeroSeg, zeroSeg, } return newIPv6Section(segs) } // ToLinkLocalIPv6 converts to a link-local Ipv6 address. Any MAC prefix length is ignored. Other elements of this address section are incorporated into the conversion. // This will provide the latter 4 segments of an IPv6 address, to be paired with the link-local IPv6 prefix of 4 segments. func (addr *MACAddress) ToLinkLocalIPv6() (*IPv6Address, addrerr.IncompatibleAddressError) { sect, err := addr.ToEUI64IPv6() if err != nil { return nil, err } return newIPv6Address(IPv6LinkLocalPrefix.Append(sect)), nil } // ToEUI64IPv6 converts to an Ipv6 address section. Any MAC prefix length is ignored. Other elements of this address section are incorporated into the conversion. // This will provide the latter 4 segments of an IPv6 address, to be paired with an IPv6 prefix of 4 segments. func (addr *MACAddress) ToEUI64IPv6() (*IPv6AddressSection, addrerr.IncompatibleAddressError) { return NewIPv6SectionFromMAC(addr.init()) } // IsEUI64 returns whether this section is consistent with an IPv6 EUI64Size section, // which means it came from an extended 8 byte address, // and the corresponding segments in the middle match 0xff and 0xff/fe for MAC/not-MAC func (addr *MACAddress) IsEUI64(asMAC bool) bool { if addr.GetSegmentCount() == ExtendedUniqueIdentifier64SegmentCount { //getSegmentCount() == EXTENDED_UNIQUE_IDENTIFIER_64_SEGMENT_COUNT section := addr.GetSection() seg3 := section.GetSegment(3) seg4 := section.GetSegment(4) if seg3.matches(0xff) { if asMAC { return seg4.matches(0xff) } return seg4.matches(0xfe) } } return false } // ToEUI64 converts to IPv6 EUI-64 section // // http://standards.ieee.org/develop/regauth/tut/eui64.pdf // // If asMAC if true, this address is considered MAC and the EUI-64 is extended using ff-ff, otherwise this address is considered EUI-48 and extended using ff-fe // Note that IPv6 treats MAC as EUI-48 and extends MAC to IPv6 addresses using ff-fe func (addr *MACAddress) ToEUI64(asMAC bool) (*MACAddress, addrerr.IncompatibleAddressError) { section := addr.GetSection() if addr.GetSegmentCount() == ExtendedUniqueIdentifier48SegmentCount { segs := createSegmentArray(ExtendedUniqueIdentifier64SegmentCount) section.copySubDivisions(0, 3, segs) segs[3] = ffMACSeg.ToDiv() if asMAC { segs[4] = ffMACSeg.ToDiv() } else { segs[4] = feMACSeg.ToDiv() } section.copySubDivisions(3, 6, segs[5:]) prefixLen := addr.getPrefixLen() if prefixLen != nil { if prefixLen.bitCount() >= 24 { prefixLen = cacheBitCount(prefixLen.bitCount() + (MACBitsPerSegment << 1)) //two segments } } newSect := createInitializedSection(segs, prefixLen, addr.getAddrType()).ToMAC() return newMACAddress(newSect), nil } seg3 := section.GetSegment(3) seg4 := section.GetSegment(4) if seg3.matches(0xff) { if asMAC { if seg4.matches(0xff) { return addr, nil } } else { if seg4.matches(0xfe) { return addr, nil } } } return nil, &incompatibleAddressError{addressError{key: "ipaddress.mac.error.not.eui.convertible"}} } // String implements the [fmt.Stringer] interface, returning the canonical string provided by ToCanonicalString, or "" if the receiver is a nil pointer. func (addr *MACAddress) String() string { if addr == nil { return nilString() } return addr.init().addressInternal.toString() } // Format implements [fmt.Formatter] interface. It accepts the formats // - 'v' for the default address and section format (either the normalized or canonical string), // - 's' (string) for the same, // - 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix), // - 'd' (decimal), 'x' (lowercase hexadecimal), and // - 'X' (uppercase hexadecimal). // Also supported are some of fmt's format flags for integral types. // Sign control is not supported since addresses and sections are never negative. // '#' for an alternate format is supported, which adds a leading zero for octal, and for hexadecimal it adds // a leading "0x" or "0X" for "%#x" and "%#X" respectively. // Also supported is specification of minimum digits precision, output field width, // space or zero padding, and '-' for left or right justification. func (addr MACAddress) Format(state fmt.State, verb rune) { addr.init().format(state, verb) } // GetSegmentStrings returns a slice with the string for each segment being the string that is normalized with wildcards. func (addr *MACAddress) GetSegmentStrings() []string { if addr == nil { return nil } return addr.init().getSegmentStrings() } // ToCanonicalString produces a canonical string for the address. // // For MAC, it uses the canonical standardized IEEE 802 MAC address representation of xx-xx-xx-xx-xx-xx. An example is "01-23-45-67-89-ab". // For range segments, '|' is used: "11-22-33|44-55-66". // // Each MAC address has a unique canonical string. func (addr *MACAddress) ToCanonicalString() string { if addr == nil { return nilString() } return addr.init().toCanonicalString() } // ToNormalizedString produces a normalized string for the address. // // For MAC, it differs from the canonical string. It uses the most common representation of MAC addresses: "xx:xx:xx:xx:xx:xx". An example is "01:23:45:67:89:ab". // For range segments, '-' is used: "11:22:33-44:55:66". // // Each address has a unique normalized string. func (addr *MACAddress) ToNormalizedString() string { if addr == nil { return nilString() } return addr.init().toNormalizedString() } // ToNormalizedWildcardString produces the normalized string. func (addr *MACAddress) ToNormalizedWildcardString() string { return addr.toNormalizedWildcardString() } // ToCompressedString produces a short representation of this address while remaining within the confines of standard representation(s) of the address. // // For MAC, it differs from the canonical string. It produces a shorter string for the address that has no leading zeros. func (addr *MACAddress) ToCompressedString() string { if addr == nil { return nilString() } return addr.init().toCompressedString() } // ToHexString writes this address as a single hexadecimal value (possibly two values if a range), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // If an address collection cannot be written as a range of two values, an error is returned. func (addr *MACAddress) ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toHexString(with0xPrefix) } // ToOctalString writes this address as a single octal value (possibly two values if a range), // the number of digits according to the bit count, with or without a preceding "0" prefix. // // If a multiple-valued address collection cannot be written as a single prefix block or a range of two values, an error is returned. func (addr *MACAddress) ToOctalString(with0Prefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toOctalString(with0Prefix) } // ToBinaryString writes this address as a single binary value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0b" prefix. // // If an address collection cannot be written as a range of two values, an error is returned. func (addr *MACAddress) ToBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().toBinaryString(with0bPrefix) } // GetDottedAddress returns an AddressDivisionGrouping which organizes the address into segments of bit-length 16, rather than the more typical 8 bits per segment. // // If this represents a collection of MAC addresses, this returns an error when unable to join two address segments, // the first with a range of values, into a division of the larger bit-length that represents the same set of values. func (addr *MACAddress) GetDottedAddress() (*AddressDivisionGrouping, addrerr.IncompatibleAddressError) { return addr.init().GetSection().GetDottedGrouping() } // ToDottedString produces the dotted hexadecimal format aaaa.bbbb.cccc func (addr *MACAddress) ToDottedString() (string, addrerr.IncompatibleAddressError) { if addr == nil { return nilString(), nil } return addr.init().GetSection().ToDottedString() } // ToSpaceDelimitedString produces a string delimited by spaces: aa bb cc dd ee ff func (addr *MACAddress) ToSpaceDelimitedString() string { if addr == nil { return nilString() } return addr.init().GetSection().ToSpaceDelimitedString() } // ToDashedString produces a string delimited by dashes: "aa-bb-cc-dd-ee-ff". // For range segments, '|' is used: "11-22-33|44-55-66". // It returns the same string as ToCanonicalString. func (addr *MACAddress) ToDashedString() string { if addr == nil { return nilString() } return addr.init().GetSection().ToDashedString() } // ToColonDelimitedString produces a string delimited by colons: "aa:bb:cc:dd:ee:ff". // For range segments, '-' is used: "11:22:33-44:55:66". // It returns the same string as ToNormalizedString. func (addr *MACAddress) ToColonDelimitedString() string { if addr == nil { return nilString() } return addr.init().GetSection().ToColonDelimitedString() } // ToCustomString creates a customized string from this address or address collection according to the given string option parameters. func (addr *MACAddress) ToCustomString(stringOptions addrstr.StringOptions) string { if addr == nil { return nilString() } return addr.init().GetSection().toCustomString(stringOptions) } // ToAddressString retrieves or generates a MACAddressString instance for this MACAddress instance. // This may be the MACAddressString this instance was generated from, if it was generated from a MACAddressString. // // In general, users are intended to create MACAddress instances from MACAddressString instances, // while the reverse direction is generally not common and not useful, except under specific circumstances. // // However, the reverse direction can be useful under certain circumstances, // such as when maintaining a collection of MACAddressString instances. func (addr *MACAddress) ToAddressString() *MACAddressString { addr = addr.init() cache := addr.cache if cache != nil { res := addr.cache.identifierStr if res != nil { hostIdStr := res.idStr return hostIdStr.(*MACAddressString) } } return newMACAddressStringFromAddr(addr.toCanonicalString(), addr) } func (addr *MACAddress) toMaxLower() *MACAddress { return addr.init().addressInternal.toMaxLower().ToMAC() } func (addr *MACAddress) toMinUpper() *MACAddress { return addr.init().addressInternal.toMinUpper().ToMAC() } // ToAddressBase converts to an Address, a polymorphic type usable with all addresses and subnets. // Afterwards, you can convert back with ToMAC. // // ToAddressBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (addr *MACAddress) ToAddressBase() *Address { if addr != nil { addr = addr.init() } return (*Address)(addr) } // Wrap wraps this address, returning a WrappedAddress, an implementation of ExtendedSegmentSeries, // which can be used to write code that works with both addresses and address sections. func (addr *MACAddress) Wrap() WrappedAddress { return wrapAddress(addr.ToAddressBase()) } // ToKey creates the associated address key. // While addresses can be compared with the Compare, TrieCompare or Equal methods as well as various provided instances of AddressComparator, // they are not comparable with Go operators. // However, AddressKey instances are comparable with Go operators, and thus can be used as map keys. func (addr *MACAddress) ToKey() MACAddressKey { key := MACAddressKey{ additionalByteCount: uint8(addr.GetSegmentCount()) - MediaAccessControlSegmentCount, } section := addr.GetSection() divs := section.getDivArray() var lowerVal, upperVal uint64 if addr.IsMultiple() { for _, div := range divs { seg := div.ToMAC() lowerVal = (lowerVal << MACBitsPerSegment) | uint64(seg.GetMACSegmentValue()) upperVal = (upperVal << MACBitsPerSegment) | uint64(seg.GetMACUpperSegmentValue()) } } else { for _, div := range divs { seg := div.ToMAC() lowerVal = (lowerVal << MACBitsPerSegment) | uint64(seg.GetMACSegmentValue()) } upperVal = lowerVal } key.vals.lower = lowerVal key.vals.upper = upperVal return key } func fromMACKey(key MACAddressKey) *MACAddress { additionalByteCount := key.additionalByteCount segCount := int(additionalByteCount) + MediaAccessControlSegmentCount return NewMACAddressFromRangeExt( func(segmentIndex int) MACSegInt { segIndex := (segCount - 1) - segmentIndex return MACSegInt(key.vals.lower >> (segIndex << macBitsToSegmentBitshift)) }, func(segmentIndex int) MACSegInt { segIndex := (segCount - 1) - segmentIndex return MACSegInt(key.vals.upper >> (segIndex << macBitsToSegmentBitshift)) }, additionalByteCount != 0, ) } // ToGenericKey produces a generic Key[*MACAddress] that can be used with generic code working with [Address], [IPAddress], [IPv4Address], [IPv6Address] and [MACAddress]. // ToKey produces a more compact key for code that is MAC-specific. func (addr *MACAddress) ToGenericKey() Key[*MACAddress] { // Note: We intentionally do not populate the "scheme" field for MAC-48. // With Key[*IPv4Address], by leaving the scheme zero for MAC-48, the zero Key[*MACAddress] matches up with the key produced here by the zero address. // We do not need the scheme field for Key[*MACAddress] since the generic type indicates MAC, but we do need a flag to distinguish 64-bit EUI-64. key := Key[*MACAddress]{} if isExtended := addr.GetSegmentCount() == ExtendedUniqueIdentifier64SegmentCount; isExtended { key.scheme = eui64Scheme } addr.init().toMACKey(&key.keyContents) return key } func (addr *MACAddress) fromKey(scheme addressScheme, key *keyContents) *MACAddress { // See ToGenericKey for details such as the fact that the scheme is populated only for eui64Scheme return fromMACAddrKey(scheme, key) } func (addr *MACAddress) toMACKey(contents *keyContents) { section := addr.GetSection() divs := section.getDivArray() if addr.IsMultiple() { for i, div := range divs { seg := div.ToMAC() val := &contents.vals[i>>3] val.lower = (val.lower << MACBitsPerSegment) | uint64(seg.GetMACSegmentValue()) val.upper = (val.upper << MACBitsPerSegment) | uint64(seg.GetMACUpperSegmentValue()) } } else { for i, div := range divs { seg := div.ToMAC() val := &contents.vals[i>>3] newLower := (val.lower << MACBitsPerSegment) | uint64(seg.GetMACSegmentValue()) val.lower = newLower val.upper = newLower } } } func fromMACAddrKey(scheme addressScheme, key *keyContents) *MACAddress { segCount := MediaAccessControlSegmentCount isExtended := false // Note: the check here must be for eui64Scheme and not mac48Scheme // ToGenericKey will only populate the scheme to eui64Scheme, it will be left as 0 otherwise if isExtended = scheme == eui64Scheme; isExtended { segCount = ExtendedUniqueIdentifier64SegmentCount } return NewMACAddressFromRangeExt( func(segmentIndex int) MACSegInt { valsIndex := segmentIndex >> 3 segIndex := ((segCount - 1) - segmentIndex) & 0x7 return MACSegInt(key.vals[valsIndex].lower >> (segIndex << macBitsToSegmentBitshift)) }, func(segmentIndex int) MACSegInt { valsIndex := segmentIndex >> 3 segIndex := ((segCount - 1) - segmentIndex) & 0x7 return MACSegInt(key.vals[valsIndex].upper >> (segIndex << macBitsToSegmentBitshift)) }, isExtended, ) } ipaddress-go-1.5.4/ipaddr/macaddressprovider.go000066400000000000000000000065721440250641600215710ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "sync" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) type macAddressProvider interface { getAddress() (*MACAddress, addrerr.IncompatibleAddressError) // If the address was created by parsing, this provides the parameters used when creating the address, // otherwise nil getParameters() addrstrparam.MACAddressStringParams } type macAddressNullProvider struct { validationOptions addrstrparam.MACAddressStringParams } var invalidMACProvider = macAddressEmptyProvider{macAddressNullProvider{defaultMACAddrParameters}} func (provider macAddressNullProvider) getParameters() addrstrparam.MACAddressStringParams { return provider.validationOptions } func (provider macAddressNullProvider) getAddress() (*MACAddress, addrerr.IncompatibleAddressError) { return nil, nil } type macAddressEmptyProvider struct { macAddressNullProvider } var defaultMACAddressEmptyProvider = macAddressEmptyProvider{macAddressNullProvider{defaultMACAddrParameters}} type macAddressAllProvider struct { validationOptions addrstrparam.MACAddressStringParams address *MACAddress creationLock *sync.Mutex } func (provider *macAddressAllProvider) getParameters() addrstrparam.MACAddressStringParams { return provider.validationOptions } func (provider *macAddressAllProvider) getAddress() (*MACAddress, addrerr.IncompatibleAddressError) { addr := provider.address if addr == nil { provider.creationLock.Lock() addr = provider.address if addr == nil { validationOptions := provider.validationOptions size := validationOptions.GetPreferredLen() creator := macType.getNetwork().getAddressCreator() var segCount int if size == addrstrparam.EUI64Len { segCount = ExtendedUniqueIdentifier64SegmentCount } else { segCount = MediaAccessControlSegmentCount } allRangeSegment := creator.createRangeSegment(0, MACMaxValuePerSegment) segments := make([]*AddressDivision, segCount) for i := range segments { segments[i] = allRangeSegment } section := creator.createSectionInternal(segments, true) addr = creator.createAddressInternal(section.ToSectionBase(), nil).ToMAC() } provider.creationLock.Unlock() } return addr, nil } var macAddressDefaultAllProvider = &macAddressAllProvider{validationOptions: defaultMACAddrParameters, creationLock: &sync.Mutex{}} type wrappedMACAddressProvider struct { address *MACAddress } func (provider wrappedMACAddressProvider) getParameters() addrstrparam.MACAddressStringParams { return nil } func (provider wrappedMACAddressProvider) getAddress() (*MACAddress, addrerr.IncompatibleAddressError) { return provider.address, nil } var ( _, _, _ macAddressProvider = macAddressEmptyProvider{}, &macAddressAllProvider{}, &wrappedMACAddressProvider{} ) ipaddress-go-1.5.4/ipaddr/macaddrstr.go000066400000000000000000000314221440250641600200240ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "strings" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) var defaultMACAddrParameters = new(addrstrparam.MACAddressStringParamsBuilder).ToParams() // NewMACAddressStringParams constructs a MACAddressString that will parse the given string according to the given parameters. func NewMACAddressStringParams(str string, params addrstrparam.MACAddressStringParams) *MACAddressString { var p addrstrparam.MACAddressStringParams if params == nil { p = defaultMACAddrParameters } else { p = addrstrparam.CopyMACAddressStringParams(params) } return parseMACAddressString(str, p) } // NewMACAddressString constructs a MACAddressString that will parse the given string according to the default parameters. func NewMACAddressString(str string) *MACAddressString { return parseMACAddressString(str, defaultMACAddrParameters) } func newMACAddressStringFromAddr(str string, addr *MACAddress) *MACAddressString { return &MACAddressString{ str: str, addressProvider: wrappedMACAddressProvider{addr}, } } func parseMACAddressString(str string, params addrstrparam.MACAddressStringParams) *MACAddressString { str = strings.TrimSpace(str) res := &MACAddressString{str: str} res.validate(params) return res } var zeroMACAddressString = NewMACAddressString("") // MACAddressString parses the string representation of a MAC address. Such a string can represent just a single address or a collection of addresses like "1:*:1-3:1-4:5:6". // // This supports a wide range of address formats and provides specific error messages, and allows specific configuration. // // You can control all the supported formats using MACAddressStringParamsBuilder to build a parameters instance of MACAddressStringParams. // When not using the constructor that takes a MACAddressStringParams, a default instance of MACAddressStringParams is used that is generally permissive. // // Supported Formats // // Ranges are supported: // // • wildcards '*' and ranges '-' (for example "1:*:1-3:1-4:5:6"), useful for working with MAC address collections // • SQL wildcards '%" and "_", although '%' is considered an SQL wildcard only when it is not considered an IPv6 zone indicator // // // The different methods of representing MAC addresses are supported: // // • 6 or 8 bytes in hex representation like "aa:bb:cc:dd:ee:ff" // • The same but with a hyphen separator like "aa-bb-cc-dd-ee-ff" (the range separator in this case becomes '/') // • The same but with space separator like "aa bb cc dd ee ff" // • The dotted representation, 4 sets of 12 bits in hex representation like "aaa.bbb.ccc.ddd" // • The 12 or 16 hex representation with no separators like "aabbccddeeff" // // // All of the above range variations also work for each of these ways of representing MAC addresses. // // Some additional formats: // // • null or empty strings representing an unspecified address // • the single wildcard address "*" which represents all MAC addresses // // // Usage // Once you have constructed a MACAddressString object, you can convert it to a [MACAddress] object with GetAddress or ToAddress. // // For empty addresses, both ToAddress and GetAddress return nil. For invalid addresses, GetAddress and ToAddress return nil, with ToAddress also returning an error. // // This type is concurrency-safe. In fact, MACAddressString objects are immutable. // A MACAddressString object represents a single MAC address representation that cannot be changed after construction. // Some derived state is created upon demand and cached, such as the derived [MACAddress] instances. type MACAddressString struct { str string addressProvider macAddressProvider validateError addrerr.AddressStringError } func (addrStr *MACAddressString) init() *MACAddressString { if addrStr.addressProvider == nil && addrStr.validateError == nil { return zeroMACAddressString } return addrStr } // GetValidationOptions returns the validation options supplied when constructing this address string, // or the default options if no options were supplied. It returns nil if no parameters were used to construct the address. func (addrStr *MACAddressString) GetValidationOptions() addrstrparam.MACAddressStringParams { provider, _ := addrStr.getAddressProvider() if provider != nil { return provider.getParameters() } return nil } // String implements the [fmt.Stringer] interface, // returning the original string used to create this MACAddressString (altered by strings.TrimSpace), // or "" if the receiver is a nil pointer. func (addrStr *MACAddressString) String() string { if addrStr == nil { return nilString() } return addrStr.str } // Format implements the [fmt.Formatter] interface. // It accepts the verbs hat are applicable to strings, // namely the verbs %s, %q, %x and %X. func (addrStr MACAddressString) Format(state fmt.State, verb rune) { s := flagsFromState(state, verb) _, _ = state.Write([]byte(fmt.Sprintf(s, addrStr.str))) } // ToNormalizedString produces a normalized string for the address. // // For MAC, it differs from the canonical string. It uses the most common representation of MAC addresses: "xx:xx:xx:xx:xx:xx". An example is "01:23:45:67:89:ab". // For range segments, '-' is used: "11:22:33-44:55:66". // // If the original string is not a valid address string, the original string is used. func (addrStr *MACAddressString) ToNormalizedString() string { addr := addrStr.GetAddress() if addr != nil { return addr.toNormalizedString() } return addrStr.String() } // GetAddress returns the MAC address if this MACAddressString is a valid string representing a MAC address or address collection. Otherwise, it returns nil. // // Use ToAddress for an equivalent method that returns an error when the format is invalid. func (addrStr *MACAddressString) GetAddress() *MACAddress { addr, _ := addrStr.ToAddress() return addr } // ToAddress produces the MACAddress corresponding to this MACAddressString. // // If this object does not represent a specific MACAddress or address collection, nil is returned. // // If the string used to construct this object is not a known format (empty string, address, or range of addresses) then this method returns an error. // // An equivalent method that does not return the error is GetAddress. // // The error can be addrerr.AddressStringError for an invalid string, or addrerr.IncompatibleAddressError for non-standard strings that cannot be converted to MACAddress. func (addrStr *MACAddressString) ToAddress() (*MACAddress, addrerr.AddressError) { provider, err := addrStr.getAddressProvider() if err != nil { return nil, err } return provider.getAddress() } // IsPrefixed returns whether this address has an associated prefix length, // which for MAC means that the string represents the set of all addresses with the same prefix. func (addrStr *MACAddressString) IsPrefixed() bool { return addrStr.getPrefixLen() != nil } // GetPrefixLen returns the prefix length if this address is a prefixed address, otherwise it returns nil. // // For MAC addresses, the prefix is initially inferred from the range, so "1:2:3:*:*:*" has a prefix length of 24. // Addresses derived from the original may retain the original prefix length regardless of their range. func (addrStr *MACAddressString) GetPrefixLen() PrefixLen { return addrStr.getPrefixLen().copy() } func (addrStr *MACAddressString) getPrefixLen() PrefixLen { addr := addrStr.GetAddress() if addr != nil { return addr.getPrefixLen() } return nil } // IsFullRange returns whether the address represents the set of all valid MAC addresses for its address length func (addrStr *MACAddressString) IsFullRange() bool { addr := addrStr.GetAddress() return addr != nil && addr.IsFullRange() } // IsEmpty returns true if the address is empty (zero-length). func (addrStr *MACAddressString) IsEmpty() bool { addr, err := addrStr.ToAddress() return err == nil && addr == nil } // IsZero returns whether this string represents a MAC address whose value is exactly zero. func (addrStr *MACAddressString) IsZero() bool { addr := addrStr.GetAddress() return addr != nil && addr.IsZero() } // IsValid returns whether this is a valid MAC address string format. // The accepted MAC address formats are: // a MAC address or address collection, the address representing all MAC addresses, or an empty string. // If this method returns false, and you want more details, call Validate and examine the error. func (addrStr *MACAddressString) IsValid() bool { return addrStr.Validate() == nil } func (addrStr *MACAddressString) getAddressProvider() (macAddressProvider, addrerr.AddressStringError) { addrStr = addrStr.init() err := addrStr.Validate() return addrStr.addressProvider, err } func (addrStr *MACAddressString) validate(validationOptions addrstrparam.MACAddressStringParams) { addrStr.addressProvider, addrStr.validateError = validator.validateMACAddressStr(addrStr, validationOptions) } // Validate validates that this string is a valid address, and if not, throws an exception with a descriptive message indicating why it is not. func (addrStr *MACAddressString) Validate() addrerr.AddressStringError { return addrStr.init().validateError } // Compare compares this address string with another, // returning a negative number, zero, or a positive number if this address string is less than, equal to, or greater than the other. // // All address strings are comparable. If two address strings are invalid, their strings are compared. // Two valid address trings are compared using the comparison rules for their respective addresses. func (addrStr *MACAddressString) Compare(other *MACAddressString) int { if addrStr == other { return 0 } else if addrStr == nil { return -1 } else if other == nil { return 1 } addrStr = addrStr.init() other = other.init() if addrStr == other { return 0 } if addrStr.IsValid() { if other.IsValid() { addr := addrStr.GetAddress() if addr != nil { otherAddr := other.GetAddress() if otherAddr != nil { return addr.Compare(otherAddr) } } // one or the other is nil, either empty or IncompatibleAddressException return strings.Compare(addrStr.String(), other.String()) } return 1 } else if other.IsValid() { return -1 } return strings.Compare(addrStr.String(), other.String()) } // Equal returns whether this MACAddressString is equal to the given one. // Two MACAddressString objects are equal if they represent the same set of addresses. // // If a MACAddressString is invalid, it is equal to another address only if the other address was constructed from the same string. func (addrStr *MACAddressString) Equal(other *MACAddressString) bool { if addrStr == nil { return other == nil } else if other == nil { return false } addrStr = addrStr.init() other = other.init() if addrStr == other { return true } //if they have the same string, they must be the same, //but the converse is not true, if they have different strings, they can still be the same // Also note that we do not call equals() on the validation options, this is intended as an optimization, // and probably better to avoid going through all the validation objects here stringsMatch := addrStr.String() == other.String() if stringsMatch && addrStr.GetValidationOptions() == other.GetValidationOptions() { return true } if addrStr.IsValid() { if other.IsValid() { value := addrStr.GetAddress() if value != nil { otherValue := other.GetAddress() if otherValue != nil { return value.equals(otherValue) } else { return false } } else if other.GetAddress() != nil { return false } // both are nil, either empty or addrerr.IncompatibleAddressError return stringsMatch } } else if !other.IsValid() { // both are invalid return stringsMatch // Two invalid addresses are not equal unless strings match, regardless of validation options } return false } // Wrap wraps this address string, returning a WrappedMACAddressString as an implementation of ExtendedIdentifierString, // which can be used to write code that works with different host identifier types polymorphically, including IPAddressString, MACAddressString, and HostName. func (addrStr *MACAddressString) Wrap() ExtendedIdentifierString { return WrappedMACAddressString{addrStr} } ipaddress-go-1.5.4/ipaddr/macsection.go000066400000000000000000001131261440250641600200270ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math/big" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) func createMACSection(segments []*AddressDivision) *MACAddressSection { return &MACAddressSection{ addressSectionInternal{ addressDivisionGroupingInternal{ addressDivisionGroupingBase: addressDivisionGroupingBase{ divisions: standardDivArray(segments), addrType: macType, cache: &valueCache{ stringCache: stringCache{ macStringCache: &macStringCache{}, }, }, }, }, }, } } // NewMACSection constructs a MAC address or address collection section from the given segments. func NewMACSection(segments []*MACAddressSegment) *MACAddressSection { return createMACSectionFromSegs(segments) } func createMACSectionFromSegs(orig []*MACAddressSegment) *MACAddressSection { segCount := len(orig) newSegs := make([]*AddressDivision, segCount) var newPref PrefixLen isMultiple := false if segCount != 0 { isBlock := true for i := segCount - 1; i >= 0; i-- { segment := orig[i] if segment == nil { segment = zeroMACSeg if isBlock && i != segCount-1 { newPref = getNetworkPrefixLen(MACBitsPerSegment, MACBitsPerSegment, i) isBlock = false } } else { if isBlock { minPref := segment.GetMinPrefixLenForBlock() if minPref > 0 { if minPref != MACBitsPerSegment || i != segCount-1 { newPref = getNetworkPrefixLen(MACBitsPerSegment, minPref, i) } isBlock = false } } isMultiple = isMultiple || segment.isMultiple() } newSegs[i] = segment.ToDiv() } if isBlock { newPref = cacheBitCount(0) } } res := createMACSection(newSegs) res.isMult = isMultiple res.prefixLength = newPref return res } func newMACSectionParsed(segments []*AddressDivision, isMultiple bool) (res *MACAddressSection) { res = createMACSection(segments) res.initImplicitPrefLen(MACBitsPerSegment) res.isMult = isMultiple return } func newMACSectionEUI(segments []*AddressDivision) (res *MACAddressSection) { res = createMACSection(segments) res.initMultAndImplicitPrefLen(MACBitsPerSegment) return } // NewMACSectionFromBytes constructs a MAC address section from the given byte slice. // The segment count is determined by the slice length, even if the segment count exceeds 8 segments. func NewMACSectionFromBytes(bytes []byte, segmentCount int) (res *MACAddressSection, err addrerr.AddressValueError) { if segmentCount < 0 { segmentCount = len(bytes) } expectedByteCount := segmentCount segments, err := toSegments( bytes, segmentCount, MACBytesPerSegment, MACBitsPerSegment, macNetwork.getAddressCreator(), nil) if err == nil { // note prefix len is nil res = createMACSection(segments) if expectedByteCount == len(bytes) { bytes = cloneBytes(bytes) res.cache.bytesCache = &bytesCache{lowerBytes: bytes} if !res.isMult { // not a prefix block res.cache.bytesCache.upperBytes = bytes } } } return } // NewMACSectionFromUint64 constructs a MAC address section of the given segment count from the given value. // The least significant bits of the given value will be used. func NewMACSectionFromUint64(val uint64, segmentCount int) (res *MACAddressSection) { if segmentCount < 0 { segmentCount = MediaAccessControlSegmentCount } segments := createSegmentsUint64( segmentCount, 0, val, MACBytesPerSegment, MACBitsPerSegment, macNetwork.getAddressCreator(), nil) // note prefix len is nil res = createMACSection(segments) return } // NewMACSectionFromVals constructs a MAC address section of the given segment count from the given values. func NewMACSectionFromVals(vals MACSegmentValueProvider, segmentCount int) (res *MACAddressSection) { res = NewMACSectionFromRange(vals, nil, segmentCount) return } // NewMACSectionFromRange constructs a MAC address collection section of the given segment count from the given values. func NewMACSectionFromRange(vals, upperVals MACSegmentValueProvider, segmentCount int) (res *MACAddressSection) { if segmentCount < 0 { segmentCount = 0 } segments, isMultiple := createSegments( WrapMACSegmentValueProvider(vals), WrapMACSegmentValueProvider(upperVals), segmentCount, MACBitsPerSegment, macNetwork.getAddressCreator(), nil) res = createMACSection(segments) if isMultiple { res.initImplicitPrefLen(MACBitsPerSegment) res.isMult = true } return } // MACAddressSection is a section of a MACAddress. // // It is a series of 0 to 8 individual MAC address segments. type MACAddressSection struct { addressSectionInternal } // Contains returns whether this is same type and version as the given address section and whether it contains all values in the given section. // // Sections must also have the same number of segments to be comparable, otherwise false is returned. func (section *MACAddressSection) Contains(other AddressSectionType) bool { if section == nil { return other == nil || other.ToSectionBase() == nil } return section.contains(other) } // Equal returns whether the given address section is equal to this address section. // Two address sections are equal if they represent the same set of sections. // They must match: // - type/version: MAC // - segment counts // - segment value ranges // Prefix lengths are ignored. func (section *MACAddressSection) Equal(other AddressSectionType) bool { if section == nil { return other == nil || other.ToSectionBase() == nil } return section.equal(other) } // Compare returns a negative integer, zero, or a positive integer if this address section is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (section *MACAddressSection) Compare(item AddressItem) int { return CountComparator.Compare(section, item) } // CompareSize compares the counts of two items, the number of individual items represented. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether this section represents more individual address sections than another. // // CompareSize returns a positive integer if this address section has a larger count than the one given, zero if they are the same, or a negative integer if the other has a larger count. func (section *MACAddressSection) CompareSize(other AddressItem) int { if section == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return section.compareSize(other) } // GetBitsPerSegment returns the number of bits comprising each segment in this section. Segments in the same address section are equal length. func (section *MACAddressSection) GetBitsPerSegment() BitCount { return MACBitsPerSegment } // GetBytesPerSegment returns the number of bytes comprising each segment in this section. Segments in the same address section are equal length. func (section *MACAddressSection) GetBytesPerSegment() int { return MACBytesPerSegment } // GetCount returns the count of possible distinct values for this item. // If not representing multiple values, the count is 1, // unless this is a division grouping with no divisions, or an address section with no segments, in which case it is 0. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (section *MACAddressSection) GetCount() *big.Int { if section == nil { return bigZero() } return section.cacheCount(func() *big.Int { return count(func(index int) uint64 { return section.GetSegment(index).GetValueCount() }, section.GetSegmentCount(), 6, 0x7fffffffffffff) }) } func (section *MACAddressSection) getCachedCount() *big.Int { if section == nil { return bigZero() } return section.cachedCount(func() *big.Int { return count(func(index int) uint64 { return section.GetSegment(index).GetValueCount() }, section.GetSegmentCount(), 6, 0x7fffffffffffff) }) } // IsMultiple returns whether this section represents multiple values. func (section *MACAddressSection) IsMultiple() bool { return section != nil && section.isMultiple() } // IsPrefixed returns whether this section has an associated prefix length. func (section *MACAddressSection) IsPrefixed() bool { return section != nil && section.isPrefixed() } // GetPrefixCount returns the number of distinct prefix values in this item. // // The prefix length is given by GetPrefixLen. // // If this has a non-nil prefix length, returns the number of distinct prefix values. // // If this has a nil prefix length, returns the same value as GetCount. func (section *MACAddressSection) GetPrefixCount() *big.Int { return section.cachePrefixCount(func() *big.Int { return section.GetPrefixCountLen(section.getPrefixLen().bitCount()) }) } // GetPrefixCountLen returns the number of distinct prefix values in this item for the given prefix length. func (section *MACAddressSection) GetPrefixCountLen(prefixLen BitCount) *big.Int { if prefixLen <= 0 { return bigOne() } else if bc := section.GetBitCount(); prefixLen >= bc { return section.GetCount() } networkSegmentIndex := getNetworkSegmentIndex(prefixLen, section.GetBytesPerSegment(), section.GetBitsPerSegment()) hostSegmentIndex := getHostSegmentIndex(prefixLen, section.GetBytesPerSegment(), section.GetBitsPerSegment()) return section.calcCount(func() *big.Int { return count(func(index int) uint64 { if (networkSegmentIndex == hostSegmentIndex) && index == networkSegmentIndex { segmentPrefixLength := getPrefixedSegmentPrefixLength(section.GetBitsPerSegment(), prefixLen, index) return getPrefixValueCount(section.GetSegment(index).ToSegmentBase(), segmentPrefixLength.bitCount()) } return section.GetSegment(index).GetValueCount() }, networkSegmentIndex+1, 6, 0x7fffffffffffff) }) } // GetBlockCount returns the count of distinct values in the given number of initial (more significant) segments. func (section *MACAddressSection) GetBlockCount(segments int) *big.Int { return section.calcCount(func() *big.Int { return count(func(index int) uint64 { return section.GetSegment(index).GetValueCount() }, segments, 6, 0x7fffffffffffff) }) } // WithoutPrefixLen provides the same address section but with no prefix length. The values remain unchanged. func (section *MACAddressSection) WithoutPrefixLen() *MACAddressSection { if !section.IsPrefixed() { return section } return section.withoutPrefixLen().ToMAC() } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address section. // The provided prefix length will be adjusted to these boundaries if necessary. func (section *MACAddressSection) SetPrefixLen(prefixLen BitCount) *MACAddressSection { return section.setPrefixLen(prefixLen).ToMAC() } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address section. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this address section has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this address section has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section *MACAddressSection) SetPrefixLenZeroed(prefixLen BitCount) (*MACAddressSection, addrerr.IncompatibleAddressError) { res, err := section.setPrefixLenZeroed(prefixLen) return res.ToMAC(), err } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address section. // // If this address section has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (section *MACAddressSection) AdjustPrefixLen(prefixLen BitCount) *AddressSection { return section.adjustPrefixLen(prefixLen).ToSectionBase() } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address section. // // If this address section has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section *MACAddressSection) AdjustPrefixLenZeroed(prefixLen BitCount) (*AddressSection, addrerr.IncompatibleAddressError) { res, err := section.adjustPrefixLenZeroed(prefixLen) return res.ToSectionBase(), err } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this address section. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such address section - it is required that the range of values match the range of a prefix block. // If there is no such address section, then nil is returned. func (section *MACAddressSection) AssignPrefixForSingleBlock() *MACAddressSection { return section.assignPrefixForSingleBlock().ToMAC() } // AssignMinPrefixForBlock returns an equivalent address section, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this address section. // // In other words, this method assigns a prefix length to this address section matching the largest prefix block in this address section. func (section *MACAddressSection) AssignMinPrefixForBlock() *MACAddressSection { return section.assignMinPrefixForBlock().ToMAC() } // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. func (section *MACAddressSection) GetSegment(index int) *MACAddressSegment { return section.getDivision(index).ToMAC() } // ForEachSegment visits each segment in order from most-significant to least, the most significant with index 0, calling the given function for each, terminating early if the function returns true. // Returns the number of visited segments. func (section *MACAddressSection) ForEachSegment(consumer func(segmentIndex int, segment *MACAddressSegment) (stop bool)) int { divArray := section.getDivArray() if divArray != nil { for i, div := range divArray { if consumer(i, div.ToMAC()) { return i + 1 } } } return len(divArray) } // ToDivGrouping converts to an AddressDivisionGrouping, a polymorphic type usable with all address sections and division groupings. // Afterwards, you can convert back with ToMAC. // // ToDivGrouping can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *MACAddressSection) ToDivGrouping() *AddressDivisionGrouping { return section.ToSectionBase().ToDivGrouping() } // ToSectionBase converts to an AddressSection, a polymorphic type usable with all address sections. // Afterwards, you can convert back with ToMAC. // // ToSectionBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *MACAddressSection) ToSectionBase() *AddressSection { return (*AddressSection)(section) } // Wrap wraps this address section, returning a WrappedAddressSection, an implementation of ExtendedSegmentSeries, // which can be used to write code that works with both addresses and address sections. func (section *MACAddressSection) Wrap() WrappedAddressSection { return wrapSection(section.ToSectionBase()) } // GetTrailingSection gets the subsection from the series starting from the given index. // The first segment is at index 0. func (section *MACAddressSection) GetTrailingSection(index int) *MACAddressSection { return section.GetSubSection(index, section.GetSegmentCount()) } // GetSubSection gets the subsection from the series starting from the given index and ending just before the give endIndex. // The first segment is at index 0. func (section *MACAddressSection) GetSubSection(index, endIndex int) *MACAddressSection { return section.getSubSection(index, endIndex).ToMAC() } // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. func (section *MACAddressSection) CopySegments(segs []*MACAddressSegment) (count int) { return section.ForEachSegment(func(index int, seg *MACAddressSegment) (stop bool) { if stop = index >= len(segs); !stop { segs[index] = seg } return }) } // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (section *MACAddressSection) CopySubSegments(start, end int, segs []*MACAddressSegment) (count int) { start, end, targetStart := adjust1To1StartIndices(start, end, section.GetDivisionCount(), len(segs)) segs = segs[targetStart:] return section.forEachSubDivision(start, end, func(index int, div *AddressDivision) { segs[index] = div.ToMAC() }, len(segs)) } // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as this section. func (section *MACAddressSection) GetSegments() (res []*MACAddressSegment) { res = make([]*MACAddressSegment, section.GetSegmentCount()) section.CopySegments(res) return } // GetLower returns the section in the range with the lowest numeric value, // which will be the same section if it represents a single value. // For example, for "1:1:1:2-3:4:5-6", the series "1:1:1:2:4:5" is returned. func (section *MACAddressSection) GetLower() *MACAddressSection { return section.getLower().ToMAC() } // GetUpper returns the section in the range with the highest numeric value, // which will be the same section if it represents a single value. // For example, for "1:1:1:2-3:4:5-6", the series "1:1:1:3:4:6" is returned. func (section *MACAddressSection) GetUpper() *MACAddressSection { return section.getUpper().ToMAC() } // Uint64Value returns the lowest individual address section in the address section collection as a uint64. func (section *MACAddressSection) Uint64Value() uint64 { return section.getLongValue(true) } // UpperUint64Value returns the highest individual address section in the address section collection as a uint64. func (section *MACAddressSection) UpperUint64Value() uint64 { return section.getLongValue(false) } func (section *MACAddressSection) getLongValue(lower bool) (result uint64) { segCount := section.GetSegmentCount() if segCount == 0 { return } seg := section.GetSegment(0) if lower { result = uint64(seg.GetSegmentValue()) } else { result = uint64(seg.GetUpperSegmentValue()) } bitsPerSegment := section.GetBitsPerSegment() for i := 1; i < segCount; i++ { result = result << uint(bitsPerSegment) seg = section.GetSegment(i) if lower { result |= uint64(seg.GetSegmentValue()) } else { result |= uint64(seg.GetUpperSegmentValue()) } } return } // ToPrefixBlock returns the section with the same prefix as this section while the remaining bits span all values. // The returned section will be the block of all sections with the same prefix. // // If this section has no prefix, this section is returned. func (section *MACAddressSection) ToPrefixBlock() *MACAddressSection { return section.toPrefixBlock().ToMAC() } // ToPrefixBlockLen returns the section with the same prefix of the given length as this section while the remaining bits span all values. // The returned section will be the block of all sections with the same prefix. func (section *MACAddressSection) ToPrefixBlockLen(prefLen BitCount) *MACAddressSection { return section.toPrefixBlockLen(prefLen).ToMAC() } // ToBlock creates a new block of address sections by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (section *MACAddressSection) ToBlock(segmentIndex int, lower, upper SegInt) *MACAddressSection { return section.toBlock(segmentIndex, lower, upper).ToMAC() } // Iterator provides an iterator to iterate through the individual address sections of this address section. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual address sections. // // Call IsMultiple to determine if this instance represents multiple address sections, or GetCount for the count. func (section *MACAddressSection) Iterator() Iterator[*MACAddressSection] { if section == nil { return macSectionIterator{nilSectIterator()} } return macSectionIterator{section.sectionIterator(nil)} } // PrefixIterator provides an iterator to iterate through the individual prefixes of this address section, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this address section. // // If the series has no prefix length, then this is equivalent to Iterator. func (section *MACAddressSection) PrefixIterator() Iterator[*MACAddressSection] { return macSectionIterator{section.prefixIterator(false)} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address section. // Each iterated address section will be a prefix block with the same prefix length as this address section. // // If this address section has no prefix length, then this is equivalent to Iterator. func (section *MACAddressSection) PrefixBlockIterator() Iterator[*MACAddressSection] { return macSectionIterator{section.prefixIterator(true)} } // IncrementBoundary returns the item that is the given increment from the range boundaries of this item. // // If the given increment is positive, adds the value to the highest (GetUpper) in the range to produce a new item. // If the given increment is negative, adds the value to the lowest (GetLower) in the range to produce a new item. // If the increment is zero, returns this. // // If this represents just a single value, this item is simply incremented by the given increment value, positive or negative. // // On overflow or underflow, IncrementBoundary returns nil. func (section *MACAddressSection) IncrementBoundary(increment int64) *MACAddressSection { return section.incrementBoundary(increment).ToMAC() } // IsAdaptiveZero returns true if the division grouping was originally created as an implicitly zero-valued section or grouping (e.g. IPv4AddressSection{}), // meaning it was not constructed using a constructor function. // Such a grouping, which has no divisions or segments, is convertible to an implicitly zero-valued grouping of any type or version, whether IPv6, IPv4, MAC, or other. // In other words, when a section or grouping is the zero-value, then it is equivalent and convertible to the zero value of any other section or grouping type. func (section *MACAddressSection) IsAdaptiveZero() bool { return section != nil && section.matchesZeroGrouping() } func getMacMaxValueLong(segmentCount int) uint64 { return macMaxValues[segmentCount] } var macMaxValues = []uint64{ 0, MACMaxValuePerSegment, 0xffff, 0xffffff, 0xffffffff, 0xffffffffff, 0xffffffffffff, 0xffffffffffffff, 0xffffffffffffffff} // Increment returns the item that is the given increment upwards into the range, // with the increment of 0 returning the first in the range. // // If the increment i matches or exceeds the range count c, then i - c + 1 // is added to the upper item of the range. // An increment matching the count gives you the item just above the highest in the range. // // If the increment is negative, it is added to the lowest of the range. // To get the item just below the lowest of the range, use the increment -1. // // If this represents just a single value, the item is simply incremented by the given increment, positive or negative. // // If this item represents multiple values, a positive increment i is equivalent i + 1 values from the iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the count is equivalent to the same number of iterator values preceding the last value of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On overflow or underflow, Increment returns nil. func (section *MACAddressSection) Increment(incrementVal int64) *MACAddressSection { if incrementVal == 0 && !section.isMultiple() { return section } segCount := section.GetSegmentCount() lowerValue := section.Uint64Value() upperValue := section.UpperUint64Value() count := section.GetCount() countMinus1 := count.Sub(count, bigOneConst()).Uint64() isOverflow := checkOverflow(incrementVal, lowerValue, upperValue, countMinus1, getMacMaxValueLong(segCount)) if isOverflow { return nil } return increment( section.ToSectionBase(), incrementVal, macNetwork.getAddressCreator(), countMinus1, section.Uint64Value(), section.UpperUint64Value(), section.addressSectionInternal.getLower, section.addressSectionInternal.getUpper, section.getPrefixLen()).ToMAC() } // ReverseBits returns a new section with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (section *MACAddressSection) ReverseBits(perByte bool) (*MACAddressSection, addrerr.IncompatibleAddressError) { res, err := section.reverseBits(perByte) return res.ToMAC(), err } // ReverseBytes returns a new section with the bytes reversed. Any prefix length is dropped. func (section *MACAddressSection) ReverseBytes() *MACAddressSection { return section.ReverseSegments() } // ReverseSegments returns a new section with the segments reversed. func (section *MACAddressSection) ReverseSegments() *MACAddressSection { if section.GetSegmentCount() <= 1 { if section.IsPrefixed() { return section.WithoutPrefixLen() } return section } res, _ := section.reverseSegments( func(i int) (*AddressSegment, addrerr.IncompatibleAddressError) { return section.GetSegment(i).ToSegmentBase(), nil }, ) return res.ToMAC() } // Append creates a new section by appending the given section to this section. func (section *MACAddressSection) Append(other *MACAddressSection) *MACAddressSection { count := section.GetSegmentCount() return section.ReplaceLen(count, count, other, 0, other.GetSegmentCount()) } // Insert creates a new section by inserting the given section into this section at the given index. func (section *MACAddressSection) Insert(index int, other *MACAddressSection) *MACAddressSection { return section.ReplaceLen(index, index, other, 0, other.GetSegmentCount()) } // Replace replaces the segments of this section starting at the given index with the given replacement segments. func (section *MACAddressSection) Replace(index int, replacement *MACAddressSection) *MACAddressSection { return section.ReplaceLen(index, index+replacement.GetSegmentCount(), replacement, 0, replacement.GetSegmentCount()) } // ReplaceLen replaces segments starting from startIndex and ending before endIndex with the segments starting at replacementStartIndex and // ending before replacementEndIndex from the replacement section. func (section *MACAddressSection) ReplaceLen(startIndex, endIndex int, replacement *MACAddressSection, replacementStartIndex, replacementEndIndex int) *MACAddressSection { return section.replaceLen(startIndex, endIndex, replacement.ToSectionBase(), replacementStartIndex, replacementEndIndex, macBitsToSegmentBitshift).ToMAC() } var ( canonicalWildcards = new(addrstr.WildcardsBuilder).SetRangeSeparator(MacDashedSegmentRangeSeparatorStr).SetWildcard(SegmentWildcardStr).ToWildcards() macNormalizedParams = new(addrstr.MACStringOptionsBuilder).SetExpandedSegments(true).ToOptions() macCanonicalParams = new(addrstr.MACStringOptionsBuilder).SetSeparator(MACDashSegmentSeparator).SetExpandedSegments(true).SetWildcards(canonicalWildcards).ToOptions() macCompressedParams = new(addrstr.MACStringOptionsBuilder).ToOptions() dottedParams = new(addrstr.MACStringOptionsBuilder).SetSeparator(MacDottedSegmentSeparator).SetExpandedSegments(true).ToOptions() spaceDelimitedParams = new(addrstr.MACStringOptionsBuilder).SetSeparator(MacSpaceSegmentSeparator).SetExpandedSegments(true).ToOptions() ) // ToHexString writes this address section as a single hexadecimal value (possibly two values if a range), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // If a multiple-valued section cannot be written as a range of two values, an error is returned. func (section *MACAddressSection) ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toHexString(with0xPrefix) } // ToOctalString writes this address section as a single octal value (possibly two values if a range), // the number of digits according to the bit count, with or without a preceding "0" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *MACAddressSection) ToOctalString(with0Prefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toOctalString(with0Prefix) } // ToBinaryString writes this address section as a single binary value (possibly two values if a range), // the number of digits according to the bit count, with or without a preceding "0b" prefix. // // If a multiple-valued section cannot be written as a range of two values, an error is returned. func (section *MACAddressSection) ToBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toBinaryString(with0bPrefix) } // ToCanonicalString produces a canonical string for the address section. // // For MAC, it uses the canonical standardized IEEE 802 MAC address representation of xx-xx-xx-xx-xx-xx. An example is "01-23-45-67-89-ab". // For range segments, '|' is used: "11-22-33|44-55-66". func (section *MACAddressSection) ToCanonicalString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toCustomString(macCanonicalParams) } return cacheStr(&cache.canonicalString, func() string { return section.toCustomString(macCanonicalParams) }) } // ToNormalizedString produces a normalized string for the address section. // // For MAC, it differs from the canonical string. It uses the most common representation of MAC addresses: "xx:xx:xx:xx:xx:xx". An example is "01:23:45:67:89:ab". // For range segments, '-' is used: "11:22:33-44:55:66". func (section *MACAddressSection) ToNormalizedString() string { if section == nil { return nilString() } cch := section.getStringCache() if cch == nil { return section.toCustomString(macNormalizedParams) } strp := &cch.normalizedMACString return cacheStr(strp, func() string { return section.toCustomString(macNormalizedParams) }) } // ToNormalizedWildcardString produces the normalized string. func (section *MACAddressSection) ToNormalizedWildcardString() string { return section.ToNormalizedString() } // ToCompressedString produces a short representation of this address section while remaining within the confines of standard representation(s) of the address. // // For MAC, it differs from the canonical string. It produces a shorter string for the address that has no leading zeros. func (section *MACAddressSection) ToCompressedString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toCustomString(macCompressedParams) } return cacheStr(&cache.compressedMACString, func() string { return section.toCustomString(macCompressedParams) }) } // ToDottedString produces the dotted hexadecimal format "aaaa.bbbb.cccc". func (section *MACAddressSection) ToDottedString() (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } dottedGrouping, err := section.GetDottedGrouping() if err != nil { return "", err } cache := section.getStringCache() if cache == nil { return toNormalizedString(dottedParams, dottedGrouping), nil } return cacheStrErr(&cache.dottedString, func() (string, addrerr.IncompatibleAddressError) { return toNormalizedString(dottedParams, dottedGrouping), nil }) } // GetDottedGrouping returns an AddressDivisionGrouping which organizes the address section into segments of bit-length 16, rather than the more typical 8 bits per segment. // // If this represents a collection of MAC addresses, this returns an error when unable to join two address segments, // the first with a range of values, into a division of the larger bit-length that represents the same set of values. func (section *MACAddressSection) GetDottedGrouping() (*AddressDivisionGrouping, addrerr.IncompatibleAddressError) { segmentCount := section.GetSegmentCount() var newSegs []*AddressDivision newSegmentBitCount := section.GetBitsPerSegment() << 1 var segIndex, newSegIndex int newSegmentCount := (segmentCount + 1) >> 1 newSegs = make([]*AddressDivision, newSegmentCount) bitsPerSeg := section.GetBitsPerSegment() for segIndex+1 < segmentCount { segment1 := section.GetSegment(segIndex) segIndex++ segment2 := section.GetSegment(segIndex) segIndex++ if segment1.isMultiple() && !segment2.IsFullRange() { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.invalid.joined.ranges"}} } val := (segment1.GetSegmentValue() << uint(bitsPerSeg)) | segment2.GetSegmentValue() upperVal := (segment1.GetUpperSegmentValue() << uint(bitsPerSeg)) | segment2.GetUpperSegmentValue() vals := newRangeDivision(DivInt(val), DivInt(upperVal), newSegmentBitCount) newSegs[newSegIndex] = createAddressDivision(vals) newSegIndex++ } if segIndex < segmentCount { segment := section.GetSegment(segIndex) val := segment.GetSegmentValue() << uint(bitsPerSeg) upperVal := segment.GetUpperSegmentValue() << uint(bitsPerSeg) vals := newRangeDivision(DivInt(val), DivInt(upperVal), newSegmentBitCount) newSegs[newSegIndex] = createAddressDivision(vals) } grouping := createInitializedGrouping(newSegs, section.getPrefixLen()) return grouping, nil } // ToSpaceDelimitedString produces a string delimited by spaces: "aa bb cc dd ee ff". func (section *MACAddressSection) ToSpaceDelimitedString() string { if section == nil { return nilString() } cache := section.getStringCache() if cache == nil { return section.toCustomString(spaceDelimitedParams) } return cacheStr(&cache.spaceDelimitedString, func() string { return section.toCustomString(spaceDelimitedParams) }) } // ToDashedString produces a string delimited by dashes: "aa-bb-cc-dd-ee-ff". // For range segments, '|' is used: "11-22-33|44-55-66". // It returns the same string as ToCanonicalString. func (section *MACAddressSection) ToDashedString() string { if section == nil { return nilString() } return section.ToCanonicalString() } // ToColonDelimitedString produces a string delimited by colons: "aa:bb:cc:dd:ee:ff". // For range segments, '-' is used: "11:22:33-44:55:66". // It returns the same string as ToNormalizedString. func (section *MACAddressSection) ToColonDelimitedString() string { if section == nil { return nilString() } return section.ToNormalizedString() } // String implements the [fmt.Stringer] interface, returning the normalized string provided by ToNormalizedString, or "" if the receiver is a nil pointer. func (section *MACAddressSection) String() string { if section == nil { return nilString() } return section.toString() } // GetSegmentStrings returns a slice with the string for each segment being the string that is normalized with wildcards. func (section *MACAddressSection) GetSegmentStrings() []string { if section == nil { return nil } return section.getSegmentStrings() } ipaddress-go-1.5.4/ipaddr/macsegment.go000066400000000000000000000476251440250641600200370ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math/big" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" ) type MACSegInt = uint8 type MACSegmentValueProvider func(segmentIndex int) MACSegInt // WrapMACSegmentValueProvider converts the given MACSegmentValueProvider to a SegmentValueProvider func WrapMACSegmentValueProvider(f MACSegmentValueProvider) SegmentValueProvider { if f == nil { return nil } return func(segmentIndex int) SegInt { return SegInt(f(segmentIndex)) } } // WrapSegmentValueProviderForMAC converts the given SegmentValueProvider to a MACSegmentValueProvider // Values that do not fit MACSegInt are truncated. func WrapSegmentValueProviderForMAC(f SegmentValueProvider) MACSegmentValueProvider { if f == nil { return nil } return func(segmentIndex int) MACSegInt { return MACSegInt(f(segmentIndex)) } } const useMACSegmentCache = true type macSegmentValues struct { value MACSegInt upperValue MACSegInt cache divCache } func (seg *macSegmentValues) getAddrType() addrType { return macType } func (seg *macSegmentValues) includesZero() bool { return seg.value == 0 } func (seg *macSegmentValues) includesMax() bool { return seg.upperValue == 0xff } func (seg *macSegmentValues) isMultiple() bool { return seg.value != seg.upperValue } func (seg *macSegmentValues) getCount() *big.Int { return big.NewInt(int64(seg.upperValue-seg.value) + 1) } func (seg *macSegmentValues) getBitCount() BitCount { return MACBitsPerSegment } func (seg *macSegmentValues) getByteCount() int { return MACBytesPerSegment } func (seg *macSegmentValues) getValue() *BigDivInt { return big.NewInt(int64(seg.value)) } func (seg *macSegmentValues) getUpperValue() *BigDivInt { return big.NewInt(int64(seg.upperValue)) } func (seg *macSegmentValues) getDivisionValue() DivInt { return DivInt(seg.value) } func (seg *macSegmentValues) getUpperDivisionValue() DivInt { return DivInt(seg.upperValue) } func (seg *macSegmentValues) getDivisionPrefixLength() PrefixLen { return nil } func (seg *macSegmentValues) getSegmentValue() SegInt { return SegInt(seg.value) } func (seg *macSegmentValues) getUpperSegmentValue() SegInt { return SegInt(seg.upperValue) } func (seg *macSegmentValues) calcBytesInternal() (bytes, upperBytes []byte) { bytes = []byte{byte(seg.value)} if seg.isMultiple() { upperBytes = []byte{byte(seg.upperValue)} } else { upperBytes = bytes } return } func (seg *macSegmentValues) bytesInternal(upper bool) []byte { if upper { return []byte{byte(seg.upperValue)} } return []byte{byte(seg.value)} } func (seg *macSegmentValues) deriveNew(val, upperVal DivInt, _ PrefixLen) divisionValues { return newMACSegmentValues(MACSegInt(val), MACSegInt(upperVal)) } func (seg *macSegmentValues) derivePrefixed(_ PrefixLen) divisionValues { return seg } func (seg *macSegmentValues) deriveNewSeg(val SegInt, _ PrefixLen) divisionValues { return newMACSegmentVal(MACSegInt(val)) } func (seg *macSegmentValues) deriveNewMultiSeg(val, upperVal SegInt, _ PrefixLen) divisionValues { return newMACSegmentValues(MACSegInt(val), MACSegInt(upperVal)) } func (seg *macSegmentValues) getCache() *divCache { return &seg.cache } var _ divisionValues = &macSegmentValues{} var zeroMACSeg = NewMACSegment(0) var allRangeMACSeg = NewMACRangeSegment(0, MACMaxValuePerSegment) // MACAddressSegment represents a segment of a MAC address. For MAC, segments are 1 byte. // A MAC segment contains a single value or a range of sequential values, a prefix length, and it has bit length of 8 bits. // // Segments are immutable, which also makes them concurrency-safe. type MACAddressSegment struct { addressSegmentInternal } // GetMACSegmentValue returns the lower value. Same as GetSegmentValue but returned as a MACSegInt. func (seg *MACAddressSegment) GetMACSegmentValue() MACSegInt { return MACSegInt(seg.GetSegmentValue()) } // GetMACUpperSegmentValue returns the lower value. Same as GetUpperSegmentValue but returned as a MACSegInt. func (seg *MACAddressSegment) GetMACUpperSegmentValue() MACSegInt { return MACSegInt(seg.GetUpperSegmentValue()) } func (seg *MACAddressSegment) init() *MACAddressSegment { if seg.divisionValues == nil { return zeroMACSeg } return seg } // Contains returns whether this is same type and version as the given segment and whether it contains all values in the given segment. func (seg *MACAddressSegment) Contains(other AddressSegmentType) bool { if seg == nil { return other == nil || other.ToSegmentBase() == nil } return seg.init().contains(other) } // Equal returns whether the given segment is equal to this segment. // Two segments are equal if they match: // - type/version: MAC // - value range // Prefix lengths are ignored. func (seg *MACAddressSegment) Equal(other AddressSegmentType) bool { if seg == nil { return other == nil || other.ToDiv() == nil //return seg.getAddrType() == macType && other.(StandardDivisionType).ToDiv() == nil } return seg.init().equal(other) } // PrefixContains returns whether the prefix values in the prefix of the given segment are also prefix values in this segment. // It returns whether the prefix of this segment contains the prefix of the given segment. func (seg *MACAddressSegment) PrefixContains(other AddressSegmentType, prefixLength BitCount) bool { return seg.init().addressSegmentInternal.PrefixContains(other, prefixLength) } // PrefixEqual returns whether the prefix bits of this segment match the same bits of the given segment. // It returns whether the two segments share the same range of prefix values using the given prefix length. func (seg *MACAddressSegment) PrefixEqual(other AddressSegmentType, prefixLength BitCount) bool { return seg.init().addressSegmentInternal.PrefixEqual(other, prefixLength) } // Compare returns a negative integer, zero, or a positive integer if this address segment is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (seg *MACAddressSegment) Compare(item AddressItem) int { return CountComparator.Compare(seg, item) } // CompareSize compares the counts of two items, the number of individual values within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether one represents more individual values than another. // // CompareSize returns a positive integer if this segment has a larger count than the item given, zero if they are the same, or a negative integer if the other has a larger count. func (seg *MACAddressSegment) CompareSize(other AddressItem) int { if seg == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return seg.init().compareSize(other) } // GetBitCount returns the number of bits in each value comprising this address item, which is 8. func (seg *MACAddressSegment) GetBitCount() BitCount { return IPv4BitsPerSegment } // GetByteCount returns the number of bytes required for each value comprising this address item, which is 1. func (seg *MACAddressSegment) GetByteCount() int { return IPv4BytesPerSegment } // GetMaxValue gets the maximum possible value for this type or version of segment, determined by the number of bits. // // For the highest range value of this particular segment, use GetUpperSegmentValue. func (seg *MACAddressSegment) GetMaxValue() MACSegInt { return 0xff } // GetLower returns a segment representing just the lowest value in the range, which will be the same segment if it represents a single value. func (seg *MACAddressSegment) GetLower() *MACAddressSegment { return seg.init().getLower().ToMAC() } // GetUpper returns a segment representing just the highest value in the range, which will be the same segment if it represents a single value. func (seg *MACAddressSegment) GetUpper() *MACAddressSegment { return seg.init().getUpper().ToMAC() } // IsMultiple returns whether this segment represents multiple values. func (seg *MACAddressSegment) IsMultiple() bool { return seg != nil && seg.isMultiple() } // GetCount returns the count of possible distinct values for this item. // If not representing multiple values, the count is 1. // // For instance, a segment with the value range of 3-7 has count 5. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (seg *MACAddressSegment) GetCount() *big.Int { if seg == nil { return bigZero() } return seg.getCount() } // Bytes returns the lowest value in the address segment range as a byte slice. func (seg *MACAddressSegment) Bytes() []byte { return seg.init().addressSegmentInternal.Bytes() } // UpperBytes returns the highest value in the address segment range as a byte slice. func (seg *MACAddressSegment) UpperBytes() []byte { return seg.init().addressSegmentInternal.UpperBytes() } // CopyBytes copies the lowest value in the address segment range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (seg *MACAddressSegment) CopyBytes(bytes []byte) []byte { return seg.init().addressSegmentInternal.CopyBytes(bytes) } // CopyUpperBytes copies the highest value in the address segment range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (seg *MACAddressSegment) CopyUpperBytes(bytes []byte) []byte { return seg.init().addressSegmentInternal.CopyUpperBytes(bytes) } // GetPrefixCountLen returns the count of the number of distinct prefix values for the given prefix length in the range of values of this segment. func (seg *MACAddressSegment) GetPrefixCountLen(segmentPrefixLength BitCount) *big.Int { return seg.init().addressSegmentInternal.GetPrefixCountLen(segmentPrefixLength) } // GetPrefixValueCountLen returns the same value as GetPrefixCountLen as an integer. func (seg *MACAddressSegment) GetPrefixValueCountLen(segmentPrefixLength BitCount) SegIntCount { return seg.init().addressSegmentInternal.GetPrefixValueCountLen(segmentPrefixLength) } // IsOneBit returns true if the bit in the lower value of this segment at the given index is 1, where index 0 is the most significant bit. func (seg *MACAddressSegment) IsOneBit(segmentBitIndex BitCount) bool { return seg.init().addressSegmentInternal.IsOneBit(segmentBitIndex) } func (seg *MACAddressSegment) setString( addressStr string, isStandardString bool, lowerStringStartIndex, lowerStringEndIndex int, originalLowerValue SegInt) { if cache := seg.getCache(); cache != nil { if isStandardString && originalLowerValue == seg.getSegmentValue() { cacheStr(&cache.cachedString, func() string { return addressStr[lowerStringStartIndex:lowerStringEndIndex] }) } } } func (seg *MACAddressSegment) setRangeString( addressStr string, isStandardRangeString bool, lowerStringStartIndex, upperStringEndIndex int, rangeLower, rangeUpper SegInt) { if cache := seg.getCache(); cache != nil { if seg.IsFullRange() { cacheStrPtr(&cache.cachedString, &segmentWildcardStr) } else if isStandardRangeString && rangeLower == seg.getSegmentValue() && rangeUpper == seg.getUpperSegmentValue() { cacheStr(&cache.cachedString, func() string { return addressStr[lowerStringStartIndex:upperStringEndIndex] }) } } } // Iterator provides an iterator to iterate through the individual address segments of this address segment. // // Call IsMultiple to determine if this instance represents multiple address segments, or GetValueCount for the count. func (seg *MACAddressSegment) Iterator() Iterator[*MACAddressSegment] { if seg == nil { return macSegmentIterator{nilSegIterator()} } return macSegmentIterator{seg.init().iterator()} } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks of the given prefix length, // one for each prefix of that length in the segment. func (seg *MACAddressSegment) PrefixBlockIterator(segmentPrefixLen BitCount) Iterator[*MACAddressSegment] { return macSegmentIterator{seg.init().prefixedBlockIterator(segmentPrefixLen)} } // PrefixIterator provides an iterator to iterate through the individual prefixes of the given prefix length in this segment, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this range. func (seg *MACAddressSegment) PrefixIterator(segmentPrefixLen BitCount) Iterator[*MACAddressSegment] { return macSegmentIterator{seg.init().prefixedIterator(segmentPrefixLen)} } // ReverseBits returns a segment with the bits reversed. // // If this segment represents a range of values that cannot be reversed, then this returns an error. // // To be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // Otherwise the result is not contiguous and thus cannot be represented by a sequential range of values. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (seg *MACAddressSegment) ReverseBits(_ bool) (res *MACAddressSegment, err addrerr.IncompatibleAddressError) { if seg.divisionValues == nil { res = seg return } if seg.isMultiple() { if isReversible := seg.isReversibleRange(false); isReversible { res = seg return } err = &incompatibleAddressError{addressError{key: "ipaddress.error.reverseRange"}} return } oldVal := MACSegInt(seg.GetSegmentValue()) val := MACSegInt(reverseUint8(uint8(oldVal))) if oldVal == val { res = seg } else { res = NewMACSegment(val) } return } // ReverseBytes returns a segment with the bytes reversed, which for a MAC segment is always the original segment. func (seg *MACAddressSegment) ReverseBytes() (*MACAddressSegment, addrerr.IncompatibleAddressError) { return seg, nil } // Join joins with another MAC segment to produce a IPv6 segment. func (seg *MACAddressSegment) Join(macSegment1 *MACAddressSegment, prefixLength PrefixLen) (*IPv6AddressSegment, addrerr.IncompatibleAddressError) { return seg.joinSegs(macSegment1, false, prefixLength) } // JoinAndFlip2ndBit joins with another MAC segment to produce a IPv6 segment with the second bit flipped from 1 to 0. func (seg *MACAddressSegment) JoinAndFlip2ndBit(macSegment1 *MACAddressSegment, prefixLength PrefixLen) (*IPv6AddressSegment, addrerr.IncompatibleAddressError) { return seg.joinSegs(macSegment1, true, prefixLength) } func (seg *MACAddressSegment) joinSegs(macSegment1 *MACAddressSegment, flip bool, prefixLength PrefixLen) (*IPv6AddressSegment, addrerr.IncompatibleAddressError) { if seg.isMultiple() { // if the high segment has a range, the low segment must match the full range, // otherwise it is not possible to create an equivalent range when joining if !macSegment1.IsFullRange() { return nil, &incompatibleAddressError{addressError{key: "ipaddress.error.invalidMACIPv6Range"}} } } lower0 := seg.GetSegmentValue() upper0 := seg.GetUpperSegmentValue() if flip { mask2ndBit := SegInt(0x2) if !seg.MatchesWithMask(mask2ndBit&lower0, mask2ndBit) { // ensures that bit remains constant return nil, &incompatibleAddressError{addressError{key: "ipaddress.mac.error.not.eui.convertible"}} } lower0 ^= mask2ndBit //flip the universal/local bit upper0 ^= mask2ndBit } return NewIPv6RangePrefixedSegment( IPv6SegInt((lower0<<8)|macSegment1.getSegmentValue()), IPv6SegInt((upper0<<8)|macSegment1.getUpperSegmentValue()), prefixLength), nil } // ToDiv converts to an AddressDivision, a polymorphic type usable with all address segments and divisions. // Afterwards, you can convert back with ToMAC. // // ToDiv can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *MACAddressSegment) ToDiv() *AddressDivision { return seg.ToSegmentBase().ToDiv() } // ToSegmentBase converts to an AddressSegment, a polymorphic type usable with all address segments. // Afterwards, you can convert back with ToMAC. // // ToSegmentBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *MACAddressSegment) ToSegmentBase() *AddressSegment { if seg == nil { return nil } return (*AddressSegment)(seg.init()) } // GetString produces a normalized string to represent the segment. // // For MAC segments, the string is the same as that produced by GetWildcardString. func (seg *MACAddressSegment) GetString() string { if seg == nil { return nilString() } return seg.init().getString() } // GetWildcardString produces a normalized string to represent the segment, favouring wildcards and range characters. // The explicit range of a range-valued segment will be printed. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and the bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (seg *MACAddressSegment) GetWildcardString() string { if seg == nil { return nilString() } return seg.init().getWildcardString() } // String produces a string that is useful when a segment is provided with no context. It uses the hexadecimal radix with the string prefix for hex ("0x"). // GetWildcardString and GetString are more appropriate in context with other segments or divisions. They do not use a string prefix and use '*' for full-range segments. func (seg *MACAddressSegment) String() string { if seg == nil { return nilString() } return seg.init().toString() } // NewMACSegment constructs a segment of a MAC address with the given value. func NewMACSegment(val MACSegInt) *MACAddressSegment { return newMACSegment(newMACSegmentVal(val)) } // NewMACRangeSegment constructs a segment of a MAC address collection with the given range of sequential values. func NewMACRangeSegment(val, upperVal MACSegInt) *MACAddressSegment { return newMACSegment(newMACSegmentValues(val, upperVal)) } func newMACSegment(vals *macSegmentValues) *MACAddressSegment { return &MACAddressSegment{ addressSegmentInternal{ addressDivisionInternal{ addressDivisionBase{vals}, }, }, } } var ( allRangeValsMAC = &macSegmentValues{ upperValue: MACMaxValuePerSegment, } segmentCacheMAC = makeSegmentCacheMAC() ) func makeSegmentCacheMAC() (segmentCacheMAC []macSegmentValues) { if useMACSegmentCache { segmentCacheMAC = make([]macSegmentValues, MACMaxValuePerSegment+1) for i := range segmentCacheMAC { vals := &segmentCacheMAC[i] segi := MACSegInt(i) vals.value = segi vals.upperValue = segi } } return } func newMACSegmentVal(value MACSegInt) *macSegmentValues { if useMACSegmentCache { result := &segmentCacheMAC[value] //checkValuesMAC(value, value, result) return result } return &macSegmentValues{value: value, upperValue: value} } func newMACSegmentValues(value, upperValue MACSegInt) *macSegmentValues { if value == upperValue { return newMACSegmentVal(value) } else if value > upperValue { value, upperValue = upperValue, value } if useMACSegmentCache && value == 0 && upperValue == MACMaxValuePerSegment { return allRangeValsMAC } return &macSegmentValues{value: value, upperValue: upperValue} } ipaddress-go-1.5.4/ipaddr/mask.go000066400000000000000000000640351440250641600166410ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math/big" "math/bits" ) var ( defaultMasker = extendedMaskerBase{maskerBase{true}} defaultNonSequentialMasker = extendedMaskerBase{} defaultOrMasker = bitwiseOrerBase{true} defaultNonSequentialOrMasker = bitwiseOrerBase{} ) // Masker is used to mask (apply bitwise conjunction) division and segment values. type Masker interface { // GetMaskedLower provides the lowest masked value, which is not necessarily the lowest value masked. GetMaskedLower(value, maskValue uint64) uint64 // GetMaskedUpper provides the highest masked value, which is not necessarily the highest value masked. GetMaskedUpper(upperValue, maskValue uint64) uint64 // IsSequential returns whether masking all values in the range results in a sequential set of values. IsSequential() bool } type maskerBase struct { isSequentialVal bool } // GetMaskedLower provides the lowest masked value, which is not necessarily the lowest value masked. func (masker maskerBase) GetMaskedLower(value, maskValue uint64) uint64 { return value & maskValue } // GetMaskedUpper provides the highest masked value, which is not necessarily the highest value masked. func (masker maskerBase) GetMaskedUpper(upperValue, maskValue uint64) uint64 { return upperValue & maskValue } // IsSequential returns whether masking all values in the range results in a sequential set of values. func (masker maskerBase) IsSequential() bool { return masker.isSequentialVal } var _ Masker = maskerBase{} func newFullRangeMasker(fullRangeBit int, isSequential bool) Masker { return fullRangeMasker{ fullRangeBit: fullRangeBit, upperMask: ^uint64(0) >> uint(fullRangeBit), maskerBase: maskerBase{isSequential}, } } // These can be cached by the int used to construct type fullRangeMasker struct { maskerBase upperMask uint64 //upperMask = ~0L >>> fullRangeBit; fullRangeBit int } // GetMaskedLower provides the lowest masked value, which is not necessarily the lowest value masked. func (masker fullRangeMasker) GetMaskedLower(value, maskValue uint64) uint64 { return masker.maskerBase.GetMaskedLower(value & ^masker.upperMask, maskValue) } // GetMaskedUpper provides the highest masked value, which is not necessarily the highest value masked. func (masker fullRangeMasker) GetMaskedUpper(upperValue, maskValue uint64) uint64 { return masker.maskerBase.GetMaskedUpper(upperValue|masker.upperMask, maskValue) } func newSpecificValueMasker(lower, upper uint64) Masker { return specificValueMasker{lower: lower, upper: upper} } type specificValueMasker struct { maskerBase lower, upper uint64 } // GetMaskedLower provides the lowest masked value, which is not necessarily the lowest value masked. func (masker specificValueMasker) GetMaskedLower(value, maskValue uint64) uint64 { return masker.maskerBase.GetMaskedLower(value, maskValue) } // GetMaskedUpper provides the highest masked value, which is not necessarily the highest value masked. func (masker specificValueMasker) GetMaskedUpper(upperValue, maskValue uint64) uint64 { return masker.maskerBase.GetMaskedUpper(upperValue, maskValue) } // Extended maskers for handling > 64 bits // // ExtendedMasker handles value masking for divisions with bit counts larger than 64 bits. type ExtendedMasker interface { Masker GetExtendedMaskedLower(extendedValue, extendedMaskValue uint64) uint64 GetExtendedMaskedUpper(extendedUpperValue, extendedMaskValue uint64) uint64 } type extendedMaskerBase struct { maskerBase } // GetExtendedMaskedLower provides the lowest masked value, which is not necessarily the lowest value masked. func (masker extendedMaskerBase) GetExtendedMaskedLower(extendedValue, extendedMaskValue uint64) uint64 { return extendedValue & extendedMaskValue } // GetExtendedMaskedUpper provides the highest masked value, which is not necessarily the highest value masked. func (masker extendedMaskerBase) GetExtendedMaskedUpper(extendedUpperValue, extendedMaskValue uint64) uint64 { return extendedUpperValue & extendedMaskValue } var _ ExtendedMasker = extendedMaskerBase{} var _ Masker = extendedMaskerBase{} func newExtendedFullRangeMasker(fullRangeBit int, isSequential bool) ExtendedMasker { var upperMask, extendedUpperMask uint64 if fullRangeBit >= 64 { upperMask = ^uint64(0) >> (uint(fullRangeBit) - 64) } else { extendedUpperMask = ^uint64(0) >> uint(fullRangeBit) upperMask = 0xffffffffffffffff } return extendedFullRangeMasker{ extendedUpperMask: extendedUpperMask, upperMask: upperMask, extendedMaskerBase: extendedMaskerBase{maskerBase{isSequential}}, } } // These can be cached by the int used to construct type extendedFullRangeMasker struct { extendedMaskerBase upperMask, extendedUpperMask uint64 } // GetMaskedLower provides the lowest masked value, which is not necessarily the lowest value masked. func (masker extendedFullRangeMasker) GetMaskedLower(value, maskValue uint64) uint64 { return masker.extendedMaskerBase.GetMaskedLower(value & ^masker.upperMask, maskValue) } // GetMaskedUpper provides the highest masked value, which is not necessarily the highest value masked. func (masker extendedFullRangeMasker) GetMaskedUpper(upperValue, maskValue uint64) uint64 { return masker.extendedMaskerBase.GetMaskedUpper(upperValue|masker.upperMask, maskValue) } // GetExtendedMaskedLower provides the lowest masked value, which is not necessarily the lowest value masked. func (masker extendedFullRangeMasker) GetExtendedMaskedLower(extendedValue, extendedMaskValue uint64) uint64 { return masker.extendedMaskerBase.GetExtendedMaskedLower(extendedValue & ^masker.extendedUpperMask, extendedMaskValue) } // GetExtendedMaskedUpper provides the highest masked value, which is not necessarily the highest value masked. func (masker extendedFullRangeMasker) GetExtendedMaskedUpper(extendedUpperValue, extendedMaskValue uint64) uint64 { return masker.extendedMaskerBase.GetExtendedMaskedUpper(extendedUpperValue|masker.extendedUpperMask, extendedMaskValue) } func newExtendedSpecificValueMasker(extendedLower, lower, extendedUpper, upper uint64) ExtendedMasker { return extendedSpecificValueMasker{ extendedLower: extendedLower, lower: lower, extendedUpper: extendedUpper, upper: upper, } } // These can be cached by the int used to construct type extendedSpecificValueMasker struct { extendedMaskerBase extendedLower, lower, extendedUpper, upper uint64 } func (masker extendedSpecificValueMasker) GetMaskedLower(_, maskValue uint64) uint64 { return masker.extendedMaskerBase.GetMaskedLower(masker.lower, maskValue) } func (masker extendedSpecificValueMasker) GetMaskedUpper(_, maskValue uint64) uint64 { return masker.extendedMaskerBase.GetMaskedUpper(masker.upper, maskValue) } func (masker extendedSpecificValueMasker) GetExtendedMaskedLower(_, extendedMaskValue uint64) uint64 { return masker.extendedMaskerBase.GetExtendedMaskedLower(masker.extendedLower, extendedMaskValue) } func (masker extendedSpecificValueMasker) GetExtendedMaskedUpper(_, extendedMaskValue uint64) uint64 { return masker.extendedMaskerBase.GetExtendedMaskedUpper(masker.extendedUpper, extendedMaskValue) } func newWrappedMasker(masker Masker) ExtendedMasker { return wrappedMasker{ extendedMaskerBase: extendedMaskerBase{maskerBase{masker.IsSequential()}}, masker: masker, } } type wrappedMasker struct { extendedMaskerBase masker Masker } func (masker wrappedMasker) GetMaskedLower(value, maskValue uint64) uint64 { return masker.masker.GetMaskedLower(value, maskValue) } func (masker wrappedMasker) GetMaskedUpper(upperValue, maskValue uint64) uint64 { return masker.masker.GetMaskedUpper(upperValue, maskValue) } // // // // // BitwiseOrer is used for bitwise disjunction applied to division and segment values. type BitwiseOrer interface { // GetOredLower provides the lowest value after the disjunction, which is not necessarily the lowest value apriori. GetOredLower(value, maskValue uint64) uint64 // GetOredUpper provides the highest value after the disjunction, which is not necessarily the highest value apriori. GetOredUpper(upperValue, maskValue uint64) uint64 // IsSequential returns whether applying bitwise disjunction to all values in the range results in a sequential set of values. IsSequential() bool } type bitwiseOrerBase struct { isSequentialVal bool } func (masker bitwiseOrerBase) GetOredLower(value, maskValue uint64) uint64 { return value | maskValue } func (masker bitwiseOrerBase) GetOredUpper(upperValue, maskValue uint64) uint64 { return upperValue | maskValue } // IsSequential returns whether masking all values in the range results in a sequential set of values. func (masker bitwiseOrerBase) IsSequential() bool { return masker.isSequentialVal } var _ BitwiseOrer = bitwiseOrerBase{} func newFullRangeBitwiseOrer(fullRangeBit int, isSequential bool) BitwiseOrer { return fullRangeBitwiseOrer{ fullRangeBit: fullRangeBit, upperMask: ^uint64(0) >> uint(fullRangeBit), bitwiseOrerBase: bitwiseOrerBase{isSequential}, } } // These can be cached by the int used to construct type fullRangeBitwiseOrer struct { bitwiseOrerBase upperMask uint64 fullRangeBit int } func (masker fullRangeBitwiseOrer) GetOredLower(value, maskValue uint64) uint64 { return masker.bitwiseOrerBase.GetOredLower(value & ^masker.upperMask, maskValue) } func (masker fullRangeBitwiseOrer) GetOredUpper(upperValue, maskValue uint64) uint64 { return masker.bitwiseOrerBase.GetOredUpper(upperValue|masker.upperMask, maskValue) } func newSpecificValueBitwiseOrer(lower, upper uint64) BitwiseOrer { return specificValueBitwiseOrer{lower: lower, upper: upper} } type specificValueBitwiseOrer struct { bitwiseOrerBase lower, upper uint64 } func (masker specificValueBitwiseOrer) GetOredLower(value, maskValue uint64) uint64 { return masker.bitwiseOrerBase.GetOredLower(value, maskValue) } func (masker specificValueBitwiseOrer) GetOredUpper(upperValue, maskValue uint64) uint64 { return masker.bitwiseOrerBase.GetOredUpper(upperValue, maskValue) } // // // // // MaskExtendedRange masks divisions with bit counts larger than 64 bits. Use MaskRange for smaller divisions. func MaskExtendedRange( value, extendedValue, upperValue, extendedUpperValue, maskValue, extendedMaskValue, maxValue, extendedMaxValue uint64) ExtendedMasker { //algorithm: //here we find the highest bit that is part of the range, highestDifferingBitInRange (ie changes from lower to upper) //then we find the highest bit in the mask that is 1 that is the same or below highestDifferingBitInRange (if such a bit exists) // //this gives us the highest bit that is part of the masked range (ie changes from lower to upper after applying the mask) //if this latter bit exists, then any bit below it in the mask must be 1 to include the entire range. extendedDiffering := extendedValue ^ extendedUpperValue if extendedDiffering == 0 { // the top is single-valued so just need to check the lower part masker := MaskRange(value, upperValue, maskValue, maxValue) if masker == defaultMasker { return defaultMasker } return newWrappedMasker(masker) } if (maskValue == maxValue && extendedMaskValue == extendedMaxValue /* all ones mask */) || (maskValue == 0 && extendedMaskValue == 0 /* all zeros mask */) { return defaultMasker } highestDifferingBitInRange := bits.LeadingZeros64(extendedDiffering) extendedDifferingMasked := extendedMaskValue & (^uint64(0) >> uint(highestDifferingBitInRange)) var highestDifferingBitMasked int if extendedDifferingMasked != 0 { differingIsLowestBit := extendedDifferingMasked == 1 highestDifferingBitMasked = bits.LeadingZeros64(extendedDifferingMasked) var maskedIsSequential bool hostMask := ^uint64(0) >> uint(highestDifferingBitMasked+1) if !differingIsLowestBit { // Anything below highestDifferingBitMasked in the mask must be ones. //for the first mask bit that is 1, all bits that follow must also be 1 maskedIsSequential = (extendedMaskValue&hostMask) == hostMask && maskValue == maxValue //check if all ones below } else { maskedIsSequential = maskValue == maxValue } if value == 0 && extendedValue == 0 && upperValue == maxValue && extendedUpperValue == extendedMaxValue { // full range if maskedIsSequential { return defaultMasker } return defaultNonSequentialMasker } if highestDifferingBitMasked > highestDifferingBitInRange { if maskedIsSequential { // We need to check that the range is larger enough that when chopping off the top it remains sequential // Note: a count of 2 in the extended could equate to a count of 2 total! // upper: xxxxxxx1 00000000 // lower: xxxxxxx0 11111111 // Or, it could be everything: // upper: xxxxxxx1 11111111 // lower: xxxxxxx0 00000000 // So for that reason, we need to check the full count here and not just extended countRequiredForSequential := bigOne() countRequiredForSequential.Lsh(countRequiredForSequential, 128-uint(highestDifferingBitMasked)) var upperBig, lowerBig, val big.Int upperBig.SetUint64(extendedUpperValue).Lsh(&upperBig, 64).Or(&upperBig, val.SetUint64(upperValue)) lowerBig.SetUint64(extendedValue).Lsh(&lowerBig, 64).Or(&lowerBig, val.SetUint64(value)) count := upperBig.Sub(&upperBig, &lowerBig).Add(&upperBig, bigOne()) maskedIsSequential = count.CmpAbs(countRequiredForSequential) >= 0 } return newExtendedFullRangeMasker(highestDifferingBitMasked, maskedIsSequential) } else if !maskedIsSequential { var bigHostZeroed, bigHostMask, val big.Int bigHostMask.SetUint64(hostMask).Lsh(&bigHostMask, 64).Or(&bigHostMask, val.SetUint64(^uint64(0))) bigHostZeroed.Not(&bigHostMask) var upperBig, lowerBig big.Int upperBig.SetUint64(extendedUpperValue).Lsh(&upperBig, 64).Or(&upperBig, val.SetUint64(upperValue)) lowerBig.SetUint64(extendedValue).Lsh(&lowerBig, 64).Or(&lowerBig, val.SetUint64(value)) var upperToBeMaskedBig, lowerToBeMaskedBig, maskBig big.Int upperToBeMaskedBig.And(&upperBig, &bigHostZeroed) lowerToBeMaskedBig.Or(&lowerBig, &bigHostMask) maskBig.SetUint64(extendedMaskValue).Lsh(&maskBig, 64).Or(&maskBig, val.SetUint64(maskValue)) for nextBit := 128 - (highestDifferingBitMasked + 1) - 1; nextBit >= 0; nextBit-- { // check if the bit in the mask is 1 if maskBig.Bit(nextBit) != 0 { val.Set(&upperToBeMaskedBig).SetBit(&val, nextBit, 1) if val.CmpAbs(&upperBig) <= 0 { upperToBeMaskedBig.Set(&val) } val.Set(&lowerToBeMaskedBig).SetBit(&val, nextBit, 0) if val.CmpAbs(&lowerBig) >= 0 { lowerToBeMaskedBig.Set(&val) } } //else // keep our upperToBeMasked bit as 0 // keep our lowerToBeMasked bit as 1 } var lowerMaskedBig, upperMaskedBig big.Int lowerMaskedBig.Set(&lowerToBeMaskedBig).And(&lowerToBeMaskedBig, val.SetUint64(^uint64(0))) upperMaskedBig.Set(&upperToBeMaskedBig).And(&upperToBeMaskedBig, &val) return newExtendedSpecificValueMasker( lowerToBeMaskedBig.Rsh(&lowerToBeMaskedBig, 64).Uint64(), lowerMaskedBig.Uint64(), upperToBeMaskedBig.Rsh(&upperToBeMaskedBig, 64).Uint64(), upperMaskedBig.Uint64()) } return defaultMasker } // When masking, the top becomes single-valued. // We go to the lower values to find highestDifferingBitMasked. // At this point, the highest differing bit in the lower range is 0 // and the highestDifferingBitMasked is the first 1 bit in the lower mask if maskValue == 0 { // the mask zeroes out everything, return defaultMasker } maskedIsSequential := true highestDifferingBitMaskedLow := bits.LeadingZeros64(maskValue) if maskValue != maxValue && highestDifferingBitMaskedLow < 63 { //for the first mask bit that is 1, all bits that follow must also be 1 hostMask := ^uint64(0) >> uint(highestDifferingBitMaskedLow+1) // this shift of since case of highestDifferingBitMaskedLow of 64 and 63 taken care of, so the shift is < 64 maskedIsSequential = (maskValue & hostMask) == hostMask //check if all ones below } if maskedIsSequential { //Note: a count of 2 in the lower values could equate to a count of everything in the full range: //upper: xxxxxx10 00000000 //lower: xxxxxxx0 11111111 //Another example: //upper: xxxxxxx1 00000001 //lower: xxxxxxx0 00000000 //So for that reason, we need to check the full count here and not just lower values //We need to check that the range is larger enough that when chopping off the top it remains sequential countRequiredForSequential := bigOne() countRequiredForSequential.Lsh(countRequiredForSequential, 64-uint(highestDifferingBitMaskedLow)) var upperBig, lowerBig, val big.Int upperBig.SetUint64(extendedUpperValue).Lsh(&upperBig, 64).Or(&upperBig, val.SetUint64(upperValue)) lowerBig.SetUint64(extendedValue).Lsh(&lowerBig, 64).Or(&lowerBig, val.SetUint64(value)) count := upperBig.Sub(&upperBig, &lowerBig).Add(&upperBig, bigOne()) maskedIsSequential = count.CmpAbs(countRequiredForSequential) >= 0 } highestDifferingBitMasked = highestDifferingBitMaskedLow + 64 return newExtendedFullRangeMasker(highestDifferingBitMasked, maskedIsSequential) } // MaskRange masks divisions with bit counts 64 bits or smaller. Use MaskExtendedRange for larger divisions. func MaskRange(value, upperValue, maskValue, maxValue uint64) Masker { if value == upperValue { return defaultMasker } if maskValue == 0 || maskValue == maxValue { return defaultMasker } //algorithm: //here we find the highest bit that is part of the range, highestDifferingBitInRange (ie changes from lower to upper) //then we find the highest bit in the mask that is 1 that is the same or below highestDifferingBitInRange (if such a bit exists) //this gives us the highest bit that is part of the masked range (ie changes from lower to upper after applying the mask) //if this latter bit exists, then any bit below it in the mask must be 1 to remain sequential. differing := value ^ upperValue if differing != 1 { highestDifferingBitInRange := bits.LeadingZeros64(differing) maskMask := ^uint64(0) >> uint(highestDifferingBitInRange) differingMasked := maskValue & maskMask foundDiffering := differingMasked != 0 if foundDiffering { // Anything below highestDifferingBitMasked in the mask must be ones. // Also, if we have masked out any 1 bit in the original, then anything that we do not mask out that follows must be all ones highestDifferingBitMasked := bits.LeadingZeros64(differingMasked) // first one bit in the mask covering the range var hostMask uint64 if highestDifferingBitMasked != 63 { hostMask = ^uint64(0) >> uint(highestDifferingBitMasked+1) } //for the first mask bit that is 1, all bits that follow must also be 1 maskedIsSequential := (maskValue & hostMask) == hostMask if maxValue == ^uint64(0) && (!maskedIsSequential || highestDifferingBitMasked > highestDifferingBitInRange) { highestOneBit := bits.LeadingZeros64(upperValue) // note we know highestOneBit < 64, otherwise differing would be 1 or 0 maxValue = ^uint64(0) >> uint(highestOneBit) } if value == 0 && upperValue == maxValue { // full range if maskedIsSequential { return defaultMasker } else { return defaultNonSequentialMasker } } if highestDifferingBitMasked > highestDifferingBitInRange { if maskedIsSequential { // the count will determine if the masked range is sequential if highestDifferingBitMasked < 63 { count := upperValue - value + 1 // if original range is 0xxxx to 1xxxx and our mask starts with a single 0 so the mask is 01111, // then our new range covers 4 bits at most (could be less). // If the range covers 4 bits, we need to know if that range covers the same count of values as 0000 to 1111. // If so, the resulting range is not disjoint. // How do we know the range is disjoint otherwise? We know because it has the values 1111 and 0000. // In order to go from 0xxxx to 1xxxx you must cross the consecutive values 01111 and 10000. // These values are consecutive in the original range (ie 01111 is followed by 10000) but in the new range // they are farthest apart and we need the entire range to fill the gap between them. // That count of values for the entire range is 1111 - 0000 + 1 = 10000 // So in this example, the first bit in the original range is bit 0, highestDifferingBitMasked is 1, // and the range must cover 2 to the power of (5 - 1), // or 2 to the power of bit count - highestDifferingBitMasked, or 1 shifted by that much. countRequiredForSequential := uint64(1) << uint(64-highestDifferingBitMasked) if count < countRequiredForSequential { // the resulting masked values are disjoint, not sequential maskedIsSequential = false } } // else count of 2 is good enough, even if the masked range does not cover both values, then the result is a single value, which is also sequential // another way of looking at it: when the range is just two, we do not need to see if the masked range covers all values in between, as there is no values in between } // The range part of the values will go from 0 to the mask itself. // This is because we know that if the range is 0xxxx... to 1yyyy..., then 01111... and 10000... are also in the range, // since that is the only way to transition from 0xxxx... to 1yyyy... // Because the mask has no 1 bit at the top bit, then we know that when masking with those two values 01111... and 10000... // we get the mask itself and 00000 as the result. return newFullRangeMasker(highestDifferingBitMasked, maskedIsSequential) } else if !maskedIsSequential { hostZeroed := ^hostMask upperToBeMasked := upperValue & hostZeroed lowerToBeMasked := value | hostMask // we find a value in the range that will produce the highest and lowest values when masked for nextBit := uint64(1) << (64 - uint(highestDifferingBitMasked+1) - 1); nextBit != 0; nextBit >>= 1 { // check if the bit in the mask is 1 if (maskValue & nextBit) != 0 { candidate := upperToBeMasked | nextBit if candidate <= upperValue { upperToBeMasked = candidate } candidate = lowerToBeMasked & ^nextBit if candidate >= value { lowerToBeMasked = candidate } } //else // keep our upperToBeMasked bit as 0 // keep our lowerToBeMasked bit as 1 } return newSpecificValueMasker(lowerToBeMasked, upperToBeMasked) } // else fall through to default masker } } return defaultMasker } func bitwiseOrRange(value, upperValue, maskValue, maxValue uint64) BitwiseOrer { if value == upperValue { return defaultOrMasker } // if(value > upperValue) { // throw new IllegalArgumentException("value > upper value"); // } if maskValue == 0 || maskValue == maxValue { return defaultOrMasker } //algorithm: //here we find the highest bit that is part of the range, highestDifferingBitInRange (ie changes from lower to upper) //then we find the highest bit in the mask that is 0 that is the same or below highestDifferingBitInRange (if such a bit exists) //this gives us the highest bit that is part of the masked range (ie changes from lower to upper after applying the mask) //if this latter bit exists, then any bit below it in the mask must be 0 to include the entire range. differing := value ^ upperValue if differing != 1 { highestDifferingBitInRange := bits.LeadingZeros64(differing) maskMask := ^uint64(0) >> uint(highestDifferingBitInRange) differingMasked := maskValue & maskMask foundDiffering := differingMasked != maskMask // mask not all ones if foundDiffering { highestDifferingBitMasked := bits.LeadingZeros64(^differingMasked & maskMask) // first 0 bit in the part of the mask covering the range var hostMask uint64 if highestDifferingBitMasked != 63 { hostMask = ^uint64(0) >> uint(highestDifferingBitMasked+1) } maskedIsSequential := (maskValue & hostMask) == 0 if maxValue == ^uint64(0) && (!maskedIsSequential || highestDifferingBitMasked > highestDifferingBitInRange) { highestOneBit := bits.LeadingZeros64(upperValue) // note we know highestOneBit < 64, otherwise differing would be 1 or 0, so shift is OK maxValue = ^uint64(0) >> uint(highestOneBit) } if value == 0 && upperValue == maxValue { // full range if maskedIsSequential { return defaultOrMasker } else { return defaultNonSequentialOrMasker } } if highestDifferingBitMasked > highestDifferingBitInRange { if maskedIsSequential { // the count will determine if the ored range is sequential if highestDifferingBitMasked < 63 { count := upperValue - value + 1 countRequiredForSequential := uint64(1) << uint(64-highestDifferingBitMasked) if count < countRequiredForSequential { // the resulting ored values are disjoint, not sequential maskedIsSequential = false } } } return newFullRangeBitwiseOrer(highestDifferingBitMasked, maskedIsSequential) } else if !maskedIsSequential { hostZeroed := ^hostMask upperToBeMasked := upperValue & hostZeroed lowerToBeMasked := value | hostMask for nextBit := uint64(1) << uint(64-(highestDifferingBitMasked+1)-1); nextBit != 0; nextBit >>= 1 { // check if the bit in the mask is 0 if (maskValue & nextBit) == 0 { candidate := upperToBeMasked | nextBit if candidate <= upperValue { upperToBeMasked = candidate } candidate = lowerToBeMasked & ^nextBit if candidate >= value { lowerToBeMasked = candidate } } //else // keep our upperToBeMasked bit as 0 // keep our lowerToBeMasked bit as 1 } return newSpecificValueBitwiseOrer(lowerToBeMasked, upperToBeMasked) } } } return defaultOrMasker } ipaddress-go-1.5.4/ipaddr/merge.go000066400000000000000000000251571440250641600170070ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import "sort" func getMergedPrefixBlocks(sections []ExtendedIPSegmentSeries) []ExtendedIPSegmentSeries { // TODO LATER change to generics , this also allows us to possibly avoid the slice copy with the return slice and maybe the passed in one too singleElement, list := organizeSequentially(sections) if singleElement { return list } first := sections[0] bitCount := first.GetBitCount() bitsPerSegment := first.GetBitsPerSegment() bytesPerSegment := first.GetBytesPerSegment() // Now we see if we can match blocks or join them into larger blocks removedCount := 0 listLen := len(list) j := listLen - 1 i := j - 1 top: for j > 0 { item := list[i] otherItem := list[j] compare := ReverseHighValueComparator.CompareSeries(item, otherItem) // check for strict containment, case 1: // w z // x y if compare > 0 { removedCount++ k := j + 1 for k < listLen && list[k] == nil { k++ } if k < listLen { list[j] = list[k] list[k] = nil } else { list[j] = nil j = i i-- } continue } // non-strict containment, case 2: // w z // w z // // reverse containment, case 3: // w y // w z rcompare := ReverseLowValueComparator.CompareSeries(item, otherItem) if rcompare >= 0 { removedCount++ list[i] = otherItem list[j] = nil j = i i-- continue } // check for merge, case 4: // w x // y z // where x and y adjacent, becoming: // w z // prefixLen := item.GetPrefixLen() otherPrefixLen := otherItem.GetPrefixLen() if !prefixLen.Equal(otherPrefixLen) { j = i i-- continue } var matchBitIndex BitCount if prefixLen == nil { matchBitIndex = bitCount - 1 } else { matchBitIndex = prefixLen.bitCount() - 1 } var lastMatchSegmentIndex, lastBitSegmentIndex int if matchBitIndex != 0 { lastMatchSegmentIndex = getNetworkSegmentIndex(matchBitIndex, bytesPerSegment, bitsPerSegment) lastBitSegmentIndex = getHostSegmentIndex(matchBitIndex, bytesPerSegment, bitsPerSegment) } itemSegment := item.GetGenericSegment(lastMatchSegmentIndex) otherItemSegment := otherItem.GetGenericSegment(lastMatchSegmentIndex) itemSegmentValue := itemSegment.GetSegmentValue() otherItemSegmentValue := otherItemSegment.GetSegmentValue() segmentLastBitIndex := bitsPerSegment - 1 if lastBitSegmentIndex == lastMatchSegmentIndex { segmentBitToCheck := matchBitIndex % bitsPerSegment shift := segmentLastBitIndex - segmentBitToCheck itemSegmentValue >>= uint(shift) otherItemSegmentValue >>= uint(shift) } else { itemBitValue := item.GetGenericSegment(lastBitSegmentIndex).GetSegmentValue() otherItemBitalue := otherItem.GetGenericSegment(lastBitSegmentIndex).GetSegmentValue() //we will make space for the last bit so we can do a single comparison itemSegmentValue = (itemSegmentValue << 1) | (itemBitValue >> uint(segmentLastBitIndex)) otherItemSegmentValue = (otherItemSegmentValue << 1) | (otherItemBitalue >> uint(segmentLastBitIndex)) } if itemSegmentValue != otherItemSegmentValue { itemSegmentValue ^= 1 //the ^ 1 flips the first bit if itemSegmentValue != otherItemSegmentValue { //neither an exact match nor a match when flipping the bit, so move on j = i i-- continue } //else we will merge these two into a single prefix block, presuming the initial segments match } //check initial segments for k := lastMatchSegmentIndex - 1; k >= 0; k-- { itemSegment = item.GetGenericSegment(k) otherItemSegment = otherItem.GetGenericSegment(k) val := itemSegment.GetSegmentValue() otherVal := otherItemSegment.GetSegmentValue() if val != otherVal { j = i i-- continue top } } joinedItem := otherItem.ToPrefixBlockLen(matchBitIndex) list[i] = joinedItem removedCount++ k := j + 1 for k < listLen && list[k] == nil { k++ } if k < listLen { list[j] = list[k] list[k] = nil } else { list[j] = nil j = i i-- } } if removedCount > 0 { newSize := listLen - removedCount for k, l := 0, 0; k < newSize; k, l = k+1, l+1 { for list[l] == nil { l++ } if k != l { list[k] = list[l] } } list = list[:newSize] } return list } func getMergedSequentialBlocks(sections []ExtendedIPSegmentSeries) []ExtendedIPSegmentSeries { // TODO change to generics , this also allows us to possibly avoid the slice copy with the return slice and maybe the passed in one too singleElement, list := organizeSequentialMerge(sections) if singleElement { list[0] = list[0].WithoutPrefixLen() return list } removedCount := 0 j := len(list) - 1 i := j - 1 ithRangeSegmentIndex, jthRangeSegmentIndex := -1, -1 top: for j > 0 { item := list[i] otherItem := list[j] compare := ReverseHighValueComparator.Compare(item, otherItem) // check for strict containment, case 1: // w z // x y if compare > 0 { removedCount++ k := j + 1 for k < len(list) && list[k] == nil { k++ } if k < len(list) { list[j] = list[k] list[k] = nil jthRangeSegmentIndex = -1 } else { list[j] = nil j = i i-- jthRangeSegmentIndex = ithRangeSegmentIndex ithRangeSegmentIndex = -1 } continue } // non-strict containment, case 2: // w z // w z // // reverse containment, case 3: // w y // w z rcompare := ReverseLowValueComparator.Compare(item, otherItem) if rcompare >= 0 { removedCount++ list[i] = otherItem list[j] = nil j = i i-- jthRangeSegmentIndex = ithRangeSegmentIndex ithRangeSegmentIndex = -1 continue } //check for overlap if ithRangeSegmentIndex < 0 { ithRangeSegmentIndex = item.GetSequentialBlockIndex() } if jthRangeSegmentIndex < 0 { jthRangeSegmentIndex = otherItem.GetSequentialBlockIndex() } // check for overlap in the non-full range segment, // which must be the same segment in both, otherwise it cannot be overlap, // it can only be containment. // The one with the earlier range segment can only contain the other, there cannot be overlap. // eg 1.a-b.*.* and 1.2.3.* overlap in range segment 2 must have a <= 2 <= b and that means 1.a-b.*.* contains 1.2.3.* if ithRangeSegmentIndex != jthRangeSegmentIndex { j = i i-- jthRangeSegmentIndex = ithRangeSegmentIndex ithRangeSegmentIndex = -1 continue } rangeSegment := item.GetGenericSegment(ithRangeSegmentIndex) otherRangeSegment := otherItem.GetGenericSegment(ithRangeSegmentIndex) otherRangeItemValue := otherRangeSegment.GetSegmentValue() rangeItemUpperValue := rangeSegment.GetUpperSegmentValue() //check for overlapping range in the range segment if rangeItemUpperValue < otherRangeItemValue && rangeItemUpperValue+1 != otherRangeItemValue { j = i i-- ithRangeSegmentIndex = -1 continue } // now check all previous segments match for k := ithRangeSegmentIndex - 1; k >= 0; k-- { itemSegment := item.GetGenericSegment(k) otherItemSegment := otherItem.GetGenericSegment(k) val := itemSegment.GetSegmentValue() otherVal := otherItemSegment.GetSegmentValue() if val != otherVal { j = i i-- ithRangeSegmentIndex = -1 continue top } } upper := rangeItemUpperValue otherUpper := otherRangeSegment.GetUpperSegmentValue() if otherUpper > upper { upper = otherUpper } joinedItem := item.ToBlock(ithRangeSegmentIndex, rangeSegment.GetSegmentValue(), upper) list[i] = joinedItem if joinedItem.GetGenericSegment(ithRangeSegmentIndex).IsFullRange() { if ithRangeSegmentIndex == 0 { list = list[:1] list[i] = joinedItem return list } ithRangeSegmentIndex-- } removedCount++ k := j + 1 for k < len(list) && list[k] == nil { k++ } if k < len(list) { list[j] = list[k] list[k] = nil jthRangeSegmentIndex = -1 } else { list[j] = nil j = i i-- jthRangeSegmentIndex = ithRangeSegmentIndex ithRangeSegmentIndex = -1 } } if removedCount > 0 { newSize := len(list) - removedCount for k, l := 0, 0; k < newSize; k, l = k+1, l+1 { for list[l] == nil { l++ } list[k] = list[l].WithoutPrefixLen() } list = list[:newSize] } else { for n := 0; n < len(list); n++ { list[n] = list[n].WithoutPrefixLen() } } return list } func organizeSequentially(sections []ExtendedIPSegmentSeries) (singleElement bool, list []ExtendedIPSegmentSeries) { //TODO change to generics var sequentialList []ExtendedIPSegmentSeries length := len(sections) for i := 0; i < length; i++ { section := sections[i] if section == nil { continue } if !section.IsSequential() { if sequentialList == nil { sequentialList = make([]ExtendedIPSegmentSeries, 0, length) for j := 0; j < i; j++ { series := sections[j] if series != nil { sequentialList = append(sequentialList, series) } } } iterator := section.SequentialBlockIterator() for iterator.HasNext() { sequentialList = append(sequentialList, iterator.Next()) } } else if sequentialList != nil { sequentialList = append(sequentialList, section) } } if sequentialList == nil { sequentialList = sections } sequentialLen := len(sequentialList) for j := 0; j < sequentialLen; j++ { series := sequentialList[j] if series.IsSinglePrefixBlock() { list = append(list, series) } else { span := series.SpanWithPrefixBlocks() list = append(list, span...) } } if len(list) <= 1 { return true, list } sort.Slice(list, func(i, j int) bool { return LowValueComparator.CompareSeries(list[i], list[j]) < 0 }) return false, list } func organizeSequentialMerge(sections []ExtendedIPSegmentSeries) (singleElement bool, list []ExtendedIPSegmentSeries) { // TODO LATER change to generics for i := 0; i < len(sections); i++ { section := sections[i] if section == nil { continue } if section.IsSequential() { list = append(list, section) } else { iterator := section.SequentialBlockIterator() for iterator.HasNext() { list = append(list, iterator.Next()) } } } if len(list) == 1 { singleElement = true return } sort.Slice(list, func(i, j int) bool { return LowValueComparator.CompareSeries(list[i], list[j]) < 0 }) return } ipaddress-go-1.5.4/ipaddr/messages/000077500000000000000000000000001440250641600171565ustar00rootroot00000000000000ipaddress-go-1.5.4/ipaddr/messages/convert.go000066400000000000000000000070371440250641600211740ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package main import ( "bufio" "io/ioutil" "log" "os" "strconv" "strings" ) func main() { path := "ipaddr/" mappings, err := readPropertiesFile(path + "IPAddressResources.properties") if err != nil { log.Fatal(err) } source := writeSourceFile(mappings) _ = ioutil.WriteFile(path+"ipaddressresources.go", source, 0644) } func writeSourceFile(mappings map[string]string) []byte { indexMappings := make(map[string]int) indices := make([]int, len(mappings)) valsArray := make([]string, len(mappings)) i := 0 valLen := 0 // create the mappings from string to index into slice, from slice entry to string index for key, val := range mappings { indexMappings[key] = i indices[i] = valLen valsArray[i] = val valLen += len(val) i++ } // now prepare the source code for each of the three elements, the map, the slice, and the string mappingsStr := "\n" for key, val := range indexMappings { mappingsStr += "`" + key + "`: " + strconv.Itoa(val) + ",\n" } indicesStr := "" for i, val := range indices { if i%10 == 0 { indicesStr += "\n" } indicesStr += strconv.Itoa(val) + "," } strStr := "\n" for i, val := range valsArray { if i > 0 { strStr += "+\n" } strStr += "`" + val + "`" } bytes := `// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Code generated by running convert.go from the go workspace directory (the one containing the src folder). Do not edit. package ipaddr var keyStrMap = map[string]int {` + mappingsStr + ` } var strIndices = []int{` + indicesStr + ` } var strVals =` + strStr + ` func lookupStr(key string) (result string) { if index, ok := keyStrMap[key]; ok { start, end := strIndices[index], strIndices[index+1] result = strVals[start:end] } return } ` return []byte(bytes) } func readPropertiesFile(filename string) (map[string]string, error) { config := make(map[string]string) file, err := os.Open(filename) if err != nil { return nil, err } defer func() { _ = file.Close() }() scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() line = strings.TrimSpace(line) if len(line) > 0 { firstChar := line[0] if firstChar != '#' && firstChar != '=' { if divIndex := strings.Index(line, "="); divIndex > 0 && divIndex < len(line)-1 { key := line[:divIndex] value := line[divIndex+1:] config[key] = value } } } } if err := scanner.Err(); err != nil { return nil, err } return config, nil } ipaddress-go-1.5.4/ipaddr/network.go000066400000000000000000000277171440250641600174050ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "net" "sync" "unsafe" ) type addressNetwork interface { getAddressCreator() parsedAddressCreator } // IPAddressNetwork represents a network of addresses of a single IP version providing a collection of standard address components for that version, such as masks and loopbacks. type IPAddressNetwork interface { GetLoopback() *IPAddress GetNetworkMask(prefixLength BitCount) *IPAddress GetPrefixedNetworkMask(prefixLength BitCount) *IPAddress GetHostMask(prefixLength BitCount) *IPAddress GetPrefixedHostMask(prefixLength BitCount) *IPAddress getIPAddressCreator() ipAddressCreator addressNetwork } type ipAddressNetwork struct { subnetsMasksWithPrefix, subnetMasks, hostMasksWithPrefix, hostMasks []*IPAddress } // // // // // type ipv6AddressNetwork struct { ipAddressNetwork creator ipv6AddressCreator } func (network *ipv6AddressNetwork) getIPAddressCreator() ipAddressCreator { return &network.creator } func (network *ipv6AddressNetwork) getAddressCreator() parsedAddressCreator { return &network.creator } func (network *ipv6AddressNetwork) GetLoopback() *IPAddress { return ipv6loopback.ToIP() } func (network *ipv6AddressNetwork) GetNetworkMask(prefLen BitCount) *IPAddress { return getMask(IPv6, zeroIPv6Seg.ToDiv(), prefLen, network.subnetMasks, true, false) } func (network *ipv6AddressNetwork) GetPrefixedNetworkMask(prefLen BitCount) *IPAddress { return getMask(IPv6, zeroIPv6Seg.ToDiv(), prefLen, network.subnetsMasksWithPrefix, true, true) } func (network *ipv6AddressNetwork) GetHostMask(prefLen BitCount) *IPAddress { return getMask(IPv6, zeroIPv6Seg.ToDiv(), prefLen, network.hostMasks, false, false) } func (network *ipv6AddressNetwork) GetPrefixedHostMask(prefLen BitCount) *IPAddress { return getMask(IPv6, zeroIPv6Seg.ToDiv(), prefLen, network.hostMasksWithPrefix, false, true) } var _ IPAddressNetwork = &ipv6AddressNetwork{} // IPv6AddressNetwork is the implementation of IPAddressNetwork for IPv6 type IPv6AddressNetwork struct { *ipv6AddressNetwork } func (network IPv6AddressNetwork) GetLoopback() *IPv6Address { return ipv6loopback } func (network IPv6AddressNetwork) GetNetworkMask(prefLen BitCount) *IPv6Address { return network.ipv6AddressNetwork.GetNetworkMask(prefLen).ToIPv6() } func (network IPv6AddressNetwork) GetPrefixedNetworkMask(prefLen BitCount) *IPv6Address { return network.ipv6AddressNetwork.GetPrefixedNetworkMask(prefLen).ToIPv6() } func (network IPv6AddressNetwork) GetHostMask(prefLen BitCount) *IPv6Address { return network.ipv6AddressNetwork.GetHostMask(prefLen).ToIPv6() } func (network IPv6AddressNetwork) GetPrefixedHostMask(prefLen BitCount) *IPv6Address { return network.ipv6AddressNetwork.GetPrefixedHostMask(prefLen).ToIPv6() } var ipv6Network = &ipv6AddressNetwork{ ipAddressNetwork: ipAddressNetwork{ make([]*IPAddress, IPv6BitCount+1), make([]*IPAddress, IPv6BitCount+1), make([]*IPAddress, IPv6BitCount+1), make([]*IPAddress, IPv6BitCount+1), }, } var IPv6Network = &IPv6AddressNetwork{ipv6Network} // // // // // type ipv4AddressNetwork struct { ipAddressNetwork creator ipv4AddressCreator } func (network *ipv4AddressNetwork) getIPAddressCreator() ipAddressCreator { return &network.creator } func (network *ipv4AddressNetwork) getAddressCreator() parsedAddressCreator { return &network.creator } func (network *ipv4AddressNetwork) GetLoopback() *IPAddress { return ipv4loopback.ToIP() } func (network *ipv4AddressNetwork) GetNetworkMask(prefLen BitCount) *IPAddress { return getMask(IPv4, zeroIPv4Seg.ToDiv(), prefLen, network.subnetMasks, true, false) } func (network *ipv4AddressNetwork) GetPrefixedNetworkMask(prefLen BitCount) *IPAddress { return getMask(IPv4, zeroIPv4Seg.ToDiv(), prefLen, network.subnetsMasksWithPrefix, true, true) } func (network *ipv4AddressNetwork) GetHostMask(prefLen BitCount) *IPAddress { return getMask(IPv4, zeroIPv4Seg.ToDiv(), prefLen, network.hostMasks, false, false) } func (network *ipv4AddressNetwork) GetPrefixedHostMask(prefLen BitCount) *IPAddress { return getMask(IPv4, zeroIPv4Seg.ToDiv(), prefLen, network.hostMasksWithPrefix, false, true) } var _ IPAddressNetwork = &ipv4AddressNetwork{} // IPv4AddressNetwork is the implementation of IPAddressNetwork for IPv4 type IPv4AddressNetwork struct { *ipv4AddressNetwork } func (network IPv4AddressNetwork) GetLoopback() *IPv4Address { return ipv4loopback } func (network IPv4AddressNetwork) GetNetworkMask(prefLen BitCount) *IPv4Address { return network.ipv4AddressNetwork.GetNetworkMask(prefLen).ToIPv4() } func (network IPv4AddressNetwork) GetPrefixedNetworkMask(prefLen BitCount) *IPv4Address { return network.ipv4AddressNetwork.GetPrefixedNetworkMask(prefLen).ToIPv4() } func (network IPv4AddressNetwork) GetHostMask(prefLen BitCount) *IPv4Address { return network.ipv4AddressNetwork.GetHostMask(prefLen).ToIPv4() } func (network IPv4AddressNetwork) GetPrefixedHostMask(prefLen BitCount) *IPv4Address { return network.ipv4AddressNetwork.GetPrefixedHostMask(prefLen).ToIPv4() } var ipv4Network = &ipv4AddressNetwork{ ipAddressNetwork: ipAddressNetwork{ make([]*IPAddress, IPv4BitCount+1), make([]*IPAddress, IPv4BitCount+1), make([]*IPAddress, IPv4BitCount+1), make([]*IPAddress, IPv4BitCount+1), }, } var IPv4Network = &IPv4AddressNetwork{ipv4Network} var maskMutex sync.Mutex func getMask(version IPVersion, zeroSeg *AddressDivision, networkPrefixLength BitCount, cache []*IPAddress, network, withPrefixLength bool) *IPAddress { bits := networkPrefixLength addressBitLength := version.GetBitCount() if bits < 0 { bits = 0 } else if bits > addressBitLength { bits = addressBitLength } cacheIndex := bits subnet := (*IPAddress)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache[cacheIndex])))) if subnet != nil { return subnet } maskMutex.Lock() subnet = cache[cacheIndex] if subnet != nil { maskMutex.Unlock() return subnet } // // // var onesSubnetIndex, zerosSubnetIndex int if network { onesSubnetIndex = int(addressBitLength) zerosSubnetIndex = 0 } else { onesSubnetIndex = 0 zerosSubnetIndex = int(addressBitLength) } segmentCount := version.GetSegmentCount() bitsPerSegment := version.GetBitsPerSegment() maxSegmentValue := version.GetMaxSegmentValue() onesSubnet := (*IPAddress)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache[onesSubnetIndex])))) if onesSubnet == nil { newSegments := createSegmentArray(segmentCount) if withPrefixLength { if network { segment := createAddressDivision(zeroSeg.deriveNewSeg(maxSegmentValue, nil)) lastSegment := createAddressDivision(zeroSeg.deriveNewSeg(maxSegmentValue, cacheBitCount(bitsPerSegment) /* bitsPerSegment */)) lastIndex := len(newSegments) - 1 fillDivs(newSegments[:lastIndex], segment) newSegments[lastIndex] = lastSegment onesSubnet = createIPAddress(createSection(newSegments, cacheBitCount(addressBitLength), version.toType()), NoZone) } else { segment := createAddressDivision(zeroSeg.deriveNewSeg(maxSegmentValue, cacheBitCount(0))) fillDivs(newSegments, segment) onesSubnet = createIPAddress(createSection(newSegments, cacheBitCount(0), version.toType()), NoZone) } } else { segment := createAddressDivision(zeroSeg.deriveNewSeg(maxSegmentValue, nil)) fillDivs(newSegments, segment) onesSubnet = createIPAddress(createSection(newSegments, nil, version.toType()), NoZone) /* address creation */ } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache[onesSubnetIndex])) atomicStorePointer(dataLoc, unsafe.Pointer(onesSubnet)) } zerosSubnet := (*IPAddress)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache[zerosSubnetIndex])))) if zerosSubnet == nil { newSegments := createSegmentArray(segmentCount) if withPrefixLength { prefLen := cacheBitCount(0) if network { segment := createAddressDivision(zeroSeg.deriveNewSeg(0, prefLen)) fillDivs(newSegments, segment) zerosSubnet = createIPAddress(createSection(newSegments, prefLen, version.toType()), NoZone) } else { lastSegment := createAddressDivision(zeroSeg.deriveNewSeg(0, cacheBitCount(bitsPerSegment) /* bitsPerSegment */)) lastIndex := len(newSegments) - 1 fillDivs(newSegments[:lastIndex], zeroSeg) newSegments[lastIndex] = lastSegment zerosSubnet = createIPAddress(createSection(newSegments, cacheBitCount(addressBitLength), version.toType()), NoZone) } } else { segment := createAddressDivision(zeroSeg.deriveNewSeg(0, nil)) fillDivs(newSegments, segment) zerosSubnet = createIPAddress(createSection(newSegments, nil, version.toType()), NoZone) } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache[zerosSubnetIndex])) atomicStorePointer(dataLoc, unsafe.Pointer(zerosSubnet)) } prefix := bits onesSegment := onesSubnet.getDivision(0) zerosSegment := zerosSubnet.getDivision(0) newSegments := createSegmentArray(segmentCount)[:0] i := 0 for ; bits > 0; i, bits = i+1, bits-bitsPerSegment { if bits <= bitsPerSegment { var segment *AddressDivision //first do a check whether we have already created a segment like the one we need offset := ((bits - 1) % bitsPerSegment) + 1 for j, entry := 0, offset; j < segmentCount; j, entry = j+1, entry+bitsPerSegment { //for j := 0, entry = offset; j < segmentCount; j++, entry += bitsPerSegment { if entry != cacheIndex { //we already know that the entry at cacheIndex is nil prev := cache[entry] if prev != nil { segment = prev.getDivision(j) break } } } //if none of the other addresses with a similar segment are created yet, we need a new segment. if segment == nil { if network { mask := maxSegmentValue & (maxSegmentValue << uint(bitsPerSegment-bits)) if withPrefixLength { segment = createAddressDivision(zeroSeg.deriveNewSeg(mask, getDivisionPrefixLength(bitsPerSegment, bits))) } else { segment = createAddressDivision(zeroSeg.deriveNewSeg(mask, nil)) } } else { mask := maxSegmentValue & ^(maxSegmentValue << uint(bitsPerSegment-bits)) if withPrefixLength { segment = createAddressDivision(zeroSeg.deriveNewSeg(mask, getDivisionPrefixLength(bitsPerSegment, bits))) } else { segment = createAddressDivision(zeroSeg.deriveNewSeg(mask, nil)) } } } newSegments = append(newSegments, segment) } else { if network { newSegments = append(newSegments, onesSegment) } else { newSegments = append(newSegments, zerosSegment) } } } for ; i < segmentCount; i++ { if network { newSegments = append(newSegments, zerosSegment) } else { newSegments = append(newSegments, onesSegment) } } var prefLen PrefixLen if withPrefixLength { prefLen = cacheBitCount(prefix) } subnet = createIPAddress(createSection(newSegments, prefLen, version.toType()), NoZone) dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache[cacheIndex])) atomicStorePointer(dataLoc, unsafe.Pointer(subnet)) maskMutex.Unlock() return subnet } type macAddressNetwork struct { creator macAddressCreator } func (network *macAddressNetwork) getAddressCreator() parsedAddressCreator { return &network.creator } var macNetwork = &macAddressNetwork{} var _ addressNetwork = &macAddressNetwork{} var ipv4loopback = createIPv4Loopback() var ipv6loopback = createIPv6Loopback() func createIPv6Loopback() *IPv6Address { ipv6loopback, _ := NewIPv6AddressFromBytes(net.IPv6loopback) return ipv6loopback } func createIPv4Loopback() *IPv4Address { ipv4loopback, _ := NewIPv4AddressFromBytes([]byte{127, 0, 0, 1}) return ipv4loopback } ipaddress-go-1.5.4/ipaddr/parsedaddr.go000066400000000000000000001732101440250641600200130ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "strconv" "sync" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) // How address sections and addresses and ranges can be created here: // section (with no error) -> address -> sequential range // non-nil hostSection -> hostAddress // nil hostSection -> section (with no error) -> address -> hostAddress // lower/upper boundary -> sequential range // lower boundary -> mask (ie address used as mask) type translatedResult struct { sections *sectionResult rng *SequentialRange[*IPAddress] mask *IPAddress //series IPAddressDivisionSeries // TODO LATER division grouping creation } type boundaryResult struct { lowerSection, upperSection *IPAddressSection } func (res *boundaryResult) createRange() *SequentialRange[*IPAddress] { //we need to add zone in order to reuse the lower and upper lowerSection := res.lowerSection creator := lowerSection.getAddrType().getIPNetwork().getIPAddressCreator() rangeLower := creator.createAddressInternalFromSection(lowerSection, NoZone, nil) var rangeUpper *IPAddress if res.upperSection == nil { rangeUpper = rangeLower } else { rangeUpper = creator.createAddressInternalFromSection(res.upperSection, NoZone, nil) } result := rangeLower.SpanWithRange(rangeUpper) return result } func (res *boundaryResult) createMask() *IPAddress { lowerSection := res.lowerSection creator := lowerSection.getAddrType().getIPNetwork().getIPAddressCreator() return creator.createAddressInternalFromSection(res.lowerSection, NoZone, nil) } type sectionResult struct { section, hostSection *IPAddressSection address, hostAddress *IPAddress joinHostError, joinAddressError /* inet_aton, single seg */, mixedError, maskError addrerr.IncompatibleAddressError } func (res *sectionResult) withoutAddressException() bool { return res.joinAddressError == nil && res.mixedError == nil && res.maskError == nil } type parsedIPAddress struct { ipAddressParseData ipAddrProvider // provides a few methods like isInvalid options addrstrparam.IPAddressStringParams originator HostIdentifierString vals translatedResult skipCntains *bool maskers, mixedMaskers []Masker creationLock sync.Mutex } func (parseData *parsedIPAddress) values() *translatedResult { return &parseData.vals } func (parseData *parsedIPAddress) providerCompare(other ipAddressProvider) (int, addrerr.IncompatibleAddressError) { return providerCompare(parseData, other) } func (parseData *parsedIPAddress) providerEquals(other ipAddressProvider) (bool, addrerr.IncompatibleAddressError) { return providerEquals(parseData, other) } func (parseData *parsedIPAddress) isProvidingIPAddress() bool { return true } func (parseData *parsedIPAddress) getType() ipType { return fromVersion(parseData.getProviderIPVersion()) } func (parseData *parsedIPAddress) getParameters() addrstrparam.IPAddressStringParams { return parseData.options } // Note: the following are needed because we have two anonymous fields and there are name clashes // Instead of defaulting to the default methods in ipAddressProvider, we need to defer to our parsed data for these methods // func (parseData *parsedIPAddress) isProvidingMixedIPv6() bool { return parseData.ipAddressParseData.isProvidingMixedIPv6() } func (parseData *parsedIPAddress) isProvidingIPv6() bool { return parseData.ipAddressParseData.isProvidingIPv6() } func (parseData *parsedIPAddress) isProvidingIPv4() bool { return parseData.ipAddressParseData.isProvidingIPv4() } func (parseData *parsedIPAddress) isProvidingBase85IPv6() bool { return parseData.ipAddressParseData.isProvidingBase85IPv6() } func (parseData *parsedIPAddress) getProviderIPVersion() IPVersion { return parseData.ipAddressParseData.getProviderIPVersion() } func (parseData *parsedIPAddress) getIPAddressParseData() *ipAddressParseData { return &parseData.ipAddressParseData } // creation methods start here func (parseData *parsedIPAddress) createSections(doSections, doRangeBoundaries, withUpper bool) (sections sectionResult, boundaries boundaryResult) { version := parseData.getProviderIPVersion() if version.IsIPv4() { return parseData.createIPv4Sections(doSections, doRangeBoundaries, withUpper) } else if version.IsIPv6() { return parseData.createIPv6Sections(doSections, doRangeBoundaries, withUpper) } return } func (parseData *parsedIPAddress) getProviderSeqRange() *SequentialRange[*IPAddress] { val := parseData.values() result := (*SequentialRange[*IPAddress])(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&val.rng)))) if result == nil { parseData.creationLock.Lock() result = val.rng if result == nil { sections := val.sections if sections == nil { _, boundaries := parseData.createSections(false, true, true) // creates lower, upper, then range from the two result = boundaries.createRange() } else { if sections.withoutAddressException() { result = sections.address.ToSequentialRange() } else { _, boundaries := parseData.createSections(false, true, true) result = boundaries.createRange() } } dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&val.rng)) atomicStorePointer(dataLoc, unsafe.Pointer(result)) } parseData.creationLock.Unlock() } return result } // this is for parsed addresses which are masks in and of themselves // with masks, only the lower value matters func (parseData *parsedIPAddress) getValForMask() *IPAddress { val := parseData.values() mask := (*IPAddress)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&val.mask)))) if mask == nil { parseData.creationLock.Lock() mask = val.mask if mask == nil { _, boundaries := parseData.createSections(false, true, false) mask = boundaries.createMask() dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&val.mask)) atomicStorePointer(dataLoc, unsafe.Pointer(mask)) } parseData.creationLock.Unlock() } return mask } func (parseData *parsedIPAddress) getCachedAddresses(forHostAddr bool) *sectionResult { val := parseData.values() sections := (*sectionResult)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&val.sections)))) if sections == nil { parseData.creationLock.Lock() sections = val.sections if sections == nil { sects, _ := parseData.createSections(true, false, false) sections = §s dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&val.sections)) atomicStorePointer(dataLoc, unsafe.Pointer(sections)) } parseData.creationLock.Unlock() } if sections.withoutAddressException() { var addr *IPAddress if forHostAddr { addr = (*IPAddress)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(§ions.hostAddress)))) } else { addr = (*IPAddress)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(§ions.address)))) } if addr == nil { parseData.creationLock.Lock() if forHostAddr { addr = sections.hostAddress } else { addr = sections.address } if addr == nil { var section *IPAddressSection var originator HostIdentifierString if forHostAddr { section = sections.hostSection if section == nil { section = sections.section } } else { section = sections.section originator = parseData.originator } creator := section.getAddrType().getIPNetwork().getIPAddressCreator() addr = creator.createAddressInternalFromSection(section, parseData.getQualifier().getZone(), originator) var dataLoc *unsafe.Pointer if forHostAddr { dataLoc = (*unsafe.Pointer)(unsafe.Pointer(§ions.hostAddress)) } else { // if range created first, stick the lower and upper into the address cache, // but only if the address no prefix, because the range never has prefix lengths if rng := val.rng; rng != nil && !addr.IsPrefixed() { cache := addr.cache if cache != nil { cache.addrsCache = &addrsCache{ lower: rng.lower.ToAddressBase(), upper: rng.upper.ToAddressBase(), } } } dataLoc = (*unsafe.Pointer)(unsafe.Pointer(§ions.address)) } atomicStorePointer(dataLoc, unsafe.Pointer(addr)) } parseData.creationLock.Unlock() } } return sections } // this is for parsed addresses which have associated masks func (parseData *parsedIPAddress) getProviderMask() *IPAddress { return parseData.getQualifier().getMaskLower() } func (parseData *parsedIPAddress) getProviderHostAddress() (*IPAddress, addrerr.IncompatibleAddressError) { addrs := parseData.getCachedAddresses(true) if addrs.mixedError != nil { return nil, addrs.mixedError } else if addrs.joinHostError != nil { return nil, addrs.joinHostError } return addrs.hostAddress, nil } func (parseData *parsedIPAddress) getProviderAddress() (*IPAddress, addrerr.IncompatibleAddressError) { addrs := parseData.getCachedAddresses(false) if addrs.mixedError != nil { return nil, addrs.mixedError } else if addrs.maskError != nil { return nil, addrs.maskError } else if addrs.joinAddressError != nil { return nil, addrs.joinAddressError } return addrs.address, nil } func (parseData *parsedIPAddress) getVersionedAddress(version IPVersion) (*IPAddress, addrerr.IncompatibleAddressError) { thisVersion := parseData.getProviderIPVersion() if version != thisVersion { return nil, nil } return parseData.getProviderAddress() } func (parseData *parsedIPAddress) getProviderNetworkPrefixLen() PrefixLen { return parseData.getQualifier().getEquivalentPrefixLen() } // TODO LATER getDivisionGrouping //func (parseData *parsedIPAddress) groupingIsSequential() bool { // try { // return getDivisionGrouping().isSequential(); // } catch(IncompatibleAddressException e) { // // division groupings avoid all IncompatibleAddressException caused by regrouping the values into segments of different size // // that takes care of two of the sources of IncompatibleAddressException: joining mixed segs, and expanding inet_aton ipv4 or single-segment ipv6 into the standard number of ipv4 or ipv6 segments // // // Those remaining are the IncompatibleAddressException caused by masks, which are the result of individual divisions becoming non-sequential // // So in such cases, you know we are not sequential. So we return false. // // the usual caveat is that this cannot happen with standard network or host masks // return false; // } // } // //func (parseData *parsedIPAddress) IsSequential() bool { // TranslatedResult val = values; // if(val != null) { // // check address first // if(!val.withoutSections()) { // // address already there, use it if we can // if(val.withoutAddressException()) { // return val.getAddress().isSequential(); // } // return groupingIsSequential(); // } // if(!val.withoutGrouping()) { // return groupingIsSequential(); // } // } // // neither address nor grouping is there, create the address // val = getCachedAddresses(false); // if(val.withoutAddressException()) { // return val.getAddress().isSequential(); // } // return groupingIsSequential(); // } // skips contains checking for addresses already parsed - // so this is not a case of unusual string formatting, because this is not for comparing strings, // but more a case of whether the parsing data structures are easy to use or not func (parseData *parsedIPAddress) skipContains() bool { segmentCount := parseData.getAddressParseData().getSegmentCount() // first we must excluded cases where the segments line up differently than standard, although we do not exclude ipv6 compressed if parseData.isProvidingIPv4() { if segmentCount != IPv4SegmentCount { // accounts for is_inet_aton_joined, singleSegment and wildcard segments return true } } else { if parseData.isProvidingMixedIPv6() || (segmentCount != IPv6SegmentCount && !parseData.isCompressed()) { // accounts for single segment and wildcard segments return true } } // exclude non-standard masks which will modify segment values from their parsed values mask := parseData.getProviderMask() if mask != nil && mask.GetBlockMaskPrefixLen(true) == nil { // handles non-standard masks return true } return false } //we do not call this method with parse data from inet_aton or single segment strings, so the cast to int is fine. //this is only for addresses with standard segment counts, although we do allow compressed. func (parseData *parsedIPAddress) isPrefixSubnet(networkPrefixLength BitCount) bool { var bytesPerSegment int var max SegInt var bitsPerSegment BitCount if parseData.isProvidingIPv4() { bytesPerSegment = IPv4BytesPerSegment bitsPerSegment = IPv4BitsPerSegment max = IPv4MaxValuePerSegment } else { bytesPerSegment = IPv6BytesPerSegment bitsPerSegment = IPv6BitsPerSegment max = IPv6MaxValuePerSegment } addressParseData := parseData.getAddressParseData() segmentCount := addressParseData.getSegmentCount() if parseData.isCompressed() { compressedCount := IPv6SegmentCount - segmentCount compressedIndex := addressParseData.getConsecutiveSeparatorSegmentIndex() return isPrefixSubnet( func(segmentIndex int) SegInt { if segmentIndex >= compressedIndex { if segmentIndex-compressedIndex < compressedCount { return 0 } segmentIndex -= compressedCount } return SegInt(parseData.getValue(segmentIndex, keyLower)) }, func(segmentIndex int) SegInt { if segmentIndex >= compressedIndex { if segmentIndex-compressedIndex < compressedCount { return 0 } segmentIndex -= compressedCount } return SegInt(parseData.getValue(segmentIndex, keyUpper)) }, segmentCount+compressedCount, bytesPerSegment, bitsPerSegment, max, networkPrefixLength, zerosOrFullRange) } return isPrefixSubnet( func(segmentIndex int) SegInt { return SegInt(parseData.getValue(segmentIndex, keyLower)) }, func(segmentIndex int) SegInt { return SegInt(parseData.getValue(segmentIndex, keyUpper)) }, segmentCount, bytesPerSegment, bitsPerSegment, max, networkPrefixLength, zerosOrFullRange) } func (parseData *parsedIPAddress) containmentCheck(other ipAddressProvider, networkOnly, equals, checkZone bool) (res boolSetting) { if otherParsed, ok := other.(*parsedIPAddress); ok { sect := (*sectionResult)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&parseData.vals.sections)))) otherSect := (*sectionResult)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&otherParsed.vals.sections)))) if sect == nil || otherSect == nil { // one or the other value not yet created, so take the shortcut that provides an answer most (but not all) of the time // An answer is provided for all normalized, conventional or canonical addresses res = parseData.containsProv(otherParsed, networkOnly, equals) if checkZone && res.isSet && res.val { res.val = parseData.getQualifier().getZone() == otherParsed.getQualifier().getZone() } } // else we defer to the values-based containment check (in the caller), which is best since it is ready to go } return } func (parseData *parsedIPAddress) containsProvider(other ipAddressProvider) (res boolSetting) { return parseData.containmentCheck(other, false, false, true) } func (parseData *parsedIPAddress) parsedEquals(other ipAddressProvider) (res boolSetting) { return parseData.containmentCheck(other, false, true, true) } func (parseData *parsedIPAddress) prefixContainsProvider(other ipAddressProvider) boolSetting { return parseData.containmentCheck(other, true, false, false) } func (parseData *parsedIPAddress) prefixEqualsProvider(other ipAddressProvider) boolSetting { return parseData.containmentCheck(other, true, true, false) } //not used for invalid, or cases where parseData.isEmpty or parseData.isAll func (parseData *parsedIPAddress) containsProv(other *parsedIPAddress, networkOnly, equals bool) (res boolSetting) { pd := parseData.getAddressParseData() otherParseData := other.getAddressParseData() segmentData := pd.getSegmentData() //grab this field for thread safety, other threads can make it disappear otherSegmentData := otherParseData.getSegmentData() //grab this field for thread safety, other threads can make it disappear if segmentData == nil || otherSegmentData == nil { return } else if parseData.skipContains() || other.skipContains() { // this excludes mixed addresses, amongst others return } ipVersion := parseData.getProviderIPVersion() if ipVersion != other.getProviderIPVersion() { return boolSetting{true, false} } segmentCount := pd.getSegmentCount() otherSegmentCount := otherParseData.getSegmentCount() var max SegInt var compressedAlready, otherCompressedAlready bool var expectedSegCount, bytesPerSegment int var bitsPerSegment BitCount if parseData.isProvidingIPv4() { max = IPv4MaxValuePerSegment expectedSegCount = IPv4SegmentCount bitsPerSegment = IPv4BitsPerSegment bytesPerSegment = IPv4BytesPerSegment compressedAlready = true otherCompressedAlready = true } else { max = IPv6MaxValuePerSegment expectedSegCount = IPv6SegmentCount bitsPerSegment = IPv6BitsPerSegment bytesPerSegment = IPv6BytesPerSegment compressedAlready = expectedSegCount == segmentCount otherCompressedAlready = expectedSegCount == otherSegmentCount } pref := parseData.getProviderNetworkPrefixLen() otherPref := other.getProviderNetworkPrefixLen() var networkSegIndex, hostSegIndex, endIndex, otherHostAllSegIndex, hostAllSegIndex int endIndex = segmentCount // determine what indexes to use for network, host, and prefix block adjustments (hostAllSegIndex and otherHostAllSegIndex) var adjustedOtherPref PrefixLen if pref == nil { networkOnly = false hostAllSegIndex = expectedSegCount otherHostAllSegIndex = expectedSegCount hostSegIndex = expectedSegCount networkSegIndex = hostSegIndex - 1 } else { prefLen := pref.bitCount() if networkOnly { hostSegIndex = getHostSegmentIndex(prefLen, bytesPerSegment, bitsPerSegment) hostAllSegIndex = hostSegIndex otherHostAllSegIndex = hostSegIndex networkSegIndex = getNetworkSegmentIndex(prefLen, bytesPerSegment, bitsPerSegment) // we treat the other as if it were a prefix block of the same prefix length // this allows us to compare entire segments for prefixEquals, ignoring the host values adjustedOtherPref = pref } else { otherHostAllSegIndex = expectedSegCount hostSegIndex = getHostSegmentIndex(prefLen, bytesPerSegment, bitsPerSegment) networkSegIndex = getNetworkSegmentIndex(prefLen, bytesPerSegment, bitsPerSegment) if parseData.isPrefixSubnet(prefLen) { hostAllSegIndex = hostSegIndex if !equals { // no need to look at host for containment when a prefix subnet networkOnly = true } } else { hostAllSegIndex = expectedSegCount } } } // Now determine if the other is a prefix block subnet, and if so, adjust otherHostAllSegIndex if otherPref != nil { otherPrefLen := otherPref.bitCount() if adjustedOtherPref == nil || otherPrefLen < adjustedOtherPref.bitCount() { otherHostIndex := getHostSegmentIndex(otherPrefLen, bytesPerSegment, bitsPerSegment) if otherHostIndex < otherHostAllSegIndex && other.isPrefixSubnet(otherPrefLen) { otherHostAllSegIndex = otherHostIndex } } else { otherPref = adjustedOtherPref } } else { otherPref = adjustedOtherPref } i, j, normalizedCount := 0, 0, 0 var compressedCount, otherCompressedCount int for i < endIndex || compressedCount > 0 { if networkOnly && normalizedCount > networkSegIndex { break } var lower, upper SegInt if compressedCount <= 0 { lower = SegInt(parseData.getValue(i, keyLower)) upper = SegInt(parseData.getValue(i, keyUpper)) } if normalizedCount >= hostAllSegIndex { // we've reached the prefixed segment segPrefLength := getSegmentPrefixLength(bitsPerSegment, pref, normalizedCount) segPref := segPrefLength.bitCount() networkMask := ^SegInt(0) << uint(bitsPerSegment-segPref) hostMask := ^networkMask lower &= networkMask upper |= hostMask } var otherLower, otherUpper SegInt if normalizedCount > otherHostAllSegIndex { otherLower = 0 otherUpper = max } else { if otherCompressedCount <= 0 { otherLower = SegInt(otherParseData.getValue(j, keyLower)) otherUpper = SegInt(otherParseData.getValue(j, keyUpper)) } if normalizedCount == otherHostAllSegIndex { // we've reached the prefixed segment segPrefLength := getSegmentPrefixLength(bitsPerSegment, otherPref, normalizedCount) segPref := segPrefLength.bitCount() networkMask := ^SegInt(0) << uint(bitsPerSegment-segPref) hostMask := ^networkMask otherLower &= networkMask otherUpper |= hostMask } } if equals { if lower != otherLower || upper != otherUpper { return boolSetting{true, false} } } else { if lower > otherLower || upper < otherUpper { return boolSetting{true, false} } } if !compressedAlready { if compressedCount > 0 { compressedCount-- if compressedCount == 0 { compressedAlready = true } } else if parseData.segmentIsCompressed(i) { i++ compressedCount = expectedSegCount - segmentCount } else { i++ } } else { i++ } if !otherCompressedAlready { if otherCompressedCount > 0 { otherCompressedCount-- if otherCompressedCount == 0 { otherCompressedAlready = true } } else if other.segmentIsCompressed(j) { j++ otherCompressedCount = expectedSegCount - otherSegmentCount } else { j++ } } else { j++ } normalizedCount++ } return boolSetting{true, true} } func allocateSegments( segments, originalSegments []*AddressDivision, segmentCount, originalCount int) []*AddressDivision { if segments == nil { segments = createSegmentArray(segmentCount) if originalCount > 0 { copy(segments, originalSegments[:originalCount]) } } return segments } func (parseData *parsedIPAddress) createIPv4Sections(doSections, doRangeBoundaries, withUpper bool) (sections sectionResult, boundaries boundaryResult) { qualifier := parseData.getQualifier() prefLen := getPrefixLength(qualifier) isMultiple := false isHostMultiple := false var segIsMult bool mask := parseData.getProviderMask() if mask != nil && mask.GetBlockMaskPrefixLen(true) != nil { mask = nil //we don't do any masking if the mask is a subnet mask, instead we just map it to the corresponding prefix length } hasMask := mask != nil addrParseData := parseData.getAddressParseData() segmentCount := addrParseData.getSegmentCount() if hasMask && parseData.maskers == nil { parseData.maskers = make([]Masker, segmentCount) } creator := ipv4Type.getIPNetwork().getIPAddressCreator() missingCount := IPv4SegmentCount - segmentCount var hostSegments, segments, lowerSegments, upperSegments []*AddressDivision if doSections { segments = createSegmentArray(IPv4SegmentCount) } else if doRangeBoundaries { lowerSegments = createSegmentArray(IPv4SegmentCount) } else { return } expandedSegments := missingCount <= 0 expandedStart, expandedEnd := -1, -1 addressString := parseData.str maskedIsDifferent := false for i, normalizedSegmentIndex := 0, 0; i < segmentCount; i++ { lower := addrParseData.getValue(i, keyLower) upper := addrParseData.getValue(i, keyUpper) if !expandedSegments { //check for any missing segments that we should account for here isLastSegment := i == segmentCount-1 isWildcard := addrParseData.isWildcard(i) expandedSegments = isLastSegment if !expandedSegments { // if we are inet_aton, we must wait for last segment // otherwise, we check if we are wildcard and no other wildcard further down expandedSegments = !parseData.is_inet_aton_joined() && isWildcard if expandedSegments { for j := i + 1; j < segmentCount; j++ { if addrParseData.isWildcard(j) { //another wildcard further down expandedSegments = false break } } } } if expandedSegments { if isWildcard { upper = 0xffffffff >> uint((3-missingCount)<<3) } else { expandedStart = i expandedEnd = i + missingCount } bits := BitCount(missingCount+1) << ipv4BitsToSegmentBitshift // BitCount(missingCount+1) * IPv4BitsPerSegment var maskedLower, maskedUpper uint64 if hasMask { var divMask uint64 for k := 0; k <= missingCount; k++ { divMask = (divMask << uint(IPv4BitsPerSegment)) | uint64(mask.GetSegment(normalizedSegmentIndex+k).GetSegmentValue()) } masker := parseData.maskers[i] if masker == nil { maxValue := ^(^uint64(0) << uint(bits)) masker = MaskRange(lower, upper, divMask, maxValue) parseData.maskers[i] = masker } if !masker.IsSequential() && sections.maskError == nil { sections.maskError = &incompatibleAddressError{ addressError: addressError{ str: maskString(lower, upper, divMask), key: "ipaddress.error.maskMismatch", }, } } maskedLower = masker.GetMaskedLower(lower, divMask) maskedUpper = masker.GetMaskedUpper(upper, divMask) maskedIsDifferent = maskedIsDifferent || maskedLower != lower || maskedUpper != upper } else { maskedLower = lower maskedUpper = upper } shift := bits count := missingCount for count >= 0 { //add the missing segments shift -= IPv4BitsPerSegment currentPrefix := getSegmentPrefixLength(IPv4BitsPerSegment, prefLen, normalizedSegmentIndex) //currentPrefix := getQualifierSegmentPrefixLength(normalizedSegmentIndex, , qualifier) hostSegLower := SegInt((lower >> uint(shift)) & IPv4MaxValuePerSegment) var hostSegUpper SegInt if lower == upper { hostSegUpper = hostSegLower } else { hostSegUpper = SegInt((upper >> uint(shift)) & IPv4MaxValuePerSegment) } var maskedSegLower, maskedSegUpper SegInt if hasMask { maskedSegLower = SegInt((maskedLower >> uint(shift)) & IPv4MaxValuePerSegment) if maskedLower == maskedUpper { maskedSegUpper = maskedSegLower } else { maskedSegUpper = SegInt((maskedUpper >> uint(shift)) & IPv4MaxValuePerSegment) } } else { maskedSegLower = hostSegLower maskedSegUpper = hostSegUpper } if doSections { if maskedIsDifferent || currentPrefix != nil { hostSegments = allocateSegments(hostSegments, segments, IPv4SegmentCount, normalizedSegmentIndex) hostSegments[normalizedSegmentIndex], segIsMult = parseData.createSegment( addressString, IPv4, hostSegLower, hostSegUpper, false, i, nil, creator) isHostMultiple = isHostMultiple || segIsMult } segments[normalizedSegmentIndex], segIsMult = parseData.createSegment( addressString, IPv4, maskedSegLower, maskedSegUpper, false, i, currentPrefix, creator) isMultiple = isMultiple || segIsMult } if doRangeBoundaries { isRange := maskedSegLower != maskedSegUpper if !doSections || isRange { if doSections { lowerSegments = allocateSegments(lowerSegments, segments, IPv4SegmentCount, normalizedSegmentIndex) } // else segments already allocated lowerSegments[normalizedSegmentIndex], _ = parseData.createSegment( addressString, IPv4, maskedSegLower, maskedSegLower, false, i, currentPrefix, creator) } else if lowerSegments != nil { lowerSegments[normalizedSegmentIndex] = segments[normalizedSegmentIndex] } if withUpper { if isRange { upperSegments = allocateSegments(upperSegments, lowerSegments, IPv4SegmentCount, normalizedSegmentIndex) upperSegments[normalizedSegmentIndex], _ = parseData.createSegment( addressString, IPv4, maskedSegUpper, maskedSegUpper, false, i, currentPrefix, creator) } else if upperSegments != nil { upperSegments[normalizedSegmentIndex] = lowerSegments[normalizedSegmentIndex] } } } normalizedSegmentIndex++ count-- } addrParseData.setBitLength(i, bits) continue } //end handle inet_aton joined segments } hostLower, hostUpper := lower, upper var masker Masker unmasked := true if hasMask { masker = parseData.maskers[i] maskInt := uint64(mask.GetSegment(normalizedSegmentIndex).GetSegmentValue()) if masker == nil { masker = MaskRange(lower, upper, maskInt, uint64(creator.getMaxValuePerSegment())) parseData.maskers[i] = masker } if !masker.IsSequential() && sections.maskError == nil { sections.maskError = &incompatibleAddressError{ addressError: addressError{ str: maskString(lower, upper, maskInt), key: "ipaddress.error.maskMismatch", }, } } lower = masker.GetMaskedLower(lower, maskInt) upper = masker.GetMaskedUpper(upper, maskInt) unmasked = hostLower == lower && hostUpper == upper maskedIsDifferent = maskedIsDifferent || !unmasked } segmentPrefixLength := getSegmentPrefixLength(IPv4BitsPerSegment, prefLen, normalizedSegmentIndex) if doSections { if maskedIsDifferent || segmentPrefixLength != nil { hostSegments = allocateSegments(hostSegments, segments, IPv4SegmentCount, normalizedSegmentIndex) hostSegments[normalizedSegmentIndex], segIsMult = parseData.createSegment( addressString, IPv4, SegInt(hostLower), SegInt(hostUpper), true, i, nil, creator) isHostMultiple = isHostMultiple || segIsMult } segments[normalizedSegmentIndex], segIsMult = parseData.createSegment( addressString, IPv4, SegInt(lower), SegInt(upper), unmasked, i, segmentPrefixLength, creator) isMultiple = isMultiple || segIsMult } if doRangeBoundaries { isRange := lower != upper if !doSections || isRange { if doSections { lowerSegments = allocateSegments(lowerSegments, segments, IPv4SegmentCount, normalizedSegmentIndex) } // else segments already allocated lowerSegments[normalizedSegmentIndex], _ = parseData.createSegment( addressString, IPv4, SegInt(lower), SegInt(lower), false, i, segmentPrefixLength, creator) } else if lowerSegments != nil { lowerSegments[normalizedSegmentIndex] = segments[normalizedSegmentIndex] } if withUpper { if isRange { upperSegments = allocateSegments(upperSegments, lowerSegments, IPv4SegmentCount, normalizedSegmentIndex) upperSegments[normalizedSegmentIndex], _ = parseData.createSegment( addressString, IPv4, SegInt(upper), SegInt(upper), false, i, segmentPrefixLength, creator) } else if upperSegments != nil { upperSegments[normalizedSegmentIndex] = lowerSegments[normalizedSegmentIndex] } } } normalizedSegmentIndex++ addrParseData.setBitLength(i, IPv4BitsPerSegment) } prefLength := getPrefixLength(qualifier) var result, hostResult *IPAddressSection if doSections { result = creator.createPrefixedSectionInternal(segments, isMultiple, prefLength) sections.section = result if hostSegments != nil { hostResult = creator.createSectionInternal(hostSegments, isHostMultiple).ToIP() sections.hostSection = hostResult if checkExpandedValues(hostResult, expandedStart, expandedEnd) { sections.joinHostError = &incompatibleAddressError{ addressError{ str: addressString, key: "ipaddress.error.invalid.joined.ranges", }, } } } if checkExpandedValues(result, expandedStart, expandedEnd) { sections.joinAddressError = &incompatibleAddressError{addressError{str: addressString, key: "ipaddress.error.invalid.joined.ranges"}} if hostResult == nil { sections.joinHostError = sections.joinAddressError } } } if doRangeBoundaries { // if we have a prefix subnet, it is possible our lower and upper boundaries exceed what appears in the parsed address prefixLength := getPrefixLength(qualifier) isPrefixSub := false if prefixLength != nil { var lowerSegs, upperSegs []*AddressDivision if doSections { upperSegs = segments lowerSegs = upperSegs } else { lowerSegs = lowerSegments if upperSegments == nil { upperSegs = lowerSegments } else { upperSegs = upperSegments } } isPrefixSub = isPrefixSubnet( func(index int) SegInt { return lowerSegs[index].ToSegmentBase().GetSegmentValue() }, func(index int) SegInt { return upperSegs[index].ToSegmentBase().GetUpperSegmentValue() }, len(lowerSegs), IPv4BytesPerSegment, IPv4BitsPerSegment, IPv4MaxValuePerSegment, prefixLength.bitCount(), zerosOnly) if isPrefixSub { if lowerSegments == nil { //allocate lower segments from address segments lowerSegments = allocateSegments(lowerSegments, segments, IPv4SegmentCount, IPv4SegmentCount) } if upperSegments == nil { //allocate upper segments from lower segments upperSegments = allocateSegments(upperSegments, lowerSegments, IPv4SegmentCount, IPv4SegmentCount) } } } if lowerSegments != nil { boundaries.lowerSection = creator.createPrefixedSectionInternalSingle(lowerSegments, false, prefLength) } if upperSegments != nil { section := creator.createPrefixedSectionInternal(upperSegments, false, prefLength) if isPrefixSub { section = section.ToPrefixBlock() } boundaries.upperSection = section.GetUpper() } } return } func (parseData *parsedIPAddress) createIPv6Sections(doSections, doRangeBoundaries, withUpper bool) (sections sectionResult, boundaries boundaryResult) { qualifier := parseData.getQualifier() prefLen := getPrefixLength(qualifier) mask := parseData.getProviderMask() if mask != nil && mask.GetBlockMaskPrefixLen(true) != nil { mask = nil //we don't do any masking if the mask is a subnet mask, instead we just map it to the corresponding prefix length } hasMask := mask != nil isMultiple := false isHostMultiple := false var segIsMult bool addressParseData := parseData.getAddressParseData() segmentCount := addressParseData.getSegmentCount() if hasMask && parseData.maskers == nil { parseData.maskers = make([]Masker, segmentCount) } creator := ipv6Type.getIPNetwork().getIPAddressCreator() ipv6SegmentCount := IPv6SegmentCount var hostSegments, segments, lowerSegments, upperSegments []*AddressDivision if doSections { segments = createSegmentArray(IPv6SegmentCount) } else if doRangeBoundaries { lowerSegments = createSegmentArray(IPv6SegmentCount) } else { return } mixed := parseData.isProvidingMixedIPv6() normalizedSegmentIndex := 0 var missingSegmentCount int if mixed { missingSegmentCount = IPv6MixedOriginalSegmentCount } else { missingSegmentCount = IPv6SegmentCount } missingSegmentCount -= segmentCount expandedSegments := missingSegmentCount <= 0 expandedStart, expandedEnd := -1, -1 addressString := parseData.str maskedIsDifferent := false //get the segments for IPv6 for i := 0; i < segmentCount; i++ { lower := addressParseData.getValue(i, keyLower) upper := addressParseData.getValue(i, keyUpper) if !expandedSegments { isLastSegment := i == segmentCount-1 isWildcard := addressParseData.isWildcard(i) isCompressed := parseData.segmentIsCompressed(i) // figure out if this segment should be expanded expandedSegments = isLastSegment || isCompressed if !expandedSegments { // we check if we are wildcard and no other wildcard or compressed segment further down expandedSegments = isWildcard if expandedSegments { for j := i + 1; j < segmentCount; j++ { if addressParseData.isWildcard(j) || parseData.segmentIsCompressed(j) { expandedSegments = false break } } } } if expandedSegments { var lowerHighBytes, upperHighBytes uint64 hostIsRange := false if !isCompressed { if isWildcard { if missingSegmentCount > 3 { upperHighBytes = 0xffffffffffffffff >> uint((7-missingSegmentCount)<<4) upper = 0xffffffffffffffff } else { upperHighBytes = 0 upper = 0xffffffffffffffff >> uint((3-missingSegmentCount)<<4) } lower = 0 hostIsRange = true } else { if missingSegmentCount > 3 { lowerHighBytes = addressParseData.getValue(i, keyExtendedLower) //the high half of the lower value upperHighBytes = addressParseData.getValue(i, keyExtendedUpper) //the high half of the upper value hostIsRange = (lower != upper) || (lowerHighBytes != upperHighBytes) } else { hostIsRange = lower != upper } expandedStart = i expandedEnd = i + missingSegmentCount } } bits := BitCount(missingSegmentCount+1) << ipv6BitsToSegmentBitshift // BitCount(missingSegmentCount+1) * IPv6BitsPerSegment var maskedLower, maskedUpper, maskedLowerHighBytes, maskedUpperHighBytes uint64 maskedIsRange := false if hasMask { // line up the mask segments into two longs if isCompressed { parseData.maskers[i] = defaultMasker } else { bitsPerSegment := IPv6BitsPerSegment var maskVal uint64 = 0 if missingSegmentCount >= 4 { cachedMasker := parseData.maskers[i] var extendedMaskVal uint64 extendedCount := missingSegmentCount - 3 for k := 0; k < extendedCount; k++ { extendedMaskVal = (extendedMaskVal << uint(bitsPerSegment)) | mask.GetSegment(normalizedSegmentIndex+k).getDivisionValue() } for k := extendedCount; k <= missingSegmentCount; k++ { maskVal = (maskVal << uint(bitsPerSegment)) | mask.GetSegment(normalizedSegmentIndex+k).getDivisionValue() } if cachedMasker == nil { // shift must be 6 bits at most for this shift to work per the java spec (so it must be less than 2^6 = 64) extendedMaxValue := ^(^DivInt(0) << uint(bits-DivIntSize)) cachedMasker = MaskExtendedRange( lower, lowerHighBytes, upper, upperHighBytes, maskVal, extendedMaskVal, 0xffffffffffffffff, extendedMaxValue) parseData.maskers[i] = cachedMasker } if !cachedMasker.IsSequential() && sections.maskError == nil { sections.maskError = &incompatibleAddressError{ addressError: addressError{ str: addressString, key: "ipaddress.error.maskMismatch", }, } } masker := cachedMasker.(ExtendedMasker) maskedLowerHighBytes = masker.GetExtendedMaskedLower(lowerHighBytes, extendedMaskVal) maskedUpperHighBytes = masker.GetExtendedMaskedUpper(upperHighBytes, extendedMaskVal) maskedLower = masker.GetMaskedLower(lower, maskVal) maskedUpper = masker.GetMaskedUpper(upper, maskVal) maskedIsRange = (maskedLower != maskedUpper) || (maskedLowerHighBytes != maskedUpperHighBytes) maskedIsDifferent = maskedIsDifferent || maskedLower != lower || maskedUpper != upper || maskedLowerHighBytes != lowerHighBytes || maskedUpperHighBytes != upperHighBytes } else { masker := parseData.maskers[i] for k := 0; k <= missingSegmentCount; k++ { maskVal = (maskVal << uint(bitsPerSegment)) | mask.GetSegment(normalizedSegmentIndex+k).getDivisionValue() } if masker == nil { // shift must be 6 bits at most for this shift to work per the java spec (so it must be less than 2^6 = 64) maxValue := ^(^DivInt(0) << uint(bits)) masker = MaskRange(lower, upper, maskVal, maxValue) parseData.maskers[i] = masker } if !masker.IsSequential() && sections.maskError == nil { sections.maskError = &incompatibleAddressError{ addressError: addressError{ str: maskString(lower, upper, maskVal), key: "ipaddress.error.maskMismatch", }, } } maskedLower = masker.GetMaskedLower(lower, maskVal) maskedUpper = masker.GetMaskedUpper(upper, maskVal) maskedIsRange = maskedLower != maskedUpper maskedIsDifferent = maskedIsDifferent || maskedLower != lower || maskedUpper != upper } } } else { maskedLowerHighBytes = lowerHighBytes maskedUpperHighBytes = upperHighBytes maskedLower = lower maskedUpper = upper maskedIsRange = hostIsRange } shift := bits count := missingSegmentCount for count >= 0 { // add the missing segments currentPrefix := getSegmentPrefixLength(IPv6BitsPerSegment, prefLen, normalizedSegmentIndex) var hostSegLower, hostSegUpper, maskedSegLower, maskedSegUpper uint64 if !isCompressed { shift -= IPv6BitsPerSegment if count >= 4 { shorterShift := shift - (IPv6BitsPerSegment << 2) hostSegLower = (lowerHighBytes >> uint(shorterShift)) & IPv6MaxValuePerSegment if hostIsRange { hostSegUpper = (upperHighBytes >> uint(shorterShift)) & IPv6MaxValuePerSegment } else { hostSegUpper = hostSegLower } if hasMask { maskedSegLower = (maskedLowerHighBytes >> uint(shorterShift)) & IPv6MaxValuePerSegment if maskedIsRange { maskedSegUpper = (maskedUpperHighBytes >> uint(shorterShift)) & IPv6MaxValuePerSegment } else { maskedSegUpper = maskedSegLower } } else { maskedSegLower = hostSegLower maskedSegUpper = hostSegUpper } } else { hostSegLower = (lower >> uint(shift)) & IPv6MaxValuePerSegment if hostIsRange { hostSegUpper = (upper >> uint(shift)) & IPv6MaxValuePerSegment } else { hostSegUpper = hostSegLower } if hasMask { maskedSegLower = (maskedLower >> uint(shift)) & IPv6MaxValuePerSegment if maskedIsRange { maskedSegUpper = (maskedUpper >> uint(shift)) & IPv6MaxValuePerSegment } else { maskedSegUpper = maskedSegLower } } else { maskedSegLower = hostSegLower maskedSegUpper = hostSegUpper } } } if doSections { if maskedIsDifferent || currentPrefix != nil { hostSegments = allocateSegments(hostSegments, segments, ipv6SegmentCount, normalizedSegmentIndex) hostSegments[normalizedSegmentIndex], segIsMult = parseData.createSegment( addressString, IPv6, SegInt(hostSegLower), SegInt(hostSegUpper), false, i, nil, creator) isHostMultiple = isHostMultiple || segIsMult } segments[normalizedSegmentIndex], segIsMult = parseData.createSegment( addressString, IPv6, SegInt(maskedSegLower), SegInt(maskedSegUpper), false, i, currentPrefix, creator) isMultiple = isMultiple || segIsMult } if doRangeBoundaries { isSegRange := maskedSegLower != maskedSegUpper if !doSections || isSegRange { if doSections { lowerSegments = allocateSegments(lowerSegments, segments, ipv6SegmentCount, normalizedSegmentIndex) } // else segments already allocated lowerSegments[normalizedSegmentIndex], _ = parseData.createSegment( addressString, IPv6, SegInt(maskedSegLower), SegInt(maskedSegLower), false, i, currentPrefix, creator) } else if lowerSegments != nil { lowerSegments[normalizedSegmentIndex] = segments[normalizedSegmentIndex] } if withUpper { if isSegRange { upperSegments = allocateSegments(upperSegments, lowerSegments, ipv6SegmentCount, normalizedSegmentIndex) upperSegments[normalizedSegmentIndex], _ = parseData.createSegment( addressString, IPv6, SegInt(maskedSegUpper), SegInt(maskedSegUpper), false, i, currentPrefix, creator) } else if upperSegments != nil { upperSegments[normalizedSegmentIndex] = lowerSegments[normalizedSegmentIndex] } } } normalizedSegmentIndex++ count-- } addressParseData.setBitLength(i, bits) continue } //end handle joined segments } hostLower, hostUpper := lower, upper var masker Masker unmasked := true if hasMask { masker = parseData.maskers[i] maskInt := uint64(mask.GetSegment(normalizedSegmentIndex).GetSegmentValue()) if masker == nil { masker = MaskRange(lower, upper, maskInt, uint64(creator.getMaxValuePerSegment())) parseData.maskers[i] = masker } if !masker.IsSequential() && sections.maskError == nil { sections.maskError = &incompatibleAddressError{ addressError: addressError{ str: maskString(lower, upper, maskInt), key: "ipaddress.error.maskMismatch", }, } } lower = masker.GetMaskedLower(lower, maskInt) upper = masker.GetMaskedUpper(upper, maskInt) unmasked = hostLower == lower && hostUpper == upper maskedIsDifferent = maskedIsDifferent || !unmasked } segmentPrefixLength := getSegmentPrefixLength(IPv6BitsPerSegment, prefLen, normalizedSegmentIndex) if doSections { if maskedIsDifferent || segmentPrefixLength != nil { hostSegments = allocateSegments(hostSegments, segments, ipv6SegmentCount, normalizedSegmentIndex) hostSegments[normalizedSegmentIndex], segIsMult = parseData.createSegment( addressString, IPv6, SegInt(hostLower), SegInt(hostUpper), true, i, nil, creator) isHostMultiple = isHostMultiple || segIsMult } segments[normalizedSegmentIndex], segIsMult = parseData.createSegment( addressString, IPv6, SegInt(lower), SegInt(upper), unmasked, i, segmentPrefixLength, creator) isMultiple = isMultiple || segIsMult } if doRangeBoundaries { isRange := lower != upper if !doSections || isRange { if doSections { lowerSegments = allocateSegments(lowerSegments, segments, ipv6SegmentCount, normalizedSegmentIndex) } // else segments already allocated lowerSegments[normalizedSegmentIndex], _ = parseData.createSegment( addressString, IPv6, SegInt(lower), SegInt(lower), false, i, segmentPrefixLength, creator) } else if lowerSegments != nil { lowerSegments[normalizedSegmentIndex] = segments[normalizedSegmentIndex] } if withUpper { if isRange { upperSegments = allocateSegments(upperSegments, lowerSegments, ipv6SegmentCount, normalizedSegmentIndex) upperSegments[normalizedSegmentIndex], _ = parseData.createSegment( addressString, IPv6, SegInt(upper), SegInt(upper), false, i, segmentPrefixLength, creator) } else if upperSegments != nil { upperSegments[normalizedSegmentIndex] = lowerSegments[normalizedSegmentIndex] } } } normalizedSegmentIndex++ addressParseData.setBitLength(i, IPv6BitsPerSegment) } prefLength := getPrefixLength(qualifier) if mixed { ipv4Range := parseData.mixedParsedAddress.getProviderSeqRange().ToIPv4() if hasMask && parseData.mixedMaskers == nil { parseData.mixedMaskers = make([]Masker, IPv4SegmentCount) } for n := 0; n < 2; n++ { m := n << 1 segmentPrefixLength := getSegmentPrefixLength(IPv6BitsPerSegment, prefLen, normalizedSegmentIndex) //segmentPrefixLength := getQualifierSegmentPrefixLength(normalizedSegmentIndex, IPv6BitsPerSegment, qualifier) o := m + 1 oneLow := ipv4Range.GetLower().GetSegment(m) twoLow := ipv4Range.GetLower().GetSegment(o) oneUp := ipv4Range.GetUpper().GetSegment(m) twoUp := ipv4Range.GetUpper().GetSegment(o) oneLower := oneLow.GetSegmentValue() twoLower := twoLow.GetSegmentValue() oneUpper := oneUp.GetSegmentValue() twoUpper := twoUp.GetSegmentValue() originalOneLower := oneLower originalTwoLower := twoLower originalOneUpper := oneUpper originalTwoUpper := twoUpper if hasMask { maskInt := uint64(mask.GetSegment(normalizedSegmentIndex).GetSegmentValue()) shift := IPv4BitsPerSegment shiftedMask := maskInt >> uint(shift) masker := parseData.mixedMaskers[m] lstringLower := uint64(oneLower) lstringUpper := uint64(oneUpper) if masker == nil { masker = MaskRange(lstringLower, lstringUpper, shiftedMask, IPv4MaxValuePerSegment) parseData.mixedMaskers[m] = masker } if !masker.IsSequential() && sections.maskError == nil { sections.maskError = &incompatibleAddressError{ addressError: addressError{ str: maskString(lstringLower, lstringUpper, shiftedMask), key: "ipaddress.error.maskMismatch", }, } } oneLower = SegInt(masker.GetMaskedLower(lstringLower, shiftedMask)) oneUpper = SegInt(masker.GetMaskedUpper(lstringUpper, shiftedMask)) lstringLower = uint64(twoLower) lstringUpper = uint64(twoUpper) masker = parseData.mixedMaskers[m+1] if masker == nil { masker = MaskRange(lstringLower, lstringUpper, maskInt, IPv4MaxValuePerSegment) parseData.mixedMaskers[m+1] = masker } if !masker.IsSequential() && sections.maskError == nil { sections.maskError = &incompatibleAddressError{ addressError: addressError{ str: maskString(lstringLower, lstringUpper, maskInt), key: "ipaddress.error.maskMismatch", }, } } twoLower = SegInt(masker.GetMaskedLower(lstringLower, maskInt)) twoUpper = SegInt(masker.GetMaskedUpper(lstringUpper, maskInt)) maskedIsDifferent = maskedIsDifferent || oneLower != originalOneLower || oneUpper != originalOneUpper || twoLower != originalTwoLower || twoUpper != originalTwoUpper } isRange := oneLower != oneUpper || twoLower != twoUpper if doSections { doHostSegment := maskedIsDifferent || segmentPrefixLength != nil if doHostSegment { hostSegments = allocateSegments(hostSegments, segments, ipv6SegmentCount, normalizedSegmentIndex) } if !isRange { if doHostSegment { hostSegments[normalizedSegmentIndex] = createIPv6Segment(originalOneLower, originalTwoLower, nil, creator) } segments[normalizedSegmentIndex] = createIPv6Segment( oneLower, twoLower, segmentPrefixLength, creator) } else { if doHostSegment { hostSegments[normalizedSegmentIndex] = createIPv6RangeSegment( §ions, ipv4Range, originalOneLower, originalOneUpper, originalTwoLower, originalTwoUpper, nil, creator) } segments[normalizedSegmentIndex] = createIPv6RangeSegment( §ions, ipv4Range, oneLower, oneUpper, twoLower, twoUpper, segmentPrefixLength, creator) isMultiple = true } } if doRangeBoundaries { if !doSections || isRange { if doSections { lowerSegments = allocateSegments(lowerSegments, segments, ipv6SegmentCount, normalizedSegmentIndex) } // else segments already allocated lowerSegments[normalizedSegmentIndex] = createIPv6Segment( oneLower, twoLower, segmentPrefixLength, creator) } else if lowerSegments != nil { lowerSegments[normalizedSegmentIndex] = segments[normalizedSegmentIndex] } if withUpper { if isRange { upperSegments = allocateSegments(upperSegments, lowerSegments, ipv6SegmentCount, normalizedSegmentIndex) upperSegments[normalizedSegmentIndex] = createIPv6Segment( oneUpper, twoUpper, segmentPrefixLength, // we must keep prefix length for upper to get prefix subnet creation creator) } else if upperSegments != nil { upperSegments[normalizedSegmentIndex] = lowerSegments[normalizedSegmentIndex] } } } normalizedSegmentIndex++ } } var result, hostResult *IPAddressSection if doSections { if hostSegments != nil { hostResult = creator.createSectionInternal(hostSegments, isHostMultiple).ToIP() sections.hostSection = hostResult if checkExpandedValues(hostResult, expandedStart, expandedEnd) { sections.joinHostError = &incompatibleAddressError{addressError{str: addressString, key: "ipaddress.error.invalid.joined.ranges"}} } } result = creator.createPrefixedSectionInternal(segments, isMultiple, prefLength) sections.section = result if checkExpandedValues(result, expandedStart, expandedEnd) { sections.joinAddressError = &incompatibleAddressError{addressError{str: addressString, key: "ipaddress.error.invalid.joined.ranges"}} if hostResult == nil { sections.joinHostError = sections.joinAddressError } } } if doRangeBoundaries { prefixLength := getPrefixLength(qualifier) isPrefixSub := false if prefixLength != nil { var lowerSegs, upperSegs []*AddressDivision if doSections { lowerSegs = segments upperSegs = segments } else { lowerSegs = lowerSegments if upperSegments == nil { upperSegs = lowerSegments } else { upperSegs = upperSegments } } isPrefixSub = isPrefixSubnet( func(index int) SegInt { return lowerSegs[index].ToSegmentBase().GetSegmentValue() }, func(index int) SegInt { return upperSegs[index].ToSegmentBase().GetUpperSegmentValue() }, len(lowerSegs), IPv6BytesPerSegment, IPv6BitsPerSegment, IPv6MaxValuePerSegment, prefixLength.bitCount(), zerosOnly) if isPrefixSub { if lowerSegments == nil { //allocate lower segments from address segments lowerSegments = allocateSegments(lowerSegments, segments, ipv6SegmentCount, ipv6SegmentCount) } if upperSegments == nil { //allocate upper segments from lower segments upperSegments = allocateSegments(upperSegments, lowerSegments, ipv6SegmentCount, ipv6SegmentCount) } } } if lowerSegments != nil { boundaries.lowerSection = creator.createPrefixedSectionInternalSingle(lowerSegments, false, prefLength) } if upperSegments != nil { section := creator.createPrefixedSectionInternal(upperSegments, false, prefLength) if isPrefixSub { section = section.ToPrefixBlock() } boundaries.upperSection = section.GetUpper() } } return } func maskString(lower, upper, maskInt uint64) string { return strconv.FormatUint(lower, 10) + "-" + strconv.FormatUint(upper, 10) + " /" + strconv.FormatUint(maskInt, 10) } // When expanding a set of segments into multiple, it is possible that the new segments do not accurately // cover the same ranges of values. This occurs when there is a range in the upper segments and the lower // segments do not cover the full range (as is the case in the original unexpanded segment). // // This does not include compressed 0 segments or compressed '*' segments, as neither can have the issue. // // Returns true if the expansion was invalid. func checkExpandedValues(section *IPAddressSection, start, end int) bool { if section != nil && start < end { seg := section.GetSegment(start) lastWasRange := seg.isMultiple() for { start++ seg = section.GetSegment(start) if lastWasRange { if !seg.IsFullRange() { return true } } else { lastWasRange = seg.isMultiple() } if start >= end { break } } } return false } func (parseData *parsedIPAddress) createSegment( addressString string, version IPVersion, val, upperVal SegInt, useFlags bool, parsedSegIndex int, segmentPrefixLength PrefixLen, creator parsedAddressCreator) (div *AddressDivision, isMultiple bool) { parsed := parseData.getAddressParseData() if val != upperVal { return createRangeSeg(addressString, version, val, upperVal, useFlags, parsed, parsedSegIndex, segmentPrefixLength, creator), true } var result *AddressDivision if !useFlags { result = creator.createSegment(val, val, segmentPrefixLength) } else { result = creator.createSegmentInternal( val, segmentPrefixLength, addressString, val, parsed.getFlag(parsedSegIndex, keyStandardStr), parsed.getIndex(parsedSegIndex, keyLowerStrStartIndex), parsed.getIndex(parsedSegIndex, keyLowerStrEndIndex)) } return result, false } // create an IPv6 segment by joining two IPv4 segments func createIPv6Segment(value1, value2 SegInt, segmentPrefixLength PrefixLen, creator parsedAddressCreator) *AddressDivision { value := (value1 << uint(IPv4BitsPerSegment)) | value2 result := creator.createPrefixSegment(value, segmentPrefixLength) return result } // create an IPv6 segment by joining two IPv4 segments func createIPv6RangeSegment( //finalResult *translatedResult, sections *sectionResult, _ *SequentialRange[*IPv4Address], // this was only used to be put into any exceptions upperRangeLower, upperRangeUpper, lowerRangeLower, lowerRangeUpper SegInt, segmentPrefixLength PrefixLen, creator ipAddressCreator) *AddressDivision { shift := IPv4BitsPerSegment if upperRangeLower != upperRangeUpper { //if the high segment has a range, the low segment must match the full range, //otherwise it is not possible to create an equivalent IPv6 range when joining two IPv4 ranges if sections.mixedError == nil && lowerRangeLower != 0 || lowerRangeUpper != IPv4MaxValuePerSegment { sections.mixedError = &incompatibleAddressError{ addressError: addressError{ key: "ipaddress.error.invalidMixedRange", }, } } } return creator.createSegment( (upperRangeLower<> ipv6BitsToSegmentBitshift) //note this is intentionally a signed shift and not >>> so that networkPrefixLength of 0 returns -1 } return int((networkPrefixLength - 1) / bitsPerSegment) } return int((networkPrefixLength - 1) >> ipv4BitsToSegmentBitshift) } // getHostSegmentIndex returns the index of the segment containing the first byte outside the network prefix. // When networkPrefixLength is nil, or it matches or exceeds the bit length, returns the segment count. func getHostSegmentIndex(networkPrefixLength BitCount, bytesPerSegment int, bitsPerSegment BitCount) int { if bytesPerSegment != 1 { if bytesPerSegment == 2 { return int(networkPrefixLength >> ipv6BitsToSegmentBitshift) } return int(networkPrefixLength / bitsPerSegment) } return int(networkPrefixLength >> ipv4BitsToSegmentBitshift) } /** * Across an address prefixes are: * IPv6: (nil):...:(nil):(1 to 16):(0):...:(0) * or IPv4: ...(nil).(1 to 8).(0)... */ func getSegmentPrefixLength(bitsPerSegment BitCount, prefixLength PrefixLen, segmentIndex int) PrefixLen { if prefixLength != nil { return getPrefixedSegmentPrefixLength(bitsPerSegment, prefixLength.bitCount(), segmentIndex) } return nil } func getAdjustedPrefixLength(bitsPerSegment BitCount, prefixLength BitCount, fromIndex, endIndex int) PrefixLen { var decrement, totalBits int if bitsPerSegment == 8 { decrement = fromIndex << ipv4BitsToSegmentBitshift totalBits = endIndex << ipv4BitsToSegmentBitshift } else if bitsPerSegment == 16 { decrement = fromIndex << ipv6BitsToSegmentBitshift totalBits = endIndex << ipv6BitsToSegmentBitshift } else { decrement = fromIndex * int(bitsPerSegment) totalBits = endIndex * int(bitsPerSegment) } return getDivisionPrefixLength(BitCount(totalBits), prefixLength-BitCount(decrement)) } func getPrefixedSegmentPrefixLength(bitsPerSegment BitCount, prefixLength BitCount, segmentIndex int) PrefixLen { var decrement int if bitsPerSegment == 8 { decrement = segmentIndex << ipv4BitsToSegmentBitshift } else if bitsPerSegment == 16 { decrement = segmentIndex << ipv6BitsToSegmentBitshift } else { decrement = segmentIndex * int(bitsPerSegment) } return getDivisionPrefixLength(bitsPerSegment, prefixLength-BitCount(decrement)) } /** * Across an address prefixes are: * IPv6: (nil):...:(nil):(1 to 16):(0):...:(0) * or IPv4: ...(nil).(1 to 8).(0)... */ func getDivisionPrefixLength(divisionBits, divisionPrefixedBits BitCount) PrefixLen { if divisionPrefixedBits <= 0 { return cacheBitCount(0) //none of the bits in this segment matter } else if divisionPrefixedBits <= divisionBits { return cacheBitCount(divisionPrefixedBits) //some of the bits in this segment matter } return nil //all the bits in this segment matter } // getNetworkPrefixLen translates a non-nil segment prefix length into an address prefix length. // When calling this for the first segment with a non-nil prefix length, this gives the overall prefix length. // // Across an address prefixes are: // IPv6: (nil):...:(nil):(1 to 16):(0):...:(0) // or IPv4: ...(nil).(1 to 8).(0)... func getNetworkPrefixLen(bitsPerSegment, segmentPrefixLength BitCount, segmentIndex int) PrefixLen { var increment BitCount if bitsPerSegment == 8 { increment = BitCount(segmentIndex) << ipv4BitsToSegmentBitshift } else if bitsPerSegment == 16 { increment = BitCount(segmentIndex) << ipv6BitsToSegmentBitshift } else { increment = BitCount(segmentIndex) * bitsPerSegment } return cacheBitCount(increment + segmentPrefixLength) } func getSegmentsBitCount(bitsPerSegment BitCount, segmentCount int) BitCount { if bitsPerSegment == 8 { return BitCount(segmentCount) << ipv4BitsToSegmentBitshift } else if bitsPerSegment == 16 { return BitCount(segmentCount) << ipv6BitsToSegmentBitshift } return BitCount(segmentCount) * bitsPerSegment } // TODO LATER getDivisionGrouping: This extended prefix subnet: follow the latest Java code which has been updated. // //public static boolean isPrefixSubnet( // DivisionValueProvider lowerValueProvider, // DivisionValueProvider lowerExtendedValueProvider, // DivisionValueProvider upperValueProvider, // DivisionValueProvider upperExtendedValueProvider, // DivisionLengthProvider bitLengthProvider, // int divisionCount, // Integer networkPrefixLength, // PrefixConfiguration prefixConfiguration, // boolean fullRangeOnly) { // if(networkPrefixLength == null || prefixConfiguration.prefixedSubnetsAreExplicit()) { // return false; // } // if(networkPrefixLength < 0) { // networkPrefixLength = 0; // } // int totalBitLength = 0; // topLoop: // for(int i = 0; i < divisionCount; i++) { // int divBitLength = bitLengthProvider.getLength(i); // Integer divisionPrefLength = ParsedAddressGrouping.getDivisionPrefixLength(divBitLength, networkPrefixLength - totalBitLength); // if(divBitLength == 0) { // continue; // } // if(divisionPrefLength == null) { // totalBitLength += divBitLength; // continue; // } // int divisionPrefixLength = divisionPrefLength; // int extendedPrefixLength, extendedDivBitLength; // boolean isExtended, hasExtendedPrefixLength; // boolean hasPrefLen = divisionPrefixLength != divBitLength; // if(hasPrefLen) { // // for values larger than 64 bits, the "extended" values are the upper (aka most significant, leftmost) bits // if(isExtended = (divBitLength > Long.SIZE)) { // extendedDivBitLength = divBitLength - Long.SIZE; // divBitLength = Long.SIZE; // if(hasExtendedPrefixLength = (divisionPrefixLength < extendedDivBitLength)) { // extendedPrefixLength = divisionPrefixLength; // divisionPrefixLength = 0; // } else { // isExtended = false; // extendedPrefixLength = extendedDivBitLength; // divisionPrefixLength -= extendedDivBitLength; // } // } else { // extendedPrefixLength = extendedDivBitLength = 0; // hasExtendedPrefixLength = false; // } // } else { // extendedPrefixLength = extendedDivBitLength = 0; // hasExtendedPrefixLength = isExtended = false;// we may be extended, but we set to false because we do nothing when no prefix // } // while(true) { // if(isExtended) { // long extendedLower = lowerExtendedValueProvider.getValue(i); // if(extendedPrefixLength == 0) { // if(extendedLower != 0) { // return false; // } // long extendedUpper = upperExtendedValueProvider.getValue(i); // if(fullRangeOnly) { // long maxVal = ~0L >>> (Long.SIZE - extendedDivBitLength); // if(extendedUpper != maxVal) { // return false; // } // } else { // int upperOnes = Long.numberOfTrailingZeros(~extendedUpper); // if(upperOnes > 0) { // if(upperOnes < Long.SIZE && (extendedUpper >>> upperOnes) != 0) { // return false; // } // fullRangeOnly = true; // } else if(extendedUpper != 0) { // return false; // } // } // } else if(hasExtendedPrefixLength) { // int divHostBits = extendedDivBitLength - extendedPrefixLength; // < 64, when 64 handled by block above // if(fullRangeOnly) { // long hostMask = ~(~0L << divHostBits); // if((hostMask & extendedLower) != 0) { // return false; // } // long extendedUpper = upperExtendedValueProvider.getValue(i); // if((hostMask & extendedUpper) != hostMask) { // return false; // } // } else { // int lowerZeros = Long.numberOfTrailingZeros(extendedLower); // if(lowerZeros < divHostBits) { // return false; // } // long extendedUpper = upperExtendedValueProvider.getValue(i); // int upperOnes = Long.numberOfTrailingZeros(~extendedUpper); // if(upperOnes < divHostBits) { // int upperZeros = Long.numberOfTrailingZeros(extendedUpper >>> upperOnes); // if(upperOnes + upperZeros < divHostBits) { // return false; // } // fullRangeOnly = upperOnes > 0; // } else { // fullRangeOnly = true; // } // } // } // } // if(divisionPrefixLength == 0) { // long lower = lowerValueProvider.getValue(i); // if(lower != 0) { // return false; // } // long upper = upperValueProvider.getValue(i); // if(fullRangeOnly) { // long maxVal = ~0L >>> (Long.SIZE - divBitLength); // if(upper != maxVal) { // return false; // } // } else { // int upperOnes = Long.numberOfTrailingZeros(~upper); // if(upperOnes > 0) { // if(upperOnes < Long.SIZE && (upper >>> upperOnes) != 0) { // return false; // } // fullRangeOnly = true; // } else if(upper != 0) { // return false; // } // } // } else if(hasPrefLen){ // long lower = lowerValueProvider.getValue(i); // int divHostBits = divBitLength - divisionPrefixLength; // < 64, when 64 handled by block above // if(fullRangeOnly) { // long hostMask = ~(~0L << divHostBits); // if((hostMask & lower) != 0) { // return false; // } // long upper = upperValueProvider.getValue(i); // if((hostMask & upper) != hostMask) { // return false; // } // } else { // int lowerZeros = Long.numberOfTrailingZeros(lower); // if(lowerZeros < divHostBits) { // return false; // } // long upper = upperValueProvider.getValue(i); // int upperOnes = Long.numberOfTrailingZeros(~upper); // if(upperOnes < divHostBits) { // int upperZeros = Long.numberOfTrailingZeros(upper >>> upperOnes); // if(upperOnes + upperZeros < divHostBits) { // return false; // } // fullRangeOnly = upperOnes > 0; // } else { // fullRangeOnly = true; // } // } // } // if(++i == divisionCount) { // break topLoop; // } // divBitLength = bitLengthProvider.getLength(i); // if(hasExtendedPrefixLength = isExtended = (divBitLength > Long.SIZE)) { // extendedDivBitLength = divBitLength - Long.SIZE; // divBitLength = Long.SIZE; // } else { // extendedDivBitLength = 0; // } // extendedPrefixLength = divisionPrefixLength = 0; // } // end while // } // return true; //} type subnetOption int const ( zerosOnly = subnetOption(iota) fullRangeOnly zerosToFullRange zerosOrFullRange ) // // For explicit prefix config this always returns false. // For all prefix subnets config this always returns true if the prefix length does not extend beyond the address end. func isPrefixSubnet( lowerValueProvider, upperValueProvider SegmentValueProvider, segmentCount, bytesPerSegment int, bitsPerSegment BitCount, segmentMaxValue SegInt, prefLen BitCount, subnetOption subnetOption) bool { if prefLen < 0 { prefLen = 0 } else { var totalBitCount BitCount if bitsPerSegment == 8 { totalBitCount = BitCount(segmentCount) << ipv4BitsToSegmentBitshift } else if bitsPerSegment == 16 { totalBitCount = BitCount(segmentCount) << ipv6BitsToSegmentBitshift } else { totalBitCount = BitCount(segmentCount) * bitsPerSegment } if prefLen >= totalBitCount { return false } } prefixedSegment := getHostSegmentIndex(prefLen, bytesPerSegment, bitsPerSegment) i := prefixedSegment if i < segmentCount { zero := PrefixBitCount(0) segmentPrefixLength := getPrefixedSegmentPrefixLength(bitsPerSegment, prefLen, i) for { //we want to see if there is a sequence of zeros followed by a sequence of full-range bits from the prefix onwards //once we start seeing full range bits, the remained of the section must be full range //for instance x marks the start of zeros and y marks the start of full range: //segment 1 segment 2 ... //upper: 10101010 10100111 11111111 11111111 //lower: 00111010 00100000 00000000 00000000 // x y //upper: 10101010 10100000 00000000 00111111 //lower: 00111010 00100000 10000000 00000000 // x y // //the bit marked x in each set of 4 segment of 8 bits is a sequence of zeros, followed by full range bits starting at bit y lower := lowerValueProvider(i) prefLen := segmentPrefixLength.bitCount() if prefLen == 0 { if lower != 0 { return false } upper := upperValueProvider(i) if subnetOption == fullRangeOnly { if upper != segmentMaxValue { return false } } else if upper != 0 { if subnetOption == zerosOnly { return false } else if upper == segmentMaxValue { if subnetOption == zerosOrFullRange && i > prefixedSegment { return false } } else if subnetOption == zerosOrFullRange { return false } else { //zerosToFullRange upperTrailingOnes := bits.TrailingZeros64(^uint64(upper)) if (upper >> uint(upperTrailingOnes)) != 0 { return false } } subnetOption = fullRangeOnly } } else if prefLen < bitsPerSegment { segHostBits := bitsPerSegment - prefLen hostMask := ^(^SegInt(0) << uint(segHostBits)) if (hostMask & lower) != 0 { return false } upper := upperValueProvider(i) if subnetOption == fullRangeOnly { if (hostMask & upper) != hostMask { return false } } else { hostUpper := hostMask & upper if hostUpper != 0 { if subnetOption == zerosOnly { return false } else if hostUpper == hostMask { if subnetOption == zerosOrFullRange && i > prefixedSegment { return false } } else if subnetOption == zerosOrFullRange { return false } else { // zerosToFullRange upperTrailingOnes := uint(bits.TrailingZeros64(^uint64(upper))) hostMask >>= upperTrailingOnes upper >>= upperTrailingOnes if (hostMask & upper) != 0 { return false } } subnetOption = fullRangeOnly } } } segmentPrefixLength = &zero i++ if i >= segmentCount { break } } } return true } ipaddress-go-1.5.4/ipaddr/parsedata.go000066400000000000000000000510051440250641600176430ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr const ( upperAdjustment = 8 // These are for the flags. // A standard string is a string showing only the lower value of a segment, in lowercase. // A standard range string shows both values, low to high, with the standard separator. keyWildcard uint32 = 0x10000 keySingleWildcard uint32 = 0x20000 keyStandardStr uint32 = 0x40000 keyStandardRangeStr uint32 = 0x80000 keyRangeWildcard uint32 = 0x100000 keyInferredLowerBoundary uint32 = 0x200000 keyInferredUpperBoundary uint32 = 0x400000 keyMergedMixed uint32 = 0x800000 keyRadix uint32 = 0x00ff keyBitSize uint32 = 0xff00 bitSizeShift = 8 // the flags, radix and bit size are stored in the same int, the radix takes the low byte, // the bit size the next byte, the remaining 16 bits are available for flags. keyLowerRadixIndex = 0 keyBitSizeIndex = keyLowerRadixIndex flagsIndex = keyLowerRadixIndex keyUpperRadixIndex = keyLowerRadixIndex + upperAdjustment // these are for the segment values - they must be even-numbered keyLower = 2 keyExtendedLower = 4 keyUpper = keyLower + upperAdjustment keyExtendedUpper = keyExtendedLower + upperAdjustment // these are for the indices keyLowerStrDigitsIndex = 1 keyLowerStrStartIndex = 6 keyLowerStrEndIndex = 7 keyUpperStrDigitsIndex = keyLowerStrDigitsIndex + upperAdjustment keyUpperStrStartIndex = keyLowerStrStartIndex + upperAdjustment keyUpperStrEndIndex = keyLowerStrEndIndex + upperAdjustment segmentDataSize = 16 segmentIndexShift = 4 ipv4SegmentDataSize = segmentDataSize * 4 ipv6SegmentDataSize = segmentDataSize * 8 ) type addressParseData struct { segmentData []uint32 segmentCount int anyWildcard, isEmpty, isAllVal, isSingleSegmentVal bool // these are indices into the original string used while parsing consecutiveSepIndex, consecutiveSepSegmentIndex, addressEndIndex int str string } func (parseData *addressParseData) init(str string) { parseData.consecutiveSepIndex = -1 parseData.consecutiveSepSegmentIndex = -1 parseData.str = str } func (parseData *addressParseData) getString() string { return parseData.str } func (parseData *addressParseData) initSegmentData(segmentCapacity int) { dataSize := 0 if segmentCapacity == 4 { dataSize = ipv4SegmentDataSize } else if segmentCapacity == 8 { dataSize = ipv6SegmentDataSize } else if segmentCapacity == 1 { dataSize = segmentDataSize // segmentDataSize * segmentCapacity } else { dataSize = segmentCapacity * segmentDataSize } parseData.segmentData = make([]uint32, dataSize) } func (parseData *addressParseData) getSegmentData() []uint32 { return parseData.segmentData } func (parseData *addressParseData) incrementSegmentCount() { parseData.segmentCount++ } func (parseData *addressParseData) getSegmentCount() int { return parseData.segmentCount } func (parseData *addressParseData) getConsecutiveSeparatorSegmentIndex() int { return parseData.consecutiveSepSegmentIndex } func (parseData *addressParseData) setConsecutiveSeparatorSegmentIndex(val int) { parseData.consecutiveSepSegmentIndex = val } func (parseData *addressParseData) getConsecutiveSeparatorIndex() int { return parseData.consecutiveSepIndex } func (parseData *addressParseData) setConsecutiveSeparatorIndex(val int) { parseData.consecutiveSepIndex = val } func (parseData *addressParseData) isProvidingEmpty() bool { return parseData.isEmpty } func (parseData *addressParseData) setEmpty(val bool) { parseData.isEmpty = val } func (parseData *addressParseData) isAll() bool { return parseData.isAllVal } func (parseData *addressParseData) setAll() { parseData.isAllVal = true } func (parseData *addressParseData) getAddressEndIndex() int { return parseData.addressEndIndex } func (parseData *addressParseData) setAddressEndIndex(val int) { parseData.addressEndIndex = val } func (parseData *addressParseData) isSingleSegment() bool { return parseData.isSingleSegmentVal } func (parseData *addressParseData) setSingleSegment() { parseData.isSingleSegmentVal = true } func (parseData *addressParseData) hasWildcard() bool { return parseData.anyWildcard } func (parseData *addressParseData) setHasWildcard() { parseData.anyWildcard = true } func (parseData *addressParseData) unsetFlag(segmentIndex int, flagIndicator uint32) { index := (segmentIndex << segmentIndexShift) | flagsIndex segmentData := parseData.getSegmentData() segmentData[index] &= uint32(0xffff) ^ flagIndicator // segmentData[index] &= ~flagIndicator } func (parseData *addressParseData) getFlag(segmentIndex int, flagIndicator uint32) bool { segmentData := parseData.getSegmentData() return (segmentData[(segmentIndex<> bitSizeShift return BitCount(bitLength) } func (parseData *addressParseData) setBitLength(segmentIndex int, length BitCount) { segmentData := parseData.getSegmentData() segmentData[(segmentIndex<> 32) segmentData[index|1] = uint32(value10 & 0xffffffff) index = baseIndex | indexIndicator11 segmentData[index] = uint32(value11 >> 32) segmentData[index|1] = uint32(value11 & 0xffffffff) } func (parseData *addressParseData) set7Index4ValuesFlags(segmentIndex, indexIndicator0 int, value0 uint32, indexIndicator1 int, value1 uint32, indexIndicator2 int, value2 uint32, indexIndicator3 int, value3 uint32, indexIndicator4 int, value4 uint32, indexIndicator5 int, value5 uint32, indexIndicator6 int, value6 uint32, indexIndicator7 int, value7 uint64, indexIndicator8 int, value8 uint64, indexIndicator9 int, value9 uint64, indexIndicator10 int, value10 uint64) { baseIndex := segmentIndex << segmentIndexShift segmentData := parseData.getSegmentData() setIndexValuesFlags(baseIndex, segmentData, indexIndicator0, value0, indexIndicator1, value1, indexIndicator2, value2, indexIndicator3, value3, indexIndicator4, value4, indexIndicator5, value5, indexIndicator6, value6, indexIndicator7, value7, indexIndicator8, value8) index := baseIndex | indexIndicator9 segmentData[index] = uint32(value9 >> 32) segmentData[index|1] = uint32(value9 & 0xffffffff) index = baseIndex | indexIndicator10 segmentData[index] = uint32(value10 >> 32) segmentData[index|1] = uint32(value10 & 0xffffffff) } func (parseData *addressParseData) set8Index2ValuesFlags(segmentIndex, indexIndicator0 int, value0 uint32, indexIndicator1 int, value1 uint32, indexIndicator2 int, value2 uint32, indexIndicator3 int, value3 uint32, indexIndicator4 int, value4 uint32, indexIndicator5 int, value5 uint32, indexIndicator6 int, value6 uint32, indexIndicator7 int, value7 uint32, indexIndicator8 int, value8 uint64, indexIndicator9 int, value9 uint64) { baseIndex := segmentIndex << segmentIndexShift segmentData := parseData.getSegmentData() setIndexValuesFlags(baseIndex, segmentData, indexIndicator0, value0, indexIndicator1, value1, indexIndicator2, value2, indexIndicator3, value3, indexIndicator4, value4, indexIndicator5, value5, indexIndicator6, value6, indexIndicator8, value8, indexIndicator9, value9) segmentData[baseIndex|indexIndicator7] = value7 } func (parseData *addressParseData) set7Index2ValuesFlags(segmentIndex, indexIndicator0 int, value0 uint32, indexIndicator1 int, value1 uint32, indexIndicator2 int, value2 uint32, indexIndicator3 int, value3 uint32, indexIndicator4 int, value4 uint32, indexIndicator5 int, value5 uint32, indexIndicator6 int, value6 uint32, indexIndicator7 int, value7 uint64, indexIndicator8 int, value8 uint64) { baseIndex := segmentIndex << segmentIndexShift segmentData := parseData.getSegmentData() setIndexValuesFlags(baseIndex, segmentData, indexIndicator0, value0, indexIndicator1, value1, indexIndicator2, value2, indexIndicator3, value3, indexIndicator4, value4, indexIndicator5, value5, indexIndicator6, value6, indexIndicator7, value7, indexIndicator8, value8) } func setIndexValuesFlags( baseIndex int, segmentData []uint32, indexIndicator0 int, value0 uint32, indexIndicator1 int, value1 uint32, indexIndicator2 int, value2 uint32, indexIndicator3 int, value3 uint32, indexIndicator4 int, value4 uint32, indexIndicator5 int, value5 uint32, indexIndicator6 int, value6 uint32, indexIndicator7 int, value7 uint64, indexIndicator8 int, value8 uint64) { segmentData[baseIndex|indexIndicator0] = value0 segmentData[baseIndex|indexIndicator1] = value1 segmentData[baseIndex|indexIndicator2] = value2 segmentData[baseIndex|indexIndicator3] = value3 segmentData[baseIndex|indexIndicator4] = value4 segmentData[baseIndex|indexIndicator5] = value5 segmentData[baseIndex|indexIndicator6] = value6 index := baseIndex | indexIndicator7 segmentData[index] = uint32(value7 >> 32) segmentData[index|1] = uint32(value7 & 0xffffffff) index = baseIndex | indexIndicator8 segmentData[index] = uint32(value8 >> 32) segmentData[index|1] = uint32(value8 & 0xffffffff) } func (parseData *addressParseData) setValue(segmentIndex, indexIndicator int, value uint64) { index := (segmentIndex << segmentIndexShift) | indexIndicator upperValue := uint32(value >> 32) lowerValue := uint32(value & 0xffffffff) segmentData := parseData.getSegmentData() segmentData[index] = upperValue segmentData[index|1] = lowerValue } func (parseData *addressParseData) getValue(segmentIndex, indexIndicator int) uint64 { return getValueFromData(segmentIndex, indexIndicator, parseData.getSegmentData()) } func getValueFromData(segmentIndex, indexIndicator int, segmentData []uint32) uint64 { index := (segmentIndex << segmentIndexShift) | indexIndicator upperValue := uint64(segmentData[index]) lowerValue := 0xffffffff & uint64(segmentData[index|1]) value := (upperValue << 32) | lowerValue return value } func (parseData *addressParseData) isMergedMixed(segmentIndex int) bool { return parseData.getFlag(segmentIndex, keyMergedMixed) } func (parseData *addressParseData) isWildcard(segmentIndex int) bool { return parseData.getFlag(segmentIndex, keyWildcard) } func (parseData *addressParseData) hasRange(segmentIndex int) bool { return parseData.hasEitherFlag(segmentIndex, keySingleWildcard, keyRangeWildcard) } func (parseData *addressParseData) isInferredUpperBoundary(segmentIndex int) bool { return parseData.getFlag(segmentIndex, keyInferredUpperBoundary) } type ipAddressParseData struct { addressParseData qualifier parsedHostIdentifierStringQualifier qualifierIndex int hasPrefixSeparatorVal, isZonedVal bool ipVersion IPVersion is_inet_aton_joined_val bool has_inet_aton_value_val bool // either octal 01 or hex 0x1 hasIPv4LeadingZerosVal, isBinaryVal bool isBase85, isBase85ZonedVal bool mixedParsedAddress *parsedIPAddress } func (parseData *ipAddressParseData) init(str string) { parseData.qualifierIndex = -1 parseData.addressParseData.init(str) } func (parseData *ipAddressParseData) getAddressParseData() *addressParseData { return &parseData.addressParseData } func (parseData *ipAddressParseData) getProviderIPVersion() IPVersion { return parseData.ipVersion } func (parseData *ipAddressParseData) setVersion(version IPVersion) { parseData.ipVersion = version } func (parseData *ipAddressParseData) isProvidingIPv6() bool { version := parseData.getProviderIPVersion() return version.IsIPv6() } func (parseData *ipAddressParseData) isProvidingIPv4() bool { version := parseData.getProviderIPVersion() return version.IsIPv4() } func (parseData *ipAddressParseData) is_inet_aton_joined() bool { return parseData.is_inet_aton_joined_val } func (parseData *ipAddressParseData) set_inet_aton_joined(val bool) { parseData.is_inet_aton_joined_val = val } func (parseData *ipAddressParseData) has_inet_aton_value() bool { return parseData.has_inet_aton_value_val } func (parseData *ipAddressParseData) set_has_inet_aton_value(val bool) { parseData.has_inet_aton_value_val = val } func (parseData *ipAddressParseData) hasIPv4LeadingZeros() bool { return parseData.hasIPv4LeadingZerosVal } func (parseData *ipAddressParseData) setHasIPv4LeadingZeros(val bool) { parseData.hasIPv4LeadingZerosVal = val } func (parseData *ipAddressParseData) hasBinaryDigits() bool { return parseData.isBinaryVal } func (parseData *ipAddressParseData) setHasBinaryDigits(val bool) { parseData.isBinaryVal = val } func (parseData *ipAddressParseData) getQualifier() *parsedHostIdentifierStringQualifier { return &parseData.qualifier } func (parseData *ipAddressParseData) getQualifierIndex() int { return parseData.qualifierIndex } func (parseData *ipAddressParseData) clearQualifier() { parseData.qualifierIndex = -1 parseData.isZonedVal = false parseData.isBase85ZonedVal = false parseData.hasPrefixSeparatorVal = false parseData.qualifier = parsedHostIdentifierStringQualifier{} } func (parseData *ipAddressParseData) setQualifierIndex(index int) { parseData.qualifierIndex = index } func (parseData *ipAddressParseData) isZoned() bool { return parseData.isZonedVal } func (parseData *ipAddressParseData) setZoned(val bool) { parseData.isZonedVal = val } func (parseData *ipAddressParseData) hasPrefixSeparator() bool { return parseData.hasPrefixSeparatorVal } func (parseData *ipAddressParseData) setHasPrefixSeparator(val bool) { parseData.hasPrefixSeparatorVal = val } func (parseData *ipAddressParseData) isProvidingBase85IPv6() bool { return parseData.isBase85 } func (parseData *ipAddressParseData) setBase85(val bool) { parseData.isBase85 = val } func (parseData *ipAddressParseData) isBase85Zoned() bool { return parseData.isBase85ZonedVal } func (parseData *ipAddressParseData) setBase85Zoned(val bool) { parseData.isBase85ZonedVal = val } func (parseData *ipAddressParseData) isCompressed() bool { return parseData.addressParseData.getConsecutiveSeparatorIndex() >= 0 } func (parseData *ipAddressParseData) segIsCompressed(index int, segmentData []uint32) bool { end := getIndexFromData(index, keyUpperStrEndIndex, segmentData) start := getIndexFromData(index, keyLowerStrStartIndex, segmentData) return start == end } func (parseData *ipAddressParseData) segmentIsCompressed(index int) bool { return parseData.segIsCompressed(index, parseData.addressParseData.getSegmentData()) } func (parseData *ipAddressParseData) isProvidingMixedIPv6() bool { return parseData.mixedParsedAddress != nil } func (parseData *ipAddressParseData) setMixedParsedAddress(val *parsedIPAddress) { parseData.mixedParsedAddress = val } type macFormat *byte const ( dash = '-' colon = ':' space = ' ' dot = '.' ) var ( dashedByte byte = dash colonByte byte = colon spaceByte byte = space dotByte byte = dot dashed macFormat = &dashedByte colonDelimited macFormat = &colonByte dotted macFormat = &dotByte spaceDelimited macFormat = &spaceByte unknownFormat macFormat ) type macAddressParseData struct { addressParseData isDoubleSegmentVal, isExtendedVal bool format macFormat } func (parseData *macAddressParseData) init(str string) { parseData.addressParseData.init(str) } func (parseData *macAddressParseData) getAddressParseData() *addressParseData { return &parseData.addressParseData } func (parseData *macAddressParseData) getFormat() macFormat { return parseData.format } func (parseData *macAddressParseData) setFormat(format macFormat) { parseData.format = format } func (parseData *macAddressParseData) isDoubleSegment() bool { return parseData.isExtendedVal } func (parseData *macAddressParseData) setDoubleSegment(val bool) { parseData.isExtendedVal = val } func (parseData *macAddressParseData) isExtended() bool { return parseData.isDoubleSegmentVal } func (parseData *macAddressParseData) setExtended(val bool) { parseData.isDoubleSegmentVal = val } ipaddress-go-1.5.4/ipaddr/parsedhost.go000066400000000000000000000152211440250641600200530ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" "strings" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" ) type embeddedAddress struct { isUNCIPv6Literal, isReverseDNS bool addressStringError addrerr.AddressStringError addressProvider ipAddressProvider } var ( noQualifier = &parsedHostIdentifierStringQualifier{} ) type parsedHostCache struct { normalizedLabels []string host string } type parsedHost struct { separatorIndices []int // can be nil normalizedFlags []bool labelsQualifier parsedHostIdentifierStringQualifier embeddedAddress embeddedAddress originalStr string *parsedHostCache params addrstrparam.HostNameParams } func (host *parsedHost) getQualifier() *parsedHostIdentifierStringQualifier { return &host.labelsQualifier } func (host *parsedHost) isIPv6Address() bool { return host.hasEmbeddedAddress() && host.getAddressProvider().isProvidingIPv6() } func (host *parsedHost) getPort() Port { return host.labelsQualifier.getPort() } func (host *parsedHost) getService() string { return host.labelsQualifier.getService() } func (host *parsedHost) getNetworkPrefixLen() PrefixLen { return host.labelsQualifier.getNetworkPrefixLen() } func (host *parsedHost) getEquivalentPrefixLen() PrefixLen { return host.labelsQualifier.getEquivalentPrefixLen() } func (host *parsedHost) getMask() *IPAddress { return host.labelsQualifier.getMaskLower() } func (host *parsedHost) getAddressProvider() ipAddressProvider { return host.embeddedAddress.addressProvider } func (host *parsedHost) hasEmbeddedAddress() bool { return host.embeddedAddress.addressProvider != nil } func (host *parsedHost) isAddressString() bool { return host.getAddressProvider() != nil } func (host *parsedHost) asAddress() (*IPAddress, addrerr.IncompatibleAddressError) { if host.hasEmbeddedAddress() { return host.getAddressProvider().getProviderAddress() } return nil, nil } func (host *parsedHost) mapString(addressProvider ipAddressProvider) string { if addressProvider.isProvidingAllAddresses() { return SegmentWildcardStr //} else if addressProvider.isProvidingPrefixOnly() { //return IPAddressNetwork.getPrefixString(addressProvider.getProviderNetworkPrefixLength()) } else if addressProvider.isProvidingEmpty() { return "" } return host.originalStr } func (host *parsedHost) asGenericAddressString() *IPAddressString { if host.hasEmbeddedAddress() { addressProvider := host.getAddressProvider() if addressProvider.isProvidingAllAddresses() { return NewIPAddressStringParams(SegmentWildcardStr, addressProvider.getParameters()) } else if addressProvider.isProvidingEmpty() { return NewIPAddressStringParams("", addressProvider.getParameters()) } else { addr, err := addressProvider.getProviderAddress() if err != nil { return NewIPAddressStringParams(host.originalStr, addressProvider.getParameters()) } return addr.ToAddressString() } } return nil } func (host *parsedHost) getHost() string { return host.buildHostString() } func (host *parsedHost) buildHostString() string { if host.parsedHostCache == nil { var hostStr string if host.hasEmbeddedAddress() { addressProvider := host.getAddressProvider() addr, err := addressProvider.getProviderAddress() if err == nil && addr != nil { section := addr.GetSection() //port was stripped out //mask and prefix removed by toNormalizedWildcardString //getSection() removes zone hostStr = section.ToCanonicalWildcardString() } else { hostStr = host.mapString(addressProvider) } } else { var label string normalizedFlags := host.normalizedFlags var hostStrBuilder strings.Builder for i, lastSep := 0, -1; i < len(host.separatorIndices); i++ { index := host.separatorIndices[i] if len(normalizedFlags) > 0 && !normalizedFlags[i] { var normalizedLabelBuilder strings.Builder normalizedLabelBuilder.Grow((index - lastSep) - 1) for j := lastSep + 1; j < index; j++ { c := host.originalStr[j] if c >= 'A' && c <= 'Z' { c = c + ('a' - 'A') } normalizedLabelBuilder.WriteByte(c) } label = normalizedLabelBuilder.String() } else { label = host.originalStr[lastSep+1 : index] } if i > 0 { hostStrBuilder.WriteByte(LabelSeparator) } hostStrBuilder.WriteString(label) lastSep = index } hostStr = hostStrBuilder.String() } return hostStr } return host.parsedHostCache.host } func (host *parsedHost) buildNormalizedLabels() []string { if host.parsedHostCache == nil { var normalizedLabels []string if host.hasEmbeddedAddress() { addressProvider := host.getAddressProvider() addr, err := addressProvider.getProviderAddress() if err == nil && addr != nil { section := addr.GetSection() normalizedLabels = section.GetSegmentStrings() } else { hostStr := host.mapString(addressProvider) if addressProvider.isProvidingEmpty() { normalizedLabels = []string{} } else { normalizedLabels = []string{hostStr} } } } else { normalizedLabels = make([]string, len(host.separatorIndices)) normalizedFlags := host.normalizedFlags for i, lastSep := 0, -1; i < len(normalizedLabels); i++ { index := host.separatorIndices[i] if len(normalizedFlags) > 0 && !normalizedFlags[i] { var normalizedLabelBuilder strings.Builder normalizedLabelBuilder.Grow((index - lastSep) - 1) for j := lastSep + 1; j < index; j++ { c := host.originalStr[j] if c >= 'A' && c <= 'Z' { c = c + ('a' - 'A') } normalizedLabelBuilder.WriteByte(c) } normalizedLabels[i] = normalizedLabelBuilder.String() } else { normalizedLabels[i] = host.originalStr[lastSep+1 : index] } lastSep = index } } return normalizedLabels } return host.parsedHostCache.normalizedLabels } func (host *parsedHost) getNormalizedLabels() []string { return host.buildNormalizedLabels() } func (host *parsedHost) isUNCIPv6Literal() bool { return host.embeddedAddress.isUNCIPv6Literal } func (host *parsedHost) isReverseDNS() bool { return host.embeddedAddress.isReverseDNS } ipaddress-go-1.5.4/ipaddr/parsemacaddr.go000066400000000000000000000237521440250641600203350ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" "sync" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" ) type parsedMACAddress struct { macAddressParseData originator *MACAddressString address *MACAddress params addrstrparam.MACAddressStringParams creationLock *sync.Mutex } func (provider *parsedMACAddress) getParameters() addrstrparam.MACAddressStringParams { return provider.params } func (parseData *parsedMACAddress) getMACAddressParseData() *macAddressParseData { return &parseData.macAddressParseData } func (parseData *parsedMACAddress) getAddress() (*MACAddress, addrerr.IncompatibleAddressError) { var err addrerr.IncompatibleAddressError addr := (*MACAddress)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&parseData.address)))) if addr == nil { parseData.creationLock.Lock() addr = parseData.address if addr == nil { addr, err = parseData.createAddress() if err == nil { parseData.segmentData = nil // no longer needed dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&parseData.address)) atomicStorePointer(dataLoc, unsafe.Pointer(addr)) } } parseData.creationLock.Unlock() } return addr, err } func (parseData *parsedMACAddress) createAddress() (*MACAddress, addrerr.IncompatibleAddressError) { creator := macType.getNetwork().getAddressCreator() sect, err := parseData.createSection() if err != nil { return nil, err } return creator.createAddressInternal(sect.ToSectionBase(), parseData.originator).ToMAC(), nil } func (parseData *parsedMACAddress) createSection() (*MACAddressSection, addrerr.IncompatibleAddressError) { addressString := parseData.str addressParseData := parseData.getAddressParseData() actualInitialSegmentCount := addressParseData.getSegmentCount() creator := macType.getNetwork().getAddressCreator() isMultiple := false var segIsMult bool format := parseData.getFormat() var finalSegmentCount, initialSegmentCount int if format == nil { if parseData.isExtended() { initialSegmentCount = ExtendedUniqueIdentifier64SegmentCount } else { initialSegmentCount = MediaAccessControlSegmentCount } finalSegmentCount = initialSegmentCount } else if format == dotted { if parseData.isExtended() { initialSegmentCount = MediaAccessControlDotted64SegmentCount } else { initialSegmentCount = MediaAccessControlDottedSegmentCount } if actualInitialSegmentCount <= MediaAccessControlDottedSegmentCount && !parseData.isExtended() { finalSegmentCount = MediaAccessControlSegmentCount } else { finalSegmentCount = ExtendedUniqueIdentifier64SegmentCount } } else { if addressParseData.isSingleSegment() || parseData.isDoubleSegment() { if parseData.isExtended() { finalSegmentCount = ExtendedUniqueIdentifier64SegmentCount } else { finalSegmentCount = MediaAccessControlSegmentCount } } else if actualInitialSegmentCount <= MediaAccessControlSegmentCount && !parseData.isExtended() { finalSegmentCount = MediaAccessControlSegmentCount } else { finalSegmentCount = ExtendedUniqueIdentifier64SegmentCount } initialSegmentCount = finalSegmentCount } missingCount := initialSegmentCount - actualInitialSegmentCount expandedSegments := missingCount <= 0 segments := make([]*AddressDivision, finalSegmentCount) for i, normalizedSegmentIndex := 0, 0; i < actualInitialSegmentCount; i++ { lower := addressParseData.getValue(i, keyLower) upper := addressParseData.getValue(i, keyUpper) if format == dotted { //aaa.bbb.ccc.ddd //aabb is becoming aa.bb segLower := SegInt(lower) segUpper := SegInt(upper) lowerHalfLower := segLower >> 8 lowerHalfUpper := segUpper >> 8 adjustedLower2 := segLower & 0xff adjustedUpper2 := segUpper & 0xff if lowerHalfLower != lowerHalfUpper && adjustedUpper2-adjustedLower2 != 0xff { return nil, &incompatibleAddressError{addressError{str: addressString, key: "ipaddress.error.invalid.joined.ranges"}} } segments[normalizedSegmentIndex], segIsMult = createSegment( addressString, lowerHalfLower, lowerHalfUpper, false, addressParseData, i, creator) normalizedSegmentIndex++ isMultiple = isMultiple || segIsMult segments[normalizedSegmentIndex], segIsMult = createSegment( addressString, adjustedLower2, adjustedUpper2, false, addressParseData, i, creator) isMultiple = isMultiple || segIsMult } else { if addressParseData.isSingleSegment() || parseData.isDoubleSegment() { useStringIndicators := true var count int if i == actualInitialSegmentCount-1 { count = missingCount } else { count = MACOrganizationalUniqueIdentifierSegmentCount - 1 } missingCount -= count isRange := lower != upper previousAdjustedWasRange := false for count >= 0 { //add the missing segments var newLower, newUpper uint64 if isRange { segmentMask := uint64(MACMaxValuePerSegment) shift := uint64(count) << macBitsToSegmentBitshift newLower = (lower >> shift) & segmentMask newUpper = (upper >> shift) & segmentMask if previousAdjustedWasRange && newUpper-newLower != MACMaxValuePerSegment { //any range extending into upper segments must have full range in lower segments //otherwise there is no way for us to represent the address //so we need to check whether the lower parts cover the full range //eg cannot represent 0.0.0x100-0x10f or 0.0.1-1ff, but can do 0.0.0x100-0x1ff or 0.0.0-1ff return nil, &incompatibleAddressError{addressError{str: addressString, key: "ipaddress.error.invalid.joined.ranges"}} } previousAdjustedWasRange = newLower != newUpper //we may be able to reuse our strings on the final segment //for previous segments, strings can be reused only when the value is 0, which we do not need to cacheBitCountx. Any other value changes when shifted. if count == 0 && newLower == lower { if newUpper != upper { addressParseData.unsetFlag(i, keyStandardRangeStr) } } else { useStringIndicators = false } } else { newLower = (lower >> uint(count<<3)) & MACMaxValuePerSegment newUpper = newLower if count != 0 || newLower != lower { useStringIndicators = false } } segments[normalizedSegmentIndex], segIsMult = createSegment( addressString, SegInt(newLower), SegInt(newUpper), useStringIndicators, addressParseData, i, creator) isMultiple = isMultiple || segIsMult normalizedSegmentIndex++ count-- } continue } //end joined segments segments[normalizedSegmentIndex], segIsMult = createSegment( addressString, SegInt(lower), SegInt(upper), true, addressParseData, i, creator) isMultiple = isMultiple || segIsMult } if !expandedSegments { //check for any missing segments that we should account for here if addressParseData.isWildcard(i) { expandSegments := true for j := i + 1; j < actualInitialSegmentCount; j++ { if addressParseData.isWildcard(j) { //another wildcard further down expandSegments = false break } } if expandSegments { expandedSegments = true count := missingCount for ; count > 0; count-- { //add the missing segments if format == dotted { seg, _ := createSegment( addressString, 0, MACMaxValuePerSegment, false, addressParseData, i, creator) normalizedSegmentIndex++ segments[normalizedSegmentIndex] = seg normalizedSegmentIndex++ segments[normalizedSegmentIndex] = seg } else { normalizedSegmentIndex++ segments[normalizedSegmentIndex], _ = createSegment( addressString, 0, MACMaxValuePerSegment, false, addressParseData, i, creator) } isMultiple = true } } } } normalizedSegmentIndex++ } return creator.createSectionInternal(segments, isMultiple).ToMAC(), nil } func createSegment( addressString string, val, upperVal SegInt, useFlags bool, parseData *addressParseData, parsedSegIndex int, creator parsedAddressCreator) (div *AddressDivision, isMultiple bool) { if val != upperVal { return createRangeSegment(addressString, val, upperVal, useFlags, parseData, parsedSegIndex, creator), true } var result *AddressDivision if !useFlags { result = creator.createSegment(val, val, nil) } else { result = creator.createSegmentInternal( val, nil, //prefix length addressString, val, parseData.getFlag(parsedSegIndex, keyStandardStr), parseData.getIndex(parsedSegIndex, keyLowerStrStartIndex), parseData.getIndex(parsedSegIndex, keyLowerStrEndIndex)) } return result, false } func createRangeSegment( addressString string, lower, upper SegInt, useFlags bool, parseData *addressParseData, parsedSegIndex int, creator parsedAddressCreator) *AddressDivision { var result *AddressDivision if !useFlags { result = creator.createSegment(lower, upper, nil) } else { result = creator.createRangeSegmentInternal( lower, upper, nil, addressString, lower, upper, parseData.getFlag(parsedSegIndex, keyStandardStr), parseData.getFlag(parsedSegIndex, keyStandardRangeStr), parseData.getIndex(parsedSegIndex, keyLowerStrStartIndex), parseData.getIndex(parsedSegIndex, keyLowerStrEndIndex), parseData.getIndex(parsedSegIndex, keyUpperStrEndIndex)) } return result } ipaddress-go-1.5.4/ipaddr/parsequalifier.go000066400000000000000000000100761440250641600207160ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) type parsedHostIdentifierStringQualifier struct { // if there is a port for the host, this will be its numeric value port Port // non-nil for a host with port service string // non-empty for host with a service instead of a port // if there is a prefix length for the address, this will be its numeric value networkPrefixLength PrefixLen //non-nil for a prefix-only address, sometimes non-nil for IPv4, IPv6 // If instead of a prefix length a mask was provided, this is the mask. // We can also have both a prefix length and mask if one is added when merging qualifiers */' mask *parsedIPAddress // overrides the parsed mask if present mergedMask *IPAddress // this is the IPv6 scope id or network interface name zone Zone isZoned bool } func (parsedQual *parsedHostIdentifierStringQualifier) clearPortOrService() { parsedQual.port = nil parsedQual.service = "" } func (parsedQual *parsedHostIdentifierStringQualifier) clearPrefixOrMask() { parsedQual.networkPrefixLength = nil parsedQual.mask = nil } func (parsedQual *parsedHostIdentifierStringQualifier) merge(other *parsedHostIdentifierStringQualifier) (err addrerr.IncompatibleAddressError) { if parsedQual.networkPrefixLength == nil || (other.networkPrefixLength != nil && other.networkPrefixLength.bitCount() < parsedQual.networkPrefixLength.bitCount()) { parsedQual.networkPrefixLength = other.networkPrefixLength } if parsedQual.mask == nil { parsedQual.mask = other.mask } else { otherMask := other.getMaskLower() if otherMask != nil { parsedQual.mergedMask, err = parsedQual.getMaskLower().Mask(otherMask) } } return } func (parsedQual *parsedHostIdentifierStringQualifier) getMaskLower() *IPAddress { if mask := parsedQual.mergedMask; mask != nil { return mask } if mask := parsedQual.mask; mask != nil { return mask.getValForMask() } return nil } func (parsedQual *parsedHostIdentifierStringQualifier) getNetworkPrefixLen() PrefixLen { return parsedQual.networkPrefixLength } func (parsedQual *parsedHostIdentifierStringQualifier) getEquivalentPrefixLen() PrefixLen { pref := parsedQual.getNetworkPrefixLen() if pref == nil { mask := parsedQual.getMaskLower() if mask != nil { pref = mask.GetBlockMaskPrefixLen(true) } } return pref } // we distinguish callers with empty zones vs callers in which there was no zone indicator func (parsedQual *parsedHostIdentifierStringQualifier) setZone(z *Zone) { if z != nil { parsedQual.zone = *z parsedQual.isZoned = true } } func (parsedQual *parsedHostIdentifierStringQualifier) getZone() Zone { return parsedQual.zone } func (parsedQual *parsedHostIdentifierStringQualifier) getPort() Port { return parsedQual.port } func (parsedQual *parsedHostIdentifierStringQualifier) getService() string { return parsedQual.service } func (parsedQual *parsedHostIdentifierStringQualifier) inferVersion(validationOptions addrstrparam.IPAddressStringParams) IPVersion { if parsedQual.networkPrefixLength != nil { if parsedQual.networkPrefixLength.bitCount() > IPv4BitCount && !validationOptions.GetIPv4Params().AllowsPrefixesBeyondAddressSize() { return IPv6 } } else if mask := parsedQual.mask; mask != nil { if mask.isProvidingIPv6() { return IPv6 } else if mask.isProvidingIPv4() { return IPv4 } } if parsedQual.isZoned { //if parsedQual.zone != "" { return IPv6 } return IndeterminateIPVersion } ipaddress-go-1.5.4/ipaddr/partition.go000066400000000000000000000242761440250641600177220ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math/big" ) // Partition is a collection of items (such as addresses) partitioned from an original item (such as a subnet). // Much like an iterator, the elements of a partition can be iterated just once (using the iterator, using ForEach, or using any other iteration), // after which it becomes empty. type Partition[T any] struct { original, single T hasSingle bool iterator Iterator[T] count *big.Int } // MappedPartition is a mapping from the address types in a [Partition] to values of a generic type V. type MappedPartition[T GenericKeyConstraint[T], V any] map[Key[T]]V // ApplyForEachConditionally supplies to the given function each element of the given partition, // inserting return values into the returned map as directed. When the action returns true as the second return value, // then the other return value is added to the map. func ApplyForEachConditionally[T GenericKeyConstraint[T], V any](part *Partition[T], action func(T) (V, bool)) MappedPartition[T, V] { results := make(map[Key[T]]V) if action != nil && part != nil { part.ForEach(func(addr T) { if result, ok := action(addr); ok { results[addr.ToGenericKey()] = result } }) } return results } // ApplyForEach supplies to the given function each element of the given partition, // inserting return values into the returned map. func ApplyForEach[T GenericKeyConstraint[T], V any](part *Partition[T], action func(T) V) MappedPartition[T, V] { results := make(map[Key[T]]V) if action != nil && part != nil { part.ForEach(func(addr T) { results[addr.ToGenericKey()] = action(addr) }) } return results } var ( _ MappedPartition[*Address, any] = ApplyForEach[*Address, any](nil, nil) _ MappedPartition[*IPAddress, any] = ApplyForEach[*IPAddress, any](nil, nil) _ MappedPartition[*IPv4Address, any] = ApplyForEach[*IPv4Address, any](nil, nil) _ MappedPartition[*IPv6Address, any] = ApplyForEach[*IPv6Address, any](nil, nil) _ MappedPartition[*MACAddress, any] = ApplyForEach[*MACAddress, any](nil, nil) ) // ForEach calls the given action on each partition element. func (part *Partition[T]) ForEach(action func(T)) { if part.iterator == nil { if part.hasSingle { part.hasSingle = false action(part.single) } } else { iterator := part.iterator for iterator.HasNext() { action(iterator.Next()) } part.iterator = nil } } // Iterator provides an iterator to iterate through each element of the partition. func (part *Partition[T]) Iterator() Iterator[T] { if part.iterator == nil { if part.hasSingle { part.hasSingle = false res := &singleIterator[T]{original: part.single} return res } return nil } res := part.iterator part.iterator = nil return res } // PredicateForEach applies the supplied predicate operation to each element of the partition, // returning true if they all return true, false otherwise func (part *Partition[T]) PredicateForEach(predicate func(T) bool) bool { return part.predicateForEach(predicate, false) } // PredicateForEachEarly applies the supplied predicate operation to each element of the partition, // returning false if the given predicate returns false for any of the elements. // // The method returns when one application of the predicate returns false (determining the overall result) func (part *Partition[T]) PredicateForEachEarly(predicate func(T) bool) bool { return part.predicateForEach(predicate, false) } func (part *Partition[T]) predicateForEach(predicate func(T) bool, returnEarly bool) bool { if part.iterator == nil { return predicate(part.single) } result := true iterator := part.iterator for iterator.HasNext() { if !predicate(iterator.Next()) { result = false if returnEarly { break } } } return result } // PredicateForAnyEarly applies the supplied predicate operation to each element of the partition, // returning true if the given predicate returns true for any of the elements. // // The method returns when one application of the predicate returns true (determining the overall result) func (part *Partition[T]) PredicateForAnyEarly(predicate func(T) bool) bool { return part.predicateForAny(predicate, true) } // PredicateForAny applies the supplied predicate operation to each element of the partition, // returning true if the given predicate returns true for any of the elements. func (part *Partition[T]) PredicateForAny(predicate func(T) bool) bool { return part.predicateForAny(predicate, false) } func (part *Partition[T]) predicateForAny(predicate func(address T) bool, returnEarly bool) bool { return !part.predicateForEach(func(addr T) bool { return !predicate(addr) }, returnEarly) } // SpanPartitionConstraint is the generic type constraint for IP subnet spanning partitions. type SpanPartitionConstraint[T any] interface { AddressDivisionSeries PrefixedConstraint[T] SpanWithPrefixBlocks() []T } var ( _ SpanPartitionConstraint[*IPAddress] _ SpanPartitionConstraint[*IPv4Address] _ SpanPartitionConstraint[*IPv6Address] _ SpanPartitionConstraint[*IPAddressSection] _ SpanPartitionConstraint[*IPv4AddressSection] _ SpanPartitionConstraint[*IPv6AddressSection] ) // PartitionWithSpanningBlocks partitions the address series into prefix blocks and single addresses. // // This method iterates through a list of prefix blocks of different sizes that span the entire subnet. func PartitionWithSpanningBlocks[T SpanPartitionConstraint[T]](newAddr T) *Partition[T] { if !newAddr.IsMultiple() { if !newAddr.IsPrefixed() { return &Partition[T]{ original: newAddr, single: newAddr, hasSingle: true, count: bigOneConst(), } } return &Partition[T]{ original: newAddr, single: newAddr.WithoutPrefixLen(), hasSingle: true, count: bigOneConst(), } } else if newAddr.IsSinglePrefixBlock() { return &Partition[T]{ original: newAddr, single: newAddr, hasSingle: true, count: bigOneConst(), } } blocks := newAddr.SpanWithPrefixBlocks() return &Partition[T]{ original: newAddr, iterator: &sliceIterator[T]{blocks}, count: big.NewInt(int64(len(blocks))), } } // PartitionIpv6WithSpanningBlocks partitions the IPv6 address into prefix blocks and single addresses. // // This function is here for backwards compatibility, PartitionWithSpanningBlocks is recommended instead. func PartitionIpv6WithSpanningBlocks(newAddr *IPv6Address) *Partition[*IPv6Address] { return PartitionWithSpanningBlocks(newAddr) } // PartitionIpv4WithSpanningBlocks partitions the IPv4 address into prefix blocks and single addresses. // // This function is here for backwards compatibility, PartitionWithSpanningBlocks is recommended instead. func PartitionIpv4WithSpanningBlocks(newAddr *IPv4Address) *Partition[*IPv4Address] { return PartitionWithSpanningBlocks(newAddr) } // PartitionIPv6WithSingleBlockSize partitions the IPv6 address into prefix blocks and single addresses. // // This function is here for backwards compatibility, PartitionWithSingleBlockSize is recommended instead. func PartitionIPv6WithSingleBlockSize(newAddr *IPv6Address) *Partition[*IPv6Address] { return PartitionWithSingleBlockSize(newAddr) } // PartitionIPv4WithSingleBlockSize partitions the IPv4 address into prefix blocks and single addresses. // // This function is here for backwards compatibility, PartitionWithSingleBlockSize is recommended instead. func PartitionIPv4WithSingleBlockSize(newAddr *IPv4Address) *Partition[*IPv4Address] { return PartitionWithSingleBlockSize(newAddr) } // IteratePartitionConstraint is the generic type constraint for IP subnet and IP section iteration partitions. type IteratePartitionConstraint[T any] interface { AddressDivisionSeries PrefixedConstraint[T] AssignMinPrefixForBlock() T PrefixBlockIterator() Iterator[T] Iterator() Iterator[T] } var ( _ IteratePartitionConstraint[*Address] _ IteratePartitionConstraint[*IPAddress] _ IteratePartitionConstraint[*IPv4Address] _ IteratePartitionConstraint[*IPv6Address] _ IteratePartitionConstraint[*MACAddress] _ IteratePartitionConstraint[*IPAddressSection] _ IteratePartitionConstraint[*IPv4AddressSection] _ IteratePartitionConstraint[*IPv6AddressSection] _ IteratePartitionConstraint[*MACAddressSection] ) // PartitionWithSingleBlockSize partitions the address series into prefix blocks and single addresses. // // This method chooses the maximum block size for a list of prefix blocks contained by the address or subnet, // and then iterates to produce blocks of that size. func PartitionWithSingleBlockSize[T IteratePartitionConstraint[T]](newAddr T) *Partition[T] { if !newAddr.IsMultiple() { if !newAddr.IsPrefixed() { return &Partition[T]{ original: newAddr, single: newAddr, hasSingle: true, count: bigOneConst(), } } return &Partition[T]{ original: newAddr, single: newAddr.WithoutPrefixLen(), hasSingle: true, count: bigOneConst(), } } else if newAddr.IsSinglePrefixBlock() { return &Partition[T]{ original: newAddr, single: newAddr, hasSingle: true, count: bigOneConst(), } } // prefix blocks are handled as prefix blocks, // such as 1.2.*.*, which is handled as prefix block iterator for 1.2.0.0/16, // but 1.2.3-4.5 is handled as iterator with no prefix lengths involved series := newAddr.AssignMinPrefixForBlock() if series.GetPrefixLen().bitCount() != newAddr.GetBitCount() { return &Partition[T]{ original: newAddr, iterator: series.PrefixBlockIterator(), count: series.GetPrefixCountLen(series.GetPrefixLen().bitCount()), } } return &Partition[T]{ original: newAddr, iterator: newAddr.WithoutPrefixLen().Iterator(), count: newAddr.GetCount(), } } // TODO LATER partition ranges (not just addresses) with spanning blocks ipaddress-go-1.5.4/ipaddr/rangeiterator.go000066400000000000000000000034021440250641600205430ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr type sequRangeIterator[T SequentialRangeConstraint[T]] struct { rng *SequentialRange[T] creator func(T, T) *SequentialRange[T] prefixBlockIterator Iterator[T] prefixLength BitCount notFirst bool } func (it *sequRangeIterator[T]) HasNext() bool { return it.prefixBlockIterator.HasNext() } func (it *sequRangeIterator[T]) Next() (res *SequentialRange[T]) { if it.HasNext() { next := it.prefixBlockIterator.Next() if !it.notFirst { it.notFirst = true // next is a prefix block lower := it.rng.GetLower() prefLen := it.prefixLength if it.HasNext() { if !lower.IncludesZeroHostLen(prefLen) { return it.creator(lower, next.GetUpper()) } } else { upper := it.rng.GetUpper() if !lower.IncludesZeroHostLen(prefLen) || !upper.IncludesMaxHostLen(prefLen) { return it.creator(lower, upper) } } } else if !it.HasNext() { upper := it.rng.GetUpper() if !upper.IncludesMaxHostLen(it.prefixLength) { return it.creator(next.GetLower(), upper) } } lower, upper := next.getLowestHighestAddrs() return newSequRangeUnchecked(lower, upper, lower != upper) } return } ipaddress-go-1.5.4/ipaddr/section.go000066400000000000000000003174741440250641600173620ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math/big" "strconv" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) var zeroSection = createSection(zeroDivs, nil, zeroType) func createSection(segments []*AddressDivision, prefixLength PrefixLen, addrType addrType) *AddressSection { sect := &AddressSection{ addressSectionInternal{ addressDivisionGroupingInternal{ addressDivisionGroupingBase: addressDivisionGroupingBase{ divisions: standardDivArray(segments), prefixLength: prefixLength, addrType: addrType, cache: &valueCache{}, }, }, }, } assignStringCache(§.addressDivisionGroupingBase, addrType) return sect } // callers to this function supply segments with prefix length consistent with the supplied prefix length func createSectionMultiple(segments []*AddressDivision, prefixLength PrefixLen, addrType addrType, isMultiple bool) *AddressSection { result := createSection(segments, prefixLength, addrType) result.isMult = isMultiple return result } // callers to this function supply segments with prefix length consistent with the supplied prefix length func createInitializedSection(segments []*AddressDivision, prefixLength PrefixLen, addrType addrType) *AddressSection { result := createSection(segments, prefixLength, addrType) result.initMultiple() // assigns isMultiple return result } // callers to this function supply segments with prefix length consistent with the supplied prefix length func deriveAddressSectionPrefLen(from *AddressSection, segments []*AddressDivision, prefixLength PrefixLen) *AddressSection { result := createSection(segments, prefixLength, from.getAddrType()) result.initMultiple() // assigns isMultiple return result } // callers to this function supply segments with prefix length consistent with the prefix length of this section func deriveAddressSection(from *AddressSection, segments []*AddressDivision) (res *AddressSection) { return deriveAddressSectionPrefLen(from, segments, from.prefixLength) } func assignStringCache(section *addressDivisionGroupingBase, addrType addrType) { stringCache := §ion.cache.stringCache if addrType.isIPv4() { stringCache.ipStringCache = &ipStringCache{} stringCache.ipv4StringCache = &ipv4StringCache{} } else if addrType.isIPv6() { stringCache.ipStringCache = &ipStringCache{} stringCache.ipv6StringCache = &ipv6StringCache{} } else if addrType.isMAC() { stringCache.macStringCache = &macStringCache{} } } ////////////////////////////////////////////////////////////////// // // // type addressSectionInternal struct { addressDivisionGroupingInternal } func (section *addressSectionInternal) initImplicitPrefLen(bitsPerSegment BitCount) { segCount := section.GetSegmentCount() if segCount != 0 { for i := segCount - 1; i >= 0; i-- { segment := section.GetSegment(i) minPref := segment.GetMinPrefixLenForBlock() if minPref > 0 { if minPref != bitsPerSegment || i != segCount-1 { section.prefixLength = getNetworkPrefixLen(bitsPerSegment, minPref, i) } return } } section.prefixLength = cacheBitCount(0) } } func (section *addressSectionInternal) initMultAndImplicitPrefLen(bitsPerSegment BitCount) { segCount := section.GetSegmentCount() if segCount != 0 { isMultiple := false isBlock := true for i := segCount - 1; i >= 0; i-- { segment := section.GetSegment(i) if isBlock { minPref := segment.GetMinPrefixLenForBlock() if minPref > 0 { if minPref != bitsPerSegment || i != segCount-1 { section.prefixLength = getNetworkPrefixLen(bitsPerSegment, minPref, i) } isBlock = false if isMultiple { // nothing left to do return } } } if !isMultiple && segment.isMultiple() { isMultiple = true section.isMult = true if !isBlock { // nothing left to do return } } } if isBlock { section.prefixLength = cacheBitCount(0) } } } func createDivisionsFromSegs( segProvider func(index int) *IPAddressSegment, segCount int, bitsToSegmentShift uint, bitsPerSegment BitCount, bytesPerSegment int, maxValuePerSegment SegInt, zeroSeg, zeroSegZeroPrefix, zeroSegPrefixBlock *IPAddressSegment, assignedPrefLen PrefixLen) (divs []*AddressDivision, newPref PrefixLen, isMultiple bool) { divs = make([]*AddressDivision, segCount) prefixedSegment := -1 if assignedPrefLen != nil { p := assignedPrefLen.bitCount() if p < 0 { p = 0 assignedPrefLen = cacheBitCount(p) } else { boundaryBits := BitCount(segCount << bitsToSegmentShift) if p > boundaryBits { p = boundaryBits assignedPrefLen = cacheBitCount(p) } } prefixedSegment = getNetworkSegmentIndex(p, bytesPerSegment, bitsPerSegment) } var previousSegPrefixed bool var lastSegment *IPAddressSegment for i := 0; i < segCount; i++ { segment := segProvider(i) if segment == nil { if previousSegPrefixed { divs[i] = zeroSegZeroPrefix.ToDiv() } else if i == prefixedSegment { newPref = cachePrefixLen(assignedPrefLen) segPref := getPrefixedSegmentPrefixLength(bitsPerSegment, assignedPrefLen.bitCount(), prefixedSegment) if i+1 < segCount && isPrefixSubnet( func(segmentIndex int) SegInt { seg := segProvider(segmentIndex + i + 1) if seg == nil { return 0 } return seg.GetSegmentValue() }, func(segmentIndex int) SegInt { seg := segProvider(segmentIndex + i + 1) if seg == nil { return 0 } return seg.GetUpperSegmentValue() }, segCount-(i+1), bytesPerSegment, bitsPerSegment, maxValuePerSegment, 0, zerosOnly) { divs[i] = zeroSeg.toPrefixedNetworkDivision(segPref) i++ isMultiple = isMultiple || i < len(divs) || segPref.bitCount() < bitsPerSegment for ; i < len(divs); i++ { divs[i] = zeroSegPrefixBlock.ToDiv() } break } else { divs[i] = zeroSeg.toPrefixedNetworkDivision(segPref) } } else { divs[i] = zeroSeg.ToDiv() // nil segs are just zero } } else { // The final prefix length is the minimum amongst the assigned one and all of the segments' own prefixes segPrefix := segment.getDivisionPrefixLength() segIsPrefixed := segPrefix != nil if previousSegPrefixed { if !segIsPrefixed || segPrefix.bitCount() != 0 { divs[i] = createAddressDivision( segment.derivePrefixed(cacheBitCount(0))) // change seg prefix to 0 } else { divs[i] = segment.ToDiv() // seg prefix is already 0 } } else { // if a prefix length was supplied, we must check for prefix subnets var segPrefixSwitch bool var assignedSegPref PrefixLen if i == prefixedSegment || (prefixedSegment > 0 && segIsPrefixed) { // there exists an assigned prefix length assignedSegPref = getPrefixedSegmentPrefixLength(bitsPerSegment, assignedPrefLen.bitCount(), i) if segIsPrefixed { if assignedSegPref == nil || segPrefix.bitCount() < assignedSegPref.bitCount() { if segPrefix.bitCount() == 0 && i > 0 { // normalize boundaries by looking back if !lastSegment.IsPrefixed() { divs[i-1] = createAddressDivision( lastSegment.derivePrefixed(cacheBitCount(bitsPerSegment))) } } newPref = getNetworkPrefixLen(bitsPerSegment, segPrefix.bitCount(), i) } else { newPref = cachePrefixLen(assignedPrefLen) segPrefixSwitch = assignedSegPref.bitCount() < segPrefix.bitCount() } } else { newPref = cachePrefixLen(assignedPrefLen) segPrefixSwitch = true } if isPrefixSubnet( func(segmentIndex int) SegInt { seg := segProvider(segmentIndex) if seg == nil { return 0 } return seg.GetSegmentValue() }, func(segmentIndex int) SegInt { seg := segProvider(segmentIndex) if seg == nil { return 0 } return seg.GetUpperSegmentValue() }, segCount, bytesPerSegment, bitsPerSegment, maxValuePerSegment, newPref.bitCount(), zerosOnly) { divs[i] = segment.toPrefixedNetworkDivision(assignedSegPref) i++ isMultiple = isMultiple || i < len(divs) || newPref.bitCount() < bitsPerSegment for ; i < len(divs); i++ { divs[i] = zeroSegPrefixBlock.ToDiv() } break } previousSegPrefixed = true } else if segIsPrefixed { if segPrefix.bitCount() == 0 && i > 0 { // normalize boundaries by looking back if !lastSegment.IsPrefixed() { divs[i-1] = createAddressDivision(lastSegment.derivePrefixed(cacheBitCount(bitsPerSegment))) } } newPref = getNetworkPrefixLen(bitsPerSegment, segPrefix.bitCount(), i) previousSegPrefixed = true } if segPrefixSwitch { divs[i] = createAddressDivision(segment.derivePrefixed(assignedSegPref)) // change seg prefix } else { divs[i] = segment.ToDiv() } } isMultiple = isMultiple || segment.isMultiple() } lastSegment = segment } return } func (section *addressSectionInternal) matchesTypeAndCount(other *AddressSection) (matches bool, count int) { count = section.GetDivisionCount() if count != other.GetDivisionCount() { return } else if section.getAddrType() != other.getAddrType() { return } matches = true return } func (section *addressSectionInternal) equal(otherT AddressSectionType) bool { if otherT == nil { return false } other := otherT.ToSectionBase() if other == nil { return false } matchesStructure, _ := section.matchesTypeAndCount(other) return matchesStructure && section.sameCountTypeEquals(other) } func (section *addressSectionInternal) sameCountTypeEquals(other *AddressSection) bool { count := section.GetSegmentCount() for i := count - 1; i >= 0; i-- { if !section.GetSegment(i).sameTypeEquals(other.GetSegment(i)) { return false } } return true } func (section *addressSectionInternal) sameCountTypeContains(other *AddressSection) bool { count := section.GetSegmentCount() for i := count - 1; i >= 0; i-- { if !section.GetSegment(i).sameTypeContains(other.GetSegment(i)) { return false } } return true } // GetBitsPerSegment returns the number of bits comprising each segment in this section. Segments in the same address section are equal length. func (section *addressSectionInternal) GetBitsPerSegment() BitCount { addrType := section.getAddrType() if addrType.isIPv4() { return IPv4BitsPerSegment } else if addrType.isIPv6() { return IPv6BitsPerSegment } else if addrType.isMAC() { return MACBitsPerSegment } if section.GetDivisionCount() == 0 { return 0 } return section.getDivision(0).GetBitCount() } // GetBytesPerSegment returns the number of bytes comprising each segment in this section. Segments in the same address section are equal length. func (section *addressSectionInternal) GetBytesPerSegment() int { addrType := section.getAddrType() if addrType.isIPv4() { return IPv4BytesPerSegment } else if addrType.isIPv6() { return IPv6BytesPerSegment } else if addrType.isMAC() { return MACBytesPerSegment } if section.GetDivisionCount() == 0 { return 0 } return section.getDivision(0).GetByteCount() } // GetSegment returns the segment at the given index. // The first segment is at index 0. // GetSegment will panic given a negative index or an index matching or larger than the segment count. func (section *addressSectionInternal) GetSegment(index int) *AddressSegment { return section.getDivision(index).ToSegmentBase() } // GetGenericSegment returns the segment as an AddressSegmentType, // allowing all segment types to be represented by a single type. // The first segment is at index 0. // GetGenericSegment will panic given a negative index or an index matching or larger than the segment count. func (section *addressSectionInternal) GetGenericSegment(index int) AddressSegmentType { return section.GetSegment(index) } // GetSegmentCount returns the segment count. func (section *addressSectionInternal) GetSegmentCount() int { return section.GetDivisionCount() } // ForEachSegment visits each segment in order from most-significant to least, the most significant with index 0, calling the given function for each, terminating early if the function returns true. // Returns the number of visited segments. func (section *addressSectionInternal) ForEachSegment(consumer func(segmentIndex int, segment *AddressSegment) (stop bool)) int { divArray := section.getDivArray() if divArray != nil { for i, div := range divArray { if consumer(i, div.ToSegmentBase()) { return i + 1 } } } return len(divArray) } // GetBitCount returns the number of bits in each value comprising this address item. func (section *addressSectionInternal) GetBitCount() BitCount { divLen := section.GetDivisionCount() if divLen == 0 { return 0 } return getSegmentsBitCount(section.getDivision(0).GetBitCount(), section.GetSegmentCount()) } // GetByteCount returns the number of bytes required for each value comprising this address item. func (section *addressSectionInternal) GetByteCount() int { return int((section.GetBitCount() + 7) >> 3) } // GetMaxSegmentValue returns the maximum possible segment value for this type of address. // // Note this is not the maximum of the range of segment values in this specific address, // this is the maximum value of any segment for this address type and version, determined by the number of bits per segment. func (section *addressSectionInternal) GetMaxSegmentValue() SegInt { addrType := section.getAddrType() if addrType.isIPv4() { return IPv4MaxValuePerSegment } else if addrType.isIPv6() { return IPv6MaxValuePerSegment } else if addrType.isMAC() { return MACMaxValuePerSegment } divLen := section.GetDivisionCount() if divLen == 0 { return 0 } return section.GetSegment(0).GetMaxValue() } // TestBit returns true if the bit in the lower value of this section at the given index is 1, where index 0 refers to the least significant bit. // In other words, it computes (bits & (1 << n)) != 0), using the lower value of this section. // TestBit will panic if n < 0, or if it matches or exceeds the bit count of this item. func (section *addressSectionInternal) TestBit(n BitCount) bool { return section.IsOneBit(section.GetBitCount() - (n + 1)) } // IsOneBit returns true if the bit in the lower value of this section at the given index is 1, where index 0 refers to the most significant bit. // IsOneBit will panic if bitIndex is less than zero, or if it is larger than the bit count of this item. func (section *addressSectionInternal) IsOneBit(prefixBitIndex BitCount) bool { bitsPerSegment := section.GetBitsPerSegment() bytesPerSegment := section.GetBytesPerSegment() segment := section.GetSegment(getHostSegmentIndex(prefixBitIndex, bytesPerSegment, bitsPerSegment)) segmentBitIndex := prefixBitIndex % bitsPerSegment return segment.IsOneBit(segmentBitIndex) } // Gets the subsection from the series starting from the given index and ending just before the give endIndex. // The first segment is at index 0. func (section *addressSectionInternal) getSubSection(index, endIndex int) *AddressSection { if index < 0 { index = 0 } thisSegmentCount := section.GetSegmentCount() if endIndex > thisSegmentCount { endIndex = thisSegmentCount } segmentCount := endIndex - index if segmentCount <= 0 { if thisSegmentCount == 0 { return section.toAddressSection() } // we do not want an inconsistency where mac zero length can have prefix len zero while ip sections cannot return zeroSection } if index == 0 && endIndex == thisSegmentCount { return section.toAddressSection() } segs := section.getSubDivisions(index, endIndex) newPrefLen := section.getPrefixLen() if newPrefLen != nil { newPrefLen = getAdjustedPrefixLength(section.GetBitsPerSegment(), newPrefLen.bitCount(), index, endIndex) } addrType := section.getAddrType() if !section.isMultiple() { return createSection(segs, newPrefLen, addrType) } return deriveAddressSectionPrefLen(section.toAddressSection(), segs, newPrefLen) } func (section *addressSectionInternal) getLowestHighestSections() (lower, upper *AddressSection) { if !section.isMultiple() { lower = section.toAddressSection() upper = lower return } cache := section.cache if cache == nil { return section.createLowestHighestSections() } cached := (*groupingCache)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&cache.sectionCache)))) if cached == nil { cached = &groupingCache{} cached.lower, cached.upper = section.createLowestHighestSections() dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&cache.sectionCache)) atomicStorePointer(dataLoc, unsafe.Pointer(cached)) } lower = cached.lower upper = cached.upper return } func (section *addressSectionInternal) createLowestHighestSections() (lower, upper *AddressSection) { segmentCount := section.GetSegmentCount() lowSegs := createSegmentArray(segmentCount) var highSegs []*AddressDivision if section.isMultiple() { highSegs = createSegmentArray(segmentCount) } for i := 0; i < segmentCount; i++ { seg := section.GetSegment(i) lowSegs[i] = seg.GetLower().ToDiv() if highSegs != nil { highSegs[i] = seg.GetUpper().ToDiv() } } lower = deriveAddressSection(section.toAddressSection(), lowSegs) if highSegs == nil { upper = lower } else { upper = deriveAddressSection(section.toAddressSection(), highSegs) } return } // Returns the address created by converting this address to an address with a 0 as the first bit following the prefix, followed by all ones to the end, and with the prefix length then removed // Returns the same address if it has no prefix length. func (section *addressSectionInternal) toMaxLower() *AddressSection { return section.toAboveOrBelow(false) } // Returns the address created by converting this address to an address with a 1 as the first bit following the prefix, followed by all zeros to the end, and with the prefix length then removed // Returns the same address if it has no prefix length func (section *addressSectionInternal) toMinUpper() *AddressSection { return section.toAboveOrBelow(true) } func (section *addressSectionInternal) toAboveOrBelow(above bool) *AddressSection { prefLen := section.GetPrefixLen() if prefLen == nil { return section.toAddressSection() } prefBits := prefLen.Len() segmentCount := section.GetSegmentCount() if prefBits == section.GetBitCount() || segmentCount == 0 { return section.withoutPrefixLen() } segmentByteCount := section.GetBytesPerSegment() segmentBitCount := section.GetBitsPerSegment() newSegs := createSegmentArray(segmentCount) if prefBits > 0 { networkSegmentIndex := getNetworkSegmentIndex(prefBits, segmentByteCount, segmentBitCount) section.copySubDivisions(0, networkSegmentIndex, newSegs) } hostSegmentIndex := getHostSegmentIndex(prefBits, segmentByteCount, segmentBitCount) if hostSegmentIndex < segmentCount { oldSeg := section.getDivision(hostSegmentIndex) oldVal := oldSeg.getUpperSegmentValue() segPrefBits := getPrefixedSegmentPrefixLength(segmentBitCount, prefBits, hostSegmentIndex).bitCount() // 1 bit followed by zeros allOnes := ^SegInt(0) var newVal SegInt if above { hostBits := uint(segmentBitCount - segPrefBits) networkMask := allOnes << (hostBits - 1) hostMask := ^(allOnes << hostBits) newVal = (oldVal | hostMask) & networkMask } else { hostBits := uint(segmentBitCount - segPrefBits) networkMask := allOnes << hostBits hostMask := ^(allOnes<> 1 i := 0 isSame := !section.isPrefixed() //when reversing, the prefix must go for j := count - 1; i < halfCount; i, j = i+1, j-1 { var newj, newi *AddressSegment if newj, err = segProducer(i); err != nil { return } if newi, err = segProducer(j); err != nil { return } origi := section.GetSegment(i) origj := section.GetSegment(j) newSegs[j] = newj.ToDiv() newSegs[i] = newi.ToDiv() if isSame && !(segValsSame(newi.getSegmentValue(), origi.getSegmentValue(), newi.getUpperSegmentValue(), origi.getUpperSegmentValue()) && segValsSame(newj.getSegmentValue(), origj.getSegmentValue(), newj.getUpperSegmentValue(), origj.getUpperSegmentValue())) { isSame = false } } if (count & 1) == 1 { //the count is odd, handle the middle one seg := section.getDivision(i) newSegs[i] = seg // gets segment i without prefix length } if isSame { res = section.toAddressSection() return } res = deriveAddressSectionPrefLen(section.toAddressSection(), newSegs, nil) return } func (section *addressSectionInternal) reverseBits(perByte bool) (res *AddressSection, err addrerr.IncompatibleAddressError) { if perByte { isSame := !section.isPrefixed() //when reversing, the prefix must go count := section.GetSegmentCount() newSegs := createSegmentArray(count) for i := 0; i < count; i++ { seg := section.GetSegment(i) var reversedSeg *AddressSegment reversedSeg, err = seg.ReverseBits(perByte) if err != nil { return } newSegs[i] = reversedSeg.ToDiv() if isSame && !segValsSame(seg.getSegmentValue(), reversedSeg.getSegmentValue(), seg.getUpperSegmentValue(), reversedSeg.getUpperSegmentValue()) { isSame = false } } if isSame { res = section.toAddressSection() //We can do this because for ipv6 startIndex stays the same and for mac startIndex and extended stays the same return } res = deriveAddressSectionPrefLen(section.toAddressSection(), newSegs, nil) return } return section.reverseSegments( func(i int) (*AddressSegment, addrerr.IncompatibleAddressError) { return section.GetSegment(i).ReverseBits(perByte) }, ) } func (section *addressSectionInternal) reverseBytes(perSegment bool) (res *AddressSection, err addrerr.IncompatibleAddressError) { if perSegment { isSame := !section.isPrefixed() //when reversing, the prefix must go count := section.GetSegmentCount() newSegs := createSegmentArray(count) for i := 0; i < count; i++ { seg := section.GetSegment(i) var reversedSeg *AddressSegment reversedSeg, err = seg.ReverseBytes() if err != nil { return } newSegs[i] = reversedSeg.ToDiv() if isSame && !segValsSame(seg.getSegmentValue(), reversedSeg.getSegmentValue(), seg.getUpperSegmentValue(), reversedSeg.getUpperSegmentValue()) { isSame = false } } if isSame { res = section.toAddressSection() //We can do this because for ipv6 startIndex stays the same and for mac startIndex and extended stays the same return } res = deriveAddressSectionPrefLen(section.toAddressSection(), newSegs, nil) return } return section.reverseSegments( func(i int) (*AddressSegment, addrerr.IncompatibleAddressError) { return section.GetSegment(i).ReverseBytes() }, ) } // callers to replace have ensures the component sections have consistent prefix lengths for the replacement func (section *addressSectionInternal) replace( index, endIndex int, replacement *AddressSection, replacementStartIndex, replacementEndIndex int, prefixLen PrefixLen) *AddressSection { otherSegmentCount := replacementEndIndex - replacementStartIndex segmentCount := section.GetSegmentCount() totalSegmentCount := segmentCount + otherSegmentCount - (endIndex - index) segs := createSegmentArray(totalSegmentCount) sect := section.toAddressSection() sect.copySubDivisions(0, index, segs) if index < totalSegmentCount { replacement.copySubDivisions(replacementStartIndex, replacementEndIndex, segs[index:]) if index+otherSegmentCount < totalSegmentCount { sect.copySubDivisions(endIndex, segmentCount, segs[index+otherSegmentCount:]) } } addrType := sect.getAddrType() if addrType.isZeroSegments() { // zero-length section addrType = replacement.getAddrType() } return createInitializedSection(segs, prefixLen, addrType) } // Replaces segments starting from startIndex and ending before endIndex with the segments starting at replacementStartIndex and // ending before replacementEndIndex from the replacement section. func (section *addressSectionInternal) replaceLen(startIndex, endIndex int, replacement *AddressSection, replacementStartIndex, replacementEndIndex int, segmentToBitsShift uint) *AddressSection { segmentCount := section.GetSegmentCount() startIndex, endIndex, replacementStartIndex, replacementEndIndex = adjustIndices(startIndex, endIndex, segmentCount, replacementStartIndex, replacementEndIndex, replacement.GetSegmentCount()) replacedCount := endIndex - startIndex replacementCount := replacementEndIndex - replacementStartIndex // unlike ipvx, sections of zero length with 0 prefix are still considered to be applying their prefix during replacement, // because you can have zero length prefixes when there are no bits in the section prefixLength := section.getPrefixLen() if replacementCount == 0 && replacedCount == 0 { if prefixLength != nil { prefLen := prefixLength.bitCount() if prefLen <= BitCount(startIndex< BitCount(replacementStartIndex< BitCount(replacementStartIndex< 0 { return replacement } else { replacementPrefisLength := replacement.getPrefixLen() if replacementPrefisLength != nil && replacementPrefisLength.bitCount() == 0 { // prefix length is 0 return replacement } } } startBits := BitCount(startIndex << segmentToBitsShift) var newPrefixLength PrefixLen if prefixLength != nil && prefixLength.bitCount() <= startBits { newPrefixLength = prefixLength } else { replacementPrefLen := replacement.getPrefixLen() if replacementPrefLen != nil && replacementPrefLen.bitCount() <= BitCount(replacementEndIndex< replacementStartBits { replacementPrefixLen = replacementPrefLen.bitCount() - replacementStartBits } newPrefixLength = cacheBitCount(startBits + replacementPrefixLen) } else if prefixLength != nil { replacementBits := BitCount(replacementCount << segmentToBitsShift) var endPrefixBits BitCount endIndexBits := BitCount(endIndex << segmentToBitsShift) if prefixLength.bitCount() > endIndexBits { endPrefixBits = prefixLength.bitCount() - endIndexBits } newPrefixLength = cacheBitCount(startBits + replacementBits + endPrefixBits) } else { newPrefixLength = nil } } result := section.replace(startIndex, endIndex, replacement, replacementStartIndex, replacementEndIndex, newPrefixLength) return result } func (section *addressSectionInternal) toPrefixBlock() *AddressSection { prefixLength := section.getPrefixLen() if prefixLength == nil { return section.toAddressSection() } return section.toPrefixBlockLen(prefixLength.bitCount()) } func (section *addressSectionInternal) toPrefixBlockLen(prefLen BitCount) *AddressSection { prefLen = checkSubnet(section, prefLen) segCount := section.GetSegmentCount() if segCount == 0 { return section.toAddressSection() } segmentByteCount := section.GetBytesPerSegment() segmentBitCount := section.GetBitsPerSegment() existingPrefixLength := section.getPrefixLen() prefixMatches := existingPrefixLength != nil && existingPrefixLength.bitCount() == prefLen if prefixMatches { prefixedSegmentIndex := getHostSegmentIndex(prefLen, segmentByteCount, segmentBitCount) if prefixedSegmentIndex >= segCount { return section.toAddressSection() } segPrefLength := getPrefixedSegmentPrefixLength(segmentBitCount, prefLen, prefixedSegmentIndex).bitCount() seg := section.GetSegment(prefixedSegmentIndex) if seg.containsPrefixBlock(segPrefLength) { i := prefixedSegmentIndex + 1 for ; i < segCount; i++ { seg = section.GetSegment(i) if !seg.IsFullRange() { break } } if i == segCount { return section.toAddressSection() } } } prefixedSegmentIndex := 0 newSegs := createSegmentArray(segCount) if prefLen > 0 { prefixedSegmentIndex = getNetworkSegmentIndex(prefLen, segmentByteCount, segmentBitCount) section.copySubDivisions(0, prefixedSegmentIndex, newSegs) } for i := prefixedSegmentIndex; i < segCount; i++ { segPrefLength := getPrefixedSegmentPrefixLength(segmentBitCount, prefLen, i) oldSeg := section.getDivision(i) newSegs[i] = oldSeg.toPrefixedNetworkDivision(segPrefLength) } return createSectionMultiple(newSegs, cacheBitCount(prefLen), section.getAddrType(), section.isMultiple() || prefLen < section.GetBitCount()) } func (section *addressSectionInternal) toBlock(segmentIndex int, lower, upper SegInt) *AddressSection { segCount := section.GetSegmentCount() i := segmentIndex if i < 0 { i = 0 } maxSegVal := section.GetMaxSegmentValue() for ; i < segCount; i++ { seg := section.GetSegment(segmentIndex) var lowerVal, upperVal SegInt if i == segmentIndex { lowerVal, upperVal = lower, upper } else { upperVal = maxSegVal } if !segsSame(nil, seg.getDivisionPrefixLength(), lowerVal, seg.GetSegmentValue(), upperVal, seg.GetUpperSegmentValue()) { newSegs := createSegmentArray(segCount) section.copySubDivisions(0, i, newSegs) newSeg := createAddressDivision(seg.deriveNewMultiSeg(lowerVal, upperVal, nil)) newSegs[i] = newSeg var allSeg *AddressDivision if j := i + 1; j < segCount { if i == segmentIndex { allSeg = createAddressDivision(seg.deriveNewMultiSeg(0, maxSegVal, nil)) } else { allSeg = newSeg } newSegs[j] = allSeg for j++; j < segCount; j++ { newSegs[j] = allSeg } } return createSectionMultiple(newSegs, nil, section.getAddrType(), segmentIndex < segCount-1 || lower != upper) } } return section.toAddressSection() } func (section *addressSectionInternal) withoutPrefixLen() *AddressSection { if !section.isPrefixed() { return section.toAddressSection() } if sect := section.toIPAddressSection(); sect != nil { return sect.withoutPrefixLen().ToSectionBase() } return createSectionMultiple(section.getDivisionsInternal(), nil, section.getAddrType(), section.isMultiple()) } func (section *addressSectionInternal) getAdjustedPrefix(adjustment BitCount) BitCount { prefix := section.getPrefixLen() bitCount := section.GetBitCount() var result BitCount if prefix == nil { if adjustment > 0 { // start from 0 if adjustment > bitCount { result = bitCount } else { result = adjustment } } else { // start from end if -adjustment < bitCount { result = bitCount + adjustment } } } else { result = prefix.bitCount() + adjustment if result > bitCount { result = bitCount } else if result < 0 { result = 0 } } return result } func (section *addressSectionInternal) adjustPrefixLen(adjustment BitCount) *AddressSection { // no zeroing res, _ := section.adjustPrefixLength(adjustment, false) return res } func (section *addressSectionInternal) adjustPrefixLenZeroed(adjustment BitCount) (*AddressSection, addrerr.IncompatibleAddressError) { return section.adjustPrefixLength(adjustment, true) } func (section *addressSectionInternal) adjustPrefixLength(adjustment BitCount, withZeros bool) (*AddressSection, addrerr.IncompatibleAddressError) { if adjustment == 0 && section.isPrefixed() { return section.toAddressSection(), nil } prefix := section.getAdjustedPrefix(adjustment) return section.setPrefixLength(prefix, withZeros) } func (section *addressSectionInternal) setPrefixLen(prefixLen BitCount) *AddressSection { // no zeroing res, _ := section.setPrefixLength(prefixLen, false) return res } func (section *addressSectionInternal) setPrefixLenZeroed(prefixLen BitCount) (*AddressSection, addrerr.IncompatibleAddressError) { return section.setPrefixLength(prefixLen, true) } func (section *addressSectionInternal) setPrefixLength( networkPrefixLength BitCount, withZeros bool, ) (res *AddressSection, err addrerr.IncompatibleAddressError) { existingPrefixLength := section.getPrefixLen() if existingPrefixLength != nil && networkPrefixLength == existingPrefixLength.bitCount() { res = section.toAddressSection() return } segmentCount := section.GetSegmentCount() var appliedPrefixLen PrefixLen // purposely nil when there are no segments verifyMask := false var startIndex int var segmentMaskProducer func(int) SegInt if segmentCount != 0 { maxVal := section.GetMaxSegmentValue() appliedPrefixLen = cacheBitCount(networkPrefixLength) var minPrefIndex, maxPrefIndex int var minPrefLen, maxPrefLen BitCount bitsPerSegment := section.GetBitsPerSegment() bytesPerSegment := section.GetBytesPerSegment() prefIndex := getNetworkSegmentIndex(networkPrefixLength, bytesPerSegment, bitsPerSegment) if existingPrefixLength != nil { verifyMask = true existingPrefLen := existingPrefixLength.bitCount() existingPrefIndex := getNetworkSegmentIndex(existingPrefLen, bytesPerSegment, bitsPerSegment) // can be -1 if existingPrefLen is 0 if prefIndex > existingPrefIndex { maxPrefIndex = prefIndex minPrefIndex = existingPrefIndex } else { maxPrefIndex = existingPrefIndex minPrefIndex = prefIndex } if withZeros { if networkPrefixLength < existingPrefLen { minPrefLen = networkPrefixLength maxPrefLen = existingPrefLen } else { minPrefLen = existingPrefLen maxPrefLen = networkPrefixLength } startIndex = minPrefIndex segmentMaskProducer = func(i int) SegInt { if i >= minPrefIndex { if i <= maxPrefIndex { minSegPrefLen := getPrefixedSegmentPrefixLength(bitsPerSegment, minPrefLen, i).bitCount() minMask := maxVal << uint(bitsPerSegment-minSegPrefLen) maxSegPrefLen := getPrefixedSegmentPrefixLength(bitsPerSegment, maxPrefLen, i) if maxSegPrefLen != nil { maxMask := maxVal << uint(bitsPerSegment-maxSegPrefLen.bitCount()) return minMask | ^maxMask } return minMask } } return maxVal } } else { startIndex = minPrefIndex } } else { startIndex = prefIndex } if segmentMaskProducer == nil { segmentMaskProducer = func(i int) SegInt { return maxVal } } } if startIndex < 0 { startIndex = 0 } return section.getSubnetSegments( startIndex, appliedPrefixLen, verifyMask, func(i int) *AddressDivision { return section.getDivision(i) }, segmentMaskProducer, ) } func (section *addressSectionInternal) assignPrefixForSingleBlock() *AddressSection { newPrefix := section.GetPrefixLenForSingleBlock() if newPrefix == nil { return nil } newSect := section.setPrefixLen(newPrefix.bitCount()) cache := newSect.cache if cache != nil { // no atomic writes required since we created this new section in here cache.isSinglePrefixBlock = &trueVal cache.equivalentPrefix = cachePrefix(newPrefix.bitCount()) cache.minPrefix = newPrefix } return newSect } // Constructs an equivalent address section with the smallest CIDR prefix possible (largest network), // such that the range of values are a set of subnet blocks for that prefix. func (section *addressSectionInternal) assignMinPrefixForBlock() *AddressSection { return section.setPrefixLen(section.GetMinPrefixLenForBlock()) } // PrefixEqual determines if the given section matches this section up to the prefix length of this section. // It returns whether the argument section has the same address section prefix values as this. // // All prefix bits of this section must be present in the other section to be comparable, otherwise false is returned. func (section *addressSectionInternal) PrefixEqual(other AddressSectionType) (res bool) { o := other.ToSectionBase() if section.toAddressSection() == o { return true } else if section.getAddrType() != o.getAddrType() { return } return section.prefixContains(o, false) } // PrefixContains returns whether the prefix values in the given address section // are prefix values in this address section, using the prefix length of this section. // If this address section has no prefix length, the entire address is compared. // // It returns whether the prefix of this address contains all values of the same prefix length in the given address. // // All prefix bits of this section must be present in the other section to be comparable. func (section *addressSectionInternal) PrefixContains(other AddressSectionType) (res bool) { o := other.ToSectionBase() if section.toAddressSection() == o { return true } else if section.getAddrType() != o.getAddrType() { return } return section.prefixContains(o, true) } func (section *addressSectionInternal) prefixContains(other *AddressSection, contains bool) (res bool) { prefixLength := section.getPrefixLen() var prefixedSection int if prefixLength == nil { prefixedSection = section.GetSegmentCount() if prefixedSection > other.GetSegmentCount() { return } } else { prefLen := prefixLength.bitCount() prefixedSection = getNetworkSegmentIndex(prefLen, section.GetBytesPerSegment(), section.GetBitsPerSegment()) if prefixedSection >= 0 { if prefixedSection >= other.GetSegmentCount() { return } one := section.GetSegment(prefixedSection) two := other.GetSegment(prefixedSection) segPrefixLength := getPrefixedSegmentPrefixLength(one.getBitCount(), prefLen, prefixedSection) if contains { if !one.PrefixContains(two, segPrefixLength.bitCount()) { return } } else { if !one.PrefixEqual(two, segPrefixLength.bitCount()) { return } } } } for prefixedSection--; prefixedSection >= 0; prefixedSection-- { one := section.GetSegment(prefixedSection) two := other.GetSegment(prefixedSection) if contains { if !one.Contains(two) { return } } else { if !one.equalsSegment(two) { return } } } return true } func (section *addressSectionInternal) contains(other AddressSectionType) bool { if other == nil { return true } otherSection := other.ToSectionBase() if section.toAddressSection() == otherSection || otherSection == nil { return true } //check if they are comparable first matches, count := section.matchesTypeAndCount(otherSection) if !matches { return false } else { for i := count - 1; i >= 0; i-- { if !section.GetSegment(i).sameTypeContains(otherSection.GetSegment(i)) { return false } } } return true } func (section *addressSectionInternal) getStringCache() *stringCache { if section.hasNoDivisions() { return &zeroStringCache } cache := section.cache if cache == nil { return nil } return &cache.stringCache } func (section *addressSectionInternal) getLower() *AddressSection { lower, _ := section.getLowestHighestSections() return lower } func (section *addressSectionInternal) getUpper() *AddressSection { _, upper := section.getLowestHighestSections() return upper } func (section *addressSectionInternal) incrementBoundary(increment int64) *AddressSection { if increment <= 0 { if increment == 0 { return section.toAddressSection() } return section.getLower().increment(increment) } return section.getUpper().increment(increment) } func (section *addressSectionInternal) increment(increment int64) *AddressSection { if sect := section.toIPv4AddressSection(); sect != nil { return sect.Increment(increment).ToSectionBase() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.Increment(increment).ToSectionBase() } else if sect := section.toMACAddressSection(); sect != nil { return sect.Increment(increment).ToSectionBase() } return nil } var ( otherOctalPrefix = "0o" otherHexPrefix = "0X" //decimalParams = new(StringOptionsBuilder).SetRadix(10).SetExpandedSegments(true).ToOptions() hexParams = new(addrstr.StringOptionsBuilder).SetRadix(16).SetHasSeparator(false).SetExpandedSegments(true).ToOptions() hexUppercaseParams = new(addrstr.StringOptionsBuilder).SetRadix(16).SetHasSeparator(false).SetExpandedSegments(true).SetUppercase(true).ToOptions() hexPrefixedParams = new(addrstr.StringOptionsBuilder).SetRadix(16).SetHasSeparator(false).SetExpandedSegments(true).SetAddressLabel(HexPrefix).ToOptions() hexPrefixedUppercaseParams = new(addrstr.StringOptionsBuilder).SetRadix(16).SetHasSeparator(false).SetExpandedSegments(true).SetAddressLabel(HexPrefix).SetUppercase(true).ToOptions() octalParams = new(addrstr.StringOptionsBuilder).SetRadix(8).SetHasSeparator(false).SetExpandedSegments(true).ToOptions() octalPrefixedParams = new(addrstr.StringOptionsBuilder).SetRadix(8).SetHasSeparator(false).SetExpandedSegments(true).SetAddressLabel(OctalPrefix).ToOptions() octal0oPrefixedParams = new(addrstr.StringOptionsBuilder).SetRadix(8).SetHasSeparator(false).SetExpandedSegments(true).SetAddressLabel(otherOctalPrefix).ToOptions() binaryParams = new(addrstr.StringOptionsBuilder).SetRadix(2).SetHasSeparator(false).SetExpandedSegments(true).ToOptions() binaryPrefixedParams = new(addrstr.StringOptionsBuilder).SetRadix(2).SetHasSeparator(false).SetExpandedSegments(true).SetAddressLabel(BinaryPrefix).ToOptions() decimalParams = new(addrstr.StringOptionsBuilder).SetRadix(10).SetHasSeparator(false).SetExpandedSegments(true).ToOptions() ) // Format is intentionally the only method with non-pointer receivers. It is not intended to be called directly, it is intended for use by the fmt package. // When called by a function in the fmt package, nil values are detected before this method is called, avoiding a panic when calling this method. // Format implements [fmt.Formatter] interface. It accepts the formats // - 'v' for the default address and section format (either the normalized or canonical string), // - 's' (string) for the same, // - 'b' (binary), 'o' (octal with 0 prefix), 'O' (octal with 0o prefix), // - 'd' (decimal), 'x' (lowercase hexadecimal), and // - 'X' (uppercase hexadecimal). // Also supported are some of fmt's format flags for integral types. // Sign control is not supported since addresses and sections are never negative. // '#' for an alternate format is supported, which adds a leading zero for octal, and for hexadecimal it adds // a leading "0x" or "0X" for "%#x" and "%#X" respectively. // Also supported is specification of minimum digits precision, output field width, // space or zero padding, and '-' for left or right justification. func (section addressSectionInternal) Format(state fmt.State, verb rune) { section.format(state, verb, NoZone, false) } func (section *addressSectionInternal) format(state fmt.State, verb rune, zone Zone, useCanonical bool) { var str string var err error var isStringFormat bool _, hasPrecision := state.Precision() _, hasWidth := state.Width() useDefaultStr := !hasPrecision && !hasWidth switch verb { case 's', 'v', 'q': isStringFormat = true if useCanonical { if zone != NoZone { str = section.toAddressSection().ToIPv6().toCanonicalString(zone) } else { str = section.toCanonicalString() } } else { if zone != NoZone { str = section.toAddressSection().ToIPv6().toNormalizedString(zone) } else { str = section.toNormalizedString() } } if verb == 'q' && useDefaultStr { if state.Flag('#') && (zone == NoZone || strconv.CanBackquote(string(zone))) { str = "`" + str + "`" } else if zone == NoZone { str = `"` + str + `"` } else { str = strconv.Quote(str) // zones should not have special characters, but you cannot be sure } } case 'x': useDefaultStr = useDefaultStr && zone == NoZone str, err = section.toHexString(useDefaultStr && state.Flag('#')) case 'X': useDefaultStr = useDefaultStr && zone == NoZone if useDefaultStr && state.Flag('#') { str, err = section.toLongStringZoned(NoZone, hexPrefixedUppercaseParams) } else { str, err = section.toLongStringZoned(NoZone, hexUppercaseParams) } case 'b': useDefaultStr = useDefaultStr && zone == NoZone str, err = section.toBinaryString(useDefaultStr && state.Flag('#')) case 'o': useDefaultStr = useDefaultStr && zone == NoZone str, err = section.toOctalString(useDefaultStr && state.Flag('#')) case 'O': useDefaultStr = useDefaultStr && zone == NoZone if useDefaultStr { str, err = section.toLongOctalStringZoned(NoZone, octal0oPrefixedParams) } else { str, err = section.toLongOctalStringZoned(NoZone, octalParams) } case 'd': useDefaultStr = useDefaultStr && zone == NoZone str, err = section.toDecimalStringZoned(NoZone) default: // format not supported _, _ = fmt.Fprintf(state, "%%!%c(address=%s)", verb, section.toString()) return } if err != nil { // could not produce an octal, binary, hex or decimal string, so use string format instead isStringFormat = true if useCanonical { str = section.toCanonicalString() } else { str = section.toNormalizedString() } } if useDefaultStr { _, _ = state.Write([]byte(str)) } else if isStringFormat { section.writeStrFmt(state, verb, str, zone) } else { section.writeNumberFmt(state, verb, str, zone) } } func (section addressSectionInternal) writeStrFmt(state fmt.State, verb rune, str string, zone Zone) { if precision, hasPrecision := state.Precision(); hasPrecision && len(str) > precision { str = str[:precision] } if verb == 'q' { if state.Flag('#') && (zone == NoZone || strconv.CanBackquote(string(zone))) { str = "`" + str + "`" } else if zone == NoZone { str = `"` + str + `"` } else { str = strconv.Quote(str) // zones should not have special characters, but you cannot be sure } } var leftPaddingCount, rightPaddingCount int if width, hasWidth := state.Width(); hasWidth && len(str) < width { // padding required paddingCount := width - len(str) if state.Flag('-') { // right padding with spaces (takes precedence over '0' flag) rightPaddingCount = paddingCount } else { // left padding with spaces leftPaddingCount = paddingCount } } // left padding/str/right padding writeBytes(state, ' ', leftPaddingCount) _, _ = state.Write([]byte(str)) writeBytes(state, ' ', rightPaddingCount) } func (section addressSectionInternal) writeNumberFmt(state fmt.State, verb rune, str string, zone Zone) { var prefix string if verb == 'O' { prefix = otherOctalPrefix // "0o" } else if state.Flag('#') { switch verb { case 'x': prefix = HexPrefix case 'X': prefix = otherHexPrefix case 'b': prefix = BinaryPrefix case 'o': prefix = OctalPrefix } } isMulti := section.isMultiple() var addrStr, secondStr string var separator byte if isMulti { separatorIndex := len(str) >> 1 addrStr = str[:separatorIndex] separator = str[separatorIndex] secondStr = str[separatorIndex+1:] } else { addrStr = str } precision, hasPrecision := state.Precision() width, hasWidth := state.Width() usePrecision := hasPrecision if section.hasNoDivisions() { usePrecision = false prefix = "" } for { var zeroCount, leftPaddingCount, rightPaddingCount int if usePrecision { if len(addrStr) > precision { frontChar := addrStr[0] if frontChar == '0' { i := 1 // eliminate leading zeros to match the precision (all the way to nothing) for len(addrStr) > precision+i { frontChar = addrStr[i] if frontChar != '0' { break } i++ } addrStr = addrStr[i:] } } else if len(addrStr) < precision { // expand to match the precision zeroCount = precision - len(addrStr) } } length := len(prefix) + zeroCount + len(addrStr) zoneRequired := len(zone) > 0 if zoneRequired { length += len(zone) + 1 } if hasWidth && length < width { // padding required paddingCount := width - length if state.Flag('-') { // right padding with spaces (takes precedence over '0' flag) rightPaddingCount = paddingCount } else if state.Flag('0') && !hasPrecision { // left padding with zeros zeroCount = paddingCount } else { // left padding with spaces leftPaddingCount = paddingCount } } // left padding/prefix/zeros/str/right padding writeBytes(state, ' ', leftPaddingCount) writeStr(state, prefix, 1) writeBytes(state, '0', zeroCount) _, _ = state.Write([]byte(addrStr)) if zoneRequired { _, _ = state.Write([]byte{IPv6ZoneSeparator}) _, _ = state.Write([]byte(zone)) } writeBytes(state, ' ', rightPaddingCount) if !isMulti { break } addrStr = secondStr isMulti = false _, _ = state.Write([]byte{separator}) } } func writeStr(state fmt.State, str string, count int) { if count > 0 && len(str) > 0 { bytes := []byte(str) for ; count > 0; count-- { _, _ = state.Write(bytes) } } } func writeBytes(state fmt.State, b byte, count int) { if count > 0 { bytes := make([]byte, count) for i := range bytes { bytes[i] = b } _, _ = state.Write(bytes) } } func (section *addressSectionInternal) toCanonicalString() string { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToCanonicalString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToCanonicalString() } else if sect := section.toMACAddressSection(); sect != nil { return sect.ToCanonicalString() } // zero section return nilSection() } func (section *addressSectionInternal) toNormalizedString() string { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToNormalizedString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToNormalizedString() } else if sect := section.toMACAddressSection(); sect != nil { return sect.ToNormalizedString() } return nilSection() } func (section *addressSectionInternal) toNormalizedWildcardString() string { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToNormalizedWildcardString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToNormalizedWildcardString() } else if sect := section.toMACAddressSection(); sect != nil { return sect.ToNormalizedWildcardString() } return nilSection() } func (section *addressSectionInternal) toCompressedString() string { if sect := section.toIPv4AddressSection(); sect != nil { return sect.ToCompressedString() } else if sect := section.toIPv6AddressSection(); sect != nil { return sect.ToCompressedString() } else if sect := section.toMACAddressSection(); sect != nil { return sect.ToCompressedString() } return nilSection() } func (section *addressSectionInternal) toDecimalStringZoned(zone Zone) (string, addrerr.IncompatibleAddressError) { if isDual, err := section.isDualString(); err != nil { return "", err } else { var largeGrouping *IPAddressLargeDivisionGrouping if section.hasNoDivisions() { largeGrouping = NewIPAddressLargeDivGrouping(nil) } else { bytes := section.getBytes() prefLen := section.getPrefixLen() bitCount := section.GetBitCount() var div *IPAddressLargeDivision if isDual { div = NewIPAddressLargeRangePrefixDivision(bytes, section.getUpperBytes(), prefLen, bitCount, 10) } else { div = NewIPAddressLargePrefixDivision(bytes, prefLen, bitCount, 10) } largeGrouping = NewIPAddressLargeDivGrouping([]*IPAddressLargeDivision{div}) } return toNormalizedZonedString(decimalParams, largeGrouping, zone), nil } } func (section *addressSectionInternal) toHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { cache := section.getStringCache() if cache == nil { return section.toHexStringZoned(with0xPrefix, NoZone) } var cacheField **string if with0xPrefix { cacheField = &cache.hexStringPrefixed } else { cacheField = &cache.hexString } return cacheStrErr(cacheField, func() (string, addrerr.IncompatibleAddressError) { return section.toHexStringZoned(with0xPrefix, NoZone) }) } func (section *addressSectionInternal) toHexStringZoned(with0xPrefix bool, zone Zone) (string, addrerr.IncompatibleAddressError) { if with0xPrefix { return section.toLongStringZoned(zone, hexPrefixedParams) } return section.toLongStringZoned(zone, hexParams) } func (section *addressSectionInternal) toOctalString(with0Prefix bool) (string, addrerr.IncompatibleAddressError) { cache := section.getStringCache() if cache == nil { return section.toOctalStringZoned(with0Prefix, NoZone) } var cacheField **string if with0Prefix { cacheField = &cache.octalStringPrefixed } else { cacheField = &cache.octalString } return cacheStrErr(cacheField, func() (string, addrerr.IncompatibleAddressError) { return section.toOctalStringZoned(with0Prefix, NoZone) }) } func (section *addressSectionInternal) toOctalStringZoned(with0Prefix bool, zone Zone) (string, addrerr.IncompatibleAddressError) { var opts addrstr.StringOptions if with0Prefix { opts = octalPrefixedParams } else { opts = octalParams } return section.toLongOctalStringZoned(zone, opts) } func (section *addressSectionInternal) toLongOctalStringZoned(zone Zone, opts addrstr.StringOptions) (string, addrerr.IncompatibleAddressError) { if isDual, err := section.isDualString(); err != nil { return "", err } else if isDual { lowerDivs, _ := section.getLower().createNewDivisions(3) upperDivs, _ := section.getUpper().createNewDivisions(3) lowerPart := createInitializedGrouping(lowerDivs, nil) upperPart := createInitializedGrouping(upperDivs, nil) return toNormalizedStringRange(toParams(opts), lowerPart, upperPart, zone), nil } divs, _ := section.createNewDivisions(3) part := createInitializedGrouping(divs, nil) return toParams(opts).toZonedString(part, zone), nil } func (section *addressSectionInternal) toBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) { cache := section.getStringCache() if cache == nil { return section.toBinaryStringZoned(with0bPrefix, NoZone) } var cacheField **string if with0bPrefix { cacheField = &cache.binaryStringPrefixed } else { cacheField = &cache.binaryString } return cacheStrErr(cacheField, func() (string, addrerr.IncompatibleAddressError) { return section.toBinaryStringZoned(with0bPrefix, NoZone) }) } func (section *addressSectionInternal) toBinaryStringZoned(with0bPrefix bool, zone Zone) (string, addrerr.IncompatibleAddressError) { if with0bPrefix { return section.toLongStringZoned(zone, binaryPrefixedParams) } return section.toLongStringZoned(zone, binaryParams) } func (section *addressSectionInternal) toLongStringZoned(zone Zone, params addrstr.StringOptions) (string, addrerr.IncompatibleAddressError) { if isDual, err := section.isDualString(); err != nil { return "", err } else if isDual { sect := section.toAddressSection() return toNormalizedStringRange(toParams(params), sect.GetLower(), sect.GetUpper(), zone), nil } return section.toCustomStringZoned(params, zone), nil } func (section *addressSectionInternal) toCustomString(stringOptions addrstr.StringOptions) string { return toNormalizedString(stringOptions, section.toAddressSection()) } func (section *addressSectionInternal) toCustomStringZoned(stringOptions addrstr.StringOptions, zone Zone) string { return toNormalizedZonedString(stringOptions, section.toAddressSection(), zone) } func (section *addressSectionInternal) isDualString() (bool, addrerr.IncompatibleAddressError) { count := section.GetSegmentCount() if section.isMultiple() { //at this point we know we will return true, but we determine now if we must return addrerr.IncompatibleAddressError for i := 0; i < count; i++ { division := section.GetSegment(i) if division.isMultiple() { isLastFull := true for j := count - 1; j >= i; j-- { division = section.GetSegment(j) if division.isMultiple() { if !isLastFull { return false, &incompatibleAddressError{addressError{key: "ipaddress.error.segmentMismatch"}} } isLastFull = division.IsFullRange() } else { isLastFull = false } } return true, nil } } } return false, nil } // used by iterator() and nonZeroHostIterator() in section types func (section *addressSectionInternal) sectionIterator(excludeFunc func([]*AddressDivision) bool) Iterator[*AddressSection] { useOriginal := !section.isMultiple() var original = section.toAddressSection() var iterator Iterator[[]*AddressDivision] if useOriginal { if excludeFunc != nil && excludeFunc(section.getDivisionsInternal()) { original = nil // the single-valued iterator starts out empty } } else { iterator = allSegmentsIterator( section.GetSegmentCount(), nil, func(index int) Iterator[*AddressSegment] { return section.GetSegment(index).iterator() }, excludeFunc) } return sectIterator( useOriginal, original, false, iterator) } func (section *addressSectionInternal) prefixIterator(isBlockIterator bool) Iterator[*AddressSection] { prefLen := section.prefixLength if prefLen == nil { return section.sectionIterator(nil) } prefLength := prefLen.bitCount() var useOriginal bool if isBlockIterator { useOriginal = section.IsSinglePrefixBlock() } else { useOriginal = section.GetPrefixCount().CmpAbs(bigOneConst()) == 0 } bitsPerSeg := section.GetBitsPerSegment() bytesPerSeg := section.GetBytesPerSegment() networkSegIndex := getNetworkSegmentIndex(prefLength, bytesPerSeg, bitsPerSeg) hostSegIndex := getHostSegmentIndex(prefLength, bytesPerSeg, bitsPerSeg) segCount := section.GetSegmentCount() var iterator Iterator[[]*AddressDivision] if !useOriginal { var hostSegIteratorProducer func(index int) Iterator[*AddressSegment] if isBlockIterator { hostSegIteratorProducer = func(index int) Iterator[*AddressSegment] { return section.GetSegment(index).prefixBlockIterator() } } else { hostSegIteratorProducer = func(index int) Iterator[*AddressSegment] { return section.GetSegment(index).prefixIterator() } } iterator = segmentsIterator( segCount, nil, //when no prefix we defer to other iterator, when there is one we use the whole original section in the encompassing iterator and not just the original segments func(index int) Iterator[*AddressSegment] { return section.GetSegment(index).iterator() }, nil, networkSegIndex, hostSegIndex, hostSegIteratorProducer) } if isBlockIterator { return sectIterator( useOriginal, section.toAddressSection(), prefLength < section.GetBitCount(), iterator) } return prefixSectIterator( useOriginal, section.toAddressSection(), iterator) } func (section *addressSectionInternal) blockIterator(segmentCount int) Iterator[*AddressSection] { if segmentCount < 0 { segmentCount = 0 } allSegsCount := section.GetSegmentCount() if segmentCount >= allSegsCount { return section.sectionIterator(nil) } useOriginal := !section.isMultipleTo(segmentCount) var iterator Iterator[[]*AddressDivision] if !useOriginal { var hostSegIteratorProducer func(index int) Iterator[*AddressSegment] hostSegIteratorProducer = func(index int) Iterator[*AddressSegment] { return section.GetSegment(index).identityIterator() } segIteratorProducer := func(index int) Iterator[*AddressSegment] { return section.GetSegment(index).iterator() } iterator = segmentsIterator( allSegsCount, nil, //when no prefix we defer to other iterator, when there is one we use the whole original section in the encompassing iterator and not just the original segments segIteratorProducer, nil, segmentCount-1, segmentCount, hostSegIteratorProducer) } return sectIterator( useOriginal, section.toAddressSection(), section.isMultipleFrom(segmentCount), iterator) } func (section *addressSectionInternal) sequentialBlockIterator() Iterator[*AddressSection] { return section.blockIterator(section.GetSequentialBlockIndex()) } // GetSequentialBlockCount provides the count of elements from the sequential block iterator, the minimal number of sequential address sections that comprise this address section. func (section *addressSectionInternal) GetSequentialBlockCount() *big.Int { sequentialSegCount := section.GetSequentialBlockIndex() return section.GetPrefixCountLen(BitCount(sequentialSegCount) * section.GetBitsPerSegment()) } func (section *addressSectionInternal) isMultipleTo(segmentCount int) bool { for i := 0; i < segmentCount; i++ { if section.GetSegment(i).isMultiple() { return true } } return false } func (section *addressSectionInternal) isMultipleFrom(segmentCount int) bool { segTotal := section.GetSegmentCount() for i := segmentCount; i < segTotal; i++ { if section.GetSegment(i).isMultiple() { return true } } return false } func (section *addressSectionInternal) getSubnetSegments( // called by methods to adjust/remove/set prefix length, masking methods, zero host and zero network methods startIndex int, networkPrefixLength PrefixLen, verifyMask bool, segProducer func(int) *AddressDivision, segmentMaskProducer func(int) SegInt, ) (res *AddressSection, err addrerr.IncompatibleAddressError) { networkPrefixLength = checkPrefLen(networkPrefixLength, section.GetBitCount()) bitsPerSegment := section.GetBitsPerSegment() count := section.GetSegmentCount() for i := startIndex; i < count; i++ { segmentPrefixLength := getSegmentPrefixLength(bitsPerSegment, networkPrefixLength, i) seg := segProducer(i) //note that the mask can represent a range (for example a CIDR mask), //but we use the lowest value (maskSegment.value) in the range when masking (ie we discard the range) maskValue := segmentMaskProducer(i) origValue, origUpperValue := seg.getSegmentValue(), seg.getUpperSegmentValue() value, upperValue := origValue, origUpperValue if verifyMask { mask64 := uint64(maskValue) val64 := uint64(value) upperVal64 := uint64(upperValue) masker := MaskRange(val64, upperVal64, mask64, seg.GetMaxValue()) if !masker.IsSequential() { err = &incompatibleAddressError{addressError{key: "ipaddress.error.maskMismatch"}} return } value = SegInt(masker.GetMaskedLower(val64, mask64)) upperValue = SegInt(masker.GetMaskedUpper(upperVal64, mask64)) } else { value &= maskValue upperValue &= maskValue } if !segsSame(segmentPrefixLength, seg.getDivisionPrefixLength(), value, origValue, upperValue, origUpperValue) { newSegments := createSegmentArray(count) section.copySubDivisions(0, i, newSegments) newSegments[i] = createAddressDivision(seg.deriveNewMultiSeg(value, upperValue, segmentPrefixLength)) for i++; i < count; i++ { segmentPrefixLength = getSegmentPrefixLength(bitsPerSegment, networkPrefixLength, i) seg = segProducer(i) maskValue = segmentMaskProducer(i) origValue, origUpperValue = seg.getSegmentValue(), seg.getUpperSegmentValue() value, upperValue = origValue, origUpperValue if verifyMask { mask64 := uint64(maskValue) val64 := uint64(value) upperVal64 := uint64(upperValue) masker := MaskRange(val64, upperVal64, mask64, seg.GetMaxValue()) if !masker.IsSequential() { err = &incompatibleAddressError{addressError{key: "ipaddress.error.maskMismatch"}} return } value = SegInt(masker.GetMaskedLower(val64, mask64)) upperValue = SegInt(masker.GetMaskedUpper(upperVal64, mask64)) } else { value &= maskValue upperValue &= maskValue } if !segsSame(segmentPrefixLength, seg.getDivisionPrefixLength(), value, origValue, upperValue, origUpperValue) { newSegments[i] = createAddressDivision(seg.deriveNewMultiSeg(value, upperValue, segmentPrefixLength)) } else { newSegments[i] = seg } } res = deriveAddressSectionPrefLen(section.toAddressSection(), newSegments, networkPrefixLength) return } } res = section.toAddressSection() return } func (section *addressSectionInternal) toAddressSection() *AddressSection { return (*AddressSection)(unsafe.Pointer(section)) } func (section *addressSectionInternal) toIPAddressSection() *IPAddressSection { return section.toAddressSection().ToIP() } func (section *addressSectionInternal) toIPv4AddressSection() *IPv4AddressSection { return section.toAddressSection().ToIPv4() } func (section *addressSectionInternal) toIPv6AddressSection() *IPv6AddressSection { return section.toAddressSection().ToIPv6() } func (section *addressSectionInternal) toMACAddressSection() *MACAddressSection { return section.toAddressSection().ToMAC() } //// only needed for godoc / pkgsite // IsZero returns whether this section matches exactly the value of zero. func (section *addressSectionInternal) IsZero() bool { return section.addressDivisionGroupingInternal.IsZero() } // IncludesZero returns whether this section includes the value of zero within its range. func (section *addressSectionInternal) IncludesZero() bool { return section.addressDivisionGroupingInternal.IncludesZero() } // IsMax returns whether this section matches exactly the maximum possible value, the value whose bits are all ones. func (section *addressSectionInternal) IsMax() bool { return section.addressDivisionGroupingInternal.IsMax() } // IncludesMax returns whether this section includes the max value, the value whose bits are all ones, within its range. func (section *addressSectionInternal) IncludesMax() bool { return section.addressDivisionGroupingInternal.IncludesMax() } // IsFullRange returns whether this address item represents all possible values attainable by an address item of this type. // // This is true if and only if both IncludesZero and IncludesMax return true. func (section *addressSectionInternal) IsFullRange() bool { return section.addressDivisionGroupingInternal.IsFullRange() } // GetSequentialBlockIndex gets the minimal segment index for which all following segments are full-range blocks. // // The segment at this index is not a full-range block itself, unless all segments are full-range. // The segment at this index and all following segments form a sequential range. // For the full address section to be sequential, the preceding segments must be single-valued. func (section *addressSectionInternal) GetSequentialBlockIndex() int { return section.addressDivisionGroupingInternal.GetSequentialBlockIndex() } // GetPrefixLen returns the prefix length, or nil if there is no prefix length. // // A prefix length indicates the number of bits in the initial part of the address item that comprises the prefix. // // A prefix is a part of the address item that is not specific to that address but common amongst a group of such items, such as a CIDR prefix block subnet. func (section *addressSectionInternal) GetPrefixLen() PrefixLen { return section.addressDivisionGroupingInternal.GetPrefixLen() } // ContainsPrefixBlock returns whether the values of this item contains the block of values for the given prefix length. // // Unlike ContainsSinglePrefixBlock, whether there are multiple prefix values in this item for the given prefix length makes no difference. // // Use GetMinPrefixLenForBlock to determine the smallest prefix length for which this method returns true. func (section *addressSectionInternal) ContainsPrefixBlock(prefixLen BitCount) bool { prefixLen = checkSubnet(section, prefixLen) divCount := section.GetSegmentCount() bitsPerSegment := section.GetBitsPerSegment() i := getHostSegmentIndex(prefixLen, section.GetBytesPerSegment(), bitsPerSegment) if i < divCount { div := section.GetSegment(i) segmentPrefixLength := getPrefixedSegmentPrefixLength(bitsPerSegment, prefixLen, i) if !div.ContainsPrefixBlock(segmentPrefixLength.bitCount()) { return false } for i++; i < divCount; i++ { div = section.GetSegment(i) if !div.IsFullRange() { return false } } } return true } // ContainsSinglePrefixBlock returns whether the values of this grouping contains a single prefix block for the given prefix length. // // This means there is only one prefix of the given length in this item, and this item contains the prefix block for that given prefix. // // Use GetPrefixLenForSingleBlock to determine whether there is a prefix length for which this method returns true. func (section *addressSectionInternal) ContainsSinglePrefixBlock(prefixLen BitCount) bool { return section.addressDivisionGroupingInternal.ContainsSinglePrefixBlock(prefixLen) } // IsPrefixBlock returns whether this address segment series has a prefix length and includes the block associated with its prefix length. // If the prefix length matches the bit count, this returns true. // // This is different from ContainsPrefixBlock in that this method returns // false if the series has no prefix length or a prefix length that differs from a prefix length for which ContainsPrefixBlock returns true. func (section *addressSectionInternal) IsPrefixBlock() bool { prefLen := section.getPrefixLen() return prefLen != nil && section.ContainsPrefixBlock(prefLen.bitCount()) } // IsSinglePrefixBlock returns whether the range matches the block of values for a single prefix identified by the prefix length of this address. // This is similar to IsPrefixBlock except that it returns false when the subnet has multiple prefixes. // // What distinguishes this method from ContainsSinglePrefixBlock is that this method returns // false if the series does not have a prefix length assigned to it, // or a prefix length that differs from a prefix length for which ContainsSinglePrefixBlock returns true. // // It is similar to IsPrefixBlock but returns false when there are multiple prefixes. func (section *addressSectionInternal) IsSinglePrefixBlock() bool { return section.addressDivisionGroupingInternal.IsSinglePrefixBlock() } // GetMinPrefixLenForBlock returns the smallest prefix length such that this section includes the block of all values for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this section represents a single value, this returns the bit count. func (section *addressSectionInternal) GetMinPrefixLenForBlock() BitCount { return section.addressDivisionGroupingInternal.GetMinPrefixLenForBlock() } // GetPrefixLenForSingleBlock returns a prefix length for which the range of this address section matches the block of addresses for that prefix. // // If no such prefix exists, GetPrefixLenForSingleBlock returns nil. // // If this address section represents a single value, returns the bit length. func (section *addressSectionInternal) GetPrefixLenForSingleBlock() PrefixLen { return section.addressDivisionGroupingInternal.GetPrefixLenForSingleBlock() } // GetValue returns the lowest individual address section in this address section as an integer value. func (section *addressSectionInternal) GetValue() *big.Int { return section.addressDivisionGroupingInternal.GetValue() } // GetUpperValue returns the highest individual address section in this address section as an integer value. func (section *addressSectionInternal) GetUpperValue() *big.Int { return section.addressDivisionGroupingInternal.GetUpperValue() } // Bytes returns the lowest individual address section in this address section as a byte slice. func (section *addressSectionInternal) Bytes() []byte { return section.addressDivisionGroupingInternal.Bytes() } // UpperBytes returns the highest individual address section in this address section as a byte slice. func (section *addressSectionInternal) UpperBytes() []byte { return section.addressDivisionGroupingInternal.UpperBytes() } // CopyBytes copies the value of the lowest individual address section in the section into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (section *addressSectionInternal) CopyBytes(bytes []byte) []byte { return section.addressDivisionGroupingInternal.CopyBytes(bytes) } // CopyUpperBytes copies the value of the highest individual address section in the section into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (section *addressSectionInternal) CopyUpperBytes(bytes []byte) []byte { return section.addressDivisionGroupingInternal.CopyUpperBytes(bytes) } // IsSequential returns whether the section represents a range of values that are sequential. // // Generally, this means that any segment covering a range of values must be followed by segment that are full range, covering all values. func (section *addressSectionInternal) IsSequential() bool { return section.addressDivisionGroupingInternal.IsSequential() } // GetLeadingBitCount returns the number of consecutive leading one or zero bits. // If ones is true, returns the number of consecutive leading one bits. // Otherwise, returns the number of consecutive leading zero bits. // // This method applies to the lower value of the range if this section represents multiple values. func (section *addressSectionInternal) GetLeadingBitCount(ones bool) BitCount { count := section.GetSegmentCount() if count == 0 { return 0 } var front SegInt if ones { front = section.GetSegment(0).GetMaxValue() } var prefixLen BitCount for i := 0; i < count; i++ { seg := section.GetSegment(i) value := seg.getSegmentValue() if value != front { return prefixLen + seg.GetLeadingBitCount(ones) } prefixLen += seg.getBitCount() } return prefixLen } // GetTrailingBitCount returns the number of consecutive trailing one or zero bits. // If ones is true, returns the number of consecutive trailing zero bits. // Otherwise, returns the number of consecutive trailing one bits. // // This method applies to the lower value of the range if this section represents multiple values. func (section *addressSectionInternal) GetTrailingBitCount(ones bool) BitCount { count := section.GetSegmentCount() if count == 0 { return 0 } var back SegInt if ones { back = section.GetSegment(0).GetMaxValue() } var bitLen BitCount for i := count - 1; i >= 0; i-- { seg := section.GetSegment(i) value := seg.getSegmentValue() if value != back { return bitLen + seg.GetTrailingBitCount(ones) } bitLen += seg.getBitCount() } return bitLen } //// end needed for godoc / pkgsite // An AddressSection is section of an address, containing a certain number of consecutive segments. // // It is a series of individual address segments. Each segment has equal bit-length. Each address is backed by an address section that contains all the segments of the address. // // AddressSection instances are immutable. This also makes them concurrency-safe. // // Most operations that can be performed on Address instances can also be performed on AddressSection instances and vice-versa. type AddressSection struct { addressSectionInternal } // Contains returns whether this is same type and version as the given address section and whether it contains all values in the given section. // // Sections must also have the same number of segments to be comparable, otherwise false is returned. func (section *AddressSection) Contains(other AddressSectionType) bool { if section == nil { return other == nil || other.ToSectionBase() == nil } return section.contains(other) } // Equal returns whether the given address section is equal to this address section. // Two address sections are equal if they represent the same set of sections. // They must match: // - type/version (IPv4, IPv6, MAC, etc) // - segment counts // - bits per segment // - segment value ranges // Prefix lengths are ignored. func (section *AddressSection) Equal(other AddressSectionType) bool { if section == nil { return other == nil || other.ToSectionBase() == nil } return section.equal(other) } // Compare returns a negative integer, zero, or a positive integer if this address section is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (section *AddressSection) Compare(item AddressItem) int { return CountComparator.Compare(section, item) } // CompareSize compares the counts of two address sections, the number of individual sections represented. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether one section represents more individual address sections than another. // // CompareSize returns a positive integer if this address section has a larger count than the one given, zero if they are the same, or a negative integer if the other has a larger count. func (section *AddressSection) CompareSize(other AddressItem) int { if section == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return section.compareSize(other) } // GetCount returns the count of possible distinct values for this item. // If not representing multiple values, the count is 1, // unless this is a division grouping with no divisions, or an address section with no segments, in which case it is 0. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (section *AddressSection) GetCount() *big.Int { if section == nil { return bigZero() } else if sect := section.ToIPv4(); sect != nil { return sect.GetCount() } else if sect := section.ToIPv6(); sect != nil { return sect.GetCount() } else if sect := section.ToMAC(); sect != nil { return sect.GetCount() } return section.addressDivisionGroupingBase.getCount() } // IsMultiple returns whether this section represents multiple values. func (section *AddressSection) IsMultiple() bool { return section != nil && section.isMultiple() } // GetPrefixCount returns the number of distinct prefix values in this item. // // The prefix length is given by GetPrefixLen. // // If this has a non-nil prefix length, returns the number of distinct prefix values. // // If this has a nil prefix length, returns the same value as GetCount. func (section *AddressSection) GetPrefixCount() *big.Int { if sect := section.ToIPv4(); sect != nil { return sect.GetPrefixCount() } else if sect := section.ToIPv6(); sect != nil { return sect.GetPrefixCount() } else if sect := section.ToMAC(); sect != nil { return sect.GetPrefixCount() } return section.addressDivisionGroupingBase.GetPrefixCount() } // GetPrefixCountLen returns the number of distinct prefix values in this item for the given prefix length. func (section *AddressSection) GetPrefixCountLen(prefixLen BitCount) *big.Int { if sect := section.ToIPv4(); sect != nil { return sect.GetPrefixCountLen(prefixLen) } else if sect := section.ToIPv6(); sect != nil { return sect.GetPrefixCountLen(prefixLen) } else if sect := section.ToMAC(); sect != nil { return sect.GetPrefixCountLen(prefixLen) } return section.addressDivisionGroupingBase.GetPrefixCountLen(prefixLen) } // GetBlockCount returns the count of distinct values in the given number of initial (more significant) segments. func (section *AddressSection) GetBlockCount(segments int) *big.Int { if sect := section.ToIPv4(); sect != nil { return sect.GetBlockCount(segments) } else if sect := section.ToIPv6(); sect != nil { return sect.GetBlockCount(segments) } else if sect := section.ToMAC(); sect != nil { return sect.GetBlockCount(segments) } return section.addressDivisionGroupingBase.GetBlockCount(segments) } // GetTrailingSection gets the subsection from the series starting from the given index. // The first segment is at index 0. func (section *AddressSection) GetTrailingSection(index int) *AddressSection { return section.getSubSection(index, section.GetSegmentCount()) } // GetSubSection gets the subsection from the series starting from the given index and ending just before the give endIndex. // The first segment is at index 0. func (section *AddressSection) GetSubSection(index, endIndex int) *AddressSection { return section.getSubSection(index, endIndex) } // CopySubSegments copies the existing segments from the given start index until but not including the segment at the given end index, // into the given slice, as much as can be fit into the slice, returning the number of segments copied. func (section *AddressSection) CopySubSegments(start, end int, segs []*AddressSegment) (count int) { start, end, targetStart := adjust1To1StartIndices(start, end, section.GetDivisionCount(), len(segs)) segs = segs[targetStart:] return section.forEachSubDivision(start, end, func(index int, div *AddressDivision) { segs[index] = div.ToSegmentBase() }, len(segs)) } // CopySegments copies the existing segments into the given slice, // as much as can be fit into the slice, returning the number of segments copied. func (section *AddressSection) CopySegments(segs []*AddressSegment) (count int) { return section.ForEachSegment(func(index int, seg *AddressSegment) (stop bool) { if stop = index >= len(segs); !stop { segs[index] = seg } return }) } // GetSegments returns a slice with the address segments. The returned slice is not backed by the same array as this section. func (section *AddressSection) GetSegments() (res []*AddressSegment) { res = make([]*AddressSegment, section.GetSegmentCount()) section.CopySegments(res) return } // GetLower returns the section in the range with the lowest numeric value, // which will be the same section if it represents a single value. // For example, for "1.2-3.4.5-6", the section "1.2.4.5" is returned. func (section *AddressSection) GetLower() *AddressSection { return section.getLower() } // GetUpper returns the section in the range with the highest numeric value, // which will be the same section if it represents a single value. // For example, for "1.2-3.4.5-6", the section "1.3.4.6" is returned. func (section *AddressSection) GetUpper() *AddressSection { return section.getUpper() } // IsPrefixed returns whether this section has an associated prefix length. func (section *AddressSection) IsPrefixed() bool { return section != nil && section.isPrefixed() } // ToPrefixBlock returns the section with the same prefix as this section while the remaining bits span all values. // The returned section will be the block of all sections with the same prefix. // // If this section has no prefix, this section is returned. func (section *AddressSection) ToPrefixBlock() *AddressSection { return section.toPrefixBlock() } // ToPrefixBlockLen returns the section with the same prefix of the given length as this section while the remaining bits span all values. // The returned section will be the block of all sections with the same prefix. func (section *AddressSection) ToPrefixBlockLen(prefLen BitCount) *AddressSection { return section.toPrefixBlockLen(prefLen) } // WithoutPrefixLen provides the same address section but with no prefix length. The values remain unchanged. func (section *AddressSection) WithoutPrefixLen() *AddressSection { if !section.IsPrefixed() { return section } return section.withoutPrefixLen() } // SetPrefixLen sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address section. // The provided prefix length will be adjusted to these boundaries if necessary. func (section *AddressSection) SetPrefixLen(prefixLen BitCount) *AddressSection { return section.setPrefixLen(prefixLen) } // SetPrefixLenZeroed sets the prefix length. // // A prefix length will not be set to a value lower than zero or beyond the bit length of the address section. // The provided prefix length will be adjusted to these boundaries if necessary. // // If this address section has a prefix length, and the prefix length is increased when setting the new prefix length, the bits moved within the prefix become zero. // If this address section has a prefix length, and the prefix length is decreased when setting the new prefix length, the bits moved outside the prefix become zero. // // In other words, bits that move from one side of the prefix length to the other (bits moved into the prefix or outside the prefix) are zeroed. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section *AddressSection) SetPrefixLenZeroed(prefixLen BitCount) (*AddressSection, addrerr.IncompatibleAddressError) { return section.setPrefixLenZeroed(prefixLen) } // AdjustPrefixLen increases or decreases the prefix length by the given increment. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address section. // // If this address section has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. func (section *AddressSection) AdjustPrefixLen(prefixLen BitCount) *AddressSection { return section.adjustPrefixLen(prefixLen).ToSectionBase() } // AdjustPrefixLenZeroed increases or decreases the prefix length by the given increment while zeroing out the bits that have moved into or outside the prefix. // // A prefix length will not be adjusted lower than zero or beyond the bit length of the address section. // // If this address section has no prefix length, then the prefix length will be set to the adjustment if positive, // or it will be set to the adjustment added to the bit count if negative. // // When prefix length is increased, the bits moved within the prefix become zero. // When a prefix length is decreased, the bits moved outside the prefix become zero. // // If the result cannot be zeroed because zeroing out bits results in a non-contiguous segment, an error is returned. func (section *AddressSection) AdjustPrefixLenZeroed(prefixLen BitCount) (*AddressSection, addrerr.IncompatibleAddressError) { res, err := section.adjustPrefixLenZeroed(prefixLen) return res.ToSectionBase(), err } // AssignPrefixForSingleBlock returns the equivalent prefix block that matches exactly the range of values in this address section. // The returned block will have an assigned prefix length indicating the prefix length for the block. // // There may be no such address section - it is required that the range of values match the range of a prefix block. // If there is no such address section, then nil is returned. func (section *AddressSection) AssignPrefixForSingleBlock() *AddressSection { return section.assignPrefixForSingleBlock() } // AssignMinPrefixForBlock returns an equivalent address section, assigned the smallest prefix length possible, // such that the prefix block for that prefix length is in this address section. // // In other words, this method assigns a prefix length to this address section matching the largest prefix block in this address section. func (section *AddressSection) AssignMinPrefixForBlock() *AddressSection { return section.assignMinPrefixForBlock() } // ToBlock creates a new block of address sections by changing the segment at the given index to have the given lower and upper value, // and changing the following segments to be full-range. func (section *AddressSection) ToBlock(segmentIndex int, lower, upper SegInt) *AddressSection { return section.toBlock(segmentIndex, lower, upper) } // IsAdaptiveZero returns true if the division grouping was originally created as an implicitly zero-valued section or grouping (e.g. IPv4AddressSection{}), // meaning it was not constructed using a constructor function. // Such a grouping, which has no divisions or segments, is convertible to an implicitly zero-valued grouping of any type or version, whether IPv6, IPv4, MAC, or other. // In other words, when a section or grouping is the zero-value, then it is equivalent and convertible to the zero value of any other section or grouping type. func (section *AddressSection) IsAdaptiveZero() bool { return section != nil && section.matchesZeroGrouping() } // IsIP returns true if this address section originated as an IPv4 or IPv6 section, or a zero-length IP section. If so, use ToIP to convert back to the IP-specific type. func (section *AddressSection) IsIP() bool { return section != nil && section.matchesIPSectionType() } // IsIPv4 returns true if this address section originated as an IPv4 section. If so, use ToIPv4 to convert back to the IPv4-specific type. func (section *AddressSection) IsIPv4() bool { return section != nil && section.matchesIPv4SectionType() } // IsIPv6 returns true if this address section originated as an IPv6 section. If so, use ToIPv6 to convert back to the IPv6-specific type. func (section *AddressSection) IsIPv6() bool { return section != nil && section.matchesIPv6SectionType() } // IsMAC returns true if this address section originated as a MAC section. If so, use ToMAC to convert back to the MAC-specific type. func (section *AddressSection) IsMAC() bool { return section != nil && section.matchesMACSectionType() } // ToDivGrouping converts to an AddressDivisionGrouping, a polymorphic type usable with all address sections and division groupings. // Afterwards, you can convert back with ToSectionBase. // // ToDivGrouping can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *AddressSection) ToDivGrouping() *AddressDivisionGrouping { return (*AddressDivisionGrouping)(unsafe.Pointer(section)) } // ToIP converts to an IPAddressSection if this address section originated as an IPv4 or IPv6 section, or an implicitly zero-valued IP section. // If not, ToIP returns nil. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *AddressSection) ToIP() *IPAddressSection { if section.IsIP() { return (*IPAddressSection)(unsafe.Pointer(section)) } return nil } // ToIPv6 converts to an IPv6AddressSection if this section originated as an IPv6 section. // If not, ToIPv6 returns nil. // // ToIPv6 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *AddressSection) ToIPv6() *IPv6AddressSection { if section.IsIPv6() { return (*IPv6AddressSection)(unsafe.Pointer(section)) } return nil } // ToIPv4 converts to an IPv4AddressSection if this section originated as an IPv4 section. // If not, ToIPv4 returns nil. // // ToIPv4 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *AddressSection) ToIPv4() *IPv4AddressSection { if section.IsIPv4() { return (*IPv4AddressSection)(unsafe.Pointer(section)) } return nil } // ToMAC converts to a MACAddressSection if this section originated as a MAC section. // If not, ToMAC returns nil. // // ToMAC can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *AddressSection) ToMAC() *MACAddressSection { if section.IsMAC() { return (*MACAddressSection)(section) } return nil } // ToSectionBase is an identity method. // // ToSectionBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (section *AddressSection) ToSectionBase() *AddressSection { return section } // Wrap wraps this address section, returning a WrappedAddressSection, an implementation of ExtendedSegmentSeries, // which can be used to write code that works with both addresses and address sections. func (section *AddressSection) Wrap() WrappedAddressSection { return wrapSection(section) } // Iterator provides an iterator to iterate through the individual address sections of this address section. // // When iterating, the prefix length is preserved. Remove it using WithoutPrefixLen prior to iterating if you wish to drop it from all individual address sections. // // Call IsMultiple to determine if this instance represents multiple address sections, or GetCount for the count. func (section *AddressSection) Iterator() Iterator[*AddressSection] { if section == nil { return nilSectIterator() } return section.sectionIterator(nil) } // PrefixIterator provides an iterator to iterate through the individual prefixes of this address section, // each iterated element spanning the range of values for its prefix. // // It is similar to the prefix block iterator, except for possibly the first and last iterated elements, which might not be prefix blocks, // instead constraining themselves to values from this address section. // // If the series has no prefix length, then this is equivalent to Iterator. func (section *AddressSection) PrefixIterator() Iterator[*AddressSection] { return section.prefixIterator(false) } // PrefixBlockIterator provides an iterator to iterate through the individual prefix blocks, one for each prefix of this address section. // Each iterated address section will be a prefix block with the same prefix length as this address section. // // If this address section has no prefix length, then this is equivalent to Iterator. func (section *AddressSection) PrefixBlockIterator() Iterator[*AddressSection] { return section.prefixIterator(true) } // IncrementBoundary returns the item that is the given increment from the range boundaries of this item. // // If the given increment is positive, adds the value to the highest (GetUpper) in the range to produce a new item. // If the given increment is negative, adds the value to the lowest (GetLower) in the range to produce a new item. // If the increment is zero, returns this. // // If this represents just a single value, this item is simply incremented by the given increment value, positive or negative. // // On overflow or underflow, IncrementBoundary returns nil. func (section *AddressSection) IncrementBoundary(increment int64) *AddressSection { return section.incrementBoundary(increment) } // Increment returns the item that is the given increment upwards into the range, // with the increment of 0 returning the first in the range. // // If the increment i matches or exceeds the range count c, then i - c + 1 // is added to the upper item of the range. // An increment matching the count gives you the item just above the highest in the range. // // If the increment is negative, it is added to the lowest of the range. // To get the item just below the lowest of the range, use the increment -1. // // If this represents just a single value, the item is simply incremented by the given increment, positive or negative. // // If this item represents multiple values, a positive increment i is equivalent i + 1 values from the iterator and beyond. // For instance, a increment of 0 is the first value from the iterator, an increment of 1 is the second value from the iterator, and so on. // An increment of a negative value added to the count is equivalent to the same number of iterator values preceding the last value of the iterator. // For instance, an increment of count - 1 is the last value from the iterator, an increment of count - 2 is the second last value, and so on. // // On overflow or underflow, Increment returns nil. func (section *AddressSection) Increment(increment int64) *AddressSection { return section.increment(increment) } // ReverseBits returns a new section with the bits reversed. Any prefix length is dropped. // // If the bits within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (section *AddressSection) ReverseBits(perByte bool) (*AddressSection, addrerr.IncompatibleAddressError) { return section.reverseBits(perByte) } // ReverseBytes returns a new section with the bytes reversed. Any prefix length is dropped. // // If each segment is more than 1 byte long, and the bytes within a single segment cannot be reversed because the segment represents a range, // and reversing the segment values results in a range that is not contiguous, then this returns an error. // // In practice this means that to be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. func (section *AddressSection) ReverseBytes() (*AddressSection, addrerr.IncompatibleAddressError) { return section.reverseBytes(false) } // ReverseSegments returns a new section with the segments reversed. func (section *AddressSection) ReverseSegments() *AddressSection { if section.GetSegmentCount() <= 1 { if section.IsPrefixed() { return section.WithoutPrefixLen() } return section } res, _ := section.reverseSegments( func(i int) (*AddressSegment, addrerr.IncompatibleAddressError) { return section.GetSegment(i).withoutPrefixLen(), nil }, ) return res } // String implements the [fmt.Stringer] interface, returning the normalized string provided by ToNormalizedString, or "" if the receiver is a nil pointer. func (section *AddressSection) String() string { if section == nil { return nilString() } return section.toString() } // ToCanonicalString produces a canonical string for the address section. // // For IPv4, dotted octet format, also known as dotted decimal format, is used. // https://datatracker.ietf.org/doc/html/draft-main-ipaddr-text-rep-00#section-2.1 // // For IPv6, RFC 5952 describes canonical string representation. // https://en.wikipedia.org/wiki/IPv6_address#Representation // http://tools.ietf.org/html/rfc5952 // // For MAC, it uses the canonical standardized IEEE 802 MAC address representation of xx-xx-xx-xx-xx-xx. An example is "01-23-45-67-89-ab". // For range segments, '|' is used: "11-22-33|44-55-66". func (section *AddressSection) ToCanonicalString() string { if section == nil { return nilString() } return section.toCanonicalString() } // ToNormalizedString produces a normalized string for the address section. // // For IPv4, it is the same as the canonical string. // // For IPv6, it differs from the canonical string. Zero-segments are not compressed. // // For MAC, it differs from the canonical string. It uses the most common representation of MAC addresses: "xx:xx:xx:xx:xx:xx". An example is "01:23:45:67:89:ab". // For range segments, '-' is used: "11:22:33-44:55:66". func (section *AddressSection) ToNormalizedString() string { if section == nil { return nilString() } return section.toNormalizedString() } // ToNormalizedWildcardString produces a string similar to the normalized string but for IP address sections it avoids the CIDR prefix length. // Multiple-valued segments will be shown with wildcards and ranges (denoted by '*' and '-') instead of using the CIDR prefix notation. func (section *AddressSection) ToNormalizedWildcardString() string { if section == nil { return nilString() } return section.toNormalizedWildcardString() } // ToCompressedString produces a short representation of this address section while remaining within the confines of standard representation(s) of the address. // // For IPv4, it is the same as the canonical string. // // For IPv6, it differs from the canonical string. It compresses the maximum number of zeros and/or host segments with the IPv6 compression notation '::'. // // For MAC, it differs from the canonical string. It produces a shorter string for the address that has no leading zeros. func (section *AddressSection) ToCompressedString() string { if section == nil { return nilString() } return section.toCompressedString() } // ToHexString writes this address section as a single hexadecimal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *AddressSection) ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toHexString(with0xPrefix) } // ToOctalString writes this address section as a single octal value (possibly two values if a range), // the number of digits according to the bit count, with or without a preceding "0" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *AddressSection) ToOctalString(with0Prefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toOctalString(with0Prefix) } // ToBinaryString writes this address section as a single binary value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0b" prefix. // // If a multiple-valued section cannot be written as a single prefix block or a range of two values, an error is returned. func (section *AddressSection) ToBinaryString(with0bPrefix bool) (string, addrerr.IncompatibleAddressError) { if section == nil { return nilString(), nil } return section.toBinaryString(with0bPrefix) } // ToCustomString creates a customized string from this address section according to the given string option parameters. func (section *AddressSection) ToCustomString(stringOptions addrstr.StringOptions) string { if section == nil { return nilString() } return section.toCustomString(stringOptions) } // GetSegmentStrings returns a slice with the string for each segment being the string that is normalized with wildcards. func (section *AddressSection) GetSegmentStrings() []string { if section == nil { return nil } return section.getSegmentStrings() } func seriesValsSame(one, two AddressSegmentSeries) bool { if one == two { return true } count := one.GetDivisionCount() if count != two.GetDivisionCount() { panic(two) } for i := count - 1; i >= 0; i-- { // reverse order since less significant segments more likely to differ oneSeg := one.GetGenericSegment(i) twoSeg := two.GetGenericSegment(i) if !segValsSame(oneSeg.GetSegmentValue(), twoSeg.GetSegmentValue(), oneSeg.GetUpperSegmentValue(), twoSeg.GetUpperSegmentValue()) { return false } } return true } func toSegments( bytes []byte, segmentCount int, bytesPerSegment int, bitsPerSegment BitCount, creator addressSegmentCreator, assignedPrefixLength PrefixLen) (segments []*AddressDivision, err addrerr.AddressValueError) { segments = createSegmentArray(segmentCount) byteIndex, segmentIndex := len(bytes), segmentCount-1 for ; segmentIndex >= 0; segmentIndex-- { var value SegInt k := byteIndex - bytesPerSegment if k < 0 { k = 0 } for j := k; j < byteIndex; j++ { byteValue := bytes[j] value <<= 8 value |= SegInt(byteValue) } byteIndex = k segmentPrefixLength := getSegmentPrefixLength(bitsPerSegment, assignedPrefixLength, segmentIndex) seg := creator.createSegment(value, value, segmentPrefixLength) segments[segmentIndex] = seg } // any remaining bytes should be zero for byteIndex--; byteIndex >= 0; byteIndex-- { if bytes[byteIndex] != 0 { err = &addressValueError{ addressError: addressError{key: "ipaddress.error.exceeds.size"}, val: int(bytes[byteIndex]), } break } } return } ipaddress-go-1.5.4/ipaddr/sectiterator.go000066400000000000000000000203541440250641600204120ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr type singleSegmentsIterator struct { original []*AddressDivision } func (it *singleSegmentsIterator) HasNext() bool { return it.original != nil } func (it *singleSegmentsIterator) Next() (res []*AddressDivision) { if it.HasNext() { res = it.original it.original = nil } return } type multiSegmentsIterator struct { done bool variations []Iterator[*AddressSegment] nextSet []*AddressDivision segIteratorProducer, hostSegIteratorProducer func(int) Iterator[*AddressSegment] networkSegmentIndex, hostSegmentIndex int excludeFunc func([]*AddressDivision) bool } func (it *multiSegmentsIterator) HasNext() bool { return !it.done } func (it *multiSegmentsIterator) updateVariations(start int) { i := start nextSet := it.nextSet variations := it.variations segIteratorProducer := it.segIteratorProducer for ; i < it.hostSegmentIndex; i++ { variations[i] = segIteratorProducer(i) nextSet[i] = variations[i].Next().ToDiv() } if i == it.networkSegmentIndex { variations[i] = it.hostSegIteratorProducer(i) nextSet[i] = variations[i].Next().ToDiv() } } func (it *multiSegmentsIterator) init() { it.updateVariations(0) nextSet := it.nextSet variations := it.variations divCount := len(variations) hostSegIteratorProducer := it.hostSegIteratorProducer // for regular iterators (not prefix block), networkSegmentIndex is last segment (count - 1) for i := it.networkSegmentIndex + 1; i < divCount; i++ { variations[i] = hostSegIteratorProducer(i) nextSet[i] = variations[i].Next().ToDiv() } excludeFunc := it.excludeFunc if excludeFunc != nil && excludeFunc(it.nextSet) { it.increment() } } func (it *multiSegmentsIterator) Next() (res []*AddressDivision) { if it.HasNext() { res = it.increment() } return } func (it *multiSegmentsIterator) increment() (res []*AddressDivision) { var previousSegs []*AddressDivision // the current set of segments already holds the next iteration, // this searches for the set of segments to follow. variations := it.variations nextSet := it.nextSet for j := it.networkSegmentIndex; j >= 0; j-- { //for regular iterators (not prefix block), networkSegmentIndex is last segment (count - 1) for variations[j].HasNext() { if previousSegs == nil { previousSegs = cloneDivs(nextSet) } nextSet[j] = variations[j].Next().ToDiv() it.updateVariations(j + 1) excludeFunc := it.excludeFunc if excludeFunc != nil && excludeFunc(nextSet) { // try again, starting over j = it.networkSegmentIndex } else { return previousSegs } } } it.done = true if previousSegs == nil { // never found set of candidate segments return nextSet } // found a candidate to follow, but was rejected. // nextSet has that rejected candidate, // so we must return the set that was created prior to that. return previousSegs } // this iterator function used by addresses and segment arrays, for iterators that are not prefix or prefix block iterators func allSegmentsIterator( divCount int, segSupplier func() []*AddressDivision, // only useful for a segment iterator. Address/section iterators use address/section for single valued iterator. segIteratorProducer func(int) Iterator[*AddressSegment], excludeFunc func([]*AddressDivision) bool /* can be nil */) Iterator[[]*AddressDivision] { return segmentsIterator(divCount, segSupplier, segIteratorProducer, excludeFunc, divCount-1, divCount, nil) } // used to produce regular iterators with or without zero-host values, and prefix block iterators func segmentsIterator( divCount int, segSupplier func() []*AddressDivision, segIteratorProducer func(int) Iterator[*AddressSegment], // unused at this time, since we do not have a public segments iterator excludeFunc func([]*AddressDivision) bool, // can be nil networkSegmentIndex, hostSegmentIndex int, hostSegIteratorProducer func(int) Iterator[*AddressSegment]) Iterator[[]*AddressDivision] { // returns Iterator if segSupplier != nil { return &singleSegmentsIterator{segSupplier()} } iterator := &multiSegmentsIterator{ variations: make([]Iterator[*AddressSegment], divCount), nextSet: make([]*AddressDivision, divCount), segIteratorProducer: segIteratorProducer, hostSegIteratorProducer: hostSegIteratorProducer, networkSegmentIndex: networkSegmentIndex, hostSegmentIndex: hostSegmentIndex, excludeFunc: excludeFunc, } iterator.init() return iterator } // this iterator function used by sequential ranges func rangeSegmentsIterator( divCount int, segIteratorProducer func(int) Iterator[*AddressSegment], networkSegmentIndex, hostSegmentIndex int, prefixedSegIteratorProducer func(int) Iterator[*AddressSegment]) Iterator[[]*AddressDivision] { return segmentsIterator( divCount, nil, segIteratorProducer, nil, networkSegmentIndex, hostSegmentIndex, prefixedSegIteratorProducer) } type singleSectionIterator struct { original *AddressSection } func (it *singleSectionIterator) HasNext() bool { return it.original != nil } func (it *singleSectionIterator) Next() (res *AddressSection) { if it.HasNext() { res = it.original it.original = nil } return } type multiSectionIterator struct { original *AddressSection iterator Iterator[[]*AddressDivision] valsAreMultiple bool prefixLen PrefixLen } func (it *multiSectionIterator) HasNext() bool { return it.iterator.HasNext() } func (it *multiSectionIterator) Next() (res *AddressSection) { if it.HasNext() { segs := it.iterator.Next() original := it.original res = createSection(segs, it.prefixLen, original.addrType) res.isMult = it.valsAreMultiple } return } func nilSectIterator() Iterator[*AddressSection] { return &singleSectionIterator{} } func sectIterator( useOriginal bool, original *AddressSection, valsAreMultiple bool, iterator Iterator[[]*AddressDivision], ) Iterator[*AddressSection] { if useOriginal { return &singleSectionIterator{original: original} } return &multiSectionIterator{ original: original, iterator: iterator, valsAreMultiple: valsAreMultiple, prefixLen: original.getPrefixLen(), } } type prefixSectionIterator struct { original *AddressSection iterator Iterator[[]*AddressDivision] isNotFirst bool prefixLen PrefixLen } func (it *prefixSectionIterator) HasNext() bool { return it.iterator.HasNext() } func (it *prefixSectionIterator) Next() (res *AddressSection) { if it.HasNext() { segs := it.iterator.Next() original := it.original res = createSection(segs, it.prefixLen, original.addrType) if !it.isNotFirst { res.initMultiple() // sets isMultiple it.isNotFirst = true } else if !it.HasNext() { res.initMultiple() // sets isMultiple } else { res.isMult = true } } return } func prefixSectIterator( useOriginal bool, original *AddressSection, iterator Iterator[[]*AddressDivision], ) Iterator[*AddressSection] { if useOriginal { return &singleSectionIterator{original: original} } return &prefixSectionIterator{ original: original, iterator: iterator, prefixLen: original.getPrefixLen(), } } type ipSectionIterator struct { Iterator[*AddressSection] } func (iter ipSectionIterator) Next() *IPAddressSection { return iter.Iterator.Next().ToIP() } type ipv4SectionIterator struct { Iterator[*AddressSection] } func (iter ipv4SectionIterator) Next() *IPv4AddressSection { return iter.Iterator.Next().ToIPv4() } type ipv6SectionIterator struct { Iterator[*AddressSection] } func (iter ipv6SectionIterator) Next() *IPv6AddressSection { return iter.Iterator.Next().ToIPv6() } type macSectionIterator struct { Iterator[*AddressSection] } func (iter macSectionIterator) Next() *MACAddressSection { return iter.Iterator.Next().ToMAC() } ipaddress-go-1.5.4/ipaddr/segiterator.go000066400000000000000000000126051440250641600202320ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr type singleSegmentIterator struct { original *AddressSegment } func (it *singleSegmentIterator) HasNext() bool { return it.original != nil } func (it *singleSegmentIterator) Next() (res *AddressSegment) { if it.HasNext() { res = it.original.toAddressSegment() it.original = nil } return } type segmentIterator struct { done bool current, last SegInt creator segderiver segmentPrefixLength PrefixLen } func (it *segmentIterator) HasNext() bool { return !it.done } func (it *segmentIterator) Next() (res *AddressSegment) { if it.HasNext() { cur := it.current res = createAddressSegment( it.creator.deriveNewSeg( cur, it.segmentPrefixLength)) cur++ if cur > it.last { it.done = true } else { it.current = cur } } return } type segmentPrefBlockIterator struct { segmentIterator upperShiftMask SegInt shiftAdjustment BitCount } func (it *segmentPrefBlockIterator) Next() (res *AddressSegment) { if it.HasNext() { cur := it.current blockLow := cur << uint(it.shiftAdjustment) res = createAddressSegment( it.creator.deriveNewMultiSeg( blockLow, blockLow|it.upperShiftMask, it.segmentPrefixLength)) cur++ if cur > it.last { it.done = true } else { it.current = cur } } return } type segmentPrefIterator struct { segmentPrefBlockIterator originalLower, originalUpper SegInt notFirst bool } func (it *segmentPrefIterator) Next() (res *AddressSegment) { if it.HasNext() { cur := it.current blockLow := cur << uint(it.shiftAdjustment) blockHigh := blockLow | it.upperShiftMask cur++ it.current = cur var low, high SegInt if it.notFirst { low = blockLow } else { low = it.originalLower it.notFirst = true } if cur <= it.last { high = blockHigh } else { high = it.originalUpper it.done = true } res = createAddressSegment( it.creator.deriveNewMultiSeg( low, high, it.segmentPrefixLength)) } return } func nilSegIterator() Iterator[*AddressSegment] { return &singleSegmentIterator{} } func segIterator( original *addressSegmentInternal, originalLower, originalUpper SegInt, bitCount BitCount, creator segderiver, segmentPrefixLength PrefixLen, isPrefixIterator, isBlockIterator bool) Iterator[*AddressSegment] { var shiftAdjustment BitCount var shiftMask, upperShiftMask SegInt if segmentPrefixLength == nil { isPrefixIterator = false // prefixBlockIterator() in which seg has no prefix isBlockIterator = false } if isPrefixIterator { prefLen := segmentPrefixLength.bitCount() prefLen = checkBitCount(bitCount, prefLen) shiftAdjustment = bitCount - prefLen shiftMask = ^SegInt(0) << uint(shiftAdjustment) upperShiftMask = ^shiftMask } if original != nil && !original.isMultiple() { seg := original.toAddressSegment() if isBlockIterator { seg = createAddressSegment( creator.deriveNewMultiSeg( originalLower&shiftMask, originalUpper|upperShiftMask, segmentPrefixLength)) } return &singleSegmentIterator{original: seg} } if isPrefixIterator { current := originalLower >> uint(shiftAdjustment) last := originalUpper >> uint(shiftAdjustment) segIterator := segmentIterator{ current: current, last: last, creator: creator, segmentPrefixLength: segmentPrefixLength, } prefBlockIterator := segmentPrefBlockIterator{ segmentIterator: segIterator, upperShiftMask: upperShiftMask, shiftAdjustment: shiftAdjustment, } if isBlockIterator { return &prefBlockIterator } return &segmentPrefIterator{ segmentPrefBlockIterator: prefBlockIterator, originalLower: originalLower, originalUpper: originalUpper, } } return &segmentIterator{ current: originalLower, last: originalUpper, creator: creator, segmentPrefixLength: segmentPrefixLength, } } type ipSegmentIterator struct { Iterator[*AddressSegment] } func (iter ipSegmentIterator) Next() *IPAddressSegment { return iter.Iterator.Next().ToIP() } // wrappedSegmentIterator converts an IP address segment iterator to an address segment iterator type wrappedSegmentIterator[T AddressSegmentType] struct { Iterator[T] } func (iter wrappedSegmentIterator[T]) Next() *AddressSegment { return iter.Iterator.Next().ToSegmentBase() } type ipv4SegmentIterator struct { Iterator[*AddressSegment] } func (iter ipv4SegmentIterator) Next() *IPv4AddressSegment { return iter.Iterator.Next().ToIPv4() } type ipv6SegmentIterator struct { Iterator[*AddressSegment] } func (iter ipv6SegmentIterator) Next() *IPv6AddressSegment { return iter.Iterator.Next().ToIPv6() } type macSegmentIterator struct { Iterator[*AddressSegment] } func (iter macSegmentIterator) Next() *MACAddressSegment { return iter.Iterator.Next().ToMAC() } ipaddress-go-1.5.4/ipaddr/segment.go000066400000000000000000001217031440250641600173440ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math/big" "math/bits" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) // SegInt is an integer type for holding generic address segment values. It is at least as large as all address segment values: [IPv6SegInt], [IPv4SegInt], [MACSegInt]. type SegInt = uint32 // must be at least uint16 to handle IPv6, at least 32 to handle single segment IPv4, and no larger than 64 because we use bits.TrailingZeros64. IP address segment code uses bits.TrailingZeros32 and bits.LeadingZeros32, so it cannot be larger than 32. const SegIntSize = 32 // must match the bit count of SegInt type SegIntCount = uint64 // must be able to hold: (max value of SegInt) + 1 type segderiver interface { // deriveNew produces a new segment with the same bit count as the old deriveNewMultiSeg(val, upperVal SegInt, prefLen PrefixLen) divisionValues // deriveNew produces a new segment with the same bit count as the old deriveNewSeg(val SegInt, prefLen PrefixLen) divisionValues } type segmentValues interface { // getSegmentValue gets the lower value for a segment getSegmentValue() SegInt // getUpperSegmentValue gets the upper value for a segment getUpperSegmentValue() SegInt } // compareSegInt returns a negative number, 0 or a positive number if integer one is less than, equal to, or greater than integer two. func compareSegInt(one, two SegInt) int { if one < two { return -1 } else if one > two { return 1 } return 0 } func createAddressSegment(vals divisionValues) *AddressSegment { return &AddressSegment{ addressSegmentInternal{ addressDivisionInternal{ addressDivisionBase{ vals, }, }, }, } } type addressSegmentInternal struct { addressDivisionInternal } func (seg *addressSegmentInternal) sameTypeContains(otherSeg *AddressSegment) bool { return otherSeg.GetSegmentValue() >= seg.GetSegmentValue() && otherSeg.GetUpperSegmentValue() <= seg.GetUpperSegmentValue() } func (seg *addressSegmentInternal) contains(other AddressSegmentType) bool { if other == nil { return true } otherSeg := other.ToSegmentBase() if seg.toAddressSegment() == otherSeg || otherSeg == nil { return true } else if matchesStructure, _ := seg.matchesStructure(other); matchesStructure { return seg.sameTypeContains(otherSeg) } return false } func (seg *addressSegmentInternal) equal(other AddressSegmentType) bool { if other == nil || other.ToSegmentBase() == nil { return false } if seg.isMultiple() { if other.IsMultiple() { matches, _ := seg.matchesStructure(other) otherDivision := other.ToSegmentBase() return matches && segValsSame(seg.getSegmentValue(), otherDivision.getSegmentValue(), seg.getUpperSegmentValue(), otherDivision.getUpperSegmentValue()) } else { return false } } else if other.IsMultiple() { return false } matches, _ := seg.matchesStructure(other) otherDivision := other.ToSegmentBase() return matches && segValSame(seg.GetSegmentValue(), otherDivision.GetSegmentValue()) } func (seg *addressSegmentInternal) equalsSegment(other *AddressSegment) bool { matchesStructure, _ := seg.matchesStructure(other) return matchesStructure && seg.sameTypeEquals(other) } func (seg *addressSegmentInternal) sameTypeEquals(other *AddressSegment) bool { if seg.isMultiple() { return other.isMultiple() && segValsSame(seg.getSegmentValue(), other.getSegmentValue(), seg.getUpperSegmentValue(), other.getUpperSegmentValue()) } return !other.isMultiple() && seg.getSegmentValue() == other.getSegmentValue() } // PrefixContains returns whether the prefix values in the prefix of the given segment are also prefix values in this segment. // It returns whether the prefix of this segment contains the prefix of the given segment. func (seg *addressSegmentInternal) PrefixContains(other AddressSegmentType, prefixLength BitCount) bool { prefixLength = checkBitCount(prefixLength, seg.GetBitCount()) shift := seg.GetBitCount() - prefixLength if shift <= 0 { return seg.contains(other) } return (other.GetSegmentValue()>>uint(shift)) >= (seg.GetSegmentValue()>>uint(shift)) && (other.GetUpperSegmentValue()>>uint(shift)) <= (seg.GetUpperSegmentValue()>>uint(shift)) } // PrefixEqual returns whether the prefix bits of this segment match the same bits of the given segment. // It returns whether the two segments share the same range of prefix values using the given prefix length. func (seg *addressSegmentInternal) PrefixEqual(other AddressSegmentType, prefixLength BitCount) bool { prefixLength = checkBitCount(prefixLength, seg.GetBitCount()) shift := seg.GetBitCount() - prefixLength if shift <= 0 { return seg.GetSegmentValue() == other.GetSegmentValue() && seg.GetUpperSegmentValue() == other.GetUpperSegmentValue() } return (other.GetSegmentValue()>>uint(shift)) == (seg.GetSegmentValue()>>uint(shift)) && (other.GetUpperSegmentValue()>>uint(shift)) == (seg.GetUpperSegmentValue()>>uint(shift)) } func (seg *addressSegmentInternal) toAddressSegment() *AddressSegment { return (*AddressSegment)(unsafe.Pointer(seg)) } // GetSegmentValue returns the lower value of the segment value range. func (seg *addressSegmentInternal) GetSegmentValue() SegInt { vals := seg.divisionValues if vals == nil { return 0 } return vals.getSegmentValue() } // GetUpperSegmentValue returns the upper value of the segment value range. func (seg *addressSegmentInternal) GetUpperSegmentValue() SegInt { vals := seg.divisionValues if vals == nil { return 0 } return vals.getUpperSegmentValue() } // Matches returns true if the segment range matches the given single value. func (seg *addressSegmentInternal) Matches(value SegInt) bool { return seg.matches(DivInt(value)) } // MatchesWithMask applies the mask to this segment and then compares the result with the given value, // returning true if the range of the resulting segment matches that single value. func (seg *addressSegmentInternal) MatchesWithMask(value, mask SegInt) bool { return seg.matchesWithMask(DivInt(value), DivInt(mask)) } // MatchesValsWithMask applies the mask to this segment and then compares the result with the given values, // returning true if the range of the resulting segment matches the given range. func (seg *addressSegmentInternal) MatchesValsWithMask(lowerValue, upperValue, mask SegInt) bool { return seg.matchesValsWithMask(DivInt(lowerValue), DivInt(upperValue), DivInt(mask)) } // GetPrefixCountLen returns the count of the number of distinct prefix values for the given prefix length in the range of values of this segment. func (seg *addressSegmentInternal) GetPrefixCountLen(segmentPrefixLength BitCount) *big.Int { return bigZero().SetUint64(seg.GetPrefixValueCountLen(segmentPrefixLength)) } // GetPrefixValueCountLen returns the same value as GetPrefixCountLen as an integer. func (seg *addressSegmentInternal) GetPrefixValueCountLen(segmentPrefixLength BitCount) SegIntCount { return getPrefixValueCount(seg.toAddressSegment(), segmentPrefixLength) } // GetValueCount returns the same value as GetCount as an integer. func (seg *addressSegmentInternal) GetValueCount() SegIntCount { return uint64(seg.GetUpperSegmentValue()-seg.GetSegmentValue()) + 1 } // GetMaxValue gets the maximum possible value for this type or version of segment, determined by the number of bits. // // For the highest range value of this particular segment, use GetUpperSegmentValue. func (seg *addressSegmentInternal) GetMaxValue() SegInt { return ^(^SegInt(0) << uint(seg.GetBitCount())) } // TestBit returns true if the bit in the lower value of this segment at the given index is 1, where index 0 refers to the least significant bit. // In other words, it computes (bits & (1 << n)) != 0), using the lower value of this section. // TestBit will panic if n < 0, or if it matches or exceeds the bit count of this item. func (seg *addressSegmentInternal) TestBit(n BitCount) bool { value := seg.GetSegmentValue() if n < 0 || n > seg.GetBitCount() { panic("invalid bit index") } return (value & (1 << uint(n))) != 0 } // IsOneBit returns true if the bit in the lower value of this segment at the given index is 1, where index 0 refers to the most significant bit. // IsOneBit will panic if bitIndex is less than zero, or if it is larger than the bit count of this item. func (seg *addressSegmentInternal) IsOneBit(segmentBitIndex BitCount) bool { value := seg.GetSegmentValue() bitCount := seg.GetBitCount() if segmentBitIndex < 0 || segmentBitIndex > seg.GetBitCount() { panic("invalid bit index") } return (value & (1 << uint(bitCount-(segmentBitIndex+1)))) != 0 } func (seg *addressSegmentInternal) getLower() *AddressSegment { if !seg.isMultiple() { return seg.toAddressSegment() } vals := seg.divisionValues var newVals divisionValues if vals != nil { newVals = seg.deriveNewMultiSeg(seg.GetSegmentValue(), seg.GetSegmentValue(), seg.getDivisionPrefixLength()) } return createAddressSegment(newVals) } func (seg *addressSegmentInternal) getUpper() *AddressSegment { if !seg.isMultiple() { return seg.toAddressSegment() } vals := seg.divisionValues var newVals divisionValues if vals != nil { newVals = seg.deriveNewMultiSeg(seg.GetUpperSegmentValue(), seg.GetUpperSegmentValue(), seg.getDivisionPrefixLength()) } return createAddressSegment(newVals) } func (seg *addressSegmentInternal) withoutPrefixLen() *AddressSegment { if seg.isPrefixed() { return createAddressDivision(seg.derivePrefixed(nil)).ToSegmentBase() } return seg.toAddressSegment() } func (seg *addressSegmentInternal) getDefaultSegmentWildcardString() string { return SegmentWildcardStr } func (seg *addressSegmentInternal) iterator() Iterator[*AddressSegment] { return seg.segmentIterator(seg.getDivisionPrefixLength(), false, false) } func (seg *addressSegmentInternal) identityIterator() Iterator[*AddressSegment] { return &singleSegmentIterator{original: seg.toAddressSegment()} } func (seg *addressSegmentInternal) prefixBlockIterator() Iterator[*AddressSegment] { return seg.segmentIterator(seg.getDivisionPrefixLength(), true, true) } func (seg *addressSegmentInternal) prefixedBlockIterator(segPrefLen BitCount) Iterator[*AddressSegment] { return seg.segmentIterator(cacheBitCount(segPrefLen), true, true) } func (seg *addressSegmentInternal) prefixIterator() Iterator[*AddressSegment] { return seg.segmentIterator(seg.getDivisionPrefixLength(), true, false) } func (seg *addressSegmentInternal) prefixedIterator(segPrefLen BitCount) Iterator[*AddressSegment] { return seg.segmentIterator(cacheBitCount(segPrefLen), true, false) } func (seg *addressSegmentInternal) segmentIterator(segPrefLen PrefixLen, isPrefixIterator, isBlockIterator bool) Iterator[*AddressSegment] { vals := seg.divisionValues if vals == nil { return segIterator(seg, 0, 0, 0, nil, nil, false, false, ) } return segIterator(seg, seg.getSegmentValue(), seg.getUpperSegmentValue(), seg.getBitCount(), vals, segPrefLen, isPrefixIterator, isBlockIterator, ) } // GetLeadingBitCount returns the number of consecutive leading one or zero bits. // If ones is true, returns the number of consecutive leading one bits. // Otherwise, returns the number of consecutive leading zero bits. // // This method applies only to the lower value of the range if this segment represents multiple values. func (seg *addressSegmentInternal) GetLeadingBitCount(ones bool) BitCount { extraLeading := 32 - seg.GetBitCount() val := seg.GetSegmentValue() if ones { //leading ones return BitCount(bits.LeadingZeros32(uint32(^val&seg.GetMaxValue()))) - extraLeading } // leading zeros return BitCount(bits.LeadingZeros32(uint32(val))) - extraLeading } // GetTrailingBitCount returns the number of consecutive trailing one or zero bits. // If ones is true, returns the number of consecutive trailing zero bits. // Otherwise, returns the number of consecutive trailing one bits. // // This method applies only to the lower value of the range if this segment represents multiple values. func (seg *addressSegmentInternal) GetTrailingBitCount(ones bool) BitCount { val := seg.GetSegmentValue() if ones { // trailing ones return BitCount(bits.TrailingZeros32(uint32(^val))) } //trailing zeros bitCount := uint(seg.GetBitCount()) return BitCount(bits.TrailingZeros32(uint32(val | (1 << bitCount)))) } // GetSegmentNetworkMask returns a value comprising the same number of total bits as the bit-length of this segment, // the value that is all one-bits for the given number of bits followed by all zero-bits. func (seg *addressSegmentInternal) GetSegmentNetworkMask(networkBits BitCount) SegInt { bitCount := seg.GetBitCount() networkBits = checkBitCount(networkBits, bitCount) return seg.GetMaxValue() & (^SegInt(0) << uint(bitCount-networkBits)) } // GetSegmentHostMask returns a value comprising the same number of total bits as the bit-length of this segment, // the value that is all zero-bits for the given number of bits followed by all one-bits. func (seg *addressSegmentInternal) GetSegmentHostMask(networkBits BitCount) SegInt { bitCount := seg.GetBitCount() networkBits = checkBitCount(networkBits, bitCount) return ^(^SegInt(0) << uint(bitCount-networkBits)) } var ( // wildcards differ, for divs we use only range since div size not implicit, here we use both range and * hexParamsSeg = new(addrstr.IPStringOptionsBuilder).SetRadix(16).SetSegmentStrPrefix(HexPrefix).ToOptions() decimalParamsSeg = new(addrstr.IPStringOptionsBuilder).SetRadix(10).ToOptions() ) // We do not need to "override" ToNormalizedString() and ToHexString(bool) because neither prints leading zeros according to bit count, so zero-segments of type IPv4/IPv6/MAC are printed consistently // ToNormalizedString produces a string that is consistent for all address segments of the same type and version. // IPv4 segments use base 10, while other segment types use base 16. func (seg *addressSegmentInternal) ToNormalizedString() string { stringer := func() string { switch seg.getDefaultTextualRadix() { case 10: return seg.toStringOpts(decimalParamsSeg) default: return seg.toStringOpts(macCompressedParams) } } if seg.divisionValues != nil { if cache := seg.getCache(); cache != nil { return cacheStr(&cache.cachedNormalizedString, stringer) } } return stringer() } // ToHexString writes this address segment as a single hexadecimal value (possibly two values if a range that is not a prefixed block), // the number of digits according to the bit count, with or without a preceding "0x" prefix. // // For segments, the error is always nil. func (seg *addressSegmentInternal) ToHexString(with0xPrefix bool) (string, addrerr.IncompatibleAddressError) { var stringer func() string if with0xPrefix { stringer = func() string { return seg.toStringOpts(hexParamsSeg) } } else { stringer = func() string { return seg.toStringOpts(macCompressedParams) } } if seg.divisionValues != nil { if cache := seg.getCache(); cache != nil { if with0xPrefix { return cacheStr(&cache.cached0xHexString, stringer), nil } return cacheStr(&cache.cachedHexString, stringer), nil } } return stringer(), nil } func (seg *addressSegmentInternal) reverseMultiValSeg(perByte bool) (res *AddressSegment, err addrerr.IncompatibleAddressError) { if isReversible := seg.isReversibleRange(perByte); isReversible { // all reversible multi-valued segs reverse to the same segment res = seg.withoutPrefixLen() return } err = &incompatibleAddressError{addressError{key: "ipaddress.error.reverseRange"}} return } // ReverseBits returns a segment with the bits reversed. // // If this segment represents a range of values that cannot be reversed, then this returns an error. // // To be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // Otherwise the result is not contiguous and thus cannot be represented by a sequential range of values. // // If perByte is true, the bits are reversed within each byte, otherwise all the bits are reversed. func (seg *addressSegmentInternal) ReverseBits(perByte bool) (res *AddressSegment, err addrerr.IncompatibleAddressError) { if seg.divisionValues == nil { res = seg.toAddressSegment() return } if seg.isMultiple() { return seg.reverseMultiValSeg(perByte) } byteCount := seg.GetByteCount() oldVal := seg.GetSegmentValue() var val SegInt switch byteCount { case 1: val = SegInt(reverseUint8(uint8(oldVal))) case 2: val = SegInt(reverseUint16(uint16(oldVal))) if perByte { val = ((val & 0xff) << 8) | (val >> 8) } case 3: val = reverseUint32(uint32(oldVal)) >> 8 if perByte { val = ((val & 0xff) << 16) | (val & 0xff00) | (val >> 16) } case 4: val = reverseUint32(uint32(oldVal)) if perByte { val = ((val & 0xff) << 24) | (val&0xff00)<<8 | (val&0xff0000)>>8 | (val >> 24) } default: // SegInt is at most 32 bits so this default case is not possible err = &incompatibleAddressError{addressError{key: "ipaddress.error.reverseRange"}} return } if oldVal == val && !seg.isPrefixed() { res = seg.toAddressSegment() } else { res = createAddressSegment(seg.deriveNewSeg(val, nil)) } return } // ReverseBytes returns a segment with the bytes reversed. // // If this segment represents a range of values that cannot be reversed, then this returns an error. // // To be reversible, a range must include all values except possibly the largest and/or smallest, which reverse to themselves. // Otherwise the result is not contiguous and thus cannot be represented by a sequential range of values. func (seg *addressSegmentInternal) ReverseBytes() (res *AddressSegment, err addrerr.IncompatibleAddressError) { byteCount := seg.GetByteCount() if byteCount <= 1 { res = seg.toAddressSegment() return } if seg.isMultiple() { return seg.reverseMultiValSeg(false) } oldVal := seg.GetSegmentValue() var val SegInt switch byteCount { case 2: val = ((oldVal & 0xff) << 8) | (oldVal >> 8) case 3: val = ((oldVal & 0xff) << 16) | (oldVal & 0xff00) | (oldVal >> 16) case 4: val = ((oldVal & 0xff) << 24) | (oldVal&0xff00)<<8 | (oldVal&0xff0000)>>8 | (oldVal >> 24) default: // SegInt is at most 32 bits so this default case is not possible err = &incompatibleAddressError{addressError{key: "ipaddress.error.reverseRange"}} return } if oldVal == val && !seg.isPrefixed() { res = seg.toAddressSegment() } else { res = createAddressSegment(seg.deriveNewSeg(val, nil)) } return } func (seg *addressSegmentInternal) isReversibleRange(perByte bool) (isReversible bool) { // Consider the case of reversing the bits of a range // Any range that can be successfully reversed must span all bits (otherwise after flipping you'd have a range in which the lower bit is constant, which is impossible in any contiguous range) // So that means at least one value has 0xxxx and another has 1xxxx (using 5 bits for our example). This means you must have the values 01111 and 10000 since the range is contiguous. // But reversing a range twice results in the original again, meaning the reversed must also be reversible, so the reversed also has 01111 and 10000. // So this means both the original and the reversed also have those two patterns flipped, which are 00001 and 11110. // So this means both ranges must span from at most 1 to at least 11110. // However, the two remaining values, 0 and 11111, are optional, as they are boundary value and remain themselves when reversed, and hence have no effect on whether the reversed range is contiguous. // So the only reversible ranges are 0-11111, 0-11110, 1-11110, and 1-11111. //----------------------- // Consider the case of reversing each of the bytes of a range. // // You can apply the same argument to the top multiple byte, // which means it is 0 or 1 to 254 or 255. // Suppose there is another byte to follow. // If you take the upper byte range, and you hold it constant, then reversing the next byte applies the same argument to that byte. // And so the lower byte must span from at most 1 to at least 11111110. // This argument holds when holding the upper byte constant at any value. // So the lower byte must span from at most 1 to at least 111111110 for any value. // So you have x 00000001-x 111111110 and y 00000001-y 111111110 and so on. // But all the bytes form a range, so you must also have the values in-between. // So that means you have 1 00000001 to 1 111111110 to 10 111111110 to 11 111111110 all the way to x 11111110, where x is at least 11111110. // In all cases, the upper byte lower value is at most 1, and 1 < 10000000. // That means you always have 10000000 00000000. // So you have the reverse as well (as argued above, for any value we also have the reverse). // So you always have 00000001 00000000. // // In other words, if the upper byte has lower 0, then the full bytes lower must be at most 0 00000001 // Otherwise, when the upper byte has lower 1, the the full bytes lower is at most 1 00000000. // // In other words, if any upper byte has lower value 1, then all lower values to follow are 0. // If all upper bytes have lower value 0, then the next byte is permitted to have lower value 1. // // In summary, any upper byte having lower of 1 forces the remaining lower values to be 0. // // WHen the upper bytes are all zero, and thus the lower is at most 0 0 0 0 1, // then the only remaining lower value is 0 0 0 0 0. This reverses to itself, so it is optional. // // The same argument applies to upper boundaries. // //----------------------- // Consider the case of reversing the bytes of a range. // Any range that can be successfully reversed must span all bits // (otherwise after flipping you'd have a range in which a lower bit is constant, which is impossible in any contiguous range) // So that means at least one value has 0xxxxx and another has 1xxxxx (we use 6 bits for our example, and we assume each byte has 3 bits). // This means you must have the values 011111 and 100000 since the range is contiguous. // But reversing a range twice results in the original again, meaning the reversed must also be reversible, so the reversed also has 011111 and 100000. // So this means both the original and the reversed also have those two bytes in each flipped, which are 111011 and 000100. // So the range must have 000100, 011111, 100000, 111011, so it must be at least 000100 to 111011. // So what if the range does not have 000001? then the reversed range cannot have 001000, the byte-reversed address. // But we know it spans 000100 to 111011. So the original must have 000001. // What if it does not have 111110? Then the reversed cannot have 110111, the byte-reversed address. // But we know it ranges from 000100 to 111011. So the original must have 111110. // So it must range from 000001 to 111110. The only remaining values in question are 000000 and 111111. // But once again, the two remaining values are optional, because the byte-reverse to themselves. // So for the byte-reverse case, we have the same potential ranges as in the bit-reverse case: 0-111111, 0-111110, 1-111110, and 1-111111 if perByte { byteCount := seg.GetByteCount() bitCount := seg.GetBitCount() val := seg.GetSegmentValue() upperVal := seg.GetUpperSegmentValue() for i := 1; i <= byteCount; i++ { bitShift := i << 3 shift := bitCount - BitCount(bitShift) byteVal := val >> uint(shift) upperByteVal := upperVal >> uint(shift) if byteVal != upperByteVal { if byteVal > 1 || upperByteVal < 254 { return false } i++ if i <= byteCount { lowerIsZero := byteVal == 1 upperIsMax := upperByteVal == 254 for { bitShift = i << 3 shift = bitCount - BitCount(bitShift) byteVal = val >> uint(shift) upperByteVal = upperVal >> uint(shift) if lowerIsZero { if byteVal != 0 { return } } else { if byteVal > 1 { return } lowerIsZero = byteVal == 1 } if upperIsMax { if upperByteVal != 255 { return } } else { if upperByteVal < 254 { return } upperIsMax = upperByteVal == 254 } i++ if i > byteCount { break } } } return true } } return true } isReversible = seg.GetSegmentValue() <= 1 && seg.GetUpperSegmentValue() >= seg.GetMaxValue()-1 return } //// only needed for godoc / pkgsite // GetBitCount returns the number of bits in each value comprising this address item. func (seg *addressSegmentInternal) GetBitCount() BitCount { return seg.addressDivisionInternal.GetBitCount() } // GetByteCount returns the number of bytes required for each value comprising this address item. func (seg *addressSegmentInternal) GetByteCount() int { return seg.addressDivisionInternal.GetByteCount() } // GetValue returns the lowest value in the address segment range as a big integer. func (seg *addressSegmentInternal) GetValue() *BigDivInt { return seg.addressDivisionInternal.GetValue() } // GetUpperValue returns the highest value in the address segment range as a big integer. func (seg *addressSegmentInternal) GetUpperValue() *BigDivInt { return seg.addressDivisionInternal.GetUpperValue() } // Bytes returns the lowest value in the address segment range as a byte slice. func (seg *addressSegmentInternal) Bytes() []byte { return seg.addressDivisionInternal.Bytes() } // UpperBytes returns the highest value in the address segment range as a byte slice. func (seg *addressSegmentInternal) UpperBytes() []byte { return seg.addressDivisionInternal.UpperBytes() } // CopyBytes copies the lowest value in the address segment range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (seg *addressSegmentInternal) CopyBytes(bytes []byte) []byte { return seg.addressDivisionInternal.CopyBytes(bytes) } // CopyUpperBytes copies the highest value in the address segment range into a byte slice. // // If the value can fit in the given slice, the value is copied into that slice and a length-adjusted sub-slice is returned. // Otherwise, a new slice is created and returned with the value. func (seg *addressSegmentInternal) CopyUpperBytes(bytes []byte) []byte { return seg.addressDivisionInternal.CopyUpperBytes(bytes) } // IsZero returns whether this segment matches exactly the value of zero. func (seg *addressSegmentInternal) IsZero() bool { return seg.addressDivisionInternal.IsZero() } // IncludesZero returns whether this segment includes the value of zero within its range. func (seg *addressSegmentInternal) IncludesZero() bool { return seg.addressDivisionInternal.IncludesZero() } // IsMax returns whether this segment matches exactly the maximum possible value, the value whose bits are all ones. func (seg *addressSegmentInternal) IsMax() bool { return seg.addressDivisionInternal.IsMax() } // IncludesMax returns whether this segment includes the max value, the value whose bits are all ones, within its range. func (seg *addressSegmentInternal) IncludesMax() bool { return seg.addressDivisionInternal.IncludesMax() } // IsFullRange returns whether the segment range includes all possible values for its bit length. // // This is true if and only if both IncludesZero and IncludesMax return true. func (seg *addressSegmentInternal) IsFullRange() bool { return seg.addressDivisionInternal.IsFullRange() } // ContainsPrefixBlock returns whether the segment range includes the block of values for the given prefix length. func (seg *addressSegmentInternal) ContainsPrefixBlock(prefixLen BitCount) bool { return seg.addressDivisionInternal.ContainsPrefixBlock(prefixLen) } // ContainsSinglePrefixBlock returns whether the segment range matches exactly the block of values for the given prefix length and has just a single prefix for that prefix length. func (seg *addressSegmentInternal) ContainsSinglePrefixBlock(prefixLen BitCount) bool { return seg.addressDivisionInternal.ContainsSinglePrefixBlock(prefixLen) } // GetMinPrefixLenForBlock returns the smallest prefix length such that this segment includes the block of all values for that prefix length. // // If the entire range can be described this way, then this method returns the same value as GetPrefixLenForSingleBlock. // // There may be a single prefix, or multiple possible prefix values in this item for the returned prefix length. // Use GetPrefixLenForSingleBlock to avoid the case of multiple prefix values. // // If this segment represents a single value, this returns the bit count. func (seg *addressSegmentInternal) GetMinPrefixLenForBlock() BitCount { return seg.addressDivisionInternal.GetMinPrefixLenForBlock() } // GetPrefixLenForSingleBlock returns a prefix length for which there is only one prefix in this segment, // and the range of values in this segment matches the block of all values for that prefix. // // If the range of segment values can be described this way, then this method returns the same value as GetMinPrefixLenForBlock. // // If no such prefix length exists, returns nil. // // If this segment represents a single value, this returns the bit count of the segment. func (seg *addressSegmentInternal) GetPrefixLenForSingleBlock() PrefixLen { return seg.addressDivisionInternal.GetPrefixLenForSingleBlock() } // IsSinglePrefix determines if the segment has a single prefix value for the given prefix length. You can call GetPrefixCountLen to get the count of prefixes. func (seg *addressSegmentInternal) IsSinglePrefix(divisionPrefixLength BitCount) bool { return seg.addressDivisionInternal.IsSinglePrefix(divisionPrefixLength) } //// end needed for godoc / pkgsite // // AddressSegment represents a single segment of an address. A segment contains a single value or a range of sequential values and it has an assigned bit length. // // The current implementations of this type are the most common representations of IPv4, IPv6 and MAC; // segments are 1 byte for Ipv4, they are two bytes for Ipv6, and they are 1 byte for MAC addresses. // // There are alternative forms of dividing addresses into divisions, such as the dotted representation for MAC like "1111.2222.3333", // the embedded IPv4 representation for IPv6 like "f:f:f:f:f:f:1.2.3.4", the inet_aton formats like "1.2" for IPv4, and so on. // // The general rules are that segments have a whole number of bytes, and in a given address all segments have the same length. // // When alternatives forms do not follow the general rules for segments, you can use [AddressDivision] instead. // Divisions do not have the restriction that divisions of an address are equal length and a whole number of bytes. // Divisions can be grouped using [AddressDivisionGrouping]. // // AddressSegment objects are immutable and thus are also concurrency-safe. type AddressSegment struct { addressSegmentInternal } // Contains returns whether this is same type and version as the given segment and whether it contains all values in the given segment. func (seg *AddressSegment) Contains(other AddressSegmentType) bool { if seg == nil { return other == nil || other.ToSegmentBase() == nil } return seg.contains(other) } // Equal returns whether the given segment is equal to this segment. // Two segments are equal if they match: // - type/version (IPv4, IPv6, MAC) // - value range // Prefix lengths are ignored. func (seg *AddressSegment) Equal(other AddressSegmentType) bool { if seg == nil { return other == nil || other.ToDiv() == nil } return seg.equal(other) } // Compare returns a negative integer, zero, or a positive integer if this address segment is less than, equal, or greater than the given item. // Any address item is comparable to any other. All address items use CountComparator to compare. func (seg *AddressSegment) Compare(item AddressItem) int { return CountComparator.Compare(seg, item) } // CompareSize compares the counts of two items, the number of individual values within. // // Rather than calculating counts with GetCount, there can be more efficient ways of determining whether one represents more individual values than another. // // CompareSize returns a positive integer if this segment has a larger count than the item given, zero if they are the same, or a negative integer if the other has a larger count. func (seg *AddressSegment) CompareSize(other AddressItem) int { if seg == nil { if isNilItem(other) { return 0 } // we have size 0, other has size >= 1 return -1 } return seg.compareSize(other) } // GetLower returns a segment representing just the lowest value in the range, which will be the same segment if it represents a single value. func (seg *addressSegmentInternal) GetLower() *AddressSegment { return seg.getLower() } // GetUpper returns a segment representing just the highest value in the range, which will be the same segment if it represents a single value. func (seg *addressSegmentInternal) GetUpper() *AddressSegment { return seg.getUpper() } // IsMultiple returns whether this segment represents multiple values. func (seg *AddressSegment) IsMultiple() bool { return seg != nil && seg.isMultiple() } // GetCount returns the count of possible distinct values for this item. // If not representing multiple values, the count is 1. // // For instance, a segment with the value range of 3-7 has count 5. // // Use IsMultiple if you simply want to know if the count is greater than 1. func (seg *AddressSegment) GetCount() *big.Int { if seg == nil { return bigZero() } return seg.getCount() } // IsIP returns true if this segment originated as an IPv4 or IPv6 segment, or an implicitly zero-valued IP segment. If so, use ToIP to convert back to the IP-specific type. func (seg *AddressSegment) IsIP() bool { return seg != nil && seg.matchesIPSegment() } // IsIPv4 returns true if this segment originated as an IPv4 segment. If so, use ToIPv4 to convert back to the IPv4-specific type. func (seg *AddressSegment) IsIPv4() bool { return seg != nil && seg.matchesIPv4Segment() } // IsIPv6 returns true if this segment originated as an IPv6 segment. If so, use ToIPv6 to convert back to the IPv6-specific type. func (seg *AddressSegment) IsIPv6() bool { return seg != nil && seg.matchesIPv6Segment() } // IsMAC returns true if this segment originated as a MAC segment. If so, use ToMAC to convert back to the MAC-specific type. func (seg *AddressSegment) IsMAC() bool { return seg != nil && seg.matchesMACSegment() } // Iterator provides an iterator to iterate through the individual address segments of this address segment. // // Call IsMultiple to determine if this instance represents multiple address segments, or GetValueCount for the count. func (seg *AddressSegment) Iterator() Iterator[*AddressSegment] { if seg == nil { return nilSegIterator() } return seg.iterator() } // ToIP converts to an IPAddressSegment if this division originated as an IPv4 or IPv6 segment, or an implicitly zero-valued IP segment. // If not, ToIP returns nil. // // ToIP can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *AddressSegment) ToIP() *IPAddressSegment { if seg.IsIP() { return (*IPAddressSegment)(unsafe.Pointer(seg)) } return nil } // ToIPv4 converts to an IPv4AddressSegment if this segment originated as an IPv4 segment. // If not, ToIPv4 returns nil. // // ToIPv4 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *AddressSegment) ToIPv4() *IPv4AddressSegment { if seg.IsIPv4() { return (*IPv4AddressSegment)(unsafe.Pointer(seg)) } return nil } // ToIPv6 converts to an IPv6AddressSegment if this segment originated as an IPv6 segment. // If not, ToIPv6 returns nil. // // ToIPv6 can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *AddressSegment) ToIPv6() *IPv6AddressSegment { if seg.IsIPv6() { return (*IPv6AddressSegment)(unsafe.Pointer(seg)) } return nil } // ToMAC converts to a MACAddressSegment if this segment originated as a MAC segment. // If not, ToMAC returns nil. // // ToMAC can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *AddressSegment) ToMAC() *MACAddressSegment { if seg.IsMAC() { return (*MACAddressSegment)(seg) } return nil } // ToSegmentBase is an identity method. // // ToSegmentBase can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *AddressSegment) ToSegmentBase() *AddressSegment { return seg } // ToDiv converts to an AddressDivision, a polymorphic type usable with all address segments and divisions. // Afterwards, you can convert back with ToSegmentBase. // // ToDiv can be called with a nil receiver, enabling you to chain this method with methods that might return a nil pointer. func (seg *AddressSegment) ToDiv() *AddressDivision { return (*AddressDivision)(unsafe.Pointer(seg)) } // GetString produces a normalized string to represent the segment. // If the segment is an IP segment string with CIDR network prefix block for its prefix length, then the string contains only the lower value of the block range. // Otherwise, the explicit range will be printed. // If the segment is not an IP segment, then the string is the same as that produced by GetWildcardString. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (seg *AddressSegment) GetString() string { if seg == nil { return nilString() } return seg.getString() } // GetWildcardString produces a normalized string to represent the segment, favouring wildcards and range characters while ignoring any network prefix length. // The explicit range of a range-valued segment will be printed. // // The string returned is useful in the context of creating strings for address sections or full addresses, // in which case the radix and the bit-length can be deduced from the context. // The String method produces strings more appropriate when no context is provided. func (seg *AddressSegment) GetWildcardString() string { if seg == nil { return nilString() } return seg.getWildcardString() } // String produces a string that is useful when a segment string is provided with no context. // If the segment was originally constructed as an IPv4 address segment it uses decimal, otherwise hexadecimal. // It uses a string prefix for hex ("0x"), and does not use the wildcard '*', because division size is variable, so '*' is ambiguous. // GetWildcardString is more appropriate in context with other segments or divisions. It does not use a string prefix and uses '*' for full-range segments. // GetString is more appropriate in context with prefix lengths, it uses zeros instead of wildcards with full prefix block ranges alongside prefix lengths. func (seg *AddressSegment) String() string { if seg == nil { return nilString() } return seg.toString() } func segsSame(onePref, twoPref PrefixLen, oneVal, twoVal, oneUpperVal, twoUpperVal SegInt) bool { return onePref.Equal(twoPref) && oneVal == twoVal && oneUpperVal == twoUpperVal } func segValsSame(oneVal, twoVal, oneUpperVal, twoUpperVal SegInt) bool { return oneVal == twoVal && oneUpperVal == twoUpperVal } func segValSame(oneVal, twoVal SegInt) bool { return oneVal == twoVal } func getPrefixValueCount(segment *AddressSegment, segmentPrefixLength BitCount) SegIntCount { shiftAdjustment := segment.GetBitCount() - segmentPrefixLength if shiftAdjustment <= 0 { return SegIntCount(segment.GetUpperSegmentValue()) - SegIntCount(segment.GetSegmentValue()) + 1 } return SegIntCount(segment.GetUpperSegmentValue()>>uint(shiftAdjustment)) - SegIntCount(segment.GetSegmentValue()>>uint(shiftAdjustment)) + 1 } func getSegmentPrefLen( _ AddressSegmentSeries, prefLen PrefixLen, bitsPerSegment, bitsMatchedSoFar BitCount, segment *AddressSegment) PrefixLen { if ipSeg := segment.ToIP(); ipSeg != nil { return ipSeg.GetSegmentPrefixLen() } else if prefLen != nil { result := prefLen.Len() - bitsMatchedSoFar if result <= bitsPerSegment { if result < 0 { result = 0 } return cacheBitCount(result) } } return nil } func getMatchingBits(segment1, segment2 *AddressSegment, maxBits, bitsPerSegment BitCount) BitCount { if maxBits == 0 { return 0 } val1 := segment1.getSegmentValue() val2 := segment2.getSegmentValue() xor := val1 ^ val2 switch bitsPerSegment { case IPv4BitsPerSegment: return BitCount(bits.LeadingZeros8(uint8(xor))) case IPv6BitsPerSegment: return BitCount(bits.LeadingZeros16(uint16(xor))) default: return BitCount(bits.LeadingZeros32(xor)) - 32 + bitsPerSegment } } ipaddress-go-1.5.4/ipaddr/split.go000066400000000000000000000316061440250641600170370ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "container/list" "math/bits" ) // TODO LATER change to generics , this also allows us to possibly avoid the slice copy with the return slice // So, what was I thinking at the time? Hate it when I do that, write a to-do without enough details. // I think I wanted the ExtendedIPSegmentSeries slice to be generic somehow. // So, the idea I think, is that we can use the trick we used in tries, with [T TrieConstraint[T]], allowing us to specify methods like ToPrefixBlock T // So, then we can return []T. Overall this is a bit of work. // In fact, when you think about it, generics can give you all the same things as ExtendedIPSegmentSeries // getSpanningPrefixBlocks returns the smallest set of prefix blocks that spans both this and the supplied address or subnet. func getSpanningPrefixBlocks( first, other ExtendedIPSegmentSeries, ) []ExtendedIPSegmentSeries { result := checkPrefixBlockContainment(first, other) if result != nil { return wrapNonNilInSlice(result) } return applyOperatorToLowerUpper( first, other, true, splitIntoPrefixBlocks) } func getSpanningSequentialBlocks( // TODO LATER change to generics , this also allows us to possibly avoid the slice copy with the return slice first, other ExtendedIPSegmentSeries, ) []ExtendedIPSegmentSeries { result := checkSequentialBlockContainment(first, other) if result != nil { return wrapNonNilInSlice(result) } return applyOperatorToLowerUpper( first, other, true, splitIntoSequentialBlocks) } func checkPrefixBlockContainment( first, other ExtendedIPSegmentSeries, ) ExtendedIPSegmentSeries { if first.Contains(other) { return checkPrefixBlockFormat(first, other, true) //return checkPrefixBlockFormat(first, other, true, prefixAdder, arrayProducer); //return cloneToIPSections(checkPrefixBlockFormat(first, other, true, // func(series AddressSegmentSeries) AddressSegmentSeries { return prefixAdder(series.(*IPAddressSection)) }, //)) } else if other.Contains(first) { return checkPrefixBlockFormat(other, first, false) //return checkPrefixBlockFormat(other, first, false, prefixAdder, arrayProducer); //return cloneToIPSections(checkPrefixBlockFormat(other, first, false, // func(series AddressSegmentSeries) AddressSegmentSeries { return prefixAdder(series.(*IPAddressSection)) }, //)) } return nil } func wrapNonNilInSlice(result ExtendedIPSegmentSeries) []ExtendedIPSegmentSeries { if result != nil { return []ExtendedIPSegmentSeries{result} } return nil } func checkSequentialBlockContainment( first, other ExtendedIPSegmentSeries, ) ExtendedIPSegmentSeries { if first.Contains(other) { return checkSequentialBlockFormat(first, other, true) // return checkSequentialBlockFormat(first, other, true, prefixRemover, arrayProducer); } else if other.Contains(first) { //return checkSequentialBlockFormat(other, first, false, prefixRemover, arrayProducer); return checkSequentialBlockFormat(other, first, false) } return nil } func checkPrefixBlockFormat( container, contained ExtendedIPSegmentSeries, checkEqual bool, ) (result ExtendedIPSegmentSeries) { if container.IsPrefixed() && container.IsSinglePrefixBlock() { result = container } else if checkEqual && contained.IsPrefixed() && container.CompareSize(contained) == 0 && contained.IsSinglePrefixBlock() { result = contained } else { result = container.AssignPrefixForSingleBlock() // this returns nil if cannot be a prefix block } return } func checkSequentialBlockFormat( container, contained ExtendedIPSegmentSeries, checkEqual bool, ) (result ExtendedIPSegmentSeries) { if !container.IsPrefixed() { if container.IsSequential() { result = container } } else if checkEqual && !contained.IsPrefixed() && container.CompareSize(contained) == 0 { if contained.IsSequential() { result = contained } } else if container.IsSequential() { result = container.WithoutPrefixLen() } return } func splitIntoSequentialBlocks( lower, upper ExtendedIPSegmentSeries) (blocks []ExtendedIPSegmentSeries) { segCount := lower.GetDivisionCount() if segCount == 0 { //all segments match, it's just a single series //blocks.add(lower); return []ExtendedIPSegmentSeries{lower} } blocks = make([]ExtendedIPSegmentSeries, 0, IPv6SegmentCount) var previousSegmentBits BitCount var currentSegment int bitsPerSegment := lower.GetBitsPerSegment() var segSegment int var lowerValue, upperValue SegInt var stack seriesStack var toAdd list.List toAdd.Init() for { for { segSegment = currentSegment lowerSeg := lower.GetGenericSegment(currentSegment) upperSeg := upper.GetGenericSegment(currentSegment) currentSegment++ lowerValue = lowerSeg.GetSegmentValue() // these are single addresses, so lower or upper value no different here upperValue = upperSeg.GetSegmentValue() previousSegmentBits += bitsPerSegment if lowerValue != upperValue || currentSegment >= segCount { break } } if lowerValue == upperValue { blocks = append(blocks, lower) } else { lowerIsLowest := lower.IncludesZeroHostLen(previousSegmentBits) higherIsHighest := upper.IncludesMaxHostLen(previousSegmentBits) if lowerIsLowest { if higherIsHighest { // full range series := lower.ToBlock(segSegment, lowerValue, upperValue) blocks = append(blocks, series) } else { topLower, _ := upper.ToZeroHostLen(previousSegmentBits) middleUpper := topLower.Increment(-1) series := lower.ToBlock(segSegment, lowerValue, middleUpper.GetGenericSegment(segSegment).GetSegmentValue()) blocks = append(blocks, series) lower = topLower continue } } else if higherIsHighest { bottomUpper, _ := lower.ToMaxHostLen(previousSegmentBits) topLower := bottomUpper.Increment(1) series := topLower.ToBlock(segSegment, topLower.GetGenericSegment(segSegment).GetSegmentValue(), upperValue) toAdd.PushFront(series) upper = bottomUpper continue } else { //from top to bottom we have: top - topLower - middleUpper - middleLower - bottomUpper - lower topLower, _ := upper.ToZeroHostLen(previousSegmentBits) middleUpper := topLower.Increment(-1) bottomUpper, _ := lower.ToMaxHostLen(previousSegmentBits) middleLower := bottomUpper.Increment(1) if LowValueComparator.CompareSeries(middleLower, middleUpper) <= 0 { series := middleLower.ToBlock( segSegment, middleLower.GetGenericSegment(segSegment).GetSegmentValue(), middleUpper.GetGenericSegment(segSegment).GetSegmentValue()) toAdd.PushFront(series) } stack.init(IPv6SegmentCount) stack.push(topLower, upper, previousSegmentBits, currentSegment) // do this one later upper = bottomUpper continue } } if toAdd.Len() != 0 { for { saved := toAdd.Front() if saved == nil { break } toAdd.Remove(saved) blocks = append(blocks, saved.Value.(ExtendedIPSegmentSeries)) } } var popped bool if popped, lower, upper, previousSegmentBits, currentSegment = stack.pop(); !popped { return blocks } } } func splitIntoPrefixBlocks( lower, upper ExtendedIPSegmentSeries) (blocks []ExtendedIPSegmentSeries) { blocks = make([]ExtendedIPSegmentSeries, 0, IPv6BitCount) var previousSegmentBits BitCount var currentSegment int var stack seriesStack segCount := lower.GetDivisionCount() bitsPerSegment := lower.GetBitsPerSegment() for { //Find first non-matching bit. var differing SegInt for ; currentSegment < segCount; currentSegment++ { lowerSeg := lower.GetGenericSegment(currentSegment) upperSeg := upper.GetGenericSegment(currentSegment) lowerValue := lowerSeg.GetSegmentValue() //these are single addresses, so lower or upper value no different here upperValue := upperSeg.GetSegmentValue() differing = lowerValue ^ upperValue if differing != 0 { break } previousSegmentBits += bitsPerSegment } if differing == 0 { //all bits match, it's just a single address blocks = append(blocks, lower.ToPrefixBlockLen(lower.GetBitCount())) } else { differingIsLowestBit := differing == 1 if differingIsLowestBit && currentSegment+1 == segCount { //only the very last bit differs, so we have a prefix block right there blocks = append(blocks, lower.ToPrefixBlockLen(lower.GetBitCount()-1)) } else { highestDifferingBitInRange := BitCount(bits.LeadingZeros32(uint32(differing))) - (32 - bitsPerSegment) differingBitPrefixLen := highestDifferingBitInRange + previousSegmentBits if lower.IncludesZeroHostLen(differingBitPrefixLen) && upper.IncludesMaxHostLen(differingBitPrefixLen) { //full range at the differing bit, we have a single prefix block blocks = append(blocks, lower.ToPrefixBlockLen(differingBitPrefixLen)) } else { //neither a prefix block nor a single address //we split into two new ranges to continue //starting from the differing bit, //lower top becomes 1000000... //upper bottom becomes 01111111... //so in each new range, the differing bit is at least one further to the right (or more) lowerTop, _ := upper.ToZeroHostLen(differingBitPrefixLen + 1) upperBottom := lowerTop.Increment(-1) if differingIsLowestBit { previousSegmentBits += bitsPerSegment currentSegment++ } stack.init(int(IPv6BitCount)) stack.push(lowerTop, upper, previousSegmentBits, currentSegment) // do upper one later upper = upperBottom // do lower one now continue } } } var popped bool if popped, lower, upper, previousSegmentBits, currentSegment = stack.pop(); !popped { return blocks } } } func applyOperatorToLowerUpper( first, other ExtendedIPSegmentSeries, removePrefixes bool, operatorFunctor func(lower, upper ExtendedIPSegmentSeries) []ExtendedIPSegmentSeries) []ExtendedIPSegmentSeries { var lower, upper ExtendedIPSegmentSeries if seriesValsSame(first, other) { if removePrefixes && first.IsPrefixed() { if other.IsPrefixed() { lower = first.WithoutPrefixLen() } else { lower = other } } else { lower = first } upper = lower.GetUpper() lower = lower.GetLower() } else { firstLower := first.GetLower() otherLower := other.GetLower() firstUpper := first.GetUpper() otherUpper := other.GetUpper() if LowValueComparator.CompareSeries(firstLower, otherLower) > 0 { lower = otherLower } else { lower = firstLower } if LowValueComparator.CompareSeries(firstUpper, otherUpper) < 0 { upper = otherUpper } else { upper = firstUpper } if removePrefixes { lower = lower.WithoutPrefixLen() upper = upper.WithoutPrefixLen() } } return operatorFunctor(lower, upper) } type seriesStack struct { seriesPairs []ExtendedIPSegmentSeries // stack items indexes []int // stack items bits []BitCount // stack items } // grows to have capacity at least as large as size func (stack *seriesStack) init(size int) { if stack.seriesPairs == nil { stack.seriesPairs = make([]ExtendedIPSegmentSeries, 0, size<<1) stack.indexes = make([]int, 0, size) stack.bits = make([]BitCount, 0, size) } } func (stack *seriesStack) push(lower, upper ExtendedIPSegmentSeries, previousSegmentBits BitCount, currentSegment int) { stack.seriesPairs = append(stack.seriesPairs, lower, upper) stack.indexes = append(stack.indexes, currentSegment) stack.bits = append(stack.bits, previousSegmentBits) } func (stack *seriesStack) pop() (popped bool, lower, upper ExtendedIPSegmentSeries, previousSegmentBits BitCount, currentSegment int) { seriesPairs := stack.seriesPairs length := len(seriesPairs) if length <= 0 { return } length-- upper = seriesPairs[length] length-- lower = seriesPairs[length] stack.seriesPairs = seriesPairs[:length] indexes := stack.indexes length = len(indexes) - 1 currentSegment = indexes[length] stack.indexes = indexes[:length] stackbits := stack.bits previousSegmentBits = stackbits[length] stack.bits = stackbits[:length] popped = true return } func spanWithPrefixBlocks(orig ExtendedIPSegmentSeries) (list []ExtendedIPSegmentSeries) { iterator := orig.SequentialBlockIterator() for iterator.HasNext() { list = append(list, iterator.Next().SpanWithPrefixBlocks()...) } return list } func spanWithSequentialBlocks(orig ExtendedIPSegmentSeries) (list []ExtendedIPSegmentSeries) { iterator := orig.SequentialBlockIterator() for iterator.HasNext() { list = append(list, iterator.Next()) } return list } ipaddress-go-1.5.4/ipaddr/stringparams.go000066400000000000000000001436041440250641600204200ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "strings" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" ) // Note: For IPv6, translation from options to params is more complicated and requires the section, so it's done in IPv6AddressSection methods func toNormalizedIPZonedString(opts addrstr.IPStringOptions, section AddressDivisionSeries, zone Zone) string { return toIPParams(opts).toZonedString(section, zone) } func toNormalizedIPString(opts addrstr.IPStringOptions, section AddressDivisionSeries) string { return toIPParams(opts).toString(section) } func toNormalizedZonedString(opts addrstr.StringOptions, section AddressDivisionSeries, zone Zone) string { return toParams(opts).toZonedString(section, zone) } func toNormalizedString(opts addrstr.StringOptions, section AddressDivisionSeries) string { return toParams(opts).toString(section) } func toIPParams(opts addrstr.IPStringOptions) *ipAddressStringParams { return &ipAddressStringParams{ addressStringParams: addressStringParams{ radix: opts.GetRadix(), separator: opts.GetSeparator(), hasSep: opts.HasSeparator(), uppercase: opts.IsUppercase(), expandSegments: opts.IsExpandedSegments(), wildcards: opts.GetWildcards(), segmentStrPrefix: opts.GetSegmentStrPrefix(), reverse: opts.IsReverse(), addressLabel: opts.GetAddressLabel(), zoneSeparator: opts.GetZoneSeparator(), }, wildcardOption: opts.GetWildcardOption(), addressSuffix: opts.GetAddressSuffix(), } } func toParams(opts addrstr.StringOptions) *addressStringParams { return &addressStringParams{ radix: opts.GetRadix(), separator: opts.GetSeparator(), hasSep: opts.HasSeparator(), uppercase: opts.IsUppercase(), expandSegments: opts.IsExpandedSegments(), wildcards: opts.GetWildcards(), segmentStrPrefix: opts.GetSegmentStrPrefix(), addressLabel: opts.GetAddressLabel(), reverse: opts.IsReverse(), // the options don't provide a zone separator (only IPStringOptions do), so we must specify what it is zoneSeparator: IPv6ZoneSeparatorStr, } } func from(opts addrstr.IPv6StringOptions, addr *IPv6AddressSection) (res *ipv6StringParams) { res = &ipv6StringParams{ ipAddressStringParams: ipAddressStringParams{ addressStringParams: addressStringParams{ radix: opts.GetRadix(), separator: opts.GetSeparator(), hasSep: opts.HasSeparator(), uppercase: opts.IsUppercase(), expandSegments: opts.IsExpandedSegments(), wildcards: opts.GetWildcards(), segmentStrPrefix: opts.GetSegmentStrPrefix(), reverse: opts.IsReverse(), splitDigits: opts.IsSplitDigits(), addressLabel: opts.GetAddressLabel(), zoneSeparator: opts.GetZoneSeparator(), }, wildcardOption: opts.GetWildcardOption(), addressSuffix: opts.GetAddressSuffix(), }, } if opts.GetCompressOptions() != nil { compressOptions := opts.GetCompressOptions() maxIndex, maxCount := addr.getCompressIndexAndCount(compressOptions, opts.IsMixed()) if maxCount > 0 { res.firstCompressedSegmentIndex = maxIndex res.nextUncompressedIndex = maxIndex + maxCount res.hostCompressed = compressOptions.GetCompressionChoiceOptions().CompressHost() && addr.IsPrefixed() && (res.nextUncompressedIndex > getHostSegmentIndex(addr.getNetworkPrefixLen().bitCount(), IPv6BytesPerSegment, IPv6BitsPerSegment)) } } return res } type divStringProvider interface { getLowerStringLength(radix int) int getUpperStringLength(radix int) int getLowerString(radix int, uppercase bool, appendable *strings.Builder) getLowerStringChopped(radix int, choppedDigits int, uppercase bool, appendable *strings.Builder) getUpperString(radix int, uppercase bool, appendable *strings.Builder) getUpperStringMasked(radix int, uppercase bool, appendable *strings.Builder) getSplitLowerString(radix int, choppedDigits int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string, appendable *strings.Builder) getSplitRangeString(rangeSeparator string, wildcard string, radix int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string, appendable *strings.Builder) addrerr.IncompatibleAddressError getSplitRangeStringLength(rangeSeparator string, wildcard string, leadingZeroCount int, radix int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string) int getRangeDigitCount(radix int) int // if leadingZeroCount is -1, returns the number of leading zeros for maximum width, based on the width of the value adjustLowerLeadingZeroCount(leadingZeroCount int, radix int) int // if leadingZeroCount is -1, returns the number of leading zeros for maximum width, based on the width of the value adjustUpperLeadingZeroCount(leadingZeroCount int, radix int) int getMaxDigitCountRadix(radix int) int // returns the default radix for textual representations of addresses (10 for IPv4, 16 for IPv6) getDefaultTextualRadix() int // put this in divisionValues perhaps? or use addrType // returns the number of digits for the maximum possible value of the division when using the default radix getMaxDigitCount() int // A simple string using just the lower value and the default radix. getDefaultLowerString() string // A simple string using just the lower and upper values and the default radix, separated by the default range character. getDefaultRangeString() string // This is the wildcard string to be used when producing the default strings with getString() or getWildcardString() // // Since no parameters for the string are provided, default settings are used, but they must be consistent with the address. // //For instance, generally the '-' is used as a range separator, but in some cases that character is used for a segment separator. // // Note that this only applies to "default" settings, there are additional string methods that allow you to specify these separator characters. // Those methods must be aware of the defaults as well, to know when they can defer to the defaults and when they cannot. getDefaultRangeSeparatorString() string } // Each segment params has settings to write exactly one type of IP address part string segment. type addressSegmentParams interface { getWildcards() addrstr.Wildcards preferWildcards() bool // returns -1 for as many leading zeros as needed to write the max number of characters per segment, // or 0, 1, 2, 3 to indicate the number of leading zeros getLeadingZeros(segmentIndex int) int getSegmentStrPrefix() string getRadix() int isUppercase() bool isSplitDigits() bool hasSeparator() bool getSplitDigitSeparator() byte isReverseSplitDigits() bool } type addressStringParams struct { wildcards addrstr.Wildcards expandSegments bool //whether to expand 1 to 001 for IPv4 or 0001 for IPv6 segmentStrPrefix string //eg for inet_aton style there is 0x for hex, 0 for octal radix int //the segment separator and in the case of split digits, the digit separator separator byte // default is ' ' hasSep bool // whether there is a separator at all uppercase bool //whether to print A or a //print the segments in reverse, and in the case of splitDigits, print the digits in reverse as well reverse bool //in each segment, split the digits with the separator, so that 123.456.1.1 becomes 1.2.3.4.5.6.1.1 splitDigits bool addressLabel string zoneSeparator string } func (params *addressStringParams) getWildcards() addrstr.Wildcards { return params.wildcards } func (params *addressStringParams) preferWildcards() bool { return true } //returns -1 to expand func (params *addressStringParams) getLeadingZeros(_ int) int { if params.expandSegments { return -1 } return 0 } func (params *addressStringParams) getSegmentStrPrefix() string { return params.segmentStrPrefix } func (params *addressStringParams) getRadix() int { return params.radix } func (params *addressStringParams) isUppercase() bool { return params.uppercase } func (params *addressStringParams) isSplitDigits() bool { return params.splitDigits } func (params *addressStringParams) hasSeparator() bool { return params.hasSep } func (params *addressStringParams) getSplitDigitSeparator() byte { return params.separator } func (params *addressStringParams) isReverseSplitDigits() bool { return params.reverse } func (params *addressStringParams) getSegmentsStringLength(part AddressDivisionSeries) int { count := 0 divCount := part.GetDivisionCount() if divCount != 0 { for i := 0; i < divCount; i++ { count += params.appendSegment(i, nil, part) } //Character separator = getSeparator(); if params.hasSep { count += divCount - 1 // the number of separators } } return count } func (params *addressStringParams) appendSegments(builder *strings.Builder, part AddressDivisionSeries) *strings.Builder { divCount := part.GetDivisionCount() if divCount != 0 { reverse := params.reverse i := 0 hasSeparator := params.hasSep separator := params.separator for { segIndex := i if reverse { segIndex = divCount - i - 1 } params.appendSegment(segIndex, builder, part) i++ if i == divCount { break } if hasSeparator { builder.WriteByte(separator) } } } return builder } func (params *addressStringParams) appendSingleDivision(seg DivisionType, builder *strings.Builder) int { writer := stringWriter{seg} if builder == nil { result, _ := writer.getStandardString(0, params, nil) return result + params.getAddressLabelLength() } params.appendLabel(builder) _, _ = writer.getStandardString(0, params, builder) return 0 } func (params *addressStringParams) getDivisionStringLength(seg DivisionType) int { return params.appendSingleDivision(seg, nil) } func (params *addressStringParams) appendDivision(builder *strings.Builder, seg DivisionType) *strings.Builder { params.appendSingleDivision(seg, builder) return builder } func (params *addressStringParams) appendSegment(segmentIndex int, builder *strings.Builder, part AddressDivisionSeries) int { div := part.GetGenericDivision(segmentIndex) writer := stringWriter{div} res, _ := writer.getStandardString(segmentIndex, params, builder) return res } func (params *addressStringParams) getZoneLength(zone Zone, sep string) int { if zone != NoZone { return len(zone) + len(sep) /* zone separator is one char */ } return 0 } func (params *addressStringParams) getZonedStringLength(addr AddressDivisionSeries, zone Zone) int { if addr.GetDivisionCount() > 0 { result := params.getStringLength(addr) if zone != NoZone { result += params.getZoneLength(zone, params.zoneSeparator) } return result } return 0 } func (params *addressStringParams) getStringLength(addr AddressDivisionSeries) int { if addr.GetDivisionCount() > 0 { return params.getAddressLabelLength() + params.getSegmentsStringLength(addr) } return 0 } func (params *addressStringParams) appendZone(builder *strings.Builder, zone Zone) *strings.Builder { if zone != NoZone { builder.WriteString(params.zoneSeparator) builder.WriteString(string(zone)) } return builder } func (params *addressStringParams) appendZoned(builder *strings.Builder, addr AddressDivisionSeries, zone Zone) *strings.Builder { if addr.GetDivisionCount() > 0 { params.appendLabel(builder) params.appendSegments(builder, addr) params.appendZone(builder, zone) } return builder } func (params *addressStringParams) append(builder *strings.Builder, addr AddressDivisionSeries) *strings.Builder { return params.appendZoned(builder, addr, NoZone) } func (params *addressStringParams) toZonedString(addr AddressDivisionSeries, zone Zone) string { length := params.getZonedStringLength(addr, zone) builder := &strings.Builder{} builder.Grow(length) params.appendZoned(builder, addr, zone) checkLengths(length, builder) return builder.String() } func (params *addressStringParams) appendLabel(builder *strings.Builder) *strings.Builder { str := params.addressLabel if str != "" { builder.WriteString(str) } return builder } func (params *addressStringParams) getAddressLabelLength() int { return len(params.addressLabel) } func (params *addressStringParams) toString(addr AddressDivisionSeries) string { length := params.getStringLength(addr) builder := &strings.Builder{} builder.Grow(length) params.append(builder, addr) checkLengths(length, builder) return builder.String() } // func checkLengths(length int, builder *strings.Builder) { //Note: re-enable this when doing development //calcMatch := length == builder.Len() //capMatch := length == builder.Cap() //if !calcMatch || !capMatch { // panic(fmt.Sprintf("length is %d, capacity is %d, expected length is %d", builder.Len(), builder.Cap(), length)) //} } func (params *addressStringParams) clone() *addressStringParams { result := *params return &result } var _ addressSegmentParams = &addressStringParams{} // Each StringParams has settings to write exactly one type of IP address part string. type ipAddressStringParams struct { addressStringParams wildcardOption addrstr.WildcardOption expandSeg []int //the same as expandSegments but for each segment addressSuffix string } func (params *ipAddressStringParams) preferWildcards() bool { return params.wildcardOption == addrstr.WildcardsAll } func (params *ipAddressStringParams) getExpandedSegmentLength(segmentIndex int) int { expandSegment := params.expandSeg if expandSegment == nil || len(expandSegment) <= segmentIndex { return 0 } return expandSegment[segmentIndex] } func (params *ipAddressStringParams) expandSegment(index, expansionLength, segmentCount int) { expandSegment := params.expandSeg if expandSegment == nil { expandSegment = make([]int, segmentCount) params.expandSeg = expandSegment } expandSegment[index] = expansionLength } //returns -1 for MAX, or 0, 1, 2, 3 to indicate the string prefix length func (params *ipAddressStringParams) getLeadingZeros(segmentIndex int) int { expandSegment := params.expandSeg if params.expandSegments { return -1 } else if expandSegment != nil && len(expandSegment) > segmentIndex { return expandSegment[segmentIndex] } return 0 } func (params *ipAddressStringParams) getTrailingSegmentSeparator() byte { return params.separator } func (params *ipAddressStringParams) appendSuffix(builder *strings.Builder) *strings.Builder { suffix := params.addressSuffix if len(suffix) > 0 { builder.WriteString(suffix) } return builder } func (params *ipAddressStringParams) getAddressSuffixLength() int { suffix := params.addressSuffix return len(suffix) } func (params *ipAddressStringParams) getTrailingSeparatorCount(addr AddressDivisionSeries) int { count := addr.GetDivisionCount() if count > 0 { return count - 1 } return 0 } func getPrefixIndicatorStringLength(addr AddressDivisionSeries) int { if addr.IsPrefixed() { return toUnsignedStringLengthFast(uint16(addr.GetPrefixLen().bitCount()), 10) + 1 } return 0 } func (params *ipAddressStringParams) getSegmentsStringLength(part AddressDivisionSeries) int { count := 0 divCount := part.GetDivisionCount() if divCount != 0 { prefLen := part.GetPrefixLen() for i := 0; i < divCount; i++ { div := part.GetGenericDivision(i) count += params.appendSegment(i, div, prefLen, nil, part) if prefLen != nil { bc := prefLen.bitCount() dc := div.GetBitCount() var bits BitCount if bc > dc { bits = bc - dc } prefLen = cacheBitCount(bits) } } if params.hasSep { count += divCount - 1 // the number of separators } } return count } func (params *ipAddressStringParams) getStringLength(series AddressDivisionSeries) int { count := params.getSegmentsStringLength(series) if !params.reverse && !params.preferWildcards() { count += getPrefixIndicatorStringLength(series) } return count + params.getAddressSuffixLength() + params.getAddressLabelLength() } func (params *ipAddressStringParams) appendPrefixIndicator(builder *strings.Builder, addr AddressDivisionSeries) *strings.Builder { if addr.IsPrefixed() { builder.WriteByte(PrefixLenSeparator) return toUnsignedStringCased(uint64(addr.GetPrefixLen().bitCount()), 10, 0, false, builder) } return builder } func (params *ipAddressStringParams) appendSegments(builder *strings.Builder, part AddressDivisionSeries) *strings.Builder { divCount := part.GetDivisionCount() if divCount != 0 { prefLen := part.GetPrefixLen() reverse := params.reverse i := 0 hasSeparator := params.hasSep separator := params.separator for { segIndex := i if reverse { segIndex = divCount - i - 1 } div := part.GetGenericDivision(segIndex) params.appendSegment(segIndex, div, prefLen, builder, part) if prefLen != nil { bc := prefLen.bitCount() dc := div.GetBitCount() var bits BitCount if bc > dc { bits = bc - dc } prefLen = cacheBitCount(bits) } i++ if i == divCount { break } if hasSeparator { builder.WriteByte(separator) } } } return builder } func (params *ipAddressStringParams) append(builder *strings.Builder, addr AddressDivisionSeries, zone Zone) *strings.Builder { if addr.GetDivisionCount() > 0 { params.appendSuffix(params.appendZone(params.appendSegments(params.appendLabel(builder), addr), zone)) if !params.reverse && !params.preferWildcards() { params.appendPrefixIndicator(builder, addr) } } return builder } func (params *ipAddressStringParams) appendSegment(segmentIndex int, div DivisionType, divPrefixLen PrefixLen, builder *strings.Builder, part AddressDivisionSeries) int { if params.isSplitDigits() { panic("split digits") // split digits restricted to IPv6, because we ignore the errors generated by split digits elsewhere. } writer := stringWriter{div} // consider all the cases in which we need not account for prefix length if params.preferWildcards() || divPrefixLen == nil || divPrefixLen.bitCount() >= div.GetBitCount() || !part.IsPrefixBlock() /* || params.isSplitDigits() */ { count, _ := writer.getStandardString(segmentIndex, params, builder) return count } // prefix length will have an impact on the string - either we need not print the range at all // because it is equivalent to the prefix length, or we need to adjust the upper value of the // range so that the host is zero when printing the string if div.IsSinglePrefix(divPrefixLen.bitCount()) { //if div.ContainsSinglePrefixBlock(*divPrefixLen) { //xxx ContainsSinglePrefix xxxx // this could be slightly quicker since we know it is a prefix block (since the whole part is), all we need to know is that it is single prefix. Add such a method to divStringProvider. return writer.getLowerStandardString(segmentIndex, params, builder) } return writer.getPrefixAdjustedRangeString(segmentIndex, params, builder) } func (params *ipAddressStringParams) getZonedStringLength(addr AddressDivisionSeries, zone Zone) int { if addr.GetDivisionCount() > 0 { result := params.getStringLength(addr) if zone != NoZone { result += params.getZoneLength(zone, params.zoneSeparator) } return result } return 0 } func (params *ipAddressStringParams) toZonedString(series AddressDivisionSeries, zone Zone) string { length := params.getZonedStringLength(series, zone) builder := strings.Builder{} builder.Grow(length) params.append(&builder, series, zone) checkLengths(length, &builder) return builder.String() } func (params *ipAddressStringParams) toString(series AddressDivisionSeries) string { return params.toZonedString(series, NoZone) } func (params *ipAddressStringParams) clone() *ipAddressStringParams { result := *params expandSegment := params.expandSeg if expandSegment != nil { result.expandSeg = cloneInts(expandSegment) } return &result } // Each IPv6StringParams has settings to write exactly one IPv6 address section string type ipv6StringParams struct { ipAddressStringParams firstCompressedSegmentIndex, nextUncompressedIndex int //the start and end of any compressed section hostCompressed bool //whether the host was compressed, which with some prefix configurations means we must print the network prefix to indicate that the host is full range } func (params *ipv6StringParams) endIsCompressed(addr IPAddressSegmentSeries) bool { return params.nextUncompressedIndex >= addr.GetDivisionCount() } func (params *ipv6StringParams) isCompressed(_ IPAddressSegmentSeries) bool { return params.firstCompressedSegmentIndex >= 0 } func (params *ipv6StringParams) getTrailingSeparatorCount(addr *IPv6AddressSection) int { return params.getTrailingSepCount(addr) } func (params *ipv6StringParams) getTrailingSepCount(addr IPAddressSegmentSeries) int { divisionCount := addr.GetDivisionCount() if divisionCount == 0 { return 0 } count := divisionCount - 1 //separators with no compression if params.isCompressed(addr) { firstCompressedSegmentIndex := params.firstCompressedSegmentIndex nextUncompressedIndex := params.nextUncompressedIndex count -= (nextUncompressedIndex - firstCompressedSegmentIndex) - 1 //missing seps if firstCompressedSegmentIndex == 0 /* additional separator at front */ || nextUncompressedIndex >= divisionCount /* additional separator at end */ { count++ } } return count } func (params *ipv6StringParams) append(builder *strings.Builder, addr *IPv6AddressSection, zone Zone) (err addrerr.IncompatibleAddressError) { if addr.GetDivisionCount() > 0 { // Our order is label, then segments, then zone, then suffix, then prefix length. err = params.appendSegments(params.appendLabel(builder), addr) if err != nil { return } params.appendSuffix(params.appendZone(builder, zone)) if !params.reverse && (!params.preferWildcards() || params.hostCompressed) { params.appendPrefixIndicator(builder, addr) } } return } func (params *ipv6StringParams) appendSegment(segmentIndex int, div DivisionType, divPrefixLen PrefixLen, builder *strings.Builder, part AddressDivisionSeries) (count int, err addrerr.IncompatibleAddressError) { if params.isSplitDigits() { writer := stringWriter{div} count, err = writer.getStandardString(segmentIndex, params, builder) return } count = params.ipAddressStringParams.appendSegment(segmentIndex, div, divPrefixLen, builder, part) return } func (params *ipv6StringParams) appendSegments(builder *strings.Builder, addr IPv6AddressSegmentSeries) (err addrerr.IncompatibleAddressError) { divisionCount := addr.GetDivisionCount() if divisionCount <= 0 { return nil } lastIndex := divisionCount - 1 separator := params.separator reverse := params.reverse i := 0 firstCompressedSegmentIndex := params.firstCompressedSegmentIndex nextUncompressedIndex := params.nextUncompressedIndex hasSep := params.hasSeparator() for { segIndex := i if reverse { segIndex = lastIndex - i } if segIndex < firstCompressedSegmentIndex || segIndex >= nextUncompressedIndex { div := addr.GetSegment(segIndex) prefLen := div.getDivisionPrefixLength() // Needs to be DivisionType _, err = params.appendSegment(segIndex, div, prefLen, builder, addr) i++ if i > lastIndex { break } if hasSep { builder.WriteByte(separator) } } else { firstCompressed := firstCompressedSegmentIndex if reverse { firstCompressed = nextUncompressedIndex - 1 } if segIndex == firstCompressed && hasSep { //the segment is compressed builder.WriteByte(separator) if i == 0 { //when compressing the front we use two separators builder.WriteByte(separator) } } //else we are in the middle of a compressed set of segments, so nothing to write i++ if i > lastIndex { break } } } return } func (params *ipv6StringParams) getSegmentsStringLength(part IPv6AddressSegmentSeries) int { count := 0 divCount := part.GetDivisionCount() if divCount != 0 { i := 0 firstCompressedSegmentIndex := params.firstCompressedSegmentIndex nextUncompressedIndex := params.nextUncompressedIndex for { if i < firstCompressedSegmentIndex || i >= nextUncompressedIndex { div := part.GetSegment(i) prefLen := div.GetSegmentPrefixLen() additionalCount, _ := params.appendSegment(i, div, prefLen, nil, part) count += additionalCount i++ if i >= divCount { break } if params.hasSeparator() { count++ } } else { if i == firstCompressedSegmentIndex && params.hasSeparator() { //the segment is compressed count++ if i == 0 { //when compressing the front we use two separators count++ } } //else we are in the middle of a compressed set of segments, so nothing to write i++ if i >= divCount { break } } } } return count } func (params *ipv6StringParams) getStringLength(addr *IPv6AddressSection) int { count := params.getSegmentsStringLength(addr) if !params.reverse && (!params.preferWildcards() || params.hostCompressed) { count += getPrefixIndicatorStringLength(addr) } return count + params.getAddressSuffixLength() + params.getAddressLabelLength() } func (params *ipv6StringParams) getZonedStringLength(addr *IPv6AddressSection, zone Zone) int { if addr.GetDivisionCount() > 0 { result := params.getStringLength(addr) if zone != NoZone { result += params.getZoneLength(zone, params.zoneSeparator) } return result } return 0 } func (params *ipv6StringParams) toZonedSplitString(addr *IPv6AddressSection, zone Zone) (str string, err addrerr.IncompatibleAddressError) { length := params.getZonedStringLength(addr, zone) builder := strings.Builder{} builder.Grow(length) if err = params.append(&builder, addr, zone); err != nil { return } checkLengths(length, &builder) str = builder.String() return } func (params *ipv6StringParams) toZonedString(addr *IPv6AddressSection, zone Zone) string { length := params.getZonedStringLength(addr, zone) builder := strings.Builder{} builder.Grow(length) _ = params.append(&builder, addr, zone) // only split strings produce errors checkLengths(length, &builder) return builder.String() } func (params *ipv6StringParams) toString(addr *IPv6AddressSection) string { return params.toZonedString(addr, NoZone) } func (params *ipv6StringParams) clone() *ipv6StringParams { res := *params res.ipAddressStringParams = *res.ipAddressStringParams.clone() return &res } // Each IPv6StringParams has settings to write exactly one IPv6 address section string type ipv6v4MixedParams struct { ipv6Params *ipv6StringParams ipv4Params *ipAddressStringParams } func (params *ipv6v4MixedParams) getTrailingSegmentSeparator() byte { return params.ipv4Params.getTrailingSegmentSeparator() } func (params *ipv6v4MixedParams) getTrailingSeparatorCount(addr *IPv6v4MixedAddressGrouping) int { return params.ipv4Params.getTrailingSeparatorCount(addr.GetIPv4AddressSection()) } func (params *ipv6v4MixedParams) getStringLength(addr *IPv6v4MixedAddressGrouping, zone Zone) int { if addr.GetDivisionCount() > 0 { ipv6Params := params.ipv6Params ipv6length := ipv6Params.getSegmentsStringLength(addr.GetIPv6AddressSection()) ipv4length := params.ipv4Params.getSegmentsStringLength(addr.GetIPv4AddressSection()) length := ipv6length + ipv4length if ipv6Params.nextUncompressedIndex < addr.GetIPv6AddressSection().GetSegmentCount() { length++ } length += params.getPrefixStringLength(addr) length += ipv6Params.getZoneLength(zone, ipv6Params.zoneSeparator) length += ipv6Params.getAddressSuffixLength() length += ipv6Params.getAddressLabelLength() return length } return 0 } func (params *ipv6v4MixedParams) toString(addr *IPv6v4MixedAddressGrouping) string { return params.toZonedString(addr, NoZone) } func (params *ipv6v4MixedParams) toZonedString(addr *IPv6v4MixedAddressGrouping, zone Zone) string { length := params.getStringLength(addr, zone) builder := &strings.Builder{} builder.Grow(length) params.append(builder, addr, zone) checkLengths(length, builder) return builder.String() } func (params *ipv6v4MixedParams) getDivisionStringLength(seg *AddressDivision) int { return params.ipv6Params.getDivisionStringLength(seg) } func (params *ipv6v4MixedParams) appendDivision(builder *strings.Builder, seg *AddressDivision) *strings.Builder { return params.ipv6Params.appendDivision(builder, seg) } func (params *ipv6v4MixedParams) append(builder *strings.Builder, addr *IPv6v4MixedAddressGrouping, zone Zone) *strings.Builder { if addr.GetDivisionCount() > 0 { ipv6Params := params.ipv6Params ipv6Params.appendLabel(builder) _ = ipv6Params.appendSegments(builder, addr.GetIPv6AddressSection()) if ipv6Params.nextUncompressedIndex < addr.GetIPv6AddressSection().GetSegmentCount() { builder.WriteByte(ipv6Params.getTrailingSegmentSeparator()) } params.ipv4Params.appendSegments(builder, addr.GetIPv4AddressSection()) /* * RFC 4038: for bracketed addresses, zone is inside and prefix outside, putting prefix after zone. * * Suffixes are things like .in-addr.arpa, .ip6.arpa, .ipv6-literal.net * which generally convert an address string to a host * As with our HostName, we support host/prefix in which case the prefix is applied * to the resolved address. * * So in summary, our order is zone, then suffix, then prefix length. */ ipv6Params.appendZone(builder, zone) ipv6Params.appendSuffix(builder) params.appendPrefixIndicator(builder, addr) } return builder } func (params *ipv6v4MixedParams) getPrefixStringLength(addr *IPv6v4MixedAddressGrouping) int { if params.requiresPrefixIndicatorIPv6(addr.GetIPv6AddressSection()) || params.requiresPrefixIndicatorIPv4(addr.GetIPv4AddressSection()) { return getPrefixIndicatorStringLength(addr) } return 0 } func (params *ipv6v4MixedParams) appendPrefixIndicator(builder *strings.Builder, addr *IPv6v4MixedAddressGrouping) { if params.requiresPrefixIndicatorIPv6(addr.GetIPv6AddressSection()) || params.requiresPrefixIndicatorIPv4(addr.GetIPv4AddressSection()) { params.ipv6Params.appendPrefixIndicator(builder, addr) } } func (params *ipv6v4MixedParams) requiresPrefixIndicatorIPv4(ipv4Section *IPv4AddressSection) bool { return ipv4Section.IsPrefixed() && !params.ipv4Params.preferWildcards() } func (params *ipv6v4MixedParams) requiresPrefixIndicatorIPv6(ipv6Section IPv6AddressSegmentSeries) bool { //func (params *ipv6v4MixedParams) requiresPrefixIndicatorIPv6(ipv6Section *IPv6AddressSection) bool { ipv6Params := params.ipv6Params return ipv6Section.IsPrefixed() && (!ipv6Params.preferWildcards() || ipv6Params.hostCompressed) } func (params *ipv6v4MixedParams) clone() *ipv6v4MixedParams { ipv6Params := *params.ipv6Params ipv4Params := *params.ipv4Params return &ipv6v4MixedParams{ ipv6Params: &ipv6Params, ipv4Params: &ipv4Params, } } type stringWriter struct { DivisionType } // Produces a string to represent the segment, using wildcards and range characters. // Use this instead of getWildcardString() if you have a customized wildcard or range separator or you have a non-zero leadingZeroCount, func (writer stringWriter) getStandardString(segmentIndex int, params addressSegmentParams, appendable *strings.Builder) (digitCount int, err addrerr.IncompatibleAddressError) { //div := writer.div if !writer.IsMultiple() { splitDigits := params.isSplitDigits() if splitDigits { radix := params.getRadix() leadingZeroCount := params.getLeadingZeros(segmentIndex) leadingZeroCount = writer.adjustLowerLeadingZeroCount(leadingZeroCount, radix) stringPrefix := params.getSegmentStrPrefix() prefLen := len(stringPrefix) if appendable == nil { var length int if leadingZeroCount != 0 { if leadingZeroCount < 0 { length = writer.getMaxDigitCountRadix(radix) } else { length = writer.getLowerStringLength(radix) + leadingZeroCount } } else { length = writer.getLowerStringLength(radix) } count := (length << 1) - 1 if prefLen > 0 { count += length * prefLen } return count, nil } else { var splitDigitSeparator byte = ' ' if params.hasSeparator() { splitDigitSeparator = params.getSplitDigitSeparator() } reverseSplitDigits := params.isReverseSplitDigits() uppercase := params.isUppercase() if reverseSplitDigits { writer.getSplitLowerString(radix, 0, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, appendable) if leadingZeroCount != 0 { appendable.WriteByte(splitDigitSeparator) getSplitLeadingZeros(leadingZeroCount, splitDigitSeparator, stringPrefix, appendable) } } else { if leadingZeroCount != 0 { getSplitLeadingZeros(leadingZeroCount, splitDigitSeparator, stringPrefix, appendable) appendable.WriteByte(splitDigitSeparator) } writer.getSplitLowerString(radix, 0, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, appendable) } return } } return writer.getLowerStandardString(segmentIndex, params, appendable), nil } else if writer.IsFullRange() { wildcard := params.getWildcards().GetWildcard() if len(wildcard) > 0 { splitDigits := params.isSplitDigits() if splitDigits { radix := params.getRadix() if appendable == nil { length := writer.getMaxDigitCountRadix(radix) count := length*(len(wildcard)+1) - 1 return count, nil } var splitDigitSeparator byte = ' ' if params.hasSeparator() { splitDigitSeparator = params.getSplitDigitSeparator() } dg := writer.getMaxDigitCountRadix(radix) getSplitCharStr(dg, splitDigitSeparator, wildcard, "", appendable) return } return getFullRangeString(wildcard, appendable), nil } } return writer.getRangeString(segmentIndex, params, appendable) } func (writer stringWriter) getPrefixAdjustedRangeString(segmentIndex int, params addressSegmentParams, appendable *strings.Builder) int { leadingZeroCount := params.getLeadingZeros(segmentIndex) radix := params.getRadix() lowerLeadingZeroCount := writer.adjustLowerLeadingZeroCount(leadingZeroCount, radix) upperLeadingZeroCount := writer.adjustUpperLeadingZeroCount(leadingZeroCount, radix) //if the wildcards match those in use by getString(), and there is no character prefix, let's defer to getString() so that it is cached wildcards := params.getWildcards() rangeSeparator := wildcards.GetRangeSeparator() rangeDigitCount := 0 if len(wildcards.GetSingleWildcard()) != 0 { rangeDigitCount = writer.getRangeDigitCount(radix) } //If we can, we reuse the standard string to construct this string (must have the same radix and no chopped digits) //We can insert leading zeros, string prefix, and a different separator string if necessary //Also, we cannot in the case of full range (in which case we are only here because we do not want '*') if rangeDigitCount == 0 && radix == writer.getDefaultTextualRadix() && !writer.IsFullRange() { //we call getString() to cache the result, and we call getString instead of getWildcardString() because it will also mask with the segment prefix length str := writer.GetString() rangeSep := writer.getDefaultRangeSeparatorString() stringPrefix := params.getSegmentStrPrefix() prefLen := len(stringPrefix) if lowerLeadingZeroCount == 0 && upperLeadingZeroCount == 0 && rangeSep == rangeSeparator && prefLen == 0 { if appendable == nil { return len(str) } else { if params.isUppercase() { appendUppercase(str, radix, appendable) } else { appendable.WriteString(str) } return 0 } } else { if appendable == nil { count := len(str) + (len(rangeSeparator) - len(rangeSep)) + lowerLeadingZeroCount + upperLeadingZeroCount if prefLen > 0 { count += prefLen << 1 } return count } else { firstEnd := strings.Index(str, rangeSep) if prefLen > 0 { appendable.WriteString(stringPrefix) } if lowerLeadingZeroCount > 0 { getLeadingZeros(lowerLeadingZeroCount, appendable) } appendable.WriteString(str[0:firstEnd]) appendable.WriteString(rangeSeparator) if prefLen > 0 { appendable.WriteString(stringPrefix) } if upperLeadingZeroCount > 0 { getLeadingZeros(upperLeadingZeroCount, appendable) } appendable.WriteString(str[firstEnd+len(rangeSep):]) return 0 } } } rangeDigitCount = writer.adjustRangeDigits(rangeDigitCount) if leadingZeroCount < 0 && appendable == nil { charLength := writer.getMaxDigitCountRadix(radix) stringPrefix := params.getSegmentStrPrefix() prefLen := len(stringPrefix) if rangeDigitCount != 0 { count := charLength if prefLen > 0 { count += prefLen } return count } count := charLength << 1 if prefLen > 0 { count += prefLen << 1 } count += len(rangeSeparator) return count } if rangeDigitCount != 0 { return writer.getRangeDigitString(segmentIndex, params, appendable) } return writer.getRangeStringWithCounts(segmentIndex, params, lowerLeadingZeroCount, upperLeadingZeroCount, true, appendable) } func (writer stringWriter) getLowerStandardString(segmentIndex int, params addressSegmentParams, appendable *strings.Builder) int { count := 0 stringPrefix := params.getSegmentStrPrefix() prefLen := len(stringPrefix) if prefLen > 0 { if appendable == nil { count += prefLen } else { appendable.WriteString(stringPrefix) } } radix := params.getRadix() leadingZeroCount := params.getLeadingZeros(segmentIndex) if leadingZeroCount != 0 { if appendable == nil { if leadingZeroCount < 0 { return count + writer.getMaxDigitCountRadix(radix) } else { count += leadingZeroCount } } else { leadingZeroCount = writer.adjustLowerLeadingZeroCount(leadingZeroCount, radix) getLeadingZeros(leadingZeroCount, appendable) } } uppercase := params.isUppercase() if radix == writer.getDefaultTextualRadix() { // Equivalent to GetString for ip addresses but not GetWildcardString. // For other addresses, equivalent to either one. str := writer.getStringAsLower() if appendable == nil { return count + len(str) } else if uppercase { appendUppercase(str, radix, appendable) } else { appendable.WriteString(str) } } else { if appendable == nil { return count + writer.getLowerStringLength(radix) } else { writer.getLowerString(radix, uppercase, appendable) } } return 0 } func (writer stringWriter) getRangeString(segmentIndex int, params addressSegmentParams, appendable *strings.Builder) (digitCount int, err addrerr.IncompatibleAddressError) { splitDigits := params.isSplitDigits() radix := params.getRadix() leadingZeroCount := params.getLeadingZeros(segmentIndex) wildcards := params.getWildcards() rangeSeparator := wildcards.GetRangeSeparator() singleWC := wildcards.GetSingleWildcard() rangeDigitCount := 0 if singleWC != "" { rangeDigitCount = writer.getRangeDigitCount(radix) } lowerLeadingZeroCount := writer.adjustLowerLeadingZeroCount(leadingZeroCount, radix) upperLeadingZeroCount := writer.adjustUpperLeadingZeroCount(leadingZeroCount, radix) //check the case where we can use the result of getWildcardString which is cached. //It must have same radix and no chopped digits, and no splitting or reversal of digits. //We can insert leading zeros, string prefix, and a different separator string if necessary. //Also, we cannot in the case of full range (in which case we are only here because we do not want '*') if rangeDigitCount == 0 && radix == writer.getDefaultTextualRadix() && !splitDigits && !writer.IsFullRange() { str := writer.GetWildcardString() rangeSep := writer.getDefaultRangeSeparatorString() stringPrefix := params.getSegmentStrPrefix() prefLen := len(stringPrefix) if lowerLeadingZeroCount == 0 && upperLeadingZeroCount == 0 && prefLen == 0 && rangeSeparator == rangeSep { if appendable == nil { return len(str), nil } appendable.WriteString(str) return } else { if appendable == nil { count := len(str) + (len(rangeSeparator) - len(rangeSep)) + lowerLeadingZeroCount + upperLeadingZeroCount if prefLen > 0 { count += prefLen << 1 } return count, nil } else { firstEnd := strings.Index(str, rangeSep) if prefLen > 0 { appendable.WriteString(stringPrefix) } if lowerLeadingZeroCount > 0 { getLeadingZeros(lowerLeadingZeroCount, appendable) } appendable.WriteString(str[0:firstEnd]) appendable.WriteString(rangeSeparator) if prefLen > 0 { appendable.WriteString(stringPrefix) } if upperLeadingZeroCount > 0 { getLeadingZeros(upperLeadingZeroCount, appendable) } appendable.WriteString(str[firstEnd+len(rangeSep):]) return } } } /* split digits that result in digit ranges of * are similar to range digits range digits eg f00-fff is both f__ and f.*.* One difference is that for decimal last range digit is 0-5 (ie 255) but for split we only check full range (0-9) eg 200-255 is 2__ but not 2.*.* another difference: when calculating range digits, the count is 0 unless the entire range can be written as range digits eg f10-fff has no range digits but is f.1-f.* */ if !splitDigits && leadingZeroCount < 0 && appendable == nil { stringPrefix := params.getSegmentStrPrefix() prefLen := len(stringPrefix) charLength := writer.getMaxDigitCountRadix(radix) if rangeDigitCount != 0 { count := charLength if prefLen > 0 { count += prefLen } return count, nil } count := charLength << 1 if prefLen > 0 { count += prefLen << 1 } count += len(rangeSeparator) return count, nil } rangeDigitCount = writer.adjustRangeDigits(rangeDigitCount) if rangeDigitCount != 0 { // wildcards like _ if splitDigits { return writer.getSplitRangeDigitString(segmentIndex, params, appendable), nil } else { return writer.getRangeDigitString(segmentIndex, params, appendable), nil } } if splitDigits { return writer.writeSplitRangeString(segmentIndex, params, appendable) } return writer.getRangeStringWithCounts(segmentIndex, params, lowerLeadingZeroCount, upperLeadingZeroCount, false, appendable), nil } func (writer stringWriter) getSplitRangeDigitString(segmentIndex int, params addressSegmentParams, appendable *strings.Builder) int { radix := params.getRadix() leadingZerosCount := params.getLeadingZeros(segmentIndex) leadingZerosCount = writer.adjustLowerLeadingZeroCount(leadingZerosCount, radix) stringPrefix := params.getSegmentStrPrefix() if appendable == nil { length := writer.getLowerStringLength(radix) + leadingZerosCount count := (length << 1) - 1 prefLen := len(stringPrefix) if prefLen > 0 { count += length * prefLen } return count } else { wildcards := params.getWildcards() dc := writer.getRangeDigitCount(radix) rangeDigits := writer.adjustRangeDigits(dc) var splitDigitSeparator byte = ' ' if params.hasSeparator() { splitDigitSeparator = params.getSplitDigitSeparator() } reverseSplitDigits := params.isReverseSplitDigits() uppercase := params.isUppercase() if reverseSplitDigits { getSplitCharStr(rangeDigits, splitDigitSeparator, wildcards.GetSingleWildcard(), stringPrefix, appendable) appendable.WriteByte(splitDigitSeparator) writer.getSplitLowerString(radix, rangeDigits, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, appendable) if leadingZerosCount > 0 { appendable.WriteByte(splitDigitSeparator) getSplitLeadingZeros(leadingZerosCount, splitDigitSeparator, stringPrefix, appendable) } } else { if leadingZerosCount != 0 { getSplitLeadingZeros(leadingZerosCount, splitDigitSeparator, stringPrefix, appendable) appendable.WriteByte(splitDigitSeparator) } writer.getSplitLowerString(radix, rangeDigits, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, appendable) appendable.WriteByte(splitDigitSeparator) getSplitCharStr(rangeDigits, splitDigitSeparator, wildcards.GetSingleWildcard(), stringPrefix, appendable) } } return 0 } func (writer stringWriter) getRangeDigitString(segmentIndex int, params addressSegmentParams, appendable *strings.Builder) int { radix := params.getRadix() leadingZerosCount := params.getLeadingZeros(segmentIndex) leadingZerosCount = writer.adjustLowerLeadingZeroCount(leadingZerosCount, radix) stringPrefix := params.getSegmentStrPrefix() prefLen := len(stringPrefix) wildcards := params.getWildcards() dc := writer.getRangeDigitCount(radix) rangeDigits := writer.adjustRangeDigits(dc) if appendable == nil { return writer.getLowerStringLength(radix) + leadingZerosCount + prefLen } else { if prefLen > 0 { appendable.WriteString(stringPrefix) } if leadingZerosCount > 0 { getLeadingZeros(leadingZerosCount, appendable) } uppercase := params.isUppercase() writer.getLowerStringChopped(radix, rangeDigits, uppercase, appendable) for i := 0; i < rangeDigits; i++ { appendable.WriteString(wildcards.GetSingleWildcard()) } } return 0 } func (writer stringWriter) adjustRangeDigits(rangeDigits int) int { if rangeDigits != 0 { //Note: ranges like ___ intended to represent 0-fff cannot work because the range does not include 2 digit and 1 digit numbers //This only happens when the lower value is 0 and there is more than 1 range digit //That's because you can then omit any leading zeros. //Ranges like f___ representing f000-ffff are fine. if !writer.IncludesZero() || rangeDigits == 1 { return rangeDigits } } return 0 } func (writer stringWriter) getRangeStringWithCounts( segmentIndex int, params addressSegmentParams, lowerLeadingZerosCount int, upperLeadingZerosCount int, maskUpper bool, appendable *strings.Builder) int { _ = segmentIndex stringPrefix := params.getSegmentStrPrefix() radix := params.getRadix() rangeSeparator := params.getWildcards().GetRangeSeparator() uppercase := params.isUppercase() return getRangeString(writer.DivisionType, rangeSeparator, lowerLeadingZerosCount, upperLeadingZerosCount, stringPrefix, radix, uppercase, maskUpper, appendable) } func (writer stringWriter) writeSplitRangeString( segmentIndex int, params addressSegmentParams, appendable *strings.Builder) (int, addrerr.IncompatibleAddressError) { stringPrefix := params.getSegmentStrPrefix() radix := params.getRadix() leadingZeroCount := params.getLeadingZeros(segmentIndex) //for split ranges, it is the leading zeros of the upper value that matters leadingZeroCount = writer.adjustUpperLeadingZeroCount(leadingZeroCount, radix) wildcards := params.getWildcards() uppercase := params.isUppercase() var splitDigitSeparator byte = ' ' if params.hasSeparator() { splitDigitSeparator = params.getSplitDigitSeparator() } reverseSplitDigits := params.isReverseSplitDigits() rangeSeparator := wildcards.GetRangeSeparator() if appendable == nil { return writer.getSplitRangeStringLength( rangeSeparator, wildcards.GetWildcard(), leadingZeroCount, radix, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix), nil } else { hasLeadingZeros := leadingZeroCount != 0 if hasLeadingZeros && !reverseSplitDigits { getSplitLeadingZeros(leadingZeroCount, splitDigitSeparator, stringPrefix, appendable) appendable.WriteByte(splitDigitSeparator) hasLeadingZeros = false } if err := writer.getSplitRangeString( rangeSeparator, wildcards.GetWildcard(), radix, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, appendable); err != nil { return 0, err } if hasLeadingZeros { appendable.WriteByte(splitDigitSeparator) getSplitLeadingZeros(leadingZeroCount, splitDigitSeparator, stringPrefix, appendable) } } return 0, nil } func getSplitCharStr(count int, splitDigitSeparator byte, characters string, stringPrefix string, builder *strings.Builder) { prefLen := len(stringPrefix) if count > 0 { for { if prefLen > 0 { builder.WriteString(stringPrefix) } builder.WriteString(characters) count-- if count <= 0 { break } builder.WriteByte(splitDigitSeparator) } } } func getSplitChar(count int, splitDigitSeparator, character byte, stringPrefix string, builder *strings.Builder) { prefLen := len(stringPrefix) if count > 0 { for { if prefLen > 0 { builder.WriteString(stringPrefix) } builder.WriteByte(character) count-- if count <= 0 { break } builder.WriteByte(splitDigitSeparator) } } } func getSplitLeadingZeros(leadingZeroCount int, splitDigitSeparator byte, stringPrefix string, builder *strings.Builder) { getSplitChar(leadingZeroCount, splitDigitSeparator, '0', stringPrefix, builder) } func appendUppercase(str string, radix int, appendable *strings.Builder) { if radix > 10 { for i := 0; i < len(str); i++ { c := str[i] if c >= 'a' && c <= 'z' { c -= byte('a') - byte('A') } appendable.WriteByte(c) } } else { appendable.WriteString(str) } } func getFullRangeString(wildcard string, appendable *strings.Builder) int { if appendable == nil { return len(wildcard) } appendable.WriteString(wildcard) return 0 } func getLeadingZeros(leadingZeroCount int, builder *strings.Builder) { if leadingZeroCount > 0 { stringArray := zeros increment := len(stringArray) if leadingZeroCount > increment { for leadingZeroCount > increment { builder.WriteString(stringArray) leadingZeroCount -= increment } } builder.WriteString(stringArray[:leadingZeroCount]) } } const zeros = "00000000000000000000" func toNormalizedStringRange(params *addressStringParams, lower, upper AddressDivisionSeries, zone Zone) string { if lower.GetDivisionCount() > 0 { var builder strings.Builder length := params.getStringLength(lower) + params.getZonedStringLength(upper, zone) separator := params.getWildcards().GetRangeSeparator() if separator != "" { length += len(separator) builder.Grow(length) params.append(&builder, lower).WriteString(separator) params.appendZoned(&builder, upper, zone) } else { builder.Grow(length) params.appendZoned(params.append(&builder, lower), upper, zone) } checkLengths(length, &builder) return builder.String() } return "" } ipaddress-go-1.5.4/ipaddr/strings.go000066400000000000000000000733771440250641600174100ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math/big" "strconv" "strings" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" ) const ( digits = "0123456789abcdefghijklmnopqrstuvwxyz" extendedDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~" uppercaseDigits = extendedDigits doubleDigitsDecimal = "00010203040506070809" + "10111213141516171819" + "20212223242526272829" + "30313233343536373839" + "40414243444546474849" + "50515253545556575859" + "60616263646566676869" + "70717273747576777879" + "80818283848586878889" + "90919293949596979899" ) func isExtendedDigits(radix int) bool { return radix > len(digits) } func getDigits(uppercase bool, radix int) string { if uppercase || isExtendedDigits(radix) { return uppercaseDigits } return digits } func toUnsignedString(value uint64, radix int, appendable *strings.Builder) *strings.Builder { return toUnsignedStringCased(value, radix, 0, false, appendable) } func toUnsignedStringCased(value uint64, radix, choppedDigits int, uppercase bool, appendable *strings.Builder) *strings.Builder { if value > 0xffff || choppedDigits != 0 || !toUnsignedStringFast(uint16(value), radix, uppercase, appendable) { toUnsignedStringSlow(value, radix, choppedDigits, uppercase, appendable) } return appendable } func toUnsignedStringFast(value uint16, radix int, uppercase bool, appendable *strings.Builder) bool { if value <= 1 { //for values larger than 1, result can be different with different radix (radix is 2 and up) if value == 0 { appendable.WriteByte('0') } else { appendable.WriteByte('1') } return true } //var quotient, remainder uint //we iterate on value == quotient * radix + remainder if radix == 10 { // we know value <= 0xffff (ie 16 bits or less) if value < 10 { appendable.WriteByte(digits[value]) return true } else if value < 100 { dig := doubleDigitsDecimal digIndex := value << 1 appendable.WriteByte(dig[digIndex]) appendable.WriteByte(dig[digIndex+1]) return true } else if value < 200 { dig := doubleDigitsDecimal digIndex := (value - 100) << 1 appendable.WriteByte('1') appendable.WriteByte(dig[digIndex]) appendable.WriteByte(dig[digIndex+1]) return true } else if value < 300 { dig := doubleDigitsDecimal digIndex := (value - 200) << 1 appendable.WriteByte('2') appendable.WriteByte(dig[digIndex]) appendable.WriteByte(dig[digIndex+1]) return true } dig := digits uval := uint(value) var res [5]byte i := 4 for { //value == quotient * 10 + remainder quotient := (uval * 0xcccd) >> 19 // floor of n/10 is floor of ((0xcccd * n / 2^16) / 2^3) remainder := uval - ((quotient << 3) + (quotient << 1)) //multiplication by 2 added to multiplication by 2^3 is multiplication by 2 + 8 = 10 res[i] = dig[remainder] uval = quotient if uval == 0 { break } i-- } appendable.Write(res[i:]) return true } else if radix == 16 { if value < 0x10 { dig := getDigits(uppercase, radix) appendable.WriteByte(dig[value]) return true } else if value == 0xffff { if uppercase { appendable.WriteString("FFFF") } else { appendable.WriteString("ffff") } return true } dig := getDigits(uppercase, radix) shift := uint(12) for { index := (value >> shift) & 15 if index != 0 { // index 0 is digit "0", no need to write leading zeros appendable.WriteByte(dig[index]) shift -= 4 for shift > 0 { appendable.WriteByte(dig[(value>>shift)&15]) shift -= 4 } break } shift -= 4 if shift == 0 { break } } appendable.WriteByte(dig[value&15]) return true } else if radix == 8 { dig := digits if value < 010 { appendable.WriteByte(dig[value]) return true } shift := uint(15) for { index := (value >> shift) & 7 if index != 0 { // index 0 is digit "0" appendable.WriteByte(dig[index]) shift -= 3 for shift > 0 { appendable.WriteByte(dig[(value>>shift)&7]) shift -= 3 } break } shift -= 3 if shift == 0 { break } } appendable.WriteByte(dig[value&7]) return true } else if radix == 2 { //note that we already know value != 0 and that value <= 0xffff var digitIndex int if (value >> 8) == 0 { if value == 0xff { appendable.WriteString("11111111") return true } else if (value >> 4) == 0 { digitIndex = 4 } else { digitIndex = 8 } } else { if value == 0xffff { appendable.WriteString("1111111111111111") return true } else if (value >> 4) == 0 { digitIndex = 12 } else { digitIndex = 16 } } for digitIndex--; digitIndex > 0; digitIndex-- { digit := (value >> uint(digitIndex)) & 1 if digit == 1 { appendable.WriteByte('1') for digitIndex--; digitIndex > 0; digitIndex-- { digit = (value >> uint(digitIndex)) & 1 if digit == 0 { appendable.WriteByte('0') } else { appendable.WriteByte('1') } } break } } if (value & 1) == 0 { appendable.WriteByte('0') } else { appendable.WriteByte('1') } return true } return false } func toUnsignedStringSlow( value uint64, radix, choppedDigits int, uppercase bool, appendable *strings.Builder) { var str string if radix <= 36 { // strconv.FormatUint doesn't work with larger radix str = strconv.FormatUint(value, radix) if choppedDigits > 0 { str = str[:len(str)-choppedDigits] } if uppercase && radix > 10 { strlen := len(str) diff := uint8('a' - 'A') for i := 0; i < strlen; i++ { c := str[i] if c > '9' { c -= diff } appendable.WriteByte(c) } } else { appendable.WriteString(str) } return } var bytes [13]byte index := 13 dig := extendedDigits rad64 := uint64(radix) for value >= rad64 { val := value value /= rad64 if choppedDigits > 0 { choppedDigits-- continue } index-- remainder := val - (value * rad64) bytes[index] = dig[remainder] } if choppedDigits == 0 { appendable.WriteByte(dig[value]) } appendable.Write(bytes[index:]) } func toUnsignedStringLength(value uint64, radix int) int { if value <= 0xffff { if result := toUnsignedStringLengthFast(uint16(value), radix); result >= 0 { return result } } return toUnsignedStringLengthSlow(value, radix) } const maxUint = ^uint(0) func toUnsignedStringLengthSlow(value uint64, radix int) int { count := 1 useInts := value <= uint64(maxUint) value2 := uint(radix) if useInts { value2 = uint(value) } uradix := uint(radix) for value2 >= uradix { if useInts { value2 /= uradix } else { value /= uint64(radix) if value <= uint64(maxUint) { useInts = true value2 = uint(value) } } count++ } return count } func toUnsignedStringLengthFast(value uint16, radix int) int { if value <= 1 { //for values larger than 1, result can be different with different radix (radix is 2 and up) return 1 } if radix == 10 { //this needs value <= 0xffff (ie 16 bits or less) which is a prereq to calling this method if value < 10 { return 1 } else if value < 100 { return 2 } else if value < 1000 { return 3 } else if value < 10000 { return 4 } return 5 } else if radix == 16 { //this needs value <= 0xffff (ie 16 bits or less) if value < 0x10 { return 1 } else if value < 0x100 { return 2 } else if value < 0x1000 { return 3 } return 4 } else if radix == 8 { //this needs value <= 0xffff (ie 16 bits or less) if value < 010 { return 1 } else if value < 0100 { return 2 } else if value < 01000 { return 3 } else if value < 010000 { return 4 } else if value < 0100000 { return 5 } return 6 } else if radix == 2 { //count the number of digits //note that we already know value != 0 and that value <= 0xffff //and we use both of those facts digitCount := 15 val := value if val>>8 == 0 { digitCount -= 8 } else { val >>= 8 } if val>>4 == 0 { digitCount -= 4 } else { val >>= 4 } if val>>2 == 0 { digitCount -= 2 } else { val >>= 2 } //at this point, if (val & 2) != 0 we have undercounted the digit count by 1 if (val & 2) != 0 { digitCount++ } return digitCount } return -1 } func toDefaultString(val uint64, radix int) string { //0 and 1 are common segment values, and additionally they are the same regardless of radix (even binary) //so we have a fast path for them if val == 0 { return "0" } else if val == 1 { return "1" } var length int var quotient, remainder, value uint //we iterate on //value == quotient * radix + remainder if radix == 10 { if val < 10 { return digits[val : val+1] } else if val < 100 { dig := doubleDigitsDecimal value = uint(val) digIndex := value << 1 var builder strings.Builder builder.Grow(2) builder.WriteByte(dig[digIndex]) builder.WriteByte(dig[digIndex+1]) return builder.String() } else if val < 200 { dig := doubleDigitsDecimal value = uint(val) digIndex := (value - 100) << 1 var builder strings.Builder builder.WriteByte('1') builder.WriteByte(dig[digIndex]) builder.WriteByte(dig[digIndex+1]) return builder.String() } else if val < 300 { dig := doubleDigitsDecimal value = uint(val) digIndex := (value - 200) << 1 var builder strings.Builder builder.WriteByte('2') builder.WriteByte(dig[digIndex]) builder.WriteByte(dig[digIndex+1]) return builder.String() } else if val < 1000 { length = 3 value = uint(val) } else { return strconv.FormatUint(val, 10) } chars := make([]byte, length) dig := digits for value != 0 { length-- //value == quotient * 10 + remainder quotient = (value * 0xcccd) >> 19 //floor of n/10 is floor of ((0xcccd * n / (2 ^ 16)) / (2 ^ 3)) remainder = value - ((quotient << 3) + (quotient << 1)) //multiplication by 2 added to multiplication by 2 ^ 3 is multiplication by 2 + 8 = 10 chars[length] = dig[remainder] value = quotient } return string(chars) } else if radix == 16 { if val < 0x10 { return digits[val : val+1] } var builder strings.Builder if val < 0x100 { length = 2 value = uint(val) } else if val < 0x1000 { length = 3 value = uint(val) } else if val < 0x10000 { if val == 0xffff { return "ffff" } value = uint(val) length = 4 } else { return strconv.FormatUint(val, 16) } dig := digits builder.Grow(length) shift := uint(12) for { index := (value >> shift) & 15 if index != 0 { // index 0 is digit "0", so no need to write a leading zero builder.WriteByte(dig[index]) shift -= 4 for shift > 0 { builder.WriteByte(dig[(value>>shift)&15]) shift -= 4 } break } shift -= 4 if shift == 0 { break } } builder.WriteByte(dig[value&15]) return builder.String() } return strconv.FormatUint(val, radix) } func toDefaultBigString(val, radix *BigDivInt, uppercase bool, choppedDigits, maxDigits int) string { if bigIsZero(val) { return "0" } else if bigAbsIsOne(val) { return "1" } dig := getDigits(uppercase, int(radix.Uint64())) var builder strings.Builder if maxDigits > 0 { //maxDigits is 0 or less if the max digits is unknown if maxDigits <= choppedDigits { return "" } toDefaultStringRecursive(val, radix, uppercase, choppedDigits, maxDigits, dig, true, &builder) } else { var quotient big.Int quotient.Set(val) for { //value == quotient * 16 + remainder var remainder big.Int quotient.QuoRem("ient, radix, &remainder) if choppedDigits > 0 { choppedDigits-- continue } builder.WriteByte(dig[remainder.Uint64()]) if bigIsZero("ient) { break } } if builder.Len() == 0 { return "" // all digits are chopped } return reverse(builder.String()) } return builder.String() } func toDefaultStringRecursive(val *BigDivInt, radix *BigDivInt, uppercase bool, choppedDigits, digitCount int, dig string, highest bool, builder *strings.Builder) { if val.IsUint64() { longVal := val.Uint64() intRadix := int(radix.Int64()) if !highest { getLeadingZeros(digitCount-toUnsignedStringLength(longVal, intRadix), builder) } toUnsignedStringCased(longVal, intRadix, choppedDigits, uppercase, builder) } else if digitCount > choppedDigits { halfCount := digitCount >> 1 var quotient, remainder big.Int var radixPower = getRadixPower(radix, halfCount) quotient.QuoRem(val, radixPower, &remainder) if highest && bigIsZero("ient) { // only do low toDefaultStringRecursive(&remainder, radix, uppercase, choppedDigits, halfCount, dig, true, builder) } else { toDefaultStringRecursive("ient, radix, uppercase, max(0, choppedDigits-halfCount), digitCount-halfCount, dig, highest, builder) toDefaultStringRecursive(&remainder, radix, uppercase, choppedDigits, halfCount, dig, false, builder) } } } func getRadixPower(radix *big.Int, power int) *big.Int { if power == 1 { return radix } intRadix := radix.Uint64() key := intRadix<<32 | uint64(power) theMapPtr := (*map[uint64]*big.Int)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&radixPowerMap)))) theMap := *theMapPtr if res, ok := theMap[key]; ok { return res } result := new(big.Int) if (power & 1) == 0 { halfPower := getRadixPower(radix, power>>1) result.Mul(halfPower, halfPower) } else { halfPower := getRadixPower(radix, (power-1)>>1) result.Mul(halfPower, halfPower).Mul(result, radix) } //replace the map atomically newRadixMap := createRadixMap() theNewMap := *newRadixMap for k, val := range theMap { theNewMap[k] = val } theNewMap[key] = result dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&radixPowerMap)) atomicStorePointer(dataLoc, unsafe.Pointer(newRadixMap)) return result } var radixPowerMap = createRadixMap() // we use a pointer so we can overwrite atomically func createRadixMap() *map[uint64]*big.Int { res := make(map[uint64]*big.Int) return &res } func reverse(s string) string { bts := []byte(s) for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { bts[i], bts[j] = bts[j], bts[i] } return string(bts) } func getDefaultRangeStringVals(strProvider divStringProvider, val1, val2 uint64, radix int) string { var len1, len2 int // we iterate on //value == quotient * radix + remainder var value1, value2 uint // we iterate on //value == quotient * radix + remainder if radix == 10 { if val2 < 10 { len2 = 1 } else if val2 < 100 { len2 = 2 } else if val2 < 1000 { len2 = 3 } else { return buildDefaultRangeString(strProvider, radix) } value2 = uint(val2) if val1 < 10 { len1 = 1 } else if val1 < 100 { len1 = 2 } else if val1 < 1000 { len1 = 3 } else { return buildDefaultRangeString(strProvider, radix) } value1 = uint(val1) charsStr := strings.Builder{} charsStr.Grow(len1 + len2 + 1) dig := digits doubleDig := doubleDigitsDecimal var quotient, remainder uint var chars []byte if val1 < 10 { charsStr.WriteByte(dig[value1]) } else if val1 < 100 { digIndex := value1 << 1 charsStr.WriteByte(doubleDig[digIndex]) charsStr.WriteByte(doubleDig[digIndex+1]) } else if val1 < 200 { charsStr.WriteByte('1') digIndex := (value1 - 100) << 1 charsStr.WriteByte(doubleDig[digIndex]) charsStr.WriteByte(doubleDig[digIndex+1]) } else if val1 < 300 { charsStr.WriteByte('2') digIndex := (value1 - 200) << 1 charsStr.WriteByte(doubleDig[digIndex]) charsStr.WriteByte(doubleDig[digIndex+1]) } else { chars = make([]byte, len2) // note that len2 >= len1 origLen1 := len1 for { //value == quotient * 10 + remainder quotient = (value1 * 0xcccd) >> 19 //floor of n/10 is floor of ((0xcccd * n / (2 ^ 16)) / (2 ^ 3)) remainder = value1 - ((quotient << 3) + (quotient << 1)) //multiplication by 2 added to multiplication by 2 ^ 3 is multiplication by 2 + 8 = 10 len1-- chars[len1] = dig[remainder] value1 = quotient if value1 == 0 { break } } charsStr.Write(chars[:origLen1]) } charsStr.WriteByte(RangeSeparator) if val2 < 10 { charsStr.WriteByte(dig[value2]) } else if val2 < 100 { digIndex := value2 << 1 charsStr.WriteByte(doubleDig[digIndex]) charsStr.WriteByte(doubleDig[digIndex+1]) } else if val2 < 200 { charsStr.WriteByte('1') digIndex := (value2 - 100) << 1 charsStr.WriteByte(doubleDig[digIndex]) charsStr.WriteByte(doubleDig[digIndex+1]) } else if val2 < 300 { charsStr.WriteByte('2') digIndex := (value2 - 200) << 1 charsStr.WriteByte(doubleDig[digIndex]) charsStr.WriteByte(doubleDig[digIndex+1]) } else { origLen2 := len2 if chars == nil { chars = make([]byte, len2) } for { quotient = (value2 * 0xcccd) >> 19 remainder = value2 - ((quotient << 3) + (quotient << 1)) len2-- chars[len2] = dig[remainder] value2 = quotient if value2 == 0 { break } } charsStr.Write(chars[:origLen2]) } return charsStr.String() } else if radix == 16 { if val2 < 0x10 { len2 = 1 } else if val2 < 0x100 { len2 = 2 } else if val2 < 0x1000 { len2 = 3 } else if val2 < 0x10000 { len2 = 4 } else { return buildDefaultRangeString(strProvider, radix) } if val1 < 0x10 { len1 = 1 } else if val1 < 0x100 { len1 = 2 } else if val1 < 0x1000 { len1 = 3 } else if val1 < 0x10000 { len1 = 4 } else { return buildDefaultRangeString(strProvider, radix) } value1 = uint(val1) charsStr := strings.Builder{} charsStr.Grow(len1 + len2 + 1) dig := digits if val1 < 0x10 { charsStr.WriteByte(dig[value1]) } else { shift := uint(12) for { index := (value1 >> shift) & 15 if index != 0 { // index 0 is digit "0" charsStr.WriteByte(dig[index]) shift -= 4 for shift > 0 { charsStr.WriteByte(dig[(value1>>shift)&15]) shift -= 4 } break } shift -= 4 if shift == 0 { break } } charsStr.WriteByte(dig[value1&15]) } charsStr.WriteByte(RangeSeparator) value2 = uint(val2) if val2 < 0x10 { charsStr.WriteByte(dig[value2]) } else { shift := uint(12) for { index := (value2 >> shift) & 15 if index != 0 { // index 0 is digit "0" charsStr.WriteByte(dig[index]) shift -= 4 for shift > 0 { charsStr.WriteByte(dig[(value2>>shift)&15]) shift -= 4 } break } shift -= 4 if shift == 0 { break } } charsStr.WriteByte(dig[value2&15]) } return charsStr.String() } return buildDefaultRangeString(strProvider, radix) } func buildDefaultRangeString(strProvider divStringProvider, radix int) string { builder := strings.Builder{} builder.Grow(20) getRangeString(strProvider, RangeSeparatorStr, 0, 0, "", radix, false, false, &builder) return builder.String() } func getRangeString( strProvider divStringProvider, rangeSeparator string, lowerLeadingZerosCount, upperLeadingZerosCount int, stringPrefix string, radix int, uppercase, maskUpper bool, appendable *strings.Builder) int { prefLen := len(stringPrefix) hasStringPrefix := prefLen > 0 if appendable == nil { count := lowerLeadingZerosCount + upperLeadingZerosCount + strProvider.getLowerStringLength(radix) + strProvider.getUpperStringLength(radix) + len(rangeSeparator) if hasStringPrefix { count += prefLen << 1 } return count } else { if hasStringPrefix { appendable.WriteString(stringPrefix) } if lowerLeadingZerosCount > 0 { getLeadingZeros(lowerLeadingZerosCount, appendable) } strProvider.getLowerString(radix, uppercase, appendable) appendable.WriteString(rangeSeparator) if hasStringPrefix { appendable.WriteString(stringPrefix) } if upperLeadingZerosCount > 0 { getLeadingZeros(upperLeadingZerosCount, appendable) } if maskUpper { strProvider.getUpperStringMasked(radix, uppercase, appendable) } else { strProvider.getUpperString(radix, uppercase, appendable) } } return 0 } func toSplitUnsignedString( value uint64, radix, choppedDigits int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string, appendable *strings.Builder) { if reverseSplitDigits { appendDigits(value, radix, choppedDigits, uppercase, splitDigitSeparator, stringPrefix, appendable) } else { // for ::1 this produces // 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa // each seg is 4 digits like 1.0.0.0 for the last seg var tmpBuilder strings.Builder appendDigits(value, radix, choppedDigits, uppercase, splitDigitSeparator, stringPrefix, &tmpBuilder) stringPrefixLen := len(stringPrefix) str := tmpBuilder.String() back := tmpBuilder.Len() - 1 for { appendable.WriteString(stringPrefix) appendable.WriteByte(str[back]) back -= stringPrefixLen // skip the prefix, if any back -= 2 // 1 for the separator, 1 for the byte if back < 0 { break } appendable.WriteByte(splitDigitSeparator) } } } func toUnsignedSplitRangeString( lower, upper uint64, rangeSeparator, wildcard string, radix int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string, appendable *strings.Builder) (err addrerr.IncompatibleAddressError) { //A split can be invalid. Consider xxx.456-789. //The number 691, which is in the range 456-789, is not in the range 4-7.5-8.6-9 //In such cases we have IncompatibleAddressError //To avoid such cases, we must have lower digits covering the full range, for example 400-799 in which lower digits are both 0-9 ranges. //If we have 401-799 then 500 will not be included when splitting. //If we have 400-798 then 599 will not be included when splitting. //If we have 410-799 then 500 will not be included when splitting. //If we have 400-789 then 599 will not be included when splitting. if reverseSplitDigits { err = appendRangeDigits(lower, upper, rangeSeparator, wildcard, radix, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, appendable) } else { var tmpBuilder strings.Builder err = appendRangeDigits(lower, upper, rangeSeparator, wildcard, radix, uppercase, splitDigitSeparator, reverseSplitDigits, stringPrefix, &tmpBuilder) if err == nil { str := tmpBuilder.String() for back := tmpBuilder.Len() - 1; back >= 0; back-- { appendable.WriteByte(str[back]) } } } return } func toUnsignedSplitRangeStringLength( lower, upper uint64, rangeSeparator, wildcard string, leadingZerosCount, radix int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string) int { _ = rangeSeparator _ = uppercase _ = splitDigitSeparator _ = reverseSplitDigits digitsLength := -1 //we will count one too many split digit separators in here stringPrefixLength := len(stringPrefix) radix64 := uint64(radix) for { upperDigit := int(upper % radix64) lowerDigit := int(lower % radix64) isFull := (lowerDigit == 0) && (upperDigit == radix-1) if isFull { digitsLength += len(wildcard) + 1 } else { //if not full range, they must not be the same either, otherwise they would be illegal for split range. //this is because we know whenever entering the loop that upper != lower, and we know this also means the least significant digits must differ. digitsLength += (stringPrefixLength << 1) + 4 /* 1 for each digit, 1 for range separator, 1 for split digit separator */ } upper /= radix64 lower /= radix64 if upper == lower { break } } remaining := 0 if upper != 0 { remaining = toUnsignedStringLength(upper, radix) } remaining += leadingZerosCount if remaining > 0 { digitsLength += remaining * (stringPrefixLength + 2 /* one for each splitDigitSeparator, 1 for each digit */) } return digitsLength } func appendDigits( value uint64, radix int, choppedDigits int, uppercase bool, splitDigitSeparator byte, stringPrefix string, appendable *strings.Builder) { useInts := value <= uint64(maxUint) value2 := uint(radix) if useInts { value2 = uint(value) } uradix := uint(radix) rad64 := uint64(radix) dig := digits if uppercase { dig = uppercaseDigits } var index uint prefLen := len(stringPrefix) for value2 >= uradix { if useInts { val := value2 value2 /= uradix if choppedDigits > 0 { choppedDigits-- continue } index = val % uradix } else { val := value value /= rad64 if value <= uint64(maxUint) { useInts = true value2 = uint(value) } if choppedDigits > 0 { choppedDigits-- continue } index = uint(val % rad64) } if prefLen > 0 { appendable.WriteString(stringPrefix) } appendable.WriteByte(dig[index]) appendable.WriteByte(splitDigitSeparator) } if choppedDigits == 0 { if prefLen > 0 { appendable.WriteString(stringPrefix) } appendable.WriteByte(dig[value2]) } } func appendRangeDigits( lower, upper uint64, rangeSeparator, wildcard string, radix int, uppercase bool, splitDigitSeparator byte, reverseSplitDigits bool, stringPrefix string, appendable *strings.Builder) addrerr.IncompatibleAddressError { dig := digits if uppercase { dig = uppercaseDigits } previousWasFullRange := true useInts := upper <= uint64(maxUint) lowerInt := uint(radix) upperInt := lowerInt if useInts { upperInt = uint(upper) lowerInt = uint(lower) } uradix := uint(radix) rad64 := uint64(radix) prefLen := len(stringPrefix) for { var upperDigit, lowerDigit uint if useInts { ud := upperInt upperDigit = upperInt % uradix upperInt /= uradix if ud == lowerInt { lowerInt = upperInt lowerDigit = upperDigit } else { lowerDigit = lowerInt % uradix lowerInt /= uradix } } else { ud := upper upperDigit = uint(upper % rad64) upper /= rad64 if ud == lower { lower = upper lowerDigit = upperDigit } else { lowerDigit = uint(lower % rad64) lower /= rad64 } if upper <= uint64(maxUint) { useInts = true upperInt = uint(upper) lowerInt = uint(lower) } } if lowerDigit == upperDigit { previousWasFullRange = false if reverseSplitDigits { if prefLen > 0 { appendable.WriteString(stringPrefix) } appendable.WriteByte(dig[lowerDigit]) } else { //in this case, whatever we do here will be completely reversed following this method call appendable.WriteByte(dig[lowerDigit]) for k := prefLen - 1; k >= 0; k-- { appendable.WriteByte(stringPrefix[k]) } } } else { if !previousWasFullRange { return &incompatibleAddressError{addressError{key: "ipaddress.error.splitMismatch"}} } previousWasFullRange = (lowerDigit == 0) && (upperDigit == uradix-1) if previousWasFullRange && len(wildcard) > 0 { if reverseSplitDigits { appendable.WriteString(wildcard) } else { //in this case, whatever we do here will be completely reversed following this method call for k := len(wildcard) - 1; k >= 0; k-- { appendable.WriteByte(wildcard[k]) } } } else { if reverseSplitDigits { if prefLen > 0 { appendable.WriteString(stringPrefix) } appendable.WriteByte(dig[lowerDigit]) appendable.WriteString(rangeSeparator) appendable.WriteByte(dig[upperDigit]) } else { //in this case, whatever we do here will be completely reversed following this method call appendable.WriteByte(dig[upperDigit]) appendable.WriteString(rangeSeparator) appendable.WriteByte(dig[lowerDigit]) for k := prefLen - 1; k >= 0; k-- { appendable.WriteByte(stringPrefix[k]) } } } } if upperInt == 0 { break } appendable.WriteByte(splitDigitSeparator) } return nil } var maxDigitMap = createDigitMap() // we use a pointer so we can overwrite atomically func createDigitMap() *map[uint64]int { res := make(map[uint64]int) return &res } func getBigMaxDigitCount(radix int, bitCount BitCount, maxValue *BigDivInt) int { return getMaxDigitCountCalc(radix, bitCount, func() int { return getBigDigitCount(maxValue, big.NewInt(int64(radix))) }) } func getMaxDigitCount(radix int, bitCount BitCount, maxValue uint64) int { return getMaxDigitCountCalc(radix, bitCount, func() int { return getDigitCount(maxValue, radix) }) } func getMaxDigitCountCalc(radix int, bitCount BitCount, calc func() int) int { rad64 := uint64(radix) key := (rad64 << 32) | uint64(bitCount) theMapPtr := (*map[uint64]int)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&maxDigitMap)))) theMap := *theMapPtr if digs, ok := theMap[key]; ok { return digs } digs := calc() newMaxDigitMap := createDigitMap() theNewMap := *newMaxDigitMap for k, val := range theMap { theNewMap[k] = val } theNewMap[key] = digs dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&maxDigitMap)) atomicStorePointer(dataLoc, unsafe.Pointer(newMaxDigitMap)) return digs } func getDigitCount(value uint64, radix int) int { result := 1 if radix == 16 { for { value >>= 4 if value == 0 { break } result++ } } else { if radix == 10 { if value < 10 { return 1 } else if value < 100 { return 2 } else if value < 1000 { return 3 } value /= 1000 result = 3 //we start with 3 in the loop below } else if radix == 8 { for { value >>= 3 if value == 0 { break } result++ } return result } rad64 := uint64(radix) for { value /= rad64 if value == 0 { break } result++ } } return result } func getBigDigitCount(val, radix *BigDivInt) int { if bigIsZero(val) || bigAbsIsOne(val) { return 1 } result := 1 var v big.Int v.Set(val) for { v.Quo(&v, radix) if bigIsZero(&v) { break } result++ } return result } ipaddress-go-1.5.4/ipaddr/test/000077500000000000000000000000001440250641600163265ustar00rootroot00000000000000ipaddress-go-1.5.4/ipaddr/test/addresses.go000066400000000000000000000635341440250641600206450ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package test import ( "net" "sync" "github.com/seancfoley/ipaddress-go/ipaddr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) var ( hostOptions = new(addrstrparam.HostNameParamsBuilder). AllowEmpty(false). NormalizeToLowercase(true). AllowPort(true). AllowService(true). AllowBracketedIPv6(true). AllowBracketedIPv4(true). GetIPAddressParamsBuilder(). AllowPrefix(true). AllowMask(true). SetRangeParams(addrstrparam.NoRange). Allow_inet_aton(false). AllowEmpty(false). AllowAll(false). AllowSingleSegment(false). GetIPv4AddressParamsBuilder(). AllowLeadingZeros(true). AllowUnlimitedLeadingZeros(false). AllowPrefixLenLeadingZeros(true). AllowPrefixesBeyondAddressSize(false). AllowWildcardedSeparator(true). AllowBinary(true). GetParentBuilder(). GetIPv6AddressParamsBuilder(). AllowLeadingZeros(true). AllowUnlimitedLeadingZeros(false). AllowPrefixLenLeadingZeros(true). AllowPrefixesBeyondAddressSize(false). AllowWildcardedSeparator(true). AllowMixed(true). AllowZone(true). AllowBinary(true). GetParentBuilder().GetParentBuilder().ToParams() hostInetAtonOptions = new(addrstrparam.HostNameParamsBuilder).Set(hostOptions).GetIPAddressParamsBuilder().Allow_inet_aton(true).AllowSingleSegment(true).GetParentBuilder().ToParams() addressOptions = new(addrstrparam.IPAddressStringParamsBuilder).Set(hostOptions.GetIPAddressParams()).ToParams() macAddressOptions = new(addrstrparam.MACAddressStringParamsBuilder). AllowEmpty(false). AllowAll(false). GetFormatParamsBuilder(). SetRangeParams(addrstrparam.NoRange). AllowLeadingZeros(true). AllowUnlimitedLeadingZeros(false). AllowWildcardedSeparator(true). AllowShortSegments(true). GetParentBuilder(). ToParams() ) type testAddresses interface { createAddress(string) *ipaddr.IPAddressString createInetAtonAddress(string) *ipaddr.IPAddressString createParametrizedAddress(string, addrstrparam.RangeParams) *ipaddr.IPAddressString createParamsAddress(string, addrstrparam.IPAddressStringParams) *ipaddr.IPAddressString createAddressFromIP(ip net.IP) *ipaddr.IPAddress createIPv4Address(uint32) *ipaddr.IPv4Address createIPv6Address(high, low uint64) *ipaddr.IPv6Address createDoubleParametrizedAddress(str string, ipv4Params, ipv6Params addrstrparam.RangeParams) *ipaddr.IPAddressString createHost(string) *ipaddr.HostName createInetAtonHost(string) *ipaddr.HostName createParamsHost(string, addrstrparam.HostNameParams) *ipaddr.HostName createMACAddress(string) *ipaddr.MACAddressString createMACAddressFromBytes(bytes net.HardwareAddr) *ipaddr.MACAddress createMACAddressFromUint64(bytes uint64, extended bool) *ipaddr.MACAddress createMACParamsAddress(string, addrstrparam.MACAddressStringParams) *ipaddr.MACAddressString isLenient() bool allowsRange() bool getAllCached() []*ipaddr.IPAddress getAllMACCached() []*ipaddr.MACAddress } type addresses struct { caching bool strIPAddressStrCache map[string]*ipaddr.IPAddressString strIPAddressStrCacheLock *sync.Mutex inetAtonStrIPAddressStrCache map[string]*ipaddr.IPAddressString inetAtonStrIPAddressStrCacheLock *sync.Mutex netIPv4AddressCache map[[4]byte]*ipaddr.IPAddress netIPv4AddressCacheLock *sync.Mutex netIPv6AddressCache map[[16]byte]*ipaddr.IPAddress netIPv6AddressCacheLock *sync.Mutex intIPv4AddressCache map[uint32]*ipaddr.IPv4Address intIPv4AddressCacheLock *sync.Mutex intsIPv6AddressCache map[[2]uint64]*ipaddr.IPv6Address intsIPv6AddressCacheLock *sync.Mutex strMACAddressStrCache map[string]*ipaddr.MACAddressString strMACAddressStrCacheLock *sync.Mutex netMACAddressCache map[[6]byte]*ipaddr.MACAddress netMACAddressCacheLock *sync.Mutex netMACExtAddressCache map[[8]byte]*ipaddr.MACAddress netMACExtAddressCacheLock *sync.Mutex uint64MACAddressCache map[uint64]*ipaddr.MACAddress uint64MACAddressCacheLock *sync.Mutex uint64MACExtAddressCache map[uint64]*ipaddr.MACAddress uint64MACExtAddressCacheLock *sync.Mutex strHostStrCache map[string]*ipaddr.HostName strHostStrCacheLock *sync.Mutex inetAtonStrHostStrCache map[string]*ipaddr.HostName inetAtonStrIHostStrCacheLock *sync.Mutex strParamsIPAddressStrCache map[addrstrparam.IPAddressStringParams]map[string]*ipaddr.IPAddressString strParamsIPAddressStrCacheLock *sync.Mutex strParamsMACAddressStrCache map[addrstrparam.MACAddressStringParams]map[string]*ipaddr.MACAddressString strParamsMACAddressStrCacheLock *sync.Mutex strParamsHostStrCache map[addrstrparam.HostNameParams]map[string]*ipaddr.HostName strParamsHostStrCacheLock *sync.Mutex } func (t *addresses) useCache(use bool) { if use { if t.caching { return } t.caching = use t.strIPAddressStrCache = make(map[string]*ipaddr.IPAddressString) t.strIPAddressStrCacheLock = &sync.Mutex{} t.inetAtonStrIPAddressStrCache = make(map[string]*ipaddr.IPAddressString) t.inetAtonStrIPAddressStrCacheLock = &sync.Mutex{} t.netIPv4AddressCache = make(map[[4]byte]*ipaddr.IPAddress) t.netIPv4AddressCacheLock = &sync.Mutex{} t.netIPv6AddressCache = make(map[[16]byte]*ipaddr.IPAddress) t.netIPv6AddressCacheLock = &sync.Mutex{} t.intIPv4AddressCache = make(map[uint32]*ipaddr.IPv4Address) t.intIPv4AddressCacheLock = &sync.Mutex{} t.intsIPv6AddressCache = make(map[[2]uint64]*ipaddr.IPv6Address) t.intsIPv6AddressCacheLock = &sync.Mutex{} t.strMACAddressStrCache = make(map[string]*ipaddr.MACAddressString) t.strMACAddressStrCacheLock = &sync.Mutex{} t.netMACAddressCache = make(map[[6]byte]*ipaddr.MACAddress) t.netMACAddressCacheLock = &sync.Mutex{} t.netMACExtAddressCache = make(map[[8]byte]*ipaddr.MACAddress) t.netMACExtAddressCacheLock = &sync.Mutex{} t.uint64MACAddressCache = make(map[uint64]*ipaddr.MACAddress) t.uint64MACAddressCacheLock = &sync.Mutex{} t.uint64MACExtAddressCache = make(map[uint64]*ipaddr.MACAddress) t.uint64MACExtAddressCacheLock = &sync.Mutex{} t.strHostStrCache = make(map[string]*ipaddr.HostName) t.strHostStrCacheLock = &sync.Mutex{} t.inetAtonStrHostStrCache = make(map[string]*ipaddr.HostName) t.inetAtonStrIHostStrCacheLock = &sync.Mutex{} t.strParamsIPAddressStrCache = make(map[addrstrparam.IPAddressStringParams]map[string]*ipaddr.IPAddressString) t.strParamsIPAddressStrCacheLock = &sync.Mutex{} t.strParamsMACAddressStrCache = make(map[addrstrparam.MACAddressStringParams]map[string]*ipaddr.MACAddressString) t.strParamsMACAddressStrCacheLock = &sync.Mutex{} t.strParamsHostStrCache = make(map[addrstrparam.HostNameParams]map[string]*ipaddr.HostName) t.strParamsHostStrCacheLock = &sync.Mutex{} } else { if !t.caching { return } *t = addresses{} } } func (t *addresses) getAllCached() (all []*ipaddr.IPAddress) { if !t.caching { return } t.strIPAddressStrCacheLock.Lock() t.netIPv4AddressCacheLock.Lock() t.netIPv6AddressCacheLock.Lock() t.intIPv4AddressCacheLock.Lock() t.intsIPv6AddressCacheLock.Lock() all = make([]*ipaddr.IPAddress, 0, len(t.strIPAddressStrCache)+ len(t.netIPv4AddressCache)+len(t.netIPv6AddressCache)+ len(t.intIPv4AddressCache)+len(t.intsIPv6AddressCache)) for _, str := range t.strIPAddressStrCache { if addr := str.GetAddress(); addr != nil { all = append(all, addr) } } for _, addr := range t.netIPv4AddressCache { all = append(all, addr) } for _, addr := range t.netIPv6AddressCache { all = append(all, addr) } for _, addr := range t.intIPv4AddressCache { all = append(all, addr.ToIP()) } for _, addr := range t.intsIPv6AddressCache { all = append(all, addr.ToIP()) } t.intsIPv6AddressCacheLock.Unlock() t.intIPv4AddressCacheLock.Unlock() t.netIPv6AddressCacheLock.Unlock() t.netIPv4AddressCacheLock.Unlock() t.strIPAddressStrCacheLock.Unlock() return } func (t *addresses) getAllMACCached() (all []*ipaddr.MACAddress) { if !t.caching { return } t.strMACAddressStrCacheLock.Lock() t.netMACAddressCacheLock.Lock() t.netMACExtAddressCacheLock.Lock() t.uint64MACAddressCacheLock.Lock() t.uint64MACExtAddressCacheLock.Lock() all = make([]*ipaddr.MACAddress, 0, len(t.strMACAddressStrCache)+ len(t.netMACAddressCache)+len(t.netMACExtAddressCache)+ len(t.uint64MACAddressCache)+len(t.uint64MACExtAddressCache)) for _, str := range t.strMACAddressStrCache { if addr := str.GetAddress(); addr != nil { all = append(all, addr) } } for _, addr := range t.netMACAddressCache { all = append(all, addr) } for _, addr := range t.netMACExtAddressCache { all = append(all, addr) } for _, addr := range t.uint64MACAddressCache { all = append(all, addr) } for _, addr := range t.uint64MACExtAddressCache { all = append(all, addr) } t.uint64MACExtAddressCacheLock.Unlock() t.uint64MACAddressCacheLock.Unlock() t.netMACExtAddressCacheLock.Unlock() t.netMACAddressCacheLock.Unlock() t.strMACAddressStrCacheLock.Unlock() return } func (t *addresses) createParametrizedAddress(str string, params addrstrparam.RangeParams) *ipaddr.IPAddressString { var opts addrstrparam.IPAddressStringParams if params == addrstrparam.NoRange { opts = noRangeAddressOptions } else if params == addrstrparam.WildcardOnly { opts = wildcardOnlyAddressOptions } else if params == addrstrparam.WildcardAndRange { opts = wildcardAndRangeAddressOptions } else { opts = new(addrstrparam.IPAddressStringParamsBuilder).Set(wildcardAndRangeAddressOptions). SetRangeParams(params).ToParams() } if t.caching { return t.createParamsAddress(str, opts) } return ipaddr.NewIPAddressStringParams(str, opts) } func (t *addresses) createParamsAddress(str string, opts addrstrparam.IPAddressStringParams) (res *ipaddr.IPAddressString) { if t.caching { t.strParamsIPAddressStrCacheLock.Lock() defer t.strParamsIPAddressStrCacheLock.Unlock() mp := t.strParamsIPAddressStrCache[opts] if mp == nil { t.strParamsIPAddressStrCache[opts] = make(map[string]*ipaddr.IPAddressString) } else { res = mp[str] if res != nil { return } } } res = ipaddr.NewIPAddressStringParams(str, opts) if t.caching { t.strParamsIPAddressStrCache[opts][str] = res } return } func (t *addresses) createDoubleParametrizedAddress(str string, ipv4Params, ipv6Params addrstrparam.RangeParams) *ipaddr.IPAddressString { var opts addrstrparam.IPAddressStringParams if ipv4Params == ipv6Params { if ipv4Params == addrstrparam.NoRange { opts = noRangeAddressOptions } else if ipv4Params == addrstrparam.WildcardOnly { opts = wildcardOnlyAddressOptions } else if ipv4Params == addrstrparam.WildcardAndRange { opts = wildcardAndRangeAddressOptions } } if opts == nil { opts = new(addrstrparam.IPAddressStringParamsBuilder).Set(wildcardAndRangeAddressOptions). GetIPv4AddressParamsBuilder().SetRangeParams(ipv4Params).GetParentBuilder(). GetIPv6AddressParamsBuilder().SetRangeParams(ipv6Params).GetParentBuilder().ToParams() } if t.caching { return t.createParamsAddress(str, opts) } return ipaddr.NewIPAddressStringParams(str, opts) } func (t *addresses) createAddress(str string) (res *ipaddr.IPAddressString) { if t.caching { t.strIPAddressStrCacheLock.Lock() defer t.strIPAddressStrCacheLock.Unlock() res = t.strIPAddressStrCache[str] if res != nil { //fmt.Printf("reusing %v\n", res) return } } res = ipaddr.NewIPAddressStringParams(str, addressOptions) if t.caching { t.strIPAddressStrCache[str] = res } return } func (t *addresses) createInetAtonAddress(str string) (res *ipaddr.IPAddressString) { if t.caching { t.inetAtonStrIPAddressStrCacheLock.Lock() defer t.inetAtonStrIPAddressStrCacheLock.Unlock() res = t.inetAtonStrIPAddressStrCache[str] if res != nil { return } } res = ipaddr.NewIPAddressStringParams(str, inetAtonwildcardAndRangeOptions) if t.caching { t.inetAtonStrIPAddressStrCache[str] = res } return } func (t *addresses) createAddressFromIP(ip net.IP) (res *ipaddr.IPAddress) { if t.caching { if ipv4 := ip.To4(); ipv4 != nil { t.netIPv4AddressCacheLock.Lock() defer t.netIPv4AddressCacheLock.Unlock() var key [4]byte copy(key[:], ipv4) res = t.netIPv4AddressCache[key] if res != nil { return } res, _ = ipaddr.NewIPAddressFromNetIP(ip) t.netIPv4AddressCache[key] = res } else if len(ip) == 16 { t.netIPv6AddressCacheLock.Lock() defer t.netIPv6AddressCacheLock.Unlock() var key [16]byte copy(key[:], ip) res = t.netIPv6AddressCache[key] if res != nil { return } res, _ = ipaddr.NewIPAddressFromNetIP(ip) t.netIPv6AddressCache[key] = res } else { res, _ = ipaddr.NewIPAddressFromNetIP(ip) } return } res, _ = ipaddr.NewIPAddressFromNetIP(ip) return } func (t *addresses) createIPv4Address(val uint32) (res *ipaddr.IPv4Address) { if t.caching { t.intIPv4AddressCacheLock.Lock() defer t.intIPv4AddressCacheLock.Unlock() res = t.intIPv4AddressCache[val] if res != nil { return } } res = ipaddr.NewIPv4AddressFromUint32(val) if t.caching { t.intIPv4AddressCache[val] = res } return } func (t *addresses) createIPv6Address(high, low uint64) (res *ipaddr.IPv6Address) { if t.caching { t.intsIPv6AddressCacheLock.Lock() defer t.intsIPv6AddressCacheLock.Unlock() var key [2]uint64 key[0], key[1] = low, high res = t.intsIPv6AddressCache[key] if res != nil { return } res = ipaddr.NewIPv6AddressFromUint64(high, low) t.intsIPv6AddressCache[key] = res return } return ipaddr.NewIPv6AddressFromUint64(high, low) } func (t *addresses) createMACAddress(str string) (res *ipaddr.MACAddressString) { if t.caching { t.strMACAddressStrCacheLock.Lock() defer t.strMACAddressStrCacheLock.Unlock() res = t.strMACAddressStrCache[str] if res != nil { //fmt.Printf("reusing %v\n", res) return } } res = ipaddr.NewMACAddressStringParams(str, macAddressOptions) if t.caching { t.strMACAddressStrCache[str] = res } return } func (t *addresses) createMACAddressFromBytes(bytes net.HardwareAddr) (res *ipaddr.MACAddress) { if t.caching { if len(bytes) == 6 { t.netMACAddressCacheLock.Lock() defer t.netMACAddressCacheLock.Unlock() var key [6]byte copy(key[:], bytes) res = t.netMACAddressCache[key] if res != nil { return } res, _ = ipaddr.NewMACAddressFromBytes(bytes) t.netMACAddressCache[key] = res } else if len(bytes) == 8 { t.netMACExtAddressCacheLock.Lock() defer t.netMACExtAddressCacheLock.Unlock() var key [8]byte copy(key[:], bytes) res = t.netMACExtAddressCache[key] if res != nil { return } res, _ = ipaddr.NewMACAddressFromBytes(bytes) t.netMACExtAddressCache[key] = res } else { res, _ = ipaddr.NewMACAddressFromBytes(bytes) } return } res, _ = ipaddr.NewMACAddressFromBytes(bytes) return } func (t *addresses) createMACAddressFromUint64(bytes uint64, extended bool) (res *ipaddr.MACAddress) { if t.caching { if extended { t.uint64MACExtAddressCacheLock.Lock() defer t.uint64MACExtAddressCacheLock.Unlock() res = t.uint64MACExtAddressCache[bytes] if res != nil { return } res = ipaddr.NewMACAddressFromUint64Ext(bytes, extended) t.uint64MACExtAddressCache[bytes] = res } else { t.uint64MACAddressCacheLock.Lock() defer t.uint64MACAddressCacheLock.Unlock() res = t.uint64MACAddressCache[bytes] if res != nil { return } res = ipaddr.NewMACAddressFromUint64Ext(bytes, extended) t.uint64MACAddressCache[bytes] = res } return } res = ipaddr.NewMACAddressFromUint64Ext(bytes, extended) return } func (t *addresses) createMACParamsAddress(str string, opts addrstrparam.MACAddressStringParams) (res *ipaddr.MACAddressString) { if t.caching { t.strParamsMACAddressStrCacheLock.Lock() defer t.strParamsMACAddressStrCacheLock.Unlock() mp := t.strParamsMACAddressStrCache[opts] if mp == nil { t.strParamsMACAddressStrCache[opts] = make(map[string]*ipaddr.MACAddressString) } else { res = mp[str] if res != nil { return } } } res = ipaddr.NewMACAddressStringParams(str, opts) if t.caching { t.strParamsMACAddressStrCache[opts][str] = res } return } func (t *addresses) createHost(str string) (res *ipaddr.HostName) { if t.caching { t.strHostStrCacheLock.Lock() defer t.strHostStrCacheLock.Unlock() res = t.strHostStrCache[str] if res != nil { //fmt.Printf("reusing %v\n", res) return } } res = ipaddr.NewHostNameParams(str, hostOptions) if t.caching { t.strHostStrCache[str] = res } return } func (t *addresses) createInetAtonHost(str string) (res *ipaddr.HostName) { if t.caching { t.inetAtonStrIHostStrCacheLock.Lock() defer t.inetAtonStrIHostStrCacheLock.Unlock() res = t.inetAtonStrHostStrCache[str] if res != nil { return } } res = ipaddr.NewHostNameParams(str, hostInetAtonOptions) if t.caching { t.inetAtonStrHostStrCache[str] = res } return } func (t *addresses) createParamsHost(str string, opts addrstrparam.HostNameParams) (res *ipaddr.HostName) { if t.caching { t.strParamsHostStrCacheLock.Lock() defer t.strParamsHostStrCacheLock.Unlock() mp := t.strParamsHostStrCache[opts] if mp == nil { t.strParamsHostStrCache[opts] = make(map[string]*ipaddr.HostName) } else { res = mp[str] if res != nil { return } } } res = ipaddr.NewHostNameParams(str, opts) if t.caching { t.strParamsHostStrCache[opts][str] = res } return } func (t *addresses) isLenient() bool { return false } func (t *addresses) allowsRange() bool { return false } type rangedAddresses struct { *addresses rstrIPAddressStrCache map[string]*ipaddr.IPAddressString rstrIPAddressStrCacheLock *sync.Mutex rstrMACAddressStrCache map[string]*ipaddr.MACAddressString rstrMACAddressStrCacheLock *sync.Mutex rstrHostStrCache map[string]*ipaddr.HostName rstrHostStrCacheLock *sync.Mutex rinetAtonStrHostStrCache map[string]*ipaddr.HostName rinetAtonStrIHostStrCacheLock *sync.Mutex } func (t *rangedAddresses) useCache(use bool) { if use { if t.caching { return } t.rstrIPAddressStrCache = make(map[string]*ipaddr.IPAddressString) t.rstrIPAddressStrCacheLock = &sync.Mutex{} t.rstrMACAddressStrCache = make(map[string]*ipaddr.MACAddressString) t.rstrMACAddressStrCacheLock = &sync.Mutex{} t.rstrHostStrCache = make(map[string]*ipaddr.HostName) t.rstrHostStrCacheLock = &sync.Mutex{} t.rinetAtonStrHostStrCache = make(map[string]*ipaddr.HostName) t.rinetAtonStrIHostStrCacheLock = &sync.Mutex{} } else { if !t.caching { return } *t = rangedAddresses{} } t.addresses.useCache(use) } var ( wildcardAndRangeAddressOptions = new(addrstrparam.IPAddressStringParamsBuilder).Set(addressOptions).AllowAll(true).SetRangeParams(addrstrparam.WildcardAndRange).ToParams() wildcardOnlyAddressOptions = new(addrstrparam.IPAddressStringParamsBuilder).Set(wildcardAndRangeAddressOptions).SetRangeParams(addrstrparam.WildcardOnly).ToParams() noRangeAddressOptions = new(addrstrparam.IPAddressStringParamsBuilder).Set(wildcardAndRangeAddressOptions).SetRangeParams(addrstrparam.NoRange).ToParams() wildcardAndRangeMACAddressOptions = new(addrstrparam.MACAddressStringParamsBuilder).Set(macAddressOptions).AllowAll(true).GetFormatParamsBuilder().SetRangeParams(addrstrparam.WildcardAndRange).GetParentBuilder().ToParams() hostInetAtonwildcardAndRangeOptions = new(addrstrparam.HostNameParamsBuilder). AllowEmpty(false). NormalizeToLowercase(true). AllowBracketedIPv6(true). AllowBracketedIPv4(true).GetIPAddressParamsBuilder(). AllowPrefix(true). AllowMask(true). SetRangeParams(addrstrparam.WildcardAndRange). Allow_inet_aton(true). AllowEmpty(false). AllowAll(true). GetIPv4AddressParamsBuilder(). AllowPrefixLenLeadingZeros(true). AllowPrefixesBeyondAddressSize(false). AllowWildcardedSeparator(true). GetParentBuilder().GetParentBuilder().ToParams() inetAtonwildcardAndRangeOptions = new(addrstrparam.IPAddressStringParamsBuilder).Set(hostInetAtonwildcardAndRangeOptions.GetIPAddressParams()).ToParams() hostWildcardOptions = new(addrstrparam.HostNameParamsBuilder).Set(hostOptions).GetIPAddressParamsBuilder(). AllowAll(true).SetRangeParams(addrstrparam.WildcardOnly).GetParentBuilder().ToParams() hostOnlyOptions = new(addrstrparam.HostNameParamsBuilder).Set(hostOptions).AllowIPAddress(false).ToParams() hostWildcardAndRangeOptions = new(addrstrparam.HostNameParamsBuilder).Set(hostWildcardOptions).GetIPAddressParamsBuilder().SetRangeParams(addrstrparam.WildcardAndRange).GetParentBuilder().ToParams() hostWildcardAndRangeInetAtonOptions = new(addrstrparam.HostNameParamsBuilder).Set(hostWildcardOptions).GetIPAddressParamsBuilder().SetRangeParams(addrstrparam.WildcardAndRange).Allow_inet_aton(true).GetParentBuilder().ToParams() ) func (t *rangedAddresses) getAllCached() (all []*ipaddr.IPAddress) { if !t.caching { return } others := t.addresses.getAllCached() t.rstrIPAddressStrCacheLock.Lock() all = make([]*ipaddr.IPAddress, 0, len(t.rstrIPAddressStrCache)+len(others)) for _, str := range t.rstrIPAddressStrCache { if addr := str.GetAddress(); addr != nil { all = append(all, addr) } } t.rstrIPAddressStrCacheLock.Unlock() all = append(all, others...) return } func (t *rangedAddresses) createAddress(str string) (res *ipaddr.IPAddressString) { if t.caching { t.rstrIPAddressStrCacheLock.Lock() defer t.rstrIPAddressStrCacheLock.Unlock() res = t.rstrIPAddressStrCache[str] if res != nil { return } } res = ipaddr.NewIPAddressStringParams(str, wildcardAndRangeAddressOptions) if t.caching { t.rstrIPAddressStrCache[str] = res } return } func (t *rangedAddresses) createMACAddress(str string) (res *ipaddr.MACAddressString) { if t.caching { t.rstrMACAddressStrCacheLock.Lock() defer t.rstrMACAddressStrCacheLock.Unlock() res = t.rstrMACAddressStrCache[str] if res != nil { return } } res = ipaddr.NewMACAddressStringParams(str, wildcardAndRangeMACAddressOptions) if t.caching { t.rstrMACAddressStrCache[str] = res } return } func (t *rangedAddresses) createHost(str string) (res *ipaddr.HostName) { if t.caching { t.rstrHostStrCacheLock.Lock() defer t.rstrHostStrCacheLock.Unlock() res = t.rstrHostStrCache[str] if res != nil { return } } res = ipaddr.NewHostNameParams(str, hostWildcardOptions) if t.caching { t.rstrHostStrCache[str] = res } return } func (t *rangedAddresses) createInetAtonHost(str string) (res *ipaddr.HostName) { if t.caching { t.rinetAtonStrIHostStrCacheLock.Lock() defer t.rinetAtonStrIHostStrCacheLock.Unlock() res = t.rinetAtonStrHostStrCache[str] if res != nil { return } } res = ipaddr.NewHostNameParams(str, hostInetAtonwildcardAndRangeOptions) if t.caching { t.rinetAtonStrHostStrCache[str] = res } return } func (t *rangedAddresses) allowsRange() bool { return true } var ( defaultOptions = new(addrstrparam.IPAddressStringParamsBuilder).ToParams() defaultHostOptions = new(addrstrparam.HostNameParamsBuilder).ToParams() ) type allAddresses struct { *rangedAddresses astrIPAddressStrCache map[string]*ipaddr.IPAddressString astrIPAddressStrCacheLock *sync.Mutex astrHostStrCache map[string]*ipaddr.HostName astrHostStrCacheLock *sync.Mutex ainetAtonStrHostStrCache map[string]*ipaddr.HostName ainetAtonStrIHostStrCacheLock *sync.Mutex } func (t *allAddresses) useCache(use bool) { if use { if t.caching { return } t.astrIPAddressStrCache = make(map[string]*ipaddr.IPAddressString) t.astrIPAddressStrCacheLock = &sync.Mutex{} t.astrHostStrCache = make(map[string]*ipaddr.HostName) t.astrHostStrCacheLock = &sync.Mutex{} t.ainetAtonStrHostStrCache = make(map[string]*ipaddr.HostName) t.ainetAtonStrIHostStrCacheLock = &sync.Mutex{} } else { if !t.caching { return } *t = allAddresses{} } t.rangedAddresses.useCache(use) } func (t *allAddresses) getAllCached() (all []*ipaddr.IPAddress) { if !t.caching { return } others := t.rangedAddresses.getAllCached() t.astrIPAddressStrCacheLock.Lock() all = make([]*ipaddr.IPAddress, 0, len(t.astrIPAddressStrCache)+len(others)) for _, str := range t.astrIPAddressStrCache { if addr := str.GetAddress(); addr != nil { all = append(all, addr) } } t.astrIPAddressStrCacheLock.Unlock() all = append(all, others...) return } func (t *allAddresses) createAddress(str string) (res *ipaddr.IPAddressString) { if t.caching { t.astrIPAddressStrCacheLock.Lock() defer t.astrIPAddressStrCacheLock.Unlock() res = t.astrIPAddressStrCache[str] if res != nil { return } } res = ipaddr.NewIPAddressStringParams(str, defaultOptions) if t.caching { t.astrIPAddressStrCache[str] = res } return } func (t *allAddresses) createInetAtonAddress(str string) *ipaddr.IPAddressString { return t.createAddress(str) } func (t *allAddresses) createHost(str string) (res *ipaddr.HostName) { if t.caching { t.astrHostStrCacheLock.Lock() defer t.astrHostStrCacheLock.Unlock() res = t.astrHostStrCache[str] if res != nil { return } } res = ipaddr.NewHostNameParams(str, defaultHostOptions) if t.caching { t.astrHostStrCache[str] = res } return } func (t *allAddresses) createInetAtonHost(str string) (res *ipaddr.HostName) { if t.caching { t.ainetAtonStrIHostStrCacheLock.Lock() defer t.ainetAtonStrIHostStrCacheLock.Unlock() res = t.ainetAtonStrHostStrCache[str] if res != nil { return } } res = ipaddr.NewHostNameParams(str, defaultHostOptions) if t.caching { t.ainetAtonStrHostStrCache[str] = res } return } func (t *allAddresses) isLenient() bool { return true } var _, _, _ testAddresses = &addresses{}, &rangedAddresses{}, &allAddresses{} ipaddress-go-1.5.4/ipaddr/test/addressordertest.go000066400000000000000000001041511440250641600222400ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package test import ( "fmt" "math/rand" "sort" "time" "github.com/seancfoley/ipaddress-go/ipaddr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) type OrderingSupplier func(string, int) *Ordering type OrderingComparator func(one, two *Ordering) int var ( orderingOpts = new(addrstrparam.IPAddressStringParamsBuilder).AllowAll(true).SetRangeParams(addrstrparam.WildcardAndRange).ParseEmptyStrAs(addrstrparam.NoAddressOption).GetIPv6AddressParamsBuilder().AllowZone(false).GetParentBuilder().ToParams() macOrderingOpts = new(addrstrparam.MACAddressStringParamsBuilder).AllowAll(true).AllowEmpty(true).SetRangeParams(addrstrparam.WildcardAndRange).ToParams() ) type addressOrderTest struct { testBase } func (t addressOrderTest) run() { t.testOrder() } func (t addressOrderTest) testOrder() { ipAddressOrderingSupplier := func(str string, i int) *Ordering { addr := t.createParamsAddress(str, orderingOpts).GetAddress() if addr != nil { return &Ordering{ nestedType: addr.ToAddressBase(), order: i, } } return nil } macAddressOrderingSupplier := func(str string, i int) *Ordering { addr := t.createMACParamsAddress(str, macOrderingOpts).GetAddress() if addr != nil { return &Ordering{ nestedType: addr.ToAddressBase(), order: i, } } return nil } ipaddressStringOrderingSupplier := func(str string, i int) *Ordering { return &Ordering{ nestedIPAddrString: ipaddr.NewIPAddressStringParams(str, orderingOpts), order: i, } } macaddressStringOrderingSupplier := func(str string, i int) *Ordering { return &Ordering{ nestedMACAddrString: ipaddr.NewMACAddressStringParams(str, macOrderingOpts), order: i, } } nilOrderingSupplier := func(str string, i int) *Ordering { return nil } t.testDefaultOrder(ipaddressStringOrderingSupplier, nilOrderingSupplier) //cannot remember if there is a reason why we do this one twice lowValComparator := ipaddr.LowValueComparator ipAddressLowValComparator := func(one, two *Ordering) int { result := lowValComparator.Compare(one.nestedType, two.nestedType) return result } ipAddressStringLowValComparator := func(one, two *Ordering) int { var result int oneAddr := one.nestedIPAddrString.GetAddress() twoAddr := two.nestedIPAddrString.GetAddress() if oneAddr != nil && twoAddr != nil { result = lowValComparator.Compare(oneAddr, twoAddr) } else { result = one.nestedIPAddrString.Compare(two.nestedIPAddrString) } return result } macAddressStringLowValComparator := func(one, two *Ordering) int { var result int oneAddr := one.nestedMACAddrString.GetAddress() twoAddr := two.nestedMACAddrString.GetAddress() if oneAddr != nil && twoAddr != nil { result = lowValComparator.Compare(oneAddr, twoAddr) } else { result = one.nestedMACAddrString.Compare(two.nestedMACAddrString) } return result } t.testLowValueOrder(ipAddressStringLowValComparator, ipaddressStringOrderingSupplier, nilOrderingSupplier) t.testLowValueOrder(macAddressStringLowValComparator, nilOrderingSupplier, macaddressStringOrderingSupplier) t.testLowValueOrder(ipAddressLowValComparator, ipAddressOrderingSupplier, macAddressOrderingSupplier) highValComparator := ipaddr.HighValueComparator ipAddressHighValComparator := func(one, two *Ordering) int { result := highValComparator.Compare(one.nestedType, two.nestedType) return result } ipAddressStringHighValComparator := func(one, two *Ordering) int { var result int oneAddr := one.nestedIPAddrString.GetAddress() twoAddr := two.nestedIPAddrString.GetAddress() if oneAddr != nil && twoAddr != nil { result = highValComparator.Compare(oneAddr, twoAddr) } else { result = one.nestedIPAddrString.Compare(two.nestedIPAddrString) } return result } macAddressStringHighValComparator := func(one, two *Ordering) int { var result int oneAddr := one.nestedMACAddrString.GetAddress() twoAddr := two.nestedMACAddrString.GetAddress() if oneAddr != nil && twoAddr != nil { result = highValComparator.Compare(oneAddr, twoAddr) } else { result = one.nestedMACAddrString.Compare(two.nestedMACAddrString) } return result } t.testHighValueOrder(ipAddressStringHighValComparator, ipaddressStringOrderingSupplier, nilOrderingSupplier) t.testHighValueOrder(macAddressStringHighValComparator, nilOrderingSupplier, macaddressStringOrderingSupplier) t.testHighValueOrder(ipAddressHighValComparator, ipAddressOrderingSupplier, macAddressOrderingSupplier) t.testDefaultOrder(ipaddressStringOrderingSupplier, nilOrderingSupplier) t.testDefaultOrder(nilOrderingSupplier, macaddressStringOrderingSupplier) t.testDefaultOrder(ipAddressOrderingSupplier, macAddressOrderingSupplier) } type Ordering struct { nestedType *ipaddr.Address nestedIPAddrString *ipaddr.IPAddressString nestedMACAddrString *ipaddr.MACAddressString order int } func (o *Ordering) getDescription() string { if o.nestedIPAddrString != nil { return fmt.Sprintf("(expected index %d) %v", o.order, o.nestedIPAddrString) } else if o.nestedMACAddrString != nil { return fmt.Sprintf("(expected index %d) %v", o.order, o.nestedMACAddrString) } return fmt.Sprintf("(expected index %d) %v", o.order, o.nestedType) } func (o *Ordering) CompareTo(other *Ordering) int { var result int if o.nestedIPAddrString != nil { result = o.nestedIPAddrString.Compare(other.nestedIPAddrString) } else if o.nestedMACAddrString != nil { result = o.nestedMACAddrString.Compare(other.nestedMACAddrString) } else { result = o.nestedType.Compare(other.nestedType) } return result } // The default order goes by count first, and then the count of the more significant segment followed by the lower value magnitude in the same segment. func (t addressOrderTest) testDefaultOrder(ipAddressSupplier, macAddressSupplier OrderingSupplier) { var ordering []*Ordering //invalid strs := []string{ //these are already sorted by natural string ordering "/129", //invalid prefix "bla", "fo", "foo", "four", "xxx", } //order is INVALID, EMPTY, IPV4, IPV6, ALL orderNumber := 0 for _, s := range strs { ordering = append(ordering, ipAddressSupplier(s, orderNumber)) ordering = append(ordering, macAddressSupplier(s, orderNumber)) orderNumber++ } //empty ordering = append(ordering, macAddressSupplier("", orderNumber)) ordering = append(ordering, macAddressSupplier(" ", orderNumber)) ordering = append(ordering, macAddressSupplier(" ", orderNumber)) ordering = append(ordering, macAddressSupplier("", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:0:0:0", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:2:3:4", orderNumber)) ordering = append(ordering, macAddressSupplier("1:00:00:2:03:4", orderNumber)) ordering = append(ordering, macAddressSupplier("1:0:0:2:3:4", orderNumber)) ordering = append(ordering, macAddressSupplier("01:00:00:02:03:04", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:0:0:fe:ff:fe", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:0:0:fe:ff:ff", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:0:0:ff:ff:fe", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:0:0:ff:ff:ff", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:ff:ff:ff:ff:ff", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:2:3:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("a1:f0:2:3:4:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:2:3-4:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:2:1-3:4:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:f0-ff:2:3:4:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:*:2:03:4:*", orderNumber)) ordering = append(ordering, macAddressSupplier("01:*:02:03:04:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0-7f:2:3:*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0-ff:*:*:*:8", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("*:0:0:*:*:*", orderNumber)) ordering = append(ordering, macAddressSupplier("*:0:0:*:%*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:*:*:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("*:*:a:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("0-1:*:*:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("*:*:*:*:*:*", orderNumber)) ordering = append(ordering, macAddressSupplier("*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("0:0:0:0:0:0:0:1", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:0:0:0:0:0", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:ff:ff:ff:ff:ff:ff:ff", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("*:*:*:*:*:*:*:*", orderNumber)) orderNumber++ //empty ordering = append(ordering, ipAddressSupplier("", orderNumber)) ordering = append(ordering, ipAddressSupplier(" ", orderNumber)) ordering = append(ordering, ipAddressSupplier(" ", orderNumber)) ordering = append(ordering, ipAddressSupplier("", orderNumber)) orderNumber++ //a bunch of address and prefixes ordering = append(ordering, ipAddressSupplier("1.0.0.0", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.002.3.4", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.2.003.4", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.2.3.4", orderNumber)) ordering = append(ordering, ipAddressSupplier("001.002.003.004", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("255.254.255.254", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("255.254.255.255", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("255.255.255.254", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("255.255.255.255", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.002.3.*", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.002.3.*/31", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.002.*.*/17", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.002.*.*/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.002.0.0/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("001.002.000.000/16", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.2.000.0/15", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.2.0.0/15", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*.*.1-3.*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*.*.*.*", orderNumber)) ordering = append(ordering, ipAddressSupplier("*.*.%*.*", orderNumber)) orderNumber++ // ipv6 ordering = append(ordering, ipAddressSupplier("1::", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::2:3:4", orderNumber)) ordering = append(ordering, ipAddressSupplier("1::2:003:4", orderNumber)) ordering = append(ordering, ipAddressSupplier("1::2:3:4", orderNumber)) ordering = append(ordering, ipAddressSupplier("0001:0000::0002:0003:0004", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff::fffe:ffff:fffe", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff::fffe:ffff:ffff", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff::ffff:ffff:fffe", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff::ffff:ffff:ffff", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::2:3:*/127", orderNumber)) ordering = append(ordering, ipAddressSupplier("1::2:3:*", orderNumber)) ordering = append(ordering, ipAddressSupplier("1::2:3:*/111", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::2:1-3:4:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*::*:*:*", orderNumber)) ordering = append(ordering, ipAddressSupplier("*::*:%*:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1:0:*/31", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*::*:*:*:*:*/16", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1:8000::/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("a1:8000::/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("0001::/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("1:*/16", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*:*:a:*:*:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("2::/15", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*:*:*:*:*:*:*:*/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("*:*", orderNumber)) ordering = append(ordering, ipAddressSupplier("*:*:*:*:*:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*", orderNumber)) ordering = append(ordering, ipAddressSupplier("**", orderNumber)) ordering = append(ordering, ipAddressSupplier(" *", orderNumber)) ordering = append(ordering, ipAddressSupplier("%%", orderNumber)) orderNumber++ t.checkOrdering(ordering, orderNumber, nil) } func (t addressOrderTest) testHighValueOrder(comparator OrderingComparator, ipAddressSupplier, macAddressSupplier OrderingSupplier) { var ordering []*Ordering //order is INVALID, EMPTY, IPV4, IPV6, ALL orderNumber := 0 //invalid strs := []string{ //these are already sorted by natural string ordering "/129", //invalid prefix "bla", "fo", "foo", "four", "xxx", } for _, s := range strs { ordering = append(ordering, ipAddressSupplier(s, orderNumber)) ordering = append(ordering, macAddressSupplier(s, orderNumber)) orderNumber++ } //empty ordering = append(ordering, macAddressSupplier("", orderNumber)) ordering = append(ordering, macAddressSupplier(" ", orderNumber)) ordering = append(ordering, macAddressSupplier(" ", orderNumber)) ordering = append(ordering, macAddressSupplier("", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:0:0:0", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:2:3:4", orderNumber)) ordering = append(ordering, macAddressSupplier("1:00:00:2:03:4", orderNumber)) ordering = append(ordering, macAddressSupplier("1:0:0:2:3:4", orderNumber)) ordering = append(ordering, macAddressSupplier("01:00:00:02:03:04", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:2:3:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:2:3-4:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:2:1-3:4:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0-7f:2:3:*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:*:2:03:4:*", orderNumber)) ordering = append(ordering, macAddressSupplier("01:*:02:03:04:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:f0-ff:2:3:4:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0-ff:*:*:*:8", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("0-1:*:*:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("a1:f0:2:3:4:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:0:0:fe:ff:fe", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:0:0:fe:ff:ff", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:0:0:ff:ff:fe", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("*:0:0:*:*:*", orderNumber)) ordering = append(ordering, macAddressSupplier("*:0:0:*:%*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:0:0:ff:ff:ff", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("*:*:a:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("*:*:*:*:*:*", orderNumber)) ordering = append(ordering, macAddressSupplier("*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:ff:ff:ff:ff:ff", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("0:0:0:0:0:0:0:1", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:0:0:0:0:0", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("*:*:*:*:*:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:ff:ff:ff:ff:ff:ff:ff", orderNumber)) orderNumber++ //empty ordering = append(ordering, ipAddressSupplier("", orderNumber)) ordering = append(ordering, ipAddressSupplier(" ", orderNumber)) ordering = append(ordering, ipAddressSupplier(" ", orderNumber)) ordering = append(ordering, ipAddressSupplier("", orderNumber)) orderNumber++ //a bunch of address ordering = append(ordering, ipAddressSupplier("1.0.0.0", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.002.0.*/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.002.3.4", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.2.003.4", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.2.3.4", orderNumber)) ordering = append(ordering, ipAddressSupplier("001.002.003.004", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.002.3.*", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.002.3.*/31", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.002.0.0/17", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.002.0-127.*/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.002.0.0/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("001.002.000.000/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.002.*.*/16", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.2.000.0/15", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.2-3.*.*/15", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("255.254.255.254", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("255.254.255.255", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*.*.1-3.*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("255.255.255.254", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*.*.*.*", orderNumber)) ordering = append(ordering, ipAddressSupplier("*.*.%*.*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("255.255.255.255", orderNumber)) orderNumber++ // IPv6 ordering = append(ordering, ipAddressSupplier("1::", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::*/31", orderNumber)) ordering = append(ordering, ipAddressSupplier("1::*/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::2:2:*/111", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::2:3:4", orderNumber)) ordering = append(ordering, ipAddressSupplier("1::2:003:4", orderNumber)) ordering = append(ordering, ipAddressSupplier("1::2:3:4", orderNumber)) ordering = append(ordering, ipAddressSupplier("0001:0000::0002:0003:0004", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::2:2-3:*/111", orderNumber)) ordering = append(ordering, ipAddressSupplier("1::2:2:0/111", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::2:3:*/127", orderNumber)) ordering = append(ordering, ipAddressSupplier("1::2:3:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::2:1-3:4:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1:0-1:*/31", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::/17", orderNumber)) ordering = append(ordering, ipAddressSupplier("1:0-7fff:*:*/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("0001:0000::0000:0000:0000/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("1:*/17", orderNumber)) ordering = append(ordering, ipAddressSupplier("1:*/16", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1:8000::/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("2:*/15", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("2::/15", orderNumber)) ordering = append(ordering, ipAddressSupplier("2-3:*/15", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("a1:8000::/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff::fffe:ffff:fffe", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff::fffe:ffff:ffff", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff::ffff:ffff:fffe", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*::*:*:*", orderNumber)) ordering = append(ordering, ipAddressSupplier("*::*:%*:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff::ffff:ffff:ffff", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*::*:*:*:*:*/16", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*:*:a:*:*:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*:*:*:*:*:*:*:*/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("*:*", orderNumber)) ordering = append(ordering, ipAddressSupplier("*:*:*:*:*:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*", orderNumber)) ordering = append(ordering, ipAddressSupplier("**", orderNumber)) ordering = append(ordering, ipAddressSupplier(" *", orderNumber)) ordering = append(ordering, ipAddressSupplier("%%", orderNumber)) orderNumber++ t.checkOrdering(ordering, orderNumber, comparator) } func (t addressOrderTest) testLowValueOrder(comparator OrderingComparator, ipAddressSupplier, macAddressSupplier OrderingSupplier) { var ordering []*Ordering //order is INVALID, EMPTY, IPV4, IPV6, ALL orderNumber := 0 //invalid strs := []string{ //these are already sorted by natural string ordering "/129", //invalid prefix "bla", "fo", "foo", "four", "xxx", } for _, s := range strs { ordering = append(ordering, ipAddressSupplier(s, orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier(s, orderNumber)) orderNumber++ } //empty ordering = append(ordering, macAddressSupplier("", orderNumber)) ordering = append(ordering, macAddressSupplier(" ", orderNumber)) ordering = append(ordering, macAddressSupplier(" ", orderNumber)) ordering = append(ordering, macAddressSupplier("", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("0-1:*:*:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("*:0:0:*:*:*", orderNumber)) ordering = append(ordering, macAddressSupplier("*:0:0:*:%*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("*:*:*:*:*:*", orderNumber)) ordering = append(ordering, macAddressSupplier("*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("*:*:a:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:0:0:0", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0-ff:*:*:*:8", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:2:3:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:2:3-4:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:2:3:4", orderNumber)) ordering = append(ordering, macAddressSupplier("1:00:00:2:03:4", orderNumber)) ordering = append(ordering, macAddressSupplier("1:0:0:2:3:4", orderNumber)) ordering = append(ordering, macAddressSupplier("01:00:00:02:03:04", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:2:1-3:4:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0-7f:2:3:*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:*:2:03:4:*", orderNumber)) ordering = append(ordering, macAddressSupplier("01:*:02:03:04:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:f0-ff:2:3:4:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("a1:f0:2:3:4:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:0:0:fe:ff:fe", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:0:0:fe:ff:ff", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:0:0:ff:ff:fe", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:0:0:ff:ff:ff", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:ff:ff:ff:ff:ff", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("*:*:*:*:*:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("0:0:0:0:0:0:0:1", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("1:0:0:0:0:0:0:0", orderNumber)) orderNumber++ ordering = append(ordering, macAddressSupplier("ff:ff:ff:ff:ff:ff:ff:ff", orderNumber)) orderNumber++ //empty ordering = append(ordering, ipAddressSupplier("", orderNumber)) ordering = append(ordering, ipAddressSupplier(" ", orderNumber)) ordering = append(ordering, ipAddressSupplier(" ", orderNumber)) ordering = append(ordering, ipAddressSupplier("", orderNumber)) orderNumber++ //a bunch of address ordering = append(ordering, ipAddressSupplier("*.*.*.*", orderNumber)) ordering = append(ordering, ipAddressSupplier("*.*.%*.*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*.*.1-3.*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.0.0.0", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.000.0.*/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.002.0.0/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("001.002.000.000/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.002.*.*/16", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.2.000.0/15", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.2-3.*.*/15", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.002.3.*", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.002.3.*/31", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1.002.3.4", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.2.003.4", orderNumber)) ordering = append(ordering, ipAddressSupplier("1.2.3.4", orderNumber)) ordering = append(ordering, ipAddressSupplier("001.002.003.004", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("255.254.255.254", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("255.254.255.255", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("255.255.255.254", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("255.255.255.255", orderNumber)) orderNumber++ // IPv6 ordering = append(ordering, ipAddressSupplier("*::*:*:*", orderNumber)) ordering = append(ordering, ipAddressSupplier("*::*:%*:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*::*:*:*:*:*/16", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*:*:*:*:*:*:*:*/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("*:*", orderNumber)) ordering = append(ordering, ipAddressSupplier("*:*:*:*:*:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*:*:a:*:*:*:*:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1:0::*/16", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1:0:*/16", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::/31", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::/17", orderNumber)) ordering = append(ordering, ipAddressSupplier("1:0::/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("0001:0000::0000:0000:0000/16", orderNumber)) ordering = append(ordering, ipAddressSupplier("1:*/17", orderNumber)) ordering = append(ordering, ipAddressSupplier("1:*/16", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::2:2:*/111", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::2:3:*/127", orderNumber)) ordering = append(ordering, ipAddressSupplier("1::2:3:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::2:3:4", orderNumber)) ordering = append(ordering, ipAddressSupplier("1::2:003:4", orderNumber)) ordering = append(ordering, ipAddressSupplier("1::2:3:4", orderNumber)) ordering = append(ordering, ipAddressSupplier("0001:0000::0002:0003:0004", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1::2:1-3:4:*", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("1:8000::/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("2::0:*/15", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("2:0:*/15", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("2:*/15", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("2::/15", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("a1:8000::/17", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff::fffe:ffff:fffe", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff::fffe:ffff:ffff", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff::ffff:ffff:fffe", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff::ffff:ffff:ffff", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff:ffff:ffff::", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("ffff:ffff:ffff:ffff::1", orderNumber)) orderNumber++ ordering = append(ordering, ipAddressSupplier("*", orderNumber)) ordering = append(ordering, ipAddressSupplier("**", orderNumber)) ordering = append(ordering, ipAddressSupplier(" *", orderNumber)) ordering = append(ordering, ipAddressSupplier("%%", orderNumber)) orderNumber++ t.checkOrdering(ordering, orderNumber, comparator) } func (t addressOrderTest) checkOrdering(ordering []*Ordering, orderCount int, comparator OrderingComparator) { length := len(ordering) for i := 0; i < length; i++ { val := ordering[i] if val == nil { j := length - 1 for j > i { if ordering[j] != nil { ordering[i] = ordering[j] ordering[j] = nil break } j-- } length = j } } ordering = ordering[:length] rand.Seed(time.Now().UnixNano()) rand.Shuffle(len(ordering), func(i, j int) { ordering[i], ordering[j] = ordering[j], ordering[i] }) if comparator != nil { sort.Slice(ordering, func(i, j int) bool { return comparator(ordering[i], ordering[j]) < 0 }) } else { sort.Slice(ordering, func(i, j int) bool { return ordering[i].CompareTo(ordering[j]) < 0 }) } failedOrdering := false lastOrder := -1 for i := 0; i < len(ordering); i++ { orderingItem := ordering[i] order := orderingItem.order if order < lastOrder { failedOrdering = true failureStr := fmt.Sprintf("item %v: %v is in wrong place in ordering ( order number: %v, previous order number: %v)", i+1, orderingItem.nestedType, order, lastOrder) t.addFailure(newFailure(failureStr, nil)) } lastOrder = order } if failedOrdering { sorted := make([]string, 0, len(ordering)) previousOrder, lastIndex := -1, -1 for i := 0; i < len(ordering); i++ { o := ordering[i] currentOrder := o.order var index int if currentOrder == previousOrder { index = lastIndex } else { index = i + 1 } sorted = append(sorted, fmt.Sprintf("\n(sorted index %v) %v", index, o.getDescription())) previousOrder = currentOrder lastIndex = index } t.addFailure(newFailure(fmt.Sprintf("ordering failed: %v", sorted), nil)) } t.incrementTestCount() } ipaddress-go-1.5.4/ipaddr/test/hostalltest.go000066400000000000000000000013211440250641600212200ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package test type hostAllTester struct { hostRangeTester } func (t hostAllTester) run() { // nothing to test here } ipaddress-go-1.5.4/ipaddr/test/hostrangetest.go000066400000000000000000000240451440250641600215540ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package test import ( "fmt" "github.com/seancfoley/ipaddress-go/ipaddr" ) type hostRangeTester struct { hostTester } func (t hostRangeTester) run() { t.testMatches(true, "1.*.*.*/255.0.0.0", "1.0.0.0/255.0.0.0") t.testMatches(true, "1.0.0.0/8", "1.0.0.0/255.0.0.0") t.testMatches(true, "1.2.3.4/255.0.0.0", "1.2.3.4") t.testMatches(true, "1.2.3.4/255.0.0.0", "1.2.3.4") t.testMatches(true, "1.2.3.4/255.0.0.0", "1.2.3.4") t.testMatches(true, "1.0.0.0/255.0.0.0", "1.*.*.*") t.testMatches(true, "1.0.0.0/255.0.0.0", "1.*.___.*") t.testMatches(false, "1.0.0.0/255.0.0.0", "1.0-255.*.*") //failing due to the options t.testMatchesParams(true, "1.0.0.0/255.0.0.0", "1.0-255.*.*", hostWildcardAndRangeOptions) t.testMatchesParams(true, "1-2.0-0.00-00.00-0", "1-2.0.0.0", hostWildcardAndRangeOptions) t.testMatchesParams(true, "1-2:0-0:00-00:00-0:0-000:0000-0000:0000-00:0000-0", "1-2:0:0:0:0:0:0:0", hostWildcardAndRangeOptions) t.testMatchesParams(true, "00-0.0-0.00-00.00-0", "0.0.0.0", hostWildcardAndRangeOptions) t.testMatchesParams(true, "0-00:0-0:00-00:00-0:0-000:0000-0000:0000-00:0000-0", "::", hostWildcardAndRangeOptions) t.testResolved("a::b:*:d:1.2.*%x", "a::b:*:d:1.2.*%x") t.testResolved("[a::b:*:d:1.2.*%x]", "a::b:*:d:1.2.*%x") t.testResolved("[a::*:c:d:1.*.3.4]", "a::*:c:d:1.*.3.4") t.testResolved("2001:0000:1234:0000:*:C1C0:ABCD:0876%x", "2001:0:1234:0:*:c1c0:abcd:876%x") t.testResolved("[2001:*:1234:0000:0000:C1C0:ABCD:0876%x]", "2001:*:1234::C1C0:abcd:876%x") t.testResolved("[2001:0000:*:0000:0000:C1C0:ABCD:0876]", "2001:0:*::C1C0:abcd:876") t.testResolved("2001:0000:*:0000:0000:C1C0:ABCD:0876", "2001:0:*::C1C0:abcd:876") t.testResolved("1.2.*.04", "1.2.*.4") t.testResolved_inet_aton("1.*.0-255.3", "1.*.*.3") t.testResolved_inet_aton("1.*.3", "1.*.0.3") t.testResolved("[1.2.*.4]", "1.2.*.4") t.testResolved("espn.*.com", "") //no wildcards for hosts, just addresses t.testResolved("*.instapundit.com", "") t.testResolved("es*n.com", "") t.testResolved("inst*undit.com", "") if t.fullTest && runDNS { t.testResolved("espn.com/24", "199.181.132.*") } t.testResolved("3*", "") t.testResolved("*", "*") t.testResolved("3.*", "3.*.*.*") t.testResolved("3:*", "3:*:*:*:*:*:*:*") t.testResolved("9.*.237.26", "9.*.237.26") t.testResolved("*.70.146.*", "*.70.146.*") t.hostLabelsTest("*", []string{"*"}) t.hostLabelsTest("**", []string{"*"}) t.hostTest(true, "1.2.3.4/1.2.3.4") t.hostTest(false, "1.2.3.4/*") t.hostTest(false, "1.*.3.4/*") t.hostTest(true, "1.*.3.4") t.hostTest(true, "1:*:3:4") t.testMasked("1.*.3.4", "", nil, "1.*.3.4") t.testMasked("1.*.3.4/255.255.1.0", "255.255.1.0", nil, "1.*.1.0") t.testMasked("1.*.3.4/255.255.254.0", "255.255.254.0", p23, "1.*.3.4/23") t.testMasked("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "", nil, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") t.testMasked("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0:101:0:101:0:101:0:101", "0:101:0:101:0:101:0:101", nil, "0:101:0:101:0:101:0:101") t.testMasked("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/ffff:ffff:8000::", "ffff:ffff:8000::", p33, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/33") t.testMasked("ffff:ffff::/ffff:ffff:8000::", "ffff:ffff:8000::", p33, "ffff:ffff::/33") t.testMasked("bla.com/ffff:ffff:8000::", "ffff:ffff:8000::", p33, "") t.testMasked("bla.com", "", nil, "") t.testHostOrWildcardAddress("1_.2.3.4", 4, "1_.2.3.4", "10-19.2.3.4") t.testHostOrRangeAddress("1-2.2.3.4", 4, "1-2.2.3.4", "1-2.2.3.4") t.testHostOrAddress_inet_aton("1-9.1-2", 2, 4, "1-9.1-2", "1-9.0.0.1-2") t.testHostOrAddress_inet_aton("1-9.0x1-0x22", 2, 4, "1-9.0x1-0x22", "1-9.0.0.1-34") t.testHostOrAddress_inet_aton("1-9.0x1-0x22", 2, 4, "1-9.0x1-0x22", "1-9.0.0.1-34") t.testHostOrAddress_inet_aton("9-1.0X1-0x22", 2, 4, "9-1.0x1-0x22", "1-9.0.0.1-34") t.testHostOrAddress_inet_aton("9-1.0X1-0X22", 2, 4, "9-1.0x1-0x22", "1-9.0.0.1-34") t.testHostOnly("9-1g.0x1-0x22", 2, "9-1g.0x1-0x22", "") t.testHostOrAddress_inet_aton("1-9.0x1-0x22.03.04", 4, 4, "1-9.0x1-0x22.03.04", "1-9.1-34.3.4") t.testAddress("1::2", 8, "[1:0:0:0:0:0:0:2]", "1:0:0:0:0:0:0:2") t.testAddress("1.2.3.4", 4, "1.2.3.4", "1.2.3.4") t.hostTester.run() } func (t hostRangeTester) testMatches(matches bool, host1, host2 string) { t.testMatchesParams(matches, host1, host2, hostWildcardOptions) } func (t hostRangeTester) testHostAndAddress(h *ipaddr.HostName, hostLabelCount, addressLabelCount int, isValidHost, isValidAddress bool, normalizedHostString, normalizedAddressString string) { if h.IsValid() != isValidHost { t.addFailure(newHostFailure("unexpected invalid host", h)) } else { var expectedLen int if isValidAddress { expectedLen = addressLabelCount } else if isValidHost { expectedLen = hostLabelCount } else { expectedLen = 1 } if len(h.GetNormalizedLabels()) != expectedLen { t.addFailure(newHostFailure(fmt.Sprintf("labels length is %v expected %v", len(h.GetNormalizedLabels()), expectedLen), h)) } else { addr := h.AsAddress() if isValidAddress != h.IsAddress() { t.addFailure(newHostFailure("not address "+addr.String(), h)) } else if isValidAddress != (addr != nil) { t.addFailure(newHostFailure("addr is "+addr.String(), h)) } else if isValidAddress && addr.ToNormalizedString() != normalizedAddressString { t.addFailure(newHostFailure("addr string is "+addr.ToNormalizedString()+" expected "+normalizedAddressString, h)) } else { nhString := h.ToNormalizedString() var expected string if h.IsAddress() && addr.IsIPv6() { if isValidHost { expected = normalizedHostString } else { expected = h.String() } } else { if isValidAddress { expected = normalizedAddressString } else if isValidHost { expected = normalizedHostString } else { expected = h.String() } } if nhString != expected { t.addFailure(newHostFailure("host string is "+nhString+" expected "+expected, h)) } } } } t.incrementTestCount() } func (t hostRangeTester) testHostOrAddress_inet_aton(x string, hostLabelCount, addressLabelCount int, normalizedHostString, normalizedAddressString string) { t.testHostAndAddressAll(x, hostLabelCount, addressLabelCount, true, false, false, true, normalizedHostString, normalizedAddressString) } func (t hostRangeTester) testHostOrRangeAddress(x string, labelCount int, normalizedHostString, normalizedAddressString string) { t.testHostAndAddressAll(x, labelCount, labelCount, true, false, true, true, normalizedHostString, normalizedAddressString) } func (t hostRangeTester) testHostOrWildcardAddress(x string, labelCount int, normalizedHostString, normalizedAddressString string) { t.testHostAndAddressAll(x, labelCount, labelCount, true, true, true, true, normalizedHostString, normalizedAddressString) } func (t hostRangeTester) testAddress(x string, labelCount int, normalizedHostString, normalizedAddressString string) { t.testHostAndAddressAll(x, labelCount, labelCount, false, true, true, true, normalizedHostString, normalizedAddressString) } func (t hostRangeTester) testHostOnly(x string, labelCount int, normalizedHostString, normalizedAddressString string) { t.testHostAndAddressAll(x, labelCount, labelCount, true, false, false, false, normalizedHostString, normalizedAddressString) } func (t hostRangeTester) testHostAndAddressAll(x string, hostLabelCount, addressLabelCount int, isHostName, isAddressNotRanged, isRangeAddress, is_inet_aton_RangeAddress bool, normalizedHostString, normalizedAddressString string) { //we want to handle 4 cases //1. a.b.com host only //2. 1:: address //3. a-b.c__ either way inet_aton //4. a-b.c__.3.4 either way h := t.createParamsHost(x, hostOnlyOptions) t.testHostAndAddress(h, hostLabelCount, addressLabelCount, isHostName, false, normalizedHostString, normalizedAddressString) isAddress := isAddressNotRanged h = t.createParamsHost(x, hostWildcardOptions) t.testHostAndAddress(h, hostLabelCount, addressLabelCount, isHostName || isAddress, isAddress, normalizedHostString, normalizedAddressString) isAddress = isAddressNotRanged || isRangeAddress h = t.createParamsHost(x, hostWildcardAndRangeOptions) t.testHostAndAddress(h, hostLabelCount, addressLabelCount, isHostName || isAddress, isAddress, normalizedHostString, normalizedAddressString) isAddress = isAddressNotRanged || isRangeAddress || is_inet_aton_RangeAddress h = t.createParamsHost(x, hostWildcardAndRangeInetAtonOptions) t.testHostAndAddress(h, hostLabelCount, addressLabelCount, isHostName || isAddress, isAddress, normalizedHostString, normalizedAddressString) } func (t hostRangeTester) testMasked(masked, mask string, prefixLength ipaddr.PrefixLen, result string) { maskedHostStr := t.createHost(masked) var maskAddr *ipaddr.IPAddress if mask != "" { maskAddr = t.createAddress(mask).GetAddress() } if result != "" { resultAddr := t.createAddress(result).GetAddress() maskedAddr := maskedHostStr.GetAddress() if !maskedAddr.Equal(resultAddr) { t.addFailure(newIPAddrFailure("masked "+maskedAddr.String()+" instead of expected "+resultAddr.String(), maskedAddr)) } } if !addressesEqual(maskAddr, maskedHostStr.GetMask()) { //if !maskAddr.Equal(maskedHostStr.GetMask()) { t.addFailure(newHostFailure("masked "+maskAddr.String()+" instead of expected "+maskedHostStr.GetMask().String(), maskedHostStr)) } if !maskedHostStr.GetNetworkPrefixLen().Equal(prefixLength) { t.addFailure(newHostFailure("masked prefix length was "+maskedHostStr.GetNetworkPrefixLen().String()+" instead of expected "+prefixLength.String(), maskedHostStr)) } t.incrementTestCount() } ipaddress-go-1.5.4/ipaddr/test/hosttest.go000066400000000000000000001324731440250641600205440ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package test import ( "fmt" "net" "strconv" "strings" "sync/atomic" "github.com/seancfoley/ipaddress-go/ipaddr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) var runDNS = false type hostTester struct { testBase } func (t hostTester) run() { t.testSelf("1.2.3.4", false) t.testSelf("1::", false) t.testSelf("[1::]", false) t.testSelf("bla.com", false) t.testSelf("::1", true) t.testSelf("[::1]", true) t.testSelf("localhost", true) t.testSelf("127.0.0.1", true) t.testSelf("[127.0.0.1]", true) t.testSelf("[localhost]", false) //square brackets are for ipv6 t.testSelf("-ab-.com", false) t.testMatches(true, "a.com", "A.cOm") t.testMatches(false, "a.comx", "a.com") t.testMatches(false, "1::", "2::") t.testMatches(false, "1::", "1.2.3.4") t.testMatches(true, "1::", "1:0::") t.testMatches(true, "f::", "F:0::") t.testMatches(true, "1::", "[1:0::]") t.testMatches(true, "[1::]", "1:0::") t.testMatches(false, "1::", "1:0:1::") t.testMatches(true, "1.2.3.4", "1.2.3.4") t.testMatches(true, "1.2.3.4", "001.2.3.04") t.testMatches(true, "1.2.3.4", "::ffff:1.2.3.4") //ipv4 mapped t.testMatches(true, "1:2:3:4:5:6:1.2.3.4%a", "1:2:3:4:5:6:102:304%a") t.testMatches(false, "1:2:3:4:5:6:1.2.3.4%", "1:2:3:4:5:6:102:304%") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4%%", "1:2:3:4:5:6:102:304%%") t.testMatches(true, "[1:2:3:4:5:6:1.2.3.4%25%31]", "1:2:3:4:5:6:102:304%1") t.testMatches(true, "[1:2:3:4:5:6:102:304%25%31]", "1:2:3:4:5:6:102:304%1") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4%-", "1:2:3:4:5:6:102:304%-") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4%-/64", "1:2:3:4:5:6:102:304%-/64") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4/1:2:3:4:5:6:1.2.3.4", "1:2:3:4:5:6:1.2.3.4") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4/1:2:3:4:5:6:0.0.0.0", "1:2:3:4:5:6::") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4/1:2:3:4:5:0:0.0.0.0", "1:2:3:4:5::") t.testMatches(true, "[1:2:3:4:5:6::%y]", "1:2:3:4:5:6::%y") t.testMatches(true, "[1:2:3:4:5:6::%25y]", "1:2:3:4:5:6::%y") //see RFC 6874 about %25 t.testMatches(true, "[1:2:3:4:5:6::]/32", "1:2:3:4:5:6::/32") t.testMatches(true, "[1:2::]/32", "1:2::/32") t.testMatches(true, "[1:ff00::]/24", "1:ff00::/24") t.testMatches(true, "[1:ffff::]/24", "1:ffff::/24") t.testMatches(false, "1.2.3.4/255.0.0.0", "1.0.0.0/255.0.0.0") t.testMatches(true, "[IPv6:1:2:3:4:5:6:7:8%y]", "1:2:3:4:5:6:7:8%y") t.testMatches(true, "[IPv6:1:2:3:4:5:6:7:8]", "1:2:3:4:5:6:7:8") t.testMatches(true, "[IPv6:1:2:3:4:5:6::]/32", "1:2:3:4:5:6::/32") t.testMatches(true, "[IPv6:1:2::]/32", "1:2::/32") t.testMatches(true, "[IPv6:::1]", "::1") t.testMatches(true, "[IPv6:1::]", "1::") t.testResolved("a::b:c:d:1.2.3.4%x", "a::b:c:d:1.2.3.4%x") t.testResolved("[a::b:c:d:1.2.3.4%x]", "a::b:c:d:1.2.3.4%x") t.testResolved("[a::b:c:d:1.2.3.4]", "a::b:c:d:1.2.3.4") //square brackets can enclose ipv6 in host names but not addresses t.testResolved("2001:0000:1234:0000:0000:C1C0:ABCD:0876%x", "2001:0:1234::c1c0:abcd:876%x") t.testResolved("[2001:0000:1234:0000:0000:C1C0:ABCD:0876%x]", "2001:0:1234::c1c0:abcd:876%x") t.testResolved("[2001:0000:1234:0000:0000:C1C0:ABCD:0876]", "2001:0:1234::C1C0:abcd:876") //square brackets can enclose ipv6 in host names but not addresses t.testResolved("2001:0000:1234:0000:0000:C1C0:ABCD:0876", "2001:0:1234::C1C0:abcd:876") //square brackets can enclose ipv6 in host names but not addresses t.testResolved("1.2.3.04", "1.2.3.4") t.testResolved_inet_aton("1.2.3", "1.2.0.3") t.testResolved("[1.2.3.4]", "1.2.3.4") if t.fullTest && runDNS { t.testResolved("espn.com", "199.181.132.250") t.testResolved("espn.com/24", "199.181.132.0/24") t.testResolved("instapundit.com", "72.32.173.45") } t.testResolved("9.32.237.26", "9.32.237.26") t.testResolved("9.70.146.84", "9.70.146.84") t.testResolved("", "") t.testNormalizedHost(true, "[A::b:c:d:1.2.03.4]", "[a:0:0:b:c:d:102:304]") //square brackets can enclose ipv6 in host names but not addresses t.testNormalizedHost(true, "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]", "[2001:0:1234:0:0:c1c0:abcd:876]") //square brackets can enclose ipv6 in host names but not addresses t.testNormalizedHost(true, "1.2.3.04", "1.2.3.4") t.testCanonical("[A:0::c:d:1.2.03.4]", "a::c:d:102:304") //square brackets can enclose ipv6 in host names but not addresses t.testCanonical("[2001:0000:1234:0000:0000:C1C0:ABCD:0876]", "2001:0:1234::c1c0:abcd:876") //square brackets can enclose ipv6 in host names but not addresses t.testCanonical("1.2.3.04", "1.2.3.4") t.testNormalizedHost(true, "WWW.ABC.COM", "www.abc.com") t.testNormalizedHost(true, "WWW.AB-C.COM", "www.ab-c.com") t.testURL("https://1.2.3.4") t.testURL("https://[a:a:a:a:b:b:b:b]") t.testURL("https://a:a:a:a:b:b:b:b") t.hostLabelsTest("one.two.three.four.five.six.seven.EIGHT", []string{"one", "two", "three", "four", "five", "six", "seven", "eight"}) t.hostLabelsTest("one.two.three.four.fIVE.sIX.seven", []string{"one", "two", "three", "four", "five", "six", "seven"}) t.hostLabelsTest("one.two.THREE.four.five.six", []string{"one", "two", "three", "four", "five", "six"}) t.hostLabelsTest("one.two.three.four.five", []string{"one", "two", "three", "four", "five"}) t.hostLabelsTest("one.two.three.four", []string{"one", "two", "three", "four"}) t.hostLabelsTest("one.Two.three", []string{"one", "two", "three"}) t.hostLabelsTest("onE.two", []string{"one", "two"}) t.hostLabelsTest("one", []string{"one"}) var emptyLabels []string if t.isLenient() { emptyLabels = []string{"127", "0", "0", "1"} } else { emptyLabels = []string{} } t.hostLabelsTest("", emptyLabels) t.hostLabelsTest(" ", emptyLabels) t.hostLabelsTest("1.2.3.4", []string{"1", "2", "3", "4"}) t.hostLabelsTest("1:2:3:4:5:6:7:8", []string{"1", "2", "3", "4", "5", "6", "7", "8"}) t.hostLabelsTest("[::]", []string{"0", "0", "0", "0", "0", "0", "0", "0"}) t.hostLabelsTest("::", []string{"0", "0", "0", "0", "0", "0", "0", "0"}) t.hostTest(true, "1.2.3.4/1.2.3.4") t.hostTest(true, "1.2.3.4/255.0.0.0") t.hostTest(true, "abc.com/255.0.0.0") t.hostTest(true, "abc.com/::") t.hostTest(true, "abc.com/::1") //Since service names cannot have ':' and can be at most 15 chars, and since all IPv6 must have a ':' or must be at least 32 digits otherwise, there is no ambiguity below //of course, none of the forms below can appear in a URL t.hostTest(true, "abc.com/1::1") //this is abc.com with mask 1::1 t.hostTest(true, "abc.com/1:1") //this one is abc.com with prefix 1 and port 1 t.hostTest(true, "abc.com/1:abc") //this one is abc.com with prefix 1 and service abc t.hostTest(true, "abc.com/1.2.3.4") //this is abc.com with mask 1.2.3.4 t.hostTest(true, "abc.com:a1-2-3-4") //this is abc.com with service a1-2-3-4 (note service must have at least one letter) t.hostTest(true, "abc.com/1::") t.hostTest(true, "abc.com/32") t.hostTest(true, "abc.com.") t.hostTest(true, "abc.com./32") t.hostTest(false, "[1.2.3.4") t.hostTest(false, "[1:2:3:4:5:6:7:8") t.hostTest(true, "[a::b:c:d:1.2.3.4]") //square brackets can enclose ipv6 in host names but not addresses t.hostTest(true, "[a::b:c:d:1.2.3.4%x]") t.hostTest(true, "a::b:c:d:1.2.3.4%x") t.hostTest(false, "a:b:c:d:1.2.3.4%x") t.hostTest(true, "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]") //square brackets can enclose ipv6 in host names but not addresses t.hostTest(true, "2001:0000:1234:0000:0000:C1C0:ABCD:0876%x") //ipv6 must be enclosed in [] t.hostTest(true, "[2001:0000:1234:0000:0000:C1C0:ABCD:0876%x]") //zones not allowed when using [] t.hostTest(true, "1:2:3:4:5:6:1.2.3.4%%") //the % is the zone itself, when treated as an address t.hostTest(false, "[1:2:3:4:5:6:1.2.3.4%%]") //the % is an encoding, when treated as a host t.hostTest(true, "1:2:3:4:5:6:1.2.3.4%%") //the % is allowed in zone, when treated as a address t.hostTest(true, "[1:2:3:4:5:6:1.2.3.4%25%31]") //the % is an encoding, when treated as a host, so this is in fact the zone of 1 (%25 is zone char, %31 is 1) t.hostTest(true, "1:2:3:4:5:6:1.2.3.4%25%31") //this is in fact the zone 25%31 t.hostTest(true, "1.2.3.4") t.hostTest_inet_aton(true, "1.2.3") t.hostTest(true, "0x1.0x2.0x3.04") t.hostTest(true, "0X1.0x2.0x3.04") t.hostTest(true, "0x1.0x2.0b3.04") t.hostTest(true, "0x1.0x2.0B3.04") t.hostTest(true, "[1.2.3.4]") t.hostTest(true, "a_b.com") t.hostTest(true, "_ab.com") t.hostTest(true, "_ab_.com") t.hostTest(false, "-ab-.com") t.hostTest(false, "ab-.com") t.hostTest(false, "-ab.com") t.hostTest(false, "ab.-com") t.hostTest(false, "ab.com-") t.hostTest(true, "a9b.com") t.hostTest(true, "9ab.com") t.hostTest(true, "999.com") t.hostTest(true, "ab9.com") t.hostTest(true, "ab9.com9") t.hostTest_inet_aton(true, "999") t.hostTest_inet_aton(true, "111.999") t.hostTest(false, "999.111") t.hostTest(false, "a*b.com") t.hostTest(false, "*ab.com") t.hostTest(false, "ab.com*") t.hostTest(false, "*.ab.com") t.hostTest(false, "ab.com.*") t.hostTest(false, "ab.co&m") t.hostTest(false, "#.ab.com") t.hostTest(false, "cd.ab.com.~") t.hostTest(false, "#x.ab.com") t.hostTest(false, "cd.ab.com.x~") t.hostTest(false, "x#.ab.com") t.hostTest(false, "cd.ab.com.~x") t.hostTest(true, "xx.ab.com.xx") t.hostTest(true, "ab.cde.fgh.com") t.hostTest(true, "aB.cDE.fgh.COm") t.hostTest(true, "123-123456789-123456789-123456789-123456789-123456789-123456789.com") //label 63 chars t.hostTest(false, "1234-123456789-123456789-123456789-123456789-123456789-123456789.com") //label 64 chars t.hostTest(false, "123.123456789.123456789.123456789.123456789.123456789.123456789.123") //all numbers t.hostTest(true, "aaa.123456789.123456789.123456789.123456789.123456789.123456789.123") //numbers everywhere but first label t.hostTest(false, "a11"+ "-123456789-123456789-123456789-123456789-12345678."+ "-123456789-123456789-123456789-123456789-12345678."+ "-123456789-123456789-123456789-123456789-12345678."+ "-123456789-123456789-123456789-123456789-12345678."+ "-123456789-123456789-123456789-123456789-123456789") //253 chars, but segments start with - t.hostTest(true, "a11"+ "-123456789-123456789-123456789-123456789-12345678."+ "0123456789-123456789-123456789-123456789-12345678."+ "0123456789-123456789-123456789-123456789-12345678."+ "0123456789-123456789-123456789-123456789-12345678."+ "0123456789-123456789-123456789-123456789-123456789") //253 chars t.hostTest(false, "111"+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "01234567890123456789012345678901234567890123456789") //all number t.hostTest(true, "222"+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678f") //not all number, 253 chars t.hostTest(false, "a222"+ "-123456789-123456789-123456789-123456789-12345678."+ "0123456789-123456789-123456789-123456789-12345678."+ "0123456789-123456789-123456789-123456789-12345678."+ "0123456789-123456789-123456789-123456789-12345678."+ "0123456789-123456789-123456789-123456789-123456789") //254 chars t.hostTest(true, "a33"+ ".123456789.123456789.123456789.123456789.123456789"+ ".123456789.123456789.123456789.123456789.123456789"+ ".123456789.123456789.123456789.123456789.123456789"+ ".123456789.123456789.123456789.123456789.123456789"+ ".123456789.123456789.123456789.123456789.123456789") //253 chars t.hostTest(false, "444"+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "01234567890123456789012345678901234567890123456789") //all number t.hostTest(true, "555"+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678f") //not all number t.hostTest(true, "777"+ "01234567890123456789012345678901234567890123456789"+ "0123456789.123456789012345678901234567890123456789"+ "012345678901234567890123.5678901234567890123456789"+ "01234567890123456789012345678901234567.90123456789"+ "0123456789012345678901234567890123456789012345678f") //first 3 segments are 63 chars t.hostTest(false, "777"+ "01234567890123456789012345678901234567890123456789"+ "01234567890.23456789012345678901234567890123456789"+ "012345678901234567890123.5678901234567890123456789"+ "01234567890123456789012345678901234567.90123456789"+ "0123456789012345678901234567890123456789012345678f") //first segment 64 chars t.hostTest(false, "a666"+ ".123456789.123456789.123456789.123456789.123456789"+ ".123456789.123456789.123456789.123456789.123456789"+ ".123456789.123456789.123456789.123456789.123456789"+ ".123456789.123456789.123456789.123456789.123456789"+ ".123456789.123456789.123456789.123456789.123456789") //254 chars t.hostTest(true, "a.9."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5") //252 chars, 127 segments t.hostTest(false, ".a.7."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5") //252 chars, 127 segments, extra dot at front allowTrailingDot2 := true t.hostTest(allowTrailingDot2, "222"+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678f.") //not all number, 253 chars with trailing dot t.hostTest(allowTrailingDot2, "a.8."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5.") //252 chars, 127 segments, extra dot at end t.hostTest(false, "222"+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678."+ "0123456789012345678901234567890123456789012345678..") // double trailing dot t.hostTest(false, "a.6."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5."+ "1.1.1.1.1.2.2.2.2.2.3.3.3.3.3.4.4.4.4.4.5.5.5.5.5.8") //254 chars, 128 segments t.hostTest(false, "a:b:com") t.hostTest(true, "a:b::ccc") t.hostTest(true, "a:b:c:d:e:f:a:b") t.hostTest(false, ".as.b.com") //starts with dot allowTrailingDot1 := true t.hostTest(allowTrailingDot1, "as.b.com.") //ends with dot t.hostTest(false, ".as.b.com.") //starts and ends with dot t.hostTest(false, "as..b.com") //double dot t.hostTest(false, "as.b..com") //double dot t.hostTest(false, "..as.b.com") //starts with dots t.hostTest(false, "as.b.com..") //ends with dots t.hostTest(false, "1.2.3.4:123456789012345a") t.hostTest(false, "1.2.3.4:") t.hostTest(false, "1.2.3.4:a-") t.hostTest(false, "1.2.3.4:-a") t.hostTest(false, "1.2.3.4:a--b") t.hostTest(false, "1.2.3.4:x-") t.hostTest(false, "1.2.3.4:-x") t.hostTest(false, "1.2.3.4:x--x") allowEmptyZone := true t.hostTest(allowEmptyZone, "[::1%25/32]") // empty zone t.hostTest(allowEmptyZone, "::1%/32") // empty zone t.hostTest(false, "[a.b.com]:nfs") //non-Ipv6 inside brackets t.hostTest(true, "[::]:nfs") t.hostTest(true, "255.22.2.111.3.in-addr.arpa:35") //not a valid address but still a valid host t.hostTest(false, "[::1]x") t.hostTest(false, "[::1x]") t.hostTest(false, "[::x1]") t.hostTest(false, "x[::1]") t.hostTest(false, "[]") t.hostTest(false, "[a]") t.hostTest(false, "1.2.2.256:33") t.hostTest(true, "f.F.f.f.F.e.e.0.0.d.D.d.d.c.c.c.c.b.b.b.b.a.a.a.a.b.b.b.b.c.c.c.C.ip6.int:45") //not an address, but a valid host t.hostTest(true, "f.F.f.f.F.e.e.0.0.d.D.d.d.c.c.c.c.b.b.b.b.a.a.a.a.b.b.b.b.c.c.c.C.ip6.int") //not an address, but a valid host t.hostTest(false, "aa-bb-cc-dd-ee-ff-.ipv6-literal.net") t.hostTest(true, "aa-bb-cc-dd-ge-ff.ipv6-literal.net") //not an address but a valid host t.hostTest(true, "[1::/16]/32") t.hostTest(true, "[1::/16]/16") t.hostTest(true, "[1.2.3.4/16]/32") t.hostTest(true, "[1.2.3.4/16]/16") t.hostTest(true, "[1.2.3.4/16]/255.255.255.0") t.hostTest(true, "[1.2.3.4/16]/255.255.0.0") t.hostTest(true, "[1.2.3.4/255.255.255.0]/16") t.hostTest(true, "[1.2.3.4/255.255.0.0]/16") t.hostTest(true, "[1.2.3.4/255.255.255.0]/255.255.255.0") t.hostTest(true, "[1.2.3.4/255.255.0.0]/255.255.255.0") t.hostTest(true, "[1.2.3.4/255.255.255.0]/255.255.0.0") t.hostTest(allowEmptyZone, "1::%") // empty zone t.hostTest(allowEmptyZone, "1::%/16") // empty zone t.hostTest(allowEmptyZone, "a:b:c:d:e:f:a:b%/64") // empty zone t.hostTest(false, "::1:88888") //port too large, also too large to be ipv6 segment t.hostTest(false, "::1:88_8") //invalid because no letter in service name, nor is it a port t.hostTest(t.isLenient(), "::1:88-8") //valid because address with ranged segment, but it is not a service because no letter, nor a port t.hostTest(true, "::1:8888") t.hostTest(true, "::1:58888") t.hostTest(true, "::1:8a-8") t.hostTest(t.isLenient(), "::1:-8a88") //this passes if the second segment considered a range t.hostTest(false, "1.2.3.4:-8a8") //-8a8 can only be a port or service, but leading hyphen not allowed for a service t.hostTest(true, "1.2.3.4:8-a8") t.hostTest(t.isLenient(), "::1:8a8-:2") t.hostTest(t.isLenient(), "::1:-8a8:2") t.hostTest(t.isLenient(), "::1:8a8-") //this passes if the second segment considered a range, cannot be a service due to trailing hyphen t.hostTest(t.isLenient(), "::1:-8a8") //this passes if the second segment considered a range, cannot be a service due to leading hyphen t.hostTest(true, "[1.2.3.4]/255.255.255.0") t.hostTest(true, "[::]/ffff::") t.hostTest(false, "[::]/255.255.0.0") // prefix len equivalent t.hostTest(false, "[::]/0.255.0.0") // not prefix len t.hostTest(false, "[1.2.3.4]/ffff::") t.hostTest(false, "[1.2.3.4]/::ffff") // note the colon placement here could be confused with port t.hostTest(false, "[1.2.3.4]/ffff::ffff") // // And now the same but the mask versions don't match t.hostTest(true, "[1.2.3.4/255.0.0.0]/255.255.255.0") // prefix len equivalent t.hostTest(true, "[::/ff::]/ffff::") t.hostTest(false, "[::/ffff::]/255.255.0.0") t.hostTest(false, "[::/ffff::]/0.255.0.0") // not prefix len t.hostTest(false, "[::/::ffff]/0.255.0.0") // not prefix len t.hostTest(false, "[1.2.3.4/0.0.0.255]/ffff::") t.hostTest(false, "[1.2.3.4/0.0.0.255]/::ffff") // note the colon placement here could be confused with port t.hostTest(false, "[1.2.3.4/0.0.0.255]/ffff::ffff") t.hostTest(false, "[1.2.3.4/255.0.0.0]/ffff::") t.hostTest(false, "[1.2.3.4/255.0.0.0]/::ffff") // note the colon placement here could be confused with port t.hostTest(false, "[1.2.3.4/255.0.0.0]/ffff::ffff") portNum1 := ipaddr.PortInt(1) portNum3 := ipaddr.PortInt(3) portNum33 := ipaddr.PortInt(33) portNum35 := ipaddr.PortInt(35) portNum45 := ipaddr.PortInt(45) portNum80 := ipaddr.PortInt(80) portNum123 := ipaddr.PortInt(123) portNum48888 := ipaddr.PortInt(48888) port1 := ToPort(portNum1) port3 := ToPort(portNum3) port33 := ToPort(portNum33) port35 := ToPort(portNum35) port45 := ToPort(portNum45) port80 := ToPort(portNum80) port123 := ToPort(portNum123) port48888 := ToPort(portNum48888) t.testHostAddressPortZone("aa-bb-cc-dd-ee-ff-aaaa-bbbb.ipv6-literal.net", "aa:bb:cc:dd:ee:ff:aaaa:bbbb", nil, "") t.testHostAddress("aa-bb-cc-dd-ee-ff-aaaa-bbbbseth0.ipv6-literal.net", "aa:bb:cc:dd:ee:ff:aaaa:bbbb", "aa:bb:cc:dd:ee:ff:aaaa:bbbb%eth0", nil, "eth0") t.testHostPortZone("aa-bb-cc-dd-ee-ff.ipv6-literal.net", "aa-bb-cc-dd-ee-ff.ipv6-literal.net", nil, "") //not a valid address, too few segments, but a valid host t.testHostPortZone("aa-Bb-cc-dd-ee-FF.ipv6-literal.net", "aa-bb-cc-dd-ee-ff.ipv6-literal.net", nil, "") //not a valid address, too few segments, but a valid host t.testHostAddressPortZone("aa-bb-cc-dd-ee-ff-aaaa-bbb.ipv6-literal.net", "aa:bb:cc:dd:ee:ff:aaaa:bbb", nil, "") t.testHostAddressPortZone("aa-Bb-cc-dd-ee-FF-aaaa-bbb.ipv6-literal.net", "aa:bb:cc:dd:ee:ff:aaaa:bbb", nil, "") t.testHostAddressPortZone("f.f.f.f.e.e.0.0.d.d.d.d.c.c.c.c.b.b.b.b.a.a.a.a.b.b.b.b.c.c.c.c.ip6.arpa", "cccc:bbbb:aaaa:bbbb:cccc:dddd:ee:ffff", nil, "") t.testHostAddressPortZone("f.f.f.f.e.e.0.0.d.d.d.d.c.c.c.c.b.b.b.b.a.a.a.a.b.b.b.b.c.c.c.c.ip6.int", "cccc:bbbb:aaaa:bbbb:cccc:dddd:ee:ffff", nil, "") t.testHostAddressPortZone("f.f.f.f.e.e.0.0.d.d.d.d.c.c.c.c.b.b.b.b.a.a.a.a.b.b.b.b.c.c.c.c.ip6.int:45", "cccc:bbbb:aaaa:bbbb:cccc:dddd:ee:ffff", port45, "") t.testHostAddressPortZone("F.f.f.F.e.e.0.0.d.D.d.d.c.c.c.c.b.b.b.b.a.a.a.a.b.b.b.b.c.c.c.C.ip6.int:45", "cccc:bbbb:aaaa:bbbb:cccc:dddd:ee:ffff", port45, "") t.testHostPortZone("f.F.f.f.F.e.e.0.0.d.D.d.d.c.c.c.c.b.b.b.b.a.a.a.a.b.b.b.b.c.c.c.C.ip6.int:45", "f.f.f.f.f.e.e.0.0.d.d.d.d.c.c.c.c.b.b.b.b.a.a.a.a.b.b.b.b.c.c.c.c.ip6.int", port45, "") //not a valid address, but a valid host t.testHostAddressPortZone("255.22.2.111.in-addr.arpa", "111.2.22.255", nil, "") t.testHostAddressPortZone("255.22.2.111.in-addr.arpa:35", "111.2.22.255", port35, "") t.testHostPortZone("255.22.2.111.3.in-addr.arpa:35", "255.22.2.111.3.in-addr.arpa", port35, "") t.testHostAddressPortZone("1.2.2.1:33", "1.2.2.1", port33, "") t.testHostAddressPortZone("[::1]:33", "::1", port33, "") t.testHostAddressPortZone("::1:33", "::1:33", nil, "") t.testHostAddress("::1%eth0", "::1", "::1%eth0", nil, "eth0") t.testHostAddress("[::1%eth0]:33", "::1", "::1%eth0", port33, "eth0") t.testHostPortZone("bla.bla:33", "bla.bla", port33, "") t.testHostPortZone("blA:33", "bla", port33, "") t.testHostPortZone("f:33", "f", port33, "") t.testHostAddressPortZone("f::33", "f::33", nil, "") t.testHostAddressPortZone("::1", "::1", nil, "") t.testHostAddressPortZone("[::1]", "::1", nil, "") t.testHostAddressWithService("1.2.3.4:nfs", "1.2.3.4", "nfs", "") t.testHostPortServZonePref("[::1%eth0]:nfs", "::1", "::1%eth0", nil, "nfs", "eth0", nil) t.testHostAddressWithService("1.2.3.4:12345678901234a", "1.2.3.4", "12345678901234a", "") t.testHostAddressWithService("[::1]:12345678901234a", "::1", "12345678901234a", "") t.testHostAddressWithService("[::1]:12345678901234x", "::1", "12345678901234x", "") t.testHostAddressWithService("1.2.3.4:a", "1.2.3.4", "a", "") t.testHostAddressWithService("1.2.3.4:a-b-c", "1.2.3.4", "a-b-c", "") t.testHostAddressWithService("[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]:a-b-c", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "a-b-c", "") t.testHostPortServZonePref("a.b.c/16:nfs", "a.b.c", "", nil, "nfs", "", p16) t.testHostPortServZonePref("a.b.c./16:nfs", "a.b.c", "", nil, "nfs", "", p16) t.testHostPortServZonePref("a.b.c/16:80", "a.b.c", "", port80, "", "", p16) t.testHostPortServZonePref("a.b.c./16:nfs", "a.b.c", "", nil, "nfs", "", p16) t.testHostPortServZonePref("a.b.c./16:80", "a.b.c", "", port80, "", "", p16) t.testHostPortServZonePref("a.b.c:80", "a.b.c", "", port80, "", "", nil) t.testHostPortServZonePref("a.b.c.:80", "a.b.c", "", port80, "", "", nil) t.testHostWithService("a.b.c:nfs", "a.b.c", "nfs", "") t.testHostWithService("a.b.com:12345678901234a", "a.b.com", "12345678901234a", "") t.testHostWithService("a.b.com.:12345678901234a", "a.b.com", "12345678901234a", "") t.testHostWithService("a.b.com:12345678901234x", "a.b.com", "12345678901234x", "") t.testHostWithService("a.b.com:x12345678901234", "a.b.com", "x12345678901234", "") t.testHostWithService("a.b.com:12345x789012345", "a.b.com", "12345x789012345", "") t.testHostWithService("a.b.com:a", "a.b.com", "a", "") t.testHostWithService("a.b.com:a-b-c", "a.b.com", "a-b-c", "") t.testHostWithService("a.b.c:a-b-c", "a.b.c", "a-b-c", "") t.testHostWithService("123-123456789-123456789-123456789-123456789-123456789-123456789.com:a-b-c", "123-123456789-123456789-123456789-123456789-123456789-123456789.com", "a-b-c", "") t.testHostWithService("123-123456789-123456789-123456789-123456789-123456789-123456789.com:12345x789012345", "123-123456789-123456789-123456789-123456789-123456789-123456789.com", "12345x789012345", "") expectPortParams := new(addrstrparam.HostNameParamsBuilder).Set(hostOptions).ExpectPort(true).ToParams() t.testHostAddressWithService("fe80::6a05:caff:fe3:nfs", "fe80::6a05:caff:fe3", "nfs", "") t.testHostAddressPortZone("fe80::6a05:caff:fe3:123", "fe80::6a05:caff:fe3:123", nil, "") hostName := t.createParamsHost("fe80::6a05:caff:fe3:123", expectPortParams) t.testHostPortServZone(hostName, "fe80::6a05:caff:fe3", "fe80::6a05:caff:fe3", port123, "", "") t.testHostAddress("[1::%25%241]", "1::", "1::%$1", nil, "$1") t.testHostAddress("[1::%%241]", "1::", "1::%$1", nil, "$1") //when zone marker not %25 we are forgiving t.testHostAddress("[1::%25%241]:123", "1::", "1::%$1", port123, "$1") t.testHostAddress("[1::%%241]:123", "1::", "1::%$1", port123, "$1") t.testHostAddress("1::%25%241:123", "1::", "1::%25%241", port123, "25%241") //%hexhex encoding only when inside '[]' since '[]' is the proper URL format t.testHostAddress("1::%%241:123", "1::", "1::%%241", port123, "%241") t.testHostAddressPref("1::%%1/16", "1:*:*:*:*:*:*:*", "1::%%1/16", nil, "%1", p16) t.testHostAddressPref("[1::%251]/16", "1:*:*:*:*:*:*:*", "1::%1/16", nil, "1", p16) t.testHostAddressPref("[1::%251/16]:3", "1:*:*:*:*:*:*:*", "1::%1/16", port3, "1", p16) t.testHostAddressPref("1::%1/16:3", "1:*:*:*:*:*:*:*", "1::%1/16", port3, "1", p16) t.testHostAddressPref("1::%%1/16:3", "1:*:*:*:*:*:*:*", "1::%%1/16", port3, "%1", p16) //that's right, zone, prefix and port! t.testHostAddressPref("[1::/16]:3", "1:*:*:*:*:*:*:*", "1::/16", port3, "", p16) t.testHostAddressPref("[1::/16]/32", "1:*:*:*:*:*:*:*", "1::/16", nil, "", p16) t.testHostAddressPref("[1::/16]/16", "1:*:*:*:*:*:*:*", "1::/16", nil, "", p16) t.testHostAddressPref("[1.2.3.4/16]/32", "1.2.3.4", "1.2.3.4/16", nil, "", p16) t.testHostAddressPref("[1.2.3.4/16]/16", "1.2.3.4", "1.2.3.4/16", nil, "", p16) t.testHostAddressPref("[1.2.3.4/16]/255.255.255.0", "1.2.3.4", "1.2.3.4/16", nil, "", p16) t.testHostAddressPref("[1.2.3.4/255.255.255.0]/16", "1.2.3.4", "1.2.3.4/16", nil, "", p16) t.testHostAddressPref("[1.2.3.4/16]/255.255.0.0", "1.2.3.4", "1.2.3.4/16", nil, "", p16) t.testHostAddressPref("[1.2.3.4/255.255.0.0]/16", "1.2.3.4", "1.2.3.4/16", nil, "", p16) t.testHostAddressPref("[1.2.3.4/255.255.0.0]/255.255.255.0", "1.2.3.4", "1.2.3.4/16", nil, "", p16) t.testHostAddressPref("[1.2.3.4/255.255.255.0]/255.255.0.0", "1.2.3.4", "1.2.3.4/16", nil, "", p16) t.testHostAddressPref("[1.2.3.4/255.255.0.0]/255.255.255.0", "1.2.3.4", "1.2.3.4/16", nil, "", p16) t.testHostAddressPref("1::/16:3", "1:*:*:*:*:*:*:*", "1::/16", port3, "", p16) t.testHostAddressPref("[1::%251/16]", "1:*:*:*:*:*:*:*", "1::%1/16", nil, "1", p16) t.testHostAddressPref("[1::%25%241/16]", "1:*:*:*:*:*:*:*", "1::%$1/16", nil, "$1", p16) t.testHostAddressPref("1::%1/16", "1:*:*:*:*:*:*:*", "1::%1/16", nil, "1", p16) t.testHostAddressPref("1::%1%1/16", "1:*:*:*:*:*:*:*", "1::%1%1/16", nil, "1%1", p16) t.testHostAddressPref("1.2.3.4/16", "1.2.3.4", "1.2.3.4/16", nil, "", p16) t.testHostAddressPref("1.2.0.0/16", "1.2.*.*", "1.2.0.0/16", nil, "", p16) t.testHostPortServZonePref("a.b.com/24", "a.b.com", "", nil, "", "", p24) t.testHostPortServZonePref("a.b.com./24", "a.b.com", "", nil, "", "", p24) t.testHostPortServZonePref("a.b.com", "a.b.com", "", nil, "", "", nil) t.testHostPortServZonePref("a.b.com.", "a.b.com", "", nil, "", "", nil) t.testHostAddressPref("[fe80::%2]/64", "fe80::*:*:*:*", "fe80::%2/64", nil, "2", p64) //prefix outside the host (can be either inside or outside) t.testHostAddressPref("fe80::%2/64", "fe80::*:*:*:*", "fe80::%2/64", nil, "2", p64) t.testHostAddress("[::123%25%25%25aaa%25]", "::123", "::123%%%aaa%", nil, "%%aaa%") t.testHostAddress("[::123%25%25%25%24aa%25]", "::123", "::123%%%$aa%", nil, "%%$aa%") t.testHostAddress("[::123%25%24%25%24aa%25]", "::123", "::123%$%$aa%", nil, "$%$aa%") t.testHostAddress("::123%%%", "::123", "::123%%%", nil, "%%") t.testHostAddress("fe80:0:0:0:0:6a05:caff:fe3%x:123", "fe80::6a05:caff:fe3", "fe80::6a05:caff:fe3%x", port123, "x") t.testHostPortServZonePref("fe80:0:0:0:0:6a05:caff:fe3%x:abc", "fe80::6a05:caff:fe3", "fe80::6a05:caff:fe3%x", nil, "abc", "x", nil) t.testHostPortServZonePref("fe80:0:0:0:0:6a05:caff:fe3%x/64:abc", "fe80::6a05:caff:fe3", "fe80::6a05:caff:fe3%x/64", nil, "abc", "x", p64) //that's right, zone, prefix and service t.testHostPortServZonePref("[fe80:0:0:0:0:6a05:caff:fe3%x/64]:abc", "fe80::6a05:caff:fe3", "fe80::6a05:caff:fe3%x/64", nil, "abc", "x", p64) //that's right, zone, prefix and service t.testHostAddress("fe80::6a05:caff:fe3%x:123", "fe80::6a05:caff:fe3", "fe80::6a05:caff:fe3%x", port123, "x") t.testHostPortServZonePref("fe80::6a05:caff:fe3%x:abc", "fe80::6a05:caff:fe3", "fe80::6a05:caff:fe3%x", nil, "abc", "x", nil) t.testHostAddressPortZone("fe80:0:0:0:0:6a05:caff:fe3", "fe80::6a05:caff:fe3", nil, "") t.testHostAddressWithService("fe80:0:0:0:0:0:6a05:caff:fe3", "fe80::6a05:caff", "fe3", "") t.testHostAddressPortZone("fe80:0:0:0:0:6a05:caff:fe3:123", "fe80::6a05:caff:fe3", port123, "") t.testHostAddressWithService("fe80:0:0:0:0:6a05:caff:fe3:*", "fe80::6a05:caff:fe3", "*", "") t.testHostAddressPortZone("::1:8888", "::1:8888", nil, "") t.testHostAddressWithService("::1:88g8", "::1", "88g8", "") t.testHostAddressWithService("::1:88a8", "::1:88a8", "", "") hostName = t.createParamsHost("::1:88a8", expectPortParams) t.testHostPortServZone(hostName, "::1", "::1", nil, "88a8", "") t.testHostAddressPortZone("::1:48888", "::1", port48888, "") t.testHostAddressWithService("::1:nfs", "::1", "nfs", "") t.testHostAddressWithService(":::*", "::", "*", "") t.testHostAddressPortZone(":::1", "::", port1, "") t.testHostAddressPortZone(":::123", "::", port123, "") t.testHostAddressPortZone("[::]:123", "::", port123, "") t.testHostInetSocketAddress("1.2.3.4:80", "1.2.3.4", portNum80) t.testHostInetSocketAddress(":::123", "::", portNum123) t.testHostInetSocketAddress("[::]:123", "::", portNum123) //t.testHostInetSocketAddress("a.com:123", "a.com", 123); //t.testHostInetSocketAddress("espn.com:123", "espn.com", 123); //t.testHostInetSocketAddress("foo:123", "foo", 123); t.testNotHostInetSocketAddress("1.2.3.4") t.testNotHostInetSocketAddress("::") t.testNotHostInetSocketAddress("a.com") t.testNotHostInetSocketAddress("foo") t.testHostInetSocketAddressService("1.2.3.4:http", func(s string) ipaddr.Port { if s == "http" { port80 := ipaddr.PortInt(80) return ToPort(port80) } return nil }, "1.2.3.4", 80) t.testHostInetSocketAddressSA("1.2.3.4:http", func(s string) ipaddr.Port { if s == "htt" { port80 := ipaddr.PortInt(80) return ToPort(port80) } return nil }, nil) t.testHostInetSocketAddressSA("1.2.3.4:http", nil, nil) } func (t hostTester) testSelf(host string, isSelf bool) { w := t.createHost(host) if isSelf != w.IsSelf() { t.addFailure(newHostFailure("failed: isSelf is "+strconv.FormatBool(isSelf), w)) } t.incrementTestCount() } func hostConversionMatches(host1, host2 *ipaddr.HostName) bool { h1 := host1.AsAddress() if h1 != nil && h1.IsIPv4() { h2 := host2.AsAddress() if !h2.IsIPv4() { if conv.IsIPv4Convertible(h2) { return h1.Equal(conv.ToIPv4(h2)) } } } else if h1 != nil && h1.IsIPv6() { h2 := host2.AsAddress() if !h2.IsIPv6() { if conv.IsIPv6Convertible(h2) { return h1.Equal(conv.ToIPv6(h2)) } } } return false } func (t hostTester) testMatches(matches bool, host1, host2 string) { t.testMatchesParams(matches, host1, host2, hostOptions) } func (t hostTester) testMatchesParams(matches bool, host1, host2 string, options addrstrparam.HostNameParams) { h1 := t.createParamsHost(host1, options) h2 := t.createParamsHost(host2, options) if matches != h1.Equal(h2) && matches != hostConversionMatches(h1, h2) { t.addFailure(newHostFailure("failed: match with "+host2, h1)) } else { if matches != h2.Equal(h1) && matches != hostConversionMatches(h2, h1) { t.addFailure(newHostFailure("failed: match with "+host1, h2)) } else { t.testNormalizedMatches(h1) t.testNormalizedMatches(h2) } } t.incrementTestCount() } func isReserved(c byte) bool { isUnreserved := (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == ipaddr.RangeSeparator || c == ipaddr.LabelSeparator || c == '_' || c == '~' return !isUnreserved } func translateReserved(addr *ipaddr.IPv6Address, str string) string { //This is particularly targeted towards the zone if !addr.HasZone() { return str } index := strings.Index(str, ipaddr.IPv6ZoneSeparatorStr) translated := strings.Builder{} translated.Grow(((len(str) - index) * 3) + index) translated.WriteString(str[:index]) translated.WriteString("%25") for i := index + 1; i < len(str); i++ { c := str[i] if isReserved(c) { translated.WriteByte('%') translated.WriteString(strconv.FormatUint(uint64(c), 16)) } else { translated.WriteByte(c) } } return translated.String() } func (t hostTester) testNormalizedMatches(h1 *ipaddr.HostName) { var normalized string if h1.IsAddress() && h1.AsAddress().IsPrefixed() && h1.AsAddress().IsIPv6() { addr := h1.AsAddress().GetLower().WithoutPrefixLen().ToIPv6() normalized = "[" + translateReserved(addr, addr.ToNormalizedString()) + "]/" + h1.AsAddress().GetNetworkPrefixLen().String() } else if h1.IsAddress() && h1.AsAddress().IsIPv6() { addr := h1.AsAddress().ToIPv6() normalized = "[" + translateReserved(addr, addr.ToNormalizedWildcardString()) + "]" } else { normalized = h1.ToNormalizedString() } h1Bracketed := h1.ToNormalizedString() if h1Bracketed != normalized { t.addFailure(newHostFailure("failed: bracketed is "+normalized, h1)) } t.incrementTestCount() } func (t hostTester) testResolved_inet_aton(original, expectedResolved string) { origAddress := t.createInetAtonHost(original) t.testResolvedHost(origAddress, original, expectedResolved) } func (t hostTester) testResolved(original, expectedResolved string) { origAddress := t.createHost(original) t.testResolvedHost(origAddress, original, expectedResolved) } func (t hostTester) testResolvedHost(original *ipaddr.HostName, originalStr, expectedResolved string) { resolvedAddress := original.GetAddress() var result bool if resolvedAddress == nil && original.IsAllAddresses() && expectedResolved != "" { //special case for "*" exp := t.createAddress(expectedResolved) result = original.AsAddressString().Equal(exp) } else { if resolvedAddress == nil { result = expectedResolved == "" } else { expectedStr := t.createAddress(expectedResolved) expected := expectedStr.GetAddress() result = resolvedAddress.Equal(expected) } } if !result { if resolvedAddress == nil { t.addFailure(newHostFailure("resolved was nil, original was "+originalStr, original)) } else { t.addFailure(newHostFailure("resolved was "+resolvedAddress.String()+" original was "+originalStr, original)) } } else if resolvedAddress != nil && !(resolvedAddress.IsIPv6() && resolvedAddress.ToIPv6().HasZone()) { host := resolvedAddress.ToHostName() if !original.Equal(host) && !original.IsSelf() && !host.IsSelf() { t.addFailure(newHostFailure("reverse was "+host.String()+" original was "+original.String(), original)) } else if !original.IsAddress() { } } t.incrementTestCount() } func (t hostTester) testNormalizedHost(expectMatch bool, original, expected string) { w := t.createHost(original) normalized := w.ToNormalizedString() if (normalized != expected) == expectMatch { t.addFailure(newHostFailure("normalization was "+normalized, w)) } t.incrementTestCount() } func (t hostTester) testCanonical(original, expected string) { w := t.createHost(original) canonical := w.AsAddress().ToCanonicalString() if canonical != (expected) { t.addFailure(newHostFailure("canonicalization was "+canonical, w)) } t.incrementTestCount() } func (t hostTester) testURL(url string) { w := t.createHost(url) err := w.Validate() if err == nil { t.addFailure(newHostFailure("failed: "+"URL "+url, w)) } t.incrementTestCount() } func (t hostTester) hostTest_inet_aton(pass bool, x string) { addr := t.createInetAtonHost(x) t.hostTestDouble(pass, addr, false) } func (t hostTester) hostTest(pass bool, x string) { addr := t.createHost(x) t.hostTestDouble(pass, addr, true) } var i uint64 func (t hostTester) hostTestDouble(pass bool, addr *ipaddr.HostName, doubleTest bool) { t.hostNameTest(pass, addr) //do it a second time to test the caching t.hostNameTest(pass, addr) if pass && doubleTest { //here we call getHost twice, once after calling getNormalizedLabels and once without calling getNormalizedLabels, //this is because getHost will use the labels but only if they exist already two := t.createParamsHost(addr.String(), addr.GetValidationOptions()) var twoString, oneString string myI := atomic.LoadUint64(&i) if myI%2 == 0 { two.GetNormalizedLabels() twoString = two.GetHost() oneString = addr.GetHost() } else { oneString = addr.GetHost() two.GetNormalizedLabels() twoString = two.GetHost() } myI++ atomic.StoreUint64(&i, myI) if oneString != twoString { t.addFailure(newHostFailure(oneString+" "+twoString, addr)) } t.incrementTestCount() } } func (t hostTester) hostNameTest(pass bool, addr *ipaddr.HostName) { if t.isNotExpected(pass, addr) { t.addFailure(newHostFailure("error parsing host "+addr.String(), addr)) //this part just for debugging t.isNotExpected(pass, addr) } t.incrementTestCount() } func (t hostTester) isNotExpected(expectedPass bool, addr *ipaddr.HostName) bool { err := addr.Validate() if err != nil { return expectedPass } return !expectedPass } func (t hostTester) toExpected(expected string, expectedPort ipaddr.PortInt) *net.TCPAddr { h := t.createHost(expected) addr := h.GetAddress() var zone ipaddr.Zone if addr.IsIPv6() { zone = addr.ToIPv6().GetZone() } return &net.TCPAddr{ IP: addr.GetNetIP(), Port: int(expectedPort), Zone: string(zone), } } func (t hostTester) testNotHostInetSocketAddress(host string) { t.testHostInetSocketAddressSA(host, nil, nil) } func (t hostTester) testHostInetSocketAddress(host, expected string, expectedPort ipaddr.PortInt) { t.testHostInetSocketAddressService(host, nil, expected, expectedPort) } func (t hostTester) testHostInetSocketAddressService(host string, serviceMapper func(string) ipaddr.Port, expected string, expectedPort ipaddr.PortInt) { t.testHostInetSocketAddressSA(host, serviceMapper, t.toExpected(expected, expectedPort)) } func (t hostTester) testHostInetSocketAddressSA(host string, serviceMapper func(string) ipaddr.Port, expected *net.TCPAddr) { h := t.createHost(host) socketAddr := h.ToNetTCPAddrService(serviceMapper) if socketAddr == nil && expected == nil { } else if socketAddr == nil || expected == nil { t.addFailure(newHostFailure(fmt.Sprintf("socket address mismatch, expected: %v result: %v", expected, socketAddr), h)) } else if socketAddr.Port != expected.Port || socketAddr.Zone != expected.Zone || !socketAddr.IP.Equal(expected.IP) { t.addFailure(newHostFailure("socket address mismatch, expected: "+expected.String()+" result: "+socketAddr.String(), h)) } if socketAddr != nil && h.GetService() == "" { h2, _ := ipaddr.NewHostNameFromNetTCPAddr(socketAddr) if !h.Equal(h2) { t.addFailure(newHostFailure("socket address mismatch, expected: "+h.String()+" result: "+h2.String(), h)) } } t.incrementTestCount() } func (t hostTester) testHostAddressWithService(host, hostExpected, serviceExpected string, expectedZone ipaddr.Zone) { t.testHostPortServZonePref(host, hostExpected, hostExpected, nil, serviceExpected, expectedZone, nil) } func (t hostTester) testHostWithService(host, hostExpected, serviceExpected string, expectedZone ipaddr.Zone) { t.testHostPortServZonePref(host, hostExpected, "", nil, serviceExpected, expectedZone, nil) } func (t hostTester) testHostAddressPortZone(host, hostExpected string, portExpected ipaddr.Port, expectedZone ipaddr.Zone) { t.testHostAddress(host, hostExpected, hostExpected, portExpected, expectedZone) } func (t hostTester) testHostAddressPortZonePref(host, hostExpected string, portExpected ipaddr.Port, expectedZone ipaddr.Zone, prefixLength ipaddr.PrefixLen) { t.testHostAddressPref(host, hostExpected, hostExpected, portExpected, expectedZone, prefixLength) } func (t hostTester) testHostPortZone(host, hostExpected string, portExpected ipaddr.Port, expectedZone ipaddr.Zone) { t.testHostPortServZonePref(host, hostExpected, "", portExpected, "", expectedZone, nil) } func (t hostTester) testHostAddress(host, hostExpected, addrExpected string, portExpected ipaddr.Port, expectedZone ipaddr.Zone) { t.testHostPortServZonePref(host, hostExpected, addrExpected, portExpected, "", expectedZone, nil) } func (t hostTester) testHostAddressPref(host, hostExpected, addrExpected string, portExpected ipaddr.Port, expectedZone ipaddr.Zone, prefixLengthExpected ipaddr.PrefixLen) { t.testHostPortServZonePref(host, hostExpected, addrExpected, portExpected, "", expectedZone, prefixLengthExpected) } func (t hostTester) testHostPortServZonePref(host, hostExpected, addrExpected string, portExpected ipaddr.Port, serviceExpected string, expectedZone ipaddr.Zone, prefixLengthExpected ipaddr.PrefixLen) { hostName := t.createHost(host) t.testHostAll(hostName, hostExpected, addrExpected, portExpected, serviceExpected, expectedZone, prefixLengthExpected) } func (t hostTester) testHostPortServZone(hostName *ipaddr.HostName, hostExpected, addrExpected string, portExpected ipaddr.Port, serviceExpected string, expectedZone ipaddr.Zone) { t.testHostAll(hostName, hostExpected, addrExpected, portExpected, serviceExpected, expectedZone, nil) } func addressesEqual(one, two *ipaddr.IPAddress) bool { return one.Equal(two) } func (t hostTester) testHostAll(hostName *ipaddr.HostName, hostExpected, addrExpected string, portExpected ipaddr.Port, serviceExpected string, expectedZone ipaddr.Zone, prefixLengthExpected ipaddr.PrefixLen) { h := hostName.GetHost() var addressExpected *ipaddr.IPAddress if addrExpected != "" { addressExpected = t.createAddress(addrExpected).GetAddress() } addrHost := hostName.AsAddress() port := hostName.GetPort() var zone ipaddr.Zone if addrHost != nil && addrHost.IsIPv6() { zone = addrHost.ToIPv6().GetZone() } prefLength := hostName.GetNetworkPrefixLen() if h != hostExpected { t.addFailure(newHostFailure("failed: host is "+h, hostName)) } else if !port.Equal(portExpected) { t.addFailure(newHostFailure("failed: port is "+port.String(), hostName)) } else if zone != expectedZone { t.addFailure(newHostFailure("failed: zone is "+zone.String(), hostName)) } else if !addressesEqual(addrHost, addressExpected) { t.addFailure(newHostFailure(fmt.Sprintf("failed: address is %v", addrHost), hostName)) } else if !prefLength.Equal(prefixLengthExpected) { t.addFailure(newHostFailure("failed: prefix is "+prefLength.String(), hostName)) } if addressExpected != nil && addrHost != nil { if serviceExpected == "" { if portExpected != nil { h2 := ipaddr.NewHostNameFromAddrPort(addrHost, uint16(portExpected.Port())) if !h2.Equal(hostName) { t.addFailure(newHostFailure("failed: host is "+h2.String(), hostName)) } h3 := ipaddr.NewHostNameFromAddrPort(addressExpected, uint16(portExpected.Port())) if !h3.Equal(hostName) { t.addFailure(newHostFailure("failed: host is "+h3.String(), hostName)) } } else if expectedZone == "" { if prefixLengthExpected == nil { h2, _ := ipaddr.NewHostNameFromNetIP(addrHost.GetNetIP()) if !h2.Equal(hostName) { t.addFailure(newHostFailure("failed: host is "+h2.String(), hostName)) } } else { h2, _ := ipaddr.NewHostNameFromPrefixedNetIP(addrHost.GetNetIP(), prefixLengthExpected) if !h2.Equal(hostName) { t.addFailure(newHostFailure("failed: host is "+h2.String(), hostName)) } } } } } t.incrementTestCount() } func ToPort(i ipaddr.PortInt) ipaddr.Port { res := ipaddr.PortNum(i) return &res } ipaddress-go-1.5.4/ipaddr/test/ipaddralltest.go000066400000000000000000001076451440250641600215260ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package test import ( "math" "math/big" "strconv" "github.com/seancfoley/ipaddress-go/ipaddr" ) type ipAddressAllTester struct { ipAddressRangeTester } func (t ipAddressAllTester) run() { t.testMatches(true, "-", "*.*") t.testMatches(true, "-", "*.*.*.*") t.testMatches(true, "-0000000000000000efabffffffffffff", "00000000000000000000000000000000-0000000000000000efabffffffffffff") t.testMatches(true, "00000000000000000000000000000000-", "00000000000000000000000000000000-ffffffffffffffffffffffffffffffff") t.testMatches(true, "abfe0000000000000000000000000000-", "abfe0000000000000000000000000000-ffffffffffffffffffffffffffffffff") t.testMatches(true, "-0x0000000000000000efabffffffffffff", "00000000000000000000000000000000-0000000000000000efabffffffffffff") t.testMatches(true, "-0X0000000000000000efabffffffffffff", "00000000000000000000000000000000-0000000000000000efabffffffffffff") t.testMatches(true, "0x00000000000000000000000000000000-", "00000000000000000000000000000000-ffffffffffffffffffffffffffffffff") t.testMatches(true, "0xabcd0000000000000000000000000000-", "abcd0000000000000000000000000000-ffffffffffffffffffffffffffffffff") // these are the same addresses as the above tests in hex, but here in base 85 t.testMatches(true, ipaddr.AlternativeRangeSeparatorStr+"0000000000=l?k|EPzi+", "00000000000000000000"+ipaddr.AlternativeRangeSeparatorStr+"0000000000=l?k|EPzi+") t.testMatches(true, "00000000000000000000"+ipaddr.AlternativeRangeSeparatorStr, "00000000000000000000"+ipaddr.AlternativeRangeSeparatorStr+"=r54lj&NUUO~Hi%c2ym0") t.testMatches(true, "oBky9Vh_d)e!eUd#8280"+ipaddr.AlternativeRangeSeparatorStr, "oBky9Vh_d)e!eUd#8280"+ipaddr.AlternativeRangeSeparatorStr+"=r54lj&NUUO~Hi%c2ym0") t.testMatches(true, "*.*.*.*", "-4294967295") // ok on all tests t.testMatches(true, "*.*.*.*", "-0xffffffff") // ok on all tests t.testMatches(true, "*.*.*.*", "-037777777777") // ok on all tests t.testMatches(true, "*.*.*.*", "0-") t.testMatches(true, "*.*.*.*", "-") t.testMatches(true, "0.-", "0.*.*.*") t.testMatches(true, "0.-", "0.*") t.testMatches(true, "0.0.-", "0.0.*.*") t.testMatches(true, "0.0.-", "0.0.*") t.testMatches(true, "0.-.0", "0.*.0.0") //ok t.testMatches(true, "-.0.-", "*.0.*.*") // more than one inferred range t.testMatches(true, "-.0.-", "*.0.*") t.testMatches(true, "1-.0.256-", "1-255.0.256-65535") // 1-.0.256- becomes 1-255.0.*.255 // more than one inferred range t.testMatches(true, "0.1-.256-", "0.1-255.256-65535") // more than one inferred range t.testMatches(true, "1-.65536-", "1-255.65536-16777215") // test more than one inferred range t.testMatches(true, "0b1.0b01.0b101.0b11111111", "1.1.5.255") t.testMatches(true, "0b1.0b01.0b101.0b11111111/16", "1.1.5.255/16") t.testMatches(true, "0b1.1.0b101.0b11111111/16", "1.1.5.255/16") t.testMatches(true, "aaaabbbbccccddddeeeeffffaaaabbbb", "aaaa:bbbb:cccc:dddd:eeee:ffff:aaaa:bbbb") t.testMatches(true, "aaaabbbbcccccdddffffffffffffffff-aaaabbbbccccdddd0000000000000000", "aaaa:bbbb:cccc:cddd-dddd:*:*:*:*") t.testMatches(true, "aaaabbbbccccdddd0000000000000000-aaaabbbbcccccdddffffffffffffffff", "aaaa:bbbb:cccc:cddd-dddd:*:*:*:*") t.testMatches(true, "4)+k&C#VzJ4br>0wv%Yp", "1080::8:800:200c:417a") t.testMatches(true, "=r54lj&NUUO~Hi%c2ym0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") t.testMatches(true, "=r54lj&NUUO~Hi%c2yl0"+ipaddr.AlternativeRangeSeparatorStr+"=r54lj&NUUO~Hi%c2ym0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffaa-ffff") t.testMatches(true, "ef86:1dc3:deba:d48:612d:f19c:de7d:e89c", "********************") // base 85 t.testMatches(true, "--------------------", "f677:73f6:11b4:5073:4a06:76c2:ceae:1474") t.ipv6test(true, "0x00010002000300040000000000000000-0x0001000200030004ffffffffffffffff") t.ipv6test(true, "0x0001000200030004ffffffffffffffff-0x00010002000300040000000000000000") t.ipv6test(true, "0x00010002000300040000000000000000") t.ipv6test(true, "00010002000300040000000000000000-0001000200030004ffffffffffffffff") t.ipv6test(true, "0001000200030004ffffffffffffffff-00010002000300040000000000000000") t.ipv6test(true, "00010002000300040000000000000000") t.ipv6test(true, "00|M>t|ttwH6V6EEzblZ"+ipaddr.AlternativeRangeSeparatorStr+"00|M>t|ttwH6V6EEzkrZ") t.ipv6test(true, "00|M>t|ttwH6V6EEzkrZ"+ipaddr.AlternativeRangeSeparatorStr+"00|M>t|ttwH6V6EEzblZ") t.ipv6test(false, "00|M>t|ttwH6V6EEzkr"+ipaddr.AlternativeRangeSeparatorStr+"00|M>t|ttwH6V6EEzblZ") t.ipv6test(false, "00|M>t|ttwH6V6EEzkrZ"+ipaddr.AlternativeRangeSeparatorStr+"0|M>t|ttwH6V6EEzblZ") t.ipv6test(false, "00|M>t|ttwH6V6EEzkrZx"+ipaddr.AlternativeRangeSeparatorStr+"00|M>t|ttwH6V6EEzblZ") t.ipv6test(false, "00|M>t|ttwH6V6EEzkrZ"+ipaddr.AlternativeRangeSeparatorStr+"x00|M>t|ttwH6V6EEzblZ") t.ipv6test(true, "00000000000000000000000000000000-0001ffffffffffffffffffffffffffff") t.ipv6test(true, "=q{+M|w0(OeO5^F85=Cb") t.ipv6test(false, "=q{+M|w0.OeO5^F85=Cb") // . t.ipv6test(false, "=q{+:|w0(OeO5^F85=Cb") // : t.ipv6test(false, "=q{+M|w0(OeO5^F85=C/") // / in middle t.ipv6test(false, "=q{+M|w0(OeO5^F85=/b") // / in middle t.ipv6test(true, "=q{+M|w0(OeO5^F85=Cb/127") // ok t.ipv6test(true, "=q{+-|w0(OeO5^-85=Cb") // two '-' t.ipv6test(true, "=q{+M|w0(OeO5^F85=Cb"+ipaddr.IPv6AlternativeZoneSeparatorStr+"eth0") // ok t.ipv6test(false, "=q{+M|w0(OeO5^F85=C"+ipaddr.IPv6AlternativeZoneSeparatorStr+"eth0") // too soon t.testAllContains("*", "1:2:3:4:1:2:3:4", true) t.testAllContains("*", "1.2.3.4.5", false) t.testAllContains("*", "1.2.3.4", true) t.testAllContains("*/64", "1.2.3.4", false) t.testAllContains("*.*", "1::", false) t.testAllContains("*:*", "1::", true) t.testAllContains("*:*", "1.2.3.4", false) t.testAllContains("*.*", "1.2.3.4", true) t.testAllContains("*/64", "::", true) t.testNormalized("aaaabbbbcccccddd0000000000000000-aaaabbbbccccddddffffffffffffffff", "aaaa:bbbb:cccc:cddd-dddd:*:*:*:*") t.testCanonical("aaaabbbbcccccddd0000000000000000-aaaabbbbccccddddffffffffffffffff", "aaaa:bbbb:cccc:cddd-dddd:*:*:*:*") p0 := cacheTestBits(0) p1 := cacheTestBits(1) p15 := cacheTestBits(15) p16 := cacheTestBits(16) p32 := cacheTestBits(32) p64 := cacheTestBits(64) p89 := cacheTestBits(89) p126 := cacheTestBits(126) p128 := cacheTestBits(128) t.testSubnetStringRange2("*.0-65535", "0.0.0.0", "255.0.255.255", []interface{}{[]uint{0, 255}, []uint{0, 65535}}) // only valid with inet_aton allowed, and inet_aton takes precedence over wildcard t.testSubnetStringRange2("00000000000000000000000000000000-00000000000000000000007fffffffff", "::", "::7f:ffff:ffff", []interface{}{[]*big.Int{bigZeroConst(), setBigString("00000000000000000000007fffffffff", 16)}}) t.testSubnetStringRange2("00000000000000000000000000000000-00000000007fffffffffffffffffffff", "::", "::7f:ffff:ffff:ffff:ffff:ffff", []interface{}{[]*big.Int{bigZeroConst(), setBigString("00000000007fffffffffffffffffffff", 16)}}) t.testSubnetStringRange2("00000000000000000000000000000000-7fffffffffffffffffffffffffffffff", "::", "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", []interface{}{[]*big.Int{bigZeroConst(), setBigString("7fffffffffffffffffffffffffffffff", 16)}}) t.testSubnetStringRange2("00000000000000000000000000000000-ffffffffffffffffffffffffffffffff", "::", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", []interface{}{[]*big.Int{bigZeroConst(), setBigString("ffffffffffffffffffffffffffffffff", 16)}}) t.testSubnetStringRange2("0000000000000000000000000000abcd-0000000000000000000000000000bbcd", "::abcd", "::bbcd", []interface{}{[]uint{0xabcd, 0xbbcd}}) t.testMaskedIncompatibleAddress("*/f0ff::", "::", "f0ff::") t.testMaskedIncompatibleAddress("*/129.0.0.0", "0.0.0.0", "129.0.0.0") t.testMaskedIncompatibleAddress("*:*/f0ff::", "::", "f0ff::") t.testMaskedIncompatibleAddress("*.*/129.0.0.0", "0.0.0.0", "129.0.0.0") t.testIncompatibleAddress2("*.257-65535", "0.0.1.1", "255.0.255.255", []interface{}{[2]uint{0, 255}, [2]uint{257, 65535}}) //[0-255, 257-65535] t.testIncompatibleAddress2("1-1000", "1", "1000", []interface{}{[2]uint{1, 1000}}) //[1-1000] t.testIncompatibleAddress2("50000-60000", "50000", "60000", []interface{}{[2]uint{50000, 60000}}) //[50000-60000] t.testIncompatibleAddress2("*.11-16000111", "0.11", "255.16000111", []interface{}{[2]uint{0, 255}, [2]uint{11, 16000111}}) //[0-255, 11-16000111] t.testIncompatibleAddress2("0-255.11-16000111", "0.11", "255.16000111", []interface{}{[2]uint{0, 255}, [2]uint{11, 16000111}}) //[0-255, 11-16000111] // inet_aton t.testIncompatibleAddress2("0-254.10101-16000111", "0.10101", "254.16000111", []interface{}{[2]uint{0, 254}, [2]uint{10101, 16000111}}) // [0-254, 10101-16000111] // inet_aton t.testIncompatibleAddress2("1.10101-16000111", "1.10101", "1.16000111", []interface{}{1, [2]uint{10101, 16000111}}) //[1, 10101-16000111] // inet_aton t.testIncompatibleAddress2("3-1.10101-16000111", "1.10101", "3.16000111", []interface{}{[2]uint{1, 3}, [2]uint{10101, 16000111}}) //[1-3, 10101-16000111] // inet_aton t.testIncompatibleAddress2("00000000000000000000000000000000-abcdefabcdefabcdefabcdefabcdefab", "::", "abcd:efab:cdef:abcd:efab:cdef:abcd:efab", [2]*big.Int{bigZeroConst(), setBigString("abcdefabcdefabcdefabcdefabcdefab", 16)}) //[0-abcdefabcdefabcdefabcdefabcdefab] t.testIncompatibleAddress2("abcdefabcdefabcdefabcdefabcdefab-ffffffffffffffffffffffffffffffff", "abcd:efab:cdef:abcd:efab:cdef:abcd:efab", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", [2]*big.Int{setBigString("abcdefabcdefabcdefabcdefabcdefab", 16), setBigString("ffffffffffffffffffffffffffffffff", 16)}) //[abcdefabcdefabcdefabcdefabcdefab-ffffffffffffffffffffffffffffffff] t.testIncompatibleAddress2("abcdefabcdefabcdefabcdefabcdefab-bbcdefabcdefabcdefabcdefabcdefab", "abcd:efab:cdef:abcd:efab:cdef:abcd:efab", "bbcd:efab:cdef:abcd:efab:cdef:abcd:efab", [2]*big.Int{setBigString("abcdefabcdefabcdefabcdefabcdefab", 16), setBigString("bbcdefabcdefabcdefabcdefabcdefab", 16)}) //[abcdefabcdefabcdefabcdefabcdefab-bbcdefabcdefabcdefabcdefabcdefab] t.testIncompatibleAddress2("-abcdefabcdefabcdefabcdefabcdefab", "::", "abcd:efab:cdef:abcd:efab:cdef:abcd:efab", [2]*big.Int{bigZeroConst(), setBigString("abcdefabcdefabcdefabcdefabcdefab", 16)}) //[0-abcdefabcdefabcdefabcdefabcdefab] t.testIncompatibleAddress2("abcdefabcdefabcdefabcdefabcdefab-", "abcd:efab:cdef:abcd:efab:cdef:abcd:efab", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", [2]*big.Int{setBigString("abcdefabcdefabcdefabcdefabcdefab", 16), setBigString("ffffffffffffffffffffffffffffffff", 16)}) //[abcdefabcdefabcdefabcdefabcdefab-ffffffffffffffffffffffffffffffff] t.testIncompatibleAddress2("a:bb:c:dd:e:f:1.1-65535", "a:bb:c:dd:e:f:1.1", "a:bb:c:dd:e:f:1.65535", []interface{}{0xa, 0xbb, 0xc, 0xdd, 0xe, 0xf, 1, []uint{1, 0xffff}}) // mixed with inet_aton, mixed is incompatible address //[a, bb, c, dd, e, f, 1, 1-ffff] // with prefix lengths // inet_aton *.0.*.*/15 t.testSubnetStringRange("*.0-65535/15", "0.0.0.0", "255.0.255.255", []interface{}{[2]uint{0, 255}, [2]uint{0, 65535}}, p15) // only valid with inet_aton allowed, and inet_aton takes precedence over wildcard t.testSubnetStringRange("*.0-131071/15", "0.0.0.0", "255.1.255.255", []interface{}{[2]uint{0, 255}, [2]uint{0, 131071}}, p15) // only valid with inet_aton allowed, and inet_aton takes precedence over wildcard t.testSubnetStringRange("*.0.0-65535/15", "0.0.0.0", "255.0.255.255", []interface{}{[2]uint{0, 255}, 0, [2]uint{0, 65535}}, p15) t.testSubnetStringRange("*.0-1.0-65535/15", "0.0.0.0", "255.1.255.255", []interface{}{[2]uint{0, 255}, [2]uint{0, 1}, [2]uint{0, 65535}}, p15) t.testSubnetStringRange("00000000000000000000000000000000-00000000000000000000007fffffffff/89", "::", "::7f:ffff:ffff", []interface{}{[2]*big.Int{bigZeroConst(), setBigString("00000000000000000000007fffffffff", 16)}}, p89) t.testSubnetStringRange("00000000000000000000000000000000-00000000007fffffffffffffffffffff/89", "::", "::7f:ffff:ffff:ffff:ffff:ffff", []interface{}{[2]*big.Int{bigZeroConst(), setBigString("00000000007fffffffffffffffffffff", 16)}}, p89) t.testSubnetStringRange("00000000000000000000000000000000-7fffffffffffffffffffffffffffffff/0", "::", "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", []interface{}{[2]*big.Int{bigZeroConst(), setBigString("7fffffffffffffffffffffffffffffff", 16)}}, p0) t.testSubnetStringRange("00000000000000000000000000000000-7fffffffffffffffffffffffffffffff/1", "::", "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", []interface{}{[2]*big.Int{bigZeroConst(), setBigString("7fffffffffffffffffffffffffffffff", 17)}}, p1) t.testSubnetStringRange("00000000000000000000000000000000/1", "::", "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", []interface{}{[2]*big.Int{bigZeroConst(), setBigString("7fffffffffffffffffffffffffffffff", 17)}}, p1) t.testSubnetStringRange("00000000000000000000000000000000-7fffffffffffffffffffffffffffffff/1", "::", "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", []interface{}{[2]*big.Int{bigZeroConst(), setBigString("7fffffffffffffffffffffffffffffff", 16)}}, p1) t.testSubnetStringRange("0000000000000000000000000000abcd-0000000000000000000000000000bbcd/126", "::abcd", "::bbcd", []interface{}{[2]uint{0xabcd, 0xbbcd}}, p126) t.testSubnetStringRange("00000000000000000000000000000000/89", "::", "::7f:ffff:ffff", []interface{}{[2]*big.Int{bigZeroConst(), setBigString("00000000000000000000007fffffffff", 16)}}, p89) t.testIncompatibleAddress("*.11-16000111/32", "0.11", "255.16000111", []interface{}{[2]uint{0, 255}, [2]uint{11, 16000111}}, p32) //[0-255, 11-16000111] t.testIncompatibleAddress("*.257-65535/16", "0.0.1.1", "255.0.255.255", []interface{}{[2]uint{0, 255}, [2]uint{257, 65535}}, p16) //[0-255, 257-65535] t.testIncompatibleAddress("1-1000/16", "1", "1000", []interface{}{[2]uint{1, 1000}}, p16) //[1-1000] t.testIncompatibleAddress("50000-60000/16", "50000", "60000", []interface{}{[2]uint{50000, 60000}}, p16) //[50000-60000] t.testIncompatibleAddress("3-1.10101-16000111/16", "1.10101", "3.16000111", []interface{}{[2]uint{1, 3}, [2]uint{10101, 16000111}}, p16) //[1-3, 10101-16000111] // inet_aton t.testIncompatibleAddress("00000000000000000000000000000000-abcdefabcdefabcdefabcdefabcdefab/64", "::", "abcd:efab:cdef:abcd:efab:cdef:abcd:efab", [2]*big.Int{bigZeroConst(), setBigString("abcdefabcdefabcdefabcdefabcdefab", 16)}, p64) //[0-abcdefabcdefabcdefabcdefabcdefab] t.testIncompatibleAddress("abcdefabcdefabcdefabcdefabcdefab-ffffffffffffffffffffffffffffffff/64", "abcd:efab:cdef:abcd:efab:cdef:abcd:efab", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", [2]*big.Int{setBigString("abcdefabcdefabcdefabcdefabcdefab", 16), setBigString("ffffffffffffffffffffffffffffffff", 16)}, p64) //[abcdefabcdefabcdefabcdefabcdefab-ffffffffffffffffffffffffffffffff] t.testIncompatibleAddress("abcdefabcdefabcdefabcdefabcdefab-bbcdefabcdefabcdefabcdefabcdefab/64", "abcd:efab:cdef:abcd:efab:cdef:abcd:efab", "bbcd:efab:cdef:abcd:efab:cdef:abcd:efab", [2]*big.Int{setBigString("abcdefabcdefabcdefabcdefabcdefab", 16), setBigString("bbcdefabcdefabcdefabcdefabcdefab", 16)}, p64) //[abcdefabcdefabcdefabcdefabcdefab-bbcdefabcdefabcdefabcdefabcdefab] t.testIncompatibleAddress("-abcdefabcdefabcdefabcdefabcdefab/64", "::", "abcd:efab:cdef:abcd:efab:cdef:abcd:efab", [2]*big.Int{bigZeroConst(), setBigString("abcdefabcdefabcdefabcdefabcdefab", 16)}, p64) //[0-abcdefabcdefabcdefabcdefabcdefab] t.testIncompatibleAddress("abcdefabcdefabcdefabcdefabcdefab-/64", "abcd:efab:cdef:abcd:efab:cdef:abcd:efab", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", [2]*big.Int{setBigString("abcdefabcdefabcdefabcdefabcdefab", 16), setBigString("ffffffffffffffffffffffffffffffff", 16)}, p64) //[abcdefabcdefabcdefabcdefabcdefab-ffffffffffffffffffffffffffffffff] t.testIncompatibleAddress2("a:bb:c:dd:e:f:1.1-65535", "a:bb:c:dd:e:f:1.1", "a:bb:c:dd:e:f:1.65535", []interface{}{0xa, 0xbb, 0xc, 0xdd, 0xe, 0xf, 1, []uint{1, 0xffff}}) // mixed with inet_aton, mixed is incompatible address //[a, bb, c, dd, e, f, 1, 1-ffff] t.testMaskedIncompatibleAddress("1234567890abcdef1234567890abcdef-2234567890abcdef1234567890abcdef/ffff:0:ffff:0:ffff:0:ffff:0", "1234::", "2234:0:ffff:0:ffff:0:ffff:0") t.testSubnetStringRange1("1234567890abcdef1234567890abcdef-2234567890abcdef1234567890abcdef/::ffff:ffff:FFFF:ffff:FFFF", "00000000000000000000000000000000", "000000000000ffffffffffffffffffff", []interface{}{[]*big.Int{bigZeroConst(), setBigString("000000000000ffffffffffffffffffff", 16)}}, nil, true, ) t.testIncompatibleAddress1("1234567890abcdef1234567890abcdef-2234567890abcdef1234567890abcdef/ffff:ffff:ffff:ffff:ffff:FFFF:ffff:FFFF", "1234567890abcdef1234567890abcdef", "2234567890abcdef1234567890abcdef", []interface{}{[]*big.Int{setBigString("1234567890abcdef1234567890abcdef", 16), setBigString("2234567890abcdef1234567890abcdef", 16)}}, p128, true, ) t.testSubnetStringRange1("1234567890abcdef1234567890abcdef-2234567890abcdef1234567890abcdef/fff:ffff:ffff:ffff:ffff:FFFF:ffff:FFFF", "00000000000000000000000000000000", "0fffffffffffffffffffffffffffffff", []interface{}{[]*big.Int{bigZeroConst(), setBigString("0fffffffffffffffffffffffffffffff", 16)}}, nil, true, ) t.testMaskedIncompatibleAddress("1234567890abcdef1234567890abcdef-2234567890abcdef1234567890abcded/fff:ffff:ffff:ffff:ffff:FFFF:ffff:FFFF", "00000000000000000000000000000000", "0fffffffffffffffffffffffffffffff", ) t.testSubnetStringRange1("1234567890abcdef1234567890abcdef-2234567890abcdef2234567890abcdef/::ffff:ffff:FFFF:ffff:FFFF", "00000000000000000000000000000000", "000000000000ffffffffffffffffffff", []interface{}{[]*big.Int{bigZeroConst(), setBigString("000000000000ffffffffffffffffffff", 16)}}, nil, true) t.testSubnetStringRange1("1234567890abcdef1234567890abcdef-2234567890abcdef2234567890abcdef/::FFFF:ffff:FFFF", "00000000000000000000000000000000", "00000000000000000000ffffffffffff", []interface{}{[]*big.Int{bigZeroConst(), setBigString("00000000000000000000ffffffffffff", 16)}}, nil, true) t.testMaskedIncompatibleAddress("1234567890abcdef1234567890abcdef-2234567890abcdef2234567890abcdef/::FFFF:ffff:0000", "00000000000000000000000000000000", "00000000000000000000ffffffff0000") t.testIncompatibleAddress1("1234567890abcdef1234567890abcdef-2234567890abcdef1234567890abcdef/ffff:FFFF:ffff:FFFF::", "1234567890abcdef1234567890abcdef", "2234567890abcdef1234567890abcdef", []interface{}{[]*big.Int{setBigString("1234567890abcdef1234567890abcdef", 16), setBigString("2234567890abcdef1234567890abcdef", 16)}}, p64, true) //void testMaskedRange(long value, long upperValue, long maskValue, boolean expectedIsSequential, long expectedLower, long expectedUpper) { t.testMaskedRange(2, 5, 2, false, 0, 2) // for range 2 to 5, masking with 2 gives range 2 to 0, ie reverse the range, t.testMaskedRange(2, 5, 6, false, 2, 4) t.testMaskedRange(2, 5, 7, true, 2, 5) t.testMaskedRange(2, 5, 1, true, 0, 1) t.testMaskedRange(1, 3, 1, true, 0, 1) t.testMaskedRange(2, 5, 0, true, 0, 0) t.testMaskedRange(1, 3, 0, true, 0, 0) t.testMaskedRange(1, 511, 511, true, 1, 511) t.testMaskedRange(101, 612, 511, true, 0, 511) t.testMaskedRange(102, 612, 511, false, 0, 511) t.testMaskedRange(102, 611, 511, false, 0, 511) t.testMaskedRange(1024, 1535, 511, true, 0, 511) //0x400 to 0x5ff with mask t.testMaskedRange(1024, 1534, 511, true, 0, 510) t.testMaskedRange(1026, 1536, 511, false, 0, 511) t.testMaskedRange(1025, 1536, 511, true, 0, 511) t.testMaskedRange(1025, 1535, 511, true, 1, 511) t.testMaskedRange(0x400, 0x5ff, 0x1ff, true, 0, 0x1ff) //0x400 to 0x5ff with mask t.testMaskedRange(0x400, 0x5fe, 0x1ff, true, 0, 0x1fe) t.testMaskedRange(0x402, 0x600, 0x1ff, false, 0, 0x1ff) t.testMaskedRange(0x401, 0x600, 0x1ff, true, 0, 0x1ff) t.testMaskedRange(0x401, 0x5ff, 0x1ff, true, 1, 0x1ff) t.testMaskedRange(0x401, 0x5ff, 0, true, 0, 0) t.testMaskedRange(0x401, 0x5ff, 1, true, 0, 1) // these 5 essentially the same as above 5 but in the extended 8 bytes t.testMaskedRange(0x40000000000, 0x5ffffffffff, 0x1ffffffffff, true, 0, 0x1ffffffffff) t.testMaskedRange(0x40000000000, 0x5fffffffffe, 0x1ffffffffff, true, 0, 0x1fffffffffe) t.testMaskedRange(0x40000000002, 0x60000000000, 0x1ffffffffff, false, 0, 0x1ffffffffff) t.testMaskedRange(0x40000000001, 0x60000000000, 0x1ffffffffff, true, 0, 0x1ffffffffff) t.testMaskedRange(0x40000000001, 0x5ffffffffff, 0x1ffffffffff, true, 1, 0x1ffffffffff) // mask 0x1ff is 9 ones, 5ff is 10 followed by 9 ones, 0x400 is 10 followed by 9 zeros // ignoring the last 7 zeros, // this is equivalent to 1000 to 1010 masked by 11, so we clearly must use the highest value to get the masked highest value t.testMaskedRange(0x40000000000, 0x5ff00000000, 0x1ffffffffff, true, 0, 0x1ff00000000) t.testMaskedRange(0x40000000000, 0x5fe00000000, 0x1ffffffffff, true, 0, 0x1fe00000000) // now this is equivalent to 1000 to 10000 masked by 11, so we've now include the mask value in the range // 0x600 is 110 followed by 8 zeros // 0x400 is 100 followed by 8 zeros // 0x401 is 100 followed by 7 zeros and a 1 // 0x402 is 100 followed by 7 zeros and a 2 // 0x1ff is 001 followed by 8 ones // so we can get the lowest value by masking the top value 0x600 // and we need all values in between 0x600 and 0x601 to fill in the gap to 0x401 and make it sequential again t.testMaskedRange(0x40000000000, 0x60000000000, 0x1ffffffffff, true, 0, 0x1ffffffffff) t.testMaskedRange(0x40200000000, 0x60000000000, 0x1ffffffffff, false, 0, 0x1ffffffffff) t.testMaskedRange(0x40100000000, 0x60000000000, 0x1ffffffffff, false, 0, 0x1ffffffffff) t.testMaskedRange(0x40100000000, 0x600ffffffff, 0x1ffffffffff, true, 0, 0x1ffffffffff) t.testMaskedRange(0x40100000000, 0x5ff00000000, 0x1ffffffffff, true, 0x100000000, 0x1ff00000000) t.testMaskedRange(0x40100000000, 0x5ffffffffff, 0x1ffffffffff, true, 0x100000000, 0x1ffffffffff) t.testMaskedRange(0x400ffffffff, 0x5ffffffffff, 0x1ffffffffff, true, 0xffffffff, 0x1ffffffffff) t.testMaskedRangeExtended( 1, 0xcafe, // lower 1, 0xbadcafe, // upper 0x1ff, 0x10000000, // mask math.MaxUint64, 0x10000000000-1, // max true, //sequential 0, 0, // lower result 0x1ff, 0) // upper result t.testMaskedRangeExtended(1, 0xcafe, 1, 0xbadcafe, 0x1fe, 0x10000000, // mask math.MaxUint64, 0x10000000000-1, false, 0, 0, 0x1fe, 0) t.testMaskedRangeExtended(1, 0xcafe, 1, 0xbadcafe, math.MaxUint64, 0x10000000, // mask math.MaxUint64, 0x10000000000-1, true, 0, 0, math.MaxUint64, 0) t.testMaskedRangeExtended(1, 0xcafe, 1, 0xbadcafe, math.MaxUint64>>1, 0x10000000, // mask math.MaxUint64, 0x10000000000-1, true, 0, 0, math.MaxUint64>>1, 0) t.testMaskedRangeExtended(1, 0xcafe, 1, 0xbadcafe, 1, 0x10000000, // mask math.MaxUint64, 0x10000000000-1, true, 0, 0, 1, 0) t.testMaskedRangeExtended(1, 0xcafe, 1, 0xbadcafe, 0, 0x10000000, math.MaxUint64, 0x10000000000-1, true, 0, 0, 0, 0) t.testStrings() t.testBackAndForth() t.ipAddressRangeTester.run() } func (t ipAddressAllTester) testBackAndForth() { t.testBackAndForthIPv4("127.0.0.1") t.testBackAndForthIPv4("128.0.0.1") t.testBackAndForthIPv4("255.255.255.255") t.testBackAndForthIPv4("128.255.255.255") t.testBackAndForthIPv6("::1") t.testBackAndForthIPv6("8000::1") t.testBackAndForthIPv6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") t.testBackAndForthIPv6("ffff:a:b:c:d:e:f:cccc") t.testBackAndForthIPv6("cfff:a:b:c:d:e:f:cccc") t.testBackAndForthIPv6("7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") } func (t ipAddressAllTester) testBackAndForthIPv4(addrStr string) { // agnostic BigInteger and back addr := ipaddr.NewIPAddressString(addrStr).GetAddress() value := addr.GetValue() bigIntBytes := value.Bytes() byteCount := addr.GetByteCount() if len(bigIntBytes) < byteCount { // want correct byte length bytes := make([]byte, byteCount) copy(bytes[len(bytes)-len(bigIntBytes):], bigIntBytes) bigIntBytes = bytes } andAgain, _ := ipaddr.NewIPAddressFromNetIP(bigIntBytes) if !andAgain.Equal(addr) { t.addFailure(newIPAddrFailure("BigInteger result was "+andAgain.String()+" original was "+addr.String(), addr)) } // byte[] and back bytes := addr.Bytes() backAgain, _ := ipaddr.NewIPAddressFromNetIP(bytes) if !backAgain.Equal(addr) { t.addFailure(newIPAddrFailure("bytes result was "+backAgain.String()+" original was "+addr.String(), addr)) } // IPv4 int and back addrv4 := addr.ToIPv4() val := addrv4.Uint32Value() backAgainv4 := ipaddr.NewIPv4AddressFromUint32(val) if !backAgainv4.Equal(addrv4) { t.addFailure(newIPAddrFailure("int result was "+backAgainv4.String()+" original was "+addrv4.String(), addrv4.ToIP())) } } func (t ipAddressAllTester) testBackAndForthIPv6(addrStr string) { // agnostic BigInteger and back addr := ipaddr.NewIPAddressString(addrStr).GetAddress() value := addr.GetValue() bigIntBytes := value.Bytes() byteCount := addr.GetByteCount() if len(bigIntBytes) < byteCount { // want correct byte length bytes := make([]byte, byteCount) copy(bytes[len(bytes)-len(bigIntBytes):], bigIntBytes) bigIntBytes = bytes } andAgain, _ := ipaddr.NewIPAddressFromNetIP(bigIntBytes) if !andAgain.Equal(addr) { t.addFailure(newIPAddrFailure("BigInteger result was "+andAgain.String()+" original was "+addr.String(), addr)) } // byte[] and back bytes := addr.Bytes() backAgain, _ := ipaddr.NewIPAddressFromNetIP(bytes) if !backAgain.Equal(addr) { t.addFailure(newIPAddrFailure("bytes result was "+backAgain.String()+" original was "+addr.String(), addr)) } // IPv6 BigInteger and back addrv6 := addr.ToIPv6() value = addrv6.GetValue() backAgainv6, err := ipaddr.NewIPv6AddressFromInt(value) if err != nil { t.addFailure(newIPAddrFailure("got error creating from bytes "+value.String()+" err: "+err.Error(), addr)) } else if !backAgainv6.Equal(addrv6) { t.addFailure(newIPAddrFailure("int result was "+backAgainv6.String()+" original was "+addrv6.String(), addrv6.ToIP())) } } // tests the maskRange method and its counterpart that works with divs > 64 bits, maskExtendedRange func (t ipAddressAllTester) testMaskedRange(value, upperValue, maskValue uint64, expectedIsSequential bool, expectedLower, expectedUpper uint64) { masker := ipaddr.MaskRange(value, upperValue, maskValue, math.MaxUint64) lowerResult := masker.GetMaskedLower(value, maskValue) upperResult := masker.GetMaskedUpper(upperValue, maskValue) isSequential := masker.IsSequential() if isSequential != expectedIsSequential || lowerResult != expectedLower || upperResult != expectedUpper { reason := "" if lowerResult != expectedLower { reason += "lower mismatch " + strconv.FormatUint(lowerResult, 10) + "(" + strconv.FormatUint(lowerResult, 2) + ") with expected " + strconv.FormatUint(expectedLower, 10) + "(" + strconv.FormatUint(expectedLower, 2) + ") " } if upperResult != expectedUpper { reason += "upper mismatch " + strconv.FormatUint(upperResult, 10) + "(" + strconv.FormatUint(upperResult, 2) + ") with expected " + strconv.FormatUint(expectedUpper, 10) + "(" + strconv.FormatUint(expectedUpper, 2) + ") " } if isSequential != expectedIsSequential { reason += "sequential mismatch " } t.addFailure(newFailure("invalid masking, "+reason+ strconv.FormatUint(value, 10)+"("+strconv.FormatUint(value, 2)+")"+" to "+ strconv.FormatUint(upperValue, 10)+"("+strconv.FormatUint(upperValue, 2)+")"+" masked with "+ strconv.FormatUint(maskValue, 10)+"("+strconv.FormatUint(maskValue, 2)+")"+" results in "+ strconv.FormatUint(lowerResult, 10)+"("+strconv.FormatUint(lowerResult, 2)+")"+" lower and "+ strconv.FormatUint(upperResult, 10)+"("+strconv.FormatUint(upperResult, 2)+")"+" upper and sequential "+ strconv.FormatBool(isSequential)+" instead of expected "+ strconv.FormatUint(expectedLower, 10)+"("+strconv.FormatUint(expectedLower, 2)+")"+" lower and "+ strconv.FormatUint(expectedUpper, 10)+"("+strconv.FormatUint(expectedUpper, 2)+")"+" upper and sequential "+ strconv.FormatBool(expectedIsSequential), nil)) } t.incrementTestCount() t.testMaskedRangeExtended(value, 0, upperValue, 0, maskValue, 0, math.MaxUint64, math.MaxUint64, expectedIsSequential, expectedLower, 0, expectedUpper, 0) t.testMaskedRangeExtended(0, value, math.MaxUint64, upperValue, math.MaxUint64, maskValue, math.MaxUint64, math.MaxUint64, expectedIsSequential, 0, expectedLower, math.MaxUint64, expectedUpper) } func (t ipAddressAllTester) testMaskedRangeExtended(value, extendedValue, upperValue, extendedUpperValue, maskValue, extendedMaskValue, maxValue, extendedMaxValue uint64, expectedIsSequential bool, expectedLower, expectedExtendedLower, expectedUpper, expectedExtendedUpper uint64) { masker := ipaddr.MaskExtendedRange( value, extendedValue, upperValue, extendedUpperValue, maskValue, extendedMaskValue, maxValue, extendedMaxValue) lowerResult := masker.GetMaskedLower(value, maskValue) upperResult := masker.GetMaskedUpper(upperValue, maskValue) extendedLowerResult := masker.GetExtendedMaskedLower(extendedValue, extendedMaskValue) extendedUpperResult := masker.GetExtendedMaskedUpper(extendedUpperValue, extendedMaskValue) isSequential := masker.IsSequential() if masker.IsSequential() != expectedIsSequential || lowerResult != expectedLower || upperResult != expectedUpper || extendedLowerResult != expectedExtendedLower || extendedUpperResult != expectedExtendedUpper { reason := "" if lowerResult != expectedLower || extendedLowerResult != expectedExtendedLower { reason += "lower mismatch " } if upperResult != expectedUpper || extendedUpperResult != expectedExtendedUpper { reason += "upper mismatch " } if isSequential != expectedIsSequential { reason += "sequential mismatch " } t.addFailure(newFailure("invalid masking, "+reason, nil)) } t.incrementTestCount() } func (t ipAddressAllTester) testAllContains(cidr1, cidr2 string, result bool) { wstr := t.createAddress(cidr1) w2str := t.createAddress(cidr2) t.testStringContains(result, false, wstr, w2str) t.incrementTestCount() } func (t ipAddressAllTester) testStrings() { //It is good to have at least one base 85 input test, since we have code that caches base 85 input strings for output t.testIPv6Strings("4)+k&C#VzJ4br>0wv%Yp", "1080:0:0:0:8:800:200c:417a", //normalized "1080:0:0:0:8:800:200c:417a", //normalizedWildcards "1080::8:800:200c:417a", //canonicalWildcards "1080:0:0:0:8:800:200c:417a", //sql "1080:0000:0000:0000:0008:0800:200c:417a", "1080::8:800:200c:417a", //compressed "1080::8:800:200c:417a", "1080::8:800:200c:417a", //subnet "1080::8:800:200c:417a", //compressedWildcard "1080::8:800:32.12.65.122", //mixed no compress "1080::8:800:32.12.65.122", //mixedNoCompressHost "1080::8:800:32.12.65.122", "1080::8:800:32.12.65.122", "a.7.1.4.c.0.0.2.0.0.8.0.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.0.1.ip6.arpa", "1080-0-0-0-8-800-200c-417a.ipv6-literal.net", "4)+k&C#VzJ4br>0wv%Yp", "0x108000000000000000080800200c417a", "00204000000000000000000000100200004003040572") t.testIPv6Strings("008JOm8Mm5*yBppL!sg0", "0:ffff:ffff:ffff:ffff:ffff:ffff:ffff", //normalized "0:ffff:ffff:ffff:ffff:ffff:ffff:ffff", //normalizedWildcards "0:ffff:ffff:ffff:ffff:ffff:ffff:ffff", //canonicalWildcards "0:ffff:ffff:ffff:ffff:ffff:ffff:ffff", //sql "0000:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::ffff:ffff:ffff:ffff:ffff:ffff:ffff", //compressed "0:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::ffff:ffff:ffff:ffff:ffff:ffff:ffff", //subnet "::ffff:ffff:ffff:ffff:ffff:ffff:ffff", //compressedWildcard "::ffff:ffff:ffff:ffff:ffff:255.255.255.255", //mixed no compress "::ffff:ffff:ffff:ffff:ffff:255.255.255.255", //mixedNoCompressHost "::ffff:ffff:ffff:ffff:ffff:255.255.255.255", "::ffff:ffff:ffff:ffff:ffff:255.255.255.255", "f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.0.0.0.0.ip6.arpa", "0-ffff-ffff-ffff-ffff-ffff-ffff-ffff.ipv6-literal.net", "008JOm8Mm5*yBppL!sg0", "0x0000ffffffffffffffffffffffffffff", "00000017777777777777777777777777777777777777") t.testIPv6Strings("=r54lj&NUUO~Hi%c2ym0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", //normalized "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", //normalizedWildcards "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", //canonicalWildcards "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", //sql "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", //compressed "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", //subnet "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", //compressedWildcard "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255", //mixed no compress "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255", //mixedNoCompressHost "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255", "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255", "f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.ip6.arpa", "ffff-ffff-ffff-ffff-ffff-ffff-ffff-ffff.ipv6-literal.net", "=r54lj&NUUO~Hi%c2ym0", "0xffffffffffffffffffffffffffffffff", "03777777777777777777777777777777777777777777") } ipaddress-go-1.5.4/ipaddr/test/ipaddrrangetest.go000066400000000000000000006430651440250641600220530ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package test import ( "fmt" "math/big" "math/rand" "sort" "strconv" "strings" "time" "github.com/seancfoley/ipaddress-go/ipaddr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) type ipAddressRangeTester struct { ipAddressTester } func (t ipAddressRangeTester) run() { t.testIPv4Mapped("::0-1:ffff:c0a8:0a14", false) t.testIPv4Mapped("0:0:0:0:0-1:ffff:c0a8:0a14", false) t.testIPv4Mapped("::0-1:ffff:1.2.3.4", false) t.testIPv4Mapped("0:0:0:0:0-1:ffff:1.2.3.4", false) t.testEquivalentPrefix("*.*.*.*", 0) t.testEquivalentPrefix("0-127.*.*.*", 1) t.testEquivalentPrefix("128-255.*.*.*", 1) t.testEquivalentPrefix("*.*.*.*/1", 0) t.testEquivalentPrefix("0.*.*.*/1", 8) t.testEquivalentPrefix("128-255.*.*.*/1", 1) t.testEquivalentPrefix("1.2.*.*", 16) t.testEquivalentPrefix("1.2.*.*/24", 16) t.testEquivalentMinPrefix("1.2.*.0/24", cacheTestBits(16), 16) t.testEquivalentMinPrefix("1.2.0-255.0/24", cacheTestBits(16), 16) t.testEquivalentPrefix("1.2.1.0/24", 24) t.testEquivalentPrefix("1.2.1.*/24", 24) t.testEquivalentPrefix("1.2.1.*", 24) t.testEquivalentMinPrefix("1.2.*.4", nil, 32) t.testEquivalentPrefix("1.2.252-255.*", 22) t.testEquivalentPrefix("1.2.252-255.0-255", 22) t.testEquivalentPrefix("1.2.0-3.0-255", 22) t.testEquivalentPrefix("1.2.128-131.0-255", 22) t.testEquivalentMinPrefix("1.2.253-255.0-255", nil, 24) t.testEquivalentMinPrefix("1.2.252-255.0-254", nil, 32) t.testEquivalentMinPrefix("1.2.251-255.0-254", nil, 32) t.testEquivalentMinPrefix("1.2.251-255.0-255", nil, 24) t.testEquivalentMinPrefix("1.2.1-3.*", nil, 24) t.testEquivalentPrefix("1.2.0-3.*", 22) t.testEquivalentPrefix("*:*", 0) t.testEquivalentPrefix("::/0", 0) t.testEquivalentMinPrefix("0-1::/0", nil, 128) t.testEquivalentPrefix("::/1", 1) t.testEquivalentMinPrefix("0-1::/1", nil, 128) t.testEquivalentMinPrefix("8000-ffff::/1", nil, 128) t.testEquivalentPrefix("8000-ffff:*", 1) t.testEquivalentMinPrefix("7fff-ffff:*", nil, 16) t.testEquivalentMinPrefix("7fff-ffff:*/1", nil, 16) t.testEquivalentPrefix("11:8000-ffff:*/1", 17) t.testEquivalentPrefix("11:8000-ffff:*", 17) t.testEquivalentPrefix("1:2:*", 32) t.testEquivalentMinPrefix("1:2:*:*::/64", cacheTestBits(32), 32) t.testEquivalentPrefix("1:2:*:*/64", 32) t.testEquivalentPrefix("1:2:3:4:5:*:*/64", 80) t.testEquivalentMinPrefix("1:2:*::/64", nil, 64) t.testEquivalentMinPrefix("1:2:*::", nil, 128) t.testEquivalentPrefix("1:2:8000-ffff:*", 33) t.testEquivalentPrefix("1:2:0000-7fff:*", 33) t.testEquivalentPrefix("1:2:c000-ffff:*", 34) t.testEquivalentPrefix("1:2:0000-3fff:*", 34) t.testEquivalentPrefix("1:2:8000-bfff:*", 34) t.testEquivalentPrefix("1:2:4000-7fff:*", 34) t.testEquivalentPrefix("1:2:fffc-ffff:*", 46) t.testEquivalentPrefix("1:2:fffc-ffff:0-ffff:*", 46) t.testEquivalentMinPrefix("1:2:fffd-ffff:0-ffff:*", nil, 48) t.testEquivalentMinPrefix("1:2:fffc-ffff:0-fffe:*", nil, 64) t.testEquivalentMinPrefix("1:2:fffb-ffff:0-fffe:*", nil, 64) t.testEquivalentMinPrefix("1:2:fffb-ffff:0-ffff:*", nil, 48) t.testTrees() t.testStrings() t.testReverse("1:2:*:4:5:6:a:b", false, false) t.testReverse("1:1:1:1-fffe:2:3:3:3", false, false) // 0x1-0xfffe reverseBitsPerByte throws t.testReverse("1-fffe:0-ffff:0-ffff:0-fffe:1-ffff:1-ffff:1-fffe:1-ffff", false, true) // all reversible t.testReverse("1-fffe:0-ffff:1-ffff:0-fffe:0-fffe:1-ffff:0-ffff:1-fffe", true, true) // all reversible t.testReverse("1:1:1:0-fffe:1-fffe:*:1:1", false, false) // 100-feff or aa01-aafe are byte reversible becoming 100-feff and xx01-xxfe where x is reverse of a t.testReverse("ffff:80:*:ff:01:ffff", false, false) t.testReverse("ffff:8000:fffe::7fff:0001:ffff", true, false) t.testReverse("ffff:8000:*:8000:1:*:01:ffff", true, false) t.testReverse("ffff:8118:ffff:*:1-fffe:ffff", false, true) t.testReverse("ffff:8181:c3c3::4224:2400:0-fffe", false, true) t.testReverse("ffff:1:ff:ff:*:*", false, false) t.testPrefixes("255.127.0.0/16", 16, -5, "255.127.0.0/24", "255.0.0.0/8", "255.96.*.*/11", "255.127.0.0/16", "255.127.0.0/16") t.testPrefixes("255.127.0.0/17", 16, -17, "255.127.0.0/24", "255.127.0.0/16", "0.0.0-127.*/0", "255.127.0-127.*/16", "255.127.0.0/16") t.testPrefixes("ffff:ffff:1:ffff::/64", 16, -5, "ffff:ffff:1:ffff::/80", "ffff:ffff:1::/48", "ffff:ffff:1:ffe0:*:*:*:*/59", "ffff::*:*:*:*/16", "ffff::/16") t.testPrefixes("ffff:ffff:1:ffff::/64", 16, 1, "ffff:ffff:1:ffff::/80", "ffff:ffff:1::/48", "ffff:ffff:1:ffff::/65", "ffff::*:*:*:*/16", "ffff::/16") var bc0, bc7, bc8, bc15, bc16, bc32 ipaddr.BitCount bc0, bc7, bc8, bc15, bc16, bc32 = 0, 7, 8, 15, 16, 32 t.testBitwiseOr("1.2.0.0/16", &bc8, "0.0.3.248", "1.2.3.248-255") t.testBitwiseOr("1.2.0.0/16", &bc7, "0.0.2.0", "1.2.2-3.*") t.testBitwiseOr("1.2.*.*", &bc7, "0.0.3.0", "") t.testBitwiseOr("1.2.0-3.*", &bc0, "0.0.3.0", "1.2.3.*") t.testBitwiseOr("1.2.0.0/16", &bc7, "0.0.3.0", "1.2.3.*") t.testBitwiseOr("0.0.0.0/0", &bc0, "128.192.224.240", "128-255.192-255.224-255.240-255") t.testBitwiseOr("*.*", &bc0, "128.192.224.240", "128-255.192-255.224-255.240-255") t.testBitwiseOr("0.0.0.0/0", &bc0, "128.192.224.64", "") t.testBitwiseOr("*.*", &bc0, "128.192.224.64", "") t.testPrefixBitwiseOr("1.3.0.0/15", 24, "0.0.255.1", "1.3.255.0", "1.3.255.1/15") t.testPrefixBitwiseOr("1.3.0.1/15", 24, "0.0.255.1", "1.3.255.1/24", "1.3.255.1/15") t.testPrefixBitwiseOr("1.3.0.1/15", 24, "0.0.255.0", "1.3.255.1/24", "1.3.255.1/15") t.testPrefixBitwiseOr("1.2.0.0/22", 24, "0.0.3.248", "1.2.3.0/24", "1.2.3.248-255/22") t.testPrefixBitwiseOr("1.2.0.0/24", 24, "0.0.3.248", "1.2.3.0/24", "1.2.3.248-255/24") t.testPrefixBitwiseOr("1.2.0.0/22", 23, "0.0.3.0", "1.2.2.0/23", "1.2.3.0-255/22") t.testPrefixBitwiseOr("1.2.0.0/24", 23, "0.0.3.0", "1.2.2.*", "1.2.3.0-255/24") t.testPrefixBitwiseOr("1:2::/46", 47, "0:0:3::", "1:2:2::/47", "1:2:3:*:*:*:*:*/46") t.testPrefixBitwiseOr("0.0.0.0/16", 18, "0.0.2.8", "0.0.0-192.0/18", "") t.testBitwiseOr("1:2::/32", &bc16, "0:0:3:fff8::", "1:2:3:fff8-ffff:*") t.testBitwiseOr("1:2::/32", &bc15, "0:0:2::", "1:2:2-3:*") t.testBitwiseOr("1:2:*", &bc0, "0:0:8000::", "1:2:8000-ffff:*") t.testBitwiseOr("1:2:*", &bc0, "0:0:c000::", "1:2:c000-ffff:*") t.testBitwiseOr("1:2::/32", &bc15, "0:0:3::", "1:2:3:*") t.testBitwiseOr("::/0", &bc0, "8000:c000:e000:fff0::", "8000-ffff:c000-ffff:e000-ffff:fff0-ffff:*") t.testBitwiseOr("*:*", &bc0, "8000:c000:e000:fff0::", "8000-ffff:c000-ffff:e000-ffff:fff0-ffff:*") t.testBitwiseOr("::/0", &bc0, "8000:c000:e000:4000::", "") t.testBitwiseOr("1:1::/16", &bc32, "0:2:3::ffff", "1:2:3::ffff") //note the prefix length is dropped to become "1.2.3.*", but equality still holds t.testBitwiseOr("1:1:0:*:0/16", nil, "0:2:3:*:ffff", "1:3:3:*:*:*:*:ffff") //note the prefix length is dropped to become "1.2.3.*", but equality still holds t.testBitwiseOr("1:0:0:1::/16", &bc32, "0:2:3::ffff", "1:2:3:1::ffff") //note the prefix length is dropped to become "1.2.3.*", but equality still holds t.testPrefixBitwiseOr("::/32", 34, "0:0:2:8::", "0:0:0-c000::/34", "") t.testDelimitedCount("1,2-3,4:3:4,5:6:7:8:ffff:ffff", 8) t.testDelimitedCount("1,2::3,6:7:8:4,5-6:6,8", 16) t.testDelimitedCount("1:2:3:*:4::5", 1) t.testDelimitedCount("1:2,3,*:3:ffff:ffff:6:4:5,ff,7,8,99", 15) t.testDelimitedCount("0,1-2,3,5:3::6:4:5,ffff,7,8,99", 30) t.testMatches(true, "1.2.3.4/16", "1.2.3.4") t.testMatches(true, "1.2.3.4/15", "1.2.3.4") t.testMatches(true, "1.2.3.4/17", "1.2.3.4") t.testMatches(true, "1.2.0.4/16", "1.2.0.4") t.testMatches(true, "1.2.3.0/16", "1.2.3.0") t.testMatches(true, "1.2.3.4/14", "1.2.3.4") t.testMatches(true, "1.2.0.4/14", "1.2.0.4") t.testMatches(true, "1.2.0.0/14", "1.2.0.0") t.testMatches(true, "1.0.3.0/14", "1.0.3.0") t.testMatches(true, "1.2.0.0/16", "1.2.*.*") t.testMatches(true, "1.2.0.0/16", "1.2.*") t.testMatches(true, "1.4.0.0/14", "1.4-7.*") t.testMatches(true, "1.4.0.0/14", "1.4-7.*.*") t.testMatches(false, "1.2.3.4/16", "1.2.*/255.255.0.0") t.testMatches(false, "1.2.3.4/15", "1.2.3.*/255.254.0.0") t.testMatches(false, "1.2.3.4/17", "1.2.3.*/255.255.128.0") t.testMatches(true, "1.2.0.0/16", "1.2.*/255.255.0.0") t.testMatches(true, "1.2.3.*/15", "1.2.3.*/255.254.0.0") t.testMatches(true, "1.2.3.*/17", "1.2.3.*/255.255.128.0") t.testMatches(false, "1.1.3.4/15", "1.2.3.*/255.254.0.0") t.testMatches(false, "1.1.3.4/17", "1.2.3.*/255.255.128.0") t.testMatches(true, "1:2::/32", "1:2:*:*:*:*:*:*") t.testMatches(true, "1:2::/32", "1:2:*:*:*:*:*.*.*.*") t.testMatches(true, "1:2::/32", "1:2:*") t.testMatches(false, "1:2::/32", "1:2:*:*:*:*:3:*") t.testMatches(false, "1:2::/32", "1:2:*:*:*:*:*.*.3.*") t.testMatches(false, "1:2::/31", "1:2:*") t.testMatches(false, "1:2::/33", "1:2::*") t.testMatches(true, "1:2::/32", "1:2:*:*:*:*:*:*/ffff:ffff::") t.testMatches(true, "1:2::/31", "1:2-3:*:*:*:*:*:*/ffff:fffe::") t.testMatches(true, "1:2::/33", "1:2:0-7fff:*:*:*:*:*/ffff:ffff:8000::") t.testMatches(false, "1:2::/24", "1:__:*") t.testMatches(false, "1:2::/28", "1:_::/32") t.testMatches(false, "1:2::/20", "1:___::/32") t.testMatches(false, "1:2::/16", "1:____::/32") t.testMatches(false, "1:ffef::/24", "1:ff__::/32") t.testMatches(false, "1:ffef::/24", "1:ff__:*:*") t.testMatches(true, "1::/24", "1:__:*") t.testMatches(true, "1::/28", "1:_::/32") t.testMatches(true, "1::/20", "1:___::/32") t.testMatches(true, "1::/16", "1:____::/32") t.testMatches(true, "1:ff00::/24", "1:ff__::/32") t.testMatches(true, "1:ff00::/24", "1:ff__:*:*") t.testMatches(true, "250-255.200-255.0-255.20-29", "25_.2__.___.2_") t.testMatches(true, "150-159.100-199.0-99.10-19", "15_.1__.__.1_") t.testMatches(false, "251-255.200-255.0-255.20-29", "25_.2__.___.2_") t.testMatches(false, "150-158.100-199.0-99.10-19", "15_.1__.__.1_") t.testMatches(true, "250-25f:200-2ff:0-fff:20-2f::", "25_:2__:___:2_::") t.testMatches(true, "150-15f:100-1ff:0-ff:10-1f::", "15_:1__:__:1_::") t.testMatches(false, "250-25f:201-2ff:0-fff:20-2f::", "25_:2__:___:2_::") t.testMatches(false, "150-15f:100-1ef:0-ff:10-1f::", "15_:1__:__:1_::") t.testMatches(true, "::250-25f:200-2ff:0-fff:20-2f", "::25_:2__:___:2_") t.testMatches(true, "::150-15f:100-1ff:0-ff:10-1f", "::15_:1__:__:1_") t.testMatches(true, "250-25f:200-2ff::0-fff:20-2f", "25_:2__::___:2_") t.testMatches(true, "150-15f:100-1ff::0-ff:10-1f", "15_:1__::__:1_") t.testMatches(true, "1:2:3:4:5:6:1-2.*.0.4", "1:2:3:4:5:6:100-2ff:4") // mixed starting with range t.testMatches(true, "1:2:3:4:5:6:1.2.0.4-5", "1:2:3:4:5:6:102:4-5") // mixed ending with range t.testMatches(true, "1:2:3:4:5:6:1.2.0.*", "1:2:3:4:5:6:102:0-ff") // mixed ending with range t.testMatches(true, "1:2:3:4:5:6:1.2.0._", "1:2:3:4:5:6:102:0-9") // mixed ending with range t.testMatches(true, "1:2:3:4:5:6:1.2.0.1_", "1:2:3:4:5:6:102:a-13") // mixed ending with range t.testMatches(true, "1:2:3:4:5:6:1.2.0.4-5", "1:2:3:4:5:6:102:5-4") // mixed ending with range t.testMatches(true, "1:2:3:4:5:6:1.2.0.4-5", "1:2:3:4:5:6:1.2.0.5-4") // mixed ending with range t.testMatches(true, "1:2:3:4:5:6:1.2-255.0.4-5", "1:2:3:4:5:6:1.255-2.0.5-4") // mixed ending with range t.testMatches(false, "1:2:3:4:5:6:1-3.2.0.4-5", "1:2:3:4:5:6:3-1.2.0.5-4") // inet.ipaddr.IncompatibleAddressException: 1-3, 2, IP Address error: IPv4 segment ranges cannot be converted to IPv6 segment ranges t.testMatches(true, "1:2:3:4:5:6:1-3.*.0.4-5", "1:2:3:4:5:6:3-1.*.0.5-4") t.testMatches(true, "1:2:3:4:5:6:1-3.*.0.4-5", "1:2:3:4:5:6:3ff-100:5-4") t.testMatches(true, "1.2.2-3.4", "1.2.3-2.4") t.testMatches(true, "1.255-2.2-3.4", "1.2-255.3-2.4") t.testMatches(true, "1:2:3:4:5:6:7:7-8", "1:2:3:4:5:6:7:8-7") t.testMatches(true, "1-ffff:2:3:4:5:6:7:7-8", "ffff-1:2:3:4:5:6:7:8-7") t.testMatches(true, "1-ffff:2:3:4:aa-5:6:7:7-8", "ffff-1:2:3:4:5-aa:6:7:8-7") t.testMatches(true, "1.2.*.4", "1.2.255-0.4") t.testMatches(true, "1:2:3:4:5:*:7:7-8", "1:2:3:4:5:ffff-0:7:8-7") t.testMatchesInetAton(true, "1.2.3", "1.2.0.3", true) t.testMatchesInetAton(true, "1.2.2-3.4", "0x1.0x2.2-0x3.0x4", true) t.testMatchesInetAton(true, "1.2.2-3.4", "0x1.0x2.0x2-0x3.0x4", true) t.testMatchesInetAton(true, "1.2.2-3.4", "0x1.0x2.0x2-3.0x4", true) t.testMatchesInetAton(true, "1.2.2-3.4", "01.02.2-03.04", true) t.testMatchesInetAton(true, "1.2.2-3.4", "01.02.2-3.04", true) t.testMatchesInetAton(true, "1.2.2-3.4", "01.02.02-03.04", true) t.testMatchesInetAton(true, "1.2.2-3.4", "01.02.0x2-03.04", true) t.testMatchesInetAton(true, "1.2.2-3.4", "01.02.0x2-0x3.04", true) t.testMatchesInetAton(true, "1.2.0200-0277.4", "01.02.02__.04", true) t.testMatchesInetAton(true, "1.2.0x20-0x2f.4", "01.02.0x2_.04", true) t.testMatchesInetAton(true, "1.2.0x10-0x1f.4", "01.02.0x1_.04", true) t.testMatchesInetAton(true, "1.2.*.4", "01.02.0x__.04", true) t.testMatchesInetAton(true, "1.2.0-077.4", "01.02.0__.04", true) t.testMatchesInetAton(true, "1.2.2-3.4", "01.02.0x2-0x3.04", true) t.testMatchesInetAton(true, "0.0.0-1.4", "00.0x0.0x00-0x000001.04", true) t.testMatchesInetAton(true, "11.10-11.10-11.10-11", "11.012-0xb.0xa-013.012-0xB", true) t.testMatchesInetAton(true, "11.10-11.*.10-11", "11.012-0xb.0x0-0xff.012-0xB", true) t.testMatchesInetAton(true, "1.*", "1.*.*.0x0-0xff", true) t.testMatchesInetAton(true, "1.*", "1.0-255.0-65535", true) t.testMatchesInetAton(true, "1.*", "1.0-0xff.0-0xffff", true) t.testMatchesInetAton(true, "1.*", "1.0x0-0xff.00-0xffff", true) t.testMatchesInetAton(true, "11.11.0-11.*", "11.11.0-0xbff", true) t.testMatchesInetAton(true, "11.0.0.11-11", "11.0x00000000000000000b-0000000000000000000013", true) t.testMatchesInetAton(true, "11.1-11.*/16", "11.0x10000-786431/16", true) t.testMatchesInetAton(true, "11.1-11.*/16", "11.0x10000-0xbffff/16", true) t.testMatches(true, "1:2:3:4:5:6:1.2.3.4/128", "1:2:3:4:5:6:102:304") t.testMatches(false, "1:2:3:4:5:6:1.2.3.4/96", "1:2:3:4:5:6:*:*") t.testMatches(false, "1:2:3:4:5:6:255.2.3.4/97", "1:2:3:4:5:6:8000-ffff:*") t.testMatches(false, "1:2:3:4:5:6:1.2.3.4/112", "1:2:3:4:5:6:102:*") t.testMatches(false, "1:2:3:4:5:6:1.2.255.4/115", "1:2:3:4:5:6:102:e000-ffff") t.testMatches(false, "1.2.3.4/0", "*.*") t.testMatches(false, "1.2.3.4/0", "*.*.*.*") t.testMatches(false, "1:2:3:4:5:6:7:8/0", "*:*") t.testMatches(false, "1:2:3:4:5:6:7:8/0", "*:*:*:*:*:*:*:*") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4/96", "1:2:3:4:5:6:102:304") t.testMatches(true, "1:2:3:4:5:6:255.2.3.4/97", "1:2:3:4:5:6:ff02:304") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4/112", "1:2:3:4:5:6:102:304") t.testMatches(true, "1:2:3:4:5:6:1.2.255.4/115", "1:2:3:4:5:6:102:ff04") t.testMatches(true, "1.2.3.4/0", "1.2.3.4") t.testMatches(true, "1.2.3.4/0", "1.2.3.4") t.testMatches(true, "1:2:3:4:5:6:7:8/0", "1:2:3:4:5:6:7:8") t.testMatches(true, "1:2:3:4:5:6:7:8/0", "1:2:3:4:5:6:7:8") t.testMatches(true, "1:2:3:4:5:6:0.0.0.0/96", "1:2:3:4:5:6:*:*") t.testMatches(false, "1:2:3:4:5:6:255.0.0.0/97", "1:2:3:4:5:6:8000-ffff:*") t.testMatches(true, "1:2:3:4:5:6:255.0.0.0/97", "1:2:3:4:5:6:ff00:0") t.testMatches(true, "1:2:3:4:5:6:128.0.0.0/97", "1:2:3:4:5:6:8000-ffff:*") t.testMatches(true, "1:2:3:4:5:6:1.2.0.0/112", "1:2:3:4:5:6:102:*") t.testMatches(false, "1:2:3:4:5:6:1.2.255.0/115", "1:2:3:4:5:6:102:e000-ffff") t.testMatches(true, "1:2:3:4:5:6:1.2.255.0/115", "1:2:3:4:5:6:102:FF00") t.testMatches(true, "1:2:3:4:5:6:1.2.224.0/115", "1:2:3:4:5:6:102:e000-ffff") t.testMatches(true, "0.0.0.0/0", "*.*") t.testMatches(true, "0.0.0.0/0", "*.*.*.*") t.testMatches(true, "::/0", "*:*") t.testMatches(true, "::/0", "*:*:*:*:*:*:*:*") t.testMatches(true, "1-02.03-4.05-06.07", "1-2.3-4.5-6.7") t.testMatches(true, "1-002.003-4.005-006.007", "1-2.3-4.5-6.7") t.testMatches(true, "1-2.0-0.00-00.00-0", "1-2.0.0.0") t.testMatches(true, "1-2:0-0:00-00:00-0:0-000:0000-0000:0000-00:0000-0", "1-2:0:0:0:0:0:0:0") t.testMatches(true, "00-0.0-0.00-00.00-0", "0.0.0.0") t.testMatches(true, "0-00:0-0:00-00:00-0:0-000:0000-0000:0000-00:0000-0", "::") t.testMatches(true, "-1.22.33.4", "0-1.22.33.4") t.testMatches(true, "-1.22.33.4", "0-1.22.33.4") t.testMatches(true, "22.1-.33.4", "22.1-255.33.4") t.testMatches(true, "22.33.4.1-", "22.33.4.1-255") t.testMatches(true, "aa:-1:cc::d:ee:f", "aa:0-1:cc::d:ee:f") t.testMatches(true, "aa:dd-:cc::d:ee:f", "aa:dd-ffff:cc::d:ee:f") t.testMatches(true, "aa:dd-:cc::d:ee:f-", "aa:dd-ffff:cc::d:ee:f-ffff") t.testMatches(true, "-:0:0:0:0:0:0:0", "0-ffff:0:0:0:0:0:0:0") t.testMatches(true, "0-:0:0:0:0:0:0:0", "-ffff:0:0:0:0:0:0:0") t.testMatches(true, "ffff:0:0:0:0:0:0:0", "ffff-:0:0:0:0:0:0:0") t.testMatches(true, "-0:0:0:0:0:0:0:0", "::") t.testMatches(true, "0:0:-:0:0:0:0:0", "0:0:0-ffff:0:0:0:0:0") t.testMatches(true, "0:0:0-:0:0:0:0:0", "0:0:-ffff:0:0:0:0:0") t.testMatches(true, "0:0:ffff:0:0:0:0:0", "0:0:ffff-:0:0:0:0:0") t.testMatches(true, "0:0:-0:0:0:0:0:0", "::") t.testMatches(true, "0:-:0:0:0:0:0:0", "0:0-ffff:0:0:0:0:0:0") t.testMatches(true, "0:0-:0:0:0:0:0:0", "0:-ffff:0:0:0:0:0:0") t.testMatches(true, "0:ffff:0:0:0:0:0:0", "0:ffff-:0:0:0:0:0:0") t.testMatches(true, "0:-0:0:0:0:0:0:0", "::") t.testMatches(true, "::1:0:0:0.0.0.0", "0:0:0:1::0.0.0.0") t.testMatches(true, "1::-1:16", "1::0-1:16") t.testMatches(true, "1:-1::16/32", "1:0-1::16") t.testMatches(true, "1:-1::16", "1:0-1::16/32") t.testMatches(true, "0.0.0.-", "0.0.0.*") // ok t.testMatches(true, "1-.0.0.1-", "1-255.0.0.1-255") // ok // more than one inferred range t.testMatches(true, "0b1.0b01.0b101.1-0b11111111", "1.1.5.1-255") t.testMatches(true, "0b1.0b01.0b101.0b11110000-0b11111111", "1.1.5.240-255") t.testMatches(true, "0b1.0b01.0b101.0b1111____", "1.1.5.240-255") t.testMatches(true, "::0b0000111100001111-0b1111000011110000:3", "::f0f-f0f0:3") t.testMatches(true, "::0b000011110000____:3", "::f00-f0f:3") t.testMatches(true, "::0B000011110000____:3", "::f00-f0f:3") t.testMatches(true, "::f0f-0b1111000011110000:3", "::f0f-f0f0:3") t.testMatches(true, "::0b0000111100001111-f0f0:3", "::f0f-f0f0:3") t.testMatches(true, "::0B0000111100001111-f0f0:3", "::f0f-f0f0:3") allowsIPv4PrefixBeyondAddressSize := t.createAddress("1.2.3.4").GetValidationOptions().GetIPv4Params().AllowsPrefixesBeyondAddressSize() allowsIPv6PrefixBeyondAddressSize := t.createAddress("::1").GetValidationOptions().GetIPv6Params().AllowsPrefixesBeyondAddressSize() t.ipv4test(true, "1.2.*.4/1") t.ipv4test(false, "1.2.*.4/-1") t.ipv4test(false, "1.2.*.4/") t.ipv4test(false, "1.2.*.4/x") t.ipv4test(allowsIPv4PrefixBeyondAddressSize, "1.2.*.4/33") t.ipv6test(true, "1:*::1/1") t.ipv6test(false, "1:*::1/-1") t.ipv6test(false, "1:*::1/") t.ipv6test(false, "1:*::1/x") t.ipv6test(allowsIPv6PrefixBeyondAddressSize, "1:*::1/129") //masks that have wildcards in them t.ipv4test(false, "1.2.3.4/*") t.ipv4test(false, "1.2.*.4/*") t.ipv4test(false, "1.2.3.4/1-2.2.3.4") t.ipv4test(false, "1.2.*.4/1-2.2.3.4") t.ipv4test(false, "1.2.3.4/**") t.ipv4test(false, "1.2.*.4/**") t.ipv4test(false, "1.2.3.4/*.*") t.ipv4test(false, "1.2.*.4/*.*") t.ipv4test(false, "1.2.3.4/*:*") t.ipv4test(false, "1.2.*.4/*:*") t.ipv4test(false, "1.2.3.4/*:*:*:*:*:*:*:*") t.ipv4test(false, "1.2.*.4/*:*:*:*:*:*:*:*") t.ipv4test(false, "1.2.3.4/1.2.*.4") t.ipv4test(false, "1.2.*.4/1.2.*.4") t.ipv4test(true, "1.2.*.4/1.2.3.4") t.ipv6test(false, "1:2::1/*") t.ipv6test(false, "1:*::1/*") t.ipv6test(false, "1:2::1/1:1-2:3:4:5:6:7:8") t.ipv6test(false, "1:*::1/1:1-2:3:4:5:6:7:8") t.ipv6test(false, "1:2::1/**") t.ipv6test(false, "1:*::1/**") t.ipv6test(false, "1:2::1/*:*") t.ipv6test(false, "1:*::1/*:*") t.ipv6test(false, "1:2::1/*.*") t.ipv6test(false, "1:*::1/*.*") t.ipv6test(false, "1:2::1/*.*.*.*") t.ipv6test(false, "1:*::1/*.*.*.*") t.ipv6test(false, "1:2::1/1:*::2") t.ipv6test(false, "1:*::1/1:*::2") t.ipv6test(true, "1:*::1/1::2") t.ipv4rangetest(true, "1.1.*.100-101", addrstrparam.WildcardAndRange) t.ipv4rangetest(true, "1.2.*.101-100", addrstrparam.WildcardAndRange) //downwards range t.ipv4rangetest(false, "1.2.*.1010-100", addrstrparam.WildcardAndRange) //downwards range t.ipv4rangetest(true, "1.2.*.101-101", addrstrparam.WildcardAndRange) t.ipv6rangetest(true, "1:2:f4:a-ff:0-2::1", addrstrparam.WildcardAndRange) t.ipv6rangetest(true, "1:2:4:ff-a:0-2::1", addrstrparam.WildcardAndRange) //downwards range t.ipv6rangetest(false, "1:2:4:ff1ff-a:0-2::1", addrstrparam.WildcardAndRange) //downwards range t.ipv4rangetest(true, "1.2.*.101-100/24", addrstrparam.WildcardAndRange) //downwards range but covered CIDR //these tests create strings that validate ipv4 and ipv6 differently, allowing ranges for one and not the other t.ipv4rangestest(true, "1.*.3.4", addrstrparam.WildcardAndRange, addrstrparam.NoRange) t.ipv4rangestest(false, "1.*.3.4", addrstrparam.NoRange, addrstrparam.WildcardAndRange) t.ipv6rangestest(false, "a:*::1.*.3.4", addrstrparam.WildcardAndRange, addrstrparam.NoRange) t.ipv6rangestest(true, "a:*::1.*.3.4", addrstrparam.NoRange, addrstrparam.WildcardAndRange) t.ipv6rangestest(false, "a:*::", addrstrparam.WildcardAndRange, addrstrparam.NoRange) t.ipv6rangestest(true, "a:*::", addrstrparam.NoRange, addrstrparam.WildcardAndRange) // octal, hex, dec overflow // do it with 1, 2, 3, 4 segments t.ipv4_inet_aton_test(true, "0.0.0.1-255") t.ipv4_inet_aton_test(false, "0.0.0.1-256") t.ipv4_inet_aton_test(true, "0.0.512-65535") t.ipv4_inet_aton_test(false, "0.0.512-65536") t.ipv4_inet_aton_test(true, "0.65536-16777215") t.ipv4_inet_aton_test(false, "0.65536-16777216") t.ipv4_inet_aton_test(true, "16777216-4294967295") t.ipv4_inet_aton_test(true, "0b00000001000000000000000000000000-4294967295") t.ipv4_inet_aton_test(false, "16777216-4294967296") t.ipv4_inet_aton_test(false, "0.0.0.0x1x") t.ipv4_inet_aton_test(false, "0.0.0.1x") t.ipv4_inet_aton_test(true, "0.0.0.0x1-0xff") t.ipv4_inet_aton_test(false, "0.0.0.0x1-0x100") t.ipv4_inet_aton_test(true, "0.0.0xfffe-0xffff") t.ipv4_inet_aton_test(false, "0.0.0xfffe-0x10000") t.ipv4_inet_aton_test(false, "0.0.0x10000-0x10001") t.ipv4_inet_aton_test(true, "0.0-0xffffff") t.ipv4_inet_aton_test(false, "0.0-0x1000000") t.ipv4_inet_aton_test(true, "0x11000000-0xffffffff") t.ipv4_inet_aton_test(false, "0x11000000-0x100000000") t.ipv4_inet_aton_test(false, "0x100000000-0x100ffffff") t.ipv4_inet_aton_test(true, "0.0.0.00-0377") t.ipv4_inet_aton_test(false, "0.0.0.00-0400") t.ipv4_inet_aton_test(true, "0.0.0x100-017777") t.ipv4_inet_aton_test(false, "0.0.0x100-0200000") t.ipv4_inet_aton_test(true, "0.0x10000-077777777") t.ipv4_inet_aton_test(false, "0.0x10000-0100000000") t.ipv4_inet_aton_test(true, "0x1000000-03777777777") t.ipv4_inet_aton_test(true, "0x1000000-037777777777") t.ipv4_inet_aton_test(true, "0x1000000-0b11111111111111111111111111111111") //[0-1, 0, 0-255, 0-255] t.ipv4_inet_aton_test(false, "0x1000000-040000000000") t.ipv4test(true, "*") //toAddress() should not work on this, toAddress(Version) should. t.ipv4test2(false, "*%", false, true) //because the string could represent ipv6, and we are allowing zone, we treat the % as ipv6 zone, and then we invalidate because no zone for ipv4 t.ipv4test2(false, "*%x", false, true) //no zone for ipv4 t.ipv4test(true, "**") // toAddress() should not work on this, toAddress(Version) should. t.ipv6test(true, "**") // toAddress() should not work on this, toAddress(Version) should. t.ipv6test(true, "*%x") //ipv6 which allows zone t.ipv4test(true, "*.*.*.*") //toAddress() should work on this t.ipv4test(true, "1.*.3") t.ipv4test(false, "a.*.3.4") t.ipv4test(false, "*.a.3.4") t.ipv4test(false, "1.*.a.4") t.ipv4test(false, "1.*.3.a") t.ipv4test(false, ".2.3.*") t.ipv4test(false, "1..*.4") t.ipv4test(false, "1.*..4") t.ipv4test(false, "*.2.3.") t.ipv4test(false, "256.*.3.4") t.ipv4test(false, "1.256.*.4") t.ipv4test(false, "*.2.256.4") t.ipv4test(false, "1.*.3.256") t.ipv4test(true, "0.0.*.0") t.ipv4test(true, "00.*.0.0") t.ipv4test(true, "0.00.*.0") t.ipv4test(true, "0.*.00.0") t.ipv4test(true, "*.0.0.00") t.ipv4test(true, "000.0.*.0") t.ipv4test(true, "0.000.0.*") t.ipv4test(true, "*.0.000.0") t.ipv4test(true, "0.0.*.000") t.ipv4test(true, "0.0.*.0") t.ipv4test(true, "00.*.0.0") t.ipv4test(true, "0.00.*.0") t.ipv4test(true, "0.*.00.0") t.ipv4test(true, "*.0.0.00") t.ipv4test(true, "000.0.*.0") t.ipv4test(true, "0.000.0.*") t.ipv4test(true, "*.0.000.0") t.ipv4test(true, "0.0.*.000") t.ipv4test(true, "000.000.000.*") t.ipv4test(t.isLenient(), "0000.0.*.0") t.ipv4test(t.isLenient(), "*.0000.0.0") t.ipv4test(t.isLenient(), "0.*.0000.0") t.ipv4test(t.isLenient(), "*.0.0.0000") t.ipv4test(false, ".0.*.0") t.ipv4test(false, "0..*.0") t.ipv4test(false, "0.*..0") t.ipv4test(false, "*.0.0.") t.ipv4test(true, "1.*.3.4/255.1.0.0") t.ipv4test(false, "1.*.3.4/255.1.0.0/16") t.ipv4test(false, "1.*.3.4/255.*.0.0") //range in mask t.ipv4test(false, "1.*.3.4/255.1-2.0.0") //range in mask t.ipv4test(false, "1.*.3.4/1::1") //mask mismatch t.ipv6test(false, "1:*::/1.2.3.4") //mask mismatch t.ipv4test(false, "1.2.3.4/255.*.0.0") //range in mask t.ipv4test(false, "1.2.3.4/255.1-2.0.0") //range in mask t.ipv6test(false, "1:2::/1:*::") //range in mask t.ipv6test(false, "1:2::/1:1-2::") //range in mask t.ipv4testOnly(false, "1:2:3:4:5:*:7:8") //fixed t.ipv4testOnly(false, "*::1") //fixed t.ipv6test(true, "*") //toAddress() should not work on this, toAddress(version) should t.ipv6test(true, "*%") //toAddress() should not work on this, toAddress(version) should t.ipv6test(true, "*:*:*:*:*:*:*:*") //toAddress() should work on this t.ipv6test(true, "*::1") // loopback, compressed, non-routable t.ipv4test(true, "1.0-0.3.0") t.ipv4test(true, "1.0-3.3.0") t.ipv4test(true, "1.1-3.3.0") t.ipv4test(true, "1-8.1-3.2-4.0-5") t.ipv6test(true, "1:0-0:2:0::") t.ipv6test(true, "1:0-3:2:0::") t.ipv6test(true, "1:1-3:2:0::") t.ipv6test(true, "1-fff:1-3:2-4:0-5::") t.ipv6test(false, "-:0:0:0:0:0:0:0:0") t.ipv6test(true, "-:0:0:0:0:0:0:0") // this is actually equivalent to 0-ffff:0:0:0:0:0:0:0 or 0-:0:0:0:0:0:0:0 or -ffff:0:0:0:0:0:0:0 t.ipv6test(false, "-:0:0:0:0:0:0") t.ipv6test(false, "-:0:0:0:0:0") t.ipv6test(false, "-:0:0:0:0") t.ipv6test(false, "-:0:0:0") t.ipv6test(false, "-:0:0") t.ipv6test(false, "-:0") t.ipv6test(false, ":-0:0:0:0:0:0:0") t.ipv6test(false, ":-0:0:0:0:0:0") t.ipv6test(false, ":-0:0:0:0:0") t.ipv6test(false, ":-0:0:0:0") t.ipv6test(false, ":-0:0:0") t.ipv6test(false, ":-0:0") t.ipv6test(false, ":-0") t.ipv6test(false, "-:1:1:1:1:1:1:1:1") t.ipv6test(true, "-:1:1:1:1:1:1:1") // this is actually equivalent to 0-ffff:0:0:0:0:0:0:0 or 0-:0:0:0:0:0:0:0 or -ffff:0:0:0:0:0:0:0 t.ipv6test(false, "-:1:1:1:1:1:1") t.ipv6test(false, "-:1:1:1:1:1") t.ipv6test(false, "-:1:1:1:1") t.ipv6test(false, "-:1:1:1") t.ipv6test(false, "-:1:1") t.ipv6test(false, "-:1") t.ipv6test(false, ":-1:1:1:1:1:1:1") t.ipv6test(false, ":-1:1:1:1:1:1") t.ipv6test(false, ":-1:1:1:1:1") t.ipv6test(false, ":-1:1:1:1") t.ipv6test(false, ":-1:1:1") t.ipv6test(false, ":-1:1") t.ipv6test(false, ":-1") t.ipv6test(true, "::*") // unspecified, compressed, non-routable t.ipv6test(true, "0:0:*:0:0:0:0:1") // loopback, full t.ipv6test(true, "0:0:*:0:0:0:0:0") // unspecified, full t.ipv6test(true, "2001:*:0:0:8:800:200C:417A") // unicast, full t.ipv6test(true, "FF01:*:0:0:0:0:0:101") // multicast, full t.ipv6test(true, "2001:DB8::8:800:200C:*") // unicast, compressed t.ipv6test(true, "FF01::*:101") // multicast, compressed t.ipv6test(false, "2001:DB8:0:0:8:*:200C:417A:221") // unicast, full t.ipv6test(false, "FF01::101::*") // multicast, compressed t.ipv6test(true, "fe80::217:f2ff:*:ed62") t.ipv6test(true, "2001:*:1234:0000:0000:C1C0:ABCD:0876") t.ipv6test(true, "3ffe:0b00:0000:0000:0001:0000:*:000a") t.ipv6test(true, "FF02:0000:0000:0000:0000:0000:*:0001") t.ipv6test(true, "*:0000:0000:0000:0000:0000:0000:0001") t.ipv6zerotest(false, "0000:0000:0000:0000:*0000:0000:0000:*0") t.ipv6test(t.isLenient(), "02001:*:1234:0000:0000:C1C0:ABCD:0876") // extra 0 not allowed! t.ipv6test(false, "2001:0000:1234:0000:0*:C1C0:ABCD:0876") // extra 0 not allowed! t.ipv6test(true, "2001:0000:1234:0000:*:C1C0:ABCD:0876") t.ipv6test(true, " 2001:0000:1234:0000:0000:C1C0:ABCD:0876") // leading space t.ipv6test(true, "2001:0000:1234:0000:0000:C1C0:ABCD:0876 ") // trailing space t.ipv6test(true, " 2001:0000:1234:0000:0000:C1C0:ABCD:0876 ") // leading and trailing space t.ipv6test(false, "2001:0000:1234:0000:0000:C1C0*:ABCD:0876 0") // junk after valid address t.ipv6test(false, "0 2001:0000:123*:0000:0000:C1C0:ABCD:0876") // junk before valid address t.ipv6test(false, "2001:0000:1234: 0000:0000:C1C0:*:0876") // internal space t.ipv6test(true, "3ffe:0b00:*:0001:0000:0000:000a") t.ipv6test(false, "3ffe:0b00:1:0001:0000:0000:000a") // seven segments t.ipv6test(false, "FF02:0000:0000:0000:0000:0000:0000:*:0001") // nine segments t.ipv6test(false, "3ffe:*::1::a") // double "::" t.ipv6test(false, "::1111:2222:3333:4444:5555:*::") // double "::" t.ipv6test(true, "2::10") t.ipv6test(true, "ff02::1") t.ipv6test(true, "fe80:*::") t.ipv6test(true, "2002:*::") t.ipv6test(true, "2001:*::") t.ipv6test(true, "*:0db8:1234::") t.ipv6test(true, "::ffff:*:0") t.ipv6test(true, "*::1") t.ipv6test(true, "1:2:3:4:*:6:7:8") t.ipv6test(true, "1:2:*:4:5:6::8") t.ipv6test(true, "1:2:3:4:5::*") t.ipv6test(true, "1:2:3:*::8") t.ipv6test(true, "1:2:3::8") t.ipv6test(true, "*:2::8") t.ipv6test(true, "1::*") t.ipv6test(true, "*::2:3:4:5:6:7") t.ipv6test(true, "*::2:3:4:5:6") t.ipv6test(true, "1::2:3:4:*") t.ipv6test(true, "1::2:*:4") t.ipv6test(true, "1::*:3") t.ipv6test(true, "1::*") t.ipv6test(true, "::*:3:4:5:6:7:8") t.ipv6test(true, "*::2:3:4:5:6:7") t.ipv6test(true, "::*:3:4:5:6") t.ipv6test(true, "::*:3:4:5") t.ipv6test(true, "::2:3:*") t.ipv6test(true, "*::2:3") t.ipv6test(true, "::*") t.ipv6test(true, "1:*:3:4:5:6::") t.ipv6test(true, "1:2:3:4:*::") t.ipv6test(true, "1:2:3:*::") t.ipv6test(true, "1:2:3::*") t.ipv6test(true, "*:2::") t.ipv6test(true, "*::") t.ipv6test(true, "*:2:3:4:5::7:8") t.ipv6test(false, "1:2:3::4:5::7:*") // Double "::" t.ipv6test(false, "12345::6:7:*") t.ipv6test(true, "1:2:3:4::*:*") t.ipv6test(true, "1:*:3::7:8") t.ipv6test(true, "*:*::7:8") t.ipv6test(true, "*::*:8") // Testing IPv4 addresses represented as dotted-quads // Leading zero's in IPv4 addresses not allowed: some systems treat the leading "0" in ".086" as the start of an octal number // Update: The BNF in RFC 3986 explicitly defines the dec-octet (for IPv4 addresses) not to have a leading zero //t.ipv6test(false,"fe80:0000:0000:*:0204:61ff:254.157.241.086"); t.ipv6test(!t.isLenient(), "fe80:0000:0000:*:0204:61ff:254.157.241.086") t.ipv6test(true, "::*:192.0.128.*") t.ipv6test(false, "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4") t.ipv6test(true, "1111:2222:*:4444:5555:6666:00.00.00.00") t.ipv6test(true, "1111:2222:3333:4444:5555:6666:000.*.000.000") t.ipv6test(false, "*:2222:3333:4444:5555:6666:256.256.256.256") t.ipv6test(true, "*:2222:3333:4444:5555:6666:123.123.123.123") t.ipv6test(true, "1111:*:3333:4444:5555::123.123.123.123") t.ipv6test(true, "1111:2222:*:4444::123.123.123.123") t.ipv6test(true, "1111:2222:3333::*.*.123.123") t.ipv6test(true, "1111:2222::123.123.*.*") t.ipv6test(true, "1111:2222::123.123.123.*") t.ipv6test(true, "1111::123.*.123.123") t.ipv6test(true, "::123.123.123.*") t.ipv6test(true, "1111:2222:3333:4444::*:123.123.123.123") t.ipv6test(true, "1111:2222:*::6666:123.123.123.123") t.ipv6test(true, "*:2222::6666:123.123.123.123") t.ipv6test(true, "1111::6666:*.*.*.*") t.ipv6test(true, "::6666:123.123.2.123") t.ipv6test(true, "1111:*:3333::5555:6666:123.*.123.123") t.ipv6test(true, "1111:2222::*:6666:123.123.*.*") t.ipv6test(true, "1111::*:6666:*.*.123.123") t.ipv6test(true, "1111::*:6666:*.0-255.123.123") //1111::*:6666:*.123.123 t.ipv6test(true, "::5555:6666:123.123.123.123") t.ipv6test(true, "1111:2222::4444:5555:*:123.123.123.123") t.ipv6test(true, "1111::4444:5555:6666:123.*.123.123") t.ipv6test(true, "*::4444:5555:6666:123.123.123.123") t.ipv6test(true, "1111::*:4444:5555:6666:123.123.123.123") t.ipv6test(true, "::2222:*:4444:5555:6666:123.123.123.123") t.ipv6test(true, "::*:*:*:*:*:*.*.*.*") t.ipv6test(true, "*::*:*:*:*:*.*.*.*") t.ipv6test(false, "*:::*:*:*:*.*.*.*") t.ipv6test(false, "*:::*:*:*:*:*.*.*.*") t.ipv6test(true, "*::*:*:*:*:*.*.*.*") t.ipv6test(false, "*::*:*:*:*:*:*.*.*.*") t.ipv6test(false, "*:*:*:*:*:*:*:*:*.*.*.*") t.ipv6test(false, "*:*:*:*:*:*:*::*.*.*.*") t.ipv6test(false, "*:*:*:*:*:*::*:*.*.*.*") t.ipv6test(true, "*:*:*:*:*:*:*.*.*.*") t.ipv6test(true, "*:*:*:*:*::*.*.*.*") t.ipv6test(true, "*:*:*:*::*:*.*.*.*") t.ipv6test(true, "::*") t.ipv6test(true, "*:0:0:0:0:0:0:*") // Additional cases: http://crisp.tweakblogs.net/blog/2031/ipv6-validation-%28and-caveats%29.html t.ipv6test(true, "0:a:b:*:d:e:f::") t.ipv6test(true, "::0:a:*:*:d:e:f") // syntactically correct, but bad form (::0:... could be combined) t.ipv6test(true, "a:b:c:*:*:f:0::") t.ipv6test(false, "':10.*.0.1") t.ipv4test(true, "1.*.4") t.ipv4test(true, "1.2.*") t.ipv4test(true, "*.1") t.ipv4test(true, "1.*") t.ipv4test(true, "1.*.1") t.ipv4test(true, "1.*.*") t.ipv4test(true, "*.*.1") t.ipv4test(true, "*.1.*") t.ipv4test(t.isLenient(), "1") t.ipv4test(t.isLenient(), "1.1") t.ipv4test(t.isLenient(), "1.1.1") t.ipv4test(true, "*.1.2.*") t.ipv4test(true, "*.1.*.2") t.ipv4test(true, "*.*.*.2") t.ipv4test(true, "*.*.*.*") t.ipv4test(true, "1.*.2.*") t.ipv4test(true, "1.2.*.*") t.ipv4test(true, "*.*") t.ipv6test(true, "1::1.2.*") t.ipv6test(true, "1::1.2.**") t.ipv6test(false, "1::1.2.**z") t.ipv6test(true, "1::1.2.3.4") t.ipv6test(true, "1:*:1") t.ipv4test(true, "1.2.*") t.ipv4test(false, "%.%") t.ipv6test(false, "1::1.2.%") t.ipv6test(true, "1::1.2.*%") t.ipv6test(true, "1::1.2.*%z") t.ipv6test(false, "1:%:1") t.ipv6test(true, "1::%-.1") t.ipv6test(true, "1::%-.1/16") //that is a zone of "-." and a prefix of 16 t.ipv6test(true, "1::%-1/16") //that is a zone of "-" and a prefix of 16 t.ipv6test(true, "1::-1:16") //that is just an address with a ranged segment 0-1 t.ipv6test(true, "1::%-.1-16") // -.1-16 is the zone t.ipv6test(true, "1::%-.1/16") //we treat /16 as prefix length t.ipv6test(false, "1::%-.1:16") //we reject ':' as part of zone t.ipv6test(false, "1::%-.1/1a") //prefix has 'a' t.ipv6test(false, "1::%-1/1a") //prefix has 'a' t.ipv6test(true, "1::%%1") //zone has '%' t.ipv6test(true, "1::%%1/16") //zone has '%' t.ipv6test(true, "1::%%ab") //zone has '%' t.ipv6test(true, "1::%%ab/16") //zone has '%' t.ipv6test(true, "1::%$1") //zone has '$' t.ipv6test(true, "1::%$1/16") //zone has '$' t.ipv4test(true, "1.2.%") //we allow this now, the % is seen as a wildcard because we are ipv4 - if we allow zone and the string can be interpreted as ipv6 then % is seen as zone character t.ipv6test(true, "1:*") t.ipv6test(true, "*:1:*") t.ipv6test(true, "*:1") //t.ipv6test(true, "*:1:1.*.1");//cannot be converted to ipv6 range t.ipv6test(true, "*:1:1.*.*") //t.ipv6test(true, "*:1:*.1");//cannot be converted to ipv6 range t.ipv6test(true, "*:1:*.0-255.1.1") t.ipv6test(true, "*:1:1.*") t.ipv6test(false, "1:1:1.*.1") t.ipv6test(false, "1:1:1.*.1.1") t.ipv6test(true, "1:1:*.*") t.ipv6test(true, "1:2:3:4:5:*.*") t.ipv6test(true, "1:2:3:4:5:6:*.*") t.ipv6test(false, "1:1:1.*") t.ipv6test(true, "1::1:1.*.*") t.ipv6test(true, "1::1:*.*.1.1") t.ipv6test(true, "1::1:1.*") t.ipv6test(true, "1:*.*.*.*") //in this one, the wildcard covers both ipv6 and ipv4 parts t.ipv6test(true, "1::*.*.*.*") t.ipv6test(true, "1:*.*.1.2") //in this one, the wildcard covers both ipv6 and ipv4 parts t.ipv6test(true, "1::*.*.1.2") //compression takes precedence so the wildcard does not cover both ipv6 and ipv4 parts t.ipv6test(true, "1::2:*.*.1.2") //compression takes precedence so the wildcard does not cover both ipv6 and ipv4 parts t.ipv6test(true, "::2:*.*.1.2") //compression takes precedence so the wildcard does not cover both ipv6 and ipv4 parts t.ipv6test(false, "1:1.*.2") t.ipv6test(false, "1:1.*.2.2") t.ipv6test(t.isLenient(), "1:*:1.2") t.ipv6test(true, "*:1:1.*") t.ipv6test(t.isLenient(), "*:1:1.2.3") t.ipv6test(true, "::1:1.*") t.ipv6test(t.isLenient(), "::1:1.2.3") t.ipv6test(true, "1:*:1") t.ipv6test(true, "1:*:1:1.1.*") t.ipv6test(true, "1:*:1:1.1.*.*") t.ipv6test(true, "1:*:1:*") t.ipv6test(true, "1:*:1:*.*.1.2") t.ipv6test(true, "1:*:1:1.*") t.ipv6test(t.isLenient(), "1:*:1:1.2.3") t.ipv6test(false, "1:*:1:2:3:4:5:6:7") t.ipv6test(false, "1:*:1:2:3:4:5:1.2.3.4") t.ipv6test(true, "1:*:2:3:4:5:1.2.3.4") t.ipv6test(false, "1:*:2:3:4:5:1.2.3.4.5") t.ipv6test(false, "1:1:2:3:4:5:1.2.3.4.5") t.ipv6test(false, "1:1:2:3:4:5:6:1.2.3.4") t.ipv6test(false, "1:1:2:3:4:5:6:1.*.3.4") t.ipv6test(true, "1:2:3:4:5:6:1.2.3.4") t.ipv6test(true, "1:2:3:4:5:6:1.*.3.4") t.ipv4test(true, "255._.3.4") t.ipv4test(true, "1.255._.4") t.ipv4test(true, "_.2.255.4") t.ipv4test(true, "1._.3.255") t.ipv4test(true, "255.__.3.4") t.ipv4test(true, "1.255.__.4") t.ipv4test(true, "__.2.255.4") t.ipv4test(true, "1.__.3.255") t.ipv4test(true, "255.___.3.4") t.ipv4test(true, "1.255.___.4") t.ipv4test(true, "___.2.255.4") t.ipv4test(true, "1.___.3.255") t.ipv4test(t.isLenient(), "255.____.3.4") t.ipv4test(t.isLenient(), "1.255.____.4") t.ipv4test(t.isLenient(), "____.2.255.4") t.ipv4test(t.isLenient(), "1.____.3.255") t.ipv4test(false, "255._2_.3.4") t.ipv4test(false, "1.255._2_.4") t.ipv4test(false, "_2_.2.255.4") t.ipv4test(false, "1._2_.3.255") t.ipv4test(true, "255.2__.3.4") t.ipv4test(true, "1.255.2__.4") t.ipv4test(true, "2__.2.255.4") t.ipv4test(true, "1.2__.3.255") t.ipv4test(true, "255.2_.3.4") t.ipv4test(true, "1.255.2_.4") t.ipv4test(true, "2_.2.255.4") t.ipv4test(true, "1.2_.3.255") t.ipv4test(false, "255.__2.3.4") t.ipv4test(false, "1.255.__2.4") t.ipv4test(false, "__2.2.255.4") t.ipv4test(false, "1.__2.3.255") t.ipv4test(true, "25_.__.3.4") t.ipv4test(true, "1.255.2__._") t.ipv4test(true, "2_.2_.255.__") t.ipv4test(false, "1.2__.3__.25_") t.ipv4test(true, "1.2__.3_.25_") t.ipv4test(true, "1.2__.2__.25_") t.ipv4test(false, "1.1--2.1.1") t.ipv4test(false, "1.1-2-3.1.1") t.ipv4test(false, "1.1-2-.1.1") t.ipv4test(false, "1.-1-2.1.1") t.ipv4test(false, "1.1_2_.1.1") t.ipv4test(false, "1.1_2.1.1") t.ipv4test(true, "1.1_.1.1") t.ipv4test(false, "1.1_-2.1.1") t.ipv4test(false, "1.1-2_.1.1") t.ipv4test(false, "1.1*-2.1.1") t.ipv4test(false, "1.1-2*.1.1") t.ipv4test(false, "1.*1-2.1.1") t.ipv4test(false, "1.1-*2.1.1") t.ipv4test(false, "1.*-2.1.1") t.ipv4test(false, "1.1-*.1.1") t.ipv6test(false, "1:1--2:1:1::") t.ipv6test(false, "1:1-2-3:1:1::") t.ipv6test(false, "1:1-2-:1:1::") t.ipv6test(false, "1:-1-2:1:1::") t.ipv6test(false, "1:1_2_:1.1::") t.ipv6test(false, "1:1_2:1:1::") t.ipv6test(true, "1:1_:1:1::") t.ipv6test(false, "1:1_-2:1:1::") t.ipv6test(false, "1:1-2_:1:1::") t.ipv6test(false, "1:1-_2:1:1::") t.ipv6test(false, "1:1*-2:1:1::") t.ipv6test(false, "1:1-2*:1:1::") t.ipv6test(false, "1:*-2:1:1::") t.ipv6test(false, "1:1-*:1:1::") t.ipv6test(false, "1:*1-2:1:1::") t.ipv6test(false, "1:1-*2:1:1::") //double - // _4_ single char wildcards not in trailing position t.ipv6test(true, "::ffff:_:0") t.ipv6test(true, "_::1") t.ipv6test(true, "1:2:3:4:_:6:7:8") t.ipv6test(true, "1:2:_:4:5:6::8") t.ipv6test(true, "1:2:3:4:5::_") t.ipv6test(true, "1:2:3:_::8") t.ipv6test(true, "_:2::8") t.ipv6test(true, "1::_") t.ipv6test(true, "_::2:3:4:5:6:7") t.ipv6test(true, "_::2:3:4:5:6") t.ipv6test(true, "1::2:3:4:_") t.ipv6test(true, "1::2:_:4") t.ipv6test(true, "1::_:3") t.ipv6test(true, "1::_") t.ipv6test(true, "::ffff:__:0") t.ipv6test(true, "__::1") t.ipv6test(true, "1:2:3:4:__:6:7:8") t.ipv6test(true, "1:2:__:4:5:6::8") t.ipv6test(true, "1:2:3:4:5::__") t.ipv6test(true, "1:2:3:__::8") t.ipv6test(true, "__:2::8") t.ipv6test(true, "1::__") t.ipv6test(true, "__::2:3:4:5:6:7") t.ipv6test(true, "__::2:3:4:5:6") t.ipv6test(true, "1::2:3:4:__") t.ipv6test(true, "1::2:__:4") t.ipv6test(true, "1::__:3") t.ipv6test(true, "1::__") t.ipv6test(true, "::ffff:___:0") t.ipv6test(true, "___::1") t.ipv6test(true, "1:2:3:4:___:6:7:8") t.ipv6test(true, "1:2:___:4:5:6::8") t.ipv6test(true, "1:2:3:4:5::___") t.ipv6test(true, "1:2:3:___::8") t.ipv6test(true, "___:2::8") t.ipv6test(true, "1::___") t.ipv6test(true, "___::2:3:4:5:6:7") t.ipv6test(true, "___::2:3:4:5:6") t.ipv6test(true, "1::2:3:4:___") t.ipv6test(true, "1::2:___:4") t.ipv6test(true, "1::___:3") t.ipv6test(true, "1::___") t.ipv6test(true, "::ffff:____:0") t.ipv6test(true, "____::1") t.ipv6test(true, "1:2:3:4:____:6:7:8") t.ipv6test(true, "1:2:____:4:5:6::8") t.ipv6test(true, "1:2:3:4:5::____") t.ipv6test(true, "1:2:3:____::8") t.ipv6test(true, "____:2::8") t.ipv6test(true, "1::____") t.ipv6test(true, "____::2:3:4:5:6:7") t.ipv6test(true, "____::2:3:4:5:6") t.ipv6test(true, "1::2:3:4:____") t.ipv6test(true, "1::2:____:4") t.ipv6test(true, "1::____:3") t.ipv6test(true, "1::____") t.ipv6test(false, "::ffff:_____:0") t.ipv6test(false, "_____::1") t.ipv6test(false, "1:2:3:4:_____:6:7:8") t.ipv6test(false, "1:2:_____:4:5:6::8") t.ipv6test(false, "1:2:3:4:5::_____") t.ipv6test(false, "1:2:3:_____::8") t.ipv6test(false, "_____:2::8") t.ipv6test(false, "1::_____") t.ipv6test(false, "_____::2:3:4:5:6:7") t.ipv6test(false, "_____::2:3:4:5:6") t.ipv6test(false, "1::2:3:4:_____") t.ipv6test(false, "1::2:_____:4") t.ipv6test(false, "1::_____:3") t.ipv6test(false, "1::_____") t.ipv6test(false, "::ffff:ff___:0") t.ipv6test(false, "f____::1") t.ipv6test(false, "1:2:3:4:ffff_:6:7:8") t.ipv6test(false, "1:2:ffff_:4:5:6::8") t.ipv6test(false, "1:2:3:4:5::f_f__") t.ipv6test(false, "1:2:3:fff__::8") t.ipv6test(false, "f___f:2::8") t.ipv6test(false, "1::ff_ff") t.ipv6test(false, "ff_ff::2:3:4:5:6:7") t.ipv6test(false, "f____::2:3:4:5:6") t.ipv6test(false, "1::2:3:4:F____") t.ipv6test(false, "1::2:FF___:4") t.ipv6test(false, "1::FFF__:3") t.ipv6test(false, "1::FFFF_") t.ipv6test(false, "::ffff:_2_:0") t.ipv6test(false, "_2_::1") t.ipv6test(false, "1:2:3:4:_2_:6:7:8") t.ipv6test(false, "1:2:_2_:4:5:6::8") t.ipv6test(false, "1:2:3:4:5::_2_") t.ipv6test(false, "1:2:3:_2_::8") t.ipv6test(false, "_2_:2::8") t.ipv6test(false, "1::_2_") t.ipv6test(false, "_2_::2:3:4:5:6:7") t.ipv6test(false, "_2_::2:3:4:5:6") t.ipv6test(false, "1::2:3:4:_2_") t.ipv6test(false, "1::2:_2_:4") t.ipv6test(false, "1::_2_:3") t.ipv6test(false, "1::_2_") t.ipv6test(false, "::ffff:_2:0") t.ipv6test(false, "_2::1") t.ipv6test(false, "1:2:3:4:_2:6:7:8") t.ipv6test(false, "1:2:_2:4:5:6::8") t.ipv6test(false, "1:2:3:4:5::_2") t.ipv6test(false, "1:2:3:_2::8") t.ipv6test(false, "_2:2::8") t.ipv6test(false, "1::_2") t.ipv6test(false, "_2::2:3:4:5:6:7") t.ipv6test(false, "_2::2:3:4:5:6") t.ipv6test(false, "1::2:3:4:_2") t.ipv6test(false, "1::2:_2:4") t.ipv6test(false, "1::_2:3") t.ipv6test(false, "1::_2") t.ipv6test(true, "::ffff:2_:0") t.ipv6test(true, "2_::1") t.ipv6test(true, "1:2:3:4:2_:6:7:8") t.ipv6test(true, "1:2:2_:4:5:6::8") t.ipv6test(true, "1:2:3:4:5::2_") t.ipv6test(true, "1:2:3:2_::8") t.ipv6test(true, "2_:2::8") t.ipv6test(true, "1::2_") t.ipv6test(true, "2_::2:3:4:5:6:7") t.ipv6test(true, "2_::2:3:4:5:6") t.ipv6test(true, "1::2:3:4:2_") t.ipv6test(true, "1::2:2_:4") t.ipv6test(true, "1::2_:3") t.ipv6test(true, "1::2_") t.ipv6test(true, "::ffff:2___:0") t.ipv6test(true, "2___::1") t.ipv6test(true, "1:2:3:4:2___:6:7:8") t.ipv6test(true, "1:2:2___:4:5:6::8") t.ipv6test(true, "1:2:3:4:5::2___") t.ipv6test(true, "1:2:3:2___::8") t.ipv6test(true, "2___:2::8") t.ipv6test(true, "1::2___") t.ipv6test(true, "2___::2:3:4:5:6:7") t.ipv6test(true, "2___::2:3:4:5:6") t.ipv6test(true, "1::2:3:4:2___") t.ipv6test(true, "1::2:2___:4") t.ipv6test(true, "1::2___:3") t.ipv6test(true, "1::2___") t.ipv6test(true, "::fff_:2___:0") t.ipv6test(true, "2___::_") t.ipv6test(true, "1:2:3:4:2___:6_:7_:8") t.ipv6test(true, "1:2:2___:4:5:6::8__") t.ipv6test(true, "1:2:3_:4:5::2___") t.ipv6test(true, "1:2:3:2___::8") t.ipv6test(true, "2___:2::8") t.ipv6test(true, "1::2___") t.ipv6test(true, "2___::2_:3__:4:5:6:7") t.ipv6test(true, "2___::2:3_:4:5:6") t.ipv6test(true, "1::2:3:4_:2___") t.ipv6test(true, "1::2:2___:4f__") t.ipv6test(true, "1___::2___:3___") t.ipv6test(true, "1_::2___") t.ipv6test(t.isLenient(), "*:1:1._.__") t.ipv6test(true, "*:1:1._.__.___") //t.ipv6test(false, "*:_:1:_.1.1._");//this passes validation but conversion to mask fails because the ipv4 ranges cannot be converted to ipv6 ranges t.ipv6test(true, "*:_:1:1._.1._") t.ipv6test(true, "*:_:1:_.___.1._") t.ipv6test(true, "*:_:1:_.___._.___") t.ipv6test(true, "1:*:1_:1:1.1_.1.1") t.ipv6test(false, "1:1:1.2_.1") t.ipv6test(false, "1:1:1.2__.1.1") t.ipv6test(false, "1:1:_.*") t.ipv6test(false, "1:1:1._") t.ipv6test(true, "a-f:b:c:d:e:f:a:bb") t.ipv6test(true, "-f:b:c:d:e:f:a:bb") t.testCIDRSubnets("9.*.237.26/0", "9.*.237.26") t.testCIDRSubnets("9.*.237.26/1", "9.*.237.26") t.testCIDRSubnets("9.*.237.26/4", "9.*.237.26") t.testCIDRSubnets("9.*.237.26/5", "9.*.237.26") t.testCIDRSubnets("9.*.237.26/7", "9.*.237.26") t.testCIDRSubnets("9.*.237.26/8", "9.*.237.26") t.testCIDRSubnets("9.*.237.26/9", "9.*.237.26") t.testCIDRSubnets("9.*.237.26/16", "9.*.237.26") t.testCIDRSubnets("9.*.237.26/30", "9.*.237.26") t.testCIDRSubnets("9.*.237.26/31", "9.*.237.26-27") t.testCIDRSubnets("9.*.237.26/32", "9.*.237.26") t.testContains("0.0.0.0/0", "1-2.*.3.*", false) t.testContains("0-127.0.0.0/8", "127-127.*.3.*", false) t.testContains("0.0.0.0/4", "13-15.*.3.*", false) t.testContains("0-15.*.*.*/4", "13-15.*.3.*", false) t.testContains("0.0.0.0/4", "9.*.237.*/16", false) t.testContains("0.0.0.0/4", "8-9.*.237.*/16", false) t.testNotContains("1-2.0.0.0/4", "9.*.237.*/16") t.testNotContains("1-2.0.0.0/4", "8-9.*.237.*/16") t.testNotContains("1-2.0.0.0/4", "9-17.*.237.*/16") t.testContains("8.0.0.0/5", "15.2.3.4", false) t.testContains("8.0.0.0/7", "8-9.*.3.*", false) t.testContains("9.0.0.0/8", "9.*.3.*", false) t.testContains("9.128.0.0/9", "9.128-255.*.0", false) t.testContains("9.128.0.0/15", "9.128-129.3.*", false) t.testContains("9.129.0.0/16", "9.129.3.*", false) t.testNotContains("9.129.0.0/16", "9.128-129.3.*") t.testNotContains("9.129.0.0/16", "9.128.3.*") t.testContains("9.129.237.24/30", "9.129.237.24-27", true) t.testContains("9.129.237.24/30", "9.129.237.24-27/31", true) t.testContains("9.129.237.24-27/30", "9.129.237.24-27/31", true) t.testContains("*.*.*.*/0", "9.129.237.26/0", false) t.testContains("0.0.0.0/0", "*.*.*.*/0", true) t.testContains("0.0.0.0/4", "0-15.0.0.*/4", false) t.testNotContains("192.0.0.0/4", "0-15.0.0.*/4") t.testNotContains("0-127.129.237.26/1", "0-127.0.*.0/1") t.testNotContains("9.129.237.26/0", "*.*.*.1/0") t.testNotContains("9.129.237.26/4", "0-15.0.1.*/4") t.testNotContains("1-16.0.0.*/4", "9.129.237.26/4") t.testNotContains("9.129.237.26/5", "8-15.0.0.0/5") t.testNotContains("9.129.237.26/7", "8-9.0.0.1-3/7") t.testNotContains("7-9.0.0.1-3/7", "9.129.237.26/7") t.testNotContains("9.129.237.26/8", "9.*.0.0/8") t.testNotContains("9.129.237.26/9", "9.128-255.0.0/9") t.testNotContains("9.129.237.26/15", "9.128-129.0.*/15") t.testNotContains("9.129.237.26/16", "9.129.*.1/16") t.testNotContains("9.129.237.26/30", "9.129.237.27/30") t.testContains("0.0.0.0/4", "9.129.237.26/4", false) t.testContains("8.0.0.0/5", "8-15.0.0.0/5", false) t.testContains("8.0.0.0/7", "8-9.0.0.1-3/7", false) t.testContains("7-9.*.*.*/7", "9.129.237.26/7", false) t.testContains("9.0.0.0/8", "9.*.0.0/8", false) t.testContains("9.128.0.0/9", "9.128-255.0.0/9", false) t.testContains("9.128.0.0/15", "9.128-129.0.*/15", false) t.testContains("9.128.0.0/15", "9.128.0.0/15", true) t.testContains("9.129.0.0/16", "9.129.*.*/16", true) t.testContains("9.128-129.*.*/15", "9.128.0.0/15", true) t.testContains("9.128.*.*/16", "9.128.0.0/16", true) t.testContains("9.129.*.*/16", "9.129.*.*/16", true) t.testContains("9.129.*.*/16", "9.129.*.0/16", false) t.testContains("9.129.237.24/30", "9.129.237.24-27/30", true) t.testContains("9.128-129.*.26/32", "9.128-129.*.26/32", true) t.testNotContains("1-16.0.0.0/4", "9.129.237.26/4") t.testNotContains("9.129.237.26/5", "8-15.0.0.0/5") t.testNotContains("9.129.237.26/7", "8-9.0.0.1-3/7") t.testNotContains("7-9.0.0.1-3/7", "9.129.237.26/7") t.testNotContains("9.129.237.26/8", "9.*.0.0/8") t.testNotContains("9.129.237.26/9", "9.128-255.0.0/9") t.testNotContains("9.129.237.26/15", "9.128-129.0.*/15") t.testNotContains("9.129.237.26/16", "9.129.*.1/16") t.testNotContains("9.129.237.26/16", "9.129.1.*/16") t.testNotContains("9.129.237.25/30", "9.129.237.26/30") t.testContains("1-16.0.0.*/4", "9.0.0.*/4", false) t.testNotContains("1-16.0.0.0-254/4", "9.0.0.*/4") t.testContains("0-16.0.0.0/4", "9.0.0.*/4", false) t.testContains("8-15.129.237.26/5", "9.129.237.26/5", false) t.testContains("8-9.129.237.26/7", "9.129.237.26/7", false) t.testContains("7-9.0.0.1-3/7", "9.0.0.2/7", false) t.testContains("9.*.237.26/8", "9.*.237.26/8", true) t.testContains("9.128-255.237.26/9", "9.129.237.26/9", false) t.testContains("9.128-129.237.26/15", "9.129.237.26/15", false) t.testContains("9.129.*.*/16", "9.129.237.26/16", false) t.testContains("9.129.237.24-27/30", "9.129.237.26/30", false) t.testContains("9.128-129.*.26/32", "9.128-129.*.26/32", true) t.testNotContains("9.129.237.26/4", "16-17.0.0.*/4") t.testNotContains("9.129.237.26/7", "2.0.0.1-3/7") t.testContains("::ffff:1.*.3.4", "1.2.3.4", false) //ipv4 mapped t.testNotContains("::ffff:1.2-4.3.4/112", "1.2-3.3.*") t.testNotContains("ffff:0:0:0:0:0:*:0/32", "ffff:0:ffff:1-d:e:f:*:b") t.testNotContains("fffc-ffff::ffff/30", "fffd-fffe:0:0:0:0:0:0:0/30") t.testNotContains("ffff:0-d::ffff/32", "ffff:a-c:0:0:0:0:0:0/32") t.testNotContains("ffff::ffff/0", "a-b:0:b:0:c:d-e:*:0/0") t.testNotContains("ffff::ffff/1", "8000-8fff:0:0:0:0:*:a-b:0/1") t.testNotContains("ffff:*::fffb/126", "ffff:*:0:0:0:0:0:fffc-ffff/126") t.testNotContains("ffff:1-2::fffb/126", "ffff:1-2:0:0:0:0:0:fffc-ffff/126") t.testContains("::ffff:1.2-4.0.0/112", "1.2-3.3.*", false) t.testContains("0:0:0:0:0:0:0:0/0", "a:*:c:d:e:1-ffff:a:b", false) t.testContains("8000:0:0:0:0:0:0:0/1", "8000-8fff:b:c:d:e:f:*:b", false) t.testNotContains("8000:0:0:0:0:0:0:0/1", "7fff-8fff:b:c:d:e:f:*:b") t.testContains("ffff:0:0:0:0:0:0:0/30", "ffff:0-3:c:d:e:f:a:b", false) t.testNotContains("ffff:0:0:0:0:0:0:0/30", "ffff:0-4:c:d:e:f:a:b") t.testContains("ffff:0:0:0:0:0:0:0/32", "ffff:0:ffff:1-d:e:f:*:b", false) t.testContains("fffc-ffff::/30", "fffd-fffe:0:0:0:0:0:0:0/30", false) t.testContains("ffff:0-d::/32", "ffff:a-c:0:0:0:0:0:0/32", false) t.testNotContains("ffff:0:0:0:0:1-2:0:0/32", "ffff:0-1:ffff:d:e:f:a:b") t.testContains("ffff:0:0:0:0:4-ffff:0:fffc-ffff", "ffff:0:0:0:0:4-ffff:0:fffd-ffff", false) t.testContains("ffff:0:0:0:0:4-ffff:0:fffc/126", "ffff:0:0:0:0:4-ffff:0:fffd-ffff", false) t.testContains("ffff:0:0:0:0:4-ffff:0:fffc/126", "ffff:0:0:0:0:4-ffff:0:fffc-ffff", true) t.testContains("ffff:0:*:0:0:4-ffff:0:ffff/128", "ffff:0:*:0:0:4-ffff:0:ffff", true) t.testContains("ffff:*:0:0:0:0:0:fffa-ffff/126", "ffff:*::ffff/126", false) t.testContains("::/0", "a-b:0:b:0:c:d-e:*:0/0", false) t.testContains("8000::/1", "8000-8fff:0:0:0:0:*:a-b:0/1", false) t.testContains("ffff:*::fffc/126", "ffff:*:0:0:0:0:0:fffc-ffff/126", true) t.testContains("ffff:1-2::fffc/126", "ffff:1-2:0:0:0:0:0:fffc-ffff/126", true) t.testContains("10.162.155.1-255", "10.162.155.1-51", false) t.testContains("10.162.155.1-51", "10.162.155.1-51", true) t.testContains("10.162.1-51.155", "10.162.1-51.155", true) t.testContains("10.162.1-255.155", "10.162.1-51.155", false) t.testContains("1-255.10.162.155", "1-51.10.162.155", false) t.testContains("10.162.155.0-255", "10.162.155.0-51", false) t.testContains("10.162.155.0-51", "10.162.155.0-51", true) t.testContains("10.162.0-51.155", "10.162.0-51.155", true) t.testContains("10.162.0-255.155", "10.162.0-51.155", false) t.testContains("0-255.10.162.155", "0-51.10.162.155", false) t.testNotContains("192.13.1.0/25", "192.13.1.1-255") t.testNotContains("192.13.1.1-255", "192.13.1.0/25") t.testContains("192.13.1.0/25", "192.13.1.1-127", false) t.testContains("192.13.1.0/25", "192.13.1.0-127", true) t.testContains("192.13.1.0-127", "192.13.1.0/25", true) t.testContains("ffff:1-3::/32", "ffff:2::", false) t.testContains("ffff:2-3::/32", "ffff:2::", false) t.testContains("ffff:1-3::/32", "ffff:3::", false) t.testNotContains("ffff:1-3::/32", "ffff:4::") t.testContains("ffff:1000-3000::/20", "ffff:2000::", false) t.testContains("ffff:2000-3000::/20", "ffff:2000::", false) t.testContains("ffff:1000-3000::/20", "ffff:3000::", false) t.testNotContains("ffff:1000-3000::/20", "ffff:4000::") t.testNotContains("ffff:2000-3000::/20", "ffff:4000::") t.testContains("ffff:1000::/20", "ffff:1111-1222::", false) t.testNotContains("ffff:1000::/20", "ffff:1-::") t.testContains("ffff:1-:*", "ffff:1000::/20", false) t.testNotContains("ffff:1000::/20", "ffff:1111-2222::") t.testNotContains("ffff:1000::/20", "ffff:1-10::") t.testNotContains("ffff:1000::/20", "ffff:1-1::") t.testContains("::/64", "::", false) t.testNotContains("1:2::/64", "::") t.testContains("1:2::/64", "1:2::", false) t.testNotContains("5.62.62-63.*", "5.62.64.1") t.testNotContains("5.62.62-63.*", "5.62.68.1") t.testNotContains("5.62.62-63.*", "5.62.78.1") t.testContains("192.13.1.0/25", "192.13.1.1-127", false) t.testNotContains("192.13.1.0/25", "192.13.1.1-255") t.testContains("192.13.1.0-255", "192.13.1.0/23", false) t.testContains("192.13.0-1.0-255", "192.13.1.0/23", false) t.testContains("192.13.0-1.0-255", "192.13.0.0/23", true) t.testSubnet("1.2-4.3.4", "255.255.254.255", 24, "1.2-4.2.4/24", "1.2-4.2.4", "1.2-4.3.4/24") t.testSubnet("1.2-4.3.4", "255.248.254.255", 24, "1.0.2.4/24", "1.0.2.4", "1.2-4.3.4/24") t.testSubnet("__::", "ffff::", 128, "0-ff:0:0:0:0:0:0:0/128", "0-ff:0:0:0:0:0:0:0", "0-ff:0:0:0:0:0:0:0/128") t.testSubnet("0-ff::", "fff0::", 128, "", "", "0-ff:0:0:0:0:0:0:0/128") t.testSubnet("0-ff::", "fff0::", 12, "0-ff:0:0:0:0:0:0:0/12", "", "0-ff:0:0:0:0:0:0:0/12") t.testSubnet("0-f0::", "fff0::", 12, "0-f0:0:0:0:0:0:0:0/12", "", "0-f0:0:0:0:0:0:0:0/12") t.testSubnet("0-f::", "fff0::", 12, "0-f:0:0:0:0:0:0:0/12", "0:0:0:0:0:0:0:0", "0-f:0:0:0:0:0:0:0/12") t.testSubnet("0-f::*", "fff0::ffff", 12, "0-f:0:0:0:0:0:0:*/12", "0:0:0:0:0:0:0:*", "0-f:0:0:0:0:0:0:*/12") t.testSubnet("::1:__", "::1:ffff", 128, "0:0:0:0:0:0:1:0-ff/128", "0:0:0:0:0:0:1:0-ff", "0:0:0:0:0:0:1:0-ff/128") t.testSubnet("::1:__", "::1:ffff", 126, "0:0:0:0:0:0:1:0-fc/126", "0:0:0:0:0:0:1:0-ff", "0:0:0:0:0:0:1:0-fc/126") t.testSubnet("::1:0-ff", "::1:fff0", 128, "", "", "0:0:0:0:0:0:1:0-ff/128") t.testSubnet("::1:0-ff", "::1:fff0", 124, "0:0:0:0:0:0:1:0-f0/124", "", "0:0:0:0:0:0:1:0-f0/124") t.testSubnet("*::1:0-f", "ffff::1:fff0", 124, "*:0:0:0:0:0:1:0/124", "*:0:0:0:0:0:1:0", "*:0:0:0:0:0:1:0/124") t.testReverseHostAddress("*.*.0-240.0/20") t.testReverseHostAddress("*.*.0.0/16") t.testReverseHostAddress("*:0-f000::/20") t.testResolved("8.*.27.26", "8.*.27.26") t.testResolved("2001:*:0:0:8:800:200C:417A", "2001:*:0:0:8:800:200C:417A") t.testNormalized("ABCD:EF12:*:*:***:A:*:BBBB", "abcd:ef12:*:*:*:a:*:bbbb") t.testNormalized("ABCD:EF12:*:*:**:A:***:BBBB%g", "abcd:ef12:*:*:*:a:*:bbbb%g") t.testNormalized("1.*", "1.*.*.*") t.testNormalized("*.1.*", "*.1.*.*") t.testNormalized("*:1::*", "*:1::*") t.testNormalized("*:1:*", "*:1:*:*:*:*:*:*") t.testNormalized("001-002:0001-0002:01-2:1-02:01-02:*", "1-2:1-2:1-2:1-2:1-2:*:*:*") t.testInsertAndAppend("a:b:c:d:e:f:aa:bb/0", "1:2:3:4:5:6:7:8/0", []ipaddr.BitCount{0, 0, 0, 0, 0, 0, 0, 0, 0}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb", "1:2:3:4:5:6:7:8/0", []ipaddr.BitCount{0, 16, 32, 48, 64, 80, 96, 112, 128}) t.testInsertAndAppendPrefs("a:b:c:d:e:f:aa:bb/0", "1:2:3:4:5:6:7:8", []ipaddr.PrefixLen{nil, p0, p0, p0, p0, p0, p0, p0, p0}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb/64", "1:2:3:4:5:6:7:8/64", []ipaddr.BitCount{64, 64, 64, 64, 64, 64, 64, 64, 64}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb", "1:2:3:4:5:6:7:8/64", []ipaddr.BitCount{64, 64, 64, 64, 64, 80, 96, 112, 128}) t.testInsertAndAppendPrefs("a:b:c:d:e:f:aa:bb/63", "1:2:3:4:5:6:7:8", []ipaddr.PrefixLen{nil, nil, nil, nil, p63, p63, p63, p63, p63}) t.testInsertAndAppendPrefs("a:b:c:d:e:f:aa:bb/64", "1:2:3:4:5:6:7:8", []ipaddr.PrefixLen{nil, nil, nil, nil, p64, p64, p64, p64, p64}) t.testInsertAndAppendPrefs("a:b:c:d:e:f:aa:bb/65", "1:2:3:4:5:6:7:8", []ipaddr.PrefixLen{nil, nil, nil, nil, nil, p65, p65, p65, p65}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb/128", "1:2:3:4:5:6:7:8/128", []ipaddr.BitCount{128, 128, 128, 128, 128, 128, 128, 128, 128}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb", "1:2:3:4:5:6:7:8/128", []ipaddr.BitCount{128, 128, 128, 128, 128, 128, 128, 128, 128}) t.testInsertAndAppendPrefs("a:b:c:d:e:f:aa:bb/128", "1:2:3:4:5:6:7:8", []ipaddr.PrefixLen{nil, nil, nil, nil, nil, nil, nil, nil, p128}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb/32", "1:2:3:4:5:6:7:8/64", []ipaddr.BitCount{64, 64, 32, 32, 32, 32, 32, 32, 32}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb/64", "1:2:3:4:5:6:7:8/32", []ipaddr.BitCount{32, 32, 32, 48, 64, 64, 64, 64, 64}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb/0", "1:2:3:4:5:6:7:8/64", []ipaddr.BitCount{64, 0, 0, 0, 0, 0, 0, 0, 0}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb/64", "1:2:3:4:5:6:7:8/0", []ipaddr.BitCount{0, 16, 32, 48, 64, 64, 64, 64, 64}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb/64", "1:2:3:4:5:6:7:8/128", []ipaddr.BitCount{128, 128, 128, 128, 64, 64, 64, 64, 64}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb/128", "1:2:3:4:5:6:7:8/64", []ipaddr.BitCount{64, 64, 64, 64, 64, 80, 96, 112, 128}) t.testInsertAndAppend("1.2.3.4/0", "5.6.7.8/0", []ipaddr.BitCount{0, 0, 0, 0, 0}) t.testInsertAndAppend("1.2.3.4", "5.6.7.8/0", []ipaddr.BitCount{0, 8, 16, 24, 32}) t.testInsertAndAppendPrefs("1.2.3.4/0", "5.6.7.8", []ipaddr.PrefixLen{nil, p0, p0, p0, p0}) t.testInsertAndAppend("1.2.3.4/16", "5.6.7.8/16", []ipaddr.BitCount{16, 16, 16, 16, 16}) t.testInsertAndAppend("1.2.3.4", "5.6.7.8/16", []ipaddr.BitCount{16, 16, 16, 24, 32}) t.testInsertAndAppendPrefs("1.2.3.4/16", "5.6.7.8", []ipaddr.PrefixLen{nil, nil, p16, p16, p16}) t.testInsertAndAppend("1.2.3.4/32", "5.6.7.8/32", []ipaddr.BitCount{32, 32, 32, 32, 32}) t.testInsertAndAppend("1.2.3.4", "5.6.7.8/32", []ipaddr.BitCount{32, 32, 32, 32, 32}) t.testInsertAndAppendPrefs("1.2.3.4/31", "5.6.7.8", []ipaddr.PrefixLen{nil, nil, nil, nil, p31}) t.testInsertAndAppendPrefs("1.2.3.4/32", "5.6.7.8", []ipaddr.PrefixLen{nil, nil, nil, nil, p32}) t.testInsertAndAppend("1.2.3.4/16", "5.6.7.8/24", []ipaddr.BitCount{24, 24, 16, 16, 16}) t.testInsertAndAppend("1.2.3.4/24", "5.6.7.8/7", []ipaddr.BitCount{7, 8, 16, 24, 24}) t.testInsertAndAppend("1.2.3.4/24", "5.6.7.8/16", []ipaddr.BitCount{16, 16, 16, 24, 24}) t.testInsertAndAppend("1.2.3.4/0", "5.6.7.8/16", []ipaddr.BitCount{16, 0, 0, 0, 0}) t.testInsertAndAppend("1.2.3.4/16", "5.6.7.8/0", []ipaddr.BitCount{0, 8, 16, 16, 16}) t.testInsertAndAppend("1.2.3.4/17", "5.6.7.8/0", []ipaddr.BitCount{0, 8, 16, 17, 17}) t.testInsertAndAppend("1.2.3.4/16", "5.6.7.8/32", []ipaddr.BitCount{32, 32, 16, 16, 16}) t.testInsertAndAppend("1.2.3.4/32", "5.6.7.8/16", []ipaddr.BitCount{16, 16, 16, 24, 32}) t.testReplace("a:b:c:d:e:f:aa:bb/0", "1:2:3:4:5:6:7:8/0") t.testReplace("a:b:c:d:e:f:aa:bb", "1:2:3:4:5:6:7:8/0") t.testReplace("a:b:c:d:e:f:aa:bb/0", "1:2:3:4:5:6:7:8") t.testReplace("a:b:c:d:e:f:aa:bb/64", "1:2:3:4:5:6:7:8/64") t.testReplace("a:b:c:d:e:f:aa:bb", "1:2:3:4:5:6:7:8/64") t.testReplace("a:b:c:d:e:f:aa:bb/63", "1:2:3:4:5:6:7:8") t.testReplace("a:b:c:d:e:f:aa:bb/64", "1:2:3:4:5:6:7:8") t.testReplace("a:b:c:d:e:f:aa:bb/65", "1:2:3:4:5:6:7:8") t.testReplace("a:b:c:d:e:f:aa:bb/128", "1:2:3:4:5:6:7:8/128") t.testReplace("a:b:c:d:e:f:aa:bb", "1:2:3:4:5:6:7:8/128") t.testReplace("a:b:c:d:e:f:aa:bb/128", "1:2:3:4:5:6:7:8") t.testReplace("a:b:c:d:e:f:aa:bb/32", "1:2:3:4:5:6:7:8/64") t.testReplace("a:b:c:d:e:f:aa:bb/64", "1:2:3:4:5:6:7:8/32") t.testReplace("a:b:c:d:e:f:aa:bb/0", "1:2:3:4:5:6:7:8/64") t.testReplace("a:b:c:d:e:f:aa:bb/64", "1:2:3:4:5:6:7:8/0") t.testReplace("a:b:c:d:e:f:aa:bb/64", "1:2:3:4:5:6:7:8/128") t.testReplace("a:b:c:d:e:f:aa:bb/128", "1:2:3:4:5:6:7:8/64") t.testReplace("1.2.3.4/0", "5.6.7.8/0") t.testReplace("1.2.3.4", "5.6.7.8/0") t.testReplace("1.2.3.4/0", "5.6.7.8") t.testReplace("1.2.3.4/16", "5.6.7.8/16") t.testReplace("1.2.3.4", "5.6.7.8/16") t.testReplace("1.2.3.4/16", "5.6.7.8") t.testReplace("1.2.3.4/32", "5.6.7.8/32") t.testReplace("1.2.3.4", "5.6.7.8/32") t.testReplace("1.2.3.4/31", "5.6.7.8") t.testReplace("1.2.3.4/32", "5.6.7.8") t.testReplace("1.2.3.4/16", "5.6.7.8/24") t.testReplace("1.2.3.4/24", "5.6.7.8/7") t.testReplace("1.2.3.4/24", "5.6.7.8/16") t.testReplace("1.2.3.4/0", "5.6.7.8/16") t.testReplace("1.2.3.4/16", "5.6.7.8/0") t.testReplace("1.2.3.4/17", "5.6.7.8/0") t.testReplace("1.2.3.4/16", "5.6.7.8/32") t.testReplace("1.2.3.4/32", "5.6.7.8/16") t.testSub("1:1::/32", "1:1:1:1:1:1:1:1", []string{ "1:1:0:0:0:0:0:0/48", "1:1:2-fffe:0:0:0:0:0/47", "1:1:1:0:0:0:0:0/64", "1:1:1:2-fffe:0:0:0:0/63", "1:1:1:1:0:0:0:0/80", "1:1:1:1:2-fffe:0:0:0/79", "1:1:1:1:1:0:0:0/96", "1:1:1:1:1:2-fffe:0:0/95", "1:1:1:1:1:1:0:0/112", "1:1:1:1:1:1:2-fffe:0/111", "1:1:1:1:1:1:1:0", "1:1:1:1:1:1:1:2-fffe/127", }) t.testSub("1:1::/32", "1:1::/16", []string{ "1:1:1-ffff:0:0:0:0:0/48", "1:1:0:1-ffff:0:0:0:0/64", "1:1:0:0:1-ffff:0:0:0/80", "1:1:0:0:0:1-ffff:0:0/96", "1:1:0:0:0:0:1-ffff:0/112", "1:1:0:0:0:0:0:1-ffff"}, ) t.testSub("1:1::/32", "1:1::/48", []string{"1:1:1-ffff:0:0:0:0:0/48"}) t.testSub("1:1::/32", "1:1::/64", []string{ "1:1:1-ffff:0:0:0:0:0/48", "1:1:0:1-ffff:0:0:0:0/64", }) t.testSub("1:1::/32", "1:1:2:2::/64", []string{ "1:1:0:0:0:0:0:0/47", "1:1:3-ffff:0:0:0:0:0/48", "1:1:2:0:0:0:0:0/63", "1:1:2:3-ffff:0:0:0:0/64", }) t.testSub("10.0.0.0/22", "10.0.0.0/24", []string{"10.0.1-3.0/24"}) //[10.0.1-3.0/24] t.testIntersect("1:1:1-3:1:1:1:1:1", "1:1:2-4:1:1:1:1:1", "1:1:2-3:1:1:1:1:1") t.testIntersect("1:1:1-3:1:0:1:1:1", "1:1:2-4:1:1:1:1:1", "") t.testToPrefixBlock("1.3.*.*", "1.3.*.*") t.testToPrefixBlock("1.2-3.*.*", "1.2-3.*.*") t.testToPrefixBlock("1.3.3.4/15", "1.2-3.*.*/15") t.testToPrefixBlock("*.3.3.4/15", "*.2-3.*.*/15") t.testToPrefixBlock("1.3.3.4/16", "1.3.*.*/16") t.testToPrefixBlock("1:3:3:4::/15", "0-1:*/15") t.testToPrefixBlock("*:3:3:4::/15", "0-fffe::/15") t.testToPrefixBlock("1:3:3:4::/16", "1:*/16") t.testMaxHost("1.*.255.255/16", "1.*.255.255/16") t.testMaxHost("1.2.*.*/16", "1.2.255.255/16") t.testMaxHost("1.*.*.*/16", "1.*.255.255/16") t.testMaxHost("1.2.*.1/16", "1.2.255.255/16") t.testMaxHost("1.*.*.1/16", "1.*.255.255/16") t.testZeroHost("1.*.0.0/16", "1.*.0.0/16") t.testZeroHost("1.2.*.*/16", "1.2.0.0/16") t.testZeroHost("1.*.*.*/16", "1.*.0.0/16") t.testZeroHost("1.2.*.1/16", "1.2.0.0/16") t.testZeroHost("1.*.*.1/16", "1.*.0.0/16") t.testZeroNetwork("1.*.0.0/16", "0.0.0.0/16") t.testZeroNetwork("1.2.*.*/16", "0.0.*.*/16") t.testZeroNetwork("1.*.*.*/16", "0.0.*.*/16") t.testZeroNetwork("1.2.*.1/16", "0.0.*.1/16") t.testZeroNetwork("1.*.*.1/16", "0.0.*.1/16") t.testMaxHost("1:*::ffff:ffff:ffff:ffff/64", "1:*::ffff:ffff:ffff:ffff/64") t.testMaxHost("1:2::ffff:ffff:ffff:ffff/64", "1:2::ffff:ffff:ffff:ffff/64") t.testMaxHost("1:*::*:*:*:*/64", "1:*::ffff:ffff:ffff:ffff/64") t.testMaxHost("1:2::*:*:*:*/64", "1:2::ffff:ffff:ffff:ffff/64") t.testMaxHost("1:2::*:*:*:1/64", "1:2::ffff:ffff:ffff:ffff/64") t.testMaxHost("1:*:1/64", "1:*:ffff:ffff:ffff:ffff/64") t.testMaxHost("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0") t.testMaxHost("*:*/0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0") t.testMaxHost("::/128", "::/128") t.testZeroHost("1:*::/64", "1:*::/64") t.testZeroHost("1:2::/64", "1:2::/64") t.testZeroHost("1:*::*:*:*:*/64", "1:*::/64") t.testZeroHost("1:2::*:*:*:*/64", "1:2::/64") t.testZeroHost("1:2::*:*:*:1/64", "1:2::/64") t.testZeroHost("1:*:1/64", "1:*:*:*::/64") t.testZeroHost("::/0", "::/0") t.testZeroHost("*:*/0", "::/0") t.testZeroHost("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128") t.testZeroHost("1:2:3:4::/64", "1:2:3:4::/64") t.testZeroHost("1:2:*/64", "1:2:*:*::/64") t.testZeroHost("1:2:3:4:*:1/64", "1:2:3:4::/64") t.testZeroHost("1:*:1/64", "1:*:*:*::/64") t.testZeroNetwork("1:*::/64", "::/64") t.testZeroNetwork("1:2::/64", "::/64") t.testZeroNetwork("1:*::*:*:*:*/64", "::*:*:*:*/64") t.testZeroNetwork("1:2::*:*:*:*/64", "::*:*:*:*/64") t.testZeroNetwork("1:2::*:*:*:1/64", "::*:*:*:1/64") t.testZeroNetwork("1:*:1/64", "::*:*:*:1/64") t.testZeroNetwork("::/0", "::/0") t.testZeroNetwork("*:*/0", "*:*/0") t.testZeroNetwork("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128", "::/128") t.testZeroNetwork("1:2:3:4::/64", "::/64") t.testZeroNetwork("1:2:3:4:*/64", "0:0:0:0:*/64") t.testZeroNetwork("1:2:*/64", "0:0:0:0:*/64") t.testZeroNetwork("1:2:3:4:*:1/64", "0:0:0:0:*:1/64") t.testZeroNetwork("1:*:1/64", "0:0:0:0:*:1/64") t.testIsPrefixBlock("1.2.*.*", false, false) t.testIsPrefixBlock("1.2.3.*", false, false) t.testIsPrefixBlock("1.*.*.*", false, false) t.testIsPrefixBlock("1.2-3.*.*", false, false) t.testIsPrefixBlock("1.2.128-255.*", false, false) t.testIsPrefixBlock("*.*/0", true, true) t.testIsPrefixBlock("1.2.*.*/16", true, true) t.testIsPrefixBlock("1.2.3.*/16", false, false) t.testIsPrefixBlock("1.*.*.*/16", true, false) t.testIsPrefixBlock("1.2-3.*.*/16", true, false) t.testIsPrefixBlock("1.2.128-255.*/16", false, false) t.testPrefixBlocks("1.2.*.*", 8, false, false) t.testPrefixBlocks("1.2.3.*", 8, false, false) t.testPrefixBlocks("1.*.*.*", 8, true, true) t.testPrefixBlocks("1.2-3.*.*", 8, false, false) t.testPrefixBlocks("1.2.128-255.*", 8, false, false) t.testPrefixBlocks("*.*/0", 8, true, false) t.testPrefixBlocks("1.2.*.*/16", 8, false, false) t.testPrefixBlocks("1.2.3.*/16", 8, false, false) t.testPrefixBlocks("1.*.*.*/16", 8, true, true) t.testPrefixBlocks("1.2-3.*.*/16", 8, false, false) t.testPrefixBlocks("1.2.128-255.*/16", 8, false, false) t.testPrefixBlocks("1.2.*.*", 24, true, false) t.testPrefixBlocks("1.2.3.*", 24, true, true) t.testPrefixBlocks("1.*.*.*", 24, true, false) t.testPrefixBlocks("1.2-3.*.*", 24, true, false) t.testPrefixBlocks("1.2.128-255.*", 24, true, false) t.testPrefixBlocks("*.*/0", 24, true, false) t.testPrefixBlocks("1.2.*.*/16", 24, true, false) t.testPrefixBlocks("1.2.3.*/16", 24, true, true) t.testPrefixBlocks("1.*.*.*/16", 24, true, false) t.testPrefixBlocks("1.2-3.*.*/16", 24, true, false) t.testPrefixBlocks("1.2.128-255.*/16", 24, true, false) t.testIsPrefixBlock("a:b:c:d:*/64", true, true) t.testIsPrefixBlock("a:b:c:*/64", true, false) t.testIsPrefixBlock("a:b:c:d-e:*/64", true, false) t.testIsPrefixBlock("a:b:c:d:e:*/64", false, false) t.testIsPrefixBlock("a:b:c:d:0-ffff:*/64", true, true) t.testIsPrefixBlock("a:b:c:d:8000-ffff:*/64", false, false) t.testPrefixBlocks("a:b:c:d:*/64", 0, false, false) t.testPrefixBlocks("a:b:c:*/64", 0, false, false) t.testPrefixBlocks("a:b:c:d-e:*/64", 0, false, false) t.testPrefixBlocks("*:*/64", 0, true, true) t.testPrefixBlocks("a:b:c:d:e:*/64", 0, false, false) t.testPrefixBlocks("a:b:c:d:0-ffff:*/64", 0, false, false) t.testPrefixBlocks("a:b:c:d:*/64", 63, false, false) t.testPrefixBlocks("a:b:c:*/64", 63, true, false) t.testPrefixBlocks("a:b:c:d-e:*/64", 63, false, false) t.testPrefixBlocks("a:b:c:e-f:*/64", 63, true, true) t.testPrefixBlocks("a:b:c:d:e:*/64", 63, false, false) t.testPrefixBlocks("a:b:c:d:0-ffff:*/64", 63, false, false) t.testPrefixBlocks("a:b:c:d:*/64", 64, true, true) t.testPrefixBlocks("a:b:c:*/64", 64, true, false) t.testPrefixBlocks("a:b:c:d-e:*/64", 64, true, false) t.testPrefixBlocks("a:b:c:d:e:*/64", 64, false, false) t.testPrefixBlocks("a:b:c:d:0-ffff:*/64", 64, true, true) t.testPrefixBlocks("a:b:c:d:8000-ffff:*/64", 64, false, false) t.testPrefixBlocks("a:b:c:d:*/64", 65, true, false) t.testPrefixBlocks("a:b:c:*/64", 65, true, false) t.testPrefixBlocks("a:b:c:d-e:*/64", 65, true, false) t.testPrefixBlocks("a:b:c:d:e:*/64", 65, false, false) t.testPrefixBlocks("a:b:c:d:0-ffff:*/64", 65, true, false) t.testPrefixBlocks("a:b:c:d:8000-ffff:*/64", 65, true, true) t.testPrefixBlocks("a:b:c:d:0-ffff:*/64", 65, true, false) t.testPrefixBlocks("a:b:c:d:*/64", 128, true, false) t.testPrefixBlocks("a:b:c:*/64", 128, true, false) t.testPrefixBlocks("a:b:c:d-e:*/64", 128, true, false) t.testPrefixBlocks("a:b:c:d:e:*/64", 128, true, false) t.testPrefixBlocks("a:b:c:d:0-ffff:*/64", 128, true, false) t.testSplitBytes("1.2.*.4") t.testSplitBytes("1.2-4.3.4/16") t.testSplitBytes("1.2.3.4-5/0") t.testSplitBytes("1.2.*/32") t.testSplitBytes("ffff:2:3:4:eeee:dddd:cccc-dddd:bbbb") t.testSplitBytes("ffff:2:3:4:eeee:dddd:cccc:bbbb/64") t.testSplitBytes("ffff:2:3:4:*:dddd:cccc:bbbb/0") t.testSplitBytes("*:*/128") t.testSplitBytes("*:*") t.testIncrement("1.2.*.*/16", 0, "1.2.0.0") t.testIncrement("1.2.*.*/16", 1, "1.2.0.1") t.testIncrement("1.2.*.*/16", 65535, "1.2.255.255") t.testIncrement("1.2.*.*/16", 65536, "1.3.0.0") t.testIncrement("1.2.*.*/16", -1, "1.1.255.255") t.testIncrement("1.2.*.*/16", -65536, "1.1.0.0") t.testIncrement("1.2.*.*/16", -65537, "1.0.255.255") t.testIncrement("ffff:ffff:ffff:ffff:ffff:1-2:2-3:ffff", 0, "ffff:ffff:ffff:ffff:ffff:1:2:ffff") t.testIncrement("ffff:ffff:ffff:ffff:ffff:1-2:2-3:ffff", 1, "ffff:ffff:ffff:ffff:ffff:1:3:ffff") t.testIncrement("ffff:ffff:ffff:ffff:ffff:1-2:2-3:ffff", 3, "ffff:ffff:ffff:ffff:ffff:2:3:ffff") t.testIncrement("ffff:ffff:ffff:ffff:ffff:1-2:2-3:ffff", 4, "ffff:ffff:ffff:ffff:ffff:2:4::") t.testIncrement("ffff:ffff:ffff:ffff:ffff:1-2:2-3:ffff", 5, "ffff:ffff:ffff:ffff:ffff:2:4:1") t.testIncrement("ffff:ffff:ffff:ffff:ffff:fffe-ffff:fffe-ffff:ffff", 5, "") t.testIncrement("ffff:ffff:ffff:ffff:ffff:1-2:2-3:ffff", -0x10002ffff, "ffff:ffff:ffff:ffff:ffff::") t.testIncrement("ffff:ffff:ffff:ffff:ffff:1-2:2-3:ffff", -0x100030000, "ffff:ffff:ffff:ffff:fffe:ffff:ffff:ffff") t.testIncrement("ffff:ffff:ffff:ffff:ffff:1-2:2-3:ffff", -0x100030003, "ffff:ffff:ffff:ffff:fffe:ffff:ffff:fffc") t.testIncrement("ffff:ffff:ffff:ffff:ffff:1-2:2-3:ffff", -0x100030004, "ffff:ffff:ffff:ffff:fffe:ffff:ffff:fffb") t.testIncrement("::1-2:2-3:ffff", -0x100030000, "") t.testIncrement("ffff:3-4:ffff:ffff:ffff:1-2:2-3::", 7, "ffff:4:ffff:ffff:ffff:2:3::") t.testIncrement("ffff:3-4:ffff:ffff:ffff:1-2:2-3::", 9, "ffff:4:ffff:ffff:ffff:2:3:2") t.testLeadingZeroAddr("00-1.1.2.3", true) t.testLeadingZeroAddr("1.00-1.2.3", true) t.testLeadingZeroAddr("1.2.00-1.3", true) t.testLeadingZeroAddr("1.2.3.00-1", true) t.testLeadingZeroAddr("1-01.1.2.3", true) t.testLeadingZeroAddr("1.01-1.2.3", true) t.testLeadingZeroAddr("1.2.1-01.3", true) t.testLeadingZeroAddr("1.2.3.01-1", true) t.testLeadingZeroAddr("0-1.1.2.3", false) t.testLeadingZeroAddr("1.0-1.2.3", false) t.testLeadingZeroAddr("1.2.0-1.3", false) t.testLeadingZeroAddr("1.2.3.0-1", false) t.testLeadingZeroAddr("00-1:1:2:3::", true) t.testLeadingZeroAddr("1:00-1:2:3::", true) t.testLeadingZeroAddr("1:2:00-1:3::", true) t.testLeadingZeroAddr("1:2:3:00-1::", true) t.testLeadingZeroAddr("1-01:1:2:3::", true) t.testLeadingZeroAddr("1:1-01:2:3::", true) t.testLeadingZeroAddr("1:2:1-01:3::", true) t.testLeadingZeroAddr("1:2:3:1-01::", true) t.testLeadingZeroAddr("0-1:1:2:3::", false) t.testLeadingZeroAddr("1:0-1:2:3::", false) t.testLeadingZeroAddr("1:2:0-1:3::", false) t.testLeadingZeroAddr("1:2:3:0-1::", false) t.testRangeExtend("1.2.3.4-5", "1.2.4.3", "1.2.3-5.6", "", "1.2.3.4", "1.2.5.6") t.testRangeExtend("1.2.3.4-5", "1.2.4.3", "1.2.1-5.6", "", "1.2.1.6", "1.2.5.6") t.testRangeJoin2([]string{}, []string{}) t.testRangeJoin2([]string{ "", "", }, []string{}) t.testRangeJoin2([]string{ "1.2.3.4", "1.2.3.5", }, []string{ "1.2.3.4", "1.2.3.5", }) t.testRangeJoin2([]string{ "", "", "", "", }, []string{}) t.testRangeJoin2([]string{ "1.2.3.4", "1.2.3.5", "", "", }, []string{ "1.2.3.4", "1.2.3.5", }) t.testRangeJoin2([]string{ "", "", "1.2.3.4", "1.2.3.5", }, []string{ "1.2.3.4", "1.2.3.5", }) t.testRangeJoin2([]string{ "", "", "1.2.3.4", "1.2.3.5", "", "", }, []string{ "1.2.3.4", "1.2.3.5", }) t.testRangeJoin2([]string{ "1.2.3.4", "1.2.3.5", "", "", "1.2.3.255", "1.2.4.1", }, []string{ "1.2.3.4", "1.2.3.5", "1.2.3.255", "1.2.4.1", }) t.testRangeJoin2([]string{ "1.2.3.4", "1.2.3.255", "", "", "1.2.3.255", "1.2.4.1", }, []string{ "1.2.3.4", "1.2.4.1", }) t.testRangeJoin2([]string{ "1.2.3.4", "1.2.3.255", "", "", "1.2.3.255", "1.2.4.1", "1.2.4.2", "1.5.0.0", }, []string{ "1.2.3.4", "1.5.0.0", }) t.testRangeJoin2([]string{ "1.2.3.4", "1.2.3.255", "", "", "1.2.3.255", "1.2.4.1", "1.2.4.2", "255.255.255.255", "::", "::2", }, []string{ "1.2.3.4", "255.255.255.255", "::", "::2", }) t.testRangeJoin2([]string{ "1.2.3.4", "1.2.3.255", "1.2.3.255", "1.2.4.1", "1.2.4.2", "255.255.255.255", "::", "::1", }, []string{ "1.2.3.4", "255.255.255.255", "::", "::1", }) t.testRangeJoin2([]string{ "0.0.0.0", "0.0.0.1", "1.2.3.4", "1.2.3.255", "1.2.3.255", "1.2.4.1", "1.2.4.2", "255.255.255.255", "::", "::1", "::", "::2", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", }, []string{ "0.0.0.0", "0.0.0.1", "1.2.3.4", "255.255.255.255", "::", "::2", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", }) t.testRangeJoin2([]string{ "0.0.0.0", "0.0.0.1", "1.2.3.4", "1.2.3.255", "::", "::2", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", }, []string{ "0.0.0.0", "0.0.0.1", "1.2.3.4", "1.2.3.255", "::", "::2", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", }) t.testRangeJoin2([]string{ "0.0.0.0", "0.0.0.1", "1.2.3.4", "1.2.3.255", "::", "::2", "", "", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", }, []string{ "0.0.0.0", "0.0.0.1", "1.2.3.4", "1.2.3.255", "::", "::2", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", }) t.testRangeJoin2([]string{ "", "", "0.0.0.0", "0.0.0.1", "", "", "1.2.3.4", "1.2.3.255", "", "", "::1:2:3:4", "::1:2:3:4", "", "", "", "", "::1:2:3:4", "::1:2:3:5", "", "", "::1:2:3:6", "::1:2:3:6", "1.2.3.255", "1.2.4.1", "", "", "1.2.4.2", "255.255.255.255", "::", "::1", "", "", "::", "::2", "", "", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "", "", }, []string{ "0.0.0.0", "0.0.0.1", "1.2.3.4", "255.255.255.255", "::", "::2", "::1:2:3:4", "::1:2:3:6", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", }) t.testRangeJoin2([]string{ "", "", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "", "", "::", "::2", "", "", "::", "::1", "", "", "1.2.4.2", "255.255.255.255", "", "", "", "", "1.2.3.255", "1.2.4.1", "::1:2:3:6", "::1:2:3:6", "", "", "::1:2:3:4", "::1:2:3:5", "", "", "::1:2:3:4", "::1:2:3:4", "1.2.3.4", "1.2.3.255", "", "", "0.0.0.0", "0.0.0.1", "", "", }, []string{ "0.0.0.0", "0.0.0.1", "1.2.3.4", "255.255.255.255", "::", "::2", "::1:2:3:4", "::1:2:3:6", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", }) t.testIncompatibleAddress2("a:b:c:d:e:f:1.2.*.4", "a:b:c:d:e:f:1.2.0.4", "a:b:c:d:e:f:1.2.255.4", []interface{}{0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 1, 2, []uint{0, 0xff}, 4}) //[a, b, c, d, e, f, 1, 2, 0-ff, 4] t.testIncompatibleAddress2("::ffff:0.0.*.0", "::ffff:0.0.0.0", "::ffff:0.0.255.0", []interface{}{0, 0xffff, 0, 0, []uint{0, 0xff}, 0}) //[0, ffff, 0, 0, 0-ff, 0] t.testIncompatibleAddress2("::ffff:*.0.0.0", "::ffff:0.0.0.0", "::ffff:255.0.0.0", []interface{}{0, 0xffff, []uint{0, 0xff}, 0, 0, 0}) //[0, ffff, 0-ff, 0, 0, 0] t.testMaskedIncompatibleAddress("0-ffff::1/f000::10", "::", "f000::") t.testSubnetStringRange("0-ffff::1/f000::", "::1", "ffff::1", []interface{}{[]uint{0, 0xffff}, 0, 1}, p4) t.testSubnetStringRange("0-ffff::/f000::", "::", "ffff::", []interface{}{[]uint{0, 0xffff}, 0}, p4) t.testSubnetStringRange("0-f000::/f000::", "::", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", []interface{}{[]uint{0, 0xffff}, []*big.Int{bigZeroConst(), setBigString("ffffffffffffffffffffffffffff", 16)}}, p4) //[0-f000, 0] t.testSubnetStringRange2("0-ffff::/0fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::", "fff::", []interface{}{[]uint{0, 0xfff}, 0}) // [0-fff, 0] // /8 prefix? t.testSubnetStringRange2("1.*.*.*", "1.0.0.0", "1.255.255.255", []interface{}{1, []uint{0, 0xff}, []uint{0, 0xff}, []uint{0, 0xff}}) //[1, 0-255, 0-255, 0-255] t.testSubnetStringRange2("1.*.*", "1.0.0.0", "1.255.255.255", []interface{}{1, []uint{0, 0xff}, []uint{0, 0xffff}}) //[1, 0-255, 0-65535] t.testSubnetStringRange2("1.*", "1.0.0.0", "1.255.255.255", []interface{}{1, []uint{0, 0xffffff}}) //[1, 0-16777215] t.testSubnetStringRange2("a:b:c:*:cc:d:e:f", "a:b:c:0:cc:d:e:f", "a:b:c:ffff:cc:d:e:f", []interface{}{0xa, 0xb, 0xc, []uint{0, 0xffff}, 0xcc, 0xd, 0xe, 0xf}) //[a, b, c, 0-ffff, cc, d, e, f] t.testSubnetStringRange2("a:*:cc:d:e:f", "a::cc:d:e:f", "a:ffff:ffff:ffff:cc:d:e:f", []interface{}{0xa, []uint64{0, 0xffffffffffff}, 0xcc, 0xd, 0xe, 0xf}) //[a, 0-ffffffffffff, cc, d, e, f] t.testSubnetStringRange2("*:cc:d:e:f", "::cc:d:e:f", "ffff:ffff:ffff:ffff:cc:d:e:f", []interface{}{[]*big.Int{bigZeroConst(), setBigString("ffffffffffffffff", 16)}, 0xcc, 0xd, 0xe, 0xf}) //[0-ffffffffffffffff, cc, d, e, f] t.testSubnetStringRange2("a:b:c:*:cc:d:1.255.3.128", "a:b:c:0:cc:d:1.255.3.128", "a:b:c:ffff:cc:d:1.255.3.128", []interface{}{0xa, 0xb, 0xc, []uint{0, 0xffff}, 0xcc, 0xd, 1, 255, 3, 128}) //[a, b, c, 0-ffff, cc, d, e, f] t.testSubnetStringRange2("a:*:cc:d:1.255.3.128", "a::cc:d:1.255.3.128", "a:ffff:ffff:ffff:cc:d:1.255.3.128", []interface{}{0xa, []uint64{0, 0xffffffffffff}, 0xcc, 0xd, 1, 255, 3, 128}) //[a, 0-ffffffffffff, cc, d, e, f] t.testSubnetStringRange2("*:cc:d:1.255.3.128", "::cc:d:1.255.3.128", "ffff:ffff:ffff:ffff:cc:d:1.255.3.128", []interface{}{[]*big.Int{bigZeroConst(), setBigString("ffffffffffffffff", 16)}, 0xcc, 0xd, 1, 255, 3, 128}) //[0-ffffffffffffffff, cc, d, e, f] if t.isLenient() { // inet_aton t.testSubnetStringRange2("1.*.1", "1.0.0.1", "1.255.0.1", []interface{}{1, []uint{0, 0xff}, 1}) //[1, 0-255, 1] t.testSubnetStringRange2("*.1", "0.0.0.1", "255.0.0.1", []interface{}{[]uint{0, 0xff}, 1}) //[0-255, 1] t.testIncompatibleAddress2("a:b:cc:*.4", "a:b:cc:0:0:0:0.0.0.4", "a:b:cc:ffff:ffff:ffff:255.0.0.4", []interface{}{0xa, 0xb, 0xcc, []*big.Int{bigZeroConst(), setBigString("ffffffffffffff", 16)}, 4}) //[a, b, cc, 0-ffffffffffffff, 4] t.testIncompatibleAddress2("1:2:3:4:*.3.4", "1:2:3:4::0.3.0.4", "1:2:3:4:ffff:ffff:255.3.0.4", []interface{}{1, 2, 3, 4, []uint64{0, 0xffffffffff}, 3, 4}) //[1, 2, 3, 4, 0-ffffffffff, 3, 4] t.testIncompatibleAddress2("1:2:3:4:*.4", "1:2:3:4::0.0.0.4", "1:2:3:4:ffff:ffff:255.0.0.4", []interface{}{1, 2, 3, 4, []uint64{0, 0xffffffffff}, 4}) //[1, 2, 3, 4, 0-ffffffffff, 4] } else { // not inet_aton t.testSubnetStringRange2("1.*.1", "1.0.0.1", "1.255.255.1", []interface{}{1, []uint{0, 0xffff}, 1}) t.testSubnetStringRange2("*.1", "0.0.0.1", "255.255.255.1", []interface{}{[]uint{0, 0xffffff}, 1}) t.testIncompatibleAddress2("a:b:cc:*.4", "a:b:cc:0:0:0:0.0.0.4", "a:b:cc:ffff:ffff:ffff:255.255.255.4", []interface{}{0xa, 0xb, 0xcc, []*big.Int{bigZeroConst(), setBigString("ffffffffffffffffff", 16)}, 4}) //[a, b, cc, 0-ffffffffffffffffff, 4] t.testSubnetStringRange2("1:2:3:4:*.3.4", "1:2:3:4::0.0.3.4", "1:2:3:4:ffff:ffff:255.255.3.4", []interface{}{1, 2, 3, 4, []uint64{0, 0xffffffffffff}, 3, 4}) //[1, 2, 3, 4, 0-ffffffffffff, 3, 4] t.testIncompatibleAddress2("1:2:3:4:*.4", "1:2:3:4::0.0.0.4", "1:2:3:4:ffff:ffff:255.255.255.4", []interface{}{1, 2, 3, 4, []uint64{0, 0xffffffffffffff}, 4}) //[1, 2, 3, 4, 0-ffffffffffffff, 4] } t.testSubnetStringRange1("1-2.3.4-5.6", "1.3.4.6", "2.3.5.6", []interface{}{[]uint{1, 2}, 3, []uint{4, 5}, 6}, nil, false) //[1-2, 3, 4-5, 6] t.testSubnetStringRange1("1-2:3:4-5:6::", "1:3:4:6::", "2:3:5:6::", []interface{}{[]uint{1, 2}, 3, []uint{4, 5}, 6, 0}, nil, false) //[1-2, 3, 4-5, 6, 0] t.testIncompatibleAddress1("1:2:3:4:5:6:1-3.2.0.4-5", "1:2:3:4:5:6:1.2.0.4", "1:2:3:4:5:6:3.2.0.5", []interface{}{1, 2, 3, 4, 5, 6, []uint{1, 3}, 2, 0, []uint{4, 5}}, nil, false) //[1, 2, 3, 4, 5, 6, 1-3, 2, 0, 4-5] t.testMaskedIncompatibleAddress("0.0.0.*/0.0.0.128", "0.0.0.0", "0.0.0.128") //iae t.testSubnetStringRange1("1.2-3.4.5", "1.2.4.5", "1.3.4.5", []interface{}{1, []uint{2, 3}, 4, 5}, nil, false) //[1, 2-3, 4, 5] t.testSubnetStringRange1("1:2-3:4:5::", "1:2:4:5::", "1:3:4:5::", []interface{}{1, []uint{2, 3}, 4, 5, 0}, nil, false) //[1, 2-3, 4, 5, 0] t.testSubnetStringRange1("1:2:4:5:6-9:7:8:f", "1:2:4:5:6:7:8:f", "1:2:4:5:9:7:8:f", []interface{}{1, 2, 4, 5, []uint{6, 9}, 7, 8, 0xf}, nil, false) //[1, 2, 4, 5, 6-9, 7, 8, f] t.testIncompatibleAddress1("a:b:cc:dd:e:*.2.3.4", "a:b:cc:dd:e:0:0.2.3.4", "a:b:cc:dd:e:ffff:255.2.3.4", []interface{}{0xa, 0xb, 0xcc, 0xdd, 0xe, []uint{0, 0xffffff}, 2, 3, 4}, nil, false) // [a, b, cc, dd, e, 0-ffffff, 2, 3, 4] t.testIncompatibleAddress1("a:b:cc:dd:*.2.3.4", "a:b:cc:dd:0:0:0.2.3.4", "a:b:cc:dd:ffff:ffff:255.2.3.4", []interface{}{0xa, 0xb, 0xcc, 0xdd, []uint64{0, 0xffffffffff}, 2, 3, 4}, nil, false) // [a, b, cc, dd, 0-ffffffffff, 2, 3, 4] t.testIncompatibleAddress1("a:b:cc:*.2.3.4", "a:b:cc:0:0:0:0.2.3.4", "a:b:cc:ffff:ffff:ffff:255.2.3.4", []interface{}{0xa, 0xb, 0xcc, []uint64{0, 0xffffffffffffff}, 2, 3, 4}, nil, false) // [a, b, cc, 0-ffffffffffffff, 2, 3, 4] t.testSubnetStringRange1("1:2:4:5:6-9:7:8:f/ffff:0:ffff:0:ffff:0:ffff:0", "1:0:4:0:6:0:8:0", "1:0:4:0:9:0:8:0", []interface{}{1, 0, 4, 0, []uint{6, 9}, 0, 8, 0}, nil, false) //[1, 2, 4, 5, 6-9, 7, 8, f] t.testSubnetStringRange1("1:2:4:5-6:6:7:8:f/ffff:0:ffff:0:ffff:0:ffff:0", "1:0:4:0:6:0:8:0", "1:0:4:0:6:0:8:0", []interface{}{1, 0, 4, 0, 6, 0, 8, 0}, nil, true) //[1, 2, 4, 5, 6-9, 7, 8, f] t.testSubnetStringRange1("1.*.*.*/11", "1.0.0.0", "1.255.255.255", []interface{}{1, []uint{0, 0xff}, []uint{0, 0xff}, []uint{0, 0xff}}, p11, true) //[1, 0-255, 0-255, 0-255] t.testSubnetStringRange1("1.*.*/32", "1.0.0.0", "1.255.255.255", []interface{}{1, []uint{0, 0xff}, []uint{0, 0xffff}}, p32, true) //[1, 0-255, 0-65535] t.testSubnetStringRange1("1.*/24", "1.0.0.0", "1.255.255.255", []interface{}{1, []uint{0, 0xffffff}}, p24, true) //[1, 0-16777215] t.testSubnetStringRange("a:b:c:*:cc:d:e:f/64", "a:b:c:0:cc:d:e:f", "a:b:c:ffff:cc:d:e:f", []interface{}{0xa, 0xb, 0xc, []uint{0, 0xffff}, 0xcc, 0xd, 0xe, 0xf}, p64) //[a, b, c, 0-ffff, cc, d, e, f] t.testSubnetStringRange("a:*:cc:d:e:f/64", "a::cc:d:e:f", "a:ffff:ffff:ffff:cc:d:e:f", []interface{}{0xa, []uint64{0, 0xffffffffffff}, 0xcc, 0xd, 0xe, 0xf}, p64) //[a, 0-ffffffffffff, cc, d, e, f] t.testSubnetStringRange("*:cc:d:e:f/64", "::cc:d:e:f", "ffff:ffff:ffff:ffff:cc:d:e:f", []interface{}{[]*big.Int{bigZeroConst(), setBigString("ffffffffffffffff", 16)}, 0xcc, 0xd, 0xe, 0xf}, p64) //[0-ffffffffffffffff, cc, d, e, f] //prefix subnets t.testSubnetStringRange("a:*::/64", "a::", "a:ffff::ffff:ffff:ffff:ffff", []interface{}{0xa, []uint{0, 0xffff}, []*big.Int{bigZeroConst(), setBigString("ffffffffffffffff", 16)}}, p64) //[a, 0-ffffffffffff, cc, d, e, f] t.testSubnetStringRange("1.128.0.0/11", "1.128.0.0", "1.159.255.255", []interface{}{1, []uint{128, 159}, []uint{0, 0xff}, []uint{0, 0xff}}, p11) //[1, 0-255, 0-255, 0-255] if t.isLenient() { // inet_aton t.testSubnetStringRange("1.*.1/16", "1.0.0.1", "1.255.0.1", []interface{}{1, []uint{0, 0xff}, 1}, p16) //[1, 0-255, 1] t.testSubnetStringRange("*.1/16", "0.0.0.1", "255.0.0.1", []interface{}{[]uint{0, 0xff}, 1}, p16) //[0-255, 1] t.testIncompatibleAddress("a:b:cc:*.4/112", "a:b:cc:0:0:0:0.0.0.4", "a:b:cc:ffff:ffff:ffff:255.0.0.4", []interface{}{0xa, 0xb, 0xcc, []*big.Int{bigZeroConst(), setBigString("ffffffffffffff", 16)}, 4}, p112) //[a, b, cc, 0-ffffffffffffff, 4] t.testIncompatibleAddress("1:2:3:4:*.3.4/112", "1:2:3:4::0.3.0.4", "1:2:3:4:ffff:ffff:255.3.0.4", []interface{}{1, 2, 3, 4, []uint64{0, 0xffffffffff}, 3, 4}, p112) //[1, 2, 3, 4, 0-ffffffffff, 3, 4] t.testIncompatibleAddress("1:2:3:4:*.4/112", "1:2:3:4::0.0.0.4", "1:2:3:4:ffff:ffff:255.0.0.4", []interface{}{1, 2, 3, 4, []uint64{0, 0xffffffffff}, 4}, p112) //[1, 2, 3, 4, 0-ffffffffff, 4] // prefix subnet t.testIncompatibleAddress("a:b:cc:*.0/112", "a:b:cc:0:0:0:0.0.0.0", "a:b:cc:ffff:ffff:ffff:255.0.255.255", []interface{}{0xa, 0xb, 0xcc, []*big.Int{bigZeroConst(), setBigString("ffffffffffffff", 16)}, []uint{0, 0xffff}}, p112) //[a, b, cc, 0-ffffffffffffff, 4] } else { // not inet_aton t.testSubnetStringRange("1.*.1/16", "1.0.0.1", "1.255.255.1", []interface{}{1, []uint{0, 0xffff}, 1}, p16) t.testSubnetStringRange("*.1/16", "0.0.0.1", "255.255.255.1", []interface{}{[]uint{0, 0xffffff}, 1}, p16) t.testIncompatibleAddress("a:b:cc:*.4/112", "a:b:cc:0:0:0:0.0.0.4", "a:b:cc:ffff:ffff:ffff:255.255.255.4", []interface{}{0xa, 0xb, 0xcc, []*big.Int{bigZeroConst(), setBigString("ffffffffffffffffff", 16)}, 4}, p112) //[a, b, cc, 0-ffffffffffffffffff, 4] t.testSubnetStringRange("1:2:3:4:*.3.4/112", "1:2:3:4::0.0.3.4", "1:2:3:4:ffff:ffff:255.255.3.4", []interface{}{1, 2, 3, 4, []uint64{0, 0xffffffffffff}, 3, 4}, p112) //[1, 2, 3, 4, 0-ffffffffffff, 3, 4] t.testIncompatibleAddress("1:2:3:4:*.4/112", "1:2:3:4::0.0.0.4", "1:2:3:4:ffff:ffff:255.255.255.4", []interface{}{1, 2, 3, 4, []uint64{0, 0xffffffffffffff}, 4}, p112) //[1, 2, 3, 4, 0-ffffffffffffff, 4] // prefix subnet t.testSubnetStringRange("1:2:3:4:*.0.0/112", "1:2:3:4::0.0.0.0", "1:2:3:4:ffff:ffff:255.255.255.255", []interface{}{1, 2, 3, 4, []uint64{0, 0xffffffffffff}, []uint{0, 0xff}, []uint{0, 0xff}}, p112) //[1, 2, 3, 4, 0-ffffffffffffff, 4] } // prefix subnet t.testSubnetStringRange("a:b:cc::0.0.0.0/64", "a:b:cc:0:0:0:0.0.0.0", "a:b:cc::ffff:ffff:255.255.255.255", []interface{}{0xa, 0xb, 0xcc, []uint64{0, 0xffffffff}, []uint{0, 0xff}, []uint{0, 0xff}, []uint{0, 0xff}, []uint{0, 0xff}}, p64) //[a, b, cc, 0-ffffffffffffff, 4] t.testSubnetStringRange("1-2.3.4-5.6/16", "1.3.4.6", "2.3.5.6", []interface{}{[]uint{1, 2}, 3, []uint{4, 5}, 6}, p16) //[1-2, 3, 4-5, 6] t.testSubnetStringRange("1-2.3.4-5.0/23", "1.3.4.0", "2.3.5.0", []interface{}{[]uint{1, 2}, 3, []uint{4, 5}, 0}, p23) //[1-2, 3, 4-5, 6] t.testSubnetStringRange("1-2.3.4-5.0/24", "1.3.4.0", "2.3.5.255", []interface{}{[]uint{1, 2}, 3, []uint{4, 5}, []uint{0, 0xff}}, p24) //[1-2, 3, 4-5, 6] t.testSubnetStringRange("1-2:3:4-5:6::/48", "1:3:4:6::", "2:3:5:6::", []interface{}{[]uint{1, 2}, 3, []uint{4, 5}, 6, 0}, p48) //[1-2, 3, 4-5, 6, 0] t.testSubnetStringRange("1-2:3:4-5::/48", "1:3:4::", "2:3:5:ffff:ffff:ffff:ffff:ffff", []interface{}{[]uint{1, 2}, 3, []uint{4, 5}, []*big.Int{bigZeroConst(), setBigString("ffffffffffffffffffff", 16)}}, p48) //[1-2, 3, 4-5, 6, 0] t.testIncompatibleAddress("1:2:3:4:5:6:1-3.2.0.0/112", "1:2:3:4:5:6:1.2.0.0", "1:2:3:4:5:6:3.2.255.255", []interface{}{1, 2, 3, 4, 5, 6, []uint{1, 3}, 2, []uint{0, 0xff}, []uint{0, 0xff}}, p112) //[1, 2, 3, 4, 5, 6, 1-3, 2, 0, 4-5] t.testIncompatibleAddress("1:2:3:4:5:6:1-3.2.0.4-5/112", "1:2:3:4:5:6:1.2.0.4", "1:2:3:4:5:6:3.2.0.5", []interface{}{1, 2, 3, 4, 5, 6, []uint{1, 3}, 2, 0, []uint{4, 5}}, p112) //[1, 2, 3, 4, 5, 6, 1-3, 2, 0, 4-5] t.testSubnetStringRange1("1-3.1-3.1-3.1-3/175.80.81.83", "1.0.0.1", "3.0.1.3", []interface{}{[]int{1, 3}, 0, []int{0, 1}, []int{1, 3}}, nil, false) t.testMaskedIncompatibleAddress("*.*/202.63.240.51", "0.0.0.0", "202.63.240.51") //10101010 00111111 11110000 00110011 t.testMaskedIncompatibleAddress("*.*/63.240.51.202", "0.0.0.0", "63.240.51.202") t.testMaskedIncompatibleAddress("*.*/240.51.202.63", "0.0.0.0", "240.51.202.63") t.testMaskedIncompatibleAddress("*.*/51.202.63.240", "0.0.0.0", "51.202.63.240") t.testMaskedIncompatibleAddress("*.*.*.*/202.63.240.51", "0.0.0.0", "202.63.240.51") t.testMaskedIncompatibleAddress("*.*.*.*/63.240.51.202", "0.0.0.0", "63.240.51.202") t.testMaskedIncompatibleAddress("*.*.*.*/240.51.202.63", "0.0.0.0", "240.51.202.63") t.testMaskedIncompatibleAddress("*.*.*.*/51.202.63.240", "0.0.0.0", "51.202.63.240") t.testMaskedIncompatibleAddress("*:aaaa:bbbb:cccc/abcd:dcba:aaaa:bbbb:cccc::dddd", "::cccc", "abcd:dcba:aaaa:bbbb:cccc::cccc") t.testMaskedIncompatibleAddress("aaaa:bbbb:*:cccc/abcd:dcba:aaaa:bbbb:cccc::dddd", "aa88:98ba::cccc", "aa88:98ba:aaaa:bbbb:cccc::cccc") t.testMaskedIncompatibleAddress("aaaa:bbbb:*/abcd:dcba:aaaa:bbbb:cccc::dddd", "aa88:98ba::", "aa88:98ba:aaaa:bbbb:cccc::dddd") t.testMaskedIncompatibleAddress("*.*/63.255.15.0", "0.0.0.0", "63.255.15.0") t.testSubnetStringRange1("*.*/63.15.255.255", "0.0.0.0", "63.15.255.255", []interface{}{[]int{0, 63}, []int{0, 0xfffff}}, nil, false) t.testPrefix("25:51:27:*:*:*:*:*", nil, 48, p48) t.testPrefix("25:51:27:*:*:*:*:*/48", p48, 48, p48) t.testPrefix("25:50-51:27::/48", p48, 48, nil) t.testPrefix("25:50-51:27:*:*:*:*:*", nil, 48, nil) t.testPrefix("25:51:27:12:82:55:2:2", nil, 128, p128) t.testPrefix("*:*:*:*:*:*:*:*", nil, 0, p0) t.testPrefix("*:*:*:*:*:*:0-fe:*", nil, 112, nil) t.testPrefix("*:*:*:*:*:*:0-ff:*", nil, 104, nil) t.testPrefix("*:*:*:*:*:*:0-ffff:*", nil, 0, p0) t.testPrefix("*:*:*:*:*:*:0-7fff:*", nil, 97, nil) t.testPrefix("*:*:*:*:*:*:8000-ffff:*", nil, 97, nil) t.testPrefix("*.*.*.*", nil, 0, p0) t.testPrefix("3.*.*.*", nil, 8, p8) t.testPrefix("3.*.*.1-3", nil, 32, nil) t.testPrefix("3.0-127.*.*", nil, 9, p9) t.testPrefix("3.128-255.*.*", nil, 9, p9) t.testMasked("1.*.3.4", "", nil, "1.*.3.4") t.testMasked("1.*.3.4/255.255.1.0", "255.255.1.0", nil, "1.*.1.0") t.testMasked("1.*.3.4/255.255.254.0", "255.255.254.0", p23, "1.*.3.4/23") t.testMasked("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "", nil, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") t.testMasked("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0:101:0:101:0:101:0:101", "0:101:0:101:0:101:0:101", nil, "0:101:0:101:0:101:0:101") t.testMasked("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/ffff:ffff:8000::", "ffff:ffff:8000::", p33, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/33") t.testMasked("ffff:ffff::/ffff:ffff:8000::", "ffff:ffff:8000::", p33, "ffff:ffff::/33") t.testIPv4Wildcarded("1.2.3.4", 8, "1.2.3.4", "1.2.3.4") t.testIPv4Wildcarded("1.2.3.4", 9, "1.2.3.4", "1.2.3.4") t.testIPv4Wildcarded("1.2.3.4", 15, "1.2.3.4", "1.2.3.4") t.testIPv4Wildcarded("1.3.3.4", 15, "1.3.3.4", "1.3.3.4") t.testIPv4Wildcarded("1.2.3.4", 16, "1.2.3.4", "1.2.3.4") t.testWildcarded("1::1", 16, "1::1/16", "1:0:0:0:0:0:0:1", "1::1", "1::1", "1:0:0:0:0:0:0:1") t.testIPv4Wildcarded("1.3.0.0", 15, "1.3.0.0", "1.3.0.0") t.testIPv4Wildcarded("1.0.0.0", 8, "1.*.*.*", "1.%.%.%") t.testIPv4Wildcarded("1.0.0.0", 9, "1.0-127.*.*", "1.0-127.%.%") t.testIPv4Wildcarded("1.2.0.0", 15, "1.2-3.*.*", "1.2-3.%.%") t.testIPv4Wildcarded("1.2.0.0", 16, "1.2.*.*", "1.2.%.%") t.testWildcarded("1:0::", 32, "1::/32", "1:0:*:*:*:*:*:*", "1:0:*:*:*:*:*:*", "1::*:*:*:*:*:*", "1:0:%:%:%:%:%:%") t.testIPv6Wildcarded("1::", 16, "1::/16", "1:*:*:*:*:*:*:*", "1:%:%:%:%:%:%:%") t.testIPv6Wildcarded("1::", 20, "1::/20", "1:0-fff:*:*:*:*:*:*", "1:0-fff:%:%:%:%:%:%") t.testIPv6Wildcarded("1:f000::", 20, "1:f000::/20", "1:f000-ffff:*:*:*:*:*:*", "1:f___:%:%:%:%:%:%") t.testIPv6Wildcarded("1::", 17, "1::/17", "1:0-7fff:*:*:*:*:*:*", "1:0-7fff:%:%:%:%:%:%") t.testIPv6Wildcarded("1:10::", 28, "1:10::/28", "1:10-1f:*:*:*:*:*:*", "1:1_:%:%:%:%:%:%") t.testIPv6Wildcarded("1::", 28, "1::/28", "1:0-f:*:*:*:*:*:*", "1:_:%:%:%:%:%:%") t.testIPv6Wildcarded("1::", 31, "1::/31", "1:0-1:*:*:*:*:*:*", "1:0-1:%:%:%:%:%:%") t.testWildcarded("1::", 36, "1::/36", "1:0:0-fff:*:*:*:*:*", "1:0:0-fff:*:*:*:*:*", "1::0-fff:*:*:*:*:*", "1:0:0-fff:%:%:%:%:%") t.testWildcarded("1::", 52, "1::/52", "1:0:0:0-fff:*:*:*:*", "1::0-fff:*:*:*:*", "1::0-fff:*:*:*:*", "1:0:0:0-fff:%:%:%:%") t.testWildcarded("1::", 60, "1::/60", "1:0:0:0-f:*:*:*:*", "1::0-f:*:*:*:*", "1::0-f:*:*:*:*", "1:0:0:_:%:%:%:%") t.testIPv4Wildcarded("1.*.*.*", 8, "1.*.*.*", "1.%.%.%") t.testIPv4Wildcarded("1.0-127.*.*", 9, "1.0-127.*.*", "1.0-127.%.%") t.testWildcarded("1:0:*", 32, "1::/32", "1:0:*:*:*:*:*:*", "1:0:*:*:*:*:*:*", "1::*:*:*:*:*:*", "1:0:%:%:%:%:%:%") t.testIPv6Wildcarded("1:*", 16, "1::/16", "1:*:*:*:*:*:*:*", "1:%:%:%:%:%:%:%") t.testIPv6Wildcarded("1:0-fff:*", 20, "1::/20", "1:0-fff:*:*:*:*:*:*", "1:0-fff:%:%:%:%:%:%") t.testIPv6Wildcarded("1:f000-ffff:*", 20, "1:f000::/20", "1:f000-ffff:*:*:*:*:*:*", "1:f___:%:%:%:%:%:%") t.testIPv6Wildcarded("1:8000-ffff:*", 17, "1:8000::/17", "1:8000-ffff:*:*:*:*:*:*", "1:8000-ffff:%:%:%:%:%:%") t.testIPv6Wildcarded("1:10-1f:*", 28, "1:10::/28", "1:10-1f:*:*:*:*:*:*", "1:1_:%:%:%:%:%:%") t.testIPv6Wildcarded("1:0-f:*", 28, "1::/28", "1:0-f:*:*:*:*:*:*", "1:_:%:%:%:%:%:%") t.testIPv6Wildcarded("1:0-1:*", 31, "1::/31", "1:0-1:*:*:*:*:*:*", "1:0-1:%:%:%:%:%:%") t.testWildcarded("1:0:0-fff:*", 36, "1::/36", "1:0:0-fff:*:*:*:*:*", "1:0:0-fff:*:*:*:*:*", "1::0-fff:*:*:*:*:*", "1:0:0-fff:%:%:%:%:%") t.testWildcarded("1:0:0:0-fff:*", 52, "1::/52", "1:0:0:0-fff:*:*:*:*", "1::0-fff:*:*:*:*", "1::0-fff:*:*:*:*", "1:0:0:0-fff:%:%:%:%") t.testWildcarded("1:0:0:0-f:*", 60, "1::/60", "1:0:0:0-f:*:*:*:*", "1::0-f:*:*:*:*", "1::0-f:*:*:*:*", "1:0:0:_:%:%:%:%") t.testPrefixCount("1.2.3.*/31", 128) t.testPrefixCount("1.2.3.*/25", 2) t.testPrefixCount("1.2.*.4/31", 256) t.testPrefixCount("1.2.*.5/31", 256) t.testPrefixCount("1.2.*.4/23", 128) t.testPrefixCount("::1:2:*:4/111", 65536>>1) t.testPrefixCount("::1:2:*:4/107", 2048) t.testPrefixCount("*.2.*.4/23", 128*256) t.testPrefixCount("*.2.3.*/7", 128) t.testPrefixCount("2-3.2.3.*/8", 2) t.testPrefixCount("2-3.3-4.3.*/16", 4) t.testPrefixCount("2-3.3-4.3.*/12", 2) t.testPrefixCount("2-3.3-4.3.*", 256*2*2) t.testPrefixCount("2-3.3-4.3.*/32", 256*2*2) t.testPrefixCount("192.168.0.0-8/29", 2) t.testPrefixCount("192.168.0.0-15/29", 2) t.testPrefixCount("1.2.3.*/0", 1) t.testPrefixCount("1.2.3.4/0", 1) t.testPrefixCount("*.*/0", 1) t.testPrefixCount("*:*/0", 1) t.testPrefixCount("*.*/1", 2) t.testPrefixCount("*:*/1", 2) t.testCountExcludeZeros("1.2.3.4", 1, 1) t.testCountExcludeZeros("1.2.3.4/0", 1, 1) t.testCountExcludeZeros("1.2.3.4/32", 1, 0) t.testCountExcludeZeros("1.2.3.5/31", 1, 1) t.testCountExcludeZeros("1.2.3.4/31", 2, 1) t.testCountExcludeZeros("1.2.3.4/30", 4, 3) t.testCountExcludeZeros("1.2.3.6/30", 1, 1) t.testCountRangeParams("1.1-2.3.4", 2, 2, addrstrparam.WildcardAndRange) t.testCountExcludeZeros("1.2.3.0/24", 256, 255) t.testCountExcludeZeros("1.*.3.4", 256, 256) t.testCountExcludeZeros("1.2.252.0/22", 4*256, (4*256)-1) t.testCountExcludeZeros("1-2.2.252.0/22", 2*4*256, 2*((4*256)-1)) t.testRangeBlocks("1.1-3.*.*", 2, 3) t.testRangeBlocks("5-9.1-3.*.*", 2, 15) t.testRangeBlocks("1.1-3.*.1", 2, 3) t.testRangeBlocks("5-9.1-3.1.*", 2, 15) t.testRangeBlocks("5-9.0.0.0/9", 2, 5*128) t.testRangeBlocks("4-8.0.0.0/7", 2, 6*256) t.testRangeBlocks("1.128.0.0/12", 2, 16) t.testRangeBlocks("1.128.0.0/20", 2, 1) t.testRangeBlocks("5-9.1-3.1.0/9", 2, 15) t.testRangeBlocks("5-9.1-3.1.0/7", 2, 15) t.testRangeBlocks("5-9.0.0.0/7", 2, 5) t.testRangeBlocks("1.128.0.0/4", 2, 1) t.testRangeBlocks("1-3.1-3.1-3.1-3", 1, 3) t.testRangeBlocks("1-3.1-3.1-3.1-3", 2, 9) t.testRangeBlocks("1-3.1-3.1-3.1-3", 3, 27) t.testRangeBlocks("1-3.1-3.1-3.1-3", 4, 81) t.testRangeBlocks("1-3:1-3:1-3:1-3::", 1, 3) t.testRangeBlocks("1-3:1-3:1-3:1-3::", 2, 9) t.testRangeBlocks("1-3:1-3:1-3:1-3::", 3, 27) t.testRangeBlocks("1-3:1-3:1-3:1-3::", 4, 81) t.testRangeBlocks("1-3:1-3:1-3:1-3:*", 1, 3) t.testRangeBlocks("1-3:1-3:1-3:1-3:*", 2, 9) t.testRangeBlocks("1-3:1-3:1-3:1-3:*", 3, 27) t.testRangeBlocks("1-3:1-3:1-3:1-3:*", 4, 81) t.testRangeBlocks("::1-3:1-3:1-3:1-3", 5, 3) t.testRangeBlocks("::1-3:1-3:1-3:1-3", 6, 9) t.testRangeBlocks("::1-3:1-3:1-3:1-3", 7, 27) t.testRangeBlocks("::1-3:1-3:1-3:1-3", 8, 81) t.testRangeBlocks("1-3:1-3:1-3:1-3:1-3:1-3:1-3:1-3", 8, 81*81) t.testRangeBlocks("5-9:0:0:0::/17", 2, 5*0x8000) t.testRangeBlocks("4-8:0:0:0::/15", 2, 6*0x10000) t.testRangeBlocks("1:100:0:0::/24", 2, 256) t.testRangeBlocks("1:128:0:0::/36", 2, 1) t.testRangeBlocks("5-9:1-3:1:0::/17", 2, 15) t.testRangeBlocks("5-9:1-3:1:0::/15", 2, 15) t.testRangeBlocks("5-9:0:0:0::/15", 2, 5) t.testRangeBlocks("1:128:0:0::/12", 2, 1) t.testRangeBlocks("1:128:0:0::/24", 2, 1) t.testRangeCount("1.2.3.4", "1.2.3.4", 1) t.testRangeCount("1.2.3.4", "1.2.3.5", 2) t.testRangeCount("1.2.3.4", "1.2.3.6", 3) t.testRangeCount("1.2.3.255", "1.2.4.1", 3) t.testRangeCount("1.2.3.254", "1.2.4.0", 3) t.testRangeCount("1.2.3.254", "1.3.4.0", 3+256*256) //on the slow side, generating 180k+ addresses t.testRangeCountBig("0.0.0.0", "255.255.255.255", new(big.Int).SetUint64(256*256*256*256)) t.testRangeCountBig("0.0.0.0", "255.253.255.255", new(big.Int).SetUint64(255*16777216+253*65536+255*256+255+1)) bi := new(big.Int).SetUint64(255*16777216 + 253*65536 + 255*256 + 252) bi2 := new(big.Int).SetUint64(2*16777216 + 256) bi.Sub(bi, bi2) bi.Add(bi, bigOneConst()) t.testRangeCountBig("2.0.1.0", "255.253.255.252", bi) t.testRangeCount("::1:2:3:4", "::1:2:3:4", 1) t.testRangeCount("::1:2:3:4", "::1:2:3:5", 2) t.testRangeCount("::1:2:3:4", "::1:2:3:6", 3) t.testRangeCount("::1:2:3:ffff", "::1:2:4:1", 3) t.testRangeCount("::1:2:3:fffe", "::1:2:4:0", 3) t.testRangeCount("::1:2:3:4:1", "::1:2:3:4:1", 1) t.testRangeCount("::1:2:3:4:1", "::1:2:3:5:1", 0x10000+1) t.testRangeCount("::1:2:3:4:1", "::1:2:3:6:1", 2*0x10000+1) t.testRangeCount("::1:2:3:4:0", "::1:2:3:5:1", 0x10000+2) t.testRangeCount("::1:2:3:4:0", "::1:2:3:6:1", 2*0x10000+2) t.testRangeCount("::1:2:3:4:1", "::1:2:3:5:3", 0x10000+3) t.testRangeCount("::1:2:3:4:1", "::1:2:3:6:3", 2*0x10000+3) t.testRangeCount("::1:2:3:fffe", "::1:2:5:0", 3+0x10000) t.testRangeCount("::1:2:3:fffe", "::1:2:6:0", 3+0x20000) t.testRangePrefixCount("1.2.3.4", "1.2.3.4", 24, 1) t.testRangePrefixCount("1.2.3.4", "1.2.3.6", 24, 1) t.testRangePrefixCount("1.2.3.4", "1.2.3.6", 23, 1) t.testRangePrefixCount("1.2.3.4", "1.2.3.6", 25, 1) t.testRangePrefixCount("2.3.4.5", "2.3.6.5", 24, 3) t.testRangePrefixCount("2.3.4.5", "2.3.6.5", 22, 1) t.testRangePrefixCount("2.3.4.5", "2.3.6.5", 23, 2) t.testRangePrefixCount("2.3.255.5", "2.4.1.5", 25, 5) t.testRangePrefixCount("2.3.255.5", "2.4.0.5", 24, 2) t.testRangePrefixCount("2.3.255.5", "2.4.1.5", 24, 3) t.testRangePrefixCount("::1:2:3:fffe", "::1:2:5:0", 112, 3) if t.fullTest { t.testRangePrefixCount("::1:2:3:fffe", "::1:2:5:0", 128, 3+0x10000) t.testRangePrefixCount("::1:2:3:fffe", "::1:2:6:0", 128, 3+0x20000) } t.testRangePrefixCount("2:3:ffff:5::", "2:4:1:5::", 49, 5) t.testRangePrefixCount("2:3:ffff:5::", "2:4:0:5::", 48, 2) t.testRangePrefixCount("2:3:ffff:5::", "2:4:1:5::", 48, 3) //these can take a while, since they generate 48640, 65536, and 32758 addresses respectively t.testCountRangeParams("1.*.11-200.4", 190*256, 190*256, addrstrparam.WildcardAndRange) t.testCountExcludeZeros("1.3.*.4/16", 256, 256) t.testCountRangeParams("1.2.*.1-3/25", 256*3, 256*3, addrstrparam.WildcardAndRange) t.testCountRangeParams("1.2.*.0-2/25", 256*3, (256*3)-256, addrstrparam.WildcardAndRange) t.testCountRangeParams("11-13.*.0.0/23", 3*256*2*256, ((3*256)*(2*256))-(3*256), addrstrparam.WildcardAndRange) //this one test can take a while, since it generates (0xffff + 1) = 65536 addresses t.testCountExcludeZeros("*::1", 0xffff+1, 0xffff+1) t.testCountRangeParams("1-3::1", 3, 3, addrstrparam.WildcardAndRange) t.testCountRangeParams("0-299::1", 0x299+1, 0x299+1, addrstrparam.WildcardAndRange) //this one test can take a while, since it generates 3 * (0xffff + 1) = 196606 addresses t.testCountRangeParams("1:2:4:*:0-2::1", 3*(0xffff+1), 3*(0xffff+1), addrstrparam.WildcardAndRange) t.testCountRangeParams("1:2:4:0-2:0-2::1", 3*3, 3*3, addrstrparam.WildcardAndRange) t.testCountExcludeZeros("1::2:3", 1, 1) t.testCountExcludeZeros("1::2:3/128", 1, 0) t.testCountExcludeZeros("1::2:3/127", 1, 1) t.testPrefixCount("1::2/128", 1) t.testPrefixCount("1::2:*/127", 0x8000) t.testPrefixCount("1::2:*/113", 2) t.testPrefixCount("1::2:*/112", 1) t.testPrefixCount("*::2:*/112", 0x10000) t.testPrefixCount("*:1-3::2:*/112", 0x10000*3) t.testPrefixCount("*:1-3::2:*/0", 1) t.testCountExcludeZeros("1:2::fffc:0/110", 4*0x10000, (4*0x10000)-1) t.testCountExcludeZeros("1-2:2::fffc:0/110", 2*4*0x10000, 2*((4*0x10000)-1)) t.testCountExcludeZeros("*::", 0xffff+1, 0xffff+1) t.testCountExcludeZeros("::*", 0xffff+1, 0xffff+1) t.testCountExcludeZeros("0-199::0-199", (0x19a)*(0x19a), (0x19a)*(0x19a)) //bi := new(big.Int).SetUint64(255 * 16777216 + 253 * 65536 + 255 * 256 + 252) //bi2 := new(big.Int).SetUint64(2 * 16777216+ 256) //bi.Sub(bi, bi2) //bi.Add(bi, bigOneConst()) bi, _ = new(big.Int).SetString("ffffffffffffffffffffffffffffffff", 16) bi.Add(bi, bigOneConst()) t.testCountBig("*:*", bi, bi) bi, _ = new(big.Int).SetString("10000", 16) full := bi.Exp(bi, new(big.Int).SetInt64(8), nil) bi, _ = new(big.Int).SetString("10000", 16) half := bi.Exp(bi, new(big.Int).SetInt64(4), nil) t.testCountBig("*:*/64", full, new(big.Int).Sub(full, half)) t.testMerge("192.168.0.0/28", "192.168.0.0/29", "192.168.0.8/29") t.testMerge("1:2:3:4::/64", "1:2:3:4:8000::/65", "1:2:3:4::/66", "1:2:3:4:4000::/66") t.testMerge("1:2:3:4::/64", "1:2:3:4::/66", "1:2:3:4:8000::/65", "1:2:3:4:4000::/66") t.testMerge("1:2:3:4::/64", "1:2:3:4::/66", "1:2:3:4:4000::/66", "1:2:3:4:8000::/65") t.testMerge("1:2:3:4::/64", "1:2:3:4:4000::/66", "1:2:3:4::/66", "1:2:3:4:8000::/65") t.testMerge("1:2:3:4::/63", "1:2:3:4:8000::/65", "1:2:3:4::/66", "1:2:3:4:4000::/66", "1:2:3:5:4000::/66", "1:2:3:5::/66", "1:2:3:5:8000::/65") t.testMerge("1:2:3:4::/63", "1:2:3:4-5::/66", "1:2:3:4-5:8000::/65", "1:2:3:4-5:4000::/66") //[1:2:3:5::/65] t.testMerge2("1:2:3:4::/64", "1:2:3:6::/64", "1:2:3:4:8000::/65", "1:2:3:4::/66", "1:2:3:4:4000::/66", "1:2:3:6:4000::/66", "1:2:3:6::/66", "1:2:3:6:8000::/65") t.testMerge2("1.2.1.*", "1.2.2.*", "1.2.1.0", "1.2.2.0", "1.2.1-2.1-255") t.testMergeRange("1.2.1-2.*", "1.2.1.0", "1.2.2.0", "1.2.1-2.1-255") t.testMerge("*.*", "*.*", "1.2.3.4") t.testMerge("*.*", "1.2.3.4", "*.*") t.testMerge("*.*", "*.*", "*.*") t.testMerge("*:*", "*:*", "::") t.testMerge("*:*", "::", "*:*") t.testMerge("*:*", "*:*", "*:*") t.testMerge("*.*", "0.0.0.0/1", "128.0.0.0/1") t.testMerge("*.*", "128.0.0.0/1", "0.0.0.0/1") t.testMerge("128.0.0.0/1", "128.0.0.0/1", "128.0.0.0/1") t.testMerge("0.0.0.0/1", "0.0.0.0/1", "0.0.0.0/1") t.testMergeRange("*.*", "0.0.0.0/1", "128.0.0.0/1") t.testMergeRange("*.*", "128.0.0.0/1", "0.0.0.0/1") t.testMergeRange("128.0.0.0/1", "128.0.0.0/1", "128.0.0.0/1") t.testMergeRange("0.0.0.0/1", "0.0.0.0/1", "0.0.0.0/1") t.testMerge("*:*", "::/1", "8000::/1") t.testMerge("*:*", "8000::/1", "::/1") t.testMerge("8000::/1", "8000::/1", "8000::/1") t.testMerge("::/1", "::/1", "::/1") t.testMergeRange("*:*", "::/1", "8000::/1") t.testMergeRange("*:*", "8000::/1", "::/1") t.testMergeRange("8000::/1", "8000::/1", "8000::/1") t.testMergeRange("::/1", "::/1", "::/1") t.testMerge("0-127.*", "0-127.*", "1.2.3.4") t.testMergeRange("*.*", "*.*", "1.2.3.4") t.testMergeRange("*.*", "1.2.3.4", "*.*") t.testMergeRange("*.*", "*.*", "*.*") t.testMergeRange("*:*", "*:*", "::") t.testMergeRange("*:*", "::", "*:*") t.testMergeRange("*:*", "*:*", "*:*") t.testMergeRange("0-127.*", "0-127.*", "1.2.3.4") t.testMerge("1.2.3.4/32", "1.2.3.4") t.testMergeRange("1.2.3.4", "1.2.3.4") t.testMerge("192.168.0.0/28", "192.168.0.0", "192.168.0.1", "192.168.0.2", "192.168.0.3", "192.168.0.4", "192.168.0.5", "192.168.0.6", "192.168.0.7", "192.168.0.8", "192.168.0.9", "192.168.0.10", "192.168.0.11", "192.168.0.12", "192.168.0.13", "192.168.0.14", "192.168.0.15") t.testMerge("192.168.0.0/20", "192.168.12.*", "192.168.13.*", "192.168.14.*", "192.168.6.*", "192.168.7.*", "192.168.8.*", "192.168.3.*", "192.168.4.*", "192.168.5.*", "192.168.15.*", "192.168.9.*", "192.168.10.*", "192.168.11.*", "192.168.0.*", "192.168.1.*", "192.168.2.*") t.testMerge("0.0.0.0/4", "15.*", "12.*", "13.*", "14.*", "9.*", "10.*", "11.*", "6.*", "7.*", "8.*", "3.*", "4.*", "5.*", "0.*", "1.*", "2.*") t.testMerge("192.168.0.0/28", "192.168.0.0/29", "192.168.0.1/29", "192.168.0.2/29", "192.168.0.3/29", "192.168.0.4/29", "192.168.0.5/29", "192.168.0.6/29", "192.168.0.7/29", "192.168.0.8/29", "192.168.0.9/29", "192.168.0.10/29", "192.168.0.11/29", "192.168.0.12/29", "192.168.0.13/29", "192.168.0.14/29", "192.168.0.15/29") t.testMerge("1.2.2.0/23", "1.2.3.0/24", "1.2.2.0/24") //prefix at segment boundary t.testMerge("1.2.3.0/24", "1.2.3.128/25", "1.2.3.0/25") //prefix just beyond segment boundary t.testMerge("1.2.2.0/23", "1.2.3.0/24", "1.2.2.0/23") t.testMerge("1.2.2.0/23", "1.2.2.0/23", "1.2.3.0/24") t.testMerge("1.2.0.0/16", "1.2.0.0/16", "1.2.3.0/24") t.testMerge("1.2.3.0/24", "1.2.3.0/24", "1.2.3.0/24") t.testMerge2("1.2.3.0/24", "1.1.2.0/24", "1.2.3.0/24", "1.1.2.0/24") t.testMerge2("1.2.3.0/24", "1.2.6.0/24", "1.2.3.0/24", "1.2.6.0/24") t.testMerge2("1.2.3.0/24", "1.2.7.0/24", "1.2.3.0/24", "1.2.7.0/24") t.testMerge2("1.2.3.128/25", "1.2.2.0/25", "1.2.3.128/25", "1.2.2.0/25") t.testMerge("1.2.2-3.*/23", "1.2.3.*", "1.2.2.*") //prefix at segment boundary t.testMerge("1.2.3.*/24", "1.2.3.128-255", "1.2.3.0-127") //prefix just beyond segment boundary t.testMerge("1.2.2-3.*/23", "1.2.2-3.*", "1.2.3.*/24") t.testMerge("1.2.*.*/16", "1.2.*.*/16", "1.2.3.*/24") t.testMerge("1.2.3.*/24", "1.2.3.*/24", "1.2.3.*/24") t.testMerge("1.2.3.*/24", "1.2.3.*", "1.2.3.*") t.testMerge2("1.2.3.1/32", "1.2.3.2/32", "1.2.3.1-2") t.testMerge2("1.2.3.*/24", "1.1.2.*/24", "1.2.3.*/24", "1.1.2.*/24") t.testMerge2("1.2.3.*/24", "1.2.6.*/24", "1.2.3.*/24", "1.2.6.*/24") t.testMerge2("1.2.3.*/24", "1.2.7.*/24", "1.2.3.*/24", "1.2.7.*/24") t.testMerge2("1.2.3.128-255/25", "1.2.2.0-127/25", "1.2.3.128-255/25", "1.2.2.0-127/25") t.testMergeRange("1.2.3-4.*", "1.2.3.*", "1.2.4.*") t.testMergeRange("1.2.3-4.*", "1.2.3-4.*", "1.2.4.*") t.testMergeRange2("1.2.3-4.*", "2.2.3.*", "1-2.2.3.*", "1.2.4.*") t.testMergeRange2("1.2.3-4.*", "2.2.3.*", "1.2.3-4.*", "2.2.3.*") t.testMergeRange("1.0-25.*", "1.0-6.*", "1.4-25.*") t.testMergeRange("1-2.*", "1.0-6.*", "1.4-255.*", "2.*") t.testMergeRange("1-2:*", "1:0-6:*", "1:4-ffff:*", "2:*") t.testMergeRange("3.1-2.*", "3.1.0-6.*", "3.1.4-255.*", "3.2.*") t.testMergeRange("3:1-2:*", "3:1:0-6:*", "3:1:4-ffff:*", "3:2:*") t.testMergeRange("1.2.3.1-2", "1.2.3.1-2") t.testMergeRange2("1.2.2.1", "1.2.3.1", "1.2.2-3.1") t.testMergeRange2("1.2.3-4.*", "2.2.3-4.*", "1-2.2.3-4.*") t.testMergeRange2("1:2:3-4:*", "2:2:3-4:*", "1-2:2:3-4:*") //the following 4 are an example where prefix blocks require more addresses t.testMerge2("1.2.3.0/24", "1.2.4.0/23", "1.2.3.0/24", "1.2.4.0/24", "1.2.5.0/24") t.testMergeRange("1.2.3-5.*", "1.2.3.0/24", "1.2.4.0/24", "1.2.5.0/24") t.testMerge2("1.2.3.*", "1.2.4-5.*", "1.2.3.*", "1.2.4.*", "1.2.5.*") t.testMergeRange("1.2.3-5.*", "1.2.3.*", "1.2.4.*", "1.2.5.*") t.testMergeRange("1.2.3-5.*", "1.2.3.*", "1.2.4.*", "1.2.4.1-255", "1.2.5.*") t.testMergeRange2("1.2.3-5.*", "8.2.3-5.*", "1.2.3.*", "8.2.3.*", "1.2.4.*", "8.2.4.*", "8.2.5.*", "1.2.5.*") t.testMergeRange2("1.2.3-5.*", "1.7.4.1-255", "1.2.3.*", "1.2.4.*", "1.7.4.1-255", "1.2.5.*") t.testMergeRange2("1.2.3-5.*", "1.2.7.*", "1.2.3.*", "1.2.4.*", "1.2.7.*", "1.2.5.*") t.testMergeRange2("1::2:3-5:*", "8::2:3-5:*", "1::2:3:*", "8::2:3:*", "1::2:4:*", "8::2:4:*", "8::2:5:*", "1::2:5:*") t.testMergeRange2("1::2:3-5:*", "1::7:4:1-255", "1::2:3:*", "1::2:4:*", "1::7:4:1-255", "1::2:5:*") t.testMergeRange2("1:2:3-5:*", "8:2:3-5:*", "1:2:3:*", "8:2:3:*", "1:2:4:*", "8:2:4:*", "8:2:5:*", "1:2:5:*") t.testMergeRange2("1:2:3-5:*", "1:7:4:1-255:*", "1:2:3:*", "1:2:4:*", "1:7:4:1-255:*", "1:2:5:*") t.testMergeRange("1:2:2-9:*", "1:2:8-9:*", "1:2:6-8:*", "1:2:5-7:*", "1:2:2-4:*") t.testMergeRange2("1:2:2-9:*", "1:2:11-12:*", "1:2:8-9:*", "1:2:6-8:*", "1:2:11-12:*", "1:2:5-7:*", "1:2:2-4:*") t.testMergeRange("2-9:*", "8-9:*", "6-8:*", "5-7:*", "2-4:*") t.testMergeRange("::1:2:2-9:*", "::1:2:8-9:*", "::1:2:6-8:*", "::1:2:5-7:*", "::1:2:2-4:*") t.testMergeRange("::1:2:2-9", "::1:2:8-9", "::1:2:6-8", "::1:2:5-7", "::1:2:2-4") t.testMergeRange2("1.2.3.1-199", "1.2.3.201-255", "1.2.3.1-3", "1.2.3.4-199", "1.2.3.201-220", "1.2.3.210-255") if t.fullTest { t.testMergeSingles("1.2.3.*") t.testMergeSingles("1::2:*") t.testMerge("1.*.*.*", "1.1-254.1-254.*", "1.1-254.0-1.*", "1.1-254.255.*", "1.0.*.*", "1.253-255.*.*") t.testMergeRange("1.*.*.*", "1.1-254.1-254.*", "1.1-254.0-1.*", "1.1-254.255.*", "1.0.*.*", "1.253-255.*.*") t.testMerge2("1:1:*", "1:2:*", "1:2:1-fffe:*", "1:2:0-1:*", "1:2:ffff:*", "1:1:*") t.testMergeRange("1:1-2:*", "1:2:1-fffe:*", "1:2:0-1:*", "1:2:ffff:*", "1:1:*") t.testMerge("1:0-ff:*", "1:2:1-fffe:*", "1:2:0-1:*", "1:2:ffff:*", "1:1:*", "1:3-ff:*", "1:0:*") t.testMergeRange("1:0-ff:*", "1:2:1-fffe:*", "1:2:0-1:*", "1:2:ffff:*", "1:1:*", "1:3-ff:*", "1:0:*") t.testMerge("1:0-ff:*", "1:1-fe:1-fffe:*", "1:1-fe:0-1:*", "1:1-fe:ffff:*", "1:0:*", "1:0-ff:*") t.testMergeRange("1:0-ff:*", "1:1-fe:1-fffe:*", "1:1-fe:0-1:*", "1:1-fe:ffff:*", "1:0:*", "1:0-ff:*") } t.testSpanAndMerge("1.2.3.0", "1.2.3.1", 1, []string{"1.2.3.0/31"}, 1, []string{"1.2.3.0-1"}) //rangeCount t.testSpanAndMerge("1.2.3.4", "1.2.5.8", 9, []string{"1.2.3.4-7/30", "1.2.3.8-15/29", "1.2.3.16-31/28", "1.2.3.32-63/27", "1.2.3.64-127/26", "1.2.3.128-255/25", "1.2.4.0-255/24", "1.2.5.0-7/29", "1.2.5.8"}, 3, []string{"1.2.3.4-255", "1.2.4.*", "1.2.5.0-8"}) t.testSpanAndMerge("a:b:c:d:1::", "a:b:c:d:10::", 5, []string{"a:b:c:d:1::/80", "a:b:c:d:2::/79", "a:b:c:d:4::/78", "a:b:c:d:8::/77", "a:b:c:d:10::"}, 2, []string{"a:b:c:d:1-f:*:*:*", "a:b:c:d:10::"}) //[a:b:c:d:1::/80, a:b:c:d:2::/79, a:b:c:d:4::/78, a:b:c:d:8::/77, a:b:c:d:10::] t.testSpanAndMerge("a:b:c:d:1::/80", "a:b:c:d:10::", 5, []string{"a:b:c:d:1::/80", "a:b:c:d:2::/79", "a:b:c:d:4::/78", "a:b:c:d:8::/77", "a:b:c:d:10::"}, 2, []string{"a:b:c:d:1-f:*:*:*", "a:b:c:d:10::"}) t.testSpanAndMerge("a:b:c:d:2::", "a:b:c:d:10::", 4, []string{"a:b:c:d:2::/79", "a:b:c:d:4::/78", "a:b:c:d:8::/77", "a:b:c:d:10::"}, 2, []string{"a:b:c:d:2-f:*:*:*", "a:b:c:d:10::"}) t.testSpanAndMerge("a:b:c:d:2::", "a:b:c:d:10::/76", 4, []string{"a:b:c:d:2::/79", "a:b:c:d:4::/78", "a:b:c:d:8::/77", "a:b:c:d:10::/76"}, 1, []string{"a:b:c:d:2-1f:*:*:*"}) t.testSpanAndMerge("a:b:c:d:2::/79", "a:b:c:d:10::/76", 4, []string{"a:b:c:d:2::/79", "a:b:c:d:4::/78", "a:b:c:d:8::/77", "a:b:c:d:10::/76"}, 1, []string{"a:b:c:d:2-1f:*:*:*"}) //[a:b:c:d:2::/79, a:b:c:d:4::/78, a:b:c:d:8::/77, a:b:c:d:10::/76] t.testSpanAndMerge("1.2.3.0", "1.2.3.*", 1, []string{"1.2.3.*/24"}, 1, []string{"1.2.3.*/24"}) //rangeCount t.testCover("1.2.3.4", "1.2.4.4", "1.2.0.0/21") t.testCoverSingle("1.10-11.3.4", "1.10.0.0/15") t.testCover("0.0.1.1", "128.0.0.0", "*.*/0") t.testCover("0.0.1.1", "0.0.1.1", "0.0.1.1/32") t.testCover("0-1.0.1.1", "0-1.0.1.1", "0.0.0.0/7") t.testCoverSingle("0.0.1.1", "0.0.1.1/32") t.testCover("0.0.1.1", "0.0.1.0", "0.0.1.0-1/31") t.testCoverSingle("1.2.0.0/16", "1.2.0.0/16") t.testCoverSingle("1.2.0.1/16", "1.2.0.1/32") t.testCoverSingle("8000:a:b:c::/64", "8000:a:b:c::/64") t.testCover("8000::", "::", "*:*/0") t.testCover("*:0:*:0:*:0:*:0", "0:*:0:*:0:*:0:*", "*:*/0") t.testCover("0:0:*:0:*:0:*:0", "0:*:0:*:0:*:0:*", "0:*/16") t.testCover("0:0:0-63:0:*:0:*:0", "0:0:64:*:0:*:0:*", "0:0:0-7f:*/41") t.testCover("8000::/1", "::", "*:*/0") t.testCover("8000::/1", "::/64", "*:*/0") t.testCover("::1:ffff", "::1:ffff", "::1:ffff/128") t.testCover("::1", "::", "::0-1/127") t.testCoverSingle("ffff:ffff:ffff:ffff::/64", "ffff:ffff:ffff:ffff:*/64") t.ipAddressTester.run() } func setBigString(str string, base int) *big.Int { res, b := new(big.Int).SetString(str, base) if !b { panic("bad string for big int") } return res } func (t ipAddressRangeTester) ipv4rangestest(pass bool, x string, ipv4RangeOptions, ipv6RangeOptions addrstrparam.RangeParams) { t.iprangestest(pass, x, false, false, true, ipv4RangeOptions, ipv6RangeOptions) } func (t ipAddressRangeTester) ipv4rangetest(pass bool, x string, rangeOptions addrstrparam.RangeParams) { t.iprangetest(pass, x, false, false, true, rangeOptions) } func (t ipAddressRangeTester) ipv6rangestest(pass bool, x string, ipv4Options, ipv6Options addrstrparam.RangeParams) { t.iprangestest(pass, x, false, false, false, ipv4Options, ipv6Options) } func (t ipAddressRangeTester) ipv6rangetest(pass bool, x string, options addrstrparam.RangeParams) { t.iprangetest(pass, x, false, false, false, options) } func (t ipAddressRangeTester) iprangestest(pass bool, x string, isZero, notBoth, ipv4Test bool, ipv4RangeOptions, ipv6RangeOptions addrstrparam.RangeParams) { addr := t.createDoubleParametrizedAddress(x, ipv4RangeOptions, ipv6RangeOptions) if t.iptest(pass, addr, isZero, notBoth, ipv4Test) { //do it a second time to test the caching t.iptest(pass, addr, isZero, notBoth, ipv4Test) } } func (t ipAddressRangeTester) iprangetest(pass bool, x string, isZero, notBoth, ipv4Test bool, rangeOptions addrstrparam.RangeParams) { addr := t.createParametrizedAddress(x, rangeOptions) if t.iptest(pass, addr, isZero, notBoth, ipv4Test) { //do it a second time to test the caching t.iptest(pass, addr, isZero, notBoth, ipv4Test) } } func (t ipAddressRangeTester) testPrefix(original string, prefixLength ipaddr.PrefixLen, minPrefix ipaddr.BitCount, equivalentPrefix ipaddr.PrefixLen) { addr := t.createAddress(original).GetAddress() t.testBase.testPrefix(addr, prefixLength, minPrefix, equivalentPrefix) t.incrementTestCount() } func (t ipAddressRangeTester) testMasked(masked, mask string, prefixLength ipaddr.PrefixLen, result string) { maskedAddrStr := t.createAddress(masked) maskedAddr := maskedAddrStr.GetAddress() var maskAddr *ipaddr.IPAddress if mask != "" { maskAddr = t.createAddress(mask).GetAddress() } resultAddr := t.createAddress(result).GetAddress() if !maskedAddr.Equal(resultAddr) { t.addFailure(newIPAddrFailure("masked "+maskedAddr.String()+" instead of expected "+resultAddr.String(), maskedAddr)) } if !maskAddr.Equal(maskedAddrStr.GetMask()) { t.addFailure(newIPAddrFailure("masked "+maskAddr.String()+" instead of expected "+maskedAddrStr.GetMask().String(), maskedAddr)) } if !resultAddr.GetNetworkPrefixLen().Equal(prefixLength) { t.addFailure(newIPAddrFailure("masked prefix length was "+resultAddr.GetNetworkPrefixLen().String()+" instead of expected "+prefixLength.String(), maskedAddr)) } t.incrementTestCount() } func (t ipAddressRangeTester) testIPv4Wildcarded(original string, bits ipaddr.BitCount, expected, expectedSQL string) { t.testWildcarded(original, bits, expected, expected, expected, expected, expectedSQL) } func (t ipAddressRangeTester) testIPv6Wildcarded(original string, bits ipaddr.BitCount, expectedSubnet, expectedNormalizedCompressedCanonical, expectedSQL string) { all := expectedNormalizedCompressedCanonical t.testWildcarded(original, bits, expectedSubnet, all, all, all, expectedSQL) } func (t ipAddressRangeTester) testWildcarded(original string, bits ipaddr.BitCount, expectedSubnet, expectedNormalized, expectedCanonical, expectedCompressed, expectedSQL string) { w := t.createAddress(original) addr := w.GetAddress() if addr.GetNetworkPrefixLen() == nil || addr.GetNetworkPrefixLen().Len() > bits { addr = addr.SetPrefixLen(bits) if addr.IsZeroHost() { addr = addr.ToPrefixBlock() } } str := addr.ToCompressedWildcardString() if str != (expectedCompressed) { t.addFailure(newFailure("failed expected: "+expectedCompressed+" actual: "+str, w)) } else { w2 := t.createAddress(original + "/" + strconv.Itoa(int(bits))) addr2 := w2.GetAddress() str = addr2.ToCompressedWildcardString() if str != expectedCompressed { t.addFailure(newFailure("failed expected: "+expectedCompressed+" actual: "+str, w)) } else { str = addr.ToNormalizedWildcardString() if str != (expectedNormalized) { t.addFailure(newFailure("failed expected: "+expectedNormalized+" actual: "+str, w)) } else { str = addr2.ToNormalizedWildcardString() if str != (expectedNormalized) { t.addFailure(newFailure("failed expected: "+expectedNormalized+" actual: "+str, w)) } else { str = addr.ToCanonicalWildcardString() if str != (expectedCanonical) { t.addFailure(newFailure("failed expected: "+expectedCanonical+" actual: "+str, w)) } else { str = addr.ToSubnetString() if str != (expectedSubnet) { t.addFailure(newFailure("failed expected: "+expectedSubnet+" actual: "+str, w)) } else { str = addr2.ToSubnetString() if str != (expectedSubnet) { t.addFailure(newFailure("failed expected: "+expectedSubnet+" actual: "+str, w)) } else { str = addr2.ToSQLWildcardString() if str != (expectedSQL) { t.addFailure(newFailure("failed expected: "+expectedSQL+" actual: "+str, w)) } } } } } } } } t.incrementTestCount() } const countLimit = 1024 func (t ipAddressRangeTester) testPrefixCount(original string, number uint64) { w := t.createAddress(original) t.testPrefixCountImpl(w.Wrap(), number) } func (t ipAddressRangeTester) testCountRangeParams(original string, number, excludeZerosNumber uint64, rangeOptions addrstrparam.RangeParams) { w := t.createParametrizedAddress(original, rangeOptions) t.testCountRedirect(w.Wrap(), number, excludeZerosNumber) } func (t ipAddressRangeTester) testCountExcludeZeros(original string, number, excludeZerosNumber uint64) { w := t.createAddress(original) t.testCountRedirect(w.Wrap(), number, excludeZerosNumber) } func (t ipAddressRangeTester) testCountBig(original string, number, excludeZerosNumber *big.Int) { w := t.createAddress(original) t.testCountBigExcludeZeros(w, number, false) if excludeZerosNumber.Sign() != -1 { t.testCountBigExcludeZeros(w, excludeZerosNumber, true) } } func getNonZeroHostIterator(val *ipaddr.IPAddress) ipaddr.Iterator[*ipaddr.IPAddress] { return ipaddr.NewFilteredIPAddrIterator(val.Iterator(), (*ipaddr.IPAddress).IsZeroHost) } func getNonZeroHostCount(val *ipaddr.IPAddress) *big.Int { count := val.GetCount() if !val.IsPrefixed() || val.GetNetworkPrefixLen().Len() > val.GetBitCount() { return count } if !val.IncludesZeroHost() { return count } return new(big.Int).Sub(val.GetCount(), val.GetPrefixCount()) } func (t ipAddressRangeTester) testCountBigExcludeZeros(w *ipaddr.IPAddressString, number *big.Int, excludeZeroHosts bool) { val := w.GetAddress() var count *big.Int if excludeZeroHosts { count = getNonZeroHostCount(val) } else { count = val.GetCount() } if count.Cmp(number) != 0 { t.addFailure(newFailure("big count was "+count.String(), w)) } t.incrementTestCount() } func (t ipAddressRangeTester) testRangeCount(low, high string, number uint64) { w := t.createAddress(low) w2 := t.createAddress(high) t.testRangeCountImpl(w, w2, number) } func (t ipAddressRangeTester) testRangeCountBig(low, high string, number *big.Int) { w := t.createAddress(low) w2 := t.createAddress(high) t.testRangeCountR(w, w2, number) } func (t ipAddressRangeTester) testRangeCountR(w, high *ipaddr.IPAddressString, number *big.Int) { val := w.GetAddress().SpanWithRange(high.GetAddress()) count := val.GetCount() if count.Cmp(number) != 0 { t.addFailure(newFailure("big count was "+count.String(), w)) } t.incrementTestCount() } func (t ipAddressRangeTester) testRangeCountImpl(w, high *ipaddr.IPAddressString, number uint64) { if !t.fullTest && number > countLimit { return } val := w.GetAddress().SpanWithRange(high.GetAddress()) count := val.GetCount() if count.Cmp(new(big.Int).SetUint64(number)) != 0 { t.addFailure(newFailure("count was "+count.String()+" instead of expected count "+strconv.FormatUint(number, 10), w)) } else { addrIterator := val.Iterator() var counter uint64 var set []*ipaddr.IPAddress //Set
set = new HashSet
(); var next *ipaddr.IPAddress for addrIterator.HasNext() { next = addrIterator.Next() if counter == 0 { lower := val.GetLower() if !next.Equal(lower) { t.addFailure(newIPAddrFailure("lowest: "+lower.String()+" next: "+next.String(), next)) } } set = append(set, next) counter++ } if number < uint64(maxInt) && len(set) != int(number) { t.addFailure(newSeqRangeFailure("set count was "+strconv.Itoa(len(set))+" instead of expected "+strconv.FormatUint(number, 10), val)) } else if counter != number { t.addFailure(newSeqRangeFailure("set count was "+strconv.Itoa(len(set))+" instead of expected "+strconv.FormatUint(number, 10), val)) } else if number > 0 { if !next.Equal(val.GetUpper()) { t.addFailure(newIPAddrFailure("highest: "+val.GetUpper().String(), next)) } else { lower := val.GetLower() if counter == 1 && !val.GetUpper().Equal(lower) { t.addFailure(newIPAddrFailure("highest: "+val.GetUpper().String()+" lowest: "+val.GetLower().String(), next)) } } } else { t.addFailure(newFailure("unexpected zero count "+val.String(), w)) } } t.incrementTestCount() } func (t ipAddressRangeTester) testRangePrefixCount(low, high string, prefixLength ipaddr.BitCount, number uint64) { w := t.createAddress(low) w2 := t.createAddress(high) t.testRangePrefixCountImpl(w, w2, prefixLength, number) } func (t ipAddressRangeTester) testRangePrefixCountImpl(w, high *ipaddr.IPAddressString, prefixLength ipaddr.BitCount, number uint64) { if !t.fullTest && number > countLimit { return } val := w.GetAddress().SpanWithRange(high.GetAddress()) count := val.GetPrefixCountLen(prefixLength) var prefixSet, prefixBlockSet []ipaddr.AddressItem if count.Cmp(new(big.Int).SetUint64(number)) != 0 { t.addFailure(newFailure("count was "+count.String()+" instead of expected count "+strconv.FormatUint(number, 10), w)) } else { addrIterator := val.PrefixBlockIterator(prefixLength) var counter uint64 var next, previous *ipaddr.IPAddress set := prefixBlockSet for addrIterator.HasNext() { next = addrIterator.Next() if !next.IsPrefixBlock() { t.addFailure(newIPAddrFailure("not prefix block next: "+next.String(), next)) break } if !next.IsSinglePrefixBlock() { t.addFailure(newIPAddrFailure("not single prefix block next: "+next.String(), next)) break } if previous != nil && next.Intersect(previous) != nil { t.addFailure(newIPAddrFailure("intersection of "+previous.String()+" when iterating: "+next.Intersect(previous).String(), next)) break } set = append(set, next) previous = next counter++ } if number < uint64(maxInt) && len(set) != int(number) { t.addFailure(newSeqRangeFailure("set count was "+strconv.Itoa(len(set))+" instead of expected "+strconv.FormatUint(number, 10), val)) } else if counter != number { t.addFailure(newSeqRangeFailure("set count was "+strconv.Itoa(len(set))+" instead of expected "+strconv.FormatUint(number, 10), val)) } else if number < 0 { t.addFailure(newSeqRangeFailure("unexpected zero count ", val)) } totalCount := val.GetCount() countedCount := bigZero() rangeIterator := val.PrefixIterator(prefixLength) counter = 0 rangeSet := prefixSet var nextRange, previousRange *ipaddr.IPAddressSeqRange for rangeIterator.HasNext() { nextRange = rangeIterator.Next() blocks := nextRange.SpanWithPrefixBlocks() if previous != nil && addrIterator.HasNext() { if len(blocks) != 1 { t.addFailure(newSeqRangeFailure("not prefix next: "+nextRange.String(), nextRange)) break } if !blocks[0].IsSinglePrefixBlock() { t.addFailure(newSeqRangeFailure("not single prefix next: "+nextRange.String(), nextRange)) break } } countedCount.Add(countedCount, nextRange.GetCount()) if previousRange != nil && nextRange.Intersect(previousRange) != nil { t.addFailure(newSeqRangeFailure("intersection of "+previousRange.String()+" when iterating: "+nextRange.Intersect(previousRange).String(), nextRange)) break } rangeSet = append(rangeSet, nextRange) previousRange = nextRange //System.out.println(next); counter++ } if number < uint64(maxInt) && len(rangeSet) != int(number) { t.addFailure(newSeqRangeFailure("set count was "+strconv.Itoa(len(rangeSet))+" instead of expected "+strconv.FormatUint(number, 10), val)) } else if counter != number { t.addFailure(newSeqRangeFailure("set count was "+strconv.Itoa(len(rangeSet))+" instead of expected "+strconv.FormatUint(number, 10), val)) } else if number < 0 { t.addFailure(newSeqRangeFailure("unexpected zero count ", val)) } else if countedCount.Cmp(totalCount) != 0 { t.addFailure(newSeqRangeFailure("count mismatch, expected "+totalCount.String()+" got "+countedCount.String(), val)) } //Function> spliteratorFunc = // range -> range.prefixBlockSpliterator(prefixLength); // //testSpliterate(t, val, 0, number, spliteratorFunc); //testSpliterate(t, val, 1, number, spliteratorFunc); //testSpliterate(t, val, 8, number, spliteratorFunc); //testSpliterate(t, val, -1, number, spliteratorFunc); // //spliteratorFunc = range -> range.prefixSpliterator(prefixLength); // //testSpliterate(t, val, 0, number, spliteratorFunc); //testSpliterate(t, val, 1, number, spliteratorFunc); //testSpliterate(t, val, 8, number, spliteratorFunc); //testSpliterate(t, val, -1, number, spliteratorFunc); // //testStream(t, val, prefixSet, range -> range.prefixStream(prefixLength)); //testStream(t, val, prefixBlockSet, range -> range.prefixBlockStream(prefixLength)); } t.incrementTestCount() } func (t ipAddressRangeTester) testRangeBlocks(original string, segmentCount int, number uint64) { w := t.createAddress(original) t.testRangeBlocksImpl(w, segmentCount, number) } func (t ipAddressRangeTester) testRangeBlocksImpl(w *ipaddr.IPAddressString, segmentCount int, number uint64) { if !t.fullTest && number > countLimit { return } val := w.GetAddress() count := val.GetBlockCount(segmentCount) var set []ipaddr.AddressItem if count.Cmp(new(big.Int).SetUint64(number)) != 0 { t.addFailure(newFailure("count was "+count.String()+" instead of expected count "+strconv.FormatUint(number, 10), w)) } else { addrIterator := val.BlockIterator(segmentCount) var counter, sectionCounter uint64 valSection := val.GetSubSection(0, segmentCount) sectionIterator := valSection.Iterator() var next *ipaddr.IPAddress var nextSection *ipaddr.IPAddressSection for addrIterator.HasNext() { next = addrIterator.Next() nextSection = sectionIterator.Next() if counter == 0 { lower := val.GetLower() lowerSection := lower.GetSubSection(0, segmentCount) nextAddrSection := next.GetSubSection(0, segmentCount) if !nextAddrSection.Equal(lowerSection) || !lowerSection.Equal(nextAddrSection) { t.addFailure(newSegmentSeriesFailure("lowest: "+lower.String()+" next addr: "+nextAddrSection.String(), nextAddrSection)) } if !nextSection.Equal(lowerSection) || !lowerSection.Equal(nextSection) { t.addFailure(newSegmentSeriesFailure("lowest: "+lower.String()+" next sectiob: "+nextSection.String(), nextSection)) } if !nextSection.Equal(nextAddrSection) || !nextAddrSection.Equal(nextSection) { t.addFailure(newSegmentSeriesFailure("nextAddrSection: "+nextAddrSection.String()+" next section: "+nextSection.String(), nextSection)) } if !next.GetPrefixLen().Equal(val.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("val prefix length: "+val.GetPrefixLen().String()+" lowest prefix length: "+next.GetPrefixLen().String(), next)) } if !lower.GetPrefixLen().Equal(val.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("val prefix length: "+val.GetPrefixLen().String()+" lowest prefix length: "+lower.GetPrefixLen().String(), lower)) } } else if counter == 1 { if !next.GetPrefixLen().Equal(val.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("val prefix length: "+val.GetPrefixLen().String()+" next prefix length: "+next.GetPrefixLen().String(), next)) } } set = append(set, next) counter++ sectionCounter++ } if number < uint64(maxInt) && len(set) != int(number) { t.addFailure(newFailure("set count was "+strconv.Itoa(len(set))+" instead of expected "+strconv.Itoa(int(number)), w)) } else if sectionIterator.HasNext() { t.addFailure(newFailure("counter mismatch, count was "+strconv.FormatUint(counter, 10)+" section count "+strconv.FormatUint(sectionCounter, 10), w)) } else if number > 0 { upperSection := val.GetUpper().GetSubSection(0, segmentCount) nextAddrSection := next.GetSubSection(0, segmentCount) if !nextAddrSection.Equal(upperSection) || !upperSection.Equal(nextAddrSection) { t.addFailure(newSegmentSeriesFailure("highest: "+upperSection.String()+" next addr: "+nextAddrSection.String(), nextAddrSection)) } if !nextSection.Equal(upperSection) || !upperSection.Equal(nextSection) { t.addFailure(newSegmentSeriesFailure("highest: "+upperSection.String()+" next section: "+nextSection.String(), nextSection)) } else { lower := val.GetLower() lowerSection := lower.GetSubSection(0, segmentCount) if counter == 1 && !upperSection.Equal(lowerSection) { t.addFailure(newIPAddrFailure("highest: "+val.GetUpper().String()+" lowest: "+val.GetLower().String(), next)) } if !next.GetPrefixLen().Equal(val.GetPrefixLen()) { t.addFailure(newIPAddrFailure("val prefix length: "+val.GetPrefixLen().String()+" upper prefix length: "+next.GetPrefixLen().String(), next)) } if !val.GetUpper().GetPrefixLen().Equal(val.GetPrefixLen()) { t.addFailure(newIPAddrFailure("val prefix length: "+val.GetPrefixLen().String()+" upper prefix length: "+val.GetUpper().GetPrefixLen().String(), next)) } } } else { t.addFailure(newIPAddrFailure("unexpected zero count ", val)) } //Function> spliteratorFunc = addr -> addr.blockSpliterator(segmentCount); // //testSpliterate(t, val, 0, number, spliteratorFunc); //testSpliterate(t, val, 1, number, spliteratorFunc); //testSpliterate(t, val, 5, number, spliteratorFunc); //testSpliterate(t, val, -1, number, spliteratorFunc); // //testStream(t, val, set, addr -> addr.blockStream(segmentCount)); } t.incrementTestCount() } func (t ipAddressRangeTester) testSpanAndMerge(address1, address2 string, count int, expected []string, rangeCount int, rangeExpected []string) { string1 := t.createAddress(address1) string2 := t.createAddress(address2) addr1 := string1.GetAddress() addr2 := string2.GetAddress() result := addr1.SpanWithPrefixBlocksTo(addr2) resultList := result var expectedList []*ipaddr.IPAddress for _, s := range expected { expectedList = append(expectedList, t.createAddress(s).GetAddress()) } if !ipaddr.AddrsMatchOrdered(resultList, expectedList) { t.addFailure(newIPAddrFailure("merge mismatch merging "+addr1.String()+" and "+addr2.String()+" into "+asSliceString(resultList)+" expected "+asSliceString(expectedList), addr1)) } if count != len(result) { t.addFailure(newIPAddrFailure("merge mismatch merging "+addr1.String()+" and "+addr2.String()+" into "+asSliceString(resultList)+" expected count of "+strconv.Itoa(count), addr1)) } for _, addr := range result { if !addr.IsPrefixed() || !addr.IsPrefixBlock() { t.addFailure(newIPAddrFailure("merged addr "+addr.String()+" is not prefix block", addr)) } } result2 := addr1.SpanWithSequentialBlocksTo(addr2) resultList = result2 expectedList = expectedList[:0] for _, s := range rangeExpected { expectedList = append(expectedList, t.createAddress(s).GetAddress()) } if !ipaddr.AddrsMatchOrdered(resultList, expectedList) { t.addFailure(newIPAddrFailure("range merge mismatch merging "+addr1.String()+" and "+addr2.String()+" into "+asSliceString(resultList)+" expected "+asSliceString(expectedList), addr1)) } if rangeCount != len(result2) { t.addFailure(newIPAddrFailure("range merge mismatch merging "+addr1.String()+" and "+addr2.String()+" into "+asSliceString(resultList)+" expected count of "+strconv.Itoa(rangeCount), addr1)) } for _, addr := range result2 { if addr.IsPrefixed() { t.addFailure(newIPAddrFailure("merged addr "+addr.String()+" is prefixed", addr)) } } backAgain := result[0].MergeToPrefixBlocks(result...) matches := ipaddr.AddrsMatchOrdered(result, backAgain) if !matches { t.addFailure(newIPAddrFailure("merge mismatch merging "+addr1.String()+" and "+addr2.String()+" into "+asSliceString(result)+" and "+asSliceString(backAgain), addr1)) } backAgain = result[len(result)-1].MergeToPrefixBlocks(result...) matches = ipaddr.AddrsMatchOrdered(result, backAgain) if !matches { t.addFailure(newIPAddrFailure("merge mismatch merging "+addr1.String()+" and "+addr2.String()+" into "+asSliceString(result)+" and "+asSliceString(backAgain), addr1)) } if len(result) > 2 { backAgain = result[len(result)/2].MergeToPrefixBlocks(result...) matches = ipaddr.AddrsMatchOrdered(result, backAgain) if !matches { t.addFailure(newIPAddrFailure("merge mismatch merging "+addr1.String()+" and "+addr2.String()+" into "+asSliceString(result)+" and "+asSliceString(backAgain), addr1)) } } backAgain = result2[0].MergeToSequentialBlocks(result2...) matches = ipaddr.AddrsMatchOrdered(result2, backAgain) if !matches { t.addFailure(newIPAddrFailure("merge mismatch merging "+addr1.String()+" and "+addr2.String()+" into "+asSliceString(result2)+" and "+asSliceString(backAgain), addr1)) } backAgain = result2[len(result2)-1].MergeToSequentialBlocks(result2...) matches = ipaddr.AddrsMatchOrdered(result2, backAgain) if !matches { t.addFailure(newIPAddrFailure("merge mismatch merging "+addr1.String()+" and "+addr2.String()+" into "+asSliceString(result2)+" and "+asSliceString(backAgain), addr1)) } if len(result2) > 2 { backAgain = result2[len(result2)/2].MergeToSequentialBlocks(result2...) matches = ipaddr.AddrsMatchOrdered(result2, backAgain) if !matches { t.addFailure(newIPAddrFailure("merge mismatch merging "+addr1.String()+" and "+addr2.String()+" into "+asSliceString(result2)+" and "+asSliceString(backAgain), addr1)) } } var rangeList []*ipaddr.IPAddressSeqRange for _, a := range result { rng := a.ToSequentialRange() rangeList = append(rangeList, rng) } joined := rangeList[0].Join(rangeList...) if len(joined) == 0 || len(joined) > 1 || !joined[0].GetLower().Equal(addr1.GetLower()) || !joined[0].GetUpper().Equal(addr2.GetUpper()) { t.addFailure(newIPAddrFailure("joined range "+asRangeSliceString(joined)+" did not match "+addr1.String()+" and "+addr2.String(), addr1)) } rangeList = rangeList[:0] for _, a := range result2 { rng := a.ToSequentialRange() rangeList = append(rangeList, rng) } joined = rangeList[0].Join(rangeList...) if len(joined) == 0 || len(joined) > 1 || !joined[0].GetLower().Equal(addr1.GetLower()) || !joined[0].GetUpper().Equal(addr2.GetUpper()) { t.addFailure(newIPAddrFailure("joined range "+asRangeSliceString(joined)+" did not match "+addr1.String()+" and "+addr2.String(), addr1)) } t.incrementTestCount() } func (t ipAddressRangeTester) testMergeSingles(addrStr string) { resultStr := t.createAddress(addrStr) addr := resultStr.GetAddress() iter := addr.Iterator() var addrs []*ipaddr.IPAddress for iter.HasNext() { addrs = append(addrs, iter.Next()) } rand.Seed(time.Now().UnixNano()) rand.Shuffle(len(addrs), func(i, j int) { addrs[i], addrs[j] = addrs[j], addrs[i] }) arr := addrs first := addrs[len(addrs)/2] result := first.MergeToPrefixBlocks(arr...) if len(result) != 1 { t.addFailure(newIPAddrFailure("merged addresses "+asSliceString(result)+" is not "+addrStr, addr)) } else if !addr.Equal(result[0]) { t.addFailure(newIPAddrFailure("merged address "+result[0].String()+" is not "+addrStr, addr)) } merged2 := getMergedPrefixBlocksAltMerge(arr) merged3 := getMergedPrefixBlocksAltRange(arr) merged4 := getMergedPrefixBlocksAltRange2(arr) if len(merged2) != 1 || !result[0].Equal(merged2[0]) { t.addFailure(newIPAddrFailure("merge prefix mismatch merging, expected "+asSliceString(result)+" got "+asSliceString(merged2), result[0])) } if len(merged3) != 1 || !result[0].Equal(merged3[0]) { t.addFailure(newIPAddrFailure("merge prefix mismatch merging, expected "+asSliceString(result)+" got "+asSliceString(merged3), result[0])) } if len(merged4) != 1 || !result[0].Equal(merged4[0]) { t.addFailure(newIPAddrFailure("merge prefix mismatch merging, expected "+asSliceString(result)+" got "+asSliceString(merged4), result[0])) } result = addrs[len(addrs)/2].MergeToSequentialBlocks(arr...) if len(result) != 1 { t.addFailure(newIPAddrFailure("merged addresses "+asSliceString(result)+" is not "+addrStr, addr)) } else if !addr.Equal(result[0]) { t.addFailure(newIPAddrFailure("merged address "+result[0].String()+" is not "+addrStr, addr)) } t.incrementTestCount() } func (t ipAddressRangeTester) testMergeRange(result string, addresses ...string) { t.testMergeImpl(result, false, addresses...) } func (t ipAddressRangeTester) testMergeRange2(result string, result2 string, addresses ...string) { t.testMerge2Impl(result, result2, false, addresses...) } func (t ipAddressRangeTester) testMerge(result string, addresses ...string) { t.testMergeImpl(result, true, addresses...) } func (t ipAddressRangeTester) testMerge2(result string, result2 string, addresses ...string) { t.testMerge2Impl(result, result2, true, addresses...) } func getMergedPrefixBlocksAlt(mergedBlocks []*ipaddr.IPAddress) (result []*ipaddr.IPAddress) { for _, series := range mergedBlocks { result = append(result, series.SpanWithPrefixBlocks()...) } return } func getMergedPrefixBlocksAltRange(addresses []*ipaddr.IPAddress) (result []*ipaddr.IPAddress) { var ranges []*ipaddr.IPAddressSeqRange for _, addr := range addresses { iter := addr.SequentialBlockIterator() for iter.HasNext() { next := iter.Next().ToSequentialRange() ranges = append(ranges, next) } } joined := ranges[0].Join(ranges...) for _, rng := range joined { joins := rng.SpanWithPrefixBlocks() for _, join := range joins { result = append(result, join) } } return } func getMergedPrefixBlocksAltRange2(addresses []*ipaddr.IPAddress) (result []*ipaddr.IPAddress) { var ranges []*ipaddr.IPAddressSeqRange for _, addr := range addresses { iter := addr.SequentialBlockIterator() for iter.HasNext() { next := iter.Next().ToSequentialRange() ranges = append(ranges, next) } } sort.Slice(ranges, func(i, j int) bool { return ipaddr.LowValueComparator.CompareRanges(ranges[i], ranges[j]) < 0 }) for i := 0; i < len(ranges); i++ { one := ranges[i] if one == nil { continue } for j := i + 1; j < len(ranges); j++ { two := ranges[j] if two == nil { continue } joined := one.JoinTo(two) if joined == nil { continue } ranges[j] = nil ranges[i] = joined one = joined i = -1 break } } for i := 0; i < len(ranges); i++ { one := ranges[i] if one == nil { continue } joins := one.SpanWithPrefixBlocks() for _, join := range joins { result = append(result, join) } } return } func getMergedPrefixBlocksAltMerge(addresses []*ipaddr.IPAddress) []*ipaddr.IPAddress { merged := addresses[0].MergeToSequentialBlocks(addresses...) return getMergedPrefixBlocksAlt(merged) } func joinAddrToAddresses(addresses []*ipaddr.IPAddress, another *ipaddr.IPAddress) []*ipaddr.IPAddress { result := make([]*ipaddr.IPAddress, len(addresses)+1) copy(result, addresses) result[len(addresses)] = another return result } func (t ipAddressRangeTester) testMergeImpl(result string, prefix bool, addresses ...string) { resultStr := t.createAddress(result) string2 := t.createAddress(addresses[0]) resultAddr := resultStr.GetAddress() addr2 := string2.GetAddress() mergers := make([]*ipaddr.IPAddress, len(addresses)-1) for i := 0; i < len(mergers); i++ { mergers[i] = t.createAddress(addresses[i+1]).GetAddress() } merged := addr2.MergeToSequentialBlocks(mergers...) if prefix { merged2 := getMergedPrefixBlocksAlt(merged) merged3 := getMergedPrefixBlocksAltRange(joinAddrToAddresses(mergers, addr2)) merged4 := getMergedPrefixBlocksAltRange(joinAddrToAddresses(mergers, addr2)) merged = addr2.MergeToPrefixBlocks(mergers...) if len(merged2) != 1 || !resultAddr.Equal(merged2[0]) { t.addFailure(newIPAddrFailure("merge prefix mismatch merging "+strings.Join(addresses, ",")+" expected "+result+" got "+asSliceString(merged2), resultAddr)) } if len(merged3) != 1 || !resultAddr.Equal(merged3[0]) { t.addFailure(newIPAddrFailure("merge prefix mismatch merging "+strings.Join(addresses, ",")+" expected "+result+" got "+asSliceString(merged3), resultAddr)) } if len(merged4) != 1 || !resultAddr.Equal(merged4[0]) { t.addFailure(newIPAddrFailure("merge prefix mismatch merging "+strings.Join(addresses, ",")+" expected "+result+" got "+asSliceString(merged4), resultAddr)) } } if len(merged) != 1 || !resultAddr.Equal(merged[0]) { t.addFailure(newIPAddrFailure("mismatch merging "+strings.Join(addresses, ",")+" expected "+result+" got "+asSliceString(merged), resultAddr)) } for _, m := range merged { if prefix { if !m.IsPrefixed() || !m.IsPrefixBlock() { t.addFailure(newIPAddrFailure("merged addr "+m.String()+" is not prefix block", m)) } } else { if m.IsPrefixed() { t.addFailure(newIPAddrFailure("merged addr "+m.String()+" is prefixed", m)) } } } t.incrementTestCount() } //like testMerge but the merge results in two addresses func (t ipAddressRangeTester) testMerge2Impl(result, result2 string, prefix bool, addresses ...string) { resultStr := t.createAddress(result) resultStr2 := t.createAddress(result2) string2 := t.createAddress(addresses[0]) resultAddr := resultStr.GetAddress() resultAddr2 := resultStr2.GetAddress() addr2 := string2.GetAddress() mergers := make([]*ipaddr.IPAddress, len(addresses)-1) for i := 0; i < len(mergers); i++ { mergers[i] = t.createAddress(addresses[i+1]).GetAddress() } seqMerged := addr2.MergeToSequentialBlocks(mergers...) var merged []*ipaddr.IPAddress if prefix { merged = addr2.MergeToPrefixBlocks(mergers...) } else { merged = seqMerged } var all, expected []*ipaddr.IPAddress all = append(all, merged...) expected = append(append(expected, resultAddr), resultAddr2) if !ipaddr.AddrsMatchUnordered(all, expected) { t.addFailure(newIPAddrFailure("mismatch merging "+strings.Join(addresses, ",")+" expected "+asSliceString(expected)+" got "+asSliceString(all), resultAddr)) } if prefix { merged2 := getMergedPrefixBlocksAlt(merged) merged3 := getMergedPrefixBlocksAltRange(joinAddrToAddresses(mergers, addr2)) merged4 := getMergedPrefixBlocksAltRange2(joinAddrToAddresses(mergers, addr2)) if len(merged2) != 2 || !ipaddr.AddrsMatchOrdered(merged, merged2) { t.addFailure(newIPAddrFailure("merge prefix mismatch merging "+strings.Join(addresses, ",")+" expected "+asSliceString(expected)+" got "+asSliceString(merged2), resultAddr)) } //merge prefix mismatch merging 1:2:3:4:8000::/65,1:2:3:4::/66,1:2:3:4:4000::/66,1:2:3:6:4000::/66,1:2:3:6::/66,1:2:3:6:8000::/65 //expected [1:2:3:4:*:*:*:* 1:2:3:6:*:*:*:*] //got [1:2:3:4:0-3fff:*:*:* 1:2:3:6:*:*:*:*] if len(merged3) != 2 || !ipaddr.AddrsMatchOrdered(merged, merged3) { t.addFailure(newIPAddrFailure("merge prefix mismatch merging "+strings.Join(addresses, ",")+" expected "+asSliceString(expected)+" got "+asSliceString(merged3), resultAddr)) //merged3 = getMergedPrefixBlocksAltRange(joinAddrToAddresses(mergers, addr2)) } if len(merged4) != 2 || !ipaddr.AddrsMatchOrdered(merged, merged4) { t.addFailure(newIPAddrFailure("merge prefix mismatch merging "+strings.Join(addresses, ",")+" expected "+asSliceString(expected)+" got "+asSliceString(merged4), resultAddr)) } } for _, m := range merged { if prefix { if !m.IsPrefixed() || !m.IsPrefixBlock() { t.addFailure(newIPAddrFailure("merged addr "+m.String()+" is not prefix block", m)) } } else { if m.IsPrefixed() { t.addFailure(newIPAddrFailure("merged addr "+m.String()+" is prefixed", m)) } } } t.incrementTestCount() } func (t ipAddressRangeTester) testCoverSingle(oneStr, resultStr string) { oneAddr := t.createAddress(oneStr).GetAddress() resultAddr := t.createAddress(resultStr).GetAddress() result := oneAddr.CoverWithPrefixBlock() if !result.Equal(resultAddr) { t.addFailure(newIPAddrFailure("cover was "+result.String()+" instead of expected "+resultAddr.String(), oneAddr)) } t.testCover(oneAddr.GetUpper().String(), oneAddr.GetLower().String(), resultStr) t.testCover(oneAddr.GetUpper().String(), oneStr, resultStr) t.incrementTestCount() } func (t ipAddressRangeTester) testCover(oneStr, twoStr, resultStr string) { oneAddr := t.createAddress(oneStr).GetAddress() twoAddr := t.createAddress(twoStr).GetAddress() resultAddr := t.createAddress(resultStr).GetAddress() result := oneAddr.CoverWithPrefixBlockTo(twoAddr) if !result.Equal(resultAddr) || !resultAddr.GetNetworkPrefixLen().Equal(result.GetNetworkPrefixLen()) { t.addFailure(newIPAddrFailure("cover was "+result.String()+" instead of expected "+resultAddr.String(), oneAddr)) } t.incrementTestCount() } func (t ipAddressRangeTester) testTrees() { t.testTree("1.2.3.4", []string{ "1.2.3.4", "1.2.3.*", "1.2.*.*", "1.*.*.*", "*.*.*.*", "*", }) t.testTree("1.2.3.*", []string{ "1.2.3.*", "1.2.*.*", "1.*.*.*", "*.*.*.*", "*", }) t.testTree("1.2.*.*", []string{ "1.2.*.*", "1.*.*.*", "*.*.*.*", "*", }) t.testTree("a:b:c:d:e:f:a:b", []string{ "a:b:c:d:e:f:a:b", "a:b:c:d:e:f:a::/112", "a:b:c:d:e:f::/96", "a:b:c:d:e::/80", "a:b:c:d::/64", "a:b:c::/48", "a:b::/32", "a::/16", "::/0", "*", }) t.testTree("1.2.3.4/28", []string{ "1.2.3.4/28", "1.2.3.4/24", "1.2.0.4/16", "1.0.0.4/8", "0.0.0.4/0", }) t.testTree("1.2.3.4/17", []string{ "1.2.3.4/17", "1.2.3.4/16", "1.0.3.4/8", "0.0.3.4/0", }) t.testTree("a:b:c:d:e:f:a:b/97", []string{ "a:b:c:d:e:f:a:b/97", "a:b:c:d:e:f:a:b/96", "a:b:c:d:e::a:b/80", "a:b:c:d::a:b/64", "a:b:c::a:b/48", "a:b::a:b/32", "a::a:b/16", "::a:b/0", }) t.testTree("a:b:c:d:e:f:ffff:b/97", []string{ "a:b:c:d:e:f:ffff:b/97", "a:b:c:d:e:f:7fff:b/96", "a:b:c:d:e::7fff:b/80", "a:b:c:d::7fff:b/64", "a:b:c::7fff:b/48", "a:b::7fff:b/32", "a::7fff:b/16", "::7fff:b/0", }) t.testTree("a:b:c:d:e:f:a:b/96", []string{ "a:b:c:d:e:f:a:b/96", "a:b:c:d:e::a:b/80", "a:b:c:d::a:b/64", "a:b:c::a:b/48", "a:b::a:b/32", "a::a:b/16", "::a:b/0", }) t.testTree("a:b:c:d::a:b", []string{ "a:b:c:d::a:b", "a:b:c:d:0:0:a::/112", "a:b:c:d::/96", "a:b:c:d::/80", "a:b:c:d::/64", "a:b:c::/48", "a:b::/32", "a::/16", "::/0", "*", }) t.testTree("::c:d:e:f:a:b", []string{ "::c:d:e:f:a:b", "0:0:c:d:e:f:a::/112", "0:0:c:d:e:f::/96", "0:0:c:d:e::/80", "0:0:c:d::/64", "0:0:c::/48", "::/32", "::/16", "::/0", "*", }) } func (t ipAddressRangeTester) testTree(start string, parents []string) { str := t.createAddress(start) originaLabelStr := str labelStr := str originalPrefixed := str.IsPrefixed() if !originalPrefixed { address := str.GetAddress() //convert 1.2.3.* to 1.2.3.*/24 which is needed by adjustPrefixBySegment address = address.AssignPrefixForSingleBlock() str = address.ToAddressString() } original := str i := 0 var last *ipaddr.IPAddressString for { label := getLabel(labelStr) expected := parents[i] if label != expected { t.addFailure(newFailure("failed expected: "+expected+" actual: "+label, str)) break } last = str str = enlargeSubnetStr(str) if str == nil || last == str { break } labelStr = str i++ } //now do the same thing but use the IPAddress objects instead labelStr = originaLabelStr str = original i = 0 for { label := getLabel(labelStr) expected := parents[i] if label != expected { t.addFailure(newFailure("failed expected: "+expected+" actual: "+label, str)) break } labelAddr := enlargeSubnet(str.GetAddress()) str = labelAddr.ToAddressString() labelStr = str if str.GetNetworkPrefixLen().Len() == 0 { //when network prefix is 0, IPAddress.adjustPrefixBySegment() returns the same address break } i++ } t.incrementTestCount() } func (t ipAddressRangeTester) testIPv4IPAddrStrings(w *ipaddr.IPAddressString, ipAddr *ipaddr.IPAddress, normalizedString, normalizedWildcardString, sqlString, fullString, octalString, hexString, reverseDNSString, singleHex, singleOctal string) { t.testBase.testStrings(w, ipAddr, normalizedString, normalizedWildcardString, normalizedWildcardString, sqlString, fullString, normalizedString, normalizedString, normalizedWildcardString, normalizedString, normalizedWildcardString, reverseDNSString, normalizedString, singleHex, singleOctal) //now test some IPv4-only strings t.testIPv4OnlyStrings(w, ipAddr.ToIPv4(), octalString, hexString) t.testInetAtonCombos(w, ipAddr.ToIPv4()) } func (t ipAddressRangeTester) testIPv4OnlyStrings(w *ipaddr.IPAddressString, ipAddr *ipaddr.IPv4Address, octalString, hexString string) { oct := ipAddr.ToInetAtonString(ipaddr.Inet_aton_radix_octal) hex := ipAddr.ToInetAtonString(ipaddr.Inet_aton_radix_hex) octMatch := oct == octalString if !octMatch { t.addFailure(newFailure("failed expected: "+octalString+" actual: "+oct, w)) } else { hexMatch := hex == hexString if !hexMatch { t.addFailure(newFailure("failed expected: "+hexString+" actual: "+hex, w)) } } t.incrementTestCount() } func (t ipAddressRangeTester) testInetAtonCombos(w *ipaddr.IPAddressString, ipAddr *ipaddr.IPv4Address) { vals := []ipaddr.Inet_aton_radix{ipaddr.Inet_aton_radix_octal, ipaddr.Inet_aton_radix_hex, ipaddr.Inet_aton_radix_decimal} for _, radix := range vals { for i := 0; i < ipaddr.IPv4SegmentCount; i++ { str, e := ipAddr.ToInetAtonJoinedString(radix, i) if e != nil { //verify this case: joining segments results in a joined segment that is not a contiguous range section := ipAddr.GetSection() verifiedIllegalJoin := false for j := section.GetSegmentCount() - i - 1; j < section.GetSegmentCount()-1; j++ { if section.GetSegment(j).IsMultiple() { for j++; j < section.GetSegmentCount(); j++ { if !section.GetSegment(j).IsFullRange() { verifiedIllegalJoin = true break } } } } if !verifiedIllegalJoin { t.addFailure(newFailure("failed expected: "+ipAddr.String()+" actual: "+e.Error(), w)) } } else { parsed := ipaddr.NewIPAddressStringParams(str, inetAtonwildcardAndRangeOptions) parsedValue := parsed.GetAddress() if !ipAddr.Equal(parsedValue) { t.addFailure(newFailure("failed expected: "+ipAddr.String()+" actual: "+parsedValue.String(), w)) } else { origStr := str count := 0 for pos := strings.IndexByte(str, ipaddr.IPv4SegmentSeparator); pos >= 0 && pos < len(str); { //for ((pos = str.indexOf(ipaddr.IPv4SegmentSeparator)) >= 0){ str = str[pos+1:] pos = strings.IndexByte(str, ipaddr.IPv4SegmentSeparator) count++ } if ipaddr.IPv4SegmentCount-1-i != count { failStr := "failed expected separator count in " + origStr + ": " + strconv.Itoa(ipaddr.IPv4SegmentCount-1-i) + " actual separator count: " + strconv.Itoa(count) t.addFailure(newFailure(failStr, w)) } } } t.incrementTestCount() } } } func (t ipAddressRangeTester) testIPv4Strings(addr, normalizedString, normalizedWildcardString, sqlString, fullString, octalString, hexString, reverseDNSString, singleHex, singleOctal string) { w := t.createAddress(addr) ipAddr := w.GetAddress() if ipAddr == nil { t.addFailure(newFailure("failed expected IPv4 address, got nil ", w)) return } t.testIPv4IPAddrStrings(w, ipAddr, normalizedString, normalizedWildcardString, sqlString, fullString, octalString, hexString, reverseDNSString, singleHex, singleOctal) } func (t ipAddressRangeTester) testIPv6Strings(addr, normalizedString, normalizedWildcardString, canonicalWildcardString, sqlString, fullString, compressedString, canonicalString, subnetString, compressedWildcardString, mixedStringNoCompressMixed, mixedStringNoCompressHost, mixedStringCompressCoveredHost, mixedString, reverseDNSString, uncHostString, base85String, singleHex, singleOctal string) { w := t.createAddress(addr) ipAddr := w.GetAddress() if ipAddr == nil { t.addFailure(newFailure("failed expected IPv6 address, got nil ", w)) return } t.testBase.testIPv6Strings(w, ipAddr, normalizedString, normalizedWildcardString, canonicalWildcardString, sqlString, fullString, compressedString, canonicalString, subnetString, compressedWildcardString, mixedStringNoCompressMixed, mixedStringNoCompressHost, mixedStringCompressCoveredHost, mixedString, reverseDNSString, uncHostString, base85String, singleHex, singleOctal) } //each ipv4 failure is 6, each ipv6 is 10, current total is 520 func (t ipAddressRangeTester) testStrings() { t.testIPv4Strings("1.2.3.4", "1.2.3.4", "1.2.3.4", "1.2.3.4", "001.002.003.004", "01.02.03.04", "0x1.0x2.0x3.0x4", "4.3.2.1.in-addr.arpa", "0x01020304", "000100401404") t.testIPv4Strings("1.2.3.4/16", "1.2.3.4/16", "1.2.3.4", "1.2.3.4", "001.002.003.004/16", "01.02.03.04/16", "0x1.0x2.0x3.0x4/16", "4.3.2.1.in-addr.arpa", "0x01020304", "000100401404") t.testIPv4Strings("1.2.*.*", "1.2.*.*", "1.2.*.*", "1.2.%.%", "001.002.000-255.000-255", "01.02.*.*", "0x1.0x2.*.*", "*.*.2.1.in-addr.arpa", "0x01020000-0x0102ffff", "000100400000-000100577777") //note that wildcards are never converted to CIDR. t.testIPv4Strings("1.2.*", "1.2.*.*", "1.2.*.*", "1.2.%.%", "001.002.000-255.000-255", "01.02.*.*", "0x1.0x2.*.*", "*.*.2.1.in-addr.arpa", "0x01020000-0x0102ffff", "000100400000-000100577777") t.testIPv4Strings("1.2.*.*/16", "1.2.0.0/16", "1.2.*.*", "1.2.%.%", "001.002.000.000/16", "01.02.00.00/16", "0x1.0x2.0x0.0x0/16", "*.*.2.1.in-addr.arpa", "0x01020000-0x0102ffff", "000100400000-000100577777") t.testIPv4Strings("1.2.*/16", "1.2.0.0/16", "1.2.*.*", "1.2.%.%", "001.002.000.000/16", "01.02.00.00/16", "0x1.0x2.0x0.0x0/16", "*.*.2.1.in-addr.arpa", "0x01020000-0x0102ffff", "000100400000-000100577777") t.testIPv4Strings("1.*.*/16", "1.*.0.0/16", "1.*.*.*", "1.%.%.%", "001.000-255.000.000/16", "01.*.00.00/16", "0x1.*.0x0.0x0/16", "*.*.*.1.in-addr.arpa", "0x01000000-0x01ffffff", "000100000000-000177777777") t.testIPv4Strings("0.0.0.0", "0.0.0.0", "0.0.0.0", "0.0.0.0", "000.000.000.000", "00.00.00.00", "0x0.0x0.0x0.0x0", "0.0.0.0.in-addr.arpa", "0x00000000", "000000000000") t.testIPv4Strings("9.63.127.254", "9.63.127.254", "9.63.127.254", "9.63.127.254", "009.063.127.254", "011.077.0177.0376", "0x9.0x3f.0x7f.0xfe", "254.127.63.9.in-addr.arpa", "0x093f7ffe", "001117677776") t.testIPv4Strings("9.63.127.254/16", "9.63.127.254/16", "9.63.127.254", "9.63.127.254", "009.063.127.254/16", "011.077.0177.0376/16", "0x9.0x3f.0x7f.0xfe/16", "254.127.63.9.in-addr.arpa", "0x093f7ffe", "001117677776") t.testIPv4Strings("9.63.*.*", "9.63.*.*", "9.63.*.*", "9.63.%.%", "009.063.000-255.000-255", "011.077.*.*", "0x9.0x3f.*.*", "*.*.63.9.in-addr.arpa", "0x093f0000-0x093fffff", "001117600000-001117777777") //note that wildcards are never converted to CIDR. t.testIPv4Strings("9.63.*", "9.63.*.*", "9.63.*.*", "9.63.%.%", "009.063.000-255.000-255", "011.077.*.*", "0x9.0x3f.*.*", "*.*.63.9.in-addr.arpa", "0x093f0000-0x093fffff", "001117600000-001117777777") t.testIPv4Strings("9.63.*.*/16", "9.63.0.0/16", "9.63.*.*", "9.63.%.%", "009.063.000.000/16", "011.077.00.00/16", "0x9.0x3f.0x0.0x0/16", "*.*.63.9.in-addr.arpa", "0x093f0000-0x093fffff", "001117600000-001117777777") t.testIPv4Strings("9.63.*/16", "9.63.0.0/16", "9.63.*.*", "9.63.%.%", "009.063.000.000/16", "011.077.00.00/16", "0x9.0x3f.0x0.0x0/16", "*.*.63.9.in-addr.arpa", "0x093f0000-0x093fffff", "001117600000-001117777777") t.testIPv4Strings("9.*.*/16", "9.*.0.0/16", "9.*.*.*", "9.%.%.%", "009.000-255.000.000/16", "011.*.00.00/16", "0x9.*.0x0.0x0/16", "*.*.*.9.in-addr.arpa", "0x09000000-0x09ffffff", "001100000000-001177777777") t.testIPv4Strings("1.2.3.250-255", "1.2.3.250-255", "1.2.3.250-255", "1.2.3.25_", "001.002.003.250-255", "01.02.03.0372-0377", "0x1.0x2.0x3.0xfa-0xff", "250-255.3.2.1.in-addr.arpa", "0x010203fa-0x010203ff", "000100401772-000100401777") t.testIPv4Strings("1.2.3.200-255", "1.2.3.200-255", "1.2.3.200-255", "1.2.3.2__", "001.002.003.200-255", "01.02.03.0310-0377", "0x1.0x2.0x3.0xc8-0xff", "200-255.3.2.1.in-addr.arpa", "0x010203c8-0x010203ff", "000100401710-000100401777") t.testIPv4Strings("1.2.3.100-199", "1.2.3.100-199", "1.2.3.100-199", "1.2.3.1__", "001.002.003.100-199", "01.02.03.0144-0307", "0x1.0x2.0x3.0x64-0xc7", "100-199.3.2.1.in-addr.arpa", "0x01020364-0x010203c7", "000100401544-000100401707") t.testIPv4Strings("100-199.2.3.100-199", "100-199.2.3.100-199", "100-199.2.3.100-199", "1__.2.3.1__", "100-199.002.003.100-199", "0144-0307.02.03.0144-0307", "0x64-0xc7.0x2.0x3.0x64-0xc7", "100-199.3.2.100-199.in-addr.arpa", "", "") t.testIPv4Strings("100-199.2.3.100-198", "100-199.2.3.100-198", "100-199.2.3.100-198", "1__.2.3.100-198", "100-199.002.003.100-198", "0144-0307.02.03.0144-0306", "0x64-0xc7.0x2.0x3.0x64-0xc6", "100-198.3.2.100-199.in-addr.arpa", "", "") t.testIPv4Strings("1.2.3.0-99", "1.2.3.0-99", "1.2.3.0-99", "1.2.3.0-99", "001.002.003.000-099", "01.02.03.00-0143", "0x1.0x2.0x3.0x0-0x63", "0-99.3.2.1.in-addr.arpa", "0x01020300-0x01020363", "000100401400-000100401543") t.testIPv4Strings("1.2.3.100-155", "1.2.3.100-155", "1.2.3.100-155", "1.2.3.100-155", "001.002.003.100-155", "01.02.03.0144-0233", "0x1.0x2.0x3.0x64-0x9b", "100-155.3.2.1.in-addr.arpa", "0x01020364-0x0102039b", "000100401544-000100401633") t.testIPv4Strings("1.2.3.100-255", "1.2.3.100-255", "1.2.3.100-255", "1.2.3.100-255", "001.002.003.100-255", "01.02.03.0144-0377", "0x1.0x2.0x3.0x64-0xff", "100-255.3.2.1.in-addr.arpa", "0x01020364-0x010203ff", "000100401544-000100401777") t.testIPv4Strings("1.129-254.5.5/12", "1.129-254.5.5/12", "1.129-254.5.5", "1.129-254.5.5", "001.129-254.005.005/12", "01.0201-0376.05.05/12", "0x1.0x81-0xfe.0x5.0x5/12", "5.5.129-254.1.in-addr.arpa", "", "") t.testIPv4Strings("1.2__.5.5/14", "1.200-255.5.5/14", "1.200-255.5.5", "1.2__.5.5", "001.200-255.005.005/14", "01.0310-0377.05.05/14", "0x1.0xc8-0xff.0x5.0x5/14", "5.5.200-255.1.in-addr.arpa", "", "") t.testIPv4Strings("1.*.5.5/12", "1.*.5.5/12", "1.*.5.5", "1.%.5.5", "001.000-255.005.005/12", "01.*.05.05/12", "0x1.*.0x5.0x5/12", "5.5.*.1.in-addr.arpa", "", "") //OK we are testing 01.*.02405/12 and our bounds check for inet_aton does not work because later when creating address it is not treated as inet_aton due to the * //so when we do the bounds checking for inet_aton we need to check for * and only test with single segment boundaries //also check for that setting where * extends beyond single segment t.testIPv6Strings("::", "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0:0", "::", "0:0:0:0:0:0:0:0", "0000:0000:0000:0000:0000:0000:0000:0000", "::", "::", "::", "::", "::0.0.0.0", "::", "::", "::", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", "0-0-0-0-0-0-0-0.ipv6-literal.net", "00000000000000000000", "0x00000000000000000000000000000000", "00000000000000000000000000000000000000000000") t.testIPv6Strings("::2", "0:0:0:0:0:0:0:2", "0:0:0:0:0:0:0:2", "::2", "0:0:0:0:0:0:0:2", "0000:0000:0000:0000:0000:0000:0000:0002", "::2", "::2", "::2", "::2", "::0.0.0.2", "::0.0.0.2", "::0.0.0.2", "::0.0.0.2", "2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", "0-0-0-0-0-0-0-2.ipv6-literal.net", "00000000000000000002", "0x00000000000000000000000000000002", "00000000000000000000000000000000000000000002") t.testIPv6Strings("::7fff:ffff:ffff:ffff", "0:0:0:0:7fff:ffff:ffff:ffff", "0:0:0:0:7fff:ffff:ffff:ffff", "::7fff:ffff:ffff:ffff", "0:0:0:0:7fff:ffff:ffff:ffff", "0000:0000:0000:0000:7fff:ffff:ffff:ffff", "::7fff:ffff:ffff:ffff", "::7fff:ffff:ffff:ffff", "::7fff:ffff:ffff:ffff", "::7fff:ffff:ffff:ffff", "::7fff:ffff:255.255.255.255", "::7fff:ffff:255.255.255.255", "::7fff:ffff:255.255.255.255", "::7fff:ffff:255.255.255.255", "f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.7.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", "0-0-0-0-7fff-ffff-ffff-ffff.ipv6-literal.net", "0000000000d*-h_{Y}sg", "0x00000000000000007fffffffffffffff", "00000000000000000000000777777777777777777777") t.testIPv6Strings("0:0:0:1::", "0:0:0:1:0:0:0:0", "0:0:0:1:0:0:0:0", "0:0:0:1::", "0:0:0:1:0:0:0:0", "0000:0000:0000:0001:0000:0000:0000:0000", "0:0:0:1::", "0:0:0:1::", "0:0:0:1::", "0:0:0:1::", "::1:0:0:0.0.0.0", "0:0:0:1::", "0:0:0:1::", "0:0:0:1::", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", "0-0-0-1-0-0-0-0.ipv6-literal.net", "0000000000_sw2=@*|O1", "0x00000000000000010000000000000000", "00000000000000000000002000000000000000000000") t.testIPv6Strings("::8fff:ffff:ffff:ffff", "0:0:0:0:8fff:ffff:ffff:ffff", "0:0:0:0:8fff:ffff:ffff:ffff", "::8fff:ffff:ffff:ffff", "0:0:0:0:8fff:ffff:ffff:ffff", "0000:0000:0000:0000:8fff:ffff:ffff:ffff", "::8fff:ffff:ffff:ffff", "::8fff:ffff:ffff:ffff", "::8fff:ffff:ffff:ffff", "::8fff:ffff:ffff:ffff", "::8fff:ffff:255.255.255.255", "::8fff:ffff:255.255.255.255", "::8fff:ffff:255.255.255.255", "::8fff:ffff:255.255.255.255", "f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", "0-0-0-0-8fff-ffff-ffff-ffff.ipv6-literal.net", "0000000000i(`c)xypow", "0x00000000000000008fffffffffffffff", "00000000000000000000001077777777777777777777") t.testIPv6Strings("::8fff:ffff:ffff:ffff:ffff", "0:0:0:8fff:ffff:ffff:ffff:ffff", "0:0:0:8fff:ffff:ffff:ffff:ffff", "::8fff:ffff:ffff:ffff:ffff", "0:0:0:8fff:ffff:ffff:ffff:ffff", "0000:0000:0000:8fff:ffff:ffff:ffff:ffff", "::8fff:ffff:ffff:ffff:ffff", "::8fff:ffff:ffff:ffff:ffff", "::8fff:ffff:ffff:ffff:ffff", "::8fff:ffff:ffff:ffff:ffff", "::8fff:ffff:ffff:255.255.255.255", "::8fff:ffff:ffff:255.255.255.255", "::8fff:ffff:ffff:255.255.255.255", "::8fff:ffff:ffff:255.255.255.255", "f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.8.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", "0-0-0-8fff-ffff-ffff-ffff-ffff.ipv6-literal.net", "00000004&U-n{rbbza$w", "0x0000000000008fffffffffffffffffff", "00000000000000000217777777777777777777777777") t.testIPv6Strings("a:b:c:d:e:f:a:b", "a:b:c:d:e:f:a:b", "a:b:c:d:e:f:a:b", "a:b:c:d:e:f:a:b", "a:b:c:d:e:f:a:b", "000a:000b:000c:000d:000e:000f:000a:000b", "a:b:c:d:e:f:a:b", "a:b:c:d:e:f:a:b", "a:b:c:d:e:f:a:b", "a:b:c:d:e:f:a:b", "a:b:c:d:e:f:0.10.0.11", "a:b:c:d:e:f:0.10.0.11", "a:b:c:d:e:f:0.10.0.11", "a:b:c:d:e:f:0.10.0.11", "b.0.0.0.a.0.0.0.f.0.0.0.e.0.0.0.d.0.0.0.c.0.0.0.b.0.0.0.a.0.0.0.ip6.arpa", "a-b-c-d-e-f-a-b.ipv6-literal.net", "00|N0s0$ND2DCD&%D3QB", "0x000a000b000c000d000e000f000a000b", "00000240001300006000032000160000740002400013") t.testIPv6Strings("a:b:c:d:e:f:a:b/64", "a:b:c:d:e:f:a:b/64", "a:b:c:d:e:f:a:b", "a:b:c:d:e:f:a:b", "a:b:c:d:e:f:a:b", "000a:000b:000c:000d:000e:000f:000a:000b/64", "a:b:c:d:e:f:a:b/64", "a:b:c:d:e:f:a:b/64", "a:b:c:d:e:f:a:b/64", "a:b:c:d:e:f:a:b", "a:b:c:d:e:f:0.10.0.11/64", "a:b:c:d:e:f:0.10.0.11/64", "a:b:c:d:e:f:0.10.0.11/64", "a:b:c:d:e:f:0.10.0.11/64", "b.0.0.0.a.0.0.0.f.0.0.0.e.0.0.0.d.0.0.0.c.0.0.0.b.0.0.0.a.0.0.0.ip6.arpa", "a-b-c-d-e-f-a-b.ipv6-literal.net/64", "00|N0s0$ND2DCD&%D3QB/64", "0x000a000b000c000d000e000f000a000b", "00000240001300006000032000160000740002400013") t.testIPv6Strings("::c:d:e:f:a:b/64", "0:0:c:d:e:f:a:b/64", "0:0:c:d:e:f:a:b", "::c:d:e:f:a:b", "0:0:c:d:e:f:a:b", "0000:0000:000c:000d:000e:000f:000a:000b/64", "::c:d:e:f:a:b/64", "::c:d:e:f:a:b/64", "::c:d:e:f:a:b/64", "::c:d:e:f:a:b", "::c:d:e:f:0.10.0.11/64", "::c:d:e:f:0.10.0.11/64", "::c:d:e:f:0.10.0.11/64", "::c:d:e:f:0.10.0.11/64", "b.0.0.0.a.0.0.0.f.0.0.0.e.0.0.0.d.0.0.0.c.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", "0-0-c-d-e-f-a-b.ipv6-literal.net/64", "0000001G~Ie^C9jXExx>/64", "0x00000000000c000d000e000f000a000b", "00000000000000006000032000160000740002400013") t.testIPv6Strings("::c:d:e:f:a:b", "0:0:c:d:e:f:a:b", "0:0:c:d:e:f:a:b", "::c:d:e:f:a:b", "0:0:c:d:e:f:a:b", "0000:0000:000c:000d:000e:000f:000a:000b", "::c:d:e:f:a:b", "::c:d:e:f:a:b", "::c:d:e:f:a:b", "::c:d:e:f:a:b", "::c:d:e:f:0.10.0.11", "::c:d:e:f:0.10.0.11", "::c:d:e:f:0.10.0.11", "::c:d:e:f:0.10.0.11", "b.0.0.0.a.0.0.0.f.0.0.0.e.0.0.0.d.0.0.0.c.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", "0-0-c-d-e-f-a-b.ipv6-literal.net", "0000001G~Ie^C9jXExx>", "0x00000000000c000d000e000f000a000b", "00000000000000006000032000160000740002400013") t.testIPv6Strings("a:b:c:d::", "a:b:c:d:0:0:0:0", "a:b:c:d:0:0:0:0", "a:b:c:d::", "a:b:c:d:0:0:0:0", "000a:000b:000c:000d:0000:0000:0000:0000", "a:b:c:d::", "a:b:c:d::", "a:b:c:d::", "a:b:c:d::", "a:b:c:d::0.0.0.0", "a:b:c:d::", "a:b:c:d::", "a:b:c:d::", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.0.0.0.c.0.0.0.b.0.0.0.a.0.0.0.ip6.arpa", "a-b-c-d-0-0-0-0.ipv6-literal.net", "00|N0s0$ND2BxK96%Chk", "0x000a000b000c000d0000000000000000", "00000240001300006000032000000000000000000000") t.testIPv6Strings("a:b:c:d::/64", "a:b:c:d:0:0:0:0/64", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:%:%:%:%", "000a:000b:000c:000d:0000:0000:0000:0000/64", "a:b:c:d::/64", "a:b:c:d::/64", "a:b:c:d::/64", "a:b:c:d:*:*:*:*", "a:b:c:d::0.0.0.0/64", "a:b:c:d::0.0.0.0/64", "a:b:c:d::/64", "a:b:c:d::/64", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.d.0.0.0.c.0.0.0.b.0.0.0.a.0.0.0.ip6.arpa", "a-b-c-d-0-0-0-0.ipv6-literal.net/64", "00|N0s0$ND2BxK96%Chk/64", "0x000a000b000c000d0000000000000000-0x000a000b000c000dffffffffffffffff", "00000240001300006000032000000000000000000000-00000240001300006000033777777777777777777777") t.testIPv6Strings("a::d:*:*:*:*/65", "a:0:0:d:0-8000:0:0:0/65", "a:0:0:d:*:*:*:*", "a::d:*:*:*:*", "a:0:0:d:%:%:%:%", "000a:0000:0000:000d:0000-8000:0000:0000:0000/65", "a:0:0:d:0-8000::/65", "a:0:0:d:0-8000::/65", "a:0:0:d:0-8000::/65", "a::d:*:*:*:*", "a::d:0-8000:0:0.0.0.0/65", "a::d:0-8000:0:0.0.0.0/65", "a:0:0:d:0-8000::/65", "a:0:0:d:0-8000::/65", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.d.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-d-0"+ipaddr.IPv6AlternativeRangeSeparatorStr+"8000-0-0-0.ipv6-literal.net/65", "00|M>t|tt+WbKhfd5~qN"+ipaddr.IPv6AlternativeRangeSeparatorStr+"00|M>t|tt+;M72aZe}L&/65", "0x000a00000000000d0000000000000000-0x000a00000000000dffffffffffffffff", "00000240000000000000032000000000000000000000-00000240000000000000033777777777777777777777") t.testIPv6Strings("a::d:0-7fff:*:*:*/65", "a:0:0:d:0:0:0:0/65", "a:0:0:d:0-7fff:*:*:*", "a::d:0-7fff:*:*:*", "a:0:0:d:0-7fff:%:%:%", "000a:0000:0000:000d:0000:0000:0000:0000/65", "a:0:0:d::/65", "a:0:0:d::/65", "a:0:0:d::/65", "a::d:0-7fff:*:*:*", "a::d:0:0:0.0.0.0/65", "a::d:0:0:0.0.0.0/65", "a:0:0:d::/65", "a:0:0:d::/65", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.0-7.d.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-d-0-0-0-0.ipv6-literal.net/65", "00|M>t|tt+WbKhfd5~qN/65", "0x000a00000000000d0000000000000000-0x000a00000000000d7fffffffffffffff", "00000240000000000000032000000000000000000000-00000240000000000000032777777777777777777777") t.testIPv6Strings("a::d:0:0:0:0/65", "a:0:0:d:0:0:0:0/65", "a:0:0:d:0-7fff:*:*:*", "a::d:0-7fff:*:*:*", "a:0:0:d:0-7fff:%:%:%", "000a:0000:0000:000d:0000:0000:0000:0000/65", "a:0:0:d::/65", "a:0:0:d::/65", "a:0:0:d::/65", "a::d:0-7fff:*:*:*", "a::d:0:0:0.0.0.0/65", "a::d:0:0:0.0.0.0/65", "a:0:0:d::/65", "a:0:0:d::/65", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.0-7.d.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-d-0-0-0-0.ipv6-literal.net/65", "00|M>t|tt+WbKhfd5~qN/65", "0x000a00000000000d0000000000000000-0x000a00000000000d7fffffffffffffff", "00000240000000000000032000000000000000000000-00000240000000000000032777777777777777777777") t.testIPv6Strings("a::d:*:*:*:0/65", "a:0:0:d:*:*:*:0/65", "a:0:0:d:*:*:*:0", "a::d:*:*:*:0", "a:0:0:d:%:%:%:0", "000a:0000:0000:000d:0000-ffff:0000-ffff:0000-ffff:0000/65", "a::d:*:*:*:0/65", "a::d:*:*:*:0/65", "a:0:0:d:*:*:*::/65", "a::d:*:*:*:0", "a::d:*:*:*.*.0.0/65", "a::d:*:*:*.*.0.0/65", "a::d:*:*:*.*.0.0/65", "a::d:*:*:*.*.0.0/65", "0.0.0.0.*.*.*.*.*.*.*.*.*.*.*.*.d.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-d-*-*-*-0.ipv6-literal.net/65", "", "", "") t.testIPv6Strings("a::d:0:*:0:*/65", "a:0:0:d:0:*:0:*/65", "a:0:0:d:0:*:0:*", "a::d:0:*:0:*", "a:0:0:d:0:%:0:%", "000a:0000:0000:000d:0000:0000-ffff:0000:0000-ffff/65", "a::d:0:*:0:*/65", "a::d:0:*:0:*/65", "a:0:0:d:0:*::*/65", "a::d:0:*:0:*", "a::d:0:*:0.0.*.*/65", "a::d:0:*:0.0.*.*/65", "a::d:0:*:0.0.*.*/65", "a::d:0:*:0.0.*.*/65", "*.*.*.*.0.0.0.0.*.*.*.*.0.0.0.0.d.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-d-0-*-0-*.ipv6-literal.net/65", "", "", "") t.testIPv6Strings("a::d:*:0:0:0/65", "a:0:0:d:*:0:0:0/65", "a:0:0:d:*:0:0:0", "a:0:0:d:*::", "a:0:0:d:%:0:0:0", "000a:0000:0000:000d:0000-ffff:0000:0000:0000/65", "a:0:0:d:*::/65", "a:0:0:d:*::/65", "a:0:0:d:*::/65", "a:0:0:d:*::", "a::d:*:0:0.0.0.0/65", "a::d:*:0:0.0.0.0/65", "a:0:0:d:*::/65", "a:0:0:d:*::/65", "0.0.0.0.0.0.0.0.0.0.0.0.*.*.*.*.d.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-d-*-0-0-0.ipv6-literal.net/65", "", "", "") t.testIPv6Strings("a:b:c:d:*::/64", "a:b:c:d:*:0:0:0/64", "a:b:c:d:*:0:0:0", "a:b:c:d:*::", "a:b:c:d:%:0:0:0", "000a:000b:000c:000d:0000-ffff:0000:0000:0000/64", "a:b:c:d:*::/64", "a:b:c:d:*::/64", "a:b:c:d:*::/64", "a:b:c:d:*::", "a:b:c:d:*::0.0.0.0/64", "a:b:c:d:*::0.0.0.0/64", "a:b:c:d:*::/64", "a:b:c:d:*::/64", "0.0.0.0.0.0.0.0.0.0.0.0.*.*.*.*.d.0.0.0.c.0.0.0.b.0.0.0.a.0.0.0.ip6.arpa", "a-b-c-d-*-0-0-0.ipv6-literal.net/64", "", "", "") t.testIPv6Strings("a:0:c:d:e:f:0:0/97", "a:0:c:d:e:f:0:0/97", "a:0:c:d:e:f:0-7fff:*", "a:0:c:d:e:f:0-7fff:*", "a:0:c:d:e:f:0-7fff:%", "000a:0000:000c:000d:000e:000f:0000:0000/97", "a:0:c:d:e:f::/97", "a:0:c:d:e:f::/97", "a:0:c:d:e:f::/97", "a::c:d:e:f:0-7fff:*", "a::c:d:e:f:0.0.0.0/97", "a::c:d:e:f:0.0.0.0/97", "a::c:d:e:f:0.0.0.0/97", "a:0:c:d:e:f::/97", "*.*.*.*.*.*.*.0-7.f.0.0.0.e.0.0.0.d.0.0.0.c.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-c-d-e-f-0-0.ipv6-literal.net/97", "00|M>t};s?v~hFl`j3_$/97", "0x000a0000000c000d000e000f00000000-0x000a0000000c000d000e000f7fffffff", "00000240000000006000032000160000740000000000-00000240000000006000032000160000757777777777") t.testIPv6Strings("a:0:c:d:e:f:0:0/96", "a:0:c:d:e:f:0:0/96", "a:0:c:d:e:f:*:*", "a:0:c:d:e:f:*:*", "a:0:c:d:e:f:%:%", "000a:0000:000c:000d:000e:000f:0000:0000/96", "a:0:c:d:e:f::/96", "a:0:c:d:e:f::/96", "a:0:c:d:e:f::/96", "a::c:d:e:f:*:*", "a::c:d:e:f:0.0.0.0/96", "a::c:d:e:f:0.0.0.0/96", "a:0:c:d:e:f::/96", "a:0:c:d:e:f::/96", "*.*.*.*.*.*.*.*.f.0.0.0.e.0.0.0.d.0.0.0.c.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-c-d-e-f-0-0.ipv6-literal.net/96", "00|M>t};s?v~hFl`j3_$/96", "0x000a0000000c000d000e000f00000000-0x000a0000000c000d000e000fffffffff", "00000240000000006000032000160000740000000000-00000240000000006000032000160000777777777777") t.testIPv6Strings("a:0:c:d:e:f:1:0/112", "a:0:c:d:e:f:1:0/112", "a:0:c:d:e:f:1:*", "a:0:c:d:e:f:1:*", "a:0:c:d:e:f:1:%", "000a:0000:000c:000d:000e:000f:0001:0000/112", "a::c:d:e:f:1:0/112", //compressed "a:0:c:d:e:f:1:0/112", //canonical (only zeros are single so not compressed) "a:0:c:d:e:f:1::/112", //subnet "a::c:d:e:f:1:*", //compressed wildcard "a::c:d:e:f:0.1.0.0/112", //mixed, no compress "a::c:d:e:f:0.1.0.0/112", //mixed, no compress host "a::c:d:e:f:0.1.0.0/112", "a::c:d:e:f:0.1.0.0/112", "*.*.*.*.1.0.0.0.f.0.0.0.e.0.0.0.d.0.0.0.c.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-c-d-e-f-1-0.ipv6-literal.net/112", "00|M>t};s?v~hFl`jD0%/112", "0x000a0000000c000d000e000f00010000-0x000a0000000c000d000e000f0001ffff", "00000240000000006000032000160000740000200000-00000240000000006000032000160000740000377777") //mixed t.testIPv6Strings("a:0:c:d:0:0:1:0/112", "a:0:c:d:0:0:1:0/112", //normalized "a:0:c:d:0:0:1:*", //normalized wildcard "a:0:c:d::1:*", //canonical wildcard "a:0:c:d:0:0:1:%", //sql "000a:0000:000c:000d:0000:0000:0001:0000/112", //full "a:0:c:d::1:0/112", //compressed "a:0:c:d::1:0/112", //canonical "a:0:c:d:0:0:1::/112", //subnet "a:0:c:d::1:*", //compressed wildcard "a:0:c:d::0.1.0.0/112", //mixed, no compress "a:0:c:d::0.1.0.0/112", //mixed, no compress host "a:0:c:d::0.1.0.0/112", "a:0:c:d::0.1.0.0/112", "*.*.*.*.1.0.0.0.0.0.0.0.0.0.0.0.d.0.0.0.c.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-c-d-0-0-1-0.ipv6-literal.net/112", "00|M>t};s?v}5L>MDR^a/112", "0x000a0000000c000d0000000000010000-0x000a0000000c000d000000000001ffff", "00000240000000006000032000000000000000200000-00000240000000006000032000000000000000377777") //mixed t.testIPv6Strings("a:b:c:*::/64", "a:b:c:*:0:0:0:0/64", "a:b:c:*:*:*:*:*", "a:b:c:*:*:*:*:*", "a:b:c:%:%:%:%:%", "000a:000b:000c:0000-ffff:0000:0000:0000:0000/64", "a:b:c:*::/64", "a:b:c:*::/64", "a:b:c:*::/64", "a:b:c:*:*:*:*:*", "a:b:c:*::0.0.0.0/64", "a:b:c:*::0.0.0.0/64", "a:b:c:*::/64", "a:b:c:*::/64", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.c.0.0.0.b.0.0.0.a.0.0.0.ip6.arpa", "a-b-c-*-0-0-0-0.ipv6-literal.net/64", "00|N0s0$N0-%*(tF5l-X"+ipaddr.IPv6AlternativeRangeSeparatorStr+"00|N0s0;%Z;E{Rk+ZU@X/64", "0x000a000b000c00000000000000000000-0x000a000b000cffffffffffffffffffff", "00000240001300006000000000000000000000000000-00000240001300006377777777777777777777777777") t.testIPv6Strings("a::/64", "a:0:0:0:0:0:0:0/64", "a:0:0:0:*:*:*:*", "a::*:*:*:*", "a:0:0:0:%:%:%:%", "000a:0000:0000:0000:0000:0000:0000:0000/64", "a::/64", "a::/64", "a::/64", "a::*:*:*:*", "a::0.0.0.0/64", "a::0.0.0.0/64", "a::/64", "a::/64", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.0.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-0-0-0-0-0.ipv6-literal.net/64", "00|M>t|ttwH6V62lVY`A/64", "0x000a0000000000000000000000000000-0x000a000000000000ffffffffffffffff", "00000240000000000000000000000000000000000000-00000240000000000000001777777777777777777777") t.testIPv6Strings("a:0:0:d:e:f:0:0/112", "a:0:0:d:e:f:0:0/112", "a:0:0:d:e:f:0:*", "a::d:e:f:0:*", "a:0:0:d:e:f:0:%", "000a:0000:0000:000d:000e:000f:0000:0000/112", "a::d:e:f:0:0/112", "a::d:e:f:0:0/112", "a:0:0:d:e:f::/112", "a::d:e:f:0:*", "a::d:e:f:0.0.0.0/112", "a::d:e:f:0.0.0.0/112", "a::d:e:f:0.0.0.0/112", "a:0:0:d:e:f::/112", "*.*.*.*.0.0.0.0.f.0.0.0.e.0.0.0.d.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-d-e-f-0-0.ipv6-literal.net/112", "00|M>t|tt+WcwbECb*xq/112", "0x000a00000000000d000e000f00000000-0x000a00000000000d000e000f0000ffff", "00000240000000000000032000160000740000000000-00000240000000000000032000160000740000177777") t.testIPv6Strings("a:0:c:d:e:f:0:0/112", "a:0:c:d:e:f:0:0/112", "a:0:c:d:e:f:0:*", "a:0:c:d:e:f:0:*", "a:0:c:d:e:f:0:%", "000a:0000:000c:000d:000e:000f:0000:0000/112", "a:0:c:d:e:f::/112", "a:0:c:d:e:f::/112", "a:0:c:d:e:f::/112", "a::c:d:e:f:0:*", "a::c:d:e:f:0.0.0.0/112", "a::c:d:e:f:0.0.0.0/112", "a::c:d:e:f:0.0.0.0/112", "a:0:c:d:e:f::/112", "*.*.*.*.0.0.0.0.f.0.0.0.e.0.0.0.d.0.0.0.c.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-c-d-e-f-0-0.ipv6-literal.net/112", "00|M>t};s?v~hFl`j3_$/112", "0x000a0000000c000d000e000f00000000-0x000a0000000c000d000e000f0000ffff", "00000240000000006000032000160000740000000000-00000240000000006000032000160000740000177777") t.testIPv6Strings("a:0:c:d:e:f:a:0/112", "a:0:c:d:e:f:a:0/112", "a:0:c:d:e:f:a:*", "a:0:c:d:e:f:a:*", "a:0:c:d:e:f:a:%", "000a:0000:000c:000d:000e:000f:000a:0000/112", "a::c:d:e:f:a:0/112", "a:0:c:d:e:f:a:0/112", "a:0:c:d:e:f:a::/112", "a::c:d:e:f:a:*", "a::c:d:e:f:0.10.0.0/112", "a::c:d:e:f:0.10.0.0/112", "a::c:d:e:f:0.10.0.0/112", "a::c:d:e:f:0.10.0.0/112", "*.*.*.*.a.0.0.0.f.0.0.0.e.0.0.0.d.0.0.0.c.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-c-d-e-f-a-0.ipv6-literal.net/112", "00|M>t};s?v~hFl`k9s=/112", "0x000a0000000c000d000e000f000a0000-0x000a0000000c000d000e000f000affff", "00000240000000006000032000160000740002400000-00000240000000006000032000160000740002577777") t.testIPv6Strings("a:0:c:d:0:0:0:100/120", "a:0:c:d:0:0:0:100/120", //normalized "a:0:c:d:0:0:0:100-1ff", //normalized wildcard "a:0:c:d::100-1ff", //canonical wildcard "a:0:c:d:0:0:0:1__", //sql "000a:0000:000c:000d:0000:0000:0000:0100/120", //full "a:0:c:d::100/120", //compressed "a:0:c:d::100/120", //canonical "a:0:c:d::100/120", //subnet "a:0:c:d::100-1ff", //compressed wildcard "a:0:c:d::0.0.1.0/120", //mixed, no compress "a:0:c:d::0.0.1.0/120", //mixed, no compress host "a:0:c:d::0.0.1.0/120", "a:0:c:d::0.0.1.0/120", "*.*.1.0.0.0.0.0.0.0.0.0.0.0.0.0.d.0.0.0.c.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-c-d-0-0-0-100.ipv6-literal.net/120", "00|M>t};s?v}5L>MDI>a/120", "0x000a0000000c000d0000000000000100-0x000a0000000c000d00000000000001ff", "00000240000000006000032000000000000000000400-00000240000000006000032000000000000000000777") //mixed t.testIPv6Strings("a:b:c:d:*/64", "a:b:c:d:0:0:0:0/64", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:%:%:%:%", "000a:000b:000c:000d:0000:0000:0000:0000/64", "a:b:c:d::/64", "a:b:c:d::/64", "a:b:c:d::/64", "a:b:c:d:*:*:*:*", "a:b:c:d::0.0.0.0/64", "a:b:c:d::0.0.0.0/64", "a:b:c:d::/64", "a:b:c:d::/64", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.d.0.0.0.c.0.0.0.b.0.0.0.a.0.0.0.ip6.arpa", "a-b-c-d-0-0-0-0.ipv6-literal.net/64", "00|N0s0$ND2BxK96%Chk/64", "0x000a000b000c000d0000000000000000-0x000a000b000c000dffffffffffffffff", "00000240001300006000032000000000000000000000-00000240001300006000033777777777777777777777") t.testIPv6Strings("a:b:c:d:*:*:*:*/64", "a:b:c:d:0:0:0:0/64", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:%:%:%:%", "000a:000b:000c:000d:0000:0000:0000:0000/64", "a:b:c:d::/64", "a:b:c:d::/64", "a:b:c:d::/64", "a:b:c:d:*:*:*:*", "a:b:c:d::0.0.0.0/64", "a:b:c:d::0.0.0.0/64", "a:b:c:d::/64", "a:b:c:d::/64", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.d.0.0.0.c.0.0.0.b.0.0.0.a.0.0.0.ip6.arpa", "a-b-c-d-0-0-0-0.ipv6-literal.net/64", "00|N0s0$ND2BxK96%Chk/64", "0x000a000b000c000d0000000000000000-0x000a000b000c000dffffffffffffffff", "00000240001300006000032000000000000000000000-00000240001300006000033777777777777777777777") t.testIPv6Strings("a::d:*:*:*:*/64", "a:0:0:d:0:0:0:0/64", "a:0:0:d:*:*:*:*", "a::d:*:*:*:*", "a:0:0:d:%:%:%:%", "000a:0000:0000:000d:0000:0000:0000:0000/64", "a:0:0:d::/64", "a:0:0:d::/64", "a:0:0:d::/64", "a::d:*:*:*:*", "a::d:0:0:0.0.0.0/64", "a::d:0:0:0.0.0.0/64", "a:0:0:d::/64", "a:0:0:d::/64", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.d.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-d-0-0-0-0.ipv6-literal.net/64", "00|M>t|tt+WbKhfd5~qN/64", "0x000a00000000000d0000000000000000-0x000a00000000000dffffffffffffffff", "00000240000000000000032000000000000000000000-00000240000000000000033777777777777777777777") t.testIPv6Strings("1::/32", "1:0:0:0:0:0:0:0/32", "1:0:*:*:*:*:*:*", "1:0:*:*:*:*:*:*", "1:0:%:%:%:%:%:%", "0001:0000:0000:0000:0000:0000:0000:0000/32", "1::/32", "1::/32", "1::/32", "1::*:*:*:*:*:*", "1::0.0.0.0/32", "1::0.0.0.0/32", "1::/32", "1::/32", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.0.0.0.0.1.0.0.0.ip6.arpa", "1-0-0-0-0-0-0-0.ipv6-literal.net/32", "008JOm8Mm5*yBppL!sg1/32", "0x00010000000000000000000000000000-0x00010000ffffffffffffffffffffffff", "00000020000000000000000000000000000000000000-00000020000077777777777777777777777777777777") t.testIPv6Strings("ffff::/104", "ffff:0:0:0:0:0:0:0/104", "ffff:0:0:0:0:0:0-ff:*", "ffff::0-ff:*", "ffff:0:0:0:0:0:0-ff:%", "ffff:0000:0000:0000:0000:0000:0000:0000/104", "ffff::/104", "ffff::/104", "ffff::/104", "ffff::0-ff:*", "ffff::0.0.0.0/104", "ffff::0.0.0.0/104", "ffff::0.0.0.0/104", "ffff::/104", "*.*.*.*.*.*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.f.f.f.ip6.arpa", "ffff-0-0-0-0-0-0-0.ipv6-literal.net/104", "=q{+M|w0(OeO5^EGP660/104", "0xffff0000000000000000000000000000-0xffff0000000000000000000000ffffff", "03777760000000000000000000000000000000000000-03777760000000000000000000000000000077777777") t.testIPv6Strings("ffff::/108", "ffff:0:0:0:0:0:0:0/108", "ffff:0:0:0:0:0:0-f:*", "ffff::0-f:*", "ffff:0:0:0:0:0:_:%", "ffff:0000:0000:0000:0000:0000:0000:0000/108", "ffff::/108", "ffff::/108", "ffff::/108", "ffff::0-f:*", "ffff::0.0.0.0/108", "ffff::0.0.0.0/108", "ffff::0.0.0.0/108", "ffff::/108", "*.*.*.*.*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.f.f.f.ip6.arpa", "ffff-0-0-0-0-0-0-0.ipv6-literal.net/108", "=q{+M|w0(OeO5^EGP660/108", "0xffff0000000000000000000000000000-0xffff00000000000000000000000fffff", "03777760000000000000000000000000000000000000-03777760000000000000000000000000000003777777") t.testIPv6Strings("ffff::1000:0/108", "ffff:0:0:0:0:0:1000:0/108", "ffff:0:0:0:0:0:1000-100f:*", "ffff::1000-100f:*", "ffff:0:0:0:0:0:100_:%", "ffff:0000:0000:0000:0000:0000:1000:0000/108", "ffff::1000:0/108", "ffff::1000:0/108", "ffff:0:0:0:0:0:1000::/108", "ffff::1000-100f:*", "ffff::16.0.0.0/108", "ffff::16.0.0.0/108", "ffff::16.0.0.0/108", "ffff::16.0.0.0/108", "*.*.*.*.*.0.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.f.f.f.ip6.arpa", "ffff-0-0-0-0-0-1000-0.ipv6-literal.net/108", "=q{+M|w0(OeO5^ELbE%G/108", "0xffff0000000000000000000010000000-0xffff00000000000000000000100fffff", "03777760000000000000000000000000002000000000-03777760000000000000000000000000002003777777") t.testIPv6Strings("ffff::a000:0/108", "ffff:0:0:0:0:0:a000:0/108", "ffff:0:0:0:0:0:a000-a00f:*", "ffff::a000-a00f:*", "ffff:0:0:0:0:0:a00_:%", "ffff:0000:0000:0000:0000:0000:a000:0000/108", "ffff::a000:0/108", "ffff::a000:0/108", "ffff:0:0:0:0:0:a000::/108", "ffff::a000-a00f:*", "ffff::160.0.0.0/108", "ffff::160.0.0.0/108", "ffff::160.0.0.0/108", "ffff::160.0.0.0/108", "*.*.*.*.*.0.0.a.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.f.f.f.ip6.arpa", "ffff-0-0-0-0-0-a000-0.ipv6-literal.net/108", "=q{+M|w0(OeO5^E(z82>/108", "0xffff00000000000000000000a0000000-0xffff00000000000000000000a00fffff", "03777760000000000000000000000000024000000000-03777760000000000000000000000000024003777777") t.testIPv6Strings("ffff::/107", "ffff:0:0:0:0:0:0:0/107", "ffff:0:0:0:0:0:0-1f:*", "ffff::0-1f:*", "ffff:0:0:0:0:0:0-1f:%", "ffff:0000:0000:0000:0000:0000:0000:0000/107", "ffff::/107", "ffff::/107", "ffff::/107", "ffff::0-1f:*", "ffff::0.0.0.0/107", "ffff::0.0.0.0/107", "ffff::0.0.0.0/107", "ffff::/107", "*.*.*.*.*.0-1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.f.f.f.ip6.arpa", "ffff-0-0-0-0-0-0-0.ipv6-literal.net/107", "=q{+M|w0(OeO5^EGP660/107", "0xffff0000000000000000000000000000-0xffff00000000000000000000001fffff", "03777760000000000000000000000000000000000000-03777760000000000000000000000000000007777777") t.testIPv6Strings("abcd::/107", "abcd:0:0:0:0:0:0:0/107", "abcd:0:0:0:0:0:0-1f:*", "abcd::0-1f:*", "abcd:0:0:0:0:0:0-1f:%", "abcd:0000:0000:0000:0000:0000:0000:0000/107", "abcd::/107", "abcd::/107", "abcd::/107", "abcd::0-1f:*", "abcd::0.0.0.0/107", "abcd::0.0.0.0/107", "abcd::0.0.0.0/107", "abcd::/107", "*.*.*.*.*.0-1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.ip6.arpa", "abcd-0-0-0-0-0-0-0.ipv6-literal.net/107", "o6)n`s#^$cP5&p^H}p=a/107", "0xabcd0000000000000000000000000000-0xabcd00000000000000000000001fffff", "02536320000000000000000000000000000000000000-02536320000000000000000000000000000007777777") t.testIPv6Strings("1:2:3:4::/80", "1:2:3:4:0:0:0:0/80", //normalized "1:2:3:4:0:*:*:*", //normalizedWildcards "1:2:3:4:0:*:*:*", //canonicalWildcards "1:2:3:4:0:%:%:%", //sql "0001:0002:0003:0004:0000:0000:0000:0000/80", "1:2:3:4::/80", //compressed "1:2:3:4::/80", "1:2:3:4::/80", "1:2:3:4::*:*:*", "1:2:3:4::0.0.0.0/80", //mixed no compress "1:2:3:4::0.0.0.0/80", //mixedNoCompressHost "1:2:3:4::/80", "1:2:3:4::/80", "*.*.*.*.*.*.*.*.*.*.*.*.0.0.0.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.ip6.arpa", "1-2-3-4-0-0-0-0.ipv6-literal.net/80", "008JQWOV7Skb)C|ve)jA/80", "0x00010002000300040000000000000000-0x00010002000300040000ffffffffffff", "00000020000200001400010000000000000000000000-00000020000200001400010000007777777777777777") t.testIPv6Strings("a:b:c:*:*:*:*:*", //as noted above, addresses are not converted to prefix if starting as wildcards. "a:b:c:*:*:*:*:*", "a:b:c:*:*:*:*:*", "a:b:c:*:*:*:*:*", "a:b:c:%:%:%:%:%", "000a:000b:000c:0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff", "a:b:c:*:*:*:*:*", "a:b:c:*:*:*:*:*", "a:b:c:*:*:*:*:*", "a:b:c:*:*:*:*:*", "a:b:c:*:*:*:*.*.*.*", "a:b:c:*:*:*:*.*.*.*", "a:b:c:*:*:*:*.*.*.*", "a:b:c:*:*:*:*.*.*.*", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.c.0.0.0.b.0.0.0.a.0.0.0.ip6.arpa", "a-b-c-*-*-*-*-*.ipv6-literal.net", "00|N0s0$N0-%*(tF5l-X"+ipaddr.IPv6AlternativeRangeSeparatorStr+"00|N0s0;%a&*sUa#KSGX", "0x000a000b000c00000000000000000000-0x000a000b000cffffffffffffffffffff", "00000240001300006000000000000000000000000000-00000240001300006377777777777777777777777777") t.testIPv6Strings("a:b:c:d:*", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:%:%:%:%", "000a:000b:000c:000d:0000-ffff:0000-ffff:0000-ffff:0000-ffff", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*.*.*.*", "a:b:c:d:*:*:*.*.*.*", "a:b:c:d:*:*:*.*.*.*", "a:b:c:d:*:*:*.*.*.*", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.d.0.0.0.c.0.0.0.b.0.0.0.a.0.0.0.ip6.arpa", "a-b-c-d-*-*-*-*.ipv6-literal.net", "00|N0s0$ND2BxK96%Chk"+ipaddr.IPv6AlternativeRangeSeparatorStr+"00|N0s0$ND{&WM}~o9(k", "0x000a000b000c000d0000000000000000-0x000a000b000c000dffffffffffffffff", "00000240001300006000032000000000000000000000-00000240001300006000033777777777777777777777") t.testIPv6Strings("a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:%:%:%:%", "000a:000b:000c:000d:0000-ffff:0000-ffff:0000-ffff:0000-ffff", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*:*", "a:b:c:d:*:*:*.*.*.*", "a:b:c:d:*:*:*.*.*.*", "a:b:c:d:*:*:*.*.*.*", "a:b:c:d:*:*:*.*.*.*", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.d.0.0.0.c.0.0.0.b.0.0.0.a.0.0.0.ip6.arpa", "a-b-c-d-*-*-*-*.ipv6-literal.net", "00|N0s0$ND2BxK96%Chk"+ipaddr.IPv6AlternativeRangeSeparatorStr+"00|N0s0$ND{&WM}~o9(k", "0x000a000b000c000d0000000000000000-0x000a000b000c000dffffffffffffffff", "00000240001300006000032000000000000000000000-00000240001300006000033777777777777777777777") t.testIPv6Strings("a::c:d:*", "a:0:0:0:0:c:d:*", "a:0:0:0:0:c:d:*", "a::c:d:*", "a:0:0:0:0:c:d:%", "000a:0000:0000:0000:0000:000c:000d:0000-ffff", "a::c:d:*", "a::c:d:*", "a::c:d:*", "a::c:d:*", "a::c:0.13.*.*", "a::c:0.13.*.*", "a::c:0.13.*.*", "a::c:0.13.*.*", "*.*.*.*.d.0.0.0.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-0-0-c-d-*.ipv6-literal.net", "00|M>t|ttwH6V6EEzblZ"+ipaddr.IPv6AlternativeRangeSeparatorStr+"00|M>t|ttwH6V6EEzkrZ", "0x000a0000000000000000000c000d0000-0x000a0000000000000000000c000dffff", "00000240000000000000000000000000600003200000-00000240000000000000000000000000600003377777") t.testIPv6Strings("a::d:*:*:*:*", "a:0:0:d:*:*:*:*", "a:0:0:d:*:*:*:*", "a::d:*:*:*:*", "a:0:0:d:%:%:%:%", "000a:0000:0000:000d:0000-ffff:0000-ffff:0000-ffff:0000-ffff", "a::d:*:*:*:*", "a::d:*:*:*:*", "a::d:*:*:*:*", "a::d:*:*:*:*", "a::d:*:*:*.*.*.*", "a::d:*:*:*.*.*.*", "a::d:*:*:*.*.*.*", "a::d:*:*:*.*.*.*", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.d.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-d-*-*-*-*.ipv6-literal.net", "00|M>t|tt+WbKhfd5~qN"+ipaddr.IPv6AlternativeRangeSeparatorStr+"00|M>t|tt-R6^kVV>{?N", "0x000a00000000000d0000000000000000-0x000a00000000000dffffffffffffffff", "00000240000000000000032000000000000000000000-00000240000000000000033777777777777777777777") t.testIPv6Strings("a::c:d:*/64", "a:0:0:0:0:c:d:*/64", "a:0:0:0:0:c:d:*", "a::c:d:*", "a:0:0:0:0:c:d:%", "000a:0000:0000:0000:0000:000c:000d:0000-ffff/64", "a::c:d:*/64", "a::c:d:*/64", "a::c:d:*/64", "a::c:d:*", "a::c:0.13.*.*/64", "a::c:0.13.*.*/64", "a::c:0.13.*.*/64", "a::c:0.13.*.*/64", "*.*.*.*.d.0.0.0.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-0-0-c-d-*.ipv6-literal.net/64", "00|M>t|ttwH6V6EEzblZ"+ipaddr.IPv6AlternativeRangeSeparatorStr+"00|M>t|ttwH6V6EEzkrZ/64", "0x000a0000000000000000000c000d0000-0x000a0000000000000000000c000dffff", "00000240000000000000000000000000600003200000-00000240000000000000000000000000600003377777") t.testIPv6Strings("a::c:d:*/80", //similar to above, but allows us to test the base 85 string with non-64 bit prefix "a:0:0:0:0:c:d:*/80", "a:0:0:0:0:c:d:*", "a::c:d:*", "a:0:0:0:0:c:d:%", "000a:0000:0000:0000:0000:000c:000d:0000-ffff/80", "a::c:d:*/80", "a::c:d:*/80", "a::c:d:*/80", "a::c:d:*", "a::c:0.13.*.*/80", "a::c:0.13.*.*/80", "a::c:0.13.*.*/80", "a::c:0.13.*.*/80", "*.*.*.*.d.0.0.0.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-0-0-c-d-*.ipv6-literal.net/80", "00|M>t|ttwH6V6EEzblZ"+ipaddr.IPv6AlternativeRangeSeparatorStr+"00|M>t|ttwH6V6EEzkrZ/80", "0x000a0000000000000000000c000d0000-0x000a0000000000000000000c000dffff", "00000240000000000000000000000000600003200000-00000240000000000000000000000000600003377777") t.testIPv6Strings("a::c:d:*/48", //similar to above, but allows us to test the base 85 string with non-64 bit prefix "a:0:0:0:0:c:d:*/48", "a:0:0:0:0:c:d:*", "a::c:d:*", "a:0:0:0:0:c:d:%", "000a:0000:0000:0000:0000:000c:000d:0000-ffff/48", "a::c:d:*/48", "a::c:d:*/48", "a::c:d:*/48", "a::c:d:*", "a::c:0.13.*.*/48", "a::c:0.13.*.*/48", "a::c:0.13.*.*/48", "a::c:0.13.*.*/48", "*.*.*.*.d.0.0.0.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.a.0.0.0.ip6.arpa", "a-0-0-0-0-c-d-*.ipv6-literal.net/48", "00|M>t|ttwH6V6EEzblZ"+ipaddr.IPv6AlternativeRangeSeparatorStr+"00|M>t|ttwH6V6EEzkrZ/48", "0x000a0000000000000000000c000d0000-0x000a0000000000000000000c000dffff", "00000240000000000000000000000000600003200000-00000240000000000000000000000000600003377777") t.testIPv4Strings("1.2.0.4/16", "1.2.0.4/16", "1.2.0.4", "1.2.0.4", "001.002.000.004/16", "01.02.00.04/16", "0x1.0x2.0x0.0x4/16", "4.0.2.1.in-addr.arpa", "0x01020004", "000100400004") t.testIPv4Strings("1.2.3.0/16", "1.2.3.0/16", "1.2.3.0", "1.2.3.0", "001.002.003.000/16", "01.02.03.00/16", "0x1.0x2.0x3.0x0/16", "0.3.2.1.in-addr.arpa", "0x01020300", "000100401400") t.testIPv4Strings("1.2.0.0/14", "1.2.0.0/14", "1.2.0.0", "1.2.0.0", "001.002.000.000/14", "01.02.00.00/14", "0x1.0x2.0x0.0x0/14", "0.0.2.1.in-addr.arpa", "0x01020000", "000100400000") t.testIPv4Strings("1.2.*.4/16", "1.2.*.4/16", "1.2.*.4", "1.2.%.4", "001.002.000-255.004/16", "01.02.*.04/16", "0x1.0x2.*.0x4/16", "4.*.2.1.in-addr.arpa", "", "") t.testIPv4Strings("1.2.3.*/16", "1.2.3.*/16", "1.2.3.*", "1.2.3.%", "001.002.003.000-255/16", "01.02.03.*/16", "0x1.0x2.0x3.*/16", "*.3.2.1.in-addr.arpa", "0x01020300-0x010203ff", "000100401400-000100401777") t.testIPv4Strings("1.2.*.*/14", "1.2.*.*/14", "1.2.*.*", "1.2.%.%", "001.002.000-255.000-255/14", "01.02.*.*/14", "0x1.0x2.*.*/14", "*.*.2.1.in-addr.arpa", "0x01020000-0x0102ffff", "000100400000-000100577777") //000100400000-000100400000/14" t.testIPv6Strings("ffff::/8", "ffff:0:0:0:0:0:0:0/8", "ffff:0:0:0:0:0:0:0", "ffff::", "ffff:0:0:0:0:0:0:0", "ffff:0000:0000:0000:0000:0000:0000:0000/8", "ffff::/8", "ffff::/8", "ffff::/8", "ffff::", "ffff::0.0.0.0/8", "ffff::0.0.0.0/8", "ffff::/8", "ffff::/8", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.f.f.f.ip6.arpa", "ffff-0-0-0-0-0-0-0.ipv6-literal.net/8", "=q{+M|w0(OeO5^EGP660/8", "0xffff0000000000000000000000000000", "03777760000000000000000000000000000000000000") t.testIPv6Strings("ffff::eeee:eeee/108", "ffff:0:0:0:0:0:eeee:eeee/108", "ffff:0:0:0:0:0:eeee:eeee", "ffff::eeee:eeee", "ffff:0:0:0:0:0:eeee:eeee", "ffff:0000:0000:0000:0000:0000:eeee:eeee/108", "ffff::eeee:eeee/108", "ffff::eeee:eeee/108", "ffff::eeee:eeee/108", "ffff::eeee:eeee", "ffff::238.238.238.238/108", "ffff::238.238.238.238/108", "ffff::238.238.238.238/108", "ffff::238.238.238.238/108", "e.e.e.e.e.e.e.e.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.f.f.f.ip6.arpa", "ffff-0-0-0-0-0-eeee-eeee.ipv6-literal.net/108", "=q{+M|w0(OeO5^F87dpH/108", "0xffff00000000000000000000eeeeeeee", "03777760000000000000000000000000035673567356") t.testIPv6Strings("1:2:3:4::%x%x%", //Note: % is the zone character (not sql wildcard), so this is handled as 1:2:3:4:: with zone x%x% "1:2:3:4:0:0:0:0%x%x%", //normalized "1:2:3:4:0:0:0:0%x%x%", //normalizedWildcards "1:2:3:4::%x%x%", //canonicalWildcards "1:2:3:4:0:0:0:0%x%x%", //sql "0001:0002:0003:0004:0000:0000:0000:0000%x%x%", "1:2:3:4::%x%x%", //compressed "1:2:3:4::%x%x%", //canonical "1:2:3:4::%x%x%", //subnet "1:2:3:4::%x%x%", //compressed wildcard "1:2:3:4::0.0.0.0%x%x%", //mixed no compress "1:2:3:4::%x%x%", //mixedNoCompressHost "1:2:3:4::%x%x%", "1:2:3:4::%x%x%", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.ip6.arpa", "1-2-3-4-0-0-0-0sx%x%.ipv6-literal.net", //the first % is converted to s, the rest is not, resul "008JQWOV7Skb)C|ve)jA"+ipaddr.IPv6AlternativeZoneSeparatorStr+"x%x%", "0x00010002000300040000000000000000%x%x%", "00000020000200001400010000000000000000000000%x%x%") //mixed t.testIPv6Strings("1:2:3:4::%seth0", //Note: % is the zone character (not sql wildcard), so this is handled as 1:2:3:4:: with zone x%x% "1:2:3:4:0:0:0:0%seth0", //normalized "1:2:3:4:0:0:0:0%seth0", //normalizedWildcards "1:2:3:4::%seth0", //canonicalWildcards "1:2:3:4:0:0:0:0%seth0", //sql "0001:0002:0003:0004:0000:0000:0000:0000%seth0", "1:2:3:4::%seth0", //compressed "1:2:3:4::%seth0", //canonical "1:2:3:4::%seth0", //subnet "1:2:3:4::%seth0", //compressed wildcard "1:2:3:4::0.0.0.0%seth0", //mixed no compress "1:2:3:4::%seth0", //mixedNoCompressHost "1:2:3:4::%seth0", "1:2:3:4::%seth0", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.ip6.arpa", "1-2-3-4-0-0-0-0sseth0.ipv6-literal.net", "008JQWOV7Skb)C|ve)jA"+ipaddr.IPv6AlternativeZoneSeparatorStr+"seth0", "0x00010002000300040000000000000000%seth0", "00000020000200001400010000000000000000000000%seth0") //mixed t.testIPv6Strings("1:2:3:4:5:6:7:8%a/64", //Note: % is the zone character (not sql wildcard), so this is handled as 1:2:3:4:: with zone :%:% "1:2:3:4:5:6:7:8%a/64", //normalized "1:2:3:4:5:6:7:8%a", //normalizedWildcards "1:2:3:4:5:6:7:8%a", //canonicalWildcards "1:2:3:4:5:6:7:8%a", //sql "0001:0002:0003:0004:0005:0006:0007:0008%a/64", "1:2:3:4:5:6:7:8%a/64", //compressed "1:2:3:4:5:6:7:8%a/64", //canonical "1:2:3:4:5:6:7:8%a/64", //subnet "1:2:3:4:5:6:7:8%a", //compressed wildcard "1:2:3:4:5:6:0.7.0.8%a/64", //mixed no compress "1:2:3:4:5:6:0.7.0.8%a/64", //mixedNoCompressHost "1:2:3:4:5:6:0.7.0.8%a/64", "1:2:3:4:5:6:0.7.0.8%a/64", "8.0.0.0.7.0.0.0.6.0.0.0.5.0.0.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.ip6.arpa", "1-2-3-4-5-6-7-8sa.ipv6-literal.net/64", "008JQWOV7SkcR4tS1R_a"+ipaddr.IPv6AlternativeZoneSeparatorStr+"a/64", "0x00010002000300040005000600070008%a", "00000020000200001400010000050000300001600010%a") t.testIPv6Strings("1:2:3:4::%a/64", //Note: % is the zone character (not sql wildcard), so this is handled as 1:2:3:4:: with zone :%:% "1:2:3:4:0:0:0:0%a/64", //normalized "1:2:3:4:*:*:*:*%a", //normalizedWildcards "1:2:3:4:*:*:*:*%a", //canonicalWildcards "1:2:3:4:%:%:%:%%a", //sql "0001:0002:0003:0004:0000:0000:0000:0000%a/64", "1:2:3:4::%a/64", //compressed "1:2:3:4::%a/64", //canonical "1:2:3:4::%a/64", //subnet "1:2:3:4:*:*:*:*%a", //compressed wildcard "1:2:3:4::0.0.0.0%a/64", //mixed no compress "1:2:3:4::0.0.0.0%a/64", //mixedNoCompressHost "1:2:3:4::%a/64", "1:2:3:4::%a/64", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.ip6.arpa", "1-2-3-4-0-0-0-0sa.ipv6-literal.net/64", "008JQWOV7Skb)C|ve)jA"+ipaddr.IPv6AlternativeZoneSeparatorStr+"a/64", "0x00010002000300040000000000000000-0x0001000200030004ffffffffffffffff%a", "00000020000200001400010000000000000000000000-00000020000200001400011777777777777777777777%a") t.testIPv6Strings("1:2:3:4::%.a.a", //Note: % is the zone character (not sql wildcard), so this is handled as 1:2:3:4:: with zone .a.a "1:2:3:4:0:0:0:0%.a.a", //normalized "1:2:3:4:0:0:0:0%.a.a", //normalizedWildcards "1:2:3:4::%.a.a", //canonicalWildcards "1:2:3:4:0:0:0:0%.a.a", //sql "0001:0002:0003:0004:0000:0000:0000:0000%.a.a", "1:2:3:4::%.a.a", //compressed "1:2:3:4::%.a.a", //canonical "1:2:3:4::%.a.a", //subnet "1:2:3:4::%.a.a", //compressed wildcard "1:2:3:4::0.0.0.0%.a.a", //mixed no compress "1:2:3:4::%.a.a", //mixedNoCompressHost "1:2:3:4::%.a.a", "1:2:3:4::%.a.a", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.ip6.arpa", "1-2-3-4-0-0-0-0s.a.a.ipv6-literal.net", "008JQWOV7Skb)C|ve)jA"+ipaddr.IPv6AlternativeZoneSeparatorStr+".a.a", "0x00010002000300040000000000000000%.a.a", "00000020000200001400010000000000000000000000%.a.a") //mixed t.testIPv6Strings("1:2:3:4::*:*:*", "1:2:3:4:0:*:*:*", //normalized "1:2:3:4:0:*:*:*", //normalizedWildcards "1:2:3:4:0:*:*:*", //canonicalWildcards "1:2:3:4:0:%:%:%", //sql "0001:0002:0003:0004:0000:0000-ffff:0000-ffff:0000-ffff", "1:2:3:4::*:*:*", //compressed "1:2:3:4:0:*:*:*", //canonical "1:2:3:4::*:*:*", //subnet "1:2:3:4::*:*:*", //compressed wildcard "1:2:3:4::*:*.*.*.*", //mixed no compress "1:2:3:4::*:*.*.*.*", //mixedNoCompressHost "1:2:3:4::*:*.*.*.*", "1:2:3:4::*:*.*.*.*", "*.*.*.*.*.*.*.*.*.*.*.*.0.0.0.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.ip6.arpa", "1-2-3-4-0-*-*-*.ipv6-literal.net", "008JQWOV7Skb)C|ve)jA"+ipaddr.IPv6AlternativeRangeSeparatorStr+"008JQWOV7Skb?_P3;X#A", "0x00010002000300040000000000000000-0x00010002000300040000ffffffffffff", "00000020000200001400010000000000000000000000-00000020000200001400010000007777777777777777") t.testIPv6Strings("1:2:3:4::", "1:2:3:4:0:0:0:0", //normalized "1:2:3:4:0:0:0:0", //normalizedWildcards "1:2:3:4::", //canonicalWildcards "1:2:3:4:0:0:0:0", //sql "0001:0002:0003:0004:0000:0000:0000:0000", "1:2:3:4::", //compressed "1:2:3:4::", "1:2:3:4::", "1:2:3:4::", "1:2:3:4::0.0.0.0", //mixed no compress "1:2:3:4::", //mixedNoCompressHost "1:2:3:4::", "1:2:3:4::", "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.ip6.arpa", "1-2-3-4-0-0-0-0.ipv6-literal.net", "008JQWOV7Skb)C|ve)jA", "0x00010002000300040000000000000000", "00000020000200001400010000000000000000000000") //mixed t.testIPv6Strings("1:2:3:4:0:6::", "1:2:3:4:0:6:0:0", //normalized "1:2:3:4:0:6:0:0", //normalizedWildcards "1:2:3:4:0:6::", //canonicalWildcards "1:2:3:4:0:6:0:0", //sql "0001:0002:0003:0004:0000:0006:0000:0000", "1:2:3:4:0:6::", //compressed "1:2:3:4:0:6::", "1:2:3:4:0:6::", //subnet "1:2:3:4:0:6::", //compressedWildcard "1:2:3:4::6:0.0.0.0", //mixed no compress "1:2:3:4:0:6::", //mixedNoCompressHost "1:2:3:4:0:6::", "1:2:3:4:0:6::", "0.0.0.0.0.0.0.0.6.0.0.0.0.0.0.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.ip6.arpa", "1-2-3-4-0-6-0-0.ipv6-literal.net", "008JQWOV7Skb)D3fCrWG", "0x00010002000300040000000600000000", "00000020000200001400010000000000300000000000") t.testIPv6Strings("1:2:3:0:0:6::", "1:2:3:0:0:6:0:0", //normalized "1:2:3:0:0:6:0:0", //normalizedWildcards "1:2:3::6:0:0", //canonicalWildcards "1:2:3:0:0:6:0:0", //sql "0001:0002:0003:0000:0000:0006:0000:0000", "1:2:3::6:0:0", //compressed "1:2:3::6:0:0", "1:2:3::6:0:0", //subnet "1:2:3::6:0:0", //compressedWildcard "1:2:3::6:0.0.0.0", //mixed no compress "1:2:3::6:0.0.0.0", //mixedNoCompressHost "1:2:3::6:0.0.0.0", "1:2:3:0:0:6::", "0.0.0.0.0.0.0.0.6.0.0.0.0.0.0.0.0.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.ip6.arpa", "1-2-3-0-0-6-0-0.ipv6-literal.net", "008JQWOV7O(=61h*;$LC", "0x00010002000300000000000600000000", "00000020000200001400000000000000300000000000") t.testFmtStrings("1.2.3.4", "1.2.3.4", "1.2.3.4", "01020304", "01020304", "0x01020304", "0x01020304", "00100401404", "0o00100401404", "000100401404", "00000001000000100000001100000100", "0b00000001000000100000001100000100", "0016909060") t.testFmtStrings("255.2.0.0/16", "255.2.0.0/16", "255.2.0.0/16", "ff020000-ff02ffff", "FF020000-FF02FFFF", "0xff020000-0xff02ffff", "0xFF020000-0xFF02FFFF", "37700400000-37700577777", "0o37700400000-0o37700577777", "037700400000-037700577777", "11111111000000100000000000000000-11111111000000101111111111111111", "0b11111111000000100000000000000000-0b11111111000000101111111111111111", "4278321152-4278386687") //fmt.Println("default addr is: " + ipaddr.NewIPAddressString("").GetAddress().String()) 0.0.0.0 t.testFmtStringsIP(&ipaddr.IPAddress{}, "", "", "", "", "", "", "", "", "", "", "", "") t.testFmtStrings("100:100:ff0a:b0c:100:100:ff0a:b0c", "100:100:ff0a:b0c:100:100:ff0a:b0c", "100:100:ff0a:b0c:100:100:ff0a:b0c", "01000100ff0a0b0c01000100ff0a0b0c", "01000100FF0A0B0C01000100FF0A0B0C", "0x01000100ff0a0b0c01000100ff0a0b0c", "0x01000100FF0A0B0C01000100FF0A0B0C", "0010000040077605013030004000020037702405414", "0o0010000040077605013030004000020037702405414", "00010000040077605013030004000020037702405414", "00000001000000000000000100000000111111110000101000001011000011000000000100000000000000010000000011111111000010100000101100001100", "0b00000001000000000000000100000000111111110000101000001011000011000000000100000000000000010000000011111111000010100000101100001100", "001329248357125338454677668972538235660") } func (t ipAddressRangeTester) testFmtStrings( addr string, defaultStr, strString, lowerHex, upperHex, lowerHexPrefixed, upperHexPrefixed, octalNoPrefix, octalPrefixed, octalOPrefix, binary, binaryPrefixed, decimal string) { w := t.createAddress(addr) ipAddress := w.GetAddress() t.testFmtStringsIP(ipAddress, defaultStr, strString, lowerHex, upperHex, lowerHexPrefixed, upperHexPrefixed, octalNoPrefix, octalPrefixed, octalOPrefix, binary, binaryPrefixed, decimal) } func (t ipAddressRangeTester) testFmtStringsIP( ipAddress *ipaddr.IPAddress, defaultStr, strString, lowerHex, upperHex, lowerHexPrefixed, upperHexPrefixed, octalNoPrefix, octalPrefixed, octalOPrefix, binary, binaryPrefixed, decimal string) { quotedStr := "\"" + defaultStr + "\"" backtickStr := "`" + defaultStr + "`" expectedString := defaultStr + " " + strString + " " + quotedStr + " " + backtickStr + " " + lowerHex + " " + upperHex + " " + lowerHexPrefixed + " " + upperHexPrefixed + " " + octalNoPrefix + " " + octalPrefixed + " " + octalOPrefix + " " + binary + " " + binaryPrefixed + " " + decimal formatString := "%v %s %q %#q %x %X %#x %#X %o %O %#o %b %#b %d" var ipaddrs1, ipaddrs2, ipaddrs3, ipaddrs4 []interface{} var slice1 []ipaddr.IPAddress var slice2 []*ipaddr.IPAddress var slice3 []ipaddr.Address var slice4 []*ipaddr.Address var slice5 []interface{} expectedDefaults := "[" for i := 0; i < 14; i++ { ipaddrs1 = append(ipaddrs1, ipAddress) ipaddrs2 = append(ipaddrs2, *ipAddress) ipaddrs3 = append(ipaddrs3, ipAddress.ToAddressBase()) ipaddrs4 = append(ipaddrs4, *ipAddress.ToAddressBase()) slice1 = append(slice1, *ipAddress) slice2 = append(slice2, ipAddress) slice3 = append(slice3, *ipAddress.ToAddressBase()) slice4 = append(slice4, ipAddress.ToAddressBase()) if i%4 == 0 { slice5 = append(slice5, *ipAddress) } else if i%4 == 1 { slice5 = append(slice5, ipAddress) } else if i%4 == 2 { slice5 = append(slice5, *ipAddress.ToAddressBase()) } else if i%4 == 3 { slice5 = append(slice5, ipAddress.ToAddressBase()) } expectedDefaults += ipAddress.String() if i < 13 { expectedDefaults += " " } } expectedDefaults += "]" result1 := fmt.Sprintf(formatString, ipaddrs1...) result2 := fmt.Sprintf(formatString, ipaddrs2...) result3 := fmt.Sprintf(formatString, ipaddrs3...) result4 := fmt.Sprintf(formatString, ipaddrs4...) result5 := fmt.Sprint(slice1) result6 := fmt.Sprint(slice2) result7 := fmt.Sprint(slice3) result8 := fmt.Sprint(slice4) result9 := fmt.Sprint(slice5) if result1 != expectedString { // x "" `` x vs x "" `` 0x t.addFailure(newIPAddrFailure("failed expected: "+expectedString+" actual: "+result1, ipAddress)) } else if result2 != expectedString { t.addFailure(newIPAddrFailure("failed expected: "+expectedString+" actual: "+result2, ipAddress)) } else if result3 != expectedString { t.addFailure(newIPAddrFailure("failed expected: "+expectedString+" actual: "+result3, ipAddress)) } else if result4 != expectedString { t.addFailure(newIPAddrFailure("failed expected: "+expectedString+" actual: "+result4, ipAddress)) } else if result5 != expectedDefaults { t.addFailure(newIPAddrFailure("failed expected: "+expectedString+" actual: "+result5, ipAddress)) } else if result6 != expectedDefaults { t.addFailure(newIPAddrFailure("failed expected: "+expectedString+" actual: "+result6, ipAddress)) } else if result7 != expectedDefaults { t.addFailure(newIPAddrFailure("failed expected: "+expectedString+" actual: "+result7, ipAddress)) } else if result8 != expectedDefaults { t.addFailure(newIPAddrFailure("failed expected: "+expectedString+" actual: "+result8, ipAddress)) } else if result9 != expectedDefaults { t.addFailure(newIPAddrFailure("failed expected: "+expectedString+" actual: "+result9, ipAddress)) } } func enlargeSubnetStr(str *ipaddr.IPAddressString /*boolean nextSegment false , int bitsPerSegment, /* boolean skipBitCountPrefix false */) *ipaddr.IPAddressString { addr := str.GetAddress() if addr == nil { return nil } res := enlargeSubnet(addr) if res == addr { if !res.IsPrefixBlock() { return nil } return ipaddr.NewIPAddressString(ipaddr.SegmentWildcardStr) } return res.ToAddressString() } func enlargeSubnet(addr *ipaddr.IPAddress /*boolean nextSegment false , int bitsPerSegment, /* boolean skipBitCountPrefix false */) *ipaddr.IPAddress { prefix := addr.GetNetworkPrefixLen() if prefix == nil { return addr.SetPrefixLen(addr.GetBitCount()) } prefLen := prefix.Len() if prefLen == 0 { return addr } adjustment := ((prefLen - 1) % addr.GetBitsPerSegment()) + 1 addr, _ = addr.SetPrefixLenZeroed(prefLen - adjustment) if addr.GetLower().IsZeroHost() { addr = addr.ToPrefixBlock() } return addr } func getLabel(addressString *ipaddr.IPAddressString) string { address := addressString.GetAddress() if address == nil { return addressString.String() } if !address.IsMultiple() { return address.ToPrefixLenString() } return address.ToSubnetString() } func asSlice(addrs []*ipaddr.IPAddress) (result []string) { if addrLen := len(addrs); addrLen > 0 { result = make([]string, 0, addrLen) for _, addr := range addrs { result = append(result, addr.ToNormalizedWildcardString()) } } return } func asSliceString(addrs []*ipaddr.IPAddress) string { return fmt.Sprint(asSlice(addrs)) } func asRangeSlice(addrs []*ipaddr.IPAddressSeqRange) (result []string) { if addrLen := len(addrs); addrLen > 0 { result = make([]string, 0, addrLen) for _, addr := range addrs { result = append(result, addr.ToNormalizedString()) } } return } func asRangeSliceString(addrs []*ipaddr.IPAddressSeqRange) string { return fmt.Sprintf("%v", asRangeSlice(addrs)) } ipaddress-go-1.5.4/ipaddr/test/ipaddrtest.go000066400000000000000000006640151440250641600210340ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package test import ( "bytes" "fmt" "math" "math/big" "net" "strconv" "strings" "github.com/seancfoley/ipaddress-go/ipaddr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) type ipAddressTester struct { testBase } func (t ipAddressTester) run() { t.testIPv4Mapped("::ffff:c0a8:0a14", true) t.testIPv4Mapped("0:0:0:0:0:ffff:c0a8:0a14", true) t.testIPv4Mapped("::ffff:1.2.3.4", true) t.testIPv4Mapped("0:0:0:0:0:ffff:1.2.3.4", true) t.testIPv4Mapped("::1:ffff:c0a8:0a14", false) t.testIPv4Mapped("0:0:0:0:1:ffff:c0a8:0a14", false) t.testIPv4Mapped("::1:ffff:1.2.3.4", false) t.testIPv4Mapped("0:0:0:0:1:ffff:1.2.3.4", false) t.testEquivalentPrefix("1.2.3.4", 32) t.testEquivalentPrefix("0.0.0.0/1", 1) t.testEquivalentPrefix("128.0.0.0/1", 1) t.testEquivalentPrefix("1.2.0.0/15", 15) t.testEquivalentPrefix("1.2.0.0/16", 16) t.testEquivalentPrefix("1:2::/32", 32) t.testEquivalentPrefix("8000::/1", 1) t.testEquivalentPrefix("1:2::/31", 31) t.testEquivalentPrefix("1:2::/34", 34) t.testEquivalentPrefix("1.2.3.4/32", 32) t.testEquivalentPrefix("1.2.3.4/1", 32) t.testEquivalentPrefix("1.2.3.4/15", 32) t.testEquivalentPrefix("1.2.3.4/16", 32) t.testEquivalentPrefix("1.2.3.4/32", 32) t.testEquivalentPrefix("1:2::/1", 128) t.testEquivalentPrefix("1:2::/128", 128) t.testReverse("255.127.128.255", false, false) t.testReverse("255.127.128.255/16", false, false) t.testReverse("1.2.3.4", false, false) t.testReverse("1.1.2.2", false, false) t.testReverse("1.1.1.1", false, false) t.testReverse("0.0.0.0", true, true) t.testReverse("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true, true) t.testReverse("ffff:ffff:1:ffff:ffff:ffff:ffff:ffff", false, false) t.testReverse("ffff:ffff:8181:ffff:ffff:ffff:ffff:ffff", false, true) t.testReverse("ffff:ffff:c3c3:ffff:ffff:ffff:ffff:ffff", false, true) t.testReverse("ffff:4242:c3c3:2424:ffff:ffff:ffff:ffff", false, true) t.testReverse("ffff:ffff:8000:ffff:ffff:0001:ffff:ffff", true, false) t.testReverse("ffff:ffff:1:ffff:ffff:ffff:ffff:ffff/64", false, false) t.testReverse("1:2:3:4:5:6:7:8", false, false) t.testReverse("1:1:2:2:3:3:4:4", false, false) t.testReverse("1:1:1:1:1:1:1:1", false, false) t.testReverse("::", true, true) t.testPrefixes("255.127.128.255", 16, -5, "255.127.128.255", "255.127.128.255/32", "255.127.128.255/27", "255.127.128.255/16", "255.127.128.255/16") t.testPrefixes("255.127.128.255/32", 16, -5, "255.127.128.255", "255.127.128.0/24", "255.127.128.224/27", //xxx need to specify the non prefix subnet xxxx (224-224) range "255.127.0.0/16", "255.127.0.0/16") t.testPrefixes("255.127.0.0/16", 18, 17, "255.127.0.0/24", "255.0.0.0/8", "255.127.0.0", "255.127.0.0/18", "255.127.0.0/16") t.testPrefixes("255.127.0.0/16", 18, 16, "255.127.0.0/24", "255.0.0.0/8", "255.127.0.0/32", "255.127.0.0/18", "255.127.0.0/16") t.testPrefixes("254.0.0.0/7", 18, 17, "254.0.0.0/8", "0.0.0.0/0", "254.0.0.0/24", "254.0.0.0/18", "254.0.0.0/7") t.testPrefixes("254.255.127.128/7", 18, 17, "254.255.127.128/8", "0.255.127.128/0", "254.0.0.128/24", "254.0.63.128/18", "254.255.127.128/7") t.testPrefixes("254.255.127.128/23", 18, 17, "254.255.126.128/24", "254.255.1.128/16", "254.255.126.0/32", "254.255.65.128/18", "254.255.65.128/18") t.testPrefixes("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 16, -5, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/123", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/16", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/16") t.testPrefixes("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128", 16, -5, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:0/112", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffe0/123", "ffff::/16", "ffff::/16") t.testPrefixes("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 15, 1, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/15", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/15") t.testPrefixes("ffff:ffff:1:ffff:ffff:ffff:1:ffff/64", 16, -5, "ffff:ffff:1:ffff:0:ffff:1:ffff/80", "ffff:ffff:1::ffff:ffff:1:ffff/48", "ffff:ffff:1:ffe0:ffff:ffff:1:ffff/59", "ffff::ffff:ffff:1:ffff/16", "ffff::ffff:ffff:1:ffff/16") t.testPrefixes("ffff:ffff:1:ffff::/63", 16, -5, "ffff:ffff:1:fffe::/64", "ffff:ffff:1:1::/48", "ffff:ffff:1:ffc1::/58", "ffff:0:0:1::/16", "ffff:0:0:1::/16") t.testPrefixes("ffff:ffff:1:ffff::/63", 17, -64, "ffff:ffff:1:fffe::/64", "ffff:ffff:1:1::/48", "0:0:0:1::/0", "ffff:8000:0:1::/16", "ffff:8000:0:1::/16") t.testPrefixes("ffff:ffff:1:ffff::/63", 15, -63, "ffff:ffff:1:fffe::/64", "ffff:ffff:1:1::/48", "0:0:0:1::/0", "fffe:0:0:1::/15", "fffe:0:0:1::/15") t.testPrefixes("ffff:ffff:1:ffff::/63", 65, 1, "ffff:ffff:1:fffe::/64", "ffff:ffff:1:1::/48", "ffff:ffff:1:fffe::/64", "ffff:ffff:1:fffe::/65", "ffff:ffff:1:ffff::/63") t.testPrefixes("ffff:ffff:1:ffff:ffff:ffff:ffff:ffff/128", 127, 1, "ffff:ffff:1:ffff:ffff:ffff:ffff:ffff", "ffff:ffff:1:ffff:ffff:ffff:ffff::/112", "ffff:ffff:1:ffff:ffff:ffff:ffff:ffff", "ffff:ffff:1:ffff:ffff:ffff:ffff:fffe/127", "ffff:ffff:1:ffff:ffff:ffff:ffff:fffe/127") var bcneg1, bc0, bc1, bc8, bc16, bc32 ipaddr.BitCount bcneg1, bc0, bc1, bc8, bc16, bc32 = -1, 0, 1, 8, 16, 32 t.testBitwiseOr("1.2.0.0", nil, "0.0.3.4", "1.2.3.4") t.testBitwiseOr("1.2.0.0", nil, "0.0.0.0", "1.2.0.0") t.testBitwiseOr("1.2.0.0", nil, "255.255.255.255", "255.255.255.255") t.testBitwiseOr("1.0.0.0/8", &bc16, "0.2.3.0", "1.2.3.0/24") //note the prefix length is dropped to become "1.2.3.*", but equality still holds t.testBitwiseOr("1.2.0.0/16", &bc8, "0.0.3.0", "1.2.3.0/24") //note the prefix length is dropped to become "1.2.3.*", but equality still holds t.testBitwiseOr("0.0.0.0", nil, "1.2.3.4", "1.2.3.4") t.testBitwiseOr("0.0.0.0", &bc1, "1.2.3.4", "1.2.3.4") t.testBitwiseOr("0.0.0.0", &bcneg1, "1.2.3.4", "1.2.3.4") t.testBitwiseOr("0.0.0.0", &bc0, "1.2.3.4", "1.2.3.4") t.testBitwiseOr("0.0.0.0/0", &bcneg1, "1.2.3.4", "") t.testBitwiseOr("0.0.0.0/16", nil, "0.0.255.255", "0.0.255.255") t.testPrefixBitwiseOr("0.0.0.0/16", 18, "0.0.98.8", "", "") t.testPrefixBitwiseOr("0.0.0.0/16", 18, "0.0.194.8", "0.0.192.0/18", "") //no zeroing going on - first one applies mask up to the new prefix and then applies the prefix, second one masks everything and then keeps the prefix as well (which in the case of all prefixes subnets wipes out any masking done in host) t.testPrefixBitwiseOr("0.0.0.1/16", 18, "0.0.194.8", "0.0.192.1/18", "0.0.194.9/16") t.testPrefixBitwiseOr("1.2.0.0/16", 24, "0.0.3.248", "", "") t.testPrefixBitwiseOr("1.2.0.0/16", 23, "0.0.3.0", "", "") t.testPrefixBitwiseOr("1.2.0.0", 24, "0.0.3.248", "1.2.3.0", "1.2.3.248") t.testPrefixBitwiseOr("1.2.0.0", 24, "0.0.3.0", "1.2.3.0", "1.2.3.0") t.testPrefixBitwiseOr("1.2.0.0", 23, "0.0.3.0", "1.2.2.0", "1.2.3.0") t.testPrefixBitwiseOr("::/32", 36, "0:0:6004:8::", "", "") t.testPrefixBitwiseOr("::/32", 36, "0:0:f000:8::", "0:0:f000::/36", "") t.testPrefixBitwiseOr("1:2::/32", 48, "0:0:3:effe::", "", "") t.testPrefixBitwiseOr("1:2::/32", 47, "0:0:3::", "", "") t.testPrefixBitwiseOr("1:2::/46", 48, "0:0:3:248::", "1:2:3::/48", "") t.testPrefixBitwiseOr("1:2::/48", 48, "0:0:3:248::", "1:2:3::/48", "") t.testPrefixBitwiseOr("1:2::/48", 47, "0:0:3::", "1:2:2::/48", "1:2:3::/48") t.testPrefixBitwiseOr("1:2::", 48, "0:0:3:248::", "1:2:3::", "1:2:3:248::") t.testPrefixBitwiseOr("1:2::", 47, "0:0:3::", "1:2:2::", "1:2:3::") t.testBitwiseOr("1:2::", nil, "0:0:3:4::", "1:2:3:4::") t.testBitwiseOr("1:2::", nil, "::", "1:2::") t.testBitwiseOr("1:2::", nil, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") t.testBitwiseOr("1:2::", nil, "fffe:fffd:ffff:ffff:ffff:ffff:ff0f:ffff", "ffff:ffff:ffff:ffff:ffff:ffff:ff0f:ffff") t.testBitwiseOr("1::/16", &bc32, "0:2:3::", "1:2:3::/48") //note the prefix length is dropped to become "1.2.3.*", but equality still holds t.testBitwiseOr("1:2::/32", &bc16, "0:0:3::", "1:2:3::/48") //note the prefix length is dropped to become "1.2.3.*", but equality still holds t.testBitwiseOr("::", nil, "::1:2:3:4", "::1:2:3:4") t.testBitwiseOr("::", &bc1, "::1:2:3:4", "::1:2:3:4") t.testBitwiseOr("::", &bcneg1, "::1:2:3:4", "::1:2:3:4") t.testBitwiseOr("::", &bc0, "::1:2:3:4", "::1:2:3:4") t.testBitwiseOr("::/0", &bcneg1, "::1:2:3:4", "") t.testBitwiseOr("::/32", nil, "::ffff:ffff:ffff:ffff:ffff:ffff", "::ffff:ffff:ffff:ffff:ffff:ffff") t.testDelimitedCount("1,2.3.4,5.6", 4) //this will iterate through 1.3.4.6 1.3.5.6 2.3.4.6 2.3.5.6 t.testDelimitedCount("1,2.3,6.4,5.6,8", 16) t.testDelimitedCount("1:2:3:6:4:5:6:8", 1) t.testDelimitedCount("1:2,3,4:3:6:4:5,6fff,7,8,99:6:8", 15) t.testMatches(false, "1::", "2::") t.testMatches(false, "1::", "1.2.3.4") t.testMatches(true, "1::", "1:0::") t.testMatches(true, "f::", "F:0::") t.testMatches(false, "1::", "1:0:1::") t.testMatches(false, "f::1.2.3.4", "F:0::1.1.1.1") t.testMatches(true, "f::1.2.3.4", "F:0::1.2.3.4") t.testMatches(true, "1.2.3.4", "1.2.3.4") t.testMatches(true, "1.2.3.4", "001.2.3.04") t.testMatches(true, "1.2.3.4", "::ffff:1.2.3.4") //ipv4 mapped t.testMatches(true, "1.2.3.4/32", "1.2.3.4") //inet_aton style t.testMatchesInetAton(true, "1.2.3", "1.2.0.3", true) t.testMatchesInetAton(true, "1.2.3.4", "0x1.0x2.0x3.0x4", true) t.testMatchesInetAton(true, "1.2.3.4", "01.02.03.04", true) t.testMatchesInetAton(true, "0.0.0.4", "00.0x0.0x00.04", true) t.testMatchesInetAton(true, "11.11.11.11", "11.0xb.013.0xB", true) t.testMatchesInetAton(true, "11.11.0.11", "11.0xb.0xB", true) t.testMatchesInetAton(true, "11.11.0.11", "11.0x00000000000000000b.0000000000000000000013", true) t.testMatchesInetAton(true, "11.11.0.11/16", "11.720907/16", true) t.testMatchesInetAton(true, "11.0.0.11/16", "184549387/16", true) t.testMatchesInetAton(true, "11.0.0.11/16", "0xb00000b/16", true) t.testMatchesInetAton(true, "11.0.0.11/16", "01300000013/16", true) t.testMatches(true, "/16", "/16") //no prefix to speak of, since not known to be ipv4 or ipv6 t.testMatches(false, "/16", "/15") t.testMatches(true, "/15", "/15") t.testMatches(true, "/0", "/0") t.testMatches(false, "/1", "/0") t.testMatches(false, "/0", "/1") t.testMatches(true, "/128", "/128") t.testMatches(false, "/127", "/128") t.testMatches(false, "/128", "/127") t.testMatches(true, "11::1.2.3.4/112", "11::102:304/112") t.testMatches(true, "11:0:0:0:0:0:1.2.3.4/112", "11:0:0:0:0:0:102:304/112") t.testMatches(true, "1:2::/32", "1:2::/ffff:ffff::") t.testMatches(true, "1:2::/1", "1:2::/8000::") t.testMatches(true, "1:2::/1", "1:2::/ffff:ffff::1") t.testMatches(true, "1:2::/31", "1:2::/ffff:fffe::") t.testMatches(true, "0.2.3.0", "1.2.3.4/0.255.255.0") t.testMatches(true, "1.2.128.0/16", "1.2.128.4/255.255.254.1") t.testMatches(true, "1.2.2.0/15", "1.2.3.4/255.254.2.3") t.testMatches(true, "1.2.0.4/17", "1.2.3.4/255.255.128.5") t.testMatches(false, "1.2.0.0/16", "1.2.3.4/255.255.0.0") t.testMatches(false, "1.2.0.0/15", "1.2.3.4/255.254.0.0") t.testMatches(false, "1.2.0.0/17", "1.2.3.4/255.255.128.0") t.testMatches(true, "1.2.3.4/16", "1.2.3.4/255.255.0.0") t.testMatches(true, "1.2.3.4/15", "1.2.3.4/255.254.0.0") t.testMatches(true, "1.2.3.4/17", "1.2.3.4/255.255.128.0") t.testMatches(false, "1.1.3.4/15", "1.2.3.4/255.254.0.0") t.testMatches(false, "1.1.3.4/17", "1.2.3.4/255.255.128.0") t.testMatches(false, "0.2.3.4", "1.2.3.4/0.255.255.0") t.testMatches(false, "1.2.3.0", "1.2.3.4/0.255.255.0") t.testMatches(false, "1.2.3.4", "1.2.3.4/0.255.255.0") t.testMatches(false, "1.1.3.4/16", "1.2.3.4/255.255.0.0") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4/1:2:3:4:5:6:1.2.3.4", "1:2:3:4:5:6:1.2.3.4") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4/1:2:3:4:5:6:0.0.0.0", "1:2:3:4:5:6::") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4/1:2:3:4:5:0:0.0.0.0", "1:2:3:4:5::") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4%12", "1:2:3:4:5:6:102:304%12") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4%a", "1:2:3:4:5:6:102:304%a") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4%", "1:2:3:4:5:6:102:304%") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4%%", "1:2:3:4:5:6:102:304%%") //the % reappearing as the zone itself is ok t.testMatches(false, "1:2:3:4:5:6:1.2.3.4%a", "1:2:3:4:5:6:102:304") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4%", "1:2:3:4:5:6:102:304%") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4%-a-", "1:2:3:4:5:6:102:304%-a-") //we don't validate the zone itself, so the % reappearing as the zone itself is ok t.testMatches(true, "1::0.0.0.0%-1", "1::%-1") t.testMatches(false, "1::0.0.0.0", "1::%-1") //zones do not match t.testMatches(false, "1::0.0.0.0%-1", "1::") //zones do not match t.testMatches(true, "1:2:3:4::0.0.0.0/64", "1:2:3:4::/64") //more stuff with prefix in mixed part 1:2:3:4:5:6:1.2.3.4/128 t.testMatches(true, "1:2:3:4:5:6:0.0.0.0/96", "1:2:3:4:5:6::/96") t.testMatches(true, "1:2:3:4:5:6:128.0.0.0/97", "1:2:3:4:5:6:8000::/97") t.testMatches(true, "1:2:3:4:5:6:1.2.0.0/112", "1:2:3:4:5:6:102::/112") t.testMatches(true, "1:2:3:4:5:6:1.2.224.0/115", "1:2:3:4:5:6:102:e000/115") t.testMatches(true, "1:2:3:4:5:6:1.2.3.4/128", "1:2:3:4:5:6:102:304/128") t.testMatches(true, "0b1.0b01.0b101.0b11111111", "1.1.5.255") t.testMatches(true, "0b1.0b01.0b101.0b11111111/16", "1.1.5.255/16") t.testMatches(true, "0b1.1.0b101.0b11111111/16", "1.1.5.255/16") t.testMatches(true, "::0b1111111111111111:1", "::ffff:1") t.testMatches(true, "0b1111111111111111:1::/64", "ffff:1::/64") t.testMatches(true, "::0b1111111111111111:1:0", "::0b1111111111111111:0b0.0b1.0b0.0b0") t.ipv6test(t.allowsRange(), "aa:-1:cc::d:ee:f") //same as "aa:0-1:cc::d:ee:f" t.ipv6test(t.allowsRange(), "aa:-dd:cc::d:ee:f") //same as "aa:0-dd:cc::d:ee:f" t.ipv6test(t.allowsRange(), "aa:1-:cc:d::ee:f") //same as "aa:1-ff:cc:d::ee:f" t.ipv6test(t.allowsRange(), "-1:aa:cc:d::ee:f") //same as "aa:0-1:cc:d::ee:f" t.ipv6test(t.allowsRange(), "1-:aa:cc:d::ee:f") //same as "aa:0-1:cc:d::ee:f" t.ipv6test(t.allowsRange(), "aa:cc:d::ee:f:1-") t.ipv6test(t.allowsRange(), "aa:0-1:cc:d::ee:f") t.ipv6test(t.allowsRange(), "aa:1-ff:cc:d::ee:f") t.ipv4test(t.allowsRange(), "1.-1.33.4") t.ipv4test(t.allowsRange(), "-1.22.33.4") t.ipv4test(t.allowsRange(), "22.1-.33.4") t.ipv4test(t.allowsRange(), "22.33.4.1-") t.ipv4test(t.allowsRange(), "1-.22.33.4") t.ipv4test(t.allowsRange(), "22.0-1.33.4") t.ipv4test(t.allowsRange(), "22.1-22.33.4") t.ipv4test(false, "1.+1.33.4") t.ipv4test(false, "+1.22.33.4") t.ipv4test(false, "22.1+.33.4") t.ipv4test(false, "22.33.4.1+") t.ipv4test(false, "1+.22.33.4") t.ipv4test(false, "22.0+1.33.4") t.ipv4test(false, "22.1+22.33.4") t.ipv6test(false, "::0b11111111111111111:1") // one digit too many t.ipv6test(false, "::0b111111111111111:1") // one digit too few t.ipv4test(t.allowsRange(), "0b1.0b01.0b101.1-0b11111111") t.ipv4test(t.allowsRange(), "0b1.0b01.0b101.0b11110000-0b11111111") t.ipv6test(t.allowsRange(), "::0b0000111100001111-0b1111000011110000:3") t.ipv6test(t.allowsRange(), "0b0000111100001111-0b1111000011110000::3") t.ipv6test(t.allowsRange(), "1::0b0000111100001111-0b1111000011110000:3") t.ipv6test(t.allowsRange(), "1::0b0000111100001111-0b1111000011110000") t.ipv6test(t.allowsRange(), "1:0b0000111100001111-0b1111000011110000:3::") t.ipv4test(false, "0b1.0b01.0b101.0b111111111") // one digit too many t.ipv4test(false, "0b.0b01.0b101.0b111111111") // one digit too few t.ipv4test(false, "0b1.0b01.0b101.0b11121111") // not binary t.ipv4test(false, "0b1.0b2.0b101.0b1111111") // not binary t.ipv4test(false, "0b1.b1.0b101.0b1111111") // not binary t.ipv4test(true, "1.2.3.4/255.1.0.0") t.ipv4test(false, "1.2.3.4/1::1") //mask mismatch t.ipv6test(true, "1:2::/1:2::") t.ipv6test(false, "1:2::/1:2::/16") t.ipv6test(false, "1:2::/1.2.3.4") //mask mismatch allowsIPv4PrefixBeyondAddressSize := t.createAddress("1.2.3.4").GetValidationOptions().GetIPv4Params().AllowsPrefixesBeyondAddressSize() allowsIPv6PrefixBeyondAddressSize := t.createAddress("1.2.3.4").GetValidationOptions().GetIPv6Params().AllowsPrefixesBeyondAddressSize() //test some valid and invalid prefixes t.ipv4test(true, "1.2.3.4/1") t.ipv4test(false, "1.2.3.4/ 1") t.ipv4test(false, "1.2.3.4/-1") t.ipv4test(false, "1.2.3.4/+1") t.ipv4test(false, "1.2.3.4/") t.ipv4test(true, "1.2.3.4/1.2.3.4") t.ipv4test(false, "1.2.3.4/x") t.ipv4test(allowsIPv4PrefixBeyondAddressSize, "1.2.3.4/33") //we are not allowing extra-large prefixes t.ipv6test(true, "1::1/1") t.ipv6test(false, "1::1/-1") t.ipv6test(false, "1::1/") t.ipv6test(false, "1::1/x") t.ipv6test(allowsIPv6PrefixBeyondAddressSize, "1::1/129") //we are not allowing extra-large prefixes t.ipv6test(true, "1::1/1::1") t.ipv4zerotest(t.isLenient(), "") //this needs special validation options to be valid t.ipv4test(true, "1.2.3.4") t.ipv4test(false, "[1.2.3.4]") //HostName accepts square brackets, not addresses t.ipv4test(false, "a") t.ipv4test(t.isLenient(), "1.2.3") t.ipv4test(false, "a.2.3.4") t.ipv4test(false, "1.a.3.4") t.ipv4test(false, "1.2.a.4") t.ipv4test(false, "1.2.3.a") t.ipv4test(false, ".2.3.4") t.ipv4test(false, "1..3.4") t.ipv4test(false, "1.2..4") t.ipv4test(false, "1.2.3.") t.ipv4test(false, "256.2.3.4") t.ipv4test(false, "1.256.3.4") t.ipv4test(false, "1.2.256.4") t.ipv4test(false, "1.2.3.256") t.ipv4test(false, "f.f.f.f") t.ipv4zerotest(true, "0.0.0.0") t.ipv4zerotest(true, "00.0.0.0") t.ipv4zerotest(true, "0.00.0.0") t.ipv4zerotest(true, "0.0.00.0") t.ipv4zerotest(true, "0.0.0.00") t.ipv4zerotest(true, "000.0.0.0") t.ipv4zerotest(true, "0.000.0.0") t.ipv4zerotest(true, "0.0.000.0") t.ipv4zerotest(true, "0.0.0.000") t.ipv4zerotest(true, "000.000.000.000") t.ipv4zerotest(t.isLenient(), "0000.0.0.0") t.ipv4zerotest(t.isLenient(), "0.0000.0.0") t.ipv4zerotest(t.isLenient(), "0.0.0000.0") t.ipv4zerotest(t.isLenient(), "0.0.0.0000") t.ipv4test(true, "3.3.3.3") t.ipv4test(true, "33.3.3.3") t.ipv4test(true, "3.33.3.3") t.ipv4test(true, "3.3.33.3") t.ipv4test(true, "3.3.3.33") t.ipv4test(true, "233.3.3.3") t.ipv4test(true, "3.233.3.3") t.ipv4test(true, "3.3.233.3") t.ipv4test(true, "3.3.3.233") t.ipv4test(true, "200.200.200.200") t.ipv4test(t.isLenient(), "0333.0.0.0") t.ipv4test(t.isLenient(), "0.0333.0.0") t.ipv4test(t.isLenient(), "0.0.0333.0") t.ipv4test(t.isLenient(), "0.0.0.0333") t.ipv4test(false, "1.2.3:4") t.ipv4test(false, "1.2:3.4") t.ipv6test(false, "1.2.3:4") t.ipv6test(false, "1.2:3.4") t.ipv4test(false, "1.2.3.4:1.2.3.4") t.ipv4test(false, "1.2.3.4.1:2.3.4") t.ipv4test(false, "1.2.3.4.1.2:3.4") t.ipv4test(false, "1.2.3.4.1.2.3:4") t.ipv6test(false, "1.2.3.4:1.2.3.4") t.ipv6test(false, "1.2.3.4.1:2.3.4") t.ipv6test(false, "1.2.3.4.1.2:3.4") t.ipv6test(false, "1.2.3.4.1.2.3:4") t.ipv4test(false, "1:2.3.4") t.ipv4test(false, "1:2:3.4") t.ipv4test(false, "1:2:3:4") t.ipv6test(false, "1:2.3.4") t.ipv6test(false, "1:2:3.4") t.ipv6test(false, "1:2:3:4") t.ipv6test(false, "1.2.3.4.1.2.3.4") t.ipv6test(false, "1:2.3.4.1.2.3.4") t.ipv6test(false, "1:2:3.4.1.2.3.4") t.ipv6test(false, "1:2:3:4.1.2.3.4") t.ipv6test(false, "1:2:3:4:1.2.3.4") t.ipv6test(false, "1:2:3:4:1:2.3.4") t.ipv6test(true, "1:2:3:4:1:2:1.2.3.4") t.ipv6test(t.isLenient(), "1:2:3:4:1:2:3.4") // if inet_aton allowed, this is equivalent to 1:2:3:4:1:2:0.0.3.4 or 1:2:3:4:1:2:0:304 t.ipv6test(true, "1:2:3:4:1:2:3:4") t.ipv6zerotest(true, "0:0:0:0:0:0:0:0") t.ipv6zerotest(true, "00:0:0:0:0:0:0:0") t.ipv6zerotest(true, "0:00:0:0:0:0:0:0") t.ipv6zerotest(true, "0:0:00:0:0:0:0:0") t.ipv6zerotest(true, "0:0:0:00:0:0:0:0") t.ipv6zerotest(true, "0:0:0:0:00:0:0:0") t.ipv6zerotest(true, "0:0:0:0:0:00:0:0") t.ipv6zerotest(true, "0:0:0:0:0:0:00:0") t.ipv6zerotest(true, "0:0:0:0:0:0:0:00") t.ipv6zerotest(true, "0:0:0:0:0:0:0:0") t.ipv6zerotest(true, "000:0:0:0:0:0:0:0") t.ipv6zerotest(true, "0:000:0:0:0:0:0:0") t.ipv6zerotest(true, "0:0:000:0:0:0:0:0") t.ipv6zerotest(true, "0:0:0:000:0:0:0:0") t.ipv6zerotest(true, "0:0:0:0:000:0:0:0") t.ipv6zerotest(true, "0:0:0:0:0:000:0:0") t.ipv6zerotest(true, "0:0:0:0:0:0:000:0") t.ipv6zerotest(true, "0:0:0:0:0:0:0:000") t.ipv6zerotest(true, "0000:0:0:0:0:0:0:0") t.ipv6zerotest(true, "0:0000:0:0:0:0:0:0") t.ipv6zerotest(true, "0:0:0000:0:0:0:0:0") t.ipv6zerotest(true, "0:0:0:0000:0:0:0:0") t.ipv6zerotest(true, "0:0:0:0:0000:0:0:0") t.ipv6zerotest(true, "0:0:0:0:0:0000:0:0") t.ipv6zerotest(true, "0:0:0:0:0:0:0000:0") t.ipv6zerotest(true, "0:0:0:0:0:0:0:0000") t.ipv6zerotest(t.isLenient(), "00000:0:0:0:0:0:0:0") t.ipv6zerotest(t.isLenient(), "0:00000:0:0:0:0:0:0") t.ipv6zerotest(t.isLenient(), "0:0:00000:0:0:0:0:0") t.ipv6zerotest(t.isLenient(), "0:0:0:00000:0:0:0:0") t.ipv6zerotest(t.isLenient(), "0:0:0:0:00000:0:0:0") t.ipv6zerotest(t.isLenient(), "0:0:0:0:0:00000:0:0") t.ipv6zerotest(t.isLenient(), "0:0:0:0:0:0:00000:0") t.ipv6zerotest(t.isLenient(), "0:0:0:0:0:0:0:00000") t.ipv6zerotest(t.isLenient(), "00000:00000:00000:00000:00000:00000:00000:00000") t.ipv6test(t.isLenient(), "03333:0:0:0:0:0:0:0") t.ipv6test(t.isLenient(), "0:03333:0:0:0:0:0:0") t.ipv6test(t.isLenient(), "0:0:03333:0:0:0:0:0") t.ipv6test(t.isLenient(), "0:0:0:03333:0:0:0:0") t.ipv6test(t.isLenient(), "0:0:0:0:03333:0:0:0") t.ipv6test(t.isLenient(), "0:0:0:0:0:03333:0:0") t.ipv6test(t.isLenient(), "0:0:0:0:0:0:03333:0") t.ipv6test(t.isLenient(), "0:0:0:0:0:0:0:03333") t.ipv6test(t.isLenient(), "03333:03333:03333:03333:03333:03333:03333:03333") t.ipv4test(false, ".0.0.0") t.ipv4test(false, "0..0.0") t.ipv4test(false, "0.0..0") t.ipv4test(false, "0.0.0.") t.ipv4test(false, "/0") t.ipv4test(false, "/1") t.ipv4test(false, "/31") t.ipv4test(false, "/32") t.ipv4test(false, "/33") t.ipv4test(false, "1.2.3.4//16") t.ipv4test(false, "1.2.3.4//") t.ipv4test(false, "1.2.3.4/") t.ipv4test(false, "/1.2.3.4//16") t.ipv4test(false, "/1.2.3.4/16") t.ipv4test(false, "/1.2.3.4") t.ipv4test(false, "1.2.3.4/y") t.ipv4test(true, "1.2.3.4/16") t.ipv6test(false, "1:2::3:4//16") t.ipv6test(false, "1:2::3:4//") t.ipv6test(false, "1:2::3:4/") t.ipv6test(false, "1:2::3:4/y") t.ipv6test(true, "1:2::3:4/16") t.ipv6test(true, "1:2::3:1.2.3.4/16") t.ipv6test(false, "1:2::3:1.2.3.4//16") t.ipv6test(false, "1:2::3:1.2.3.4//") t.ipv6test(false, "1:2::3:1.2.3.4/y") t.ipv4test(false, "127.0.0.1/x") t.ipv4test(false, "127.0.0.1/127.0.0.1/x") t.ipv4_inet_aton_test(true, "0.0.0.255") t.ipv4_inet_aton_test(false, "0.0.0.256") t.ipv4_inet_aton_test(true, "0.0.65535") t.ipv4_inet_aton_test(false, "0.0.65536") t.ipv4_inet_aton_test(true, "0.16777215") t.ipv4_inet_aton_test(false, "0.16777216") t.ipv4_inet_aton_test(true, "4294967295") t.ipv4_inet_aton_test(false, "4294967296") t.ipv4_inet_aton_test(true, "0.0.0.0xff") t.ipv4_inet_aton_test(false, "0.0.0.0x100") t.ipv4_inet_aton_test(true, "0.0.0xffff") t.ipv4_inet_aton_test(false, "0.0.0x10000") t.ipv4_inet_aton_test(true, "0.0xffffff") t.ipv4_inet_aton_test(false, "0.0x1000000") t.ipv4_inet_aton_test(true, "0xffffffff") t.ipv4_inet_aton_test(false, "0x100000000") t.ipv4_inet_aton_test(true, "0.0.0.0377") t.ipv4_inet_aton_test(false, "0.0.0.0400") t.ipv4_inet_aton_test(true, "0.0.017777") t.ipv4_inet_aton_test(false, "0.0.0200000") t.ipv4_inet_aton_test(true, "0.077777777") t.ipv4_inet_aton_test(false, "0.0100000000") t.ipv4_inet_aton_test(true, "03777777777") t.ipv4_inet_aton_test(true, "037777777777") t.ipv4_inet_aton_test(false, "040000000000") t.ipv4_inet_aton_test(false, "1.00x.1.1") t.ipv4_inet_aton_test(false, "00x1.1.1.1") t.ipv4_inet_aton_test(false, "1.00x0.1.1") t.ipv4_inet_aton_test(false, "1.0xx.1.1") t.ipv4_inet_aton_test(false, "1.xx.1.1") t.ipv4_inet_aton_test(false, "1.0x4x.1.1") t.ipv4_inet_aton_test(false, "1.x4.1.1") t.ipv4test(false, "1.00x.1.1") t.ipv4test(false, "1.0xx.1.1") t.ipv4test(false, "1.xx.1.1") t.ipv4test(false, "1.0x4x.1.1") t.ipv4test(false, "1.x4.1.1") t.ipv4test(false, "1.4.1.1%1") //ipv4 zone t.ipv6test(false, "1:00x:3:4:5:6:7:8") t.ipv6test(false, "1:0xx:3:4:5:6:7:8") t.ipv6test(false, "1:xx:3:4:5:6:7:8") t.ipv6test(false, "1:0x4x:3:4:5:6:7:8") t.ipv6test(false, "1:x4:3:4:5:6:7:8") t.ipv4testOnly(false, "1:2:3:4:5:6:7:8") t.ipv4testOnly(false, "::1") // in this test, the validation will fail unless validation options have allowEmpty t.ipv6zerotest(t.isLenient(), "") // empty string //this needs special validation options to be valid t.ipv6test(false, "/0") t.ipv6test(false, "/1") t.ipv6test(false, "/127") t.ipv6test(false, "/128") t.ipv6test(false, "/129") t.ipv6test(true, "::/0") t.ipv6test(false, ":1.2.3.4") //invalid t.ipv6test(true, "::1.2.3.4") t.ipv6test(true, "::1") // loopback, compressed, non-routable t.ipv6zerotest(true, "::") // unspecified, compressed, non-routable t.ipv6test(true, "0:0:0:0:0:0:0:1") // loopback, full t.ipv6zerotest(true, "0:0:0:0:0:0:0:0") // unspecified, full t.ipv6test(true, "2001:DB8:0:0:8:800:200C:417A") // unicast, full t.ipv6test(true, "FF01:0:0:0:0:0:0:101") // multicast, full t.ipv6test(true, "2001:DB8::8:800:200C:417A") // unicast, compressed t.ipv6test(true, "FF01::101") // multicast, compressed t.ipv6test(false, "2001:DB8:0:0:8:800:200C:417A:221") // unicast, full t.ipv6test(false, "FF01::101::2") // multicast, compressed t.ipv6test(true, "fe80::217:f2ff:fe07:ed62") t.ipv6test(false, "[a::b:c:d:1.2.3.4]") // square brackets can enclose ipv6 in host names but not addresses t.ipv6test(false, "[a::b:c:d:1.2.3.4%x]") // square brackets can enclose ipv6 in host names but not addresses t.ipv6test(true, "a::b:c:d:1.2.3.4%x") // t.ipv6test(false, "[2001:0000:1234:0000:0000:C1C0:ABCD:0876]") // square brackets can enclose ipv6 in host names but not addresses t.ipv6test(true, "2001:0000:1234:0000:0000:C1C0:ABCD:0876%x") // square brackets can enclose ipv6 in host names but not addresses t.ipv6test(false, "[2001:0000:1234:0000:0000:C1C0:ABCD:0876%x]") // t.ipv6test(true, "::1%/32") // empty zone t.ipv6test(true, "::1%") // empty zone t.ipv6test(true, "2001:0000:1234:0000:0000:C1C0:ABCD:0876") t.ipv6test(true, "3ffe:0b00:0000:0000:0001:0000:0000:000a") t.ipv6test(true, "FF02:0000:0000:0000:0000:0000:0000:0001") t.ipv6test(true, "0000:0000:0000:0000:0000:0000:0000:0001") t.ipv6zerotest(true, "0000:0000:0000:0000:0000:0000:0000:0000") t.ipv6test(t.isLenient(), "02001:0000:1234:0000:0000:C1C0:ABCD:0876") // extra 0 not allowed! t.ipv6test(t.isLenient(), "2001:0000:1234:0000:00001:C1C0:ABCD:0876") // extra 0 not allowed! t.ipv6test(false, "2001:0000:1234:0000:0000:C1C0:ABCD:0876 0") // junk after valid address t.ipv6test(false, "0 2001:0000:1234:0000:0000:C1C0:ABCD:0876") // junk before valid address t.ipv6test(false, "2001:0000:1234: 0000:0000:C1C0:ABCD:0876") // internal space t.ipv6test(false, "3ffe:0b00:0000:0001:0000:0000:000a") // seven segments t.ipv6test(false, "FF02:0000:0000:0000:0000:0000:0000:0000:0001") // nine segments t.ipv6test(false, "3ffe:b00::1::a") // double "::" t.ipv6test(false, "::1111:2222:3333:4444:5555:6666::") // double "::" t.ipv6test(true, "2::10") t.ipv6test(true, "ff02::1") t.ipv6test(true, "fe80::") t.ipv6test(true, "2002::") t.ipv6test(true, "2001:db8::") t.ipv6test(true, "2001:0db8:1234::") t.ipv6test(true, "::ffff:0:0") t.ipv6test(true, "::1") t.ipv6test(true, "1:2:3:4:5:6:7:8") t.ipv6test(true, "1:2:3:4:5:6::8") t.ipv6test(true, "1:2:3:4:5::8") t.ipv6test(true, "1:2:3:4::8") t.ipv6test(true, "1:2:3::8") t.ipv6test(true, "1:2::8") t.ipv6test(true, "1::8") t.ipv6test(true, "1::2:3:4:5:6:7") t.ipv6test(true, "1::2:3:4:5:6") t.ipv6test(true, "1::2:3:4:5") t.ipv6test(true, "1::2:3:4") t.ipv6test(true, "1::2:3") t.ipv6test(true, "1::8") t.ipv6test(true, "::2:3:4:5:6:7:8") t.ipv6test(true, "::2:3:4:5:6:7") t.ipv6test(true, "::2:3:4:5:6") t.ipv6test(true, "::2:3:4:5") t.ipv6test(true, "::2:3:4") t.ipv6test(true, "::2:3") t.ipv6test(true, "::8") t.ipv6test(true, "1:2:3:4:5:6::") t.ipv6test(true, "1:2:3:4:5::") t.ipv6test(true, "1:2:3:4::") t.ipv6test(true, "1:2:3::") t.ipv6test(true, "1:2::") t.ipv6test(true, "1::") t.ipv6test(true, "1:2:3:4:5::7:8") t.ipv6test(false, "1:2:3::4:5::7:8") // Double "::" t.ipv6test(false, "12345::6:7:8") t.ipv6test(true, "1:2:3:4::7:8") t.ipv6test(true, "1:2:3::7:8") t.ipv6test(true, "1:2::7:8") t.ipv6test(true, "1::7:8") // IPv4 addresses as dotted-quads t.ipv6test(true, "1:2:3:4:5:6:1.2.3.4") t.ipv6zerotest(true, "0:0:0:0:0:0:0.0.0.0") t.ipv6test(true, "1:2:3:4:5::1.2.3.4") t.ipv6zerotest(true, "0:0:0:0:0::0.0.0.0") t.ipv6zerotest(true, "0::0.0.0.0") t.ipv6zerotest(true, "::0.0.0.0") t.ipv6test(false, "1:2:3:4:5:6:.2.3.4") t.ipv6test(false, "1:2:3:4:5:6:1.2.3.") t.ipv6test(false, "1:2:3:4:5:6:1.2..4") t.ipv6test(true, "1:2:3:4:5:6:1.2.3.4") t.ipv6test(true, "1:2:3:4::1.2.3.4") t.ipv6test(true, "1:2:3::1.2.3.4") t.ipv6test(true, "1:2::1.2.3.4") t.ipv6test(true, "1::1.2.3.4") t.ipv6test(true, "1:2:3:4::5:1.2.3.4") t.ipv6test(true, "1:2:3::5:1.2.3.4") t.ipv6test(true, "1:2::5:1.2.3.4") t.ipv6test(true, "1::5:1.2.3.4") t.ipv6test(true, "1::5:11.22.33.44") t.ipv6test(false, "1::5:400.2.3.4") t.ipv6test(false, "1::5:260.2.3.4") t.ipv6test(false, "1::5:256.2.3.4") t.ipv6test(false, "1::5:1.256.3.4") t.ipv6test(false, "1::5:1.2.256.4") t.ipv6test(false, "1::5:1.2.3.256") t.ipv6test(false, "1::5:300.2.3.4") t.ipv6test(false, "1::5:1.300.3.4") t.ipv6test(false, "1::5:1.2.300.4") t.ipv6test(false, "1::5:1.2.3.300") t.ipv6test(false, "1::5:900.2.3.4") t.ipv6test(false, "1::5:1.900.3.4") t.ipv6test(false, "1::5:1.2.900.4") t.ipv6test(false, "1::5:1.2.3.900") t.ipv6test(false, "1::5:300.300.300.300") t.ipv6test(false, "1::5:3000.30.30.30") t.ipv6test(false, "1::400.2.3.4") t.ipv6test(false, "1::260.2.3.4") t.ipv6test(false, "1::256.2.3.4") t.ipv6test(false, "1::1.256.3.4") t.ipv6test(false, "1::1.2.256.4") t.ipv6test(false, "1::1.2.3.256") t.ipv6test(false, "1::300.2.3.4") t.ipv6test(false, "1::1.300.3.4") t.ipv6test(false, "1::1.2.300.4") t.ipv6test(false, "1::1.2.3.300") t.ipv6test(false, "1::900.2.3.4") t.ipv6test(false, "1::1.900.3.4") t.ipv6test(false, "1::1.2.900.4") t.ipv6test(false, "1::1.2.3.900") t.ipv6test(false, "1::300.300.300.300") t.ipv6test(false, "1::3000.30.30.30") t.ipv6test(false, "::400.2.3.4") t.ipv6test(false, "::260.2.3.4") t.ipv6test(false, "::256.2.3.4") t.ipv6test(false, "::1.256.3.4") t.ipv6test(false, "::1.2.256.4") t.ipv6test(false, "::1.2.3.256") t.ipv6test(false, "::300.2.3.4") t.ipv6test(false, "::1.300.3.4") t.ipv6test(false, "::1.2.300.4") t.ipv6test(false, "::1.2.3.300") t.ipv6test(false, "::900.2.3.4") t.ipv6test(false, "::1.900.3.4") t.ipv6test(false, "::1.2.900.4") t.ipv6test(false, "::1.2.3.900") t.ipv6test(false, "::300.300.300.300") t.ipv6test(false, "::3000.30.30.30") t.ipv6test(true, "fe80::217:f2ff:254.7.237.98") t.ipv6test(true, "::ffff:192.168.1.26") t.ipv6test(false, "2001:1:1:1:1:1:255Z255X255Y255") // garbage instead of "." in IPv4 t.ipv6test(false, "::ffff:192x168.1.26") // ditto t.ipv6test(true, "::ffff:192.168.1.1") t.ipv6test(true, "0:0:0:0:0:0:13.1.68.3") // IPv4-compatible IPv6 address, full, deprecated t.ipv6test(true, "0:0:0:0:0:FFFF:129.144.52.38") // IPv4-mapped IPv6 address, full t.ipv6test(true, "::13.1.68.3") // IPv4-compatible IPv6 address, compressed, deprecated t.ipv6test(true, "::FFFF:129.144.52.38") // IPv4-mapped IPv6 address, compressed t.ipv6test(true, "fe80:0:0:0:204:61ff:254.157.241.86") t.ipv6test(true, "fe80::204:61ff:254.157.241.86") t.ipv6test(true, "::ffff:12.34.56.78") t.ipv6test(t.isLenient(), "::ffff:2.3.4") t.ipv6test(false, "::ffff:257.1.2.3") t.ipv6testOnly(false, "1.2.3.4") //stuff that might be mistaken for mixed if we parse incorrectly t.ipv6test(false, "a:b:c:d:e:f:a:b:c:d:e:f:1.2.3.4") t.ipv6test(false, "a:b:c:d:e:f:a:b:c:d:e:f:a:b.") t.ipv6test(false, "a:b:c:d:e:f:1.a:b:c:d:e:f:a") t.ipv6test(false, "a:b:c:d:e:f:1.a:b:c:d:e:f:a:b") t.ipv6test(false, "a:b:c:d:e:f:.a:b:c:d:e:f:a:b") t.ipv6test(false, "::a:b:c:d:e:f:1.2.3.4") t.ipv6test(false, "::a:b:c:d:e:f:a:b.") t.ipv6test(false, "::1.a:b:c:d:e:f:a") t.ipv6test(false, "::1.a:b:c:d:e:f:a:b") t.ipv6test(false, "::.a:b:c:d:e:f:a:b") t.ipv6test(false, "1::a:b:c:d:e:f:1.2.3.4") t.ipv6test(false, "1::a:b:c:d:e:f:a:b.") t.ipv6test(false, "1::1.a:b:c:d:e:f:a") t.ipv6test(false, "1::1.a:b:c:d:e:f:a:b") t.ipv6test(false, "1::.a:b:c:d:e:f:a:b") t.ipv6test(true, "1:2:3:4:5:6:1.2.3.4/1:2:3:4:5:6:1.2.3.4") // Testing IPv4 addresses represented as dotted-quads // Leading zero's in IPv4 addresses not allowed: some systems treat the leading "0" in ".086" as the start of an octal number // Update: The BNF in RFC 3986 explicitly defines the dec-octet (for IPv4 addresses) not to have a leading zero //t.ipv6test(false,"fe80:0000:0000:0000:0204:61ff:254.157.241.086"); t.ipv6test(!t.isLenient(), "fe80:0000:0000:0000:0204:61ff:254.157.241.086") //note the 086 is treated as octal when lenient! So the lenient in this case fails. t.ipv6test(true, "::ffff:192.0.2.128") // this is always OK, since there's a single digit t.ipv6test(false, "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4") //t.ipv6test(false,"1111:2222:3333:4444:5555:6666:00.00.00.00"); t.ipv6test(true, "1111:2222:3333:4444:5555:6666:00.00.00.00") //t.ipv6test(false,"1111:2222:3333:4444:5555:6666:000.000.000.000"); t.ipv6test(true, "1111:2222:3333:4444:5555:6666:000.000.000.000") t.ipv6test(false, "1111:2222:3333:4444:5555:6666:256.256.256.256") // Not testing address with subnet mask // t.ipv6test(true,"2001:0DB8:0000:CD30:0000:0000:0000:0000/60");// full, with prefix // t.ipv6test(true,"2001:0DB8::CD30:0:0:0:0/60");// compressed, with prefix // t.ipv6test(true,"2001:0DB8:0:CD30::/60");// compressed, with prefix //2 // t.ipv6test(true,"::/128");// compressed, unspecified address type, non-routable // t.ipv6test(true,"::1/128");// compressed, loopback address type, non-routable // t.ipv6test(true,"FF00::/8");// compressed, multicast address type // t.ipv6test(true,"FE80::/10");// compressed, link-local unicast, non-routable // t.ipv6test(true,"FEC0::/10");// compressed, site-local unicast, deprecated // t.ipv6test(false,"124.15.6.89/60");// standard IPv4, prefix not allowed t.ipv6test(true, "fe80:0000:0000:0000:0204:61ff:fe9d:f156") t.ipv6test(true, "fe80:0:0:0:204:61ff:fe9d:f156") t.ipv6test(true, "fe80::204:61ff:fe9d:f156") t.ipv6test(true, "::1") t.ipv6test(true, "fe80::") t.ipv6test(true, "fe80::1") t.ipv6test(false, ":") t.ipv6test(true, "::ffff:c000:280") // Aeron supplied these test cases t.ipv6test(false, "1111:2222:3333:4444::5555:") t.ipv6test(false, "1111:2222:3333::5555:") t.ipv6test(false, "1111:2222::5555:") t.ipv6test(false, "1111::5555:") t.ipv6test(false, "::5555:") t.ipv6test(false, ":::") t.ipv6test(false, "1111:") t.ipv6test(false, ":") t.ipv6test(false, ":1111:2222:3333:4444::5555") t.ipv6test(false, ":1111:2222:3333::5555") t.ipv6test(false, ":1111:2222::5555") t.ipv6test(false, ":1111::5555") t.ipv6test(false, ":::5555") t.ipv6test(false, ":::") // Additional test cases // from http://rt.cpan.org/Public/Bug/Display.html?id=50693 t.ipv6test(true, "2001:0db8:85a3:0000:0000:8a2e:0370:7334") t.ipv6test(true, "2001:db8:85a3:0:0:8a2e:370:7334") t.ipv6test(true, "2001:db8:85a3::8a2e:370:7334") t.ipv6test(true, "2001:0db8:0000:0000:0000:0000:1428:57ab") t.ipv6test(true, "2001:0db8:0000:0000:0000::1428:57ab") t.ipv6test(true, "2001:0db8:0:0:0:0:1428:57ab") t.ipv6test(true, "2001:0db8:0:0::1428:57ab") t.ipv6test(true, "2001:0db8::1428:57ab") t.ipv6test(true, "2001:db8::1428:57ab") t.ipv6test(true, "0000:0000:0000:0000:0000:0000:0000:0001") t.ipv6test(true, "::1") t.ipv6test(true, "::ffff:0c22:384e") t.ipv6test(true, "2001:0db8:1234:0000:0000:0000:0000:0000") t.ipv6test(true, "2001:0db8:1234:ffff:ffff:ffff:ffff:ffff") t.ipv6test(true, "2001:db8:a::123") t.ipv6test(true, "fe80::") t.ipv6test2(false, "123", false, t.isLenient()) //this is passing the ipv4 side as inet_aton t.ipv6test(false, "ldkfj") t.ipv6test(false, "2001::FFD3::57ab") t.ipv6test(false, "2001:db8:85a3::8a2e:37023:7334") t.ipv6test(false, "2001:db8:85a3::8a2e:370k:7334") t.ipv6test(false, "1:2:3:4:5:6:7:8:9") t.ipv6test(false, "1::2::3") t.ipv6test(false, "1:::3:4:5") t.ipv6test(false, "1:2:3::4:5:6:7:8:9") t.ipv6test(true, "1111:2222:3333:4444:5555:6666:7777:8888") t.ipv6test(true, "1111:2222:3333:4444:5555:6666:7777::") t.ipv6test(true, "1111:2222:3333:4444:5555:6666::") t.ipv6test(true, "1111:2222:3333:4444:5555::") t.ipv6test(true, "1111:2222:3333:4444::") t.ipv6test(true, "1111:2222:3333::") t.ipv6test(true, "1111:2222::") t.ipv6test(true, "1111::") t.ipv6test(true, "1111:2222:3333:4444:5555:6666::8888") t.ipv6test(true, "1111:2222:3333:4444:5555::8888") t.ipv6test(true, "1111:2222:3333:4444::8888") t.ipv6test(true, "1111:2222:3333::8888") t.ipv6test(true, "1111:2222::8888") t.ipv6test(true, "1111::8888") t.ipv6test(true, "::8888") t.ipv6test(true, "1111:2222:3333:4444:5555::7777:8888") t.ipv6test(true, "1111:2222:3333:4444::7777:8888") t.ipv6test(true, "1111:2222:3333::7777:8888") t.ipv6test(true, "1111:2222::7777:8888") t.ipv6test(true, "1111::7777:8888") t.ipv6test(true, "::7777:8888") t.ipv6test(true, "1111:2222:3333:4444::6666:7777:8888") t.ipv6test(true, "1111:2222:3333::6666:7777:8888") t.ipv6test(true, "1111:2222::6666:7777:8888") t.ipv6test(true, "1111::6666:7777:8888") t.ipv6test(true, "::6666:7777:8888") t.ipv6test(true, "1111:2222:3333::5555:6666:7777:8888") t.ipv6test(true, "1111:2222::5555:6666:7777:8888") t.ipv6test(true, "1111::5555:6666:7777:8888") t.ipv6test(true, "::5555:6666:7777:8888") t.ipv6test(true, "1111:2222::4444:5555:6666:7777:8888") t.ipv6test(true, "1111::4444:5555:6666:7777:8888") t.ipv6test(true, "::4444:5555:6666:7777:8888") t.ipv6test(true, "1111::3333:4444:5555:6666:7777:8888") t.ipv6test(true, "::3333:4444:5555:6666:7777:8888") t.ipv6test(true, "::2222:3333:4444:5555:6666:7777:8888") t.ipv6test(true, "1111:2222:3333:4444:5555:6666:123.123.123.123") t.ipv6test(true, "1111:2222:3333:4444:5555::123.123.123.123") t.ipv6test(true, "1111:2222:3333:4444::123.123.123.123") t.ipv6test(true, "1111:2222:3333::123.123.123.123") t.ipv6test(true, "1111:2222::123.123.123.123") t.ipv6test(true, "1111::123.123.123.123") t.ipv6test(true, "::123.123.123.123") t.ipv6test(true, "1111:2222:3333:4444::6666:123.123.123.123") t.ipv6test(true, "1111:2222:3333::6666:123.123.123.123") t.ipv6test(true, "1111:2222::6666:123.123.123.123") t.ipv6test(true, "1111::6666:123.123.123.123") t.ipv6test(true, "::6666:123.123.123.123") t.ipv6test(true, "1111:2222:3333::5555:6666:123.123.123.123") t.ipv6test(true, "1111:2222::5555:6666:123.123.123.123") t.ipv6test(true, "1111::5555:6666:123.123.123.123") t.ipv6test(true, "::5555:6666:123.123.123.123") t.ipv6test(true, "1111:2222::4444:5555:6666:123.123.123.123") t.ipv6test(true, "1111::4444:5555:6666:123.123.123.123") t.ipv6test(true, "::4444:5555:6666:123.123.123.123") t.ipv6test(true, "1111::3333:4444:5555:6666:123.123.123.123") t.ipv6test(true, "::2222:3333:4444:5555:6666:123.123.123.123") t.ipv6test(false, "1::2:3:4:5:6:1.2.3.4") t.ipv6zerotest(true, "::") t.ipv6zerotest(true, "0:0:0:0:0:0:0:0") // Playing with combinations of "0" and "::" // NB: these are all sytactically correct, but are bad form // because "0" adjacent to "::" should be combined into "::" t.ipv6zerotest(true, "::0:0:0:0:0:0:0") t.ipv6zerotest(true, "::0:0:0:0:0:0") t.ipv6zerotest(true, "::0:0:0:0:0") t.ipv6zerotest(true, "::0:0:0:0") t.ipv6zerotest(true, "::0:0:0") t.ipv6zerotest(true, "::0:0") t.ipv6zerotest(true, "::0") t.ipv6zerotest(true, "0:0:0:0:0:0:0::") t.ipv6zerotest(true, "0:0:0:0:0:0::") t.ipv6zerotest(true, "0:0:0:0:0::") t.ipv6zerotest(true, "0:0:0:0::") t.ipv6zerotest(true, "0:0:0::") t.ipv6zerotest(true, "0:0::") t.ipv6zerotest(true, "0::") // New invalid from Aeron // Invalid data t.ipv6test(false, "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") // Too many components t.ipv6test(false, "1111:2222:3333:4444:5555:6666:7777:8888:9999") t.ipv6test(false, "1111:2222:3333:4444:5555:6666:7777:8888::") t.ipv6test(false, "::2222:3333:4444:5555:6666:7777:8888:9999") // Too few components t.ipv6test(false, "1111:2222:3333:4444:5555:6666:7777") t.ipv6test(false, "1111:2222:3333:4444:5555:6666") t.ipv6test(false, "1111:2222:3333:4444:5555") t.ipv6test(false, "1111:2222:3333:4444") t.ipv6test(false, "1111:2222:3333") t.ipv6test(false, "1111:2222") t.ipv6test2(false, "1111", false, t.isLenient()) // this is passing the ipv4 side for inet_aton //t.ipv6test(false,"1111"); // Missing : t.ipv6test(false, "11112222:3333:4444:5555:6666:7777:8888") t.ipv6test(false, "1111:22223333:4444:5555:6666:7777:8888") t.ipv6test(false, "1111:2222:33334444:5555:6666:7777:8888") t.ipv6test(false, "1111:2222:3333:44445555:6666:7777:8888") t.ipv6test(false, "1111:2222:3333:4444:55556666:7777:8888") t.ipv6test(false, "1111:2222:3333:4444:5555:66667777:8888") t.ipv6test(false, "1111:2222:3333:4444:5555:6666:77778888") // Missing : intended for :: t.ipv6test(false, "1111:2222:3333:4444:5555:6666:7777:8888:") t.ipv6test(false, "1111:2222:3333:4444:5555:6666:7777:") t.ipv6test(false, "1111:2222:3333:4444:5555:6666:") t.ipv6test(false, "1111:2222:3333:4444:5555:") t.ipv6test(false, "1111:2222:3333:4444:") t.ipv6test(false, "1111:2222:3333:") t.ipv6test(false, "1111:2222:") t.ipv6test(false, "1111:") t.ipv6test(false, ":") t.ipv6test(false, ":8888") t.ipv6test(false, ":7777:8888") t.ipv6test(false, ":6666:7777:8888") t.ipv6test(false, ":5555:6666:7777:8888") t.ipv6test(false, ":4444:5555:6666:7777:8888") t.ipv6test(false, ":3333:4444:5555:6666:7777:8888") t.ipv6test(false, ":2222:3333:4444:5555:6666:7777:8888") t.ipv6test(false, ":1111:2222:3333:4444:5555:6666:7777:8888") // ::: t.ipv6test(false, ":::2222:3333:4444:5555:6666:7777:8888") t.ipv6test(false, "1111:::3333:4444:5555:6666:7777:8888") t.ipv6test(false, "1111:2222:::4444:5555:6666:7777:8888") t.ipv6test(false, "1111:2222:3333:::5555:6666:7777:8888") t.ipv6test(false, "1111:2222:3333:4444:::6666:7777:8888") t.ipv6test(false, "1111:2222:3333:4444:5555:::7777:8888") t.ipv6test(false, "1111:2222:3333:4444:5555:6666:::8888") t.ipv6test(false, "1111:2222:3333:4444:5555:6666:7777:::") // Double ::"); t.ipv6test(false, "::2222::4444:5555:6666:7777:8888") t.ipv6test(false, "::2222:3333::5555:6666:7777:8888") t.ipv6test(false, "::2222:3333:4444::6666:7777:8888") t.ipv6test(false, "::2222:3333:4444:5555::7777:8888") t.ipv6test(false, "::2222:3333:4444:5555:7777::8888") t.ipv6test(false, "::2222:3333:4444:5555:7777:8888::") t.ipv6test(false, "1111::3333::5555:6666:7777:8888") t.ipv6test(false, "1111::3333:4444::6666:7777:8888") t.ipv6test(false, "1111::3333:4444:5555::7777:8888") t.ipv6test(false, "1111::3333:4444:5555:6666::8888") t.ipv6test(false, "1111::3333:4444:5555:6666:7777::") t.ipv6test(false, "1111:2222::4444::6666:7777:8888") t.ipv6test(false, "1111:2222::4444:5555::7777:8888") t.ipv6test(false, "1111:2222::4444:5555:6666::8888") t.ipv6test(false, "1111:2222::4444:5555:6666:7777::") t.ipv6test(false, "1111:2222:3333::5555::7777:8888") t.ipv6test(false, "1111:2222:3333::5555:6666::8888") t.ipv6test(false, "1111:2222:3333::5555:6666:7777::") t.ipv6test(false, "1111:2222:3333:4444::6666::8888") t.ipv6test(false, "1111:2222:3333:4444::6666:7777::") t.ipv6test(false, "1111:2222:3333:4444:5555::7777::") // Too many components" t.ipv6test(false, "1111:2222:3333:4444:5555:6666:7777:8888:1.2.3.4") t.ipv6test(false, "1111:2222:3333:4444:5555:6666:7777:1.2.3.4") t.ipv6test(false, "1111:2222:3333:4444:5555:6666::1.2.3.4") t.ipv6test(false, "::2222:3333:4444:5555:6666:7777:1.2.3.4") t.ipv6test(false, "1111:2222:3333:4444:5555:6666:1.2.3.4.5") // Too few components t.ipv6test(false, "1111:2222:3333:4444:5555:1.2.3.4") t.ipv6test(false, "1111:2222:3333:4444:1.2.3.4") t.ipv6test(false, "1111:2222:3333:1.2.3.4") t.ipv6test(false, "1111:2222:1.2.3.4") t.ipv6test(false, "1111:1.2.3.4") t.ipv6testOnly(false, "1.2.3.4") // Missing : t.ipv6test(false, "11112222:3333:4444:5555:6666:1.2.3.4") t.ipv6test(false, "1111:22223333:4444:5555:6666:1.2.3.4") t.ipv6test(false, "1111:2222:33334444:5555:6666:1.2.3.4") t.ipv6test(false, "1111:2222:3333:44445555:6666:1.2.3.4") t.ipv6test(false, "1111:2222:3333:4444:55556666:1.2.3.4") t.ipv6test(false, "1111:2222:3333:4444:5555:66661.2.3.4") // Missing . t.ipv6test(false, "1111:2222:3333:4444:5555:6666:255255.255.255") t.ipv6test(false, "1111:2222:3333:4444:5555:6666:255.255255.255") t.ipv6test(false, "1111:2222:3333:4444:5555:6666:255.255.255255") // Missing : intended for :: t.ipv6test(false, ":1.2.3.4") t.ipv6test(false, ":6666:1.2.3.4") t.ipv6test(false, ":5555:6666:1.2.3.4") t.ipv6test(false, ":4444:5555:6666:1.2.3.4") t.ipv6test(false, ":3333:4444:5555:6666:1.2.3.4") t.ipv6test(false, ":2222:3333:4444:5555:6666:1.2.3.4") t.ipv6test(false, ":1111:2222:3333:4444:5555:6666:1.2.3.4") // ::: t.ipv6test(false, ":::2222:3333:4444:5555:6666:1.2.3.4") t.ipv6test(false, "1111:::3333:4444:5555:6666:1.2.3.4") t.ipv6test(false, "1111:2222:::4444:5555:6666:1.2.3.4") t.ipv6test(false, "1111:2222:3333:::5555:6666:1.2.3.4") t.ipv6test(false, "1111:2222:3333:4444:::6666:1.2.3.4") t.ipv6test(false, "1111:2222:3333:4444:5555:::1.2.3.4") // Double :: t.ipv6test(false, "::2222::4444:5555:6666:1.2.3.4") t.ipv6test(false, "::2222:3333::5555:6666:1.2.3.4") t.ipv6test(false, "::2222:3333:4444::6666:1.2.3.4") t.ipv6test(false, "::2222:3333:4444:5555::1.2.3.4") t.ipv6test(false, "1111::3333::5555:6666:1.2.3.4") t.ipv6test(false, "1111::3333:4444::6666:1.2.3.4") t.ipv6test(false, "1111::3333:4444:5555::1.2.3.4") t.ipv6test(false, "1111:2222::4444::6666:1.2.3.4") t.ipv6test(false, "1111:2222::4444:5555::1.2.3.4") t.ipv6test(false, "1111:2222:3333::5555::1.2.3.4") // Missing parts t.ipv6test(false, "::.") t.ipv6test(false, "::..") t.ipv6test(false, "::...") t.ipv6test(false, "::1...") t.ipv6test(false, "::1.2..") t.ipv6test(false, "::1.2.3.") t.ipv6test(false, "::.2..") t.ipv6test(false, "::.2.3.") t.ipv6test(false, "::.2.3.4") t.ipv6test(false, "::..3.") t.ipv6test(false, "::..3.4") t.ipv6test(false, "::...4") // Extra : in front t.ipv6test(false, ":1111:2222:3333:4444:5555:6666:7777::") t.ipv6test(false, ":1111:2222:3333:4444:5555:6666::") t.ipv6test(false, ":1111:2222:3333:4444:5555::") t.ipv6test(false, ":1111:2222:3333:4444::") t.ipv6test(false, ":1111:2222:3333::") t.ipv6test(false, ":1111:2222::") t.ipv6test(false, ":1111::") t.ipv6test(false, ":::") t.ipv6test(false, ":1111:2222:3333:4444:5555:6666::8888") t.ipv6test(false, ":1111:2222:3333:4444:5555::8888") t.ipv6test(false, ":1111:2222:3333:4444::8888") t.ipv6test(false, ":1111:2222:3333::8888") t.ipv6test(false, ":1111:2222::8888") t.ipv6test(false, ":1111::8888") t.ipv6test(false, ":::8888") t.ipv6test(false, ":1111:2222:3333:4444:5555::7777:8888") t.ipv6test(false, ":1111:2222:3333:4444::7777:8888") t.ipv6test(false, ":1111:2222:3333::7777:8888") t.ipv6test(false, ":1111:2222::7777:8888") t.ipv6test(false, ":1111::7777:8888") t.ipv6test(false, ":::7777:8888") t.ipv6test(false, ":1111:2222:3333:4444::6666:7777:8888") t.ipv6test(false, ":1111:2222:3333::6666:7777:8888") t.ipv6test(false, ":1111:2222::6666:7777:8888") t.ipv6test(false, ":1111::6666:7777:8888") t.ipv6test(false, ":::6666:7777:8888") t.ipv6test(false, ":1111:2222:3333::5555:6666:7777:8888") t.ipv6test(false, ":1111:2222::5555:6666:7777:8888") t.ipv6test(false, ":1111::5555:6666:7777:8888") t.ipv6test(false, ":::5555:6666:7777:8888") t.ipv6test(false, ":1111:2222::4444:5555:6666:7777:8888") t.ipv6test(false, ":1111::4444:5555:6666:7777:8888") t.ipv6test(false, ":::4444:5555:6666:7777:8888") t.ipv6test(false, ":1111::3333:4444:5555:6666:7777:8888") t.ipv6test(false, ":::3333:4444:5555:6666:7777:8888") t.ipv6test(false, ":::2222:3333:4444:5555:6666:7777:8888") t.ipv6test(false, ":1111:2222:3333:4444:5555:6666:1.2.3.4") t.ipv6test(false, ":1111:2222:3333:4444:5555::1.2.3.4") t.ipv6test(false, ":1111:2222:3333:4444::1.2.3.4") t.ipv6test(false, ":1111:2222:3333::1.2.3.4") t.ipv6test(false, ":1111:2222::1.2.3.4") t.ipv6test(false, ":1111::1.2.3.4") t.ipv6test(false, ":::1.2.3.4") t.ipv6test(false, ":1111:2222:3333:4444::6666:1.2.3.4") t.ipv6test(false, ":1111:2222:3333::6666:1.2.3.4") t.ipv6test(false, ":1111:2222::6666:1.2.3.4") t.ipv6test(false, ":1111::6666:1.2.3.4") t.ipv6test(false, ":::6666:1.2.3.4") t.ipv6test(false, ":1111:2222:3333::5555:6666:1.2.3.4") t.ipv6test(false, ":1111:2222::5555:6666:1.2.3.4") t.ipv6test(false, ":1111::5555:6666:1.2.3.4") t.ipv6test(false, ":::5555:6666:1.2.3.4") t.ipv6test(false, ":1111:2222::4444:5555:6666:1.2.3.4") t.ipv6test(false, ":1111::4444:5555:6666:1.2.3.4") t.ipv6test(false, ":::4444:5555:6666:1.2.3.4") t.ipv6test(false, ":1111::3333:4444:5555:6666:1.2.3.4") t.ipv6test(false, ":::2222:3333:4444:5555:6666:1.2.3.4") // Extra : at end t.ipv6test(false, "1111:2222:3333:4444:5555:6666:7777:::") t.ipv6test(false, "1111:2222:3333:4444:5555:6666:::") t.ipv6test(false, "1111:2222:3333:4444:5555:::") t.ipv6test(false, "1111:2222:3333:4444:::") t.ipv6test(false, "1111:2222:3333:::") t.ipv6test(false, "1111:2222:::") t.ipv6test(false, "1111:::") t.ipv6test(false, ":::") t.ipv6test(false, "1111:2222:3333:4444:5555:6666::8888:") t.ipv6test(false, "1111:2222:3333:4444:5555::8888:") t.ipv6test(false, "1111:2222:3333:4444::8888:") t.ipv6test(false, "1111:2222:3333::8888:") t.ipv6test(false, "1111:2222::8888:") t.ipv6test(false, "1111::8888:") t.ipv6test(false, "::8888:") t.ipv6test(false, "1111:2222:3333:4444:5555::7777:8888:") t.ipv6test(false, "1111:2222:3333:4444::7777:8888:") t.ipv6test(false, "1111:2222:3333::7777:8888:") t.ipv6test(false, "1111:2222::7777:8888:") t.ipv6test(false, "1111::7777:8888:") t.ipv6test(false, "::7777:8888:") t.ipv6test(false, "1111:2222:3333:4444::6666:7777:8888:") t.ipv6test(false, "1111:2222:3333::6666:7777:8888:") t.ipv6test(false, "1111:2222::6666:7777:8888:") t.ipv6test(false, "1111::6666:7777:8888:") t.ipv6test(false, "::6666:7777:8888:") t.ipv6test(false, "1111:2222:3333::5555:6666:7777:8888:") t.ipv6test(false, "1111:2222::5555:6666:7777:8888:") t.ipv6test(false, "1111::5555:6666:7777:8888:") t.ipv6test(false, "::5555:6666:7777:8888:") t.ipv6test(false, "1111:2222::4444:5555:6666:7777:8888:") t.ipv6test(false, "1111::4444:5555:6666:7777:8888:") t.ipv6test(false, "::4444:5555:6666:7777:8888:") t.ipv6test(false, "1111::3333:4444:5555:6666:7777:8888:") t.ipv6test(false, "::3333:4444:5555:6666:7777:8888:") t.ipv6test(false, "::2222:3333:4444:5555:6666:7777:8888:") // Additional cases: http://crisp.tweakblogs.net/blog/2031/ipv6-validation-%28and-caveats%29.html t.ipv6test(true, "0:a:b:c:d:e:f::") t.ipv6test(true, "::0:a:b:c:d:e:f") // syntactically correct, but bad form (::0:... could be combined) t.ipv6test(true, "a:b:c:d:e:f:0::") t.ipv6test(false, "':10.0.0.1") t.testCIDRSubnets("9.129.237.26/32", "9.129.237.26/32") t.testCIDRSubnets("ffff::ffff/128", "ffff:0:0:0:0:0:0:ffff/128") t.testMasksAndPrefixes() t.testContains("0.0.0.0/0", "1.2.3.4", false) t.testContains("0.0.0.0/1", "127.2.3.4", false) t.testNotContains("0.0.0.0/1", "128.2.3.4") t.testContains("0.0.0.0/4", "15.2.3.4", false) t.testContains("0.0.0.0/4", "9.129.0.0/16", false) t.testContains("8.0.0.0/5", "15.2.3.4", false) t.testContains("8.0.0.0/7", "9.2.3.4", false) t.testContains("9.0.0.0/8", "9.2.3.4", false) t.testContains("9.128.0.0/9", "9.255.3.4", false) t.testContains("9.128.0.0/15", "9.128.3.4", false) t.testNotContains("9.128.0.0/15", "10.128.3.4") t.testContains("9.129.0.0/16", "9.129.3.4", false) t.testContains("9.129.237.24/30", "9.129.237.27", false) t.testContains("9.129.237.24/30", "9.129.237.26/31", false) t.testContains("9.129.237.26/32", "9.129.237.26", true) t.testNotContains("9.129.237.26/32", "9.128.237.26") t.testContains("0.0.0.0/0", "0.0.0.0/0", true) t.testContains("0.0.0.0/1", "0.0.0.0/1", true) t.testContains("0.0.0.0/4", "0.0.0.0/4", true) t.testContains("8.0.0.0/5", "8.0.0.0/5", true) t.testContains("8.0.0.0/7", "8.0.0.0/7", true) t.testContains("9.0.0.0/8", "9.0.0.0/8", true) t.testContains("9.128.0.0/9", "9.128.0.0/9", true) t.testContains("9.128.0.0/15", "9.128.0.0/15", true) t.testContains("9.129.0.0/16", "9.129.0.0/16", true) t.testContains("9.129.237.24/30", "9.129.237.24/30", true) t.testContains("9.129.237.26/32", "9.129.237.26/32", true) t.testContains("::ffff:1.2.3.4", "1.2.3.4", true) //ipv4 mapped t.testContains("::ffff:1.2.0.0/112", "1.2.3.4", false) t.testContains("::ffff:1.2.0.0/112", "1.2.0.0/16", true) t.testContains("0:0:0:0:0:0:0:0/0", "a:b:c:d:e:f:a:b", false) t.testContains("8000:0:0:0:0:0:0:0/1", "8aaa:b:c:d:e:f:a:b", false) t.testNotContains("8000:0:0:0:0:0:0:0/1", "aaa:b:c:d:e:f:a:b") t.testContains("ffff:0:0:0:0:0:0:0/30", "ffff:3:c:d:e:f:a:b", false) t.testNotContains("ffff:0:0:0:0:0:0:0/30", "ffff:4:c:d:e:f:a:b") t.testContains("ffff:0:0:0:0:0:0:0/32", "ffff:0:ffff:d:e:f:a:b", false) t.testNotContains("ffff:0:0:0:0:0:0:0/32", "ffff:1:ffff:d:e:f:a:b") t.testContains("ffff:0:0:0:0:0:0:fffc/126", "ffff:0:0:0:0:0:0:ffff", false) t.testContains("ffff:0:0:0:0:0:0:ffff/128", "ffff:0:0:0:0:0:0:ffff", true) t.testContains("::/0", "0:0:0:0:0:0:0:0/0", true) t.testContains("8000::/1", "8000:0:0:0:0:0:0:0/1", true) t.testContains("ffff::/30", "ffff:0:0:0:0:0:0:0/30", true) t.testContains("ffff::/32", "ffff:0:0:0:0:0:0:0/32", true) t.testContains("ffff::fffc/126", "ffff:0:0:0:0:0:0:fffc/126", true) t.testContains("ffff::ffff/128", "ffff:0:0:0:0:0:0:ffff/128", true) t.testContains("2001:db8::/120", "2001:db8::1", false) t.testContains("2001:db8::1/120", "2001:db8::1", true) t.testNotContains("2001:db8::1/120", "2001:db8::") t.testContains("2001:db8::/112", "2001:db8::", false) t.testContains("2001:db8::/111", "2001:db8::", false) t.testContains("2001:db8::/113", "2001:db8::", false) t.testNotContains("2001:db80::/113", "2001:db8::") t.testNotContains("2001:db0::/113", "2001:db8::") t.testNotContains("2001:db7::/113", "2001:db8::") t.testContains("2001:0db8:85a3:0000:0000:8a2e:0370:7334/120", "2001:0db8:85a3:0000:0000:8a2e:0370:7334/128", true) t.testContains("2001:0db8:85a3::8a2e:0370:7334/120", "2001:0db8:85a3:0000:0000:8a2e:0370:7334/128", true) t.testContains("2001:0db8:85a3:0000:0000:8a2e:0370:7334/120", "2001:0db8:85a3::8a2e:0370:7334/128", true) t.testContains("2001:0db8:85a3::8a2e:0370:7334/120", "2001:0db8:85a3::8a2e:0370:7334/128", true) t.testContains("2001:0db8:85a3:0000:0000:8a2e:0370::/120", "2001:0db8:85a3:0000:0000:8a2e:0370::/128", false) t.testContains("2001:0db8:85a3:0000:0000:8a2e:0370::/120", "2001:0db8:85a3::8a2e:0370:0/128", false) t.testContains("2001:0db8:85a3::8a2e:0370:0/120", "2001:0db8:85a3:0000:0000:8a2e:0370::/128", false) t.testContains("2001:0db8:85a3::8a2e:0370:0/120", "2001:0db8:85a3::8a2e:0370:0/128", false) t.testNotContains("12::/4", "123::") t.testNotContains("12::/4", "1234::") t.testNotContains("12::/8", "123::") t.testNotContains("123::/8", "1234::") t.testNotContains("12::/12", "123::") t.testNotContains("12::/16", "123::") t.testNotContains("12::/24", "123::") t.testNotContains("1:12::/20", "1:123::") t.testNotContains("1:12::/20", "1:1234::") t.testNotContains("1:12::/24", "1:123::") t.testNotContains("1:123::/24", "1:1234::") t.testNotContains("1:12::/28", "1:123::") t.testNotContains("1:12::/32", "1:123::") t.testNotContains("1:12::/40", "1:123::") t.testNotContainsNoReverse("1.0.0.0/16", "1.0.0.0/8", true) t.testContains("::/4", "123::", false) t.testNotContains("::/4", "1234::") t.testNotContains("::/8", "123::") t.testNotContains("100::/8", "1234::") t.testNotContains("10::/12", "123::") t.testNotContains("10::/16", "123::") t.testNotContains("10::/24", "123::") t.testNotContains("1:12::/20", "1:123::") t.testNotContains("1::/20", "1:1234::") t.testNotContains("1::/24", "1:123::") t.testNotContains("1:100::/24", "1:1234::") t.testNotContains("1:10::/28", "1:123::") t.testNotContains("1:10::/32", "1:123::") t.testNotContains("1:10::/40", "1:123::") t.testContains("1.0.0.0/16", "1.0.0.0/24", false) t.testContains("5.62.62.0/23", "5.62.63.1", false) t.testNotContains("5.62.62.0/23", "5.62.64.1") t.testNotContains("5.62.62.0/23", "5.62.68.1") t.testNotContains("5.62.62.0/23", "5.62.78.1") t.testNetmasks(0, "0.0.0.0/0", "0.0.0.0", "255.255.255.255", "::/0", "::", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") //test that the given prefix gives ipv4 and ipv6 addresses matching the netmasks t.testNetmasks(1, "128.0.0.0/1", "128.0.0.0", "127.255.255.255", "8000::/1", "8000::", "7fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") t.testNetmasks(15, "255.254.0.0/15", "255.254.0.0", "0.1.255.255", "fffe::/15", "fffe::", "1:ffff:ffff:ffff:ffff:ffff:ffff:ffff") t.testNetmasks(16, "255.255.0.0/16", "255.255.0.0", "0.0.255.255", "ffff::/16", "ffff::", "::ffff:ffff:ffff:ffff:ffff:ffff:ffff") t.testNetmasks(17, "255.255.128.0/17", "255.255.128.0", "0.0.127.255", "ffff:8000::/17", "ffff:8000::", "::7fff:ffff:ffff:ffff:ffff:ffff:ffff") t.testNetmasks(31, "255.255.255.254/31", "255.255.255.254", "0.0.0.1", "ffff:fffe::/31", "ffff:fffe::", "::1:ffff:ffff:ffff:ffff:ffff:ffff") t.testNetmasks(32, "255.255.255.255/32", "255.255.255.255", "0.0.0.0", "ffff:ffff::/32", "ffff:ffff::", "::ffff:ffff:ffff:ffff:ffff:ffff") t.testNetmasks(127, "255.255.255.255/127", "", "0.0.0.0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe/127", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "::1") t.testNetmasks(128, "255.255.255.255/128", "", "0.0.0.0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "::") t.testNetmasks(129, "255.255.255.255/129", "", "0.0.0.0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/129", "", "::") t.checkNotMask("254.255.0.0") t.checkNotMask("255.255.0.1") t.checkNotMask("0.1.0.0") t.checkNotMask("0::10") t.checkNotMask("1::0") //Some mask/address combinations do not result in a contiguous range and thus don't work //The underlying rule is that mask bits that are 0 must be above the resulting segment range. //Any bit in the mask that is 0 must not fall below any bit in the masked segment range that is different between low and high //Any network mask must eliminate the entire range in the segment //Any host mask is fine t.testSubnet("1.2.0.0", "0.0.255.255", 16 /* mask is valid with prefix */, "0.0.0.0/16" /* mask is valid alone */, "0.0.0.0", "1.2.0.0/16" /* prefix alone */) t.testSubnet("1.2.0.0", "0.0.255.255", 17, "0.0.0.0/17", "0.0.0.0", "1.2.0.0/17") t.testSubnet("1.2.128.0", "0.0.255.255", 17, "0.0.128.0/17", "0.0.128.0", "1.2.128.0/17") t.testSubnet("1.2.0.0", "0.0.255.255", 15, "0.0.0.0/15", "0.0.0.0", "1.2.0.0/15") t.testSubnet("1.2.0.0", "0.0.255.255", 15, "0.0.0.0/15", "0.0.0.0", "1.2.0.0/15") t.testSubnet("1.2.0.0/15", "0.0.255.255", 16, "0.0.0.0/16", "0.0.*.*", "1.2.0.0/15") // t.testSubnet("1.2.0.0/15", "0.0.255.255", 15, "0.0.0.0/15", "0.0.*.*", "1.2.0.0/15") t.testSubnet("1.2.0.0/15", "0.0.255.255", 15, "0.0.0.0/15", "0.0.*.*", "1.2.0.0/15") t.testSubnet("1.0.0.0/15", "0.1.255.255", 15, "0.0.0.0/15", "0.0-1.*.*", "1.0.0.0/15") t.testSubnet("1.2.0.0/17", "0.0.255.255", 16, "0.0.0-127.*/16", "0.0.0-127.*", "1.2.0-127.*/16") t.testSubnet("1.2.0.0/17", "0.0.255.255", 17, "0.0.0.0/17", "0.0.0-127.*", "1.2.0.0/17") t.testSubnet("1.2.128.0/17", "0.0.255.255", 17, "0.0.128.0/17", "0.0.128-255.*", "1.2.128.0/17") t.testSubnet("1.2.0.0/17", "0.0.255.255", 15, "0.0.0-127.*/15", "0.0.0-127.*", "1.2.0-127.*/15") // t.testSubnet("1.3.128.0/17", "0.0.255.255", 15, "0.1.128-255.*/15", "0.0.128-255.*", "1.2.0-127.*/15") // t.testSubnet("1.3.128.0/17", "255.255.255.255", 15, "1.3.128-255.*/15", "1.3.128-255.*", "1.2.0-127.*/15") t.testSubnet("1.3.0.0/16", "255.255.255.255", 8, "1.3.*.*/8", "1.3.*.*", "1.0.*.*/8") t.testSubnet("1.0.0.0/16", "255.255.255.255", 8, "1.0.*.*/8", "1.0.*.*", "1.0.*.*/8") t.testSubnet("1.0.0.0/18", "255.255.255.255", 16, "1.0.0-63.*/16", "1.0.0-63.*", "1.0.0-63.*/16") t.testSubnet("1.2.0.0", "255.255.0.0", 16, "1.2.0.0/16", "1.2.0.0", "1.2.0.0/16") t.testSubnet("1.2.0.0", "255.255.0.0", 17, "1.2.0.0/17", "1.2.0.0", "1.2.0.0/17") t.testSubnet("1.2.128.0", "255.255.0.0", 17, "1.2.0.0/17", "1.2.0.0", "1.2.128.0/17") t.testSubnet("1.2.128.0", "255.255.128.0", 17, "1.2.128.0/17", "1.2.128.0", "1.2.128.0/17") t.testSubnet("1.2.0.0", "255.255.0.0", 15, "1.2.0.0/15", "1.2.0.0", "1.2.0.0/15") t.testSubnet("1.2.0.0/17", "255.255.0.0", 16, "1.2.0-127.*/16", "1.2.0.0", "1.2.0-127.*/16") t.testSubnet("1.2.0.0/17", "255.255.0.0", 17, "1.2.0.0/17", "1.2.0.0", "1.2.0.0/17") t.testSubnet("1.2.128.0/17", "255.255.0.0", 17, "1.2.0.0/17", "1.2.0.0", "1.2.128.0/17") t.testSubnet("1.2.128.0/17", "255.255.128.0", 17, "1.2.128.0/17", "1.2.128.0", "1.2.128.0/17") t.testSubnet("1.2.0.0/17", "255.255.0.0", 15, "1.2.0-127.*/15", "1.2.0.0", "1.2.0-127.*/15") t.testSubnet("1.2.0.0/16", "255.255.0.0", 16, "1.2.0.0/16", "1.2.0.0", "1.2.0.0/16") t.testSubnet("1.2.0.0/16", "255.255.0.0", 17, "1.2.0.0/17", "1.2.0.0", "1.2.0.0/16") t.testSubnet("1.2.0.0/16", "255.255.0.0", 17, "1.2.0.0/17", "1.2.0.0", "1.2.0.0/16") t.testSubnet("1.2.0.0/16", "255.255.128.0", 17, "1.2.0-128.0/17", "", "1.2.0.0/16") t.testSubnet("1.2.0.0/16", "255.255.0.0", 15, "1.2.*.*/15", "1.2.0.0", "1.2.*.*/15") t.testSubnet("1.2.0.0/15", "255.255.0.0", 16, "1.2-3.0.0/16", "1.2-3.0.0", "1.2.0.0/15") t.testSubnet("1.2.0.0/15", "255.255.0.0", 17, "1.2-3.0.0/17", "1.2-3.0.0", "1.2.0.0/15") t.testSubnet("1.2.0.0/15", "255.255.128.0", 17, "1.2-3.0-128.0/17", "", "1.2.0.0/15") t.testSubnet("1.2.0.0/15", "255.255.128.0", 18, "", "", "1.2.0.0/15") t.testSubnet("1.2.0.0/15", "255.255.192.0", 18, "1.2-3.0-192.0/18", "", "1.2.0.0/15") t.testSubnet("1.0.0.0/12", "255.254.0.0", 16, "", "", "1.0.0.0/12") t.testSubnet("1.0.0.0/12", "255.243.0.255", 16, "1.0-3.0.0/16", "1.0-3.0.*", "1.0.0.0/12") t.testSubnet("1.0.0.0/12", "255.255.0.0", 16, "1.0-15.0.0/16", "1.0-15.0.0", "1.0.0.0/12") t.testSubnet("1.0.0.0/12", "255.240.0.0", 16, "1.0.0.0/16", "1.0.0.0", "1.0.0.0/12") t.testSubnet("1.0.0.0/12", "255.248.0.0", 13, "1.0-8.0.0/13", "", "1.0.0.0/12") t.testSubnet("1.2.0.0/15", "255.254.128.0", 17, "1.2.0-128.0/17", "", "1.2.0.0/15") t.testSubnet("1.2.0.0/15", "255.255.128.0", 17, "1.2-3.0-128.0/17", "", "1.2.0.0/15") t.testSubnet("1.2.0.0/15", "255.252.128.0", 17, "1.0.0-128.0/17", "", "1.2.0.0/15") t.testSubnet("1.2.0.0/15", "255.252.128.0", 18, "", "", "1.2.0.0/15") t.testSubnet("1.2.0.0/15", "255.255.127.0", 15, "1.2.0.0/15", "1.2-3.0-127.0", "1.2.0.0/15") t.testSubnet("1.2.0.0/15", "255.255.0.255", 15, "1.2.0.0/15", "1.2-3.0.*", "1.2.0.0/15") t.testSubnet("1.2.128.1/17", "0.0.255.255", 17, "0.0.128.1/17", "0.0.128.1", "1.2.128.1/17") t.testSubnet("1.2.3.4", "0.0.255.255", 16 /* mask is valid with prefix */, "0.0.3.4/16" /* mask is valid alone */, "0.0.3.4", "1.2.3.4/16" /* prefix alone */) t.testSubnet("1.2.3.4", "0.0.255.255", 17, "0.0.3.4/17", "0.0.3.4", "1.2.3.4/17") t.testSubnet("1.2.128.4", "0.0.255.255", 17, "0.0.128.4/17", "0.0.128.4", "1.2.128.4/17") t.testSubnet("1.2.3.4", "0.0.255.255", 15, "0.0.3.4/15", "0.0.3.4", "1.2.3.4/15") t.testSubnet("1.1.3.4", "0.0.255.255", 15, "0.1.3.4/15", "0.0.3.4", "1.1.3.4/15") t.testSubnet("1.2.128.4", "0.0.255.255", 15, "0.0.128.4/15", "0.0.128.4", "1.2.128.4/15") t.testSubnet("1.2.3.4/15", "0.0.255.255", 16, "0.0.3.4/16", "0.0.3.4", "1.2.3.4/15") //second to last is 0.0.0.0/15 and I don't know why. we are applying the mask only. I can see how the range becomes /16 but why the string look ike that? t.testSubnet("1.2.3.4/15", "0.0.255.255", 17, "0.0.3.4/17", "0.0.3.4", "1.2.3.4/15") t.testSubnet("1.2.128.4/15", "0.0.255.255", 17, "0.0.128.4/17", "0.0.128.4", "1.2.128.4/15") t.testSubnet("1.2.3.4/15", "0.0.255.255", 15, "0.0.3.4/15", "0.0.3.4", "1.2.3.4/15") t.testSubnet("1.1.3.4/15", "0.0.255.255", 15, "0.1.3.4/15", "0.0.3.4", "1.1.3.4/15") t.testSubnet("1.2.128.4/15", "0.0.255.255", 15, "0.0.128.4/15", "0.0.128.4", "1.2.128.4/15") t.testSubnet("1.1.3.4/15", "0.1.255.255", 15, "0.1.3.4/15", "0.1.3.4", "1.1.3.4/15") t.testSubnet("1.0.3.4/15", "0.1.255.255", 15, "0.0.3.4/15", "0.0.3.4", "1.0.3.4/15") t.testSubnet("1.2.3.4/17", "0.0.255.255", 16, "0.0.3.4/16", "0.0.3.4", "1.2.3.4/16") t.testSubnet("1.2.3.4/17", "0.0.255.255", 17, "0.0.3.4/17", "0.0.3.4", "1.2.3.4/17") t.testSubnet("1.2.128.4/17", "0.0.255.255", 17, "0.0.128.4/17", "0.0.128.4", "1.2.128.4/17") t.testSubnet("1.2.3.4/17", "0.0.255.255", 15, "0.0.3.4/15", "0.0.3.4", "1.2.3.4/15") t.testSubnet("1.1.3.4/17", "0.0.255.255", 15, "0.1.3.4/15", "0.0.3.4", "1.0.3.4/15") t.testSubnet("1.2.128.4/17", "0.0.255.255", 15, "0.0.128.4/15", "0.0.128.4", "1.2.0.4/15") t.testSubnet("1.2.3.4", "255.255.0.0", 16, "1.2.3.4/16", "1.2.0.0", "1.2.3.4/16") t.testSubnet("1.2.3.4", "255.255.0.0", 17, "1.2.3.4/17", "1.2.0.0", "1.2.3.4/17") t.testSubnet("1.2.128.4", "255.255.0.0", 17, "1.2.0.4/17", "1.2.0.0", "1.2.128.4/17") t.testSubnet("1.2.128.4", "255.255.128.0", 17, "1.2.128.4/17", "1.2.128.0", "1.2.128.4/17") t.testSubnet("1.2.3.4", "255.255.0.0", 15, "1.2.3.4/15", "1.2.0.0", "1.2.3.4/15") t.testSubnet("1.1.3.4", "255.255.0.0", 15, "1.1.3.4/15", "1.1.0.0", "1.1.3.4/15") t.testSubnet("1.2.128.4", "255.255.0.0", 15, "1.2.128.4/15", "1.2.0.0", "1.2.128.4/15") t.testSubnet("1.2.3.4/17", "255.255.0.0", 16, "1.2.3.4/16", "1.2.0.0", "1.2.3.4/16") t.testSubnet("1.2.3.4/17", "255.255.0.0", 17, "1.2.3.4/17", "1.2.0.0", "1.2.3.4/17") t.testSubnet("1.2.128.4/17", "255.255.0.0", 17, "1.2.0.4/17", "1.2.0.0", "1.2.128.4/17") t.testSubnet("1.2.128.4/17", "255.255.128.0", 17, "1.2.128.4/17", "1.2.128.0", "1.2.128.4/17") t.testSubnet("1.2.3.4/17", "255.255.0.0", 15, "1.2.3.4/15", "1.2.0.0", "1.2.3.4/15") t.testSubnet("1.1.3.4/17", "255.255.0.0", 15, "1.1.3.4/15", "1.1.0.0", "1.0.3.4/15") t.testSubnet("1.2.128.4/17", "255.255.0.0", 15, "1.2.128.4/15", "1.2.0.0", "1.2.0.4/15") t.testSubnet("1.2.3.4/16", "255.255.0.0", 16, "1.2.3.4/16", "1.2.0.0", "1.2.3.4/16") t.testSubnet("1.2.3.4/16", "255.255.0.0", 17, "1.2.3.4/17", "1.2.0.0", "1.2.3.4/16") t.testSubnet("1.2.128.4/16", "255.255.0.0", 17, "1.2.0.4/17", "1.2.0.0", "1.2.128.4/16") t.testSubnet("1.2.128.4/16", "255.255.128.0", 17, "1.2.128.4/17", "1.2.128.0", "1.2.128.4/16") t.testSubnet("1.2.3.4/16", "255.255.0.0", 15, "1.2.3.4/15", "1.2.0.0", "1.2.3.4/15") t.testSubnet("1.1.3.4/16", "255.255.0.0", 15, "1.1.3.4/15", "1.1.0.0", "1.0.3.4/15") t.testSubnet("1.2.128.4/16", "255.255.0.0", 15, "1.2.128.4/15", "1.2.0.0", "1.2.128.4/15") t.testSubnet("1.2.3.4/15", "255.255.0.0", 16, "1.2.3.4/16", "1.2.0.0", "1.2.3.4/15") t.testSubnet("1.2.3.4/15", "255.255.0.0", 17, "1.2.3.4/17", "1.2.0.0", "1.2.3.4/15") t.testSubnet("1.2.128.4/15", "255.255.0.0", 17, "1.2.0.4/17", "1.2.0.0", "1.2.128.4/15") t.testSubnet("1.2.128.4/15", "255.255.128.0", 17, "1.2.128.4/17", "1.2.128.0", "1.2.128.4/15") t.testSubnet("1.2.128.4/15", "255.255.128.0", 18, "1.2.128.4/18", "1.2.128.0", "1.2.128.4/15") t.testSubnet("1.2.128.4/15", "255.255.192.0", 18, "1.2.128.4/18", "1.2.128.0", "1.2.128.4/15") t.testSubnet("1.2.3.4/12", "255.254.0.0", 16, "1.2.3.4/16", "1.2.0.0", "1.2.3.4/12") t.testSubnet("1.2.3.4/12", "255.243.0.255", 16, "1.2.3.4/16", "1.2.0.4", "1.2.3.4/12") t.testSubnet("1.2.3.4/12", "255.255.0.0", 16, "1.2.3.4/16", "1.2.0.0", "1.2.3.4/12") t.testSubnet("1.2.3.4/12", "255.240.0.0", 16, "1.0.3.4/16", "1.0.0.0", "1.2.3.4/12") t.testSubnet("1.2.3.4/12", "255.248.0.0", 13, "1.2.3.4/13", "1.0.0.0", "1.2.3.4/12") t.testSubnet("1.2.128.4/15", "255.254.128.0", 17, "1.2.128.4/17", "1.2.128.0", "1.2.128.4/15") t.testSubnet("1.2.128.4/15", "255.255.128.0", 17, "1.2.128.4/17", "1.2.128.0", "1.2.128.4/15") t.testSubnet("1.2.128.4/15", "255.252.128.0", 17, "1.0.128.4/17", "1.0.128.0", "1.2.128.4/15") t.testSubnet("1.2.128.4/15", "255.252.128.0", 18, "1.0.128.4/18", "1.0.128.0", "1.2.128.4/15") t.testSubnet("1.2.3.4/15", "255.255.127.0", 15, "1.2.3.4/15", "1.2.3.0", "1.2.3.4/15") t.testSubnet("1.1.3.4/15", "255.255.0.0", 15, "1.1.3.4/15", "1.1.0.0", "1.1.3.4/15") t.testSubnet("1.2.128.4/15", "255.255.0.255", 15, "1.2.128.4/15", "1.2.0.4", "1.2.128.4/15") t.testSubnet("::/8", "ffff::", 128, "0-ff:0:0:0:0:0:0:0/128", "0-ff:0:0:0:0:0:0:0", "0:0:0:0:0:0:0:0/8") t.testSubnet("::/8", "fff0::", 128, "", "", "0:0:0:0:0:0:0:0/8") /*x*/ t.testSubnet("::/8", "fff0::", 12, "0-f0:0:0:0:0:0:0:0/12", "", "0:0:0:0:0:0:0:0/8") t.testSubnet("1.2.0.0/16", "255.255.0.1", 24, "1.2.0.0/24", "1.2.0.0-1", "1.2.0.0/16") t.testSubnet("1.2.0.0/16", "255.255.0.3", 24, "1.2.0.0/24", "1.2.0.0-3", "1.2.0.0/16") t.testSubnet("1.2.0.0/16", "255.255.3.3", 24, "1.2.0-3.0/24", "1.2.0-3.0-3", "1.2.0.0/16") t.testSplit("9.129.237.26", 0, "", "", "", 1, "9.129.237.26", 2) //compare the two for equality. compare the bytes of the second one with the bytes of the second one having no mask. t.testSplit("9.129.237.26", 8, "9", "9", "9/8", 2, "129.237.26", 2) t.testSplit("9.129.237.26", 16, "9.129", "9.129", "9.129/16", 2, "237.26", 2) t.testSplit("9.129.237.26", 31, "9.129.237.26-27", "9.129.237.26", "9.129.237.26/31", 2, "0", 2) t.testSplit("9.129.237.26", 32, "9.129.237.26", "9.129.237.26", "9.129.237.26/32", 2, "", 1) t.testSplit("1.2.3.4", 4, "0-15", "0", "0/4", 2, "1.2.3.4", 2) t.testSplit("255.2.3.4", 4, "240-255", "240", "240/4", 1, "15.2.3.4", 2) t.testSplit("9:129::237:26", 0, "", "", "", 1, "9:129:0:0:0:0:237:26", 12) //compare the two for equality. compare the bytes of the second one with the bytes of the second one having no mask. t.testSplit("9:129::237:26", 16, "9", "9", "9/16", 2, "129:0:0:0:0:237:26", 12) t.testSplit("9:129::237:26", 31, "9:128-129", "9:128", "9:128/31", 2, "1:0:0:0:0:237:26", 12) t.testSplit("9:129::237:26", 32, "9:129", "9:129", "9:129/32", 2, "0:0:0:0:237:26", 10) t.testSplit("9:129::237:26", 33, "9:129:0-7fff", "9:129:0", "9:129:0/33", 2, "0:0:0:0:237:26", 10) t.testSplit("9:129::237:26", 63, "9:129:0:0-1", "9:129:0:0", "9:129:0:0/63", 4, "0:0:0:237:26", 10) t.testSplit("9:129::237:26", 64, "9:129:0:0", "9:129:0:0", "9:129:0:0/64", 4, "0:0:237:26", 10) t.testSplit("9:129::237:26", 96, "9:129:0:0:0:0", "9:129:0:0:0:0", "9:129:0:0:0:0/96", 4, "237:26", 4) t.testSplit("9:129::237:26", 111, "9:129:0:0:0:0:236-237", "9:129:0:0:0:0:236", "9:129:0:0:0:0:236/111", 12, "1:26", 4) t.testSplit("9:129::237:26", 112, "9:129:0:0:0:0:237", "9:129:0:0:0:0:237", "9:129:0:0:0:0:237/112", 12, "26", 4) t.testSplit("9:129::237:26", 113, "9:129:0:0:0:0:237:0-7fff", "9:129:0:0:0:0:237:0", "9:129:0:0:0:0:237:0/113", 12, "26", 4) t.testSplit("9:129::237:ffff", 113, "9:129:0:0:0:0:237:8000-ffff", "9:129:0:0:0:0:237:8000", "9:129:0:0:0:0:237:8000/113", 12, "7fff", 3) t.testSplit("9:129::237:26", 127, "9:129:0:0:0:0:237:26-27", "9:129:0:0:0:0:237:26", "9:129:0:0:0:0:237:26/127", 12, "0", 5) //previously when splitting host we would have just one ipv4 segment, but now we have two ipv4 segments t.testSplit("9:129::237:26", 128, "9:129:0:0:0:0:237:26", "9:129:0:0:0:0:237:26", "9:129:0:0:0:0:237:26/128", 12, "", 1) USE_UPPERCASE := 2 t.testSplit("a:b:c:d:e:f:a:b", 4, "0-fff", "0", "0/4", 2, "a:b:c:d:e:f:a:b", 6*USE_UPPERCASE) t.testSplit("ffff:b:c:d:e:f:a:b", 4, "f000-ffff", "f000", "f000/4", 1*USE_UPPERCASE, "fff:b:c:d:e:f:a:b", 6*USE_UPPERCASE) t.testSplit("ffff:b:c:d:e:f:a:b", 2, "c000-ffff", "c000", "c000/2", 1*USE_UPPERCASE, "3fff:b:c:d:e:f:a:b", 6*USE_UPPERCASE) t.testURL("https://1.2.3.4") t.testURL("https://[a:a:a:a:b:b:b:b]") t.testURL("https://a:a:a:a:b:b:b:b") // TODO LATER maybe - testSections works with getStartsWithSQLClause //testSections("9.129.237.26", 0, 1) //testSections("9.129.237.26", 8, 1 /* 2 */) //testSections("9.129.237.26", 16, 1 /* 2 */) //testSections("9.129.237.26", 24, 1 /* 2 */) //testSections("9.129.237.26", 32, 1 /* 2 */) //testSections("9:129::237:26", 0, 1) //testSections("9:129::237:26", 16, 1 /* 2 */) //testSections("9:129::237:26", 64, 2 /* 4 */) //testSections("9:129::237:26", 80, 2 /* 4 */) //testSections("9:129::237:26", 96, 2 /* 4 */) //testSections("9:129::237:26", 112, 2 /* 12 */) //testSections("9:129::237:26", 128, 2 /* 12 */) // //testSections("9.129.237.26", 7, 2 /* 4 */) //testSections("9.129.237.26", 9, 128 /* 256 */) //129 is 10000001 //testSections("9.129.237.26", 10, 64 /* 128 */) //testSections("9.129.237.26", 11, 32 /* 64 */) //testSections("9.129.237.26", 12, 16 /* 32 */) //testSections("9.129.237.26", 13, 8 /* 16 */) //testSections("9.129.237.26", 14, 4 /* 8 */) //10000000 to 10000011 (128 to 131) //testSections("9.129.237.26", 15, 2 /* 4 */) //10000000 to 10000001 (128 to 129) // TODO LATER testVariantCounts works with string collections ////test that the given address has the given number of standard variants and total variants //testVariantCounts("::", 2, 2, 9, 1297); //testVariantCounts("::1", 2, 2, 10, 1298); ////testVariantCounts("::1", 2, 2, IPv6Address.network().getStandardLoopbackStrings().length, 1298);//this confirms that IPv6Address.getStandardLoopbackStrings() is being initialized properly //testVariantCounts("::ffff:1.2.3.4", 6, 4, 20, 1410, 1320);//ipv4 mapped //testVariantCounts("::fffe:1.2.3.4", 2, 4, 20, 1320, 1320);//almost identical but not ipv4 mapped //testVariantCounts("::ffff:0:0", 6, 4, 24, 1474, 1384);//ipv4 mapped //testVariantCounts("::fffe:0:0", 2, 4, 24, 1384, 1384);//almost identical but not ipv4 mapped //testVariantCounts("2:2:2:2:2:2:2:2", 2, 1, 6, 1280); //testVariantCounts("2:0:0:2:0:2:2:2", 2, 2, 18, 2240); //testVariantCounts("a:b:c:0:d:e:f:1", 2, 4, 12 * USE_UPPERCASE, 1920 * USE_UPPERCASE); //testVariantCounts("a:b:c:0:0:d:e:f", 2, 4, 12 * USE_UPPERCASE, 1600 * USE_UPPERCASE); //testVariantCounts("a:b:c:d:e:f:0:1", 2, 4, 8 * USE_UPPERCASE, 1408 * USE_UPPERCASE); //testVariantCounts("a:b:c:d:e:f:0:0", 2, 4, 8 * USE_UPPERCASE, 1344 * USE_UPPERCASE); //testVariantCounts("a:b:c:d:e:f:a:b", 2, 2, 6 * USE_UPPERCASE, 1280 * USE_UPPERCASE); //testVariantCounts("aaaa:bbbb:cccc:dddd:eeee:ffff:aaaa:bbbb", 2, 2, 2 * USE_UPPERCASE, 2 * USE_UPPERCASE); //testVariantCounts("a111:1111:1111:1111:1111:1111:9999:9999", 2, 2, 2 * USE_UPPERCASE, 2 * USE_UPPERCASE); //testVariantCounts("1a11:1111:1111:1111:1111:1111:9999:9999", 2, 2, 2 * USE_UPPERCASE, 2 * USE_UPPERCASE); //testVariantCounts("11a1:1111:1111:1111:1111:1111:9999:9999", 2, 2, 2 * USE_UPPERCASE, 2 * USE_UPPERCASE); //testVariantCounts("111a:1111:1111:1111:1111:1111:9999:9999", 2, 2, 2 * USE_UPPERCASE, 2 * USE_UPPERCASE); //testVariantCounts("aaaa:b:cccc:dddd:eeee:ffff:aaaa:bbbb", 2, 2, 4 * USE_UPPERCASE, 4 * USE_UPPERCASE); //testVariantCounts("aaaa:b:cc:dddd:eeee:ffff:aaaa:bbbb", 2, 2, 4 * USE_UPPERCASE, 8 * USE_UPPERCASE); //testVariantCounts("1.2.3.4", 6, 1, 2, 420, 90, 16); //testVariantCounts("0.0.0.0", 6, 1, 2, 484, 90, 16); //testVariantCounts("1111:2222:aaaa:4444:5555:6666:7070:700a", 2, 1 * USE_UPPERCASE, 1 * USE_UPPERCASE + 2 * USE_UPPERCASE, 1 * USE_UPPERCASE + 2 * USE_UPPERCASE);//this one can be capitalized when mixed //testVariantCounts("1111:2222:3333:4444:5555:6666:7070:700a", 2, 2, 1 * USE_UPPERCASE + 2, 1 * USE_UPPERCASE + 2);//this one can only be capitalized when not mixed, so the 2 mixed cases are not doubled t.testReverseHostAddress("1.2.0.0/20") t.testReverseHostAddress("1.2.3.4") t.testReverseHostAddress("1:f000::/20") b1 := -1 t.testFromBytes([]byte{byte(b1), byte(b1), byte(b1), byte(b1)}, "255.255.255.255") t.testFromBytes([]byte{1, 2, 3, 4}, "1.2.3.4") b := [16]byte{} t.testFromBytes(b[:], "::") t.testFromBytes([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, "::1") t.testFromBytes([]byte{0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, 1, 0, 2}, "a:b:c:d:e:f:1:2") if t.fullTest && runDNS { //t.testResolved("espn.com", "199.181.132.250") //t.testResolved("instapundit.com", "72.32.173.45") t.testResolved("espn.com", "::ffff:df9:b87b") t.testResolved("instapundit.com", "::ffff:ac43:b0af") } t.testResolved("9.32.237.26", "9.32.237.26") t.testResolved("9.70.146.84", "9.70.146.84") t.testNormalized("1.2.3.4", "1.2.3.4") t.testNormalized("1.2.00.4", "1.2.0.4") t.testNormalized("000.2.00.4", "0.2.0.4") t.testNormalized("00.2.00.000", "0.2.0.0") t.testNormalized("000.000.000.000", "0.0.0.0") t.testNormalized("A:B:C:D:E:F:A:B", "a:b:c:d:e:f:a:b") t.testNormalized("ABCD:ABCD:CCCC:Dddd:EeEe:fFfF:aAAA:Bbbb", "abcd:abcd:cccc:dddd:eeee:ffff:aaaa:bbbb") t.testNormalized("AB12:12CD:CCCC:Dddd:EeEe:fFfF:aAAA:Bbbb", "ab12:12cd:cccc:dddd:eeee:ffff:aaaa:bbbb") t.testNormalized("ABCD::CCCC:Dddd:EeEe:fFfF:aAAA:Bbbb", "abcd::cccc:dddd:eeee:ffff:aaaa:bbbb") t.testNormalized("::ABCD:CCCC:Dddd:EeEe:fFfF:aAAA:Bbbb", "::abcd:cccc:dddd:eeee:ffff:aaaa:bbbb") t.testNormalized("ABCD:ABCD:CCCC:Dddd:EeEe:fFfF:aAAA::", "abcd:abcd:cccc:dddd:eeee:ffff:aaaa::") t.testNormalized("::ABCD:Dddd:EeEe:fFfF:aAAA:Bbbb", "::abcd:dddd:eeee:ffff:aaaa:bbbb") t.testNormalized("ABCD:ABCD:CCCC:Dddd:fFfF:aAAA::", "abcd:abcd:cccc:dddd:ffff:aaaa::") t.testNormalized("::ABCD", "::abcd") t.testNormalized("aAAA::", "aaaa::") t.testNormalized("0:0:0:0:0:0:0:0", "::") t.testNormalized("0000:0000:0000:0000:0000:0000:0000:0000", "::") t.testNormalizedMC("0000:0000:0000:0000:0000:0000:0000:0000", "0:0:0:0:0:0:0:0", true, false) t.testNormalized("0:0:0:0:0:0:0:1", "::1") t.testNormalizedMC("0:0:0:0:0:0:0:1", "0:0:0:0:0:0:0:1", true, false) t.testNormalizedMC("0:0:0:0::0:0:1", "0:0:0:0:0:0:0:1", true, false) t.testNormalized("0000:0000:0000:0000:0000:0000:0000:0001", "::1") t.testNormalized("1:0:0:0:0:0:0:0", "1::") t.testNormalized("0001:0000:0000:0000:0000:0000:0000:0000", "1::") t.testNormalized("1:0:0:0:0:0:0:1", "1::1") t.testNormalized("0001:0000:0000:0000:0000:0000:0000:0001", "1::1") t.testNormalized("1:0:0:0::0:0:1", "1::1") t.testNormalized("0001::0000:0000:0000:0000:0000:0001", "1::1") t.testNormalized("0001:0000:0000:0000:0000:0000::0001", "1::1") t.testNormalized("::0000:0000:0000:0000:0000:0001", "::1") t.testNormalized("0001:0000:0000:0000:0000:0000::", "1::") t.testNormalized("1:0::1", "1::1") t.testNormalized("0001:0000::0001", "1::1") t.testNormalized("0::", "::") t.testNormalized("0000::", "::") t.testNormalized("::0", "::") t.testNormalized("::0000", "::") t.testNormalized("0:0:0:0:1:0:0:0", "::1:0:0:0") t.testNormalized("0000:0000:0000:0000:0001:0000:0000:0000", "::1:0:0:0") t.testNormalized("0:0:0:1:0:0:0:0", "0:0:0:1::") t.testNormalized("0000:0000:0000:0001:0000:0000:0000:0000", "0:0:0:1::") t.testNormalized("0:1:0:1:0:1:0:1", "::1:0:1:0:1:0:1") t.testNormalized("0000:0001:0000:0001:0000:0001:0000:0001", "::1:0:1:0:1:0:1") t.testNormalized("1:1:0:1:0:1:0:1", "1:1::1:0:1:0:1") t.testNormalized("0001:0001:0000:0001:0000:0001:0000:0001", "1:1::1:0:1:0:1") t.testNormalizedMC("A:B:C:D:E:F:000.000.000.000", "a:b:c:d:e:f::", true, true) t.testNormalizedMC("A:B:C:D:E::000.000.000.000", "a:b:c:d:e::", true, true) t.testNormalizedMC("::B:C:D:E:F:000.000.000.000", "0:b:c:d:e:f::", true, true) t.testNormalizedMC("A:B:C:D::000.000.000.000", "a:b:c:d::", true, true) t.testNormalizedMC("::C:D:E:F:000.000.000.000", "::c:d:e:f:0.0.0.0", true, true) t.testNormalizedMC("::C:D:E:F:000.000.000.000", "0:0:c:d:e:f:0.0.0.0", true, false) t.testNormalizedMC("A:B:C::E:F:000.000.000.000", "a:b:c:0:e:f::", true, true) t.testNormalizedMC("A:B::E:F:000.000.000.000", "a:b::e:f:0.0.0.0", true, true) t.testNormalizedMC("A:B:C:D:E:F:000.000.000.001", "a:b:c:d:e:f:0.0.0.1", true, true) t.testNormalizedMC("A:B:C:D:E::000.000.000.001", "a:b:c:d:e::0.0.0.1", true, true) t.testNormalizedMC("::B:C:D:E:F:000.000.000.001", "::b:c:d:e:f:0.0.0.1", true, true) t.testNormalizedMC("A:B:C:D::000.000.000.001", "a:b:c:d::0.0.0.1", true, true) t.testNormalizedMC("::C:D:E:F:000.000.000.001", "::c:d:e:f:0.0.0.1", true, true) t.testNormalizedMC("::C:D:E:F:000.000.000.001", "0:0:c:d:e:f:0.0.0.1", true, false) t.testNormalizedMC("A:B:C::E:F:000.000.000.001", "a:b:c::e:f:0.0.0.1", true, true) t.testNormalizedMC("A:B::E:F:000.000.000.001", "a:b::e:f:0.0.0.1", true, true) t.testNormalizedMC("A:B:C:D:E:F:001.000.000.000", "a:b:c:d:e:f:1.0.0.0", true, true) t.testNormalizedMC("A:B:C:D:E::001.000.000.000", "a:b:c:d:e::1.0.0.0", true, true) t.testNormalizedMC("::B:C:D:E:F:001.000.000.000", "::b:c:d:e:f:1.0.0.0", true, true) t.testNormalizedMC("A:B:C:D::001.000.000.000", "a:b:c:d::1.0.0.0", true, true) t.testNormalizedMC("::C:D:E:F:001.000.000.000", "::c:d:e:f:1.0.0.0", true, true) t.testNormalizedMC("::C:D:E:F:001.000.000.000", "0:0:c:d:e:f:1.0.0.0", true, false) t.testNormalizedMC("A:B:C::E:F:001.000.000.000", "a:b:c::e:f:1.0.0.0", true, true) t.testNormalizedMC("A:B::E:F:001.000.000.000", "a:b::e:f:1.0.0.0", true, true) t.testCanonical("0001:0000:0000:000F:0000:0000:0001:0001", "1::f:0:0:1:1") //must be leftmost t.testCanonical("0001:0001:0000:000F:0000:0001:0000:0001", "1:1:0:f:0:1:0:1") //but singles not compressed t.testMixed("0001:0001:0000:000F:0000:0001:0000:0001", "1:1::f:0:1:0.0.0.1") //singles compressed in mixed t.testCompressed("a.b.c.d", "a.b.c.d") t.testCompressed("1:0:1:1:1:1:1:1", "1::1:1:1:1:1:1") t.testCanonical("1:0:1:1:1:1:1:1", "1:0:1:1:1:1:1:1") t.testMixed("1:0:1:1:1:1:1:1", "1::1:1:1:1:0.1.0.1") t.testMixedNoComp("::", "::", "::0.0.0.0") t.testMixed("::1", "::0.0.0.1") t.testMask("1.2.3.4", "0.0.2.0", "0.0.2.0") t.testMask("1.2.3.4", "0.0.1.0", "0.0.1.0") t.testMask("A:B:C:D:E:F:A:B", "A:0:C:0:E:0:A:0", "A:0:C:0:E:0:A:0") t.testMask("A:B:C:D:E:F:A:B", "FFFF:FFFF:FFFF:FFFF::", "A:B:C:D::") t.testMask("A:B:C:D:E:F:A:B", "::FFFF:FFFF:FFFF:FFFF", "::E:F:A:B") t.testRadices("255.127.254.2", "11111111.1111111.11111110.10", 2) t.testRadices("2.254.127.255", "10.11111110.1111111.11111111", 2) t.testRadices("1.12.4.8", "1.1100.100.1000", 2) t.testRadices("8.4.12.1", "1000.100.1100.1", 2) t.testRadices("10.5.10.5", "1010.101.1010.101", 2) t.testRadices("5.10.5.10", "101.1010.101.1010", 2) t.testRadices("0.1.0.1", "0.1.0.1", 2) t.testRadices("1.0.1.0", "1.0.1.0", 2) t.testRadices("255.127.254.2", "513.241.512.2", 7) t.testRadices("2.254.127.255", "2.512.241.513", 7) t.testRadices("0.1.0.1", "0.1.0.1", 7) t.testRadices("1.0.1.0", "1.0.1.0", 7) t.testRadices("255.127.254.2", "120.87.11e.2", 15) t.testRadices("2.254.127.255", "2.11e.87.120", 15) t.testRadices("0.1.0.1", "0.1.0.1", 15) t.testRadices("1.0.1.0", "1.0.1.0", 15) var ninePrefs [9]ipaddr.PrefixLen t.testInsertAndAppendPrefs("a:b:c:d:e:f:aa:bb", "1:2:3:4:5:6:7:8", ninePrefs[:]) t.testInsertAndAppendPrefs("1.2.3.4", "5.6.7.8", ninePrefs[:5]) t.testReplace("a:b:c:d:e:f:aa:bb", "1:2:3:4:5:6:7:8") t.testReplace("1.2.3.4", "5.6.7.8") t.testInvalidIpv4Values() t.testInvalidIpv6Values() t.testIPv4Values([]int{1, 2, 3, 4}, "16909060") t.testIPv4Values([]int{0, 0, 0, 0}, "0") t.testIPv4Values([]int{255, 255, 255, 255}, strconv.FormatUint(0xffffffff, 10)) t.testIPv6Values([]int{1, 2, 3, 4, 5, 6, 7, 8}, "5192455318486707404433266433261576") t.testIPv6Values([]int{0, 0, 0, 0, 0, 0, 0, 0}, "0") t.testIPv6Values([]int{0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}, one28().String()) t.testSub("10.0.0.0/22", "10.0.1.0/24", []string{"10.0.0.0/24", "10.0.2.0/23"}) t.testIntersect("1:1::/32", "1:1:1:1:1:1:1:1", "1:1:1:1:1:1:1:1") //1:1:0:0:0:0:0:0/32 t.testIntersectLowest("1:1::/32", "1:1::/16", "1:1::/32", true) //1:1::/16 1:1:0:0:0:0:0:0/32 t.testIntersect("1:1::/32", "1:1::/48", "1:1::/48") t.testIntersect("1:1::/32", "1:1::/64", "1:1::/64") t.testIntersect("1:1::/32", "1:1:2:2::/64", "1:1:2:2::/64") t.testIntersect("1:1::/32", "1:0:2:2::/64", "") t.testIntersect("10.0.0.0/22", "10.0.0.0/24", "10.0.0.0/24") //[10.0.0.0/24, 10.0.2.0/23] t.testIntersect("10.0.0.0/22", "10.0.1.0/24", "10.0.1.0/24") //[10.0.1-3.0/24] t.testToPrefixBlock("1:3::3:4", "1:3::3:4") t.testToPrefixBlock("1.3.3.4", "1.3.3.4") t.testMaxHost("1.2.3.4", "255.255.255.255/0") t.testMaxHost("1.2.255.255/16", "1.2.255.255/16") t.testMaxHost("1:2:3:4:5:6:7:8", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0") t.testMaxHost("1:2:ffff:ffff:ffff:ffff:ffff:ffff/64", "1:2:ffff:ffff:ffff:ffff:ffff:ffff/64") t.testMaxHost("1:2:3:4:5:6:7:8/64", "1:2:3:4:ffff:ffff:ffff:ffff/64") t.testMaxHost("1:2:3:4:5:6:7:8/128", "1:2:3:4:5:6:7:8/128") t.testZeroHost("1.2.3.4", "0.0.0.0/0") t.testZeroHost("1.2.0.0/16", "1.2.0.0/16") t.testZeroHost("1:2:3:4:5:6:7:8", "::/0") t.testZeroHost("1:2::/64", "1:2::/64") t.testZeroHost("1:2:3:4:5:6:7:8/64", "1:2:3:4::/64") t.testZeroHost("1:2:3:4:5:6:7:8/128", "1:2:3:4:5:6:7:8/128") t.testZeroNetwork("1.2.3.4", "0.0.0.0") t.testZeroNetwork("1.2.0.0/16", "0.0.0.0/16") t.testZeroNetwork("1:2:3:4:5:6:7:8", "::") t.testZeroNetwork("1:2::/64", "::/64") t.testZeroNetwork("1:2:3:4:5:6:7:8/64", "::5:6:7:8/64") t.testZeroNetwork("1:2:3:4:5:6:7:8/128", "::/128") t.testIsPrefixBlock("1.2.3.4", false, false) t.testIsPrefixBlock("1.2.3.4/16", false, false) t.testIsPrefixBlock("1.2.0.0/16", true, true) t.testIsPrefixBlock("1.2.3.4/0", false, false) t.testIsPrefixBlock("1.2.3.3/31", false, false) t.testIsPrefixBlock("1.2.3.4/31", true, true) t.testIsPrefixBlock("1.2.3.4/32", true, true) t.testPrefixBlocks("1.2.3.4", 8, false, false) t.testPrefixBlocks("1.2.3.4/16", 8, false, false) t.testPrefixBlocks("1.2.0.0/16", 8, false, false) t.testPrefixBlocks("1.2.3.4/0", 8, false, false) t.testPrefixBlocks("1.2.3.4/8", 8, false, false) t.testPrefixBlocks("1.2.3.4/31", 8, false, false) t.testPrefixBlocks("1.2.3.4/32", 8, false, false) t.testPrefixBlocks("1.2.3.4", 24, false, false) t.testPrefixBlocks("1.2.3.4/16", 24, false, false) t.testPrefixBlocks("1.2.0.0/16", 24, true, false) t.testPrefixBlocks("1.2.3.4/0", 24, false, false) t.testPrefixBlocks("1.2.3.4/24", 24, false, false) t.testPrefixBlocks("1.2.3.4/31", 24, false, false) t.testPrefixBlocks("1.2.3.4/32", 24, false, false) t.testIsPrefixBlock("a:b:c:d:e:f:a:b", false, false) t.testIsPrefixBlock("a:b:c:d:e:f:a:b/64", false, false) t.testIsPrefixBlock("a:b:c:d::/64", true, true) t.testIsPrefixBlock("a:b:c:d:e::/64", false, false) t.testIsPrefixBlock("a:b:c::/64", true, true) t.testIsPrefixBlock("a:b:c:d:e:f:a:b/0", false, false) t.testIsPrefixBlock("a:b:c:d:e:f:a:b/127", false, false) t.testIsPrefixBlock("a:b:c:d:e:f:a:b/128", true, true) t.testPrefixBlocks("a:b:c:d:e:f:a:b", 0, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/64", 0, false, false) t.testPrefixBlocks("a:b:c:d::/64", 0, false, false) t.testPrefixBlocks("a:b:c:d:e::/64", 0, false, false) t.testPrefixBlocks("a:b:c::/64", 0, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/0", 0, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/127", 0, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/128", 0, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b", 63, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/64", 63, false, false) t.testPrefixBlocks("a:b:c:d::/64", 63, false, false) t.testPrefixBlocks("a:b:c:d:e::/64", 63, false, false) t.testPrefixBlocks("a:b:c::/64", 63, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/0", 63, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/127", 63, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/128", 63, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b", 64, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/64", 64, false, false) t.testPrefixBlocks("a:b:c:d::/64", 64, true, true) t.testPrefixBlocks("a:b:c:d:e::/64", 64, false, false) t.testPrefixBlocks("a:b:c::/64", 64, true, true) t.testPrefixBlocks("a:b:c:d:e:f:a:b/0", 64, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/127", 64, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/128", 64, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b", 65, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/64", 65, false, false) t.testPrefixBlocks("a:b:c:d::/64", 65, true, false) t.testPrefixBlocks("a:b:c:d:e::/64", 65, false, false) t.testPrefixBlocks("a:b:c::/64", 65, true, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/0", 65, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/127", 65, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/128", 65, false, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b", 128, true, true) t.testPrefixBlocks("a:b:c:d:e:f:a:b/64", 128, true, true) t.testPrefixBlocks("a:b:c:d::/64", 128, true, false) t.testPrefixBlocks("a:b:c:d:e::/64", 128, true, true) t.testPrefixBlocks("a:b:c::/64", 128, true, false) t.testPrefixBlocks("a:b:c:d:e:f:a:b/0", 128, true, true) t.testPrefixBlocks("a:b:c:d:e:f:a:b/127", 128, true, true) t.testPrefixBlocks("a:b:c:d:e:f:a:b/128", 128, true, true) t.testSplitBytes("1.2.3.4") t.testSplitBytes("1.2.3.4/16") t.testSplitBytes("1.2.3.4/0") t.testSplitBytes("1.2.3.4/32") t.testSplitBytes("ffff:2:3:4:eeee:dddd:cccc:bbbb") t.testSplitBytes("ffff:2:3:4:eeee:dddd:cccc:bbbb/64") t.testSplitBytes("ffff:2:3:4:eeee:dddd:cccc:bbbb/0") t.testSplitBytes("ffff:2:3:4:eeee:dddd:cccc:bbbb/128") t.testByteExtension("255.255.255.255", [][]byte{ {0, 0, 255, 255, 255, 255}, {0, 255, 255, 255, 255}, {255, 255, 255, 255}, }) t.testByteExtension("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", [][]byte{ {0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, {0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, {0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, }) t.testByteExtension("0.0.0.255", [][]byte{ {0, 0, 0, 0, 0, 0, 0, 0, 0, 255}, {0, 0, 0, 0, 0, 0, 0, 0, 255}, {0, 0, 0, 0, 255}, {0, 0, 0, 255}, }) t.testByteExtension("::ff", [][]byte{ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff}, {0, 0xff}, {0xff}, }) t.testByteExtension("0.0.0.127", [][]byte{ {0, 0, 0, 0, 0, 127}, {0, 0, 0, 0, 127}, {0, 0, 0, 127}, {0, 127}, {127}, }) t.testByteExtension("::7f", [][]byte{ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127}, {0, 0, 127}, {0, 127}, {127}, }) t.testByteExtension("255.255.255.128", [][]byte{ {0, 0, 0, 0, 0, 0, 255, 255, 255, 128}, {0, 255, 255, 255, 128}, {0, 0, 255, 255, 255, 128}, {0, 255, 255, 255, 128}, {255, 255, 255, 128}, }) t.testByteExtension("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff80", [][]byte{ {0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80}, {0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80}, {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80}, {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80}, }) t.testByteExtension("ffff:ffff:ffff:ffff:ffff:ffff:ffff:8000", [][]byte{ {0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0}, {0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0}, {0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0}, {0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0}, {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0}, }) t.testByteExtension("1.2.3.4", [][]byte{ {1, 2, 3, 4}, {0, 1, 2, 3, 4}, }) t.testByteExtension("102:304:506:708:90a:b0c:d0e:f10", [][]byte{ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, }) t.testLargeDivs([][]byte{ {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10, 11, 12}, {13, 14, 15, 16}, }) t.testLargeDivs([][]byte{ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, }) t.testLargeDivs([][]byte{ {1, 2, 3, 4, 5}, //new byte[] {}, {6, 7, 8, 9, 10, 11, 12}, {13, 14, 15, 16}, }) t.testLargeDivs([][]byte{ {1}, {2}, {3}, {4}, {5}, {6, 7}, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15}, {16}, }) t.testLargeDivs([][]byte{ {1}, {2, 3}, {4}, }) t.testIncrement("1.2.3.4", 0, "1.2.3.4") t.testIncrement("1.2.3.4", 1, "1.2.3.5") t.testIncrement("1.2.3.4", -1, "1.2.3.3") t.testIncrement("1.2.3.4", -4, "1.2.3.0") t.testIncrement("1.2.3.4", -5, "1.2.2.255") t.testIncrement("0.0.0.4", -5, "") t.testIncrement("1.2.3.4", 251, "1.2.3.255") t.testIncrement("1.2.3.4", 252, "1.2.4.0") t.testIncrement("1.2.3.4", 256, "1.2.4.4") t.testIncrement("1.2.3.4", 256, "1.2.4.4") t.testIncrement("1.2.3.4", 65536, "1.3.3.4") t.testIncrement("1.2.3.4", 16777216, "2.2.3.4") t.testIncrement("1.2.3.4", 4261412864, "255.2.3.4") t.testIncrement("1.2.3.4", 4278190080, "") t.testIncrement("1.2.3.4", 4278058236, "") t.testIncrement("1.2.3.4", 4278058237, "") t.testIncrement("1.2.3.4", 4278058235, "255.255.255.255") t.testIncrement("255.0.0.4", -4278190084, "0.0.0.0") t.testIncrement("255.0.0.4", -4278190085, "") t.testIncrement("ffff:ffff:ffff:ffff:f000::0", 1, "ffff:ffff:ffff:ffff:f000::1") t.testIncrement("ffff:ffff:ffff:ffff:f000::0", -1, "ffff:ffff:ffff:ffff:efff:ffff:ffff:ffff") t.testIncrement("ffff:ffff:ffff:ffff:8000::", math.MinInt64, "ffff:ffff:ffff:ffff::") t.testIncrement("ffff:ffff:ffff:ffff:7fff:ffff:ffff:ffff", math.MinInt64, "ffff:ffff:ffff:fffe:ffff:ffff:ffff:ffff") t.testIncrement("ffff:ffff:ffff:ffff:7fff:ffff:ffff:fffe", math.MinInt64, "ffff:ffff:ffff:fffe:ffff:ffff:ffff:fffe") t.testIncrement("::8000:0:0:0", math.MinInt64, "::") t.testIncrement("::7fff:ffff:ffff:ffff", math.MinInt64, "") t.testIncrement("::7fff:ffff:ffff:ffff", math.MinInt64, "") t.testIncrement("::7fff:ffff:ffff:fffe", math.MinInt64, "") t.testIncrement("ffff:ffff:ffff:ffff:8000::0", math.MaxInt64, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") t.testIncrement("ffff:ffff:ffff:ffff:8000::1", math.MaxInt64, "") t.testIncrement("::1", 1, "::2") t.testIncrement("::1", 0, "::1") t.testIncrement("::1", -1, "::") t.testIncrement("::1", -2, "") t.testIncrement("::2", 1, "::3") t.testIncrement("::2", -1, "::1") t.testIncrement("::2", -2, "::") t.testIncrement("::2", -3, "") t.testIncrement("1::1", 0, "1::1") t.testIncrement("1::1", 1, "1::2") t.testIncrement("1::1", -1, "1::") t.testIncrement("1::1", -2, "::ffff:ffff:ffff:ffff:ffff:ffff:ffff") t.testIncrement("1::2", 1, "1::3") t.testIncrement("1::2", -1, "1::1") t.testIncrement("1::2", -2, "1::") t.testIncrement("1::2", -3, "::ffff:ffff:ffff:ffff:ffff:ffff:ffff") t.testIncrement("::fffe", 2, "::1:0") t.testIncrement("::ffff", 2, "::1:1") t.testIncrement("::1:ffff", 2, "::2:1") t.testIncrement("::1:ffff", -2, "::1:fffd") t.testIncrement("::1:ffff", -0x10000, "::ffff") t.testIncrement("::1:ffff", -0x10001, "::fffe") t.testLeadingZeroAddr("00.1.2.3", true) t.testLeadingZeroAddr("1.00.2.3", true) t.testLeadingZeroAddr("1.2.00.3", true) t.testLeadingZeroAddr("1.2.3.00", true) t.testLeadingZeroAddr("01.1.2.3", true) t.testLeadingZeroAddr("1.01.2.3", true) t.testLeadingZeroAddr("1.2.01.3", true) t.testLeadingZeroAddr("1.2.3.01", true) t.testLeadingZeroAddr("0.1.2.3", false) t.testLeadingZeroAddr("1.0.2.3", false) t.testLeadingZeroAddr("1.2.0.3", false) t.testLeadingZeroAddr("1.2.3.0", false) // octal and hex addresses are not allowed when we disallow leading zeros. // if we allow leading zeros, the inet aton settings determine if hex is allowed, // or whether leading zeros are interpreted as octal. // We can also disallow octal leading zeros, which are extra zeros after the 0x for hex or the 0 for octal. // We never allow 00x regardless of the settings. // Note that having a flag to disallow leading zeros and then seeing 1.02.3.4 being allowed, that would be annoying, so we do not do that anymore. t.testInetAtonLeadingZeroAddr("11.1.2.3", false, false, false) // boolean are (a) has a leading zero (b) has a leading zero following 0x or 0 and (c) the leading zeros are octal (not hex) t.testInetAtonLeadingZeroAddr("0.1.2.3", false, false, false) t.testInetAtonLeadingZeroAddr("1.0.2.3", false, false, false) t.testInetAtonLeadingZeroAddr("1.2.0.3", false, false, false) t.testInetAtonLeadingZeroAddr("1.2.3.0", false, false, false) t.testInetAtonLeadingZeroAddr("0x1.1.2.3", true, false, false) t.testInetAtonLeadingZeroAddr("1.0x1.2.3", true, false, false) t.testInetAtonLeadingZeroAddr("1.2.0x1.3", true, false, false) t.testInetAtonLeadingZeroAddr("1.2.3.0x1", true, false, false) t.testInetAtonLeadingZeroAddr("0x01.1.2.3", true, true, false) t.testInetAtonLeadingZeroAddr("1.0x01.2.3", true, true, false) t.testInetAtonLeadingZeroAddr("1.2.0x01.3", true, true, false) t.testInetAtonLeadingZeroAddr("1.2.3.0x01", true, true, false) t.testInetAtonLeadingZeroAddr("01.1.2.3", true, false, true) t.testInetAtonLeadingZeroAddr("1.01.2.3", true, false, true) t.testInetAtonLeadingZeroAddr("1.2.01.3", true, false, true) t.testInetAtonLeadingZeroAddr("1.2.3.01", true, false, true) t.testInetAtonLeadingZeroAddr("010.1.2.3", true, false, true) t.testInetAtonLeadingZeroAddr("1.010.2.3", true, false, true) t.testInetAtonLeadingZeroAddr("1.2.010.3", true, false, true) t.testInetAtonLeadingZeroAddr("1.2.3.010", true, false, true) t.testInetAtonLeadingZeroAddr("001.1.2.3", true, true, true) t.testInetAtonLeadingZeroAddr("1.001.2.3", true, true, true) t.testInetAtonLeadingZeroAddr("1.2.001.3", true, true, true) t.testInetAtonLeadingZeroAddr("1.2.3.001", true, true, true) t.testLeadingZeroAddr("00:1:2:3::", true) t.testLeadingZeroAddr("1:00:2:3::", true) t.testLeadingZeroAddr("1:2:00:3::", true) t.testLeadingZeroAddr("1:2:3:00::", true) t.testLeadingZeroAddr("01:1:2:3::", true) t.testLeadingZeroAddr("1:01:2:3::", true) t.testLeadingZeroAddr("1:2:01:3::", true) t.testLeadingZeroAddr("1:2:3:01::", true) t.testLeadingZeroAddr("0:1:2:3::", false) t.testLeadingZeroAddr("1:0:2:3::", false) t.testLeadingZeroAddr("1:2:0:3::", false) t.testLeadingZeroAddr("1:2:3:0::", false) //a b x y t.testRangeJoin("1.2.3.4", "1.2.4.3", "1.2.4.5", "1.2.5.6", "", "") t.testRangeIntersect("1.2.3.4", "1.2.4.3", "1.2.4.5", "1.2.5.6", "", "") t.testRangeSubtract("1.2.3.4", "1.2.4.3", "1.2.4.5", "1.2.5.6", "1.2.3.4", "1.2.4.3") t.testRangeExtend("1.2.3.4", "1.2.4.3", "1.2.4.5", "1.2.5.6", "1.2.3.4", "1.2.5.6") t.testRangeExtend("1.2.3.4", "", "1.2.5.6", "", "1.2.3.4", "1.2.5.6") t.testRangeExtend("1.2.3.4", "1.2.4.3", "1.2.5.6", "", "1.2.3.4", "1.2.5.6") //a x b y t.testRangeJoin("1.2.3.4", "1.2.4.5", "1.2.4.3", "1.2.5.6", "1.2.3.4", "1.2.5.6") t.testRangeIntersect("1.2.3.4", "1.2.4.5", "1.2.4.3", "1.2.5.6", "1.2.4.3", "1.2.4.5") t.testRangeSubtract("1.2.3.4", "1.2.4.5", "1.2.4.3", "1.2.5.6", "1.2.3.4", "1.2.4.2") t.testRangeExtend("1.2.3.4", "1.2.4.5", "1.2.4.3", "1.2.5.6", "1.2.3.4", "1.2.5.6") t.testRangeExtend("1.2.3.4", "", "1.2.5.6", "", "1.2.3.4", "1.2.5.6") t.testRangeExtend("1.2.3.4", "1.2.4.5", "1.2.5.6", "", "1.2.3.4", "1.2.5.6") //a x y b t.testRangeJoin("1.2.3.4", "1.2.5.6", "1.2.4.3", "1.2.4.5", "1.2.3.4", "1.2.5.6") t.testRangeIntersect("1.2.3.4", "1.2.5.6", "1.2.4.3", "1.2.4.5", "1.2.4.3", "1.2.4.5") t.testRangeSubtract("1.2.3.4", "1.2.5.6", "1.2.4.3", "1.2.4.5", "1.2.3.4", "1.2.4.2", "1.2.4.6", "1.2.5.6") t.testRangeExtend("1.2.3.4", "1.2.5.6", "1.2.4.3", "1.2.4.5", "1.2.3.4", "1.2.5.6") t.testRangeExtend("1.2.3.4", "1.2.5.6", "1.2.4.3", "", "1.2.3.4", "1.2.5.6") //a b x y t.testRangeJoin("1:2:3:4::", "1:2:4:3::", "1:2:4:5::", "1:2:5:6::", "", "") t.testRangeIntersect("1:2:3:4::", "1:2:4:3::", "1:2:4:5::", "1:2:5:6::", "", "") t.testRangeSubtract("1:2:3:4::", "1:2:4:3::", "1:2:4:5::", "1:2:5:6::", "1:2:3:4::", "1:2:4:3::") t.testRangeExtend("1:2:3:4::", "1:2:4:3::", "1:2:4:5::", "1:2:5:6::", "1:2:3:4::", "1:2:5:6::") t.testRangeExtend("1:2:3:4::", "", "1:2:5:6::", "", "1:2:3:4::", "1:2:5:6::") t.testRangeExtend("1:2:3:4::", "1:2:4:3::", "1:2:5:6::", "", "1:2:3:4::", "1:2:5:6::") //a x b y t.testRangeJoin("1:2:3:4::", "1:2:4:5::", "1:2:4:3::", "1:2:5:6::", "1:2:3:4::", "1:2:5:6::") t.testRangeIntersect("1:2:3:4::", "1:2:4:5::", "1:2:4:3::", "1:2:5:6::", "1:2:4:3::", "1:2:4:5::") t.testRangeSubtract("1:2:3:4::", "1:2:4:5::", "1:2:4:3::", "1:2:5:6::", "1:2:3:4::", "1:2:4:2:ffff:ffff:ffff:ffff") t.testRangeExtend("1:2:3:4::", "1:2:4:5::", "1:2:4:3::", "1:2:5:6::", "1:2:3:4::", "1:2:5:6::") t.testRangeExtend("1:2:3:4::", "", "1:2:5:6::", "", "1:2:3:4::", "1:2:5:6::") t.testRangeExtend("1:2:3:4::", "1:2:4:5::", "1:2:5:6::", "", "1:2:3:4::", "1:2:5:6::") //a x y b t.testRangeJoin("1:2:3:4::", "1:2:5:6::", "1:2:4:3::", "1:2:4:5::", "1:2:3:4::", "1:2:5:6::") t.testRangeIntersect("1:2:3:4::", "1:2:5:6::", "1:2:4:3::", "1:2:4:5::", "1:2:4:3::", "1:2:4:5::") t.testRangeSubtract("1:2:3:4::", "1:2:5:6::", "1:2:4:3::", "1:2:4:5::", "1:2:3:4::", "1:2:4:2:ffff:ffff:ffff:ffff", "1:2:4:5::1", "1:2:5:6::") t.testRangeExtend("1:2:3:4::", "1:2:5:6::", "1:2:4:3::", "1:2:4:5::", "1:2:3:4::", "1:2:5:6::") t.testRangeExtend("1:2:5:6::", "", "1:2:3:4::", "", "1:2:3:4::", "1:2:5:6::") t.testRangeExtend("1:2:5:6::", "", "1:2:3:4::", "1:2:4:5::", "1:2:3:4::", "1:2:5:6::") t.testAddressStringRange1("1.2.3.4", []interface{}{1, 2, 3, 4}) t.testAddressStringRange1("a:b:cc:dd:e:f:1.2.3.4", []interface{}{0xa, 0xb, 0xcc, 0xdd, 0xe, 0xf, 1, 2, 3, 4}) t.testAddressStringRange1("1:2:4:5:6:7:8:f", []interface{}{1, 2, 4, 5, 6, 7, 8, 0xf}) t.testAddressStringRange1("1:2:4:5::", []interface{}{1, 2, 4, 5, 0}) t.testAddressStringRange1("::1:2:4:5", []interface{}{0, 1, 2, 4, 5}) t.testAddressStringRange1("1:2:4:5::6", []interface{}{1, 2, 4, 5, 0, 6}) t.testAddressStringRange1("a:b:c::cc:d:1.255.3.128", []interface{}{0xa, 0xb, 0xc, 0x0, 0xcc, 0xd, 1, 255, 3, 128}) //[a, b, c, 0-ffff, cc, d, e, f] t.testAddressStringRange1("a::cc:d:1.255.3.128", []interface{}{0xa, 0x0, 0xcc, 0xd, 1, 255, 3, 128}) //[a, 0-ffffffffffff, cc, d, e, f] t.testAddressStringRange1("::cc:d:1.255.3.128", []interface{}{0x0, 0xcc, 0xd, 1, 255, 3, 128}) //[0-ffffffffffffffff, cc, d, e, f] // with prefix lengths p15 := cacheTestBits(15) p16 := cacheTestBits(16) p31 := cacheTestBits(31) p63 := cacheTestBits(63) p64 := cacheTestBits(64) p127 := cacheTestBits(127) t.testAddressStringRange("1.2.3.4/31", []interface{}{1, 2, 3, []uint{4, 5}}, p31) t.testAddressStringRange("a:b:cc:dd:e:f:1.2.3.4/127", []interface{}{0xa, 0xb, 0xcc, 0xdd, 0xe, 0xf, 1, 2, 3, []uint{4, 5}}, p127) t.testAddressStringRange("1:2:4:5::/64", []interface{}{1, 2, 4, 5, []*big.Int{bigZeroConst(), setBigString("ffffffffffffffff", 16)}}, p64) t.testAddressStringRange("1.2.3.4/15", []interface{}{1, 2, 3, 4}, p15) t.testAddressStringRange("a:b:cc:dd:e:f:1.2.3.4/63", []interface{}{0xa, 0xb, 0xcc, 0xdd, 0xe, 0xf, 1, 2, 3, 4}, p63) t.testAddressStringRange("1:2:4:5::/63", []interface{}{1, 2, 4, 5, 0}, p63) t.testAddressStringRange("::cc:d:1.255.3.128/16", []interface{}{0x0, 0xcc, 0xd, 1, 255, 3, 128}, p16) //[0-ffffffffffffffff, cc, d, e, f] // with masks t.testSubnetStringRange2("::aaaa:bbbb:cccc/abcd:dcba:aaaa:bbbb:cccc::dddd", "::cccc", "::cccc", []interface{}{0, 0, 0, 0xcccc}) t.testSubnetStringRange2("::aaaa:bbbb:cccc/abcd:abcd:dcba:aaaa:bbbb:cccc::dddd", "::8888:0:cccc", "::8888:0:cccc", []interface{}{0, 0x8888, 0, 0xcccc}) t.testSubnetStringRange2("aaaa:bbbb::cccc/abcd:dcba:aaaa:bbbb:cccc::dddd", "aa88:98ba::cccc", "aa88:98ba::cccc", []interface{}{0xaa88, 0x98ba, 0, 0xcccc}) t.testSubnetStringRange2("aaaa:bbbb::/abcd:dcba:aaaa:bbbb:cccc::dddd", "aa88:98ba::", "aa88:98ba::", []interface{}{0xaa88, 0x98ba, 0}) t.testSubnetStringRange1("3.3.3.3/175.80.81.83", "3.0.1.3", "3.0.1.3", []interface{}{3, 0, 1, 3}, nil, true) t.testAllocator([]string{"192.168.10.0/24"}, []uint64{50, 30, 20, 2, 2, 2}, 2, []struct { count int addr string }{ { count: 50, addr: "192.168.10.0/26", }, { count: 30, addr: "192.168.10.64/27", }, { count: 20, addr: "192.168.10.96/27", }, { count: 2, addr: "192.168.10.128/30", }, { count: 2, addr: "192.168.10.132/30", }, { count: 2, addr: "192.168.10.136/30", }, }) t.testAllocator([]string{"192.168.10.0/24"}, []uint64{60, 12, 12, 28}, 2, []struct { count int addr string }{ { count: 60, addr: "192.168.10.0/26", }, { count: 28, addr: "192.168.10.64/27", }, { count: 12, addr: "192.168.10.96/28", }, { count: 12, addr: "192.168.10.112/28", }, }) t.testAllocator([]string{"192.168.10.0/24"}, []uint64{60, 12, 12, 28}, -10, []struct { count int addr string }{ { count: 60, addr: "192.168.10.0/26", }, { count: 28, addr: " 192.168.10.64/27", }, { count: 12, addr: "192.168.10.96/31", }, { count: 12, addr: " 192.168.10.98/31", }, }) t.testAllocator([]string{"192.168.10.0/24"}, []uint64{60, 12, 12, 28}, -15, []struct { count int addr string }{ { count: 60, addr: "192.168.10.0/26", }, { count: 28, addr: "192.168.10.64/28", }, }) t.testAllocator([]string{"192.168.10.0/24"}, []uint64{60, 12, 12, 28}, -30, []struct { count int addr string }{ { count: 60, addr: "192.168.10.0/27", }, }) t.testAllocator([]string{"192.168.10.0/24"}, []uint64{60, 12, 12, 28}, -60, []struct { count int addr string }(nil)) t.testAllocator([]string{"1::/64"}, []uint64{17, 3, 12, 4, 50}, 1, []struct { count int addr string }{ { count: 50, addr: "1::/122", }, { count: 17, addr: "1::40/123", }, { count: 12, addr: "1::60/124", }, { count: 4, addr: "1::70/125", }, { count: 3, addr: "1::78/126", }, }) t.testAllocatorLen([]string{"192.168.10.0/24"}, []ipaddr.BitCount{5, 5, 2, 6, 2, 2}, []struct { count int addr string }{ { count: 64, addr: "192.168.10.0/26", }, { count: 32, addr: "192.168.10.64/27", }, { count: 32, addr: "192.168.10.96/27", }, { count: 4, addr: "192.168.10.128/30", }, { count: 4, addr: "192.168.10.132/30", }, { count: 4, addr: "192.168.10.136/30", }, }) t.testAllocatorLen([]string{"192.168.10.0/24"}, []ipaddr.BitCount{6, 5, 4, 4}, []struct { count int addr string }{ { count: 64, addr: "192.168.10.0/26", }, { count: 32, addr: "192.168.10.64/27", }, { count: 16, addr: "192.168.10.96/28", }, {count: 16, addr: "192.168.10.112/28", }, }) t.testAllocatorLen([]string{"192.168.10.0/24"}, []ipaddr.BitCount{1, 1, 5, 6}, []struct { count int addr string }{ { count: 64, addr: "192.168.10.0/26", }, { count: 32, addr: " 192.168.10.64/27", }, { count: 2, addr: "192.168.10.96/31", }, { count: 2, addr: " 192.168.10.98/31", }, }) t.testAllocatorLen([]string{"192.168.10.0/24"}, []ipaddr.BitCount{6, 4}, []struct { count int addr string }{ { count: 64, addr: "192.168.10.0/26", }, { count: 16, addr: "192.168.10.64/28", }, }) t.testAllocatorLen([]string{"192.168.10.0/24"}, []ipaddr.BitCount{5}, []struct { count int addr string }{ { count: 32, addr: "192.168.10.0/27", }, }) t.testAllocatorLen([]string{"192.168.10.0/24"}, []ipaddr.BitCount{}, []struct { count int addr string }(nil)) t.testAllocatorLen([]string{"1::/64"}, []int{6, 4, 2, 3, 5}, []struct { count int addr string }{ { count: 64, addr: "1::/122", }, { count: 32, addr: "1::40/123", }, { count: 16, addr: "1::60/124", }, { count: 8, addr: "1::70/125", }, { count: 4, addr: "1::78/126", }, }) } func one28() *big.Int { sixty4 := new(big.Int).SetUint64(0xffffffffffffffff) sixtyFour := new(big.Int).Set(sixty4) sixty4.Or(sixtyFour.Lsh(sixtyFour, 64), sixty4) return sixty4 } func (t ipAddressTester) testAllocatorLen(blocksStrs []string, bitLengths []ipaddr.BitCount, expected []struct { count int addr string }) { var blocks []*ipaddr.IPAddress for _, str := range blocksStrs { blocks = append(blocks, t.createAddress(str).GetAddress()) } testAllocatorLen(t, blocks, bitLengths, expected) if blocks[0].GetIPVersion().IsIPv4() { blocks4 := cloneToIPv4Addrs(blocks) testAllocatorLen(t, blocks4, bitLengths, expected) } else if blocks[0].GetIPVersion().IsIPv6() { blocks6 := cloneToIPv6Addrs(blocks) testAllocatorLen(t, blocks6, bitLengths, expected) } } // PrefixBlockAllocator[T PrefixBlockConstraint[T]] func testAllocatorLen[T ipaddr.PrefixBlockConstraint[T]](t ipAddressTester, blocks []T, bitLengths []ipaddr.BitCount, expected []struct { count int addr string }) { alloc := ipaddr.PrefixBlockAllocator[T]{} alloc.AddAvailable(blocks...) //alloc.SetReserved(reservedCount) allocatedBlocks := alloc.AllocateMultiBitLens(bitLengths...) //fmt.Println("allocated are: ", allocatedBlocks) for i, ab := range allocatedBlocks { if len(expected) <= i { continue // note we will fail on the length check below } expectedAddr := t.createAddress(expected[i].addr).GetAddress() if !ab.GetAddress().Equal(expectedAddr) { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetAddress(), " with expected address ", expectedAddr), expectedAddr)) } if ab.GetSize().Cmp(big.NewInt(int64(expected[i].count))) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with expected count ", expected[i].count), expectedAddr)) } if ab.GetSize().Cmp(ab.GetCount()) > 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with count ", ab.GetCount()), expectedAddr)) } //if reservedCount <= 0 && ab.GetSize().Cmp(ab.GetCount()) < 0 { // t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with count 2 ", ab.GetCount()), expectedAddr)) //} if expectedAddr.GetCount().Cmp(ab.GetCount()) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with count ", ab.GetCount()), expectedAddr)) } if ab.GetReservedCount() != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetReservedCount(), " with expected reserved count ", 0), expectedAddr)) } } if len(allocatedBlocks) != len(expected) { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch blocks length: ", len(allocatedBlocks), " with ", len(expected)), nil)) } // put em back and see what happens for _, allocated := range allocatedBlocks { alloc.AddAvailable(allocated.GetAddress()) } if !ipaddr.AddrsMatchUnordered(blocks, alloc.GetAvailable()) { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch blocks: ", blocks, " with ", alloc.GetAvailable()), nil)) } t.incrementTestCount() } func (t ipAddressTester) testAllocator(blocksStrs []string, sizes []uint64, reservedCount int, expected []struct { count int addr string }) { var blocks []*ipaddr.IPAddress for _, str := range blocksStrs { blocks = append(blocks, t.createAddress(str).GetAddress()) } alloc := ipaddr.IPPrefixBlockAllocator{} alloc.AddAvailable(blocks...) alloc.SetReserved(reservedCount) allocatedBlocks := alloc.AllocateSizes(sizes...) //fmt.Println("allocated are: ", allocatedBlocks) for i, ab := range allocatedBlocks { if len(expected) <= i { continue // note we will fail on the length check below } expectedAddr := t.createAddress(expected[i].addr).GetAddress() if !ab.GetAddress().Equal(expectedAddr) { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetAddress(), " with expected address ", expectedAddr), expectedAddr)) } if ab.GetSize().Cmp(big.NewInt(int64(expected[i].count))) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with expected count ", expected[i].count), expectedAddr)) } if reservedCount >= 0 && ab.GetSize().Cmp(ab.GetCount()) > 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with count ", ab.GetCount()), expectedAddr)) } //if reservedCount <= 0 && ab.GetSize().Cmp(ab.GetCount()) < 0 { // t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with count 2 ", ab.GetCount()), expectedAddr)) //} if expectedAddr.GetCount().Cmp(ab.GetCount()) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with count ", ab.GetCount()), expectedAddr)) } if ab.GetReservedCount() != reservedCount { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetReservedCount(), " with expected reserved count ", reservedCount), expectedAddr)) } } if len(allocatedBlocks) != len(expected) { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch blocks length: ", len(allocatedBlocks), " with ", len(expected)), nil)) } // put em back and see what happens for _, allocated := range allocatedBlocks { alloc.AddAvailable(allocated.GetAddress()) } if !ipaddr.AddrsMatchUnordered(blocks, alloc.GetAvailable()) { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch blocks: ", blocks, " with ", alloc.GetAvailable()), nil)) } t.incrementTestCount() if alloc.GetVersion().IsIPv4() { t.testIPv4Allocator(blocksStrs, sizes, reservedCount, expected) } else if alloc.GetVersion().IsIPv6() { t.testIPv6Allocator(blocksStrs, sizes, reservedCount, expected) } } func (t ipAddressTester) testIPv4Allocator(blocksStrs []string, sizes []uint64, reservedCount int, expected []struct { count int addr string }) { var blocks []*ipaddr.IPv4Address for _, str := range blocksStrs { blocks = append(blocks, t.createAddress(str).GetAddress().ToIPv4()) } alloc := ipaddr.IPv4PrefixBlockAllocator{} alloc.AddAvailable(blocks...) alloc.SetReserved(reservedCount) allocatedBlocks := alloc.AllocateSizes(sizes...) //fmt.Println("allocated are: ", allocatedBlocks) for i, ab := range allocatedBlocks { if len(expected) <= i { continue // note we will fail on the length check below } expectedAddr := t.createAddress(expected[i].addr).GetAddress() if !ab.GetAddress().Equal(expectedAddr) { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetAddress(), " with expected address ", expectedAddr), expectedAddr)) } if ab.GetSize().Cmp(big.NewInt(int64(expected[i].count))) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with expected count ", expected[i].count), expectedAddr)) } if reservedCount >= 0 && ab.GetSize().Cmp(ab.GetCount()) > 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with count ", ab.GetCount()), expectedAddr)) } //if reservedCount <= 0 && ab.GetSize().Cmp(ab.GetCount()) < 0 { // t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with count 2 ", ab.GetCount()), expectedAddr)) //} if expectedAddr.GetCount().Cmp(ab.GetCount()) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with count ", ab.GetCount()), expectedAddr)) } if ab.GetReservedCount() != reservedCount { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetReservedCount(), " with expected reserved count ", reservedCount), expectedAddr)) } } if len(allocatedBlocks) != len(expected) { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch blocks length: ", len(allocatedBlocks), " with ", len(expected)), nil)) } //fmt.Println(allocatedBlocks) //fmt.Println(alloc) // put em back and see what happens for _, allocated := range allocatedBlocks { alloc.AddAvailable(allocated.GetAddress()) //fmt.Println(alloc) } if !ipaddr.AddrsMatchUnordered(cloneIPv4AddrsToIPAddrs(blocks), cloneIPv4AddrsToIPAddrs(alloc.GetAvailable())) { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch blocks: ", blocks, " with ", alloc.GetAvailable()), nil)) } t.incrementTestCount() } func (t ipAddressTester) testIPv6Allocator(blocksStrs []string, sizes []uint64, reservedCount int, expected []struct { count int addr string }) { var blocks []*ipaddr.IPv6Address for _, str := range blocksStrs { blocks = append(blocks, t.createAddress(str).GetAddress().ToIPv6()) } alloc := ipaddr.IPv6PrefixBlockAllocator{} alloc.AddAvailable(blocks...) alloc.SetReserved(reservedCount) allocatedBlocks := alloc.AllocateSizes(sizes...) //fmt.Println("allocated are: ", allocatedBlocks) for i, ab := range allocatedBlocks { if len(expected) <= i { continue // note we will fail on the length check below } expectedAddr := t.createAddress(expected[i].addr).GetAddress() if !ab.GetAddress().Equal(expectedAddr) { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetAddress(), " with expected address ", expectedAddr), expectedAddr)) } if ab.GetSize().Cmp(big.NewInt(int64(expected[i].count))) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with expected count ", expected[i].count), expectedAddr)) } if reservedCount >= 0 && ab.GetSize().Cmp(ab.GetCount()) > 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with count ", ab.GetCount()), expectedAddr)) } //if reservedCount <= 0 && ab.GetSize().Cmp(ab.GetCount()) < 0 { // t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with count 2 ", ab.GetCount()), expectedAddr)) //} if expectedAddr.GetCount().Cmp(ab.GetCount()) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetSize(), " with count ", ab.GetCount()), expectedAddr)) } if ab.GetReservedCount() != reservedCount { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch: ", ab.GetReservedCount(), " with expected reserved count ", reservedCount), expectedAddr)) } } if len(allocatedBlocks) != len(expected) { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch blocks length: ", len(allocatedBlocks), " with ", len(expected)), nil)) } // put em back and see what happens for _, allocated := range allocatedBlocks { alloc.AddAvailable(allocated.GetAddress()) } if !ipaddr.AddrsMatchUnordered(cloneIPv6AddrsToIPAddrs(blocks), cloneIPv6AddrsToIPAddrs(alloc.GetAvailable())) { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch blocks: ", blocks, " with ", alloc.GetAvailable()), nil)) } t.incrementTestCount() } func cloneIPv4AddrsToIPAddrs(orig []*ipaddr.IPv4Address) []*ipaddr.IPAddress { result := make([]*ipaddr.IPAddress, len(orig)) for i := range orig { result[i] = orig[i].ToIP() } return result } func cloneIPv6AddrsToIPAddrs(orig []*ipaddr.IPv6Address) []*ipaddr.IPAddress { result := make([]*ipaddr.IPAddress, len(orig)) for i := range orig { result[i] = orig[i].ToIP() } return result } func cloneToIPv4Addrs(orig []*ipaddr.IPAddress) []*ipaddr.IPv4Address { return cloneTo(orig, func(a *ipaddr.IPAddress) *ipaddr.IPv4Address { return a.ToIPv4() }) } func cloneToIPv6Addrs(orig []*ipaddr.IPAddress) []*ipaddr.IPv6Address { return cloneTo(orig, func(a *ipaddr.IPAddress) *ipaddr.IPv6Address { return a.ToIPv6() }) } func cloneTo[T any, U any](orig []T, conv func(T) U) []U { result := make([]U, len(orig)) for i := range orig { result[i] = conv(orig[i]) } return result } func (t ipAddressTester) testLargeDivs(bs [][]byte) { var divList []*ipaddr.IPAddressLargeDivision byteTotal := 0 for _, b := range bs { byteTotal += len(b) divList = append(divList, ipaddr.NewIPAddressLargeDivision(b, len(b)<<3, 16)) } grouping := ipaddr.NewIPAddressLargeDivGrouping(divList) bytes1 := make([]byte, byteTotal) var bytes2, bytes3, bytes4, bytes5, bytes6, bytes7, bytes8 []byte byteTotal = 0 for _, b := range bs { copy(bytes1[byteTotal:], b) byteTotal += len(b) } bytes2 = grouping.Bytes() bytes3 = make([]byte, byteTotal) grouping.CopyBytes(bytes3) bytes4 = grouping.UpperBytes() bytes5 = make([]byte, 0, byteTotal) bytes5 = grouping.CopyUpperBytes(bytes5) grouping2 := ipaddr.NewIPAddressLargeDivGrouping([]*ipaddr.IPAddressLargeDivision{ipaddr.NewIPAddressLargeDivision(bytes5, len(bytes5)<<3, 16)}) bytes6 = grouping2.Bytes() bytes7 = grouping.Bytes() bytes8 = grouping2.UpperBytes() if bytes.Compare(bytes1, bytes2) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch bytes: ", bytes1, " with ", bytes2), grouping)) } else if bytes.Compare(bytes1, bytes3) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch bytes: ", bytes1, " with ", bytes3), grouping)) } else if bytes.Compare(bytes1, bytes4) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch bytes: ", bytes1, " with ", bytes4), grouping)) } else if bytes.Compare(bytes1, bytes5) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch bytes: ", bytes1, " with ", bytes5), grouping)) } else if bytes.Compare(bytes1, bytes6) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch bytes: ", bytes1, " with ", bytes6), grouping)) } else if bytes.Compare(bytes1, bytes7) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch bytes: ", bytes1, " with ", bytes7), grouping)) } else if bytes.Compare(bytes1, bytes8) != 0 { t.addFailure(newAddressItemFailure(fmt.Sprint("mismatch bytes: ", bytes1, " with ", bytes8), grouping)) } if (len(bs) == 1 && grouping.Compare(grouping2) != 0) || (len(bs) != 1 && grouping.Compare(grouping2) == 0) { t.addFailure(newAddressItemFailure(fmt.Sprint("match grouping", grouping, " with ", grouping2), grouping)) } t.incrementTestCount() } func (t ipAddressTester) testEquivalentPrefix(host string, prefix ipaddr.BitCount) { t.testEquivalentMinPrefix(host, cacheTestBits(prefix), prefix) } func (t ipAddressTester) testEquivalentMinPrefix(host string, equivPrefix ipaddr.PrefixLen, minPrefix ipaddr.BitCount) { str := t.createAddress(host) h1, err := str.ToAddress() if err != nil { t.addFailure(newFailure("failed "+err.Error(), str)) return } equiv := h1.GetPrefixLenForSingleBlock() if !equivPrefix.Equal(equiv) { t.addFailure(newIPAddrFailure("failed: prefix expected: "+equivPrefix.String()+" prefix got: "+equiv.String(), h1)) equiv = h1.GetPrefixLenForSingleBlock() } else { prefixed := h1.AssignPrefixForSingleBlock() bareHost := host index := strings.Index(host, "/") if index >= 0 { bareHost = host[:index] } direct := t.createAddress(bareHost + "/" + equivPrefix.String()) directAddress := direct.GetAddress() if equivPrefix != nil && h1.IsPrefixed() && h1.IsPrefixBlock() { directAddress = makePrefixSubnet(directAddress) } var isFailed bool if equiv == nil { isFailed = prefixed != nil } else { //fmt.Printf("%v %v %v\n", direct, directAddress.ToNormalizedWildcardString(), prefixed.ToNormalizedWildcardString()) isFailed = !directAddress.Equal(prefixed) // prefixed is prefix block, directAddress is not } if isFailed { t.addFailure(newIPAddrFailure("failed: prefix expected: "+direct.String(), prefixed)) } else { minPref := h1.GetMinPrefixLenForBlock() if minPref != minPrefix { t.addFailure(newIPAddrFailure("failed: prefix expected: "+bitCountToString(minPrefix)+" prefix got: "+bitCountToString(minPref), h1)) } else { minPrefixed := h1.AssignMinPrefixForBlock() bareHost := host index := strings.Index(host, "/") if index >= 0 { bareHost = host[:index] } direct = t.createAddress(bareHost + "/" + bitCountToString(minPrefix)) directAddress = direct.GetAddress() if h1.IsPrefixed() && h1.IsPrefixBlock() { directAddress = makePrefixSubnet(directAddress) } if !directAddress.Equal(minPrefixed) { // orig "1:2:*::/64" failed: expected match between: 1:2:*::*:*:*/64 and 1:2:*::/64 t.addFailure(newIPAddrFailure("failed: expected match between: "+directAddress.String()+" and "+minPrefixed.String(), minPrefixed)) } } } } t.incrementTestCount() } func (t ipAddressTester) testSubnet(addressStr, maskStr string, prefix ipaddr.BitCount, normalizedPrefixSubnetString, normalizedSubnetString, normalizedPrefixString string) { t.testHostAddress(addressStr) isValidMask := normalizedSubnetString != "" str := t.createAddress(addressStr) maskString := t.createAddress(maskStr) value := str.GetAddress() originalPrefix := value.GetNetworkPrefixLen() mask := maskString.GetAddress() var subnet3 *ipaddr.IPAddress if originalPrefix == nil || originalPrefix.Len() > prefix { var perr error subnet3, perr = value.SetPrefixLenZeroed(prefix) if perr != nil { t.addFailure(newIPAddrFailure("testSubnet failed setting prefix "+bitCountToString(prefix)+" to: "+value.String()+" error: "+perr.Error(), subnet3)) } } else { subnet3 = value } string3 := subnet3.ToNormalizedString() if string3 != normalizedPrefixString { t.addFailure(newIPAddrFailure("testSubnet failed normalizedPrefixString: "+string3+" expected: "+normalizedPrefixString, subnet3)) } else { subnet2, err := value.Mask(mask) //here? if isValidMask && err != nil { t.addFailure(newIPAddrFailure("testSubnet errored with mask "+mask.String(), value)) } else if !isValidMask && err == nil { t.addFailure(newIPAddrFailure("testSubnet failed to error with mask "+mask.String(), value)) } else if isValidMask { subnet2 = subnet2.WithoutPrefixLen() string2 := subnet2.ToNormalizedString() if string2 != normalizedSubnetString { t.addFailure(newIPAddrFailure("testSubnet failed: "+string2+" expected: "+normalizedSubnetString, subnet2)) } else { if subnet2.GetNetworkPrefixLen() != nil { t.addFailure(newIPAddrFailure("testSubnet failed, expected nil prefix, got: "+subnet2.GetNetworkPrefixLen().String(), subnet2)) } else { subnet4, err := value.Mask(mask) //1.2.0.0/15 masked with 0.0.255.255, does this result in full host or not? previously I had it that way, but now I wonder why if err != nil { t.addFailure(newIPAddrFailure("testSubnet errored with mask "+mask.String(), value)) } if !subnet4.GetNetworkPrefixLen().Equal(originalPrefix) { t.addFailure(newIPAddrFailure("testSubnet failed, expected "+originalPrefix.String()+" prefix, got: "+subnet4.GetNetworkPrefixLen().String(), subnet2)) } else { if originalPrefix != nil { //the prefix will be different, but the addresses will be the same, except for full subnets //IPAddress addr = subnet2.setPrefixLength(originalPrefix, false);//0.0.*.* set to have prefix 15 addr := subnet2.SetPrefixLen(originalPrefix.Len()) //0.0.*.* set to have prefix 15 if !subnet4.Equal(addr) { t.addFailure(newIPAddrFailure("testSubnet failed: "+subnet4.String()+" expected: "+addr.String(), subnet4)) //subnet2.SetPrefixLen(originalPrefix); //addr second div 0-1, subnet4 second div 0-0 } } else { if !subnet4.Equal(subnet2) { t.addFailure(newIPAddrFailure("testSubnet failed: "+subnet4.String()+" expected: "+subnet2.String(), subnet4)) } } } } } } } t.incrementTestCount() } func (t ipAddressTester) testHostAddress(addressStr string) { str := t.createAddress(addressStr) address := str.GetAddress() if address != nil { hostAddress := str.GetHostAddress() prefixIndex := strings.Index(addressStr, ipaddr.PrefixLenSeparatorStr) if prefixIndex < 0 { if !address.Equal(hostAddress) || !address.Contains(hostAddress) { t.addFailure(newFailure("failed host address with no prefix: "+hostAddress.String()+" expected: "+address.String(), str)) } } else { substr := addressStr[:prefixIndex] str2 := t.createAddress(substr) address2 := str2.GetAddress() if !address2.Equal(hostAddress) { t.addFailure(newFailure("failed host address: "+hostAddress.String()+" expected: "+address2.String(), str)) } } } } func (t ipAddressTester) testReverse(addressStr string, bitsReversedIsSame, bitsReversedPerByteIsSame bool) { str := t.createAddress(addressStr) addr := str.GetAddress() t.testBase.testReverse(addr.ToAddressBase().Wrap(), bitsReversedIsSame, bitsReversedPerByteIsSame) t.incrementTestCount() } func (t ipAddressTester) testPrefixes( orig string, prefix, adjustment ipaddr.BitCount, next, previous, adjusted, prefixSet, prefixApplied string) { original := t.createAddress(orig).GetAddress() if original.IsPrefixed() { removed := original.WithoutPrefixLen() for i := 0; i < removed.GetSegmentCount(); i++ { if !removed.GetSegment(i).Equal(original.GetSegment(i)) { t.addFailure(newIPAddrFailure("removed prefix: "+removed.String(), original)) break } } } t.testBase.testPrefixes(original.Wrap(), prefix, adjustment, t.createAddress(next).GetAddress().Wrap(), t.createAddress(previous).GetAddress().Wrap(), t.createAddress(adjusted).GetAddress().Wrap(), t.createAddress(prefixSet).GetAddress().Wrap(), t.createAddress(prefixApplied).GetAddress().Wrap()) t.incrementTestCount() } func (t ipAddressTester) testBitwiseOr(orig string, prefixAdjustment *ipaddr.BitCount, or, expectedResult string) { original := t.createAddress(orig).GetAddress() orAddr := t.createAddress(or).GetAddress() if prefixAdjustment != nil { var err error original, err = original.AdjustPrefixLenZeroed(*prefixAdjustment) if err != nil { t.addFailure(newIPAddrFailure("adjusted prefix error: "+err.Error(), original)) return } } result, err := original.BitwiseOr(orAddr) if err != nil { if expectedResult != "" { t.addFailure(newIPAddrFailure("ored errored unexpectedly, "+original.String()+" orAddr: "+orAddr.String()+" "+err.Error(), original)) } } else { if expectedResult == "" { t.addFailure(newIPAddrFailure("ored expected error, "+original.String()+" orAddr: "+orAddr.String()+" result: "+result.String(), original)) } else { expectedResultAddr := t.createAddress(expectedResult).GetAddress() if !expectedResultAddr.Equal(result) { t.addFailure(newIPAddrFailure("ored expected: "+expectedResultAddr.String()+" actual: "+result.String(), original)) } if !result.GetPrefixLen().Equal(original.GetPrefixLen()) { t.addFailure(newIPAddrFailure("ored expected nil prefix: "+expectedResultAddr.String()+" actual: "+result.GetPrefixLen().String(), original)) } } } t.incrementTestCount() } func (t ipAddressTester) testPrefixBitwiseOr(orig string, prefix ipaddr.BitCount, or, expectedNetworkResult, expectedFullResult string) { original := t.createAddress(orig).GetAddress() orAddr := t.createAddress(or).GetAddress() result, err := original.BitwiseOr(orAddr) if err != nil { if expectedFullResult != "" { t.addFailure(newIPAddrFailure("ored errored unexpectedly "+original.String()+" orAddr: "+orAddr.String()+" "+err.Error(), original)) } } else { if expectedFullResult == "" { t.addFailure(newIPAddrFailure("ored expected error, "+original.String()+" orAddr: "+orAddr.String()+" result: "+result.String(), original)) } else { expected := t.createAddress(expectedFullResult) expectedResultAddr := expected.GetAddress() if !expectedResultAddr.Equal(result) || !expectedResultAddr.GetPrefixLen().Equal(result.GetPrefixLen()) { //result, _ = original.BitwiseOr(orAddr); t.addFailure(newIPAddrFailure("ored expected: "+expectedResultAddr.String()+" actual: "+result.String(), original)) } } } t.incrementTestCount() } func (t ipAddressTester) testDelimitedCount(str string, expectedCount int) { delims := ipaddr.DelimitedAddressString(str) strs := delims.ParseDelimitedSegments() var set []*ipaddr.IPAddress count := 0 for strs.HasNext() { set = append(set, t.createAddress(strs.Next()).GetAddress()) count++ } if count != expectedCount || len(set) != count || count != delims.CountDelimitedAddresses() { t.addFailure(newFailure("count mismatch, count: "+strconv.Itoa(count)+" set count: "+strconv.Itoa(len(set))+" calculated count: "+strconv.Itoa(delims.CountDelimitedAddresses())+" expected: "+strconv.Itoa(expectedCount), nil)) } t.incrementTestCount() } func (t ipAddressTester) testMatches(matches bool, host1Str, host2Str string) { t.testMatchesInetAton(matches, host1Str, host2Str, false) } func (t ipAddressTester) testMatchesInetAton(matches bool, host1Str, host2Str string, inet_aton bool) { var h1, h2 *ipaddr.IPAddressString if inet_aton { h1 = t.createInetAtonAddress(host1Str) h2 = t.createInetAtonAddress(host2Str) } else { h1 = t.createAddress(host1Str) h2 = t.createAddress(host2Str) } straightMatch := h1.Equal(h2) if matches != straightMatch && matches != conversionMatches(h1, h2) { t.addFailure(newFailure("failed: matching "+h1.String()+" with "+h2.String(), h1)) } else { if matches != h2.Equal(h1) && matches != conversionMatches(h2, h1) { t.addFailure(newFailure("failed: match with "+h1.String(), h2)) } else { var failed bool if matches { failed = h1.Compare(h2) != 0 && conversionCompare(h1, h2) != 0 } else { failed = h1.Compare(h2) == 0 } if failed { t.addFailure(newFailure("failed: matching "+h1.String()+" with "+h2.String(), h2)) } else { if matches { failed = h2.Compare(h1) != 0 && conversionCompare(h2, h1) != 0 } else { failed = h2.Compare(h1) == 0 } if failed { t.addFailure(newFailure("failed: match with "+h2.String(), h1)) } else if straightMatch { if h1.GetNetworkPrefixLen() != nil { if !h1.PrefixEqual(h2) { t.addFailure(newFailure("failed: prefix match fail with "+h1.String(), h2)) } else { //this three step test is done so we try it before validation, and then try again before address creation, due to optimizations in IPAddressString if inet_aton { h1 = t.createInetAtonAddress(host1Str) h2 = t.createInetAtonAddress(host2Str) } else { h1 = t.createAddress(host1Str) h2 = t.createAddress(host2Str) } if !h1.PrefixEqual(h2) { t.addFailure(newFailure("failed: prefix match fail with "+h1.String(), h2)) } h1.IsValid() h2.IsValid() if !h1.PrefixEqual(h2) { t.addFailure(newFailure("failed: 2 prefix match fail with "+h1.String(), h2)) } h1.GetAddress() h2.GetAddress() if !h1.PrefixEqual(h2) { t.addFailure(newFailure("failed: 3 prefix match fail with "+h1.String(), h2)) } } } } } } } t.incrementTestCount() } func (t ipAddressTester) ipv4_inet_aton_test(pass bool, x string) { addr := t.createInetAtonAddress(x) t.iptest(pass, addr, false, false, true) } func (t ipAddressTester) ipv4test(pass bool, x string) { addr := t.createAddress(x) t.iptest(pass, addr, false, false, true) } func (t ipAddressTester) ipv4test2(pass bool, x string, isZero, notBothTheSame bool) { addr := t.createAddress(x) t.iptest(pass, addr, isZero, notBothTheSame, true) } func (t ipAddressTester) ipv4testOnly(pass bool, x string) { addr := t.createAddress(x) t.iptest(pass, addr, false, true, true) } func (t ipAddressTester) ipv4zerotest(pass bool, x string) { addr := t.createAddress(x) t.iptest(pass, addr, true, false, true) } func (t ipAddressTester) ipv6test(pass bool, x string) { addr := t.createAddress(x) t.iptest(pass, addr, false, false, false) } func (t ipAddressTester) ipv6test2(pass bool, x string, isZero, notBothTheSame bool) { addr := t.createAddress(x) t.iptest(pass, addr, isZero, notBothTheSame, false) } func (t ipAddressTester) ipv6testOnly(pass bool, x string) { addr := t.createAddress(x) t.iptest(pass, addr, false, true, false) } func (t ipAddressTester) ipv6zerotest(pass bool, x string) { addr := t.createAddress(x) t.iptest(pass, addr, true, false, false) } func (t ipAddressTester) iptest(pass bool, addr *ipaddr.IPAddressString, isZero, notBothTheSame, ipv4Test bool) bool { failed := false var pass2 bool if notBothTheSame { pass2 = !pass } else { pass2 = pass } //notBoth means we validate as IPv4 or as IPv6, we don't validate as either one if t.isNotExpected(pass, addr, ipv4Test, !ipv4Test) || t.isNotExpected(pass2, addr, false, false) { failed = true if addr.GetAddress() != nil { t.addFailure(newFailure("parse failure for "+addr.String()+" parsed to "+addr.GetAddress().String(), addr)) } else { t.addFailure(newFailure("parse failure for "+addr.String(), addr)) } ////this part just for debugging if t.isNotExpected(pass, addr, ipv4Test, !ipv4Test) { t.isNotExpected(pass, addr, ipv4Test, !ipv4Test) } else { t.isNotExpected(pass2, addr, false, false) } } else { var zeroPass bool if notBothTheSame { zeroPass = !isZero } else { zeroPass = pass && !isZero } if t.isNotExpectedNonZero(zeroPass, addr) { failed = true t.addFailure(newFailure("zero parse failure", addr)) //this part just for debugging //boolean val = isNotExpectedNonZero(zeroPass, addr); t.isNotExpectedNonZero(zeroPass, addr) } else { //test the bytes if pass && len(addr.String()) > 0 && addr.GetAddress() != nil && !(addr.GetAddress().IsIPv6() && addr.GetAddress().ToIPv6().HasZone()) && !addr.IsPrefixed() { //only for valid addresses address := addr.GetAddress() failed = !t.testBytes(address) } } } t.incrementTestCount() return !failed } func (t ipAddressTester) isNotExpected(expectedPass bool, addr *ipaddr.IPAddressString, isIPv4, isIPv6 bool) bool { var err error if isIPv4 { err = addr.ValidateIPv4() if err == nil { _, err = addr.ToVersionedAddress(ipaddr.IPv4) } } else if isIPv6 { err = addr.ValidateIPv6() if err == nil { _, err = addr.ToVersionedAddress(ipaddr.IPv6) } } else { err = addr.Validate() } if err != nil { return expectedPass } return !expectedPass } func (t ipAddressTester) isNotExpectedNonZero(expectedPass bool, addr *ipaddr.IPAddressString) bool { if !addr.IsValid() && !addr.IsAllAddresses() { // //if(!addr.isIPAddress() && !addr.isPrefixOnly() && !addr.isAllAddresses()) { return expectedPass } //if expectedPass is true, we are expecting a non-zero address //return true to indicate we have gotten something not expected if addr.GetAddress() != nil && addr.GetAddress().IsZero() { return expectedPass } return !expectedPass } func (t ipAddressTester) testBytes(addr *ipaddr.IPAddress) bool { failed := false if t.allowsRange() && addr.IsMultiple() { b := addr.Bytes() b2 := addr.GetLower().Bytes() if !bytes.Equal(b, b2) { t.addFailure(newIPAddrFailure("bytes on addr "+addr.String(), addr.ToIP())) failed = true } bytesToUse := make([]byte, ipaddr.IPv6ByteCount) b2 = addr.GetLower().CopyNetIP(bytesToUse) if !bytes.Equal(b, b2) { t.addFailure(newIPAddrFailure("bytes on addr "+addr.String(), addr.ToIP())) failed = true } return !failed } addrString := addr.String() index := strings.Index(addrString, "/") if index >= 0 { addrString = addrString[:index] //addrString = addrString.substring(0, index); } inetAddress := net.ParseIP(addrString) if addr.IsIPv4() { inetAddress = inetAddress.To4() } b2 := addr.Bytes() if !bytes.Equal(inetAddress, b2) { t.addFailure(newIPAddrFailure("bytes on addr "+inetAddress.String(), addr)) } bytesToUse := make([]byte, ipaddr.IPv6ByteCount) b4 := addr.CopyBytes(bytesToUse) if !bytes.Equal(inetAddress, b4) { t.addFailure(newIPAddrFailure("bytes on addr "+inetAddress.String(), addr)) } bytesToUse = make([]byte, ipaddr.IPv6ByteCount) b4 = addr.CopyNetIP(bytesToUse) if !bytes.Equal(inetAddress, b4) { t.addFailure(newIPAddrFailure("bytes on addr "+inetAddress.String(), addr)) } return !failed } func (t ipAddressTester) testMaskBytes(cidr2 string, w2 *ipaddr.IPAddressString) { if t.allowsRange() { t.testBytes(w2.GetAddress()) return } index := strings.Index(cidr2, "/") if index < 0 { index = len(cidr2) } w3 := t.createAddress(cidr2[:index]) inetAddress := net.ParseIP(w3.String()) if w3.IsIPv4() { inetAddress = inetAddress.To4() } b2 := w3.GetAddress().Bytes() if !bytes.Equal(inetAddress, b2) { t.addFailure(newFailure("bytes on addr "+inetAddress.String(), w3)) } else { b3 := w2.GetAddress().Bytes() if !bytes.Equal(b3, b2) { //if(!Arrays.equals(b3, b2)) { t.addFailure(newFailure("bytes on addr "+w3.String(), w2)) } } } func (t ipAddressTester) testCIDRSubnets(cidr1, normalizedString string) { w := t.createAddress(cidr1) w2 := t.createAddress(normalizedString) first := w.Equal(w2) v, err := w.ToAddress() v2, err2 := w2.ToAddress() if err != nil { t.addFailure(newFailure("testCIDRSubnets addresses "+w.String()+", "+w2.String()+": "+err.Error(), w2)) } if err2 != nil { t.addFailure(newFailure("testCIDRSubnets addresses "+w.String()+", "+w2.String()+": "+err2.Error(), w2)) } second := v.Equal(v2) if !first || !second { t.addFailure(newFailure("failed "+w2.String(), w)) } else { str := v2.ToNormalizedString() if normalizedString != (str) { t.addFailure(newFailure("failed "+str, w2)) } else { t.testMaskBytes(normalizedString, w2) } } t.incrementTestCount() } func (t ipAddressTester) testMasksAndPrefixes() { sampleIpv6 := t.createAddress("1234:abcd:cdef:5678:9abc:def0:1234:5678").GetAddress().ToIPv6() sampleIpv4 := t.createAddress("123.156.178.201").GetAddress().ToIPv4() ipv6Network := ipaddr.IPv6Network ipv6SampleNetMask := sampleIpv6.GetNetworkMask() ipv6SampleHostMask := sampleIpv6.GetHostMask() onesNetworkMask := ipv6Network.GetNetworkMask(ipaddr.IPv6BitCount) onesHostMask := ipv6Network.GetHostMask(0) if !ipv6SampleNetMask.Equal(onesNetworkMask) { t.addFailure(newIPAddrFailure("mask mismatch between address "+ipv6SampleNetMask.String()+" and network "+onesNetworkMask.String(), sampleIpv6.ToIP())) } if !ipv6SampleHostMask.Equal(onesHostMask) { t.addFailure(newIPAddrFailure("mask mismatch between address "+ipv6SampleHostMask.String()+" and network "+onesHostMask.String(), sampleIpv6.ToIP())) } ipv4Network := ipaddr.IPv4Network ipv4SampleNetMask := sampleIpv4.GetNetworkMask() ipv4SampleHostMask := sampleIpv4.GetHostMask() onesNetworkMaskv4 := ipv4Network.GetNetworkMask(ipaddr.IPv4BitCount) onesHostMaskv4 := ipv4Network.GetHostMask(0) if !ipv4SampleNetMask.Equal(onesNetworkMaskv4) { t.addFailure(newIPAddrFailure("mask mismatch between address "+ipv4SampleNetMask.String()+" and network "+onesNetworkMaskv4.String(), sampleIpv4.ToIP())) } if !ipv4SampleHostMask.Equal(onesHostMaskv4) { t.addFailure(newIPAddrFailure("mask mismatch between address "+ipv4SampleHostMask.String()+" and network "+onesHostMaskv4.String(), sampleIpv4.ToIP())) } for i := ipaddr.BitCount(0); i <= ipaddr.IPv6BitCount; i++ { bits := i ipv6HostMask := ipv6Network.GetHostMask(bits).ToIP() if t.checkMask(ipv6HostMask, bits, false, false) { ipv6NetworkMask := ipv6Network.GetPrefixedNetworkMask(bits).ToIP() if t.checkMask(ipv6NetworkMask, bits, true, false) { samplePrefixedIpv6 := sampleIpv6.SetPrefixLen(bits) ipv6NetworkMask2 := samplePrefixedIpv6.GetNetworkMask() ipv6HostMask2 := samplePrefixedIpv6.GetHostMask() if !ipv6NetworkMask2.Equal(ipv6NetworkMask) { t.addFailure(newIPAddrFailure("mask mismatch between address "+ipv6NetworkMask2.String()+" and network "+ipv6NetworkMask.String(), samplePrefixedIpv6.ToIP())) } if !ipv6HostMask2.Equal(ipv6HostMask) { t.addFailure(newIPAddrFailure("mask mismatch between address "+ipv6HostMask2.String()+" and network "+ipv6HostMask.String(), samplePrefixedIpv6.ToIP())) } if i <= ipaddr.IPv4BitCount { ipv4HostMask := ipv4Network.GetHostMask(bits).ToIP() if t.checkMask(ipv4HostMask, bits, false, false) { ipv4NetworkMask := ipv4Network.GetPrefixedNetworkMask(bits).ToIP() t.checkMask(ipv4NetworkMask, bits, true, false) samplePrefixedIpv4 := sampleIpv4.SetPrefixLen(bits) ipv4NetworkMask2 := samplePrefixedIpv4.GetNetworkMask() ipv4HostMask2 := samplePrefixedIpv4.GetHostMask() if !ipv4NetworkMask2.Equal(ipv4NetworkMask) { t.addFailure(newIPAddrFailure("mask mismatch between address "+ipv4NetworkMask2.String()+" and network "+ipv4NetworkMask.String(), samplePrefixedIpv4.ToIP())) } if !ipv4HostMask2.Equal(ipv4HostMask) { t.addFailure(newIPAddrFailure("mask mismatch between address "+ipv4HostMask2.String()+" and network "+ipv4HostMask.String(), samplePrefixedIpv4.ToIP())) } } } } } } } func (t ipAddressTester) checkMask(address *ipaddr.IPAddress, prefixBits ipaddr.BitCount, network bool, secondTry bool) bool { maskPrefix := address.GetBlockMaskPrefixLen(network) otherMaskPrefix := address.GetBlockMaskPrefixLen(!network) // A mask is either network or host, but not both, unless it is all zeros or ones // so this ensures that a network mask is or is not a host mask, and vice versa var other bool if prefixBits == 0 || prefixBits == address.GetBitCount() { other = otherMaskPrefix == nil } else { other = otherMaskPrefix != nil } if maskPrefix.Len() != min(prefixBits, address.GetBitCount()) || other { t.addFailure(newIPAddrFailure("failed mask "+address.String()+" otherMaskPrefix: "+otherMaskPrefix.String(), address)) return false } if network { addr := address if address.IsPrefixBlock() { addr = address.GetLower() } if !addr.IsZeroHostLen(prefixBits) || (addr.IsPrefixed() && !addr.IsZeroHost()) { t.addFailure(newIPAddrFailure(addr.String()+" is zero host failure "+strconv.FormatBool(addr.IsZeroHostLen(prefixBits)), address)) return false } if prefixBits < address.GetBitCount()-1 && !addr.IsZeroHostLen(prefixBits+1) { t.addFailure(newIPAddrFailure(addr.String()+" is zero host failure "+strconv.FormatBool(addr.IsZeroHostLen(prefixBits+1)), address)) return false } if prefixBits > 0 && addr.IsZeroHostLen(prefixBits-1) { t.addFailure(newIPAddrFailure(addr.String()+" is zero host failure "+strconv.FormatBool(addr.IsZeroHostLen(prefixBits-1)), address)) return false } } else { if !address.IncludesMaxHostLen(prefixBits) || (address.IsPrefixed() && !address.IncludesMaxHost()) { t.addFailure(newIPAddrFailure(address.String()+" is zero host failure "+strconv.FormatBool(address.IncludesMaxHostLen(prefixBits)), address)) return false } if prefixBits < address.GetBitCount()-1 && !address.IncludesMaxHostLen(prefixBits+1) { t.addFailure(newIPAddrFailure(address.String()+" is max host failure "+strconv.FormatBool(address.IncludesMaxHostLen(prefixBits+1)), address)) return false } if prefixBits > 0 && address.IncludesMaxHostLen(prefixBits-1) { t.addFailure(newIPAddrFailure(address.String()+" is max host failure "+strconv.FormatBool(address.IncludesMaxHostLen(prefixBits-1)), address)) return false } } //ones := network leadingBits := address.GetLeadingBitCount(network) var trailingBits ipaddr.BitCount if network && address.IsPrefixBlock() { trailingBits = address.GetLower().GetTrailingBitCount(!network) } else { trailingBits = address.GetTrailingBitCount(!network) } if leadingBits != prefixBits { t.addFailure(newIPAddrFailure("leading bits failure, bit counts are leading: "+bitCountToString(leadingBits)+" trailing: "+bitCountToString(trailingBits), address)) return false } if leadingBits+trailingBits != address.GetBitCount() { t.addFailure(newIPAddrFailure("bit counts are leading: "+bitCountToString(leadingBits)+" trailing: "+bitCountToString(trailingBits), address)) return false } if network { originalPrefixStr := "/" + bitCountToString(prefixBits) prefix := t.createAddress(originalPrefixStr) prefixExtra := originalPrefixStr addressWithNoPrefix := address if address.IsPrefixed() { var err error addressWithNoPrefix, err = address.Mask(address.GetNetwork().GetNetworkMask(address.GetPrefixLen().Len())) if err != nil { t.addFailure(newIPAddrFailure("failed mask "+err.Error(), address)) } } ipForNormalizeMask := addressWithNoPrefix.String() maskStrx2 := t.normalizeMask(originalPrefixStr, ipForNormalizeMask) + prefixExtra maskStrx3 := t.normalizeMask(bitCountToString(prefixBits), ipForNormalizeMask) + prefixExtra normalStr := address.ToNormalizedString() if maskStrx2 != normalStr || maskStrx3 != normalStr { t.addFailure(newFailure("failed prefix conversion", prefix)) return false } } t.incrementTestCount() if !secondTry { thebytes := address.Bytes() var another *ipaddr.IPAddress if network { another, _ = ipaddr.NewIPAddressFromPrefixedNetIP(thebytes, cacheTestBits(prefixBits)) } else { another, _ = ipaddr.NewIPAddressFromNetIP(thebytes) if another.IsIPv4() && prefixBits > ipaddr.IPv4BitCount { // ::ffff:ffff:ffff is interpreted as IPv4-mapped and gives the IPv4 address 255.255.255.255, so we flip it back to IPv6 another = ipaddr.DefaultAddressConverter{}.ToIPv6(another).ToIP() } } result := t.checkMask(another, prefixBits, network, true) //now check the prefix in the mask if result { prefixBitsMismatch := false addrPrefixBits := address.GetPrefixLen() if !network { prefixBitsMismatch = addrPrefixBits != nil } else { prefixBitsMismatch = addrPrefixBits == nil || (prefixBits != addrPrefixBits.Len()) } if prefixBitsMismatch { t.addFailure(newIPAddrFailure("prefix incorrect", address)) return false } } } return true } func (t ipAddressTester) normalizeMask(maskString, ipString string) string { if ipString != "" && len(strings.TrimSpace(ipString)) > 0 && maskString != "" && len(strings.TrimSpace(maskString)) > 0 { maskString = strings.TrimSpace(maskString) if strings.HasPrefix(maskString, "/") { maskString = maskString[1:] } addressString := ipaddr.NewIPAddressString(ipString) if addressString.IsValid() { version := addressString.GetIPVersion() prefix, perr := ipaddr.ValidatePrefixLenStr(maskString, version) if perr != nil { t.addFailure(newFailure("prefix string incorrect: "+perr.Error(), addressString)) return "" } maskAddress := addressString.GetAddress().GetNetwork().GetNetworkMask(prefix.Len()) return maskAddress.ToNormalizedString() } } //Note that here I could normalize the mask to be a full one with an else return maskString } func (t ipAddressTester) testNotContains(cidr1, cidr2 string) { t.testNotContainsNoReverse(cidr1, cidr2, false) } func (t ipAddressTester) testNotContainsNoReverse(cidr1, cidr2 string, skipReverse bool) { w := t.createAddress(cidr1).GetAddress() w2 := t.createAddress(cidr2).GetAddress() if w.Contains(w2) { t.addFailure(newIPAddrFailure("failed "+w2.String(), w)) } else if !skipReverse && w2.Contains(w) { t.addFailure(newIPAddrFailure("failed "+w.String(), w2)) } t.testContainsEqual(cidr1, cidr2, false, false) t.incrementTestCount() } func (t ipAddressTester) testContains(cidr1, cidr2 string, equal bool) { t.testContainsEqual(cidr1, cidr2, true, equal) } func (t ipAddressTester) testContainsEqual(cidr1, cidr2 string, result, equal bool) { wstr := t.createAddress(cidr1) w2str := t.createAddress(cidr2) w := wstr.GetAddress() w2 := w2str.GetAddress() needsConversion := !w.GetIPVersion().Equal(w2.GetIPVersion()) firstContains := w.Contains(w2) convCont := false if !firstContains { convCont = conversionContains(w, w2) } if !firstContains && !convCont { if result { t.addFailure(newIPAddrFailure("containment failed "+w2.String(), w)) } } else { if !result && firstContains { t.addFailure(newIPAddrFailure("containment passed "+w2.String(), w)) } else if !result { t.addFailure(newIPAddrFailure("conv containment passed "+w2.String(), w)) } else { if equal { if !(w2.Contains(w) || conversionContains(w2, w)) { t.addFailure(newIPAddrFailure("failed "+w.String(), w2)) } } else { if w2.Contains(w) || conversionContains(w2, w) { t.addFailure(newIPAddrFailure("failed "+w.String(), w2)) } } } } if !convCont { t.testStringContains(result, equal, wstr, w2str) //compare again, this tests the string-based optimization (which is skipped if we validated already) t.testStringContains(result, equal, t.createAddress(cidr1), t.createAddress(cidr2)) } if !needsConversion { wstr = t.createAddress(cidr1) w2str = t.createAddress(cidr2) prefContains := wstr.PrefixContains(w2str) if !prefContains { // if contains, then prefix should also contain other prefix if result { t.addFailure(newIPAddrFailure("str prefix containment failed "+w2.String(), w)) } wstr.IsValid() w2str.IsValid() prefContains = wstr.PrefixContains(w2str) if prefContains { t.addFailure(newIPAddrFailure("str prefix containment failed "+w2.String(), w)) } w = wstr.GetAddress() w2 = w2str.GetAddress() prefContains = wstr.PrefixContains(w2str) if prefContains { t.addFailure(newIPAddrFailure("str prefix containment failed "+w2.String(), w)) } } if !needsConversion { // with explicit subnets strings look like 1.2.*.*/16 // now do testing on the prefix block, allowing us to test prefixContains wstr = t.createAddress(wstr.GetAddress().ToPrefixBlock().String()) w2str = t.createAddress(w2str.GetAddress().ToPrefixBlock().String()) prefContains = wstr.PrefixContains(w2str) wstr.IsValid() w2str.IsValid() prefContains2 := wstr.PrefixContains(w2str) w = wstr.GetAddress() w2 = w2str.GetAddress() origContains := w.Contains(w2) prefContains3 := wstr.PrefixContains(w2str) if !origContains { // if the prefix block does not contain, then prefix should also not contain other prefix if prefContains { t.addFailure(newIPAddrFailure("str prefix containment failed "+w2.String(), w)) } if prefContains2 { t.addFailure(newIPAddrFailure("str prefix containment failed "+w2.String(), w)) } if prefContains3 { t.addFailure(newIPAddrFailure("str prefix containment failed "+w2.String(), w)) } } else { // if contains, then prefix should also contain other prefix if !prefContains { t.addFailure(newIPAddrFailure("str prefix containment failed "+w2.String(), w)) } if !prefContains2 { t.addFailure(newIPAddrFailure("str prefix containment failed "+w2.String(), w)) } if !prefContains3 { t.addFailure(newIPAddrFailure("str prefix containment failed "+w2.String(), w)) } } // again do testing on the prefix block, allowing us to test prefixEquals wstr = t.createAddress(wstr.GetAddress().ToPrefixBlock().String()) w2str = t.createAddress(w2str.GetAddress().ToPrefixBlock().String()) prefEquals := wstr.PrefixEqual(w2str) wstr.IsValid() w2str.IsValid() prefEquals2 := wstr.PrefixEqual(w2str) w = wstr.GetAddress() w2 = w2str.GetAddress() origEquals := w.PrefixEqual(w2) prefEquals3 := wstr.PrefixEqual(w2str) if !origEquals { // if the prefix block does not contain, then prefix should also not contain other prefix if prefEquals { t.addFailure(newIPAddrFailure("str prefix equality failed "+w2.String(), w)) } if prefEquals2 { t.addFailure(newIPAddrFailure("str prefix equality failed "+w2.String(), w)) } if prefEquals3 { t.addFailure(newIPAddrFailure("str prefix equality failed "+w2.String(), w)) } } else { // if prefix blocks are equal, then prefix should also equal other prefix if !prefEquals { fmt.Printf("prefix equals: %v %v\n", w, w2) w.PrefixEqual(w2) t.addFailure(newIPAddrFailure("str prefix equality failed "+w2.String(), w)) } if !prefEquals2 { t.addFailure(newIPAddrFailure("str prefix equality failed "+w2.String(), w)) } if !prefEquals3 { t.addFailure(newIPAddrFailure("str prefix equality failed "+w2.String(), w)) } } } } t.incrementTestCount() } func (t ipAddressTester) testStringContains(result, equal bool, wstr, w2str *ipaddr.IPAddressString) { if !wstr.Contains(w2str) { if result { t.addFailure(newFailure("containment failed "+w2str.String(), wstr)) } } else { if !result { t.addFailure(newFailure("containment passed "+w2str.String(), wstr)) } else { if equal { if !w2str.Contains(wstr) { t.addFailure(newFailure("failed "+wstr.String(), w2str)) } } else { if w2str.Contains(wstr) { t.addFailure(newFailure("failed "+wstr.String(), w2str)) } } } } } func isSameAllAround(supplied, internal *ipaddr.IPAddress) bool { return supplied.Equal(internal) && internal.Equal(supplied) && internal.GetNetworkPrefixLen().Equal(supplied.GetNetworkPrefixLen()) && internal.GetMinPrefixLenForBlock() == supplied.GetMinPrefixLenForBlock() && internal.GetPrefixLenForSingleBlock().Equal(supplied.GetPrefixLenForSingleBlock()) && internal.GetCount().Cmp(supplied.GetCount()) == 0 } func (t ipAddressTester) testNetmasks(prefix ipaddr.BitCount, ipv4NetworkAddress, ipv4NetworkAddressNoPrefix, ipv4HostAddress, ipv6NetworkAddress, ipv6NetworkAddressNoPrefix, ipv6HostAddress string) { ipv6Addr := t.createAddress(ipv6NetworkAddress) ipv4Addr := t.createAddress(ipv4NetworkAddress) if prefix <= ipaddr.IPv6BitCount { w2NoPrefix := t.createAddress(ipv6NetworkAddressNoPrefix) _, err := ipaddr.ValidatePrefixLenStr(strconv.Itoa(int(prefix)), ipaddr.IPv6) if err != nil { t.addFailure(newFailure("failed prefix "+strconv.Itoa(int(prefix))+": "+err.Error(), w2NoPrefix)) } ipv6AddrValue := ipv6Addr.GetAddress() ipv6network := ipv6AddrValue.GetNetwork() ipv6AddrValue = ipv6AddrValue.GetLower() addr6 := ipv6network.GetPrefixedNetworkMask(prefix) addr6NoPrefix := ipv6network.GetNetworkMask(prefix) w2ValueNoPrefix := w2NoPrefix.GetAddress() if (!isSameAllAround(ipv6AddrValue, addr6)) || !isSameAllAround(w2ValueNoPrefix, addr6NoPrefix) { if !isSameAllAround(ipv6AddrValue, addr6) { t.addFailure(newIPAddrFailure("failed "+addr6.String(), ipv6AddrValue)) } else { t.addFailure(newIPAddrFailure("failed "+addr6NoPrefix.String(), w2ValueNoPrefix)) } } else { addrHost6 := ipv6network.GetHostMask(prefix) ipv6HostAddrString := t.createAddress(ipv6HostAddress) ipv6HostAddrValue := ipv6HostAddrString.GetAddress() if !isSameAllAround(ipv6HostAddrValue, addrHost6) { t.addFailure(newFailure("failed "+addrHost6.String(), ipv6HostAddrString)) } else if prefix <= ipaddr.IPv4BitCount { wNoPrefix := t.createAddress(ipv4NetworkAddressNoPrefix) _, err = ipaddr.ValidatePrefixLenStr(strconv.Itoa(int(prefix)), ipaddr.IPv4) if err != nil { t.addFailure(newFailure("failed prefix "+strconv.Itoa(int(prefix))+": "+err.Error(), wNoPrefix)) } wValue := ipv4Addr.GetAddress() ipv4network := wValue.GetNetwork() wValue = wValue.GetLower() addr4 := ipv4network.GetPrefixedNetworkMask(prefix) addr4NoPrefix := ipv4network.GetNetworkMask(prefix) wValueNoPrefix := wNoPrefix.GetAddress() if (!isSameAllAround(wValue, addr4)) || !isSameAllAround(wValueNoPrefix, addr4NoPrefix) { if !isSameAllAround(wValue, addr4) { t.addFailure(newIPAddrFailure("failed "+addr4.String(), wValue)) } else { t.addFailure(newIPAddrFailure("failed "+addr4NoPrefix.String(), wValueNoPrefix)) } } else { addr4 := ipv4network.GetHostMask(prefix) ipv4Addr = t.createAddress(ipv4HostAddress) wValue = ipv4Addr.GetAddress() if !isSameAllAround(wValue, addr4) { t.addFailure(newFailure("failed "+addr4.String(), ipv4Addr)) } } } else { //prefix > IPv4Address.BIT_COUNT _, err := ipv4Addr.ToAddress() if err == nil { t.addFailure(newFailure("did not succeed with extra-large prefix", ipv4Addr)) } } } } else { _, err := ipv6Addr.ToAddress() if err == nil { t.addFailure(newFailure("succeeded with invalid prefix in "+ipv6Addr.String(), ipv4Addr)) } _, err = ipv4Addr.ToAddress() if err == nil { //t.addFailure(newFailure("succeeded with invalid prefix in "+ipv4Addr.String()+": "+err.Error(), ipv4Addr)) t.addFailure(newFailure("succeeded with invalid prefix in "+ipv4Addr.String(), ipv4Addr)) } } t.incrementTestCount() } func (t ipAddressTester) checkAddrNotMask(address *ipaddr.IPAddress, network bool) bool { maskPrefix := address.GetBlockMaskPrefixLen(network) otherMaskPrefix := address.GetBlockMaskPrefixLen(!network) if maskPrefix != nil { t.addFailure(newIPAddrFailure("failed not mask "+maskPrefix.String(), address)) return false } if otherMaskPrefix != nil { t.addFailure(newIPAddrFailure("failed not mask "+otherMaskPrefix.String(), address)) return false } t.incrementTestCount() return true } func (t ipAddressTester) checkNotMask(addr string) { addressStr := t.createAddress(addr) address := addressStr.GetAddress() val := (address.Bytes()[0] & 1) == 0 if t.checkAddrNotMask(address, val) { t.checkAddrNotMask(address, !val) } } func (t ipAddressTester) testSplit(address string, bits ipaddr.BitCount, network, networkNoRange, networkWithPrefix string, networkStringCount int, host string, hostStringCount int) { w := t.createAddress(address) v := w.GetAddress() section := v.GetNetworkSectionLen(bits) section = section.WithoutPrefixLen() sectionStr := section.ToNormalizedString() if sectionStr != network { t.addFailure(newFailure("failed got "+sectionStr+" expected "+network, w)) } else { sectionWithPrefix := v.GetNetworkSectionLen(bits) sectionStrWithPrefix := sectionWithPrefix.ToNormalizedString() if sectionStrWithPrefix != (networkWithPrefix) { t.addFailure(newFailure("failed got "+sectionStrWithPrefix+" expected "+networkWithPrefix, w)) } else { s := section.GetLower() sectionStrNoRange := s.ToNormalizedString() if sectionStrNoRange != networkNoRange || s.GetCount().Int64() != 1 { t.addFailure(newFailure("failed got "+sectionStrNoRange+" expected "+networkNoRange, w)) } else { // TODO LATER string collections //IPAddressPartStringCollection coll = sectionWithPrefix.toStandardStringCollection(); //String standards[] = coll.toStrings(); //if(standards.length != networkStringCount) { // addFailure(new Failure("failed " + section + " expected count " + networkStringCount + " was " + standards.length, w)); //} else { section = v.GetHostSectionLen(bits) section = section.WithoutPrefixLen() //printStrings(section); sectionStr = section.ToNormalizedString() if sectionStr != (host) { t.addFailure(newFailure("failed "+sectionStr+" expected "+host, w)) } //else { TODO LATER string collections // String standardStrs[] = section.toStandardStringCollection().toStrings(); // if(standardStrs.length != hostStringCount) { // addFailure(new Failure("failed " + section + " expected count " + hostStringCount + " was " + standardStrs.length, w)); // //standardStrs = section.toStandardStringCollection().toStrings(); // } //} //} } } } t.incrementTestCount() } func (t ipAddressTester) testURL(url string) { w := t.createAddress(url) _, err := w.ToAddress() if err == nil { t.addFailure(newFailure("failed: "+"URL "+url, w)) } addr := w.GetAddress() if addr != nil { t.addFailure(newFailure("failed: "+"URL "+url, w)) } w2 := t.createAddress(url) addr = w2.GetAddress() if addr != nil { t.addFailure(newFailure("failed: "+"URL "+url, w2)) } _, err = w2.ToAddress() if err == nil { t.addFailure(newFailure("failed: "+"URL "+url, w2)) } } // gets host address, then creates a second ip addr to match the original and gets host address that way // then checks that they match func (t ipAddressTester) testReverseHostAddress(str string) { addrStr := t.createAddress(str) addr := addrStr.GetAddress() hostAddr := addrStr.GetHostAddress() var hostAddr2 *ipaddr.IPAddress if addr.IsIPv6() { newAddr, err := ipaddr.NewIPv6Address(addr.ToIPv6().GetSection()) if err != nil { t.addFailure(newIPAddrFailure("error creating address from "+addr.String()+": "+err.Error(), addr)) return } newAddrString := newAddr.ToAddressString() hostAddr2 = newAddrString.GetHostAddress() } else { newAddr, err := ipaddr.NewIPv4Address(addr.ToIPv4().GetSection()) if err != nil { t.addFailure(newIPAddrFailure("error creating address from "+addr.String()+": "+err.Error(), addr)) return } newAddrString := newAddr.ToAddressString() hostAddr2 = newAddrString.GetHostAddress() } if !hostAddr.Equal(hostAddr2) { t.addFailure(newIPAddrFailure("expected "+hostAddr.String()+" got "+hostAddr2.String(), addr)) } t.incrementTestCount() } func (t ipAddressTester) testFromBytes(bytes []byte, expected string) { addr := t.createAddressFromIP(bytes) addr2 := t.createAddress(expected) result := addr.Equal(addr2.GetAddress()) if !result { t.addFailure(newIPAddrFailure("created was "+addr.String()+" expected was "+addr2.String(), addr)) } else { if addr.IsIPv4() { val := uint32(0) for i := 0; i < len(bytes); i++ { val <<= 8 val |= uint32(bytes[i]) } addr := t.createIPv4Address(val) result = addr.Equal(addr2.GetAddress()) if !result { t.addFailure(newIPAddrFailure("created was "+addr.String()+" expected was "+addr2.String(), addr.ToIP())) } } else { var highVal, lowVal uint64 i := 0 for ; i < 8; i++ { highVal <<= 8 highVal |= uint64(bytes[i]) } for ; i < 16; i++ { lowVal <<= 8 lowVal |= uint64(bytes[i]) } addr := t.createIPv6Address(highVal, lowVal) result = addr.Equal(addr2.GetAddress()) if !result { t.addFailure(newIPAddrFailure("created was "+addr.String()+" expected was "+addr2.String(), addr.ToIP())) } } } t.incrementTestCount() } func (t ipAddressTester) testResolved(original, expected string) { origAddress := t.createAddress(original) resolvedAddress := origAddress.GetAddress() if resolvedAddress == nil { resolvedAddress = t.createHost(original).GetAddress() } expectedAddress := t.createAddress(expected) var result bool if resolvedAddress == nil { result = expected == "" } else { result = resolvedAddress.Equal(expectedAddress.GetAddress()) } if !result { t.addFailure(newFailure("resolved was "+resolvedAddress.String()+" original was "+original, origAddress)) } t.incrementTestCount() } func (t ipAddressTester) testNormalized(original, expected string) { t.testNormalizedMC(original, expected, false, true) } func (t ipAddressTester) testMask(original, mask, expected string) { w := t.createAddress(original) orig := w.GetAddress() maskString := t.createAddress(mask) maskAddr := maskString.GetAddress() masked, err := orig.Mask(maskAddr) if err != nil { t.addFailure(newIPAddrFailure("testMask errored with mask "+maskAddr.String()+" error: "+err.Error(), orig)) } expectedStr := t.createAddress(expected) expectedAddr := expectedStr.GetAddress() if !masked.Equal(expectedAddr) { t.addFailure(newFailure("mask was "+mask+" and masked was "+masked.String(), w)) } t.incrementTestCount() } func (t ipAddressTester) testNormalizedMC(original, expected string, keepMixed, compress bool) { w := t.createAddress(original) if w.IsIPv6() { val := w.GetAddress().ToIPv6() var paramsBuilder = new(addrstr.IPv6StringOptionsBuilder) if compress { compressOpts := new(addrstr.CompressOptionsBuilder).SetCompressSingle(true).SetCompressionChoiceOptions(addrstr.ZerosOrHost).ToOptions() paramsBuilder = paramsBuilder.SetCompressOptions(compressOpts) } fromString := val.ToAddressString() if fromString != nil && fromString.IsMixedIPv6() { paramsBuilder.SetMixed(true) } params := paramsBuilder.ToOptions() normalized, err := val.ToCustomString(params) if err != nil { t.addFailure(newIPAddrFailure("ToCustomString errored with error: "+err.Error(), val.ToIP())) } if normalized != expected { t.addFailure(newFailure("normalization 1 was "+normalized+" expected was "+expected, w)) } } else if w.IsIPv4() { val := w.GetAddress().ToIPv4() normalized := val.ToNormalizedString() if normalized != expected { t.addFailure(newFailure("normalization 2 was "+normalized, w)) } } else { t.addFailure(newFailure("normalization failed on "+original, w)) } t.incrementTestCount() } func (t ipAddressTester) testCompressed(original, expected string) { w := t.createAddress(original) var normalized string val := w.GetAddress() if val != nil { normalized = val.ToCompressedString() } else { normalized = w.String() } if normalized != expected { t.addFailure(newFailure("canonical was "+normalized, w)) } t.incrementTestCount() } func (t ipAddressTester) testCanonical(original, expected string) { w := t.createAddress(original) addr := w.GetAddress() normalized := addr.ToCanonicalString() if normalized != expected { t.addFailure(newFailure("canonical was "+normalized, w)) } t.incrementTestCount() } func (t ipAddressTester) testMixed(original, expected string) { t.testMixedNoComp(original, expected, expected) } func (t ipAddressTester) testMixedNoComp(original, expected, expectedNoCompression string) { w := t.createAddress(original) val := w.GetAddress().ToIPv6() normalized, err := val.ToMixedString() if err != nil { t.addFailure(newIPAddrFailure("testMixedNoComp errored with error: "+err.Error(), val.ToIP())) } if normalized != expected { t.addFailure(newFailure("mixed was "+normalized+" expected was "+expected, w)) } else { compressOpts := new(addrstr.CompressOptionsBuilder).SetCompressSingle(true).SetCompressionChoiceOptions(addrstr.ZerosOrHost).SetMixedCompressionOptions(addrstr.NoMixedCompression).ToOptions() normalized, err := val.ToCustomString(new(addrstr.IPv6StringOptionsBuilder).SetMixed(true).SetCompressOptions(compressOpts).ToOptions()) if err != nil { t.addFailure(newIPAddrFailure("ToCustomString errored with error: "+err.Error(), val.ToIP())) } if normalized != expectedNoCompression { t.addFailure(newFailure("mixed was "+normalized+" expected was "+expectedNoCompression, w)) } } t.incrementTestCount() } func (t ipAddressTester) testRadices(original, expected string, radix int) { w := t.createAddress(original) val := w.GetAddress() options := new(addrstr.IPv4StringOptionsBuilder).SetRadix(radix).ToOptions() normalized := val.ToCustomString(options) if normalized != expected { t.addFailure(newFailure("string was "+normalized+" expected was "+expected, w)) } t.incrementTestCount() } func (t ipAddressTester) testInsertAndAppend(front, back string, expectedPref []ipaddr.BitCount) { is := make([]ipaddr.PrefixLen, len(expectedPref)) for i := 0; i < len(expectedPref); i++ { is[i] = cacheTestBits(expectedPref[i]) } t.testInsertAndAppendPrefs(front, back, is) } func (t ipAddressTester) testInsertAndAppendPrefs(front, back string, expectedPref []ipaddr.PrefixLen) { f := t.createAddress(front).GetAddress() b := t.createAddress(back).GetAddress() sep := byte(ipaddr.IPv4SegmentSeparator) if f.IsIPv6() { sep = ipaddr.IPv6SegmentSeparator } t.testAppendAndInsert(f.ToAddressBase(), b.ToAddressBase(), f.GetSegmentStrings(), b.GetSegmentStrings(), sep, expectedPref, false) } func (t ipAddressTester) testReplace(front, back string) { f := t.createAddress(front).GetAddress() b := t.createAddress(back).GetAddress() sep := byte(ipaddr.IPv4SegmentSeparator) if f.IsIPv6() { sep = ipaddr.IPv6SegmentSeparator } t.testBase.testReplace(f.ToAddressBase(), b.ToAddressBase(), f.GetSegmentStrings(), b.GetSegmentStrings(), sep, false) } func (t ipAddressTester) testInvalidIpv4Values() { //try { thebytes := []byte{1, 0, 0, 0, 0} thebytes[0] = 1 addr, err := ipaddr.NewIPv4AddressFromBytes(thebytes) if err == nil { t.addFailure(newIPAddrFailure("failed expected error for "+addr.String(), addr.ToIP())) } addr, err = ipaddr.NewIPv4AddressFromBytes([]byte{0, 0, 0, 0, 0}) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+addr.String()+" error: "+err.Error(), addr.ToIP())) } addr, err = ipaddr.NewIPv4AddressFromBytes([]byte{0, 0, 0, 0}) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+addr.String()+" error: "+err.Error(), addr.ToIP())) } addr, err = ipaddr.NewIPv4AddressFromBytes([]byte{0, 0, 0}) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+addr.String()+" error: "+err.Error(), addr.ToIP())) } addr, err = ipaddr.NewIPv4AddressFromBytes([]byte{0, 0}) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+addr.String()+" error: "+err.Error(), addr.ToIP())) } addr = ipaddr.NewIPv4AddressFromVals(func(segmentIndex int) ipaddr.IPv4SegInt { var val = 256 // will be truncated to 0 return ipaddr.IPv4SegInt(val) }) if !addr.IsZero() { t.addFailure(newIPAddrFailure("failed expected exception for "+addr.String(), addr.ToIP())) } addr = ipaddr.NewIPv4AddressFromVals(func(segmentIndex int) ipaddr.IPv4SegInt { var val = -1 // will be truncated to 0 return ipaddr.IPv4SegInt(val) }) if !addr.IsMax() { t.addFailure(newIPAddrFailure("failed expected exception for "+addr.String(), addr.ToIP())) } addr = ipaddr.NewIPv4AddressFromVals(func(segmentIndex int) ipaddr.IPv4SegInt { var val = 255 // will be truncated to 0 return ipaddr.IPv4SegInt(val) }) if !addr.IsMax() { t.addFailure(newIPAddrFailure("failed expected exception for "+addr.String(), addr.ToIP())) } } func (t ipAddressTester) testIPv4Values(segs []int, decimal string) { vals := make([]byte, len(segs)) strb := strings.Builder{} intval := uint32(0) bigInt := new(big.Int) bitsPerSegment := ipaddr.IPv4BitsPerSegment for i := 0; i < len(segs); i++ { seg := segs[i] if strb.Len() > 0 { strb.WriteByte('.') } strb.WriteString(strconv.Itoa(seg)) vals[i] = byte(seg) intval = (intval << uint(bitsPerSegment)) | uint32(seg) bigInt = bigInt.Lsh(bigInt, uint(bitsPerSegment)).Or(bigInt, new(big.Int).SetInt64(int64(seg))) } strbStr := strb.String() ipaddressStr := t.createAddress(strbStr) addr := [7]*ipaddr.IPv4Address{} addr[0] = t.createAddressFromIP(vals).ToIPv4() addr[1] = ipaddressStr.GetAddress().ToIPv4() addr[2] = t.createIPv4Address(intval) ips := net.ParseIP(strbStr) ips2 := net.IPv4(vals[0], vals[1], vals[2], vals[3]) ip, err := ipaddr.NewIPv4AddressFromBytes(ips) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+strbStr+" error: "+err.Error(), ip.ToIP())) } ip2, err := ipaddr.NewIPv4AddressFromBytes(ips2) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+strbStr+" error: "+err.Error(), ip2.ToIP())) } addr[3] = ip addr[4] = ip2 addr[5] = ipaddr.NewIPv4AddressFromUint32(intval) addr[6] = ipaddr.NewIPv4AddressFromUint32(uint32(bigInt.Uint64())) for j := 0; j < len(addr); j++ { for k := j; k < len(addr); k++ { if !addr[k].Equal(addr[j]) || !addr[j].Equal(addr[k]) { t.addFailure(newFailure("failed equals: "+addr[k].String()+" and "+addr[j].String(), ipaddressStr)) } } } if decimal != "" { for i := 0; i < len(addr); i++ { if decimal != addr[i].GetValue().String() { t.addFailure(newFailure("failed equals: "+addr[i].GetValue().String()+" and "+decimal, ipaddressStr)) } if decimal != strconv.FormatUint(uint64(addr[i].Uint32Value()), 10) { t.addFailure(newFailure("failed equals: "+strconv.FormatUint(uint64(addr[i].Uint32Value()), 10)+" and "+decimal, ipaddressStr)) } } } } func (t ipAddressTester) testIPv6Values(segs []int, decimal string) { vals := make([]byte, len(segs)*int(ipaddr.IPv6BytesPerSegment)) strb := strings.Builder{} bigInt := new(big.Int) bitsPerSegment := ipaddr.IPv6BitsPerSegment for i := 0; i < len(segs); i++ { seg := segs[i] if strb.Len() > 0 { strb.WriteByte(':') } strb.WriteString(strconv.FormatUint(uint64(seg), 16)) vals[i<<1] = byte(seg >> 8) vals[(i<<1)+1] = byte(seg) bigInt = bigInt.Lsh(bigInt, uint(bitsPerSegment)).Or(bigInt, new(big.Int).SetInt64(int64(seg))) } strbStr := strb.String() ipaddressStr := t.createAddress(strbStr) addr := [5]*ipaddr.IPv6Address{} addr[0] = t.createAddressFromIP(vals).ToIPv6() addr[1] = ipaddressStr.GetAddress().ToIPv6() ips := net.ParseIP(strbStr) ips2 := net.IP{vals[0], vals[1], vals[2], vals[3], vals[4], vals[5], vals[6], vals[7], vals[8], vals[9], vals[10], vals[11], vals[12], vals[13], vals[14], vals[15]} ip, err := ipaddr.NewIPv6AddressFromBytes(ips) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+strbStr+" error: "+err.Error(), ip.ToIP())) } ip2, err := ipaddr.NewIPv6AddressFromBytes(ips2) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+strbStr+" error: "+err.Error(), ip2.ToIP())) } addr[2] = ip addr[3] = ip2 ip3, err := ipaddr.NewIPv6AddressFromInt(bigInt) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+strbStr+" error: "+err.Error(), ip2.ToIP())) } addr[4] = ip3 for j := 0; j < len(addr); j++ { for k := j; k < len(addr); k++ { if !addr[k].Equal(addr[j]) || !addr[j].Equal(addr[k]) { // 0 and 3 not matching ::1:2:3:4 and 1:2:3:4:5:6:7:8 t.addFailure(newFailure("failed equals: "+addr[k].String()+" and "+addr[j].String(), ipaddressStr)) } } } if decimal != "" { for i := 0; i < len(addr); i++ { if decimal != addr[i].GetValue().String() { t.addFailure(newFailure("failed equals: "+addr[i].GetValue().String()+" and "+decimal, ipaddressStr)) } } } } func (t ipAddressTester) testInvalidIpv6Values() { thebytes := []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} thebytes[0] = 1 addr, err := ipaddr.NewIPv6AddressFromBytes(thebytes) if err == nil { t.addFailure(newIPAddrFailure("failed expected error for "+addr.String(), addr.ToIP())) } addr, err = ipaddr.NewIPv6AddressFromBytes([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+addr.String()+" error: "+err.Error(), addr.ToIP())) } addr, err = ipaddr.NewIPv6AddressFromBytes([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+addr.String()+" error: "+err.Error(), addr.ToIP())) } addr, err = ipaddr.NewIPv6AddressFromBytes([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+addr.String()+" error: "+err.Error(), addr.ToIP())) } addr, err = ipaddr.NewIPv6AddressFromBytes([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) if err != nil { t.addFailure(newIPAddrFailure("failed unexpected error for "+addr.String()+" error: "+err.Error(), addr.ToIP())) } addr = ipaddr.NewIPv6AddressFromVals(func(segmentIndex int) ipaddr.IPv6SegInt { var val = 0x10000 // will be truncated to 0 return ipaddr.IPv6SegInt(val) }) if !addr.IsZero() { t.addFailure(newIPAddrFailure("failed expected exception for "+addr.String(), addr.ToIP())) } addr = ipaddr.NewIPv6AddressFromVals(func(segmentIndex int) ipaddr.IPv6SegInt { var val = -1 // will be truncated to 0 return ipaddr.IPv6SegInt(val) }) if !addr.IsMax() { t.addFailure(newIPAddrFailure("failed expected exception for "+addr.String(), addr.ToIP())) } addr = ipaddr.NewIPv6AddressFromVals(func(segmentIndex int) ipaddr.IPv6SegInt { var val = 0xffff // will be truncated to 0 return ipaddr.IPv6SegInt(val) }) if !addr.IsMax() { t.addFailure(newIPAddrFailure("failed expected exception for "+addr.String(), addr.ToIP())) } addr, err = ipaddr.NewIPv6AddressFromInt(new(big.Int).SetInt64(-1)) if err == nil { t.addFailure(newIPAddrFailure("failed, expected error for -1", addr.ToIP())) } addr, err = ipaddr.NewIPv6AddressFromInt(new(big.Int)) if err != nil || !addr.IsZero() { t.addFailure(newIPAddrFailure("failed, unexpected error for "+new(big.Int).String(), addr.ToIP())) } addr, err = ipaddr.NewIPv6AddressFromInt(one28()) if err != nil || !addr.IsMax() { t.addFailure(newIPAddrFailure("failed, unexpected error for "+one28().String(), addr.ToIP())) } addr, err = ipaddr.NewIPv6AddressFromInt(new(big.Int).Add(one28(), bigOneConst())) if err == nil { t.addFailure(newIPAddrFailure("failed, expected error for "+new(big.Int).Add(one28(), bigOneConst()).String(), addr.ToIP())) } addr, err = ipaddr.NewIPv6AddressFromInt(new(big.Int).SetUint64(0xffffffff)) if err != nil { t.addFailure(newIPAddrFailure("failed, unexpected error for "+new(big.Int).SetUint64(0xffffffff).String(), addr.ToIP())) } addr, err = ipaddr.NewIPv6AddressFromInt(new(big.Int).SetUint64(0x1ffffffff)) if err != nil { t.addFailure(newIPAddrFailure("failed, unexpected error for "+new(big.Int).SetUint64(0x1ffffffff).String(), addr.ToIP())) } t.incrementTestCount() } func (t ipAddressTester) testSub(one, two string, resultStrings []string) { str := t.createAddress(one) sub := t.createAddress(two) addr := str.GetAddress() subAddr := sub.GetAddress() res := addr.Subtract(subAddr) if len(resultStrings) == 0 { if len(res) != 0 { t.addFailure(newIPAddrFailure("non-nil subtraction with "+addr.String(), subAddr)) } } else { if len(resultStrings) != len(res) { t.addFailure(newIPAddrFailure(fmt.Sprintf("length mismatch %v with %v", res, resultStrings), subAddr)) } else { results := make([]*ipaddr.IPAddress, len(resultStrings)) for i := 0; i < len(resultStrings); i++ { results[i] = t.createAddress(resultStrings[i]).GetAddress() } for _, r := range res { found := false for _, result := range results { if r.Equal(result) && r.GetNetworkPrefixLen().Equal(result.GetNetworkPrefixLen()) { found = true break } } if !found { t.addFailure(newIPAddrFailure(fmt.Sprintf("mismatch with %v", resultStrings), r)) } } } } t.incrementTestCount() } func (t ipAddressTester) testIntersect(one, two, resultString string) { t.testIntersectLowest(one, two, resultString, false) } func (t ipAddressTester) testIntersectLowest(one, two, resultString string, lowest bool) { str := t.createAddress(one) string2 := t.createAddress(two) addr := str.GetAddress() addr2 := string2.GetAddress() r := addr.Intersect(addr2) if resultString == "" { if r != nil { t.addFailure(newIPAddrFailure("non-nil intersection with "+addr.String(), addr2)) } } else { result := t.createAddress(resultString).GetAddress() if lowest { result = result.GetLower() } if !r.Equal(result) || !r.GetNetworkPrefixLen().Equal(result.GetNetworkPrefixLen()) { t.addFailure(newIPAddrFailure("mismatch with "+result.String(), r)) } } t.incrementTestCount() } func (t ipAddressTester) testToPrefixBlock(addrString, subnetString string) { str := t.createAddress(addrString) string2 := t.createAddress(subnetString) addr := str.GetAddress() subnet := string2.GetAddress() prefixBlock := addr.ToPrefixBlock() if !subnet.Equal(prefixBlock) { t.addFailure(newIPAddrFailure("prefix block mismatch "+subnet.String()+" with block "+prefixBlock.String(), addr)) } else if !subnet.GetNetworkPrefixLen().Equal(prefixBlock.GetNetworkPrefixLen()) { t.addFailure(newIPAddrFailure("prefix block length mismatch "+subnet.GetNetworkPrefixLen().String()+" and "+prefixBlock.GetNetworkPrefixLen().String(), addr)) } t.incrementTestCount() } func (t ipAddressTester) testZeroHost(addrString, zeroHostString string) { str := t.createAddress(addrString) string2 := t.createAddress(zeroHostString) addr := str.GetAddress() specialHost := string2.GetAddress() transformedHost, err := addr.ToZeroHost() if err != nil { t.addFailure(newIPAddrFailure("unexpected error max host: "+err.Error(), addr)) return } hostSection := transformedHost.GetHostSection() if hostSection.GetSegmentCount() > 0 && !hostSection.IsZero() { t.addFailure(newIPAddrFailure("non-zero host "+hostSection.String(), addr)) } if !transformedHost.GetNetworkPrefixLen().Equal(specialHost.GetNetworkPrefixLen()) { t.addFailure(newIPAddrFailure("prefix length mismatch "+transformedHost.GetNetworkPrefixLen().String()+" and "+specialHost.GetNetworkPrefixLen().String(), addr)) } //for i := 0; i < addr.GetSegmentCount(); i++ { // seg := addr.GetSegment(i) // for j := 0; j < 2; j++ { // TODO LATER consider re-adding toZeroHost on segments, and then if you do, put back the old tests here using it // currently the section toZeroHost uses getSubnetSegments with masks //IPAddressSegment newSeg = seg.toZeroHost(); //if(seg.isPrefixed()) { // Integer segPrefix = seg.getSegmentPrefixLength(); // boolean allPrefsSubnets = seg.getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets(); // if(allPrefsSubnets) { // if(newSeg.isPrefixed()) { // addFailure(new Failure("prefix length unexpected " + newSeg.getSegmentPrefixLength(), seg)); // } // } else { // if(!newSeg.isPrefixed() || !segPrefix.equals(newSeg.getSegmentPrefixLength())) { // addFailure(new Failure("prefix length mismatch " + segPrefix + " and " + newSeg.getSegmentPrefixLength(), seg)); // } // IPAddressSegment expected = seg.toNetworkSegment(segPrefix).getLower(); // if(!newSeg.getLower().equals(expected)) { // newSeg = seg.toZeroHost(); // addFailure(new Failure("new seg mismatch " + newSeg + " expected: " + expected, newSeg)); // } // expected = seg.toNetworkSegment(segPrefix).getUpper().toZeroHost(); // if(!newSeg.getUpper().equals(expected)) { // newSeg = seg.toZeroHost(); // addFailure(new Failure("new seg mismatch " + newSeg + " expected: " + expected, newSeg)); // } // } //} else if(newSeg.isPrefixed() || !newSeg.isZero()) { // addFailure(new Failure("new seg not zero " + newSeg, newSeg)); //} //seg = newSeg // } //} t.incrementTestCount() } func (t ipAddressTester) testZeroNetwork(addrString, zeroNetworkString string) { str := t.createAddress(addrString) string2 := t.createAddress(zeroNetworkString) addr := str.GetAddress() zeroNetwork := string2.GetAddress() transformedNetwork := addr.ToZeroNetwork() if !zeroNetwork.Equal(transformedNetwork) { t.addFailure(newIPAddrFailure("mismatch "+zeroNetwork.String()+" with network "+transformedNetwork.String(), addr)) } networkSection := transformedNetwork.GetNetworkSection() if networkSection.GetSegmentCount() > 0 && !networkSection.IsZero() { t.addFailure(newIPAddrFailure("non-zero network "+networkSection.String(), addr)) } if !transformedNetwork.GetNetworkPrefixLen().Equal(zeroNetwork.GetNetworkPrefixLen()) { t.addFailure(newIPAddrFailure("network prefix length mismatch "+transformedNetwork.GetNetworkPrefixLen().String()+" and "+zeroNetwork.GetNetworkPrefixLen().String(), addr)) } t.incrementTestCount() } func (t ipAddressTester) testMaxHost(addrString, maxHostString string) { str := t.createAddress(addrString) string2 := t.createAddress(maxHostString) addr := str.GetAddress() specialHost := string2.GetAddress() transformedHost, err := addr.ToMaxHost() if err != nil { t.addFailure(newIPAddrFailure("unexpected error max host: "+err.Error(), addr)) return } if !specialHost.Equal(transformedHost) { t.addFailure(newIPAddrFailure("mismatch "+specialHost.String()+" with host "+transformedHost.String(), addr)) } else if !transformedHost.GetNetworkPrefixLen().Equal(specialHost.GetNetworkPrefixLen()) { t.addFailure(newIPAddrFailure("prefix length mismatch "+transformedHost.GetNetworkPrefixLen().String()+" and "+specialHost.GetNetworkPrefixLen().String(), addr)) } t.incrementTestCount() } func (t ipAddressTester) testSplitBytes(addressStr string) { addr := t.createAddress(addressStr).GetAddress() t.testSplitBytesAddr(addr) } func (t ipAddressTester) testSplitBytesAddr(addr *ipaddr.IPAddress) { thebytes := addr.Bytes() addresses := reconstitute(addr.GetIPVersion(), thebytes, addr.GetBytesPerSegment()) if addr.IsMultiple() { for _, addrNext := range addresses { if !addr.GetLower().Equal(addrNext) { t.addFailure(newIPAddrFailure("lower reconstitute failure: "+addrNext.String(), addr)) } } thebytes = addr.UpperBytes() addresses = reconstitute(addr.GetIPVersion(), thebytes, addr.GetBytesPerSegment()) for _, addrNext := range addresses { if !addr.GetUpper().Equal(addrNext) { t.addFailure(newIPAddrFailure("upper reconstitute failure: "+addrNext.String(), addr)) } } } else { for _, addrNext := range addresses { if !addr.Equal(addrNext) { t.addFailure(newIPAddrFailure("reconstitute failure: "+addrNext.String(), addr)) } } } } func (t ipAddressTester) testByteExtension(addrString string, byteRepresentations [][]byte) { addrStr := t.createAddress(addrString) addr := addrStr.GetAddress() var all []*ipaddr.IPAddress if addr.IsIPv4() { for _, byteRepresentation := range byteRepresentations { ipv4Addr, err := ipaddr.NewIPv4AddressFromBytes(byteRepresentation) if err != nil { t.addFailure(newFailure("unexpected error: "+err.Error(), addrStr)) return } all = append(all, ipv4Addr.ToIP()) } all = append(all, addr) var lastBytes []byte for i := 0; i < len(all); i++ { byts := all[i].Bytes() if lastBytes == nil { lastBytes = byts if len(byts) != ipaddr.IPv4ByteCount { t.addFailure(newFailure("bytes length "+strconv.Itoa(len(byts)), addrStr)) } ipv4Addr, err := ipaddr.NewIPv4AddressFromBytes(byts) if err != nil { t.addFailure(newFailure("unexpected error: "+err.Error(), addrStr)) return } all = append(all, ipv4Addr.ToIP()) ipv4Addr = ipaddr.NewIPv4AddressFromUint32(uint32(new(big.Int).SetBytes(byts).Uint64())) all = append(all, ipv4Addr.ToIP()) } else if !bytes.Equal(lastBytes, byts) { t.addFailure(newFailure(fmt.Sprintf("generated addr bytes mismatch %v and %v", byts, lastBytes), addrStr)) } } } else { for _, byteRepresentation := range byteRepresentations { ipv6Addr, err := ipaddr.NewIPv6AddressFromBytes(byteRepresentation) if err != nil { t.addFailure(newFailure("unexpected error: "+err.Error(), addrStr)) return } all = append(all, ipv6Addr.ToIP()) } all = append(all, addr) var lastBytes []byte for i := 0; i < len(all); i++ { byts := all[i].Bytes() if lastBytes == nil { lastBytes = byts if len(byts) != ipaddr.IPv6ByteCount { t.addFailure(newFailure("bytes length "+strconv.Itoa(len(byts)), addrStr)) } ipv6Addr, err := ipaddr.NewIPv6AddressFromBytes(byts) if err != nil { t.addFailure(newFailure("unexpected error: "+err.Error(), addrStr)) return } all = append(all, ipv6Addr.ToIP()) b := new(big.Int).SetBytes(byts) all = append(all, ipv6Addr.ToIP()) bs := b.Bytes() ipv6Addr, err = ipaddr.NewIPv6AddressFromBytes(bs) if err != nil { t.addFailure(newFailure("unexpected error: "+err.Error(), addrStr)) return } all = append(all, ipv6Addr.ToIP()) } else if !bytes.Equal(lastBytes, byts) { t.addFailure(newFailure(fmt.Sprintf("generated addr bytes mismatch %v and %v", byts, lastBytes), addrStr)) } } } var allBytes [][]byte for _, addr := range all { allBytes = append(allBytes, addr.Bytes()) } for _, addr := range all { for _, addr2 := range all { if !addr.Equal(addr2) { t.addFailure(newFailure("addr mismatch "+addr.String()+" and "+addr2.String(), addrStr)) } } } for _, b := range allBytes { for _, b2 := range allBytes { if !bytes.Equal(b, b2) { t.addFailure(newFailure(fmt.Sprintf("addr mismatch %v and %v", b, b2), addrStr)) } } } t.incrementTestCount() } func reconstitute(version ipaddr.IPVersion, bytes []byte, segmentByteSize int) []*ipaddr.IPAddress { var addresses []*ipaddr.IPAddress sets := createSets(bytes, segmentByteSize) creator := ipaddr.IPAddressCreator{version} for _, set := range sets { var segments, segments2 []*ipaddr.IPAddressSegment for i, ind := 0, 0; i < len(set); i++ { setBytes := set[i] sec1 := creator.NewIPSectionFromBytes(setBytes) sec2 := creator.NewIPSectionFromBytes(bytes[ind : ind+len(setBytes)]) segs := sec1.GetSegments() segs2 := sec2.GetSegments() if i%2 == 1 { segs, segs2 = segs2, segs } ind += len(setBytes) segments = append(segments, segs...) segments2 = append(segments2, segs2...) } addr1, _ := ipaddr.NewIPAddressFromSegs(segments) addr2, _ := ipaddr.NewIPAddressFromSegs(segments2) addresses = append(addresses, addr1) addresses = append(addresses, addr2) } return addresses } func createSets(bytes []byte, segmentByteSize int) [][][]byte { //break into two, and three segmentLength := len(bytes) / segmentByteSize sets := [][][]byte{ { make([]byte, (segmentLength/2)*segmentByteSize), make([]byte, (segmentLength-segmentLength/2)*segmentByteSize), }, { make([]byte, (segmentLength/3)*segmentByteSize), make([]byte, (segmentLength/3)*segmentByteSize), make([]byte, (segmentLength-2*(segmentLength/3))*segmentByteSize), }, } for _, set := range sets { for i, ind := 0, 0; i < len(set); i++ { part := set[i] copy(part, bytes[ind:]) ind += len(part) } } return sets } func (t ipAddressTester) testIsPrefixBlock( orig string, isPrefixBlock, isSinglePrefixBlock bool) { original := t.createAddress(orig).GetAddress() if isPrefixBlock != original.IsPrefixBlock() { t.addFailure(newIPAddrFailure("is prefix block: "+strconv.FormatBool(original.IsPrefixBlock())+" expected: "+strconv.FormatBool(isPrefixBlock), original)) } else if isSinglePrefixBlock != original.IsSinglePrefixBlock() { t.addFailure(newIPAddrFailure("is single prefix block: "+strconv.FormatBool(original.IsSinglePrefixBlock())+" expected: "+strconv.FormatBool(isSinglePrefixBlock), original)) } t.incrementTestCount() } func (t ipAddressTester) testPrefixBlocks( orig string, prefix ipaddr.BitCount, containsPrefixBlock, containsSinglePrefixBlock bool) { original := t.createAddress(orig).GetAddress() if containsPrefixBlock != original.ContainsPrefixBlock(prefix) { t.addFailure(newIPAddrFailure("contains prefix block: "+strconv.FormatBool(original.ContainsPrefixBlock(prefix))+" expected: "+strconv.FormatBool(containsPrefixBlock), original)) } else if containsSinglePrefixBlock != original.ContainsSinglePrefixBlock(prefix) { t.addFailure(newIPAddrFailure("contains single prefix block: "+strconv.FormatBool(original.ContainsSinglePrefixBlock(prefix))+" expected: "+strconv.FormatBool(containsPrefixBlock), original)) } t.incrementTestCount() } func (t ipAddressTester) testIncrement(originalStr string, increment int64, resultStr string) { var addr *ipaddr.IPAddress if resultStr != "" { addr = t.createAddress(resultStr).GetAddress() } t.testBase.testIncrement(t.createAddress(originalStr).GetAddress().ToAddressBase(), increment, addr.ToAddressBase()) } func (t ipAddressTester) testLeadingZeroAddr(addrStr string, hasLeadingZeros bool) { str := t.createAddress(addrStr) _, err := str.ToAddress() if err != nil { t.addFailure(newFailure("unexpected error "+err.Error(), str)) } params := new(addrstrparam.IPAddressStringParamsBuilder). GetIPv4AddressParamsBuilder().AllowLeadingZeros(false).GetParentBuilder(). GetIPv6AddressParamsBuilder().AllowLeadingZeros(false).GetParentBuilder().ToParams() str = ipaddr.NewIPAddressStringParams(addrStr, params) _, err = str.ToAddress() if err == nil { if hasLeadingZeros { t.addFailure(newFailure("leading zeros allowed when forbidden", str)) } } else { if !hasLeadingZeros { t.addFailure(newFailure("leading zeros not there", str)) } } t.incrementTestCount() } func (t ipAddressTester) testInetAtonLeadingZeroAddr(addrStr string, hasLeadingZeros, hasInetAtonLeadingZeros, isInetAtonOctal bool) { str := t.createInetAtonAddress(addrStr) addr, err := str.ToAddress() if err != nil { t.addFailure(newFailure("unexpected error "+err.Error(), str)) return } value := addr.GetValue() params := new(addrstrparam.IPAddressStringParamsBuilder). GetIPv4AddressParamsBuilder().AllowLeadingZeros(false).GetParentBuilder().ToParams() str = ipaddr.NewIPAddressStringParams(addrStr, params) _, err = str.ToAddress() if err == nil { if hasLeadingZeros { t.addFailure(newFailure("leading zeros allowed when forbidden", str)) } } else { if !hasLeadingZeros { t.addFailure(newFailure("leading zeros not there", str)) } } params = new(addrstrparam.IPAddressStringParamsBuilder).Set(params).GetIPv4AddressParamsBuilder().AllowLeadingZeros(true).Allow_inet_aton(true).Allow_inet_aton_leading_zeros(false).GetParentBuilder().ToParams() str = ipaddr.NewIPAddressStringParams(addrStr, params) _, err = str.ToAddress() if err == nil { if hasInetAtonLeadingZeros { t.addFailure(newFailure("leading zeros allowed when forbidden", str)) } } else { if !hasInetAtonLeadingZeros { t.addFailure(newFailure("leading zeros not there", str)) } } params = new(addrstrparam.IPAddressStringParamsBuilder).Set(params).Allow_inet_aton(false).ToParams() str = ipaddr.NewIPAddressStringParams(addrStr, params) _, err = str.ToAddress() if isInetAtonOctal { addr, err = str.ToAddress() if err != nil { t.addFailure(newFailure("inet aton octal should be decimal, unexpected error: "+err.Error(), str)) return } value2 := addr.GetValue() octalDiffers := false for i := 0; i < addr.GetSegmentCount(); i++ { octalDiffers = octalDiffers || addr.GetSegment(i).GetSegmentValue() >= 7 } valsEqual := value.Cmp(value2) == 0 if !octalDiffers { valsEqual = !valsEqual } if valsEqual { t.addFailure(newFailure("inet aton octal should be unequal", str)) } } else if hasLeadingZeros { // if not octal but has leading zeros, then must be hex _, err = str.ToAddress() if err == nil { t.addFailure(newFailure("inet aton hex should be forbidden", str)) } } else { // neither octal nor hex addr, err = str.ToAddress() if err != nil { t.addFailure(newFailure("inet aton should have no effect, unexpected error: "+err.Error(), str)) return } value2 := addr.GetValue() if value.Cmp(value2) != 0 { t.addFailure(newFailure("should be same value", str)) } } t.incrementTestCount() } func (t ipAddressTester) testRangeExtend(lower1, higher1, lower2, higher2, resultLower, resultHigher string) { t.testRangeExtendImpl(lower1, higher1, lower2, higher2, resultLower, resultHigher) t.testRangeExtendImpl(lower2, higher2, lower1, higher1, resultHigher, resultLower) } func (t ipAddressTester) testRangeExtendImpl(lower1, higher1, lower2, higher2, resultLower, resultHigher string) { var addr, addr2 *ipaddr.IPAddress var range1, range2, result2 *ipaddr.IPAddressSeqRange addr = t.createAddress(lower1).GetAddress() if higher1 == "" { range1 = addr.ToSequentialRange() } else { addr2 = t.createAddress(higher1).GetAddress() range1 = addr.SpanWithRange(addr2) } addr = t.createAddress(lower2).GetAddress() if higher2 == "" { result2 = range1.Extend(addr.ToSequentialRange()) range2 = addr.ToSequentialRange() } else { addr2 = t.createAddress(higher2).GetAddress() range2 = addr.SpanWithRange(addr2) } result := range1.Extend(range2) if result2 != nil { if !result.Equal(result2) { t.addFailure(newIPAddrFailure("mismatch result "+result.String()+"' with '"+result2.String()+"'", addr)) } } if resultLower == "" { if result != nil { t.addFailure(newIPAddrFailure("mismatch result "+result.String()+" expected nil extending '"+range1.String()+"' with '"+range2.String()+"'", addr)) } } else { addr = t.createAddress(resultLower).GetAddress() addr2 = t.createAddress(resultHigher).GetAddress() expectedResult := addr.SpanWithRange(addr2) if !result.Equal(expectedResult) { t.addFailure(newIPAddrFailure("mismatch result '"+result.String()+"' expected '"+expectedResult.String()+"' extending '"+range1.String()+"' with '"+range2.String()+"'", addr)) } } t.incrementTestCount() } func (t ipAddressTester) testRangeJoin(lower1, higher1, lower2, higher2, resultLower, resultHigher string) { t.testRangeJoinImpl(lower1, higher1, lower2, higher2, resultLower, resultHigher) t.testRangeJoinImpl(lower2, higher2, lower1, higher1, resultHigher, resultLower) } func (t ipAddressTester) testRangeJoinImpl(lower1, higher1, lower2, higher2, resultLower, resultHigher string) { addr := t.createAddress(lower1).GetAddress() addr2 := t.createAddress(higher1).GetAddress() range1 := addr.SpanWithRange(addr2) addr = t.createAddress(lower2).GetAddress() addr2 = t.createAddress(higher2).GetAddress() range2 := addr.SpanWithRange(addr2) result := range1.JoinTo(range2) if resultLower == "" { if result != nil { t.addFailure(newIPAddrFailure(fmt.Sprintf("mismatch result %v expected nil joining '"+addr.String()+"' with '"+addr2.String()+"'", result), addr)) } } else { addr = t.createAddress(resultLower).GetAddress() addr2 = t.createAddress(resultHigher).GetAddress() expectedResult := addr.SpanWithRange(addr2) if !result.Equal(expectedResult) { t.addFailure(newIPAddrFailure(fmt.Sprintf("mismatch result %v expected '"+expectedResult.String()+"' joining '"+addr.String()+"' with '"+addr2.String()+"'", result), addr)) } } t.incrementTestCount() } func (t ipAddressTester) testRangeIntersect(lower1, higher1, lower2, higher2, resultLower, resultHigher string) { t.testRangeIntersectImpl(lower1, higher1, lower2, higher2, resultLower, resultHigher) t.testRangeIntersectImpl(lower2, higher2, lower1, higher1, resultHigher, resultLower) } func (t ipAddressTester) testRangeIntersectImpl(lower1, higher1, lower2, higher2, resultLower, resultHigher string) { addr := t.createAddress(lower1).GetAddress() addr2 := t.createAddress(higher1).GetAddress() range1 := addr.SpanWithRange(addr2) addr = t.createAddress(lower2).GetAddress() addr2 = t.createAddress(higher2).GetAddress() range2 := addr.SpanWithRange(addr2) result := range1.Intersect(range2) if resultLower == "" { if result != nil { t.addFailure(newIPAddrFailure("mismatch result "+result.String()+" expected nil intersecting '"+addr.String()+"' with '"+addr2.String()+"'", addr)) } } else { addr := t.createAddress(resultLower).GetAddress() addr2 := t.createAddress(resultHigher).GetAddress() expectedResult := addr.SpanWithRange(addr2) if !result.Equal(expectedResult) { t.addFailure(newIPAddrFailure("mismatch result '"+result.String()+"' expected '"+expectedResult.String()+"' intersecting '"+addr.String()+"' with '"+addr2.String()+"'", addr)) } } t.incrementTestCount() } func (t ipAddressTester) testRangeSubtract(lower1, higher1, lower2, higher2 string, resultPairs ...string) { addr := t.createAddress(lower1).GetAddress() addr2 := t.createAddress(higher1).GetAddress() range1 := addr.SpanWithRange(addr2) addr = t.createAddress(lower2).GetAddress() addr2 = t.createAddress(higher2).GetAddress() range2 := addr.SpanWithRange(addr2) result := range1.Subtract(range2) if len(resultPairs) == 0 { if len(result) != 0 { t.addFailure(newIPAddrFailure(fmt.Sprintf("mismatch result %v expected zero length result subtracting '"+addr2.String()+"' from '"+addr.String()+"'", result), addr)) } } else { //resultPairs.length >= 2 addr = t.createAddress(resultPairs[0]).GetAddress() addr2 = t.createAddress(resultPairs[1]).GetAddress() expectedResult := addr.SpanWithRange(addr2) if len(result) == 0 || !result[0].Equal(expectedResult) { t.addFailure(newIPAddrFailure(fmt.Sprintf("mismatch result %v expected '"+expectedResult.String()+"' subtracting '"+addr2.String()+"' from '"+addr.String()+"'", result), addr)) } else if len(resultPairs) == 4 { addr = t.createAddress(resultPairs[2]).GetAddress() addr2 = t.createAddress(resultPairs[3]).GetAddress() expectedResult = addr.SpanWithRange(addr2) if len(result) == 1 || !result[1].Equal(expectedResult) { t.addFailure(newIPAddrFailure(fmt.Sprintf("mismatch result %v expected '"+expectedResult.String()+"' subtracting '"+addr2.String()+"' from '"+addr.String()+"'", result), addr)) } } else if len(result) > 1 { t.addFailure(newIPAddrFailure(fmt.Sprintf("mismatch result %v expected %v ranges subtracting '"+addr2.String()+"' from '"+addr.String()+"'", result, len(resultPairs)/2), addr)) } } t.incrementTestCount() } func (t ipAddressTester) testRangeJoin2(inputs, expected []string) { var rangeList []*ipaddr.IPAddressSeqRange for i := 1; i < len(inputs); i += 2 { if inputs[i-1] == "" { rangeList = append(rangeList, nil) continue } w := t.createAddress(inputs[i-1]) w2 := t.createAddress(inputs[i]) val := w.GetAddress().SpanWithRange(w2.GetAddress()) rangeList = append(rangeList, val) } var rng *ipaddr.SequentialRange[*ipaddr.IPAddress] result := rng.Join(rangeList...) rangeList = rangeList[:0] for i := 1; i < len(expected); i += 2 { w := t.createAddress(expected[i-1]) w2 := t.createAddress(expected[i]) val := w.GetAddress().SpanWithRange(w2.GetAddress()) rangeList = append(rangeList, val) } if len(result) != len(rangeList) { t.addFailure(newFailure(fmt.Sprintf("failed expected: %v actual: %v", rangeList, result), nil)) } for i := 0; i < len(result); i++ { if !result[i].Equal(rangeList[i]) { t.addFailure(newSeqRangeFailure("failed expected: "+rangeList[i].String()+" actual: "+result[i].String(), result[i])) } } t.incrementTestCount() } // divs is an array with the series of values or range of values in the grouping // divs must be an []interface{} with each element a *big.Int/int/uint/uint64 or an array of two *big.Int/int/uint/uint64 // Alternatively, instead of supplying Object[1] you can supply the first and only element instead func (t ipAddressTester) testAddressStringRangeP(address string, isIncompatibleAddress, isMaskedIncompatibleAddress bool, lowerAddress, upperAddress string, divs interface{}, prefixLength ipaddr.PrefixLen, isSequential *bool) { addrStr := t.createAddress(address) // TODO LATER this code and the calling tests are all ready to go once I support toDivisionGrouping, //just a little more Java to go translation in here is needed, but not much. I left some of the Java types to help with clarity. //IPAddressDivisionSeries s, err := addrStr.ToDivisionGrouping(); //if err != nil { // if !isMaskedIncompatibleAddress { // t.addFailure(newFailure("address " + addrStr.String() + " produced error " + e.Error() + " when getting grouping ", addrStr)); // } //} else if(isMaskedIncompatibleAddress) { // t.addFailure(newFailure("masked incompatible address " + addrStr.String() + " did not produce error when getting grouping " + s.String(), addrStr)); //} if !isMaskedIncompatibleAddress { var divisions []interface{} if bidivs, ok := divs.([2]*big.Int); ok { divisions = []interface{}{bidivs} } else if bidiv, ok := divs.(*big.Int); ok { divisions = []interface{}{bidiv} } else if intdivs, ok := divs.([2]int); ok { divisions = []interface{}{intdivs} } else if intdiv, ok := divs.(int); ok { divisions = []interface{}{intdiv} } else if uintdivs, ok := divs.([2]uint); ok { divisions = []interface{}{uintdivs} } else if uintdiv, ok := divs.(uint); ok { divisions = []interface{}{uintdiv} } else if uint64divs, ok := divs.([2]uint64); ok { divisions = []interface{}{uint64divs} } else if uint64div, ok := divs.(uint64); ok { divisions = []interface{}{uint64div} } else { divisions = divs.([]interface{}) } //if s.getDivisionCount() != len(divisions) { // t.addFailure(newFailure("grouping " + s.String() + " for " + addrStr.String() + " does not have expected length " + strconv.Itoa(len(divisions)), addrStr)); //} var totalBits ipaddr.BitCount for i := 0; i < len(divisions); i++ { //IPAddressGenericDivision d = s.GetDivision(i); //int divBits = d.getBitCount(); //totalBits += divBits; //BigInteger val := d.GetValue(); //BigInteger upperVal := d.GetUpperValue(); expectedDivision := divisions[i] var expectedUpper, expectedLower *big.Int if expected, ok := expectedDivision.(int); ok { expectedUpper = new(big.Int).SetInt64(int64(expected)) expectedLower = expectedUpper } else if expected, ok := expectedDivision.([]int); ok { expectedUpper = new(big.Int).SetUint64(uint64(expected[0])) expectedLower = new(big.Int).SetUint64(uint64(expected[1])) } else if expected, ok := expectedDivision.(uint); ok { expectedUpper = new(big.Int).SetUint64(uint64(expected)) expectedLower = expectedUpper } else if expected, ok := expectedDivision.([]uint); ok { expectedUpper = new(big.Int).SetUint64(uint64(expected[0])) expectedLower = new(big.Int).SetUint64(uint64(expected[1])) } else if expected, ok := expectedDivision.(uint64); ok { expectedUpper = new(big.Int).SetUint64(expected) expectedLower = expectedUpper } else if expected, ok := expectedDivision.([]uint64); ok { expectedUpper = new(big.Int).SetUint64(expected[0]) expectedLower = new(big.Int).SetUint64(expected[1]) } else if expected, ok := expectedDivision.([]*big.Int); ok { expectedLower = expected[0] expectedUpper = expected[1] } else if expected, ok := expectedDivision.(*big.Int); ok { expectedUpper = expectedLower expectedLower = expected } //if val.Cmp(expectedLower) != 0 { // t.addFailure(newFailure("division val " + val.String() + " for " + addrStr.String() + " is not expected val " + expectedLower.String(), addrStr)); //} else if(upperVal.Cmp(expectedUpper) != 0) { // t.addFailure(newFailure("upper division val " + upperVal.String() + " for " + addrStr.String() + " is not expected val " + expectedUpper.String(), addrStr)); //} } var expectedBitCount ipaddr.BitCount if addrStr.IsIPv4() { expectedBitCount = ipaddr.IPv4BitCount } else { expectedBitCount = ipaddr.IPv6BitCount } if totalBits != expectedBitCount { //t.addFailure(newFailure("bit count " + totalBits.String() + " for " + addrStr.String() + " is not expected " + expectedBitCount.String(), addrStr)); } //if !s.GetPrefixLen().Equal(prefixLength) { // t.addFailure(newFailure("prefix length " + s.GetPrefixLen().String() + " for " + s.String() + " is not expected " + prefixLength.String(), addrStr)); //} } rangeString := t.createAddress(address) // go directly to getting the range which should never throw IncompatibleAddressException even for incompatible addresses range1 := rangeString.GetSequentialRange() low := t.createAddress(lowerAddress).GetAddress().GetLower() // getLower() needed for auto subnets up := t.createAddress(upperAddress).GetAddress().GetUpper() // getUpper() needed for auto subnets if !range1.GetLower().Equal(low) { t.addFailure(newSeqRangeFailure("range lower "+range1.GetLower().String()+" does not match expected "+low.String(), range1)) } if !range1.GetUpper().Equal(up) { t.addFailure(newSeqRangeFailure("range upper "+range1.GetUpper().String()+" does not match expected "+up.String(), range1)) } addrStr = t.createAddress(address) // now we should throw IncompatibleAddressException if address is incompatible addr, err := addrStr.ToAddress() if err != nil { if !isIncompatibleAddress { t.addFailure(newFailure("address "+addrStr.String()+" identified as an incompatible address", addrStr)) } addrRange, err := addrStr.ToSequentialRange() if err != nil { t.addFailure(newFailure("unexpected error getting range from "+addrStr.String(), addrStr)) return } if !range1.Equal(addrRange) || !addrRange.Equal(range1) { t.addFailure(newFailure("address range from "+addrStr.String()+" ("+addrRange.GetLower().String()+","+addrRange.GetUpper().String()+")"+ " does not match range from address string "+rangeString.String()+" ("+range1.GetLower().String()+","+range1.GetUpper().String()+")", addrStr)) } } else { if isIncompatibleAddress { t.addFailure(newFailure("address "+addrStr.String()+" not identified as an incompatible address, instead it is "+addr.String(), addrStr)) } if isSequential != nil { if *isSequential != addr.IsSequential() { t.addFailure(newIPAddrFailure("sequential mismatch, unexpectedly: "+addr.String(), addr)) } } addrRange := addr.ToSequentialRange() if !range1.Equal(addrRange) || !addrRange.Equal(range1) { t.addFailure(newIPAddrFailure("address range from "+addr.String()+" ("+addrRange.GetLower().String()+","+addrRange.GetUpper().String()+")"+ " does not match range from address string "+rangeString.String()+" ("+range1.GetLower().String()+","+range1.GetUpper().String()+")", addr)) } // now get the range from rangeString after you get the address, which should get it a different way, from the address after := rangeString.GetAddress() lowerFromSeqRange := after.GetLower() upperFromSeqRange := after.GetUpper() lowerFromAddr := addr.GetLower() upperFromAddr := addr.GetUpper() if !lowerFromSeqRange.Equal(lowerFromAddr) || !lowerFromSeqRange.GetNetworkPrefixLen().Equal(lowerFromAddr.GetNetworkPrefixLen()) { t.addFailure(newIPAddrFailure("lower from range "+lowerFromSeqRange.String()+" does not match lower from address "+lowerFromAddr.String(), lowerFromSeqRange)) } if !upperFromSeqRange.Equal(upperFromAddr) || !upperFromSeqRange.GetNetworkPrefixLen().Equal(upperFromAddr.GetNetworkPrefixLen()) { t.addFailure(newIPAddrFailure("upper from range "+upperFromSeqRange.String()+" does not match upper from address "+upperFromAddr.String(), upperFromSeqRange)) } // now get the range from a string after you get the address first, which should get it a different way, from the address oneMore := t.createAddress(address) oneMore.GetAddress() rangeAfterAddr := oneMore.GetSequentialRange() if !range1.Equal(rangeAfterAddr) || !rangeAfterAddr.Equal(range1) { t.addFailure(newIPAddrFailure("address range from "+rangeString.String()+" after address ("+rangeAfterAddr.GetLower().String()+","+rangeAfterAddr.GetUpper().String()+")"+ " does not match range from address string "+rangeString.String()+" before address ("+range1.GetLower().String()+","+range1.GetUpper().String()+")", addr)) } if !addrRange.Equal(rangeAfterAddr) || !rangeAfterAddr.Equal(addrRange) { t.addFailure(newIPAddrFailure("address range from "+rangeString.String()+" after address ("+rangeAfterAddr.GetLower().String()+","+rangeAfterAddr.GetUpper().String()+")"+ " does not match range from address string "+addr.String()+" ("+addrRange.GetLower().String()+","+addrRange.GetUpper().String()+")", addr)) } } //seqStr := t.createAddress(address) //if isSequential != nil { //if *isSequential != seqStr.IsSequential() { // t.addFailure(newFailure("sequential mismatch, unexpectedly: "+seqStr.String(), seqStr)) //} //if !isMaskedIncompatibleAddress && isSequential != seqStr.ToDivisionGrouping().IsSequential() { // t.addFailure(newFailure("sequential grouping mismatch, unexpectedly, " + seqStr.String() + " and " + seqStr.ToDivisionGrouping().String() , seqStr)); //} //} t.incrementTestCount() } func (t ipAddressTester) testMaskedIncompatibleAddress(address, lower, upper string) { t.testAddressStringRangeP(address, true, true, lower, upper, nil, nil, nil) } func (t ipAddressTester) testIncompatibleAddress2(address, lower, upper string, divisions interface{}) { t.testIncompatibleAddress(address, lower, upper, divisions, nil) } func (t ipAddressTester) testIncompatibleAddress(address, lower, upper string, divisions interface{}, prefixLength ipaddr.PrefixLen) { t.testAddressStringRangeP(address, true, false, lower, upper, divisions, prefixLength, nil) } func (t ipAddressTester) testIncompatibleAddress1(address, lower, upper string, divisions interface{}, prefixLength ipaddr.PrefixLen, isSequential bool) { t.testAddressStringRangeP(address, true, false, lower, upper, divisions, prefixLength, &isSequential) } func (t ipAddressTester) testSubnetStringRange2(address, lower, upper string, divisions interface{}) { t.testSubnetStringRange(address, lower, upper, divisions, nil) } func (t ipAddressTester) testSubnetStringRange(address, lower, upper string, divisions interface{}, prefixLength ipaddr.PrefixLen) { t.testAddressStringRangeP(address, false, false, lower, upper, divisions, prefixLength, nil) } func (t ipAddressTester) testSubnetStringRange1(address, lower, upper string, divisions interface{}, prefixLength ipaddr.PrefixLen, isSequential bool) { t.testAddressStringRangeP(address, false, false, lower, upper, divisions, prefixLength, &isSequential) } func (t ipAddressTester) testAddressStringRange1(address string, divisions interface{}) { t.testAddressStringRangeP(address, false, false, address, address, divisions, nil, &trueVal) } func (t ipAddressTester) testAddressStringRange(address string, divisions interface{}, prefixLength ipaddr.PrefixLen) { t.testAddressStringRangeP(address, false, false, address, address, divisions, prefixLength, &trueVal) } func (t ipAddressTester) testIPv4Mapped(str string, expected bool) { addrStr := t.createAddress(str) if addrStr.IsIPv4Mapped() != expected { t.addFailure(newFailure(fmt.Sprint("invalid IPv4-mapped result: ", !expected), addrStr)) } else if addrStr.GetAddress().ToIPv6().IsIPv4Mapped() != expected { t.addFailure(newFailure(fmt.Sprint("invalid IPv4-mapped result: ", !expected), addrStr)) } t.incrementTestCount() } var trueVal = true var conv = ipaddr.DefaultAddressConverter{} func conversionContains(h1, h2 *ipaddr.IPAddress) bool { if h1.IsIPv4() { if !h2.IsIPv4() { if conv.IsIPv4Convertible(h2) { return h1.Contains(conv.ToIPv4(h2)) } } } else if h1.IsIPv6() { if !h2.IsIPv6() { if conv.IsIPv6Convertible(h2) { return h1.Contains(conv.ToIPv6(h2)) } } } return false } func conversionMatches(h1, h2 *ipaddr.IPAddressString) bool { if h1.IsIPv4() { if !h2.IsIPv4() { if h2.GetAddress() != nil && conv.IsIPv4Convertible(h2.GetAddress()) { return h1.GetAddress().Equal(conv.ToIPv4(h2.GetAddress())) } } } else if h1.IsIPv6() { if !h2.IsIPv6() { if h2.GetAddress() != nil && conv.IsIPv6Convertible(h2.GetAddress()) { return h1.GetAddress().Equal(conv.ToIPv6(h2.GetAddress())) } } } return false } func conversionCompare(h1, h2 *ipaddr.IPAddressString) int { if h1.IsIPv4() { if !h2.IsIPv4() { if h2.GetAddress() != nil && conv.IsIPv4Convertible(h2.GetAddress()) { return h1.GetAddress().Compare(conv.ToIPv4(h2.GetAddress())) } } return -1 } else if h1.IsIPv6() { if !h2.IsIPv6() { if h2.GetAddress() != nil && conv.IsIPv6Convertible(h2.GetAddress()) { return h1.GetAddress().Compare(conv.ToIPv6(h2.GetAddress())) } } } return 1 } func makePrefixSubnet(directAddress *ipaddr.IPAddress) *ipaddr.IPAddress { segs := directAddress.GetSegments() pref := directAddress.GetPrefixLen() prefSeg := int(pref.Len() / directAddress.GetBitsPerSegment()) if prefSeg < len(segs) { creator := ipaddr.IPAddressCreator{directAddress.GetIPVersion()} if directAddress.GetPrefixCount().Cmp(bigOneConst()) == 0 { origSeg := segs[prefSeg] mask := origSeg.GetSegmentNetworkMask(pref.Len() % directAddress.GetBitsPerSegment()) segs[prefSeg] = creator.CreateSegment(origSeg.GetSegmentValue()&mask, origSeg.GetUpperSegmentValue()&mask, origSeg.GetSegmentPrefixLen()) for ps := prefSeg + 1; ps < len(segs); ps++ { segs[ps] = creator.CreatePrefixSegment(0, cacheTestBits(0)) } thebytes := make([]byte, directAddress.GetByteCount()) bytesPerSegment := directAddress.GetBytesPerSegment() for i, j := 0, 0; i < len(segs); i++ { segs[i].CopyBytes(thebytes[j:]) j += bytesPerSegment } directAddress, _ = ipaddr.NewIPAddressFromPrefixedNetIP(thebytes, pref) } else { //we could have used SegmentValueProvider in both blocks, but mixing it up to test everything origSeg := segs[prefSeg] mask := origSeg.GetSegmentNetworkMask(pref.Len() % directAddress.GetBitsPerSegment()) directAddress = creator.NewIPAddressFromPrefixedVals( func(segmentIndex int) ipaddr.SegInt { if segmentIndex < prefSeg { return segs[segmentIndex].GetSegmentValue() } else if segmentIndex == prefSeg { return origSeg.GetSegmentValue() & mask } else { return 0 } }, func(segmentIndex int) ipaddr.SegInt { if segmentIndex < prefSeg { return segs[segmentIndex].GetUpperSegmentValue() } else if segmentIndex == prefSeg { return origSeg.GetUpperSegmentValue() & mask } else { return 0 } }, pref, ) } } return directAddress } ipaddress-go-1.5.4/ipaddr/test/keytest.go000066400000000000000000000276231440250641600203570ustar00rootroot00000000000000package test import ( "fmt" "github.com/seancfoley/ipaddress-go/ipaddr" "sync/atomic" ) type keyTester struct { testBase } var didKeyTest int32 func (t keyTester) run() { didIt := atomic.LoadInt32(&didKeyTest) if didIt == 0 { cached := t.getAllCached() cachedMAC := t.getAllMACCached() if len(cached) > 0 || len(cachedMAC) > 0 { swapped := atomic.CompareAndSwapInt32(&didKeyTest, 0, 1) if swapped { if len(cached) > 0 { zeroAddr := ipaddr.Address{} zeroIPAddr := ipaddr.IPAddress{} zero4Addr := ipaddr.IPv4Address{} zero6Addr := ipaddr.IPv6Address{} cached = append(cached, &zeroIPAddr, zero4Addr.ToIP(), zero6Addr.ToIP()) //fmt.Printf("testing %d IPs\n", len(cached)) testGenericKeys[*ipaddr.IPAddress](t, cached) addrs := make([]*ipaddr.Address, 0, len(cached)+4) for _, addr := range cached { addrs = append(addrs, addr.ToAddressBase()) } addrs = append(addrs, &zeroAddr, zeroIPAddr.ToAddressBase(), zero4Addr.ToAddressBase(), zero6Addr.ToAddressBase()) testGenericKeys[*ipaddr.Address](t, addrs) t.testKeys(addrs) ipv4Addrs := make([]*ipaddr.IPv4Address, 0, len(cached)+1) for _, addr := range cached { if addr.IsIPv4() { ipv4Addrs = append(ipv4Addrs, addr.ToIPv4()) } } ipv4Addrs = append(ipv4Addrs, &zero4Addr) testGenericKeys[*ipaddr.IPv4Address](t, ipv4Addrs) ipv6Addrs := make([]*ipaddr.IPv6Address, 0, len(cached)+1) for _, addr := range cached { if addr.IsIPv6() { ipv6Addrs = append(ipv6Addrs, addr.ToIPv6()) } } ipv6Addrs = append(ipv6Addrs, &zero6Addr) testGenericKeys[*ipaddr.IPv6Address](t, ipv6Addrs) t.testNetNetIPs(cached) t.testNetIPAddrs(cached) t.testNetIPs(cached) } if len(cachedMAC) > 0 { zeroAddr := ipaddr.Address{} zeroMACAddr := ipaddr.MACAddress{} cachedMAC = append(cachedMAC, &zeroMACAddr) //fmt.Printf("testing %d MACS\n", len(cachedMAC)) testGenericKeys[*ipaddr.MACAddress](t, cachedMAC) addrs := make([]*ipaddr.Address, 0, len(cached)+1) for _, addr := range cached { addrs = append(addrs, addr.ToAddressBase()) } addrs = append(addrs, &zeroAddr, zeroMACAddr.ToAddressBase()) testGenericKeys[*ipaddr.Address](t, addrs) t.testKeys(addrs) } } } } key4 := ipaddr.Key[*ipaddr.IPv4Address]{} key6 := ipaddr.Key[*ipaddr.IPv6Address]{} ipKey := ipaddr.Key[*ipaddr.IPAddress]{} macKey := ipaddr.Key[*ipaddr.MACAddress]{} key := ipaddr.Key[*ipaddr.Address]{} zeroAddr := ipaddr.Address{} zeroMACAddr := ipaddr.MACAddress{} keyEquals(t, key4, zero4Addr.ToGenericKey()) keyEquals(t, key6, zero6Addr.ToGenericKey()) keyEquals(t, ipKey, zeroIPAddr.ToGenericKey()) keyEquals(t, key, zeroAddr.ToGenericKey()) keyEquals(t, macKey, zeroMACAddr.ToGenericKey()) equals(t, key4.ToAddress(), &zero4Addr) equals(t, key6.ToAddress(), &zero6Addr) equals(t, macKey.ToAddress(), &zeroMACAddr) equals(t, ipKey.ToAddress(), &zeroIPAddr) equals(t, key.ToAddress(), &zeroAddr) ipv4key := ipaddr.IPv4AddressKey{} ipv6key := ipaddr.IPv6AddressKey{} macAddrKey := ipaddr.MACAddressKey{} keyEquals(t, ipv4key, zero4Addr.ToKey()) keyEquals(t, ipv6key, zero6Addr.ToKey()) keyEquals(t, macAddrKey, zeroMACAddr.ToKey()) equals(t, ipv4key.ToAddress(), &zero4Addr) equals(t, ipv6key.ToAddress(), &zero6Addr) equals(t, macAddrKey.ToAddress(), &zeroMACAddr) } var ( zeroIPAddr = ipaddr.IPAddress{} zero4Addr = ipaddr.IPv4Address{} zero6Addr = ipaddr.IPv6Address{} ) func keyEquals[TE interface { addFailure(failure) }, T interface { comparable fmt.Stringer }](t TE, one, two T) { if two != one { f := newAddrFailure("comparison of "+one.String()+" with "+two.String(), nil) t.addFailure(f) } } func (t keyTester) testNetNetIPs(cached []*ipaddr.IPAddress) { // test that key creation and address creation from keys works for _, addr := range cached { addr1Lower := addr.GetLower() addr1Upper := addr.GetLower() addr2Lower := ipaddr.NewIPAddressFromNetNetIPAddr(addr1Lower.GetNetNetIPAddr()) addr2Upper := ipaddr.NewIPAddressFromNetNetIPAddr(addr1Upper.GetNetNetIPAddr()) equals(t, addr1Lower, addr2Lower) equals(t, addr1Upper, addr2Upper) if addrv4 := addr.ToIPv4(); addrv4 != nil { addr1Lower := addrv4.GetLower() addr1Upper := addrv4.GetLower() addr2Lower := ipaddr.NewIPAddressFromNetNetIPAddr(addr1Lower.GetNetNetIPAddr()).ToIPv4() addr2Upper := ipaddr.NewIPAddressFromNetNetIPAddr(addr1Upper.GetNetNetIPAddr()).ToIPv4() equals(t, addr1Lower, addr2Lower) equals(t, addr1Upper, addr2Upper) } if addrv6 := addr.ToIPv6(); addrv6 != nil { addr1Lower := addrv6.GetLower() addr1Upper := addrv6.GetLower() addr2Lower := ipaddr.NewIPAddressFromNetNetIPAddr(addr1Lower.GetNetNetIPAddr()).ToIPv6() addr2Upper := ipaddr.NewIPAddressFromNetNetIPAddr(addr1Upper.GetNetNetIPAddr()).ToIPv6() equals(t, addr1Lower, addr2Lower) equals(t, addr1Upper, addr2Upper) } } } func (t keyTester) testNetIPAddrs(cached []*ipaddr.IPAddress) { // test that key creation and address creation from keys works for _, addr := range cached { addr1Lower := addr.GetLower() addr1Upper := addr.GetLower() if addr.IsIPv6() { if addr1Lower.ToIPv6().IsIPv4Mapped() { // net.IP will switch to IPv4, so we might as well just do that ourselves addr4, _ := addr1Lower.ToIPv6().GetEmbeddedIPv4Address() addr1Lower = addr4.ToIP() } if addr1Upper.ToIPv6().IsIPv4Mapped() { addr4, _ := addr1Upper.ToIPv6().GetEmbeddedIPv4Address() addr1Upper = addr4.ToIP() } } addr2Lower, _ := ipaddr.NewIPAddressFromNetIPAddr(addr1Lower.GetNetIPAddr()) addr2Upper, _ := ipaddr.NewIPAddressFromNetIPAddr(addr1Upper.GetUpperNetIPAddr()) equals(t, addr1Lower, addr2Lower) equals(t, addr1Upper, addr2Upper) if addrv4 := addr.ToIPv4(); addrv4 != nil { addr1Lower := addrv4.GetLower() addr1Upper := addrv4.GetLower() addr2Lower, _ := ipaddr.NewIPAddressFromNetIPAddr(addr1Lower.GetNetIPAddr()) addr2Upper, _ := ipaddr.NewIPAddressFromNetIPAddr(addr1Upper.GetUpperNetIPAddr()) addr2Lower3 := addr2Lower.ToIPv4() addr2Upper3 := addr2Upper.ToIPv4() equals(t, addr1Lower, addr2Lower3) equals(t, addr1Upper, addr2Upper3) } if addrv6 := addr.ToIPv6(); addrv6 != nil { addr1Lower := addrv6.GetLower() addr1Upper := addrv6.GetLower() if !addr1Lower.IsIPv4Mapped() { // net.IP will switch to IPv4, so we might as well just do that ourselves addr2Lower, _ := ipaddr.NewIPAddressFromNetIPAddr(addr1Lower.GetNetIPAddr()) addr2Lower3 := addr2Lower.ToIPv6() equals(t, addr1Lower, addr2Lower3) } if !addr1Upper.IsIPv4Mapped() { addr2Upper, _ := ipaddr.NewIPAddressFromNetIPAddr(addr1Upper.GetUpperNetIPAddr()) addr2Upper3 := addr2Upper.ToIPv6() equals(t, addr1Upper, addr2Upper3) } } t.incrementTestCount() } } func (t keyTester) testNetIPs(cached []*ipaddr.IPAddress) { // test that key creation and address creation from keys works for _, addr := range cached { addr1Lower := addr.GetLower() addr1Upper := addr.GetLower() if addr.IsIPv6() { if addr.ToIPv6().HasZone() { // net.IP cannot store zone, so we need to drop it to check equality addr1Lower = addr1Lower.ToIPv6().WithoutZone().ToIP() addr1Upper = addr1Upper.ToIPv6().WithoutZone().ToIP() } if addr1Lower.ToIPv6().IsIPv4Mapped() { // net.IP will switch to IPv4, so we might as well just do that ourselves addr4, _ := addr1Lower.ToIPv6().GetEmbeddedIPv4Address() addr1Lower = addr4.ToIP() } if addr1Upper.ToIPv6().IsIPv4Mapped() { addr4, _ := addr1Upper.ToIPv6().GetEmbeddedIPv4Address() addr1Upper = addr4.ToIP() } } addr2Lower, _ := ipaddr.NewIPAddressFromNetIP(addr1Lower.GetNetIP()) addr2Upper, _ := ipaddr.NewIPAddressFromNetIP(addr1Upper.GetUpperNetIP()) equals(t, addr1Lower, addr2Lower) equals(t, addr1Upper, addr2Upper) if addrv4 := addr.ToIPv4(); addrv4 != nil { addr1Lower := addrv4.GetLower() addr1Upper := addrv4.GetLower() addr2Lower, _ := ipaddr.NewIPAddressFromNetIPAddr(addr1Lower.GetNetIPAddr()) addr2Upper, _ := ipaddr.NewIPAddressFromNetIPAddr(addr1Upper.GetUpperNetIPAddr()) addr2Lower3 := addr2Lower.ToIPv4() addr2Upper3 := addr2Upper.ToIPv4() equals(t, addr1Lower, addr2Lower3) equals(t, addr1Upper, addr2Upper3) } if addrv6 := addr.ToIPv6(); addrv6 != nil { addr1Lower := addrv6.GetLower() addr1Upper := addrv6.GetLower() if addrv6.HasZone() { // net.IP cannot store zone, so we need to drop it to check equality addr1Lower = addr1Lower.WithoutZone() addr1Upper = addr1Upper.WithoutZone() } if !addr1Lower.IsIPv4Mapped() { // net.IP will switch to IPv4, so we might as well just do that ourselves addr2Lower, _ := ipaddr.NewIPAddressFromNetIPAddr(addr1Lower.GetNetIPAddr()) addr2Lower3 := addr2Lower.ToIPv6() equals(t, addr1Lower, addr2Lower3) } if !addr1Upper.IsIPv4Mapped() { addr2Upper, _ := ipaddr.NewIPAddressFromNetIPAddr(addr1Upper.GetUpperNetIPAddr()) addr2Upper3 := addr2Upper.ToIPv6() equals(t, addr1Upper, addr2Upper3) } } t.incrementTestCount() } } func (t keyTester) testKeys(cached []*ipaddr.Address) { // test that key creation and address creation from keys works //var ipv4Count, ipv6Count, macCount uint64 for _, addr := range cached { addr2 := addr.ToKey().ToAddress() equals(t, addr, addr2) if ipAddr := addr.ToIP(); ipAddr != nil { other := ipAddr.ToKey().ToAddress() equals(t, ipAddr, other) ipRange := ipaddr.NewSequentialRange(ipAddr, &zeroIPAddr) ipRangeBack := ipRange.ToKey().ToSeqRange() equals(t, ipRangeBack.GetLower(), &zeroIPAddr) equals(t, ipRangeBack.GetUpper(), ipAddr) if !ipAddr.IsMax() { oneUp := ipAddr.Increment(1) ipRange := ipaddr.NewSequentialRange(ipAddr, oneUp) ipRangeBack := ipRange.ToKey().ToSeqRange() equals(t, ipRangeBack.GetUpper(), oneUp) equals(t, ipRangeBack.GetLower(), ipAddr) } } if addrv4 := addr.ToIPv4(); addrv4 != nil { other := addrv4.ToKey().ToAddress() equals(t, addrv4, other) ipRange := ipaddr.NewSequentialRange(addrv4, &zero4Addr) ipRangeBack := ipRange.ToKey().ToSeqRange() equals(t, ipRangeBack.GetLower(), &zero4Addr) equals(t, ipRangeBack.GetUpper(), addrv4) if !addrv4.IsMax() { oneUp := addrv4.Increment(1) ipRange := ipaddr.NewSequentialRange(addrv4, oneUp) ipRangeBack := ipRange.ToKey().ToSeqRange() equals(t, ipRangeBack.GetUpper(), oneUp) equals(t, ipRangeBack.GetLower(), addrv4) } //ipv4Count++ } if addrv6 := addr.ToIPv6(); addrv6 != nil { other := addrv6.ToKey().ToAddress() equals(t, addrv6, other) ipRange := ipaddr.NewSequentialRange(addrv6, &zero6Addr) ipRangeBack := ipRange.ToKey().ToSeqRange() equals(t, ipRangeBack.GetLower(), &zero6Addr) equals(t, ipRangeBack.GetUpper(), addrv6) if !addrv6.IsMax() { oneUp := addrv6.Increment(1) ipRange := ipaddr.NewSequentialRange(addrv6, oneUp) ipRangeBack := ipRange.ToKey().ToSeqRange() equals(t, ipRangeBack.GetUpper(), oneUp) equals(t, ipRangeBack.GetLower(), addrv6) } //ipv6Count++ } if addrmac := addr.ToMAC(); addrmac != nil { other := addrmac.ToKey().ToAddress() equals(t, addrmac, other) //macCount++ } t.incrementTestCount() } } type AddrConstraint[T ipaddr.KeyConstraint[T]] interface { ipaddr.GenericKeyConstraint[T] ipaddr.AddressType } func testGenericKeys[T AddrConstraint[T]](t keyTester, cached []T) { for _, addr := range cached { addr2 := addr.ToGenericKey().ToAddress() equals(t, addr, addr2) t.incrementTestCount() } } func equals[TE interface{ addFailure(failure) }, T ipaddr.AddressType](t TE, one, two T) { if !one.Equal(two) || !two.Equal(one) { f := newAddrFailure("comparison of "+one.String()+" with "+two.String(), two.ToAddressBase()) t.addFailure(f) } else if one.Compare(two) != 0 || two.Compare(one) != 0 { f := newAddrFailure("comparison of "+one.String()+" with "+two.String(), two.ToAddressBase()) t.addFailure(f) } } ipaddress-go-1.5.4/ipaddr/test/macaddrrangetest.go000066400000000000000000001336041440250641600221740ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package test import ( "math" "strconv" "github.com/seancfoley/ipaddress-go/ipaddr" ) type macAddressRangeTester struct { macAddressTester } func (t macAddressRangeTester) run() { t.mactest(true, "*-abcdef|fffffe") // IncompatibleAddressError t.mactest(true, "0|ffffff-abcdef|fffffe") // IncompatibleAddressError t.mactest(true, "*-ab0000|ffffff") t.mactest(true, "0|ffffff-ab0000|ffffff") t.mactest(false, "*|1") t.mactest(false, "1|*") t.mactest(true, "*-1") t.mactest(true, "1-*") t.mactest(true, "*-*") t.mactest(true, "*:1") t.mactest(true, "1:*") t.mactest(true, "*:*") t.mactest(false, "1:1") t.mactest(false, "1-1") t.mactest(true, "0xaabbccddeeee-0xaabbccddeeff") t.mactest(true, "0xaabbccddeeee-aabbccddeeff") t.mactest(true, "aabbccddeeee-0xaabbccddeeff") t.mactest(true, "aabbccddeeee-aabbccddeeff") t.mactest(t.allowsRange(), "aa-|1-*-d-ee-f") t.mactest(t.allowsRange(), "|1-aa-cc-*-ee-f") t.mactest(t.allowsRange(), "aa-1|-cc-d-*-f") t.mactest(t.allowsRange(), "1|-aa-cc-d-*") t.mactest(t.allowsRange(), "aa-0|1-cc-*") t.mactest(t.allowsRange(), "aa-1|ff-*") t.mactest(t.allowsRange(), "*-1|ff-*") t.mactest(true, "aa0000|abffff-ddeeff") t.mactest(true, "aa0000|abffff-*") t.mactest(false, "aabbcc|ddeeff-ddeefff") t.mactest(false, "aabbcc|aabbcd-ddeefffff") t.mactest(true, "aabbcc|aabbcd-ddeeffffff") t.mactest(false, "aabbcc|aabbcd-ddeefffffff") t.mactest(true, "aabbcc|aabbcd-*") t.mactest(false, "aabbcc|aabbcd-ddeefffffff") t.mactest(true, "ddeeff-aa0000|afffff") t.mactest(true, "*-aa0000|afffff") t.mactest(false, "ddeefff-aabbcc|ddeeff") t.mactest(true, "ddeeff-aabbffffcc|aabbffffdd") t.mactest(false, "ddeeff-aabbffffccc|aabbffffddd") t.mactest(false, "ddeeff-aabbffffc|aabbffffd") t.mactest(false, "ddeefffffff-aabbcc|aabbcd") t.mactest(true, "000000000000-abcdefabcdef") t.mactest(true, "000000000000-ffffffffffff") t.mactest(true, "abcdefabcdef-ffffffffffff") t.mactest(true, "000000000000-abcdefabcdef") t.mactest(true, "000000000000-ffffffffffff") t.mactest(true, "abcdefabcdefabcd-ffffffffffffffff") t.mactest(true, "000000000000-") t.mactest(true, "-000000000010") t.mactest(true, "-abcdefabcdefabcd") t.mactest(true, "abcdefabcdefabcd-") // length mismatch t.mactest(false, "0000000000001-abcdefabcdef") t.mactest(false, "000000000000-fffffffffffff") t.mactest(false, "abcdefabcdeff-ffffffffffff") t.mactest(false, "000000000000-abcdefabcdeff") t.mactest(false, "0000000000-ffffffffffff") t.mactest(false, "abcdefabcdef-ffffffff") t.mactest(false, "00000000000-") t.mactest(false, "-00000000001") t.mactest(false, "-abcdefabcdefabcde") t.mactest(false, "abcdefabcdefabcde-") t.mactest(false, "000000000000-abcdefabcdefabcd") t.mactest(false, "0000000000000000-ffffffffffff") t.mactest(false, "efabcdefabcd-ffffffffffffffff") t.mactest(false, "0000000000000000-abcdefabcdef") t.mactest(false, "0000000000000000-ffffffffffff") t.mactest(false, "abcdefabcdefabc-fffffffffffffff") t.mactest(true, "11:11:11:ff:_:0") t.mactest(true, "_:11:11:11:1:1") t.mactest(true, "1:2:3:4:_:6:7:8") t.mactest(true, "1:2:_:4:5:6:8:7") t.mactest(true, "1:3:4:5:6:_") t.mactest(true, "1:2:3:_:5:6:8:7") t.mactest(true, "_:2:3:5:ff:8") t.mactest(true, "1:11:11:11:1:_") t.mactest(true, "_:2:3:4:5:6:7:7") t.mactest(true, "_:2:3:4:5:6") t.mactest(true, "1:5:2:3:4:_") t.mactest(true, "11:11:11:ff:__:0") t.mactest(true, "__:11:11:11:1:1") t.mactest(true, "1:2:3:4:__:6:7:8") t.mactest(true, "1:2:__:4:5:6:7:8") t.mactest(true, "1:2:3:4:5:__") t.mactest(true, "1:2:3:__:5:8") t.mactest(true, "__:2:3:4:5:8") t.mactest(true, "1:2:3:4:5:__") t.mactest(true, "__:2:3:4:5:6:7:8") t.mactest(true, "__:2:3:4:5:6") t.mactest(true, "1:2:3:3:4:__") t.mactest(false, "11:11:11:ff:___:0") t.mactest(false, "___:11:11:11:1:1") t.mactest(false, "1:2:3:4:___:6:7:8") t.mactest(false, "1:2:___:4:5:6:7:8") t.mactest(false, "1:2:3:4:5:___") t.mactest(false, "1:2:3:___:5:8") t.mactest(false, "___:2:3:4:5:8") t.mactest(false, "1:2:3:4:5:___") t.mactest(false, "___:2:3:4:5:6:7:8") t.mactest(false, "___:2:3:4:5:6") t.mactest(false, "1:2:3:4:5:___") t.mactest(false, "11:11:11:ff:_2_:0") t.mactest(false, "_2_:11:11:11:1:1") t.mactest(false, "1:2:3:4:_2_:6:7:8") t.mactest(false, "1:2:_2_:4:5:6:7:8") t.mactest(false, "1:2:3:4:5:_2_") t.mactest(false, "1:2:3:_2_:5:8") t.mactest(false, "_2_:2:3:4:5:8") t.mactest(false, "1:2:3:4:5:_2_") t.mactest(false, "_2_:2:3:4:5:6:7") t.mactest(false, "_2_:2:3:4:5:6") t.mactest(false, "11:11:11:ff:_2:0") t.mactest(false, "_2:11:11:11:1:1") t.mactest(false, "1:2:3:4:_2:6:7:8") t.mactest(false, "1:2:_2:4:5:6:7:8") t.mactest(false, "1:2:3:4:5:_2") t.mactest(false, "1:2:3:_2:5:8") t.mactest(false, "_2:2:3:4:5:8") t.mactest(false, "1:2:3:4:5:_2") t.mactest(false, "_2:2:3:4:5:6:7:8") t.mactest(false, "_2:2:3:4:5:6") t.mactest(true, "11:11:11:ff:2_:0") t.mactest(true, "2_:11:11:11:1:1") t.mactest(true, "1:2:3:4:2_:6:7:8") t.mactest(true, "1:2:2_:4:5:6:7:8") t.mactest(true, "1:2:3:4:5:2_") t.mactest(true, "1:2:3:2_:5:8") t.mactest(true, "2_:2:3:4:5:8") t.mactest(true, "1:2:3:4:5:2_") t.mactest(true, "2_:2:3:4:5:6:7:8") t.mactest(true, "2_:2:3:4:5:6") t.testEquivalentPrefix("*:*", 0) t.testEquivalentPrefix("*:*:*:*:*:*", 0) t.testEquivalentPrefix("*:*:*:*:*:*:*:*", 0) t.testEquivalentPrefix("80-ff:*", 1) t.testEquivalentPrefix("0-7f:*", 1) t.testEquivalentPrefix("1:2:*", 16) t.testEquivalentPrefix("1:2:*:*:*:*", 16) t.testEquivalentMinPrefix("1:2:*:0:*:*", nil, 32) t.testEquivalentMinPrefix("1:2:*:0:0:0", nil, 48) t.testEquivalentPrefix("1:2:80-ff:*", 17) t.testEquivalentPrefix("1:2:00-7f:*", 17) t.testEquivalentPrefix("1:2:c0-ff:*", 18) t.testEquivalentPrefix("1:2:00-3f:*", 18) t.testEquivalentPrefix("1:2:80-bf:*", 18) t.testEquivalentPrefix("1:2:40-7f:*", 18) t.testEquivalentPrefix("1:2:fc-ff:*", 22) t.testEquivalentPrefix("1:2:fc-ff:0-ff:*", 22) t.testEquivalentMinPrefix("1:2:fd-ff:0-ff:*", nil, 24) t.testEquivalentMinPrefix("1:2:fc-ff:0-fe:*", nil, 32) t.testEquivalentMinPrefix("1:2:fb-ff:0-fe:*", nil, 32) t.testEquivalentMinPrefix("1:2:fb-ff:0-ff:*", nil, 24) t.testReverse("1:2:*:4:5:6", false, false) t.testReverse("1:1:1-ff:2:3:3", false, false) t.testReverse("1:1:0-fe:1-fe:*:1", false, false) t.testReverse("ff:80:*:ff:01:ff", false, false) t.testReverse("ff:80:fe:7f:01:ff", true, false) t.testReverse("ff:80:*:*:01:ff", true, false) t.testReverse("ff:81:ff:*:1-fe:ff", false, true) t.testReverse("ff:81:c3:42:24:0-fe", false, true) t.testReverse("ff:1:ff:ff:*:*", false, false) t.testIncrement("ff:ff:ff:ff:ff:1-2:2-3:ff", 0, "ff:ff:ff:ff:ff:1:2:ff") t.testIncrement("ff:ff:ff:ff:ff:1-2:2-3:ff", 2, "ff:ff:ff:ff:ff:2:2:ff") t.testIncrement("ff:ff:ff:ff:ff:1-2:2-3:ff", 3, "ff:ff:ff:ff:ff:2:3:ff") t.testIncrement("ff:ff:ff:ff:ff:1-2:2-3:ff", 4, "ff:ff:ff:ff:ff:2:4:0") t.testIncrement("ff:ff:ff:ff:ff:fe-ff:fe-ff:ff", 4, "") t.testIncrement("ff:ff:ff:1-2:2-3:ff", 0, "ff:ff:ff:1:2:ff") t.testIncrement("ff:ff:ff:1-2:2-3:ff", 2, "ff:ff:ff:2:2:ff") t.testIncrement("ff:ff:ff:1-2:2-3:ff", 3, "ff:ff:ff:2:3:ff") t.testIncrement("ff:ff:ff:1-2:2-3:ff", 4, "ff:ff:ff:2:4:0") t.testIncrement("ff:ff:ff:fe-ff:fe-ff:ff", 4, "") t.testIncrement("ff:ff:ff:ff:ff:1-2:2-3:ff", -0x102fb, "ff:ff:ff:ff:ff:0:0:4") t.testIncrement("ff:ff:ff:ff:ff:1-2:2-3:ff", -0x102fc, "ff:ff:ff:ff:ff:0:0:3") t.testIncrement("ff:ff:ff:ff:ff:1-2:2-3:ff", -0x102ff, "ff:ff:ff:ff:ff:0:0:0") t.testIncrement("ff:ff:ff:ff:ff:1-2:2-3:ff", -0x10300, "ff:ff:ff:ff:fe:ff:ff:ff") t.testIncrement("0:0:0:0:0:1-2:2-3:ff", -0x10300, "") t.testIncrement("ff:ff:ff:1-2:2-3:ff", -0x102fb, "ff:ff:ff:0:0:4") t.testIncrement("ff:ff:ff:1-2:2-3:ff", -0x102fc, "ff:ff:ff:0:0:3") t.testIncrement("ff:ff:ff:1-2:2-3:ff", -0x102ff, "ff:ff:ff:0:0:0") t.testIncrement("ff:ff:ff:1-2:2-3:ff", -0x10300, "ff:ff:fe:ff:ff:ff") t.testIncrement("0:0:0:1-2:2-3:ff", -0x10300, "") t.testIncrement("ff:3-4:ff:ff:ff:1-2:2-3:0", 6, "ff:4:ff:ff:ff:2:2:0") t.testIncrement("ff:3-4:ff:ff:ff:1-2:2-3:0", 8, "ff:4:ff:ff:ff:2:3:1") t.testIncrement("3-4:ff:ff:1-2:2-3:0", 6, "4:ff:ff:2:2:0") t.testIncrement("3-4:ff:ff:1-2:2-3:0", 8, "4:ff:ff:2:3:1") t.testPrefix("25:51:27:*:*:*", p24, p24) t.testPrefix("25:50-51:27:*:*:*", p24, nil) t.testPrefix("25:51:27:12:82:55", nil, p48) t.testPrefix("*:*:*:*:*:*", p0, p0) t.testPrefix("*:*:*:*:*:*:*:*", p0, p0) t.testPrefix("*:*:*:*:*:*:0-fe:*", p56, nil) t.testPrefix("*:*:*:*:*:*:0-ff:*", p0, p0) t.testPrefix("*:*:*:*:*:*:0-7f:*", p49, nil) t.testPrefix("*:*:*:*:*:*:80-ff:*", p49, nil) t.testPrefix("*.*.*.*", p0, p0) t.testPrefix("3.*.*.*", p16, p16) t.testPrefix("3.*.*.1-3", nil, nil) t.testPrefix("3.0-7fff.*.*", p17, p17) t.testPrefix("3.8000-ffff.*.*", p17, p17) t.testPrefixes("25:51:27:*:*:*", 16, -5, "25:51:27:00:*:*", "25:51:0:*:*:*", "25:51:20:*:*:*", "25:51:0:*:*:*", "25:51:0:*:*:*") t.testPrefixes("*:*:*:*:*:*:0-fe:*", 15, 2, "*:*:*:*:*:*:0-fe:0", "*:*:*:*:*:*:0:*", "*:*:*:*:*:*:0-fe:0-3f", "*:00-fe:00:00:00:00:00:*", "*:00-fe:00:00:00:00:00:*") t.testPrefixes("*:*:*:*:*:*:*:*", 15, 2, "0:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "0-3f:*:*:*:*:*:*:*", "0:0-1:*:*:*:*:*:*", "*:*:*:*:*:*:*:*") t.testPrefixes("1:*:*:*:*:*", 15, 2, "1:0:*:*:*:*", "0:*:*:*:*:*", "1:0-3f:*:*:*:*", "1:0-1:*:*:*:*", "1:*:*:*:*:*") t.testPrefixes("3.8000-ffff.*.*", 15, 2, "3.8000-80ff.*.*", "00:03:00-7f:*:*:*:*:*", "3.8000-9fff.*.*", "00:02:00-7f:*:*:*:*:*", "00:02:00-7f:*:*:*:*:*") t.testPrefixes("3.8000-ffff.*.*", 31, 2, "3.8000-80ff.*.*", "00:03:00-7f:*:*:*:*:*", "3.8000-9fff.*.*", "3.8000-8001.*.*", "3.8000-ffff.*.*") t.testStrings() t.testMACCount("11:22:33:44:55:ff", 1) t.testMACCount("11:22:*:0-2:55:ff", 3*0x100) t.testMACCount("11:22:2:0-2:55:*", 3*0x100) t.testMACCount("11:2-4:1:0-2:55:ff", 9) t.testMACCount("112-114.1.0-2.55ff", 9) t.testMACCount("*.1.0-2.55ff", 3*0x10000) t.testMACCount("1-2.1-2.1-2.2-3", 16) t.testMACCount("1-2.1.*.2-3", 4*0x10000) t.testMACCount("11:*:*:0-2:55:ff", 3*0x100*0x100) t.testMACPrefixCount("11:22:*:0-2:55:ff", 3*0x100) t.testMACPrefixCount("11:22:*:0-2:55:*", 3*0x100) t.testMACPrefixCount("11:22:1:2:55:*", 1) t.testToOUIPrefixed("25:51:27:*:*:*") t.testToOUIPrefixed("*:*:*:*:*:*") t.testToOUIPrefixed("*:*:*:25:51:27") t.testToOUIPrefixed("ff:ee:25:51:27:*:*:*") t.testToOUIPrefixed("*:*:*:*:*:*:*:*") t.testToOUIPrefixed("*:*:*:25:51:27:ff:ee") t.testToOUIPrefixed("123.456.789.abc") t.testToOUIPrefixed("123.456.789.*") t.testOUIPrefixed("ff:7f:fe:2:7f:fe", "ff:7f:fe:*", 24) t.testOUIPrefixed("ff:7f:fe:2:7f:*", "ff:7f:fe:*", 24) t.testOUIPrefixed("ff:7f:fe:*", "ff:7f:fe:*", 24) t.testOUIPrefixed("ff:*", "ff:*", 8) t.testOUIPrefixed("ff:7f:fe:2:7f:fe:7f:fe", "ff:7f:fe:*:*:*:*:*", 24) t.testOUIPrefixed("ff:7f:0-f:*", "ff:7f:0-f:*", 20) t.testRadices("11:10:*:1-7f:f3:2", "10001:10000:*:1-1111111:11110011:10", 2) t.testRadices("0:1:0:1:0-1:1:0:1", "0:1:0:1:0-1:1:0:1", 2) t.testRadices("f3-ff:7f:fe:*:7_:fe", "f3-ff:7f:fe:*:70-7f:fe", 16) t.testRadices("*:1:0:1:0-1:1:0:1", "*:1:0:1:0-1:1:0:1", 16) t.testRadices("ff:7f:*:2:7_:fe", "255:127:*:2:112-127:254", 10) t.testRadices("*:1:0:1:0-1:1:0:1", "*:1:0:1:0-1:1:0:1", 10) t.testRadices("ff:*:fe:2:7d-7f:fe", "513:*:512:2:236-241:512", 7) t.testRadices("1:0:0-1:0:1:*", "1:0:0-1:0:1:*", 7) t.testRadices("ff:70-7f:fe:2:*:fe", "377:160-177:376:2:*:376", 8) t.testRadices("1:0:0-1:0:1:*", "1:0:0-1:0:1:*", 8) t.testRadices("ff:7f:fa-fe:2:7f:*", "120:87:11a-11e:2:87:*", 15) t.testRadices("1:0:0-1:0:1:*", "1:0:0-1:0:1:*", 15) t.testMatches(true, "aa:-1:cc:d:ee:f", "aa:0-1:cc:d:ee:f") t.testMatches(true, "aa:-:cc:d:ee:f", "aa:*:cc:d:ee:f") t.testMatches(true, "-:-:cc:d:ee:f", "*:cc:d:ee:f") t.testMatches(true, "aa:-dd:cc:d:ee:f", "aa:0-dd:cc:d:ee:f") t.testMatches(true, "aa:1-:cc:d:ee:f", "aa:1-ff:cc:d:ee:f") t.testMatches(true, "-1:aa:cc:d:ee:f", "0-1:aa:cc:d:ee:f") t.testMatches(true, "1-:aa:cc:d:ee:f", "1-ff:aa:cc:d:ee:f") t.testMatches(true, "aa:cc:d:ee:f:1-", "aa:cc:d:ee:f:1-ff") t.testMatches(true, "aa-|1-cc-d-ee-f", "aa-0|1-cc-d-ee-f") t.testMatches(true, "|1-aa-cc-d-ee-f", "0|1-aa-cc-d-ee-f") t.testMatches(true, "aa-1|-cc-d-ee-f", "aa-1|ff-cc-d-ee-f") t.testMatches(true, "1|-aa-cc-d-ee-f", "1|ff-aa-cc-d-ee-f") t.testMatches(true, "|-aa-cc-d-ee-f", "*-aa-cc-d-ee-f") t.testMatches(true, "|-|-cc-d-ee-f", "*-cc-d-ee-f") t.testMatches(true, "|-|-cc-d-ee-|", "*-*-cc-d-ee-*") t.testMatches(true, "|-|-cc-d-ee-2|", "*-*-cc-d-ee-2|ff") t.testMatches(true, "|-|-cc-d-ee-|2", "*-*-cc-d-ee-0|2") t.testMatches(true, "*-|-*", "*-*") t.testMatches(true, "*-|-|", "*-*") t.testMatches(true, "|-|-*", "*:*") t.testMatches(true, "*:*:*:*:*:*", "*:*") t.testMatches(true, "1:*:*:*:*:*", "1:*") t.testMatches(true, "*:*:*:*:*:1", "*:1") t.testMatches(true, "*:*:*:12:34:56", "*-123456") t.testMatches(true, "12:34:56:*:*:*", "123456-*") t.testMatches(true, "1:*:*:*:*:*", "1-*") t.testMatches(true, "*:*:*:*:*:1", "*-1") t.testMatches(true, "*-*-*", "*:*:*") t.testMatches(true, "*-*", "*:*:*") t.testMatches(true, "bbaacc0dee0f", "bb:aa:cc:d:ee:f") t.testMatches(true, "bbaacc0dee0faab0", "bb:aa:cc:d:ee:f:aa:b0") t.testMatches(false, "*-abcdef|fffffe", "0|ffffff-abcdef|fffffe") // inet.ipaddr.IncompatibleAddressException: *-abcdef|fffffe, IP Address error: range of joined segments cannot be divided into individual ranges t.testMatches(true, "*-ab0000|ffffff", "0|ffffff-ab0000|ffffff") t.testMatches(true, "*-ab|fe-aa-aa-aa-aa", "0|ff-ab|fe-aa-aa-aa-aa") // inferred lower and upper boundaries // single segment t.testMatches(true, "-abffffffffff", "000000000000-abffffffffff") t.testMatches(true, "000000000000-", "000000000000-ffffffffffff") t.testMatches(true, "ab0000000000-", "ab0000000000-ffffffffffff") t.testMatches(true, "-0xabffffffffff", "000000000000-abffffffffff") t.testMatches(true, "0x000000000000-", "000000000000-ffffffffffff") t.testMatches(true, "0xab0000000000-", "ab0000000000-ffffffffffff") t.testMatches(true, "-abffffffffffffff", "0000000000000000-abffffffffffffff") t.testMatches(true, "0000000000000000-", "0000000000000000-ffffffffffffffff") t.testMatches(true, "ab00000000000000-", "ab00000000000000-ffffffffffffffff") t.testMatches(true, "-0xabffffffffffffff", "0000000000000000-abffffffffffffff") t.testMatches(true, "0x0000000000000000-", "0000000000000000-ffffffffffffffff") t.testMatches(true, "0xab00000000000000-", "ab00000000000000-ffffffffffffffff") // dotted t.testMatches(true, "f302.3304.-06ff", "f302.3304.0-06ff") t.testMatches(true, "f302.-06ff.3304", "f302.0-06ff.3304") t.testMatches(true, "-06ff.f302.3304", "0-06ff.f302.3304") t.testMatches(true, "f302.3304.ffff.-06ff", "f302.3304.ffff.0-06ff") t.testMatches(true, "f302.3304.-06ff.ffff", "f302.3304.0-06ff.ffff") t.testMatches(true, "f302.-06ff.3304.ffff", "f302.0-06ff.3304.ffff") t.testMatches(true, "-06ff.f302.3304.ffff", "0-06ff.f302.3304.ffff") t.testMatches(true, "f302.3304.100-", "f302.3304.100-ffff") t.testMatches(true, "f302.100-.3304", "f302.100-ffff.3304") t.testMatches(true, "100-.f302.3304", "100-ffff.f302.3304") t.testMatches(true, "f302.3304.ffff.1100-", "f302.3304.ffff.1100-ffff") t.testMatches(true, "f302.3304.1100-.ffff", "f302.3304.1100-ffff.ffff") t.testMatches(true, "f302.1100-.3304.ffff", "f302.1100-ffff.3304.ffff") t.testMatches(true, "1100-.f302.3304.ffff", "1100-ffff.f302.3304.ffff") // colon t.testMatches(true, "aa-:bb:cc:dd:ee:ff", "aa-ff:bb:cc:dd:ee:ff") t.testMatches(true, "aa-:bb-:cc:dd:ee:ff", "aa-ff:bb-ff:cc:dd:ee:ff") t.testMatches(true, "aa-:bb:cc-:dd:ee:ff", "aa-ff:bb:cc-ff:dd:ee:ff") t.testMatches(true, "aa-:bb:cc:dd-:ee:ff", "aa-ff:bb:cc:dd-ff:ee:ff") t.testMatches(true, "aa-:bb:cc:dd:ee-:ff", "aa-ff:bb:cc:dd:ee-ff:ff") t.testMatches(true, "aa-:bb:cc:dd:ee:ff-", "aa-ff:bb:cc:dd:ee:ff") t.testMatches(true, "aa-:bb:cc:dd:ee:ee-", "aa-ff:bb:cc:dd:ee:ee-ff") t.testMatches(true, "aa-:bb:cc:dd:ee:ee-:aa:bb", "aa-ff:bb:cc:dd:ee:ee-ff:aa:bb") t.testMatches(true, "aa-:bb:cc:dd:ee:ee:aa-:bb", "aa-ff:bb:cc:dd:ee:ee:aa-ff:bb") t.testMatches(true, "aa-:bb:cc:dd:ee:ee:aa:bb-", "aa-ff:bb:cc:dd:ee:ee:aa:bb-ff") t.testMatches(true, "-ff:bb:cc:dd:ee:ff", "00-ff:bb:cc:dd:ee:ff") t.testMatches(true, "-ff:-bb:cc:dd:ee:ff", "00-ff:00-bb:cc:dd:ee:ff") t.testMatches(true, "-ff:-bb:0-cc:dd:ee:ff", "00-ff:00-bb:-cc:dd:ee:ff") t.testMatches(true, "ff:-bb:0-cc:dd-0:ee:ff", "ff:00-bb:-cc:-dd:ee:ff") // reverse range t.testMatches(true, "ff:-bb:0-cc:0-dd:ee-:ff", "ff:00-bb:-cc:-dd:ee-ff:ff") t.testMatches(true, "ff:-bb:0-cc:0-dd:ee-:-ff", "ff:00-bb:-cc:-dd:ee-ff:0-ff") t.testMatches(true, "ff:-bb:0-cc:0-dd:ee-:-ff:0-aa:bb", "ff:00-bb:-cc:-dd:ee-ff:0-ff:-aa:bb") t.testMatches(true, "ff:-bb:0-cc:0-dd:ee-:-ff:0-aa:bb-", "ff:bb-0:-cc:-dd:ee-ff:0-ff:-aa:bb-ff") // end inferred lower and upper boundaries t.testMatches(true, "1-02.03-4.05-06.07", "1-2.3-4.5-6.7") t.testMatches(true, "1-002.003-4.005-006.007", "1-2.3-4.5-6.7") t.testMatches(true, "1-002.003-4.0005-006.0007", "1-2.3-4.5-6.7") t.testMatches(true, "1100-22ff.003-4.0005-006.0007", "1100-22ff.3-4.5-6.7") t.testMatches(true, "1-2.0-0.00-00.00-0", "1-2.0.0.0") t.testMatches(true, "00-0.0-0.00-00.00-0", "0.0.0.0") t.testMatches(true, "0-00:0-0:00-00:00-0:0-00:00-00:00-00:00-0", "0:0:0:0:0:0:0:0") t.testMatches(true, "1-2:0-0:00-00:00-0:0-00:00-00:00-00:00-0", "1-2:0:0:0:0:0:0:0") t.testMatches(true, "11:11:11:ff:20-2f:0", "11:11:11:ff:2_:0") t.testMatches(true, "20-2f:11:11:11:1:1", "2_:11:11:11:1:1") t.testMatches(true, "1:2:3:4:20-2f:6:7:8", "1:2:3:4:2_:6:7:8") t.testMatches(true, "1:2:20-2f:4:5:6:7:8", "1:2:2_:4:5:6:7:8") t.testMatches(true, "1:2:3:4:5:20-2f", "1:2:3:4:5:2_") t.testMatches(true, "1:2:3:20-2f:5:8", "1:2:3:2_:5:8") t.testMatches(true, "20-2f:2:3:4:5:8", "2_:2:3:4:5:8") t.testMatches(true, "1:2:3:4:5:20-2f", "1:2:3:4:5:2_") t.testMatches(true, "20-2f:2:3:4:5:6:7:8", "2_:2:3:4:5:6:7:8") t.testMatches(true, "20-2f:2:3:4:5:6", "2_:2:3:4:5:6") t.testMatches(true, "11:11:11:ff:0-f:0", "11:11:11:ff:_:0") t.testMatches(true, "0-f:11:11:11:1:1", "_:11:11:11:1:1") t.testMatches(true, "1:2:3:4:0-f:6:7:8", "1:2:3:4:_:6:7:8") t.testMatches(true, "1:2:0-f:4:5:6:7:8", "1:2:_:4:5:6:7:8") t.testMatches(true, "1:2:3:4:6:0-f", "1:2:3:4:6:_") t.testMatches(true, "1:2:3:0-f:5:6:8:8", "1:2:3:_:5:6:8:8") t.testMatches(true, "0-f:2:3:5:ff:8", "_:2:3:5:ff:8") t.testMatches(true, "1:11:11:11:1:0-f", "1:11:11:11:1:_") t.testMatches(true, "0-f:2:3:4:5:6:7:8", "_:2:3:4:5:6:7:8") t.testMatches(true, "0-f:2:3:4:5:6", "_:2:3:4:5:6") t.testMatches(true, "1:5:2:3:4:0-f", "1:5:2:3:4:_") t.testMatches(true, "11:11:11:ff:*:0", "11:11:11:ff:__:0") t.testMatches(true, "*:11:11:11:1:1", "__:11:11:11:1:1") t.testMatches(true, "1:2:3:4:*:6:7:8", "1:2:3:4:__:6:7:8") t.testMatches(true, "1:2:*:4:5:6:7:8", "1:2:__:4:5:6:7:8") t.testMatches(true, "1:2:3:4:5:*", "1:2:3:4:5:__") t.testMatches(true, "1:2:3:*:5:8", "1:2:3:__:5:8") t.testMatches(true, "*:2:3:4:5:8", "__:2:3:4:5:8") t.testMatches(true, "1:2:3:4:5:*", "1:2:3:4:5:__") t.testMatches(true, "*:2:3:4:5:6:7:8", "__:2:3:4:5:6:7:8") t.testMatches(true, "*:2:3:4:5:6", "__:2:3:4:5:6") t.testMatches(true, "1:2:3:4:4:*", "1:2:3:4:4:__") t.testMatches(true, "0-ff.2.3.4", "__.2.3.4") t.testMatches(true, "1.2.3.0-ff", "1.2.3.__") t.testMatches(true, "0-f.2.3.4", "_.2.3.4") t.testMatches(true, "1.2.3.0-f", "1.2.3._") t.testMatches(true, "1.0-fff.3.4", "1.___.3.4") t.testMatches(true, "1.2.0-fff.0-f", "1.2.___._") t.testMatches(true, "1.*.3.4", "1.____.3.4") t.testMatches(true, "1.2.*.0-f", "1.2.____._") t.testMatches(true, "*.2.3.4", "____.2.3.4") t.testMatches(true, "1.2.3.*", "1.2.3.____") t.testDelimitedCount("1,2|3,4-3-4,5-6-7-8", 8) //this will iterate through 1.3.4.6 1.3.5.6 2.3.4.6 2.3.5.6 t.testDelimitedCount("1,2-3,6-7-8-4,5|6-6,8", 16) //this will iterate through 1.3.4.6 1.3.5.6 2.3.4.6 2.3.5.6 t.testDelimitedCount("1:2:3:*:4:5", 1) //this will iterate through 1.3.4.6 1.3.5.6 2.3.4.6 2.3.5.6 t.testDelimitedCount("1:2,3,*:3:6:4:5,ff,7,8,99", 15) //this will iterate through 1.3.4.6 1.3.5.6 2.3.4.6 2.3.5.6 t.testDelimitedCount("1:0,1-2,3,5:3:6:4:5,ff,7,8,99", 30) //this will iterate through 1.3.4.6 1.3.5.6 2.3.4.6 2.3.5.6 t.testNotContains("*.*", "1.2.3.4") t.testContains("*.*.*.*", "1.2.3.4", false) t.testContains("*.*.*", "1.2.3", false) t.testContains("*.*.1.aa00-ffff", "1.2.1.bbbb", false) t.testContains("*.*.1.aa00-ffff", "0-ffff.*.1.aa00-ffff", true) t.testContains("0-1ff.*.*.*", "127.2.3.4", false) t.testContains("0-1ff.*.*.*", "128.2.3.4", false) t.testNotContains("0-1ff.*", "200.2.3.4") t.testNotContains("0-1ff.*", "128.2.3.4") t.testContains("0-1ff.*", "128.2.3", false) t.testContains("0-ff.*.*.*", "15.2.3.4", false) t.testContains("0-ff.*", "15.2.3", false) t.testContains("9.129.*.*", "9.129.237.26", false) t.testContains("9.129.*", "9.129.237", false) t.testNotContains("9.129.*.25", "9.129.237.26") t.testContains("9.129.*.26", "9.129.237.26", false) t.testContains("9.129.*.26", "9.129.*.26", true) t.testContains("9.a0-ae.1.226-254", "9.ad.1.227", false) t.testNotContains("9.a0-ac.1.226-254", "9.ad.1.227") t.testNotContains("9.a0-ae.2.226-254", "9.ad.1.227") t.testContains("9.a0-ae.1.226-254", "9.a0-ae.1.226-254", true) t.testContains("8-9:a0-ae:1-3:20-26:0:1", "9:ad:1:20:0:1", false) t.testContains("8-9:a0-ae:1-3:20-26:0:1", "9:ad:1:23-25:0:1", false) t.testNotContains("8-9:a0-ae:1-3:20-26:0:1", "9:ad:1:23-27:0:1") t.testNotContains("8-9:a0-ae:1-3:20-26:0:1", "9:ad:1:18-25:0:1") t.testContains("*:*:*:*:ab:*:*:*", "*:*:*:*:ab:*:*:*", true) t.testContains("*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", true) t.testContains("*:*:*:*:*:*:*:*", "a:b:c:d:e:f:a:b", false) t.testContains("*:*:*:*:*:*", "a:b:c:d:a:b", false) t.testContains("80-8f:*:*:*:*:*", "8a:d:e:f:a:b", false) t.testContains("*:*:*:*:*:80-8f", "d:e:f:a:b:8a", false) t.testContains("*:*:*:*:*:*:*:*", "a:*:c:d:e:1-ff:a:b", false) t.testContains("8a-8d:*:*:*:*:*:*:*", "8c:b:c:d:e:f:*:b", false) t.testNotContains("80:0:0:0:0:0:0:0-1", "7f-8f:b:c:d:e:f:*:b") t.testContains("ff:0-3:*:*:*:*:*:*", "ff:0-3:c:d:e:f:a:b", false) t.testNotContains("ff:0-3:*:*:*:*:*:*", "ff:0-4:c:d:e:f:a:b") t.testContains("ff:0:*:*:*:*:*:*", "ff:0:ff:1-d:e:f:*:b", false) t.testContains("*:*:ff:0:*:*:*:*", "*:b:ff:0:ff:1-d:e:f", false) t.testNotContains("ff:0:*:*:*:*:*:*", "ff:0-1:ff:d:e:f:a:b") t.testContains("ff:0:0:0:0:4-ff:0:fc-ff", "ff:0:0:0:0:4-ff:0:fd-ff", false) t.testContains("ff:0:0:0:0:4-ff:0:fc-ff", "ff:0:0:0:0:4-ff:0:fc-ff", true) t.testContains("ff:0:*:0:0:4-ff:0:ff", "ff:0:*:0:0:4-ff:0:ff", true) t.testContains("*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", true) t.testContains("80-8f:*:*:*:*:80-8f", "83-8e:*:*:*:a-b:80-8f", false) t.testContains("80-8f:*:*:*:*:80-8f", "83-8e:*:*:*:a-b:80-8f", false) t.testNotContains("80-8f:*:*:*:*:80-8f", "7f-8e:*:*:*:a-b:80-8f") t.testLongShort2("ff:ff:ff:ff:ff:*:ff:1-ff", "ff:ff:ff:*:ff:1-ff", true) t.testLongShort2("12-cd-cc-dd-ee-ff-*", "12-cd-cc-*", true) t.testLongShort2("12CD.CCdd.*.a", "12CD.*.a", true) t.testLongShort2("*-0D0E0F0A0B", "0A0B0C-*", true) t.testLongShort("*-0D0E0F0A0B", "*-0A0B0C") t.testLongShort2("*-0D0E0F0A0B", "*-*", true) t.testLongShort("ee:ff:aa:*:dd:ee:ff", "ee:ff:a-b:bb:cc:dd") t.testLongShort2("ee:ff:aa:*:dd:ee:ff", "ee:ff:a-b:*:dd", true) t.testLongShort2("e:f:a:b:c:d:e:e-f", "e:*", true) t.testSections("00-1:21-ff:*:10") t.testSections("00-1:21-ff:2f:*:10") t.testSections("*-A7-94-07-CB-*") t.testSections("aa-*") t.testSections("aa-bb-*") t.testSections("aa-bb-cc-*") t.testSections("8-9:a0-ae:1-3:20-26:0:1") t.testSections("fe-ef-39-*-94-07-b|C-D0") t.testSections("5634-5678.*.7feb.6b40") t.testSections("ff:0:1:*") t.testSections("ff:0:1:*:*:*:*:*") t.testInsertAndAppend("*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", []ipaddr.BitCount{0, 0, 0, 0, 0, 0, 0, 0, 0}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb", "*:*:*:*:*:*:*:*", []ipaddr.BitCount{0, 8, 16, 24, 32, 40, 48, 56, 64}) t.testInsertAndAppendPrefs("*:*:*:*:*:*:*:*", "1:2:3:4:5:6:7:8", []ipaddr.PrefixLen{nil, p0, p0, p0, p0, p0, p0, p0, p0}) t.testInsertAndAppend("a:b:c:d:*:*:*:*", "1:2:3:4:*:*:*:*", []ipaddr.BitCount{32, 32, 32, 32, 32, 32, 32, 32, 32}) t.testInsertAndAppend("a:b:c:d:e:f:aa:bb", "1:2:3:4:*:*:*:*", []ipaddr.BitCount{32, 32, 32, 32, 32, 40, 48, 56, 64}) t.testInsertAndAppendPrefs("a:b:c:0-1:*:*:*:*", "1:2:3:4:5:6:7:8", []ipaddr.PrefixLen{pnil, pnil, pnil, pnil, p31, p31, p31, p31, p31}) t.testInsertAndAppendPrefs("a:b:c:d:*:*:*:*", "1:2:3:4:5:6:7:8", []ipaddr.PrefixLen{pnil, pnil, pnil, pnil, p32, p32, p32, p32, p32}) t.testInsertAndAppendPrefs("a:b:c:d:0-7f:*:*:*", "1:2:3:4:5:6:7:8", []ipaddr.PrefixLen{pnil, pnil, pnil, pnil, pnil, p33, p33, p33, p33}) t.testInsertAndAppend("a:b:*:*:*:*:*:*", "1:2:3:4:*:*:*:*", []ipaddr.BitCount{32, 32, 16, 16, 16, 16, 16, 16, 16}) t.testInsertAndAppend("a:b:c:d:*:*:*:*", "1:2:*:*:*:*:*:*", []ipaddr.BitCount{16, 16, 16, 24, 32, 32, 32, 32, 32}) t.testInsertAndAppendPrefs("*:*:*:*:*:*:*:*", "1:2:3:4:*:*:*:*", []ipaddr.PrefixLen{p32, p0, p0, p0, p0, p0, p0, p0, p0}) t.testInsertAndAppendPrefs("a:b:c:d:*:*:*:*", "1:2:3:4:5:6:7:8", []ipaddr.PrefixLen{pnil, pnil, pnil, pnil, p32, p32, p32, p32, p32}) t.testInsertAndAppendPrefs("a:b:c:d:e:f:aa:bb", "1:2:3:4:*:*:*:*", []ipaddr.PrefixLen{p32, p32, p32, p32, p32, p40, p48, p56, pnil}) t.testReplace("*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*") t.testReplace("a:b:c:d:e:f:aa:bb", "*:*:*:*:*:*:*:*") t.testReplace("*:*:*:*:*:*:*:*", "1:2:3:4:5:6:7:8") t.testReplace("a:b:c:d:*:*:*:*", "1:2:3:4:*:*:*:*") t.testReplace("a:b:c:d:e:f:aa:bb", "1:2:3:4:*:*:*:*") t.testReplace("a:b:c:0-1:*:*:*:*", "1:2:3:4:5:6:7:8") t.testReplace("a:b:c:d:*:*:*:*", "1:2:3:4:5:6:7:8") t.testReplace("a:b:c:d:0-7f:*:*:*", "1:2:3:4:5:6:7:8") t.testReplace("a:b:*:*:*:*:*:*", "1:2:3:4:*:*:*:*") t.testReplace("a:b:c:d:*:*:*:*", "1:2:*:*:*:*:*:*") t.testReplace("*:*:*:*:*:*:*:*", "1:2:3:4:*:*:*:*") t.testReplace("a:b:c:d:*:*:*:*", "1:2:3:4:5:6:7:8") t.testReplace("a:b:c:d:e:f:aa:bb", "1:2:3:4:*:*:*:*") t.testMACIPv6("aaaa:bbbb:cccc:dddd:0221:2fff:fe00-feff:6e10", "00:21:2f:*:6e:10") t.testMACIPv6("*:*:*:*:200-2ff:FF:FE00-FEFF:*", "0:*:0:*:*:*") t.testMACIPv6("*:*:*:*:200-3ff:abFF:FE01-FE03:*", "0-1:*:ab:1-3:*:*") t.testMACIPv6("*:*:*:*:a200-a3ff:abFF:FE01-FE03:*", "a0-a1:*:ab:1-3:*:*") t.testMACIPv6("*:2:*:*:a388-a399:abFF:FE01-FE03:*", "a1:88-99:ab:1-3:*:*") t.testMACIPv6("*:2:*:*:a388-a399:abFF:FE01-FE03:*", "a1:88-99:ab:1-3:*:*") t.testMACIPv6("1:0:0:0:8a0:bbff:fe00-feff:*", "0a:a0:bb:*:*:*") //[1:0:0:0:aa0:bbff:fe00-feff:*, 1:0:0:0:8a0:bbff:fe00-feff:*] t.testMACIPv6("1:0:0:0:200:bbff:fe00:b00-cff", "00:00:bb:00:0b-0c:*") t.testMACIPv6("1:0:0:0:200:bbff:fe00:b00-cff", "00:00:bb:00:0b-0c:*") t.testMACIPv6("1:0:0:0:c200:aaff:fec0:b00-cff", "c0:00:aa:c0:0b-0c:*") t.testMACIPv6("1:0:0:0:200:aaff:fe00:b00", "00:00:aa:00:0b:00") t.testMACIPv6("1:0:0:0:200:bbff:fe00:b00-cff", "00:00:bb:00:0b-0c:*") t.testMACIPv6("1:0:0:0:200:bbff:fe00-feff:*", "00:00:bb:*:*:*") t.testPrefixBlock("ff:*:*:*:*:*", 8) t.testPrefixBlock("ff:ff:*:*:*:*", 16) t.testPrefixBlock("ff:fe-ff:*:*:*:*", 15) t.testPrefixBlock("ff:fc-ff:*:*:*:*", 14) t.testPrefixBlock("ff:ff:80-ff:*:*:*", 17) t.testPrefixBlock("ff:ff:0-7f:*:*:*", 17) t.testPrefixBlock("ff:ff:0-3f:*:*:*", 18) t.testPrefixBlock("ff:0:0:ff:0:*", 40) t.testPrefixBlock("ff:0:0:ff:0:fe-ff", 47) t.testTrees() t.macAddressTester.run() } func (t macAddressRangeTester) testToOUIPrefixed(addrString string) { w := t.createMACAddress(addrString) v := w.GetAddress() suffixSeg := ipaddr.NewMACRangeSegment(0, 0xff) suffixSegs := make([]*ipaddr.MACAddressSegment, v.GetSegmentCount()) v.CopySubSegments(0, 3, suffixSegs) for i := 3; i < len(suffixSegs); i++ { suffixSegs[i] = suffixSeg } suffix := ipaddr.NewMACSection(suffixSegs) suffixed, err := ipaddr.NewMACAddress(suffix) if err != nil { t.addFailure(newMACFailure(err.Error(), w)) return } prefixed := v.ToOUIPrefixBlock() if !prefixed.Equal(suffixed) { t.addFailure(newMACFailure("failed oui prefixed "+prefixed.String()+" constructed "+suffixed.String(), w)) } t.incrementTestCount() } func (t macAddressRangeTester) testOUIPrefixed(original, expected string, expectedPref ipaddr.BitCount) { w := t.createMACAddress(original) val := w.GetAddress() w2 := t.createMACAddress(expected) expectedAddress := w2.GetAddress() prefixed := val.ToOUIPrefixBlock() if !prefixed.Equal(expectedAddress) { t.addFailure(newMACFailure("oui prefixed was "+prefixed.String()+" expected was "+expected, w)) } if expectedPref != prefixed.GetPrefixLen().Len() { t.addFailure(newMACFailure("oui prefix was "+prefixed.GetPrefixLen().String()+" expected was "+bitCountToString(expectedPref), w)) } t.incrementTestCount() } func (t macAddressRangeTester) testEquivalentPrefix(host string, prefix ipaddr.BitCount) { t.testEquivalentMinPrefix(host, cacheTestBits(prefix), prefix) } func (t macAddressRangeTester) testEquivalentMinPrefix(host string, equivPrefix ipaddr.PrefixLen, minPrefix ipaddr.BitCount) { str := t.createMACAddress(host) h1, err := str.ToAddress() if err != nil { t.addFailure(newMACFailure(err.Error(), str)) } else { equiv := h1.GetPrefixLenForSingleBlock() if !equivPrefix.Equal(equiv) { t.addFailure(newMACAddrFailure("failed: prefix expected: "+equivPrefix.String()+" prefix got: "+equiv.String(), h1)) } else { minPref := h1.GetMinPrefixLenForBlock() if minPref != minPrefix { t.addFailure(newMACAddrFailure("failed: prefix expected: "+bitCountToString(minPrefix)+" prefix got: "+bitCountToString(minPref), h1)) } } } t.incrementTestCount() } func bitCountToString(i ipaddr.BitCount) string { return strconv.Itoa(i) } func (t macAddressRangeTester) testMACCount(original string, number uint64) { w := t.createMACAddress(original) t.testMACCountRedirect(w, number) } func (t macAddressRangeTester) testMACCountRedirect(w *ipaddr.MACAddressString, number uint64) { t.testCountRedirect(w.Wrap(), number, math.MaxUint64) } func (t macAddressRangeTester) testMACPrefixCount(original string, number uint64) { w := t.createMACAddress(original) t.testMACPrefixCountImpl(w, number) } func (t macAddressRangeTester) testMACPrefixCountImpl(w *ipaddr.MACAddressString, number uint64) { t.testPrefixCountImpl(w.Wrap(), number) } func (t macAddressRangeTester) testPrefixBlock(prefixedAddressStr string, expectedPref ipaddr.BitCount) { //starting with prefixed address like 1:2:3:*:*:*, get lower (which should retain prefix except for allsubnets) //check if prefix block (not), then for all subnets assign prefix, then for all call toPrefixBlock, compare with original and also the prefix prefixedAddressString := t.createMACAddress(prefixedAddressStr) prefixedAddress := prefixedAddressString.GetAddress() //a whole bunch of address manipulations, all of which we will reverse and then convert back to the prefix block lower := prefixedAddress.GetLower() upper := prefixedAddress.GetUpper() removedPrefix := prefixedAddress.WithoutPrefixLen() adjustPrefix := prefixedAddress.AdjustPrefixLen(1) adjustPrefix2 := prefixedAddress.AdjustPrefixLen(-1) hostSeg := int((prefixedAddress.GetPrefixLen().Len() + (ipaddr.MACBitsPerSegment - 1)) / ipaddr.MACBitsPerSegment) replaced := prefixedAddress replacement := t.createMACAddress("1:1:1:1:1:1").GetAddress() for i := hostSeg; i < replaced.GetSegmentCount(); i++ { replaced = replaced.ReplaceLen(i, i+1, replacement, i) } if lower.Equal(upper) { t.addFailure(newSegmentSeriesFailure("lower / upper "+lower.String()+" / "+upper.String(), prefixedAddress)) } if !prefixedAddress.IsPrefixBlock() { t.addFailure(newSegmentSeriesFailure("not prefix block", prefixedAddress)) } if !adjustPrefix.IsPrefixBlock() { t.addFailure(newSegmentSeriesFailure("adjustPrefix is not prefix block: "+adjustPrefix.String(), prefixedAddress)) } if !adjustPrefix.IsPrefixed() { t.addFailure(newSegmentSeriesFailure("adjustPrefix not prefixed: "+adjustPrefix.GetPrefixLen().String(), prefixedAddress)) } if !adjustPrefix2.IsPrefixed() { t.addFailure(newSegmentSeriesFailure("adjustPrefix2 not prefixed: "+adjustPrefix2.GetPrefixLen().String(), prefixedAddress)) } if removedPrefix.IsPrefixed() { t.addFailure(newSegmentSeriesFailure("removedPrefix prefixed: "+removedPrefix.GetPrefixLen().String(), prefixedAddress)) } if !replaced.IsPrefixed() { t.addFailure(newSegmentSeriesFailure("replaced prefixed: "+replaced.GetPrefixLen().String(), prefixedAddress)) } if hostSeg < replaced.GetSegmentCount() && replaced.IsPrefixBlock() { t.addFailure(newSegmentSeriesFailure("replaced is prefix block: "+replaced.String(), prefixedAddress)) } if adjustPrefix2.IsPrefixBlock() { t.addFailure(newSegmentSeriesFailure("adjustPrefix2 is not prefix block: "+adjustPrefix2.String(), prefixedAddress)) } if !lower.IsPrefixed() { t.addFailure(newSegmentSeriesFailure("lower not prefixed: "+lower.String(), prefixedAddress)) } if lower.IsPrefixBlock() { t.addFailure(newSegmentSeriesFailure("lower is prefix block: "+lower.String(), prefixedAddress)) } if !upper.IsPrefixed() { t.addFailure(newSegmentSeriesFailure("upper not prefixed: "+upper.String(), prefixedAddress)) } if upper.IsPrefixBlock() { t.addFailure(newSegmentSeriesFailure("upper is prefix block: "+upper.String(), prefixedAddress)) } prefixedBlockAgain := lower.ToPrefixBlock() if !prefixedBlockAgain.IsPrefixBlock() { t.addFailure(newSegmentSeriesFailure("reconstituted prefix block not a prefix block: "+prefixedBlockAgain.String(), prefixedAddress)) } if !prefixedBlockAgain.Equal(prefixedAddress) { t.addFailure(newSegmentSeriesFailure("reconstituted prefix block mismatch: "+prefixedBlockAgain.String()+" original: "+prefixedAddress.String(), prefixedAddress)) } if !prefixedBlockAgain.GetPrefixLen().Equal(prefixedAddress.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("reconstituted prefix block prefix mismatch: "+prefixedBlockAgain.GetPrefixLen().String()+" original: "+prefixedAddress.GetPrefixLen().String(), prefixedAddress)) } //now revert all our operations, convert them all back to prefix blocks, and compare removedPrefix = removedPrefix.SetPrefixLen(prefixedAddress.GetPrefixLen().Len()) adjustPrefix = adjustPrefix.AdjustPrefixLen(-1) adjustPrefix2 = adjustPrefix2.AdjustPrefixLen(1) if !prefixedBlockAgain.Equal(removedPrefix.ToPrefixBlock()) || !prefixedBlockAgain.GetPrefixLen().Equal(removedPrefix.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("reconstituted prefix block mismatch with other: "+prefixedBlockAgain.String()+" other: "+removedPrefix.String(), prefixedAddress)) } if !prefixedBlockAgain.Equal(adjustPrefix.ToPrefixBlock()) || !prefixedBlockAgain.GetPrefixLen().Equal(adjustPrefix.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("reconstituted prefix block mismatch with other: "+prefixedBlockAgain.String()+" other: "+adjustPrefix.String(), prefixedAddress)) } if !prefixedBlockAgain.GetPrefixLen().Equal(adjustPrefix2.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("reconstituted prefix block mismatch with other: "+prefixedBlockAgain.String()+" other: "+adjustPrefix2.String(), prefixedAddress)) } if !prefixedBlockAgain.Equal(replaced.ToPrefixBlock()) || !prefixedBlockAgain.GetPrefixLen().Equal(replaced.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("reconstituted prefix block mismatch with other: "+prefixedBlockAgain.String()+" other: "+replaced.String(), prefixedAddress)) } if !prefixedBlockAgain.Equal(upper.ToPrefixBlock()) || !prefixedBlockAgain.GetPrefixLen().Equal(upper.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("reconstituted prefix block mismatch with other: "+prefixedBlockAgain.String()+" other: "+upper.String(), prefixedAddress)) } t.incrementTestCount() } func (t macAddressRangeTester) testStrings() { t.testMACStrings("a:b:c:d:*:*:*", "0a:0b:0c:0d:*:*:*:*", //normalizedString, //toColonDelimitedString "a:b:c:d:*:*:*:*", //compressedString, "0a-0b-0c-0d-*-*-*-*", //canonicalString, //toDashedString "0a0b.0c0d.*.*", //dottedString, "0a 0b 0c 0d * * * *", //spaceDelimitedString, "0a0b0c0d00000000-0a0b0c0dffffffff") //singleHex t.testMACStrings("a:b:c:*:*:*:*", "0a:0b:0c:*:*:*:*:*", //normalizedString, //toColonDelimitedString "a:b:c:*:*:*:*:*", //compressedString, "0a-0b-0c-*-*-*-*-*", //canonicalString, //toDashedString "0a0b.0c00-0cff.*.*", //dottedString, "0a 0b 0c * * * * *", //spaceDelimitedString, "0a0b0c0000000000-0a0b0cffffffffff") //singleHex t.testMACStrings("a:b:c:d:*", "0a:0b:0c:0d:*:*", //normalizedString, //toColonDelimitedString "a:b:c:d:*:*", //compressedString, "0a-0b-0c-0d-*-*", //canonicalString, //toDashedString "0a0b.0c0d.*", //dottedString, "0a 0b 0c 0d * *", //spaceDelimitedString, "0a0b0c0d0000-0a0b0c0dffff") //singleHex t.testMACStrings("a:b:c:d:1-2:*", "0a:0b:0c:0d:01-02:*", //normalizedString, //toColonDelimitedString "a:b:c:d:1-2:*", //compressedString, "0a-0b-0c-0d-01|02-*", //canonicalString, //toDashedString "0a0b.0c0d.0100-02ff", //dottedString, "0a 0b 0c 0d 01-02 *", //spaceDelimitedString, "0a0b0c0d0100-0a0b0c0d02ff") //singleHex t.testMACStrings("0:0:c:d:e:f:10-1f:b", "00:00:0c:0d:0e:0f:10-1f:0b", //normalizedString, //toColonDelimitedString "0:0:c:d:e:f:10-1f:b", //compressedString, "00-00-0c-0d-0e-0f-10|1f-0b", //canonicalString, //toDashedString "", //dottedString, "00 00 0c 0d 0e 0f 10-1f 0b", //spaceDelimitedString, "") //singleHex t.testMACStrings("0:0:c:d:e:f:10-1f:*", "00:00:0c:0d:0e:0f:10-1f:*", //normalizedString, //toColonDelimitedString "0:0:c:d:e:f:10-1f:*", //compressedString, "00-00-0c-0d-0e-0f-10|1f-*", //canonicalString, //toDashedString "0000.0c0d.0e0f.1000-1fff", //dottedString, "00 00 0c 0d 0e 0f 10-1f *", //spaceDelimitedString, "00000c0d0e0f1000-00000c0d0e0f1fff") //singleHex t.testMACStrings("a-b:b-c:0c-0d:0d-e:e-0f:f-ff:aa-bb:bb-cc", "0a-0b:0b-0c:0c-0d:0d-0e:0e-0f:0f-ff:aa-bb:bb-cc", //normalizedString, //toColonDelimitedString "a-b:b-c:c-d:d-e:e-f:f-ff:aa-bb:bb-cc", //compressedString, "0a|0b-0b|0c-0c|0d-0d|0e-0e|0f-0f|ff-aa|bb-bb|cc", //canonicalString, //toDashedString "", //dottedString, "0a-0b 0b-0c 0c-0d 0d-0e 0e-0f 0f-ff aa-bb bb-cc", //spaceDelimitedString, "") //singleHex t.testMACStrings("12-ef:*:cd:d:0:*", "12-ef:*:cd:0d:00:*", //normalizedString, //toColonDelimitedString "12-ef:*:cd:d:0:*", //compressedString, "12|ef-*-cd-0d-00-*", //canonicalString, //toDashedString "1200-efff.cd0d.0000-00ff", //dottedString, "12-ef * cd 0d 00 *", //spaceDelimitedString, "") //singleHex t.testMACStrings("ff:ff:*:*:aa-ff:0-de", "ff:ff:*:*:aa-ff:00-de", //normalizedString, //toColonDelimitedString "ff:ff:*:*:aa-ff:0-de", //compressedString, "ff-ff-*-*-aa|ff-00|de", //canonicalString, //toDashedString "", //dottedString, "ff ff * * aa-ff 00-de", //spaceDelimitedString, "") //singleHex t.testMACStrings("ff:ff:aa-ff:*:*:*", "ff:ff:aa-ff:*:*:*", //normalizedString, //toColonDelimitedString "ff:ff:aa-ff:*:*:*", //compressedString, "ff-ff-aa|ff-*-*-*", //canonicalString, //toDashedString "ffff.aa00-ffff.*", //dottedString, "ff ff aa-ff * * *", //spaceDelimitedString, "ffffaa000000-ffffffffffff") //singleHex t.testMACStrings("ff:f:aa-ff:*:*:*", "ff:0f:aa-ff:*:*:*", //normalizedString, //toColonDelimitedString "ff:f:aa-ff:*:*:*", //compressedString, "ff-0f-aa|ff-*-*-*", //canonicalString, //toDashedString "ff0f.aa00-ffff.*", //dottedString, "ff 0f aa-ff * * *", //spaceDelimitedString, "ff0faa000000-ff0fffffffff") //singleHex t.testMACStrings("ff:ff:ee:aa-ff:*:*", "ff:ff:ee:aa-ff:*:*", //normalizedString, //toColonDelimitedString "ff:ff:ee:aa-ff:*:*", //compressedString, "ff-ff-ee-aa|ff-*-*", //canonicalString, //toDashedString "ffff.eeaa-eeff.*", //dottedString, "ff ff ee aa-ff * *", //spaceDelimitedString, "ffffeeaa0000-ffffeeffffff") //singleHex t.testMACStrings("*", "*:*:*:*:*:*", //normalizedString, //toColonDelimitedString "*:*:*:*:*:*", //compressedString, "*-*-*-*-*-*", //canonicalString, //toDashedString "*.*.*", //dottedString, "* * * * * *", //spaceDelimitedString, "000000000000-ffffffffffff") //singleHex t.testMACStrings("1-3:2:33:4:55-60:6", "01-03:02:33:04:55-60:06", "1-3:2:33:4:55-60:6", "01|03-02-33-04-55|60-06", "", "01-03 02 33 04 55-60 06", "") t.testMACStrings("f3:2:33:4:6:55-60", "f3:02:33:04:06:55-60", "f3:2:33:4:6:55-60", "f3-02-33-04-06-55|60", "f302.3304.0655-0660", "f3 02 33 04 06 55-60", "f30233040655-f30233040660") t.testMACStrings("*-b00cff", "*:*:*:b0:0c:ff", "*:*:*:b0:c:ff", "*-*-*-b0-0c-ff", "", "* * * b0 0c ff", "") t.testMACStrings("0aa0bb-*", "0a:a0:bb:*:*:*", "a:a0:bb:*:*:*", "0a-a0-bb-*-*-*", "0aa0.bb00-bbff.*", "0a a0 bb * * *", "0aa0bb000000-0aa0bbffffff") t.testMACStrings("0000aa|0000bb-000b00|000cff", "00:00:aa-bb:00:0b-0c:*", "0:0:aa-bb:0:b-c:*", "00-00-aa|bb-00-0b|0c-*", "", "00 00 aa-bb 00 0b-0c *", "") t.testMACStrings("c000aa|c000bb-c00b00|c00cff", "c0:00:aa-bb:c0:0b-0c:*", "c0:0:aa-bb:c0:b-c:*", "c0-00-aa|bb-c0-0b|0c-*", "", "c0 00 aa-bb c0 0b-0c *", "") t.testMACStrings("0000aa|0000bb-000b00", "00:00:aa-bb:00:0b:00", "0:0:aa-bb:0:b:0", "00-00-aa|bb-00-0b-00", "", "00 00 aa-bb 00 0b 00", "") t.testMACStrings("0000bb-000b00|000cff", "00:00:bb:00:0b-0c:*", "0:0:bb:0:b-c:*", "00-00-bb-00-0b|0c-*", "0000.bb00.0b00-0cff", //"", "00 00 bb 00 0b-0c *", "0000bb000b00-0000bb000cff") t.testMACStrings("0000aa|0000bb-*", "00:00:aa-bb:*:*:*", "0:0:aa-bb:*:*:*", "00-00-aa|bb-*-*-*", "0000.aa00-bbff.*", "00 00 aa-bb * * *", "0000aa000000-0000bbffffff") t.testMACStrings("*-000b00|000cff", "*:*:*:00:0b-0c:*", "*:*:*:0:b-c:*", "*-*-*-00-0b|0c-*", "", "* * * 00 0b-0c *", "") } func (t macAddressRangeTester) testTree(start string, parents []string) { usePrefixBlocks := false for k := 0; k < 2; k++ { if k == 1 { usePrefixBlocks = true } str := t.createMACParamsAddress(start, wildcardAndRangeMACAddressOptions) addr := str.GetAddress() //now do the same thing but use the IPAddress objects instead var i, j int var pref ipaddr.PrefixLen brokeEarly := false for { label := getMACLabel(addr.ToAddressString()) expected := parents[i] if label != expected { t.addFailure(newMACFailure("failed expected: "+expected+" actual: "+label, str)) brokeEarly = true break } pref = addr.GetPrefixLen() addr = enlargeMACSubnet(addr) if usePrefixBlocks { addr = addr.ToPrefixBlock() i++ } j++ if pref != nil && pref.Len() == 0 { //when network prefix is 0, Address.toSupernet() returns the same address break } } if !brokeEarly && j != len(parents) { t.addFailure(newMACFailure("failed: invalid label count "+strconv.Itoa(len(parents))+" expected "+strconv.Itoa(j), str)) } t.incrementTestCount() } } func enlargeMACSubnet(addr *ipaddr.MACAddress /*boolean nextSegment false , int bitsPerSegment, /* boolean skipBitCountPrefix false */) *ipaddr.MACAddress { prefix := addr.GetPrefixLen() if prefix == nil { return addr.SetPrefixLen(addr.GetBitCount() - addr.GetBitsPerSegment()) } prefLen := prefix.Len() if prefLen == 0 { return addr } adjustment := ((prefLen - 1) % addr.GetBitsPerSegment()) + 1 addr = addr.SetPrefixLen(prefLen - adjustment) return addr } func getMACLabel(addressString *ipaddr.MACAddressString) string { address := addressString.GetAddress() if address == nil { return addressString.String() } return address.ToNormalizedString() } func (t macAddressRangeTester) testTrees() { t.testTree("1:2:0-8f:*", []string{ "01:02:00-8f:*:*:*", "01:02:*:*:*:*", "01:*:*:*:*:*", "*:*:*:*:*:*", }) t.testTree("a:b:c:d:e:f", []string{ "0a:0b:0c:0d:0e:0f", "0a:0b:0c:0d:0e:*", "0a:0b:0c:0d:*:*", "0a:0b:0c:*:*:*", "0a:0b:*:*:*:*", "0a:*:*:*:*:*", "*:*:*:*:*:*", }) t.testTree("a:b:c:d:e:f:a:b", []string{ "0a:0b:0c:0d:0e:0f:0a:0b", "0a:0b:0c:0d:0e:0f:0a:*", "0a:0b:0c:0d:0e:0f:*:*", "0a:0b:0c:0d:0e:*:*:*", "0a:0b:0c:0d:*:*:*:*", "0a:0b:0c:*:*:*:*:*", "0a:0b:*:*:*:*:*:*", "0a:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", }) t.testTree("a:b:c:d:e:f:a0-bf:*", []string{ //this one is good now "0a:0b:0c:0d:0e:0f:a0-bf:*", "0a:0b:0c:0d:0e:0f:*:*", "0a:0b:0c:0d:0e:*:*:*", "0a:0b:0c:0d:*:*:*:*", "0a:0b:0c:*:*:*:*:*", "0a:0b:*:*:*:*:*:*", "0a:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", }) t.testTree("a:b:c:d:e:f:a2-a3:*", []string{ //this one is good now "0a:0b:0c:0d:0e:0f:a2-a3:*", "0a:0b:0c:0d:0e:0f:*:*", "0a:0b:0c:0d:0e:*:*:*", "0a:0b:0c:0d:*:*:*:*", "0a:0b:0c:*:*:*:*:*", "0a:0b:*:*:*:*:*:*", "0a:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", }) t.testTree("a:b:c:d:e:f:1f-80:*", []string{ "0a:0b:0c:0d:0e:0f:1f-80:*", "0a:0b:0c:0d:0e:0f:*:*", "0a:0b:0c:0d:0e:*:*:*", "0a:0b:0c:0d:*:*:*:*", "0a:0b:0c:*:*:*:*:*", "0a:0b:*:*:*:*:*:*", "0a:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", }) t.testTree("a:b:c:11-12:*", []string{ "0a:0b:0c:11-12:*:*", "0a:0b:0c:*:*:*", "0a:0b:*:*:*:*", "0a:*:*:*:*:*", "*:*:*:*:*:*", }) } ipaddress-go-1.5.4/ipaddr/test/macaddrtest.go000066400000000000000000001504711440250641600211600ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package test import ( "bytes" "math" "math/big" "net" "strconv" "strings" "github.com/seancfoley/ipaddress-go/ipaddr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) type macAddressTester struct { testBase } func (t macAddressTester) run() { t.mactest(true, "aa:b:cc:d:ee:f") t.mactest(false, "aaa:b:cc:d:ee:f") t.mactest(false, "aa:bbb:cc:d:ee:f") t.mactest(false, "aa:bb:ccc:d:ee:f") t.mactest(false, "aa:bb:cc:ddd:ee:f") t.mactest(false, "aa:bb:cc:dd:eee:f") t.mactest(false, "aa:bb:cc:dd:ee:fff") t.mactest(false, "aa:bb:cc:dd:ee:ff:eee:aa") t.mactest(false, "aa:bb:cc:dd:ee:ff:ee:aaa") t.mactest(true, "aa:bb:cc:dd:ee:ff:ee:aa") t.mactest(false, "0xaa:b:cc:d:ee:f") t.mactest(false, "aa:0xb:cc:d:ee:f") t.mactest(false, "aa:b:0xcc:d:ee:f") t.mactest(false, "aa:b:cx:d:ee:f") t.mactest(false, "aa:b:cx:d:ee:fg") t.mactest(true, "aa-b-cc-d-ee-f") t.mactest(false, "aaa-b-cc-d-ee-f") t.mactest(false, "aa-bbb-cc-d-ee-f") t.mactest(false, "aa-bb-ccc-d-ee-f") t.mactest(false, "aa-bb-cc-ddd-ee-f") t.mactest(false, "aa-bb-cc-dd-eee-f") t.mactest(false, "aa-bb-cc-dd-ee-fff") t.mactest(false, "aa-bb-cc-dd-ee-ff-eee-aa") t.mactest(false, "aa-bb-cc-dd-ee-ff-ee-aaa") t.mactest(true, "aa-bb-cc-dd-ee-ff-ee-aa") t.mactest(false, "0xaa-b-cc-d-ee-f") t.mactest(false, "xaa-b-cc-d-ee-f") t.mactest(false, "aa-b-cc-d-ee-0xf") t.mactest(false, "aa-b-cc-d-ee-0xff") t.mactest(false, "aa-0xb-cc-d-ee-f") t.mactest(false, "aa-b-cx-d-ee-f") t.mactest(false, "aa-b-0xc-d-ee-f") t.mactest(false, "aa-b-cx-d-ee-fg") t.mactest(true, "aabb.ccdd.eeff") t.mactest(false, "aabbc.ccdd.eeff") t.mactest(false, "aabb.ccddc.eeff") t.mactest(false, "aabb.ccdd.eeffc") t.mactest(false, "aabb.ccdd.eeff.ccdde") t.mactest(true, "aabb.ccdd.eeff.ccde") t.mactest(false, "aabb.ccdd.eeff.0xccdd") t.mactest(false, "0xaabb.ccdd.eeff.ccdd") t.mactest(false, "aabb.0xccdd.eeff.ccdd") t.mactest(false, "aabb.ccgd.eeff.ccdd") t.mactest(true, "1:2:3:4:5:6") t.mactest(true, "11:22:33:44:55:66") t.mactest(false, "11:22:33:444:55:66") t.mactest(false, "aa:x:cc:d:ee:f") t.mactest(false, "aa:g:cc:d:ee:f") t.mactest(t.allowsRange(), "aa:-1:cc:d:ee:f") //same as "aa:0-1:cc:d:ee:f" t.mactest(t.allowsRange(), "aa:-dd:cc:d:ee:f") //same as "aa:0-dd:cc:d:ee:f" t.mactest(t.allowsRange(), "aa:1-:cc:d:ee:f") //same as "aa:1-ff:cc:d:ee:f" t.mactest(t.allowsRange(), "-1:aa:cc:d:ee:f") //same as "aa:0-1:cc:d:ee:f" t.mactest(t.allowsRange(), "1-:aa:cc:d:ee:f") //same as "aa:0-1:cc:d:ee:f" t.mactest(t.allowsRange(), "aa:cc:d:ee:f:1-") t.mactest(t.allowsRange(), "aa:0-1:cc:d:ee:f") t.mactest(t.allowsRange(), "aa:1-ff:cc:d:ee:f") t.mactest(t.allowsRange(), "aa-|1-cc-d-ee-f") t.mactest(t.allowsRange(), "|1-aa-cc-d-ee-f") t.mactest(t.allowsRange(), "aa-1|-cc-d-ee-f") t.mactest(t.allowsRange(), "1|-aa-cc-d-ee-f") t.mactest(t.allowsRange(), "aa-0|1-cc-d-ee-f") t.mactest(t.allowsRange(), "aa-1|ff-cc-d-ee-f") t.mactest(t.allowsRange(), "aa-ff-cc|dd-d-ee-f") t.mactest(false, "aa-||1-cc-d-ee-f") t.mactest(false, "aa-1||-cc-d-ee-f") t.mactest(true, "a:bb:c:dd:e:ff") t.mactest(true, "aa:bb:cc:dd:ee:ff") t.mactest(false, "aa:bb:cc:dd::ee:ff") t.mactest(false, "aa:bb::dd:ee:ff") t.mactest(false, "aa:bb-cc:dd:ee:ff") t.mactest(true, "aabbcc-ddeeff") t.mactest(false, "aaabbcc-ddeeff") t.mactest(false, "aabbcc-ddeefff") t.mactest(false, "aabbcc-ddeeffff") t.mactest(false, "aabbcc-ddeefffff") t.mactest(true, "aabbcc-ddeeffffff") t.mactest(false, "aaabbcc-ddeeffffff") t.mactest(false, "aaaabbcc-ddeeffffff") t.mactest(false, "aaaaaabbcc-ddeeffffff") t.mactest(false, "aaabbcc-ddeeffff") t.mactest(false, "aabbcc.ddeeff") t.mactest(false, "aabbcc:ddeeff") t.mactest(false, "aabbcc ddeeff") t.mactest(false, "aa-bb-cc dd-ee-ff") t.mactest(false, "aa bb cc dd ee-ff") t.mactest(false, "aa:bb:cc dd:ee:ff") t.mactest(false, "aa bb cc dd ee:ff") t.mactest(false, "aa-bb-cc:dd-ee-ff") t.mactest(false, "aa.b.cc.d.ee.f") t.mactest(false, "aa.bb.cc.dd.ee.ff") t.mactest(false, "aa.bb.cc dd.ee.ff") t.mactest(false, "aa-bb-cc-dd:ee-ff") t.mactest(false, "aa-bb-cc-dd-ee:-ff") t.mactest(false, "aa-bb-cc-dd-ee--ff") t.mactest(false, "aa-bb-cc-dd--ee") t.mactest(false, "aa:bb:cc:dd:ee:ff:") t.mactest(false, "aa:bb:cc:dd:ee:ff:aa") t.mactest(false, "ff:aa:bb:cc:dd:ee:ff") t.mactest(true, "aa:bb:cc:dd:ee:ff:aa:bb") t.mactest(true, "ee:ff:aa:bb:cc:dd:ee:ff") t.mactest(false, ":aa:bb:cc:dd:ee:ff:aa:bb") t.mactest(false, "ee:ff:aa:bb:cc:dd:ee:ff:") t.mactest(false, "aa:aa:bb:cc:dd:ee:ff:aa:bb") t.mactest(false, "ee:ff:aa:bb:cc:dd:ee:ff:ee") t.mactest(false, ":aa:bb:cc:dd:ee:ff") t.mactest(false, "aa:bb cc:dd:ee:ff") t.mactest(false, "aa:bb:cc:dd.ee:ff") t.mactest(false, "aaa:bb:cc:dd:ee:ff") t.mactest(false, "aa:bbb:cc:dd:ee:ff") t.mactest(false, "aa:bb:ccc:dd:ee:ff") t.mactest(false, "aa:bb:cc:ddd:ee:ff") t.mactest(false, "aa:bb:cc:dd:eee:ff") t.mactest(false, "aa:bb:cc:dd:ee:fff") t.mactest(true, "f-a-b-c-d-e") t.mactest(false, "-a-b-c-d-e") t.mactest(false, "f--b-c-d-e") t.mactest(false, "f-b-c-d-e") t.mactest(false, "f-a-b-c-d-") t.mactest(false, "f-a-b-c--e") t.mactestZero(true, "0:0:0:0:0:0", true) t.mactestZero(true, "00:0:0:0:0:0", true) t.mactestZero(true, "0:00:0:0:0:0", true) t.mactestZero(true, "0:0:00:0:0:0", true) t.mactestZero(true, "0:0:0:00:0:0", true) t.mactestZero(true, "0:0:0:0:00:0", true) t.mactestZero(true, "0:0:0:0:0:00", true) t.mactestZero(t.isLenient(), "000:0:0:0:0:0", true) t.mactestZero(t.isLenient(), "0:000:0:0:0:0", true) t.mactestZero(t.isLenient(), "0:0:000:0:0:0", true) t.mactestZero(t.isLenient(), "0:0:0:000:0:0", true) t.mactestZero(t.isLenient(), "0:0:0:0:000:0", true) t.mactestZero(t.isLenient(), "0:0:0:0:0:000", true) t.mactestZero(t.isLenient(), "0:0:0:0:0:0:000:0", true) t.mactestZero(t.isLenient(), "0:0:0:0:0:0:0:000", true) t.mactestZero(t.isLenient(), "000:000:000:000", true) t.mactestZero(true, "00.0.0", true) t.mactestZero(true, "0.00.0", true) t.mactestZero(true, "0.0.00", true) t.mactestZero(true, "0.0.0.00", true) t.mactestZero(true, "000.0.0", true) t.mactestZero(true, "0.000.0", true) t.mactestZero(true, "0.00.000", true) t.mactestZero(true, "0000.0.0", true) t.mactestZero(true, "0.0000.0", true) t.mactestZero(true, "0.00.0000", true) t.mactestZero(t.isLenient(), "00000.0.0", true) t.mactestZero(t.isLenient(), "0.00000.0", true) t.mactestZero(t.isLenient(), "0.0.00000", true) t.mactestZero(t.isLenient(), "00000.00000.00000", true) t.mactestZero(t.isLenient(), "00000.00000.00000.00000", true) t.mactestZero(true, "3:3:3:3:3:3", false) t.mactestZero(true, "33:3:3:3:3:3", false) t.mactestZero(true, "3:33:3:3:3:3", false) t.mactestZero(true, "3:3:33:3:3:3", false) t.mactestZero(true, "3:3:3:33:3:3", false) t.mactestZero(true, "3:3:3:3:33:3", false) t.mactestZero(true, "3:3:3:3:3:33", false) t.mactestZero(t.isLenient(), "033:3:3:3:3:3", false) t.mactestZero(t.isLenient(), "3:033:3:3:3:3", false) t.mactestZero(t.isLenient(), "3:3:033:3:3:3", false) t.mactestZero(t.isLenient(), "3:3:3:033:3:3", false) t.mactestZero(t.isLenient(), "3:3:3:3:033:3", false) t.mactestZero(t.isLenient(), "3:3:3:3:3:033", false) t.mactestZero(t.isLenient(), "3:3:3:3:3:3:033:3", false) t.mactestZero(t.isLenient(), "3:3:3:3:3:3:3:033", false) t.mactestZero(t.isLenient(), "033:033:033:033", false) t.mactestZero(true, "33.3.3", false) t.mactestZero(true, "3.33.3", false) t.mactestZero(true, "3.3.33", false) t.mactestZero(true, "3.3.3.33", false) t.mactestZero(true, "333.3.3", false) t.mactestZero(true, "3.333.3", false) t.mactestZero(true, "3.33.333", false) t.mactestZero(true, "3333.3.3", false) t.mactestZero(true, "3.3333.3", false) t.mactestZero(true, "3.33.3333", false) t.mactestZero(t.isLenient(), "03333.3.3", false) t.mactestZero(t.isLenient(), "3.03333.3", false) t.mactestZero(t.isLenient(), "3.3.03333", false) t.mactestZero(t.isLenient(), "03333.03333.03333", false) t.mactestZero(t.isLenient(), "03333.03333.03333.03333", false) eight := [8]byte{} t.testFromBytes([]byte{255, 255, 255, 255, 255, 255}, "ff:ff:ff:ff:ff:ff") t.testFromBytes([]byte{1, 2, 3, 4, 5, 6}, "1:2:3:4:5:6") t.testFromBytes([]byte{0x12, 127, 0xf, 0x7f, 0x7a, 0x7b}, "12:7f:f:7f:7a:7b") t.testFromBytes(eight[:], "0-0-0-0-0-0-0-0") t.testFromBytes([]byte{0, 0, 0, 1, 0, 0, 0, 1}, "0-0-0-1-0-0-0-1") t.testFromBytes([]byte{10, 11, 12, 13, 14, 15, 1, 2}, "a:b:c:d:e:f:1:2") t.testReverse("1:2:3:4:5:6", false, false) t.testReverse("1:1:2:2:3:3", false, false) t.testReverse("1:1:1:1:1:1", false, false) t.testReverse("0:0:0:0:0:0", true, true) t.testReverse("ff:ff:ff:ff:ff:ff", true, true) t.testReverse("ff:ff:ff:ff:ff:ff:ff:ff", true, true) t.testReverse("ff:80:ff:ff:01:ff", true, false) t.testReverse("ff:81:ff:ff:ff:ff", false, true) t.testReverse("ff:81:c3:42:24:ff", false, true) t.testReverse("ff:1:ff:ff:ff:ff", false, false) t.testReverse("11:22:33:44:55:66", false, false) t.testReverse("11:11:22:22:33:33", false, false) t.testReverse("11:11:22:22:33:33:44:55", false, false) t.testReverse("11:11:11:11:11:11:11:11", false, false) t.testReverse("0:0:0:0:0:0:00:00", true, true) t.testIncrement("ff:ff:ff:ff:f0:0:0:0", 1, "ff:ff:ff:ff:f0:0:0:1") t.testIncrement("ff:ff:ff:ff:f0:0:0:0", -1, "ff:ff:ff:ff:ef:ff:ff:ff") t.testIncrement("ff:ff:f0:0:0:0", 1, "ff:ff:f0:0:0:1") t.testIncrement("ff:ff:f0:0:0:0", -1, "ff:ff:ef:ff:ff:ff") t.testIncrement("80:0:0:0:0:0:0:0", math.MinInt64, "0:0:0:0:0:0:0:0") t.testIncrement("7f:ff:ff:ff:ff:ff:ff:ff", math.MinInt64, "") t.testIncrement("7f:ff:ff:ff:ff:ff:ff:fe", math.MinInt64, "") t.testIncrement("0:0:0:0:80:0:0:0", math.MinInt64, "") t.testIncrement("80:0:0:0:0:0:0:0", math.MaxInt64, "ff:ff:ff:ff:ff:ff:ff:ff") t.testIncrement("80:0:0:0:0:0:0:1", math.MaxInt64, "") t.testIncrement("ff:ff:ff:ff:80:0:0:0", -0x80000000, "ff:ff:ff:ff:0:0:0:0") t.testIncrement("ff:ff:ff:ff:7f:ff:ff:ff", -0x80000000, "ff:ff:ff:fe:ff:ff:ff:ff") t.testIncrement("ff:ff:ff:ff:7f:ff:ff:fe", -0x80000000, "ff:ff:ff:fe:ff:ff:ff:fe") t.testIncrement("0:0:0:0:80:0:0:0", -0x80000000, "0:0:0:0:0:0:0:0") t.testIncrement("0:0:0:0:7f:ff:ff:ff", -0x80000000, "") t.testIncrement("0:0:0:0:7f:ff:ff:ff", -0x80000000, "") t.testIncrement("0:0:0:0:7f:ff:ff:fe", -0x80000000, "") t.testIncrement("ff:ff:ff:ff:80:0:0:0", 0x7fffffff, "ff:ff:ff:ff:ff:ff:ff:ff") t.testIncrement("ff:ff:ff:ff:80:0:0:1", 0x7fffffff, "") t.testIncrement("ff:ff:80:0:0:0", -0x80000000, "ff:ff:0:0:0:0") t.testIncrement("ff:ff:7f:ff:ff:ff", -0x80000000, "ff:fe:ff:ff:ff:ff") t.testIncrement("ff:ff:7f:ff:ff:fe", -0x80000000, "ff:fe:ff:ff:ff:fe") t.testIncrement("0:0:80:0:0:0", -0x80000000, "0:0:0:0:0:0") t.testIncrement("0:0:7f:ff:ff:ff", -0x80000000, "") t.testIncrement("0:0:7f:ff:ff:ff", -0x80000000, "") t.testIncrement("0:0:7f:ff:ff:fe", -0x80000000, "") t.testIncrement("ff:ff:80:0:0:0", 0x7fffffff, "ff:ff:ff:ff:ff:ff") t.testIncrement("ff:ff:80:0:0:1", 0x7fffffff, "") t.testIncrement("0:0:0:0:0:0:0:1", 1, "0:0:0:0:0:0:0:2") t.testIncrement("0:0:0:0:0:0:0:1", 0, "0:0:0:0:0:0:0:1") t.testIncrement("0:0:0:0:0:0:0:1", -1, "0:0:0:0:0:0:0:0") t.testIncrement("0:0:0:0:0:0:0:1", -2, "") t.testIncrement("0:0:0:0:0:0:0:2", 1, "0:0:0:0:0:0:0:3") t.testIncrement("0:0:0:0:0:0:0:2", -1, "0:0:0:0:0:0:0:1") t.testIncrement("0:0:0:0:0:0:0:2", -2, "0:0:0:0:0:0:0:0") t.testIncrement("0:0:0:0:0:0:0:2", -3, "") t.testIncrement("0:0:0:0:0:1", 1, "0:0:0:0:0:2") t.testIncrement("0:0:0:0:0:1", 0, "0:0:0:0:0:1") t.testIncrement("0:0:0:0:0:1", -1, "0:0:0:0:0:0") t.testIncrement("0:0:0:0:0:1", -2, "") t.testIncrement("0:0:0:0:0:2", 1, "0:0:0:0:0:3") t.testIncrement("0:0:0:0:0:2", -1, "0:0:0:0:0:1") t.testIncrement("0:0:0:0:0:2", -2, "0:0:0:0:0:0") t.testIncrement("0:0:0:0:0:2", -3, "") t.testIncrement("1:0:0:0:0:0:0:1", 0, "1:0:0:0:0:0:0:1") t.testIncrement("1:0:0:0:0:0:0:1", 1, "1:0:0:0:0:0:0:2") t.testIncrement("1:0:0:0:0:0:0:1", -1, "1:0:0:0:0:0:0:0") t.testIncrement("1:0:0:0:0:0:0:1", -2, "0:ff:ff:ff:ff:ff:ff:ff") t.testIncrement("1:0:0:0:0:0:0:2", 1, "1:0:0:0:0:0:0:3") t.testIncrement("1:0:0:0:0:0:0:2", -1, "1:0:0:0:0:0:0:1") t.testIncrement("1:0:0:0:0:0:0:2", -2, "1:0:0:0:0:0:0:0") t.testIncrement("1:0:0:0:0:0:0:2", -3, "0:ff:ff:ff:ff:ff:ff:ff") t.testIncrement("1:0:0:0:0:1", 0, "1:0:0:0:0:1") t.testIncrement("1:0:0:0:0:1", 1, "1:0:0:0:0:2") t.testIncrement("1:0:0:0:0:1", -1, "1:0:0:0:0:0") t.testIncrement("1:0:0:0:0:1", -2, "0:ff:ff:ff:ff:ff") t.testIncrement("1:0:0:0:0:2", 1, "1:0:0:0:0:3") t.testIncrement("1:0:0:0:0:2", -1, "1:0:0:0:0:1") t.testIncrement("1:0:0:0:0:2", -2, "1:0:0:0:0:0") t.testIncrement("1:0:0:0:0:2", -3, "0:ff:ff:ff:ff:ff") t.testIncrement("0:0:0:0:0:0:0:fe", 2, "0:0:0:0:0:0:1:0") t.testIncrement("0:0:0:0:0:0:0:ff", 2, "0:0:0:0:0:0:1:1") t.testIncrement("0:0:0:0:0:0:1:ff", 2, "0:0:0:0:0:0:2:1") t.testIncrement("0:0:0:0:0:0:1:ff", -2, "0:0:0:0:0:0:1:fd") t.testIncrement("0:0:0:0:0:0:1:ff", -0x100, "0:0:0:0:0:0:0:ff") t.testIncrement("0:0:0:0:0:0:1:ff", -0x101, "0:0:0:0:0:0:0:fe") t.testIncrement("0:0:0:0:0:fe", 2, "0:0:0:0:1:0") t.testIncrement("0:0:0:0:0:ff", 2, "0:0:0:0:1:1") t.testIncrement("0:0:0:0:1:ff", 2, "0:0:0:0:2:1") t.testIncrement("0:0:0:0:1:ff", -2, "0:0:0:0:1:fd") t.testIncrement("0:0:0:0:1:ff", -0x100, "0:0:0:0:0:ff") t.testIncrement("0:0:0:0:1:ff", -0x101, "0:0:0:0:0:fe") t.testPrefixes("25:51:27:12:82:55", 16, -5, "25:51:27:12:82:55", "25:51:27:12:82:0", "25:51:27:12:82:40", "25:51:0:0:0:0", "25:51:0:0:0:0") t.testRadices("11:10:ff:7f:f3:2", "10001:10000:11111111:1111111:11110011:10", 2) t.testRadices("2:fe:7f:ff:10:11", "10:11111110:1111111:11111111:10000:10001", 2) t.testRadices("5:10:5:10:5:10", "101:10000:101:10000:101:10000", 2) t.testRadices("0:1:0:1:0:1:0:1", "0:1:0:1:0:1:0:1", 2) t.testRadices("1:0:1:0:1:0:1:0", "1:0:1:0:1:0:1:0", 2) t.testRadices("0:1:0:1:0:1", "0:1:0:1:0:1", 2) t.testRadices("1:0:1:0:1:0", "1:0:1:0:1:0", 2) t.testRadices("ff:7f:fe:2:7f:fe", "ff:7f:fe:2:7f:fe", 16) t.testRadices("2:fe:7f:ff:7f:fe", "2:fe:7f:ff:7f:fe", 16) t.testRadices("0:1:0:1:0:1", "0:1:0:1:0:1", 16) t.testRadices("1:0:1:0:1:0", "1:0:1:0:1:0", 16) t.testRadices("ff:7f:fe:2:7f:fe", "255:127:254:2:127:254", 10) t.testRadices("2:fe:7f:ff:7f:fe", "2:254:127:255:127:254", 10) t.testRadices("0:1:0:1:0:1", "0:1:0:1:0:1", 10) t.testRadices("1:0:1:0:1:0", "1:0:1:0:1:0", 10) t.testRadices("ff:7f:fe:2:7f:fe", "513:241:512:2:241:512", 7) t.testRadices("2:fe:7f:ff:7f:fe", "2:512:241:513:241:512", 7) t.testRadices("0:1:0:1:0:1:0:1", "0:1:0:1:0:1:0:1", 7) t.testRadices("1:0:1:0:1:0:1:0", "1:0:1:0:1:0:1:0", 7) t.testRadices("0:1:0:1:0:1", "0:1:0:1:0:1", 7) t.testRadices("1:0:1:0:1:0", "1:0:1:0:1:0", 7) t.testRadices("ff:7f:fe:2:7f:fe", "377:177:376:2:177:376", 8) t.testRadices("2:fe:7f:ff:7f:fe", "2:376:177:377:177:376", 8) t.testRadices("0:1:0:1:0:1", "0:1:0:1:0:1", 8) t.testRadices("1:0:1:0:1:0", "1:0:1:0:1:0", 8) t.testRadices("ff:7f:fe:2:7f:fe", "120:87:11e:2:87:11e", 15) t.testRadices("2:fe:7f:ff:7f:fe", "2:11e:87:120:87:11e", 15) t.testRadices("0:1:0:1:0:1", "0:1:0:1:0:1", 15) t.testRadices("1:0:1:0:1:0", "1:0:1:0:1:0", 15) t.testNormalized("A:B:C:D:E:F:A:B", "0a:0b:0c:0d:0e:0f:0a:0b") t.testNormalized("AB:AB:CC:Dd:Ee:fF:aA:Bb", "ab:ab:cc:dd:ee:ff:aa:bb") t.testNormalized("12:CD:CC:dd:Ee:fF:AA:Bb", "12:cd:cc:dd:ee:ff:aa:bb") t.testNormalized("12:CD:CC:dd:Ee:fF", "12:cd:cc:dd:ee:ff") t.testNormalized("0:0:0:0:0:0:0:0", "00:00:00:00:00:00:00:00") t.testNormalized("0:0:0:0:0:0", "00:00:00:00:00:00") t.testNormalized("0:1:0:2:0:3:0:0", "00:01:00:02:00:03:00:00") t.testNormalized("0:1:0:2:0:3", "00:01:00:02:00:03") t.testNormalized("A-B-C-D-E-F-A-B", "0a:0b:0c:0d:0e:0f:0a:0b") t.testNormalized("AB-AB-CC-Dd-Ee-fF-aA-Bb", "ab:ab:cc:dd:ee:ff:aa:bb") t.testNormalized("12-CD-CC-dd-Ee-fF-AA-Bb", "12:cd:cc:dd:ee:ff:aa:bb") t.testNormalized("12-CD-CC-dd-Ee-fF", "12:cd:cc:dd:ee:ff") t.testNormalized("0-0-0-0-0-0-0-0", "00:00:00:00:00:00:00:00") t.testNormalized("0-0-0-0-0-0", "00:00:00:00:00:00") t.testNormalized("0-1-0-2-0-3-0-0", "00:01:00:02:00:03:00:00") t.testNormalized("0-1-0-2-0-3", "00:01:00:02:00:03") t.testNormalized("A B C D E F A B", "0a:0b:0c:0d:0e:0f:0a:0b") t.testNormalized("AB AB CC Dd Ee fF aA Bb", "ab:ab:cc:dd:ee:ff:aa:bb") t.testNormalized("12 CD CC dd Ee fF AA Bb", "12:cd:cc:dd:ee:ff:aa:bb") t.testNormalized("12 CD CC dd Ee fF", "12:cd:cc:dd:ee:ff") t.testNormalized("0 0 0 0 0 0 0 0", "00:00:00:00:00:00:00:00") t.testNormalized("0 0 0 0 0 0", "00:00:00:00:00:00") t.testNormalized("0 1 0 2 0 3 0 0", "00:01:00:02:00:03:00:00") t.testNormalized("0 1 0 2 0 3", "00:01:00:02:00:03") t.testNormalized("0A0B.0C0D.0E0F", "0a:0b:0c:0d:0e:0f") t.testNormalized("A0B.C0D.E0F", "0a:0b:0c:0d:0e:0f") t.testNormalized("AB.C00.DE0F", "00:ab:0c:00:de:0f") t.testNormalized("A0.B00.c00d", "00:a0:0b:00:c0:0d") t.testNormalized("0A0B.0C0D.0E0F.0a0b", "0a:0b:0c:0d:0e:0f:0a:0b") t.testNormalized("A0B.C0D.E0F.1234", "0a:0b:0c:0d:0e:0f:12:34") t.testNormalized("AB.C00.DE0F.123", "00:ab:0c:00:de:0f:01:23") t.testNormalized("A0.B00.c00d.4", "00:a0:0b:00:c0:0d:00:04") t.testNormalized("12CD.CCdd.EefF", "12:cd:cc:dd:ee:ff") t.testNormalized("0000.0000.0000", "00:00:00:00:00:00") t.testNormalized("0002.0003.0003", "00:02:00:03:00:03") t.testNormalized("0A0B0C-0D0E0F", "0a:0b:0c:0d:0e:0f") t.testNormalized("0A0B0C-0D0E0F", "0a:0b:0c:0d:0e:0f") t.testNormalized("0A0B0C-0D0E0F0A0B", "0a:0b:0c:0d:0e:0f:0a:0b") t.testNormalized("ABABCC-DdEefFaABb", "ab:ab:cc:dd:ee:ff:aa:bb") t.testNormalized("12CDCC-ddEefFAABb", "12:cd:cc:dd:ee:ff:aa:bb") t.testNormalized("12CDCC-ddEefF", "12:cd:cc:dd:ee:ff") t.testNormalized("aaaabb-bbcccc", "aa:aa:bb:bb:cc:cc") t.testNormalized("010233045506", "01:02:33:04:55:06") t.testNormalized("000000-0000000000", "00:00:00:00:00:00:00:00") t.testNormalized("000000-000000", "00:00:00:00:00:00") t.testNormalized("000100-0200030000", "00:01:00:02:00:03:00:00") t.testNormalized("000100-020003", "00:01:00:02:00:03") t.testNormalized("0A0B0C0D0E0F", "0a:0b:0c:0d:0e:0f") t.testNormalized("0x0A0B0C0D0E0F", "0a:0b:0c:0d:0e:0f") t.testNormalized("0A0B0C0D0E0F0A0B", "0a:0b:0c:0d:0e:0f:0a:0b") t.testNormalized("ABABCCDdEefFaABb", "ab:ab:cc:dd:ee:ff:aa:bb") t.testNormalized("12CDCCddEefFAABb", "12:cd:cc:dd:ee:ff:aa:bb") t.testNormalized("12CDCCddEefF", "12:cd:cc:dd:ee:ff") t.testNormalized("0000000000000000", "00:00:00:00:00:00:00:00") t.testNormalized("000000000000", "00:00:00:00:00:00") t.testNormalized("0001000200030000", "00:01:00:02:00:03:00:00") t.testNormalized("000100020003", "00:01:00:02:00:03") t.testCanonical("A:B:C:D:E:F:A:B", "0a-0b-0c-0d-0e-0f-0a-0b") t.testCanonical("AB:AB:CC:Dd:Ee:fF:aA:Bb", "ab-ab-cc-dd-ee-ff-aa-bb") t.testCanonical("12:CD:CC:dd:Ee:fF:AA:Bb", "12-cd-cc-dd-ee-ff-aa-bb") t.testCanonical("12:CD:CC:dd:Ee:fF", "12-cd-cc-dd-ee-ff") t.testCanonical("0:0:0:0:0:0:0:0", "00-00-00-00-00-00-00-00") t.testCanonical("0:0:0:0:0:0", "00-00-00-00-00-00") t.testCanonical("0:1:0:2:0:3:0:0", "00-01-00-02-00-03-00-00") t.testCanonical("0:1:0:2:0:3", "00-01-00-02-00-03") t.testCanonical("A-B-C-D-E-F-A-B", "0a-0b-0c-0d-0e-0f-0a-0b") t.testCanonical("AB-AB-CC-Dd-Ee-fF-aA-Bb", "ab-ab-cc-dd-ee-ff-aa-bb") t.testCanonical("12-CD-CC-dd-Ee-fF-AA-Bb", "12-cd-cc-dd-ee-ff-aa-bb") t.testCanonical("12-CD-CC-dd-Ee-fF", "12-cd-cc-dd-ee-ff") t.testCanonical("0-0-0-0-0-0-0-0", "00-00-00-00-00-00-00-00") t.testCanonical("0-0-0-0-0-0", "00-00-00-00-00-00") t.testCanonical("0-1-0-2-0-3-0-0", "00-01-00-02-00-03-00-00") t.testCanonical("0-1-0-2-0-3", "00-01-00-02-00-03") t.testCanonical("A B C D E F A B", "0a-0b-0c-0d-0e-0f-0a-0b") t.testCanonical("AB AB CC Dd Ee fF aA Bb", "ab-ab-cc-dd-ee-ff-aa-bb") t.testCanonical("12 CD CC dd Ee fF AA Bb", "12-cd-cc-dd-ee-ff-aa-bb") t.testCanonical("12 CD CC dd Ee fF", "12-cd-cc-dd-ee-ff") t.testCanonical("0 0 0 0 0 0 0 0", "00-00-00-00-00-00-00-00") t.testCanonical("0 0 0 0 0 0", "00-00-00-00-00-00") t.testCanonical("0 1 0 2 0 3 0 0", "00-01-00-02-00-03-00-00") t.testCanonical("0 1 0 2 0 3", "00-01-00-02-00-03") t.testCanonical("0A0B.0C0D.0E0F", "0a-0b-0c-0d-0e-0f") t.testCanonical("BA0B.DC0D.FE0F", "ba-0b-dc-0d-fe-0f") t.testCanonical("A0B.C0D.E0F", "0a-0b-0c-0d-0e-0f") t.testCanonical("AB.C00.DE0F", "00-ab-0c-00-de-0f") t.testCanonical("A.B.c", "00-0a-00-0b-00-0c") t.testCanonical("12CD.CCdd.EefF", "12-cd-cc-dd-ee-ff") t.testCanonical("0000.0000.0000", "00-00-00-00-00-00") t.testCanonical("0002.0003.0003", "00-02-00-03-00-03") t.testCanonical("0020.0030.0030", "00-20-00-30-00-30") t.testCanonical("0A0B0C-0D0E0F", "0a-0b-0c-0d-0e-0f") t.testCanonical("0A0B0C-0D0E0F0A0B", "0a-0b-0c-0d-0e-0f-0a-0b") t.testCanonical("ABABCC-DdEefFaABb", "ab-ab-cc-dd-ee-ff-aa-bb") t.testCanonical("12CDCC-ddEefFAABb", "12-cd-cc-dd-ee-ff-aa-bb") t.testCanonical("12CDCC-ddEefF", "12-cd-cc-dd-ee-ff") t.testCanonical("000000-0000000000", "00-00-00-00-00-00-00-00") t.testCanonical("000000-000000", "00-00-00-00-00-00") t.testCanonical("000100-0200030000", "00-01-00-02-00-03-00-00") t.testCanonical("000100-020003", "00-01-00-02-00-03") t.testCanonical("0A0B0C0D0E0F", "0a-0b-0c-0d-0e-0f") t.testCanonical("0A0B0C0D0E0F0A0B", "0a-0b-0c-0d-0e-0f-0a-0b") t.testCanonical("ABABCCDdEefFaABb", "ab-ab-cc-dd-ee-ff-aa-bb") t.testCanonical("12CDCCddEefFAABb", "12-cd-cc-dd-ee-ff-aa-bb") t.testCanonical("12CDCCddEefF", "12-cd-cc-dd-ee-ff") t.testCanonical("0000000000000000", "00-00-00-00-00-00-00-00") t.testCanonical("000000000000", "00-00-00-00-00-00") t.testCanonical("0001000200030000", "00-01-00-02-00-03-00-00") t.testCanonical("000100020003", "00-01-00-02-00-03") t.testMatches(true, "0A0B0C0D0E0F", "0a0b0c-0d0e0f") t.testMatches(true, "0A0B0C0D0E0F", "0a:0b:0c:0d:0e:0f") t.testMatches(true, "0A 0B 0C 0D 0E 0F", "0a:0b:0c:0d:0e:0f") t.testMatches(true, "0A 0B 0C 0D 0E 0F", "0a-0b-0c-0d-0e-0f") t.testMatches(true, "0A 0B 0C 0D 0E 0F", "a-b-c-d-e-f") t.testMatches(false, "0A 0B 0C 0D 0E 0F", "a-b-c-d-e-f-a-b") t.testMatches(true, "0A0B.0C0D.0E0F", "0a:0b:0c:0d:0e:0f") t.testMatches(false, "0A0B.1C0D.0E0F", "0a:0b:0c:0d:0e:0f") t.testMatches(false, "0A0B.1C0D.0E0F", "aa:bb:0a:0b:0c:0d:0e:0f") t.testDelimitedCount("1,2-3-4,5-6-7-8", 4) //this will iterate through 1.3.4.6 1.3.5.6 2.3.4.6 2.3.5.6 t.testDelimitedCount("1,2-3,6-7-8-4,5-6,8", 16) //this will iterate through 1.3.4.6 1.3.5.6 2.3.4.6 2.3.5.6 t.testDelimitedCount("1:2:3:6:4:5", 1) //this will iterate through 1.3.4.6 1.3.5.6 2.3.4.6 2.3.5.6 t.testDelimitedCount("1:2,3,4:3:6:4:5,ff,7,8,99", 15) //this will iterate through 1.3.4.6 1.3.5.6 2.3.4.6 2.3.5.6 t.testContains("1.2.3.4", "1.2.3.4", true) t.testContains("1111.2222.3333", "1111.2222.3333", true) t.testNotContains("1111.2222.3333", "1111.2222.3233") t.testContains("a:b:c:d:e:f:a:b", "a:b:c:d:e:f:a:b", true) t.testLongShort("ff:ff:ff:ff:ff:ff:ff:ff", "ff:ff:ff:ff:ff:ff") t.testLongShort("12-cd-cc-dd-ee-ff-aa-bb", "12-cd-cc-dd-ee-ff") t.testLongShort("12CD.CCdd.EefF.a", "12CD.EefF.a") t.testLongShort("0A0B0C-0D0E0F0A0B", "0A0B0C-0D0E0F") t.testLongShort("ee:ff:aa:bb:cc:dd:ee:ff", "ee:ff:aa:bb:cc:dd") t.testLongShort("e:f:a:b:c:d:e:f", "e:f:a:b:c:d") t.testSections("00:21:2f:b5:6e:10") t.testSections("39-A7-94-07-CB-D0") t.testSections("0012.7feb.6b40") t.testSections("fe:ef:00:21:2f:b5:6e:10") t.testSections("fe-ef-39-A7-94-07-CB-D0") t.testSections("1234.0012.7feb.6b40") zerosPref := [9]ipaddr.PrefixLen{} t.testInsertAndAppendPrefs("a:b:c:d:e:f:aa:bb", "1:2:3:4:5:6:7:8", zerosPref[:]) t.testReplace("a:b:c:d:e:f:aa:bb", "1:2:3:4:5:6:7:8") t.testInvalidMACValues() var sixZeros [6]int var eightZeros [8]int t.testMACValues([]int{1, 2, 3, 4, 5, 6}, "1108152157446") t.testMACValues([]int{1, 2, 3, 4, 5, 6, 7, 8}, "72623859790382856") t.testMACValues(sixZeros[:], "0") t.testMACValues(eightZeros[:], "0") t.testMACValues([]int{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, strconv.Itoa(0xffffffffffff)) sixty4 := new(big.Int).SetUint64(0xffffffffffffffff) //BigInteger thirtyTwo = BigInteger.valueOf(0xffffffffL); //BigInteger sixty4 = thirtyTwo.shiftLeft(32).or(thirtyTwo); t.testMACValuesBig([]int{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, sixty4.String(), "-1") t.testMACIPv6("aaaa:bbbb:cccc:dddd:0221:2fff:feb5:6e10", "00:21:2f:b5:6e:10") t.testMACIPv6("fe80::0e3a:bbff:fe2a:cd23", "0c:3a:bb:2a:cd:23") t.testMACIPv6("ffff:ffff:ffff:ffff:3BA7:94FF:FE07:CBD0", "39-A7-94-07-CB-D0") t.testMACIPv6("FE80::212:7FFF:FEEB:6B40", "0012.7feb.6b40") t.testMACIPv6("2001:DB8::212:7FFF:FEEB:6B40", "0012.7feb.6b40") t.testStrings() } func (t macAddressTester) testMACValues(segs []int, decimal string) { t.testMACValuesBig(segs, decimal, "") } func (t macAddressTester) testMACValuesBig(segs []int, decimal, negativeDecimal string) { vals := make([]byte, len(segs)) strb := strings.Builder{} var longval uint64 bigInteger := bigZero() bitsPerSegment := ipaddr.MACBitsPerSegment for i := 0; i < len(segs); i++ { seg := segs[i] if strb.Len() > 0 { strb.WriteByte(':') } strb.WriteString(strconv.FormatInt(int64(seg), 16)) vals[i] = byte(seg) longval = (longval << uint(bitsPerSegment)) | uint64(seg) bigInteger = bigInteger.Add(bigInteger.Lsh(bigInteger, uint(bitsPerSegment)), new(big.Int).SetInt64(int64(seg))) } addr := [3]*ipaddr.MACAddress{} i := 0 addr[i] = t.createMACAddressFromBytes(vals) i++ addr[i] = t.createMACAddress(strb.String()).GetAddress() i++ addr[i] = t.createMACAddressFromUint64(longval, len(segs) == 8) //i++ for j := 0; j < len(addr); j++ { for k := j; k < len(addr); k++ { if !addr[k].Equal(addr[j]) || !addr[j].Equal(addr[k]) { t.addFailure(newSegmentSeriesFailure("failed equals: "+addr[k].String()+" and "+addr[j].String(), addr[k])) } } } if decimal != "" { for i = 0; i < len(addr); i++ { if decimal != (addr[i].GetValue().String()) { t.addFailure(newSegmentSeriesFailure("failed equals: "+addr[i].GetValue().String()+" and "+decimal, addr[i])) } longVal := addr[i].Uint64Value() lv := strconv.FormatUint(longVal, 10) if longVal < 0 { if lv != negativeDecimal { t.addFailure(newSegmentSeriesFailure("failed equals: "+lv+" and "+decimal, addr[i])) } } else if decimal != lv { t.addFailure(newSegmentSeriesFailure("failed equals: "+lv+" and "+decimal, addr[i])) } } } } func (t macAddressTester) testInvalidMACValues() { thebytes := [9]byte{} thebytes[0] = 1 addr, err := ipaddr.NewMACAddressFromBytes(thebytes[:]) if err == nil { t.addFailure(newSegmentSeriesFailure("failed expected error for "+addr.String(), addr)) } thebytes = [9]byte{} addr, err = ipaddr.NewMACAddressFromBytes(thebytes[:]) if err != nil { t.addFailure(newSegmentSeriesFailure("failed unexpected error for "+addr.String(), addr)) } bytes2 := [8]byte{} addr, err = ipaddr.NewMACAddressFromBytes(bytes2[:]) if err != nil { t.addFailure(newSegmentSeriesFailure("failed unexpected error for "+addr.String(), addr)) } bytes3 := [7]byte{} addr, err = ipaddr.NewMACAddressFromBytes(bytes3[:]) if err != nil { t.addFailure(newSegmentSeriesFailure("failed unexpected error for "+addr.String(), addr)) } bytes4 := [6]byte{} addr, err = ipaddr.NewMACAddressFromBytes(bytes4[:]) if err != nil { t.addFailure(newSegmentSeriesFailure("failed unexpected error for "+addr.String(), addr)) } bytes5 := [5]byte{} addr, err = ipaddr.NewMACAddressFromBytes(bytes5[:]) if err != nil { t.addFailure(newSegmentSeriesFailure("failed unexpected error for "+addr.String(), addr)) } addr = ipaddr.NewMACAddressFromVals(func(segmentIndex int) ipaddr.MACSegInt { var val = 256 // will be truncated to 0 return ipaddr.MACSegInt(val) }) if !addr.IsZero() { t.addFailure(newSegmentSeriesFailure("failed expected exception for "+addr.String(), addr)) } addr = ipaddr.NewMACAddressFromVals(func(segmentIndex int) ipaddr.MACSegInt { var val = -1 // will be truncated to 0 return ipaddr.MACSegInt(val) }) if !addr.IsMax() { t.addFailure(newSegmentSeriesFailure("failed expected exception for "+addr.String(), addr)) } addr = ipaddr.NewMACAddressFromVals(func(segmentIndex int) ipaddr.MACSegInt { var val = 255 // will be truncated to 0 return ipaddr.MACSegInt(val) }) if !addr.IsMax() { t.addFailure(newSegmentSeriesFailure("failed expected exception for "+addr.String(), addr)) } } func (t macAddressTester) testInsertAndAppend(front, back string, expectedPref []ipaddr.BitCount) { is := make([]ipaddr.PrefixLen, len(expectedPref)) for i := 0; i < len(expectedPref); i++ { is[i] = cacheTestBits(expectedPref[i]) } t.testInsertAndAppendPrefs(front, back, is) } func (t macAddressTester) testInsertAndAppendPrefs(front, back string, expectedPref []ipaddr.PrefixLen) { f := t.createMACAddress(front).GetAddress() b := t.createMACAddress(back).GetAddress() t.testAppendAndInsert(f.ToAddressBase(), b.ToAddressBase(), f.GetSegmentStrings(), b.GetSegmentStrings(), ipaddr.MACColonSegmentSeparator, expectedPref, true) } func (t macAddressTester) testReplace(front, back string) { f := t.createMACAddress(front).GetAddress() b := t.createMACAddress(back).GetAddress() t.testBase.testReplace(f.ToAddressBase(), b.ToAddressBase(), f.GetSegmentStrings(), b.GetSegmentStrings(), ipaddr.MACColonSegmentSeparator, true) } func (t macAddressTester) testSections(addrString string) { w := t.createMACAddress(addrString) v := w.GetAddress() odiSection := v.GetODISection() ouiSection := v.GetOUISection() front := v.GetSubSection(0, 3) back := v.GetTrailingSection(front.GetSegmentCount()) first := !ouiSection.Equal(front) if (first) || !all3Equals(ouiSection.GetPrefixLen(), front.GetPrefixLen(), prefixAdjust(v.GetPrefixLen(), 24, 0)) { if first { t.addFailure(newMACFailure("failed oui "+ouiSection.String()+" expected "+front.String(), w)) } else { t.addFailure(newMACFailure("failed oui pref "+ouiSection.GetPrefixLen().String()+" expected "+prefixAdjust(v.GetPrefixLen(), 24, 0).String()+" for "+front.String(), w)) } } else { first = !odiSection.Equal(back) if (first) || !all3Equals(odiSection.GetPrefixLen(), back.GetPrefixLen(), prefixAdjust(v.GetPrefixLen(), 64, -24)) { if first { t.addFailure(newMACFailure("failed odi "+odiSection.String()+" expected "+back.String(), w)) } else { t.addFailure(newMACFailure("failed odi pref "+odiSection.GetPrefixLen().String()+" expected "+prefixAdjust(v.GetPrefixLen(), 64, -24).String()+" for "+back.String(), w)) } } else { middle := v.GetSubSection(1, 5) odiSection2 := odiSection.GetSubSection(0, 5-ouiSection.GetSegmentCount()) ouiSection2 := ouiSection.GetTrailingSection(1) odiSection = middle.GetTrailingSection(2) ouiSection = middle.GetSubSection(0, 2) if !ouiSection.Equal(ouiSection2) || !ouiSection.GetPrefixLen().Equal(ouiSection2.GetPrefixLen()) { t.addFailure(newMACFailure("failed odi "+ouiSection.String()+" expected "+ouiSection2.String(), w)) } else if !odiSection.Equal(odiSection2) || !odiSection.GetPrefixLen().Equal(odiSection2.GetPrefixLen()) { t.addFailure(newMACFailure("failed odi "+odiSection.String()+" expected "+odiSection2.String(), w)) } else if ouiSection.GetSegmentCount() != 2 || ouiSection2.GetSegmentCount() != 2 { t.addFailure(newMACFailure("failed oui count "+strconv.Itoa(ouiSection.GetSegmentCount())+" expected 2", w)) } else if odiSection.GetSegmentCount() != 2 || odiSection2.GetSegmentCount() != 2 { t.addFailure(newMACFailure("failed oui count "+strconv.Itoa(odiSection.GetSegmentCount())+" expected 2", w)) } else { odiEmpty := odiSection.GetSubSection(0, 0) ouiEmpty := ouiSection.GetSubSection(0, 0) if !odiEmpty.Equal(ouiEmpty) || odiEmpty.GetSegmentCount() > 0 || ouiEmpty.GetSegmentCount() > 0 { t.addFailure(newMACFailure("failed odi empty "+odiEmpty.String()+" oui empty "+ouiEmpty.String(), w)) } else { midEmpty := middle.GetSubSection(0, 0) if !ouiEmpty.Equal(midEmpty) || midEmpty.GetSegmentCount() != 0 { t.addFailure(newMACFailure("failed odi empty "+midEmpty.String()+" expected "+ouiEmpty.String(), w)) } else { midEmpty2 := middle.GetSubSection(1, 1) if !ouiEmpty.Equal(midEmpty2) || midEmpty2.GetSegmentCount() != 0 { t.addFailure(newMACFailure("failed odi empty "+midEmpty2.String()+" expected "+ouiEmpty.String(), w)) } } } } } } t.incrementTestCount() } func prefixAdjust(existing ipaddr.PrefixLen, max, adj ipaddr.BitCount) ipaddr.PrefixLen { if existing == nil { return nil } if existing.Len() > max { return nil } res := existing.Len() + adj if res < 0 { return cacheTestBits(0) } return cacheTestBits(res) } func (t macAddressTester) testLongShort(longAddr, shortAddr string) { t.testLongShort2(longAddr, shortAddr, false) } func (t macAddressTester) testLongShort2(longAddr, shortAddr string, shortCanBeLong bool) { params := new(addrstrparam.MACAddressStringParamsBuilder).SetPreferredLen(addrstrparam.MAC48Len).ToParams() longString := ipaddr.NewMACAddressStringParams(longAddr, params) shortString := ipaddr.NewMACAddressStringParams(shortAddr, params) if !shortString.IsValid() { t.addFailure(newMACFailure("short not valid "+shortString.String(), shortString)) } if longString.IsValid() { t.addFailure(newMACFailure("long valid "+longString.String(), longString)) } params = new(addrstrparam.MACAddressStringParamsBuilder).SetPreferredLen(addrstrparam.EUI64Len).ToParams() longString = ipaddr.NewMACAddressStringParams(longAddr, params) shortString = ipaddr.NewMACAddressStringParams(shortAddr, params) isValid := shortString.IsValid() if shortCanBeLong { isValid = !isValid } if isValid { t.addFailure(newMACFailure("short valid "+shortString.String(), shortString)) } if !longString.IsValid() { t.addFailure(newMACFailure("long not valid "+longString.String(), longString)) } if longString.GetAddress().GetSegmentCount() != ipaddr.ExtendedUniqueIdentifier64SegmentCount { t.addFailure(newMACFailure("long not enough segments "+longString.String(), longString)) } if shortCanBeLong && shortString.GetAddress().GetSegmentCount() != ipaddr.ExtendedUniqueIdentifier64SegmentCount { t.addFailure(newMACFailure("also not enough segments "+shortString.String(), shortString)) } params = new(addrstrparam.MACAddressStringParamsBuilder).SetPreferredLen(addrstrparam.UnspecifiedMACLen).ToParams() longString = ipaddr.NewMACAddressStringParams(longAddr, params) shortString = ipaddr.NewMACAddressStringParams(shortAddr, params) if !shortString.IsValid() { t.addFailure(newMACFailure("short not valid "+shortString.String(), shortString)) } if !longString.IsValid() { t.addFailure(newMACFailure("long not valid "+longString.String(), longString)) } t.incrementTestCount() } func (t macAddressTester) testContains(addr1, addr2 string, equal bool) { w := t.createMACAddress(addr1).GetAddress() w2 := t.createMACAddress(addr2).GetAddress() if !w.Contains(w2) { t.addFailure(newSegmentSeriesFailure("failed "+w2.String(), w)) } else { otherContains := w2.Contains(w) if equal { otherContains = !otherContains } if otherContains { t.addFailure(newSegmentSeriesFailure("failed "+w.String(), w2)) } } t.incrementTestCount() } func (t macAddressTester) testNotContains(cidr1, cidr2 string) { w := t.createMACAddress(cidr1).GetAddress() w2 := t.createMACAddress(cidr2).GetAddress() if w.Contains(w2) { t.addFailure(newSegmentSeriesFailure("failed "+w2.String(), w)) } else if w2.Contains(w) { t.addFailure(newSegmentSeriesFailure("failed "+w.String(), w2)) } t.incrementTestCount() } func (t macAddressTester) testDelimitedCount(str string, expectedCount int) { delims := ipaddr.DelimitedAddressString(str) strs := delims.ParseDelimitedSegments() var set []*ipaddr.MACAddress count := 0 for strs.HasNext() { addr, err := t.createMACAddress(strs.Next()).ToAddress() if err != nil { t.addFailure(newFailure("unexpected error "+err.Error(), nil)) return } if addr == nil { t.addFailure(newFailure("unexpected nil address", nil)) return } set = append(set, addr) count++ } if count != expectedCount || len(set) != count || count != delims.CountDelimitedAddresses() { t.addFailure(newFailure("count mismatch, count: "+strconv.Itoa(count)+" set count: "+strconv.Itoa(len(set))+" calculated count: "+strconv.Itoa(delims.CountDelimitedAddresses())+" expected: "+strconv.Itoa(expectedCount), nil)) } t.incrementTestCount() } func (t macAddressTester) testMatches(matches bool, host1Str, host2Str string) { h1 := t.createMACAddress(host1Str) h2 := t.createMACAddress(host2Str) if matches != h1.Equal(h2) { t.addFailure(newMACFailure("failed: match with "+h2.String(), h1)) } else { if matches != h2.Equal(h1) { t.addFailure(newMACFailure("failed: match with "+h1.String(), h2)) } else { comparison := h1.Compare(h2) == 0 if matches { comparison = !comparison } if comparison { t.addFailure(newMACFailure("failed: match with "+h1.String(), h2)) } else { comparison := h2.Compare(h1) == 0 if matches { comparison = !comparison } if comparison { t.addFailure(newMACFailure("failed: match with "+h2.String(), h1)) } } } } t.incrementTestCount() } func (t macAddressTester) testNormalized(original, expected string) { w := t.createMACAddress(original) val := w.GetAddress() if val == nil { t.addFailure(newMACFailure("normalization was nil", w)) } else { normalized := val.ToNormalizedString() if expected != normalized { t.addFailure(newMACFailure("mac normalization was "+normalized, w)) } } t.incrementTestCount() } func (t macAddressTester) testCanonical(original, expected string) { w := t.createMACAddress(original) val := w.GetAddress() if val == nil { t.addFailure(newMACFailure("normalization was nil", w)) } else { normalized := val.ToCanonicalString() if expected != normalized { t.addFailure(newMACFailure("canonical was "+normalized, w)) } } t.incrementTestCount() } func (t macAddressTester) testRadices(original, expected string, radix int) { w := t.createMACAddress(original) val := w.GetAddress() options := new(addrstr.MACStringOptionsBuilder).SetRadix(radix).ToOptions() normalized := val.ToCustomString(options) if normalized != expected { t.addFailure(newMACFailure("string was "+normalized+" expected was "+expected, w)) } t.incrementTestCount() } func (t macAddressTester) testReverse(addressStr string, bitsReversedIsSame, bitsReversedPerByteIsSame bool) { str := t.createMACAddress(addressStr) t.testBase.testReverse(str.GetAddress().ToAddressBase().Wrap(), bitsReversedIsSame, bitsReversedPerByteIsSame) t.incrementTestCount() } func (t macAddressTester) testIncrement(originalStr string, increment int64, resultStr string) { var addr *ipaddr.MACAddress if resultStr != "" { addr = t.createMACAddress(resultStr).GetAddress() } t.testBase.testIncrement(t.createMACAddress(originalStr).GetAddress().ToAddressBase(), increment, addr.ToAddressBase()) } func (t macAddressTester) testPrefix(original string, prefixLength, equivalentPrefix ipaddr.PrefixLen) { mac := t.createMACAddress(original).GetAddress() var bc = mac.GetBitCount() if prefixLength != nil { bc = prefixLength.Len() } t.testBase.testPrefix(mac, prefixLength, bc, equivalentPrefix) t.incrementTestCount() } func (t macAddressTester) testPrefixes(original string, prefix, adjustment ipaddr.BitCount, next string, previous, adjusted, prefixSet, prefixApplied string) { t.testBase.testSegmentSeriesPrefixes(t.createMACAddress(original).GetAddress().Wrap(), prefix, adjustment, t.createMACAddress(next).GetAddress().Wrap(), t.createMACAddress(previous).GetAddress().Wrap(), t.createMACAddress(adjusted).GetAddress().Wrap(), t.createMACAddress(prefixSet).GetAddress().Wrap(), t.createMACAddress(prefixApplied).GetAddress().Wrap()) t.incrementTestCount() } func (t macAddressTester) testMACStrings(addr, normalizedString, //toColonDelimitedString compressedString, canonicalString, //toDashedString dottedString, spaceDelimitedString, singleHex string) { w := t.createMACAddress(addr) ipAddr := w.GetAddress() t.testBase.testMACStrings(w, ipAddr, normalizedString, compressedString, canonicalString, dottedString, spaceDelimitedString, singleHex) } func (t macAddressTester) mactest(pass bool, x string) { t.mactestZero(pass, x, false) } func (t macAddressTester) mactestZero(pass bool, x string, isZero bool) { t.mactestImpl(pass, t.createMACAddress(x), isZero) } func (t macAddressTester) mactestImpl(pass bool, addr *ipaddr.MACAddressString, isZero bool) { //notBoth means we validate as IPv4 or as IPv6, we don't validate as either one if t.isNotExpected(pass, addr) { t.addFailure(newMACFailure("parse failure: "+addr.String(), addr)) } else { zeroPass := pass && !isZero if t.isNotExpectedNonZero(zeroPass, addr) { t.addFailure(newMACFailure("zero parse failure: "+addr.String(), addr)) } else { //test the bytes if pass && len(addr.String()) > 0 && addr.GetAddress() != nil { taddr := addr.GetAddress() if t.allowsRange() && taddr.IsMultiple() { } else if !t.testBytes(taddr) { t.addFailure(newMACFailure("parse bytes failure: "+addr.String(), addr)) } } } } t.incrementTestCount() } func (t macAddressTester) testBytes(addr *ipaddr.MACAddress) bool { failed := false macAddrbytes := addr.Bytes() another := t.createMACAddressFromBytes(macAddrbytes) if !addr.Equal(another) { t.addFailure(newSegmentSeriesFailure(addr.String(), addr)) } var builder strings.Builder builder.WriteString(addr.ToColonDelimitedString()) if addr.GetSegmentCount() < 8 { builder.WriteString("::") } ipstr := builder.String() inetAddress := net.ParseIP(ipstr) ipv6Bytes := inetAddress macBytes := make([]byte, len(macAddrbytes)) for i := 0; i < len(macBytes); i++ { macBytes[i] = ipv6Bytes[(i<<1)+1] } if !bytes.Equal(macBytes, macAddrbytes) { failed = true t.addFailure(newSegmentSeriesFailure("bytes on addr "+inetAddress.String(), addr)) } return !failed } func (t macAddressTester) testFromBytes(bytes []byte, expected string) { addr := t.createMACAddressFromBytes(bytes) addr2 := t.createMACAddress(expected) result := addr.Equal(addr2.GetAddress()) if !result { t.addFailure(newSegmentSeriesFailure("created was "+addr.String()+" expected was "+addr2.String(), addr)) } else { var val uint64 for i := 0; i < len(bytes); i++ { val <<= 8 val |= uint64(bytes[i]) } addr = t.createMACAddressFromUint64(val, len(bytes) > 6) result = addr.Equal(addr2.GetAddress()) if !result { t.addFailure(newSegmentSeriesFailure("created was "+addr.String()+" expected was "+addr2.String(), addr)) } } t.incrementTestCount() } func (t macAddressTester) isNotExpected(expectedPass bool, addr *ipaddr.MACAddressString) bool { err := addr.Validate() if err != nil { return expectedPass } return !expectedPass } func (t macAddressTester) isNotExpectedNonZero(expectedPass bool, addr *ipaddr.MACAddressString) bool { if !addr.IsValid() { return expectedPass } //if expectedPass is true, we are expecting a non-zero address //return true to indicate we have gotten something not expected if addr.GetAddress() != nil && addr.GetAddress().IsZero() { return expectedPass } return !expectedPass } func (t macAddressTester) testMACIPv6(ipv6, mac string) { ipv6Str := t.createAddress(ipv6) macStr := t.createMACAddress(mac) addr := ipv6Str.GetAddress().ToIPv6() back := addr.GetHostSectionLen(64) if !addr.IsEUI64() { t.addFailure(newSegmentSeriesFailure("eui 64 check "+back.String(), back)) } else { macAddr := macStr.GetAddress() macBack, err := macAddr.ToEUI64IPv6() if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error "+err.Error(), macAddr)) } linkLocal, err := macAddr.ToLinkLocalIPv6() if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for link local "+err.Error(), macAddr)) return } if !linkLocal.IsLinkLocal() { t.addFailure(newSegmentSeriesFailure("eui 64 conv link local "+macAddr.String(), linkLocal)) } else { if !macBack.Equal(back) { t.addFailure(newSegmentSeriesFailure("eui 64 conv "+back.String(), macBack)) } else { macAddr64, err := macAddr.ToEUI64(false) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for mac address to EUI64 "+err.Error(), macAddr)) return } if macAddr.IsEUI64(true) || macAddr.IsEUI64(false) || !macAddr64.IsEUI64(false) { t.addFailure(newSegmentSeriesFailure("mac eui test "+macAddr64.String(), macAddr)) } else { backFromMac64Addr, err := ipaddr.NewIPv6AddressFromMAC(addr, macAddr64) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for mac address 64 to EUI64 "+err.Error(), macAddr)) return } backFromMac64 := backFromMac64Addr.GetHostSectionLen(64) if !backFromMac64.Equal(back) { t.addFailure(newSegmentSeriesFailure("eui 64 conv 2"+back.String(), backFromMac64)) } else { backFromMacAddr, err := ipaddr.NewIPv6AddressFromMAC(addr, macAddr) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for mac address to EUI64 "+err.Error(), macAddr)) return } backFromMac := backFromMacAddr.GetHostSectionLen(64) if !backFromMac.Equal(back) { t.addFailure(newSegmentSeriesFailure("eui 64 conv 3"+back.String(), backFromMac)) } else { withPrefix := false //we do the loop twice, once with prefixes, the other without origAddr := addr origBackFromMac := backFromMac for { addr = origAddr backFromMac = origBackFromMac frontIpv6 := addr.GetNetworkSectionLen(64) backLinkLocal := linkLocal.GetHostSectionLen(64) backIpv6 := addr.GetHostSectionLen(64) if withPrefix { addr = addr.SetPrefixLen(64) } else { frontIpv6 = frontIpv6.WithoutPrefixLen() backFromMac = backFromMac.WithoutPrefixLen() backLinkLocal = backLinkLocal.WithoutPrefixLen() backIpv6 = backIpv6.WithoutPrefixLen() } backIPv6_1, err := addr.ToEUI(true) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error 1 for address to EUI64 "+err.Error(), addr)) } backIPv6_2, err := addr.ToEUI(false) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error 2 for address to EUI64 "+err.Error(), addr)) } splitJoined1, err := ipaddr.NewIPv6AddressFromMACSection(frontIpv6, backIPv6_1.GetSection()) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for ipv6 construction "+err.Error(), macAddr)) } splitJoined2, err := ipaddr.NewIPv6AddressFromMACSection(frontIpv6, backIPv6_2.GetSection()) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for ipv6 construction "+err.Error(), macAddr)) } splitJoined3, err := ipaddr.NewIPv6Address(frontIpv6.Append(backIpv6)) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for ipv6 construction "+err.Error(), macAddr)) } bk, err := ipaddr.NewIPv6SectionFromMAC(backIPv6_1) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for ipv6 construction "+err.Error(), macAddr)) } splitJoined4, err := ipaddr.NewIPv6Address(frontIpv6.Append(bk)) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for ipv6 construction "+err.Error(), macAddr)) } bk, err = ipaddr.NewIPv6SectionFromMAC(backIPv6_2) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for ipv6 construction "+err.Error(), macAddr)) } splitJoined5, err := ipaddr.NewIPv6Address(frontIpv6.Append(bk)) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for ipv6 construction "+err.Error(), macAddr)) } both3 := backFromMac var all []*ipaddr.IPv6Address ipa, err := ipaddr.NewIPv6Address(addr.GetSection().Replace(4, both3)) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for ipv6 construction "+err.Error(), addr)) } all = append(all, ipa) ipa, err = ipaddr.NewIPv6Address(frontIpv6.Append(both3)) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for ipv6 construction "+err.Error(), addr)) } all = append(all, ipa) ipa, err = ipaddr.NewIPv6Address(frontIpv6.Append(both3)) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for ipv6 construction "+err.Error(), addr)) } all = append(all, ipa) all = append(all, splitJoined1) all = append(all, splitJoined2) all = append(all, splitJoined3) all = append(all, splitJoined4) all = append(all, splitJoined5) ipa, err = ipaddr.NewIPv6Address(addr.GetSection().Replace(4, backLinkLocal)) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for ipv6 construction "+err.Error(), addr)) } all = append(all, ipa) ipa, err = ipaddr.NewIPv6Address(frontIpv6.Append(backLinkLocal)) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error for ipv6 construction "+err.Error(), addr)) } all = append(all, ipa) //All of these should be equal! for i := range all { for j := range all { if !all[i].Equal(all[j]) { t.addFailure(newSegmentSeriesFailure("failure matching "+all[i].String()+" to "+all[j].String(), addr)) } if !all[i].GetNetworkPrefixLen().Equal(all[j].GetNetworkPrefixLen()) { t.addFailure(newSegmentSeriesFailure("failure matching "+all[i].GetNetworkPrefixLen().String()+" to "+all[j].GetNetworkPrefixLen().String(), addr)) } } } if withPrefix { break } withPrefix = true } } } } } } } t.incrementTestCount() } func (t macAddressTester) testStrings() { t.testMACStrings("a:b:c:d:e:f:a:b", "0a:0b:0c:0d:0e:0f:0a:0b", //normalizedString, //toColonDelimitedString "a:b:c:d:e:f:a:b", //compressedString, "0a-0b-0c-0d-0e-0f-0a-0b", //canonicalString, //toDashedString "0a0b.0c0d.0e0f.0a0b", //dottedString, "0a 0b 0c 0d 0e 0f 0a 0b", //spaceDelimitedString, "0a0b0c0d0e0f0a0b") //singleHex t.testMACStrings("ab:ab:bc:cd:De:ef", "ab:ab:bc:cd:de:ef", //normalizedString, //toColonDelimitedString "ab:ab:bc:cd:de:ef", //compressedString, "ab-ab-bc-cd-de-ef", //canonicalString, //toDashedString "abab.bccd.deef", //dottedString, "ab ab bc cd de ef", //spaceDelimitedString, "ababbccddeef") //singleHex t.testMACStrings("ab:AB:bc:cd:de:ef:aB:aB", "ab:ab:bc:cd:de:ef:ab:ab", //normalizedString, //toColonDelimitedString "ab:ab:bc:cd:de:ef:ab:ab", //compressedString, "ab-ab-bc-cd-de-ef-ab-ab", //canonicalString, //toDashedString "abab.bccd.deef.abab", //dottedString, "ab ab bc cd de ef ab ab", //spaceDelimitedString, "ababbccddeefabab") //singleHex t.testMACStrings("a:b:c:d:0:0", "0a:0b:0c:0d:00:00", //normalizedString, //toColonDelimitedString "a:b:c:d:0:0", //compressedString, "0a-0b-0c-0d-00-00", //canonicalString, //toDashedString "0a0b.0c0d.0000", //dottedString, "0a 0b 0c 0d 00 00", //spaceDelimitedString, "0a0b0c0d0000") //singleHex t.testMACStrings("ff:00:10:01:10:11", "ff:00:10:01:10:11", //normalizedString, //toColonDelimitedString "ff:0:10:1:10:11", //compressedString, "ff-00-10-01-10-11", //canonicalString, //toDashedString "ff00.1001.1011", //dottedString, "ff 00 10 01 10 11", //spaceDelimitedString, "ff0010011011") //singleHex t.testMACStrings("0aa0bbb00cff", "0a:a0:bb:b0:0c:ff", "a:a0:bb:b0:c:ff", "0a-a0-bb-b0-0c-ff", "0aa0.bbb0.0cff", "0a a0 bb b0 0c ff", "0aa0bbb00cff") t.testMACStrings("0aa0bb-b00cff", "0a:a0:bb:b0:0c:ff", "a:a0:bb:b0:c:ff", "0a-a0-bb-b0-0c-ff", "0aa0.bbb0.0cff", "0a a0 bb b0 0c ff", "0aa0bbb00cff") } func all3Equals(one, two, three ipaddr.PrefixLen) bool { return one.Equal(two) && one.Equal(three) } ipaddress-go-1.5.4/ipaddr/test/specialtypestest.go000066400000000000000000002211161440250641600222650ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package test import ( "math/big" "net" "strconv" "github.com/seancfoley/ipaddress-go/ipaddr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) type specialTypesTester struct { testBase } var ( hostOptionsSpecial = new(addrstrparam.HostNameParamsBuilder).AllowEmpty(true).GetIPAddressParamsBuilder().AllowEmpty(true).ParseEmptyStrAs(addrstrparam.LoopbackOption).SetRangeParams(addrstrparam.WildcardOnly).AllowAll(true).GetParentBuilder().ToParams() addressOptionsSpecial = new(addrstrparam.IPAddressStringParamsBuilder).Set(hostOptionsSpecial.GetIPAddressParams()).AllowEmpty(true).ParseEmptyStrAs(addrstrparam.LoopbackOption).ToParams() macOptionsSpecial = new(addrstrparam.MACAddressStringParamsBuilder).Set(macAddressOptions).AllowEmpty(true).SetRangeParams(addrstrparam.WildcardOnly).AllowAll(true).ToParams() emptyAddressOptions = new(addrstrparam.HostNameParamsBuilder).Set(hostOptions).GetIPAddressParamsBuilder().AllowEmpty(true).ParseEmptyStrAs(addrstrparam.LoopbackOption).GetParentBuilder().ToParams() emptyAddressNoLoopbackOptions = new(addrstrparam.HostNameParamsBuilder).Set(emptyAddressOptions).GetIPAddressParamsBuilder().ParseEmptyStrAs(addrstrparam.NoAddressOption).GetParentBuilder().ToParams() ) func (t specialTypesTester) run() { allSingleHex := "0x00000000-0xffffffff" allSingleOctal := "000000000000-037777777777" t.testIPv4Strings("*", true, "*.*.*.*", "*.*.*.*", "%.%.%.%", "000-255.000-255.000-255.000-255", "*.*.*.*.in-addr.arpa", allSingleHex, allSingleOctal) t.testIPv4Strings("***.***.***.***", true, "*.*.*.*", "*.*.*.*", "%.%.%.%", "000-255.000-255.000-255.000-255", "*.*.*.*.in-addr.arpa", allSingleHex, allSingleOctal) t.testIPv4Strings("*.*", false, "*.*.*.*", "*.*.*.*", "%.%.%.%", "000-255.000-255.000-255.000-255", "*.*.*.*.in-addr.arpa", allSingleHex, allSingleOctal) t.testIPv4Strings("*.*/16", false, "*.*.0.0/16", "*.*.*.*", "%.%.%.%", "000-255.000-255.000.000/16", "*.*.*.*.in-addr.arpa", allSingleHex, allSingleOctal) t.testIPv4Strings("*.*/16", true, "*.*.0.0/16", "*.*.*.*", "%.%.%.%", "000-255.000-255.000.000/16", "*.*.*.*.in-addr.arpa", allSingleHex, allSingleOctal) t.testIPv4Strings("*/16", true, "*.*.0.0/16", "*.*.*.*", "%.%.%.%", "000-255.000-255.000.000/16", "*.*.*.*.in-addr.arpa", allSingleHex, allSingleOctal) t.testIPv4Strings("*/255.255.0.0", false, "*.*.0.0/16", "*.*.*.*", "%.%.%.%", "000-255.000-255.000.000/16", "*.*.*.*.in-addr.arpa", allSingleHex, allSingleOctal) t.testIPv4Strings("*/255.255.0.0", true, "*.*.0.0/16", "*.*.*.*", "%.%.%.%", "000-255.000-255.000.000/16", "*.*.*.*.in-addr.arpa", allSingleHex, allSingleOctal) t.testIPv4Strings("", false, "127.0.0.1", "127.0.0.1", "127.0.0.1", "127.000.000.001", "1.0.0.127.in-addr.arpa", "0x7f000001", "017700000001") t.testIPv4Strings("", true, "127.0.0.1", "127.0.0.1", "127.0.0.1", "127.000.000.001", "1.0.0.127.in-addr.arpa", "0x7f000001", "017700000001") base85All := "00000000000000000000" + ipaddr.ExtendedDigitsRangeSeparatorStr + "=r54lj&NUUO~Hi%c2ym0" //base85AllPrefixed := base85All + "/16" //base85AllPrefixed64 := base85All + "/64" base8516 := "00000000000000000000" + ipaddr.ExtendedDigitsRangeSeparatorStr + "=q{+M|w0(OeO5^EGP660" + "/16" base8564 := "00000000000000000000" + ipaddr.ExtendedDigitsRangeSeparatorStr + "=r54lj&NUTUTif>jH#O0" + "/64" allSingleHexIPv6 := "0x00000000000000000000000000000000-0xffffffffffffffffffffffffffffffff" allSingleOctalIPv6 := "00000000000000000000000000000000000000000000-03777777777777777777777777777777777777777777" t.testIPv6Strings("*", true, "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "%:%:%:%:%:%:%:%", "0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*.*.*.*", "*:*:*:*:*:*:*.*.*.*", "*:*:*:*:*:*:*.*.*.*", "*:*:*:*:*:*:*.*.*.*", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.ip6.arpa", "*-*-*-*-*-*-*-*.ipv6-literal.net", base85All, allSingleHexIPv6, allSingleOctalIPv6) t.testIPv6Strings("*:*", false, "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "%:%:%:%:%:%:%:%", "0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*.*.*.*", "*:*:*:*:*:*:*.*.*.*", "*:*:*:*:*:*:*.*.*.*", "*:*:*:*:*:*:*.*.*.*", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.ip6.arpa", "*-*-*-*-*-*-*-*.ipv6-literal.net", base85All, allSingleHexIPv6, allSingleOctalIPv6) t.testIPv6Strings("*:*", true, "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "%:%:%:%:%:%:%:%", "0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000-ffff", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*.*.*.*", "*:*:*:*:*:*:*.*.*.*", "*:*:*:*:*:*:*.*.*.*", "*:*:*:*:*:*:*.*.*.*", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.ip6.arpa", "*-*-*-*-*-*-*-*.ipv6-literal.net", base85All, allSingleHexIPv6, allSingleOctalIPv6) t.testIPv6Strings("*/16", true, "*:0:0:0:0:0:0:0/16", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "%:%:%:%:%:%:%:%", "0000-ffff:0000:0000:0000:0000:0000:0000:0000/16", "*::/16", "*::/16", "*::/16", "*:*:*:*:*:*:*:*", "*::0.0.0.0/16", "*::0.0.0.0/16", "*::/16", "*::/16", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.ip6.arpa", "*-0-0-0-0-0-0-0.ipv6-literal.net/16", base8516, allSingleHexIPv6, allSingleOctalIPv6) t.testIPv6Strings("*:*/16", false, "*:0:0:0:0:0:0:0/16", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "%:%:%:%:%:%:%:%", "0000-ffff:0000:0000:0000:0000:0000:0000:0000/16", "*::/16", "*::/16", "*::/16", "*:*:*:*:*:*:*:*", "*::0.0.0.0/16", "*::0.0.0.0/16", "*::/16", "*::/16", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.ip6.arpa", "*-0-0-0-0-0-0-0.ipv6-literal.net/16", base8516, allSingleHexIPv6, allSingleOctalIPv6) t.testIPv6Strings("*:*/16", true, "*:0:0:0:0:0:0:0/16", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "%:%:%:%:%:%:%:%", "0000-ffff:0000:0000:0000:0000:0000:0000:0000/16", "*::/16", "*::/16", "*::/16", "*:*:*:*:*:*:*:*", "*::0.0.0.0/16", "*::0.0.0.0/16", "*::/16", "*::/16", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.ip6.arpa", "*-0-0-0-0-0-0-0.ipv6-literal.net/16", base8516, allSingleHexIPv6, allSingleOctalIPv6) t.testIPv6Strings("*/64", false, "*:*:*:*:0:0:0:0/64", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "%:%:%:%:%:%:%:%", "0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000:0000:0000:0000/64", "*:*:*:*::/64", "*:*:*:*::/64", "*:*:*:*::/64", "*:*:*:*:*:*:*:*", "*:*:*:*::0.0.0.0/64", "*:*:*:*::0.0.0.0/64", "*:*:*:*::/64", "*:*:*:*::/64", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.ip6.arpa", "*-*-*-*-0-0-0-0.ipv6-literal.net/64", base8564, allSingleHexIPv6, allSingleOctalIPv6) t.testIPv6Strings("*/64", true, "*:*:*:*:0:0:0:0/64", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "%:%:%:%:%:%:%:%", "0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000:0000:0000:0000/64", "*:*:*:*::/64", "*:*:*:*::/64", "*:*:*:*::/64", "*:*:*:*:*:*:*:*", "*:*:*:*::0.0.0.0/64", "*:*:*:*::0.0.0.0/64", "*:*:*:*::/64", "*:*:*:*::/64", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.ip6.arpa", "*-*-*-*-0-0-0-0.ipv6-literal.net/64", base8564, allSingleHexIPv6, allSingleOctalIPv6) t.testIPv6Strings("*:*/64", false, "*:*:*:*:0:0:0:0/64", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "%:%:%:%:%:%:%:%", "0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000:0000:0000:0000/64", "*:*:*:*::/64", "*:*:*:*::/64", "*:*:*:*::/64", "*:*:*:*:*:*:*:*", "*:*:*:*::0.0.0.0/64", "*:*:*:*::0.0.0.0/64", "*:*:*:*::/64", "*:*:*:*::/64", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.ip6.arpa", "*-*-*-*-0-0-0-0.ipv6-literal.net/64", base8564, allSingleHexIPv6, allSingleOctalIPv6) t.testIPv6Strings("*:*/64", true, "*:*:*:*:0:0:0:0/64", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "%:%:%:%:%:%:%:%", "0000-ffff:0000-ffff:0000-ffff:0000-ffff:0000:0000:0000:0000/64", "*:*:*:*::/64", "*:*:*:*::/64", "*:*:*:*::/64", "*:*:*:*:*:*:*:*", "*:*:*:*::0.0.0.0/64", "*:*:*:*::0.0.0.0/64", "*:*:*:*::/64", "*:*:*:*::/64", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.ip6.arpa", "*-*-*-*-0-0-0-0.ipv6-literal.net/64", base8564, allSingleHexIPv6, allSingleOctalIPv6) t.testIPv6Strings("*/ffff::", false, "*:0:0:0:0:0:0:0/16", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "%:%:%:%:%:%:%:%", "0000-ffff:0000:0000:0000:0000:0000:0000:0000/16", "*::/16", "*::/16", "*::/16", "*:*:*:*:*:*:*:*", "*::0.0.0.0/16", "*::0.0.0.0/16", "*::/16", "*::/16", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.ip6.arpa", "*-0-0-0-0-0-0-0.ipv6-literal.net/16", base8516, allSingleHexIPv6, allSingleOctalIPv6) t.testIPv6Strings("*/ffff::", true, "*:0:0:0:0:0:0:0/16", "*:*:*:*:*:*:*:*", "*:*:*:*:*:*:*:*", "%:%:%:%:%:%:%:%", "0000-ffff:0000:0000:0000:0000:0000:0000:0000/16", "*::/16", "*::/16", "*::/16", "*:*:*:*:*:*:*:*", "*::0.0.0.0/16", "*::0.0.0.0/16", "*::/16", "*::/16", "*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.ip6.arpa", "*-0-0-0-0-0-0-0.ipv6-literal.net/16", base8516, allSingleHexIPv6, allSingleOctalIPv6) t.testIPv6Strings("", true, "0:0:0:0:0:0:0:1", "0:0:0:0:0:0:0:1", "::1", "0:0:0:0:0:0:0:1", "0000:0000:0000:0000:0000:0000:0000:0001", "::1", "::1", "::1", "::1", "::0.0.0.1", "::0.0.0.1", "::0.0.0.1", "::0.0.0.1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa", "0-0-0-0-0-0-0-1.ipv6-literal.net", "00000000000000000001", "0x00000000000000000000000000000001", "00000000000000000000000000000000000000000001") nilStr := `` t.testBase.testIPv6Strings(nil, nil, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr, nilStr) t.testInvalidValues() t.testValidity() t.testEmptyValues() t.testAllValues() t.testAllValuesVersioned(ipaddr.IPv4, getCount(255, 4)) t.testAllValuesVersioned(ipaddr.IPv6, getCount(0xffff, 8)) t.testAllMACValues(getCount(0xff, 6), getCount(0xff, 8)) addressEmpty := t.createParamsHost("", emptyAddressOptions) t.hostLabelsHostTest(addressEmpty, []string{"127", "0", "0", "1"}) addressEmpty2 := t.createParamsHost("", emptyAddressNoLoopbackOptions) t.hostLabelsHostTest(addressEmpty2, []string{}) hostEmpty := t.createParamsHost("", hostOptionsSpecial) t.hostLabelsHostTest(hostEmpty, []string{"127", "0", "0", "1"}) t.testEmptyIsSelf() t.testSelf("localhost", true) t.testSelf("127.0.0.1", true) t.testSelf("::1", true) t.testSelf("[::1]", true) t.testSelf("*", false) t.testSelf("sean.com", false) t.testSelf("1.2.3.4", false) t.testSelf("::", false) t.testSelf("[::]", false) t.testSelf("[1:2:3:4:1:2:3:4]", false) t.testSelf("1:2:3:4:1:2:3:4", false) t.testEmptyLoopback() t.testLoopback("127.0.0.1", true) t.testLoopback("::1", true) t.testLoopback("*", false) t.testLoopback("1.2.3.4", false) t.testLoopback("::", false) t.testLoopback("1:2:3:4:1:2:3:4", false) t.testNils() t.testZeros() } func (t specialTypesTester) testIPv4Strings(addr string, explicit bool, normalizedString, normalizedWildcardString, sqlString, fullString, reverseDNSString, singleHex, singleOctal string) { w := t.createParamsAddress(addr, addressOptionsSpecial) var ipAddr *ipaddr.IPAddress if explicit { ipAddr = w.GetVersionedAddress(ipaddr.IPv4) } else { ipAddr = w.GetAddress() } t.testStrings(w, ipAddr, normalizedString, normalizedWildcardString, normalizedWildcardString, sqlString, fullString, normalizedString, normalizedString, normalizedWildcardString, normalizedString, normalizedWildcardString, reverseDNSString, normalizedString, singleHex, singleOctal) } func (t specialTypesTester) testIPv6Strings(addr string, explicit bool, normalizedString, normalizedWildcardString, canonicalWildcardString, sqlString, fullString, compressedString, canonicalString, subnetString, compressedWildcardString, mixedStringNoCompressMixed, mixedStringNoCompressHost, mixedStringCompressCoveredHost, mixedString, reverseDNSString, uncHostString, base85String, singleHex, singleOctal string) { w := t.createParamsAddress(addr, addressOptionsSpecial) var ipAddr *ipaddr.IPAddress if explicit { ipAddr = w.GetVersionedAddress(ipaddr.IPv6) } else { ipAddr = w.GetAddress() } t.testBase.testIPv6Strings(w, ipAddr, normalizedString, normalizedWildcardString, canonicalWildcardString, sqlString, fullString, compressedString, canonicalString, subnetString, compressedWildcardString, mixedStringNoCompressMixed, mixedStringNoCompressHost, mixedStringCompressCoveredHost, mixedString, reverseDNSString, uncHostString, base85String, singleHex, singleOctal) } func (t specialTypesTester) testEmptyValues() { t.testEmptyValuesOpts(hostOptionsSpecial, addressOptionsSpecial) zeroHostOptions := new(addrstrparam.HostNameParamsBuilder).GetIPAddressParamsBuilder().ParseEmptyStrAs(addrstrparam.LoopbackOption).GetParentBuilder().ToParams() zeroAddrOptions := new(addrstrparam.IPAddressStringParamsBuilder).ParseEmptyStrAs(addrstrparam.LoopbackOption).ToParams() t.testEmptyValuesOpts(zeroHostOptions, zeroAddrOptions) zeroHostOptions = new(addrstrparam.HostNameParamsBuilder).GetIPAddressParamsBuilder().ParseEmptyStrAs(addrstrparam.ZeroAddressOption).GetParentBuilder().ToParams() zeroAddrOptions = new(addrstrparam.IPAddressStringParamsBuilder).ParseEmptyStrAs(addrstrparam.ZeroAddressOption).ToParams() t.testEmptyValuesOpts(zeroHostOptions, zeroAddrOptions) zeroHostOptions = new(addrstrparam.HostNameParamsBuilder).GetIPAddressParamsBuilder().ParseEmptyStrAs(addrstrparam.NoAddressOption).GetParentBuilder().ToParams() zeroAddrOptions = new(addrstrparam.IPAddressStringParamsBuilder).ParseEmptyStrAs(addrstrparam.NoAddressOption).ToParams() t.testEmptyValuesOpts(zeroHostOptions, zeroAddrOptions) } func (t specialTypesTester) testEmptyValuesOpts(hp addrstrparam.HostNameParams, sp addrstrparam.IPAddressStringParams) { hostEmpty := t.createParamsHost("", hp) addressEmpty := t.createParamsAddress("", sp) // preferredVersion := new(ipaddr.IPAddressStringParamsBuilder).ToParams().GetPreferredVersion() preferredAddressVersion := ipaddr.IPVersion(sp.GetPreferredVersion()) preferredHostVersion := ipaddr.IPVersion(hp.GetPreferredVersion()) //var addr, addr2 net.IP var addr net.IP if preferredAddressVersion.IsIPv6() { if sp.EmptyStrParsedAs() == addrstrparam.LoopbackOption { addr = net.IPv6loopback } else if sp.EmptyStrParsedAs() == addrstrparam.ZeroAddressOption { addr = net.IPv6zero } } else { if sp.EmptyStrParsedAs() == addrstrparam.LoopbackOption { addr = net.IPv4(127, 0, 0, 1) } else if sp.EmptyStrParsedAs() == addrstrparam.ZeroAddressOption { addr = net.IPv4(0, 0, 0, 0) } } if preferredAddressVersion != preferredHostVersion { t.addFailure(newFailure("failure: precondition to test is that options have same preferred version", addressEmpty)) } if addr == nil { // empty string not parsed as an address if addressEmpty.GetAddress() != nil { t.addFailure(newFailure("no match "+addressEmpty.GetAddress().String(), addressEmpty)) } addr, err := addressEmpty.ToAddress() if addr != nil { t.addFailure(newFailure("no match "+addr.String(), addressEmpty)) } if err != nil { t.addFailure(newFailure("no match "+err.Error(), addressEmpty)) } if hostEmpty.AsAddress() != nil { t.addFailure(newHostFailure("host "+hostEmpty.String()+" treated as address "+hostEmpty.AsAddress().String(), hostEmpty)) //t.addFailure(newHostFailure("no match "+hostEmpty.AsAddress().String(), hostEmpty)) } return } address, _ := ipaddr.NewIPAddressFromNetIP(addr) if !addressEmpty.GetAddress().Equal(address) { t.addFailure(newFailure("no match "+addr.String(), addressEmpty)) } else if addressEmpty.GetAddress().Compare(address) != 0 { t.addFailure(newFailure("no match "+addr.String(), addressEmpty)) } else if addressEmpty.GetAddress().GetCount().Cmp(bigOneConst()) != 0 { t.addFailure(newFailure("no count match "+addr.String(), addressEmpty)) } else { addressEmpty = hostEmpty.AsAddressString() //note that hostEmpty allows empty strings and they resolve to loopbacks, but they are not treated as addresses if addressEmpty == nil { t.addFailure(newFailure("host "+hostEmpty.String()+" treated as address "+addressEmpty.String(), addressEmpty)) } else if !addressEmpty.GetAddress().Equal(address) { t.addFailure(newFailure("no match "+addressEmpty.GetAddress().String()+" with "+address.String(), addressEmpty)) } else if addressEmpty.GetAddress().Compare(address) != 0 { t.addFailure(newFailure("no match "+addr.String(), addressEmpty)) } else if addressEmpty.GetAddress().GetCount().Cmp(bigOneConst()) != 0 { t.addFailure(newFailure("no count match "+addr.String(), addressEmpty)) } else { addressEmptyValue := hostEmpty.GetAddress() if !addressEmptyValue.Equal(address) { t.addFailure(newFailure("no match "+addr.String(), addressEmpty)) } else if addressEmptyValue.Compare(address) != 0 { t.addFailure(newFailure("no match "+addr.String(), addressEmpty)) } else if addressEmptyValue.GetCount().Cmp(bigOneConst()) != 0 { t.addFailure(newFailure("no count match "+addr.String(), addressEmpty)) } } } t.incrementTestCount() } func (t specialTypesTester) testInvalidValues() { // invalid mask addressAll := t.createParamsAddress("*/f0ff::", addressOptionsSpecial) _, err := addressAll.ToAddress() if err == nil { t.addFailure(newFailure("unexpectedly valid", addressAll)) } else { // valid mask addressAll = t.createParamsAddress("*/fff0::", addressOptionsSpecial) //try { if addressAll.GetAddress() == nil { t.addFailure(newFailure("unexpectedly invalid", addressAll)) } else { //ambiguous addressAll = t.createParamsAddress("*", addressOptionsSpecial) if addressAll.GetAddress() != nil { t.addFailure(newFailure("unexpectedly invalid", addressAll)) } else { //ambiguous addressAll = t.createParamsAddress("*/16", addressOptionsSpecial) if addressAll.GetAddress() != nil { t.addFailure(newFailure("unexpectedly invalid", addressAll)) } //unambiguous similar addresses tested with testStrings() } } } } func (t specialTypesTester) testValidity() { hostEmpty := t.createHost("") hostAll := t.createHost("*") hostAllIPv4 := t.createHost("*.*.*.*") hostAllIPv6 := t.createHost("*:*:*:*:*:*:*:*") addressEmpty := t.createAddress("") addressAll := t.createAddress("*") macEmpty := t.createMACAddress("") macAll := t.createMACAddress("*") if hostEmpty.IsValid() { t.addFailure(newHostFailure("unexpectedly valid", hostEmpty)) } else if hostAll.IsValid() { t.addFailure(newHostFailure("unexpectedly valid", hostAll)) } else if hostAllIPv4.IsValid() { t.addFailure(newHostFailure("unexpectedly valid", hostAllIPv4)) } else if hostAllIPv6.IsValid() { t.addFailure(newHostFailure("unexpectedly valid", hostAllIPv6)) } else if addressEmpty.IsValid() { t.addFailure(newFailure("unexpectedly valid", addressEmpty)) } else if addressAll.IsValid() { t.addFailure(newFailure("unexpectedly valid", addressAll)) } else if macEmpty.IsValid() { t.addFailure(newMACFailure("unexpectedly valid", macEmpty)) } else if macAll.IsValid() { t.addFailure(newMACFailure("unexpectedly valid", macAll)) } else if hostAll.GetAddress() != nil { t.addFailure(newHostFailure("unexpectedly valid", hostAll)) } else if hostEmpty.GetAddress() != nil { t.addFailure(newHostFailure("unexpectedly valid", hostEmpty)) } else { hostEmpty = t.createParamsHost("", hostOptionsSpecial) hostAll = t.createParamsHost("*", hostOptionsSpecial) hostAllIPv4 = t.createParamsHost("*.*.*.*", hostOptionsSpecial) hostAllIPv6 = t.createParamsHost("*:*:*:*:*:*:*:*", hostOptionsSpecial) addressEmpty = t.createParamsAddress("", addressOptionsSpecial) addressAll = t.createParamsAddress("*", addressOptionsSpecial) macEmpty = t.createMACParamsAddress("", macOptionsSpecial) macAll = t.createMACParamsAddress("*", macOptionsSpecial) if !hostEmpty.IsValid() { t.addFailure(newHostFailure("unexpectedly invalid", hostEmpty)) } else if !hostAll.IsValid() { t.addFailure(newHostFailure("unexpectedly invalid", hostAll)) } else if !hostAllIPv4.IsValid() { t.addFailure(newHostFailure("unexpectedly invalid", hostAllIPv4)) } else if !hostAllIPv6.IsValid() { t.addFailure(newHostFailure("unexpectedly invalid", hostAllIPv6)) } else if !addressEmpty.IsValid() { t.addFailure(newFailure("unexpectedly invalid", addressEmpty)) } else if !addressAll.IsValid() { t.addFailure(newFailure("unexpectedly invalid", addressAll)) } else if !macEmpty.IsValid() { t.addFailure(newMACFailure("unexpectedly invalid", macEmpty)) } else if !macAll.IsValid() { t.addFailure(newMACFailure("unexpectedly invalid", macAll)) } else if hostEmpty.GetAddress() == nil { //loopback t.addFailure(newHostFailure("unexpectedly invalid", hostEmpty)) } else if hostAll.GetAddress() != nil { t.addFailure(newHostFailure("unexpectedly invalid", hostAll)) } else { //With empty strings, if we wish to allow them, there are two options, //we can either treat them as host names and we defer to the validation options for host names, as done above, //or we treat than as addresses and use the address options to control behaviour, as we do here. hostEmpty = t.createParamsHost("", emptyAddressOptions) if !hostEmpty.IsValid() { t.addFailure(newHostFailure("unexpectedly invalid", hostEmpty)) } else if hostEmpty.GetAddress() == nil { //loopback t.addFailure(newHostFailure("unexpectedly invalid", hostEmpty)) } else { addressAll = t.createParamsAddress("*.*/64", addressOptionsSpecial) // invalid prefix if addressAll.IsValid() { t.addFailure(newFailure("unexpectedly valid: "+addressAll.String(), addressAll)) } } } } t.incrementTestCount() } func (t specialTypesTester) testAllMACValues(count1, count2 *big.Int) { macAll := t.createMACParamsAddress("*", macOptionsSpecial).GetAddress() macAll2 := t.createMACParamsAddress("*:*:*:*:*:*:*", macOptionsSpecial).GetAddress() address1Str := "*:*:*:*:*:*" address2Str := "*:*:*:*:*:*:*:*" mac1 := t.createMACParamsAddress(address1Str, macOptionsSpecial).GetAddress() mac2 := t.createMACParamsAddress(address2Str, macOptionsSpecial).GetAddress() if !macAll.Equal(mac1) { t.addFailure(newSegmentSeriesFailure("no match "+macAll.String(), mac1)) } else if !macAll2.Equal(mac2) { t.addFailure(newSegmentSeriesFailure("no match "+macAll2.String(), mac2)) } else if macAll.Compare(mac1) != 0 { t.addFailure(newSegmentSeriesFailure("no match "+macAll.String(), mac1)) } else if macAll2.Compare(mac2) != 0 { t.addFailure(newSegmentSeriesFailure("no match "+macAll2.String(), mac2)) } else if macAll.GetCount().Cmp(count1) != 0 { t.addFailure(newSegmentSeriesFailure("no count match ", macAll)) } else if macAll2.GetCount().Cmp(count2) != 0 { t.addFailure(newSegmentSeriesFailure("no count match ", macAll2)) } t.incrementTestCount() } func (t specialTypesTester) testAllValuesVersioned(version ipaddr.IPVersion, count *big.Int) { hostAll := t.createParamsHost("*", hostOptionsSpecial) addressAllStr := t.createParamsAddress("*", addressOptionsSpecial) addressAll := addressAllStr.GetVersionedAddress(version) var address2Str = "*.*.*.*" if !version.IsIPv4() { address2Str = "*:*:*:*:*:*:*:*" } address := t.createParamsAddress(address2Str, addressOptionsSpecial).GetAddress() if !addressAll.Equal(address) { t.addFailure(newIPAddrFailure("no match "+address.String(), addressAll)) } else if addressAll.Compare(address) != 0 { t.addFailure(newIPAddrFailure("no match "+address.String(), addressAll)) } else if addressAll.GetCount().Cmp(count) != 0 { t.addFailure(newIPAddrFailure("no count match ", addressAll)) } else { str := hostAll.AsAddressString() addressAll = str.GetVersionedAddress(version) if !addressAll.Equal(address) { t.addFailure(newIPAddrFailure("no match "+address.String(), addressAll)) } else if addressAll.Compare(address) != 0 { t.addFailure(newIPAddrFailure("no match "+address.String(), addressAll)) } else if addressAll.GetCount().Cmp(count) != 0 { t.addFailure(newIPAddrFailure("no count match ", addressAll)) } } t.incrementTestCount() } func (t specialTypesTester) testAllValues() { hostAll := t.createParamsHost("*", hostOptionsSpecial) addressAll := t.createParamsAddress("*", addressOptionsSpecial) macAll := t.createMACParamsAddress("*", macOptionsSpecial) if addressAll.GetAddress() != nil { t.addFailure(newFailure("non nil", addressAll)) } else if hostAll.AsAddress() != nil { t.addFailure(newHostFailure("non nil", hostAll)) } else if hostAll.GetAddress() != nil { t.addFailure(newHostFailure("non nil", hostAll)) } else if macAll.GetAddress() == nil { t.addFailure(newMACFailure("nil", macAll)) } t.incrementTestCount() } func (t specialTypesTester) testEmptyIsSelf() { w := t.createParamsHost("", hostOptionsSpecial) if !w.IsSelf() { t.addFailure(newHostFailure("failed: isSelf is "+strconv.FormatBool(w.IsSelf()), w)) } w2 := t.createParamsHost("", emptyAddressOptions) if !w2.IsSelf() { t.addFailure(newHostFailure("failed: isSelf is "+strconv.FormatBool(w2.IsSelf()), w2)) } t.incrementTestCount() } func (t specialTypesTester) testSelf(host string, isSelf bool) { w := t.createParamsHost(host, hostOptionsSpecial) if isSelf != w.IsSelf() { t.addFailure(newHostFailure("failed: isSelf is "+strconv.FormatBool(isSelf), w)) } t.incrementTestCount() } func (t specialTypesTester) testEmptyLoopback() { w := t.createParamsHost("", hostOptionsSpecial) if !w.IsLoopback() { t.addFailure(newHostFailure("failed: isSelf is "+strconv.FormatBool(w.IsSelf()), w)) } addressEmptyValue := w.GetAddress() if !addressEmptyValue.IsLoopback() { t.addFailure(newIPAddrFailure("failed: isSelf is "+strconv.FormatBool(addressEmptyValue.IsLoopback()), addressEmptyValue)) } w2 := t.createParamsHost("", emptyAddressOptions) if !w2.IsLoopback() { t.addFailure(newHostFailure("failed: isSelf is "+strconv.FormatBool(w2.IsSelf()), w2)) } t.incrementTestCount() } func (t specialTypesTester) testLoopback(host string, isSelf bool) { w := t.createParamsHost(host, hostOptionsSpecial) if isSelf != w.IsLoopback() { t.addFailure(newHostFailure("failed: isSelf is "+strconv.FormatBool(isSelf), w)) } w2 := t.createParamsAddress(host, addressOptionsSpecial) if isSelf != w2.IsLoopback() { t.addFailure(newHostFailure("failed: isSelf is "+strconv.FormatBool(isSelf), w)) } t.incrementTestCount() } func (t specialTypesTester) testZeros() { addrZero := ipaddr.Address{} ipZero := ipaddr.IPAddress{} macZero := ipaddr.MACAddress{} ipv4Zero := ipaddr.IPv4Address{} ipv6Zero := ipaddr.IPv6Address{} if addrZero.ToIP() == nil || addrZero.ToIPv4() != nil || addrZero.ToIPv6() != nil || addrZero.ToMAC() != nil { t.addFailure(newAddrFailure("zero of "+addrZero.String(), &addrZero)) } else if !ipZero.ToAddressBase().Equal(&ipZero) || !ipZero.ToIP().Equal(&ipZero) || ipZero.ToIPv4() != nil || ipZero.ToIPv6() != nil { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), &ipZero)) } else if !macZero.ToAddressBase().Equal(&macZero) { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), &ipv4Zero)) } else if !ipv4Zero.ToAddressBase().Equal(&ipv4Zero) || !ipv4Zero.ToIP().Equal(&ipv4Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), &ipv4Zero)) } else if !ipv6Zero.ToAddressBase().Equal(&ipv6Zero) || !ipv6Zero.ToIP().Equal(&ipv6Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), &ipv6Zero)) } addrZeroKey := ipaddr.Key[*ipaddr.Address]{} ipZeroKey := ipaddr.Key[*ipaddr.IPAddress]{} ipv4ZeroKey := ipaddr.IPv4AddressKey{} ipv6ZeroKey := ipaddr.IPv6AddressKey{} macZeroKey := ipaddr.MACAddressKey{} addrZero2 := addrZeroKey.ToAddress() ipZero2 := ipZeroKey.ToAddress() ipv4Zero2 := ipv4ZeroKey.ToAddress() ipv6Zero2 := ipv6ZeroKey.ToAddress() macZero2 := macZeroKey.ToAddress() //fmt.Println(addrZeroKey, ipZeroKey, ipv4ZeroKey, ipv6ZeroKey, macZeroKey) // check that zero values from addresses coming from zero value keys match zero values from addresses if !addrZero.Equal(addrZero2) || !addrZero2.Equal(&addrZero) || !addrZero2.Equal(addrZero2) || !addrZero.Equal(&addrZero) { t.addFailure(newAddrFailure("zero of "+addrZero.String(), addrZero2)) } else if addrZero.Compare(addrZero2) != 0 || addrZero2.Compare(&addrZero) != 0 || addrZero.Compare(&addrZero) != 0 || addrZero2.Compare(addrZero2) != 0 { t.addFailure(newAddrFailure("zero of "+addrZero.String(), addrZero2)) } else if !ipZero.Equal(ipZero2) || !ipZero2.Equal(&ipZero) || !ipZero2.Equal(ipZero2) || !ipZero.Equal(&ipZero) { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero2)) } else if ipZero.Compare(ipZero2) != 0 || ipZero2.Compare(&ipZero) != 0 || ipZero.Compare(&ipZero) != 0 || ipZero2.Compare(ipZero2) != 0 { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero2)) } else if !ipv4Zero.Equal(ipv4Zero2) || !ipv4Zero2.Equal(&ipv4Zero) || !ipv4Zero2.Equal(ipv4Zero2) || !ipv4Zero.Equal(&ipv4Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero2)) } else if ipv4Zero.Compare(ipv4Zero2) != 0 || ipv4Zero2.Compare(&ipv4Zero) != 0 || ipv4Zero.Compare(&ipv4Zero) != 0 || ipv4Zero2.Compare(ipv4Zero2) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero2)) } else if !ipv6Zero.Equal(ipv6Zero2) || !ipv6Zero2.Equal(&ipv6Zero) || !ipv6Zero2.Equal(ipv6Zero2) || !ipv6Zero.Equal(&ipv6Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero2)) } else if ipv6Zero.Compare(ipv6Zero2) != 0 || ipv6Zero2.Compare(&ipv6Zero) != 0 || ipv6Zero.Compare(&ipv6Zero) != 0 || ipv6Zero2.Compare(ipv6Zero2) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero2)) } else if !macZero.Equal(macZero2) || !macZero2.Equal(&macZero) || !macZero2.Equal(macZero2) || !macZero.Equal(&macZero) { t.addFailure(newAddressItemFailure("zero of "+macZero.String(), macZero2)) } else if macZero.Compare(macZero2) != 0 || macZero2.Compare(&macZero) != 0 || macZero.Compare(&macZero) != 0 || macZero2.Compare(macZero2) != 0 { t.addFailure(newAddressItemFailure("zero of "+macZero.String(), macZero2)) } // check that the various addresses from zero value keys are all different if !addrZero2.Equal(ipZero2) || addrZero2.Equal(ipv4Zero2) || addrZero2.Equal(ipv6Zero2) || addrZero2.Equal(macZero2) { t.addFailure(newAddrFailure("zero of "+addrZero2.String(), addrZero2)) } else if addrZero2.Compare(ipZero2) != 0 || addrZero2.Compare(ipv4Zero2) == 0 || addrZero2.Compare(ipv6Zero2) == 0 || addrZero2.Compare(macZero2) == 0 { t.addFailure(newAddrFailure("zero of "+addrZero2.String(), addrZero2)) } else if !ipZero2.Equal(addrZero2) || ipZero2.Equal(ipv4Zero2) || ipZero2.Equal(ipv6Zero2) || ipZero2.Equal(macZero2) { t.addFailure(newIPAddrFailure("zero of "+ipZero2.String(), ipZero2)) } else if ipZero2.Compare(addrZero2) != 0 || ipZero2.Compare(ipv4Zero2) == 0 || ipZero2.Compare(ipv6Zero2) == 0 || ipZero2.Compare(macZero2) == 0 { t.addFailure(newIPAddrFailure("zero of "+ipZero2.String(), ipZero2)) } else if ipv4Zero2.Equal(addrZero2) || ipv4Zero2.Equal(ipZero2) || ipv4Zero2.Equal(ipv6Zero2) || ipv4Zero2.Equal(macZero2) { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero2.String(), ipv4Zero2)) } else if ipv4Zero2.Compare(addrZero2) == 0 || ipv4Zero2.Compare(ipZero2) == 0 || ipv4Zero2.Compare(ipv6Zero2) == 0 || ipv4Zero2.Compare(macZero2) == 0 { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero2.String(), ipv4Zero2)) } else if ipv6Zero2.Equal(addrZero2) || ipv6Zero2.Equal(ipZero2) || ipv6Zero2.Equal(ipv4Zero2) || ipv6Zero2.Equal(macZero2) { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero2.String(), ipv6Zero2)) } else if ipv6Zero2.Compare(addrZero2) == 0 || ipv6Zero2.Compare(ipZero2) == 0 || ipv6Zero2.Compare(ipv4Zero2) == 0 || ipv6Zero2.Compare(macZero2) == 0 { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero2.String(), ipv6Zero2)) } else if macZero2.Equal(addrZero2) || macZero2.Equal(ipZero2) || macZero2.Equal(ipv4Zero2) || macZero2.Equal(ipv6Zero2) { t.addFailure(newAddressItemFailure("zero of "+macZero2.String(), macZero2)) } else if macZero2.Compare(addrZero2) == 0 || macZero2.Compare(ipZero2) == 0 || macZero2.Compare(ipv4Zero2) == 0 || macZero2.Compare(ipv6Zero2) == 0 { t.addFailure(newAddressItemFailure("zero of "+macZero2.String(), macZero2)) } // check that the zero addresses are all different if !addrZero.Equal(&ipZero) || addrZero.Equal(&ipv4Zero) || addrZero.Equal(&ipv6Zero) || addrZero.Equal(&macZero) { t.addFailure(newAddrFailure("zero of "+addrZero.String(), &addrZero)) } else if addrZero.Compare(&ipZero) != 0 || addrZero.Compare(&ipv4Zero) == 0 || addrZero.Compare(&ipv6Zero) == 0 || addrZero.Compare(&macZero) == 0 { t.addFailure(newAddrFailure("zero of "+addrZero.String(), &addrZero)) } else if !ipZero.Equal(&addrZero) || ipZero.Equal(&ipv4Zero) || ipZero.Equal(&ipv6Zero) || ipZero.Equal(&macZero) { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), &ipZero)) } else if ipZero.Compare(&addrZero) != 0 || ipZero.Compare(&ipv4Zero) == 0 || ipZero.Compare(&ipv6Zero) == 0 || ipZero.Compare(&macZero) == 0 { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), &ipZero)) } else if ipv4Zero.Equal(&addrZero) || ipv4Zero.Equal(&ipZero) || ipv4Zero.Equal(&ipv6Zero) || ipv4Zero.Equal(&macZero) { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), &ipv4Zero)) } else if ipv4Zero.Compare(&addrZero) == 0 || ipv4Zero.Compare(&ipZero) == 0 || ipv4Zero.Compare(&ipv6Zero) == 0 || ipv4Zero.Compare(&macZero) == 0 { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), &ipv4Zero)) } else if ipv6Zero.Equal(&addrZero) || ipv6Zero.Equal(&ipZero) || ipv6Zero.Equal(&ipv4Zero) || ipv6Zero.Equal(&macZero) { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), &ipv6Zero)) } else if ipv6Zero.Compare(&addrZero) == 0 || ipv6Zero.Compare(&ipZero) == 0 || ipv6Zero.Compare(&ipv4Zero) == 0 || ipv6Zero.Compare(&macZero) == 0 { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), &ipv6Zero)) } else if macZero.Equal(&addrZero) || macZero.Equal(&ipZero) || macZero.Equal(&ipv4Zero) || macZero.Equal(&ipv6Zero) { t.addFailure(newAddressItemFailure("zero of "+macZero.String(), &macZero)) } else if macZero.Compare(&addrZero) == 0 || macZero.Compare(&ipZero) == 0 || macZero.Compare(&ipv4Zero) == 0 || macZero.Compare(&ipv6Zero) == 0 { t.addFailure(newAddressItemFailure("zero of "+macZero.String(), &macZero)) } ipRangeZero := ipaddr.IPAddressSeqRange{} ipv4RangeZero := ipaddr.IPv4AddressSeqRange{} ipv6RangeZero := ipaddr.IPv6AddressSeqRange{} ipZero3 := ipRangeZero.GetLower() ipv4Zero3 := ipv4RangeZero.GetLower() ipv6Zero3 := ipv6RangeZero.GetLower() // check that zero values from address ranges match zero values from addresses if !ipZero.Equal(ipZero3) || !ipZero3.Equal(&ipZero) || !ipZero3.Equal(ipZero3) || !ipZero.Equal(&ipZero) { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero3)) } else if ipZero.Compare(ipZero3) != 0 || ipZero3.Compare(&ipZero) != 0 || ipZero.Compare(&ipZero) != 0 || ipZero3.Compare(ipZero3) != 0 { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero3)) } else if !ipv4Zero.Equal(ipv4Zero3) || !ipv4Zero3.Equal(&ipv4Zero) || !ipv4Zero3.Equal(ipv4Zero3) || !ipv4Zero.Equal(&ipv4Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero3)) } else if ipv4Zero.Compare(ipv4Zero3) != 0 || ipv4Zero3.Compare(&ipv4Zero) != 0 || ipv4Zero.Compare(&ipv4Zero) != 0 || ipv4Zero3.Compare(ipv4Zero3) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero3)) } else if !ipv6Zero.Equal(ipv6Zero3) || !ipv6Zero3.Equal(&ipv6Zero) || !ipv6Zero3.Equal(ipv6Zero3) || !ipv6Zero.Equal(&ipv6Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero3)) } else if ipv6Zero.Compare(ipv6Zero3) != 0 || ipv6Zero3.Compare(&ipv6Zero) != 0 || ipv6Zero.Compare(&ipv6Zero) != 0 || ipv6Zero3.Compare(ipv6Zero3) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero3)) } ipZero3 = ipRangeZero.GetUpper() ipv4Zero3 = ipv4RangeZero.GetUpper() ipv6Zero3 = ipv6RangeZero.GetUpper() // check that zero values from address ranges match zero values from addresses if !ipZero.Equal(ipZero3) || !ipZero3.Equal(&ipZero) || !ipZero3.Equal(ipZero3) || !ipZero.Equal(&ipZero) { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero3)) } else if ipZero.Compare(ipZero3) != 0 || ipZero3.Compare(&ipZero) != 0 || ipZero.Compare(&ipZero) != 0 || ipZero3.Compare(ipZero3) != 0 { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero3)) } else if !ipv4Zero.Equal(ipv4Zero3) || !ipv4Zero3.Equal(&ipv4Zero) || !ipv4Zero3.Equal(ipv4Zero3) || !ipv4Zero.Equal(&ipv4Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero3)) } else if ipv4Zero.Compare(ipv4Zero3) != 0 || ipv4Zero3.Compare(&ipv4Zero) != 0 || ipv4Zero.Compare(&ipv4Zero) != 0 || ipv4Zero3.Compare(ipv4Zero3) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero3)) } else if !ipv6Zero.Equal(ipv6Zero3) || !ipv6Zero3.Equal(&ipv6Zero) || !ipv6Zero3.Equal(ipv6Zero3) || !ipv6Zero.Equal(&ipv6Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero3)) } else if ipv6Zero.Compare(ipv6Zero3) != 0 || ipv6Zero3.Compare(&ipv6Zero) != 0 || ipv6Zero.Compare(&ipv6Zero) != 0 || ipv6Zero3.Compare(ipv6Zero3) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero3)) } ipZero3 = ipRangeZero.ToKey().ToSeqRange().GetLower().ToKey().ToAddress() ipv4Zero3 = ipv4RangeZero.ToKey().ToSeqRange().GetLower().ToKey().ToAddress() // Goland parser mistakenly flags this, resolving ToKey incorrectly ipv6Zero3 = ipv6RangeZero.ToKey().ToSeqRange().GetLower().ToKey().ToAddress() // Goland parser mistakenly flags this, resolving ToKey incorrectly // check that zero values from address ranges match zero values from addresses if !ipZero.Equal(ipZero3) || !ipZero3.Equal(&ipZero) || !ipZero3.Equal(ipZero3) || !ipZero.Equal(&ipZero) { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero3)) } else if ipZero.Compare(ipZero3) != 0 || ipZero3.Compare(&ipZero) != 0 || ipZero.Compare(&ipZero) != 0 || ipZero3.Compare(ipZero3) != 0 { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero3)) } else if !ipv4Zero.Equal(ipv4Zero3) || !ipv4Zero3.Equal(&ipv4Zero) || !ipv4Zero3.Equal(ipv4Zero3) || !ipv4Zero.Equal(&ipv4Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero3)) } else if ipv4Zero.Compare(ipv4Zero3) != 0 || ipv4Zero3.Compare(&ipv4Zero) != 0 || ipv4Zero.Compare(&ipv4Zero) != 0 || ipv4Zero3.Compare(ipv4Zero3) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero3)) } else if !ipv6Zero.Equal(ipv6Zero3) || !ipv6Zero3.Equal(&ipv6Zero) || !ipv6Zero3.Equal(ipv6Zero3) || !ipv6Zero.Equal(&ipv6Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero3)) } else if ipv6Zero.Compare(ipv6Zero3) != 0 || ipv6Zero3.Compare(&ipv6Zero) != 0 || ipv6Zero.Compare(&ipv6Zero) != 0 || ipv6Zero3.Compare(ipv6Zero3) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero3)) } ipZero3 = ipRangeZero.ToKey().ToSeqRange().GetUpper().ToKey().ToAddress() ipv4Zero3 = ipv4RangeZero.ToKey().ToSeqRange().GetUpper().ToKey().ToAddress() // Goland parser mistakenly flags this, resolving ToKey incorrectly ipv6Zero3 = ipv6RangeZero.ToKey().ToSeqRange().GetUpper().ToKey().ToAddress() // Goland parser mistakenly flags this, resolving ToKey incorrectly // check that zero values from address ranges match zero values from addresses if !ipZero.Equal(ipZero3) || !ipZero3.Equal(&ipZero) || !ipZero3.Equal(ipZero3) || !ipZero.Equal(&ipZero) { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero3)) } else if ipZero.Compare(ipZero3) != 0 || ipZero3.Compare(&ipZero) != 0 || ipZero.Compare(&ipZero) != 0 || ipZero3.Compare(ipZero3) != 0 { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero3)) } else if !ipv4Zero.Equal(ipv4Zero3) || !ipv4Zero3.Equal(&ipv4Zero) || !ipv4Zero3.Equal(ipv4Zero3) || !ipv4Zero.Equal(&ipv4Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero3)) } else if ipv4Zero.Compare(ipv4Zero3) != 0 || ipv4Zero3.Compare(&ipv4Zero) != 0 || ipv4Zero.Compare(&ipv4Zero) != 0 || ipv4Zero3.Compare(ipv4Zero3) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero3)) } else if !ipv6Zero.Equal(ipv6Zero3) || !ipv6Zero3.Equal(&ipv6Zero) || !ipv6Zero3.Equal(ipv6Zero3) || !ipv6Zero.Equal(&ipv6Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero3)) } else if ipv6Zero.Compare(ipv6Zero3) != 0 || ipv6Zero3.Compare(&ipv6Zero) != 0 || ipv6Zero.Compare(&ipv6Zero) != 0 || ipv6Zero3.Compare(ipv6Zero3) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero3)) } ipZero3 = ipRangeZero.ToKey().ToSeqRange().GetLower() ipv4Zero3 = ipv4RangeZero.ToKey().ToSeqRange().GetLower() ipv6Zero3 = ipv6RangeZero.ToKey().ToSeqRange().GetLower() // check that zero values from address ranges match zero values from addresses if !ipZero.Equal(ipZero3) || !ipZero3.Equal(&ipZero) || !ipZero3.Equal(ipZero3) || !ipZero.Equal(&ipZero) { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero3)) } else if ipZero.Compare(ipZero3) != 0 || ipZero3.Compare(&ipZero) != 0 || ipZero.Compare(&ipZero) != 0 || ipZero3.Compare(ipZero3) != 0 { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero3)) } else if !ipv4Zero.Equal(ipv4Zero3) || !ipv4Zero3.Equal(&ipv4Zero) || !ipv4Zero3.Equal(ipv4Zero3) || !ipv4Zero.Equal(&ipv4Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero3)) } else if ipv4Zero.Compare(ipv4Zero3) != 0 || ipv4Zero3.Compare(&ipv4Zero) != 0 || ipv4Zero.Compare(&ipv4Zero) != 0 || ipv4Zero3.Compare(ipv4Zero3) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero3)) } else if !ipv6Zero.Equal(ipv6Zero3) || !ipv6Zero3.Equal(&ipv6Zero) || !ipv6Zero3.Equal(ipv6Zero3) || !ipv6Zero.Equal(&ipv6Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero3)) } else if ipv6Zero.Compare(ipv6Zero3) != 0 || ipv6Zero3.Compare(&ipv6Zero) != 0 || ipv6Zero.Compare(&ipv6Zero) != 0 || ipv6Zero3.Compare(ipv6Zero3) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero3)) } ipZero3 = ipRangeZero.ToKey().ToSeqRange().GetUpper() ipv4Zero3 = ipv4RangeZero.ToKey().ToSeqRange().GetUpper() ipv6Zero3 = ipv6RangeZero.ToKey().ToSeqRange().GetUpper() // check that zero values from address ranges match zero values from addresses if !ipZero.Equal(ipZero3) || !ipZero3.Equal(&ipZero) || !ipZero3.Equal(ipZero3) || !ipZero.Equal(&ipZero) { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero3)) } else if ipZero.Compare(ipZero3) != 0 || ipZero3.Compare(&ipZero) != 0 || ipZero.Compare(&ipZero) != 0 || ipZero3.Compare(ipZero3) != 0 { t.addFailure(newIPAddrFailure("zero of "+ipZero.String(), ipZero3)) } else if !ipv4Zero.Equal(ipv4Zero3) || !ipv4Zero3.Equal(&ipv4Zero) || !ipv4Zero3.Equal(ipv4Zero3) || !ipv4Zero.Equal(&ipv4Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero3)) } else if ipv4Zero.Compare(ipv4Zero3) != 0 || ipv4Zero3.Compare(&ipv4Zero) != 0 || ipv4Zero.Compare(&ipv4Zero) != 0 || ipv4Zero3.Compare(ipv4Zero3) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv4Zero.String(), ipv4Zero3)) } else if !ipv6Zero.Equal(ipv6Zero3) || !ipv6Zero3.Equal(&ipv6Zero) || !ipv6Zero3.Equal(ipv6Zero3) || !ipv6Zero.Equal(&ipv6Zero) { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero3)) } else if ipv6Zero.Compare(ipv6Zero3) != 0 || ipv6Zero3.Compare(&ipv6Zero) != 0 || ipv6Zero.Compare(&ipv6Zero) != 0 || ipv6Zero3.Compare(ipv6Zero3) != 0 { t.addFailure(newAddressItemFailure("zero of "+ipv6Zero.String(), ipv6Zero3)) } ipRangeZeroKey := ipRangeZero.ToKey() ipv4ZeroRangeKey := ipv4RangeZero.ToIP().ToKey() ipv6ZeroRangeKey := ipv6RangeZero.ToIP().ToKey() if ipRangeZeroKey != ipRangeZeroKey || ipRangeZeroKey == ipv4ZeroRangeKey || ipRangeZeroKey == ipv6ZeroRangeKey { t.addFailure(newAddressItemFailure("zero of "+ipRangeZeroKey.String(), &ipRangeZero)) } iprange := ipaddr.NewIPSeqRange(nil, &ipZero) ipv4range := ipaddr.NewIPv4SeqRange(nil, &ipv4Zero).ToIP() ipv6range := ipaddr.NewIPv6SeqRange(nil, &ipv6Zero).ToIP() if iprange.ToKey() != iprange.ToKey() || iprange.ToKey() == ipv4range.ToKey() || iprange.ToKey() == ipv6range.ToKey() { t.addFailure(newAddressItemFailure("range from nil "+iprange.String(), iprange)) } iprange = ipaddr.NewSequentialRange(nil, &ipZero) ipv4range = ipaddr.NewSequentialRange(nil, &ipv4Zero).ToIP() ipv6range = ipaddr.NewSequentialRange(nil, &ipv6Zero).ToIP() if iprange.ToKey() != iprange.ToKey() || iprange.ToKey() == ipv4range.ToKey() || iprange.ToKey() == ipv6range.ToKey() { t.addFailure(newAddressItemFailure("range from nil "+iprange.String(), iprange)) } iprange = ipaddr.NewIPSeqRange(&ipZero, nil) ipv4range = ipaddr.NewIPv4SeqRange(&ipv4Zero, nil).ToIP() ipv6range = ipaddr.NewIPv6SeqRange(&ipv6Zero, nil).ToIP() if iprange.ToKey() != iprange.ToKey() || iprange.ToKey() == ipv4range.ToKey() || iprange.ToKey() == ipv6range.ToKey() { t.addFailure(newAddressItemFailure("range from nil "+iprange.String(), iprange)) } iprange = ipaddr.NewSequentialRange(&ipZero, nil) ipv4range = ipaddr.NewSequentialRange(&ipv4Zero, nil).ToIP() ipv6range = ipaddr.NewSequentialRange(&ipv6Zero, nil).ToIP() if iprange.ToKey() != iprange.ToKey() || iprange.ToKey() == ipv4range.ToKey() || iprange.ToKey() == ipv6range.ToKey() { t.addFailure(newAddressItemFailure("range from nil "+iprange.String(), iprange)) } t.incrementTestCount() } func (t specialTypesTester) testNils() { var ipRangesIPv4 []*ipaddr.IPAddressSeqRange ipv4Addr1 := ipaddr.NewIPAddressString("1.2.3.3").GetAddress().ToIPv4() ipv4Addr2 := ipaddr.NewIPAddressString("2.2.3.4-5").GetAddress().ToIPv4() ipRangesIPv4 = append(ipRangesIPv4, nil) ipRangesIPv4 = append(ipRangesIPv4, &ipaddr.IPAddressSeqRange{}) ipRangesIPv4 = append(ipRangesIPv4, ipaddr.NewIPv4SeqRange(nil, nil).ToIP()) ipRangesIPv4 = append(ipRangesIPv4, (&ipaddr.IPv4AddressSeqRange{}).ToIP()) ipRangesIPv4 = append(ipRangesIPv4, ipaddr.NewIPv4SeqRange(&ipaddr.IPv4Address{}, nil).ToIP()) ipRangesIPv4 = append(ipRangesIPv4, ipaddr.NewIPv4SeqRange(ipv4Addr1, nil).ToIP()) ipRangesIPv4 = append(ipRangesIPv4, ipaddr.NewIPv4SeqRange(nil, ipv4Addr2).ToIP()) ipRangesIPv4 = append(ipRangesIPv4, ipaddr.NewIPv4SeqRange(ipv4Addr1, ipv4Addr2).ToIP()) for i := range ipRangesIPv4 { range1 := ipRangesIPv4[i] for j := i; j < len(ipRangesIPv4); j++ { range2 := ipRangesIPv4[j] if i == j { if range1.Compare(range2) != 0 { t.addFailure(newSeqRangeFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if range2.Compare(range1) != 0 { t.addFailure(newSeqRangeFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if !range1.Equal(range2) { t.addFailure(newSeqRangeFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if !range2.Equal(range1) { t.addFailure(newSeqRangeFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } else { if c := range1.Compare(range2); c > 0 { t.addFailure(newSeqRangeFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if c == 0 && !range1.Equal(range2) { t.addFailure(newSeqRangeFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if c2 := range2.Compare(range1); c2 < 0 { t.addFailure(newSeqRangeFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if c2 == 0 && (!range2.Equal(range1) || c != 0) { t.addFailure(newSeqRangeFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } } } ipv6Addr1 := ipaddr.NewIPAddressString("1:2:3:3::").GetAddress().ToIPv6() ipv6Addr2 := ipaddr.NewIPAddressString("2:2:3:4-5::").GetAddress().ToIPv6() var ipRangesIPv6 []*ipaddr.IPAddressSeqRange ipRangesIPv6 = append(ipRangesIPv6, nil) ipRangesIPv6 = append(ipRangesIPv6, &ipaddr.IPAddressSeqRange{}) ipRangesIPv6 = append(ipRangesIPv6, ipaddr.NewIPv6SeqRange(nil, nil).ToIP()) ipRangesIPv6 = append(ipRangesIPv6, (&ipaddr.IPv6AddressSeqRange{}).ToIP()) ipRangesIPv6 = append(ipRangesIPv6, ipaddr.NewIPv6SeqRange(ipv6Addr1, nil).ToIP()) ipRangesIPv6 = append(ipRangesIPv6, ipaddr.NewIPv6SeqRange(nil, ipv6Addr2).ToIP()) ipRangesIPv6 = append(ipRangesIPv6, ipaddr.NewIPv6SeqRange(ipv6Addr1, ipv6Addr2).ToIP()) for i := range ipRangesIPv6 { range1 := ipRangesIPv6[i] for j := i; j < len(ipRangesIPv6); j++ { range2 := ipRangesIPv6[j] if i == j { if range1.Compare(range2) != 0 { t.addFailure(newSeqRangeFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if range2.Compare(range1) != 0 { t.addFailure(newSeqRangeFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if !range1.Equal(range2) { t.addFailure(newSeqRangeFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if !range2.Equal(range1) { t.addFailure(newSeqRangeFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } else { if c := range1.Compare(range2); c > 0 { t.addFailure(newSeqRangeFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if c == 0 && !range1.Equal(range2) { t.addFailure(newSeqRangeFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if c2 := range2.Compare(range1); c2 < 0 { t.addFailure(newSeqRangeFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if c2 == 0 && (!range2.Equal(range1) || c != 0) { t.addFailure(newSeqRangeFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } } } for _, range1 := range ipRangesIPv4 { for _, range2 := range ipRangesIPv6 { // the nils and the blank ranges c1 := range1.Compare(range2) c2 := range2.Compare(range1) if range1 == nil { if range2 == nil { if c1 != 0 || c2 != 0 { t.addFailure(newSeqRangeFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } } else if c1 >= 0 { t.addFailure(newSeqRangeFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } } else if range2 == nil { if c1 <= 0 || c2 >= 0 { t.addFailure(newSeqRangeFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } } else if range1.GetByteCount() == 0 { if range2.GetByteCount() == 0 { if c1 != 0 || c2 != 0 { t.addFailure(newSeqRangeFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } } else { if c1 >= 0 || c2 <= 0 { t.addFailure(newSeqRangeFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } } } else if range2.GetByteCount() == 0 { if c1 <= 0 || c2 >= 0 { t.addFailure(newSeqRangeFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } } else if c1 >= 0 { t.addFailure(newSeqRangeFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if c2 <= 0 { t.addFailure(newSeqRangeFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } } } nil1 := ipaddr.CountComparator.CompareSeries(nil, nil) nil2 := ipaddr.CountComparator.CompareRanges(nil, nil) nil3 := ipaddr.CountComparator.CompareAddresses(nil, nil) nil4 := ipaddr.CountComparator.CompareDivisions(nil, nil) nil5 := ipaddr.CountComparator.CompareAddressSections(nil, nil) nil6 := ipaddr.CountComparator.CompareSegments(nil, nil) nil7 := ipaddr.CountComparator.Compare(nil, nil) if nil1 != 0 || nil2 != 0 || nil3 != 0 || nil4 != 0 || nil5 != 0 || nil6 != 0 || nil7 != 0 { t.addFailure(newSegmentSeriesFailure("comparison of nils yields non-zero", nil)) } ipv4Section1 := ipv4Addr1.GetSection() ipv6Section1 := ipv6Addr1.GetSection() ipv4Range1 := ipRangesIPv4[len(ipRangesIPv4)-1] ipv6Range1 := ipRangesIPv6[len(ipRangesIPv6)-1] ipv4Segment1 := ipv4Section1.GetSegment(0) ipv6Segment1 := ipv6Section1.GetSegment(0) ipDivision := ipaddr.NewDivision(11, 8) nil1 = ipaddr.CountComparator.CompareSeries(ipv4Addr1, nil) nil11 := ipaddr.CountComparator.CompareSeries(ipv6Addr1, nil) nil2 = ipaddr.CountComparator.CompareRanges(ipv4Range1, nil) nil21 := ipaddr.CountComparator.CompareRanges(ipv6Range1, nil) nil3 = ipaddr.CountComparator.CompareAddresses(ipv4Addr1, nil) nil4 = ipaddr.CountComparator.CompareDivisions(ipv4Segment1, nil) nil400 := ipaddr.CountComparator.CompareDivisions(ipv6Segment1, nil) nil40 := ipaddr.CountComparator.CompareDivisions(ipDivision, nil) nil41 := ipaddr.CountComparator.CompareSeries(ipv4Section1, nil) nil42 := ipaddr.CountComparator.CompareSeries(ipv6Section1, nil) nil5 = ipaddr.CountComparator.CompareAddressSections(ipv4Section1, nil) nil51 := ipaddr.CountComparator.CompareAddressSections(ipv6Section1, nil) nil6 = ipaddr.CountComparator.CompareSegments(ipv4Segment1, nil) nil60 := ipaddr.CountComparator.CompareSegments(ipv6Segment1, nil) nil7 = ipaddr.CountComparator.Compare(ipv4Addr1, nil) if nil1 <= 0 || nil11 <= 0 || nil2 <= 0 || nil21 <= 0 || nil3 <= 0 || nil4 <= 0 || nil400 <= 0 || nil40 <= 0 || nil41 <= 0 || nil42 <= 0 || nil5 <= 0 || nil51 <= 0 || nil6 <= 0 || nil60 <= 0 || nil7 <= 0 { t.addFailure(newSegmentSeriesFailure("comparison of nils yields negative", nil)) } nil1 = ipaddr.CountComparator.CompareSeries(nil, ipv4Addr1) nil11 = ipaddr.CountComparator.CompareSeries(nil, ipv6Addr1) nil2 = ipaddr.CountComparator.CompareRanges(nil, ipv4Range1) nil21 = ipaddr.CountComparator.CompareRanges(nil, ipv6Range1) nil3 = ipaddr.CountComparator.CompareAddresses(nil, ipv4Addr1) nil4 = ipaddr.CountComparator.CompareDivisions(nil, ipv4Segment1) nil400 = ipaddr.CountComparator.CompareDivisions(nil, ipv6Segment1) nil40 = ipaddr.CountComparator.CompareDivisions(nil, ipDivision) nil41 = ipaddr.CountComparator.CompareSeries(nil, ipv4Section1) nil42 = ipaddr.CountComparator.CompareSeries(nil, ipv6Section1) nil5 = ipaddr.CountComparator.CompareAddressSections(nil, ipv4Section1) nil51 = ipaddr.CountComparator.CompareAddressSections(nil, ipv6Section1) nil6 = ipaddr.CountComparator.CompareSegments(nil, ipv4Segment1) nil60 = ipaddr.CountComparator.CompareSegments(nil, ipv6Segment1) nil7 = ipaddr.CountComparator.Compare(nil, ipv4Addr1) if nil1 >= 0 || nil11 >= 0 || nil2 >= 0 || nil21 >= 0 || nil3 >= 0 || nil4 >= 0 || nil400 >= 0 || nil40 >= 0 || nil41 >= 0 || nil42 >= 0 || nil5 >= 0 || nil51 >= 0 || nil6 >= 0 || nil60 >= 0 || nil7 >= 0 { t.addFailure(newSegmentSeriesFailure("comparison of nils yields positive", nil)) } noIPV6Error := func(sect *ipaddr.IPv6AddressSection) *ipaddr.IPAddress { ipv6addrx, _ := ipaddr.NewIPv6Address(sect) return ipv6addrx.ToIP() } var ipAddressesIPv6 []*ipaddr.IPAddress ipAddressesIPv6 = append(ipAddressesIPv6, nil) ipAddressesIPv6 = append(ipAddressesIPv6, &ipaddr.IPAddress{}) ipAddressesIPv6 = append(ipAddressesIPv6, (&ipaddr.IPv6Address{}).ToIP()) ipAddressesIPv6 = append(ipAddressesIPv6, (&ipaddr.IPv6AddressSeqRange{}).GetLower().ToIP()) ipAddressesIPv6 = append(ipAddressesIPv6, noIPV6Error(nil)) ipAddressesIPv6 = append(ipAddressesIPv6, noIPV6Error(ipv6Section1)) for i := range ipAddressesIPv6 { range1 := ipAddressesIPv6[i] //fmt.Printf("range %d using fmt is %v\n", i+1, range1) //fmt.Printf("range %d using Stringer is "+range1.String()+"\n\n", i+1) for j := i; j < len(ipAddressesIPv6); j++ { range2 := ipAddressesIPv6[j] if i == j { if range1.Compare(range2) != 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if range2.Compare(range1) != 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if !range1.Equal(range2) { t.addFailure(newSegmentSeriesFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if !range2.Equal(range1) { t.addFailure(newSegmentSeriesFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } else { if c := range1.Compare(range2); c > 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if c == 0 && !range1.Equal(range2) { t.addFailure(newSegmentSeriesFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if c2 := range2.Compare(range1); c2 < 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if c2 == 0 && (!range2.Equal(range1) || c != 0) { t.addFailure(newSegmentSeriesFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } } } noIPV4Error := func(sect *ipaddr.IPv4AddressSection) *ipaddr.IPAddress { ipv4addrx, _ := ipaddr.NewIPv4Address(sect) return ipv4addrx.ToIP() } var ipAddressesIPv4 []*ipaddr.IPAddress ipAddressesIPv4 = append(ipAddressesIPv4, nil) ipAddressesIPv4 = append(ipAddressesIPv4, &ipaddr.IPAddress{}) ipAddressesIPv4 = append(ipAddressesIPv4, (&ipaddr.IPv4Address{}).ToIP()) ipAddressesIPv4 = append(ipAddressesIPv4, (&ipaddr.IPv4AddressSeqRange{}).GetLower().ToIP()) ipAddressesIPv4 = append(ipAddressesIPv4, noIPV4Error(nil)) ipAddressesIPv4 = append(ipAddressesIPv4, noIPV4Error(ipv4Section1)) for i := range ipAddressesIPv4 { range1 := ipAddressesIPv4[i] for j := i; j < len(ipAddressesIPv4); j++ { range2 := ipAddressesIPv4[j] if i == j { if range1.Compare(range2) != 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if range2.Compare(range1) != 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if !range1.Equal(range2) { t.addFailure(newSegmentSeriesFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if !range2.Equal(range1) { t.addFailure(newSegmentSeriesFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } else { if c := range1.Compare(range2); c > 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if c == 0 && !range1.Equal(range2) { t.addFailure(newSegmentSeriesFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if c2 := range2.Compare(range1); c2 < 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if c2 == 0 && (!range2.Equal(range1) || c != 0) { t.addFailure(newSegmentSeriesFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } } } var ipSectionsIPv6 []*ipaddr.IPAddressSection ipSectionsIPv6 = append(ipSectionsIPv6, nil) ipSectionsIPv6 = append(ipSectionsIPv6, &ipaddr.IPAddressSection{}) ipSectionsIPv6 = append(ipSectionsIPv6, (&ipaddr.IPv6AddressSection{}).ToIP()) // note that this IP section can be any section type ipSectionsIPv6 = append(ipSectionsIPv6, ipv6Section1.ToIP()) ipSectionsIPv6 = append(ipSectionsIPv6, ipv6Addr2.GetSection().ToIP()) for i := range ipSectionsIPv6 { range1 := ipSectionsIPv6[i] for j := i; j < len(ipSectionsIPv6); j++ { range2 := ipSectionsIPv6[j] if i == j { if range1.Compare(range2) != 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if range2.Compare(range1) != 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if !range1.Equal(range2) { t.addFailure(newSegmentSeriesFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if !range2.Equal(range1) { t.addFailure(newSegmentSeriesFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } else { if c := range1.Compare(range2); c > 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if c == 0 && !range1.Equal(range2) { t.addFailure(newSegmentSeriesFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if c2 := range2.Compare(range1); c2 < 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if c2 == 0 && (!range2.Equal(range1) || c != 0) { t.addFailure(newSegmentSeriesFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } } } var ipSectionsIPv4 []*ipaddr.IPAddressSection ipSectionsIPv4 = append(ipSectionsIPv4, nil) ipSectionsIPv4 = append(ipSectionsIPv4, &ipaddr.IPAddressSection{}) ipSectionsIPv4 = append(ipSectionsIPv4, (&ipaddr.IPv4AddressSection{}).ToIP()) ipSectionsIPv4 = append(ipSectionsIPv4, ipv4Section1.ToIP()) ipSectionsIPv4 = append(ipSectionsIPv4, ipv4Addr2.GetSection().ToIP()) for i := range ipSectionsIPv4 { range1 := ipSectionsIPv4[i] for j := i; j < len(ipSectionsIPv4); j++ { range2 := ipSectionsIPv4[j] if i == j { if range1.Compare(range2) != 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if range2.Compare(range1) != 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if !range1.Equal(range2) { t.addFailure(newSegmentSeriesFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if !range2.Equal(range1) { t.addFailure(newSegmentSeriesFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } else { if c := range1.Compare(range2); c > 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if c == 0 && !range1.Equal(range2) { t.addFailure(newSegmentSeriesFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if c2 := range2.Compare(range1); c2 < 0 { t.addFailure(newSegmentSeriesFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if c2 == 0 && (!range2.Equal(range1) || c != 0) { t.addFailure(newSegmentSeriesFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } } } var ipSegmentsIPv6 []*ipaddr.AddressSegment ipv6SegMult := ipv6Addr2.GetSegment(3) ipSegmentsIPv6 = append(ipSegmentsIPv6, nil) ipSegmentsIPv6 = append(ipSegmentsIPv6, &ipaddr.AddressSegment{}) ipSegmentsIPv6 = append(ipSegmentsIPv6, (&ipaddr.IPAddressSegment{}).ToSegmentBase()) ipSegmentsIPv6 = append(ipSegmentsIPv6, (&ipaddr.IPv6AddressSegment{}).ToSegmentBase()) ipSegmentsIPv6 = append(ipSegmentsIPv6, ipv6Segment1.ToSegmentBase()) ipSegmentsIPv6 = append(ipSegmentsIPv6, ipv6SegMult.ToSegmentBase()) for i := range ipSegmentsIPv6 { range1 := ipSegmentsIPv6[i] for j := i; j < len(ipSegmentsIPv6); j++ { range2 := ipSegmentsIPv6[j] if i == j { if range1.Compare(range2) != 0 { t.addFailure(newDivisionFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if range2.Compare(range1) != 0 { t.addFailure(newDivisionFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if !range1.Equal(range2) { t.addFailure(newDivisionFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if !range2.Equal(range1) { t.addFailure(newDivisionFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } else { if c := range1.Compare(range2); c > 0 { t.addFailure(newDivisionFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if c == 0 && !range1.Equal(range2) { t.addFailure(newDivisionFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if c2 := range2.Compare(range1); c2 < 0 { t.addFailure(newDivisionFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if c2 == 0 && (!range2.Equal(range1) || c != 0) { t.addFailure(newDivisionFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } } } var ipSegmentsIPv4 []*ipaddr.AddressSegment ipv4SegMult := ipv4Addr2.GetSegment(3) ipSegmentsIPv4 = append(ipSegmentsIPv4, nil) ipSegmentsIPv4 = append(ipSegmentsIPv4, &ipaddr.AddressSegment{}) ipSegmentsIPv4 = append(ipSegmentsIPv4, (&ipaddr.IPAddressSegment{}).ToSegmentBase()) ipSegmentsIPv4 = append(ipSegmentsIPv4, (&ipaddr.IPv4AddressSegment{}).ToSegmentBase()) ipSegmentsIPv4 = append(ipSegmentsIPv4, ipv4Segment1.ToSegmentBase()) ipSegmentsIPv4 = append(ipSegmentsIPv4, ipv4SegMult.ToSegmentBase()) for i := range ipSegmentsIPv4 { range1 := ipSegmentsIPv4[i] for j := i; j < len(ipSegmentsIPv4); j++ { range2 := ipSegmentsIPv4[j] if i == j { if range1.Compare(range2) != 0 { t.addFailure(newDivisionFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if range2.Compare(range1) != 0 { t.addFailure(newDivisionFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if !range1.Equal(range2) { t.addFailure(newDivisionFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if !range2.Equal(range1) { t.addFailure(newDivisionFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } else { if c := range1.Compare(range2); c > 0 { t.addFailure(newDivisionFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if c == 0 && !range1.Equal(range2) { t.addFailure(newDivisionFailure(range1.String()+" and "+range2.String()+" not equal", range1)) } else if c2 := range2.Compare(range1); c2 < 0 { t.addFailure(newDivisionFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if c2 == 0 && (!range2.Equal(range1) || c != 0) { t.addFailure(newDivisionFailure(range2.String()+" and "+range1.String()+" not equal", range1)) } } } } var ipv4AddressItems, ipv6AddressItems, ipv4RangeItems, ipv6RangeItems, ipv4SectionItems, ipv6SectionItems, ipv4SegmentItems, ipv6SegmentItems []ipaddr.AddressItem for _, item := range ipAddressesIPv4 { ipv4AddressItems = append(ipv4AddressItems, item) } for _, item := range ipAddressesIPv6 { // items in ipv6 list that are not specifically ipv6 are not necessarily bigger than similar items in ipv4 list if item.IsIPv6() { ipv6AddressItems = append(ipv6AddressItems, item) } } for _, item := range ipRangesIPv4 { ipv4RangeItems = append(ipv4RangeItems, item) } for _, item := range ipRangesIPv6 { // items in ipv6 list that are not specifically ipv6 are not necessarily bigger than similar items in ipv4 list if item.IsIPv6() { ipv6RangeItems = append(ipv6RangeItems, item) } } for _, item := range ipSectionsIPv4 { ipv4SectionItems = append(ipv4SectionItems, item) } for _, item := range ipSectionsIPv6 { // items in ipv6 list that are not specifically ipv6 are not necessarily bigger than similar items in ipv4 list if item.IsIPv6() && !item.IsAdaptiveZero() { ipv6SectionItems = append(ipv6SectionItems, item) } } for _, item := range ipSegmentsIPv4 { ipv4SegmentItems = append(ipv4SegmentItems, item) } for _, item := range ipSegmentsIPv6 { // items in ipv6 list that are not specifically ipv6 are not necessarily bigger than similar items in ipv4 list if item.IsIPv6() { ipv6SegmentItems = append(ipv6SegmentItems, item) } } // addresses > sections/groupings > seq ranges > divisions // ipv6 > ipv4s var allLists [][]ipaddr.AddressItem allLists = append(allLists, []ipaddr.AddressItem{nil}) allLists = append(allLists, ipv4SegmentItems) allLists = append(allLists, ipv6SegmentItems) allLists = append(allLists, ipv4RangeItems) allLists = append(allLists, ipv6RangeItems) allLists = append(allLists, ipv4SectionItems) allLists = append(allLists, ipv6SectionItems) allLists = append(allLists, ipv4AddressItems) allLists = append(allLists, ipv6AddressItems) for i, list1 := range allLists { for j := i + 1; j < len(allLists); j++ { t.compareLists(list1, allLists[j]) } } } func (t specialTypesTester) compareLists(items1, items2 []ipaddr.AddressItem) { for _, range1 := range items1 { for _, range2 := range items2 { // the nils and the blank ranges var c1, c2 int if range1 == nil || range2 == nil { c1 = ipaddr.CountComparator.Compare(range1, range2) c2 = ipaddr.CountComparator.Compare(range2, range1) } else { c1 = range1.Compare(range2) c2 = range2.Compare(range1) } if range1 == nil { if range2 == nil { if c1 != 0 || c2 != 0 { t.addFailure(newAddressItemFailure("comparison of nil with nil", range1)) } } else if c1 >= 0 { ipaddr.CountComparator.Compare(range1, range2) t.addFailure(newAddressItemFailure("comparison of nil with "+range2.String(), range1)) } } else if range2 == nil { if c1 <= 0 || c2 >= 0 { t.addFailure(newAddressItemFailure("comparison of "+range1.String()+" with nil", range1)) } } else if c1 > 0 { range1.Compare(range2) t.addFailure(newAddressItemFailure("comparison of "+range1.String()+" with "+range2.String()+" yields "+strconv.Itoa(range1.Compare(range2)), range1)) } else if c2 < 0 { t.addFailure(newAddressItemFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if c1 == 0 { if c2 != 0 { t.addFailure(newAddressItemFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if range1.GetCount().BitLen() != 0 && !range1.IsZero() { t.addFailure(newAddressItemFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } else if range2.GetCount().BitLen() != 0 && !range2.IsZero() { t.addFailure(newAddressItemFailure("comparison of "+range2.String()+" with "+range1.String()+" yields "+strconv.Itoa(range2.Compare(range1)), range1)) } } } } } func getCount(segmentMax, segmentCount uint64) *big.Int { segMax := new(big.Int).SetUint64(segmentMax + 1) return segMax.Exp(segMax, new(big.Int).SetUint64(segmentCount), nil) } ipaddress-go-1.5.4/ipaddr/test/testbase.go000066400000000000000000001775001440250641600205010ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package test import ( "fmt" "math" "math/big" "strconv" "strings" "sync" "time" "github.com/seancfoley/ipaddress-go/ipaddr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) func Test(isLimited bool) { acc := testAccumulator{lock: &sync.Mutex{}} var addresses addresses fullTest := false //fullTest := true fmt.Println("Starting TestRunner") startTime := time.Now() rangedAddresses := rangedAddresses{addresses: &addresses} allAddresses := allAddresses{rangedAddresses: &rangedAddresses} if isLimited { acc = testAll(addresses, rangedAddresses, allAddresses, fullTest) } else { // warm up with no caching acc = testAll(addresses, rangedAddresses, allAddresses, fullTest) allAddresses.useCache(true) rangedAddresses.useCache(true) addresses.useCache(true) routineCount := 100 var wg sync.WaitGroup wg.Add(routineCount) for i := 0; i < routineCount; i++ { go func() { defer wg.Done() newAcc := testAll(addresses, rangedAddresses, allAddresses, fullTest) acc.add(newAcc) }() } wg.Wait() } endTime := time.Now().Sub(startTime) //fmt.Printf("TestRunner\ntest count: %d\nfail count: %d\n", acc.counter, len(acc.failures)) if len(acc.failures) > 0 { fmt.Printf("%v\n", acc.failures) } fmt.Printf("TestRunner\ntest count: %d\nfail count: %d\n", acc.counter, len(acc.failures)) fmt.Printf("Done: TestRunner\nDone in %v\n", endTime) } func testAll(addresses addresses, rangedAddresses rangedAddresses, allAddresses allAddresses, fullTest bool) testAccumulator { acc := testAccumulator{lock: &sync.Mutex{}} tester := ipAddressTester{testBase{testResults: &acc, testAddresses: &addresses, fullTest: fullTest}} tester.run() hTester := hostTester{testBase{testResults: &acc, testAddresses: &addresses, fullTest: fullTest}} hTester.run() macTester := macAddressTester{testBase{testResults: &acc, testAddresses: &addresses, fullTest: fullTest}} macTester.run() rangeTester := ipAddressRangeTester{ipAddressTester{testBase{testResults: &acc, testAddresses: &rangedAddresses, fullTest: fullTest}}} rangeTester.run() hostRTester := hostRangeTester{hostTester{testBase{testResults: &acc, testAddresses: &rangedAddresses, fullTest: fullTest}}} hostRTester.run() macRangeTester := macAddressRangeTester{macAddressTester{testBase{testResults: &acc, testAddresses: &rangedAddresses, fullTest: fullTest}}} macRangeTester.run() allTester := ipAddressAllTester{ipAddressRangeTester{ipAddressTester{testBase{testResults: &acc, testAddresses: &allAddresses, fullTest: fullTest}}}} allTester.run() hostATester := hostAllTester{hostRangeTester{hostTester{testBase{testResults: &acc, testAddresses: &rangedAddresses, fullTest: fullTest}}}} hostATester.run() //rangedAddresses.getAllCached() sTypesTester := specialTypesTester{testBase{testResults: &acc, testAddresses: &addresses, fullTest: fullTest}} sTypesTester.run() addressOrderTester := addressOrderTest{testBase{testResults: &acc, testAddresses: &addresses, fullTest: fullTest}} addressOrderTester.run() //addresses.getAllCached() // because trie test creates a mega tree from all previously created addresses, it should go last genericTrieTester := trieTesterGeneric{testBase{testResults: &acc, testAddresses: &allAddresses, fullTest: fullTest}} genericTrieTester.run() keyTester := keyTester{testBase{testResults: &acc, testAddresses: &allAddresses, fullTest: fullTest}} keyTester.run() return acc } type testResults interface { // test failures addFailure(failure) // store test counts incrementTestCount() } type testAccumulator struct { counter int64 failures []failure lock *sync.Mutex } func (t *testAccumulator) add(other testAccumulator) { t.lock.Lock() t.failures = append(t.failures, other.failures...) t.counter += other.counter //fmt.Printf("added %d to get %d in counter\n", other.counter, t.counter) t.lock.Unlock() } func (t *testAccumulator) addFailure(f failure) { t.failures = append(t.failures, f) } func (t *testAccumulator) incrementTestCount() { t.counter++ } type testBase struct { fullTest bool testResults testAddresses } func (t testBase) testReverse(series ipaddr.ExtendedSegmentSeries, bitsReversedIsSame, bitsReversedPerByteIsSame bool) { segmentsReversed := series.ReverseSegments() divCount := series.GetDivisionCount() for i := 0; i < series.GetSegmentCount(); i++ { seg0 := series.GetSegment(i) seg1 := segmentsReversed.GetSegment(divCount - i - 1) if !seg0.Equal(seg1) { t.addFailure(newSegmentSeriesFailure("reversal: "+series.String()+" "+segmentsReversed.String(), series)) return } } bytesReversed, err := segmentsReversed.ReverseBytes() if err != nil { t.addFailure(newSegmentSeriesFailure("failed "+err.Error(), series)) return } bytesReversed, err = bytesReversed.ReverseBytes() if err != nil { t.addFailure(newSegmentSeriesFailure("failed "+err.Error(), series)) return } bytesReversed = bytesReversed.ReverseSegments() if !series.Equal(bytesReversed) { t.addFailure(newSegmentSeriesFailure("bytes reversal: "+series.String(), series)) return } bitsReversed, err := series.ReverseBits(false) if err != nil { t.addFailure(newSegmentSeriesFailure("failed "+err.Error(), series)) return } var equalityResult = series.Equal(bitsReversed) if bitsReversedIsSame { equalityResult = !equalityResult } if equalityResult { t.addFailure(newSegmentSeriesFailure("bit reversal 2a: "+series.String()+" "+bitsReversed.String(), series)) return } bitsReversed, err = bitsReversed.ReverseBits(false) if err != nil { t.addFailure(newSegmentSeriesFailure("failed "+err.Error(), series)) return } if !series.Equal(bitsReversed) { t.addFailure(newSegmentSeriesFailure("bit reversal 2: "+series.String(), series)) return } bitsReversed2, err := series.ReverseBits(true) if err != nil { t.addFailure(newSegmentSeriesFailure("failed "+err.Error(), series)) return } equalityResult = series.Equal(bitsReversed2) if bitsReversedPerByteIsSame { equalityResult = !equalityResult } if equalityResult { t.addFailure(newSegmentSeriesFailure("bit reversal 3a: "+series.String(), series)) return } bitsReversed2, err = bitsReversed2.ReverseBits(true) if err != nil { t.addFailure(newSegmentSeriesFailure("failed "+err.Error(), series)) return } if !series.Equal(bitsReversed2) { t.addFailure(newSegmentSeriesFailure("bit reversal 3: "+series.String(), series)) return } bytes := series.Bytes() // ab cd ef becomes fe dc ba bitsReversed3, err := series.ReverseBytes() if err != nil { t.addFailure(newSegmentSeriesFailure("failed "+err.Error(), series)) return } for i, j := 0, len(bytes)-1; i < bitsReversed3.GetSegmentCount(); i++ { seg := bitsReversed3.GetSegment(i) segBytes := seg.Bytes() if !seg.IsMultiple() { bytesLen := len(segBytes) >> 1 last := len(segBytes) - 1 for m := 0; m < bytesLen; m++ { first, lastByte := segBytes[m], segBytes[last-m] segBytes[m], segBytes[last-m] = lastByte, first } } for k := seg.GetByteCount() - 1; k >= 0; k-- { if segBytes[k] != bytes[j] { //reversal 4: 1:1:1:1-fffe:2:3:3:3 300:300:300:200:1-fffe:100:100:100 t.addFailure(newSegmentSeriesFailure("reversal 4: "+series.String()+" "+bitsReversed3.String(), series)) return } j-- } } } func (t testBase) testSegmentSeriesPrefixes(original ipaddr.ExtendedSegmentSeries, prefix, adjustment ipaddr.BitCount, _, _, adjusted, prefixSet, _ ipaddr.ExtendedSegmentSeries) { for j := 0; j < 2; j++ { var removed ipaddr.ExtendedSegmentSeries var err error if j == 0 { removed, err = original.AdjustPrefixLenZeroed(original.GetBitCount() + 1) } else { removed, err = original.AdjustPrefixLenZeroed(original.GetBitCount()) } if err != nil { t.addFailure(newSegmentSeriesFailure("removed prefix error: "+err.Error(), original)) break } if original.IsPrefixed() { prefLength := original.GetPrefixLen().Len() bitsSoFar := ipaddr.BitCount(0) for i := 0; i < removed.GetSegmentCount(); i++ { prevBitsSoFar := bitsSoFar seg := removed.GetSegment(i) bitsSoFar += seg.GetBitCount() if prefLength >= bitsSoFar { if !seg.Equal(original.GetSegment(i)) { t.addFailure(newSegmentSeriesFailure("removed prefix: "+removed.String(), original)) break } } else if prefLength <= prevBitsSoFar { if !seg.IsZero() { t.addFailure(newSegmentSeriesFailure("removed prefix all: "+removed.String(), original)) break } } else { segPrefix := prefLength - prevBitsSoFar mask := ^ipaddr.SegInt(0) << uint(seg.GetBitCount()-segPrefix) lower := seg.GetSegmentValue() upper := seg.GetUpperSegmentValue() if (lower&mask) != lower || (upper&mask) != upper { t.addFailure(newSegmentSeriesFailure("prefix app: "+removed.String()+" "+strconv.Itoa(int(lower&mask))+" "+strconv.Itoa(int(upper&mask)), original)) break } } } } else if !removed.Equal(original) { t.addFailure(newSegmentSeriesFailure("prefix removed: "+removed.String(), original)) } } } func (t testBase) testPrefixes(original ipaddr.ExtendedIPSegmentSeries, prefix, adjustment ipaddr.BitCount, _, _, adjusted, prefixSet, _ ipaddr.ExtendedIPSegmentSeries) { for j := 0; j < 2; j++ { var removed ipaddr.ExtendedIPSegmentSeries var err error if j == 0 { removed, err = original.AdjustPrefixLenZeroed(original.GetBitCount() + 1) } else { removed, err = original.AdjustPrefixLenZeroed(original.GetBitCount()) } if err != nil { t.addFailure(newSegmentSeriesFailure("removed prefix error: "+err.Error(), original)) break } if original.IsPrefixed() { prefLength := original.GetPrefixLen().Len() bitsSoFar := ipaddr.BitCount(0) for i := 0; i < removed.GetSegmentCount(); i++ { prevBitsSoFar := bitsSoFar seg := removed.GetSegment(i) bitsSoFar += seg.GetBitCount() if prefLength >= bitsSoFar { if !seg.Equal(original.GetSegment(i)) { t.addFailure(newSegmentSeriesFailure("removed prefix: "+removed.String(), original)) break } } else if prefLength <= prevBitsSoFar { if !seg.IsZero() { t.addFailure(newSegmentSeriesFailure("removed prefix all: "+removed.String(), original)) break } } else { segPrefix := prefLength - prevBitsSoFar mask := ^ipaddr.SegInt(0) << uint(seg.GetBitCount()-segPrefix) lower := seg.GetSegmentValue() upper := seg.GetUpperSegmentValue() if (lower&mask) != lower || (upper&mask) != upper { //removed = original.removePrefixLength(); t.addFailure(newSegmentSeriesFailure("prefix app: "+removed.String()+" "+strconv.Itoa(int(lower&mask))+" "+strconv.Itoa(int(upper&mask)), original)) break } } } } else if !removed.Equal(original) { t.addFailure(newSegmentSeriesFailure("prefix removed: "+removed.String(), original)) } } var adjustedSeries ipaddr.ExtendedIPSegmentSeries adjustedSeries, err := original.AdjustPrefixLenZeroed(adjustment) if err != nil { t.addFailure(newSegmentSeriesFailure("adjusted prefix error: "+err.Error(), original)) return } adjustedPrefix := adjustedSeries.GetPrefixLen() if (original.IsPrefixed() && adjustedPrefix.Matches(original.GetBitCount()+adjustment)) || (!original.IsPrefixBlock() && adjustedSeries.IsZeroHost()) { //xxxxx if we do not have prefix block, then our positive adjustment creates what would be one, then our expected is one which is wrong // all host bits of matching address are zeroed out, so we must get the zero host and not the prefix subnet adjusted, err = adjusted.ToZeroHost() if err != nil { t.addFailure(newSegmentSeriesFailure("adjusted prefix error: "+err.Error(), original)) return } } if !adjustedSeries.Equal(adjusted) { t.addFailure(newSegmentSeriesFailure("prefix adjusted: "+adjustedSeries.String(), adjusted)) _, _ = original.AdjustPrefixLenZeroed(adjustment) } else { adjustedSeries, err = original.SetPrefixLenZeroed(prefix) if err != nil { t.addFailure(newSegmentSeriesFailure("set prefix error: "+err.Error(), original)) return } if (original.IsPrefixed() && original.GetPrefixLen().Matches(original.GetBitCount())) || (!original.IsPrefixBlock() && adjustedSeries.IsZeroHost()) { // all host bits of matching address are zeroed out, so we must get the zero host and not the prefix subnet prefixSet, err = prefixSet.ToZeroHost() if err != nil { t.addFailure(newSegmentSeriesFailure("set prefix error: "+err.Error(), original)) return } } setPrefix := adjustedSeries.GetPrefixLen() if !adjustedSeries.Equal(prefixSet) { //fmt.Println(original.String() + " set: " + adjustedSeries.String() + " expected: " + prefixSet.String() + " set prefix: " + bitCountToString(prefix)) t.addFailure(newSegmentSeriesFailure("prefix set: "+adjustedSeries.String(), prefixSet)) } else { originalPref := original.GetPrefixLen() var expected ExpectedPrefixes bitLength := original.GetBitCount() if originalPref == nil { if adjustment <= 0 { expected.adjusted = cacheTestBits(bitLength + adjustment) } else { expected.adjusted = cacheTestBits(adjustment) } expected.set = cacheTestBits(prefix) } else { adj := min(max(0, originalPref.Len()+adjustment), original.GetBitCount()) expected.adjusted = cacheTestBits(adj) expected.set = cacheTestBits(prefix) } if !expected.compare(adjustedPrefix, setPrefix) { t.addFailure(newSegmentSeriesFailure("expected: "+expected.adjusted.String()+" actual "+adjustedPrefix.String()+" expected: "+expected.set.String()+" actual "+setPrefix.String(), original)) } } } } func (t testBase) testReplace(front, back *ipaddr.Address, fronts, backs []string, sep byte, isMac bool) { bitsPerSegment := front.GetBitsPerSegment() segmentCount := front.GetSegmentCount() isIpv4 := !isMac && segmentCount == ipaddr.IPv4SegmentCount prefixes := strings.Builder{} prefixes.WriteString("[\n") for replaceTargetIndex := 0; replaceTargetIndex < len(fronts); replaceTargetIndex++ { if replaceTargetIndex > 0 { prefixes.WriteString(",\n") } prefixes.WriteString("[") for replaceCount := 0; replaceCount < len(fronts)-replaceTargetIndex; replaceCount++ { if replaceCount > 0 { prefixes.WriteString(",\n") } prefixes.WriteString(" [") lowest := strings.Builder{} for replaceSourceIndex := 0; replaceSourceIndex < len(backs)-replaceCount; replaceSourceIndex++ { //We are replacing replaceCount segments in front at index replaceTargetIndex with the same number of segments starting at replaceSourceIndex in back str := strings.Builder{} k := 0 for ; k < replaceTargetIndex; k++ { if str.Len() > 0 { str.WriteByte(sep) } str.WriteString(fronts[k]) } current := k limit := replaceCount + current for ; k < limit; k++ { if str.Len() > 0 { str.WriteByte(sep) } str.WriteString(backs[replaceSourceIndex+k-current]) } for ; k < segmentCount; k++ { if str.Len() > 0 { str.WriteByte(sep) } str.WriteString(fronts[k]) } var prefix ipaddr.PrefixLen frontPrefixed := front.IsPrefixed() if frontPrefixed && (front.GetPrefixLen().Len() <= ipaddr.BitCount(replaceTargetIndex)*bitsPerSegment) && (isMac || replaceTargetIndex > 0) { //when replaceTargetIndex is 0, slight difference between mac and ipvx, for ipvx we do not account for a front prefix of 0 prefix = front.GetPrefixLen() } else if back.IsPrefixed() && (back.GetPrefixLen().Len() <= ipaddr.BitCount(replaceSourceIndex+replaceCount)*bitsPerSegment) && (isMac || replaceCount > 0) { //when replaceCount 0, slight difference between mac and ipvx, for ipvx we do not account for a back prefix prefix = cacheTestBits((ipaddr.BitCount(replaceTargetIndex) * bitsPerSegment) + max(0, back.GetPrefixLen().Len()-(ipaddr.BitCount(replaceSourceIndex)*bitsPerSegment))) } else if frontPrefixed { if front.GetPrefixLen().Len() <= ipaddr.BitCount(replaceTargetIndex+replaceCount)*bitsPerSegment { prefix = cacheTestBits(ipaddr.BitCount(replaceTargetIndex+replaceCount) * bitsPerSegment) } else { prefix = front.GetPrefixLen() } } replaceStr := " replacing " + strconv.Itoa(replaceCount) + " segments in " + front.String() + " at index " + strconv.Itoa(replaceTargetIndex) + " with segments from " + back.String() + " starting at " + strconv.Itoa(replaceSourceIndex) var new1, new2 *ipaddr.Address if isMac { fromMac := front.ToMAC() new1 = fromMac.ReplaceLen(replaceTargetIndex, replaceTargetIndex+replaceCount, back.ToMAC(), replaceSourceIndex).ToAddressBase() hostIdStr := t.createMACAddress(str.String()) new2 = hostIdStr.GetAddress().ToAddressBase() if prefix != nil { new2 = new2.SetPrefixLen(prefix.Len()) } } else { if prefix != nil { str.WriteByte('/') str.WriteString(prefix.String()) } hostIdStr := t.createAddress(str.String()) new2 = hostIdStr.GetAddress().ToAddressBase() if isIpv4 { frontIPv4 := front.ToIPv4() new1 = frontIPv4.ReplaceLen(replaceTargetIndex, replaceTargetIndex+replaceCount, back.ToIPv4(), replaceSourceIndex).ToAddressBase() } else { frontIPv6 := front.ToIPv6() new1 = frontIPv6.ReplaceLen(replaceTargetIndex, replaceTargetIndex+replaceCount, back.ToIPv6(), replaceSourceIndex).ToAddressBase() } } if !new1.Equal(new2) { failStr := "Replacement was " + new1.String() + " expected was " + new2.String() + " " + replaceStr t.addFailure(newIPAddrFailure(failStr, front.ToIP())) } if lowest.Len() > 0 { lowest.WriteByte(',') } lowest.WriteString(prefix.String()) } prefixes.WriteString(lowest.String()) prefixes.WriteByte(']') } prefixes.WriteByte(']') } prefixes.WriteByte(']') } func (t testBase) testAppendAndInsert(front, back *ipaddr.Address, fronts, backs []string, sep byte, expectedPref []ipaddr.PrefixLen, isMac bool) { extra := 0 if isMac { extra = ipaddr.ExtendedUniqueIdentifier64SegmentCount - front.GetSegmentCount() } bitsPerSegment := front.GetBitsPerSegment() isIpv4 := !isMac && front.GetSegmentCount() == ipaddr.IPv4SegmentCount for i := 0; i < len(fronts); i++ { str := strings.Builder{} k := 0 for ; k < i; k++ { if str.Len() > 0 { str.WriteByte(sep) } str.WriteString(fronts[k]) } for ; k < len(fronts); k++ { if str.Len() > 0 { str.WriteByte(sep) } str.WriteString(backs[k]) } //Split up into two sections to test append frontSection := front.GetSubSection(0, i) backSection := back.GetTrailingSection(i) var backSectionInvalid, frontSectionInvalid *ipaddr.AddressSection if i-(1+extra) >= 0 && i+1+extra <= front.GetSegmentCount() { backSectionInvalid = back.GetTrailingSection(i - (1 + extra)) frontSectionInvalid = front.GetSubSection(0, i+1+extra) } //Split up even further into 3 sections to test insert //List splits = new ArrayList(front.getSegmentCount() + 3); var splits [][]*ipaddr.AddressSection for m := 0; m <= frontSection.GetSegmentCount(); m++ { sub1 := frontSection.GetSubSection(0, m) sub2 := frontSection.GetSubSection(m, frontSection.GetSegmentCount()) splits = append(splits, []*ipaddr.AddressSection{sub1, sub2, backSection}) } for m := 0; m <= backSection.GetSegmentCount(); m++ { sub1 := backSection.GetSubSection(0, m) sub2 := backSection.GetSubSection(m, backSection.GetSegmentCount()) splits = append(splits, []*ipaddr.AddressSection{frontSection, sub1, sub2}) } //now you can insert the middle one after appending the first and last //Keep in mind that inserting the first one is like a prepend, which is like an append //Inserting the last one is an append //We already test append pretty good //So really, just insert the middle one after appending first and last var splitsJoined []*ipaddr.Address var mixed, mixed2 *ipaddr.Address if isMac { hostIdStr := t.createMACAddress(str.String()) mixed = hostIdStr.GetAddress().ToAddressBase() ignoreFrontPrefLen := i == 0 // we ignore the front prefix len if we are taking 0 bits from the front if !ignoreFrontPrefLen && front.IsPrefixed() && front.GetPrefixLen().Len() <= ipaddr.BitCount(i)*bitsPerSegment { mixed = mixed.SetPrefixLen(front.GetPrefixLen().Len()) } else if back.IsPrefixed() { mixed = mixed.SetPrefixLen(max(ipaddr.BitCount(i)*bitsPerSegment, back.GetPrefixLen().Len())) } sec := frontSection.ToMAC().Append(backSection.ToMAC()) mixed2x, err := ipaddr.NewMACAddress(sec) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error: "+err.Error(), sec)) } mixed2 = mixed2x.ToAddressBase() if frontSectionInvalid != nil && backSectionInvalid != nil { //This doesn't fail anymore because we allow large sections newSec := (frontSection.ToMAC()).Append(backSectionInvalid.ToMAC()) if newSec.GetSegmentCount() != frontSection.GetSegmentCount()+backSectionInvalid.GetSegmentCount() { t.addFailure(newSegmentSeriesFailure("unexpected seg count: "+strconv.Itoa(newSec.GetSegmentCount()), newSec)) } newSec = (frontSectionInvalid.ToMAC()).Append(backSection.ToMAC()) if newSec.GetSegmentCount() != frontSectionInvalid.GetSegmentCount()+backSection.GetSegmentCount() { t.addFailure(newSegmentSeriesFailure("unexpected seg count: "+strconv.Itoa(newSec.GetSegmentCount()), newSec)) } } for o := 0; o < len(splits); o++ { split := splits[o] f := split[0] g := split[1] h := split[2] sec = f.ToMAC().Append(h.ToMAC()) sec = sec.Insert(f.GetSegmentCount(), g.ToMAC()) if h.IsPrefixed() && h.GetPrefixLen().Len() == 0 && !f.IsPrefixed() { gPref := ipaddr.BitCount(g.GetSegmentCount()) * ipaddr.MACBitsPerSegment if g.IsPrefixed() { gPref = g.GetPrefixLen().Len() } sec = sec.SetPrefixLen(ipaddr.BitCount(f.GetSegmentCount())*ipaddr.MACBitsPerSegment + gPref) } mixed3, err := ipaddr.NewMACAddress(sec) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error: "+err.Error(), sec)) } splitsJoined = append(splitsJoined, mixed3.ToAddressBase()) } } else { if front.IsPrefixed() && front.GetPrefixLen().Len() <= (ipaddr.BitCount(i)*bitsPerSegment) && i > 0 { str.WriteByte('/') str.WriteString(strconv.Itoa(int(front.GetPrefixLen().Len()))) } else if back.IsPrefixed() { str.WriteByte('/') if ipaddr.BitCount(i)*bitsPerSegment > back.GetPrefixLen().Len() { str.WriteString(strconv.Itoa(i * int(bitsPerSegment))) } else { str.WriteString(strconv.Itoa(int(back.GetPrefixLen().Len()))) } } hostIdStr := t.createAddress(str.String()) mixed = hostIdStr.GetAddress().ToAddressBase() if isIpv4 { sec := (frontSection.ToIPv4()).Append(backSection.ToIPv4()) mixed2x, err := ipaddr.NewIPv4Address(sec) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error: "+err.Error(), sec)) } mixed2 = mixed2x.ToAddressBase() if frontSectionInvalid != nil && backSectionInvalid != nil { newSec := (frontSection.ToIPv4()).Append(backSectionInvalid.ToIPv4()) if newSec.GetSegmentCount() != frontSection.GetSegmentCount()+backSectionInvalid.GetSegmentCount() { t.addFailure(newSegmentSeriesFailure("unexpected seg count: "+strconv.Itoa(newSec.GetSegmentCount()), newSec)) } newSec = (frontSectionInvalid.ToIPv4()).Append(backSection.ToIPv4()) if newSec.GetSegmentCount() != frontSectionInvalid.GetSegmentCount()+backSection.GetSegmentCount() { t.addFailure(newSegmentSeriesFailure("unexpected seg count: "+strconv.Itoa(newSec.GetSegmentCount()), newSec)) } } for o := 0; o < len(splits); o++ { split := splits[o] f := split[0] g := split[1] h := split[2] sec = (f.ToIPv4()).Append(h.ToIPv4()) sec = sec.Insert(f.GetSegmentCount(), g.ToIPv4()) if h.IsPrefixed() && h.GetPrefixLen().Len() == 0 && !f.IsPrefixed() { gPref := ipaddr.BitCount(g.GetSegmentCount()) * ipaddr.IPv4BitsPerSegment if g.IsPrefixed() { gPref = g.GetPrefixLen().Len() } sec = sec.SetPrefixLen(ipaddr.BitCount(f.GetSegmentCount())*ipaddr.IPv4BitsPerSegment + gPref) } mixed3, err := ipaddr.NewIPv4Address(sec) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error: "+err.Error(), sec)) } splitsJoined = append(splitsJoined, mixed3.ToAddressBase()) } } else { // IPv6 sec := frontSection.ToIPv6().Append(backSection.ToIPv6()) mixed2x, err := ipaddr.NewIPv6Address(sec) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error: "+err.Error(), sec)) } mixed2 = mixed2x.ToAddressBase() if frontSectionInvalid != nil && backSectionInvalid != nil { newSec := (frontSection.ToIPv6()).Append(backSectionInvalid.ToIPv6()) if newSec.GetSegmentCount() != frontSection.GetSegmentCount()+backSectionInvalid.GetSegmentCount() { t.addFailure(newSegmentSeriesFailure("unexpected seg count: "+strconv.Itoa(newSec.GetSegmentCount()), newSec)) } newSec = (frontSectionInvalid.ToIPv6()).Append(backSection.ToIPv6()) if newSec.GetSegmentCount() != frontSectionInvalid.GetSegmentCount()+backSection.GetSegmentCount() { t.addFailure(newSegmentSeriesFailure("unexpected seg count: "+strconv.Itoa(newSec.GetSegmentCount()), newSec)) } } for o := 0; o < len(splits); o++ { split := splits[o] f := split[0] g := split[1] h := split[2] sec = f.ToIPv6().Append(h.ToIPv6()) sec = sec.Insert(f.GetSegmentCount(), g.ToIPv6()) if h.IsPrefixed() && h.GetPrefixLen().Len() == 0 && !f.IsPrefixed() { gPref := ipaddr.BitCount(g.GetSegmentCount()) * ipaddr.IPv6BitsPerSegment if g.IsPrefixed() { gPref = g.GetPrefixLen().Len() } sec = sec.SetPrefixLen(ipaddr.BitCount(f.GetSegmentCount())*ipaddr.IPv6BitsPerSegment + gPref) } mixed3, err := ipaddr.NewIPv6Address(sec) if err != nil { t.addFailure(newSegmentSeriesFailure("unexpected error: "+err.Error(), sec)) } splitsJoined = append(splitsJoined, mixed3.ToAddressBase()) } } } if !mixed.Equal(mixed2) { t.addFailure(newSegmentSeriesFailure("mixed was "+mixed.String()+" expected was "+mixed2.String(), mixed)) } if !expectedPref[i].Equal(mixed.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("mixed prefix was "+mixed.GetPrefixLen().String()+" expected was "+expectedPref[i].String(), mixed)) } if !expectedPref[i].Equal(mixed2.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("mixed2 prefix was "+mixed2.GetPrefixLen().String()+" expected was "+expectedPref[i].String(), mixed2)) } for o := 0; o < len(splitsJoined); o++ { mixed3 := splitsJoined[o] if !mixed.Equal(mixed3) { t.addFailure(newSegmentSeriesFailure("mixed was "+mixed3.String()+" expected was "+mixed.String(), mixed3)) } if !mixed3.Equal(mixed2) { t.addFailure(newSegmentSeriesFailure("mixed was "+mixed3.String()+" expected was "+mixed2.String(), mixed3)) } if !expectedPref[i].Equal(mixed3.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("mixed3 prefix was "+mixed3.GetPrefixLen().String()+" expected was "+expectedPref[i].String(), mixed3)) } } } t.incrementTestCount() } func (t testBase) testIncrement(orig *ipaddr.Address, increment int64, expectedResult *ipaddr.Address) { t.testIncrementF(orig, increment, expectedResult, true) } func (t testBase) testIncrementF(orig *ipaddr.Address, increment int64, expectedResult *ipaddr.Address, first bool) { result := orig.Increment(increment) if expectedResult == nil { if result != nil { t.addFailure(newSegmentSeriesFailure("increment mismatch result "+result.String()+" vs none expected", orig)) } } else { if !result.Equal(expectedResult) { t.addFailure(newSegmentSeriesFailure("increment mismatch result "+result.String()+" vs expected "+expectedResult.String(), orig)) } if first && !orig.IsMultiple() && increment > math.MinInt64 { //negating Long.MIN_VALUE results in same address t.testIncrementF(expectedResult, -increment, orig, false) } } t.incrementTestCount() } func (t testBase) testPrefix(original ipaddr.AddressSegmentSeries, prefixLength ipaddr.PrefixLen, minPrefix ipaddr.BitCount, equivalentPrefix ipaddr.PrefixLen) { if !original.GetPrefixLen().Equal(prefixLength) { t.addFailure(newSegmentSeriesFailure("prefix: "+original.GetPrefixLen().String()+" expected: "+prefixLength.String(), original)) } else if !cacheTestBits(original.GetMinPrefixLenForBlock()).Equal(cacheTestBits(minPrefix)) { t.addFailure(newSegmentSeriesFailure("min prefix: "+strconv.Itoa(int(original.GetMinPrefixLenForBlock()))+" expected: "+bitCountToString(minPrefix), original)) } else if !original.GetPrefixLenForSingleBlock().Equal(equivalentPrefix) { t.addFailure(newSegmentSeriesFailure("equivalent prefix: "+original.GetPrefixLenForSingleBlock().String()+" expected: "+equivalentPrefix.String(), original)) } } func (t testBase) testIPv6Strings(w *ipaddr.IPAddressString, ipAddr *ipaddr.IPAddress, normalizedString, normalizedWildcardString, canonicalWildcardString, sqlString, fullString, compressedString, canonicalString, subnetString, compressedWildcardString, mixedStringNoCompressMixed, mixedStringNoCompressHost, mixedStringCompressCoveredHost, mixedString, reverseDNSString, uncHostString, base85String, singleHex, singleOctal string) { t.testStrings(w, ipAddr, normalizedString, normalizedWildcardString, canonicalWildcardString, sqlString, fullString, compressedString, canonicalString, subnetString, subnetString, compressedWildcardString, reverseDNSString, uncHostString, singleHex, singleOctal) //now test some IPv6-only strings t.testIPv6OnlyStrings(w, ipAddr.ToIPv6(), mixedStringNoCompressMixed, mixedStringNoCompressHost, mixedStringCompressCoveredHost, mixedString, base85String) } func (t testBase) testIPv6OnlyStrings(w *ipaddr.IPAddressString, ipAddr *ipaddr.IPv6Address, mixedStringNoCompressMixed, mixedStringNoCompressHost, mixedStringCompressCoveredHost, mixedString, base85String string) { base85 := "" var err error base85, err = ipAddr.ToBase85String() if err != nil { isMatch := base85String == "" if !isMatch { t.addFailure(newIPAddrFailure("failed expected: "+base85String+" actual: "+err.Error(), w.GetAddress())) } } else { b85Match := base85 == base85String if !b85Match { t.addFailure(newIPAddrFailure("failed expected: "+base85String+" actual: "+base85, w.GetAddress())) } } m, _ := ipAddr.ToMixedString() compressOpts := new(addrstr.CompressOptionsBuilder).SetCompressSingle(true).SetCompressionChoiceOptions(addrstr.ZerosOrHost).SetMixedCompressionOptions(addrstr.MixedCompressionCoveredByHost) mixedParams := new(addrstr.IPv6StringOptionsBuilder).SetMixed(true).SetCompressOptions(compressOpts).ToOptions() mixedCompressCoveredHost, _ := ipAddr.ToCustomString(mixedParams) compressOpts = new(addrstr.CompressOptionsBuilder).SetCompressSingle(true).SetCompressionChoiceOptions(addrstr.ZerosOrHost).SetMixedCompressionOptions(addrstr.MixedCompressionNoHost) mixedParams = new(addrstr.IPv6StringOptionsBuilder).SetMixed(true).SetCompressOptions(compressOpts).ToOptions() mixedNoCompressHost, _ := ipAddr.ToCustomString(mixedParams) compressOpts = new(addrstr.CompressOptionsBuilder).SetCompressSingle(true).SetCompressionChoiceOptions(addrstr.ZerosOrHost).SetMixedCompressionOptions(addrstr.NoMixedCompression) mixedParams = new(addrstr.IPv6StringOptionsBuilder).SetMixed(true).SetCompressOptions(compressOpts).ToOptions() mixedNoCompressMixed, _ := ipAddr.ToCustomString(mixedParams) t.confirmAddrStrings(ipAddr.ToIP(), m, mixedCompressCoveredHost, mixedNoCompressHost, mixedNoCompressMixed, base85) t.confirmHostStrings(ipAddr.ToIP(), false, m, mixedCompressCoveredHost, mixedNoCompressHost, mixedNoCompressMixed) nMatch := m == (mixedString) if !nMatch { t.addFailure(newFailure("failed expected: "+mixedString+" actual: "+m, w)) } else { mccMatch := mixedCompressCoveredHost == (mixedStringCompressCoveredHost) if !mccMatch { t.addFailure(newFailure("failed expected: "+mixedStringCompressCoveredHost+" actual: "+mixedCompressCoveredHost, w)) } else { msMatch := mixedNoCompressHost == (mixedStringNoCompressHost) if !msMatch { t.addFailure(newFailure("failed expected: "+mixedStringNoCompressHost+" actual: "+mixedNoCompressHost, w)) } else { mncmMatch := mixedNoCompressMixed == (mixedStringNoCompressMixed) if !mncmMatch { t.addFailure(newFailure("failed expected: "+mixedStringNoCompressMixed+" actual: "+mixedNoCompressMixed, w)) } } } } t.incrementTestCount() } func (t testBase) confirmMACAddrStrings(macAddr *ipaddr.MACAddress, strs ...string) bool { for _, str := range strs { addrString := ipaddr.NewMACAddressString(str) addr := addrString.GetAddress() if !macAddr.Equal(addr) { t.addFailure(newSegmentSeriesFailure("failed produced string: "+str, macAddr)) return false } } t.incrementTestCount() return true } func (t testBase) confirmAddrStrings(ipAddr *ipaddr.IPAddress, strs ...string) bool { for _, str := range strs { if str == "" { continue } addrString := t.createParamsAddress(str, defaultOptions) addr := addrString.GetAddress() if !ipAddr.Equal(addr) { t.addFailure(newIPAddrFailure("failed produced string: "+str, ipAddr)) return false } } t.incrementTestCount() return true } func (t testBase) confirmIPAddrStrings(ipAddr *ipaddr.IPAddress, strs ...*ipaddr.IPAddressString) bool { for _, str := range strs { addr := str.GetAddress() if !ipAddr.Equal(addr) { t.addFailure(newIPAddrFailure("failed produced string: "+str.String(), ipAddr)) return false } } t.incrementTestCount() return true } func (t testBase) confirmHostStrings(ipAddr *ipaddr.IPAddress, omitZone bool, strs ...string) bool { for _, str := range strs { hostName := ipaddr.NewHostName(str) a := hostName.GetAddress() if omitZone { ipv6Addr := ipAddr.ToIPv6() ipv6Addr, _ = ipaddr.NewIPv6Address(ipv6Addr.GetSection()) ipAddr = ipv6Addr.ToIP() } if !ipAddr.Equal(a) { t.addFailure(newIPAddrFailure("failed produced string: "+str, ipAddr)) return false } again := hostName.ToNormalizedString() hostName = ipaddr.NewHostName(again) a = hostName.GetAddress() if !ipAddr.Equal(a) { t.addFailure(newIPAddrFailure("failed produced string: "+str, ipAddr)) return false } } t.incrementTestCount() return true } func (t testBase) confirmHostNameStrings(ipAddr *ipaddr.IPAddress, strs ...*ipaddr.HostName) bool { for _, str := range strs { a := str.GetAddress() if !ipAddr.Equal(a) { t.addFailure(newIPAddrFailure("failed produced string: "+str.String(), ipAddr)) return false } again := str.ToNormalizedString() str = ipaddr.NewHostName(again) a = str.GetAddress() if !ipAddr.Equal(a) { t.addFailure(newIPAddrFailure("failed produced string: "+str.String(), ipAddr)) return false } } t.incrementTestCount() return true } func (t testBase) testMACStrings(w *ipaddr.MACAddressString, ipAddr *ipaddr.MACAddress, normalizedString, //toColonDelimitedString compressedString, canonicalString, //toDashedString dottedString, spaceDelimitedString, singleHex string) { // testing: could test a leading zero split digit non-reverse string - a funky range string with split digits and leading zeros, like 100-299.*.10-19.4-7 which should be 1-2.0-9.0-9.*.*.*.0.1.0-9.0.0.4-7 c := ipAddr.ToCompressedString() canonical := ipAddr.ToCanonicalString() d := ipAddr.ToDashedString() n := ipAddr.ToNormalizedString() cd := ipAddr.ToColonDelimitedString() sd := ipAddr.ToSpaceDelimitedString() var hex, hexNoPrefix string var err error hex, err = ipAddr.ToHexString(true) if err != nil { isMatch := singleHex == "" if !isMatch { t.addFailure(newMACFailure("failed expected: "+singleHex+" actual: "+err.Error(), w)) } } else { t.confirmMACAddrStrings(ipAddr, hex) } hexNoPrefix, err = ipAddr.ToHexString(false) if err != nil { isMatch := singleHex == "" if !isMatch { t.addFailure(newMACFailure("failed expected non-nil, actual: "+err.Error(), w)) } } else { isMatch := singleHex == (hexNoPrefix) if !isMatch { t.addFailure(newMACFailure("failed expected: "+singleHex+" actual: "+hexNoPrefix, w)) } t.confirmMACAddrStrings(ipAddr, hexNoPrefix) //For ipv4, no 0x means decimal } t.confirmMACAddrStrings(ipAddr, c, canonical, d, n, cd, sd) nMatch := normalizedString == (n) if !nMatch { t.addFailure(newMACFailure("failed expected: "+normalizedString+" actual: "+n, w)) } else { nwMatch := normalizedString == (cd) if !nwMatch { t.addFailure(newMACFailure("failed expected: "+normalizedString+" actual: "+cd, w)) } else { cawMatch := spaceDelimitedString == (sd) if !cawMatch { t.addFailure(newMACFailure("failed expected: "+spaceDelimitedString+" actual: "+sd, w)) } else { cMatch := compressedString == (c) if !cMatch { t.addFailure(newMACFailure("failed expected: "+compressedString+" actual: "+c, w)) } else { var sMatch bool var dotted string dotted, err = ipAddr.ToDottedString() if err != nil { sMatch = dottedString == "" } else { t.confirmMACAddrStrings(ipAddr, dotted) sMatch = dotted == (dottedString) } if !sMatch { t.addFailure(newMACFailure("failed expected: "+dottedString+" actual: "+dotted, w)) } else { dashedMatch := canonicalString == (d) if !dashedMatch { t.addFailure(newMACFailure("failed expected: "+canonicalString+" actual: "+d, w)) } else { canonicalMatch := canonicalString == (canonical) if !canonicalMatch { t.addFailure(newMACFailure("failed expected: "+canonicalString+" actual: "+canonical, w)) } } } } } } } t.incrementTestCount() } func (t testBase) testHostAddressStr(addressStr string) { str := t.createAddress(addressStr) address := str.GetAddress() if address != nil { hostAddress := str.GetHostAddress() prefixIndex := strings.IndexByte(addressStr, ipaddr.PrefixLenSeparator) if prefixIndex < 0 { if !address.Equal(hostAddress) || !address.Contains(hostAddress) { t.addFailure(newFailure("failed host address with no prefix: "+hostAddress.String()+" expected: "+address.String(), str)) } } else { substr := addressStr[:prefixIndex] str2 := t.createAddress(substr) address2 := str2.GetAddress() if !address2.Equal(hostAddress) { t.addFailure(newFailure("failed host address: "+hostAddress.String()+" expected: "+address2.String(), str)) } } } } func (t testBase) testStrings(w *ipaddr.IPAddressString, ipAddr *ipaddr.IPAddress, normalizedString, normalizedWildcardString, canonicalWildcardString, sqlString, fullString, compressedString, canonicalString, subnetString, cidrString, compressedWildcardString, reverseDNSString, uncHostString, singleHex, singleOctal string) { // testing: could test a leading zero split digit non-reverse string - a funky range string with split digits and leading zeros, like 100-299.*.10-19.4-7 which should be 1-2.0-9.0-9.*.*.*.0.1.0-9.0.0.4-7 if !ipAddr.IsIPv6() || !ipAddr.ToIPv6().HasZone() { if singleHex != "" && singleOctal != "" { fmtStr := fmt.Sprintf("%s %v %#x %#o", ipAddr, ipAddr, ipAddr, ipAddr) expectedFmtStr := canonicalString + " " + canonicalString + " " + singleHex + " " + singleOctal if fmtStr != expectedFmtStr { t.addFailure(newFailure("failed expected: "+expectedFmtStr+" actual: "+fmtStr, w)) } } else if singleHex == "" && singleOctal == "" { fmtStr := fmt.Sprintf("%s %v", ipAddr, ipAddr) expectedFmtStr := canonicalString + " " + canonicalString if fmtStr != expectedFmtStr { t.addFailure(newFailure("failed expected: "+expectedFmtStr+" actual: "+fmtStr, w)) } } } t.testHostAddressStr(w.String()) c := ipAddr.ToCompressedString() canonical := ipAddr.ToCanonicalString() s := ipAddr.ToSubnetString() cidr := ipAddr.ToPrefixLenString() n := ipAddr.ToNormalizedString() nw := ipAddr.ToNormalizedWildcardString() caw := ipAddr.ToCanonicalWildcardString() cw := ipAddr.ToCompressedWildcardString() sql := ipAddr.ToSQLWildcardString() full := ipAddr.ToFullString() rDNS, _ := ipAddr.ToReverseDNSString() unc := ipAddr.ToUNCHostName() var hex, hexNoPrefix, octal string var err error //try { hex, err = ipAddr.ToHexString(true) if err != nil { isMatch := singleHex == "" if !isMatch { t.addFailure(newFailure("failed expected: "+singleHex+" actual: "+err.Error(), w)) } } else { isMatch := singleHex == hex if !isMatch { t.addFailure(newFailure("failed expected: "+singleHex+" actual: "+hex, w)) } t.confirmAddrStrings(ipAddr, hex) } hexNoPrefix, err = ipAddr.ToHexString(false) if err != nil { isMatch := singleHex == "" if !isMatch { t.addFailure(newFailure("failed expected: "+singleHex+" actual: "+err.Error(), w)) } } else { if ipAddr.IsIPv6() { t.confirmAddrStrings(ipAddr, hexNoPrefix) //For ipv4, no 0x means decimal } } octal, err = ipAddr.ToOctalString(true) if err != nil { isMatch := singleOctal == "" if !isMatch { t.addFailure(newFailure("failed expected: "+singleOctal+" actual: "+err.Error(), w)) } } else { isMatch := singleOctal == (octal) if !isMatch { t.addFailure(newFailure("failed expected: "+singleOctal+" actual: "+octal, w)) } if ipAddr.IsIPv4() { t.confirmAddrStrings(ipAddr, octal) } } binary, err := ipAddr.ToBinaryString(false) if err != nil { isMatch := singleHex == "" if !isMatch { t.addFailure(newFailure("failed expected non-nil binary string but got: "+err.Error(), w)) } } else if ipAddr == nil { if binary != "" { t.addFailure(newFailure("failed expected for nil binary string but got: "+binary, w)) } } else { for i := 0; i < len(binary); i++ { c2 := binary[i] if c2 == '%' || c2 == '/' { //in most cases we handle prefixed strings by printing the whole address as a range. //however, for prefixed non-multiple addresses we still have the prefix next := strings.IndexByte(binary[i+1:], '-') if next >= 0 { i = next + 1 } else { if c2 == '/' && len(binary)-i > 4 { t.addFailure(newFailure("failed binary prefix: "+binary, w)) } break } } if c2 != '0' && c2 != '1' && c2 != '-' { t.addFailure(newFailure("failed expected non-nil binary string but got: "+binary, w)) break } } var withStrPrefix string next := strings.IndexByte(binary, '-') if next >= 0 { withStrPrefix = ipaddr.BinaryPrefix + binary[:next+1] + ipaddr.BinaryPrefix + binary[next+1:] } else { withStrPrefix = ipaddr.BinaryPrefix + binary } t.confirmAddrStrings(ipAddr, withStrPrefix) } binary = ipAddr.ToSegmentedBinaryString() t.confirmAddrStrings(ipAddr, c, canonical, s, cidr, n, nw, caw, cw, binary) if ipAddr.IsIPv6() { t.confirmAddrStrings(ipAddr, full) t.confirmHostStrings(ipAddr, true, rDNS) // reverse-DNS are valid hosts with embedded addresses skipUncParse := false zone := strings.IndexByte(unc, 's') if zone >= 0 { badChar := strings.IndexAny(unc[zone+1:], "%") if badChar >= 0 { skipUncParse = true } } if !skipUncParse { t.confirmHostStrings(ipAddr, false, unc) // UNCs are usually (as long as no abnormal zone) valid hosts with embedded addresses } } else { params := new(addrstrparam.IPAddressStringParamsBuilder).Allow_inet_aton(false).ToParams() fullAddrString := ipaddr.NewIPAddressStringParams(full, params) t.confirmIPAddrStrings(ipAddr, fullAddrString) t.confirmHostStrings(ipAddr, false, rDNS, unc) //these two are valid hosts with embedded addresses } t.confirmHostStrings(ipAddr, false, c, canonical, s, cidr, n, nw, caw, cw) if ipAddr.IsIPv6() { t.confirmHostStrings(ipAddr, false, full) } else { params := new(addrstrparam.HostNameParamsBuilder).GetIPAddressParamsBuilder().Allow_inet_aton(false).GetParentBuilder().ToParams() fullAddrString := ipaddr.NewHostNameParams(full, params) t.confirmHostNameStrings(ipAddr, fullAddrString) } nMatch := normalizedString == (n) if !nMatch { t.addFailure(newFailure("failed expected: "+normalizedString+" actual: "+n, w)) } else { nwMatch := normalizedWildcardString == (nw) if !nwMatch { t.addFailure(newFailure("failed expected: "+normalizedWildcardString+" actual: "+nw, w)) } else { cawMatch := canonicalWildcardString == (caw) if !cawMatch { t.addFailure(newFailure("failed expected: "+canonicalWildcardString+" actual: "+caw, w)) } else { cMatch := compressedString == (c) if !cMatch { t.addFailure(newFailure("failed expected: "+compressedString+" actual: "+c, w)) } else { sMatch := subnetString == (s) if !sMatch { t.addFailure(newFailure("failed expected: "+subnetString+" actual: "+s, w)) } else { cwMatch := compressedWildcardString == (cw) if !cwMatch { t.addFailure(newFailure("failed expected: "+compressedWildcardString+" actual: "+cw, w)) } else { wMatch := sqlString == (sql) if !wMatch { t.addFailure(newFailure("failed expected: "+sqlString+" actual: "+sql, w)) } else { cidrMatch := cidrString == (cidr) if !cidrMatch { t.addFailure(newFailure("failed expected: "+cidrString+" actual: "+cidr, w)) } else { canonicalMatch := canonicalString == (canonical) if !canonicalMatch { t.addFailure(newFailure("failed expected: "+canonicalString+" actual: "+canonical, w)) } else { fullMatch := fullString == (full) if !fullMatch { t.addFailure(newFailure("failed expected: "+fullString+" actual: "+full, w)) } else { rdnsMatch := reverseDNSString == rDNS if !rdnsMatch { t.addFailure(newFailure("failed expected: "+reverseDNSString+" actual: "+rDNS, w)) } else { uncMatch := uncHostString == unc if !uncMatch { t.addFailure(newFailure("failed expected: "+uncHostString+" actual: "+unc, w)) } } } } } } } } } } } } t.incrementTestCount() } func (t testBase) testCountRedirect(w ipaddr.ExtendedIdentifierString, number uint64, excludeZerosNumber uint64) { t.testCountImpl(w, number, false) if excludeZerosNumber != math.MaxUint64 { // this is used to filter out mac tests t.testCountImpl(w, excludeZerosNumber, true) } } // wrappedAddressIterator converts an address iterator of any address type to an iterator of *Address type wrappedAddressIterator[T ipaddr.AddressType] struct { ipaddr.Iterator[T] } func (iter wrappedAddressIterator[T]) Next() *ipaddr.Address { return iter.Iterator.Next().ToAddressBase() } func (t testBase) testCountImpl(w ipaddr.ExtendedIdentifierString, number uint64, excludeZeroHosts bool) { if !t.fullTest && number > countLimit { return } val := w.GetAddress() var count *big.Int if excludeZeroHosts { count = getNonZeroHostCount(val.ToAddressBase().ToIP()) } else { count = val.GetCount() } var set []ipaddr.AddressItem if count.Cmp(new(big.Int).SetUint64(number)) != 0 { t.addFailure(newSegmentSeriesFailure("count was "+count.String()+" instead of expected count "+strconv.FormatUint(number, 10), val)) } else { var addrIterator ipaddr.Iterator[*ipaddr.Address] if excludeZeroHosts { addrIterator = wrappedAddressIterator[*ipaddr.IPAddress]{getNonZeroHostIterator(val.ToAddressBase().ToIP())} } else { addrIterator = val.ToAddressBase().Iterator() } var counter uint64 var next *ipaddr.Address for addrIterator.HasNext() { next = addrIterator.Next() if counter == 0 { lower := val.ToAddressBase().GetLower() if excludeZeroHosts { if lower.ToIP().IsZeroHost() && next.Equal(lower) { t.addFailure(newIPAddrFailure("lowest: "+lower.String()+" next: "+next.String(), next.ToIP())) } } else { if !next.Equal(lower) { t.addFailure(newSegmentSeriesFailure("lowest: "+lower.String()+" next: "+next.String(), next)) } } if !next.GetPrefixLen().Equal(val.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("val prefix length: "+val.GetPrefixLen().String()+" upper prefix length: "+next.GetPrefixLen().String(), next)) } if !lower.GetPrefixLen().Equal(val.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("val prefix length: "+val.GetPrefixLen().String()+" lowest prefix length: "+lower.GetPrefixLen().String(), lower)) } } else if counter == 1 { if !next.GetPrefixLen().Equal(val.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("val prefix length: "+val.GetPrefixLen().String()+" upper prefix length: "+next.GetPrefixLen().String(), next)) } } set = append(set, next) counter++ } if number < uint64(maxInt) && len(set) != int(number) { t.addFailure(newSegmentSeriesFailure("set count was "+strconv.Itoa(len(set))+" instead of expected "+strconv.FormatUint(number, 10), val.ToAddressBase())) } else if counter != number { t.addFailure(newSegmentSeriesFailure("set count was "+strconv.Itoa(len(set))+" instead of expected "+strconv.FormatUint(number, 10), val.ToAddressBase())) } else if number > 0 { if !next.Equal(val.ToAddressBase().GetUpper()) { t.addFailure(newSegmentSeriesFailure("highest: "+val.ToAddressBase().GetUpper().String(), next)) } else { lower := val.ToAddressBase().GetLower() if excludeZeroHosts { addr := val.ToAddressBase().ToIP() if counter == 1 && (!addr.GetUpper().Equal(lower) && !addr.GetUpper().IsZeroHost() && !lower.ToIP().IsZeroHost()) { t.addFailure(newSegmentSeriesFailure("highest: "+val.ToAddressBase().GetUpper().String()+" lowest: "+val.ToAddressBase().GetLower().String(), next)) } } else { if counter == 1 && !val.ToAddressBase().GetUpper().Equal(lower) { t.addFailure(newSegmentSeriesFailure("highest: "+val.ToAddressBase().GetUpper().String()+" lowest: "+val.ToAddressBase().GetLower().String(), next)) } } if !next.GetPrefixLen().Equal(val.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("val prefix length: "+val.GetPrefixLen().String()+" upper prefix length: "+next.GetPrefixLen().String(), next)) } if !val.ToAddressBase().GetUpper().GetPrefixLen().Equal(val.GetPrefixLen()) { t.addFailure(newSegmentSeriesFailure("val prefix length: "+val.GetPrefixLen().String()+" upper prefix length: "+val.ToAddressBase().GetUpper().GetPrefixLen().String(), next)) } } } else { if excludeZeroHosts { if !val.ToAddressBase().ToIP().IsZeroHost() { t.addFailure(newSegmentSeriesFailure("unexpected non-zero-host: "+val.ToAddressBase().ToIP().String(), val)) } } else { t.addFailure(newSegmentSeriesFailure("unexpected zero count ", val)) } } //if(!excludeZeroHosts){ // // // Function> spliteratorFunc = excludeZeroHosts ? // // addr -> ((IPAddress)addr).nonZeroHostSpliterator() : Address::spliterator; // Function> spliteratorFunc = Address::spliterator; // // testSpliterate(t, val, 0, number, spliteratorFunc); // testSpliterate(t, val, 1, number, spliteratorFunc); // testSpliterate(t, val, 8, number, spliteratorFunc); // testSpliterate(t, val, -1, number, spliteratorFunc); // // testStream(t, val, set, Address::stream); // // AddressSection section = val.getSection(); // // // Function> sectionFunc = excludeZeroHosts ? // // addr -> ((IPAddressSection)section).nonZeroHostSpliterator() : AddressSection::spliterator; // Function> sectionFunc = AddressSection::spliterator; // // testSpliterate(t, section, 0, number, sectionFunc); // testSpliterate(t, section, 1, number, sectionFunc); // testSpliterate(t, section, 2, number, sectionFunc); // set = testSpliterate(t, section, 7, number, sectionFunc); // testSpliterate(t, section, -1, number, sectionFunc); // // testStream(t, section, set, AddressSection::stream); // // Set createdSet = null; // if(section instanceof IPv6AddressSection) { // createdSet = ((IPv6AddressSection) section).segmentsStream().map(IPv6AddressSection::new).collect(Collectors.toSet()); // } else if(section instanceof IPv4AddressSection) { // createdSet = ((IPv4AddressSection) section).segmentsStream().map(IPv4AddressSection::new).collect(Collectors.toSet()); // } else if(section instanceof MACAddressSection) { // createdSet = ((MACAddressSection) section).segmentsStream().map(MACAddressSection::new).collect(Collectors.toSet()); // } // // testStream(t, section, createdSet, AddressSection::stream); // //} } t.incrementTestCount() } const ( intSize = 32 << (^uint(0) >> 63) // 32 or 64 maxInt = 1< countLimit { return } val := w.GetAddress() _, isIp := val.(*ipaddr.IPAddress) isPrefixed := val.IsPrefixed() count := val.GetPrefixCount() var prefixSet, prefixBlockSet []ipaddr.AddressItem //HashSet prefixSet = new HashSet(); //HashSet prefixBlockSet = new HashSet(); if count.Cmp(new(big.Int).SetUint64(number)) != 0 { t.addFailure(newSegmentSeriesFailure("count was "+count.String()+" instead of expected count "+strconv.FormatUint(number, 10), val)) } else { loopCount := 0 totalCount := val.GetCount() var countedCount *big.Int originalIsPrefixBlock := val.IsPrefixBlock() for loopCount++; loopCount <= 2; loopCount++ { countedCount = bigZero() isBlock := loopCount == 1 var addrIterator ipaddr.Iterator[*ipaddr.Address] var set []ipaddr.AddressItem if isBlock { set = prefixBlockSet addrIterator = val.ToAddressBase().PrefixBlockIterator() } else { set = prefixSet addrIterator = val.ToAddressBase().PrefixIterator() } var counter uint64 var previous, next *ipaddr.Address for addrIterator.HasNext() { next = addrIterator.Next() if isBlock || (originalIsPrefixBlock && previous != nil && addrIterator.HasNext()) { if isPrefixed { if !next.IsPrefixBlock() { t.addFailure(newSegmentSeriesFailure("not prefix block next: "+next.String(), next)) break } if !next.IsSinglePrefixBlock() { t.addFailure(newSegmentSeriesFailure("not single prefix block next: "+next.String(), next)) break } } else { if next.IsPrefixBlock() { t.addFailure(newSegmentSeriesFailure("not prefix block next: "+next.String(), next)) break } if next.IsPrefixBlock() { t.addFailure(newSegmentSeriesFailure("not single prefix block next: "+next.String(), next)) break } } } if !isBlock { countedCount.Add(countedCount, next.GetCount()) } if isIp && previous != nil { if next.ToIP().Intersect(previous.ToIP()) != nil { t.addFailure(newSegmentSeriesFailure("intersection of "+previous.String()+" when iterating: "+next.ToIP().Intersect(previous.ToIP()).String(), next)) break } } set = append(set, next) counter++ previous = next } if number < uint64(maxInt) && len(set) != int(number) { t.addFailure(newSegmentSeriesFailure("set count was "+strconv.Itoa(len(set))+" instead of expected "+strconv.FormatUint(number, 10), val.ToAddressBase())) } else if counter != number { t.addFailure(newSegmentSeriesFailure("set count was "+strconv.Itoa(len(set))+" instead of expected "+strconv.FormatUint(number, 10), val.ToAddressBase())) } else if number < 0 { t.addFailure(newSegmentSeriesFailure("unexpected zero count ", val.ToAddressBase())) } else if !isBlock && countedCount.Cmp(totalCount) != 0 { t.addFailure(newSegmentSeriesFailure("count mismatch, expected "+totalCount.String()+" got "+countedCount.String(), val.ToAddressBase())) } // Function> spliteratorFunc = isBlock ? //Address::prefixBlockSpliterator : Address::prefixSpliterator; // // testSpliterate(t, val, 0, number, spliteratorFunc); // testSpliterate(t, val, 1, number, spliteratorFunc); // testSpliterate(t, val, 8, number, spliteratorFunc); // testSpliterate(t, val, -1, number, spliteratorFunc); // // if(isIp && isPrefixed) { // // use val to indicate prefix length, // // but we actually iterate on a value with different prefix length, while assigning the prefix length with the spliterator call // IPAddress ipAddr = ((IPAddress) val); // Integer prefLength = ipAddr.getPrefixLength(); // IPAddress iteratedVal = null; // if(prefLength >= val.getBitCount() - 3) { // if(!val.getNetwork().getPrefixConfiguration().allPrefixedAddressesAreSubnets()) { // iteratedVal = ipAddr.setPrefixLength(prefLength - 3, false, false); // } // } else { // iteratedVal = ipAddr.adjustPrefixLength(3, false); // } // // // if(iteratedVal != null) { // IPAddress ival = iteratedVal; // spliteratorFunc = isBlock ? addr -> ival.prefixBlockSpliterator(prefLength): // addr -> ival.prefixSpliterator(prefLength); // // testSpliterate(t, val, 0, number, spliteratorFunc); // testSpliterate(t, val, 1, number, spliteratorFunc); // testSpliterate(t, val, 3, number, spliteratorFunc); // } // } } //testStream(t, val, prefixSet, Address::prefixStream); //testStream(t, val, prefixBlockSet, Address::prefixBlockStream); } // segment tests //AddressSegment lastSeg = null; //for(int i = 0; i < val.getSegmentCount(); i++) {// note this can be a little slow with IPv6 // AddressSegment seg = val.getSegment(i); //if(i == 0 || !seg.equals(lastSeg)) { //Function> funct = segm -> segm.spliterator(); //int segCount = seg.getValueCount(); //Set segmentSet = testSpliterate(t, seg, 0, segCount, funct); //testSpliterate(t, seg, 1, segCount, funct); //testSpliterate(t, seg, 8, segCount, funct); //testSpliterate(t, seg, -1, segCount, funct); // //testStream(t, seg, segmentSet, AddressSegment::stream); // //if(seg instanceof IPAddressSegment) { // IPAddressSegment ipseg = ((IPAddressSegment)seg); // if(ipseg.isPrefixed()) { // Function> func = segm -> segm.prefixSpliterator(); // segCount = ipseg.getPrefixValueCount(); // testSpliterate(t, ipseg, 0, segCount, func); // testSpliterate(t, ipseg, 1, segCount, func); // segmentSet = testSpliterate(t, ipseg, 8, segCount, func); // testSpliterate(t, ipseg, -1, segCount, func); // // testStream(t, ipseg, segmentSet, IPAddressSegment::prefixStream); // // func = segm -> segm.prefixBlockSpliterator(); // testSpliterate(t, ipseg, 0, segCount, func); // testSpliterate(t, ipseg, 1, segCount, func); // testSpliterate(t, ipseg, 8, segCount, func); // segmentSet = testSpliterate(t, ipseg, -1, segCount, func); // // testStream(t, ipseg, segmentSet, IPAddressSegment::prefixBlockStream); // } //} //} //lastSeg = seg; //} t.incrementTestCount() } func (t testBase) hostLabelsTest(x string, labels []string) { host := t.createHost(x) t.hostLabelsHostTest(host, labels) } func (t testBase) hostLabelsHostTest(host *ipaddr.HostName, labels []string) { normalizedLabels := host.GetNormalizedLabels() if len(normalizedLabels) != len(labels) { t.addFailure(newHostFailure("normalization length "+strconv.Itoa(len(host.GetNormalizedLabels())), host)) } else { for i := 0; i < len(labels); i++ { normalizedLabels := host.GetNormalizedLabels() if labels[i] != (normalizedLabels[i]) { t.addFailure(newHostFailure("normalization label "+host.GetNormalizedLabels()[i]+" not expected label "+labels[i], host)) break } } } t.incrementTestCount() } func min(a, b ipaddr.BitCount) ipaddr.BitCount { if a < b { return a } return b } func max(a, b ipaddr.BitCount) ipaddr.BitCount { if a > b { return a } return b } type ExpectedPrefixes struct { //next, previous, adjusted, set ipaddr.PrefixLen adjusted, set ipaddr.PrefixLen } func (exp ExpectedPrefixes) compare(adjusted, set ipaddr.PrefixLen) bool { return adjusted.Equal(exp.adjusted) && set.Equal(exp.set) } type failure struct { str string rng *ipaddr.IPAddressSeqRange idStr ipaddr.HostIdentifierString series ipaddr.AddressSegmentSeries div ipaddr.DivisionType item ipaddr.AddressItem //trie *ipaddr.AddressTrie trieAssocNew *ipaddr.AssociativeTrie[*ipaddr.Address, any] trieNew *ipaddr.Trie[*ipaddr.Address] } func (f failure) String() string { return concat( concat( concat( concat( //concat( concat( "", f.series), //f.trie), f.idStr), f.rng), f.div), f.item) + ": " + f.str } func concat(str string, stringer fmt.Stringer) string { if stringer != nil { stringerStr := stringer.String() if stringerStr == "" { stringerStr = "" } if stringerStr != "" { if str != "" { return stringerStr + ": " + str } return stringerStr } } return str } func newAddressItemFailure(str string, item ipaddr.AddressItem) failure { return failure{ str: str, item: item, } } func newDivisionFailure(str string, div ipaddr.DivisionType) failure { return failure{ str: str, div: div, } } func newSegmentSeriesFailure(str string, series ipaddr.AddressSegmentSeries) failure { return failure{ str: str, series: series, } } func newSeqRangeFailure(str string, rng *ipaddr.IPAddressSeqRange) failure { return failure{ str: str, rng: rng, } } func newHostIdFailure(str string, idStr ipaddr.HostIdentifierString) failure { return failure{ str: str, idStr: idStr, } } func newTrieFailure(str string, trie *ipaddr.Trie[*ipaddr.Address]) failure { return failure{ str: str, trieNew: trie, } } func newAssocTrieFailure(str string, trie *ipaddr.AssociativeTrie[*ipaddr.Address, any]) failure { return failure{ str: str, trieAssocNew: trie, } } func newAddrFailure(str string, addr *ipaddr.Address) failure { return newSegmentSeriesFailure(str, addr) } func newIPAddrFailure(str string, addr *ipaddr.IPAddress) failure { return newSegmentSeriesFailure(str, addr) } func newMACAddrFailure(str string, addr *ipaddr.MACAddress) failure { return newSegmentSeriesFailure(str, addr) } func newHostFailure(str string, host *ipaddr.HostName) failure { return newHostIdFailure(str, host) } func newMACFailure(str string, addrStr *ipaddr.MACAddressString) failure { return newHostIdFailure(str, addrStr) } func newFailure(str string, addrStr *ipaddr.IPAddressString) failure { return newHostIdFailure(str, addrStr) } func cacheTestBits(i ipaddr.BitCount) ipaddr.PrefixLen { return ipaddr.ToPrefixLen(i) } var ( pnil ipaddr.PrefixLen = nil p0 = cacheTestBits(0) p4 = cacheTestBits(4) p8 = cacheTestBits(8) p9 = cacheTestBits(9) p11 = cacheTestBits(11) //p15 = cacheTestBits(15) p16 = cacheTestBits(16) p17 = cacheTestBits(17) p23 = cacheTestBits(23) p24 = cacheTestBits(24) p30 = cacheTestBits(30) p31 = cacheTestBits(31) p32 = cacheTestBits(32) p33 = cacheTestBits(33) p40 = cacheTestBits(40) p48 = cacheTestBits(48) p49 = cacheTestBits(49) p56 = cacheTestBits(56) p63 = cacheTestBits(63) p64 = cacheTestBits(64) p65 = cacheTestBits(65) //p97 = cacheTestBits(97) //p104 = cacheTestBits(104) p110 = cacheTestBits(110) p112 = cacheTestBits(112) //p127 = cacheTestBits(127) p128 = cacheTestBits(128) ) func bigOne() *big.Int { return big.NewInt(1) } var one = bigOne() func bigOneConst() *big.Int { return one } func bigZero() *big.Int { return new(big.Int) } var zero = bigZero() func bigZeroConst() *big.Int { return zero } ipaddress-go-1.5.4/ipaddr/test/testtries.go000066400000000000000000000304151440250641600207060ustar00rootroot00000000000000// // Copyright 2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package test import "github.com/seancfoley/ipaddress-go/ipaddr" type trieStrings struct { addrs []string treeString, addedNodeString, treeToIndexString, addedNodeToIndexString string } var treeOne = trieStrings{ addrs: []string{ "1::ffff:2:3:5", "1::ffff:2:3:4", "1::ffff:2:3:6", "1::ffff:2:3:12", "1::ffff:aa:3:4", "1::ff:aa:3:4", "1::ff:aa:3:12", "bb::ffff:2:3:6", "bb::ffff:2:3:12", "bb::ffff:2:3:22", "bb::ffff:2:3:32", "bb::ffff:2:3:42", "bb::ffff:2:3:43", }, treeString: "\n" + "○ ::/0 (13)\n" + "└─○ ::/8 (13)\n" + " ├─○ 1::/64 (7)\n" + " │ ├─○ 1::ff:aa:3:0/123 (2)\n" + " │ │ ├─● 1::ff:aa:3:4 (1)\n" + " │ │ └─● 1::ff:aa:3:12 (1)\n" + " │ └─○ 1::ffff:0:0:0/88 (5)\n" + " │ ├─○ 1::ffff:2:3:0/123 (4)\n" + " │ │ ├─○ 1::ffff:2:3:4/126 (3)\n" + " │ │ │ ├─○ 1::ffff:2:3:4/127 (2)\n" + " │ │ │ │ ├─● 1::ffff:2:3:4 (1)\n" + " │ │ │ │ └─● 1::ffff:2:3:5 (1)\n" + " │ │ │ └─● 1::ffff:2:3:6 (1)\n" + " │ │ └─● 1::ffff:2:3:12 (1)\n" + " │ └─● 1::ffff:aa:3:4 (1)\n" + " └─○ bb::ffff:2:3:0/121 (6)\n" + " ├─○ bb::ffff:2:3:0/122 (4)\n" + " │ ├─○ bb::ffff:2:3:0/123 (2)\n" + " │ │ ├─● bb::ffff:2:3:6 (1)\n" + " │ │ └─● bb::ffff:2:3:12 (1)\n" + " │ └─○ bb::ffff:2:3:20/123 (2)\n" + " │ ├─● bb::ffff:2:3:22 (1)\n" + " │ └─● bb::ffff:2:3:32 (1)\n" + " └─○ bb::ffff:2:3:42/127 (2)\n" + " ├─● bb::ffff:2:3:42 (1)\n" + " └─● bb::ffff:2:3:43 (1)\n", addedNodeString: "\n" + "○ ::/0\n" + "├─● 1::ff:aa:3:4\n" + "├─● 1::ff:aa:3:12\n" + "├─● 1::ffff:2:3:4\n" + "├─● 1::ffff:2:3:5\n" + "├─● 1::ffff:2:3:6\n" + "├─● 1::ffff:2:3:12\n" + "├─● 1::ffff:aa:3:4\n" + "├─● bb::ffff:2:3:6\n" + "├─● bb::ffff:2:3:12\n" + "├─● bb::ffff:2:3:22\n" + "├─● bb::ffff:2:3:32\n" + "├─● bb::ffff:2:3:42\n" + "└─● bb::ffff:2:3:43\n", treeToIndexString: "\n" + "○ ::/0 = 0 (13)\n" + "└─○ ::/8 = 0 (13)\n" + " ├─○ 1::/64 = 0 (7)\n" + " │ ├─○ 1::ff:aa:3:0/123 = 0 (2)\n" + " │ │ ├─● 1::ff:aa:3:4 = 5 (1)\n" + " │ │ └─● 1::ff:aa:3:12 = 6 (1)\n" + " │ └─○ 1::ffff:0:0:0/88 = 0 (5)\n" + " │ ├─○ 1::ffff:2:3:0/123 = 0 (4)\n" + " │ │ ├─○ 1::ffff:2:3:4/126 = 0 (3)\n" + " │ │ │ ├─○ 1::ffff:2:3:4/127 = 0 (2)\n" + " │ │ │ │ ├─● 1::ffff:2:3:4 = 1 (1)\n" + " │ │ │ │ └─● 1::ffff:2:3:5 = 0 (1)\n" + " │ │ │ └─● 1::ffff:2:3:6 = 2 (1)\n" + " │ │ └─● 1::ffff:2:3:12 = 3 (1)\n" + " │ └─● 1::ffff:aa:3:4 = 4 (1)\n" + " └─○ bb::ffff:2:3:0/121 = 0 (6)\n" + " ├─○ bb::ffff:2:3:0/122 = 0 (4)\n" + " │ ├─○ bb::ffff:2:3:0/123 = 0 (2)\n" + " │ │ ├─● bb::ffff:2:3:6 = 7 (1)\n" + " │ │ └─● bb::ffff:2:3:12 = 8 (1)\n" + " │ └─○ bb::ffff:2:3:20/123 = 0 (2)\n" + " │ ├─● bb::ffff:2:3:22 = 9 (1)\n" + " │ └─● bb::ffff:2:3:32 = 10 (1)\n" + " └─○ bb::ffff:2:3:42/127 = 0 (2)\n" + " ├─● bb::ffff:2:3:42 = 11 (1)\n" + " └─● bb::ffff:2:3:43 = 12 (1)\n", addedNodeToIndexString: "\n" + "○ ::/0 = 0\n" + "├─● 1::ff:aa:3:4 = 5\n" + "├─● 1::ff:aa:3:12 = 6\n" + "├─● 1::ffff:2:3:4 = 1\n" + "├─● 1::ffff:2:3:5 = 0\n" + "├─● 1::ffff:2:3:6 = 2\n" + "├─● 1::ffff:2:3:12 = 3\n" + "├─● 1::ffff:aa:3:4 = 4\n" + "├─● bb::ffff:2:3:6 = 7\n" + "├─● bb::ffff:2:3:12 = 8\n" + "├─● bb::ffff:2:3:22 = 9\n" + "├─● bb::ffff:2:3:32 = 10\n" + "├─● bb::ffff:2:3:42 = 11\n" + "└─● bb::ffff:2:3:43 = 12\n", } var treeTwo = trieStrings{ addrs: []string{ "ff80::/8", "ff80:8000::/16", "ff80:8000::/24", "ff80:8000::/32", "ff80:8000:c000::/34", "ff80:8000:c800::/36", "ff80:8000:cc00::/38", "ff80:8000:cc00::/40", }, treeString: "\n" + "○ ::/0 (8)\n" + "└─○ ff80::/16 (8)\n" + " ├─● ff80:: (1)\n" + " └─● ff80:8000::/24 (7)\n" + " └─● ff80:8000::/32 (6)\n" + " ├─● ff80:8000:: (1)\n" + " └─● ff80:8000:c000::/34 (4)\n" + " └─○ ff80:8000:c800::/37 (3)\n" + " ├─● ff80:8000:c800:: (1)\n" + " └─● ff80:8000:cc00::/38 (2)\n" + " └─● ff80:8000:cc00::/40 (1)\n", addedNodeString: "\n" + "○ ::/0\n" + "├─● ff80::\n" + "└─● ff80:8000::/24\n" + " └─● ff80:8000::/32\n" + " ├─● ff80:8000::\n" + " └─● ff80:8000:c000::/34\n" + " ├─● ff80:8000:c800::\n" + " └─● ff80:8000:cc00::/38\n" + " └─● ff80:8000:cc00::/40\n", treeToIndexString: "\n" + "○ ::/0 = 0 (8)\n" + "└─○ ff80::/16 = 0 (8)\n" + " ├─● ff80:: = 0 (1)\n" + " └─● ff80:8000::/24 = 2 (7)\n" + " └─● ff80:8000::/32 = 3 (6)\n" + " ├─● ff80:8000:: = 1 (1)\n" + " └─● ff80:8000:c000::/34 = 4 (4)\n" + " └─○ ff80:8000:c800::/37 = 0 (3)\n" + " ├─● ff80:8000:c800:: = 5 (1)\n" + " └─● ff80:8000:cc00::/38 = 6 (2)\n" + " └─● ff80:8000:cc00::/40 = 7 (1)\n", addedNodeToIndexString: "\n" + "○ ::/0 = 0\n" + "├─● ff80:: = 0\n" + "└─● ff80:8000::/24 = 2\n" + " └─● ff80:8000::/32 = 3\n" + " ├─● ff80:8000:: = 1\n" + " └─● ff80:8000:c000::/34 = 4\n" + " ├─● ff80:8000:c800:: = 5\n" + " └─● ff80:8000:cc00::/38 = 6\n" + " └─● ff80:8000:cc00::/40 = 7\n", } var treeThree = trieStrings{ addrs: []string{ "192.168.10.0/24", "192.168.10.0/26", "192.168.10.64/27", "192.168.10.96/27", "192.168.10.128/30", "192.168.10.132/30", "192.168.10.136/30", }, treeString: "\n" + "○ 0.0.0.0/0 (7)\n" + "└─● 192.168.10.0/24 (7)\n" + " ├─○ 192.168.10.0/25 (3)\n" + " │ ├─● 192.168.10.0/26 (1)\n" + " │ └─○ 192.168.10.64/26 (2)\n" + " │ ├─● 192.168.10.64/27 (1)\n" + " │ └─● 192.168.10.96/27 (1)\n" + " └─○ 192.168.10.128/28 (3)\n" + " ├─○ 192.168.10.128/29 (2)\n" + " │ ├─● 192.168.10.128/30 (1)\n" + " │ └─● 192.168.10.132/30 (1)\n" + " └─● 192.168.10.136/30 (1)\n", addedNodeString: "\n" + "○ 0.0.0.0/0\n" + "└─● 192.168.10.0/24\n" + " ├─● 192.168.10.0/26\n" + " ├─● 192.168.10.64/27\n" + " ├─● 192.168.10.96/27\n" + " ├─● 192.168.10.128/30\n" + " ├─● 192.168.10.132/30\n" + " └─● 192.168.10.136/30\n", treeToIndexString: "\n" + "○ 0.0.0.0/0 = 0 (7)\n" + "└─● 192.168.10.0/24 = 0 (7)\n" + " ├─○ 192.168.10.0/25 = 0 (3)\n" + " │ ├─● 192.168.10.0/26 = 1 (1)\n" + " │ └─○ 192.168.10.64/26 = 0 (2)\n" + " │ ├─● 192.168.10.64/27 = 2 (1)\n" + " │ └─● 192.168.10.96/27 = 3 (1)\n" + " └─○ 192.168.10.128/28 = 0 (3)\n" + " ├─○ 192.168.10.128/29 = 0 (2)\n" + " │ ├─● 192.168.10.128/30 = 4 (1)\n" + " │ └─● 192.168.10.132/30 = 5 (1)\n" + " └─● 192.168.10.136/30 = 6 (1)\n", addedNodeToIndexString: "\n" + "○ 0.0.0.0/0 = 0\n" + "└─● 192.168.10.0/24 = 0\n" + " ├─● 192.168.10.0/26 = 1\n" + " ├─● 192.168.10.64/27 = 2\n" + " ├─● 192.168.10.96/27 = 3\n" + " ├─● 192.168.10.128/30 = 4\n" + " ├─● 192.168.10.132/30 = 5\n" + " └─● 192.168.10.136/30 = 6\n", } var treeFour = trieStrings{ addrs: []string{}, treeString: "\n" + "\n", addedNodeString: "\n" + "○ \n", treeToIndexString: "\n" + "\n", addedNodeToIndexString: "\n" + "○ = 0\n", } var testIPAddressTries = [][]string{{ "1.2.3.4", "1.2.3.5", "1.2.3.6", "1.2.3.3", "1.2.3.255", "2.2.3.5", "2.2.3.128", "2.2.3.0/24", "2.2.4.0/24", "2.2.7.0/24", "2.2.4.3", "1::ffff:2:3:5", "1::ffff:2:3:4", "1::ffff:2:3:6", "1::ffff:2:3:12", "1::ffff:aa:3:4", "1::ff:aa:3:4", "1::ff:aa:3:12", "bb::ffff:2:3:6", "bb::ffff:2:3:12", "bb::ffff:2:3:22", "bb::ffff:2:3:32", "bb::ffff:2:3:42", "bb::ffff:2:3:43", }, { "0.0.0.0/8", "0.0.0.0/16", "0.0.0.0/24", "0.0.0.0", }, { "1.2.3.4", }, {}, { "128.0.0.0", }, { "0.0.0.0", }, { "0.0.0.0/0", "128.0.0.0/8", "128.128.0.0/16", "128.128.128.0/24", "128.128.128.128", }, { "0.0.0.0/0", "0.0.0.0/8", "0.128.0.0/16", "0.128.0.0/24", "0.128.0.128", }, { "128.0.0.0/8", "128.128.0.0/16", "128.128.128.0/24", "128.128.128.128", }, { "0.0.0.0/8", "0.128.0.0/16", "0.128.0.0/24", "0.128.0.128", }, { "ff80::/8", "ff80:8000::/16", "ff80:8000::/24", "ff80:8000::/32", "ff80:8000:c000::/34", "ff80:8000:c800::/36", "ff80:8000:cc00::/38", "ff80:8000:cc00::/40", }, { "0.0.0.0/0", "128.0.0.0/8", "128.0.0.0/16", "128.0.128.0/24", "128.0.128.0", }, { "0.0.0.0/0", "0.0.0.0/8", "0.0.0.0/16", "0.0.0.0/24", "0.0.0.0", }, { "1.2.3.0", "1.2.3.0/31", // consecutive "1.2.3.1", "1.2.3.0/30", "1.2.3.2", }, } var testMACTries = [][]string{{ "a:b:c:d:e:f", "f:e:c:d:b:a", "a:b:c:*:*:*", "a:b:c:d:*:*", "a:b:c:e:f:*", }, { "a:b:c:d:e:f", }, { "a:b:c:d:*:*", }, {}, { "a:a:a:b:c:d:e:f", "a:a:f:e:c:d:b:a", "a:a:a:b:c:*:*:*", "a:a:a:b:c:d:*:*", "a:a:a:b:c:b:*:*", "a:a:a:b:c:e:f:*", }, { "*:*:*:*:*:*", }, } type AddressKey = ipaddr.Key[*ipaddr.Address] func collect(addrs []string, converter func(string) *ipaddr.Address) []*ipaddr.Address { list := make([]*ipaddr.Address, 0, len(addrs)) dupChecker := make(map[AddressKey]struct{}) for _, str := range addrs { addr := converter(str) if addr != nil { key := addr.ToKey() if _, exists := dupChecker[key]; !exists { dupChecker[key] = struct{}{} list = append(list, addr) } } } return list } // IPAddressPredicateAdapter has methods to supply IP, IPv4, and IPv6 addresses to a wrapped predicate function that takes Address arguments type IPAddressPredicateAdapter struct { Adapted func(*ipaddr.Address) bool } // IPPredicate calls the wrapped predicate function with the given IP address as the argument func (a IPAddressPredicateAdapter) IPPredicate(addr *ipaddr.IPAddress) bool { return a.Adapted(addr.ToAddressBase()) } // IPv4Predicate calls the wrapped predicate function with the given IPv4 address as the argument func (a IPAddressPredicateAdapter) IPv4Predicate(addr *ipaddr.IPv4Address) bool { return a.Adapted(addr.ToAddressBase()) } // IPv6Predicate calls the wrapped predicate function with the given IPv6 address as the argument func (a IPAddressPredicateAdapter) IPv6Predicate(addr *ipaddr.IPv6Address) bool { return a.Adapted(addr.ToAddressBase()) } // IPAddressActionAdapter has methods to supply IP, IPv4, and IPv6 addresses to a wrapped consumer function that takes Address arguments type IPAddressActionAdapter struct { Adapted func(*ipaddr.Address) } // IPAction calls the wrapped consumer function with the given IP address as the argument func (a IPAddressActionAdapter) IPAction(addr *ipaddr.IPAddress) { a.Adapted(addr.ToAddressBase()) } // IPv4Action calls the wrapped consumer function with the given IPv4 address as the argument func (a IPAddressActionAdapter) IPv4Action(addr *ipaddr.IPv4Address) { a.Adapted(addr.ToAddressBase()) } // IPv6Action calls the wrapped consumer function with the given IPv6 address as the argument func (a IPAddressActionAdapter) IPv6Action(addr *ipaddr.IPv6Address) { a.Adapted(addr.ToAddressBase()) } ipaddress-go-1.5.4/ipaddr/test/trietest.go000066400000000000000000001567231440250641600205360ustar00rootroot00000000000000// // Copyright 2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package test import ( "fmt" "github.com/seancfoley/ipaddress-go/ipaddr" "reflect" "strconv" "sync/atomic" ) type trieTesterGeneric struct { //TODO to truly test the generics, need to create trieTesterGeneric[T][V] and then filter out the code for each in run() testBase } var didOneMegaTree int32 type AddressTrie = ipaddr.AddressTrie type AddressTrieNode = ipaddr.TrieNode[*ipaddr.Address] func NewIPv4AddressGenericTrie() *AddressTrie { return &AddressTrie{} } func NewIPv4AddressAssociativeGenericTrie[V any]() *ipaddr.AssociativeTrie[*ipaddr.Address, V] { return &ipaddr.AssociativeTrie[*ipaddr.Address, V]{} } func NewIPv6AddressGenericTrie() *AddressTrie { return &AddressTrie{} } func NewIPv6AddressAssociativeGenericTrie[V any]() *ipaddr.AssociativeTrie[*ipaddr.Address, V] { return &ipaddr.AssociativeTrie[*ipaddr.Address, V]{} } func NewAddressGenericTrie() *AddressTrie { return &AddressTrie{} } func NewAssociativeAddressGenericTrie[V any]() *ipaddr.AssociativeTrie[*ipaddr.Address, V] { return &ipaddr.AssociativeTrie[*ipaddr.Address, V]{} } func (t trieTesterGeneric) run() { t.testAddressCheck() t.partitionTest() sampleIPAddressTries := t.getSampleIPAddressTries() for _, treeAddrs := range sampleIPAddressTries { t.testRemove(treeAddrs) } notDoneEmptyIPv6 := true notDoneEmptyIPv4 := true for _, treeAddrs := range sampleIPAddressTries { ipv6Tree := NewIPv6AddressGenericTrie() t.createIPv6SampleTree(ipv6Tree, treeAddrs) size := ipv6Tree.Size() if size > 0 || notDoneEmptyIPv6 { if notDoneEmptyIPv6 { notDoneEmptyIPv6 = size != 0 } t.testIterate(ipv6Tree) t.testContains(ipv6Tree) } ipv4Tree := NewIPv4AddressGenericTrie() t.createIPv4SampleTree(ipv4Tree, treeAddrs) size = ipv4Tree.Size() if size > 0 || notDoneEmptyIPv4 { if notDoneEmptyIPv4 { notDoneEmptyIPv4 = size != 0 } t.testIterate(ipv4Tree) t.testContains(ipv4Tree) } } notDoneEmptyIPv6 = true notDoneEmptyIPv4 = true for i, treeAddrs := range sampleIPAddressTries { _ = i addrs := collect(treeAddrs, func(addrStr string) *ipaddr.Address { return t.createAddress(addrStr).GetAddress().ToIPv4().ToAddressBase() }) size := len(addrs) if size > 0 || notDoneEmptyIPv4 { if notDoneEmptyIPv4 { notDoneEmptyIPv4 = size != 0 } t.testAdd(NewIPv4AddressGenericTrie(), addrs) t.testEdges(NewIPv4AddressGenericTrie(), addrs) t.testMap(NewIPv4AddressAssociativeGenericTrie[any](), addrs, func(i int) any { return i }, func(v any) any { return 2 * 1 }) } addrsv6 := collect(treeAddrs, func(addrStr string) *ipaddr.Address { return t.createAddress(addrStr).GetAddress().ToIPv6().ToAddressBase() }) size = len(addrsv6) if size > 0 || notDoneEmptyIPv6 { if notDoneEmptyIPv6 { notDoneEmptyIPv6 = size != 0 } t.testAdd(NewIPv6AddressGenericTrie(), addrsv6) t.testEdges(NewIPv6AddressGenericTrie(), addrsv6) t.testMap(NewIPv6AddressAssociativeGenericTrie[any](), addrsv6, func(i int) any { return "bla" + strconv.Itoa(i) }, func(str any) any { return str.(string) + "foo" }) } } notDoneEmptyMAC := true for _, treeAddrs := range testMACTries { tree := NewAddressGenericTrie() macTree := tree t.createMACSampleTree(macTree, treeAddrs) size := macTree.Size() if size > 0 || notDoneEmptyMAC { if notDoneEmptyMAC { notDoneEmptyMAC = size != 0 } t.testIterate(macTree) t.testContains(macTree) } } notDoneEmptyMAC = true for _, treeAddrs := range testMACTries { addrs := collect(treeAddrs, func(addrStr string) *ipaddr.Address { return t.createMACAddress(addrStr).GetAddress().ToAddressBase() }) size := len(addrs) if size > 0 || notDoneEmptyIPv4 { if notDoneEmptyMAC { notDoneEmptyMAC = size != 0 } tree := NewAddressGenericTrie() t.testAdd(tree, addrs) tree2 := NewAddressGenericTrie() t.testEdges(tree2, addrs) tree3 := NewAssociativeAddressGenericTrie[any]() t.testMap(tree3, addrs, func(i int) any { return i }, func(i any) any { if i == nil { return 3 } return 3 * i.(int) }) } } for _, treeAddrs := range testMACTries { t.testRemoveMAC(treeAddrs) } doMegaTreeInt := atomic.LoadInt32(&didOneMegaTree) if doMegaTreeInt == 0 { cached := t.getAllCached() if len(cached) > 0 { doMegaTree := atomic.CompareAndSwapInt32(&didOneMegaTree, 0, 1) if doMegaTree { //fmt.Println("doing the mega") ipv6Tree1 := NewIPv6AddressGenericTrie() t.createIPv6SampleTreeAddrs(ipv6Tree1, cached) //fmt.Println(ipv6Tree1) //fmt.Printf("ipv6 mega tree has %v elements", ipv6Tree1.Size()) t.testIterate(ipv6Tree1) t.testContains(ipv6Tree1) ipv4Tree1 := NewIPv4AddressGenericTrie() t.createIPv4SampleTreeAddrs(ipv4Tree1, cached) //fmt.Println(ipv4Tree1) //fmt.Printf("ipv4 mega tree has %v elements", ipv4Tree1.Size()) t.testIterate(ipv4Tree1) t.testContains(ipv4Tree1) } } } t.testString(treeOne) t.testString(treeTwo) t.testString(treeThree) t.testString(treeFour) t.testZeroValuedAddedTrees() // try deleting the root trieb := NewAddressGenericTrie() trie := trieb if trie.Size() != 0 { t.addFailure(newTrieFailure("unexpected size "+strconv.Itoa(trie.Size()), trie)) } if trie.NodeSize() != 0 { t.addFailure(newTrieFailure("unexpected size "+strconv.Itoa(trie.NodeSize()), trie)) } trie.Add(ipaddr.NewIPAddressString("0.0.0.0/0").GetAddress().ToAddressBase()) if trie.Size() != 1 { t.addFailure(newTrieFailure("unexpected size "+strconv.Itoa(trie.Size()), trie)) } if trie.NodeSize() != 1 { t.addFailure(newTrieFailure("unexpected size "+strconv.Itoa(trie.Size()), trie)) } trie.GetRoot().Remove() if trie.Size() != 0 { t.addFailure(newTrieFailure("unexpected size "+strconv.Itoa(trie.Size()), trie)) } if trie.NodeSize() != 1 { t.addFailure(newTrieFailure("unexpected node size "+strconv.Itoa(trie.NodeSize()), trie)) } trie = NewIPv4AddressGenericTrie() trie.Add(ipaddr.NewIPAddressString("1.2.3.4").GetAddress().ToAddressBase()) trie.GetRoot().SetAdded() if trie.Size() != 2 { t.addFailure(newTrieFailure("unexpected size "+strconv.Itoa(trie.Size()), trie)) } trie.GetRoot().Remove() if trie.Size() != 1 { t.addFailure(newTrieFailure("unexpected size "+strconv.Itoa(trie.Size()), trie)) } if trie.NodeSize() != 2 { t.addFailure(newTrieFailure("unexpected node size "+strconv.Itoa(trie.NodeSize()), trie)) } trie.Clear() if trie.NodeSize() != 1 { t.addFailure(newTrieFailure("unexpected node size "+strconv.Itoa(trie.NodeSize()), trie)) } if trie.Size() != 0 { t.addFailure(newTrieFailure("unexpected size "+strconv.Itoa(trie.Size()), trie)) } trie.GetRoot().Remove() if trie.NodeSize() != 1 { t.addFailure(newTrieFailure("unexpected node size "+strconv.Itoa(trie.NodeSize()), trie)) } if trie.Size() != 0 { t.addFailure(newTrieFailure("unexpected size "+strconv.Itoa(trie.Size()), trie)) } t.incrementTestCount() } func (t trieTesterGeneric) testString(strs trieStrings) { addrTree := &AddressTrie{} t.createIPSampleTree(addrTree, strs.addrs) treeStr := addrTree.String() if treeStr != strs.treeString { t.addFailure(newTrieFailure("trie string not right, got "+treeStr+" instead of expected "+strs.treeString, addrTree)) } addedString := addrTree.AddedNodesTreeString() if addedString != strs.addedNodeString { t.addFailure(newTrieFailure("trie string not right, got "+addedString+" instead of expected "+strs.addedNodeString, addrTree)) } tree := addrTree.ConstructAddedNodesTree() addedNodeTreeStr := tree.String() if addedNodeTreeStr != strs.addedNodeString { t.addFailure(newTrieFailure("assoc trie string not right, got "+addedNodeTreeStr+" instead of expected "+strs.addedNodeString, addrTree)) } troot := tree.GetRoot() tcount := tcountNodes(troot) if !troot.IsAdded() { tcount-- } if tcount != addrTree.Size() { t.addFailure(newTrieFailure("size not right, got "+strconv.Itoa(tcount)+" instead of expected "+strconv.Itoa(addrTree.Size()), addrTree)) } assocTrie := &ipaddr.AssociativeTrie[*ipaddr.Address, int]{} for i, addr := range strs.addrs { addressStr := t.createAddress(addr) address := addressStr.GetAddress() assocTrie.Put(address.ToAddressBase(), i) } treeStr = assocTrie.String() if treeStr != strs.treeToIndexString { t.addFailure(newTrieFailure("trie string not right, got "+treeStr+" instead of expected "+strs.treeToIndexString, addrTree)) } addedNodeTreeStr = assocTrie.AddedNodesTreeString() associatedTreeStr := strs.addedNodeToIndexString if addedNodeTreeStr != associatedTreeStr { t.addFailure(newTrieFailure("assoc trie string not right, got "+treeStr+" instead of expected "+strs.addedNodeToIndexString, addrTree)) } contree := assocTrie.ConstructAddedNodesTree() addedNodeTreeStr = contree.String() if addedNodeTreeStr != associatedTreeStr { t.addFailure(newTrieFailure("assoc trie string not right, got "+addedNodeTreeStr+" instead of expected "+strs.addedNodeToIndexString, addrTree)) } root := contree.GetRoot() count := countNodes(root) if !root.IsAdded() { count-- } if count != assocTrie.Size() { t.addFailure(newTrieFailure("size not right, got "+strconv.Itoa(count)+" instead of expected "+strconv.Itoa(assocTrie.Size()), addrTree)) } t.incrementTestCount() } func countNodes(node ipaddr.AssociativeAddedTreeNode[*ipaddr.Address, int]) int { count := 1 for _, n := range node.GetSubNodes() { count += countNodes(n) } return count } func tcountNodes(node ipaddr.AddedTreeNode[*ipaddr.Address]) int { count := 1 for _, n := range node.GetSubNodes() { count += tcountNodes(n) } return count } func (t trieTesterGeneric) testZeroValuedAddedTrees() { addedTree := ipaddr.AddedTree[*ipaddr.IPv4Address]{} t.checkString(addedTree.String(), "\n○ \n") t.checkString(addedTree.GetRoot().String(), "○ ") t.checkString(addedTree.GetRoot().GetKey().String(), "") t.checkString(fmt.Sprint(addedTree.GetRoot().GetSubNodes()), "[]") t.checkString(addedTree.GetRoot().TreeString(), "\n○ \n") addedTreeNode := ipaddr.AddedTreeNode[*ipaddr.IPv4Address]{} t.checkString(addedTreeNode.String(), "○ ") t.checkString(addedTreeNode.GetKey().String(), "") t.checkString(fmt.Sprint(addedTreeNode.GetSubNodes()), "[]") t.checkString(addedTreeNode.TreeString(), "\n○ \n") assocAddedTree := ipaddr.AssociativeAddedTree[*ipaddr.IPv4Address, int]{} t.checkString(assocAddedTree.String(), "\n○ = 0\n") t.checkString(assocAddedTree.GetRoot().String(), "○ = 0") t.checkString(assocAddedTree.GetRoot().GetKey().String(), "") t.checkString(fmt.Sprint(assocAddedTree.GetRoot().GetValue()), "0") t.checkString(fmt.Sprint(assocAddedTree.GetRoot().GetSubNodes()), "[]") t.checkString(assocAddedTree.GetRoot().TreeString(), "\n○ = 0\n") assocAddedTreeNode := ipaddr.AssociativeAddedTreeNode[*ipaddr.IPAddress, float64]{} t.checkString(assocAddedTreeNode.String(), "○ = 0") t.checkString(assocAddedTreeNode.GetKey().String(), "") t.checkString(fmt.Sprint(assocAddedTreeNode.GetValue()), "0") t.checkString(fmt.Sprint(assocAddedTreeNode.GetSubNodes()), "[]") t.checkString(assocAddedTreeNode.TreeString(), "\n○ = 0\n") t.incrementTestCount() } func (t trieTesterGeneric) checkString(actual, expected string) { if actual != expected { t.addFailure(newAddressItemFailure(" mismatched strings, expected "+expected+" got "+actual, nil)) } } func (t trieTesterGeneric) testAddressCheck() { addr := t.createAddress("1.2.3.4/16").GetAddress() t.testConvertedAddrBlock(addr.ToAddressBase(), nil) t.testIPAddrBlock("1.2.3.4") t.testIPAddrBlock("::") t.testNonBlock("1-3.2.3.4") t.testConvertedBlock("1.2.3.4-5", p31) t.testNonBlock("1.2.3.5-6") t.testConvertedBlock("1.2.3.4-7", p30) t.testNonBlock("::1-2:0") t.testNonBlock("::1-2:0/112") t.testConvertedBlock("::0-3:0/112", p110) t.testIPAddrBlock("::/64") t.testIPAddrBlock("1.2.0.0/16") mac := t.createMACAddress("a:b:c:*:*:*").GetAddress() mac = mac.SetPrefixLen(48) t.testConvertedAddrBlock(mac.ToAddressBase(), p24) t.testMACAddrBlock("a:b:c:*:*:*") t.testNonMACBlock("a:b:c:*:2:*") t.testNonBlock("a:b:c:*:2:*") // passes nil into checkBlockOrAddress t.testMACAddrBlock("a:b:c:1:2:3") } func (t trieTesterGeneric) testConvertedBlock(str string, expectedPrefLen ipaddr.PrefixLen) { addr := t.createAddress(str).GetAddress() t.testConvertedAddrBlock(addr.ToAddressBase(), expectedPrefLen) } func (t trieTesterGeneric) testConvertedAddrBlock(addr *ipaddr.Address, expectedPrefLen ipaddr.PrefixLen) { result := addr.ToSinglePrefixBlockOrAddress() if result == nil { t.addFailure(newAddrFailure("unexpectedly got no single block or address for "+addr.String(), addr)) } if !addr.Equal(result) && !result.GetPrefixLen().Equal(expectedPrefLen) { t.addFailure(newAddrFailure("unexpectedly got wrong pref len "+result.GetPrefixLen().String()+" not "+expectedPrefLen.String(), addr)) } } func (t trieTesterGeneric) testNonBlock(str string) { addr := t.createAddress(str).GetAddress() result := addr.ToSinglePrefixBlockOrAddress() if result != nil { t.addFailure(newIPAddrFailure("unexpectedly got a single block or address for "+addr.String(), addr)) } } func (t trieTesterGeneric) testNonMACBlock(str string) { addr := t.createMACAddress(str).GetAddress() result := addr.ToSinglePrefixBlockOrAddress() if result != nil { t.addFailure(newMACAddrFailure("unexpectedly got a single block or address for "+addr.String(), addr)) } } func (t trieTesterGeneric) testIPAddrBlock(str string) { addr := t.createAddress(str).GetAddress() result := addr.ToSinglePrefixBlockOrAddress() if result != addr { t.addFailure(newIPAddrFailure("unexpectedly got different address "+result.String()+" for "+addr.String(), addr)) } } func (t trieTesterGeneric) testMACAddrBlock(str string) { addr := t.createMACAddress(str).GetAddress() result := addr.ToSinglePrefixBlockOrAddress() if result != addr { t.addFailure(newMACAddrFailure("unexpectedly got different address "+result.String()+" for "+addr.String(), addr)) } } func (t trieTesterGeneric) partitionTest() { addrs := "1.2.1-15.*" trie := NewIPv4AddressGenericTrie() addr := t.createAddress(addrs).GetAddress() t.partitionForTrie(trie, addr) } func (t trieTesterGeneric) partitionForTrie(trie *AddressTrie, subnet *ipaddr.IPAddress) { ipaddr.PartitionWithSingleBlockSize(subnet).PredicateForEach(IPAddressPredicateAdapter{trie.Add}.IPPredicate) if trie.Size() != 15 { t.addFailure(newTrieFailure("partition size unexpected "+strconv.Itoa(trie.Size())+", expected 15", trie.Clone())) } partition := ipaddr.PartitionWithSingleBlockSize(subnet.ToAddressBase()) all := ipaddr.ApplyForEach[*ipaddr.Address, *AddressTrieNode](partition, trie.GetAddedNode) if len(all) != 15 { t.addFailure(newTrieFailure("map size unexpected "+strconv.Itoa(trie.Size())+", expected 15", trie)) } partition = ipaddr.PartitionWithSingleBlockSize(subnet.ToAddressBase()) keyAll := ipaddr.ApplyForEach[*ipaddr.Address, struct{}](partition, func(*ipaddr.Address) struct{} { return struct{}{} }) if len(keyAll) != 15 { t.addFailure(newTrieFailure("map size unexpected "+strconv.Itoa(trie.Size())+", expected 15", trie)) } keyAll1 := make(map[ipaddr.Key[*ipaddr.Address]]struct{}) for k, v := range keyAll { keyAll1[k.ToAddress().ToKey()] = v } all2 := make(ipaddr.MappedPartition[*ipaddr.Address, *AddressTrieNode]) keyAll2 := make(map[ipaddr.Key[*ipaddr.Address]]struct{}) ipaddr.PartitionWithSingleBlockSize(subnet).ForEach(func(addr *ipaddr.IPAddress) { node := trie.GetAddedNode(addr.ToAddressBase()) all2[addr.ToAddressBase().ToKey()] = node keyAll2[addr.ToAddressBase().ToKey()] = struct{}{} }) // using keys and struct{} allow for deep-equal comparison if !reflect.DeepEqual(keyAll1, keyAll2) { str := fmt.Sprintf("maps not equal %v and %v", all, all2) reflect.DeepEqual(keyAll1, keyAll2) t.addFailure(newTrieFailure(str, trie)) } // the maps using *Address keys and *Address nodes are not expected to be equal since they use pointers for k, v := range all { if !k.ToAddress().Equal(v.GetKey()) { t.addFailure(newTrieFailure("node key wrong for "+k.String(), trie)) } found := false for k2, v2 := range all2 { if !k2.ToAddress().Equal(v2.GetKey()) { t.addFailure(newTrieFailure("node key wrong for "+k2.String(), trie)) } if k.ToAddress().Equal(k2.ToAddress()) { found = true break } } if !found { t.addFailure(newTrieFailure("could not find "+k.String(), trie)) } } trie.Clear() ipaddr.PartitionWithSpanningBlocks(subnet).PredicateForEach(IPAddressPredicateAdapter{trie.Add}.IPPredicate) if trie.Size() != 4 { t.addFailure(newTrieFailure("partition size unexpected "+strconv.Itoa(trie.Size())+", expected 4", trie.Clone())) } trie.Clear() ipaddr.PartitionWithSingleBlockSize(subnet).PredicateForEach(IPAddressPredicateAdapter{trie.Add}.IPPredicate) ipaddr.PartitionWithSpanningBlocks(subnet).PredicateForEach(IPAddressPredicateAdapter{trie.Add}.IPPredicate) if trie.Size() != 18 { t.addFailure(newTrieFailure("partition size unexpected "+strconv.Itoa(trie.Size())+", expected 18", trie.Clone())) } allAreThere := ipaddr.PartitionWithSingleBlockSize(subnet).PredicateForEach(IPAddressPredicateAdapter{trie.Contains}.IPPredicate) allAreThere2 := ipaddr.PartitionWithSpanningBlocks(subnet).PredicateForEach(IPAddressPredicateAdapter{trie.Contains}.IPPredicate) if !(allAreThere && allAreThere2) { t.addFailure(newTrieFailure("partition contains check failing", trie)) } t.incrementTestCount() } func (t trieTesterGeneric) getSampleIPAddressTries() [][]string { if !t.fullTest { return testIPAddressTries } var oneMore []string for _, tree := range testIPAddressTries { for _, addr := range tree { oneMore = append(oneMore, addr) } } return append(testIPAddressTries, oneMore) } func (t trieTesterGeneric) testRemove(addrs []string) { ipv6Tree := NewIPv6AddressGenericTrie() ipv4Tree := NewIPv4AddressGenericTrie() t.testRemoveAddrs(ipv6Tree, addrs, func(addrStr string) *ipaddr.Address { return t.createAddress(addrStr).GetAddress().ToIPv6().ToAddressBase() }) t.testRemoveAddrs(ipv4Tree, addrs, func(addrStr string) *ipaddr.Address { return t.createAddress(addrStr).GetAddress().ToIPv4().ToAddressBase() }) // reverse the address order var addrs2 = make([]string, len(addrs)) for i := range addrs { addrs2[len(addrs2)-i-1] = addrs[i] } // both trees should be empty now t.testRemoveAddrs(ipv6Tree, addrs, func(addrStr string) *ipaddr.Address { return t.createAddress(addrStr).GetAddress().ToIPv6().ToAddressBase() }) t.testRemoveAddrs(ipv4Tree, addrs, func(addrStr string) *ipaddr.Address { return t.createAddress(addrStr).GetAddress().ToIPv4().ToAddressBase() }) } func (t trieTesterGeneric) testRemoveMAC(addrs []string) { tree := NewAddressGenericTrie() t.testRemoveAddrs(tree, addrs, func(addrStr string) *ipaddr.Address { return t.createMACAddress(addrStr).GetAddress().ToAddressBase() }) // reverse the address order var addrs2 = make([]string, len(addrs)) for i := range addrs { addrs2[len(addrs2)-i-1] = addrs[i] } // tree should be empty now t.testRemoveAddrs(tree, addrs, func(addrStr string) *ipaddr.Address { return t.createMACAddress(addrStr).GetAddress().ToAddressBase() }) t.incrementTestCount() } func (t trieTesterGeneric) testRemoveAddrs(tree *AddressTrie, addrs []string, converter func(addrStr string) *ipaddr.Address) { count := 0 var list []*ipaddr.Address dupChecker := make(map[AddressKey]struct{}) for _, str := range addrs { addr := converter(str) if addr != nil { key := addr.ToKey() if _, exists := dupChecker[key]; !exists { dupChecker[key] = struct{}{} list = append(list, addr) count++ tree.Add(addr) } } } t.testRemoveAddrsConverted(tree, count, list) } func (t trieTesterGeneric) testRemoveAddrsConverted(tree *AddressTrie, count int, addrs []*ipaddr.Address) { tree2 := tree.Clone() tree3 := tree2.Clone() tree4 := tree2.Clone() tree5 := tree4.Clone() tree5.Clear() tree5.AddTrie(tree4.GetRoot()) nodeSize4 := tree4.NodeSize() if tree4.Size() != count { t.addFailure(newTrieFailure("trie size not right, got "+strconv.Itoa(tree4.Size())+" instead of expected "+strconv.Itoa(count), tree4)) } tree4.Clear() if tree4.Size() != 0 { t.addFailure(newTrieFailure("trie size not zero, got "+strconv.Itoa(tree4.Size())+" after clearing trie", tree4)) } if tree4.NodeSize() != 1 { if tree4.NodeSize() != 0 || tree4.GetRoot() != nil { t.addFailure(newTrieFailure("node size not 1, got "+strconv.Itoa(tree4.NodeSize())+" after clearing trie", tree4)) } } if tree5.Size() != count { t.addFailure(newTrieFailure("trie size not right, got "+strconv.Itoa(tree5.Size())+" instead of expected "+strconv.Itoa(count), tree5)) } if tree5.NodeSize() != nodeSize4 { t.addFailure(newTrieFailure("trie size not right, got "+strconv.Itoa(tree5.Size())+" instead of expected "+strconv.Itoa(nodeSize4), tree5)) } origSize := tree.Size() origNodeSize := tree.NodeSize() size := origSize nodeSize := origNodeSize iterator := tree.NodeIterator(true) for iterator.HasNext() { node := iterator.Next() iterator.Remove() newSize := tree.Size() if size-1 != newSize { t.addFailure(newTrieFailure("trie size mismatch, expected "+strconv.Itoa(size-1)+" got "+strconv.Itoa(newSize)+" when removing node "+node.String(), tree)) } size = newSize newSize = tree.NodeSize() if newSize > nodeSize { t.addFailure(newTrieFailure("node size mismatch, expected smaller than "+strconv.Itoa(nodeSize)+" got "+strconv.Itoa(newSize)+" when removing node "+node.String(), tree)) } nodeSize = newSize } if tree.Size() != 0 || !tree.IsEmpty() { t.addFailure(newTrieFailure("trie size not zero, got "+strconv.Itoa(tree.Size())+" after clearing trie", tree)) } if tree.NodeSize() != 1 { if tree.NodeSize() != 0 || tree.GetRoot() != nil { t.addFailure(newTrieFailure("node size not 1, got "+strconv.Itoa(tree.NodeSize())+" after clearing trie", tree)) } } size = origSize nodeSize = origNodeSize // now remove by order from array addrs[] for _, addr := range addrs { if addr != nil { tree2.Remove(addr) newSize := tree2.Size() if size-1 != newSize { t.addFailure(newTrieFailure("trie size mismatch, expected "+strconv.Itoa(size-1)+" got "+strconv.Itoa(newSize), tree2)) } size = newSize newSize = tree2.NodeSize() if newSize > nodeSize { t.addFailure(newTrieFailure("node size mismatch, expected smaller than "+strconv.Itoa(nodeSize)+" got "+strconv.Itoa(newSize), tree2)) } nodeSize = newSize } } if tree2.Size() != 0 || !tree2.IsEmpty() { t.addFailure(newTrieFailure("trie size not zero, got "+strconv.Itoa(tree2.Size())+" after clearing trie", tree2)) } if tree2.NodeSize() != 1 { if tree2.NodeSize() != 0 || tree2.GetRoot() != nil { t.addFailure(newTrieFailure("node size not 1, got "+strconv.Itoa(tree2.NodeSize())+" after clearing trie", tree2)) } } // now remove full subtrees at once addressesRemoved := 0 for _, addr := range addrs { if addr != nil { node := tree3.GetAddedNode(addr) nodeCountToBeRemoved := 0 if node != nil { nodeCountToBeRemoved = 1 lowerNode := node.GetLowerSubNode() if lowerNode != nil { nodeCountToBeRemoved += lowerNode.Size() } upperNode := node.GetUpperSubNode() if upperNode != nil { nodeCountToBeRemoved += upperNode.Size() } } preRemovalSize := tree3.Size() tree3.RemoveElementsContainedBy(addr) addressesRemoved++ // we cannot check for smaller tree or node size because many elements might have been already erased newSize := tree3.Size() if newSize != preRemovalSize-nodeCountToBeRemoved { t.addFailure(newTrieFailure("removal size mismatch, expected to remove "+strconv.Itoa(nodeCountToBeRemoved)+" but removed "+strconv.Itoa(preRemovalSize-newSize), tree3)) } if newSize > origSize-addressesRemoved { t.addFailure(newTrieFailure("trie size mismatch, expected smaller than "+strconv.Itoa(origSize-addressesRemoved)+" got "+strconv.Itoa(newSize), tree3)) } newSize = tree3.NodeSize() if newSize > origNodeSize-addressesRemoved && newSize > 1 { t.addFailure(newTrieFailure("node size mismatch, expected smaller than "+strconv.Itoa(origSize-addressesRemoved)+" got "+strconv.Itoa(newSize), tree3)) } } } if tree3.Size() != 0 || !tree3.IsEmpty() { t.addFailure(newTrieFailure("trie size not zero, got "+strconv.Itoa(tree3.Size())+" after clearing trie", tree3)) } if tree3.NodeSize() != 1 { if tree3.NodeSize() != 0 || tree3.GetRoot() != nil { t.addFailure(newTrieFailure("node size not 1, got "+strconv.Itoa(tree3.NodeSize())+" after clearing trie", tree3)) } } t.incrementTestCount() } func (t trieTesterGeneric) createMACSampleTree(tree *AddressTrie, addrs []string) { for _, addr := range addrs { addressStr := t.createMACAddress(addr) address := addressStr.GetAddress() tree.Add(address.ToAddressBase()) } } func (t trieTesterGeneric) createIPv6SampleTree(tree *AddressTrie, addrs []string) { for _, addr := range addrs { addressStr := t.createAddress(addr) if addressStr.IsIPv6() { address := addressStr.GetAddress() tree.Add(address.ToAddressBase()) } } } func (t trieTesterGeneric) createIPv6SampleTreeAddrs(tree *AddressTrie, addrs []*ipaddr.IPAddress) { for _, addr := range addrs { if addr.IsIPv6() { addr = addr.ToSinglePrefixBlockOrAddress() if addr != nil { address := addr.ToIPv6() tree.Add(address.ToAddressBase()) } } } } func (t trieTesterGeneric) createIPv4SampleTreeAddrs(tree *AddressTrie, addrs []*ipaddr.IPAddress) { for _, addr := range addrs { if addr.IsIPv4() { addr = addr.ToSinglePrefixBlockOrAddress() if addr != nil { address := addr.ToIPv4() tree.Add(address.ToAddressBase()) } } } } func (t trieTesterGeneric) createIPv4SampleTree(tree *AddressTrie, addrs []string) { for _, addr := range addrs { addressStr := t.createAddress(addr) if addressStr.IsIPv4() { address := addressStr.GetAddress().ToIPv4() tree.Add(address.ToAddressBase()) } } } func (t trieTesterGeneric) createIPSampleTree(tree *AddressTrie, addrs []string) { for _, addr := range addrs { addressStr := t.createAddress(addr) address := addressStr.GetAddress() tree.Add(address.ToAddressBase()) } } //, T extends Address> func (t trieTesterGeneric) testIterationContainment(tree *AddressTrie) { t.testIterationContainmentTree(tree, func(trie *AddressTrie) ipaddr.CachingTrieIterator[*AddressTrieNode] { return trie.BlockSizeCachingAllNodeIterator() }, false) t.testIterationContainmentTree(tree, func(trie *AddressTrie) ipaddr.CachingTrieIterator[*AddressTrieNode] { return trie.ContainingFirstAllNodeIterator(true) }, false /* added only */) t.testIterationContainmentTree(tree, func(trie *AddressTrie) ipaddr.CachingTrieIterator[*AddressTrieNode] { return trie.ContainingFirstAllNodeIterator(false) }, false /* added only */) t.testIterationContainmentTree(tree, func(trie *AddressTrie) ipaddr.CachingTrieIterator[*AddressTrieNode] { return trie.ContainingFirstIterator(true) }, true /* added only */) t.testIterationContainmentTree(tree, func(trie *AddressTrie) ipaddr.CachingTrieIterator[*AddressTrieNode] { return trie.ContainingFirstIterator(false) }, true /* added only */) } func (t trieTesterGeneric) testIterationContainmentTree( trie *ipaddr.Trie[*ipaddr.Address], iteratorFunc func(addressTrie *AddressTrie) ipaddr.CachingTrieIterator[*AddressTrieNode], addedNodesOnly bool) { iterator := iteratorFunc(trie) for iterator.HasNext() { next := iterator.Next() nextAddr := next.GetKey() var parentPrefix ipaddr.PrefixLen parent := next.GetParent() skipCheck := false if parent != nil { parentPrefix = parent.GetKey().GetPrefixLen() if addedNodesOnly { if !parent.IsAdded() { skipCheck = true } else { parentPrefix = parent.GetKey().GetPrefixLen() } } } cached := iterator.GetCached() if !skipCheck { //panic: interface conversion: tree.C is nil, not *ipaddr.PrefixBitCount if cached == nil { if parentPrefix != nil { t.addFailure(newTrieFailure("mismatched prefix for "+next.String()+", cached is "+fmt.Sprint(iterator.GetCached())+" and expected value is "+parentPrefix.String(), trie)) } } else if !cached.(ipaddr.PrefixLen).Equal(parentPrefix) { t.addFailure(newTrieFailure("mismatched prefix for "+next.String()+", cached is "+fmt.Sprint(iterator.GetCached())+" and expected value is "+parentPrefix.String(), trie)) } } prefLen := nextAddr.GetPrefixLen() iterator.CacheWithLowerSubNode(prefLen) iterator.CacheWithUpperSubNode(prefLen) } t.incrementTestCount() } func (t trieTesterGeneric) testIterate(tree *AddressTrie) { type triePtr = *AddressTrie t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.BlockSizeNodeIterator(true) }, triePtr.Size) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.BlockSizeAllNodeIterator(true) }, triePtr.NodeSize) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.BlockSizeNodeIterator(false) }, triePtr.Size) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.BlockSizeAllNodeIterator(false) }, triePtr.NodeSize) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.BlockSizeCachingAllNodeIterator() }, triePtr.NodeSize) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.NodeIterator(true) }, triePtr.Size) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.AllNodeIterator(true) }, triePtr.NodeSize) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.NodeIterator(false) }, triePtr.Size) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.AllNodeIterator(false) }, triePtr.NodeSize) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.ContainedFirstIterator(true) }, triePtr.Size) t.testIterator(tree, func(trie *AddressTrie) ipaddr.Iterator[*AddressTrieNode] { return trie.ContainedFirstAllNodeIterator(true) }, triePtr.NodeSize) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.ContainedFirstIterator(false) }, triePtr.Size) t.testIterator(tree, func(trie *AddressTrie) ipaddr.Iterator[*AddressTrieNode] { return trie.ContainedFirstAllNodeIterator(false) }, triePtr.NodeSize) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.ContainingFirstIterator(true) }, triePtr.Size) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.ContainingFirstAllNodeIterator(true) }, triePtr.NodeSize) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.ContainingFirstIterator(false) }, triePtr.Size) t.testIteratorRem(tree, func(trie *AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode] { return trie.ContainingFirstAllNodeIterator(false) }, triePtr.NodeSize) t.testIterationContainment(tree) t.incrementTestCount() } func (t trieTesterGeneric) testIteratorRem( trie *AddressTrie, iteratorFunc func(*AddressTrie) ipaddr.IteratorWithRemove[*AddressTrieNode], countFunc func(*AddressTrie) int) { // iterate the tree, confirm the size by counting // clone the trie, iterate again, but remove each time, confirm the size // confirm trie is empty at the end if trie.Size() > 0 { clonedTrie := trie.Clone() node := clonedTrie.FirstNode() toAdd := node.GetKey() node.Remove() modIterator := iteratorFunc(clonedTrie) mod := clonedTrie.Size() / 2 i := 0 shouldThrow := false func() { defer func() { if r := recover(); r != nil { if !shouldThrow { t.addFailure(newTrieFailure("unexpected throw ", clonedTrie)) } } }() for modIterator.HasNext() { i++ if i == mod { shouldThrow = true clonedTrie.Add(toAdd) } modIterator.Next() if shouldThrow { t.addFailure(newTrieFailure("expected panic ", clonedTrie)) break } } }() } firstTime := true for { expectedSize := countFunc(trie) actualSize := 0 set := make(map[AddressKey]struct{}) iterator := iteratorFunc(trie) for iterator.HasNext() { next := iterator.Next() nextAddr := next.GetKey() set[nextAddr.ToKey()] = struct{}{} actualSize++ if !firstTime { func() { defer func() { if r := recover(); r != nil { t.addFailure(newTrieFailure("removal "+next.String()+" should be supported", trie)) } }() iterator.Remove() if trie.Contains(nextAddr) { t.addFailure(newTrieFailure("after removal "+next.String()+" still in trie ", trie)) } }() } else { if next.IsAdded() { if !trie.Contains(nextAddr) { t.addFailure(newTrieFailure("after iteration "+next.String()+" not in trie ", trie)) } else if trie.GetAddedNode(nextAddr) == nil { t.addFailure(newTrieFailure("after iteration address node for "+nextAddr.String()+" not in trie ", trie)) } } else { if trie.Contains(nextAddr) { t.addFailure(newTrieFailure("non-added node "+next.String()+" in trie ", trie)) } else if trie.GetNode(nextAddr) == nil { t.addFailure(newTrieFailure("after iteration address node for "+nextAddr.String()+" not in trie ", trie)) } else if trie.GetAddedNode(nextAddr) != nil { t.addFailure(newTrieFailure("after iteration non-added node for "+nextAddr.String()+" added in trie ", trie)) } } } } if len(set) != expectedSize { t.addFailure(newTrieFailure("set count was "+strconv.Itoa(len(set))+" instead of expected "+strconv.Itoa(expectedSize), trie)) } else if actualSize != expectedSize { t.addFailure(newTrieFailure("count was "+strconv.Itoa(actualSize)+" instead of expected "+strconv.Itoa(expectedSize), trie)) } trie = trie.Clone() if !firstTime { break } firstTime = false } if !trie.IsEmpty() { t.addFailure(newTrieFailure("trie not empty, size "+strconv.Itoa(trie.Size())+" after removing everything", trie)) } else if trie.NodeSize() > 1 { t.addFailure(newTrieFailure("trie node size not 1, "+strconv.Itoa(trie.NodeSize())+" after removing everything", trie)) } else if trie.Size() > 0 { t.addFailure(newTrieFailure("trie size not 0, "+strconv.Itoa(trie.Size())+" after removing everything", trie)) } t.incrementTestCount() } func (t trieTesterGeneric) testIterator( trie *AddressTrie, iteratorFunc func(*AddressTrie) ipaddr.Iterator[*AddressTrieNode], countFunc func(*AddressTrie) int) { // iterate the tree, confirm the size by counting // clone the trie, iterate again, but remove each time, confirm the size // confirm trie is empty at the end if trie.Size() > 0 { clonedTrie := trie.Clone() node := clonedTrie.FirstNode() toAdd := node.GetKey() node.Remove() modIterator := iteratorFunc(clonedTrie) mod := clonedTrie.Size() / 2 i := 0 shouldThrow := false func() { defer func() { if r := recover(); r != nil { if !shouldThrow { t.addFailure(newTrieFailure("unexpected throw ", clonedTrie)) } } }() for modIterator.HasNext() { i++ if i == mod { shouldThrow = true clonedTrie.Add(toAdd) } modIterator.Next() if shouldThrow { t.addFailure(newTrieFailure("expected panic ", clonedTrie)) break } } }() } expectedSize := countFunc(trie) actualSize := 0 set := make(map[AddressKey]struct{}) iterator := iteratorFunc(trie) for iterator.HasNext() { next := iterator.Next() nextAddr := next.GetKey() set[nextAddr.ToKey()] = struct{}{} actualSize++ if next.IsAdded() { if !trie.Contains(nextAddr) { t.addFailure(newTrieFailure("after iteration "+next.String()+" not in trie ", trie)) } else if trie.GetAddedNode(nextAddr) == nil { t.addFailure(newTrieFailure("after iteration address node for "+nextAddr.String()+" not in trie ", trie)) } } else { if trie.Contains(nextAddr) { t.addFailure(newTrieFailure("non-added node "+next.String()+" in trie ", trie)) } else if trie.GetNode(nextAddr) == nil { t.addFailure(newTrieFailure("after iteration address node for "+nextAddr.String()+" not in trie ", trie)) } else if trie.GetAddedNode(nextAddr) != nil { t.addFailure(newTrieFailure("after iteration non-added node for "+nextAddr.String()+" added in trie ", trie)) } } } if len(set) != expectedSize { t.addFailure(newTrieFailure("set count was "+strconv.Itoa(len(set))+" instead of expected "+strconv.Itoa(expectedSize), trie)) } else if actualSize != expectedSize { t.addFailure(newTrieFailure("count was "+strconv.Itoa(actualSize)+" instead of expected "+strconv.Itoa(expectedSize), trie)) } t.incrementTestCount() } func (t trieTesterGeneric) testContains(trie *AddressTrie) { if trie.Size() > 0 { last := trie.GetAddedNode(trie.LastAddedNode().GetKey()) if !trie.Contains(last.GetKey()) { t.addFailure(newTrieFailure("failure "+last.String()+" not in trie ", trie)) } last.Remove() if trie.Contains(last.GetKey()) { t.addFailure(newTrieFailure("failure "+last.String()+" is in trie ", trie)) } trie.Add(last.GetKey()) if !trie.Contains(last.GetKey()) { t.addFailure(newTrieFailure("failure "+last.String()+" not in trie ", trie)) } } iterator := trie.AllNodeIterator(true) for iterator.HasNext() { next := iterator.Next() nextAddr := next.GetKey() if next.IsAdded() { if !trie.Contains(nextAddr) { t.addFailure(newTrieFailure("after iteration "+next.String()+" not in trie ", trie)) } else if trie.GetAddedNode(nextAddr) == nil { t.addFailure(newTrieFailure("after iteration address node for "+nextAddr.String()+" not in trie ", trie)) } } else { if trie.Contains(nextAddr) { t.addFailure(newTrieFailure("non-added node "+next.String()+" in trie ", trie)) } else if trie.GetNode(nextAddr) == nil { t.addFailure(newTrieFailure("after iteration address node for "+nextAddr.String()+" not in trie ", trie)) } else if trie.GetAddedNode(nextAddr) != nil { t.addFailure(newTrieFailure("after iteration non-added node for "+nextAddr.String()+" added in trie ", trie)) } } parent := next.GetParent() var parentPrefLen ipaddr.PrefixLen if parent != nil { parentKey := parent.GetKey() parentPrefLen = parentKey.GetPrefixLen() } else { parentPrefLen = ipaddr.ToPrefixLen(0) } prefLen := nextAddr.GetPrefixLen() var halfwayAddr *ipaddr.Address var halfway ipaddr.BitCount if prefLen == nil { prefLen = ipaddr.ToPrefixLen(nextAddr.GetBitCount()) } halfway = parentPrefLen.Len() + ((prefLen.Len() - parentPrefLen.Len()) >> 1) halfwayAddr = nextAddr.SetPrefixLen(halfway).ToPrefixBlock() halfwayIsParent := parent != nil && parentPrefLen.Len() == halfway containedBy := trie.ElementsContainedBy(halfwayAddr) if halfwayIsParent { if containedBy != parent { t.addFailure(newTrieFailure("containedBy is "+containedBy.String()+" for address "+halfwayAddr.String()+" instead of expected "+parent.String(), trie)) } } else { if containedBy != next { t.addFailure(newTrieFailure("containedBy is "+containedBy.String()+" for address "+halfwayAddr.String()+" instead of expected "+next.String(), trie)) } } lpm := trie.LongestPrefixMatch(halfwayAddr) //if ok != (lpm != nil) { // t.addFailure(newTrieFailure("expecting a match for "+halfwayAddr.String(), trie)) //} smallestContaining := trie.LongestPrefixMatchNode(halfwayAddr) containing := trie.ElementsContaining(halfwayAddr) elementsContains := trie.ElementContains(halfwayAddr) addedParent := parent for addedParent != nil && !addedParent.IsAdded() { addedParent = addedParent.GetParent() } if addedParent == nil && prefLen.Len() == 0 && next.IsAdded() { addedParent = next } if addedParent == nil { //fmt.Printf("empty containing is %s\n", containing) if (containing != nil && containing.Count() > 0) || lpm != nil { t.addFailure(newTrieFailure("containing is "+containing.String()+" for address "+halfwayAddr.String()+" instead of expected nil", trie)) } else if elementsContains { t.addFailure(newTrieFailure("containing is true for address "+halfwayAddr.String()+" instead of expected false", trie)) } } else { var lastContaining *ipaddr.ContainmentPathNode[*ipaddr.Address] //fmt.Printf("containing is %s\n", containing) if containing.Count() > 0 { lastContaining = containing.ShortestPrefixMatch() for { if next := lastContaining.Next(); next == nil { break } else { lastContaining = next } } } if lastContaining == nil || !lastContaining.GetKey().Equal(addedParent.GetKey()) { t.addFailure(newTrieFailure("containing ends with "+lastContaining.String()+" for address "+halfwayAddr.String()+" instead of expected "+addedParent.String(), trie)) } else if !lastContaining.GetKey().Equal(smallestContaining.GetKey()) { t.addFailure(newTrieFailure("containing ends with "+lastContaining.String()+" for address "+halfwayAddr.String()+" instead of expected smallest containing "+smallestContaining.String(), trie)) } else if lastContaining.GetKey() != lpm { t.addFailure(newTrieFailure("containing ends with addr "+lastContaining.GetKey().String()+" for address "+halfwayAddr.String()+" instead of expected "+lpm.String(), trie)) } if !elementsContains { t.addFailure(newTrieFailure("containing is false for address "+halfwayAddr.String()+" instead of expected true", trie)) } } } t.incrementTestCount() } func (t trieTesterGeneric) testEdges(trie *AddressTrie, addrs []*ipaddr.Address) { trie2 := trie.Clone() for _, addr := range addrs { trie.Add(addr) } i := 0 ordered := make([]*AddressTrieNode, 0, len(addrs)) iter := trie.Iterator() for addr := iter.Next(); addr != nil; addr = iter.Next() { if i%2 == 0 { trie2.Add(addr) } i++ ordered = append(ordered, trie.GetAddedNode(addr)) } i = 0 nodeIter := trie.NodeIterator(true) treeSize := trie.Size() iter = trie.Iterator() for addr := iter.Next(); addr != nil; addr = iter.Next() { node := nodeIter.Next() floor := trie2.FloorAddedNode(addr) lower := trie2.LowerAddedNode(addr) ceiling := trie2.CeilingAddedNode(addr) higher := trie2.HigherAddedNode(addr) if i == 0 { if node != trie.FirstAddedNode() { t.addFailure(newTrieFailure("wrong first, got "+trie.FirstAddedNode().String()+" not "+node.String(), trie)) } } else if i == treeSize-1 { if node != trie.LastAddedNode() { t.addFailure(newTrieFailure("wrong last, got "+trie.LastAddedNode().String()+" not "+node.String(), trie)) } } if i%2 == 0 { // in the second trie if !floor.Equal(node) { t.addFailure(newTrieFailure("wrong floor, got "+floor.String()+" not "+node.String(), trie)) } else if !ceiling.Equal(node) { t.addFailure(newTrieFailure("wrong ceiling, got "+ceiling.String()+" not "+node.String(), trie)) } else { if i > 0 { expected := ordered[i-2] if !lower.Equal(expected) { t.addFailure(newTrieFailure("wrong lower, got "+lower.String()+" not "+expected.String(), trie)) } } else { if lower != nil { t.addFailure(newTrieFailure("wrong lower, got "+lower.String()+" not nil", trie)) } } if i < len(ordered)-2 { expected := ordered[i+2] if !higher.Equal(expected) { t.addFailure(newTrieFailure("wrong higher, got "+higher.String()+" not "+expected.String(), trie)) } } else { if higher != nil { t.addFailure(newTrieFailure("wrong higher, got "+higher.String()+" not nil", trie)) } } } } else { // not in the second trie if i > 0 { expected := ordered[i-1] if !lower.Equal(expected) { t.addFailure(newTrieFailure("wrong lower, got "+lower.String()+" not "+expected.String(), trie)) } else if !lower.Equal(floor) { t.addFailure(newTrieFailure("wrong floor, got "+floor.String()+" not "+expected.String(), trie)) } } else { if lower != nil { t.addFailure(newTrieFailure("wrong lower, got "+lower.String()+" not nil", trie)) } else if floor != nil { t.addFailure(newTrieFailure("wrong floor, got "+floor.String()+" not nil", trie)) } } if i < len(ordered)-1 { expected := ordered[i+1] if !higher.Equal(expected) { t.addFailure(newTrieFailure("wrong higher, got "+higher.String()+" not "+expected.String(), trie)) } else if !higher.Equal(ceiling) { t.addFailure(newTrieFailure("wrong ceiling, got "+ceiling.String()+" not "+expected.String(), trie)) } } else { if higher != nil { t.addFailure(newTrieFailure("wrong higher, got "+higher.String()+" not nil", trie)) } else if ceiling != nil { t.addFailure(newTrieFailure("wrong ceiling, got "+ceiling.String()+" not nil", trie)) } } } i++ } t.incrementTestCount() } // pass in an empty trie func (t trieTesterGeneric) testAdd(trie *AddressTrie, addrs []*ipaddr.Address) { trie2 := trie.Clone() trie3 := trie.Clone() trie4 := trie.Clone() k := 0 for _, addr := range addrs { k++ if k%2 == 0 { added := trie.Add(addr) if !added { t.addFailure(newTrieFailure("trie empty, adding "+addr.String()+" should succeed ", trie)) } } else { node := trie.AddNode(addr) if node == nil || !node.GetKey().Equal(addr) { t.addFailure(newTrieFailure("trie empty, adding "+addr.String()+" should succeed ", trie)) } } } if trie.Size() != len(addrs) { t.addFailure(newTrieFailure("trie size incorrect: "+strconv.Itoa(trie.Size())+", not "+strconv.Itoa(len(addrs)), trie)) } node := trie.GetRoot() i := 0 for ; i < len(addrs)/2; i++ { trie2.Add(addrs[i]) } for ; i < len(addrs); i++ { trie3.Add(addrs[i]) } trie2.AddTrie(node) trie3.AddTrie(node) trie4.AddTrie(node) if !trie.Equal(trie2) { t.addFailure(newTrieFailure("tries not equal: "+trie.String()+" and "+trie2.String(), trie)) } if !trie3.Equal(trie2) { t.addFailure(newTrieFailure("tries not equal: "+trie3.String()+" and "+trie2.String(), trie)) } if !trie3.Equal(trie4) { t.addFailure(newTrieFailure("tries not equal: "+trie3.String()+" and "+trie4.String(), trie)) } t.incrementTestCount() } func (t trieTesterGeneric) testMap(trie *ipaddr.AssociativeTrie[*ipaddr.Address, any], addrs []*ipaddr.Address, valueProducer func(int) any, mapper func(any) any) { // seems we used to use the mapper but no now // put tests trie2 := trie.Clone() trie4 := trie.Clone() for i, addr := range addrs { trie.Put(addr, valueProducer(i)) } for i, addr := range addrs { v, _ := trie.Get(addr) expected := valueProducer(i) if !reflect.DeepEqual(v, expected) { //reflect deep equal //fmt.Println(trie) t.addFailure(newAssocTrieFailure(fmt.Sprintf("got mismatch, got %v, not %v for %v", v, expected, addr), trie)) //v, _ = trie.Get(addr) } } // all trie2 from now on trie2.PutTrie(trie.GetRoot()) for i, addr := range addrs { v, _ := trie2.Get(addr) expected := valueProducer(i) if !reflect.DeepEqual(v, expected) { t.addFailure(newAssocTrieFailure(fmt.Sprintf("got mismatch, got %v, not %v", v, expected), trie2)) } if i%2 == 0 { trie2.Remove(addr) } } if trie2.Size() != (len(addrs) >> 1) { t.addFailure(newAssocTrieFailure("got size mismatch, got "+strconv.Itoa(trie2.Size())+" not "+strconv.Itoa(len(addrs)>>1), trie2)) } trie2.PutTrie(trie.GetRoot()) for i, addr := range addrs { v, _ := trie2.Get(addr) expected := valueProducer(i) if !reflect.DeepEqual(v, expected) { t.addFailure(newAssocTrieFailure(fmt.Sprintf("get mismatch, got %v, not %v", v, expected), trie2)) } } if trie2.Size() != len(addrs) { t.addFailure(newAssocTrieFailure("got size mismatch, got "+strconv.Itoa(trie2.Size())+" not "+strconv.Itoa(len(addrs)), trie2)) } for i, addr := range addrs { if i%2 == 0 { b := trie2.Remove(addr) if !b { t.addFailure(newAssocTrieFailure("remove should have succeeded", trie2)) } b = trie2.Remove(addr) if b { t.addFailure(newAssocTrieFailure("remove should not have succeeded", trie2)) } } } for i, addr := range addrs { _, exists := trie2.Put(addr, valueProducer(i)) if exists != (i%2 == 0) { t.addFailure(newAssocTrieFailure("putNew mismatch", trie2)) } } if trie2.Size() != len(addrs) { t.addFailure(newAssocTrieFailure("got size mismatch, got "+strconv.Itoa(trie.Size())+" not "+strconv.Itoa(len(addrs)), trie2)) } for i, addr := range addrs { _, res := trie2.Put(addr, valueProducer(i+1)) if res { t.addFailure(newAssocTrieFailure("putNew mismatch", trie2)) } } if trie2.Size() != len(addrs) { t.addFailure(newAssocTrieFailure("got size mismatch, got "+strconv.Itoa(trie.Size())+" not "+strconv.Itoa(len(addrs)), trie2)) } for i, addr := range addrs { v, _ := trie2.Get(addr) expected := valueProducer(i + 1) if !reflect.DeepEqual(v, expected) { t.addFailure(newAssocTrieFailure(fmt.Sprintf("get mismatch, got %v, not %v", v, expected), trie)) } } for i, addr := range addrs { v, _ := trie2.Put(addr, valueProducer(i)) expected := valueProducer(i + 1) if !reflect.DeepEqual(v, expected) { t.addFailure(newAssocTrieFailure(fmt.Sprintf("get mismatch, got %v, not %v", v, expected), trie)) } v, _ = trie2.Get(addr) expected = valueProducer(i) if !reflect.DeepEqual(v, expected) { t.addFailure(newAssocTrieFailure(fmt.Sprintf("get mismatch, got %v, not %v", v, expected), trie)) } } k := 0 for i, addr := range addrs { if i%2 == 0 { b := trie2.Remove(addr) if !b { t.addFailure(newAssocTrieFailure("remove should have succeeded", trie2)) } } // the reason for the (i % 8 == 1) is that the existing value is already valueProducer.apply(i), // so half the time we are re-adding the existing value, // half the time we are changing to a new value var value any if i%4 == 1 { if i%8 == 1 { value = valueProducer(i + 1) } else { value = valueProducer(i) } } node := trie2.Remap(addr, func(val any, found bool) (any, bool) { if val == nil { if found { t.addFailure(newAssocTrieFailure("got unexpected vals", trie2)) } return valueProducer(0), true } else { if !found { t.addFailure(newAssocTrieFailure("got unexpected vals", trie2)) } return value, value != nil } }) if node == nil || !node.GetKey().Equal(addr) { t.addFailure(newAssocTrieFailure("got unexpected return, got "+node.String(), trie2)) } if i%2 != 0 && value == nil { k++ } } if trie2.Size()+k != len(addrs) { t.addFailure(newAssocTrieFailure("got size mismatch, got "+strconv.Itoa(trie2.Size())+" not "+strconv.Itoa(len(addrs)-k), trie2)) } for i, addr := range addrs { v, _ := trie2.Get(addr) var expected any if i%2 == 0 { expected = valueProducer(0) } else if i%4 == 1 { if i%8 == 1 { expected = valueProducer(i + 1) } else { expected = valueProducer(i) } } if !reflect.DeepEqual(v, expected) { t.addFailure(newAssocTrieFailure(fmt.Sprintf("got mismatch, got %v, not %v", v, expected), trie)) } } for _, addr := range addrs { trie2.RemapIfAbsent(addr, func() any { return valueProducer(1) }) // false } if trie2.Size() != len(addrs) { t.addFailure(newAssocTrieFailure("got size mismatch, got "+strconv.Itoa(trie2.Size())+" not "+strconv.Itoa(len(addrs)), trie2)) } for i, addr := range addrs { v, _ := trie2.Get(addr) var expected any if i%2 == 0 { expected = valueProducer(0) } else if i%4 == 1 { if i%8 == 1 { expected = valueProducer(i + 1) } else { expected = valueProducer(i) } } else { // remapped expected = valueProducer(1) } if !reflect.DeepEqual(v, expected) { t.addFailure(newAssocTrieFailure(fmt.Sprintf("get mismatch, got %v, not %v", v, expected), trie)) } } if trie2.Size() != len(addrs) { t.addFailure(newAssocTrieFailure("got size mismatch, got "+strconv.Itoa(trie2.Size())+" not "+strconv.Itoa(len(addrs)), trie2)) } for i, addr := range addrs { if i%2 == 0 { trie2.GetNode(addr).Remove() } } if trie2.Size() != (len(addrs) >> 1) { t.addFailure(newAssocTrieFailure("got size mismatch, got "+strconv.Itoa(trie2.Size())+" not "+strconv.Itoa(len(addrs)>>1), trie2)) } //for i, addr := range addrs { // node := trie2.RemapIfAbsent(addr, func() (any, bool) { // return nil, false // }) // false // if (node == nil) != (i%2 == 0) { // t.addFailure(newAssocTrieFailure("got unexpected return, got "+node.String(), trie2)) // } //} //if trie2.Size() != (len(addrs) >> 1) { // t.addFailure(newAssocTrieFailure("got size mismatch, got "+strconv.Itoa(trie2.Size())+" not "+strconv.Itoa(len(addrs)>>1), trie2)) //} for _, addr := range addrs { node := trie2.RemapIfAbsent(addr, func() any { return nil }) // true if node != nil && !node.GetKey().Equal(addr) { t.addFailure(newAssocTrieFailure("got unexpected return, got "+node.String(), trie2)) } //if node == nil { // trie2.Put(addr, nil) //} } if trie2.Size() != len(addrs) { t.addFailure(newAssocTrieFailure("got size mismatch, got "+strconv.Itoa(trie2.Size())+" not "+strconv.Itoa(len(addrs)), trie2)) } for i, addr := range addrs { v, _ := trie2.Get(addr) var expected any if i%2 == 0 { } else if i%4 == 1 { if i%8 == 1 { expected = valueProducer(i + 1) } else { expected = valueProducer(i) } } else { expected = valueProducer(1) } if !reflect.DeepEqual(v, expected) { t.addFailure(newAssocTrieFailure(fmt.Sprintf("get mismatch, got %v, not %v", v, expected), trie)) } } var firstNode *ipaddr.AssociativeTrieNode[*ipaddr.Address, any] func() { defer func() { if r := recover(); r != nil { // r is what was passed to panic v := firstNode.GetValue() // firstNode is the node with the value we were remapping _, b := trie2.Put(firstNode.GetKey(), v) if b { t.addFailure(newAssocTrieFailure("should have added", trie2)) } } }() // remove the first addr so we should panic for _, addr := range addrs { node := trie2.GetAddedNode(addr) firstNode = node // remove the node so we should panic on the remap trie2.Remove(addr) trie2.RemapIfAbsent(addr, func() any { trie2.Add(addr) return valueProducer(1) }) // false t.addFailure(newAssocTrieFailure("should have paniced", trie2)) } }() func() { defer func() { if r := recover(); r != nil { v := firstNode.GetValue() _, b := trie2.Put(firstNode.GetKey(), v) if !b { t.addFailure(newAssocTrieFailure("should have added", trie2)) } } }() for _, addr := range addrs { node := trie2.GetAddedNode(addr) firstNode = node trie2.Remap(addr, func(any, bool) (any, bool) { node.Remove() return valueProducer(1), true }) t.addFailure(newAssocTrieFailure("should have paniced", trie2)) } }() // all trie4 from now on for i, addr := range addrs { node := trie4.PutNode(addr, valueProducer(i)) v := node.GetValue() if !reflect.DeepEqual(v, valueProducer(i)) { t.addFailure(newAssocTrieFailure(fmt.Sprintf("got putNode mismatch, got %v not %v", node.GetValue(), valueProducer(i)), trie)) } } if trie4.Size() != len(addrs) { t.addFailure(newAssocTrieFailure("got size mismatch, got "+strconv.Itoa(trie4.Size())+" not "+strconv.Itoa(len(addrs)), trie4)) } // end put tests } ipaddress-go-1.5.4/ipaddr/types.go000066400000000000000000000254141440250641600170500ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math" "math/big" "strconv" ) type boolSetting struct { isSet, val bool } var ( falseVal = false trueVal = true ) // A PrefixLen indicates the length of the prefix for an address, section, division grouping, segment, or division. // The zero value, which is nil, indicates that there is no prefix length. type PrefixLen = *PrefixBitCount // ToPrefixLen converts the given int to a prefix length func ToPrefixLen(i int) PrefixLen { res := PrefixBitCount(i) return &res } // BitCount represents a count of bits in an address, section, grouping, segment, or division. // Using signed integers allows for easier arithmetic, avoiding bugs. // However, all methods adjust bit counts to match address size, // so negative bit counts or bit counts larger than address size are meaningless. type BitCount = int // using signed integers allows for easier arithmetic const maxBitCountInternal, minBitCountInternal = math.MaxUint8, 0 // A PrefixBitCount is the count of bits in a non-nil PrefixLen. // For arithmetic, you may wish to use the signed integer type BitCount instead, which you can get from a PrefixLen using the Len method. type PrefixBitCount uint8 // Len returns the length of the prefix. If the receiver is nil, representing the absence of a prefix length, returns 0. // It will also return 0 if the receiver is a prefix with length of 0. To distinguish the two, compare the receiver with nil. func (prefixBitCount *PrefixBitCount) Len() BitCount { if prefixBitCount == nil { return 0 } return prefixBitCount.bitCount() } // IsNil returns true if this is nil, meaning it represents having no prefix length, or the absence of a prefix length func (prefixBitCount *PrefixBitCount) IsNil() bool { return prefixBitCount == nil } func (prefixBitCount *PrefixBitCount) bitCount() BitCount { return BitCount(*prefixBitCount) } func (prefixBitCount *PrefixBitCount) copy() PrefixLen { if prefixBitCount == nil { return nil } res := *prefixBitCount return &res } // Equal compares two PrefixLen values for equality. This method is intended for the PrefixLen type. BitCount values should be compared with the == operator. func (prefixBitCount *PrefixBitCount) Equal(other PrefixLen) bool { if prefixBitCount == nil { return other == nil } return other != nil && prefixBitCount.bitCount() == other.bitCount() } // Matches compares a PrefixLen value with a bit count func (prefixBitCount *PrefixBitCount) Matches(other BitCount) bool { return prefixBitCount != nil && prefixBitCount.bitCount() == other } // Compare compares PrefixLen values, returning -1, 0, or 1 if this prefix length is less than, equal to, or greater than the given prefix length. // This method is intended for the PrefixLen type. BitCount values should be compared with ==, >, <, >= and <= operators. func (prefixBitCount *PrefixBitCount) Compare(other PrefixLen) int { if prefixBitCount == nil { if other == nil { return 0 } return 1 } else if other == nil { return -1 } return prefixBitCount.bitCount() - other.bitCount() } // String returns the bit count as a base-10 positive integer string, or "" if the receiver is a nil pointer. func (prefixBitCount *PrefixBitCount) String() string { if prefixBitCount == nil { return nilString() } return strconv.Itoa(prefixBitCount.bitCount()) } // HostBitCount is the count of bits in a host. // For arithmetic, you may wish to use the signed integer type BitCount instead, which you can get from a HostBitCount using the Len method. type HostBitCount uint8 // BitsForCount returns the number of bits required outside the prefix length // for a single prefix block to span at least as many addresses as the given count. // Mathematically, it is the ceiling of the base 2 logarithm of the given count. // A count of zero returns nil. func BitsForCount(count uint64) (result *HostBitCount) { if count != 0 { var res HostBitCount countMinusOne := count - 1 if (countMinusOne & (0xfff0000000000000)) != 0 { // conversion to float64 will fail count = (countMinusOne >> 53) + 1 res = 53 } res += HostBitCount(math.Ilogb(float64((count << 1) - 1))) return &res } return nil } // BlockSize is the reverse of BitsForCount, giving the total number of values when ranging across the number of host bits. // The nil *HostBitCount returns 0. func (hostBitCount *HostBitCount) BlockSize() *big.Int { if hostBitCount == nil { return bigZero() } return new(big.Int).Lsh(bigOneConst(), uint(*hostBitCount)) } // Len returns the length of the host. If the receiver is nil, representing the absence of a host length, returns 0. // It will also return 0 if the receiver has a host length of 0. To distinguish the two, compare the receiver with nil. func (hostBitCount *HostBitCount) Len() BitCount { if hostBitCount == nil { return 0 } return BitCount(*hostBitCount) } // String returns the bit count as a base-10 positive integer string, or "" if the receiver is a nil pointer. func (hostBitCount *HostBitCount) String() string { if hostBitCount == nil { return nilString() } return strconv.Itoa(hostBitCount.Len()) } // IsNil returns true if this is nil, meaning it represents having no identified host length. func (hostBitCount *HostBitCount) IsNil() bool { return hostBitCount == nil } var cachedPrefixBitCounts, cachedPrefixLens = initPrefLens() func initPrefLens() ([]PrefixBitCount, []PrefixLen) { cachedPrefBitcounts := make([]PrefixBitCount, maxBitCountInternal) cachedPrefLens := make([]PrefixLen, maxBitCountInternal) for i := 0; i <= IPv6BitCount; i++ { cachedPrefBitcounts[i] = PrefixBitCount(i) cachedPrefLens[i] = &cachedPrefBitcounts[i] } return cachedPrefBitcounts, cachedPrefLens } func cacheBitCount(i BitCount) PrefixLen { if i < minBitCountInternal { i = minBitCountInternal } if i < len(cachedPrefixBitCounts) { return &cachedPrefixBitCounts[i] } if i > maxBitCountInternal { i = maxBitCountInternal } res := PrefixBitCount(i) return &res } func cachePrefix(i BitCount) *PrefixLen { if i < minBitCountInternal { i = minBitCountInternal } if i < len(cachedPrefixLens) { return &cachedPrefixLens[i] } if i > maxBitCountInternal { i = maxBitCountInternal } val := PrefixBitCount(i) res := &val return &res } func cachePrefixLen(external PrefixLen) PrefixLen { if external == nil { return nil } return cacheBitCount(external.bitCount()) } var p PrefixLen func cacheNilPrefix() *PrefixLen { return &p } const maxPortNumInternal, minPortNumInternal = math.MaxUint16, 0 // Port represents the port of a UDP or TCP address. A nil value indicates no port. type Port = *PortNum type PortInt = int // using signed integers allows for easier arithmetic // PortNum is the port number for a non-nil Port. For arithmetic, you might wish to use the signed integer type PortInt instead. type PortNum uint16 func (portNum *PortNum) portNum() PortInt { return PortInt(*portNum) } func (portNum *PortNum) copy() Port { if portNum == nil { return nil } res := *portNum return &res } // Num converts to a PortPortIntNum, returning 0 if the receiver is nil. func (portNum *PortNum) Num() PortInt { if portNum == nil { return 0 } return PortInt(*portNum) } // Port dereferences this PortNum, while returning 0 if the receiver is nil. func (portNum *PortNum) Port() PortNum { if portNum == nil { return 0 } return *portNum } // Equal compares two Port values for equality. func (portNum *PortNum) Equal(other Port) bool { if portNum == nil { return other == nil } return other != nil && portNum.portNum() == other.portNum() } // Matches compares a Port value with a port number. func (portNum *PortNum) Matches(other PortInt) bool { return portNum != nil && portNum.portNum() == other } // Compare compares PrefixLen values, returning -1, 0, or 1 if the receiver is less than, equal to, or greater than the argument. func (portNum *PortNum) Compare(other Port) int { if portNum == nil { if other == nil { return 0 } return -1 } else if other == nil { return 1 } return portNum.portNum() - other.portNum() } // String returns the bit count as a base-10 positive integer string, or "" if the receiver is a nil pointer. func (portNum *PortNum) String() string { if portNum == nil { return nilString() } return strconv.Itoa(portNum.portNum()) } func cachePorts(i PortInt) Port { if i < minPortNumInternal { i = minPortNumInternal } else if i > maxPortNumInternal { i = maxPortNumInternal } res := PortNum(i) return &res } func bigOne() *big.Int { return big.NewInt(1) } var one = bigOne() func bigOneConst() *big.Int { return one } func bigZero() *big.Int { return new(big.Int) } var zero = bigZero() func bigZeroConst() *big.Int { return zero } var minusOne = big.NewInt(-1) func bigMinusOneConst() *big.Int { return minusOne } func bigSixteen() *big.Int { return big.NewInt(16) } func bigIsZero(val *BigDivInt) bool { return len(val.Bits()) == 0 // slightly faster than div.value.BitLen() == 0 } func bigIsOne(val *BigDivInt) bool { return bigAbsIsOne(val) && val.Sign() > 0 } func bigAbsIsOne(val *BigDivInt) bool { bits := val.Bits() return len(bits) == 1 && bits[0] == 1 } func checkSubnet(item BitItem, prefixLength BitCount) BitCount { return checkBitCount(prefixLength, item.GetBitCount()) } func checkDiv(div DivisionType, prefixLength BitCount) BitCount { return checkBitCount(prefixLength, div.GetBitCount()) } func checkBitCount(prefixLength, max BitCount) BitCount { if prefixLength > max { return max } else if prefixLength < 0 { return 0 } return prefixLength } func checkPrefLen(prefixLength PrefixLen, max BitCount) PrefixLen { if prefixLength != nil { prefLen := prefixLength.bitCount() if prefLen > max { return cacheBitCount(max) } else if prefLen < 0 { return cacheBitCount(0) } } return prefixLength } // wrapperIterator notifies the iterator to the right when wrapperIterator reaches its final value type wrappedIterator struct { iterator Iterator[*IPAddressSegment] finalValue []bool indexi int } func (wrapped *wrappedIterator) HasNext() bool { return wrapped.iterator.HasNext() } func (wrapped *wrappedIterator) Next() *IPAddressSegment { iter := wrapped.iterator next := iter.Next() if !iter.HasNext() { wrapped.finalValue[wrapped.indexi+1] = true } return next } ipaddress-go-1.5.4/ipaddr/utils.go000066400000000000000000000145011440250641600170370ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "fmt" "math/big" "sync/atomic" "unsafe" ) func nilString() string { return "" } // nilSection prints a string for sections with a nil division slice or division slice of 0 length. // For division groupings, the division slice string is generated from using the slice, see toString() or defaultFormat() in grouping code. func nilSection() string { return "" } func cloneInts(orig []int) []int { return append(make([]int, 0, len(orig)), orig...) } func cloneDivs(orig []*AddressDivision) []*AddressDivision { return append(make([]*AddressDivision, 0, len(orig)), orig...) } func cloneLargeDivs(orig []*IPAddressLargeDivision) []*IPAddressLargeDivision { return append(make([]*IPAddressLargeDivision, 0, len(orig)), orig...) } func cloneBytes(orig []byte) []byte { return append(make([]byte, 0, len(orig)), orig...) } func fillDivs(orig []*AddressDivision, val *AddressDivision) { for i := range orig { orig[i] = val } } // copies cached into bytes, unless bytes is too small, in which case cached is cloned func getBytesCopy(bytes, cached []byte) []byte { if bytes == nil || len(bytes) < len(cached) { return cloneBytes(cached) } copy(bytes, cached) return bytes[:len(cached)] } // note: only to be used when you already know the total size fits into a long func longCount(section *AddressSection, segCount int) uint64 { result := getLongCount(func(index int) uint64 { return section.GetSegment(index).GetValueCount() }, segCount) return result } func getLongCount(segmentCountProvider func(index int) uint64, segCount int) uint64 { if segCount <= 0 { return 1 } result := segmentCountProvider(0) for i := 1; i < segCount; i++ { result *= segmentCountProvider(i) } return result } // note: only to be used when you already know the total size fits into a long func longPrefixCount(section *AddressSection, prefixLength BitCount) uint64 { bitsPerSegment := section.GetBitsPerSegment() bytesPerSegment := section.GetBytesPerSegment() networkSegmentIndex := getNetworkSegmentIndex(prefixLength, bytesPerSegment, bitsPerSegment) hostSegmentIndex := getHostSegmentIndex(prefixLength, bytesPerSegment, bitsPerSegment) return getLongCount(func(index int) uint64 { if (networkSegmentIndex == hostSegmentIndex) && index == networkSegmentIndex { segmentPrefixLength := getPrefixedSegmentPrefixLength(section.GetBitsPerSegment(), prefixLength, index) return getPrefixValueCount(section.GetSegment(index), segmentPrefixLength.bitCount()) } return section.GetSegment(index).GetValueCount() }, networkSegmentIndex+1) } func mult(currentResult *big.Int, newResult uint64) *big.Int { if currentResult == nil { return bigZero().SetUint64(newResult) } else if newResult == 1 { return currentResult } newBig := bigZero().SetUint64(newResult) return currentResult.Mul(currentResult, newBig) } // only called when isMultiple() is true, so segCount >= 1 func count(segmentCountProvider func(index int) uint64, segCount, safeMultiplies int, safeLimit uint64) *big.Int { if segCount <= 0 { return bigOne() } var result *big.Int i := 0 for { curResult := segmentCountProvider(i) i++ if i == segCount { return mult(result, curResult) } limit := i + safeMultiplies if segCount <= limit { // all multiplies are safe for i < segCount { curResult *= segmentCountProvider(i) i++ } return mult(result, curResult) } // do the safe multiplies which cannot overflow for i < limit { curResult *= segmentCountProvider(i) i++ } // do as many additional multiplies as current result allows for curResult <= safeLimit { curResult *= segmentCountProvider(i) i++ if i == segCount { return mult(result, curResult) } } result = mult(result, curResult) } } func reverseUint8(b uint8) uint8 { x := b x = ((x & 0xaa) >> 1) | ((x & 0x55) << 1) x = ((x & 0xcc) >> 2) | ((x & 0x33) << 2) x = (x >> 4) | (x << 4) return x } func reverseUint16(b uint16) uint16 { x := b x = ((x & 0xaaaa) >> 1) | ((x & 0x5555) << 1) x = ((x & 0xcccc) >> 2) | ((x & 0x3333) << 2) x = ((x & 0xf0f0) >> 4) | ((x & 0x0f0f) << 4) return (x >> 8) | (x << 8) } func reverseUint32(i uint32) uint32 { x := i x = ((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1) x = ((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2) x = ((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4) x = ((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8) return (x >> 16) | (x << 16) } func flagsFromState(state fmt.State, verb rune) string { flags := "# +-0" vals := make([]rune, 0, len(flags)+5) // %, flags, width, '.', precision, verb vals = append(vals, '%') for i := 0; i < len(flags); i++ { b := flags[i] if state.Flag(int(b)) { vals = append(vals, rune(b)) } } width, widthOK := state.Width() precision, precisionOK := state.Precision() if widthOK || precisionOK { var wpv string if widthOK && precisionOK { wpv = fmt.Sprintf("%d.%d%c", width, precision, verb) } else if widthOK { wpv = fmt.Sprintf("%d%c", width, verb) } else { wpv = fmt.Sprintf(".%d%c", precision, verb) } return string(vals) + wpv } vals = append(vals, verb) return string(vals) } func umin(a, b uint) uint { if a < b { return a } return b } func min(a, b int) int { if a < b { return a } return b } func max(a, b int) int { if a > b { return a } return b } func minSegInt(a, b SegInt) SegInt { if a < b { return a } return b } func maxSegInt(a, b SegInt) SegInt { if a > b { return a } return b } // TODO LATER generics: replace all uses of atomicStorePointer with atomic.Pointer, when we move up to 1.19 func atomicLoadPointer(dataLoc *unsafe.Pointer) unsafe.Pointer { return atomic.LoadPointer(dataLoc) } func atomicStorePointer(dataLoc *unsafe.Pointer, val unsafe.Pointer) { atomic.StorePointer(dataLoc, val) } ipaddress-go-1.5.4/ipaddr/validate.go000066400000000000000000005222101440250641600174710ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "math" "math/big" "strings" "sync" "sync/atomic" "unicode" "unsafe" "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) var chars, extendedChars = createChars() func createChars() (chars [int('z') + 1]byte, extendedChars [int('~') + 1]byte) { i := byte(1) for c := '1'; i < 10; i, c = i+1, c+1 { chars[c] = i } for c, c2 := 'a', 'A'; i < 26; i, c, c2 = i+1, c+1, c2+1 { chars[c] = i chars[c2] = i } var extendedDigits = []byte{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '!', '#', '$', '%', '&', '(', ')', '*', '+', '-', ';', '<', '=', '>', '?', '@', '^', '_', '`', '{', '|', '}', '~'} extLen := byte(len(extendedDigits)) for i = 0; i < extLen; i++ { c := extendedDigits[i] extendedChars[c] = i } return } const ( longSize = 64 maxHostLength = 253 maxHostSegments = 127 maxLabelLength = 63 macDoubleSegmentDigitCount = 6 macExtendedDoubleSegmentDigitCount = 10 macSingleSegmentDigitCount = 12 macExtendedSingleSegmentDigitCount = 16 ipv6SingleSegmentDigitCount = 32 ipv6BinarySingleSegmentDigitCount = 128 ipv4BinarySingleSegmentDigitCount = 32 ipv6Base85SingleSegmentDigitCount = 20 maxWildcards = ipv6Base85SingleSegmentDigitCount - 1 // 20 wildcards is equivalent to a base 85 address ipv4SingleSegmentOctalDigitCount = 11 longHexDigits = longSize >> 2 longBinaryDigits = longSize ) var ( macMaxTriple uint64 = (MACMaxValuePerSegment << uint64(MACBitsPerSegment*2)) | (MACMaxValuePerSegment << uint64(MACBitsPerSegment)) | MACMaxValuePerSegment macMaxQuintuple = (macMaxTriple << uint64(MACBitsPerSegment*2)) | uint64(macMaxTriple>>uint64(MACBitsPerSegment)) ) func isSingleSegmentIPv6( str string, totalDigits int, isRange bool, frontTotalDigits int, ipv6SpecificOptions addrstrparam.IPv6AddressStringParams) (isSingle bool, err addrerr.AddressStringError) { backIsIpv6 := totalDigits == ipv6SingleSegmentDigitCount || // 32 hex chars with or without 0x (ipv6SpecificOptions.AllowsBinary() && totalDigits == ipv6BinarySingleSegmentDigitCount+2) || // 128 binary chars with 0b (isRange && totalDigits == 0 && (frontTotalDigits == ipv6SingleSegmentDigitCount || (ipv6SpecificOptions.AllowsBinary() && frontTotalDigits == ipv6BinarySingleSegmentDigitCount+2))) if backIsIpv6 && isRange && totalDigits != 0 { frontIsIpv6 := frontTotalDigits == ipv6SingleSegmentDigitCount || (ipv6SpecificOptions.AllowsBinary() && frontTotalDigits == ipv6BinarySingleSegmentDigitCount+2) || frontTotalDigits == 0 if !frontIsIpv6 { err = &addressStringError{addressError{str: str, key: "ipaddress.error.too.few.segments.digit.count"}} return } } isSingle = backIsIpv6 return } // When checking for binary single segment, we must check for the exact number of digits for IPv4. // This is because of ambiguity between IPv6 hex 32 chars starting with 0b and 0b before 30 binary chars. // So we must therefore avoid 0b before 30 binary chars for IPv4. We must require 0b before 32 binary chars. // This only applies to single-segment. // For segmented IPv4, there is no ambiguity and we allow binary segments of varying lengths, // just like we do for inet_aton. func isSingleSegmentIPv4( str string, nonZeroDigits, totalDigits int, isRange bool, frontNonZeroDigits, frontTotalDigits int, ipv4SpecificOptions addrstrparam.IPv4AddressStringParams) (isSingle bool, err addrerr.AddressStringError) { backIsIpv4 := nonZeroDigits <= ipv4SingleSegmentOctalDigitCount || (ipv4SpecificOptions.AllowsBinary() && totalDigits == ipv4BinarySingleSegmentDigitCount+2) || (isRange && totalDigits == 0 && (frontTotalDigits <= ipv4SingleSegmentOctalDigitCount || (ipv4SpecificOptions.AllowsBinary() && frontTotalDigits == ipv4BinarySingleSegmentDigitCount+2))) if backIsIpv4 && isRange && totalDigits != 0 { frontIsIpv4 := frontNonZeroDigits <= ipv4SingleSegmentOctalDigitCount || (ipv4SpecificOptions.AllowsBinary() && frontTotalDigits == ipv4BinarySingleSegmentDigitCount+2) || frontTotalDigits == 0 if !frontIsIpv4 { err = &addressStringError{addressError{str: str, key: "ipaddress.error.too.few.segments.digit.count"}} return } } isSingle = backIsIpv4 return } type strValidator struct{} func (strValidator) validateIPAddressStr(fromString *IPAddressString, validationOptions addrstrparam.IPAddressStringParams) (prov ipAddressProvider, err addrerr.AddressStringError) { str := fromString.str pa := parsedIPAddress{ originator: fromString, options: validationOptions, ipAddressParseData: ipAddressParseData{addressParseData: addressParseData{str: str}}, } if err = validateIPAddress(validationOptions, str, 0, len(str), pa.getIPAddressParseData(), false); err == nil { if err = parseAddressQualifier(str, validationOptions, nil, pa.getIPAddressParseData(), len(str)); err == nil { prov, err = chooseIPAddressProvider(fromString, str, validationOptions, &pa) } else { prov = getInvalidProvider(validationOptions) } } else { prov = getInvalidProvider(validationOptions) } return } func getInvalidProvider(validationOptions addrstrparam.IPAddressStringParams) ipAddressProvider { if validationOptions == defaultIPAddrParameters { return invalidProvider } return &nullProvider{isInvalidVal: true, ipType: invalidType, params: validationOptions} } func (strValidator) validateMACAddressStr(fromString *MACAddressString, validationOptions addrstrparam.MACAddressStringParams) (prov macAddressProvider, err addrerr.AddressStringError) { str := fromString.str pa := parsedMACAddress{ originator: fromString, macAddressParseData: macAddressParseData{addressParseData: addressParseData{str: str}}, params: validationOptions, creationLock: &sync.Mutex{}, } if err = validateMACAddress(validationOptions, str, 0, len(str), pa.getMACAddressParseData()); err == nil { addressParseData := pa.getAddressParseData() prov, err = chooseMACAddressProvider(fromString, validationOptions, &pa, addressParseData) } else { prov = getInvalidMACProvider(validationOptions) } if err != nil && prov == nil { prov = getInvalidMACProvider(validationOptions) } return } func getInvalidMACProvider(validationOptions addrstrparam.MACAddressStringParams) macAddressProvider { if validationOptions == defaultMACAddrParameters { return invalidMACProvider } return macAddressNullProvider{validationOptions} } func validateIPAddress( validationOptions addrstrparam.IPAddressStringParams, str string, strStartIndex, strEndIndex int, parseData *ipAddressParseData, isEmbeddedIPv4 bool) addrerr.AddressStringError { return validateAddress(validationOptions, nil, str, strStartIndex, strEndIndex, parseData, nil, isEmbeddedIPv4) } func validateMACAddress( validationOptions addrstrparam.MACAddressStringParams, str string, strStartIndex, strEndIndex int, parseData *macAddressParseData) addrerr.AddressStringError { return validateAddress(nil, validationOptions, str, strStartIndex, strEndIndex, nil, parseData, false) } /** * This method is the mega-parser. * It is designed to go through the characters one-by-one as a big if/else. * You have basically several cases: digits, segment separators (. : -), end characters like zone or prefix length, * range characters denoting a range a-b, wildcard char *, and the 'x' character used to denote hex like 0xf. * * Most of the processing occurs in the segment characters, where each segment is analyzed based on what chars came before. * * We can parse all possible imaginable variations of mac, ipv4, and ipv6. * * This is not the clearest way to write such a parser, because the code for each possible variation is interspersed amongst the various cases, * so you cannot easily see the code for a given variation clearly, but written this way it may be the fastest parser since we basically account * for all possibilities simultaneously as we move through the characters just once. * */ func validateAddress( validationOptions addrstrparam.IPAddressStringParams, macOptions addrstrparam.MACAddressStringParams, str string, strStartIndex, strEndIndex int, ipParseData *ipAddressParseData, macParseData *macAddressParseData, isEmbeddedIPv4 bool) addrerr.AddressStringError { isMac := macParseData != nil var parseData *addressParseData var stringFormatParams addrstrparam.AddressStringFormatParams var ipv6SpecificOptions addrstrparam.IPv6AddressStringParams var ipv4SpecificOptions addrstrparam.IPv4AddressStringParams var macSpecificOptions addrstrparam.MACAddressStringFormatParams var baseOptions addrstrparam.AddressStringParams var macFormat macFormat canBeBase85 := false if isMac { baseOptions = macOptions macSpecificOptions = macOptions.GetFormatParams() stringFormatParams = macSpecificOptions macParseData.init(str) parseData = macParseData.getAddressParseData() } else { baseOptions = validationOptions // we set stringFormatParams when we know what ip version we have ipParseData.init(str) parseData = ipParseData.getAddressParseData() ipv6SpecificOptions = validationOptions.GetIPv6Params() canBeBase85 = ipv6SpecificOptions.AllowsBase85() ipv4SpecificOptions = validationOptions.GetIPv4Params() } index := strStartIndex // per segment variables var frontDigitCount, frontLeadingZeroCount, frontSingleWildcardCount, leadingZeroCount, singleWildcardCount, wildcardCount, frontWildcardCount int var extendedCharacterIndex, extendedRangeWildcardIndex, rangeWildcardIndex, hexDelimiterIndex, frontHexDelimiterIndex, segmentStartIndex, segmentValueStartIndex = -1, -1, -1, -1, -1, index, index var isSegmented, leadingWithZero, hasDigits, frontIsStandardRangeChar, atEnd, firstSegmentDashedRange, frontUppercase, uppercase, isSingleIPv6, isSingleSegment, isDoubleSegment bool var err addrerr.AddressStringError checkCharCounts := true var version IPVersion var currentValueHex, currentFrontValueHex, extendedValue uint64 charArray := chars var currentChar byte for { if index >= strEndIndex { // for ipv6 or ipv4 we set current char to be the right separator to parse the last segment, // so that is why we have the check here for index == strEndIndex and not index >= strEndIndex atEnd = index == strEndIndex if atEnd { parseData.setAddressEndIndex(index) if isSegmented { if isMac { currentChar = byte(*macFormat) isDoubleSegment = parseData.getSegmentCount() == 1 && currentChar == RangeSeparator macParseData.setDoubleSegment(isDoubleSegment) if isDoubleSegment { totalDigits := index - segmentValueStartIndex macParseData.setExtended(totalDigits == macExtendedDoubleSegmentDigitCount) } } else { // we are not base 85, so error if necessary if extendedCharacterIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, extendedCharacterIndex} } //current char is either . or : to handle last segment, unless we have double :: in which case we already handled last segment if version.IsIPv4() { currentChar = IPv4SegmentSeparator } else { //ipv6 if index == segmentStartIndex { if index == parseData.getConsecutiveSeparatorIndex()+2 { //ends with ::, we've already parsed the last segment break } return &addressStringError{addressError{str: str, key: "ipaddress.error.cannot.end.with.single.separator"}} } else if ipParseData.isProvidingMixedIPv6() { //no need to parse the last segment, since it is mixed we already have break } else { currentChar = IPv6SegmentSeparator } } } } else { // no segment separator so far and segmentCount is 0 // it could be all addresses like "*", empty "", prefix-only ip address like /64, single segment like 12345, or single segment range like 12345-67890 totalCharacterCount := index - strStartIndex if totalCharacterCount == 0 { //it is "" if !isMac && ipParseData.hasPrefixSeparator() { //if !validationOptions.AllowsPrefixOnly() { return &addressStringError{addressError{str: str, key: "ipaddress.error.prefix.only"}} //} } else if !baseOptions.AllowsEmpty() { return &addressStringError{addressError{str: str, key: "ipaddress.error.empty"}} } parseData.setEmpty(true) break } else if wildcardCount == totalCharacterCount && wildcardCount <= maxWildcards { //20 wildcards are base 85! if !baseOptions.AllowsAll() { return &addressStringError{addressError{str: str, key: "ipaddress.error.all"}} } parseData.setHasWildcard() parseData.setAll() break } // At this point it is single segment like 12345 or single segment range like 12345-67890 totalDigits := index - segmentValueStartIndex frontTotalDigits := frontLeadingZeroCount + frontDigitCount if isMac { // we handle the double segment format abcdef-abcdef here isDoubleSeg := (totalDigits == macDoubleSegmentDigitCount || totalDigits == macExtendedDoubleSegmentDigitCount) && (frontTotalDigits == macDoubleSegmentDigitCount || frontWildcardCount > 0) isDoubleSeg = isDoubleSeg || (frontTotalDigits == macDoubleSegmentDigitCount && wildcardCount > 0) isDoubleSeg = isDoubleSeg || (frontWildcardCount > 0 && wildcardCount > 0) if isDoubleSeg && !firstSegmentDashedRange { //checks for *-abcdef and abcdef-* and abcdef-abcdef and *-* two segment addresses // firstSegmentDashedRange means that the range character is '|' addressSize := macOptions.GetPreferredLen() if addressSize == addrstrparam.EUI64Len && totalDigits == macDoubleSegmentDigitCount { return &addressStringError{addressError{str: str, key: "ipaddress.error.too.few.segments"}} } else if addressSize == addrstrparam.MAC48Len && totalDigits == macExtendedDoubleSegmentDigitCount { return &addressStringError{addressError{str: str, key: "ipaddress.error.too.many.segments"}} } // we have aaaaaa-bbbbbb if !macOptions.AllowsSingleDashed() { return &addressStringError{addressError{str: str, key: "ipaddress.mac.error.format"}} } isDoubleSegment = true macParseData.setDoubleSegment(true) macParseData.setExtended(totalDigits == macExtendedDoubleSegmentDigitCount) //we have aaaaaa-bbbbbbbbbb currentChar = MACDashSegmentSeparator checkCharCounts = false //counted chars already } else if frontWildcardCount > 0 || wildcardCount > 0 { // either x-* or *-x, we treat these as if they can be expanded to x-*-*-*-*-* or *-*-*-*-*-x if !macOptions.AllowsSingleDashed() { return &addressStringError{addressError{str: str, key: "ipaddress.mac.error.format"}} } currentChar = MACDashSegmentSeparator } else { // a string of digits with no segment separator // here we handle abcdefabcdef or abcdefabcdef|abcdefabcdef or abcdefabcdef-abcdefabcdef if !baseOptions.AllowsSingleSegment() { return &addressStringError{addressError{str: str, key: "ipaddress.error.single.segment"}} } is12Digits := totalDigits == macSingleSegmentDigitCount is16Digits := totalDigits == macExtendedSingleSegmentDigitCount isNoDigits := totalDigits == 0 if is12Digits || is16Digits || isNoDigits { var frontIs12Digits, frontIs16Digits, frontIsNoDigits bool if rangeWildcardIndex >= 0 { frontIs12Digits = frontTotalDigits == macSingleSegmentDigitCount frontIs16Digits = frontTotalDigits == macExtendedSingleSegmentDigitCount frontIsNoDigits = frontTotalDigits == 0 if is12Digits { if !frontIs12Digits && !frontIsNoDigits { return &addressStringError{addressError{str: str, key: "ipaddress.error.front.digit.count"}} } } else if is16Digits { if !frontIs16Digits && !frontIsNoDigits { return &addressStringError{addressError{str: str, key: "ipaddress.error.front.digit.count"}} } } else if isNoDigits { if !frontIs12Digits && !frontIs16Digits { return &addressStringError{addressError{str: str, key: "ipaddress.error.front.digit.count"}} } } } else if isNoDigits { return &addressStringError{addressError{str: str, key: "ipaddress.error.too.few.segments.digit.count"}} } isSingleSegment = true parseData.setSingleSegment() macParseData.setExtended(is16Digits || frontIs16Digits) currentChar = MACColonSegmentSeparator checkCharCounts = false //counted chars already } else { return &addressStringError{addressError{str: str, key: "ipaddress.error.too.few.segments.digit.count"}} } } } else { //a string of digits with no segment separator if !baseOptions.AllowsSingleSegment() { return &addressStringError{addressError{str: str, key: "ipaddress.error.single.segment"}} } isRange := rangeWildcardIndex >= 0 isSingleSeg, serr := isSingleSegmentIPv6(str, totalDigits, isRange, frontTotalDigits, ipv6SpecificOptions) if serr != nil { return serr } else if isSingleSeg { // we are not base 85, so error if necessary if extendedCharacterIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, extendedCharacterIndex, } } isSingleIPv6 = true currentChar = IPv6SegmentSeparator } else { if canBeBase85 { if canBeBase85, serr = parseBase85( validationOptions, str, strStartIndex, strEndIndex, ipParseData, extendedRangeWildcardIndex, totalCharacterCount, index); canBeBase85 { break } if serr != nil { return serr } } leadingZeros := leadingZeroCount if leadingWithZero { leadingZeros++ } isSingleSeg, serr = isSingleSegmentIPv4( str, totalDigits-leadingZeros, totalDigits, isRange, frontDigitCount, frontTotalDigits, ipv4SpecificOptions) if serr != nil { return serr } else if isSingleSeg { // we are not base 85, so error if necessary if extendedCharacterIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, extendedCharacterIndex, } } currentChar = IPv4SegmentSeparator } else { return &addressStringError{addressError{str: str, key: "ipaddress.error.too.few.segments.digit.count"}} } } isSingleSegment = true parseData.setSingleSegment() checkCharCounts = false // counted chars already } } } else { break } } else { currentChar = str[index] } // evaluate the character if currentChar <= '9' && currentChar >= '0' { if hasDigits { currentValueHex = currentValueHex<<4 | uint64(charArray[currentChar]) } else { if currentChar == '0' { if leadingWithZero { leadingZeroCount++ } else { leadingWithZero = true } } else { hasDigits = true currentValueHex = currentValueHex<<4 | uint64(charArray[currentChar]) } } index++ } else if currentChar >= 'a' && currentChar <= 'f' { currentValueHex = currentValueHex<<4 | uint64(charArray[currentChar]) hasDigits = true index++ } else if currentChar == IPv4SegmentSeparator { segCount := parseData.getSegmentCount() // could be mac or ipv4, we handle either one if isMac { if segCount == 0 { if !macOptions.AllowsDotted() { return &addressStringError{addressError{str: str, key: "ipaddress.mac.error.format"}} } macFormat = dotted macParseData.setFormat(macFormat) parseData.initSegmentData(MediaAccessControlDotted64SegmentCount) isSegmented = true } else { if macFormat != dotted { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.mac.error.mix.format.characters.at.index"}}, index} } var limit int if macOptions.GetPreferredLen() == addrstrparam.MAC48Len { limit = MediaAccessControlDottedSegmentCount } else { limit = MediaAccessControlDotted64SegmentCount } if segCount >= limit { return &addressStringError{addressError{str: str, key: "ipaddress.error.too.many.segments"}} } } } else { //end of an ipv4 segment if segCount == 0 { if !validationOptions.AllowsIPv4() { return &addressStringError{addressError{str: str, key: "ipaddress.error.ipv4"}} } version = IPv4 ipParseData.setVersion(version) stringFormatParams = ipv4SpecificOptions canBeBase85 = false parseData.initSegmentData(IPv4SegmentCount) isSegmented = true } else if ipParseData.getProviderIPVersion().IsIPv6() { //mixed IPv6 address like 1:2:3:4:5:6:1.2.3.4 if !ipv6SpecificOptions.AllowsMixed() { return &addressStringError{addressError{str: str, key: "ipaddress.error.no.mixed"}} } totalSegmentCount := segCount + IPv6MixedReplacedSegmentCount if totalSegmentCount > IPv6SegmentCount { return &addressStringError{addressError{str: str, key: "ipaddress.error.too.many.segments"}} } if wildcardCount > 0 { if parseData.getConsecutiveSeparatorIndex() < 0 && totalSegmentCount < IPv6SegmentCount && ipv6SpecificOptions.AllowsWildcardedSeparator() { // the '*' is covering an additional ipv6 segment (eg 1:2:3:4:5:*.2.3.4, the * covers both an ipv4 and ipv6 segment) // we flag this IPv6 segment with keyMergedMixed parseData.setHasWildcard() assign6Attributes2Values1Flags(segmentStartIndex, index, segmentStartIndex, segmentStartIndex, index, segmentStartIndex, parseData, segCount, 0, IPv6MaxValuePerSegment, keyWildcard|keyMergedMixed) parseData.incrementSegmentCount() } } mixedOptions := ipv6SpecificOptions.GetMixedParams() pa := &parsedIPAddress{ ipAddressParseData: ipAddressParseData{addressParseData: addressParseData{str: str}}, options: mixedOptions, } err = validateIPAddress(mixedOptions, str, segmentStartIndex, strEndIndex, &pa.ipAddressParseData, true) if err != nil { return err } pa.clearQualifier() err = checkSegments(str, mixedOptions, pa.getIPAddressParseData()) if err != nil { return err } ipParseData.setMixedParsedAddress(pa) index = pa.getAddressParseData().getAddressEndIndex() continue } else if segCount >= IPv4SegmentCount { return &addressStringError{addressError{str: str, key: "ipaddress.error.ipv4.too.many.segments"}} } } if wildcardCount > 0 { if !stringFormatParams.GetRangeParams().AllowsWildcard() { return &addressStringError{addressError{str: str, key: "ipaddress.error.no.wildcard"}} } //wildcards must appear alone totalDigits := index - segmentStartIndex if wildcardCount != totalDigits || hexDelimiterIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } parseData.setHasWildcard() startIndex := index - wildcardCount var max uint64 if isMac { max = MACMaxValuePerDottedSegment } else { max = IPv4MaxValuePerSegment } assign6Attributes2Values1Flags(startIndex, index, startIndex, startIndex, index, startIndex, parseData, segCount, 0, max, keyWildcard) wildcardCount = 0 } else { var flags, rangeFlags, radix uint32 var value uint64 digitStartIndex := segmentValueStartIndex + leadingZeroCount digitCount := index - digitStartIndex if leadingWithZero { if digitCount == 1 { if leadingZeroCount == 0 && rangeWildcardIndex < 0 && hexDelimiterIndex < 0 { // handles 0, but not 1-0 or 0x0 assign4Attributes(digitStartIndex, index, parseData, segCount, 10, segmentValueStartIndex) parseData.incrementSegmentCount() index++ segmentStartIndex = index segmentValueStartIndex = index leadingWithZero = false continue } } else { leadingZeroCount++ digitStartIndex++ digitCount-- } leadingWithZero = false // reset this flag now that we've used it } noValuesToSet := false if digitCount == 0 { // we allow an empty range boundary to denote the max value if rangeWildcardIndex < 0 || hexDelimiterIndex >= 0 || !stringFormatParams.GetRangeParams().AllowsInferredBoundary() { // starts with '.', or has two consecutive '.' return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.empty.segment.at.index"}}, index} } else if isMac { value = MACMaxValuePerDottedSegment radix = MACDefaultTextualRadix } else { value = IPv4MaxValuePerSegment // for inet-aton multi-segment, this will be adjusted later radix = IPv4DefaultTextualRadix } rangeFlags = keyInferredUpperBoundary } else { // digitCount > 0 // Note: we cannot do max value check on ipv4 until after all segments have been read due to inet_aton joined segments, // although we can do a preliminary check here that is in fact needed to prevent overflow when calculating values later isBinary := false hasLeadingZeros := leadingZeroCount > 0 isSingleWildcard := singleWildcardCount > 0 if isMac || hexDelimiterIndex >= 0 { if isMac { // mac dotted segments aabb.ccdd.eeff maxMacChars := 4 if digitCount > maxMacChars { // return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, segmentValueStartIndex} } totalDigits := digitCount + leadingZeroCount if hexDelimiterIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, hexDelimiterIndex} } else if leadingZeroCount > 0 && !stringFormatParams.AllowsLeadingZeros() { return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } else if !stringFormatParams.AllowsUnlimitedLeadingZeros() && totalDigits > maxMacChars { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, segmentValueStartIndex} } else if !macSpecificOptions.AllowsShortSegments() && totalDigits < maxMacChars { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.short.at.index"}}, segmentValueStartIndex} } } else if !stringFormatParams.AllowsLeadingZeros() { // the '0' preceding the 'x' is not allowed return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } else if !ipv4SpecificOptions.Allows_inet_aton_hex() { return &addressStringError{addressError{str: str, key: "ipaddress.error.ipv4.segment.hex"}} } else if hasLeadingZeros && !ipv4SpecificOptions.Allows_inet_aton_leading_zeros() { // the '0' following the 'x' is not allowed return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } else { if digitCount > 8 { // 0xffffffff return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, segmentValueStartIndex} } ipParseData.set_has_inet_aton_value(true) } radix = 16 if isSingleWildcard { if rangeWildcardIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } err = assignSingleWildcard16(currentValueHex, str, digitStartIndex, index, singleWildcardCount, parseData, segCount, segmentValueStartIndex, stringFormatParams) if err != nil { return err } value = 0 noValuesToSet = true singleWildcardCount = 0 } else { value = currentValueHex } hexDelimiterIndex = -1 } else { isBinaryOrOctal := hasLeadingZeros if isBinaryOrOctal { isBinary = ipv4SpecificOptions.AllowsBinary() && isBinaryDelimiter(str, digitStartIndex) isBinaryOrOctal = isBinary || ipv4SpecificOptions.Allows_inet_aton_octal() } if isBinaryOrOctal { if !stringFormatParams.AllowsLeadingZeros() { return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } if isBinary { if digitCount > 33 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, segmentValueStartIndex} } digitStartIndex++ // exclude the 'b' in 0b1100 digitCount-- // exclude the 'b' radix = 2 ipParseData.setHasBinaryDigits(true) if isSingleWildcard { if rangeWildcardIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } if digitCount > 16 { if parseErr := parseSingleSegmentSingleWildcard2(str, digitStartIndex, index, singleWildcardCount, parseData, segCount, segmentValueStartIndex, stringFormatParams); parseErr != nil { return parseErr } } else { if parseErr := switchSingleWildcard2(currentValueHex, str, digitStartIndex, index, singleWildcardCount, parseData, segCount, segmentValueStartIndex, stringFormatParams); parseErr != nil { return parseErr } } value = 0 noValuesToSet = true singleWildcardCount = 0 } else { if digitCount > 16 { value = parseLong2(str, digitStartIndex, index) } else { value, err = switchValue2(currentValueHex, str, digitCount) if err != nil { return err } } } } else { if leadingZeroCount > 1 && !ipv4SpecificOptions.Allows_inet_aton_leading_zeros() { return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } else if digitCount > 11 { //octal 037777777777 return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, segmentValueStartIndex} } ipParseData.set_has_inet_aton_value(true) radix = 8 if isSingleWildcard { if rangeWildcardIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } if parseErr := switchSingleWildcard8(currentValueHex, str, digitStartIndex, index, singleWildcardCount, parseData, segCount, segmentValueStartIndex, stringFormatParams); parseErr != nil { return parseErr } value = 0 noValuesToSet = true singleWildcardCount = 0 } else { value, err = switchValue8(currentValueHex, str, digitCount) if err != nil { return err } } } } else { if hasLeadingZeros { if !stringFormatParams.AllowsLeadingZeros() { return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } ipParseData.setHasIPv4LeadingZeros(true) } if digitCount > 10 { // 4294967295 return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, segmentValueStartIndex} } radix = 10 if isSingleWildcard { if rangeWildcardIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } if parseErr := switchSingleWildcard10(currentValueHex, str, digitStartIndex, index, singleWildcardCount, parseData, segCount, segmentValueStartIndex, ipv4SpecificOptions); parseErr != nil { return parseErr } value = 0 noValuesToSet = true singleWildcardCount = 0 } else { value, err = switchValue10(currentValueHex, str, digitCount) if err != nil { return err } flags = keyStandardStr } } } hasDigits = false currentValueHex = 0 } if rangeWildcardIndex >= 0 { var frontRadix uint32 var front uint64 frontStartIndex := rangeWildcardIndex - frontDigitCount frontEndIndex := rangeWildcardIndex frontLeadingZeroStartIndex := frontStartIndex - frontLeadingZeroCount if !stringFormatParams.GetRangeParams().AllowsRangeSeparator() { return &addressStringError{addressError{str: str, key: "ipaddress.error.no.range"}} } else if frontSingleWildcardCount > 0 || frontWildcardCount > 0 { //no wildcards in ranges return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, rangeWildcardIndex} } frontEmpty := frontStartIndex == frontEndIndex isReversed := false hasFrontLeadingZeros := frontLeadingZeroCount > 0 if isMac || frontHexDelimiterIndex >= 0 { if isMac { totalFrontDigits := frontDigitCount + frontLeadingZeroCount maxMacChars := 4 if frontHexDelimiterIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, frontHexDelimiterIndex} } else if hasFrontLeadingZeros && !stringFormatParams.AllowsLeadingZeros() { return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } else if !stringFormatParams.AllowsUnlimitedLeadingZeros() && totalFrontDigits > maxMacChars { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, frontLeadingZeroStartIndex} } else if !macSpecificOptions.AllowsShortSegments() && totalFrontDigits < maxMacChars { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.short.at.index"}}, frontLeadingZeroStartIndex} } else if frontEmpty { //we allow the front of a range to be empty in which case it is 0 if !stringFormatParams.GetRangeParams().AllowsInferredBoundary() { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.empty.segment.at.index"}}, index} } rangeFlags |= keyInferredLowerBoundary front = 0 } else if frontDigitCount > maxMacChars { // mac dotted segments aaa.bbb.ccc.ddd return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, frontLeadingZeroStartIndex} } else { front = currentFrontValueHex isReversed = front > value && digitCount != 0 } } else if !stringFormatParams.AllowsLeadingZeros() { // the '0' preceding the 'x' is not allowed return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } else if !ipv4SpecificOptions.Allows_inet_aton_hex() { return &addressStringError{addressError{str: str, key: "ipaddress.error.ipv4.segment.hex"}} } else if hasFrontLeadingZeros && !ipv4SpecificOptions.Allows_inet_aton_leading_zeros() { // the '0' following the 'x' is not allowed return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } else if frontEmpty { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.empty.segment.at.index"}}, index} } else if frontDigitCount > 8 { // 0xffffffff return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, frontLeadingZeroStartIndex} } else { ipParseData.set_has_inet_aton_value(true) front = currentFrontValueHex isReversed = front > value && digitCount != 0 } frontRadix = 16 } else { if hasFrontLeadingZeros { if !stringFormatParams.AllowsLeadingZeros() { return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } if ipv4SpecificOptions.AllowsBinary() && isBinaryDelimiter(str, frontStartIndex) { if frontDigitCount > 33 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, frontLeadingZeroStartIndex} } ipParseData.setHasBinaryDigits(true) frontStartIndex++ frontDigitCount-- if frontDigitCount > 16 { front = parseLong2(str, frontStartIndex, frontEndIndex) } else { front, err = switchValue2(currentFrontValueHex, str, frontDigitCount) if err != nil { return err } } frontRadix = 2 isReversed = digitCount != 0 && front > value } else if ipv4SpecificOptions.Allows_inet_aton_octal() { if frontLeadingZeroCount > 1 && !ipv4SpecificOptions.Allows_inet_aton_leading_zeros() { return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } else if frontDigitCount > 11 { // 037777777777 return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, frontLeadingZeroStartIndex} } ipParseData.set_has_inet_aton_value(true) front, err = switchValue8(currentFrontValueHex, str, frontDigitCount) if err != nil { return err } frontRadix = 8 isReversed = digitCount != 0 && front > value } } if frontRadix == 0 { frontRadix = 10 if frontEmpty { //we allow the front of a range to be empty in which case it is 0 if !stringFormatParams.GetRangeParams().AllowsInferredBoundary() { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.empty.segment.at.index"}}, index} } rangeFlags |= keyInferredLowerBoundary } else if frontDigitCount > 10 { // 4294967295 return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, frontLeadingZeroStartIndex} } else { front, err = switchValue10(currentFrontValueHex, str, frontDigitCount) if err != nil { return err } if hasFrontLeadingZeros { if !stringFormatParams.AllowsLeadingZeros() { return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } ipParseData.setHasIPv4LeadingZeros(true) } isReversed = digitCount != 0 && front > value if !isReversed { if leadingZeroCount == 0 && (flags&keyStandardStr) != 0 { rangeFlags |= keyStandardRangeStr | keyStandardStr } else { rangeFlags |= keyStandardStr } } } } } backEndIndex := index if isReversed { if !stringFormatParams.GetRangeParams().AllowsReverseRange() { return &addressStringError{addressError{str: str, key: "ipaddress.error.invalidRange"}} } // switcheroo frontStartIndex, digitStartIndex = digitStartIndex, frontStartIndex frontEndIndex, backEndIndex = backEndIndex, frontEndIndex frontLeadingZeroStartIndex, segmentValueStartIndex = segmentValueStartIndex, frontLeadingZeroStartIndex frontRadix, radix = radix, frontRadix front, value = value, front } assign6Attributes2Values2Flags(frontStartIndex, frontEndIndex, frontLeadingZeroStartIndex, digitStartIndex, backEndIndex, segmentValueStartIndex, parseData, segCount, front, value, rangeFlags|keyRangeWildcard|frontRadix, radix) rangeWildcardIndex = -1 } else if !noValuesToSet { assign3Attributes1Values1Flags(digitStartIndex, index, segmentValueStartIndex, parseData, segCount, value, flags|radix) } leadingZeroCount = 0 } parseData.incrementSegmentCount() index++ segmentValueStartIndex = index segmentStartIndex = index // end of IPv4 segments and mac segments with '.' separators } else { //checking for all IPv6 and MAC48Len segments, as well as the front range of all segments IPv4, IPv6, and MAC48Len //the range character '-' is the same as one of the separators '-' for MAC48Len, //so further work is required to distinguish between the front of IPv6/IPv4/MAC48Len range and MAC48Len segment //we also handle IPv6 segment and MAC48Len segment in the same place to avoid code duplication var isSpace, isDashedRangeChar, isRangeChar bool if currentChar == IPv6SegmentSeparator { isRangeChar = false isSpace = false } else { isRangeChar = currentChar == RangeSeparator if isRangeChar || (isMac && (currentChar == MacDashedSegmentRangeSeparator)) { isSpace = false isDashedRangeChar = !isRangeChar /* There are 3 cases here, A, B and C. A - we have two MAC48Len segments a-b- B - we have the front of a range segment, either a-b which is MAC48Len or ipv6AddrType, or a|b or ab which is MAC48Len C - we have a single segment, either a MAC48Len segment a- or an IPv6 or MAC48Len segment a: */ /* Here we have either a '-' or '|' character or a space ' ' If we have a '-' character: For MAC48Len address, the cases are: 1. we did not previously set macFormat and we did not previously encounter '|' -if rangeWildcardIndex >= 0 we have dashed a-b- we treat as two segments, case A (we cannot have a|b because that would have set macFormat previously) -if rangeWildcardIndex < 0, we treat as front of range, case B, later we will know for sure if really front of range 2. we previously set macFormat or we previously encountered '|' if set to dashed we treat as one segment, may or may not be range segment, case C if we previously encountered '|' we treat as dashed range segment, case C if not we treat as front of range, case B For IPv6, this is always front of range, case B If we have a '|' character, we have front of range MAC48Len, case B */ // we know either isRangeChar or isDashedRangeChar is true at this point endOfHexSegment := false if isMac { if macFormat == nil { if rangeWildcardIndex >= 0 && !firstSegmentDashedRange { //case A, we have two segments a-b- or a-b| //we handle the first segment here, we handle the second segment in the usual place below if frontHexDelimiterIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, frontHexDelimiterIndex} } else if hexDelimiterIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, hexDelimiterIndex} } else if !macOptions.AllowsDashed() { return &addressStringError{addressError{str: str, key: "ipaddress.mac.error.format"}} } macFormat = dashed macParseData.setFormat(macFormat) checkCharCounts = false //counting chars later parseData.initSegmentData(ExtendedUniqueIdentifier64SegmentCount) isSegmented = true if frontWildcardCount > 0 { if !stringFormatParams.GetRangeParams().AllowsWildcard() { return &addressStringError{addressError{str: str, key: "ipaddress.error.no.wildcard"}} } else if frontSingleWildcardCount > 0 || frontLeadingZeroCount > 0 || frontDigitCount > 0 || frontHexDelimiterIndex >= 0 { //wildcards must appear alone return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, rangeWildcardIndex} } parseData.setHasWildcard() backDigits := index - segmentValueStartIndex var upperValue uint64 if isDoubleSegment || backDigits == macDoubleSegmentDigitCount { //even when not already identified as a double segment address, which is something we can see //only when we reach the end of the address, we may have a-b| where a is * and b is a 6 digit value. //Here we are considering the max value of a. //If b is 6 digits, we need to consider the max value of * as if we know already it will be double segment. //We can do this because the max values will be checked after the address has been parsed, //so even if a-b| ends up being a full address a-b|c-d-e-f-a and not a-b|c, //the fact that we have 6 digits here will invalidate the first address, //so we can safely assume that this address must be a double segment a-b|c even before we have seen that. upperValue = macMaxTriple } else { upperValue = MACMaxValuePerSegment } startIndex := rangeWildcardIndex - frontWildcardCount assign6Attributes2Values1Flags(startIndex, rangeWildcardIndex, startIndex, startIndex, rangeWildcardIndex, startIndex, parseData, 0, 0, upperValue, keyWildcard) } else { if !stringFormatParams.AllowsLeadingZeros() && frontLeadingZeroCount > 0 { return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } startIndex := rangeWildcardIndex - frontDigitCount leadingZeroStartIndex := startIndex - frontLeadingZeroCount if frontSingleWildcardCount > 0 { if parseErr := assignSingleWildcard16(currentFrontValueHex, str, startIndex, rangeWildcardIndex, singleWildcardCount, parseData, 0, leadingZeroStartIndex, stringFormatParams); parseErr != nil { return parseErr } } else { var flags uint32 if !frontUppercase { if frontDigitCount == 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.empty.segment.at.index"}}, startIndex} } flags = keyStandardStr } assign3Attributes1Values1Flags(startIndex, rangeWildcardIndex, leadingZeroStartIndex, parseData, 0, currentFrontValueHex, flags) } } segmentValueStartIndex = rangeWildcardIndex + 1 segmentStartIndex = segmentValueStartIndex rangeWildcardIndex = -1 parseData.incrementSegmentCount() //end of handling the first segment a- in a-b- //below we handle b- by setting endOfSegment here endOfHexSegment = isRangeChar } else { //we will treat this as the front of a range if isDashedRangeChar { firstSegmentDashedRange = true } else { endOfHexSegment = firstSegmentDashedRange } } } else { if macFormat == dashed { endOfHexSegment = isRangeChar } else if isDashedRangeChar { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } } } if !endOfHexSegment { if extendedCharacterIndex < 0 { //case B if rangeWildcardIndex >= 0 { if canBeBase85 { index++ extendedCharacterIndex = index } else { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } } else { //here is where we handle the front 'a' of a range like 'a-b' rangeWildcardIndex = index frontIsStandardRangeChar = isRangeChar frontDigitCount = ((index - segmentValueStartIndex) - leadingZeroCount) - wildcardCount frontLeadingZeroCount = leadingZeroCount if leadingWithZero { if frontDigitCount != 1 { frontLeadingZeroCount++ frontDigitCount-- } } frontUppercase = uppercase frontHexDelimiterIndex = hexDelimiterIndex frontWildcardCount = wildcardCount frontSingleWildcardCount = singleWildcardCount currentFrontValueHex = currentValueHex index++ segmentValueStartIndex = index hasDigits = false uppercase = false leadingWithZero = false hexDelimiterIndex = -1 leadingZeroCount = 0 wildcardCount = 0 singleWildcardCount = 0 currentValueHex = 0 } } else { index++ } continue } } else if isMac && currentChar == space { isSpace = true } else { // other characters handled here isZoneChar := false if currentChar == PrefixLenSeparator { if isMac { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, index} } strEndIndex = index ipParseData.setHasPrefixSeparator(true) ipParseData.setQualifierIndex(index + 1) } else if currentChar >= 'A' && currentChar <= 'F' { // this is not paired with 'a' to 'f' because these are not canonical and hence not part of the fast path index++ currentValueHex = (currentValueHex << 4) | uint64(charArray[currentChar]) hasDigits = true uppercase = true } else { isSegWildcard := currentChar == SegmentWildcard if !isSegWildcard { isZoneChar = currentChar == SegmentSqlWildcard isSegWildcard = isZoneChar } if isSegWildcard { //the character * is always treated as wildcard (but later can be determined to be a base 85 character) //the character % denotes a zone and is also a character for the SQL wildcard, //and it is also a base 85 character, //so we treat it as zone only if the options allow it and it is in the zone position. //Either we have seen an ipv6 segment separator, or we are at the end of the correct number of digits for ipv6 single segment (which rules out base 85 or ipv4 single segment), //or we are the '*' all wildcard so far which can represent everything including ipv6 // //In all other cases, the character is treated as wildcard, //but as is the case of other characters we may later discover we are base 85 ipv6 //For base 85, we decided that having the same character mean two different thing depending on position in the string, that is not reasonable. //In fact, if the zone character were allowed, can you tell if there is a zone here or not: %%%%%%%%%%%%%%%%%%%%%% canBeZone := isZoneChar && !isMac && ipv6SpecificOptions.AllowsZone() if canBeZone { isIPv6 := parseData.getSegmentCount() > 0 && (isEmbeddedIPv4 || ipParseData.getProviderIPVersion() == IPv6) /* at end of IPv6 regular or mixed */ if !isIPv6 { isIPv6, _ = isSingleSegmentIPv6(str, index-segmentValueStartIndex, rangeWildcardIndex >= 0, frontLeadingZeroCount+frontDigitCount, ipv6SpecificOptions) if !isIPv6 { isIPv6 = wildcardCount == index && wildcardCount <= maxWildcards /* all wildcards so far */ } } canBeZone = isIPv6 } if canBeZone { //we are not base 85 canBeBase85 = false strEndIndex = index ipParseData.setZoned(true) ipParseData.setQualifierIndex(index + 1) } else { wildcardCount++ index++ } } else if currentChar == SegmentSqlSingleWildcard { hasDigits = true index++ singleWildcardCount++ } else if isHexDelimiter(currentChar) { if hasDigits || !leadingWithZero || leadingZeroCount > 0 || hexDelimiterIndex >= 0 || singleWildcardCount > 0 { if canBeBase85 { if extendedCharacterIndex < 0 { extendedCharacterIndex = index } index++ } else { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } } else { if isMac { if parseData.getSegmentCount() > 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, index} } } else if version.IsIPv6() { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, index} } hexDelimiterIndex = index leadingWithZero = false index++ segmentValueStartIndex = index } //the remaining possibilities are base85 only } else if currentChar == AlternativeRangeSeparatorStr[0] { if index+1 == strEndIndex { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, index} } currentChar = str[index+1] if currentChar == AlternativeRangeSeparatorStr[1] { if canBeBase85 { if extendedCharacterIndex < 0 { extendedCharacterIndex = index } else if extendedRangeWildcardIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } extendedRangeWildcardIndex = index } else { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, index} } index += 2 } else if currentChar == IPv6AlternativeZoneSeparatorStr[1] { if canBeBase85 && !isMac && ipv6SpecificOptions.AllowsZone() { strEndIndex = index ipParseData.setZoned(true) ipParseData.setBase85Zoned(true) ipParseData.setQualifierIndex(index + 2) } else { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, index} } } else { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, index - 1} } } else { if canBeBase85 { if currentChar < 0 || int(currentChar) >= len(extendedChars) { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, index} } val := extendedChars[currentChar] if val == 0 { //note that we already check for the currentChar '0' character at another else/if block, so any other character mapped to the value 0 is an invalid character return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, index} } else if extendedCharacterIndex < 0 { extendedCharacterIndex = index } } else { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, index} } index++ } } continue } } // ipv6 and mac segments handled here segCount := parseData.getSegmentCount() var hexMaxChars int if isMac { if segCount == 0 { if isSingleSegment { parseData.initSegmentData(1) } else { if hexDelimiterIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, hexDelimiterIndex} } else { var isNoExc bool if isRangeChar { isNoExc = macOptions.AllowsDashed() } else if isSpace { isNoExc = macOptions.AllowsSpaceDelimited() } else { isNoExc = macOptions.AllowsColonDelimited() } if !isNoExc { return &addressStringError{addressError{str: str, key: "ipaddress.mac.error.format"}} } else if isRangeChar { macFormat = dashed macParseData.setFormat(macFormat) checkCharCounts = false //counting chars later } else { if isSpace { macFormat = spaceDelimited } else { macFormat = colonDelimited } macParseData.setFormat(macFormat) } } parseData.initSegmentData(ExtendedUniqueIdentifier64SegmentCount) isSegmented = true } } else { isExc := false if isRangeChar { isExc = macFormat != dashed } else if isSpace { isExc = macFormat != spaceDelimited } else { isExc = macFormat != colonDelimited } if isExc { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.mac.error.mix.format.characters.at.index"}}, index} } var segLimit int if macOptions.GetPreferredLen() == addrstrparam.MAC48Len { segLimit = MediaAccessControlSegmentCount } else { segLimit = ExtendedUniqueIdentifier64SegmentCount } if segCount >= segLimit { return &addressStringError{addressError{str: str, key: "ipaddress.error.too.many.segments"}} } } hexMaxChars = MACSegmentMaxChars //will be ignored for single or double segments due to checkCharCounts booleans } else { if segCount == 0 { if !validationOptions.AllowsIPv6() { return &addressStringError{addressError{str: str, key: "ipaddress.error.ipv6"}} } canBeBase85 = false version = IPv6 ipParseData.setVersion(version) stringFormatParams = ipv6SpecificOptions isSegmented = true if index == strStartIndex { firstIndex := index index++ if index == strEndIndex { return &addressStringError{addressError{str: str, key: "ipaddress.error.too.few.segments"}} } else if str[index] != IPv6SegmentSeparator { return &addressStringError{addressError{str: str, key: "ipaddress.error.ipv6.cannot.start.with.single.separator"}} } parseData.initSegmentData(IPv6SegmentCount) parseData.setConsecutiveSeparatorSegmentIndex(0) parseData.setConsecutiveSeparatorIndex(firstIndex) assign3Attributes(index, index, parseData, 0, index) parseData.incrementSegmentCount() index++ segmentValueStartIndex = index segmentStartIndex = segmentValueStartIndex continue } else { if isSingleSegment { parseData.initSegmentData(1) } else { if hexDelimiterIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, hexDelimiterIndex} } parseData.initSegmentData(IPv6SegmentCount) } } } else if ipParseData.getProviderIPVersion().IsIPv4() { return &addressStringError{addressError{str: str, key: "ipaddress.error.ipv6.separator"}} } else if segCount >= IPv6SegmentCount { return &addressStringError{addressError{str: str, key: "ipaddress.error.too.many.segments"}} } hexMaxChars = IPv6SegmentMaxChars // will be ignored for single segment due to checkCharCounts boolean } if index == segmentStartIndex { // empty segment if isMac { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.empty.segment.at.index"}}, index} } else if parseData.getConsecutiveSeparatorIndex() >= 0 { return &addressStringError{addressError{str: str, key: "ipaddress.error.ipv6.ambiguous"}} } parseData.setConsecutiveSeparatorSegmentIndex(segCount) parseData.setConsecutiveSeparatorIndex(index - 1) assign3Attributes(index, index, parseData, segCount, index) parseData.incrementSegmentCount() } else if wildcardCount > 0 && !isSingleIPv6 { if !stringFormatParams.GetRangeParams().AllowsWildcard() { return &addressStringError{addressError{str: str, key: "ipaddress.error.no.wildcard"}} } totalDigits := index - segmentStartIndex if wildcardCount != totalDigits || hexDelimiterIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } parseData.setHasWildcard() startIndex := index - wildcardCount var maxVal uint64 if isMac { if isDoubleSegment { maxVal = macMaxTriple } else { maxVal = MACMaxValuePerSegment } } else { maxVal = IPv6MaxValuePerSegment } assign6Attributes2Values1Flags(startIndex, index, startIndex, startIndex, index, startIndex, parseData, segCount, 0, maxVal, keyWildcard) parseData.incrementSegmentCount() wildcardCount = 0 } else { startIndex := segmentValueStartIndex digitCount := index - startIndex noValuesToSet := false var value uint64 var flags, rangeFlags uint32 if leadingWithZero { if digitCount == 1 { // can only be a single 0 if leadingZeroCount == 0 && rangeWildcardIndex < 0 { // handles 0 but not 1-0 assign3Attributes(startIndex, index, parseData, segCount, segmentValueStartIndex) parseData.incrementSegmentCount() index++ segmentValueStartIndex = index segmentStartIndex = segmentValueStartIndex leadingWithZero = false continue } } else { if hasDigits { leadingZeroCount++ } startIndex += leadingZeroCount digitCount -= leadingZeroCount } leadingWithZero = false } if leadingZeroCount == 0 { if digitCount == 0 { // since we have already checked for an empty segment, this can only happen with a range, ie rangeWildcardIndex >= 0 // we allow an empty range boundary to denote the max value if !stringFormatParams.GetRangeParams().AllowsInferredBoundary() { // starts with '.', or has two consecutive '.' return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.empty.segment.at.index"}}, index} } else if isMac { if isSingleSegment { if macParseData.isExtended() { value = 0xffffffffffffffff } else { value = 0xffffffffffff } } else { value = MACMaxValuePerSegment } } else { if isSingleIPv6 { value = 0xffffffffffffffff extendedValue = value } else { value = IPv6MaxValuePerSegment } } rangeFlags = keyInferredUpperBoundary } else { if digitCount > hexMaxChars && checkCharCounts { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, segmentValueStartIndex} } if singleWildcardCount > 0 { noValuesToSet = true if rangeWildcardIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } else if isSingleIPv6 { //We need this special call here because single ipv6 hex is 128 bits and cannot fit into a long if parseErr := parseSingleSegmentSingleWildcard16(currentValueHex, str, startIndex, index, singleWildcardCount, parseData, segCount, segmentValueStartIndex, stringFormatParams); parseErr != nil { return parseErr } } else { if parseErr := assignSingleWildcard16(currentValueHex, str, startIndex, index, singleWildcardCount, parseData, segCount, segmentValueStartIndex, stringFormatParams); parseErr != nil { return parseErr } } uppercase = false singleWildcardCount = 0 } else { if isSingleIPv6 { //We need this special branch here because single ipv6 hex is 128 bits and cannot fit into a long midIndex := index - 16 if startIndex < midIndex { extendedValue = parseLong16(str, startIndex, midIndex) value = parseLong16(str, midIndex, index) } else { value = currentValueHex } } else { value = currentValueHex if uppercase { uppercase = false } else { flags = keyStandardStr } } } hasDigits = false currentValueHex = 0 } } else { if leadingZeroCount == 1 && (digitCount == 17 || digitCount == 129) && ipv6SpecificOptions.AllowsBinary() && isBinaryDelimiter(str, startIndex) { // IPv6 binary - to avoid ambiguity, all binary digits must be present, and preceded by 0b. // So for a single segment IPv6 segment: // 0b11 is a hex segment, 0b111 is hex, 0b1111 is invalid (too short for binary, too long for hex), 0b1100110011001100 is a binary segment if !stringFormatParams.AllowsLeadingZeros() { return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } startIndex++ // exclude the 'b' in 0b1100 digitCount-- // exclude the 'b' if singleWildcardCount > 0 { if rangeWildcardIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } else if isSingleIPv6 { if parseErr := parseSingleSegmentSingleWildcard2(str, startIndex, index, singleWildcardCount, parseData, segCount, segmentValueStartIndex, stringFormatParams); parseErr != nil { return parseErr } } else { if parseErr := switchSingleWildcard2(currentValueHex, str, startIndex, index, singleWildcardCount, parseData, segCount, segmentValueStartIndex, stringFormatParams); parseErr != nil { return parseErr } } noValuesToSet = true singleWildcardCount = 0 } else { if isSingleIPv6 { //We need this special branch here because single ipv6 hex is 128 bits and cannot fit into a long midIndex := index - 64 extendedValue = parseLong2(str, startIndex, midIndex) value = parseLong2(str, midIndex, index) } else { value, err = switchValue2(currentValueHex, str, digitCount) if err != nil { return err } } flags = 2 // radix } ipParseData.setHasBinaryDigits(true) hasDigits = false currentValueHex = 0 } else { if digitCount > hexMaxChars && checkCharCounts { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, segmentValueStartIndex} } else if !stringFormatParams.AllowsLeadingZeros() { return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } else if !stringFormatParams.AllowsUnlimitedLeadingZeros() && checkCharCounts && (digitCount+leadingZeroCount) > hexMaxChars { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, segmentValueStartIndex} } if singleWildcardCount > 0 { noValuesToSet = true if rangeWildcardIndex >= 0 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, index} } else if isSingleIPv6 { //We need this special call here because single ipv6 hex is 128 bits and cannot fit into a long if parseErr := parseSingleSegmentSingleWildcard16(currentValueHex, str, startIndex, index, singleWildcardCount, parseData, segCount, segmentValueStartIndex, stringFormatParams); parseErr != nil { return parseErr } } else { if parseErr := assignSingleWildcard16(currentValueHex, str, startIndex, index, singleWildcardCount, parseData, segCount, segmentValueStartIndex, stringFormatParams); parseErr != nil { return parseErr } } uppercase = false singleWildcardCount = 0 } else { if isSingleIPv6 { //We need this special branch here because single ipv6 hex is 128 bits and cannot fit into a long midIndex := index - 16 if startIndex < midIndex { extendedValue = parseLong16(str, startIndex, midIndex) value = parseLong16(str, midIndex, index) } else { value = currentValueHex } } else { value = currentValueHex if uppercase { uppercase = false } else { flags = keyStandardStr } } } hasDigits = false currentValueHex = 0 } } if rangeWildcardIndex >= 0 { frontStartIndex := rangeWildcardIndex - frontDigitCount frontEndIndex := rangeWildcardIndex frontLeadingZeroStartIndex := frontStartIndex - frontLeadingZeroCount frontTotalDigitCount := frontDigitCount + frontLeadingZeroCount //the stuff that uses frontLeadingZeroCount needs to be sectioned off when singleIPv6 if !stringFormatParams.GetRangeParams().AllowsRangeSeparator() { return &addressStringError{addressError{str: str, key: "ipaddress.error.no.range"}} } else if frontHexDelimiterIndex >= 0 && !isSingleSegment { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, frontHexDelimiterIndex} } else if frontSingleWildcardCount > 0 || frontWildcardCount > 0 { // no wildcards in ranges return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.combination.at.index"}}, rangeWildcardIndex} } else if isMac && !macSpecificOptions.AllowsShortSegments() && frontTotalDigitCount < 2 { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.short.at.index"}}, frontLeadingZeroStartIndex} } var upperRadix uint32 frontIsBinary := false var front, extendedFront uint64 frontEmpty := frontStartIndex == frontEndIndex isReversed := false if frontEmpty { if !stringFormatParams.GetRangeParams().AllowsInferredBoundary() { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.empty.segment.at.index"}}, index} } rangeFlags |= keyInferredLowerBoundary } else { if frontLeadingZeroCount == 1 && frontDigitCount == 17 && ipv6SpecificOptions.AllowsBinary() && isBinaryDelimiter(str, frontStartIndex) { // IPv6 binary - to avoid ambiguity, all binary digits must be present, and preceded by 0b. frontStartIndex++ // exclude the 'b' in 0b1100 frontDigitCount-- // exclude the 'b' front, err = switchValue2(currentFrontValueHex, str, frontDigitCount) if err != nil { return err } upperRadix = 2 // radix rangeFlags = upperRadix // radix ipParseData.setHasBinaryDigits(true) isReversed = front > value frontIsBinary = true } else if isSingleIPv6 { //We need this special block here because single ipv6 hex is 128 bits and cannot fit into a long if frontDigitCount == 129 { // binary frontStartIndex++ // exclude the 'b' in 0b1100 frontDigitCount-- // exclude the 'b' upperRadix = 2 // radix rangeFlags = 2 // radix ipParseData.setHasBinaryDigits(true) frontMidIndex := frontEndIndex - 64 extendedFront = parseLong2(str, frontStartIndex, frontMidIndex) front = parseLong2(str, frontMidIndex, frontEndIndex) } else { frontMidIndex := frontEndIndex - 16 if frontStartIndex < frontMidIndex { extendedFront = parseLong16(str, frontStartIndex, frontMidIndex) front = parseLong16(str, frontMidIndex, frontEndIndex) } else { front = currentFrontValueHex } } isReversed = (extendedFront > extendedValue) || (extendedFront == extendedValue && front > value) } else { if !stringFormatParams.AllowsLeadingZeros() && frontLeadingZeroCount > 0 { return &addressStringError{addressError{str: str, key: "ipaddress.error.segment.leading.zeros"}} } else if checkCharCounts { if frontDigitCount > hexMaxChars { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, frontLeadingZeroStartIndex} } else if !stringFormatParams.AllowsUnlimitedLeadingZeros() && frontTotalDigitCount > hexMaxChars { return &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.segment.too.long.at.index"}}, frontLeadingZeroStartIndex} } } front = currentFrontValueHex isReversed = front > value extendedFront = 0 } } backEndIndex := index if isReversed { if !stringFormatParams.GetRangeParams().AllowsReverseRange() { return &addressStringError{addressError{str: str, key: "ipaddress.error.invalidRange"}} } // switcheroo frontStartIndex, startIndex = startIndex, frontStartIndex frontEndIndex, backEndIndex = backEndIndex, frontEndIndex frontLeadingZeroStartIndex, segmentValueStartIndex = segmentValueStartIndex, frontLeadingZeroStartIndex front, value = value, front extendedFront, extendedValue = extendedValue, extendedFront } if isSingleIPv6 { assign7Attributes4Values1Flags(frontStartIndex, frontEndIndex, frontLeadingZeroStartIndex, startIndex, backEndIndex, segmentValueStartIndex, parseData, segCount, front, extendedFront, value, extendedValue, rangeFlags|keyRangeWildcard, upperRadix) } else { if !frontUppercase && !frontEmpty && !isReversed && !frontIsBinary { if leadingZeroCount == 0 && (flags&keyStandardStr) != 0 && frontIsStandardRangeChar { rangeFlags |= keyStandardRangeStr | keyStandardStr } else { rangeFlags |= keyStandardStr } } assign6Attributes2Values2Flags(frontStartIndex, frontEndIndex, frontLeadingZeroStartIndex, startIndex, backEndIndex, segmentValueStartIndex, parseData, segCount, front, value, rangeFlags|keyRangeWildcard, upperRadix) } rangeWildcardIndex = -1 } else if !noValuesToSet { if isSingleIPv6 { assign3Attributes2Values1Flags(startIndex, index, segmentValueStartIndex, parseData, segCount, value, extendedValue, flags) } else { assign3Attributes1Values1Flags(startIndex, index, segmentValueStartIndex, parseData, segCount, value, flags) } } parseData.incrementSegmentCount() leadingZeroCount = 0 } index++ segmentValueStartIndex = index segmentStartIndex = segmentValueStartIndex // end of IPv6 and MAC48Len segments } // end of all cases } // end of character loop return nil } func (strValidator) validatePrefixLenStr(fullAddr string, version IPVersion) (prefixLen PrefixLen, err addrerr.AddressStringError) { var qualifier parsedHostIdentifierStringQualifier isPrefix, err := validatePrefix(fullAddr, nil, defaultIPAddrParameters, nil, &qualifier, 0, len(fullAddr), version) if !isPrefix { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalidCIDRPrefix"}} } else { prefixLen = qualifier.getNetworkPrefixLen() } return } func parsePortOrService( fullAddr string, zone *Zone, validationOptions addrstrparam.HostNameParams, res *parsedHostIdentifierStringQualifier, index, endIndex int) (err addrerr.AddressStringError) { isPort := true var hasLetter, hasDigits, isAll bool var charCount, digitCount int var port int lastHyphen := -1 charArray := chars for i := index; i < endIndex; i++ { c := fullAddr[i] if c >= '1' && c <= '9' { if isPort { digitCount++ if digitCount > 5 { // 65535 is max isPort = false } else { hasDigits = true port = port*10 + int(charArray[c]) } } charCount++ } else if c == '0' { if isPort && hasDigits { digitCount++ if digitCount > 5 { // 65535 is max isPort = false } else { port *= 10 } } charCount++ } else { //http://www.iana.org/assignments/port-numbers //valid service name chars: //https://tools.ietf.org/html/rfc6335#section-5.1 //https://tools.ietf.org/html/rfc6335#section-10.1 isPort = false isHyphen := c == '-' isAll = c == SegmentWildcard if (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || isHyphen || isAll { if isHyphen { if i == index { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.host.error.invalid.service.hyphen.start"}} return } else if i-1 == lastHyphen { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.host.error.invalid.service.hyphen.consecutive"}} return } else if i == endIndex-1 { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.host.error.invalid.service.hyphen.end"}} return } lastHyphen = i } else if isAll { if i > index { err = &addressStringIndexError{ addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.character.combination.at.index"}}, i} return } else if i+1 < endIndex { err = &addressStringIndexError{ addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.character.combination.at.index"}}, i + 1} return } hasLetter = true charCount++ break } else { hasLetter = true } charCount++ } else { err = &addressStringIndexError{ addressStringError{addressError{str: fullAddr, key: "ipaddress.host.error.invalid.port.service"}}, i} return } } } if isPort { if !validationOptions.AllowsPort() { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.host.error.port"}} return } else if port == 0 { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.host.error.invalidPort.no.digits"}} return } else if port > 65535 { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.host.error.invalidPort.too.large"}} return } res.setZone(zone) res.port = cachePorts(PortInt(port)) return } else if !validationOptions.AllowsService() { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.host.error.service"}} return } else if charCount == 0 { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.host.error.invalidService.no.chars"}} return } else if charCount > 15 { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.host.error.invalidService.too.long"}} return } else if !hasLetter { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.host.error.invalidService.no.letter"}} return } res.setZone(zone) res.service = fullAddr[index:endIndex] return } func parseValidatedPrefix( result BitCount, fullAddr string, zone *Zone, validationOptions addrstrparam.IPAddressStringParams, res *parsedHostIdentifierStringQualifier, digitCount, leadingZeros int, ipVersion IPVersion) (err addrerr.AddressStringError) { if digitCount == 0 { //we know leadingZeroCount is > 0 since we have checked already if there were no characters at all leadingZeros-- // digitCount++ digitCount is unused after this, no need for it to be accurate } asIPv4 := ipVersion.IsIPv4() if asIPv4 { if leadingZeros > 0 && !validationOptions.GetIPv4Params().AllowsPrefixLenLeadingZeros() { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.ipv4.prefix.leading.zeros"}} return } if result > IPv4BitCount { allowPrefixesBeyondAddressSize := validationOptions.GetIPv4Params().AllowsPrefixesBeyondAddressSize() if !allowPrefixesBeyondAddressSize { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.prefixSize"}} return } result = IPv4BitCount } } else { if leadingZeros > 0 && !validationOptions.GetIPv6Params().AllowsPrefixLenLeadingZeros() { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.ipv6.prefix.leading.zeros"}} return } if result > IPv6BitCount { allowPrefixesBeyondAddressSize := validationOptions.GetIPv6Params().AllowsPrefixesBeyondAddressSize() if !allowPrefixesBeyondAddressSize { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.prefixSize"}} return } result = IPv6BitCount } } res.networkPrefixLength = cacheBitCount(result) res.setZone(zone) return } func validatePrefix( fullAddr string, zone *Zone, validationOptions addrstrparam.IPAddressStringParams, hostValidationOptions addrstrparam.HostNameParams, res *parsedHostIdentifierStringQualifier, index, endIndex int, ipVersion IPVersion) (isPrefix bool, err addrerr.AddressStringError) { if index == len(fullAddr) { return } isPrefix = true prefixEndIndex := endIndex hasDigits := false var result BitCount var leadingZeros int charArray := chars for i := index; i < endIndex; i++ { c := fullAddr[i] if c >= '1' && c <= '9' { hasDigits = true result = result*10 + BitCount(charArray[c]) } else if c == '0' { if hasDigits { result *= 10 } else { leadingZeros++ } } else if c == PortSeparator && hostValidationOptions != nil && (hostValidationOptions.AllowsPort() || hostValidationOptions.AllowsService()) && i > index { // check if we have a port or service. If not, possibly an IPv6 mask. // Also, parsing for port first (rather than prefix) allows us to call // parseValidatedPrefix with the knowledge that whatever is supplied can only be a prefix. portErr := parsePortOrService(fullAddr, zone, hostValidationOptions, res, i+1, endIndex) //portQualifier, err = parsePortOrService(fullAddr, zone, hostValidationOptions, res, i+1, endIndex) if portErr != nil { return } prefixEndIndex = i break } else { isPrefix = false break } } //we treat as a prefix if all the characters were digits, even if there were too many, unless the mask options allow for inet_aton single segment if isPrefix { err = parseValidatedPrefix(result, fullAddr, zone, validationOptions, res, prefixEndIndex-index /* digitCount */, leadingZeros, ipVersion) } return } func parseAddressQualifier( fullAddr string, validationOptions addrstrparam.IPAddressStringParams, hostValidationOptions addrstrparam.HostNameParams, ipAddressParseData *ipAddressParseData, endIndex int) (err addrerr.AddressStringError) { qualifierIndex := ipAddressParseData.getQualifierIndex() addressIsEmpty := ipAddressParseData.getAddressParseData().isProvidingEmpty() ipVersion := ipAddressParseData.getProviderIPVersion() res := ipAddressParseData.getQualifier() if ipAddressParseData.hasPrefixSeparator() { return parsePrefix(fullAddr, nil, validationOptions, hostValidationOptions, res, addressIsEmpty, qualifierIndex, endIndex, ipVersion) } else if ipAddressParseData.isZoned() { if ipAddressParseData.isBase85Zoned() && !ipAddressParseData.isProvidingBase85IPv6() { err = &addressStringIndexError{ addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.character.at.index"}}, qualifierIndex - 1} return } if addressIsEmpty { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.only.zone"}} return } return parseZone(fullAddr, validationOptions, res, addressIsEmpty, qualifierIndex, endIndex, ipVersion) } return } func parseHostAddressQualifier( fullAddr string, validationOptions addrstrparam.IPAddressStringParams, hostValidationOptions addrstrparam.HostNameParams, isPrefixed, hasPort bool, ipAddressParseData *ipAddressParseData, qualifierIndex, endIndex int) (err addrerr.AddressStringError) { res := ipAddressParseData.getQualifier() addressIsEmpty := ipAddressParseData.getAddressParseData().isProvidingEmpty() ipVersion := ipAddressParseData.getProviderIPVersion() if isPrefixed { return parsePrefix(fullAddr, nil, validationOptions, hostValidationOptions, res, addressIsEmpty, qualifierIndex, endIndex, ipVersion) } else if ipAddressParseData.isZoned() { if addressIsEmpty { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.only.zone"}} return } return parseEncodedZone(fullAddr, validationOptions, res, addressIsEmpty, qualifierIndex, endIndex, ipVersion) } else if hasPort { //isPort is always false when validating an address return parsePortOrService(fullAddr, nil, hostValidationOptions, res, qualifierIndex, endIndex) } return } func parsePrefix( fullAddr string, zone *Zone, validationOptions addrstrparam.IPAddressStringParams, hostValidationOptions addrstrparam.HostNameParams, res *parsedHostIdentifierStringQualifier, addressIsEmpty bool, index, endIndex int, ipVersion IPVersion) (err addrerr.AddressStringError) { if validationOptions.AllowsPrefix() { var isPrefix bool isPrefix, err = validatePrefix(fullAddr, zone, validationOptions, hostValidationOptions, res, index, endIndex, ipVersion) if err != nil || isPrefix { return } } if addressIsEmpty { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.mask.address.empty"}} } else if validationOptions.AllowsMask() { //check for a mask //check if we need a new validation options for the mask maskOptions := toMaskOptions(validationOptions, ipVersion) pa := &parsedIPAddress{ ipAddressParseData: ipAddressParseData{addressParseData: addressParseData{str: fullAddr}}, options: maskOptions, } err = validateIPAddress(maskOptions, fullAddr, index, endIndex, pa.getIPAddressParseData(), false) if err != nil { err = &addressStringNestedError{ addressStringError: addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalidCIDRPrefixOrMask"}}, nested: err, } return } maskParseData := pa.getAddressParseData() if maskParseData.isProvidingEmpty() { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.mask.empty"}} return } else if maskParseData.isAll() { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.mask.wildcard"}} return } err = checkSegments(fullAddr, maskOptions, pa.getIPAddressParseData()) if err != nil { err = &addressStringNestedError{ addressStringError: addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalidCIDRPrefixOrMask"}}, nested: err, } return } maskEndIndex := maskParseData.getAddressEndIndex() if maskEndIndex != endIndex { // 1.2.3.4/ or 1.2.3.4// or 1.2.3.4/% err = &addressStringIndexError{ addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.mask.extra.chars"}}, maskEndIndex + 1} return } maskVersion := pa.getProviderIPVersion() if maskVersion.IsIPv4() && maskParseData.getSegmentCount() == 1 && !maskParseData.hasWildcard() && !validationOptions.GetIPv4Params().Allows_inet_aton_single_segment_mask() { //1.2.3.4/33 where 33 is an aton_inet single segment address and not a prefix length err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.mask.single.segment"}} return } else if !ipVersion.IsIndeterminate() && (maskVersion.IsIPv4() != ipVersion.IsIPv4() || maskVersion.IsIPv6() != ipVersion.IsIPv6()) { //note that this also covers the cases of non-standard addresses in the mask, ie mask neither ipv4 or ipv6 err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.ipMismatch"}} return } res.mask = pa res.setZone(zone) } else if validationOptions.AllowsPrefix() { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalidCIDRPrefixOrMask"}} } else { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.CIDRNotAllowed"}} } return } func parseHostNameQualifier( fullAddr string, validationOptions addrstrparam.IPAddressStringParams, hostValidationOptions addrstrparam.HostNameParams, res *parsedHostIdentifierStringQualifier, isPrefixed, isPort, // always false for address addressIsEmpty bool, index, endIndex int, ipVersion IPVersion) (err addrerr.AddressStringError) { if isPrefixed { return parsePrefix(fullAddr, nil, validationOptions, hostValidationOptions, res, addressIsEmpty, index, endIndex, ipVersion) } else if isPort { // isPort is always false when validating an address return parsePortOrService(fullAddr, nil, hostValidationOptions, res, index, endIndex) } //res = noQualifier return } // ValidateZoneStr returns an error if the given zone is invalid func ValidateZoneStr(zoneStr string) (zone Zone, err addrerr.AddressStringError) { for i := 0; i < len(zoneStr); i++ { c := zone[i] if c == PrefixLenSeparator { err = &addressStringIndexError{addressStringError{addressError{str: zoneStr, key: "ipaddress.error.invalid.zone"}}, i} return } if c == IPv6SegmentSeparator { err = &addressStringIndexError{addressStringError{addressError{str: zoneStr, key: "ipaddress.error.invalid.zone"}}, i} return } } return Zone(zoneStr), nil } func isReserved(c byte) bool { isUnreserved := (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == RangeSeparator || c == LabelSeparator || c == '_' || c == '~' return !isUnreserved } func parseZone( fullAddr string, validationOptions addrstrparam.IPAddressStringParams, res *parsedHostIdentifierStringQualifier, addressIsEmpty bool, index, endIndex int, ipVersion IPVersion) (err addrerr.AddressStringError) { if index == endIndex && !validationOptions.GetIPv6Params().AllowsEmptyZone() { err = &addressStringIndexError{addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.zone"}}, index} return } for i := index; i < endIndex; i++ { c := fullAddr[i] if c == PrefixLenSeparator { if i == index && !validationOptions.GetIPv6Params().AllowsEmptyZone() { err = &addressStringIndexError{addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.zone"}}, index} return } zone := Zone(fullAddr[index:i]) return parsePrefix(fullAddr, &zone, validationOptions, nil, res, addressIsEmpty, i+1, endIndex, ipVersion) } else if c == IPv6SegmentSeparator { err = &addressStringIndexError{ addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.zone"}}, i} return } } z := Zone(fullAddr[index:endIndex]) res.setZone(&z) return } func parseEncodedZone( fullAddr string, validationOptions addrstrparam.IPAddressStringParams, res *parsedHostIdentifierStringQualifier, addressIsEmpty bool, index, endIndex int, ipVersion IPVersion) (err addrerr.AddressStringError) { if index == endIndex && !validationOptions.GetIPv6Params().AllowsEmptyZone() { err = &addressStringIndexError{addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.zone"}}, index} return } var result strings.Builder var zone string for i := index; i < endIndex; i++ { c := fullAddr[i] //we are in here when we have a square bracketed host like [::1] //not if we have a HostName with no brackets //https://tools.ietf.org/html/rfc6874 //https://tools.ietf.org/html/rfc4007#section-11.7 if c == IPv6ZoneSeparator { if i+2 >= endIndex { err = &addressStringIndexError{ addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.zone.encoding"}}, i} return } //percent encoded if result.Cap() == 0 { result.Grow(endIndex - index) result.WriteString(fullAddr[index:i]) } charArray := chars i++ c = charArray[fullAddr[i]] << 4 i++ c |= charArray[fullAddr[i]] } else if c == PrefixLenSeparator { if i == index && !validationOptions.GetIPv6Params().AllowsEmptyZone() { err = &addressStringIndexError{addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.zone"}}, index} return } if result.Cap() > 0 { zone = result.String() } else { zone = fullAddr[index:i] } z := Zone(zone) return parsePrefix(fullAddr, &z, validationOptions, nil, res, addressIsEmpty, i+1, endIndex, ipVersion) } else if isReserved(c) { err = &addressStringIndexError{ addressStringError{addressError{str: fullAddr, key: "ipaddress.error.invalid.zone"}}, i} return } if result.Cap() > 0 { result.WriteByte(c) } } if result.Len() == 0 { zone = fullAddr[index:endIndex] } else { zone = result.String() } z := Zone(zone) res.setZone(&z) return } // whether no wildcards or range characters allowed func isNoRange(rp addrstrparam.RangeParams) bool { return !rp.AllowsWildcard() && !rp.AllowsRangeSeparator() && !rp.AllowsSingleWildcard() } /** * Some options are not supported in masks (prefix, wildcards, etc) * So we eliminate those options while preserving the others from the address options. * @param validationOptions * @param ipVersion * @return */ func toMaskOptions(validationOptions addrstrparam.IPAddressStringParams, ipVersion IPVersion) (res addrstrparam.IPAddressStringParams) { //We must provide options that do not allow a mask with wildcards or ranges var builder *addrstrparam.IPAddressStringParamsBuilder if ipVersion.IsIndeterminate() || ipVersion.IsIPv6() { ipv6Options := validationOptions.GetIPv6Params() if !isNoRange(ipv6Options.GetRangeParams()) { builder = new(addrstrparam.IPAddressStringParamsBuilder).Set(validationOptions) builder.GetIPv6AddressParamsBuilder().SetRangeParams(addrstrparam.NoRange) } if ipv6Options.AllowsMixed() && !isNoRange(ipv6Options.GetMixedParams().GetIPv4Params().GetRangeParams()) { if builder == nil { builder = new(addrstrparam.IPAddressStringParamsBuilder).Set(validationOptions) } builder.GetIPv6AddressParamsBuilder().SetRangeParams(addrstrparam.NoRange) } } if ipVersion.IsIndeterminate() || ipVersion.IsIPv4() { ipv4Options := validationOptions.GetIPv4Params() if !isNoRange(ipv4Options.GetRangeParams()) { if builder == nil { builder = new(addrstrparam.IPAddressStringParamsBuilder).Set(validationOptions) } builder.GetIPv4AddressParamsBuilder().SetRangeParams(addrstrparam.NoRange) } } if validationOptions.AllowsAll() { if builder == nil { builder = new(addrstrparam.IPAddressStringParamsBuilder).Set(validationOptions) } builder.AllowAll(false) } if builder == nil { res = validationOptions } else { res = builder.ToParams() } return } func assign3Attributes(start, end int, parseData *addressParseData, parsedSegIndex, leadingZeroStartIndex int) { ustart := uint32(start) uend := uint32(end) uleadingZeroStart := uint32(leadingZeroStartIndex) parseData.setIndex(parsedSegIndex, keyLowerStrDigitsIndex, uleadingZeroStart, keyLowerStrStartIndex, ustart, keyLowerStrEndIndex, uend, keyUpperStrDigitsIndex, uleadingZeroStart, keyUpperStrStartIndex, ustart, keyUpperStrEndIndex, uend) } func assign4Attributes(start, end int, parseData *addressParseData, parsedSegIndex, radix, leadingZeroStartIndex int) { ustart := uint32(start) uend := uint32(end) uleadingZeroStart := uint32(leadingZeroStartIndex) parseData.set7IndexFlags(parsedSegIndex, keyLowerRadixIndex, uint32(radix), keyLowerStrDigitsIndex, uleadingZeroStart, keyLowerStrStartIndex, ustart, keyLowerStrEndIndex, uend, keyUpperStrDigitsIndex, uleadingZeroStart, keyUpperStrStartIndex, ustart, keyUpperStrEndIndex, uend) } func assign7Attributes4Values1Flags(frontStart, frontEnd, frontLeadingZeroStartIndex, start, end, leadingZeroStartIndex int, parseData *addressParseData, parsedSegIndex int, frontValue, frontExtendedValue, value, extendedValue uint64, flags uint32, upperRadix uint32) { parseData.set8Index4ValuesFlags(parsedSegIndex, flagsIndex, flags, keyLowerStrDigitsIndex, uint32(frontLeadingZeroStartIndex), keyLowerStrStartIndex, uint32(frontStart), keyLowerStrEndIndex, uint32(frontEnd), keyUpperRadixIndex, uint32(upperRadix), keyUpperStrDigitsIndex, uint32(leadingZeroStartIndex), keyUpperStrStartIndex, uint32(start), keyUpperStrEndIndex, uint32(end), keyLower, frontValue, keyExtendedLower, frontExtendedValue, keyUpper, value, keyExtendedUpper, extendedValue) } func assign6Attributes4Values1Flags(frontStart, frontEnd, frontLeadingZeroStartIndex, start, end, leadingZeroStartIndex int, parseData *addressParseData, parsedSegIndex int, frontValue, frontExtendedValue, value, extendedValue uint64, flags uint32) { parseData.set7Index4ValuesFlags(parsedSegIndex, flagsIndex, flags, keyLowerStrDigitsIndex, uint32(frontLeadingZeroStartIndex), keyLowerStrStartIndex, uint32(frontStart), keyLowerStrEndIndex, uint32(frontEnd), keyUpperStrDigitsIndex, uint32(leadingZeroStartIndex), keyUpperStrStartIndex, uint32(start), keyUpperStrEndIndex, uint32(end), keyLower, frontValue, keyExtendedLower, frontExtendedValue, keyUpper, value, keyExtendedUpper, extendedValue) } func assign6Attributes2Values1Flags(frontStart, frontEnd, frontLeadingZeroStartIndex, start, end, leadingZeroStartIndex int, parseData *addressParseData, parsedSegIndex int, frontValue, value uint64, flags uint32) { parseData.set7Index2ValuesFlags(parsedSegIndex, flagsIndex, flags, keyLowerStrDigitsIndex, uint32(frontLeadingZeroStartIndex), keyLowerStrStartIndex, uint32(frontStart), keyLowerStrEndIndex, uint32(frontEnd), keyUpperStrDigitsIndex, uint32(leadingZeroStartIndex), keyUpperStrStartIndex, uint32(start), keyUpperStrEndIndex, uint32(end), keyLower, frontValue, keyUpper, value) } func assign6Attributes2Values2Flags(frontStart, frontEnd, frontLeadingZeroStartIndex, start, end, leadingZeroStartIndex int, parseData *addressParseData, parsedSegIndex int, frontValue, value uint64, flags /* includes lower radix */ uint32, upperRadix uint32) { parseData.set8Index2ValuesFlags(parsedSegIndex, flagsIndex, flags, keyLowerStrDigitsIndex, uint32(frontLeadingZeroStartIndex), keyLowerStrStartIndex, uint32(frontStart), keyLowerStrEndIndex, uint32(frontEnd), keyUpperRadixIndex, uint32(upperRadix), keyUpperStrDigitsIndex, uint32(leadingZeroStartIndex), keyUpperStrStartIndex, uint32(start), keyUpperStrEndIndex, uint32(end), keyLower, frontValue, keyUpper, value) } func assign3Attributes2Values1Flags(start, end, leadingZeroStart int, parseData *addressParseData, parsedSegIndex int, value, extendedValue uint64, flags uint32) { ustart := uint32(start) uend := uint32(end) uleadingZeroStart := uint32(leadingZeroStart) parseData.set7Index4ValuesFlags(parsedSegIndex, flagsIndex, flags, keyLowerStrDigitsIndex, uleadingZeroStart, keyLowerStrStartIndex, ustart, keyLowerStrEndIndex, uend, keyUpperStrDigitsIndex, uleadingZeroStart, keyUpperStrStartIndex, ustart, keyUpperStrEndIndex, uend, keyLower, value, keyExtendedLower, extendedValue, keyUpper, value, keyExtendedUpper, extendedValue) } func assign3Attributes1Values1Flags(start, end, leadingZeroStart int, parseData *addressParseData, parsedSegIndex int, value uint64, flags uint32) { ustart := uint32(start) uend := uint32(end) uleadingZeroStart := uint32(leadingZeroStart) parseData.set7Index2ValuesFlags(parsedSegIndex, flagsIndex, flags, keyUpperStrDigitsIndex, uleadingZeroStart, keyLowerStrDigitsIndex, uleadingZeroStart, keyUpperStrStartIndex, ustart, keyLowerStrStartIndex, ustart, keyUpperStrEndIndex, uend, keyLowerStrEndIndex, uend, keyLower, value, keyUpper, value) } func isBinaryDelimiter(str string, index int) bool { c := str[index] return c == 'b' || c == 'B' } func isHexDelimiter(c byte) bool { return c == 'x' || c == 'X' } var lowBitsVal uint64 = 0xffffffffffffffff var lowBitsMask = new(big.Int).SetUint64(lowBitsVal) func parseBase85( validationOptions addrstrparam.IPAddressStringParams, str string, strStartIndex, strEndIndex int, ipAddressParseData *ipAddressParseData, extendedRangeWildcardIndex, totalCharacterCount, index int) (bool, addrerr.AddressStringError) { parseData := ipAddressParseData.getAddressParseData() if extendedRangeWildcardIndex < 0 { if totalCharacterCount == ipv6Base85SingleSegmentDigitCount { if !validationOptions.AllowsIPv6() { return false, &addressStringError{addressError{str: str, key: "ipaddress.error.ipv6"}} } ipAddressParseData.setVersion(IPv6) val := parse85(str, strStartIndex, strEndIndex) var lowBits, highBits big.Int lowBits.And(val, lowBitsMask) val.Rsh(val, 64) highBits.And(val, lowBitsMask) val.Rsh(val, 64) //note that even with the correct number of base-85 digits, we can have a value too large if !bigIsZero(val) { return false, &addressStringError{addressError{str: str, key: "ipaddress.error.address.too.large"}} } value, extendedValue := lowBits.Uint64(), highBits.Uint64() parseData.initSegmentData(1) parseData.incrementSegmentCount() assign3Attributes2Values1Flags(strStartIndex, strEndIndex, strStartIndex, parseData, 0, value, extendedValue, 85) ipAddressParseData.setBase85(true) return true, nil } } else { if totalCharacterCount == (ipv6Base85SingleSegmentDigitCount<<1)+len(IPv6AlternativeRangeSeparatorStr) /* two base 85 addresses */ || (totalCharacterCount == ipv6Base85SingleSegmentDigitCount+len(IPv6AlternativeRangeSeparatorStr) && (extendedRangeWildcardIndex == 0 || extendedRangeWildcardIndex+len(IPv6AlternativeRangeSeparatorStr) == strEndIndex)) { /* note that we already check that extendedRangeWildcardIndex is at index 20 */ if !validationOptions.AllowsIPv6() { return false, &addressStringError{addressError{str: str, key: "ipaddress.error.ipv6"}} } ipv6SpecificOptions := validationOptions.GetIPv6Params() if !ipv6SpecificOptions.GetRangeParams().AllowsRangeSeparator() { return false, &addressStringError{addressError{str: str, key: "ipaddress.error.no.range"}} } ipAddressParseData.setVersion(IPv6) frontEndIndex, flags := extendedRangeWildcardIndex, uint32(0) var value, value2, extendedValue, extendedValue2 uint64 var lowerStart, lowerEnd, upperStart, upperEnd int // if frontEndIndex == strStartIndex+ipv6Base85SingleSegmentDigitCount { val := parse85(str, strStartIndex, frontEndIndex) var lowBits, highBits big.Int lowBits.And(val, lowBitsMask) val.Rsh(val, 64) highBits.And(val, lowBitsMask) value, extendedValue = lowBits.Uint64(), highBits.Uint64() if frontEndIndex+len(IPv6AlternativeRangeSeparatorStr) < strEndIndex { val2 := parse85(str, frontEndIndex+len(IPv6AlternativeRangeSeparatorStr), strEndIndex) var lowBits, highBits big.Int lowBits.And(val2, lowBitsMask) val2.Rsh(val2, 64) highBits.And(val2, lowBitsMask) value2, extendedValue2 = lowBits.Uint64(), highBits.Uint64() if val.Cmp(val2) > 0 { if !ipv6SpecificOptions.GetRangeParams().AllowsReverseRange() { return false, &addressStringError{addressError{str: str, key: "ipaddress.error.invalidRange"}} } //note that even with the correct number of base-85 digits, we can have a value too large val.Rsh(val, 64) if !bigIsZero(val) { return false, &addressStringError{addressError{str: str, key: "ipaddress.error.address.too.large"}} } lowerStart = frontEndIndex + len(IPv6AlternativeRangeSeparatorStr) lowerEnd = strEndIndex upperStart = strStartIndex upperEnd = frontEndIndex } else { //note that even with the correct number of base-85 digits, we can have a value too large val2.Rsh(val2, 64) if !bigIsZero(val2) { return false, &addressStringError{addressError{str: str, key: "ipaddress.error.address.too.large"}} } lowerStart = strStartIndex lowerEnd = frontEndIndex upperStart = frontEndIndex + len(IPv6AlternativeRangeSeparatorStr) upperEnd = strEndIndex } } else { if !ipv6SpecificOptions.GetRangeParams().AllowsInferredBoundary() { return false, &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.empty.segment.at.index"}}, extendedRangeWildcardIndex} } lowerStart = strStartIndex lowerEnd = frontEndIndex upperStart = strEndIndex upperEnd = strEndIndex value2 = lowBitsVal extendedValue2 = lowBitsVal flags = keyInferredUpperBoundary } } else if frontEndIndex == 0 { if !ipv6SpecificOptions.GetRangeParams().AllowsInferredBoundary() { return false, &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.empty.segment.at.index"}}, index} } flags = keyInferredLowerBoundary val2 := parse85(str, frontEndIndex+len(IPv6AlternativeRangeSeparatorStr), strEndIndex) var lowBits, highBits big.Int lowBits.And(val2, lowBitsMask) val2.Rsh(val2, 64) highBits.And(val2, lowBitsMask) val2.Rsh(val2, 64) //note that even with the correct number of base-85 digits, we can have a value too large if !bigIsZero(val2) { return false, &addressStringError{addressError{str: str, key: "ipaddress.error.address.too.large"}} } value2, extendedValue2 = lowBits.Uint64(), highBits.Uint64() upperStart = len(IPv6AlternativeRangeSeparatorStr) upperEnd = strEndIndex } else { return false, &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, extendedRangeWildcardIndex} } parseData.incrementSegmentCount() parseData.initSegmentData(1) //parseData.setHasRange(); assign7Attributes4Values1Flags(lowerStart, lowerEnd, lowerStart, upperStart, upperEnd, upperStart, parseData, 0, value, extendedValue, value2, extendedValue2, keyRangeWildcard|85|flags, 85) ipAddressParseData.setBase85(true) return true, nil } } return false, nil } func chooseMACAddressProvider(fromString *MACAddressString, validationOptions addrstrparam.MACAddressStringParams, pa *parsedMACAddress, addressParseData *addressParseData) (res macAddressProvider, err addrerr.AddressStringError) { if addressParseData.isProvidingEmpty() { if validationOptions == defaultMACAddrParameters { res = defaultMACAddressEmptyProvider } else { res = macAddressEmptyProvider{macAddressNullProvider{validationOptions}} } } else if addressParseData.isAll() { if validationOptions == defaultMACAddrParameters { res = macAddressDefaultAllProvider } else { res = &macAddressAllProvider{validationOptions: validationOptions, creationLock: &sync.Mutex{}} } } else { if err = checkMACSegments(fromString.str, validationOptions, pa); err == nil { res = pa } } return } var maskCache = [3][IPv6BitCount + 1]*maskCreator{} var loopbackCache = newEmptyAddrCreator(defaultIPAddrParameters, NoZone) func chooseIPAddressProvider( originator HostIdentifierString, fullAddr string, validationOptions addrstrparam.IPAddressStringParams, parseData *parsedIPAddress) (res ipAddressProvider, err addrerr.AddressStringError) { qualifier := parseData.getQualifier() version := parseData.getProviderIPVersion() if version.IsIndeterminate() { version = qualifier.inferVersion(validationOptions) // checks whether a mask, prefix length, or zone makes the version clear optionsVersion := inferVersion(validationOptions) // checks whether IPv4 or IPv6 is disallowed if version.IsIndeterminate() { version = optionsVersion parseData.setVersion(version) } else if !optionsVersion.IsIndeterminate() && version != optionsVersion { var key string if version.IsIPv6() { key = "ipaddress.error.ipv6" } else { key = "ipaddress.error.ipv4" } err = &addressStringError{addressError{str: fullAddr, key: key}} return } addressParseData := parseData.getAddressParseData() if addressParseData.isProvidingEmpty() { networkPrefixLength := qualifier.getNetworkPrefixLen() if networkPrefixLength != nil { if version.IsIndeterminate() { version = IPVersion(validationOptions.GetPreferredVersion()) } prefLen := networkPrefixLength.bitCount() if validationOptions == defaultIPAddrParameters && prefLen <= IPv6BitCount { index := 0 if version.IsIPv4() { index = 1 } else if version.IsIPv6() { index = 2 } creator := (*maskCreator)(atomicLoadPointer((*unsafe.Pointer)(unsafe.Pointer(&maskCache[index][prefLen])))) if creator == nil { creator = newMaskCreator(defaultIPAddrParameters, version, networkPrefixLength) dataLoc := (*unsafe.Pointer)(unsafe.Pointer(&maskCache[index][prefLen])) atomic.StorePointer(dataLoc, unsafe.Pointer(creator)) } res = creator return } res = newMaskCreator(validationOptions, version, networkPrefixLength) return } else { emptyOpt := validationOptions.EmptyStrParsedAs() if emptyOpt == addrstrparam.LoopbackOption || emptyOpt == addrstrparam.ZeroAddressOption { result := getLoopbackCreator(validationOptions, qualifier) if result != nil { res = result } return } if validationOptions == defaultIPAddrParameters { res = emptyProvider } else { res = &nullProvider{isEmpty: true, ipType: emptyType, params: validationOptions} } return } } else { //isAll // Before reaching here, we already checked whether we allow "all", "allowAll". if version.IsIndeterminate() && // version not inferred, nor is a particular version disallowed validationOptions.AllStrParsedAs() == addrstrparam.AllPreferredIPVersion { preferredVersion := IPVersion(validationOptions.GetPreferredVersion()) if !preferredVersion.IsIndeterminate() { var formatParams addrstrparam.IPAddressStringFormatParams if preferredVersion.IsIPv6() { formatParams = validationOptions.GetIPv6Params() } else { formatParams = validationOptions.GetIPv4Params() } if formatParams.AllowsWildcardedSeparator() { version = preferredVersion } } } res = newAllCreator(qualifier, version, originator, validationOptions) return } } else { if parseData.isZoned() && version.IsIPv4() { err = &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.only.ipv6.has.zone"}} return } if err = checkSegments(fullAddr, validationOptions, parseData.getIPAddressParseData()); err == nil { res = parseData } } return } func inferVersion(params addrstrparam.IPAddressStringParams) IPVersion { if params.AllowsIPv6() { if !params.AllowsIPv4() { return IPv6 } } else if params.AllowsIPv4() { return IPv4 } return IndeterminateIPVersion } func getLoopbackCreator(validationOptions addrstrparam.IPAddressStringParams, qualifier *parsedHostIdentifierStringQualifier) (res *emptyAddrCreator) { zone := qualifier.getZone() defaultParams := defaultIPAddrParameters if validationOptions == defaultParams && zone == NoZone { res = loopbackCache if res == nil { res = newEmptyAddrCreator(defaultIPAddrParameters, NoZone) } return } res = newEmptyAddrCreator(validationOptions, zone) return } func checkSegmentMaxValues( fullAddr string, parseData *addressParseData, segmentIndex int, params addrstrparam.AddressStringFormatParams, maxValue uint64, maxDigitCount, maxUpperDigitCount int) addrerr.AddressStringError { if parseData.getFlag(segmentIndex, keySingleWildcard) { value := parseData.getValue(segmentIndex, keyLower) if value > maxValue { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.ipv4.segment.too.large"}} } if parseData.getValue(segmentIndex, keyUpper) > maxValue { parseData.setValue(segmentIndex, keyUpper, maxValue) } if !params.AllowsUnlimitedLeadingZeros() { lowerRadix := parseData.getRadix(segmentIndex, keyLowerRadixIndex) if parseData.getIndex(segmentIndex, keyLowerStrEndIndex)-parseData.getIndex(segmentIndex, keyLowerStrDigitsIndex)-getStringPrefixCharCount(lowerRadix) > maxDigitCount { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.segment.too.long"}} } } } else { value := parseData.getValue(segmentIndex, keyUpper) if value > maxValue { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.ipv4.segment.too.large"}} } if !params.AllowsUnlimitedLeadingZeros() { lowerRadix := parseData.getRadix(segmentIndex, keyLowerRadixIndex) lowerEndIndex := parseData.getIndex(segmentIndex, keyLowerStrEndIndex) upperEndIndex := parseData.getIndex(segmentIndex, keyUpperStrEndIndex) if lowerEndIndex-parseData.getIndex(segmentIndex, keyLowerStrDigitsIndex)-getStringPrefixCharCount(lowerRadix) > maxDigitCount { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.segment.too.long"}} } if lowerEndIndex != upperEndIndex { upperRadix := parseData.getRadix(segmentIndex, keyUpperRadixIndex) if upperEndIndex-parseData.getIndex(segmentIndex, keyUpperStrDigitsIndex)-getStringPrefixCharCount(upperRadix) > maxUpperDigitCount { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.segment.too.long"}} } } } } return nil } func checkMACSegments( fullAddr string, validationOptions addrstrparam.MACAddressStringParams, parseData *parsedMACAddress) addrerr.AddressStringError { var err addrerr.AddressStringError format := parseData.getFormat() if format != unknownFormat { addressParseData := parseData.getAddressParseData() hasWildcardSeparator := addressParseData.hasWildcard() && validationOptions.GetFormatParams().AllowsWildcardedSeparator() //note that too many segments is checked inside the general parsing method segCount := addressParseData.getSegmentCount() if format == dotted { if segCount <= MediaAccessControlDottedSegmentCount && validationOptions.GetPreferredLen() != addrstrparam.EUI64Len { if !hasWildcardSeparator && segCount != MediaAccessControlDottedSegmentCount { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.too.few.segments"}} } } else if !hasWildcardSeparator && segCount < MediaAccessControlDotted64SegmentCount { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.too.few.segments"}} } else { parseData.setExtended(true) } } else if segCount > 2 { if segCount <= MediaAccessControlSegmentCount && validationOptions.GetPreferredLen() != addrstrparam.EUI64Len { if !hasWildcardSeparator && segCount != MediaAccessControlSegmentCount { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.too.few.segments"}} } } else if !hasWildcardSeparator && segCount < ExtendedUniqueIdentifier64SegmentCount { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.too.few.segments"}} } else { parseData.setExtended(true) } // we do not check char counts in the main parsing code for dashed, because we allow both // aabbcc-ddeeff and aa-bb-cc-dd-ee-ff, so we defer to the check until here if parseData.getFormat() == dashed { for i := 0; i < segCount; i++ { err = checkSegmentMaxValues( fullAddr, addressParseData, i, validationOptions.GetFormatParams(), MACMaxValuePerSegment, MACSegmentMaxChars, MACSegmentMaxChars) if err != nil { return err } } } } else { if parseData.getFormat() == dashed { //for single segment, we have already counted the exact number of hex digits //for double segment, we have already counted the exact number of hex digits in some cases and not others. //Basically, for address like a-b we have already counted the exact number of hex digits, //for addresses starting with a|b- or a-b| we have not, //but rather than figure out which are checked and which are not it's just as quick to check them all here if parseData.isDoubleSegment() { params := validationOptions.GetFormatParams() err = checkSegmentMaxValues(fullAddr, addressParseData, 0, params, macMaxTriple, macDoubleSegmentDigitCount, macDoubleSegmentDigitCount) if err != nil { return err } if parseData.isExtended() { err = checkSegmentMaxValues(fullAddr, addressParseData, 1, params, macMaxQuintuple, macExtendedDoubleSegmentDigitCount, macExtendedDoubleSegmentDigitCount) } else { err = checkSegmentMaxValues(fullAddr, addressParseData, 1, params, macMaxTriple, macDoubleSegmentDigitCount, macDoubleSegmentDigitCount) } if err != nil { return err } } } else if !hasWildcardSeparator { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.too.few.segments"}} } if validationOptions.GetPreferredLen() == addrstrparam.EUI64Len { parseData.setExtended(true) } } } //else single segment return nil } func checkSegments( fullAddr string, validationOptions addrstrparam.IPAddressStringParams, parseData *ipAddressParseData) addrerr.AddressStringError { addressParseData := parseData.getAddressParseData() segCount := addressParseData.getSegmentCount() version := parseData.getProviderIPVersion() if version.IsIPv4() { missingCount := IPv4SegmentCount - segCount ipv4Options := validationOptions.GetIPv4Params() hasWildcardSeparator := addressParseData.hasWildcard() && ipv4Options.AllowsWildcardedSeparator() //single segments are handled in the parsing code with the allowSingleSegment setting if missingCount > 0 && segCount > 1 { if ipv4Options.Allows_inet_aton_joinedSegments() { parseData.set_inet_aton_joined(true) } else if !hasWildcardSeparator { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.ipv4.too.few.segments"}} } } //here we check whether values are too large notUnlimitedLength := !ipv4Options.AllowsUnlimitedLeadingZeros() hasMissingSegs := missingCount > 0 && ipv4Options.Allows_inet_aton_joinedSegments() for i := 0; i < segCount; i++ { var max uint64 if hasMissingSegs && i == segCount-1 { max = getMaxIPv4Value(missingCount + 1) if addressParseData.isInferredUpperBoundary(i) { parseData.setValue(i, keyUpper, max) continue } } else { max = IPv4MaxValuePerSegment } if parseData.getFlag(i, keySingleWildcard) { value := parseData.getValue(i, keyLower) if value > max { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.ipv4.segment.too.large"}} } if parseData.getValue(i, keyUpper) > max { parseData.setValue(i, keyUpper, max) } if notUnlimitedLength { lowerRadix := addressParseData.getRadix(i, keyLowerRadixIndex) maxDigitCount := getMaxIPv4StringLength(missingCount, lowerRadix) if parseData.getIndex(i, keyLowerStrEndIndex)-parseData.getIndex(i, keyLowerStrDigitsIndex)-getStringPrefixCharCount(lowerRadix) > maxDigitCount { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.segment.too.long"}} } } } else { value := parseData.getValue(i, keyUpper) if value > max { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.ipv4.segment.too.large"}} } if notUnlimitedLength { lowerRadix := addressParseData.getRadix(i, keyLowerRadixIndex) maxDigitCount := getMaxIPv4StringLength(missingCount, lowerRadix) lowerEndIndex := parseData.getIndex(i, keyLowerStrEndIndex) upperEndIndex := parseData.getIndex(i, keyUpperStrEndIndex) if lowerEndIndex-parseData.getIndex(i, keyLowerStrDigitsIndex)-getStringPrefixCharCount(lowerRadix) > maxDigitCount { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.segment.too.long"}} } if lowerEndIndex != upperEndIndex { upperRadix := parseData.getRadix(i, keyUpperRadixIndex) maxUpperDigitCount := getMaxIPv4StringLength(missingCount, upperRadix) if upperEndIndex-parseData.getIndex(i, keyUpperStrDigitsIndex)-getStringPrefixCharCount(upperRadix) > maxUpperDigitCount { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.segment.too.long"}} } } } } } } else { totalSegmentCount := segCount if parseData.isProvidingMixedIPv6() { totalSegmentCount += IPv6MixedReplacedSegmentCount } hasWildcardSeparator := addressParseData.hasWildcard() && validationOptions.GetIPv6Params().AllowsWildcardedSeparator() if !hasWildcardSeparator && totalSegmentCount != 1 && totalSegmentCount < IPv6SegmentCount && !parseData.isCompressed() { return &addressStringError{addressError{str: fullAddr, key: "ipaddress.error.too.few.segments"}} } } return nil } func checkSingleWildcard(str string, start, end, digitsEnd int, options addrstrparam.AddressStringFormatParams) addrerr.AddressStringError { _ = start if !options.GetRangeParams().AllowsSingleWildcard() { return &addressStringError{addressError{str: str, key: "ipaddress.error.no.single.wildcard"}} } for k := digitsEnd; k < end; k++ { if str[k] != SegmentSqlSingleWildcard { return &addressStringError{addressError{str: str, key: "ipaddress.error.single.wildcard.order"}} } } return nil } func switchSingleWildcard10(currentValueHex uint64, s string, start, end, numSingleWildcards int, parseData *addressParseData, parsedSegIndex, leadingZeroStartIndex int, options addrstrparam.AddressStringFormatParams) (err addrerr.AddressStringError) { digitsEnd := end - numSingleWildcards err = checkSingleWildcard(s, start, end, digitsEnd, options) if err != nil { return } var lower uint64 if start < digitsEnd { lower, err = switchValue10(currentValueHex, s, digitsEnd-start) if err != nil { return } } var upper uint64 switch numSingleWildcards { case 1: lower *= 10 upper = lower + 9 case 2: lower *= 100 upper = lower + 99 case 3: lower *= 1000 upper = lower + 999 default: power := uint64(math.Pow10(numSingleWildcards)) lower *= power upper = lower + power - 1 } var radix uint32 = 10 assign6Attributes2Values2Flags(start, end, leadingZeroStartIndex, start, end, leadingZeroStartIndex, parseData, parsedSegIndex, lower, upper, keySingleWildcard|radix, radix) return } func switchSingleWildcard2(currentValueHex uint64, s string, start, end, numSingleWildcards int, parseData *addressParseData, parsedSegIndex, leadingZeroStartIndex int, options addrstrparam.AddressStringFormatParams) (err addrerr.AddressStringError) { digitsEnd := end - numSingleWildcards err = checkSingleWildcard(s, start, end, digitsEnd, options) if err != nil { return } var lower, upper uint64 if start < digitsEnd { lower, err = switchValue2(currentValueHex, s, digitsEnd-start) if err != nil { return } } else { lower = 0 } lower <<= uint(numSingleWildcards) switch numSingleWildcards { case 1: upper = lower | 0x1 case 2: upper = lower | 0x3 case 3: upper = lower | 0x7 case 4: upper = lower | 0xf case 5: upper = lower | 0x1f case 6: upper = lower | 0x3f case 7: upper = lower | 0x7f case 8: upper = lower | 0xff case 9: upper = lower | 0x1ff case 10: upper = lower | 0x3ff case 11: upper = lower | 0x7ff case 12: upper = lower | 0xfff case 13: upper = lower | 0x1fff case 14: upper = lower | 0x3fff case 15: upper = lower | 0x7fff case 16: upper = lower | 0xffff default: upper = lower | ^(^uint64(0) << uint(numSingleWildcards)) } var radix uint32 = 2 assign6Attributes2Values2Flags(start, end, leadingZeroStartIndex, start, end, leadingZeroStartIndex, parseData, parsedSegIndex, lower, upper, keySingleWildcard|radix, radix) return } func switchSingleWildcard8(currentValueHex uint64, s string, start, end, numSingleWildcards int, parseData *addressParseData, parsedSegIndex, leadingZeroStartIndex int, options addrstrparam.AddressStringFormatParams) (err addrerr.AddressStringError) { digitsEnd := end - numSingleWildcards err = checkSingleWildcard(s, start, end, digitsEnd, options) if err != nil { return } var lower, upper uint64 if start < digitsEnd { lower, err = switchValue8(currentValueHex, s, digitsEnd-start) if err != nil { return } } switch numSingleWildcards { case 1: lower <<= 3 upper = lower | 07 case 2: lower <<= 6 upper = lower | 077 case 3: lower <<= 9 upper = lower | 0777 default: shift := numSingleWildcards * 3 lower <<= uint(shift) upper = lower | ^(^uint64(0) << uint(shift)) } var radix uint32 = 8 assign6Attributes2Values2Flags(start, end, leadingZeroStartIndex, start, end, leadingZeroStartIndex, parseData, parsedSegIndex, lower, upper, keySingleWildcard|radix, radix) return } func assignSingleWildcard16(lower uint64, s string, start, end, numSingleWildcards int, parseData *addressParseData, parsedSegIndex, leadingZeroStartIndex int, options addrstrparam.AddressStringFormatParams) (err addrerr.AddressStringError) { digitsEnd := end - numSingleWildcards err = checkSingleWildcard(s, start, end, digitsEnd, options) if err != nil { return } shift := numSingleWildcards << 2 lower <<= uint(shift) upper := lower | ^(^uint64(0) << uint(shift)) assign6Attributes2Values1Flags(start, end, leadingZeroStartIndex, start, end, leadingZeroStartIndex, parseData, parsedSegIndex, lower, upper, keySingleWildcard) return } func parseSingleSegmentSingleWildcard16(currentValueHex uint64, s string, start, end, numSingleWildcards int, parseData *addressParseData, parsedSegIndex, leadingZeroStartIndex int, options addrstrparam.AddressStringFormatParams) (err addrerr.AddressStringError) { digitsEnd := end - numSingleWildcards err = checkSingleWildcard(s, start, end, digitsEnd, options) if err != nil { return } var upper, lower, extendedLower, extendedUpper uint64 if numSingleWildcards < longHexDigits { midIndex := end - longHexDigits lower = parseLong16(s, midIndex, digitsEnd) shift := numSingleWildcards << 2 lower <<= uint(shift) upper = lower | ^(^uint64(0) << uint(shift)) extendedLower = parseLong16(s, start, midIndex) extendedUpper = extendedLower } else if numSingleWildcards == longHexDigits { lower = 0 upper = 0xffffffffffffffff extendedUpper = currentValueHex extendedLower = currentValueHex } else { lower = 0 upper = 0xffffffffffffffff extendedLower = currentValueHex shift := (numSingleWildcards - longHexDigits) << 2 extendedLower <<= uint(shift) extendedUpper = extendedLower | ^(^uint64(0) << uint(shift)) } assign6Attributes4Values1Flags(start, end, leadingZeroStartIndex, start, end, leadingZeroStartIndex, parseData, parsedSegIndex, lower, extendedLower, upper, extendedUpper, keySingleWildcard) return } func parseSingleSegmentSingleWildcard2(s string, start, end, numSingleWildcards int, parseData *addressParseData, parsedSegIndex, leadingZeroStartIndex int, options addrstrparam.AddressStringFormatParams) (err addrerr.AddressStringError) { digitsEnd := end - numSingleWildcards err = checkSingleWildcard(s, start, end, digitsEnd, options) if err != nil { return } var upper, lower, extendedLower, extendedUpper uint64 midIndex := end - longBinaryDigits if numSingleWildcards < longBinaryDigits { lower = parseLong2(s, midIndex, digitsEnd) shift := numSingleWildcards lower <<= uint(shift) upper = lower | ^(^uint64(0) << uint(shift)) extendedLower = parseLong2(s, start, midIndex) extendedUpper = extendedLower } else if numSingleWildcards == longBinaryDigits { upper = 0xffffffffffffffff extendedLower = parseLong2(s, start, midIndex) extendedUpper = extendedLower } else { upper = 0xffffffffffffffff shift := numSingleWildcards - longBinaryDigits extendedLower = parseLong2(s, start, midIndex-shift) extendedLower <<= uint(shift) extendedUpper = extendedLower | ^(^uint64(0) << uint(shift)) } assign6Attributes4Values1Flags(start, end, leadingZeroStartIndex, start, end, leadingZeroStartIndex, parseData, parsedSegIndex, lower, extendedLower, upper, extendedUpper, keySingleWildcard) return } //////////////////////// var maxValues = [5]uint64{0, IPv4MaxValuePerSegment, 0xffff, 0xffffff, 0xffffffff} func getMaxIPv4Value(segmentCount int) uint64 { return maxValues[segmentCount] } func getStringPrefixCharCount(radix uint32) int { switch radix { case 10: return 0 case 16: case 2: return 2 default: } return 1 } var maxIPv4StringLen = [9][]int{ //indices are [radix / 2][additionalSegments], and we handle radices 8, 10, 16 {3, 6, 8, 11}, //no radix supplied we treat as octal, the longest {8, 16, 24, 32}, // binary {}, {}, {3, 6, 8, 11}, //octal: 0377, 0177777, 077777777, 037777777777 {IPv4SegmentMaxChars, 5, 8, 10}, //decimal: 255, 65535, 16777215, 4294967295 {}, {}, {2, 4, 6, 8}, //hex: 0xff, 0xffff, 0xffffff, 0xffffffff } func getMaxIPv4StringLength(additionalSegmentsCovered int, radix uint32) int { radixHalved := radix >> 1 if radixHalved < uint32(len(maxIPv4StringLen)) { sl := maxIPv4StringLen[radixHalved] if additionalSegmentsCovered >= 0 && additionalSegmentsCovered < len(sl) { return sl[additionalSegmentsCovered] } } return 0 } func switchValue2(currentHexValue uint64, s string, digitCount int) (result uint64, err addrerr.AddressStringError) { result = 0xf & currentHexValue if result > 1 { err = &addressStringError{addressError{str: s, key: "ipaddress.error.ipv4.invalid.binary.digit"}} return } shift := 0 for digitCount--; digitCount > 0; digitCount-- { shift++ currentHexValue >>= 4 next := 0xf & currentHexValue if next >= 1 { if next == 1 { result |= 1 << uint(shift) } else { err = &addressStringError{addressError{str: s, key: "ipaddress.error.ipv4.invalid.binary.digit"}} return } } } return } /** * The digits were stored as a hex value, this switches them to an octal value. * * @param currentHexValue * @param digitCount * @return */ func switchValue8(currentHexValue uint64, s string, digitCount int) (result uint64, err addrerr.AddressStringError) { result = 0xf & currentHexValue if result >= 8 { err = &addressStringError{addressError{str: s, key: "ipaddress.error.ipv4.invalid.octal.digit"}} return } shift := 0 for digitCount--; digitCount > 0; digitCount-- { shift += 3 currentHexValue >>= 4 next := 0xf & currentHexValue if next >= 8 { err = &addressStringError{addressError{str: s, key: "ipaddress.error.ipv4.invalid.octal.digit"}} return } result |= next << uint(shift) } return } func switchValue10(currentHexValue uint64, s string, digitCount int) (result uint64, err addrerr.AddressStringError) { result = 0xf & currentHexValue if result >= 10 { err = &addressStringError{addressError{str: s, key: "ipaddress.error.ipv4.invalid.decimal.digit"}} return } digitCount-- if digitCount > 0 { factor := uint64(10) for { currentHexValue >>= 4 next := 0xf & currentHexValue if next >= 10 { err = &addressStringError{addressError{str: s, key: "ipaddress.error.ipv4.invalid.decimal.digit"}} return } result += next * factor digitCount-- if digitCount == 0 { break } if factor == 10 { factor = 100 } else if factor == 100 { factor = 1000 } else { factor *= 10 } } } return } func parseLong2(s string, start, end int) uint64 { charArray := chars result := uint64(charArray[s[start]]) for start++; start < end; start++ { c := s[start] if c == '1' { result = (result << 1) | 1 } else { result <<= 1 } } return result } func parseLong8(s string, start, end int) uint64 { charArray := chars result := uint64(charArray[s[start]]) for start++; start < end; start++ { result = (result << 3) | uint64(charArray[s[start]]) } return result } func parseLong10(s string, start, end int) uint64 { charArray := chars result := uint64(charArray[s[start]]) for start++; start < end; start++ { result = (result * 10) + uint64(charArray[s[start]]) } return result } func parseLong16(s string, start, end int) uint64 { charArray := chars result := uint64(charArray[s[start]]) for start++; start < end; start++ { result = (result << 4) | uint64(charArray[s[start]]) } return result } var base85Powers = createBase85Powers() func createBase85Powers() []big.Int { res := make([]big.Int, 10) eightyFive := big.NewInt(85) res[0].SetUint64(1) for i := 1; i < len(res); i++ { res[i].Mul(&res[i-1], eightyFive) } return res } func parse85(s string, start, end int) *big.Int { charArray := extendedChars var result big.Int var last bool for { var partialEnd, power int left := end - start if last = left <= 9; last { partialEnd = end power = left } else { partialEnd = start + 9 power = 9 } var partialResult = uint64(charArray[s[start]]) for start++; start < partialEnd; start++ { next := charArray[s[start]] partialResult = (partialResult * 85) + uint64(next) } result.Mul(&result, &base85Powers[power]).Add(&result, new(big.Int).SetUint64(partialResult)) if last { break } } return &result } //according to rfc 1035 or 952, a label must start with a letter, must end with a letter or digit, and must have in the middle a letter or digit or - //rfc 1123 relaxed that to allow labels to start with a digit, section 2.1 has a discussion on this. It states that the highest level component name must be alphabetic - referring to .com or .net or whatever. //furthermore, the underscore has become generally acceptable, as indicated in rfc 2181 //there is actually a distinction between host names and domain names. a host name is a specific type of domain name identifying hosts. //hosts are not supposed to have the underscore. //en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax //en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names //max length is 63, cannot start or end with hyphen //strictly speaking, the underscore is not allowed anywhere, but it seems that rule is sometimes broken //also, underscores seem to be a part of dns names that are not part of host names, so we allow it here to be safe //networkadminkb.com/KB/a156/windows-2003-dns-and-the-underscore.aspx //It's a little confusing. rfc 2181 https://www.ietf.org/rfc/rfc2181.txt in section 11 on name syntax says that any chars are allowed in dns. //However, it also says internet host names might have restrictions of their own, and this was defined in rfc 1035. //rfc 1035 defines the restrictions on internet host names, in section 2.3.1 http://www.ietf.org/rfc/rfc1035.txt //So we will follow rfc 1035 and in addition allow the underscore. var ( ipvFutureUppercase = byte(unicode.ToUpper(rune(IPvFuture))) defaultEmptyHost = &parsedHost{} ) func (strValidator) validateHostName(fromHost *HostName, validationOptions addrstrparam.HostNameParams) (psdHost *parsedHost, err addrerr.HostNameError) { str := fromHost.str addrLen := len(str) if addrLen > maxHostLength { if addrLen > maxHostLength+1 || str[maxHostLength] != LabelSeparator { err = &hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid.length"}} return } } var segmentUppercase, isNotNormalized, squareBracketed, tryIPv6, tryIPv4, isPrefixed, hasPortOrService, hostIsEmpty bool isAllDigits, isPossiblyIPv6, isPossiblyIPv4 := true, true, true isSpecialOnlyIndex, qualifierIndex, index, lastSeparatorIndex := -1, -1, -1, -1 labelCount := 0 maxLocalLabels := 6 //should be at least 4 to avoid the array for ipv4 addresses var separatorIndices []int var normalizedFlags []bool sep0, sep1, sep2, sep3, sep4, sep5 := -1, -1, -1, -1, -1, -1 var isUpper0, isUpper1, isUpper2, isUpper3, isUpper4, isUpper5 bool var currentChar byte for index++; index <= addrLen; index++ { //grab the character to evaluate if index == addrLen { if index == 0 { hostIsEmpty = true break } segmentCountMatchesIPv4 := isPossiblyIPv4 && (labelCount+1 == IPv4SegmentCount) || (labelCount+1 < IPv4SegmentCount && isSpecialOnlyIndex >= 0) || (labelCount+1 < IPv4SegmentCount && validationOptions.GetIPAddressParams().GetIPv4Params().Allows_inet_aton_joinedSegments()) || labelCount == 0 && validationOptions.GetIPAddressParams().AllowsSingleSegment() if isAllDigits { if isPossiblyIPv4 && segmentCountMatchesIPv4 { tryIPv4 = true break } isPossiblyIPv4 = false if hasPortOrService && isPossiblyIPv6 { //isPossiblyIPv6 is already false if labelCount > 0 //since it is all digits, it cannot be host, so we set tryIPv6 rather than just isPossiblyIPv6 tryIPv6 = true break } err = &hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid"}} return } isPossiblyIPv4 = isPossiblyIPv4 && segmentCountMatchesIPv4 currentChar = LabelSeparator } else { currentChar = str[index] } //check that character //we break out of the loop if we hit '[', '*', '%' (as zone or wildcard), or ':' that is not interpreted as port (and this is ipv6) //we exit the loop prematurely if we hit '/' or ':' interpreted as port if currentChar >= 'a' && currentChar <= 'z' { if currentChar > 'f' { isPossiblyIPv6 = false isPossiblyIPv4 = isPossiblyIPv4 && (currentChar == 'x' && validationOptions.GetIPAddressParams().GetIPv4Params().Allows_inet_aton_hex()) } else if currentChar == 'b' { isPossiblyIPv4 = isPossiblyIPv4 && validationOptions.GetIPAddressParams().GetIPv4Params().AllowsBinary() } isAllDigits = false } else if currentChar >= '0' && currentChar <= '9' { //nothing to do continue } else if currentChar >= 'A' && currentChar <= 'Z' { if currentChar > 'F' { isPossiblyIPv6 = false isPossiblyIPv4 = isPossiblyIPv4 && (currentChar == 'X' && validationOptions.GetIPAddressParams().GetIPv4Params().Allows_inet_aton_hex()) } else if currentChar == 'B' { isPossiblyIPv4 = isPossiblyIPv4 && validationOptions.GetIPAddressParams().GetIPv4Params().AllowsBinary() } segmentUppercase = true isAllDigits = false } else if currentChar == LabelSeparator { length := index - lastSeparatorIndex - 1 if length > maxLabelLength { err = &hostNameError{addressError{str: str, key: "ipaddress.error.segment.too.long"}} return } else if length == 0 { if index < addrLen { err = &hostNameError{addressError{str: str, key: "ipaddress.host.error.segment.too.short"}} return } isPossiblyIPv4 = false isNotNormalized = true } else { if labelCount < maxLocalLabels { if labelCount < 3 { if labelCount == 0 { sep0 = index isUpper0 = segmentUppercase } else if labelCount == 1 { sep1 = index isUpper1 = segmentUppercase } else { sep2 = index isUpper2 = segmentUppercase } } else { if labelCount == 3 { sep3 = index isUpper3 = segmentUppercase } else if labelCount == 4 { sep4 = index isUpper4 = segmentUppercase } else { sep5 = index isUpper5 = segmentUppercase } } labelCount++ } else if labelCount == maxLocalLabels { separatorIndices = make([]int, maxHostSegments+1) separatorIndices[labelCount] = index if validationOptions.NormalizesToLowercase() { normalizedFlags = make([]bool, maxHostSegments+1) normalizedFlags[labelCount] = !segmentUppercase isNotNormalized = isNotNormalized || segmentUppercase } labelCount++ } else { separatorIndices[labelCount] = index if normalizedFlags != nil { normalizedFlags[labelCount] = !segmentUppercase isNotNormalized = isNotNormalized || segmentUppercase } labelCount++ if labelCount > maxHostSegments { err = &hostNameError{addressError{str: str, key: "ipaddress.host.error.too.many.segments"}} return } } segmentUppercase = false //this is per segment so reset it } lastSeparatorIndex = index isPossiblyIPv6 = isPossiblyIPv6 && (index == addrLen) //A '.' means not ipv6 (if we see ':' we jump out of loop so mixed address not possible), but for single segment we end up here even without a '.' character in the string } else if currentChar == '_' { //this is not supported in host names but is supported in domain names, see discussion in HostName isAllDigits = false } else if currentChar == '-' { //host name segments cannot end with '-' if index == lastSeparatorIndex+1 || index == addrLen-1 || str[index+1] == LabelSeparator { err = &hostNameIndexError{hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid.character.at.index"}}, index} return } isAllDigits = false } else if currentChar == IPv6StartBracket { if index == 0 && labelCount == 0 && addrLen > 2 { squareBracketed = true break } err = &hostNameIndexError{hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid.character.at.index"}}, index} return } else if currentChar == PrefixLenSeparator { isPrefixed = true qualifierIndex = index + 1 addrLen = index isNotNormalized = true index-- } else { a := currentChar == SegmentWildcard if a || currentChar == SegmentSqlSingleWildcard { b := !a addressOptions := validationOptions.GetIPAddressParams() if b && addressOptions.GetIPv6Params().AllowsZone() { //if we allow zones, we treat '%' as a zone and not as a wildcard if isPossiblyIPv6 && labelCount < IPv6SegmentCount { tryIPv6 = true isPossiblyIPv4 = false break } err = &hostNameIndexError{hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid.character.at.index"}}, index} return } else { if isPossiblyIPv4 { if addressOptions.GetIPv4Params().GetRangeParams().AllowsWildcard() { if isSpecialOnlyIndex < 0 { isSpecialOnlyIndex = index } } else { isPossiblyIPv4 = false } } if isPossiblyIPv6 && addressOptions.GetIPv6Params().GetRangeParams().AllowsWildcard() { if isSpecialOnlyIndex < 0 { isSpecialOnlyIndex = index } } else { if !isPossiblyIPv4 { //needs to be either ipv4 or ipv6 err = &hostNameIndexError{hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid.character.at.index"}}, index} return } isPossiblyIPv6 = false } } isAllDigits = false } else if currentChar == IPv6SegmentSeparator { //also might denote a port if validationOptions.AllowsPort() || validationOptions.AllowsService() { hasPortOrService = true qualifierIndex = index + 1 addrLen = index //causes loop to terminate, but only after handling the last segment isNotNormalized = true index-- } else { isPossiblyIPv4 = false if isPossiblyIPv6 { tryIPv6 = true break } err = &hostNameIndexError{hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid.character.at.index"}}, index} return } } else if currentChar == AlternativeRangeSeparatorStr[0] { //} else if currentChar == AlternativeRangeSeparator { if index+1 == addrLen { err = &hostNameIndexError{hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid.character.at.index"}}, index} return } currentChar = str[index+1] if currentChar == AlternativeRangeSeparatorStr[1] { isAllDigits = false isPossiblyIPv4 = false isPossiblyIPv6 = false if isSpecialOnlyIndex < 0 { isSpecialOnlyIndex = index } index++ } else { err = &hostNameIndexError{hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid.character.at.index"}}, index} return } } else { err = &hostNameIndexError{hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid.character.at.index"}}, index} return } } } /* 1. squareBracketed: [ addr ] 2. tryIPv4 || tryIPv6: this is a string with characters that invalidate it as a host but it still may in fact be an address This includes ipv6 strings, ipv4/ipv6 strings with '*', or all dot/digit strings like 1.2.3.4 that are 4 segments 3. isPossiblyIPv4: this is a string with digits, - and _ characters and the number of separators matches ipv4. Such strings can also be valid hosts. The range options flag (controlling whether we allow '-' or '_' in addresses) for ipv4 can control whether it is treated as host or address. It also includes "" empty addresses. isPossiblyIPv6: something like f:: or f:1, the former IPv6 and the latter a host "f" with port 1. Such strings can be valid addresses or hosts. If it parses as an address, we do not treat as host. */ psdHost = &parsedHost{originalStr: str, params: validationOptions} addressOptions := validationOptions.GetIPAddressParams() isIPAddress := squareBracketed || tryIPv4 || tryIPv6 if !validationOptions.AllowsIPAddress() { if isIPAddress { err = &hostNameError{addressError{str: str, key: "ipaddress.host.error.ipaddress"}} return } } else if isIPAddress || isPossiblyIPv4 || isPossiblyIPv6 { provider, addrErr, hostErr := func() (provider ipAddressProvider, addrErr addrerr.AddressError, hostErr addrerr.HostNameError) { pa := parsedIPAddress{ ipAddressParseData: ipAddressParseData{addressParseData: addressParseData{str: str}}, options: addressOptions, originator: fromHost, } hostQualifier := psdHost.getQualifier() if squareBracketed { //Note: //Firstly, we need to find the address end which is denoted by the end bracket //Secondly, while zones appear inside bracket, prefix or port appears outside, according to rfc 4038 //So we keep track of the boolean endsWithPrefix to differentiate. endIndex := addrLen - 1 endsWithQualifier := str[endIndex] != IPv6EndBracket if endsWithQualifier { for endIndex--; str[endIndex] != IPv6EndBracket; endIndex-- { if endIndex == 1 { err = &hostNameError{addressError{str: str, key: "ipaddress.host.error.bracketed.missing.end"}} return } } } startIndex := 1 if strings.HasPrefix(str[1:], SmtpIPv6Identifier) { //SMTP rfc 2821 allows [IPv6:ipv6address] startIndex = 6 } else { /* RFC 3986 section 3.2.2 host = IP-literal / IPv4address / reg-name IP-literal = "[" ( IPv6address / IPvFuture ) "]" IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) If a URI containing an IP-literal that starts with "v" (case-insensitive), indicating that the version flag is present, is dereferenced by an application that does not know the meaning of that version flag, then the application should return an appropriate error for "address mechanism not supported". */ firstChar := str[1] if firstChar == IPvFuture || firstChar == ipvFutureUppercase { err = &hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid.mechanism"}} return } } addrErr = validateIPAddress(addressOptions, str, startIndex, endIndex, pa.getIPAddressParseData(), false) if addrErr != nil { return } if endsWithQualifier { //here we check what is in the qualifier that follows the bracket: prefix/mask or port? //if prefix/mask, we supply the qualifier to the address, otherwise we supply it to the host prefixIndex := endIndex + 1 prefixChar := str[prefixIndex] if prefixChar == PrefixLenSeparator { isPrefixed = true } else if prefixChar == PortSeparator { hasPortOrService = true } else { hostErr = &hostNameIndexError{hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid.character.at.index"}}, prefixIndex} return } qualifierIndex = prefixIndex + 1 //skip the ']/' endIndex = len(str) addressParseData := pa.getAddressParseData() addrErr = parseHostNameQualifier( str, addressOptions, validationOptions, hostQualifier, isPrefixed, hasPortOrService, addressParseData.isProvidingEmpty(), qualifierIndex, endIndex, pa.getProviderIPVersion()) if addrErr != nil { return } insideBracketsQualifierIndex := pa.getQualifierIndex() if pa.isZoned() && str[insideBracketsQualifierIndex] == '2' && str[insideBracketsQualifierIndex+1] == '5' { //handle %25 from rfc 6874 insideBracketsQualifierIndex += 2 } addrErr = parseHostAddressQualifier( str, addressOptions, nil, pa.hasPrefixSeparator(), false, pa.getIPAddressParseData(), insideBracketsQualifierIndex, prefixIndex-1) if addrErr != nil { return } if isPrefixed { // since we have an address, we apply the prefix to the address rather than to the host // rather than use the prefix as a host qualifier, we treat it as an address qualifier and leave the host qualifier as noQualifier // also, keep in mind you can combine prefix with zone like fe80::%2/64, see https://tools.ietf.org/html/rfc4007#section-11.7 // if there are two prefix lengths, we choose the smaller (larger network) // if two masks, we combine them (if both network masks, this is the same as choosing smaller prefix) addrQualifier := pa.getIPAddressParseData().getQualifier() addrErr = addrQualifier.merge(hostQualifier) if addrErr != nil { return } hostQualifier.clearPrefixOrMask() // note it makes no sense to indicate a port or service with a prefix } } else { qualifierIndex = pa.getQualifierIndex() isPrefixed = pa.hasPrefixSeparator() hasPortOrService = false if pa.isZoned() && str[qualifierIndex] == '2' && str[qualifierIndex+1] == '5' { //handle %25 from rfc 6874 qualifierIndex += 2 } addrErr = parseHostAddressQualifier(str, addressOptions, validationOptions, isPrefixed, hasPortOrService, pa.getIPAddressParseData(), qualifierIndex, endIndex) if addrErr != nil { return } } //SMTP rfc 2821 allows [ipv4address] version := pa.getProviderIPVersion() if !version.IsIPv6() && !validationOptions.AllowsBracketedIPv4() { err = &hostNameError{addressError{str: str, key: "ipaddress.host.error.bracketed.not.ipv6"}} return } } else { //not square-bracketed /* there are cases where it can be ipv4 or ipv6, but not many any address with a '.' in it cannot be ipv6 at this point (if we hit a ':' first we would have jumped out of the loop) any address with a ':' has gone through tests to see if up until that point it could match an ipv4 address or an ipv6 address it can only be ipv4 if it has right number of segments, and only decimal digits. it can only be ipv6 if it has only hex digits. so when can it be both? if it looks like *: at the start, so that it has the right number of segments for ipv4 but does not have a '.' invalidating ipv6 so in that case we might have either something like *:1 for it to be ipv4 (ambiguous is treated as ipv4) or *:f:: to be ipv6 So we validate the potential port (or ipv6 segment) to determine which one and then go from there Also, if it is single segment address that is all decimal digits. */ // We start by checking if there is potentially a port or service // if IPv6, we may need to try a :x as a port or service and as a trailing segment firstTrySucceeded := false hasAddressPortOrService := false addressQualifierIndex := -1 isPotentiallyIPv6 := isPossiblyIPv6 || tryIPv6 if isPotentiallyIPv6 { //find the last port separator, currently we point to the first one with qualifierIndex //note that the service we find here could be the ipv4 part of either an ipv6 address or ipv6 mask like this 1:2:3:4:5:6:1.2.3.4 or 1:2:3:4:5:6:1.2.3.4/1:2:3:4:5:6:1.2.3.4 if !isPrefixed && (validationOptions.AllowsPort() || validationOptions.AllowsService()) { for j := len(str) - 1; j >= 0; j-- { c := str[j] if c == IPv6SegmentSeparator { hasAddressPortOrService = true addressQualifierIndex = j + 1 } else if (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '-') || (c == SegmentWildcard) { //see validateHostNamePort for more details on valid ports and service names continue } break } } } else { hasAddressPortOrService = hasPortOrService addressQualifierIndex = qualifierIndex } var endIndex int if hasAddressPortOrService { //validate the potential port addrErr = parsePortOrService(str, nil, validationOptions, hostQualifier, addressQualifierIndex, len(str)) if addrErr != nil { //certainly not IPv4 since it doesn't qualify as port (see comment above) if !isPotentiallyIPv6 { //not IPv6 either, so we're done with checking for address return } // no need to call hostQualifier.clear() since parsePortOrService does not populate qualifier on error endIndex = len(str) } else if isPotentiallyIPv6 { //here it can be either a port or part of an IPv6 address, like this: fe80::6a05:caff:fe3:123 expectPort := validationOptions.ExpectsPort() if expectPort { //try with port first, then try as IPv6 no port endIndex = addressQualifierIndex - 1 } else { //try as IPv6 with no port first, try with port second endIndex = len(str) } //first try addrErr = validateIPAddress(addressOptions, str, 0, endIndex, pa.getIPAddressParseData(), false) if addrErr == nil { // Since no square brackets, we parse as an address (this can affect how zones are parsed). // Also, an address cannot end with a single ':' like a port, so we cannot take a shortcut here and parse for port, we must strip it off first (hence no host parameters passed) addrErr = parseAddressQualifier(str, addressOptions, nil, pa.getIPAddressParseData(), endIndex) } if firstTrySucceeded = addrErr == nil; !firstTrySucceeded { pa = parsedIPAddress{ ipAddressParseData: ipAddressParseData{addressParseData: addressParseData{str: str}}, options: addressOptions, originator: fromHost, } if expectPort { // we tried with port first, now we try as IPv6 no port hostQualifier.clearPortOrService() endIndex = len(str) } else { // we tried as IPv6 with no port first, now we try with port second endIndex = addressQualifierIndex - 1 } } else if !expectPort { // it is an address // we tried with no port and succeeded, so clear the port, it was not a port hostQualifier.clearPortOrService() } } else { endIndex = addressQualifierIndex - 1 } } else { endIndex = len(str) } if !firstTrySucceeded { if addrErr = validateIPAddress(addressOptions, str, 0, endIndex, pa.getIPAddressParseData(), false); addrErr == nil { //since no square brackets, we parse as an address (this can affect how zones are parsed) //Also, an address cannot end with a single ':' like a port, so we cannot take a shortcut here and parse for port, we must strip it off first (hence no host parameters passed) addrErr = parseAddressQualifier(str, addressOptions, nil, pa.getIPAddressParseData(), endIndex) } if addrErr != nil { return } } } // we successfully parsed an IP address provider, addrErr = chooseIPAddressProvider(fromHost, str, addressOptions, &pa) return }() if hostErr != nil { err = hostErr return } if addrErr != nil { if isIPAddress { err = &hostAddressNestedError{nested: addrErr} return } psdHost.labelsQualifier.clearPortOrService() //fall though and evaluate as a host } else { psdHost.embeddedAddress.addressProvider = provider return } } hostQualifier := psdHost.getQualifier() addrErr := parseHostNameQualifier( str, addressOptions, validationOptions, hostQualifier, isPrefixed, hasPortOrService, hostIsEmpty, qualifierIndex, len(str), IndeterminateIPVersion) if addrErr != nil { err = &hostAddressNestedError{nested: addrErr} return } if hostIsEmpty { if !validationOptions.AllowsEmpty() { err = &hostNameError{addressError{str: str, key: "ipaddress.host.error.empty"}} return } if *hostQualifier == defaultEmptyHost.labelsQualifier { psdHost = defaultEmptyHost } } else { if labelCount <= maxLocalLabels { maxLocalLabels = labelCount separatorIndices = make([]int, maxLocalLabels) if validationOptions.NormalizesToLowercase() { normalizedFlags = make([]bool, maxLocalLabels) } } else if labelCount != len(separatorIndices) { trimmedSeparatorIndices := make([]int, labelCount) copy(trimmedSeparatorIndices[maxLocalLabels:], separatorIndices[maxLocalLabels:labelCount]) separatorIndices = trimmedSeparatorIndices if normalizedFlags != nil { trimmedNormalizedFlags := make([]bool, labelCount) copy(trimmedNormalizedFlags[maxLocalLabels:], normalizedFlags[maxLocalLabels:labelCount]) normalizedFlags = trimmedNormalizedFlags } } for i := 0; i < maxLocalLabels; i++ { var nextSep int var isUpper bool if i < 2 { if i == 0 { nextSep = sep0 isUpper = isUpper0 } else { nextSep = sep1 isUpper = isUpper1 } } else if i < 4 { if i == 2 { nextSep = sep2 isUpper = isUpper2 } else { nextSep = sep3 isUpper = isUpper3 } } else if i == 4 { nextSep = sep4 isUpper = isUpper4 } else { nextSep = sep5 isUpper = isUpper5 } separatorIndices[i] = nextSep if normalizedFlags != nil { normalizedFlags[i] = !isUpper isNotNormalized = isNotNormalized || isUpper } } //We support a.b.com/24:80 (prefix and port combo) //or just port, or a service where-ever a port can appear //A prefix with port can mean a subnet of addresses using the same port everywhere (the subnet being the prefix block of the resolved address), //or just denote the prefix length of the resolved address along with a port //here we check what is in the qualifier that follows the bracket: prefix/mask or port? //if prefix/mask, we supply the qualifier to the address, otherwise we supply it to the host //also, it is possible the address has a zone var addrQualifier *parsedHostIdentifierStringQualifier if isPrefixed { addrQualifier = hostQualifier } else { addrQualifier = noQualifier } embeddedAddr := checkSpecialHosts(str, addrLen, addrQualifier) hasEmbeddedAddr := embeddedAddr.addressProvider != nil if isSpecialOnlyIndex >= 0 && (!hasEmbeddedAddr || embeddedAddr.addressStringError != nil) { if embeddedAddr.addressStringError != nil { err = &hostAddressNestedError{ hostNameIndexError: hostNameIndexError{ hostNameError: hostNameError{ addressError{str: str, key: "ipaddress.host.error.invalid.character.at.index"}, }, index: isSpecialOnlyIndex, }, nested: embeddedAddr.addressStringError, } return } err = &hostNameIndexError{hostNameError{addressError{str: str, key: "ipaddress.host.error.invalid.character.at.index"}}, isSpecialOnlyIndex} return } psdHost.separatorIndices = separatorIndices psdHost.normalizedFlags = normalizedFlags if !hasEmbeddedAddr { if !isNotNormalized { normalizedLabels := make([]string, len(separatorIndices)) for i, lastSep := 0, -1; i < len(normalizedLabels); i++ { index := separatorIndices[i] normalizedLabels[i] = str[lastSep+1 : index] lastSep = index } psdHost.parsedHostCache = &parsedHostCache{ host: str, normalizedLabels: normalizedLabels, } } } else { if isPrefixed { psdHost.labelsQualifier.clearPrefixOrMask() } psdHost.embeddedAddress = embeddedAddr } } return } var defaultUncOpts = new(addrstrparam.IPAddressStringParamsBuilder). AllowIPv4(false).AllowEmpty(false).AllowMask(false).AllowPrefix(false).ToParams() var reverseDNSIPv4Opts = new(addrstrparam.IPAddressStringParamsBuilder). AllowIPv6(false).AllowEmpty(false).AllowMask(false).AllowPrefix(false). GetIPv4AddressParamsBuilder().Allow_inet_aton(false).GetParentBuilder().ToParams() var reverseDNSIPv6Opts = new(addrstrparam.IPAddressStringParamsBuilder). AllowIPv4(false).AllowEmpty(false).AllowMask(false).AllowPrefix(false). GetIPv6AddressParamsBuilder().AllowMixed(false).AllowZone(false).GetParentBuilder().ToParams() func checkSpecialHosts(str string, addrLen int, hostQualifier *parsedHostIdentifierStringQualifier) (emb embeddedAddress) { suffix := IPv6UncSuffix //note that by using addrLen we are omitting any terminating prefix if addrLen > len(suffix) { suffixStartIndex := addrLen - len(suffix) //get the address for the UNC IPv6 host if strings.EqualFold(str[suffixStartIndex:suffixStartIndex+len(suffix)], suffix) { var builder strings.Builder beginStr := str[:suffixStartIndex] foundZone := false for i := 0; i < len(beginStr); i++ { c := beginStr[i] if c == IPv6UncSegmentSeparator { c = IPv6SegmentSeparator } else if c == IPv6UncRangeSeparatorStr[0] { if i+1 < len(beginStr) { c = beginStr[i+1] if c == IPv6UncRangeSeparatorStr[1] { c = RangeSeparator i++ } } } else if c == IPv6UncZoneSeparator && !foundZone { foundZone = true c = IPv6ZoneSeparator } builder.WriteByte(c) } emb = embeddedAddress{ isUNCIPv6Literal: true, } pa := parsedIPAddress{ options: defaultUncOpts, ipAddressParseData: ipAddressParseData{addressParseData: addressParseData{str: str}}, } var err addrerr.AddressStringError addrStr := builder.String() addrStrLen := len(addrStr) if err = validateIPAddress(defaultUncOpts, addrStr, 0, addrStrLen, pa.getIPAddressParseData(), false); err == nil { if err = parseAddressQualifier(addrStr, defaultUncOpts, nil, pa.getIPAddressParseData(), addrStrLen); err == nil { //if err = parseAddressQualifier(str, defaultUncOpts, nil, pa.getIPAddressParseData(), addrStrLen); err == nil { if *pa.getQualifier() == *noQualifier { *pa.getQualifier() = *hostQualifier } else if *hostQualifier != *noQualifier { _ = pa.getQualifier().merge(hostQualifier) } emb.addressProvider, err = chooseIPAddressProvider(nil, str, defaultUncOpts, &pa) } } emb.addressStringError = err return } } //Note: could support bitstring labels and support subnets in them, however they appear to be generally unused in the real world //RFC 2673 //arpa: https://www.ibm.com/support/knowledgecenter/SSLTBW_1.13.0/com.ibm.zos.r13.halz002/f1a1b3b1220.htm //Also, support partial dns lookups and map then to the associated subnet with prefix length, which I think we may //already do for ipv4 but not for ipv6, ipv4 uses the prefix notation d.c.b.a/x but ipv6 uses fewer nibbles //on the ipv6 side, would just need to add the proper number of zeros and the prefix length suffix3 := IPv6ReverseDnsSuffixDeprecated if addrLen > len(suffix3) { suffix = IPv4ReverseDnsSuffix suffix2 := IPv6ReverseDnsSuffix var isIpv4, isMatch bool suffixStartIndex := addrLen - len(suffix) if isMatch = suffixStartIndex > 0 && strings.EqualFold(str[suffixStartIndex:suffixStartIndex+len(suffix)], suffix); !isMatch { suffixStartIndex = addrLen - len(suffix2) if isMatch = suffixStartIndex > 0 && strings.EqualFold(str[suffixStartIndex:suffixStartIndex+len(suffix2)], suffix2); !isMatch { suffixStartIndex = addrLen - len(suffix3) isMatch = suffixStartIndex > 0 && strings.EqualFold(str[suffixStartIndex:suffixStartIndex+len(suffix3)], suffix3) } } else { isIpv4 = true } if isMatch { emb = embeddedAddress{ isReverseDNS: true, } var err addrerr.AddressStringError var sequence string var params addrstrparam.IPAddressStringParams if isIpv4 { sequence, err = convertReverseDNSIPv4(str, suffixStartIndex) params = reverseDNSIPv4Opts } else { sequence, err = convertReverseDNSIPv6(str, suffixStartIndex) params = reverseDNSIPv6Opts } if err == nil { pa := parsedIPAddress{ options: params, ipAddressParseData: ipAddressParseData{addressParseData: addressParseData{str: sequence}}, } if err = validateIPAddress(params, sequence, 0, len(sequence), pa.getIPAddressParseData(), false); err == nil { if err = parseAddressQualifier(str, params, nil, pa.getIPAddressParseData(), len(str)); err == nil { pa.qualifier = *hostQualifier emb.addressProvider, err = chooseIPAddressProvider(nil, sequence, params, &pa) } } } emb.addressStringError = err } } // //handle TLD host https://tools.ietf.org/html/draft-osamu-v6ops-ipv4-literal-in-url-02 // //https://www.ietf.org/proceedings/87/slides/slides-87-v6ops-6.pdf // suffix = ".v4"; // if(addrLen > suffix.length() && // str.regionMatches(true, suffixStartIndex = addrLen - suffix.length(), suffix, 0, suffix.length())) { // //not an rfc, so let's leave it // } return } //123.2.3.4 is 4.3.2.123.in-addr.arpa. func convertReverseDNSIPv4(str string, suffixStartIndex int) (string, addrerr.AddressStringError) { var builder strings.Builder builder.Grow(suffixStartIndex) segCount := 0 j := suffixStartIndex for i := suffixStartIndex - 1; i > 0; i-- { c1 := str[i] if c1 == IPv4SegmentSeparator { if j-i <= 1 { return "", &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, i} } for k := i + 1; k < j; k++ { builder.WriteByte(str[k]) } builder.WriteByte(c1) j = i segCount++ } } for k := 0; k < j; k++ { builder.WriteByte(str[k]) } if segCount+1 != IPv4SegmentCount { return "", &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, 0} } return builder.String(), nil } //4321:0:1:2:3:4:567:89ab would be b.a.9.8.7.6.5.0.4.0.0.0.3.0.0.0.2.0.0.0.1.0.0.0.0.0.0.0.1.2.3.4.IP6.ARPA func convertReverseDNSIPv6(str string, suffixStartIndex int) (string, addrerr.AddressStringError) { var builder strings.Builder builder.Grow(suffixStartIndex) segCount := 0 for i := suffixStartIndex - 1; i >= 0; { var low, high strings.Builder isRange := false for j := 0; j < 4; j++ { c1 := str[i] i-- if i >= 0 { c2 := str[i] i-- if c2 == IPv4SegmentSeparator { if c1 == SegmentWildcard { isRange = true low.WriteByte('0') high.WriteByte('f') } else { if isRange { return "", &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, i + 1} } low.WriteByte(c1) high.WriteByte(c1) } } else if c2 == RangeSeparator { high.WriteByte(c1) if i >= 1 { c2 = str[i] i-- low.WriteByte(c2) isFullRange := (c2 == '0' && c1 == 'f') if isRange && !isFullRange { return "", &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, i + 1} } c2 = str[i] i-- if c2 != IPv4SegmentSeparator { return "", &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, i + 1} } } else { return "", &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, i} } isRange = true } else { return "", &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, i + 1} } } else if j < 3 { return "", &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, i + 1} } else { if c1 == SegmentWildcard { isRange = true low.WriteByte('0') high.WriteByte('f') } else { if isRange { return "", &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, 0} } low.WriteByte(c1) high.WriteByte(c1) } } } segCount++ if builder.Len() > 0 { builder.WriteByte(IPv6SegmentSeparator) } builder.WriteString(low.String()) if isRange { builder.WriteByte(RangeSeparator) builder.WriteString(high.String()) } } if segCount != IPv6SegmentCount { return "", &addressStringIndexError{ addressStringError{addressError{str: str, key: "ipaddress.error.invalid.character.at.index"}}, 0} } return builder.String(), nil } // // we need to initialize parsing package variables first before using them, so we put these at the bottom of this file var zeroIPAddressString = NewIPAddressString("") var ipv4MappedPrefix = NewIPAddressString("::ffff:0:0/96") ipaddress-go-1.5.4/ipaddr/validator.go000066400000000000000000000027461440250641600176740ustar00rootroot00000000000000// // Copyright 2020-2022 Sean C Foley // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package ipaddr import ( "github.com/seancfoley/ipaddress-go/ipaddr/addrerr" "github.com/seancfoley/ipaddress-go/ipaddr/addrstrparam" ) const ( SmtpIPv6Identifier = "IPv6:" IPvFuture = 'v' ) // Interface for validation and parsing of host identifier strings type hostIdentifierStringValidator interface { validateHostName(fromHost *HostName, validationOptions addrstrparam.HostNameParams) (*parsedHost, addrerr.HostNameError) validateIPAddressStr(fromString *IPAddressString, validationOptions addrstrparam.IPAddressStringParams) (ipAddressProvider, addrerr.AddressStringError) validateMACAddressStr(fromString *MACAddressString, validationOptions addrstrparam.MACAddressStringParams) (macAddressProvider, addrerr.AddressStringError) validatePrefixLenStr(fullAddr string, version IPVersion) (PrefixLen, addrerr.AddressStringError) } var _ hostIdentifierStringValidator = strValidator{}