This commit is contained in:
asolana 2022-10-07 11:36:39 +02:00
parent 74910f96da
commit 82dd30ee5c
61 changed files with 2197 additions and 2030 deletions

View file

@ -3,10 +3,8 @@ package core
import (
"context"
"crypto/ed25519"
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
"io"
"net"
"net/url"
"time"
@ -16,8 +14,6 @@ import (
"github.com/Arceliar/phony"
"github.com/gologme/log"
"github.com/yggdrasil-network/yggdrasil-go/src/config"
//"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
"github.com/yggdrasil-network/yggdrasil-go/src/version"
)
@ -29,62 +25,86 @@ type Core struct {
// guarantee that it will be covered by the mutex
phony.Inbox
*iwe.PacketConn
config *config.NodeConfig // Config
ctx context.Context
cancel context.CancelFunc
secret ed25519.PrivateKey
public ed25519.PublicKey
links links
proto protoHandler
log *log.Logger
log Logger
addPeerTimer *time.Timer
ctx context.Context
ctxCancel context.CancelFunc
config struct {
_peers map[Peer]*linkInfo // configurable after startup
_listeners map[ListenAddress]struct{} // configurable after startup
nodeinfo NodeInfo // immutable after startup
nodeinfoPrivacy NodeInfoPrivacy // immutable after startup
_allowedPublicKeys map[[32]byte]struct{} // configurable after startup
}
}
func (c *Core) _init() error {
// TODO separate init and start functions
// Init sets up structs
// Start launches goroutines that depend on structs being set up
// This is pretty much required to completely avoid race conditions
c.config.RLock()
defer c.config.RUnlock()
func New(secret ed25519.PrivateKey, logger Logger, opts ...SetupOption) (*Core, error) {
c := &Core{
log: logger,
}
if name := version.BuildName(); name != "unknown" {
c.log.Infoln("Build name:", name)
}
if version := version.BuildVersion(); version != "unknown" {
c.log.Infoln("Build version:", version)
}
c.ctx, c.cancel = context.WithCancel(context.Background())
// Take a copy of the private key so that it is in our own memory space.
if len(secret) != ed25519.PrivateKeySize {
return nil, fmt.Errorf("private key is incorrect length")
}
c.secret = make(ed25519.PrivateKey, ed25519.PrivateKeySize)
copy(c.secret, secret)
c.public = secret.Public().(ed25519.PublicKey)
var err error
if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil {
return nil, fmt.Errorf("error creating encryption: %w", err)
}
c.config._peers = map[Peer]*linkInfo{}
c.config._listeners = map[ListenAddress]struct{}{}
c.config._allowedPublicKeys = map[[32]byte]struct{}{}
for _, opt := range opts {
c._applyOption(opt)
}
if c.log == nil {
c.log = log.New(ioutil.Discard, "", 0)
c.log = log.New(io.Discard, "", 0)
}
sigPriv, err := hex.DecodeString(c.config.PrivateKey)
if err != nil {
return err
}
if len(sigPriv) < ed25519.PrivateKeySize {
return errors.New("PrivateKey is incorrect length")
}
c.secret = ed25519.PrivateKey(sigPriv)
c.public = c.secret.Public().(ed25519.PublicKey)
// TODO check public against current.PublicKey, error if they don't match
c.PacketConn, err = iwe.NewPacketConn(c.secret)
c.ctx, c.ctxCancel = context.WithCancel(context.Background())
c.proto.init(c)
if err := c.proto.nodeinfo.setNodeInfo(c.config.NodeInfo, c.config.NodeInfoPrivacy); err != nil {
return fmt.Errorf("setNodeInfo: %w", err)
if err := c.links.init(c); err != nil {
return nil, fmt.Errorf("error initialising links: %w", err)
}
return err
if err := c.proto.nodeinfo.setNodeInfo(c.config.nodeinfo, bool(c.config.nodeinfoPrivacy)); err != nil {
return nil, fmt.Errorf("error setting node info: %w", err)
}
for listenaddr := range c.config._listeners {
u, err := url.Parse(string(listenaddr))
if err != nil {
c.log.Errorf("Invalid listener URI %q specified, ignoring\n", listenaddr)
continue
}
if _, err = c.links.listen(u, ""); err != nil {
c.log.Errorf("Failed to start listener %q: %s\n", listenaddr, err)
}
}
c.Act(nil, c._addPeerLoop)
return c, nil
}
// If any static peers were provided in the configuration above then we should
// configure them. The loop ensures that disconnected peers will eventually
// be reconnected with.
func (c *Core) _addPeerLoop() {
c.config.RLock()
defer c.config.RUnlock()
if c.addPeerTimer == nil {
select {
case <-c.ctx.Done():
return
default:
}
// Add peers from the Peers section
for _, peer := range c.config.Peers {
for peer := range c.config._peers {
go func(peer string, intf string) {
u, err := url.Parse(peer)
if err != nil {
@ -93,22 +113,7 @@ func (c *Core) _addPeerLoop() {
if err := c.CallPeer(u, intf); err != nil {
c.log.Errorln("Failed to add peer:", err)
}
}(peer, "") // TODO: this should be acted and not in a goroutine?
}
// Add peers from the InterfacePeers section
for intf, intfpeers := range c.config.InterfacePeers {
for _, peer := range intfpeers {
go func(peer string, intf string) {
u, err := url.Parse(peer)
if err != nil {
c.log.Errorln("Failed to parse peer url:", peer, err)
}
if err := c.CallPeer(u, intf); err != nil {
c.log.Errorln("Failed to add peer:", err)
}
}(peer, intf) // TODO: this should be acted and not in a goroutine?
}
}(peer.URI, peer.SourceInterface) // TODO: this should be acted and not in a goroutine?
}
c.addPeerTimer = time.AfterFunc(time.Minute, func() {
@ -116,75 +121,24 @@ func (c *Core) _addPeerLoop() {
})
}
// Start starts up Yggdrasil using the provided config.NodeConfig, and outputs
// debug logging through the provided log.Logger. The started stack will include
// TCP and UDP sockets, a multicast discovery socket, an admin socket, router,
// switch and DHT node. A config.NodeState is returned which contains both the
// current and previous configurations (from reconfigures).
func (c *Core) Start(nc *config.NodeConfig, log *log.Logger) (err error) {
phony.Block(c, func() {
err = c._start(nc, log)
})
return
}
// This function is unsafe and should only be ran by the core actor.
func (c *Core) _start(nc *config.NodeConfig, log *log.Logger) error {
c.log = log
c.config = nc
if name := version.BuildName(); name != "unknown" {
c.log.Infoln("Build name:", name)
}
if version := version.BuildVersion(); version != "unknown" {
c.log.Infoln("Build version:", version)
}
c.log.Infoln("Starting up...")
if err := c._init(); err != nil {
c.log.Errorln("Failed to initialize core")
return err
}
if err := c.links.init(c); err != nil {
c.log.Errorln("Failed to start link interfaces")
return err
}
c.addPeerTimer = time.AfterFunc(0, func() {
c.Act(nil, c._addPeerLoop)
})
c.log.Infoln("Startup complete")
return nil
}
// Stop shuts down the Yggdrasil node.
func (c *Core) Stop() {
phony.Block(c, func() {
c.log.Infoln("Stopping...")
c._close()
_ = c._close()
c.log.Infoln("Stopped")
})
}
func (c *Core) Close() error {
var err error
phony.Block(c, func() {
err = c._close()
})
return err
}
// This function is unsafe and should only be ran by the core actor.
func (c *Core) _close() error {
c.ctxCancel()
c.cancel()
c.links.shutdown()
err := c.PacketConn.Close()
if c.addPeerTimer != nil {
c.addPeerTimer.Stop()
c.addPeerTimer = nil
}
_ = c.links.stop()
return err
}
@ -237,3 +191,16 @@ func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) {
}
return
}
type Logger interface {
Printf(string, ...interface{})
Println(...interface{})
Infof(string, ...interface{})
Infoln(...interface{})
Warnf(string, ...interface{})
Warnln(...interface{})
Errorf(string, ...interface{})
Errorln(...interface{})
Debugf(string, ...interface{})
Debugln(...interface{})
}