8

Host A:

tar cf -  stuff | dd | nc  -N -l 12987

Host B:

nc a.example.com 12987 | dd | tar tf - 

On host A dd prints its summary after tar completes. Thus it is clear, that tar closes the pipe/file -> EOF.

165040+0 records in 165040+0 records out 84500480 bytes transferred in 25.464802 secs (3318325 bytes/sec)

On both hosts nc happily sits there without exiting. nc(1) :

   -N      shutdown(2) the network socket after EOF on the input.  Some
           servers require this to finish their work.

Thus on host A nc should have seen EOF, closed the damn socket and on host B nc should have seen the TCP connection terminate and should have closed stdout (stdin of dd/tar).

How do I tell nc to close stdout / terminate on host B and terminate on host A.

nc bug?

-D (debug) does nothing. nc can't even tell its version number... sigh

Both hosts are FreeBSD 10.3-RELEASE-p4, IPv4 only.

georg
  • 283
  • 2
  • 7

2 Answers2

13

I, too, was puzzled by netcat's behavior, so I dug into the code. Here's the whole story:

nc servers (nc -l) and clients only exit after the mutual connection was closed. That is, if each of the parties sent a FIN packet to the other party.

A server always sends a FIN packet after receiving a FIN packet from the client. (Unless the server already sent a FIN packet.)

A client sends a FIN packet either:

  • after EOF on stdin, when run with argument -N
  • after EOF on stdin, when the server already sent a FIN packet

With option -d stdin is ignored and nc behaves as if it encountered EOF on stdin.

Option -N always implies sending FIN after encountering EOF on stdin.

Ways to exit the nc processes after exchanging data:

  1. Georg's answer

     server$ echo hello | nc -l -N 2000
     client$ nc -d localhost 2000
    

    After sending hello, the server encounters EOF on stdin an sends FIN because of -N.

    The client receives the message and, due to -d, sees EOF on stdin and sends FIN, because the server already sent FIN.

    The connection is closed, both the client and the server exit.

  2. Client initiates the close

     server$ echo hello | nc -l 2000
     client$ nc -dN localhost 2000
    

    The server keeps the connection open after EOF on stdin.

    The client sees EOF on stdin and sends FIN, because of -N.

    The server sends FIN after receiving the client's FIN.

    The connection is closed, both the client and the server exit.

ens
  • 245
  • 2
  • 5
  • 1
    Thank you. But you answer (2.) problematic. If server run `(echo hello; sleep 5; echo world;) | nc -l 2000`, client will only receive `hello` and close TCP with FIN, `world` will **not** be received. I have tried it on openSUSE 15.1. – Jimm Chen Feb 14 '20 at 13:57
4

nc establishes a bi-directional connection. I.e. it sends stdin from host B to host A as well as the desired one from A to B.

Use -d on host B to ignore stdin. -N on host A is still needed to close the TCP connection on EOF.


In summary

Host A:

tar cf -  stuff | dd | nc  -N -l 12987

Host B:

nc -d a.example.com 12987 | dd | tar tf - 
georg
  • 283
  • 2
  • 7