はじめに
どうも。こんにちは。
42tokyoといったところで、簡単なIRCサーバの実装を行なっています。
今回は「RFC1459 Internet Relay Chat Protocol」を読み、IRCプロトコルについて理解を深めていきます。
この記事では、以下の章について書いております。
- 1. INTRODUCTION
- 2. THE IRC SPECIFICATION
- 3. IRC Concepts
- 9. Current Problems
また今回の課題では、サーバ間通信(Server to server)の実装は求められていないため、RFC内のサーバ間通信に関する記述箇所は、省略している部分もあります。
間違い等があれば、ご指摘ください。
1. INTORODUCTION
- IRC (Internet Relay Chat)プロトコルは、長年に渡ってテキストベースでの通信用に設計されてきた
- この文書では、現在(1993年ごろ)のIRCプロトコルについて説明する
- IRCプロトコルは、ネットワークプロトコルがTCP/IPであることを前提に開発されているが、必ずしもそうである必要はない
- IRC自体は、クライアント・サーバモデルで設計されているので、複数のマシンに分散して動作する
1.1 Servers
- サーバは、クライアント間で行われる通信を仲介する
- サーバは、他のサーバとも繋がり、IRCネットワークを形成する。(スパニングツリープロトコルを採用する)
1.2 Clients
- クライアントとは、クライアント・サーバモデルにおける、サーバに接続し利用者が操作するもののこと?
- クライアントは一意のニックネームを持つ
- ニックネームは、最大9文字で、クライアントの識別に使われる
(ニックネームに使用できる文字は、プロトコルの文法規則を参照すること。) - サーバは、接続しているクライアントに関する情報を持つ [MUST]
- サーバが持つクライアントの情報は、以下の通り
- ニックネーム
- クライアントが動作しているホストの実名
- そのホスト上のクライアントのユーザ名
- クライアントが接続しているサーバ名
1.2.1 Operators
- オペレータとは、管理者権限を持ったクライアントのこと
- オペレータに与えられる権限は、基本的なネットワーク管理に関する権限が与えられる
- 4.1.7 SQUIT(サーバの切断)
- 4.3.5 CONNECT(サーバの再接続)
- 4.6.2 KILL(任意のクライアントを削除する権限)など
※ なお、これらの権限をオペレータに与えるべきか議論の余地がある。
1.3 Channels
- チャンネルとは、1つ以上のクライアントが所属する名前付きのグループのこと
- そのチャンネル名宛のメッセージをすべて受信する
- チャンネルは、1人目の参加者が現れたときに生成され、最後の1人が離れたときに削除される。(つまり、クライアントが所属していないチャンネルは、存在してはいけない?)
- クライアントは、チャンネル名でチャンネルを識別する
- チャンネル名のprefixは、「&」か「#」で始まる
- チャンネル名は、最大200文字まで
- チャンネル名に、スペース、Ctrl+G(またはASCII 7)、カンマ( , )は、使用できない
- チャンネルには、2種類ある
- 1つは、そのネットワークを構成する全てのサーバが知っているチャンネル(分散チャンネル)。(もう1つは、そのチャンネルがあるサーバしか知らないチャンネルか?)
- 分散チャンネルは、そのチャンネルがあるサーバに接続しているクライアントだけが参加できる
- 分散チャンネルのprefixは「&」
- チャンネルには、さまざまな設定を施すチャンネル・モードが用意されている。(4.2.3 MODE command)
- ユーザは、JOINコマンドを使うことでチャンネルの作成、参加ができる
- ユーザが送信したJOINコマンドの対象(チャンネル名)が存在しなければ、チャンネルは作成される
このとき、作成したユーザはチャンネルオペレータになる。 - 対象(チャンネル名)が存在している場合は、チャンネルへの参加要求を意味するメッセージとなる。この参加要求が受け入れられかは、そのチャンネルのモードに依存する。(例えば、招待制のチャンネル(+i mode)の場合、チャンネルに参加できるのは、招待されたユーザのみとなるので、このチャンネルへの参加要求は拒否される。)
- ユーザが同時期に参加できるチャンネルの上限数を10個とすることを推奨する。[be recommended] (詳細は、8.13 Channel membershipを参照)
〜
以降は、サーバ間通信で発生する事象の記述なので、今回は省略。
〜
1.3.1 Channel Operators
- チャンネル・オペレータ(「chop」または「chanop」)は、そのチャンネルを「所有(own)」していると見なされる
- チャンネル・オペレータには、「所有」するチャンネルを管理する権限が与えられる
KICK - チャンネルからクライアントを退出させる
MODE - チャンネル・モードを変更する
INVITE - 招待専用チャンネル(+i mode)にクライアントを招待する
TOPIC - チャンネル(+t mode)のトピックを変更する
- NAMES, WHO, WHOISコマンドへの返信時に、チャンネル・オペレータのニックネームに「@」が付く
これによって、チャンネル・オペレータであることを示す
2. THE IRC SPECIFICATION
2.1 Overview
- RFC1459で説明するプロトコルは、サーバー間接続(server to server)とクライアント間接続(client to server)の両方で使用できる。しかし、クライアント間接続には、サーバ間接続よりも多くの制限がある
2.2 Character codes
- 特定の文字セットが指定されているわけではない
- プロトコルは、8ビットで1オクテットを構成するCharacter codeの集合に基づいている
- メッセージは、任意の数のオクテットで構成される; いくつかのオクテットは、メッセージの区切り(デリミタ)を表す制御コードに使用される
- このようなプロトコルであることに関係なく、デリミタとキーワードは、USASCIIターミナルとtelnet接続から使用できるプロトコルとなっている。?
- IRCはスカンジナビア語が起源なので、「{、}、|」は、それぞれ「[、]、\」の小文字に相当する。?
これは、2つのニックネームの等価性を判断する際に重要となる。?
2.3 Messages
- サーバとクライアントは互いにメッセージを送り合うが、そのメッセージへの返信は、ある場合とない場合がある
- メッセージに有効なコマンドが含まれていれば、クライアントは指定されたとおりの返信を期待すべき。しかし、無期限に返信を待つことは推奨されない。(返信の待機時間については、後のセクションで説明する。)
- 基本的に、クライアントからサーバー、サーバーからサーバーへの通信は、非同期です
- IRCメッセージは、最大で、3つの主要な構成要素がある:
[prefix(option)] [command] [command parameter(最大15個)]
※ [prefix] [command] [command parameter]は、ASCIIスペース文字(0x20)で区切られる。
prefixについて
- セミコロン「;」(ASCII 56 or 0x3b)は、prefixの存在を示す。「;」は、メッセージの先頭に付けなければならない。[MUST]
- セミコロンとprefixの間に空白を付けてはいけない。[MUST NOT]
- prefixは、サーバーが使用する。prefixはメッセージの送信元を示す
- メッセージにprefixがない場合、受信したコネクションが、そのメッセージの送信元だと見なされる
- クライアントが送信するメッセージに、prefix(「;」)を使うべきではない
- クライアントが使用できるprefixは、自身のニックネームのみ。このニックネームは、登録されている必要がある
- prefixの示す送信元が、サーバのデータベースにない場合、
送信元がメッセージの到着元とは異なるリンクから登録されている場合?、
サーバはそのメッセージを無視しなければならない。[MUST]
commandについて
- コマンドは、有効なIRCコマンドか、ASCIIテキストで表現された3桁の数字でなければならない。[MUST]
メッセージの書式について
- IRCメッセージの終端文字は、CR-LF(キャリッジ・リターン・ライン・フィード)のペアとなる
- メッセージの長さは、最大で512文字(CR-LFを含む)
(つまり、コマンドとそのパラメータに許される最大文字数は510文字。) - 継続メッセージ行の規定はない。?(バックスラッシュみたいな規定はない。といった意味か?)
- 実装の詳細は、7 Client and server authenticationを参照のこと
2.3.1 Message format in 'pseudo' BNF
- メッセージは、オクテットの連続ストリームから抽出されなければならない。[MUST]
- そのために、CR-LFをメッセージの終端文字として指定している
- 空のメッセージは無視される。(よって、CR-LFを終端文字として使うことができる。)
- 抽出されたメッセージは、<prefix>, <command>, <params>に解析される
- <params>は、更に<middle>か<trailing>のどちらかに解析される
<message> ::= [':' <prefix> <SPACE> ] <command> <params> <crlf>
<prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
<command> ::= <letter> { <letter> } | <number> <number> <number>
<SPACE> ::= ' ' { ' ' }
<params> ::= <SPACE> [ ':' <trailing> | <middle> <params> ]
<middle> ::= <Any *non-empty* sequence of octets not including SPACE
or NULL or CR or LF, the first of which may not be ':'>
<trailing> ::= <Any, possibly *empty*, sequence of octets not including
NULL or CR or LF>
<crlf> ::= CR LF
ほとんどのメッセージは、抽出されたパラメータ文字列のセマンティクスと構文を、リスト内の位置によって追加指定する。??
例えば、多くのサーバーコマンドは、コマンドの後の最初のパラメータがターゲットのリストであると仮定する??:
<target> ::= <to> [ "," <target> ]
<to> ::= <channel> | <user> '@' <servername> | <nick> | <mask>
<channel> ::= ('#' | '&') <chstring>
<servername> ::= <host>
<host> ::= see RFC 952 [DNS:4] for details on allowed hostnames
<nick> ::= <letter> { <letter> | <number> | <special> }
<mask> ::= ('#' | '$') <chstring>
<chstring> ::= <any 8bit code except SPACE, BELL, NULL, CR, LF and comma (',')>
その他のパラメータ構文:
<user> ::= <nonwhite> { <nonwhite> }
<letter> ::= 'a' ... 'z' | 'A' ... 'Z'
<number> ::= '0' ... '9'
<special> ::= '-' | '[' | ']' | '\' | '`' | '^' | '{' | '}'
<nonwhite> ::= <any 8bit code except SPACE (0x20), NUL (0x0), CR (0xd), and LF (0xa)>
NOTE:
- <SPACE>は、スペース文字(ascii 32, 0x20)のみ。
TABなど他の制御文字は、NON-WHITE-SPACEとみなされることに注意。 - <params>の解析において、<middle>と<trailing>は等しく扱われる。
<trailing>では、パラメータにスペース文字(ascii 32, 0x20)を含むことができるため、構文上分けている。 - CRとLFが、<params>文字列に含まれないのは、単純にメッセージのフレーミング??のせいである。(これは変更される可能性がある。)
- NULL文字は、メッセージ内で使用できない。
基本的にNULL文字は、メッセージ・フレーミングの中で、特別な文字として扱われないため、<params>の中に入れることができる。しかし、それによってC言語の文字列処理が複雑になるので、使用できないようになっている。 - 最後の<params>は、空文字列でもよい。
- 拡張接頭辞(extended prefix)(['!' <user> ] ['@' <host> ])は、サーバ間通信で使用してはいけない。
サーバからクライアントに送るメッセージで使用することを想定している。
これによって、クライアントがそのためのメッセージを送ることなく、メッセージの送信元に関する詳細な情報をクライアントに提供する。
2.4 Numeric replies
- サーバーは、受け取ったメッセージに対して返信を行う。[MUST]
- よく使用される返信は、数値返信(Numeric reply)で、エラーと正常な返信の両方に使用される。[MUST]
- 数値返信は、以下の構造からなるメッセージとして送信される。[MUST]
<sender prefix> <the three digit numeric> <the target of the reply>
- 数値返信は、クライアントが行うことはできない。サーバーが数値返信を受信した場合、すべて無視される。
- 数値返信と通常のメッセージは、キーワードが文字列ではなく3桁の数字で構成されていること以外、同じ。
- 返信(reply)の詳細は、第6章REPLIESを参照。
3. IRC Concepts
3.1 One-to-one communication
- いくつかのサーバを経由して、クライアント同士がメッセージのやり取りを行う場合、スパニングツリープロトコルを用いて経路を構築する必要がある。またその経路が最短な経路となることが求められる
例1: client1とclient2の間のメッセージは、serverAによってのみ見られ、
serverAはそれをそのままclient2に送信する。
例2: client1とclient3間のメッセージは、serverAとserverBとclient3によって見られる。
他のclientやserverはメッセージを見ることができない。
例3: client2とclient4間のメッセージは、serverA、B、C、D とclient4だけが見る。
3.2 One-to-many
- IRCの主な目的は、簡単な方法で、効率的に会議(1対多の通信)を可能にする場所を提供することです
(1つのクライアントが複数のクライアントへメッセージを同時に送信すること?)
3.2.1 To a list
- クライアントがユーザーの"リスト"を使って、複数のクライアントへメッセージを送信する方法が、最も効率が悪い
- クライアントはメッセージを配信する宛先のリストをサーバに与え、サーバはそれを使ってメッセージを送信する
この方法は、グループを使用する場合ほど効率的ではない
3.2.2 To a group (channel)
- IRCプロトコルにおいて、チャンネルはマルチキャストグループと同等の役割を持っている
- チャンネルは、クライアントの出入り(参加や退出)があり、動的な存在として扱割れる
- チャンネル上に送信されたメッセージは、そのチャンネルに参加するクライアントが接続するサーバにのみ送信される
- 同じチャンネルに参加する複数のクライアントが、同じサーバに接続している場合、
メッセージはそのサーバに一度だけ送信され、その後、チャンネルの各クライアントに送信される。
この動作は、チャンネルに参加するクライアント全員がメッセージを受け取るまで繰り返される。
上記の画像を参照
例4: クライアントが1人のチャンネル
チャンネルへのメッセージはサーバに送られ、その後どこにも送られない。
例5: クライアントが2人のチャンネル
すべてのメッセージは、2つのクライアント間で行われるプライベートメッセージ
のようにパスを通過する。
例6: client1、2、3が参加するチャンネル
チャンネルへのすべてのメッセージは、すべてのクライアントに送られる。
client1がメッセージを送ると、そのメッセージはclient2に送られ、
さらにserverBを経由してclient3に送られる。
3.2.3 To a host/server mask
- ホストマスクやサーバマスクのあるメッセージを送信する仕組みがある
- このメッセージは、マスク情報に一致したクライアントにのみメッセージが送られる
- メッセージの配送は、チャンネルと同様の方法で行われる
3.3 One to all
- One-to-Allタイプのメッセージとは、ブロードキャストメッセージと言える
すべてのクライアントかサーバー、またはその両方に送信される - 大規模なネットワークでは、1つのメッセージをすべての宛先へ届けるために、トラフィックが増大する
- メッセージの内容によっては、すべてのサーバにブロードキャストする以外、選択肢がないものもある
(例えば、各サーバが保持する状態情報をサーバー間で適度に統一させるようなメッセージなど)
3.3.1 Client to Client
- ひとつのメッセージだけで、すべてのクライアントに対して送信するようなメッセージのクラス(種類?)は存在しない
3.3.2 Clients to Server
- 状態情報の変更をもたらすほとんどのコマンド(channel・membership, channel・mode、user statusなど)は、デフォルトですべてのサーバーに送られなければならず [MUST] 、この配布はクライアントが変更することはできない [MAY NOT]
3.3.3 Server to Server
- サーバ間で行われるメッセージのほとんどは、すべてのサーバに配信される
- これは、ユーザー、チャンネル、サーバーのいずれかに影響を与えるメッセージにのみ要求される
- これらはIRCで見られる基本的な仕様なので、サーバーから発信されたメッセージのほとんどは、接続されているすべてのサーバーにブロードキャストされる
7 Client and server authentication
link1
- クライアントとサーバーは、同じレベルの認証を受ける
- 両者ともに、サーバへの接続すべてに対して、IP番号とホスト名のルックアップ(及びその逆チェック)が実行される
- その接続にパスワードが設定されている場合、パスワードチェックが行われる(パスワードチェックはサーバでのみよく使用される)
- 追加的なチェックとして、接続を行ったユーザー名のチェックがある。これは、一般的になりつつある
(通常、接続の相手側のユーザー名を見つけるには、RFC1413に記述されているIDENTのような認証サーバーに接続する必要がある) - サーバー間接続では、identサーバーの使用などに加え、パスワードの使用を強く推奨する
(パスワードのない接続では、通信相手が判別できない)
8.13 Channel membership
link2
- 現在(当時)のサーバーでは、登録されたローカルユーザは最大10チャンネルまで参加できる
- 非ローカルユーザには制限がないので、
サーバーは他のすべてのユーザと(合理的に)一貫性を保ったチャンネルメンバーシップを設定できる
9. Current Problems
- このプロトコルにはいくつかの問題点があると同時に、これらの問題を解決するための作業が進行している
9.1 Scalability
- IRCプロトコルは、大規模なネットワークでの使用に適さない
- この問題は、すべてのサーバーが他のすべてのサーバー、クライアント、チャンネルに関する情報を持ち、変更があるとすぐに更新される要件から生じている
- 2つのサーバ間のパス長を最小に保ち、スパニングツリーを可能な限り強く分岐させるために、サーバーの数を少なく保つことが望ましい
9.2 Labels
- IRCプロトコルには3種類のラベルがある「ニックネーム・チャンネル名・サーバ名」
- これらのラベルは、それぞれ独自のドメインを持ち、ドメイン内での重複は許されない
- しかし現在(1993年ごろ)、ユーザーが3つのラベルのうち、どれかを選ぶことが可能となっている
- これによって、ニックネーム、チャンネル名、サーバ名を一意に保つことが出来ず、衝突する可能性がある
- なので、各ラベルが一意で重複することなく、効果的に管理できる仕組みが必要
9.2.1 Nicknames
- ニックネームが重複した場合、どちらか一方の要求を拒否するか、またはKILL(4.6.1)コマンドを使用して、両方のニックネームをリストから削除する
9.2.2 Channels
- チャンネルに関する情報(チャンネル名、所属するユーザ、プロパティなど)は、すべてのサーバが保持する
- この仕様は、拡張性だけでなく、プライバシーの問題にも影響を与える
- また、チャンネル名の衝突が発生した場合、ニックネームのような排他的な解決ではなく、包括的な方法で解決される
(2人のユーザが同名のチャンネル名を作成した場合、2人のユーザはそのチャンネルのメンバと見なす。)
9.2.3 Servers
- サーバの数は、ユーザやチャンネルの数に比べて少ないが、全てのサーバで情報をもつ必要がある
- サーバは、ネットワーク内で特定のマスクによって位置や識別情報を隠していることがある
9.3 Algorithms
- サーバの実装には、チャンネルリストをチェックするとき、N^2アルゴリズムで行われている箇所がある
- 現在(1993年ごろ)のサーバでは、データベースの一貫性チェックはなく、各サーバは隣接するサーバが正しいと仮定する
- このため、バグを多く含んだサーバの接続や既存のネットワークに矛盾を持ち込む、と大きな問題が発生する
- 現在(1993年ごろ)、一意な内部ラベルとグローバル・ラベルがないため、競合状態が多く存在する
- これら競合状態は、一般的に、メッセージがIRCネットワークを通過して影響を及ぼすのに時間がかかるという問題から発生する
- またユニークなラベルに変更しても、チャンネル関連のコマンドが中断されるという問題がある
最後に
deepLに翻訳してもらいながら読み進めましたが、理解するのが難しかったです。
クライアントとユーザは、同義なのか?といった単語の解釈をするのに時間が掛かりました。
まあ、とりあえず、IRCプロトコルについて少し分かったと思います。
次回は、RFC2810 Internet Relay Chat: Architecture を読んでみたいと思います。
参考