リビジョン | 6027 (tree) |
---|---|
日時 | 2015-09-20 01:51:28 |
作者 | (del#24082) |
全角スペースをASCIIに変換した。
@@ -63,41 +63,41 @@ | ||
63 | 63 | |
64 | 64 | <!-- |
65 | 65 | <h2><a name="module">モジュール構成</a></h2> |
66 | - Tera Termパッケージに含まれる実行モジュール(.exeと.dll)の関連図を以下に示します。実行ファイルの拡張子は".exe"になっており、必要に応じてDLLが動的リンクされます。いずれも32ビットプログラム(x86)であるために、x86-64やIA-64といった64ビット環境ではそのまま動作するかどうかは評価されていません。 | |
67 | - | |
66 | + Tera Termパッケージに含まれる実行モジュール(.exeと.dll)の関連図を以下に示します。実行ファイルの拡張子は".exe"になっており、必要に応じてDLLが動的リンクされます。いずれも32ビットプログラム(x86)であるために、x86-64やIA-64といった64ビット環境ではそのまま動作するかどうかは評価されていません。 | |
67 | + | |
68 | 68 | <div align="center"> |
69 | 69 | <img src="image/module_relation.png" width=720 height=540> |
70 | 70 | </div> |
71 | 71 | |
72 | - 通常、ユーザがデスクトップやスタートメニューからアプリケーションを起動するときに、呼び出される実行ファイルは"ttermpro.exe"になります。実行ファイルはさらに5つのDLLとダイナミックリンクしています。静的リンクを行い、単一のEXEファイルにしていないのは、1つのプロセスのメモリ占有率を抑えるためです。Tera Termでは多数の起動が行われることが想定されるため、初期設計段階からDLLに分割されています。一度読み込まれたDLLは、複数のプロセス間で共有することができます。<br> | |
73 | - <br> | |
74 | - | |
75 | - マクロスクリプトを実行する際は、"ttpmacro.exe"というまったく別のプロセスが呼び出されます。"ttermpro.exe"とプロセス単位で分けられているのは、マクロを単体で実行できるようにするためです。両プロセス間で、データのやりとりを行うためには、プロセス間通信が必要です。Tera Termでは、DDE(Dynamic Data Exchange)と呼ばれる現在ではレガシーとなってしまったしくみが採用されています。将来のWindowsではDDEがサポートされなくなる可能性があり、その場合Tera Term上でマクロを実行することは一切できなくなります。<br> | |
76 | - <br> | |
77 | - | |
78 | - TTSSHやTTProxy、TTXKanjiMenuといったプラグイン形式のDLLは、Tera Termの起動時に明示的に LoadLibrary() を使ってダイナミックロードされます。ロード対象となるDLLのファイル名は、TTXInit()#ttplug.c において、"TTX*.DLL"というパターンにマッチしたものとなります。<br> | |
79 | - <br> | |
80 | - | |
81 | - "keycode.exe"と"ttpmenu.exe"、"LogMeTT.exe"は単体アプリケーションです。<br> | |
82 | - <br> | |
83 | - | |
84 | - Cygwin接続のしくみについては、別の節で説明します。 | |
72 | + 通常、ユーザがデスクトップやスタートメニューからアプリケーションを起動するときに、呼び出される実行ファイルは"ttermpro.exe"になります。実行ファイルはさらに5つのDLLとダイナミックリンクしています。静的リンクを行い、単一のEXEファイルにしていないのは、1つのプロセスのメモリ占有率を抑えるためです。Tera Termでは多数の起動が行われることが想定されるため、初期設計段階からDLLに分割されています。一度読み込まれたDLLは、複数のプロセス間で共有することができます。<br> | |
73 | + <br> | |
74 | + | |
75 | + マクロスクリプトを実行する際は、"ttpmacro.exe"というまったく別のプロセスが呼び出されます。"ttermpro.exe"とプロセス単位で分けられているのは、マクロを単体で実行できるようにするためです。両プロセス間で、データのやりとりを行うためには、プロセス間通信が必要です。Tera Termでは、DDE(Dynamic Data Exchange)と呼ばれる現在ではレガシーとなってしまったしくみが採用されています。将来のWindowsではDDEがサポートされなくなる可能性があり、その場合Tera Term上でマクロを実行することは一切できなくなります。<br> | |
76 | + <br> | |
77 | + | |
78 | + TTSSHやTTProxy、TTXKanjiMenuといったプラグイン形式のDLLは、Tera Termの起動時に明示的に LoadLibrary() を使ってダイナミックロードされます。ロード対象となるDLLのファイル名は、TTXInit()#ttplug.c において、"TTX*.DLL"というパターンにマッチしたものとなります。<br> | |
79 | + <br> | |
80 | + | |
81 | + "keycode.exe"と"ttpmenu.exe"、"LogMeTT.exe"は単体アプリケーションです。<br> | |
82 | + <br> | |
83 | + | |
84 | + Cygwin接続のしくみについては、別の節で説明します。 | |
85 | 85 | |
86 | 86 | <hr> |
87 | 87 | |
88 | 88 | |
89 | 89 | <h2><a name="library">ライブラリ構成</a></h2> |
90 | - 高度な機能を実現するために、フルスクラッチで実装することは効率がいいとは言えません。Tera Termでは開発効率化を図るために、オープンソースのライブラリを積極的に利用しています。ただし、オープンソース製品のライセンスによる競合には注意を払う必要があります(特にGPL)。<br> | |
91 | - 下図に、オープンソースのライブラリをリンクしているモジュールと、そのリンク状況を示します。Tera Termマクロプログラムにおいて、"waitregex"や"sprintf"コマンドにおいて正規表現を利用するために、Onigurumaと呼ばれる正規表現ライブラリをリンクしています。Tera Term本体では、バージョンダイアログにOnigurumaのバージョンを表示するためだけにリンクをしています。 | |
92 | - | |
90 | + 高度な機能を実現するために、フルスクラッチで実装することは効率がいいとは言えません。Tera Termでは開発効率化を図るために、オープンソースのライブラリを積極的に利用しています。ただし、オープンソース製品のライセンスによる競合には注意を払う必要があります(特にGPL)。<br> | |
91 | + 下図に、オープンソースのライブラリをリンクしているモジュールと、そのリンク状況を示します。Tera Termマクロプログラムにおいて、"waitregex"や"sprintf"コマンドにおいて正規表現を利用するために、Onigurumaと呼ばれる正規表現ライブラリをリンクしています。Tera Term本体では、バージョンダイアログにOnigurumaのバージョンを表示するためだけにリンクをしています。 | |
92 | + | |
93 | 93 | <p> |
94 | - SSHモジュールである"TTSSH"は、暗号処理を行うためにOpenSSLを利用しています。"OpenSSL"というネーミングからWebアクセスに使われるSSL(Secure Socket Layer)プロトコル専用のライブラリかと思われがちですが、基本的な暗号アルゴリズムをサポートしていることから、TTSSHではOpenSSLに含まれる低レイヤのルーチンを利用するだけに留まっています。このことは、すなわちOpenSSLライブラリにセキュリティホールが発見されたとしても、TTSSHへの影響は極めて低いということです。<br> | |
95 | - zlibライブラリは、SSHパケットの圧縮を行うために利用しています。ただし、ダイヤルアップ回線などの低速度なネットワークにおいては、パケット圧縮は有効ですが、昨今の高速回線ではむしろ速度低下を招く足かせとなります。ゆえに、デフォルトではパケット圧縮機能は無効化されています。 | |
96 | - PuTTYは世界標準であるフリーのターミナルエミュレータです。PuTTYに含まれるPageantと呼ばれるSSH認証エージェントがあるのですが、TTSSHでPageantによる公開鍵認証をサポートするために、PuTTYのソースコードを利用しています。 | |
94 | + SSHモジュールである"TTSSH"は、暗号処理を行うためにOpenSSLを利用しています。"OpenSSL"というネーミングからWebアクセスに使われるSSL(Secure Socket Layer)プロトコル専用のライブラリかと思われがちですが、基本的な暗号アルゴリズムをサポートしていることから、TTSSHではOpenSSLに含まれる低レイヤのルーチンを利用するだけに留まっています。このことは、すなわちOpenSSLライブラリにセキュリティホールが発見されたとしても、TTSSHへの影響は極めて低いということです。<br> | |
95 | + zlibライブラリは、SSHパケットの圧縮を行うために利用しています。ただし、ダイヤルアップ回線などの低速度なネットワークにおいては、パケット圧縮は有効ですが、昨今の高速回線ではむしろ速度低下を招く足かせとなります。ゆえに、デフォルトではパケット圧縮機能は無効化されています。 | |
96 | + PuTTYは世界標準であるフリーのターミナルエミュレータです。PuTTYに含まれるPageantと呼ばれるSSH認証エージェントがあるのですが、TTSSHでPageantによる公開鍵認証をサポートするために、PuTTYのソースコードを利用しています。 | |
97 | 97 | </p> |
98 | - | |
99 | - なお、いずれのライブラリも静的リンク(static link)としています。ライブラリのコンパイルオプションには"/MT"を付加しています。動的リンク(dynamic link)を行うと、一部のユーザ環境でTera Termが起動できないという現象が発生したために、現在では動的リンクは行っていません。 | |
100 | - | |
98 | + | |
99 | + なお、いずれのライブラリも静的リンク(static link)としています。ライブラリのコンパイルオプションには"/MT"を付加しています。動的リンク(dynamic link)を行うと、一部のユーザ環境でTera Termが起動できないという現象が発生したために、現在では動的リンクは行っていません。 | |
100 | + | |
101 | 101 | |
102 | 102 | <div align="center"> |
103 | 103 | <img src="image/library_relation.png" width=720 height=540> |
@@ -176,7 +176,7 @@ | ||
176 | 176 | |
177 | 177 | Basically, the export function of the plug-in module should be designed not to interfere other plug-in modules. Also, when the plug-in module is called by Tera Term body, the module needs to check the request for own module. <br> |
178 | 178 | Every export function of plug-in module are as follows: |
179 | - | |
179 | + | |
180 | 180 | <p> |
181 | 181 | <table border=1 align=center> |
182 | 182 | <tr> |
@@ -265,8 +265,8 @@ | ||
265 | 265 | |
266 | 266 | </table> |
267 | 267 | </p> |
268 | - | |
269 | 268 | |
269 | + | |
270 | 270 | <hr> |
271 | 271 | |
272 | 272 |
@@ -273,12 +273,12 @@ | ||
273 | 273 | <!-- |
274 | 274 | |
275 | 275 | <h2><a name="configuration">設定ファイルの読み書き</a></h2> |
276 | - Windowsではアプリケーションのデータ保存のために、レジストリが伝統的に利用されていますが、Tera Termではその誕生がWindows 3.1までに遡るために、.iniファイルによるローカルディレクトリへの保存方法が標準となっています。<br> | |
277 | - パッケージに同梱されるCollectorやLogMeTT、CygTermに関してもローカルディレクトリへデータが保存されます。<br> | |
278 | - 例外として、TeraTerm Menuはデフォルトでレジストリへ保存をします。カレントディレクトリに"ttpmenu.ini"(0バイトで可)を設置することで、レジストリの代わりに.iniファイルを使うようにすることもできます。ただし、レジストリから.iniファイルへの移行は自動で行いませんので、手動で再登録してください。<br> | |
279 | - <br> | |
280 | - | |
281 | - teraterm.iniファイルにエントリを追加した場合は、ReadIniFile()#ttset.cに設定を読み込みするようにします。 | |
276 | + Windowsではアプリケーションのデータ保存のために、レジストリが伝統的に利用されていますが、Tera Termではその誕生がWindows 3.1までに遡るために、.iniファイルによるローカルディレクトリへの保存方法が標準となっています。<br> | |
277 | + パッケージに同梱されるCollectorやLogMeTT、CygTermに関してもローカルディレクトリへデータが保存されます。<br> | |
278 | + 例外として、TeraTerm Menuはデフォルトでレジストリへ保存をします。カレントディレクトリに"ttpmenu.ini"(0バイトで可)を設置することで、レジストリの代わりに.iniファイルを使うようにすることもできます。ただし、レジストリから.iniファイルへの移行は自動で行いませんので、手動で再登録してください。<br> | |
279 | + <br> | |
280 | + | |
281 | + teraterm.iniファイルにエントリを追加した場合は、ReadIniFile()#ttset.cに設定を読み込みするようにします。 | |
282 | 282 | |
283 | 283 | <pre class=code> |
284 | 284 | ts->ConfirmChangePaste = |
@@ -285,7 +285,7 @@ | ||
285 | 285 | GetOnOff(Section, "ConfirmChangePaste", FName, TRUE); |
286 | 286 | </pre> |
287 | 287 | |
288 | - WriteIniFile()#ttset.c に設定を書き込みするようにします。 | |
288 | + WriteIniFile()#ttset.c に設定を書き込みするようにします。 | |
289 | 289 | |
290 | 290 | <pre class=code> |
291 | 291 | WriteOnOff(Section, "ConfirmChangePaste", FName, |
@@ -292,7 +292,7 @@ | ||
292 | 292 | ts->ConfirmChangePaste); |
293 | 293 | </pre> |
294 | 294 | |
295 | - エントリに文字列を設定する場合は、Win32APIのGetPrivateProfileString()とWritePrivateProfileString()を使います。数値を扱いたい場合は、GetPrivateProfileInt()とWriteInt()を使います。 | |
295 | + エントリに文字列を設定する場合は、Win32APIのGetPrivateProfileString()とWritePrivateProfileString()を使います。数値を扱いたい場合は、GetPrivateProfileInt()とWriteInt()を使います。 | |
296 | 296 | |
297 | 297 | <hr> |
298 | 298 |
@@ -301,9 +301,9 @@ | ||
301 | 301 | <h2><a name="secure">セキュアプログラミング</a></h2> |
302 | 302 | |
303 | 303 | <h3>文字列操作</h3> |
304 | - WindowsのデフォルトアカウントはAdministrator権限を保持するために(ただし、Windows Vistaには当てはまらない)、アプリケーションにバッファオーバーフローの不具合があると、管理者権限を第三者に奪取されてしまう危険性があります。<br> | |
305 | - 従来、C言語の文字列処理は開発者のミスにより、バッファオーバーフローが発生しやすいという状況にありました。そこで、MicrosoftはVisual Studio 2005から文字列処理関数のセキュリティ強化バージョンを提供するようになりました。<br> | |
306 | - <br> | |
304 | + WindowsのデフォルトアカウントはAdministrator権限を保持するために(ただし、Windows Vistaには当てはまらない)、アプリケーションにバッファオーバーフローの不具合があると、管理者権限を第三者に奪取されてしまう危険性があります。<br> | |
305 | + 従来、C言語の文字列処理は開発者のミスにより、バッファオーバーフローが発生しやすいという状況にありました。そこで、MicrosoftはVisual Studio 2005から文字列処理関数のセキュリティ強化バージョンを提供するようになりました。<br> | |
306 | + <br> | |
307 | 307 | |
308 | 308 | <ul> |
309 | 309 | <li><a href="http://msdn2.microsoft.com/ja-jp/library/8ef0s5kh(VS.80).aspx">CRT のセキュリティ強化(MSDNライブラリ)</a></li> |
@@ -310,8 +310,8 @@ | ||
310 | 310 | </ul> |
311 | 311 | <br> |
312 | 312 | |
313 | - Tera Termではセキュリティ強化を図るため、文字列操作のほとんどをセキュリティ強化バージョンに置き換えています。以下に代替関数を示します。<br> | |
314 | - <br> | |
313 | + Tera Termではセキュリティ強化を図るため、文字列操作のほとんどをセキュリティ強化バージョンに置き換えています。以下に代替関数を示します。<br> | |
314 | + <br> | |
315 | 315 | |
316 | 316 | <table border=1 align=center> |
317 | 317 | <tr> |
@@ -334,15 +334,15 @@ | ||
334 | 334 | <td>strncpy_s()</td> |
335 | 335 | </tr> |
336 | 336 | </table> |
337 | - <br> | |
338 | - | |
339 | - デフォルトのロケールが適用されると、期待する動作とならないケースにおいては、_snprintf_s_l()を使用しています。<br> | |
340 | - いずれの関数においても、_s("secure")という接尾辞が付くため、見た目に区別が付きやすくなっています。当然のことながら、これらの関数はANSI C非互換です。<br> | |
341 | - <br> | |
342 | - なお、これらの関数を利用する際、Count引数(格納する最大文字数)には"_TRUNCATE"マクロを指定しており、バッファオーバーフローが発生する場合は、強制的にバッファの切り詰めを行っています。 | |
337 | + <br> | |
338 | + | |
339 | + デフォルトのロケールが適用されると、期待する動作とならないケースにおいては、_snprintf_s_l()を使用しています。<br> | |
340 | + いずれの関数においても、_s("secure")という接尾辞が付くため、見た目に区別が付きやすくなっています。当然のことながら、これらの関数はANSI C非互換です。<br> | |
341 | + <br> | |
342 | + なお、これらの関数を利用する際、Count引数(格納する最大文字数)には"_TRUNCATE"マクロを指定しており、バッファオーバーフローが発生する場合は、強制的にバッファの切り詰めを行っています。 | |
343 | 343 | <p> |
344 | 344 | |
345 | - 以下に、strncpy_s()の使用例を示します。strncpy_s()の第2引数(numberOfElements)には、<b>ナル文字(\0)も含めた</b>バッファサイズを指定します。書き込み先のバッファは3バイトしかないので、第3引数(strSource)で指定した5バイトのデータは、2バイトに切り詰められ、buf[]には"he\0"が格納されます。 | |
345 | + 以下に、strncpy_s()の使用例を示します。strncpy_s()の第2引数(numberOfElements)には、<b>ナル文字(\0)も含めた</b>バッファサイズを指定します。書き込み先のバッファは3バイトしかないので、第3引数(strSource)で指定した5バイトのデータは、2バイトに切り詰められ、buf[]には"he\0"が格納されます。 | |
346 | 346 | |
347 | 347 | <pre class=code> |
348 | 348 | char buf[3]; |
@@ -349,8 +349,8 @@ | ||
349 | 349 | strncpy_s(buf, sizeof(buf), "hello", _TRUNCATE); |
350 | 350 | </pre> |
351 | 351 | |
352 | - 次に、strncat_s()の使用例を示します。当該関数は、すでに存在する文字列に、さらに文字列を連結するものであるため、第1引数(strDest)は<b>かならずnull-terminateしている</b>必要性があります。strncpy_s()の第2引数(numberOfElements)には、ナル文字(\0)も含めたバッファサイズを指定します。以下の例では、最初の関数を実行すると、5バイト(4文字+ナル文字)が格納されます。2つめの関数を実行する際、残り2バイトしかないので、2文字だけがコピーされ、最終的に"TeraTe"(4文字+2文字+ナル文字)となります。 | |
353 | - | |
352 | + 次に、strncat_s()の使用例を示します。当該関数は、すでに存在する文字列に、さらに文字列を連結するものであるため、第1引数(strDest)は<b>かならずnull-terminateしている</b>必要性があります。strncpy_s()の第2引数(numberOfElements)には、ナル文字(\0)も含めたバッファサイズを指定します。以下の例では、最初の関数を実行すると、5バイト(4文字+ナル文字)が格納されます。2つめの関数を実行する際、残り2バイトしかないので、2文字だけがコピーされ、最終的に"TeraTe"(4文字+2文字+ナル文字)となります。 | |
353 | + | |
354 | 354 | <pre class=code> |
355 | 355 | char str[7]; |
356 | 356 | str[0] = '\0'; |
@@ -358,7 +358,7 @@ | ||
358 | 358 | strncat_s(str, sizeof(str), "Term", _TRUNCATE); |
359 | 359 | </pre> |
360 | 360 | |
361 | - 最後に、_snprintf_s()です。紛らわしいのが _snprintf() という関数であり、この関数は<b>null-terminateされない</b>ケースがあるため、使用禁止です。以下に、_snprintf_s()の使用例を示します。以下の例では、buf[]には"ab\0"が格納されます。 | |
361 | + 最後に、_snprintf_s()です。紛らわしいのが _snprintf() という関数であり、この関数は<b>null-terminateされない</b>ケースがあるため、使用禁止です。以下に、_snprintf_s()の使用例を示します。以下の例では、buf[]には"ab\0"が格納されます。 | |
362 | 362 | |
363 | 363 | <pre class=code> |
364 | 364 | char buf[3]; |
@@ -374,8 +374,8 @@ | ||
374 | 374 | |
375 | 375 | <h3>ダイナミックローディング</h3> |
376 | 376 | |
377 | - Windowsのアプリケーションプログラムは、単一のバイナリファイルを変更することなく、新旧のバージョンのWindows上で起動できるようにするためには、アプリケーションプログラム側での工夫が必要です。<br> | |
378 | - たとえば、Windows2000で導入された SetLayeredWindowAttributes() APIを直接呼び出すと、WindowsNT4.0や98などではアプリケーションの起動に失敗するようになります。そのため、新しいAPIを呼び出すときは、LoadLibrary()を使って動的ロードするようにします。<br> | |
377 | + Windowsのアプリケーションプログラムは、単一のバイナリファイルを変更することなく、新旧のバージョンのWindows上で起動できるようにするためには、アプリケーションプログラム側での工夫が必要です。<br> | |
378 | + たとえば、Windows2000で導入された SetLayeredWindowAttributes() APIを直接呼び出すと、WindowsNT4.0や98などではアプリケーションの起動に失敗するようになります。そのため、新しいAPIを呼び出すときは、LoadLibrary()を使って動的ロードするようにします。<br> | |
379 | 379 | |
380 | 380 | <pre class=code> |
381 | 381 | static BOOL MySetLayeredWindowAttributes(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags) |
@@ -401,7 +401,7 @@ | ||
401 | 401 | } |
402 | 402 | </pre> |
403 | 403 | |
404 | - いちいち、手で関数プロトタイプを書いていくのは面倒である場合は、「DLLの遅延読み込み」というしくみを利用すると、上記のような手順は不要です。いきなり、関数を呼び出すことができます。ダイレクトに呼び出したい関数がある場合、それが古いWindowsではサポートされていないものであるならば、Visual Studioのプロジェクト設定で、「DLLの遅延読み込み」に該当するDLLを指定しておきます。 | |
404 | + いちいち、手で関数プロトタイプを書いていくのは面倒である場合は、「DLLの遅延読み込み」というしくみを利用すると、上記のような手順は不要です。いきなり、関数を呼び出すことができます。ダイレクトに呼び出したい関数がある場合、それが古いWindowsではサポートされていないものであるならば、Visual Studioのプロジェクト設定で、「DLLの遅延読み込み」に該当するDLLを指定しておきます。 | |
405 | 405 | |
406 | 406 | |
407 | 407 | <h3>Windows 95</h3> |
@@ -421,10 +421,10 @@ | ||
421 | 421 | |
422 | 422 | <h2><a name="debug">デバッグ手法</a></h2> |
423 | 423 | <h3>debug printf</h3> |
424 | - Windowsアプリケーションでは printf() が使えません。標準出力がどこにも割り当てられていないからです。AllocConsole()とfreopen()を使えば、Windowsアプリケーションにおいても printf() を利用することができます。<br> | |
425 | - OutputDebugString()というAPIがあります。これは Visual Studio のデバッグコンソールにメッセージ出力することができる関数です。当該APIは、"Debug build"および"Release build"に関係なく、デバッガが存在すれば、メッセージを送信します。ゆえに、 Visual Studioがなくとも、<a href="http://www.vector.co.jp/soft/win95/prog/se046776.html">DBCon</a>のようなツールを使えば、アプリケーションの単体起動においても、OutputDebugString()によるメッセージを拾うことができます。<br> | |
426 | - Tera Termでは、可変長引数を扱えるようにラッパー関数を用意しています。 | |
427 | - | |
424 | + Windowsアプリケーションでは printf() が使えません。標準出力がどこにも割り当てられていないからです。AllocConsole()とfreopen()を使えば、Windowsアプリケーションにおいても printf() を利用することができます。<br> | |
425 | + OutputDebugString()というAPIがあります。これは Visual Studio のデバッグコンソールにメッセージ出力することができる関数です。当該APIは、"Debug build"および"Release build"に関係なく、デバッガが存在すれば、メッセージを送信します。ゆえに、 Visual Studioがなくとも、<a href="http://www.vector.co.jp/soft/win95/prog/se046776.html">DBCon</a>のようなツールを使えば、アプリケーションの単体起動においても、OutputDebugString()によるメッセージを拾うことができます。<br> | |
426 | + Tera Termでは、可変長引数を扱えるようにラッパー関数を用意しています。 | |
427 | + | |
428 | 428 | <pre class=code> |
429 | 429 | void OutputDebugPrintf(char *fmt, ...) { |
430 | 430 | char tmp[1024]; |
@@ -436,7 +436,7 @@ | ||
436 | 436 | </pre> |
437 | 437 | |
438 | 438 | <h3>memory leak</h3> |
439 | - malloc()等による確保したヒープメモリの解放し忘れによる「メモリリーク」を、自動で検出するしくみが Visual Studio には用意されています。プログラムの起動時に、以下のコードを挿入するだけです。プログラムの終了時に、解放していないヒープメモリがあれば、 Visual Studio の「出力」ウィンドウにリストアップされます。 | |
439 | + malloc()等による確保したヒープメモリの解放し忘れによる「メモリリーク」を、自動で検出するしくみが Visual Studio には用意されています。プログラムの起動時に、以下のコードを挿入するだけです。プログラムの終了時に、解放していないヒープメモリがあれば、 Visual Studio の「出力」ウィンドウにリストアップされます。 | |
440 | 440 | |
441 | 441 | <pre class=code> |
442 | 442 | #ifdef _DEBUG |
@@ -444,14 +444,14 @@ | ||
444 | 444 | #endif |
445 | 445 | </pre> |
446 | 446 | |
447 | - なお、Windowsのように仮想記憶で動くアプリケーションプログラムに関しては、プログラムの終了時に解放されていないメモリが存在した場合、OSが面倒を見て、メモリが解放されるようになっています。 | |
447 | + なお、Windowsのように仮想記憶で動くアプリケーションプログラムに関しては、プログラムの終了時に解放されていないメモリが存在した場合、OSが面倒を見て、メモリが解放されるようになっています。 | |
448 | 448 | |
449 | 449 | <hr> |
450 | 450 | |
451 | 451 | |
452 | 452 | <h2><a name="thread">マルチスレッド</a></h2> |
453 | - Windowsのアプリケーションはマルチスレッドで設計されることがほとんどですが、Windows 3.1から95の時代ではあまり一般的ではありませんでした。そのため、元々Tera Termはマルチスレッド化されていません。ソースコードを見ると分かるように、グローバル変数が多用されているため、ほとんどの処理がスレッドセーフではありません。<br> | |
454 | - ただし、一部の処理においては _beginthreadex() API を使ってスレッドが生成されています。以下にスレッド生成箇所を示します。 | |
453 | + Windowsのアプリケーションはマルチスレッドで設計されることがほとんどですが、Windows 3.1から95の時代ではあまり一般的ではありませんでした。そのため、元々Tera Termはマルチスレッド化されていません。ソースコードを見ると分かるように、グローバル変数が多用されているため、ほとんどの処理がスレッドセーフではありません。<br> | |
454 | + ただし、一部の処理においては _beginthreadex() API を使ってスレッドが生成されています。以下にスレッド生成箇所を示します。 | |
455 | 455 | |
456 | 456 | <p> |
457 | 457 | <div align=center><b>Tera Term</b></div> |
@@ -503,9 +503,9 @@ | ||
503 | 503 | </table> |
504 | 504 | </p> |
505 | 505 | |
506 | - すでに説明したとおり、Tera Term(TTSSH含む)の内部処理はスレッドセーフではないため、シンプルにスレッドを生成し、スレッド内から送受信処理等を行おうとすると、不具合が発生してしまいます。<br> | |
507 | - TELNETやSSHのキープアライブ(ハートビート)処理を実現するためには、定期的にパケットの送信処理を行う必要があります。また、SCPによるファイル送受信を行う際においても、ファイルの送信処理中に、ユーザの端末操作のレスポンスを落とさないために、スレッドの使用が不可欠です。<br> | |
508 | - そこで、マルチスレッドを使う場合は、モードレスダイアログを非表示で作成したあとに、_beginthreadex() APIでスレッドを生成し、実際の処理はモードレスダイアログに行わせるという手段を使用しています。このしくみにより、マルチスレッドを使いながら、スレッドセーフを保つことができます。以下に、コード例を示します。<br> | |
506 | + すでに説明したとおり、Tera Term(TTSSH含む)の内部処理はスレッドセーフではないため、シンプルにスレッドを生成し、スレッド内から送受信処理等を行おうとすると、不具合が発生してしまいます。<br> | |
507 | + TELNETやSSHのキープアライブ(ハートビート)処理を実現するためには、定期的にパケットの送信処理を行う必要があります。また、SCPによるファイル送受信を行う際においても、ファイルの送信処理中に、ユーザの端末操作のレスポンスを落とさないために、スレッドの使用が不可欠です。<br> | |
508 | + そこで、マルチスレッドを使う場合は、モードレスダイアログを非表示で作成したあとに、_beginthreadex() APIでスレッドを生成し、実際の処理はモードレスダイアログに行わせるという手段を使用しています。このしくみにより、マルチスレッドを使いながら、スレッドセーフを保つことができます。以下に、コード例を示します。<br> | |
509 | 509 | |
510 | 510 | <pre class=code> |
511 | 511 | #define WM_SEND_HEARTBEAT (WM_USER + 1) |
@@ -579,9 +579,9 @@ | ||
579 | 579 | |
580 | 580 | <h2><a name="dde">DDEによるプロセス間通信</a></h2> |
581 | 581 | <h3>概要</h3> |
582 | - DDE(Dynamic Data Exchange)の誕生は、1987年のWindows 2.0までに遡ります。DDEはプロセス間通信を行うためのしくみですが、現在ではレガシーな方式であり、ほとんどのアプリケーションでは利用されていません。Windowsにおけるプロセス間通信といえば、メールスロットや名前付きパイプ、OLEなどが定番です。<br> | |
583 | - かつては、DDEによるプロセス間の通信データをキャプチャすることができる「DDEスパイ」(DDESPY.EXE)というツールがVisual Studioに付属していましたが、現在のVisual Studioにはもはや含まれていません。<br> | |
584 | - DDEに関するリファレンスはMSDNライブラリから参照することができます。<br> | |
582 | + DDE(Dynamic Data Exchange)の誕生は、1987年のWindows 2.0までに遡ります。DDEはプロセス間通信を行うためのしくみですが、現在ではレガシーな方式であり、ほとんどのアプリケーションでは利用されていません。Windowsにおけるプロセス間通信といえば、メールスロットや名前付きパイプ、OLEなどが定番です。<br> | |
583 | + かつては、DDEによるプロセス間の通信データをキャプチャすることができる「DDEスパイ」(DDESPY.EXE)というツールがVisual Studioに付属していましたが、現在のVisual Studioにはもはや含まれていません。<br> | |
584 | + DDEに関するリファレンスはMSDNライブラリから参照することができます。<br> | |
585 | 585 | |
586 | 586 | <p> |
587 | 587 | <ul> |
@@ -590,15 +590,15 @@ | ||
590 | 590 | </ul> |
591 | 591 | </p> |
592 | 592 | |
593 | - DDEは、TCPによるネットワーク通信と似ており、サーバとクライアント間を一対一で接続し、通信を行います。アプリケーションがDDEによる通信を行うために、DDEML(Dynamic Data Exchange Management Library)と呼ばれるライブラリをWin32 APIとして提供されています。<br> | |
594 | - DDE通信を行うために、一方がサーバとなり、他方がクライアントになる必要があります。また、通信のセッションをシステム全体でユニークとするために、識別情報が必要です。TCP通信ではIPアドレスとポート番号が使われますが、DDE通信では「サービス名」と「トピック名」の組み合わせが使われます。Tera Termではサービス名は"TERATERM"という文字列が使われ、トピック名はTera Term本体のウィンドウハンドル(HVTWin)の16進数値を文字列化したものが使われています。<br> | |
595 | - このようなしくみになっているために、マクロスクリプトからまったく別のTera Termへコマンドを送ることはできません。<br> | |
593 | + DDEは、TCPによるネットワーク通信と似ており、サーバとクライアント間を一対一で接続し、通信を行います。アプリケーションがDDEによる通信を行うために、DDEML(Dynamic Data Exchange Management Library)と呼ばれるライブラリをWin32 APIとして提供されています。<br> | |
594 | + DDE通信を行うために、一方がサーバとなり、他方がクライアントになる必要があります。また、通信のセッションをシステム全体でユニークとするために、識別情報が必要です。TCP通信ではIPアドレスとポート番号が使われますが、DDE通信では「サービス名」と「トピック名」の組み合わせが使われます。Tera Termではサービス名は"TERATERM"という文字列が使われ、トピック名はTera Term本体のウィンドウハンドル(HVTWin)の16進数値を文字列化したものが使われています。<br> | |
595 | + このようなしくみになっているために、マクロスクリプトからまったく別のTera Termへコマンドを送ることはできません。<br> | |
596 | 596 | |
597 | 597 | <div align="center"> |
598 | 598 | <img src="image/dde.png" width=720 height=540> |
599 | 599 | </div> |
600 | 600 | |
601 | - 上図に示すように、Tera Term本体("ttermpro.exe")がDDEサーバとなり、マクロプログラム("ttpmacro.exe")がDDEクライアントとなります。DDEでは、やりとりするデータの塊のことを「トランザクション」と呼びます。トランザクションには以下に示すような何種類かがあります。タイプは"ddeml.h"でマクロ定義されています。<br> | |
601 | + 上図に示すように、Tera Term本体("ttermpro.exe")がDDEサーバとなり、マクロプログラム("ttpmacro.exe")がDDEクライアントとなります。DDEでは、やりとりするデータの塊のことを「トランザクション」と呼びます。トランザクションには以下に示すような何種類かがあります。タイプは"ddeml.h"でマクロ定義されています。<br> | |
602 | 602 | |
603 | 603 | <p> |
604 | 604 | <table border=1 align=center> |
@@ -635,12 +635,12 @@ | ||
635 | 635 | </table> |
636 | 636 | </p> |
637 | 637 | |
638 | - DDE通信の特徴として、アドバイズループ(advise loop)という概念があります。DDEサーバがアドバイズループに入ると、クライアントはサーバから定期的にデータを受け取り続けることができます。Tera Termでは、リモートホストからの受信データを、マクロプログラムへ渡すために、アドバイズループが使われています。<br> | |
638 | + DDE通信の特徴として、アドバイズループ(advise loop)という概念があります。DDEサーバがアドバイズループに入ると、クライアントはサーバから定期的にデータを受け取り続けることができます。Tera Termでは、リモートホストからの受信データを、マクロプログラムへ渡すために、アドバイズループが使われています。<br> | |
639 | 639 | |
640 | 640 | <h3>ライブラリ</h3> |
641 | - Tera Termで使われているDDEMLについて、以下に示します。 | |
642 | - | |
643 | - | |
641 | + Tera Termで使われているDDEMLについて、以下に示します。 | |
642 | + | |
643 | + | |
644 | 644 | <p> |
645 | 645 | <table border=1 align=center> |
646 | 646 | <tr> |
@@ -704,10 +704,10 @@ | ||
704 | 704 | |
705 | 705 | |
706 | 706 | <h3>実装</h3> |
707 | - DDEサーバ側の実装について見ていきます。Tera Term本体("ttermpro.exe")がDDEサーバとなり、かならずDDEサーバから起動されます。マクロプログラム("ttpmacro.exe")から直接マクロスクリプトが実行されるケースにおいても、"connect"マクロによりDDE接続をしないと、通信が開始できません。<br> | |
708 | - Tera TermのControlメニューからMacroを呼び出した場合、RunMacro()#ttdde.c がコールされます。<br> | |
709 | - HVTWinウィンドウハンドルからトピック名(8バイト)を作成し、DDEの初期化とサーバの登録を行います。また、このタイミングでDDEバッファ(1KB)を作成しています。その後、"ttpmacro.exe"を /D= オプションでトピック名を渡しつつ、起動をします。<br> | |
710 | - | |
707 | + DDEサーバ側の実装について見ていきます。Tera Term本体("ttermpro.exe")がDDEサーバとなり、かならずDDEサーバから起動されます。マクロプログラム("ttpmacro.exe")から直接マクロスクリプトが実行されるケースにおいても、"connect"マクロによりDDE接続をしないと、通信が開始できません。<br> | |
708 | + Tera TermのControlメニューからMacroを呼び出した場合、RunMacro()#ttdde.c がコールされます。<br> | |
709 | + HVTWinウィンドウハンドルからトピック名(8バイト)を作成し、DDEの初期化とサーバの登録を行います。また、このタイミングでDDEバッファ(1KB)を作成しています。その後、"ttpmacro.exe"を /D= オプションでトピック名を渡しつつ、起動をします。<br> | |
710 | + | |
711 | 711 | <pre class=code> |
712 | 712 | SetTopic(); |
713 | 713 | if (! InitDDE()) return; |
@@ -715,10 +715,10 @@ | ||
715 | 715 | strncat_s(Cmnd,sizeof(Cmnd),TopicName,_TRUNCATE); |
716 | 716 | </pre> |
717 | 717 | |
718 | - DDEサーバに、DDEクライアントからトランザクションが送られてきたときは、DdeCallbackProcコールバック関数が呼び出されます。コールバック関数は、DdeInitialize()でDDEの初期化を行うときに登録されます。<br><br> | |
719 | - | |
720 | - 次に、DDEクライアントについて見てみましょう。マクロプログラムの起動時、InitDDE()#ttmdde.c が呼び出され、DDEクライアントとして初期化が行われます。DDEの初期化は、DdeInitialize()で行われ、同時にDdeCallbackProcコールバック関数が登録されます。DDEサーバから届いたトランザクションは、コールバック関数で処理されます。<br> | |
721 | - DDE通信を始めるためには、DdeConnect()を呼び出し、サーバと接続する必要があります。次に、"ttpmacro.exe"のウィンドウハンドル(HWin)をサーバへ通知するために、XTYP_EXECUTEトランザクションで送ります。最後に、XTYP_ADVSTARTトランザクションをサーバへ送り、アドバイズループを開始します。<br> | |
718 | + DDEサーバに、DDEクライアントからトランザクションが送られてきたときは、DdeCallbackProcコールバック関数が呼び出されます。コールバック関数は、DdeInitialize()でDDEの初期化を行うときに登録されます。<br><br> | |
719 | + | |
720 | + 次に、DDEクライアントについて見てみましょう。マクロプログラムの起動時、InitDDE()#ttmdde.c が呼び出され、DDEクライアントとして初期化が行われます。DDEの初期化は、DdeInitialize()で行われ、同時にDdeCallbackProcコールバック関数が登録されます。DDEサーバから届いたトランザクションは、コールバック関数で処理されます。<br> | |
721 | + DDE通信を始めるためには、DdeConnect()を呼び出し、サーバと接続する必要があります。次に、"ttpmacro.exe"のウィンドウハンドル(HWin)をサーバへ通知するために、XTYP_EXECUTEトランザクションで送ります。最後に、XTYP_ADVSTARTトランザクションをサーバへ送り、アドバイズループを開始します。<br> | |
722 | 722 | |
723 | 723 | <pre class=code> |
724 | 724 | ConvH = DdeConnect(Inst, Service, Topic, NULL); |
@@ -740,38 +740,38 @@ | ||
740 | 740 | |
741 | 741 | |
742 | 742 | <h3>バッファの管理</h3> |
743 | - マクロプログラムでは"wait"コマンド等で、リモートホストから送られてきたデータを監視するための機能が用意されています。この機能を実現するためには、Tera Term本体とマクロプログラムのそれぞれにおいて、バッファを用意する必要があり、プロセス間通信(DDEトランザクション)により、Tera Term本体からマクロプログラムへリモートホストからの受信データを送らなければなりません。<br> | |
743 | + マクロプログラムでは"wait"コマンド等で、リモートホストから送られてきたデータを監視するための機能が用意されています。この機能を実現するためには、Tera Term本体とマクロプログラムのそれぞれにおいて、バッファを用意する必要があり、プロセス間通信(DDEトランザクション)により、Tera Term本体からマクロプログラムへリモートホストからの受信データを送らなければなりません。<br> | |
744 | 744 | |
745 | 745 | <div align="center"> |
746 | 746 | <img src="image/dde_flowcontrol.png" width=720 height=540> |
747 | 747 | </div> |
748 | 748 | |
749 | - まず、Tera Term本体におけるリモートホストからのTCPパケット受信は、アイドルループ OnIdle()#teraterm.cpp にて行われます。OnIdle()から呼び出される CommReceive()#commlib.c において、TCPパケットデータをバッファ(cv->InBuff[])に格納します。このバッファは 1KB の大きさを持ちます。また、リングバッファではないため、バッファフルになった場合は、TCPパケットの受信をしません。ただし、バッファフル状態が長く続くと、Windowsカーネル内にTCPパケットが溜まっていき、いずれはリモートホストからのパケットを受信できなくなる可能性があります。<br> | |
750 | - エスケープシーケンスの解析処理を行う過程で、「ログ採取」か「マクロ実行」を行っている場合は、LogPut1()が呼び出され、DDEバッファ(cv.LogBuf[])へ受信データが格納されます。すなわち、ログ採取とマクロ実行におけるバッファは共通です。このバッファは1KBの大きさを持つリングバッファであり、バッファフルになった場合は、最古のデータから上書きされてゆきます。<br> | |
751 | - なお、バイナリモードでログ採取においては、cv.BinBuf[] という別のバッファへデータが格納されますので、DDEバッファとは別物です。言い換えると、バイナリモードにおけるデータをDDE通信させることはできないということです。単純な"wait"コマンドでは、バイナリデータ(制御コードなど)を待つことはできません。<br> | |
752 | - Tera Term本体のDDEバッファのデータは、エスケープシーケンスの解析処理が完了後、DDEAdv()#ttdde.c がすぐに呼び出され、自分自身(DDEサーバ)へ XTYP_ADVREQ トランザクションを送ります。XTYP_ADVREQを受け取ったら、DDEコールバック関数 DdeCallbackProc() が呼び出され、マクロプログラムへのデータ送信を行います。ここでアドバイズループが使われています。<br> | |
749 | + まず、Tera Term本体におけるリモートホストからのTCPパケット受信は、アイドルループ OnIdle()#teraterm.cpp にて行われます。OnIdle()から呼び出される CommReceive()#commlib.c において、TCPパケットデータをバッファ(cv->InBuff[])に格納します。このバッファは 1KB の大きさを持ちます。また、リングバッファではないため、バッファフルになった場合は、TCPパケットの受信をしません。ただし、バッファフル状態が長く続くと、Windowsカーネル内にTCPパケットが溜まっていき、いずれはリモートホストからのパケットを受信できなくなる可能性があります。<br> | |
750 | + エスケープシーケンスの解析処理を行う過程で、「ログ採取」か「マクロ実行」を行っている場合は、LogPut1()が呼び出され、DDEバッファ(cv.LogBuf[])へ受信データが格納されます。すなわち、ログ採取とマクロ実行におけるバッファは共通です。このバッファは1KBの大きさを持つリングバッファであり、バッファフルになった場合は、最古のデータから上書きされてゆきます。<br> | |
751 | + なお、バイナリモードでログ採取においては、cv.BinBuf[] という別のバッファへデータが格納されますので、DDEバッファとは別物です。言い換えると、バイナリモードにおけるデータをDDE通信させることはできないということです。単純な"wait"コマンドでは、バイナリデータ(制御コードなど)を待つことはできません。<br> | |
752 | + Tera Term本体のDDEバッファのデータは、エスケープシーケンスの解析処理が完了後、DDEAdv()#ttdde.c がすぐに呼び出され、自分自身(DDEサーバ)へ XTYP_ADVREQ トランザクションを送ります。XTYP_ADVREQを受け取ったら、DDEコールバック関数 DdeCallbackProc() が呼び出され、マクロプログラムへのデータ送信を行います。ここでアドバイズループが使われています。<br> | |
753 | 753 | |
754 | 754 | <div align="center"> |
755 | 755 | <img src="image/dde_buffer.png" width=720 height=540> |
756 | 756 | </div> |
757 | 757 | |
758 | - アドバイズループによりDDEサーバよりデータが送られてくると、DDEクライアントであるマクロプログラムにおいては、XTYP_ADVDATAトランザクションがDDEコールバック関数 DdeCallbackProc()#ttmdde.c により処理されます。<br> | |
759 | - | |
760 | - なお、Tera Term本体において、DDE通信用のバッファと、ログ採取用のバッファは cv.LogBuf[] で共有されています。バッファの先頭とデータサイズを表すインデックスは、DDE通信の場合は"DStart"と"Dcount"、ログ採取の場合は"LStart"と"Lcount"と区別されています。実際には、1つのバッファを共有しているわけなので、それぞれのインデックスが食い違うと、誤動作する原因となるため、常に同期を取っておくことになります。<br> | |
758 | + アドバイズループによりDDEサーバよりデータが送られてくると、DDEクライアントであるマクロプログラムにおいては、XTYP_ADVDATAトランザクションがDDEコールバック関数 DdeCallbackProc()#ttmdde.c により処理されます。<br> | |
759 | + | |
760 | + なお、Tera Term本体において、DDE通信用のバッファと、ログ採取用のバッファは cv.LogBuf[] で共有されています。バッファの先頭とデータサイズを表すインデックスは、DDE通信の場合は"DStart"と"Dcount"、ログ採取の場合は"LStart"と"Lcount"と区別されています。実際には、1つのバッファを共有しているわけなので、それぞれのインデックスが食い違うと、誤動作する原因となるため、常に同期を取っておくことになります。<br> | |
761 | 761 | <hr> |
762 | 762 | |
763 | 763 | |
764 | 764 | <h2><a name="ttssh">TTSSHによるSSHの設計と実装</a></h2> |
765 | 765 | <h3>概要</h3> |
766 | - オリジナルのTTSSHは<a href="http://www.cs.cmu.edu/People/roc/">Robert O'Callahan</a>氏(現在は<a href="http://weblogs.mozillazine.org/roc/">Mozilla hacker</a>として活躍)により開発されたプラグインです。SSH1へ対応しており、ポートフォワーディングやzlibによるパケット圧縮もサポートしていました。TTSSHは、Tera Termをセキュア通信に対応させるためのプラグインであったために、SCPやSFTP等には未対応でした。オリジナルTera Termが1998年に開発凍結後も、2001年ごろまでメンテナンスが続けられていました。<br> | |
767 | - TTSSHのSSH2対応を実現するために、TeraTerm Projectにより2004年から設計と実装が始められました。3年の歳月をかけて、ほぼSSH2プロトコルのフルサポートを実現しました。現在ではSCPへも対応しています。将来的にはSFTPへも対応されるかもしれません。<br> | |
768 | - 原則、TTSSHの実装は<a href="http://www.openssh.com/">OpenSSH</a>を参考にしています。一部、コードをそのまま流用しているところもあります。ただし、OpenSSHはUNIXのコマンドライン向けに設計されているため、Tera TermのようなWindowsアプリケーションにはそのまま適合しない箇所も多く、フレームワークとしてはOpenSSHと大きく異なったものとなっています。<br> | |
766 | + オリジナルのTTSSHは<a href="http://www.cs.cmu.edu/People/roc/">Robert O'Callahan</a>氏(現在は<a href="http://weblogs.mozillazine.org/roc/">Mozilla hacker</a>として活躍)により開発されたプラグインです。SSH1へ対応しており、ポートフォワーディングやzlibによるパケット圧縮もサポートしていました。TTSSHは、Tera Termをセキュア通信に対応させるためのプラグインであったために、SCPやSFTP等には未対応でした。オリジナルTera Termが1998年に開発凍結後も、2001年ごろまでメンテナンスが続けられていました。<br> | |
767 | + TTSSHのSSH2対応を実現するために、TeraTerm Projectにより2004年から設計と実装が始められました。3年の歳月をかけて、ほぼSSH2プロトコルのフルサポートを実現しました。現在ではSCPへも対応しています。将来的にはSFTPへも対応されるかもしれません。<br> | |
768 | + 原則、TTSSHの実装は<a href="http://www.openssh.com/">OpenSSH</a>を参考にしています。一部、コードをそのまま流用しているところもあります。ただし、OpenSSHはUNIXのコマンドライン向けに設計されているため、Tera TermのようなWindowsアプリケーションにはそのまま適合しない箇所も多く、フレームワークとしてはOpenSSHと大きく異なったものとなっています。<br> | |
769 | 769 | |
770 | 770 | |
771 | 771 | <h3>SSHプロトコル</h3> |
772 | - SSH(Secure Shell)は、バージョン1(厳密には1.5)とバージョン2が存在し、略して"SSH1"および"SSH2"と呼ばれます。それらのバージョン間にはプロトコル仕様としての互換性はありません。SSH1にはセキュリティ上の問題があるために、現在はほとんど利用されません。<br> | |
773 | - SSH2プロトコルの仕様に関しては、RFC化されています。 | |
774 | - | |
772 | + SSH(Secure Shell)は、バージョン1(厳密には1.5)とバージョン2が存在し、略して"SSH1"および"SSH2"と呼ばれます。それらのバージョン間にはプロトコル仕様としての互換性はありません。SSH1にはセキュリティ上の問題があるために、現在はほとんど利用されません。<br> | |
773 | + SSH2プロトコルの仕様に関しては、RFC化されています。 | |
774 | + | |
775 | 775 | <p> |
776 | 776 | <ul> |
777 | 777 | <li><a href="http://www.ietf.org/rfc/rfc4250.txt">RFC4250: The Secure Shell (SSH) Protocol Assigned Numbers</a></li> |
@@ -788,7 +788,7 @@ | ||
788 | 788 | |
789 | 789 | |
790 | 790 | <h3>接続処理</h3> |
791 | - TTSSHは、Tera Termの一部のコードでもあるため、ネットワーク接続処理はTera TermとTTSSHの間を行き来することになり、処理の流れが複雑になっています。また、SSHプロトコルそのもののフローを熟知していないと、TTSSHのシーケンスを追っていくのが難しくなっています。以下に、リモートホストへの接続を行うまでのフローを示します。<br> | |
791 | + TTSSHは、Tera Termの一部のコードでもあるため、ネットワーク接続処理はTera TermとTTSSHの間を行き来することになり、処理の流れが複雑になっています。また、SSHプロトコルそのもののフローを熟知していないと、TTSSHのシーケンスを追っていくのが難しくなっています。以下に、リモートホストへの接続を行うまでのフローを示します。<br> | |
792 | 792 | |
793 | 793 | <div align="center"> |
794 | 794 | <img src="image/ssh.png" width=720 height=540> |
@@ -820,18 +820,18 @@ | ||
820 | 820 | } |
821 | 821 | </pre> |
822 | 822 | |
823 | - SSH通信に載せられて、実際にパケットが送出されるのは、finish_send_packet()から呼び出される finish_send_packet_special() です。パケットを送信するときのフォーマットについて、以下に示します。共通鍵暗号でパケットデータを暗号化する前に、ヘッダとフッタを付ける必要があります。<br> | |
824 | - パケットサイズはHMACを除く長さです。パケットサイズそのものはビッグエンディアン形式で、4バイト分格納しますが、その"4"バイトは含まれません。ペイロードの直後にパディングを埋めるのは、共通鍵暗号で暗号化するときに「ブロックサイズ単位」になっていなければ、アルゴリズム的に暗号化できないからです。ブロックサイズは暗号アルゴリズムにより異なり、たとえば3DES-CBCならば24バイト、AES128ならば16バイトです。<br> | |
825 | - HMAC(Keyed-Hashing for Message Authentication)は、暗号化本文に対するハッシュです。ハッシュのアルゴリズムは選択可能であり、"MD5"や"SHA-1"がよく使われています。HMACを付加することにより、「第三者によるデータの改ざん」を検出することができます。HMACは、暗号化対象となる本文を秘密鍵とシーケンス番号を加え、ハッシュ値を計算します。秘密鍵とシーケンス番号を加えることにより、第三者がデータをまるごと差し替えたとしても、送信者が生成したハッシュ値を復元することは理論上できません。<br> | |
826 | - | |
823 | + SSH通信に載せられて、実際にパケットが送出されるのは、finish_send_packet()から呼び出される finish_send_packet_special() です。パケットを送信するときのフォーマットについて、以下に示します。共通鍵暗号でパケットデータを暗号化する前に、ヘッダとフッタを付ける必要があります。<br> | |
824 | + パケットサイズはHMACを除く長さです。パケットサイズそのものはビッグエンディアン形式で、4バイト分格納しますが、その"4"バイトは含まれません。ペイロードの直後にパディングを埋めるのは、共通鍵暗号で暗号化するときに「ブロックサイズ単位」になっていなければ、アルゴリズム的に暗号化できないからです。ブロックサイズは暗号アルゴリズムにより異なり、たとえば3DES-CBCならば24バイト、AES128ならば16バイトです。<br> | |
825 | + HMAC(Keyed-Hashing for Message Authentication)は、暗号化本文に対するハッシュです。ハッシュのアルゴリズムは選択可能であり、"MD5"や"SHA-1"がよく使われています。HMACを付加することにより、「第三者によるデータの改ざん」を検出することができます。HMACは、暗号化対象となる本文を秘密鍵とシーケンス番号を加え、ハッシュ値を計算します。秘密鍵とシーケンス番号を加えることにより、第三者がデータをまるごと差し替えたとしても、送信者が生成したハッシュ値を復元することは理論上できません。<br> | |
826 | + | |
827 | 827 | |
828 | 828 | <div align="center"> |
829 | 829 | <img src="image/ssh_packet_format1.png" width=720 height=540> |
830 | 830 | </div> |
831 | 831 | |
832 | - zlibによるパケット圧縮を行う場合における、パケットを送信するときのフォーマットについて、以下に示します。パケット圧縮を行うのは、「ペイロード」の部分のみで、残りは通常の送信パケットとフォーマットは同じです。なお、パケットを圧縮したとしても、かならずしも元のサイズよりも小さくなるとは限らないので、そのことを考慮したバッファ管理が必要です。<br> | |
833 | - パケット圧縮送信で難しいのは、圧縮を開始するタイミングです。ローカルホストからリモートホストへのSSH接続を開始すると、実にたくさんのネゴシエーションが行われますが、パケットを圧縮してよいのは決められたタイミングであり、このタイミングを間違えると、サーバとまったく通信ができなくなります。<br> | |
834 | - 通常のパケット圧縮の場合は、"SSH2_MSG_KEXINIT"を受信したタイミングです。遅延パケット圧縮の場合は、ユーザ認証が成功したタイミング("SSH2_MSG_USERAUTH_SUCCESS"を受信した時)です。遅延パケット圧縮というのは、それまで"SSH2_MSG_KEXINIT"を受信したタイミングで圧縮を開始していたのを、ユーザ認証が完了するまで延長する方式です。遅延パケット圧縮は、zlibライブラリのセキュリティホールにより、不正なSSHサーバへ接続しただけで、クライアント側に影響が出るのを回避するためのしくみです。 | |
832 | + zlibによるパケット圧縮を行う場合における、パケットを送信するときのフォーマットについて、以下に示します。パケット圧縮を行うのは、「ペイロード」の部分のみで、残りは通常の送信パケットとフォーマットは同じです。なお、パケットを圧縮したとしても、かならずしも元のサイズよりも小さくなるとは限らないので、そのことを考慮したバッファ管理が必要です。<br> | |
833 | + パケット圧縮送信で難しいのは、圧縮を開始するタイミングです。ローカルホストからリモートホストへのSSH接続を開始すると、実にたくさんのネゴシエーションが行われますが、パケットを圧縮してよいのは決められたタイミングであり、このタイミングを間違えると、サーバとまったく通信ができなくなります。<br> | |
834 | + 通常のパケット圧縮の場合は、"SSH2_MSG_KEXINIT"を受信したタイミングです。遅延パケット圧縮の場合は、ユーザ認証が成功したタイミング("SSH2_MSG_USERAUTH_SUCCESS"を受信した時)です。遅延パケット圧縮というのは、それまで"SSH2_MSG_KEXINIT"を受信したタイミングで圧縮を開始していたのを、ユーザ認証が完了するまで延長する方式です。遅延パケット圧縮は、zlibライブラリのセキュリティホールにより、不正なSSHサーバへ接続しただけで、クライアント側に影響が出るのを回避するためのしくみです。 | |
835 | 835 | |
836 | 836 | |
837 | 837 | <div align="center"> |
@@ -840,16 +840,16 @@ | ||
840 | 840 | |
841 | 841 | |
842 | 842 | <h3>受信パケット処理</h3> |
843 | - パケットの受信は、TeraTerm本体側からは recv ソケット関数を呼び出した場合に、それがTELNETなのかSSHなのかを意識させないような設計になっていることと、 recv ソケット関数の呼び出しでは、かならずしも十分なバッファサイズが指定されてくるとは限らないため、少々実装が複雑になっています。<br> | |
843 | + パケットの受信は、TeraTerm本体側からは recv ソケット関数を呼び出した場合に、それがTELNETなのかSSHなのかを意識させないような設計になっていることと、 recv ソケット関数の呼び出しでは、かならずしも十分なバッファサイズが指定されてくるとは限らないため、少々実装が複雑になっています。<br> | |
844 | 844 | |
845 | 845 | <div align="center"> |
846 | 846 | <img src="image/ssh_recv_packet.png" width=720 height=540> |
847 | 847 | </div> |
848 | 848 | |
849 | - TeraTerm本体側は OnIdle()#teraterm.cpp というアイドルループにおいて、常時パケットの受信がないかをポーリングしています。それが CommReceive() で、recv()を呼び出します。recv()はTTSSHによりフックされているので、ソケット関数ではなく、TTXrecv()#ttxssh.c が呼び出されます。<br> | |
850 | - CommReceive()は recv() を呼び出す際に、バッファ(cv->InBuff[])の空きポインタとサイズを引数に渡します。バッファサイズは 1KB です。つまり、TTXrecv()のサイズには、1~1024 までの数値が渡される可能性があるということです。<br> | |
851 | - TTXrecv()から呼び出される PKT_recv() は、少々複雑なループ処理となっています。SSH接続を初めて行うときのシーケンスを以下に示します。 | |
852 | - | |
849 | + TeraTerm本体側は OnIdle()#teraterm.cpp というアイドルループにおいて、常時パケットの受信がないかをポーリングしています。それが CommReceive() で、recv()を呼び出します。recv()はTTSSHによりフックされているので、ソケット関数ではなく、TTXrecv()#ttxssh.c が呼び出されます。<br> | |
850 | + CommReceive()は recv() を呼び出す際に、バッファ(cv->InBuff[])の空きポインタとサイズを引数に渡します。バッファサイズは 1KB です。つまり、TTXrecv()のサイズには、1~1024 までの数値が渡される可能性があるということです。<br> | |
851 | + TTXrecv()から呼び出される PKT_recv() は、少々複雑なループ処理となっています。SSH接続を初めて行うときのシーケンスを以下に示します。 | |
852 | + | |
853 | 853 | <ol> |
854 | 854 | <li>recv_data() で本当の recv() を呼び出し、サーバからの受信パケットをカーネルから受け取る。pvar->pkt_state.datalenが更新される。 </li> |
855 | 855 | <li>SSH_handle_server_ID() でSSHサーバのバージョンチェックが行われる。pvar->pkt_state.datastart と pvar->pkt_state.datalen を更新する。</li> |
@@ -857,7 +857,7 @@ | ||
857 | 857 | <li>TeraTermの recv() は"0"で返ってくる。すなわち、受信データなし。</li> |
858 | 858 | </ol> |
859 | 859 | |
860 | - 次に、SSH通信のための共通鍵生成までのシーケンスを以下に示します。 | |
860 | + 次に、SSH通信のための共通鍵生成までのシーケンスを以下に示します。 | |
861 | 861 | |
862 | 862 | <ol> |
863 | 863 | <li>recv_data() で本当の recv() を呼び出し、サーバからの受信パケットをカーネルから受け取る。pvar->pkt_state.datalenが更新される。 </li> |
@@ -869,7 +869,7 @@ | ||
869 | 869 | <li>TeraTermの recv() は"0"で返ってくる。すなわち、受信データなし。</li> |
870 | 870 | </ol> |
871 | 871 | |
872 | - 次に、端末データ通信のシーケンスを以下に示します。 | |
872 | + 次に、端末データ通信のシーケンスを以下に示します。 | |
873 | 873 | |
874 | 874 | <ol> |
875 | 875 | <li>recv_data() で本当の recv() を呼び出し、サーバからの受信パケットをカーネルから受け取る。pvar->pkt_state.datalenが更新される。 </li> |
@@ -885,10 +885,10 @@ | ||
885 | 885 | |
886 | 886 | |
887 | 887 | <h3>シーケンス制御</h3> |
888 | - SSH2接続を行うことで、通信経路を暗号化することができるのが特徴ですが、パケットの暗号化を行うためには、「鍵」が必要です。通信経路の暗号化には、共通鍵による共通鍵暗号が利用されます。公開鍵暗号のほうがセキュリティ強度は高いのですが、暗号処理に多大な時間がかかるため、SSHのような通信性能が要求されるしくみでは採用されません。SSH2では、共通鍵暗号アルゴリズムとして、AES(Advanced Encryption Standard:Rijndaelアルゴリズム)や3DES(Triple DES)などが利用されます。<br> | |
889 | - 共通鍵は通信を行う二者間でのみに共有される情報であり、第三者に知られてはなりません。SSH2では、クライアントがリモートホスト(SSHサーバ)へTCP接続した時に、"Diffie-Hellman"アルゴリズムをベースとした独自の方式により、クライアントとサーバでしか知り得ないDH(Diffie-Hellman)鍵を生成します。DH鍵生成までの過程は、ネットワーク上をパケットが平文で流れるため、第三者によるパケットキャプチャが可能となっていますが、パケットを覗かれても、DH鍵は理論上第三者には分からないようになっています。<br> | |
890 | - 共通鍵が生成できたあとは、その鍵を使ってパケットを暗号化します。SSH2では、送受信されるパケットは種類があるため、それぞれに「メッセージ番号」を割り振っています。RFC4250にメッセージ番号の一覧があります。メッセージ名は"SSH2_MSG_xxxx"というネーミングになっており、TTSSH内部でも同じ名前でマクロ定義しています。<br> | |
891 | - 以下に、クライアントからサーバへTCP接続(ポート22番)してから、パスワード認証でユーザ認証されるまでの流れを示します。<br> | |
888 | + SSH2接続を行うことで、通信経路を暗号化することができるのが特徴ですが、パケットの暗号化を行うためには、「鍵」が必要です。通信経路の暗号化には、共通鍵による共通鍵暗号が利用されます。公開鍵暗号のほうがセキュリティ強度は高いのですが、暗号処理に多大な時間がかかるため、SSHのような通信性能が要求されるしくみでは採用されません。SSH2では、共通鍵暗号アルゴリズムとして、AES(Advanced Encryption Standard:Rijndaelアルゴリズム)や3DES(Triple DES)などが利用されます。<br> | |
889 | + 共通鍵は通信を行う二者間でのみに共有される情報であり、第三者に知られてはなりません。SSH2では、クライアントがリモートホスト(SSHサーバ)へTCP接続した時に、"Diffie-Hellman"アルゴリズムをベースとした独自の方式により、クライアントとサーバでしか知り得ないDH(Diffie-Hellman)鍵を生成します。DH鍵生成までの過程は、ネットワーク上をパケットが平文で流れるため、第三者によるパケットキャプチャが可能となっていますが、パケットを覗かれても、DH鍵は理論上第三者には分からないようになっています。<br> | |
890 | + 共通鍵が生成できたあとは、その鍵を使ってパケットを暗号化します。SSH2では、送受信されるパケットは種類があるため、それぞれに「メッセージ番号」を割り振っています。RFC4250にメッセージ番号の一覧があります。メッセージ名は"SSH2_MSG_xxxx"というネーミングになっており、TTSSH内部でも同じ名前でマクロ定義しています。<br> | |
891 | + 以下に、クライアントからサーバへTCP接続(ポート22番)してから、パスワード認証でユーザ認証されるまでの流れを示します。<br> | |
892 | 892 | |
893 | 893 | |
894 | 894 | <div align="center"> |
@@ -899,13 +899,13 @@ | ||
899 | 899 | <img src="image/ssh2_sequence2.png" width=720 height=540> |
900 | 900 | </div> |
901 | 901 | |
902 | - 以下は、リモートホストのシェル上で"exit"や"logout"として、クライアントから明示的にシェルをクローズするときの、パケットの流れを示しています。<br> | |
902 | + 以下は、リモートホストのシェル上で"exit"や"logout"として、クライアントから明示的にシェルをクローズするときの、パケットの流れを示しています。<br> | |
903 | 903 | |
904 | 904 | <div align="center"> |
905 | 905 | <img src="image/ssh2_sequence3.png" width=720 height=540> |
906 | 906 | </div> |
907 | 907 | |
908 | - TTSSHは、SSH2でパスワード認証のほかにkeyboard-interactive認証、publickey認証、Pageantを利用したpublickey認証をサポートしています。それぞれの認証方式でどのようなシーケンスで認証が行われるのか、以下に示します。 | |
908 | + TTSSHは、SSH2でパスワード認証のほかにkeyboard-interactive認証、publickey認証、Pageantを利用したpublickey認証をサポートしています。それぞれの認証方式でどのようなシーケンスで認証が行われるのか、以下に示します。 | |
909 | 909 | |
910 | 910 | <div align="center"> |
911 | 911 | <img src="image/ssh2_auth1.png" width=720 height=540> |
@@ -917,83 +917,83 @@ | ||
917 | 917 | |
918 | 918 | |
919 | 919 | <h3>疑似端末のしくみ</h3> |
920 | - SSH2では、新しく「フロー制御」という概念が取り込まれています。TCPのウィンドウと同じ考え方で、「ウィンドウサイズ」というしくみを導入しています。この機能により、クライアント(Tera Term)とサーバ(SSHデーモン)間において、フロー制御が働くため、原則データが溢れることはありません。<br> | |
921 | - ところで、SSH2におけるフロー制御があるにも関わらず、大量のクリップボードをTeraTermの端末へペーストすると、サーバ側での「データの取りこぼし」が発生することがあります。この現象を理解するためには、UNIXにおける疑似端末(PTY: pseudo-terminal)の動作原理を知る必要があります。 | |
920 | + SSH2では、新しく「フロー制御」という概念が取り込まれています。TCPのウィンドウと同じ考え方で、「ウィンドウサイズ」というしくみを導入しています。この機能により、クライアント(Tera Term)とサーバ(SSHデーモン)間において、フロー制御が働くため、原則データが溢れることはありません。<br> | |
921 | + ところで、SSH2におけるフロー制御があるにも関わらず、大量のクリップボードをTeraTermの端末へペーストすると、サーバ側での「データの取りこぼし」が発生することがあります。この現象を理解するためには、UNIXにおける疑似端末(PTY: pseudo-terminal)の動作原理を知る必要があります。 | |
922 | 922 | |
923 | 923 | <div align="center"> |
924 | 924 | <img src="image/pty.png" width=720 height=540> |
925 | 925 | </div> |
926 | 926 | |
927 | - SSHデーモン(sshd)はクライアントに対して、あたかもサーバ側のシェルが直接接続されているかのように見せる必要があります。逆に、シェル上で動くプログラムは、文字を送りたいときは printf(3) を、文字を受け取りたい場合は scanf(3) といったCライブラリ関数を呼び出すだけでよく、その先がシリアルコンソールなのか、VGAコンソールなのか、SSH接続されているのかは、一切気にしなくてよいようになっています。<br> | |
928 | - sshdは、クライアントからの接続要求があったタイミングで、openpty(3)を使って、疑似端末の初期化を行います。疑似端末では、カーネル空間でクライアントとサーバをつなぐために、「マスターデバイスドライバ」と「スレーブデバイスドライバ」が用意されます。マスターデバイスドライバが担当するデバイスファイルは"/dev/ptyXX"、スレーブデバイスドライバでは"/dev/ttyXX"です。つまり、sshdはマスターデバイスドライバへアクセスすることで、シェルとお話をすることができます。シェルは、sshdからforkされて子プロセスとなり、親プロセス(sshd)が初期化済みのスレーブデバイスドライバとお話をすることになります。この疑似端末のしくみにより、sshdとシェルが接続されます。<br> | |
929 | - なお、端末ラインディシプリン(line discipline: 回線規約)というのは、たとえばプログラムが getchar() を呼び出したときに、Enterキーを押下するまで、プログラムに制御が渡りません。端末ラインディシプリンは、プログラム実行中での「行内編集」を可能とするためのモジュールです。Linuxでは、端末ラインディシプリンは /proc/tty/ldiscs で確認できます(N_TTYが標準的に利用される)。 | |
927 | + SSHデーモン(sshd)はクライアントに対して、あたかもサーバ側のシェルが直接接続されているかのように見せる必要があります。逆に、シェル上で動くプログラムは、文字を送りたいときは printf(3) を、文字を受け取りたい場合は scanf(3) といったCライブラリ関数を呼び出すだけでよく、その先がシリアルコンソールなのか、VGAコンソールなのか、SSH接続されているのかは、一切気にしなくてよいようになっています。<br> | |
928 | + sshdは、クライアントからの接続要求があったタイミングで、openpty(3)を使って、疑似端末の初期化を行います。疑似端末では、カーネル空間でクライアントとサーバをつなぐために、「マスターデバイスドライバ」と「スレーブデバイスドライバ」が用意されます。マスターデバイスドライバが担当するデバイスファイルは"/dev/ptyXX"、スレーブデバイスドライバでは"/dev/ttyXX"です。つまり、sshdはマスターデバイスドライバへアクセスすることで、シェルとお話をすることができます。シェルは、sshdからforkされて子プロセスとなり、親プロセス(sshd)が初期化済みのスレーブデバイスドライバとお話をすることになります。この疑似端末のしくみにより、sshdとシェルが接続されます。<br> | |
929 | + なお、端末ラインディシプリン(line discipline: 回線規約)というのは、たとえばプログラムが getchar() を呼び出したときに、Enterキーを押下するまで、プログラムに制御が渡りません。端末ラインディシプリンは、プログラム実行中での「行内編集」を可能とするためのモジュールです。Linuxでは、端末ラインディシプリンは /proc/tty/ldiscs で確認できます(N_TTYが標準的に利用される)。 | |
930 | 930 | |
931 | 931 | |
932 | 932 | <h3>SCP(Secure Copy)</h3> |
933 | - SCPは OpenSSH パッケージに含まれるプログラムの1つであり、SSHセッションを使ってファイルの送受信を行うことができます。SCPを利用するためには、リモートサーバに"sshd"だけではなく、"scp"コマンドも導入されている必要があります。OpenSSHのSCPは、sshd デーモンから"scp"コマンドが子プロセスとして起動されることで実現されています。なお、SCPとSFTP(Secure File Transfer Program)はまったく別のプロトコルで、互換性はなく、SCPは純粋にファイルの「送信」と「受信」しかできません。<br> | |
934 | - SSHセッション上でファイル転送を行うには、クライアントからサーバへ接続が成功したあとに、シェルオープン(pty-req)の代わりに、「外部コマンドの実行」(exec)という形式で、SCPが利用できるようになります。 | |
935 | - | |
936 | - <p><font size=3>・SSH2の場合</font></p> | |
937 | - SSH2_MSG_CHANNEL_REQUEST をサーバへ送るときに、"pty-req"の代わりに"exec"をサービス名として指定すると、外部コマンドを実行することができます。 | |
933 | + SCPは OpenSSH パッケージに含まれるプログラムの1つであり、SSHセッションを使ってファイルの送受信を行うことができます。SCPを利用するためには、リモートサーバに"sshd"だけではなく、"scp"コマンドも導入されている必要があります。OpenSSHのSCPは、sshd デーモンから"scp"コマンドが子プロセスとして起動されることで実現されています。なお、SCPとSFTP(Secure File Transfer Program)はまったく別のプロトコルで、互換性はなく、SCPは純粋にファイルの「送信」と「受信」しかできません。<br> | |
934 | + SSHセッション上でファイル転送を行うには、クライアントからサーバへ接続が成功したあとに、シェルオープン(pty-req)の代わりに、「外部コマンドの実行」(exec)という形式で、SCPが利用できるようになります。 | |
935 | + | |
936 | + <p><font size=3>・SSH2の場合</font></p> | |
937 | + SSH2_MSG_CHANNEL_REQUEST をサーバへ送るときに、"pty-req"の代わりに"exec"をサービス名として指定すると、外部コマンドを実行することができます。 | |
938 | 938 | <pre> |
939 | - ユーザ認証成功後 | |
940 | - ----> SSH2_MSG_CHANNEL_OPEN(90) | |
941 | - <---- SSH2_MSG_CHANNEL_OPEN_CONFIRMATION(91) | |
942 | - ----> SSH2_MSG_CHANNEL_REQUEST(98) サービス名"exec"で外部コマンド送信("scp -f") | |
943 | - <---- SSH2_MSG_CHANNEL_WINDOW_ADJUST (remote_window+=131072バイト) | |
944 | - <---- SSH2_MSG_CHANNEL_EXTENDED_DATA (local_window-=36バイト) | |
945 | - <---- SSH2_MSG_CHANNEL_DATA(94) | |
939 | + ユーザ認証成功後 | |
940 | + ----> SSH2_MSG_CHANNEL_OPEN(90) | |
941 | + <---- SSH2_MSG_CHANNEL_OPEN_CONFIRMATION(91) | |
942 | + ----> SSH2_MSG_CHANNEL_REQUEST(98) サービス名"exec"で外部コマンド送信("scp -f") | |
943 | + <---- SSH2_MSG_CHANNEL_WINDOW_ADJUST (remote_window+=131072バイト) | |
944 | + <---- SSH2_MSG_CHANNEL_EXTENDED_DATA (local_window-=36バイト) | |
945 | + <---- SSH2_MSG_CHANNEL_DATA(94) | |
946 | 946 | </pre> |
947 | 947 | |
948 | - <p><font size=3>・SSH1の場合</font></p> | |
949 | - セッションを開くときに、SSH_CMSG_EXEC_CMD をサーバへ送ると、外部コマンドを実行することができます。 | |
950 | - | |
951 | - <p><font size=3>・外部コマンドの書式</font></p> | |
948 | + <p><font size=3>・SSH1の場合</font></p> | |
949 | + セッションを開くときに、SSH_CMSG_EXEC_CMD をサーバへ送ると、外部コマンドを実行することができます。 | |
950 | + | |
951 | + <p><font size=3>・外部コマンドの書式</font></p> | |
952 | 952 | <pre> |
953 | - * "scp [-v] [-r] [-p] [-d] -t ファイル名" ローカルからリモートへのコピー | |
954 | - * "scp [-v] [-r] [-p] [-d] -f ファイル名" リモートからローカルへのコピー | |
955 | - -v verbose | |
956 | - -r リカーシブ | |
957 | - -p タイムスタンプ保持 | |
958 | - -d ディレクトリ | |
959 | - -t Local-to-Remoteへコピー | |
960 | - -f Remote-to-Localへコピー | |
953 | + * "scp [-v] [-r] [-p] [-d] -t ファイル名" ローカルからリモートへのコピー | |
954 | + * "scp [-v] [-r] [-p] [-d] -f ファイル名" リモートからローカルへのコピー | |
955 | + -v verbose | |
956 | + -r リカーシブ | |
957 | + -p タイムスタンプ保持 | |
958 | + -d ディレクトリ | |
959 | + -t Local-to-Remoteへコピー | |
960 | + -f Remote-to-Localへコピー | |
961 | 961 | </pre> |
962 | 962 | |
963 | - <p><font size=3>・データ転送</font></p> | |
964 | - 外部コマンドの送信が完了したあとに、ファイルの内容を送信および受信することができます。 | |
963 | + <p><font size=3>・データ転送</font></p> | |
964 | + 外部コマンドの送信が完了したあとに、ファイルの内容を送信および受信することができます。 | |
965 | 965 | <pre> |
966 | - 1.送信の流れ | |
967 | - ・"Tタイムスタンプ 0 タイムスタンプ 0"を送信(オプション) | |
968 | - ・"C0666 サイズ ファイル名"を送信 | |
969 | - ・ファイルの内容を送信 | |
970 | - ・セッションクローズ | |
966 | + 1.送信の流れ | |
967 | + ・"Tタイムスタンプ 0 タイムスタンプ 0"を送信(オプション) | |
968 | + ・"C0666 サイズ ファイル名"を送信 | |
969 | + ・ファイルの内容を送信 | |
970 | + ・セッションクローズ | |
971 | 971 | |
972 | - 2.受信の流れ | |
973 | - ・"Tタイムスタンプ 0 タイムスタンプ 0"を受信(オプション) | |
974 | - ・0を送信 | |
975 | - ・"C0666 サイズ ファイル名"を受信 | |
976 | - ・0を送信 | |
977 | - ・ファイルの内容を受信 | |
978 | - ・ファイルのタイムスタンプを設定(オプション) | |
979 | - ・0を送信 | |
980 | - ・セッションクローズ | |
972 | + 2.受信の流れ | |
973 | + ・"Tタイムスタンプ 0 タイムスタンプ 0"を受信(オプション) | |
974 | + ・0を送信 | |
975 | + ・"C0666 サイズ ファイル名"を受信 | |
976 | + ・0を送信 | |
977 | + ・ファイルの内容を受信 | |
978 | + ・ファイルのタイムスタンプを設定(オプション) | |
979 | + ・0を送信 | |
980 | + ・セッションクローズ | |
981 | 981 | </pre> |
982 | 982 | |
983 | - <p><font size=3>・注意事項</font></p> | |
984 | - ファイル名にディレクトリが含まれるときは、パスの区切りは「/」となります。「\」は受け付けないので、変換が必要です。 | |
983 | + <p><font size=3>・注意事項</font></p> | |
984 | + ファイル名にディレクトリが含まれるときは、パスの区切りは「/」となります。「\」は受け付けないので、変換が必要です。 | |
985 | 985 | |
986 | 986 | |
987 | 987 | |
988 | 988 | <h3>X11転送</h3> |
989 | - X11転送(X11 port forwarding)は、SSHサーバ上でXウィンドウアプリケーションを起動し、アプリケーションのGUI画面をTera Termが動作しているコンソールPCに飛ばすしくみです。このしくみを使うと、SSHセッション上で"xeyes"や"firefox"、"xemacs"などのソフトウェアを動かすことができるようになります。なお、コンソールPC上には、Xming(http://sourceforge.net/projects/xming/)などのXサーバをあらかじめ用意しておく必要があります。<br> | |
990 | - 下図にX11転送のフローを示します。図を見ると分かるように、Tera Term(TTSSH)はXアプリケーションとXサーバをつなぐ橋渡しの役目を持ちます。このようなTera Termのことを"Redirector"や"Port forwarder"、"TCP proxy"と呼びます。 | |
989 | + X11転送(X11 port forwarding)は、SSHサーバ上でXウィンドウアプリケーションを起動し、アプリケーションのGUI画面をTera Termが動作しているコンソールPCに飛ばすしくみです。このしくみを使うと、SSHセッション上で"xeyes"や"firefox"、"xemacs"などのソフトウェアを動かすことができるようになります。なお、コンソールPC上には、Xming(http://sourceforge.net/projects/xming/)などのXサーバをあらかじめ用意しておく必要があります。<br> | |
990 | + 下図にX11転送のフローを示します。図を見ると分かるように、Tera Term(TTSSH)はXアプリケーションとXサーバをつなぐ橋渡しの役目を持ちます。このようなTera Termのことを"Redirector"や"Port forwarder"、"TCP proxy"と呼びます。 | |
991 | 991 | |
992 | 992 | <div align="center"> |
993 | 993 | <img src="image/x11forward.png" width=720 height=540> |
994 | 994 | </div> |
995 | 995 | |
996 | - X11転送を利用するためには、Tera TermおよびSSHサーバの双方に事前設定が必要です。まず、Tera Termのほうはteraterm.iniに下記の設定が必要です。 | |
996 | + X11転送を利用するためには、Tera TermおよびSSHサーバの双方に事前設定が必要です。まず、Tera Termのほうはteraterm.iniに下記の設定が必要です。 | |
997 | 997 | |
998 | 998 | <pre class=code> |
999 | 999 | [TTSSH] |
@@ -1000,13 +1000,13 @@ | ||
1000 | 1000 | DefaultForwarding=X |
1001 | 1001 | </pre> |
1002 | 1002 | |
1003 | - SSHサーバのほうは、OpenSSHを例に挙げると、"sshd_config"に下記の設定が必要です。デフォルトは"no"になっているため、通常はデフォルトではX11転送が使えません。 | |
1003 | + SSHサーバのほうは、OpenSSHを例に挙げると、"sshd_config"に下記の設定が必要です。デフォルトは"no"になっているため、通常はデフォルトではX11転送が使えません。 | |
1004 | 1004 | |
1005 | 1005 | <pre class=code> |
1006 | 1006 | X11Forwarding=yes |
1007 | 1007 | </pre> |
1008 | 1008 | |
1009 | - Tera TermはX11転送が有効であると、spec.typeに"FWD_REMOTE_X11_TO_LOCAL"を設定します。これはSSHサーバ側からTera Term側に向かって、X11転送を行うことを意味します。Tera Termは、リモートホストにSSH接続する際、セッションオープン後の"SSH2_MSG_CHANNEL_OPEN_CONFIRMATION"において、X11転送の初期化を行います。 | |
1009 | + Tera TermはX11転送が有効であると、spec.typeに"FWD_REMOTE_X11_TO_LOCAL"を設定します。これはSSHサーバ側からTera Term側に向かって、X11転送を行うことを意味します。Tera Termは、リモートホストにSSH接続する際、セッションオープン後の"SSH2_MSG_CHANNEL_OPEN_CONFIRMATION"において、X11転送の初期化を行います。 | |
1010 | 1010 | |
1011 | 1011 | <pre class=code> |
1012 | 1012 | if (c->type == TYPE_SHELL) { |
@@ -1017,7 +1017,7 @@ | ||
1017 | 1017 | } |
1018 | 1018 | </pre> |
1019 | 1019 | |
1020 | - FWD_prep_forwarding()では、"x11-req"サービス名と"MIT-MAGIC-COOKIE-1"をSSHサーバに送信し、SSHサーバ側のX11転送の初期化を促します。SSH接続時にX11の初期化が完了すると、SSHサーバ側に環境変数"DISPLAY"が自動的に設定されます。 | |
1020 | + FWD_prep_forwarding()では、"x11-req"サービス名と"MIT-MAGIC-COOKIE-1"をSSHサーバに送信し、SSHサーバ側のX11転送の初期化を促します。SSH接続時にX11の初期化が完了すると、SSHサーバ側に環境変数"DISPLAY"が自動的に設定されます。 | |
1021 | 1021 | |
1022 | 1022 | <pre class=code> |
1023 | 1023 | # echo $DISPLAY |
@@ -1024,8 +1024,8 @@ | ||
1024 | 1024 | DISPLAY=localhost:10.0 |
1025 | 1025 | </pre> |
1026 | 1026 | |
1027 | - ここまで準備が整うと、SSHサーバ上でXアプリケーションを起動させることができます。XアプリケーションからXサーバ、すなわちSSHサーバからTera Termへ送られてくるデータは、SSH2_MSG_CHANNEL_DATA メッセージ形式となります。当該メッセージは FWD_received_data() で処理され、Xサーバ(TCP/6000)へ送られます。Xサーバのソケットは channel->local_socket で、ノンブロッキングモードで扱われます。そのため、一度でパケットを全部送れない場合があるため、送れなかったデータは内部バッファに溜めておく必要があります。また、channel->local_socket にパケットをsendすることにより、FD_WRITE メッセージが発生し、write_local_connection_buffer() が呼び出されます。ここでは、前回送れなかったデータがあれば、内部バッファから取り出し、再度Xサーバへの送信を試みます。<br> | |
1028 | - 反対に、Xサーバ、すなわちX11の画面上で何らかの操作が行われた場合、Tera TermからSSHサーバにデータを送信する必要があります。このとき、Tera Termへは FD_READ メッセージが発生し、read_local_connection() が呼び出されます。ここでは、Xサーバから送られてきたデータを SSH2_MSG_CHANNEL_DATA メッセージ形式に載せて、SSHサーバへ送ります。 | |
1027 | + ここまで準備が整うと、SSHサーバ上でXアプリケーションを起動させることができます。XアプリケーションからXサーバ、すなわちSSHサーバからTera Termへ送られてくるデータは、SSH2_MSG_CHANNEL_DATA メッセージ形式となります。当該メッセージは FWD_received_data() で処理され、Xサーバ(TCP/6000)へ送られます。Xサーバのソケットは channel->local_socket で、ノンブロッキングモードで扱われます。そのため、一度でパケットを全部送れない場合があるため、送れなかったデータは内部バッファに溜めておく必要があります。また、channel->local_socket にパケットをsendすることにより、FD_WRITE メッセージが発生し、write_local_connection_buffer() が呼び出されます。ここでは、前回送れなかったデータがあれば、内部バッファから取り出し、再度Xサーバへの送信を試みます。<br> | |
1028 | + 反対に、Xサーバ、すなわちX11の画面上で何らかの操作が行われた場合、Tera TermからSSHサーバにデータを送信する必要があります。このとき、Tera Termへは FD_READ メッセージが発生し、read_local_connection() が呼び出されます。ここでは、Xサーバから送られてきたデータを SSH2_MSG_CHANNEL_DATA メッセージ形式に載せて、SSHサーバへ送ります。 | |
1029 | 1029 | |
1030 | 1030 | <hr> |
1031 | 1031 |
@@ -1032,17 +1032,17 @@ | ||
1032 | 1032 | |
1033 | 1033 | <h2><a name="macro">マクロ言語の設計と実装</a></h2> |
1034 | 1034 | <h3>概要</h3> |
1035 | - Tera Termのマクロスクリプトは、BASIC風の言語仕様となっています。BisonやFlexといったしくみは利用しておらず、力業的な独自の構文解析(再帰的下降法)により実装されています。そのため、本格的なスクリプト言語としての記述はできない側面があります。<br> | |
1036 | - | |
1035 | + Tera Termのマクロスクリプトは、BASIC風の言語仕様となっています。BisonやFlexといったしくみは利用しておらず、力業的な独自の構文解析(再帰的下降法)により実装されています。そのため、本格的なスクリプト言語としての記述はできない側面があります。<br> | |
1036 | + | |
1037 | 1037 | <h3>ファイルの読み込み</h3> |
1038 | - ttpmacro.exeの起動時に、マクロファイル(.ttl)が一括してバッファへ読み込まれます。 | |
1039 | - | |
1038 | + ttpmacro.exeの起動時に、マクロファイル(.ttl)が一括してバッファへ読み込まれます。 | |
1039 | + | |
1040 | 1040 | <p><ul> |
1041 | 1041 | <li>OnInitDialog()#ttmmain.cpp -> InitTTL() -> InitBuff() -> LoadMacroFile()</li> |
1042 | 1042 | </ul></p> |
1043 | 1043 | |
1044 | - 初めて読み込まれるマクロファイルの全内容は Buff[0] # ttmbuff.c に格納されます。この時点で、ファイルの内容は一括して読み込まれるため、マクロ実行中はファイルを削除してしまっても問題はありません。ただし、"include"で別のファイルを読み込む場合は、includeを実行する時点で、include対象となるファイルの読み込みが発生します。 | |
1045 | - | |
1044 | + 初めて読み込まれるマクロファイルの全内容は Buff[0] # ttmbuff.c に格納されます。この時点で、ファイルの内容は一括して読み込まれるため、マクロ実行中はファイルを削除してしまっても問題はありません。ただし、"include"で別のファイルを読み込む場合は、includeを実行する時点で、include対象となるファイルの読み込みが発生します。 | |
1045 | + | |
1046 | 1046 | <pre class=code> |
1047 | 1047 | #define MAXNESTLEVEL 10 /* 扱えるファイル数(includeは9つまで)*/ |
1048 | 1048 |
@@ -1055,7 +1055,7 @@ | ||
1055 | 1055 | |
1056 | 1056 | |
1057 | 1057 | <h3>マクロエンジン</h3> |
1058 | - マクロ処理はアイドルループ OnIdle()#ttmmain.cpp で行われます。アイドルループでは TTLStatus 変数により、マクロエンジンの動作を変えています。通常の実行状態は IdTTLRun がセットされています。以下に、動作一覧を示します。 | |
1058 | + マクロ処理はアイドルループ OnIdle()#ttmmain.cpp で行われます。アイドルループでは TTLStatus 変数により、マクロエンジンの動作を変えています。通常の実行状態は IdTTLRun がセットされています。以下に、動作一覧を示します。 | |
1059 | 1059 | |
1060 | 1060 | <p> |
1061 | 1061 | <table border=1 align=center> |
@@ -1104,7 +1104,7 @@ | ||
1104 | 1104 | |
1105 | 1105 | |
1106 | 1106 | <h3>インタープリタ処理</h3> |
1107 | - アイドルループから Exec()#ttl.c が定期的に呼び出される度に、マクロファイルが一行ずつ処理されてゆきます。GetNewLine() では、バッファから一行分を取り出し、LineBuff[]#ttmparse.c へ格納します。行の終わりかどうかは、「ASCIIコードが0x20未満で、かつタブ(0x09)以外」のコードが出現したタイミングで判定しています。先頭の空白やタブは無視されます。セミコロン(;)が出現すると、以降の処理をスキップするため、コメントは行の途中でも付けられることになります。<br> | |
1107 | + アイドルループから Exec()#ttl.c が定期的に呼び出される度に、マクロファイルが一行ずつ処理されてゆきます。GetNewLine() では、バッファから一行分を取り出し、LineBuff[]#ttmparse.c へ格納します。行の終わりかどうかは、「ASCIIコードが0x20未満で、かつタブ(0x09)以外」のコードが出現したタイミングで判定しています。先頭の空白やタブは無視されます。セミコロン(;)が出現すると、以降の処理をスキップするため、コメントは行の途中でも付けられることになります。<br> | |
1108 | 1108 | |
1109 | 1109 | <pre class=code> |
1110 | 1110 | char LineBuff[MaxLineLen]; /* 1つの行は500バイトまで格納可能 */ |
@@ -1112,7 +1112,7 @@ | ||
1112 | 1112 | WORD LineLen; /* バッファサイズ */ |
1113 | 1113 | </pre> |
1114 | 1114 | |
1115 | - Exec()から呼ばれる ExecCmnd() で、字句解析を行います。字句解析は単純な文字列検索であり、LineBuff[]を1バイトずつ参照していきます。大まかな処理の流れは以下のとおりです。 | |
1115 | + Exec()から呼ばれる ExecCmnd() で、字句解析を行います。字句解析は単純な文字列検索であり、LineBuff[]を1バイトずつ参照していきます。大まかな処理の流れは以下のとおりです。 | |
1116 | 1116 | |
1117 | 1117 | <p><ol> |
1118 | 1118 | <li>endwhileの判定</li> |
@@ -1124,19 +1124,19 @@ | ||
1124 | 1124 | <li>文法エラー(上記のいずれでもない場合)</li> |
1125 | 1125 | </ol></p> |
1126 | 1126 | |
1127 | - マクロコマンドかどうかは、GetReservedWord()で判別しています。_stricmp()で比較しているので、アルファベットの大文字・小文字は区別されません(case-insensitive)。マクロコマンドの場合は、TTLxxx() の関数を呼び出します。<br> | |
1128 | - 識別子の判定は、GetIdentifier() で行います。アルファベット(a-z, A-Z)および数値(0-9)、アンダースコア(_)から構成されるトークンを切り出します。トークンは32文字までです。トークンは「変数」として扱われます。左辺値に変数が来る場合は、「変数への代入」しかありえないので、その直後に「イコール(=)」があるかどうかを調べます。<br> | |
1129 | - イコール以降の判定処理は、以下の順番となります。 | |
1130 | - | |
1127 | + マクロコマンドかどうかは、GetReservedWord()で判別しています。_stricmp()で比較しているので、アルファベットの大文字・小文字は区別されません(case-insensitive)。マクロコマンドの場合は、TTLxxx() の関数を呼び出します。<br> | |
1128 | + 識別子の判定は、GetIdentifier() で行います。アルファベット(a-z, A-Z)および数値(0-9)、アンダースコア(_)から構成されるトークンを切り出します。トークンは32文字までです。トークンは「変数」として扱われます。左辺値に変数が来る場合は、「変数への代入」しかありえないので、その直後に「イコール(=)」があるかどうかを調べます。<br> | |
1129 | + イコール以降の判定処理は、以下の順番となります。 | |
1130 | + | |
1131 | 1131 | <p><ol> |
1132 | 1132 | <li>文字列の判定</li> |
1133 | 1133 | <li>計算式の判定</li> |
1134 | 1134 | </ol></p> |
1135 | 1135 | |
1136 | - 文字列かどうかは GetString() で判定します。文字列は’か”でクォートされているため、取り出すのは容易です。<br> | |
1137 | - 計算式の判定は、GetExpression() で行います。ここでは再帰的下降法により、構文解析されます。<br> | |
1138 | - 左辺値が定義済みの変数かどうかは CheckVar() でチェックし、数値もしくは文字列をセットします。そうではない場合は NewStrVar() で、新しい変数として登録します。 | |
1139 | - | |
1136 | + 文字列かどうかは GetString() で判定します。文字列は’か”でクォートされているため、取り出すのは容易です。<br> | |
1137 | + 計算式の判定は、GetExpression() で行います。ここでは再帰的下降法により、構文解析されます。<br> | |
1138 | + 左辺値が定義済みの変数かどうかは CheckVar() でチェックし、数値もしくは文字列をセットします。そうではない場合は NewStrVar() で、新しい変数として登録します。 | |
1139 | + | |
1140 | 1140 | |
1141 | 1141 | <hr> |
1142 | 1142 |
@@ -1144,11 +1144,11 @@ | ||
1144 | 1144 | |
1145 | 1145 | <h2><a name="caret">キャレット制御</a></h2> |
1146 | 1146 | <h3>概要</h3> |
1147 | - ユーザが端末上でキーボード入力を行うと、カーソルが移動しますが、サーバからのエスケープシーケンスにより、キーボード入力なしにカーソルを移動させる必要があります。また、ウィンドウが非アクティブ状態の場合においても、カーソルを表示させることにより、ブロードキャストモードにおいて、複数端末の同時操作性を向上させています。 | |
1147 | + ユーザが端末上でキーボード入力を行うと、カーソルが移動しますが、サーバからのエスケープシーケンスにより、キーボード入力なしにカーソルを移動させる必要があります。また、ウィンドウが非アクティブ状態の場合においても、カーソルを表示させることにより、ブロードキャストモードにおいて、複数端末の同時操作性を向上させています。 | |
1148 | 1148 | <br> |
1149 | 1149 | |
1150 | 1150 | <h3>システムキャレット</h3> |
1151 | - Tera Termにおけるカーソル描画には、システムキャレットを利用しています。Tera Termで使用されているシステムキャレットを制御するAPIを以下に示します。 | |
1151 | + Tera Termにおけるカーソル描画には、システムキャレットを利用しています。Tera Termで使用されているシステムキャレットを制御するAPIを以下に示します。 | |
1152 | 1152 | |
1153 | 1153 | <p><ul> |
1154 | 1154 | <li>CreateCaret</li> |
@@ -1160,7 +1160,7 @@ | ||
1160 | 1160 | <li>ShowCaret</li> |
1161 | 1161 | </ul></p> |
1162 | 1162 | |
1163 | - <a href="http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/jpwinui/html/_win32_createcaret.asp">CreateCaretのドキュメント</a>によると、 | |
1163 | + <a href="http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/jpwinui/html/_win32_createcaret.asp">CreateCaretのドキュメント</a>によると、 | |
1164 | 1164 | |
1165 | 1165 | <pre> |
1166 | 1166 | システムは 1 つのキューにつき 1 つのキャレットを提供します。ウィンドウが |
@@ -1170,16 +1170,16 @@ | ||
1170 | 1170 | </pre> |
1171 | 1171 | |
1172 | 1172 | とあるため、ウィンドウがアクティブになったタイミングで CreateCaret() を呼び出し、フォーカスが外れ、非アクティブになるタイミングで DestroyCaret() を呼び出す必要があることを意味しています。<br> |
1173 | - キャレットの表示は CaretOn()#vtdisp.c で、消去は CaretOff()#vtdisp.c で実装されています。CaretOn()やCaretOff()が呼び出されるタイミングは、エスケープシーケンス処理 VTParse() の箇所以外にも、マウスボタンを押したときやウィンドウのリサイズを行っているときなどがあります。<br> | |
1173 | + キャレットの表示は CaretOn()#vtdisp.c で、消去は CaretOff()#vtdisp.c で実装されています。CaretOn()やCaretOff()が呼び出されるタイミングは、エスケープシーケンス処理 VTParse() の箇所以外にも、マウスボタンを押したときやウィンドウのリサイズを行っているときなどがあります。<br> | |
1174 | 1174 | |
1175 | 1175 | |
1176 | 1176 | |
1177 | 1177 | <h3>非アクティブ時のカーソル表示</h3> |
1178 | - ウィンドウが非アクティブの場合は、カーソルが消滅します。Windowsの上ではユーザが操作できうるウィンドウは1つであるため、システムキャレットも1つのみ用意されています。通常のオペレーションにおいては、この動作で問題がありません。<br> | |
1179 | - しかし、ブロードキャストモードを利用する場合、非アクティブのTera Termウィンドウに対して、コマンドを投入することになります。特に、viなどで複数の端末を同時操作するときは、カーソルが消えていると不都合があります。<br> | |
1180 | - そこで、ウィンドウが非アクティブの場合においても、カーソルを描画するようにしています。ただし、システムキャレットは使えないので、自前でカーソルを描画する必要があります。Tera Termのウィンドウが非アクティブの場合においても、リモートホストから送られてくるエスケープシーケンスを処理するためにメインエンジンは動いており、常にカーソル位置は更新されています。現在のカーソル位置は、CursorXとCursorYに設定されています。<br> | |
1181 | - 非アクティブ時のカーソル表示は CaretKillFocus() で行っています。このときに表示されるカーソルを「ポリゴンカーソル」と呼んでいます。ts.VTColor[0] は Text color です。非アクティブ状態でカーソル位置が更新されるときは、以前に描いたカーソルを消す必要があるので、そのときは ts.VTColor[1] で表される Background color で再描画することで、以前のカーソルを消去しています。<br> | |
1182 | - Background colorでポリゴンカーソルを描画すると、ちょうどそのとき背景にあった文字の一部が欠けることがあります。そのため、その文字の再描画を行う必要があり、UpdateCaretKillFocus() で実現しています。当該関数では InvalidateRect() で WM_PAINT を送ることにより、文字の再描画を促しています。<br> | |
1178 | + ウィンドウが非アクティブの場合は、カーソルが消滅します。Windowsの上ではユーザが操作できうるウィンドウは1つであるため、システムキャレットも1つのみ用意されています。通常のオペレーションにおいては、この動作で問題がありません。<br> | |
1179 | + しかし、ブロードキャストモードを利用する場合、非アクティブのTera Termウィンドウに対して、コマンドを投入することになります。特に、viなどで複数の端末を同時操作するときは、カーソルが消えていると不都合があります。<br> | |
1180 | + そこで、ウィンドウが非アクティブの場合においても、カーソルを描画するようにしています。ただし、システムキャレットは使えないので、自前でカーソルを描画する必要があります。Tera Termのウィンドウが非アクティブの場合においても、リモートホストから送られてくるエスケープシーケンスを処理するためにメインエンジンは動いており、常にカーソル位置は更新されています。現在のカーソル位置は、CursorXとCursorYに設定されています。<br> | |
1181 | + 非アクティブ時のカーソル表示は CaretKillFocus() で行っています。このときに表示されるカーソルを「ポリゴンカーソル」と呼んでいます。ts.VTColor[0] は Text color です。非アクティブ状態でカーソル位置が更新されるときは、以前に描いたカーソルを消す必要があるので、そのときは ts.VTColor[1] で表される Background color で再描画することで、以前のカーソルを消去しています。<br> | |
1182 | + Background colorでポリゴンカーソルを描画すると、ちょうどそのとき背景にあった文字の一部が欠けることがあります。そのため、その文字の再描画を行う必要があり、UpdateCaretKillFocus() で実現しています。当該関数では InvalidateRect() で WM_PAINT を送ることにより、文字の再描画を促しています。<br> | |
1183 | 1183 | |
1184 | 1184 | <pre class=code> |
1185 | 1185 | void CaretKillFocus(BOOL show) |
@@ -1227,7 +1227,7 @@ | ||
1227 | 1227 | |
1228 | 1228 | |
1229 | 1229 | <h3>非アクティブ時のカーソル表示タイミング</h3> |
1230 | - 非アクティブ時のカーソル表示のタイミングは、いくつかのパターンがあるため、漏れなく対処しておく必要があります。表示タイミングとしては以下のとおりです。 | |
1230 | + 非アクティブ時のカーソル表示のタイミングは、いくつかのパターンがあるため、漏れなく対処しておく必要があります。表示タイミングとしては以下のとおりです。 | |
1231 | 1231 | |
1232 | 1232 | <p> |
1233 | 1233 | <ul> |
@@ -1249,11 +1249,11 @@ | ||
1249 | 1249 | |
1250 | 1250 | <h2><a name="serial">シリアルポート</a></h2> |
1251 | 1251 | <h3>概要</h3> |
1252 | - Tera TermはUART(16550A)互換のシリアルポートに対応しているため、シリアルコンソールが使用できます。シリアルポートのことを、COM(Communication Port)ポートと呼ぶこともあります。OSが検出したCOMポートは、順に"COM1"、"COM2"といった名前が付けられ、アプリケーションから利用することができます。Microsoft Windows XPでは、最大256個のCOMポート(COM1~COM256)までが利用可能です。<br> | |
1253 | - パソコンに搭載されるCOMポートは、せいぜい1つ、多くても2つであり、最近ではまったくCOMポートがないパソコンも存在します。そのため、USB接続によるシリアルポートを実現する「USBシリアル変換ケーブル」が発売されています。こういった製品の特徴として、OSに認識させるCOMポートの番号を、ユーザが自由に設定できるようになっています。すなわち、Tera Term見えには、2つのCOMポートがあった場合、それぞれ"COM1"、"COM2"として認識できるとは限らず、"COM1"、"COM7"といったふうに認識できるようになる必要があります。<br> | |
1254 | - | |
1252 | + Tera TermはUART(16550A)互換のシリアルポートに対応しているため、シリアルコンソールが使用できます。シリアルポートのことを、COM(Communication Port)ポートと呼ぶこともあります。OSが検出したCOMポートは、順に"COM1"、"COM2"といった名前が付けられ、アプリケーションから利用することができます。Microsoft Windows XPでは、最大256個のCOMポート(COM1~COM256)までが利用可能です。<br> | |
1253 | + パソコンに搭載されるCOMポートは、せいぜい1つ、多くても2つであり、最近ではまったくCOMポートがないパソコンも存在します。そのため、USB接続によるシリアルポートを実現する「USBシリアル変換ケーブル」が発売されています。こういった製品の特徴として、OSに認識させるCOMポートの番号を、ユーザが自由に設定できるようになっています。すなわち、Tera Term見えには、2つのCOMポートがあった場合、それぞれ"COM1"、"COM2"として認識できるとは限らず、"COM1"、"COM7"といったふうに認識できるようになる必要があります。<br> | |
1254 | + | |
1255 | 1255 | <h3>COMポートのリストアップ</h3> |
1256 | - かつてのTera Termでは、"COM1"から"COM256"までのすべてのCOMポートを、接続ダイアログにリストアップしていましたが、使い勝手がよくありませんでした。そこで、接続ダイアログを呼び出したタイミングにおいて(Tera Term起動時のみでは不十分)、OSが認識しているCOMポートを検出するようにして、必要なCOMポートのみを表示させるようにしました。その検出ロジックが、DetectComPorts()#ttcmn.c です。QueryDosDevice() APIを使用し、MS-DOSデバイス名から"COM"を探します。<br> | |
1256 | + かつてのTera Termでは、"COM1"から"COM256"までのすべてのCOMポートを、接続ダイアログにリストアップしていましたが、使い勝手がよくありませんでした。そこで、接続ダイアログを呼び出したタイミングにおいて(Tera Term起動時のみでは不十分)、OSが認識しているCOMポートを検出するようにして、必要なCOMポートのみを表示させるようにしました。その検出ロジックが、DetectComPorts()#ttcmn.c です。QueryDosDevice() APIを使用し、MS-DOSデバイス名から"COM"を探します。<br> | |
1257 | 1257 | |
1258 | 1258 | <pre class=code> |
1259 | 1259 | if (((h = GetModuleHandle("kernel32.dll")) != NULL) && |
@@ -1272,8 +1272,8 @@ | ||
1272 | 1272 | |
1273 | 1273 | <h3>COMポートのフルネーム取得</h3> |
1274 | 1274 | |
1275 | - 上記の処理だけでもユーザビリティは向上するのですが、さらなる欲求として、各COMポートに付けられる「フルネーム」を同時に表示したくなります。COMポートの番号とともに、フルネームも付加表示できると、さらに使い勝手がよくなることが期待されます。この課題を解決するのが、ListupSerialPort()#ttcmn.c です。<br> | |
1276 | - | |
1275 | + 上記の処理だけでもユーザビリティは向上するのですが、さらなる欲求として、各COMポートに付けられる「フルネーム」を同時に表示したくなります。COMポートの番号とともに、フルネームも付加表示できると、さらに使い勝手がよくなることが期待されます。この課題を解決するのが、ListupSerialPort()#ttcmn.c です。<br> | |
1276 | + | |
1277 | 1277 | <pre class=code> |
1278 | 1278 | static void ListupSerialPort(LPWORD ComPortTable, int comports, char **ComPortDesc, int ComPortMax) |
1279 | 1279 | { |
@@ -1466,8 +1466,8 @@ | ||
1466 | 1466 | </table> |
1467 | 1467 | |
1468 | 1468 | <h3>テスト手法</h3> |
1469 | - バイナリ転送プロトコルはシリアル回線で利用されることが多いのですが、最近のPCは、シリアルポートが搭載されていないため、シリアル接続でのテストを行うことが難しくなっています。そこで、<a href="http://com0com.sourceforge.net/">com0comというNull-modem emulator</a>を利用すると、1つのPC内で仮想的に2つのCOMポートを生成し、Tera Term同士、Tera Termと別ターミナルソフト同士で、シリアル通信を行うことができます。 | |
1470 | - | |
1469 | + バイナリ転送プロトコルはシリアル回線で利用されることが多いのですが、最近のPCは、シリアルポートが搭載されていないため、シリアル接続でのテストを行うことが難しくなっています。そこで、<a href="http://com0com.sourceforge.net/">com0comというNull-modem emulator</a>を利用すると、1つのPC内で仮想的に2つのCOMポートを生成し、Tera Term同士、Tera Termと別ターミナルソフト同士で、シリアル通信を行うことができます。 | |
1470 | + | |
1471 | 1471 | |
1472 | 1472 | <div align="center"> |
1473 | 1473 | <img src="image/devman_com0com.png" width=468 height=171> |
@@ -1475,8 +1475,8 @@ | ||
1475 | 1475 | |
1476 | 1476 | |
1477 | 1477 | <h3>記号</h3> |
1478 | - バイナリ転送プロトコルでは、ACKやCANといったキャラクタ表記が使われますが、これらはASCIIコード表から来ています。man 7 ascii でASCIIコード一覧が参照できます。以下に代表的な記号と値を引用します。 | |
1479 | - | |
1478 | + バイナリ転送プロトコルでは、ACKやCANといったキャラクタ表記が使われますが、これらはASCIIコード表から来ています。man 7 ascii でASCIIコード一覧が参照できます。以下に代表的な記号と値を引用します。 | |
1479 | + | |
1480 | 1480 | |
1481 | 1481 | <pre class=code> |
1482 | 1482 | Oct Dec Hex Char Oct Dec Hex Char |
@@ -1494,14 +1494,14 @@ | ||
1494 | 1494 | <h3>XMODEM</h3> |
1495 | 1495 | XMODEMは、ファイルのデータを一定のサイズ(128バイトおよび1024バイト)に分割し、ブロックごとにACKを確認しながら、送信を行うプロトコルです。ブロック単位で、毎回ACKを確認するため、転送速度は速くはありませんが、実装がシンプルとなります。<br> |
1496 | 1496 | 最後のブロックが一定のサイズに満たない場合は、満たすようにCPMEOF(0x1A)がパディングされます。すなわち、データを送ると、かならずデータのサイズが一定のサイズの倍数となり、末尾にCPMEOFが付加される場合があるということです。そのため、ファイルの送信に完全性を求める場合は、XMOMDEは使えません。なお、CPMEOFというのは、MS-DOSの前身であるCP/MというOSにおいて、テキストファイルの終端(EOF)を表す値のことです。<br> |
1497 | - XMODEMのプロトコルについては、下記サイトが参考になります。 | |
1497 | + XMODEMのプロトコルについては、下記サイトが参考になります。 | |
1498 | 1498 | |
1499 | 1499 | <ul> |
1500 | - <li><a href="http://ja.wikipedia.org/wiki/XMODEM">XMODEM - Wikipedia</a></li> | |
1500 | + <li><a href="http://ja.wikipedia.org/wiki/XMODEM">XMODEM - Wikipedia</a></li> | |
1501 | 1501 | </ul> |
1502 | 1502 | <br> |
1503 | 1503 | |
1504 | - teraterm.ini で XmodemLog エントリを有効にすると、通信ログを採取することができます。通信ログファイルは、ttermpro.exe と同じディレクトリに"XMODEM.LOG"という名前で生成されます。 | |
1504 | + teraterm.ini で XmodemLog エントリを有効にすると、通信ログを採取することができます。通信ログファイルは、ttermpro.exe と同じディレクトリに"XMODEM.LOG"という名前で生成されます。 | |
1505 | 1505 | |
1506 | 1506 | <pre class=code> |
1507 | 1507 | ; XMODEM log |
@@ -1508,8 +1508,8 @@ | ||
1508 | 1508 | XmodemLog=on |
1509 | 1509 | </pre> |
1510 | 1510 | |
1511 | - 簡単な例として、Tera Term(COM10)から<a href="http://nanno.dip.jp/softlib/man/rlogin/">RLogin</a>(COM11)に対して、67バイトのファイルを送信した場合の通信ログを示します。「<<<」行はTera Termがホストから受信したデータで、「>>>」行はTera Termが送信したデータです。 | |
1512 | - | |
1511 | + 簡単な例として、Tera Term(COM10)から<a href="http://nanno.dip.jp/softlib/man/rlogin/">RLogin</a>(COM11)に対して、67バイトのファイルを送信した場合の通信ログを示します。「<<<」行はTera Termがホストから受信したデータで、「>>>」行はTera Termが送信したデータです。 | |
1512 | + | |
1513 | 1513 | <pre class=code> |
1514 | 1514 | <<< |
1515 | 1515 | 15 . |
@@ -1535,8 +1535,8 @@ | ||
1535 | 1535 | 06 |
1536 | 1536 | </pre> |
1537 | 1537 | |
1538 | - 上記ログの意味は以下のとおりです。 | |
1539 | - | |
1538 | + 上記ログの意味は以下のとおりです。 | |
1539 | + | |
1540 | 1540 | <ol> |
1541 | 1541 | <li>NAK受信</li> |
1542 | 1542 | <li>ブロックデータの送信</li> |
@@ -1545,12 +1545,12 @@ | ||
1545 | 1545 | <li>ACK受信</li> |
1546 | 1546 | </ol> |
1547 | 1547 | |
1548 | - ブロックデータは、「ヘッダ(3byte)+データ(128byte)+CRC(1byte)」から構成されます。この例では、送信データが128byte未満なので、パディングとしてCPMEOF(0x1A)で埋められています。 | |
1548 | + ブロックデータは、「ヘッダ(3byte)+データ(128byte)+CRC(1byte)」から構成されます。この例では、送信データが128byte未満なので、パディングとしてCPMEOF(0x1A)で埋められています。 | |
1549 | 1549 | |
1550 | 1550 | <p></p> |
1551 | 1551 | |
1552 | - 次はもう少し大きめのサイズ(1772byte)のデータを送信してみます。(1772+127)/128=14 で、14回ブロック転送されるはずです。下記に通信ログを示します。ヘッダの第2バイトがブロック番号(1オリジン)であり、1(0x01)~14(0x0E)まで増加していることが分かります。最後のブロックは128バイトに収まるように、CPMEOFがパディングとして付加されています。<br> | |
1553 | - このようにXMODEMによる通信では、送信後ファイルの末尾にゴミが付いたように見えるので、厳密にはファイルが壊れます。 | |
1552 | + 次はもう少し大きめのサイズ(1772byte)のデータを送信してみます。(1772+127)/128=14 で、14回ブロック転送されるはずです。下記に通信ログを示します。ヘッダの第2バイトがブロック番号(1オリジン)であり、1(0x01)~14(0x0E)まで増加していることが分かります。最後のブロックは128バイトに収まるように、CPMEOFがパディングとして付加されています。<br> | |
1553 | + このようにXMODEMによる通信では、送信後ファイルの末尾にゴミが付いたように見えるので、厳密にはファイルが壊れます。 | |
1554 | 1554 | |
1555 | 1555 | <pre class=code> |
1556 | 1556 | <<< |
@@ -1625,16 +1625,16 @@ | ||
1625 | 1625 | |
1626 | 1626 | |
1627 | 1627 | <h3>YMODEM</h3> |
1628 | - YMODEMは、XMODEMを改良したプロトコルです。XMODEMとの違いはいくつかありますが、大きな違いの1つとして、ファイル情報が送れるということです。YMODEMでは、ファイル名やファイルサイズをホストに知らせることができるので、送信したファイルの末尾からCPMEOFを除去することができます。<br> | |
1628 | + YMODEMは、XMODEMを改良したプロトコルです。XMODEMとの違いはいくつかありますが、大きな違いの1つとして、ファイル情報が送れるということです。YMODEMでは、ファイル名やファイルサイズをホストに知らせることができるので、送信したファイルの末尾からCPMEOFを除去することができます。<br> | |
1629 | 1629 | |
1630 | - YMODEMのプロトコルについては、下記サイトが参考になります。 | |
1630 | + YMODEMのプロトコルについては、下記サイトが参考になります。 | |
1631 | 1631 | |
1632 | 1632 | <ul> |
1633 | - <li><a href="http://ja.wikipedia.org/wiki/YMODEM">YMODEM - Wikipedia</a></li> | |
1633 | + <li><a href="http://ja.wikipedia.org/wiki/YMODEM">YMODEM - Wikipedia</a></li> | |
1634 | 1634 | </ul> |
1635 | 1635 | <br> |
1636 | 1636 | |
1637 | - teraterm.ini で YmodemLog エントリを有効にすると、通信ログを採取することができます。通信ログファイルは、ttermpro.exe と同じディレクトリに"YMODEM.LOG"という名前で生成されます。 | |
1637 | + teraterm.ini で YmodemLog エントリを有効にすると、通信ログを採取することができます。通信ログファイルは、ttermpro.exe と同じディレクトリに"YMODEM.LOG"という名前で生成されます。 | |
1638 | 1638 | |
1639 | 1639 | <pre class=code> |
1640 | 1640 | ; YMODEM log |
@@ -1641,8 +1641,8 @@ | ||
1641 | 1641 | YmodemLog=on |
1642 | 1642 | </pre> |
1643 | 1643 | |
1644 | - 簡単な例として、Tera Term(COM10)から<a href="http://nanno.dip.jp/softlib/man/rlogin/">RLogin</a>(COM11)に対して、67バイトのファイルを送信した場合の通信ログを示します。「<<<」行はTera Termがホストから受信したデータで、「>>>」行はTera Termが送信したデータです。 | |
1645 | - | |
1644 | + 簡単な例として、Tera Term(COM10)から<a href="http://nanno.dip.jp/softlib/man/rlogin/">RLogin</a>(COM11)に対して、67バイトのファイルを送信した場合の通信ログを示します。「<<<」行はTera Termがホストから受信したデータで、「>>>」行はTera Termが送信したデータです。 | |
1645 | + | |
1646 | 1646 | <pre class=code> |
1647 | 1647 | <<< |
1648 | 1648 | 43 C |
@@ -1864,8 +1864,8 @@ | ||
1864 | 1864 | 06 |
1865 | 1865 | </pre> |
1866 | 1866 | |
1867 | - 上記ログの意味は以下のとおりです。 | |
1868 | - | |
1867 | + 上記ログの意味は以下のとおりです。 | |
1868 | + | |
1869 | 1869 | <ol> |
1870 | 1870 | <li>'C'(送信要求)受信</li> |
1871 | 1871 | <li>ブロック0(ファイル情報)の送信</li> |
@@ -1889,24 +1889,24 @@ | ||
1889 | 1889 | |
1890 | 1890 | |
1891 | 1891 | <h3>KERMIT</h3> |
1892 | - KERMIT(カーミット:セサミストリートに登場するカエルのマペット)は、1981年にコロンビア大学で開発されたファイル転送プロトコルであり、現在はカーミットプロジェクトによりメンテナンスされています。下記のサイトから仕様書が入手できます。<br> | |
1893 | - | |
1892 | + KERMIT(カーミット:セサミストリートに登場するカエルのマペット)は、1981年にコロンビア大学で開発されたファイル転送プロトコルであり、現在はカーミットプロジェクトによりメンテナンスされています。下記のサイトから仕様書が入手できます。<br> | |
1893 | + | |
1894 | 1894 | <ul> |
1895 | 1895 | <li><a href="http://www.kermitproject.org/">The Kermit Project</a></li> |
1896 | 1896 | </ul> |
1897 | 1897 | <br> |
1898 | 1898 | |
1899 | - 上記サイトでは、ソースコードも配布されており、様々なプラットフォームに実装されています。実装の名称としては、C-KermitやE-Kermit、Kermit95などがあります。<br> | |
1900 | - teraterm.ini で KmtLog エントリを有効にすると、通信ログを採取することができます。通信ログファイルは、ttermpro.exe と同じディレクトリに"KERMIT.LOG"という名前で生成されます。 | |
1901 | - | |
1899 | + 上記サイトでは、ソースコードも配布されており、様々なプラットフォームに実装されています。実装の名称としては、C-KermitやE-Kermit、Kermit95などがあります。<br> | |
1900 | + teraterm.ini で KmtLog エントリを有効にすると、通信ログを採取することができます。通信ログファイルは、ttermpro.exe と同じディレクトリに"KERMIT.LOG"という名前で生成されます。 | |
1901 | + | |
1902 | 1902 | <pre class=code> |
1903 | 1903 | ; Kermit log |
1904 | 1904 | KmtLog=on |
1905 | 1905 | </pre> |
1906 | 1906 | |
1907 | - KERMITは元々低速なシリアル通信を想定しているため、一度に送れるデータサイズはせいぜい94バイトです。ただし、拡張オプションで数千バイトのデータを扱うことはできますが、クライアントとサーバの両方が当該機能をサポートしている必要があります。<br> | |
1908 | - パケットのフォーマットは基本形式と拡張形式があり、仕様書の「Appendix I Packet Format and Types」に分かりやすい図解があります。下記に引用します。<br> | |
1909 | - 以下は基本形式です。94バイトまでしか扱えません。 | |
1907 | + KERMITは元々低速なシリアル通信を想定しているため、一度に送れるデータサイズはせいぜい94バイトです。ただし、拡張オプションで数千バイトのデータを扱うことはできますが、クライアントとサーバの両方が当該機能をサポートしている必要があります。<br> | |
1908 | + パケットのフォーマットは基本形式と拡張形式があり、仕様書の「Appendix I Packet Format and Types」に分かりやすい図解があります。下記に引用します。<br> | |
1909 | + 以下は基本形式です。94バイトまでしか扱えません。 | |
1910 | 1910 | |
1911 | 1911 | <pre class=code> |
1912 | 1912 | Basic Kermit Packet Layout |
@@ -1926,7 +1926,7 @@ | ||
1926 | 1926 | CHECK 加算チェックサム。1,2,3バイトのいずれかの形式を選べる。 |
1927 | 1927 | </pre> |
1928 | 1928 | |
1929 | - 以下は拡張形式です。 | |
1929 | + 以下は拡張形式です。 | |
1930 | 1930 | |
1931 | 1931 | <pre class=code> |
1932 | 1932 | Kermit Extended Packet Layout |
@@ -1943,11 +1943,11 @@ | ||
1943 | 1943 | HCHECK is a single-character type 1 checksum |
1944 | 1944 | </pre> |
1945 | 1945 | |
1946 | - 拡張形式では94バイト以上のデータを一度に送れるようにするため、データサイズを表現する領域が2バイトに増えています。基本形式の"LEN"は常にゼロです(32を加算するので、ASCIIコードの空白になる)。また、ヘッダサイズが3バイト増えており、ヘッダ用のチェックサムが追加されています。<br> | |
1947 | - <br> | |
1948 | - | |
1949 | - 下記は初期文字列です。 | |
1950 | - | |
1946 | + 拡張形式では94バイト以上のデータを一度に送れるようにするため、データサイズを表現する領域が2バイトに増えています。基本形式の"LEN"は常にゼロです(32を加算するので、ASCIIコードの空白になる)。また、ヘッダサイズが3バイト増えており、ヘッダ用のチェックサムが追加されています。<br> | |
1947 | + <br> | |
1948 | + | |
1949 | + 下記は初期文字列です。 | |
1950 | + | |
1951 | 1951 | <pre class=code> |
1952 | 1952 | Initialization String |
1953 | 1953 | 1 2 3 4 5 6 7 8 9 10 |
@@ -1975,7 +1975,7 @@ | ||
1975 | 1975 | Low part of extended packet maximum length (mod(max,95)+32) |
1976 | 1976 | </pre> |
1977 | 1977 | |
1978 | - 下記はパケット種別です。 | |
1978 | + 下記はパケット種別です。 | |
1979 | 1979 | |
1980 | 1980 | <pre class=code> |
1981 | 1981 | Packet Types |