|
こんばんは。 現在、DNSクライアントを勉強中です。 サンプルをまだ解析中なんですけど、どうしてもわからない箇所がいくつかあるので…質問させていただきます。 まず、フラグについてです。 えっと…少し説明しにくいんですけど、何故、16ビットと書いてあるのに第15ビットまでの説明までしかないのでしょうか? 次に、ネットワークバイトオーダについてです。 サンプル中で、質問数、回答数、権威数などはpackするときにネットワークバイトオーダに変換しているのに、IDとフラグを変換しないのは何故でしょうか? どういう物をネットワークバイトオーダに変換しなければいけないのかよくわからなくて…。 最後に、サンプル中に出てくる$restについてです。 データを受信した時、細かく言うと$restにはどういった順番でデータが入っているのでしょうか? それがよくわからなく、get_domainサブルーチンに渡したときの処理の仕方がよくわかりません…(−−; 以上長々と申し訳ありませんが、どうかよろしくお願いしますm(_ _)m |
|
>>3325 ken > 何故、16ビットと書いてあるのに第15ビットまでの説明までしか > ないのでしょうか? http://x68000.startshop.co.jp/~68user/net/resolver-2.html#flag のところですね。わたくしの誤りです。RCODE が 3ビットとなって いますが、正しくは4ビットです。近いうちに直します。 # 表の部分で「1……15bit」とあるところも、「1……16bit」の # 誤りですね。 > 質問数、回答数、権威数などはpackするときにネットワークバイト > オーダに変換しているのに、IDとフラグを変換しないのは何故でしょうか? http://x68000.startshop.co.jp/~68user/net/resolver-3.html#4 の以下の部分ですね (一部略)。 sub make_query { my ($type, $class) = @_; # 識別子設定 my $id = pack('B16', '0000000000000000'); # フラグを構成する各要素を設定 my $qr = QR::Query; # 0:質問 my $opcode = OPCODE::StandardQuery; # 0:標準照会 my $aa = 0; # Authoritative Answer (応答時にセット) # フラグ (0 と 1 の文字列) my $flg_binary = sprintf("%d%04d%d%d%d%d%03d%d", $qr, $opcode, $aa, (略)); # フラグ (2バイトのバイナリ) my $flg = pack("B16", $flg_binary); # 質問数のみ 1。回答数、権威数、追加情報数はいずれも 0。 my $question_count = pack('n', 1); my $response_count = pack('n', 0); # foo.example.com というホスト名を 3foo7example3com という形に変換。 my $query_name = &make_domain($query_host); # リクエストは「識別子+フラグ+質問数+回答数+権威数+追加情報数+質問」からなる。 my $request = $id.$flg.$question_count.$response_count.$auth_count.$ext_count.$question; return $request; } ID やフラグは pack('B16') でバイナリデータに変換していますが、 その際に順番が入れ替わることはありません。 pack('B16', '1111000000000011') は順序を保持したまま 1111000000000011 となります。 0000001111110000 となることはありません。 ただし数値の場合は、Short 値を作ろうとして素直に my $question_count = pack('s', 1); などとしてしまうと、リトルエンディアンな Intel 系では 0x01 0x00、 ビッグエンディアンな Motorola・Sparc 系では 0x00 0x01 となって しまいます。よってこの場合は my $question_count = pack('n', 1); と n を使って、どの環境でも 0x00 0x01 となるようにするわけです。 > データを受信した時、細かく言うと$restにはどういった順番でデータが > 入っているのでしょうか? 以下、http://x68000.startshop.co.jp/~68user/net/sample/resolver-1.pl の sub parse_response 関数の説明をします。 my ($id, $flg, $question_count, $response_count, $auth_count, $ext_count, $rest) = unpack('nnnnnna*', $org_dgram); の時点では、$rest には「(7)質問レコード以降のデータ」(回答レコード・権威 レコード・追加情報レコードも含む) が入っています。 データの順序は http://x68000.startshop.co.jp/~68user/net/resolver-2.html#4 の「解析した応答の構成」の順のとおりです。 $rest の先頭のデータは「(7)質問レコード」ですが、「(7)質問レコード」は (7.1)照会名 (可変長) (7.2)照会タイプ (7.3)照会クラス から成り立っていますので、$rest の先頭データは「(7.1)照会名」であるとも 言えます。 その後、 foreach my $count ($question_count){ my $domain; # この時点で $rest の先頭データは「(7.1)照会名」 ($domain, $rest) = get_domain($rest, $org_dgram); # この時点で $rest の先頭データは「(7.2)照会タイプ」 my ($type, $class) = unpack('nn', $rest); # ↑「(7.2)照会タイプ」と「(7.3)照会クラス」を取り出す。 substr($rest, 0, 4) = ''; # ↑取り出した「(7.2)照会タイプ」と「(7.3)照会クラス」を削る。 printf "質問: ドメイン名: %s\n", $domain; printf "質問: タイプ: %d (%s)\n", $type, TYPE::getName($type); printf "質問: クラス: %d (%s)\n", $class, CLASS::getName($class); print $hr_line; } といったふうに、$rest の先頭部分から順に、データを取り出す⇒削る という処理を繰り返します。 質問部分を読み終わると、$rest にはリソースレコード (回答・権威・追加情報) が 残っています。リソースレコードは ドメイン名 (可変長) タイプ クラス 生存時間 (TTL) リソースデータ長 リソースデータ (可変長) から成っていますので、 foreach (1 .. $count){ my $domain; ($domain, $rest) = get_domain($rest, $org_dgram); # ↑ここでドメインを切り出す。 my ($type, $class, $ttl, $rdata_length) = unpack('nnNn', $rest); # ↑タイプ・クラス・生存時間・リソースデータ長を取り出す。 substr($rest, 0, 10) = ''; # ↑タイプ・クラス・生存時間・リソースデータ長の分を削る。 my ($rdata) = substr($rest, 0, $rdata_length); substr($rest, 0, $rdata_length) = ''; # ↑リソースデータ取得 } と、ここでもやはり「データを先頭から取り出し、削る」の繰り返している わけです。 |
|
なるほど…だいたいよくわかりました、ありがとうございます。 でもまだget_domainサブルーチン中の処理がよくわかりませんねぇ…。 while文の最初のif文の中です。 493: if ( ( $len & 0xc0 ) == 0xc0 ){ 494: # 上位 2ビットが立っていたら、もう 1バイト読みこむ (合計 16ビット)。 495: # そして上位 2ビットを落とし、残り 14ビットを DNS サーバから 496: # 返された UDP データグラム先頭からのオフセットとして再帰。。 497: 498: my $offset = unpack('n', $data) ^ 0xc000; 499: my $new_data = substr($org_data, $offset); 500: substr($data, 0, 2) = ''; 501: 502: my ($domain_part) = get_domain($new_data, $org_data); 503: push(@domains, $domain_part); 504: last; 505: 506: } else { 507: # 上位 2ビットが立っていないので、カウンタとして扱う。 508: substr($data, 0, 1) = ''; 509: } まず、if文の中で$dataの上位2ビットが立っていたときに、その2ビットを除いた上位14ビットを$offsetに入れて、$org_dataの$offset番目からのデータを$new_dataに入れて、$dataの上位2ビットを落としたところまではわかりました。 そのあと再びget_domainサブルーチンを呼び出すところからが…どうなるのでしょうか??(^^; また、else文の中の1行はどういった意味を持っているのでしょうか? ここでいう「カウンタ」という物もよくわからなくて…。 どうか、よろしくお願いします |
|
>>3327 ken > そのあと再びget_domainサブルーチンを呼び出すところからが… > どうなるのでしょうか??(^^; http://x68000.startshop.co.jp/~68user/net/resolver-2.html#6 の例だと、 最初に get_domain が受け取った $data が (0x04)www2(0xC0)(0x13).... で、一回目の while ループが終了した時点で $data = "(0xC0)(0x13)...." @domains=('www2');★1 となっています。 二回目の while ループでは my $len = unpack('C', $data); で $len は 0xC0 になり、 if ( ( $len & 0xc0 ) == 0xc0 ){ が真なので my $offset = unpack('n', $data) ^ 0xc000; ⇒ $offset は 0x13 my $new_data = substr($org_data, $offset); ⇒ $new_data は、UDP データグラム全体 ($org_data) の 0x13バイト目 substr($data, 0, 2) = ''; ⇒ $data は "(0xC0)(0x13)...." から "...." となる (先頭 2バイトを削る) となります。$new_data には (0x09)startshop(0x02)co(0x02)jp(0x00) が入っています。 ここで my ($domain_part) = get_domain($new_data, $org_data);★2 push(@domains, $domain_part);★3 と get_domain を再帰呼び出しすると、 sub get_domain { my ($data, $org_data) = @_; の $data には (0x09)startshop(0x02)co(0x02)jp(0x00).... が入っています。再帰呼び出しされた get_domain が処理を終えるときには @domains = ('startshop', 'co', 'jp'); となっており、最終的に return 'startshop.co.jp'; とドメインの一部を返します。 で、再帰呼び出しした側の get_domain に処理が戻ると、以下のような 流れになります。 ★1 の @domains にはすでに www2 が入っている ★2 の $domain_part には、再帰呼び出しした get_domain から受け取った startshop.co.jp が入っている ★3 で @domains = ('www2', 'startshop.co.jp') となる > また、else文の中の1行はどういった意味を持っているのでしょうか? > ここでいう「カウンタ」という物もよくわからなくて…。 (0x03)www(0x02)jp(0x07)FreeBSD(0x03)org(0x00) の 3・2・7・3・0 がここでいうカウンタです。$len には その後に続くバイト長が入っており、 508: substr($data, 0, 1) = ''; はその $len の部分を削っています。 # 説明文で使用していない用語 (カウンタ) を、ソースで使ってはいけませんね。 |
|
先日質問させていただいた、tomtomです。 クライアントが受信待ちの状態の時に、サーバに ソケットをクローズされたら、クライアントは、 どうなるのでしょうか? 以前質問させていただいた時に、undefを受信してしまう という問題があったのですが、サーバ側で勝手にセッションを 終了してしまっていた可能性があるので、その辺が関係している かどうか疑問に思ったため、再度質問させていただきました。 |
|
あ〜っ、なるほど! わかりました!完璧にわかって疑問が紐解けましたよ〜! get_domainの再起呼び出し後はそういう処理だったんですね…。 カウンタの意味もわかりましたし(^^;)、もうばっちりです! 長々と本当にありがとうございましたm(_ _)m もしまた何かありましたら…よろしくお願いいたします(^^; |