我跟踪了一个curl命令:
strace -s 2000 -f curl google.com
并看到 2 个 DNS 查询
recvfrom(3, "\302\325\201\200\0\1\0\1\0\0\0\0\6google\3com\0\0\34\0\1\6google\3com\0\0\34\0\1\0\0\0\362\0\20*\0\24P@\t\10\v\0\0\0\0\0\0 \16", 2048, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.65.7")}, [28->16]) = 66
recvfrom(3, "X\320\201\200\0\1\0\1\0\0\0\0\6google\3com\0\0\1\0\1\6google\3com\0\0\1\0\1\0\0\1)\0\4\216\372\263\356", 65536, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("192.168.65.7")}, [28->16]) = 54
然后,我看到对 142.250.179.238 的 connect() 系统调用,这是 Google 的 IP。
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("142.250.179.238")}, 16) = 0
我相信2个recvfrom调用之一包含IP“142.250.179.238”,否则curl无法知道要连接的IP。
我的问题:2个recvfrom字符串的格式是什么?以及如何解析它来获取IP地址?
"\302\325\201\200\0\1\0\1\0\0\0\0\6google\3com\0\0\34\0\1\6google\3com\0\0\34\0\1\0\0\0\362\0\20*\0\24P@\t\10\v\0\0\0\0\0\0 \16"
"X\320\201\200\0\1\0\1\0\0\0\0\6google\3com\0\0\1\0\1\6google\3com\0\0\1\0\1\0\0\1)\0\4\216\372\263\356"
答案1
recvfrom(2)
记录如下(使用下面的 Linux 手册页):
ssize_t recvfrom(int sockfd, void buf[restrict .len], size_t len, int flags, struct sockaddr *_Nullable restrict src_addr, socklen_t *_Nullable restrict addrlen);
或者更简单地POSIX定义:
ssize_t recvfrom(int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len);
要了解 的输出strace
,应该阅读每个系统调用的手册页。
Buf/buffer 是系统调用成功后将提供接收到的数据的空间,并且其内容以strace
类似于 C 的方式显示回来。
缓冲区空间显示为一系列字节编码,就像 C 语言中一样,与 . 显示的任何缓冲区或字符串一样strace
。后面\
跟3位数字表示是八进制number 表示不能显示为简单 ASCII 字符或C 语言定义的任何预定义特殊首选显示形式(对于某些特殊字符,例如\n
下面第二个命令中使用的字符)的字节值。printf
参见示例维基百科的参考对这个:
\nnn
其数值由nnn
解释为给出的字节八进制数
因为我们知道我们要寻找什么(八进制):
$ printf '\\%o' 142 250 179 238; printf '\n'
\216\372\263\356
这在第二个数据的末尾可见recvfrom()
。
当然,这不再是关于系统调用,而是关于 DNS 协议。要了解有关 DNS 回复的更多信息,请阅读记录此内容的 RFC,尤其是RFC 1035:域名 - 实施和规范第 4.1.3 节。资源记录格式:
Each resource record has the following format: 1 1 1 1 1 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | / / / NAME / | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | CLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TTL | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | RDLENGTH | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| / RDATA / / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
该 RFC 中描述了每个部分(此处 RDLENGTH = 4 并且 RDATA 保存 4 字节 IPv4 地址)。
FWIW,第一个接收的是 IPv6 的回复。其中\034
28 代表AAAA
IPv6 记录(RFC 3596 - 2.1 AAAA 记录类型) 而不是A
IPv4 记录的 1。