Develop and Download Open Source Software

Browse Subversion Repository

Contents of /branches/4-stable/doc/ja/html/reference/sourcecode.html

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10874 - (show annotations) (download) (as text)
Fri Aug 25 14:22:15 2023 UTC (7 months, 2 weeks ago) by nmaya
File MIME type: text/html
File size: 103613 byte(s)
準拠している RFC について追記・修正
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2 "http://www.w3.org/TR/html4/strict.dtd">
3 <HTML>
4 <HEAD>
5 <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
6 <TITLE>Tera Termソースコード解説</TITLE>
7 <META http-equiv="Content-Style-Type" content="text/css">
8 <link rel="stylesheet" href="../style.css" type="text/css">
9 </HEAD>
10 <BODY>
11
12 <h1 class="center">Tera Termソースコード解説</h1>
13
14 <hr width=80% align=center>
15
16 <ol>
17 <li><a href="#foreword">はじめに</a></li>
18 <li><a href="#skillset">必要スキル</a></li>
19 <li><a href="#module">モジュール構成</a></li>
20 <li><a href="#library">ライブラリ構成</a></li>
21 <li><a href="#plugin">プラグインサポート</a></li>
22 <li><a href="#configuration">設定ファイルの読み書き</a></li>
23 <li><a href="#secure">セキュアプログラミング</a></li>
24 <li><a href="#compatibility">古いバージョンのWindowsとの互換性維持</a></li>
25 <li><a href="#debug">デバッグ手法</a></li>
26 <li><a href="#thread">マルチスレッド</a></li>
27 <li><a href="#dde">DDEによるプロセス間通信</a></li>
28 <li><a href="#ttssh">TTSSHによるSSHの設計と実装</a></li>
29 <li><a href="#macro">マクロ言語の設計と実装</a></li>
30 <li><a href="#caret">キャレット制御</a></li>
31 <li><a href="#serial">シリアルポート</a></li>
32 <li><a href="#xyzmodem">バイナリ転送プロトコル</a></li>
33 </ol>
34
35 <hr width=80% align=center>
36
37 <h2 id="foreword">はじめに</h2>
38  本文書では、Tera Termのソースコードについて解説をします。解説対象とするソースコードはバージョン"4.58"(2008年2月現在)のものをベースとしています。
39 <hr>
40
41
42 <h2 id="skillset">必要スキル</h2>
43  Tera Termのパッケージに含まれるほとんどのプログラムは、C言語で記述されています。一部のコードはC++言語で、MFC(Microsoft Foundation Class)が利用されています。Windows特有の処理を行うために、Win32 APIが多用されているため、APIの知識が必要となってきます。<br>
44  ソースコードをビルドするためには、Microsoft Visual Studio 2005 Standard Edition以上が必要です。Express EditionではMFCが利用できないため、ビルドができません。また、C++BuilderやTurbo C++ Explorer、gccなどのコンパイラにおいても、ビルドすることはできません。<br>
45  Windowsプログラミングに関する情報の源は、Microsoftが提供する「MSDNライブラリ」にあります。開発を行う際は、MSDNライブラリを頻繁に参照することになります。<br>
46  
47 <ul>
48 <li><a href="http://msdn2.microsoft.com/en-us/library/default.aspx" target="_blank">MSDNライブラリ</a></li>
49 <li><a href="http://msdn2.microsoft.com/ja-jp/library/default.aspx" target="_blank">MSDNライブラリ(日本語版)</a></li>
50 </ul>
51
52 <p>
53  ただし、CygTermのみはCygwinのgccでコンパイルをします。ゆえに、CygTermはgccの機能を使った実装になっています。言語はC++です。
54 </p>
55
56  Tera TermのメインエンジンはC++で実装されていますが、C言語的なコーディングがなされているため、Tera Termのソースコードを読み解くには、C言語に関する基礎知識があれば問題ないと言えます。ただし、Microsoft Visual C++(VC++)はANSI C準拠(C89)とはいえ、C99には未対応であるために、本来のC99相当の機能が独自に拡張されている部分もあります。そうした独自拡張された関数には、頭文字にアンダースコア(_)が付いているために、区別が付けやすくなっています。たとえば、VC++の_snprintf()は、ANSI C(C99)のsnprintf()とは似て非なるものです。<br>
57
58 <hr>
59
60
61 <h2 id="module">モジュール構成</h2>
62  Tera Termパッケージに含まれる実行モジュール(.exeと.dll)の関連図を以下に示します。実行ファイルの拡張子は".exe"になっており、必要に応じてDLLが動的リンクされます。いずれも32ビットプログラム(x86)であるために、x86-64やIA-64といった64ビット環境ではそのまま動作するかどうかは評価されていません。
63  
64 <div align="center">
65 <img src="image/module_relation.png" width=720 height=540>
66 </div>
67
68  通常、ユーザがデスクトップやスタートメニューからアプリケーションを起動するときに、呼び出される実行ファイルは"ttermpro.exe"になります。実行ファイルはさらに5つのDLLとダイナミックリンクしています。静的リンクを行い、単一のEXEファイルにしていないのは、1つのプロセスのメモリ占有率を抑えるためです。Tera Termでは多数の起動が行われることが想定されるため、初期設計段階からDLLに分割されています。一度読み込まれたDLLは、複数のプロセス間で共有することができます。<br>
69  <br>
70  
71  マクロスクリプトを実行する際は、"ttpmacro.exe"というまったく別のプロセスが呼び出されます。"ttermpro.exe"とプロセス単位で分けられているのは、マクロを単体で実行できるようにするためです。両プロセス間で、データのやりとりを行うためには、プロセス間通信が必要です。Tera Termでは、DDE(Dynamic Data Exchange)と呼ばれる現在ではレガシーとなってしまったしくみが採用されています。将来のWindowsではDDEがサポートされなくなる可能性があり、その場合Tera Term上でマクロを実行することは一切できなくなります。<br>
72  <br>
73  
74  TTSSHやTTProxy、TTXKanjiMenuといったプラグイン形式のDLLは、Tera Termの起動時に明示的に LoadLibrary() を使ってダイナミックロードされます。ロード対象となるDLLのファイル名は、TTXInit()#ttplug.c において、"TTX*.DLL"というパターンにマッチしたものとなります。<br>
75  <br>
76  
77  "keycode.exe"と"ttpmenu.exe"、"LogMeTT.exe"は単体アプリケーションです。<br>
78  <br>
79  
80  Cygwin接続のしくみについては、別の節で説明します。
81
82 <hr>
83
84
85 <h2 id="library">ライブラリ構成</h2>
86 <p>
87 高度な機能を実現するために、フルスクラッチで実装することは効率がいいとは言えません。Tera Termでは開発効率化を図るために、オープンソースのライブラリを積極的に利用しています。ただし、オープンソース製品のライセンスによる競合には注意を払う必要があります(特にGPL)。<br>
88 下図に、オープンソースのライブラリをリンクしているモジュールと、そのリンク状況を示します。
89 </p>
90 <p>
91 Tera Termマクロプログラムは、正規表現ライブラリ"Oniguruma"をリンクしています。"waitregex","strmatch","strreplace"コマンドにおいて正規表現を利用するためです。<br>
92 Tera Term本体では、バージョンダイアログにOnigurumaのバージョンを表示するためだけにリンクをしています。
93 </p>
94 <p>
95 Tera Termマクロプログラムは、疑似乱数生成器"SFMT"をリンクしています。"random"コマンドにおいて乱数の生成に利用されています。
96 </p>
97 <p>
98 SSHモジュールであるTTSSHは、暗号処理を行うために"OpenSSL"をリンクしています。OpenSSLというネーミングからWebアクセスに使われるSSL(Secure Socket Layer)プロトコル専用のライブラリかと思われがちですが、そうではありません。OpenSSLは基本的な暗号アルゴリズムをサポートしており、TTSSHではOpenSSLに含まれる暗号化/復号ルーチンのみを利用しています。このことは、すなわちOpenSSLライブラリにSSL関連のセキュリティホールが発見されたとしても、TTSSHへの影響は極めて低いということです。
99 </p>
100 <p>
101 SSHモジュールであるTTSSHは、SSHパケットの圧縮を行うために圧縮ライブラリ"zlib"をリンクしています。ただし、ダイヤルアップ回線などの低速度なネットワークにおいては、パケット圧縮は有効ですが、昨今の高速回線ではむしろ速度低下を招く足かせとなります。ゆえに、デフォルトではパケット圧縮機能は無効化されています。
102 </p>
103 <p>
104 SSHモジュールであるTTSSHは、端末エミュレータ"PuTTY"をリンクしています。PuTTYにはPageantと呼ばれるSSH認証エージェントがあるのですが、TTSSHでPageantによる公開鍵認証をサポートするために、PuTTYのソースコードを利用しています。PuTTYの通信部分および端末エミュレーション部分のソースコードは利用していません。
105 </p>
106 <p>
107 なお、いずれのライブラリも静的リンク(static link)としています。ライブラリのコンパイルオプションには"/MT"を付加しています。動的リンク(dynamic link)を行うと、一部のユーザ環境でTera Termが起動できないという現象が発生したために、現在では動的リンクは行っていません。
108 <p>
109
110 <div align="center">
111 <img src="image/library_relation.png" width=720 height=540>
112 </div>
113
114 <hr>
115
116
117 <h2 id="plugin">プラグインサポート</h2>
118  Tera Termでは、DLLという形式でプラグインのしくみをサポートしています。プラグイン形式のDLLファイルを、Tera Termがインストールされているディレクトリへ設置するだけで、Tera Termのソースコードを修正することなく、機能追加を行うことができます。代表的なプラグインとして、TTSSHがあります。<br>
119  プラグインを作成するためのサンプルコードとして、TTXSamples\ttxtest\ttxtest.c というソースファイルが用意されています。プラグインを開発するときは、このソースファイルをひな形とするとよいでしょう。実践的なプラグインとして、"TTX KanjiMenu"のソースコード(TTXKanjiMenu\配下)がシンプルで分かりやすいです。<br><br>
120
121  プラグインは、Tera Term("ttermpro.exe")の起動時に読み込まれます。TTXInit()#ttplug.c が読み込みを行う関数で、カレントディレクトリから"TTX*.DLL"というワイルドカードに合致するDLLファイルが読み込み対象となります。<br>
122  複数のDLLが存在する場合は、Tera Term本体からチェインするような形で、各DLLのエクスポート関数が連結されます。連結される順番は、それぞれのDLLが定義するオーダー値(TTXExports構造体のloadOrderメンバ)で決定され、現状下記の通りとなっています。 
123 <p>
124 <table border=1 align=center>
125 <tr>
126 <th>モジュール</th>
127 <th>オーダー</th>
128 </tr>
129
130 <tr>
131 <td>TTProxy</td>
132 <td>0</td>
133 </tr>
134
135 <tr>
136 <td>TTSSH</td>
137 <td>2500</td>
138 </tr>
139
140 <tr>
141 <td>TTX Kanji Menu</td>
142 <td>5000</td>
143 </tr>
144 </table>
145 </p>
146
147 オーダー値が小さいほど、Tera Term本体側に近くなります。たとえば、Tera Term本体からTTXModifyMenu()が呼び出された場合、
148  
149  <ul>
150   <li>TTXModifyMenu()#ttplug.c → TTProxyのTTXModifyMenu() → TTSSHのTTXModifyMenu() → TTX Kanji MenuのTTXModifyMenu()</li>
151  </ul><br>
152  
153 という順番で、各DLLの関数が呼び出されていくことになります。
154  <br>
155
156  各DLLが、Tera Term本体側から呼び出してもらうためにエクスポートする関数群は、TTXExports構造体で定義し、TTXBind()で渡します。たとえば、TTX Kanji Menuのエクスポート関数は以下のとおりです。不要な関数は NULL で定義してあります。
157
158 <pre class=code>
159 static TTXExports Exports = {
160 /* This must contain the size of the structure. See below for its usage. */
161 sizeof(TTXExports),
162
163 /* This is the load order number of this DLL. */
164 ORDER,
165
166 /* Now we just list the functions that we've implemented. */
167 TTXInit,
168 NULL, /* TTXGetUIHooks */
169 NULL, /* TTXGetSetupHooks */
170 NULL, /* TTXOpenTCP */
171 NULL, /* TTXCloseTCP */
172 NULL, /* TTXSetWinSize */
173 TTXModifyMenu,
174 TTXModifyPopupMenu,
175 TTXProcessCommand,
176 NULL, /* TTXEnd */
177 NULL /* TTXSetCommandLine */
178 };
179 </pre>
180
181  原則、プラグインのエクスポート関数は、他のプラグインと干渉しないように設計をするべきです。また、Tera Term本体側からの呼び出しが、自分宛てであるかどうかを判断する必要がある場合もあります。<br>
182  プラグインがエクスポートする関数について、以下に示します。
183  
184 <p>
185 <table border=1 align=center>
186 <tr>
187 <th>関数</th>
188 <th>意味</th>
189 </tr>
190
191 <tr>
192 <td>TTXBind</td>
193 <td>一番始めに呼び出される関数であり、エクスポート関数のテーブルを渡す。</td>
194 </tr>
195
196 <tr>
197 <td>TTXInit</td>
198 <td>TTXBind()の呼び出し後にすぐに実行される関数で、Tera Term本体のグローバル変数(ts, cv)を受け取り、プラグインの初期化を行う。</td>
199 </tr>
200
201 <tr>
202 <td>TTXGetUIHooks</td>
203 <td>ダイアログのハンドルをフックするための関数。Tera Term本体のダイアログインターフェイスを変更したい場合に使う。フック対象の関数は以下のとおり。<br>
204 &SetupTerminal, &SetupWin, &SetupKeyboard, &SetupSerialPort,
205 &SetupTCPIP, &GetHostName, &ChangeDirectory, &AboutDialog,
206 &ChooseFontDlg, &SetupGeneral, &WindowWindow
207 </td>
208 </tr>
209
210 <tr>
211 <td>TTXGetSetupHooks</td>
212 <td>セットアップルーチンをフックするための関数。フックした側は、元の関数も責任を持って呼び出す必要がある。複数のプラグインが存在する場合、関数がチェインされていく。フック対象の関数は以下のとおり。<br>
213 &ReadIniFile, &WriteIniFile, &ReadKeyboardCnf, &CopyHostList,
214 &AddHostToList, &ParseParam
215 </td>
216 </tr>
217
218 <tr>
219 <td>TTXOpenTCP</td>
220 <td>TCP接続を行うときに呼び出される関数。シリアル接続のときは呼び出されない。また、以下のソケットインターフェイスをフックすることもできる。<br>
221 &Pclosesocket, &Pconnect, &Phtonl, &Phtons, &Pinet_addr,
222 &Pioctlsocket, &Precv, &Pselect, &Psend, &Psetsockopt,
223 &Psocket, &PWSAAsyncSelect, &PWSAAsyncGetHostByName,
224 &PWSACancelAsyncRequest, &PWSAGetLastError
225 </td>
226 </tr>
227
228 <tr>
229 <td>TTXCloseTCP</td>
230 <td>TCPコネクションが切断されるときに呼び出される関数。シリアル接続のときは呼び出されない。下記のうちフックしたインターフェイスがあるならば、元に戻す必要がある。<br>
231 &Pclosesocket, &Pconnect, &Phtonl, &Phtons, &Pinet_addr,
232 &Pioctlsocket, &Precv, &Pselect, &Psend, &Psetsockopt,
233 &Psocket, &PWSAAsyncSelect, &PWSAAsyncGetHostByName,
234 &PWSACancelAsyncRequest, &PWSAGetLastError
235 </td>
236 </tr>
237
238 <tr>
239 <td>TTXSetWinSize</td>
240 <td>Tera Termウィンドウの画面サイズが変更されたときに呼び出される関数。</td>
241 </tr>
242
243 <tr>
244 <td>TTXModifyMenu</td>
245 <td>Tera Termのメニューが初期化されるときに呼び出される関数。プラグイン用のメニューを挿入したい場合に使われる。
246 </td>
247 </tr>
248
249 <tr>
250 <td>TTXModifyPopupMenu</td>
251 <td>Tera Termのポップアップメニューが初期化されるときに呼び出される関数。プラグイン用のポップアップメニューを挿入したい場合に使われる。</td>
252 </tr>
253
254 <tr>
255 <td>TTXProcessCommand</td>
256 <td>メニューが呼び出されたときに実行される関数。プラグイン用のメニューを処理したいときに使われる。
257 </td>
258 </tr>
259
260 <tr>
261 <td>TTXEnd</td>
262 <td>Tera Term本体が終了するときに呼び出される関数。</td>
263 </tr>
264
265 <tr>
266 <td>TTXSetCommandLine</td>
267 <td>新規接続やセッションの複製を行うときに、コマンドラインパラメータの処理を行うときに呼び出される関数。プラグイン独自のオプションを追加したときは、ここで処理される。
268 </td>
269 </tr>
270
271 </table>
272 </p>
273  
274
275 <hr>
276
277
278
279 <h2 id="configuration">設定ファイルの読み書き</h2>
280  Windowsではアプリケーションのデータ保存のために、レジストリが伝統的に利用されていますが、Tera Termではその誕生がWindows 3.1までに遡るために、.iniファイルによるローカルディレクトリへの保存方法が標準となっています。<br>
281  パッケージに同梱されるCollectorやCygTermに関してもローカルディレクトリへデータが保存されます。<br>
282  LogMeTTとTTLEditはレジストリへ保存されます。<br>
283  例外として、TeraTerm Menuはデフォルトでレジストリへ保存をします。カレントディレクトリに"ttpmenu.ini"(0バイトで可)を設置することで、レジストリの代わりに.iniファイルを使うようにすることもできます。ただし、レジストリから.iniファイルへの移行は自動で行いませんので、手動で再登録してください。<br>
284  <br>
285  
286  teraterm.iniファイルにエントリを追加した場合は、ReadIniFile()#ttset.cに設定を読み込みするようにします。
287
288 <pre class=code>
289 ts->ConfirmChangePaste =
290 GetOnOff(Section, "ConfirmChangePaste", FName, TRUE);
291 </pre>
292
293  WriteIniFile()#ttset.c に設定を書き込みするようにします。
294
295 <pre class=code>
296 WriteOnOff(Section, "ConfirmChangePaste", FName,
297 ts->ConfirmChangePaste);
298 </pre>
299
300  エントリに文字列を設定する場合は、Win32APIのGetPrivateProfileString()とWritePrivateProfileString()を使います。数値を扱いたい場合は、GetPrivateProfileInt()とWriteInt()を使います。
301
302 <hr>
303
304
305
306 <h2 id="secure">セキュアプログラミング</h2>
307
308 <h3>文字列操作</h3>
309  WindowsのデフォルトアカウントはAdministrator権限を保持するために(ただし、Windows Vistaには当てはまらない)、アプリケーションにバッファオーバーフローの不具合があると、管理者権限を第三者に奪取されてしまう危険性があります。<br>
310  従来、C言語の文字列処理は開発者のミスにより、バッファオーバーフローが発生しやすいという状況にありました。そこで、MicrosoftはVisual Studio 2005から文字列処理関数のセキュリティ強化バージョンを提供するようになりました。<br>
311  <br>
312
313 <ul>
314 <li><a href="http://msdn2.microsoft.com/ja-jp/library/8ef0s5kh(VS.80).aspx" target="_blank">CRT のセキュリティ強化(MSDNライブラリ)</a></li>
315 </ul>
316 <br>
317
318  Tera Termではセキュリティ強化を図るため、文字列操作のほとんどをセキュリティ強化バージョンに置き換えています。以下に代替関数を示します。<br>
319  <br>
320
321 <table border=1 align=center>
322 <tr>
323 <th></th>
324 <th></th>
325 </tr>
326
327 <tr>
328 <td>sprintf(), _snprintf()</td>
329 <td>_snprintf_s()</td>
330 </tr>
331
332 <tr>
333 <td>strcat(), strncat()</td>
334 <td>strncat_s()</td>
335 </tr>
336
337 <tr>
338 <td>strcpy(), strncpy()</td>
339 <td>strncpy_s()</td>
340 </tr>
341 </table>
342  <br>
343  
344  デフォルトのロケールが適用されると、期待する動作とならないケースにおいては、_snprintf_s_l()を使用しています。<br>
345  いずれの関数においても、_s("secure")という接尾辞が付くため、見た目に区別が付きやすくなっています。当然のことながら、これらの関数はANSI C非互換です。<br>
346  <br>
347  なお、これらの関数を利用する際、Count引数(格納する最大文字数)には"_TRUNCATE"マクロを指定しており、バッファオーバーフローが発生する場合は、強制的にバッファの切り詰めを行っています。
348 <p>
349
350  以下に、strncpy_s()の使用例を示します。strncpy_s()の第2引数(numberOfElements)には、<b>ナル文字(\0)も含めた</b>バッファサイズを指定します。書き込み先のバッファは3バイトしかないので、第3引数(strSource)で指定した5バイトのデータは、2バイトに切り詰められ、buf[]には"he\0"が格納されます。
351
352 <pre class=code>
353 char buf[3];
354 strncpy_s(buf, sizeof(buf), "hello", _TRUNCATE);
355 </pre>
356
357  次に、strncat_s()の使用例を示します。当該関数は、すでに存在する文字列に、さらに文字列を連結するものであるため、第1引数(strDest)は<b>かならずnull-terminateしている</b>必要性があります。strncpy_s()の第2引数(numberOfElements)には、ナル文字(\0)も含めたバッファサイズを指定します。以下の例では、最初の関数を実行すると、5バイト(4文字+ナル文字)が格納されます。2つめの関数を実行する際、残り2バイトしかないので、2文字だけがコピーされ、最終的に"TeraTe"(4文字+2文字+ナル文字)となります。
358  
359 <pre class=code>
360 char str[7];
361 str[0] = '\0';
362 strncat_s(str, sizeof(str), "Tera", _TRUNCATE);
363 strncat_s(str, sizeof(str), "Term", _TRUNCATE);
364 </pre>
365
366  最後に、_snprintf_s()です。紛らわしいのが _snprintf() という関数であり、この関数は<b>null-terminateされない</b>ケースがあるため、使用禁止です。以下に、_snprintf_s()の使用例を示します。以下の例では、buf[]には"ab\0"が格納されます。
367
368 <pre class=code>
369 char buf[3];
370 _snprintf_s(buf, sizeof(buf), _TRUNCATE, "abcdef");
371 </pre>
372
373
374 <hr>
375
376
377
378 <h2 id="compatibility">古いバージョンのWindowsとの互換性維持</h2>
379
380 <h3>ダイナミックローディング</h3>
381
382  Windowsのアプリケーションプログラムは、単一のバイナリファイルを変更することなく、新旧のバージョンのWindows上で起動できるようにするためには、アプリケーションプログラム側での工夫が必要です。<br>
383  たとえば、Windows2000で導入された SetLayeredWindowAttributes() APIを直接呼び出すと、WindowsNT4.0や98などではアプリケーションの起動に失敗するようになります。そのため、新しいAPIを呼び出すときは、LoadLibrary()を使って動的ロードするようにします。<br>
384
385 <pre class=code>
386 static BOOL MySetLayeredWindowAttributes(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags)
387 {
388 typedef BOOL (WINAPI *func)(HWND,COLORREF,BYTE,DWORD);
389 static HMODULE g_hmodUser32 = NULL;
390 static func g_pSetLayeredWindowAttributes = NULL;
391
392 if (g_hmodUser32 == NULL) {
393 g_hmodUser32 = LoadLibrary("user32.dll");
394 if (g_hmodUser32 == NULL)
395 return FALSE;
396
397 g_pSetLayeredWindowAttributes =
398 (func)GetProcAddress(g_hmodUser32, "SetLayeredWindowAttributes");
399 }
400
401 if (g_pSetLayeredWindowAttributes == NULL)
402 return FALSE;
403
404 return g_pSetLayeredWindowAttributes(hwnd, crKey,
405 bAlpha, dwFlags);
406 }
407 </pre>
408
409  いちいち、手で関数プロトタイプを書いていくのは面倒である場合は、「DLLの遅延読み込み」というしくみを利用すると、上記のような手順は不要です。いきなり、関数を呼び出すことができます。ダイレクトに呼び出したい関数がある場合、それが古いWindowsではサポートされていないものであるならば、Visual Studioのプロジェクト設定で、「DLLの遅延読み込み」に該当するDLLを指定しておきます。
410
411
412 <h3>Windows 95</h3>
413
414 Visual Studio 2005になってから、Windows 95のサポートが打ち切られました。よって、必然的にVisual Studio 2005でビルドしたバイナリは、Windows 95では動かないことになります。参考までに、Visual Studio 2008では Windows 98 と NT4.0 、2000 のサポートが打ち切られ、Visual Studio 2010でも同様です。今後は、Windows XPもサポートされなくなることが予想されます。<p>
415
416 現在、Tera TermではVisual Studio 2005によりビルドされていますが、とある工夫により Windows 95 でも動作するようになっています。もちろん、Microsoft非公認の方法であるため、Microsoftからの正式なサポートは受けられません。<br>
417 そもそも、Visual Studio 2005 でビルドされたバイナリは、デフォルトで IsDebuggerPresent 関数にリンクしてしまっています。当該関数は Windows 98 からサポートされたAPIであるため、Windows 95ではリンクエラーとなるわけです。<br>
418 そこで、Windows 95において、ダミーで IsDebuggerPresent 関数のシンボルを定義してあげれば、プログラムの起動時にエラーになることはなくなるのです。詳細は"comapt_w95.h"ヘッダを参照してください。<br>
419
420 <ul>
421 <li><a href="https://ja.osdn.net/projects/ttssh2/svn/view/trunk/teraterm/common/compat_w95.h?view=markup&root=ttssh2" target="_blank">comapt_w95.h</a></li>
422 </ul>
423
424 <hr>
425
426
427 <h2 id="debug">デバッグ手法</h2>
428 <h3>debug printf</h3>
429  Windowsアプリケーションでは printf() が使えません。標準出力がどこにも割り当てられていないからです。AllocConsole()とfreopen()を使えば、Windowsアプリケーションにおいても printf() を利用することができます。<br>
430  OutputDebugString()というAPIがあります。これは Visual Studio のデバッグコンソールにメッセージ出力することができる関数です。当該APIは、"Debug build"および"Release build"に関係なく、デバッガが存在すれば、メッセージを送信します。ゆえに、 Visual Studioがなくとも、<a href="http://www.vector.co.jp/soft/win95/prog/se046776.html" target="_blank">DBCon</a>のようなツールを使えば、アプリケーションの単体起動においても、OutputDebugString()によるメッセージを拾うことができます。<br>
431  Tera Termでは、可変長引数を扱えるようにラッパー関数を用意しています。
432  
433 <pre class=code>
434 void OutputDebugPrintf(char *fmt, ...) {
435 char tmp[1024];
436 va_list arg;
437 va_start(arg, fmt);
438 _vsnprintf_s(tmp, sizeof(tmp), _TRUNCATE, fmt, arg);
439 OutputDebugString(tmp);
440 }
441 </pre>
442
443 <h3>memory leak</h3>
444  malloc()等による確保したヒープメモリの解放し忘れによる「メモリリーク」を、自動で検出するしくみが Visual Studio には用意されています。プログラムの起動時に、以下のコードを挿入するだけです。プログラムの終了時に、解放していないヒープメモリがあれば、 Visual Studio の「出力」ウィンドウにリストアップされます。
445
446 <pre class=code>
447 #ifdef _DEBUG
448 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
449 #endif
450 </pre>
451
452  なお、Windowsのように仮想記憶で動くアプリケーションプログラムに関しては、プログラムの終了時に解放されていないメモリが存在した場合、OSが面倒を見て、メモリが解放されるようになっています。
453
454 <hr>
455
456
457 <h2 id="thread">マルチスレッド</h2>
458  Windowsのアプリケーションはマルチスレッドで設計されることがほとんどですが、Windows 3.1から95の時代ではあまり一般的ではありませんでした。そのため、元々Tera Termはマルチスレッド化されていません。ソースコードを見ると分かるように、グローバル変数が多用されているため、ほとんどの処理がスレッドセーフではありません。<br>
459  ただし、一部の処理においては _beginthreadex() API を使ってスレッドが生成されています。以下にスレッド生成箇所を示します。
460
461 <p>
462 <div align=center><b>Tera Term</b></div>
463 <table border=1 align=center>
464 <tr>
465 <th>生成箇所</th>
466 <th>ソースファイル</th>
467 </tr>
468
469 <tr>
470 <td>シリアル接続</td>
471 <td>CommStart()#commlib.c</td>
472 </tr>
473
474 <tr>
475 <td>TELNETキープアライブ</td>
476 <td>TelStartKeepAliveThread()#telnet.c</td>
477 </tr>
478
479 <tr>
480 <td>IPv4/v6ソケットの生成</td>
481 <td>WSAAsyncGetAddrInfo()#WSAAsyncGetAddrInfo.c</td>
482 </tr>
483 </table>
484
485 <br>
486
487 <div align=center><b>TTSSH</b></div>
488 <table border=1 align=center>
489 <tr>
490 <th>生成箇所</th>
491 <th>ソースファイル</th>
492 </tr>
493
494 <tr>
495 <td>SSHキープアライブ</td>
496 <td>start_ssh_heartbeat_thread()#ssh.c</td>
497 </tr>
498
499 <tr>
500 <td>SCP送信処理</td>
501 <td>SSH2_scp_tolocal()#ssh.c</td>
502 </tr>
503
504 <tr>
505 <td>SCP受信処理</td>
506 <td>SSH2_scp_fromremote()#ssh.c</td>
507 </tr>
508 </table>
509 </p>
510
511  すでに説明したとおり、Tera Term(TTSSH含む)の内部処理はスレッドセーフではないため、シンプルにスレッドを生成し、スレッド内から送受信処理等を行おうとすると、不具合が発生してしまいます。<br>
512  TELNETやSSHのキープアライブ(ハートビート)処理を実現するためには、定期的にパケットの送信処理を行う必要があります。また、SCPによるファイル送受信を行う際においても、ファイルの送信処理中に、ユーザの端末操作のレスポンスを落とさないために、スレッドの使用が不可欠です。<br>
513  そこで、マルチスレッドを使う場合は、モードレスダイアログを非表示で作成したあとに、_beginthreadex() APIでスレッドを生成し、実際の処理はモードレスダイアログに行わせるという手段を使用しています。このしくみにより、マルチスレッドを使いながら、スレッドセーフを保つことができます。以下に、コード例を示します。<br>
514
515 <pre class=code>
516 #define WM_SEND_HEARTBEAT (WM_USER + 1)
517
518 static INT_PTR CALLBACK telnet_heartbeat_dlg_proc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
519 {
520
521 switch (msg) {
522 case WM_INITDIALOG:
523 return FALSE;
524
525 case WM_SEND_HEARTBEAT:
526 TelSendNOP();
527 return TRUE;
528 break;
529
530 case WM_COMMAND:
531 break;
532
533 case WM_CLOSE:
534 return TRUE;
535
536 case WM_DESTROY:
537 return TRUE;
538
539 default:
540 return FALSE;
541 }
542 return TRUE;
543 }
544
545 static unsigned _stdcall TelKeepAliveThread(void *dummy) {
546 static int instance = 0;
547
548 if (instance > 0)
549 return 0;
550 instance++;
551
552 while (cv.Open && nop_interval > 0) {
553 if (time(NULL) >= cv.LastSendTime + nop_interval) {
554 SendMessage(keepalive_dialog, WM_SEND_HEARTBEAT, 0, 0);
555 }
556
557 Sleep(100);
558 }
559 instance--;
560 return 0;
561 }
562
563 void TelStartKeepAliveThread() {
564 unsigned tid;
565
566 if (ts.TelKeepAliveInterval > 0) {
567 nop_interval = ts.TelKeepAliveInterval;
568
569 keepalive_dialog = CreateDialog(hInst, MAKEINTRESOURCE(IDD_BROADCAST_DIALOG),
570 HVTWin, telnet_heartbeat_dlg_proc);
571
572 keepalive_thread = (HANDLE)_beginthreadex(NULL, 0, TelKeepAliveThread, NULL, 0, &tid);
573 if (keepalive_thread == 0) {
574 keepalive_thread = INVALID_HANDLE_VALUE;
575 nop_interval = 0;
576 }
577 }
578 }
579 </pre>
580
581
582
583 <hr>
584
585
586 <h2 id="dde">DDEによるプロセス間通信</h2>
587 <h3>概要</h3>
588  DDE(Dynamic Data Exchange)の誕生は、1987年のWindows 2.0までに遡ります。DDEはプロセス間通信を行うためのしくみですが、現在ではレガシーな方式であり、ほとんどのアプリケーションでは利用されていません。Windowsにおけるプロセス間通信といえば、メールスロットや名前付きパイプ、OLEなどが定番です。<br>
589  かつては、DDEによるプロセス間の通信データをキャプチャすることができる「DDEスパイ」(DDESPY.EXE)というツールがVisual Studioに付属していましたが、現在のVisual Studioにはもはや含まれていません。<br>
590  DDEに関するリファレンスはMSDNライブラリから参照することができます。<br>
591
592 <p>
593 <ul>
594 <li><a href="http://msdn2.microsoft.com/en-us/library/ms648711(VS.85).aspx" target="_blank">Dynamic Data Exchange(MSDNライブラリ)</a></li>
595 <li><a href="http://msdn2.microsoft.com/en-us/library/ms648712(VS.85).aspx" target="_blank">Dynamic Data Exchange Management Library(MSDNライブラリ)</a></li>
596 </ul>
597 </p>
598
599  DDEは、TCPによるネットワーク通信と似ており、サーバとクライアント間を一対一で接続し、通信を行います。アプリケーションがDDEによる通信を行うために、DDEML(Dynamic Data Exchange Management Library)と呼ばれるライブラリをWin32 APIとして提供されています。<br>
600  DDE通信を行うために、一方がサーバとなり、他方がクライアントになる必要があります。また、通信のセッションをシステム全体でユニークとするために、識別情報が必要です。TCP通信ではIPアドレスとポート番号が使われますが、DDE通信では「サービス名」と「トピック名」の組み合わせが使われます。Tera Termではサービス名は"TERATERM"という文字列が使われ、トピック名はTera Term本体のウィンドウハンドル(HVTWin)の16進数値を文字列化したものが使われています。<br>
601  このようなしくみになっているために、マクロスクリプトからまったく別のTera Termへコマンドを送ることはできません。<br>
602
603 <div align="center">
604 <img src="image/dde.png" width=720 height=540>
605 </div>
606
607  上図に示すように、Tera Term本体("ttermpro.exe")がDDEサーバとなり、マクロプログラム("ttpmacro.exe")がDDEクライアントとなります。DDEでは、やりとりするデータの塊のことを「トランザクション」と呼びます。トランザクションには以下に示すような何種類かがあります。タイプは"ddeml.h"でマクロ定義されています。<br>
608
609 <p>
610 <table border=1 align=center>
611 <tr>
612 <th>タイプ</th>
613 <th>意味</th>
614 </tr>
615
616 <tr>
617 <td>XTYP_ADVREQ</td>
618 <td>DDEサーバがクライアントへデータを送るために、DDEサーバが自分自身に送るメッセージ。</td>
619 </tr>
620
621 <tr>
622 <td>XTYP_POKE</td>
623 <td>DDEクライアントからサーバへデータを送る。</td>
624 </tr>
625
626 <tr>
627 <td>XTYP_ADVSTART</td>
628 <td>DDEサーバに対してアドバイズループの開始を指示する。</td>
629 </tr>
630
631 <tr>
632 <td>XTYP_ADVDATA</td>
633 <td>DDEクライアントにデータを定期的に送る。</td>
634 </tr>
635
636 <tr>
637 <td>XTYP_EXECUTE</td>
638 <td>DDEサーバに文字列を送り、何らかの処理をサーバに指示する。</td>
639 </tr>
640
641 </table>
642 </p>
643
644  DDE通信の特徴として、アドバイズループ(advise loop)という概念があります。DDEサーバがアドバイズループに入ると、クライアントはサーバから定期的にデータを受け取り続けることができます。Tera Termでは、リモートホストからの受信データを、マクロプログラムへ渡すために、アドバイズループが使われています。<br>
645
646 <h3>ライブラリ</h3>
647  Tera Termで使われているDDEMLについて、以下に示します。
648  
649  
650 <p>
651 <table border=1 align=center>
652 <tr>
653 <th>関数名</th>
654 <th>機能</th>
655 </tr>
656
657 <tr>
658 <td>DdeInitialize</td>
659 <td>DDEを初期化し、コールバック関数を登録する。初期化できるとインスタンスを返す。</td>
660 </tr>
661
662 <tr>
663 <td>DdeCreateStringHandle</td>
664 <td>文字列リテラルからハンドルを作成する。ハンドルはサーバとクライアントの通信用に使われる。</td>
665 </tr>
666
667 <tr>
668 <td>DdeNameService</td>
669 <td>インスタンスとサービス名("TERATERM")をサーバに登録する。登録後、XTYP_REGISTERトランザクションがクライアントへ送られる。登録解除する際にも使われる。</td>
670 </tr>
671
672 <tr>
673 <td>DdeCmpStringHandles</td>
674 <td>2つの文字列ハンドルを比較する。</td>
675 </tr>
676
677 <tr>
678 <td>DdeClientTransaction</td>
679 <td>クライアントからサーバへトランザクションを送ることができる。トランザクションタイプとして、XTYP_REQUEST・XTYP_EXECUTE・XTYP_ADVSTART・XTYP_POKEなどが指定できる。サーバからのACKを待つまでのタイムアウト時間を指定することができ、Tera Termではほとんど"1000ミリ秒(1秒)"が指定されている。ただし、ACKを確認するケースにおいては"5000ミリ秒(5秒)"が指定されている。</td>
680 </tr>
681
682 <tr>
683 <td>DdeAccessData</td>
684 <td>DDEハンドルから実際のデータへのポインタを取得する。データの取り出しが終わったら、DdeUnaccessData()を呼び出すこと。</td>
685 </tr>
686
687 <tr>
688 <td>DdeCreateDataHandle</td>
689 <td>DDEオブジェクトを作成し、ハンドルを返す。DDEサーバのアドバイズループや、XTYP_REQUESTトランザクション受信時に、DDEクライアントへデータを送るために使われている。</td>
690 </tr>
691
692 <tr>
693 <td>DdeGetData</td>
694 <td>DDEオブジェクトからバッファへコピーする。</td>
695 </tr>
696
697 <tr>
698 <td>DdeDisconnect</td>
699 <td>DDE通信を終了する</td>
700 </tr>
701
702 <tr>
703 <td>DdePostAdvise</td>
704 <td>DDEサーバ側で使われる関数で、自分自身に XTYP_ADVREQ トランザクションを送る。</td>
705 </tr>
706
707 </table>
708 </p>
709
710
711
712 <h3>実装</h3>
713  DDEサーバ側の実装について見ていきます。Tera Term本体("ttermpro.exe")がDDEサーバとなり、かならずDDEサーバから起動されます。マクロプログラム("ttpmacro.exe")から直接マクロスクリプトが実行されるケースにおいても、"connect"マクロによりDDE接続をしないと、通信が開始できません。<br>
714  Tera TermのControlメニューからMacroを呼び出した場合、RunMacro()#ttdde.c がコールされます。<br>
715  HVTWinウィンドウハンドルからトピック名(8バイト)を作成し、DDEの初期化とサーバの登録を行います。また、このタイミングでDDEバッファ(1KB)を作成しています。その後、"ttpmacro.exe"を /D= オプションでトピック名を渡しつつ、起動をします。<br>
716  
717 <pre class=code>
718 SetTopic();
719 if (! InitDDE()) return;
720 strncpy_s(Cmnd, sizeof(Cmnd),"TTPMACRO /D=", _TRUNCATE);
721 strncat_s(Cmnd,sizeof(Cmnd),TopicName,_TRUNCATE);
722 </pre>
723
724  DDEサーバに、DDEクライアントからトランザクションが送られてきたときは、DdeCallbackProcコールバック関数が呼び出されます。コールバック関数は、DdeInitialize()でDDEの初期化を行うときに登録されます。<br><br>
725  
726  次に、DDEクライアントについて見てみましょう。マクロプログラムの起動時、InitDDE()#ttmdde.c が呼び出され、DDEクライアントとして初期化が行われます。DDEの初期化は、DdeInitialize()で行われ、同時にDdeCallbackProcコールバック関数が登録されます。DDEサーバから届いたトランザクションは、コールバック関数で処理されます。<br>
727  DDE通信を始めるためには、DdeConnect()を呼び出し、サーバと接続する必要があります。次に、"ttpmacro.exe"のウィンドウハンドル(HWin)をサーバへ通知するために、XTYP_EXECUTEトランザクションで送ります。最後に、XTYP_ADVSTARTトランザクションをサーバへ送り、アドバイズループを開始します。<br>
728
729 <pre class=code>
730 ConvH = DdeConnect(Inst, Service, Topic, NULL);
731 if (ConvH == 0) return FALSE;
732 Linked = TRUE;
733
734 Cmd[0] = CmdSetHWnd;
735 w = HIWORD(HWin);
736 Word2HexStr(w,&(Cmd[1]));
737 w = LOWORD(HWin);
738 Word2HexStr(w,&(Cmd[5]));
739
740 DdeClientTransaction(Cmd,strlen(Cmd)+1,ConvH,0,
741 CF_OEMTEXT,XTYP_EXECUTE,1000,NULL);
742
743 DdeClientTransaction(NULL,0,ConvH,Item,
744 CF_OEMTEXT,XTYP_ADVSTART,1000,NULL);
745 </pre>
746
747
748 <h3>バッファの管理</h3>
749  マクロプログラムでは"wait"コマンド等で、リモートホストから送られてきたデータを監視するための機能が用意されています。この機能を実現するためには、Tera Term本体とマクロプログラムのそれぞれにおいて、バッファを用意する必要があり、プロセス間通信(DDEトランザクション)により、Tera Term本体からマクロプログラムへリモートホストからの受信データを送らなければなりません。<br>
750
751 <div align="center">
752 <img src="image/dde_flowcontrol.png" width=720 height=540>
753 </div>
754
755  まず、Tera Term本体におけるリモートホストからのTCPパケット受信は、アイドルループ OnIdle()#teraterm.cpp にて行われます。OnIdle()から呼び出される CommReceive()#commlib.c において、TCPパケットデータをバッファ(cv->InBuff[])に格納します。このバッファは 1KB の大きさを持ちます。また、リングバッファではないため、バッファフルになった場合は、TCPパケットの受信をしません。ただし、バッファフル状態が長く続くと、Windowsカーネル内にTCPパケットが溜まっていき、いずれはリモートホストからのパケットを受信できなくなる可能性があります。<br>
756  エスケープシーケンスの解析処理を行う過程で、「ログ採取」か「マクロ実行」を行っている場合は、LogPut1()が呼び出され、DDEバッファ(cv.LogBuf[])へ受信データが格納されます。すなわち、ログ採取とマクロ実行におけるバッファは共通です。このバッファは1KBの大きさを持つリングバッファであり、バッファフルになった場合は、最古のデータから上書きされてゆきます。<br>
757  なお、バイナリモードでログ採取においては、cv.BinBuf[] という別のバッファへデータが格納されますので、DDEバッファとは別物です。言い換えると、バイナリモードにおけるデータをDDE通信させることはできないということです。単純な"wait"コマンドでは、バイナリデータ(制御コードなど)を待つことはできません。<br>
758  Tera Term本体のDDEバッファのデータは、エスケープシーケンスの解析処理が完了後、DDEAdv()#ttdde.c がすぐに呼び出され、自分自身(DDEサーバ)へ XTYP_ADVREQ トランザクションを送ります。XTYP_ADVREQを受け取ったら、DDEコールバック関数 DdeCallbackProc() が呼び出され、マクロプログラムへのデータ送信を行います。ここでアドバイズループが使われています。<br>
759
760 <div align="center">
761 <img src="image/dde_buffer.png" width=720 height=540>
762 </div>
763
764  アドバイズループによりDDEサーバよりデータが送られてくると、DDEクライアントであるマクロプログラムにおいては、XTYP_ADVDATAトランザクションがDDEコールバック関数 DdeCallbackProc()#ttmdde.c により処理されます。<br>
765  
766  なお、Tera Term本体において、DDE通信用のバッファと、ログ採取用のバッファは cv.LogBuf[] で共有されています。バッファの先頭とデータサイズを表すインデックスは、DDE通信の場合は"DStart"と"Dcount"、ログ採取の場合は"LStart"と"Lcount"と区別されています。実際には、1つのバッファを共有しているわけなので、それぞれのインデックスが食い違うと、誤動作する原因となるため、常に同期を取っておくことになります。<br>
767 <hr>
768
769
770 <h2 id="ttssh">TTSSHによるSSHの設計と実装</h2>
771 <h3>概要</h3>
772  オリジナルのTTSSHは<a href="http://www.cs.cmu.edu/People/roc/" target="_blank">Robert O'Callahan</a>氏(現在は<a href="http://robert.ocallahan.org/" target="_blank">元Mozilla hacker</a>として活躍)により開発されたプラグインです。SSH1へ対応しており、ポートフォワーディングやzlibによるパケット圧縮もサポートしていました。TTSSHは、Tera Termをセキュア通信に対応させるためのプラグインであったために、SCPやSFTP等には未対応でした。オリジナルTera Termが1998年に開発凍結後も、2001年ごろまでメンテナンスが続けられていました。<br>
773  TTSSHのSSH2対応を実現するために、TeraTerm Projectにより2004年から設計と実装が始められました。3年の歳月をかけて、ほぼSSH2プロトコルのフルサポートを実現しました。現在ではSCPへも対応しています。将来的にはSFTPへも対応されるかもしれません。<br>
774  原則、TTSSHの実装は<a href="http://www.openssh.com/" target="_blank">OpenSSH</a>を参考にしています。一部、コードをそのまま流用しているところもあります。ただし、OpenSSHはUNIXのコマンドライン向けに設計されているため、Tera TermのようなWindowsアプリケーションにはそのまま適合しない箇所も多く、フレームワークとしてはOpenSSHと大きく異なったものとなっています。<br>
775
776
777 <h3>SSHプロトコル</h3>
778  SSH(Secure Shell)は、バージョン1(厳密には1.5)とバージョン2が存在し、略して"SSH1"および"SSH2"と呼ばれます。それらのバージョン間にはプロトコル仕様としての互換性はありません。SSH1にはセキュリティ上の問題があるために、現在はほとんど利用されません。<br>
779  SSH2プロトコルの仕様に関しては、RFC化されています。
780  
781 <p>
782 <ul>
783 <li><a href="http://www.ietf.org/rfc/rfc4250.txt" target="_blank">RFC4250: The Secure Shell (SSH) Protocol Assigned Numbers</a></li>
784 <li><a href="http://www.ietf.org/rfc/rfc4251.txt" target="_blank">RFC4251: The Secure Shell (SSH) Protocol Architecture</a></li>
785 <li><a href="http://www.ietf.org/rfc/rfc4252.txt" target="_blank">RFC4252: The Secure Shell (SSH) Authentication Protocol</a></li>
786 <li><a href="http://www.ietf.org/rfc/rfc4253.txt" target="_blank">RFC4253: The Secure Shell (SSH) Transport Layer Protocol</a></li>
787 <li><a href="http://www.ietf.org/rfc/rfc4254.txt" target="_blank">RFC4254: The Secure Shell (SSH) Connection Protocol</a></li>
788 <li><a href="http://www.ietf.org/rfc/rfc4255.txt" target="_blank">RFC4255: Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints</a></li>
789 <li><a href="http://www.ietf.org/rfc/rfc4256.txt" target="_blank">RFC4256: Generic Message Exchange Authentication for the Secure Shell Protocol (SSH)</a></li>
790 <li><a href="http://www.ietf.org/rfc/rfc4344.txt" target="_blank">RFC4344: The Secure Shell (SSH) Transport Layer Encryption Modes</a></li>
791 <li><a href="http://www.ietf.org/rfc/rfc4419.txt" target="_blank">RFC4419: Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol</a></li>
792 <!--li><a href="http://www.ietf.org/rfc/rfc5647.txt" target="_blank">RFC5647: AES Galois Counter Mode for the Secure Shell Transport Layer Protocol</a></li-->
793 <li><a href="http://www.ietf.org/rfc/rfc5656.txt" target="_blank">RFC5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer</a></li>
794 <li><a href="http://www.ietf.org/rfc/rfc6668.txt" target="_blank">RFC6668: SHA-2 Data Integrity Verification for the Secure Shell (SSH) Transport Layer Protocol</a></li>
795 <li><a href="http://www.ietf.org/rfc/rfc8268.txt" target="_blank">RFC8268: More Modular Exponentiation (MODP) Diffie-Hellman (DH) Key Exchange (KEX) Groups for Secure Shell (SSH)</a></li>
796 <li><a href="http://www.ietf.org/rfc/rfc8332.txt" target="_blank">RFC8332: Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol</a></li>
797 <li><a href="http://www.ietf.org/rfc/rfc8709.txt" target="_blank">RFC8709: Ed25519 and Ed448 Public Key Algorithms for the Secure Shell (SSH) Protocol</a></li>
798 </ul>
799 </p>
800
801
802 <h3>接続処理</h3>
803  TTSSHは、Tera Termの一部のコードでもあるため、ネットワーク接続処理はTera TermとTTSSHの間を行き来することになり、処理の流れが複雑になっています。また、SSHプロトコルそのもののフローを熟知していないと、TTSSHのシーケンスを追っていくのが難しくなっています。以下に、リモートホストへの接続を行うまでのフローを示します。<br>
804
805 <div align="center">
806 <img src="image/ssh.png" width=720 height=540>
807 </div>
808
809
810 <h3>送信パケット処理</h3>
811 SSH2プロトコルに載せて、パケットをサーバへ送るときのコードは以下のような書き方となります。begin_send_packet()の呼び出しで、「pvar->ssh_state.outbuf + 12」が返り値となり、それがペイロードを表します。ペイロードは純粋にサーバへ送りたいデータのことで、サイズやパディング等を含みません。<br>
812
813 <pre class=code>
814 buffer_t *msg;
815 int len;
816 char *s;
817 unsigned char *outmsg;
818
819 msg = buffer_init();
820 if (msg != NULL) {
821 buffer_put_int(msg, SSH2_DISCONNECT_PROTOCOL_ERROR);
822 s = "disconnected by server request";
823 buffer_put_string(msg, s, strlen(s));
824 s = "";
825 buffer_put_string(msg, s, strlen(s));
826
827 len = buffer_len(msg);
828 outmsg = begin_send_packet(pvar, SSH2_MSG_DISCONNECT, len);
829 memcpy(outmsg, buffer_ptr(msg), len);
830 finish_send_packet(pvar);
831 buffer_free(msg);
832 }
833 </pre>
834
835  SSH通信に載せられて、実際にパケットが送出されるのは、finish_send_packet()から呼び出される finish_send_packet_special() です。パケットを送信するときのフォーマットについて、以下に示します。共通鍵暗号でパケットデータを暗号化する前に、ヘッダとフッタを付ける必要があります。<br>
836  パケットサイズはHMACを除く長さです。パケットサイズそのものはビッグエンディアン形式で、4バイト分格納しますが、その"4"バイトは含まれません。ペイロードの直後にパディングを埋めるのは、共通鍵暗号で暗号化するときに「ブロックサイズ単位」になっていなければ、アルゴリズム的に暗号化できないからです。ブロックサイズは暗号アルゴリズムにより異なり、たとえば3DES-CBCならば24バイト、AES128ならば16バイトです。<br>
837  HMAC(Keyed-Hashing for Message Authentication)は、暗号化本文に対するハッシュです。ハッシュのアルゴリズムは選択可能であり、"MD5"や"SHA-1"がよく使われています。HMACを付加することにより、「第三者によるデータの改ざん」を検出することができます。HMACは、暗号化対象となる本文を秘密鍵とシーケンス番号を加え、ハッシュ値を計算します。秘密鍵とシーケンス番号を加えることにより、第三者がデータをまるごと差し替えたとしても、送信者が生成したハッシュ値を復元することは理論上できません。<br>
838  
839
840 <div align="center">
841 <img src="image/ssh_packet_format1.png" width=720 height=540>
842 </div>
843
844  zlibによるパケット圧縮を行う場合における、パケットを送信するときのフォーマットについて、以下に示します。パケット圧縮を行うのは、「ペイロード」の部分のみで、残りは通常の送信パケットとフォーマットは同じです。なお、パケットを圧縮したとしても、かならずしも元のサイズよりも小さくなるとは限らないので、そのことを考慮したバッファ管理が必要です。<br>
845  パケット圧縮送信で難しいのは、圧縮を開始するタイミングです。ローカルホストからリモートホストへのSSH接続を開始すると、実にたくさんのネゴシエーションが行われますが、パケットを圧縮してよいのは決められたタイミングであり、このタイミングを間違えると、サーバとまったく通信ができなくなります。<br>
846  通常のパケット圧縮の場合は、"SSH2_MSG_KEXINIT"を受信したタイミングです。遅延パケット圧縮の場合は、ユーザ認証が成功したタイミング("SSH2_MSG_USERAUTH_SUCCESS"を受信した時)です。遅延パケット圧縮というのは、それまで"SSH2_MSG_KEXINIT"を受信したタイミングで圧縮を開始していたのを、ユーザ認証が完了するまで延長する方式です。遅延パケット圧縮は、zlibライブラリのセキュリティホールにより、不正なSSHサーバへ接続しただけで、クライアント側に影響が出るのを回避するためのしくみです。
847
848
849 <div align="center">
850 <img src="image/ssh_packet_format2.png" width=720 height=540>
851 </div>
852
853
854 <h3>受信パケット処理</h3>
855  パケットの受信は、TeraTerm本体側からは recv ソケット関数を呼び出した場合に、それがTELNETなのかSSHなのかを意識させないような設計になっていることと、 recv ソケット関数の呼び出しでは、かならずしも十分なバッファサイズが指定されてくるとは限らないため、少々実装が複雑になっています。<br>
856
857 <div align="center">
858 <img src="image/ssh_recv_packet.png" width=720 height=540>
859 </div>
860
861  TeraTerm本体側は OnIdle()#teraterm.cpp というアイドルループにおいて、常時パケットの受信がないかをポーリングしています。それが CommReceive() で、recv()を呼び出します。recv()はTTSSHによりフックされているので、ソケット関数ではなく、TTXrecv()#ttxssh.c が呼び出されます。<br>
862  CommReceive()は recv() を呼び出す際に、バッファ(cv->InBuff[])の空きポインタとサイズを引数に渡します。バッファサイズは 1KB です。つまり、TTXrecv()のサイズには、1〜1024 までの数値が渡される可能性があるということです。<br>
863  TTXrecv()から呼び出される PKT_recv() は、少々複雑なループ処理となっています。SSH接続を初めて行うときのシーケンスを以下に示します。
864  
865 <ol>
866 <li>recv_data() で本当の recv() を呼び出し、サーバからの受信パケットをカーネルから受け取る。pvar->pkt_state.datalenが更新される。 </li>
867 <li>SSH_handle_server_ID() でSSHサーバのバージョンチェックが行われる。pvar->pkt_state.datastart と pvar->pkt_state.datalen を更新する。</li>
868 <li>再度、recv_data() が呼ばれるが、サーバからの受信データがもうないので、connection_closed=TRUE として while ループを抜ける。</li>
869 <li>TeraTermの recv() は"0"で返ってくる。すなわち、受信データなし。</li>
870 </ol>
871
872  次に、SSH通信のための共通鍵生成までのシーケンスを以下に示します。
873
874 <ol>
875 <li>recv_data() で本当の recv() を呼び出し、サーバからの受信パケットをカーネルから受け取る。pvar->pkt_state.datalenが更新される。 </li>
876 <li>SSH_predecrpyt_packet() で、受信パケットの先頭ブロックのみを復号化する。SSHパケットのサイズを取得する。</li>
877 <li>妥当なSSHパケットサイズならば、SSH_handle_packet() を呼び出し、メッセージタイプに応じたハンドラを呼び出す。pvar->ssh_state.payload と pvar->ssh_state.payloadlen を設定する。</li>
878 <li>pvar->pkt_state.datastart と pvar->pkt_state.datalen を更新する。</li>
879 <li>pvar->pkt_state.datalen がゼロになるまで、SSH_predecrpyt_packet() の処理を繰り返す。</li>
880 <li>recv_data() が呼ばれるが、サーバからの受信データがもうないので、connection_closed=TRUE として while ループを抜ける。</li>
881 <li>TeraTermの recv() は"0"で返ってくる。すなわち、受信データなし。</li>
882 </ol>
883
884  次に、端末データ通信のシーケンスを以下に示します。
885
886 <ol>
887 <li>recv_data() で本当の recv() を呼び出し、サーバからの受信パケットをカーネルから受け取る。pvar->pkt_state.datalenが更新される。 </li>
888 <li>SSH_predecrpyt_packet() で、受信パケットの先頭ブロックのみを復号化する。SSHパケットのサイズを取得する。</li>
889 <li>妥当なSSHパケットサイズならば、SSH_handle_packet() を呼び出し、メッセージタイプに応じたハンドラを呼び出す。pvar->ssh_state.payload と pvar->ssh_state.payloadlen を設定する。</li>
890 <li>メッセージタイプがSSH2_MSG_CHANNEL_DATAなので、handle_SSH2_channel_data() を呼び出す。pvar->ssh_state.payload_datalen と pvar->ssh_state.payload_datastart を設定する。</li>
891 <li>pvar->pkt_state.datastart と pvar->pkt_state.datalen を更新する。</li>
892 <li>SSH_is_any_payload() が真を返すようになり、PKT_recv()に渡されてきたバッファへデータをコピーする。</li>
893 <li>TeraTerm側のバッファサイズがいっぱいになった場合は、SSH端末データが残っていたとしても、PKT_recv()は返る。</li>
894 <li>TeraTerm側のバッファサイズに余裕がある場合は、recv_data()を呼び出し、サーバからの受信データを取得する。</li>
895 <li>TeraTermの recv() は「受信データサイズ」で返ってくる。</li>
896 </ol>
897
898
899 <h3>シーケンス制御</h3>
900  SSHは通信経路を暗号化することができるのが特徴ですが、パケットの暗号化を行うためには「鍵」が必要です。通信経路の暗号化には、共通鍵による共通鍵暗号が利用されます。公開鍵暗号のほうがセキュリティ強度は高いのですが、暗号処理に多大な時間がかかるため、SSHのような通信性能が要求されるしくみでは採用されません。SSH2では、共通鍵暗号アルゴリズムとして、AES(Advanced Encryption Standard:Rijndaelアルゴリズム)や3DES(Triple DES)などが利用されます。<br>
901  共通鍵は通信を行う二者間でのみに共有される情報であり、第三者に知られてはなりません。SSH2では、クライアントがリモートホスト(SSHサーバ)へTCP接続した時に、"Diffie-Hellman"アルゴリズムをベースとした独自の方式により、クライアントとサーバでしか知り得ない共通鍵を共有します。DH鍵共有が完了するまでの過程は、ネットワーク上をパケットが平文で流れるため、第三者によるパケットキャプチャが可能となっていますが、パケットを覗かれても、共通鍵は理論上第三者には分からないようになっています。<br>
902  共通鍵が共有できたあとは、その鍵を使ってパケットを暗号化します。SSH2では、送受信されるパケットは種類があるため、それぞれに「メッセージ番号」を割り振っています。RFC4250にメッセージ番号の一覧があります。メッセージ名は"SSH2_MSG_xxxx"というネーミングになっており、TTSSH内部でも同じ名前でマクロ定義しています。<br>
903  以下に、クライアントからサーバへTCP接続(ポート22番)してから、パスワード認証でユーザ認証されるまでの流れを示します。<br>
904
905
906 <div align="center">
907 <img src="image/ssh2_connect1_version.png">
908 </div>
909
910 <div align="center">
911 <img src="image/ssh2_connect2_kex.png">
912 </div>
913
914 <div align="center">
915 <img src="image/ssh2_connect3_auth.png">
916 </div>
917
918 <div align="center">
919 <img src="image/ssh2_connect4_chennel.png">
920 </div>
921
922  以下は、リモートホストのシェル上で"exit"や"logout"として、クライアントから明示的にシェルをクローズするときの、パケットの流れを示しています。<br>
923
924 <div align="center">
925 <img src="image/ssh2_disconnect.png">
926 </div>
927
928
929
930 <h3>疑似端末のしくみ</h3>
931  SSH2では、新しく「フロー制御」という概念が取り込まれています。TCPのウィンドウと同じ考え方で、「ウィンドウサイズ」というしくみを導入しています。この機能により、クライアント(Tera Term)とサーバ(SSHデーモン)間において、フロー制御が働くため、原則データが溢れることはありません。<br>
932  ところで、SSH2におけるフロー制御があるにも関わらず、大量のクリップボードをTeraTermの端末へペーストすると、サーバ側での「データの取りこぼし」が発生することがあります。この現象を理解するためには、UNIXにおける疑似端末(PTY: pseudo-terminal)の動作原理を知る必要があります。
933
934 <div align="center">
935 <img src="image/pty.png" width=720 height=540>
936 </div>
937
938  SSHデーモン(sshd)はクライアントに対して、あたかもサーバ側のシェルが直接接続されているかのように見せる必要があります。逆に、シェル上で動くプログラムは、文字を送りたいときは printf(3) を、文字を受け取りたい場合は scanf(3) といったCライブラリ関数を呼び出すだけでよく、その先がシリアルコンソールなのか、VGAコンソールなのか、SSH接続されているのかは、一切気にしなくてよいようになっています。<br>
939  sshdは、クライアントからの接続要求があったタイミングで、openpty(3)を使って、疑似端末の初期化を行います。疑似端末では、カーネル空間でクライアントとサーバをつなぐために、「マスターデバイスドライバ」と「スレーブデバイスドライバ」が用意されます。マスターデバイスドライバが担当するデバイスファイルは"/dev/pty[p-za-e][0-9a-f]"、スレーブデバイスドライバでは"/dev/tty[p-za-e][0-9a-f]"です。つまり、sshdはマスターデバイスドライバへアクセスすることで、シェルとお話をすることができます。シェルは、sshdからforkされて子プロセスとなり、親プロセス(sshd)が初期化済みのスレーブデバイスドライバとお話をすることになります。この疑似端末のしくみにより、sshdとシェルが接続されます。<br>
940  なお、端末ラインディシプリン(line discipline: 回線規約)というのは、たとえばプログラムが getchar() を呼び出したときに、Enterキーを押下するまで、プログラムに制御が渡りません。端末ラインディシプリンは、プログラム実行中での「行内編集」を可能とするためのモジュールです。Linuxでは、端末ラインディシプリンは /proc/tty/ldiscs で確認できます(N_TTYが標準的に利用される)。
941
942
943 <h3>SCP(Secure Copy)</h3>
944  SCPは OpenSSH パッケージに含まれるプログラムの1つであり、SSHセッションを使ってファイルの送受信を行うことができます。SCPを利用するためには、リモートサーバに"sshd"だけではなく、"scp"コマンドも導入されている必要があります。OpenSSHのSCPは、sshd デーモンから"scp"コマンドが子プロセスとして起動されることで実現されています。なお、SCPとSFTP(Secure File Transfer Program)はまったく別のプロトコルで、互換性はなく、SCPは純粋にファイルの「送信」と「受信」しかできません。<br>
945  SSHセッション上でファイル転送を行うには、クライアントからサーバへ接続が成功したあとに、シェルオープン(pty-req)の代わりに、「外部コマンドの実行」(exec)という形式で、SCPが利用できるようになります。
946  
947  <p><font size=3>・SSH2の場合</font></p>
948  SSH2_MSG_CHANNEL_REQUEST をサーバへ送るときに、"pty-req"の代わりに"exec"をサービス名として指定すると、外部コマンドを実行することができます。
949 <pre>
950   ユーザ認証成功後
951    ----&gt; SSH2_MSG_CHANNEL_OPEN(90)
952    &lt;---- SSH2_MSG_CHANNEL_OPEN_CONFIRMATION(91)
953    ----&gt; SSH2_MSG_CHANNEL_REQUEST(98) サービス名&quot;exec&quot;で外部コマンド送信(&quot;scp -f&quot;)
954    &lt;---- SSH2_MSG_CHANNEL_WINDOW_ADJUST (remote_window+=131072バイト)
955    &lt;---- SSH2_MSG_CHANNEL_EXTENDED_DATA (local_window-=36バイト)
956    &lt;---- SSH2_MSG_CHANNEL_DATA(94)
957 </pre>
958
959  <p><font size=3>・SSH1の場合</font></p>
960   セッションを開くときに、SSH_CMSG_EXEC_CMD をサーバへ送ると、外部コマンドを実行することができます。
961  
962  <p><font size=3>・外部コマンドの書式</font></p>
963 <pre>
964  * "scp [-v] [-r] [-p] [-d] -t ファイル名" ローカルからリモートへのコピー
965  * "scp [-v] [-r] [-p] [-d] -f ファイル名" リモートからローカルへのコピー
966   -v verbose
967   -r リカーシブ
968   -p タイムスタンプ保持
969   -d ディレクトリ
970   -t Local-to-Remoteへコピー
971   -f Remote-to-Localへコピー
972 </pre>
973
974  <p><font size=3>・データ転送</font></p>
975   外部コマンドの送信が完了したあとに、ファイルの内容を送信および受信することができます。
976 <pre>
977   1.送信の流れ
978    ・"Tタイムスタンプ 0 タイムスタンプ 0"を送信(オプション)
979    ・"C0666 サイズ ファイル名"を送信
980    ・ファイルの内容を送信
981    ・セッションクローズ
982
983   2.受信の流れ
984    ・"Tタイムスタンプ 0 タイムスタンプ 0"を受信(オプション)
985    ・0を送信
986    ・"C0666 サイズ ファイル名"を受信
987    ・0を送信
988    ・ファイルの内容を受信
989    ・ファイルのタイムスタンプを設定(オプション)
990    ・0を送信
991    ・セッションクローズ
992 </pre>
993
994  <p><font size=3>・注意事項</font></p>
995   ファイル名にディレクトリが含まれるときは、パスの区切りは「/」となります。「\」は受け付けないので、変換が必要です。
996
997
998
999 <h3>X11転送</h3>
1000  X11転送(X11 port forwarding)は、SSHサーバ上でXウィンドウアプリケーションを起動し、アプリケーションのGUI画面をTera Termが動作しているコンソールPCに飛ばすしくみです。このしくみを使うと、SSHセッション上で"xeyes"や"firefox"、"xemacs"などのソフトウェアを動かすことができるようになります。なお、コンソールPC上には、Xming(http://sourceforge.net/projects/xming/)などのXサーバをあらかじめ用意しておく必要があります。<br>
1001  下図にX11転送のフローを示します。図を見ると分かるように、Tera Term(TTSSH)はXアプリケーションとXサーバをつなぐ橋渡しの役目を持ちます。このようなTera Termのことを"Redirector"や"Port forwarder"、"TCP proxy"と呼びます。
1002
1003 <div align="center">
1004 <img src="image/x11forward.png" width=720 height=540>
1005 </div>
1006
1007  X11転送を利用するためには、Tera TermおよびSSHサーバの双方に事前設定が必要です。まず、Tera Termのほうはteraterm.iniに下記の設定が必要です。
1008
1009 <pre class=code>
1010 [TTSSH]
1011 DefaultForwarding=X
1012 </pre>
1013
1014  SSHサーバのほうは、OpenSSHを例に挙げると、"sshd_config"に下記の設定が必要です。デフォルトは"no"になっているため、通常はデフォルトではX11転送が使えません。
1015
1016 <pre class=code>
1017 X11Forwarding=yes
1018 </pre>
1019
1020  Tera TermはX11転送が有効であると、spec.typeに"FWD_REMOTE_X11_TO_LOCAL"を設定します。これはSSHサーバ側からTera Term側に向かって、X11転送を行うことを意味します。Tera Termは、リモートホストにSSH接続する際、セッションオープン後の"SSH2_MSG_CHANNEL_OPEN_CONFIRMATION"において、X11転送の初期化を行います。
1021
1022 <pre class=code>
1023 if (c->type == TYPE_SHELL) {
1024 // ポートフォワーディングの準備 (2005.2.26, 2005.6.21 yutaka)
1025 // シェルオープンしたあとに X11 の要求を出さなくてはならない。(2005.7.3 yutaka)
1026 FWD_prep_forwarding(pvar);
1027 FWD_enter_interactive_mode(pvar);
1028 }
1029 </pre>
1030
1031  FWD_prep_forwarding()では、"x11-req"サービス名と"MIT-MAGIC-COOKIE-1"をSSHサーバに送信し、SSHサーバ側のX11転送の初期化を促します。SSH接続時にX11の初期化が完了すると、SSHサーバ側に環境変数"DISPLAY"が自動的に設定されます。
1032
1033 <pre class=code>
1034 # echo $DISPLAY
1035 DISPLAY=localhost:10.0
1036 </pre>
1037
1038  ここまで準備が整うと、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>
1039  反対に、Xサーバ、すなわちX11の画面上で何らかの操作が行われた場合、Tera TermからSSHサーバにデータを送信する必要があります。このとき、Tera Termへは FD_READ メッセージが発生し、read_local_connection() が呼び出されます。ここでは、Xサーバから送られてきたデータを SSH2_MSG_CHANNEL_DATA メッセージ形式に載せて、SSHサーバへ送ります。
1040
1041 <hr>
1042
1043
1044 <h2 id="macro">マクロ言語の設計と実装</h2>
1045 <h3>概要</h3>
1046  Tera Termのマクロスクリプトは、BASIC風の言語仕様となっています。BisonやFlexといったしくみは利用しておらず、力業的な独自の構文解析(再帰的下降法)により実装されています。そのため、本格的なスクリプト言語としての記述はできない側面があります。<br>
1047  
1048 <h3>ファイルの読み込み</h3>
1049  ttpmacro.exeの起動時に、マクロファイル(.ttl)が一括してバッファへ読み込まれます。
1050  
1051 <p><ul>
1052 <li>OnInitDialog()#ttmmain.cpp -> InitTTL() -> InitBuff() -> LoadMacroFile()</li>
1053 </ul></p>
1054
1055  初めて読み込まれるマクロファイルの全内容は Buff[0] # ttmbuff.c に格納されます。この時点で、ファイルの内容は一括して読み込まれるため、マクロ実行中はファイルを削除してしまっても問題はありません。ただし、"include"で別のファイルを読み込む場合は、includeを実行する時点で、include対象となるファイルの読み込みが発生します。
1056  
1057 <pre class=code>
1058 #define MAXNESTLEVEL 10 /* 扱えるファイル数(includeは9つまで)*/
1059
1060 static int INest; /* 現在のネスト位置 */
1061 static HANDLE BuffHandle[MAXNESTLEVEL]; /* GlobalAlloc()によるバッファ */
1062 static PCHAR Buff[MAXNESTLEVEL]; /* バッファ領域 */
1063 static BINT BuffLen[MAXNESTLEVEL]; /* ファイルサイズ(バッファサイズ) */
1064 static BINT BuffPtr[MAXNESTLEVEL]; /* バッファのオフセット(読み込み位置)*/
1065 </pre>
1066
1067
1068 <h3>マクロエンジン</h3>
1069  マクロ処理はアイドルループ OnIdle()#ttmmain.cpp で行われます。アイドルループでは TTLStatus 変数により、マクロエンジンの動作を変えています。通常の実行状態は IdTTLRun がセットされています。以下に、動作一覧を示します。
1070
1071 <p>
1072 <table border=1 align=center>
1073 <tr>
1074 <th>条件</th>
1075 <th>処理</th>
1076 </tr>
1077
1078 <tr>
1079 <td>TTLStatus==IdTTLEnd</td>
1080 <td>マクロプログラムを終了する</td>
1081 </tr>
1082
1083 <tr>
1084 <td>送信データがある場合(OutLen > 0)</td>
1085 <td>Tera Term本体へデータを送る</td>
1086 </tr>
1087
1088 <tr>
1089 <td>TTLStatus==IdTTLRun</td>
1090 <td>一行ずつマクロを実行する</td>
1091 </tr>
1092
1093 <tr>
1094 <td>TTLStatus==IdTTLWait</td>
1095 <td>ウェイトする('wait'コマンド)</td>
1096 </tr>
1097
1098 <tr>
1099 <td>TTLStatus==IdTTLWaitLn</td>
1100 <td>ウェイトする('waitln'コマンド)</td>
1101 </tr>
1102
1103 <tr>
1104 <td>TTLStatus==IdTTLWaitNL</td>
1105 <td>一行受信する('recvln'コマンド)</td>
1106 </tr>
1107
1108 <tr>
1109 <td>TTLStatus==IdTTLWait2</td>
1110 <td>文字列を待つ('waitrecv'コマンド)</td>
1111 </tr>
1112
1113 </table>
1114 </p>
1115
1116
1117 <h3>インタープリタ処理</h3>
1118  アイドルループから Exec()#ttl.c が定期的に呼び出される度に、マクロファイルが一行ずつ処理されてゆきます。GetNewLine() では、バッファから一行分を取り出し、LineBuff[]#ttmparse.c へ格納します。行の終わりかどうかは、「ASCIIコードが0x20未満で、かつタブ(0x09)以外」のコードが出現したタイミングで判定しています。先頭の空白やタブは無視されます。セミコロン(;)が出現すると、以降の処理をスキップするため、コメントは行の途中でも付けられることになります。<br>
1119
1120 <pre class=code>
1121 char LineBuff[MaxLineLen]; /* 1つの行は500バイトまで格納可能 */
1122 WORD LinePtr; /* バッファオフセット */
1123 WORD LineLen; /* バッファサイズ */
1124 </pre>
1125
1126  Exec()から呼ばれる ExecCmnd() で、字句解析を行います。字句解析は単純な文字列検索であり、LineBuff[]を1バイトずつ参照していきます。大まかな処理の流れは以下のとおりです。
1127
1128 <p><ol>
1129 <li>endwhileの判定</li>
1130 <li>break処理</li>
1131 <li>endifの判定</li>
1132 <li>elseの判定</li>
1133 <li>マクロコマンドの実行</li>
1134 <li>識別子の判定</li>
1135 <li>文法エラー(上記のいずれでもない場合)</li>
1136 </ol></p>
1137
1138  マクロコマンドかどうかは、GetReservedWord()で判別しています。_stricmp()で比較しているので、アルファベットの大文字・小文字は区別されません(case-insensitive)。マクロコマンドの場合は、TTLxxx() の関数を呼び出します。<br>
1139  識別子の判定は、GetIdentifier() で行います。アルファベット(a-z, A-Z)および数値(0-9)、アンダースコア(_)から構成されるトークンを切り出します。トークンは32文字までです。トークンは「変数」として扱われます。左辺値に変数が来る場合は、「変数への代入」しかありえないので、その直後に「イコール(=)」があるかどうかを調べます。<br>
1140  イコール以降の判定処理は、以下の順番となります。
1141  
1142 <p><ol>
1143 <li>文字列の判定</li>
1144 <li>計算式の判定</li>
1145 </ol></p>
1146
1147  文字列かどうかは GetString() で判定します。文字列は’か”でクォートされているため、取り出すのは容易です。<br>
1148  計算式の判定は、GetExpression() で行います。ここでは再帰的下降法により、構文解析されます。<br>
1149  左辺値が定義済みの変数かどうかは CheckVar() でチェックし、数値もしくは文字列をセットします。そうではない場合は NewStrVar() で、新しい変数として登録します。
1150  
1151
1152 <hr>
1153
1154
1155
1156 <h2 id="caret">キャレット制御</h2>
1157 <h3>概要</h3>
1158  ユーザが端末上でキーボード入力を行うと、カーソルが移動しますが、サーバからのエスケープシーケンスにより、キーボード入力なしにカーソルを移動させる必要があります。また、ウィンドウが非アクティブ状態の場合においても、カーソルを表示させることにより、ブロードキャストモードにおいて、複数端末の同時操作性を向上させています。
1159 <br>
1160
1161 <h3>システムキャレット</h3>
1162  Tera Termにおけるカーソル描画には、システムキャレットを利用しています。Tera Termで使用されているシステムキャレットを制御するAPIを以下に示します。
1163
1164 <p><ul>
1165 <li>CreateCaret</li>
1166 <li>DestroyCaret</li>
1167 <li>GetCaretBlinkTime</li>
1168 <li>HideCaret</li>
1169 <li>SetCaretBlinkTime</li>
1170 <li>SetCaretPos</li>
1171 <li>ShowCaret</li>
1172 </ul></p>
1173
1174  <a href="https://msdn.microsoft.com/ja-jp/library/cc410685.aspx" target="_blank">CreateCaretのドキュメント</a>によると、
1175
1176 <pre>
1177 システムは 1 つのキューにつき 1 つのキャレットを提供します。ウィンドウが
1178 キーボードフォーカスを備えているとき、またはアクティブな状態のときにだけ、
1179 キャレットを作成するべきです。また、キーボードフォーカスを失ったり非アク
1180 ティブになる前に、キャレットを破棄するべきです。
1181 </pre>
1182
1183 とあるため、ウィンドウがアクティブになったタイミングで CreateCaret() を呼び出し、フォーカスが外れ、非アクティブになるタイミングで DestroyCaret() を呼び出す必要があることを意味しています。<br>
1184  キャレットの表示は CaretOn()#vtdisp.c で、消去は CaretOff()#vtdisp.c で実装されています。CaretOn()やCaretOff()が呼び出されるタイミングは、エスケープシーケンス処理 VTParse() の箇所以外にも、マウスボタンを押したときやウィンドウのリサイズを行っているときなどがあります。<br>
1185
1186
1187
1188 <h3>非アクティブ時のカーソル表示</h3>
1189  ウィンドウが非アクティブの場合は、カーソルが消滅します。Windowsの上ではユーザが操作できうるウィンドウは1つであるため、システムキャレットも1つのみ用意されています。通常のオペレーションにおいては、この動作で問題がありません。<br>
1190  しかし、ブロードキャストモードを利用する場合、非アクティブのTera Termウィンドウに対して、コマンドを投入することになります。特に、viなどで複数の端末を同時操作するときは、カーソルが消えていると不都合があります。<br>
1191  そこで、ウィンドウが非アクティブの場合においても、カーソルを描画するようにしています。ただし、システムキャレットは使えないので、自前でカーソルを描画する必要があります。Tera Termのウィンドウが非アクティブの場合においても、リモートホストから送られてくるエスケープシーケンスを処理するためにメインエンジンは動いており、常にカーソル位置は更新されています。現在のカーソル位置は、CursorXとCursorYに設定されています。<br>
1192  非アクティブ時のカーソル表示は CaretKillFocus() で行っています。このときに表示されるカーソルを「ポリゴンカーソル」と呼んでいます。ts.VTColor[0] は Text color です。非アクティブ状態でカーソル位置が更新されるときは、以前に描いたカーソルを消す必要があるので、そのときは ts.VTColor[1] で表される Background color で再描画することで、以前のカーソルを消去しています。<br>
1193  Background colorでポリゴンカーソルを描画すると、ちょうどそのとき背景にあった文字の一部が欠けることがあります。そのため、その文字の再描画を行う必要があり、UpdateCaretKillFocus() で実現しています。当該関数では InvalidateRect() で WM_PAINT を送ることにより、文字の再描画を促しています。<br>
1194
1195 <pre class=code>
1196 void CaretKillFocus(BOOL show)
1197 {
1198 int CaretX, CaretY;
1199 POINT p[5];
1200 HPEN oldpen;
1201 HDC hdc;
1202
1203 DispInitDC();
1204 hdc = VTDC;
1205
1206 CaretX = (CursorX-WinOrgX)*FontWidth;
1207 CaretY = (CursorY-WinOrgY)*FontHeight;
1208
1209 p[0].x = CaretX;
1210 p[0].y = CaretY;
1211 p[1].x = CaretX;
1212 p[1].y = CaretY + FontHeight - 1;
1213 if (CursorOnDBCS)
1214 p[2].x = CaretX + FontWidth*2 - 1;
1215 else
1216 p[2].x = CaretX + FontWidth - 1;
1217 p[2].y = CaretY + FontHeight - 1;
1218 if (CursorOnDBCS)
1219 p[3].x = CaretX + FontWidth*2 - 1;
1220 else
1221 p[3].x = CaretX + FontWidth - 1;
1222 p[3].y = CaretY;
1223 p[4].x = CaretX;
1224 p[4].y = CaretY;
1225
1226 if (show) { // ポリゴンカーソルを表示(非フォーカス時)
1227 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, ts.VTColor[0]));
1228 } else {
1229 oldpen = SelectObject(hdc, CreatePen(PS_SOLID, 0, ts.VTColor[1]));
1230 }
1231 Polyline(VTDC, p, 5);
1232 oldpen = SelectObject(hdc, oldpen);
1233 DeleteObject(oldpen);
1234
1235 DispReleaseDC();
1236 }
1237 </pre>
1238
1239
1240 <h3>非アクティブ時のカーソル表示タイミング</h3>
1241  非アクティブ時のカーソル表示のタイミングは、いくつかのパターンがあるため、漏れなく対処しておく必要があります。表示タイミングとしては以下のとおりです。
1242
1243 <p>
1244 <ul>
1245 <li>ウィンドウがアクティブ(Active == TRUE)の場合は、ポリゴンキャレット描画関数(CaretKillFocus)を一切呼ばないようにする。</li>
1246 <li>CaretOn()では、非アクティブ(Active == FALSE)の場合、ShowCaret()を呼ぶタイミングで、ポリゴンキャレット描画関数(true)を呼ぶ。</li>
1247 <li>CaretOff()では、非アクティブ(Active == FALSE)の場合、HideCaret()を呼ぶタイミングで、ポリゴンキャレット描画関数(false)を呼ぶ。</li>
1248 <li>IsCaretOn()の判定論理に、(!Active && (CaretStatus==0)) のORを追加する。</li>
1249 <li>ChangeCaret()は何もしない</li>
1250 <li>WM_KILLFOCUSされるタイミングでは、IsCaretOn()が真であれば、ポリゴンキャレット描画関数(true)を呼ぶ。</li>
1251 <li>WM_ACTIVEされるタイミングでは、IsCaretOn()が真であれば、ポリゴンキャレット描画関数(false)を呼ぶ。</li>
1252 </ul>
1253 </p>
1254
1255 <br>
1256
1257
1258 <hr>
1259
1260
1261 <h2 id="serial">シリアルポート</h2>
1262 <h3>概要</h3>
1263  Tera TermはUART(16550A)互換のシリアルポートに対応しているため、シリアルコンソールが使用できます。シリアルポートのことを、COM(Communication Port)ポートと呼ぶこともあります。OSが検出したCOMポートは、順に"COM1"、"COM2"といった名前が付けられ、アプリケーションから利用することができます。Microsoft Windows XPでは、最大256個のCOMポート(COM1〜COM256)までが利用可能です。<br>
1264  パソコンに搭載されるCOMポートは、せいぜい1つ、多くても2つであり、最近ではまったくCOMポートがないパソコンも存在します。そのため、USB接続によるシリアルポートを実現する「USBシリアル変換ケーブル」が発売されています。こういった製品の特徴として、OSに認識させるCOMポートの番号を、ユーザが自由に設定できるようになっています。すなわち、Tera Term見えには、2つのCOMポートがあった場合、それぞれ"COM1"、"COM2"として認識できるとは限らず、"COM1"、"COM7"といったふうに認識できるようになる必要があります。<br>
1265  
1266 <h3>COMポートのリストアップ</h3>
1267  かつてのTera Termでは、"COM1"から"COM256"までのすべてのCOMポートを、接続ダイアログにリストアップしていましたが、使い勝手がよくありませんでした。そこで、接続ダイアログを呼び出したタイミングにおいて(Tera Term起動時のみでは不十分)、OSが認識しているCOMポートを検出するようにして、必要なCOMポートのみを表示させるようにしました。その検出ロジックが、DetectComPorts()#ttcmn.c です。QueryDosDevice() APIを使用し、MS-DOSデバイス名から"COM"を探します。<br>
1268
1269 <pre class=code>
1270 if (((h = GetModuleHandle("kernel32.dll")) != NULL) &&
1271 (GetProcAddress(h, "QueryDosDeviceA") != NULL) &&
1272 (QueryDosDevice(NULL, devicesBuff, 65535) != 0)) {
1273 p = devicesBuff;
1274 while (*p != '\0') {
1275 if (strncmp(p, "COM", 3) == 0 && p[3] != '\0') {
1276 ComPortTable[comports++] = atoi(p+3);
1277 if (comports >= ComPortMax)
1278 break;
1279 }
1280 p += (strlen(p)+1);
1281 }
1282 </pre>
1283
1284 <h3>COMポートのフルネーム取得</h3>
1285
1286  上記の処理だけでもユーザビリティは向上するのですが、さらなる欲求として、各COMポートに付けられる「フルネーム」を同時に表示したくなります。COMポートの番号とともに、フルネームも付加表示できると、さらに使い勝手がよくなることが期待されます。この課題を解決するのが、ListupSerialPort()#ttcmn.c です。<br>
1287  
1288 <pre class=code>
1289 static void ListupSerialPort(LPWORD ComPortTable, int comports, char **ComPortDesc, int ComPortMax)
1290 {
1291 GUID ClassGuid[1];
1292 DWORD dwRequiredSize;
1293 BOOL bRet;
1294 HDEVINFO DeviceInfoSet = NULL;
1295 SP_DEVINFO_DATA DeviceInfoData;
1296 DWORD dwMemberIndex = 0;
1297 int i;
1298
1299 DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
1300
1301 bRet =
1302 SetupDiClassGuidsFromName(_T("PORTS"), (LPGUID) & ClassGuid, 1,
1303 &dwRequiredSize);
1304 if (!bRet) {
1305 goto cleanup;
1306 }
1307
1308 DeviceInfoSet =
1309 SetupDiGetClassDevs(&ClassGuid[0], NULL, NULL, DIGCF_PRESENT | DIGCF_PROFILE);
1310
1311 if (DeviceInfoSet) {
1312 dwMemberIndex = 0;
1313 while (SetupDiEnumDeviceInfo
1314 (DeviceInfoSet, dwMemberIndex++, &DeviceInfoData)) {
1315 TCHAR szFriendlyName[MAX_PATH];
1316 TCHAR szPortName[MAX_PATH];
1317 DWORD dwReqSize = 0;
1318 DWORD dwPropType;
1319 DWORD dwType = REG_SZ;
1320 HKEY hKey = NULL;
1321
1322 bRet = SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
1323 &DeviceInfoData,
1324 SPDRP_FRIENDLYNAME,
1325 &dwPropType,
1326 (LPBYTE)
1327 szFriendlyName,
1328 sizeof(szFriendlyName),
1329 &dwReqSize);
1330
1331 hKey = SetupDiOpenDevRegKey(DeviceInfoSet,
1332 &DeviceInfoData,
1333 DICS_FLAG_GLOBAL,
1334 0, DIREG_DEV, KEY_READ);
1335 if (hKey) {
1336 long lRet;
1337 dwReqSize = sizeof(szPortName);
1338 lRet = RegQueryValueEx(hKey,
1339 _T("PortName"),
1340 0,
1341 &dwType,
1342 (LPBYTE) & szPortName,
1343 &dwReqSize);
1344 RegCloseKey(hKey);
1345 }
1346
1347 if (_strnicmp(szPortName, "COM", 3) == 0) { // COMポートドライバを発見
1348 int port = atoi(&szPortName[3]);
1349 int i;
1350
1351 for (i = 0 ; i < comports ; i++) {
1352 if (ComPortTable[i] == port) { // 接続を確認
1353 ComPortDesc[i] = _strdup(szFriendlyName);
1354 break;
1355 }
1356 }
1357 }
1358 }
1359 }
1360
1361 cleanup:
1362 SetupDiDestroyDeviceInfoList(DeviceInfoSet);
1363 }
1364 </pre>
1365
1366
1367 <hr>
1368
1369
1370 <h2 id="xyzmodem">バイナリ転送プロトコル</h2>
1371 <h3>概要</h3>
1372 パソコン通信時代に、バイナリファイルを転送するためのプロトコルが多数開発され、Tera Termではいくつかの転送方式をサポートしています。これらのプロトコルは、今となってはレガシー仕様であり、ほとんど利用されることはありません。現在では、ルータなどの組み込み機器において、ファームウェアのアップロードに使われるぐらいです。
1373 本節では、XMODEM/YMODEM/ZMODEMに関して説明します。<br>
1374
1375 <h3>仕様</h3>
1376 XMODEMの登場は1977年とかなり古く、また仕様がシンプルであるがゆえ、オリジナルの仕様(Ward Christensen氏が開発)を改版した亜種が多数存在しました。その後、XMODEMを改良したYMODEM(Chuck Forsberg氏が開発)が登場し、さらにYMODEMの欠点を改善したZMODEM(Chuck Forsberg氏が開発)が1986年に開発されました。<br>
1377 このように仕様自体が、どこかの団体により規定されたわけではないため、XMODEM/YMODEM/ZMODEMの実装にはクセがあります。Tera TermがサポートするXMODEM/YMODEM/ZMODEMはベーシックなものですが、接続相手によってはうまく動かないことも多く、ユーザによるコンフィグレーションが必要となる場合があります。<br>
1378
1379 <h3>階層構造</h3>
1380 バイナリ転送プロトコルを容易に追加できるようにするため、各モジュールは階層構造になっています。
1381
1382 <pre class=code>
1383 +-------------------------------------------------------+
1384 |ttermpro.exe (filesys.cpp) |
1385 +-------------------------------------------------------+
1386 |ttpfile.dll (ttfile.c) |
1387 +-------+--------+--------+--------+--------+-----------+
1388 |Kermit | XMODEM | YMODEM | ZMODEM | B-Plus | Quick-VAN |
1389 +-------+--------+--------+--------+--------+-----------+
1390 </pre>
1391
1392 たとえば、XMODEMの送信メニューを選択した場合、処理のフローは以下のようになります。
1393
1394 <pre class=code>
1395 filesys.cpp: OnFileXSend() -> XMODEMStart() -> OpenProtoDlg() ->
1396 ttfile.c: ProtoInit() ->
1397 xmodem.c: XInit()
1398 </pre>
1399
1400 ZMODEMの受信メニューの処理に関しては、以下のとおりです。
1401
1402 <pre class=code>
1403 filesys.cpp: OnFileZRcv() -> ZMODEMStart() -> OpenProtoDlg() ->
1404 ttfile.c: ProtoInit() ->
1405 zmodem.c: ZInit()
1406 </pre>
1407
1408 <h3>エントリポイント</h3>
1409 いかなるプロトコルを実装しようとも、ttpfile.dllにおける関数インターフェイス(エントリポイント)が用意されていれば、容易に新規プロトコルとして組み込むことができるようになっています。エントリポイントは、ProtoInit()・ProtoParse()・ProtoTimeOutProc()・ProtoCancel()から呼び出されます。<br>
1410 XMODEMのエントリポイントについて、以下に示します。
1411
1412 <table border=1 align=center>
1413 <tr>
1414 <th>関数</th>
1415 <th>意味</th>
1416 </tr>
1417
1418 <tr>
1419 <td>XInit</td>
1420 <td>初期化</td>
1421 </tr>
1422
1423 <tr>
1424 <td>XSendPacket</td>
1425 <td>ファイル送信</td>
1426 </tr>
1427
1428 <tr>
1429 <td>XReadPacket</td>
1430 <td>ファイル受信</td>
1431 </tr>
1432
1433 <tr>
1434 <td>XTimeOutProc</td>
1435 <td>タイムアウト処理</td>
1436 </tr>
1437
1438 <tr>
1439 <td>XCancel</td>
1440 <td>キャンセル処理</td>
1441 </tr>
1442 </table>
1443 <br>
1444
1445 ZMODEMのエントリポイントについて、以下に示します。
1446
1447 <table border=1 align=center>
1448 <tr>
1449 <th>関数</th>
1450 <th>意味</th>
1451 </tr>
1452
1453 <tr>
1454 <td>ZInit</td>
1455 <td>初期化</td>
1456 </tr>
1457
1458 <tr>
1459 <td>ZParse</td>
1460 <td>ファイル送信</td>
1461 </tr>
1462
1463 <tr>
1464 <td>ZParse</td>
1465 <td>ファイル受信</td>
1466 </tr>
1467
1468 <tr>
1469 <td>ZTimeOutProc</td>
1470 <td>タイムアウト処理</td>
1471 </tr>
1472
1473 <tr>
1474 <td>ZCancel</td>
1475 <td>キャンセル処理</td>
1476 </tr>
1477 </table>
1478
1479 <h3>テスト手法</h3>
1480  バイナリ転送プロトコルはシリアル回線で利用されることが多いのですが、最近のPCは、シリアルポートが搭載されていないため、シリアル接続でのテストを行うことが難しくなっています。そこで、<a href="http://com0com.sourceforge.net/" target="_blank">com0comというNull-modem emulator</a>を利用すると、1つのPC内で仮想的に2つのCOMポートを生成し、Tera Term同士、Tera Termと別ターミナルソフト同士で、シリアル通信を行うことができます。
1481  
1482
1483 <div align="center">
1484 <img src="image/devman_com0com.png" width=468 height=171>
1485 </div>
1486
1487
1488 <h3>記号</h3>
1489  バイナリ転送プロトコルでは、ACKやCANといったキャラクタ表記が使われますが、これらはASCIIコード表から来ています。man 7 ascii でASCIIコード一覧が参照できます。以下に代表的な記号と値を引用します。
1490  
1491
1492 <pre class=code>
1493 Oct Dec Hex Char Oct Dec Hex Char
1494 ------------------------------------------------------------------------
1495 001 1 01 SOH (start of heading) 101 65 41 A
1496 002 2 02 STX (start of text) 102 66 42 B
1497 004 4 04 EOT (end of transmission) 104 68 44 D
1498 006 6 06 ACK (acknowledge) 106 70 46 F
1499 025 21 15 NAK (negative ack.) 125 85 55 U
1500 030 24 18 CAN (cancel) 130 88 58 X
1501 </pre>
1502
1503
1504
1505 <h3>XMODEM</h3>
1506 XMODEMは、ファイルのデータを一定のサイズ(128バイトおよび1024バイト)に分割し、ブロックごとにACKを確認しながら、送信を行うプロトコルです。ブロック単位で、毎回ACKを確認するため、転送速度は速くはありませんが、実装がシンプルとなります。<br>
1507 最後のブロックが一定のサイズに満たない場合は、満たすようにCPMEOF(0x1A)がパディングされます。すなわち、データを送ると、かならずデータのサイズが一定のサイズの倍数となり、末尾にCPMEOFが付加される場合があるということです。そのため、ファイルの送信に完全性を求める場合は、XMOMDEは使えません。なお、CPMEOFというのは、MS-DOSの前身であるCP/MというOSにおいて、テキストファイルの終端(EOF)を表す値のことです。<br>
1508  XMODEMのプロトコルについては、下記サイトが参考になります。
1509
1510 <ul>
1511  <li><a href="http://ja.wikipedia.org/wiki/XMODEM" target="_blank">XMODEM - Wikipedia</a></li>
1512 </ul>
1513 <br>
1514
1515  teraterm.ini で XmodemLog エントリを有効にすると、通信ログを採取することができます。通信ログファイルは、ttermpro.exe と同じディレクトリに"XMODEM.LOG"という名前で生成されます。
1516
1517 <pre class=code>
1518 ; XMODEM log
1519 XmodemLog=on
1520 </pre>
1521
1522  簡単な例として、Tera Term(COM10)から<a href="http://nanno.dip.jp/softlib/man/rlogin/" target="_blank">RLogin</a>(COM11)に対して、67バイトのファイルを送信した場合の通信ログを示します。「&lt;&lt;&lt;」行はTera Termがホストから受信したデータで、「&gt;&gt;&gt;」行はTera Termが送信したデータです。
1523  
1524 <pre class=code>
1525 &lt;&lt;&lt;
1526 15 .
1527
1528 &gt;&gt;&gt;
1529 01 01 FE 23 0D 0A 23 20 6B 6E 6F 77 6E 5F 68 6F ...#..# known_ho
1530 73 74 73 20 66 69 6C 65 20 66 6F 72 20 54 54 53 sts file for TTS
1531 53 48 28 41 6E 20 53 53 48 20 45 78 74 65 6E 73 SH(An SSH Extens
1532 69 6F 6E 20 74 6F 20 54 65 72 61 20 54 65 72 6D ion to Tera Term
1533 29 0D 0A 23 0D 0A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A )..#............
1534 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1535 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1536 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1537 1A 1A 1A EC ....
1538
1539 &lt;&lt;&lt;
1540 06 .
1541
1542 &gt;&gt;&gt;
1543 04 .
1544
1545 &lt;&lt;&lt;
1546 06
1547 </pre>
1548
1549  上記ログの意味は以下のとおりです。
1550  
1551 <ol>
1552 <li>NAK受信(15)</li>
1553 <li>ブロックデータの送信</li>
1554 <li>ACK受信(06)</li>
1555 <li>EOTの送信(04)</li>
1556 <li>ACK受信(06)</li>
1557 </ol>
1558
1559  ブロックデータは、「ヘッダ(3byte)+データ(128byte)+CRC(1byte)」から構成されます。この例では、送信データが128byte未満なので、パディングとしてCPMEOF(0x1A)で埋められています。
1560
1561 <p></p>
1562
1563  次はもう少し大きめのサイズ(1772byte)のデータを送信してみます。(1772+127)/128=14 で、14回ブロック転送されるはずです。下記に通信ログを示します。ヘッダの第2バイトがブロック番号(1オリジン)であり、1(0x01)〜14(0x0E)まで増加していることが分かります。最後のブロックは128バイトに収まるように、CPMEOFがパディングとして付加されています。<br>
1564  このようにXMODEMによる通信では、送信後ファイルの末尾にゴミが付いたように見えるので、厳密にはファイルが壊れます。
1565
1566 <pre class=code>
1567 &lt;&lt;&lt;
1568 15 .
1569
1570 &gt;&gt;&gt;
1571 01 01 FE 3B 20 73 61 6D 70 6C 65 20 6D 61 63 72 ...; sample macr
1572 6F 20 6F 66 20 54 65 72 61 20 54 65 72 6D 0D 0A o of Tera Term..
1573 3B 0D 0A 3B 20 46 69 6C 65 3A 20 73 63 72 65 65 ;..; File: scree
1574 6E 63 61 70 74 75 72 65 2E 74 74 6C 0D 0A 3B 20 ncapture.ttl..;
1575 44 65 73 63 72 69 70 74 69 6F 6E 3A 20 63 61 70 Description: cap
1576 74 75 72 65 20 73 63 72 65 65 6E 20 63 6F 6E 74 ture screen cont
1577 65 6E 74 73 20 61 6E 64 20 77 72 69 74 65 20 74 ents and write t
1578 6F 20 66 69 6C 65 0D 0A 3B 20 45 6E 76 69 72 6F o file..; Enviro
1579 6E 6D 65 F4 nme.
1580
1581 &lt;&lt;&lt;
1582 06 .
1583
1584 &gt;&gt;&gt;
1585 01 02 FD 6E 74 3A 20 67 65 6E 65 72 69 63 0D 0A ...nt: generic..
1586 3B 20 55 70 64 61 74 65 3A 20 32 30 30 37 2F 31 ; Update: 2007/1
1587 31 2F 32 35 2C 20 31 32 2F 35 2C 20 32 30 30 38 1/25, 12/5, 2008
1588 2F 30 31 2F 33 30 0D 0A 3B 20 41 75 74 68 6F 72 /01/30..; Author
1589 3A 20 49 57 41 4D 4F 54 4F 20 4B 6F 75 69 63 68 : IWAMOTO Kouich
1590 69 20 28 64 6F 64 61 29 2C 20 59 75 74 61 6B 61 i (doda), Yutaka
1591 20 48 69 72 61 74 61 0D 0A 3B 20 54 69 70 73 3A Hirata..; Tips:
1592 0D 0A 3B 20 20 20 49 74 20 69 73 20 72 65 63 6F ..; It is reco
1593 6D 6D 65 CA mme.
1594
1595 &lt;&lt;&lt;
1596 06 .
1597
1598 &gt;&gt;&gt;
1599 01 03 FC 6E 64 65 64 20 74 68 61 74 20 79 6F 75 ...nded that you
1600 20 77 69 6C 6C 20 61 64 64 20 69 6E 20 74 68 65 will add in the
1601 20 66 6F 6C 6C 6F 77 69 6E 67 20 65 6E 74 72 79 following entry
1602 0D 0A 3B 20 20 20 69 6E 20 60 4B 45 59 42 4F 41 ..; in `KEYBOA
1603 52 44 2E 43 4E 46 27 20 66 69 6C 65 20 62 65 63 RD.CNF' file bec
1604 61 75 73 65 20 79 6F 75 20 63 61 6E 20 63 61 70 ause you can cap
1605 74 75 72 65 20 79 6F 75 72 20 73 63 72 65 65 6E ture your screen
1606 0D 0A 3B 20 20 20 61 74 20 6F 6E 65 27 73 20 66 ..; at one's f
1607 69 6E 67 9C ing.
1608
1609 :
1610 :
1611 :
1612
1613 &lt;&lt;&lt;
1614 06 .
1615
1616 &gt;&gt;&gt;
1617 01 0E F1 73 70 72 69 6E 74 66 20 22 73 63 72 65 ...sprintf &quot;scre
1618 65 6E 63 61 70 74 75 72 65 5F 25 73 25 73 25 73 encapture_%s%s%s
1619 2D 25 73 25 73 25 73 2E 74 78 74 22 20 44 61 74 -%s%s%s.txt&quot; Dat
1620 65 59 20 44 61 74 65 4D 20 44 61 74 65 44 20 54 eY DateM DateD T
1621 69 6D 65 48 20 54 69 6D 65 4D 20 54 69 6D 65 53 imeH TimeM TimeS
1622 0D 0A 66 69 6C 65 6E 61 6D 65 20 3D 20 69 6E 70 ..filename = inp
1623 75 74 73 74 72 0D 0A 72 65 74 75 72 6E 0D 0A 1A utstr..return...
1624 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1625 1A 1A 1A 75 ...u
1626
1627 &lt;&lt;&lt;
1628 06 .
1629
1630 &gt;&gt;&gt;
1631 04 .
1632
1633 &lt;&lt;&lt;
1634 06
1635 </pre>
1636
1637
1638 <h3>YMODEM</h3>
1639  YMODEMは、XMODEMを改良したプロトコルです。XMODEMとの違いはいくつかありますが、大きな違いの1つとして、ファイル情報が送れるということです。YMODEMでは、ファイル名やファイルサイズをホストに知らせることができるので、送信したファイルの末尾からCPMEOFを除去することができます。<br>
1640
1641  YMODEMのプロトコルについては、下記サイトが参考になります。
1642
1643 <ul>
1644  <li><a href="http://ja.wikipedia.org/wiki/YMODEM" target="_blank">YMODEM - Wikipedia</a></li>
1645 </ul>
1646 <br>
1647
1648  teraterm.ini で YmodemLog エントリを有効にすると、通信ログを採取することができます。通信ログファイルは、ttermpro.exe と同じディレクトリに"YMODEM.LOG"という名前で生成されます。
1649
1650 <pre class=code>
1651 ; YMODEM log
1652 YmodemLog=on
1653 </pre>
1654
1655  簡単な例として、Tera Term(COM10)から<a href="http://nanno.dip.jp/softlib/man/rlogin/" target="_blank">RLogin</a>(COM11)に対して、67バイトのファイルを送信した場合の通信ログを示します。「&lt;&lt;&lt;」行はTera Termがホストから受信したデータで、「&gt;&gt;&gt;」行はTera Termが送信したデータです。
1656  
1657 <pre class=code>
1658 &lt;&lt;&lt;
1659 43 C
1660
1661 &gt;&gt;&gt;
1662 02 00 FF 73 73 68 5F 6B 6E 6F 77 6E 5F 68 6F 73 ...ssh_known_hos
1663 74 73 00 36 37 20 31 31 31 36 32 32 30 30 31 30 ts.67 1116220010
1664 30 20 31 30 30 36 34 34 00 00 00 00 00 00 00 00 0 100644........
1665 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1666 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1667 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1668 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1669 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1670 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1671 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1672 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1673 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1674 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1675 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1676 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1677 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1678 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1679 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1680 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1681 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1682 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1683 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1684 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1685 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1686 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1687 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1688 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1689 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1690 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1692 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1693 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1694 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1695 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1696 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1697 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1698 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1699 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1700 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1701 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1702 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1703 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1704 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1705 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1706 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1707 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1708 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1709 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1710 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1711 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1712 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1713 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1714 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1715 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1716 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1717 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1718 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1719 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1720 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1721 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1722 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1723 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1724 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1725 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1726 00 00 00 1B 08 .....
1727
1728 &lt;&lt;&lt;
1729 06 43 .C
1730
1731 &gt;&gt;&gt;
1732 02 01 FE 23 0D 0A 23 20 6B 6E 6F 77 6E 5F 68 6F ...#..# known_ho
1733 73 74 73 20 66 69 6C 65 20 66 6F 72 20 54 54 53 sts file for TTS
1734 53 48 28 41 6E 20 53 53 48 20 45 78 74 65 6E 73 SH(An SSH Extens
1735 69 6F 6E 20 74 6F 20 54 65 72 61 20 54 65 72 6D ion to Tera Term
1736 29 0D 0A 23 0D 0A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A )..#............
1737 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1738 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1739 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1740 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1741 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1742 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1743 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1744 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1745 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1746 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1747 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1748 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1749 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1750 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1751 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1752 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1753 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1754 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1755 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1756 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1757 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1758 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1759 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1760 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1761 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1762 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1763 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1764 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1765 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1766 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1767 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1768 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1769 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1770 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1771 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1772 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1773 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1774 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1775 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1776 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1777 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1778 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1779 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1780 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1781 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1782 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1783 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1784 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1785 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1786 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1787 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1788 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1789 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1790 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1791 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1792 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1793 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1794 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1795 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A 1A ................
1796 1A 1A 1A 6D 7A ...mz
1797
1798 &lt;&lt;&lt;
1799 06 .
1800
1801 &gt;&gt;&gt;
1802 04 .
1803
1804 &lt;&lt;&lt;
1805 06 43 .C
1806
1807 &gt;&gt;&gt;
1808 02 00 FF 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1809 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1811 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1812 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1813 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1814 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1815 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1816 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1817 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1818 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1819 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1820 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1821 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1822 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1823 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1824 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1825 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1826 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1827 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1828 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1829 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1830 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1831 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1832 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1833 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1834 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1835 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1836 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1837 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1838 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1839 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1840 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1841 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1842 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1843 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1844 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1845 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1846 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1847 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1848 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1849 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1850 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1851 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1852 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1853 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1854 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1855 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1856 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1857 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1858 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1859 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1860 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1861 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1862 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1863 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1864 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1865 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1866 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1867 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1868 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1869 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1870 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1871 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1872 00 00 00 00 00 .....
1873
1874 &lt;&lt;&lt;
1875 06
1876 </pre>
1877
1878  上記ログの意味は以下のとおりです。
1879  
1880 <ol>
1881 <li>'C'(送信要求)受信(43)</li>
1882 <li>ブロック0(ファイル情報)の送信</li>
1883 <li>ACK受信(06)</li>
1884 <li>'C'(送信要求)受信 (43)</li>
1885 <li>ブロック1の送信</li>
1886 <li>ACK受信 (06)</li>
1887 <li>EOTの送信 (04)</li>
1888 <li>ACK受信(06)</li>
1889 <li>'C'(送信要求)受信 (43)</li>
1890 <li>ブロック0(オールゼロ)の送信</li>
1891 <li>ACK受信(06)</li>
1892 </ol>
1893
1894
1895
1896 <h3>ZMODEM</h3>
1897
1898 TBD
1899
1900
1901
1902 <h3>KERMIT</h3>
1903  KERMIT(カーミット:セサミストリートに登場するカエルのマペット)は、1981年にコロンビア大学で開発されたファイル転送プロトコルであり、現在はカーミットプロジェクトによりメンテナンスされています。下記のサイトから仕様書が入手できます。<br>
1904  
1905 <ul>
1906 <li><a href="http://www.kermitproject.org/" target="_blank">The Kermit Project</a></li>
1907 </ul>
1908 <br>
1909
1910  上記サイトでは、ソースコードも配布されており、様々なプラットフォームに実装されています。実装の名称としては、C-KermitやE-Kermit、Kermit95などがあります。<br>
1911  teraterm.ini で KmtLog エントリを有効にすると、通信ログを採取することができます。通信ログファイルは、ttermpro.exe と同じディレクトリに"KERMIT.LOG"という名前で生成されます。
1912  
1913 <pre class=code>
1914 ; Kermit log
1915 KmtLog=on
1916 </pre>
1917
1918  KERMITは元々低速なシリアル通信を想定しているため、一度に送れるデータサイズはせいぜい94バイトです。ただし、拡張オプションで数千バイトのデータを扱うことはできますが、クライアントとサーバの両方が当該機能をサポートしている必要があります。<br>
1919  パケットのフォーマットは基本形式と拡張形式があり、仕様書の「Appendix I Packet Format and Types」に分かりやすい図解があります。下記に引用します。<br>
1920  以下は基本形式です。94バイトまでしか扱えません。
1921
1922 <pre class=code>
1923 Basic Kermit Packet Layout
1924 |&lt;------Included in CHECK------&gt;|
1925 | |
1926 +------+-----+-----+------+------ - - -+-------+
1927 | MARK | LEN | SEQ | TYPE | DATA | CHECK |&lt;terminator&gt;
1928 +------+-----+-----+------+------ - - -+-------+
1929 | |
1930 |&lt;--------LEN-32 characters------&gt;|
1931
1932 MARK パケットの先頭。CTRL-A(0x01)が入る。
1933 LEN パケットサイズ+32。"LEN+2"バイトが全体サイズとなる。
1934 SEQ 「シーケンス番号+32」の剰余64。シーケンス番号は0〜63まで。
1935 TYPE 大文字アルファベットでパケットの種別を表す。
1936 DATA データ
1937 CHECK 加算チェックサム。1,2,3バイトのいずれかの形式を選べる。
1938 &lt;terminator&gt; 制御コード
1939 </pre>
1940
1941  以下は拡張形式です。
1942
1943 <pre class=code>
1944 Kermit Extended Packet Layout
1945 |&lt;-------------------------Included in CHECK-------------&gt;|
1946 | |
1947 |&lt;-------Included in HCHECK-------&gt;| |
1948 | | |
1949 +------+-----+-----+------+-------+-------+--------+----- - - - -+-------+
1950 | MARK | | SEQ | TYPE | LENX1 | LENX2 | HCHECK | DATA | CHECK |
1951 +------+-----+-----+------+-------+-------+--------+----- - - - -+-------+
1952 blank | |
1953 |&lt;-------------------&gt;|
1954 LX1=LENX1-32, LX2=LX2-32 95 x LX1 + LX2 chars
1955 HCHECK is a single-character type 1 checksum
1956 </pre>
1957
1958  拡張形式では94バイト以上のデータを一度に送れるようにするため、データサイズを表現する領域が2バイトに増えています。基本形式の"LEN"は常にゼロです(32を加算するので、ASCIIコードの空白になる)。また、ヘッダサイズが3バイト増えており、ヘッダ用のチェックサムが追加されています。<br>
1959  <br>
1960  
1961  下記は初期文字列です。
1962  
1963 <pre class=code>
1964 Initialization String
1965 1 2 3 4 5 6 7 8 9 10
1966 +-------+-------+-------+-------+-------+-------+-------+-------+-------+- -
1967 | MAXL | TIME | NPAD | PADC | EOL | QCTL | QBIN | CHKT | REPT |
1968 +-------+-------+-------+-------+-------+-------+-------+-------+-------+- -
1969 10 CAPAS+1 CAPAS+2 CAPAS+3
1970 - --+-------+ - -+--------+--------+--------+- -
1971 | CAPAS ... 0| WINDO | MAXLX1 | MAXLX1 |
1972 - --+-------+- -+--------+--------+--------+- -
1973
1974 MAXL Maximum length (0-94) +32
1975 TIME Timeout, seconds (0-94) +32
1976 NPAD Number of pad characters (0-94) +32
1977 EOL Packet terminator (0-63) +32
1978 QCTL Control prefix, literal
1979 QBIN 8th bit prefix, literal
1980 CHKT Block check type {1,2,3}, literal
1981 REPT Repeat count prefix, literal
1982 CAPAS Extendable capabilities mask, ends when value-32 is even
1983 WINDO Window size (0-31) +32
1984 MAXLX1
1985 High part of extended packet maximum length (int(max/95)+32)
1986 MAXLX2
1987 Low part of extended packet maximum length (mod(max,95)+32)
1988 </pre>
1989
1990  下記はパケット種別です。
1991
1992 <pre class=code>
1993 Packet Types
1994 Y Acknowledgment (ACK). Data according to what kind of packet is being acknowledged.
1995 N Negative Acknowledgment (NAK). Data field always empty.
1996 S Send Initiation. Data field contains unencoded initialization string. Tells receiver to expect files. ACK to this packet also contains unencoded initialization string.
1997 I Initialize. Data field contains unencoded initialization string. Sent to server to set parameters prior to a command. ACK to this packet also contains unencoded initialization string.
1998 F File Header. Indicates file data about to arrive for named file. Data field contains encoded file name. ACK to this packet may contain encoded name receiver will store file under.
1999 X Text Header. Indicates screen data about to arrive. Data field contains encoded heading for display.
2000 A File Attributes. Data field contains unencoded attributes. ACK may contain unencoded corresponding
2001 agreement or refusal, per attribute.
2002 D Data Packet. Data field contains encoded file or screen data. ACK may contain X to interrupt sending this file, Z to interrupt entire transaction.
2003 Z End of file. Data field may contain D for Discard.
2004 B Break transmission.
2005 E Error. Data field contains encoded error message.
2006 R Receive Initiate. Data field contains encoded file name.
2007 C Host Command. Data field contains encoded command for host's command processor.
2008 K Kermit Command. Data field contains encoded command for Kermit command processor.
2009 T Timeout psuedopacket, for internal use.
2010 Q Block check error psuedopacket, for internal use.
2011 G Generic Kermit Command. Data field contains a single character subcommand, followed by zero or more
2012 length-encoded operands, encoded after formation:
2013 I Login [&lt;%user[%password[%account]]&gt;]
2014 C CWD, Change Working Directory [&lt;%directory[%password]&gt;]
2015 L Logout, Bye
2016 F Finish (Shut down the server, but don't logout).
2017 D Directory [&lt;%filespec&gt;]
2018 U Disk Usage Query [&lt;%area&gt;]
2019 E Erase (delete) &lt;%filespec&gt;
2020 T Type &lt;%filespec&gt;
2021 R Rename &lt;%oldname%newname&gt;
2022 K Copy &lt;%source%destination&gt;
2023 W Who's logged in? [&lt;%user ID or network host[%options]&gt;]
2024 M Send a short Message &lt;%destination%text&gt;
2025 H Help [&lt;%topic&gt;]
2026 Q Server Status Query
2027 P Program &lt;%[program-filespec][%program-commands]&gt;
2028 J Journal &lt;%command[%argument]&gt;
2029 V Variable &lt;%command[%argument[%argument]]&gt;
2030 </pre>
2031
2032 </BODY>
2033 </HTML>

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26