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 (
|
2021-06-12 06:06:39 -05:00
|
|
|
"context"
|
2021-05-08 08:35:58 -05:00
|
|
|
"crypto/ed25519"
|
2023-04-06 21:45:49 +01:00
|
|
|
"crypto/tls"
|
2021-06-13 04:54:06 -05:00
|
|
|
"fmt"
|
2022-08-06 15:21:21 +01:00
|
|
|
"io"
|
2021-07-05 13:14:12 -05:00
|
|
|
"net"
|
2021-05-23 21:47:12 -05:00
|
|
|
"net/url"
|
2019-01-14 17:21:15 +00:00
|
|
|
"time"
|
2018-06-12 17:50:08 -05:00
|
|
|
|
2021-07-05 13:14:12 -05:00
|
|
|
iwe "github.com/Arceliar/ironwood/encrypted"
|
2023-05-19 19:33:40 +01:00
|
|
|
iwn "github.com/Arceliar/ironwood/network"
|
2021-07-05 13:14:12 -05:00
|
|
|
iwt "github.com/Arceliar/ironwood/types"
|
2019-08-28 12:17:19 +01:00
|
|
|
"github.com/Arceliar/phony"
|
2019-01-27 13:31:43 +00:00
|
|
|
"github.com/gologme/log"
|
|
|
|
|
2023-05-19 19:33:40 +01:00
|
|
|
"github.com/yggdrasil-network/yggdrasil-go/src/address"
|
2019-08-11 00:31:22 +03:00
|
|
|
"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
|
2018-12-29 18:51:51 +00:00
|
|
|
// 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
|
2021-07-05 13:14:12 -05:00
|
|
|
*iwe.PacketConn
|
2022-07-24 10:23:25 +01:00
|
|
|
ctx context.Context
|
|
|
|
cancel context.CancelFunc
|
2021-05-08 08:35:58 -05:00
|
|
|
secret ed25519.PrivateKey
|
|
|
|
public ed25519.PublicKey
|
2020-05-23 10:23:55 -05:00
|
|
|
links links
|
2021-06-13 04:54:06 -05:00
|
|
|
proto protoHandler
|
2022-10-02 13:20:39 +01:00
|
|
|
log Logger
|
2019-09-18 15:22:17 +01:00
|
|
|
addPeerTimer *time.Timer
|
2022-07-24 10:23:25 +01:00
|
|
|
config struct {
|
2023-10-11 18:25:35 +01:00
|
|
|
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
|
|
|
|
}
|
2023-05-13 14:44:38 -05:00
|
|
|
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{
|
2022-09-03 12:34:29 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2022-09-17 20:07:00 +01:00
|
|
|
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)
|
|
|
|
}
|
2023-05-19 19:33:40 +01:00
|
|
|
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)
|
2019-07-16 09:49:28 +01:00
|
|
|
}
|
2022-09-17 20:07:00 +01:00
|
|
|
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
|
|
|
|
2022-11-12 11:30:03 +00:00
|
|
|
func (c *Core) RetryPeersNow() {
|
2023-10-22 15:51:30 +01:00
|
|
|
phony.Block(&c.links, func() {
|
|
|
|
for _, l := range c.links._links {
|
|
|
|
select {
|
|
|
|
case l.kick <- struct{}{}:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2022-11-12 11:30:03 +00:00
|
|
|
}
|
|
|
|
|
2019-03-29 18:05:17 +00:00
|
|
|
// Stop shuts down the Yggdrasil node.
|
2018-05-27 22:13:37 +01:00
|
|
|
func (c *Core) Stop() {
|
2021-07-06 19:45:12 -05:00
|
|
|
phony.Block(c, func() {
|
|
|
|
c.log.Infoln("Stopping...")
|
2022-09-24 17:05:44 +01:00
|
|
|
_ = c._close()
|
2021-07-06 19:45:12 -05:00
|
|
|
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.
|
2021-07-06 19:45:12 -05:00
|
|
|
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()
|
2021-07-06 19:45:12 -05:00
|
|
|
err := c.PacketConn.Close()
|
2019-09-18 15:34:26 +01:00
|
|
|
if c.addPeerTimer != nil {
|
|
|
|
c.addPeerTimer.Stop()
|
2021-05-29 21:37:13 -05:00
|
|
|
c.addPeerTimer = nil
|
2019-09-18 15:34:26 +01:00
|
|
|
}
|
2021-07-06 19:45:12 -05:00
|
|
|
return err
|
2018-05-27 22:13:37 +01:00
|
|
|
}
|
2021-07-05 13:14:12 -05:00
|
|
|
|
|
|
|
func (c *Core) MTU() uint64 {
|
|
|
|
const sessionTypeOverhead = 1
|
2023-05-13 14:44:38 -05:00
|
|
|
MTU := c.PacketConn.MTU() - sessionTypeOverhead
|
|
|
|
if MTU > 65535 {
|
|
|
|
MTU = 65535
|
|
|
|
}
|
|
|
|
return MTU
|
2021-07-05 13:14:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) {
|
2023-05-21 12:38:16 -05:00
|
|
|
buf := allocBytes(int(c.PacketConn.MTU()))
|
2023-05-21 12:49:49 -05:00
|
|
|
defer freeBytes(buf)
|
2021-07-05 13:14:12 -05:00
|
|
|
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) {
|
2023-05-21 12:38:16 -05:00
|
|
|
buf := allocBytes(0)
|
2023-05-21 12:49:49 -05:00
|
|
|
defer freeBytes(buf)
|
2021-07-05 13:14:12 -05:00
|
|
|
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
|
|
|
|
2023-05-19 19:33:40 +01:00
|
|
|
func (c *Core) doPathNotify(key ed25519.PublicKey) {
|
|
|
|
c.Act(nil, func() {
|
|
|
|
if c.pathNotify != nil {
|
|
|
|
c.pathNotify(key)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-05-13 14:44:38 -05:00
|
|
|
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{})
|
2022-12-18 00:37:34 -05:00
|
|
|
Traceln(...interface{})
|
2022-10-02 13:20:39 +01:00
|
|
|
}
|