Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make netcat discard all bytes after disconnect

I use netcat to run a tiny development webserver through the bash, which is able to handle one connection at once.

Netcat is started as follows (-k to make it survive multiple connections):

nc -kl 3000

A bash script processes the browser's request which is received by netcat, and builds the response, which is sent back to the browser through the same netcat instance.

Everything works as expected, but sometimes, the browser does not get the file it requested. My suspicion: If a connection is closed before the response is sent completely, the response's remainder is sent as response to the following request (it, of course, not belongs to).

Proof

  1. Terminal 1 (Server): nc -kl 3000
  2. Terminal 2 (simulates Browser): nc localhost 3000
  3. Type hello\n in terminal 1.
  4. Terminal 2 prints hello\n.
  5. Do Ctrl+C in terminal 2 to end the connection.
  6. Type world\n in terminal 1.
  7. Run nc localhost 3000 in terminal 2 again (new connection).
  8. Terminal 2 immediately shows world\n even though world\n was actually sent when no connection existed, meant as second response line in the first connection.

Required behavior: Ignore all bytes that are passed to netcat if no connection exists.

Is it possible using netcat? (I prefer a tool like netcat as it comes pre-installed on all machines.)

like image 340
ideaboxer Avatar asked Apr 22 '17 12:04

ideaboxer


2 Answers

well, as a workaround and if you can do that, instead of running ncat using the keep-open option, just respawn it between each connection in a while loop:

while true; do
    #what you want to do with: ncat -l 3000 
done

Then each time the process will respawn, it will discard all stdin, and then you start over with the next i/o for the following process.

Of course, if respawning your bash script isn't convenient, then it might mean you're not using the right tool for the job. (you might be able to workaround that playing with fifos or using temporary fds, but that'd be overengineering to avoid writing a script in a language that'd be a better fit for the job).


If you're not already doing it, did you try running your script from netcat and not the other way around?

ncat -kl 3000 -c ./script.sh

it will spawn the script for each connection, on which it will redirect stdin/stdout. Then when the client will disconnect, the script will get killed and should release your input fd.

Actually, if you're serving files as an http server, you might to look up:

ncat -lk 3000 --lua-exec httpd.lua

with the httpd.lua offered with the ncat distribution


If you want a simple run anywhere script to do something on your system, you can write a small python script.

It is installed per default on ubuntu (and many other systems), and you have a minimalist web server implementation provided, for example to serve files you just do:

python -m SimpleHTTPServer <port>
  • cf SimpleHTTPServer for py2
  • or http.server in py3

it is rather easy to customize, to fit your unique needs, and will offer you a full control over your sockets and file descriptors.

like image 86
zmo Avatar answered Oct 21 '22 20:10

zmo


Sticking with the need for "run everywhere" script, you could try processing the input line by line and detecting if an ESTABLISHED connection exists with netstat:

while read line; do 
  netstat -an | grep 3000 | grep -q ESTABLISHED; 
  ret=$?; 
  if [ "$ret" == "0" ]; then 
    echo $line; 
  else 
    # this output is just for fun, and can be removed
    echo "not sending $line" >&2; 
  fi; 
done | nc -kl 3000

This will have the limitation of being line-based and may still send data to netcat wrongly if the connection is closed just after the netstat call. The two grep calls should make sure that you dont wrongly detect your own grep.

Ive tried looking for signals returned by netcat when the connection is closed but cant see any exposed to the shell.

The use of a fifo can be used to demonstrate that netcat keeps its stdin open, even when no connection exists:

mkfifo test

nc -kl < test

# then in a different shell
for a in {0..5}; do echo hi > test; done

The for does not block, so netcat is actively reading the stdin but buffering it (and I cant see an option to turn off buffering). This means you cant really tell the difference between netcat with or without an active connection, except by looking at the network state. netstat is one of many ways of getting this state.

like image 33
spacepickle Avatar answered Oct 21 '22 22:10

spacepickle