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を見直してみるか。見直すような発見が待っててくれるかな?