5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-22 14:10:28 +00:00

Version 0.3.15 (#731)

This commit is contained in:
Neil Alexander 2020-09-27 15:50:58 +01:00
commit d3672545a3
No known key found for this signature in database
GPG Key ID: A02A2019A2BB0944
31 changed files with 387 additions and 301 deletions

View File

@ -3,6 +3,19 @@
# Check https://circleci.com/docs/2.0/language-go/ for more details # Check https://circleci.com/docs/2.0/language-go/ for more details
version: 2.1 version: 2.1
jobs: jobs:
lint:
docker:
- image: circleci/golang:1.14.1
steps:
- checkout
- run:
name: Run golangci-lint
command: |
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.31.0
golangci-lint run
build-linux: build-linux:
docker: docker:
- image: circleci/golang:1.14.1 - image: circleci/golang:1.14.1
@ -201,9 +214,16 @@ workflows:
version: 2.1 version: 2.1
build: build:
jobs: jobs:
- build-linux - lint
- build-macos - build-linux:
- build-other requires:
- lint
- build-macos:
requires:
- lint
- build-other:
requires:
- lint
- upload: - upload:
requires: requires:
- build-linux - build-linux

10
.golangci.yml Normal file
View File

@ -0,0 +1,10 @@
run:
build-tags:
- lint
issues-exit-code: 0 # TODO: change this to 1 when we want it to fail builds
skip-dirs:
- contrib/
- misc/
linters:
disable:
- gocyclo

View File

@ -25,6 +25,27 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- in case of vulnerabilities. - in case of vulnerabilities.
--> -->
## [0.3.15] - 2020-09-27
### Added
- Support for pinning remote public keys in peering strings has been added, e.g.
- By signing public key: `tcp://host:port?ed25519=key`
- By encryption public key: `tcp://host:port?curve25519=key`
- By both: `tcp://host:port?ed25519=key&curve25519=key`
- By multiple, in case of DNS round-robin or similar: `tcp://host:port?curve25519=key&curve25519=key&ed25519=key&ed25519=key`
- Some checks to prevent Yggdrasil-over-Yggdrasil peerings have been added
- Added support for SOCKS proxy authentication, e.g. `socks://user@password:host/...`
### Fixed
- Some bugs in the multicast code that could cause unnecessary CPU usage have been fixed
- A possible multicast deadlock on macOS when enumerating interfaces has been fixed
- A deadlock in the connection code has been fixed
- Updated HJSON dependency that caused some build problems
### Changed
- `DisconnectPeer` and `RemovePeer` have been separated and implemented properly now
- Less nodes are stored in the DHT now, reducing ambient network traffic and possible instability
- Default config file for FreeBSD is now at `/usr/local/etc/yggdrasil.conf` instead of `/etc/yggdrasil.conf`
## [0.3.14] - 2020-03-28 ## [0.3.14] - 2020-03-28
### Fixed ### Fixed
- Fixes a memory leak that may occur if packets are incorrectly never removed from a switch queue - Fixes a memory leak that may occur if packets are incorrectly never removed from a switch queue

View File

@ -26,7 +26,7 @@ some of the below:
- Linux - Linux
- `.deb` and `.rpm` packages are built by CI for Debian and Red Hat-based - `.deb` and `.rpm` packages are built by CI for Debian and Red Hat-based
distributions distributions
- Void and Arch packages also available within their respective repositories - Arch, Nix, Void packages also available within their respective repositories
- macOS - macOS
- `.pkg` packages are built by CI - `.pkg` packages are built by CI
- Ubiquiti EdgeOS - Ubiquiti EdgeOS

View File

@ -51,7 +51,7 @@ func main() {
for { for {
newKey := <-newKeys newKey := <-newKeys
if isBetter(currentBest[:], newKey.id[:]) || len(currentBest) == 0 { if isBetter(currentBest, newKey.id[:]) || len(currentBest) == 0 {
currentBest = newKey.id currentBest = newKey.id
for _, channel := range threadChannels { for _, channel := range threadChannels {
select { select {
@ -61,13 +61,13 @@ func main() {
fmt.Println("--------------------------------------------------------------------------------") fmt.Println("--------------------------------------------------------------------------------")
switch { switch {
case *doSig: case *doSig:
fmt.Println("sigPriv:", hex.EncodeToString(newKey.priv[:])) fmt.Println("sigPriv:", hex.EncodeToString(newKey.priv))
fmt.Println("sigPub:", hex.EncodeToString(newKey.pub[:])) fmt.Println("sigPub:", hex.EncodeToString(newKey.pub))
fmt.Println("TreeID:", hex.EncodeToString(newKey.id[:])) fmt.Println("TreeID:", hex.EncodeToString(newKey.id))
default: default:
fmt.Println("boxPriv:", hex.EncodeToString(newKey.priv[:])) fmt.Println("boxPriv:", hex.EncodeToString(newKey.priv))
fmt.Println("boxPub:", hex.EncodeToString(newKey.pub[:])) fmt.Println("boxPub:", hex.EncodeToString(newKey.pub))
fmt.Println("NodeID:", hex.EncodeToString(newKey.id[:])) fmt.Println("NodeID:", hex.EncodeToString(newKey.id))
fmt.Println("IP:", newKey.ip) fmt.Println("IP:", newKey.ip)
} }
} }
@ -76,11 +76,8 @@ func main() {
func isBetter(oldID, newID []byte) bool { func isBetter(oldID, newID []byte) bool {
for idx := range oldID { for idx := range oldID {
if newID[idx] > oldID[idx] { if newID[idx] != oldID[idx] {
return true return newID[idx] > oldID[idx]
}
if newID[idx] < oldID[idx] {
return false
} }
} }
return false return false

View File

@ -60,8 +60,8 @@ func readConfig(useconf *bool, useconffile *string, normaliseconf *bool) *config
// throwing everywhere when it's converting things into UTF-16 for the hell // throwing everywhere when it's converting things into UTF-16 for the hell
// of it - remove it and decode back down into UTF-8. This is necessary // of it - remove it and decode back down into UTF-8. This is necessary
// because hjson doesn't know what to do with UTF-16 and will panic // because hjson doesn't know what to do with UTF-16 and will panic
if bytes.Compare(conf[0:2], []byte{0xFF, 0xFE}) == 0 || if bytes.Equal(conf[0:2], []byte{0xFF, 0xFE}) ||
bytes.Compare(conf[0:2], []byte{0xFE, 0xFF}) == 0 { bytes.Equal(conf[0:2], []byte{0xFE, 0xFF}) {
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM) utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
decoder := utf.NewDecoder() decoder := utf.NewDecoder()
conf, err = decoder.Bytes(conf) conf, err = decoder.Bytes(conf)
@ -222,7 +222,7 @@ func main() {
getNodeID := func() *crypto.NodeID { getNodeID := func() *crypto.NodeID {
if pubkey, err := hex.DecodeString(cfg.EncryptionPublicKey); err == nil { if pubkey, err := hex.DecodeString(cfg.EncryptionPublicKey); err == nil {
var box crypto.BoxPubKey var box crypto.BoxPubKey
copy(box[:], pubkey[:]) copy(box[:], pubkey)
return crypto.GetNodeID(&box) return crypto.GetNodeID(&box)
} }
return nil return nil
@ -328,9 +328,9 @@ func main() {
// deferred Stop function above will run which will shut down TUN/TAP. // deferred Stop function above will run which will shut down TUN/TAP.
for { for {
select { select {
case _ = <-c: case <-c:
goto exit goto exit
case _ = <-r: case <-r:
if *useconffile != "" { if *useconffile != "" {
cfg = readConfig(useconf, useconffile, normaliseconf) cfg = readConfig(useconf, useconffile, normaliseconf)
logger.Infoln("Reloading configuration from", *useconffile) logger.Infoln("Reloading configuration from", *useconffile)

View File

@ -78,8 +78,8 @@ func run() int {
if *server == endpoint { if *server == endpoint {
if config, err := ioutil.ReadFile(defaults.GetDefaults().DefaultConfigFile); err == nil { if config, err := ioutil.ReadFile(defaults.GetDefaults().DefaultConfigFile); err == nil {
if bytes.Compare(config[0:2], []byte{0xFF, 0xFE}) == 0 || if bytes.Equal(config[0:2], []byte{0xFF, 0xFE}) ||
bytes.Compare(config[0:2], []byte{0xFE, 0xFF}) == 0 { bytes.Equal(config[0:2], []byte{0xFE, 0xFF}) {
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM) utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
decoder := utf.NewDecoder() decoder := utf.NewDecoder()
config, err = decoder.Bytes(config) config, err = decoder.Bytes(config)

View File

@ -1,97 +0,0 @@
package main
/*
This is a small utility that is designed to accompany the vyatta-yggdrasil
package. It takes a HJSON configuration file, makes changes to it based on
the command line arguments, and then spits out an updated file.
*/
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"strconv"
"github.com/hjson/hjson-go"
"golang.org/x/text/encoding/unicode"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
)
type nodeConfig = config.NodeConfig
func main() {
useconffile := flag.String("useconffile", "/etc/yggdrasil.conf", "update config at specified file path")
flag.Parse()
cfg := nodeConfig{}
var config []byte
var err error
config, err = ioutil.ReadFile(*useconffile)
if err != nil {
panic(err)
}
if bytes.Compare(config[0:2], []byte{0xFF, 0xFE}) == 0 ||
bytes.Compare(config[0:2], []byte{0xFE, 0xFF}) == 0 {
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
decoder := utf.NewDecoder()
config, err = decoder.Bytes(config)
if err != nil {
panic(err)
}
}
var dat map[string]interface{}
if err := hjson.Unmarshal(config, &dat); err != nil {
panic(err)
}
confJson, err := json.Marshal(dat)
if err != nil {
panic(err)
}
json.Unmarshal(confJson, &cfg)
switch flag.Arg(0) {
case "setMTU":
cfg.IfMTU, err = strconv.Atoi(flag.Arg(1))
if err != nil {
cfg.IfMTU = 1280
}
if mtu, _ := strconv.Atoi(flag.Arg(1)); mtu < 1280 {
cfg.IfMTU = 1280
}
case "setIfName":
cfg.IfName = flag.Arg(1)
case "setListen":
cfg.Listen = flag.Arg(1)
case "setAdminListen":
cfg.AdminListen = flag.Arg(1)
case "setIfTapMode":
if flag.Arg(1) == "true" {
cfg.IfTAPMode = true
} else {
cfg.IfTAPMode = false
}
case "addPeer":
found := false
for _, v := range cfg.Peers {
if v == flag.Arg(1) {
found = true
}
}
if !found {
cfg.Peers = append(cfg.Peers, flag.Arg(1))
}
case "removePeer":
for k, v := range cfg.Peers {
if v == flag.Arg(1) {
cfg.Peers = append(cfg.Peers[:k], cfg.Peers[k+1:]...)
}
}
}
bs, err := hjson.Marshal(cfg)
if err != nil {
panic(err)
}
fmt.Println(string(bs))
return
}

3
go.mod
View File

@ -7,11 +7,12 @@ require (
github.com/cheggaaa/pb/v3 v3.0.4 github.com/cheggaaa/pb/v3 v3.0.4
github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8
github.com/hashicorp/go-syslog v1.0.0 github.com/hashicorp/go-syslog v1.0.0
github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible github.com/hjson/hjson-go v3.0.2-0.20200316202735-d5d0e8b0617d+incompatible
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0 github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0
github.com/mitchellh/mapstructure v1.1.2 github.com/mitchellh/mapstructure v1.1.2
github.com/vishvananda/netlink v1.0.0 github.com/vishvananda/netlink v1.0.0
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f // indirect github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f // indirect
github.com/yggdrasil-network/yggdrasil-extras v0.0.0-20200525205615-6c8a4a2e8855 // indirect
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d
golang.org/x/net v0.0.0-20200301022130-244492dfa37a golang.org/x/net v0.0.0-20200301022130-244492dfa37a
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527

6
go.sum
View File

@ -10,8 +10,8 @@ github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8 h1:WD8iJ37bRNwvETMfVTu
github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U= github.com/gologme/log v0.0.0-20181207131047-4e5d8ccb38e8/go.mod h1:gq31gQ8wEHkR+WekdWsqDuf8pXTUZA9BnnzTuPz1Y9U=
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible h1:bLQ2Ve+eW65id3b8xEMQiAwJT4qGZeywAEMLvXjznvw= github.com/hjson/hjson-go v3.0.2-0.20200316202735-d5d0e8b0617d+incompatible h1:v6BPcb9q9U6JDVsuizxBr/piVB/2Y1Q5GWoBybvZVWI=
github.com/hjson/hjson-go v3.0.1-0.20190209023717-9147687966d9+incompatible/go.mod h1:qsetwF8NlsTsOTwZTApNlTCerV+b2GjYRRcIk4JMFio= github.com/hjson/hjson-go v3.0.2-0.20200316202735-d5d0e8b0617d+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 h1:YnZmFjg0Nvk8851WTVWlqMC1ecJH07Ctz+Ezxx4u54g=
github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0/go.mod h1:rUi0/YffDo1oXBOGn1KRq7Fr07LX48XEBecQnmwjsAo= github.com/kardianos/minwinsvc v0.0.0-20151122163309-cad6b2b879b0/go.mod h1:rUi0/YffDo1oXBOGn1KRq7Fr07LX48XEBecQnmwjsAo=
github.com/lxn/walk v0.0.0-20191128110447-55ccb3a9f5c1 h1:/QwQcwWVOQXcoNuV9tHx30gQ3q7jCE/rKcGjwzsa5tg= github.com/lxn/walk v0.0.0-20191128110447-55ccb3a9f5c1 h1:/QwQcwWVOQXcoNuV9tHx30gQ3q7jCE/rKcGjwzsa5tg=
@ -31,6 +31,8 @@ github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCO
github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f h1:nBX3nTcmxEtHSERBJaIo1Qa26VwRaopnZmfDQUXsF4I= github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f h1:nBX3nTcmxEtHSERBJaIo1Qa26VwRaopnZmfDQUXsF4I=
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/yggdrasil-network/yggdrasil-extras v0.0.0-20200525205615-6c8a4a2e8855 h1:xLQihK8bAKOEDii/Z39dHTgSJzetm2TQ1YKRPRX87R4=
github.com/yggdrasil-network/yggdrasil-extras v0.0.0-20200525205615-6c8a4a2e8855/go.mod h1:xQdsh08Io6nV4WRnOVTe6gI8/2iTvfLDQ0CYa5aMt+I=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=

View File

@ -1,20 +1,24 @@
// +build !lint
package main package main
import "fmt" import (
import "bufio" "bufio"
import "os" "flag"
import "strings" "fmt"
import "strconv" "os"
import "time" "runtime"
"runtime/pprof"
"strconv"
"strings"
"time"
import "runtime" "github.com/gologme/log"
import "runtime/pprof"
import "flag"
import "github.com/gologme/log" . "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil"
import . "github.com/yggdrasil-network/yggdrasil-go/src/yggdrasil" . "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
import . "github.com/yggdrasil-network/yggdrasil-go/src/crypto" )
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -53,7 +53,7 @@ func (a *AdminSocket) AddHandler(name string, args []string, handlerfunc func(In
return nil return nil
} }
// init runs the initial admin setup. // Init runs the initial admin setup.
func (a *AdminSocket) Init(c *yggdrasil.Core, state *config.NodeState, log *log.Logger, options interface{}) error { func (a *AdminSocket) Init(c *yggdrasil.Core, state *config.NodeState, log *log.Logger, options interface{}) error {
a.core = c a.core = c
a.log = log a.log = log
@ -181,32 +181,57 @@ func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) {
in["uri"].(string), in["uri"].(string),
}, },
}, nil }, nil
} else {
return Info{
"not_added": []string{
in["uri"].(string),
},
}, errors.New("Failed to add peer")
} }
return Info{
"not_added": []string{
in["uri"].(string),
},
}, errors.New("Failed to add peer")
}) })
a.AddHandler("removePeer", []string{"port"}, func(in Info) (Info, error) { a.AddHandler("disconnectPeer", []string{"port"}, func(in Info) (Info, error) {
port, err := strconv.ParseInt(fmt.Sprint(in["port"]), 10, 64) port, err := strconv.ParseInt(fmt.Sprint(in["port"]), 10, 64)
if err != nil { if err != nil {
return Info{}, err return Info{}, err
} }
if a.core.DisconnectPeer(uint64(port)) == nil { if a.core.DisconnectPeer(uint64(port)) == nil {
return Info{ return Info{
"removed": []string{ "disconnected": []string{
fmt.Sprint(port), fmt.Sprint(port),
}, },
}, nil }, nil
} else { } else {
return Info{ return Info{
"not_removed": []string{ "not_disconnected": []string{
fmt.Sprint(port), fmt.Sprint(port),
}, },
}, errors.New("Failed to disconnect peer")
}
})
a.AddHandler("removePeer", []string{"uri", "[interface]"}, func(in Info) (Info, error) {
// Set sane defaults
intf := ""
// Has interface been specified?
if itf, ok := in["interface"]; ok {
intf = itf.(string)
}
if a.core.RemovePeer(in["uri"].(string), intf) == nil {
return Info{
"removed": []string{
in["uri"].(string),
},
}, nil
} else {
return Info{
"not_removed": []string{
in["uri"].(string),
},
}, errors.New("Failed to remove peer") }, errors.New("Failed to remove peer")
} }
return Info{
"not_removed": []string{
in["uri"].(string),
},
}, errors.New("Failed to remove peer")
}) })
a.AddHandler("getAllowedEncryptionPublicKeys", []string{}, func(in Info) (Info, error) { a.AddHandler("getAllowedEncryptionPublicKeys", []string{}, func(in Info) (Info, error) {
return Info{"allowed_box_pubs": a.core.GetAllowedEncryptionPublicKeys()}, nil return Info{"allowed_box_pubs": a.core.GetAllowedEncryptionPublicKeys()}, nil
@ -218,13 +243,12 @@ func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) {
in["box_pub_key"].(string), in["box_pub_key"].(string),
}, },
}, nil }, nil
} else {
return Info{
"not_added": []string{
in["box_pub_key"].(string),
},
}, errors.New("Failed to add allowed key")
} }
return Info{
"not_added": []string{
in["box_pub_key"].(string),
},
}, errors.New("Failed to add allowed key")
}) })
a.AddHandler("removeAllowedEncryptionPublicKey", []string{"box_pub_key"}, func(in Info) (Info, error) { a.AddHandler("removeAllowedEncryptionPublicKey", []string{"box_pub_key"}, func(in Info) (Info, error) {
if a.core.RemoveAllowedEncryptionPublicKey(in["box_pub_key"].(string)) == nil { if a.core.RemoveAllowedEncryptionPublicKey(in["box_pub_key"].(string)) == nil {
@ -233,13 +257,12 @@ func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) {
in["box_pub_key"].(string), in["box_pub_key"].(string),
}, },
}, nil }, nil
} else {
return Info{
"not_removed": []string{
in["box_pub_key"].(string),
},
}, errors.New("Failed to remove allowed key")
} }
return Info{
"not_removed": []string{
in["box_pub_key"].(string),
},
}, errors.New("Failed to remove allowed key")
}) })
a.AddHandler("dhtPing", []string{"box_pub_key", "coords", "[target]"}, func(in Info) (Info, error) { a.AddHandler("dhtPing", []string{"box_pub_key", "coords", "[target]"}, func(in Info) (Info, error) {
var reserr error var reserr error
@ -250,10 +273,10 @@ func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) {
coords := util.DecodeCoordString(in["coords"].(string)) coords := util.DecodeCoordString(in["coords"].(string))
var boxPubKey crypto.BoxPubKey var boxPubKey crypto.BoxPubKey
if b, err := hex.DecodeString(in["box_pub_key"].(string)); err == nil { if b, err := hex.DecodeString(in["box_pub_key"].(string)); err == nil {
copy(boxPubKey[:], b[:]) copy(boxPubKey[:], b)
if n, err := hex.DecodeString(in["target"].(string)); err == nil { if n, err := hex.DecodeString(in["target"].(string)); err == nil {
var targetNodeID crypto.NodeID var targetNodeID crypto.NodeID
copy(targetNodeID[:], n[:]) copy(targetNodeID[:], n)
result, reserr = a.core.DHTPing(boxPubKey, coords, &targetNodeID) result, reserr = a.core.DHTPing(boxPubKey, coords, &targetNodeID)
} else { } else {
result, reserr = a.core.DHTPing(boxPubKey, coords, nil) result, reserr = a.core.DHTPing(boxPubKey, coords, nil)
@ -287,14 +310,13 @@ func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) {
var jsoninfo interface{} var jsoninfo interface{}
if err := json.Unmarshal(nodeinfo, &jsoninfo); err != nil { if err := json.Unmarshal(nodeinfo, &jsoninfo); err != nil {
return Info{}, err return Info{}, err
} else {
return Info{"nodeinfo": jsoninfo}, nil
} }
return Info{"nodeinfo": jsoninfo}, nil
} else if in["box_pub_key"] == nil || in["coords"] == nil { } else if in["box_pub_key"] == nil || in["coords"] == nil {
return Info{}, errors.New("Expecting both box_pub_key and coords") return Info{}, errors.New("Expecting both box_pub_key and coords")
} else { } else {
if b, err := hex.DecodeString(in["box_pub_key"].(string)); err == nil { if b, err := hex.DecodeString(in["box_pub_key"].(string)); err == nil {
copy(boxPubKey[:], b[:]) copy(boxPubKey[:], b)
} else { } else {
return Info{}, err return Info{}, err
} }
@ -305,12 +327,10 @@ func (a *AdminSocket) SetupAdminHandlers(na *AdminSocket) {
var m map[string]interface{} var m map[string]interface{}
if err = json.Unmarshal(result, &m); err == nil { if err = json.Unmarshal(result, &m); err == nil {
return Info{"nodeinfo": m}, nil return Info{"nodeinfo": m}, nil
} else {
return Info{}, err
} }
} else {
return Info{}, err return Info{}, err
} }
return Info{}, err
}) })
} }
@ -333,9 +353,8 @@ func (a *AdminSocket) Stop() error {
if a.listener != nil { if a.listener != nil {
a.started = false a.started = false
return a.listener.Close() return a.listener.Close()
} else {
return nil
} }
return nil
} }
// listen is run by start and manages API connections. // listen is run by start and manages API connections.

View File

@ -272,7 +272,7 @@ func (n *BoxNonce) Increment() {
n[len(n)-1] += 2 n[len(n)-1] += 2
for i := len(n) - 2; i >= 0; i-- { for i := len(n) - 2; i >= 0; i-- {
if n[i+1] < oldNonce[i+1] { if n[i+1] < oldNonce[i+1] {
n[i] += 1 n[i]++
} }
} }
} }

View File

@ -10,7 +10,7 @@ func GetDefaults() platformDefaultParameters {
DefaultAdminListen: "unix:///var/run/yggdrasil.sock", DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
// Configuration (used for yggdrasilctl) // Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf", DefaultConfigFile: "/usr/local/etc/yggdrasil.conf",
// Multicast interfaces // Multicast interfaces
DefaultMulticastInterfaces: []string{ DefaultMulticastInterfaces: []string{

View File

@ -21,16 +21,20 @@ import (
// automatically. // automatically.
type Multicast struct { type Multicast struct {
phony.Inbox phony.Inbox
core *yggdrasil.Core core *yggdrasil.Core
config *config.NodeState config *config.NodeState
log *log.Logger log *log.Logger
sock *ipv6.PacketConn sock *ipv6.PacketConn
groupAddr string groupAddr string
listeners map[string]*listenerInfo listeners map[string]*listenerInfo
listenPort uint16 listenPort uint16
isOpen bool isOpen bool
announcer *time.Timer _interfaces map[string]interfaceInfo
platformhandler *time.Timer }
type interfaceInfo struct {
iface net.Interface
addrs []net.Addr
} }
type listenerInfo struct { type listenerInfo struct {
@ -45,6 +49,7 @@ func (m *Multicast) Init(core *yggdrasil.Core, state *config.NodeState, log *log
m.config = state m.config = state
m.log = log m.log = log
m.listeners = make(map[string]*listenerInfo) m.listeners = make(map[string]*listenerInfo)
m._interfaces = make(map[string]interfaceInfo)
current := m.config.GetCurrent() current := m.config.GetCurrent()
m.listenPort = current.LinkLocalTCPPort m.listenPort = current.LinkLocalTCPPort
m.groupAddr = "[ff02::114]:9001" m.groupAddr = "[ff02::114]:9001"
@ -90,8 +95,8 @@ func (m *Multicast) _start() error {
m.isOpen = true m.isOpen = true
go m.listen() go m.listen()
m.Act(m, m.multicastStarted) m.Act(nil, m._multicastStarted)
m.Act(m, m.announce) m.Act(nil, m._announce)
return nil return nil
} }
@ -118,12 +123,6 @@ func (m *Multicast) Stop() error {
func (m *Multicast) _stop() error { func (m *Multicast) _stop() error {
m.log.Infoln("Stopping multicast module") m.log.Infoln("Stopping multicast module")
m.isOpen = false m.isOpen = false
if m.announcer != nil {
m.announcer.Stop()
}
if m.platformhandler != nil {
m.platformhandler.Stop()
}
if m.sock != nil { if m.sock != nil {
m.sock.Close() m.sock.Close()
} }
@ -134,7 +133,7 @@ func (m *Multicast) _stop() error {
// and then signals the various module goroutines to reconfigure themselves if // and then signals the various module goroutines to reconfigure themselves if
// needed. // needed.
func (m *Multicast) UpdateConfig(config *config.NodeConfig) { func (m *Multicast) UpdateConfig(config *config.NodeConfig) {
m.Act(m, func() { m._updateConfig(config) }) m.Act(nil, func() { m._updateConfig(config) })
} }
func (m *Multicast) _updateConfig(config *config.NodeConfig) { func (m *Multicast) _updateConfig(config *config.NodeConfig) {
@ -156,10 +155,35 @@ func (m *Multicast) _updateConfig(config *config.NodeConfig) {
m.log.Debugln("Reloaded multicast configuration successfully") m.log.Debugln("Reloaded multicast configuration successfully")
} }
// GetInterfaces returns the currently known/enabled multicast interfaces. It is func (m *Multicast) _updateInterfaces() {
// expected that UpdateInterfaces has been called at least once before calling interfaces := make(map[string]interfaceInfo)
// this method. intfs := m.getAllowedInterfaces()
for _, intf := range intfs {
addrs, err := intf.Addrs()
if err != nil {
m.log.Warnf("Failed up get addresses for interface %s: %s", intf.Name, err)
continue
}
interfaces[intf.Name] = interfaceInfo{
iface: intf,
addrs: addrs,
}
}
m._interfaces = interfaces
}
func (m *Multicast) Interfaces() map[string]net.Interface { func (m *Multicast) Interfaces() map[string]net.Interface {
interfaces := make(map[string]net.Interface)
phony.Block(m, func() {
for _, info := range m._interfaces {
interfaces[info.iface.Name] = info.iface
}
})
return interfaces
}
// getAllowedInterfaces returns the currently known/enabled multicast interfaces.
func (m *Multicast) getAllowedInterfaces() map[string]net.Interface {
interfaces := make(map[string]net.Interface) interfaces := make(map[string]net.Interface)
// Get interface expressions from config // Get interface expressions from config
current := m.config.GetCurrent() current := m.config.GetCurrent()
@ -198,7 +222,11 @@ func (m *Multicast) Interfaces() map[string]net.Interface {
return interfaces return interfaces
} }
func (m *Multicast) announce() { func (m *Multicast) _announce() {
if !m.isOpen {
return
}
m._updateInterfaces()
groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr) groupAddr, err := net.ResolveUDPAddr("udp6", m.groupAddr)
if err != nil { if err != nil {
panic(err) panic(err)
@ -207,7 +235,6 @@ func (m *Multicast) announce() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
interfaces := m.Interfaces()
// There might be interfaces that we configured listeners for but are no // There might be interfaces that we configured listeners for but are no
// longer up - if that's the case then we should stop the listeners // longer up - if that's the case then we should stop the listeners
for name, info := range m.listeners { for name, info := range m.listeners {
@ -219,7 +246,7 @@ func (m *Multicast) announce() {
} }
// If the interface is no longer visible on the system then stop the // If the interface is no longer visible on the system then stop the
// listener, as another one will be started further down // listener, as another one will be started further down
if _, ok := interfaces[name]; !ok { if _, ok := m._interfaces[name]; !ok {
stop() stop()
continue continue
} }
@ -232,17 +259,13 @@ func (m *Multicast) announce() {
continue continue
} }
// Find the interface that matches the listener // Find the interface that matches the listener
if intf, err := net.InterfaceByName(name); err == nil { if info, ok := m._interfaces[name]; ok {
if addrs, err := intf.Addrs(); err == nil { for _, addr := range info.addrs {
// Loop through the addresses attached to that listener and see if any if ip, _, err := net.ParseCIDR(addr.String()); err == nil {
// of them match the current address of the listener // Does the interface address match our listener address?
for _, addr := range addrs { if ip.Equal(listenaddr.IP) {
if ip, _, err := net.ParseCIDR(addr.String()); err == nil { found = true
// Does the interface address match our listener address? break
if ip.Equal(listenaddr.IP) {
found = true
break
}
} }
} }
} }
@ -256,13 +279,9 @@ func (m *Multicast) announce() {
} }
// Now that we have a list of valid interfaces from the operating system, // Now that we have a list of valid interfaces from the operating system,
// we can start checking if we can send multicasts on them // we can start checking if we can send multicasts on them
for _, iface := range interfaces { for _, info := range m._interfaces {
// Find interface addresses iface := info.iface
addrs, err := iface.Addrs() for _, addr := range info.addrs {
if err != nil {
panic(err)
}
for _, addr := range addrs {
addrIP, _, _ := net.ParseCIDR(addr.String()) addrIP, _, _ := net.ParseCIDR(addr.String())
// Ignore IPv4 addresses // Ignore IPv4 addresses
if addrIP.To4() != nil { if addrIP.To4() != nil {
@ -312,8 +331,8 @@ func (m *Multicast) announce() {
break break
} }
} }
m.announcer = time.AfterFunc(time.Second, func() { time.AfterFunc(time.Second, func() {
m.Act(m, m.announce) m.Act(nil, m._announce)
}) })
} }
@ -351,7 +370,11 @@ func (m *Multicast) listen() {
if addr.IP.String() != from.IP.String() { if addr.IP.String() != from.IP.String() {
continue continue
} }
if _, ok := m.Interfaces()[from.Zone]; ok { var interfaces map[string]interfaceInfo
phony.Block(m, func() {
interfaces = m._interfaces
})
if _, ok := interfaces[from.Zone]; ok {
addr.Zone = "" addr.Zone = ""
if err := m.core.CallPeer("tcp://"+addr.String(), from.Zone); err != nil { if err := m.core.CallPeer("tcp://"+addr.String(), from.Zone); err != nil {
m.log.Debugln("Call from multicast failed:", err) m.log.Debugln("Call from multicast failed:", err)

View File

@ -31,16 +31,19 @@ import (
var awdlGoroutineStarted bool var awdlGoroutineStarted bool
func (m *Multicast) multicastStarted() { func (m *Multicast) _multicastStarted() {
if !m.isOpen {
return
}
C.StopAWDLBrowsing() C.StopAWDLBrowsing()
for intf := range m.Interfaces() { for intf := range m._interfaces {
if intf == "awdl0" { if intf == "awdl0" {
C.StartAWDLBrowsing() C.StartAWDLBrowsing()
break break
} }
} }
m.platformhandler = time.AfterFunc(time.Minute, func() { time.AfterFunc(time.Minute, func() {
m.Act(m, m.multicastStarted) m.Act(nil, m._multicastStarted)
}) })
} }

View File

@ -4,7 +4,7 @@ package multicast
import "syscall" import "syscall"
func (m *Multicast) multicastStarted() { func (m *Multicast) _multicastStarted() {
} }

View File

@ -5,7 +5,7 @@ package multicast
import "syscall" import "syscall"
import "golang.org/x/sys/unix" import "golang.org/x/sys/unix"
func (m *Multicast) multicastStarted() { func (m *Multicast) _multicastStarted() {
} }

View File

@ -5,7 +5,7 @@ package multicast
import "syscall" import "syscall"
import "golang.org/x/sys/windows" import "golang.org/x/sys/windows"
func (m *Multicast) multicastStarted() { func (m *Multicast) _multicastStarted() {
} }

View File

@ -68,16 +68,14 @@ func (t *TunAdapter) SetupAdminHandlers(a *admin.AdminSocket) {
a.AddHandler("addLocalSubnet", []string{"subnet"}, func(in admin.Info) (admin.Info, error) { a.AddHandler("addLocalSubnet", []string{"subnet"}, func(in admin.Info) (admin.Info, error) {
if err := t.ckr.addLocalSubnet(in["subnet"].(string)); err == nil { if err := t.ckr.addLocalSubnet(in["subnet"].(string)); err == nil {
return admin.Info{"added": []string{in["subnet"].(string)}}, nil return admin.Info{"added": []string{in["subnet"].(string)}}, nil
} else {
return admin.Info{"not_added": []string{in["subnet"].(string)}}, errors.New("Failed to add source subnet")
} }
return admin.Info{"not_added": []string{in["subnet"].(string)}}, errors.New("Failed to add source subnet")
}) })
a.AddHandler("addRemoteSubnet", []string{"subnet", "box_pub_key"}, func(in admin.Info) (admin.Info, error) { a.AddHandler("addRemoteSubnet", []string{"subnet", "box_pub_key"}, func(in admin.Info) (admin.Info, error) {
if err := t.ckr.addRemoteSubnet(in["subnet"].(string), in["box_pub_key"].(string)); err == nil { if err := t.ckr.addRemoteSubnet(in["subnet"].(string), in["box_pub_key"].(string)); err == nil {
return admin.Info{"added": []string{fmt.Sprintf("%s via %s", in["subnet"].(string), in["box_pub_key"].(string))}}, nil return admin.Info{"added": []string{fmt.Sprintf("%s via %s", in["subnet"].(string), in["box_pub_key"].(string))}}, nil
} else {
return admin.Info{"not_added": []string{fmt.Sprintf("%s via %s", in["subnet"].(string), in["box_pub_key"].(string))}}, errors.New("Failed to add route")
} }
return admin.Info{"not_added": []string{fmt.Sprintf("%s via %s", in["subnet"].(string), in["box_pub_key"].(string))}}, errors.New("Failed to add route")
}) })
a.AddHandler("getSourceSubnets", []string{}, func(in admin.Info) (admin.Info, error) { a.AddHandler("getSourceSubnets", []string{}, func(in admin.Info) (admin.Info, error) {
var subnets []string var subnets []string
@ -104,15 +102,13 @@ func (t *TunAdapter) SetupAdminHandlers(a *admin.AdminSocket) {
a.AddHandler("removeLocalSubnet", []string{"subnet"}, func(in admin.Info) (admin.Info, error) { a.AddHandler("removeLocalSubnet", []string{"subnet"}, func(in admin.Info) (admin.Info, error) {
if err := t.ckr.removeLocalSubnet(in["subnet"].(string)); err == nil { if err := t.ckr.removeLocalSubnet(in["subnet"].(string)); err == nil {
return admin.Info{"removed": []string{in["subnet"].(string)}}, nil return admin.Info{"removed": []string{in["subnet"].(string)}}, nil
} else {
return admin.Info{"not_removed": []string{in["subnet"].(string)}}, errors.New("Failed to remove source subnet")
} }
return admin.Info{"not_removed": []string{in["subnet"].(string)}}, errors.New("Failed to remove source subnet")
}) })
a.AddHandler("removeRemoteSubnet", []string{"subnet", "box_pub_key"}, func(in admin.Info) (admin.Info, error) { a.AddHandler("removeRemoteSubnet", []string{"subnet", "box_pub_key"}, func(in admin.Info) (admin.Info, error) {
if err := t.ckr.removeRemoteSubnet(in["subnet"].(string), in["box_pub_key"].(string)); err == nil { if err := t.ckr.removeRemoteSubnet(in["subnet"].(string), in["box_pub_key"].(string)); err == nil {
return admin.Info{"removed": []string{fmt.Sprintf("%s via %s", in["subnet"].(string), in["box_pub_key"].(string))}}, nil return admin.Info{"removed": []string{fmt.Sprintf("%s via %s", in["subnet"].(string), in["box_pub_key"].(string))}}, nil
} else {
return admin.Info{"not_removed": []string{fmt.Sprintf("%s via %s", in["subnet"].(string), in["box_pub_key"].(string))}}, errors.New("Failed to remove route")
} }
return admin.Info{"not_removed": []string{fmt.Sprintf("%s via %s", in["subnet"].(string), in["box_pub_key"].(string))}}, errors.New("Failed to remove route")
}) })
} }

View File

@ -199,7 +199,6 @@ func (tun *TunAdapter) _handlePacket(recvd []byte, err error) {
tc.writeFrom(nil, packet) tc.writeFrom(nil, packet)
} }
}) })
return
}() }()
} }
} }

View File

@ -54,12 +54,11 @@ func (c *cancellation) Cancel(err error) error {
defer c.mutex.Unlock() defer c.mutex.Unlock()
if c.done { if c.done {
return c.err return c.err
} else {
c.err = err
c.done = true
close(c.cancel)
return nil
} }
c.err = err
c.done = true
close(c.cancel)
return nil
} }
// Error returns the error provided to Cancel, or nil if no error has been provided. // Error returns the error provided to Cancel, or nil if no error has been provided.

View File

@ -30,9 +30,8 @@ func UnlockThread() {
func ResizeBytes(bs []byte, length int) []byte { func ResizeBytes(bs []byte, length int) []byte {
if cap(bs) >= length { if cap(bs) >= length {
return bs[:length] return bs[:length]
} else {
return make([]byte, length)
} }
return make([]byte, length)
} }
// TimerStop stops a timer and makes sure the channel is drained, returns true if the timer was stopped before firing. // TimerStop stops a timer and makes sure the channel is drained, returns true if the timer was stopped before firing.

View File

@ -468,12 +468,31 @@ func (c *Core) AddPeer(addr string, sintf string) error {
return nil return nil
} }
// RemovePeer is not implemented yet.
func (c *Core) RemovePeer(addr string, sintf string) error { func (c *Core) RemovePeer(addr string, sintf string) error {
// TODO: Implement a reverse of AddPeer, where we look up the port number if sintf == "" {
// based on the addr and sintf, disconnect it and then remove it from the for i, peer := range c.config.Current.Peers {
// peers list so we don't reconnect to it later if peer == addr {
return errors.New("not implemented") c.config.Current.Peers = append(c.config.Current.Peers[:i], c.config.Current.Peers[i+1:]...)
break
}
}
} else if _, ok := c.config.Current.InterfacePeers[sintf]; ok {
for i, peer := range c.config.Current.InterfacePeers[sintf] {
if peer == addr {
c.config.Current.InterfacePeers[sintf] = append(c.config.Current.InterfacePeers[sintf][:i], c.config.Current.InterfacePeers[sintf][i+1:]...)
break
}
}
}
ports := c.peers.ports.Load().(map[switchPort]*peer)
for p, peer := range ports {
if addr == peer.intf.name {
c.peers.removePeer(p)
}
}
return nil
} }
// CallPeer calls a peer once. This should be specified in the peer URI format, // CallPeer calls a peer once. This should be specified in the peer URI format,

View File

@ -145,7 +145,8 @@ func (c *Conn) search() error {
} }
// Used in session keep-alive traffic // Used in session keep-alive traffic
func (c *Conn) doSearch() { func (c *Conn) _doSearch() {
s := fmt.Sprintf("conn=%p", c)
routerWork := func() { routerWork := func() {
// Check to see if there is a search already matching the destination // Check to see if there is a search already matching the destination
sinfo, isIn := c.core.router.searches.searches[*c.nodeID] sinfo, isIn := c.core.router.searches.searches[*c.nodeID]
@ -153,7 +154,7 @@ func (c *Conn) doSearch() {
// Nothing was found, so create a new search // Nothing was found, so create a new search
searchCompleted := func(sinfo *sessionInfo, e error) {} searchCompleted := func(sinfo *sessionInfo, e error) {}
sinfo = c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted) sinfo = c.core.router.searches.newIterSearch(c.nodeID, c.nodeMask, searchCompleted)
c.core.log.Debugf("%s DHT search started: %p", c.String(), sinfo) c.core.log.Debugf("%s DHT search started: %p", s, sinfo)
// Start the search // Start the search
sinfo.startSearch() sinfo.startSearch()
} }
@ -166,10 +167,9 @@ func (c *Conn) _getDeadlineCancellation(t *time.Time) (util.Cancellation, bool)
// A deadline is set, so return a Cancellation that uses it // A deadline is set, so return a Cancellation that uses it
c := util.CancellationWithDeadline(c.session.cancel, *t) c := util.CancellationWithDeadline(c.session.cancel, *t)
return c, true return c, true
} else {
// No deadline was set, so just return the existing cancellation and a dummy value
return c.session.cancel, false
} }
// No deadline was set, so just return the existing cancellation and a dummy value
return c.session.cancel, false
} }
// SetReadCallback allows you to specify a function that will be called whenever // SetReadCallback allows you to specify a function that will be called whenever
@ -224,9 +224,8 @@ func (c *Conn) readNoCopy() ([]byte, error) {
case <-cancel.Finished(): case <-cancel.Finished():
if cancel.Error() == util.CancellationTimeoutError { if cancel.Error() == util.CancellationTimeoutError {
return nil, ConnError{errors.New("read timeout"), true, false, false, 0} return nil, ConnError{errors.New("read timeout"), true, false, false, 0}
} else {
return nil, ConnError{errors.New("session closed"), false, false, true, 0}
} }
return nil, ConnError{errors.New("session closed"), false, false, true, 0}
case bs := <-c.readBuffer: case bs := <-c.readBuffer:
return bs, nil return bs, nil
} }
@ -269,7 +268,7 @@ func (c *Conn) _write(msg FlowKeyMessage) error {
case time.Since(c.session.time) > 6*time.Second: case time.Since(c.session.time) > 6*time.Second:
if c.session.time.Before(c.session.pingTime) && time.Since(c.session.pingTime) > 6*time.Second { if c.session.time.Before(c.session.pingTime) && time.Since(c.session.pingTime) > 6*time.Second {
// TODO double check that the above condition is correct // TODO double check that the above condition is correct
c.doSearch() c._doSearch()
} else { } else {
c.session.ping(c.session) // TODO send from self if this becomes an actor c.session.ping(c.session) // TODO send from self if this becomes an actor
} }

View File

@ -260,7 +260,9 @@ func (t *dht) handleRes(res *dhtRes) {
key: res.Key, key: res.Key,
coords: res.Coords, coords: res.Coords,
} }
t.insert(&rinfo) if t.isImportant(&rinfo) {
t.insert(&rinfo)
}
for _, info := range res.Infos { for _, info := range res.Infos {
if *info.getNodeID() == t.nodeID { if *info.getNodeID() == t.nodeID {
continue continue

View File

@ -16,6 +16,7 @@ import (
"github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/crypto" "github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/util"
"golang.org/x/net/proxy"
"github.com/Arceliar/phony" "github.com/Arceliar/phony"
) )
@ -50,6 +51,7 @@ type linkInterface struct {
name string name string
link *link link *link
peer *peer peer *peer
options linkOptions
msgIO linkInterfaceMsgIO msgIO linkInterfaceMsgIO
info linkInfo info linkInfo
incoming bool incoming bool
@ -67,6 +69,11 @@ type linkInterface struct {
unstalled bool // False if an idle notification to the switch hasn't been sent because we stalled (or are first starting up) unstalled bool // False if an idle notification to the switch hasn't been sent because we stalled (or are first starting up)
} }
type linkOptions struct {
pinnedCurve25519Keys map[crypto.BoxPubKey]struct{}
pinnedEd25519Keys map[crypto.SigPubKey]struct{}
}
func (l *link) init(c *Core) error { func (l *link) init(c *Core) error {
l.core = c l.core = c
l.mutex.Lock() l.mutex.Lock()
@ -92,13 +99,41 @@ func (l *link) call(uri string, sintf string) error {
return fmt.Errorf("peer %s is not correctly formatted (%s)", uri, err) return fmt.Errorf("peer %s is not correctly formatted (%s)", uri, err)
} }
pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/") pathtokens := strings.Split(strings.Trim(u.Path, "/"), "/")
tcpOpts := tcpOptions{}
if pubkeys, ok := u.Query()["curve25519"]; ok && len(pubkeys) > 0 {
tcpOpts.pinnedCurve25519Keys = make(map[crypto.BoxPubKey]struct{})
for _, pubkey := range pubkeys {
if boxPub, err := hex.DecodeString(pubkey); err == nil {
var boxPubKey crypto.BoxPubKey
copy(boxPubKey[:], boxPub)
tcpOpts.pinnedCurve25519Keys[boxPubKey] = struct{}{}
}
}
}
if pubkeys, ok := u.Query()["ed25519"]; ok && len(pubkeys) > 0 {
tcpOpts.pinnedEd25519Keys = make(map[crypto.SigPubKey]struct{})
for _, pubkey := range pubkeys {
if sigPub, err := hex.DecodeString(pubkey); err == nil {
var sigPubKey crypto.SigPubKey
copy(sigPubKey[:], sigPub)
tcpOpts.pinnedEd25519Keys[sigPubKey] = struct{}{}
}
}
}
switch u.Scheme { switch u.Scheme {
case "tcp": case "tcp":
l.tcp.call(u.Host, nil, sintf, nil) l.tcp.call(u.Host, tcpOpts, sintf)
case "socks": case "socks":
l.tcp.call(pathtokens[0], u.Host, sintf, nil) tcpOpts.socksProxyAddr = u.Host
if u.User != nil {
tcpOpts.socksProxyAuth = &proxy.Auth{}
tcpOpts.socksProxyAuth.User = u.User.Username()
tcpOpts.socksProxyAuth.Password, _ = u.User.Password()
}
l.tcp.call(pathtokens[0], tcpOpts, sintf)
case "tls": case "tls":
l.tcp.call(u.Host, nil, sintf, l.tcp.tls.forDialer) tcpOpts.upgrade = l.tcp.tls.forDialer
l.tcp.call(u.Host, tcpOpts, sintf)
default: default:
return errors.New("unknown call scheme: " + u.Scheme) return errors.New("unknown call scheme: " + u.Scheme)
} }
@ -122,12 +157,13 @@ func (l *link) listen(uri string) error {
} }
} }
func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote string, incoming, force bool) (*linkInterface, error) { func (l *link) create(msgIO linkInterfaceMsgIO, name, linkType, local, remote string, incoming, force bool, options linkOptions) (*linkInterface, error) {
// Technically anything unique would work for names, but let's pick something human readable, just for debugging // Technically anything unique would work for names, but let's pick something human readable, just for debugging
intf := linkInterface{ intf := linkInterface{
name: name, name: name,
link: l, link: l,
msgIO: msgIO, options: options,
msgIO: msgIO,
info: linkInfo{ info: linkInfo{
linkType: linkType, linkType: linkType,
local: local, local: local,
@ -181,6 +217,20 @@ func (intf *linkInterface) handler() error {
intf.link.core.log.Errorln("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer)) intf.link.core.log.Errorln("Failed to connect to node: " + intf.name + " version: " + fmt.Sprintf("%d.%d", meta.ver, meta.minorVer))
return errors.New("failed to connect: wrong version") return errors.New("failed to connect: wrong version")
} }
// Check if the remote side matches the keys we expected. This is a bit of a weak
// check - in future versions we really should check a signature or something like that.
if pinned := intf.options.pinnedCurve25519Keys; pinned != nil {
if _, allowed := pinned[meta.box]; !allowed {
intf.link.core.log.Errorf("Failed to connect to node: %q sent curve25519 key that does not match pinned keys", intf.name)
return fmt.Errorf("failed to connect: host sent curve25519 key that does not match pinned keys")
}
}
if pinned := intf.options.pinnedEd25519Keys; pinned != nil {
if _, allowed := pinned[meta.sig]; !allowed {
intf.link.core.log.Errorf("Failed to connect to node: %q sent ed25519 key that does not match pinned keys", intf.name)
return fmt.Errorf("failed to connect: host sent ed25519 key that does not match pinned keys")
}
}
// Check if we're authorized to connect to this key / IP // Check if we're authorized to connect to this key / IP
if intf.incoming && !intf.force && !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) { if intf.incoming && !intf.force && !intf.link.core.peers.isAllowedEncryptionPublicKey(&meta.box) {
intf.link.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s", intf.link.core.log.Warnf("%s connection from %s forbidden: AllowedEncryptionPublicKeys does not contain key %s",
@ -203,17 +253,16 @@ func (intf *linkInterface) handler() error {
<-oldIntf.closed <-oldIntf.closed
} }
return nil return nil
} else {
intf.closed = make(chan struct{})
intf.link.interfaces[intf.info] = intf
defer func() {
intf.link.mutex.Lock()
delete(intf.link.interfaces, intf.info)
intf.link.mutex.Unlock()
close(intf.closed)
}()
intf.link.core.log.Debugln("DEBUG: registered interface for", intf.name)
} }
intf.closed = make(chan struct{})
intf.link.interfaces[intf.info] = intf
defer func() {
intf.link.mutex.Lock()
delete(intf.link.interfaces, intf.info)
intf.link.mutex.Unlock()
close(intf.closed)
}()
intf.link.core.log.Debugln("DEBUG: registered interface for", intf.name)
intf.link.mutex.Unlock() intf.link.mutex.Unlock()
// Create peer // Create peer
shared := crypto.GetSharedKey(myLinkPriv, &meta.link) shared := crypto.GetSharedKey(myLinkPriv, &meta.link)

View File

@ -136,15 +136,15 @@ func (m *nodeinfo) _setNodeInfo(given interface{}, privacy bool) error {
newnodeinfo[key] = value newnodeinfo[key] = value
} }
} }
if newjson, err := json.Marshal(newnodeinfo); err == nil { newjson, err := json.Marshal(newnodeinfo)
if err == nil {
if len(newjson) > 16384 { if len(newjson) > 16384 {
return errors.New("NodeInfo exceeds max length of 16384 bytes") return errors.New("NodeInfo exceeds max length of 16384 bytes")
} }
m.myNodeInfo = newjson m.myNodeInfo = newjson
return nil return nil
} else {
return err
} }
return err
} }
// Add nodeinfo into the cache for a node // Add nodeinfo into the cache for a node

View File

@ -532,7 +532,6 @@ func (t *switchTable) unlockedHandleMsg(msg *switchMsg, fromPort switchPort, rep
if true || doUpdate { if true || doUpdate {
t.updater.Store(&sync.Once{}) t.updater.Store(&sync.Once{})
} }
return
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -615,9 +614,8 @@ func (t *switchTable) portIsCloser(dest []byte, port switchPort) bool {
theirDist := info.locator.dist(dest) theirDist := info.locator.dist(dest)
myDist := table.self.dist(dest) myDist := table.self.dist(dest)
return theirDist < myDist return theirDist < myDist
} else {
return false
} }
return false
} }
// Get the coords of a packet without decoding // Get the coords of a packet without decoding

View File

@ -25,6 +25,7 @@ import (
"golang.org/x/net/proxy" "golang.org/x/net/proxy"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/util" "github.com/yggdrasil-network/yggdrasil-go/src/util"
) )
@ -57,6 +58,14 @@ type TcpUpgrade struct {
name string name string
} }
type tcpOptions struct {
linkOptions
upgrade *TcpUpgrade
socksProxyAddr string
socksProxyAuth *proxy.Auth
socksPeerAddr string
}
func (l *TcpListener) Stop() { func (l *TcpListener) Stop() {
defer func() { recover() }() defer func() { recover() }()
close(l.stop) close(l.stop)
@ -196,10 +205,9 @@ func (t *tcp) listener(l *TcpListener, listenaddr string) {
t.mutex.Unlock() t.mutex.Unlock()
l.Listener.Close() l.Listener.Close()
return return
} else {
t.listeners[listenaddr] = l
t.mutex.Unlock()
} }
t.listeners[listenaddr] = l
t.mutex.Unlock()
// And here we go! // And here we go!
defer func() { defer func() {
t.link.core.log.Infoln("Stopping TCP listener on:", l.Listener.Addr().String()) t.link.core.log.Infoln("Stopping TCP listener on:", l.Listener.Addr().String())
@ -221,7 +229,10 @@ func (t *tcp) listener(l *TcpListener, listenaddr string) {
return return
} }
t.waitgroup.Add(1) t.waitgroup.Add(1)
go t.handler(sock, true, nil, l.upgrade) options := tcpOptions{
upgrade: l.upgrade,
}
go t.handler(sock, true, options)
} }
} }
@ -239,12 +250,12 @@ func (t *tcp) startCalling(saddr string) bool {
// If the dial is successful, it launches the handler. // If the dial is successful, it launches the handler.
// When finished, it removes the outgoing call, so reconnection attempts can be made later. // When finished, it removes the outgoing call, so reconnection attempts can be made later.
// This all happens in a separate goroutine that it spawns. // This all happens in a separate goroutine that it spawns.
func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *TcpUpgrade) { func (t *tcp) call(saddr string, options tcpOptions, sintf string) {
go func() { go func() {
callname := saddr callname := saddr
callproto := "TCP" callproto := "TCP"
if upgrade != nil { if options.upgrade != nil {
callproto = strings.ToUpper(upgrade.name) callproto = strings.ToUpper(options.upgrade.name)
} }
if sintf != "" { if sintf != "" {
callname = fmt.Sprintf("%s/%s/%s", callproto, saddr, sintf) callname = fmt.Sprintf("%s/%s/%s", callproto, saddr, sintf)
@ -263,17 +274,16 @@ func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *Tcp
}() }()
var conn net.Conn var conn net.Conn
var err error var err error
socksaddr, issocks := options.(string) if options.socksProxyAddr != "" {
if issocks {
if sintf != "" { if sintf != "" {
return return
} }
dialerdst, er := net.ResolveTCPAddr("tcp", socksaddr) dialerdst, er := net.ResolveTCPAddr("tcp", options.socksProxyAddr)
if er != nil { if er != nil {
return return
} }
var dialer proxy.Dialer var dialer proxy.Dialer
dialer, err = proxy.SOCKS5("tcp", dialerdst.String(), nil, proxy.Direct) dialer, err = proxy.SOCKS5("tcp", dialerdst.String(), options.socksProxyAuth, proxy.Direct)
if err != nil { if err != nil {
return return
} }
@ -282,7 +292,8 @@ func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *Tcp
return return
} }
t.waitgroup.Add(1) t.waitgroup.Add(1)
t.handler(conn, false, saddr, nil) options.socksPeerAddr = conn.RemoteAddr().String()
t.handler(conn, false, options)
} else { } else {
dst, err := net.ResolveTCPAddr("tcp", saddr) dst, err := net.ResolveTCPAddr("tcp", saddr)
if err != nil { if err != nil {
@ -348,36 +359,35 @@ func (t *tcp) call(saddr string, options interface{}, sintf string, upgrade *Tcp
return return
} }
t.waitgroup.Add(1) t.waitgroup.Add(1)
t.handler(conn, false, nil, upgrade) t.handler(conn, false, options)
} }
}() }()
} }
func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}, upgrade *TcpUpgrade) { func (t *tcp) handler(sock net.Conn, incoming bool, options tcpOptions) {
defer t.waitgroup.Done() // Happens after sock.close defer t.waitgroup.Done() // Happens after sock.close
defer sock.Close() defer sock.Close()
t.setExtraOptions(sock) t.setExtraOptions(sock)
var upgraded bool var upgraded bool
if upgrade != nil { if options.upgrade != nil {
var err error var err error
if sock, err = upgrade.upgrade(sock); err != nil { if sock, err = options.upgrade.upgrade(sock); err != nil {
t.link.core.log.Errorln("TCP handler upgrade failed:", err) t.link.core.log.Errorln("TCP handler upgrade failed:", err)
return return
} else {
upgraded = true
} }
upgraded = true
} }
stream := stream{} stream := stream{}
stream.init(sock) stream.init(sock)
var name, proto, local, remote string var name, proto, local, remote string
if socksaddr, issocks := options.(string); issocks { if options.socksProxyAddr != "" {
name = "socks://" + sock.RemoteAddr().String() + "/" + socksaddr name = "socks://" + sock.RemoteAddr().String() + "/" + options.socksPeerAddr
proto = "socks" proto = "socks"
local, _, _ = net.SplitHostPort(sock.LocalAddr().String()) local, _, _ = net.SplitHostPort(sock.LocalAddr().String())
remote, _, _ = net.SplitHostPort(socksaddr) remote, _, _ = net.SplitHostPort(options.socksPeerAddr)
} else { } else {
if upgraded { if upgraded {
proto = upgrade.name proto = options.upgrade.name
name = proto + "://" + sock.RemoteAddr().String() name = proto + "://" + sock.RemoteAddr().String()
} else { } else {
proto = "tcp" proto = "tcp"
@ -386,8 +396,21 @@ func (t *tcp) handler(sock net.Conn, incoming bool, options interface{}, upgrade
local, _, _ = net.SplitHostPort(sock.LocalAddr().String()) local, _, _ = net.SplitHostPort(sock.LocalAddr().String())
remote, _, _ = net.SplitHostPort(sock.RemoteAddr().String()) remote, _, _ = net.SplitHostPort(sock.RemoteAddr().String())
} }
localIP := net.ParseIP(local)
if localIP = localIP.To16(); localIP != nil {
var laddr address.Address
var lsubnet address.Subnet
copy(laddr[:], localIP)
copy(lsubnet[:], localIP)
if laddr.IsValid() || lsubnet.IsValid() {
// The local address is with the network address/prefix range
// This would route ygg over ygg, which we don't want
t.link.core.log.Debugln("Dropping ygg-tunneled connection", local, remote)
return
}
}
force := net.ParseIP(strings.Split(remote, "%")[0]).IsLinkLocalUnicast() force := net.ParseIP(strings.Split(remote, "%")[0]).IsLinkLocalUnicast()
link, err := t.link.core.link.create(&stream, name, proto, local, remote, incoming, force) link, err := t.link.core.link.create(&stream, name, proto, local, remote, incoming, force, options.linkOptions)
if err != nil { if err != nil {
t.link.core.log.Println(err) t.link.core.log.Println(err)
panic(err) panic(err)

View File

@ -28,11 +28,11 @@ func version_getBaseMetadata() version_metadata {
} }
} }
// Gest the length of the metadata for this version, used to know how many bytes to read from the start of a connection. // Gets the length of the metadata for this version, used to know how many bytes to read from the start of a connection.
func version_getMetaLength() (mlen int) { func version_getMetaLength() (mlen int) {
mlen += 4 // meta mlen += 4 // meta
mlen += 1 // ver, as long as it's < 127, which it is in this version mlen++ // ver, as long as it's < 127, which it is in this version
mlen += 1 // minorVer, as long as it's < 127, which it is in this version mlen++ // minorVer, as long as it's < 127, which it is in this version
mlen += crypto.BoxPubKeyLen // box mlen += crypto.BoxPubKeyLen // box
mlen += crypto.SigPubKeyLen // sig mlen += crypto.SigPubKeyLen // sig
mlen += crypto.BoxPubKeyLen // link mlen += crypto.BoxPubKeyLen // link