| 
									
										
										
										
											2019-06-30 16:07:58 -06:00
										 |  |  | // Copyright 2015 Matthew Holt and The Caddy Authors | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License. | 
					
						
							|  |  |  | // You may obtain a copy of the License at | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and | 
					
						
							|  |  |  | // limitations under the License. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | package caddyhttp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	"net/textproto" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	"regexp" | 
					
						
							| 
									
										
										
										
											2020-12-02 13:26:28 -07:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2021-05-03 00:35:28 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-02 12:37:06 -06:00
										 |  |  | 	"github.com/caddyserver/caddy/v2" | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" | 
					
						
							| 
									
										
										
										
											2020-05-26 17:35:27 -06:00
										 |  |  | 	"go.uber.org/zap" | 
					
						
							| 
									
										
										
										
											2019-04-03 11:47:27 -04:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type ( | 
					
						
							| 
									
										
										
										
											2019-11-15 12:47:06 -07:00
										 |  |  | 	// MatchHost matches requests by the Host value (case-insensitive). | 
					
						
							| 
									
										
										
										
											2019-12-31 16:57:54 -07:00
										 |  |  | 	// | 
					
						
							| 
									
										
										
										
											2020-01-17 10:57:57 -07:00
										 |  |  | 	// When used in a top-level HTTP route, | 
					
						
							| 
									
										
										
										
											2019-12-31 16:57:54 -07:00
										 |  |  | 	// [qualifying domain names](/docs/automatic-https#hostname-requirements) | 
					
						
							|  |  |  | 	// may trigger [automatic HTTPS](/docs/automatic-https), which automatically | 
					
						
							|  |  |  | 	// provisions and renews certificates for you. Before doing this, you | 
					
						
							|  |  |  | 	// should ensure that DNS records for these domains are properly configured, | 
					
						
							|  |  |  | 	// especially A/AAAA pointed at your server. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// Automatic HTTPS can be | 
					
						
							| 
									
										
										
										
											2020-03-24 10:53:53 -06:00
										 |  |  | 	// [customized or disabled](/docs/modules/http#servers/automatic_https). | 
					
						
							| 
									
										
										
										
											2020-04-01 11:40:59 -06:00
										 |  |  | 	// | 
					
						
							|  |  |  | 	// Wildcards (`*`) may be used to represent exactly one label of the | 
					
						
							|  |  |  | 	// hostname, in accordance with RFC 1034 (because host matchers are also | 
					
						
							|  |  |  | 	// used for automatic HTTPS which influences TLS certificates). Thus, | 
					
						
							|  |  |  | 	// a host of `*` matches hosts like `localhost` or `internal` but not | 
					
						
							|  |  |  | 	// `example.com`. To catch all hosts, omit the host matcher entirely. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// The wildcard can be useful for matching all subdomains, for example: | 
					
						
							|  |  |  | 	// `*.example.com` matches `foo.example.com` but not `foo.bar.example.com`. | 
					
						
							| 
									
										
										
										
											2020-12-02 13:26:28 -07:00
										 |  |  | 	// | 
					
						
							|  |  |  | 	// Duplicate entries will return an error. | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | 	MatchHost []string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-09 14:00:32 -07:00
										 |  |  | 	// MatchPath matches requests by the URI's path (case-insensitive). Path | 
					
						
							|  |  |  | 	// matches are exact, but wildcards may be used: | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// - At the end, for a prefix match (`/prefix/*`) | 
					
						
							|  |  |  | 	// - At the beginning, for a suffix match (`*.suffix`) | 
					
						
							|  |  |  | 	// - On both sides, for a substring match (`*/contains/*`) | 
					
						
							|  |  |  | 	// - In the middle, for a globular match (`/accounts/*/info`) | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// This matcher is fast, so it does not support regular expressions or | 
					
						
							| 
									
										
										
										
											2020-01-17 10:57:57 -07:00
										 |  |  | 	// capture groups. For slower but more powerful matching, use the | 
					
						
							|  |  |  | 	// path_regexp matcher. | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | 	MatchPath []string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// MatchPathRE matches requests by a regular expression on the URI's path. | 
					
						
							| 
									
										
										
										
											2019-12-29 13:16:34 -07:00
										 |  |  | 	// | 
					
						
							|  |  |  | 	// Upon a match, it adds placeholders to the request: `{http.regexp.name.capture_group}` | 
					
						
							|  |  |  | 	// where `name` is the regular expression's name, and `capture_group` is either | 
					
						
							|  |  |  | 	// the named or positional capture group from the expression itself. If no name | 
					
						
							|  |  |  | 	// is given, then the placeholder omits the name: `{http.regexp.capture_group}` | 
					
						
							|  |  |  | 	// (potentially leading to collisions). | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | 	MatchPathRE struct{ MatchRegexp } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// MatchMethod matches requests by the method. | 
					
						
							|  |  |  | 	MatchMethod []string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// MatchQuery matches requests by URI's query string. | 
					
						
							|  |  |  | 	MatchQuery url.Values | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-14 11:00:46 -07:00
										 |  |  | 	// MatchHeader matches requests by header fields. It performs fast, | 
					
						
							|  |  |  | 	// exact string comparisons of the field values. Fast prefix, suffix, | 
					
						
							|  |  |  | 	// and substring matches can also be done by suffixing, prefixing, or | 
					
						
							|  |  |  | 	// surrounding the value with the wildcard `*` character, respectively. | 
					
						
							|  |  |  | 	// If a list is null, the header must not exist. If the list is empty, | 
					
						
							|  |  |  | 	// the field must simply exist, regardless of its value. | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | 	MatchHeader http.Header | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// MatchHeaderRE matches requests by a regular expression on header fields. | 
					
						
							| 
									
										
										
										
											2019-12-29 13:16:34 -07:00
										 |  |  | 	// | 
					
						
							|  |  |  | 	// Upon a match, it adds placeholders to the request: `{http.regexp.name.capture_group}` | 
					
						
							|  |  |  | 	// where `name` is the regular expression's name, and `capture_group` is either | 
					
						
							|  |  |  | 	// the named or positional capture group from the expression itself. If no name | 
					
						
							|  |  |  | 	// is given, then the placeholder omits the name: `{http.regexp.capture_group}` | 
					
						
							|  |  |  | 	// (potentially leading to collisions). | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | 	MatchHeaderRE map[string]*MatchRegexp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// MatchProtocol matches requests by protocol. | 
					
						
							|  |  |  | 	MatchProtocol string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | 	// MatchRemoteIP matches requests by client IP (or CIDR range). | 
					
						
							|  |  |  | 	MatchRemoteIP struct { | 
					
						
							| 
									
										
										
										
											2020-12-10 16:09:30 -07:00
										 |  |  | 		// The IPs or CIDR ranges to match. | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | 		Ranges []string `json:"ranges,omitempty"` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-10 16:09:30 -07:00
										 |  |  | 		// If true, prefer the first IP in the request's X-Forwarded-For | 
					
						
							|  |  |  | 		// header, if present, rather than the immediate peer's IP, as | 
					
						
							|  |  |  | 		// the reference IP against which to match. Note that it is easy | 
					
						
							|  |  |  | 		// to spoof request headers. Default: false | 
					
						
							|  |  |  | 		Forwarded bool `json:"forwarded,omitempty"` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-26 17:35:27 -06:00
										 |  |  | 		cidrs  []*net.IPNet | 
					
						
							|  |  |  | 		logger *zap.Logger | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | 	// MatchNot matches requests by negating the results of its matcher | 
					
						
							|  |  |  | 	// sets. A single "not" matcher takes one or more matcher sets. Each | 
					
						
							|  |  |  | 	// matcher set is OR'ed; in other words, if any matcher set returns | 
					
						
							|  |  |  | 	// true, the final result of the "not" matcher is false. Individual | 
					
						
							|  |  |  | 	// matchers within a set work the same (i.e. different matchers in | 
					
						
							|  |  |  | 	// the same set are AND'ed). | 
					
						
							| 
									
										
										
										
											2020-04-06 18:44:12 -06:00
										 |  |  | 	// | 
					
						
							|  |  |  | 	// Note that the generated docs which describe the structure of | 
					
						
							|  |  |  | 	// this module are wrong because of how this type unmarshals JSON | 
					
						
							|  |  |  | 	// in a custom way. The correct structure is: | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// ```json | 
					
						
							|  |  |  | 	// [ | 
					
						
							|  |  |  | 	// 	{}, | 
					
						
							|  |  |  | 	// 	{} | 
					
						
							|  |  |  | 	// ] | 
					
						
							|  |  |  | 	// ``` | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// where each of the array elements is a matcher set, i.e. an | 
					
						
							|  |  |  | 	// object keyed by matcher name. | 
					
						
							| 
									
										
										
										
											2020-03-30 11:53:19 -06:00
										 |  |  | 	MatchNot struct { | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | 		MatcherSetsRaw []caddy.ModuleMap `json:"-" caddy:"namespace=http.matchers"` | 
					
						
							|  |  |  | 		MatcherSets    []MatcherSet      `json:"-"` | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	caddy.RegisterModule(MatchHost{}) | 
					
						
							|  |  |  | 	caddy.RegisterModule(MatchPath{}) | 
					
						
							|  |  |  | 	caddy.RegisterModule(MatchPathRE{}) | 
					
						
							|  |  |  | 	caddy.RegisterModule(MatchMethod{}) | 
					
						
							|  |  |  | 	caddy.RegisterModule(MatchQuery{}) | 
					
						
							|  |  |  | 	caddy.RegisterModule(MatchHeader{}) | 
					
						
							|  |  |  | 	caddy.RegisterModule(MatchHeaderRE{}) | 
					
						
							|  |  |  | 	caddy.RegisterModule(new(MatchProtocol)) | 
					
						
							|  |  |  | 	caddy.RegisterModule(MatchRemoteIP{}) | 
					
						
							| 
									
										
										
										
											2020-03-30 11:53:19 -06:00
										 |  |  | 	caddy.RegisterModule(MatchNot{}) | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // CaddyModule returns the Caddy module information. | 
					
						
							|  |  |  | func (MatchHost) CaddyModule() caddy.ModuleInfo { | 
					
						
							|  |  |  | 	return caddy.ModuleInfo{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		ID:  "http.matchers.host", | 
					
						
							|  |  |  | 		New: func() caddy.Module { return new(MatchHost) }, | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // UnmarshalCaddyfile implements caddyfile.Unmarshaler. | 
					
						
							|  |  |  | func (m *MatchHost) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { | 
					
						
							| 
									
										
										
										
											2020-03-18 23:36:25 -06:00
										 |  |  | 	for d.Next() { | 
					
						
							|  |  |  | 		*m = append(*m, d.RemainingArgs()...) | 
					
						
							| 
									
										
										
										
											2020-04-06 15:07:07 -04:00
										 |  |  | 		if d.NextBlock(0) { | 
					
						
							|  |  |  | 			return d.Err("malformed host matcher: blocks are not supported") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-18 23:36:25 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 13:26:28 -07:00
										 |  |  | // Provision sets up and validates m, including making it more efficient for large lists. | 
					
						
							|  |  |  | func (m MatchHost) Provision(_ caddy.Context) error { | 
					
						
							|  |  |  | 	// check for duplicates; they are nonsensical and reduce efficiency | 
					
						
							|  |  |  | 	// (we could just remove them, but the user should know their config is erroneous) | 
					
						
							|  |  |  | 	seen := make(map[string]int) | 
					
						
							|  |  |  | 	for i, h := range m { | 
					
						
							|  |  |  | 		h = strings.ToLower(h) | 
					
						
							|  |  |  | 		if firstI, ok := seen[h]; ok { | 
					
						
							|  |  |  | 			return fmt.Errorf("host at index %d is repeated at index %d: %s", firstI, i, h) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		seen[h] = i | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if m.large() { | 
					
						
							|  |  |  | 		// sort the slice lexicographically, grouping "fuzzy" entries (wildcards and placeholders) | 
					
						
							|  |  |  | 		// at the front of the list; this allows us to use binary search for exact matches, which | 
					
						
							|  |  |  | 		// we have seen from experience is the most common kind of value in large lists; and any | 
					
						
							|  |  |  | 		// other kinds of values (wildcards and placeholders) are grouped in front so the linear | 
					
						
							|  |  |  | 		// search should find a match fairly quickly | 
					
						
							|  |  |  | 		sort.Slice(m, func(i, j int) bool { | 
					
						
							|  |  |  | 			iInexact, jInexact := m.fuzzy(m[i]), m.fuzzy(m[j]) | 
					
						
							|  |  |  | 			if iInexact && !jInexact { | 
					
						
							|  |  |  | 				return true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if !iInexact && jInexact { | 
					
						
							|  |  |  | 				return false | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return m[i] < m[j] | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | // Match returns true if r matches m. | 
					
						
							| 
									
										
										
										
											2019-05-22 12:32:36 -06:00
										 |  |  | func (m MatchHost) Match(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2019-06-20 20:24:46 -06:00
										 |  |  | 	reqHost, _, err := net.SplitHostPort(r.Host) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// OK; probably didn't have a port | 
					
						
							|  |  |  | 		reqHost = r.Host | 
					
						
							| 
									
										
										
										
											2019-09-18 09:45:21 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// make sure we strip the brackets from IPv6 addresses | 
					
						
							|  |  |  | 		reqHost = strings.TrimPrefix(reqHost, "[") | 
					
						
							|  |  |  | 		reqHost = strings.TrimSuffix(reqHost, "]") | 
					
						
							| 
									
										
										
										
											2019-06-20 20:24:46 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 13:26:28 -07:00
										 |  |  | 	if m.large() { | 
					
						
							|  |  |  | 		// fast path: locate exact match using binary search (about 100-1000x faster for large lists) | 
					
						
							|  |  |  | 		pos := sort.Search(len(m), func(i int) bool { | 
					
						
							|  |  |  | 			if m.fuzzy(m[i]) { | 
					
						
							|  |  |  | 				return false | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return m[i] >= reqHost | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		if pos < len(m) && m[pos] == reqHost { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-29 13:12:52 -07:00
										 |  |  | 	repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) | 
					
						
							| 
									
										
										
										
											2019-10-14 19:29:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | outer: | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 	for _, host := range m { | 
					
						
							| 
									
										
										
										
											2020-12-02 13:26:28 -07:00
										 |  |  | 		// fast path: if matcher is large, we already know we don't have an exact | 
					
						
							|  |  |  | 		// match, so we're only looking for fuzzy match now, which should be at the | 
					
						
							|  |  |  | 		// front of the list; if we have reached a value that is not fuzzy, there | 
					
						
							|  |  |  | 		// will be no match and we can short-circuit for efficiency | 
					
						
							|  |  |  | 		if m.large() && !m.fuzzy(host) { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-14 19:29:36 +02:00
										 |  |  | 		host = repl.ReplaceAll(host, "") | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 		if strings.Contains(host, "*") { | 
					
						
							|  |  |  | 			patternParts := strings.Split(host, ".") | 
					
						
							| 
									
										
										
										
											2019-06-20 20:24:46 -06:00
										 |  |  | 			incomingParts := strings.Split(reqHost, ".") | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 			if len(patternParts) != len(incomingParts) { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for i := range patternParts { | 
					
						
							|  |  |  | 				if patternParts[i] == "*" { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if !strings.EqualFold(patternParts[i], incomingParts[i]) { | 
					
						
							|  |  |  | 					continue outer | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return true | 
					
						
							| 
									
										
										
										
											2019-06-20 20:24:46 -06:00
										 |  |  | 		} else if strings.EqualFold(reqHost, host) { | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-06-20 20:24:46 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-02 13:26:28 -07:00
										 |  |  | // fuzzy returns true if the given hostname h is not a specific | 
					
						
							|  |  |  | // hostname, e.g. has placeholders or wildcards. | 
					
						
							|  |  |  | func (MatchHost) fuzzy(h string) bool { return strings.ContainsAny(h, "{*") } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // large returns true if m is considered to be large. Optimizing | 
					
						
							|  |  |  | // the matcher for smaller lists has diminishing returns. | 
					
						
							|  |  |  | // See related benchmark function in test file to conduct experiments. | 
					
						
							|  |  |  | func (m MatchHost) large() bool { return len(m) > 100 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | // CaddyModule returns the Caddy module information. | 
					
						
							|  |  |  | func (MatchPath) CaddyModule() caddy.ModuleInfo { | 
					
						
							|  |  |  | 	return caddy.ModuleInfo{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		ID:  "http.matchers.path", | 
					
						
							|  |  |  | 		New: func() caddy.Module { return new(MatchPath) }, | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-15 12:47:06 -07:00
										 |  |  | // Provision lower-cases the paths in m to ensure case-insensitive matching. | 
					
						
							|  |  |  | func (m MatchPath) Provision(_ caddy.Context) error { | 
					
						
							|  |  |  | 	for i := range m { | 
					
						
							|  |  |  | 		m[i] = strings.ToLower(m[i]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | // Match returns true if r matches m. | 
					
						
							| 
									
										
										
										
											2019-05-22 12:32:36 -06:00
										 |  |  | func (m MatchPath) Match(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2019-11-15 12:47:06 -07:00
										 |  |  | 	lowerPath := strings.ToLower(r.URL.Path) | 
					
						
							| 
									
										
										
										
											2019-12-17 10:14:04 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// see #2917; Windows ignores trailing dots and spaces | 
					
						
							|  |  |  | 	// when accessing files (sigh), potentially causing a | 
					
						
							|  |  |  | 	// security risk (cry) if PHP files end up being served | 
					
						
							|  |  |  | 	// as static files, exposing the source code, instead of | 
					
						
							|  |  |  | 	// being matched by *.php to be treated as PHP scripts | 
					
						
							|  |  |  | 	lowerPath = strings.TrimRight(lowerPath, ". ") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-09 14:00:32 -07:00
										 |  |  | 	repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | 	for _, matchPath := range m { | 
					
						
							| 
									
										
										
										
											2020-01-09 14:00:32 -07:00
										 |  |  | 		matchPath = repl.ReplaceAll(matchPath, "") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-16 16:08:33 -06:00
										 |  |  | 		// special case: whole path is wildcard; this is unnecessary | 
					
						
							|  |  |  | 		// as it matches all requests, which is the same as no matcher | 
					
						
							|  |  |  | 		if matchPath == "*" { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-09 14:00:32 -07:00
										 |  |  | 		// special case: first and last characters are wildcard, | 
					
						
							|  |  |  | 		// treat it as a fast substring match | 
					
						
							| 
									
										
										
										
											2020-03-16 16:08:33 -06:00
										 |  |  | 		if len(matchPath) > 1 && | 
					
						
							|  |  |  | 			strings.HasPrefix(matchPath, "*") && | 
					
						
							|  |  |  | 			strings.HasSuffix(matchPath, "*") { | 
					
						
							| 
									
										
										
										
											2020-01-09 14:00:32 -07:00
										 |  |  | 			if strings.Contains(lowerPath, matchPath[1:len(matchPath)-1]) { | 
					
						
							| 
									
										
										
										
											2019-11-28 21:11:45 -07:00
										 |  |  | 				return true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// special case: first character is a wildcard, | 
					
						
							|  |  |  | 		// treat it as a fast suffix match | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | 		if strings.HasPrefix(matchPath, "*") { | 
					
						
							| 
									
										
										
										
											2019-11-28 21:11:45 -07:00
										 |  |  | 			if strings.HasSuffix(lowerPath, matchPath[1:]) { | 
					
						
							|  |  |  | 				return true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-28 21:11:45 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-09 14:00:32 -07:00
										 |  |  | 		// special case: last character is a wildcard, | 
					
						
							|  |  |  | 		// treat it as a fast prefix match | 
					
						
							|  |  |  | 		if strings.HasSuffix(matchPath, "*") { | 
					
						
							|  |  |  | 			if strings.HasPrefix(lowerPath, matchPath[:len(matchPath)-1]) { | 
					
						
							|  |  |  | 				return true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// for everything else, try globular matching, which also | 
					
						
							|  |  |  | 		// is exact matching if there are no glob/wildcard chars; | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | 		// can ignore error here because we can't handle it anyway | 
					
						
							| 
									
										
										
										
											2019-11-15 12:47:06 -07:00
										 |  |  | 		matches, _ := filepath.Match(matchPath, lowerPath) | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | 		if matches { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // UnmarshalCaddyfile implements caddyfile.Unmarshaler. | 
					
						
							|  |  |  | func (m *MatchPath) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	for d.Next() { | 
					
						
							| 
									
										
										
										
											2020-03-18 23:36:25 -06:00
										 |  |  | 		*m = append(*m, d.RemainingArgs()...) | 
					
						
							| 
									
										
										
										
											2020-04-06 15:07:07 -04:00
										 |  |  | 		if d.NextBlock(0) { | 
					
						
							|  |  |  | 			return d.Err("malformed path matcher: blocks are not supported") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | // CaddyModule returns the Caddy module information. | 
					
						
							|  |  |  | func (MatchPathRE) CaddyModule() caddy.ModuleInfo { | 
					
						
							|  |  |  | 	return caddy.ModuleInfo{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		ID:  "http.matchers.path_regexp", | 
					
						
							|  |  |  | 		New: func() caddy.Module { return new(MatchPathRE) }, | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | // Match returns true if r matches m. | 
					
						
							| 
									
										
										
										
											2019-05-22 12:32:36 -06:00
										 |  |  | func (m MatchPathRE) Match(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2019-12-29 13:12:52 -07:00
										 |  |  | 	repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) | 
					
						
							| 
									
										
										
										
											2019-11-29 11:23:49 -07:00
										 |  |  | 	return m.MatchRegexp.Match(r.URL.Path, repl) | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | // CaddyModule returns the Caddy module information. | 
					
						
							|  |  |  | func (MatchMethod) CaddyModule() caddy.ModuleInfo { | 
					
						
							|  |  |  | 	return caddy.ModuleInfo{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		ID:  "http.matchers.method", | 
					
						
							|  |  |  | 		New: func() caddy.Module { return new(MatchMethod) }, | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // UnmarshalCaddyfile implements caddyfile.Unmarshaler. | 
					
						
							|  |  |  | func (m *MatchMethod) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	for d.Next() { | 
					
						
							| 
									
										
										
										
											2020-03-18 23:36:25 -06:00
										 |  |  | 		*m = append(*m, d.RemainingArgs()...) | 
					
						
							| 
									
										
										
										
											2020-04-06 15:07:07 -04:00
										 |  |  | 		if d.NextBlock(0) { | 
					
						
							|  |  |  | 			return d.Err("malformed method matcher: blocks are not supported") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | // Match returns true if r matches m. | 
					
						
							| 
									
										
										
										
											2019-05-22 12:32:36 -06:00
										 |  |  | func (m MatchMethod) Match(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 	for _, method := range m { | 
					
						
							|  |  |  | 		if r.Method == method { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | // CaddyModule returns the Caddy module information. | 
					
						
							|  |  |  | func (MatchQuery) CaddyModule() caddy.ModuleInfo { | 
					
						
							|  |  |  | 	return caddy.ModuleInfo{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		ID:  "http.matchers.query", | 
					
						
							|  |  |  | 		New: func() caddy.Module { return new(MatchQuery) }, | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // UnmarshalCaddyfile implements caddyfile.Unmarshaler. | 
					
						
							|  |  |  | func (m *MatchQuery) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { | 
					
						
							| 
									
										
										
										
											2020-06-16 12:02:23 -06:00
										 |  |  | 	if *m == nil { | 
					
						
							|  |  |  | 		*m = make(map[string][]string) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	for d.Next() { | 
					
						
							| 
									
										
										
										
											2020-11-02 18:05:01 -05:00
										 |  |  | 		for _, query := range d.RemainingArgs() { | 
					
						
							|  |  |  | 			if query == "" { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			parts := strings.SplitN(query, "=", 2) | 
					
						
							|  |  |  | 			if len(parts) != 2 { | 
					
						
							|  |  |  | 				return d.Errf("malformed query matcher token: %s; must be in param=val format", d.Val()) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			url.Values(*m).Add(parts[0], parts[1]) | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-04-06 15:07:07 -04:00
										 |  |  | 		if d.NextBlock(0) { | 
					
						
							|  |  |  | 			return d.Err("malformed query matcher: blocks are not supported") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-16 12:02:23 -06:00
										 |  |  | // Match returns true if r matches m. An empty m matches an empty query string. | 
					
						
							| 
									
										
										
										
											2019-05-22 12:32:36 -06:00
										 |  |  | func (m MatchQuery) Match(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2020-06-26 22:14:47 +01:00
										 |  |  | 	repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 	for param, vals := range m { | 
					
						
							| 
									
										
										
										
											2020-06-26 22:14:47 +01:00
										 |  |  | 		param = repl.ReplaceAll(param, "") | 
					
						
							| 
									
										
										
										
											2019-11-30 09:05:22 +13:00
										 |  |  | 		paramVal, found := r.URL.Query()[param] | 
					
						
							|  |  |  | 		if found { | 
					
						
							|  |  |  | 			for _, v := range vals { | 
					
						
							| 
									
										
										
										
											2020-06-26 22:14:47 +01:00
										 |  |  | 				v = repl.ReplaceAll(v, "") | 
					
						
							| 
									
										
										
										
											2019-11-30 09:05:22 +13:00
										 |  |  | 				if paramVal[0] == v || v == "*" { | 
					
						
							|  |  |  | 					return true | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-06-16 12:02:23 -06:00
										 |  |  | 	return len(m) == 0 && len(r.URL.Query()) == 0 | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | // CaddyModule returns the Caddy module information. | 
					
						
							|  |  |  | func (MatchHeader) CaddyModule() caddy.ModuleInfo { | 
					
						
							|  |  |  | 	return caddy.ModuleInfo{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		ID:  "http.matchers.header", | 
					
						
							|  |  |  | 		New: func() caddy.Module { return new(MatchHeader) }, | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // UnmarshalCaddyfile implements caddyfile.Unmarshaler. | 
					
						
							|  |  |  | func (m *MatchHeader) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { | 
					
						
							| 
									
										
										
										
											2019-09-10 19:21:52 -06:00
										 |  |  | 	if *m == nil { | 
					
						
							|  |  |  | 		*m = make(map[string][]string) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	for d.Next() { | 
					
						
							|  |  |  | 		var field, val string | 
					
						
							| 
									
										
										
										
											2020-12-09 18:28:14 +00:00
										 |  |  | 		if !d.Args(&field) { | 
					
						
							|  |  |  | 			return d.Errf("malformed header matcher: expected field") | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-10-31 12:27:01 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-09 18:28:14 +00:00
										 |  |  | 		if strings.HasPrefix(field, "!") { | 
					
						
							|  |  |  | 			if len(field) == 1 { | 
					
						
							|  |  |  | 				return d.Errf("malformed header matcher: must have field name following ! character") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			field = field[1:] | 
					
						
							|  |  |  | 			headers := *m | 
					
						
							|  |  |  | 			headers[field] = nil | 
					
						
							|  |  |  | 			m = &headers | 
					
						
							|  |  |  | 			if d.NextArg() { | 
					
						
							|  |  |  | 				return d.Errf("malformed header matcher: null matching headers cannot have a field value") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			if !d.NextArg() { | 
					
						
							|  |  |  | 				return d.Errf("malformed header matcher: expected both field and value") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// If multiple header matchers with the same header field are defined, | 
					
						
							|  |  |  | 			// we want to add the existing to the list of headers (will be OR'ed) | 
					
						
							|  |  |  | 			val = d.Val() | 
					
						
							|  |  |  | 			http.Header(*m).Add(field, val) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-10-31 12:27:01 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-06 15:07:07 -04:00
										 |  |  | 		if d.NextBlock(0) { | 
					
						
							|  |  |  | 			return d.Err("malformed header matcher: blocks are not supported") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-26 17:35:27 -06:00
										 |  |  | // Match returns true if r matches m. | 
					
						
							|  |  |  | func (m MatchHeader) Match(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2021-02-11 16:27:09 -07:00
										 |  |  | 	repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) | 
					
						
							|  |  |  | 	return matchHeaders(r.Header, http.Header(m), r.Host, repl) | 
					
						
							| 
									
										
										
										
											2020-05-26 17:35:27 -06:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-02-20 18:55:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-26 17:35:27 -06:00
										 |  |  | // getHeaderFieldVals returns the field values for the given fieldName from input. | 
					
						
							|  |  |  | // The host parameter should be obtained from the http.Request.Host field since | 
					
						
							|  |  |  | // net/http removes it from the header map. | 
					
						
							|  |  |  | func getHeaderFieldVals(input http.Header, fieldName, host string) []string { | 
					
						
							|  |  |  | 	fieldName = textproto.CanonicalMIMEHeaderKey(fieldName) | 
					
						
							|  |  |  | 	if fieldName == "Host" && host != "" { | 
					
						
							|  |  |  | 		return []string{host} | 
					
						
							| 
									
										
										
										
											2020-02-20 18:55:47 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-05-26 17:35:27 -06:00
										 |  |  | 	return input[fieldName] | 
					
						
							| 
									
										
										
										
											2020-02-20 18:55:47 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-26 17:35:27 -06:00
										 |  |  | // matchHeaders returns true if input matches the criteria in against without regex. | 
					
						
							|  |  |  | // The host parameter should be obtained from the http.Request.Host field since | 
					
						
							|  |  |  | // net/http removes it from the header map. | 
					
						
							| 
									
										
										
										
											2021-02-11 16:27:09 -07:00
										 |  |  | func matchHeaders(input, against http.Header, host string, repl *caddy.Replacer) bool { | 
					
						
							| 
									
										
										
										
											2020-05-26 17:35:27 -06:00
										 |  |  | 	for field, allowedFieldVals := range against { | 
					
						
							|  |  |  | 		actualFieldVals := getHeaderFieldVals(input, field, host) | 
					
						
							| 
									
										
										
										
											2020-02-20 18:55:47 +01:00
										 |  |  | 		if allowedFieldVals != nil && len(allowedFieldVals) == 0 && actualFieldVals != nil { | 
					
						
							| 
									
										
										
										
											2019-09-06 14:25:16 -06:00
										 |  |  | 			// a non-nil but empty list of allowed values means | 
					
						
							|  |  |  | 			// match if the header field exists at all | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-17 11:29:43 -07:00
										 |  |  | 		if allowedFieldVals == nil && actualFieldVals == nil { | 
					
						
							|  |  |  | 			// a nil list means match if the header does not exist at all | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 		var match bool | 
					
						
							|  |  |  | 	fieldVals: | 
					
						
							|  |  |  | 		for _, actualFieldVal := range actualFieldVals { | 
					
						
							|  |  |  | 			for _, allowedFieldVal := range allowedFieldVals { | 
					
						
							| 
									
										
										
										
											2021-02-11 16:27:09 -07:00
										 |  |  | 				if repl != nil { | 
					
						
							|  |  |  | 					allowedFieldVal = repl.ReplaceAll(allowedFieldVal, "") | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-11-27 11:52:31 -07:00
										 |  |  | 				switch { | 
					
						
							| 
									
										
										
										
											2020-02-20 18:55:47 +01:00
										 |  |  | 				case allowedFieldVal == "*": | 
					
						
							|  |  |  | 					match = true | 
					
						
							| 
									
										
										
										
											2019-11-27 11:52:31 -07:00
										 |  |  | 				case strings.HasPrefix(allowedFieldVal, "*") && strings.HasSuffix(allowedFieldVal, "*"): | 
					
						
							|  |  |  | 					match = strings.Contains(actualFieldVal, allowedFieldVal[1:len(allowedFieldVal)-1]) | 
					
						
							|  |  |  | 				case strings.HasPrefix(allowedFieldVal, "*"): | 
					
						
							|  |  |  | 					match = strings.HasSuffix(actualFieldVal, allowedFieldVal[1:]) | 
					
						
							|  |  |  | 				case strings.HasSuffix(allowedFieldVal, "*"): | 
					
						
							|  |  |  | 					match = strings.HasPrefix(actualFieldVal, allowedFieldVal[:len(allowedFieldVal)-1]) | 
					
						
							|  |  |  | 				default: | 
					
						
							|  |  |  | 					match = actualFieldVal == allowedFieldVal | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if match { | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 					break fieldVals | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 		if !match { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | // CaddyModule returns the Caddy module information. | 
					
						
							|  |  |  | func (MatchHeaderRE) CaddyModule() caddy.ModuleInfo { | 
					
						
							|  |  |  | 	return caddy.ModuleInfo{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		ID:  "http.matchers.header_regexp", | 
					
						
							|  |  |  | 		New: func() caddy.Module { return new(MatchHeaderRE) }, | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // UnmarshalCaddyfile implements caddyfile.Unmarshaler. | 
					
						
							|  |  |  | func (m *MatchHeaderRE) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { | 
					
						
							|  |  |  | 	if *m == nil { | 
					
						
							|  |  |  | 		*m = make(map[string]*MatchRegexp) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for d.Next() { | 
					
						
							| 
									
										
										
										
											2020-02-20 18:55:47 +01:00
										 |  |  | 		var first, second, third string | 
					
						
							|  |  |  | 		if !d.Args(&first, &second) { | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 			return d.ArgErr() | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-02-20 18:55:47 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		var name, field, val string | 
					
						
							|  |  |  | 		if d.Args(&third) { | 
					
						
							|  |  |  | 			name = first | 
					
						
							|  |  |  | 			field = second | 
					
						
							|  |  |  | 			val = third | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			field = first | 
					
						
							|  |  |  | 			val = second | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		(*m)[field] = &MatchRegexp{Pattern: val, Name: name} | 
					
						
							| 
									
										
										
										
											2020-04-06 15:07:07 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if d.NextBlock(0) { | 
					
						
							|  |  |  | 			return d.Err("malformed header_regexp matcher: blocks are not supported") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | // Match returns true if r matches m. | 
					
						
							| 
									
										
										
										
											2019-05-22 12:32:36 -06:00
										 |  |  | func (m MatchHeaderRE) Match(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	for field, rm := range m { | 
					
						
							| 
									
										
										
										
											2020-05-26 17:35:27 -06:00
										 |  |  | 		actualFieldVals := getHeaderFieldVals(r.Header, field, r.Host) | 
					
						
							| 
									
										
										
										
											2020-02-20 18:55:47 +01:00
										 |  |  | 		match := false | 
					
						
							|  |  |  | 	fieldVal: | 
					
						
							|  |  |  | 		for _, actualFieldVal := range actualFieldVals { | 
					
						
							|  |  |  | 			repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) | 
					
						
							|  |  |  | 			if rm.Match(actualFieldVal, repl) { | 
					
						
							|  |  |  | 				match = true | 
					
						
							|  |  |  | 				break fieldVal | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 		if !match { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | // Provision compiles m's regular expressions. | 
					
						
							| 
									
										
										
										
											2019-06-21 14:36:26 -06:00
										 |  |  | func (m MatchHeaderRE) Provision(ctx caddy.Context) error { | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	for _, rm := range m { | 
					
						
							| 
									
										
										
										
											2019-06-21 14:36:26 -06:00
										 |  |  | 		err := rm.Provision(ctx) | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | // Validate validates m's regular expressions. | 
					
						
							| 
									
										
										
										
											2019-05-22 12:32:36 -06:00
										 |  |  | func (m MatchHeaderRE) Validate() error { | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	for _, rm := range m { | 
					
						
							|  |  |  | 		err := rm.Validate() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | // CaddyModule returns the Caddy module information. | 
					
						
							|  |  |  | func (MatchProtocol) CaddyModule() caddy.ModuleInfo { | 
					
						
							|  |  |  | 	return caddy.ModuleInfo{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		ID:  "http.matchers.protocol", | 
					
						
							|  |  |  | 		New: func() caddy.Module { return new(MatchProtocol) }, | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | // Match returns true if r matches m. | 
					
						
							| 
									
										
										
										
											2019-05-22 12:32:36 -06:00
										 |  |  | func (m MatchProtocol) Match(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	switch string(m) { | 
					
						
							|  |  |  | 	case "grpc": | 
					
						
							|  |  |  | 		return r.Header.Get("content-type") == "application/grpc" | 
					
						
							|  |  |  | 	case "https": | 
					
						
							|  |  |  | 		return r.TLS != nil | 
					
						
							|  |  |  | 	case "http": | 
					
						
							|  |  |  | 		return r.TLS == nil | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // UnmarshalCaddyfile implements caddyfile.Unmarshaler. | 
					
						
							|  |  |  | func (m *MatchProtocol) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	for d.Next() { | 
					
						
							|  |  |  | 		var proto string | 
					
						
							|  |  |  | 		if !d.Args(&proto) { | 
					
						
							|  |  |  | 			return d.Err("expected exactly one protocol") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		*m = MatchProtocol(proto) | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | // CaddyModule returns the Caddy module information. | 
					
						
							| 
									
										
										
										
											2020-03-30 11:53:19 -06:00
										 |  |  | func (MatchNot) CaddyModule() caddy.ModuleInfo { | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	return caddy.ModuleInfo{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		ID:  "http.matchers.not", | 
					
						
							| 
									
										
										
										
											2020-03-30 11:53:19 -06:00
										 |  |  | 		New: func() caddy.Module { return new(MatchNot) }, | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // UnmarshalCaddyfile implements caddyfile.Unmarshaler. | 
					
						
							| 
									
										
										
										
											2020-03-30 11:53:19 -06:00
										 |  |  | func (m *MatchNot) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { | 
					
						
							| 
									
										
										
										
											2019-09-30 09:09:57 -06:00
										 |  |  | 	// first, unmarshal each matcher in the set from its tokens | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | 	type matcherPair struct { | 
					
						
							|  |  |  | 		raw     caddy.ModuleMap | 
					
						
							|  |  |  | 		decoded MatcherSet | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-30 09:09:57 -06:00
										 |  |  | 	for d.Next() { | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | 		var mp matcherPair | 
					
						
							|  |  |  | 		matcherMap := make(map[string]RequestMatcher) | 
					
						
							| 
									
										
										
										
											2020-05-11 16:38:33 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// in case there are multiple instances of the same matcher, concatenate | 
					
						
							|  |  |  | 		// their tokens (we expect that UnmarshalCaddyfile should be able to | 
					
						
							|  |  |  | 		// handle more than one segment); otherwise, we'd overwrite other | 
					
						
							|  |  |  | 		// instances of the matcher in this set | 
					
						
							|  |  |  | 		tokensByMatcherName := make(map[string][]caddyfile.Token) | 
					
						
							|  |  |  | 		for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); { | 
					
						
							| 
									
										
										
										
											2019-09-30 09:09:57 -06:00
										 |  |  | 			matcherName := d.Val() | 
					
						
							| 
									
										
										
										
											2020-05-11 16:38:33 -04:00
										 |  |  | 			tokensByMatcherName[matcherName] = append(tokensByMatcherName[matcherName], d.NextSegment()...) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for matcherName, tokens := range tokensByMatcherName { | 
					
						
							| 
									
										
										
										
											2019-09-30 09:09:57 -06:00
										 |  |  | 			mod, err := caddy.GetModule("http.matchers." + matcherName) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return d.Errf("getting matcher module '%s': %v", matcherName, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			unm, ok := mod.New().(caddyfile.Unmarshaler) | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				return d.Errf("matcher module '%s' is not a Caddyfile unmarshaler", matcherName) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-05-11 16:38:33 -04:00
										 |  |  | 			err = unm.UnmarshalCaddyfile(caddyfile.NewDispenser(tokens)) | 
					
						
							| 
									
										
										
										
											2019-09-30 09:09:57 -06:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-05-11 16:38:33 -04:00
										 |  |  | 			rm, ok := unm.(RequestMatcher) | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				return fmt.Errorf("matcher module '%s' is not a request matcher", matcherName) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-09-30 09:09:57 -06:00
										 |  |  | 			matcherMap[matcherName] = rm | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | 			mp.decoded = append(mp.decoded, rm) | 
					
						
							| 
									
										
										
										
											2019-09-30 09:09:57 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | 		// we should now have a functional 'not' matcher, but we also | 
					
						
							|  |  |  | 		// need to be able to marshal as JSON, otherwise config | 
					
						
							|  |  |  | 		// adaptation will be missing the matchers! | 
					
						
							|  |  |  | 		mp.raw = make(caddy.ModuleMap) | 
					
						
							|  |  |  | 		for name, matcher := range matcherMap { | 
					
						
							|  |  |  | 			jsonBytes, err := json.Marshal(matcher) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return fmt.Errorf("marshaling %T matcher: %v", matcher, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			mp.raw[name] = jsonBytes | 
					
						
							| 
									
										
										
										
											2019-09-30 09:09:57 -06:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | 		m.MatcherSetsRaw = append(m.MatcherSetsRaw, mp.raw) | 
					
						
							| 
									
										
										
										
											2019-09-30 09:09:57 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | // UnmarshalJSON satisfies json.Unmarshaler. It puts the JSON | 
					
						
							|  |  |  | // bytes directly into m's MatcherSetsRaw field. | 
					
						
							|  |  |  | func (m *MatchNot) UnmarshalJSON(data []byte) error { | 
					
						
							|  |  |  | 	return json.Unmarshal(data, &m.MatcherSetsRaw) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MarshalJSON satisfies json.Marshaler by marshaling | 
					
						
							|  |  |  | // m's raw matcher sets. | 
					
						
							|  |  |  | func (m MatchNot) MarshalJSON() ([]byte, error) { | 
					
						
							|  |  |  | 	return json.Marshal(m.MatcherSetsRaw) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | // Provision loads the matcher modules to be negated. | 
					
						
							| 
									
										
										
										
											2020-03-30 11:53:19 -06:00
										 |  |  | func (m *MatchNot) Provision(ctx caddy.Context) error { | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | 	matcherSets, err := ctx.LoadModule(m, "MatcherSetsRaw") | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | 		return fmt.Errorf("loading matcher sets: %v", err) | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | 	for _, modMap := range matcherSets.([]map[string]interface{}) { | 
					
						
							|  |  |  | 		var ms MatcherSet | 
					
						
							|  |  |  | 		for _, modIface := range modMap { | 
					
						
							|  |  |  | 			ms = append(ms, modIface.(RequestMatcher)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		m.MatcherSets = append(m.MatcherSets, ms) | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | // Match returns true if r matches m. Since this matcher negates | 
					
						
							|  |  |  | // the embedded matchers, false is returned if any of its matcher | 
					
						
							|  |  |  | // sets return true. | 
					
						
							| 
									
										
										
										
											2020-03-30 11:53:19 -06:00
										 |  |  | func (m MatchNot) Match(r *http.Request) bool { | 
					
						
							| 
									
										
										
										
											2020-04-01 10:58:29 -06:00
										 |  |  | 	for _, ms := range m.MatcherSets { | 
					
						
							|  |  |  | 		if ms.Match(r) { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | // CaddyModule returns the Caddy module information. | 
					
						
							|  |  |  | func (MatchRemoteIP) CaddyModule() caddy.ModuleInfo { | 
					
						
							|  |  |  | 	return caddy.ModuleInfo{ | 
					
						
							| 
									
										
										
										
											2019-12-10 13:36:46 -07:00
										 |  |  | 		ID:  "http.matchers.remote_ip", | 
					
						
							|  |  |  | 		New: func() caddy.Module { return new(MatchRemoteIP) }, | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // UnmarshalCaddyfile implements caddyfile.Unmarshaler. | 
					
						
							|  |  |  | func (m *MatchRemoteIP) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	for d.Next() { | 
					
						
							| 
									
										
										
										
											2020-12-10 16:09:30 -07:00
										 |  |  | 		for d.NextArg() { | 
					
						
							|  |  |  | 			if d.Val() == "forwarded" { | 
					
						
							|  |  |  | 				if len(m.Ranges) > 0 { | 
					
						
							|  |  |  | 					return d.Err("if used, 'forwarded' must be first argument") | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				m.Forwarded = true | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			m.Ranges = append(m.Ranges, d.Val()) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-04-06 15:07:07 -04:00
										 |  |  | 		if d.NextBlock(0) { | 
					
						
							|  |  |  | 			return d.Err("malformed remote_ip matcher: blocks are not supported") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | // Provision parses m's IP ranges, either from IP or CIDR expressions. | 
					
						
							| 
									
										
										
										
											2019-06-14 11:58:28 -06:00
										 |  |  | func (m *MatchRemoteIP) Provision(ctx caddy.Context) error { | 
					
						
							| 
									
										
										
										
											2020-05-26 17:35:27 -06:00
										 |  |  | 	m.logger = ctx.Logger(m) | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | 	for _, str := range m.Ranges { | 
					
						
							|  |  |  | 		if strings.Contains(str, "/") { | 
					
						
							|  |  |  | 			_, ipNet, err := net.ParseCIDR(str) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return fmt.Errorf("parsing CIDR expression: %v", err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			m.cidrs = append(m.cidrs, ipNet) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			ip := net.ParseIP(str) | 
					
						
							|  |  |  | 			if ip == nil { | 
					
						
							|  |  |  | 				return fmt.Errorf("invalid IP address: %s", str) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			mask := len(ip) * 8 | 
					
						
							|  |  |  | 			m.cidrs = append(m.cidrs, &net.IPNet{ | 
					
						
							|  |  |  | 				IP:   ip, | 
					
						
							|  |  |  | 				Mask: net.CIDRMask(mask, mask), | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m MatchRemoteIP) getClientIP(r *http.Request) (net.IP, error) { | 
					
						
							| 
									
										
										
										
											2020-12-10 16:09:30 -07:00
										 |  |  | 	remote := r.RemoteAddr | 
					
						
							|  |  |  | 	if m.Forwarded { | 
					
						
							|  |  |  | 		if fwdFor := r.Header.Get("X-Forwarded-For"); fwdFor != "" { | 
					
						
							|  |  |  | 			remote = strings.TrimSpace(strings.Split(fwdFor, ",")[0]) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	ipStr, _, err := net.SplitHostPort(remote) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		ipStr = remote // OK; probably didn't have a port | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	ip := net.ParseIP(ipStr) | 
					
						
							|  |  |  | 	if ip == nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("invalid client IP address: %s", ipStr) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return ip, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Match returns true if r matches m. | 
					
						
							|  |  |  | func (m MatchRemoteIP) Match(r *http.Request) bool { | 
					
						
							|  |  |  | 	clientIP, err := m.getClientIP(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-05-26 17:35:27 -06:00
										 |  |  | 		m.logger.Error("getting client IP", zap.Error(err)) | 
					
						
							| 
									
										
										
										
											2019-06-04 13:42:54 -06:00
										 |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, ipRange := range m.cidrs { | 
					
						
							|  |  |  | 		if ipRange.Contains(clientIP) { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-28 10:30:48 +08:00
										 |  |  | // MatchRegexp is an embedable type for matching | 
					
						
							| 
									
										
										
										
											2019-12-29 13:16:34 -07:00
										 |  |  | // using regular expressions. It adds placeholders | 
					
						
							|  |  |  | // to the request's replacer. | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | type MatchRegexp struct { | 
					
						
							| 
									
										
										
										
											2019-12-29 13:16:34 -07:00
										 |  |  | 	// A unique name for this regular expression. Optional, | 
					
						
							|  |  |  | 	// but useful to prevent overwriting captures from other | 
					
						
							|  |  |  | 	// regexp matchers. | 
					
						
							|  |  |  | 	Name string `json:"name,omitempty"` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The regular expression to evaluate, in RE2 syntax, | 
					
						
							|  |  |  | 	// which is the same general syntax used by Go, Perl, | 
					
						
							|  |  |  | 	// and Python. For details, see | 
					
						
							|  |  |  | 	// [Go's regexp package](https://golang.org/pkg/regexp/). | 
					
						
							|  |  |  | 	// Captures are accessible via placeholders. Unnamed | 
					
						
							|  |  |  | 	// capture groups are exposed as their numeric, 1-based | 
					
						
							|  |  |  | 	// index, while named capture groups are available by | 
					
						
							|  |  |  | 	// the capture group name. | 
					
						
							|  |  |  | 	Pattern string `json:"pattern"` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	compiled *regexp.Regexp | 
					
						
							| 
									
										
										
										
											2019-12-29 13:16:34 -07:00
										 |  |  | 	phPrefix string | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | // Provision compiles the regular expression. | 
					
						
							| 
									
										
										
										
											2019-06-21 14:36:26 -06:00
										 |  |  | func (mre *MatchRegexp) Provision(caddy.Context) error { | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	re, err := regexp.Compile(mre.Pattern) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("compiling matcher regexp %s: %v", mre.Pattern, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mre.compiled = re | 
					
						
							| 
									
										
										
										
											2019-12-29 13:16:34 -07:00
										 |  |  | 	mre.phPrefix = regexpPlaceholderPrefix | 
					
						
							|  |  |  | 	if mre.Name != "" { | 
					
						
							|  |  |  | 		mre.phPrefix += "." + mre.Name | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | // Validate ensures mre is set up correctly. | 
					
						
							|  |  |  | func (mre *MatchRegexp) Validate() error { | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	if mre.Name != "" && !wordRE.MatchString(mre.Name) { | 
					
						
							|  |  |  | 		return fmt.Errorf("invalid regexp name (must contain only word characters): %s", mre.Name) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 13:13:39 -06:00
										 |  |  | // Match returns true if input matches the compiled regular | 
					
						
							|  |  |  | // expression in mre. It sets values on the replacer repl | 
					
						
							|  |  |  | // associated with capture groups, using the given scope | 
					
						
							| 
									
										
										
										
											2019-12-29 13:12:52 -07:00
										 |  |  | // (namespace). | 
					
						
							|  |  |  | func (mre *MatchRegexp) Match(input string, repl *caddy.Replacer) bool { | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	matches := mre.compiled.FindStringSubmatch(input) | 
					
						
							|  |  |  | 	if matches == nil { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// save all capture groups, first by index | 
					
						
							|  |  |  | 	for i, match := range matches { | 
					
						
							| 
									
										
										
										
											2021-05-03 00:35:28 +08:00
										 |  |  | 		key := mre.phPrefix + "." + strconv.Itoa(i) | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | 		repl.Set(key, match) | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// then by name | 
					
						
							|  |  |  | 	for i, name := range mre.compiled.SubexpNames() { | 
					
						
							|  |  |  | 		if i != 0 && name != "" { | 
					
						
							| 
									
										
										
										
											2021-05-03 00:35:28 +08:00
										 |  |  | 			key := mre.phPrefix + "." + name | 
					
						
							| 
									
										
										
										
											2019-05-20 10:59:20 -06:00
										 |  |  | 			repl.Set(key, matches[i]) | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // UnmarshalCaddyfile implements caddyfile.Unmarshaler. | 
					
						
							|  |  |  | func (mre *MatchRegexp) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	for d.Next() { | 
					
						
							|  |  |  | 		args := d.RemainingArgs() | 
					
						
							|  |  |  | 		switch len(args) { | 
					
						
							|  |  |  | 		case 1: | 
					
						
							|  |  |  | 			mre.Pattern = args[0] | 
					
						
							|  |  |  | 		case 2: | 
					
						
							|  |  |  | 			mre.Name = args[0] | 
					
						
							|  |  |  | 			mre.Pattern = args[1] | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return d.ArgErr() | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-04-06 15:07:07 -04:00
										 |  |  | 		if d.NextBlock(0) { | 
					
						
							|  |  |  | 			return d.Err("malformed path_regexp matcher: blocks are not supported") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:02 -06:00
										 |  |  | var wordRE = regexp.MustCompile(`\w+`) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-29 13:16:34 -07:00
										 |  |  | const regexpPlaceholderPrefix = "http.regexp" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-11 20:42:55 -06:00
										 |  |  | // Interface guards | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | var ( | 
					
						
							| 
									
										
										
										
											2019-06-20 20:24:46 -06:00
										 |  |  | 	_ RequestMatcher    = (*MatchHost)(nil) | 
					
						
							| 
									
										
										
										
											2020-12-02 13:26:28 -07:00
										 |  |  | 	_ caddy.Provisioner = (*MatchHost)(nil) | 
					
						
							| 
									
										
										
										
											2019-06-20 20:24:46 -06:00
										 |  |  | 	_ RequestMatcher    = (*MatchPath)(nil) | 
					
						
							|  |  |  | 	_ RequestMatcher    = (*MatchPathRE)(nil) | 
					
						
							| 
									
										
										
										
											2019-06-21 14:36:26 -06:00
										 |  |  | 	_ caddy.Provisioner = (*MatchPathRE)(nil) | 
					
						
							| 
									
										
										
										
											2019-06-20 20:24:46 -06:00
										 |  |  | 	_ RequestMatcher    = (*MatchMethod)(nil) | 
					
						
							|  |  |  | 	_ RequestMatcher    = (*MatchQuery)(nil) | 
					
						
							|  |  |  | 	_ RequestMatcher    = (*MatchHeader)(nil) | 
					
						
							|  |  |  | 	_ RequestMatcher    = (*MatchHeaderRE)(nil) | 
					
						
							| 
									
										
										
										
											2019-06-21 14:36:26 -06:00
										 |  |  | 	_ caddy.Provisioner = (*MatchHeaderRE)(nil) | 
					
						
							| 
									
										
										
										
											2019-06-20 20:24:46 -06:00
										 |  |  | 	_ RequestMatcher    = (*MatchProtocol)(nil) | 
					
						
							|  |  |  | 	_ RequestMatcher    = (*MatchRemoteIP)(nil) | 
					
						
							| 
									
										
										
										
											2019-06-14 11:58:28 -06:00
										 |  |  | 	_ caddy.Provisioner = (*MatchRemoteIP)(nil) | 
					
						
							| 
									
										
										
										
											2020-03-30 11:53:19 -06:00
										 |  |  | 	_ RequestMatcher    = (*MatchNot)(nil) | 
					
						
							|  |  |  | 	_ caddy.Provisioner = (*MatchNot)(nil) | 
					
						
							| 
									
										
										
										
											2019-06-21 14:36:26 -06:00
										 |  |  | 	_ caddy.Provisioner = (*MatchRegexp)(nil) | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	_ caddyfile.Unmarshaler = (*MatchHost)(nil) | 
					
						
							|  |  |  | 	_ caddyfile.Unmarshaler = (*MatchPath)(nil) | 
					
						
							|  |  |  | 	_ caddyfile.Unmarshaler = (*MatchPathRE)(nil) | 
					
						
							|  |  |  | 	_ caddyfile.Unmarshaler = (*MatchMethod)(nil) | 
					
						
							|  |  |  | 	_ caddyfile.Unmarshaler = (*MatchQuery)(nil) | 
					
						
							|  |  |  | 	_ caddyfile.Unmarshaler = (*MatchHeader)(nil) | 
					
						
							|  |  |  | 	_ caddyfile.Unmarshaler = (*MatchHeaderRE)(nil) | 
					
						
							|  |  |  | 	_ caddyfile.Unmarshaler = (*MatchProtocol)(nil) | 
					
						
							|  |  |  | 	_ caddyfile.Unmarshaler = (*MatchRemoteIP)(nil) | 
					
						
							| 
									
										
										
										
											2020-09-26 02:50:26 +03:00
										 |  |  | 	_ caddyfile.Unmarshaler = (*VarsMatcher)(nil) | 
					
						
							|  |  |  | 	_ caddyfile.Unmarshaler = (*MatchVarsRE)(nil) | 
					
						
							| 
									
										
										
										
											2019-09-17 15:16:17 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-30 11:53:19 -06:00
										 |  |  | 	_ json.Marshaler   = (*MatchNot)(nil) | 
					
						
							|  |  |  | 	_ json.Unmarshaler = (*MatchNot)(nil) | 
					
						
							| 
									
										
										
										
											2019-03-31 20:41:29 -06:00
										 |  |  | ) |