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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions cmd/server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -975,24 +975,40 @@ func (c *Config) BlacklistGeneration() uint64 {
// lazily on first read from c.NodeBlacklist (covering the JSON-load path
// where the setter was never called).
func (c *Config) IsBlacklisted(pubkey string) bool {
if c == nil {
set := c.blacklistSet()
if len(set) == 0 {
return false
}
return set[strings.ToLower(strings.TrimSpace(pubkey))]
}

// HasNodeBlacklist reports whether at least one (non-blank) pubkey is
// blacklisted. It reads the same atomic set as IsBlacklisted, so unlike
// len(c.NodeBlacklist) it is safe against a concurrent SetNodeBlacklist.
func (c *Config) HasNodeBlacklist() bool {
return len(c.blacklistSet()) > 0
}

// blacklistSet returns the active normalised blacklist set (shared,
// read-only), materialising it lazily from the JSON-loaded slice on first
// read. CAS-style: if another goroutine wins the race, ours is dropped.
func (c *Config) blacklistSet() map[string]bool {
if c == nil {
return nil
}
mp := c.blacklistSetPtr.Load()
if mp == nil {
// Lazy first-read materialisation from the JSON-loaded slice.
// CAS-style: if another goroutine wins the race, drop ours.
built := buildBlacklistSet(c.NodeBlacklist)
if c.blacklistSetPtr.CompareAndSwap(nil, &built) {
mp = &built
} else {
mp = c.blacklistSetPtr.Load()
}
}
if mp == nil || len(*mp) == 0 {
return false
if mp == nil {
return nil
}
return (*mp)[strings.ToLower(strings.TrimSpace(pubkey))]
return *mp
}

// IsNameHidden returns true if the given node name starts with any of the
Expand Down
107 changes: 3 additions & 104 deletions cmd/server/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2773,8 +2773,10 @@ func (s *Server) handleAnalyticsTopology(w http.ResponseWriter, r *http.Request)
return
}
}
// The store hands out its shared cached result; the filter never
// writes to it and returns a filtered copy when anything is hidden.
data := s.store.GetAnalyticsTopologyWithWindow(region, area, window)
if s.cfg != nil && len(s.cfg.NodeBlacklist) > 0 {
if s.cfg != nil && (s.cfg.HasNodeBlacklist() || len(s.cfg.hiddenPrefixes()) > 0) {
data = s.filterBlacklistedFromTopology(data)
}
writeJSON(w, data)
Expand Down Expand Up @@ -4204,109 +4206,6 @@ func constantTimeEqual(a, b string) bool {
return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
}

// filterBlacklistedFromTopology removes blacklisted + hidden-prefix node
// references (#1181) from the topology analytics response (TopRepeaters,
// TopPairs, BestPathList, MultiObsNodes, PerObserverReach).
func (s *Server) filterBlacklistedFromTopology(data map[string]interface{}) map[string]interface{} {
// Filter TopRepeaters
if repeaters, ok := data["topRepeaters"]; ok {
if arr, ok := repeaters.([]TopRepeater); ok {
var filtered []TopRepeater
for _, r := range arr {
if pk, ok := r.Pubkey.(string); ok && s.cfg.IsBlacklisted(pk) {
continue
}
if name, ok := r.Name.(string); ok && s.cfg.IsNameHidden(name) {
continue
}
filtered = append(filtered, r)
}
data["topRepeaters"] = filtered
}
}

// Filter TopPairs
if pairs, ok := data["topPairs"]; ok {
if arr, ok := pairs.([]TopPair); ok {
var filtered []TopPair
for _, p := range arr {
if pkA, ok := p.PubkeyA.(string); ok && s.cfg.IsBlacklisted(pkA) {
continue
}
if pkB, ok := p.PubkeyB.(string); ok && s.cfg.IsBlacklisted(pkB) {
continue
}
if nameA, ok := p.NameA.(string); ok && s.cfg.IsNameHidden(nameA) {
continue
}
if nameB, ok := p.NameB.(string); ok && s.cfg.IsNameHidden(nameB) {
continue
}
filtered = append(filtered, p)
}
data["topPairs"] = filtered
}
}

// Filter BestPathList
if paths, ok := data["bestPathList"]; ok {
if arr, ok := paths.([]BestPathEntry); ok {
var filtered []BestPathEntry
for _, p := range arr {
if pk, ok := p.Pubkey.(string); ok && s.cfg.IsBlacklisted(pk) {
continue
}
if pk, ok := p.Pubkey.(string); ok && s.isPubkeyHidden(pk) {
continue
}
filtered = append(filtered, p)
}
data["bestPathList"] = filtered
}
}

// Filter MultiObsNodes
if nodes, ok := data["multiObsNodes"]; ok {
if arr, ok := nodes.([]MultiObsNode); ok {
var filtered []MultiObsNode
for _, n := range arr {
if pk, ok := n.Pubkey.(string); ok && s.cfg.IsBlacklisted(pk) {
continue
}
if name, ok := n.Name.(string); ok && s.cfg.IsNameHidden(name) {
continue
}
filtered = append(filtered, n)
}
data["multiObsNodes"] = filtered
}
}

// Filter PerObserverReach
if reach, ok := data["perObserverReach"]; ok {
if m, ok := reach.(map[string]*ObserverReach); ok {
for k, v := range m {
for ri := range v.Rings {
var filteredNodes []ReachNode
for _, rn := range v.Rings[ri].Nodes {
if pk, ok := rn.Pubkey.(string); ok && s.cfg.IsBlacklisted(pk) {
continue
}
if name, ok := rn.Name.(string); ok && s.cfg.IsNameHidden(name) {
continue
}
filteredNodes = append(filteredNodes, rn)
}
v.Rings[ri].Nodes = filteredNodes
}
m[k] = v
}
}
}

return data
}

// filterBlacklistedFromSubpaths removes blacklisted node references from
// the subpaths analytics response.
func (s *Server) filterBlacklistedFromSubpaths(data map[string]interface{}) map[string]interface{} {
Expand Down
Loading
Loading