Import github.com/hashicorp/go-sockaddr
This commit is contained in:
parent
26a26a489d
commit
4104e9e53a
|
@ -0,0 +1,373 @@
|
||||||
|
Mozilla 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.
|
|
@ -0,0 +1,52 @@
|
||||||
|
TOOLS= golang.org/x/tools/cover
|
||||||
|
GOCOVER_TMPFILE?= $(GOCOVER_FILE).tmp
|
||||||
|
GOCOVER_FILE?= .cover.out
|
||||||
|
GOCOVERHTML?= coverage.html
|
||||||
|
|
||||||
|
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:6060/pkg/github.com/hashicorp/go-sockaddr/
|
||||||
|
godoc -http=:6060 -goroot $GOROOT
|
|
@ -0,0 +1,126 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
// 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` equivilant 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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` equivilant 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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` equivilant of:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// $ sockaddr eval -r '{{GetAllInterfaces | include "name" <<ARG>> | 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,935 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, 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) {
|
||||||
|
defaultIfName, err := getDefaultIfName()
|
||||||
|
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` equivilant of:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | include "RFC" "6890" }}'
|
||||||
|
/// ```
|
||||||
|
func GetPrivateInterfaces() (IfAddrs, error) {
|
||||||
|
privateIfs, err := GetDefaultInterfaces()
|
||||||
|
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|up", privateIfs)
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, err
|
||||||
|
}
|
||||||
|
if len(privateIfs) == 0 {
|
||||||
|
return IfAddrs{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderedIfAddrBy(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` equivilant of:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// $ sockaddr eval -r '{{GetDefaultInterfaces | include "type" "ip" | include "flags" "forwardable|up" | sort "type,size" | exclude "RFC" "6890" }}'
|
||||||
|
/// ```
|
||||||
|
func GetPublicInterfaces() (IfAddrs, error) {
|
||||||
|
publicIfs, err := GetDefaultInterfaces()
|
||||||
|
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|up", publicIfs)
|
||||||
|
if err != nil {
|
||||||
|
return IfAddrs{}, err
|
||||||
|
}
|
||||||
|
if len(publicIfs) == 0 {
|
||||||
|
return IfAddrs{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderedIfAddrBy(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 {
|
||||||
|
if ifType != "ip" && ifType != "ipv4" && ifType != "ipv6" && ifType != "unix" {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 "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 "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 "+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 : len(in)], nil
|
||||||
|
}
|
||||||
|
return in[off:len(in)], 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 := regexp.MustCompile(`[\s]+`)
|
||||||
|
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 := regexp.MustCompile(`[\s]+`)
|
||||||
|
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 := regexp.MustCompile(`^Ethernet adapter ([^\s:]+):`)
|
||||||
|
ipAddrRE := regexp.MustCompile(`^ IPv[46] Address\. \. \. \. \. \. \. \. \. \. \. : ([^\s]+)`)
|
||||||
|
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")
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// +build darwin dragonfly freebsd netbsd openbsd
|
||||||
|
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultBSDIfNameCmd is the comamnd to run on BSDs to get the default
|
||||||
|
// interface
|
||||||
|
func defaultBSDIfNameCmd() []string {
|
||||||
|
return []string{"/sbin/route", "-n", "get", "default"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDefaultIfName is a *BSD-specific function for extracting the name of the
|
||||||
|
// interface from route(8).
|
||||||
|
func getDefaultIfName() (string, error) {
|
||||||
|
var cmd []string = defaultBSDIfNameCmd()
|
||||||
|
out, err := exec.Command(cmd[0], cmd[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
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
// +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)")
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultLinuxIfNameCmd is the comamnd to run on Linux to get the default
|
||||||
|
// interface.
|
||||||
|
func defaultLinuxIfNameCmd() []string {
|
||||||
|
return []string{"/sbin/ip", "route"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDefaultIfName is a Linux-specific function for extracting the name of the
|
||||||
|
// interface from ip(8).
|
||||||
|
func getDefaultIfName() (string, error) {
|
||||||
|
var cmd []string = defaultLinuxIfNameCmd()
|
||||||
|
out, err := exec.Command(cmd[0], cmd[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
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultSolarisIfNameCmd is the comamnd to run on Solaris to get the default
|
||||||
|
// interface
|
||||||
|
func defaultSolarisIfNameCmd() []string {
|
||||||
|
return []string{"/usr/sbin/route", "-n", "get", "default"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDefaultIfName is an Solaris-specific function for extracting the name of
|
||||||
|
// the interface from route(8).
|
||||||
|
func getDefaultIfName() (string, error) {
|
||||||
|
var cmd []string = defaultSolarisIfNameCmd()
|
||||||
|
out, err := exec.Command(cmd[0], cmd[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
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultWindowsIfNameCmd is the comamnd to run on Windows to get the default
|
||||||
|
// interface.
|
||||||
|
func defaultWindowsIfNameCmd() []string {
|
||||||
|
return []string{"netstat", "-rn"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// defaultWindowsIfNameCmd is the comamnd to run on Windows to get the default
|
||||||
|
// interface.
|
||||||
|
func defaultWindowsIPConfigCmd() []string {
|
||||||
|
return []string{"ipconfig"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDefaultIfName is a Windows-specific function for extracting the name of
|
||||||
|
// the interface from `netstat -rn` and `ipconfig`.
|
||||||
|
func getDefaultIfName() (string, error) {
|
||||||
|
ipAddr, err := getWindowsIPOnDefaultRoute()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWindowsIPOnDefaultRoute() (string, error) {
|
||||||
|
var cmd []string = defaultWindowsIfNameCmd()
|
||||||
|
out, err := exec.Command(cmd[0], cmd[1:]...).Output()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultIPAddr string
|
||||||
|
if defaultIPAddr, err = parseDefaultIfNameFromWindowsNetstatRN(string(out)); err != nil {
|
||||||
|
return "", errors.New("No IP on default route found")
|
||||||
|
}
|
||||||
|
return defaultIPAddr, nil
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package 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)
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"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 {
|
||||||
|
return ip.NetIPMask().String()
|
||||||
|
},
|
||||||
|
"network": func(ip IPAddr) string {
|
||||||
|
return ip.Network().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())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package 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()
|
||||||
|
}
|
|
@ -0,0 +1,505 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"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
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
// 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<<uint(IPv4len*8-ipv4.Maskbits()))
|
||||||
|
},
|
||||||
|
"uint32": func(ipv4 IPv4Addr) string {
|
||||||
|
return fmt.Sprintf("%d", uint32(ipv4.Address))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,591 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// IPv6Address is a named type representing an IPv6 address.
|
||||||
|
IPv6Address *big.Int
|
||||||
|
|
||||||
|
// IPv6Network is a named type representing an IPv6 network.
|
||||||
|
IPv6Network *big.Int
|
||||||
|
|
||||||
|
// IPv6Mask is a named type representing an IPv6 network mask.
|
||||||
|
IPv6Mask *big.Int
|
||||||
|
)
|
||||||
|
|
||||||
|
// IPv6HostPrefix is a constant represents a /128 IPv6 Prefix.
|
||||||
|
const IPv6HostPrefix = IPPrefixLen(128)
|
||||||
|
|
||||||
|
// ipv6HostMask is an unexported big.Int representing a /128 IPv6 address.
|
||||||
|
// This value must be a constant and always set to all ones.
|
||||||
|
var ipv6HostMask IPv6Mask
|
||||||
|
|
||||||
|
// ipv6AddrAttrMap is a map of the IPv6Addr type-specific attributes.
|
||||||
|
var ipv6AddrAttrMap map[AttrName]func(IPv6Addr) string
|
||||||
|
var ipv6AddrAttrs []AttrName
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
biMask := new(big.Int)
|
||||||
|
biMask.SetBytes([]byte{
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
0xff, 0xff,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
ipv6HostMask = IPv6Mask(biMask)
|
||||||
|
|
||||||
|
ipv6AddrInit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPv6Addr implements a convenience wrapper around the union of Go's
|
||||||
|
// built-in net.IP and net.IPNet types. In UNIX-speak, IPv6Addr implements
|
||||||
|
// `sockaddr` when the the address family is set to AF_INET6
|
||||||
|
// (i.e. `sockaddr_in6`).
|
||||||
|
type IPv6Addr struct {
|
||||||
|
IPAddr
|
||||||
|
Address IPv6Address
|
||||||
|
Mask IPv6Mask
|
||||||
|
Port IPPort
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIPv6Addr creates an IPv6Addr from a string. String can be in the form of
|
||||||
|
// an an IPv6:port (e.g. `[2001:4860:0:2001::68]:80`, in which case the mask is
|
||||||
|
// assumed to be a /128), an IPv6 address (e.g. `2001:4860:0:2001::68`, also
|
||||||
|
// with a `/128` mask), an IPv6 CIDR (e.g. `2001:4860:0:2001::68/64`, which has
|
||||||
|
// its IP port initialized to zero). ipv6Str can not be a hostname.
|
||||||
|
//
|
||||||
|
// NOTE: Many net.*() routines will initialize and return an IPv4 address.
|
||||||
|
// Always test to make sure the address returned cannot be converted to a 4 byte
|
||||||
|
// array using To4().
|
||||||
|
func NewIPv6Addr(ipv6Str string) (IPv6Addr, error) {
|
||||||
|
v6Addr := false
|
||||||
|
LOOP:
|
||||||
|
for i := 0; i < len(ipv6Str); i++ {
|
||||||
|
switch ipv6Str[i] {
|
||||||
|
case '.':
|
||||||
|
break LOOP
|
||||||
|
case ':':
|
||||||
|
v6Addr = true
|
||||||
|
break LOOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !v6Addr {
|
||||||
|
return IPv6Addr{}, fmt.Errorf("Unable to resolve %+q as an IPv6 address, appears to be an IPv4 address", ipv6Str)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to parse ipv6Str as a /128 host with a port number.
|
||||||
|
tcpAddr, err := net.ResolveTCPAddr("tcp6", ipv6Str)
|
||||||
|
if err == nil {
|
||||||
|
ipv6 := tcpAddr.IP.To16()
|
||||||
|
if ipv6 == nil {
|
||||||
|
return IPv6Addr{}, fmt.Errorf("Unable to resolve %+q as a 16byte IPv6 address", ipv6Str)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6BigIntAddr := new(big.Int)
|
||||||
|
ipv6BigIntAddr.SetBytes(ipv6)
|
||||||
|
|
||||||
|
ipv6BigIntMask := new(big.Int)
|
||||||
|
ipv6BigIntMask.Set(ipv6HostMask)
|
||||||
|
|
||||||
|
ipv6Addr := IPv6Addr{
|
||||||
|
Address: IPv6Address(ipv6BigIntAddr),
|
||||||
|
Mask: IPv6Mask(ipv6BigIntMask),
|
||||||
|
Port: IPPort(tcpAddr.Port),
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipv6Addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse as a naked IPv6 address. Trim square brackets if present.
|
||||||
|
if len(ipv6Str) > 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
|
||||||
|
}
|
|
@ -0,0 +1,947 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
// ForwardingBlacklist is a faux RFC that includes a list of non-forwardable IP
|
||||||
|
// blocks.
|
||||||
|
const ForwardingBlacklist = 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: SockAddrs{
|
||||||
|
// [RFC919] Broadcasting Internet Datagrams
|
||||||
|
MustIPv4Addr("255.255.255.255/32"), // [RFC1122], §7 Broadcast IP Addressing - Proposed Standards
|
||||||
|
},
|
||||||
|
1122: SockAddrs{
|
||||||
|
// [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: SockAddrs{
|
||||||
|
// [RFC1112] Host Extensions for IP Multicasting
|
||||||
|
MustIPv4Addr("224.0.0.0/4"), // [RFC1112], §4 Host Group Addresses
|
||||||
|
},
|
||||||
|
1918: SockAddrs{
|
||||||
|
// [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: SockAddrs{
|
||||||
|
// [RFC2544] Benchmarking Methodology for Network
|
||||||
|
// Interconnect Devices
|
||||||
|
MustIPv4Addr("198.18.0.0/15"),
|
||||||
|
},
|
||||||
|
2765: SockAddrs{
|
||||||
|
// [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: SockAddrs{
|
||||||
|
// [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: SockAddrs{ // 6to4 address
|
||||||
|
// [RFC3056] Connection of IPv6 Domains via IPv4 Clouds
|
||||||
|
|
||||||
|
// [RFC3056], §2 IPv6 Prefix Allocation
|
||||||
|
MustIPv6Addr("2002::/16"),
|
||||||
|
},
|
||||||
|
3068: SockAddrs{
|
||||||
|
// [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: SockAddrs{
|
||||||
|
// [RFC3171] IANA Guidelines for IPv4 Multicast Address Assignments
|
||||||
|
MustIPv4Addr("224.0.0.0/4"),
|
||||||
|
},
|
||||||
|
3330: SockAddrs{
|
||||||
|
// [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: SockAddrs{
|
||||||
|
// [RFC3849] IPv6 Address Prefix Reserved for Documentation
|
||||||
|
MustIPv6Addr("2001:db8::/32"), // [RFC3849], §4 IANA Considerations
|
||||||
|
},
|
||||||
|
3927: SockAddrs{
|
||||||
|
// [RFC3927] Dynamic Configuration of IPv4 Link-Local Addresses
|
||||||
|
MustIPv4Addr("169.254.0.0/16"), // [RFC3927], §2.1 Link-Local Address Selection
|
||||||
|
},
|
||||||
|
4038: SockAddrs{
|
||||||
|
// [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: SockAddrs{
|
||||||
|
// [RFC4193] Unique Local IPv6 Unicast Addresses
|
||||||
|
MustIPv6Addr("fc00::/7"),
|
||||||
|
},
|
||||||
|
4291: SockAddrs{
|
||||||
|
// [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: SockAddrs{
|
||||||
|
// [RFC4380] Teredo: Tunneling IPv6 over UDP through
|
||||||
|
// Network Address Translations (NATs)
|
||||||
|
|
||||||
|
// [RFC4380], §2.6 Global Teredo IPv6 Service Prefix
|
||||||
|
MustIPv6Addr("2001:0000::/32"),
|
||||||
|
},
|
||||||
|
4773: SockAddrs{
|
||||||
|
// [RFC4773] Administration of the IANA Special Purpose IPv6 Address Block
|
||||||
|
MustIPv6Addr("2001:0000::/23"), // IANA
|
||||||
|
},
|
||||||
|
4843: SockAddrs{
|
||||||
|
// [RFC4843] An IPv6 Prefix for Overlay Routable Cryptographic Hash Identifiers (ORCHID)
|
||||||
|
MustIPv6Addr("2001:10::/28"), // [RFC4843], §7 IANA Considerations
|
||||||
|
},
|
||||||
|
5180: SockAddrs{
|
||||||
|
// [RFC5180] IPv6 Benchmarking Methodology for Network Interconnect Devices
|
||||||
|
MustIPv6Addr("2001:0200::/48"), // [RFC5180], §8 IANA Considerations
|
||||||
|
},
|
||||||
|
5735: SockAddrs{
|
||||||
|
// [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: SockAddrs{
|
||||||
|
// [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: SockAddrs{
|
||||||
|
// [RFC6052] IPv6 Addressing of IPv4/IPv6 Translators
|
||||||
|
MustIPv6Addr("64:ff9b::/96"), // [RFC6052], §2.1. Well-Known Prefix
|
||||||
|
},
|
||||||
|
6333: SockAddrs{
|
||||||
|
// [RFC6333] Dual-Stack Lite Broadband Deployments Following IPv4 Exhaustion
|
||||||
|
MustIPv4Addr("192.0.0.0/29"), // [RFC6333], §5.7 Well-Known IPv4 Address
|
||||||
|
},
|
||||||
|
6598: SockAddrs{
|
||||||
|
// [RFC6598] IANA-Reserved IPv4 Prefix for Shared Address Space
|
||||||
|
MustIPv4Addr("100.64.0.0/10"),
|
||||||
|
},
|
||||||
|
6666: SockAddrs{
|
||||||
|
// [RFC6666] A Discard Prefix for IPv6
|
||||||
|
MustIPv6Addr("0100::/64"),
|
||||||
|
},
|
||||||
|
6890: SockAddrs{
|
||||||
|
// [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: SockAddrs{
|
||||||
|
// [RFC7335] IPv4 Service Continuity Prefix
|
||||||
|
MustIPv4Addr("192.0.0.0/29"), // [RFC7335], §6 IANA Considerations
|
||||||
|
},
|
||||||
|
ForwardingBlacklist: SockAddrs{ // 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: struct{}{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for rfcNum, sas := range rfcNetMap {
|
||||||
|
if _, found := rfcBlacklist[rfcNum]; !found {
|
||||||
|
fn(rfcNum, sas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
package sockaddr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
package 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
|
||||||
|
}
|
|
@ -0,0 +1,125 @@
|
||||||
|
package 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": sockaddr.IfAttr,
|
||||||
|
"join": sockaddr.JoinIfAddrs,
|
||||||
|
"limit": sockaddr.LimitIfAddrs,
|
||||||
|
"offset": sockaddr.OffsetIfAddrs,
|
||||||
|
"unique": sockaddr.UniqueIfAddrsBy,
|
||||||
|
|
||||||
|
// Return a Private RFC 6890 IP address string that is attached
|
||||||
|
// to the default route and a forwardable address.
|
||||||
|
"GetPrivateIP": sockaddr.GetPrivateIP,
|
||||||
|
|
||||||
|
// Return a Public RFC 6890 IP address string that is attached
|
||||||
|
// to the default route and a forwardable address.
|
||||||
|
"GetPublicIP": sockaddr.GetPublicIP,
|
||||||
|
|
||||||
|
// Return the first IP address of the named interface, sorted by
|
||||||
|
// the largest network size.
|
||||||
|
"GetInterfaceIP": sockaddr.GetInterfaceIP,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package 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()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -419,6 +419,18 @@
|
||||||
"revision": "6e85be8fee1dcaa02c0eaaac2df5a8fbecf94145",
|
"revision": "6e85be8fee1dcaa02c0eaaac2df5a8fbecf94145",
|
||||||
"revisionTime": "2016-09-30T03:51:02Z"
|
"revisionTime": "2016-09-30T03:51:02Z"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "AL5OQyjfTdgkO7XfMZ5HWTkVK9k=",
|
||||||
|
"path": "github.com/hashicorp/go-sockaddr",
|
||||||
|
"revision": "a99c57b5a5f48c51607e63089075fe7434fe219a",
|
||||||
|
"revisionTime": "2016-12-02T03:16:41Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "Y+c+ahUug8/vK+cWRLatV5aB4ps=",
|
||||||
|
"path": "github.com/hashicorp/go-sockaddr/template",
|
||||||
|
"revision": "a99c57b5a5f48c51607e63089075fe7434fe219a",
|
||||||
|
"revisionTime": "2016-12-02T03:16:41Z"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "xZ7Ban1x//6uUIU1xtrTbCYNHBc=",
|
"checksumSHA1": "xZ7Ban1x//6uUIU1xtrTbCYNHBc=",
|
||||||
"path": "github.com/hashicorp/go-syslog",
|
"path": "github.com/hashicorp/go-syslog",
|
||||||
|
|
Loading…
Reference in New Issue