5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-09-20 14:12:32 +00:00
yggdrasil-go/src/yggdrasil/admin.go

211 lines
5.1 KiB
Go

package yggdrasil
import "net"
import "os"
import "bytes"
import "fmt"
import "sort"
// TODO: Make all of this JSON
// TODO: Add authentication
// TODO: Is any of this thread safe?
type admin struct {
core *Core
listenaddr string
}
func (a *admin) init(c *Core, listenaddr string) {
a.core = c
a.listenaddr = listenaddr
go a.listen()
}
func (a *admin) listen() {
l, err := net.Listen("tcp", a.listenaddr)
if err != nil {
a.core.log.Printf("Admin socket failed to listen: %v", err)
os.Exit(1)
}
defer l.Close()
a.core.log.Printf("Admin socket listening on %s", l.Addr().String())
for {
conn, err := l.Accept()
if err == nil {
a.handleRequest(conn)
}
}
}
func (a *admin) handleRequest(conn net.Conn) {
buf := make([]byte, 1024)
_, err := conn.Read(buf)
if err != nil {
a.core.log.Printf("Admin socket failed to read: %v", err)
conn.Close()
return
}
buf = bytes.Trim(buf, "\x00\r\n\t")
switch string(buf) {
case "dot":
const mDepth = 1024
m := make(map[[mDepth]switchPort]string)
table := a.core.switchTable.table.Load().(lookupTable)
peers := a.core.peers.ports.Load().(map[switchPort]*peer)
// Add my own entry
peerID := address_addrForNodeID(getNodeID(&peers[0].box))
myAddr := net.IP(peerID[:]).String()
var index [mDepth]switchPort
copy(index[:mDepth], table.self.coords[:])
m[index] = myAddr
// Connect switch table entries to peer entries
for _, tableentry := range table.elems {
for _, peerentry := range peers {
if peerentry.port == tableentry.port {
peerID := address_addrForNodeID(getNodeID(&peerentry.box))
addr := net.IP(peerID[:]).String()
var index [mDepth]switchPort
copy(index[:], tableentry.locator.coords)
m[index] = addr
}
}
}
getPorts := func(coords []byte) []switchPort {
var ports []switchPort
for offset := 0; ; {
coord, length := wire_decode_uint64(coords[offset:])
if length == 0 {
break
}
ports = append(ports, switchPort(coord))
offset += length
}
return ports
}
// Look up everything we know from DHT
getDHT := func() {
for i := 0; i < a.core.dht.nBuckets(); i++ {
b := a.core.dht.getBucket(i)
for _, v := range b.infos {
destPorts := getPorts(v.coords)
addr := net.IP(address_addrForNodeID(v.nodeID_hidden)[:]).String()
var index [mDepth]switchPort
copy(index[:], destPorts)
m[index] = addr
}
}
}
a.core.router.doAdmin(getDHT)
// Look up everything we know from active sessions
getSessions := func() {
for _, sinfo := range a.core.sessions.sinfos {
destPorts := getPorts(sinfo.coords)
var index [mDepth]switchPort
copy(index[:], destPorts)
m[index] = net.IP(sinfo.theirAddr[:]).String()
}
}
a.core.router.doAdmin(getSessions)
// Start building a tree from all known nodes
type nodeInfo struct {
name string
key [mDepth]switchPort
parent [mDepth]switchPort
}
infos := make(map[[mDepth]switchPort]nodeInfo)
// First fill the tree with all known nodes, no parents
for k, n := range m {
infos[k] = nodeInfo{
name: n,
key: k,
}
}
// Now go through and create placeholders for any missing nodes
for _, info := range infos {
for idx, port := range info.key {
if port == 0 {
break
}
var key [mDepth]switchPort
copy(key[:idx], info.key[:])
newInfo, isIn := infos[key]
if isIn {
continue
}
newInfo.name = "missing"
newInfo.key = key
infos[key] = newInfo
}
}
// Now go through and attach parents
for _, info := range infos {
info.parent = info.key
for idx := len(info.parent) - 1; idx >= 0; idx-- {
if info.parent[idx] != 0 {
info.parent[idx] = 0
break
}
}
infos[info.key] = info
}
// Finally, get a sorted list of keys, which we use to organize the output
var keys [][mDepth]switchPort
for _, info := range infos {
keys = append(keys, info.key)
}
less := func(i, j int) bool {
for idx := range keys[i] {
if keys[i][idx] < keys[j][idx] {
return true
}
if keys[i][idx] > keys[j][idx] {
return false
}
}
return false
}
sort.Slice(keys, less)
// Now print it all out
conn.Write([]byte(fmt.Sprintf("digraph {\n")))
// First set the labels
for _, key := range keys {
info := infos[key]
if info.name == myAddr {
conn.Write([]byte(fmt.Sprintf("\"%v\" [ style = \"filled\", label = \"%v\" ];\n", info.key, info.name)))
} else {
conn.Write([]byte(fmt.Sprintf("\"%v\" [ label = \"%v\" ];\n", info.key, info.name)))
}
}
// Then print the tree structure
for _, key := range keys {
info := infos[key]
if info.key == info.parent {
continue
} // happens for the root, skip it
for idx := len(info.key) - 1; idx >= 0; idx-- {
port := info.key[idx]
if port == 0 {
continue
}
conn.Write([]byte(fmt.Sprintf(" \"%+v\" -> \"%+v\" [ label = \"%v\" ];\n", info.parent, info.key, port)))
break
}
}
conn.Write([]byte(fmt.Sprintf("}\n")))
break
default:
conn.Write([]byte("I didn't understand that!\n"))
}
if err != nil {
a.core.log.Printf("Admin socket error: %v", err)
}
conn.Close()
}