252 lines
6.3 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"
2023-04-06 21:45:49 +01:00
"crypto/tls"
"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"
iwn "github.com/Arceliar/ironwood/network"
iwt "github.com/Arceliar/ironwood/types"
"github.com/Arceliar/phony"
"github.com/gologme/log"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
"github.com/yggdrasil-network/yggdrasil-go/src/version"
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
2022-10-02 13:20:39 +01:00
log Logger
addPeerTimer *time.Timer
2022-07-24 10:23:25 +01:00
config struct {
tls *tls.Config // immutable after startup
2023-04-06 21:45:49 +01:00
//_peers map[Peer]*linkInfo // configurable after startup
2022-07-24 10:23:25 +01:00
_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
}
pathNotify func(ed25519.PublicKey)
2017-12-28 22:16:20 -06:00
}
2023-04-06 21:45:49 +01:00
func New(cert *tls.Certificate, logger 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
}
2023-04-06 21:45:49 +01:00
c.ctx, c.cancel = context.WithCancel(context.Background())
if c.log == nil {
c.log = log.New(io.Discard, "", 0)
}
if name := version.BuildName(); name != "unknown" {
c.log.Infoln("Build name:", name)
}
if version := version.BuildVersion(); version != "unknown" {
c.log.Infoln("Build version:", version)
}
2023-04-06 21:45:49 +01:00
2022-07-24 10:23:25 +01:00
var err error
2022-08-06 15:05:12 +01:00
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 {
2023-04-06 21:45:49 +01:00
switch opt.(type) {
case Peer, ListenAddress:
// We can't do peers yet as the links aren't set up.
continue
default:
if err = c._applyOption(opt); err != nil {
return nil, fmt.Errorf("failed to apply configuration option %T: %w", opt, err)
}
}
2022-07-24 10:23:25 +01:00
}
2023-04-06 21:45:49 +01:00
if cert == nil || cert.PrivateKey == nil {
return nil, fmt.Errorf("no private key supplied")
}
var ok bool
if c.secret, ok = cert.PrivateKey.(ed25519.PrivateKey); !ok {
return nil, fmt.Errorf("private key must be ed25519")
}
if len(c.secret) != ed25519.PrivateKeySize {
return nil, fmt.Errorf("private key is incorrect length")
}
c.public = c.secret.Public().(ed25519.PublicKey)
if c.config.tls, err = c.generateTLSConfig(cert); err != nil {
return nil, fmt.Errorf("error generating TLS config: %w", err)
}
keyXform := func(key ed25519.PublicKey) ed25519.PublicKey {
return address.SubnetForKey(key).GetKey()
}
if c.PacketConn, err = iwe.NewPacketConn(
c.secret,
iwn.WithBloomTransform(keyXform),
iwn.WithPeerMaxMessageSize(65535*2),
iwn.WithPathNotify(c.doPathNotify),
); err != nil {
2023-04-06 21:45:49 +01:00
return nil, fmt.Errorf("error creating encryption: %w", err)
}
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
}
2023-04-06 21:45:49 +01:00
for _, opt := range opts {
switch opt.(type) {
case Peer, ListenAddress:
// Now do the peers and listeners.
if err = c._applyOption(opt); err != nil {
return nil, fmt.Errorf("failed to apply configuration option %T: %w", opt, err)
}
default:
continue
}
}
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)
}
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)
}
2022-07-24 10:23:25 +01:00
}
return c, nil
}
2018-12-29 19:14:26 +00:00
func (c *Core) RetryPeersNow() {
phony.Block(&c.links, func() {
for _, l := range c.links._links {
select {
case l.kick <- struct{}{}:
default:
}
}
})
}
// 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...")
2022-09-24 17:05:44 +01:00
_ = 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()
2022-09-24 17:05:44 +01:00
c.links.shutdown()
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
}
return err
2018-05-27 22:13:37 +01:00
}
func (c *Core) MTU() uint64 {
const sessionTypeOverhead = 1
MTU := c.PacketConn.MTU() - sessionTypeOverhead
if MTU > 65535 {
MTU = 65535
}
return MTU
}
func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) {
buf := allocBytes(int(c.PacketConn.MTU()))
2023-05-21 12:49:49 -05:00
defer freeBytes(buf)
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 := allocBytes(0)
2023-05-21 12:49:49 -05:00
defer freeBytes(buf)
buf = append(buf, typeSessionTraffic)
buf = append(buf, p...)
n, err = c.PacketConn.WriteTo(buf, addr)
if n > 0 {
n -= 1
}
return
}
2022-10-02 13:20:39 +01:00
func (c *Core) doPathNotify(key ed25519.PublicKey) {
c.Act(nil, func() {
if c.pathNotify != nil {
c.pathNotify(key)
}
})
}
func (c *Core) SetPathNotify(notify func(ed25519.PublicKey)) {
c.Act(nil, func() {
c.pathNotify = notify
})
}
2022-10-02 13:20:39 +01:00
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{})
Traceln(...interface{})
2022-10-02 13:20:39 +01:00
}