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 · modeblame

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