diff --git a/.circleci/config.yml b/.circleci/config.yml index 3e223e55..3f555df5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ version: 2.1 jobs: build-linux: docker: - - image: circleci/golang:1.13.3 + - image: circleci/golang:1.14.1 steps: - checkout @@ -106,11 +106,11 @@ jobs: echo -e "Host *\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config - run: - name: Install Go 1.13.3 + name: Install Go 1.14.1 command: | cd /tmp - curl -LO https://dl.google.com/go/go1.13.3.darwin-amd64.pkg - sudo installer -pkg /tmp/go1.13.3.darwin-amd64.pkg -target / + curl -LO https://dl.google.com/go/go1.14.1.darwin-amd64.pkg + sudo installer -pkg /tmp/go1.14.1.darwin-amd64.pkg -target / #- run: # name: Install Gomobile @@ -146,7 +146,7 @@ jobs: build-other: docker: - - image: circleci/golang:1.13.3 + - image: circleci/golang:1.14.1 steps: - checkout diff --git a/CHANGELOG.md b/CHANGELOG.md index ab3e9666..9b0216eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - in case of vulnerabilities. --> +## [0.3.14] - 2020-03-28 +### Fixed +- Fixes a memory leak that may occur if packets are incorrectly never removed from a switch queue + +### Changed +- Make DHT searches a bit more reliable by tracking the 16 most recently visited nodes + ## [0.3.13] - 2020-02-21 ### Added - Support for the Wireguard TUN driver, which now replaces Water and provides far better support and performance on Windows diff --git a/contrib/ansible/genkeys.go b/contrib/ansible/genkeys.go index 5213e8b8..1d7c222d 100644 --- a/contrib/ansible/genkeys.go +++ b/contrib/ansible/genkeys.go @@ -14,6 +14,7 @@ import ( "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/crypto" + "github.com/cheggaaa/pb/v3" ) var numHosts = flag.Int("hosts", 1, "number of host vars to generate") @@ -29,6 +30,8 @@ type keySet struct { func main() { flag.Parse() + bar := pb.StartNew(*keyTries * 2 + *numHosts) + if *numHosts > *keyTries { println("Can't generate less keys than hosts.") return @@ -37,21 +40,25 @@ func main() { var encryptionKeys []keySet for i := 0; i < *numHosts+1; i++ { encryptionKeys = append(encryptionKeys, newBoxKey()) + bar.Increment() } encryptionKeys = sortKeySetArray(encryptionKeys) for i := 0; i < *keyTries-*numHosts-1; i++ { encryptionKeys[0] = newBoxKey() encryptionKeys = bubbleUpTo(encryptionKeys, 0) + bar.Increment() } var signatureKeys []keySet for i := 0; i < *numHosts+1; i++ { signatureKeys = append(signatureKeys, newSigKey()) + bar.Increment() } signatureKeys = sortKeySetArray(signatureKeys) for i := 0; i < *keyTries-*numHosts-1; i++ { signatureKeys[0] = newSigKey() signatureKeys = bubbleUpTo(signatureKeys, 0) + bar.Increment() } os.MkdirAll("host_vars", 0755) @@ -76,7 +83,9 @@ func main() { defer file.Close() file.WriteString(fmt.Sprintf("vault_yggdrasil_encryption_private_key: %v\n", hex.EncodeToString(encryptionKeys[i].priv))) file.WriteString(fmt.Sprintf("vault_yggdrasil_signing_private_key: %v\n", hex.EncodeToString(signatureKeys[i].priv))) + bar.Increment() } + bar.Finish() } func newBoxKey() keySet { diff --git a/contrib/apparmor/usr.bin.yggdrasil b/contrib/apparmor/usr.bin.yggdrasil index e31a27b0..3dea142c 100644 --- a/contrib/apparmor/usr.bin.yggdrasil +++ b/contrib/apparmor/usr.bin.yggdrasil @@ -1,10 +1,11 @@ -# Last Modified: Sat Mar 9 06:08:02 2019 +# Last Modified: Tue Mar 10 16:38:14 2020 #include /usr/bin/yggdrasil { #include capability net_admin, + capability net_raw, network inet stream, network inet dgram, @@ -14,6 +15,7 @@ /lib/@{multiarch}/ld-*.so mr, /proc/sys/net/core/somaxconn r, + owner /sys/kernel/mm/transparent_hugepage/hpage_pmd_size r, /dev/net/tun rw, /usr/bin/yggdrasil mr, diff --git a/go.mod b/go.mod index ea1f309b..04cd5b90 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.13 require ( github.com/Arceliar/phony v0.0.0-20191006174943-d0c68492aca0 + github.com/cheggaaa/pb/v3 v3.0.4 github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 github.com/hashicorp/go-syslog v1.0.0 github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible @@ -11,10 +12,10 @@ require ( github.com/mitchellh/mapstructure v1.1.2 github.com/vishvananda/netlink v1.0.0 github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f // indirect - golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 - golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 - golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7 - golang.org/x/text v0.3.2 - golang.zx2c4.com/wireguard v0.0.20200121 - golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25 + golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d + golang.org/x/net v0.0.0-20200301022130-244492dfa37a + golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 + golang.org/x/text v0.3.3-0.20191230102452-929e72ca90de + golang.zx2c4.com/wireguard v0.0.20200320 + golang.zx2c4.com/wireguard/windows v0.1.0 ) diff --git a/go.sum b/go.sum index 63638837..6d08e865 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,11 @@ github.com/Arceliar/phony v0.0.0-20191006174943-d0c68492aca0 h1:p3puK8Sl2xK+2FnnIvY/C0N1aqJo2kbEsdAzU+Tnv48= github.com/Arceliar/phony v0.0.0-20191006174943-d0c68492aca0/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= +github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= +github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= +github.com/cheggaaa/pb/v3 v3.0.4 h1:QZEPYOj2ix6d5oEg63fbHmpolrnNiwjUsk+h74Yt4bM= +github.com/cheggaaa/pb/v3 v3.0.4/go.mod h1:7rgWxLrAUcFMkvJuv09+DYi7mMUYi8nO9iOWcvGJPfw= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 h1:WD8iJ37bRNwvETMfVTusVSAi0WdXTpfNVGY2aHycNKY= github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= @@ -8,9 +14,17 @@ github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible h1:b github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 h1:YnZmFjg0Nvk8851WTVWlqMC1ecJH07Ctz+Ezxx4u54g= github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0/go.mod h1:rUi0/YffDo1oXBOGn1KRq7Fr07LX48XEBecQnmwjsAo= -github.com/lxn/walk v0.0.0-20191031081659-c0bb82ae46cb/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= -github.com/lxn/win v0.0.0-20191024121223-cc00c7492fe1 h1:h0wbuSK8xUNmMwDdCxZx2OLdkVck6Bb31zj4CxCN5I4= -github.com/lxn/win v0.0.0-20191024121223-cc00c7492fe1/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA= +github.com/lxn/walk v0.0.0-20191128110447-55ccb3a9f5c1 h1:/QwQcwWVOQXcoNuV9tHx30gQ3q7jCE/rKcGjwzsa5tg= +github.com/lxn/walk v0.0.0-20191128110447-55ccb3a9f5c1/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= +github.com/lxn/win v0.0.0-20191128105842-2da648fda5b4 h1:5BmtGkQbch91lglMHQ9JIDGiYCL3kBRBA0ItZTvOcEI= +github.com/lxn/win v0.0.0-20191128105842-2da648fda5b4/go.mod h1:ouWl4wViUNh8tPSIwxTVMuS014WakR1hqvBc2I0bMoA= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM= @@ -19,28 +33,30 @@ github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f h1:nBX3nTcmxEtHS github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876 h1:sKJQZMuxjOAR/Uo2LBfU90onWEf1dF4C+0hPJCc9Mpc= -golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw= +golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191028085509-fe3aa8a45271/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7 h1:/W9OPMnnpmFXHYkcp2rQsbFUbRlRzfECQjmAFiOyHE8= -golang.org/x/sys v0.0.0-20200103143344-a1369afcdac7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200301040627-c5d0d7b4ec88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3-0.20191230102452-929e72ca90de h1:aYKJLPSrddB2N7/6OKyFqJ337SXpo61bBuvO5p1+7iY= +golang.org/x/text v0.3.3-0.20191230102452-929e72ca90de/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1 h1:KxtBKNgJUQG8vwZzJKkwBGOcqp95xLu6A6KIMde1kl0= -golang.zx2c4.com/wireguard v0.0.20191013-0.20191030132932-4cdf805b29b1/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= -golang.zx2c4.com/wireguard v0.0.20200121 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTBCAg/Qf8= -golang.zx2c4.com/wireguard v0.0.20200121/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= -golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25 h1:TreP+furSwdqoSToFrwb1S5cwxb7jhOsnwj2MsDeT+4= -golang.zx2c4.com/wireguard/windows v0.0.35-0.20191123133119-cb4a03094c25/go.mod h1:EO8KCpT944a9CnwHJLZ1sl84FfIrY42fP/fcXUuYhKM= +golang.zx2c4.com/wireguard v0.0.20200122-0.20200214175355-9cbcff10dd3e/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4= +golang.zx2c4.com/wireguard v0.0.20200320 h1:1vE6zVeO7fix9cJX1Z9ZQ+ikPIIx7vIyU0o0tLDD88g= +golang.zx2c4.com/wireguard v0.0.20200320/go.mod h1:lDian4Sw4poJ04SgHh35nzMVwGSYlPumkdnHcucAQoY= +golang.zx2c4.com/wireguard/windows v0.1.0 h1:742izt2DAJBpIQT+DvrzN58P9p7fO4BUFOgMzY9qVhw= +golang.zx2c4.com/wireguard/windows v0.1.0/go.mod h1:EK7CxrFnicmYJ0ZCF6crBh2/EMMeSxMlqgLlwN0Kv9s= diff --git a/src/yggdrasil/dht.go b/src/yggdrasil/dht.go index 013fd1e4..8efc549f 100644 --- a/src/yggdrasil/dht.go +++ b/src/yggdrasil/dht.go @@ -260,9 +260,7 @@ func (t *dht) handleRes(res *dhtRes) { key: res.Key, coords: res.Coords, } - if t.isImportant(&rinfo) { - t.insert(&rinfo) - } + t.insert(&rinfo) for _, info := range res.Infos { if *info.getNodeID() == t.nodeID { continue diff --git a/src/yggdrasil/search.go b/src/yggdrasil/search.go index 584a056d..91f0490c 100644 --- a/src/yggdrasil/search.go +++ b/src/yggdrasil/search.go @@ -4,15 +4,13 @@ package yggdrasil // The basic idea is as follows: // We may know a NodeID (with a mask) and want to connect -// We begin a search by initializing a list of all nodes in our DHT, sorted by closest to the destination -// We then iteratively ping nodes from the search, marking each pinged node as visited -// We add any unvisited nodes from ping responses to the search, truncating to some maximum search size -// This stops when we either run out of nodes to ping (we hit a dead end where we can't make progress without going back), or we reach the destination -// A new search packet is sent immediately after receiving a response -// A new search packet is sent periodically, once per second, in case a packet was dropped (this slowly causes the search to become parallel if the search doesn't timeout but also doesn't finish within 1 second for whatever reason) - -// TODO? -// Some kind of max search steps, in case the node is offline, so we don't crawl through too much of the network looking for a destination that isn't there? +// We begin a search by sending a dht lookup to ourself +// Each time a node responds, we sort the results and filter to only include useful nodes +// We then periodically send a packet to the first node from the list (after re-filtering) +// This happens in parallel for each node that replies +// Meanwhile, we keep a list of the (up to) 16 closest nodes to the destination that we've visited +// We only consider an unvisited node useful if either the list isn't full or the unvisited node is closer to the destination than the furthest node on the list +// That gives the search some chance to recover if it hits a dead end where a node doesn't know everyone it should import ( "errors" @@ -24,7 +22,8 @@ import ( // This defines the time after which we time out a search (so it can restart). const search_RETRY_TIME = 3 * time.Second -const search_STEP_TIME = 100 * time.Millisecond +const search_STEP_TIME = time.Second +const search_MAX_RESULTS = dht_lookup_size // Information about an ongoing search. // Includes the target NodeID, the bitmask to match it to an IP, and the list of nodes to visit / already visited. @@ -33,7 +32,7 @@ type searchInfo struct { dest crypto.NodeID mask crypto.NodeID time time.Time - visited crypto.NodeID // Closest address visited so far + visited []*crypto.NodeID // Closest addresses visited so far callback func(*sessionInfo, error) // TODO context.Context for timeout and cancellation send uint64 // log number of requests sent @@ -75,6 +74,9 @@ func (s *searches) createSearch(dest *crypto.NodeID, mask *crypto.NodeID, callba // If there is, it adds the response info to the search and triggers a new search step. // If there's no ongoing search, or we if the dhtRes finished the search (it was from the target node), then don't do anything more. func (sinfo *searchInfo) handleDHTRes(res *dhtRes) { + if nfo := sinfo.searches.searches[sinfo.dest]; nfo != sinfo { + return // already done + } if res != nil { sinfo.recv++ if sinfo.checkDHTRes(res) { @@ -105,16 +107,32 @@ func (sinfo *searchInfo) doSearchStep(infos []*dhtInfo) { // Get a list of search targets that are close enough to the destination to try // Requires an initial list as input func (sinfo *searchInfo) getAllowedInfos(infos []*dhtInfo) []*dhtInfo { + var temp []*dhtInfo + for _, info := range infos { + if false && len(sinfo.visited) < search_MAX_RESULTS { + // We're not full on results yet, so don't block anything yet + } else if !dht_ordered(&sinfo.dest, info.getNodeID(), sinfo.visited[len(sinfo.visited)-1]) { + // Too far away + continue + } + var known bool + for _, nfo := range sinfo.visited { + if *nfo == *info.getNodeID() { + known = true + break + } + } + if !known { + temp = append(temp, info) + } + } + infos = append(infos[:0], temp...) // restrict to only the allowed infos sort.SliceStable(infos, func(i, j int) bool { // Should return true if i is closer to the destination than j return dht_ordered(&sinfo.dest, infos[i].getNodeID(), infos[j].getNodeID()) - }) - // Remove anything too far away to be useful - for idx, info := range infos { - if !dht_ordered(&sinfo.dest, info.getNodeID(), &sinfo.visited) { - infos = infos[:idx] - break - } + }) // Sort infos to start with the closest + if len(infos) > search_MAX_RESULTS { + infos = infos[:search_MAX_RESULTS] // Limit max number of infos } return infos } @@ -164,6 +182,7 @@ func (sinfo *searchInfo) startSearch() { if elapsed > search_RETRY_TIME { // cleanup delete(sinfo.searches.searches, sinfo.dest) + sinfo.searches.router.core.log.Debugln("search timeout:", &sinfo.dest, sinfo.send, sinfo.recv) sinfo.callback(nil, errors.New("search reached dead end")) return } @@ -176,7 +195,7 @@ func (sinfo *searchInfo) startSearch() { // Calls create search, and initializes the iterative search parts of the struct before returning it. func (s *searches) newIterSearch(dest *crypto.NodeID, mask *crypto.NodeID, callback func(*sessionInfo, error)) *searchInfo { sinfo := s.createSearch(dest, mask, callback) - sinfo.visited = s.router.dht.nodeID + sinfo.visited = append(sinfo.visited, &s.router.dht.nodeID) return sinfo } @@ -185,13 +204,29 @@ func (s *searches) newIterSearch(dest *crypto.NodeID, mask *crypto.NodeID, callb // Otherwise return false. func (sinfo *searchInfo) checkDHTRes(res *dhtRes) bool { from := dhtInfo{key: res.Key, coords: res.Coords} - if *from.getNodeID() != sinfo.visited && dht_ordered(&sinfo.dest, from.getNodeID(), &sinfo.visited) { - // Closer to the destination, so update visited - sinfo.searches.router.core.log.Debugln("Updating search:", &sinfo.dest, from.getNodeID(), sinfo.send, sinfo.recv) - sinfo.visited = *from.getNodeID() - sinfo.time = time.Now() - } them := from.getNodeID() + var known bool + for _, v := range sinfo.visited { + if *v == *them { + known = true + break + } + } + if !known { + if len(sinfo.visited) < search_MAX_RESULTS || dht_ordered(&sinfo.dest, them, sinfo.visited[len(sinfo.visited)-1]) { + // Closer to the destination than the threshold, so update visited + sinfo.searches.router.core.log.Debugln("Updating search:", &sinfo.dest, them, sinfo.send, sinfo.recv) + sinfo.visited = append(sinfo.visited, them) + sort.SliceStable(sinfo.visited, func(i, j int) bool { + // Should return true if i is closer to the destination than j + return dht_ordered(&sinfo.dest, sinfo.visited[i], sinfo.visited[j]) + }) // Sort infos to start with the closest + if len(sinfo.visited) > search_MAX_RESULTS { + sinfo.visited = sinfo.visited[:search_MAX_RESULTS] + } + sinfo.time = time.Now() + } + } var destMasked crypto.NodeID var themMasked crypto.NodeID for idx := 0; idx < crypto.NodeIDLen; idx++ {