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.
159 lines
4.3 KiB
159 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)
|
|
}
|
|
}
|
|
|