9

I'm wondering how to get a shell script to listen in on a certain port (maybe using netcat?). Hopefully so that when a message is sent to that port, the script records the message and then runs a function.

Example:

  1. Computer 1 has the script running in the background, the script opened port 1234 to incoming traffic

  2. Computer 2 sends message "hello world" to port 1234 of computer 1

  3. Script on Computer 1 records the message "hello world" to a variable $MESSAGE

  4. Script runs function now that variable $MESSAGE has been set

How do I go about doning this?

9

Should be possible with socat.

Write such a script "getmsg.sh" to receive one message via stdin:

#!/bin/bash
read MESSAGE
echo "PID: $$"
echo "$MESSAGE"

Then run this socat command to invoke our script for each tcp connection on port 7777:

socat -u tcp-l:7777,fork system:./getmsg.sh

Send a test message from another shell:

echo "message 1" | netcat localhost 7777
  • Have you tested it? – Tomasz Oct 5 '16 at 19:02
  • Rewritten and tested now ;) – rudimeier Oct 5 '16 at 19:14
  • 1
    I was inspired by your solution and found a way that works with netcat: nc -l 7777 | ./getmsg.sh – Daniel Oct 5 '16 at 20:09
  • Glad to hear that. But netcat exists after one connection. socat would do the same if you remove ",fork" from my command line. – rudimeier Oct 5 '16 at 20:17
5

The UCSPI-TCP way

There are toolsets other than netcat. Here are how to use a few of them. They all presume the existence of a service script that runs your func, whatever that may be:

#!/bin/sh
while read -r MESSAGE
do
    echo 1>&2 "${TCPREMOTEIP}" "${TCPREMOTEPORT}" rx "${MESSAGE}"
    func
done

The TCPREMOTEIP and TCPREMOTEPORT environment variables are defined by the UCSPI-TCP protocol.

The script is spawned as an individual process per TCP connection using the various toolsets. In what follows, the tools are shown as used within a short script. Such a script, conventionally named run, is how one would run them under a daemontools-family service manager. They can of course be invoked directly.

Bernstein ucspi-tcp

With Daniel J. Bernstein's ucspi-tcp, tcpserver spawns the service script:

#!/bin/sh -e
exec tcpserver -v -P -R -H -l 0 0.0.0.0 7777 ./service

There are IPv6-capable enhanced versions of Bernstein ucspi-tcp. With Erwin Hoffman's, tcpserver attempts to handle both IPv4 and IPv6 in one (if the operating system supports this, a few do not) and spawns the service script:

#!/bin/sh -e
exec tcpserver -v -P -R -H -l 0 ::0 7777 ./service

Bercot s6-networking, s6, and execline

With Laurent Bercot's s6-networking, s6-tcpserver4 and s6-tcpserver6 handle IPv4 and IPv6 separately, and spawn the service script:

#!/command/execlineb
s6-tcpserver4 -v 0.0.0.0 7777 
./service
#!/command/execlineb
s6-tcpserver6 -v ::0 7777 
./service

One can build up more complex servers by interposing tools such as s6-tcpserver-access and s6-applyuidgid in the chain immediately before ./service.

nosh UCSPI tools

With the nosh toolset, tcp-socket-listen listens on the TCP socket, again handling IPv4 and IPv6 simulataneously if the operating system supports doing so, and chains to tcp-socket-accept which in turn spawns the service script:

#!/bin/nosh
tcp-socket-listen --combine4and6 :: 7777
tcp-socket-accept --verbose --localname 0
./service

Or one runs two separate processes, on operating systems such as OpenBSD:

#!/bin/nosh
tcp-socket-listen 0.0.0.0 7777
tcp-socket-accept --verbose --localname 0
./service
#!/bin/nosh
tcp-socket-listen :: 7777
tcp-socket-accept --verbose --localname ::
./service

One can build up more complex servers by interposing tools such as ucspi-socket-rules-check and setuidgid in the chain.

#!/bin/nosh
tcp-socket-listen --combine4and6 :: 7777
setuidgid unprivileged-user
tcp-socket-accept --verbose --localname 0
ucspi-socket-rules-check --verbose
./service

Pape ipsvd

With Gerrit Pape's ipsvd, tcpsvd spawns the service script:

#!/bin/sh -e
exec tcpsvd -v 0.0.0.0 7777 ./service

UCSPI-UDP

The common service script can handle when standard input is a stream socket. But you didn't specify TCP explicitly.

Although some of the aforementioned toolkits can be used to build UDP servers in similar fashion to how one can use them to build TCP servers (c.f. udp-socket-listen in nosh), it's tricky to build the actual service program with shell script, as the shell's builtins do not necessarily cope well when standard input is a datagram socket.

Further reading

0

This can also be done with udpsvd which is available on Ubuntu/ Debian (see manpage) as well as built-in to busybox. Example:

# simple UDP "echo" on port 9998
udpsvd 0.0.0.0 9998 cat

Replace cat with your shell script to execute, stdin is the packet.

With netcat, you can run in a loop to keep listening, and pass each packet to myscript:

 while true; do nc -ul 9998 | myscript.sh; done

If you wanted to pass all received packets as a stream to a single invocation of your script:

# this will keep listening instead of terminating the process:
nc -kul 9998 |myscript.sh

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.