diff --git a/cmd/tailscale/cli/serve_v2.go b/cmd/tailscale/cli/serve_v2.go index c359fce1f..2f1c4eda8 100644 --- a/cmd/tailscale/cli/serve_v2.go +++ b/cmd/tailscale/cli/serve_v2.go @@ -440,10 +440,9 @@ const backgroundExistsMsg = "background configuration already exists, use `tails // validateConfig checks if the serve config is valid to serve the type wanted on the port. // dnsName is a FQDN or a serviceName (with `svc:` prefix). func (e *serveEnv) validateConfig(sc *ipn.ServeConfig, port uint16, wantServe serveType, dnsName string) error { - forService := ipn.IsServiceName(dnsName) var tcpHandlerForPort *ipn.TCPPortHandler - if forService { - svc := sc.Services[tailcfg.ServiceName(dnsName)] + if svcName, ok := tailcfg.AsServiceName(dnsName); ok { + svc := sc.Services[svcName] if svc == nil { return nil } @@ -521,7 +520,7 @@ func (e *serveEnv) setServe(sc *ipn.ServeConfig, st *ipnstate.Status, dnsName st // update the serve config based on if funnel is enabled // Since funnel is not supported for services, we only apply it for node's serve. - if !ipn.IsServiceName(dnsName) { + if _, ok := tailcfg.AsServiceName(dnsName); !ok { e.applyFunnel(sc, dnsName, srvPort, allowFunnel) } return nil @@ -544,14 +543,14 @@ var ( // serve config and status. func (e *serveEnv) messageForPort(sc *ipn.ServeConfig, st *ipnstate.Status, dnsName string, srvType serveType, srvPort uint16) string { var output strings.Builder - forService := ipn.IsServiceName(dnsName) + svcName, forService := tailcfg.AsServiceName(dnsName) var hp ipn.HostPort var webConfig *ipn.WebServerConfig var tcpHandler *ipn.TCPPortHandler ips := st.TailscaleIPs host := dnsName if forService { - host = tailcfg.ServiceName(dnsName).WithoutPrefix() + "." + st.CurrentTailnet.MagicDNSSuffix + host = strings.Join([]string{svcName.WithoutPrefix(), st.CurrentTailnet.MagicDNSSuffix}, ".") } hp = ipn.HostPort(net.JoinHostPort(host, strconv.Itoa(int(srvPort)))) @@ -578,7 +577,6 @@ func (e *serveEnv) messageForPort(sc *ipn.ServeConfig, st *ipnstate.Status, dnsN return "", "" } if forService { - svcName := tailcfg.ServiceName(dnsName) serviceIPMaps, err := tailcfg.UnmarshalNodeCapJSON[tailcfg.ServiceIPMappings](st.Self.CapMap, tailcfg.NodeAttrServiceHost) if err != nil || len(serviceIPMaps) == 0 || serviceIPMaps[0][svcName] == nil { // The capmap does not contain IPs for this service yet. Usually this means @@ -924,12 +922,12 @@ func (e *serveEnv) removeWebServe(sc *ipn.ServeConfig, st *ipnstate.Status, dnsN if sc == nil { return nil } - forService := ipn.IsServiceName(dnsName) + portStr := strconv.Itoa(int(srvPort)) hostName := dnsName webServeMap := sc.Web + svcName, forService := tailcfg.AsServiceName(dnsName) if forService { - svcName := tailcfg.ServiceName(dnsName) svc := sc.Services[svcName] if svc == nil { return errors.New("service does not exist") diff --git a/ipn/serve.go b/ipn/serve.go index 595463a37..5f42dd8e1 100644 --- a/ipn/serve.go +++ b/ipn/serve.go @@ -177,8 +177,8 @@ func (sc *ServeConfig) GetWebHandler(dnsName string, hp HostPort, mount string) if sc == nil { return nil } - if IsServiceName(dnsName) { - if svc, ok := sc.Services[tailcfg.ServiceName(dnsName)]; ok && svc.Web != nil { + if svcName, ok := tailcfg.AsServiceName(dnsName); ok { + if svc, ok := sc.Services[svcName]; ok && svc.Web != nil { if webCfg, ok := svc.Web[hp]; ok { return webCfg.Handlers[mount] } @@ -197,8 +197,8 @@ func (sc *ServeConfig) GetTCPPortHandler(port uint16, dnsName string) *TCPPortHa if sc == nil { return nil } - if IsServiceName(dnsName) { - if svc, ok := sc.Services[tailcfg.ServiceName(dnsName)]; ok && svc != nil { + if svcName, ok := tailcfg.AsServiceName(dnsName); ok { + if svc, ok := sc.Services[svcName]; ok && svc != nil { return svc.TCP[port] } return nil @@ -244,18 +244,6 @@ func (sc *ServeConfig) IsTCPForwardingAny() bool { return false } -// IsServiceName reports whether if the given string is a valid service name. -func IsServiceName(s string) bool { - return tailcfg.ServiceName(s).Validate() == nil -} - -// asServiceName reports whether if the given string is a valid service name, -// and if so returns the name as a [tailcfg.ServiceName]. -func asServiceName(s string) (svcName tailcfg.ServiceName, ok bool) { - svcName = tailcfg.ServiceName(s) - return svcName, svcName.Validate() == nil -} - // IsTCPForwardingOnPort reports whether ServeConfig is currently forwarding // in TCPForward mode on the given port for a DNSName. DNSName will be either node's DNSName, or a // serviceName for service hosted on node. This is exclusive of Web/HTTPS serving. @@ -263,9 +251,9 @@ func (sc *ServeConfig) IsTCPForwardingOnPort(port uint16, dnsName string) bool { if sc == nil { return false } - forService := IsServiceName(dnsName) - if forService { - svc, ok := sc.Services[tailcfg.ServiceName(dnsName)] + + if svcName, ok := tailcfg.AsServiceName(dnsName); ok { + svc, ok := sc.Services[svcName] if !ok || svc == nil { return false } @@ -293,7 +281,7 @@ func (sc *ServeConfig) IsServingHTTPS(port uint16, dnsName string) bool { return false } var tcpHandlers map[uint16]*TCPPortHandler - if svcName, ok := asServiceName(dnsName); ok { + if svcName, ok := tailcfg.AsServiceName(dnsName); ok { if svc := sc.Services[svcName]; svc != nil { tcpHandlers = svc.TCP } @@ -315,8 +303,8 @@ func (sc *ServeConfig) IsServingHTTP(port uint16, dnsName string) bool { if sc == nil { return false } - if IsServiceName(dnsName) { - if svc, ok := sc.Services[tailcfg.ServiceName(dnsName)]; ok && svc != nil { + if svcName, ok := tailcfg.AsServiceName(dnsName); ok { + if svc, ok := sc.Services[svcName]; ok && svc != nil { if svc.TCP[port] != nil { return svc.TCP[port].HTTP } @@ -359,8 +347,7 @@ func (sc *ServeConfig) SetWebHandler(st *ipnstate.Status, handler *HTTPHandler, tcpMap := &sc.TCP webServerMap := &sc.Web hostName := host - if IsServiceName(host) { - svcName := tailcfg.ServiceName(host) + if svcName, ok := tailcfg.AsServiceName(host); ok { hostName = svcName.WithoutPrefix() + "." + st.CurrentTailnet.MagicDNSSuffix if _, ok := sc.Services[svcName]; !ok { mak.Set(&sc.Services, svcName, new(ServiceConfig)) @@ -403,8 +390,7 @@ func (sc *ServeConfig) SetTCPForwarding(port uint16, fwdAddr string, terminateTL sc = new(ServeConfig) } tcpPortHandler := &sc.TCP - if IsServiceName(host) { - svcName := tailcfg.ServiceName(host) + if svcName, ok := tailcfg.AsServiceName(host); ok { svcConfig, ok := sc.Services[svcName] if !ok { svcConfig = new(ServiceConfig) @@ -499,8 +485,8 @@ func (sc *ServeConfig) RemoveServiceWebHandler(st *ipnstate.Status, svcName tail // RemoveTCPForwarding deletes the TCP forwarding configuration for the given // port from the serve config. func (sc *ServeConfig) RemoveTCPForwarding(dnsName string, port uint16) { - if IsServiceName(dnsName) { - if svc, ok := sc.Services[tailcfg.ServiceName(dnsName)]; ok && svc != nil { + if svcName, ok := tailcfg.AsServiceName(dnsName); ok { + if svc := sc.Services[svcName]; svc != nil { delete(svc.TCP, port) if len(svc.TCP) == 0 { svc.TCP = nil diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 3c262960a..9b952f20d 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -915,6 +915,13 @@ type TPMInfo struct { // This is not related to the older [Service] used in [Hostinfo.Services]. type ServiceName string +// AsServiceName reports whether if the given string is a valid service name, +// and if so returns the name as a [tailcfg.ServiceName]. +func AsServiceName(s string) (svcName ServiceName, ok bool) { + svcName = ServiceName(s) + return svcName, svcName.Validate() == nil +} + // Validate validates if the service name is formatted correctly. // We only allow valid DNS labels, since the expectation is that these will be // used as parts of domain names. All errors are [vizerror.Error].