私はこれが非常に説明的なタイトルではないことを知っています(提案歓迎)。しかし、実際には私はこの問題で何時間も問題を抱えており、問題の原因がどこにあるのかわかりません。
ローカルネットワークのピア間の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:%M
rlwrap
rlwrap
sed -u
sed
-u
ts
bash 5.2.15、tmux 3.3aでテストされました。