入り口に戻る

原文はこちら。翻訳にあたって、オープンソースグループ・ジャパンの MIT ライセンスの訳を参考にした。

関数型(function prototype)の表記を一部変更。「〜 〜Proc(〜)」を「typedef 〜 (*Ice〜Proc)(〜)」に変更。そうしないと、別の箇所の文章や他の関数の定義と合わなくなる。明らかに関数名が間違っているところも改めた。(「IcePoAuthProc」のところが「IcePoAuthStatus」に、「IcePaAuthStatus IcePaAuthProc」とあるべきところが「IcePoAuthStatus PoAuthStatus」になっていた。)

# Inter-Client Exchange Library(日本語訳)

## X Consortium Standard

### Ralph Mor

X Consortium

X Version 11, Release 7.7

Version 1.0

Copyright © 1993, 1994, 1996 X Consortium

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of the X Consortium shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the X Consortium.

(訳)

(本ソフトウェアおよび関連する文書のファイル（以下「ソフトウェア」）の複製を取得した全ての人物に対し、以下の条件に従うことを前提に、ソフトウェアを無制限に扱うことを無償で許可する。これには、ソフトウェアの複製を使用、複製、改変、結合、公開、頒布、再許諾、および/または販売する権利、およびソフトウェアを提供する人物に同様の行為を許可する権利が含まれるが、これらに限定されない。)

(上記の著作権表示および本許諾表示を、ソフトウェアの全ての複製または実質的な部分に記載するものとする。)

(ソフトウェアは「現状有姿」で提供され、明示的であるか黙示的であるかを問わず、いかなる種類の保証も行われない。ここでいう保証には、商品性、特定目的への適合性、および権利の非侵害性に関する保証も含まれるが、これらに限定されるものではない。THE X CONSORTIUM は、契約、不法行為、またはその他の行為であるかを問わず、ソフトウェアまたはソフトウェアの使用もしくはその他に取り扱いに起因または関連して生じるいかなる請求、損害賠償、その他の責任について、一切の責任を負わない。)

(X Consortium の名称は、この表示に記載されている場合を除き、X Consortium の事前の書面による承認を得ずに、宣伝であろうとその他の形であろうと、ソフトウェアの販売を促進するもの、またはソフトウェアの使用その他の扱いを奨励 するものに使用してはならない。)

## 第一章 ICE の概略

クライアント間の交信に使えるプロトコルとしては、様々なものが考えられる。そして、その種のプロトコルの間には多 くの類似点と共通の機能が見出せる。例えば、認証(authentication)、版の交渉(version negotiation)、バイト順の交渉(byte order negotiation)などである。Inter-Client Exchange (ICE) プロトコルの狙いは、そのようなプロトコルを構築するための骨組みを規定することである。ICE プロトコルを用いることで、共通の交渉機構を使い回せるようになるとともに、単一の伝送接続(transport connection)上での多重プロトコル通信が可能になる。

## 第二章 ICE ライブラリ - ICE の C 言語インターフェイス

ICE を利用しようとするクライアントは、まず自身が理解できるプロトコルの群を ICE ライブラリに登録しなければならない。各プロトコルに対して、主たる命令コード(major opcode)が動的に割りてられる。主命令コードは 1 - 255 の範囲の値をとる(交信を試みる二つのクライアントの間で、同一のプロトコルに対して異なる主命令コードが割り当てられることもある)。次にクライアント は他のクライアントとの接続を開くか、さもなくば、他のクライアントからの接続を待ち受ける。この段階で認証を行うことができる。一つのクライアントが、 他のクライアントへの接続を図りながら、同時に自身への接続を待ち受けるということも可能である(入れ籠型のセッションマネージャがその例)。二つのクラ イアントの間に一度 ICE 接続が確立された後、使用したいプロトコルを「活性化」するために、クライアントの一方から `ProtocolSetup` を提起する必要がある。他の一方が `ProtocolSetup` を受理すると(ここでも認証を行うことができる)、両クライアントはそのプロトコル固有のメッセージ群を用いて交信できるようになる。単一の ICE 接続の上で、複数のプロトコルを稼働させることが可能である。ある ICE 接続において最早使われていないプロトコルが存在するとき、ICE ライブラリにその旨を通知するのはクライアントの仕事である。但し、各下位プロトコル(subprotocol)がプロトコルの終了作業を開始する方法に ついては、ICE では定めない。

ICE ライブラリでは、受け取ったメッセージを処理するためにコールバック(callback)を利用している。コールバックを用いることで、`ProtocolSetup` メッセージや認証作業を舞台裏で始末できるようになる。クライアントが特定のメッセージを待ち受けて処理から返らない(block)時に、ICE ライブラリで他のメッセージのために領域を確保する必要がない、ということもコールバックの利点の一つである。

## 第三章 対象読者

この文書は主に、ICE を基層としたプロトコルのライブラリを開発する人に向けて書かれている。一般的には、ICE を利用するアプリケーションは直接 ICE ライブラリの関数を呼び出すのではなく、個々のプロトコル・ライブラリの関数を呼び出すものである。しかしながら、ICE 接続を受け入れるため、交信開始時に ICE ライブラリの関数をいくらか呼び出さなければならないアプリケーションも存在するであろう(例えば、クライアントからの接続を受け付けるセッションマネー ジャなど)。とはいえ、総じてプロトコル・ライブラリは、 ICE 内部の細かい点をアプリケーションから隠すように設計すべきである。

## 第四章 ヘッダファイルとライブラリ名

ヘッダファイル <X11/ICE/ICElib.h> で ICElib の全てのデータ型と関数型(function prototype)を定義する。`ICElib.h` はヘッダファイル <X11/ICE/ICE.h> を取り込んでいる(include)。`ICE.h` で ICElib の全ての定数を定義する。メッセージの読み書きを行う必要のあるプロトコル・ライブラリは、ヘッダファイル <X11/ICE/ICEmsg.h> を取り込む(include)。

アプリケーションに ICElib を結合する(link)には、-lICE を用いるものとする。

## 第五章 接頭辞について

`ProtocolSetup` を提起したクライアントと `ProtocolReply` で応答するクライアントとを区別するため、ICE ライブラリで使用する名前には以下の接頭辞を付ける。

- `IcePo` - Ice Protocol Originator プロトコルの開始方

- `IcePa` - Ice Protocol Acceptor プロトコルの受理方

## 第六章 プロトコルの登録

二者のクライアントが特定のプロトコルに則ったメッセージを交換し合うには、双方とも同プロトコルを ICE ライブラリに登録していなければならない。登録を通じて両者は、同プロトコルに対する主命令コードを取得し、メッセージ処理や認証作業を行うコールバック を設定する。登録用の関数が二つ、別々に用意されている。

- 一つは `ProtocolSetup` を提起する側で用いるもの。

- 一つは `ProtocolReply` で応答する側が用いるもの。

両クライアントによる ICE 接続の確立に先んじてプロトコルの登録を行うのが望ましい。プロトコルの登録が ICE 接続の成立後に行われる場合、プロトコルが未だ登録されていない内に同プロトコルの `ProtocolSetup` が届いてしまうという事態も起こり得る。ICE 接続の成立前にプロトコルを登録することが不可能であれば、先の競合条件が生じないよう、適当な予防策を講じるべきである。

関数 `IceRegisterForProtocolSetup` は、`ProtocolSetup` を提起するクライアントが使用する。

`int IceRegisterForProtocolSetup(`const char *protocol_name, const char *vendor, const char *release, int version_count, IcePoVersionRec *version_recs, int auth_count, const char **auth_names, IcePoAuthProc *auth_procs, IceIOErrorProc io_error_proc`)`;

protocol_name | 登録するプロトコルの名前を表す文字列。
vendor | 開発者を表す文字列。意味は同プロトコルで定める。
release | 公開版を表す文字列。意味は同プロトコルで定める。
version_count | 同プロトコルの使用可能な版の数。
version_recs | 版番号とそれに対応するコールバックの組の配列。
auth_count | 使用可能な認証方式の数。
auth_names | 使用可能な認証方式の名前の配列。
auth_procs | 認証を行うコールバックの配列。各認証方式に一つづつ。
io_error_proc | 入出力エラーのハンドラ(処理器)、もしくは NULL。

関数 `IceRegisterForProtocolSetup` は、成功すれば未使用の主命令コードを、エラーが起きれば -1 を返す。プロトコルを実際に利用できる状態にするには、この主命令コードを付して関数 `IceProtocolSetup` を呼び出す必要がある。プロトコルが一度有効になった後、同プロトコルに基づく全てのメッセージは、この主命令コードを指定して送信するものとする。

一つのプロトコル・ライブラリで同一プロトコルの複数の版を運用することも可能である。引数 version_recs には、そのプロトコルの利用可能な版を、優先度に関して降順に並べる。一つ一つの版の情報(version record)は、プロトコルの版番号の上位(major version)、版番号の下位(minor version)及び受け取ったメッセージを処理するためのコールバック関数から成る。

typedef struct {
int major_version;
int minor_version;
IcePoProcessMsgProc process_msg_proc;
} IcePoVersionRec;

`ProtocolSetup` を提起したクライアントによって受信されるメッセージ群を処理するのは、コールバック関数 `IcePoProcessMsgProc` の仕事である。更なる情報は、メッセージを処理するコールバック を見よ。

プロトコルが利用可能な状態になる前の段階で認証を要求することもできる。プロトコル・ライブラリは、自身の対応し ている認証方式を ICE ライブラリに登録しておかなければならない。引数 auth_names と引数 auth_procs には、それぞれ、認証方式の名前とそれを実行するコールバックを、優先度に関して降順に列挙する。コールバック関数 `IcePoAuthProc` についての情報は、認証方式 を見よ。

コールバック `IceIOErrorProc` は、ICE 接続が予期せぬ形で途切れた場合に呼び出される。そのような事態の発生を知る必要がなければ、io_error_proc に NULL を渡すものとする。詳しくは、エラーの処理 を参照。

関数 `IceRegisterForProtocolReply` は、`ProtocolSetup` に対して `ProtocolReply` で応答するクライアントによって使用される。

`int IceRegisterForProtocolReply(`const char *protocol_name, const char *vendor, const char *release, int version_count, IcePaVersionRec *version_recs, int auth_count, const char **auth_names, IcePaAuthProc *auth_procs, IceHostBasedAuthProc host_based_auth_proc, IceProtocolSetupProc protocol_setup_proc, IceProtocolActivateProc protocol_activate_proc, IceIOErrorProc io_error_proc`)`;

protocol_name | 登録するプロトコルの名前を表す文字列。
vendor | 開発者を表す文字列。意味は同プロトコルで定める。
release | 公開版を表す文字列。意味は同プロトコルで定める。
version_count | 同プロトコルの使用可能な版の数。
version_recs | 版番号とそれに対応するコールバックの組の配列。
auth_count | 使用可能な認証方式の数。
auth_names | 使用可能な認証方式の名前の配列。
auth_procs | 認証を行うコールバックの配列。各認証方式に一つづつ。
host_based_auth_proc | ホスト名に基づく認証のためのコールバック。
protocol_setup_proc | `ProtocolSetup` に対する認証が通った時、`ProtocolReply` の送信に先立って呼び出されるコールバック。
protocol_activate_proc | `ProtocolReply` の送信後に呼び出されるコールバック。
io_error_proc | 入出力エラーのハンドラ(処理器)、もしくは NULL。

