- Package:
- netcat-openbsd
- Source:
- netcat-openbsd
- Description:
- TCP/IP swiss army knife
- Submitter:
- Chris Davies
- Date:
- 2022-06-21 08:03:03 UTC
- Severity:
- normal
Hi, Trying to diagnose an OpenVPN issue I started throwing UDP packets around between to distinct hosts. I have discovered that the UDP traffic provided by netcat-openbsd is mauled by the insertion of (at least) "X" characters at the beginning of the "connection" before any other data is passed. Here is the example scenario: 1. Host "client". Two terminals, each running one command: tshark -i eth0 -nlp port 50493 nc -vvv -u server 50493 2. Host "server". Two terminals, each running one command: tshark -i eth0 -nlp port 50493 nc -vvv -ul 50493 Nothing is typed in to the nc running either on "client" or "server". Data is seen on the wire: 5 packets sent; 5 packets received. Retrying with the tshark -V flag shows that all packets are (apparently) normal UDP packets each containing a single one-byte character "X" (0x58). The net result, however, is that the data stream output by the listening nc on host "server" starts with "XXXXX", which corrupts the expected message. To illustrate further, if you run this on host "client": echo "hello" | nc -vvv -u server 50493 This is the received (but not expected) output: XXXXXhello The unexpected behaviour does not happen with netcat-traditional.
Hello,
I ran into this as well, and after some investigation I found out this
only happens when using the verbose flag "-v":
netcat.c, lines 609-616:
----------------------------%<-------------------------------------------------
if (vflag) {
/* For UDP, make sure we are connected. */
if (uflag) {
if (udptest(s) == -1) {
ret = 1;
continue;
}
}
----------------------------%<-------------------------------------------------
netcat.c, lines 1207-1229:
----------------------------%<-------------------------------------------------
/*
* udptest()
* Do a few writes to see if the UDP port is there.
* Fails once PF state table is full.
*/
int
udptest(int s)
{
int i, t;
if ((write(s, "X", 1) != 1) ||
((write(s, "X", 1) != 1) && (errno == ECONNREFUSED)))
return -1;
/* Give the remote host some time to reply. */
for (i = 0, t = (timeout == -1) ? UDP_SCAN_TIMEOUT : (timeout / 1000);
i < t; i++) {
sleep(1);
if ((write(s, "X", 1) != 1) && (errno == ECONNREFUSED))
return -1;
}
return 1;
}
----------------------------%<-------------------------------------------------
So I believe it can be cured with a simple "don't do it!"[1] :-)
Performing this test with blank packets still works without the server seeing anything.
That makes the server see a bunch of ‘\0’s instead of ‘X’s; I'd argue that writing control characters is more confusing than writing something visible. OTOH, ‘write(fd, buf, 0)’ has an undefined behavior; in practice an empty message is sent (as for ‘send(s, NULL, 0, 0)’), but receiving an empty buffer is what terminates the poll loop, so if the listening side is `nc -u -l` (without ‘-k’), it closes the socket and exit() before the client has a chance to perform further write()s.
This makes nc totally unusable for UDP.