mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 11:15:07 +03:00 
			
		
		
		
	
		
			Some checks failed
		
		
	
	Yggdrasil / Lint (push) Has been cancelled
				
			Yggdrasil / Analyse (push) Has been cancelled
				
			Yggdrasil / Build & Test (Linux, Go 1.22) (push) Has been cancelled
				
			Yggdrasil / Build & Test (Linux, Go 1.23) (push) Has been cancelled
				
			Yggdrasil / Build & Test (Linux, Go 1.24) (push) Has been cancelled
				
			Yggdrasil / Build & Test (Windows, Go 1.22) (push) Has been cancelled
				
			Yggdrasil / Build & Test (Windows, Go 1.23) (push) Has been cancelled
				
			Yggdrasil / Build & Test (Windows, Go 1.24) (push) Has been cancelled
				
			Yggdrasil / Build & Test (macOS, Go 1.22) (push) Has been cancelled
				
			Yggdrasil / Build & Test (macOS, Go 1.23) (push) Has been cancelled
				
			Yggdrasil / Build & Test (macOS, Go 1.24) (push) Has been cancelled
				
			Yggdrasil / Build (Cross freebsd, Go 1.22) (push) Has been cancelled
				
			Yggdrasil / Build (Cross freebsd, Go 1.23) (push) Has been cancelled
				
			Yggdrasil / Build (Cross freebsd, Go 1.24) (push) Has been cancelled
				
			Yggdrasil / Build (Cross openbsd, Go 1.22) (push) Has been cancelled
				
			Yggdrasil / Build (Cross openbsd, Go 1.23) (push) Has been cancelled
				
			Yggdrasil / Build (Cross openbsd, Go 1.24) (push) Has been cancelled
				
			Yggdrasil / All tests passed (push) Has been cancelled
				
			
		
			
				
	
	
		
			301 lines
		
	
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			301 lines
		
	
	
	
		
			7.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package mobile
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto/ed25519"
 | 
						|
	"encoding/hex"
 | 
						|
	"encoding/json"
 | 
						|
	"net"
 | 
						|
	"regexp"
 | 
						|
 | 
						|
	"github.com/gologme/log"
 | 
						|
 | 
						|
	"github.com/yggdrasil-network/yggdrasil-go/src/address"
 | 
						|
	"github.com/yggdrasil-network/yggdrasil-go/src/config"
 | 
						|
	"github.com/yggdrasil-network/yggdrasil-go/src/core"
 | 
						|
	"github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc"
 | 
						|
	"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
 | 
						|
	"github.com/yggdrasil-network/yggdrasil-go/src/tun"
 | 
						|
	"github.com/yggdrasil-network/yggdrasil-go/src/version"
 | 
						|
)
 | 
						|
 | 
						|
// Yggdrasil mobile package is meant to "plug the gap" for mobile support, as
 | 
						|
// Gomobile will not create headers for Swift/Obj-C etc if they have complex
 | 
						|
// (non-native) types. Therefore for iOS we will expose some nice simple
 | 
						|
// functions. Note that in the case of iOS we handle reading/writing to/from TUN
 | 
						|
// in Swift therefore we use the "dummy" TUN interface instead.
 | 
						|
type Yggdrasil struct {
 | 
						|
	core      *core.Core
 | 
						|
	iprwc     *ipv6rwc.ReadWriteCloser
 | 
						|
	config    *config.NodeConfig
 | 
						|
	multicast *multicast.Multicast
 | 
						|
	tun       *tun.TunAdapter // optional
 | 
						|
	log       MobileLogger
 | 
						|
	logger    *log.Logger
 | 
						|
}
 | 
						|
 | 
						|
// StartAutoconfigure starts a node with a randomly generated config
 | 
						|
func (m *Yggdrasil) StartAutoconfigure() error {
 | 
						|
	return m.StartJSON([]byte("{}"))
 | 
						|
}
 | 
						|
 | 
						|
// StartJSON starts a node with the given JSON config. You can get JSON config
 | 
						|
// (rather than HJSON) by using the GenerateConfigJSON() function
 | 
						|
func (m *Yggdrasil) StartJSON(configjson []byte) error {
 | 
						|
	setMemLimitIfPossible()
 | 
						|
 | 
						|
	logger := log.New(m.log, "", 0)
 | 
						|
	logger.EnableLevel("error")
 | 
						|
	logger.EnableLevel("warn")
 | 
						|
	logger.EnableLevel("info")
 | 
						|
	m.logger = logger
 | 
						|
	m.config = config.GenerateConfig()
 | 
						|
	if err := m.config.UnmarshalHJSON(configjson); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	// Set up the Yggdrasil node itself.
 | 
						|
	{
 | 
						|
		iprange := net.IPNet{
 | 
						|
			IP:   net.ParseIP("200::"),
 | 
						|
			Mask: net.CIDRMask(7, 128),
 | 
						|
		}
 | 
						|
		options := []core.SetupOption{
 | 
						|
			core.PeerFilter(func(ip net.IP) bool {
 | 
						|
				return !iprange.Contains(ip)
 | 
						|
			}),
 | 
						|
		}
 | 
						|
		for _, peer := range m.config.Peers {
 | 
						|
			options = append(options, core.Peer{URI: peer})
 | 
						|
		}
 | 
						|
		for intf, peers := range m.config.InterfacePeers {
 | 
						|
			for _, peer := range peers {
 | 
						|
				options = append(options, core.Peer{URI: peer, SourceInterface: intf})
 | 
						|
			}
 | 
						|
		}
 | 
						|
		for _, allowed := range m.config.AllowedPublicKeys {
 | 
						|
			k, err := hex.DecodeString(allowed)
 | 
						|
			if err != nil {
 | 
						|
				panic(err)
 | 
						|
			}
 | 
						|
			options = append(options, core.AllowedPublicKey(k[:]))
 | 
						|
		}
 | 
						|
		for _, lAddr := range m.config.Listen {
 | 
						|
			options = append(options, core.ListenAddress(lAddr))
 | 
						|
		}
 | 
						|
		var err error
 | 
						|
		m.core, err = core.New(m.config.Certificate, logger, options...)
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
		address, subnet := m.core.Address(), m.core.Subnet()
 | 
						|
		logger.Infof("Your public key is %s", hex.EncodeToString(m.core.PublicKey()))
 | 
						|
		logger.Infof("Your IPv6 address is %s", address.String())
 | 
						|
		logger.Infof("Your IPv6 subnet is %s", subnet.String())
 | 
						|
	}
 | 
						|
 | 
						|
	// Set up the multicast module.
 | 
						|
	if len(m.config.MulticastInterfaces) > 0 {
 | 
						|
		var err error
 | 
						|
		logger.Infof("Initializing multicast %s", "")
 | 
						|
		options := []multicast.SetupOption{}
 | 
						|
		for _, intf := range m.config.MulticastInterfaces {
 | 
						|
			options = append(options, multicast.MulticastInterface{
 | 
						|
				Regex:    regexp.MustCompile(intf.Regex),
 | 
						|
				Beacon:   intf.Beacon,
 | 
						|
				Listen:   intf.Listen,
 | 
						|
				Port:     intf.Port,
 | 
						|
				Priority: uint8(intf.Priority),
 | 
						|
				Password: intf.Password,
 | 
						|
			})
 | 
						|
		}
 | 
						|
		logger.Infof("Starting multicast %s", "")
 | 
						|
		m.multicast, err = multicast.New(m.core, m.logger, options...)
 | 
						|
		if err != nil {
 | 
						|
			logger.Errorln("An error occurred starting multicast:", err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	mtu := m.config.IfMTU
 | 
						|
	m.iprwc = ipv6rwc.NewReadWriteCloser(m.core)
 | 
						|
	if m.iprwc.MaxMTU() < mtu {
 | 
						|
		mtu = m.iprwc.MaxMTU()
 | 
						|
	}
 | 
						|
	m.iprwc.SetMTU(mtu)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Send sends a packet to Yggdrasil. It should be a fully formed
 | 
						|
// IPv6 packet
 | 
						|
func (m *Yggdrasil) Send(p []byte) error {
 | 
						|
	if m.iprwc == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	_, _ = m.iprwc.Write(p)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Send sends a packet from given buffer to Yggdrasil. From first byte up to length.
 | 
						|
func (m *Yggdrasil) SendBuffer(p []byte, length int) error {
 | 
						|
	if m.iprwc == nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	if len(p) < length {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	_, _ = m.iprwc.Write(p[:length])
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Recv waits for and reads a packet coming from Yggdrasil. It
 | 
						|
// will be a fully formed IPv6 packet
 | 
						|
func (m *Yggdrasil) Recv() ([]byte, error) {
 | 
						|
	if m.iprwc == nil {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
	var buf [65535]byte
 | 
						|
	n, _ := m.iprwc.Read(buf[:])
 | 
						|
	return buf[:n], nil
 | 
						|
}
 | 
						|
 | 
						|
// Recv waits for and reads a packet coming from Yggdrasil to given buffer, returning size of packet
 | 
						|
func (m *Yggdrasil) RecvBuffer(buf []byte) (int, error) {
 | 
						|
	if m.iprwc == nil {
 | 
						|
		return 0, nil
 | 
						|
	}
 | 
						|
	n, _ := m.iprwc.Read(buf)
 | 
						|
	return n, nil
 | 
						|
}
 | 
						|
 | 
						|
// Stop the mobile Yggdrasil instance
 | 
						|
func (m *Yggdrasil) Stop() error {
 | 
						|
	logger := log.New(m.log, "", 0)
 | 
						|
	logger.EnableLevel("info")
 | 
						|
	logger.Infof("Stopping the mobile Yggdrasil instance %s", "")
 | 
						|
	if m.multicast != nil {
 | 
						|
		logger.Infof("Stopping multicast %s", "")
 | 
						|
		if err := m.multicast.Stop(); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	logger.Infof("Stopping TUN device %s", "")
 | 
						|
	if m.tun != nil {
 | 
						|
		if err := m.tun.Stop(); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	logger.Infof("Stopping Yggdrasil core %s", "")
 | 
						|
	m.core.Stop()
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Retry resets the peer connection timer and tries to dial them immediately.
 | 
						|
func (m *Yggdrasil) RetryPeersNow() {
 | 
						|
	m.core.RetryPeersNow()
 | 
						|
}
 | 
						|
 | 
						|
// GenerateConfigJSON generates mobile-friendly configuration in JSON format
 | 
						|
func GenerateConfigJSON() []byte {
 | 
						|
	nc := config.GenerateConfig()
 | 
						|
	nc.IfName = "none"
 | 
						|
	if json, err := json.Marshal(nc); err == nil {
 | 
						|
		return json
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// GetAddressString gets the node's IPv6 address
 | 
						|
func (m *Yggdrasil) GetAddressString() string {
 | 
						|
	ip := m.core.Address()
 | 
						|
	return ip.String()
 | 
						|
}
 | 
						|
 | 
						|
// GetSubnetString gets the node's IPv6 subnet in CIDR notation
 | 
						|
func (m *Yggdrasil) GetSubnetString() string {
 | 
						|
	subnet := m.core.Subnet()
 | 
						|
	return subnet.String()
 | 
						|
}
 | 
						|
 | 
						|
// GetPublicKeyString gets the node's public key in hex form
 | 
						|
func (m *Yggdrasil) GetPublicKeyString() string {
 | 
						|
	return hex.EncodeToString(m.core.GetSelf().Key)
 | 
						|
}
 | 
						|
 | 
						|
// GetRoutingEntries gets the number of entries in the routing table
 | 
						|
func (m *Yggdrasil) GetRoutingEntries() int {
 | 
						|
	return int(m.core.GetSelf().RoutingEntries)
 | 
						|
}
 | 
						|
 | 
						|
func (m *Yggdrasil) GetPeersJSON() (result string) {
 | 
						|
	peers := []struct {
 | 
						|
		core.PeerInfo
 | 
						|
		IP string
 | 
						|
	}{}
 | 
						|
	for _, v := range m.core.GetPeers() {
 | 
						|
		var ip string
 | 
						|
		if v.Key != nil {
 | 
						|
			a := address.AddrForKey(v.Key)
 | 
						|
			ip = net.IP(a[:]).String()
 | 
						|
		}
 | 
						|
		peers = append(peers, struct {
 | 
						|
			core.PeerInfo
 | 
						|
			IP string
 | 
						|
		}{
 | 
						|
			PeerInfo: v,
 | 
						|
			IP:       ip,
 | 
						|
		})
 | 
						|
	}
 | 
						|
	if res, err := json.Marshal(peers); err == nil {
 | 
						|
		return string(res)
 | 
						|
	} else {
 | 
						|
		return "{}"
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (m *Yggdrasil) GetPathsJSON() (result string) {
 | 
						|
	if res, err := json.Marshal(m.core.GetPaths()); err == nil {
 | 
						|
		return string(res)
 | 
						|
	} else {
 | 
						|
		return "{}"
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (m *Yggdrasil) GetTreeJSON() (result string) {
 | 
						|
	if res, err := json.Marshal(m.core.GetTree()); err == nil {
 | 
						|
		return string(res)
 | 
						|
	} else {
 | 
						|
		return "{}"
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// GetMTU returns the configured node MTU. This must be called AFTER Start.
 | 
						|
func (m *Yggdrasil) GetMTU() int {
 | 
						|
	return int(m.core.MTU())
 | 
						|
}
 | 
						|
 | 
						|
func GetVersion() string {
 | 
						|
	return version.BuildVersion()
 | 
						|
}
 | 
						|
 | 
						|
type ConfigSummary struct {
 | 
						|
	PublicKey   string
 | 
						|
	IPv6Address string
 | 
						|
	IPv6Subnet  string
 | 
						|
}
 | 
						|
 | 
						|
func SummaryForConfig(b []byte) *ConfigSummary {
 | 
						|
	cfg := config.GenerateConfig()
 | 
						|
	if err := cfg.UnmarshalHJSON(b); err != nil {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	pub := ed25519.PrivateKey(cfg.PrivateKey).Public().(ed25519.PublicKey)
 | 
						|
	hpub := hex.EncodeToString(pub)
 | 
						|
	addr := net.IP(address.AddrForKey(pub)[:])
 | 
						|
	snet := net.IPNet{
 | 
						|
		IP:   append(address.SubnetForKey(pub)[:], 0, 0, 0, 0, 0, 0, 0, 0),
 | 
						|
		Mask: net.CIDRMask(64, 128),
 | 
						|
	}
 | 
						|
	return &ConfigSummary{
 | 
						|
		PublicKey:   hpub,
 | 
						|
		IPv6Address: addr.String(),
 | 
						|
		IPv6Subnet:  snet.String(),
 | 
						|
	}
 | 
						|
}
 |