pax_global_header00006660000000000000000000000064131622132060014506gustar00rootroot0000000000000052 comment=5402765a250372d4dff783e1a69dc3b50987caaf golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/000077500000000000000000000000001316221320600233155ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/.gitignore000066400000000000000000000004441316221320600253070ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof .cover.out* coverage.html golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/GNUmakefile000066400000000000000000000037551316221320600254010ustar00rootroot00000000000000TOOLS= golang.org/x/tools/cover GOCOVER_TMPFILE?= $(GOCOVER_FILE).tmp GOCOVER_FILE?= .cover.out GOCOVERHTML?= coverage.html FIND=`/usr/bin/which 2> /dev/null gfind find | /usr/bin/grep -v ^no | /usr/bin/head -n 1` XARGS=`/usr/bin/which 2> /dev/null gxargs xargs | /usr/bin/grep -v ^no | /usr/bin/head -n 1` test:: $(GOCOVER_FILE) @$(MAKE) -C cmd/sockaddr test cover:: coverage_report $(GOCOVER_FILE):: @${FIND} . -type d ! -path '*cmd*' ! -path '*.git*' -print0 | ${XARGS} -0 -I % sh -ec "cd % && rm -f $(GOCOVER_TMPFILE) && go test -coverprofile=$(GOCOVER_TMPFILE)" @echo 'mode: set' > $(GOCOVER_FILE) @${FIND} . -type f ! -path '*cmd*' ! -path '*.git*' -name "$(GOCOVER_TMPFILE)" -print0 | ${XARGS} -0 -n1 cat $(GOCOVER_TMPFILE) | grep -v '^mode: ' >> ${PWD}/$(GOCOVER_FILE) $(GOCOVERHTML): $(GOCOVER_FILE) go tool cover -html=$(GOCOVER_FILE) -o $(GOCOVERHTML) coverage_report:: $(GOCOVER_FILE) go tool cover -html=$(GOCOVER_FILE) audit_tools:: @go get -u github.com/golang/lint/golint && echo "Installed golint:" @go get -u github.com/fzipp/gocyclo && echo "Installed gocyclo:" @go get -u github.com/remyoudompheng/go-misc/deadcode && echo "Installed deadcode:" @go get -u github.com/client9/misspell/cmd/misspell && echo "Installed misspell:" @go get -u github.com/gordonklaus/ineffassign && echo "Installed ineffassign:" audit:: deadcode go tool vet -all *.go go tool vet -shadow=true *.go golint *.go ineffassign . gocyclo -over 65 *.go misspell *.go clean:: rm -f $(GOCOVER_FILE) $(GOCOVERHTML) dev:: @go build @$(MAKE) -B -C cmd/sockaddr sockaddr install:: @go install @$(MAKE) -C cmd/sockaddr install doc:: @echo Visit: http://127.0.0.1:6161/pkg/github.com/hashicorp/go-sockaddr/ godoc -http=:6161 -goroot $GOROOT world:: @set -e; \ for os in solaris darwin freebsd linux windows; do \ for arch in amd64; do \ printf "Building on %s-%s\n" "$${os}" "$${arch}" ; \ env GOOS="$${os}" GOARCH="$${arch}" go build -o /dev/null; \ done; \ done $(MAKE) -C cmd/sockaddr world golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/LICENSE000066400000000000000000000405251316221320600243300ustar00rootroot00000000000000Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/README.md000066400000000000000000000127751316221320600246100ustar00rootroot00000000000000# go-sockaddr ## `sockaddr` Library Socket address convenience functions for Go. `go-sockaddr` is a convenience library that makes doing the right thing with IP addresses easy. `go-sockaddr` is loosely modeled after the UNIX `sockaddr_t` and creates a union of the family of `sockaddr_t` types (see below for an ascii diagram). Library documentation is available at [https://godoc.org/github.com/hashicorp/go-sockaddr](https://godoc.org/github.com/hashicorp/go-sockaddr). The primary intent of the library was to make it possible to define heuristics for selecting the correct IP addresses when a configuration is evaluated at runtime. See the [docs](https://godoc.org/github.com/hashicorp/go-sockaddr), [`template` package](https://godoc.org/github.com/hashicorp/go-sockaddr/template), tests, and [CLI utility](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr) for details and hints as to how to use this library. For example, with this library it is possible to find an IP address that: * is attached to a default route ([`GetDefaultInterfaces()`](https://godoc.org/github.com/hashicorp/go-sockaddr#GetDefaultInterfaces)) * is contained within a CIDR block ([`IfByNetwork()`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByNetwork)) * is an RFC1918 address ([`IfByRFC("1918")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByRFC)) * is ordered ([`OrderedIfAddrBy(args)`](https://godoc.org/github.com/hashicorp/go-sockaddr#OrderedIfAddrBy) where `args` includes, but is not limited to, [`AscIfType`](https://godoc.org/github.com/hashicorp/go-sockaddr#AscIfType), [`AscNetworkSize`](https://godoc.org/github.com/hashicorp/go-sockaddr#AscNetworkSize)) * excludes all IPv6 addresses ([`IfByType("^(IPv4)$")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByType)) * is larger than a `/32` ([`IfByMaskSize(32)`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByMaskSize)) * is not on a `down` interface ([`ExcludeIfs("flags", "down")`](https://godoc.org/github.com/hashicorp/go-sockaddr#ExcludeIfs)) * preferences an IPv6 address over an IPv4 address ([`SortIfByType()`](https://godoc.org/github.com/hashicorp/go-sockaddr#SortIfByType) + [`ReverseIfAddrs()`](https://godoc.org/github.com/hashicorp/go-sockaddr#ReverseIfAddrs)); and * excludes any IP in RFC6890 address ([`IfByRFC("6890")`](https://godoc.org/github.com/hashicorp/go-sockaddr#IfByRFC)) Or any combination or variation therein. There are also a few simple helper functions such as `GetPublicIP` and `GetPrivateIP` which both return strings and select the first public or private IP address on the default interface, respectively. Similarly, there is also a helper function called `GetInterfaceIP` which returns the first usable IP address on the named interface. ## `sockaddr` CLI Given the possible complexity of the `sockaddr` library, there is a CLI utility that accompanies the library, also called [`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr). The [`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr) utility exposes nearly all of the functionality of the library and can be used either as an administrative tool or testing tool. To install the [`sockaddr`](https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr), run: ```text $ go get -u github.com/hashicorp/go-sockaddr/cmd/sockaddr ``` If you're familiar with UNIX's `sockaddr` struct's, the following diagram mapping the C `sockaddr` (top) to `go-sockaddr` structs (bottom) and interfaces will be helpful: ``` +-------------------------------------------------------+ | | | sockaddr | | SockAddr | | | | +--------------+ +----------------------------------+ | | | sockaddr_un | | | | | | SockAddrUnix | | sockaddr_in{,6} | | | +--------------+ | IPAddr | | | | | | | | +-------------+ +--------------+ | | | | | sockaddr_in | | sockaddr_in6 | | | | | | IPv4Addr | | IPv6Addr | | | | | +-------------+ +--------------+ | | | | | | | +----------------------------------+ | | | +-------------------------------------------------------+ ``` ## Inspiration and Design There were many subtle inspirations that led to this design, but the most direct inspiration for the filtering syntax was OpenBSD's [`pf.conf(5)`](https://www.freebsd.org/cgi/man.cgi?query=pf.conf&apropos=0&sektion=0&arch=default&format=html#PARAMETERS) firewall syntax that lets you select the first IP address on a given named interface. The original problem stemmed from: * needing to create immutable images using [Packer](https://www.packer.io) that ran the [Consul](https://www.consul.io) process (Consul can only use one IP address at a time); * images that may or may not have multiple interfaces or IP addresses at runtime; and * we didn't want to rely on configuration management to render out the correct IP address if the VM image was being used in an auto-scaling group. Instead we needed some way to codify a heuristic that would correctly select the right IP address but the input parameters were not known when the image was created. golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/000077500000000000000000000000001316221320600240605ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/000077500000000000000000000000001316221320600256525ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/.gitignore000066400000000000000000000000201316221320600276320ustar00rootroot00000000000000/sockaddr /bin/ golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/GNUmakefile000066400000000000000000000010241316221320600277210ustar00rootroot00000000000000BIN:=sockaddr SRCS:=$(shell find . -name '*.go' ! -path '*/vendor/*') .DEFAULT_GOAL := dev .PHONY: dev dev: $(BIN) @install $(BIN) ${GOPATH}/bin/ $(BIN): $(SRCS) go build -o $@ .PHONY: clean clean:: rm -f $(BIN) bin/* regression/*.diff rmdir bin/ || true .PHONY: install install:: $(BIN) install sockaddr ${GOPATH}/bin/ .PHONY: test test:: $(BIN) @$(MAKE) -C regression .PHONY: world world:: mkdir -p bin gox -os="solaris darwin freebsd linux windows" -arch="386 amd64 arm" -output="bin/sockaddr_{{.OS}}_{{.Arch}}" . golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/README.md000066400000000000000000000146541316221320600271430ustar00rootroot00000000000000# `sockaddr(1)` `sockaddr` is a CLI utility that wraps and exposes `go-sockaddr` functionality from the command line. ```text $ go get -u github.com/hashicorp/go-sockaddr/cmd/sockaddr ``` ```text % sockaddr -h usage: sockaddr [--version] [--help] [] Available commands are: dump Parses IP addresses eval Evaluates a sockaddr template rfc Test to see if an IP is part of a known RFC version Prints the sockaddr version ``` ## `sockaddr dump` ```text Usage: sockaddr dump [options] input [...] Parse address(es) or interface and dumps various output. Options: -4 Parse the input as IPv4 only -6 Parse the input as IPv6 only -H Machine readable output -I Parse the argument as an interface name -i Parse the input as IP address (either IPv4 or IPv6) -n Show only the value -o Name of an attribute to pass through -u Parse the input as a UNIX Socket only ``` ### `sockaddr dump` example output By default it prints out all available information unless the `-o` flag is specified. ```text % sockaddr dump 127.0.0.2/8 Attribute Value type IPv4 string 127.0.0.2/8 host 127.0.0.2 address 127.0.0.2 port 0 netmask 255.0.0.0 network 127.0.0.0/8 mask_bits 8 binary 01111111000000000000000000000010 hex 7f000002 first_usable 127.0.0.1 last_usable 127.255.255.254 octets 127 0 0 2 size 16777216 broadcast 127.255.255.255 uint32 2130706434 DialPacket "udp4" "" DialStream "tcp4" "" ListenPacket "udp4" "" ListenStream "tcp4" "" $ sockaddr dump -H -o host,address,port -o mask_bits 127.0.0.3:8600 host 127.0.0.3:8600 address 127.0.0.3 port 8600 mask_bits 32 $ sockaddr dump -H -n -o host,address,port -o mask_bits 127.0.0.3:8600 127.0.0.3:8600 127.0.0.3 8600 32 $ sockaddr dump -o type,address,hex,network '[2001:db8::3/32]' Attribute Value type IPv6 address 2001:db8::3 network 2001:db8::/32 hex 20010db8000000000000000000000003 $ sockaddr dump /tmp/example.sock Attribute Value type UNIX string "/tmp/example.sock" path /tmp/example.sock DialPacket "unixgram" "/tmp/example.sock" DialStream "unix" "/tmp/example.sock" ListenPacket "unixgram" "/tmp/example.sock" ListenStream "unix" "/tmp/example.sock" ``` ## `sockaddr eval` ```text Usage: sockaddr eval [options] [template ...] Parse the sockaddr template and evaluates the output. The `sockaddr` library has the potential to be very complex, which is why the `sockaddr` command supports an `eval` subcommand in order to test configurations from the command line. The `eval` subcommand automatically wraps its input with the `{{` and `}}` template delimiters unless the `-r` command is specified, in which case `eval` parses the raw input. If the `template` argument passed to `eval` is a dash (`-`), then `sockaddr eval` will read from stdin and automatically sets the `-r` flag. Options: -d Debug output -n Suppress newlines between args -r Suppress wrapping the input with {{ }} delimiters ``` Here are a few impractical examples to get you started: ```text $ sockaddr eval 'GetAllInterfaces | include "flags" "forwardable" | include "up" | sort "default,type,size" | include "RFC" "6890" | attr "address"' 172.14.6.167 $ sockaddr eval 'GetDefaultInterfaces | sort "type,size" | include "RFC" "6890" | limit 1 | join "address" " "' 172.14.6.167 $ sockaddr eval 'GetPublicIP' 203.0.113.4 $ sockaddr eval 'GetPrivateIP' 172.14.6.167 $ sockaddr eval 'GetInterfaceIP "eth0"' 172.14.6.167 $ sockaddr eval 'GetAllInterfaces | include "network" "172.14.6.0/24" | attr "address"' 172.14.6.167 $ sockaddr eval 'GetPrivateInterfaces | join "type" " "' IPv4 IPv6 $ sockaddr eval 'GetAllInterfaces | include "flags" "forwardable" | join "address" " "' 203.0.113.4 2001:0DB8::1 $ sockaddr eval 'GetAllInterfaces | include "name" "lo0" | include "type" "IPv6" | sort "address" | join "address" " "' 100:: fe80::1 $ sockaddr eval '. | include "rfc" "1918" | print | len | lt 2' true $ sockaddr eval -r '{{with $ifSet := include "name" "lo0" . }}{{ range include "type" "IPv6" $ifSet | sort "address" | reverse}}{{ . }} {{end}}{{end}}' fe80::1/64 {1 16384 lo0 up|loopback|multicast} 100:: {1 16384 lo0 up|loopback|multicast} $ sockaddr eval '. | include "name" "lo0" | include "type" "IPv6" | sort "address" | join "address" " "' 100:: fe80::1 $ cat <<'EOF' | sockaddr eval - {{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | join "address" " "}} EOF 100:: fe80::1 $ sockaddr eval 'GetPrivateInterfaces | include "flags" "forwardable|up" | include "type" "IPv4" | math "network" "+2" | attr "address"' 172.14.6.2 $ cat <<'EOF' | sudo tee -a /etc/profile export CONSUL_HTTP_ADDR="http://`sockaddr eval 'GetInterfaceIP \"eth0\"'`:8500" EOF ``` ## `sockaddr rfc` ```text $ sockaddr rfc Usage: sockaddr rfc [RFC Number] [IP Address] Tests a given IP address to see if it is part of a known RFC. If the IP address belongs to a known RFC, return exit code 0 and print the status. If the IP does not belong to an RFC, return 1. If the RFC is not known, return 2. Options: -s Silent, only return different exit codes $ sockaddr rfc 1918 192.168.1.10 192.168.1.10 is part of RFC 1918 $ sockaddr rfc 6890 '[::1]' 100:: is part of RFC 6890 $ sockaddr rfc list 919 1112 1122 1918 2544 2765 2928 3056 3068 3171 3330 3849 3927 4038 4193 4291 4380 4773 4843 5180 5735 5737 6052 6333 6598 6666 6890 7335 ``` ## `sockaddr tech-support` If one of the helper methods that derives its output from `GetDefaultInterfaces` is misbehaving, submit the output from this command as an issue along with any miscellaneous details that are specific to your environment. ```text Usage: sockaddr tech-support [options] Print out network diagnostic information that can be used by support. The `sockaddr` library relies on OS-specific commands and output which can potentially be brittle. The `tech-support` subcommand emits all of the platform-specific network details required to debug why a given `sockaddr` API call is behaving differently than expected. The `-output` flag controls the output format. The default output mode is Markdown (`md`) however a raw mode (`raw`) is available to obtain the original output. Options: -output Encode the output using one of Markdown ("md") or Raw ("raw") ``` ## `sockaddr version` The lowly version stub. ```text $ sockaddr version sockaddr 0.1.0-dev ``` golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/command/000077500000000000000000000000001316221320600272705ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/command/autohelp.go000066400000000000000000000052171316221320600314450ustar00rootroot00000000000000package command import ( "flag" "fmt" "sort" "strings" wordwrap "github.com/mitchellh/go-wordwrap" "github.com/ryanuber/columnize" ) // AutoHelp specifies the necessary methods required to have their help // completely generated for them. type AutoHelp interface { Usage() string Description() string InitOpts() VisitAllFlags(func(f *flag.Flag)) } // MakeHelp generates a help string based on the capabilities of the Command func MakeHelp(c AutoHelp) string { usageText := c.Usage() // If the length of Usage() is zero, then assume this is a hidden // command. if len(usageText) == 0 { return "" } descriptionText := wordwrap.WrapString(c.Description(), 60) descrLines := strings.Split(descriptionText, "\n") prefixedLines := make([]string, len(descrLines)) for i := range descrLines { prefixedLines[i] = " " + descrLines[i] } descriptionText = strings.Join(prefixedLines, "\n") c.InitOpts() flags := []*flag.Flag{} c.VisitAllFlags(func(f *flag.Flag) { flags = append(flags, f) }) optionsText := OptionsHelpOutput(flags) var helpOutput string switch { case len(optionsText) == 0 && len(descriptionText) == 0: helpOutput = usageText case len(optionsText) == 0: helpOutput = fmt.Sprintf(`Usage: %s %s`, usageText, descriptionText) case len(descriptionText) == 0 && len(optionsText) > 0: helpOutput = fmt.Sprintf(`Usage: %s Options: %s`, usageText, optionsText) default: helpOutput = fmt.Sprintf(`Usage: %s %s Options: %s`, usageText, descriptionText, optionsText) } return strings.TrimSpace(helpOutput) } // ByOptName implements sort.Interface for flag.Flag based on the Name field. type ByName []*flag.Flag func (a ByName) Len() int { return len(a) } func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByName) Less(i, j int) bool { // Bubble up single-char args to the top of the list switch { case len(a[i].Name) == 1 && len(a[j].Name) != 1: return true case len(a[i].Name) != 1 && len(a[j].Name) == 1: return false default: // Case-insensitive sort. Use case as a tie breaker, however. a1 := strings.ToLower(a[i].Name) a2 := strings.ToLower(a[j].Name) if a1 == a2 { return a[i].Name < a[j].Name } else { return a1 < a2 } } } // OptionsHelpOutput returns a string of formatted options func OptionsHelpOutput(flags []*flag.Flag) string { sort.Sort(ByName(flags)) var output []string for _, f := range flags { if len(f.Usage) == 0 { continue } output = append(output, fmt.Sprintf("-%s | %s", f.Name, f.Usage)) } optionsOutput := columnize.Format(output, &columnize.Config{ Delim: "|", Glue: " ", Prefix: " ", Empty: "", }) return optionsOutput } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/command/dump.go000066400000000000000000000163441316221320600305740ustar00rootroot00000000000000package command import ( "flag" "fmt" "github.com/hashicorp/errwrap" sockaddr "github.com/hashicorp/go-sockaddr" "github.com/mitchellh/cli" "github.com/ryanuber/columnize" ) type DumpCommand struct { Ui cli.Ui // attrNames is a list of attribute names to include in the output attrNames []string // flags is a list of options belonging to this command flags *flag.FlagSet // machineMode changes the output format to be machine friendly // (i.e. tab-separated values). machineMode bool // valueOnly changes the output format to include only values valueOnly bool // ifOnly parses the input as an interface name ifOnly bool // ipOnly parses the input as an IP address (either IPv4 or IPv6) ipOnly bool // v4Only parses the input exclusively as an IPv4 address v4Only bool // v6Only parses the input exclusively as an IPv6 address v6Only bool // unixOnly parses the input exclusively as a UNIX Socket unixOnly bool } // Description is the long-form command help. func (c *DumpCommand) Description() string { return `Parse address(es) or interface and dumps various output.` } // Help returns the full help output expected by `sockaddr -h cmd` func (c *DumpCommand) Help() string { return MakeHelp(c) } // InitOpts is responsible for setup of this command's configuration via the // command line. InitOpts() does not parse the arguments (see parseOpts()). func (c *DumpCommand) InitOpts() { c.flags = flag.NewFlagSet("dump", flag.ContinueOnError) c.flags.Usage = func() { c.Ui.Output(c.Help()) } c.flags.BoolVar(&c.machineMode, "H", false, "Machine readable output") c.flags.BoolVar(&c.valueOnly, "n", false, "Show only the value") c.flags.BoolVar(&c.v4Only, "4", false, "Parse the input as IPv4 only") c.flags.BoolVar(&c.v6Only, "6", false, "Parse the input as IPv6 only") c.flags.BoolVar(&c.ifOnly, "I", false, "Parse the argument as an interface name") c.flags.BoolVar(&c.ipOnly, "i", false, "Parse the input as IP address (either IPv4 or IPv6)") c.flags.BoolVar(&c.unixOnly, "u", false, "Parse the input as a UNIX Socket only") c.flags.Var((*MultiArg)(&c.attrNames), "o", "Name of an attribute to pass through") } // Run executes this command. func (c *DumpCommand) Run(args []string) int { if len(args) == 0 { c.Ui.Error(c.Help()) return 1 } c.InitOpts() addrs, err := c.parseOpts(args) if err != nil { if errwrap.Contains(err, "flag: help requested") { return 0 } return 1 } for _, addr := range addrs { var sa sockaddr.SockAddr var ifAddrs sockaddr.IfAddrs var err error switch { case c.v4Only: sa, err = sockaddr.NewIPv4Addr(addr) case c.v6Only: sa, err = sockaddr.NewIPv6Addr(addr) case c.unixOnly: sa, err = sockaddr.NewUnixSock(addr) case c.ipOnly: sa, err = sockaddr.NewIPAddr(addr) case c.ifOnly: ifAddrs, err = sockaddr.GetAllInterfaces() if err != nil { break } ifAddrs, _, err = sockaddr.IfByName(addr, ifAddrs) default: sa, err = sockaddr.NewSockAddr(addr) } if err != nil { c.Ui.Error(fmt.Sprintf("Unable to parse %+q: %v", addr, err)) return 1 } if sa != nil { c.dumpSockAddr(sa) } else if ifAddrs != nil { c.dumpIfAddrs(ifAddrs) } else { panic("bad") } } return 0 } // Synopsis returns a terse description used when listing sub-commands. func (c *DumpCommand) Synopsis() string { return `Parses input as an IP or interface name(s) and dumps various information` } // Usage is the one-line usage description func (c *DumpCommand) Usage() string { return `sockaddr dump [options] input [...]` } // VisitAllFlags forwards the visitor function to the FlagSet func (c *DumpCommand) VisitAllFlags(fn func(*flag.Flag)) { c.flags.VisitAll(fn) } func (c *DumpCommand) dumpIfAddrs(ifAddrs sockaddr.IfAddrs) { for _, ifAddr := range ifAddrs { c.dumpSockAddr(ifAddr.SockAddr) } } func (c *DumpCommand) dumpSockAddr(sa sockaddr.SockAddr) { reservedAttrs := []sockaddr.AttrName{"Attribute"} const maxNumAttrs = 32 output := make([]string, 0, maxNumAttrs+len(reservedAttrs)) allowedAttrs := make(map[sockaddr.AttrName]struct{}, len(c.attrNames)+len(reservedAttrs)) for _, attr := range reservedAttrs { allowedAttrs[attr] = struct{}{} } for _, attr := range c.attrNames { allowedAttrs[sockaddr.AttrName(attr)] = struct{}{} } // allowedAttr returns true if the attribute is allowed to be appended // to the output. allowedAttr := func(k sockaddr.AttrName) bool { if len(allowedAttrs) == len(reservedAttrs) { return true } _, found := allowedAttrs[k] return found } // outFmt is a small helper function to reduce the tedium below. outFmt // returns a new slice and expects the value to already be a string. outFmt := func(o []string, k sockaddr.AttrName, v interface{}) []string { if !allowedAttr(k) { return o } switch { case c.valueOnly: return append(o, fmt.Sprintf("%s", v)) case !c.valueOnly && c.machineMode: return append(o, fmt.Sprintf("%s\t%s", k, v)) case !c.valueOnly && !c.machineMode: fallthrough default: return append(o, fmt.Sprintf("%s | %s", k, v)) } } if !c.machineMode { output = outFmt(output, "Attribute", "Value") } // Attributes for all SockAddr types for _, attr := range sockaddr.SockAddrAttrs() { output = outFmt(output, attr, sockaddr.SockAddrAttr(sa, attr)) } // Attributes for all IP types (both IPv4 and IPv6) if sa.Type()&sockaddr.TypeIP != 0 { ip := *sockaddr.ToIPAddr(sa) for _, attr := range sockaddr.IPAttrs() { output = outFmt(output, attr, sockaddr.IPAddrAttr(ip, attr)) } } if sa.Type() == sockaddr.TypeIPv4 { ipv4 := *sockaddr.ToIPv4Addr(sa) for _, attr := range sockaddr.IPv4Attrs() { output = outFmt(output, attr, sockaddr.IPv4AddrAttr(ipv4, attr)) } } if sa.Type() == sockaddr.TypeIPv6 { ipv6 := *sockaddr.ToIPv6Addr(sa) for _, attr := range sockaddr.IPv6Attrs() { output = outFmt(output, attr, sockaddr.IPv6AddrAttr(ipv6, attr)) } } if sa.Type() == sockaddr.TypeUnix { us := *sockaddr.ToUnixSock(sa) for _, attr := range sockaddr.UnixSockAttrs() { output = outFmt(output, attr, sockaddr.UnixSockAttr(us, attr)) } } // Developer-focused arguments { arg1, arg2 := sa.DialPacketArgs() output = outFmt(output, "DialPacket", fmt.Sprintf("%+q %+q", arg1, arg2)) } { arg1, arg2 := sa.DialStreamArgs() output = outFmt(output, "DialStream", fmt.Sprintf("%+q %+q", arg1, arg2)) } { arg1, arg2 := sa.ListenPacketArgs() output = outFmt(output, "ListenPacket", fmt.Sprintf("%+q %+q", arg1, arg2)) } { arg1, arg2 := sa.ListenStreamArgs() output = outFmt(output, "ListenStream", fmt.Sprintf("%+q %+q", arg1, arg2)) } result := columnize.SimpleFormat(output) c.Ui.Output(result) } // parseOpts is responsible for parsing the options set in InitOpts(). Returns // a list of non-parsed flags. func (c *DumpCommand) parseOpts(args []string) ([]string, error) { if err := c.flags.Parse(args); err != nil { return nil, err } conflictingOptsCount := 0 if c.v4Only { conflictingOptsCount++ } if c.v6Only { conflictingOptsCount++ } if c.unixOnly { conflictingOptsCount++ } if c.ifOnly { conflictingOptsCount++ } if c.ipOnly { conflictingOptsCount++ } if conflictingOptsCount > 1 { return nil, fmt.Errorf("Conflicting options specified, only one parsing mode may be specified at a time") } return c.flags.Args(), nil } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/command/eval.go000066400000000000000000000077731316221320600305640ustar00rootroot00000000000000package command import ( "bytes" "flag" "fmt" "io" "os" "strings" "github.com/hashicorp/errwrap" "github.com/hashicorp/go-sockaddr/template" "github.com/mitchellh/cli" ) type EvalCommand struct { Ui cli.Ui // debugOutput emits framed output vs raw output. debugOutput bool // flags is a list of options belonging to this command flags *flag.FlagSet // rawInput disables wrapping the string in the text/template {{ }} // handlebars. rawInput bool // suppressNewline changes whether or not there's a newline between each // arg passed to the eval subcommand. suppressNewline bool } // Description is the long-form command help. func (c *EvalCommand) Description() string { return `Parse the sockaddr template and evaluates the output. ` + "The `sockaddr` library has the potential to be very complex, which is why the " + "`sockaddr` command supports an `eval` subcommand in order to test configurations " + "from the command line. The `eval` subcommand automatically wraps its input with " + "the `{{` and `}}` template delimiters unless the `-r` command is specified, in " + "which case `eval` parses the raw input. If the `template` argument passed to " + "`eval` is a dash (`-`), then `sockaddr eval` will read from stdin and " + "automatically sets the `-r` flag." } // Help returns the full help output expected by `sockaddr -h cmd` func (c *EvalCommand) Help() string { return MakeHelp(c) } // InitOpts is responsible for setup of this command's configuration via the // command line. InitOpts() does not parse the arguments (see parseOpts()). func (c *EvalCommand) InitOpts() { c.flags = flag.NewFlagSet("eval", flag.ContinueOnError) c.flags.Usage = func() { c.Ui.Output(c.Help()) } c.flags.BoolVar(&c.debugOutput, "d", false, "Debug output") c.flags.BoolVar(&c.suppressNewline, "n", false, "Suppress newlines between args") c.flags.BoolVar(&c.rawInput, "r", false, "Suppress wrapping the input with {{ }} delimiters") } // Run executes this command. func (c *EvalCommand) Run(args []string) int { if len(args) == 0 { c.Ui.Error(c.Help()) return 1 } c.InitOpts() tmpls, err := c.parseOpts(args) if err != nil { if errwrap.Contains(err, "flag: help requested") { return 0 } return 1 } inputs, outputs := make([]string, len(tmpls)), make([]string, len(tmpls)) var rawInput, readStdin bool for i, in := range tmpls { if readStdin { break } rawInput = c.rawInput if in == "-" { rawInput = true var f io.Reader = os.Stdin var buf bytes.Buffer if _, err := io.Copy(&buf, f); err != nil { c.Ui.Error(fmt.Sprintf("[ERROR]: Error reading from stdin: %v", err)) return 1 } in = buf.String() if len(in) == 0 { return 0 } readStdin = true } inputs[i] = in if !rawInput { in = `{{` + in + `}}` inputs[i] = in } out, err := template.Parse(in) if err != nil { c.Ui.Error(fmt.Sprintf("ERROR[%d] in: %q\n[%d] msg: %v\n", i, in, i, err)) return 1 } outputs[i] = out } if c.debugOutput { for i, out := range outputs { c.Ui.Output(fmt.Sprintf("[%d] in: %q\n[%d] out: %q\n", i, inputs[i], i, out)) if i != len(outputs)-1 { if c.debugOutput { c.Ui.Output(fmt.Sprintf("---\n")) } } } } else { sep := "\n" if c.suppressNewline { sep = "" } c.Ui.Output(strings.Join(outputs, sep)) } return 0 } // Synopsis returns a terse description used when listing sub-commands. func (c *EvalCommand) Synopsis() string { return `Evaluates a sockaddr template` } // Usage is the one-line usage description func (c *EvalCommand) Usage() string { return `sockaddr eval [options] [template ...]` } // VisitAllFlags forwards the visitor function to the FlagSet func (c *EvalCommand) VisitAllFlags(fn func(*flag.Flag)) { c.flags.VisitAll(fn) } // parseOpts is responsible for parsing the options set in InitOpts(). Returns // a list of non-parsed flags. func (c *EvalCommand) parseOpts(args []string) ([]string, error) { if err := c.flags.Parse(args); err != nil { return nil, err } return c.flags.Args(), nil } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/command/multi_arg.go000066400000000000000000000004321316221320600316010ustar00rootroot00000000000000package command import "regexp" type MultiArg []string func (v *MultiArg) String() string { return "" } func (v *MultiArg) Set(raw string) error { parts := regexp.MustCompile(`[\s]*,[\s]*`).Split(raw, -1) for _, part := range parts { *v = append(*v, part) } return nil } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/command/rfc.go000066400000000000000000000063631316221320600304010ustar00rootroot00000000000000package command import ( "flag" "fmt" "strconv" "github.com/hashicorp/errwrap" sockaddr "github.com/hashicorp/go-sockaddr" "github.com/mitchellh/cli" ) type RFCCommand struct { Ui cli.Ui // flags is a list of options belonging to this command flags *flag.FlagSet // silentMode prevents any output and only returns exit code 1 when the // IP address is NOT a member of the known RFC. Unknown RFCs return a // status code of 2. silentMode bool } // Description is the long-form command help. func (c *RFCCommand) Description() string { return `Tests a given IP address to see if it is part of a known RFC. If the IP address belongs to a known RFC, return exit code 0 and print the status. If the IP does not belong to an RFC, return 1. If the RFC is not known, return 2.` } // Help returns the full help output expected by `sockaddr -h cmd` func (c *RFCCommand) Help() string { return MakeHelp(c) } // InitOpts is responsible for setup of this command's configuration via the // command line. InitOpts() does not parse the arguments (see parseOpts()). func (c *RFCCommand) InitOpts() { c.flags = flag.NewFlagSet("rfc", flag.ContinueOnError) c.flags.Usage = func() { c.Ui.Output(c.Help()) } c.flags.BoolVar(&c.silentMode, "s", false, "Silent, only return different exit codes") } // Run executes this command. func (c *RFCCommand) Run(args []string) int { if len(args) == 0 { c.Ui.Error(c.Help()) return 1 } c.InitOpts() unprocessedArgs, err := c.parseOpts(args) if err != nil { if errwrap.Contains(err, "flag: help requested") { return 0 } return 1 } switch numArgs := len(unprocessedArgs); { case numArgs != 2 && numArgs != 0: c.Ui.Error(`ERROR: Need an RFC Number and an IP address to test.`) c.Ui.Error(c.Help()) fallthrough case numArgs == 0: return 1 } // Parse the RFC Number rfcNum, err := strconv.ParseUint(unprocessedArgs[0], 10, 32) if err != nil { c.Ui.Error(fmt.Sprintf("ERROR: Invalid RFC Number %+q: %v", unprocessedArgs[0], err)) return 2 } // Parse the IP address ipAddr, err := sockaddr.NewIPAddr(unprocessedArgs[1]) if err != nil { c.Ui.Error(fmt.Sprintf("ERROR: Invalid IP address %+q: %v", unprocessedArgs[1], err)) return 3 } switch inRFC := sockaddr.IsRFC(uint(rfcNum), ipAddr); { case inRFC && !c.silentMode: c.Ui.Output(fmt.Sprintf("%s is part of RFC %d", ipAddr, rfcNum)) fallthrough case inRFC: return 0 case !inRFC && !c.silentMode: c.Ui.Output(fmt.Sprintf("%s is not part of RFC %d", ipAddr, rfcNum)) fallthrough case !inRFC: return 1 default: panic("bad") } } // Synopsis returns a terse description used when listing sub-commands. func (c *RFCCommand) Synopsis() string { return `Test to see if an IP is part of a known RFC` } // Usage is the one-line usage description func (c *RFCCommand) Usage() string { return `sockaddr rfc [RFC Number] [IP Address]` } // VisitAllFlags forwards the visitor function to the FlagSet func (c *RFCCommand) VisitAllFlags(fn func(*flag.Flag)) { c.flags.VisitAll(fn) } // parseOpts is responsible for parsing the options set in InitOpts(). Returns // a list of non-parsed flags. func (c *RFCCommand) parseOpts(args []string) ([]string, error) { if err := c.flags.Parse(args); err != nil { return nil, err } return c.flags.Args(), nil } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/command/rfc_list.go000066400000000000000000000041311316221320600314230ustar00rootroot00000000000000package command import ( "flag" "fmt" "sort" "github.com/hashicorp/errwrap" sockaddr "github.com/hashicorp/go-sockaddr" "github.com/mitchellh/cli" ) type RFCListCommand struct { Ui cli.Ui // flags is a list of options belonging to this command flags *flag.FlagSet } // Description is the long-form command help. func (c *RFCListCommand) Description() string { return `Lists all known RFCs.` } // Help returns the full help output expected by `sockaddr -h cmd` func (c *RFCListCommand) Help() string { return MakeHelp(c) } // InitOpts is responsible for setup of this command's configuration via the // command line. InitOpts() does not parse the arguments (see parseOpts()). func (c *RFCListCommand) InitOpts() { c.flags = flag.NewFlagSet("list", flag.ContinueOnError) c.flags.Usage = func() { c.Ui.Output(c.Help()) } } type rfcNums []uint func (s rfcNums) Len() int { return len(s) } func (s rfcNums) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s rfcNums) Less(i, j int) bool { return s[i] < s[j] } // Run executes this command. func (c *RFCListCommand) Run(args []string) int { if len(args) != 0 { c.Ui.Error(c.Help()) return 1 } c.InitOpts() _, err := c.parseOpts(args) if err != nil { if errwrap.Contains(err, "flag: help requested") { return 0 } return 1 } var rfcs rfcNums sockaddr.VisitAllRFCs(func(rfcNum uint, sas sockaddr.SockAddrs) { rfcs = append(rfcs, rfcNum) }) sort.Sort(rfcs) for _, rfcNum := range rfcs { c.Ui.Output(fmt.Sprintf("%d", rfcNum)) } return 0 } // Synopsis returns a terse description used when listing sub-commands. func (c *RFCListCommand) Synopsis() string { return `Lists all known RFCs` } // Usage is the one-line usage description func (c *RFCListCommand) Usage() string { return `sockaddr rfc list` } // VisitAllFlags forwards the visitor function to the FlagSet func (c *RFCListCommand) VisitAllFlags(fn func(*flag.Flag)) { c.flags.VisitAll(fn) } func (c *RFCListCommand) parseOpts(args []string) ([]string, error) { if err := c.flags.Parse(args); err != nil { return nil, err } return c.flags.Args(), nil } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/command/tech_support.go000066400000000000000000000132001316221320600323320ustar00rootroot00000000000000package command import ( "flag" "fmt" "net" "os/exec" "runtime" "github.com/hashicorp/errwrap" sockaddr "github.com/hashicorp/go-sockaddr" "github.com/mitchellh/cli" ) type TechSupportCommand struct { Ui cli.Ui // outputMode controls the type of output encoding. outputMode string // flags is a list of options belonging to this command flags *flag.FlagSet } // Description is the long-form command help. func (c *TechSupportCommand) Description() string { return `Print out network diagnostic information that can be used by support. ` + "The `sockaddr` library relies on OS-specific commands and output which can potentially be " + "brittle. The `tech-support` subcommand emits all of the platform-specific " + "network details required to debug why a given `sockaddr` API call is behaving " + "differently than expected. The `-output` flag controls the output format. " + "The default output mode is Markdown (`md`) however a raw mode (`raw`) is " + "available to obtain the original output." } // Help returns the full help output expected by `sockaddr -h cmd` func (c *TechSupportCommand) Help() string { return MakeHelp(c) } // InitOpts is responsible for setup of this command's configuration via the // command line. InitOpts() does not parse the arguments (see parseOpts()). func (c *TechSupportCommand) InitOpts() { c.flags = flag.NewFlagSet("tech-support", flag.ContinueOnError) c.flags.Usage = func() { c.Ui.Output(c.Help()) } c.flags.StringVar(&c.outputMode, "output", "md", `Encode the output using one of Markdown ("md") or Raw ("raw")`) } // Run executes this command. func (c *TechSupportCommand) Run(args []string) int { c.InitOpts() rest, err := c.parseOpts(args) if err != nil { if errwrap.Contains(err, "flag: help requested") { return 0 } return 1 } if len(rest) != 0 { c.Ui.Error(c.Help()) return 1 } ri, err := sockaddr.NewRouteInfo() if err != nil { c.Ui.Error(fmt.Sprintf("error loading route information: %v", err)) return 1 } const initNumCmds = 4 type cmdResult struct { cmd []string out string } output := make(map[string]cmdResult, initNumCmds) ri.VisitCommands(func(name string, cmd []string) { out, err := exec.Command(cmd[0], cmd[1:]...).Output() if err != nil { out = []byte(fmt.Sprintf("ERROR: command %q failed: %v", name, err)) } output[name] = cmdResult{ cmd: cmd, out: string(out), } }) out := c.rowWriterOutputFactory() for cmdName, result := range output { switch c.outputMode { case "md": c.Ui.Output(fmt.Sprintf("## cmd: `%s`", cmdName)) c.Ui.Output("") c.Ui.Output(fmt.Sprintf("Command: `%#v`", result.cmd)) c.Ui.Output("```") c.Ui.Output(result.out) c.Ui.Output("```") c.Ui.Output("") case "raw": c.Ui.Output(fmt.Sprintf("cmd: %q: %#v", cmdName, result.cmd)) c.Ui.Output("") c.Ui.Output(result.out) c.Ui.Output("") default: c.Ui.Error(fmt.Sprintf("Unsupported output type: %q", c.outputMode)) return 1 } out("s", "GOOS", runtime.GOOS) out("s", "GOARCH", runtime.GOARCH) out("s", "Compiler", runtime.Compiler) out("s", "Version", runtime.Version()) ifs, err := net.Interfaces() if err != nil { out("v", "net.Interfaces", err) } else { for i, intf := range ifs { out("s", fmt.Sprintf("net.Interfaces[%d].Name", i), intf.Name) out("s", fmt.Sprintf("net.Interfaces[%d].Flags", i), intf.Flags) out("+v", fmt.Sprintf("net.Interfaces[%d].Raw", i), intf) addrs, err := intf.Addrs() if err != nil { out("v", fmt.Sprintf("net.Interfaces[%d].Addrs", i), err) } else { for j, addr := range addrs { out("s", fmt.Sprintf("net.Interfaces[%d].Addrs[%d]", i, j), addr) } } } } } return 0 } // Synopsis returns a terse description used when listing sub-commands. func (c *TechSupportCommand) Synopsis() string { return `Dumps diagnostic information about a platform's network` } // Usage is the one-line usage description func (c *TechSupportCommand) Usage() string { return `sockaddr tech-support [options]` } // VisitAllFlags forwards the visitor function to the FlagSet func (c *TechSupportCommand) VisitAllFlags(fn func(*flag.Flag)) { c.flags.VisitAll(fn) } // parseOpts is responsible for parsing the options set in InitOpts(). Returns // a list of non-parsed flags. func (c *TechSupportCommand) parseOpts(args []string) ([]string, error) { if err := c.flags.Parse(args); err != nil { return nil, err } switch c.outputMode { case "md", "markdown": c.outputMode = "md" case "raw": default: return nil, fmt.Errorf(`Invalid output mode %q, supported output types are "md" (default) and "raw"`, c.outputMode) } return c.flags.Args(), nil } func (c *TechSupportCommand) rowWriterOutputFactory() func(valueVerb, key string, val interface{}) { type _Fmt string type _Verb string var lineNoFmt string var keyVerb _Verb var fmtMap map[_Verb]_Fmt switch c.outputMode { case "md": lineNoFmt = "%02d." keyVerb = "s" fmtMap = map[_Verb]_Fmt{ "s": "`%s`", "-s": "%s", "v": "`%v`", "+v": "`%#v`", } case "raw": lineNoFmt = "%02d:" keyVerb = "-s" fmtMap = map[_Verb]_Fmt{ "s": "%q", "-s": "%s", "v": "%v", "+v": "%#v", } default: panic(fmt.Sprintf("Unsupported output type: %q", c.outputMode)) } var count int return func(valueVerb, key string, val interface{}) { count++ keyFmt, ok := fmtMap[keyVerb] if !ok { panic(fmt.Sprintf("Invalid key verb: %q", keyVerb)) } valFmt, ok := fmtMap[_Verb(valueVerb)] if !ok { panic(fmt.Sprintf("Invalid value verb: %q", valueVerb)) } outputModeFmt := fmt.Sprintf("%s %s:\t%s", lineNoFmt, keyFmt, valFmt) c.Ui.Output(fmt.Sprintf(outputModeFmt, count, key, val)) } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/command/version.go000066400000000000000000000007171316221320600313110ustar00rootroot00000000000000package command import ( "fmt" "github.com/mitchellh/cli" ) // VersionCommand is a Command implementation prints the version. type VersionCommand struct { HumanVersion string Ui cli.Ui } func (c *VersionCommand) Help() string { return "" } func (c *VersionCommand) Run(_ []string) int { c.Ui.Output(fmt.Sprintf("sockaddr %s", c.HumanVersion)) return 0 } func (c *VersionCommand) Synopsis() string { return "Prints the sockaddr version" } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/commands.go000066400000000000000000000017411316221320600300050ustar00rootroot00000000000000package main import ( "os" "github.com/hashicorp/go-sockaddr/cmd/sockaddr/command" "github.com/mitchellh/cli" ) // Commands is the mapping of all the available CLI commands. var Commands map[string]cli.CommandFactory func init() { ui := &cli.BasicUi{Writer: os.Stdout} Commands = map[string]cli.CommandFactory{ "dump": func() (cli.Command, error) { return &command.DumpCommand{ Ui: ui, }, nil }, "eval": func() (cli.Command, error) { return &command.EvalCommand{ Ui: ui, }, nil }, "rfc": func() (cli.Command, error) { return &command.RFCCommand{ Ui: ui, }, nil }, "rfc list": func() (cli.Command, error) { return &command.RFCListCommand{ Ui: ui, }, nil }, "tech-support": func() (cli.Command, error) { return &command.TechSupportCommand{ Ui: ui, }, nil }, "version": func() (cli.Command, error) { return &command.VersionCommand{ HumanVersion: GetHumanVersion(), Ui: ui, }, nil }, } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/main.go000066400000000000000000000014101316221320600271210ustar00rootroot00000000000000package main import ( "fmt" "io/ioutil" "log" "os" "github.com/mitchellh/cli" ) func main() { os.Exit(realMain()) } func realMain() int { log.SetOutput(ioutil.Discard) // Get the command line args. We shortcut "--version" and "-v" to just // show the version. args := os.Args[1:] for _, arg := range args { if arg == "--" { break } if arg == "-v" || arg == "--version" { newArgs := make([]string, len(args)+1) newArgs[0] = "version" copy(newArgs[1:], args) args = newArgs break } } cli := &cli.CLI{ Args: args, Commands: Commands, HelpFunc: cli.BasicHelpFunc("sockaddr"), } exitCode, err := cli.Run() if err != nil { fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err.Error()) return 1 } return exitCode } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/000077500000000000000000000000001316221320600300325ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/.gitignore000066400000000000000000000000101316221320600320110ustar00rootroot00000000000000/*.diff golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/GNUmakefile000066400000000000000000000002031316221320600320770ustar00rootroot00000000000000.DEFAULT_GOAL := test clean:: rm -f *.diff *.out test:: @rm -f *.diff @./run_all.sh @printf "All tests ran successfully.\n" golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expected/000077500000000000000000000000001316221320600316335ustar00rootroot00000000000000sockaddr.out000066400000000000000000000006351316221320600341030ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedUsage: sockaddr [--version] [--help] [] Available commands are: dump Parses input as an IP or interface name(s) and dumps various information eval Evaluates a sockaddr template rfc Test to see if an IP is part of a known RFC tech-support Dumps diagnostic information about a platform's network version Prints the sockaddr version sockaddr_-v.out000066400000000000000000000000231316221320600344740ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedsockaddr 0.1.0-dev sockaddr_dump-00-help.out000066400000000000000000000006571316221320600362770ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedUsage: sockaddr dump [options] input [...] Parse address(es) or interface and dumps various output. Options: -4 Parse the input as IPv4 only -6 Parse the input as IPv6 only -H Machine readable output -I Parse the argument as an interface name -i Parse the input as IP address (either IPv4 or IPv6) -n Show only the value -o Name of an attribute to pass through -u Parse the input as a UNIX Socket only sockaddr_dump-01.out000066400000000000000000000010131316221320600353350ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedAttribute Value type IPv4 string 127.0.0.1 host 127.0.0.1 address 127.0.0.1 port 0 netmask 255.255.255.255 network 127.0.0.1 mask_bits 32 binary 01111111000000000000000000000001 hex 7f000001 first_usable 127.0.0.1 last_usable 127.0.0.1 octets 127 0 0 1 size 1 broadcast 127.0.0.1 uint32 2130706433 DialPacket "udp4" "" DialStream "tcp4" "" ListenPacket "udp4" "127.0.0.1:0" ListenStream "tcp4" "127.0.0.1:0" sockaddr_dump-02.out000066400000000000000000000010031316221320600353350ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedAttribute Value type IPv4 string 127.0.0.2/8 host 127.0.0.2 address 127.0.0.2 port 0 netmask 255.0.0.0 network 127.0.0.0 mask_bits 8 binary 01111111000000000000000000000010 hex 7f000002 first_usable 127.0.0.1 last_usable 127.255.255.254 octets 127 0 0 2 size 16777216 broadcast 127.255.255.255 uint32 2130706434 DialPacket "udp4" "" DialStream "tcp4" "" ListenPacket "udp4" "" ListenStream "tcp4" "" sockaddr_dump-03.out000066400000000000000000000013161316221320600353450ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedAttribute Value type IPv6 string 2001:db8::3 host 2001:db8::3 address 2001:db8::3 port 0 netmask ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff network 2001:db8::3 mask_bits 128 binary 00100000000000010000110110111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011 hex 20010db8000000000000000000000003 first_usable 2001:db8::3 last_usable 2001:db8::3 octets 32 1 13 184 0 0 0 0 0 0 0 0 0 0 0 3 size 1 uint128 42540766411282592856903984951653826563 DialPacket "udp6" "" DialStream "tcp6" "" ListenPacket "udp6" "[2001:db8::3]:0" ListenStream "tcp6" "[2001:db8::3]:0" sockaddr_dump-04.out000066400000000000000000000013031316221320600353420ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedAttribute Value type IPv6 string 2001:db8::4/64 host 2001:db8::4 address 2001:db8::4 port 0 netmask ffff:ffff:ffff:ffff:: network 2001:db8:: mask_bits 64 binary 00100000000000010000110110111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100 hex 20010db8000000000000000000000004 first_usable 2001:db8:: last_usable 2001:db8::ffff:ffff:ffff:ffff octets 32 1 13 184 0 0 0 0 0 0 0 0 0 0 0 4 size 18446744073709551616 uint128 42540766411282592856903984951653826564 DialPacket "udp6" "" DialStream "tcp6" "" ListenPacket "udp6" "" ListenStream "tcp6" "" sockaddr_dump-05.out000066400000000000000000000003671316221320600353540ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedAttribute Value type UNIX string "/tmp/example" path /tmp/example DialPacket "unixgram" "/tmp/example" DialStream "unix" "/tmp/example" ListenPacket "unixgram" "/tmp/example" ListenStream "unix" "/tmp/example" sockaddr_dump-06.out000066400000000000000000000013731316221320600353530ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedAttribute Value type IPv6 string [2001:db8::6]:22 host [2001:db8::6]:22 address 2001:db8::6 port 22 netmask ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff network 2001:db8::6 mask_bits 128 binary 00100000000000010000110110111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000110 hex 20010db8000000000000000000000006 first_usable 2001:db8::6 last_usable 2001:db8::6 octets 32 1 13 184 0 0 0 0 0 0 0 0 0 0 0 6 size 1 uint128 42540766411282592856903984951653826566 DialPacket "udp6" "[2001:db8::6]:22" DialStream "tcp6" "[2001:db8::6]:22" ListenPacket "udp6" "[2001:db8::6]:22" ListenStream "tcp6" "[2001:db8::6]:22" sockaddr_dump-07.out000066400000000000000000000011751316221320600353540ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedtype IPv6 string [2001:db8::7]:22 host [2001:db8::7]:22 address 2001:db8::7 port 22 netmask ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff network 2001:db8::7 mask_bits 128 binary 00100000000000010000110110111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111 hex 20010db8000000000000000000000007 first_usable 2001:db8::7 last_usable 2001:db8::7 octets 32 1 13 184 0 0 0 0 0 0 0 0 0 0 0 7 size 1 uint128 42540766411282592856903984951653826567 DialPacket "udp6" "[2001:db8::7]:22" DialStream "tcp6" "[2001:db8::7]:22" ListenPacket "udp6" "[2001:db8::7]:22" ListenStream "tcp6" "[2001:db8::7]:22" sockaddr_dump-08.out000066400000000000000000000000751316221320600353530ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedAttribute Value type IPv6 string [2001:db8::8]:22 sockaddr_dump-09.out000066400000000000000000000000341316221320600353470ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedValue IPv6 [2001:db8::8]:22 sockaddr_dump-10.out000066400000000000000000000000261316221320600353400ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedIPv6 [2001:db8::8]:22 sockaddr_dump-11.out000066400000000000000000000031351316221320600353450ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedAttribute Value type IPv4 string 192.168.0.1 host 192.168.0.1 address 192.168.0.1 port 0 netmask 255.255.255.255 network 192.168.0.1 mask_bits 32 binary 11000000101010000000000000000001 hex c0a80001 first_usable 192.168.0.1 last_usable 192.168.0.1 octets 192 168 0 1 size 1 broadcast 192.168.0.1 uint32 3232235521 DialPacket "udp4" "" DialStream "tcp4" "" ListenPacket "udp4" "192.168.0.1:0" ListenStream "tcp4" "192.168.0.1:0" Attribute Value type IPv4 string 192.168.0.1 host 192.168.0.1 address 192.168.0.1 port 0 netmask 255.255.255.255 network 192.168.0.1 mask_bits 32 binary 11000000101010000000000000000001 hex c0a80001 first_usable 192.168.0.1 last_usable 192.168.0.1 octets 192 168 0 1 size 1 broadcast 192.168.0.1 uint32 3232235521 DialPacket "udp4" "" DialStream "tcp4" "" ListenPacket "udp4" "192.168.0.1:0" ListenStream "tcp4" "192.168.0.1:0" Attribute Value type IPv4 string 192.168.0.1 host 192.168.0.1 address 192.168.0.1 port 0 netmask 255.255.255.255 network 192.168.0.1 mask_bits 32 binary 11000000101010000000000000000001 hex c0a80001 first_usable 192.168.0.1 last_usable 192.168.0.1 octets 192 168 0 1 size 1 broadcast 192.168.0.1 uint32 3232235521 DialPacket "udp4" "" DialStream "tcp4" "" ListenPacket "udp4" "192.168.0.1:0" ListenStream "tcp4" "192.168.0.1:0" sockaddr_dump-12.out000066400000000000000000000030601316221320600353430ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedAttribute Value type IPv4 string 192.168.0.1/16 host 192.168.0.1 address 192.168.0.1 port 0 netmask 255.255.0.0 network 192.168.0.0 mask_bits 16 binary 11000000101010000000000000000001 hex c0a80001 first_usable 192.168.0.1 last_usable 192.168.255.254 octets 192 168 0 1 size 65536 broadcast 192.168.255.255 uint32 3232235521 DialPacket "udp4" "" DialStream "tcp4" "" ListenPacket "udp4" "" ListenStream "tcp4" "" Attribute Value type IPv4 string 192.168.0.1/16 host 192.168.0.1 address 192.168.0.1 port 0 netmask 255.255.0.0 network 192.168.0.0 mask_bits 16 binary 11000000101010000000000000000001 hex c0a80001 first_usable 192.168.0.1 last_usable 192.168.255.254 octets 192 168 0 1 size 65536 broadcast 192.168.255.255 uint32 3232235521 DialPacket "udp4" "" DialStream "tcp4" "" ListenPacket "udp4" "" ListenStream "tcp4" "" Attribute Value type IPv4 string 192.168.0.1/16 host 192.168.0.1 address 192.168.0.1 port 0 netmask 255.255.0.0 network 192.168.0.0 mask_bits 16 binary 11000000101010000000000000000001 hex c0a80001 first_usable 192.168.0.1 last_usable 192.168.255.254 octets 192 168 0 1 size 65536 broadcast 192.168.255.255 uint32 3232235521 DialPacket "udp4" "" DialStream "tcp4" "" ListenPacket "udp4" "" ListenStream "tcp4" "" sockaddr_dump-13.out000066400000000000000000000034161316221320600353510ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedAttribute Value type IPv4 string 0.0.0.0/1 host 0.0.0.0 address 0.0.0.0 port 0 netmask 128.0.0.0 network 0.0.0.0 mask_bits 1 binary 00000000000000000000000000000000 hex 00000000 first_usable 0.0.0.1 last_usable 127.255.255.254 octets 0 0 0 0 size 2147483648 broadcast 127.255.255.255 uint32 0 DialPacket "udp4" "" DialStream "tcp4" "" ListenPacket "udp4" "" ListenStream "tcp4" "" Unable to parse "0:0:0:0:0:0::/97": Unable to convert 0:0:0:0:0:0::/97 to an IPv4 address Attribute Value type IPv6 string ::/97 host :: address :: port 0 netmask ffff:ffff:ffff:ffff:ffff:ffff:8000:0 network :: mask_bits 97 binary 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 hex 00000000000000000000000000000000 first_usable :: last_usable ::7fff:ffff octets 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 size 2147483648 uint128 0 DialPacket "udp6" "" DialStream "tcp6" "" ListenPacket "udp6" "" ListenStream "tcp6" "" Attribute Value type IPv6 string ::/97 host :: address :: port 0 netmask ffff:ffff:ffff:ffff:ffff:ffff:8000:0 network :: mask_bits 97 binary 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 hex 00000000000000000000000000000000 first_usable :: last_usable ::7fff:ffff octets 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 size 2147483648 uint128 0 DialPacket "udp6" "" DialStream "tcp6" "" ListenPacket "udp6" "" ListenStream "tcp6" "" sockaddr_dump-14.out000066400000000000000000000001231316221320600353420ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedUnable to parse "::c0a8:1": Unable to string convert "::c0a8:1" to an IPv4 address sockaddr_eval-00-help.out000066400000000000000000000013541316221320600362540ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedUsage: sockaddr eval [options] [template ...] Parse the sockaddr template and evaluates the output. The `sockaddr` library has the potential to be very complex, which is why the `sockaddr` command supports an `eval` subcommand in order to test configurations from the command line. The `eval` subcommand automatically wraps its input with the `{{` and `}}` template delimiters unless the `-r` command is specified, in which case `eval` parses the raw input. If the `template` argument passed to `eval` is a dash (`-`), then `sockaddr eval` will read from stdin and automatically sets the `-r` flag. Options: -d Debug output -n Suppress newlines between args -r Suppress wrapping the input with {{ }} delimiters sockaddr_eval-01.out000066400000000000000000000002141316221320600353210ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expected[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast} ::1 {1 16384 lo0 up|loopback|multicast} fe80::1/64 {1 16384 lo0 up|loopback|multicast}] sockaddr_eval-02.out000066400000000000000000000004301316221320600353220ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expected[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast} ::1 {1 16384 lo0 up|loopback|multicast} fe80::1/64 {1 16384 lo0 up|loopback|multicast}] [127.0.0.1/8 {1 16384 lo0 up|loopback|multicast} ::1 {1 16384 lo0 up|loopback|multicast} fe80::1/64 {1 16384 lo0 up|loopback|multicast}] sockaddr_eval-03.out000066400000000000000000000000141316221320600353210ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expected::1 fe80::1 sockaddr_eval-04.out000066400000000000000000000002151316221320600353250ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expected[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast} ::1 {1 16384 lo0 up|loopback|multicast} fe80::1/64 {1 16384 lo0 up|loopback|multicast}] sockaddr_eval-05.out000066400000000000000000000000271316221320600353270ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedup|broadcast|multicast sockaddr_rfc-00.out000066400000000000000000000006061316221320600351500ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedUsage: sockaddr rfc [RFC Number] [IP Address] Tests a given IP address to see if it is part of a known RFC. If the IP address belongs to a known RFC, return exit code 0 and print the status. If the IP does not belong to an RFC, return 1. If the RFC is not known, return 2. Options: -s Silent, only return different exit codes Subcommands: list Lists all known RFCs sockaddr_rfc-01.out000066400000000000000000000006061316221320600351510ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedUsage: sockaddr rfc [RFC Number] [IP Address] Tests a given IP address to see if it is part of a known RFC. If the IP address belongs to a known RFC, return exit code 0 and print the status. If the IP does not belong to an RFC, return 1. If the RFC is not known, return 2. Options: -s Silent, only return different exit codes Subcommands: list Lists all known RFCs sockaddr_rfc_list-00.out000066400000000000000000000000621316221320600361770ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedUsage: sockaddr rfc list Lists all known RFCs. sockaddr_rfc_list-01.out000066400000000000000000000002131316221320600361760ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expected919 1112 1122 1918 2544 2765 2928 3056 3068 3171 3330 3849 3927 4038 4193 4291 4380 4773 4843 5180 5735 5737 6052 6333 6598 6666 6890 7335 sockaddr_rfc_list-02.out000066400000000000000000000000621316221320600362010ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedUsage: sockaddr rfc list Lists all known RFCs. sockaddr_version-00.out000066400000000000000000000000231316221320600360540ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedsockaddr 0.1.0-dev sockaddr_version-01.out000066400000000000000000000000231316221320600360550ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/expectedsockaddr 0.1.0-dev golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/run_all.sh000077500000000000000000000011741316221320600320300ustar00rootroot00000000000000#!/bin/sh -- FIND=`/usr/bin/which 2> /dev/null gfind find | /usr/bin/grep -v ^no | /usr/bin/head -n 1` XARGS=`/usr/bin/which 2> /dev/null gxargs xargs | /usr/bin/grep -v ^no | /usr/bin/head -n 1` set -e set -u num_cpus=$(getconf NPROCESSORS_ONLN) set +e ${FIND} . -maxdepth 1 -name 'test_*.sh' -print0 | ${XARGS} -0 -n1 -P${num_cpus} ./run_one.sh set -e # rune_one.sh generates the .diff files diffs=$(find . -name '*.diff') if [ -z "${diffs}" ]; then exit 0 fi printf "The following tests failed (check the respective .diff file for details):\n\n" for d in ${diffs}; do printf "\t%s\n" "$(basename ${d} .diff)" done exit 1 golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression/run_one.sh000077500000000000000000000024441316221320600320420ustar00rootroot00000000000000#!/bin/sh -e -u -- set -e set -u verbose="" if [ "$1" = "-v" ]; then verbose="true" shift fi if [ $# -ne 1 ]; then printf "Usage: %s [ test script ]\n\n" "$(basename $0)" printf "ERROR: Need a single test script to execute\n" exit 1 fi # chdir(2) to the directory where the script resides cd "$(dirname "$0")" exact_name="$(basename ${1} .sh)" test_name="$(echo ${exact_name} | sed -e s@^test_@@)" test_script="${exact_name}.sh" test_out="${test_name}.out" expected_out="expected/${test_name}.out" if [ ! -r "${test_script}" ]; then printf "ERROR: Test script %s does not exist\n" "${test_script}" exit 2 fi if [ -n "${verbose}" ]; then cat "${test_script}" | tail -n 1 fi set +e "./${test_script}" > "${test_out}" 2>&1 if [ ! -r "${expected_out}" ]; then printf "ERROR: Expected test output (%s) does not exist\n" "${expected_out}" exit 2 fi cmp -s "${expected_out}" "${test_out}" result=$? set -e if [ "${result}" -eq 0 ]; then if [ -n "${verbose}" ]; then cat "${test_out}" fi rm -f "${test_out}" exit 0 fi diff_out="${test_name}.diff" set +e diff -u "${test_out}" "${expected_out}" > "${diff_out}" set -e # If run as an interactive TTY, pass along the diff to the caller if [ -t 0 -o -n "${verbose}" ]; then cat "${diff_out}" fi exit 1 test_sockaddr.sh000077500000000000000000000000601316221320600331370ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr test_sockaddr_dump-00-help.sh000077500000000000000000000000651316221320600353340ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr dump test_sockaddr_dump-01.sh000077500000000000000000000000771316221320600344120ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr dump 127.0.0.1 test_sockaddr_dump-02.sh000077500000000000000000000001011316221320600343770ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr dump 127.0.0.2/8 test_sockaddr_dump-03.sh000077500000000000000000000001051316221320600344040ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr dump '[2001:db8::3]' test_sockaddr_dump-04.sh000077500000000000000000000001061316221320600344060ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr dump '2001:db8::4/64' test_sockaddr_dump-05.sh000077500000000000000000000001021316221320600344030ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr dump /tmp/example test_sockaddr_dump-06.sh000077500000000000000000000001101316221320600344030ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr dump '[2001:db8::6]:22' test_sockaddr_dump-07.sh000077500000000000000000000001131316221320600344070ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr dump -H '[2001:db8::7]:22' test_sockaddr_dump-08.sh000077500000000000000000000001271316221320600344150ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr dump -o string,type '[2001:db8::8]:22' test_sockaddr_dump-09.sh000077500000000000000000000001321316221320600344120ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr dump -n -o string,type '[2001:db8::8]:22' test_sockaddr_dump-10.sh000077500000000000000000000001351316221320600344050ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr dump -H -n -o string,type '[2001:db8::8]:22' test_sockaddr_dump-11.sh000077500000000000000000000003141316221320600344050ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 # Verified via: cat sockaddr_dump-11.out | sort | uniq -c ../sockaddr dump '192.168.0.1' ../sockaddr dump '::ffff:192.168.0.1' ../sockaddr dump '0:0:0:0:0:ffff:192.168.0.1' test_sockaddr_dump-12.sh000077500000000000000000000003271316221320600344120ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 # Verified via: cat sockaddr_dump-12.out | sort | uniq -c ../sockaddr dump '192.168.0.1/16' ../sockaddr dump '::ffff:192.168.0.1/112' ../sockaddr dump '0:0:0:0:0:ffff:192.168.0.1/112' test_sockaddr_dump-13.sh000077500000000000000000000005321316221320600344110ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- exec 2>&1 # This should succeed because it is a mapped address ../sockaddr dump -4 '0:0:0:0:0:ffff::/97' # This should fail even though it is an IPv4 compatible address ../sockaddr dump -4 '0:0:0:0:0:0::/97' # These should succeed as an IPv6 addresses ../sockaddr dump -6 '0:0:0:0:0:0::/97' ../sockaddr dump -i '0:0:0:0:0:0::/97' test_sockaddr_dump-14.sh000077500000000000000000000005471316221320600344200ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 ../sockaddr dump -4 '::c0a8:1' ../sockaddr dump -6 '::c0a8:1' ../sockaddr dump -4 '::c0a8:1/112' ../sockaddr dump -6 '::c0a8:1/112' ../sockaddr dump -4 '0:0:0:0:0:ffff:c0a8:1/112' ../sockaddr dump -6 '0:0:0:0:0:ffff:c0a8:1/112' ../sockaddr dump -4 '[0:0:0:0:0:ffff:c0a8:1/112]' ../sockaddr dump -6 '[0:0:0:0:0:ffff:c0a8:1/112]' test_sockaddr_eval-00-help.sh000077500000000000000000000000651316221320600353160ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr eval test_sockaddr_eval-01.sh000077500000000000000000000001551316221320600343710ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr eval 'GetAllInterfaces | include "name" "lo0" | printf "%v"' test_sockaddr_eval-02.sh000077500000000000000000000002451316221320600343720ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr eval 'GetAllInterfaces | include "name" "lo0" | printf "%v"' 'GetAllInterfaces | include "name" "lo0" | printf "%v"' test_sockaddr_eval-03.sh000077500000000000000000000002161316221320600343710ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr eval '. | include "name" "lo0" | include "type" "IPv6" | sort "address" | join "address" " "' test_sockaddr_eval-04.sh000077500000000000000000000002031316221320600343660ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 cat <<'EOF' | exec ../sockaddr eval - {{GetAllInterfaces | include "name" "lo0" | printf "%v"}} EOF test_sockaddr_eval-05.sh000077500000000000000000000001671316221320600344000ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 ../sockaddr eval 'GetPrivateInterfaces | include "flags" "up|multicast" | attr "flags"' test_sockaddr_rfc-00.sh000077500000000000000000000000671316221320600342150ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr -h rfc test_sockaddr_rfc-01.sh000077500000000000000000000000741316221320600342140ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr rfc -h list test_sockaddr_rfc_list-00.sh000077500000000000000000000000741316221320600352460ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr -h rfc list test_sockaddr_rfc_list-01.sh000077500000000000000000000000711316221320600352440ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr rfc list test_sockaddr_rfc_list-02.sh000077500000000000000000000000741316221320600352500ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr rfc list -h test_sockaddr_version-00.sh000077500000000000000000000000701316221320600351220ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr version test_sockaddr_version-01.sh000077500000000000000000000000701316221320600351230ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/regression#!/bin/sh -- set -e exec 2>&1 exec ../sockaddr version golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/vendor/000077500000000000000000000000001316221320600271475ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/vendor/vendor.json000066400000000000000000000033121316221320600313360ustar00rootroot00000000000000{ "comment": "", "ignore": "test", "package": [ { "checksumSHA1": "gNO0JNpLzYOdInGeq7HqMZUzx9M=", "path": "github.com/armon/go-radix", "revision": "4239b77079c7b5d1243b7b4736304ce8ddb6f0f2", "revisionTime": "2016-01-15T23:47:25Z" }, { "checksumSHA1": "Isa9x3nvIJ12hvgdvUUBty+yplU=", "path": "github.com/bgentry/speakeasy", "revision": "675b82c74c0ed12283ee81ba8a534c8982c07b85", "revisionTime": "2016-10-13T10:26:35Z" }, { "checksumSHA1": "cdOCt0Yb+hdErz8NAQqayxPmRsY=", "path": "github.com/hashicorp/errwrap", "revision": "7554cd9344cec97297fa6649b055a8c98c2a1e55", "revisionTime": "2014-10-28T05:47:10Z" }, { "checksumSHA1": "xZuhljnmBysJPta/lMyYmJdujCg=", "path": "github.com/mattn/go-isatty", "revision": "66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8", "revisionTime": "2016-08-06T12:27:52Z" }, { "checksumSHA1": "UP+pXl+ic9y6qrpZA5MqDIAuGfw=", "path": "github.com/mitchellh/cli", "revision": "ee8578a9c12a5bb9d55303b9665cc448772c81b8", "revisionTime": "2017-03-28T05:23:52Z" }, { "checksumSHA1": "L3leymg2RT8hFl5uL+5KP/LpBkg=", "path": "github.com/mitchellh/go-wordwrap", "revision": "ad45545899c7b13c020ea92b2072220eefad42b8", "revisionTime": "2015-03-14T17:03:34Z" }, { "checksumSHA1": "ZOhewV1DsQjTYlx8a+ifrZki2Vg=", "path": "github.com/ryanuber/columnize", "revision": "e85e216cf3d2864d0db8946e973e6fcd881373a3", "revisionTime": "2017-02-08T07:23:47Z" }, { "checksumSHA1": "aVgPDgwY3/t4J/JOw9H3FVMHqh0=", "path": "golang.org/x/sys/unix", "revision": "c200b10b5d5e122be351b67af224adc6128af5bf", "revisionTime": "2016-10-22T18:22:21Z" } ], "rootPath": "github.com/hashicorp/go-sockaddr/cmd/sockaddr" } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/cmd/sockaddr/version.go000066400000000000000000000020221316221320600276620ustar00rootroot00000000000000package main import ( "fmt" "strings" ) // The git commit that was compiled. This will be filled in by the compiler. var ( GitCommit string GitDescribe string ) // The main version number that is being run at the moment. const Version = "0.2.0" // A pre-release marker for the version. If this is "" (empty string) // then it means that it is a final release. Otherwise, this is a pre-release // such as "dev" (in development), "beta", "rc1", etc. const VersionPrerelease = "dev" // GetHumanVersion composes the parts of the version in a way that's suitable // for displaying to humans. func GetHumanVersion() string { version := Version if GitDescribe != "" { version = GitDescribe } release := VersionPrerelease if GitDescribe == "" && release == "" { release = "dev" } if release != "" { version += fmt.Sprintf("-%s", release) if GitCommit != "" { version += fmt.Sprintf(" (%s)", GitCommit) } } // Strip off any single quotes added by the git information. return strings.Replace(version, "'", "", -1) } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/doc.go000066400000000000000000000002021316221320600244030ustar00rootroot00000000000000/* Package sockaddr is a Go implementation of the UNIX socket family data types and related helper functions. */ package sockaddr golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/ifaddr.go000066400000000000000000000143031316221320600250760ustar00rootroot00000000000000package sockaddr import "strings" // ifAddrAttrMap is a map of the IfAddr type-specific attributes. var ifAddrAttrMap map[AttrName]func(IfAddr) string var ifAddrAttrs []AttrName func init() { ifAddrAttrInit() } // GetPrivateIP returns a string with a single IP address that is part of RFC // 6890 and has a default route. If the system can't determine its IP address // or find an RFC 6890 IP address, an empty string will be returned instead. // This function is the `eval` equivalent of: // // ``` // $ sockaddr eval -r '{{GetPrivateInterfaces | attr "address"}}' /// ``` func GetPrivateIP() (string, error) { privateIfs, err := GetPrivateInterfaces() if err != nil { return "", err } if len(privateIfs) < 1 { return "", nil } ifAddr := privateIfs[0] ip := *ToIPAddr(ifAddr.SockAddr) return ip.NetIP().String(), nil } // GetPrivateIPs returns a string with all IP addresses that are part of RFC // 6890 (regardless of whether or not there is a default route, unlike // GetPublicIP). If the system can't find any RFC 6890 IP addresses, an empty // string will be returned instead. This function is the `eval` equivalent of: // // ``` // $ sockaddr eval -r '{{GetAllInterfaces | include "RFC" "6890" | join "address" " "}}' /// ``` func GetPrivateIPs() (string, error) { ifAddrs, err := GetAllInterfaces() if err != nil { return "", err } else if len(ifAddrs) < 1 { return "", nil } ifAddrs, _ = FilterIfByType(ifAddrs, TypeIP) if len(ifAddrs) == 0 { return "", nil } OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(ifAddrs) ifAddrs, _, err = IfByRFC("6890", ifAddrs) if err != nil { return "", err } else if len(ifAddrs) == 0 { return "", nil } _, ifAddrs, err = IfByRFC(ForwardingBlacklistRFC, ifAddrs) if err != nil { return "", err } else if len(ifAddrs) == 0 { return "", nil } ips := make([]string, 0, len(ifAddrs)) for _, ifAddr := range ifAddrs { ip := *ToIPAddr(ifAddr.SockAddr) s := ip.NetIP().String() ips = append(ips, s) } return strings.Join(ips, " "), nil } // GetPublicIP returns a string with a single IP address that is NOT part of RFC // 6890 and has a default route. If the system can't determine its IP address // or find a non RFC 6890 IP address, an empty string will be returned instead. // This function is the `eval` equivalent of: // // ``` // $ sockaddr eval -r '{{GetPublicInterfaces | attr "address"}}' /// ``` func GetPublicIP() (string, error) { publicIfs, err := GetPublicInterfaces() if err != nil { return "", err } else if len(publicIfs) < 1 { return "", nil } ifAddr := publicIfs[0] ip := *ToIPAddr(ifAddr.SockAddr) return ip.NetIP().String(), nil } // GetPublicIPs returns a string with all IP addresses that are NOT part of RFC // 6890 (regardless of whether or not there is a default route, unlike // GetPublicIP). If the system can't find any non RFC 6890 IP addresses, an // empty string will be returned instead. This function is the `eval` // equivalent of: // // ``` // $ sockaddr eval -r '{{GetAllInterfaces | exclude "RFC" "6890" | join "address" " "}}' /// ``` func GetPublicIPs() (string, error) { ifAddrs, err := GetAllInterfaces() if err != nil { return "", err } else if len(ifAddrs) < 1 { return "", nil } ifAddrs, _ = FilterIfByType(ifAddrs, TypeIP) if len(ifAddrs) == 0 { return "", nil } OrderedIfAddrBy(AscIfType, AscIfNetworkSize).Sort(ifAddrs) _, ifAddrs, err = IfByRFC("6890", ifAddrs) if err != nil { return "", err } else if len(ifAddrs) == 0 { return "", nil } ips := make([]string, 0, len(ifAddrs)) for _, ifAddr := range ifAddrs { ip := *ToIPAddr(ifAddr.SockAddr) s := ip.NetIP().String() ips = append(ips, s) } return strings.Join(ips, " "), nil } // GetInterfaceIP returns a string with a single IP address sorted by the size // of the network (i.e. IP addresses with a smaller netmask, larger network // size, are sorted first). This function is the `eval` equivalent of: // // ``` // $ sockaddr eval -r '{{GetAllInterfaces | include "name" <> | sort "type,size" | include "flag" "forwardable" | attr "address" }}' /// ``` func GetInterfaceIP(namedIfRE string) (string, error) { ifAddrs, err := GetAllInterfaces() if err != nil { return "", err } ifAddrs, _, err = IfByName(namedIfRE, ifAddrs) if err != nil { return "", err } ifAddrs, _, err = IfByFlag("forwardable", ifAddrs) if err != nil { return "", err } ifAddrs, err = SortIfBy("+type,+size", ifAddrs) if err != nil { return "", err } if len(ifAddrs) == 0 { return "", err } ip := ToIPAddr(ifAddrs[0].SockAddr) if ip == nil { return "", err } return IPAddrAttr(*ip, "address"), nil } // GetInterfaceIPs returns a string with all IPs, sorted by the size of the // network (i.e. IP addresses with a smaller netmask, larger network size, are // sorted first), on a named interface. This function is the `eval` equivalent // of: // // ``` // $ sockaddr eval -r '{{GetAllInterfaces | include "name" <> | sort "type,size" | join "address" " "}}' /// ``` func GetInterfaceIPs(namedIfRE string) (string, error) { ifAddrs, err := GetAllInterfaces() if err != nil { return "", err } ifAddrs, _, err = IfByName(namedIfRE, ifAddrs) if err != nil { return "", err } ifAddrs, err = SortIfBy("+type,+size", ifAddrs) if err != nil { return "", err } if len(ifAddrs) == 0 { return "", err } ips := make([]string, 0, len(ifAddrs)) for _, ifAddr := range ifAddrs { ip := *ToIPAddr(ifAddr.SockAddr) s := ip.NetIP().String() ips = append(ips, s) } return strings.Join(ips, " "), nil } // IfAddrAttrs returns a list of attributes supported by the IfAddr type func IfAddrAttrs() []AttrName { return ifAddrAttrs } // IfAddrAttr returns a string representation of an attribute for the given // IfAddr. func IfAddrAttr(ifAddr IfAddr, attrName AttrName) string { fn, found := ifAddrAttrMap[attrName] if !found { return "" } return fn(ifAddr) } // ifAddrAttrInit is called once at init() func ifAddrAttrInit() { // Sorted for human readability ifAddrAttrs = []AttrName{ "flags", "name", } ifAddrAttrMap = map[AttrName]func(ifAddr IfAddr) string{ "flags": func(ifAddr IfAddr) string { return ifAddr.Interface.Flags.String() }, "name": func(ifAddr IfAddr) string { return ifAddr.Interface.Name }, } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/ifaddr_test.go000066400000000000000000000255701316221320600261450ustar00rootroot00000000000000package sockaddr_test import ( "fmt" "net" "os" "strings" "testing" sockaddr "github.com/hashicorp/go-sockaddr" ) func boolEnvVar(envvar string, emptyDefault bool) bool { v := os.Getenv(envvar) switch strings.ToLower(v) { case "": return emptyDefault case "0", "f", "n": return false case "1", "t", "y": return true default: fmt.Fprintf(os.Stderr, "Unsupported %s flag %q", envvar, v) return true } } // havePrivateIP is a helper function that returns true when we believe we // should have a private IP address. This changes the failure mode of various // tests that expect a private IP address. // // When you have a private IP assigned to the host, set the environment variable // SOCKADDR_HAVE_PRIVATE_IP=1 func havePrivateIP() bool { return boolEnvVar("SOCKADDR_HAVE_PRIVATE_IP", true) } // havePublicIP is a helper function that returns true when we believe we should // have a public IP address. This changes the failure mode of various tests // that expect a public IP address. // // When you have a public IP assigned to the host, set the environment variable // SOCKADDR_HAVE_PUBLIC_IP=1 func havePublicIP() bool { return boolEnvVar("SOCKADDR_HAVE_PUBLIC_IP", false) } func TestGetPrivateIP(t *testing.T) { reportOnPrivate := func(args ...interface{}) { if havePrivateIP() { t.Fatalf(args[0].(string), args[1:]...) } else { t.Skipf(args[0].(string), args[1:]...) } } ip, err := sockaddr.GetPrivateIP() if err != nil { reportOnPrivate("unable to get a private IP: %v", err) } if ip == "" { reportOnPrivate("it's hard to test this reliably") } } func TestGetPrivateIPs(t *testing.T) { reportOnPrivate := func(args ...interface{}) { if havePrivateIP() { t.Fatalf(args[0].(string), args[1:]...) } else { t.Skipf(args[0].(string), args[1:]...) } } ips, err := sockaddr.GetPrivateIPs() if err != nil { reportOnPrivate("unable to get a private IPs: %v", err) } if ips == "" { reportOnPrivate("it's hard to test this reliably") } } func TestGetPublicIP(t *testing.T) { reportOnPublic := func(args ...interface{}) { if havePublicIP() { t.Fatalf(args[0].(string), args[1:]...) } else { t.Skipf(args[0].(string), args[1:]...) } } ip, err := sockaddr.GetPublicIP() if err != nil { reportOnPublic("unable to get a public IP: %v", err) } if ip == "" { reportOnPublic("it's hard to test this reliably") } } func TestGetPublicIPs(t *testing.T) { reportOnPublic := func(args ...interface{}) { if havePublicIP() { t.Fatalf(args[0].(string), args[1:]...) } else { t.Skipf(args[0].(string), args[1:]...) } } ips, err := sockaddr.GetPublicIPs() if err != nil { reportOnPublic("unable to get a public IPs: %v", err) } if ips == "" { reportOnPublic("it's hard to test this reliably") } } func TestGetInterfaceIP(t *testing.T) { ip, err := sockaddr.GetInterfaceIP(`^.*[\d]$`) if err != nil { t.Fatalf("regexp failed: %v", err) } if ip == "" { t.Skip("it's hard to test this reliably") } } func TestIfAddrAttr(t *testing.T) { tests := []struct { name string ifAddr sockaddr.IfAddr attr string expected string }{ { name: "name", ifAddr: sockaddr.IfAddr{ Interface: net.Interface{ Name: "abc0", }, }, attr: "name", expected: "abc0", }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d must have a name", i) } result, err := sockaddr.IfAttr(test.attr, test.ifAddr) if err != nil { t.Errorf("failed to get attr %q from %v", test.name, test.ifAddr) } if result != test.expected { t.Errorf("unexpected result") } } } func TestIfAddrMath(t *testing.T) { tests := []struct { name string ifAddr sockaddr.IfAddr operation string value string expected string wantFail bool }{ { name: "ipv4 address +2", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "address", value: "+2", expected: "127.0.0.3/8", }, { name: "ipv4 address -2", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "address", value: "-2", expected: "126.255.255.255/8", }, { name: "ipv4 address + overflow 0xff00ff03", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "address", value: fmt.Sprintf("+%d", 0xff00ff03), expected: "126.0.255.4/8", }, { name: "ipv4 address - underflow 0xff00ff04", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "address", value: fmt.Sprintf("-%d", 0xff00ff04), expected: "127.255.0.253/8", }, { name: "ipv6 address +2", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::1/128"), }, operation: "address", value: "+2", expected: "::3", }, { name: "ipv6 address -3", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::4/128"), }, operation: "address", value: "-3", expected: "::1", }, { name: "ipv6 address + overflow", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"), }, operation: "address", value: fmt.Sprintf("+%d", 0x03), expected: "::2", }, { name: "ipv6 address + underflow", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::1/128"), }, operation: "address", value: fmt.Sprintf("-%d", 0x03), expected: "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", }, { name: "ipv4 network +2", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "network", value: "+2", expected: "127.0.0.2/8", }, { name: "ipv4 network -2", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "network", value: "-2", expected: "127.255.255.254/8", }, { // Value exceeds /8 name: "ipv4 network + overflow 0xff00ff03", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "network", value: fmt.Sprintf("+%d", 0xff00ff03), expected: "127.0.255.3/8", }, { // Value exceeds /8 name: "ipv4 network - underflow+wrap 0xff00ff04", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "network", value: fmt.Sprintf("-%d", 0xff00ff04), expected: "127.255.0.252/8", }, { name: "ipv6 network +6", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("fe80::1/64"), }, operation: "network", value: "+6", expected: "fe80::6/64", }, { name: "ipv6 network -6", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("fe80::ff/64"), }, operation: "network", value: "-6", expected: "fe80::ffff:ffff:ffff:fffa/64", }, { // Value exceeds /104 mask name: "ipv6 network + overflow 0xff00ff03", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("fe80::1/104"), }, operation: "network", value: fmt.Sprintf("+%d", 0xff00ff03), expected: "fe80::ff03/104", }, { // Value exceeds /104 name: "ipv6 network - underflow+wrap 0xff00ff04", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("fe80::1/104"), }, operation: "network", value: fmt.Sprintf("-%d", 0xff00ff04), expected: "fe80::ff:fc/104", }, { name: "ipv4 address missing sign", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "address", value: "123", wantFail: true, }, { name: "ipv4 network missing sign", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "network", value: "123", wantFail: true, }, { name: "ipv6 address missing sign", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::1/128"), }, operation: "address", value: "123", wantFail: true, }, { name: "ipv6 network missing sign", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::1/128"), }, operation: "network", value: "123", wantFail: true, }, { name: "ipv4 address bad value", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "address", value: "+xyz", wantFail: true, }, { name: "ipv4 network bad value", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "network", value: "-xyz", wantFail: true, }, { name: "ipv6 address bad value", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::1/128"), }, operation: "address", value: "+xyz", wantFail: true, }, { name: "ipv6 network bad value", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::1/128"), }, operation: "network", value: "-xyz", wantFail: true, }, { name: "ipv4 bad operation", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.1/8"), }, operation: "gooz", value: "+xyz", wantFail: true, }, { name: "ipv6 bad operation", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::1/128"), }, operation: "frabba", value: "+xyz", wantFail: true, }, { name: "unix unsupported operation", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustUnixSock("/tmp/bar"), }, operation: "address", value: "+123", wantFail: true, }, { name: "unix unsupported operation", ifAddr: sockaddr.IfAddr{ SockAddr: sockaddr.MustUnixSock("/tmp/foo"), }, operation: "network", value: "+123", wantFail: true, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d must have a name", i) } results, err := sockaddr.IfAddrsMath(test.operation, test.value, sockaddr.IfAddrs{test.ifAddr}) if test.wantFail { if err != nil { continue } else { t.Fatalf("%s: failed to fail math operation %q with value %q on %v", test.name, test.operation, test.value, test.ifAddr) } } else if err != nil { t.Fatalf("%s: failed to compute math operation %q with value %q on %v", test.name, test.operation, test.value, test.ifAddr) } if len(results) != 1 { t.Fatalf("%s: bad", test.name) } result := results[0] switch saType := result.Type(); saType { case sockaddr.TypeIPv4: ipv4 := sockaddr.ToIPv4Addr(result.SockAddr) if ipv4 == nil { t.Fatalf("bad: %T %+#v", result, result) } if got := ipv4.String(); got != test.expected { t.Errorf("unexpected result %q: want %q got %q", test.name, test.expected, got) } case sockaddr.TypeIPv6: ipv6 := sockaddr.ToIPv6Addr(result.SockAddr) if ipv6 == nil { t.Fatalf("bad: %T %+#v", result, result) } if got := ipv6.String(); got != test.expected { t.Errorf("unexpected result %q: want %q got %q", test.name, test.expected, got) } default: t.Fatalf("bad") } } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/ifaddrs.go000066400000000000000000001076021316221320600252660ustar00rootroot00000000000000package sockaddr import ( "errors" "fmt" "math/big" "net" "regexp" "sort" "strconv" "strings" ) var ( // Centralize all regexps and regexp.Copy() where necessary. signRE *regexp.Regexp = regexp.MustCompile(`^[\s]*[+-]`) whitespaceRE *regexp.Regexp = regexp.MustCompile(`[\s]+`) ifNameRE *regexp.Regexp = regexp.MustCompile(`^Ethernet adapter ([^\s:]+):`) ipAddrRE *regexp.Regexp = regexp.MustCompile(`^ IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`) ) // IfAddrs is a slice of IfAddr type IfAddrs []IfAddr func (ifs IfAddrs) Len() int { return len(ifs) } // CmpIfFunc is the function signature that must be met to be used in the // OrderedIfAddrBy multiIfAddrSorter type CmpIfAddrFunc func(p1, p2 *IfAddr) int // multiIfAddrSorter implements the Sort interface, sorting the IfAddrs within. type multiIfAddrSorter struct { ifAddrs IfAddrs cmp []CmpIfAddrFunc } // Sort sorts the argument slice according to the Cmp functions passed to // OrderedIfAddrBy. func (ms *multiIfAddrSorter) Sort(ifAddrs IfAddrs) { ms.ifAddrs = ifAddrs sort.Sort(ms) } // OrderedIfAddrBy sorts SockAddr by the list of sort function pointers. func OrderedIfAddrBy(cmpFuncs ...CmpIfAddrFunc) *multiIfAddrSorter { return &multiIfAddrSorter{ cmp: cmpFuncs, } } // Len is part of sort.Interface. func (ms *multiIfAddrSorter) Len() int { return len(ms.ifAddrs) } // Less is part of sort.Interface. It is implemented by looping along the Cmp() // functions until it finds a comparison that is either less than or greater // than. A return value of 0 defers sorting to the next function in the // multisorter (which means the results of sorting may leave the resutls in a // non-deterministic order). func (ms *multiIfAddrSorter) Less(i, j int) bool { p, q := &ms.ifAddrs[i], &ms.ifAddrs[j] // Try all but the last comparison. var k int for k = 0; k < len(ms.cmp)-1; k++ { cmp := ms.cmp[k] x := cmp(p, q) switch x { case -1: // p < q, so we have a decision. return true case 1: // p > q, so we have a decision. return false } // p == q; try the next comparison. } // All comparisons to here said "equal", so just return whatever the // final comparison reports. switch ms.cmp[k](p, q) { case -1: return true case 1: return false default: // Still a tie! Now what? return false panic("undefined sort order for remaining items in the list") } } // Swap is part of sort.Interface. func (ms *multiIfAddrSorter) Swap(i, j int) { ms.ifAddrs[i], ms.ifAddrs[j] = ms.ifAddrs[j], ms.ifAddrs[i] } // AscIfAddress is a sorting function to sort IfAddrs by their respective // address type. Non-equal types are deferred in the sort. func AscIfAddress(p1Ptr, p2Ptr *IfAddr) int { return AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr) } // AscIfDefault is a sorting function to sort IfAddrs by whether or not they // have a default route or not. Non-equal types are deferred in the sort. // // FIXME: This is a particularly expensive sorting operation because of the // non-memoized calls to NewRouteInfo(). In an ideal world the routeInfo data // once at the start of the sort and pass it along as a context or by wrapping // the IfAddr type with this information (this would also solve the inability to // return errors and the possibility of failing silently). Fortunately, // N*log(N) where N = 3 is only ~6.2 invocations. Not ideal, but not worth // optimizing today. The common case is this gets called once or twice. // Patches welcome. func AscIfDefault(p1Ptr, p2Ptr *IfAddr) int { ri, err := NewRouteInfo() if err != nil { return sortDeferDecision } defaultIfName, err := ri.GetDefaultInterfaceName() if err != nil { return sortDeferDecision } switch { case p1Ptr.Interface.Name == defaultIfName && p2Ptr.Interface.Name == defaultIfName: return sortDeferDecision case p1Ptr.Interface.Name == defaultIfName: return sortReceiverBeforeArg case p2Ptr.Interface.Name == defaultIfName: return sortArgBeforeReceiver default: return sortDeferDecision } } // AscIfName is a sorting function to sort IfAddrs by their interface names. func AscIfName(p1Ptr, p2Ptr *IfAddr) int { return strings.Compare(p1Ptr.Name, p2Ptr.Name) } // AscIfNetworkSize is a sorting function to sort IfAddrs by their respective // network mask size. func AscIfNetworkSize(p1Ptr, p2Ptr *IfAddr) int { return AscNetworkSize(&p1Ptr.SockAddr, &p2Ptr.SockAddr) } // AscIfPort is a sorting function to sort IfAddrs by their respective // port type. Non-equal types are deferred in the sort. func AscIfPort(p1Ptr, p2Ptr *IfAddr) int { return AscPort(&p1Ptr.SockAddr, &p2Ptr.SockAddr) } // AscIfPrivate is a sorting function to sort IfAddrs by "private" values before // "public" values. Both IPv4 and IPv6 are compared against RFC6890 (RFC6890 // includes, and is not limited to, RFC1918 and RFC6598 for IPv4, and IPv6 // includes RFC4193). func AscIfPrivate(p1Ptr, p2Ptr *IfAddr) int { return AscPrivate(&p1Ptr.SockAddr, &p2Ptr.SockAddr) } // AscIfType is a sorting function to sort IfAddrs by their respective address // type. Non-equal types are deferred in the sort. func AscIfType(p1Ptr, p2Ptr *IfAddr) int { return AscType(&p1Ptr.SockAddr, &p2Ptr.SockAddr) } // DescIfAddress is identical to AscIfAddress but reverse ordered. func DescIfAddress(p1Ptr, p2Ptr *IfAddr) int { return -1 * AscAddress(&p1Ptr.SockAddr, &p2Ptr.SockAddr) } // DescIfDefault is identical to AscIfDefault but reverse ordered. func DescIfDefault(p1Ptr, p2Ptr *IfAddr) int { return -1 * AscIfDefault(p1Ptr, p2Ptr) } // DescIfName is identical to AscIfName but reverse ordered. func DescIfName(p1Ptr, p2Ptr *IfAddr) int { return -1 * strings.Compare(p1Ptr.Name, p2Ptr.Name) } // DescIfNetworkSize is identical to AscIfNetworkSize but reverse ordered. func DescIfNetworkSize(p1Ptr, p2Ptr *IfAddr) int { return -1 * AscNetworkSize(&p1Ptr.SockAddr, &p2Ptr.SockAddr) } // DescIfPort is identical to AscIfPort but reverse ordered. func DescIfPort(p1Ptr, p2Ptr *IfAddr) int { return -1 * AscPort(&p1Ptr.SockAddr, &p2Ptr.SockAddr) } // DescIfPrivate is identical to AscIfPrivate but reverse ordered. func DescIfPrivate(p1Ptr, p2Ptr *IfAddr) int { return -1 * AscPrivate(&p1Ptr.SockAddr, &p2Ptr.SockAddr) } // DescIfType is identical to AscIfType but reverse ordered. func DescIfType(p1Ptr, p2Ptr *IfAddr) int { return -1 * AscType(&p1Ptr.SockAddr, &p2Ptr.SockAddr) } // FilterIfByType filters IfAddrs and returns a list of the matching type func FilterIfByType(ifAddrs IfAddrs, type_ SockAddrType) (matchedIfs, excludedIfs IfAddrs) { excludedIfs = make(IfAddrs, 0, len(ifAddrs)) matchedIfs = make(IfAddrs, 0, len(ifAddrs)) for _, ifAddr := range ifAddrs { if ifAddr.SockAddr.Type()&type_ != 0 { matchedIfs = append(matchedIfs, ifAddr) } else { excludedIfs = append(excludedIfs, ifAddr) } } return matchedIfs, excludedIfs } // IfAttr forwards the selector to IfAttr.Attr() for resolution. If there is // more than one IfAddr, only the first IfAddr is used. func IfAttr(selectorName string, ifAddr IfAddr) (string, error) { attrName := AttrName(strings.ToLower(selectorName)) attrVal, err := ifAddr.Attr(attrName) return attrVal, err } // IfAttrs forwards the selector to IfAttrs.Attr() for resolution. If there is // more than one IfAddr, only the first IfAddr is used. func IfAttrs(selectorName string, ifAddrs IfAddrs) (string, error) { if len(ifAddrs) == 0 { return "", nil } attrName := AttrName(strings.ToLower(selectorName)) attrVal, err := ifAddrs[0].Attr(attrName) return attrVal, err } // GetAllInterfaces iterates over all available network interfaces and finds all // available IP addresses on each interface and converts them to // sockaddr.IPAddrs, and returning the result as an array of IfAddr. func GetAllInterfaces() (IfAddrs, error) { ifs, err := net.Interfaces() if err != nil { return nil, err } ifAddrs := make(IfAddrs, 0, len(ifs)) for _, intf := range ifs { addrs, err := intf.Addrs() if err != nil { return nil, err } for _, addr := range addrs { var ipAddr IPAddr ipAddr, err = NewIPAddr(addr.String()) if err != nil { return IfAddrs{}, fmt.Errorf("unable to create an IP address from %q", addr.String()) } ifAddr := IfAddr{ SockAddr: ipAddr, Interface: intf, } ifAddrs = append(ifAddrs, ifAddr) } } return ifAddrs, nil } // GetDefaultInterfaces returns IfAddrs of the addresses attached to the default // route. func GetDefaultInterfaces() (IfAddrs, error) { ri, err := NewRouteInfo() if err != nil { return nil, err } defaultIfName, err := ri.GetDefaultInterfaceName() if err != nil { return nil, err } var defaultIfs, ifAddrs IfAddrs ifAddrs, err = GetAllInterfaces() for _, ifAddr := range ifAddrs { if ifAddr.Name == defaultIfName { defaultIfs = append(defaultIfs, ifAddr) } } return defaultIfs, nil } // GetPrivateInterfaces returns an IfAddrs that are part of RFC 6890 and have a // default route. If the system can't determine its IP address or find an RFC // 6890 IP address, an empty IfAddrs will be returned instead. This function is // the `eval` equivalent of: // // ``` // $ sockaddr eval -r '{{GetAllInterfaces | include "type" "ip" | include "flags" "forwardable" | include "flags" "up" | sort "default,type,size" | include "RFC" "6890" }}' /// ``` func GetPrivateInterfaces() (IfAddrs, error) { privateIfs, err := GetAllInterfaces() if err != nil { return IfAddrs{}, err } if len(privateIfs) == 0 { return IfAddrs{}, nil } privateIfs, _ = FilterIfByType(privateIfs, TypeIP) if len(privateIfs) == 0 { return IfAddrs{}, nil } privateIfs, _, err = IfByFlag("forwardable", privateIfs) if err != nil { return IfAddrs{}, err } privateIfs, _, err = IfByFlag("up", privateIfs) if err != nil { return IfAddrs{}, err } if len(privateIfs) == 0 { return IfAddrs{}, nil } OrderedIfAddrBy(AscIfDefault, AscIfType, AscIfNetworkSize).Sort(privateIfs) privateIfs, _, err = IfByRFC("6890", privateIfs) if err != nil { return IfAddrs{}, err } else if len(privateIfs) == 0 { return IfAddrs{}, nil } return privateIfs, nil } // GetPublicInterfaces returns an IfAddrs that are NOT part of RFC 6890 and has a // default route. If the system can't determine its IP address or find a non // RFC 6890 IP address, an empty IfAddrs will be returned instead. This // function is the `eval` equivalent of: // // ``` // $ sockaddr eval -r '{{GetAllInterfaces | include "type" "ip" | include "flags" "forwardable" | include "flags" "up" | sort "default,type,size" | exclude "RFC" "6890" }}' /// ``` func GetPublicInterfaces() (IfAddrs, error) { publicIfs, err := GetAllInterfaces() if err != nil { return IfAddrs{}, err } if len(publicIfs) == 0 { return IfAddrs{}, nil } publicIfs, _ = FilterIfByType(publicIfs, TypeIP) if len(publicIfs) == 0 { return IfAddrs{}, nil } publicIfs, _, err = IfByFlag("forwardable", publicIfs) if err != nil { return IfAddrs{}, err } publicIfs, _, err = IfByFlag("up", publicIfs) if err != nil { return IfAddrs{}, err } if len(publicIfs) == 0 { return IfAddrs{}, nil } OrderedIfAddrBy(AscIfDefault, AscIfType, AscIfNetworkSize).Sort(publicIfs) _, publicIfs, err = IfByRFC("6890", publicIfs) if err != nil { return IfAddrs{}, err } else if len(publicIfs) == 0 { return IfAddrs{}, nil } return publicIfs, nil } // IfByAddress returns a list of matched and non-matched IfAddrs, or an error if // the regexp fails to compile. func IfByAddress(inputRe string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) { re, err := regexp.Compile(inputRe) if err != nil { return nil, nil, fmt.Errorf("Unable to compile address regexp %+q: %v", inputRe, err) } matchedAddrs := make(IfAddrs, 0, len(ifAddrs)) excludedAddrs := make(IfAddrs, 0, len(ifAddrs)) for _, addr := range ifAddrs { if re.MatchString(addr.SockAddr.String()) { matchedAddrs = append(matchedAddrs, addr) } else { excludedAddrs = append(excludedAddrs, addr) } } return matchedAddrs, excludedAddrs, nil } // IfByName returns a list of matched and non-matched IfAddrs, or an error if // the regexp fails to compile. func IfByName(inputRe string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) { re, err := regexp.Compile(inputRe) if err != nil { return nil, nil, fmt.Errorf("Unable to compile name regexp %+q: %v", inputRe, err) } matchedAddrs := make(IfAddrs, 0, len(ifAddrs)) excludedAddrs := make(IfAddrs, 0, len(ifAddrs)) for _, addr := range ifAddrs { if re.MatchString(addr.Name) { matchedAddrs = append(matchedAddrs, addr) } else { excludedAddrs = append(excludedAddrs, addr) } } return matchedAddrs, excludedAddrs, nil } // IfByPort returns a list of matched and non-matched IfAddrs, or an error if // the regexp fails to compile. func IfByPort(inputRe string, ifAddrs IfAddrs) (matchedIfs, excludedIfs IfAddrs, err error) { re, err := regexp.Compile(inputRe) if err != nil { return nil, nil, fmt.Errorf("Unable to compile port regexp %+q: %v", inputRe, err) } ipIfs, nonIfs := FilterIfByType(ifAddrs, TypeIP) matchedIfs = make(IfAddrs, 0, len(ipIfs)) excludedIfs = append(IfAddrs(nil), nonIfs...) for _, addr := range ipIfs { ipAddr := ToIPAddr(addr.SockAddr) if ipAddr == nil { continue } port := strconv.FormatInt(int64((*ipAddr).IPPort()), 10) if re.MatchString(port) { matchedIfs = append(matchedIfs, addr) } else { excludedIfs = append(excludedIfs, addr) } } return matchedIfs, excludedIfs, nil } // IfByRFC returns a list of matched and non-matched IfAddrs that contain the // relevant RFC-specified traits. func IfByRFC(selectorParam string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) { inputRFC, err := strconv.ParseUint(selectorParam, 10, 64) if err != nil { return IfAddrs{}, IfAddrs{}, fmt.Errorf("unable to parse RFC number %q: %v", selectorParam, err) } matchedIfAddrs := make(IfAddrs, 0, len(ifAddrs)) remainingIfAddrs := make(IfAddrs, 0, len(ifAddrs)) rfcNetMap := KnownRFCs() rfcNets, ok := rfcNetMap[uint(inputRFC)] if !ok { return nil, nil, fmt.Errorf("unsupported RFC %d", inputRFC) } for _, ifAddr := range ifAddrs { var contained bool for _, rfcNet := range rfcNets { if rfcNet.Contains(ifAddr.SockAddr) { matchedIfAddrs = append(matchedIfAddrs, ifAddr) contained = true break } } if !contained { remainingIfAddrs = append(remainingIfAddrs, ifAddr) } } return matchedIfAddrs, remainingIfAddrs, nil } // IfByRFCs returns a list of matched and non-matched IfAddrs that contain the // relevant RFC-specified traits. Multiple RFCs can be specified and separated // by the `|` symbol. No protection is taken to ensure an IfAddr does not end // up in both the included and excluded list. func IfByRFCs(selectorParam string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) { var includedIfs, excludedIfs IfAddrs for _, rfcStr := range strings.Split(selectorParam, "|") { includedRFCIfs, excludedRFCIfs, err := IfByRFC(rfcStr, ifAddrs) if err != nil { return IfAddrs{}, IfAddrs{}, fmt.Errorf("unable to lookup RFC number %q: %v", rfcStr, err) } includedIfs = append(includedIfs, includedRFCIfs...) excludedIfs = append(excludedIfs, excludedRFCIfs...) } return includedIfs, excludedIfs, nil } // IfByMaskSize returns a list of matched and non-matched IfAddrs that have the // matching mask size. func IfByMaskSize(selectorParam string, ifAddrs IfAddrs) (matchedIfs, excludedIfs IfAddrs, err error) { maskSize, err := strconv.ParseUint(selectorParam, 10, 64) if err != nil { return IfAddrs{}, IfAddrs{}, fmt.Errorf("invalid exclude size argument (%q): %v", selectorParam, err) } ipIfs, nonIfs := FilterIfByType(ifAddrs, TypeIP) matchedIfs = make(IfAddrs, 0, len(ipIfs)) excludedIfs = append(IfAddrs(nil), nonIfs...) for _, addr := range ipIfs { ipAddr := ToIPAddr(addr.SockAddr) if ipAddr == nil { return IfAddrs{}, IfAddrs{}, fmt.Errorf("unable to filter mask sizes on non-IP type %s: %v", addr.SockAddr.Type().String(), addr.SockAddr.String()) } switch { case (*ipAddr).Type()&TypeIPv4 != 0 && maskSize > 32: return IfAddrs{}, IfAddrs{}, fmt.Errorf("mask size out of bounds for IPv4 address: %d", maskSize) case (*ipAddr).Type()&TypeIPv6 != 0 && maskSize > 128: return IfAddrs{}, IfAddrs{}, fmt.Errorf("mask size out of bounds for IPv6 address: %d", maskSize) } if (*ipAddr).Maskbits() == int(maskSize) { matchedIfs = append(matchedIfs, addr) } else { excludedIfs = append(excludedIfs, addr) } } return matchedIfs, excludedIfs, nil } // IfByType returns a list of matching and non-matching IfAddr that match the // specified type. For instance: // // include "type" "IPv4,IPv6" // // will include any IfAddrs that is either an IPv4 or IPv6 address. Any // addresses on those interfaces that don't match will be included in the // remainder results. func IfByType(inputTypes string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) { matchingIfAddrs := make(IfAddrs, 0, len(ifAddrs)) remainingIfAddrs := make(IfAddrs, 0, len(ifAddrs)) ifTypes := strings.Split(strings.ToLower(inputTypes), "|") for _, ifType := range ifTypes { switch ifType { case "ip", "ipv4", "ipv6", "unix": // Valid types default: return nil, nil, fmt.Errorf("unsupported type %q %q", ifType, inputTypes) } } for _, ifAddr := range ifAddrs { for _, ifType := range ifTypes { var matched bool switch { case ifType == "ip" && ifAddr.SockAddr.Type()&TypeIP != 0: matched = true case ifType == "ipv4" && ifAddr.SockAddr.Type()&TypeIPv4 != 0: matched = true case ifType == "ipv6" && ifAddr.SockAddr.Type()&TypeIPv6 != 0: matched = true case ifType == "unix" && ifAddr.SockAddr.Type()&TypeUnix != 0: matched = true } if matched { matchingIfAddrs = append(matchingIfAddrs, ifAddr) } else { remainingIfAddrs = append(remainingIfAddrs, ifAddr) } } } return matchingIfAddrs, remainingIfAddrs, nil } // IfByFlag returns a list of matching and non-matching IfAddrs that match the // specified type. For instance: // // include "flag" "up,broadcast" // // will include any IfAddrs that have both the "up" and "broadcast" flags set. // Any addresses on those interfaces that don't match will be omitted from the // results. func IfByFlag(inputFlags string, ifAddrs IfAddrs) (matched, remainder IfAddrs, err error) { matchedAddrs := make(IfAddrs, 0, len(ifAddrs)) excludedAddrs := make(IfAddrs, 0, len(ifAddrs)) var wantForwardable, wantGlobalUnicast, wantInterfaceLocalMulticast, wantLinkLocalMulticast, wantLinkLocalUnicast, wantLoopback, wantMulticast, wantUnspecified bool var ifFlags net.Flags var checkFlags, checkAttrs bool for _, flagName := range strings.Split(strings.ToLower(inputFlags), "|") { switch flagName { case "broadcast": checkFlags = true ifFlags = ifFlags | net.FlagBroadcast case "down": checkFlags = true ifFlags = (ifFlags &^ net.FlagUp) case "forwardable": checkAttrs = true wantForwardable = true case "global unicast": checkAttrs = true wantGlobalUnicast = true case "interface-local multicast": checkAttrs = true wantInterfaceLocalMulticast = true case "link-local multicast": checkAttrs = true wantLinkLocalMulticast = true case "link-local unicast": checkAttrs = true wantLinkLocalUnicast = true case "loopback": checkAttrs = true checkFlags = true ifFlags = ifFlags | net.FlagLoopback wantLoopback = true case "multicast": checkAttrs = true checkFlags = true ifFlags = ifFlags | net.FlagMulticast wantMulticast = true case "point-to-point": checkFlags = true ifFlags = ifFlags | net.FlagPointToPoint case "unspecified": checkAttrs = true wantUnspecified = true case "up": checkFlags = true ifFlags = ifFlags | net.FlagUp default: return nil, nil, fmt.Errorf("Unknown interface flag: %+q", flagName) } } for _, ifAddr := range ifAddrs { var matched bool if checkFlags && ifAddr.Interface.Flags&ifFlags == ifFlags { matched = true } if checkAttrs { if ip := ToIPAddr(ifAddr.SockAddr); ip != nil { netIP := (*ip).NetIP() switch { case wantGlobalUnicast && netIP.IsGlobalUnicast(): matched = true case wantInterfaceLocalMulticast && netIP.IsInterfaceLocalMulticast(): matched = true case wantLinkLocalMulticast && netIP.IsLinkLocalMulticast(): matched = true case wantLinkLocalUnicast && netIP.IsLinkLocalUnicast(): matched = true case wantLoopback && netIP.IsLoopback(): matched = true case wantMulticast && netIP.IsMulticast(): matched = true case wantUnspecified && netIP.IsUnspecified(): matched = true case wantForwardable && !IsRFC(ForwardingBlacklist, ifAddr.SockAddr): matched = true } } } if matched { matchedAddrs = append(matchedAddrs, ifAddr) } else { excludedAddrs = append(excludedAddrs, ifAddr) } } return matchedAddrs, excludedAddrs, nil } // IfByNetwork returns an IfAddrs that are equal to or included within the // network passed in by selector. func IfByNetwork(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, IfAddrs, error) { var includedIfs, excludedIfs IfAddrs for _, netStr := range strings.Split(selectorParam, "|") { netAddr, err := NewIPAddr(netStr) if err != nil { return nil, nil, fmt.Errorf("unable to create an IP address from %+q: %v", netStr, err) } for _, ifAddr := range inputIfAddrs { if netAddr.Contains(ifAddr.SockAddr) { includedIfs = append(includedIfs, ifAddr) } else { excludedIfs = append(excludedIfs, ifAddr) } } } return includedIfs, excludedIfs, nil } // IfAddrMath will return a new IfAddr struct with a mutated value. func IfAddrMath(operation, value string, inputIfAddr IfAddr) (IfAddr, error) { // Regexp used to enforce the sign being a required part of the grammar for // some values. signRe := signRE.Copy() switch strings.ToLower(operation) { case "address": // "address" operates on the IP address and is allowed to overflow or // underflow networks, however it will wrap along the underlying address's // underlying type. if !signRe.MatchString(value) { return IfAddr{}, fmt.Errorf("sign (+/-) is required for operation %q", operation) } switch sockType := inputIfAddr.SockAddr.Type(); sockType { case TypeIPv4: // 33 == Accept any uint32 value // TODO(seanc@): Add the ability to parse hex i, err := strconv.ParseInt(value, 10, 33) if err != nil { return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err) } ipv4 := *ToIPv4Addr(inputIfAddr.SockAddr) ipv4Uint32 := uint32(ipv4.Address) ipv4Uint32 += uint32(i) return IfAddr{ SockAddr: IPv4Addr{ Address: IPv4Address(ipv4Uint32), Mask: ipv4.Mask, }, Interface: inputIfAddr.Interface, }, nil case TypeIPv6: // 64 == Accept any int32 value // TODO(seanc@): Add the ability to parse hex. Also parse a bignum int. i, err := strconv.ParseInt(value, 10, 64) if err != nil { return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err) } ipv6 := *ToIPv6Addr(inputIfAddr.SockAddr) ipv6BigIntA := new(big.Int) ipv6BigIntA.Set(ipv6.Address) ipv6BigIntB := big.NewInt(i) ipv6Addr := ipv6BigIntA.Add(ipv6BigIntA, ipv6BigIntB) ipv6Addr.And(ipv6Addr, ipv6HostMask) return IfAddr{ SockAddr: IPv6Addr{ Address: IPv6Address(ipv6Addr), Mask: ipv6.Mask, }, Interface: inputIfAddr.Interface, }, nil default: return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType) } case "network": // "network" operates on the network address. Positive values start at the // network address and negative values wrap at the network address, which // means a "-1" value on a network will be the broadcast address after // wrapping is applied. if !signRe.MatchString(value) { return IfAddr{}, fmt.Errorf("sign (+/-) is required for operation %q", operation) } switch sockType := inputIfAddr.SockAddr.Type(); sockType { case TypeIPv4: // 33 == Accept any uint32 value // TODO(seanc@): Add the ability to parse hex i, err := strconv.ParseInt(value, 10, 33) if err != nil { return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err) } ipv4 := *ToIPv4Addr(inputIfAddr.SockAddr) ipv4Uint32 := uint32(ipv4.NetworkAddress()) // Wrap along network mask boundaries. EZ-mode wrapping made possible by // use of int64 vs a uint. var wrappedMask int64 if i >= 0 { wrappedMask = i } else { wrappedMask = 1 + i + int64(^uint32(ipv4.Mask)) } ipv4Uint32 = ipv4Uint32 + (uint32(wrappedMask) &^ uint32(ipv4.Mask)) return IfAddr{ SockAddr: IPv4Addr{ Address: IPv4Address(ipv4Uint32), Mask: ipv4.Mask, }, Interface: inputIfAddr.Interface, }, nil case TypeIPv6: // 64 == Accept any int32 value // TODO(seanc@): Add the ability to parse hex. Also parse a bignum int. i, err := strconv.ParseInt(value, 10, 64) if err != nil { return IfAddr{}, fmt.Errorf("unable to convert %q to int for operation %q: %v", value, operation, err) } ipv6 := *ToIPv6Addr(inputIfAddr.SockAddr) ipv6BigInt := new(big.Int) ipv6BigInt.Set(ipv6.NetworkAddress()) mask := new(big.Int) mask.Set(ipv6.Mask) if i > 0 { wrappedMask := new(big.Int) wrappedMask.SetInt64(i) wrappedMask.AndNot(wrappedMask, mask) ipv6BigInt.Add(ipv6BigInt, wrappedMask) } else { // Mask off any bits that exceed the network size. Subtract the // wrappedMask from the last usable - 1 wrappedMask := new(big.Int) wrappedMask.SetInt64(-1 * i) wrappedMask.Sub(wrappedMask, big.NewInt(1)) wrappedMask.AndNot(wrappedMask, mask) lastUsable := new(big.Int) lastUsable.Set(ipv6.LastUsable().(IPv6Addr).Address) ipv6BigInt = lastUsable.Sub(lastUsable, wrappedMask) } return IfAddr{ SockAddr: IPv6Addr{ Address: IPv6Address(ipv6BigInt), Mask: ipv6.Mask, }, Interface: inputIfAddr.Interface, }, nil default: return IfAddr{}, fmt.Errorf("unsupported type for operation %q: %T", operation, sockType) } default: return IfAddr{}, fmt.Errorf("unsupported math operation: %q", operation) } } // IfAddrsMath will apply an IfAddrMath operation each IfAddr struct. Any // failure will result in zero results. func IfAddrsMath(operation, value string, inputIfAddrs IfAddrs) (IfAddrs, error) { outputAddrs := make(IfAddrs, 0, len(inputIfAddrs)) for _, ifAddr := range inputIfAddrs { result, err := IfAddrMath(operation, value, ifAddr) if err != nil { return IfAddrs{}, fmt.Errorf("unable to perform an IPMath operation on %s: %v", ifAddr, err) } outputAddrs = append(outputAddrs, result) } return outputAddrs, nil } // IncludeIfs returns an IfAddrs based on the passed in selector. func IncludeIfs(selectorName, selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) { var includedIfs IfAddrs var err error switch strings.ToLower(selectorName) { case "address": includedIfs, _, err = IfByAddress(selectorParam, inputIfAddrs) case "flag", "flags": includedIfs, _, err = IfByFlag(selectorParam, inputIfAddrs) case "name": includedIfs, _, err = IfByName(selectorParam, inputIfAddrs) case "network": includedIfs, _, err = IfByNetwork(selectorParam, inputIfAddrs) case "port": includedIfs, _, err = IfByPort(selectorParam, inputIfAddrs) case "rfc", "rfcs": includedIfs, _, err = IfByRFCs(selectorParam, inputIfAddrs) case "size": includedIfs, _, err = IfByMaskSize(selectorParam, inputIfAddrs) case "type": includedIfs, _, err = IfByType(selectorParam, inputIfAddrs) default: return IfAddrs{}, fmt.Errorf("invalid include selector %q", selectorName) } if err != nil { return IfAddrs{}, err } return includedIfs, nil } // ExcludeIfs returns an IfAddrs based on the passed in selector. func ExcludeIfs(selectorName, selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) { var excludedIfs IfAddrs var err error switch strings.ToLower(selectorName) { case "address": _, excludedIfs, err = IfByAddress(selectorParam, inputIfAddrs) case "flag", "flags": _, excludedIfs, err = IfByFlag(selectorParam, inputIfAddrs) case "name": _, excludedIfs, err = IfByName(selectorParam, inputIfAddrs) case "network": _, excludedIfs, err = IfByNetwork(selectorParam, inputIfAddrs) case "port": _, excludedIfs, err = IfByPort(selectorParam, inputIfAddrs) case "rfc", "rfcs": _, excludedIfs, err = IfByRFCs(selectorParam, inputIfAddrs) case "size": _, excludedIfs, err = IfByMaskSize(selectorParam, inputIfAddrs) case "type": _, excludedIfs, err = IfByType(selectorParam, inputIfAddrs) default: return IfAddrs{}, fmt.Errorf("invalid exclude selector %q", selectorName) } if err != nil { return IfAddrs{}, err } return excludedIfs, nil } // SortIfBy returns an IfAddrs sorted based on the passed in selector. Multiple // sort clauses can be passed in as a comma delimited list without whitespace. func SortIfBy(selectorParam string, inputIfAddrs IfAddrs) (IfAddrs, error) { sortedIfs := append(IfAddrs(nil), inputIfAddrs...) clauses := strings.Split(selectorParam, ",") sortFuncs := make([]CmpIfAddrFunc, len(clauses)) for i, clause := range clauses { switch strings.TrimSpace(strings.ToLower(clause)) { case "+address", "address": // The "address" selector returns an array of IfAddrs // ordered by the network address. IfAddrs that are not // comparable will be at the end of the list and in a // non-deterministic order. sortFuncs[i] = AscIfAddress case "-address": sortFuncs[i] = DescIfAddress case "+default", "default": sortFuncs[i] = AscIfDefault case "-default": sortFuncs[i] = DescIfDefault case "+name", "name": // The "name" selector returns an array of IfAddrs // ordered by the interface name. sortFuncs[i] = AscIfName case "-name": sortFuncs[i] = DescIfName case "+port", "port": // The "port" selector returns an array of IfAddrs // ordered by the port, if included in the IfAddr. // IfAddrs that are not comparable will be at the end of // the list and in a non-deterministic order. sortFuncs[i] = AscIfPort case "-port": sortFuncs[i] = DescIfPort case "+private", "private": // The "private" selector returns an array of IfAddrs // ordered by private addresses first. IfAddrs that are // not comparable will be at the end of the list and in // a non-deterministic order. sortFuncs[i] = AscIfPrivate case "-private": sortFuncs[i] = DescIfPrivate case "+size", "size": // The "size" selector returns an array of IfAddrs // ordered by the size of the network mask, smaller mask // (larger number of hosts per network) to largest // (e.g. a /24 sorts before a /32). sortFuncs[i] = AscIfNetworkSize case "-size": sortFuncs[i] = DescIfNetworkSize case "+type", "type": // The "type" selector returns an array of IfAddrs // ordered by the type of the IfAddr. The sort order is // Unix, IPv4, then IPv6. sortFuncs[i] = AscIfType case "-type": sortFuncs[i] = DescIfType default: // Return an empty list for invalid sort types. return IfAddrs{}, fmt.Errorf("unknown sort type: %q", clause) } } OrderedIfAddrBy(sortFuncs...).Sort(sortedIfs) return sortedIfs, nil } // UniqueIfAddrsBy creates a unique set of IfAddrs based on the matching // selector. UniqueIfAddrsBy assumes the input has already been sorted. func UniqueIfAddrsBy(selectorName string, inputIfAddrs IfAddrs) (IfAddrs, error) { attrName := strings.ToLower(selectorName) ifs := make(IfAddrs, 0, len(inputIfAddrs)) var lastMatch string for _, ifAddr := range inputIfAddrs { var out string switch attrName { case "address": out = ifAddr.SockAddr.String() case "name": out = ifAddr.Name default: return nil, fmt.Errorf("unsupported unique constraint %+q", selectorName) } switch { case lastMatch == "", lastMatch != out: lastMatch = out ifs = append(ifs, ifAddr) case lastMatch == out: continue } } return ifs, nil } // JoinIfAddrs joins an IfAddrs and returns a string func JoinIfAddrs(selectorName string, joinStr string, inputIfAddrs IfAddrs) (string, error) { outputs := make([]string, 0, len(inputIfAddrs)) attrName := AttrName(strings.ToLower(selectorName)) for _, ifAddr := range inputIfAddrs { var attrVal string var err error attrVal, err = ifAddr.Attr(attrName) if err != nil { return "", err } outputs = append(outputs, attrVal) } return strings.Join(outputs, joinStr), nil } // LimitIfAddrs returns a slice of IfAddrs based on the specified limit. func LimitIfAddrs(lim uint, in IfAddrs) (IfAddrs, error) { // Clamp the limit to the length of the array if int(lim) > len(in) { lim = uint(len(in)) } return in[0:lim], nil } // OffsetIfAddrs returns a slice of IfAddrs based on the specified offset. func OffsetIfAddrs(off int, in IfAddrs) (IfAddrs, error) { var end bool if off < 0 { end = true off = off * -1 } if off > len(in) { return IfAddrs{}, fmt.Errorf("unable to seek past the end of the interface array: offset (%d) exceeds the number of interfaces (%d)", off, len(in)) } if end { return in[len(in)-off:], nil } return in[off:], nil } func (ifAddr IfAddr) String() string { return fmt.Sprintf("%s %v", ifAddr.SockAddr, ifAddr.Interface) } // parseDefaultIfNameFromRoute parses standard route(8)'s output for the *BSDs // and Solaris. func parseDefaultIfNameFromRoute(routeOut string) (string, error) { lines := strings.Split(routeOut, "\n") for _, line := range lines { kvs := strings.SplitN(line, ":", 2) if len(kvs) != 2 { continue } if strings.TrimSpace(kvs[0]) == "interface" { ifName := strings.TrimSpace(kvs[1]) return ifName, nil } } return "", errors.New("No default interface found") } // parseDefaultIfNameFromIPCmd parses the default interface from ip(8) for // Linux. func parseDefaultIfNameFromIPCmd(routeOut string) (string, error) { lines := strings.Split(routeOut, "\n") re := whitespaceRE.Copy() for _, line := range lines { kvs := re.Split(line, -1) if len(kvs) < 5 { continue } if kvs[0] == "default" && kvs[1] == "via" && kvs[3] == "dev" { ifName := strings.TrimSpace(kvs[4]) return ifName, nil } } return "", errors.New("No default interface found") } // parseDefaultIfNameWindows parses the default interface from `netstat -rn` and // `ipconfig` on Windows. func parseDefaultIfNameWindows(routeOut, ipconfigOut string) (string, error) { defaultIPAddr, err := parseDefaultIPAddrWindowsRoute(routeOut) if err != nil { return "", err } ifName, err := parseDefaultIfNameWindowsIPConfig(defaultIPAddr, ipconfigOut) if err != nil { return "", err } return ifName, nil } // parseDefaultIPAddrWindowsRoute parses the IP address on the default interface // `netstat -rn`. // // NOTES(sean): Only IPv4 addresses are parsed at this time. If you have an // IPv6 connected host, submit an issue on github.com/hashicorp/go-sockaddr with // the output from `netstat -rn`, `ipconfig`, and version of Windows to see IPv6 // support added. func parseDefaultIPAddrWindowsRoute(routeOut string) (string, error) { lines := strings.Split(routeOut, "\n") re := whitespaceRE.Copy() for _, line := range lines { kvs := re.Split(strings.TrimSpace(line), -1) if len(kvs) < 3 { continue } if kvs[0] == "0.0.0.0" && kvs[1] == "0.0.0.0" { defaultIPAddr := strings.TrimSpace(kvs[3]) return defaultIPAddr, nil } } return "", errors.New("No IP on default interface found") } // parseDefaultIfNameWindowsIPConfig parses the output of `ipconfig` to find the // interface name forwarding traffic to the default gateway. func parseDefaultIfNameWindowsIPConfig(defaultIPAddr, routeOut string) (string, error) { lines := strings.Split(routeOut, "\n") ifNameRe := ifNameRE.Copy() ipAddrRe := ipAddrRE.Copy() var ifName string for _, line := range lines { switch ifNameMatches := ifNameRe.FindStringSubmatch(line); { case len(ifNameMatches) > 1: ifName = ifNameMatches[1] continue } switch ipAddrMatches := ipAddrRe.FindStringSubmatch(line); { case len(ipAddrMatches) > 1 && ipAddrMatches[1] == defaultIPAddr: return ifName, nil } } return "", errors.New("No default interface found with matching IP") } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/ifaddrs_test.go000066400000000000000000001367601316221320600263340ustar00rootroot00000000000000package sockaddr_test import ( "fmt" "net" "reflect" "testing" sockaddr "github.com/hashicorp/go-sockaddr" ) const ( // NOTE(seanc@): Assume "en0" is the interface with a default route attached // to it. When this is not the case, change this one constant and tests // should pass (i.e. "net0"). ifNameWithDefault = "en0" ) // NOTE: A number of these code paths are exercised in template/ and // cmd/sockaddr/. // // TODO(sean@): Add better coverage for filtering functions (e.g. ExcludeBy*, // IncludeBy*). func TestCmpIfAddrFunc(t *testing.T) { tests := []struct { name string t1 sockaddr.IfAddr // must come before t2 according to the ascOp t2 sockaddr.IfAddr ascOp sockaddr.CmpIfAddrFunc ascResult int descOp sockaddr.CmpIfAddrFunc descResult int }{ { name: "empty test", t1: sockaddr.IfAddr{}, t2: sockaddr.IfAddr{}, ascOp: sockaddr.AscIfAddress, descOp: sockaddr.DescIfAddress, ascResult: 0, descResult: 0, }, { name: "ipv4 address less", t1: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.3"), }, t2: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, ascOp: sockaddr.AscIfAddress, descOp: sockaddr.DescIfAddress, ascResult: -1, descResult: -1, }, { name: "ipv4 private", t1: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("10.1.2.3"), }, t2: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("203.0.113.3"), }, ascOp: sockaddr.AscIfPrivate, descOp: sockaddr.DescIfPrivate, ascResult: 0, // not both private, can't complete the test descResult: 0, }, { name: "IfAddr name", t1: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("10.1.2.3"), Interface: net.Interface{ Name: "abc0", }, }, t2: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("203.0.113.3"), Interface: net.Interface{ Name: "xyz0", }, }, ascOp: sockaddr.AscIfName, descOp: sockaddr.DescIfName, ascResult: -1, descResult: -1, }, { name: "IfAddr network size", t1: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("10.0.0.0/8"), }, t2: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.0/24"), }, ascOp: sockaddr.AscIfNetworkSize, descOp: sockaddr.DescIfNetworkSize, ascResult: -1, descResult: -1, }, { name: "IfAddr port", t1: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("10.0.0.0:80"), }, t2: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("127.0.0.0:8600"), }, ascOp: sockaddr.AscIfPort, descOp: sockaddr.DescIfPort, ascResult: -1, descResult: -1, }, { name: "IfAddr type", t1: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("10.0.0.0:80"), }, t2: sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("[::1]:80"), }, ascOp: sockaddr.AscIfType, descOp: sockaddr.DescIfType, ascResult: -1, descResult: -1, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d must have a name", i) } // Test ascending operation ascExpected := test.ascResult ascResult := test.ascOp(&test.t1, &test.t2) if ascResult != ascExpected { t.Errorf("%s: Unexpected result %d, expected %d when comparing %v and %v using %v", test.name, ascResult, ascExpected, test.t1, test.t2, test.ascOp) } // Test descending operation descExpected := test.descResult descResult := test.descOp(&test.t2, &test.t1) if descResult != descExpected { t.Errorf("%s: Unexpected result %d, expected %d when comparing %v and %v using %v", test.name, descResult, descExpected, test.t1, test.t2, test.descOp) } if ascResult != descResult { t.Fatalf("bad") } // Reverse the args ascExpected = -1 * test.ascResult ascResult = test.ascOp(&test.t2, &test.t1) if ascResult != ascExpected { t.Errorf("%s: Unexpected result %d, expected %d when comparing %v and %v using %v", test.name, ascResult, ascExpected, test.t1, test.t2, test.ascOp) } descExpected = -1 * test.descResult descResult = test.descOp(&test.t1, &test.t2) if descResult != descExpected { t.Errorf("%s: Unexpected result %d, expected %d when comparing %v and %v using %v", test.name, descResult, descExpected, test.t1, test.t2, test.descOp) } if ascResult != descResult { t.Fatalf("bad") } // Test equality ascExpected = 0 ascResult = test.ascOp(&test.t1, &test.t1) if ascResult != ascExpected { t.Errorf("%s: Unexpected result %d, expected %d when comparing %v and %v using %v", test.name, ascResult, ascExpected, test.t1, test.t2, test.ascOp) } descExpected = 0 descResult = test.descOp(&test.t1, &test.t1) if descResult != descExpected { t.Errorf("%s: Unexpected result %d, expected %d when comparing %v and %v using %v", test.name, descResult, descExpected, test.t1, test.t2, test.descOp) } } } func TestFilterIfByFlags(t *testing.T) { tests := []struct { name string selector string ifAddrs sockaddr.IfAddrs flags net.Flags fail bool }{ { name: "broadcast", selector: "broadcast", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{ Flags: net.FlagBroadcast, }, SockAddr: sockaddr.MustIPv4Addr("1.2.3.1"), }, }, }, { name: "down", selector: "down", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{}, SockAddr: sockaddr.MustIPv4Addr("1.2.3.2"), }, }, }, { name: "forwardable IPv4", selector: "forwardable", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{}, SockAddr: sockaddr.MustIPv4Addr("1.2.3.3"), }, }, }, { name: "forwardable IPv6", selector: "forwardable", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{}, SockAddr: sockaddr.MustIPv6Addr("cc::1/128"), }, }, }, { name: "global unicast", selector: "global unicast", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{}, SockAddr: sockaddr.MustIPv6Addr("cc::2"), }, }, }, { name: "interface-local multicast", selector: "interface-local multicast", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{}, SockAddr: sockaddr.MustIPv6Addr("ff01::2"), }, }, }, { name: "link-local multicast", selector: "link-local multicast", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{}, SockAddr: sockaddr.MustIPv6Addr("ff02::3"), }, }, }, { name: "link-local unicast IPv4", selector: "link-local unicast", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{}, SockAddr: sockaddr.MustIPv4Addr("169.254.1.101"), }, }, }, { name: "link-local unicast IPv6", selector: "link-local unicast", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{}, SockAddr: sockaddr.MustIPv6Addr("fe80::3"), }, }, }, { name: "loopback ipv4", selector: "loopback", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{ Flags: net.FlagLoopback, }, SockAddr: sockaddr.MustIPv4Addr("127.0.0.1"), }, }, }, { name: "loopback ipv6", selector: "loopback", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{ Flags: net.FlagLoopback, }, SockAddr: sockaddr.MustIPv6Addr("::1"), }, }, }, { name: "multicast IPv4", selector: "multicast", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{ Flags: net.FlagMulticast, }, SockAddr: sockaddr.MustIPv4Addr("224.0.0.1"), }, }, }, { name: "multicast IPv6", selector: "multicast", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{ Flags: net.FlagMulticast, }, SockAddr: sockaddr.MustIPv6Addr("ff05::3"), }, }, }, { name: "point-to-point", selector: "point-to-point", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{ Flags: net.FlagPointToPoint, }, SockAddr: sockaddr.MustIPv6Addr("cc::3"), }, }, }, { name: "unspecified", selector: "unspecified", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{}, SockAddr: sockaddr.MustIPv6Addr("::"), }, }, }, { name: "up", selector: "up", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{ Flags: net.FlagUp, }, SockAddr: sockaddr.MustIPv6Addr("cc::3"), }, }, }, { name: "invalid", selector: "foo", fail: true, ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{}, SockAddr: sockaddr.MustIPv6Addr("cc::3"), }, }, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d needs a name", i) } t.Run(test.name, func(t *testing.T) { in, out, err := sockaddr.IfByFlag(test.selector, test.ifAddrs) if test.fail == true && err == nil { t.Fatalf("%s: expected failure", test.name) } else if test.fail == true && err != nil { return } if err != nil && test.fail != true { t.Fatalf("%s: failed: %v", test.name, err) } if ilen := len(in); ilen != 1 { t.Fatalf("%s: wrong in length %d, expected 1", test.name, ilen) } if olen := len(out); olen != 0 { t.Fatalf("%s: wrong in length %d, expected 0", test.name, olen) } }) } } func TestIfByNetwork(t *testing.T) { tests := []struct { name string input sockaddr.IfAddrs selector string matched sockaddr.IfAddrs excluded sockaddr.IfAddrs fail bool }{ { name: "exact match", input: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, }, selector: "1.2.3.4", matched: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, }, }, { name: "exact match plural", input: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.0/24"), }, }, selector: "1.2.3.0/24", matched: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.0/24"), }, }, }, { name: "split plural", input: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("12.2.3.0/24"), }, }, selector: "1.2.3.0/24", excluded: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("12.2.3.0/24"), }, }, matched: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, }, }, { name: "excluded plural", input: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("12.2.3.0/24"), }, }, selector: "10.0.0.0/8", excluded: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("12.2.3.0/24"), }, }, }, { name: "invalid selector", input: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("12.2.3.0/24"), }, }, selector: "[:]", fail: true, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d needs a name", i) } t.Run(test.name, func(t *testing.T) { matched, excluded, err := sockaddr.IfByNetwork(test.selector, test.input) if err != nil && !test.fail { t.Fatal("bad") } else if err == nil && test.fail { t.Fatal("bad") } if len(test.matched) != len(matched) { t.Fatal("bad") } else if len(test.excluded) != len(excluded) { t.Fatal("bad") } for i := 0; i < len(test.excluded); i++ { if !reflect.DeepEqual(test.excluded[i], excluded[i]) { t.Errorf("wrong excluded: %d %v %v", i, test.excluded[i], excluded[i]) } } for i := 0; i < len(test.matched); i++ { if !reflect.DeepEqual(test.matched[i], matched[i]) { t.Errorf("wrong matched: %d %v %v", i, test.matched[i], matched[i]) } } }) } } func TestFilterIfByType(t *testing.T) { tests := []struct { name string ifAddrs sockaddr.IfAddrs ifAddrType sockaddr.SockAddrType matchedLen int remainingLen int }{ { name: "include all", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("2.3.4.5"), }, }, ifAddrType: sockaddr.TypeIPv4, matchedLen: 2, remainingLen: 0, }, { name: "include some", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::1"), }, }, ifAddrType: sockaddr.TypeIPv4, matchedLen: 1, remainingLen: 1, }, { name: "exclude all", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.5"), }, }, ifAddrType: sockaddr.TypeIPv6, matchedLen: 0, remainingLen: 2, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d needs a name", i) } in, out := sockaddr.FilterIfByType(test.ifAddrs, test.ifAddrType) if len(in) != test.matchedLen { t.Fatalf("%s: wrong length %d, expected %d", test.name, len(in), test.matchedLen) } if len(out) != test.remainingLen { t.Fatalf("%s: wrong length %d, expected %d", test.name, len(out), test.remainingLen) } } } // TestGetIfAddrs runs through the motions of calling sockaddr.GetIfAddrs(), but // doesn't do much in the way of testing beyond verifying that `lo0` has a // loopback address present. func TestGetIfAddrs(t *testing.T) { ifAddrs, err := sockaddr.GetAllInterfaces() if err != nil { t.Fatalf("Unable to proceed: %v", err) } if len(ifAddrs) == 0 { t.Skip() } var loInt *sockaddr.IfAddr for _, ifAddr := range ifAddrs { val := sockaddr.IfAddrAttr(ifAddr, "name") if val == "" { t.Fatalf("name failed") } else if val == "lo0" || val == "lo" || val == "Loopback Pseudo-Interface 1" { loInt = &ifAddr break } } if loInt == nil { t.Fatalf("No loopback interfaces found, loInt nil") } if val := sockaddr.IfAddrAttr(*loInt, "flags"); !(val == "up|loopback|multicast" || val == "up|loopback") { t.Fatalf("expected different flags from loopback: %q", val) } if loInt == nil { t.Fatalf("Expected to find an lo0 interface, didn't find any") } haveIPv4, foundIPv4lo := false, false haveIPv6, foundIPv6lo := false, false switch loInt.SockAddr.(type) { case sockaddr.IPv4Addr: haveIPv4 = true // Make the semi-brittle assumption that if we have // IPv4, we also have an address at 127.0.0.1 available // to us. if loInt.SockAddr.String() == "127.0.0.1/8" { foundIPv4lo = true } case sockaddr.IPv6Addr: haveIPv6 = true if loInt.SockAddr.String() == "::1" { foundIPv6lo = true } default: t.Fatalf("Unsupported type %v for address %v", loInt.Type(), loInt) } // While not wise, it's entirely possible a host doesn't have IPv4 // enabled. if haveIPv4 && !foundIPv4lo { t.Fatalf("Had an IPv4 w/o an expected IPv4 loopback addresses") } // While prudent to run without, a sane environment may still contain an // IPv6 loopback address. if haveIPv6 && !foundIPv6lo { t.Fatalf("Had an IPv6 w/o an expected IPv6 loopback addresses") } } // TestGetDefaultIfName tests to make sure a default interface name is always // returned from getDefaultIfName(). func TestGetDefaultInterface(t *testing.T) { reportOnDefault := func(args ...interface{}) { if havePublicIP() || havePrivateIP() { t.Fatalf(args[0].(string), args[1:]...) } else { t.Skipf(args[0].(string), args[1:]...) } } ifAddrs, err := sockaddr.GetDefaultInterfaces() if err != nil { switch { case len(ifAddrs) == 0: reportOnDefault("bad: %v", err) case ifAddrs[0].Flags&net.FlagUp == 0: reportOnDefault("bad: %v", err) default: reportOnDefault("bad: %v", err) } } } func TestIfAddrAttrs(t *testing.T) { const expectedNumAttrs = 2 attrs := sockaddr.IfAddrAttrs() if len(attrs) != expectedNumAttrs { t.Fatalf("wrong number of attrs") } tests := []struct { name string ifAddr sockaddr.IfAddr attr string expected string }{ { name: "name", ifAddr: sockaddr.IfAddr{ Interface: net.Interface{ Name: "abc0", }, }, attr: "name", expected: "abc0", }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d must have a name", i) } result, err := sockaddr.IfAttrs(test.attr, sockaddr.IfAddrs{test.ifAddr}) if err != nil { t.Errorf("failed to get attr %q from %v", test.name, test.ifAddr) } if result != test.expected { t.Errorf("unexpected result") } } // Test an empty array result, err := sockaddr.IfAttrs("name", sockaddr.IfAddrs{}) if err != nil { t.Error(`failed to get attr "name" from an empty array`) } if result != "" { t.Errorf("unexpected result") } } func TestGetAllInterfaces(t *testing.T) { ifAddrs, err := sockaddr.GetAllInterfaces() if err != nil { t.Fatalf("unable to gather interfaces: %v", err) } initialLen := len(ifAddrs) if initialLen == 0 { t.Fatalf("no interfaces available") } ifAddrs, err = sockaddr.SortIfBy("name,type,port,size,address", ifAddrs) if err != nil { t.Fatalf("unable to initially sort address") } ascSorted, err := sockaddr.SortIfBy("name,type,port,size,address", ifAddrs) if err != nil { t.Fatalf("unable to asc sort address") } descSorted, err := sockaddr.SortIfBy("name,type,port,size,-address", ascSorted) if err != nil { t.Fatalf("unable to desc sort address") } if initialLen != len(ascSorted) && len(ascSorted) != len(descSorted) { t.Fatalf("wrong len") } for i := initialLen - 1; i >= 0; i-- { if !reflect.DeepEqual(descSorted[i], ifAddrs[i]) { t.Errorf("wrong sort order: %d %v %v", i, descSorted[i], ifAddrs[i]) } } } func TestGetDefaultInterfaces(t *testing.T) { reportOnDefault := func(args ...interface{}) { if havePublicIP() || havePrivateIP() { t.Fatalf(args[0].(string), args[1:]...) } else { t.Skipf(args[0].(string), args[1:]...) } } ifAddrs, err := sockaddr.GetDefaultInterfaces() if err != nil { reportOnDefault("unable to gather default interfaces: %v", err) } if len(ifAddrs) == 0 { reportOnDefault("no default interfaces available", nil) } } func TestGetPrivateInterfaces(t *testing.T) { reportOnPrivate := func(args ...interface{}) { if havePrivateIP() { t.Fatalf(args[0].(string), args[1:]...) } else { t.Skipf(args[0].(string), args[1:]...) } } ifAddrs, err := sockaddr.GetPrivateInterfaces() if err != nil { reportOnPrivate("failed: %v", err) } if len(ifAddrs) == 0 { reportOnPrivate("no public IPs found") } if len(ifAddrs[0].String()) == 0 { reportOnPrivate("no string representation of private IP found") } } func TestGetPublicInterfaces(t *testing.T) { reportOnPublic := func(args ...interface{}) { if havePublicIP() { t.Fatalf(args[0].(string), args[1:]...) } else { t.Skipf(args[0].(string), args[1:]...) } } ifAddrs, err := sockaddr.GetPublicInterfaces() if err != nil { reportOnPublic("failed: %v", err) } if len(ifAddrs) == 0 { reportOnPublic("no public IPs found") } } func TestIncludeExcludeIfs(t *testing.T) { tests := []struct { name string ifAddrs sockaddr.IfAddrs fail bool excludeNum int excludeName string excludeParam string includeName string includeParam string includeNum int }{ { name: "address", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("2.3.4.5"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("3.4.5.6"), }, }, excludeName: "address", excludeNum: 2, excludeParam: `^1\..*\.4$`, includeName: "address", includeNum: 1, includeParam: `^1\.2\.3\.`, }, { name: "address invalid", fail: true, excludeName: "address", excludeNum: 0, excludeParam: `*`, includeName: "address", includeNum: 0, includeParam: `[`, }, { name: "flag", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{ Flags: net.FlagUp | net.FlagLoopback, }, }, sockaddr.IfAddr{ Interface: net.Interface{ Flags: net.FlagLoopback, }, }, sockaddr.IfAddr{ Interface: net.Interface{ Flags: net.FlagMulticast, }, }, }, excludeName: "flags", excludeNum: 2, excludeParam: `up|loopback`, includeName: "flags", includeNum: 2, includeParam: `loopback`, }, { name: "flag invalid", fail: true, excludeName: "foo", excludeNum: 0, excludeParam: `*`, includeName: "bar", includeNum: 0, includeParam: `[`, }, { name: "name", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ Interface: net.Interface{ Name: "abc0", }, }, sockaddr.IfAddr{ Interface: net.Interface{ Name: "xyz0", }, }, sockaddr.IfAddr{ Interface: net.Interface{ Name: "docker666", }, }, }, excludeName: "name", excludeNum: 2, excludeParam: `^docker[\d]+$`, includeName: "name", includeNum: 2, includeParam: `^([a-z]+)0$`, }, { name: "name invalid", fail: true, excludeName: "name", excludeNum: 0, excludeParam: `*`, includeName: "name", includeNum: 0, includeParam: `[`, }, { name: "network", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("10.2.3.4/24"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("10.255.255.4/24"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("::1"), }, }, excludeName: "network", excludeNum: 1, excludeParam: `10.0.0.0/8`, includeName: "network", includeNum: 1, includeParam: `::/127`, }, { name: "port", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:8600"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("2.3.4.5:4646"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("3.4.5.6:4647"), }, }, excludeName: "port", excludeNum: 2, excludeParam: `0$`, includeName: "port", includeNum: 2, includeParam: `^46[\d]{2}$`, }, { name: "port invalid", fail: true, excludeName: "port", excludeNum: 0, excludeParam: `*`, includeName: "port", includeNum: 0, includeParam: `[`, }, { name: "rfc", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("10.2.3.4/24"), }, sockaddr.IfAddr{ // Excluded (/127 vs /128) SockAddr: sockaddr.MustIPv6Addr("::1/127"), }, sockaddr.IfAddr{ // Excluded (/127 vs /128) SockAddr: sockaddr.MustIPv6Addr("::/127"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("203.0.113.0/24"), }, }, excludeName: "rfc", excludeNum: 2, excludeParam: `6890`, includeName: "rfc", includeNum: 1, includeParam: `3330`, }, { name: "rfc invalid", fail: true, excludeName: "rfc", excludeNum: 0, excludeParam: `rfcOneTwoThree`, includeName: "rfc", includeNum: 0, includeParam: `99999999999999`, }, { name: "rfc IPv4 exclude", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("192.169.1.1"), }, }, excludeName: "rfc", excludeNum: 1, excludeParam: `1918`, includeName: "rfc", includeNum: 0, includeParam: `1918`, }, { name: "rfc IPv4 include", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("192.168.1.1"), }, }, excludeName: "rfc", excludeNum: 0, excludeParam: `1918`, includeName: "rfc", includeNum: 1, includeParam: `1918`, }, { name: "rfc IPv4 excluded RFCs", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("192.168.1.1"), }, }, excludeName: "rfc", excludeNum: 1, excludeParam: `4291`, includeName: "rfc", includeNum: 0, includeParam: `4291`, }, { name: "rfc IPv6 exclude", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("cc::1/127"), }, }, excludeName: "rfc", excludeNum: 1, excludeParam: `4291`, includeName: "rfc", includeNum: 0, includeParam: `4291`, }, { name: "rfc IPv6 include", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::1/127"), }, }, excludeName: "rfc", excludeNum: 0, excludeParam: `4291`, includeName: "rfc", includeNum: 1, includeParam: `4291`, }, { name: "rfc zero match", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), }, }, excludeName: "rfc", excludeNum: 1, excludeParam: `1918`, includeName: "rfc", includeNum: 0, includeParam: `1918`, }, { name: "rfc empty list", ifAddrs: sockaddr.IfAddrs{}, excludeName: "rfc", excludeNum: 0, excludeParam: `4291`, includeName: "rfc", includeNum: 0, includeParam: `1918`, }, { name: "size", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("10.2.3.4/24"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("203.0.113.0/24"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::1/24"), }, }, excludeName: "size", excludeParam: `24`, excludeNum: 0, includeName: "size", includeParam: `24`, includeNum: 3, }, { name: "size invalid", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("10.2.3.4/24"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::1/128"), }, }, fail: true, excludeName: "size", excludeParam: `33`, excludeNum: 0, includeName: "size", includeParam: `-1`, includeNum: 0, }, { name: "type", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("10.2.3.4/24"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("203.0.113.0/24"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv6Addr("::1/127"), }, }, excludeName: "type", excludeParam: `ipv6`, excludeNum: 2, includeName: "type", includeParam: `ipv4`, includeNum: 2, }, { name: "type", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("10.2.3.4/24"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("::1"), }, sockaddr.IfAddr{ SockAddr: sockaddr.MustUnixSock("/tmp/foo"), }, }, excludeName: "type", excludeParam: `ip`, excludeNum: 1, includeName: "type", includeParam: `unix`, includeNum: 1, }, { name: "type invalid arg", fail: true, excludeName: "type", excludeParam: `*`, excludeNum: 0, includeName: "type", includeParam: `[`, includeNum: 0, }, { name: "type invalid", fail: true, excludeName: "foo", excludeParam: `bar`, excludeNum: 0, includeName: "baz", includeParam: `bur`, includeNum: 0, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d must have a name", i) } t.Run(fmt.Sprintf("%s-%s", test.name, "include"), func(t *testing.T) { t.Logf("test.ifAddrs: %v", test.ifAddrs) inIfAddrs, err := sockaddr.IncludeIfs(test.includeName, test.includeParam, test.ifAddrs) t.Logf("inIfAddrs: %v", inIfAddrs) switch { case !test.fail && err != nil: t.Errorf("%s: failed unexpectedly: %v", test.name, err) case test.fail && err == nil: t.Errorf("%s: failed to throw an error", test.name) case test.fail && err != nil: // expected test failure return } if len(inIfAddrs) != test.includeNum { t.Errorf("%s: failed include length check. Expected %d, got %d. Input: %q", test.name, test.includeNum, len(inIfAddrs), test.includeParam) } }) t.Run(fmt.Sprintf("%s-%s", test.name, "exclude"), func(t *testing.T) { t.Logf("test.ifAddrs: %v", test.ifAddrs) outIfAddrs, err := sockaddr.ExcludeIfs(test.excludeName, test.excludeParam, test.ifAddrs) t.Logf("outIfAddrs: %v", outIfAddrs) switch { case !test.fail && err != nil: t.Errorf("%s: failed unexpectedly: %v", test.name, err) case test.fail && err == nil: t.Errorf("%s: failed to throw an error", test.name) case test.fail && err != nil: // expected test failure return } if len(outIfAddrs) != test.excludeNum { t.Errorf("%s: failed exclude length check. Expected %d, got %d. Input: %q", test.name, test.excludeNum, len(outIfAddrs), test.excludeParam) } }) } } func TestNewIPAddr(t *testing.T) { tests := []struct { name string input string output string pass bool }{ { name: "ipv4", input: "1.2.3.4", output: "1.2.3.4", pass: true, }, { name: "ipv6", input: "::1", output: "::1", pass: true, }, { name: "invalid", input: "255.255.255.256", output: "", pass: false, }, } for _, test := range tests { ip, err := sockaddr.NewIPAddr(test.input) switch { case err == nil && test.pass, err != nil && !test.pass: default: t.Errorf("expected %s's success to be %t", test.input, test.pass) } if !test.pass { continue } ipStr := ip.String() if ipStr != test.output { t.Errorf("Expected %q to match %q", test.input, test.output, ipStr) } } } func TestIPAttrs(t *testing.T) { const expectedIPAttrs = 11 ipAttrs := sockaddr.IPAttrs() if len(ipAttrs) != expectedIPAttrs { t.Fatalf("wrong number of args") } } func TestUniqueIfAddrsBy(t *testing.T) { tests := []struct { name string ifAddrs sockaddr.IfAddrs fail bool selector string expected []string }{ { name: "address", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("203.0.113.0/24"), Interface: net.Interface{ Name: "abc0", }, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("203.0.113.0/24"), Interface: net.Interface{ Name: "abc0", }, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("10.2.3.4"), Interface: net.Interface{ Name: "foo1", }, }, }, selector: "address", expected: []string{"203.0.113.0/24 {0 0 abc0 0}", "10.2.3.4 {0 0 foo1 0}"}, }, { name: "name", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("::1"), Interface: net.Interface{ Name: "lo0", }, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("fe80::1"), Interface: net.Interface{ Name: "lo0", }, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("127.0.0.1"), Interface: net.Interface{ Name: "foo1", }, }, }, selector: "name", expected: []string{"::1 {0 0 lo0 0}", "127.0.0.1 {0 0 foo1 0}"}, }, { name: "invalid", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{}, }, fail: true, selector: "goozfraba", expected: []string{}, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d must have a name", i) } t.Run(test.name, func(t *testing.T) { uniqueAddrs, err := sockaddr.UniqueIfAddrsBy(test.selector, test.ifAddrs) switch { case !test.fail && err != nil: t.Fatalf("%s: failed unexpectedly: %v", test.name, err) case test.fail && err == nil: t.Fatalf("%s: failed to throw an error", test.name) case test.fail && err != nil: // expected test failure return } if len(uniqueAddrs) != len(test.expected) { t.Fatalf("%s: failed uniquify by attribute %s", test.name, test.selector) } for i := 0; i < len(uniqueAddrs); i++ { got := uniqueAddrs[i].String() if got != test.expected[i] { t.Fatalf("%s: expected %q got %q", test.name, test.expected[i], got) } } }) } } func TestJoinIfAddrsBy(t *testing.T) { tests := []struct { name string ifAddrs sockaddr.IfAddrs fail bool selector string joinStr string expected string }{ { name: "address", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("203.0.113.0/24"), Interface: net.Interface{ Name: "abc0", }, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("203.0.113.1"), Interface: net.Interface{ Name: "abc0", }, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("10.2.3.4"), Interface: net.Interface{ Name: "foo1", }, }, }, selector: "address", joinStr: " ", expected: "203.0.113.0 203.0.113.1 10.2.3.4", }, { name: "name", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("::1"), Interface: net.Interface{ Name: "lo0", }, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("fe80::1"), Interface: net.Interface{ Name: "foo0", }, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("127.0.0.1"), Interface: net.Interface{ Name: "bar2", }, }, }, selector: "name", joinStr: "-/-", expected: "lo0-/-foo0-/-bar2", }, { name: "invalid", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPAddr("127.0.0.1"), Interface: net.Interface{ Name: "bar2", }, }, }, fail: true, selector: "goozfraba", expected: "", }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d must have a name", i) } t.Run(test.name, func(t *testing.T) { result, err := sockaddr.JoinIfAddrs(test.selector, test.joinStr, test.ifAddrs) switch { case !test.fail && err != nil: t.Fatalf("%s: failed unexpectedly: %v", test.name, err) case test.fail && err == nil: t.Fatalf("%s: failed to throw an error", test.name) case test.fail && err != nil: // expected test failure return } if result != test.expected { t.Fatalf("%s: expected %q got %q", test.name, test.expected, result) } }) } } func TestLimitOffset(t *testing.T) { tests := []struct { name string ifAddrs sockaddr.IfAddrs limit uint offset int fail bool expected sockaddr.IfAddrs }{ { name: "basic limit offset", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.0/24")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.1")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.2")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.3")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.4")}, }, limit: 2, offset: 1, expected: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.1")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.2")}, }, }, { name: "negative offset with limit", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.0/24")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.1")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.2")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.3")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.4")}, }, limit: 2, offset: -3, expected: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.2")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.3")}, }, }, { name: "large limit", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.0/24")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.1")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.2")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.3")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.4")}, }, limit: 100, offset: 3, expected: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.3")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.4")}, }, }, { name: "bigger offset than size", ifAddrs: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.0/24")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPAddr("203.0.113.1")}, }, fail: true, limit: 1, offset: 3, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d must have a name", i) } t.Run(test.name, func(t *testing.T) { offsetResults, err := sockaddr.OffsetIfAddrs(test.offset, test.ifAddrs) switch { case !test.fail && err != nil: t.Fatalf("%s: failed unexpectedly: %v", test.name, err) case test.fail && err == nil: t.Fatalf("%s: failed to throw an error", test.name) case test.fail && err != nil: // expected test failure return } limitResults, err := sockaddr.LimitIfAddrs(test.limit, offsetResults) switch { case !test.fail && err != nil: t.Fatalf("%s: failed unexpectedly: %v", test.name, err) case test.fail && err == nil: t.Fatalf("%s: failed to throw an error", test.name) case test.fail && err != nil: // expected test failure return } if len(test.expected) != len(limitResults) { t.Fatalf("bad") } for i := 0; i < len(test.expected); i++ { if !reflect.DeepEqual(limitResults[i], test.expected[i]) { t.Errorf("objects in ordered limit") } } }) } } func TestSortIfBy(t *testing.T) { tests := []struct { name string sortStr string in sockaddr.IfAddrs out sockaddr.IfAddrs fail bool }{ { name: "sort address", sortStr: "address", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.3")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.3")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4")}, }, }, { name: "sort +address", sortStr: "+address", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.3")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.3")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4")}, }, }, { name: "sort -address", sortStr: "-address", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.3")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.3")}, }, }, { // NOTE(seanc@): This test requires macOS, or at least a computer where // en0 has the default route. name: "sort default", sortStr: "default", in: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), Interface: net.Interface{Name: ifNameWithDefault}, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.3"), Interface: net.Interface{Name: "other0"}, }, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), Interface: net.Interface{Name: ifNameWithDefault}, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.3"), Interface: net.Interface{Name: "other0"}, }, }, }, { // NOTE(seanc@): This test requires macOS, or at least a computer where // en0 has the default route. name: "sort +default", sortStr: "+default", in: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), Interface: net.Interface{Name: "other0"}, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.3"), Interface: net.Interface{Name: ifNameWithDefault}, }, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.3"), Interface: net.Interface{Name: ifNameWithDefault}, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), Interface: net.Interface{Name: "other0"}, }, }, }, { name: "sort -default", sortStr: "-default", in: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.3"), Interface: net.Interface{Name: ifNameWithDefault}, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), Interface: net.Interface{Name: "other0"}, }, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.4"), Interface: net.Interface{Name: "other0"}, }, sockaddr.IfAddr{ SockAddr: sockaddr.MustIPv4Addr("1.2.3.3"), Interface: net.Interface{Name: ifNameWithDefault}, }, }, }, { name: "sort name", sortStr: "name", in: sockaddr.IfAddrs{ sockaddr.IfAddr{Interface: net.Interface{Name: "foo"}}, sockaddr.IfAddr{Interface: net.Interface{Name: "bar"}}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{Interface: net.Interface{Name: "bar"}}, sockaddr.IfAddr{Interface: net.Interface{Name: "foo"}}, }, }, { name: "sort +name", sortStr: "+name", in: sockaddr.IfAddrs{ sockaddr.IfAddr{Interface: net.Interface{Name: "foo"}}, sockaddr.IfAddr{Interface: net.Interface{Name: "bar"}}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{Interface: net.Interface{Name: "bar"}}, sockaddr.IfAddr{Interface: net.Interface{Name: "foo"}}, }, }, { name: "sort -name", sortStr: "-name", in: sockaddr.IfAddrs{ sockaddr.IfAddr{Interface: net.Interface{Name: "bar"}}, sockaddr.IfAddr{Interface: net.Interface{Name: "foo"}}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{Interface: net.Interface{Name: "foo"}}, sockaddr.IfAddr{Interface: net.Interface{Name: "bar"}}, }, }, { name: "sort port", sortStr: "port", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv6Addr("[::1]:53")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv6Addr("[::1]:53")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, }, { name: "sort +port", sortStr: "+port", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv6Addr("[::1]:53")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv6Addr("[::1]:53")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, }, { name: "sort -port", sortStr: "-port", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv6Addr("[::1]:53")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv6Addr("[::1]:53")}, }, }, { name: "sort private", sortStr: "private", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, }, { name: "sort +private", sortStr: "+private", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, }, { name: "sort -private", sortStr: "-private", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1")}, }, }, { name: "sort size", sortStr: "size", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1/27")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1/27")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, }, { name: "sort +size", sortStr: "+size", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1/27")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1/27")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, }, { name: "sort -size", sortStr: "-size", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1/27")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1/27")}, }, }, { name: "sort type", sortStr: "type", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv6Addr("::1")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1/27")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1/27")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv6Addr("::1")}, }, }, { name: "sort +type", sortStr: "+type", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv6Addr("::1")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1/27")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("192.168.1.1/27")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv6Addr("::1")}, }, }, { name: "sort -type", sortStr: "-type", in: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv6Addr("::1")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, out: sockaddr.IfAddrs{ sockaddr.IfAddr{SockAddr: sockaddr.MustIPv6Addr("::1")}, sockaddr.IfAddr{SockAddr: sockaddr.MustIPv4Addr("1.2.3.4:80")}, }, }, { name: "sort invalid", sortStr: "ENOENT", fail: true, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %i needs a name", i) } t.Run(test.name, func(t *testing.T) { sorted, err := sockaddr.SortIfBy(test.sortStr, test.in) if err != nil && !test.fail { t.Fatalf("%s: sort failed: %v", test.name, err) } if len(test.in) != len(sorted) { t.Fatalf("wrong len") } for i := 0; i < len(sorted); i++ { if !reflect.DeepEqual(sorted[i], test.out[i]) { t.Errorf("wrong sort order: %d %v %v", i, sorted[i], test.out[i]) } } }) } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/ifattr.go000066400000000000000000000024531316221320600251410ustar00rootroot00000000000000package sockaddr import ( "fmt" "net" ) // IfAddr is a union of a SockAddr and a net.Interface. type IfAddr struct { SockAddr net.Interface } // Attr returns the named attribute as a string func (ifAddr IfAddr) Attr(attrName AttrName) (string, error) { val := IfAddrAttr(ifAddr, attrName) if val != "" { return val, nil } return Attr(ifAddr.SockAddr, attrName) } // Attr returns the named attribute as a string func Attr(sa SockAddr, attrName AttrName) (string, error) { switch sockType := sa.Type(); { case sockType&TypeIP != 0: ip := *ToIPAddr(sa) attrVal := IPAddrAttr(ip, attrName) if attrVal != "" { return attrVal, nil } if sockType == TypeIPv4 { ipv4 := *ToIPv4Addr(sa) attrVal := IPv4AddrAttr(ipv4, attrName) if attrVal != "" { return attrVal, nil } } else if sockType == TypeIPv6 { ipv6 := *ToIPv6Addr(sa) attrVal := IPv6AddrAttr(ipv6, attrName) if attrVal != "" { return attrVal, nil } } case sockType == TypeUnix: us := *ToUnixSock(sa) attrVal := UnixSockAttr(us, attrName) if attrVal != "" { return attrVal, nil } } // Non type-specific attributes switch attrName { case "string": return sa.String(), nil case "type": return sa.Type().String(), nil } return "", fmt.Errorf("unsupported attribute name %q", attrName) } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/ifattr_test.go000066400000000000000000000052771316221320600262070ustar00rootroot00000000000000package sockaddr_test import ( "testing" sockaddr "github.com/hashicorp/go-sockaddr" ) func TestIfAttr_net(t *testing.T) { ifAddrs, err := sockaddr.GetAllInterfaces() if err != nil { t.Fatalf("Unable to proceed: %v", err) } for _, ifAddr := range ifAddrs { testSockAddrAttr(t, ifAddr) } } func TestIfAttr_unix(t *testing.T) { newUnixSock := func(path string) sockaddr.UnixSock { sa, err := sockaddr.NewUnixSock(path) if err != nil { t.Fatalf("unable to create new unix socket: %v", err) } return sa } unixSockets := []sockaddr.SockAddr{ newUnixSock("/tmp/test"), } for _, sa := range unixSockets { testSockAddrAttr(t, sa) } } func testSockAddrAttr(t *testing.T, sai interface{}) { attrNamesPerType := []struct { name sockaddr.AttrName ipv4Pass bool ipv6Pass bool unixPass bool }{ // Universal {"type", true, true, true}, {"string", true, true, true}, // IP {"name", true, true, false}, {"size", true, true, false}, {"flags", true, true, false}, {"host", true, true, false}, {"address", true, true, false}, {"port", true, true, false}, {"netmask", true, true, false}, {"network", true, true, false}, {"mask_bits", true, true, false}, {"binary", true, true, false}, {"hex", true, true, false}, {"first_usable", true, true, false}, {"last_usable", true, true, false}, {"octets", true, true, false}, // IPv4 {"broadcast", true, false, false}, {"uint32", true, false, false}, // IPv6 {"uint128", false, true, false}, // Unix {"path", false, false, true}, } for _, attrTest := range attrNamesPerType { switch v := sai.(type) { case sockaddr.IfAddr: saType := v.Type() _, err := v.Attr(attrTest.name) switch saType { case sockaddr.TypeIPv4: if err == nil && attrTest.ipv4Pass || err != nil && !attrTest.ipv4Pass { // pass } // fallthrough case sockaddr.TypeIPv6: if err == nil && attrTest.ipv6Pass || err != nil && !attrTest.ipv6Pass { // pass } // fallthrough case sockaddr.TypeUnix: if err == nil && attrTest.unixPass || err != nil && !attrTest.unixPass { // pass } // fallthrough default: t.Errorf("Unable to fetch attr name %q: %v", attrTest.name, err) } case sockaddr.SockAddr: val, err := sockaddr.Attr(v, attrTest.name) _ = err pass := len(val) > 0 switch { case v.Type() == sockaddr.TypeIPv4 && attrTest.ipv4Pass == pass, v.Type() == sockaddr.TypeIPv6 && attrTest.ipv6Pass == pass, v.Type() == sockaddr.TypeUnix && attrTest.unixPass == pass: // pass default: t.Errorf("Unable to fetch attr name %q from %v / %v + %+q", attrTest.name, v, v.Type(), val) } default: t.Fatal("unsupported type %T %v", sai, sai) } } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/ipaddr.go000066400000000000000000000073061316221320600251150ustar00rootroot00000000000000package sockaddr import ( "fmt" "math/big" "net" "strings" ) // Constants for the sizes of IPv3, IPv4, and IPv6 address types. const ( IPv3len = 6 IPv4len = 4 IPv6len = 16 ) // IPAddr is a generic IP address interface for IPv4 and IPv6 addresses, // networks, and socket endpoints. type IPAddr interface { SockAddr AddressBinString() string AddressHexString() string Cmp(SockAddr) int CmpAddress(SockAddr) int CmpPort(SockAddr) int FirstUsable() IPAddr Host() IPAddr IPPort() IPPort LastUsable() IPAddr Maskbits() int NetIP() *net.IP NetIPMask() *net.IPMask NetIPNet() *net.IPNet Network() IPAddr Octets() []int } // IPPort is the type for an IP port number for the TCP and UDP IP transports. type IPPort uint16 // IPPrefixLen is a typed integer representing the prefix length for a given // IPAddr. type IPPrefixLen byte // ipAddrAttrMap is a map of the IPAddr type-specific attributes. var ipAddrAttrMap map[AttrName]func(IPAddr) string var ipAddrAttrs []AttrName func init() { ipAddrInit() } // NewIPAddr creates a new IPAddr from a string. Returns nil if the string is // not an IPv4 or an IPv6 address. func NewIPAddr(addr string) (IPAddr, error) { ipv4Addr, err := NewIPv4Addr(addr) if err == nil { return ipv4Addr, nil } ipv6Addr, err := NewIPv6Addr(addr) if err == nil { return ipv6Addr, nil } return nil, fmt.Errorf("invalid IPAddr %v", addr) } // IPAddrAttr returns a string representation of an attribute for the given // IPAddr. func IPAddrAttr(ip IPAddr, selector AttrName) string { fn, found := ipAddrAttrMap[selector] if !found { return "" } return fn(ip) } // IPAttrs returns a list of attributes supported by the IPAddr type func IPAttrs() []AttrName { return ipAddrAttrs } // MustIPAddr is a helper method that must return an IPAddr or panic on invalid // input. func MustIPAddr(addr string) IPAddr { ip, err := NewIPAddr(addr) if err != nil { panic(fmt.Sprintf("Unable to create an IPAddr from %+q: %v", addr, err)) } return ip } // ipAddrInit is called once at init() func ipAddrInit() { // Sorted for human readability ipAddrAttrs = []AttrName{ "host", "address", "port", "netmask", "network", "mask_bits", "binary", "hex", "first_usable", "last_usable", "octets", } ipAddrAttrMap = map[AttrName]func(ip IPAddr) string{ "address": func(ip IPAddr) string { return ip.NetIP().String() }, "binary": func(ip IPAddr) string { return ip.AddressBinString() }, "first_usable": func(ip IPAddr) string { return ip.FirstUsable().String() }, "hex": func(ip IPAddr) string { return ip.AddressHexString() }, "host": func(ip IPAddr) string { return ip.Host().String() }, "last_usable": func(ip IPAddr) string { return ip.LastUsable().String() }, "mask_bits": func(ip IPAddr) string { return fmt.Sprintf("%d", ip.Maskbits()) }, "netmask": func(ip IPAddr) string { switch v := ip.(type) { case IPv4Addr: ipv4Mask := IPv4Addr{ Address: IPv4Address(v.Mask), Mask: IPv4HostMask, } return ipv4Mask.String() case IPv6Addr: ipv6Mask := new(big.Int) ipv6Mask.Set(v.Mask) ipv6MaskAddr := IPv6Addr{ Address: IPv6Address(ipv6Mask), Mask: ipv6HostMask, } return ipv6MaskAddr.String() default: return fmt.Sprintf("", ip) } }, "network": func(ip IPAddr) string { return ip.Network().NetIP().String() }, "octets": func(ip IPAddr) string { octets := ip.Octets() octetStrs := make([]string, 0, len(octets)) for _, octet := range octets { octetStrs = append(octetStrs, fmt.Sprintf("%d", octet)) } return strings.Join(octetStrs, " ") }, "port": func(ip IPAddr) string { return fmt.Sprintf("%d", ip.IPPort()) }, } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/ipaddr_test.go000066400000000000000000000136101316221320600261470ustar00rootroot00000000000000package sockaddr_test import ( "fmt" "testing" "github.com/hashicorp/go-sockaddr" ) func TestSockAddr_IPAddr_CmpAddress(t *testing.T) { tests := []struct { a string b string cmp int }{ { // 0: Same IPAddr (v4), same port a: "208.67.222.222:0", b: "208.67.222.222/32", cmp: 0, }, { // 1: Same IPAddr (v6), same port a: "[2607:f0d0:1002:0051:0000:0000:0000:0004]:0", b: "2607:f0d0:1002:0051:0000:0000:0000:0004/128", cmp: 0, }, { // 2: Same IPAddr (v4), different port a: "208.67.222.222:4646", b: "208.67.222.222/32", cmp: 0, }, { // 3: Same IPAddr (v6), different port a: "[2607:f0d0:1002:0051:0000:0000:0000:0004]:4646", b: "[2607:f0d0:1002:0051:0000:0000:0000:0004]:4647", cmp: 0, }, { // 4: Different IPAddr (v4), same port a: "208.67.220.220:4648", b: "208.67.222.222:4648", cmp: -1, }, { // 5: Different IPAddr (v6), same port a: "[2607:f0d0:1002:0051:0000:0000:0000:0004]:4648", b: "[2607:f0d0:1002:0052:0000:0000:0000:0004]:4648", cmp: -1, }, { // 6: Different IPAddr (v4), different port a: "208.67.220.220:8600", b: "208.67.222.222:4648", cmp: -1, }, { // 7: Different IPAddr (v6), different port a: "[2607:f0d0:1002:0051:0000:0000:0000:0004]:8500", b: "[2607:f0d0:1002:0052:0000:0000:0000:0004]:4648", cmp: -1, }, { // 8: Incompatible IPAddr (v4 vs v6), same port a: "208.67.220.220:8600", b: "[2607:f0d0:1002:0051:0000:0000:0000:0004]:8600", cmp: 0, }, { // 9: Incompatible IPAddr (v4 vs v6), different port a: "208.67.220.220:8500", b: "[2607:f0d0:1002:0051:0000:0000:0000:0004]:8600", cmp: 0, }, { // 10: Incompatible SockAddr types a: "128.95.120.1:123", b: "/tmp/foo.sock", cmp: 0, }, { // 11: Incompatible SockAddr types a: "[::]:123", b: "/tmp/foo.sock", cmp: 0, }, } for idx, test := range tests { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { saA, err := sockaddr.NewSockAddr(test.a) if err != nil { t.Fatalf("[%d] Unable to create a SockAddr from %+q: %v", idx, test.a, err) } saB, err := sockaddr.NewSockAddr(test.b) if err != nil { t.Fatalf("[%d] Unable to create an SockAddr from %+q: %v", idx, test.b, err) } ipA, ok := saA.(sockaddr.IPAddr) if !ok { t.Fatalf("[%d] Unable to convert SockAddr %+q to an IPAddr", idx, test.a) } if x := ipA.CmpAddress(saB); x != test.cmp { t.Errorf("[%d] IPAddr.CmpAddress() failed with %+q with %+q (expected %d, received %d)", idx, ipA, saB, test.cmp, x) } ipB, ok := saB.(sockaddr.IPAddr) if !ok { // Return success for comparing non-IPAddr types return } if x := ipA.CmpAddress(ipB); x != test.cmp { t.Errorf("[%d] IPAddr.CmpAddress() failed with %+q with %+q (expected %d, received %d)", idx, ipA, ipB, test.cmp, x) } if x := ipB.CmpAddress(ipA); x*-1 != test.cmp { t.Errorf("[%d] IPAddr.CmpAddress() failed with %+q with %+q (expected %d, received %d)", idx, ipB, ipA, test.cmp, x) } if x := ipB.CmpAddress(saA); x*-1 != test.cmp { t.Errorf("[%d] IPAddr.CmpAddress() failed with %+q with %+q (expected %d, received %d)", idx, ipB, saA, test.cmp, x) } }) } } func TestSockAddr_IPAddr_CmpPort(t *testing.T) { tests := []struct { a string b string cmp int }{ { // 0: Same IPv4Addr, same port a: "208.67.222.222:0", b: "208.67.222.222/32", cmp: 0, }, { // 1: Different IPv4Addr, same port a: "208.67.220.220:0", b: "208.67.222.222/32", cmp: 0, }, { // 2: Same IPv4Addr, different port a: "208.67.222.222:80", b: "208.67.222.222:443", cmp: -1, }, { // 3: Different IPv4Addr, different port a: "208.67.220.220:8600", b: "208.67.222.222:53", cmp: 1, }, { // 4: Same IPv6Addr, same port a: "[::]:0", b: "::/128", cmp: 0, }, { // 5: Different IPv6Addr, same port a: "[::]:0", b: "[2607:f0d0:1002:0051:0000:0000:0000:0004]:0", cmp: 0, }, { // 6: Same IPv6Addr, different port a: "[::]:8400", b: "[::]:8600", cmp: -1, }, { // 7: Different IPv6Addr, different port a: "[::]:8600", b: "[2607:f0d0:1002:0051:0000:0000:0000:0004]:53", cmp: 1, }, { // 8: Mixed IPAddr types, same port a: "[::]:53", b: "208.67.220.220:53", cmp: 0, }, { // 9: Mixed IPAddr types, different port a: "[::]:53", b: "128.95.120.1:123", cmp: -1, }, { // 10: Incompatible SockAddr types a: "128.95.120.1:123", b: "/tmp/foo.sock", cmp: 0, }, { // 11: Incompatible SockAddr types a: "[::]:123", b: "/tmp/foo.sock", cmp: 0, }, } for idx, test := range tests { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { saA, err := sockaddr.NewSockAddr(test.a) if err != nil { t.Fatalf("[%d] Unable to create a SockAddr from %+q: %v", idx, test.a, err) } saB, err := sockaddr.NewSockAddr(test.b) if err != nil { t.Fatalf("[%d] Unable to create an SockAddr from %+q: %v", idx, test.b, err) } ipA, ok := saA.(sockaddr.IPAddr) if !ok { t.Fatalf("[%d] Unable to convert SockAddr %+q to an IPAddr", idx, test.a) } if x := ipA.CmpPort(saB); x != test.cmp { t.Errorf("[%d] IPAddr.CmpPort() failed with %+q with %+q (expected %d, received %d)", idx, ipA, saB, test.cmp, x) } ipB, ok := saB.(sockaddr.IPAddr) if !ok { // Return success for comparing non-IPAddr types return } if x := ipA.CmpPort(ipB); x != test.cmp { t.Errorf("[%d] IPAddr.CmpPort() failed with %+q with %+q (expected %d, received %d)", idx, ipA, ipB, test.cmp, x) } if x := ipB.CmpPort(ipA); x*-1 != test.cmp { t.Errorf("[%d] IPAddr.CmpPort() failed with %+q with %+q (expected %d, received %d)", idx, ipB, ipA, test.cmp, x) } if x := ipB.CmpPort(saA); x*-1 != test.cmp { t.Errorf("[%d] IPAddr.CmpPort() failed with %+q with %+q (expected %d, received %d)", idx, ipB, saA, test.cmp, x) } }) } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/ipaddrs.go000066400000000000000000000064061316221320600253000ustar00rootroot00000000000000package sockaddr import "bytes" type IPAddrs []IPAddr func (s IPAddrs) Len() int { return len(s) } func (s IPAddrs) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // // SortIPAddrsByCmp is a type that satisfies sort.Interface and can be used // // by the routines in this package. The SortIPAddrsByCmp type is used to // // sort IPAddrs by Cmp() // type SortIPAddrsByCmp struct{ IPAddrs } // // Less reports whether the element with index i should sort before the // // element with index j. // func (s SortIPAddrsByCmp) Less(i, j int) bool { // // Sort by Type, then address, then port number. // return Less(s.IPAddrs[i], s.IPAddrs[j]) // } // SortIPAddrsBySpecificMaskLen is a type that satisfies sort.Interface and // can be used by the routines in this package. The // SortIPAddrsBySpecificMaskLen type is used to sort IPAddrs by smallest // network (most specific to largest network). type SortIPAddrsByNetworkSize struct{ IPAddrs } // Less reports whether the element with index i should sort before the // element with index j. func (s SortIPAddrsByNetworkSize) Less(i, j int) bool { // Sort masks with a larger binary value (i.e. fewer hosts per network // prefix) after masks with a smaller value (larger number of hosts per // prefix). switch bytes.Compare([]byte(*s.IPAddrs[i].NetIPMask()), []byte(*s.IPAddrs[j].NetIPMask())) { case 0: // Fall through to the second test if the net.IPMasks are the // same. break case 1: return true case -1: return false default: panic("bad, m'kay?") } // Sort IPs based on the length (i.e. prefer IPv4 over IPv6). iLen := len(*s.IPAddrs[i].NetIP()) jLen := len(*s.IPAddrs[j].NetIP()) if iLen != jLen { return iLen > jLen } // Sort IPs based on their network address from lowest to highest. switch bytes.Compare(s.IPAddrs[i].NetIPNet().IP, s.IPAddrs[j].NetIPNet().IP) { case 0: break case 1: return false case -1: return true default: panic("lol wut?") } // If a host does not have a port set, it always sorts after hosts // that have a port (e.g. a host with a /32 and port number is more // specific and should sort first over a host with a /32 but no port // set). if s.IPAddrs[i].IPPort() == 0 || s.IPAddrs[j].IPPort() == 0 { return false } return s.IPAddrs[i].IPPort() < s.IPAddrs[j].IPPort() } // SortIPAddrsBySpecificMaskLen is a type that satisfies sort.Interface and // can be used by the routines in this package. The // SortIPAddrsBySpecificMaskLen type is used to sort IPAddrs by smallest // network (most specific to largest network). type SortIPAddrsBySpecificMaskLen struct{ IPAddrs } // Less reports whether the element with index i should sort before the // element with index j. func (s SortIPAddrsBySpecificMaskLen) Less(i, j int) bool { return s.IPAddrs[i].Maskbits() > s.IPAddrs[j].Maskbits() } // SortIPAddrsByBroadMaskLen is a type that satisfies sort.Interface and can // be used by the routines in this package. The SortIPAddrsByBroadMaskLen // type is used to sort IPAddrs by largest network (i.e. largest subnets // first). type SortIPAddrsByBroadMaskLen struct{ IPAddrs } // Less reports whether the element with index i should sort before the // element with index j. func (s SortIPAddrsByBroadMaskLen) Less(i, j int) bool { return s.IPAddrs[i].Maskbits() < s.IPAddrs[j].Maskbits() } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/ipaddrs_test.go000066400000000000000000000274501316221320600263410ustar00rootroot00000000000000package sockaddr_test import ( "fmt" "sort" "testing" "github.com/hashicorp/go-sockaddr" ) type GoodTestIPAddrTest struct { sockAddrs sockaddr.SockAddrs sortedBySpecificMasklen sockaddr.SockAddrs sortedByBroadMasklen sockaddr.SockAddrs sortedByNetwork sockaddr.SockAddrs } type GoodTestIPAddrTests []*GoodTestIPAddrTest func makeTestIPAddrs(t *testing.T) GoodTestIPAddrTests { goodTestInputs := []struct { sockAddrs []string sortedBySpecificMasklen []string sortedByBroadMasklen []string sortedByNetwork []string }{ { sockAddrs: []string{ "10.0.0.0/8", "172.16.1.3/12", "192.168.0.0/16", "128.95.120.1/32", "192.168.1.10/24", "240.0.0.1/4", }, sortedBySpecificMasklen: []string{ "128.95.120.1/32", "192.168.1.10/24", "192.168.0.0/16", "172.16.1.3/12", "10.0.0.0/8", "240.0.0.1/4", }, sortedByBroadMasklen: []string{ "240.0.0.1/4", "10.0.0.0/8", "172.16.1.3/12", "192.168.0.0/16", "192.168.1.10/24", "128.95.120.1/32", }, sortedByNetwork: []string{ "10.0.0.0/8", "128.95.120.1/32", "172.16.1.3/12", "192.168.0.0/16", "192.168.1.10/24", "240.0.0.1/4", }, }, } gfs := make(GoodTestIPAddrTests, 0, len(goodTestInputs)) for idx, gfi := range goodTestInputs { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { gf := new(GoodTestIPAddrTest) gf.sockAddrs = make(sockaddr.SockAddrs, 0, len(gfi.sockAddrs)) for _, n := range gfi.sockAddrs { sa, err := sockaddr.NewSockAddr(n) if err != nil { t.Fatalf("Expected valid network") } gf.sockAddrs = append(gf.sockAddrs, sa) } gf.sortedBySpecificMasklen = make(sockaddr.SockAddrs, 0, len(gfi.sortedBySpecificMasklen)) for _, n := range gfi.sortedBySpecificMasklen { na, err := sockaddr.NewSockAddr(n) if err != nil { t.Fatalf("Expected valid network") } gf.sortedBySpecificMasklen = append(gf.sortedBySpecificMasklen, na) } if len(gf.sockAddrs) != len(gf.sortedBySpecificMasklen) { t.Fatalf("Expected same number of sortedBySpecificMasklen networks") } gf.sortedByBroadMasklen = make(sockaddr.SockAddrs, 0, len(gfi.sortedByBroadMasklen)) for _, n := range gfi.sortedByBroadMasklen { na, err := sockaddr.NewSockAddr(n) if err != nil { t.Fatalf("Expected valid network") } gf.sortedByBroadMasklen = append(gf.sortedByBroadMasklen, na) } if len(gf.sockAddrs) != len(gf.sortedByBroadMasklen) { t.Fatalf("Expected same number of sortedByBroadMasklen networks") } gf.sortedByNetwork = make(sockaddr.SockAddrs, 0, len(gfi.sortedByNetwork)) for _, n := range gfi.sortedByNetwork { na, err := sockaddr.NewSockAddr(n) if err != nil { t.Fatalf("Expected valid network") } gf.sortedByNetwork = append(gf.sortedByNetwork, na) } if len(gf.sockAddrs) != len(gf.sortedByNetwork) { t.Fatalf("Expected same number of sortedByNetwork networks") } }) } return gfs } func TestSockAddr_IPAddrs_BySpecificMaskLen(t *testing.T) { testInputs := sockAddrStringInputs{ { inputAddrs: []string{"10.0.0.0/8", "172.16.1.3/12", "192.168.0.0/16", "128.95.120.1/32", "192.168.1.10/24", "240.0.0.1/4", }, sortedAddrs: []string{ "128.95.120.1/32", "192.168.1.10/24", "192.168.0.0/16", "172.16.1.3/12", "10.0.0.0/8", "240.0.0.1/4", }, }, } for idx, test := range testInputs { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { inputAddrs := convertToSockAddrs(t, test.inputAddrs) sortedAddrs := convertToSockAddrs(t, test.sortedAddrs) sockaddrs := append(sockaddr.SockAddrs(nil), inputAddrs...) filteredAddrs, _ := sockaddrs.FilterByType(sockaddr.TypeIPv4) ipv4Addrs := make([]sockaddr.IPv4Addr, 0, len(filteredAddrs)) for _, x := range filteredAddrs { switch v := x.(type) { case sockaddr.IPv4Addr: ipv4Addrs = append(ipv4Addrs, v) default: t.Fatalf("invalid type") } } ipAddrs := make([]sockaddr.IPAddr, 0, len(filteredAddrs)) for _, x := range filteredAddrs { ipAddr, ok := x.(sockaddr.IPAddr) if !ok { t.Fatalf("Unable to typecast to IPAddr") } ipAddrs = append(ipAddrs, ipAddr) } sort.Sort(sockaddr.SortIPAddrsBySpecificMaskLen{ipAddrs}) var lastLen int = 32 for i, netaddr := range ipAddrs { maskLen := netaddr.Maskbits() if lastLen < maskLen { t.Fatalf("Sort by specific mask length failed") } lastLen = maskLen if sortedAddrs[i] != netaddr { t.Errorf("Expected %s, received %s in iteration %d", sortedAddrs[i], netaddr, i) } } }) } } func TestSockAddr_IPAddrs_ByBroadMaskLen(t *testing.T) { testInputs := sockAddrStringInputs{ { inputAddrs: []string{"10.0.0.0/8", "172.16.1.3/12", "192.168.0.0/16", "128.95.120.1/32", "192.168.1.10/24", "240.0.0.1/4", }, sortedAddrs: []string{ "240.0.0.1/4", "10.0.0.0/8", "172.16.1.3/12", "192.168.0.0/16", "192.168.1.10/24", "128.95.120.1/32", }, }, } for idx, test := range testInputs { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { inputAddrs := convertToSockAddrs(t, test.inputAddrs) sortedAddrs := convertToSockAddrs(t, test.sortedAddrs) sockaddrs := append(sockaddr.SockAddrs(nil), inputAddrs...) filteredAddrs, _ := sockaddrs.FilterByType(sockaddr.TypeIP) ipAddrs := make([]sockaddr.IPAddr, 0, len(filteredAddrs)) for _, x := range filteredAddrs { ipAddr, ok := x.(sockaddr.IPAddr) if !ok { t.Fatalf("Unable to typecast to IPAddr") } ipAddrs = append(ipAddrs, ipAddr) } sort.Sort(sockaddr.SortIPAddrsByBroadMaskLen{ipAddrs}) var lastLen int for i, netaddr := range ipAddrs { maskLen := netaddr.Maskbits() if lastLen > maskLen { t.Fatalf("Sort by specific mask length failed") } lastLen = maskLen if sortedAddrs[i] != netaddr { t.Errorf("Expected %s, received %s in iteration %d", sortedAddrs[i], netaddr, i) } } }) } } func TestSockAddr_IPAddrs_IPAddrsByNetwork(t *testing.T) { testInputs := sockAddrStringInputs{ { inputAddrs: []string{ "10.0.0.0/8", "172.16.1.3/12", "192.168.0.0/16", "128.95.120.1/32", "192.168.1.10/24", "240.0.0.1/4", }, sortedAddrs: []string{ "10.0.0.0/8", "128.95.120.1/32", "172.16.1.3/12", "192.168.0.0/16", "192.168.1.10/24", "240.0.0.1/4", }, }, } for idx, test := range testInputs { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { inputAddrs := convertToSockAddrs(t, test.inputAddrs) sortedAddrs := convertToSockAddrs(t, test.sortedAddrs) sockaddrs := append(sockaddr.SockAddrs(nil), inputAddrs...) ipaddrs, _ := sockaddrs.FilterByType(sockaddr.TypeIP) sockaddr.OrderedAddrBy(sockaddr.AscAddress).Sort(ipaddrs) var lastIpUint sockaddr.IPv4Address for i, sa := range ipaddrs { ipv4 := *sockaddr.ToIPv4Addr(sa) if lastIpUint > ipv4.Address { t.Fatalf("Sort by network failed") } lastIpUint = ipv4.Address if !ipv4.Equal(sortedAddrs[i]) { t.Errorf("[%d] Sort equality failed: expected %s, received %s", i, sortedAddrs[i], ipv4) } } }) } } func TestSockAddr_IPAddrs_IPAddrsByNetworkSize(t *testing.T) { testInputs := sockAddrStringInputs{ { inputAddrs: []string{ "10.0.0.0/8", "172.16.1.3/12", "128.95.120.2:53", "128.95.120.2/32", "192.168.0.0/16", "128.95.120.1/32", "192.168.1.10/24", "128.95.120.2:8600", "240.0.0.1/4", }, sortedAddrs: []string{ "128.95.120.1/32", "128.95.120.2:53", "128.95.120.2:8600", "128.95.120.2/32", "192.168.1.10/24", "192.168.0.0/16", "172.16.1.3/12", "10.0.0.0/8", "240.0.0.1/4", }, }, } for idx, test := range testInputs { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { inputAddrs := convertToSockAddrs(t, test.inputAddrs) sortedAddrs := convertToSockAddrs(t, test.sortedAddrs) sockaddrs := append(sockaddr.SockAddrs(nil), inputAddrs...) filteredAddrs, _ := sockaddrs.FilterByType(sockaddr.TypeIP) ipAddrs := make([]sockaddr.IPAddr, 0, len(filteredAddrs)) for _, x := range filteredAddrs { ipAddr, ok := x.(sockaddr.IPAddr) if !ok { t.Fatalf("Unable to typecast to IPAddr") } ipAddrs = append(ipAddrs, ipAddr) } sort.Sort(sockaddr.SortIPAddrsByNetworkSize{ipAddrs}) // var prevAddr sockaddr.IPAddr for i, ipAddr := range ipAddrs { // if i == 0 { // prevAddr = ipAddr // continue // } // if prevAddr.Cmp(ipAddr) > 0 { // t.Logf("[%d] Prev:\t%v", i, prevAddr) // t.Logf("[%d] ipAddr:\t%v", i, ipAddr) // t.Fatalf("Sort by network failed") // } // prevAddr = ipAddr if !ipAddr.Equal(sortedAddrs[i]) { t.Errorf("[%d] Sort equality failed: expected %s, received %s", i, sortedAddrs[i], ipAddr) } } }) } } // func TestSockAddr_IPAddrs_IPAddrsByCmp(t *testing.T) { // testInputs := testIPAddrsInputs{ // { // sockAddrs: []string{ // "10.0.0.0/8", // "172.16.1.3/12", // "128.95.120.2:53", // "128.95.120.2/32", // "192.168.0.0/16", // "128.95.120.1/32", // "192.168.1.10/24", // "128.95.120.2:8600", // "240.0.0.1/4", // }, // sortedSockAddrs: []string{ // "128.95.120.1/32", // "128.95.120.2:53", // "128.95.120.2:8600", // "128.95.120.2/32", // "192.168.1.10/24", // "192.168.0.0/16", // "172.16.1.3/12", // "10.0.0.0/8", // "240.0.0.1/4", // }, // }, // } // for _, test := range makeTestsFromInput(t, testInputs) { // sockaddrs := append(sockaddr.SockAddrs(nil), test.sockAddrs...) // ipAddrs := sockaddrs.FilterByTypeIPAddr() // sort.Sort(sockaddr.SortIPAddrsByCmp{ipAddrs}) // t.Logf("Here: %+v", ipAddrs) // var prevAddr sockaddr.IPAddr // for i, ipAddr := range ipAddrs { // if i == 0 { // prevAddr = ipAddr // continue // } // if prevAddr.Cmp(ipAddr) > 0 { // t.Logf("[%d] Prev:\t%v", i, prevAddr) // t.Logf("[%d] ipAddr:\t%v", i, ipAddr) // t.Fatalf("Sort by network failed") // } // prevAddr = ipAddr // if !ipAddr.Equal(test.sortedSockAddrs[i]) { // t.Errorf("[%d] Sort equality failed: expected %s, received %s", i, test.sortedSockAddrs[i], ipAddr) // } // } // } // } func TestSockAddr_IPAddrs_IPAddrsByCmp(t *testing.T) { testInputs := sockAddrStringInputs{ { inputAddrs: []string{ "10.0.0.0/8", "172.16.1.3/12", "128.95.120.2:53", "128.95.120.2:53", "128.95.120.2/32", "192.168.0.0/16", "128.95.120.1/32", "192.168.1.10/24", "128.95.120.2:8600", "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0:1", "2607:f0d0:1002:0051:0000:0000:0000:0004", "2607:f0d0:1002:0051:0000:0000:0000:0003", "2607:f0d0:1002:0051:0000:0000:0000:0005", "[2607:f0d0:1002:0051:0000:0000:0000:0004]:8600", "240.0.0.1/4", }, sortedAddrs: []string{ "10.0.0.0/8", "172.16.1.3/12", "192.168.0.0/16", "192.168.1.10/24", "240.0.0.1/4", "128.95.120.1/32", "128.95.120.2/32", "128.95.120.2:53", "128.95.120.2:53", "128.95.120.2:8600", "0:0:0:0:0:0:0:0", "0:0:0:0:0:0:0:1", "2607:f0d0:1002:0051:0000:0000:0000:0003", "2607:f0d0:1002:0051:0000:0000:0000:0004", "[2607:f0d0:1002:0051:0000:0000:0000:0004]:8600", "2607:f0d0:1002:0051:0000:0000:0000:0005", }, }, } for idx, test := range testInputs { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { shuffleStrings(test.inputAddrs) inputAddrs := convertToSockAddrs(t, test.inputAddrs) sortedAddrs := convertToSockAddrs(t, test.sortedAddrs) sockaddr.OrderedAddrBy(sockaddr.AscType, sockaddr.AscPrivate, sockaddr.AscAddress, sockaddr.AscPort).Sort(inputAddrs) for i, sockAddr := range inputAddrs { if !sockAddr.Equal(sortedAddrs[i]) { t.Errorf("[%d] Sort equality failed: expected %s, received %s", i, sortedAddrs[i], sockAddr) } } }) } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/ipv4addr.go000066400000000000000000000364121316221320600253670ustar00rootroot00000000000000package sockaddr import ( "encoding/binary" "fmt" "net" "regexp" "strconv" "strings" ) type ( // IPv4Address is a named type representing an IPv4 address. IPv4Address uint32 // IPv4Network is a named type representing an IPv4 network. IPv4Network uint32 // IPv4Mask is a named type representing an IPv4 network mask. IPv4Mask uint32 ) // IPv4HostMask is a constant represents a /32 IPv4 Address // (i.e. 255.255.255.255). const IPv4HostMask = IPv4Mask(0xffffffff) // ipv4AddrAttrMap is a map of the IPv4Addr type-specific attributes. var ipv4AddrAttrMap map[AttrName]func(IPv4Addr) string var ipv4AddrAttrs []AttrName var trailingHexNetmaskRE *regexp.Regexp // IPv4Addr implements a convenience wrapper around the union of Go's // built-in net.IP and net.IPNet types. In UNIX-speak, IPv4Addr implements // `sockaddr` when the the address family is set to AF_INET // (i.e. `sockaddr_in`). type IPv4Addr struct { IPAddr Address IPv4Address Mask IPv4Mask Port IPPort } func init() { ipv4AddrInit() trailingHexNetmaskRE = regexp.MustCompile(`/([0f]{8})$`) } // NewIPv4Addr creates an IPv4Addr from a string. String can be in the form // of either an IPv4:port (e.g. `1.2.3.4:80`, in which case the mask is // assumed to be a `/32`), an IPv4 address (e.g. `1.2.3.4`, also with a `/32` // mask), or an IPv4 CIDR (e.g. `1.2.3.4/24`, which has its IP port // initialized to zero). ipv4Str can not be a hostname. // // NOTE: Many net.*() routines will initialize and return an IPv6 address. // To create uint32 values from net.IP, always test to make sure the address // returned can be converted to a 4 byte array using To4(). func NewIPv4Addr(ipv4Str string) (IPv4Addr, error) { // Strip off any bogus hex-encoded netmasks that will be mis-parsed by Go. In // particular, clients with the Barracuda VPN client will see something like: // `192.168.3.51/00ffffff` as their IP address. trailingHexNetmaskRe := trailingHexNetmaskRE.Copy() if match := trailingHexNetmaskRe.FindStringIndex(ipv4Str); match != nil { ipv4Str = ipv4Str[:match[0]] } // Parse as an IPv4 CIDR ipAddr, network, err := net.ParseCIDR(ipv4Str) if err == nil { ipv4 := ipAddr.To4() if ipv4 == nil { return IPv4Addr{}, fmt.Errorf("Unable to convert %s to an IPv4 address", ipv4Str) } // If we see an IPv6 netmask, convert it to an IPv4 mask. netmaskSepPos := strings.LastIndexByte(ipv4Str, '/') if netmaskSepPos != -1 && netmaskSepPos+1 < len(ipv4Str) { netMask, err := strconv.ParseUint(ipv4Str[netmaskSepPos+1:], 10, 8) if err != nil { return IPv4Addr{}, fmt.Errorf("Unable to convert %s to an IPv4 address: unable to parse CIDR netmask: %v", ipv4Str, err) } else if netMask > 128 { return IPv4Addr{}, fmt.Errorf("Unable to convert %s to an IPv4 address: invalid CIDR netmask", ipv4Str) } if netMask >= 96 { // Convert the IPv6 netmask to an IPv4 netmask network.Mask = net.CIDRMask(int(netMask-96), IPv4len*8) } } ipv4Addr := IPv4Addr{ Address: IPv4Address(binary.BigEndian.Uint32(ipv4)), Mask: IPv4Mask(binary.BigEndian.Uint32(network.Mask)), } return ipv4Addr, nil } // Attempt to parse ipv4Str as a /32 host with a port number. tcpAddr, err := net.ResolveTCPAddr("tcp4", ipv4Str) if err == nil { ipv4 := tcpAddr.IP.To4() if ipv4 == nil { return IPv4Addr{}, fmt.Errorf("Unable to resolve %+q as an IPv4 address", ipv4Str) } ipv4Uint32 := binary.BigEndian.Uint32(ipv4) ipv4Addr := IPv4Addr{ Address: IPv4Address(ipv4Uint32), Mask: IPv4HostMask, Port: IPPort(tcpAddr.Port), } return ipv4Addr, nil } // Parse as a naked IPv4 address ip := net.ParseIP(ipv4Str) if ip != nil { ipv4 := ip.To4() if ipv4 == nil { return IPv4Addr{}, fmt.Errorf("Unable to string convert %+q to an IPv4 address", ipv4Str) } ipv4Uint32 := binary.BigEndian.Uint32(ipv4) ipv4Addr := IPv4Addr{ Address: IPv4Address(ipv4Uint32), Mask: IPv4HostMask, } return ipv4Addr, nil } return IPv4Addr{}, fmt.Errorf("Unable to parse %+q to an IPv4 address: %v", ipv4Str, err) } // AddressBinString returns a string with the IPv4Addr's Address represented // as a sequence of '0' and '1' characters. This method is useful for // debugging or by operators who want to inspect an address. func (ipv4 IPv4Addr) AddressBinString() string { return fmt.Sprintf("%032s", strconv.FormatUint(uint64(ipv4.Address), 2)) } // AddressHexString returns a string with the IPv4Addr address represented as // a sequence of hex characters. This method is useful for debugging or by // operators who want to inspect an address. func (ipv4 IPv4Addr) AddressHexString() string { return fmt.Sprintf("%08s", strconv.FormatUint(uint64(ipv4.Address), 16)) } // Broadcast is an IPv4Addr-only method that returns the broadcast address of // the network. // // NOTE: IPv6 only supports multicast, so this method only exists for // IPv4Addr. func (ipv4 IPv4Addr) Broadcast() IPAddr { // Nothing should listen on a broadcast address. return IPv4Addr{ Address: IPv4Address(ipv4.BroadcastAddress()), Mask: IPv4HostMask, } } // BroadcastAddress returns a IPv4Network of the IPv4Addr's broadcast // address. func (ipv4 IPv4Addr) BroadcastAddress() IPv4Network { return IPv4Network(uint32(ipv4.Address)&uint32(ipv4.Mask) | ^uint32(ipv4.Mask)) } // CmpAddress follows the Cmp() standard protocol and returns: // // - -1 If the receiver should sort first because its address is lower than arg // - 0 if the SockAddr arg is equal to the receiving IPv4Addr or the argument is // of a different type. // - 1 If the argument should sort first. func (ipv4 IPv4Addr) CmpAddress(sa SockAddr) int { ipv4b, ok := sa.(IPv4Addr) if !ok { return sortDeferDecision } switch { case ipv4.Address == ipv4b.Address: return sortDeferDecision case ipv4.Address < ipv4b.Address: return sortReceiverBeforeArg default: return sortArgBeforeReceiver } } // CmpPort follows the Cmp() standard protocol and returns: // // - -1 If the receiver should sort first because its port is lower than arg // - 0 if the SockAddr arg's port number is equal to the receiving IPv4Addr, // regardless of type. // - 1 If the argument should sort first. func (ipv4 IPv4Addr) CmpPort(sa SockAddr) int { var saPort IPPort switch v := sa.(type) { case IPv4Addr: saPort = v.Port case IPv6Addr: saPort = v.Port default: return sortDeferDecision } switch { case ipv4.Port == saPort: return sortDeferDecision case ipv4.Port < saPort: return sortReceiverBeforeArg default: return sortArgBeforeReceiver } } // CmpRFC follows the Cmp() standard protocol and returns: // // - -1 If the receiver should sort first because it belongs to the RFC and its // arg does not // - 0 if the receiver and arg both belong to the same RFC or neither do. // - 1 If the arg belongs to the RFC but receiver does not. func (ipv4 IPv4Addr) CmpRFC(rfcNum uint, sa SockAddr) int { recvInRFC := IsRFC(rfcNum, ipv4) ipv4b, ok := sa.(IPv4Addr) if !ok { // If the receiver is part of the desired RFC and the SockAddr // argument is not, return -1 so that the receiver sorts before // the non-IPv4 SockAddr. Conversely, if the receiver is not // part of the RFC, punt on sorting and leave it for the next // sorter. if recvInRFC { return sortReceiverBeforeArg } else { return sortDeferDecision } } argInRFC := IsRFC(rfcNum, ipv4b) switch { case (recvInRFC && argInRFC), (!recvInRFC && !argInRFC): // If a and b both belong to the RFC, or neither belong to // rfcNum, defer sorting to the next sorter. return sortDeferDecision case recvInRFC && !argInRFC: return sortReceiverBeforeArg default: return sortArgBeforeReceiver } } // Contains returns true if the SockAddr is contained within the receiver. func (ipv4 IPv4Addr) Contains(sa SockAddr) bool { ipv4b, ok := sa.(IPv4Addr) if !ok { return false } return ipv4.ContainsNetwork(ipv4b) } // ContainsAddress returns true if the IPv4Address is contained within the // receiver. func (ipv4 IPv4Addr) ContainsAddress(x IPv4Address) bool { return IPv4Address(ipv4.NetworkAddress()) <= x && IPv4Address(ipv4.BroadcastAddress()) >= x } // ContainsNetwork returns true if the network from IPv4Addr is contained // within the receiver. func (ipv4 IPv4Addr) ContainsNetwork(x IPv4Addr) bool { return ipv4.NetworkAddress() <= x.NetworkAddress() && ipv4.BroadcastAddress() >= x.BroadcastAddress() } // DialPacketArgs returns the arguments required to be passed to // net.DialUDP(). If the Mask of ipv4 is not a /32 or the Port is 0, // DialPacketArgs() will fail. See Host() to create an IPv4Addr with its // mask set to /32. func (ipv4 IPv4Addr) DialPacketArgs() (network, dialArgs string) { if ipv4.Mask != IPv4HostMask || ipv4.Port == 0 { return "udp4", "" } return "udp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port) } // DialStreamArgs returns the arguments required to be passed to // net.DialTCP(). If the Mask of ipv4 is not a /32 or the Port is 0, // DialStreamArgs() will fail. See Host() to create an IPv4Addr with its // mask set to /32. func (ipv4 IPv4Addr) DialStreamArgs() (network, dialArgs string) { if ipv4.Mask != IPv4HostMask || ipv4.Port == 0 { return "tcp4", "" } return "tcp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port) } // Equal returns true if a SockAddr is equal to the receiving IPv4Addr. func (ipv4 IPv4Addr) Equal(sa SockAddr) bool { ipv4b, ok := sa.(IPv4Addr) if !ok { return false } if ipv4.Port != ipv4b.Port { return false } if ipv4.Address != ipv4b.Address { return false } if ipv4.NetIPNet().String() != ipv4b.NetIPNet().String() { return false } return true } // FirstUsable returns an IPv4Addr set to the first address following the // network prefix. The first usable address in a network is normally the // gateway and should not be used except by devices forwarding packets // between two administratively distinct networks (i.e. a router). This // function does not discriminate against first usable vs "first address that // should be used." For example, FirstUsable() on "192.168.1.10/24" would // return the address "192.168.1.1/24". func (ipv4 IPv4Addr) FirstUsable() IPAddr { addr := ipv4.NetworkAddress() // If /32, return the address itself. If /31 assume a point-to-point // link and return the lower address. if ipv4.Maskbits() < 31 { addr++ } return IPv4Addr{ Address: IPv4Address(addr), Mask: IPv4HostMask, } } // Host returns a copy of ipv4 with its mask set to /32 so that it can be // used by DialPacketArgs(), DialStreamArgs(), ListenPacketArgs(), or // ListenStreamArgs(). func (ipv4 IPv4Addr) Host() IPAddr { // Nothing should listen on a broadcast address. return IPv4Addr{ Address: ipv4.Address, Mask: IPv4HostMask, Port: ipv4.Port, } } // IPPort returns the Port number attached to the IPv4Addr func (ipv4 IPv4Addr) IPPort() IPPort { return ipv4.Port } // LastUsable returns the last address before the broadcast address in a // given network. func (ipv4 IPv4Addr) LastUsable() IPAddr { addr := ipv4.BroadcastAddress() // If /32, return the address itself. If /31 assume a point-to-point // link and return the upper address. if ipv4.Maskbits() < 31 { addr-- } return IPv4Addr{ Address: IPv4Address(addr), Mask: IPv4HostMask, } } // ListenPacketArgs returns the arguments required to be passed to // net.ListenUDP(). If the Mask of ipv4 is not a /32, ListenPacketArgs() // will fail. See Host() to create an IPv4Addr with its mask set to /32. func (ipv4 IPv4Addr) ListenPacketArgs() (network, listenArgs string) { if ipv4.Mask != IPv4HostMask { return "udp4", "" } return "udp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port) } // ListenStreamArgs returns the arguments required to be passed to // net.ListenTCP(). If the Mask of ipv4 is not a /32, ListenStreamArgs() // will fail. See Host() to create an IPv4Addr with its mask set to /32. func (ipv4 IPv4Addr) ListenStreamArgs() (network, listenArgs string) { if ipv4.Mask != IPv4HostMask { return "tcp4", "" } return "tcp4", fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port) } // Maskbits returns the number of network mask bits in a given IPv4Addr. For // example, the Maskbits() of "192.168.1.1/24" would return 24. func (ipv4 IPv4Addr) Maskbits() int { mask := make(net.IPMask, IPv4len) binary.BigEndian.PutUint32(mask, uint32(ipv4.Mask)) maskOnes, _ := mask.Size() return maskOnes } // MustIPv4Addr is a helper method that must return an IPv4Addr or panic on // invalid input. func MustIPv4Addr(addr string) IPv4Addr { ipv4, err := NewIPv4Addr(addr) if err != nil { panic(fmt.Sprintf("Unable to create an IPv4Addr from %+q: %v", addr, err)) } return ipv4 } // NetIP returns the address as a net.IP (address is always presized to // IPv4). func (ipv4 IPv4Addr) NetIP() *net.IP { x := make(net.IP, IPv4len) binary.BigEndian.PutUint32(x, uint32(ipv4.Address)) return &x } // NetIPMask create a new net.IPMask from the IPv4Addr. func (ipv4 IPv4Addr) NetIPMask() *net.IPMask { ipv4Mask := net.IPMask{} ipv4Mask = make(net.IPMask, IPv4len) binary.BigEndian.PutUint32(ipv4Mask, uint32(ipv4.Mask)) return &ipv4Mask } // NetIPNet create a new net.IPNet from the IPv4Addr. func (ipv4 IPv4Addr) NetIPNet() *net.IPNet { ipv4net := &net.IPNet{} ipv4net.IP = make(net.IP, IPv4len) binary.BigEndian.PutUint32(ipv4net.IP, uint32(ipv4.NetworkAddress())) ipv4net.Mask = *ipv4.NetIPMask() return ipv4net } // Network returns the network prefix or network address for a given network. func (ipv4 IPv4Addr) Network() IPAddr { return IPv4Addr{ Address: IPv4Address(ipv4.NetworkAddress()), Mask: ipv4.Mask, } } // NetworkAddress returns an IPv4Network of the IPv4Addr's network address. func (ipv4 IPv4Addr) NetworkAddress() IPv4Network { return IPv4Network(uint32(ipv4.Address) & uint32(ipv4.Mask)) } // Octets returns a slice of the four octets in an IPv4Addr's Address. The // order of the bytes is big endian. func (ipv4 IPv4Addr) Octets() []int { return []int{ int(ipv4.Address >> 24), int((ipv4.Address >> 16) & 0xff), int((ipv4.Address >> 8) & 0xff), int(ipv4.Address & 0xff), } } // String returns a string representation of the IPv4Addr func (ipv4 IPv4Addr) String() string { if ipv4.Port != 0 { return fmt.Sprintf("%s:%d", ipv4.NetIP().String(), ipv4.Port) } if ipv4.Maskbits() == 32 { return ipv4.NetIP().String() } return fmt.Sprintf("%s/%d", ipv4.NetIP().String(), ipv4.Maskbits()) } // Type is used as a type switch and returns TypeIPv4 func (IPv4Addr) Type() SockAddrType { return TypeIPv4 } // IPv4AddrAttr returns a string representation of an attribute for the given // IPv4Addr. func IPv4AddrAttr(ipv4 IPv4Addr, selector AttrName) string { fn, found := ipv4AddrAttrMap[selector] if !found { return "" } return fn(ipv4) } // IPv4Attrs returns a list of attributes supported by the IPv4Addr type func IPv4Attrs() []AttrName { return ipv4AddrAttrs } // ipv4AddrInit is called once at init() func ipv4AddrInit() { // Sorted for human readability ipv4AddrAttrs = []AttrName{ "size", // Same position as in IPv6 for output consistency "broadcast", "uint32", } ipv4AddrAttrMap = map[AttrName]func(ipv4 IPv4Addr) string{ "broadcast": func(ipv4 IPv4Addr) string { return ipv4.Broadcast().String() }, "size": func(ipv4 IPv4Addr) string { return fmt.Sprintf("%d", 1< 2 && ipv6Str[0] == '[' && ipv6Str[len(ipv6Str)-1] == ']' { ipv6Str = ipv6Str[1 : len(ipv6Str)-1] } ip := net.ParseIP(ipv6Str) if ip != nil { ipv6 := ip.To16() if ipv6 == nil { return IPv6Addr{}, fmt.Errorf("Unable to string convert %+q to a 16byte IPv6 address", ipv6Str) } ipv6BigIntAddr := new(big.Int) ipv6BigIntAddr.SetBytes(ipv6) ipv6BigIntMask := new(big.Int) ipv6BigIntMask.Set(ipv6HostMask) return IPv6Addr{ Address: IPv6Address(ipv6BigIntAddr), Mask: IPv6Mask(ipv6BigIntMask), }, nil } // Parse as an IPv6 CIDR ipAddr, network, err := net.ParseCIDR(ipv6Str) if err == nil { ipv6 := ipAddr.To16() if ipv6 == nil { return IPv6Addr{}, fmt.Errorf("Unable to convert %+q to a 16byte IPv6 address", ipv6Str) } ipv6BigIntAddr := new(big.Int) ipv6BigIntAddr.SetBytes(ipv6) ipv6BigIntMask := new(big.Int) ipv6BigIntMask.SetBytes(network.Mask) ipv6Addr := IPv6Addr{ Address: IPv6Address(ipv6BigIntAddr), Mask: IPv6Mask(ipv6BigIntMask), } return ipv6Addr, nil } return IPv6Addr{}, fmt.Errorf("Unable to parse %+q to an IPv6 address: %v", ipv6Str, err) } // AddressBinString returns a string with the IPv6Addr's Address represented // as a sequence of '0' and '1' characters. This method is useful for // debugging or by operators who want to inspect an address. func (ipv6 IPv6Addr) AddressBinString() string { bi := big.Int(*ipv6.Address) return fmt.Sprintf("%0128s", bi.Text(2)) } // AddressHexString returns a string with the IPv6Addr address represented as // a sequence of hex characters. This method is useful for debugging or by // operators who want to inspect an address. func (ipv6 IPv6Addr) AddressHexString() string { bi := big.Int(*ipv6.Address) return fmt.Sprintf("%032s", bi.Text(16)) } // CmpAddress follows the Cmp() standard protocol and returns: // // - -1 If the receiver should sort first because its address is lower than arg // - 0 if the SockAddr arg equal to the receiving IPv6Addr or the argument is of a // different type. // - 1 If the argument should sort first. func (ipv6 IPv6Addr) CmpAddress(sa SockAddr) int { ipv6b, ok := sa.(IPv6Addr) if !ok { return sortDeferDecision } ipv6aBigInt := new(big.Int) ipv6aBigInt.Set(ipv6.Address) ipv6bBigInt := new(big.Int) ipv6bBigInt.Set(ipv6b.Address) return ipv6aBigInt.Cmp(ipv6bBigInt) } // CmpPort follows the Cmp() standard protocol and returns: // // - -1 If the receiver should sort first because its port is lower than arg // - 0 if the SockAddr arg's port number is equal to the receiving IPv6Addr, // regardless of type. // - 1 If the argument should sort first. func (ipv6 IPv6Addr) CmpPort(sa SockAddr) int { var saPort IPPort switch v := sa.(type) { case IPv4Addr: saPort = v.Port case IPv6Addr: saPort = v.Port default: return sortDeferDecision } switch { case ipv6.Port == saPort: return sortDeferDecision case ipv6.Port < saPort: return sortReceiverBeforeArg default: return sortArgBeforeReceiver } } // CmpRFC follows the Cmp() standard protocol and returns: // // - -1 If the receiver should sort first because it belongs to the RFC and its // arg does not // - 0 if the receiver and arg both belong to the same RFC or neither do. // - 1 If the arg belongs to the RFC but receiver does not. func (ipv6 IPv6Addr) CmpRFC(rfcNum uint, sa SockAddr) int { recvInRFC := IsRFC(rfcNum, ipv6) ipv6b, ok := sa.(IPv6Addr) if !ok { // If the receiver is part of the desired RFC and the SockAddr // argument is not, sort receiver before the non-IPv6 SockAddr. // Conversely, if the receiver is not part of the RFC, punt on // sorting and leave it for the next sorter. if recvInRFC { return sortReceiverBeforeArg } else { return sortDeferDecision } } argInRFC := IsRFC(rfcNum, ipv6b) switch { case (recvInRFC && argInRFC), (!recvInRFC && !argInRFC): // If a and b both belong to the RFC, or neither belong to // rfcNum, defer sorting to the next sorter. return sortDeferDecision case recvInRFC && !argInRFC: return sortReceiverBeforeArg default: return sortArgBeforeReceiver } } // Contains returns true if the SockAddr is contained within the receiver. func (ipv6 IPv6Addr) Contains(sa SockAddr) bool { ipv6b, ok := sa.(IPv6Addr) if !ok { return false } return ipv6.ContainsNetwork(ipv6b) } // ContainsAddress returns true if the IPv6Address is contained within the // receiver. func (ipv6 IPv6Addr) ContainsAddress(x IPv6Address) bool { xAddr := IPv6Addr{ Address: x, Mask: ipv6HostMask, } { xIPv6 := xAddr.FirstUsable().(IPv6Addr) yIPv6 := ipv6.FirstUsable().(IPv6Addr) if xIPv6.CmpAddress(yIPv6) >= 1 { return false } } { xIPv6 := xAddr.LastUsable().(IPv6Addr) yIPv6 := ipv6.LastUsable().(IPv6Addr) if xIPv6.CmpAddress(yIPv6) <= -1 { return false } } return true } // ContainsNetwork returns true if the network from IPv6Addr is contained within // the receiver. func (x IPv6Addr) ContainsNetwork(y IPv6Addr) bool { { xIPv6 := x.FirstUsable().(IPv6Addr) yIPv6 := y.FirstUsable().(IPv6Addr) if ret := xIPv6.CmpAddress(yIPv6); ret >= 1 { return false } } { xIPv6 := x.LastUsable().(IPv6Addr) yIPv6 := y.LastUsable().(IPv6Addr) if ret := xIPv6.CmpAddress(yIPv6); ret <= -1 { return false } } return true } // DialPacketArgs returns the arguments required to be passed to // net.DialUDP(). If the Mask of ipv6 is not a /128 or the Port is 0, // DialPacketArgs() will fail. See Host() to create an IPv6Addr with its // mask set to /128. func (ipv6 IPv6Addr) DialPacketArgs() (network, dialArgs string) { ipv6Mask := big.Int(*ipv6.Mask) if ipv6Mask.Cmp(ipv6HostMask) != 0 || ipv6.Port == 0 { return "udp6", "" } return "udp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port) } // DialStreamArgs returns the arguments required to be passed to // net.DialTCP(). If the Mask of ipv6 is not a /128 or the Port is 0, // DialStreamArgs() will fail. See Host() to create an IPv6Addr with its // mask set to /128. func (ipv6 IPv6Addr) DialStreamArgs() (network, dialArgs string) { ipv6Mask := big.Int(*ipv6.Mask) if ipv6Mask.Cmp(ipv6HostMask) != 0 || ipv6.Port == 0 { return "tcp6", "" } return "tcp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port) } // Equal returns true if a SockAddr is equal to the receiving IPv4Addr. func (ipv6a IPv6Addr) Equal(sa SockAddr) bool { ipv6b, ok := sa.(IPv6Addr) if !ok { return false } if ipv6a.NetIP().String() != ipv6b.NetIP().String() { return false } if ipv6a.NetIPNet().String() != ipv6b.NetIPNet().String() { return false } if ipv6a.Port != ipv6b.Port { return false } return true } // FirstUsable returns an IPv6Addr set to the first address following the // network prefix. The first usable address in a network is normally the // gateway and should not be used except by devices forwarding packets // between two administratively distinct networks (i.e. a router). This // function does not discriminate against first usable vs "first address that // should be used." For example, FirstUsable() on "2001:0db8::0003/64" would // return "2001:0db8::00011". func (ipv6 IPv6Addr) FirstUsable() IPAddr { return IPv6Addr{ Address: IPv6Address(ipv6.NetworkAddress()), Mask: ipv6HostMask, } } // Host returns a copy of ipv6 with its mask set to /128 so that it can be // used by DialPacketArgs(), DialStreamArgs(), ListenPacketArgs(), or // ListenStreamArgs(). func (ipv6 IPv6Addr) Host() IPAddr { // Nothing should listen on a broadcast address. return IPv6Addr{ Address: ipv6.Address, Mask: ipv6HostMask, Port: ipv6.Port, } } // IPPort returns the Port number attached to the IPv6Addr func (ipv6 IPv6Addr) IPPort() IPPort { return ipv6.Port } // LastUsable returns the last address in a given network. func (ipv6 IPv6Addr) LastUsable() IPAddr { addr := new(big.Int) addr.Set(ipv6.Address) mask := new(big.Int) mask.Set(ipv6.Mask) negMask := new(big.Int) negMask.Xor(ipv6HostMask, mask) lastAddr := new(big.Int) lastAddr.And(addr, mask) lastAddr.Or(lastAddr, negMask) return IPv6Addr{ Address: IPv6Address(lastAddr), Mask: ipv6HostMask, } } // ListenPacketArgs returns the arguments required to be passed to // net.ListenUDP(). If the Mask of ipv6 is not a /128, ListenPacketArgs() // will fail. See Host() to create an IPv6Addr with its mask set to /128. func (ipv6 IPv6Addr) ListenPacketArgs() (network, listenArgs string) { ipv6Mask := big.Int(*ipv6.Mask) if ipv6Mask.Cmp(ipv6HostMask) != 0 { return "udp6", "" } return "udp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port) } // ListenStreamArgs returns the arguments required to be passed to // net.ListenTCP(). If the Mask of ipv6 is not a /128, ListenStreamArgs() // will fail. See Host() to create an IPv6Addr with its mask set to /128. func (ipv6 IPv6Addr) ListenStreamArgs() (network, listenArgs string) { ipv6Mask := big.Int(*ipv6.Mask) if ipv6Mask.Cmp(ipv6HostMask) != 0 { return "tcp6", "" } return "tcp6", fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port) } // Maskbits returns the number of network mask bits in a given IPv6Addr. For // example, the Maskbits() of "2001:0db8::0003/64" would return 64. func (ipv6 IPv6Addr) Maskbits() int { maskOnes, _ := ipv6.NetIPNet().Mask.Size() return maskOnes } // MustIPv6Addr is a helper method that must return an IPv6Addr or panic on // invalid input. func MustIPv6Addr(addr string) IPv6Addr { ipv6, err := NewIPv6Addr(addr) if err != nil { panic(fmt.Sprintf("Unable to create an IPv6Addr from %+q: %v", addr, err)) } return ipv6 } // NetIP returns the address as a net.IP. func (ipv6 IPv6Addr) NetIP() *net.IP { return bigIntToNetIPv6(ipv6.Address) } // NetIPMask create a new net.IPMask from the IPv6Addr. func (ipv6 IPv6Addr) NetIPMask() *net.IPMask { ipv6Mask := make(net.IPMask, IPv6len) m := big.Int(*ipv6.Mask) copy(ipv6Mask, m.Bytes()) return &ipv6Mask } // Network returns a pointer to the net.IPNet within IPv4Addr receiver. func (ipv6 IPv6Addr) NetIPNet() *net.IPNet { ipv6net := &net.IPNet{} ipv6net.IP = make(net.IP, IPv6len) copy(ipv6net.IP, *ipv6.NetIP()) ipv6net.Mask = *ipv6.NetIPMask() return ipv6net } // Network returns the network prefix or network address for a given network. func (ipv6 IPv6Addr) Network() IPAddr { return IPv6Addr{ Address: IPv6Address(ipv6.NetworkAddress()), Mask: ipv6.Mask, } } // NetworkAddress returns an IPv6Network of the IPv6Addr's network address. func (ipv6 IPv6Addr) NetworkAddress() IPv6Network { addr := new(big.Int) addr.SetBytes((*ipv6.Address).Bytes()) mask := new(big.Int) mask.SetBytes(*ipv6.NetIPMask()) netAddr := new(big.Int) netAddr.And(addr, mask) return IPv6Network(netAddr) } // Octets returns a slice of the 16 octets in an IPv6Addr's Address. The // order of the bytes is big endian. func (ipv6 IPv6Addr) Octets() []int { x := make([]int, IPv6len) for i, b := range *bigIntToNetIPv6(ipv6.Address) { x[i] = int(b) } return x } // String returns a string representation of the IPv6Addr func (ipv6 IPv6Addr) String() string { if ipv6.Port != 0 { return fmt.Sprintf("[%s]:%d", ipv6.NetIP().String(), ipv6.Port) } if ipv6.Maskbits() == 128 { return ipv6.NetIP().String() } return fmt.Sprintf("%s/%d", ipv6.NetIP().String(), ipv6.Maskbits()) } // Type is used as a type switch and returns TypeIPv6 func (IPv6Addr) Type() SockAddrType { return TypeIPv6 } // IPv6Attrs returns a list of attributes supported by the IPv6Addr type func IPv6Attrs() []AttrName { return ipv6AddrAttrs } // IPv6AddrAttr returns a string representation of an attribute for the given // IPv6Addr. func IPv6AddrAttr(ipv6 IPv6Addr, selector AttrName) string { fn, found := ipv6AddrAttrMap[selector] if !found { return "" } return fn(ipv6) } // ipv6AddrInit is called once at init() func ipv6AddrInit() { // Sorted for human readability ipv6AddrAttrs = []AttrName{ "size", // Same position as in IPv6 for output consistency "uint128", } ipv6AddrAttrMap = map[AttrName]func(ipv6 IPv6Addr) string{ "size": func(ipv6 IPv6Addr) string { netSize := big.NewInt(1) netSize = netSize.Lsh(netSize, uint(IPv6len*8-ipv6.Maskbits())) return netSize.Text(10) }, "uint128": func(ipv6 IPv6Addr) string { b := big.Int(*ipv6.Address) return b.Text(10) }, } } // bigIntToNetIPv6 is a helper function that correctly returns a net.IP with the // correctly padded values. func bigIntToNetIPv6(bi *big.Int) *net.IP { x := make(net.IP, IPv6len) ipv6Bytes := bi.Bytes() // It's possibe for ipv6Bytes to be less than IPv6len bytes in size. If // they are different sizes we to pad the size of response. if len(ipv6Bytes) < IPv6len { buf := new(bytes.Buffer) buf.Grow(IPv6len) for i := len(ipv6Bytes); i < IPv6len; i++ { if err := binary.Write(buf, binary.BigEndian, byte(0)); err != nil { panic(fmt.Sprintf("Unable to pad byte %d of input %v: %v", i, bi, err)) } } for _, b := range ipv6Bytes { if err := binary.Write(buf, binary.BigEndian, b); err != nil { panic(fmt.Sprintf("Unable to preserve endianness of input %v: %v", bi, err)) } } ipv6Bytes = buf.Bytes() } i := copy(x, ipv6Bytes) if i != IPv6len { panic("IPv6 wrong size") } return &x } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/ipv6addr_test.go000066400000000000000000000667641316221320600264450ustar00rootroot00000000000000package sockaddr_test import ( "fmt" "math/big" "strings" "testing" "github.com/hashicorp/go-sockaddr" ) // ipv6HostMask is an unexported big.Int representing a /128 IPv6 address var ipv6HostMask sockaddr.IPv6Mask func init() { biMask := big.NewInt(0) biMask = biMask.SetBytes([]byte{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }, ) ipv6HostMask = sockaddr.IPv6Mask(biMask) } func newIPv6BigInt(t *testing.T, ipv6Str string) *big.Int { addr := big.NewInt(0) addrStr := strings.Join(strings.Split(ipv6Str, ":"), "") _, ok := addr.SetString(addrStr, 16) if !ok { t.Fatal("Unable to create an IPv6Addr from string %+q", ipv6Str) } return addr } func newIPv6Address(t *testing.T, ipv6Str string) sockaddr.IPv6Address { return sockaddr.IPv6Address(newIPv6BigInt(t, ipv6Str)) } func newIPv6Mask(t *testing.T, ipv6Str string) sockaddr.IPv6Mask { return sockaddr.IPv6Mask(newIPv6BigInt(t, ipv6Str)) } func newIPv6Network(t *testing.T, ipv6Str string) sockaddr.IPv6Network { return sockaddr.IPv6Network(newIPv6BigInt(t, ipv6Str)) } func TestSockAddr_IPv6Addr(t *testing.T) { tests := []struct { z00_input string z01_addrHexStr string z02_addrBinStr string z03_addrStr string z04_NetIPStringOut string z05_addrInt sockaddr.IPv6Address z06_netInt sockaddr.IPv6Network z07_ipMaskStr string z08_maskbits int z09_NetIPNetStringOut string z10_maskInt sockaddr.IPv6Mask z11_networkStr string z12_octets []int z13_firstUsable string z14_lastUsable string z16_portInt sockaddr.IPPort z17_DialPacketArgs []string z18_DialStreamArgs []string z19_ListenPacketArgs []string z20_ListenStreamArgs []string z99_pass bool }{ { // 0 -- IPv4 fail z00_input: "1.2.3.4", z99_pass: false, }, { // 1 - IPv4 with port z00_input: "5.6.7.8:80", z99_pass: false, }, { // 2 - Hostname z00_input: "www.hashicorp.com", z99_pass: false, }, { // 3 - IPv6 with port, but no square brackets z00_input: "2607:f0d0:1002:0051:0000:0000:0000:0004:8600", z99_pass: false, }, { // 4 - IPv6 with port z00_input: "[2607:f0d0:1002:0051:0000:0000:0000:0004]:8600", z01_addrHexStr: "2607f0d0100200510000000000000004", z02_addrBinStr: "00100110000001111111000011010000000100000000001000000000010100010000000000000000000000000000000000000000000000000000000000000100", z03_addrStr: "[2607:f0d0:1002:51::4]:8600", z04_NetIPStringOut: "2607:f0d0:1002:51::4", z05_addrInt: newIPv6Address(t, "2607:f0d0:1002:0051:0000:0000:0000:0004"), z06_netInt: newIPv6Network(t, "2607:f0d0:1002:0051:0000:0000:0000:0004"), z07_ipMaskStr: "ffffffffffffffffffffffffffffffff", z08_maskbits: 128, z09_NetIPNetStringOut: "2607:f0d0:1002:51::4/128", z10_maskInt: newIPv6Mask(t, "ffffffffffffffffffffffffffffffff"), z11_networkStr: "2607:f0d0:1002:51::4", z12_octets: []int{0x26, 0x7, 0xf0, 0xd0, 0x10, 0x2, 0x0, 0x51, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4}, z13_firstUsable: "2607:f0d0:1002:51::4", z14_lastUsable: "2607:f0d0:1002:51::4", z16_portInt: 8600, z17_DialPacketArgs: []string{"udp6", "[2607:f0d0:1002:51::4]:8600"}, z18_DialStreamArgs: []string{"tcp6", "[2607:f0d0:1002:51::4]:8600"}, z19_ListenPacketArgs: []string{"udp6", "[2607:f0d0:1002:51::4]:8600"}, z20_ListenStreamArgs: []string{"tcp6", "[2607:f0d0:1002:51::4]:8600"}, z99_pass: true, }, { // 5 - IPv6 z00_input: "2607:f0d0:1002:0051:0000:0000:0000:0004", z01_addrHexStr: "2607f0d0100200510000000000000004", z02_addrBinStr: "00100110000001111111000011010000000100000000001000000000010100010000000000000000000000000000000000000000000000000000000000000100", z03_addrStr: "2607:f0d0:1002:51::4", z04_NetIPStringOut: "2607:f0d0:1002:51::4", z05_addrInt: newIPv6Address(t, "2607:f0d0:1002:0051:0000:0000:0000:0004"), z06_netInt: newIPv6Network(t, "2607:f0d0:1002:0051:0000:0000:0000:0004"), z07_ipMaskStr: "ffffffffffffffffffffffffffffffff", z08_maskbits: 128, z09_NetIPNetStringOut: "2607:f0d0:1002:51::4/128", z10_maskInt: newIPv6Mask(t, "ffffffffffffffffffffffffffffffff"), z11_networkStr: "2607:f0d0:1002:51::4", z12_octets: []int{0x26, 0x7, 0xf0, 0xd0, 0x10, 0x2, 0x0, 0x51, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4}, z13_firstUsable: "2607:f0d0:1002:51::4", z14_lastUsable: "2607:f0d0:1002:51::4", z17_DialPacketArgs: []string{"udp6", ""}, z18_DialStreamArgs: []string{"tcp6", ""}, z19_ListenPacketArgs: []string{"udp6", "[2607:f0d0:1002:51::4]:0"}, z20_ListenStreamArgs: []string{"tcp6", "[2607:f0d0:1002:51::4]:0"}, z99_pass: true, }, { // 6 IPv6 with square brackets, optional z00_input: "[2607:f0d0:1002:0051:0000:0000:0000:0004]", z01_addrHexStr: "2607f0d0100200510000000000000004", z02_addrBinStr: "00100110000001111111000011010000000100000000001000000000010100010000000000000000000000000000000000000000000000000000000000000100", z03_addrStr: "2607:f0d0:1002:51::4", z04_NetIPStringOut: "2607:f0d0:1002:51::4", z05_addrInt: newIPv6Address(t, "2607:f0d0:1002:0051:0000:0000:0000:0004"), z06_netInt: newIPv6Network(t, "2607:f0d0:1002:0051:0000:0000:0000:0004"), z07_ipMaskStr: "ffffffffffffffffffffffffffffffff", z08_maskbits: 128, z09_NetIPNetStringOut: "2607:f0d0:1002:51::4/128", z10_maskInt: newIPv6Mask(t, "ffffffffffffffffffffffffffffffff"), z11_networkStr: "2607:f0d0:1002:51::4", z12_octets: []int{0x26, 0x7, 0xf0, 0xd0, 0x10, 0x2, 0x0, 0x51, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4}, z13_firstUsable: "2607:f0d0:1002:51::4", z14_lastUsable: "2607:f0d0:1002:51::4", z17_DialPacketArgs: []string{"udp6", ""}, z18_DialStreamArgs: []string{"tcp6", ""}, z19_ListenPacketArgs: []string{"udp6", "[2607:f0d0:1002:51::4]:0"}, z20_ListenStreamArgs: []string{"tcp6", "[2607:f0d0:1002:51::4]:0"}, z99_pass: true, }, { // 7 - unspecified address z00_input: "0:0:0:0:0:0:0:0", z01_addrHexStr: "00000000000000000000000000000000", z02_addrBinStr: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", z03_addrStr: "::", z04_NetIPStringOut: "::", z05_addrInt: newIPv6Address(t, "0"), z06_netInt: newIPv6Network(t, "0"), z07_ipMaskStr: "ffffffffffffffffffffffffffffffff", z08_maskbits: 128, z09_NetIPNetStringOut: "::/128", z10_maskInt: newIPv6Mask(t, "ffffffffffffffffffffffffffffffff"), z11_networkStr: "::", z12_octets: []int{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, z13_firstUsable: "::", z14_lastUsable: "::", z17_DialPacketArgs: []string{"udp6", ""}, z18_DialStreamArgs: []string{"tcp6", ""}, z19_ListenPacketArgs: []string{"udp6", "[::]:0"}, z20_ListenStreamArgs: []string{"tcp6", "[::]:0"}, z99_pass: true, }, { // 8 - loopback address z00_input: "0:0:0:0:0:0:0:1", z01_addrHexStr: "00000000000000000000000000000001", z02_addrBinStr: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", z03_addrStr: "::1", z04_NetIPStringOut: "::1", z05_addrInt: newIPv6Address(t, "0000:0000:0000:0000:0000:0000:0000:0001"), z06_netInt: newIPv6Network(t, "0000:0000:0000:0000:0000:0000:0000:0001"), z07_ipMaskStr: "ffffffffffffffffffffffffffffffff", z08_maskbits: 128, z09_NetIPNetStringOut: "::1/128", z10_maskInt: newIPv6Mask(t, "ffffffffffffffffffffffffffffffff"), z11_networkStr: "::1", z12_octets: []int{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x01}, z13_firstUsable: "::1", z14_lastUsable: "::1", z17_DialPacketArgs: []string{"udp6", ""}, z18_DialStreamArgs: []string{"tcp6", ""}, z19_ListenPacketArgs: []string{"udp6", "[::1]:0"}, z20_ListenStreamArgs: []string{"tcp6", "[::1]:0"}, z99_pass: true, }, { // 9 - IPv6 with CIDR (RFC 3849) z00_input: "2001:DB8::/32", z01_addrHexStr: "20010db8000000000000000000000000", z02_addrBinStr: "00100000000000010000110110111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", z03_addrStr: "2001:db8::/32", z04_NetIPStringOut: "2001:db8::", z05_addrInt: newIPv6Address(t, "20010db8000000000000000000000000"), z06_netInt: newIPv6Network(t, "20010db8000000000000000000000000"), z07_ipMaskStr: "ffffffff000000000000000000000000", z08_maskbits: 32, z09_NetIPNetStringOut: "2001:db8::/32", z10_maskInt: newIPv6Mask(t, "ffffffff000000000000000000000000"), z11_networkStr: "2001:db8::/32", z12_octets: []int{0x20, 0x01, 0x0d, 0xb8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, z13_firstUsable: "2001:db8::", z14_lastUsable: "2001:db8:ffff:ffff:ffff:ffff:ffff:ffff", z17_DialPacketArgs: []string{"udp6", ""}, z18_DialStreamArgs: []string{"tcp6", ""}, z19_ListenPacketArgs: []string{"udp6", ""}, z20_ListenStreamArgs: []string{"tcp6", ""}, z99_pass: true, }, { // 10 - IPv6 ::1 z00_input: "::1", z01_addrHexStr: "00000000000000000000000000000001", z02_addrBinStr: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", z03_addrStr: "::1", z04_NetIPStringOut: "::1", z05_addrInt: newIPv6Address(t, "00000000000000000000000000000001"), z06_netInt: newIPv6Network(t, "00000000000000000000000000000001"), z07_ipMaskStr: "ffffffffffffffffffffffffffffffff", z08_maskbits: 128, z09_NetIPNetStringOut: "::1/128", z10_maskInt: newIPv6Mask(t, "ffffffffffffffffffffffffffffffff"), z11_networkStr: "::1", z12_octets: []int{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1}, z13_firstUsable: "::1", z14_lastUsable: "::1", z17_DialPacketArgs: []string{"udp6", ""}, z18_DialStreamArgs: []string{"tcp6", ""}, z19_ListenPacketArgs: []string{"udp6", "[::1]:0"}, z20_ListenStreamArgs: []string{"tcp6", "[::1]:0"}, z99_pass: true, }, { // 11 - IPv6 100:: z00_input: "100::", z01_addrHexStr: "01000000000000000000000000000000", z02_addrBinStr: "00000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", z03_addrStr: "100::", z04_NetIPStringOut: "100::", z05_addrInt: newIPv6Address(t, "01000000000000000000000000000000"), z06_netInt: newIPv6Network(t, "01000000000000000000000000000000"), z07_ipMaskStr: "ffffffffffffffffffffffffffffffff", z08_maskbits: 128, z09_NetIPNetStringOut: "100::/128", z10_maskInt: newIPv6Mask(t, "ffffffffffffffffffffffffffffffff"), z11_networkStr: "100::", z12_octets: []int{0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, z13_firstUsable: "100::", z14_lastUsable: "100::", z17_DialPacketArgs: []string{"udp6", ""}, z18_DialStreamArgs: []string{"tcp6", ""}, z19_ListenPacketArgs: []string{"udp6", "[100::]:0"}, z20_ListenStreamArgs: []string{"tcp6", "[100::]:0"}, z99_pass: true, }, { // 12 - IPv6 100::2 z00_input: "100::2", z01_addrHexStr: "01000000000000000000000000000002", z02_addrBinStr: "00000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", z03_addrStr: "100::2", z04_NetIPStringOut: "100::2", z05_addrInt: newIPv6Address(t, "01000000000000000000000000000002"), z06_netInt: newIPv6Network(t, "01000000000000000000000000000002"), z07_ipMaskStr: "ffffffffffffffffffffffffffffffff", z08_maskbits: 128, z09_NetIPNetStringOut: "100::2/128", z10_maskInt: newIPv6Mask(t, "ffffffffffffffffffffffffffffffff"), z11_networkStr: "100::2", z12_octets: []int{0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x02}, z13_firstUsable: "100::2", z14_lastUsable: "100::2", z17_DialPacketArgs: []string{"udp6", ""}, z18_DialStreamArgs: []string{"tcp6", ""}, z19_ListenPacketArgs: []string{"udp6", "[100::2]:0"}, z20_ListenStreamArgs: []string{"tcp6", "[100::2]:0"}, z99_pass: true, }, { // 13 - IPv6 `[100::2]:80` z00_input: "[100::2]:80", z01_addrHexStr: "01000000000000000000000000000002", z02_addrBinStr: "00000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", z03_addrStr: "[100::2]:80", z04_NetIPStringOut: "100::2", z05_addrInt: newIPv6Address(t, "01000000000000000000000000000002"), z06_netInt: newIPv6Network(t, "01000000000000000000000000000002"), z07_ipMaskStr: "ffffffffffffffffffffffffffffffff", z08_maskbits: 128, z09_NetIPNetStringOut: "100::2/128", z10_maskInt: newIPv6Mask(t, "ffffffffffffffffffffffffffffffff"), z11_networkStr: "100::2", z12_octets: []int{0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x02}, z13_firstUsable: "100::2", z14_lastUsable: "100::2", z16_portInt: 80, z17_DialPacketArgs: []string{"udp6", "[100::2]:80"}, z18_DialStreamArgs: []string{"tcp6", "[100::2]:80"}, z19_ListenPacketArgs: []string{"udp6", "[100::2]:80"}, z20_ListenStreamArgs: []string{"tcp6", "[100::2]:80"}, z99_pass: true, }, } for idx, test := range tests { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { ipv6, err := sockaddr.NewIPv6Addr(test.z00_input) if test.z99_pass && err != nil { t.Fatalf("[%d] Unable to create an IPv6Addr from %+q: %v", idx, test.z00_input, err) } else if !test.z99_pass && err == nil { t.Fatalf("[%d] Expected test to fail for %+q", idx, test.z00_input) } else if !test.z99_pass && err != nil { // Expected failure, return success return } if type_ := ipv6.Type(); type_ != sockaddr.TypeIPv6 { t.Errorf("[%d] Expected new IPv6Addr to be Type %d, received %d (int)", idx, sockaddr.TypeIPv6, type_) } h, ok := ipv6.Host().(sockaddr.IPv6Addr) if !ok { t.Errorf("[%d] Unable to type assert +%q's Host to IPv6Addr", idx, test.z00_input) } hAddressBigInt := big.Int(*h.Address) hMaskBigInt := big.Int(*h.Mask) if hAddressBigInt.Cmp(ipv6.Address) != 0 || hMaskBigInt.Cmp(ipv6HostMask) != 0 || h.Port != ipv6.Port { t.Errorf("[%d] Expected %+q's Host() to return identical IPv6Addr except mask, received %+q", idx, test.z00_input, h.String()) } if c := cap(*ipv6.NetIP()); c != sockaddr.IPv6len { t.Errorf("[%d] Expected new IPv6Addr's Address capacity to be %d bytes, received %d", idx, sockaddr.IPv6len, c) } if l := len(*ipv6.NetIP()); l != sockaddr.IPv6len { t.Errorf("[%d] Expected new IPv6Addr's Address length to be %d bytes, received %d", idx, sockaddr.IPv6len, l) } if s := ipv6.AddressHexString(); s != test.z01_addrHexStr { t.Errorf("[%d] Expected address %+q's hexadecimal representation to be %+q, received %+q", idx, test.z00_input, test.z01_addrHexStr, s) } if s := ipv6.AddressBinString(); s != test.z02_addrBinStr { t.Errorf("[%d] Expected address %+q's binary representation to be %+q, received %+q", idx, test.z00_input, test.z02_addrBinStr, s) } if s := ipv6.String(); s != test.z03_addrStr { t.Errorf("[%d] Expected %+q's String to be %+q, received %+q", idx, test.z00_input, test.z03_addrStr, s) } if s := ipv6.NetIP().String(); s != test.z04_NetIPStringOut { t.Errorf("[%d] Expected %+q's address to be %+q, received %+q", idx, test.z00_input, test.z04_NetIPStringOut, s) } if hAddressBigInt.Cmp(test.z05_addrInt) != 0 { t.Errorf("[%d] Expected %+q's Address to return %+v, received %+v", idx, test.z00_input, test.z05_addrInt, hAddressBigInt) } n, ok := ipv6.Network().(sockaddr.IPv6Addr) if !ok { t.Errorf("[%d] Unable to type assert +%q's Network to IPv6Addr", idx, test.z00_input) } nAddressBigInt := big.Int(*n.Address) if nAddressBigInt.Cmp(test.z06_netInt) != 0 { t.Errorf("[%d] Expected %+q's Network to return %+v, received %+v", idx, test.z00_input, test.z06_netInt, n.Address) } if m := ipv6.NetIPMask().String(); m != test.z07_ipMaskStr { t.Errorf("[%d] Expected %+q's mask to be %+q, received %+q", idx, test.z00_input, test.z07_ipMaskStr, m) } if m := ipv6.Maskbits(); m != test.z08_maskbits { t.Errorf("[%dr] Expected %+q's port to be %+v, received %+v", idx, test.z00_input, test.z08_maskbits, m) } if n := ipv6.NetIPNet().String(); n != test.z09_NetIPNetStringOut { t.Errorf("[%d] Expected %+q's network to be %+q, received %+q", idx, test.z00_input, test.z09_NetIPNetStringOut, n) } ipv6MaskBigInt := big.Int(*ipv6.Mask) if ipv6MaskBigInt.Cmp(test.z10_maskInt) != 0 { t.Errorf("[%d] Expected %+q's Mask to return %+v, received %+v", idx, test.z00_input, test.z10_maskInt, ipv6MaskBigInt) } nMaskBigInt := big.Int(*n.Mask) if nMaskBigInt.Cmp(test.z10_maskInt) != 0 { t.Errorf("[%d] Expected %+q's Network's Mask to return %+v, received %+v", idx, test.z00_input, test.z10_maskInt, nMaskBigInt) } // Network()'s mask must match the IPv6Addr's Mask if n := ipv6.Network().String(); n != test.z11_networkStr { t.Errorf("[%d] Expected %+q's Network() to be %+q, received %+q", idx, test.z00_input, test.z11_networkStr, n) } if o := ipv6.Octets(); len(o) != 16 || cap(o) != 16 || o[0] != test.z12_octets[0] || o[1] != test.z12_octets[1] || o[2] != test.z12_octets[2] || o[3] != test.z12_octets[3] || o[4] != test.z12_octets[4] || o[5] != test.z12_octets[5] || o[6] != test.z12_octets[6] || o[7] != test.z12_octets[7] || o[8] != test.z12_octets[8] || o[9] != test.z12_octets[9] || o[10] != test.z12_octets[10] || o[11] != test.z12_octets[11] || o[12] != test.z12_octets[12] || o[13] != test.z12_octets[13] || o[14] != test.z12_octets[14] || o[15] != test.z12_octets[15] { t.Errorf("[%d] Expected %+q's Octets to be %x, received %x", idx, test.z00_input, test.z12_octets, o) } if f := ipv6.FirstUsable().String(); f != test.z13_firstUsable { t.Errorf("[%d] Expected %+q's FirstUsable() to be %+q, received %+q", idx, test.z00_input, test.z13_firstUsable, f) } if l := ipv6.LastUsable().String(); l != test.z14_lastUsable { t.Errorf("[%d] Expected %+q's LastUsable() to be %+q, received %+q", idx, test.z00_input, test.z14_lastUsable, l) } if p := ipv6.IPPort(); sockaddr.IPPort(p) != test.z16_portInt || sockaddr.IPPort(p) != test.z16_portInt { t.Errorf("[%d] Expected %+q's port to be %+v, received %+v", idx, test.z00_input, test.z16_portInt, p) } if dialNet, dialArgs := ipv6.DialPacketArgs(); dialNet != test.z17_DialPacketArgs[0] || dialArgs != test.z17_DialPacketArgs[1] { t.Errorf("[%d] Expected %+q's DialPacketArgs() to be %+q, received %+q, %+q", idx, test.z00_input, test.z17_DialPacketArgs, dialNet, dialArgs) } if dialNet, dialArgs := ipv6.DialStreamArgs(); dialNet != test.z18_DialStreamArgs[0] || dialArgs != test.z18_DialStreamArgs[1] { t.Errorf("[%d] Expected %+q's DialStreamArgs() to be %+q, received %+q, %+q", idx, test.z00_input, test.z18_DialStreamArgs, dialNet, dialArgs) } if listenNet, listenArgs := ipv6.ListenPacketArgs(); listenNet != test.z19_ListenPacketArgs[0] || listenArgs != test.z19_ListenPacketArgs[1] { t.Errorf("[%d] Expected %+q's ListenPacketArgs() to be %+q, received %+q, %+q", idx, test.z00_input, test.z19_ListenPacketArgs, listenNet, listenArgs) } if listenNet, listenArgs := ipv6.ListenStreamArgs(); listenNet != test.z20_ListenStreamArgs[0] || listenArgs != test.z20_ListenStreamArgs[1] { t.Errorf("[%d] Expected %+q's ListenStreamArgs() to be %+q, received %+q, %+q", idx, test.z00_input, test.z20_ListenStreamArgs, listenNet, listenArgs) } }) } } func TestSockAddr_IPv6Addr_CmpAddress(t *testing.T) { tests := []struct { a string b string cmp int }{ { // 0 a: "2001:4860:0:2001::68/128", b: "2001:4860:0:2001::68", cmp: 0, }, { // 1 a: "2607:f0d0:1002:0051:0000:0000:0000:0004/128", b: "2607:f0d0:1002:0051:0000:0000:0000:0004", cmp: 0, }, { // 2 a: "2607:f0d0:1002:0051:0000:0000:0000:0004/128", b: "2607:f0d0:1002:0051:0000:0000:0000:0004/64", cmp: 0, }, { // 3 a: "2607:f0d0:1002:0051:0000:0000:0000:0004", b: "2607:f0d0:1002:0051:0000:0000:0000:0005", cmp: -1, }, } for idx, test := range tests { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { ipv6a, err := sockaddr.NewIPv6Addr(test.a) if err != nil { t.Fatalf("[%d] Unable to create an IPv6Addr from %+q: %v", idx, test.a, err) } ipv6b, err := sockaddr.NewIPv6Addr(test.b) if err != nil { t.Fatalf("[%d] Unable to create an IPv6Addr from %+q: %v", idx, test.b, err) } if x := ipv6a.CmpAddress(ipv6b); x != test.cmp { t.Errorf("[%d] IPv6Addr.CmpAddress() failed with %+q with %+q (expected %d, received %d)", idx, ipv6a, ipv6b, test.cmp, x) } if x := ipv6b.CmpAddress(ipv6a); x*-1 != test.cmp { t.Errorf("[%d] IPv6Addr.CmpAddress() failed with %+q with %+q (expected %d, received %d)", idx, ipv6a, ipv6b, test.cmp, x) } }) } } func TestSockAddr_IPv6Addr_ContainsAddress(t *testing.T) { tests := []struct { name string input sockaddr.IPv6Addr cases []sockaddr.IPv6Addr fail bool }{ { name: "basic", input: sockaddr.MustIPv6Addr("::1/128"), cases: []sockaddr.IPv6Addr{ sockaddr.MustIPv6Addr("::1"), sockaddr.MustIPv6Addr("[::1/128]"), }, }, { name: "fail", input: sockaddr.MustIPv6Addr("::1/128"), cases: []sockaddr.IPv6Addr{ sockaddr.MustIPv6Addr("100::"), }, fail: true, }, { name: "fail2", input: sockaddr.MustIPv6Addr("100::/128"), cases: []sockaddr.IPv6Addr{ sockaddr.MustIPv6Addr("::1"), }, fail: true, }, } for idx, test := range tests { if test.name == "" { t.Fatalf("test %d needs a name", idx) } t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { ipv6 := test.input for _, tc := range test.cases { if ipv6.ContainsAddress(tc.Address) == test.fail { t.Errorf("%s: Expected %q.ContainsAddress(%q)==%t", test.name, ipv6, tc, test.fail) } } }) } } func TestSockAddr_IPv6Addr_ContainsNetwork(t *testing.T) { tests := []struct { input string pass []string fail []string }{ { // 0 input: "::1/128", pass: []string{ "::1", "[::1/128]", }, fail: []string{ "100::", }, }, } for idx, test := range tests { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { ipv6, err := sockaddr.NewIPv6Addr(test.input) if err != nil { t.Fatalf("[%d] Unable to create an IPv6Addr from %+q: %v", idx, test.input, err) } for passIdx, passInput := range test.pass { passAddr, err := sockaddr.NewIPv6Addr(passInput) if err != nil { t.Fatalf("[%d/%d] Unable to create an IPv6Addr from %+q: %v", idx, passIdx, passInput, err) } if !passAddr.ContainsNetwork(ipv6) { t.Errorf("[%d/%d] Expected %+q to contain %+q", idx, passIdx, test.input, passInput) } } for failIdx, failInput := range test.fail { failAddr, err := sockaddr.NewIPv6Addr(failInput) if err != nil { t.Fatalf("[%d/%d] Unable to create an IPv6Addr from %+q: %v", idx, failIdx, failInput, err) } if failAddr.ContainsNetwork(ipv6) { t.Errorf("[%d/%d] Expected %+q to contain %+q", idx, failIdx, test.input, failInput) } } }) } } func TestSockAddr_IPv6Addr_Equal(t *testing.T) { tests := []struct { name string input sockaddr.IPv6Addr cases sockaddr.SockAddrs fail bool }{ { name: "addr equal", input: sockaddr.MustIPv6Addr("2001:4860:0:2001::68/128"), cases: sockaddr.SockAddrs{ sockaddr.MustIPv6Addr("2001:4860:0:2001::68"), sockaddr.MustIPv6Addr("2001:4860:0:2001::68/128"), sockaddr.MustIPv6Addr("[2001:4860:0:2001::68]:0"), }, }, { name: "IPv6Addr not equal", input: sockaddr.MustIPv6Addr("2001:4860:0:2001::68/128"), cases: sockaddr.SockAddrs{ sockaddr.MustIPv6Addr("2001:DB8::/48"), sockaddr.MustIPv6Addr("2001:4860:0:2001::67/128"), sockaddr.MustIPv6Addr("2001:4860:0:2001::67"), sockaddr.MustIPv6Addr("[2001:4860:0:2001::68]:80"), sockaddr.MustIPv4Addr("1.2.3.4"), sockaddr.MustUnixSock("/tmp/foo"), }, fail: true, }, { name: "equal CIDR", input: sockaddr.MustIPv6Addr("2001:4860:0:2001::68/64"), cases: sockaddr.SockAddrs{ sockaddr.MustIPv6Addr("2001:4860:0:2001::68/64"), }, }, { name: "not equal CIDR", input: sockaddr.MustIPv6Addr("2001:4860:0:2001::68/64"), cases: sockaddr.SockAddrs{ sockaddr.MustIPv6Addr("2001:DB8::/48"), sockaddr.MustIPv6Addr("2001:4860:0:2001::67/128"), sockaddr.MustIPv6Addr("2001:4860:0:2001::67"), sockaddr.MustIPv6Addr("[2001:4860:0:2001::68]:80"), sockaddr.MustIPv4Addr("1.2.3.4/32"), sockaddr.MustUnixSock("/tmp/foo"), }, fail: true, }, } for idx, test := range tests { if test.name == "" { t.Fatalf("test %d needs a name", idx) } t.Run(test.name, func(t *testing.T) { ipv6 := test.input for _, tc := range test.cases { if ipv6.Equal(tc) == test.fail { t.Errorf("%s: Expected %s Equal(%q)=%t", test.name, ipv6, tc, test.fail) } } }) } } func TestIPv6Addr_CmpRFC(t *testing.T) { tests := []struct { name string recv sockaddr.SockAddr arg sockaddr.SockAddr rfcNum uint want int }{ { name: "simple in RFC", recv: sockaddr.MustIPv6Addr("::1"), arg: sockaddr.MustIPv6Addr("100::"), rfcNum: 6590, }, { name: "ipv6 cmp IPv4", recv: sockaddr.MustIPv6Addr("2002:c058:6301::/120"), arg: sockaddr.MustIPv4Addr("192.88.99.0/24"), rfcNum: 3068, want: -1, }, { name: "ipv6 cmp IPv4", recv: sockaddr.MustIPv6Addr("::1"), arg: sockaddr.MustIPv4Addr("1.2.3.4"), rfcNum: 6590, }, { name: "ipv6 cmp IPv4", recv: sockaddr.MustIPv6Addr("::1"), arg: sockaddr.MustIPv4Addr("192.168.1.1"), rfcNum: 1918, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d needs a name", i) } t.Run(test.name, func(t *testing.T) { if cmp := test.recv.CmpRFC(test.rfcNum, test.arg); cmp != test.want { t.Fatalf("%s: want %d got %d", test.name, test.want, cmp) } }) } } func TestIPv6Attrs(t *testing.T) { const expectedNumAttrs = 2 attrs := sockaddr.IPv6Attrs() if len(attrs) != expectedNumAttrs { t.Fatalf("wrong number of IPv6Attrs: %d vs %d", len(attrs), expectedNumAttrs) } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/rfc.go000066400000000000000000001164701316221320600244270ustar00rootroot00000000000000package sockaddr // ForwardingBlacklist is a faux RFC that includes a list of non-forwardable IP // blocks. const ForwardingBlacklist = 4294967295 const ForwardingBlacklistRFC = "4294967295" // IsRFC tests to see if an SockAddr matches the specified RFC func IsRFC(rfcNum uint, sa SockAddr) bool { rfcNetMap := KnownRFCs() rfcNets, ok := rfcNetMap[rfcNum] if !ok { return false } var contained bool for _, rfcNet := range rfcNets { if rfcNet.Contains(sa) { contained = true break } } return contained } // KnownRFCs returns an initial set of known RFCs. // // NOTE (sean@): As this list evolves over time, please submit patches to keep // this list current. If something isn't right, inquire, as it may just be a // bug on my part. Some of the inclusions were based on my judgement as to what // would be a useful value (e.g. RFC3330). // // Useful resources: // // * https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml // * https://www.iana.org/assignments/ipv6-unicast-address-assignments/ipv6-unicast-address-assignments.xhtml // * https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml func KnownRFCs() map[uint]SockAddrs { // NOTE(sean@): Multiple SockAddrs per RFC lend themselves well to a // RADIX tree, but `ENOTIME`. Patches welcome. return map[uint]SockAddrs{ 919: { // [RFC919] Broadcasting Internet Datagrams MustIPv4Addr("255.255.255.255/32"), // [RFC1122], §7 Broadcast IP Addressing - Proposed Standards }, 1122: { // [RFC1122] Requirements for Internet Hosts -- Communication Layers MustIPv4Addr("0.0.0.0/8"), // [RFC1122], §3.2.1.3 MustIPv4Addr("127.0.0.0/8"), // [RFC1122], §3.2.1.3 }, 1112: { // [RFC1112] Host Extensions for IP Multicasting MustIPv4Addr("224.0.0.0/4"), // [RFC1112], §4 Host Group Addresses }, 1918: { // [RFC1918] Address Allocation for Private Internets MustIPv4Addr("10.0.0.0/8"), MustIPv4Addr("172.16.0.0/12"), MustIPv4Addr("192.168.0.0/16"), }, 2544: { // [RFC2544] Benchmarking Methodology for Network // Interconnect Devices MustIPv4Addr("198.18.0.0/15"), }, 2765: { // [RFC2765] Stateless IP/ICMP Translation Algorithm // (SIIT) (obsoleted by RFCs 6145, which itself was // later obsoleted by 7915). // [RFC2765], §2.1 Addresses MustIPv6Addr("0:0:0:0:0:ffff:0:0/96"), }, 2928: { // [RFC2928] Initial IPv6 Sub-TLA ID Assignments MustIPv6Addr("2001::/16"), // Superblock //MustIPv6Addr("2001:0000::/23"), // IANA //MustIPv6Addr("2001:0200::/23"), // APNIC //MustIPv6Addr("2001:0400::/23"), // ARIN //MustIPv6Addr("2001:0600::/23"), // RIPE NCC //MustIPv6Addr("2001:0800::/23"), // (future assignment) // ... //MustIPv6Addr("2001:FE00::/23"), // (future assignment) }, 3056: { // 6to4 address // [RFC3056] Connection of IPv6 Domains via IPv4 Clouds // [RFC3056], §2 IPv6 Prefix Allocation MustIPv6Addr("2002::/16"), }, 3068: { // [RFC3068] An Anycast Prefix for 6to4 Relay Routers // (obsolete by RFC7526) // [RFC3068], § 6to4 Relay anycast address MustIPv4Addr("192.88.99.0/24"), // [RFC3068], §2.5 6to4 IPv6 relay anycast address // // NOTE: /120 == 128-(32-24) MustIPv6Addr("2002:c058:6301::/120"), }, 3171: { // [RFC3171] IANA Guidelines for IPv4 Multicast Address Assignments MustIPv4Addr("224.0.0.0/4"), }, 3330: { // [RFC3330] Special-Use IPv4 Addresses // Addresses in this block refer to source hosts on // "this" network. Address 0.0.0.0/32 may be used as a // source address for this host on this network; other // addresses within 0.0.0.0/8 may be used to refer to // specified hosts on this network [RFC1700, page 4]. MustIPv4Addr("0.0.0.0/8"), // 10.0.0.0/8 - This block is set aside for use in // private networks. Its intended use is documented in // [RFC1918]. Addresses within this block should not // appear on the public Internet. MustIPv4Addr("10.0.0.0/8"), // 14.0.0.0/8 - This block is set aside for assignments // to the international system of Public Data Networks // [RFC1700, page 181]. The registry of assignments // within this block can be accessed from the "Public // Data Network Numbers" link on the web page at // http://www.iana.org/numbers.html. Addresses within // this block are assigned to users and should be // treated as such. // 24.0.0.0/8 - This block was allocated in early 1996 // for use in provisioning IP service over cable // television systems. Although the IANA initially was // involved in making assignments to cable operators, // this responsibility was transferred to American // Registry for Internet Numbers (ARIN) in May 2001. // Addresses within this block are assigned in the // normal manner and should be treated as such. // 39.0.0.0/8 - This block was used in the "Class A // Subnet Experiment" that commenced in May 1995, as // documented in [RFC1797]. The experiment has been // completed and this block has been returned to the // pool of addresses reserved for future allocation or // assignment. This block therefore no longer has a // special use and is subject to allocation to a // Regional Internet Registry for assignment in the // normal manner. // 127.0.0.0/8 - This block is assigned for use as the Internet host // loopback address. A datagram sent by a higher level protocol to an // address anywhere within this block should loop back inside the host. // This is ordinarily implemented using only 127.0.0.1/32 for loopback, // but no addresses within this block should ever appear on any network // anywhere [RFC1700, page 5]. MustIPv4Addr("127.0.0.0/8"), // 128.0.0.0/16 - This block, corresponding to the // numerically lowest of the former Class B addresses, // was initially and is still reserved by the IANA. // Given the present classless nature of the IP address // space, the basis for the reservation no longer // applies and addresses in this block are subject to // future allocation to a Regional Internet Registry for // assignment in the normal manner. // 169.254.0.0/16 - This is the "link local" block. It // is allocated for communication between hosts on a // single link. Hosts obtain these addresses by // auto-configuration, such as when a DHCP server may // not be found. MustIPv4Addr("169.254.0.0/16"), // 172.16.0.0/12 - This block is set aside for use in // private networks. Its intended use is documented in // [RFC1918]. Addresses within this block should not // appear on the public Internet. MustIPv4Addr("172.16.0.0/12"), // 191.255.0.0/16 - This block, corresponding to the numerically highest // to the former Class B addresses, was initially and is still reserved // by the IANA. Given the present classless nature of the IP address // space, the basis for the reservation no longer applies and addresses // in this block are subject to future allocation to a Regional Internet // Registry for assignment in the normal manner. // 192.0.0.0/24 - This block, corresponding to the // numerically lowest of the former Class C addresses, // was initially and is still reserved by the IANA. // Given the present classless nature of the IP address // space, the basis for the reservation no longer // applies and addresses in this block are subject to // future allocation to a Regional Internet Registry for // assignment in the normal manner. // 192.0.2.0/24 - This block is assigned as "TEST-NET" for use in // documentation and example code. It is often used in conjunction with // domain names example.com or example.net in vendor and protocol // documentation. Addresses within this block should not appear on the // public Internet. MustIPv4Addr("192.0.2.0/24"), // 192.88.99.0/24 - This block is allocated for use as 6to4 relay // anycast addresses, according to [RFC3068]. MustIPv4Addr("192.88.99.0/24"), // 192.168.0.0/16 - This block is set aside for use in private networks. // Its intended use is documented in [RFC1918]. Addresses within this // block should not appear on the public Internet. MustIPv4Addr("192.168.0.0/16"), // 198.18.0.0/15 - This block has been allocated for use // in benchmark tests of network interconnect devices. // Its use is documented in [RFC2544]. MustIPv4Addr("198.18.0.0/15"), // 223.255.255.0/24 - This block, corresponding to the // numerically highest of the former Class C addresses, // was initially and is still reserved by the IANA. // Given the present classless nature of the IP address // space, the basis for the reservation no longer // applies and addresses in this block are subject to // future allocation to a Regional Internet Registry for // assignment in the normal manner. // 224.0.0.0/4 - This block, formerly known as the Class // D address space, is allocated for use in IPv4 // multicast address assignments. The IANA guidelines // for assignments from this space are described in // [RFC3171]. MustIPv4Addr("224.0.0.0/4"), // 240.0.0.0/4 - This block, formerly known as the Class E address // space, is reserved. The "limited broadcast" destination address // 255.255.255.255 should never be forwarded outside the (sub-)net of // the source. The remainder of this space is reserved // for future use. [RFC1700, page 4] MustIPv4Addr("240.0.0.0/4"), }, 3849: { // [RFC3849] IPv6 Address Prefix Reserved for Documentation MustIPv6Addr("2001:db8::/32"), // [RFC3849], §4 IANA Considerations }, 3927: { // [RFC3927] Dynamic Configuration of IPv4 Link-Local Addresses MustIPv4Addr("169.254.0.0/16"), // [RFC3927], §2.1 Link-Local Address Selection }, 4038: { // [RFC4038] Application Aspects of IPv6 Transition // [RFC4038], §4.2. IPv6 Applications in a Dual-Stack Node MustIPv6Addr("0:0:0:0:0:ffff::/96"), }, 4193: { // [RFC4193] Unique Local IPv6 Unicast Addresses MustIPv6Addr("fc00::/7"), }, 4291: { // [RFC4291] IP Version 6 Addressing Architecture // [RFC4291], §2.5.2 The Unspecified Address MustIPv6Addr("::/128"), // [RFC4291], §2.5.3 The Loopback Address MustIPv6Addr("::1/128"), // [RFC4291], §2.5.5.1. IPv4-Compatible IPv6 Address MustIPv6Addr("::/96"), // [RFC4291], §2.5.5.2. IPv4-Mapped IPv6 Address MustIPv6Addr("::ffff:0:0/96"), // [RFC4291], §2.5.6 Link-Local IPv6 Unicast Addresses MustIPv6Addr("fe80::/10"), // [RFC4291], §2.5.7 Site-Local IPv6 Unicast Addresses // (depreciated) MustIPv6Addr("fec0::/10"), // [RFC4291], §2.7 Multicast Addresses MustIPv6Addr("ff00::/8"), // IPv6 Multicast Information. // // In the following "table" below, `ff0x` is replaced // with the following values depending on the scope of // the query: // // IPv6 Multicast Scopes: // * ff00/9 // reserved // * ff01/9 // interface-local // * ff02/9 // link-local // * ff03/9 // realm-local // * ff04/9 // admin-local // * ff05/9 // site-local // * ff08/9 // organization-local // * ff0e/9 // global // * ff0f/9 // reserved // // IPv6 Multicast Addresses: // * ff0x::2 // All routers // * ff02::5 // OSPFIGP // * ff02::6 // OSPFIGP Designated Routers // * ff02::9 // RIP Routers // * ff02::a // EIGRP Routers // * ff02::d // All PIM Routers // * ff02::1a // All RPL Routers // * ff0x::fb // mDNSv6 // * ff0x::101 // All Network Time Protocol (NTP) servers // * ff02::1:1 // Link Name // * ff02::1:2 // All-dhcp-agents // * ff02::1:3 // Link-local Multicast Name Resolution // * ff05::1:3 // All-dhcp-servers // * ff02::1:ff00:0/104 // Solicited-node multicast address. // * ff02::2:ff00:0/104 // Node Information Queries }, 4380: { // [RFC4380] Teredo: Tunneling IPv6 over UDP through // Network Address Translations (NATs) // [RFC4380], §2.6 Global Teredo IPv6 Service Prefix MustIPv6Addr("2001:0000::/32"), }, 4773: { // [RFC4773] Administration of the IANA Special Purpose IPv6 Address Block MustIPv6Addr("2001:0000::/23"), // IANA }, 4843: { // [RFC4843] An IPv6 Prefix for Overlay Routable Cryptographic Hash Identifiers (ORCHID) MustIPv6Addr("2001:10::/28"), // [RFC4843], §7 IANA Considerations }, 5180: { // [RFC5180] IPv6 Benchmarking Methodology for Network Interconnect Devices MustIPv6Addr("2001:0200::/48"), // [RFC5180], §8 IANA Considerations }, 5735: { // [RFC5735] Special Use IPv4 Addresses MustIPv4Addr("192.0.2.0/24"), // TEST-NET-1 MustIPv4Addr("198.51.100.0/24"), // TEST-NET-2 MustIPv4Addr("203.0.113.0/24"), // TEST-NET-3 MustIPv4Addr("198.18.0.0/15"), // Benchmarks }, 5737: { // [RFC5737] IPv4 Address Blocks Reserved for Documentation MustIPv4Addr("192.0.2.0/24"), // TEST-NET-1 MustIPv4Addr("198.51.100.0/24"), // TEST-NET-2 MustIPv4Addr("203.0.113.0/24"), // TEST-NET-3 }, 6052: { // [RFC6052] IPv6 Addressing of IPv4/IPv6 Translators MustIPv6Addr("64:ff9b::/96"), // [RFC6052], §2.1. Well-Known Prefix }, 6333: { // [RFC6333] Dual-Stack Lite Broadband Deployments Following IPv4 Exhaustion MustIPv4Addr("192.0.0.0/29"), // [RFC6333], §5.7 Well-Known IPv4 Address }, 6598: { // [RFC6598] IANA-Reserved IPv4 Prefix for Shared Address Space MustIPv4Addr("100.64.0.0/10"), }, 6666: { // [RFC6666] A Discard Prefix for IPv6 MustIPv6Addr("0100::/64"), }, 6890: { // [RFC6890] Special-Purpose IP Address Registries // From "RFC6890 §2.2.1 Information Requirements": /* The IPv4 and IPv6 Special-Purpose Address Registries maintain the following information regarding each entry: o Address Block - A block of IPv4 or IPv6 addresses that has been registered for a special purpose. o Name - A descriptive name for the special-purpose address block. o RFC - The RFC through which the special-purpose address block was requested. o Allocation Date - The date upon which the special-purpose address block was allocated. o Termination Date - The date upon which the allocation is to be terminated. This field is applicable for limited-use allocations only. o Source - A boolean value indicating whether an address from the allocated special-purpose address block is valid when used as the source address of an IP datagram that transits two devices. o Destination - A boolean value indicating whether an address from the allocated special-purpose address block is valid when used as the destination address of an IP datagram that transits two devices. o Forwardable - A boolean value indicating whether a router may forward an IP datagram whose destination address is drawn from the allocated special-purpose address block between external interfaces. o Global - A boolean value indicating whether an IP datagram whose destination address is drawn from the allocated special-purpose address block is forwardable beyond a specified administrative domain. o Reserved-by-Protocol - A boolean value indicating whether the special-purpose address block is reserved by IP, itself. This value is "TRUE" if the RFC that created the special-purpose address block requires all compliant IP implementations to behave in a special way when processing packets either to or from addresses contained by the address block. If the value of "Destination" is FALSE, the values of "Forwardable" and "Global" must also be false. */ /*+----------------------+----------------------------+ * | Attribute | Value | * +----------------------+----------------------------+ * | Address Block | 0.0.0.0/8 | * | Name | "This host on this network"| * | RFC | [RFC1122], Section 3.2.1.3 | * | Allocation Date | September 1981 | * | Termination Date | N/A | * | Source | True | * | Destination | False | * | Forwardable | False | * | Global | False | * | Reserved-by-Protocol | True | * +----------------------+----------------------------+*/ MustIPv4Addr("0.0.0.0/8"), /*+----------------------+---------------+ * | Attribute | Value | * +----------------------+---------------+ * | Address Block | 10.0.0.0/8 | * | Name | Private-Use | * | RFC | [RFC1918] | * | Allocation Date | February 1996 | * | Termination Date | N/A | * | Source | True | * | Destination | True | * | Forwardable | True | * | Global | False | * | Reserved-by-Protocol | False | * +----------------------+---------------+ */ MustIPv4Addr("10.0.0.0/8"), /*+----------------------+----------------------+ | Attribute | Value | +----------------------+----------------------+ | Address Block | 100.64.0.0/10 | | Name | Shared Address Space | | RFC | [RFC6598] | | Allocation Date | April 2012 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | True | | Global | False | | Reserved-by-Protocol | False | +----------------------+----------------------+*/ MustIPv4Addr("100.64.0.0/10"), /*+----------------------+----------------------------+ | Attribute | Value | +----------------------+----------------------------+ | Address Block | 127.0.0.0/8 | | Name | Loopback | | RFC | [RFC1122], Section 3.2.1.3 | | Allocation Date | September 1981 | | Termination Date | N/A | | Source | False [1] | | Destination | False [1] | | Forwardable | False [1] | | Global | False [1] | | Reserved-by-Protocol | True | +----------------------+----------------------------+*/ // [1] Several protocols have been granted exceptions to // this rule. For examples, see [RFC4379] and // [RFC5884]. MustIPv4Addr("127.0.0.0/8"), /*+----------------------+----------------+ | Attribute | Value | +----------------------+----------------+ | Address Block | 169.254.0.0/16 | | Name | Link Local | | RFC | [RFC3927] | | Allocation Date | May 2005 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | False | | Global | False | | Reserved-by-Protocol | True | +----------------------+----------------+*/ MustIPv4Addr("169.254.0.0/16"), /*+----------------------+---------------+ | Attribute | Value | +----------------------+---------------+ | Address Block | 172.16.0.0/12 | | Name | Private-Use | | RFC | [RFC1918] | | Allocation Date | February 1996 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | True | | Global | False | | Reserved-by-Protocol | False | +----------------------+---------------+*/ MustIPv4Addr("172.16.0.0/12"), /*+----------------------+---------------------------------+ | Attribute | Value | +----------------------+---------------------------------+ | Address Block | 192.0.0.0/24 [2] | | Name | IETF Protocol Assignments | | RFC | Section 2.1 of this document | | Allocation Date | January 2010 | | Termination Date | N/A | | Source | False | | Destination | False | | Forwardable | False | | Global | False | | Reserved-by-Protocol | False | +----------------------+---------------------------------+*/ // [2] Not usable unless by virtue of a more specific // reservation. MustIPv4Addr("192.0.0.0/24"), /*+----------------------+--------------------------------+ | Attribute | Value | +----------------------+--------------------------------+ | Address Block | 192.0.0.0/29 | | Name | IPv4 Service Continuity Prefix | | RFC | [RFC6333], [RFC7335] | | Allocation Date | June 2011 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | True | | Global | False | | Reserved-by-Protocol | False | +----------------------+--------------------------------+*/ MustIPv4Addr("192.0.0.0/29"), /*+----------------------+----------------------------+ | Attribute | Value | +----------------------+----------------------------+ | Address Block | 192.0.2.0/24 | | Name | Documentation (TEST-NET-1) | | RFC | [RFC5737] | | Allocation Date | January 2010 | | Termination Date | N/A | | Source | False | | Destination | False | | Forwardable | False | | Global | False | | Reserved-by-Protocol | False | +----------------------+----------------------------+*/ MustIPv4Addr("192.0.2.0/24"), /*+----------------------+--------------------+ | Attribute | Value | +----------------------+--------------------+ | Address Block | 192.88.99.0/24 | | Name | 6to4 Relay Anycast | | RFC | [RFC3068] | | Allocation Date | June 2001 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | True | | Global | True | | Reserved-by-Protocol | False | +----------------------+--------------------+*/ MustIPv4Addr("192.88.99.0/24"), /*+----------------------+----------------+ | Attribute | Value | +----------------------+----------------+ | Address Block | 192.168.0.0/16 | | Name | Private-Use | | RFC | [RFC1918] | | Allocation Date | February 1996 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | True | | Global | False | | Reserved-by-Protocol | False | +----------------------+----------------+*/ MustIPv4Addr("192.168.0.0/16"), /*+----------------------+---------------+ | Attribute | Value | +----------------------+---------------+ | Address Block | 198.18.0.0/15 | | Name | Benchmarking | | RFC | [RFC2544] | | Allocation Date | March 1999 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | True | | Global | False | | Reserved-by-Protocol | False | +----------------------+---------------+*/ MustIPv4Addr("198.18.0.0/15"), /*+----------------------+----------------------------+ | Attribute | Value | +----------------------+----------------------------+ | Address Block | 198.51.100.0/24 | | Name | Documentation (TEST-NET-2) | | RFC | [RFC5737] | | Allocation Date | January 2010 | | Termination Date | N/A | | Source | False | | Destination | False | | Forwardable | False | | Global | False | | Reserved-by-Protocol | False | +----------------------+----------------------------+*/ MustIPv4Addr("198.51.100.0/24"), /*+----------------------+----------------------------+ | Attribute | Value | +----------------------+----------------------------+ | Address Block | 203.0.113.0/24 | | Name | Documentation (TEST-NET-3) | | RFC | [RFC5737] | | Allocation Date | January 2010 | | Termination Date | N/A | | Source | False | | Destination | False | | Forwardable | False | | Global | False | | Reserved-by-Protocol | False | +----------------------+----------------------------+*/ MustIPv4Addr("203.0.113.0/24"), /*+----------------------+----------------------+ | Attribute | Value | +----------------------+----------------------+ | Address Block | 240.0.0.0/4 | | Name | Reserved | | RFC | [RFC1112], Section 4 | | Allocation Date | August 1989 | | Termination Date | N/A | | Source | False | | Destination | False | | Forwardable | False | | Global | False | | Reserved-by-Protocol | True | +----------------------+----------------------+*/ MustIPv4Addr("240.0.0.0/4"), /*+----------------------+----------------------+ | Attribute | Value | +----------------------+----------------------+ | Address Block | 255.255.255.255/32 | | Name | Limited Broadcast | | RFC | [RFC0919], Section 7 | | Allocation Date | October 1984 | | Termination Date | N/A | | Source | False | | Destination | True | | Forwardable | False | | Global | False | | Reserved-by-Protocol | False | +----------------------+----------------------+*/ MustIPv4Addr("255.255.255.255/32"), /*+----------------------+------------------+ | Attribute | Value | +----------------------+------------------+ | Address Block | ::1/128 | | Name | Loopback Address | | RFC | [RFC4291] | | Allocation Date | February 2006 | | Termination Date | N/A | | Source | False | | Destination | False | | Forwardable | False | | Global | False | | Reserved-by-Protocol | True | +----------------------+------------------+*/ MustIPv6Addr("::1/128"), /*+----------------------+---------------------+ | Attribute | Value | +----------------------+---------------------+ | Address Block | ::/128 | | Name | Unspecified Address | | RFC | [RFC4291] | | Allocation Date | February 2006 | | Termination Date | N/A | | Source | True | | Destination | False | | Forwardable | False | | Global | False | | Reserved-by-Protocol | True | +----------------------+---------------------+*/ MustIPv6Addr("::/128"), /*+----------------------+---------------------+ | Attribute | Value | +----------------------+---------------------+ | Address Block | 64:ff9b::/96 | | Name | IPv4-IPv6 Translat. | | RFC | [RFC6052] | | Allocation Date | October 2010 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | True | | Global | True | | Reserved-by-Protocol | False | +----------------------+---------------------+*/ MustIPv6Addr("64:ff9b::/96"), /*+----------------------+---------------------+ | Attribute | Value | +----------------------+---------------------+ | Address Block | ::ffff:0:0/96 | | Name | IPv4-mapped Address | | RFC | [RFC4291] | | Allocation Date | February 2006 | | Termination Date | N/A | | Source | False | | Destination | False | | Forwardable | False | | Global | False | | Reserved-by-Protocol | True | +----------------------+---------------------+*/ MustIPv6Addr("::ffff:0:0/96"), /*+----------------------+----------------------------+ | Attribute | Value | +----------------------+----------------------------+ | Address Block | 100::/64 | | Name | Discard-Only Address Block | | RFC | [RFC6666] | | Allocation Date | June 2012 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | True | | Global | False | | Reserved-by-Protocol | False | +----------------------+----------------------------+*/ MustIPv6Addr("100::/64"), /*+----------------------+---------------------------+ | Attribute | Value | +----------------------+---------------------------+ | Address Block | 2001::/23 | | Name | IETF Protocol Assignments | | RFC | [RFC2928] | | Allocation Date | September 2000 | | Termination Date | N/A | | Source | False[1] | | Destination | False[1] | | Forwardable | False[1] | | Global | False[1] | | Reserved-by-Protocol | False | +----------------------+---------------------------+*/ // [1] Unless allowed by a more specific allocation. MustIPv6Addr("2001::/16"), /*+----------------------+----------------+ | Attribute | Value | +----------------------+----------------+ | Address Block | 2001::/32 | | Name | TEREDO | | RFC | [RFC4380] | | Allocation Date | January 2006 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | True | | Global | False | | Reserved-by-Protocol | False | +----------------------+----------------+*/ // Covered by previous entry, included for completeness. // // MustIPv6Addr("2001::/16"), /*+----------------------+----------------+ | Attribute | Value | +----------------------+----------------+ | Address Block | 2001:2::/48 | | Name | Benchmarking | | RFC | [RFC5180] | | Allocation Date | April 2008 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | True | | Global | False | | Reserved-by-Protocol | False | +----------------------+----------------+*/ // Covered by previous entry, included for completeness. // // MustIPv6Addr("2001:2::/48"), /*+----------------------+---------------+ | Attribute | Value | +----------------------+---------------+ | Address Block | 2001:db8::/32 | | Name | Documentation | | RFC | [RFC3849] | | Allocation Date | July 2004 | | Termination Date | N/A | | Source | False | | Destination | False | | Forwardable | False | | Global | False | | Reserved-by-Protocol | False | +----------------------+---------------+*/ // Covered by previous entry, included for completeness. // // MustIPv6Addr("2001:db8::/32"), /*+----------------------+--------------+ | Attribute | Value | +----------------------+--------------+ | Address Block | 2001:10::/28 | | Name | ORCHID | | RFC | [RFC4843] | | Allocation Date | March 2007 | | Termination Date | March 2014 | | Source | False | | Destination | False | | Forwardable | False | | Global | False | | Reserved-by-Protocol | False | +----------------------+--------------+*/ // Covered by previous entry, included for completeness. // // MustIPv6Addr("2001:10::/28"), /*+----------------------+---------------+ | Attribute | Value | +----------------------+---------------+ | Address Block | 2002::/16 [2] | | Name | 6to4 | | RFC | [RFC3056] | | Allocation Date | February 2001 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | True | | Global | N/A [2] | | Reserved-by-Protocol | False | +----------------------+---------------+*/ // [2] See [RFC3056] for details. MustIPv6Addr("2002::/16"), /*+----------------------+--------------+ | Attribute | Value | +----------------------+--------------+ | Address Block | fc00::/7 | | Name | Unique-Local | | RFC | [RFC4193] | | Allocation Date | October 2005 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | True | | Global | False | | Reserved-by-Protocol | False | +----------------------+--------------+*/ MustIPv6Addr("fc00::/7"), /*+----------------------+-----------------------+ | Attribute | Value | +----------------------+-----------------------+ | Address Block | fe80::/10 | | Name | Linked-Scoped Unicast | | RFC | [RFC4291] | | Allocation Date | February 2006 | | Termination Date | N/A | | Source | True | | Destination | True | | Forwardable | False | | Global | False | | Reserved-by-Protocol | True | +----------------------+-----------------------+*/ MustIPv6Addr("fe80::/10"), }, 7335: { // [RFC7335] IPv4 Service Continuity Prefix MustIPv4Addr("192.0.0.0/29"), // [RFC7335], §6 IANA Considerations }, ForwardingBlacklist: { // Pseudo-RFC // Blacklist of non-forwardable IP blocks taken from RFC6890 // // TODO: the attributes for forwardable should be // searcahble and embedded in the main list of RFCs // above. MustIPv4Addr("0.0.0.0/8"), MustIPv4Addr("127.0.0.0/8"), MustIPv4Addr("169.254.0.0/16"), MustIPv4Addr("192.0.0.0/24"), MustIPv4Addr("192.0.2.0/24"), MustIPv4Addr("198.51.100.0/24"), MustIPv4Addr("203.0.113.0/24"), MustIPv4Addr("240.0.0.0/4"), MustIPv4Addr("255.255.255.255/32"), MustIPv6Addr("::1/128"), MustIPv6Addr("::/128"), MustIPv6Addr("::ffff:0:0/96"), // There is no way of expressing a whitelist per RFC2928 // atm without creating a negative mask, which I don't // want to do atm. //MustIPv6Addr("2001::/23"), MustIPv6Addr("2001:db8::/32"), MustIPv6Addr("2001:10::/28"), MustIPv6Addr("fe80::/10"), }, } } // VisitAllRFCs iterates over all known RFCs and calls the visitor func VisitAllRFCs(fn func(rfcNum uint, sockaddrs SockAddrs)) { rfcNetMap := KnownRFCs() // Blacklist of faux-RFCs. Don't show the world that we're abusing the // RFC system in this library. rfcBlacklist := map[uint]struct{}{ ForwardingBlacklist: {}, } for rfcNum, sas := range rfcNetMap { if _, found := rfcBlacklist[rfcNum]; !found { fn(rfcNum, sas) } } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/rfc_test.go000066400000000000000000000022301316221320600254520ustar00rootroot00000000000000package sockaddr_test import ( "testing" sockaddr "github.com/hashicorp/go-sockaddr" ) func TestVisitAllRFCs(t *testing.T) { const expectedNumRFCs = 28 numRFCs := 0 sockaddr.VisitAllRFCs(func(rfcNum uint, sas sockaddr.SockAddrs) { numRFCs++ }) if numRFCs != expectedNumRFCs { t.Fatalf("wrong number of RFCs: %d", numRFCs) } } func TestIsRFC(t *testing.T) { tests := []struct { name string sa sockaddr.SockAddr rfcNum uint result bool }{ { name: "rfc1918 pass", sa: sockaddr.MustIPv4Addr("192.168.0.0/16"), rfcNum: 1918, result: true, }, { name: "rfc1918 fail", sa: sockaddr.MustIPv4Addr("1.2.3.4"), rfcNum: 1918, result: false, }, { name: "rfc1918 pass", sa: sockaddr.MustIPv4Addr("192.168.1.1"), rfcNum: 1918, result: true, }, { name: "invalid rfc", sa: sockaddr.MustIPv4Addr("192.168.0.0/16"), rfcNum: 999999999999, result: false, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d needs a name", i) } result := sockaddr.IsRFC(test.rfcNum, test.sa) if result != test.result { t.Fatalf("expected a match") } } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/route_info.go000066400000000000000000000011631316221320600260160ustar00rootroot00000000000000package sockaddr // RouteInterface specifies an interface for obtaining memoized route table and // network information from a given OS. type RouteInterface interface { // GetDefaultInterfaceName returns the name of the interface that has a // default route or an error and an empty string if a problem was // encountered. GetDefaultInterfaceName() (string, error) } // VisitCommands visits each command used by the platform-specific RouteInfo // implementation. func (ri routeInfo) VisitCommands(fn func(name string, cmd []string)) { for k, v := range ri.cmds { cmds := append([]string(nil), v...) fn(k, cmds) } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/route_info_bsd.go000066400000000000000000000015101316221320600266420ustar00rootroot00000000000000// +build darwin dragonfly freebsd netbsd openbsd package sockaddr import "os/exec" var cmds map[string][]string = map[string][]string{ "route": {"/sbin/route", "-n", "get", "default"}, } type routeInfo struct { cmds map[string][]string } // NewRouteInfo returns a BSD-specific implementation of the RouteInfo // interface. func NewRouteInfo() (routeInfo, error) { return routeInfo{ cmds: cmds, }, nil } // GetDefaultInterfaceName returns the interface name attached to the default // route on the default interface. func (ri routeInfo) GetDefaultInterfaceName() (string, error) { out, err := exec.Command(cmds["route"][0], cmds["route"][1:]...).Output() if err != nil { return "", err } var ifName string if ifName, err = parseDefaultIfNameFromRoute(string(out)); err != nil { return "", err } return ifName, nil } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/route_info_default.go000066400000000000000000000004121316221320600275160ustar00rootroot00000000000000// +build android nacl plan9 package sockaddr import "errors" // getDefaultIfName is the default interface function for unsupported platforms. func getDefaultIfName() (string, error) { return "", errors.New("No default interface found (unsupported platform)") } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/route_info_linux.go000066400000000000000000000017021316221320600272340ustar00rootroot00000000000000package sockaddr import ( "errors" "os/exec" ) type routeInfo struct { cmds map[string][]string } // NewRouteInfo returns a Linux-specific implementation of the RouteInfo // interface. func NewRouteInfo() (routeInfo, error) { // CoreOS Container Linux moved ip to /usr/bin/ip, so look it up on // $PATH and fallback to /sbin/ip on error. path, _ := exec.LookPath("ip") if path == "" { path = "/sbin/ip" } return routeInfo{ cmds: map[string][]string{"ip": {path, "route"}}, }, nil } // GetDefaultInterfaceName returns the interface name attached to the default // route on the default interface. func (ri routeInfo) GetDefaultInterfaceName() (string, error) { out, err := exec.Command(ri.cmds["ip"][0], ri.cmds["ip"][1:]...).Output() if err != nil { return "", err } var ifName string if ifName, err = parseDefaultIfNameFromIPCmd(string(out)); err != nil { return "", errors.New("No default interface found") } return ifName, nil } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/route_info_solaris.go000066400000000000000000000015151316221320600275530ustar00rootroot00000000000000package sockaddr import ( "errors" "os/exec" ) var cmds map[string][]string = map[string][]string{ "route": {"/usr/sbin/route", "-n", "get", "default"}, } type routeInfo struct { cmds map[string][]string } // NewRouteInfo returns a BSD-specific implementation of the RouteInfo // interface. func NewRouteInfo() (routeInfo, error) { return routeInfo{ cmds: cmds, }, nil } // GetDefaultInterfaceName returns the interface name attached to the default // route on the default interface. func (ri routeInfo) GetDefaultInterfaceName() (string, error) { out, err := exec.Command(cmds["route"][0], cmds["route"][1:]...).Output() if err != nil { return "", err } var ifName string if ifName, err = parseDefaultIfNameFromRoute(string(out)); err != nil { return "", errors.New("No default interface found") } return ifName, nil } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/route_info_test.go000066400000000000000000000141271316221320600270610ustar00rootroot00000000000000package sockaddr import "testing" func Test_parseBSDDefaultIfName(t *testing.T) { testCases := []struct { name string routeOut string want string }{ { name: "macOS Sierra 10.12 - Common", routeOut: ` route to: default destination: default mask: default gateway: 10.23.9.1 interface: en0 flags: recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire 0 0 0 0 0 0 1500 0 `, want: "en0", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { got, err := parseDefaultIfNameFromRoute(tc.routeOut) if err != nil { t.Fatalf("unable to parse default interface from route output: %v", err) } if got != tc.want { t.Errorf("got %s; want %s", got, tc.want) } }) } } func Test_parseLinuxDefaultIfName(t *testing.T) { testCases := []struct { name string routeOut string want string }{ { name: "Linux Ubuntu 14.04 - Common", routeOut: `default via 10.1.2.1 dev eth0 10.1.2.0/24 dev eth0 proto kernel scope link src 10.1.2.5 `, want: "eth0", }, { name: "Chromebook - 8743.85.0 (Official Build) stable-channel gandof, Milestone 54", routeOut: `default via 192.168.1.1 dev wlan0 metric 1 192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.174 `, want: "wlan0", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { got, err := parseDefaultIfNameFromIPCmd(tc.routeOut) if err != nil { t.Fatalf("unable to parse default interface from route output: %v", err) } if got != tc.want { t.Errorf("got %+q; want %+q", got, tc.want) } }) } } func Test_parseWindowsDefaultIfName(t *testing.T) { testCases := []struct { name string routeOut string ipconfigOut string want string }{ { name: "Windows 10 - Enterprise", routeOut: `=========================================================================== Interface List 10...08 00 27 a2 e9 51 ......Intel(R) PRO/1000 MT Desktop Adapter 13...08 00 27 35 02 ed ......Intel(R) PRO/1000 MT Desktop Adapter #2 1...........................Software Loopback Interface 1 5...00 00 00 00 00 00 00 e0 Microsoft ISATAP Adapter 8...00 00 00 00 00 00 00 e0 Microsoft ISATAP Adapter #3 =========================================================================== IPv4 Route Table =========================================================================== Active Routes: Network Destination Netmask Gateway Interface Metric 0.0.0.0 0.0.0.0 10.0.2.2 10.0.2.15 25 10.0.2.0 255.255.255.0 On-link 10.0.2.15 281 10.0.2.15 255.255.255.255 On-link 10.0.2.15 281 10.0.2.255 255.255.255.255 On-link 10.0.2.15 281 127.0.0.0 255.0.0.0 On-link 127.0.0.1 331 127.0.0.1 255.255.255.255 On-link 127.0.0.1 331 127.255.255.255 255.255.255.255 On-link 127.0.0.1 331 192.168.56.0 255.255.255.0 On-link 192.168.56.100 281 192.168.56.100 255.255.255.255 On-link 192.168.56.100 281 192.168.56.255 255.255.255.255 On-link 192.168.56.100 281 224.0.0.0 240.0.0.0 On-link 127.0.0.1 331 224.0.0.0 240.0.0.0 On-link 192.168.56.100 281 224.0.0.0 240.0.0.0 On-link 10.0.2.15 281 255.255.255.255 255.255.255.255 On-link 127.0.0.1 331 255.255.255.255 255.255.255.255 On-link 192.168.56.100 281 255.255.255.255 255.255.255.255 On-link 10.0.2.15 281 =========================================================================== Persistent Routes: None IPv6 Route Table =========================================================================== Active Routes: If Metric Network Destination Gateway 1 331 ::1/128 On-link 13 281 fe80::/64 On-link 10 281 fe80::/64 On-link 13 281 fe80::60cc:155f:77a4:ab99/128 On-link 10 281 fe80::cccc:710e:f5bb:3088/128 On-link 1 331 ff00::/8 On-link 13 281 ff00::/8 On-link 10 281 ff00::/8 On-link =========================================================================== Persistent Routes: None `, ipconfigOut: `Windows IP Configuration Ethernet adapter Ethernet: Connection-specific DNS Suffix . : host.example.org Link-local IPv6 Address . . . . . : fe80::cccc:710e:f5bb:3088%10 IPv4 Address. . . . . . . . . . . : 10.0.2.15 Subnet Mask . . . . . . . . . . . : 255.255.255.0 Default Gateway . . . . . . . . . : 10.0.2.2 Ethernet adapter Ethernet 2: Connection-specific DNS Suffix . : Link-local IPv6 Address . . . . . : fe80::60cc:155f:77a4:ab99%13 IPv4 Address. . . . . . . . . . . : 192.168.56.100 Subnet Mask . . . . . . . . . . . : 255.255.255.0 Default Gateway . . . . . . . . . : Tunnel adapter isatap.host.example.org: Media State . . . . . . . . . . . : Media disconnected Connection-specific DNS Suffix . : Tunnel adapter Reusable ISATAP Interface {F3F2E4A5-8823-40E5-87EA-1F6881BACC95}: Media State . . . . . . . . . . . : Media disconnected Connection-specific DNS Suffix . : host.example.org `, want: "Ethernet", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { got, err := parseDefaultIfNameWindows(tc.routeOut, tc.ipconfigOut) if err != nil { t.Fatalf("unable to parse default interface from route output: %v", err) } if got != tc.want { t.Errorf("got %s; want %s", got, tc.want) } }) } } func Test_VisitComands(t *testing.T) { ri, err := NewRouteInfo() if err != nil { t.Fatalf("bad: %v", err) } var count int ri.VisitCommands(func(name string, cmd []string) { count++ }) if count == 0 { t.Fatalf("Expected more than 0 items") } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/route_info_windows.go000066400000000000000000000016571316221320600276000ustar00rootroot00000000000000package sockaddr import "os/exec" var cmds map[string][]string = map[string][]string{ "netstat": {"netstat", "-rn"}, "ipconfig": {"ipconfig"}, } type routeInfo struct { cmds map[string][]string } // NewRouteInfo returns a BSD-specific implementation of the RouteInfo // interface. func NewRouteInfo() (routeInfo, error) { return routeInfo{ cmds: cmds, }, nil } // GetDefaultInterfaceName returns the interface name attached to the default // route on the default interface. func (ri routeInfo) GetDefaultInterfaceName() (string, error) { ifNameOut, err := exec.Command(cmds["netstat"][0], cmds["netstat"][1:]...).Output() if err != nil { return "", err } ipconfigOut, err := exec.Command(cmds["ipconfig"][0], cmds["ipconfig"][1:]...).Output() if err != nil { return "", err } ifName, err := parseDefaultIfNameWindows(string(ifNameOut), string(ipconfigOut)) if err != nil { return "", err } return ifName, nil } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/sockaddr.go000066400000000000000000000123501316221320600254370ustar00rootroot00000000000000package sockaddr import ( "encoding/json" "fmt" "strings" ) type SockAddrType int type AttrName string const ( TypeUnknown SockAddrType = 0x0 TypeUnix = 0x1 TypeIPv4 = 0x2 TypeIPv6 = 0x4 // TypeIP is the union of TypeIPv4 and TypeIPv6 TypeIP = 0x6 ) type SockAddr interface { // CmpRFC returns 0 if SockAddr exactly matches one of the matched RFC // networks, -1 if the receiver is contained within the RFC network, or // 1 if the address is not contained within the RFC. CmpRFC(rfcNum uint, sa SockAddr) int // Contains returns true if the SockAddr arg is contained within the // receiver Contains(SockAddr) bool // Equal allows for the comparison of two SockAddrs Equal(SockAddr) bool DialPacketArgs() (string, string) DialStreamArgs() (string, string) ListenPacketArgs() (string, string) ListenStreamArgs() (string, string) // String returns the string representation of SockAddr String() string // Type returns the SockAddrType Type() SockAddrType } // sockAddrAttrMap is a map of the SockAddr type-specific attributes. var sockAddrAttrMap map[AttrName]func(SockAddr) string var sockAddrAttrs []AttrName func init() { sockAddrInit() } // New creates a new SockAddr from the string. The order in which New() // attempts to construct a SockAddr is: IPv4Addr, IPv6Addr, SockAddrUnix. // // NOTE: New() relies on the heuristic wherein if the path begins with either a // '.' or '/' character before creating a new UnixSock. For UNIX sockets that // are absolute paths or are nested within a sub-directory, this works as // expected, however if the UNIX socket is contained in the current working // directory, this will fail unless the path begins with "./" // (e.g. "./my-local-socket"). Calls directly to NewUnixSock() do not suffer // this limitation. Invalid IP addresses such as "256.0.0.0/-1" will run afoul // of this heuristic and be assumed to be a valid UNIX socket path (which they // are, but it is probably not what you want and you won't realize it until you // stat(2) the file system to discover it doesn't exist). func NewSockAddr(s string) (SockAddr, error) { ipv4Addr, err := NewIPv4Addr(s) if err == nil { return ipv4Addr, nil } ipv6Addr, err := NewIPv6Addr(s) if err == nil { return ipv6Addr, nil } // Check to make sure the string begins with either a '.' or '/', or // contains a '/'. if len(s) > 1 && (strings.IndexAny(s[0:1], "./") != -1 || strings.IndexByte(s, '/') != -1) { unixSock, err := NewUnixSock(s) if err == nil { return unixSock, nil } } return nil, fmt.Errorf("Unable to convert %q to an IPv4 or IPv6 address, or a UNIX Socket", s) } // ToIPAddr returns an IPAddr type or nil if the type conversion fails. func ToIPAddr(sa SockAddr) *IPAddr { ipa, ok := sa.(IPAddr) if !ok { return nil } return &ipa } // ToIPv4Addr returns an IPv4Addr type or nil if the type conversion fails. func ToIPv4Addr(sa SockAddr) *IPv4Addr { switch v := sa.(type) { case IPv4Addr: return &v default: return nil } } // ToIPv6Addr returns an IPv6Addr type or nil if the type conversion fails. func ToIPv6Addr(sa SockAddr) *IPv6Addr { switch v := sa.(type) { case IPv6Addr: return &v default: return nil } } // ToUnixSock returns a UnixSock type or nil if the type conversion fails. func ToUnixSock(sa SockAddr) *UnixSock { switch v := sa.(type) { case UnixSock: return &v default: return nil } } // SockAddrAttr returns a string representation of an attribute for the given // SockAddr. func SockAddrAttr(sa SockAddr, selector AttrName) string { fn, found := sockAddrAttrMap[selector] if !found { return "" } return fn(sa) } // String() for SockAddrType returns a string representation of the // SockAddrType (e.g. "IPv4", "IPv6", "UNIX", "IP", or "unknown"). func (sat SockAddrType) String() string { switch sat { case TypeIPv4: return "IPv4" case TypeIPv6: return "IPv6" // There is no concrete "IP" type. Leaving here as a reminder. // case TypeIP: // return "IP" case TypeUnix: return "UNIX" default: panic("unsupported type") } } // sockAddrInit is called once at init() func sockAddrInit() { sockAddrAttrs = []AttrName{ "type", // type should be first "string", } sockAddrAttrMap = map[AttrName]func(sa SockAddr) string{ "string": func(sa SockAddr) string { return sa.String() }, "type": func(sa SockAddr) string { return sa.Type().String() }, } } // UnixSockAttrs returns a list of attributes supported by the UnixSock type func SockAddrAttrs() []AttrName { return sockAddrAttrs } // Although this is pretty trivial to do in a program, having the logic here is // useful all around. Note that this marshals into a *string* -- the underlying // string representation of the sockaddr. If you then unmarshal into this type // in Go, all will work as expected, but externally you can take what comes out // and use the string value directly. type SockAddrMarshaler struct { SockAddr } func (s *SockAddrMarshaler) MarshalJSON() ([]byte, error) { return json.Marshal(s.SockAddr.String()) } func (s *SockAddrMarshaler) UnmarshalJSON(in []byte) error { var str string err := json.Unmarshal(in, &str) if err != nil { return err } sa, err := NewSockAddr(str) if err != nil { return err } s.SockAddr = sa return nil } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/sockaddr_test.go000066400000000000000000000270301316221320600264770ustar00rootroot00000000000000package sockaddr_test import ( "encoding/json" "fmt" "testing" "github.com/hashicorp/go-sockaddr" ) // TODO(sean@): Either extend this test to include IPv6Addr and UnixSock, or // remove and find a good home to test this functionality elsewhere. func TestSockAddr_New(t *testing.T) { type SockAddrFixture struct { input string ResultType string NetworkAddress string BroadcastAddress string IPUint32 sockaddr.IPv4Address Maskbits int BinString string HexString string FirstUsableAddress string LastUsableAddress string } type SockAddrFixtures []SockAddrFixtures goodResults := []SockAddrFixture{ { input: "0.0.0.0", ResultType: "ipv4", NetworkAddress: "0.0.0.0", BroadcastAddress: "0.0.0.0", Maskbits: 32, IPUint32: 0, BinString: "00000000000000000000000000000000", HexString: "00000000", FirstUsableAddress: "0.0.0.0", LastUsableAddress: "0.0.0.0", }, { input: "0.0.0.0/0", ResultType: "ipv4", NetworkAddress: "0.0.0.0", BroadcastAddress: "255.255.255.255", Maskbits: 0, IPUint32: 0, BinString: "00000000000000000000000000000000", HexString: "00000000", FirstUsableAddress: "0.0.0.1", LastUsableAddress: "255.255.255.254", }, { input: "0.0.0.1", ResultType: "ipv4", NetworkAddress: "0.0.0.1", BroadcastAddress: "0.0.0.1", Maskbits: 32, IPUint32: 1, BinString: "00000000000000000000000000000001", HexString: "00000001", FirstUsableAddress: "0.0.0.1", LastUsableAddress: "0.0.0.1", }, { input: "0.0.0.1/1", ResultType: "ipv4", NetworkAddress: "0.0.0.0", BroadcastAddress: "127.255.255.255", Maskbits: 1, IPUint32: 1, BinString: "00000000000000000000000000000001", HexString: "00000001", FirstUsableAddress: "0.0.0.1", LastUsableAddress: "127.255.255.254", }, { input: "128.0.0.0", ResultType: "ipv4", NetworkAddress: "128.0.0.0", BroadcastAddress: "128.0.0.0", Maskbits: 32, IPUint32: 2147483648, BinString: "10000000000000000000000000000000", HexString: "80000000", FirstUsableAddress: "128.0.0.0", LastUsableAddress: "128.0.0.0", }, { input: "255.255.255.255", ResultType: "ipv4", NetworkAddress: "255.255.255.255", BroadcastAddress: "255.255.255.255", Maskbits: 32, IPUint32: 4294967295, BinString: "11111111111111111111111111111111", HexString: "ffffffff", FirstUsableAddress: "255.255.255.255", LastUsableAddress: "255.255.255.255", }, { input: "1.2.3.4", ResultType: "ipv4", NetworkAddress: "1.2.3.4", BroadcastAddress: "1.2.3.4", Maskbits: 32, IPUint32: 16909060, BinString: "00000001000000100000001100000100", HexString: "01020304", FirstUsableAddress: "1.2.3.4", LastUsableAddress: "1.2.3.4", }, { input: "192.168.10.10/16", ResultType: "ipv4", NetworkAddress: "192.168.0.0", BroadcastAddress: "192.168.255.255", Maskbits: 16, IPUint32: 3232238090, BinString: "11000000101010000000101000001010", HexString: "c0a80a0a", FirstUsableAddress: "192.168.0.1", LastUsableAddress: "192.168.255.254", }, { input: "192.168.1.10/24", ResultType: "ipv4", NetworkAddress: "192.168.1.0", BroadcastAddress: "192.168.1.255", Maskbits: 24, IPUint32: 3232235786, BinString: "11000000101010000000000100001010", HexString: "c0a8010a", FirstUsableAddress: "192.168.1.1", LastUsableAddress: "192.168.1.254", }, { input: "192.168.0.1", ResultType: "ipv4", NetworkAddress: "192.168.0.1", BroadcastAddress: "192.168.0.1", Maskbits: 32, IPUint32: 3232235521, BinString: "11000000101010000000000000000001", HexString: "c0a80001", FirstUsableAddress: "192.168.0.1", LastUsableAddress: "192.168.0.1", }, { input: "192.168.0.2/31", ResultType: "ipv4", NetworkAddress: "192.168.0.2", BroadcastAddress: "192.168.0.3", Maskbits: 31, IPUint32: 3232235522, BinString: "11000000101010000000000000000010", HexString: "c0a80002", FirstUsableAddress: "192.168.0.2", LastUsableAddress: "192.168.0.3", }, { input: "240.0.0.0/4", ResultType: "ipv4", NetworkAddress: "240.0.0.0", BroadcastAddress: "255.255.255.255", Maskbits: 4, IPUint32: 4026531840, BinString: "11110000000000000000000000000000", HexString: "f0000000", FirstUsableAddress: "240.0.0.1", LastUsableAddress: "255.255.255.254", }, } for idx, r := range goodResults { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { var ( addr sockaddr.IPAddr str string ) sa, err := sockaddr.NewSockAddr(r.input) if err != nil { t.Fatalf("Failed parse %s", r.input) } switch r.ResultType { case "ipv4": ipv4b, err := sockaddr.NewIPv4Addr(r.input) if err != nil { t.Fatalf("[%d] Unable to construct a new IPv4 from %s: %s", idx, r.input, err) } if !ipv4b.Equal(sa) { t.Fatalf("[%d] Equality comparison failed on fresh IPv4", idx) } type_ := sa.Type() if type_ != sockaddr.TypeIPv4 { t.Fatalf("[%d] Type mismatch for %s: %d", idx, r.input, type_) } ipv4 := sockaddr.ToIPv4Addr(sa) if ipv4 == nil { t.Fatalf("[%d] Failed ToIPv4Addr() %s", idx, r.input) } addr = ipv4.Broadcast() if addr == nil || addr.NetIP().To4().String() != r.BroadcastAddress { t.Fatalf("Failed IPv4Addr.BroadcastAddress() %s: expected %+q, received %+q", r.input, r.BroadcastAddress, addr.NetIP().To4().String()) } maskbits := ipv4.Maskbits() if maskbits != r.Maskbits { t.Fatalf("Failed Maskbits %s: %d != %d", r.input, maskbits, r.Maskbits) } if ipv4.Address != r.IPUint32 { t.Fatalf("Failed ToUint32() %s: %d != %d", r.input, ipv4.Address, r.IPUint32) } str = ipv4.AddressBinString() if str != r.BinString { t.Fatalf("Failed BinString %s: %s != %s", r.input, str, r.BinString) } str = ipv4.AddressHexString() if str != r.HexString { t.Fatalf("Failed HexString %s: %s != %s", r.input, str, r.HexString) } addr = ipv4.Network() if addr == nil || addr.NetIP().To4().String() != r.NetworkAddress { t.Fatalf("Failed NetworkAddress %s: %s != %s", r.input, addr.NetIP().To4().String(), r.NetworkAddress) } addr = ipv4.FirstUsable() if addr == nil || addr.NetIP().To4().String() != r.FirstUsableAddress { t.Fatalf("Failed FirstUsableAddress %s: %s != %s", r.input, addr.NetIP().To4().String(), r.FirstUsableAddress) } addr = ipv4.LastUsable() if addr == nil || addr.NetIP().To4().String() != r.LastUsableAddress { t.Fatalf("Failed LastUsableAddress %s: %s != %s", r.input, addr.NetIP().To4().String(), r.LastUsableAddress) } default: t.Fatalf("Unknown result type: %s", r.ResultType) } }) } badResults := []string{ "256.0.0.0", "0.0.0.0.0", } for idx, badIP := range badResults { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { sa, err := sockaddr.NewSockAddr(badIP) if err == nil { t.Fatalf("Failed should have failed to parse %s: %v", badIP, sa) } if sa != nil { t.Fatalf("SockAddr should be nil") } }) } } func TestSockAddrAttrs(t *testing.T) { const expectedNumAttrs = 2 saa := sockaddr.SockAddrAttrs() if len(saa) != expectedNumAttrs { t.Fatalf("wrong number of SockAddrAttrs: %d vs %d", len(saa), expectedNumAttrs) } tests := []struct { name string sa sockaddr.SockAddr attr sockaddr.AttrName want string }{ { name: "type", sa: sockaddr.MustIPv4Addr("1.2.3.4"), attr: "type", want: "IPv4", }, { name: "string", sa: sockaddr.MustIPv4Addr("1.2.3.4"), attr: "string", want: "1.2.3.4", }, { name: "invalid", sa: sockaddr.MustIPv4Addr("1.2.3.4"), attr: "ENOENT", want: "", }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d needs a name", i) } result := sockaddr.SockAddrAttr(test.sa, test.attr) if result != test.want { t.Fatalf("%s: expected %s got %s", test.name, test.want, result) } } } func TestToFoo(t *testing.T) { tests := []struct { name string sa sockaddr.SockAddr passIP bool passIPv4 bool passIPv6 bool passUnix bool }{ { name: "ipv4", sa: sockaddr.MustIPv4Addr("1.2.3.4"), passIP: true, passIPv4: true, }, { name: "ipv6", sa: sockaddr.MustIPv6Addr("::1"), passIP: true, passIPv6: true, }, { name: "unix", sa: sockaddr.MustUnixSock("/tmp/foo"), passUnix: true, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d must have a name", i) } switch us := sockaddr.ToUnixSock(test.sa); { case us == nil && test.passUnix, us != nil && !test.passUnix: t.Fatalf("bad") } switch ip := sockaddr.ToIPAddr(test.sa); { case ip == nil && test.passIP, ip != nil && !test.passIP: t.Fatalf("bad") } switch ipv4 := sockaddr.ToIPv4Addr(test.sa); { case ipv4 == nil && test.passIPv4, ipv4 != nil && !test.passIPv4: t.Fatalf("bad") } switch ipv6 := sockaddr.ToIPv6Addr(test.sa); { case ipv6 == nil && test.passIPv6, ipv6 != nil && !test.passIPv6: t.Fatalf("bad") } } } func TestSockAddrMarshaler(t *testing.T) { addr := "192.168.10.24/24" sa, err := sockaddr.NewSockAddr(addr) if err != nil { t.Fatal(err) } sam := &sockaddr.SockAddrMarshaler{ SockAddr: sa, } marshaled, err := json.Marshal(sam) if err != nil { t.Fatal(err) } sam2 := &sockaddr.SockAddrMarshaler{} err = json.Unmarshal(marshaled, sam2) if err != nil { t.Fatal(err) } if sam.SockAddr.String() != sam2.SockAddr.String() { t.Fatalf("mismatch after marshaling: %s vs %s", sam.SockAddr.String(), sam2.SockAddr.String()) } if sam2.SockAddr.String() != addr { t.Fatalf("mismatch after marshaling: %s vs %s", addr, sam2.SockAddr.String()) } } func TestSockAddrMultiMarshaler(t *testing.T) { addr := "192.168.10.24/24" type d struct { Addr *sockaddr.SockAddrMarshaler Addrs []*sockaddr.SockAddrMarshaler } sa, err := sockaddr.NewSockAddr(addr) if err != nil { t.Fatal(err) } myD := &d{ Addr: &sockaddr.SockAddrMarshaler{SockAddr: sa}, Addrs: []*sockaddr.SockAddrMarshaler{ &sockaddr.SockAddrMarshaler{SockAddr: sa}, &sockaddr.SockAddrMarshaler{SockAddr: sa}, &sockaddr.SockAddrMarshaler{SockAddr: sa}, }, } marshaled, err := json.Marshal(myD) if err != nil { t.Fatal(err) } var myD2 d err = json.Unmarshal(marshaled, &myD2) if err != nil { t.Fatal(err) } if myD.Addr.String() != myD2.Addr.String() { t.Fatalf("mismatch after marshaling: %s vs %s", myD.Addr.String(), myD2.Addr.String()) } if len(myD.Addrs) != len(myD2.Addrs) { t.Fatalf("mismatch after marshaling: %d vs %d", len(myD.Addrs), len(myD2.Addrs)) } for i, v := range myD.Addrs { if v.String() != myD2.Addrs[i].String() { t.Fatalf("mismatch after marshaling: %s vs %s", v.String(), myD2.Addrs[i].String()) } } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/sockaddrs.go000066400000000000000000000114131316221320600256210ustar00rootroot00000000000000package sockaddr import ( "bytes" "sort" ) // SockAddrs is a slice of SockAddrs type SockAddrs []SockAddr func (s SockAddrs) Len() int { return len(s) } func (s SockAddrs) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // CmpAddrFunc is the function signature that must be met to be used in the // OrderedAddrBy multiAddrSorter type CmpAddrFunc func(p1, p2 *SockAddr) int // multiAddrSorter implements the Sort interface, sorting the SockAddrs within. type multiAddrSorter struct { addrs SockAddrs cmp []CmpAddrFunc } // Sort sorts the argument slice according to the Cmp functions passed to // OrderedAddrBy. func (ms *multiAddrSorter) Sort(sockAddrs SockAddrs) { ms.addrs = sockAddrs sort.Sort(ms) } // OrderedAddrBy sorts SockAddr by the list of sort function pointers. func OrderedAddrBy(cmpFuncs ...CmpAddrFunc) *multiAddrSorter { return &multiAddrSorter{ cmp: cmpFuncs, } } // Len is part of sort.Interface. func (ms *multiAddrSorter) Len() int { return len(ms.addrs) } // Less is part of sort.Interface. It is implemented by looping along the // Cmp() functions until it finds a comparison that is either less than, // equal to, or greater than. func (ms *multiAddrSorter) Less(i, j int) bool { p, q := &ms.addrs[i], &ms.addrs[j] // Try all but the last comparison. var k int for k = 0; k < len(ms.cmp)-1; k++ { cmp := ms.cmp[k] x := cmp(p, q) switch x { case -1: // p < q, so we have a decision. return true case 1: // p > q, so we have a decision. return false } // p == q; try the next comparison. } // All comparisons to here said "equal", so just return whatever the // final comparison reports. switch ms.cmp[k](p, q) { case -1: return true case 1: return false default: // Still a tie! Now what? return false } } // Swap is part of sort.Interface. func (ms *multiAddrSorter) Swap(i, j int) { ms.addrs[i], ms.addrs[j] = ms.addrs[j], ms.addrs[i] } const ( // NOTE (sean@): These constants are here for code readability only and // are sprucing up the code for readability purposes. Some of the // Cmp*() variants have confusing logic (especially when dealing with // mixed-type comparisons) and this, I think, has made it easier to grok // the code faster. sortReceiverBeforeArg = -1 sortDeferDecision = 0 sortArgBeforeReceiver = 1 ) // AscAddress is a sorting function to sort SockAddrs by their respective // address type. Non-equal types are deferred in the sort. func AscAddress(p1Ptr, p2Ptr *SockAddr) int { p1 := *p1Ptr p2 := *p2Ptr switch v := p1.(type) { case IPv4Addr: return v.CmpAddress(p2) case IPv6Addr: return v.CmpAddress(p2) case UnixSock: return v.CmpAddress(p2) default: return sortDeferDecision } } // AscPort is a sorting function to sort SockAddrs by their respective address // type. Non-equal types are deferred in the sort. func AscPort(p1Ptr, p2Ptr *SockAddr) int { p1 := *p1Ptr p2 := *p2Ptr switch v := p1.(type) { case IPv4Addr: return v.CmpPort(p2) case IPv6Addr: return v.CmpPort(p2) default: return sortDeferDecision } } // AscPrivate is a sorting function to sort "more secure" private values before // "more public" values. Both IPv4 and IPv6 are compared against RFC6890 // (RFC6890 includes, and is not limited to, RFC1918 and RFC6598 for IPv4, and // IPv6 includes RFC4193). func AscPrivate(p1Ptr, p2Ptr *SockAddr) int { p1 := *p1Ptr p2 := *p2Ptr switch v := p1.(type) { case IPv4Addr, IPv6Addr: return v.CmpRFC(6890, p2) default: return sortDeferDecision } } // AscNetworkSize is a sorting function to sort SockAddrs based on their network // size. Non-equal types are deferred in the sort. func AscNetworkSize(p1Ptr, p2Ptr *SockAddr) int { p1 := *p1Ptr p2 := *p2Ptr p1Type := p1.Type() p2Type := p2.Type() // Network size operations on non-IP types make no sense if p1Type != p2Type && p1Type != TypeIP { return sortDeferDecision } ipA := p1.(IPAddr) ipB := p2.(IPAddr) return bytes.Compare([]byte(*ipA.NetIPMask()), []byte(*ipB.NetIPMask())) } // AscType is a sorting function to sort "more secure" types before // "less-secure" types. func AscType(p1Ptr, p2Ptr *SockAddr) int { p1 := *p1Ptr p2 := *p2Ptr p1Type := p1.Type() p2Type := p2.Type() switch { case p1Type < p2Type: return sortReceiverBeforeArg case p1Type == p2Type: return sortDeferDecision case p1Type > p2Type: return sortArgBeforeReceiver default: return sortDeferDecision } } // FilterByType returns two lists: a list of matched and unmatched SockAddrs func (sas SockAddrs) FilterByType(type_ SockAddrType) (matched, excluded SockAddrs) { matched = make(SockAddrs, 0, len(sas)) excluded = make(SockAddrs, 0, len(sas)) for _, sa := range sas { if sa.Type()&type_ != 0 { matched = append(matched, sa) } else { excluded = append(excluded, sa) } } return matched, excluded } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/sockaddrs_test.go000066400000000000000000000200611316221320600266570ustar00rootroot00000000000000package sockaddr_test import ( "fmt" "math/rand" "testing" "github.com/hashicorp/consul/lib" "github.com/hashicorp/go-sockaddr" ) func init() { lib.SeedMathRand() } // NOTE: A number of these code paths are exercised in template/ and // cmd/sockaddr/ // sockAddrStringInputs allows for easy test creation by developers. // Parallel arrays of string inputs are converted to their SockAddr // equivalents for use by unit tests. type sockAddrStringInputs []struct { inputAddrs []string sortedAddrs []string sortedTypes []sockaddr.SockAddrType sortFuncs []sockaddr.CmpAddrFunc numIPv4Inputs int numIPv6Inputs int numUnixInputs int } func convertToSockAddrs(t *testing.T, inputs []string) sockaddr.SockAddrs { sockAddrs := make(sockaddr.SockAddrs, 0, len(inputs)) for i, input := range inputs { sa, err := sockaddr.NewSockAddr(input) if err != nil { t.Fatalf("[%d] Invalid SockAddr input for %+q: %v", i, input, err) } sockAddrs = append(sockAddrs, sa) } return sockAddrs } // shuffleStrings randomly shuffles the list of strings func shuffleStrings(list []string) { for i := range list { j := rand.Intn(i + 1) list[i], list[j] = list[j], list[i] } } func TestSockAddr_SockAddrs_AscAddress(t *testing.T) { testInputs := sockAddrStringInputs{ { // testNum: 0 sortFuncs: []sockaddr.CmpAddrFunc{ sockaddr.AscAddress, }, numIPv4Inputs: 9, numIPv6Inputs: 1, numUnixInputs: 0, inputAddrs: []string{ "10.0.0.0/8", "172.16.1.3/12", "128.95.120.2:53", "128.95.120.2/32", "192.168.0.0/16", "128.95.120.1/32", "192.168.1.10/24", "128.95.120.2:8600", "240.0.0.1/4", "::", }, sortedAddrs: []string{ "10.0.0.0/8", "128.95.120.1/32", "128.95.120.2:53", "128.95.120.2/32", "128.95.120.2:8600", "172.16.1.3/12", "192.168.0.0/16", "192.168.1.10/24", "240.0.0.1/4", "::", }, }, } for idx, test := range testInputs { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { shuffleStrings(test.inputAddrs) inputSockAddrs := convertToSockAddrs(t, test.inputAddrs) sas := convertToSockAddrs(t, test.sortedAddrs) sortedIPv4Addrs, nonIPv4Addrs := sas.FilterByType(sockaddr.TypeIPv4) if l := len(sortedIPv4Addrs); l != test.numIPv4Inputs { t.Fatal("[%d] Missing IPv4Addrs: expected %d, received %d", idx, test.numIPv4Inputs, l) } if len(nonIPv4Addrs) != test.numIPv6Inputs+test.numUnixInputs { t.Fatal("[%d] Non-IPv4 Address in input", idx) } // Copy inputAddrs so we can manipulate it. wtb const. sockAddrs := append(sockaddr.SockAddrs(nil), inputSockAddrs...) filteredAddrs, _ := sockAddrs.FilterByType(sockaddr.TypeIPv4) sockaddr.OrderedAddrBy(test.sortFuncs...).Sort(filteredAddrs) ipv4SockAddrs, nonIPv4s := filteredAddrs.FilterByType(sockaddr.TypeIPv4) if len(nonIPv4s) != 0 { t.Fatalf("[%d] bad", idx) } for i, ipv4SockAddr := range ipv4SockAddrs { ipv4Addr := sockaddr.ToIPv4Addr(ipv4SockAddr) sortedIPv4Addr := sockaddr.ToIPv4Addr(sortedIPv4Addrs[i]) if ipv4Addr.Address != sortedIPv4Addr.Address { t.Errorf("[%d/%d] Sort equality failed: expected %s, received %s", idx, i, sortedIPv4Addrs[i], ipv4Addr) } } }) } } func TestSockAddr_SockAddrs_AscPrivate(t *testing.T) { testInputs := []struct { sortFuncs []sockaddr.CmpAddrFunc inputAddrs []string sortedAddrs []string }{ { // testNum: 0 sortFuncs: []sockaddr.CmpAddrFunc{ sockaddr.AscType, sockaddr.AscPrivate, sockaddr.AscAddress, sockaddr.AscType, sockaddr.AscAddress, sockaddr.AscPort, }, inputAddrs: []string{ "10.0.0.0/8", "172.16.1.3/12", "192.168.0.0/16", "192.168.0.0/16", "192.168.1.10/24", "128.95.120.1/32", "128.95.120.2/32", "128.95.120.2:53", "128.95.120.2:8600", "240.0.0.1/4", "::", }, sortedAddrs: []string{ "10.0.0.0/8", "172.16.1.3/12", "192.168.0.0/16", "192.168.0.0/16", "192.168.1.10/24", "240.0.0.1/4", "128.95.120.1/32", "128.95.120.2/32", // "128.95.120.2:53", // "128.95.120.2:8600", // "::", }, }, { sortFuncs: []sockaddr.CmpAddrFunc{ sockaddr.AscType, sockaddr.AscPrivate, sockaddr.AscAddress, }, inputAddrs: []string{ "1.2.3.4:53", "192.168.1.2", "/tmp/foo", "[cc::1]:8600", "[::1]:53", }, sortedAddrs: []string{ "/tmp/foo", "192.168.1.2", "1.2.3.4:53", "[::1]:53", "[cc::1]:8600", }, }, { sortFuncs: []sockaddr.CmpAddrFunc{ sockaddr.AscType, sockaddr.AscPrivate, sockaddr.AscAddress, }, inputAddrs: []string{ "/tmp/foo", "/tmp/bar", "1.2.3.4", }, sortedAddrs: []string{ "/tmp/bar", "/tmp/foo", "1.2.3.4", }, }, } for idx, test := range testInputs { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { sortedAddrs := convertToSockAddrs(t, test.sortedAddrs) inputAddrs := append([]string(nil), test.inputAddrs...) shuffleStrings(inputAddrs) inputSockAddrs := convertToSockAddrs(t, inputAddrs) sockaddr.OrderedAddrBy(test.sortFuncs...).Sort(inputSockAddrs) for i, sockAddr := range sortedAddrs { if !sockAddr.Equal(inputSockAddrs[i]) { t.Logf("Input Addrs:\t%+v", inputAddrs) t.Logf("Sorted Addrs:\t%+v", inputSockAddrs) t.Logf("Expected Addrs:\t%+v", test.sortedAddrs) t.Fatalf("[%d/%d] Sort AscType/AscAddress failed: expected %+q, received %+q", idx, i, sockAddr, inputSockAddrs[i]) } } }) } } func TestSockAddr_SockAddrs_AscPort(t *testing.T) { testInputs := []struct { name string sortFuncs []sockaddr.CmpAddrFunc inputAddrs []string sortedAddrs []string }{ { name: "simple port test", sortFuncs: []sockaddr.CmpAddrFunc{ sockaddr.AscPort, sockaddr.AscType, }, inputAddrs: []string{ "1.2.3.4:53", "/tmp/foo", "[::1]:53", }, sortedAddrs: []string{ "/tmp/foo", "1.2.3.4:53", "[::1]:53", }, }, { name: "simple port test", sortFuncs: []sockaddr.CmpAddrFunc{ sockaddr.AscPort, sockaddr.AscType, }, inputAddrs: []string{ "1.2.3.4:53", "/tmp/foo", }, sortedAddrs: []string{ "/tmp/foo", "1.2.3.4:53", }, }, } for idx, test := range testInputs { t.Run(test.name, func(t *testing.T) { sortedAddrs := convertToSockAddrs(t, test.sortedAddrs) inputAddrs := append([]string(nil), test.inputAddrs...) shuffleStrings(inputAddrs) inputSockAddrs := convertToSockAddrs(t, inputAddrs) sockaddr.OrderedAddrBy(test.sortFuncs...).Sort(inputSockAddrs) for i, sockAddr := range sortedAddrs { if !sockAddr.Equal(inputSockAddrs[i]) { t.Logf("Input Addrs:\t%+v", inputAddrs) t.Logf("Sorted Addrs:\t%+v", inputSockAddrs) t.Logf("Expected Addrs:\t%+v", test.sortedAddrs) t.Fatalf("[%d/%d] Sort AscType/AscAddress failed: expected %+q, received %+q", idx, i, sockAddr, inputSockAddrs[i]) } } }) } } func TestSockAddr_SockAddrs_AscType(t *testing.T) { testInputs := sockAddrStringInputs{ { // testNum: 0 sortFuncs: []sockaddr.CmpAddrFunc{ sockaddr.AscType, }, inputAddrs: []string{ "10.0.0.0/8", "172.16.1.3/12", "128.95.120.2:53", "::", "128.95.120.2/32", "192.168.0.0/16", "128.95.120.1/32", "192.168.1.10/24", "128.95.120.2:8600", "240.0.0.1/4", }, sortedTypes: []sockaddr.SockAddrType{ sockaddr.TypeIPv4, sockaddr.TypeIPv4, sockaddr.TypeIPv4, sockaddr.TypeIPv4, sockaddr.TypeIPv4, sockaddr.TypeIPv4, sockaddr.TypeIPv4, sockaddr.TypeIPv4, sockaddr.TypeIPv4, sockaddr.TypeIPv6, }, }, } for idx, test := range testInputs { t.Run(fmt.Sprintf("%d", idx), func(t *testing.T) { shuffleStrings(test.inputAddrs) inputSockAddrs := convertToSockAddrs(t, test.inputAddrs) sortedAddrs := convertToSockAddrs(t, test.sortedAddrs) sockaddr.OrderedAddrBy(test.sortFuncs...).Sort(inputSockAddrs) for i, sockAddr := range sortedAddrs { if sockAddr.Type() != sortedAddrs[i].Type() { t.Errorf("[%d/%d] Sort AscType failed: expected %+q, received %+q", idx, i, sortedAddrs[i], sockAddr) } } }) } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/template/000077500000000000000000000000001316221320600251305ustar00rootroot00000000000000golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/template/GNUmakefile000066400000000000000000000000201316221320600271720ustar00rootroot00000000000000test:: go test golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/template/README.md000066400000000000000000000002721316221320600264100ustar00rootroot00000000000000# sockaddr/template sockaddr's template library. See the [sockaddr/template](https://godoc.org/github.com/hashicorp/go-sockaddr/template) docs for details on how to use this template. golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/template/doc.go000066400000000000000000000241271316221320600262320ustar00rootroot00000000000000/* Package sockaddr/template provides a text/template interface the SockAddr helper functions. The primary entry point into the sockaddr/template package is through its Parse() call. For example: import ( "fmt" template "github.com/hashicorp/go-sockaddr/template" ) results, err := template.Parse(`{{ GetPrivateIP }}`) if err != nil { fmt.Errorf("Unable to find a private IP address: %v", err) } fmt.Printf("My Private IP address is: %s\n", results) Below is a list of builtin template functions and details re: their usage. It is possible to add additional functions by calling ParseIfAddrsTemplate directly. In general, the calling convention for this template library is to seed a list of initial interfaces via one of the Get*Interfaces() calls, then filter, sort, and extract the necessary attributes for use as string input. This template interface is primarily geared toward resolving specific values that are only available at runtime, but can be defined as a heuristic for execution when a config file is parsed. All functions, unless noted otherwise, return an array of IfAddr structs making it possible to `sort`, `filter`, `limit`, seek (via the `offset` function), or `unique` the list. To extract useful string information, the `attr` and `join` functions return a single string value. See below for details. Important note: see the https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr utility for more examples and for a CLI utility to experiment with the template syntax. `GetAllInterfaces` - Returns an exhaustive set of IfAddr structs available on the host. `GetAllInterfaces` is the initial input and accessible as the initial "dot" in the pipeline. Example: {{ GetAllInterfaces }} `GetDefaultInterfaces` - Returns one IfAddr for every IP that is on the interface containing the default route for the host. Example: {{ GetDefaultInterfaces }} `GetPrivateInterfaces` - Returns one IfAddr for every forwardable IP address that is included in RFC 6890 and whose interface is marked as up. NOTE: RFC 6890 is a more exhaustive version of RFC1918 because it spans IPv4 and IPv6, however, RFC6890 does permit the inclusion of likely undesired addresses such as multicast, therefore our version of "private" also filters out non-forwardable addresses. Example: {{ GetPrivateInterfaces | sort "default" | join "address" " " }} `GetPublicInterfaces` - Returns a list of IfAddr structs whos IPs are forwardable, do not match RFC 6890, and whose interface is marked up. Example: {{ GetPublicInterfaces | sort "default" | join "name" " " }} `GetPrivateIP` - Helper function that returns a string of the first IP address from GetPrivateInterfaces. Example: {{ GetPrivateIP }} `GetPrivateIPs` - Helper function that returns a string of the all private IP addresses on the host. Example: {{ GetPrivateIPs }} `GetPublicIP` - Helper function that returns a string of the first IP from GetPublicInterfaces. Example: {{ GetPublicIP }} `GetPublicIPs` - Helper function that returns a space-delimited string of the all public IP addresses on the host. Example: {{ GetPrivateIPs }} `GetInterfaceIP` - Helper function that returns a string of the first IP from the named interface. Example: {{ GetInterfaceIP "en0" }} `GetInterfaceIPs` - Helper function that returns a space-delimited list of all IPs on a given interface. Example: {{ GetInterfaceIPs "en0" }} `sort` - Sorts the IfAddrs result based on its arguments. `sort` takes one argument, a list of ways to sort its IfAddrs argument. The list of sort criteria is comma separated (`,`): - `address`, `+address`: Ascending sort of IfAddrs by Address - `-address`: Descending sort of IfAddrs by Address - `default`, `+default`: Ascending sort of IfAddrs, IfAddr with a default route first - `-default`: Descending sort of IfAddrs, IfAttr with default route last - `name`, `+name`: Ascending sort of IfAddrs by lexical ordering of interface name - `-name`: Descending sort of IfAddrs by lexical ordering of interface name - `port`, `+port`: Ascending sort of IfAddrs by port number - `-port`: Descending sort of IfAddrs by port number - `private`, `+private`: Ascending sort of IfAddrs with private addresses first - `-private`: Descending sort IfAddrs with private addresses last - `size`, `+size`: Ascending sort of IfAddrs by their network size as determined by their netmask (larger networks first) - `-size`: Descending sort of IfAddrs by their network size as determined by their netmask (smaller networks first) - `type`, `+type`: Ascending sort of IfAddrs by the type of the IfAddr (Unix, IPv4, then IPv6) - `-type`: Descending sort of IfAddrs by the type of the IfAddr (IPv6, IPv4, Unix) Example: {{ GetPrivateInterfaces | sort "default,-type,size,+address" }} `exclude` and `include`: Filters IfAddrs based on the selector criteria and its arguments. Both `exclude` and `include` take two arguments. The list of available filtering criteria is: - "address": Filter IfAddrs based on a regexp matching the string representation of the address - "flag","flags": Filter IfAddrs based on the list of flags specified. Multiple flags can be passed together using the pipe character (`|`) to create an inclusive bitmask of flags. The list of flags is included below. - "name": Filter IfAddrs based on a regexp matching the interface name. - "network": Filter IfAddrs based on whether a netowkr is included in a given CIDR. More than one CIDR can be passed in if each network is separated by the pipe character (`|`). - "port": Filter IfAddrs based on an exact match of the port number (number must be expressed as a string) - "rfc", "rfcs": Filter IfAddrs based on the matching RFC. If more than one RFC is specified, the list of RFCs can be joined together using the pipe character (`|`). - "size": Filter IfAddrs based on the exact match of the mask size. - "type": Filter IfAddrs based on their SockAddr type. Multiple types can be specified together by using the pipe character (`|`). Valid types include: `ip`, `ipv4`, `ipv6`, and `unix`. Example: {{ GetPrivateInterfaces | exclude "type" "IPv6" }} `unique`: Removes duplicate entries from the IfAddrs list, assuming the list has already been sorted. `unique` only takes one argument: - "address": Removes duplicates with the same address - "name": Removes duplicates with the same interface names Example: {{ GetAllInterfaces | sort "default,-type,address" | unique "name" }} `limit`: Reduces the size of the list to the specified value. Example: {{ GetPrivateInterfaces | limit 1 }} `offset`: Seeks into the list by the specified value. A negative value can be used to seek from the end of the list. Example: {{ GetPrivateInterfaces | offset "-2" | limit 1 }} `math`: Perform a "math" operation on each member of the list and return new values. `math` takes two arguments, the attribute to operate on and the operation's value. Supported operations include: - `address`: Adds the value, a positive or negative value expressed as a decimal string, to the address. The sign is required. This value is allowed to over or underflow networks (e.g. 127.255.255.255 `"address" "+1"` will return "128.0.0.0"). Addresses will wrap at IPv4 or IPv6 boundaries. - `network`: Add the value, a positive or negative value expressed as a decimal string, to the network address. The sign is required. Positive values are added to the network address. Negative values are subtracted from the network's broadcast address (e.g. 127.0.0.1 `"network" "-1"` will return "127.255.255.255"). Values that overflow the network size will safely wrap. Example: {{ GetPrivateInterfaces | include "type" "IP" | math "address" "+256" | attr "address" }} {{ GetPrivateInterfaces | include "type" "IP" | math "address" "-256" | attr "address" }} {{ GetPrivateInterfaces | include "type" "IP" | math "network" "+2" | attr "address" }} {{ GetPrivateInterfaces | include "type" "IP" | math "network" "-2" | attr "address" }} {{ GetPrivateInterfaces | include "flags" "forwardable|up" | include "type" "IPv4" | math "network" "+2" | attr "address" }} `attr`: Extracts a single attribute of the first member of the list and returns it as a string. `attr` takes a single attribute name. The list of available attributes is type-specific and shared between `join`. See below for a list of supported attributes. Example: {{ GetAllInterfaces | exclude "flags" "up" | attr "address" }} `Attr`: Extracts a single attribute from an `IfAttr` and in every other way performs the same as the `attr`. Example: {{ with $ifAddrs := GetAllInterfaces | include "type" "IP" | sort "+type,+address" -}} {{- range $ifAddrs -}} {{- Attr "address" . }} -- {{ Attr "network" . }}/{{ Attr "size" . -}} {{- end -}} {{- end }} `join`: Similar to `attr`, `join` extracts all matching attributes of the list and returns them as a string joined by the separator, the second argument to `join`. The list of available attributes is type-specific and shared between `join`. Example: {{ GetAllInterfaces | include "flags" "forwardable" | join "address" " " }} `exclude` and `include` flags: - `broadcast` - `down`: Is the interface down? - `forwardable`: Is the IP forwardable? - `global unicast` - `interface-local multicast` - `link-local multicast` - `link-local unicast` - `loopback` - `multicast` - `point-to-point` - `unspecified`: Is the IfAddr the IPv6 unspecified address? - `up`: Is the interface up? Attributes for `attr`, `Attr`, and `join`: SockAddr Type: - `string` - `type` IPAddr Type: - `address` - `binary` - `first_usable` - `hex` - `host` - `last_usable` - `mask_bits` - `netmask` - `network` - `octets`: Decimal values per byte - `port` - `size`: Number of hosts in the network IPv4Addr Type: - `broadcast` - `uint32`: unsigned integer representation of the value IPv6Addr Type: - `uint128`: unsigned integer representation of the value UnixSock Type: - `path` */ package template golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/template/template.go000066400000000000000000000123121316221320600272710ustar00rootroot00000000000000package template import ( "bytes" "fmt" "text/template" "github.com/hashicorp/errwrap" sockaddr "github.com/hashicorp/go-sockaddr" ) var ( // SourceFuncs is a map of all top-level functions that generate // sockaddr data types. SourceFuncs template.FuncMap // SortFuncs is a map of all functions used in sorting SortFuncs template.FuncMap // FilterFuncs is a map of all functions used in sorting FilterFuncs template.FuncMap // HelperFuncs is a map of all functions used in sorting HelperFuncs template.FuncMap ) func init() { SourceFuncs = template.FuncMap{ // GetAllInterfaces - Returns an exhaustive set of IfAddr // structs available on the host. `GetAllInterfaces` is the // initial input and accessible as the initial "dot" in the // pipeline. "GetAllInterfaces": sockaddr.GetAllInterfaces, // GetDefaultInterfaces - Returns one IfAddr for every IP that // is on the interface containing the default route for the // host. "GetDefaultInterfaces": sockaddr.GetDefaultInterfaces, // GetPrivateInterfaces - Returns one IfAddr for every IP that // matches RFC 6890, are attached to the interface with the // default route, and are forwardable IP addresses. NOTE: RFC // 6890 is a more exhaustive version of RFC1918 because it spans // IPv4 and IPv6, however it doespermit the inclusion of likely // undesired addresses such as multicast, therefore our // definition of a "private" address also excludes // non-forwardable IP addresses (as defined by the IETF). "GetPrivateInterfaces": sockaddr.GetPrivateInterfaces, // GetPublicInterfaces - Returns a list of IfAddr that do not // match RFC 6890, are attached to the default route, and are // forwardable. "GetPublicInterfaces": sockaddr.GetPublicInterfaces, } SortFuncs = template.FuncMap{ "sort": sockaddr.SortIfBy, } FilterFuncs = template.FuncMap{ "exclude": sockaddr.ExcludeIfs, "include": sockaddr.IncludeIfs, } HelperFuncs = template.FuncMap{ // Misc functions that operate on IfAddrs inputs "attr": Attr, "join": sockaddr.JoinIfAddrs, "limit": sockaddr.LimitIfAddrs, "offset": sockaddr.OffsetIfAddrs, "unique": sockaddr.UniqueIfAddrsBy, // Misc math functions that operate on a single IfAddr input "math": sockaddr.IfAddrsMath, // Return a Private RFC 6890 IP address string that is attached // to the default route and a forwardable address. "GetPrivateIP": sockaddr.GetPrivateIP, // Return all Private RFC 6890 IP addresses as a space-delimited string of // IP addresses. Addresses returned do not have to be on the interface with // a default route. "GetPrivateIPs": sockaddr.GetPrivateIPs, // Return a Public RFC 6890 IP address string that is attached // to the default route and a forwardable address. "GetPublicIP": sockaddr.GetPublicIP, // Return allPublic RFC 6890 IP addresses as a space-delimited string of IP // addresses. Addresses returned do not have to be on the interface with a // default route. "GetPublicIPs": sockaddr.GetPublicIPs, // Return the first IP address of the named interface, sorted by // the largest network size. "GetInterfaceIP": sockaddr.GetInterfaceIP, // Return all IP addresses on the named interface, sorted by the largest // network size. "GetInterfaceIPs": sockaddr.GetInterfaceIPs, } } // Attr returns the attribute from the ifAddrRaw argument. If the argument is // an IfAddrs, only the first element will be evaluated for resolution. func Attr(selectorName string, ifAddrsRaw interface{}) (string, error) { switch v := ifAddrsRaw.(type) { case sockaddr.IfAddr: return sockaddr.IfAttr(selectorName, v) case sockaddr.IfAddrs: return sockaddr.IfAttrs(selectorName, v) default: return "", fmt.Errorf("unable to obtain attribute %s from type %T (%v)", selectorName, ifAddrsRaw, ifAddrsRaw) } } // Parse parses input as template input using the addresses available on the // host, then returns the string output if there are no errors. func Parse(input string) (string, error) { addrs, err := sockaddr.GetAllInterfaces() if err != nil { return "", errwrap.Wrapf("unable to query interface addresses: {{err}}", err) } return ParseIfAddrs(input, addrs) } // ParseIfAddrs parses input as template input using the IfAddrs inputs, then // returns the string output if there are no errors. func ParseIfAddrs(input string, ifAddrs sockaddr.IfAddrs) (string, error) { return ParseIfAddrsTemplate(input, ifAddrs, template.New("sockaddr.Parse")) } // ParseIfAddrsTemplate parses input as template input using the IfAddrs inputs, // then returns the string output if there are no errors. func ParseIfAddrsTemplate(input string, ifAddrs sockaddr.IfAddrs, tmplIn *template.Template) (string, error) { // Create a template, add the function map, and parse the text. tmpl, err := tmplIn.Option("missingkey=error"). Funcs(SourceFuncs). Funcs(SortFuncs). Funcs(FilterFuncs). Funcs(HelperFuncs). Parse(input) if err != nil { return "", errwrap.Wrapf(fmt.Sprintf("unable to parse template %+q: {{err}}", input), err) } var outWriter bytes.Buffer err = tmpl.Execute(&outWriter, ifAddrs) if err != nil { return "", errwrap.Wrapf(fmt.Sprintf("unable to execute sockaddr input %+q: {{err}}", input), err) } return outWriter.String(), nil } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/template/template_test.go000066400000000000000000000253751316221320600303450ustar00rootroot00000000000000package template_test import ( "testing" socktmpl "github.com/hashicorp/go-sockaddr/template" ) func TestSockAddr_Parse(t *testing.T) { tests := []struct { name string input string output string fail bool requireOnline bool }{ { name: `basic include "name"`, input: `{{GetAllInterfaces | include "name" "lo0" | printf "%v"}}`, output: `[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast} ::1 {1 16384 lo0 up|loopback|multicast} fe80::1/64 {1 16384 lo0 up|loopback|multicast}]`, }, { name: "invalid input", input: `{{`, output: ``, fail: true, }, { name: "GetDefaultInterface", input: `{{GetDefaultInterfaces | include "type" "IPv4" | attr "name" }}`, output: `en0`, }, { name: `include "name" regexp`, input: `{{GetAllInterfaces | include "name" "^(en|lo)0$" | exclude "name" "^en0$" | sort "type" | sort "address" | join "address" " " }}`, output: `127.0.0.1 ::1 fe80::1`, }, { name: `exclude "name"`, input: `{{. | include "name" "^(en|lo)0$" | exclude "name" "^en0$" | sort "type" | sort "address" | join "address" " " }}`, output: `127.0.0.1 ::1 fe80::1`, }, { name: `"dot" pipeline, IPv4 type`, input: `{{. | include "type" "IPv4" | include "name" "^lo0$" | sort "type" | sort "address" }}`, output: `[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast}]`, }, { name: `include "type" "IPv6`, input: `{{. | include "type" "IPv6" | include "name" "^lo0$" | sort "address" }}`, output: `[::1 {1 16384 lo0 up|loopback|multicast} fe80::1/64 {1 16384 lo0 up|loopback|multicast}]`, }, { name: "better example for IP types", input: `{{. | include "type" "IPv4|IPv6" | include "name" "^lo0$" | sort "type" | sort "address" }}`, output: `[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast} ::1 {1 16384 lo0 up|loopback|multicast} fe80::1/64 {1 16384 lo0 up|loopback|multicast}]`, }, { name: "ifAddrs1", input: `{{. | include "type" "IPv4" | include "name" "^lo0$"}}`, output: `[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast}]`, }, { name: "ifAddrs2", input: `{{. | include "type" "IP" | include "name" "^lo0$" | sort "type" | sort "address" }}`, output: `[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast} ::1 {1 16384 lo0 up|loopback|multicast} fe80::1/64 {1 16384 lo0 up|loopback|multicast}]`, }, { name: `range "dot" example`, input: `{{range . | include "type" "IP" | include "name" "^lo0$"}}{{.Name}} {{.SockAddr}} {{end}}`, output: `lo0 127.0.0.1/8 lo0 ::1 lo0 fe80::1/64 `, }, { name: `exclude "type"`, input: `{{. | exclude "type" "IPv4" | include "name" "^lo0$" | sort "address" | unique "name" | join "name" " "}} {{range . | exclude "type" "IPv4" | include "name" "^lo0$"}}{{.SockAddr}} {{end}}`, output: `lo0 ::1 fe80::1/64 `, }, { name: "with variable pipeline", input: `{{with $ifSet := include "type" "IPv4" . | include "name" "^lo0$"}}{{range $ifSet }}{{.Name}} {{end}}{{range $ifSet}}{{.SockAddr}} {{end}}{{end}}`, output: `lo0 127.0.0.1/8 `, }, { name: "range sample on lo0", input: `{{with $ifAddrs := . | exclude "rfc" "1918" | include "name" "lo0" | sort "type,address" }}{{range $ifAddrs }}{{.Name}}/{{.SockAddr.NetIP}} {{end}}{{end}}`, output: `lo0/127.0.0.1 lo0/::1 lo0/fe80::1 `, }, { name: "non-RFC1918 on on lo0", input: `{{. | exclude "rfc" "1918" | include "name" "lo0" | sort "type,address" | len | eq 3}}`, output: `true`, }, { // NOTE(sean@): Difficult to reliably test includeByRFC. // In this case, we ass-u-me that the host running the // test has at least one RFC1918 address on their host. name: `include "rfc"`, input: `{{(. | include "rfc" "1918" | attr "name")}}`, output: `en0`, requireOnline: true, }, { name: "test for non-empty array", input: `{{. | include "type" "IPv4" | include "rfc" "1918" | print | len | eq (len "[]")}}`, output: `false`, }, { // NOTE(sean@): This will fail if there is a public IPv4 address on loopback. name: "non-IPv4 RFC1918", input: `{{. | include "name" "lo0" | exclude "type" "IPv4" | include "rfc" "1918" | len | eq 0}}`, output: `true`, }, { // NOTE(sean@): There are no RFC6598 addresses on most testing hosts so this should be empty. name: "rfc6598", input: `{{. | include "type" "IPv4" | include "rfc" "6598" | print | len | eq (len "[]")}}`, output: `true`, }, { name: "invalid RFC", input: `{{. | include "type" "IPv4" | include "rfc" "99999999999" | print | len | eq (len "[]")}}`, output: `true`, fail: true, }, { name: `sort asc address`, input: `{{ . | include "name" "lo0" | sort "type,address" | join "address" " " }}`, output: `127.0.0.1 ::1 fe80::1`, }, { name: `sort asc address old`, input: `{{with $ifSet := include "name" "lo0" . }}{{ range include "type" "IPv4" $ifSet | sort "address"}}{{ .SockAddr }} {{end}}{{ range include "type" "IPv6" $ifSet | sort "address"}}{{ .SockAddr }} {{end}}{{end}}`, output: `127.0.0.1/8 ::1 fe80::1/64 `, }, { name: `sort desc address`, input: `{{ . | include "name" "lo0" | sort "type,-address" | join "address" " " }}`, output: `127.0.0.1 fe80::1 ::1`, }, { name: `sort desc address`, input: `{{ . | include "name" "lo0" | include "type" "IPv6" | sort "type,-address" | join "address" " " }}`, output: `fe80::1 ::1`, }, { name: `sort asc address`, input: `{{with $ifSet := include "name" "lo0" . }}{{ range include "type" "IPv6" $ifSet | sort "address"}}{{ .SockAddr }} {{end}}{{end}}`, output: `::1 fe80::1/64 `, }, { name: "lo0 limit 1", input: `{{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | limit 1 | len}}`, output: `1`, }, { name: "join address", input: `{{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | join "address" " " }}`, output: `::1 fe80::1`, }, { name: "join name", input: `{{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | join "name" " " }}`, output: `lo0 lo0`, }, { name: "lo0 flags up and limit 1", input: `{{. | include "name" "lo0" | include "flag" "up" | sort "-type,+address" | attr "address" }}`, output: `::1`, }, { // NOTE(sean@): This is the HashiCorp default in 2016. // Indented for effect. Using "true" as the output // instead of printing the correct $rfc*Addrs values. name: "HashiCorpDefault2016", input: ` {{- with $addr := GetAllInterfaces | include "type" "IP" | include "rfc" "1918|6598" | sort "address" | attr "address" -}} {{- if ($addr | len) gt 0 -}} {{- print "true" -}}{{/* print $addr*/ -}} {{- end -}} {{- end -}}`, output: `true`, }, { name: "math address +", input: `{{GetAllInterfaces | include "name" "^lo0$" | include "type" "IP" | math "address" "+2" | sort "+type,+address" | join "address" " " }}`, output: `127.0.0.3 ::3 fe80::3`, }, { name: "math address + overflow", input: `|{{- with $ifAddrs := GetAllInterfaces | include "name" "^lo0$" | include "type" "IP" | math "address" "+16777217" | sort "+type,+address" -}} {{- range $ifAddrs -}} {{- attr "address" . }} -- {{ attr "network" . }}/{{ attr "size" . }}|{{ end -}} {{- end -}}`, output: `|128.0.0.2 -- 128.0.0.0/16777216|::100:2 -- ::100:2/1|fe80::100:2 -- fe80::/18446744073709551616|`, }, { name: "math address + overflow+wrap", input: `{{GetAllInterfaces | include "name" "^lo0$" | include "type" "IP" | math "address" "+4294967294" | sort "+type,+address" | join "address" " " }}`, output: `126.255.255.255 ::ffff:ffff fe80::ffff:ffff`, }, { name: "math address -", input: `{{GetAllInterfaces | include "name" "^lo0$" | include "type" "IP" | math "address" "-256" | sort "+type,+address" | join "address" " " }}`, output: `126.255.255.1 fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ff01 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff01`, }, { name: "math address - underflow", input: `{{GetAllInterfaces | include "name" "^lo0$" | include "type" "IP" | math "address" "-4278190082" | sort "+type,+address" | join "address" " " }}`, output: `127.255.255.255 fe7f:ffff:ffff:ffff:ffff:ffff:ff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ff:ffff`, }, { // Note to readers: lo0's link-local address (::1) address has a mask of // /128 which means its value never changes and this is expected. lo0's // site-local address has a /64 address and is expected to change. name: "math network", input: `{{GetAllInterfaces | include "name" "^lo0$" | include "type" "IP" | math "network" "+2" | sort "+type,+address" | join "address" " " }}`, output: `127.0.0.2 ::1 fe80::2`, }, { // Assume an IPv4 input of 127.0.0.1. With a value of 0xff00ff01, we wrap once on purpose. name: "math network + wrap", input: `{{GetAllInterfaces | include "name" "^lo0$" | include "type" "IP" | math "network" "+4278255368" | sort "+type,+address" | join "address" " " }}`, output: `127.0.255.8 ::1 fe80::ff00:ff08`, }, { name: "math network -", input: `{{GetAllInterfaces | include "name" "^lo0$" | include "type" "IP" | math "network" "-2" | sort "+type,+address" | join "address" " " }}`, output: `127.255.255.254 ::1 fe80::ffff:ffff:ffff:fffe`, }, { // Assume an IPv4 input of 127.0.0.1. With a value of 0xff000008 it // should wrap and underflow by 8. Assume an IPv6 input of ::1. With a // value of -0xff000008 the value underflows and wraps. name: "math network - underflow+wrap", input: `{{GetAllInterfaces | include "name" "^lo0$" | include "type" "IP" | sort "+type,+address" | math "network" "-4278190088" | join "address" " " }}`, output: `127.255.255.248 ::1 fe80::ffff:ffff:ff:fff8`, }, { // Assume the private IPs available on the host are: 10.1.2.3 // fe80::1025:f732:1001:203 name: "GetPrivateIPs", input: `{{GetPrivateIPs}}`, output: `10.1.2.3 fe80::1025:f732:1001:203`, }, { // Assume the public IPs available on the host are: 1.2.3.4 6.7.8.9 name: "GetPublicIPs", input: `{{GetPublicIPs}}`, output: `1.2.3.4 6.7.8.9`, }, { // Assume the private IPs on this host are just the IPv4 addresses: // 10.1.2.3 and 172.16.4.6 name: "GetInterfaceIPs", input: `{{GetInterfaceIPs "en0"}}`, output: `10.1.2.3 and 172.16.4.6`, }, } for i, test := range tests { test := test // capture range variable if test.name == "" { t.Fatalf("test number %d has an empty test name", i) } t.Run(test.name, func(t *testing.T) { t.Parallel() out, err := socktmpl.Parse(test.input) if err != nil && !test.fail { t.Fatalf("%q: bad: %v", test.name, err) } if out != test.output && !test.fail { t.Fatalf("%q: Expected %+q, received %+q\n%+q", test.name, test.output, out, test.input) } }) } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/unixsock.go000066400000000000000000000064601316221320600255150ustar00rootroot00000000000000package sockaddr import ( "fmt" "strings" ) type UnixSock struct { SockAddr path string } type UnixSocks []*UnixSock // unixAttrMap is a map of the UnixSockAddr type-specific attributes. var unixAttrMap map[AttrName]func(UnixSock) string var unixAttrs []AttrName func init() { unixAttrInit() } // NewUnixSock creates an UnixSock from a string path. String can be in the // form of either URI-based string (e.g. `file:///etc/passwd`), an absolute // path (e.g. `/etc/passwd`), or a relative path (e.g. `./foo`). func NewUnixSock(s string) (ret UnixSock, err error) { ret.path = s return ret, nil } // CmpAddress follows the Cmp() standard protocol and returns: // // - -1 If the receiver should sort first because its name lexically sorts before arg // - 0 if the SockAddr arg is not a UnixSock, or is a UnixSock with the same path. // - 1 If the argument should sort first. func (us UnixSock) CmpAddress(sa SockAddr) int { usb, ok := sa.(UnixSock) if !ok { return sortDeferDecision } return strings.Compare(us.Path(), usb.Path()) } // DialPacketArgs returns the arguments required to be passed to net.DialUnix() // with the `unixgram` network type. func (us UnixSock) DialPacketArgs() (network, dialArgs string) { return "unixgram", us.path } // DialStreamArgs returns the arguments required to be passed to net.DialUnix() // with the `unix` network type. func (us UnixSock) DialStreamArgs() (network, dialArgs string) { return "unix", us.path } // Equal returns true if a SockAddr is equal to the receiving UnixSock. func (us UnixSock) Equal(sa SockAddr) bool { usb, ok := sa.(UnixSock) if !ok { return false } if us.Path() != usb.Path() { return false } return true } // ListenPacketArgs returns the arguments required to be passed to // net.ListenUnixgram() with the `unixgram` network type. func (us UnixSock) ListenPacketArgs() (network, dialArgs string) { return "unixgram", us.path } // ListenStreamArgs returns the arguments required to be passed to // net.ListenUnix() with the `unix` network type. func (us UnixSock) ListenStreamArgs() (network, dialArgs string) { return "unix", us.path } // MustUnixSock is a helper method that must return an UnixSock or panic on // invalid input. func MustUnixSock(addr string) UnixSock { us, err := NewUnixSock(addr) if err != nil { panic(fmt.Sprintf("Unable to create a UnixSock from %+q: %v", addr, err)) } return us } // Path returns the given path of the UnixSock func (us UnixSock) Path() string { return us.path } // String returns the path of the UnixSock func (us UnixSock) String() string { return fmt.Sprintf("%+q", us.path) } // Type is used as a type switch and returns TypeUnix func (UnixSock) Type() SockAddrType { return TypeUnix } // UnixSockAttrs returns a list of attributes supported by the UnixSockAddr type func UnixSockAttrs() []AttrName { return unixAttrs } // UnixSockAttr returns a string representation of an attribute for the given // UnixSock. func UnixSockAttr(us UnixSock, attrName AttrName) string { fn, found := unixAttrMap[attrName] if !found { return "" } return fn(us) } // unixAttrInit is called once at init() func unixAttrInit() { // Sorted for human readability unixAttrs = []AttrName{ "path", } unixAttrMap = map[AttrName]func(us UnixSock) string{ "path": func(us UnixSock) string { return us.Path() }, } } golang-github-hashicorp-go-sockaddr-0.0~git20170627.41949a1+ds/unixsock_test.go000066400000000000000000000051231316221320600265470ustar00rootroot00000000000000package sockaddr_test import ( "testing" sockaddr "github.com/hashicorp/go-sockaddr" ) func TestUnixSock_impl_SockAddr(t *testing.T) { tests := []struct { name string input sockaddr.UnixSock dialPacketArgs []string dialStreamArgs []string listenPacketArgs []string listenStreamArgs []string }{ { name: "simple", input: sockaddr.MustUnixSock("/tmp/foo"), dialPacketArgs: []string{"unixgram", "/tmp/foo"}, dialStreamArgs: []string{"unixgram", "/tmp/foo"}, listenPacketArgs: []string{"unixgram", "/tmp/foo"}, listenStreamArgs: []string{"unixgram", "/tmp/foo"}, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d needs a name", i) } arg1, arg2 := test.input.DialPacketArgs() if arg1 != test.dialPacketArgs[0] && arg2 != test.dialPacketArgs[1] { t.Fatalf("%s: %q %q", test.name, arg1, arg2) } arg1, arg2 = test.input.DialStreamArgs() if arg1 != test.dialStreamArgs[0] && arg2 != test.dialStreamArgs[1] { t.Fatalf("%s: %q %q", test.name, arg1, arg2) } arg1, arg2 = test.input.ListenPacketArgs() if arg1 != test.listenPacketArgs[0] && arg2 != test.listenPacketArgs[1] { t.Fatalf("%s: %q %q", test.name, arg1, arg2) } arg1, arg2 = test.input.ListenStreamArgs() if arg1 != test.listenStreamArgs[0] && arg2 != test.listenStreamArgs[1] { t.Fatalf("%s: %q %q", test.name, arg1, arg2) } } } func TestUnixSock_Equal(t *testing.T) { tests := []struct { name string input sockaddr.UnixSock sa sockaddr.SockAddr equal bool }{ { name: "equal", input: sockaddr.MustUnixSock("/tmp/foo"), sa: sockaddr.MustUnixSock("/tmp/foo"), equal: true, }, { name: "not equal", input: sockaddr.MustUnixSock("/tmp/foo"), sa: sockaddr.MustUnixSock("/tmp/bar"), equal: false, }, { name: "ipv4", input: sockaddr.MustUnixSock("/tmp/foo"), sa: sockaddr.MustIPv4Addr("1.2.3.4"), equal: false, }, { name: "ipv6", input: sockaddr.MustUnixSock("/tmp/foo"), sa: sockaddr.MustIPv6Addr("::1"), equal: false, }, } for i, test := range tests { if test.name == "" { t.Fatalf("test %d needs a name", i) } t.Run(test.name, func(t *testing.T) { us := test.input if ret := us.Equal(test.sa); ret != test.equal { t.Fatalf("%s: equal: %v %q %q", test.name, ret, us, test.sa) } }) } } func TestUnixSockAttrs(t *testing.T) { const expectedNumAttrs = 1 usa := sockaddr.UnixSockAttrs() if len(usa) != expectedNumAttrs { t.Fatalf("wrong number of UnixSockAttrs: %d vs %d", len(usa), expectedNumAttrs) } }