@marcel91200 / caddy Mirror · read-only / commits / 99dcdf7e
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 12, 10:45 UTC

caddyhttp: Convert IDNs to ASCII when provisioning Host matcher

Matthew Holt committed Jun 18, 2024 at 14:43 UTC 99dcdf7e426f0dcbdffe510f241ae8a4fd5a56e6
2 files changed +23 -6
modules/caddyhttp/matchers.go
+14 -6
@@ -34,6 +34,7 @@ import (
34 "github.com/google/cel-go/cel"
35 "github.com/google/cel-go/common/types"
36 "github.com/google/cel-go/common/types/ref"
37 + "golang.org/x/net/idna"
38
39 "github.com/caddyserver/caddy/v2"
40 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
@@ -239,13 +240,20 @@ func (m *MatchHost) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
240 func (m MatchHost) Provision(_ caddy.Context) error {
241 // check for duplicates; they are nonsensical and reduce efficiency
242 // (we could just remove them, but the user should know their config is erroneous)
242 - seen := make(map[string]int)
243 - for i, h := range m {
244 - h = strings.ToLower(h)
245 - if firstI, ok := seen[h]; ok {
246 - return fmt.Errorf("host at index %d is repeated at index %d: %s", firstI, i, h)
243 + seen := make(map[string]int, len(m))
244 + for i, host := range m {
245 + asciiHost, err := idna.ToASCII(host)
246 + if err != nil {
247 + return fmt.Errorf("converting hostname '%s' to ASCII: %v", host, err)
248 + }
249 + if asciiHost != host {
250 + m[i] = asciiHost
251 + }
252 + normalizedHost := strings.ToLower(asciiHost)
253 + if firstI, ok := seen[normalizedHost]; ok {
254 + return fmt.Errorf("host at index %d is repeated at index %d: %s", firstI, i, host)
255 }
248 - seen[h] = i
256 + seen[normalizedHost] = i
257 }
258
259 if m.large() {
modules/caddyhttp/matchers_test.go
+9
@@ -78,6 +78,11 @@ func TestHostMatcher(t *testing.T) {
78 input: "bar.example.com",
79 expect: false,
80 },
81 + {
82 + match: MatchHost{"éxàmplê.com"},
83 + input: "xn--xmpl-0na6cm.com",
84 + expect: true,
85 + },
86 {
87 match: MatchHost{"*.example.com"},
88 input: "example.com",
@@ -149,6 +154,10 @@ func TestHostMatcher(t *testing.T) {
154 ctx := context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl)
155 req = req.WithContext(ctx)
156
157 + if err := tc.match.Provision(caddy.Context{}); err != nil {
158 + t.Errorf("Test %d %v: provisioning failed: %v", i, tc.match, err)
159 + }
160 +
161 actual := tc.match.Match(req)
162 if actual != tc.expect {
163 t.Errorf("Test %d %v: Expected %t, got %t for '%s'", i, tc.match, tc.expect, actual, tc.input)