diff --git a/interface.go b/interface.go new file mode 100644 index 00000000..35409e7a --- /dev/null +++ b/interface.go @@ -0,0 +1,51 @@ +package caddy + +import ( + "context" + "fmt" + "net" + "strings" +) + +func init() { + RegisterNetwork("iface", getInterfaceListener) + RegisterNetwork("iface+tcp", getInterfaceListener) + RegisterNetwork("iface+udp", getInterfaceListener) +} + +func getInterfaceListener(ctx context.Context, network, addr string, config net.ListenConfig) (any, error) { + // assuming addr = "interface+family:port" + // if family is missing, then assume tcp + family := "tcp" + parts := strings.Split(network, "+") + if len(parts) == 2 { + family = parts[1] + } + host, port, _ := net.SplitHostPort(addr) + iface, err := net.InterfaceByName(host) + if err != nil { + return nil, fmt.Errorf("interface %s not found", addr) + } + addrs, err := iface.Addrs() + if err != nil { + return nil, fmt.Errorf("error on obtaining interface %s addresses: %s", iface.Name, err) + } + if len(addrs) == 0 { + return nil, fmt.Errorf("interface %s has no addresses", iface.Name) + } + for _, addr := range addrs { + if face, ok := addr.(*net.IPNet); ok { + if ip4 := face.IP.To4(); ip4 != nil { + switch family { + case "tcp": + return net.Listen(family, net.JoinHostPort(ip4.String(), port)) + case "udp": + return net.ListenPacket(family, net.JoinHostPort(ip4.String(), port)) + default: + return net.Listen(family, net.JoinHostPort(ip4.String(), port)) + } + } + } + } + return nil, fmt.Errorf("interface %s has no IPv4 addresses", iface.Name) +} diff --git a/modules/caddyhttp/server.go b/modules/caddyhttp/server.go index 6caaabcd..a1328bcf 100644 --- a/modules/caddyhttp/server.go +++ b/modules/caddyhttp/server.go @@ -40,6 +40,10 @@ import ( "github.com/caddyserver/caddy/v2/modules/caddytls" ) +func init() { + RegisterNetworkHTTP3("iface", "iface+udp") +} + // Server describes an HTTP server. type Server struct { // Socket addresses to which to bind listeners. Accepts