#747646 netcat-openbsd: UDP connections start with "XXXXX" junk

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
#747646#5
Date:
2014-05-10 17:08:20 UTC
From:
To:
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.

#747646#10
Date:
2015-04-21 15:16:34 UTC
From:
To:
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] :-)

#747646#15
Date:
2018-11-09 18:17:11 UTC
From:
To:
Performing this test with blank packets still works without the server seeing anything.
#747646#20
Date:
2018-11-09 19:40:47 UTC
From:
To:
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.

#747646#25
Date:
2019-11-05 02:47:11 UTC
From:
To:

#747646#30
Date:
2022-06-21 06:50:50 UTC
From:
To:
This makes nc totally unusable for UDP.