Playing around with collecting syslog over TCP
mostly with rsyslog on the other side, reading through Rainer's RFCs
This commit is contained in:
parent
92f7543872
commit
fb3009574f
19
sltcpsrv/LICENSE
Normal file
19
sltcpsrv/LICENSE
Normal file
@ -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.
|
18
sltcpsrv/Makefile
Normal file
18
sltcpsrv/Makefile
Normal file
@ -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
|
8
sltcpsrv/README
Normal file
8
sltcpsrv/README
Normal file
@ -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
|
2
sltcpsrv/doc.go
Normal file
2
sltcpsrv/doc.go
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// Syslog TCP Server
|
||||||
|
package sltcpsrv
|
140
sltcpsrv/message.go
Normal file
140
sltcpsrv/message.go
Normal file
@ -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
|
||||||
|
}
|
61
sltcpsrv/server.go
Normal file
61
sltcpsrv/server.go
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
sltcpsrv/syslog-tcp-server/main.go
Normal file
16
sltcpsrv/syslog-tcp-server/main.go
Normal file
@ -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)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user