`IceRegisterForProtocolReply` は、成功すれば未使用の主命令コードを、エラーが起きれば -1 を返す。返ってきた主命令コードは、このプロトコルに則って送信される以後のメッセージ全てで使用するものとする。

一つのプロトコル・ライブラリで同一プロトコルの複数の版を運用することも可能である。引数 version_recs には、そのプロトコルの利用可能な版を、優先度に関して降順に並べる。一つ一つの版の情報(version record)は、プロトコルの版番号の上位(major version)、版番号の下位(minor version)及び受け取ったメッセージを処理するためのコールバック関数から成る。

typedef struct {
int major_version;
int minor_version;
IcePaProcessMsgProc process_msg_proc;
} IcePaVersionRec;

`ProtocolSetup` を受理したクライアントに届けられるメッセージ群を処理するのは、コールバック `IcePaProcessMsgProc` の仕事である。詳しくは メッセージを処理するコールバック を見よ。

プロトコルが利用可能な状態になる前の段階で認証を要求することもできる。プロトコル・ライブラリは、自身の対応し ている認証方式を ICE ライブラリに登録しておかなければならない。引数 auth_names と引数 auth_procs には、それぞれ、認証方式の名前とそれを実行するコールバックを、優先度に関して降順に列挙する。コールバック `IcePaAuthProc` についての情報は、認証方式 を見よ。

認証に失敗し、且つ `ProtocolSetup` を提起しているクライアントが認証を要求していない場合、開始方クライアントのホスト名を付してコールバック `IceHostBasedAuthProc` を呼び出す。もしこのコールバックが `True` を返せば、たとえ元々の認証に失敗していたとしても、`ProtocolSetup` は成功となる。`True` しか返さない `IceHostBasedAuthProc` を登録することで、実質的に認証機能が無効になることに注意する。ホスト名に基づく認証を一切許可しない場合には、引数 host_based_auth_proc に NULL を渡すものとする。

`typedef Bool (*IceHostBasedAuthProc)(`char *host_name`)`;

host_name | `ProtocolSetup` を送信したクライアントのホスト名。

引数 host_name は protocol/hostname の形をとる文字列である。protocol の部分には、{tcp, decnet, local} の何れか一つが入る。

`ProtocolSetup` メッセージの処理と認証作業がコールバックによって舞台裏で実行されることから、プロトコル・ライブラリには `ProtocolSetup` の完了を知るための仕組みが必要になる。この仕組みは二つの段階に分かれる。第一段階では、認証を成功裡に終えてから ICE ライブラリが `ProtocolReply` を送信するまでの間に、コールバック `IceProtocolSetupProc` を呼び出す。設定中のプロトコルで必要な資源はこの時に割り当てる。`IceProtocolSetupProc` の戻り値が成功を意味するものであれば、ICE ライブラリは `ProtocolReply` を送信し、それに続いて同ライブラリにてコールバック `IceProtocolActivateProc` を呼び出す。それ以外の場合は、`ProtocolSetup` に対する返答として、相手方のクライアントへエラーが送られる。

`IceProtocolActivateProc` は必須のコールバックではない。`ProtocolReply` の直後にプロトコル・ライブラリでメッセージを生成する場合に限って此れを登録する。このコールバックが不要であれば、protocol_activate_proc に NULL を渡す。

`typedef Status (*IceProtocolSetupProc)(`IceConn ice_conn, int major_version, int minor_version, char *vendor, char *release, IcePointer *client_data_ret, char **failure_reason_ret`)`;

ice_conn | ICE 接続オブジェクト。
major_version | プロトコルの版番号の上位。
minor_version | プロトコルの版番号の下位。
vendor | 開発者を表す文字列。プロトコルの開始方によって登録されたもの。
release | 公開版を表す文字列。プロトコルの開始方によって登録されたもの。
client_data_ret | コールバックで設定するクライアント・データ。
failure_reason_ret | 失敗の原因。

引数 client_data_ret に格納されたポインタは、ICE 接続上で此のプロトコルに宛ててメッセージが届く度に、コールバック `IcePaProcessMsgProc` に渡される。

vendor と release に渡される文字列は、用が済んだ時点で `free` を用いて解放するものとする。

`IceProtocolSetupProc` が不首尾に終わった場合、同コールバックは実値 0 の Status を返すとともに、failure_reason_ret に文字列領域を割り当て、そこに失敗の理由を格納する。この領域を解放するのは ICE ライブラリの仕事である。

コールバック `IceProtocolActivateProc` は次のように定義されている。

`typedef void (*IceProtocolActivateProc)(`IceConn ice_conn, IcePointer client_data`)`;

ice_conn | ICE 接続オブジェクト。
client_data | コールバック `IceProtocolSetupProc` で設定されたクライアント・データ。

コールバック `IceIOErrorProc` は、ICE 接続が予期せぬ形で途切れた場合に呼び出される。そのような事態の発生を知る必要がなければ、io_error_proc に NULL を渡すものとする。詳しくは、エラーの処理 を参照。

## メッセージを処理するコールバック

アプリケーションは、(`select` を通じて) ICE 接続上に未読のデータがあることを知った時、関数 `IceProcessMessages` を呼び出す。メッセージの処理 を参照。`IceProcessMessages` において 0 でない主命令コードの付いた ICE メッセージ・ヘッダを読み取った時( 0 は ICE プロトコルが使用する)、同関数は、メッセージの残りの部分を読み込み、開封し、場合によっては、処理するための関数を呼び出さなければならない。

`ProtocolSetup` を提起したクライアントの下にメッセージが届いた場合、コールバック `IcePoProcessMsgProc` を呼び出す。

`typedef void (*IcePoProcessMsgProc)(`IceConn ice_conn, IcePointer client_data, int opcode, unsigned long length, Bool swap, IceReplyWaitInfo *reply_wait, Bool *reply_ready_ret`)`;

ice_conn | ICE 接続オブジェクト。
client_data | 同 ICE 接続において此のプロトコルに紐付けられているクライアント・データ。
opcode | メッセージの従たる命令コード。
length | ICE ヘッダ以降のメッセージの長さ(8 バイト単位)。
swap | バイト順の交換が必要か否かを表すフラグ。
reply_wait | 呼出し元のクライアントが返答を待っているのか否かを表す。
reply_ready_ret | `True` に設定されていれば、返答の準備ができているということ。

`ProtocolSetup` を受理したクライアントの下にメッセージが届いた場合、コールバック `IcePaProcessMsgProc` を呼び出す。

`typedef void (*IcePaProcessMsgProc)(`IceConn ice_conn, IcePointer client_data, int opcode, unsigned long length, Bool swap`)`;

ice_conn | ICE 接続オブジェクト。
client_data | 同 ICE 接続において此のプロトコルに紐付けられているクライアント・データ。
opcode | メッセージの従たる命令コード。
length | ICE ヘッダ以降のメッセージの長さ(8 バイト単位)。
swap | バイト順の交換が必要か否かを表すフラグ。

メッセージを読み込むためのマクロが定義されているので、上記の両コールバックはこれを利用する(「ICE メッセージを読む」を参照)。バイト順の交換が必要になることもあり得るという点に注意する。ICE ヘッダの length フィールドのバイト順交換は、必要があれば ICElib にて行われる。

両コールバックにおいて、引数 client_data には `ProtocolSetup` の際に登録したクライアント・データへのポインタが格納されている。`IcePoProcessMsgProc` の場合、クライアント・データは `IceProtocolSetup` 呼出しの中で設定される(訳註：SMlib では SmcConn がクライアント・データとして渡される)。`IcePaProcessMsgProc` の場合、クライアント・データはコールバック `IceProtocolSetupProc` の中で設定される(訳註：SMlib では SmsConn がクライアント・データとして渡される)。

コールバック `IcePoProcessMsgProc` では、引数 reply_wait を確認する必要がある。もし replay_wait が NULL であれば、ICE ライブラリは次のように判断する。即ち、この関数はコールバックを通じてクライアントにメッセージを届けるものと見做される。例えば、ここでのメッセージ がセッション管理における「Save Yourself」メッセージであるとすると、当関数はコールバック経由でクライアントに「Save Yourself」を通知するということである。このようなコールバックが実際にどのように定義されるかは、実装次第である。

一方、reply_wait が NULL でない場合、クライアントは先に自身が送信したメッセージに対する返信もしくはエラー報告を待ち受けているものと見做される。reply_wait は `IceReplyWaitInfo` 型である。

typedef struct {
unsigned long sequence_of_request;
int major_opcode_of_request;
int minor_opcode_of_request;
IcePointer reply;
} IceReplyWaitInfo;

`IceReplyWaitInfo` 構造体にはメッセージの主/従命令コードと通し番号が入っているが、これは、現在待ち受けている返信が為される所以となったメッセージのものである。同構造体には、返信メッセージが書き込まれる領域へのポインタも含まれている(プロトコル・ライブラリにおいて、この `IcePointer` を返信用の適当な型に読み替える)。大抵、返信メッセージには固定長の部分が存在し、そして、この固定長データ部分を内部に保持する構造体へのポインタは、返信を待っているクライアントが用意する。可変長のデータがある場合、コールバック `IcePoProcessMsgProc` において追加の記憶領域が確保され、その領域へのポインタが先の固定長構造体の中に格納される。もしデータが丸ごと可変長であれば(例えば可変長文字列一 つ)、返信を待ち受ける方のクライアントは、ポインタ一つを保持できる固定長領域へのポインタを渡すだけで良い。そして、コールバック `IcePoProcessMsgProc` が記憶領域を新たに確保し、その領域へのポインタを(渡されたポインタの指し示す固定長領域に)書き込む。返信を受け取ったクライアントは、自身のために割り当てられた記憶領域の解放作業を引き受けるものとする。

reply_wait が NULL ではなく、且つ `IcePoProcessMsgProc` において同 reply_wait に対する返信もしくはエラー報告がある場合(つまりコールバックが生成されなかった場合)、引数 reply_ready_ret を `True` に設定する。エラーを返すのは、待ち受けられている返信にそのエラーが直接関連している時だけである、という点に注意する。それ以外の時は、`IcePoProcessMsgProc` は内部でエラーを処理するか、実装ライブラリのエラー・ハンドラ(処理器)を呼び出す。

reply_wait が NULL の場合は reply_ready_ret も NULL であろうから、このポインタに如何なる値も書き込まないよう気をつけなければならない。

一方、コールバック `IcePaProcessMsgProc` では、クライアントへメッセージを渡すにあたって、常にコールバックを利用する。例えば、ここでのメッセージがセッション管理における「Interact Request」メッセージであるとすると、この関数ではコールバック経由でクライアントに「Interact Request」を通知することになる。

コールバック `IcePaProcessMsgProc` が `IcePoProcessMsgProc` のように reply_wait を利用しないのは次の理由による。即ち、サーバとして振舞うプロセスは、返信を待って他の入力を弾くわけにはいかないからである(接続を試みる方のクライ アントが異常な動作をした結果、際限なくメッセージをブロックする(返信が来るまで関数から戻らない)ようになると、他のクライアントとは通信できなく なってしまう)。

