Skip to content

Instantly share code, notes, and snippets.

@hjhee
Created January 29, 2017 09:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hjhee/e2e49b19375eafb251c949dc3ac24da4 to your computer and use it in GitHub Desktop.
Save hjhee/e2e49b19375eafb251c949dc3ac24da4 to your computer and use it in GitHub Desktop.
a smtp listener for zoneminder external record trigger
// IP Camera supports motion detection, which can be configured to send
// emails upon alarm is fired
// This go programme serve as a minimal smtp server, listening login requests
// from IP Camera, and triggers zmtrigger.pl through unix socket.
package main
import (
"github.com/op/go-logging"
"net"
"github.com/siebenmann/smtpd"
"bytes"
"os"
)
var log = logging.MustGetLogger("ipc smtpd")
var format = logging.MustStringFormatter(
`%{color}%{time:15:04:05.000} %{shortfunc} | ${level:.4s} %{id:03x}%{color:reset} %{message}`,
)
var cfg = smtpd.Config{
Auth: &smtpd.AuthConfig{
Mechanisms: []string{"LOGIN",},
},
SayTime: true,
LocalName: "heimserver.heim.lan",
SftName: "ipcSmtpd",
}
func handleConnection(conn net.Conn) {
log.Infof("new connection from %v", conn.RemoteAddr())
defer conn.Close()
c := smtpd.NewConn(conn, cfg, nil)
for {
evt := c.Next()
switch evt.What {
case smtpd.COMMAND:
switch evt.Cmd {
case smtpd.AUTH:
wantInput := [][]byte{
{},
[]byte("account@heim.lan"),
[]byte("thisispassword"),
}
challenges := [][]byte{
[]byte("Username:"),
[]byte("Password:"),
nil,
}
i := 0
success := c.Authenticate(func(c *smtpd.Conn, input []byte) {
if i >= len(wantInput) {
log.Warningf("AuthFunc called %d times, expected %d calls", i+1, len(wantInput))
}
if !bytes.Equal(input, wantInput[i]) {
log.Errorf("invalid input: got %q, expected %q", input, wantInput[i])
}
if i == len(wantInput)-1 {
c.Accept()
} else {
c.AuthChallenge(challenges[i])
}
i++
})
if !success {
log.Errorf("Authentication failed from %v", conn.RemoteAddr())
c.Reject()
return
} else {
log.Infof("Authentication success, triggering zoneminder...")
sockWrite("/var/run/zm/zmtrigger.sock", os.Args[1]) // "4|on+2|5|ExtTrigger|Motion Detection trigger from IP Camera"
return
}
default:
c.Accept()
}
case smtpd.DONE:
return
case smtpd.ABORT:
return
}
}
}
func sockWrite(addr, data string) {
typ := "unix"
conn, err := net.DialUnix(typ, nil, &net.UnixAddr{addr, typ})
if err != nil {
log.Errorf("open socket failed: %e", err)
return
}
defer conn.Close()
_, err = conn.Write([]byte(data))
if err != nil {
log.Errorf("write data failed: %e", err)
return
}
}
func main() {
// log.Debugf("args: %v", os.Args)
log.Infof("Starting IPC smtp daemon...")
ln, err := net.Listen("tcp", ":25")
if err != nil {
log.Fatal(err)
}
for {
conn, err := ln.Accept()
if err != nil {
log.Error(err)
}
go handleConnection(conn)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment