cloudflare/cloudflared

Public

mirrored from https://github.com/cloudflare/cloudflaredAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2018.8.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

validation/validation.go

136lines · modecode

1package validation
2
3import (
4 "fmt"
5 "net"
6 "net/url"
7 "strings"
8
9 "golang.org/x/net/idna"
10)
11
12const defaultScheme = "http"
13
14var supportedProtocol = [2]string{"http", "https"}
15
16func ValidateHostname(hostname string) (string, error) {
17 if hostname == "" {
18 return "", fmt.Errorf("Hostname should not be empty")
19 }
20 // users gives url(contains schema) not just hostname
21 if strings.Contains(hostname, ":") || strings.Contains(hostname, "%3A") {
22 unescapeHostname, err := url.PathUnescape(hostname)
23 if err != nil {
24 return "", fmt.Errorf("Hostname(actually a URL) %s has invalid escape characters %s", hostname, unescapeHostname)
25 }
26 hostnameToURL, err := url.Parse(unescapeHostname)
27 if err != nil {
28 return "", fmt.Errorf("Hostname(actually a URL) %s has invalid format %s", hostname, hostnameToURL)
29 }
30 asciiHostname, err := idna.ToASCII(hostnameToURL.Hostname())
31 if err != nil {
32 return "", fmt.Errorf("Hostname(actually a URL) %s has invalid ASCII encdoing %s", hostname, asciiHostname)
33 }
34 return asciiHostname, nil
35 }
36
37 asciiHostname, err := idna.ToASCII(hostname)
38 if err != nil {
39 return "", fmt.Errorf("Hostname %s has invalid ASCII encdoing %s", hostname, asciiHostname)
40 }
41 hostnameToURL, err := url.Parse(asciiHostname)
42 if err != nil {
43 return "", fmt.Errorf("Hostname %s is not valid", hostnameToURL)
44 }
45 return hostnameToURL.RequestURI(), nil
46
47}
48
49func ValidateUrl(originUrl string) (string, error) {
50 if originUrl == "" {
51 return "", fmt.Errorf("Url should not be empty")
52 }
53
54 if net.ParseIP(originUrl) != nil {
55 return validateIP("", originUrl, "")
56 } else if strings.HasPrefix(originUrl, "[") && strings.HasSuffix(originUrl, "]") {
57 // ParseIP doesn't recoginze [::1]
58 return validateIP("", originUrl[1:len(originUrl)-1], "")
59 }
60
61 host, port, err := net.SplitHostPort(originUrl)
62 // user might pass in an ip address like 127.0.0.1
63 if err == nil && net.ParseIP(host) != nil {
64 return validateIP("", host, port)
65 }
66
67 unescapedUrl, err := url.PathUnescape(originUrl)
68 if err != nil {
69 return "", fmt.Errorf("URL %s has invalid escape characters %s", originUrl, unescapedUrl)
70 }
71
72 parsedUrl, err := url.Parse(unescapedUrl)
73 if err != nil {
74 return "", fmt.Errorf("URL %s has invalid format", originUrl)
75 }
76
77 // if the url is in the form of host:port, IsAbs() will think host is the schema
78 var hostname string
79 hasScheme := parsedUrl.IsAbs() && parsedUrl.Host != ""
80 if hasScheme {
81 err := validateScheme(parsedUrl.Scheme)
82 if err != nil {
83 return "", err
84 }
85 // The earlier check for ip address will miss the case http://[::1]
86 // and http://[::1]:8080
87 if net.ParseIP(parsedUrl.Hostname()) != nil {
88 return validateIP(parsedUrl.Scheme, parsedUrl.Hostname(), parsedUrl.Port())
89 }
90 hostname, err = ValidateHostname(parsedUrl.Hostname())
91 if err != nil {
92 return "", fmt.Errorf("URL %s has invalid format", originUrl)
93 }
94 if parsedUrl.Port() != "" {
95 return fmt.Sprintf("%s://%s", parsedUrl.Scheme, net.JoinHostPort(hostname, parsedUrl.Port())), nil
96 }
97 return fmt.Sprintf("%s://%s", parsedUrl.Scheme, hostname), nil
98 } else {
99 if host == "" {
100 hostname, err = ValidateHostname(originUrl)
101 if err != nil {
102 return "", fmt.Errorf("URL no %s has invalid format", originUrl)
103 }
104 return fmt.Sprintf("%s://%s", defaultScheme, hostname), nil
105 } else {
106 hostname, err = ValidateHostname(host)
107 if err != nil {
108 return "", fmt.Errorf("URL %s has invalid format", originUrl)
109 }
110 return fmt.Sprintf("%s://%s", defaultScheme, net.JoinHostPort(hostname, port)), nil
111 }
112 }
113
114}
115
116func validateScheme(scheme string) error {
117 for _, protocol := range supportedProtocol {
118 if scheme == protocol {
119 return nil
120 }
121 }
122 return fmt.Errorf("Currently Argo Tunnel does not support %s protocol.", scheme)
123}
124
125func validateIP(scheme, host, port string) (string, error) {
126 if scheme == "" {
127 scheme = defaultScheme
128 }
129 if port != "" {
130 return fmt.Sprintf("%s://%s", scheme, net.JoinHostPort(host, port)), nil
131 } else if strings.Contains(host, ":") {
132 // IPv6
133 return fmt.Sprintf("%s://[%s]", scheme, host), nil
134 }
135 return fmt.Sprintf("%s://%s", scheme, host), nil
136}
137