Start JSON-ifying the admin socket

This commit is contained in:
Neil Alexander 2018-05-20 17:21:14 +01:00
parent 89b7be1409
commit a6ef7166ea
No known key found for this signature in database
GPG Key ID: A02A2019A2BB0944

View File

@ -2,8 +2,10 @@ package yggdrasil
import "net" import "net"
import "os" import "os"
import "bytes"
// import "bytes"
import "encoding/hex" import "encoding/hex"
import "encoding/json"
import "errors" import "errors"
import "fmt" import "fmt"
import "net/url" import "net/url"
@ -22,45 +24,86 @@ type admin struct {
handlers []admin_handlerInfo handlers []admin_handlerInfo
} }
type admin_info map[string]interface{}
type admin_handlerInfo struct { type admin_handlerInfo struct {
name string // Checked against the first word of the api call name string // Checked against the first word of the api call
args []string // List of human-readable argument names args []string // List of human-readable argument names
handler func(*[]byte, ...string) // First arg is pointer to the out slice, rest is args handler func(admin_info) (admin_info, error) // First is input map, second is output
} }
func (a *admin) addHandler(name string, args []string, handler func(*[]byte, ...string)) { // Maps things like "IP", "port", "bucket", or "coords" onto strings
type admin_pair struct {
key string
val interface{}
}
type admin_nodeInfo []admin_pair
func (a *admin) addHandler(name string, args []string, handler func(admin_info) (admin_info, error)) {
a.handlers = append(a.handlers, admin_handlerInfo{name, args, handler}) a.handlers = append(a.handlers, admin_handlerInfo{name, args, handler})
} }
func (a *admin) init(c *Core, listenaddr string) { func (a *admin) init(c *Core, listenaddr string) {
a.core = c a.core = c
a.listenaddr = listenaddr a.listenaddr = listenaddr
a.addHandler("help", nil, func(out *[]byte, _ ...string) { a.addHandler("help", nil, func(in admin_info) (admin_info, error) {
handlers := make(map[string][]string)
for _, handler := range a.handlers { for _, handler := range a.handlers {
tmp := append([]string{handler.name}, handler.args...) handlers[handler.name] = handler.args
*out = append(*out, []byte(strings.Join(tmp, " "))...)
*out = append(*out, "\n"...)
} }
return admin_info{"handlers": handlers}, nil
}) })
// TODO? have other parts of the program call to add their own handlers a.addHandler("dot", nil, func(in admin_info) (admin_info, error) {
a.addHandler("dot", nil, func(out *[]byte, _ ...string) { return admin_info{"dot": string(a.getResponse_dot())}, nil
*out = a.getResponse_dot()
}) })
a.addHandler("getSelf", nil, func(out *[]byte, _ ...string) { a.addHandler("getSelf", nil, func(in admin_info) (admin_info, error) {
*out = []byte(a.printInfos([]admin_nodeInfo{*a.getData_getSelf()})) return admin_info{"self": a.getData_getSelf().asMap()}, nil
}) })
a.addHandler("getPeers", nil, func(out *[]byte, _ ...string) { a.addHandler("getPeers", nil, func(in admin_info) (admin_info, error) {
*out = []byte(a.printInfos(a.getData_getPeers())) sort := "ip"
peers := make(admin_info)
for _, peerdata := range a.getData_getPeers() {
p := peerdata.asMap()
so := fmt.Sprint(p[sort])
peers[so] = p
delete(peers[so].(map[string]interface{}), sort)
}
return admin_info{"peers": peers}, nil
}) })
a.addHandler("getSwitchPeers", nil, func(out *[]byte, _ ...string) { a.addHandler("getSwitchPeers", nil, func(in admin_info) (admin_info, error) {
*out = []byte(a.printInfos(a.getData_getSwitchPeers())) sort := "port"
switchpeers := make(admin_info)
for _, s := range a.getData_getSwitchPeers() {
p := s.asMap()
so := fmt.Sprint(p[sort])
switchpeers[so] = p
delete(switchpeers[so].(map[string]interface{}), sort)
}
return admin_info{"switchpeers": switchpeers}, nil
}) })
a.addHandler("getDHT", nil, func(out *[]byte, _ ...string) { a.addHandler("getDHT", nil, func(in admin_info) (admin_info, error) {
*out = []byte(a.printInfos(a.getData_getDHT())) sort := "ip"
dht := make(admin_info)
for _, d := range a.getData_getDHT() {
p := d.asMap()
so := fmt.Sprint(p[sort])
dht[so] = p
delete(dht[so].(map[string]interface{}), sort)
}
return admin_info{"dht": dht}, nil
}) })
a.addHandler("getSessions", nil, func(out *[]byte, _ ...string) { a.addHandler("getSessions", nil, func(in admin_info) (admin_info, error) {
*out = []byte(a.printInfos(a.getData_getSessions())) sort := "ip"
sessions := make(admin_info)
for _, s := range a.getData_getSessions() {
p := s.asMap()
so := fmt.Sprint(p[sort])
sessions[so] = p
delete(sessions[so].(map[string]interface{}), sort)
}
return admin_info{"sessions": sessions}, nil
}) })
/*
a.addHandler("addPeer", []string{"<proto://address:port>"}, func(out *[]byte, saddr ...string) { a.addHandler("addPeer", []string{"<proto://address:port>"}, func(out *[]byte, saddr ...string) {
if a.addPeer(saddr[0]) == nil { if a.addPeer(saddr[0]) == nil {
*out = []byte("Adding peer: " + saddr[0] + "\n") *out = []byte("Adding peer: " + saddr[0] + "\n")
@ -89,7 +132,7 @@ func (a *admin) init(c *Core, listenaddr string) {
info = admin_nodeInfo{ info = admin_nodeInfo{
{"Interface name", a.core.tun.iface.Name()}, {"Interface name", a.core.tun.iface.Name()},
{"TAP mode", strconv.FormatBool(a.core.tun.iface.IsTAP())}, {"TAP mode", strconv.FormatBool(a.core.tun.iface.IsTAP())},
{"MTU", strconv.Itoa(a.core.tun.mtu)}, {"mtu", strconv.Itoa(a.core.tun.mtu)},
} }
*out = []byte(a.printInfos([]admin_nodeInfo{info})) *out = []byte(a.printInfos([]admin_nodeInfo{info}))
}) })
@ -120,7 +163,7 @@ func (a *admin) init(c *Core, listenaddr string) {
info := admin_nodeInfo{ info := admin_nodeInfo{
{"Interface name", ifparams[0]}, {"Interface name", ifparams[0]},
{"TAP mode", strconv.FormatBool(iftapmode)}, {"TAP mode", strconv.FormatBool(iftapmode)},
{"MTU", strconv.Itoa(ifmtu)}, {"mtu", strconv.Itoa(ifmtu)},
} }
*out = []byte(a.printInfos([]admin_nodeInfo{info})) *out = []byte(a.printInfos([]admin_nodeInfo{info}))
} }
@ -141,7 +184,7 @@ func (a *admin) init(c *Core, listenaddr string) {
} else { } else {
*out = []byte("Failed to remove key: " + sport[0] + "\n") *out = []byte("Failed to remove key: " + sport[0] + "\n")
} }
}) })*/
go a.listen() go a.listen()
} }
@ -162,49 +205,69 @@ func (a *admin) listen() {
} }
func (a *admin) handleRequest(conn net.Conn) { func (a *admin) handleRequest(conn net.Conn) {
buf := make([]byte, 1024) defer conn.Close()
_, err := conn.Read(buf) decoder := json.NewDecoder(conn)
if err != nil { encoder := json.NewEncoder(conn)
a.core.log.Printf("Admin socket failed to read: %v", err) encoder.SetIndent("", " ")
conn.Close() recv := make(admin_info)
send := make(admin_info)
for {
if err := decoder.Decode(&recv); err != nil {
fmt.Println("Admin socket JSON decode error:", err)
return return
} }
var out []byte
buf = bytes.Trim(buf, "\x00\r\n\t") handlers:
call := strings.Split(string(buf), " ")
var cmd string
var args []string
if len(call) > 0 {
cmd = call[0]
args = call[1:]
}
done := false
for _, handler := range a.handlers { for _, handler := range a.handlers {
if cmd == handler.name { if recv["request"] == handler.name {
handler.handler(&out, args...) // Check that we have all the required arguments
done = true for _, arg := range handler.args {
// An argument in <pointy brackets> is optional and not required,
// so we can safely ignore those
if strings.HasPrefix(arg, "<") && strings.HasSuffix(arg, ">") {
continue
}
// Check if the field is missing
if _, ok := recv[arg]; !ok {
fmt.Println("Missing required argument", arg)
send = admin_info{
"error": "Missing field '" + arg + "'",
"fields": handler.args,
}
break handlers
}
}
// By this point we should have all the fields we need, so call
// the handler
response, err := handler.handler(recv)
if err != nil {
send = admin_info{
"request": recv["request"],
"error": err.Error(),
}
} else {
send = admin_info{
"request": recv["request"],
"response": response,
}
}
break break
} }
} }
if !done {
out = []byte("I didn't understand that!\n") if err := encoder.Encode(&send); err != nil {
fmt.Println("Admin socket JSON encode error:", err)
return
} }
_, err = conn.Write(out)
if err != nil { return
a.core.log.Printf("Admin socket error: %v", err)
} }
conn.Close()
} }
// Maps things like "IP", "port", "bucket", or "coords" onto strings func (n *admin_nodeInfo) asMap() map[string]interface{} {
type admin_pair struct { m := make(map[string]interface{}, len(*n))
key string
val string
}
type admin_nodeInfo []admin_pair
func (n *admin_nodeInfo) asMap() map[string]string {
m := make(map[string]string, len(*n))
for _, p := range *n { for _, p := range *n {
m[p.key] = p.val m[p.key] = p.val
} }
@ -303,7 +366,7 @@ func (a *admin) getData_getSelf() *admin_nodeInfo {
addr := a.core.router.addr addr := a.core.router.addr
coords := table.self.getCoords() coords := table.self.getCoords()
self := admin_nodeInfo{ self := admin_nodeInfo{
{"IP", net.IP(addr[:]).String()}, {"ip", net.IP(addr[:]).String()},
{"coords", fmt.Sprint(coords)}, {"coords", fmt.Sprint(coords)},
} }
return &self return &self
@ -321,11 +384,11 @@ func (a *admin) getData_getPeers() []admin_nodeInfo {
p := ports[port] p := ports[port]
addr := *address_addrForNodeID(getNodeID(&p.box)) addr := *address_addrForNodeID(getNodeID(&p.box))
info := admin_nodeInfo{ info := admin_nodeInfo{
{"IP", net.IP(addr[:]).String()}, {"ip", net.IP(addr[:]).String()},
{"port", fmt.Sprint(port)}, {"port", port},
{"uptime", fmt.Sprint(time.Since(p.firstSeen))}, {"uptime", fmt.Sprint(time.Since(p.firstSeen))},
{"bytesSent", fmt.Sprint(atomic.LoadUint64(&p.bytesSent))}, {"bytes_sent", atomic.LoadUint64(&p.bytesSent)},
{"bytesRecvd", fmt.Sprint(atomic.LoadUint64(&p.bytesRecvd))}, {"bytes_recvd", atomic.LoadUint64(&p.bytesRecvd)},
} }
peerInfos = append(peerInfos, info) peerInfos = append(peerInfos, info)
} }
@ -344,9 +407,9 @@ func (a *admin) getData_getSwitchPeers() []admin_nodeInfo {
addr := *address_addrForNodeID(getNodeID(&peer.box)) addr := *address_addrForNodeID(getNodeID(&peer.box))
coords := elem.locator.getCoords() coords := elem.locator.getCoords()
info := admin_nodeInfo{ info := admin_nodeInfo{
{"IP", net.IP(addr[:]).String()}, {"ip", net.IP(addr[:]).String()},
{"coords", fmt.Sprint(coords)}, {"coords", fmt.Sprint(coords)},
{"port", fmt.Sprint(elem.port)}, {"port", elem.port},
} }
peerInfos = append(peerInfos, info) peerInfos = append(peerInfos, info)
} }
@ -363,11 +426,11 @@ func (a *admin) getData_getDHT() []admin_nodeInfo {
for _, v := range vs { for _, v := range vs {
addr := *address_addrForNodeID(v.getNodeID()) addr := *address_addrForNodeID(v.getNodeID())
info := admin_nodeInfo{ info := admin_nodeInfo{
{"IP", net.IP(addr[:]).String()}, {"ip", net.IP(addr[:]).String()},
{"coords", fmt.Sprint(v.coords)}, {"coords", fmt.Sprint(v.coords)},
{"bucket", fmt.Sprint(i)}, {"bucket", i},
{"peerOnly", fmt.Sprint(isPeer)}, {"peer_only", isPeer},
{"lastSeen", fmt.Sprint(now.Sub(v.recv))}, {"last_seen", fmt.Sprint(now.Sub(v.recv))},
} }
infos = append(infos, info) infos = append(infos, info)
} }
@ -386,12 +449,12 @@ func (a *admin) getData_getSessions() []admin_nodeInfo {
for _, sinfo := range a.core.sessions.sinfos { for _, sinfo := range a.core.sessions.sinfos {
// TODO? skipped known but timed out sessions? // TODO? skipped known but timed out sessions?
info := admin_nodeInfo{ info := admin_nodeInfo{
{"IP", net.IP(sinfo.theirAddr[:]).String()}, {"ip", net.IP(sinfo.theirAddr[:]).String()},
{"coords", fmt.Sprint(sinfo.coords)}, {"coords", fmt.Sprint(sinfo.coords)},
{"MTU", fmt.Sprint(sinfo.getMTU())}, {"mtu", sinfo.getMTU()},
{"wasMTUFixed", fmt.Sprint(sinfo.wasMTUFixed)}, {"was_mtu_fixed", sinfo.wasMTUFixed},
{"bytesSent", fmt.Sprint(sinfo.bytesSent)}, {"bytes_sent", sinfo.bytesSent},
{"bytesRecvd", fmt.Sprint(sinfo.bytesRecvd)}, {"bytes_recvd", sinfo.bytesRecvd},
} }
infos = append(infos, info) infos = append(infos, info)
} }
@ -438,18 +501,18 @@ func (a *admin) getResponse_dot() []byte {
sessions := a.getData_getSessions() sessions := a.getData_getSessions()
// Map of coords onto IP // Map of coords onto IP
m := make(map[string]string) m := make(map[string]string)
m[self["coords"]] = self["IP"] m[self["coords"].(string)] = self["IP"].(string)
for _, peer := range peers { for _, peer := range peers {
p := peer.asMap() p := peer.asMap()
m[p["coords"]] = p["IP"] m[p["coords"].(string)] = p["IP"].(string)
} }
for _, node := range dht { for _, node := range dht {
n := node.asMap() n := node.asMap()
m[n["coords"]] = n["IP"] m[n["coords"].(string)] = n["IP"].(string)
} }
for _, node := range sessions { for _, node := range sessions {
n := node.asMap() n := node.asMap()
m[n["coords"]] = n["IP"] m[n["coords"].(string)] = n["IP"].(string)
} }
// Start building a tree from all known nodes // Start building a tree from all known nodes