## 認証方式

先に述べたように、プロトコル・ライブラリは、自身が対応している認証方式を ICE ライブラリに登録しなければならない。各認証方式に対して、二つのコールバックを登録することができる。

- 一つは、`ProtocolSetup` を提起する側の処理を行うコールバック。

- 一つは、この要求を受理もしくは拒否する側の処理を行うコールバック。

`IcePoAuthProc` は、`ProtocolSetup` を提起したクライアントのために呼び出されるコールバックである。このコールバックは、他のクライアントから送られてくる先頭メッセージ 「Authentication Required」もしくは後続メッセージ「Authentication Next Phase」に応えることができなければならない。

`typedef IcePoAuthStatus (*IcePoAuthProc)(`IceConn ice_conn, IcePointer *auth_state_ptr, Bool clean_up, Bool swap, int auth_datalen, IcePointer auth_data, int *reply_datalen_ret, IcePointer *reply_data_ret, char **error_string_ret`)`;

ice_conn | ICE 接続オブジェクト。
auth_state_ptr | 認証のコールバック手続きで使用する状態情報へのポインタ。
clean_up | `True` は認証が済んでいることを意味する。その場合、この関数はこれまで保持していた状態情報を片付ける。この時、後ろの六つの引数は無視する。
swap | `True` であれば、auth_data はバイト順を入れ替える必要がある(内容によっては)。
auth_datalen | 認証データのバイト長。
auth_data | 認証者からのデータ。
reply_datalen_ret | この関数の戻り値たる返信データのバイト長。
reply_data_ret | この関数で返される返信データ。
error_string_ret | 認証手続きの関数中でエラーが発生した場合、同関数はエラー文を割り当てた上で返り値とする。

認証方式によっては、作業の完遂に複数の段階を要することもある。その為、クライアントの認証作業中に `IcePoAuthProc` を複数回呼び出すことがあり得る。そして、その複数の呼出しの合間にも保持し続けなければならない状態情報があるであろう。各 `ProtocolSetup` の開始時点では *auth_state_ptr は NULL であり、この関数(IcePoAuthProc)を通じて状態の初期化と同ポインタの設定を行う。このポインタは、後続の本コールバックの呼出しにおい て、先行コールバックが記録した状態情報を取得するのに用いられる。

必要であれば、関数 `IceConnectionString` を呼び出すことで、`ProtocolSetup` を受理するクライアントのネットワーク ID を取得することができる。

`free` を用いて reply_data_ret と error_string_ret の両ポインタを解放するのは ICElib の仕事である。

auth_data のポインタが揮発性の(volatile)記憶領域を指していることもあり得る。コールバックの呼出し完了以後もデータを保持する必要があれば、忘れずに複製を作っておく。

`IcePoAuthProc` は、次の四つの値の何れか一つを返す。

- `IcePoAuthHaveReply` - 返信は入手可能。

- `IcePoAuthRejected` - 認証が拒否された。

- `IcePoAuthFailed` - 認証に失敗した。

- `IcePoAuthDoneCleanup` - 片付けを終えた。

`IcePaAuthProc` は、`ProtocolSetup` を受信したクライアントのために呼び出されるコールバックである。

`typedef IcePaAuthStatus (*IcePaAuthProc)(`IceConn ice_conn, IcePointer *auth_state_ptr, Bool swap, int auth_datalen, IcePointer auth_data, int *reply_datalen_ret, IcePointer *reply_data_ret, char **error_string_ret`)`;

ice_conn | ICE 接続オブジェクト。
auth_state_ptr | 認証のコールバック手続きで使用する状態情報へのポインタ。
swap | `True` であれば、auth_data はバイト順を入れ替える必要がある(内容によっては)。
auth_datalen | プロトコル開始方からの認証データのバイト長。
auth_data | プロトコル開始方からの認証データ。
reply_datalen_ret | この関数の戻り値たる認証データの長さ。
reply_data_ret | この関数の戻り値たる認証データ。
error_string_ret | 認証の拒否もしくは蹉跌に際し、戻り値としてエラー文が格納される。

認証方式によっては、作業の完遂に複数の段階を要することもある。その為、クライアントの認証作業中に `IcePaAuthProc` を複数回呼び出すことがあり得る。そして、その複数の呼出しの合間にも保持し続けなければならない状態情報があるであろう。各 `ProtocolSetup` の開始時点では、auth_datalen は 0、*auth_state_ptr は NULL であり、この関数(IcePaAuthProc)を通じて状態の初期化とポインタの設定を行う。このポインタは、後続の本コールバックの呼出しにおいて、 先行コールバックが記録した状態情報を取得するのに用いられる。

必要であれば、関数 `IceConnectionString` を呼び出すことで、`ProtocolSetup` を受理するクライアントのネットワーク ID を取得することができる。

auth_data のポインタが揮発性の(volatile)記憶領域を指していることもあり得る。コールバックの呼出し完了以後もデータを保持する必要があれば、忘れずに複製を作っておく。

reply_data_ret と error_string_ret の両ポインタを伝送し、且つ `free` を用いて解放するのは、ICElib の仕事である。

`IcePaAuthProc` は、次の四つの値の何れか一つを返す。

- `IcePaAuthContinue` - 認証を続ける(あるいは始める)。

- `IcePaAuthAccepted` - 認証に成功した。

- `IcePaAuthRejected` - 認証を拒否した。

- `IcePaAuthFailed` - 認証に失敗した。

## 第七章 ICE 接続

二つのクライアントの間で ICE 接続を確立するには、一方に接続を待ち受けるクライアントが、他方に接続を提起するクライアントが存在しなければならない。大多数のクライアントは接続を提起する側なので、まずその方から取り上げる。

## ICE 接続の開設

ICE 接続を待ち受けている他のクライアントとの間に接続を開設するには、`IceOpenConnection` を用いる。

`IceConn IceOpenConnection(`char *network_ids_list, IcePointer context, Bool must_authenticate, int major_opcode_check, int error_length, char *error_string_ret`)`;

network_ids_list | 相手のクライアントのネットワーク ID(複数可)を記す。
context | 不透明なオブジェクトへのポインタもしくは NULL。これを用いて ICE 接続の共有が可能か判断する(後述)。
must_authenticate | `True` の時、相手方のクライアントは認証を省くことができない。
major_opcode_check | 新しい ICE 接続の生成を義務づけるために使用する(後述)。
error_length | 引数 error_string_ret の長さ。
error_string_ret | null 文字終端のエラーメッセージを返す(もし存在すれば)。引数 error_string_ret は利用者の用意した記憶領域を参照する。error_length の数値分のバイトしか使用しない。

`IceOpenConnection` は、成功すれば、不透明な(opaque) ICE 接続オブジェクトを返す。失敗すれば、NULL を返す。

引数 network_ids_list には、コンマで区切ったネットワーク ID 群の並びを渡す。まず先頭のネットワーク ID を使って接続を試みる。これが失敗すれば、二番目のネットワーク ID を使って試行し、これが失敗すれば以下同様。各ネットワーク ID は次の書式をとる。

tcp/<hostname>:<portnumber> | もしくは
decnet/<hostname>::<objname> | もしくは
local/<hostname>:<path> |

大抵のプロトコル・ライブラリは、内部で `IceOpenConnection` を呼び出す、ある種の開設関数(open function)を備えることになるであろう。`IceOpenConnection` を呼び出した時、既設の ICE 接続の利用が認められることもある(接続相手のクライアントが同一の場合)。一方、ICE 接続の共用が望ましくないこともある。

引数 context によって、ICE 接続の共用の可否を判断する。context が NULL であれば、当関数の呼出者は常に接続の共有を試みる。context が NULL でなければ、呼出方は、自身が指定したものとは異なる、NULL でない context と結びついた既設の ICE 接続を用いない。

加えて、major_opcode_check に 0 でない主命令コードの値が渡された場合、既設の ICE 接続でその主命令コードが使用されていない時に限って、同接続を利用する。これを使えば、二つのクライアントの間で、同一プロトコルを用いた ICE 接続を複数設けるよう指示することも可能である。

認証に必要な処理は ICE ライブラリの内部で行われる。認証データを取得する方法は実装によって異なる。[1]

`IceOpenConnection` の呼出しが終わると、クライアントは `ProtocolSetup` を送受信できるようになる(`IceRegisterForProtocolSetup` を呼び出している方は送信、`IceRegisterForProtocolReply` を呼び出している方は受信が可能)。

## ICE 接続の待受け

ICE 接続を受け入れようとするクライアントは、接続要求を察知できるように、まず `IceListenForConnections` または `IceListenForWellKnownConnections` を呼び出さなければならない。これらの関数の結果、不透明な待受けオブジェクト(opaque "listen" objects)の配列が取得できる。利用可能な伝送方式(transport method)の種類一つにつき(例、Unix Domain、TCP、DECnet など)、一つのオブジェクトが割り当てられる。

通常、クライアントは、各伝送方式中の利用可能な名前(訳註：ポート番号、オブジェクト名、経路名)の生成と、待受けオブジェクト群(listen objects)の取得を ICElib に依頼する。そうしたクライアントは次に、`IceComposeNetworkIdList` を用いて ICElib が定めた名前(訳註：ポート番号、オブジェクト名、経路名)を取り出し、接続開設を試みる他のクライアントから、それらの名前が利用できるようにする。時 には、予め決められた伝送オブジェクト名(transport object names)で接続を待ち受けなければならないこともあろう。そうしたクライアントは、`IceListenForWellKnownConnections` を用いて、待受けオブジェクト群(listen objects)の名前(訳註：ポート番号、オブジェクト名、経路名)を指定することができる。

`Status IceListenForConnections(`int *count_ret, IceListenObj **listen_objs_ret, int error_length, char *error_string_ret`)`;

count_ret | 生成された待受けオブジェクト(listen objects)の数を返す。
listen_objs_ret | 不透明な待受けオブジェクト(opaque listen objects)へのポインタの配列を返す。
error_length | 引数 error_string_ret の長さ。
error_string_ret | エラーがあれば、null 文字終端のエラーメッセージを返す。ポインタ error_string_ret は利用者の用意した領域を指す。error_length の数値分のバイト数しか使用されない。

`IceListenForConnections` の戻り値は、失敗すれば 0、成功すれば正の値である。

`Status IceListenForWellKnownConnections(`char *port_id, int *count_ret, IceListenObj **listen_objs_ret, int error_length, char *error_string_ret`)`;

port_id | 接続を待つアドレスのポート識別子を渡す。文字列中には、スラッシュ("/")やコンマ(".")が含まれてはいけない。これらは将来の利用に備えて使わずにおく。
count_ret | 生成された待受けオブジェクト(listen objects)の数を返す。
listen_objs_ret | 不透明な待受けオブジェクト(opaque listen objects)へのポインタの配列を返す。
error_length | 引数 error_string_ret の長さ。
error_string_ret | エラーがあれば、null 文字終端のエラーメッセージを返す。ポインタ error_string_ret は、利用者の用意した記憶領域を指す。error_length の数値分のバイト数しか使用されない。

