summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2018-04-17 13:55:43 -0400
committerLuke Shumaker <lukeshu@lukeshu.com>2018-04-17 13:55:43 -0400
commit9b29b1dc30ca57e627195b2238a4fd5b68455b55 (patch)
tree43bea29066bc9e526610451803d1b6062ff5558d
initial commit
-rw-r--r--influxdb.go17
-rw-r--r--main.go75
-rw-r--r--tinc.go125
3 files changed, 217 insertions, 0 deletions
diff --git a/influxdb.go b/influxdb.go
new file mode 100644
index 0000000..992bef3
--- /dev/null
+++ b/influxdb.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "time"
+
+ client "github.com/influxdata/influxdb/client/v2"
+)
+
+type Point = *client.Point
+
+func NewPoint(name string, tags map[string]string, fields map[string]interface{}) Point {
+ pt, err := client.NewPoint(name, tags, fields, time.Now())
+ if err != nil {
+ panic(err)
+ }
+ return pt
+}
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..a4f31c3
--- /dev/null
+++ b/main.go
@@ -0,0 +1,75 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+)
+
+var wg sync.WaitGroup
+
+func Emit(pt Point) {
+ if pt == nil {
+ return
+ }
+ fmt.Println(pt)
+}
+
+func DoHostfile(fname string) {
+ defer wg.Done()
+ hostname := filepath.Base(fname)
+ cfg, err := readConfigFile(fname)
+ if err != nil {
+ Emit(NewPoint("public", map[string]string{"host":hostname}, map[string]interface{}{"error": err.Error()}))
+ return
+ }
+ for _, address := range getAddresses(cfg) {
+ wg.Add(2)
+ go Emit(DoAddress(hostname, "tcp4", address))
+ go Emit(DoAddress(hostname, "tcp6", address))
+ }
+}
+
+func DoAddress(host, network, address string) Point {
+ defer wg.Done()
+ tags := map[string]string{
+ "host": host,
+ "network": network,
+ "address": address,
+ }
+
+ addr, err := net.ResolveTCPAddr(network, address)
+ if err != nil {
+ if ae, ok := err.(*net.AddrError); ok && ae.Err == "no suitable address found" {
+ return nil
+ }
+ return NewPoint("public", tags, map[string]interface{}{"error": err.Error()})
+ }
+ conn, err := net.DialTCP(network, nil, addr)
+ if err != nil {
+ return NewPoint("public", tags, map[string]interface{}{"error": err.Error()})
+ }
+ conn.CloseWrite()
+ all, _ := ioutil.ReadAll(conn)
+ line := strings.TrimRight(string(all), "\n")
+ parts := strings.Split(line, " ")
+ if len(parts) != 3 {
+ return NewPoint("public", tags, map[string]interface{}{"error": fmt.Sprintf("malformed ID line: %q", line)})
+ }
+ return NewPoint("public", tags, map[string]interface{}{
+ "name": parts[1],
+ "version": parts[2],
+ })
+}
+
+func main() {
+ for _, fname := range os.Args[1:] {
+ wg.Add(1)
+ go DoHostfile(fname)
+ }
+ wg.Wait()
+}
diff --git a/tinc.go b/tinc.go
new file mode 100644
index 0000000..76857cb
--- /dev/null
+++ b/tinc.go
@@ -0,0 +1,125 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "net"
+ "os"
+ "strings"
+)
+
+type Error struct {
+ File string
+ Line int
+ Err error
+}
+
+func (e Error) Error() string {
+ if e.Line > 0 {
+ return fmt.Sprintf("%v:%v: %v", e.File, e.Line, e.Err)
+ } else {
+ return fmt.Sprintf("%v: %v", e.File, e.Err)
+ }
+}
+
+func parseConfigLine(line string) (key, val string) {
+ line = strings.TrimRight(line, "\t ")
+ keylen := strings.IndexAny(line, "\t =")
+ if keylen < 0 {
+ keylen = len(line)
+ }
+
+ variable := line[:keylen]
+
+ value := strings.TrimLeft(line[keylen:], "\t ")
+ if strings.HasPrefix(value, "=") {
+ value = strings.TrimLeft(value[1:], "\t ")
+ }
+
+ return variable, value
+}
+
+func readConfigFile(fname string) (map[string][]string, error) {
+ config_tree := make(map[string][]string)
+
+ fp, err := os.Open(fname)
+ if err != nil {
+ return nil, Error{File: fname, Err: err}
+ }
+
+ buffer := bufio.NewScanner(fp)
+
+ lineno := 0
+ ignore := false
+ for buffer.Scan() {
+ line := buffer.Text()
+
+ lineno++
+
+ if len(line) == 0 || line[0] == '#' {
+ continue
+ }
+
+ if ignore {
+ if strings.HasPrefix(line, "-----END") {
+ ignore = false
+ }
+
+ continue
+ }
+
+ if strings.HasPrefix(line, "-----BEGIN") {
+ ignore = true
+ continue
+
+ }
+
+ variable, value := parseConfigLine(line)
+ if len(value) == 0 {
+ err = fmt.Errorf("No value for variable: %s", variable)
+ return nil, Error{File: fname, Line: lineno, Err: err}
+ }
+ variable = strings.ToLower(variable)
+ config_tree[variable] = append(config_tree[variable], value)
+ }
+
+ err = buffer.Err()
+ if err != nil {
+ return nil, Error{File: fname, Err: err}
+ }
+ return config_tree, nil
+}
+
+// Returns a list of public addresses for a host-config in Go
+// "net.Dial" format.
+func getAddresses(cfg map[string][]string) []string {
+ var result []string
+
+ for _, node := range cfg["address"] {
+ var port string
+ space := strings.IndexByte(node, ' ')
+ if space < 0 {
+ if ports, portsOk := cfg["port"]; portsOk {
+ port = ports[0]
+ } else {
+ port = "655"
+ }
+ } else {
+ port = node[space+1:]
+ node = node[:space]
+ }
+
+ if isIPv6(node) {
+ result = append(result, fmt.Sprintf("[%s]:%s", node, port))
+ } else {
+ result = append(result, fmt.Sprintf("%s:%s", node, port))
+ }
+ }
+
+ return result
+}
+
+func isIPv6(node string) bool {
+ ip := net.ParseIP(node)
+ return ip != nil && ip.To4() == nil
+}