Closing connections via s_client in a shell script
Asked Answered
G

1

4

I am trying to make a connection to an IMAP server within a shell script. While I can connect and issue the commands, closing the connection suitably seems impossible.

This is the test command I am using:

openssl s_client -crlf -connect server:993 <<EOF
01 login USERNAME PASSWORD
02 LIST "" "*"
03 logout
EOF

Because the connection is closed as soon as the input runs out, this occurs before any output is received so I never receive the required data.

If I add the option -ign_eof option so that it will ignore the input ending to keep the connection open the output is returned as desired. But instead after the connection has been closed...

* BYE Logging out
03 OK Logout completed.

...s_client remains alive so execution never returns to the script.

Is there a solution which will cause s_client to terminate when the server closes the connection?

Or is there an alternative method using standard tools? The script would be run on Mac OS X, Debian, and a Redhat derivate, and possibly an Android terminal emulator so I would like to use fairly standard tools for portability rather than specialist packages.

Update: I have come up with an answer that I am not entirely happy with, but which does work. It uses a script to pipe commands to the openssl but which then enters an infinite loop to keep stdin open until it receives a signal to tell it to quit. Here is my test script:

#!/bin/sh

if [ "$1" = "doit" ]; then
   trap "echo \"04 logout\"; exit" SIGUSR1
   echo "00 login USERNAME PASSWORD"
   echo "01 SELECT PID-$$"
   echo "02 SELECT FOLDER"
   echo "03 FETCH 1:* (BODY[HEADER.FIELDS (Subject)])"
   while :; do :; done
fi

MYFLAG=
$0 doit | openssl s_client -crlf -connect server:993 2>/dev/null | while read LINE; do
   LINE=`echo "$LINE" | tr -d '\r'`
   [ "${LINE:0:4}" = "* OK" ] && MYFLAG=Y 
   [ "${LINE:0:33}" = "01 NO Mailbox doesn't exist: PID-" ] && PID="${LINE##*PID-}"
   [ "$MYFLAG" ] && echo "$LINE"
   [ "$PID" -a "$LINE" = "03 OK Fetch completed." ] && kill -USR1 $PID
done
echo "Finished."

The script calls itself with a parameter to output the IMAP commands which is piped to openssl, whose output is piped into a read loop so it can be processed. The MYFLAG variable is just used to hide the information output by openssl and just echo the output from the server connection.

I select a dummy folder name which includes the PID of the second instance of the script as a way to pass this back, obviously a temporary file would have been better but for the testing I wanted to keep everything self contained and be able to watch what was happening.

Once the fetch information has been displayed and the server returns the OK response, it sends a SIGUSR1 signal to the second instance of the script, that then sends the logout message and quits, closing stdin which causes s_client to disconnect.

Originally I included the 04 logout command in the initial set of echos, but when I did this the read loop only displayed as far as the fetch output then stalled, it did not even show the OK status for the operation, though everything was received.

Also I need to use tr to strip trailing carriage returns, but if I pipe the output from openssl through then nothing is received by the read loop.

Glamorous answered 13/4, 2013 at 8:12 Comment(0)
T
6

you could fix that by timing your input to openssl. the simplest way i can come up with, is by using a function that echos the input to the stdout:

#!/bin/sh

imapscript () {
echo '01 login USER PASSWD'
echo '02 LIST "" "*"'
echo '03 logout'
while sleep 1; do
  echo "04 logout"
done
}

imapscript | openssl s_client -crlf -connect server:993
Triune answered 24/4, 2013 at 16:0 Comment(3)
I had a similar idea, using a script to echo the commands, but then I had an infinite loop to keep the connection alive until the input was received and then I could kill that script. I have updated the question with my test script.Glamorous
not sure what is wrong with my solution though - i tested on debian and OSX and it get's the folder-listing fine and then quits; no need to send signals and kill, as once the server closes the connection and you try to send something (that's the echo in the loop - put there whatever you like, i only used 04 logout as it makes it clearer what we really want to do), s_client will quit due to the closed socket.Briquet
Having been able to fully test your idea, that is actually much better. I am not sure what I did differently, but when I tried something like that before it sometimes closed the connection halfway through the results of the FETCH 1:* (BODY[HEADER.FIELDS (Subject)]). Your solution seems to work every time and is exactly what I was looking for. Thanks.Glamorous

© 2022 - 2024 — McMap. All rights reserved.