From 201701ae4a2ecf9b9ce625e4a687dcc5d47c7df4 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 May 2018 13:54:51 +0100 Subject: [PATCH 1/7] Add some pretty printing to yggdrasilctl, small modifications to JSON formatting in admin socket --- src/yggdrasil/admin.go | 27 ++++++++++-------- yggdrasilctl.go | 63 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 0509a30..35bdee0 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -44,17 +44,20 @@ func (a *admin) init(c *Core, listenaddr string) { a.core = c a.listenaddr = listenaddr a.addHandler("help", nil, func(in admin_info) (admin_info, error) { - handlers := make(map[string][]string) + handlers := make(map[string]interface{}) for _, handler := range a.handlers { - handlers[handler.name] = handler.args + handlers[handler.name] = admin_info{ "fields": handler.args } } - return admin_info{"handlers": handlers}, nil + return admin_info{"help": handlers}, nil }) a.addHandler("dot", []string{}, func(in admin_info) (admin_info, error) { return admin_info{"dot": string(a.getResponse_dot())}, nil }) a.addHandler("getSelf", []string{}, func(in admin_info) (admin_info, error) { - return admin_info{"self": a.getData_getSelf().asMap()}, nil + self := a.getData_getSelf().asMap() + ip := fmt.Sprint(self["ip"]) + delete(self, "ip") + return admin_info{"self": admin_info{ ip: self }}, nil }) a.addHandler("getPeers", []string{}, func(in admin_info) (admin_info, error) { sort := "ip" @@ -133,14 +136,15 @@ func (a *admin) init(c *Core, listenaddr string) { a.addHandler("getTunTap", []string{}, func(in admin_info) (r admin_info, e error) { defer func() { recover() - r = admin_info{"name": "none"} + r = admin_info{ "tuntap": admin_info{ "none": admin_info{ } } } e = nil }() return admin_info{ - "name": a.core.tun.iface.Name(), - "tap_mode": a.core.tun.iface.IsTAP(), - "mtu": a.core.tun.mtu, + a.core.tun.iface.Name(): admin_info{ + "tap_mode": a.core.tun.iface.IsTAP(), + "mtu": a.core.tun.mtu, + }, }, nil }) a.addHandler("setTunTap", []string{"name", "[tap_mode]", "[mtu]"}, func(in admin_info) (admin_info, error) { @@ -162,9 +166,10 @@ func (a *admin) init(c *Core, listenaddr string) { return admin_info{}, errors.New("Failed to configure adapter") } else { return admin_info{ - "name": a.core.tun.iface.Name(), - "tap_mode": a.core.tun.iface.IsTAP(), - "mtu": ifmtu, + a.core.tun.iface.Name(): admin_info{ + "tap_mode": a.core.tun.iface.IsTAP(), + "mtu": ifmtu, + }, }, nil } }) diff --git a/yggdrasilctl.go b/yggdrasilctl.go index ad05963..ba42bea 100644 --- a/yggdrasilctl.go +++ b/yggdrasilctl.go @@ -4,6 +4,7 @@ import "flag" import "fmt" import "strings" import "net" +import "sort" import "encoding/json" import "strconv" import "os" @@ -58,23 +59,77 @@ func main() { panic(err) } if err := decoder.Decode(&recv); err == nil { + if recv["status"] == "error" { + if err, ok := recv["error"]; ok { + fmt.Println(err) + } else { + fmt.Println("Unspecified error occured") + } + os.Exit(1) + } if _, ok := recv["request"]; !ok { - fmt.Println("Missing request") + fmt.Println("Missing request in response (malformed response?)") return } if _, ok := recv["response"]; !ok { - fmt.Println("Missing response") + fmt.Println("Missing response body (malformed response?)") return } req := recv["request"].(map[string]interface{}) res := recv["response"].(map[string]interface{}) + defer func() { + recover() + if json, err := json.MarshalIndent(recv["response"], "", " "); err == nil { + fmt.Println(string(json)) + } + }() switch req["request"] { case "dot": fmt.Println(res["dot"]) default: - if json, err := json.MarshalIndent(recv["response"], "", " "); err == nil { - fmt.Println(string(json)) + maxWidths := make(map[string]int) + var keyOrder []string + keysOrdered := false + + for _, tlv := range res { + for slk, slv := range tlv.(map[string]interface{}) { + if !keysOrdered { + for k := range slv.(map[string]interface{}) { + keyOrder = append(keyOrder, fmt.Sprint(k)) + } + sort.Strings(keyOrder) + keysOrdered = true + } + for k, v := range slv.(map[string]interface{}) { + if len(fmt.Sprint(slk)) > maxWidths["key"] { + maxWidths["key"] = len(fmt.Sprint(slk)) + } + if len(fmt.Sprint(v)) > maxWidths[k] { + maxWidths[k] = len(fmt.Sprint(v)) + if maxWidths[k] < len(k) { + maxWidths[k] = len(k) + } + } + } + } + + if len(keyOrder) > 0 { + fmt.Printf("%-" + fmt.Sprint(maxWidths["key"]) + "s ", "") + for _, v := range keyOrder { + fmt.Printf("%-" + fmt.Sprint(maxWidths[v]) + "s ", v) + } + fmt.Println() + } + + for slk, slv := range tlv.(map[string]interface{}) { + fmt.Printf("%-" + fmt.Sprint(maxWidths["key"]) + "s ", slk) + for _, k := range keyOrder { + fmt.Printf("%-" + fmt.Sprint(maxWidths[k]) + "s ", fmt.Sprint(slv.(map[string]interface{})[k])) + } + fmt.Println() + } } + } } From ca3e541d3b27645a2802a3c2ffff2e6288a789fc Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 May 2018 14:14:57 +0100 Subject: [PATCH 2/7] Friendly formats for getDHT, getSessions, setTunTap etc --- src/yggdrasil/admin.go | 8 ++++---- yggdrasilctl.go | 34 +++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 35bdee0..0403514 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -46,7 +46,7 @@ func (a *admin) init(c *Core, listenaddr string) { a.addHandler("help", nil, func(in admin_info) (admin_info, error) { handlers := make(map[string]interface{}) for _, handler := range a.handlers { - handlers[handler.name] = admin_info{ "fields": handler.args } + handlers[handler.name] = admin_info{"fields": handler.args} } return admin_info{"help": handlers}, nil }) @@ -57,7 +57,7 @@ func (a *admin) init(c *Core, listenaddr string) { self := a.getData_getSelf().asMap() ip := fmt.Sprint(self["ip"]) delete(self, "ip") - return admin_info{"self": admin_info{ ip: self }}, nil + return admin_info{"self": admin_info{ip: self}}, nil }) a.addHandler("getPeers", []string{}, func(in admin_info) (admin_info, error) { sort := "ip" @@ -136,7 +136,7 @@ func (a *admin) init(c *Core, listenaddr string) { a.addHandler("getTunTap", []string{}, func(in admin_info) (r admin_info, e error) { defer func() { recover() - r = admin_info{ "tuntap": admin_info{ "none": admin_info{ } } } + r = admin_info{"none": admin_info{}} e = nil }() @@ -278,7 +278,7 @@ func (a *admin) handleRequest(conn net.Conn) { if _, ok := recv[arg]; !ok { send = admin_info{ "status": "error", - "error": "Expected field missing", + "error": "Expected field missing: " + arg, "expecting": arg, } break handlers diff --git a/yggdrasilctl.go b/yggdrasilctl.go index ba42bea..a2fd9c3 100644 --- a/yggdrasilctl.go +++ b/yggdrasilctl.go @@ -61,7 +61,7 @@ func main() { if err := decoder.Decode(&recv); err == nil { if recv["status"] == "error" { if err, ok := recv["error"]; ok { - fmt.Println(err) + fmt.Println("Error:", err) } else { fmt.Println("Unspecified error occured") } @@ -77,16 +77,11 @@ func main() { } req := recv["request"].(map[string]interface{}) res := recv["response"].(map[string]interface{}) - defer func() { - recover() - if json, err := json.MarshalIndent(recv["response"], "", " "); err == nil { - fmt.Println(string(json)) - } - }() + switch req["request"] { case "dot": fmt.Println(res["dot"]) - default: + case "help", "getPeers", "getSwitchPeers", "getDHT", "getSessions": maxWidths := make(map[string]int) var keyOrder []string keysOrdered := false @@ -114,22 +109,35 @@ func main() { } if len(keyOrder) > 0 { - fmt.Printf("%-" + fmt.Sprint(maxWidths["key"]) + "s ", "") + fmt.Printf("%-"+fmt.Sprint(maxWidths["key"])+"s ", "") for _, v := range keyOrder { - fmt.Printf("%-" + fmt.Sprint(maxWidths[v]) + "s ", v) + fmt.Printf("%-"+fmt.Sprint(maxWidths[v])+"s ", v) } fmt.Println() } for slk, slv := range tlv.(map[string]interface{}) { - fmt.Printf("%-" + fmt.Sprint(maxWidths["key"]) + "s ", slk) + fmt.Printf("%-"+fmt.Sprint(maxWidths["key"])+"s ", slk) for _, k := range keyOrder { - fmt.Printf("%-" + fmt.Sprint(maxWidths[k]) + "s ", fmt.Sprint(slv.(map[string]interface{})[k])) + fmt.Printf("%-"+fmt.Sprint(maxWidths[k])+"s ", fmt.Sprint(slv.(map[string]interface{})[k])) } fmt.Println() } } - + case "getTunTap", "setTunTap": + for k, v := range res { + fmt.Println("Interface name:", k) + if mtu, ok := v.(map[string]interface{})["mtu"].(float64); ok { + fmt.Println("Interface MTU:", mtu) + } + if tap_mode, ok := v.(map[string]interface{})["tap_mode"].(bool); ok { + fmt.Println("TAP mode:", tap_mode) + } + } + default: + if json, err := json.MarshalIndent(recv["response"], "", " "); err == nil { + fmt.Println(string(json)) + } } } From 61c640dbdeb6dfa72d7ff4d7f0e83967d81b00ec Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 May 2018 14:25:11 +0100 Subject: [PATCH 3/7] Add addPeer and removePeer --- yggdrasilctl.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/yggdrasilctl.go b/yggdrasilctl.go index a2fd9c3..48ea19b 100644 --- a/yggdrasilctl.go +++ b/yggdrasilctl.go @@ -134,6 +134,27 @@ func main() { fmt.Println("TAP mode:", tap_mode) } } + case "addPeer", "removePeer": + if _, ok := res["added"]; ok { + for _, v := range res["added"].([]interface{}) { + fmt.Println("Peer added:", fmt.Sprint(v)) + } + } + if _, ok := res["not_added"]; ok { + for _, v := range res["not_added"].([]interface{}) { + fmt.Println("Peer not added:", fmt.Sprint(v)) + } + } + if _, ok := res["removed"]; ok { + for _, v := range res["removed"].([]interface{}) { + fmt.Println("Peer removed:", fmt.Sprint(v)) + } + } + if _, ok := res["not_removed"]; ok { + for _, v := range res["not_removed"].([]interface{}) { + fmt.Println("Peer not removed:", fmt.Sprint(v)) + } + } default: if json, err := json.MarshalIndent(recv["response"], "", " "); err == nil { fmt.Println(string(json)) From cc4ee912794aa7da93ee046ec32135c5d3e5ad69 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 May 2018 14:29:27 +0100 Subject: [PATCH 4/7] Allow JSON output using -json argument --- yggdrasilctl.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/yggdrasilctl.go b/yggdrasilctl.go index 48ea19b..77c98fb 100644 --- a/yggdrasilctl.go +++ b/yggdrasilctl.go @@ -13,11 +13,12 @@ type admin_info map[string]interface{} func main() { server := flag.String("endpoint", "localhost:9001", "Admin socket endpoint") + injson := flag.Bool("json", false, "Output in JSON format") flag.Parse() args := flag.Args() if len(args) == 0 { - fmt.Println("usage:", os.Args[0], "[-endpoint=localhost:9001] command [key=value] [...]") + fmt.Println("usage:", os.Args[0], "[-endpoint=localhost:9001] [-json] command [key=value] [...]") fmt.Println("example:", os.Args[0], "getPeers") fmt.Println("example:", os.Args[0], "setTunTap name=auto mtu=1500 tap_mode=false") fmt.Println("example:", os.Args[0], "-endpoint=localhost:9001 getDHT") @@ -78,6 +79,13 @@ func main() { req := recv["request"].(map[string]interface{}) res := recv["response"].(map[string]interface{}) + if *injson { + if json, err := json.MarshalIndent(res, "", " "); err == nil { + fmt.Println(string(json)) + } + os.Exit(0) + } + switch req["request"] { case "dot": fmt.Println(res["dot"]) From 59688dcab3788671293a75ae84fc7c4ac9190aeb Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 May 2018 15:05:01 +0100 Subject: [PATCH 5/7] Fix formatting of bytes_sent, bytes_recvd, last_seen, uptime --- src/yggdrasil/admin.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 0403514..98aceed 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -434,12 +434,17 @@ func (a *admin) getData_getPeers() []admin_nodeInfo { for _, port := range ps { p := ports[port] addr := *address_addrForNodeID(getNodeID(&p.box)) + duration := time.Since(p.firstSeen) info := admin_nodeInfo{ {"ip", net.IP(addr[:]).String()}, {"port", port}, - {"uptime", fmt.Sprint(time.Since(p.firstSeen))}, - {"bytes_sent", atomic.LoadUint64(&p.bytesSent)}, - {"bytes_recvd", atomic.LoadUint64(&p.bytesRecvd)}, + {"uptime", fmt.Sprintf("%02d:%02d:%02d", + int(duration.Hours()), + int(duration.Minutes())%60, + int(duration.Seconds())%60, + )}, + {"bytes_sent", fmt.Sprintf("%d", atomic.LoadUint64(&p.bytesSent))}, + {"bytes_recvd", fmt.Sprintf("%d", atomic.LoadUint64(&p.bytesRecvd))}, } peerInfos = append(peerInfos, info) } @@ -481,7 +486,7 @@ func (a *admin) getData_getDHT() []admin_nodeInfo { {"coords", fmt.Sprint(v.coords)}, {"bucket", i}, {"peer_only", isPeer}, - {"last_seen", fmt.Sprint(now.Sub(v.recv))}, + {"last_seen", fmt.Sprintf("%ds", int(now.Sub(v.recv).Seconds()))}, } infos = append(infos, info) } @@ -504,8 +509,8 @@ func (a *admin) getData_getSessions() []admin_nodeInfo { {"coords", fmt.Sprint(sinfo.coords)}, {"mtu", sinfo.getMTU()}, {"was_mtu_fixed", sinfo.wasMTUFixed}, - {"bytes_sent", sinfo.bytesSent}, - {"bytes_recvd", sinfo.bytesRecvd}, + {"bytes_sent", fmt.Sprintf("%d", sinfo.bytesSent)}, + {"bytes_recvd", fmt.Sprintf("%d", sinfo.bytesRecvd)}, } infos = append(infos, info) } From 9567446f5034b831ebfa75bc4e3b3f4f3f037249 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 May 2018 15:21:23 +0100 Subject: [PATCH 6/7] Offload formatting to yggdrasilctl and keep precision in JSON --- src/yggdrasil/admin.go | 17 ++++++----------- yggdrasilctl.go | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/yggdrasil/admin.go b/src/yggdrasil/admin.go index 98aceed..b6bc7f3 100644 --- a/src/yggdrasil/admin.go +++ b/src/yggdrasil/admin.go @@ -434,17 +434,12 @@ func (a *admin) getData_getPeers() []admin_nodeInfo { for _, port := range ps { p := ports[port] addr := *address_addrForNodeID(getNodeID(&p.box)) - duration := time.Since(p.firstSeen) info := admin_nodeInfo{ {"ip", net.IP(addr[:]).String()}, {"port", port}, - {"uptime", fmt.Sprintf("%02d:%02d:%02d", - int(duration.Hours()), - int(duration.Minutes())%60, - int(duration.Seconds())%60, - )}, - {"bytes_sent", fmt.Sprintf("%d", atomic.LoadUint64(&p.bytesSent))}, - {"bytes_recvd", fmt.Sprintf("%d", atomic.LoadUint64(&p.bytesRecvd))}, + {"uptime", int(time.Since(p.firstSeen).Seconds())}, + {"bytes_sent", atomic.LoadUint64(&p.bytesSent)}, + {"bytes_recvd", atomic.LoadUint64(&p.bytesRecvd)}, } peerInfos = append(peerInfos, info) } @@ -486,7 +481,7 @@ func (a *admin) getData_getDHT() []admin_nodeInfo { {"coords", fmt.Sprint(v.coords)}, {"bucket", i}, {"peer_only", isPeer}, - {"last_seen", fmt.Sprintf("%ds", int(now.Sub(v.recv).Seconds()))}, + {"last_seen", int(now.Sub(v.recv).Seconds())}, } infos = append(infos, info) } @@ -509,8 +504,8 @@ func (a *admin) getData_getSessions() []admin_nodeInfo { {"coords", fmt.Sprint(sinfo.coords)}, {"mtu", sinfo.getMTU()}, {"was_mtu_fixed", sinfo.wasMTUFixed}, - {"bytes_sent", fmt.Sprintf("%d", sinfo.bytesSent)}, - {"bytes_recvd", fmt.Sprintf("%d", sinfo.bytesRecvd)}, + {"bytes_sent", sinfo.bytesSent}, + {"bytes_recvd", sinfo.bytesRecvd}, } infos = append(infos, info) } diff --git a/yggdrasilctl.go b/yggdrasilctl.go index 77c98fb..693fd1f 100644 --- a/yggdrasilctl.go +++ b/yggdrasilctl.go @@ -127,7 +127,20 @@ func main() { for slk, slv := range tlv.(map[string]interface{}) { fmt.Printf("%-"+fmt.Sprint(maxWidths["key"])+"s ", slk) for _, k := range keyOrder { - fmt.Printf("%-"+fmt.Sprint(maxWidths[k])+"s ", fmt.Sprint(slv.(map[string]interface{})[k])) + preformatted := slv.(map[string]interface{})[k] + var formatted string + switch k { + case "bytes_sent", "bytes_recvd": + formatted = fmt.Sprintf("%d", uint(preformatted.(float64))) + case "uptime", "last_seen": + seconds := uint(preformatted.(float64)) % 60 + minutes := uint(preformatted.(float64)/60) % 60 + hours := uint(preformatted.(float64) / 60 / 60) + formatted = fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds) + default: + formatted = fmt.Sprint(preformatted) + } + fmt.Printf("%-"+fmt.Sprint(maxWidths[k])+"s ", formatted) } fmt.Println() } From f6ea6fcc850272ad164c3887837e9c524c7022f9 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 21 May 2018 18:12:36 +0100 Subject: [PATCH 7/7] Add addAllowedBoxPub, removeAllowedBoxPub, getAllowedBoxPubs --- yggdrasilctl.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/yggdrasilctl.go b/yggdrasilctl.go index 693fd1f..2fbebf7 100644 --- a/yggdrasilctl.go +++ b/yggdrasilctl.go @@ -155,25 +155,36 @@ func main() { fmt.Println("TAP mode:", tap_mode) } } - case "addPeer", "removePeer": + case "addPeer", "removePeer", "addAllowedBoxPub", "removeAllowedBoxPub": if _, ok := res["added"]; ok { for _, v := range res["added"].([]interface{}) { - fmt.Println("Peer added:", fmt.Sprint(v)) + fmt.Println("Added:", fmt.Sprint(v)) } } if _, ok := res["not_added"]; ok { for _, v := range res["not_added"].([]interface{}) { - fmt.Println("Peer not added:", fmt.Sprint(v)) + fmt.Println("Not added:", fmt.Sprint(v)) } } if _, ok := res["removed"]; ok { for _, v := range res["removed"].([]interface{}) { - fmt.Println("Peer removed:", fmt.Sprint(v)) + fmt.Println("Removed:", fmt.Sprint(v)) } } if _, ok := res["not_removed"]; ok { for _, v := range res["not_removed"].([]interface{}) { - fmt.Println("Peer not removed:", fmt.Sprint(v)) + fmt.Println("Not removed:", fmt.Sprint(v)) + } + } + case "getAllowedBoxPubs": + if _, ok := res["allowed_box_pubs"]; !ok { + fmt.Println("All connections are allowed") + } else if res["allowed_box_pubs"] == nil { + fmt.Println("All connections are allowed") + } else { + fmt.Println("Connections are allowed only from the following public box keys:") + for _, v := range res["allowed_box_pubs"].([]interface{}) { + fmt.Println("-", v) } } default: