Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ func (admin *AdminConfig) newAdminHandler(addr NetworkAddress, remote bool, _ Co
muxWrap.remoteControl = admin.Remote
} else {
// see comment in allowedOrigins() as to why we disable the host check for unix/fd networks
muxWrap.enforceHost = !addr.isWildcardInterface() && !addr.IsUnixNetwork() && !addr.IsFdNetwork()
muxWrap.enforceHost = !addr.isWildcardInterface() && !addr.IsUnixNetwork() && !addr.IsFDNetwork()
muxWrap.allowedOrigins = admin.allowedOrigins(addr)
muxWrap.enforceOrigin = admin.EnforceOrigin
}
Expand Down Expand Up @@ -342,7 +342,7 @@ func (admin AdminConfig) allowedOrigins(addr NetworkAddress) []*url.URL {
// and a false sense of security.
//
// See also the discussion in #6832.
if admin.Origins == nil && !addr.IsUnixNetwork() && !addr.IsFdNetwork() {
if admin.Origins == nil && !addr.IsUnixNetwork() && !addr.IsFDNetwork() {
if addr.isLoopback() {
uniqueOrigins[net.JoinHostPort("localhost", addr.port())] = struct{}{}
uniqueOrigins[net.JoinHostPort("::1", addr.port())] = struct{}{}
Expand Down
93 changes: 85 additions & 8 deletions caddyconfig/httpcaddyfile/addresses.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,29 +307,107 @@ func (st *ServerType) listenersForServerBlockAddress(sblock serverBlock, addr Ad
}

// 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"]))
lnCfgVals := make([]bindOptions, 0, len(sblock.pile["bind"]))
for _, cfgVal := range sblock.pile["bind"] {
if val, ok := cfgVal.Value.(addressesWithProtocols); ok {
if val, ok := cfgVal.Value.(bindOptions); ok {
lnCfgVals = append(lnCfgVals, val)
}
}
if len(lnCfgVals) == 0 {
if defaultBindValues, ok := options["default_bind"].([]ConfigValue); ok {
for _, defaultBindValue := range defaultBindValues {
lnCfgVals = append(lnCfgVals, defaultBindValue.Value.(addressesWithProtocols))
lnCfgVals = append(lnCfgVals, defaultBindValue.Value.(bindOptions))
}
} else {
lnCfgVals = []addressesWithProtocols{{
lnCfgVals = []bindOptions{{
addresses: []string{""},
protocols: nil,
toDevice: false,
}}
}
}

// use a map to prevent duplication
listeners := map[string]map[string]struct{}{}
interfaces := map[string][]net.Addr{}
for _, lnCfgVal := range lnCfgVals {
for _, lnAddr := range lnCfgVal.addresses {
var lnAddresses []string
for _, lnAddress := range lnCfgVal.addresses {
if lnCfgVal.toDevice {
lnNetw, lnDevice, _, err := caddy.SplitNetworkAddress(lnAddress)
if err != nil {
return nil, fmt.Errorf("splitting listener interface: %v", err)
}

ifaceAddresses, ok := interfaces[lnDevice]
if !ok {
iface, err := net.InterfaceByName(lnDevice)
if err != nil {
return nil, fmt.Errorf("querying listener interface: %v: %v", lnDevice, err)
}
if iface == nil {
return nil, fmt.Errorf("querying listener interface: %v", lnDevice)
}
ifaceAddresses, err := iface.Addrs()
if err != nil {
return nil, fmt.Errorf("querying listener interface addresses: %v: %v", lnDevice, err)
}
interfaces[lnDevice] = ifaceAddresses
}

lnIfaceAddresses := []string{}
for _, ifaceAddress := range ifaceAddresses {
if caddy.IsReservedNetwork(lnNetw) {
var addrok, netwok bool

var ip net.IP
switch ifaceAddressValue := ifaceAddress.(type) {
case *net.IPAddr:
ip, addrok, netwok = ifaceAddressValue.IP, true, true
case *net.IPNet:
ip, addrok, netwok = ifaceAddressValue.IP, true, true
case *net.TCPAddr:
ip, addrok, netwok = ifaceAddressValue.IP, true, caddy.IsTCPNetwork(lnNetw)
case *net.UDPAddr:
ip, addrok, netwok = ifaceAddressValue.IP, true, caddy.IsUDPNetwork(lnNetw)
}

if addrok {
if netwok {
if caddy.IsIPv4Network(lnNetw) && len(ip) == net.IPv4len || caddy.IsIPv6Network(lnNetw) && len(ip) == net.IPv6len {
lnIfaceAddresses = append(lnIfaceAddresses, ip.String())
}
}
continue
}

var name string
switch ifaceAddressValue := ifaceAddress.(type) {
case *net.UnixAddr:
name, addrok, netwok = ifaceAddressValue.Name, true, caddy.IsUnixNetwork(lnNetw)
}

if addrok {
if netwok {
lnIfaceAddresses = append(lnIfaceAddresses, name)
}
continue
}
} else {
lnIfaceAddresses = append(lnIfaceAddresses, ifaceAddress.String())
}
}
if len(lnIfaceAddresses) == 0 {
return nil, fmt.Errorf("no available listener interface addresses for network: %v: %v", lnDevice, lnNetw)
}
for _, lnIfaceAddress := range lnIfaceAddresses {
lnAddresses = append(lnAddresses, caddy.JoinNetworkAddress(lnNetw, lnIfaceAddress, ""))
}
} else {
lnAddresses = append(lnAddresses, lnAddress)
}
}
for _, lnAddr := range lnAddresses {
lnNetw, lnHost, _, err := caddy.SplitNetworkAddress(lnAddr)
if err != nil {
return nil, fmt.Errorf("splitting listener address: %v", err)
Expand All @@ -350,11 +428,10 @@ func (st *ServerType) listenersForServerBlockAddress(sblock serverBlock, addr Ad
return listeners, nil
}

// addressesWithProtocols associates a list of listen addresses
// with a list of protocols to serve them with
type addressesWithProtocols struct {
type bindOptions struct {
addresses []string
protocols []string
toDevice bool
}

// Address represents a site address. It contains
Expand Down
17 changes: 12 additions & 5 deletions caddyconfig/httpcaddyfile/builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,16 @@ func init() {

// parseBind parses the bind directive. Syntax:
//
// bind <addresses...> [{
// protocols [h1|h2|h2c|h3] [...]
// }]
// bind <addresses...> [{
// interfaces <devices...>
// protocols [h1|h2|h2c|h3] [...]
// }]
func parseBind(h Helper) ([]ConfigValue, error) {
h.Next() // consume directive name
var addresses, protocols []string
var (
addresses, protocols []string
toDevice bool
)
addresses = h.RemainingArgs()

for h.NextBlock(0) {
Expand All @@ -72,14 +76,17 @@ func parseBind(h Helper) ([]ConfigValue, error) {
if len(protocols) == 0 {
return nil, h.Errf("protocols requires one or more arguments")
}
case "to_device":
toDevice = true
default:
return nil, h.Errf("unknown subdirective: %s", h.Val())
}
}

return []ConfigValue{{Class: "bind", Value: addressesWithProtocols{
return []ConfigValue{{Class: "bind", Value: bindOptions{
addresses: addresses,
protocols: protocols,
toDevice: toDevice,
}}}, nil
}

Expand Down
11 changes: 8 additions & 3 deletions caddyconfig/httpcaddyfile/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,10 @@ func parseOptSingleString(d *caddyfile.Dispenser, _ any) (any, error) {

func parseOptDefaultBind(d *caddyfile.Dispenser, _ any) (any, error) {
d.Next() // consume option name

var addresses, protocols []string
var (
addresses, protocols []string
toDevice bool
)
addresses = d.RemainingArgs()

if len(addresses) == 0 {
Expand All @@ -322,14 +324,17 @@ func parseOptDefaultBind(d *caddyfile.Dispenser, _ any) (any, error) {
if len(protocols) == 0 {
return nil, d.Errf("protocols requires one or more arguments")
}
case "to_device":
toDevice = true
default:
return nil, d.Errf("unknown subdirective: %s", d.Val())
}
}

return []ConfigValue{{Class: "bind", Value: addressesWithProtocols{
return []ConfigValue{{Class: "bind", Value: bindOptions{
addresses: addresses,
protocols: protocols,
toDevice: toDevice,
}}}, nil
}

Expand Down
4 changes: 2 additions & 2 deletions caddyconfig/httpcaddyfile/tlsapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ func (st ServerType) buildTLSApp(
if acmeIssuer.Challenges.BindHost == "" {
// only binding to one host is supported
var bindHost string
if asserted, ok := cfgVal.Value.(addressesWithProtocols); ok && len(asserted.addresses) > 0 {
if asserted, ok := cfgVal.Value.(bindOptions); ok && len(asserted.addresses) > 0 {
bindHost = asserted.addresses[0]
}
acmeIssuer.Challenges.BindHost = bindHost
Expand Down Expand Up @@ -613,7 +613,7 @@ func fillInGlobalACMEDefaults(issuer certmagic.Issuer, options map[string]any) e
// In Linux the same call will error with EADDRINUSE whenever the listener for the automation policy is opened
if acmeIssuer.Challenges == nil || (acmeIssuer.Challenges.DNS == nil && acmeIssuer.Challenges.BindHost == "") {
if defBinds, ok := globalDefaultBind.([]ConfigValue); ok && len(defBinds) > 0 {
if abp, ok := defBinds[0].Value.(addressesWithProtocols); ok && len(abp.addresses) > 0 {
if abp, ok := defBinds[0].Value.(bindOptions); ok && len(abp.addresses) > 0 {
if acmeIssuer.Challenges == nil {
acmeIssuer.Challenges = new(caddytls.ChallengesConfig)
}
Expand Down
4 changes: 2 additions & 2 deletions cmd/commandfuncs.go
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@ func AdminAPIRequest(adminAddr, method, uri string, headers http.Header, body io
return nil, err
}
parsedAddr.Host = addr
} else if parsedAddr.IsFdNetwork() {
} else if parsedAddr.IsFDNetwork() {
origin = "http://127.0.0.1"
}

Expand All @@ -738,7 +738,7 @@ func AdminAPIRequest(adminAddr, method, uri string, headers http.Header, body io
if err != nil {
return nil, fmt.Errorf("making request: %v", err)
}
if parsedAddr.IsUnixNetwork() || parsedAddr.IsFdNetwork() {
if parsedAddr.IsUnixNetwork() || parsedAddr.IsFDNetwork() {
// We used to conform to RFC 2616 Section 14.26 which requires
// an empty host header when there is no host, as is the case
// with unix sockets and socket fds. However, Go required a
Expand Down
7 changes: 3 additions & 4 deletions listen.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"fmt"
"net"
"os"
"slices"
"strconv"
"sync"
"sync/atomic"
Expand All @@ -37,7 +36,7 @@ func reuseUnixSocket(_, _ string) (any, error) {
func listenReusable(ctx context.Context, lnKey string, network, address string, config net.ListenConfig) (any, error) {
var socketFile *os.File

fd := slices.Contains([]string{"fd", "fdgram"}, network)
fd := IsFDNetwork(network)
if fd {
socketFd, err := strconv.ParseUint(address, 0, strconv.IntSize)
if err != nil {
Expand Down Expand Up @@ -66,8 +65,8 @@ func listenReusable(ctx context.Context, lnKey string, network, address string,
}
}

datagram := slices.Contains([]string{"udp", "udp4", "udp6", "unixgram", "fdgram"}, network)
if datagram {
packet := IsPacketNetwork(network)
if packet {
sharedPc, _, err := listenerPool.LoadOrNew(lnKey, func() (Destructor, error) {
var (
pc net.PacketConn
Expand Down
9 changes: 4 additions & 5 deletions listen_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
"io/fs"
"net"
"os"
"slices"
"strconv"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -102,7 +101,7 @@ func listenReusable(ctx context.Context, lnKey string, network, address string,
socketFile *os.File
)

fd := slices.Contains([]string{"fd", "fdgram"}, network)
fd := IsFDNetwork(network)
if fd {
socketFd, err := strconv.ParseUint(address, 0, strconv.IntSize)
if err != nil {
Expand Down Expand Up @@ -142,8 +141,8 @@ func listenReusable(ctx context.Context, lnKey string, network, address string,
}
}

datagram := slices.Contains([]string{"udp", "udp4", "udp6", "unixgram", "fdgram"}, network)
if datagram {
packet := IsPacketNetwork(network)
if packet {
if fd {
ln, err = net.FilePacketConn(socketFile)
} else {
Expand All @@ -161,7 +160,7 @@ func listenReusable(ctx context.Context, lnKey string, network, address string,
listenerPool.LoadOrStore(lnKey, nil)
}

if datagram {
if packet {
if !fd {
// TODO: Not 100% sure this is necessary, but we do this for net.UnixListener, so...
if unix, ok := ln.(*net.UnixConn); ok {
Expand Down
Loading
Loading