TeraTermProject / teraterm

Other
465 stars 38 forks source link

send マクロコマンドで想定外のデータを送信することがあるので修正する #215

Open zmatsuo opened 4 months ago

zmatsuo commented 4 months ago

send $ff $ff $ff "\xff\xff\xff" を送信したときおかしくなるという 指摘がありました。

原因はsend マクロコマンドが送信するデータが UTF-8文字列なのか、生のバイナリデータなのか判別できないことです。

"\xff\xff\xff" を UTF-8の文字列として解釈して UTF-16(内部文字コード) に変換すると L"???" (変換できない文字3キャラクタ) となり、 0x3F 0x3F 0x3F("???") が出力されます。

#189の修正で、 送信データ中にいわゆるコントロールコード(改行コード以外の0x20未満のデータ)が 入っていればバイナリデータとして取り扱うようにして対策しました。 0xff(UTF-8に変換できない文字)には未対応でした。

対策を2案考えました。

  1. 文字コード変換に失敗したらバイナリだと判断する

コントロールコード判定に加えて、 UTF-8文字列をUTF-16文字列に変換、再度UTF-8に変換して、 (又はUTF-8に変換時にエラーが発生するかチェックして) 等しい場合、文字として扱う(バイナリデータではないと判定する)。 等しくない場合、バイナリと仮定する。

2回変換して戻らない文字を含むことがあるのでは?という心配がのこるのと、 データと出力文字コードの設定によっては想定外のデータが出力されてしまうことが いまひとつです。

  1. バイナリの出力と文字列の出力を完全に分ける

ttpmacro.exeからttermpro.exeへ出力をリクエストする時、 ttermpro.exe側ではバイナリの出力か文字列の出力かわかりません。

189で判定を行うようにしましたが、

完璧には判定できません。 リクエストされたデータが文字列なのか、バイナリなのか判定できるよう ttpmacro.exe-ttermpro.exe間の通信を変更します。

send コマンドは、文字として送信、 sendbinary(仮) コマンドを新設、バイナリを送信とする などマクロコマンドの修正も行います。

根本対策です。でも大変そうな感じです。

ご意見いただけないでしょうか。

私の意見としては2で、 マクロコマンドをどうするかはちょっと後回しで まず最初はttpmacro.exe-ttermpro.exe間の通信上は判定できるようにすることを めざして調査修正するのが良いかな、です。

joeostrander commented 4 months ago

I like the sendbinary option myself.

nmaya commented 4 months ago

ちょっとわからないのでお伺いします。

原因はsend マクロコマンドが送信するデータが UTF-8文字列なのか、生のバイナリデータなのか判別できないことです。

このドキュメントによると、

マクロ  内部コードがUnicode(UTF-8)になりました。 文字列変数に文字列を代入したとき文字コードはUTF-8となります。

とあります。

"\xff\xff\xff" を UTF-8の文字列として解釈して UTF-16(内部文字コード) に変換する

こんな理解をしていますが、

「TTLファイル」 --(UTF8またはAPCを自動判定)--「ttpmacro.exe(内部コードがUnicode(UTF-8) = 読み込んだTTLファイル・変数のバッファはUTF-8)」--DDE(char OutBuf[512] でやりとり)--「ttermpro.exe」

内部文字コードがUTF-16になっていて変換しているのはttermpro.exeの中でしょうか? そうだとすると、文字列変数を今まで通りUTF-8の文字列変数は変えず、ttermpro.exe側でバイナリとして扱うようフラグを渡すイメージですか? このへん全然わかっていないので壊れないかが心配ですが、私も sendbinary コマンドを分けるイメージです。(ただ、recvbinary はあるの?という疑問がすぐに出てきそうです)

ttpmacro.exe-ttermpro.exe間の通信

とありますが、ここに書いた「NULLを含められない」と同様の箇所に対策が必要でしょうか?

zmatsuo commented 4 months ago

こんな理解をしていますが、

「TTLファイル」 --(UTF8またはAPCを自動判定)--「ttpmacro.exe(内部コードがUnicode(UTF-8) = 読み込んだTTLファイル・変数のバッファはUTF-8)」--DDE(char OutBuf[512] でやりとり)--「ttermpro.exe」

内部文字コードがUTF-16になっていて変換しているのはttermpro.exeの中でしょうか?

データの流れは私の理解と同じです。 変換はttermpro.exeの中で行っています。

現在の実装の文字列 (と、文字列型の変数)は、Cの文字列で、 "\0"(=0x00)でターミネートされたchar(char8_t)の配列のイメージです。

どうやら ttpmacro.exe <--> ttermpro.exe のDDE通信の経路には複数あるようです。 まだまだ調査中ですが、こんな風になっているようです。

  1. XTYP_POKE

ttpmacro.exe からの文字列。UTF-8の文字列かバイナリか? 現在問題になっている箇所。

  1. XTYP_EXECUTE

ttpmacro.exe からのコマンド?で、 ttermpro.exeが処理する

0 : [Command]
1 : コマンドごとのデータが続くと思われる
:    :

Command は teraterm/common/ttddecmnd.hで定義されています。

  1. XTYP_REQUEST

ttpmacro.exe からのリクエストがあったデータをttermpro.exeが返す

3-1. "PARAM" だったらParamFileName[] を送る 3-2. "DATA" だったらDDEのバッファからデータを送る

将来DDEがなくなるということはないと思いますが、 名前付きパイプやソケットに移行できるような通信仕様にしたほうが良いのでは? と思い中です。

他のプログラムから通信しやすいですし、 DDEはちょっとマイナーなのでソケットとかのほうが保守しやすそうですよね。 (もしかしたらDDEでなければならなかった理由が あるのかもしれないですがまだ読み解けていません)

Tera Term 内でどのようにDDEの通信をしているか ドキュメントはない(=仕様は公開されていない)ようなので、 Project外では使っていないとして変更してはどうかと思います。

文字とバイナリの送信を XTYP_EXECUTE を使って

[文字送信(1byte)] [長さ(htonl()した4byte)] [文字(UTF-8),0が含んでいてもok]
[バイナリ送信(1byte)] [長さ(htonl()した4byte)] [バイナリ,長さ分]

とするのはどうかなと思い中です(*)。 XTYP_POKE と XTYP_EXECUTE を使い分けているのは 片方は大きなデータが送れないとか、なにか根拠があるのかな?

DDEを使った通信を始めて扱うので、「アドバイズ?」という状態です。 このドキュメントを見たり していますがまだまだ調べ中です。

上(*)に書いたような通信方法に変更できれば、 今後文字列中にコントロールコードが入っていてもそのまま使えそうです。

recvbinary はあるの?という疑問がすぐに出てきそうです

配列に受信できるようにすればいいのかな。 実際の処理は、XTYP_REQUEST の "DATA" のところだと思うのですが…要調査です…。

nmaya commented 4 months ago

Tera Term 内でどのようにDDEの通信をしているか ドキュメントはない(=仕様は公開されていない)ようなので、

ここですかね?

zmatsuo commented 4 months ago

こんな感じかなという実装をしてみました。

手もとでのテストではバイナリ、テキストの送信はうまくいっています。 sendマクロはほとんどの場合で従来と同じように使えると思います。

recvbinary はあるの?

UTF-8で扱うようになっているはず… どうなっているのか、調べます。

zmatsuo commented 4 months ago

マクロ(ttpmacro)への送信内容はログと同様になります。

バイナリデータも扱えるかもしれませんが、 データの受信が recvln マクロコマンドしかないので、 行区切りなデータのやり取りをメインに考えている = 文字列を扱う 設計になっているんだと思います。

いまのところ、従来通りの使い方をしていて問題でないんじゃないかなと予想します。

全体の図にマクロ部分を追加しました。 https://teratermproject.github.io/manual/5/ja/reference/dev/unicode.html

joeostrander commented 4 months ago

Hi, is there a snapshot with the new sendbinary command?

zmatsuo commented 4 months ago

Please download from following URL and try it. https://ci.appveyor.com/project/teraterm/github-snapshot/builds/49952034/artifacts

joeostrander commented 4 months ago

It appears to be sending only ASCII. If I try to send FF or 255, 0 bytes are transferred. Am I doing something wrong? sendbinary $ff

zmatsuo commented 3 months ago

I tested with Serial and SSH.

Following steps were tested via SSH.

  1. login host
  2. perl show_key.pl
  3. Control/Macro, select send_ff.ttl.
# show_key.pl
use strict;
use warnings;

system("stty", "-icanon", "eol", "\001");

print "push key (Ctrl+C to exit):\n";

while (1) {
    my $input = getc(STDIN);
    last if ord($input) == 3;  # Ctrl+C

    my $hex_value = unpack("H*", $input);
    print "  0x$hex_value\n";
}

system("stty", "icanon", "eol", "^@", "echo");
;send_ff.ttl
sendbinary $ff $ff $ff
joeostrander commented 3 months ago

Sorry, it appears to work fine. My problem was that I was watching the Log dialog and expected to see Bytes transferred increase on the transmitting side. I re-did my test and saw the received bytes on the receiving side OK. I didn't realize that Bytes transferred only applied to bytes received, not sent.

Thank you

zmatsuo commented 3 months ago

😀 Merged into main branch.

Binary can be downloaded from following URL https://ci.appveyor.com/project/teraterm/github-main/builds/49967951/artifacts

Close this issue.

nmaya commented 1 month ago

全体の図にマクロ部分を追加しました。 https://teratermproject.github.io/manual/5/ja/reference/dev/unicode.html

もうちょっと詳しく書きたい