diff --git a/client/anonymize/anonymize.go b/client/anonymize/anonymize.go index 89552724a..9a6d97207 100644 --- a/client/anonymize/anonymize.go +++ b/client/anonymize/anonymize.go @@ -21,8 +21,6 @@ type Anonymizer struct { currentAnonIPv6 netip.Addr startAnonIPv4 netip.Addr startAnonIPv6 netip.Addr - - domainKeyRegex *regexp.Regexp } func DefaultAddresses() (netip.Addr, netip.Addr) { @@ -38,8 +36,6 @@ func NewAnonymizer(startIPv4, startIPv6 netip.Addr) *Anonymizer { currentAnonIPv6: startIPv6, startAnonIPv4: startIPv4, startAnonIPv6: startIPv6, - - domainKeyRegex: regexp.MustCompile(`\bdomain=([^\s,:"]+)`), } } @@ -175,15 +171,20 @@ func (a *Anonymizer) AnonymizeSchemeURI(text string) string { return re.ReplaceAllStringFunc(text, a.AnonymizeURI) } +// AnonymizeDNSLogLine anonymizes domain names in DNS log entries by replacing them with a random string. func (a *Anonymizer) AnonymizeDNSLogLine(logEntry string) string { - return a.domainKeyRegex.ReplaceAllStringFunc(logEntry, func(match string) string { - parts := strings.SplitN(match, "=", 2) + domainPattern := `dns\.Question{Name:"([^"]+)",` + domainRegex := regexp.MustCompile(domainPattern) + + return domainRegex.ReplaceAllStringFunc(logEntry, func(match string) string { + parts := strings.Split(match, `"`) if len(parts) >= 2 { domain := parts[1] if strings.HasSuffix(domain, anonTLD) { return match } - return "domain=" + a.AnonymizeDomain(domain) + randomDomain := generateRandomString(10) + anonTLD + return strings.Replace(match, domain, randomDomain, 1) } return match }) diff --git a/client/anonymize/anonymize_test.go b/client/anonymize/anonymize_test.go index ff2e48869..a3aae1ee9 100644 --- a/client/anonymize/anonymize_test.go +++ b/client/anonymize/anonymize_test.go @@ -46,59 +46,11 @@ func TestAnonymizeIP(t *testing.T) { func TestAnonymizeDNSLogLine(t *testing.T) { anonymizer := anonymize.NewAnonymizer(netip.Addr{}, netip.Addr{}) - tests := []struct { - name string - input string - original string - expect string - }{ - { - name: "Basic domain with trailing content", - input: "received DNS request for DNS forwarder: domain=example.com: something happened with code=123", - original: "example.com", - expect: `received DNS request for DNS forwarder: domain=anon-[a-zA-Z0-9]+\.domain: something happened with code=123`, - }, - { - name: "Domain with trailing dot", - input: "domain=example.com. processing request with status=pending", - original: "example.com", - expect: `domain=anon-[a-zA-Z0-9]+\.domain\. processing request with status=pending`, - }, - { - name: "Multiple domains in log", - input: "forward domain=first.com status=ok, redirect to domain=second.com port=443", - original: "first.com", // testing just one is sufficient as AnonymizeDomain is tested separately - expect: `forward domain=anon-[a-zA-Z0-9]+\.domain status=ok, redirect to domain=anon-[a-zA-Z0-9]+\.domain port=443`, - }, - { - name: "Already anonymized domain", - input: "got request domain=anon-xyz123.domain from=client1 to=server2", - original: "", // nothing should be anonymized - expect: `got request domain=anon-xyz123\.domain from=client1 to=server2`, - }, - { - name: "Subdomain with trailing dot", - input: "domain=sub.example.com. next_hop=10.0.0.1 proto=udp", - original: "example.com", - expect: `domain=sub\.anon-[a-zA-Z0-9]+\.domain\. next_hop=10\.0\.0\.1 proto=udp`, - }, - { - name: "Handler chain pattern log", - input: "pattern: domain=example.com. original: domain=*.example.com. wildcard=true priority=100", - original: "example.com", - expect: `pattern: domain=anon-[a-zA-Z0-9]+\.domain\. original: domain=\*\.anon-[a-zA-Z0-9]+\.domain\. wildcard=true priority=100`, - }, - } + testLog := `2024-04-23T20:01:11+02:00 TRAC client/internal/dns/local.go:25: received question: dns.Question{Name:"example.com", Qtype:0x1c, Qclass:0x1}` - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - result := anonymizer.AnonymizeDNSLogLine(tc.input) - if tc.original != "" { - assert.NotContains(t, result, tc.original) - } - assert.Regexp(t, tc.expect, result) - }) - } + result := anonymizer.AnonymizeDNSLogLine(testLog) + require.NotEqual(t, testLog, result) + assert.NotContains(t, result, "example.com") } func TestAnonymizeDomain(t *testing.T) { diff --git a/client/cmd/networks.go b/client/cmd/networks.go deleted file mode 100644 index 7b9724bc5..000000000 --- a/client/cmd/networks.go +++ /dev/null @@ -1,173 +0,0 @@ -package cmd - -import ( - "fmt" - "strings" - - "github.com/spf13/cobra" - "google.golang.org/grpc/status" - - "github.com/netbirdio/netbird/client/proto" -) - -var appendFlag bool - -var networksCMD = &cobra.Command{ - Use: "networks", - Aliases: []string{"routes"}, - Short: "Manage networks", - Long: `Commands to list, select, or deselect networks. Replaces the "routes" command.`, -} - -var routesListCmd = &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Short: "List networks", - Example: " netbird networks list", - Long: "List all available network routes.", - RunE: networksList, -} - -var routesSelectCmd = &cobra.Command{ - Use: "select network...|all", - Short: "Select network", - Long: "Select a list of networks by identifiers or 'all' to clear all selections and to accept all (including new) networks.\nDefault mode is replace, use -a to append to already selected networks.", - Example: " netbird networks select all\n netbird networks select route1 route2\n netbird routes select -a route3", - Args: cobra.MinimumNArgs(1), - RunE: networksSelect, -} - -var routesDeselectCmd = &cobra.Command{ - Use: "deselect network...|all", - Short: "Deselect networks", - Long: "Deselect previously selected networks by identifiers or 'all' to disable accepting any networks.", - Example: " netbird networks deselect all\n netbird networks deselect route1 route2", - Args: cobra.MinimumNArgs(1), - RunE: networksDeselect, -} - -func init() { - routesSelectCmd.PersistentFlags().BoolVarP(&appendFlag, "append", "a", false, "Append to current network selection instead of replacing") -} - -func networksList(cmd *cobra.Command, _ []string) error { - conn, err := getClient(cmd) - if err != nil { - return err - } - defer conn.Close() - - client := proto.NewDaemonServiceClient(conn) - resp, err := client.ListNetworks(cmd.Context(), &proto.ListNetworksRequest{}) - if err != nil { - return fmt.Errorf("failed to list network: %v", status.Convert(err).Message()) - } - - if len(resp.Routes) == 0 { - cmd.Println("No networks available.") - return nil - } - - printNetworks(cmd, resp) - - return nil -} - -func printNetworks(cmd *cobra.Command, resp *proto.ListNetworksResponse) { - cmd.Println("Available Networks:") - for _, route := range resp.Routes { - printNetwork(cmd, route) - } -} - -func printNetwork(cmd *cobra.Command, route *proto.Network) { - selectedStatus := getSelectedStatus(route) - domains := route.GetDomains() - - if len(domains) > 0 { - printDomainRoute(cmd, route, domains, selectedStatus) - } else { - printNetworkRoute(cmd, route, selectedStatus) - } -} - -func getSelectedStatus(route *proto.Network) string { - if route.GetSelected() { - return "Selected" - } - return "Not Selected" -} - -func printDomainRoute(cmd *cobra.Command, route *proto.Network, domains []string, selectedStatus string) { - cmd.Printf("\n - ID: %s\n Domains: %s\n Status: %s\n", route.GetID(), strings.Join(domains, ", "), selectedStatus) - resolvedIPs := route.GetResolvedIPs() - - if len(resolvedIPs) > 0 { - printResolvedIPs(cmd, domains, resolvedIPs) - } else { - cmd.Printf(" Resolved IPs: -\n") - } -} - -func printNetworkRoute(cmd *cobra.Command, route *proto.Network, selectedStatus string) { - cmd.Printf("\n - ID: %s\n Network: %s\n Status: %s\n", route.GetID(), route.GetRange(), selectedStatus) -} - -func printResolvedIPs(cmd *cobra.Command, _ []string, resolvedIPs map[string]*proto.IPList) { - cmd.Printf(" Resolved IPs:\n") - for resolvedDomain, ipList := range resolvedIPs { - cmd.Printf(" [%s]: %s\n", resolvedDomain, strings.Join(ipList.GetIps(), ", ")) - } -} - -func networksSelect(cmd *cobra.Command, args []string) error { - conn, err := getClient(cmd) - if err != nil { - return err - } - defer conn.Close() - - client := proto.NewDaemonServiceClient(conn) - req := &proto.SelectNetworksRequest{ - NetworkIDs: args, - } - - if len(args) == 1 && args[0] == "all" { - req.All = true - } else if appendFlag { - req.Append = true - } - - if _, err := client.SelectNetworks(cmd.Context(), req); err != nil { - return fmt.Errorf("failed to select networks: %v", status.Convert(err).Message()) - } - - cmd.Println("Networks selected successfully.") - - return nil -} - -func networksDeselect(cmd *cobra.Command, args []string) error { - conn, err := getClient(cmd) - if err != nil { - return err - } - defer conn.Close() - - client := proto.NewDaemonServiceClient(conn) - req := &proto.SelectNetworksRequest{ - NetworkIDs: args, - } - - if len(args) == 1 && args[0] == "all" { - req.All = true - } - - if _, err := client.DeselectNetworks(cmd.Context(), req); err != nil { - return fmt.Errorf("failed to deselect networks: %v", status.Convert(err).Message()) - } - - cmd.Println("Networks deselected successfully.") - - return nil -} diff --git a/client/cmd/root.go b/client/cmd/root.go index 0305bacc8..3f2d04ef3 100644 --- a/client/cmd/root.go +++ b/client/cmd/root.go @@ -142,14 +142,14 @@ func init() { rootCmd.AddCommand(loginCmd) rootCmd.AddCommand(versionCmd) rootCmd.AddCommand(sshCmd) - rootCmd.AddCommand(networksCMD) + rootCmd.AddCommand(routesCmd) rootCmd.AddCommand(debugCmd) serviceCmd.AddCommand(runCmd, startCmd, stopCmd, restartCmd) // service control commands are subcommands of service serviceCmd.AddCommand(installCmd, uninstallCmd) // service installer commands are subcommands of service - networksCMD.AddCommand(routesListCmd) - networksCMD.AddCommand(routesSelectCmd, routesDeselectCmd) + routesCmd.AddCommand(routesListCmd) + routesCmd.AddCommand(routesSelectCmd, routesDeselectCmd) debugCmd.AddCommand(debugBundleCmd) debugCmd.AddCommand(logCmd) diff --git a/client/cmd/status.go b/client/cmd/status.go index fa4bff77b..6db52a677 100644 --- a/client/cmd/status.go +++ b/client/cmd/status.go @@ -40,7 +40,6 @@ type peerStateDetailOutput struct { Latency time.Duration `json:"latency" yaml:"latency"` RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"` Routes []string `json:"routes" yaml:"routes"` - Networks []string `json:"networks" yaml:"networks"` } type peersStateOutput struct { @@ -99,7 +98,6 @@ type statusOutputOverview struct { RosenpassEnabled bool `json:"quantumResistance" yaml:"quantumResistance"` RosenpassPermissive bool `json:"quantumResistancePermissive" yaml:"quantumResistancePermissive"` Routes []string `json:"routes" yaml:"routes"` - Networks []string `json:"networks" yaml:"networks"` NSServerGroups []nsServerGroupStateOutput `json:"dnsServers" yaml:"dnsServers"` } @@ -284,8 +282,7 @@ func convertToStatusOutputOverview(resp *proto.StatusResponse) statusOutputOverv FQDN: pbFullStatus.GetLocalPeerState().GetFqdn(), RosenpassEnabled: pbFullStatus.GetLocalPeerState().GetRosenpassEnabled(), RosenpassPermissive: pbFullStatus.GetLocalPeerState().GetRosenpassPermissive(), - Routes: pbFullStatus.GetLocalPeerState().GetNetworks(), - Networks: pbFullStatus.GetLocalPeerState().GetNetworks(), + Routes: pbFullStatus.GetLocalPeerState().GetRoutes(), NSServerGroups: mapNSGroups(pbFullStatus.GetDnsServers()), } @@ -393,8 +390,7 @@ func mapPeers(peers []*proto.PeerState) peersStateOutput { TransferSent: transferSent, Latency: pbPeerState.GetLatency().AsDuration(), RosenpassEnabled: pbPeerState.GetRosenpassEnabled(), - Routes: pbPeerState.GetNetworks(), - Networks: pbPeerState.GetNetworks(), + Routes: pbPeerState.GetRoutes(), } peersStateDetail = append(peersStateDetail, peerState) @@ -495,10 +491,10 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays relaysString = fmt.Sprintf("%d/%d Available", overview.Relays.Available, overview.Relays.Total) } - networks := "-" - if len(overview.Networks) > 0 { - sort.Strings(overview.Networks) - networks = strings.Join(overview.Networks, ", ") + routes := "-" + if len(overview.Routes) > 0 { + sort.Strings(overview.Routes) + routes = strings.Join(overview.Routes, ", ") } var dnsServersString string @@ -560,7 +556,6 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays "Interface type: %s\n"+ "Quantum resistance: %s\n"+ "Routes: %s\n"+ - "Networks: %s\n"+ "Peers count: %s\n", fmt.Sprintf("%s/%s%s", goos, goarch, goarm), overview.DaemonVersion, @@ -573,8 +568,7 @@ func parseGeneralSummary(overview statusOutputOverview, showURL bool, showRelays interfaceIP, interfaceTypeString, rosenpassEnabledStatus, - networks, - networks, + routes, peersCountString, ) return summary @@ -637,10 +631,10 @@ func parsePeers(peers peersStateOutput, rosenpassEnabled, rosenpassPermissive bo } } - networks := "-" - if len(peerState.Networks) > 0 { - sort.Strings(peerState.Networks) - networks = strings.Join(peerState.Networks, ", ") + routes := "-" + if len(peerState.Routes) > 0 { + sort.Strings(peerState.Routes) + routes = strings.Join(peerState.Routes, ", ") } peerString := fmt.Sprintf( @@ -658,7 +652,6 @@ func parsePeers(peers peersStateOutput, rosenpassEnabled, rosenpassPermissive bo " Transfer status (received/sent) %s/%s\n"+ " Quantum resistance: %s\n"+ " Routes: %s\n"+ - " Networks: %s\n"+ " Latency: %s\n", peerState.FQDN, peerState.IP, @@ -675,8 +668,7 @@ func parsePeers(peers peersStateOutput, rosenpassEnabled, rosenpassPermissive bo toIEC(peerState.TransferReceived), toIEC(peerState.TransferSent), rosenpassEnabledStatus, - networks, - networks, + routes, peerState.Latency.String(), ) @@ -818,14 +810,6 @@ func anonymizePeerDetail(a *anonymize.Anonymizer, peer *peerStateDetailOutput) { peer.RelayAddress = a.AnonymizeURI(peer.RelayAddress) - for i, route := range peer.Networks { - peer.Networks[i] = a.AnonymizeIPString(route) - } - - for i, route := range peer.Networks { - peer.Networks[i] = a.AnonymizeRoute(route) - } - for i, route := range peer.Routes { peer.Routes[i] = a.AnonymizeIPString(route) } @@ -866,10 +850,6 @@ func anonymizeOverview(a *anonymize.Anonymizer, overview *statusOutputOverview) } } - for i, route := range overview.Networks { - overview.Networks[i] = a.AnonymizeRoute(route) - } - for i, route := range overview.Routes { overview.Routes[i] = a.AnonymizeRoute(route) } diff --git a/client/cmd/status_test.go b/client/cmd/status_test.go index 1f1e95726..ca43df8a5 100644 --- a/client/cmd/status_test.go +++ b/client/cmd/status_test.go @@ -44,7 +44,7 @@ var resp = &proto.StatusResponse{ LastWireguardHandshake: timestamppb.New(time.Date(2001, time.Month(1), 1, 1, 1, 2, 0, time.UTC)), BytesRx: 200, BytesTx: 100, - Networks: []string{ + Routes: []string{ "10.1.0.0/24", }, Latency: durationpb.New(time.Duration(10000000)), @@ -93,7 +93,7 @@ var resp = &proto.StatusResponse{ PubKey: "Some-Pub-Key", KernelInterface: true, Fqdn: "some-localhost.awesome-domain.com", - Networks: []string{ + Routes: []string{ "10.10.0.0/24", }, }, @@ -149,9 +149,6 @@ var overview = statusOutputOverview{ Routes: []string{ "10.1.0.0/24", }, - Networks: []string{ - "10.1.0.0/24", - }, Latency: time.Duration(10000000), }, { @@ -233,9 +230,6 @@ var overview = statusOutputOverview{ Routes: []string{ "10.10.0.0/24", }, - Networks: []string{ - "10.10.0.0/24", - }, } func TestConversionFromFullStatusToOutputOverview(t *testing.T) { @@ -301,9 +295,6 @@ func TestParsingToJSON(t *testing.T) { "quantumResistance": false, "routes": [ "10.1.0.0/24" - ], - "networks": [ - "10.1.0.0/24" ] }, { @@ -327,8 +318,7 @@ func TestParsingToJSON(t *testing.T) { "transferSent": 1000, "latency": 10000000, "quantumResistance": false, - "routes": null, - "networks": null + "routes": null } ] }, @@ -369,9 +359,6 @@ func TestParsingToJSON(t *testing.T) { "routes": [ "10.10.0.0/24" ], - "networks": [ - "10.10.0.0/24" - ], "dnsServers": [ { "servers": [ @@ -431,8 +418,6 @@ func TestParsingToYAML(t *testing.T) { quantumResistance: false routes: - 10.1.0.0/24 - networks: - - 10.1.0.0/24 - fqdn: peer-2.awesome-domain.com netbirdIp: 192.168.178.102 publicKey: Pubkey2 @@ -452,7 +437,6 @@ func TestParsingToYAML(t *testing.T) { latency: 10ms quantumResistance: false routes: [] - networks: [] cliVersion: development daemonVersion: 0.14.1 management: @@ -481,8 +465,6 @@ quantumResistance: false quantumResistancePermissive: false routes: - 10.10.0.0/24 -networks: - - 10.10.0.0/24 dnsServers: - servers: - 8.8.8.8:53 @@ -527,7 +509,6 @@ func TestParsingToDetail(t *testing.T) { Transfer status (received/sent) 200 B/100 B Quantum resistance: false Routes: 10.1.0.0/24 - Networks: 10.1.0.0/24 Latency: 10ms peer-2.awesome-domain.com: @@ -544,7 +525,6 @@ func TestParsingToDetail(t *testing.T) { Transfer status (received/sent) 2.0 KiB/1000 B Quantum resistance: false Routes: - - Networks: - Latency: 10ms OS: %s/%s @@ -563,7 +543,6 @@ NetBird IP: 192.168.178.100/16 Interface type: Kernel Quantum resistance: false Routes: 10.10.0.0/24 -Networks: 10.10.0.0/24 Peers count: 2/2 Connected `, lastConnectionUpdate1, lastHandshake1, lastConnectionUpdate2, lastHandshake2, runtime.GOOS, runtime.GOARCH, overview.CliVersion) @@ -585,7 +564,6 @@ NetBird IP: 192.168.178.100/16 Interface type: Kernel Quantum resistance: false Routes: 10.10.0.0/24 -Networks: 10.10.0.0/24 Peers count: 2/2 Connected ` diff --git a/client/cmd/testutil_test.go b/client/cmd/testutil_test.go index e3e644357..d998f9ea9 100644 --- a/client/cmd/testutil_test.go +++ b/client/cmd/testutil_test.go @@ -10,8 +10,6 @@ import ( "go.opentelemetry.io/otel" "github.com/netbirdio/netbird/management/server/activity" - "github.com/netbirdio/netbird/management/server/settings" - "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/telemetry" "github.com/netbirdio/netbird/util" @@ -73,7 +71,7 @@ func startManagement(t *testing.T, config *mgmt.Config, testFile string) (*grpc. t.Fatal(err) } s := grpc.NewServer() - store, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), testFile, t.TempDir()) + store, cleanUp, err := mgmt.NewTestStoreFromSQL(context.Background(), testFile, t.TempDir()) if err != nil { t.Fatal(err) } @@ -95,7 +93,7 @@ func startManagement(t *testing.T, config *mgmt.Config, testFile string) (*grpc. } secretsManager := mgmt.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay) - mgmtServer, err := mgmt.NewServer(context.Background(), config, accountManager, settings.NewManager(store), peersUpdateManager, secretsManager, nil, nil) + mgmtServer, err := mgmt.NewServer(context.Background(), config, accountManager, peersUpdateManager, secretsManager, nil, nil) if err != nil { t.Fatal(err) } diff --git a/client/internal/dns/handler_chain.go b/client/internal/dns/handler_chain.go deleted file mode 100644 index c6ac3ebd6..000000000 --- a/client/internal/dns/handler_chain.go +++ /dev/null @@ -1,222 +0,0 @@ -package dns - -import ( - "strings" - "sync" - - "github.com/miekg/dns" - log "github.com/sirupsen/logrus" -) - -const ( - PriorityDNSRoute = 100 - PriorityMatchDomain = 50 - PriorityDefault = 0 -) - -type SubdomainMatcher interface { - dns.Handler - MatchSubdomains() bool -} - -type HandlerEntry struct { - Handler dns.Handler - Priority int - Pattern string - OrigPattern string - IsWildcard bool - StopHandler handlerWithStop - MatchSubdomains bool -} - -// HandlerChain represents a prioritized chain of DNS handlers -type HandlerChain struct { - mu sync.RWMutex - handlers []HandlerEntry -} - -// ResponseWriterChain wraps a dns.ResponseWriter to track if handler wants to continue chain -type ResponseWriterChain struct { - dns.ResponseWriter - origPattern string - shouldContinue bool -} - -func (w *ResponseWriterChain) WriteMsg(m *dns.Msg) error { - // Check if this is a continue signal (NXDOMAIN with Zero bit set) - if m.Rcode == dns.RcodeNameError && m.MsgHdr.Zero { - w.shouldContinue = true - return nil - } - return w.ResponseWriter.WriteMsg(m) -} - -func NewHandlerChain() *HandlerChain { - return &HandlerChain{ - handlers: make([]HandlerEntry, 0), - } -} - -// GetOrigPattern returns the original pattern of the handler that wrote the response -func (w *ResponseWriterChain) GetOrigPattern() string { - return w.origPattern -} - -// AddHandler adds a new handler to the chain, replacing any existing handler with the same pattern and priority -func (c *HandlerChain) AddHandler(pattern string, handler dns.Handler, priority int, stopHandler handlerWithStop) { - c.mu.Lock() - defer c.mu.Unlock() - - origPattern := pattern - isWildcard := strings.HasPrefix(pattern, "*.") - if isWildcard { - pattern = pattern[2:] - } - pattern = dns.Fqdn(pattern) - origPattern = dns.Fqdn(origPattern) - - // First remove any existing handler with same original pattern and priority - for i := len(c.handlers) - 1; i >= 0; i-- { - if c.handlers[i].OrigPattern == origPattern && c.handlers[i].Priority == priority { - if c.handlers[i].StopHandler != nil { - c.handlers[i].StopHandler.stop() - } - c.handlers = append(c.handlers[:i], c.handlers[i+1:]...) - break - } - } - - // Check if handler implements SubdomainMatcher interface - matchSubdomains := false - if matcher, ok := handler.(SubdomainMatcher); ok { - matchSubdomains = matcher.MatchSubdomains() - } - - log.Debugf("adding handler pattern: domain=%s original: domain=%s wildcard=%v match_subdomain=%v priority=%d", - pattern, origPattern, isWildcard, matchSubdomains, priority) - - entry := HandlerEntry{ - Handler: handler, - Priority: priority, - Pattern: pattern, - OrigPattern: origPattern, - IsWildcard: isWildcard, - StopHandler: stopHandler, - MatchSubdomains: matchSubdomains, - } - - // Insert handler in priority order - pos := 0 - for i, h := range c.handlers { - if h.Priority < priority { - pos = i - break - } - pos = i + 1 - } - - c.handlers = append(c.handlers[:pos], append([]HandlerEntry{entry}, c.handlers[pos:]...)...) -} - -// RemoveHandler removes a handler for the given pattern and priority -func (c *HandlerChain) RemoveHandler(pattern string, priority int) { - c.mu.Lock() - defer c.mu.Unlock() - - pattern = dns.Fqdn(pattern) - - // Find and remove handlers matching both original pattern and priority - for i := len(c.handlers) - 1; i >= 0; i-- { - entry := c.handlers[i] - if entry.OrigPattern == pattern && entry.Priority == priority { - if entry.StopHandler != nil { - entry.StopHandler.stop() - } - c.handlers = append(c.handlers[:i], c.handlers[i+1:]...) - return - } - } -} - -// HasHandlers returns true if there are any handlers remaining for the given pattern -func (c *HandlerChain) HasHandlers(pattern string) bool { - c.mu.RLock() - defer c.mu.RUnlock() - - pattern = dns.Fqdn(pattern) - for _, entry := range c.handlers { - if entry.Pattern == pattern { - return true - } - } - return false -} - -func (c *HandlerChain) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { - if len(r.Question) == 0 { - return - } - - qname := r.Question[0].Name - log.Tracef("handling DNS request for domain=%s", qname) - - c.mu.RLock() - defer c.mu.RUnlock() - - log.Tracef("current handlers (%d):", len(c.handlers)) - for _, h := range c.handlers { - log.Tracef(" - pattern: domain=%s original: domain=%s wildcard=%v priority=%d", - h.Pattern, h.OrigPattern, h.IsWildcard, h.Priority) - } - - // Try handlers in priority order - for _, entry := range c.handlers { - var matched bool - switch { - case entry.Pattern == ".": - matched = true - case entry.IsWildcard: - parts := strings.Split(strings.TrimSuffix(qname, entry.Pattern), ".") - matched = len(parts) >= 2 && strings.HasSuffix(qname, entry.Pattern) - default: - // For non-wildcard patterns: - // If handler wants subdomain matching, allow suffix match - // Otherwise require exact match - if entry.MatchSubdomains { - matched = qname == entry.Pattern || strings.HasSuffix(qname, "."+entry.Pattern) - } else { - matched = qname == entry.Pattern - } - } - - if !matched { - log.Tracef("trying domain match: request: domain=%s pattern: domain=%s wildcard=%v match_subdomain=%v matched=false", - qname, entry.OrigPattern, entry.MatchSubdomains, entry.IsWildcard) - continue - } - - log.Tracef("handler matched: request: domain=%s pattern: domain=%s wildcard=%v match_subdomain=%v", - qname, entry.OrigPattern, entry.IsWildcard, entry.MatchSubdomains) - - chainWriter := &ResponseWriterChain{ - ResponseWriter: w, - origPattern: entry.OrigPattern, - } - entry.Handler.ServeDNS(chainWriter, r) - - // If handler wants to continue, try next handler - if chainWriter.shouldContinue { - log.Tracef("handler requested continue to next handler") - continue - } - return - } - - // No handler matched or all handlers passed - log.Tracef("no handler found for domain=%s", qname) - resp := &dns.Msg{} - resp.SetRcode(r, dns.RcodeNameError) - if err := w.WriteMsg(resp); err != nil { - log.Errorf("failed to write DNS response: %v", err) - } -} diff --git a/client/internal/dns/handler_chain_test.go b/client/internal/dns/handler_chain_test.go deleted file mode 100644 index 727b6e908..000000000 --- a/client/internal/dns/handler_chain_test.go +++ /dev/null @@ -1,511 +0,0 @@ -package dns_test - -import ( - "net" - "testing" - - "github.com/miekg/dns" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - - nbdns "github.com/netbirdio/netbird/client/internal/dns" -) - -// TestHandlerChain_ServeDNS_Priorities tests that handlers are executed in priority order -func TestHandlerChain_ServeDNS_Priorities(t *testing.T) { - chain := nbdns.NewHandlerChain() - - // Create mock handlers for different priorities - defaultHandler := &nbdns.MockHandler{} - matchDomainHandler := &nbdns.MockHandler{} - dnsRouteHandler := &nbdns.MockHandler{} - - // Setup handlers with different priorities - chain.AddHandler("example.com.", defaultHandler, nbdns.PriorityDefault, nil) - chain.AddHandler("example.com.", matchDomainHandler, nbdns.PriorityMatchDomain, nil) - chain.AddHandler("example.com.", dnsRouteHandler, nbdns.PriorityDNSRoute, nil) - - // Create test request - r := new(dns.Msg) - r.SetQuestion("example.com.", dns.TypeA) - - // Create test writer - w := &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}} - - // Setup expectations - only highest priority handler should be called - dnsRouteHandler.On("ServeDNS", mock.Anything, r).Once() - matchDomainHandler.On("ServeDNS", mock.Anything, r).Maybe() - defaultHandler.On("ServeDNS", mock.Anything, r).Maybe() - - // Execute - chain.ServeDNS(w, r) - - // Verify all expectations were met - dnsRouteHandler.AssertExpectations(t) - matchDomainHandler.AssertExpectations(t) - defaultHandler.AssertExpectations(t) -} - -// TestHandlerChain_ServeDNS_DomainMatching tests various domain matching scenarios -func TestHandlerChain_ServeDNS_DomainMatching(t *testing.T) { - tests := []struct { - name string - handlerDomain string - queryDomain string - isWildcard bool - matchSubdomains bool - shouldMatch bool - }{ - { - name: "exact match", - handlerDomain: "example.com.", - queryDomain: "example.com.", - isWildcard: false, - matchSubdomains: false, - shouldMatch: true, - }, - { - name: "subdomain with non-wildcard and MatchSubdomains true", - handlerDomain: "example.com.", - queryDomain: "sub.example.com.", - isWildcard: false, - matchSubdomains: true, - shouldMatch: true, - }, - { - name: "subdomain with non-wildcard and MatchSubdomains false", - handlerDomain: "example.com.", - queryDomain: "sub.example.com.", - isWildcard: false, - matchSubdomains: false, - shouldMatch: false, - }, - { - name: "wildcard match", - handlerDomain: "*.example.com.", - queryDomain: "sub.example.com.", - isWildcard: true, - matchSubdomains: false, - shouldMatch: true, - }, - { - name: "wildcard no match on apex", - handlerDomain: "*.example.com.", - queryDomain: "example.com.", - isWildcard: true, - matchSubdomains: false, - shouldMatch: false, - }, - { - name: "root zone match", - handlerDomain: ".", - queryDomain: "anything.com.", - isWildcard: false, - matchSubdomains: false, - shouldMatch: true, - }, - { - name: "no match different domain", - handlerDomain: "example.com.", - queryDomain: "example.org.", - isWildcard: false, - matchSubdomains: false, - shouldMatch: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - chain := nbdns.NewHandlerChain() - var handler dns.Handler - - if tt.matchSubdomains { - mockSubHandler := &nbdns.MockSubdomainHandler{Subdomains: true} - handler = mockSubHandler - if tt.shouldMatch { - mockSubHandler.On("ServeDNS", mock.Anything, mock.Anything).Once() - } - } else { - mockHandler := &nbdns.MockHandler{} - handler = mockHandler - if tt.shouldMatch { - mockHandler.On("ServeDNS", mock.Anything, mock.Anything).Once() - } - } - - pattern := tt.handlerDomain - if tt.isWildcard { - pattern = "*." + tt.handlerDomain[2:] - } - - chain.AddHandler(pattern, handler, nbdns.PriorityDefault, nil) - - r := new(dns.Msg) - r.SetQuestion(tt.queryDomain, dns.TypeA) - w := &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}} - - chain.ServeDNS(w, r) - - if h, ok := handler.(*nbdns.MockHandler); ok { - h.AssertExpectations(t) - } else if h, ok := handler.(*nbdns.MockSubdomainHandler); ok { - h.AssertExpectations(t) - } - }) - } -} - -// TestHandlerChain_ServeDNS_OverlappingDomains tests behavior with overlapping domain patterns -func TestHandlerChain_ServeDNS_OverlappingDomains(t *testing.T) { - tests := []struct { - name string - handlers []struct { - pattern string - priority int - } - queryDomain string - expectedCalls int - expectedHandler int // index of the handler that should be called - }{ - { - name: "wildcard and exact same priority - exact should win", - handlers: []struct { - pattern string - priority int - }{ - {pattern: "*.example.com.", priority: nbdns.PriorityDefault}, - {pattern: "example.com.", priority: nbdns.PriorityDefault}, - }, - queryDomain: "example.com.", - expectedCalls: 1, - expectedHandler: 1, // exact match handler should be called - }, - { - name: "higher priority wildcard over lower priority exact", - handlers: []struct { - pattern string - priority int - }{ - {pattern: "example.com.", priority: nbdns.PriorityDefault}, - {pattern: "*.example.com.", priority: nbdns.PriorityDNSRoute}, - }, - queryDomain: "test.example.com.", - expectedCalls: 1, - expectedHandler: 1, // higher priority wildcard handler should be called - }, - { - name: "multiple wildcards different priorities", - handlers: []struct { - pattern string - priority int - }{ - {pattern: "*.example.com.", priority: nbdns.PriorityDefault}, - {pattern: "*.example.com.", priority: nbdns.PriorityMatchDomain}, - {pattern: "*.example.com.", priority: nbdns.PriorityDNSRoute}, - }, - queryDomain: "test.example.com.", - expectedCalls: 1, - expectedHandler: 2, // highest priority handler should be called - }, - { - name: "subdomain with mix of patterns", - handlers: []struct { - pattern string - priority int - }{ - {pattern: "*.example.com.", priority: nbdns.PriorityDefault}, - {pattern: "test.example.com.", priority: nbdns.PriorityMatchDomain}, - {pattern: "*.test.example.com.", priority: nbdns.PriorityDNSRoute}, - }, - queryDomain: "sub.test.example.com.", - expectedCalls: 1, - expectedHandler: 2, // highest priority matching handler should be called - }, - { - name: "root zone with specific domain", - handlers: []struct { - pattern string - priority int - }{ - {pattern: ".", priority: nbdns.PriorityDefault}, - {pattern: "example.com.", priority: nbdns.PriorityDNSRoute}, - }, - queryDomain: "example.com.", - expectedCalls: 1, - expectedHandler: 1, // higher priority specific domain should win over root - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - chain := nbdns.NewHandlerChain() - var handlers []*nbdns.MockHandler - - // Setup handlers and expectations - for i := range tt.handlers { - handler := &nbdns.MockHandler{} - handlers = append(handlers, handler) - - // Set expectation based on whether this handler should be called - if i == tt.expectedHandler { - handler.On("ServeDNS", mock.Anything, mock.Anything).Once() - } else { - handler.On("ServeDNS", mock.Anything, mock.Anything).Maybe() - } - - chain.AddHandler(tt.handlers[i].pattern, handler, tt.handlers[i].priority, nil) - } - - // Create and execute request - r := new(dns.Msg) - r.SetQuestion(tt.queryDomain, dns.TypeA) - w := &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}} - chain.ServeDNS(w, r) - - // Verify expectations - for _, handler := range handlers { - handler.AssertExpectations(t) - } - }) - } -} - -// TestHandlerChain_ServeDNS_ChainContinuation tests the chain continuation functionality -func TestHandlerChain_ServeDNS_ChainContinuation(t *testing.T) { - chain := nbdns.NewHandlerChain() - - // Create handlers - handler1 := &nbdns.MockHandler{} - handler2 := &nbdns.MockHandler{} - handler3 := &nbdns.MockHandler{} - - // Add handlers in priority order - chain.AddHandler("example.com.", handler1, nbdns.PriorityDNSRoute, nil) - chain.AddHandler("example.com.", handler2, nbdns.PriorityMatchDomain, nil) - chain.AddHandler("example.com.", handler3, nbdns.PriorityDefault, nil) - - // Create test request - r := new(dns.Msg) - r.SetQuestion("example.com.", dns.TypeA) - - // Setup mock responses to simulate chain continuation - handler1.On("ServeDNS", mock.Anything, r).Run(func(args mock.Arguments) { - // First handler signals continue - w := args.Get(0).(*nbdns.ResponseWriterChain) - resp := new(dns.Msg) - resp.SetRcode(r, dns.RcodeNameError) - resp.MsgHdr.Zero = true // Signal to continue - assert.NoError(t, w.WriteMsg(resp)) - }).Once() - - handler2.On("ServeDNS", mock.Anything, r).Run(func(args mock.Arguments) { - // Second handler signals continue - w := args.Get(0).(*nbdns.ResponseWriterChain) - resp := new(dns.Msg) - resp.SetRcode(r, dns.RcodeNameError) - resp.MsgHdr.Zero = true - assert.NoError(t, w.WriteMsg(resp)) - }).Once() - - handler3.On("ServeDNS", mock.Anything, r).Run(func(args mock.Arguments) { - // Last handler responds normally - w := args.Get(0).(*nbdns.ResponseWriterChain) - resp := new(dns.Msg) - resp.SetRcode(r, dns.RcodeSuccess) - assert.NoError(t, w.WriteMsg(resp)) - }).Once() - - // Execute - w := &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}} - chain.ServeDNS(w, r) - - // Verify all handlers were called in order - handler1.AssertExpectations(t) - handler2.AssertExpectations(t) - handler3.AssertExpectations(t) -} - -// mockResponseWriter implements dns.ResponseWriter for testing -type mockResponseWriter struct { - mock.Mock -} - -func (m *mockResponseWriter) LocalAddr() net.Addr { return nil } -func (m *mockResponseWriter) RemoteAddr() net.Addr { return nil } -func (m *mockResponseWriter) WriteMsg(*dns.Msg) error { return nil } -func (m *mockResponseWriter) Write([]byte) (int, error) { return 0, nil } -func (m *mockResponseWriter) Close() error { return nil } -func (m *mockResponseWriter) TsigStatus() error { return nil } -func (m *mockResponseWriter) TsigTimersOnly(bool) {} -func (m *mockResponseWriter) Hijack() {} - -func TestHandlerChain_PriorityDeregistration(t *testing.T) { - tests := []struct { - name string - ops []struct { - action string // "add" or "remove" - pattern string - priority int - } - query string - expectedCalls map[int]bool // map[priority]shouldBeCalled - }{ - { - name: "remove high priority keeps lower priority handler", - ops: []struct { - action string - pattern string - priority int - }{ - {"add", "example.com.", nbdns.PriorityDNSRoute}, - {"add", "example.com.", nbdns.PriorityMatchDomain}, - {"remove", "example.com.", nbdns.PriorityDNSRoute}, - }, - query: "example.com.", - expectedCalls: map[int]bool{ - nbdns.PriorityDNSRoute: false, - nbdns.PriorityMatchDomain: true, - }, - }, - { - name: "remove lower priority keeps high priority handler", - ops: []struct { - action string - pattern string - priority int - }{ - {"add", "example.com.", nbdns.PriorityDNSRoute}, - {"add", "example.com.", nbdns.PriorityMatchDomain}, - {"remove", "example.com.", nbdns.PriorityMatchDomain}, - }, - query: "example.com.", - expectedCalls: map[int]bool{ - nbdns.PriorityDNSRoute: true, - nbdns.PriorityMatchDomain: false, - }, - }, - { - name: "remove all handlers in order", - ops: []struct { - action string - pattern string - priority int - }{ - {"add", "example.com.", nbdns.PriorityDNSRoute}, - {"add", "example.com.", nbdns.PriorityMatchDomain}, - {"add", "example.com.", nbdns.PriorityDefault}, - {"remove", "example.com.", nbdns.PriorityDNSRoute}, - {"remove", "example.com.", nbdns.PriorityMatchDomain}, - }, - query: "example.com.", - expectedCalls: map[int]bool{ - nbdns.PriorityDNSRoute: false, - nbdns.PriorityMatchDomain: false, - nbdns.PriorityDefault: true, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - chain := nbdns.NewHandlerChain() - handlers := make(map[int]*nbdns.MockHandler) - - // Execute operations - for _, op := range tt.ops { - if op.action == "add" { - handler := &nbdns.MockHandler{} - handlers[op.priority] = handler - chain.AddHandler(op.pattern, handler, op.priority, nil) - } else { - chain.RemoveHandler(op.pattern, op.priority) - } - } - - // Create test request - r := new(dns.Msg) - r.SetQuestion(tt.query, dns.TypeA) - w := &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}} - - // Setup expectations - for priority, handler := range handlers { - if shouldCall, exists := tt.expectedCalls[priority]; exists && shouldCall { - handler.On("ServeDNS", mock.Anything, r).Once() - } else { - handler.On("ServeDNS", mock.Anything, r).Maybe() - } - } - - // Execute request - chain.ServeDNS(w, r) - - // Verify expectations - for _, handler := range handlers { - handler.AssertExpectations(t) - } - - // Verify handler exists check - for priority, shouldExist := range tt.expectedCalls { - if shouldExist { - assert.True(t, chain.HasHandlers(tt.ops[0].pattern), - "Handler chain should have handlers for pattern after removing priority %d", priority) - } - } - }) - } -} - -func TestHandlerChain_MultiPriorityHandling(t *testing.T) { - chain := nbdns.NewHandlerChain() - - testDomain := "example.com." - testQuery := "test.example.com." - - // Create handlers with MatchSubdomains enabled - routeHandler := &nbdns.MockSubdomainHandler{Subdomains: true} - matchHandler := &nbdns.MockSubdomainHandler{Subdomains: true} - defaultHandler := &nbdns.MockSubdomainHandler{Subdomains: true} - - // Create test request that will be reused - r := new(dns.Msg) - r.SetQuestion(testQuery, dns.TypeA) - - // Add handlers in mixed order - chain.AddHandler(testDomain, defaultHandler, nbdns.PriorityDefault, nil) - chain.AddHandler(testDomain, routeHandler, nbdns.PriorityDNSRoute, nil) - chain.AddHandler(testDomain, matchHandler, nbdns.PriorityMatchDomain, nil) - - // Test 1: Initial state with all three handlers - w := &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}} - // Highest priority handler (routeHandler) should be called - routeHandler.On("ServeDNS", mock.Anything, r).Return().Once() - - chain.ServeDNS(w, r) - routeHandler.AssertExpectations(t) - - // Test 2: Remove highest priority handler - chain.RemoveHandler(testDomain, nbdns.PriorityDNSRoute) - assert.True(t, chain.HasHandlers(testDomain)) - - w = &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}} - // Now middle priority handler (matchHandler) should be called - matchHandler.On("ServeDNS", mock.Anything, r).Return().Once() - - chain.ServeDNS(w, r) - matchHandler.AssertExpectations(t) - - // Test 3: Remove middle priority handler - chain.RemoveHandler(testDomain, nbdns.PriorityMatchDomain) - assert.True(t, chain.HasHandlers(testDomain)) - - w = &nbdns.ResponseWriterChain{ResponseWriter: &mockResponseWriter{}} - // Now lowest priority handler (defaultHandler) should be called - defaultHandler.On("ServeDNS", mock.Anything, r).Return().Once() - - chain.ServeDNS(w, r) - defaultHandler.AssertExpectations(t) - - // Test 4: Remove last handler - chain.RemoveHandler(testDomain, nbdns.PriorityDefault) - assert.False(t, chain.HasHandlers(testDomain)) -} diff --git a/client/internal/dns/local.go b/client/internal/dns/local.go index 9a78d4d50..6a459794b 100644 --- a/client/internal/dns/local.go +++ b/client/internal/dns/local.go @@ -17,24 +17,12 @@ type localResolver struct { records sync.Map } -func (d *localResolver) MatchSubdomains() bool { - return true -} - func (d *localResolver) stop() { } -// String returns a string representation of the local resolver -func (d *localResolver) String() string { - return fmt.Sprintf("local resolver [%d records]", len(d.registeredMap)) -} - // ServeDNS handles a DNS request func (d *localResolver) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { - if len(r.Question) > 0 { - log.Tracef("received question: domain=%s type=%v class=%v", r.Question[0].Name, r.Question[0].Qtype, r.Question[0].Qclass) - } - + log.Tracef("received question: %#v", r.Question[0]) replyMessage := &dns.Msg{} replyMessage.SetReply(r) replyMessage.RecursionAvailable = true diff --git a/client/internal/dns/mock_server.go b/client/internal/dns/mock_server.go index 7e36ea5df..0739f0542 100644 --- a/client/internal/dns/mock_server.go +++ b/client/internal/dns/mock_server.go @@ -3,30 +3,14 @@ package dns import ( "fmt" - "github.com/miekg/dns" - nbdns "github.com/netbirdio/netbird/dns" ) // MockServer is the mock instance of a dns server type MockServer struct { - InitializeFunc func() error - StopFunc func() - UpdateDNSServerFunc func(serial uint64, update nbdns.Config) error - RegisterHandlerFunc func([]string, dns.Handler, int) - DeregisterHandlerFunc func([]string, int) -} - -func (m *MockServer) RegisterHandler(domains []string, handler dns.Handler, priority int) { - if m.RegisterHandlerFunc != nil { - m.RegisterHandlerFunc(domains, handler, priority) - } -} - -func (m *MockServer) DeregisterHandler(domains []string, priority int) { - if m.DeregisterHandlerFunc != nil { - m.DeregisterHandlerFunc(domains, priority) - } + InitializeFunc func() error + StopFunc func() + UpdateDNSServerFunc func(serial uint64, update nbdns.Config) error } // Initialize mock implementation of Initialize from Server interface diff --git a/client/internal/dns/server.go b/client/internal/dns/server.go index 5a9cb50d0..f0277319c 100644 --- a/client/internal/dns/server.go +++ b/client/internal/dns/server.go @@ -30,8 +30,6 @@ type IosDnsManager interface { // Server is a dns server interface type Server interface { - RegisterHandler(domains []string, handler dns.Handler, priority int) - DeregisterHandler(domains []string, priority int) Initialize() error Stop() DnsIP() string @@ -50,14 +48,12 @@ type DefaultServer struct { mux sync.Mutex service service dnsMuxMap registeredHandlerMap - handlerPriorities map[string]int localResolver *localResolver wgInterface WGIface hostManager hostManager updateSerial uint64 previousConfigHash uint64 currentConfig HostDNSConfig - handlerChain *HandlerChain // permanent related properties permanent bool @@ -78,9 +74,8 @@ type handlerWithStop interface { } type muxUpdate struct { - domain string - handler handlerWithStop - priority int + domain string + handler handlerWithStop } // NewDefaultServer returns a new dns server @@ -140,12 +135,10 @@ func NewDefaultServerIos( func newDefaultServer(ctx context.Context, wgInterface WGIface, dnsService service, statusRecorder *peer.Status, stateManager *statemanager.Manager) *DefaultServer { ctx, stop := context.WithCancel(ctx) defaultServer := &DefaultServer{ - ctx: ctx, - ctxCancel: stop, - service: dnsService, - handlerChain: NewHandlerChain(), - dnsMuxMap: make(registeredHandlerMap), - handlerPriorities: make(map[string]int), + ctx: ctx, + ctxCancel: stop, + service: dnsService, + dnsMuxMap: make(registeredHandlerMap), localResolver: &localResolver{ registeredMap: make(registrationMap), }, @@ -158,51 +151,6 @@ func newDefaultServer(ctx context.Context, wgInterface WGIface, dnsService servi return defaultServer } -func (s *DefaultServer) RegisterHandler(domains []string, handler dns.Handler, priority int) { - s.mux.Lock() - defer s.mux.Unlock() - - s.registerHandler(domains, handler, priority) -} - -func (s *DefaultServer) registerHandler(domains []string, handler dns.Handler, priority int) { - log.Debugf("registering handler %s with priority %d", handler, priority) - - for _, domain := range domains { - if domain == "" { - log.Warn("skipping empty domain") - continue - } - s.handlerChain.AddHandler(domain, handler, priority, nil) - s.handlerPriorities[domain] = priority - s.service.RegisterMux(nbdns.NormalizeZone(domain), s.handlerChain) - } -} - -func (s *DefaultServer) DeregisterHandler(domains []string, priority int) { - s.mux.Lock() - defer s.mux.Unlock() - - s.deregisterHandler(domains, priority) -} - -func (s *DefaultServer) deregisterHandler(domains []string, priority int) { - log.Debugf("deregistering handler %v with priority %d", domains, priority) - - for _, domain := range domains { - s.handlerChain.RemoveHandler(domain, priority) - - // Only deregister from service if no handlers remain - if !s.handlerChain.HasHandlers(domain) { - if domain == "" { - log.Warn("skipping empty domain") - continue - } - s.service.DeregisterMux(nbdns.NormalizeZone(domain)) - } - } -} - // Initialize instantiate host manager and the dns service func (s *DefaultServer) Initialize() (err error) { s.mux.Lock() @@ -395,14 +343,14 @@ func (s *DefaultServer) buildLocalHandlerUpdate(customZones []nbdns.CustomZone) localRecords := make(map[string]nbdns.SimpleRecord, 0) for _, customZone := range customZones { + if len(customZone.Records) == 0 { return nil, nil, fmt.Errorf("received an empty list of records") } muxUpdates = append(muxUpdates, muxUpdate{ - domain: customZone.Domain, - handler: s.localResolver, - priority: PriorityMatchDomain, + domain: customZone.Domain, + handler: s.localResolver, }) for _, record := range customZone.Records { @@ -464,9 +412,8 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam if nsGroup.Primary { muxUpdates = append(muxUpdates, muxUpdate{ - domain: nbdns.RootZone, - handler: handler, - priority: PriorityDefault, + domain: nbdns.RootZone, + handler: handler, }) continue } @@ -482,9 +429,8 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam return nil, fmt.Errorf("received a nameserver group with an empty domain element") } muxUpdates = append(muxUpdates, muxUpdate{ - domain: domain, - handler: handler, - priority: PriorityMatchDomain, + domain: domain, + handler: handler, }) } } @@ -494,16 +440,12 @@ func (s *DefaultServer) buildUpstreamHandlerUpdate(nameServerGroups []*nbdns.Nam func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) { muxUpdateMap := make(registeredHandlerMap) - handlersByPriority := make(map[string]int) var isContainRootUpdate bool - // First register new handlers for _, update := range muxUpdates { - s.registerHandler([]string{update.domain}, update.handler, update.priority) + s.service.RegisterMux(update.domain, update.handler) muxUpdateMap[update.domain] = update.handler - handlersByPriority[update.domain] = update.priority - if existingHandler, ok := s.dnsMuxMap[update.domain]; ok { existingHandler.stop() } @@ -513,7 +455,6 @@ func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) { } } - // Then deregister old handlers not in the update for key, existingHandler := range s.dnsMuxMap { _, found := muxUpdateMap[key] if !found { @@ -522,16 +463,12 @@ func (s *DefaultServer) updateMux(muxUpdates []muxUpdate) { existingHandler.stop() } else { existingHandler.stop() - // Deregister with the priority that was used to register - if oldPriority, ok := s.handlerPriorities[key]; ok { - s.deregisterHandler([]string{key}, oldPriority) - } + s.service.DeregisterMux(key) } } } s.dnsMuxMap = muxUpdateMap - s.handlerPriorities = handlersByPriority } func (s *DefaultServer) updateLocalResolver(update map[string]nbdns.SimpleRecord) { @@ -580,13 +517,13 @@ func (s *DefaultServer) upstreamCallbacks( if nsGroup.Primary { removeIndex[nbdns.RootZone] = -1 s.currentConfig.RouteAll = false - s.deregisterHandler([]string{nbdns.RootZone}, PriorityDefault) + s.service.DeregisterMux(nbdns.RootZone) } for i, item := range s.currentConfig.Domains { if _, found := removeIndex[item.Domain]; found { s.currentConfig.Domains[i].Disabled = true - s.deregisterHandler([]string{item.Domain}, PriorityMatchDomain) + s.service.DeregisterMux(item.Domain) removeIndex[item.Domain] = i } } @@ -617,7 +554,7 @@ func (s *DefaultServer) upstreamCallbacks( continue } s.currentConfig.Domains[i].Disabled = false - s.registerHandler([]string{domain}, handler, PriorityMatchDomain) + s.service.RegisterMux(domain, handler) } l := log.WithField("nameservers", nsGroup.NameServers) @@ -625,7 +562,7 @@ func (s *DefaultServer) upstreamCallbacks( if nsGroup.Primary { s.currentConfig.RouteAll = true - s.registerHandler([]string{nbdns.RootZone}, handler, PriorityDefault) + s.service.RegisterMux(nbdns.RootZone, handler) } if err := s.hostManager.applyDNSConfig(s.currentConfig, s.stateManager); err != nil { l.WithError(err).Error("reactivate temporary disabled nameserver group, DNS update apply") @@ -656,8 +593,7 @@ func (s *DefaultServer) addHostRootZone() { } handler.deactivate = func(error) {} handler.reactivate = func() {} - - s.registerHandler([]string{nbdns.RootZone}, handler, PriorityDefault) + s.service.RegisterMux(nbdns.RootZone, handler) } func (s *DefaultServer) updateNSGroupStates(groups []*nbdns.NameServerGroup) { diff --git a/client/internal/dns/server_test.go b/client/internal/dns/server_test.go index 44d20c6f3..eab9f4ecb 100644 --- a/client/internal/dns/server_test.go +++ b/client/internal/dns/server_test.go @@ -11,9 +11,7 @@ import ( "time" "github.com/golang/mock/gomock" - "github.com/miekg/dns" log "github.com/sirupsen/logrus" - "github.com/stretchr/testify/mock" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "github.com/netbirdio/netbird/client/firewall/uspfilter" @@ -514,7 +512,7 @@ func TestDNSServerStartStop(t *testing.T) { t.Error(err) } - dnsServer.registerHandler([]string{"netbird.cloud"}, dnsServer.localResolver, 1) + dnsServer.service.RegisterMux("netbird.cloud", dnsServer.localResolver) resolver := &net.Resolver{ PreferGo: true, @@ -562,9 +560,7 @@ func TestDNSServerUpstreamDeactivateCallback(t *testing.T) { localResolver: &localResolver{ registeredMap: make(registrationMap), }, - handlerChain: NewHandlerChain(), - handlerPriorities: make(map[string]int), - hostManager: hostManager, + hostManager: hostManager, currentConfig: HostDNSConfig{ Domains: []DomainConfig{ {false, "domain0", false}, @@ -876,86 +872,3 @@ func newDnsResolver(ip string, port int) *net.Resolver { }, } } - -// MockHandler implements dns.Handler interface for testing -type MockHandler struct { - mock.Mock -} - -func (m *MockHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { - m.Called(w, r) -} - -type MockSubdomainHandler struct { - MockHandler - Subdomains bool -} - -func (m *MockSubdomainHandler) MatchSubdomains() bool { - return m.Subdomains -} - -func TestHandlerChain_DomainPriorities(t *testing.T) { - chain := NewHandlerChain() - - dnsRouteHandler := &MockHandler{} - upstreamHandler := &MockSubdomainHandler{ - Subdomains: true, - } - - chain.AddHandler("example.com.", dnsRouteHandler, PriorityDNSRoute, nil) - chain.AddHandler("example.com.", upstreamHandler, PriorityMatchDomain, nil) - - testCases := []struct { - name string - query string - expectedHandler dns.Handler - }{ - { - name: "exact domain with dns route handler", - query: "example.com.", - expectedHandler: dnsRouteHandler, - }, - { - name: "subdomain should use upstream handler", - query: "sub.example.com.", - expectedHandler: upstreamHandler, - }, - { - name: "deep subdomain should use upstream handler", - query: "deep.sub.example.com.", - expectedHandler: upstreamHandler, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - r := new(dns.Msg) - r.SetQuestion(tc.query, dns.TypeA) - w := &ResponseWriterChain{ResponseWriter: &mockResponseWriter{}} - - if mh, ok := tc.expectedHandler.(*MockHandler); ok { - mh.On("ServeDNS", mock.Anything, r).Once() - } else if mh, ok := tc.expectedHandler.(*MockSubdomainHandler); ok { - mh.On("ServeDNS", mock.Anything, r).Once() - } - - chain.ServeDNS(w, r) - - if mh, ok := tc.expectedHandler.(*MockHandler); ok { - mh.AssertExpectations(t) - } else if mh, ok := tc.expectedHandler.(*MockSubdomainHandler); ok { - mh.AssertExpectations(t) - } - - // Reset mocks - if mh, ok := tc.expectedHandler.(*MockHandler); ok { - mh.ExpectedCalls = nil - mh.Calls = nil - } else if mh, ok := tc.expectedHandler.(*MockSubdomainHandler); ok { - mh.ExpectedCalls = nil - mh.Calls = nil - } - }) - } -} diff --git a/client/internal/dns/service_listener.go b/client/internal/dns/service_listener.go index 72dc4bc6e..e0f9da26f 100644 --- a/client/internal/dns/service_listener.go +++ b/client/internal/dns/service_listener.go @@ -105,7 +105,6 @@ func (s *serviceViaListener) Stop() { } func (s *serviceViaListener) RegisterMux(pattern string, handler dns.Handler) { - log.Debugf("registering dns handler for pattern: %s", pattern) s.dnsMux.Handle(pattern, handler) } diff --git a/client/internal/dns/upstream.go b/client/internal/dns/upstream.go index f0aa12b65..b3baf2fa8 100644 --- a/client/internal/dns/upstream.go +++ b/client/internal/dns/upstream.go @@ -66,15 +66,6 @@ func newUpstreamResolverBase(ctx context.Context, statusRecorder *peer.Status) * } } -// String returns a string representation of the upstream resolver -func (u *upstreamResolverBase) String() string { - return fmt.Sprintf("upstream %v", u.upstreamServers) -} - -func (u *upstreamResolverBase) MatchSubdomains() bool { - return true -} - func (u *upstreamResolverBase) stop() { log.Debugf("stopping serving DNS for upstreams %s", u.upstreamServers) u.cancel() diff --git a/client/internal/dnsfwd/forwarder.go b/client/internal/dnsfwd/forwarder.go deleted file mode 100644 index ae31ffac6..000000000 --- a/client/internal/dnsfwd/forwarder.go +++ /dev/null @@ -1,157 +0,0 @@ -package dnsfwd - -import ( - "context" - "errors" - "net" - - "github.com/miekg/dns" - log "github.com/sirupsen/logrus" - - nbdns "github.com/netbirdio/netbird/dns" -) - -const errResolveFailed = "failed to resolve query for domain=%s: %v" - -type DNSForwarder struct { - listenAddress string - ttl uint32 - domains []string - - dnsServer *dns.Server - mux *dns.ServeMux -} - -func NewDNSForwarder(listenAddress string, ttl uint32) *DNSForwarder { - log.Debugf("creating DNS forwarder with listen_address=%s ttl=%d", listenAddress, ttl) - return &DNSForwarder{ - listenAddress: listenAddress, - ttl: ttl, - } -} - -func (f *DNSForwarder) Listen(domains []string) error { - log.Infof("listen DNS forwarder on address=%s", f.listenAddress) - mux := dns.NewServeMux() - - dnsServer := &dns.Server{ - Addr: f.listenAddress, - Net: "udp", - Handler: mux, - } - f.dnsServer = dnsServer - f.mux = mux - - f.UpdateDomains(domains) - - return dnsServer.ListenAndServe() -} - -func (f *DNSForwarder) UpdateDomains(domains []string) { - log.Debugf("Updating domains from %v to %v", f.domains, domains) - - for _, d := range f.domains { - f.mux.HandleRemove(d) - } - - newDomains := filterDomains(domains) - for _, d := range newDomains { - f.mux.HandleFunc(d, f.handleDNSQuery) - } - f.domains = newDomains -} - -func (f *DNSForwarder) Close(ctx context.Context) error { - if f.dnsServer == nil { - return nil - } - return f.dnsServer.ShutdownContext(ctx) -} - -func (f *DNSForwarder) handleDNSQuery(w dns.ResponseWriter, query *dns.Msg) { - if len(query.Question) == 0 { - return - } - log.Tracef("received DNS request for DNS forwarder: domain=%v type=%v class=%v", - query.Question[0].Name, query.Question[0].Qtype, query.Question[0].Qclass) - - question := query.Question[0] - domain := question.Name - - resp := query.SetReply(query) - - ips, err := net.LookupIP(domain) - if err != nil { - var dnsErr *net.DNSError - - switch { - case errors.As(err, &dnsErr): - resp.Rcode = dns.RcodeServerFailure - if dnsErr.IsNotFound { - // Pass through NXDOMAIN - resp.Rcode = dns.RcodeNameError - } - - if dnsErr.Server != "" { - log.Warnf("failed to resolve query for domain=%s server=%s: %v", domain, dnsErr.Server, err) - } else { - log.Warnf(errResolveFailed, domain, err) - } - default: - resp.Rcode = dns.RcodeServerFailure - log.Warnf(errResolveFailed, domain, err) - } - - if err := w.WriteMsg(resp); err != nil { - log.Errorf("failed to write failure DNS response: %v", err) - } - return - } - - for _, ip := range ips { - var respRecord dns.RR - if ip.To4() == nil { - log.Tracef("resolved domain=%s to IPv6=%s", domain, ip) - rr := dns.AAAA{ - AAAA: ip, - Hdr: dns.RR_Header{ - Name: domain, - Rrtype: dns.TypeAAAA, - Class: dns.ClassINET, - Ttl: f.ttl, - }, - } - respRecord = &rr - } else { - log.Tracef("resolved domain=%s to IPv4=%s", domain, ip) - rr := dns.A{ - A: ip, - Hdr: dns.RR_Header{ - Name: domain, - Rrtype: dns.TypeA, - Class: dns.ClassINET, - Ttl: f.ttl, - }, - } - respRecord = &rr - } - resp.Answer = append(resp.Answer, respRecord) - } - - if err := w.WriteMsg(resp); err != nil { - log.Errorf("failed to write DNS response: %v", err) - } -} - -// filterDomains returns a list of normalized domains -func filterDomains(domains []string) []string { - newDomains := make([]string, 0, len(domains)) - for _, d := range domains { - if d == "" { - log.Warn("empty domain in DNS forwarder") - continue - } - newDomains = append(newDomains, nbdns.NormalizeZone(d)) - } - return newDomains -} diff --git a/client/internal/dnsfwd/manager.go b/client/internal/dnsfwd/manager.go deleted file mode 100644 index 7cff6d517..000000000 --- a/client/internal/dnsfwd/manager.go +++ /dev/null @@ -1,106 +0,0 @@ -package dnsfwd - -import ( - "context" - "fmt" - "net" - - "github.com/hashicorp/go-multierror" - log "github.com/sirupsen/logrus" - - nberrors "github.com/netbirdio/netbird/client/errors" - firewall "github.com/netbirdio/netbird/client/firewall/manager" -) - -const ( - // ListenPort is the port that the DNS forwarder listens on. It has been used by the client peers also - ListenPort = 5353 - dnsTTL = 60 //seconds -) - -type Manager struct { - firewall firewall.Manager - - fwRules []firewall.Rule - dnsForwarder *DNSForwarder -} - -func NewManager(fw firewall.Manager) *Manager { - return &Manager{ - firewall: fw, - } -} - -func (m *Manager) Start(domains []string) error { - log.Infof("starting DNS forwarder") - if m.dnsForwarder != nil { - return nil - } - - if err := m.allowDNSFirewall(); err != nil { - return err - } - - m.dnsForwarder = NewDNSForwarder(fmt.Sprintf(":%d", ListenPort), dnsTTL) - go func() { - if err := m.dnsForwarder.Listen(domains); err != nil { - // todo handle close error if it is exists - log.Errorf("failed to start DNS forwarder, err: %v", err) - } - }() - - return nil -} - -func (m *Manager) UpdateDomains(domains []string) { - if m.dnsForwarder == nil { - return - } - - m.dnsForwarder.UpdateDomains(domains) -} - -func (m *Manager) Stop(ctx context.Context) error { - if m.dnsForwarder == nil { - return nil - } - - var mErr *multierror.Error - if err := m.dropDNSFirewall(); err != nil { - mErr = multierror.Append(mErr, err) - } - - if err := m.dnsForwarder.Close(ctx); err != nil { - mErr = multierror.Append(mErr, err) - } - - m.dnsForwarder = nil - return nberrors.FormatErrorOrNil(mErr) -} - -func (h *Manager) allowDNSFirewall() error { - dport := &firewall.Port{ - IsRange: false, - Values: []int{ListenPort}, - } - dnsRules, err := h.firewall.AddPeerFiltering(net.ParseIP("0.0.0.0"), firewall.ProtocolUDP, nil, dport, firewall.RuleDirectionIN, firewall.ActionAccept, "", "") - if err != nil { - log.Errorf("failed to add allow DNS router rules, err: %v", err) - return err - } - h.fwRules = dnsRules - - return nil -} - -func (h *Manager) dropDNSFirewall() error { - var mErr *multierror.Error - for _, rule := range h.fwRules { - if err := h.firewall.DeletePeerRule(rule); err != nil { - mErr = multierror.Append(mErr, fmt.Errorf("failed to delete DNS router rules, err: %v", err)) - } - } - - h.fwRules = nil - return nberrors.FormatErrorOrNil(mErr) -} diff --git a/client/internal/engine.go b/client/internal/engine.go index 9724e2a22..34219def1 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "maps" "math/rand" "net" "net/netip" @@ -29,12 +30,10 @@ import ( "github.com/netbirdio/netbird/client/iface/device" "github.com/netbirdio/netbird/client/internal/acl" "github.com/netbirdio/netbird/client/internal/dns" - "github.com/netbirdio/netbird/client/internal/dnsfwd" "github.com/netbirdio/netbird/client/internal/networkmonitor" "github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/peer/guard" icemaker "github.com/netbirdio/netbird/client/internal/peer/ice" - "github.com/netbirdio/netbird/client/internal/peerstore" "github.com/netbirdio/netbird/client/internal/relay" "github.com/netbirdio/netbird/client/internal/rosenpass" "github.com/netbirdio/netbird/client/internal/routemanager" @@ -118,7 +117,7 @@ type Engine struct { // mgmClient is a Management Service client mgmClient mgm.Client // peerConns is a map that holds all the peers that are known to this peer - peerStore *peerstore.Store + peerConns map[string]*peer.Conn beforePeerHook nbnet.AddHookFunc afterPeerHook nbnet.RemoveHookFunc @@ -138,6 +137,10 @@ type Engine struct { TURNs []*stun.URI stunTurn atomic.Value + // clientRoutes is the most recent list of clientRoutes received from the Management Service + clientRoutes route.HAMap + clientRoutesMu sync.RWMutex + clientCtx context.Context clientCancel context.CancelFunc @@ -158,10 +161,9 @@ type Engine struct { statusRecorder *peer.Status - firewall manager.Manager - routeManager routemanager.Manager - acl acl.Manager - dnsForwardMgr *dnsfwd.Manager + firewall manager.Manager + routeManager routemanager.Manager + acl acl.Manager dnsServer dns.Server @@ -232,7 +234,7 @@ func NewEngineWithProbes( signaler: peer.NewSignaler(signalClient, config.WgPrivateKey), mgmClient: mgmClient, relayManager: relayManager, - peerStore: peerstore.NewConnStore(), + peerConns: make(map[string]*peer.Conn), syncMsgMux: &sync.Mutex{}, config: config, mobileDep: mobileDep, @@ -285,13 +287,6 @@ func (e *Engine) Stop() error { e.routeManager.Stop(e.stateManager) } - if e.dnsForwardMgr != nil { - if err := e.dnsForwardMgr.Stop(context.Background()); err != nil { - log.Errorf("failed to stop DNS forward: %v", err) - } - e.dnsForwardMgr = nil - } - if e.srWatcher != nil { e.srWatcher.Close() } @@ -305,6 +300,10 @@ func (e *Engine) Stop() error { return fmt.Errorf("failed to remove all peers: %s", err) } + e.clientRoutesMu.Lock() + e.clientRoutes = nil + e.clientRoutesMu.Unlock() + if e.cancel != nil { e.cancel() } @@ -383,8 +382,6 @@ func (e *Engine) Start() error { e.relayManager, initialRoutes, e.stateManager, - dnsServer, - e.peerStore, ) beforePeerHook, afterPeerHook, err := e.routeManager.Init() if err != nil { @@ -463,8 +460,8 @@ func (e *Engine) modifyPeers(peersUpdate []*mgmProto.RemotePeerConfig) error { var modified []*mgmProto.RemotePeerConfig for _, p := range peersUpdate { peerPubKey := p.GetWgPubKey() - if allowedIPs, ok := e.peerStore.AllowedIPs(peerPubKey); ok { - if allowedIPs != strings.Join(p.AllowedIps, ",") { + if peerConn, ok := e.peerConns[peerPubKey]; ok { + if peerConn.WgConfig().AllowedIps != strings.Join(p.AllowedIps, ",") { modified = append(modified, p) continue } @@ -495,12 +492,17 @@ func (e *Engine) modifyPeers(peersUpdate []*mgmProto.RemotePeerConfig) error { // removePeers finds and removes peers that do not exist anymore in the network map received from the Management Service. // It also removes peers that have been modified (e.g. change of IP address). They will be added again in addPeers method. func (e *Engine) removePeers(peersUpdate []*mgmProto.RemotePeerConfig) error { + currentPeers := make([]string, 0, len(e.peerConns)) + for p := range e.peerConns { + currentPeers = append(currentPeers, p) + } + newPeers := make([]string, 0, len(peersUpdate)) for _, p := range peersUpdate { newPeers = append(newPeers, p.GetWgPubKey()) } - toRemove := util.SliceDiff(e.peerStore.PeersPubKey(), newPeers) + toRemove := util.SliceDiff(currentPeers, newPeers) for _, p := range toRemove { err := e.removePeer(p) @@ -514,7 +516,7 @@ func (e *Engine) removePeers(peersUpdate []*mgmProto.RemotePeerConfig) error { func (e *Engine) removeAllPeers() error { log.Debugf("removing all peer connections") - for _, p := range e.peerStore.PeersPubKey() { + for p := range e.peerConns { err := e.removePeer(p) if err != nil { return err @@ -538,8 +540,9 @@ func (e *Engine) removePeer(peerKey string) error { } }() - conn, exists := e.peerStore.Remove(peerKey) + conn, exists := e.peerConns[peerKey] if exists { + delete(e.peerConns, peerKey) conn.Close() } return nil @@ -783,6 +786,7 @@ func (e *Engine) updateTURNs(turns []*mgmProto.ProtectedHostConfig) error { } func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error { + // intentionally leave it before checking serial because for now it can happen that peer IP changed but serial didn't if networkMap.GetPeerConfig() != nil { err := e.updateConfig(networkMap.GetPeerConfig()) @@ -802,18 +806,20 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error { e.acl.ApplyFiltering(networkMap) } - var dnsRouteFeatureFlag bool - if networkMap.PeerConfig != nil { - dnsRouteFeatureFlag = networkMap.PeerConfig.RoutingPeerDnsResolutionEnabled + protoRoutes := networkMap.GetRoutes() + if protoRoutes == nil { + protoRoutes = []*mgmProto.Route{} } - routedDomains, routes := toRoutes(networkMap.GetRoutes()) - e.updateDNSForwarder(dnsRouteFeatureFlag, routedDomains) - - if err := e.routeManager.UpdateRoutes(serial, routes, dnsRouteFeatureFlag); err != nil { + _, clientRoutes, err := e.routeManager.UpdateRoutes(serial, toRoutes(protoRoutes)) + if err != nil { log.Errorf("failed to update clientRoutes, err: %v", err) } + e.clientRoutesMu.Lock() + e.clientRoutes = clientRoutes + e.clientRoutesMu.Unlock() + log.Debugf("got peers update from Management Service, total peers to connect to = %d", len(networkMap.GetRemotePeers())) e.updateOfflinePeers(networkMap.GetOfflinePeers()) @@ -861,7 +867,8 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error { protoDNSConfig = &mgmProto.DNSConfig{} } - if err := e.dnsServer.UpdateDNSServer(serial, toDNSConfig(protoDNSConfig)); err != nil { + err = e.dnsServer.UpdateDNSServer(serial, toDNSConfig(protoDNSConfig)) + if err != nil { log.Errorf("failed to update dns server, err: %v", err) } @@ -874,12 +881,7 @@ func (e *Engine) updateNetworkMap(networkMap *mgmProto.NetworkMap) error { return nil } -func toRoutes(protoRoutes []*mgmProto.Route) ([]string, []*route.Route) { - if protoRoutes == nil { - protoRoutes = []*mgmProto.Route{} - } - - var dnsRoutes []string +func toRoutes(protoRoutes []*mgmProto.Route) []*route.Route { routes := make([]*route.Route, 0) for _, protoRoute := range protoRoutes { var prefix netip.Prefix @@ -890,8 +892,6 @@ func toRoutes(protoRoutes []*mgmProto.Route) ([]string, []*route.Route) { continue } } - dnsRoutes = append(dnsRoutes, protoRoute.Domains...) - convertedRoute := &route.Route{ ID: route.ID(protoRoute.ID), Network: prefix, @@ -905,7 +905,7 @@ func toRoutes(protoRoutes []*mgmProto.Route) ([]string, []*route.Route) { } routes = append(routes, convertedRoute) } - return dnsRoutes, routes + return routes } func toDNSConfig(protoDNSConfig *mgmProto.DNSConfig) nbdns.Config { @@ -982,16 +982,12 @@ func (e *Engine) addNewPeers(peersUpdate []*mgmProto.RemotePeerConfig) error { func (e *Engine) addNewPeer(peerConfig *mgmProto.RemotePeerConfig) error { peerKey := peerConfig.GetWgPubKey() peerIPs := peerConfig.GetAllowedIps() - if _, ok := e.peerStore.PeerConn(peerKey); !ok { + if _, ok := e.peerConns[peerKey]; !ok { conn, err := e.createPeerConn(peerKey, strings.Join(peerIPs, ",")) if err != nil { return fmt.Errorf("create peer connection: %w", err) } - - if ok := e.peerStore.AddPeerConn(peerKey, conn); !ok { - conn.Close() - return fmt.Errorf("peer already exists: %s", peerKey) - } + e.peerConns[peerKey] = conn if e.beforePeerHook != nil && e.afterPeerHook != nil { conn.AddBeforeAddPeerHook(e.beforePeerHook) @@ -1080,8 +1076,8 @@ func (e *Engine) receiveSignalEvents() { e.syncMsgMux.Lock() defer e.syncMsgMux.Unlock() - conn, ok := e.peerStore.PeerConn(msg.Key) - if !ok { + conn := e.peerConns[msg.Key] + if conn == nil { return fmt.Errorf("wrongly addressed message %s", msg.Key) } @@ -1139,7 +1135,7 @@ func (e *Engine) receiveSignalEvents() { return err } - go conn.OnRemoteCandidate(candidate, e.routeManager.GetClientRoutes()) + go conn.OnRemoteCandidate(candidate, e.GetClientRoutes()) case sProto.Body_MODE: } @@ -1243,7 +1239,7 @@ func (e *Engine) readInitialSettings() ([]*route.Route, *nbdns.Config, error) { if err != nil { return nil, nil, err } - _, routes := toRoutes(netMap.GetRoutes()) + routes := toRoutes(netMap.GetRoutes()) dnsCfg := toDNSConfig(netMap.GetDNSConfig()) return routes, &dnsCfg, nil } @@ -1326,6 +1322,26 @@ func (e *Engine) newDnsServer() ([]*route.Route, dns.Server, error) { } } +// GetClientRoutes returns the current routes from the route map +func (e *Engine) GetClientRoutes() route.HAMap { + e.clientRoutesMu.RLock() + defer e.clientRoutesMu.RUnlock() + + return maps.Clone(e.clientRoutes) +} + +// GetClientRoutesWithNetID returns the current routes from the route map, but the keys consist of the network ID only +func (e *Engine) GetClientRoutesWithNetID() map[route.NetID][]*route.Route { + e.clientRoutesMu.RLock() + defer e.clientRoutesMu.RUnlock() + + routes := make(map[route.NetID][]*route.Route, len(e.clientRoutes)) + for id, v := range e.clientRoutes { + routes[id.NetID()] = v + } + return routes +} + // GetRouteManager returns the route manager func (e *Engine) GetRouteManager() routemanager.Manager { return e.routeManager @@ -1410,8 +1426,9 @@ func (e *Engine) receiveProbeEvents() { go e.probes.WgProbe.Receive(e.ctx, func() bool { log.Debug("received wg probe request") - for _, key := range e.peerStore.PeersPubKey() { - wgStats, err := e.wgInterface.GetStats(key) + for _, peer := range e.peerConns { + key := peer.GetKey() + wgStats, err := peer.WgConfig().WgInterface.GetStats(key) if err != nil { log.Debugf("failed to get wg stats for peer %s: %s", key, err) } @@ -1488,7 +1505,7 @@ func (e *Engine) startNetworkMonitor() { func (e *Engine) addrViaRoutes(addr netip.Addr) (bool, netip.Prefix, error) { var vpnRoutes []netip.Prefix - for _, routes := range e.routeManager.GetClientRoutes() { + for _, routes := range e.GetClientRoutes() { if len(routes) > 0 && routes[0] != nil { vpnRoutes = append(vpnRoutes, routes[0].Network) } @@ -1556,40 +1573,6 @@ func (e *Engine) GetLatestNetworkMap() (*mgmProto.NetworkMap, error) { return nm, nil } -// updateDNSForwarder start or stop the DNS forwarder based on the domains and the feature flag -func (e *Engine) updateDNSForwarder(enabled bool, domains []string) { - if !enabled { - if e.dnsForwardMgr == nil { - return - } - if err := e.dnsForwardMgr.Stop(context.Background()); err != nil { - log.Errorf("failed to stop DNS forward: %v", err) - } - return - } - - if len(domains) > 0 { - log.Infof("enable domain router service for domains: %v", domains) - if e.dnsForwardMgr == nil { - e.dnsForwardMgr = dnsfwd.NewManager(e.firewall) - - if err := e.dnsForwardMgr.Start(domains); err != nil { - log.Errorf("failed to start DNS forward: %v", err) - e.dnsForwardMgr = nil - } - } else { - log.Infof("update domain router service for domains: %v", domains) - e.dnsForwardMgr.UpdateDomains(domains) - } - } else if e.dnsForwardMgr != nil { - log.Infof("disable domain router service") - if err := e.dnsForwardMgr.Stop(context.Background()); err != nil { - log.Errorf("failed to stop DNS forward: %v", err) - } - e.dnsForwardMgr = nil - } -} - // isChecksEqual checks if two slices of checks are equal. func isChecksEqual(checks []*mgmProto.Checks, oChecks []*mgmProto.Checks) bool { for _, check := range checks { diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index b81d8bd3f..b58c1f7e9 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -39,8 +39,6 @@ import ( mgmtProto "github.com/netbirdio/netbird/management/proto" "github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server/activity" - "github.com/netbirdio/netbird/management/server/settings" - "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/telemetry" relayClient "github.com/netbirdio/netbird/relay/client" "github.com/netbirdio/netbird/route" @@ -253,7 +251,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) { }, } engine.wgInterface = wgIface - engine.routeManager = routemanager.NewManager(ctx, key.PublicKey().String(), time.Minute, engine.wgInterface, engine.statusRecorder, relayMgr, nil, nil, nil, nil) + engine.routeManager = routemanager.NewManager(ctx, key.PublicKey().String(), time.Minute, engine.wgInterface, engine.statusRecorder, relayMgr, nil, nil) _, _, err = engine.routeManager.Init() require.NoError(t, err) engine.dnsServer = &dns.MockServer{ @@ -393,8 +391,8 @@ func TestEngine_UpdateNetworkMap(t *testing.T) { return } - if len(engine.peerStore.PeersPubKey()) != c.expectedLen { - t.Errorf("expecting Engine.peerConns to be of size %d, got %d", c.expectedLen, len(engine.peerStore.PeersPubKey())) + if len(engine.peerConns) != c.expectedLen { + t.Errorf("expecting Engine.peerConns to be of size %d, got %d", c.expectedLen, len(engine.peerConns)) } if engine.networkSerial != c.expectedSerial { @@ -402,7 +400,7 @@ func TestEngine_UpdateNetworkMap(t *testing.T) { } for _, p := range c.expectedPeers { - conn, ok := engine.peerStore.PeerConn(p.GetWgPubKey()) + conn, ok := engine.peerConns[p.GetWgPubKey()] if !ok { t.Errorf("expecting Engine.peerConns to contain peer %s", p) } @@ -627,10 +625,10 @@ func TestEngine_UpdateNetworkMapWithRoutes(t *testing.T) { }{} mockRouteManager := &routemanager.MockManager{ - UpdateRoutesFunc: func(updateSerial uint64, newRoutes []*route.Route) error { + UpdateRoutesFunc: func(updateSerial uint64, newRoutes []*route.Route) (map[route.ID]*route.Route, route.HAMap, error) { input.inputSerial = updateSerial input.inputRoutes = newRoutes - return testCase.inputErr + return nil, nil, testCase.inputErr }, } @@ -803,8 +801,8 @@ func TestEngine_UpdateNetworkMapWithDNSUpdate(t *testing.T) { assert.NoError(t, err, "shouldn't return error") mockRouteManager := &routemanager.MockManager{ - UpdateRoutesFunc: func(updateSerial uint64, newRoutes []*route.Route) error { - return nil + UpdateRoutesFunc: func(updateSerial uint64, newRoutes []*route.Route) (map[route.ID]*route.Route, route.HAMap, error) { + return nil, nil, nil }, } @@ -1198,7 +1196,7 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri } s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp)) - store, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), testFile, config.Datadir) + store, cleanUp, err := server.NewTestStoreFromSQL(context.Background(), testFile, config.Datadir) if err != nil { return nil, "", err } @@ -1220,7 +1218,7 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri } secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay) - mgmtServer, err := server.NewServer(context.Background(), config, accountManager, settings.NewManager(store), peersUpdateManager, secretsManager, nil, nil) + mgmtServer, err := server.NewServer(context.Background(), config, accountManager, peersUpdateManager, secretsManager, nil, nil) if err != nil { return nil, "", err } @@ -1239,8 +1237,7 @@ func getConnectedPeers(e *Engine) int { e.syncMsgMux.Lock() defer e.syncMsgMux.Unlock() i := 0 - for _, id := range e.peerStore.PeersPubKey() { - conn, _ := e.peerStore.PeerConn(id) + for _, conn := range e.peerConns { if conn.Status() == peer.StatusConnected { i++ } @@ -1252,5 +1249,5 @@ func getPeers(e *Engine) int { e.syncMsgMux.Lock() defer e.syncMsgMux.Unlock() - return len(e.peerStore.PeersPubKey()) + return len(e.peerConns) } diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index b8cb2582f..5c2e2cb60 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -747,11 +747,6 @@ func (conn *Conn) setRelayedProxy(proxy wgproxy.Proxy) { conn.wgProxyRelay = proxy } -// AllowedIP returns the allowed IP of the remote peer -func (conn *Conn) AllowedIP() net.IP { - return conn.allowedIP -} - func isController(config ConnConfig) bool { return config.LocalKey > config.Key } diff --git a/client/internal/peer/status.go b/client/internal/peer/status.go index dc461257a..74e2ee82c 100644 --- a/client/internal/peer/status.go +++ b/client/internal/peer/status.go @@ -17,11 +17,6 @@ import ( relayClient "github.com/netbirdio/netbird/relay/client" ) -type ResolvedDomainInfo struct { - Prefixes []netip.Prefix - ParentDomain domain.Domain -} - // State contains the latest state of a peer type State struct { Mux *sync.RWMutex @@ -143,7 +138,7 @@ type Status struct { rosenpassEnabled bool rosenpassPermissive bool nsGroupStates []NSGroupState - resolvedDomainsStates map[domain.Domain]ResolvedDomainInfo + resolvedDomainsStates map[domain.Domain][]netip.Prefix // To reduce the number of notification invocation this bool will be true when need to call the notification // Some Peer actions mostly used by in a batch when the network map has been synchronized. In these type of events @@ -161,7 +156,7 @@ func NewRecorder(mgmAddress string) *Status { offlinePeers: make([]State, 0), notifier: newNotifier(), mgmAddress: mgmAddress, - resolvedDomainsStates: map[domain.Domain]ResolvedDomainInfo{}, + resolvedDomainsStates: make(map[domain.Domain][]netip.Prefix), } } @@ -596,27 +591,16 @@ func (d *Status) UpdateDNSStates(dnsStates []NSGroupState) { d.nsGroupStates = dnsStates } -func (d *Status) UpdateResolvedDomainsStates(originalDomain domain.Domain, resolvedDomain domain.Domain, prefixes []netip.Prefix) { +func (d *Status) UpdateResolvedDomainsStates(domain domain.Domain, prefixes []netip.Prefix) { d.mux.Lock() defer d.mux.Unlock() - - // Store both the original domain pattern and resolved domain - d.resolvedDomainsStates[resolvedDomain] = ResolvedDomainInfo{ - Prefixes: prefixes, - ParentDomain: originalDomain, - } + d.resolvedDomainsStates[domain] = prefixes } func (d *Status) DeleteResolvedDomainsStates(domain domain.Domain) { d.mux.Lock() defer d.mux.Unlock() - - // Remove all entries that have this domain as their parent - for k, v := range d.resolvedDomainsStates { - if v.ParentDomain == domain { - delete(d.resolvedDomainsStates, k) - } - } + delete(d.resolvedDomainsStates, domain) } func (d *Status) GetRosenpassState() RosenpassState { @@ -718,7 +702,7 @@ func (d *Status) GetDNSStates() []NSGroupState { return d.nsGroupStates } -func (d *Status) GetResolvedDomainsStates() map[domain.Domain]ResolvedDomainInfo { +func (d *Status) GetResolvedDomainsStates() map[domain.Domain][]netip.Prefix { d.mux.Lock() defer d.mux.Unlock() return maps.Clone(d.resolvedDomainsStates) diff --git a/client/internal/peerstore/store.go b/client/internal/peerstore/store.go deleted file mode 100644 index 6b3385ff5..000000000 --- a/client/internal/peerstore/store.go +++ /dev/null @@ -1,87 +0,0 @@ -package peerstore - -import ( - "net" - "sync" - - "golang.org/x/exp/maps" - - "github.com/netbirdio/netbird/client/internal/peer" -) - -// Store is a thread-safe store for peer connections. -type Store struct { - peerConns map[string]*peer.Conn - peerConnsMu sync.RWMutex -} - -func NewConnStore() *Store { - return &Store{ - peerConns: make(map[string]*peer.Conn), - } -} - -func (s *Store) AddPeerConn(pubKey string, conn *peer.Conn) bool { - s.peerConnsMu.Lock() - defer s.peerConnsMu.Unlock() - - _, ok := s.peerConns[pubKey] - if ok { - return false - } - - s.peerConns[pubKey] = conn - return true -} - -func (s *Store) Remove(pubKey string) (*peer.Conn, bool) { - s.peerConnsMu.Lock() - defer s.peerConnsMu.Unlock() - - p, ok := s.peerConns[pubKey] - if !ok { - return nil, false - } - delete(s.peerConns, pubKey) - return p, true -} - -func (s *Store) AllowedIPs(pubKey string) (string, bool) { - s.peerConnsMu.RLock() - defer s.peerConnsMu.RUnlock() - - p, ok := s.peerConns[pubKey] - if !ok { - return "", false - } - return p.WgConfig().AllowedIps, true -} - -func (s *Store) AllowedIP(pubKey string) (net.IP, bool) { - s.peerConnsMu.RLock() - defer s.peerConnsMu.RUnlock() - - p, ok := s.peerConns[pubKey] - if !ok { - return nil, false - } - return p.AllowedIP(), true -} - -func (s *Store) PeerConn(pubKey string) (*peer.Conn, bool) { - s.peerConnsMu.RLock() - defer s.peerConnsMu.RUnlock() - - p, ok := s.peerConns[pubKey] - if !ok { - return nil, false - } - return p, true -} - -func (s *Store) PeersPubKey() []string { - s.peerConnsMu.RLock() - defer s.peerConnsMu.RUnlock() - - return maps.Keys(s.peerConns) -} diff --git a/client/internal/routemanager/client.go b/client/internal/routemanager/client.go index 73f552aab..13e45b3a3 100644 --- a/client/internal/routemanager/client.go +++ b/client/internal/routemanager/client.go @@ -13,20 +13,12 @@ import ( "github.com/netbirdio/netbird/client/iface" nbdns "github.com/netbirdio/netbird/client/internal/dns" "github.com/netbirdio/netbird/client/internal/peer" - "github.com/netbirdio/netbird/client/internal/peerstore" - "github.com/netbirdio/netbird/client/internal/routemanager/dnsinterceptor" "github.com/netbirdio/netbird/client/internal/routemanager/dynamic" "github.com/netbirdio/netbird/client/internal/routemanager/refcounter" "github.com/netbirdio/netbird/client/internal/routemanager/static" "github.com/netbirdio/netbird/route" ) -const ( - handlerTypeDynamic = iota - handlerTypeDomain - handlerTypeStatic -) - type routerPeerStatus struct { connected bool relayed bool @@ -61,18 +53,7 @@ type clientNetwork struct { updateSerial uint64 } -func newClientNetworkWatcher( - ctx context.Context, - dnsRouteInterval time.Duration, - wgInterface iface.IWGIface, - statusRecorder *peer.Status, - rt *route.Route, - routeRefCounter *refcounter.RouteRefCounter, - allowedIPsRefCounter *refcounter.AllowedIPsRefCounter, - dnsServer nbdns.Server, - peerStore *peerstore.Store, - useNewDNSRoute bool, -) *clientNetwork { +func newClientNetworkWatcher(ctx context.Context, dnsRouteInterval time.Duration, wgInterface iface.IWGIface, statusRecorder *peer.Status, rt *route.Route, routeRefCounter *refcounter.RouteRefCounter, allowedIPsRefCounter *refcounter.AllowedIPsRefCounter) *clientNetwork { ctx, cancel := context.WithCancel(ctx) client := &clientNetwork{ @@ -84,17 +65,7 @@ func newClientNetworkWatcher( routePeersNotifiers: make(map[string]chan struct{}), routeUpdate: make(chan routesUpdate), peerStateUpdate: make(chan struct{}), - handler: handlerFromRoute( - rt, - routeRefCounter, - allowedIPsRefCounter, - dnsRouteInterval, - statusRecorder, - wgInterface, - dnsServer, - peerStore, - useNewDNSRoute, - ), + handler: handlerFromRoute(rt, routeRefCounter, allowedIPsRefCounter, dnsRouteInterval, statusRecorder, wgInterface), } return client } @@ -397,50 +368,10 @@ func (c *clientNetwork) peersStateAndUpdateWatcher() { } } -func handlerFromRoute( - rt *route.Route, - routeRefCounter *refcounter.RouteRefCounter, - allowedIPsRefCounter *refcounter.AllowedIPsRefCounter, - dnsRouterInteval time.Duration, - statusRecorder *peer.Status, - wgInterface iface.IWGIface, - dnsServer nbdns.Server, - peerStore *peerstore.Store, - useNewDNSRoute bool, -) RouteHandler { - switch handlerType(rt, useNewDNSRoute) { - case handlerTypeDomain: - return dnsinterceptor.New( - rt, - routeRefCounter, - allowedIPsRefCounter, - statusRecorder, - dnsServer, - peerStore, - ) - case handlerTypeDynamic: +func handlerFromRoute(rt *route.Route, routeRefCounter *refcounter.RouteRefCounter, allowedIPsRefCounter *refcounter.AllowedIPsRefCounter, dnsRouterInteval time.Duration, statusRecorder *peer.Status, wgInterface iface.IWGIface) RouteHandler { + if rt.IsDynamic() { dns := nbdns.NewServiceViaMemory(wgInterface) - return dynamic.NewRoute( - rt, - routeRefCounter, - allowedIPsRefCounter, - dnsRouterInteval, - statusRecorder, - wgInterface, - fmt.Sprintf("%s:%d", dns.RuntimeIP(), dns.RuntimePort()), - ) - default: - return static.NewRoute(rt, routeRefCounter, allowedIPsRefCounter) + return dynamic.NewRoute(rt, routeRefCounter, allowedIPsRefCounter, dnsRouterInteval, statusRecorder, wgInterface, fmt.Sprintf("%s:%d", dns.RuntimeIP(), dns.RuntimePort())) } -} - -func handlerType(rt *route.Route, useNewDNSRoute bool) int { - if !rt.IsDynamic() { - return handlerTypeStatic - } - - if useNewDNSRoute { - return handlerTypeDomain - } - return handlerTypeDynamic + return static.NewRoute(rt, routeRefCounter, allowedIPsRefCounter) } diff --git a/client/internal/routemanager/dnsinterceptor/handler.go b/client/internal/routemanager/dnsinterceptor/handler.go deleted file mode 100644 index 10cb03f1d..000000000 --- a/client/internal/routemanager/dnsinterceptor/handler.go +++ /dev/null @@ -1,356 +0,0 @@ -package dnsinterceptor - -import ( - "context" - "fmt" - "net" - "net/netip" - "strings" - "sync" - "time" - - "github.com/hashicorp/go-multierror" - "github.com/miekg/dns" - log "github.com/sirupsen/logrus" - - nberrors "github.com/netbirdio/netbird/client/errors" - nbdns "github.com/netbirdio/netbird/client/internal/dns" - "github.com/netbirdio/netbird/client/internal/dnsfwd" - "github.com/netbirdio/netbird/client/internal/peer" - "github.com/netbirdio/netbird/client/internal/peerstore" - "github.com/netbirdio/netbird/client/internal/routemanager/refcounter" - "github.com/netbirdio/netbird/management/domain" - "github.com/netbirdio/netbird/route" -) - -type domainMap map[domain.Domain][]netip.Prefix - -type DnsInterceptor struct { - mu sync.RWMutex - route *route.Route - routeRefCounter *refcounter.RouteRefCounter - allowedIPsRefcounter *refcounter.AllowedIPsRefCounter - statusRecorder *peer.Status - dnsServer nbdns.Server - currentPeerKey string - interceptedDomains domainMap - peerStore *peerstore.Store -} - -func New( - rt *route.Route, - routeRefCounter *refcounter.RouteRefCounter, - allowedIPsRefCounter *refcounter.AllowedIPsRefCounter, - statusRecorder *peer.Status, - dnsServer nbdns.Server, - peerStore *peerstore.Store, -) *DnsInterceptor { - return &DnsInterceptor{ - route: rt, - routeRefCounter: routeRefCounter, - allowedIPsRefcounter: allowedIPsRefCounter, - statusRecorder: statusRecorder, - dnsServer: dnsServer, - interceptedDomains: make(domainMap), - peerStore: peerStore, - } -} - -func (d *DnsInterceptor) String() string { - return d.route.Domains.SafeString() -} - -func (d *DnsInterceptor) AddRoute(context.Context) error { - d.dnsServer.RegisterHandler(d.route.Domains.ToPunycodeList(), d, nbdns.PriorityDNSRoute) - return nil -} - -func (d *DnsInterceptor) RemoveRoute() error { - d.mu.Lock() - - var merr *multierror.Error - for domain, prefixes := range d.interceptedDomains { - for _, prefix := range prefixes { - if _, err := d.routeRefCounter.Decrement(prefix); err != nil { - merr = multierror.Append(merr, fmt.Errorf("remove dynamic route for IP %s: %v", prefix, err)) - } - if d.currentPeerKey != "" { - if _, err := d.allowedIPsRefcounter.Decrement(prefix); err != nil { - merr = multierror.Append(merr, fmt.Errorf("remove allowed IP %s: %v", prefix, err)) - } - } - } - log.Debugf("removed dynamic route(s) for [%s]: %s", domain.SafeString(), strings.ReplaceAll(fmt.Sprintf("%s", prefixes), " ", ", ")) - - } - for _, domain := range d.route.Domains { - d.statusRecorder.DeleteResolvedDomainsStates(domain) - } - - clear(d.interceptedDomains) - d.mu.Unlock() - - d.dnsServer.DeregisterHandler(d.route.Domains.ToPunycodeList(), nbdns.PriorityDNSRoute) - - return nberrors.FormatErrorOrNil(merr) -} - -func (d *DnsInterceptor) AddAllowedIPs(peerKey string) error { - d.mu.Lock() - defer d.mu.Unlock() - - var merr *multierror.Error - for domain, prefixes := range d.interceptedDomains { - for _, prefix := range prefixes { - if ref, err := d.allowedIPsRefcounter.Increment(prefix, peerKey); err != nil { - merr = multierror.Append(merr, fmt.Errorf("add allowed IP %s: %v", prefix, err)) - } else if ref.Count > 1 && ref.Out != peerKey { - log.Warnf("IP [%s] for domain [%s] is already routed by peer [%s]. HA routing disabled", - prefix.Addr(), - domain.SafeString(), - ref.Out, - ) - } - } - } - - d.currentPeerKey = peerKey - return nberrors.FormatErrorOrNil(merr) -} - -func (d *DnsInterceptor) RemoveAllowedIPs() error { - d.mu.Lock() - defer d.mu.Unlock() - - var merr *multierror.Error - for _, prefixes := range d.interceptedDomains { - for _, prefix := range prefixes { - if _, err := d.allowedIPsRefcounter.Decrement(prefix); err != nil { - merr = multierror.Append(merr, fmt.Errorf("remove allowed IP %s: %v", prefix, err)) - } - } - } - - d.currentPeerKey = "" - return nberrors.FormatErrorOrNil(merr) -} - -// ServeDNS implements the dns.Handler interface -func (d *DnsInterceptor) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { - if len(r.Question) == 0 { - return - } - log.Tracef("received DNS request for domain=%s type=%v class=%v", - r.Question[0].Name, r.Question[0].Qtype, r.Question[0].Qclass) - - d.mu.RLock() - peerKey := d.currentPeerKey - d.mu.RUnlock() - - if peerKey == "" { - log.Tracef("no current peer key set, letting next handler try for domain=%s", r.Question[0].Name) - - d.continueToNextHandler(w, r, "no current peer key") - return - } - - upstreamIP, err := d.getUpstreamIP(peerKey) - if err != nil { - log.Errorf("failed to get upstream IP: %v", err) - d.continueToNextHandler(w, r, fmt.Sprintf("failed to get upstream IP: %v", err)) - return - } - - client := &dns.Client{ - Timeout: 5 * time.Second, - Net: "udp", - } - upstream := fmt.Sprintf("%s:%d", upstreamIP, dnsfwd.ListenPort) - reply, _, err := client.ExchangeContext(context.Background(), r, upstream) - - var answer []dns.RR - if reply != nil { - answer = reply.Answer - } - log.Tracef("upstream %s (%s) DNS response for domain=%s answers=%v", upstreamIP, peerKey, r.Question[0].Name, answer) - - if err != nil { - log.Errorf("failed to exchange DNS request with %s: %v", upstream, err) - if err := w.WriteMsg(&dns.Msg{MsgHdr: dns.MsgHdr{Rcode: dns.RcodeServerFailure, Id: r.Id}}); err != nil { - log.Errorf("failed writing DNS response: %v", err) - } - return - } - - reply.Id = r.Id - if err := d.writeMsg(w, reply); err != nil { - log.Errorf("failed writing DNS response: %v", err) - } -} - -// continueToNextHandler signals the handler chain to try the next handler -func (d *DnsInterceptor) continueToNextHandler(w dns.ResponseWriter, r *dns.Msg, reason string) { - log.Tracef("continuing to next handler for domain=%s reason=%s", r.Question[0].Name, reason) - - resp := new(dns.Msg) - resp.SetRcode(r, dns.RcodeNameError) - // Set Zero bit to signal handler chain to continue - resp.MsgHdr.Zero = true - if err := w.WriteMsg(resp); err != nil { - log.Errorf("failed writing DNS continue response: %v", err) - } -} - -func (d *DnsInterceptor) getUpstreamIP(peerKey string) (net.IP, error) { - peerAllowedIP, exists := d.peerStore.AllowedIP(peerKey) - if !exists { - return nil, fmt.Errorf("peer connection not found for key: %s", peerKey) - } - return peerAllowedIP, nil -} - -func (d *DnsInterceptor) writeMsg(w dns.ResponseWriter, r *dns.Msg) error { - if r == nil { - return fmt.Errorf("received nil DNS message") - } - - if len(r.Answer) > 0 && len(r.Question) > 0 { - origPattern := "" - if writer, ok := w.(*nbdns.ResponseWriterChain); ok { - origPattern = writer.GetOrigPattern() - } - - resolvedDomain := domain.Domain(r.Question[0].Name) - - // already punycode via RegisterHandler() - originalDomain := domain.Domain(origPattern) - if originalDomain == "" { - originalDomain = resolvedDomain - } - - var newPrefixes []netip.Prefix - for _, answer := range r.Answer { - var ip netip.Addr - switch rr := answer.(type) { - case *dns.A: - addr, ok := netip.AddrFromSlice(rr.A) - if !ok { - log.Tracef("failed to convert A record for domain=%s ip=%v", resolvedDomain, rr.A) - continue - } - ip = addr - case *dns.AAAA: - addr, ok := netip.AddrFromSlice(rr.AAAA) - if !ok { - log.Tracef("failed to convert AAAA record for domain=%s ip=%v", resolvedDomain, rr.AAAA) - continue - } - ip = addr - default: - continue - } - - prefix := netip.PrefixFrom(ip, ip.BitLen()) - newPrefixes = append(newPrefixes, prefix) - } - - if len(newPrefixes) > 0 { - if err := d.updateDomainPrefixes(resolvedDomain, originalDomain, newPrefixes); err != nil { - log.Errorf("failed to update domain prefixes: %v", err) - } - } - } - - if err := w.WriteMsg(r); err != nil { - return fmt.Errorf("failed to write DNS response: %v", err) - } - - return nil -} - -func (d *DnsInterceptor) updateDomainPrefixes(resolvedDomain, originalDomain domain.Domain, newPrefixes []netip.Prefix) error { - d.mu.Lock() - defer d.mu.Unlock() - - oldPrefixes := d.interceptedDomains[resolvedDomain] - toAdd, toRemove := determinePrefixChanges(oldPrefixes, newPrefixes) - - var merr *multierror.Error - - // Add new prefixes - for _, prefix := range toAdd { - if _, err := d.routeRefCounter.Increment(prefix, struct{}{}); err != nil { - merr = multierror.Append(merr, fmt.Errorf("add route for IP %s: %v", prefix, err)) - continue - } - - if d.currentPeerKey == "" { - continue - } - if ref, err := d.allowedIPsRefcounter.Increment(prefix, d.currentPeerKey); err != nil { - merr = multierror.Append(merr, fmt.Errorf("add allowed IP %s: %v", prefix, err)) - } else if ref.Count > 1 && ref.Out != d.currentPeerKey { - log.Warnf("IP [%s] for domain [%s] is already routed by peer [%s]. HA routing disabled", - prefix.Addr(), - resolvedDomain.SafeString(), - ref.Out, - ) - } - } - - if !d.route.KeepRoute { - // Remove old prefixes - for _, prefix := range toRemove { - if _, err := d.routeRefCounter.Decrement(prefix); err != nil { - merr = multierror.Append(merr, fmt.Errorf("remove route for IP %s: %v", prefix, err)) - } - if d.currentPeerKey != "" { - if _, err := d.allowedIPsRefcounter.Decrement(prefix); err != nil { - merr = multierror.Append(merr, fmt.Errorf("remove allowed IP %s: %v", prefix, err)) - } - } - } - } - - // Update domain prefixes using resolved domain as key - if len(toAdd) > 0 || len(toRemove) > 0 { - d.interceptedDomains[resolvedDomain] = newPrefixes - originalDomain = domain.Domain(strings.TrimSuffix(string(originalDomain), ".")) - d.statusRecorder.UpdateResolvedDomainsStates(originalDomain, resolvedDomain, newPrefixes) - - if len(toAdd) > 0 { - log.Debugf("added dynamic route(s) for domain=%s (pattern: domain=%s): %s", - resolvedDomain.SafeString(), - originalDomain.SafeString(), - toAdd) - } - if len(toRemove) > 0 { - log.Debugf("removed dynamic route(s) for domain=%s (pattern: domain=%s): %s", - resolvedDomain.SafeString(), - originalDomain.SafeString(), - toRemove) - } - } - - return nberrors.FormatErrorOrNil(merr) -} - -func determinePrefixChanges(oldPrefixes, newPrefixes []netip.Prefix) (toAdd, toRemove []netip.Prefix) { - prefixSet := make(map[netip.Prefix]bool) - for _, prefix := range oldPrefixes { - prefixSet[prefix] = false - } - for _, prefix := range newPrefixes { - if _, exists := prefixSet[prefix]; exists { - prefixSet[prefix] = true - } else { - toAdd = append(toAdd, prefix) - } - } - for prefix, inUse := range prefixSet { - if !inUse { - toRemove = append(toRemove, prefix) - } - } - return -} diff --git a/client/internal/routemanager/dynamic/route.go b/client/internal/routemanager/dynamic/route.go index a0fff7713..ac94d4a5c 100644 --- a/client/internal/routemanager/dynamic/route.go +++ b/client/internal/routemanager/dynamic/route.go @@ -74,7 +74,11 @@ func NewRoute( } func (r *Route) String() string { - return r.route.Domains.SafeString() + s, err := r.route.Domains.String() + if err != nil { + return r.route.Domains.PunycodeString() + } + return s } func (r *Route) AddRoute(ctx context.Context) error { @@ -288,7 +292,7 @@ func (r *Route) updateDynamicRoutes(ctx context.Context, newDomains domainMap) e updatedPrefixes := combinePrefixes(oldPrefixes, removedPrefixes, addedPrefixes) r.dynamicDomains[domain] = updatedPrefixes - r.statusRecorder.UpdateResolvedDomainsStates(domain, domain, updatedPrefixes) + r.statusRecorder.UpdateResolvedDomainsStates(domain, updatedPrefixes) } return nberrors.FormatErrorOrNil(merr) diff --git a/client/internal/routemanager/manager.go b/client/internal/routemanager/manager.go index 389e97e2d..8bf3a91b0 100644 --- a/client/internal/routemanager/manager.go +++ b/client/internal/routemanager/manager.go @@ -12,15 +12,12 @@ import ( "time" log "github.com/sirupsen/logrus" - "golang.org/x/exp/maps" firewall "github.com/netbirdio/netbird/client/firewall/manager" "github.com/netbirdio/netbird/client/iface" "github.com/netbirdio/netbird/client/iface/configurer" - "github.com/netbirdio/netbird/client/internal/dns" "github.com/netbirdio/netbird/client/internal/listener" "github.com/netbirdio/netbird/client/internal/peer" - "github.com/netbirdio/netbird/client/internal/peerstore" "github.com/netbirdio/netbird/client/internal/routemanager/notifier" "github.com/netbirdio/netbird/client/internal/routemanager/refcounter" "github.com/netbirdio/netbird/client/internal/routemanager/systemops" @@ -36,11 +33,9 @@ import ( // Manager is a route manager interface type Manager interface { Init() (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) - UpdateRoutes(updateSerial uint64, newRoutes []*route.Route, useNewDNSRoute bool) error + UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) (map[route.ID]*route.Route, route.HAMap, error) TriggerSelection(route.HAMap) GetRouteSelector() *routeselector.RouteSelector - GetClientRoutes() route.HAMap - GetClientRoutesWithNetID() map[route.NetID][]*route.Route SetRouteChangeListener(listener listener.NetworkChangeListener) InitialRouteRange() []string EnableServerRouter(firewall firewall.Manager) error @@ -65,11 +60,6 @@ type DefaultManager struct { allowedIPsRefCounter *refcounter.AllowedIPsRefCounter dnsRouteInterval time.Duration stateManager *statemanager.Manager - // clientRoutes is the most recent list of clientRoutes received from the Management Service - clientRoutes route.HAMap - dnsServer dns.Server - peerStore *peerstore.Store - useNewDNSRoute bool } func NewManager( @@ -81,8 +71,6 @@ func NewManager( relayMgr *relayClient.Manager, initialRoutes []*route.Route, stateManager *statemanager.Manager, - dnsServer dns.Server, - peerStore *peerstore.Store, ) *DefaultManager { mCTX, cancel := context.WithCancel(ctx) notifier := notifier.NewNotifier() @@ -100,8 +88,6 @@ func NewManager( pubKey: pubKey, notifier: notifier, stateManager: stateManager, - dnsServer: dnsServer, - peerStore: peerStore, } dm.routeRefCounter = refcounter.New( @@ -130,7 +116,7 @@ func NewManager( ) if runtime.GOOS == "android" { - cr := dm.initialClientRoutes(initialRoutes) + cr := dm.clientRoutes(initialRoutes) dm.notifier.SetInitialClientRoutes(cr) } return dm @@ -221,41 +207,33 @@ func (m *DefaultManager) Stop(stateManager *statemanager.Manager) { } m.ctx = nil - - m.mux.Lock() - defer m.mux.Unlock() - m.clientRoutes = nil } // UpdateRoutes compares received routes with existing routes and removes, updates or adds them to the client and server maps -func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route, useNewDNSRoute bool) error { +func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) (map[route.ID]*route.Route, route.HAMap, error) { select { case <-m.ctx.Done(): log.Infof("not updating routes as context is closed") - return nil + return nil, nil, m.ctx.Err() default: - } + m.mux.Lock() + defer m.mux.Unlock() - m.mux.Lock() - defer m.mux.Unlock() - m.useNewDNSRoute = useNewDNSRoute + newServerRoutesMap, newClientRoutesIDMap := m.classifyRoutes(newRoutes) - newServerRoutesMap, newClientRoutesIDMap := m.classifyRoutes(newRoutes) + filteredClientRoutes := m.routeSelector.FilterSelected(newClientRoutesIDMap) + m.updateClientNetworks(updateSerial, filteredClientRoutes) + m.notifier.OnNewRoutes(filteredClientRoutes) - filteredClientRoutes := m.routeSelector.FilterSelected(newClientRoutesIDMap) - m.updateClientNetworks(updateSerial, filteredClientRoutes) - m.notifier.OnNewRoutes(filteredClientRoutes) - - if m.serverRouter != nil { - err := m.serverRouter.updateRoutes(newServerRoutesMap) - if err != nil { - return err + if m.serverRouter != nil { + err := m.serverRouter.updateRoutes(newServerRoutesMap) + if err != nil { + return nil, nil, fmt.Errorf("update routes: %w", err) + } } + + return newServerRoutesMap, newClientRoutesIDMap, nil } - - m.clientRoutes = newClientRoutesIDMap - - return nil } // SetRouteChangeListener set RouteListener for route change Notifier @@ -273,24 +251,9 @@ func (m *DefaultManager) GetRouteSelector() *routeselector.RouteSelector { return m.routeSelector } -// GetClientRoutes returns most recent list of clientRoutes received from the Management Service -func (m *DefaultManager) GetClientRoutes() route.HAMap { - m.mux.Lock() - defer m.mux.Unlock() - - return maps.Clone(m.clientRoutes) -} - -// GetClientRoutesWithNetID returns the current routes from the route map, but the keys consist of the network ID only -func (m *DefaultManager) GetClientRoutesWithNetID() map[route.NetID][]*route.Route { - m.mux.Lock() - defer m.mux.Unlock() - - routes := make(map[route.NetID][]*route.Route, len(m.clientRoutes)) - for id, v := range m.clientRoutes { - routes[id.NetID()] = v - } - return routes +// GetClientRoutes returns the client routes +func (m *DefaultManager) GetClientRoutes() map[route.HAUniqueID]*clientNetwork { + return m.clientNetworks } // TriggerSelection triggers the selection of routes, stopping deselected watchers and starting newly selected ones @@ -310,18 +273,7 @@ func (m *DefaultManager) TriggerSelection(networks route.HAMap) { continue } - clientNetworkWatcher := newClientNetworkWatcher( - m.ctx, - m.dnsRouteInterval, - m.wgInterface, - m.statusRecorder, - routes[0], - m.routeRefCounter, - m.allowedIPsRefCounter, - m.dnsServer, - m.peerStore, - m.useNewDNSRoute, - ) + clientNetworkWatcher := newClientNetworkWatcher(m.ctx, m.dnsRouteInterval, m.wgInterface, m.statusRecorder, routes[0], m.routeRefCounter, m.allowedIPsRefCounter) m.clientNetworks[id] = clientNetworkWatcher go clientNetworkWatcher.peersStateAndUpdateWatcher() clientNetworkWatcher.sendUpdateToClientNetworkWatcher(routesUpdate{routes: routes}) @@ -350,18 +302,7 @@ func (m *DefaultManager) updateClientNetworks(updateSerial uint64, networks rout for id, routes := range networks { clientNetworkWatcher, found := m.clientNetworks[id] if !found { - clientNetworkWatcher = newClientNetworkWatcher( - m.ctx, - m.dnsRouteInterval, - m.wgInterface, - m.statusRecorder, - routes[0], - m.routeRefCounter, - m.allowedIPsRefCounter, - m.dnsServer, - m.peerStore, - m.useNewDNSRoute, - ) + clientNetworkWatcher = newClientNetworkWatcher(m.ctx, m.dnsRouteInterval, m.wgInterface, m.statusRecorder, routes[0], m.routeRefCounter, m.allowedIPsRefCounter) m.clientNetworks[id] = clientNetworkWatcher go clientNetworkWatcher.peersStateAndUpdateWatcher() } @@ -404,7 +345,7 @@ func (m *DefaultManager) classifyRoutes(newRoutes []*route.Route) (map[route.ID] return newServerRoutesMap, newClientRoutesIDMap } -func (m *DefaultManager) initialClientRoutes(initialRoutes []*route.Route) []*route.Route { +func (m *DefaultManager) clientRoutes(initialRoutes []*route.Route) []*route.Route { _, crMap := m.classifyRoutes(initialRoutes) rs := make([]*route.Route, 0, len(crMap)) for _, routes := range crMap { diff --git a/client/internal/routemanager/manager_test.go b/client/internal/routemanager/manager_test.go index 4b7c984e5..07dac21b8 100644 --- a/client/internal/routemanager/manager_test.go +++ b/client/internal/routemanager/manager_test.go @@ -424,7 +424,7 @@ func TestManagerUpdateRoutes(t *testing.T) { statusRecorder := peer.NewRecorder("https://mgm") ctx := context.TODO() - routeManager := NewManager(ctx, localPeerKey, 0, wgInterface, statusRecorder, nil, nil, nil, nil, nil) + routeManager := NewManager(ctx, localPeerKey, 0, wgInterface, statusRecorder, nil, nil, nil) _, _, err = routeManager.Init() @@ -436,11 +436,11 @@ func TestManagerUpdateRoutes(t *testing.T) { } if len(testCase.inputInitRoutes) > 0 { - _ = routeManager.UpdateRoutes(testCase.inputSerial, testCase.inputRoutes, false) + _, _, err = routeManager.UpdateRoutes(testCase.inputSerial, testCase.inputRoutes) require.NoError(t, err, "should update routes with init routes") } - _ = routeManager.UpdateRoutes(testCase.inputSerial+uint64(len(testCase.inputInitRoutes)), testCase.inputRoutes, false) + _, _, err = routeManager.UpdateRoutes(testCase.inputSerial+uint64(len(testCase.inputInitRoutes)), testCase.inputRoutes) require.NoError(t, err, "should update routes") expectedWatchers := testCase.clientNetworkWatchersExpected diff --git a/client/internal/routemanager/mock.go b/client/internal/routemanager/mock.go index 64fdffceb..556a62351 100644 --- a/client/internal/routemanager/mock.go +++ b/client/internal/routemanager/mock.go @@ -2,6 +2,7 @@ package routemanager import ( "context" + "fmt" firewall "github.com/netbirdio/netbird/client/firewall/manager" "github.com/netbirdio/netbird/client/iface" @@ -14,12 +15,10 @@ import ( // MockManager is the mock instance of a route manager type MockManager struct { - UpdateRoutesFunc func(updateSerial uint64, newRoutes []*route.Route) error - TriggerSelectionFunc func(haMap route.HAMap) - GetRouteSelectorFunc func() *routeselector.RouteSelector - GetClientRoutesFunc func() route.HAMap - GetClientRoutesWithNetIDFunc func() map[route.NetID][]*route.Route - StopFunc func(manager *statemanager.Manager) + UpdateRoutesFunc func(updateSerial uint64, newRoutes []*route.Route) (map[route.ID]*route.Route, route.HAMap, error) + TriggerSelectionFunc func(haMap route.HAMap) + GetRouteSelectorFunc func() *routeselector.RouteSelector + StopFunc func(manager *statemanager.Manager) } func (m *MockManager) Init() (net.AddHookFunc, net.RemoveHookFunc, error) { @@ -32,11 +31,11 @@ func (m *MockManager) InitialRouteRange() []string { } // UpdateRoutes mock implementation of UpdateRoutes from Manager interface -func (m *MockManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route, b bool) error { +func (m *MockManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Route) (map[route.ID]*route.Route, route.HAMap, error) { if m.UpdateRoutesFunc != nil { return m.UpdateRoutesFunc(updateSerial, newRoutes) } - return nil + return nil, nil, fmt.Errorf("method UpdateRoutes is not implemented") } func (m *MockManager) TriggerSelection(networks route.HAMap) { @@ -53,22 +52,6 @@ func (m *MockManager) GetRouteSelector() *routeselector.RouteSelector { return nil } -// GetClientRoutes mock implementation of GetClientRoutes from Manager interface -func (m *MockManager) GetClientRoutes() route.HAMap { - if m.GetClientRoutesFunc != nil { - return m.GetClientRoutesFunc() - } - return nil -} - -// GetClientRoutesWithNetID mock implementation of GetClientRoutesWithNetID from Manager interface -func (m *MockManager) GetClientRoutesWithNetID() map[route.NetID][]*route.Route { - if m.GetClientRoutesWithNetIDFunc != nil { - return m.GetClientRoutesWithNetIDFunc() - } - return nil -} - // Start mock implementation of Start from Manager interface func (m *MockManager) Start(ctx context.Context, iface *iface.WGIface) { } diff --git a/client/ios/NetBirdSDK/client.go b/client/ios/NetBirdSDK/client.go index befce56a2..6f501e0c6 100644 --- a/client/ios/NetBirdSDK/client.go +++ b/client/ios/NetBirdSDK/client.go @@ -272,8 +272,8 @@ func (c *Client) GetRoutesSelectionDetails() (*RoutesSelectionDetails, error) { return nil, fmt.Errorf("not connected") } + routesMap := engine.GetClientRoutesWithNetID() routeManager := engine.GetRouteManager() - routesMap := routeManager.GetClientRoutesWithNetID() if routeManager == nil { return nil, fmt.Errorf("could not get route manager") } @@ -317,7 +317,7 @@ func (c *Client) GetRoutesSelectionDetails() (*RoutesSelectionDetails, error) { } -func prepareRouteSelectionDetails(routes []*selectRoute, resolvedDomains map[domain.Domain]peer.ResolvedDomainInfo) *RoutesSelectionDetails { +func prepareRouteSelectionDetails(routes []*selectRoute, resolvedDomains map[domain.Domain][]netip.Prefix) *RoutesSelectionDetails { var routeSelection []RoutesSelectionInfo for _, r := range routes { domainList := make([]DomainInfo, 0) @@ -325,10 +325,9 @@ func prepareRouteSelectionDetails(routes []*selectRoute, resolvedDomains map[dom domainResp := DomainInfo{ Domain: d.SafeString(), } - - if info, exists := resolvedDomains[d]; exists { + if prefixes, exists := resolvedDomains[d]; exists { var ipStrings []string - for _, prefix := range info.Prefixes { + for _, prefix := range prefixes { ipStrings = append(ipStrings, prefix.Addr().String()) } domainResp.ResolvedIPs = strings.Join(ipStrings, ", ") @@ -366,12 +365,12 @@ func (c *Client) SelectRoute(id string) error { } else { log.Debugf("select route with id: %s", id) routes := toNetIDs([]string{id}) - if err := routeSelector.SelectRoutes(routes, true, maps.Keys(routeManager.GetClientRoutesWithNetID())); err != nil { + if err := routeSelector.SelectRoutes(routes, true, maps.Keys(engine.GetClientRoutesWithNetID())); err != nil { log.Debugf("error when selecting routes: %s", err) return fmt.Errorf("select routes: %w", err) } } - routeManager.TriggerSelection(routeManager.GetClientRoutes()) + routeManager.TriggerSelection(engine.GetClientRoutes()) return nil } @@ -393,12 +392,12 @@ func (c *Client) DeselectRoute(id string) error { } else { log.Debugf("deselect route with id: %s", id) routes := toNetIDs([]string{id}) - if err := routeSelector.DeselectRoutes(routes, maps.Keys(routeManager.GetClientRoutesWithNetID())); err != nil { + if err := routeSelector.DeselectRoutes(routes, maps.Keys(engine.GetClientRoutesWithNetID())); err != nil { log.Debugf("error when deselecting routes: %s", err) return fmt.Errorf("deselect routes: %w", err) } } - routeManager.TriggerSelection(routeManager.GetClientRoutes()) + routeManager.TriggerSelection(engine.GetClientRoutes()) return nil } diff --git a/client/proto/daemon.pb.go b/client/proto/daemon.pb.go index f0d3021e9..98ce2c4a2 100644 --- a/client/proto/daemon.pb.go +++ b/client/proto/daemon.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.21.9 +// protoc v4.23.4 // source: daemon.proto package proto @@ -908,7 +908,7 @@ type PeerState struct { BytesRx int64 `protobuf:"varint,13,opt,name=bytesRx,proto3" json:"bytesRx,omitempty"` BytesTx int64 `protobuf:"varint,14,opt,name=bytesTx,proto3" json:"bytesTx,omitempty"` RosenpassEnabled bool `protobuf:"varint,15,opt,name=rosenpassEnabled,proto3" json:"rosenpassEnabled,omitempty"` - Networks []string `protobuf:"bytes,16,rep,name=networks,proto3" json:"networks,omitempty"` + Routes []string `protobuf:"bytes,16,rep,name=routes,proto3" json:"routes,omitempty"` Latency *durationpb.Duration `protobuf:"bytes,17,opt,name=latency,proto3" json:"latency,omitempty"` RelayAddress string `protobuf:"bytes,18,opt,name=relayAddress,proto3" json:"relayAddress,omitempty"` } @@ -1043,9 +1043,9 @@ func (x *PeerState) GetRosenpassEnabled() bool { return false } -func (x *PeerState) GetNetworks() []string { +func (x *PeerState) GetRoutes() []string { if x != nil { - return x.Networks + return x.Routes } return nil } @@ -1076,7 +1076,7 @@ type LocalPeerState struct { Fqdn string `protobuf:"bytes,4,opt,name=fqdn,proto3" json:"fqdn,omitempty"` RosenpassEnabled bool `protobuf:"varint,5,opt,name=rosenpassEnabled,proto3" json:"rosenpassEnabled,omitempty"` RosenpassPermissive bool `protobuf:"varint,6,opt,name=rosenpassPermissive,proto3" json:"rosenpassPermissive,omitempty"` - Networks []string `protobuf:"bytes,7,rep,name=networks,proto3" json:"networks,omitempty"` + Routes []string `protobuf:"bytes,7,rep,name=routes,proto3" json:"routes,omitempty"` } func (x *LocalPeerState) Reset() { @@ -1153,9 +1153,9 @@ func (x *LocalPeerState) GetRosenpassPermissive() bool { return false } -func (x *LocalPeerState) GetNetworks() []string { +func (x *LocalPeerState) GetRoutes() []string { if x != nil { - return x.Networks + return x.Routes } return nil } @@ -1511,14 +1511,14 @@ func (x *FullStatus) GetDnsServers() []*NSGroupState { return nil } -type ListNetworksRequest struct { +type ListRoutesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } -func (x *ListNetworksRequest) Reset() { - *x = ListNetworksRequest{} +func (x *ListRoutesRequest) Reset() { + *x = ListRoutesRequest{} if protoimpl.UnsafeEnabled { mi := &file_daemon_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1526,13 +1526,13 @@ func (x *ListNetworksRequest) Reset() { } } -func (x *ListNetworksRequest) String() string { +func (x *ListRoutesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListNetworksRequest) ProtoMessage() {} +func (*ListRoutesRequest) ProtoMessage() {} -func (x *ListNetworksRequest) ProtoReflect() protoreflect.Message { +func (x *ListRoutesRequest) ProtoReflect() protoreflect.Message { mi := &file_daemon_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1544,21 +1544,21 @@ func (x *ListNetworksRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListNetworksRequest.ProtoReflect.Descriptor instead. -func (*ListNetworksRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use ListRoutesRequest.ProtoReflect.Descriptor instead. +func (*ListRoutesRequest) Descriptor() ([]byte, []int) { return file_daemon_proto_rawDescGZIP(), []int{19} } -type ListNetworksResponse struct { +type ListRoutesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Routes []*Network `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"` + Routes []*Route `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"` } -func (x *ListNetworksResponse) Reset() { - *x = ListNetworksResponse{} +func (x *ListRoutesResponse) Reset() { + *x = ListRoutesResponse{} if protoimpl.UnsafeEnabled { mi := &file_daemon_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1566,13 +1566,13 @@ func (x *ListNetworksResponse) Reset() { } } -func (x *ListNetworksResponse) String() string { +func (x *ListRoutesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListNetworksResponse) ProtoMessage() {} +func (*ListRoutesResponse) ProtoMessage() {} -func (x *ListNetworksResponse) ProtoReflect() protoreflect.Message { +func (x *ListRoutesResponse) ProtoReflect() protoreflect.Message { mi := &file_daemon_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1584,30 +1584,30 @@ func (x *ListNetworksResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListNetworksResponse.ProtoReflect.Descriptor instead. -func (*ListNetworksResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ListRoutesResponse.ProtoReflect.Descriptor instead. +func (*ListRoutesResponse) Descriptor() ([]byte, []int) { return file_daemon_proto_rawDescGZIP(), []int{20} } -func (x *ListNetworksResponse) GetRoutes() []*Network { +func (x *ListRoutesResponse) GetRoutes() []*Route { if x != nil { return x.Routes } return nil } -type SelectNetworksRequest struct { +type SelectRoutesRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NetworkIDs []string `protobuf:"bytes,1,rep,name=networkIDs,proto3" json:"networkIDs,omitempty"` - Append bool `protobuf:"varint,2,opt,name=append,proto3" json:"append,omitempty"` - All bool `protobuf:"varint,3,opt,name=all,proto3" json:"all,omitempty"` + RouteIDs []string `protobuf:"bytes,1,rep,name=routeIDs,proto3" json:"routeIDs,omitempty"` + Append bool `protobuf:"varint,2,opt,name=append,proto3" json:"append,omitempty"` + All bool `protobuf:"varint,3,opt,name=all,proto3" json:"all,omitempty"` } -func (x *SelectNetworksRequest) Reset() { - *x = SelectNetworksRequest{} +func (x *SelectRoutesRequest) Reset() { + *x = SelectRoutesRequest{} if protoimpl.UnsafeEnabled { mi := &file_daemon_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1615,13 +1615,13 @@ func (x *SelectNetworksRequest) Reset() { } } -func (x *SelectNetworksRequest) String() string { +func (x *SelectRoutesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SelectNetworksRequest) ProtoMessage() {} +func (*SelectRoutesRequest) ProtoMessage() {} -func (x *SelectNetworksRequest) ProtoReflect() protoreflect.Message { +func (x *SelectRoutesRequest) ProtoReflect() protoreflect.Message { mi := &file_daemon_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1633,40 +1633,40 @@ func (x *SelectNetworksRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SelectNetworksRequest.ProtoReflect.Descriptor instead. -func (*SelectNetworksRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use SelectRoutesRequest.ProtoReflect.Descriptor instead. +func (*SelectRoutesRequest) Descriptor() ([]byte, []int) { return file_daemon_proto_rawDescGZIP(), []int{21} } -func (x *SelectNetworksRequest) GetNetworkIDs() []string { +func (x *SelectRoutesRequest) GetRouteIDs() []string { if x != nil { - return x.NetworkIDs + return x.RouteIDs } return nil } -func (x *SelectNetworksRequest) GetAppend() bool { +func (x *SelectRoutesRequest) GetAppend() bool { if x != nil { return x.Append } return false } -func (x *SelectNetworksRequest) GetAll() bool { +func (x *SelectRoutesRequest) GetAll() bool { if x != nil { return x.All } return false } -type SelectNetworksResponse struct { +type SelectRoutesResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields } -func (x *SelectNetworksResponse) Reset() { - *x = SelectNetworksResponse{} +func (x *SelectRoutesResponse) Reset() { + *x = SelectRoutesResponse{} if protoimpl.UnsafeEnabled { mi := &file_daemon_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1674,13 +1674,13 @@ func (x *SelectNetworksResponse) Reset() { } } -func (x *SelectNetworksResponse) String() string { +func (x *SelectRoutesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SelectNetworksResponse) ProtoMessage() {} +func (*SelectRoutesResponse) ProtoMessage() {} -func (x *SelectNetworksResponse) ProtoReflect() protoreflect.Message { +func (x *SelectRoutesResponse) ProtoReflect() protoreflect.Message { mi := &file_daemon_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1692,8 +1692,8 @@ func (x *SelectNetworksResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SelectNetworksResponse.ProtoReflect.Descriptor instead. -func (*SelectNetworksResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use SelectRoutesResponse.ProtoReflect.Descriptor instead. +func (*SelectRoutesResponse) Descriptor() ([]byte, []int) { return file_daemon_proto_rawDescGZIP(), []int{22} } @@ -1744,20 +1744,20 @@ func (x *IPList) GetIps() []string { return nil } -type Network struct { +type Route struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` - Range string `protobuf:"bytes,2,opt,name=range,proto3" json:"range,omitempty"` + Network string `protobuf:"bytes,2,opt,name=network,proto3" json:"network,omitempty"` Selected bool `protobuf:"varint,3,opt,name=selected,proto3" json:"selected,omitempty"` Domains []string `protobuf:"bytes,4,rep,name=domains,proto3" json:"domains,omitempty"` ResolvedIPs map[string]*IPList `protobuf:"bytes,5,rep,name=resolvedIPs,proto3" json:"resolvedIPs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } -func (x *Network) Reset() { - *x = Network{} +func (x *Route) Reset() { + *x = Route{} if protoimpl.UnsafeEnabled { mi := &file_daemon_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1765,13 +1765,13 @@ func (x *Network) Reset() { } } -func (x *Network) String() string { +func (x *Route) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Network) ProtoMessage() {} +func (*Route) ProtoMessage() {} -func (x *Network) ProtoReflect() protoreflect.Message { +func (x *Route) ProtoReflect() protoreflect.Message { mi := &file_daemon_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1783,40 +1783,40 @@ func (x *Network) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Network.ProtoReflect.Descriptor instead. -func (*Network) Descriptor() ([]byte, []int) { +// Deprecated: Use Route.ProtoReflect.Descriptor instead. +func (*Route) Descriptor() ([]byte, []int) { return file_daemon_proto_rawDescGZIP(), []int{24} } -func (x *Network) GetID() string { +func (x *Route) GetID() string { if x != nil { return x.ID } return "" } -func (x *Network) GetRange() string { +func (x *Route) GetNetwork() string { if x != nil { - return x.Range + return x.Network } return "" } -func (x *Network) GetSelected() bool { +func (x *Route) GetSelected() bool { if x != nil { return x.Selected } return false } -func (x *Network) GetDomains() []string { +func (x *Route) GetDomains() []string { if x != nil { return x.Domains } return nil } -func (x *Network) GetResolvedIPs() map[string]*IPList { +func (x *Route) GetResolvedIPs() map[string]*IPList { if x != nil { return x.ResolvedIPs } @@ -2671,7 +2671,7 @@ var file_daemon_proto_rawDesc = []byte{ 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, - 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x22, 0xde, 0x05, 0x0a, 0x09, 0x50, 0x65, + 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x22, 0xda, 0x05, 0x0a, 0x09, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, @@ -2710,235 +2710,233 @@ var file_daemon_proto_rawDesc = []byte{ 0x52, 0x07, 0x62, 0x79, 0x74, 0x65, 0x73, 0x54, 0x78, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, - 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x12, 0x33, 0x0a, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x11, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x6c, - 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, - 0x6c, 0x61, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xf0, 0x01, 0x0a, 0x0e, 0x4c, - 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, - 0x02, 0x49, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, - 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, - 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, - 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, - 0x71, 0x64, 0x6e, 0x12, 0x2a, 0x0a, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, - 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, - 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, - 0x30, 0x0a, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, - 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x72, 0x6f, - 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x07, 0x20, - 0x03, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x22, 0x53, 0x0a, - 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, - 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, - 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x22, 0x57, 0x0a, 0x0f, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x52, 0x0a, 0x0a, 0x52, - 0x65, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x49, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x49, 0x12, 0x1c, 0x0a, 0x09, 0x61, - 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, - 0x72, 0x0a, 0x0c, 0x4e, 0x53, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, - 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, - 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x14, 0x0a, - 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, - 0x72, 0x6f, 0x72, 0x22, 0xd2, 0x02, 0x0a, 0x0a, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, - 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3e, 0x0a, 0x0e, - 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, - 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x6c, 0x6f, - 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x05, - 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, - 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, - 0x65, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, - 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x64, 0x6e, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, - 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, - 0x4e, 0x53, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x64, 0x6e, - 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x3f, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, - 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, - 0x22, 0x61, 0x0a, 0x15, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x49, 0x44, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x70, 0x70, - 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x61, 0x70, 0x70, 0x65, 0x6e, - 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, - 0x61, 0x6c, 0x6c, 0x22, 0x18, 0x0a, 0x16, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x0a, - 0x06, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x70, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x69, 0x70, 0x73, 0x22, 0xf9, 0x01, 0x0a, 0x07, 0x4e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, - 0x65, 0x6c, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, - 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x73, 0x12, 0x42, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, - 0x49, 0x50, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, - 0x65, 0x64, 0x49, 0x50, 0x73, 0x1a, 0x4e, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, - 0x64, 0x49, 0x50, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x2e, 0x49, 0x50, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x6a, 0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, - 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61, - 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x69, 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, - 0x6f, 0x22, 0x29, 0x0a, 0x13, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x14, 0x0a, 0x12, - 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x3d, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, - 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, - 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, - 0x6c, 0x22, 0x3c, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, - 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, - 0x15, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x22, 0x13, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, - 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, - 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x44, 0x0a, 0x11, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x3b, 0x0a, 0x12, 0x43, - 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x63, 0x6c, 0x65, 0x61, 0x6e, - 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x45, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, - 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, - 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, - 0x3c, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, - 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x3b, 0x0a, - 0x1f, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, - 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x22, 0x0a, 0x20, 0x53, 0x65, - 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, - 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x62, - 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, - 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x4e, 0x49, 0x43, - 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x09, 0x0a, - 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, - 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, - 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, - 0x10, 0x07, 0x32, 0x93, 0x09, 0x0a, 0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e, - 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, - 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, - 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12, - 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x15, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, - 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, - 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, - 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x1b, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, - 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x0e, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x1d, 0x2e, 0x64, 0x61, - 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, - 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x10, - 0x44, 0x65, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x12, 0x1d, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1e, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x4e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, - 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, - 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x47, - 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, - 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, + 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x33, 0x0a, + 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x6c, 0x61, 0x74, 0x65, 0x6e, + 0x63, 0x79, 0x12, 0x22, 0x0a, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xec, 0x01, 0x0a, 0x0e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, + 0x50, 0x65, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x50, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x50, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, + 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, + 0x79, 0x12, 0x28, 0x0a, 0x0f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, + 0x66, 0x61, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x6b, 0x65, 0x72, 0x6e, + 0x65, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, + 0x71, 0x64, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x71, 0x64, 0x6e, 0x12, + 0x2a, 0x0a, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72, 0x6f, 0x73, 0x65, 0x6e, + 0x70, 0x61, 0x73, 0x73, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x13, 0x72, + 0x6f, 0x73, 0x65, 0x6e, 0x70, 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, + 0x76, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x72, 0x6f, 0x73, 0x65, 0x6e, 0x70, + 0x61, 0x73, 0x73, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, 0x65, 0x12, 0x16, 0x0a, + 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x53, 0x0a, 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x57, 0x0a, 0x0f, 0x4d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, + 0x03, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x55, 0x52, 0x4c, 0x12, + 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x14, 0x0a, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x22, 0x52, 0x0a, 0x0a, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x52, 0x49, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x55, 0x52, 0x49, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x72, 0x0a, 0x0c, 0x4e, 0x53, 0x47, 0x72, 0x6f, + 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x73, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0xd2, 0x02, 0x0a, 0x0a, + 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x61, 0x6e, + 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x6d, 0x61, + 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x35, 0x0a, + 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x69, 0x67, 0x6e, + 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x3e, 0x0a, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x0e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x50, 0x65, 0x65, 0x72, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x65, 0x65, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x70, 0x65, 0x65, 0x72, 0x73, 0x12, 0x2a, 0x0a, + 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x6c, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x64, 0x6e, 0x73, + 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4e, 0x53, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x0a, 0x64, 0x6e, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, + 0x22, 0x13, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x22, 0x5b, 0x0a, 0x13, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x6f, 0x75, + 0x74, 0x65, 0x49, 0x44, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x12, 0x10, 0x0a, + 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, + 0x16, 0x0a, 0x14, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1a, 0x0a, 0x06, 0x49, 0x50, 0x4c, 0x69, 0x73, + 0x74, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, + 0x69, 0x70, 0x73, 0x22, 0xf9, 0x01, 0x0a, 0x05, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, + 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x12, 0x40, 0x0a, + 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, 0x18, 0x05, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, 0x1a, + 0x4e, 0x0a, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x64, 0x49, 0x50, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x49, 0x50, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0x6a, 0x0a, 0x12, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x69, + 0x7a, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, + 0x69, 0x7a, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, + 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x29, 0x0a, 0x13, 0x44, + 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3d, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, - 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, - 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x45, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, - 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, - 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, - 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, - 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x4e, 0x65, + 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x3c, 0x0a, 0x12, 0x53, + 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x26, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x10, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x65, 0x74, + 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1b, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x13, 0x0a, + 0x11, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, + 0x44, 0x0a, 0x11, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x3b, 0x0a, 0x12, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x63, + 0x6c, 0x65, 0x61, 0x6e, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0d, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x65, 0x64, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x73, 0x22, 0x45, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x22, 0x3c, 0x0a, 0x13, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x22, 0x3b, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, - 0x6e, 0x63, 0x65, 0x12, 0x27, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, - 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, - 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, - 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x22, 0x22, 0x0a, 0x20, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, + 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x62, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, + 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x41, 0x4e, 0x49, 0x43, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, + 0x46, 0x41, 0x54, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, + 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, + 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, + 0x06, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x07, 0x32, 0x81, 0x09, 0x0a, + 0x0d, 0x44, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x36, + 0x0a, 0x05, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x14, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0c, 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, + 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, + 0x57, 0x61, 0x69, 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x57, 0x61, 0x69, + 0x74, 0x53, 0x53, 0x4f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x2d, 0x0a, 0x02, 0x55, 0x70, 0x12, 0x11, 0x2e, 0x64, 0x61, 0x65, 0x6d, + 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x39, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x15, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, + 0x04, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x13, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, + 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x64, 0x61, 0x65, + 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x42, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x18, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, + 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, + 0x0c, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, + 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, + 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x0e, 0x44, 0x65, + 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x1b, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x64, 0x61, 0x65, 0x6d, + 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x62, + 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, + 0x62, 0x75, 0x67, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, + 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, + 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, + 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, + 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, + 0x0b, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, + 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, + 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, + 0x0a, 0x0a, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x19, 0x2e, 0x64, + 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, + 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x1a, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x6f, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, + 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x27, 0x2e, 0x64, 0x61, + 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, + 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, + 0x74, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4d, 0x61, 0x70, 0x50, 0x65, 0x72, 0x73, 0x69, + 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -2976,12 +2974,12 @@ var file_daemon_proto_goTypes = []interface{}{ (*RelayState)(nil), // 17: daemon.RelayState (*NSGroupState)(nil), // 18: daemon.NSGroupState (*FullStatus)(nil), // 19: daemon.FullStatus - (*ListNetworksRequest)(nil), // 20: daemon.ListNetworksRequest - (*ListNetworksResponse)(nil), // 21: daemon.ListNetworksResponse - (*SelectNetworksRequest)(nil), // 22: daemon.SelectNetworksRequest - (*SelectNetworksResponse)(nil), // 23: daemon.SelectNetworksResponse + (*ListRoutesRequest)(nil), // 20: daemon.ListRoutesRequest + (*ListRoutesResponse)(nil), // 21: daemon.ListRoutesResponse + (*SelectRoutesRequest)(nil), // 22: daemon.SelectRoutesRequest + (*SelectRoutesResponse)(nil), // 23: daemon.SelectRoutesResponse (*IPList)(nil), // 24: daemon.IPList - (*Network)(nil), // 25: daemon.Network + (*Route)(nil), // 25: daemon.Route (*DebugBundleRequest)(nil), // 26: daemon.DebugBundleRequest (*DebugBundleResponse)(nil), // 27: daemon.DebugBundleResponse (*GetLogLevelRequest)(nil), // 28: daemon.GetLogLevelRequest @@ -2997,7 +2995,7 @@ var file_daemon_proto_goTypes = []interface{}{ (*DeleteStateResponse)(nil), // 38: daemon.DeleteStateResponse (*SetNetworkMapPersistenceRequest)(nil), // 39: daemon.SetNetworkMapPersistenceRequest (*SetNetworkMapPersistenceResponse)(nil), // 40: daemon.SetNetworkMapPersistenceResponse - nil, // 41: daemon.Network.ResolvedIPsEntry + nil, // 41: daemon.Route.ResolvedIPsEntry (*durationpb.Duration)(nil), // 42: google.protobuf.Duration (*timestamppb.Timestamp)(nil), // 43: google.protobuf.Timestamp } @@ -3013,21 +3011,21 @@ var file_daemon_proto_depIdxs = []int32{ 13, // 8: daemon.FullStatus.peers:type_name -> daemon.PeerState 17, // 9: daemon.FullStatus.relays:type_name -> daemon.RelayState 18, // 10: daemon.FullStatus.dns_servers:type_name -> daemon.NSGroupState - 25, // 11: daemon.ListNetworksResponse.routes:type_name -> daemon.Network - 41, // 12: daemon.Network.resolvedIPs:type_name -> daemon.Network.ResolvedIPsEntry + 25, // 11: daemon.ListRoutesResponse.routes:type_name -> daemon.Route + 41, // 12: daemon.Route.resolvedIPs:type_name -> daemon.Route.ResolvedIPsEntry 0, // 13: daemon.GetLogLevelResponse.level:type_name -> daemon.LogLevel 0, // 14: daemon.SetLogLevelRequest.level:type_name -> daemon.LogLevel 32, // 15: daemon.ListStatesResponse.states:type_name -> daemon.State - 24, // 16: daemon.Network.ResolvedIPsEntry.value:type_name -> daemon.IPList + 24, // 16: daemon.Route.ResolvedIPsEntry.value:type_name -> daemon.IPList 1, // 17: daemon.DaemonService.Login:input_type -> daemon.LoginRequest 3, // 18: daemon.DaemonService.WaitSSOLogin:input_type -> daemon.WaitSSOLoginRequest 5, // 19: daemon.DaemonService.Up:input_type -> daemon.UpRequest 7, // 20: daemon.DaemonService.Status:input_type -> daemon.StatusRequest 9, // 21: daemon.DaemonService.Down:input_type -> daemon.DownRequest 11, // 22: daemon.DaemonService.GetConfig:input_type -> daemon.GetConfigRequest - 20, // 23: daemon.DaemonService.ListNetworks:input_type -> daemon.ListNetworksRequest - 22, // 24: daemon.DaemonService.SelectNetworks:input_type -> daemon.SelectNetworksRequest - 22, // 25: daemon.DaemonService.DeselectNetworks:input_type -> daemon.SelectNetworksRequest + 20, // 23: daemon.DaemonService.ListRoutes:input_type -> daemon.ListRoutesRequest + 22, // 24: daemon.DaemonService.SelectRoutes:input_type -> daemon.SelectRoutesRequest + 22, // 25: daemon.DaemonService.DeselectRoutes:input_type -> daemon.SelectRoutesRequest 26, // 26: daemon.DaemonService.DebugBundle:input_type -> daemon.DebugBundleRequest 28, // 27: daemon.DaemonService.GetLogLevel:input_type -> daemon.GetLogLevelRequest 30, // 28: daemon.DaemonService.SetLogLevel:input_type -> daemon.SetLogLevelRequest @@ -3041,9 +3039,9 @@ var file_daemon_proto_depIdxs = []int32{ 8, // 36: daemon.DaemonService.Status:output_type -> daemon.StatusResponse 10, // 37: daemon.DaemonService.Down:output_type -> daemon.DownResponse 12, // 38: daemon.DaemonService.GetConfig:output_type -> daemon.GetConfigResponse - 21, // 39: daemon.DaemonService.ListNetworks:output_type -> daemon.ListNetworksResponse - 23, // 40: daemon.DaemonService.SelectNetworks:output_type -> daemon.SelectNetworksResponse - 23, // 41: daemon.DaemonService.DeselectNetworks:output_type -> daemon.SelectNetworksResponse + 21, // 39: daemon.DaemonService.ListRoutes:output_type -> daemon.ListRoutesResponse + 23, // 40: daemon.DaemonService.SelectRoutes:output_type -> daemon.SelectRoutesResponse + 23, // 41: daemon.DaemonService.DeselectRoutes:output_type -> daemon.SelectRoutesResponse 27, // 42: daemon.DaemonService.DebugBundle:output_type -> daemon.DebugBundleResponse 29, // 43: daemon.DaemonService.GetLogLevel:output_type -> daemon.GetLogLevelResponse 31, // 44: daemon.DaemonService.SetLogLevel:output_type -> daemon.SetLogLevelResponse @@ -3293,7 +3291,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListNetworksRequest); i { + switch v := v.(*ListRoutesRequest); i { case 0: return &v.state case 1: @@ -3305,7 +3303,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ListNetworksResponse); i { + switch v := v.(*ListRoutesResponse); i { case 0: return &v.state case 1: @@ -3317,7 +3315,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SelectNetworksRequest); i { + switch v := v.(*SelectRoutesRequest); i { case 0: return &v.state case 1: @@ -3329,7 +3327,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SelectNetworksResponse); i { + switch v := v.(*SelectRoutesResponse); i { case 0: return &v.state case 1: @@ -3353,7 +3351,7 @@ func file_daemon_proto_init() { } } file_daemon_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Network); i { + switch v := v.(*Route); i { case 0: return &v.state case 1: diff --git a/client/proto/daemon.proto b/client/proto/daemon.proto index cddf78242..96ade5b4e 100644 --- a/client/proto/daemon.proto +++ b/client/proto/daemon.proto @@ -28,14 +28,14 @@ service DaemonService { // GetConfig of the daemon. rpc GetConfig(GetConfigRequest) returns (GetConfigResponse) {} - // List available networks - rpc ListNetworks(ListNetworksRequest) returns (ListNetworksResponse) {} + // List available network routes + rpc ListRoutes(ListRoutesRequest) returns (ListRoutesResponse) {} // Select specific routes - rpc SelectNetworks(SelectNetworksRequest) returns (SelectNetworksResponse) {} + rpc SelectRoutes(SelectRoutesRequest) returns (SelectRoutesResponse) {} // Deselect specific routes - rpc DeselectNetworks(SelectNetworksRequest) returns (SelectNetworksResponse) {} + rpc DeselectRoutes(SelectRoutesRequest) returns (SelectRoutesResponse) {} // DebugBundle creates a debug bundle rpc DebugBundle(DebugBundleRequest) returns (DebugBundleResponse) {} @@ -190,7 +190,7 @@ message PeerState { int64 bytesRx = 13; int64 bytesTx = 14; bool rosenpassEnabled = 15; - repeated string networks = 16; + repeated string routes = 16; google.protobuf.Duration latency = 17; string relayAddress = 18; } @@ -203,7 +203,7 @@ message LocalPeerState { string fqdn = 4; bool rosenpassEnabled = 5; bool rosenpassPermissive = 6; - repeated string networks = 7; + repeated string routes = 7; } // SignalState contains the latest state of a signal connection @@ -244,20 +244,20 @@ message FullStatus { repeated NSGroupState dns_servers = 6; } -message ListNetworksRequest { +message ListRoutesRequest { } -message ListNetworksResponse { - repeated Network routes = 1; +message ListRoutesResponse { + repeated Route routes = 1; } -message SelectNetworksRequest { - repeated string networkIDs = 1; +message SelectRoutesRequest { + repeated string routeIDs = 1; bool append = 2; bool all = 3; } -message SelectNetworksResponse { +message SelectRoutesResponse { } message IPList { @@ -265,9 +265,9 @@ message IPList { } -message Network { +message Route { string ID = 1; - string range = 2; + string network = 2; bool selected = 3; repeated string domains = 4; map resolvedIPs = 5; diff --git a/client/proto/daemon_grpc.pb.go b/client/proto/daemon_grpc.pb.go index 39424aee9..2e063604a 100644 --- a/client/proto/daemon_grpc.pb.go +++ b/client/proto/daemon_grpc.pb.go @@ -31,12 +31,12 @@ type DaemonServiceClient interface { Down(ctx context.Context, in *DownRequest, opts ...grpc.CallOption) (*DownResponse, error) // GetConfig of the daemon. GetConfig(ctx context.Context, in *GetConfigRequest, opts ...grpc.CallOption) (*GetConfigResponse, error) - // List available networks - ListNetworks(ctx context.Context, in *ListNetworksRequest, opts ...grpc.CallOption) (*ListNetworksResponse, error) + // List available network routes + ListRoutes(ctx context.Context, in *ListRoutesRequest, opts ...grpc.CallOption) (*ListRoutesResponse, error) // Select specific routes - SelectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error) + SelectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error) // Deselect specific routes - DeselectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error) + DeselectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error) // DebugBundle creates a debug bundle DebugBundle(ctx context.Context, in *DebugBundleRequest, opts ...grpc.CallOption) (*DebugBundleResponse, error) // GetLogLevel gets the log level of the daemon @@ -115,27 +115,27 @@ func (c *daemonServiceClient) GetConfig(ctx context.Context, in *GetConfigReques return out, nil } -func (c *daemonServiceClient) ListNetworks(ctx context.Context, in *ListNetworksRequest, opts ...grpc.CallOption) (*ListNetworksResponse, error) { - out := new(ListNetworksResponse) - err := c.cc.Invoke(ctx, "/daemon.DaemonService/ListNetworks", in, out, opts...) +func (c *daemonServiceClient) ListRoutes(ctx context.Context, in *ListRoutesRequest, opts ...grpc.CallOption) (*ListRoutesResponse, error) { + out := new(ListRoutesResponse) + err := c.cc.Invoke(ctx, "/daemon.DaemonService/ListRoutes", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *daemonServiceClient) SelectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error) { - out := new(SelectNetworksResponse) - err := c.cc.Invoke(ctx, "/daemon.DaemonService/SelectNetworks", in, out, opts...) +func (c *daemonServiceClient) SelectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error) { + out := new(SelectRoutesResponse) + err := c.cc.Invoke(ctx, "/daemon.DaemonService/SelectRoutes", in, out, opts...) if err != nil { return nil, err } return out, nil } -func (c *daemonServiceClient) DeselectNetworks(ctx context.Context, in *SelectNetworksRequest, opts ...grpc.CallOption) (*SelectNetworksResponse, error) { - out := new(SelectNetworksResponse) - err := c.cc.Invoke(ctx, "/daemon.DaemonService/DeselectNetworks", in, out, opts...) +func (c *daemonServiceClient) DeselectRoutes(ctx context.Context, in *SelectRoutesRequest, opts ...grpc.CallOption) (*SelectRoutesResponse, error) { + out := new(SelectRoutesResponse) + err := c.cc.Invoke(ctx, "/daemon.DaemonService/DeselectRoutes", in, out, opts...) if err != nil { return nil, err } @@ -222,12 +222,12 @@ type DaemonServiceServer interface { Down(context.Context, *DownRequest) (*DownResponse, error) // GetConfig of the daemon. GetConfig(context.Context, *GetConfigRequest) (*GetConfigResponse, error) - // List available networks - ListNetworks(context.Context, *ListNetworksRequest) (*ListNetworksResponse, error) + // List available network routes + ListRoutes(context.Context, *ListRoutesRequest) (*ListRoutesResponse, error) // Select specific routes - SelectNetworks(context.Context, *SelectNetworksRequest) (*SelectNetworksResponse, error) + SelectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error) // Deselect specific routes - DeselectNetworks(context.Context, *SelectNetworksRequest) (*SelectNetworksResponse, error) + DeselectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error) // DebugBundle creates a debug bundle DebugBundle(context.Context, *DebugBundleRequest) (*DebugBundleResponse, error) // GetLogLevel gets the log level of the daemon @@ -267,14 +267,14 @@ func (UnimplementedDaemonServiceServer) Down(context.Context, *DownRequest) (*Do func (UnimplementedDaemonServiceServer) GetConfig(context.Context, *GetConfigRequest) (*GetConfigResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetConfig not implemented") } -func (UnimplementedDaemonServiceServer) ListNetworks(context.Context, *ListNetworksRequest) (*ListNetworksResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListNetworks not implemented") +func (UnimplementedDaemonServiceServer) ListRoutes(context.Context, *ListRoutesRequest) (*ListRoutesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListRoutes not implemented") } -func (UnimplementedDaemonServiceServer) SelectNetworks(context.Context, *SelectNetworksRequest) (*SelectNetworksResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SelectNetworks not implemented") +func (UnimplementedDaemonServiceServer) SelectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SelectRoutes not implemented") } -func (UnimplementedDaemonServiceServer) DeselectNetworks(context.Context, *SelectNetworksRequest) (*SelectNetworksResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeselectNetworks not implemented") +func (UnimplementedDaemonServiceServer) DeselectRoutes(context.Context, *SelectRoutesRequest) (*SelectRoutesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeselectRoutes not implemented") } func (UnimplementedDaemonServiceServer) DebugBundle(context.Context, *DebugBundleRequest) (*DebugBundleResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method DebugBundle not implemented") @@ -418,56 +418,56 @@ func _DaemonService_GetConfig_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } -func _DaemonService_ListNetworks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListNetworksRequest) +func _DaemonService_ListRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListRoutesRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(DaemonServiceServer).ListNetworks(ctx, in) + return srv.(DaemonServiceServer).ListRoutes(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/daemon.DaemonService/ListNetworks", + FullMethod: "/daemon.DaemonService/ListRoutes", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DaemonServiceServer).ListNetworks(ctx, req.(*ListNetworksRequest)) + return srv.(DaemonServiceServer).ListRoutes(ctx, req.(*ListRoutesRequest)) } return interceptor(ctx, in, info, handler) } -func _DaemonService_SelectNetworks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SelectNetworksRequest) +func _DaemonService_SelectRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SelectRoutesRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(DaemonServiceServer).SelectNetworks(ctx, in) + return srv.(DaemonServiceServer).SelectRoutes(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/daemon.DaemonService/SelectNetworks", + FullMethod: "/daemon.DaemonService/SelectRoutes", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DaemonServiceServer).SelectNetworks(ctx, req.(*SelectNetworksRequest)) + return srv.(DaemonServiceServer).SelectRoutes(ctx, req.(*SelectRoutesRequest)) } return interceptor(ctx, in, info, handler) } -func _DaemonService_DeselectNetworks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SelectNetworksRequest) +func _DaemonService_DeselectRoutes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SelectRoutesRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { - return srv.(DaemonServiceServer).DeselectNetworks(ctx, in) + return srv.(DaemonServiceServer).DeselectRoutes(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/daemon.DaemonService/DeselectNetworks", + FullMethod: "/daemon.DaemonService/DeselectRoutes", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(DaemonServiceServer).DeselectNetworks(ctx, req.(*SelectNetworksRequest)) + return srv.(DaemonServiceServer).DeselectRoutes(ctx, req.(*SelectRoutesRequest)) } return interceptor(ctx, in, info, handler) } @@ -630,16 +630,16 @@ var DaemonService_ServiceDesc = grpc.ServiceDesc{ Handler: _DaemonService_GetConfig_Handler, }, { - MethodName: "ListNetworks", - Handler: _DaemonService_ListNetworks_Handler, + MethodName: "ListRoutes", + Handler: _DaemonService_ListRoutes_Handler, }, { - MethodName: "SelectNetworks", - Handler: _DaemonService_SelectNetworks_Handler, + MethodName: "SelectRoutes", + Handler: _DaemonService_SelectRoutes_Handler, }, { - MethodName: "DeselectNetworks", - Handler: _DaemonService_DeselectNetworks_Handler, + MethodName: "DeselectRoutes", + Handler: _DaemonService_DeselectRoutes_Handler, }, { MethodName: "DebugBundle", diff --git a/client/server/network.go b/client/server/network.go deleted file mode 100644 index aaf361524..000000000 --- a/client/server/network.go +++ /dev/null @@ -1,176 +0,0 @@ -package server - -import ( - "context" - "fmt" - "net/netip" - "slices" - "sort" - - "golang.org/x/exp/maps" - - "github.com/netbirdio/netbird/client/proto" - "github.com/netbirdio/netbird/management/domain" - "github.com/netbirdio/netbird/route" -) - -type selectRoute struct { - NetID route.NetID - Network netip.Prefix - Domains domain.List - Selected bool -} - -// ListNetworks returns a list of all available networks. -func (s *Server) ListNetworks(context.Context, *proto.ListNetworksRequest) (*proto.ListNetworksResponse, error) { - s.mutex.Lock() - defer s.mutex.Unlock() - - if s.connectClient == nil { - return nil, fmt.Errorf("not connected") - } - - engine := s.connectClient.Engine() - if engine == nil { - return nil, fmt.Errorf("not connected") - } - - routesMap := engine.GetRouteManager().GetClientRoutesWithNetID() - routeSelector := engine.GetRouteManager().GetRouteSelector() - - var routes []*selectRoute - for id, rt := range routesMap { - if len(rt) == 0 { - continue - } - route := &selectRoute{ - NetID: id, - Network: rt[0].Network, - Domains: rt[0].Domains, - Selected: routeSelector.IsSelected(id), - } - routes = append(routes, route) - } - - sort.Slice(routes, func(i, j int) bool { - iPrefix := routes[i].Network.Bits() - jPrefix := routes[j].Network.Bits() - - if iPrefix == jPrefix { - iAddr := routes[i].Network.Addr() - jAddr := routes[j].Network.Addr() - if iAddr == jAddr { - return routes[i].NetID < routes[j].NetID - } - return iAddr.String() < jAddr.String() - } - return iPrefix < jPrefix - }) - - resolvedDomains := s.statusRecorder.GetResolvedDomainsStates() - var pbRoutes []*proto.Network - for _, route := range routes { - pbRoute := &proto.Network{ - ID: string(route.NetID), - Range: route.Network.String(), - Domains: route.Domains.ToSafeStringList(), - ResolvedIPs: map[string]*proto.IPList{}, - Selected: route.Selected, - } - - // Group resolved IPs by their parent domain - domainMap := map[domain.Domain][]string{} - - for resolvedDomain, info := range resolvedDomains { - // Check if this resolved domain's parent is in our route's domains - if slices.Contains(route.Domains, info.ParentDomain) { - ips := make([]string, 0, len(info.Prefixes)) - for _, prefix := range info.Prefixes { - ips = append(ips, prefix.Addr().String()) - } - domainMap[resolvedDomain] = ips - } - } - - // Convert to proto format - for domain, ips := range domainMap { - pbRoute.ResolvedIPs[string(domain)] = &proto.IPList{ - Ips: ips, - } - } - - pbRoutes = append(pbRoutes, pbRoute) - } - - return &proto.ListNetworksResponse{ - Routes: pbRoutes, - }, nil -} - -// SelectNetworks selects specific networks based on the client request. -func (s *Server) SelectNetworks(_ context.Context, req *proto.SelectNetworksRequest) (*proto.SelectNetworksResponse, error) { - s.mutex.Lock() - defer s.mutex.Unlock() - - if s.connectClient == nil { - return nil, fmt.Errorf("not connected") - } - - engine := s.connectClient.Engine() - if engine == nil { - return nil, fmt.Errorf("not connected") - } - - routeManager := engine.GetRouteManager() - routeSelector := routeManager.GetRouteSelector() - if req.GetAll() { - routeSelector.SelectAllRoutes() - } else { - routes := toNetIDs(req.GetNetworkIDs()) - netIdRoutes := maps.Keys(routeManager.GetClientRoutesWithNetID()) - if err := routeSelector.SelectRoutes(routes, req.GetAppend(), netIdRoutes); err != nil { - return nil, fmt.Errorf("select routes: %w", err) - } - } - routeManager.TriggerSelection(routeManager.GetClientRoutes()) - - return &proto.SelectNetworksResponse{}, nil -} - -// DeselectNetworks deselects specific networks based on the client request. -func (s *Server) DeselectNetworks(_ context.Context, req *proto.SelectNetworksRequest) (*proto.SelectNetworksResponse, error) { - s.mutex.Lock() - defer s.mutex.Unlock() - - if s.connectClient == nil { - return nil, fmt.Errorf("not connected") - } - - engine := s.connectClient.Engine() - if engine == nil { - return nil, fmt.Errorf("not connected") - } - - routeManager := engine.GetRouteManager() - routeSelector := routeManager.GetRouteSelector() - if req.GetAll() { - routeSelector.DeselectAllRoutes() - } else { - routes := toNetIDs(req.GetNetworkIDs()) - netIdRoutes := maps.Keys(routeManager.GetClientRoutesWithNetID()) - if err := routeSelector.DeselectRoutes(routes, netIdRoutes); err != nil { - return nil, fmt.Errorf("deselect routes: %w", err) - } - } - routeManager.TriggerSelection(routeManager.GetClientRoutes()) - - return &proto.SelectNetworksResponse{}, nil -} - -func toNetIDs(routes []string) []route.NetID { - var netIDs []route.NetID - for _, rt := range routes { - netIDs = append(netIDs, route.NetID(rt)) - } - return netIDs -} diff --git a/client/server/server.go b/client/server/server.go index 5640ffa39..71eb58a66 100644 --- a/client/server/server.go +++ b/client/server/server.go @@ -772,7 +772,7 @@ func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus { pbFullStatus.LocalPeerState.Fqdn = fullStatus.LocalPeerState.FQDN pbFullStatus.LocalPeerState.RosenpassPermissive = fullStatus.RosenpassState.Permissive pbFullStatus.LocalPeerState.RosenpassEnabled = fullStatus.RosenpassState.Enabled - pbFullStatus.LocalPeerState.Networks = maps.Keys(fullStatus.LocalPeerState.Routes) + pbFullStatus.LocalPeerState.Routes = maps.Keys(fullStatus.LocalPeerState.Routes) for _, peerState := range fullStatus.Peers { pbPeerState := &proto.PeerState{ @@ -791,7 +791,7 @@ func toProtoFullStatus(fullStatus peer.FullStatus) *proto.FullStatus { BytesRx: peerState.BytesRx, BytesTx: peerState.BytesTx, RosenpassEnabled: peerState.RosenpassEnabled, - Networks: maps.Keys(peerState.GetRoutes()), + Routes: maps.Keys(peerState.GetRoutes()), Latency: durationpb.New(peerState.Latency), } pbFullStatus.Peers = append(pbFullStatus.Peers, pbPeerState) diff --git a/client/server/server_test.go b/client/server/server_test.go index 128de8e02..61bdaf660 100644 --- a/client/server/server_test.go +++ b/client/server/server_test.go @@ -20,8 +20,6 @@ import ( mgmtProto "github.com/netbirdio/netbird/management/proto" "github.com/netbirdio/netbird/management/server" "github.com/netbirdio/netbird/management/server/activity" - "github.com/netbirdio/netbird/management/server/settings" - "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/telemetry" "github.com/netbirdio/netbird/signal/proto" signalServer "github.com/netbirdio/netbird/signal/server" @@ -112,7 +110,7 @@ func startManagement(t *testing.T, signalAddr string, counter *int) (*grpc.Serve return nil, "", err } s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp)) - store, cleanUp, err := store.NewTestStoreFromSQL(context.Background(), "", config.Datadir) + store, cleanUp, err := server.NewTestStoreFromSQL(context.Background(), "", config.Datadir) if err != nil { return nil, "", err } @@ -134,7 +132,7 @@ func startManagement(t *testing.T, signalAddr string, counter *int) (*grpc.Serve } secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay) - mgmtServer, err := server.NewServer(context.Background(), config, accountManager, settings.NewManager(store), peersUpdateManager, secretsManager, nil, nil) + mgmtServer, err := server.NewServer(context.Background(), config, accountManager, peersUpdateManager, secretsManager, nil, nil) if err != nil { return nil, "", err } diff --git a/client/ui/client_ui.go b/client/ui/client_ui.go index 49b0f53cf..8ca0db73f 100644 --- a/client/ui/client_ui.go +++ b/client/ui/client_ui.go @@ -58,7 +58,7 @@ func main() { var showSettings bool flag.BoolVar(&showSettings, "settings", false, "run settings windows") var showRoutes bool - flag.BoolVar(&showRoutes, "networks", false, "run networks windows") + flag.BoolVar(&showRoutes, "routes", false, "run routes windows") var errorMSG string flag.StringVar(&errorMSG, "error-msg", "", "displays a error message window") @@ -233,7 +233,7 @@ func newServiceClient(addr string, a fyne.App, showSettings bool, showRoutes boo s.showSettingsUI() return s } else if showRoutes { - s.showNetworksUI() + s.showRoutesUI() } return s @@ -549,7 +549,7 @@ func (s *serviceClient) onTrayReady() { s.mAdvancedSettings = s.mSettings.AddSubMenuItem("Advanced Settings", "Advanced settings of the application") s.loadSettings() - s.mRoutes = systray.AddMenuItem("Networks", "Open the networks management window") + s.mRoutes = systray.AddMenuItem("Network Routes", "Open the routes management window") s.mRoutes.Disable() systray.AddSeparator() @@ -657,7 +657,7 @@ func (s *serviceClient) onTrayReady() { s.mRoutes.Disable() go func() { defer s.mRoutes.Enable() - s.runSelfCommand("networks", "true") + s.runSelfCommand("routes", "true") }() } if err != nil { diff --git a/client/ui/network.go b/client/ui/network.go deleted file mode 100644 index e6f027f0e..000000000 --- a/client/ui/network.go +++ /dev/null @@ -1,355 +0,0 @@ -//go:build !(linux && 386) && !freebsd - -package main - -import ( - "fmt" - "sort" - "strings" - "time" - - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/dialog" - "fyne.io/fyne/v2/layout" - "fyne.io/fyne/v2/widget" - log "github.com/sirupsen/logrus" - - "github.com/netbirdio/netbird/client/proto" -) - -const ( - allNetworksText = "All networks" - overlappingNetworksText = "Overlapping networks" - exitNodeNetworksText = "Exit-node networks" - allNetworks filter = "all" - overlappingNetworks filter = "overlapping" - exitNodeNetworks filter = "exit-node" - getClientFMT = "get client: %v" -) - -type filter string - -func (s *serviceClient) showNetworksUI() { - s.wRoutes = s.app.NewWindow("Networks") - - allGrid := container.New(layout.NewGridLayout(3)) - go s.updateNetworks(allGrid, allNetworks) - overlappingGrid := container.New(layout.NewGridLayout(3)) - exitNodeGrid := container.New(layout.NewGridLayout(3)) - routeCheckContainer := container.NewVBox() - tabs := container.NewAppTabs( - container.NewTabItem(allNetworksText, allGrid), - container.NewTabItem(overlappingNetworksText, overlappingGrid), - container.NewTabItem(exitNodeNetworksText, exitNodeGrid), - ) - tabs.OnSelected = func(item *container.TabItem) { - s.updateNetworksBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodeGrid) - } - tabs.OnUnselected = func(item *container.TabItem) { - grid, _ := getGridAndFilterFromTab(tabs, allGrid, overlappingGrid, exitNodeGrid) - grid.Objects = nil - } - - routeCheckContainer.Add(tabs) - scrollContainer := container.NewVScroll(routeCheckContainer) - scrollContainer.SetMinSize(fyne.NewSize(200, 300)) - - buttonBox := container.NewHBox( - layout.NewSpacer(), - widget.NewButton("Refresh", func() { - s.updateNetworksBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodeGrid) - }), - widget.NewButton("Select all", func() { - _, f := getGridAndFilterFromTab(tabs, allGrid, overlappingGrid, exitNodeGrid) - s.selectAllFilteredNetworks(f) - s.updateNetworksBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodeGrid) - }), - widget.NewButton("Deselect All", func() { - _, f := getGridAndFilterFromTab(tabs, allGrid, overlappingGrid, exitNodeGrid) - s.deselectAllFilteredNetworks(f) - s.updateNetworksBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodeGrid) - }), - layout.NewSpacer(), - ) - - content := container.NewBorder(nil, buttonBox, nil, nil, scrollContainer) - - s.wRoutes.SetContent(content) - s.wRoutes.Show() - - s.startAutoRefresh(10*time.Second, tabs, allGrid, overlappingGrid, exitNodeGrid) -} - -func (s *serviceClient) updateNetworks(grid *fyne.Container, f filter) { - grid.Objects = nil - grid.Refresh() - idHeader := widget.NewLabelWithStyle(" ID", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}) - networkHeader := widget.NewLabelWithStyle("Range/Domains", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}) - resolvedIPsHeader := widget.NewLabelWithStyle("Resolved IPs", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}) - - grid.Add(idHeader) - grid.Add(networkHeader) - grid.Add(resolvedIPsHeader) - - filteredRoutes, err := s.getFilteredNetworks(f) - if err != nil { - return - } - - sortNetworksByIDs(filteredRoutes) - - for _, route := range filteredRoutes { - r := route - - checkBox := widget.NewCheck(r.GetID(), func(checked bool) { - s.selectNetwork(r.ID, checked) - }) - checkBox.Checked = route.Selected - checkBox.Resize(fyne.NewSize(20, 20)) - checkBox.Refresh() - - grid.Add(checkBox) - network := r.GetRange() - domains := r.GetDomains() - - if len(domains) == 0 { - grid.Add(widget.NewLabel(network)) - grid.Add(widget.NewLabel("")) - continue - } - - // our selectors are only for display - noopFunc := func(_ string) { - // do nothing - } - - domainsSelector := widget.NewSelect(domains, noopFunc) - domainsSelector.Selected = domains[0] - grid.Add(domainsSelector) - - var resolvedIPsList []string - for domain, ipList := range r.GetResolvedIPs() { - resolvedIPsList = append(resolvedIPsList, fmt.Sprintf("%s: %s", domain, strings.Join(ipList.GetIps(), ", "))) - } - - if len(resolvedIPsList) == 0 { - grid.Add(widget.NewLabel("")) - continue - } - - // TODO: limit width within the selector display - resolvedIPsSelector := widget.NewSelect(resolvedIPsList, noopFunc) - resolvedIPsSelector.Selected = resolvedIPsList[0] - resolvedIPsSelector.Resize(fyne.NewSize(100, 100)) - grid.Add(resolvedIPsSelector) - } - - s.wRoutes.Content().Refresh() - grid.Refresh() -} - -func (s *serviceClient) getFilteredNetworks(f filter) ([]*proto.Network, error) { - routes, err := s.fetchNetworks() - if err != nil { - log.Errorf(getClientFMT, err) - s.showError(fmt.Errorf(getClientFMT, err)) - return nil, err - } - switch f { - case overlappingNetworks: - return getOverlappingNetworks(routes), nil - case exitNodeNetworks: - return getExitNodeNetworks(routes), nil - default: - } - return routes, nil -} - -func getOverlappingNetworks(routes []*proto.Network) []*proto.Network { - var filteredRoutes []*proto.Network - existingRange := make(map[string][]*proto.Network) - for _, route := range routes { - if len(route.Domains) > 0 { - continue - } - if r, exists := existingRange[route.GetRange()]; exists { - r = append(r, route) - existingRange[route.GetRange()] = r - } else { - existingRange[route.GetRange()] = []*proto.Network{route} - } - } - for _, r := range existingRange { - if len(r) > 1 { - filteredRoutes = append(filteredRoutes, r...) - } - } - return filteredRoutes -} - -func getExitNodeNetworks(routes []*proto.Network) []*proto.Network { - var filteredRoutes []*proto.Network - for _, route := range routes { - if route.Range == "0.0.0.0/0" { - filteredRoutes = append(filteredRoutes, route) - } - } - return filteredRoutes -} - -func sortNetworksByIDs(routes []*proto.Network) { - sort.Slice(routes, func(i, j int) bool { - return strings.ToLower(routes[i].GetID()) < strings.ToLower(routes[j].GetID()) - }) -} - -func (s *serviceClient) fetchNetworks() ([]*proto.Network, error) { - conn, err := s.getSrvClient(defaultFailTimeout) - if err != nil { - return nil, fmt.Errorf(getClientFMT, err) - } - - resp, err := conn.ListNetworks(s.ctx, &proto.ListNetworksRequest{}) - if err != nil { - return nil, fmt.Errorf("failed to list routes: %v", err) - } - - return resp.Routes, nil -} - -func (s *serviceClient) selectNetwork(id string, checked bool) { - conn, err := s.getSrvClient(defaultFailTimeout) - if err != nil { - log.Errorf(getClientFMT, err) - s.showError(fmt.Errorf(getClientFMT, err)) - return - } - - req := &proto.SelectNetworksRequest{ - NetworkIDs: []string{id}, - Append: checked, - } - - if checked { - if _, err := conn.SelectNetworks(s.ctx, req); err != nil { - log.Errorf("failed to select network: %v", err) - s.showError(fmt.Errorf("failed to select network: %v", err)) - return - } - log.Infof("Route %s selected", id) - } else { - if _, err := conn.DeselectNetworks(s.ctx, req); err != nil { - log.Errorf("failed to deselect network: %v", err) - s.showError(fmt.Errorf("failed to deselect network: %v", err)) - return - } - log.Infof("Network %s deselected", id) - } -} - -func (s *serviceClient) selectAllFilteredNetworks(f filter) { - conn, err := s.getSrvClient(defaultFailTimeout) - if err != nil { - log.Errorf(getClientFMT, err) - return - } - - req := s.getNetworksRequest(f, true) - if _, err := conn.SelectNetworks(s.ctx, req); err != nil { - log.Errorf("failed to select all networks: %v", err) - s.showError(fmt.Errorf("failed to select all networks: %v", err)) - return - } - - log.Debug("All networks selected") -} - -func (s *serviceClient) deselectAllFilteredNetworks(f filter) { - conn, err := s.getSrvClient(defaultFailTimeout) - if err != nil { - log.Errorf(getClientFMT, err) - return - } - - req := s.getNetworksRequest(f, false) - if _, err := conn.DeselectNetworks(s.ctx, req); err != nil { - log.Errorf("failed to deselect all networks: %v", err) - s.showError(fmt.Errorf("failed to deselect all networks: %v", err)) - return - } - - log.Debug("All networks deselected") -} - -func (s *serviceClient) getNetworksRequest(f filter, appendRoute bool) *proto.SelectNetworksRequest { - req := &proto.SelectNetworksRequest{} - if f == allNetworks { - req.All = true - } else { - routes, err := s.getFilteredNetworks(f) - if err != nil { - return nil - } - for _, route := range routes { - req.NetworkIDs = append(req.NetworkIDs, route.GetID()) - } - req.Append = appendRoute - } - return req -} - -func (s *serviceClient) showError(err error) { - wrappedMessage := wrapText(err.Error(), 50) - - dialog.ShowError(fmt.Errorf("%s", wrappedMessage), s.wRoutes) -} - -func (s *serviceClient) startAutoRefresh(interval time.Duration, tabs *container.AppTabs, allGrid, overlappingGrid, exitNodesGrid *fyne.Container) { - ticker := time.NewTicker(interval) - go func() { - for range ticker.C { - s.updateNetworksBasedOnDisplayTab(tabs, allGrid, overlappingGrid, exitNodesGrid) - } - }() - - s.wRoutes.SetOnClosed(func() { - ticker.Stop() - }) -} - -func (s *serviceClient) updateNetworksBasedOnDisplayTab(tabs *container.AppTabs, allGrid, overlappingGrid, exitNodesGrid *fyne.Container) { - grid, f := getGridAndFilterFromTab(tabs, allGrid, overlappingGrid, exitNodesGrid) - s.wRoutes.Content().Refresh() - s.updateNetworks(grid, f) -} - -func getGridAndFilterFromTab(tabs *container.AppTabs, allGrid, overlappingGrid, exitNodesGrid *fyne.Container) (*fyne.Container, filter) { - switch tabs.Selected().Text { - case overlappingNetworksText: - return overlappingGrid, overlappingNetworks - case exitNodeNetworksText: - return exitNodesGrid, exitNodeNetworks - default: - return allGrid, allNetworks - } -} - -// wrapText inserts newlines into the text to ensure that each line is -// no longer than 'lineLength' runes. -func wrapText(text string, lineLength int) string { - var sb strings.Builder - var currentLineLength int - - for _, runeValue := range text { - sb.WriteRune(runeValue) - currentLineLength++ - - if currentLineLength >= lineLength || runeValue == '\n' { - sb.WriteRune('\n') - currentLineLength = 0 - } - } - - return sb.String() -}