Client To Client Protocol (CTCP) Klaus Zeuge Troy Rollo Ben Mesander CTCPは以下の用途を意図しています。 1.ユーザークライアント間の一般的なデータ(画像、音声、書体情報など)の転送 具体的には 2.ユーザークライアントに対する要求及び応答の場の提供 **************************************** クライアント・サーバー間の基本プロトコル **************************************** インターネットリレーチャット(IRC)クライアントとサーバー間で使用される文字は 8ビットのバイト列(オクテット列)であり8進数で\000から\377の数値 (10進数では0から255)で表されます。いくつかの文字は特別な意味を持っています。 文字 ::= '\000' .. '\377' ヌル ::= '\000' 改行 ::= '\n' 復帰 ::= '\r' 注意:3つの数字が続く「\」はここでは8進数値を表すものとします。 アルファベットが続く「\」はC言語スタイルの特殊文字を「..」は文字範囲を あらわすものとします。 サーバーへ送信される、もしくはサーバーから受信される1行の文字列(ここでは 低レベルメッセージと呼びます)は0文字以上の文字(ヌル、改行、復帰は除く)に 復帰と改行がついたものです。 低文字 ::= '\001' .. '\011' | '\013' | '\014' | '\016' .. '\377' 低行 ::= 低文字* 復帰 改行 注意:「*」は「直前の文字クラスの0個以上の連続」を、「|」は代替を表します。 ヌルがサーバーへ送られることはありません。 ****************** 低レベルエスケープ ****************** IRCサーバーとの通信メッセージにヌル、改行、復帰を含むことができないという事は クライアント間で自由なデータ(これを中間レベルメッセージと呼びます)を 送受信できないことになります。これを可能にするには3つの文字をエスケープしなけ ればなりません。そしてそれにはエスケープ文字が必要です。もちろんエスケープ文字 自身も「文字」の1つなのでエスケープする必要があります。 中エス ::= '\020' (すなわちコントロールP) 中間レベルメッセージを送信するときに{ヌル、改行、復帰、中間エスケープ}の どれかの文字がメッセージ内にあるならば、その文字は下の対応に従って2文字に 置き換えます。 ヌル --> 中エス '0' 改行 --> 中エス 'n' 復帰 --> 中エス 'r' 中エス --> 中エス 中エス 低レベルメッセージを受信するときに中エスがあるなら、その次の文字を見て、 その2文字を下の対応に従って置き換えて、中間レベルメッセージに一致するように します。 中エス '0' --> NUL 中エス 'n' --> NL 中エス 'r' --> CR 中エス 中エス --> 中エス もし中エスの次の文字が対応にない文字ならば、それはエラーですのでメッセージから その中エスを取り除き、オプションとしてユーザーにそのことを警告します。 たとえば 'x' 中エス 'y' 'z' という文字列がサーバーから送られてきたなら 'x 'y' 'z' に逆エスケープします 低レベルエスケープを行う前のサーバーへのメッセージ(逆を言えば:低レベル 逆エスケープを行う前のサーバーからのメッセージ)は以下のようになります。 中間行 ::= 文字* ************** タグつきデータ ************** 拡張データや要求・応答ペアをクライアント間で送信する場合、なんらかの拡張データ フォーマットが必要になります。拡張データは中間レベルメッセージのテキスト部に 入った形で(そして、低レベルエスケープの後では低レベルメッセージのテキスト部に 入った形で)送信されます。 拡張データを中間メッセージに入れて送るには、それを区切る方法が必要です。 これは拡張データの最初と最後に以下で定義される区切り文字を入れることで行います。 拡張区切り ::= '\001' 最初と最後の区切り文字は同じものですから、1番目の拡張区切りを奇数区切り、 その次を偶数区切りと呼ぶことにします。さらにその次は奇数区切りで、その次は偶数、 以下同様です。 データが低レベルエスケープ済みであれば(逆に言えば、低レベル逆エスケープの前で あれば)、拡張区切りの間の拡張データには拡張区切りを除く任意の文字を好きなだけ 使えます。 拡張文字 ::= '\000' | '\002' .. '\377' あるひとつの拡張メッセージは空(奇数区切りと偶数区切りの間に何もない)であるか、 1文字以上のスペースでない文字が連続したものか、1文字以上のスペースでない文字の 連続の後にスペースがあり、さらにその後に文字が0文字以上連続したものです。 拡張成分文字 ::= '\000' | '\002' .. '\037' | '\041' .. '\377' スペース ::= '\040' 拡張メッセージ ::= | 拡張成分文字+ | 拡張成分文字+ スペース 拡張文字* 注意:ここで「+」は「直前の文字クラスの1個以上の連続」を、「*」は「直前の文字 クラスの0個以上の連続」を意味します。 最初のスペースまでの文字(スペースがないなら拡張メッセージ全体)は拡張 メッセージのタグと呼ばれます。タグは拡張データの種類を示すのに使われます。 タグは任意の文字の文字列になりえます。そしてタグにアルファベットが含まれている 場合、大小の違いがあります。つまり大文字と小文字で異なる文字になります。 拡張データはPRIVMSG及びNOTICEコマンドでのみ有効です。拡張データが要求に対する 応答であるときはNOTICEで、そうでなければPRIVMSGで送られます。ユーザーや チャンネルに対するPRIVMSG及びNOTICEは拡張データを含み得ます。 PRIVMSG及びNOTICEのテキスト部は0個以上の文字列と混ざり合った0個以上の拡張 メッセージを含み得ます。 ******************** CTCPレベルエスケープ ******************** 拡張メッセージの内部で拡張区切り文字を使えるようにするためには、それを エスケープする必要があります。これは(エスケープしたものをさらにエスケープする ことがないよう、低レベルエスケープ文字とは異なる)別のエスケープ文字の必要性を 意味します。 拡エス ::= '\134' (バックスラッシュ or エンマーク - 「\」). CTCPレベルエスケープを行う場合は実際のCTCPメッセージ(拡張データ、要求、応答) のみをエスケープします。これによりユーザーは拡張エスケープ文字を送信することが できるようになります。以下の変換が行われなければいけません。 拡張区切り --> 拡エス 'a' 拡エス --> 拡エス 拡エス そして、CTCPレベル逆エスケープを行う場合はCTCPメッセージのみを以下の対応に 従って逆エスケープします。 拡エス 'a' --> 拡張区切り 拡エス 拡エス --> 拡エス 上に上げる以外の文字が続く拡エス文字があった場合、それはエラーですので、その 拡エス文字を取り除きます。例えば 'x' 拡エス 'y' 'z' のようなCTCPレベル エスケープ済みの文字列が逆エスケープされると3つの文字からなる文字列 'x' 'y' 'z' になります。 もし拡張区切りがCTCPメッセージの外側で見つかった場合、そのメッセージがその 拡張区切りを含んでいるはずです。(これは拡張区切りを奇数個含む中間レベル メッセージの最後の拡張区切りでのみ起こるはずです。) ************ エスケープ例 ************ 3つのメッセージレベルがあります。高レベル(H)はユーザー・クライアント間での テキストレベルです。中間レベル(M)はHレベルメッセージにCTCPエスケープを 適用したものです。低レベル(L)はクライアント・サーバー間でのレベルで、Mレベル メッセージに低レベルエスケープを適用したものです。 以下の関係が成り立ちます。ただし lowQuote(message) は低レベルエスケープを行う 関数、lowDequote(message) は低レベル逆エスケープを行う関数、ctcpQuote(message) はCTCPレベルエスケープを行う関数、ctcpDequote(message) はCTCPレベル逆エスケープ を行う関数、ctcpExtract(message) はメッセージから全てのCTCPメッセージを取り除く 関数です。 L = lowQuote(M) M = lowDequote(L) M = ctcpQuote(H) H = ctcpDequote(ctcpExtract(M)) 通常のテキストにCTCPメッセージを埋め込んで送信する場合。 M = ctcpQuote(H1) || '\001' || ctcpQuote(X) || '\001' || ctcpQuote(H2) 注意:「||」演算子は文字列の連結を意味します。 もちろん、0個以上の通常のテキストと0個以上のCTCPメッセージが混ざっている可能性が あります。 - --- 例 1 ----------------------------------------------------------------- あるユーザー(actorと呼ぶ)が次の文字列 Hi there!\nHow are you? すなわちユーザーが内部に改行をいれたメッセージ(どのようにしてこれを行うかは クライアントによります)、を対象のユーザー(victim)に送信したいとします。これは クライアント内部で以下のコマンドとなるでしょう。 PRIVMSG victim :Hi there!\nHow are you? \K? これにCTCPエスケープが行われ PRIVMSG victim :Hi there!\nHow are you? \\K? 低レベルエスケープが行われ PRIVMSG victim :Hi there!\020nHow are you? \\K? そして行末に改行をつけてサーバーに送られます。 これはvictim側には以下の形で届きます。 :actor PRIVMSG victim :Hi there!\020nHow are you? \\K? (\\K は SIS D47 での OK 等に似ています) これは低レベル逆エスケープにより :actor PRIVMSG victim :Hi there!\nHow are you? \\K? そしてCTCPレベル逆エスケープにより :actor PRIVMSG victim :Hi there!\nHow are you? \K? これがどのように表示されるかはクライアントにより異なりますが、"there!" と "How" の間で改行されるのが好ましいでしょう。 - --- 例 2 ----------------------------------------------------------------- actor側のクライアントが "Emacs wins" という文字列を送りたいとします。これは あるキーでSED暗号化[SEDはCTCPにより実装されているIRCクライアント間での単純な 暗号化プロトコルです。私はこれに関する参考資料を持ちません。 -- Ben]を用いると "\n\t\big\020\001\000\\:" という文字列になります。そのためクライアントはまず CTCPレベルエスケープを行い "\n\t\big\020\\a\000\\\\:" に変換します。そして 中間レベルメッセージを構成します。 PRIVMSG victim :\001SED \n\t\big\020\\a\000\\\\:\001 低レベルエスケープを行うと PRIVMSG victim :\001SED \020n\t\big\020\020\\a\0200\\\\:\001 これに改行がつけれられてサーバーへ送られます。 victim側では、文字列 :actor PRIVMSG victim :\001SED \020n\t\big\020\020\\a\0200\\\\:\001 がサーバーから受信され、低レベル逆エスケープを行って :actor PRIVMSG victim :\001SED \n\t\big\020\\a\000\\\\:\001 から文字列 "\n\t\big\020\\a\000\\\\:" が抽出されます。そしてCTCPレベル 逆エスケープにより "\n\t\big\020\001\000\\:" となり、その後あるキーを用いて SED復号化をすることで "Emacs wins" が復元されます。 - --- 例 3 ----------------------------------------------------------------- actorがvictimへのUSERINFOの要求を会話の最中に行いたいとします。そして クライアントがUSERINFO 要求を通常のメッセージの後につけて送信しようとしたと します。actorはテキストメッセージ "Say hi to Ron\n\t/actor" とCTCP要求 "USERINFO" をvictimに送信したいわけです。 PRIVMSG victim :Say hi to Ron\n\t/actor と USERINFO がCTCPレベルエスケープにより PRIVMSG victim :Say hi to Ron\n\t/actor と USERINFO になり、ひとつにまとめられて PRIVMSG victim :Say hi to Ron\n\t/actor\001USERINFO\001 そして低レベルエスケープにより PRIVMSG victim :Say hi to Ron\020n\t/actor\001USERINFO\001 になり、サーバーへ送られます。 victim側では、メッセージ :actor PRIVMSG victim :Say hi to Ron\020n\t/actor\001USERINFO\001 が受信されます。これは低レベル逆エスケープにより :actor PRIVMSG victim :Say hi to Ron\n\t/actor\001USERINFO\001 そして分割され :actor PRIVMSG victim :Say hi to Ron\n\t/actor と USERINFO 共にCTCPレベル逆エスケープされ :actor PRIVMSG victim :Say hi to Ron\n\t/actor が表示されます。一方CTCPコマンド USERINFO への応答が送信されます。この応答 USERINFO :CS student\n\001test\001 はCTCPレベルエスケープを適用され USERINFO :CS student\n\\atest\\a がNOTICEでの応答として構成されます。 NOTICE actor :\001USERINFO :CS student\n\\atest\\a\001 これに低レベルエスケープを適用され NOTICE actor :\001USERINFO :CS student\020n\\atest\\a\001 victimのサーバーへ送信されます。 actorの側で受信すると、メッセージ :victim NOTICE actor :\001USERINFO :CS student\020n\\atest\\a\001 は低レベル逆エスケープにより :victim NOTICE actor :\001USERINFO :CS student\n\\atest\\a\001 ここで全てのCTCP応答は抽出され1つのCTCP応答とメッセージが空のNOTICEコマンドに なります。 USERINFO :CS student\n\\atest\\a 得られた応答はCTCPレベル逆エスケープにより USERINFO :CS student\n\001test\001 となっておそらくactorのクライアント上で表示されるでしょう。 ************** 既存拡張データ ************** クライアント間で受け渡しされる拡張データは構造化された情報を受け渡しするのに 用いることができます。現在知られている拡張データタイプは以下の通りです。 ACTION - IRCで「ロールプレイング」を行うのに用います。 DCC - クライアント間でのファイル転送やチャットのための直結のTCP接続を 確立します。 SED - クライアント間で暗号化メッセージをやり取りするのに用います。 ACTION ====== これはIRC上でロールプレイングゲームを行うときに使われます。あるACTIONメッセージ は以下のようになります。 \001ACTION barfs on the floor.\001 このようなメッセージを受け取ったクライアントはこれを行ったユーザーが「行動」を 行っていることを示すようにフォーマットすべきです。例えば、ユーザー「actor」が 上のメッセージをチャンネル "#twilight_zone" に送信したとすると、他のユーザーの クライアントには以下のようなメッセージが表示されるはずです。 [ACTION] actor->#twilight_zone: barfs on the floor. そのチャンネルの他のユーザーは適切な印象を抱くことでしょう。 DCC === DCCは「Direct Client Connection」を意味します。CTCP DCC拡張データメッセージは クライアント間のファイル転送やTCP接続上のチャット接続をIRCサーバーから独立して 確立するのに用いられます。クライアント間の接続では通常のIRCプロトコルとは異なる 他のプロトコルが用いられます。このようにDCCプロトコルは複雑なため完全な説明は この文書の末尾にある付録Aに分離されて収められています。 SED === SEDはおそらく「Simple Encryption D???」を意味します。これはクライアント間で 暗号化されたメッセージをやり取りするのに用いられます。SEDによって暗号化された メッセージは以下のようになります。 \001SED encrypted-text-goes-here\001 このようなメッセージを受け取ったクライアントはこれを復号化して表示すべきです。 誰かこれを文書化して暗号化方法を付録Bとしてくれればよいのですが。 ****************** 既存要求・応答ペア ****************** 要求・応答ペアは2つのクライアント間で2段階のフェイズで送信されます。最初の フェイズでは要求を送信します。これはPRIVMSGコマンドで(ユーザーもしくは チャンネルに対して− それは問題ではない)行われます。 次のフェイズでは応答を送信します。これはNOTICEコマンドで行われます。 既知の要求・応答ペアで用いるコマンドは以下の通りです。 FINGER - ユーザーのフルネームと未操作時間を返します。 VERSION - クライアントのバージョンとタイプ。 SOURCE - このクライアントをでこで手に入れられるか。 USERINFO - ユーザーが設定する文字列 (クライアント作成者ではない) CLIENTINFO - クライアントが受け入れるCTCPの一覧 ERRMSG - 応答に際しエラーが起きたときに使用されます。 PING - クライアント間のIRCネットワークの遅延を測定するのに 用いられます。 TIME - 他のクライアントのローカルな日付及び時刻を取得します。 FINGER ====== これはユーザーの実名や未操作時間(後者の利用法はIRCプロトコルに取り入れられた ため廃止されました)を取得するのに用います。PRIVMSGでの要求は次のように なります。 \001FINGER\001 そしてNOTICEによる応答は以下のようになります。 \001FINGER :#\001 「#」はユーザーの実名、クライアントマシンでのログインネーム、未操作時間等の 情報を表す拡張成分文字列です。 VERSION ======= これは他のクライアントの名前やバージョンに関する情報を取得するのに用いられます。 PRIVMSGによる要求は単純で以下のようになります。 \001VERSION\001 そして応答は \001VERSION #:#:#\001 最初の「#」はクライアントの名前、2番目の「#」はクライアントのバージョン、 3番目の「#」はクライアントの動作環境を示しています。 以下を用います。 非コロン文字 ::= '\000' .. '\071' | '\073' .. '\377' 非コロン文字列であるクライアント名は "Kiwi" や "ircII" などのようなものです。 バージョンは "5.2" や "2.1.5c", 環境は "GNU Emacs 18.57.19 under SunOS 4.1.1 on Sun SLC" や "Compiled with gcc -ansi under Ultrix 4.0 on VAX-11/730" などです。 SOURCE ====== これはそのクライアントを手に入れられる場所についての情報を取得するのに 用いられます。PRIVMSGによる要求は単純で以下のようになります。 \001SOURCE\001 そして応答は以下の形式の0個以上のCTCP応答からなります。 \001SOURCE #:#:#\001 応答の終わりを示すものとして最後に \001SOURCE\001 が送られます。最初の「#」はクライアントをanonymous FTPで取得できるインターネット ホストの名前、2番目の「#」はディレクトリ名、3番目の「#」はスペースで区切られた そのディレクトリで得られるファイルのリストです。 以下を用います 非空白文字 ::= '\000' .. '\037' | '\041' .. '\377' FTPサイト名は "cs.bu.edu" や "funic.funet.fi" のような名前で与えられます。 ファイル名領域はディレクトリの指定とオプションでスペースで区切られた1つ以上の ファイル名からなります。ディレクトリ名のみが与えられている場合、そのクライアント のソースを得るには、そのディレクトリの全てのファイルをコピーすべきです。 いくつかのファイル名が指定された場合、そのディレクトリのそれらのファイルのみを コピーすればよいでしょう。ファイル名の指定にはスペースを除く全ての文字、これには 「:」も含まれます、が使えることに注意してください。例えば "pub/emacs/irc/" は ディレクトリ pub/emacs/irc/ の全てのファイルを取得することを意味します。このとき クライアントはまずユーザー「ftp」としてログインしコマンド "CD pub/emacs/irc/" 続けてコマンド "mget *" を実行できるべきです。(もちろんバイナリかつプロンプト モードにすることにも気をつけなければいけません)別の例として "/pub/irc Kiwi.5.2.el.Z" の場合は "CD /pub/irc" と "get Kiwi.5.2.el.Z" を 行うべきです。 USERINFO ======== これはユーザーによって設定可能な(そしてクライアントが設定すべきでない)文字列を 取得するのに用いられます。要求は単純に \001USERINFO\001 応答は \001USERINFO :#\001 「#」はそのクライアントのユーザーが設定した文字列です。 CLIENTINFO ========== これはクライアント開発者が他のクライアント開発者に、そのクライアントがCTCPに どれだけ対応しているかを示す簡単な方法です。 この応答はかなり冗長に、どのCTCPコマンドを理解できるか、どのタイプにはどういう パラメータを期待しているか、そしてどのような応答がそのクライアントから返されるか を説明します。 要求はPRIVMSG内の単語CLIENTINFOで行われ、オプションとしてコロンに続くスペース 区切りの1つ以上の特定の単語をもちます。CLIENTINFOのみの場合は \001CLIENTINFO\001 応答として既知のタグのリスト(上のタグつきデータを参照)を返すべきです。これは 人間が読むことのみを意図しています。 パラメータが1つ与えられた場合、応答としてそのタグの使い方を返すべきです。 パラメータが2つ与えられた場合、そのタグのサブコマンドの使い方を返すべきです。 以下同様。 ERRMSG ====== これは不明な要求があった場合に応答として用いられます。要求として用いられた場合は エラーが起きていないことを示すと共に要求内のテキストをそのまま返すべきです。 要求として用いられる場合は \001ERRMSG #\001 「#」は任意の文字を含む文字列です。応答では \001ERRMSG # :#\001 最初の「#」は要求の文字列と同じものを、2番目の「#」はエラーが起きていないことを ユーザーに知らせる短いテキストです。 異常な要求もしくはいくつかの異常な拡張データを受信したときに送信する通常の ERRMSG応答は以下のような形式です。 \001ERRMSG # :#\001 最初の「#」は失敗した要求もしくは異常な拡張データで、2番目の「#」は何がおかしい のかを説明する「不明な要求」や「復号化に失敗しました」などの文章です。 PING ==== PINGはIRCネットワークにおけるクライアント間の遅延時間を測定するのに 用いられます。PRIVMSGに埋め込まれるPING要求は次のような形式をしています。 \001PING 時刻\001 ここで「時刻」は要求側クライアントが扱いやすい形式で表された現在時刻です。 応答側クライアントはNOTICEで全く同じメッセージを送り返します。 \001PING 時刻\001 そして要求側クライアントは現在時刻から受信したメッセージ内の「時刻」を引いて IRCネットワーク上でのクライアント間の遅延を得ることができます。 TIME ==== TIME要求は対象のユーザーのクライアント環境での時刻を得るのに使われます。これは その人が起きているかどうか判断したり、どのタイムゾーンに属しているか調べるのに 役立ちます。TIME要求は次の形式をしています。 \001TIME\001 このメッセージをPRIVMSGで受け取る側ではクライアントはNOTICEで以下の形式で応答 しなければなりません。 \001TIME :human-readable-time-string\001 例としては \001TIME :Thu Aug 11 22:52:51 1994 CST\001 ** 例 ** メッセージ PRIVMSG victim :\001FINGER\001 を送信すると応答として :victim NOTICE actor :\001FINGER :Please check my USERINFO instead :Klaus Zeuge (sojge@mizar) 1 second has passed since victim gave a command last.\001 (これは1行です)、もしくは :victim NOTICE actor :\001FINGER :Please check my USERINFO instead :Klaus Zeuge (sojge@mizar) 427 seconds (7 minutes and 7 seconds) have passed since victim gave a command last.\001 Klaus Zeuge が怠け者ならこうなるでしょう? :-) メッセージ PRIVMSG victim :\001CLIENTINFO\001 を送信すると応答は :victim NOTICE actor :\001CLIENTINFO :You can request help of the commands CLIENTINFO ERRMSG FINGER USERINFO VERSION by giving an argument to CLIENTINFO.\001 メッセージ PRIVMSG victim :\001CLIENTINFO CLIENTINFO\001 を送信すると応答は :victim NOTICE actor :\001CLIENTINFO :CLIENTINFO with 0 arguments gives a list of known client query keywords. With 1 argument, a description of the client query keyword is returned.\001 そしてメッセージ PRIVMSG victim :\001clientinfo clientinfo\001 の応答はおそらく次のようなものでしょう。 :victim NOTICE actor :\001ERRMSG clientinfo clientinfo :Query is unknown\001 このメッセージはタグ "clientinfo" が未知であることを示しています。 メッセージ PRIVMSG victim :\001CLIENTINFO ERRMSG\001 の応答は :victim NOTICE actor :\001CLIENTINFO :ERRMSG is the given answer on seeing an unknown keyword. When seeing the keyword ERRMSG, it works like an echo.\001 メッセージ PRIVMSG victim :\001USERINFO\001 の応答はかなり長く :victim NOTICE actor :\001USERINFO :I'm studying computer science in Uppsala, I'm male (somehow, that seems to be an important matter on IRC:-) and I speak fluent swedish, decent german, and some english.\001 メッセージ PRIVMSG victim :\001VERSION\001 の応答は :victim NOTICE actor :\001VERSION Kiwi:5.2:GNU Emacs 18.57.19 under SunOS 4.1.1 on Sun SLC:FTP.Lysator.LiU.SE:/pub/emacs Kiwi-5.2.el.Z Kiwi.README\001 これはクライアントが Kiwi のバージョン 5.2 で GNU Emacs 18.57.19 上で使われて おり、SunOS 4.1.1 の載った Sun SLC 上で動いている場合です。また、その クライアントが anonymous FTP で入手できることも示しています。この場合は FTP.Lysator.LiU.SE に接続してFTPコマンド "cd /pub/emacs/" を入力しそこで Kiwi-5.2.el.Z と Kiwi.README をダウンロードします。多分ダウンロードしたファイル のうちのどれかにクライアントをインストールする方法が書かれているはずです。 -------------------------------------------------------------------------------- ********************************************************************** 付録 A -- DCCプロトコルについて ********************************************************************** 著者 Troy Rollo (troy@plod.cbme.unsw.oz.au) 改訂者 Ben Mesander (ben@gnu.ai.mit.edu) DCCプロトコルの作成者である Troy Rollo はDCCプロトコルはIRCII クライアント以外のクライアントに実装しやすいようには作られていないと言いました。 しかしながら、今日、DCCはIRCII以外の環境でも便利であることは明白です。ksh、 elisp、C、そしてPerl、さまざまな言語のIRCクライアントが皆DCCを実装しています。 なぜ DCC? ========= DCCによりIRCベースのプロトコルでありながらIRCサーバーネットワークの いくつかの制限を克服することができ、またより安全なチャット接続を確立することが できます。 DCCはデータを扱うクライアント間の直接のTCP接続を用います。転送量制御は ないので最高速度でパケットを送信することができます。また、サーバーリンク (もしくはそれによる負荷)の影響を受けることもありません。加えて、DCC接続を 確立する際の最初の通信以外はIRCネットワークを介さずに行われますので クラックされたサーバーで個人のメッセージをオペレータが見ようとするのを難しく します。 どうやって? =========== DCC接続の最初のソケットはDCCを開始した方のクライアントで作成されます。 このソケットはTCPソケットであり接続を受け入れるためにINADDR_ANYでなければ なりません。 開始側はソケットを生成したらその情報をCTCPコマンドのDCCを介して相手側 クライアントに伝えます。このコマンドは以下の書式を持ちます。 DCC 種別 パラメータ アドレス ポート [サイズ] 種別 - 接続種別 パラメータ - 接続種別ごとのパラメータ アドレス - 整数での開始側のアドレス ポート - 開始側が接続を受け入れるポート番号 サイズ - 接続種別が「SEND」(以下参照)であった場合、サイズは 送信するファイルの大きさを示します。昔のIRCIIクライアントは これを送信していませんでしたが現在では修正されています。 アドレス、ポート、サイズはホストバイトオーダーに変換してASCIIによる 10進整数表現で送信されなければいけません。これらはそれぞれ符号無しのlong、 符号無しのshort、符号無しのlongとして扱います。 DCCプロトコルを実装する場合、CTCP DCCメッセージの未知のパラメータも 受け入れられるようにすべきです。UNIX以外のOS上でのDCC実装では転送するファイルの タイプ、すなわちテキスト、バイナリ、それ以外の何か、に関するパラメータを追加 しようとする動きがあります。新しいパラメータがプロトコルに追加される場合、 それらを無視するクライアントと無視するクライアントに配慮しないクライアントの間で 問題なく処理を行えるような意味を持つようにすべきです。 以下のDCC接続種別が定義されています。 種別 目的 パラメータ CHAT ちょっと安全な会話をする。 文字列 "chat" SEND 相手にファイルを送信する ファイル名 以下のサブコマンドもIRCIIのDCCコマンドには含まれていますが、これはIRC経由で DCC要求を送りません。またこの文章では解説していません。 TALK TALK接続を確立する。 実装 ==== CHAT、SEND接続種別は潜在的な危険をはらむため、自動的に受け入れるべきでは ありません。かわりにそのような要求がきたことをユーザーに知らせて、受け入れるか どうかを決めさせます。 DCC SENDコマンドにおいて受信側クライアントはユーザーにファイル名を 変更する機会を与えなければいけません。また受信するファイルが既存のファイルを 上書きしないようにしなければなりません。 古いIRCIIクライアントは送信するファイルの完全なパス名を送っていました。 が、これは問題があるので新しいクライアントでは単にファイル名だけを送信します。 ポート番号は厳密に検査しなければいけません。もしポート番号が UNIX予約ポート範囲に入っていたら警告を表示すべきです。 クライアントの実装に用いられる言語が32ビットの整数を扱えない場合 (例えば、emacs 18 elisp や ksh 88 等)PRIVMSG内のホスト名が使えることが あります。 以下にクライアント間で起こるべきことをステップごとに示します。(UNIX システム上のBSDソケットを利用することを仮定しています) 送信者: ユーザーからDCCコマンドが発行されます。 ソケットを作成して INADDR_ANY, ポート 0 にバインドし、受動モードに します。(ソケットをlistenします。) CTCPを経由してソケットのアドレスとポート番号を含んだDCC要求を相手に 送ります。(理想的にはサーバーとの接続のローカル側のソケットから 取得します。それはそのホスト上でネットワークに近い側の インターフェイスであるはずで、ゲートウェイノードの場合は ルーティングホップ数が他より少なくなります。) 通常は接続を受けるまで続けます。 接続受けたら: 接続をAcceptして、 受動ソケットをクローズし、 新しいソケット上で処理を行います。 受信者: CTCP DCC要求を受け取ります DCC要求を記録してユーザーに知らせます。 ここで、ユーザーは要求を受け入れるかどうかを決めることができるべきです。 DCCコマンドが指定した送信者、種別、パラメータもしくはそれらの一部に あいまいな点がないことを確認してもらい受け入れるかどうかを 判断してもらいます。 受け入れるのであればTCPソケットを作成し、 そのソケットで与えられたアドレス・ポートに接続します。 ソケットを介して処理を行います。 種別指定詳細 ============ CHAT CHAT接続を通して1行ごとにデータを送信します。データには何も プレフィックスやコマンドをつけません。DCC CLOSEコマンドによりCHAT接続は 終了し、ソケットを閉じて、その接続に関する情報を破棄します。各行の終了 文字は改行文字 「\n」です。 FILE データはストリームではなくパケットごとに送られます。これによりDCC SEND 接続はFTP接続で失敗するような環境でも使えます。パケットのサイズは クライアントにより異なり、ユーザーにより設定されます。パケットサイズを 小さくすれば劣悪な接続でも失敗する確率を小さくすることができます。 受信側はそれぞれのパケットについて、受信した合計バイト数を通知 しなければいけません。これはネットワークバイトオーダーの符号無し4バイト 整数で行われます。送信側は受信側の通知が来るまで送信を継続するべきでは ありません。加えて送信側は受信側から最後のバイト通知がくるまで接続を 閉じるべきではありません。 古いIRCIIクライアントは通信の最初のDCCコマンドでファイルサイズを送信 していませんでした。そのようなクライアントの場合は受信側は全ての ファイル送信が終わったかどうかを知ることができず、送信側のみがそれを 知っています。にもかかわらず、IRCIIはそれを報告しません。ユーザーに おいては受信したファイルのサイズを確認することを、クライアントの作成者に おいてはファイルサイズの通知を行うことを強く推奨します。 テキストの変換については何も決まっていないことに注意してください。 IRCIIで使われている基本のブロックサイズは1024です。他のクライアントは これを採用してきました。しかしながら、実際の実装においては任意のブロックサイズを 受け入れられなければなりません。現在IRCIIではブロックサイズをユーザーが 設定できるようになっています。