I'm not sure if this is an XY problem, but I have to aupport a horrible piece of software that won't talk to modern mail servers properly. It can only do SMTP and insists on providing AUTH ...
even when the relay server does not offer or require it, sets the from address based on the username despite being separate fields in the protocol, and it will simply disconnect if it doesn't get 250-AUTH PLAIN ...
in the server's response to EHLO
. There are many unresolved complaints about this software online so I know the vendor will be of no help. I tried adding sasl auth to the SMTP server (Postfix) but did not have any success. I could not get it to verify credentials, and I fear that they will be passed on to the relayhost
which will reject them. I couldn't find a way to get Postfix to ignore sent credentials either.
To try to get around all this, I wrote a basic script that listens on its own port, and proxies the SMTP communication, validating some arbitrary credentials during the process:
#!/bin/bash
user=<<REDACTED from>>
pw=<<REDACTED password>>
function reset {
send_auth=1
authing=0
ok=1
p=/tmp/smtp_backpipe
if [[ -p $p ]]; then
# drain pipe
dd if=$p iflag=nonblock of=/dev/null 2>/dev/null
else
mkfifo $p
fi
}
function pw_check {
if [[ "${1%%[[:space:]]}" == "${auth%%[[:space:]]}" ]]; then
echo 235 2.7.0 Authentication successful > $p
else
echo 535 5.7.0 Authentication failed > $p
ok=0
fi
}
function changeo {
while (( ok )) && IFS= read -r line; do
case $1 in
in)
if (( authing )); then
>&2 printf '%s\n' "$line"
pw_check "$line"
authing=0
elif [[ "$line" =~ ^AUTH[[:space:]]+PLAIN([[:space:]]+([^[:space:]]+))?[[:space:]]*$ ]]; then
if [[ "${BASH_REMATCH[1]}" ]]; then
>&2 printf '%s\n' "$line"
pw_check "${BASH_REMATCH[2]}"
else
authing=1
echo 334 | tee -a /dev/stderr > $p
fi
else
printf '%s\n' "$line"
fi
;;
out)
if [[ ! "$line" =~ ^250[^[:alpha:]]+AUTH[[:space:]] ]]; then
printf '%s\n' "$line"
if (( send_auth )) && [[ "$line" =~ ^250[^[:alpha:]] ]]; then
send_auth=0
echo 250-AUTH PLAIN
fi
fi
;;
esac
if ! (( ok )); then
exit 1
fi
done
}
auth="printf '\0%s\0%s' "$user" "$pw" | base64
"
i=0
while true; do
>&2 echo "$(( ++i ))"
reset
cat $p | tee -a /dev/stderr | netcat -4Clp $1 | changeo in | tee -a /dev/stderr | nc -4C 127.0.0.1 25 | changeo out > $p
done
I run ./smtp_proxy 9025
and it works except the second nc
immediately connects to the real SMTP server and begins a transaction which will time out:
$ ./smtp_proxy 9025
1
220 <<REDACTED host>> ESMTP Postfix
EHLO [10.0.0.99]
250-<<REDACTED host>>
250-AUTH PLAIN
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-SMTPUTF8
250 CHUNKING
AUTH PLAIN <<REDACTED auth>>
235 2.7.0 Authentication successful
MAIL FROM:<<<REDACTED from>>>
250 2.1.0 Ok
RCPT TO:<<<REDACTED to>>>
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
From: <<REDACTED from>>
Subject: hi
To: <<REDACTED to>>
Content-Type: text/plain; charset=windows-1252; format=flowed
Content-Transfer-Encoding: 7bit
there
.
250 2.0.0 Ok: queued as F23DFE0A1B
QUIT
221 2.0.0 Bye
2
220 <<REDACTED host>> ESMTP Postfix
421 4.4.2 <<REDACTED host>> Error: timeout exceeded
I was hoping I could somehow delay the second nc
invocation until it receives data from the pipeline, maybe by wrapping it in a function or something. socat
seemed to do this on an unmodified connection:
socat -v tcp4-l:9025,crlf tcp4:127.0.0.1:25,crlf
...but as soon as I try to modify the streams by putting it in a pipeline, it does the same thing as nc
, immediate connection to the SMTP server. (Replace the nc
s above with socat TCP4-LISTEN:$1,crlf -
and socat - TCP4:127.0.0.1:25,crlf
respectively.)
nc
(e.g.,... | { until read -t 0; do sleep 1; done; nc ...; } | ...
, thoughread -t 0
is unreliable, so use one of the other options) – muru Mar 05 '21 at 08:39