pax_global_header00006660000000000000000000000064131232757330014520gustar00rootroot0000000000000052 comment=3ede32e2033de7505e6500d6c868c2b9ed9f169d go-connections-0.3.0/000077500000000000000000000000001312327573300144455ustar00rootroot00000000000000go-connections-0.3.0/CONTRIBUTING.md000066400000000000000000000040551312327573300167020ustar00rootroot00000000000000# Contributing to Docker ### Sign your work The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify the below (from [developercertificate.org](http://developercertificate.org/)): ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` Then you just add a line to every git commit message: Signed-off-by: Joe Smith Use your real name (sorry, no pseudonyms or anonymous contributions.) If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`. go-connections-0.3.0/LICENSE000066400000000000000000000250101312327573300154500ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2015 Docker, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. go-connections-0.3.0/MAINTAINERS000066400000000000000000000014231312327573300161420ustar00rootroot00000000000000# go-connections maintainers file # # This file describes who runs the docker/go-connections project and how. # This is a living document - if you see something out of date or missing, speak up! # # It is structured to be consumable by both humans and programs. # To extract its contents programmatically, use any TOML-compliant parser. # # This file is compiled into the MAINTAINERS file in docker/opensource. # [Org] [Org."Core maintainers"] people = [ "calavera", ] [people] # A reference list of all people associated with the project. # All other sections should refer to people by their canonical key # in the people section. # ADD YOURSELF HERE IN ALPHABETICAL ORDER [people.calavera] Name = "David Calavera" Email = "david.calavera@gmail.com" GitHub = "calavera" go-connections-0.3.0/README.md000066400000000000000000000007201312327573300157230ustar00rootroot00000000000000[![GoDoc](https://godoc.org/github.com/docker/go-connections?status.svg)](https://godoc.org/github.com/docker/go-connections) # Introduction go-connections provides common package to work with network connections. ## Usage See the [docs in godoc](https://godoc.org/github.com/docker/go-connections) for examples and documentation. ## License go-connections is licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for the full license text. go-connections-0.3.0/circle.yml000066400000000000000000000006171312327573300164350ustar00rootroot00000000000000dependencies: pre: # setup ipv6 - sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0 net.ipv6.conf.default.disable_ipv6=0 net.ipv6.conf.all.disable_ipv6=0 post: # install golint - go get github.com/golang/lint/golint test: pre: # run analysis before tests - go vet ./... - test -z "$(golint ./... | tee /dev/stderr)" - test -z "$(gofmt -s -l . | tee /dev/stderr)" go-connections-0.3.0/doc.go000066400000000000000000000002451312327573300155420ustar00rootroot00000000000000// Package connections provides libraries to work with network connections. // This library is divided in several components for specific usage. package connections go-connections-0.3.0/nat/000077500000000000000000000000001312327573300152275ustar00rootroot00000000000000go-connections-0.3.0/nat/nat.go000066400000000000000000000147121312327573300163450ustar00rootroot00000000000000// Package nat is a convenience package for manipulation of strings describing network ports. package nat import ( "fmt" "net" "strconv" "strings" ) const ( // portSpecTemplate is the expected format for port specifications portSpecTemplate = "ip:hostPort:containerPort" ) // PortBinding represents a binding between a Host IP address and a Host Port type PortBinding struct { // HostIP is the host IP Address HostIP string `json:"HostIp"` // HostPort is the host port number HostPort string } // PortMap is a collection of PortBinding indexed by Port type PortMap map[Port][]PortBinding // PortSet is a collection of structs indexed by Port type PortSet map[Port]struct{} // Port is a string containing port number and protocol in the format "80/tcp" type Port string // NewPort creates a new instance of a Port given a protocol and port number or port range func NewPort(proto, port string) (Port, error) { // Check for parsing issues on "port" now so we can avoid having // to check it later on. portStartInt, portEndInt, err := ParsePortRangeToInt(port) if err != nil { return "", err } if portStartInt == portEndInt { return Port(fmt.Sprintf("%d/%s", portStartInt, proto)), nil } return Port(fmt.Sprintf("%d-%d/%s", portStartInt, portEndInt, proto)), nil } // ParsePort parses the port number string and returns an int func ParsePort(rawPort string) (int, error) { if len(rawPort) == 0 { return 0, nil } port, err := strconv.ParseUint(rawPort, 10, 16) if err != nil { return 0, err } return int(port), nil } // ParsePortRangeToInt parses the port range string and returns start/end ints func ParsePortRangeToInt(rawPort string) (int, int, error) { if len(rawPort) == 0 { return 0, 0, nil } start, end, err := ParsePortRange(rawPort) if err != nil { return 0, 0, err } return int(start), int(end), nil } // Proto returns the protocol of a Port func (p Port) Proto() string { proto, _ := SplitProtoPort(string(p)) return proto } // Port returns the port number of a Port func (p Port) Port() string { _, port := SplitProtoPort(string(p)) return port } // Int returns the port number of a Port as an int func (p Port) Int() int { portStr := p.Port() // We don't need to check for an error because we're going to // assume that any error would have been found, and reported, in NewPort() port, _ := ParsePort(portStr) return port } // Range returns the start/end port numbers of a Port range as ints func (p Port) Range() (int, int, error) { return ParsePortRangeToInt(p.Port()) } // SplitProtoPort splits a port in the format of proto/port func SplitProtoPort(rawPort string) (string, string) { parts := strings.Split(rawPort, "/") l := len(parts) if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 { return "", "" } if l == 1 { return "tcp", rawPort } if len(parts[1]) == 0 { return "tcp", parts[0] } return parts[1], parts[0] } func validateProto(proto string) bool { for _, availableProto := range []string{"tcp", "udp"} { if availableProto == proto { return true } } return false } // ParsePortSpecs receives port specs in the format of ip:public:private/proto and parses // these in to the internal types func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) { var ( exposedPorts = make(map[Port]struct{}, len(ports)) bindings = make(map[Port][]PortBinding) ) for _, rawPort := range ports { portMappings, err := ParsePortSpec(rawPort) if err != nil { return nil, nil, err } for _, portMapping := range portMappings { port := portMapping.Port if _, exists := exposedPorts[port]; !exists { exposedPorts[port] = struct{}{} } bslice, exists := bindings[port] if !exists { bslice = []PortBinding{} } bindings[port] = append(bslice, portMapping.Binding) } } return exposedPorts, bindings, nil } // PortMapping is a data object mapping a Port to a PortBinding type PortMapping struct { Port Port Binding PortBinding } func splitParts(rawport string) (string, string, string) { parts := strings.Split(rawport, ":") n := len(parts) containerport := parts[n-1] switch n { case 1: return "", "", containerport case 2: return "", parts[0], containerport case 3: return parts[0], parts[1], containerport default: return strings.Join(parts[:n-2], ":"), parts[n-2], containerport } } // ParsePortSpec parses a port specification string into a slice of PortMappings func ParsePortSpec(rawPort string) ([]PortMapping, error) { var proto string rawIP, hostPort, containerPort := splitParts(rawPort) proto, containerPort = SplitProtoPort(containerPort) // Strip [] from IPV6 addresses ip, _, err := net.SplitHostPort(rawIP + ":") if err != nil { return nil, fmt.Errorf("Invalid ip address %v: %s", rawIP, err) } if ip != "" && net.ParseIP(ip) == nil { return nil, fmt.Errorf("Invalid ip address: %s", ip) } if containerPort == "" { return nil, fmt.Errorf("No port specified: %s", rawPort) } startPort, endPort, err := ParsePortRange(containerPort) if err != nil { return nil, fmt.Errorf("Invalid containerPort: %s", containerPort) } var startHostPort, endHostPort uint64 = 0, 0 if len(hostPort) > 0 { startHostPort, endHostPort, err = ParsePortRange(hostPort) if err != nil { return nil, fmt.Errorf("Invalid hostPort: %s", hostPort) } } if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) { // Allow host port range iff containerPort is not a range. // In this case, use the host port range as the dynamic // host port range to allocate into. if endPort != startPort { return nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort) } } if !validateProto(strings.ToLower(proto)) { return nil, fmt.Errorf("Invalid proto: %s", proto) } ports := []PortMapping{} for i := uint64(0); i <= (endPort - startPort); i++ { containerPort = strconv.FormatUint(startPort+i, 10) if len(hostPort) > 0 { hostPort = strconv.FormatUint(startHostPort+i, 10) } // Set hostPort to a range only if there is a single container port // and a dynamic host port. if startPort == endPort && startHostPort != endHostPort { hostPort = fmt.Sprintf("%s-%s", hostPort, strconv.FormatUint(endHostPort, 10)) } port, err := NewPort(strings.ToLower(proto), containerPort) if err != nil { return nil, err } binding := PortBinding{ HostIP: ip, HostPort: hostPort, } ports = append(ports, PortMapping{Port: port, Binding: binding}) } return ports, nil } go-connections-0.3.0/nat/nat_test.go000066400000000000000000000305201312327573300173770ustar00rootroot00000000000000package nat import ( "testing" "github.com/stretchr/testify/assert" ) func TestParsePort(t *testing.T) { var ( p int err error ) p, err = ParsePort("1234") if err != nil || p != 1234 { t.Fatal("Parsing '1234' did not succeed") } // FIXME currently this is a valid port. I don't think it should be. // I'm leaving this test commented out until we make a decision. // - erikh /* p, err = ParsePort("0123") if err != nil { t.Fatal("Successfully parsed port '0123' to '123'") } */ p, err = ParsePort("asdf") if err == nil || p != 0 { t.Fatal("Parsing port 'asdf' succeeded") } p, err = ParsePort("1asdf") if err == nil || p != 0 { t.Fatal("Parsing port '1asdf' succeeded") } } func TestParsePortRangeToInt(t *testing.T) { var ( begin int end int err error ) type TestRange struct { Range string Begin int End int } validRanges := []TestRange{ {"1234", 1234, 1234}, {"1234-1234", 1234, 1234}, {"1234-1235", 1234, 1235}, {"8000-9000", 8000, 9000}, {"0", 0, 0}, {"0-0", 0, 0}, } for _, r := range validRanges { begin, end, err = ParsePortRangeToInt(r.Range) if err != nil || begin != r.Begin { t.Fatalf("Parsing port range '%s' did not succeed. Expected begin %d, got %d", r.Range, r.Begin, begin) } if err != nil || end != r.End { t.Fatalf("Parsing port range '%s' did not succeed. Expected end %d, got %d", r.Range, r.End, end) } } invalidRanges := []string{ "asdf", "1asdf", "9000-8000", "9000-", "-8000", "-8000-", } for _, r := range invalidRanges { begin, end, err = ParsePortRangeToInt(r) if err == nil || begin != 0 || end != 0 { t.Fatalf("Parsing port range '%s' succeeded", r) } } } func TestPort(t *testing.T) { p, err := NewPort("tcp", "1234") if err != nil { t.Fatalf("tcp, 1234 had a parsing issue: %v", err) } if string(p) != "1234/tcp" { t.Fatal("tcp, 1234 did not result in the string 1234/tcp") } if p.Proto() != "tcp" { t.Fatal("protocol was not tcp") } if p.Port() != "1234" { t.Fatal("port string value was not 1234") } if p.Int() != 1234 { t.Fatal("port int value was not 1234") } p, err = NewPort("tcp", "asd1234") if err == nil { t.Fatal("tcp, asd1234 was supposed to fail") } p, err = NewPort("tcp", "1234-1230") if err == nil { t.Fatal("tcp, 1234-1230 was supposed to fail") } p, err = NewPort("tcp", "1234-1242") if err != nil { t.Fatalf("tcp, 1234-1242 had a parsing issue: %v", err) } if string(p) != "1234-1242/tcp" { t.Fatal("tcp, 1234-1242 did not result in the string 1234-1242/tcp") } } func TestSplitProtoPort(t *testing.T) { var ( proto string port string ) proto, port = SplitProtoPort("1234/tcp") if proto != "tcp" || port != "1234" { t.Fatal("Could not split 1234/tcp properly") } proto, port = SplitProtoPort("") if proto != "" || port != "" { t.Fatal("parsing an empty string yielded surprising results", proto, port) } proto, port = SplitProtoPort("1234") if proto != "tcp" || port != "1234" { t.Fatal("tcp is not the default protocol for portspec '1234'", proto, port) } proto, port = SplitProtoPort("1234/") if proto != "tcp" || port != "1234" { t.Fatal("parsing '1234/' yielded:" + port + "/" + proto) } proto, port = SplitProtoPort("/tcp") if proto != "" || port != "" { t.Fatal("parsing '/tcp' yielded:" + port + "/" + proto) } } func TestParsePortSpecFull(t *testing.T) { portMappings, err := ParsePortSpec("0.0.0.0:1234-1235:3333-3334/tcp") assert.Nil(t, err) expected := []PortMapping{ { Port: "3333/tcp", Binding: PortBinding{ HostIP: "0.0.0.0", HostPort: "1234", }, }, { Port: "3334/tcp", Binding: PortBinding{ HostIP: "0.0.0.0", HostPort: "1235", }, }, } assert.Equal(t, expected, portMappings) } func TestPartPortSpecIPV6(t *testing.T) { portMappings, err := ParsePortSpec("[2001:4860:0:2001::68]::333") assert.Nil(t, err) expected := []PortMapping{ { Port: "333/tcp", Binding: PortBinding{ HostIP: "2001:4860:0:2001::68", HostPort: "", }, }, } assert.Equal(t, expected, portMappings) } func TestPartPortSpecIPV6WithHostPort(t *testing.T) { portMappings, err := ParsePortSpec("[::1]:80:80") assert.Nil(t, err) expected := []PortMapping{ { Port: "80/tcp", Binding: PortBinding{ HostIP: "::1", HostPort: "80", }, }, } assert.Equal(t, expected, portMappings) } func TestParsePortSpecs(t *testing.T) { var ( portMap map[Port]struct{} bindingMap map[Port][]PortBinding err error ) portMap, bindingMap, err = ParsePortSpecs([]string{"1234/tcp", "2345/udp"}) if err != nil { t.Fatalf("Error while processing ParsePortSpecs: %s", err) } if _, ok := portMap[Port("1234/tcp")]; !ok { t.Fatal("1234/tcp was not parsed properly") } if _, ok := portMap[Port("2345/udp")]; !ok { t.Fatal("2345/udp was not parsed properly") } for portspec, bindings := range bindingMap { if len(bindings) != 1 { t.Fatalf("%s should have exactly one binding", portspec) } if bindings[0].HostIP != "" { t.Fatalf("HostIP should not be set for %s", portspec) } if bindings[0].HostPort != "" { t.Fatalf("HostPort should not be set for %s", portspec) } } portMap, bindingMap, err = ParsePortSpecs([]string{"1234:1234/tcp", "2345:2345/udp"}) if err != nil { t.Fatalf("Error while processing ParsePortSpecs: %s", err) } if _, ok := portMap[Port("1234/tcp")]; !ok { t.Fatal("1234/tcp was not parsed properly") } if _, ok := portMap[Port("2345/udp")]; !ok { t.Fatal("2345/udp was not parsed properly") } for portspec, bindings := range bindingMap { _, port := SplitProtoPort(string(portspec)) if len(bindings) != 1 { t.Fatalf("%s should have exactly one binding", portspec) } if bindings[0].HostIP != "" { t.Fatalf("HostIP should not be set for %s", portspec) } if bindings[0].HostPort != port { t.Fatalf("HostPort should be %s for %s", port, portspec) } } portMap, bindingMap, err = ParsePortSpecs([]string{"0.0.0.0:1234:1234/tcp", "0.0.0.0:2345:2345/udp"}) if err != nil { t.Fatalf("Error while processing ParsePortSpecs: %s", err) } if _, ok := portMap[Port("1234/tcp")]; !ok { t.Fatal("1234/tcp was not parsed properly") } if _, ok := portMap[Port("2345/udp")]; !ok { t.Fatal("2345/udp was not parsed properly") } for portspec, bindings := range bindingMap { _, port := SplitProtoPort(string(portspec)) if len(bindings) != 1 { t.Fatalf("%s should have exactly one binding", portspec) } if bindings[0].HostIP != "0.0.0.0" { t.Fatalf("HostIP is not 0.0.0.0 for %s", portspec) } if bindings[0].HostPort != port { t.Fatalf("HostPort should be %s for %s", port, portspec) } } _, _, err = ParsePortSpecs([]string{"localhost:1234:1234/tcp"}) if err == nil { t.Fatal("Received no error while trying to parse a hostname instead of ip") } } func TestParsePortSpecsWithRange(t *testing.T) { var ( portMap map[Port]struct{} bindingMap map[Port][]PortBinding err error ) portMap, bindingMap, err = ParsePortSpecs([]string{"1234-1236/tcp", "2345-2347/udp"}) if err != nil { t.Fatalf("Error while processing ParsePortSpecs: %s", err) } if _, ok := portMap[Port("1235/tcp")]; !ok { t.Fatal("1234/tcp was not parsed properly") } if _, ok := portMap[Port("2346/udp")]; !ok { t.Fatal("2345/udp was not parsed properly") } for portspec, bindings := range bindingMap { if len(bindings) != 1 { t.Fatalf("%s should have exactly one binding", portspec) } if bindings[0].HostIP != "" { t.Fatalf("HostIP should not be set for %s", portspec) } if bindings[0].HostPort != "" { t.Fatalf("HostPort should not be set for %s", portspec) } } portMap, bindingMap, err = ParsePortSpecs([]string{"1234-1236:1234-1236/tcp", "2345-2347:2345-2347/udp"}) if err != nil { t.Fatalf("Error while processing ParsePortSpecs: %s", err) } if _, ok := portMap[Port("1235/tcp")]; !ok { t.Fatal("1234/tcp was not parsed properly") } if _, ok := portMap[Port("2346/udp")]; !ok { t.Fatal("2345/udp was not parsed properly") } for portspec, bindings := range bindingMap { _, port := SplitProtoPort(string(portspec)) if len(bindings) != 1 { t.Fatalf("%s should have exactly one binding", portspec) } if bindings[0].HostIP != "" { t.Fatalf("HostIP should not be set for %s", portspec) } if bindings[0].HostPort != port { t.Fatalf("HostPort should be %s for %s", port, portspec) } } portMap, bindingMap, err = ParsePortSpecs([]string{"0.0.0.0:1234-1236:1234-1236/tcp", "0.0.0.0:2345-2347:2345-2347/udp"}) if err != nil { t.Fatalf("Error while processing ParsePortSpecs: %s", err) } if _, ok := portMap[Port("1235/tcp")]; !ok { t.Fatal("1234/tcp was not parsed properly") } if _, ok := portMap[Port("2346/udp")]; !ok { t.Fatal("2345/udp was not parsed properly") } for portspec, bindings := range bindingMap { _, port := SplitProtoPort(string(portspec)) if len(bindings) != 1 || bindings[0].HostIP != "0.0.0.0" || bindings[0].HostPort != port { t.Fatalf("Expect single binding to port %s but found %s", port, bindings) } } _, _, err = ParsePortSpecs([]string{"localhost:1234-1236:1234-1236/tcp"}) if err == nil { t.Fatal("Received no error while trying to parse a hostname instead of ip") } } func TestParseNetworkOptsPrivateOnly(t *testing.T) { ports, bindings, err := ParsePortSpecs([]string{"192.168.1.100::80"}) if err != nil { t.Fatal(err) } if len(ports) != 1 { t.Logf("Expected 1 got %d", len(ports)) t.FailNow() } if len(bindings) != 1 { t.Logf("Expected 1 got %d", len(bindings)) t.FailNow() } for k := range ports { if k.Proto() != "tcp" { t.Logf("Expected tcp got %s", k.Proto()) t.Fail() } if k.Port() != "80" { t.Logf("Expected 80 got %s", k.Port()) t.Fail() } b, exists := bindings[k] if !exists { t.Log("Binding does not exist") t.FailNow() } if len(b) != 1 { t.Logf("Expected 1 got %d", len(b)) t.FailNow() } s := b[0] if s.HostPort != "" { t.Logf("Expected \"\" got %s", s.HostPort) t.Fail() } if s.HostIP != "192.168.1.100" { t.Fail() } } } func TestParseNetworkOptsPublic(t *testing.T) { ports, bindings, err := ParsePortSpecs([]string{"192.168.1.100:8080:80"}) if err != nil { t.Fatal(err) } if len(ports) != 1 { t.Logf("Expected 1 got %d", len(ports)) t.FailNow() } if len(bindings) != 1 { t.Logf("Expected 1 got %d", len(bindings)) t.FailNow() } for k := range ports { if k.Proto() != "tcp" { t.Logf("Expected tcp got %s", k.Proto()) t.Fail() } if k.Port() != "80" { t.Logf("Expected 80 got %s", k.Port()) t.Fail() } b, exists := bindings[k] if !exists { t.Log("Binding does not exist") t.FailNow() } if len(b) != 1 { t.Logf("Expected 1 got %d", len(b)) t.FailNow() } s := b[0] if s.HostPort != "8080" { t.Logf("Expected 8080 got %s", s.HostPort) t.Fail() } if s.HostIP != "192.168.1.100" { t.Fail() } } } func TestParseNetworkOptsPublicNoPort(t *testing.T) { ports, bindings, err := ParsePortSpecs([]string{"192.168.1.100"}) if err == nil { t.Logf("Expected error Invalid containerPort") t.Fail() } if ports != nil { t.Logf("Expected nil got %s", ports) t.Fail() } if bindings != nil { t.Logf("Expected nil got %s", bindings) t.Fail() } } func TestParseNetworkOptsNegativePorts(t *testing.T) { ports, bindings, err := ParsePortSpecs([]string{"192.168.1.100:-1:-1"}) if err == nil { t.Fail() } if len(ports) != 0 { t.Logf("Expected nil got %d", len(ports)) t.Fail() } if len(bindings) != 0 { t.Logf("Expected 0 got %d", len(bindings)) t.Fail() } } func TestParseNetworkOptsUdp(t *testing.T) { ports, bindings, err := ParsePortSpecs([]string{"192.168.1.100::6000/udp"}) if err != nil { t.Fatal(err) } if len(ports) != 1 { t.Logf("Expected 1 got %d", len(ports)) t.FailNow() } if len(bindings) != 1 { t.Logf("Expected 1 got %d", len(bindings)) t.FailNow() } for k := range ports { if k.Proto() != "udp" { t.Logf("Expected udp got %s", k.Proto()) t.Fail() } if k.Port() != "6000" { t.Logf("Expected 6000 got %s", k.Port()) t.Fail() } b, exists := bindings[k] if !exists { t.Log("Binding does not exist") t.FailNow() } if len(b) != 1 { t.Logf("Expected 1 got %d", len(b)) t.FailNow() } s := b[0] if s.HostPort != "" { t.Logf("Expected \"\" got %s", s.HostPort) t.Fail() } if s.HostIP != "192.168.1.100" { t.Fail() } } } go-connections-0.3.0/nat/parse.go000066400000000000000000000027311312327573300166730ustar00rootroot00000000000000package nat import ( "fmt" "strconv" "strings" ) // PartParser parses and validates the specified string (data) using the specified template // e.g. ip:public:private -> 192.168.0.1:80:8000 // DEPRECATED: do not use, this function may be removed in a future version func PartParser(template, data string) (map[string]string, error) { // ip:public:private var ( templateParts = strings.Split(template, ":") parts = strings.Split(data, ":") out = make(map[string]string, len(templateParts)) ) if len(parts) != len(templateParts) { return nil, fmt.Errorf("Invalid format to parse. %s should match template %s", data, template) } for i, t := range templateParts { value := "" if len(parts) > i { value = parts[i] } out[t] = value } return out, nil } // ParsePortRange parses and validates the specified string as a port-range (8000-9000) func ParsePortRange(ports string) (uint64, uint64, error) { if ports == "" { return 0, 0, fmt.Errorf("Empty string specified for ports.") } if !strings.Contains(ports, "-") { start, err := strconv.ParseUint(ports, 10, 16) end := start return start, end, err } parts := strings.Split(ports, "-") start, err := strconv.ParseUint(parts[0], 10, 16) if err != nil { return 0, 0, err } end, err := strconv.ParseUint(parts[1], 10, 16) if err != nil { return 0, 0, err } if end < start { return 0, 0, fmt.Errorf("Invalid range specified for the Port: %s", ports) } return start, end, nil } go-connections-0.3.0/nat/parse_test.go000066400000000000000000000037421312327573300177350ustar00rootroot00000000000000package nat import ( "strings" "testing" ) func TestParsePortRange(t *testing.T) { if start, end, err := ParsePortRange("8000-8080"); err != nil || start != 8000 || end != 8080 { t.Fatalf("Error: %s or Expecting {start,end} values {8000,8080} but found {%d,%d}.", err, start, end) } } func TestParsePortRangeEmpty(t *testing.T) { if _, _, err := ParsePortRange(""); err == nil || err.Error() != "Empty string specified for ports." { t.Fatalf("Expected error 'Empty string specified for ports.', got %v", err) } } func TestParsePortRangeWithNoRange(t *testing.T) { start, end, err := ParsePortRange("8080") if err != nil { t.Fatal(err) } if start != 8080 || end != 8080 { t.Fatalf("Expected start and end to be the same and equal to 8080, but were %v and %v", start, end) } } func TestParsePortRangeIncorrectRange(t *testing.T) { if _, _, err := ParsePortRange("9000-8080"); err == nil || !strings.Contains(err.Error(), "Invalid range specified for the Port") { t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err) } } func TestParsePortRangeIncorrectEndRange(t *testing.T) { if _, _, err := ParsePortRange("8000-a"); err == nil || !strings.Contains(err.Error(), "invalid syntax") { t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err) } if _, _, err := ParsePortRange("8000-30a"); err == nil || !strings.Contains(err.Error(), "invalid syntax") { t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err) } } func TestParsePortRangeIncorrectStartRange(t *testing.T) { if _, _, err := ParsePortRange("a-8000"); err == nil || !strings.Contains(err.Error(), "invalid syntax") { t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err) } if _, _, err := ParsePortRange("30a-8000"); err == nil || !strings.Contains(err.Error(), "invalid syntax") { t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err) } } go-connections-0.3.0/nat/sort.go000066400000000000000000000041651312327573300165530ustar00rootroot00000000000000package nat import ( "sort" "strings" ) type portSorter struct { ports []Port by func(i, j Port) bool } func (s *portSorter) Len() int { return len(s.ports) } func (s *portSorter) Swap(i, j int) { s.ports[i], s.ports[j] = s.ports[j], s.ports[i] } func (s *portSorter) Less(i, j int) bool { ip := s.ports[i] jp := s.ports[j] return s.by(ip, jp) } // Sort sorts a list of ports using the provided predicate // This function should compare `i` and `j`, returning true if `i` is // considered to be less than `j` func Sort(ports []Port, predicate func(i, j Port) bool) { s := &portSorter{ports, predicate} sort.Sort(s) } type portMapEntry struct { port Port binding PortBinding } type portMapSorter []portMapEntry func (s portMapSorter) Len() int { return len(s) } func (s portMapSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // sort the port so that the order is: // 1. port with larger specified bindings // 2. larger port // 3. port with tcp protocol func (s portMapSorter) Less(i, j int) bool { pi, pj := s[i].port, s[j].port hpi, hpj := toInt(s[i].binding.HostPort), toInt(s[j].binding.HostPort) return hpi > hpj || pi.Int() > pj.Int() || (pi.Int() == pj.Int() && strings.ToLower(pi.Proto()) == "tcp") } // SortPortMap sorts the list of ports and their respected mapping. The ports // will explicit HostPort will be placed first. func SortPortMap(ports []Port, bindings PortMap) { s := portMapSorter{} for _, p := range ports { if binding, ok := bindings[p]; ok { for _, b := range binding { s = append(s, portMapEntry{port: p, binding: b}) } bindings[p] = []PortBinding{} } else { s = append(s, portMapEntry{port: p}) } } sort.Sort(s) var ( i int pm = make(map[Port]struct{}) ) // reorder ports for _, entry := range s { if _, ok := pm[entry.port]; !ok { ports[i] = entry.port pm[entry.port] = struct{}{} i++ } // reorder bindings for this port if _, ok := bindings[entry.port]; ok { bindings[entry.port] = append(bindings[entry.port], entry.binding) } } } func toInt(s string) uint64 { i, _, err := ParsePortRange(s) if err != nil { i = 0 } return i } go-connections-0.3.0/nat/sort_test.go000066400000000000000000000031441312327573300176060ustar00rootroot00000000000000package nat import ( "fmt" "reflect" "testing" ) func TestSortUniquePorts(t *testing.T) { ports := []Port{ Port("6379/tcp"), Port("22/tcp"), } Sort(ports, func(ip, jp Port) bool { return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp") }) first := ports[0] if fmt.Sprint(first) != "22/tcp" { t.Log(fmt.Sprint(first)) t.Fail() } } func TestSortSamePortWithDifferentProto(t *testing.T) { ports := []Port{ Port("8888/tcp"), Port("8888/udp"), Port("6379/tcp"), Port("6379/udp"), } Sort(ports, func(ip, jp Port) bool { return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && ip.Proto() == "tcp") }) first := ports[0] if fmt.Sprint(first) != "6379/tcp" { t.Fail() } } func TestSortPortMap(t *testing.T) { ports := []Port{ Port("22/tcp"), Port("22/udp"), Port("8000/tcp"), Port("6379/tcp"), Port("9999/tcp"), } portMap := PortMap{ Port("22/tcp"): []PortBinding{ {}, }, Port("8000/tcp"): []PortBinding{ {}, }, Port("6379/tcp"): []PortBinding{ {}, {HostIP: "0.0.0.0", HostPort: "32749"}, }, Port("9999/tcp"): []PortBinding{ {HostIP: "0.0.0.0", HostPort: "40000"}, }, } SortPortMap(ports, portMap) if !reflect.DeepEqual(ports, []Port{ Port("9999/tcp"), Port("6379/tcp"), Port("8000/tcp"), Port("22/tcp"), Port("22/udp"), }) { t.Errorf("failed to prioritize port with explicit mappings, got %v", ports) } if pm := portMap[Port("6379/tcp")]; !reflect.DeepEqual(pm, []PortBinding{ {HostIP: "0.0.0.0", HostPort: "32749"}, {}, }) { t.Errorf("failed to prioritize bindings with explicit mappings, got %v", pm) } } go-connections-0.3.0/proxy/000077500000000000000000000000001312327573300156265ustar00rootroot00000000000000go-connections-0.3.0/proxy/logger.go000066400000000000000000000002761312327573300174410ustar00rootroot00000000000000package proxy type logger interface { Printf(format string, args ...interface{}) } type noopLogger struct{} func (l *noopLogger) Printf(_ string, _ ...interface{}) { // Do nothing :) } go-connections-0.3.0/proxy/network_proxy_test.go000066400000000000000000000125551312327573300221560ustar00rootroot00000000000000package proxy import ( "bytes" "fmt" "io" "net" "strings" "testing" "time" ) var testBuf = []byte("Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo") var testBufSize = len(testBuf) type EchoServer interface { Run() Close() LocalAddr() net.Addr } type TCPEchoServer struct { listener net.Listener testCtx *testing.T } type UDPEchoServer struct { conn net.PacketConn testCtx *testing.T } func NewEchoServer(t *testing.T, proto, address string) EchoServer { var server EchoServer if strings.HasPrefix(proto, "tcp") { listener, err := net.Listen(proto, address) if err != nil { t.Fatal(err) } server = &TCPEchoServer{listener: listener, testCtx: t} } else { socket, err := net.ListenPacket(proto, address) if err != nil { t.Fatal(err) } server = &UDPEchoServer{conn: socket, testCtx: t} } return server } func (server *TCPEchoServer) Run() { go func() { for { client, err := server.listener.Accept() if err != nil { return } go func(client net.Conn) { if _, err := io.Copy(client, client); err != nil { server.testCtx.Logf("can't echo to the client: %v\n", err.Error()) } client.Close() }(client) } }() } func (server *TCPEchoServer) LocalAddr() net.Addr { return server.listener.Addr() } func (server *TCPEchoServer) Close() { server.listener.Close() } func (server *UDPEchoServer) Run() { go func() { readBuf := make([]byte, 1024) for { read, from, err := server.conn.ReadFrom(readBuf) if err != nil { return } for i := 0; i != read; { written, err := server.conn.WriteTo(readBuf[i:read], from) if err != nil { break } i += written } } }() } func (server *UDPEchoServer) LocalAddr() net.Addr { return server.conn.LocalAddr() } func (server *UDPEchoServer) Close() { server.conn.Close() } func testProxyAt(t *testing.T, proto string, proxy Proxy, addr string) { defer proxy.Close() go proxy.Run() client, err := net.Dial(proto, addr) if err != nil { t.Fatalf("Can't connect to the proxy: %v", err) } defer client.Close() client.SetDeadline(time.Now().Add(10 * time.Second)) if _, err = client.Write(testBuf); err != nil { t.Fatal(err) } recvBuf := make([]byte, testBufSize) if _, err = client.Read(recvBuf); err != nil { t.Fatal(err) } if !bytes.Equal(testBuf, recvBuf) { t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf)) } } func testProxy(t *testing.T, proto string, proxy Proxy) { testProxyAt(t, proto, proxy, proxy.FrontendAddr().String()) } func TestTCP4Proxy(t *testing.T) { backend := NewEchoServer(t, "tcp", "127.0.0.1:0") defer backend.Close() backend.Run() frontendAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) if err != nil { t.Fatal(err) } testProxy(t, "tcp", proxy) } func TestTCP6Proxy(t *testing.T) { backend := NewEchoServer(t, "tcp", "[::1]:0") defer backend.Close() backend.Run() frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0} proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) if err != nil { t.Fatal(err) } testProxy(t, "tcp", proxy) } func TestTCPDualStackProxy(t *testing.T) { // If I understand `godoc -src net favoriteAddrFamily` (used by the // net.Listen* functions) correctly this should work, but it doesn't. t.Skip("No support for dual stack yet") backend := NewEchoServer(t, "tcp", "[::1]:0") defer backend.Close() backend.Run() frontendAddr := &net.TCPAddr{IP: net.IPv6loopback, Port: 0} proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) if err != nil { t.Fatal(err) } ipv4ProxyAddr := &net.TCPAddr{ IP: net.IPv4(127, 0, 0, 1), Port: proxy.FrontendAddr().(*net.TCPAddr).Port, } testProxyAt(t, "tcp", proxy, ipv4ProxyAddr.String()) } func TestUDP4Proxy(t *testing.T) { backend := NewEchoServer(t, "udp", "127.0.0.1:0") defer backend.Close() backend.Run() frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) if err != nil { t.Fatal(err) } testProxy(t, "udp", proxy) } func TestUDP6Proxy(t *testing.T) { backend := NewEchoServer(t, "udp", "[::1]:0") defer backend.Close() backend.Run() frontendAddr := &net.UDPAddr{IP: net.IPv6loopback, Port: 0} proxy, err := NewProxy(frontendAddr, backend.LocalAddr()) if err != nil { t.Fatal(err) } testProxy(t, "udp", proxy) } func TestUDPWriteError(t *testing.T) { frontendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0} // Hopefully, this port will be free: */ backendAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 25587} proxy, err := NewProxy(frontendAddr, backendAddr) if err != nil { t.Fatal(err) } defer proxy.Close() go proxy.Run() client, err := net.Dial("udp", "127.0.0.1:25587") if err != nil { t.Fatalf("Can't connect to the proxy: %v", err) } defer client.Close() // Make sure the proxy doesn't stop when there is no actual backend: client.Write(testBuf) client.Write(testBuf) backend := NewEchoServer(t, "udp", "127.0.0.1:25587") defer backend.Close() backend.Run() client.SetDeadline(time.Now().Add(10 * time.Second)) if _, err = client.Write(testBuf); err != nil { t.Fatal(err) } recvBuf := make([]byte, testBufSize) if _, err = client.Read(recvBuf); err != nil { t.Fatal(err) } if !bytes.Equal(testBuf, recvBuf) { t.Fatal(fmt.Errorf("Expected [%v] but got [%v]", testBuf, recvBuf)) } } go-connections-0.3.0/proxy/proxy.go000066400000000000000000000023601312327573300173370ustar00rootroot00000000000000// Package proxy provides a network Proxy interface and implementations for TCP and UDP. package proxy import ( "fmt" "net" ) // Proxy defines the behavior of a proxy. It forwards traffic back and forth // between two endpoints : the frontend and the backend. // It can be used to do software port-mapping between two addresses. // e.g. forward all traffic between the frontend (host) 127.0.0.1:3000 // to the backend (container) at 172.17.42.108:4000. type Proxy interface { // Run starts forwarding traffic back and forth between the front // and back-end addresses. Run() // Close stops forwarding traffic and close both ends of the Proxy. Close() // FrontendAddr returns the address on which the proxy is listening. FrontendAddr() net.Addr // BackendAddr returns the proxied address. BackendAddr() net.Addr } // NewProxy creates a Proxy according to the specified frontendAddr and backendAddr. func NewProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) { switch frontendAddr.(type) { case *net.UDPAddr: return NewUDPProxy(frontendAddr.(*net.UDPAddr), backendAddr.(*net.UDPAddr)) case *net.TCPAddr: return NewTCPProxy(frontendAddr.(*net.TCPAddr), backendAddr.(*net.TCPAddr)) default: panic(fmt.Errorf("Unsupported protocol")) } } go-connections-0.3.0/proxy/stub_proxy.go000066400000000000000000000012721312327573300203750ustar00rootroot00000000000000package proxy import ( "net" ) // StubProxy is a proxy that is a stub (does nothing). type StubProxy struct { frontendAddr net.Addr backendAddr net.Addr } // Run does nothing. func (p *StubProxy) Run() {} // Close does nothing. func (p *StubProxy) Close() {} // FrontendAddr returns the frontend address. func (p *StubProxy) FrontendAddr() net.Addr { return p.frontendAddr } // BackendAddr returns the backend address. func (p *StubProxy) BackendAddr() net.Addr { return p.backendAddr } // NewStubProxy creates a new StubProxy func NewStubProxy(frontendAddr, backendAddr net.Addr) (Proxy, error) { return &StubProxy{ frontendAddr: frontendAddr, backendAddr: backendAddr, }, nil } go-connections-0.3.0/proxy/tcp_proxy.go000066400000000000000000000052321312327573300202060ustar00rootroot00000000000000package proxy import ( "io" "net" "syscall" ) // TCPProxy is a proxy for TCP connections. It implements the Proxy interface to // handle TCP traffic forwarding between the frontend and backend addresses. type TCPProxy struct { Logger logger listener *net.TCPListener frontendAddr *net.TCPAddr backendAddr *net.TCPAddr } // NewTCPProxy creates a new TCPProxy. func NewTCPProxy(frontendAddr, backendAddr *net.TCPAddr, ops ...func(*TCPProxy)) (*TCPProxy, error) { listener, err := net.ListenTCP("tcp", frontendAddr) if err != nil { return nil, err } // If the port in frontendAddr was 0 then ListenTCP will have a picked // a port to listen on, hence the call to Addr to get that actual port: proxy := &TCPProxy{ listener: listener, frontendAddr: listener.Addr().(*net.TCPAddr), backendAddr: backendAddr, Logger: &noopLogger{}, } for _, op := range ops { op(proxy) } return proxy, nil } func (proxy *TCPProxy) clientLoop(client *net.TCPConn, quit chan bool) { backend, err := net.DialTCP("tcp", nil, proxy.backendAddr) if err != nil { proxy.Logger.Printf("Can't forward traffic to backend tcp/%v: %s\n", proxy.backendAddr, err) client.Close() return } event := make(chan int64) var broker = func(to, from *net.TCPConn) { written, err := io.Copy(to, from) if err != nil { // If the socket we are writing to is shutdown with // SHUT_WR, forward it to the other end of the pipe: if err, ok := err.(*net.OpError); ok && err.Err == syscall.EPIPE { from.CloseWrite() } } to.CloseRead() event <- written } go broker(client, backend) go broker(backend, client) var transferred int64 for i := 0; i < 2; i++ { select { case written := <-event: transferred += written case <-quit: // Interrupt the two brokers and "join" them. client.Close() backend.Close() for ; i < 2; i++ { transferred += <-event } return } } client.Close() backend.Close() } // Run starts forwarding the traffic using TCP. func (proxy *TCPProxy) Run() { quit := make(chan bool) defer close(quit) for { client, err := proxy.listener.Accept() if err != nil { proxy.Logger.Printf("Stopping proxy on tcp/%v for tcp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err) return } go proxy.clientLoop(client.(*net.TCPConn), quit) } } // Close stops forwarding the traffic. func (proxy *TCPProxy) Close() { proxy.listener.Close() } // FrontendAddr returns the TCP address on which the proxy is listening. func (proxy *TCPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr } // BackendAddr returns the TCP proxied address. func (proxy *TCPProxy) BackendAddr() net.Addr { return proxy.backendAddr } go-connections-0.3.0/proxy/udp_proxy.go000066400000000000000000000111421312327573300202050ustar00rootroot00000000000000package proxy import ( "encoding/binary" "net" "strings" "sync" "syscall" "time" ) const ( // UDPConnTrackTimeout is the timeout used for UDP connection tracking UDPConnTrackTimeout = 90 * time.Second // UDPBufSize is the buffer size for the UDP proxy UDPBufSize = 65507 ) // A net.Addr where the IP is split into two fields so you can use it as a key // in a map: type connTrackKey struct { IPHigh uint64 IPLow uint64 Port int } func newConnTrackKey(addr *net.UDPAddr) *connTrackKey { if len(addr.IP) == net.IPv4len { return &connTrackKey{ IPHigh: 0, IPLow: uint64(binary.BigEndian.Uint32(addr.IP)), Port: addr.Port, } } return &connTrackKey{ IPHigh: binary.BigEndian.Uint64(addr.IP[:8]), IPLow: binary.BigEndian.Uint64(addr.IP[8:]), Port: addr.Port, } } type connTrackMap map[connTrackKey]*net.UDPConn // UDPProxy is proxy for which handles UDP datagrams. It implements the Proxy // interface to handle UDP traffic forwarding between the frontend and backend // addresses. type UDPProxy struct { Logger logger listener *net.UDPConn frontendAddr *net.UDPAddr backendAddr *net.UDPAddr connTrackTable connTrackMap connTrackLock sync.Mutex } // NewUDPProxy creates a new UDPProxy. func NewUDPProxy(frontendAddr, backendAddr *net.UDPAddr, ops ...func(*UDPProxy)) (*UDPProxy, error) { listener, err := net.ListenUDP("udp", frontendAddr) if err != nil { return nil, err } proxy := &UDPProxy{ listener: listener, frontendAddr: listener.LocalAddr().(*net.UDPAddr), backendAddr: backendAddr, connTrackTable: make(connTrackMap), Logger: &noopLogger{}, } for _, op := range ops { op(proxy) } return proxy, nil } func (proxy *UDPProxy) replyLoop(proxyConn *net.UDPConn, clientAddr *net.UDPAddr, clientKey *connTrackKey) { defer func() { proxy.connTrackLock.Lock() delete(proxy.connTrackTable, *clientKey) proxy.connTrackLock.Unlock() proxyConn.Close() }() readBuf := make([]byte, UDPBufSize) for { proxyConn.SetReadDeadline(time.Now().Add(UDPConnTrackTimeout)) again: read, err := proxyConn.Read(readBuf) if err != nil { if err, ok := err.(*net.OpError); ok && err.Err == syscall.ECONNREFUSED { // This will happen if the last write failed // (e.g: nothing is actually listening on the // proxied port on the container), ignore it // and continue until UDPConnTrackTimeout // expires: goto again } return } for i := 0; i != read; { written, err := proxy.listener.WriteToUDP(readBuf[i:read], clientAddr) if err != nil { return } i += written } } } // Run starts forwarding the traffic using UDP. func (proxy *UDPProxy) Run() { readBuf := make([]byte, UDPBufSize) for { read, from, err := proxy.listener.ReadFromUDP(readBuf) if err != nil { // NOTE: Apparently ReadFrom doesn't return // ECONNREFUSED like Read do (see comment in // UDPProxy.replyLoop) if !isClosedError(err) { proxy.Logger.Printf("Stopping proxy on udp/%v for udp/%v (%s)", proxy.frontendAddr, proxy.backendAddr, err) } break } fromKey := newConnTrackKey(from) proxy.connTrackLock.Lock() proxyConn, hit := proxy.connTrackTable[*fromKey] if !hit { proxyConn, err = net.DialUDP("udp", nil, proxy.backendAddr) if err != nil { proxy.Logger.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err) proxy.connTrackLock.Unlock() continue } proxy.connTrackTable[*fromKey] = proxyConn go proxy.replyLoop(proxyConn, from, fromKey) } proxy.connTrackLock.Unlock() for i := 0; i != read; { written, err := proxyConn.Write(readBuf[i:read]) if err != nil { proxy.Logger.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err) break } i += written } } } // Close stops forwarding the traffic. func (proxy *UDPProxy) Close() { proxy.listener.Close() proxy.connTrackLock.Lock() defer proxy.connTrackLock.Unlock() for _, conn := range proxy.connTrackTable { conn.Close() } } // FrontendAddr returns the UDP address on which the proxy is listening. func (proxy *UDPProxy) FrontendAddr() net.Addr { return proxy.frontendAddr } // BackendAddr returns the proxied UDP address. func (proxy *UDPProxy) BackendAddr() net.Addr { return proxy.backendAddr } func isClosedError(err error) bool { /* This comparison is ugly, but unfortunately, net.go doesn't export errClosing. * See: * http://golang.org/src/pkg/net/net.go * https://code.google.com/p/go/issues/detail?id=4337 * https://groups.google.com/forum/#!msg/golang-nuts/0_aaCvBmOcM/SptmDyX1XJMJ */ return strings.HasSuffix(err.Error(), "use of closed network connection") } go-connections-0.3.0/sockets/000077500000000000000000000000001312327573300161205ustar00rootroot00000000000000go-connections-0.3.0/sockets/README.md000066400000000000000000000000001312327573300173650ustar00rootroot00000000000000go-connections-0.3.0/sockets/inmem_socket.go000066400000000000000000000036051312327573300211300ustar00rootroot00000000000000package sockets import ( "errors" "net" "sync" ) var errClosed = errors.New("use of closed network connection") // InmemSocket implements net.Listener using in-memory only connections. type InmemSocket struct { chConn chan net.Conn chClose chan struct{} addr string mu sync.Mutex } // dummyAddr is used to satisfy net.Addr for the in-mem socket // it is just stored as a string and returns the string for all calls type dummyAddr string // NewInmemSocket creates an in-memory only net.Listener // The addr argument can be any string, but is used to satisfy the `Addr()` part // of the net.Listener interface func NewInmemSocket(addr string, bufSize int) *InmemSocket { return &InmemSocket{ chConn: make(chan net.Conn, bufSize), chClose: make(chan struct{}), addr: addr, } } // Addr returns the socket's addr string to satisfy net.Listener func (s *InmemSocket) Addr() net.Addr { return dummyAddr(s.addr) } // Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn. func (s *InmemSocket) Accept() (net.Conn, error) { select { case conn := <-s.chConn: return conn, nil case <-s.chClose: return nil, errClosed } } // Close closes the listener. It will be unavailable for use once closed. func (s *InmemSocket) Close() error { s.mu.Lock() defer s.mu.Unlock() select { case <-s.chClose: default: close(s.chClose) } return nil } // Dial is used to establish a connection with the in-mem server func (s *InmemSocket) Dial(network, addr string) (net.Conn, error) { srvConn, clientConn := net.Pipe() select { case s.chConn <- srvConn: case <-s.chClose: return nil, errClosed } return clientConn, nil } // Network returns the addr string, satisfies net.Addr func (a dummyAddr) Network() string { return string(a) } // String returns the string form func (a dummyAddr) String() string { return string(a) } go-connections-0.3.0/sockets/inmem_socket_test.go000066400000000000000000000011711312327573300221630ustar00rootroot00000000000000package sockets import "testing" func TestInmemSocket(t *testing.T) { l := NewInmemSocket("test", 0) defer l.Close() go func() { for { conn, err := l.Accept() if err != nil { return } conn.Write([]byte("hello")) conn.Close() } }() conn, err := l.Dial("test", "test") if err != nil { t.Fatal(err) } buf := make([]byte, 5) _, err = conn.Read(buf) if err != nil { t.Fatal(err) } if string(buf) != "hello" { t.Fatalf("expected `hello`, got %s", string(buf)) } l.Close() conn, err = l.Dial("test", "test") if err != errClosed { t.Fatalf("expected `errClosed` error, got %v", err) } } go-connections-0.3.0/sockets/proxy.go000066400000000000000000000022061312327573300176300ustar00rootroot00000000000000package sockets import ( "net" "net/url" "os" "strings" "golang.org/x/net/proxy" ) // GetProxyEnv allows access to the uppercase and the lowercase forms of // proxy-related variables. See the Go specification for details on these // variables. https://golang.org/pkg/net/http/ func GetProxyEnv(key string) string { proxyValue := os.Getenv(strings.ToUpper(key)) if proxyValue == "" { return os.Getenv(strings.ToLower(key)) } return proxyValue } // DialerFromEnvironment takes in a "direct" *net.Dialer and returns a // proxy.Dialer which will route the connections through the proxy using the // given dialer. func DialerFromEnvironment(direct *net.Dialer) (proxy.Dialer, error) { allProxy := GetProxyEnv("all_proxy") if len(allProxy) == 0 { return direct, nil } proxyURL, err := url.Parse(allProxy) if err != nil { return direct, err } proxyFromURL, err := proxy.FromURL(proxyURL, direct) if err != nil { return direct, err } noProxy := GetProxyEnv("no_proxy") if len(noProxy) == 0 { return proxyFromURL, nil } perHost := proxy.NewPerHost(proxyFromURL, direct) perHost.AddFromString(noProxy) return perHost, nil } go-connections-0.3.0/sockets/sockets.go000066400000000000000000000020541312327573300201230ustar00rootroot00000000000000// Package sockets provides helper functions to create and configure Unix or TCP sockets. package sockets import ( "errors" "net" "net/http" "time" ) // Why 32? See https://github.com/docker/docker/pull/8035. const defaultTimeout = 32 * time.Second // ErrProtocolNotAvailable is returned when a given transport protocol is not provided by the operating system. var ErrProtocolNotAvailable = errors.New("protocol not available") // ConfigureTransport configures the specified Transport according to the // specified proto and addr. // If the proto is unix (using a unix socket to communicate) or npipe the // compression is disabled. func ConfigureTransport(tr *http.Transport, proto, addr string) error { switch proto { case "unix": return configureUnixTransport(tr, proto, addr) case "npipe": return configureNpipeTransport(tr, proto, addr) default: tr.Proxy = http.ProxyFromEnvironment dialer, err := DialerFromEnvironment(&net.Dialer{ Timeout: defaultTimeout, }) if err != nil { return err } tr.Dial = dialer.Dial } return nil } go-connections-0.3.0/sockets/sockets_unix.go000066400000000000000000000015101312327573300211620ustar00rootroot00000000000000// +build !windows package sockets import ( "fmt" "net" "net/http" "syscall" "time" ) const maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path) func configureUnixTransport(tr *http.Transport, proto, addr string) error { if len(addr) > maxUnixSocketPathSize { return fmt.Errorf("Unix socket path %q is too long", addr) } // No need for compression in local communications. tr.DisableCompression = true tr.Dial = func(_, _ string) (net.Conn, error) { return net.DialTimeout(proto, addr, defaultTimeout) } return nil } func configureNpipeTransport(tr *http.Transport, proto, addr string) error { return ErrProtocolNotAvailable } // DialPipe connects to a Windows named pipe. // This is not supported on other OSes. func DialPipe(_ string, _ time.Duration) (net.Conn, error) { return nil, syscall.EAFNOSUPPORT } go-connections-0.3.0/sockets/sockets_windows.go000066400000000000000000000011611312327573300216730ustar00rootroot00000000000000package sockets import ( "net" "net/http" "time" "github.com/Microsoft/go-winio" ) func configureUnixTransport(tr *http.Transport, proto, addr string) error { return ErrProtocolNotAvailable } func configureNpipeTransport(tr *http.Transport, proto, addr string) error { // No need for compression in local communications. tr.DisableCompression = true tr.Dial = func(_, _ string) (net.Conn, error) { return DialPipe(addr, defaultTimeout) } return nil } // DialPipe connects to a Windows named pipe. func DialPipe(addr string, timeout time.Duration) (net.Conn, error) { return winio.DialPipe(addr, &timeout) } go-connections-0.3.0/sockets/tcp_socket.go000066400000000000000000000011351312327573300206050ustar00rootroot00000000000000// Package sockets provides helper functions to create and configure Unix or TCP sockets. package sockets import ( "crypto/tls" "net" ) // NewTCPSocket creates a TCP socket listener with the specified address and // the specified tls configuration. If TLSConfig is set, will encapsulate the // TCP listener inside a TLS one. func NewTCPSocket(addr string, tlsConfig *tls.Config) (net.Listener, error) { l, err := net.Listen("tcp", addr) if err != nil { return nil, err } if tlsConfig != nil { tlsConfig.NextProtos = []string{"http/1.1"} l = tls.NewListener(l, tlsConfig) } return l, nil } go-connections-0.3.0/sockets/unix_socket.go000066400000000000000000000011421312327573300210000ustar00rootroot00000000000000// +build !windows package sockets import ( "net" "os" "syscall" ) // NewUnixSocket creates a unix socket with the specified path and group. func NewUnixSocket(path string, gid int) (net.Listener, error) { if err := syscall.Unlink(path); err != nil && !os.IsNotExist(err) { return nil, err } mask := syscall.Umask(0777) defer syscall.Umask(mask) l, err := net.Listen("unix", path) if err != nil { return nil, err } if err := os.Chown(path, 0, gid); err != nil { l.Close() return nil, err } if err := os.Chmod(path, 0660); err != nil { l.Close() return nil, err } return l, nil } go-connections-0.3.0/tlsconfig/000077500000000000000000000000001312327573300164355ustar00rootroot00000000000000go-connections-0.3.0/tlsconfig/certpool_go17.go000066400000000000000000000006061312327573300214520ustar00rootroot00000000000000// +build go1.7 package tlsconfig import ( "crypto/x509" "runtime" ) // SystemCertPool returns a copy of the system cert pool, // returns an error if failed to load or empty pool on windows. func SystemCertPool() (*x509.CertPool, error) { certpool, err := x509.SystemCertPool() if err != nil && runtime.GOOS == "windows" { return x509.NewCertPool(), nil } return certpool, err } go-connections-0.3.0/tlsconfig/certpool_other.go000066400000000000000000000003721312327573300220160ustar00rootroot00000000000000// +build !go1.7 package tlsconfig import ( "crypto/x509" ) // SystemCertPool returns an new empty cert pool, // accessing system cert pool is supported in go 1.7 func SystemCertPool() (*x509.CertPool, error) { return x509.NewCertPool(), nil } go-connections-0.3.0/tlsconfig/config.go000066400000000000000000000173201312327573300202340ustar00rootroot00000000000000// Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers. // // As a reminder from https://golang.org/pkg/crypto/tls/#Config: // A Config structure is used to configure a TLS client or server. After one has been passed to a TLS function it must not be modified. // A Config may be reused; the tls package will also not modify it. package tlsconfig import ( "crypto/tls" "crypto/x509" "encoding/pem" "fmt" "io/ioutil" "os" "github.com/pkg/errors" ) // Options represents the information needed to create client and server TLS configurations. type Options struct { CAFile string // If either CertFile or KeyFile is empty, Client() will not load them // preventing the client from authenticating to the server. // However, Server() requires them and will error out if they are empty. CertFile string KeyFile string // client-only option InsecureSkipVerify bool // server-only option ClientAuth tls.ClientAuthType // If ExclusiveRootPools is set, then if a CA file is provided, the root pool used for TLS // creds will include exclusively the roots in that CA file. If no CA file is provided, // the system pool will be used. ExclusiveRootPools bool MinVersion uint16 // If Passphrase is set, it will be used to decrypt a TLS private key // if the key is encrypted Passphrase string } // Extra (server-side) accepted CBC cipher suites - will phase out in the future var acceptedCBCCiphers = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, tls.TLS_RSA_WITH_AES_256_CBC_SHA, tls.TLS_RSA_WITH_AES_128_CBC_SHA, } // DefaultServerAcceptedCiphers should be uses by code which already has a crypto/tls // options struct but wants to use a commonly accepted set of TLS cipher suites, with // known weak algorithms removed. var DefaultServerAcceptedCiphers = append(clientCipherSuites, acceptedCBCCiphers...) // allTLSVersions lists all the TLS versions and is used by the code that validates // a uint16 value as a TLS version. var allTLSVersions = map[uint16]struct{}{ tls.VersionSSL30: {}, tls.VersionTLS10: {}, tls.VersionTLS11: {}, tls.VersionTLS12: {}, } // ServerDefault returns a secure-enough TLS configuration for the server TLS configuration. func ServerDefault() *tls.Config { return &tls.Config{ // Avoid fallback to SSL protocols < TLS1.0 MinVersion: tls.VersionTLS10, PreferServerCipherSuites: true, CipherSuites: DefaultServerAcceptedCiphers, } } // ClientDefault returns a secure-enough TLS configuration for the client TLS configuration. func ClientDefault() *tls.Config { return &tls.Config{ // Prefer TLS1.2 as the client minimum MinVersion: tls.VersionTLS12, CipherSuites: clientCipherSuites, } } // certPool returns an X.509 certificate pool from `caFile`, the certificate file. func certPool(caFile string, exclusivePool bool) (*x509.CertPool, error) { // If we should verify the server, we need to load a trusted ca var ( certPool *x509.CertPool err error ) if exclusivePool { certPool = x509.NewCertPool() } else { certPool, err = SystemCertPool() if err != nil { return nil, fmt.Errorf("failed to read system certificates: %v", err) } } pem, err := ioutil.ReadFile(caFile) if err != nil { return nil, fmt.Errorf("could not read CA certificate %q: %v", caFile, err) } if !certPool.AppendCertsFromPEM(pem) { return nil, fmt.Errorf("failed to append certificates from PEM file: %q", caFile) } return certPool, nil } // isValidMinVersion checks that the input value is a valid tls minimum version func isValidMinVersion(version uint16) bool { _, ok := allTLSVersions[version] return ok } // adjustMinVersion sets the MinVersion on `config`, the input configuration. // It assumes the current MinVersion on the `config` is the lowest allowed. func adjustMinVersion(options Options, config *tls.Config) error { if options.MinVersion > 0 { if !isValidMinVersion(options.MinVersion) { return fmt.Errorf("Invalid minimum TLS version: %x", options.MinVersion) } if options.MinVersion < config.MinVersion { return fmt.Errorf("Requested minimum TLS version is too low. Should be at-least: %x", config.MinVersion) } config.MinVersion = options.MinVersion } return nil } // IsErrEncryptedKey returns true if the 'err' is an error of incorrect // password when tryin to decrypt a TLS private key func IsErrEncryptedKey(err error) bool { return errors.Cause(err) == x509.IncorrectPasswordError } // getPrivateKey returns the private key in 'keyBytes', in PEM-encoded format. // If the private key is encrypted, 'passphrase' is used to decrypted the // private key. func getPrivateKey(keyBytes []byte, passphrase string) ([]byte, error) { // this section makes some small changes to code from notary/tuf/utils/x509.go pemBlock, _ := pem.Decode(keyBytes) if pemBlock == nil { return nil, fmt.Errorf("no valid private key found") } var err error if x509.IsEncryptedPEMBlock(pemBlock) { keyBytes, err = x509.DecryptPEMBlock(pemBlock, []byte(passphrase)) if err != nil { return nil, errors.Wrap(err, "private key is encrypted, but could not decrypt it") } keyBytes = pem.EncodeToMemory(&pem.Block{Type: pemBlock.Type, Bytes: keyBytes}) } return keyBytes, nil } // getCert returns a Certificate from the CertFile and KeyFile in 'options', // if the key is encrypted, the Passphrase in 'options' will be used to // decrypt it. func getCert(options Options) ([]tls.Certificate, error) { if options.CertFile == "" && options.KeyFile == "" { return nil, nil } errMessage := "Could not load X509 key pair" cert, err := ioutil.ReadFile(options.CertFile) if err != nil { return nil, errors.Wrap(err, errMessage) } prKeyBytes, err := ioutil.ReadFile(options.KeyFile) if err != nil { return nil, errors.Wrap(err, errMessage) } prKeyBytes, err = getPrivateKey(prKeyBytes, options.Passphrase) if err != nil { return nil, errors.Wrap(err, errMessage) } tlsCert, err := tls.X509KeyPair(cert, prKeyBytes) if err != nil { return nil, errors.Wrap(err, errMessage) } return []tls.Certificate{tlsCert}, nil } // Client returns a TLS configuration meant to be used by a client. func Client(options Options) (*tls.Config, error) { tlsConfig := ClientDefault() tlsConfig.InsecureSkipVerify = options.InsecureSkipVerify if !options.InsecureSkipVerify && options.CAFile != "" { CAs, err := certPool(options.CAFile, options.ExclusiveRootPools) if err != nil { return nil, err } tlsConfig.RootCAs = CAs } tlsCerts, err := getCert(options) if err != nil { return nil, err } tlsConfig.Certificates = tlsCerts if err := adjustMinVersion(options, tlsConfig); err != nil { return nil, err } return tlsConfig, nil } // Server returns a TLS configuration meant to be used by a server. func Server(options Options) (*tls.Config, error) { tlsConfig := ServerDefault() tlsConfig.ClientAuth = options.ClientAuth tlsCert, err := tls.LoadX509KeyPair(options.CertFile, options.KeyFile) if err != nil { if os.IsNotExist(err) { return nil, fmt.Errorf("Could not load X509 key pair (cert: %q, key: %q): %v", options.CertFile, options.KeyFile, err) } return nil, fmt.Errorf("Error reading X509 key pair (cert: %q, key: %q): %v. Make sure the key is not encrypted.", options.CertFile, options.KeyFile, err) } tlsConfig.Certificates = []tls.Certificate{tlsCert} if options.ClientAuth >= tls.VerifyClientCertIfGiven && options.CAFile != "" { CAs, err := certPool(options.CAFile, options.ExclusiveRootPools) if err != nil { return nil, err } tlsConfig.ClientCAs = CAs } if err := adjustMinVersion(options, tlsConfig); err != nil { return nil, err } return tlsConfig, nil } go-connections-0.3.0/tlsconfig/config_client_ciphers.go000066400000000000000000000007401312327573300233050ustar00rootroot00000000000000// +build go1.5 // Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers. // package tlsconfig import ( "crypto/tls" ) // Client TLS cipher suites (dropping CBC ciphers for client preferred suite set) var clientCipherSuites = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, } go-connections-0.3.0/tlsconfig/config_legacy_client_ciphers.go000066400000000000000000000006071312327573300246330ustar00rootroot00000000000000// +build !go1.5 // Package tlsconfig provides primitives to retrieve secure-enough TLS configurations for both clients and servers. // package tlsconfig import ( "crypto/tls" ) // Client TLS cipher suites (dropping CBC ciphers for client preferred suite set) var clientCipherSuites = []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, } go-connections-0.3.0/tlsconfig/config_test.go000066400000000000000000000463461312327573300213050ustar00rootroot00000000000000package tlsconfig import ( "bytes" "crypto/tls" "crypto/x509" "encoding/pem" "io/ioutil" "os" "reflect" "testing" ) // This is the currently active LetsEncrypt IdenTrust cross-signed CA cert. It expires Mar 17, 2021. const ( systemRootTrustedCert = ` -----BEGIN CERTIFICATE----- MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj /PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== -----END CERTIFICATE----- ` rsaPrivateKeyFile = "fixtures/key.pem" certificateFile = "fixtures/cert.pem" multiCertificateFile = "fixtures/multi.pem" rsaEncryptedPrivateKeyFile = "fixtures/encrypted_key.pem" certificateOfEncryptedKeyFile = "fixtures/cert_of_encrypted_key.pem" ) // returns the name of a pre-generated, multiple-certificate CA file // with both RSA and ECDSA certs. func getMultiCert() string { return multiCertificateFile } // returns the names of pre-generated key and certificate files. func getCertAndKey() (string, string) { return rsaPrivateKeyFile, certificateFile } // returns the names of pre-generated, encrypted private key and // corresponding certificate file func getCertAndEncryptedKey() (string, string) { return rsaEncryptedPrivateKeyFile, certificateOfEncryptedKeyFile } // If the cert files and directory are provided but are invalid, an error is // returned. func TestConfigServerTLSFailsIfUnableToLoadCerts(t *testing.T) { key, cert := getCertAndKey() ca := getMultiCert() tempFile, err := ioutil.TempFile("", "cert-test") if err != nil { t.Fatal("Unable to create temporary empty file") } defer os.RemoveAll(tempFile.Name()) tempFile.Close() for _, badFile := range []string{"not-a-file", tempFile.Name()} { for i := 0; i < 3; i++ { files := []string{cert, key, ca} files[i] = badFile result, err := Server(Options{ CertFile: files[0], KeyFile: files[1], CAFile: files[2], ClientAuth: tls.VerifyClientCertIfGiven, }) if err == nil || result != nil { t.Fatal("Expected a non-real file to error and return a nil TLS config") } } } } // If server cert and key are provided and client auth and client CA are not // set, a tls config with only the server certs will be returned. func TestConfigServerTLSServerCertsOnly(t *testing.T) { key, cert := getCertAndKey() keypair, err := tls.LoadX509KeyPair(cert, key) if err != nil { t.Fatal("Unable to load the generated cert and key") } tlsConfig, err := Server(Options{ CertFile: cert, KeyFile: key, }) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure server TLS", err) } if len(tlsConfig.Certificates) != 1 { t.Fatal("Unexpected server certificates") } if len(tlsConfig.Certificates[0].Certificate) != len(keypair.Certificate) { t.Fatal("Unexpected server certificates") } for i, cert := range tlsConfig.Certificates[0].Certificate { if !bytes.Equal(cert, keypair.Certificate[i]) { t.Fatal("Unexpected server certificates") } } if !reflect.DeepEqual(tlsConfig.CipherSuites, DefaultServerAcceptedCiphers) { t.Fatal("Unexpected server cipher suites") } if !tlsConfig.PreferServerCipherSuites { t.Fatal("Expected server to prefer cipher suites") } if tlsConfig.MinVersion != tls.VersionTLS10 { t.Fatal("Unexpected server TLS version") } } // If client CA is provided, it will only be used if the client auth is >= // VerifyClientCertIfGiven func TestConfigServerTLSClientCANotSetIfClientAuthTooLow(t *testing.T) { key, cert := getCertAndKey() ca := getMultiCert() tlsConfig, err := Server(Options{ CertFile: cert, KeyFile: key, ClientAuth: tls.RequestClientCert, CAFile: ca, }) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure server TLS", err) } if len(tlsConfig.Certificates) != 1 { t.Fatal("Unexpected server certificates") } if tlsConfig.ClientAuth != tls.RequestClientCert { t.Fatal("ClientAuth was not set to what was in the options") } if tlsConfig.ClientCAs != nil { t.Fatalf("Client CAs should never have been set") } } // If client CA is provided, it will only be used if the client auth is >= // VerifyClientCertIfGiven func TestConfigServerTLSClientCASet(t *testing.T) { key, cert := getCertAndKey() ca := getMultiCert() tlsConfig, err := Server(Options{ CertFile: cert, KeyFile: key, ClientAuth: tls.VerifyClientCertIfGiven, CAFile: ca, }) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure server TLS", err) } if len(tlsConfig.Certificates) != 1 { t.Fatal("Unexpected server certificates") } if tlsConfig.ClientAuth != tls.VerifyClientCertIfGiven { t.Fatal("ClientAuth was not set to what was in the options") } basePool, err := SystemCertPool() if err != nil { basePool = x509.NewCertPool() } // because we are not enabling `ExclusiveRootPools`, any root pool will also contain the system roots if tlsConfig.ClientCAs == nil || len(tlsConfig.ClientCAs.Subjects()) != len(basePool.Subjects())+2 { t.Fatalf("Client CAs were never set correctly") } } // Exclusive root pools determines whether the CA pool will be a union of the system // certificate pool and custom certs, or an exclusive or of the custom certs and system pool func TestConfigServerExclusiveRootPools(t *testing.T) { key, cert := getCertAndKey() ca := getMultiCert() caBytes, err := ioutil.ReadFile(ca) if err != nil { t.Fatal("Unable to read CA certs", err) } var testCerts []*x509.Certificate for _, pemBytes := range [][]byte{caBytes, []byte(systemRootTrustedCert)} { pemBlock, _ := pem.Decode(pemBytes) if pemBlock == nil { t.Fatal("Malformed certificate") } cert, err := x509.ParseCertificate(pemBlock.Bytes) if err != nil { t.Fatal("Unable to parse certificate") } testCerts = append(testCerts, cert) } // ExclusiveRootPools not set, so should be able to verify both system-signed certs // and custom CA-signed certs tlsConfig, err := Server(Options{ CertFile: cert, KeyFile: key, ClientAuth: tls.VerifyClientCertIfGiven, CAFile: ca, }) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure server TLS", err) } for i, cert := range testCerts { if _, err := cert.Verify(x509.VerifyOptions{Roots: tlsConfig.ClientCAs}); err != nil { t.Fatalf("Unable to verify certificate %d: %v", i, err) } } // ExclusiveRootPools set and custom CA provided, so system certs should not be verifiable // and custom CA-signed certs should be verifiable tlsConfig, err = Server(Options{ CertFile: cert, KeyFile: key, ClientAuth: tls.VerifyClientCertIfGiven, CAFile: ca, ExclusiveRootPools: true, }) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure server TLS", err) } for i, cert := range testCerts { _, err := cert.Verify(x509.VerifyOptions{Roots: tlsConfig.ClientCAs}) switch { case i == 0 && err != nil: t.Fatal("Unable to verify custom certificate, even though the root pool should have only the custom CA", err) case i == 1 && err == nil: t.Fatal("Successfully verified system root-signed certificate though the root pool should have only the cusotm CA", err) } } // No CA file provided, system cert should be verifiable only tlsConfig, err = Server(Options{ CertFile: cert, KeyFile: key, }) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure server TLS", err) } for i, cert := range testCerts { _, err := cert.Verify(x509.VerifyOptions{Roots: tlsConfig.ClientCAs}) switch { case i == 1 && err != nil: t.Fatal("Unable to verify system root-signed certificate, even though the root pool should be the system pool only", err) case i == 0 && err == nil: t.Fatal("Successfully verified custom certificate though the root pool should be the system pool only", err) } } } // If a valid minimum version is specified in the options, the server's // minimum version should be set accordingly func TestConfigServerTLSMinVersionIsSetBasedOnOptions(t *testing.T) { versions := []uint16{ tls.VersionTLS11, tls.VersionTLS12, } key, cert := getCertAndKey() for _, v := range versions { tlsConfig, err := Server(Options{ MinVersion: v, CertFile: cert, KeyFile: key, }) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure server TLS", err) } if tlsConfig.MinVersion != v { t.Fatal("Unexpected minimum TLS version: ", tlsConfig.MinVersion) } } } // An error should be returned if the specified minimum version for the server // is too low, i.e. less than VersionTLS10 func TestConfigServerTLSMinVersionNotSetIfMinVersionIsTooLow(t *testing.T) { key, cert := getCertAndKey() _, err := Server(Options{ MinVersion: tls.VersionSSL30, CertFile: cert, KeyFile: key, }) if err == nil { t.Fatal("Should have returned an error for minimum version below TLS10") } } // An error should be returned if an invalid minimum version for the server is // in the options struct func TestConfigServerTLSMinVersionNotSetIfMinVersionIsInvalid(t *testing.T) { key, cert := getCertAndKey() _, err := Server(Options{ MinVersion: 1, CertFile: cert, KeyFile: key, }) if err == nil { t.Fatal("Should have returned error on invalid minimum version option") } } // The root CA is never set if InsecureSkipBoolean is set to true, but the // default client options are set func TestConfigClientTLSNoVerify(t *testing.T) { ca := getMultiCert() tlsConfig, err := Client(Options{CAFile: ca, InsecureSkipVerify: true}) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure client TLS", err) } if tlsConfig.RootCAs != nil { t.Fatal("Should not have set Root CAs", err) } if !reflect.DeepEqual(tlsConfig.CipherSuites, clientCipherSuites) { t.Fatal("Unexpected client cipher suites") } if tlsConfig.MinVersion != tls.VersionTLS12 { t.Fatal("Unexpected client TLS version") } if tlsConfig.Certificates != nil { t.Fatal("Somehow client certificates were set") } } // The root CA is never set if InsecureSkipBoolean is set to false and root CA // is not provided. func TestConfigClientTLSNoRoot(t *testing.T) { tlsConfig, err := Client(Options{}) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure client TLS", err) } if tlsConfig.RootCAs != nil { t.Fatal("Should not have set Root CAs", err) } if !reflect.DeepEqual(tlsConfig.CipherSuites, clientCipherSuites) { t.Fatal("Unexpected client cipher suites") } if tlsConfig.MinVersion != tls.VersionTLS12 { t.Fatal("Unexpected client TLS version") } if tlsConfig.Certificates != nil { t.Fatal("Somehow client certificates were set") } } // The RootCA is set if the file is provided and InsecureSkipVerify is false func TestConfigClientTLSRootCAFileWithOneCert(t *testing.T) { ca := getMultiCert() tlsConfig, err := Client(Options{CAFile: ca}) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure client TLS", err) } basePool, err := SystemCertPool() if err != nil { basePool = x509.NewCertPool() } // because we are not enabling `ExclusiveRootPools`, any root pool will also contain the system roots if tlsConfig.RootCAs == nil || len(tlsConfig.RootCAs.Subjects()) != len(basePool.Subjects())+2 { t.Fatal("Root CAs not set properly", err) } if tlsConfig.Certificates != nil { t.Fatal("Somehow client certificates were set") } } // An error is returned if a root CA is provided but the file doesn't exist. func TestConfigClientTLSNonexistentRootCAFile(t *testing.T) { tlsConfig, err := Client(Options{CAFile: "nonexistent"}) if err == nil || tlsConfig != nil { t.Fatal("Should not have been able to configure client TLS", err) } } // An error is returned if either the client cert or the key are provided // but invalid or blank. func TestConfigClientTLSClientCertOrKeyInvalid(t *testing.T) { key, cert := getCertAndKey() tempFile, err := ioutil.TempFile("", "cert-test") if err != nil { t.Fatal("Unable to create temporary empty file") } defer os.Remove(tempFile.Name()) tempFile.Close() for i := 0; i < 2; i++ { for _, invalid := range []string{"not-a-file", "", tempFile.Name()} { files := []string{cert, key} files[i] = invalid tlsConfig, err := Client(Options{CertFile: files[0], KeyFile: files[1]}) if err == nil || tlsConfig != nil { t.Fatal("Should not have been able to configure client TLS", err) } } } } // The certificate is set if the client cert and client key are provided and // valid. func TestConfigClientTLSValidClientCertAndKey(t *testing.T) { key, cert := getCertAndKey() keypair, err := tls.LoadX509KeyPair(cert, key) if err != nil { t.Fatal("Unable to load the generated cert and key") } tlsConfig, err := Client(Options{CertFile: cert, KeyFile: key}) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure client TLS", err) } if len(tlsConfig.Certificates) != 1 { t.Fatal("Unexpected client certificates") } if len(tlsConfig.Certificates[0].Certificate) != len(keypair.Certificate) { t.Fatal("Unexpected client certificates") } for i, cert := range tlsConfig.Certificates[0].Certificate { if !bytes.Equal(cert, keypair.Certificate[i]) { t.Fatal("Unexpected client certificates") } } if tlsConfig.RootCAs != nil { t.Fatal("Root CAs should not have been set", err) } } // The certificate is set if the client cert and encrypted client key are // provided and valid and passphrase can decrypt the key func TestConfigClientTLSValidClientCertAndEncryptedKey(t *testing.T) { key, cert := getCertAndEncryptedKey() tlsConfig, err := Client(Options{ CertFile: cert, KeyFile: key, Passphrase: "FooBar123", }) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure client TLS", err) } if len(tlsConfig.Certificates) != 1 { t.Fatal("Unexpected client certificates") } } // The certificate is not set if the provided passphrase cannot decrypt // the encrypted key. func TestConfigClientTLSNotSetWithInvalidPassphrase(t *testing.T) { key, cert := getCertAndEncryptedKey() tlsConfig, err := Client(Options{ CertFile: cert, KeyFile: key, Passphrase: "InvalidPassphrase", }) if !IsErrEncryptedKey(err) || tlsConfig != nil { t.Fatal("Expected failure due to incorrect passphrase.") } } // Exclusive root pools determines whether the CA pool will be a union of the system // certificate pool and custom certs, or an exclusive or of the custom certs and system pool func TestConfigClientExclusiveRootPools(t *testing.T) { ca := getMultiCert() caBytes, err := ioutil.ReadFile(ca) if err != nil { t.Fatal("Unable to read CA certs", err) } var testCerts []*x509.Certificate for _, pemBytes := range [][]byte{caBytes, []byte(systemRootTrustedCert)} { pemBlock, _ := pem.Decode(pemBytes) if pemBlock == nil { t.Fatal("Malformed certificate") } cert, err := x509.ParseCertificate(pemBlock.Bytes) if err != nil { t.Fatal("Unable to parse certificate") } testCerts = append(testCerts, cert) } // ExclusiveRootPools not set, so should be able to verify both system-signed certs // and custom CA-signed certs tlsConfig, err := Client(Options{CAFile: ca}) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure client TLS", err) } for i, cert := range testCerts { if _, err := cert.Verify(x509.VerifyOptions{Roots: tlsConfig.RootCAs}); err != nil { t.Fatalf("Unable to verify certificate %d: %v", i, err) } } // ExclusiveRootPools set and custom CA provided, so system certs should not be verifiable // and custom CA-signed certs should be verifiable tlsConfig, err = Client(Options{ CAFile: ca, ExclusiveRootPools: true, }) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure client TLS", err) } for i, cert := range testCerts { _, err := cert.Verify(x509.VerifyOptions{Roots: tlsConfig.RootCAs}) switch { case i == 0 && err != nil: t.Fatal("Unable to verify custom certificate, even though the root pool should have only the custom CA", err) case i == 1 && err == nil: t.Fatal("Successfully verified system root-signed certificate though the root pool should have only the cusotm CA", err) } } // No CA file provided, system cert should be verifiable only tlsConfig, err = Client(Options{}) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure client TLS", err) } for i, cert := range testCerts { _, err := cert.Verify(x509.VerifyOptions{Roots: tlsConfig.RootCAs}) switch { case i == 1 && err != nil: t.Fatal("Unable to verify system root-signed certificate, even though the root pool should be the system pool only", err) case i == 0 && err == nil: t.Fatal("Successfully verified custom certificate though the root pool should be the system pool only", err) } } } // If a valid MinVersion is specified in the options, the client's // minimum version should be set accordingly func TestConfigClientTLSMinVersionIsSetBasedOnOptions(t *testing.T) { key, cert := getCertAndKey() tlsConfig, err := Client(Options{ MinVersion: tls.VersionTLS12, CertFile: cert, KeyFile: key, }) if err != nil || tlsConfig == nil { t.Fatal("Unable to configure client TLS", err) } if tlsConfig.MinVersion != tls.VersionTLS12 { t.Fatal("Unexpected minimum TLS version: ", tlsConfig.MinVersion) } } // An error should be returned if the specified minimum version for the client // is too low, i.e. less than VersionTLS12 func TestConfigClientTLSMinVersionNotSetIfMinVersionIsTooLow(t *testing.T) { key, cert := getCertAndKey() _, err := Client(Options{ MinVersion: tls.VersionTLS11, CertFile: cert, KeyFile: key, }) if err == nil { t.Fatal("Should have returned an error for minimum version below TLS12") } } // An error should be returned if an invalid minimum version for the client is // in the options struct func TestConfigClientTLSMinVersionNotSetIfMinVersionIsInvalid(t *testing.T) { key, cert := getCertAndKey() _, err := Client(Options{ MinVersion: 1, CertFile: cert, KeyFile: key, }) if err == nil { t.Fatal("Should have returned error on invalid minimum version option") } } go-connections-0.3.0/tlsconfig/fixtures/000077500000000000000000000000001312327573300203065ustar00rootroot00000000000000go-connections-0.3.0/tlsconfig/fixtures/cert.pem000066400000000000000000000020261312327573300217460ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIC1jCCAb6gAwIBAgIDAw0/MA0GCSqGSIb3DQEBCwUAMA8xDTALBgNVBAMTBHRl c3QwHhcNMTYwMzI4MTg0MTQ3WhcNMjcwMzI4MTg0MTQ3WjAPMQ0wCwYDVQQDEwR0 ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1k1NO4wzCpxZ71Bo SiYSWh8SE9jHtg6lz0QjMQXzFuLhpedjHJYx9fYbD+JVk5vnRbUqNUeZVKAGahfR 9vhm5I+cm359gYU0gHawLw91oh4JCiwUu77U2obHvtvcXLf6Fb/+MoSA5wH7vbL3 T4vR1+hLt+R+kILAEHq/IlSdLD8CA0iA+ypHfCPOi5F2wVjAyMnQXgVDkAhzefpu JkhN1yUgb5WK4qoSuOUDUYq/bRosLdHXDJiWRuqaU2zxO5cHVlrNAE5RuspfEzl4 YP6boZTOomLEDbBTSJWgX2/ybvY7o4sCw7KrvyBIqSK9HbfaK1nFMFGoiSH6+1m4 amWKrwIDAQABozswOTAOBgNVHQ8BAf8EBAMCBaAwGQYDVR0lBBIwEAYIKwYBBQUH AwMGBFUdJQAwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEADuXjLtGk tU5ql+LFB32Cc2Laa0iO8aqJccOcXYKg4FD0um+1+YQO1CBZZqWjItH4CuJl5+2j Tc9sFgrIVH5CmvUkOUFPCNDAJtxBvF6RQqRpehjheHDaNsYo9JNKHKEJB6OJrDgy N5krM5FKyAp/EDTbIrGIZFMdxQGxK5MfpfPkKK44JgOQM3QWeR+LqIpfd34MD1jZ jjYdl0+quIHiIdFR0a4Uam7o9GfUmcWe1VFthLb5pNhV6t+wyuLyMXVMNacKZSz/ nOMWVQfgViZk6rHOPSMrFMc7Pp488I907MJKCryd21LcLqMuhb4BpWcJghnY8Lbs uIPLsUHr3Pfp9Q== -----END CERTIFICATE----- go-connections-0.3.0/tlsconfig/fixtures/cert_of_encrypted_key.pem000066400000000000000000000020261312327573300253570ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIC1jCCAb6gAwIBAgIDAw0/MA0GCSqGSIb3DQEBCwUAMA8xDTALBgNVBAMTBHRl c3QwHhcNMTYwNDIyMDQyMjM1WhcNMTgwNDIyMDQyMjM1WjAPMQ0wCwYDVQQDEwR0 ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4GRTos+Ik6kQG7wn 8E4HqPwgWXbY0T59UQsrbR+YbyxbUKV67Pgl4VImuUmYaism6Tm3EFYzeom5baMc vW0hC+WbwVr1rq5ddBE8akYhlPY40SxFlh563vOi7lcFGM7xuUbTlhtAhYa5xc5U thHYa8Mdqc2kMrmU4JBhNHoRk2mnRBo2J2/8RfOfioM6mH0t/MVtB/jSGpcwbbfj 2twKOpB9CoX57szVo7+DCFHpLxeuop+69REu5Egc2a5BtBuUf0fkUBKuF7yUy2xI IbgjCiGb3Z+PCIC0CjNt9wExowPAGfxAJ8s1nNlpZav3707VZRtz7Js1skRjm9aU 8fhYNQIDAQABozswOTAOBgNVHQ8BAf8EBAMCBaAwGQYDVR0lBBIwEAYIKwYBBQUH AwMGBFUdJQAwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAcKCCV5Os O2U7Ekp0jzOusV2+ykZzUe4sEds+ikblxK9SHV/pAPIVuAevdyE1LKmJ6ZGgeU2M 4MC6jC/XTlYNhsYCfKaJn53UscKI2urXFlk1Gv5VQP5EOrMWb76A5uj1nElxKe2C bMVoUuMwRd9jnz6594D80jGGYpHRaF7yLtGbiflDjB+yv1OU6WnuVNr0nOb9ShR6 WPlrQj5TUSpRHF/oKy9LVWuxYA9aiY1YREDZhhauw9pGAMx1lImfJcJ077MdxN4A DwKAx3ooajAu1n3McY1oncWW+rWs2Ptvp6lKMGoZ50ElEPCMw4/hPtPMLq/DTWNj l342KLVWgchlIA== -----END CERTIFICATE----- go-connections-0.3.0/tlsconfig/fixtures/encrypted_key.pem000066400000000000000000000033461312327573300236640ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-256-CBC,68ce1d54f187b663e152d9c5dc900fd3 ZVBeXx7kWiF0yPOORntrN6BsyIJE7krqTVhRfk6GAllaLQv0jvb31XHB1oWOaqnx tb7kUuoBeQdl1hs/iAnkDMc59WJfEK9A9cAD/SgxTgdENOrzFSRNEfqketLA4eHZ 2sOLkSfv58HwA0p0gzqSrLQBo/6ZtF/57HxH166PtErPNTS1Usu/f4Oj0UqxTfbZ B5LHsepyNLt6q/15fcY0TFYJwvgEXa4SridjT+8bTz2T+bx3QFijGnl7EdkTElni FIwnDjFZaAULqoyUIB1y8guEZVkaWKncxPdRfhId84HklWdrrLtP5D6db1xNNpsp LzGdciD3phJp6K0hpl+WrhYxuCKURa27tXMCuYOFd1hw/kM29jFbxSIlNBGN4OLL v4wYrJFM21iWsz9c7Cqw5Yls2Rsx0QrXRFIxwT25z+HNx1fysQxYuxf3r+e2oz8e 8Os7hvcxG2XDz01/zpx8kzxUcLuh+3o5UOYlo9z6qsjaD5NUXY+X90PUrVO9fk5y 8o8pnElPnV88Ihrog5YTYy6egiQWHhDk2I4qlYPOBQNKTLg3KulAcmC9vQ8mR5Sy p3c3MTgh0A3Zk5Dib+sQ0tdbwDcB2JCTqGal1FNEW5Z7qTHA4Bdm2l7hGs8cRpy4 Ehkhv3s5wWmKcbwwlPuJ0UfPeDn6v9qE2/IkOy+jWgTpaFyWtXHc1/XdqMsJ8xN0 thJw/GMtNabB1+zuayJnvmbJd2qW1smsFTHqX3BovXIH4vx1hE2d0lJpEBynk+wr gpPgrRoEiqsPcsRoVjvKH3qwJLRdcGYhKqhbvRdynlagCLmE8iAI99r82u6t+03h YNpRbafY4ceAYyK0IlRiJvGkBMfH7bMXcBMmXyQSBF27ZpNidyZSCHrU5xyHqJZO XWUhl9GHplBfueh5E831S7mDqobd8RqnUvKVygyEOol5VUFDrggTAAKKN9VzM3uT MaVymt6fA7stzf01fT+Wi7uCm5legTXG3Ca+XxD6TdE0dNzewd5jDsuqwXnt1iC4 slvuLRZeRZDNvBd0G7Ohhp6jb2HHwkv9kQTZ+UEDbR/Gwxty4oT1MnwSE0mi9ZFN 6PTjrSxpIKe+mAhgzrepLMfATGayYQzucEArPG7Vp+NJva+j6FKloqrzXMjlP0hN XSBr7AL+j+OR/tzDOoUG3xdsCl/u5hFTpjsW2ti870zoRUcK0fqJ9UIYjh66L7yT KNkXsC+OcGuGkhtQ0gxx60OI7wp4bh2pKdT6e111/WTvXxVR2C3XhFBLUfNIz/7A Oj+s0CaV4pBmCjIobLYpxC0ofLplwBLGf9xnsBiQF5dsgKgOhACeDmDMwqAJ3U/t 54hK/8Yb8W46Tjgbm0Qsj5gFXHofnyqDeQxAjsdCXsdMaPB8nyZpEkuQSEj9HlKW xIEErVufkvqyrzhX1pxPs+C839Ueyeob6ZWQurqCLTdZh+3bhKcvi5iP+aLLjMWK JT9tmAuFVkbPerqObVQFbnM4/re33YYD7QXCqta5bxcVeBI8N1HdwMYrDVhXelEx mqGleUkkDHTWzAa3u1GKOzLXAYnD0TsTwml0+k+Rf0QMBiDJiKujfy7fGqfZF2vR -----END RSA PRIVATE KEY----- go-connections-0.3.0/tlsconfig/fixtures/key.pem000066400000000000000000000032171312327573300216040ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEA1k1NO4wzCpxZ71BoSiYSWh8SE9jHtg6lz0QjMQXzFuLhpedj HJYx9fYbD+JVk5vnRbUqNUeZVKAGahfR9vhm5I+cm359gYU0gHawLw91oh4JCiwU u77U2obHvtvcXLf6Fb/+MoSA5wH7vbL3T4vR1+hLt+R+kILAEHq/IlSdLD8CA0iA +ypHfCPOi5F2wVjAyMnQXgVDkAhzefpuJkhN1yUgb5WK4qoSuOUDUYq/bRosLdHX DJiWRuqaU2zxO5cHVlrNAE5RuspfEzl4YP6boZTOomLEDbBTSJWgX2/ybvY7o4sC w7KrvyBIqSK9HbfaK1nFMFGoiSH6+1m4amWKrwIDAQABAoIBAQC802wj9grbZJzS A1WBUD6Hbi0tk6uVPR7YnD8t6QIivlL5LgLko2ruQKXjvxiMcai8gT7pp2bxa/d6 7/Yv2PxAlFH3qOLJhyeVsf7X2JVb/X8VmXXDYAiJbI0AHRX0FJ+lHoDK3nn+En9Q zSqgyqBhz+s343uptauqWZ2kkE3VNyqlPBhmKc5NcbR7Sgb4nJ3CkNAcxRkl1NeI BRFdsTUYRNR3Vd++OvOzI4uzZfCIeUVqx+r7/SeLW0UwqeprMm7g+hFQLfH+e9SA 9lx0EIRoQFwgvKju2eogpSwvkSlObXnESu5OHYtnc+jpsOC0EbQgO0d6CqVZiqjR 2dRYsZkhAoGBAO69loXSAsyqUj0rT5iq59PuMlBEAlW6hQTfl6c8bnu1JUo2s/CH OJfswxfHN32qmi99WbK2iLyrnznNYsyPnYKW0ObwuoqAdrlydfu7Fq9HSOACoIvK jRMOsiJtM3JX2bHHV7yIwJ1+h++o2Ly803j7tKtYsrRQVZiWeTcR2IRZAoGBAOXL bJFLbAhm3zRqhbiWuORqqyLxrDmIB6RY8vTdX47vwzkFGZJlCuL+vs6877I6eOc9 wjH9qcOiJQJ4DWkAE+VS5PAPoj0UDRw7AkE9v3RwnmxvAfP5rPo5KimYxKq4yX6r +Qc4ixwftCj0rxFoG4lnipwBFq4NXuHtIhbZXMZHAoGBAOGfatGtV9f0XyRP+jld yxoO0p3oqAw86dlhNgFmq0NePo+UgxmdsW5i4z1lmJu6z1xyKoMq3q7vwtrtr6GD WGhB/8tBVgnuvkUkVzw/44Bi7gxGb1OtaQXJra+7ZBN70tCgg9o5o080dWOZPruf +Hst5eDJQpoGEd7S1lulEeqBAoGBAKAqdIak6izE/wg6wu+Q5lgW3SejCOakoKb1 dIoljkhDZ2/j1RoLoVXsNzRDzlIMnV6X1jYf1ubLqj4ZTUeFTVjGuVl1nCA0TJsD qiOtFTfkkxeDG/pgaSeTFocdut4/o/nNhep5h8RXeKwfN7LLPH4+FAd+Xr98BEk2 jk8cu6RbAoGAHI9yRXKjlADBZLvxxMGHRfe7eK4PgABmluZLdsXzNmXxybrZDvdC teipvIUSym7tvdDB6LHXKVp4mYeqHe/ktRatlhbQyPso2VPoMFQyuRBYKKFFAh0V 3d6EyTRnIxn/NW+XdcCUeufFfd+3BHyux68PyUsTtKRCJYfhExzJf70= -----END RSA PRIVATE KEY----- go-connections-0.3.0/tlsconfig/fixtures/multi.pem000066400000000000000000000030401312327573300221400ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIC3DCCAcSgAwIBAgIDAw0/MA0GCSqGSIb3DQEBCwUAMA8xDTALBgNVBAMTBHRl c3QwHhcNMTYwMzI4MTg0MTQ3WhcNMjcwMzI4MTg0MTQ3WjAPMQ0wCwYDVQQDEwR0 ZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArVIJDnNnM1iX7Xj8 bja4WsgHuENRBsBCROTDjQL1w7Ksin2jmCl/D7Gk9ifRJZ/HPE3BKo6B+3CDXygJ Qvoe8SGWi6ae8lN4VgPoW7xDViAWhVmjIr+dNQXWD0hCq0YZuXyYSi5iXWeRaTvx 2eoG2VSkNnkc/0weEhX1nBGBscuz1UZqWp53m09eL7otngcNcdjmvLPiw4E3cric UoLVonzf4ZE84Q7nNmfWfMKh4zJUyn8N766GAAoC6RAKsJ0xSDeRjkzSy7vGJKBv nTBe6X1xyFZaN0mAjtRkYaxI9ZfI8K41Trhd88s4B4G61p70DY3dMLmuF8wGHVCF lMMV6wIDAQABo0EwPzAOBgNVHQ8BAf8EBAMCAqQwGQYDVR0lBBIwEAYIKwYBBQUH AwMGBFUdJQAwEgYDVR0TAQH/BAgwBgEB/wIBATANBgkqhkiG9w0BAQsFAAOCAQEA LriCH0FTaOFIBl+kxAKjs7puhIZoYLwQ8IReXdEU7kYjPff3X/eiO82A0GwMM9Fp /RdMlZGDSLyZ1a/gKCz55j9J4MW8ZH7RSEQs3dJQCvEPDO6UdgKy4Ft9yNh/ba1J 8/n0CqR+0QNov6Qp7eMDkQaDvKgCaABn8at6VLtuifJXFKDGt0LrR7wkQBJ85SZB 9GdfNSPzEZkb4FQ2gPgAk7ySoQ6Hi6mogEORbtJ7+Xiq57J+cEZQV6TOuwYgBG4e MW3h37+7V5a/absybik1F/gcx4IbEBd/7an6a+a2l5FeTED5kpzvD4+yrQAoY8lT gccRdP0O4CsLn7zlLRidPQ== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIBTzCB9qADAgECAgMDDT8wCgYIKoZIzj0EAwIwDzENMAsGA1UEAxMEdGVzdDAe Fw0xNjAzMjgxODQxNDdaFw0yNzAzMjgxODQxNDdaMA8xDTALBgNVBAMTBHRlc3Qw WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQy8xfFkSiJA10EC1MMJzkLgu6csocC UNyix7zOqijLsASE4an5LQsZ1PuhgVYnL+B9rAcnXgJaLM8YOmLRPqNdo0EwPzAO BgNVHQ8BAf8EBAMCAqQwGQYDVR0lBBIwEAYIKwYBBQUHAwMGBFUdJQAwEgYDVR0T AQH/BAgwBgEB/wIBATAKBggqhkjOPQQDAgNIADBFAiEAwUrZY7fHwr4FWONiBJo6 97V9GAbj70ZJqV5M7rt+hMECIFY66kUrv0sG2vlhicSIGwSOdB3VcijdZSelzLn1 iRk5 -----END CERTIFICATE-----