`IceListenForWellKnownConnections` は、既知の伝送方式(transport)の一つ一つを port_id の頭に付加する方法で、ネットワーク ID の一覧を構成し、その結果を用いて待受けオブジェクト群(listen objects)を作成する。port_id には、ICE ネットワーク ID 中のポート番号(portnumber)、オブジェクト名(objname)、もしくは経路(path)の部分が入る。あるネットワーク ID に対して待受けオブジェクト(listen object)が作成できなかった場合、そのネットワーク ID は無視する。一つも待受けオブジェクトが作成されなければ、`IceListenForWellKnownConnections` は失敗を意味する戻り値を返す。

`IceListenForWellKnownConnections` の戻り値は、失敗すれば 0、成功すれば正の値である。

待受けオブジェクトを閉鎖・解放するには、`IceFreeListenObjs` を用いる。

`void IceFreeListenObjs(`int count, IceListenObj *listen_objs`)`;

count | 待受けオブジェクト(listen object)の数。
listen_objs | 待受けオブジェクト群(listen objects)。

待受けオブジェクト(listen object)に対する新たな接続を検出するには、その待受けオブジェクトに結び付けられている記述子に対して、`select` を使用する。

記述子を取得するには、`IceGetListenConnectionNumber` を用いる。

`int IceGetListenConnectionNumber(`IceListenObj listen_obj`)`;

listen_obj | 待受けオブジェクト(listen object)。

待受けオブジェクトに結び付けられたネットワーク ID の文字列表現を取得するには、`IceGetListenConnectionString` を用いる。

`char *IceGetListenConnectionString(`IceListenObj listen_obj`)`;

listen_obj | 待受けオブジェクト(listen object)。

ネットワーク ID は次の書式で記述される。

tcp/<hostname>:<portnumber> | もしくは
decnet/<hostname>::<objname> | もしくは
local/<hostname>:<path> |

ネットワーク ID 群をコンマで区切って並べた文字列(`IceOpenConnection` に渡せる形式)を作るには、`IceComposeNetworkIdList` を用いる。

`char *IceComposeNetworkIdList(`int count, IceListenObj *listen_objs`)`;

count | 待受けオブジェクト(listen objects)の数。
listen_objs | 待受けオブジェクト群(listen objects)。

## ホスト名に基づく認証(ICE 接続)

ICE 接続の開設を試みた時、認証に失敗し、且つ開始方クライアントが認証を要求していない場合、開始方クライアントに接続の最後の機会を与えるべく、ホスト名 に基づく認証手続き(host based authentication procedure)を呼び出すことができる。各待受けオブジェクトには、この種のコールバックが一つ備わっている。関数 `IceSetHostBasedAuthProc` を用いて、このコールバックの設定を行う。

`void IceSetHostBasedAuthProc(`IceListenObj listen_obj, IceHostBasedAuthProc host_based_auth_proc`)`;

IceListenObj | 待受けオブジェクト(listen object)。
host_based_auth_proc | ホスト名に基づく認証手続き。

各待受けオブジェクトは、初期状態において、ホスト名に基づく認証手続きを何ら有していない。既にホスト名認証が 設定されている場合、引数 host_based_auth_proc に NULL を渡すことによって、同認証手続きを消去することができる。

`typedef Bool (*IceHostBasedAuthProc)(`char *host_name`)`;

host_name | ICE 接続の開設を試みたクライアントのホスト名。

引数 host_name には「protocol/ hostname」の形をとる文字列を渡す。ここでの「protocol」には、{tcp、decnet、local} の何れか一つが入る。

`IceHostBasedAuthProc` から `True` が返れば、たとえ元々の認証に失敗していても、接続は許可される。常に `True` を返す `IceHostBasedAuthProc` を登録することで、認証機能を実質的に無効にすることができる点に注意する。

ホスト名に基づく認証は `ProtocolSetup` の段階でも使用可能である。この段階で使うコールバックは、関数 `IceRegisterForProtocolReply` で指定する(プロトコルの登録を見よ)。

## ICE 接続の受入れ

`IceListenForConnections` で取得した待受けオブジェクト(listen object)にて接続の試みを検知した後、`IceAcceptConnection` を呼び出す。この関数は、新しく不透明な ICE 接続オブジェクト返す。

`IceConn IceAcceptConnection(`IceListenObj listen_obj, IceAcceptStatus *status_ret`)`;

listen_obj | 新たな接続を検知した待受けオブジェクト(listen object)。
status_ret | 戻り値たる状態情報。

引数 status_ret は、次の値のどれか一つに設定される。

- `IceAcceptSuccess` - 受入れ作業は成功し、この関数は新しい接続オブジェクトを返す。

- `IceAcceptFailure` - 受入れ作業は失敗し、この関数は NULL を返す。

- `IceAcceptBadMalloc` - 記憶領域の確保が失敗し、この関数は NULL を返す。

一般に、新しい接続を検知するには、待受けオブジェクトに結び付けられたファイル記述子に対して `select` を実行する。新しい接続が見つかった時は、関数 `IceAcceptConnection` を呼び出す。`IceAcceptConnection` は、仮設状態の新 ICE 接続を返すことがある。これは、接続が有効になる前の段階で、認証を行うことがあるためである。ICE ライブラリは仮設状態の接続が有効になるまでの間、他の接続要求を弾くわけにはいかないので(接続を試みるクライアントが不適切な動きをすると、際限なく 接続要求をブロックしてしまう(接続が有効になるまで関数から返らない))、アプリケーション側で接続が有効な状態に変わるのを待たなければならない。

次の擬似コードによって、接続を受理する過程を例示する。

    new_ice_conn = IceAcceptConnection (listen_obj, &accept_status);
    if (accept_status != IceAcceptSuccess)
    {
         ファイル記述子を閉じて戻る
    }

    status = IceConnectionStatus (new_ice_conn);
    time_start = time_now;

    while (status == IceConnectPending)
    {
         select() on {new_ice_conn, all open connections}

         for (each ice_conn in the list of open connections)
              if (data ready on ice_conn)
              {
                   status = IceProcessMessages (ice_conn, NULL, NULL);
                   if (status == IceProcessMessagesIOError)
                        IceCloseConnection(ice_conn);
              }
         if data ready on new_ice_conn
         {
              /*
              * IceProcessMessages は、接続が仮設でなくなる
              * まで呼び出される。これによって、接続設定要求
              * と認証手続きを処理する。
              */

              IceProcessMessages ( new_ice_conn, NULL, NULL);
              status = IceConnectionStatus (new_ice_conn);
         }
         else
         {
              if (time_now - time_start > MAX_WAIT_TIME)
                   status = IceConnectRejected;
         }
    }

    if (status == IceConnectAccepted)
    {
         Add new_ice_conn to the list of open connections
    }
    else
    {
         IceCloseConnection
         new_ice_conn
    }

`IceAcceptConnection` が呼び出され、接続が有効になると、クライアントは `ProtocolSetup` を送受信できるようになる(`IceRegisterForProtocolSetup` を呼び出している方は送信、`IceRegisterForProtocolReply` を呼び出している方は受信が可能)。

## ICE 接続の閉鎖

`IceOpenConnection` や `IceAcceptConnection` で作成された ICE 接続を閉じるには、`IceCloseConnection` を用いる。

`IceCloseStatus IceCloseConnection(`IceConn ice_conn`)`;

ice_conn | 閉鎖する ICE 接続。

ある接続を実際に閉じるには、以下の条件が満たされなければならない、

- 同 ICE 接続の開設参照数(open reference count)が 0 に達しなければならない。`IceOpenConnection` が呼び出された時、まず既に開かれている ICE 接続の利用を試みる。既存の接続が利用可能であれば、`IceOpenConnection` は同接続の開設参照数を一つ増やす。そのため、ある ICE 接続を閉じるには、`IceOpenConnection` の各呼出しに対して、それに応じた `IceCloseConnection` が一つ呼び出されなければならない。このような接続は、最後の `IceCloseConnection` を受けて初めて切断可能になる。

- 同 ICE 接続の実効プロトコル数(active protocol count)が 0 に達しなければならない。同接続において `ProtocolSetup` が成功する度に、実効プロトコル数が一つ増える。クライアントは、同接続上の使う予定のなくなったプロトコルに対して、関数 `IceProtocolShutdown` を呼び出し、これによって実効プロトコル数を一つ減らす(プロトコルの設定と終了を見よ)。

- 同 ICE 接続において終了交渉(shutdown negotiation)が可能である場合、接続相手のクライアントが接続切断に同意しなければならない。

`IceCloseConnection` は、次の値の何れか一つを返す。

- `IceClosedNow` - ICE 接続は、この時点で閉鎖された。看視手続き(watch procedures)が呼び出され、接続資源が解放された。

- `IceClosedASAP` - ICE 接続上で入出力エラーが発生したが、まだ `IceProcessMessages` の入れ籠の内から `IceCloseConnection` が呼び出されている最中である。この時点で看視手続きは既に呼び出されているが、接続は(訳註：まだ解放されておらず、これから)「できる限り早く」解放される(`IceProcessMessages` の入れ籠の層が 0 に達し、同関数が `IceProcessMessagesConnectionClosed` を返した時)。

- `IceConnectionInUse` - 他の生きたプロトコルがこの接続を使用しているため、今回は切断されなかった。

- `IceStartedShutdownNegotiation` - この時点では接続は閉鎖されなかった。そして、ICE 接続の相手方クライアントとの終了交渉(shutdown negotiation)が始まった。実際に接続が切断された時は、`IceProcessMessages` が `IceProcessMessagesConnectionClosed` を返す。

ICE 接続の相手方クライアントが終了交渉(shutdown negotiation)を提起することなく接続を断ったことが分かった時は、関数 `IceSetShutdownNegotiation` を呼び出して、終了交渉を行わない設定に切り替える。これによって、`IceCloseConnection` が壊れた接続に対して書き込んでしまうことを防ぐ。

`void IceSetShutdownNegotiation(`IceConn ice_conn, Bool negotiate`)`;

ice_conn | 有効な ICE 接続オブジェクト。
negotiate | `False`を渡せば、終了交渉は行われなくなる。

ICE 接続の終了交渉に関する設定を調べるには、`IceCheckShutdownNegotiation` を用いる。

`Bool IceCheckShutdownNegotiation(`IceConn ice_conn`)`;

ice_conn | 有効な ICE 接続オブジェクト。

`IceCheckShutdownNegotiation` が `True` を返すのは、引数で指定した接続において終了交渉が行われるよう設定されている時である。そうでない場合、`False` が返る。どの接続も、初期状態では交渉を行う設定になっている。この設定は、関数 `IceSetShutdownNegotiation` を使わなければ変更できない。

## 接続看視手続き

看視手続き(watch procedure)を新たに登録するには、`IceAddConnectionWatch` を用いる。登録された看視手続きは、ICElib が `IceOpenConnection` や `IceAcceptConnection` によって新しい接続を開く度に、あるいは `IceCloseConnection` によって接続を閉じる度に、呼び出されることになる。

`Status IceAddConnectionWatch(`IceWatchProc watch_proc, IcePointer client_data`)`;

watch_proc | ICElib で接続が開閉される時に呼び出される看視手続き(watch procedure)。
client_data | このポインタは看視手続き呼出し時にその引数として渡される。

