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 }