gethostbyaddr 探検
前回の大河小説を見ていたら、こんなのが
以前から気になっていることがあるのですが、この連載の主題の「探検」。パソコンで 漢字変換した際に、木へんの「検」とこざとへんの「険」が出てきます。 で、ググってみたところ 探検: 調査 探険: 危険を伴う探検
全く気にもとめてませんでした。上手に使い分けろよ。まあ、オイラーがやる事は、観光なんで 探検ですな。
行ってQと言う番組、どんどん過激になるな。受けを狙って無謀な事をさせてる。タレントさんは、きっと拒否出来ないんだろうね。代わりはいくらでもいますからって。ブラック番組と思うぞ。そのうちに大事故が起きないか心配。とか言いながら、見ちゃうんだな。見てる分には、安全ですから。
dig 探検
原典にあたります。
前回、逆引きして、12人に1人は、名無しの権兵衛ってのを見つけ、これは諜報機関に違いないって結論を出しちゃった。原典では、逆引きはオプションってしっかり書いてあったぞ。 こういうのをdigすると、ちゃんと未対応って回答が有るのかな。後でやってみよう。
安全・安心なソース観光コースを用意しましょ。勿論安全・安心と言ったらOpenBSD。 ソースの在処は、bindの中のbin中にあった。これって、bindの成果物でユーザーに提供する コマンド類の元ソース置き場だな。それにしては、有名なnamedが無いぞ。どーなってんの?
慌てて、NetBSDを参照してみる。
/usr/src/external/bsd/bind nb8$ ls bin CVS/ check/ dig/ html/ nsupdate/ Makefile confgen/ dnssec/ named/ rndc/ Makefile.inc delv/ host/ nslookup/ tools/
こちらは、bindのフルセットが採用されてました。ちゃんと namedも居るしね。
ふと、man3の原稿置き場で、ls get* したら、面白いものを発見。寄り道は3文の得だね。
nb8$ getaddrinfo www.NetBSD.org dgram inet6 udp 2001:470:a085:999::80 0 dgram inet udp 199.233.217.205 0 stream inet6 tcp 2001:470:a085:999::80 0 stream inet tcp 199.233.217.205 0
それはいいんだけど、OpenBSDで消えてしまっているnamedはどうなった? リリースノートを見ていたら、そっと書いてあったぞ。
DESCRIPTION NSD is a complete implementation of an authoritative DNS nameserver. Upon startup, NSD will read the database specified with -f database argument and put itself into background and answers queries on port 53 : AUTHORS NSD was written by NLnet Labs and RIPE NCC joint team. Please see CREDITS file in the distribution for further details.
大事な部分だから、別の船に乗り換えたんか。これで、毒入りパケットを防ごうって魂胆なんだな。何かとbind方面は騒々しいですから。
原点復帰。digをgdbにかけられるように独自にコンパイルしたい。 bindのdir下に、configureが有るんで、そこでごちゃごちゃしろって事だな。/tmpにそっくりコピーして、configしてからmakeしたら、無事にdigが出来上がった。
けど、惜しいかな、-g -O2 なんだよな。Makefileを書き換えちゃえ。
ob64$ for f in `find . -name Makefile` > do > sed -e 's/-g -O2/-ggdb3 -O0/' < $f > $$ > mv $$ $f > done
無事にdigが出来たので、yahoo.co.jpのIPを逆引きしてみる。
(gdb) b dig.c:1345 Breakpoint 1 at 0x4c12: file dig.c, line 1345. (gdb) r -x 183.79.135.206 Starting program: /tmp/bind/bin/dig/dig -x 183.79.135.206 Breakpoint 1, dash_option (option=0xcf7d79bf "x", next=0xcf7d79c1 "183.79.135.2\ 06", lookup=0xcf7d735c, open_type_class=0xcf7d7358, need_clone=0xcf7d733c, conf\ ig_only=isc_boolean_false, argc=3, argv=0xcf7d7904, firstarg=0xcf7d7360) at dig\ .c:1345 1345 if (get_reverse(textname, sizeof(textname), value,
ちょいと進めると、問い合わせ内容が作成されてた。
(gdb) p *(*lookup) $7 = { pending = isc_boolean_true, waiting_connect = (unknown: 3200171710), : textname = "206.135.79.183.in-addr.arpa.\000", '\276' <repeats 995 times>, cmdline = "\000", '\276' <repeats 1023 times>, rdtype = 12, qrdtype = 1, rdclass = 1,
先へ進めて
(gdb) 1783 check_result(result, "isc_app_onrun"); (gdb) 1784 isc_app_run();
isc_app_run()を実行した時、下記の結果が得られたよ。
;; ANSWER SECTION: 206.135.79.183.in-addr.arpa. 900 IN PTR f1.top.vip.kks.yahoo.co.jp.
やっぱり、ソースだけを目視してたら、こういう風に現場を押さえられなかっただろね。
続いて、isc_app_runに舞台を移して、トレースしたけど、
(gdb) 475 result = handle_signal(SIGHUP, reload_action); (gdb) 476 if (result != ISC_R_SUCCESS) (gdb) 547 (void)isc__taskmgr_dispatch(); (gdb) 549 result = evloop(); (gdb) Program received signal SIGTERM, Terminated. kill () at -:3 3 -: No such file or directory.
こういう難解な部分が出てきたので、これはもう撤収だな。ただ想像するに、DNSサーバーとのやり取りになるんで、別タスクにそれを任せて、結果が返ってきたらSIGTERMで、お知らせしてくれるような作りになってるぽい。
gethostbyaddr
挫折したんで、別の手を考える。逆引きってどうやってるのを知りたい。これがdig観光の大目玉なんだ。(最初に目的書かなかったけどね)
前回golangで逆引きする手続きを書いた。いや、書いたと言うより、正引きのコードの一行を変更しただけ。軽やかに逆引きできた。
よくそんな逆引き関数を見つけたな? コードをじっと眺めていたら、netパッケージを見ろと訴えかけていたんで、その通りに Package net のLookupHostを探したのさ。そしたら、
type Resolver func (r *Resolver) LookupAddr(ctx context.Context, addr string) (names []string, err error) func (r *Resolver) LookupCNAME(ctx context.Context, host string) (cname string, err error) func (r *Resolver) LookupHost(ctx context.Context, host string) (addrs []string, err error) func (r *Resolver) LookupIPAddr(ctx context.Context, host string) ([]IPAddr, error) func (r *Resolver) LookupMX(ctx context.Context, name string) ([]*MX, error) func (r *Resolver) LookupNS(ctx context.Context, name string) ([]*NS, error) func (r *Resolver) LookupPort(ctx context.Context, network, service string) (port int, err error) func (r *Resolver) LookupSRV(ctx context.Context, service, proto, name string) (cname string, addrs []*SRV, err error) func (r *Resolver) LookupTXT(ctx context.Context, name string) ([]string, error)
こんな群れが見つかった。で、ピンときたんだ。C語のライブラリィーも、群れていないかと。
手元に有った、ネット本で、正引き関数を調べそいつをman gethostbyname。
NAME gethostbyname, gethostbyname2, gethostbyaddr, gethostent, sethostent, endhostent, hstrerror, herror - get network host entry SYNOPSIS #include <netdb.h>
やっぱり群れてたね。名前で引くか、アドレスで引くかって事だ。惜しいかな、使い方の例は載っていなかった。
大先輩のお知恵を借りよう。誰もが最初は初心者。考えて出て来るような代物ではないからね。
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> int main(int argc, char *argv[]) { char *whichadr; struct hostent *host; struct in_addr addr; whichadr = "127.0.0.1"; if (argc > 1) whichadr = argv[1]; addr.s_addr = inet_addr(whichadr); host = gethostbyaddr((const char *)&addr.s_addr, sizeof(addr.s_addr), AF_INET); if (host == NULL) { /* gethostbyaddrのエラーはperrorでは無いので注意 */ herror("gethostbyaddr"); return 1; } printf("%s\n", host->h_name); return 0; }
先輩のコードをちょっと変更して、引数でIPアドレスを受け付けるようにしてみた。
折角なので、少し逆引きしてみる。
ob6$ ./a.out xxx.xxx.xxx.xxx # 名無しの権兵衛さん(代表) gethostbyaddr: Unknown host ob6$ ./a.out 10.0.2.15 # vbox のNAT環境で配られるやつ gethostbyaddr: Unknown host ob6$ ./a.out 192.168.10.10 # ローカル代表 gethostbyaddr: Unknown host ob6$ ./a.out 172.217.25.195 # www.google.co.jp nrt12s13-in-f3.1e100.net
digと答え合わせしたら、あってたよ。よかったね。
エラー時の検出はNULLでやってるけど、他のエラーは無いのかなと、FreeBSDのmanを見ていたら、何とgethostbyaddrの例が載ってた。例が載ってるって事は、使い方が難解な部類に入るのだろうか?
一つのOSに固執しないで、色々と見比べてみると利得が大きくなるって例だな。
EXAMPLES Print out the hostname associated with a specific IP address: const char *ipstr = "127.0.0.1"; struct in_addr ip; struct hostent *hp; if (!inet_aton(ipstr, &ip)) errx(1, "can't parse IP address %s", ipstr); if ((hp = gethostbyaddr((const void *)&ip, sizeof ip, AF_INET)) == NULL) errx(1, "no name associated with %s", ipstr); printf("name associated with %s is %s\n", ipstr, hp->h_name);
gethostbyaddr 観光
折角C語の見通しの良いコードが有るので、探検してみる。
(gdb) p whichadr $1 = 0x1c4385b00b30 "127.0.0.1" (gdb) p addr.s_addr $2 = 16777343 (gdb) p/x addr.s_addr $3 = 0x100007f
逆引き直前の値。文字列表現のIPアドレスが、整数に変換された。意味不な数値になったけど、 16進数表記にしてみると、一目瞭然。7fってのは1バイトで数値に直すと127になる。色々なIPを指定して、楽しむのさ。
(gdb) s gethostbyaddr (addr=0x7f7ffffc2c14, len=4, af=2) at /usr/src/lib/libc/asr/gethostnamadr.c:175 (gdb) info macro AF_INET Defined at /usr/include/sys/socket.h:164 included at /tmp/rev.c:3 #define AF_INET 2
OpenBSDの真骨頂。libcの生まで潜って行けますが、取り合えず先に進めます。
main (argc=1, argv=0x7f7ffffc2c68) at rev.c:20 20 if (host == NULL) { (gdb) set print pretty (gdb) p *host $4 = { h_name = 0x1c459f4d84b8 <_entbuf+24> "localhost", h_aliases = 0x1c459f4d84a0 <_entbuf>, h_addrtype = 2, h_length = 4, h_addr_list = 0x1c459f4d84a8 <_entbuf+8> }
ちゃんと答えが得られました。
そんじゃ、先ほどすっ飛ばしたlibcのソースにご対面しようと思って行ってみたら、asr_run.3なんてのがCのソースに混じって置いてあった。これはもう、 man asr_runする鹿ないな。
そして、 gethostnamadr_async.c:hostent_from_packet() この関数あたりかな。
(gdb) b hostent_from_packet Breakpoint 1 at 0xeb1e2d5f746: file /usr/src/lib/libc/asr/gethostnamadr_async.c, line 521. (gdb) r 172.217.25.195 Starting program: /tmp/a.out 172.217.25.195 Breakpoint 1, gethostnamadr_async_run (as=0x1bb2f506fb00, ar=0x7f7ffffc02c8) at /usr/src/lib/libc/asr/gethostnamadr_async.c:299 299 h = hostent_from_packet(as->as_type, (gdb) bt #0 gethostnamadr_async_run (as=0x1bb2f506fb00, ar=0x7f7ffffc02c8) at /usr/src/lib/libc/asr/gethostnamadr_async.c:299 #1 0x00001bb2a50fe971 in _libc_asr_run (as=<optimized out>, ar=0x7f7ffffc02c8) at /usr/src/lib/libc/asr/asr.c:148 #2 _libc_asr_run_sync (as=0x1bb2f506fb00, ar=0x7f7ffffc02c8) at /usr/src/lib/libc/asr/asr.c:195 #3 0x00001bb2a510d59e in gethostbyaddr (addr=<optimized out>, len=<optimized out>, af=<optimized out>) at /usr/src/lib/libc/asr/gethostnamadr.c:183 #4 0x00001bb0074005c8 in main (argc=2, argv=0x7f7ffffc04d8) at rev.c:18
man resolverも参考になるな。
port 53
原典にあたった時、53番さんって言ってたので、DNSサーバーへ問い合わせしてみる。
ob64$ ./a.out 172.217.25.195 nrt12s13-in-f195.1e100.net
こんな問い合わせをした時に、どんなパケットが行くかモニターしてみる。貧乏人なので、wiresharkなんて飼えないのさ。伝統的なtcpdumpです。
ob64$ doas tcpdump -i em0 -t -n -s 200 port 53 tcpdump: listening on em0, link-type EN10MB 10.0.2.15.24174 > 8.8.8.8.53: [bad udp cksum fecd! -> 8172] 54155+ PTR? 195.25.217.172.in-addr.arpa.(45) (ttl 64, id 1529, len 73) 8.8.8.8.53 > 10.0.2.15.24174: [udp sum ok] 54155 q: PTR? 195.25.217.172.in-addr.arpa. 4/0/0 195.25.217.172.in-addr.arpa. PTR nrt12s13-in-f195.1e100.net., 195.25.217.172.in-addr.arpa. PTR nrt12s13-in-f3.1e100.net., 195.25.217.172.in-addr.arpa. PTR nrt12s13-in-f195.1e100.net., 195.25.217.172.in-addr.arpa. PTR nrt12s13-in-f3.1e100.net.(142) (ttl 64, id 945, len 170)
tcpdumpの引数の説明
-i em0 NIC em0のパケットをキャプチャ -t 時刻はいらないよ -n わざわざサーバーの名前は調べなくてもいいよ。 -s 200 200Byteまでパケットを拾ってね。(デフォでは、64Byteだったかな) 普通は -X して、bin/asciiもdumpさせる。少しはwiresharkに近づくぞ。 port 53 DNSとのやり取りだけキャプチャしてね
DNSサーバーは下記の有名なものを使ってみた。
ob6$ ./a.out 8.8.8.8 google-public-dns-a.google.com ob6$ ./a.out 1.1.1.1 one.one.one.one
ワン ワン ワン ワンとふざけた名前が返ってきたけど、ちゃんとしたDNSサーバーだ。
ob6$ dig -x 8.8.8.8 @1.1.1.1 : ;; ANSWER SECTION: 8.8.8.8.in-addr.arpa. 1038 IN PTR google-public-dns-a.google.com. ;; Query time: 17 msec ;; SERVER: 1.1.1.1#53(1.1.1.1)
パケットの出入り確認
UDPってんで、前回の大河ドラマでも取り上げられてて好都合。例示されてたコードを盗み見した所、サーバー側では、sendto/recvfromを使ってて、クライアント側は、send/recv関数を使ってた。今回は、クライアントだな。
Breakpoint 1, _libc_send (s=3, msg=0xdfa3b75b600, len=44, flags=0) at /usr/src/lib/libc/net/send.c:39 39 return (sendto(s, msg, len, flags, NULL, 0)); (gdb) bt #0 _libc_send (s=3, msg=0xdfa3b75b600, len=44, flags=0) at /usr/src/lib/libc/net/send.c:39 #1 0x00000df9fda183a6 in udp_send (as=0xdfa2a9a6400) at /usr/src/lib/libc/asr/res_send_async.c:434 #2 res_send_async_run (as=0xdfa2a9a6400, ar=0x7f7ffffd1818) at /usr/src/lib/libc/asr/res_send_async.c:180 #3 0x00000df9fd9f67f2 in _libc_asr_run (as=0xdfa2a9a6400, ar=0xdfa3b75b600) at /usr/src/lib/libc/asr/asr.c:148 #4 0x00000df9fda0b708 in gethostnamadr_async_run (as=0xdfa2a9a5200, ar=0x7f7ffffd1818) at /usr/src/lib/libc/asr/gethostnamadr_async.c:278 #5 0x00000df9fd9f685d in _libc_asr_run (as=0xdfa2a9a5200, ar=0x7f7ffffd1818) at /usr/src/lib/libc/asr/asr.c:148 #6 _libc_asr_run_sync (as=0xdfa2a9a5200, ar=0x7f7ffffd1818) at /usr/src/lib/libc/asr/asr.c:195 #7 0x00000df9fda0559e in gethostbyaddr (addr=<optimized out>, len=<optimized out>, af=<optimized out>) at /usr/src/lib/libc/asr/gethostnamadr.c:183 #8 0x00000df7e5e005c8 in main (argc=2, argv=0x7f7ffffd1a28) at rev.c:18 (gdb) c Continuing. Breakpoint 2, _libc_recv (s=3, buf=0xdfa3b75e000, len=4096, flags=0) at /usr/src/lib/libc/net/recv.c:39 39 return (recvfrom(s, buf, len, flags, NULL, 0)); (gdb) bt #0 _libc_recv (s=3, buf=0xdfa3b75e000, len=4096, flags=0) at /usr/src/lib/libc/net/recv.c:39 #1 0x00000df9fda1801a in udp_recv (as=<optimized out>) at /usr/src/lib/libc/asr/res_send_async.c:465 #2 res_send_async_run (as=0xdfa2a9a6400, ar=0x7f7ffffd1818) at /usr/src/lib/libc/asr/res_send_async.c:193 #3 0x00000df9fd9f67f2 in _libc_asr_run (as=0xdfa2a9a6400, ar=0xdfa3b75e000) at /usr/src/lib/libc/asr/asr.c:148 #4 0x00000df9fda0b708 in gethostnamadr_async_run (as=0xdfa2a9a5200, ar=0x7f7ffffd1818) at /usr/src/lib/libc/asr/gethostnamadr_async.c:278 #5 0x00000df9fd9f6971 in _libc_asr_run (as=<optimized out>, ar=0x7f7ffffd1818) at /usr/src/lib/libc/asr/asr.c:148 #6 _libc_asr_run_sync (as=0xdfa2a9a5200, ar=0x7f7ffffd1818) at /usr/src/lib/libc/asr/asr.c:195 #7 0x00000df9fda0559e in gethostbyaddr (addr=<optimized out>, len=<optimized out>, af=<optimized out>) at /usr/src/lib/libc/asr/gethostnamadr.c:183 #8 0x00000df7e5e005c8 in main (argc=2, argv=0x7f7ffffd1a28) at rev.c:18
man gethostbyaddr on Ubuntu
先輩の説明では、この関数で逆引き出来るのは、IPv4までだよってなってた。 遠い未来に使えるようになるであろうIPv6は、どうすんねん?
先輩が無料で公開されてるIPv6本をくまなく耕せば、その答えが出てきて、オイラーもIPv6対応になるのかな。ちょっと手抜きで、ウブに答えがないか探してみたよ。
The gethostbyaddr() function returns a structure of type hostent for the given host address addr of length len and address type type. Valid address types are AF_INET and AF_INET6. The host address argument is a pointer to a struct of a type depending on the address type, for exam‐ ple a struct in_addr * (probably obtained via a call to inet_addr(3)) for address type AF_INET.
ああ、GNU extensions で、 gethostbyaddr_r を使えばってのを見落としていた。便利はいいんだけど、何となくロックインが気になる。_r は、再入可能って印か。
これを頭に置いて、BSD系のmanを見直してみるか。見直すような発見が待っててくれるかな?