| 
									
										
										
										
											2019-08-09 12:05:47 -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. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package httpcaddyfile | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											2022-08-18 00:10:57 +02:00
										 |  |  | 	"net/netip" | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	"reflect" | 
					
						
							| 
									
										
										
										
											2020-11-23 14:46:50 -05:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2020-04-14 16:11:46 -06:00
										 |  |  | 	"unicode" | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-14 23:41:15 +08:00
										 |  |  | 	"github.com/caddyserver/certmagic" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-19 09:43:17 -06:00
										 |  |  | 	"github.com/caddyserver/caddy/v2" | 
					
						
							| 
									
										
										
										
											2019-08-22 21:26:48 +03:00
										 |  |  | 	"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	"github.com/caddyserver/caddy/v2/modules/caddyhttp" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-21 15:39:58 +08:00
										 |  |  | // mapAddressToProtocolToServerBlocks returns a map of listener address to list of server | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // blocks that will be served on that address. To do this, each server block is | 
					
						
							|  |  |  | // expanded so that each one is considered individually, although keys of a | 
					
						
							|  |  |  | // server block that share the same address stay grouped together so the config | 
					
						
							|  |  |  | // isn't repeated unnecessarily. For example, this Caddyfile: | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2022-09-15 16:03:24 +02:00
										 |  |  | //	example.com { | 
					
						
							|  |  |  | //		bind 127.0.0.1 | 
					
						
							|  |  |  | //	} | 
					
						
							|  |  |  | //	www.example.com, example.net/path, localhost:9999 { | 
					
						
							|  |  |  | //		bind 127.0.0.1 1.2.3.4 | 
					
						
							|  |  |  | //	} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // | 
					
						
							|  |  |  | // has two server blocks to start with. But expressed in this Caddyfile are | 
					
						
							|  |  |  | // actually 4 listener addresses: 127.0.0.1:443, 1.2.3.4:443, 127.0.0.1:9999, | 
					
						
							|  |  |  | // and 127.0.0.1:9999. This is because the bind directive is applied to each | 
					
						
							|  |  |  | // key of its server block (specifying the host part), and each key may have | 
					
						
							|  |  |  | // a different port. And we definitely need to be sure that a site which is | 
					
						
							|  |  |  | // bound to be served on a specific interface is not served on others just | 
					
						
							| 
									
										
										
										
											2020-02-28 10:30:48 +08:00
										 |  |  | // because that is more convenient: it would be a potential security risk | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // if the difference between interfaces means private vs. public. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // So what this function does for the example above is iterate each server | 
					
						
							|  |  |  | // block, and for each server block, iterate its keys. For the first, it | 
					
						
							|  |  |  | // finds one key (example.com) and determines its listener address | 
					
						
							|  |  |  | // (127.0.0.1:443 - because of 'bind' and automatic HTTPS). It then adds | 
					
						
							|  |  |  | // the listener address to the map value returned by this function, with | 
					
						
							|  |  |  | // the first server block as one of its associations. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // It then iterates each key on the second server block and associates them | 
					
						
							|  |  |  | // with one or more listener addresses. Indeed, each key in this block has | 
					
						
							|  |  |  | // two listener addresses because of the 'bind' directive. Once we know | 
					
						
							|  |  |  | // which addresses serve which keys, we can create a new server block for | 
					
						
							|  |  |  | // each address containing the contents of the server block and only those | 
					
						
							|  |  |  | // specific keys of the server block which use that address. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // It is possible and even likely that some keys in the returned map have | 
					
						
							|  |  |  | // the exact same list of server blocks (i.e. they are identical). This | 
					
						
							|  |  |  | // happens when multiple hosts are declared with a 'bind' directive and | 
					
						
							|  |  |  | // the resulting listener addresses are not shared by any other server | 
					
						
							|  |  |  | // block (or the other server blocks are exactly identical in their token | 
					
						
							|  |  |  | // contents). This happens with our example above because 1.2.3.4:443 | 
					
						
							|  |  |  | // and 1.2.3.4:9999 are used exclusively with the second server block. This | 
					
						
							|  |  |  | // repetition may be undesirable, so call consolidateAddrMappings() to map | 
					
						
							|  |  |  | // multiple addresses to the same lists of server blocks (a many:many mapping). | 
					
						
							|  |  |  | // (Doing this is essentially a map-reduce technique.) | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | func (st *ServerType) mapAddressToProtocolToServerBlocks(originalServerBlocks []serverBlock, | 
					
						
							| 
									
										
										
										
											2023-08-08 03:40:31 +08:00
										 |  |  | 	options map[string]any, | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | ) (map[string]map[string][]serverBlock, error) { | 
					
						
							|  |  |  | 	addrToProtocolToServerBlocks := map[string]map[string][]serverBlock{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	type keyWithParsedKey struct { | 
					
						
							|  |  |  | 		key       caddyfile.Token | 
					
						
							|  |  |  | 		parsedKey Address | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for i, sblock := range originalServerBlocks { | 
					
						
							|  |  |  | 		// within a server block, we need to map all the listener addresses | 
					
						
							|  |  |  | 		// implied by the server block to the keys of the server block which | 
					
						
							|  |  |  | 		// will be served by them; this has the effect of treating each | 
					
						
							|  |  |  | 		// key of a server block as its own, but without having to repeat its | 
					
						
							|  |  |  | 		// contents in cases where multiple keys really can be served together | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 		addrToProtocolToKeyWithParsedKeys := map[string]map[string][]keyWithParsedKey{} | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 		for j, key := range sblock.block.Keys { | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 			parsedKey, err := ParseAddress(key.Text) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, fmt.Errorf("parsing key: %v", err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			parsedKey = parsedKey.Normalize() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 			// a key can have multiple listener addresses if there are multiple | 
					
						
							|  |  |  | 			// arguments to the 'bind' directive (although they will all have | 
					
						
							|  |  |  | 			// the same port, since the port is defined by the key or is implicit | 
					
						
							|  |  |  | 			// through automatic HTTPS) | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 			listeners, err := st.listenersForServerBlockAddress(sblock, parsedKey, options) | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2024-02-18 18:22:48 -06:00
										 |  |  | 				return nil, fmt.Errorf("server block %d, key %d (%s): determining listener address: %v", i, j, key.Text, err) | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 			// associate this key with its protocols and each listener address served with them | 
					
						
							|  |  |  | 			kwpk := keyWithParsedKey{key, parsedKey} | 
					
						
							|  |  |  | 			for addr, protocols := range listeners { | 
					
						
							|  |  |  | 				protocolToKeyWithParsedKeys, ok := addrToProtocolToKeyWithParsedKeys[addr] | 
					
						
							|  |  |  | 				if !ok { | 
					
						
							|  |  |  | 					protocolToKeyWithParsedKeys = map[string][]keyWithParsedKey{} | 
					
						
							|  |  |  | 					addrToProtocolToKeyWithParsedKeys[addr] = protocolToKeyWithParsedKeys | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// an empty protocol indicates the default, a nil or empty value in the ListenProtocols array | 
					
						
							|  |  |  | 				if len(protocols) == 0 { | 
					
						
							|  |  |  | 					protocols[""] = struct{}{} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				for prot := range protocols { | 
					
						
							|  |  |  | 					protocolToKeyWithParsedKeys[prot] = append( | 
					
						
							|  |  |  | 						protocolToKeyWithParsedKeys[prot], | 
					
						
							|  |  |  | 						kwpk) | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-08 21:32:10 -04:00
										 |  |  | 		// make a slice of the map keys so we can iterate in sorted order | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 		addrs := make([]string, 0, len(addrToProtocolToKeyWithParsedKeys)) | 
					
						
							|  |  |  | 		for addr := range addrToProtocolToKeyWithParsedKeys { | 
					
						
							|  |  |  | 			addrs = append(addrs, addr) | 
					
						
							| 
									
										
										
										
											2022-05-08 21:32:10 -04:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		sort.Strings(addrs) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 		// now that we know which addresses serve which keys of this | 
					
						
							|  |  |  | 		// server block, we iterate that mapping and create a list of | 
					
						
							|  |  |  | 		// new server blocks for each address where the keys of the | 
					
						
							|  |  |  | 		// server block are only the ones which use the address; but | 
					
						
							|  |  |  | 		// the contents (tokens) are of course the same | 
					
						
							| 
									
										
										
										
											2022-05-08 21:32:10 -04:00
										 |  |  | 		for _, addr := range addrs { | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 			protocolToKeyWithParsedKeys := addrToProtocolToKeyWithParsedKeys[addr] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			prots := make([]string, 0, len(protocolToKeyWithParsedKeys)) | 
					
						
							|  |  |  | 			for prot := range protocolToKeyWithParsedKeys { | 
					
						
							|  |  |  | 				prots = append(prots, prot) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			sort.Strings(prots) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			protocolToServerBlocks, ok := addrToProtocolToServerBlocks[addr] | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				protocolToServerBlocks = map[string][]serverBlock{} | 
					
						
							|  |  |  | 				addrToProtocolToServerBlocks[addr] = protocolToServerBlocks | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for _, prot := range prots { | 
					
						
							|  |  |  | 				keyWithParsedKeys := protocolToKeyWithParsedKeys[prot] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				keys := make([]caddyfile.Token, len(keyWithParsedKeys)) | 
					
						
							|  |  |  | 				parsedKeys := make([]Address, len(keyWithParsedKeys)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				for k, keyWithParsedKey := range keyWithParsedKeys { | 
					
						
							|  |  |  | 					keys[k] = keyWithParsedKey.key | 
					
						
							|  |  |  | 					parsedKeys[k] = keyWithParsedKey.parsedKey | 
					
						
							| 
									
										
										
										
											2020-04-02 14:20:30 -06:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				protocolToServerBlocks[prot] = append(protocolToServerBlocks[prot], serverBlock{ | 
					
						
							|  |  |  | 					block: caddyfile.ServerBlock{ | 
					
						
							|  |  |  | 						Keys:     keys, | 
					
						
							|  |  |  | 						Segments: sblock.block.Segments, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					pile:       sblock.pile, | 
					
						
							|  |  |  | 					parsedKeys: parsedKeys, | 
					
						
							|  |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2020-04-02 14:20:30 -06:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 	return addrToProtocolToServerBlocks, nil | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // consolidateAddrMappings eliminates repetition of identical server blocks in a mapping of | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | // single listener addresses to protocols to lists of server blocks. Since multiple addresses | 
					
						
							|  |  |  | // may serve multiple protocols to identical sites (server block contents), this function turns | 
					
						
							|  |  |  | // a 1:many mapping into a many:many mapping. Server block contents (tokens) must be | 
					
						
							|  |  |  | // exactly identical so that reflect.DeepEqual returns true in order for the addresses to be combined. | 
					
						
							|  |  |  | // Identical entries are deleted from the addrToServerBlocks map. Essentially, each pairing (each | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | // association from multiple addresses to multiple server blocks; i.e. each element of | 
					
						
							|  |  |  | // the returned slice) becomes a server definition in the output JSON. | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | func (st *ServerType) consolidateAddrMappings(addrToProtocolToServerBlocks map[string]map[string][]serverBlock) []sbAddrAssociation { | 
					
						
							|  |  |  | 	sbaddrs := make([]sbAddrAssociation, 0, len(addrToProtocolToServerBlocks)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addrs := make([]string, 0, len(addrToProtocolToServerBlocks)) | 
					
						
							|  |  |  | 	for addr := range addrToProtocolToServerBlocks { | 
					
						
							|  |  |  | 		addrs = append(addrs, addr) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sort.Strings(addrs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, addr := range addrs { | 
					
						
							|  |  |  | 		protocolToServerBlocks := addrToProtocolToServerBlocks[addr] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		prots := make([]string, 0, len(protocolToServerBlocks)) | 
					
						
							|  |  |  | 		for prot := range protocolToServerBlocks { | 
					
						
							|  |  |  | 			prots = append(prots, prot) | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 		sort.Strings(prots) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for _, prot := range prots { | 
					
						
							|  |  |  | 			serverBlocks := protocolToServerBlocks[prot] | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 			// now find other addresses that map to identical | 
					
						
							|  |  |  | 			// server blocks and add them to our map of listener | 
					
						
							|  |  |  | 			// addresses and protocols, while removing them from | 
					
						
							|  |  |  | 			// the original map | 
					
						
							|  |  |  | 			listeners := map[string]map[string]struct{}{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for otherAddr, otherProtocolToServerBlocks := range addrToProtocolToServerBlocks { | 
					
						
							|  |  |  | 				for otherProt, otherServerBlocks := range otherProtocolToServerBlocks { | 
					
						
							|  |  |  | 					if addr == otherAddr && prot == otherProt || reflect.DeepEqual(serverBlocks, otherServerBlocks) { | 
					
						
							|  |  |  | 						listener, ok := listeners[otherAddr] | 
					
						
							|  |  |  | 						if !ok { | 
					
						
							|  |  |  | 							listener = map[string]struct{}{} | 
					
						
							|  |  |  | 							listeners[otherAddr] = listener | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						listener[otherProt] = struct{}{} | 
					
						
							|  |  |  | 						delete(otherProtocolToServerBlocks, otherProt) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			addresses := make([]string, 0, len(listeners)) | 
					
						
							|  |  |  | 			for lnAddr := range listeners { | 
					
						
							|  |  |  | 				addresses = append(addresses, lnAddr) | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 			sort.Strings(addresses) | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 			addressesWithProtocols := make([]addressWithProtocols, 0, len(listeners)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for _, lnAddr := range addresses { | 
					
						
							|  |  |  | 				lnProts := listeners[lnAddr] | 
					
						
							|  |  |  | 				prots := make([]string, 0, len(lnProts)) | 
					
						
							|  |  |  | 				for prot := range lnProts { | 
					
						
							|  |  |  | 					prots = append(prots, prot) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				sort.Strings(prots) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				addressesWithProtocols = append(addressesWithProtocols, addressWithProtocols{ | 
					
						
							|  |  |  | 					address:   lnAddr, | 
					
						
							|  |  |  | 					protocols: prots, | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-23 14:46:50 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 			sbaddrs = append(sbaddrs, sbAddrAssociation{ | 
					
						
							|  |  |  | 				addressesWithProtocols: addressesWithProtocols, | 
					
						
							|  |  |  | 				serverBlocks:           serverBlocks, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-23 14:46:50 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	return sbaddrs | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | // listenersForServerBlockAddress essentially converts the Caddyfile site addresses to a map from | 
					
						
							|  |  |  | // Caddy listener addresses and the protocols to serve them with to the parsed address for each server block. | 
					
						
							|  |  |  | func (st *ServerType) listenersForServerBlockAddress(sblock serverBlock, addr Address, | 
					
						
							| 
									
										
										
										
											2023-08-08 03:40:31 +08:00
										 |  |  | 	options map[string]any, | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | ) (map[string]map[string]struct{}, error) { | 
					
						
							| 
									
										
										
										
											2023-08-19 16:58:25 +05:30
										 |  |  | 	switch addr.Scheme { | 
					
						
							|  |  |  | 	case "wss": | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("the scheme wss:// is only supported in browsers; use https:// instead") | 
					
						
							|  |  |  | 	case "ws": | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("the scheme ws:// is only supported in browsers; use http:// instead") | 
					
						
							|  |  |  | 	case "https", "http", "": | 
					
						
							|  |  |  | 		// Do nothing or handle the valid schemes | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("unsupported URL scheme %s://", addr.Scheme) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-19 11:51:17 -07:00
										 |  |  | 	// figure out the HTTP and HTTPS ports; either | 
					
						
							|  |  |  | 	// use defaults, or override with user config | 
					
						
							| 
									
										
										
										
											2020-04-02 14:20:30 -06:00
										 |  |  | 	httpPort, httpsPort := strconv.Itoa(caddyhttp.DefaultHTTPPort), strconv.Itoa(caddyhttp.DefaultHTTPSPort) | 
					
						
							| 
									
										
										
										
											2020-01-19 11:51:17 -07:00
										 |  |  | 	if hport, ok := options["http_port"]; ok { | 
					
						
							|  |  |  | 		httpPort = strconv.Itoa(hport.(int)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if hsport, ok := options["https_port"]; ok { | 
					
						
							|  |  |  | 		httpsPort = strconv.Itoa(hsport.(int)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-13 11:06:08 -06:00
										 |  |  | 	// default port is the HTTPS port | 
					
						
							|  |  |  | 	lnPort := httpsPort | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	if addr.Port != "" { | 
					
						
							|  |  |  | 		// port explicitly defined | 
					
						
							|  |  |  | 		lnPort = addr.Port | 
					
						
							| 
									
										
										
										
											2020-03-13 11:06:08 -06:00
										 |  |  | 	} else if addr.Scheme == "http" { | 
					
						
							| 
									
										
										
										
											2020-01-19 11:51:17 -07:00
										 |  |  | 		// port inferred from scheme | 
					
						
							| 
									
										
										
										
											2020-03-13 11:06:08 -06:00
										 |  |  | 		lnPort = httpPort | 
					
						
							| 
									
										
										
										
											2020-01-19 11:51:17 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// error if scheme and port combination violate convention | 
					
						
							|  |  |  | 	if (addr.Scheme == "http" && lnPort == httpsPort) || (addr.Scheme == "https" && lnPort == httpPort) { | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 		return nil, fmt.Errorf("[%s] scheme and port violate convention", addr.String()) | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 	// the bind directive specifies hosts (and potentially network), and the protocols to serve them with, but is optional | 
					
						
							|  |  |  | 	lnCfgVals := make([]addressesWithProtocols, 0, len(sblock.pile["bind"])) | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	for _, cfgVal := range sblock.pile["bind"] { | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 		if val, ok := cfgVal.Value.(addressesWithProtocols); ok { | 
					
						
							|  |  |  | 			lnCfgVals = append(lnCfgVals, val) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 	if len(lnCfgVals) == 0 { | 
					
						
							|  |  |  | 		if defaultBindValues, ok := options["default_bind"].([]ConfigValue); ok { | 
					
						
							|  |  |  | 			for _, defaultBindValue := range defaultBindValues { | 
					
						
							|  |  |  | 				lnCfgVals = append(lnCfgVals, defaultBindValue.Value.(addressesWithProtocols)) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-01-18 13:29:07 -05:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 			lnCfgVals = []addressesWithProtocols{{ | 
					
						
							|  |  |  | 				addresses: []string{""}, | 
					
						
							|  |  |  | 				protocols: nil, | 
					
						
							|  |  |  | 			}} | 
					
						
							| 
									
										
										
										
											2022-01-18 13:29:07 -05:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// use a map to prevent duplication | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 	listeners := map[string]map[string]struct{}{} | 
					
						
							|  |  |  | 	for _, lnCfgVal := range lnCfgVals { | 
					
						
							| 
									
										
										
										
											2024-10-21 10:02:29 -04:00
										 |  |  | 		for _, lnAddr := range lnCfgVal.addresses { | 
					
						
							|  |  |  | 			lnNetw, lnHost, _, err := caddy.SplitNetworkAddress(lnAddr) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, fmt.Errorf("splitting listener address: %v", err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			networkAddr, err := caddy.ParseNetworkAddress(caddy.JoinNetworkAddress(lnNetw, lnHost, lnPort)) | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return nil, fmt.Errorf("parsing network address: %v", err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if _, ok := listeners[addr.String()]; !ok { | 
					
						
							|  |  |  | 				listeners[networkAddr.String()] = map[string]struct{}{} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			for _, protocol := range lnCfgVal.protocols { | 
					
						
							|  |  |  | 				listeners[networkAddr.String()][protocol] = struct{}{} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-03-19 09:43:17 -06:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | 	return listeners, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-30 12:55:03 -04:00
										 |  |  | // addressesWithProtocols associates a list of listen addresses | 
					
						
							|  |  |  | // with a list of protocols to serve them with | 
					
						
							|  |  |  | type addressesWithProtocols struct { | 
					
						
							|  |  |  | 	addresses []string | 
					
						
							|  |  |  | 	protocols []string | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Address represents a site address. It contains | 
					
						
							|  |  |  | // the original input value, and the component | 
					
						
							|  |  |  | // parts of an address. The component parts may be | 
					
						
							|  |  |  | // updated to the correct values as setup proceeds, | 
					
						
							|  |  |  | // but the original value should never be changed. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The Host field must be in a normalized form. | 
					
						
							|  |  |  | type Address struct { | 
					
						
							|  |  |  | 	Original, Scheme, Host, Port, Path string | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | // ParseAddress parses an address string into a structured format with separate | 
					
						
							|  |  |  | // scheme, host, port, and path portions, as well as the original input string. | 
					
						
							|  |  |  | func ParseAddress(str string) (Address, error) { | 
					
						
							| 
									
										
										
										
											2020-01-09 12:35:53 -07:00
										 |  |  | 	const maxLen = 4096 | 
					
						
							|  |  |  | 	if len(str) > maxLen { | 
					
						
							|  |  |  | 		str = str[:maxLen] | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-09 12:35:53 -07:00
										 |  |  | 	remaining := strings.TrimSpace(str) | 
					
						
							|  |  |  | 	a := Address{Original: remaining} | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-09 12:35:53 -07:00
										 |  |  | 	// extract scheme | 
					
						
							|  |  |  | 	splitScheme := strings.SplitN(remaining, "://", 2) | 
					
						
							|  |  |  | 	switch len(splitScheme) { | 
					
						
							|  |  |  | 	case 0: | 
					
						
							|  |  |  | 		return a, nil | 
					
						
							|  |  |  | 	case 1: | 
					
						
							|  |  |  | 		remaining = splitScheme[0] | 
					
						
							|  |  |  | 	case 2: | 
					
						
							|  |  |  | 		a.Scheme = splitScheme[0] | 
					
						
							|  |  |  | 		remaining = splitScheme[1] | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-09 12:35:53 -07:00
										 |  |  | 	// extract host and port | 
					
						
							|  |  |  | 	hostSplit := strings.SplitN(remaining, "/", 2) | 
					
						
							|  |  |  | 	if len(hostSplit) > 0 { | 
					
						
							|  |  |  | 		host, port, err := net.SplitHostPort(hostSplit[0]) | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-01-09 12:35:53 -07:00
										 |  |  | 			host, port, err = net.SplitHostPort(hostSplit[0] + ":") | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				host = hostSplit[0] | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-09 12:35:53 -07:00
										 |  |  | 		a.Host = host | 
					
						
							|  |  |  | 		a.Port = port | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-09 12:35:53 -07:00
										 |  |  | 	if len(hostSplit) == 2 { | 
					
						
							|  |  |  | 		// all that remains is the path | 
					
						
							|  |  |  | 		a.Path = "/" + hostSplit[1] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// make sure port is valid | 
					
						
							|  |  |  | 	if a.Port != "" { | 
					
						
							|  |  |  | 		if portNum, err := strconv.Atoi(a.Port); err != nil { | 
					
						
							|  |  |  | 			return Address{}, fmt.Errorf("invalid port '%s': %v", a.Port, err) | 
					
						
							|  |  |  | 		} else if portNum < 0 || portNum > 65535 { | 
					
						
							|  |  |  | 			return Address{}, fmt.Errorf("port %d is out of range", portNum) | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-09 12:35:53 -07:00
										 |  |  | 	return a, nil | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // String returns a human-readable form of a. It will | 
					
						
							|  |  |  | // be a cleaned-up and filled-out URL string. | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | func (a Address) String() string { | 
					
						
							|  |  |  | 	if a.Host == "" && a.Port == "" { | 
					
						
							|  |  |  | 		return "" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	scheme := a.Scheme | 
					
						
							|  |  |  | 	if scheme == "" { | 
					
						
							|  |  |  | 		if a.Port == strconv.Itoa(certmagic.HTTPSPort) { | 
					
						
							|  |  |  | 			scheme = "https" | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			scheme = "http" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	s := scheme | 
					
						
							|  |  |  | 	if s != "" { | 
					
						
							|  |  |  | 		s += "://" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if a.Port != "" && | 
					
						
							|  |  |  | 		((scheme == "https" && a.Port != strconv.Itoa(caddyhttp.DefaultHTTPSPort)) || | 
					
						
							|  |  |  | 			(scheme == "http" && a.Port != strconv.Itoa(caddyhttp.DefaultHTTPPort))) { | 
					
						
							|  |  |  | 		s += net.JoinHostPort(a.Host, a.Port) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		s += a.Host | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if a.Path != "" { | 
					
						
							|  |  |  | 		s += a.Path | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return s | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:46:35 -06:00
										 |  |  | // Normalize returns a normalized version of a. | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | func (a Address) Normalize() Address { | 
					
						
							|  |  |  | 	path := a.Path | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// ensure host is normalized if it's an IP address | 
					
						
							| 
									
										
										
										
											2020-03-17 21:00:45 -06:00
										 |  |  | 	host := strings.TrimSpace(a.Host) | 
					
						
							| 
									
										
										
										
											2022-08-18 00:10:57 +02:00
										 |  |  | 	if ip, err := netip.ParseAddr(host); err == nil { | 
					
						
							|  |  |  | 		if ip.Is6() && !ip.Is4() && !ip.Is4In6() { | 
					
						
							|  |  |  | 			host = ip.String() | 
					
						
							| 
									
										
										
										
											2021-10-20 10:27:59 -06:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return Address{ | 
					
						
							|  |  |  | 		Original: a.Original, | 
					
						
							| 
									
										
										
										
											2020-04-14 16:11:46 -06:00
										 |  |  | 		Scheme:   lowerExceptPlaceholders(a.Scheme), | 
					
						
							|  |  |  | 		Host:     lowerExceptPlaceholders(host), | 
					
						
							| 
									
										
										
										
											2019-08-09 12:05:47 -06:00
										 |  |  | 		Port:     a.Port, | 
					
						
							|  |  |  | 		Path:     path, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-14 16:11:46 -06:00
										 |  |  | // lowerExceptPlaceholders lowercases s except within | 
					
						
							|  |  |  | // placeholders (substrings in non-escaped '{ }' spans). | 
					
						
							|  |  |  | // See https://github.com/caddyserver/caddy/issues/3264 | 
					
						
							|  |  |  | func lowerExceptPlaceholders(s string) string { | 
					
						
							|  |  |  | 	var sb strings.Builder | 
					
						
							|  |  |  | 	var escaped, inPlaceholder bool | 
					
						
							|  |  |  | 	for _, ch := range s { | 
					
						
							|  |  |  | 		if ch == '\\' && !escaped { | 
					
						
							|  |  |  | 			escaped = true | 
					
						
							|  |  |  | 			sb.WriteRune(ch) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if ch == '{' && !escaped { | 
					
						
							|  |  |  | 			inPlaceholder = true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if ch == '}' && inPlaceholder && !escaped { | 
					
						
							|  |  |  | 			inPlaceholder = false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if inPlaceholder { | 
					
						
							|  |  |  | 			sb.WriteRune(ch) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			sb.WriteRune(unicode.ToLower(ch)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		escaped = false | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return sb.String() | 
					
						
							|  |  |  | } |