tlsrp

TLS reverse proxy
git clone git://git.rr3.xyz/tlsrp
Log | Files | Refs | README | LICENSE

hostname.go (2261B)


      1 package main
      2 
      3 import (
      4 	"fmt"
      5 	"slices"
      6 	"strings"
      7 )
      8 
      9 // TODO: Add support for more than just alternation in patterns. We should at
     10 // least support leading wildcards, but maybe we should support full regex.
     11 // XXX: We currently don't length check hostnames or the labels within.
     12 
     13 type label string
     14 
     15 func parseLabel(labelStr string) (label, error) {
     16 	if len(labelStr) == 0 {
     17 		return "", fmt.Errorf("empty label")
     18 	}
     19 
     20 	buf := make([]byte, 0, len(labelStr))
     21 	for _, r := range labelStr {
     22 		switch {
     23 		case 'A' <= r && r <= 'Z':
     24 			r += 'a' - 'A'
     25 		case 'a' <= r && r <= 'z':
     26 			// Ok
     27 		case '0' <= r && r <= '9':
     28 			// Ok
     29 		case r == '-':
     30 			// Ok, as long as test after loop passes
     31 		default:
     32 			return "", fmt.Errorf("illegal rune in label: %q", r)
     33 		}
     34 
     35 		buf = append(buf, byte(r))
     36 	}
     37 
     38 	if buf[0] == '-' || buf[len(buf) - 1] == '-' {
     39 		return "", fmt.Errorf("hyphen at start or end of label")
     40 	}
     41 
     42 	return label(buf), nil
     43 }
     44 
     45 type hostname []label
     46 
     47 func (hostname hostname) String() string {
     48 	// Ughhhh, Go can't convert between hostname and []string,
     49 	// so we can't use strings.Join.
     50 
     51 	var sb strings.Builder
     52 	for i, label := range hostname {
     53 		if i > 0 {
     54 			sb.WriteByte('.')
     55 		}
     56 		sb.WriteString(string(label))
     57 	}
     58 
     59 	return sb.String()
     60 }
     61 
     62 func (hostname0 hostname) equal(hostname1 hostname) bool {
     63 	return slices.Equal(hostname0, hostname1)
     64 }
     65 
     66 func parseHostname(hostnameStr string) (hostname, error) {
     67 	labelStrs := strings.Split(hostnameStr, ".")
     68 	if len(labelStrs) < 2 {
     69 		return nil, fmt.Errorf("illegal hostname: expected 2 or more labels")
     70 	}
     71 
     72 	labels := make([]label, 0, len(labelStrs))
     73 	for _, labelStr := range labelStrs {
     74 		label, err := parseLabel(labelStr)
     75 		if err != nil {
     76 			return nil, fmt.Errorf("illegal hostname: %w", err)
     77 		}
     78 		labels = append(labels, label)
     79 	}
     80 
     81 	return hostname(labels), nil
     82 }
     83 
     84 type pattern []hostname
     85 
     86 func (pattern pattern) matches(hostname hostname) bool {
     87 	return slices.ContainsFunc(pattern, hostname.equal)
     88 }
     89 
     90 func parsePattern(hostnameStrs []string) (pattern, error) {
     91 	pattern := make(pattern, 0, len(hostnameStrs))
     92 	for _, hostnameStr := range hostnameStrs {
     93 		hostname, err := parseHostname(hostnameStr)
     94 		if err != nil {
     95 			return nil, err
     96 		}
     97 		pattern = append(pattern, hostname)
     98 	}
     99 
    100 	return pattern, nil
    101 }