xinetd + netcat → pitfalls
If you need to do something on the remote server, but are too lazy to mess with writing a network service, xinetd comes to the rescue.
The ease of writing servers for xinetd is attractive, it is really simple: we can write a simple script in any language that works with stdin and stdout (in the simplest case it is a regular REPL ) and we get both a console utility and a network server in one bottle.
After one minute to edit the xinetd config, we get a working server, to which you can connect with telnet or netcat and see the result on the console.
Everything is easy and beautiful. But since there is a (remote) console utility, I want to go further - to automate the work with it. Do not drive hundreds of queries with your hands and do not look in the display on the screen how it worked. That is, speaking the network language, the client part is needed.
Here, naturally, I want to use netcat (nc): we write a simple script that issues commands to stdout and interprets the responses it receives from stdin and connect it to netcat, thus turning it into a network client (although the script itself does not recognize this).
Having such “server” and “client” scripts, we get the opportunity to run them locally as a bonus. Not to mention the fact that during coding we did not write a single line regarding working with the network.
In most cases, this is enough to work happily ever after.
But there are nuances .
Having written some such remote utilities to celebrate, I found that sometimes the process of communication between the client and server written in this way hangs.
I couldn’t google anything on this topic, I had to figure it out myself, I present you the results.
How xinetd works: opens a socket, hooks it to standard input / output streams and launches an external program link.
How netcat (-e, for example) works: opens a socket, hooks it to standard input / output streams and launches an external program link.
That is, the same.
Accordingly, if both programs are geared towards working with the terminal, no one checks the ability to write or read. If you need to read, just read; need to write - just write.
Only instead of the terminal there is already a network socket, and no one works with the network like that.
With sufficient traffic intensity, a situation of dragons can devour each other from the tail: the read buffers on both sides are full, but according to the logic of the work cycles, both sides just want to say something to each other.
As a result, both processes hang in write () forever.
Attempts to solve the problem cheaply by subtracting the maximum amount of data do not cure the problem, but only postpone it.
But most of all in this story netcat upset me.
Having found no calls in strace nc for setting non-blocking mode of sockets, I looked in apt-get source and ... really didn’t find anything like it.
Netcat works with sockets in synchronous mode, causing it to hang, even without any “external program written for the console”.
Sadness is added by the fact that netcat is in fact a standard, although, in fact, unsuitable for use in the general case.
There are two
ways out : 1. Write a server for xinetd taking into account that it is a network (fcntl O_NONBLOCK, select on stdin and stdout and all that), but then xinetd is lost as a simple wrapper for simple things. You can already write a full server.
2. Write a synchronous client that will wait for the server to respond and prevent buffer overflows for reading and writing - here the simplicity of netcat is lost and you need to do something with performance so that you can send several requests at the same time without overflowing the buffer and be able to process several answers.
In my case, I chose the semi-second synchronous option:
• send 10 requests
• while there is something to send {send one request; read one answer}
• read 10 answers
... which in my particular case worked, but as a general solution, of course, is not good.
The ease of writing servers for xinetd is attractive, it is really simple: we can write a simple script in any language that works with stdin and stdout (in the simplest case it is a regular REPL ) and we get both a console utility and a network server in one bottle.
After one minute to edit the xinetd config, we get a working server, to which you can connect with telnet or netcat and see the result on the console.
Everything is easy and beautiful. But since there is a (remote) console utility, I want to go further - to automate the work with it. Do not drive hundreds of queries with your hands and do not look in the display on the screen how it worked. That is, speaking the network language, the client part is needed.
Here, naturally, I want to use netcat (nc): we write a simple script that issues commands to stdout and interprets the responses it receives from stdin and connect it to netcat, thus turning it into a network client (although the script itself does not recognize this).
Having such “server” and “client” scripts, we get the opportunity to run them locally as a bonus. Not to mention the fact that during coding we did not write a single line regarding working with the network.
In most cases, this is enough to work happily ever after.
But there are nuances .
Having written some such remote utilities to celebrate, I found that sometimes the process of communication between the client and server written in this way hangs.
I couldn’t google anything on this topic, I had to figure it out myself, I present you the results.
Who's guilty?
How xinetd works: opens a socket, hooks it to standard input / output streams and launches an external program link.
How netcat (-e, for example) works: opens a socket, hooks it to standard input / output streams and launches an external program link.
That is, the same.
Accordingly, if both programs are geared towards working with the terminal, no one checks the ability to write or read. If you need to read, just read; need to write - just write.
Only instead of the terminal there is already a network socket, and no one works with the network like that.
With sufficient traffic intensity, a situation of dragons can devour each other from the tail: the read buffers on both sides are full, but according to the logic of the work cycles, both sides just want to say something to each other.
As a result, both processes hang in write () forever.
Attempts to solve the problem cheaply by subtracting the maximum amount of data do not cure the problem, but only postpone it.
But most of all in this story netcat upset me.
Having found no calls in strace nc for setting non-blocking mode of sockets, I looked in apt-get source and ... really didn’t find anything like it.
Netcat works with sockets in synchronous mode, causing it to hang, even without any “external program written for the console”.
Sadness is added by the fact that netcat is in fact a standard, although, in fact, unsuitable for use in the general case.
What to do?
There are two
ways out : 1. Write a server for xinetd taking into account that it is a network (fcntl O_NONBLOCK, select on stdin and stdout and all that), but then xinetd is lost as a simple wrapper for simple things. You can already write a full server.
2. Write a synchronous client that will wait for the server to respond and prevent buffer overflows for reading and writing - here the simplicity of netcat is lost and you need to do something with performance so that you can send several requests at the same time without overflowing the buffer and be able to process several answers.
In my case, I chose the semi-second synchronous option:
• send 10 requests
• while there is something to send {send one request; read one answer}
• read 10 answers
... which in my particular case worked, but as a general solution, of course, is not good.