`IceAddConnectionWatch` の戻り値は、失敗すれば 0、成功すれば正の値である。

幾度かの `IceOpenConnection` の呼出しの結果、同一の ICE 接続が共用されることもあるという点に注意する。このような場合、看視手続きは、最初に接続が確立された時(認証成功後)にしか呼び出されない。同様に、接続が共有されている可能性があるので、`IceCloseConnection` を通じて実際に接続が切断された時に限り、看視手続きが呼び出される(IceConn の解放直前)。

新しい接続が作成された時に select mask にファイル記述子を加え、接続が破棄された時にファイル記述子を取り除くアプリケーションにとって、この看視手続きはとても役に立つものである。接続が共 有されている可能性があるので、select mask に対して何時ファイル記述子を加えたり除いたりすべきなのかを、看視手続き無しに知るのは難しい。

ICE ライブラリに複数の看視手続き(watch procedures)を登録できる。手続きが複数ある時の呼出し順序は、何ら想定すべきでない。

看視手続き登録の時点において、ICE ライブラリに一つでも ICE 接続が作成されていれば、そうした ICE 接続の一つ一つに対して、直ちに同看視手続きが呼び出される。その際、引数 opening に `True` を渡す。

看視手続きの関数型は `IceWatchProc` である。

`typedef void (*IceWatchProc)(`IceConn ice_conn, IcePointer client_data, Bool opening, IcePointer *watch_data`)`;

ice_conn | 開設あるいは閉鎖される ICE 接続。この接続と結び付いたファイル記述子を取得するには、`IceConnectionNumber` を呼び出す。
client_data | `IceAddConnectionWatch` の呼出し時に指定したクライアント・データへのポインタ。
opening | 接続が開かれる際の呼出しでは `True`、閉じる際の呼出しでは `False`。
watch_data | クライアント・データへのポインタを保存するために使用できる。

opening が `True` の時、クライアントは、接続終了まで保持する必要のあるデータを指すように、ポインタ *watch_data を設定する。看視手続きはその後、opening に `False` を渡して、再び呼び出される。

看視手続きを削除するには、`IceRemoveConnectionWatch` を用いる。

`void IceRemoveConnectionWatch(`IceWatchProc watch_proc, IcePointer client_data`)`;

watch_proc | `IceAddConnectionWatch` で登録した看視手続き。
client_data | `IceAddConnectionWatch` で登録したポインタ client_data。

[1] X Consortium(X 協会)の ICElib の実装では、「 .ICEauthority 」ファイルを使用する(附録 Aを見よ)。

## 第八章 プロトコルの設定と終了

ある ICE 接続においてプロトコル一つを有効化するには、`IceProtocolSetup` を用いる。

`IceProtocolSetupStatus IceProtocolSetup(`IceConn ice_conn, int my_opcode, IcePointer client_data, Bool must_authenticate, int *major_version_ret, int *minor_version_ret, char **vendor_ret, char **release_ret, int error_length, char *error_string_ret`)`;

ice_conn | 有効な ICE 接続オブジェクト。
my_opcode | 設定するプロトコルの主命令コード(major opcode)。これは `IceRegisterForProtocolSetup` で返されたもの。
client_data | このポインタで記録しているクライアント・データは、コールバック `IcePoProcessMsgProc` の引数となる。
must_authenticate | もし `True` であれば、相手方クライアントは認証を飛ばしてはいけない。
major_version_ret | 戻り値として、使用するプロトコルの版番号の上位が格納される。
minor_version_ret | 戻り値として、使用するプロトコルの版番号の下位が格納される。
vendor_ret | vendor(開発者)を示す文字列。プロトコルの受理方が記す。
release_ret | release(公開版)を示す文字列。プロトコルの受理方が記す。
error_length | 引数 error_string_ret の長さを示す。
error_string_ret | エラーがあれば、null 文字終端のエラーメッセージを返す。引数 error_string_ret は利用者の用意した領域を参照する。error_length の数値分のバイト数しか使用しない。

文字列 vendor_ret と文字列 release_ret は、必要でなくなった時は `free` を用いて解放するものとする。

`IceProtocolSetup` は、次の値の何れか一つを返す。

- `IceProtocolSetupSuccess` - major_version_ret、minor_version_ret、vendor_ret、release_ret が設定された。

- `IceProtocolSetupFailure` もしくは `IceProtocolSetupIOError` - 蹉跌の理由が error_string_ret に入っている。major_version_ret、minor_version_ret、vendor_ret、release_ret は設定されず。

- `IceProtocolAlreadyActive` - 引数で指定された ICE 接続において、このプロトコルは既に有効である。major_version_ret、minor_version_ret、vendor_ret、release_ret は設定されず。

ICE 接続上のあるプロトコルが最早使われなくなった時は、`IceProtocolShutdown` を用いて ICE ライブラリにその旨を通知する。

`Status IceProtocolShutdown(`IceConn ice_conn, int major_opcode`)`;

ice_conn | 有効な ICE 接続オブジェクト。
major_opcode | 終了するプロトコルの主命令コード(major opcode)。

`IceProtocolShutdown` の戻り値は、失敗すれば 0、成功すれば正の値である。

指定された主命令コードが登録されていないものであった場合、あるいは、指定された主命令コードを持つプロトコルが、指定された ICE 接続において一度も有効化されていない場合、`IceProtocolShutdown` は失敗する。有効化された(activated)とは、同接続において `ProtocolSetup` に成功した状態のことを指す。ICE では、各下位プロトコル(sub-protocol)がどのようにしてプロトコルの終了を提起するかについては、定めていないことに注意する。

## 第九章 メッセージの処理

ICE 接続上で受け取ったメッセージを処理するには、`IceProcessMessages` を用いる。

`IceProcessMessagesStatus IceProcessMessages(`IceConn ice_conn, IceReplyWaitInfo *reply_wait, Bool *reply_ready_ret`)`;

ice_conn | 有効な ICE 接続オブジェクト。
reply_wait | 呼出し元のクライアントが返答を待っているのか否かを表す。
reply_ready_ret | 戻り値として `True` に設定された時は、返答が用意できたということである。

`IceProcessMessages` には、二つの使い方がある。

- 第一の使い方。クライアントは、メッセージを生成した後、その返答を得るまで繰り返し `IceProcessMessages` を呼び出して、ブロック(入力待機)することができる。(訳註：使用例は X11R7.7/lib/libSM-1.2.1/src/sm_client.c がある。)

- 第二の使い方。ICE 接続上に読み込むべきデータが存在することを知らせる `select` を受けて、クライアントは reply_wait に NULL を渡して `IceProcessMessages` を呼び出す。ICE ライブラリはメッセージの全身(complete message)を 0 個以上処理できる。ブロックされないメッセージは(返答の入力待機が為されないメッセージは)、常にコールバックを呼び出して処理する。

`IceReplyWaitInfo` 構造体にはメッセージの主/従命令コードと通し番号が入っているが、これは、現在待ち受けている返信が為される所以となったメッセージのものである。同構造体には、返信メッセージが書き込まれる領域へのポインタも含まれている(プロトコル・ライブラリにおいて、この `IcePointer` を返信用の適当な型に読み替える)。大抵、返信メッセージには固定長の部分が存在し、そして、この固定長データ部分を内部に保持する構造体へのポインタは、返信を待っているクライアントが用意する。可変長のデータがある場合、コールバック `IcePoProcessMsgProc` において追加の記憶領域が確保され、その領域へのポインタが先の固定長構造体の中に格納される。もしデータが丸ごと可変長であれば(例えば可変長文字列一 つ)、返信を待ち受ける方のクライアントは、ポインタ一つを保持できる固定長領域へのポインタを渡すだけで良い。そして、コールバック `IcePoProcessMsgProc` が記憶領域を新たに確保し、その領域へのポインタを(渡されたポインタの指し示す固定長領域に)書き込む。返信を受け取ったクライアントは、自身のために 割り当てられた記憶領域の解放作業を引き受けるものとする。(訳註：X11R7.7/lib/libICE-1.0.8/src/process.c の場合、IceProcessMessages の内部では、主命令コード等に従って _IceProcessCoreMsgProc 型関数、IcePoProcessMsgProc 型関数、もしくは IcePaProcessMsgProc 型関数を呼び出している。)

    typedef struct {
         unsigned long sequence_of_request;
         int major_opcode_of_request;
         int minor_opcode_of_request;
         IcePointer reply;
    } IceReplyWaitInfo;

reply_wait が NULL ではなく、且つ `IceProcessMessages` において同 reply_wait に対する返信もしくはエラー報告がある場合(つまりコールバックが生成されなかった場合)、引数 reply_ready_ret を `True` に設定する。

reply_wait が NULL であれば、この関数を呼び出した者は reply_ready_ret にも NULL を渡しているはずであり、このポインタには何も格納されないことが保証される。

`IceProcessMessages` は次の値の何れか一つを返す。

- `IceProcessMessagesSuccess` - エラーは起きなかった。

- `IceProcessMessagesIOError` - 入出力エラーが発生した。この関数を呼び出した者は `IceCloseConnection` を使用して、明示的に接続を閉じなければならない。

- `IceProcessMessagesConnectionClosed` - ICE 接続は既に切断されている(終了交渉の故に、あるいは `IceProcessMessages` の入れ籠階層が 0 でない為に、接続の切断が遅れている)。ICE 接続資源は既に解放されているので、この時点で接続に対する操作を試みてはいけない。

## 第十章 Ping

ICE 接続の相手方クライアントに対して「Ping」メッセージを送信するには、`IcePing` を用いる。

`Status IcePing(`IceConn ice_conn, IcePingReplyProc ping_reply_proc, IcePointer client_data`)`;

ice_conn | 有効な ICE 接続オブジェクト。
ping_reply_proc | Ping の応答が届いた時に呼び出すコールバック。
client_data | このポインタはコールバック `IcePingReplyProc` に渡される。

`IcePing` は失敗すれば 0 を、成功すれば正の値を返す。

`IceProcessMessages` では Ping の応答を処理するために、`IcePingReplyProc` を呼び出す。

`typedef void (*IcePingReplyProc)(`IceConn ice_conn, IcePointer client_data`)`;

ice_conn | 有効な ICE 接続オブジェクト。
client_data | `IcePing` の呼出し時に指定した、クライアント・データへのポインタ。

## 第十一章 ICElib の情報取得関数を使う

`IceConnectStatus IceConnectionStatus(`IceConn ice_conn`)`;

`IceConnectionStatus` は ICE 接続の状態値を返す。戻り値には以下のものがある。

- `IceConnectPending` - 接続は未だ有効ではない(つまり、認証作業中である)。これは `IceAcceptConnection` によって作成された接続に対してしか意味を持たない。

- `IceConnectAccepted` - 接続は受け入れられた。これは `IceAcceptConnection` によって作成された接続に対してしか意味を持たない。

- `IceConnectRejected` - 接続は拒否された(つまり、認証に失敗した)。これは `IceAcceptConnection` によって作成された接続に対してしか意味を持たない。

- `IceConnectIOError` - 引数で指定された接続において、入出力エラーが発生した。

