Netcat 在 UDP 响应时退出
Netcat exits on UDP response
你能解释一下这种“奇怪”的行为吗?
我在 Linux 上 运行ning netcat,作为 UDP 回显服务器:
ncat -4 --exec /bin/cat -u --listen 2000
接下来,运行客户:
$ ncat -s 192.168.1.2 -u 127.0.0.1 2000
机器有一个真实的网络适配器,地址为:192.168.1.2。
在我输入一些东西后,服务器就退出了:
$ ncat -4 --exec /bin/cat -u --listen 2000 ; x=$?; echo $x
0
为什么?
这是一个 ncat
限制。
让我们使用 strace
来了解一下 ncat
的目的。方便的是,strace
有 -e
选项来过滤系统调用,例如-e %net
用于记录 network-related 系统调用。让我们先启动服务器:
$ strace -e %net ncat -4 --exec /bin/cat -u --listen 2000
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(2000), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
注意 bind
调用。套接字绑定到 0.0.0.0,这意味着它将接收发送到 127.0.0.1:2000 和 10.0.2.15:2000(我的 eth0
地址)以及属于该机器的任何其他地址的数据包.
现在让我们启动一个客户端:
$ strace -e %net ncat -s 10.0.2.15 -u 127.0.0.1 2000
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("10.0.2.15")}, 16) = 0
setsockopt(3, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(2000), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
此时客户端正在等待输入
注意 connect
调用。 UDP 是无连接的,因此调用只记住远程地址。这很方便,因为 send
将使用记录的地址。否则,有必要在每个 send
调用中指定远程地址。 我们很快就会看到另一个含义。
现在让我们输入一些内容。客户:
sendto(3, "Hello, world!\n", 14, 0, NULL, 0) = 14
服务器:
recvfrom(3, "Hello, world!\n", 131072, MSG_PEEK, {sa_family=AF_INET, sin_port=htons(35197), sin_addr=inet_addr("10.0.2.15")}, [128->16]) = 14
connect(3, {sa_family=AF_INET, sin_port=htons(35197), sin_addr=inet_addr("10.0.2.15")}, 16) = 0
recvfrom(3, "Hello, world!\n", 8192, 0, NULL, NULL) = 14
sendto(3, "Hello, world!\n", 14, 0, NULL, 0) = 14
recvfrom(3, 0x7fff8fcc9d70, 8192, 0, NULL, NULL) = -1 ECONNREFUSED (Connection refused)
服务器看到来自 10.0.2.15 的消息。第一个 recvfrom
调用是用 MSG_PEEK
标志完成的,它允许在不实际使用消息的情况下查看消息。然后 connect
(为方便起见),另一个 recvfrom
消费消息,sendto
发送回复。
现在记住服务器套接字绑定到 0.0.0.0。客户端向 127.0.0.1:2000 发送了一条消息。回复必须包括服务器套接字的地址作为来源。但是系统不能使用 0.0.0.0,因为它在网络上通常没有意义。 127.0.0.1 和 10.0.2.15 都可以,但是 ncat
没有明确说明使用哪一个。所以系统最终使用 10.0.2.15 作为源。
现在回到客户端。它向 127.0.0.1:2000 发送消息,但响应来自 10.0.2.15:2000。 connect
对 UDP 套接字的另一个影响是只接受来自指定 address:port 的数据报。如果源不匹配,系统将响应 ICMP 端口不可达。这在服务器中被报告为 ECONNREFUSED
错误。
可能的修复
使用 IP_PKTINFO
/IPV6_PKTINFO
辅助消息指定服务器中的预期源地址。
你能解释一下这种“奇怪”的行为吗? 我在 Linux 上 运行ning netcat,作为 UDP 回显服务器:
ncat -4 --exec /bin/cat -u --listen 2000
接下来,运行客户:
$ ncat -s 192.168.1.2 -u 127.0.0.1 2000
机器有一个真实的网络适配器,地址为:192.168.1.2。 在我输入一些东西后,服务器就退出了:
$ ncat -4 --exec /bin/cat -u --listen 2000 ; x=$?; echo $x
0
为什么?
这是一个 ncat
限制。
让我们使用 strace
来了解一下 ncat
的目的。方便的是,strace
有 -e
选项来过滤系统调用,例如-e %net
用于记录 network-related 系统调用。让我们先启动服务器:
$ strace -e %net ncat -4 --exec /bin/cat -u --listen 2000
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(2000), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
注意 bind
调用。套接字绑定到 0.0.0.0,这意味着它将接收发送到 127.0.0.1:2000 和 10.0.2.15:2000(我的 eth0
地址)以及属于该机器的任何其他地址的数据包.
现在让我们启动一个客户端:
$ strace -e %net ncat -s 10.0.2.15 -u 127.0.0.1 2000
socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("10.0.2.15")}, 16) = 0
setsockopt(3, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(2000), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
此时客户端正在等待输入
注意 connect
调用。 UDP 是无连接的,因此调用只记住远程地址。这很方便,因为 send
将使用记录的地址。否则,有必要在每个 send
调用中指定远程地址。 我们很快就会看到另一个含义。
现在让我们输入一些内容。客户:
sendto(3, "Hello, world!\n", 14, 0, NULL, 0) = 14
服务器:
recvfrom(3, "Hello, world!\n", 131072, MSG_PEEK, {sa_family=AF_INET, sin_port=htons(35197), sin_addr=inet_addr("10.0.2.15")}, [128->16]) = 14
connect(3, {sa_family=AF_INET, sin_port=htons(35197), sin_addr=inet_addr("10.0.2.15")}, 16) = 0
recvfrom(3, "Hello, world!\n", 8192, 0, NULL, NULL) = 14
sendto(3, "Hello, world!\n", 14, 0, NULL, 0) = 14
recvfrom(3, 0x7fff8fcc9d70, 8192, 0, NULL, NULL) = -1 ECONNREFUSED (Connection refused)
服务器看到来自 10.0.2.15 的消息。第一个 recvfrom
调用是用 MSG_PEEK
标志完成的,它允许在不实际使用消息的情况下查看消息。然后 connect
(为方便起见),另一个 recvfrom
消费消息,sendto
发送回复。
现在记住服务器套接字绑定到 0.0.0.0。客户端向 127.0.0.1:2000 发送了一条消息。回复必须包括服务器套接字的地址作为来源。但是系统不能使用 0.0.0.0,因为它在网络上通常没有意义。 127.0.0.1 和 10.0.2.15 都可以,但是 ncat
没有明确说明使用哪一个。所以系统最终使用 10.0.2.15 作为源。
现在回到客户端。它向 127.0.0.1:2000 发送消息,但响应来自 10.0.2.15:2000。 connect
对 UDP 套接字的另一个影响是只接受来自指定 address:port 的数据报。如果源不匹配,系统将响应 ICMP 端口不可达。这在服务器中被报告为 ECONNREFUSED
错误。
可能的修复
使用 IP_PKTINFO
/IPV6_PKTINFO
辅助消息指定服务器中的预期源地址。