github.com/yggdrasil-network/yggdrasil-go@v0.5.6/src/core/core.go (about)

     1  package core
     2  
     3  import (
     4  	"context"
     5  	"crypto/ed25519"
     6  	"crypto/tls"
     7  	"fmt"
     8  	"io"
     9  	"net"
    10  	"net/url"
    11  	"time"
    12  
    13  	iwe "github.com/Arceliar/ironwood/encrypted"
    14  	iwn "github.com/Arceliar/ironwood/network"
    15  	iwt "github.com/Arceliar/ironwood/types"
    16  	"github.com/Arceliar/phony"
    17  	"github.com/gologme/log"
    18  
    19  	"github.com/yggdrasil-network/yggdrasil-go/src/address"
    20  	"github.com/yggdrasil-network/yggdrasil-go/src/version"
    21  )
    22  
    23  // The Core object represents the Yggdrasil node. You should create a Core
    24  // object for each Yggdrasil node you plan to run.
    25  type Core struct {
    26  	// This is the main data structure that holds everything else for a node
    27  	// We're going to keep our own copy of the provided config - that way we can
    28  	// guarantee that it will be covered by the mutex
    29  	phony.Inbox
    30  	*iwe.PacketConn
    31  	ctx          context.Context
    32  	cancel       context.CancelFunc
    33  	secret       ed25519.PrivateKey
    34  	public       ed25519.PublicKey
    35  	links        links
    36  	proto        protoHandler
    37  	log          Logger
    38  	addPeerTimer *time.Timer
    39  	config       struct {
    40  		tls *tls.Config // immutable after startup
    41  		//_peers             map[Peer]*linkInfo         // configurable after startup
    42  		_listeners         map[ListenAddress]struct{} // configurable after startup
    43  		nodeinfo           NodeInfo                   // immutable after startup
    44  		nodeinfoPrivacy    NodeInfoPrivacy            // immutable after startup
    45  		_allowedPublicKeys map[[32]byte]struct{}      // configurable after startup
    46  	}
    47  	pathNotify func(ed25519.PublicKey)
    48  }
    49  
    50  func New(cert *tls.Certificate, logger Logger, opts ...SetupOption) (*Core, error) {
    51  	c := &Core{
    52  		log: logger,
    53  	}
    54  	c.ctx, c.cancel = context.WithCancel(context.Background())
    55  	if c.log == nil {
    56  		c.log = log.New(io.Discard, "", 0)
    57  	}
    58  
    59  	if name := version.BuildName(); name != "unknown" {
    60  		c.log.Infoln("Build name:", name)
    61  	}
    62  	if version := version.BuildVersion(); version != "unknown" {
    63  		c.log.Infoln("Build version:", version)
    64  	}
    65  
    66  	var err error
    67  	c.config._listeners = map[ListenAddress]struct{}{}
    68  	c.config._allowedPublicKeys = map[[32]byte]struct{}{}
    69  	for _, opt := range opts {
    70  		switch opt.(type) {
    71  		case Peer, ListenAddress:
    72  			// We can't do peers yet as the links aren't set up.
    73  			continue
    74  		default:
    75  			if err = c._applyOption(opt); err != nil {
    76  				return nil, fmt.Errorf("failed to apply configuration option %T: %w", opt, err)
    77  			}
    78  		}
    79  	}
    80  	if cert == nil || cert.PrivateKey == nil {
    81  		return nil, fmt.Errorf("no private key supplied")
    82  	}
    83  	var ok bool
    84  	if c.secret, ok = cert.PrivateKey.(ed25519.PrivateKey); !ok {
    85  		return nil, fmt.Errorf("private key must be ed25519")
    86  	}
    87  	if len(c.secret) != ed25519.PrivateKeySize {
    88  		return nil, fmt.Errorf("private key is incorrect length")
    89  	}
    90  	c.public = c.secret.Public().(ed25519.PublicKey)
    91  
    92  	if c.config.tls, err = c.generateTLSConfig(cert); err != nil {
    93  		return nil, fmt.Errorf("error generating TLS config: %w", err)
    94  	}
    95  	keyXform := func(key ed25519.PublicKey) ed25519.PublicKey {
    96  		return address.SubnetForKey(key).GetKey()
    97  	}
    98  	if c.PacketConn, err = iwe.NewPacketConn(
    99  		c.secret,
   100  		iwn.WithBloomTransform(keyXform),
   101  		iwn.WithPeerMaxMessageSize(65535*2),
   102  		iwn.WithPathNotify(c.doPathNotify),
   103  	); err != nil {
   104  		return nil, fmt.Errorf("error creating encryption: %w", err)
   105  	}
   106  	c.proto.init(c)
   107  	if err := c.links.init(c); err != nil {
   108  		return nil, fmt.Errorf("error initialising links: %w", err)
   109  	}
   110  	for _, opt := range opts {
   111  		switch opt.(type) {
   112  		case Peer, ListenAddress:
   113  			// Now do the peers and listeners.
   114  			if err = c._applyOption(opt); err != nil {
   115  				return nil, fmt.Errorf("failed to apply configuration option %T: %w", opt, err)
   116  			}
   117  		default:
   118  			continue
   119  		}
   120  	}
   121  	if err := c.proto.nodeinfo.setNodeInfo(c.config.nodeinfo, bool(c.config.nodeinfoPrivacy)); err != nil {
   122  		return nil, fmt.Errorf("error setting node info: %w", err)
   123  	}
   124  	for listenaddr := range c.config._listeners {
   125  		u, err := url.Parse(string(listenaddr))
   126  		if err != nil {
   127  			c.log.Errorf("Invalid listener URI %q specified, ignoring\n", listenaddr)
   128  			continue
   129  		}
   130  		if _, err = c.links.listen(u, ""); err != nil {
   131  			c.log.Errorf("Failed to start listener %q: %s\n", listenaddr, err)
   132  		}
   133  	}
   134  	return c, nil
   135  }
   136  
   137  func (c *Core) RetryPeersNow() {
   138  	phony.Block(&c.links, func() {
   139  		for _, l := range c.links._links {
   140  			select {
   141  			case l.kick <- struct{}{}:
   142  			default:
   143  			}
   144  		}
   145  	})
   146  }
   147  
   148  // Stop shuts down the Yggdrasil node.
   149  func (c *Core) Stop() {
   150  	phony.Block(c, func() {
   151  		c.log.Infoln("Stopping...")
   152  		_ = c._close()
   153  		c.log.Infoln("Stopped")
   154  	})
   155  }
   156  
   157  // This function is unsafe and should only be ran by the core actor.
   158  func (c *Core) _close() error {
   159  	c.cancel()
   160  	c.links.shutdown()
   161  	err := c.PacketConn.Close()
   162  	if c.addPeerTimer != nil {
   163  		c.addPeerTimer.Stop()
   164  		c.addPeerTimer = nil
   165  	}
   166  	return err
   167  }
   168  
   169  func (c *Core) MTU() uint64 {
   170  	const sessionTypeOverhead = 1
   171  	MTU := c.PacketConn.MTU() - sessionTypeOverhead
   172  	if MTU > 65535 {
   173  		MTU = 65535
   174  	}
   175  	return MTU
   176  }
   177  
   178  func (c *Core) ReadFrom(p []byte) (n int, from net.Addr, err error) {
   179  	buf := allocBytes(int(c.PacketConn.MTU()))
   180  	defer freeBytes(buf)
   181  	for {
   182  		bs := buf
   183  		n, from, err = c.PacketConn.ReadFrom(bs)
   184  		if err != nil {
   185  			return 0, from, err
   186  		}
   187  		if n == 0 {
   188  			continue
   189  		}
   190  		switch bs[0] {
   191  		case typeSessionTraffic:
   192  			// This is what we want to handle here
   193  		case typeSessionProto:
   194  			var key keyArray
   195  			copy(key[:], from.(iwt.Addr))
   196  			data := append([]byte(nil), bs[1:n]...)
   197  			c.proto.handleProto(nil, key, data)
   198  			continue
   199  		default:
   200  			continue
   201  		}
   202  		bs = bs[1:n]
   203  		copy(p, bs)
   204  		if len(p) < len(bs) {
   205  			n = len(p)
   206  		} else {
   207  			n = len(bs)
   208  		}
   209  		return
   210  	}
   211  }
   212  
   213  func (c *Core) WriteTo(p []byte, addr net.Addr) (n int, err error) {
   214  	buf := allocBytes(0)
   215  	defer func() { freeBytes(buf) }()
   216  	buf = append(buf, typeSessionTraffic)
   217  	buf = append(buf, p...)
   218  	n, err = c.PacketConn.WriteTo(buf, addr)
   219  	if n > 0 {
   220  		n -= 1
   221  	}
   222  	return
   223  }
   224  
   225  func (c *Core) doPathNotify(key ed25519.PublicKey) {
   226  	c.Act(nil, func() {
   227  		if c.pathNotify != nil {
   228  			c.pathNotify(key)
   229  		}
   230  	})
   231  }
   232  
   233  func (c *Core) SetPathNotify(notify func(ed25519.PublicKey)) {
   234  	c.Act(nil, func() {
   235  		c.pathNotify = notify
   236  	})
   237  }
   238  
   239  type Logger interface {
   240  	Printf(string, ...interface{})
   241  	Println(...interface{})
   242  	Infof(string, ...interface{})
   243  	Infoln(...interface{})
   244  	Warnf(string, ...interface{})
   245  	Warnln(...interface{})
   246  	Errorf(string, ...interface{})
   247  	Errorln(...interface{})
   248  	Debugf(string, ...interface{})
   249  	Debugln(...interface{})
   250  	Traceln(...interface{})
   251  }