Merge pull request #238 from neilalexander/afunix

Use AF_UNIX socket by default for admin API
This commit is contained in:
Arceliar 2018-12-10 17:34:36 -06:00 committed by GitHub
commit 6901e2fc9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 117 additions and 17 deletions

View File

@ -35,9 +35,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add `-json` command line flag for generating and normalising configuration in plain JSON instead of HJSON - Add `-json` command line flag for generating and normalising configuration in plain JSON instead of HJSON
- Build name and version numbers are now imprinted onto the build, accessible through `yggdrasil -version` and `yggdrasilctl getSelf` - Build name and version numbers are now imprinted onto the build, accessible through `yggdrasil -version` and `yggdrasilctl getSelf`
- Add ability to disable admin socket by setting `AdminListen` to `"none"` - Add ability to disable admin socket by setting `AdminListen` to `"none"`
- `yggdrasilctl` now tries to look for the default configuration file to find `AdminListen` if `-endpoint` is not specified
- `yggdrasilctl` now returns more useful logging in the event of a fatal error
### Changed ### Changed
- Switched to Chord DHT (instead of Kademlia, although still compatible at the protocol level) - Switched to Chord DHT (instead of Kademlia, although still compatible at the protocol level)
- The `AdminListen` option and `yggdrasilctl` now default to `unix:///var/run/yggdrasil.sock` on BSDs, macOS and Linux
- Cleaned up some of the parameter naming in the admin socket - Cleaned up some of the parameter naming in the admin socket
- Latency-based parent selection for the switch instead of uptime-based (should help to avoid high latency links somewhat) - Latency-based parent selection for the switch instead of uptime-based (should help to avoid high latency links somewhat)
- Real peering endpoints now shown in the admin socket `getPeers` call to help identify peerings - Real peering endpoints now shown in the admin socket `getPeers` call to help identify peerings

View File

