github.com/yggdrasil-network/yggdrasil-go@v0.5.6/contrib/mobile/mobile.go (about)

     1  package mobile
     2  
     3  import (
     4  	"encoding/hex"
     5  	"encoding/json"
     6  	"net"
     7  	"regexp"
     8  
     9  	"github.com/gologme/log"
    10  
    11  	"github.com/yggdrasil-network/yggdrasil-go/src/address"
    12  	"github.com/yggdrasil-network/yggdrasil-go/src/config"
    13  	"github.com/yggdrasil-network/yggdrasil-go/src/core"
    14  	"github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc"
    15  	"github.com/yggdrasil-network/yggdrasil-go/src/multicast"
    16  	"github.com/yggdrasil-network/yggdrasil-go/src/tun"
    17  	"github.com/yggdrasil-network/yggdrasil-go/src/version"
    18  
    19  	_ "golang.org/x/mobile/bind"
    20  )
    21  
    22  // Yggdrasil mobile package is meant to "plug the gap" for mobile support, as
    23  // Gomobile will not create headers for Swift/Obj-C etc if they have complex
    24  // (non-native) types. Therefore for iOS we will expose some nice simple
    25  // functions. Note that in the case of iOS we handle reading/writing to/from TUN
    26  // in Swift therefore we use the "dummy" TUN interface instead.
    27  type Yggdrasil struct {
    28  	core      *core.Core
    29  	iprwc     *ipv6rwc.ReadWriteCloser
    30  	config    *config.NodeConfig
    31  	multicast *multicast.Multicast
    32  	tun       *tun.TunAdapter // optional
    33  	log       MobileLogger
    34  	logger    *log.Logger
    35  }
    36  
    37  // StartAutoconfigure starts a node with a randomly generated config
    38  func (m *Yggdrasil) StartAutoconfigure() error {
    39  	return m.StartJSON([]byte("{}"))
    40  }
    41  
    42  // StartJSON starts a node with the given JSON config. You can get JSON config
    43  // (rather than HJSON) by using the GenerateConfigJSON() function
    44  func (m *Yggdrasil) StartJSON(configjson []byte) error {
    45  	setMemLimitIfPossible()
    46  
    47  	logger := log.New(m.log, "", 0)
    48  	logger.EnableLevel("error")
    49  	logger.EnableLevel("warn")
    50  	logger.EnableLevel("info")
    51  	m.logger = logger
    52  	m.config = config.GenerateConfig()
    53  	if err := m.config.UnmarshalHJSON(configjson); err != nil {
    54  		return err
    55  	}
    56  	// Set up the Yggdrasil node itself.
    57  	{
    58  		options := []core.SetupOption{}
    59  		for _, peer := range m.config.Peers {
    60  			options = append(options, core.Peer{URI: peer})
    61  		}
    62  		for intf, peers := range m.config.InterfacePeers {
    63  			for _, peer := range peers {
    64  				options = append(options, core.Peer{URI: peer, SourceInterface: intf})
    65  			}
    66  		}
    67  		for _, allowed := range m.config.AllowedPublicKeys {
    68  			k, err := hex.DecodeString(allowed)
    69  			if err != nil {
    70  				panic(err)
    71  			}
    72  			options = append(options, core.AllowedPublicKey(k[:]))
    73  		}
    74  		for _, lAddr := range m.config.Listen {
    75  			options = append(options, core.ListenAddress(lAddr))
    76  		}
    77  		var err error
    78  		m.core, err = core.New(m.config.Certificate, logger, options...)
    79  		if err != nil {
    80  			panic(err)
    81  		}
    82  		address, subnet := m.core.Address(), m.core.Subnet()
    83  		logger.Infof("Your public key is %s", hex.EncodeToString(m.core.PublicKey()))
    84  		logger.Infof("Your IPv6 address is %s", address.String())
    85  		logger.Infof("Your IPv6 subnet is %s", subnet.String())
    86  	}
    87  
    88  	// Set up the multicast module.
    89  	if len(m.config.MulticastInterfaces) > 0 {
    90  		var err error
    91  		logger.Infof("Initializing multicast %s", "")
    92  		options := []multicast.SetupOption{}
    93  		for _, intf := range m.config.MulticastInterfaces {
    94  			options = append(options, multicast.MulticastInterface{
    95  				Regex:    regexp.MustCompile(intf.Regex),
    96  				Beacon:   intf.Beacon,
    97  				Listen:   intf.Listen,
    98  				Port:     intf.Port,
    99  				Priority: uint8(intf.Priority),
   100  				Password: intf.Password,
   101  			})
   102  		}
   103  		logger.Infof("Starting multicast %s", "")
   104  		m.multicast, err = multicast.New(m.core, m.logger, options...)
   105  		if err != nil {
   106  			logger.Errorln("An error occurred starting multicast:", err)
   107  		}
   108  	}
   109  
   110  	mtu := m.config.IfMTU
   111  	m.iprwc = ipv6rwc.NewReadWriteCloser(m.core)
   112  	if m.iprwc.MaxMTU() < mtu {
   113  		mtu = m.iprwc.MaxMTU()
   114  	}
   115  	m.iprwc.SetMTU(mtu)
   116  	return nil
   117  }
   118  
   119  // Send sends a packet to Yggdrasil. It should be a fully formed
   120  // IPv6 packet
   121  func (m *Yggdrasil) Send(p []byte) error {
   122  	if m.iprwc == nil {
   123  		return nil
   124  	}
   125  	_, _ = m.iprwc.Write(p)
   126  	return nil
   127  }
   128  
   129  // Send sends a packet from given buffer to Yggdrasil. From first byte up to length.
   130  func (m *Yggdrasil) SendBuffer(p []byte, length int) error {
   131  	if m.iprwc == nil {
   132  		return nil
   133  	}
   134  	if len(p) < length {
   135  		return nil
   136  	}
   137  	_, _ = m.iprwc.Write(p[:length])
   138  	return nil
   139  }
   140  
   141  // Recv waits for and reads a packet coming from Yggdrasil. It
   142  // will be a fully formed IPv6 packet
   143  func (m *Yggdrasil) Recv() ([]byte, error) {
   144  	if m.iprwc == nil {
   145  		return nil, nil
   146  	}
   147  	var buf [65535]byte
   148  	n, _ := m.iprwc.Read(buf[:])
   149  	return buf[:n], nil
   150  }
   151  
   152  // Recv waits for and reads a packet coming from Yggdrasil to given buffer, returning size of packet
   153  func (m *Yggdrasil) RecvBuffer(buf []byte) (int, error) {
   154  	if m.iprwc == nil {
   155  		return 0, nil
   156  	}
   157  	n, _ := m.iprwc.Read(buf)
   158  	return n, nil
   159  }
   160  
   161  // Stop the mobile Yggdrasil instance
   162  func (m *Yggdrasil) Stop() error {
   163  	logger := log.New(m.log, "", 0)
   164  	logger.EnableLevel("info")
   165  	logger.Infof("Stopping the mobile Yggdrasil instance %s", "")
   166  	if m.multicast != nil {
   167  		logger.Infof("Stopping multicast %s", "")
   168  		if err := m.multicast.Stop(); err != nil {
   169  			return err
   170  		}
   171  	}
   172  	logger.Infof("Stopping TUN device %s", "")
   173  	if m.tun != nil {
   174  		if err := m.tun.Stop(); err != nil {
   175  			return err
   176  		}
   177  	}
   178  	logger.Infof("Stopping Yggdrasil core %s", "")
   179  	m.core.Stop()
   180  	return nil
   181  }
   182  
   183  // Retry resets the peer connection timer and tries to dial them immediately.
   184  func (m *Yggdrasil) RetryPeersNow() {
   185  	m.core.RetryPeersNow()
   186  }
   187  
   188  // GenerateConfigJSON generates mobile-friendly configuration in JSON format
   189  func GenerateConfigJSON() []byte {
   190  	nc := config.GenerateConfig()
   191  	nc.IfName = "none"
   192  	if json, err := json.Marshal(nc); err == nil {
   193  		return json
   194  	}
   195  	return nil
   196  }
   197  
   198  // GetAddressString gets the node's IPv6 address
   199  func (m *Yggdrasil) GetAddressString() string {
   200  	ip := m.core.Address()
   201  	return ip.String()
   202  }
   203  
   204  // GetSubnetString gets the node's IPv6 subnet in CIDR notation
   205  func (m *Yggdrasil) GetSubnetString() string {
   206  	subnet := m.core.Subnet()
   207  	return subnet.String()
   208  }
   209  
   210  // GetPublicKeyString gets the node's public key in hex form
   211  func (m *Yggdrasil) GetPublicKeyString() string {
   212  	return hex.EncodeToString(m.core.GetSelf().Key)
   213  }
   214  
   215  // GetRoutingEntries gets the number of entries in the routing table
   216  func (m *Yggdrasil) GetRoutingEntries() int {
   217  	return int(m.core.GetSelf().RoutingEntries)
   218  }
   219  
   220  func (m *Yggdrasil) GetPeersJSON() (result string) {
   221  	peers := []struct {
   222  		core.PeerInfo
   223  		IP string
   224  	}{}
   225  	for _, v := range m.core.GetPeers() {
   226  		var ip string
   227  		if v.Key != nil {
   228  			a := address.AddrForKey(v.Key)
   229  			ip = net.IP(a[:]).String()
   230  		}
   231  		peers = append(peers, struct {
   232  			core.PeerInfo
   233  			IP string
   234  		}{
   235  			PeerInfo: v,
   236  			IP:       ip,
   237  		})
   238  	}
   239  	if res, err := json.Marshal(peers); err == nil {
   240  		return string(res)
   241  	} else {
   242  		return "{}"
   243  	}
   244  }
   245  
   246  func (m *Yggdrasil) GetPathsJSON() (result string) {
   247  	if res, err := json.Marshal(m.core.GetPaths()); err == nil {
   248  		return string(res)
   249  	} else {
   250  		return "{}"
   251  	}
   252  }
   253  
   254  func (m *Yggdrasil) GetTreeJSON() (result string) {
   255  	if res, err := json.Marshal(m.core.GetTree()); err == nil {
   256  		return string(res)
   257  	} else {
   258  		return "{}"
   259  	}
   260  }
   261  
   262  // GetMTU returns the configured node MTU. This must be called AFTER Start.
   263  func (m *Yggdrasil) GetMTU() int {
   264  	return int(m.core.MTU())
   265  }
   266  
   267  func GetVersion() string {
   268  	return version.BuildVersion()
   269  }