Bashスクリプトの実行とインポートの間の奇妙な不一致

Bashスクリプトの実行とインポートの間の奇妙な不一致

私はこれが非常に説明的なタイトルではないことを知っています(提案歓迎)。しかし、実際には私はこの問題で何時間も問題を抱えており、問題の原因がどこにあるのかわかりません。

ローカルネットワークのピア間のCLIチャット用の簡単なBashスクリプトを作成しました。

#!/usr/bin/env bash

# Usage: ./lanchat <local_ip>:<local_port> <remote_ip>:<remote_port>

# set -x
set -o errexit -o nounset -o pipefail

IFS=':' read -a socket <<< "$1"
LOCAL_IP=${socket[0]}
LOCAL_PORT=${socket[1]}

IFS=':' read -a socket <<< "$2"
REMOTE_IP=${socket[0]}
REMOTE_PORT=${socket[1]}

RECV_FIFO=".tmp.lanchat"

trap "rm '$RECV_FIFO'; kill 0" EXIT

mkfifo "$RECV_FIFO"

# EDIT: As per @Kamil Maciorowski's suggestion, removing the `-q 0` part below solves the issue.
while true; do nc -n -l -q 0 -s "$LOCAL_IP" -p "$LOCAL_PORT" > "$RECV_FIFO"; done &

TMUX_TOP="while true; do cat '$RECV_FIFO'; done"
TMUX_BOTTOM="while IFS= read -r line; do nc -n -q 0 '$REMOTE_IP' '$REMOTE_PORT' <<< \$line; done"

tmux new "$TMUX_TOP" \; split -v "$TMUX_BOTTOM"

IP 172.16.0.2 のマシンは Debian 11 を実行する VPS で、172.16.0.100 は Arch を実行するローカルコンピュータです。

両方のプロンプトで手動でコマンドを実行すると、目的の結果が得られ、ネットワーク通信に問題がなく、スクリプトロジックが正しいことを確認できます。

## VPS (Debian) side as follows; exchange IPs for local (Arch) side.
$ mkfifo .tmp.lanchat
$ while true; do nc -n -l -q 0 -s 172.16.0.2 -p 1234 > .tmp.lanchat; done &
$ tmux new "while true; do cat .tmp.lanchat; done" \; split -v "while IFS= read -r line; do nc -n -q 0 172.16.0.100 1234 <<< \$line; done"
## Test communication in both directions: all right; then CTRL-C twice to exit both tmux panels
$ kill %1; rm .tmp.lanchat

ただし、両方をスクリプトとして実行すると、ローカル側(Arch)のみがサーバー(Debian)からのメッセージを印刷します。サーバーは私のローカルコンピュータから何も印刷しません。 Traceを使用して実行すると、set -x両方の変数が正しい値に置き換えられ、手動で入力したコマンドと同じように見えます。

奇妙なことは、Arch側でスクリプトを実行し、Debian側のプロンプト(上記)に従うと、すべてがうまく機能することです。また、Arch側でスクリプトを実行しましたが、源泉Debian側でも非常にうまく機能します。