`char *IceVendor(`IceConn ice_conn`)`;

`IceVendor` は、引数で指定した接続の ICE ライブラリの開発者識別文字列を返す。取得した文字列は、用が済んだところで、`free` を呼び出して解放する。

`char *IceRelease(`IceConn ice_conn`)`;

`IceRelease` は、同接続の ICE ライブラリの公開版番号識別文字列を返す。取得した文字列は、用が済んだところで、`free` を呼び出して解放する。

`int IceProtocolVersion(`IceConn ice_conn`)`;

`IceProtocolVersion` は、引数で指定した接続において用いられている ICE プロトコルの版番号の上位(major version)を返す。

`int IceProtocolRevision(`IceConn ice_conn`)`;

`IceProtocolRevision` は、引数で指定した接続において用いられている ICE プロトコルの版番号の下位(minor version)を返す。

`int IceConnectionNumber(`IceConn ice_conn`)`;

`IceConnectionNumber` は、引数で指定された ICE 接続のファイル記述子を返す。

`char *IceConnectionString(`IceConn ice_conn`)`;

`IceConnectionString` は、接続を受理した方のクライアントのネットワーク ID を返す。取得した文字列は、用が済んだところで、`free` を呼び出して解放する。

`unsigned long IceLastSentSequenceNumber(`IceConn ice_conn`)`;

`IceLastSentSequenceNumber` は、引数で指定された ICE 接続において最後に送信されたメッセージの通し番号を返す。

`unsigned long IceLastReceivedSequenceNumber(`IceConn ice_conn`)`;

`IceLastReceivedSequenceNumber` は、引数で指定された ICE 接続において最後に受信されたメッセージの通し番号を返す。

`Bool IceSwapping(`IceConn ice_conn`)`;

`IceSwapping` は、引数で指定された ICE 接続において、メッセージを読み込む際にバイト順の交換が必要である場合、`True` を返す。

`IcePointer IceGetContext(`IceConn ice_conn`)`;

`IceGetContext` は、`IceOpenConnection` で作成された接続に結び付けられている文脈(context)を返す。(訳註：X11R7.7/lib/libICE-1.0.8/include/ X11/ICE/ICElib.h 及び X11R7.7/lib/libICE-1.0.8/src/connect.c では、関数名は `IceGetConnectionContext` である。)

## 第十二章 ICE メッセージ

全ての ICE メッセージには、8 バイトの標準ヘッダがある。メッセージの読み書きを行う ICElib のマクロは、以下に掲げる命名規則に則って、メッセージ・ヘッダを解釈する。

         CARD8	major_opcode;
         CARD8	minor_opcode;
         CARD8	data[2];
         CARD32	length B32;

メッセージ・ヘッダの三番目と四番目のバイト(訳註：上記の data[2])は、必要に応じて使用する。length 欄の値は 8 バイト単位で記す。

## ICE メッセージを送る

ICE ライブラリは、メッセージの生成に用いる出力バッファ(output buffer)を保持している。ICE を基層とするプロトコルのライブラリでは、メッセージ群をまとめて一括処理することや、出力バッファを適宜放流する(flush)ことが可能である。

ICE 接続において入出力エラーが発生した場合、以後の全ての書込み操作は無視される。更なる情報は、エラー処理を見よ。

ICE 出力バッファの容量を知るには、`IceGetOutBufSize` を用いる。

`int IceGetOutBufSize(`IceConn ice_conn`)`;

ice_conn | 有効な ICE 接続オブジェクト。

ICE 出力バッファを放流する(flush)には、`IceFlush` を用いる。

`int IceFlush(`IceConn ice_conn`)`;

ice_conn | 有効な ICE 接続オブジェクト。

出力バッファは、メッセージの生成に不十分な空き領域しかない場合、暗々裏に放流されることがあるので注意する。

以下のマクロは ICE メッセージの生成に使用できる。

`IceGetHeader(`IceConn ice_conn, int major_opcode, int minor_opcode, int header_size, <C_data_type> *pmsg`)`;

ice_conn | 有効な ICE 接続オブジェクト。
major_opcode | メッセージの主命令コード。
minor_opcode | メッセージの従命令コード。
header_size | メッセージ・ヘッダの大きさ(バイト単位)。
<C_data_type> | C 言語における、メッセージ・ヘッダの実際のデータ型。
pmsg | メッセージ・ヘッダの領域を指すポインタ。 このマクロの呼出しの後、ライブラリはメッセージ・ヘッダ中にデータを格納できるようになる。

`IceGetHeader` を用いて、引数で指定した ICE 接続におけるメッセージ・ヘッダを準備する。この準備作業では、メッセージ(*pmsg)の主・従命令コードを設定し、メッセージ長を表す(*pmsg の) length 欄をヘッダ長と同じ値に合わせる。この後ろに可変長のデータが続く場合は、メッセージの length 欄を更新するものとする。

`IceGetHeaderExtra(`IceConn ice_conn, int major_opcode, int minor_opcode, int header_size, int extra, <C_data_type> *pmsg, char *pdata`)`;

ice_conn | 有効な ICE 接続オブジェクト。
major_opcode | メッセージの主命令コード。
minor_opcode | メッセージの従命令コード。
header_size | メッセージ・ヘッダの大きさ(バイト単位)。
extra | ヘッダ部分を除いた、後続データの大きさ(バイト単位)。
<C_data_type> | C 言語における、メッセージ・ヘッダの実際のデータ型。
pmsg | メッセージ・ヘッダの領域を指すポインタ。 このマクロの呼出しの後、ライブラリはメッセージ・ヘッダ中にデータを格納できるようになる。
pdata | メッセージ・ヘッダの領域の直後に位置する ICE 出力バッファへのポインタが返る。可変長データはここに格納される。ICE 出力バッファに十分な空き領域がなかった場合、pdata は NULL に設定される。

`IceGetHeaderExtra` を用いて、大きさの確定した(比較的小さな)可変長データを伴うメッセージを生成する。このメッセージの全身(complete message)は ICE 出力バッファに納まらなければならない。

`IceSimpleMessage(`IceConn ice_conn, int major_opcode, int minor_opcode`)`;

ice_conn | 有効な ICE 接続オブジェクト。
major_opcode | メッセージの主命令コード。
minor_opcode | メッセージの従命令コード。

`IceSimpleMessage` を用いて、ICE ヘッダ・メッセージと同じ大きさの、且つそれ以上の後続データを持たないメッセージを生成する。

`IceErrorHeader(`IceConn ice_conn, int offending_major_opcode, int offending_minor_opcode, int offending_sequence_num, int severity, int error_class, int data_length`)`;

ice_conn | 有効な ICE 接続オブジェクト。
offending_major_opcode | エラーが検出されたプロトコルの主命令コード。
offending_minor_opcode | エラーが検出されたプロトコルにおける従命令コード。
offending_sequence_num | エラーを起こしたメッセージの通し番号。
severity | `IceCanContinue`、`IceFatalToProtocol` もしくは `IceFatalToConnection`
error_class | エラー・クラス(error class)。
data_length | ヘッダに続く領域に書き込まれるデータの長さ(8 バイト単位)。

`IceErrorHeader` によって、エラー・メッセージのヘッダを準備する。

ICE で接続された二者のクライアントが、同一のプロトコルに対して相異なる主命令コードを用いている可能性のあることに注意する。上記マクロに渡す offending_major_opcode は、エラー・メッセージを送信した方のクライアントに割り当てられた主命令コードである。

全てのプロトコルに共通の一般エラー(generic errors)には、0x8000~0xFFFF の範囲の値で表されるクラス(class)がある。詳細は規格文書 Inter-Client Exchange Protocol を見よ。

IceBadMinor | 0x8000
IceBadState | 0x8001
IceBadLength | 0x8002
IceBadValue | 0x8003

プロトコル毎のエラーのクラスは、0x0000~0x7fff の範囲で表す。

ICE 接続にデータを書き込むには、マクロ `IceWriteData` を用いる。データが ICE 出力バッファに収まる時は、そこにデータを転写する。そうでない時は、ICE 出力バッファを放流した上で、直接データを送信する。

このマクロは `IceGetHeader` 及び `IceErrorHeader` と組み合わせて使用する。

`IceWriteData(`IceConn ice_conn, int bytes, char *data`)`;

ice_conn | 有効な ICE 接続オブジェクト。
bytes | 書き込むデータのバイト数。
data | 書き込むデータ。

データを 16 ビット単位で書き込むには、`IceWriteData16` を用いる。

`IceWriteData16(`IceConn ice_conn, int bytes, char *data`)`;

ice_conn | 有効な ICE 接続オブジェクト。
bytes | 書き込むデータのバイト数。
data | 書き込むデータ。

データを 32 ビット単位で書き込むには、`IceWriteData32` を用いる。

`IceWriteData32(`IceConn ice_conn, int bytes, char *data`)`;

ice_conn | 有効な ICE 接続オブジェクト。
bytes | 書き込むデータのバイト数。
data | 書き込むデータ。

ICE 出力バッファへの転写作業を省くには、`IceSendData` を用い、ネットワーク接続を通じて直接データを送信する。この際、必要であれば、まず ICE 出力バッファが放流される。

`IceSendData(`IceConn ice_conn, int bytes, char *data`)`;

ice_conn | 有効な ICE 接続オブジェクト。
bytes | 送信するデータのバイト数。
data | 送信するデータ。

32 ビットもしくは 64 ビットの境界に揃えるには、`IceWritePad` を用いる。詰め物は最大で 7 バイトまで指定できる。

`IceWritePad(`IceConn ice_conn, int bytes`)`;

ice_conn | 有効な ICE 接続オブジェクト。
bytes | 書き込む詰め物のバイト数。

## ICE メッセージを読む

ICE ライブラリは、メッセージの読み込みに用いる入力バッファを保持している。ICE ライブラリが、入力待機せずに適宜読み込みを行う様に実装された場合(nonblocking reads)、読み込み作業の度に 0 個以上のメッセージの全身(complete message)が入力バッファに読み込まれる。結果として、この節で説明する、メッセージ読み込みを行うマクロの全てについて、次のことが言える。即 ち、引数で指定された接続において実際の読み込み作業が行われるのは、入力バッファ中にデータが既に存在しない時に限られる。

ICE 入力バッファの容量を知るには、`IceGetInBufSize` を用いる。

`int IceGetInBufSize(`IceConn ice_conn`)`;

ice_conn | 有効な ICE 接続オブジェクト。

メッセージを読み込む時は、入出力エラーの有無を確認するよう気をつけなければならない。読込みの際に、メッセー ジ中の何処かで何らかの入出力エラーが発生した場合、そのメッセージは捨てるものとする。以下で説明するマクロの何れかを用いてメッセージを読み込んだ後 には、マクロ `IceValidIO` を用いて、接続上で入出力エラーが発生したか否かを調べることができる。ある ICE 接続において入出力エラーが発生してしまった後は、全ての読み込み操作は無視される。更なる情報は、エラーの処理を見よ。

`Bool IceValidIO(`IceConn ice_conn`)`;

ice_conn | 有効な ICE 接続オブジェクト。

以下のマクロを用いて ICE メッセージを読み込むことができる。

`IceReadSimpleMessage(`IceConn ice_conn, <C_data_type> *pmsg`)`;

ice_conn | 有効な ICE 接続オブジェクト。
<C_data_type> | C 言語における、メッセージ・ヘッダの実際のデータ型。
pmsg | このポインタはメッセージ・ヘッダに合わせられる。

`IceReadSimpleMessage` を用いて、次のようなメッセージを処理する。即ち、大きさが ICE ヘッダと同じ 8 バイトであり、且つヘッダ中の予備の 2 バイトを使って付加データを保持しているメッセージを処理する。ICE ライブラリは、メッセージの主命令コードを得るために、常にこの最初の 8 バイトを読み込む、という点に注意する。`IceReadSimpleMessage` は、単にこの 8 バイトへのポインタを返すだけであり、実際には如何なるデータも入力バッファに読み込まない。

可変長データを伴うメッセージの読込みには、二通りのやり方がある。一つは、`IceReadCompleteMessage` を用いて、メッセージの全身を一度に読み込む方法である。もう一つは、先にメッセージ・ヘッダを読み込み(ICE ヘッダの 8 バイトより大きいこともある)、次いで可変長データ部分を適度に区切って読み込む方法である。後者については、`IceReadMessageHeader` と `IceReadData` を見よ。

`IceReadCompleteMessage(`IceConn ice_conn, int header_size, <C_data_type> *pmsg, char *pdata`)`;

ice_conn | 有効な ICE 接続オブジェクト。
header_size | メッセージ・ヘッダの大きさ(バイト単位)。
<C_data_type> | C 言語における、メッセージ・ヘッダの実際のデータ型。
pmsg | このポインタはメッセージ・ヘッダに合わせられる。
pdata | このポインタはメッセージの可変長データ部分に合わせられる。

ICE 入力バッファに十分な空きがあれば、`IceReadCompleteMessage` はメッセージの全身を ICE 入力バッファに読み込む。空きがなければ、可変長データ部分を容れるバッファを新たに確保する。このマクロの呼出しの後、引数 pdata を NULL と比べて、バッファを確保するのに十分なメモリが存在したことを確かめるものとする。

`IceReadCompleteMessage` を呼び出してメッセージを処理した後には、`IceDisposeCompleteMessage` を呼び出すものとする。

`IceDisposeCompleteMessage(`IceConn ice_conn, char *pdata`)`;

ice_conn | 有効な ICE 接続オブジェクト。
pdata | `IceReadCompleteMessage` で取得した、可変長データ部分へのポインタ。

可変長データを容れるためにバッファを確保しなければならなかった場合(ICE 入力バッファに収まらなかったが故に)、そのバッファはこのマクロを通じて、ICElib の手で解放される。

`IceReadMessageHeader(`IceConn ice_conn, int header_size, <C_data_type> *pmsg`)`;

ice_conn | 有効な ICE 接続オブジェクト。
header_size | メッセージ・ヘッダの大きさ(バイト単位)。
<C_data_type> | C 言語における、メッセージ・ヘッダの実際のデータ型。
pmsg | このポインタはメッセージ・ヘッダに合わせられる。

`IceReadMessageHeader` は、メッセージのヘッダだけを読み込む。残りのデータは、`IceReadData` 系のマクロで読み込むことになる。このメッセージ読込みの手法は、可変長データ部分を適度に区切って読み込まなければならない時に採用する。

利用者が用意したバッファに直接データを読み込むには、`IceReadData` を用いる。

`IceReadData(`IceConn ice_conn, int bytes, char *pdata`)`;

ice_conn | 有効な ICE 接続オブジェクト。
bytes | 読み込むバイト数。
pdata | 利用者が用意した当バッファにデータが読み込まれる。

データを 16 ビット単位で読み込むには、`IceReadData16` を用いる。(訳註：X11R7.7/lib/libICE-1.0.8 では未実装。引数 swap を無視して IceReadData と同じ作業を実行している。)

`IceReadData16(`IceConn ice_conn, Bool swap, int bytes, char *pdata`)`;

ice_conn | 有効な ICE 接続オブジェクト。
swap | `True` であれば、値(the values)のバイト順交換を行う。
bytes | 読み込むバイト数。
pdata | 利用者が用意した当バッファにデータが読み込まれる。

データを 32 ビット単位で読み込むには、`IceReadData32` を用いる。(訳註：X11R7.7/lib/libICE-1.0.8 では未実装。引数 swap を無視して IceReadData と同じ作業を実行している。)

`IceReadData32(`IceConn ice_conn, Bool swap, int bytes, char *pdata`)`;

ice_conn | 有効な ICE 接続オブジェクト。
swap | `True` であれば、値(the values)のバイト順交換を行う。
bytes | 読み込むバイト数。
pdata | 利用者が用意した当バッファにデータが読み込まれる。

32 ビットもしくは 64 ビットの境界に揃えるには、`IceReadPad` を用いる。詰め物は最大で 7 バイトまで指定できる。

`IceReadPad(`IceConn ice_conn, int bytes`)`;

ice_conn | 有効な ICE 接続オブジェクト。
bytes | 詰め物のバイト数。

## 第十三章 エラーの処理

ICElib には標準のエラー・ハンドラ(処理器)が二つある。

- 一つは、主に致命的な事態(fatal conditions)を扱う(例えば、機器の故障で接続が絶えた等)。

- 一つは、ICE 固有のプロトコル・エラーを扱う。

もし独自のエラー処理手続きを設定したいのであれば、上の二つのエラー・ハンドラは、利用者の用意した手続きと取り替えることができる。この取替えは何度でも可能である。

ICE エラー・ハンドラを設定するには、`IceSetErrorHandler`を用いる。

`IceErrorHandler IceSetErrorHandler(`IceErrorHandler handler`)`;

handler | ICE エラー・ハンドラ。標準のハンドラに戻すには、NULL を渡す。

`IceSetErrorHandler` は、この時まで使用されていたエラー・ハンドラを返す。

ICE エラー・ハンドラは、予期せぬ ICE プロトコル・エラー(主命令コード 0)が発生した時に呼び出される。標準搭載のハンドラの動作は、説明的な文を `stderr` に表示し、重症度が「致命的(fatal)」であれば、0 でない値を付して `exit` を呼び出す、というものである。`exit` で終了してしまうのが望ましくない場合、アプリケーション側で独自のエラー・ハンドラを登録する。

他のプロトコルの法域で起こるエラーは、それぞれのプロトコル・ライブラリで処理することに注意する(各ライブラリに独自のエラー・ハンドラ備えるものとする)。

ICE エラー・ハンドラの関数型は `IceErrorHandler` である。

`typedef void (*IceErrorHandler)(`IceConn ice_conn, Bool swap, int offending_minor_opcode, unsigned long offending_sequence_num, int error_class, int severity, IcePointer values`)`;

ice_conn | ICE 接続オブジェクト。
swap | 値(the values)のバイト順交換が必要か否かを示す印。
offending_minor_opcode | 違反したメッセージの ICE 従命令コード。
offending_sequence_num | 違反したメッセージの通し番号。
error_class | 違反したメッセージのエラー・クラス。
severity | `IceCanContinue`、`IceFatalToProtocol` もしくは `IceFatalToConnection`。
values | 指定された従命令コードとクラス次第で必要となるエラー値。

ICE の層では、以下のエラー・クラスが定義されている。

`IceBadMinor`
`IceBadState`
`IceBadLength`
`IceBadValue`
`IceBadMajor`
`IceNoAuth`
`IceNoVersion`
`IceSetupFailed`
`IceAuthRejected`
`IceAuthFailed`
`IceProtocolDuplicate`
`IceMajorOpcodeDuplicate`
`IceUnknownProtocol`

詳しくは Inter-Client Exchange Protocol を見よ。

致命的な入出力エラーを処理するために、`IceSetIOErrorHandler` を用いる。

`IceIOErrorHandler IceSetIOErrorHandler(`IceIOErrorHandler handler`)`;

handler | 入出力エラーのハンドラ(処理器)。標準のハンドラに戻すには、NULL を渡す。

`IceSetIOErrorHandler` は、この時まで使用されていた入出力エラー・ハンドラを返す。

ICE 入出力エラー・ハンドラの関数型は、`IceIOErrorHandler` である。

`typedef void (*IceIOErrorHandler)(`IceConn ice_conn`)`;

ice_conn | ICE 接続オブジェクト。

ICElib で入出力エラーを処理するには、二通りのやり方がある。

- 一つ目は、入出力エラーの処理に必要な全ての作業を入出力エラー・ハンドラに行わせ、さりながら、そのエラー・ハンドラ中では `IceCloseConnection` を呼び出さずに、同ハンドラから復帰するやり方である。この時 ICE 接続は「bad IO」状態となり、同接続に対する以後の読み書き全てが無視される。次回 `IceProcessMessages` が呼び出された時、`IceProcessMessagesIOError` という状態が返ってくる。その時になって、アプリケーションから `IceCloseConnection` を呼び出す。

- 二つ目は、入出力エラー・ハンドラの中で `IceCloseConnection` を呼び出し、次いで `longjmp` 命令を用いてアプリケーションのメイン・イベント・ループに戻るやり方である。`setjmp` 命令と `longjmp` 命令は全ての環境で適切に機能するとは限らない。特にメモリ漏れ(memory leaks)が起こらないように注意しなければならない。したがって、この二つ目の方式は、一つ目のものに比べて好ましくない。

入出力エラーの通知を受け取るように設定されているプロトコル・ライブラリでは、アプリケーションの入出力エラー・ハンドラが呼び出される前に、独自の `IceIOErrorProc` 型ハンドラが呼び出される。このハンドラは、プロトコル登録関数にて設定され(`IceRegisterForProtocolSetup` 及び `IceRegisterForProtocolReply` を見よ)、そのプロトコルだけに関わる異状の始末をつける。

`typedef void (*IceIOErrorProc)(`IceConn ice_conn`)`;

ice_conn | ICE 接続オブジェクト。

全ての `IceIOErrorProc` 型コールバックは必ず返って来なければならない(must return)ということに注意する。これは、有効なプロトコルの各々が接続破断の通知を必要としているためである。アプリケーションの入出力エラー・ハンドラは、その後に呼び出されなければならない。

## 第十四章 マルチ・スレッドで使用する

アプリケーションにて複数のスレッドが ICE ライブラリを使用することを宣言するには、`IceInitThreads` を用いる。

`Status IceInitThreads(`void`)`;

マルチ・スレッドを利用するプログラムで最初に呼び出される ICElib 関数は、`IceInitThreads` でなければならない。この関数の呼出しは、他の ICElib 呼出しが行われる前に完了しなければならない。`IceInitThreads` が 0 でない Status を返すのは、この関数でスレッド・パッケージ(threads package)の初期化に成功した時であり、且つその時に限られる。`IceInitThreads` を複数回呼び出しても問題はない。そのようにしても、スレッド・パッケージ(threads package)は一度しか初期化されない。

ICElib を基層とするプロトコル・ライブラリでは、ICE 接続とやり取りする、コード中の急所部分(critical sections)に排他錠を掛けなければならない(例えば、メッセージを生成する時)。大抵はマクロとして実装される関数を二つ用意している。

`void IceLockConn(`IceConn ice_conn`)`;

