author | Alberto Bertogli
<albertito@blitiri.com.ar> 2017-07-13 20:11:27 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2017-07-13 21:05:45 UTC |
parent | 10427d7f49d8ce074a702713541658d2d1dbe97e |
.gitignore | +2 | -0 |
Makefile | +7 | -4 |
cmd/mda-lmtp/.gitignore | +2 | -0 |
cmd/mda-lmtp/mda-lmtp.go | +125 | -0 |
cmd/mda-lmtp/test.sh | +21 | -0 |
cmd/mda-lmtp/test_tcp_success.cmy | +32 | -0 |
cmd/mda-lmtp/test_unix_failure.cmy | +31 | -0 |
cmd/mda-lmtp/test_unix_success.cmy | +33 | -0 |
diff --git a/.gitignore b/.gitignore index ee7de93..02ae02d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,9 +12,11 @@ /chasquid-util /smtp-check /spf-check +/mda-lmtp cmd/chasquid-util/chasquid-util cmd/smtp-check/smtp-check cmd/spf-check/spf-check +cmd/mda-lmtp/mda-lmtp # Exclude any .pem files, to prevent accidentally including test keys and # certificates. diff --git a/Makefile b/Makefile index 844a377..b1fd20e 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ endif default: chasquid -all: chasquid chasquid-util smtp-check spf-check +all: chasquid chasquid-util smtp-check spf-check mda-lmtp chasquid: @@ -30,16 +30,19 @@ smtp-check: spf-check: go build ${GOFLAGS} ./cmd/spf-check/ +mda-lmtp: + go build ${GOFLAGS} ./cmd/mda-lmtp/ test: go test ${GOFLAGS} ./... setsid -w ./test/run.sh setsid -w ./cmd/chasquid-util/test.sh + setsid -w ./cmd/mda-lmtp/test.sh -install-binaries: chasquid chasquid-util smtp-check +install-binaries: chasquid chasquid-util smtp-check mda-lmtp mkdir -p /usr/local/bin/ - cp -a chasquid chasquid-util smtp-check /usr/local/bin/ + cp -a chasquid chasquid-util smtp-check mda-lmtp /usr/local/bin/ install-config-skeleton: if ! [ -d /etc/chasquid ] ; then cp -arv etc / ; fi @@ -51,4 +54,4 @@ install-config-skeleton: fi -.PHONY: chasquid chasquid-util smtp-check spf-check test +.PHONY: chasquid chasquid-util smtp-check spf-check mda-lmtp test diff --git a/cmd/mda-lmtp/.gitignore b/cmd/mda-lmtp/.gitignore new file mode 100644 index 0000000..9e9a4ad --- /dev/null +++ b/cmd/mda-lmtp/.gitignore @@ -0,0 +1,2 @@ +mda-lmtp +*.log diff --git a/cmd/mda-lmtp/mda-lmtp.go b/cmd/mda-lmtp/mda-lmtp.go new file mode 100644 index 0000000..cee639a --- /dev/null +++ b/cmd/mda-lmtp/mda-lmtp.go @@ -0,0 +1,125 @@ +// mda-lmtp is a very basic MDA that uses LMTP to do the delivery. +// +// See the usage below for details. +package main + +import ( + "flag" + "fmt" + "io" + "net" + "net/textproto" + "os" + "strings" +) + +// Command-line flags +var ( + fromwhom = flag.String("f", "", "Whom the message is from") + recipient = flag.String("d", "", "Recipient") + + addrNetwork = flag.String("addr_network", "", + "Network of the LMTP address (e.g. unix or tcp)") + addr = flag.String("addr", "", "LMTP server address") +) + +func usage() { + fmt.Fprintf(os.Stderr, ` +mda-lmtp is a very basic MDA that uses LMTP to do the mail delivery. + +It takes command line arguments similar to maildrop or procmail, reads an +email via standard input, and sends it over the given LMTP server. +Supports connecting to LMTP servers over UNIX sockets and TCP. + +It can be used when your mail server does not support LMTP directly. + +Example of use: +$ mda-lmtp --addr localhost:1234 -f juan@casa -d jose < email + +Flags: +`) + flag.PrintDefaults() +} + +// Exit with EX_TEMPFAIL. +func tempExit(format string, args ...interface{}) { + fmt.Printf(format+"\n", args...) + // 75 = EX_TEMPFAIL "temporary failure" exit code (sysexits.h). + os.Exit(75) +} + +func main() { + flag.Usage = usage + flag.Parse() + + if *addr == "" { + fmt.Printf("No LMTP server address given (use --addr)\n") + os.Exit(2) + } + + // Try to autodetect the network if it's missing. + if *addrNetwork == "" { + *addrNetwork = "tcp" + if strings.HasPrefix(*addr, "/") { + *addrNetwork = "unix" + } + } + + conn, err := net.Dial(*addrNetwork, *addr) + if err != nil { + tempExit("Error connecting to (%s, %s): %v", + *addrNetwork, *addr, err) + } + + tc := textproto.NewConn(conn) + + // Expect the hello from the server. + _, _, err = tc.ReadResponse(220) + if err != nil { + tempExit("Server greeting error: %v", err) + } + + hostname, err := os.Hostname() + if err != nil { + tempExit("Could not get hostname: %v", err) + } + + cmd(tc, 250, "LHLO %s", hostname) + cmd(tc, 250, "MAIL FROM:<%s>", *fromwhom) + cmd(tc, 250, "RCPT TO:<%s>", *recipient) + cmd(tc, 354, "DATA") + + w := tc.DotWriter() + _, err = io.Copy(w, os.Stdin) + w.Close() + if err != nil { + tempExit("Error writing DATA: %v", err) + } + + // This differs from SMTP: here we get one reply per recipient, with the + // result of the delivery. Since we deliver to only one recipient, read + // one code. + _, _, err = tc.ReadResponse(250) + if err != nil { + tempExit("Delivery failed remotely: %v", err) + } + + cmd(tc, 221, "QUIT") + + tc.Close() +} + +// cmd sends a command and checks it matched the expected code. +func cmd(conn *textproto.Conn, expectCode int, format string, args ...interface{}) { + id, err := conn.Cmd(format, args...) + if err != nil { + tempExit("Sent %q, got %v", fmt.Sprintf(format, args...), err) + } + conn.StartResponse(id) + defer conn.EndResponse(id) + + _, _, err = conn.ReadResponse(expectCode) + if err != nil { + tempExit("Sent %q, got %v", fmt.Sprintf(format, args...), err) + } +} diff --git a/cmd/mda-lmtp/test.sh b/cmd/mda-lmtp/test.sh new file mode 100755 index 0000000..26f4364 --- /dev/null +++ b/cmd/mda-lmtp/test.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +set -e +. $(dirname ${0})/../../test/util/lib.sh + +init + +# Build the binary once, so we can use it and launch it in chamuyero scripts. +# Otherwise, we not only spend time rebuilding it over and over, but also "go +# run" masks the exit code, which is something we care about. +go build + +for i in *.cmy; do + if ! chamuyero $i > $i.log 2>&1 ; then + echo "# Test $i failed, log follows" + cat $i.log + exit 1 + fi +done + +success diff --git a/cmd/mda-lmtp/test_tcp_success.cmy b/cmd/mda-lmtp/test_tcp_success.cmy new file mode 100644 index 0000000..f3b3256 --- /dev/null +++ b/cmd/mda-lmtp/test_tcp_success.cmy @@ -0,0 +1,32 @@ + +nc tcp_listen localhost:14932 + +mda |= ./mda-lmtp --addr=localhost:14932 -f from -d to < .data + +nc -> 220 Hola desde expect + +nc <~ LHLO .* +nc -> 250-Bienvenido! +nc -> 250 Contame... + +nc <- MAIL FROM:<from> +nc -> 250 Aja + +nc <- RCPT TO:<to> +nc -> 250 Aja + +nc <- DATA +nc -> 354 Dale + +nc <- Subject: test +nc <- +nc <- This is a test. +nc <- . + +nc -> 250 Recibido + +nc <- QUIT +nc -> 221 Chauchas + +mda wait 0 + diff --git a/cmd/mda-lmtp/test_unix_failure.cmy b/cmd/mda-lmtp/test_unix_failure.cmy new file mode 100644 index 0000000..0bb8c26 --- /dev/null +++ b/cmd/mda-lmtp/test_unix_failure.cmy @@ -0,0 +1,31 @@ + +nc unix_listen .test-sock + +mda = ./mda-lmtp --addr=.test-sock --addr_network=unix \ + -f from -d to < .data + +nc -> 220 Hola desde expect + +nc <~ LHLO .* +nc -> 250-Bienvenido! +nc -> 250 Contame... + +nc <- MAIL FROM:<from> +nc -> 250 Aja + +nc <- RCPT TO:<to> +nc -> 250 Aja + +nc <- DATA +nc -> 354 Dale + +nc <- Subject: test +nc <- +nc <- This is a test. +nc <- . + +nc -> 452 Nananana + +mda <- Delivery failed remotely: 452 Nananana +mda wait 75 + diff --git a/cmd/mda-lmtp/test_unix_success.cmy b/cmd/mda-lmtp/test_unix_success.cmy new file mode 100644 index 0000000..37f3597 --- /dev/null +++ b/cmd/mda-lmtp/test_unix_success.cmy @@ -0,0 +1,33 @@ + +nc unix_listen .test-sock + +mda |= ./mda-lmtp --addr=.test-sock --addr_network=unix \ + -f from -d to < .data + +nc -> 220 Hola desde expect + +nc <~ LHLO .* +nc -> 250-Bienvenido! +nc -> 250 Contame... + +nc <- MAIL FROM:<from> +nc -> 250 Aja + +nc <- RCPT TO:<to> +nc -> 250 Aja + +nc <- DATA +nc -> 354 Dale + +nc <- Subject: test +nc <- +nc <- This is a test. +nc <- . + +nc -> 250 Recibido + +nc <- QUIT +nc -> 221 Chauchas + +mda wait 0 +