両方に詳細な出力を追加します。CNCCalling Archの側面にも刻印がありますConnection to 172.16.0.2 1234 port [tcp/*] succeeded!。ただし、tee log.txt通貨に追加CNCDebian側のリスニングモードでは何もキャプチャされません。

#...
while true; do
    nc -n -l -q 0 -s "$LOCAL_IP" -p "$LOCAL_PORT" | tee log.txt > "$RECV_FIFO";
done &
#...

私は可能なすべての順序で2つのピア間の接続を確立しようとしています。孤児やゾンビのインスタンスがないことを確認するために、サーバーとローカルコンピュータを再起動しました。CNC何とか検出を回避するソケットを収容してください。

現在、DebianとArchは異なるバージョンのCNC。したがって、表面的に、これは可能な説明のように聞こえます。しかし、Debian側でスクリプトがうまく動作するという事実は、この可能性を排除しますか?

ここで何が起こっているのでしょうか?

答え1

Debian 12(localhostからlocalhost、別々の作業ディレクトリ)でスクリプトをテストし、問題を確認しました。私ncのものnetcat-traditional 1.10-47つまり、出身ではないnetcat-openbsd)。

問題は-q 0聴覚であるnc。からman 1 nc

-q seconds
stdinでEOF後、指定された時間(秒)を待って終了します。秒が負の場合は永遠に待ってください。

ncリスナーは、終了する前に着信接続を待っているように見えますが、-q 0着信データを待つことはありません。-q 0ツールは通常途中で終了するため、接続設定とデータ転送は別々のイベントです。これは私のテストリスニングコンテストです。nc 時々着信データをパイプに中継します。

予期しない動作を引き起こすEOFは、ジョブ制御のないシェルが非同期コマンドを実行するとき(terminateするために&、これはリスニング実行ループを使用する方法nc)、標準入力を/dev/null同等のドキュメントにリダイレクトする必要があるため、すぐに発生します。

スクリプトを受け取ると、対話型シェルはそれを解釈します。おそらく、ジョブ制御が有効なbashになります(対話型bashのデフォルト動作)。そうであれば、別のプロセスグループでバックグラウンドループを実行しますが、標準入力はまだ端末に接続されています(通常はこれを使用してバックグラウンドでタスクを実行して入力fgできます)。バックグラウンドジョブの場合、SIGTTINの入力を端末から盗むことはできず、EOFは発生しません。これにより、スクリプトをインポートしたときにインポートせずにスクリプトを実行したときに発生する問題がリスナーncで発生しなくなります。-q 0

-q 1リスニングを指定するとnc実際に役立ちます(理論的にはまだアクティブですが)。使用するか-q -1(永久に待つ)、単に省略する方が良いと思います-q(私のテストの基本的な動作)〜らしい「永遠に待つ」状態になります。)

-q 0(tmux内で)接続は意味があるので、ペイロードを送信した直後に終了したいncと思います。nc

ncアーチの振る舞いは異なっており、おそらく異なっているかもしれませんし、当時のOSの全体的なストレスがゲームに影響を与えるからです。

教訓は次のとおりです。nc+nc -lペアが一方向にのみデータを送信する場合(1行に1つのペアを使用)、送信者には-q 0便利なオプションですが、受信者には不要であり、場合によっては有害かもしれません。


次のように改善すべき領域があります。

  • コード注入の脆弱性(./lanchat <local_ip>:<local_port> <remote_ip>:<remote_port>"'; rogue command'")があります。
  • nc一方の端やもう一方の端が聞こえない短い時間があります。
  • 一対のnc「全体」セッションを処理するのに十分です。

ここではこれらの問題については説明しませんが、代替スクリプトの概要を提供できます。

#!/usr/bin/env bash

target="$(tmux new -dP 'tail -f /dev/null')"
uptty="$(tmux display-message -p -F '#{pane_tty}' -t "$target")"
tmux split -t "$target" -v "
   rlwrap tee    >(sed -u 's/^/      < /' | ts %H:%M >${uptty@Q}) \
   | nc ${*@Q} > >(sed -u 's/^/> /'       | ts %H:%M >${uptty@Q})
"
tmux a -t "$target"

スクリプトにはbash(自己および内部的にtmux)が必要です。ncたとえば、指定したい引数を使用して実行できます。

  • 最初はリスナーです./lanchat -n -l -s 192.168.11.22 -p 2345
  • その後、接続エッジがあります./lanchat 192.168.11.22 2345

単一nc接続はnc双方向のすべての通信を処理します。このスクリプトは、タイムスタンプ(ts必要に応じて両方のインスタンスを削除できます)と行を編集するためのreadline(必要に応じて削除できます)に使用されます。移植性がありません。削除しない限り、バッファリングの問題は発生しません。| ts %H:%Mrlwraprlwrapsed -used-uts

bash 5.2.15、tmux 3.3aでテストされました。

関連情報