状況
最近のWindowsではコンパネ経由でOpenSSH(クライアント・サーバともに)をインストール1することが出来ます。
さっそく使ってみたんですがいい感じに辛いログが出る。
PS > ssh -V
OpenSSH_for_Windows_8.1p1, LibreSSL 3.0.2
PS > ssh hoge
ssh: Could not resolve hostname hoge: \202\273\202\314\202\346\202\244\202\310\203z\203X\203g\202\315\225s\226\276\202\305\202\267\201B
PS >
WindowsだしShift_JIS吐いていると踏んでdecodeしてみた。
Python 3.11.0 (main, Oct 24 2022, 18:26:48) [MSC v.1933 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> msg = b"\202\273\202\314\202\346\202\244\202\310\203z\203X\203g\202\315\225s\226\276\202\305\202\267\201B"
>>> print(msg.decode('shift_jis'))
そのようなホストは不明です。
はい。
原因
どこでエラー吐いてるのか、GitHubでエラーメッセージをキーにして検索するとシュッと見つかる。
do_log2(loglevel, "%s: Could not resolve hostname %.100s: %s",
__progname, name, ssh_gai_strerror(gaierr));
で、このssh_gai_strerror
が何をしているかですが実装を見ると基本的にgai_strerror
を呼んでるだけ。WinSock版のgai_strerror
2がMBCS返すと踏んだので試してみる。
#include <stdio.h>
#include <ws2tcpip.h>
int main()
{
printf("[%s]\n", gai_strerrorA(EAI_NONAME));
}
実行結果は以下。
[そのようなホストは不明です。 ]
MBCS文字列の出自はわかった。
でもこれだと\202\273\202...
って出てくる理由にはならないのでdo_log
の呼び出しを追いかけていくと、strnvis()を呼んでるところが見つかる。これの実装はvis.cにあって、こいつがMBCS文字列を\0xx
へ書き換えている。
どうなればいいか
WinSockのgai_strerror
は内部でFormatMessage
を呼んでいると想定できて、FormatMessage
が英語メッセージを返してくれれば良い。
FormatMessageはdwLanguageId=0
で呼ばれるとスレッドロケールを参照するので、SetThreadUILanguage を使って試してみる。
#include <stdio.h>
#include <ws2tcpip.h>
int main()
{
SetThreadUILanguage(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
printf("[%s]\n", gai_strerrorA(EAI_NONAME));
}
[No such host is known. ]
なのでスレッドロケールをen-US
とかにすれば良くて、現状のソースを見ると、setlocale
3してるところはあるけど呼ばれていない4。
なので実装をいじるしかないわけですが、この謎表示は非ASCII圏では誰もが遭遇するので既に2020年(2年前…)にはissueが立っていました。
ということで、SetThreadUILanguage
してくれ~というコメントを書いておいたがどうなることやら。
-
やり方についてはOpenSSH をインストールするを参照。 ↩
-
MSDNのドキュメントはgai_strerrorA関数 (ws2tcpip.h)にある(ANSI版)。 ↩
-
MSVCでは
setlocale
してもgai_strerror
の返事に変化がなかったので、CRTランタイムでスレッドロケールをいじってないように見える。 ↩ -
main()
で呼ばれるmsetlocale()
のutf8.cでの実装はsetlocale()
を呼んでいますが、こいつはVSプロジェクトで弾かれていて、実際に呼ばれるのはcontrib/win32/win32compat/win32-utf8.cにある実装。 ↩