netcat: datagram client blocks indefinitely after EOF

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

netcat: datagram client blocks indefinitely after EOF

astian
Consider:

  $ nc -Nul 127.0.0.1 9911 &
  $ echo blah | nc -Nu 127.0.0.1 9911

That client blocks "forever".  This is because after EOF in stdin,
netcat still waits for a HUP from the remote end of the socket, but this
will only work for streams not for datagrams.  It would be nice if this
didn't block so that one could easily fire one-off datagram messages
from scripts, for example.

The tentative patch below adds an "A" flag to avoid reading from the
socket.  It should apply to "current".

Using it prevents the program from blocking indefinitely when polling
for a datagram socket for which the remote end does not reply and/or
simply closes.

TODO: Should still bind a client UNIX datagram socket when this option
is used?  (If no, this flag should conflict with "s".)

diff --git nc.1 nc.1
index 6bdfe4f..5eedccc 100644
--- nc.1
+++ nc.1
@@ -33,7 +33,7 @@
 .Nd arbitrary TCP and UDP connections and listens
 .Sh SYNOPSIS
 .Nm nc
-.Op Fl 46cDdFhklNnrStUuvz
+.Op Fl 46AcDdFhklNnrStUuvz
 .Op Fl C Ar certfile
 .Op Fl e Ar name
 .Op Fl H Ar hash
@@ -99,6 +99,10 @@ The options are as follows:
 Use IPv4 addresses only.
 .It Fl 6
 Use IPv6 addresses only.
+.It Fl A
+Do not attempt to read from the socket.  Useful in combination with
+.Fl u
+to send one-off datagram messages without waiting for a answer.
 .It Fl C Ar certfile
 Load the public key part of the TLS peer certificate from
 .Ar certfile ,
diff --git netcat.c netcat.c
index c04298f..0a0b671 100644
--- netcat.c
+++ netcat.c
@@ -74,6 +74,7 @@
 #define TLS_MUSTSTAPLE (1 << 4)
 
 /* Command Line Options */
+int Aflag; /* don't read from socket */
 int dflag; /* detached, no stdin */
 int Fflag; /* fdpass sock to stdout */
 unsigned int iflag; /* Interval Flag */
@@ -173,7 +174,7 @@ main(int argc, char *argv[])
  signal(SIGPIPE, SIG_IGN);
 
  while ((ch = getopt(argc, argv,
-    "46C:cDde:FH:hI:i:K:klM:m:NnO:o:P:p:R:rSs:T:tUuV:vW:w:X:x:Z:z"))
+    "46AC:cDde:FH:hI:i:K:klM:m:NnO:o:P:p:R:rSs:T:tUuV:vW:w:X:x:Z:z"))
     != -1) {
  switch (ch) {
  case '4':
@@ -195,6 +196,9 @@ main(int argc, char *argv[])
  else
  errx(1, "unsupported proxy protocol");
  break;
+ case 'A':
+ Aflag = 1;
+ break;
  case 'C':
  Cflag = optarg;
  break;
@@ -1093,7 +1097,8 @@ readwrite(int net_fd, struct tls *tls_ctx)
  pfd[POLL_NETOUT].events = 0;
 
  /* network in */
- pfd[POLL_NETIN].fd = net_fd;
+ /* don't read from socket if requested */
+ pfd[POLL_NETIN].fd = Aflag ? -1 : net_fd;
  pfd[POLL_NETIN].events = POLLIN;
 
  /* stdout */

Reply | Threaded
Open this post in threaded view
|

Re: netcat: datagram client blocks indefinitely after EOF

astian
I notice now that I forgot to include help/usage changes.

But more importantly, after playing with this patch a bit more I find
that while it "seems" to work in a single-core VM, in other situations
this can lead to a race between client and server.  What happens is that
the client fires off its message and then closes its socket before the
listener can call connect():

        } else if (uflag && !kflag) {
                /*
                 * For UDP and not -k, we will use recvfrom()
                 * initially to wait for a caller, then use
                 * the regular functions to talk to the caller.
                 */
                int rv;
                char buf[2048];
                struct sockaddr_storage z;

                len = sizeof(z);
                rv = recvfrom(s, buf, sizeof(buf), MSG_PEEK,
                    (struct sockaddr *)&z, &len);
                if (rv == -1)
                        err(1, "recvfrom");

Here the message from the client was received and we have the remote
address in "z".  But now if the client terminates and its socket is
shutdown the following connect fails:

                rv = connect(s, (struct sockaddr *)&z, len);
                if (rv == -1)
                        err(1, "connect");

Using "-k" in the listener avoids this, of course, since there no
connect() in that case. Hmm.