189 lines
5.1 KiB
Go
Raw Normal View History

2021-05-23 14:42:26 -05:00
package core
2017-12-28 22:16:20 -06:00
2018-06-12 17:50:08 -05:00
import (
"context"
"crypto/ed25519"
"fmt"
"io"
"net"
2021-05-23 21:47:12 -05:00
"net/url"
"time"
2018-06-12 17:50:08 -05:00
iwe "github.com/Arceliar/ironwood/encrypted"
iwt "github.com/Arceliar/ironwood/types"
"github.com/Arceliar/phony"
"github.com/gologme/log"
"github.com/yggdrasil-network/yggdrasil-go/src/util"
"github.com/yggdrasil-network/yggdrasil-go/src/version"
2022-07-24 10:23:25 +01:00
//"github.com/yggdrasil-network/yggdrasil-go/src/crypto"
2018-06-12 17:50:08 -05:00
)
2017-12-28 22:16:20 -06:00
2018-05-27 22:13:37 +01:00
// The Core object represents the Yggdrasil node. You should create a Core
// object for each Yggdrasil node you plan to run.
2017-12-28 22:16:20 -06:00
type Core struct {
2018-01-04 22:37:51 +00:00
// This is the main data structure that holds everything else for a node
// We're going to keep our own copy of the provided config - that way we can
// guarantee that it will be covered by the mutex
2019-08-28 19:31:04 +01:00
phony.Inbox
*iwe.PacketConn
2022-07-24 10:23:25 +01:00
ctx context.Context
cancel context.CancelFunc
secret ed25519.PrivateKey
public ed25519.PublicKey
2020-05-23 10:23:55 -05:00
links links
proto protoHandler
log util.Logger
addPeerTimer *time.Timer
2022-07-24 10:23:25 +01:00
config struct {
_peers map[Peer]struct{} // configurable after startup
_listeners map[ListenAddress]struct{} // configurable after startup
nodeinfo NodeInfo // immutable after startup
nodeinfoPrivacy NodeInfoPrivacy // immutable after startup
ifname IfName // immutable after startup
ifmtu IfMTU // immutable after startup
_allowedPublicKeys map[[32]byte]struct{} // configurable after startup
}
2017-12-28 22:16:20 -06:00
}
func New(secret ed25519.PrivateKey, logger util.Logger, opts ...SetupOption) (*Core, error) {
2022-07-24 10:23:25 +01:00
c := &Core{
log: logger,
2022-07-24 10:23:25 +01:00
}
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, 0, ed25519.PrivateKeySize)
copy(c.secret, secret)
c.public = secret.Public().(ed25519.PublicKey)
2022-07-24 10:23:25 +01:00
var err error
if c.PacketConn, err = iwe.NewPacketConn(c.secret); err != nil {
return nil, fmt.Errorf("error creating encryption: %w", err)
}
2022-08-06 15:05:12 +01:00
c.config._peers = map[Peer]struct{}{}
c.config._listeners = map[ListenAddress]struct{}{}
c.config._allowedPublicKeys = map[[32]byte]struct{}{}
2022-07-24 10:23:25 +01:00
for _, opt := range opts {
c._applyOption(opt)
}
2018-05-27 22:13:37 +01:00
if c.log == nil {
c.log = log.New(io.Discard, "", 0)
2018-05-27 22:13:37 +01:00
}
2022-07-24 10:23:25 +01:00
c.proto.init(c)
if err := c.links.init(c); err != nil {
return nil, fmt.Errorf("error initialising links: %w", err)
2018-12-29 19:14:26 +00:00
}
2022-07-24 10:23:25 +01:00
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)
}
2022-07-24 10:23:25 +01:00
c.addPeerTimer = time.AfterFunc(time.Minute, func() {
c.Act(nil, c._addPeerLoop)
})
if name := version.BuildName(); name != "unknown" {
c.log.Infoln("Build name:", name)
}
if version := version.BuildVersion(); version != "unknown" {
c.log.Infoln("Build version:", version)
}
return c, nil
}
2018-12-29 19:14:26 +00:00
// 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.
2019-08-28 19:53:52 +01:00
func (c *Core) _addPeerLoop() {
if c.addPeerTimer == nil {
return
}
2019-08-28 19:53:52 +01:00
// Add peers from the Peers section
2022-07-24 10:23:25 +01:00
for peer := range c.config._peers {
go func(peer string, intf string) {
2021-05-23 21:47:12 -05:00
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)
}
2022-07-24 10:23:25 +01:00
}(peer.URI, peer.SourceInterface) // TODO: this should be acted and not in a goroutine?
}
2019-08-28 19:53:52 +01:00
c.addPeerTimer = time.AfterFunc(time.Minute, func() {
c.Act(nil, c._addPeerLoop)
})
}
// Stop shuts down the Yggdrasil node.
2018-05-27 22:13:37 +01:00
func (c *Core) Stop() {
phony.Block(c, func() {
c.log.Infoln("Stopping...")
c._close()
c.log.Infoln("Stopped")
})
}
2019-08-28 19:53:52 +01:00
// This function is unsafe and should only be ran by the core actor.
func (c *Core) _close() error {
2022-07-24 10:23:25 +01:00
c.cancel()
err := c.PacketConn.Close()
2019-09-18 15:34:26 +01:00
if c.addPeerTimer != nil {
c.addPeerTimer.Stop()
c.addPeerTimer = nil
2019-09-18 15:34:26 +01:00
}
2021-06-02 14:40:09 +01:00
_ = c.links.stop()
return err
2018-05-27 22:13:37 +01:00
}
func (c *Core) MTU() uint64 {
const sessionTypeOverhead = 1
return c.PacketConn.MTU() - sessionTypeOverhead
}
func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) {
buf := make([]byte, c.PacketConn.MTU(), 65535)
for {
bs := buf
n, from, err = c.PacketConn.ReadFrom(bs)
if err != nil {
return 0, from, err
}
if n == 0 {
continue
}
switch bs[0] {
case typeSessionTraffic:
// This is what we want to handle here
case typeSessionProto:
var key keyArray
copy(key[:], from.(iwt.Addr))
data := append([]byte(nil), bs[1:n]...)
c.proto.handleProto(nil, key, data)
continue
default:
continue
}
bs = bs[1:n]
copy(p, bs)
if len(p) < len(bs) {
n = len(p)
} else {
n = len(bs)
}
return
}
}
func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) {
buf := make([]byte, 0, 65535)
buf = append(buf, typeSessionTraffic)
buf = append(buf, p...)
n, err = c.PacketConn.WriteTo(buf, addr)
if n > 0 {
n -= 1
}
return
}