You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

160 lines
4.3 KiB

package alarmdelivery
import (
"bufio"
"context"
"fmt"
"net"
"strings"
"sync"
"testing"
"time"
"github.com/gosnmp/gosnmp"
)
type memorySecrets map[string][]byte
func (s memorySecrets) Get(key string) ([]byte, bool) {
value, ok := s[key]
return append([]byte(nil), value...), ok
}
func TestEmailTransportDeliversMessageToSMTPServer(t *testing.T) {
clientConnection, serverConnection := net.Pipe()
defer serverConnection.Close()
var (
mu sync.Mutex
message string
)
serverDone := make(chan error, 1)
go func() {
reader := bufio.NewReader(serverConnection)
writer := bufio.NewWriter(serverConnection)
writeLine := func(value string) error {
if _, err := fmt.Fprintf(writer, "%s\r\n", value); err != nil {
return err
}
return writer.Flush()
}
if err := writeLine("220 smtp.test ESMTP ready"); err != nil {
serverDone <- err
return
}
var data strings.Builder
inData := false
for {
line, err := reader.ReadString('\n')
if err != nil {
serverDone <- err
return
}
trimmed := strings.TrimRight(line, "\r\n")
if inData {
if trimmed == "." {
mu.Lock()
message = data.String()
mu.Unlock()
inData = false
if err := writeLine("250 queued"); err != nil {
serverDone <- err
return
}
continue
}
data.WriteString(line)
continue
}
command := strings.ToUpper(strings.Fields(trimmed)[0])
switch command {
case "EHLO", "HELO", "MAIL", "RCPT":
if err := writeLine("250 ok"); err != nil {
serverDone <- err
return
}
case "DATA":
inData = true
if err := writeLine("354 end with dot"); err != nil {
serverDone <- err
return
}
case "QUIT":
serverDone <- writeLine("221 bye")
return
default:
serverDone <- fmt.Errorf("unexpected SMTP command %q", trimmed)
return
}
}
}()
request := validRequest(ChannelEmail)
request.Message = "serial channel alarm"
request.Email = &EmailTarget{
Host: "smtp.test", Port: 25, From: "tera@test.local",
To: []string{"operator@test.local"}, CC: []string{"audit@test.local"},
Subject: "TERA alarm\r\nInjected: no",
}
transport := EmailTransport{DialContext: func(context.Context, string, string) (net.Conn, error) {
return clientConnection, nil
}}
if err := transport.Send(context.Background(), Attempt{Channel: ChannelEmail, Request: request}); err != nil {
t.Fatal(err)
}
if err := <-serverDone; err != nil {
t.Fatal(err)
}
mu.Lock()
payload := message
mu.Unlock()
if !strings.Contains(payload, "To: operator@test.local") || !strings.Contains(payload, "Cc: audit@test.local") || !strings.Contains(payload, "serial channel alarm") {
t.Fatalf("SMTP message = %q", payload)
}
if strings.Contains(payload, "\r\nInjected:") || !strings.Contains(payload, "Subject: TERA alarm Injected: no") {
t.Fatalf("SMTP subject was not sanitized: %q", payload)
}
}
func TestSNMPTrapTransportSendsV2CAlarmPDU(t *testing.T) {
listener, err := net.ListenPacket("udp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer listener.Close()
address := listener.LocalAddr().(*net.UDPAddr)
request := validRequest(ChannelSNMPTrap)
request.Message = "CH12 link down"
request.SNMPTraps = []SNMPTrapTarget{{
TargetID: "noc", Host: "127.0.0.1", Port: address.Port, Version: "v2c",
CredentialKey: "trap/noc/community",
}}
transport := SNMPTrapTransport{Secrets: memorySecrets{"trap/noc/community": []byte("private-test-community")}}
if err := transport.Send(context.Background(), Attempt{Channel: ChannelSNMPTrap, Request: request}); err != nil {
t.Fatal(err)
}
if err := listener.SetReadDeadline(time.Now().Add(time.Second)); err != nil {
t.Fatal(err)
}
buffer := make([]byte, 65535)
count, _, err := listener.ReadFrom(buffer)
if err != nil {
t.Fatal(err)
}
packet, err := (&gosnmp.GoSNMP{}).SnmpDecodePacket(buffer[:count])
if err != nil {
t.Fatal(err)
}
if packet.Version != gosnmp.Version2c || packet.Community != "private-test-community" || packet.PDUType != gosnmp.SNMPv2Trap {
t.Fatalf("SNMP packet = %+v", packet)
}
var foundMessage bool
for _, variable := range packet.Variables {
if variable.Name == ".1.3.6.1.4.1.55555.1.2" && string(variable.Value.([]byte)) == "CH12 link down" {
foundMessage = true
}
}
if !foundMessage {
t.Fatalf("alarm message PDU not found: %+v", packet.Variables)
}
}