diff --git a/sltcpsrv/LICENSE b/sltcpsrv/LICENSE new file mode 100644 index 0000000..cea092f --- /dev/null +++ b/sltcpsrv/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2013 Dan Buch + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/sltcpsrv/Makefile b/sltcpsrv/Makefile new file mode 100644 index 0000000..9a8d0a7 --- /dev/null +++ b/sltcpsrv/Makefile @@ -0,0 +1,18 @@ +LIBS := \ + github.com/meatballhat/box-o-sand/sltcpsrv +TARGETS := \ + $(LIBS) \ + github.com/meatballhat/box-o-sand/sltcpsrv/syslog-tcp-server + +all: build test + +build: deps + go install -x $(TARGETS) + +test: + go test -x -v $(LIBS) + +deps: + go get -x $(TARGETS) + +.PHONY: all build deps test diff --git a/sltcpsrv/README b/sltcpsrv/README new file mode 100644 index 0000000..ae3e732 --- /dev/null +++ b/sltcpsrv/README @@ -0,0 +1,8 @@ +Syslog TCP Server +----------------- + +This is a TCP server that (hopefully) knows how to consume Syslog messages. + +Notable bits: + - https://tools.ietf.org/rfc/rfc5424.txt + - https://tools.ietf.org/rfc/rfc6587.txt diff --git a/sltcpsrv/doc.go b/sltcpsrv/doc.go new file mode 100644 index 0000000..571cd19 --- /dev/null +++ b/sltcpsrv/doc.go @@ -0,0 +1,2 @@ +// Syslog TCP Server +package sltcpsrv diff --git a/sltcpsrv/message.go b/sltcpsrv/message.go new file mode 100644 index 0000000..9801b9c --- /dev/null +++ b/sltcpsrv/message.go @@ -0,0 +1,140 @@ +package sltcpsrv + +import ( + "fmt" + "log" + "strconv" + "strings" +) + +type message struct { + pri uint8 + version int + timestamp string + hostname string + appName string + pid int + body string +} + +func newMessage(line []byte) *message { + if len(line) == 0 { + return nil + } + + msg := &message{ + version: -1, + pid: -1, + } + + in := "" + accum := []byte{} + everythingElse := "" + + n := len(line) + for i, b := range line { + if *isDebug { + log.Printf("in = %q, accum = %q\n", in, string(accum)) + } + + if i == 0 && b == '<' { + in = "pri" + continue + } + + if in == "pri" { + if b == '>' { + in = "pridone" + + pri, err := strconv.ParseUint(string(accum), 10, 8) + if err != nil { + if *isDebug { + log.Println("ERROR:", err) + } + return msg + } + + msg.pri = uint8(pri) + accum = []byte{} + continue + } else { + accum = append(accum, b) + continue + } + } + + if in == "pridone" { + in = "everything_else" + accum = append(accum, b) + continue + } + + if in == "everything_else" { + if i == n-1 { + accum = append(accum, b) + everythingElse = string(accum) + break + } + + accum = append(accum, b) + continue + } + } + + dateParts := []string{} + + broken := strings.SplitN(everythingElse, ":", 4) + if len(broken) != 4 { + if *isDebug { + log.Printf("ERROR: got back unexpected parts for %q: %+v\n", everythingElse, broken) + } + return msg + } + + verMonthDateHour := strings.Split(broken[0], " ") + minutes := broken[1] + secsHostProc := strings.Split(broken[2], " ") + msg.body = string(broken[3]) + + hour := "" + + switch len(verMonthDateHour) { + case 4: + v, err := strconv.ParseUint(verMonthDateHour[0], 10, 8) + if err != nil { + if *isDebug { + log.Println("ERROR:", err) + } + } else { + msg.version = int(v) + } + + dateParts = append(dateParts, verMonthDateHour[1:3]...) + hour = verMonthDateHour[3] + case 3: + dateParts = append(dateParts, verMonthDateHour[:2]...) + hour = verMonthDateHour[2] + } + + dateParts = append(dateParts, fmt.Sprintf("%s:%s:%s", hour, minutes, secsHostProc[0])) + msg.timestamp = strings.Join(dateParts, " ") + msg.hostname = string(secsHostProc[1]) + + procParts := strings.Split(secsHostProc[2], "[") + if len(procParts) > 1 { + msg.appName = string(procParts[0]) + + p, err := strconv.ParseUint(strings.Trim(procParts[1], "[]"), 10, 16) + if err != nil { + if *isDebug { + log.Println("ERROR:", err) + } + } else { + msg.pid = int(p) + } + } else { + msg.appName = string(procParts[0]) + } + + return msg +} diff --git a/sltcpsrv/server.go b/sltcpsrv/server.go new file mode 100644 index 0000000..28b51ac --- /dev/null +++ b/sltcpsrv/server.go @@ -0,0 +1,61 @@ +package sltcpsrv + +import ( + "bytes" + "flag" + "fmt" + "io/ioutil" + "log" + "net" +) + +var ( + isDebug = flag.Bool("d", false, "Turn on debugging") +) + +func ListenAndServe(port int) { + ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) + if err != nil { + log.Fatal(err) + } + + log.Printf("Listening on %v\n", ln.Addr()) + + for { + conn, err := ln.Accept() + if err != nil { + log.Printf("WARN: Failed to accept connection!: %+v\n", err) + continue + } + + go handleConnection(conn) + } +} + +func handleConnection(conn net.Conn) { + defer conn.Close() + + inbytes, err := ioutil.ReadAll(conn) + if err != nil { + log.Printf("Failed to read request body: %+v\n", err) + return + } + + records := bytes.Split(inbytes, []byte("\n")) + nonempty := [][]byte{} + + for _, rec := range records { + if len(rec) != 0 { + nonempty = append(nonempty, rec) + } + } + + n := len(nonempty) + + for i, rec := range nonempty { + msg := newMessage(rec) + if msg != nil { + fmt.Printf("%v [%d of %d]: %+v\n", conn.RemoteAddr(), i+1, n, msg) + } + } +} diff --git a/sltcpsrv/syslog-tcp-server/main.go b/sltcpsrv/syslog-tcp-server/main.go new file mode 100644 index 0000000..892009b --- /dev/null +++ b/sltcpsrv/syslog-tcp-server/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "flag" + + . "github.com/meatballhat/box-o-sand/sltcpsrv" +) + +var ( + port = flag.Int("p", 10514, "Port number on which to listen") +) + +func main() { + flag.Parse() + ListenAndServe(*port) +}