yggdrasil-go/src/core/api.go

289 lines
7.2 KiB
Go
Raw Normal View History

2021-05-23 19:42:26 +00:00
package core
import (
"crypto/ed25519"
"encoding/json"
2022-10-02 12:20:39 +00:00
"fmt"
"net"
"net/url"
2022-10-02 12:20:39 +00:00
"sync/atomic"
"time"
"github.com/Arceliar/phony"
"github.com/yggdrasil-network/yggdrasil-go/src/address"
)
2022-08-06 14:05:12 +00:00
type SelfInfo struct {
2023-03-19 10:33:07 +00:00
Key ed25519.PublicKey
RoutingEntries uint64
}
2022-08-06 14:05:12 +00:00
type PeerInfo struct {
Key ed25519.PublicKey
Root ed25519.PublicKey
Coords []uint64
Port uint64
Priority uint8
Remote string
RXBytes uint64
TXBytes uint64
Uptime time.Duration
}
type TreeEntryInfo struct {
Key ed25519.PublicKey
Parent ed25519.PublicKey
Sequence uint64
//Port uint64
//Rest uint64
}
2022-08-06 14:05:12 +00:00
type PathEntryInfo struct {
2023-03-19 10:33:07 +00:00
Key ed25519.PublicKey
Path []uint64
2023-03-19 10:33:07 +00:00
Sequence uint64
}
2022-08-06 14:05:12 +00:00
type SessionInfo struct {
Key ed25519.PublicKey
RXBytes uint64
TXBytes uint64
Uptime time.Duration
}
2022-08-06 14:05:12 +00:00
func (c *Core) GetSelf() SelfInfo {
var self SelfInfo
s := c.PacketConn.PacketConn.Debug.GetSelf()
self.Key = s.Key
2023-03-19 10:33:07 +00:00
self.RoutingEntries = s.RoutingEntries
return self
}
2022-08-06 14:05:12 +00:00
func (c *Core) GetPeers() []PeerInfo {
2023-03-19 10:33:07 +00:00
peers := []PeerInfo{}
names := make(map[net.Conn]string)
phony.Block(&c.links, func() {
for _, info := range c.links._links {
if info == nil {
continue
}
names[info.conn] = info.lname
}
})
ps := c.PacketConn.PacketConn.Debug.GetPeers()
for _, p := range ps {
2022-08-06 14:05:12 +00:00
var info PeerInfo
info.Key = p.Key
info.Root = p.Root
info.Port = p.Port
info.Priority = p.Priority
2023-03-19 10:33:07 +00:00
if p.Conn != nil {
info.Remote = p.Conn.RemoteAddr().String()
if linkconn, ok := p.Conn.(*linkConn); ok {
info.RXBytes = atomic.LoadUint64(&linkconn.rx)
info.TXBytes = atomic.LoadUint64(&linkconn.tx)
info.Uptime = time.Since(linkconn.up)
}
if name := names[p.Conn]; name != "" {
info.Remote = name
}
}
peers = append(peers, info)
}
return peers
}
func (c *Core) GetTree() []TreeEntryInfo {
var trees []TreeEntryInfo
ts := c.PacketConn.PacketConn.Debug.GetTree()
for _, t := range ts {
var info TreeEntryInfo
info.Key = t.Key
info.Parent = t.Parent
info.Sequence = t.Sequence
//info.Port = d.Port
2023-03-18 12:14:32 +00:00
//info.Rest = d.Rest
trees = append(trees, info)
}
return trees
}
2022-08-06 14:05:12 +00:00
func (c *Core) GetPaths() []PathEntryInfo {
var paths []PathEntryInfo
ps := c.PacketConn.PacketConn.Debug.GetPaths()
for _, p := range ps {
2022-08-06 14:05:12 +00:00
var info PathEntryInfo
info.Key = p.Key
2023-03-19 10:33:07 +00:00
info.Sequence = p.Sequence
info.Path = p.Path
paths = append(paths, info)
}
return paths
}
2022-08-06 14:05:12 +00:00
func (c *Core) GetSessions() []SessionInfo {
var sessions []SessionInfo
ss := c.PacketConn.Debug.GetSessions()
for _, s := range ss {
2022-08-06 14:05:12 +00:00
var info SessionInfo
info.Key = s.Key
info.RXBytes = s.RX
info.TXBytes = s.TX
info.Uptime = s.Uptime
sessions = append(sessions, info)
}
return sessions
}
2021-05-24 02:51:09 +00:00
// Listen starts a new listener (either TCP or TLS). The input should be a url.URL
// parsed from a string of the form e.g. "tcp://a.b.c.d:e". In the case of a
// link-local address, the interface should be provided as the second argument.
func (c *Core) Listen(u *url.URL, sintf string) (*Listener, error) {
switch u.Scheme {
case "tcp":
return c.links.tcp.listen(u, sintf)
case "tls":
return c.links.tls.listen(u, sintf)
case "unix":
return c.links.unix.listen(u, sintf)
default:
return nil, fmt.Errorf("unrecognised scheme %q", u.Scheme)
}
2021-05-24 02:47:12 +00:00
}
// Address gets the IPv6 address of the Yggdrasil node. This is always a /128
2019-09-01 22:47:47 +00:00
// address. The IPv6 address is only relevant when the node is operating as an
// IP router and often is meaningless when embedded into an application, unless
// that application also implements either VPN functionality or deals with IP
// packets specifically.
func (c *Core) Address() net.IP {
addr := net.IP(address.AddrForKey(c.public)[:])
return addr
}
// Subnet gets the routed IPv6 subnet of the Yggdrasil node. This is always a
2019-09-01 22:47:47 +00:00
// /64 subnet. The IPv6 subnet is only relevant when the node is operating as an
// IP router and often is meaningless when embedded into an application, unless
// that application also implements either VPN functionality or deals with IP
// packets specifically.
func (c *Core) Subnet() net.IPNet {
subnet := address.SubnetForKey(c.public)[:]
subnet = append(subnet, 0, 0, 0, 0, 0, 0, 0, 0)
return net.IPNet{IP: subnet, Mask: net.CIDRMask(64, 128)}
}
// SetLogger sets the output logger of the Yggdrasil node after startup. This
2019-09-01 22:47:47 +00:00
// may be useful if you want to redirect the output later. Note that this
// expects a Logger from the github.com/gologme/log package and not from Go's
// built-in log package.
2022-10-02 12:20:39 +00:00
func (c *Core) SetLogger(log Logger) {
c.log = log
}
// AddPeer adds a peer. This should be specified in the peer URI format, e.g.:
//
// tcp://a.b.c.d:e
// socks://a.b.c.d:e/f.g.h.i:j
//
// This adds the peer to the peer list, so that they will be called again if the
// connection drops.
func (c *Core) AddPeer(uri string, sourceInterface string) error {
var known bool
phony.Block(c, func() {
_, known = c.config._peers[Peer{uri, sourceInterface}]
})
if known {
return fmt.Errorf("peer already configured")
}
u, err := url.Parse(uri)
if err != nil {
return err
}
info, err := c.links.call(u, sourceInterface, nil)
if err != nil {
return err
}
phony.Block(c, func() {
c.config._peers[Peer{uri, sourceInterface}] = &info
})
return nil
}
// RemovePeer removes a peer. The peer should be specified in URI format, see AddPeer.
// The peer is not disconnected immediately.
func (c *Core) RemovePeer(uri string, sourceInterface string) error {
var err error
phony.Block(c, func() {
peer := Peer{uri, sourceInterface}
linkInfo, ok := c.config._peers[peer]
if !ok {
err = fmt.Errorf("peer not configured")
return
}
if ok && linkInfo != nil {
c.links.Act(nil, func() {
if link := c.links._links[*linkInfo]; link != nil {
_ = link.close()
}
})
}
delete(c.config._peers, peer)
2020-07-06 13:21:28 +00:00
})
return err
}
// CallPeer calls a peer once. This should be specified in the peer URI format,
// e.g.:
//
// tcp://a.b.c.d:e
// socks://a.b.c.d:e/f.g.h.i:j
//
// This does not add the peer to the peer list, so if the connection drops, the
// peer will not be called again automatically.
2021-05-24 02:47:12 +00:00
func (c *Core) CallPeer(u *url.URL, sintf string) error {
_, err := c.links.call(u, sintf, nil)
return err
}
func (c *Core) PublicKey() ed25519.PublicKey {
return c.public
}
// Hack to get the admin stuff working, TODO something cleaner
type AddHandler interface {
AddHandler(name, desc string, args []string, handlerfunc AddHandlerFunc) error
}
2021-09-01 01:16:57 +00:00
type AddHandlerFunc func(json.RawMessage) (interface{}, error)
// SetAdmin must be called after Init and before Start.
// It sets the admin handler for NodeInfo and the Debug admin functions.
func (c *Core) SetAdmin(a AddHandler) error {
if err := a.AddHandler(
"getNodeInfo", "Request nodeinfo from a remote node by its public key", []string{"key"},
c.proto.nodeinfo.nodeInfoAdminHandler,
); err != nil {
return err
}
if err := a.AddHandler(
"debug_remoteGetSelf", "Debug use only", []string{"key"},
c.proto.getSelfHandler,
); err != nil {
return err
}
if err := a.AddHandler(
"debug_remoteGetPeers", "Debug use only", []string{"key"},
c.proto.getPeersHandler,
); err != nil {
return err
}
if err := a.AddHandler(
"debug_remoteGetTree", "Debug use only", []string{"key"},
c.proto.getTreeHandler,
); err != nil {
return err
}
return nil
}