我可以延迟执行或以其他方式推迟管道内的传出 netcat 连接吗?

我可以延迟执行或以其他方式推迟管道内的传出 netcat 连接吗?

我不确定这是否是一个XY问题,但我必须支持一个可怕的软件,它无法正确地与现代邮件服务器通信。它只能执行 SMTP 并坚持提供,AUTH ...即使中继服务器不提供或不需要它,根据用户名设置发件人地址,尽管在协议中是单独的字段,如果不进入,它将简单地断开250-AUTH PLAIN ...连接服务器对 的响应EHLO。网上有很多关于该软件的未解决的投诉,所以我知道供应商不会提供任何帮助。我尝试将 sasl auth 添加到 SMTP 服务器(Postfix),但没有成功。我无法让它验证凭据,我担心它们会被传递给relayhost拒绝它们的人。我也找不到让 Postfix 忽略发送的凭据的方法。

为了尝试解决所有这些问题,我编写了一个基本脚本,用于侦听自己的端口,并代理 SMTP 通信,并在此过程中验证一些任意凭据:

#!/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

我运行./smtp_proxy 9025并且它工作正常,除了第二个nc立即连接到真正的 SMTP 服务器并开始一个将超时的事务:

$ ./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

我希望我可以以某种方式延迟第二次nc调用,直到它从管道接收数据,也许可以通过将其包装在函数或其他东西中。socat似乎在未修改的连接上执行此操作:

socat -v tcp4-l:9025,crlf tcp4:127.0.0.1:25,crlf

...但是一旦我尝试通过将其放入管道中来修改流,它就会执行与nc立即连接到 SMTP 服务器相同的操作。 (将nc上面的 s 分别替换为socat TCP4-LISTEN:$1,crlf -socat - TCP4:127.0.0.1:25,crlf。)

答案1

根据 @muru 链接到的一些答案,加上一个关于克服read输入不以换行符结尾时返回 false 的更一般的答案,我想出了一个实际上会延迟启动程序直到收到输入的函数:

function until_input {
    >&2 echo waiting for input # debug only, remove this line
    local line
    local nl=0
    local format='%s'
    while IFS= read -r line; do
        nl=1
        format='%s\n'
        break
    done
    >&2 echo done waiting # debug only, remove this line
    if (( nl )) || [[ "$line" ]]; then
        >&2 echo was input # debug only, remove this line
        { printf "$format" "$line"; cat; } | "$@"
    fi
}

为了证明它的工作原理,请参阅以下测试,该测试显示了初始语句组和until_input按预期并行启动,但延迟任务(此处cat)直到输入启动后才会启动。

{ echo wait >&2; sleep 2; echo go >&2; echo -n pipe$'\n'it baby; } | until_input cat

尽管这似乎是等待输入的一般问题的一个不错的解决方案,但不幸的是,它并没有解决我的问题。我放入until_input nc -4C 127.0.0.1 25管道,然后发现我需要的是,netcat在发送任何行之前,一旦有人连接到第一个管道,第二个管道就启动,因为服务器首先在 SMTP 中响应。我想我只需要在socat.

相关内容