@ -1,10 +1,13 @@
package main package main
import ( import (
"bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"io/ioutil"
"log"
"net" "net"
"net/url" "net/url"
"os" "os"
@ -12,16 +15,31 @@ import (
"strconv" "strconv"
"strings" "strings"
"golang.org/x/text/encoding/unicode"
"github.com/neilalexander/hjson-go"
"github.com/yggdrasil-network/yggdrasil-go/src/defaults" "github.com/yggdrasil-network/yggdrasil-go/src/defaults"
) )
type admin_info map[string]interface{} type admin_info map[string]interface{}
func main() { func main() {
logbuffer := &bytes.Buffer{}
logger := log.New(logbuffer, "", log.Flags())
defer func() {
if r := recover(); r != nil {
logger.Println("Fatal error:", r)
fmt.Print(logbuffer)
os.Exit(1)
}
}()
endpoint := defaults.GetDefaults().DefaultAdminListen
flag.Usage = func() { flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] command [key=value] [key=value] ...\n", os.Args[0]) fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] command [key=value] [key=value] ...\n", os.Args[0])
fmt.Println("Options:") fmt.Println("Options:")
flag.PrintDefaults() flag.PrintDefaults()
fmt.Println("Commands:\n - Use \"list\" for a list of available commands") fmt.Println("Commands:\n - Use \"list\" for a list of available commands")
fmt.Println("Examples:") fmt.Println("Examples:")
fmt.Println(" - ", os.Args[0], "list") fmt.Println(" - ", os.Args[0], "list")
@ -30,7 +48,7 @@ func main() {
fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getDHT") fmt.Println(" - ", os.Args[0], "-endpoint=tcp://localhost:9001 getDHT")
fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getDHT") fmt.Println(" - ", os.Args[0], "-endpoint=unix:///var/run/ygg.sock getDHT")
} }
server := flag.String("endpoint", defaults.GetDefaults().DefaultAdminListen, "Admin socket endpoint") server := flag.String("endpoint", endpoint, "Admin socket endpoint")
injson := flag.Bool("json", false, "Output in JSON format (as opposed to pretty-print)") injson := flag.Bool("json", false, "Output in JSON format (as opposed to pretty-print)")
verbose := flag.Bool("v", false, "Verbose output (includes public keys)") verbose := flag.Bool("v", false, "Verbose output (includes public keys)")
flag.Parse() flag.Parse()
@ -41,23 +59,59 @@ func main() {
return return
} }
if *server == endpoint {
if config, err := ioutil.ReadFile(defaults.GetDefaults().DefaultConfigFile); err == nil {
if bytes.Compare(config[0:2], []byte{0xFF, 0xFE}) == 0 ||
bytes.Compare(config[0:2], []byte{0xFE, 0xFF}) == 0 {
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
decoder := utf.NewDecoder()
config, err = decoder.Bytes(config)
if err != nil {
panic(err)
}
}
var dat map[string]interface{}
if err := hjson.Unmarshal(config, &dat); err != nil {
panic(err)
}
if ep, ok := dat["AdminListen"].(string); ok && (ep != "none" && ep != "") {
endpoint = ep
logger.Println("Found platform default config file", defaults.GetDefaults().DefaultConfigFile)
logger.Println("Using endpoint", endpoint, "from AdminListen")
} else {
logger.Println("Configuration file doesn't contain appropriate AdminListen option")
logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
}
} else {
logger.Println("Can't open config file from default location", defaults.GetDefaults().DefaultConfigFile)
logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
}
} else {
logger.Println("Using endpoint", endpoint, "from command line")
}
var conn net.Conn var conn net.Conn
u, err := url.Parse(*server) u, err := url.Parse(endpoint)
if err == nil { if err == nil {
switch strings.ToLower(u.Scheme) { switch strings.ToLower(u.Scheme) {
case "unix": case "unix":
conn, err = net.Dial("unix", (*server)[7:]) logger.Println("Connecting to UNIX socket", endpoint[7:])
conn, err = net.Dial("unix", endpoint[7:])
case "tcp": case "tcp":
logger.Println("Connecting to TCP socket", u.Host)
conn, err = net.Dial("tcp", u.Host) conn, err = net.Dial("tcp", u.Host)
default: default:
logger.Println("Unknown protocol or malformed address - check your endpoint")
err = errors.New("protocol not supported") err = errors.New("protocol not supported")
} }
} else { } else {
conn, err = net.Dial("tcp", *server) logger.Println("Connecting to TCP socket", u.Host)
conn, err = net.Dial("tcp", endpoint)
} }
if err != nil { if err != nil {
panic(err) panic(err)
} }
logger.Println("Connected")
defer conn.Close() defer conn.Close()
decoder := json.NewDecoder(conn) decoder := json.NewDecoder(conn)
@ -67,11 +121,13 @@ func main() {
for c, a := range args { for c, a := range args {
if c == 0 { if c == 0 {
logger.Printf("Sending request: %v\n", a)
send["request"] = a send["request"] = a
continue continue
} }
tokens := strings.Split(a, "=") tokens := strings.Split(a, "=")
if i, err := strconv.Atoi(tokens[1]); err == nil { if i, err := strconv.Atoi(tokens[1]); err == nil {
logger.Printf("Sending parameter %s: %d\n", tokens[0], i)
send[tokens[0]] = i send[tokens[0]] = i
} else { } else {
switch strings.ToLower(tokens[1]) { switch strings.ToLower(tokens[1]) {
@ -82,28 +138,31 @@ func main() {
default: default:
send[tokens[0]] = tokens[1] send[tokens[0]] = tokens[1]
} }
logger.Printf("Sending parameter %s: %v\n", tokens[0], send[tokens[0]])
} }
} }
if err := encoder.Encode(&send); err != nil { if err := encoder.Encode(&send); err != nil {
panic(err) panic(err)
} }
logger.Printf("Request sent")
if err := decoder.Decode(&recv); err == nil { if err := decoder.Decode(&recv); err == nil {
logger.Printf("Response received")
if recv["status"] == "error" { if recv["status"] == "error" {
if err, ok := recv["error"]; ok { if err, ok := recv["error"]; ok {
fmt.Println("Error:", err) fmt.Println("Admin socket returned an error:", err)
} else { } else {
fmt.Println("Unspecified error occured") fmt.Println("Admin socket returned an error but didn't specify any error text")
} }
os.Exit(1) os.Exit(1)
} }
if _, ok := recv["request"]; !ok { if _, ok := recv["request"]; !ok {
fmt.Println("Missing request in response (malformed response?)") fmt.Println("Missing request in response (malformed response?)")
return os.Exit(1)
} }
if _, ok := recv["response"]; !ok { if _, ok := recv["response"]; !ok {
fmt.Println("Missing response body (malformed response?)") fmt.Println("Missing response body (malformed response?)")
return os.Exit(1)
} }
req := recv["request"].(map[string]interface{}) req := recv["request"].(map[string]interface{})
res := recv["response"].(map[string]interface{}) res := recv["response"].(map[string]interface{})
@ -243,7 +302,7 @@ func main() {
queuesize := v.(map[string]interface{})["queue_size"].(float64) queuesize := v.(map[string]interface{})["queue_size"].(float64)
queuepackets := v.(map[string]interface{})["queue_packets"].(float64) queuepackets := v.(map[string]interface{})["queue_packets"].(float64)
queueid := v.(map[string]interface{})["queue_id"].(string) queueid := v.(map[string]interface{})["queue_id"].(string)
portqueues[queueport] += 1 portqueues[queueport]++
portqueuesize[queueport] += queuesize portqueuesize[queueport] += queuesize
portqueuepackets[queueport] += queuepackets portqueuepackets[queueport] += queuepackets
queuesizepercent := (100 / maximumqueuesize) * queuesize queuesizepercent := (100 / maximumqueuesize) * queuesize
@ -331,9 +390,11 @@ func main() {
fmt.Println(string(json)) fmt.Println(string(json))
} }
} }
} else {
logger.Println("Error receiving response:", err)
} }
if v, ok := recv["status"]; ok && v == "error" { if v, ok := recv["status"]; ok && v != "success" {
os.Exit(1) os.Exit(1)
} }
os.Exit(0) os.Exit(0)

View File

@ -7,6 +7,9 @@ type platformDefaultParameters struct {
// Admin socket // Admin socket
DefaultAdminListen string DefaultAdminListen string
// Configuration (used for yggdrasilctl)
DefaultConfigFile string
// TUN/TAP // TUN/TAP
MaximumIfMTU int MaximumIfMTU int
DefaultIfMTU int DefaultIfMTU int

View File

@ -7,7 +7,10 @@ package defaults
func GetDefaults() platformDefaultParameters { func GetDefaults() platformDefaultParameters {
return platformDefaultParameters{ return platformDefaultParameters{
// Admin // Admin
DefaultAdminListen: "tcp://localhost:9001", DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf",
// TUN/TAP // TUN/TAP
MaximumIfMTU: 65535, MaximumIfMTU: 65535,

View File

@ -7,7 +7,10 @@ package defaults
func GetDefaults() platformDefaultParameters { func GetDefaults() platformDefaultParameters {
return platformDefaultParameters{ return platformDefaultParameters{
// Admin // Admin
DefaultAdminListen: "tcp://localhost:9001", DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf",
// TUN/TAP // TUN/TAP
MaximumIfMTU: 32767, MaximumIfMTU: 32767,

View File

@ -7,7 +7,10 @@ package defaults
func GetDefaults() platformDefaultParameters { func GetDefaults() platformDefaultParameters {
return platformDefaultParameters{ return platformDefaultParameters{
// Admin // Admin
DefaultAdminListen: "tcp://localhost:9001", DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf",
// TUN/TAP // TUN/TAP
MaximumIfMTU: 65535, MaximumIfMTU: 65535,

View File

@ -7,7 +7,10 @@ package defaults
func GetDefaults() platformDefaultParameters { func GetDefaults() platformDefaultParameters {
return platformDefaultParameters{ return platformDefaultParameters{
// Admin // Admin
DefaultAdminListen: "tcp://localhost:9001", DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf",
// TUN/TAP // TUN/TAP
MaximumIfMTU: 9000, MaximumIfMTU: 9000,

View File

@ -7,7 +7,10 @@ package defaults
func GetDefaults() platformDefaultParameters { func GetDefaults() platformDefaultParameters {
return platformDefaultParameters{ return platformDefaultParameters{
// Admin // Admin
DefaultAdminListen: "tcp://localhost:9001", DefaultAdminListen: "unix:///var/run/yggdrasil.sock",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf",
// TUN/TAP // TUN/TAP
MaximumIfMTU: 16384, MaximumIfMTU: 16384,

View File

@ -9,6 +9,9 @@ func GetDefaults() platformDefaultParameters {
// Admin // Admin
DefaultAdminListen: "tcp://localhost:9001", DefaultAdminListen: "tcp://localhost:9001",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "/etc/yggdrasil.conf",
// TUN/TAP // TUN/TAP
MaximumIfMTU: 65535, MaximumIfMTU: 65535,
DefaultIfMTU: 65535, DefaultIfMTU: 65535,

View File

@ -9,6 +9,9 @@ func GetDefaults() platformDefaultParameters {
// Admin // Admin
DefaultAdminListen: "tcp://localhost:9001", DefaultAdminListen: "tcp://localhost:9001",
// Configuration (used for yggdrasilctl)
DefaultConfigFile: "C:\\Program Files\\Yggdrasil\\yggdrasil.conf",
// TUN/TAP // TUN/TAP
MaximumIfMTU: 65535, MaximumIfMTU: 65535,
DefaultIfMTU: 65535, DefaultIfMTU: 65535,

View File

@ -343,7 +343,19 @@ func (a *admin) listen() {
if err == nil { if err == nil {
switch strings.ToLower(u.Scheme) { switch strings.ToLower(u.Scheme) {
case "unix": case "unix":
if _, err := os.Stat(a.listenaddr[7:]); err == nil {
a.core.log.Println("WARNING:", a.listenaddr[7:], "already exists and may be in use by another process")
}
a.listener, err = net.Listen("unix", a.listenaddr[7:]) a.listener, err = net.Listen("unix", a.listenaddr[7:])
if err == nil {
switch a.listenaddr[7:8] {
case "@": // maybe abstract namespace
default:
if err := os.Chmod(a.listenaddr[7:], 0660); err != nil {
a.core.log.Println("WARNING:", a.listenaddr[:7], "may have unsafe permissions!")
}
}
}
case "tcp": case "tcp":
a.listener, err = net.Listen("tcp", u.Host) a.listener, err = net.Listen("tcp", u.Host)
default: default: