Docker – Sending a file from Docker container to host using nc: Why does host nc close the connection too early

dockernetcat

The following BASH script downloads and builds mpv (https://github.com/mpv-player/mpv-build) in a Docker container, and sends the file to the host using netcat (the host is listening using netcat):

HOSTIP="192.168.1.100"
PORT="62514"

nc -v -l $PORT > mpv &

sleep 1

sudo docker run ubuntu:14.04 /bin/bash -c "\
sed -i -e 's/# deb/deb/g' /etc/apt/sources.list; \
cat /etc/apt/sources.list; \
apt-get update; \
apt-get install --yes --force-yes git python-minimal; \
git clone https://github.com/mpv-player/mpv-build.git; \
cd mpv-build/; \
./update; \
apt-get install --yes --force-yes devscripts equivs; \
rm -f mpv-build-deps_*_*.deb; \
mk-build-deps -i -t \"apt-get --yes --force-yes --no-install-recommends\"; \
./build -j\$(nproc); \
cat mpv/build/mpv | nc $HOSTIP $PORT; \
echo Done"

#close any nc process that might be left running
nc $HOSTIP $PORT

So the script:

  1. Starts netcat (nc) on the host, listening for connections in the background
  2. Starts a Docker container, in which we clone the mpv repo, build mpv, use nc to connect to the listening socket on the host, and send the resulting binary back to the host

For some reason the resulting file is reduced in size, when received on the host side. Either it's 0 bytes or some multiple of 1024 bytes. The beginning seems intact, an x86_64 ELF executable.

Here's one packet capture where the docker container sends some of the mpv binary to the host, but where the listening nc (on the host) closes the connection prematurely (it sends a packet with the FIN flag set, a few milliseconds afters the connection is established):

docker-nc-packetdump

49152 bytes were transmitted here (it's always a multiple of 1024).

There's not always TCP retransmission errors. I made another capture with no errors, but still only a little was sent (24576) of the total 21818582 bytes the resulting mpv binary is.

Not sure what is happening here, why does nc on the listening/host side send a FIN TCP packet shortly after the connection is opened?

Best Answer

Using the docker cp command, the script can be rewritten like so:

HOSTIP="192.168.1.100"
PORT="62514"

set -e

CONTAINERID=$(sudo docker run -d ubuntu:14.04 /bin/bash -c "\
sed -i -e 's/# deb/deb/g' /etc/apt/sources.list; \
cat /etc/apt/sources.list; \
apt-get update; \
apt-get install --yes --force-yes git python-minimal; \
git clone https://github.com/mpv-player/mpv-build.git; \
cd mpv-build/; \
./update; \
apt-get install --yes --force-yes devscripts equivs; \
rm -f mpv-build-deps_*_*.deb; \
mk-build-deps -i -t \"apt-get --yes --force-yes --no-install-recommends\"; \
./build -j\$(nproc); \
echo \"From container: done building!\" | nc $HOSTIP $PORT; \
nc -v -l $PORT")

CONTAINERIP=$(sudo docker inspect $CONTAINERID|grep IPAddress|sed 's/.*IPAddress": "//'|sed 's/",$//')

echo "Started mpv-build container with Docker container ID: $CONTAINERID and IP: $CONTAINERIP" 
echo "Waiting to hear from container that the build has finished..."

#listen on host. wait for container to connect and disconnect
nc -l $PORT

#copy file from running container, while it's listening for a connection with nc
sudo docker cp $CONTAINERID:/mpv-build/mpv/build/mpv ./

#connect to the listening socket in the container to make the container finish running
echo "bye" | nc  $CONTAINERIP $PORT

echo "Done"

netcat is now only used as a sort of messaging mechanism, for the container to signal to the host when it's done building (so the host can initiate the copy), and for the host to signal to the container that it's done copying the file (so the container can exit).