diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index acc25d7..07b7431 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -583,9 +583,9 @@ func (a *admin) getResponse_dot() []byte { infos[info.key] = info } } - addInfo(dht, "fillcolor=\"#ffffff\" style=filled", "Known in DHT") // white - addInfo(sessions, "fillcolor=\"#acf3fd\" style=filled", "Open session") // blue - addInfo(peers, "fillcolor=\"#ffffb5\" style=filled", "Connected peer") // yellow + addInfo(dht, "fillcolor=\"#ffffff\" style=filled", "Known in DHT") // white + addInfo(sessions, "fillcolor=\"#acf3fd\" style=filled", "Open session") // blue + addInfo(peers, "fillcolor=\"#ffffb5\" style=filled", "Connected peer") // yellow addInfo(append([]admin_nodeInfo(nil), *self), "fillcolor=\"#a5ff8a\" style=filled", "This node") // green // Get coords as a slice of strings, FIXME? this looks very fragile coordSlice := func(coords string) []string { diff --git a/src/yggdrasil/core.go b/src/yggdrasil/core.go index 9938266..ff9c360 100644 --- a/src/yggdrasil/core.go +++ b/src/yggdrasil/core.go @@ -172,7 +172,7 @@ func (c *Core) GetAddress() *net.IP { func (c *Core) GetSubnet() *net.IPNet { subnet := address_subnetForNodeID(c.GetNodeID())[:] subnet = append(subnet, 0, 0, 0, 0, 0, 0, 0, 0) - return &net.IPNet{ IP: subnet, Mask: net.CIDRMask(64, 128) } + return &net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)} } // Sets the output logger of the Yggdrasil node after startup. This may be diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index 1f03bee..94a93f3 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -106,6 +106,7 @@ func (t *dht) handleReq(req *dhtReq) { } func (t *dht) handleRes(res *dhtRes) { + t.core.searches.handleDHTRes(res) reqs, isIn := t.reqs[res.key] if !isIn { return diff --git a/src/yggdrasil/release.go b/src/yggdrasil/release.go index fdbe8d5..1b5e9ca 100644 --- a/src/yggdrasil/release.go +++ b/src/yggdrasil/release.go @@ -8,5 +8,5 @@ import "log" // Starts the function profiler. This is only supported when built with // '-tags build'. func StartProfiler(_ *log.Logger) error { - return errors.New("Release builds do not support -pprof, build using '-tags debug'") + return errors.New("Release builds do not support -pprof, build using '-tags debug'") } diff --git a/src/yggdrasil/router.go b/src/yggdrasil/router.go index 545a15b..d4e0c8e 100644 --- a/src/yggdrasil/router.go +++ b/src/yggdrasil/router.go @@ -128,12 +128,12 @@ func (r *router) sendPacket(bs []byte) { } sinfo, isIn := r.core.searches.searches[*nodeID] if !isIn { - sinfo = r.core.searches.createSearch(nodeID, mask) + sinfo = r.core.searches.newIterSearch(nodeID, mask) } if packet != nil { sinfo.packet = packet } - r.core.searches.sendSearch(sinfo) + r.core.searches.continueSearch(sinfo) } var sinfo *sessionInfo var isIn bool diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index d440661..ff5dd54 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -16,15 +16,17 @@ package yggdrasil // The iterative parallel lookups from kad can skip over some DHT blackholes // This hides bugs, which I don't want to do right now +import "sort" import "time" //import "fmt" type searchInfo struct { - dest *NodeID - mask *NodeID - time time.Time - packet []byte + dest *NodeID + mask *NodeID + time time.Time + packet []byte + toVisit []*dhtInfo } type searches struct { @@ -55,6 +57,93 @@ func (s *searches) createSearch(dest *NodeID, mask *NodeID) *searchInfo { //////////////////////////////////////////////////////////////////////////////// +func (s *searches) handleDHTRes(res *dhtRes) { + if s.checkDHTRes(res) { + return + } + s.addToSearch(res) +} + +func (s *searches) addToSearch(res *dhtRes) { + // TODO + sinfo, isIn := s.searches[res.dest] + if !isIn { + return + } + from := dhtInfo{key: res.key, coords: res.coords} + for _, info := range res.infos { + if dht_firstCloserThanThird(info.getNodeID(), &res.dest, from.getNodeID()) { + sinfo.toVisit = append(sinfo.toVisit, info) + } + } + sort.SliceStable(sinfo.toVisit, func(i, j int) bool { + return dht_firstCloserThanThird(sinfo.toVisit[i].getNodeID(), &res.dest, sinfo.toVisit[j].getNodeID()) + }) + s.doSearchStep(sinfo) +} + +func (s *searches) doSearchStep(sinfo *searchInfo) { + if len(sinfo.toVisit) == 0 || time.Since(sinfo.time) > 6*time.Second { + // Dead end or timeout, do cleanup + delete(s.searches, *sinfo.dest) + return + } else { + // Send to the next search target + var next *dhtInfo + next, sinfo.toVisit = sinfo.toVisit[0], sinfo.toVisit[1:] + s.core.dht.ping(next, sinfo.dest) + } +} + +func (s *searches) continueSearch(sinfo *searchInfo) { + if time.Since(sinfo.time) < time.Second { + return + } + sinfo.time = time.Now() + s.doSearchStep(sinfo) +} + +func (s *searches) newIterSearch(dest *NodeID, mask *NodeID) *searchInfo { + sinfo := s.createSearch(dest, mask) + sinfo.toVisit = s.core.dht.lookup(dest, false) + return sinfo +} + +func (s *searches) checkDHTRes(res *dhtRes) bool { + info, isIn := s.searches[res.dest] + if !isIn { + return false + } + them := getNodeID(&res.key) + var destMasked NodeID + var themMasked NodeID + for idx := 0; idx < NodeIDLen; idx++ { + destMasked[idx] = info.dest[idx] & info.mask[idx] + themMasked[idx] = them[idx] & info.mask[idx] + } + if themMasked != destMasked { + return false + } + // They match, so create a session and send a sessionRequest + sinfo, isIn := s.core.sessions.getByTheirPerm(&res.key) + if !isIn { + sinfo = s.core.sessions.createSession(&res.key) + _, isIn := s.core.sessions.getByTheirPerm(&res.key) + if !isIn { + panic("This should never happen") + } + } + // FIXME (!) replay attacks could mess with coords? Give it a handle (tstamp)? + sinfo.coords = res.coords + sinfo.packet = info.packet + s.core.sessions.ping(sinfo) + // Cleanup + delete(s.searches, res.dest) + return true +} + +//////////////////////////////////////////////////////////////////////////////// + type searchReq struct { key boxPubKey // Who I am coords []byte // Where I am