Skip to content

Commit

Permalink
feat: add ValidateEndpointURI
Browse files Browse the repository at this point in the history
Adds ValidateEndpointURI, which ensures that endpoint URIs are valid
`url.URL`s and that they meet further usage qualifications, such as
square-bracket-closed IPv6 addresses and ports within a valid range.

Signed-off-by: Seán C McCord <ulexus@gmail.com>
  • Loading branch information
Ulexus authored and talos-bot committed Sep 17, 2020
1 parent 402fa79 commit 8b56890
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 0 deletions.
46 changes: 46 additions & 0 deletions net.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ import (
"fmt"
"io/ioutil"
"net"
"net/url"
"os"
"strconv"
"strings"
)

const minPortRange = 1

const maxPortRange = 65535

// IPAddrs finds and returns a list of non-loopback IP addresses of the
// current machine.
func IPAddrs() (ips []net.IP, err error) {
Expand Down Expand Up @@ -169,3 +175,43 @@ func IsIPv6(addrs ...net.IP) bool {

return false
}

// ValidateEndpointURI checks that an endpoint is valid.
// This is a more strict check that merely `url.Parse`, in that it requires such things as properly-ranged numeric ports and bracket-enclosed IPv6 addresses.
func ValidateEndpointURI(ep string) error {
u, err := url.Parse(ep)
if err != nil {
return err
}

if strings.Count(u.Host, ":") > 2 {
// More than two colon indicates that we must have an IPv6 address.
// If we have an IPv6 address, it *must* be enclosed by brackets.
if strings.Count(u.Host, "[") < 1 || strings.Count(u.Host, "]") < 1 {
return fmt.Errorf("IPv6 addresses MUST be enclosed by square brackets")
}
}

if u.Hostname() == "" {
return fmt.Errorf("hostname must not be blank")
}

if u.Port() != "" {
return validatePortNumber(u.Port())
}

return nil
}

func validatePortNumber(p string) error {
portInt, err := strconv.Atoi(p)
if err != nil {
return fmt.Errorf("port number must be numeric")
}

if portInt < minPortRange || portInt > maxPortRange {
return fmt.Errorf("port number must be between %d and %d", minPortRange, maxPortRange)
}

return nil
}
32 changes: 32 additions & 0 deletions net_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,35 @@ func TestNthIPInNetwork(t *testing.T) {
})
}
}

func TestValidateEndpointURI(t *testing.T) {
goodTests := []string{
"http://216.22.102.222",
"https://[2001:db8:abef::ffff]:65000",
"https://[2001:db8::ffff]",
"http://goethe.de",
}

for _, testEP := range goodTests {
assert.Nil(t, talosnet.ValidateEndpointURI(testEP), "URI should be valid")
}

badTests := []string{
"12.34.56.89:1234", // ipv4:port, no protocol
"[2001:db8::1]:5040", // ipv6:port, no protocol
"hostA:65301", // host:port, no protocol
"my.long.domain.name:10101", // dns:port, no protocol
"192.168.2.1", // IP without port
"[2001:db8::1]", // IPv6 without port
"kubernetes.io", // hostname without port
"2001:db8:123:445:204", // IPv6 without brackets
"http://2001:db8:101:101::1:50000", // IPv6 URL without brackets
"http://192.168.1.1:1020304", // Port out of range
"http://192.168.1.1:0", // 0 Port
"http://192.168.1.1:-1000", // Negative Port
}

for _, testEP := range badTests {
assert.NotNil(t, talosnet.ValidateEndpointURI(testEP), "URI should be invalid")
}
}

0 comments on commit 8b56890

Please sign in to comment.