`void IceUnlockConn(`IceConn ice_conn`)`;

ice_conn | ICE 接続オブジェクト。

複数回の ICElib の呼出しを跨いで ICE 接続の排他錠を維持するには、アプリケーションにて `IceAppLockConn` 及び `IceAppUnlockConn` を用いる。

`void IceAppLockConn(`IceConn ice_conn`)`;

ice_conn | ICE 接続オブジェクト。

関数 `IceAppLockConn` は、`IceAppUnlockConn` が呼び出されるまでの間、引数で指定された接続から他のスレッド群を完全に閉め出す。同接続に対して ICElib 関数を呼び出そうとする他のスレッド群は待機させられる(block)。プログラム中で前以って `IceInitThreads` を呼び出していない場合、`IceAppLockConn` は何もしない。

`void IceAppUnlockConn(`IceConn ice_conn`)`;

ice_conn | ICE 接続オブジェクト。

`IceAppLockConn` を呼び出したスレッドが関数 `IceAppUnlockConn` を呼び出すことで、前者の関数のために待機させられていた他のスレッド群は、操作対象となった接続に対する ICElib 関数の呼出しを完遂できるようになる。プログラム中で前以って `IceInitThreads` を呼び出していない場合、`IceAppUnlockConn` は何もしない。

## 第十五章 補助関数

一時的な作業領域(scratch space)を確保するには(例えば可変長データ付きのメッセージを生成する時)、`IceAllocScratch` を用いる。各 ICE 接続には、この一時作業領域が一つづつ備わっている。一時作業領域は空の状態から始まり、必要に応じて増えていく。何らかの ICElib 関数が呼び出された後には、一時作業領域の中身が保たれている保証はない。

`char *IceAllocScratch(`IceConn ice_conn, unsigned long size`)`;

ice_conn | ICE 接続オブジェクト。
size | 要求するバイト数。

`IceAllocScratch` の戻り値として取得した記憶領域は、同関数の呼出者が解放してはいけないという点に注意する。この領域は、領域を保持する ICE 接続が閉じられた時に、ICE ライブラリによって解放される。

## 第十六章 謝辞

ICE ライブラリの設計は、Bob Scheifler 氏の助言に負うところが大きいので、ここでお礼を申し上げる。また、Jordan Brown 氏、Larry Cable 氏、Donna Converse 氏、Clive Feather 氏、Stephen Gildea 氏、Vania Joloboff 氏、Kaleb Keithley 氏、Stuart Marks 氏、Hiro Miyamoto 氏、Ralph Swick 氏、Jim VanGilder 氏、及び Mike Wexler 氏にもお礼を申し上げる。

## 附録 A 認証作業のための用役関数

本文書で既に述べたように、ICE ライブラリが認証データ(`ConnectionSetup` メッセージや `ProtocolSetup` メッセージで使用するもの)を取得する方法は、実装によって異なる。†[2]

この附録では、ICE 許認可ファイル(ICE authority file)を操作する用役関数(utility functions)について説明する。許認可ファイルは、クライアント間での認証データの受け渡しに用いることができる。

.ICEauthority ファイルに対する基本的な操作としては、以下のものがある。

- ファイル名を取得する

- 排他錠を掛ける

- 排他錠を外す

- 項目(entry)を読み込む

- 項目(entry)を書き込む

- 項目(entry)を検索する

これらは比較的低水準な操作であり、ファイル中の項目を加え、削り、表示する「iceauth」のような名前のプログラムが存在することを想定している。

これらの用役関数を用いるには、ヘッダ・ファイル <X11/ICE/ICEutil.h> を取り込まなければならない(include)。

.ICEauthority ファイル中の項目(entry)一つは、次のデータ構造を以って定義されている。

    typedef struct {
         char *protocol_name;
         unsigned short protocol_data_length;
         char *protocol_data;
         char *network_id;
         char *auth_name;
         unsigned short auth_data_length;
         char *auth_data;
    } IceAuthFileEntry;

protocol_name 部には、接続設定時の認証であれば「ICE」が、そうでなければ下位プロトコルの名前(例えば「XSMP」)が入る。それぞれの項目(entry)におい て、protocol_data 部にプロトコル固有のデータを記載することができる。このデータは、ファイルから削除する必要のある古い項目を探すのに役立つ。

network_id 部には、認証を受理する側のクライアントのネットワーク ID が入る(例えばセッション・マネージャのネットワーク ID)。ネットワーク ID は次の書式で表される。

tcp/<hostname>:<portnumber> | もしくは
decnet/<hostname>::<objname> | もしくは
local/<hostname>:<path> |

auth_name 部には認証方式の名前が入る。auth_data 部には実際の認証データが入り、auth_data_length 部にはそのデータのバイト数が入る。

標準の許認可ファイル名を取得するには、`IceAuthFileName` を用いる。

`char *IceAuthFileName(`void`)`;

環境変数 ICEAUTHORITY が設定されていれば、その値が返る。さもなければ、$HOME/.ICEauthority が標準の許認可ファイル名となる。この名前を保持する領域は静的に割り当てられているので、解放してはいけない。

同期をとって許認可ファイルを更新するには、`IceLockAuthFile` を呼び出してファイルに排他錠を掛けなければならない。この関数はシステム・コール `link` の仕組みを利用している。即ち、新しいリンクの名前が既に存在するものと被った場合に、`link` の呼出しが蹉跌するという現象を利用している。

`int IceLockAuthFile(`char *file_name, int retries, int timeout, long dead`)`;

file_name | 排他錠を掛ける許認可ファイル。
retries | 再試行の回数。
timeout | 再試行と再試行との間の秒数。
dead | dead で指定された秒数を超えて存続している排他錠が既にあれば、これを破棄する。無条件に先行の排他錠を破棄するには、dead に 0 を渡す。

下記の三つの値のうち一つが返る。

- `IceAuthLockSuccess` - 排他錠を掛けるのに成功した。

- `IceAuthLockError` - システム・エラーが発生。`errno` の出番である。

- `IceAuthLockTimeout` - 指定された回数分の試行が全て失敗に終わった。

許認可ファイルに掛かった排他錠を外すには、`IceUnlockAuthFile` を用いる。

`void IceUnlockAuthFile(`char *file_name`)`;

file_name | 排他錠を外す許認可ファイル。

許認可ファイル中の次の項目(entry)を読み込むには、`IceReadAuthFileEntry` を用いる。

`IceAuthFileEntry *IceReadAuthFileEntry(`FILE *auth_file`)`;

auth_file | 許認可ファイル(authorization file)。

この関数を呼び出す前に、読み込むべきファイルを開いておくのは、アプリケーションの仕事である。エラーが起きるか、読み込むべき項目(entry)がなくなれば、NULL が返る。

取得した項目(entries)は `IceFreeAuthFileEntry` を呼び出して解放するものとする。

許認可ファイル中に項目(entry)を一つ書き込むには、`IceWriteAuthFileEntry` を用いる。

`Status IceWriteAuthFileEntry(`FILE *auth_file, IceAuthFileEntry *entry`)`;

auth_file | 許認可ファイル(authorization file)。
entry | 書き込む項目(entry)。

この関数を呼び出す前に、書き込むべきファイルを開いておくのは、アプリケーションの仕事である。関数が成功裡に終われば、0 でない Status が返る。

標準(default)の許認可ファイルの中から、指定された protocol_name/network_id/auth_name の組に一致する項目を探し出すには、`IceGetAuthFileEntry` を用いる。

`IceAuthFileEntry *IceGetAuthFileEntry(`const char *protocol_name, const char *network_id, const char *auth_name`)`;

protocol_name | 項目検索に用いるプロトコル名。
network_id | 項目検索に用いるネットワーク ID。
auth_name | 項目検索に用いる認証方式名。

`IceGetAuthFileEntry` の結果、指定したような項目が見つからなかった場合、NULL が返る。

`IceReadAuthFileEntry` もしくは `IceGetAuthFileEntry` で取得した項目(entry)を解放するには、`IceFreeAuthFileEntry` を用いる。

`void IceFreeAuthFileEntry(`IceAuthFileEntry *entry`)`;

entry | 解放する項目(entry)。

[2] X Consortium(X 協会)の ICElib は、ICE 許認可ファイルが存在するものと想定して実装されている。

## 附録 B MIT-MAGIC-COOKIE-1 による認証

X 協会の ICElib の実装には、MIT-MAGIC-COOKIE-1 を利用した簡易な認証機構が備わっている。この機構は附録 A で述べた、許認可ファイル利用関数を用いている。

この方式において、セッションマネージャのような位地にあるアプリケーションは、まず `IceGenerateMagicCookie` を呼び出してマジック・クッキーを取得し、次いで利用者の手元の(local) .ICEauthority ファイル中にそのマジック・クッキーを記録する。これによって、ローカル(訳註：local <-> remote)のクライアントは先のアプリケーションに接続できるようになる。遠隔の クライアントが接続できるようにするには、遠隔機上にある利用者の .ICEauthority ファイル中にマジック・クッキーを記録するための、何らかの遠隔実行の仕組みを用いる。

アプリケーションでは、「.ICEauthority」ファイルにマジック・クッキーを書き込むに止まらず、同クッキーをメモリにも書き込むため、関数 `IceSetPaAuthData` を呼び出す必要がある。MIT-MAGIC-COOKIE-1 認証手続きにおいて接続を受理・拒否する段になると、同手続きでは、申請者(requestor)の提示するマジック・クッキーとメモリにあるマジック・クッキーとの比較が行われる。

`char *IceGenerateMagicCookie(`int length`)`;

length | マジック・クッキーの長さを指定。

戻り値たるマジック・クッキーは null 文字終端である。マジック・クッキーのためにメモリ(一時記憶領域)を割り当てられなかった時は、上の関数は NULL を返す。さもなければ、取得したマジック・クッキーは `free` を呼び出して解放する。

認証データを一時記憶領域(memory)に写すには、`IceSetPaAuthData` を用いる。この関数は、今のところ MIT-MAGIC-COOKIE-1を用いた認証にしか使用されていないが、将来は他の認証方式で利用されることもあろう。

`void IceSetPaAuthData(`int num_entries, IceAuthDataEntry *entries`)`;

num_entries | 認証データの項目(entry)数。
entries | 認証データの項目(entry)の配列(訳註：将来、連結リストにしたいらしい)。

各項目(entry)には、プロトコルの名前(例えば、ICE 接続設定の認証であれば「ICE」、セッション管理の認証であれば「XSMP」)、受理方クライアントのネットワーク ID、認証方式の名前(例えば MIT-MAGIC-COOKIE-1)、そして認証データが結び付けられている。ICE ライブラリでは、渡された項目群と前から設定されていた項目群とを、「protocol_name、network_id、auth_name」の三値一 組を鍵として、混ぜ合わせる(merge)。(訳註：libICE-1.0.8/src/setauth.c では、三値同一の項目があれば、新しく渡された項目で上書きする。その他の新項目は配列の後ろに足していく。)

    typedef struct {
    	char *protocol_name;
    	char *network_id;
    	char *auth_name;
    	unsigned short auth_data_length;
    	char *auth_data;
    } IceAuthDataEntry;

入り口に戻る