Skip to content

Commit

Permalink
refactor: enhance hostname validation and add host validation functions
Browse files Browse the repository at this point in the history
  • Loading branch information
root4loot committed Oct 5, 2024
1 parent d298f4e commit eb1bcf3
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 4 deletions.
84 changes: 80 additions & 4 deletions hostutil/hostutil.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,110 @@
package hostutil

import (
"net"
"strconv"
"strings"
)

// IsValidHostname checks if the given hostname is valid based on RFC 1123.
// IsValidHostname checks if the given hostname is a valid domain name based on RFC 1123.
// It ensures that the hostname does not consist entirely of numeric labels.
func IsValidHostname(hostname string) bool {
if len(hostname) == 0 || len(hostname) > 255 {
return false
}
labels := strings.Split(hostname, ".")
allNumericLabels := true

for _, label := range labels {
if len(label) == 0 || len(label) > 63 {
return false
}

isNumericLabel := true
for i := 0; i < len(label); i++ {
c := label[i]
if c < '0' || c > '9' {
isNumericLabel = false
break
}
}

if !isNumericLabel {
allNumericLabels = false
}

startChar := label[0]
endChar := label[len(label)-1]
if !((startChar >= 'a' && startChar <= 'z') || (startChar >= '0' && startChar <= '9')) {
if !((startChar >= 'a' && startChar <= 'z') || (startChar >= 'A' && startChar <= 'Z') || (startChar >= '0' && startChar <= '9')) {
return false
}
if !((endChar >= 'a' && endChar <= 'z') || (endChar >= '0' && endChar <= '9')) {
if !((endChar >= 'a' && endChar <= 'z') || (endChar >= 'A' && endChar <= 'Z') || (endChar >= '0' && endChar <= '9')) {
return false
}

for i := 0; i < len(label); i++ {
c := label[i]
if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' {
if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' {
continue
}
return false
}
}

if allNumericLabels {
return false
}
return true
}

// IsValidIP checks if the provided IP address is valid.
// It compares the parsed IP's string representation with the original input.
func IsValidIP(ip string) bool {
parsedIP := net.ParseIP(ip)
if parsedIP == nil {
return false
}

var formattedIP string

if strings.Contains(ip, ".") {
parsedIP = parsedIP.To4()
if parsedIP == nil {
return false
}
formattedIP = parsedIP.String()
} else if strings.Contains(ip, ":") {
parsedIP = parsedIP.To16()
if parsedIP == nil {
return false
}
formattedIP = parsedIP.String()
} else {
return false
}

return strings.EqualFold(ip, formattedIP)
}

// IsValidPort checks if the given port is a valid port number.
func IsValidPort(port string) bool {
if p, err := strconv.Atoi(port); err == nil {
return p > 0 && p <= 65535
}
return false
}

// IsValidHost checks if the given host is a valid hostname or IP address with an optional port.
func IsValidHost(host string) bool {
hostPart, portPart, err := net.SplitHostPort(host)
if err != nil {
// If there's no port, validate the host as an IP address first, then as a hostname.
return IsValidIP(host) || IsValidHostname(host)
}

if !IsValidPort(portPart) {
return false
}

return IsValidIP(hostPart) || IsValidHostname(hostPart)
}
41 changes: 41 additions & 0 deletions hostutil/hostutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,44 @@ func TestIsValidHostname(t *testing.T) {
})
}
}

func TestIsValidHost(t *testing.T) {
tests := []struct {
host string
valid bool
}{
{"example.com", true},
{"localhost", true},
{"sub.domain.example.com", true},
{"exa_mple.com", false},
{"example..com", false},
{"example.com:8080", true},
{"localhost:3000", true},
{"127.0.0.1:8080", true},
{"exa_mple.com:8080", false},
{"example.com:99999", false},
{"example.com:-1", false},
{"127.0.0.1", true},
{"192.168.1.1", true},
{"::1", true},
{"2001:db8::ff00:42:8329", true},
{"256.256.256.256", false},
{"1234.123.123.123", false},
{"2001:db8:::ff00:42:8329", false},
{"127.0.0.1:80", true},
{"192.168.1.1:65535", true},
{"::1:8080", true},
{"256.256.256.256:80", false},
{"127.0.0.1:99999", false},
{"127.0.0.1:-80", false},
}

for _, tt := range tests {
t.Run(tt.host, func(t *testing.T) {
result := IsValidHost(tt.host)
if result != tt.valid {
t.Errorf("IsValidHost(%q) = %v; want %v", tt.host, result, tt.valid)
}
})
}
}

0 comments on commit eb1bcf3

Please sign in to comment.