|
>>4388 68userさん いろいろと細かい点までアドバイスをしていただきましてありがとうございます。 大変勉強になります。 全てに指摘点に対しご回答差し上げたいのですが、 まだ指摘された部分の修正作業を行っておりませんので、 一通り読ませていただき、気づいた点について記述させていただきます。 まず return と exit の混在ですが、 これは conv_type 関数や conv_class 関数の部分だけを 指した指摘でしょうか。 それとも exit を一つも使わない方が良いという事でしょうか。 return と exit の混在のデメリットが分からなかったので。 後、変数 search ですが、仰るとおりインターフェースがいまいちです。 何故なら現在位置を返す関数と返さない関数が混在しているからです。 ここの処理がうまく思いつかなかったので search += 2; で逃げていました(^^;。 そして変数 search の大きな問題が仰るとおり > additional = two_bytes(&int_buf[search], &search); > search += 1; です。 ここは作った当人で無いと分からないですよね(^^;。 その理由は、開発途中で方針を変更してしまったためです。 そのため現在位置は ・解析し終わった部分の、次の部分の先頭に現在位置を合わせている所 ・解析し終わった部分の末尾に現在位置を合わせている所 の2パターンが発生してしまいました。 その代償が search += 2; と search += 1; の混在です。 ここは人に解析してもらう際にはまずいので修正します。 あと host[j] = 0; ですが、ここでは www.livedoor.com というドメイン名を 3www8livedoor3com0(数字の部分は文字ではなく、文字コード)に変換しているのですが、host[j] = 0; は ドメインの最後に文字コードの 0 を連結する と言う意味で記述しました。 何となく思ったのですが、 3www8livedoor3com0 の最後の 0 は終端という意味かな。 そう考えると仰られている意味が分かりました。 その部分に関しては、記述ミスと言うより、私の理解が間違っていたのかなと思います。 それと、int_buf は完全に逃げです(^^;。 ここの部分のうまい処理が思いつきませんでした。 他の指摘された部分につきましても、順次修正して良いものを作りたいと思います。 お忙しい中、いろいろご指摘いただきありがとうございます。 |
|
>>4389 DNS勉強中 > まず return と exit の混在ですが、 main から抜ける際に return と exit が混在していることを指したつもりでした。 下請け関数から exit するのは、狙っているのなら別にいいんじゃないかと思います (課題のプログラムですし)。変な値がやってきたら終了してしまうようでは、本格的な 運用で使うことはできませんが、 - 本格的な運用では使えないことを認識している。 - どういう要求が出てきたときに問題が起こるかなんとなく想像できる。 たとえば web サーバのログから IP アドレスを拾って、複数の名前解決を 高速に行うプログラムを作りたいという要求があったとすると、この プログラムではひとつでも変なレスポンスがくると exit してしまうので 実運用には耐えられない。 - 本格的な運用で使用する場合は、どこをどういうふうに書き換えればよいか だいたい把握している。 なら問題ないと思います。 > return と exit の混在のデメリットが分からなかったので。 わたしの中では以下のようなルールにしています。 1. 基本的に main まで戻る。エラー発生時の情報出力を行うことを考えると、 各関数の役割分担が明確になるから。 2. 例外として、引数解析エラー表示の usage 関数内では exit してよい。 理由は、処理が始まっておらず後始末が不要であることと、usage は 数箇所〜数十箇所から呼ばれる可能性があり、呼び出し元での exit 忘れが怖いこと。 if ( argv[i][0] != '-' ){ usage(); exit(1); /* これを書き忘れると処理が続行してしまう */ } 1 の詳細は以下のとおり。 今回のように 1件だけのデータを処理する場合は、変な値がきたときに ほげフラグが 1〜5 の範囲外です などと出力すれば問題ないが、 一度に複数のデータを処理するバッチの場合は ほげフラグが 1〜5 の範囲外です。受注番号[%d] レコード番号[%d] 顧客番号 [%d] などとより多くの情報を出力しないと、どのデータがおかしいのか特定できない。 とはいえ、下請け関数である 文字列→コード変換関数に、 conv_type(query_type, order_no, rec_no, customer_no); などとエラー発生時に備えて各種情報を渡すのは変。エラー出力用のグローバル変数を 作って err_order_no = order_no; err_rec_no = rec_no; err_customer = customer_no; conv_type(query_type); とする、なんてのも馬鹿馬鹿しい (後者はたまに見かけてイラッときますが)。 となると、 int main(){ ret = file_main(); if ( ret == -1 ){ logging("エラー発生。処理中断・ロールバックし、全データを元に戻します"); /* 後始末を一箇所にまとめる */ } } int file_main(){ fp = fopen(filename[i], "rw"); while (1){ fgets(fp, buf, sizeof(buf)); ret = hoge_main(buf); if ( ret == -1 ){ logging("hoge_main でエラー発生。ファイル名[%d] 行番号[%d]", filename[i], line); return -1; } } } int hoge_main(char *buf){ type = conv_type(query_type); if ( type == -1 ){ logging("query_type に変換に失敗。受注番号[%d]", rec_no): return -1; } } int conv_type(char *type){ if ( 異常値 ){ logging("type が異常値 [%s]", type); return -1; } } として main まで戻るのが現実的。conv_type はファイル名だの受注番号だの 責任範囲外のことを知らなくてもよいが、最終的にはエラーリカバリに必要な 情報が揃う。 Java などのいまどきな言語であれば例外を使うべきだろうと思います。 > 3www8livedoor3com0(数字の部分は文字ではなく、文字コード)に変換して > いるのですが、host[j] = 0; は ドメインの最後に文字コードの 0 を連結 > すると言う意味で記述しました。 戻り値を strcpy(DNS_Send.QUERIES, make_domain(argv[2])); と文字列として扱うのであれば、末尾は文字終端 '\0' であるべきで、 host[j] = 0; より host[j] = '\0'; にすべきという意味で書きましたが、たしかに文字列終端でもあり、DNS 的な 0 終端でもあるわけで微妙なところですね。 バイナリ列とみなして長さを明示的に管理し、strcpy のかわりに memcpy を 使うか、あるいは動作的には問題ないのでコメントで補足しておくか、あるいは 修正は不要なのか…。 > それと、int_buf は完全に逃げです(^^;。 いいんじゃないでしょうか。「バグを出さない」ということを第一目的とするなら アリだと思います。わたしなどいまだに char と unsigned char あたりの暗黙の 型変換ではまったりしていますので、キャストがちりばめられているソースだったら 解読に苦労していたかもしれません。 ちなみに単なる「char」が signed であるか unsigned であるかは環境依存です。 とはいえ、わたしは一般的な UNIX しか知らないので、char が unsinged char である環境は見たことはないですが。 |
|
>>4389 DNS勉強中 あと、さらに細かいことをひとつ。インデントは正確につけましょう。フラグ表示 の部分をわかりやすくするために、ひとつ余計にインデントしていますが、 { フラグ表示 } と、ブロックで囲めばすみます。 インデントはプログラマがつけるものではなくエディタが勝手につけるものであり (emacs の M-x indent-region などで)、もしエディタが変なふうにインデントして しまったら、それは括弧の過不足や文字列の閉じ忘れが原因、などと気づくことが できます (python などは除く)。 # というわたしのポリシーはなかなか理解してもらえない。なぜ手でインデントを # 付ける人が多いのだろう (って、素の vi やメモ帳を使ったりするからだが)。 # めんどくさくないのかなぁ。 |
|
>>4388 68user >>4390 68user >>4391 68userさん 今日は上記3レスでアドバイスいただいた内容についてある程度修正したり、 理解に努めておりました。 ・ struct DNS_Query *query; の必要性について理解いたしました。 確かに仰られるとおり、ソースが理解しやすくなると思いました。 そのため、その部分は修正しました。 (ただ make_question, make_query との関係上、一部修正しております)。 また、make_question と make_query を作成することにより、 main 部の記述を減らし、ソースを読みやすくしました。 ただ、記述していただいたとおりに作れなかったので、メイン部に make_question(&DNS_question); count = make_query(&DNS_query, argc, argv[2], argv[3], argv[4]); と2つにわけて記述しております。 ・ パディングについても理解いたしました。 確かに私の以前のソースはパディングが入らない前提で組んでありました。 やっと意図されたことが分かりました(^^;。 ・ /usr/include/bfd.h をみて、 プロトタイプ宣言について仰られていることを確認・理解し、修正しました。 ・ perror を用いエラー処理を追加しました。 ・ 抜けていた gethostbyname のエラー処理を追加しました。 ただし、 herror や hstrerror を Windowsで使う方法が分からなかったので、 とりあえず perror を使っております。 ・ return と exit の混在を解消しました。 それにともない、 main に戻らずに終了していた所を、 main に戻って終了するように変更しました。 またそれらしいエラー情報を出力するように変更しました。 ・ two_bytes の記述変更について理解いたしました。 アドレス渡しで現在位置を変更して、 戻り値で2バイトの値を取得する方法は大変参考になりました。 確かに、教えていただいた記述の方が何かと便利ですので、 ほぼ同じソースを移植させていただきました。 それに伴い、謎の search += 1; を修正しました。 また four_bytes も同様にソースを変更しました。 この作業を実施したことにより、 search += 2; や search += 4; を大幅に減らすことが出来ました。 ・ conv_type, conv_class, class_print, type_print を codelist_t を使用する形に変更しました。 ただし、 void class_print(int class) を用いた共用の記述の仕方が 分からなかったので、 codelist_t {class, type}_list[] = { ... };をグローバル化して共用しています。 また、 qr_print, opcode_print, aa_print, tc_print, rd_print, ra_print, rcode_print も同様に codelist_t { ... }_list[]化しました(こちらはローカル)。 ・ 0 と \0 の混在を修正しました。 ・ 変数名や関数名の命名を修正しました。 ・ フラグ部分のインデントを変更しました。 ブロックで囲むと言う発想が出てこず、 かといって、インデントなりコメントで上下を囲むなどしないと、 のちのち分からないだろうなと思い、 インデントしてしまったのですが、 ブロックで囲むと言う方法が教えていただき、大変参考になりました。 とりあえず今日修正できたところは以上です。 上記以外の部分についてはまだ手をつけておりません。 他に、今まで教えていただいた事を他の部分にも流用できないか、 ソースを一通り読み直してみて調べてみようと思います。 また search を触ったことのより、ある程度検証をする必要があるのかなと思います。 月曜日は予定が入っているので、まとめるのは難しいかも知れませんが、 火曜日くらいにある程度まとめて一度アップしてみようと思います。 いろいろ参考になるアドバイスをいただきまして、ありがとうございます。 |
|
to 68userさん さっそくですが、IPv6 のアドレス省略について 双方のプログラム(resolver-1.pl, 1058.txt)に バグがあるかも知れないのでご報告します。 一例として、 198.41.0.4 www.yahoo.co.jpと入力して、 {A, B, C, D, E, F}.DNS.jpを表示させたとき、 追加情報の F.DNS.jp の出力結果は下記のとおりになります。 resolver-1.pl 2001:2F8::100::153 私の出力結果 2001:2f8::100:0:0:0:153 ちなみにEtherealの出力結果は下記のとおりです。 2001:2f8:0:100::153 IPv6 のアドレス省略表記に関する説明を下記のサイトで拝見したのですが、 http://www.soi.wide.ad.jp/iw2001/slides/04/04-1/53.html そこには IPv6 のアドレス表記に関して2つの原則が書かれていました。 1,【連続する】0 のブロックは省略できる。 2,省略できるのは【1ヶ所】だけ。 そして出力結果から推察するに、 resolver-1.pl では、原則の1, 2共に満たしてないように見受けられます。 perl が分からないので想像ですが、 resolver-1.pl では 0 を見つけたら即座に :: に置き換え、 何回でも省略が使用できるように組んであるのかなと思われます。 私の方では、一度省略を使用したら、二度と使えないようには組んでいるのですが、 0 は即座に :: に置き換えております。 そのため 0 を見つけたら、次の場所を見て、 ・次の場所も 0 なら :: に置き換える ・次の場所が 0 で無ければ、 0 を出力する。 というコードを追加しないといけないかもしれません。 勘違いでしたらすいません。 |
|
>>4393 DNS勉強中 ありがとうございます。 とりあえず TYPE=TXT の件と IPv6 の件を bugid20・21 として登録しました。 http://x68000.q-e-d.net/~68user/bugnote/bugindex.php?projectid=1 ちなみになぜわたしが web を更新しないのは、解説文からソースへの リンク方法が http://x68000.q-e-d.net/~68user/cgi-bin/cvsweb.cgi/public_html/net/org/resolver-3.html?rev=1.11&content-type=text/x-cvsweb-markup のように :preinclude 21-106 などと行番号で指定しているため、修正が非常に面倒であるからです (まぁただの言い訳なのですが)。 +10 などの相対指定にしようかとか、 m/bind\(/ などと文字列で引っ掛けようかとか迷いつつここまで来てしまいました。 ご指摘を頂いたことについては大変感謝しておりますが、修正が遅いのは なにとぞご容赦ください。 |