Telnet smtp command bash 2.05 script problem - bash

Line by line works but in script it does not.
I tried everything i could think of.
This does not work in bash 2.05
{ sleep 1; echo "helo localhost"; sleep 1; echo "mail from: test#maillinux.net"; sleep 1; echo "rcpt to: user#actualdomain.org"; sleep 1; echo "data"; sleep 1; echo "subject: test"; echo; echo "some text" ;echo "."; echo "quit"; } | telnet mailserver.actualdomain.org 25
but if you do line by line
telnet mailserver.actualdomain.org 25
220...
helo localhost
250...ok
mail from: test#maillinux.net
250..ok
rcpt to: user#actualdomain.org
250...ok
data
354 Start mail input; end with <CRLF>.<CRLF>
subject: test
some text
.
quit
then it works and i get mail.
the script works in newer bash 4.4 but i need it to work in 2.05.
seems to me that it does not do echo "." et the end and does not finish mail...and therefore does not send it.
but why???
I expect it to send mail but it does not

This is probably a difference in the version of telnet, not the version of bash.
One common problem when using network tools in a pipeline like this is how they handle EOFs on stdin. What you need is behavior like this:
Your input is written to telnet's stdin
...while this is happening, telnet starts sending that input to the remote server...
Telnet's stdin is given an EOF (end-of-file). Some broken versions of telnet exit immediately at this point.
...telnet keeps sending everything it read from its stdin over to the remote server.
...telnet finishes sending everything it read to the remote server, and then closes the sending side of the socket (via a FIN packet)
...the server keeps sending content to the client until it's sent any responses that need to be done, and then the server closes its own side by sending another FIN
telnet prints everything it got prior to getting a FIN from the server. Completion of this step is when a correctly-written telnet actually exits.
As noted above, many versions of telnet that actually exist were written assuming they would be used by humans, not scripts. Instead of sending buffered content over the wire on receiving an EOF, and waiting to receive any responses from the server before shutting down the receive side, they just quit immediately the moment an EOF is seen.
You can work around it by adding a delay at the end.
It's an ugly hack, and better replaced with using tools that know how to shut down a socket correctly, but the cheap-and-dirty approach looks something like this:
{ ...; echo "."; echo "quit"; sleep 2; } | telnet mailserver.actualdomain.org 25
For new development, consider using a built-to-purpose tool like socat, or one of the many implementations of netcat.
socat's documented behavior explicitly makes it wait to finish sending content it received on its stdin, and printing responses it received over the socket, before exiting; insofar as it has a timeout on how long it waits for responses from the server, that timeout can be explicitly configured.

Agree with Charles, this must be a different version of telnet. And a few thoughts,
You can simply use expect and send commands to automate the telnet command,
#!/user/bin/expect
spawn telnet localhost 25 <<EOF
hello localhost
mail from: local#localhost
rcpet to: user#localhost
data
Some data
.
EOF
Or,
create a text file with the mail commands and use netcat (if the telnet doesn't support for stream input)
cat mailCmd.txt | nc localhost 25
The best and reliable way is to use a utility like socat or sendmail
sendmail user#localhost < "Emailcontent.txt"

Related

Client doesn't close connection to server after receiving all responses

For a school project, I have to create a bash script which communicates with a distant netcat server and sends commands to this server, and gets back responses.
In my client script, I have a sendMessage function that sends messages to the server, and then displays the response from the server. My issue is that after receiving a response, netcat is still waiting for data from server and won't close the connection.
I already tried the -q argument, and I don't get my responses with it, the -w argument is almost what I want, but the minimum value is 1 second, which is not possible to use in my case.
Here is my function:
function sendMessage {
netcat -w 1 "$ipAddress" "$port" <<< "$*"
}
I'm still searching on my side but I have to admit a little help would be very welcome.
Finally found a working way (maybe not the best but at least it's perfectly doing what I want :D)
after all functions I send a "ENDRESPONSE" message, and on my client, I test if I have this message or not :
function sendMessage {
while read line; do
if [[ $line == "ENDRESPONSE" ]]; then
break
else
echo $line
fi
done < <(netcat "$ipAddress" "$port" <<< "$*")
}
Anyway thanks for your help, I'll try to implement other solutions later !
You can exec nc as coproc, send the line to the server, then use read with a timeout to read the response, check whether the response is complete and then close the connection by sending a signal to nc.
Instead of using nc you can perhaps also use bash TCP capabilities, like
exec 5<>/dev/tcp/"$HOST"/"$PORT"
read -t "$TIMEOUT" -n "$MAXLEN" line
exec 5<&- 5>&-

Getting +CMS ERROR: 96 While try to send sms through AT commands

echo -ne "ATZ\r\n" > /dev/ttyUSB0
echo -ne "AT+CMGF=1\r\n" > /dev/ttyUSB0
echo -ne "AT+CMGS=\"888XXXXXXX\"\rhello\x1a\n" > /dev/ttyUSB0
Since CMS ERROR-96 is for mandatory information missing.
No, you cannot send AT commands in this way. Start by reading the V.250 (all of chapter 5) and 27.005 specifications (the AT+CMGS command), links are in the tag info page. Those documents will teach you a lot with regards to AT command handling, syntax and behaviour.
The worst mistake is to send AT command lines without waiting for the final result code. In the same way you would not write a HTTP client that totally ignores responses from the HTTP server, you should not send AT commands and totally ignore responses from the modem.
And for the AT+CMGS command you have to wait with sending the sms payload data until after you have received the "\r\n> " prefix (see the first part of this answer for details).
Other things, AT command lines should be terminated with \r only and not \r\n. Opening and closing the modem device over and over again like multiple shell command redirects will cause might be an issue. That totally depends on the specific modem, and there is no guarantee that this will work reliably.
To address these last points I wrote the atinout program to make sending AT command from the command line simple and reliable. The first two AT commands can be sent as
$ echo ATZ | atinout - /dev/ttyUSB0 -
ATZ
OK
$ echo AT+CMGF=1 | atinout - /dev/ttyUSB0 -
AT+CMGF=1
OK
$
or alternatively
$ echo ATZ > commands.txt
$ echo AT+CMGF=1 >> commands.txt
$ atinout commands.txt /dev/ttyUSB0 output.txt
but for AT+CMGS I guess you have to use expect for the time being (in the future atinout will have a companion program specifically for sending AT+CMGS).

BASH: keep connection alive

I have the following scenario:
I use netcat to connect to a host running telnet server on port 23, I log in using provided username and password, issue some commands, after which I need to do fairly complex analysis of the provided output. Naturally, expect comes to mind, with a script like this:
spawn nc host 23
send "user\r"
send "pass\r"
send "command\r"
expect EOF
then, it is executed with expect example.scr >output.log, so the output file can be parsed. The parser is 150+ lines of bash code that executes under 2 seconds, and makes a decision what command should be executed next. Thus, it replaces "command" with "command2", and executes the expect script again, like this:
sed -i '/send "command\r"/send "command2\r"/' example.scr
expect example.scr >output.log
Obviously, it is not needed to re-establish telnet connection and perform log in process all over again, just to issue a single telnet command after 2 seconds of processing. A conclusion can be made, that telnet session should be kept alive as a background process, so one could freely talk to it at any given time. Naturally, using named pipes comes to mind:
mkfifo in
mkfifo output.log
cat in | nc host 23 >output.log &
echo -e "user\npass\ncommand\n" >in
cat output.log
After the file is written to, EOF causes the named pipe to close, thus terminating the telnet session. I was thinking what kind of eternal process could be piped to netcat so it can be used as telnet relay to host. I came up with a very silly idea, but it works:
nc -k -l 666 | nc host 23 >output.log &
echo -e "user\npass\ncommand\n" | nc localhost 666
cat output.log
The netcat server is started with k(eep alive), listening on port 666, and any data stream is redirected to the netcat telnet client connected to the host, while the entire conversation is dumped to output.log. One can now echo telnet commands to nc localhost 666, and read the result from output.log.
One should keep in mind that the expect script can be easily modified to accommodate SSH and even serial console connection, just by spawning ssh or socat instead of netcat. I never liked expect because it forces a use of another scripting language within bash, requires tcl libraries, and needs to be compiled for the embedded platforms, while netcat is a part of busybox and readily available everywhere.
So, the question is - could this be done in a simpler way? I'd put my bet on having some sort of link between console and TCP socket. Any suggestions are appreciated.
How about using like a file descriptor?
exec 3<>/dev/tcp/host/port
while true; do
echo -e "user\npass\ncommand" >&3
read_response_generate_next_command <&3 >&3
# if no more commands, break;
done
exec 3>&-

How to get telnet execution result code from shell script?

Using the following shell code for remote telnet request:
{
sleep 5
echo admin
sleep 3
echo pass
sleep 3
echo ls
sleep 5
echo exit
} | telnet 172.16.1.1
I want to check if telnet connection was successful or not. Trying to use $?:
echo $?
But it always returns "1", even if telnet connection was OK.
Telnet is exceptionally difficult to script in this way, there is a high degree of asynchronicity with the time it takes to establish a connection and for your intended actions to complete. expect was created for exactly this kind of purpose. You launch a program, like telnet, then declare a series of expectations - eg, when 'username: ' is emitted from the program, and an action to trigger (eg: typing in the username).
There are also libraries or wrappers for expect in many languages:
python expect
ruby expect
perl expect
Here is an example that drives telnet to make an HTTP HEAD request:
set timeout 20
spawn telnet localhost 80
expect "Connected to "
send "HEAD / HTTP/1.0\r\n\r\n"
expect "HTTP 200 OK"
Given all of this, I feel I must point out that telnet is considered insecure. Ssh is a much better choice and supports better choices for authentication (eg: public/private key auth), restrictions for commands that can be run (via .ssh/authorized_keys). With ssh, and ssh-keys set up, your script reduces to a single shell command:
ssh user#hostname ls
ssh has great support for safe, secure remote command execution.
If I'm remembering correctly, this expect script does what you're doing above.
#!/usr/bin/expect
spawn telnet 172.16.1.1
expect username:
send admin
expect password:
send pass
expect "\$ "
send ls
expect "\$ "
send exit
Here's a useful link for getting started: http://oreilly.com/catalog/expect/chapter/ch03.html

Facing difficulty to transfer a file from one server to another using TCL

I am trying to transfer a file from one server to a remote server with the help of a TCL script.
But my script stops after the message "200 Port set okay" and continues to rum from the below telnet session.
I have checked the destination location, my file is not transferred.
Please suggest what can I do or where I am wrong
#!/usr/bin/tclsh
#!/usr/bin/expect
package require Expect
set p "mm155_005.006.010.200_bt.fw"
#**************************************************************\
FILE TRANSFER TO REMOTE SERVER \
***************************************************************
spawn ftp 10.87.121.26
expect "User (10.87.121.26:(none)):"
send "user\r"
expect "Password:"
send "pswd\r"
expect "ftp>"
send "cd FW\r"
expect "ftp>"
send "ha\r"
expect "ftp>"
send "bi\r"
expect "ftp>"
send "mput \"$p\"\r"
expect "mput $p? "
send "yes\r"
expect "ftp>"
send "ls\r"
#**************************************************************\
RUNNING THE TRANSFERED FILE \
***************************************************************
spawn telnet 10.87.121.26
expect "Login: "
send "user\r"
expect "password: "
send "pswd\r"
expect "*? > "
send "cd FW\r"
expect "*? > "
send "burnboot 30 5.6(10.200)\r"
Output
spawn ftp 10.87.121.26
Connected to 10.87.121.26.
220 VxWorks FTP server (VxWorks VxWorks5.4.2) ready.
Name (10.87.121.26:vkumar): user
331 Password required
Password:
230 User logged in
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd FW
250 Changed directory to "C:/FW"
ftp> ha
Hash mark printing on (1024 bytes/hash mark).
ftp> bi
200 Type set to I, binary mode
ftp> mput "mm155_005.006.010.200_bt.fw"
mput mm155_005.006.010.200_bt.fw? yes
200 Port set okay \ I am unable to see hash progress bar after this line
spawn telnet 10.87.121.26
Trying 10.87.121.26...
Connected to 10.87.121.26.
Escape character is '^]'.
Login: user
password:
node84.7.PXM.a > cd FW
node84.7.PXM.a > bash-2.05b$
Instead of rolling your own solution in Expect (I also did this about 9 years ago), use the FTP module from tcllib -- it's already battle-hardened.
http://tcllib.sourceforge.net/doc/ftp.html
The script as reported is unlikely to produce exactly that output; there is nothing from the ls done after the mput. However, if the mput is hanging the most likely problem is that there is a firewall issue; FTP uses multiple sockets to do file transfers (which is why FTP is such a pain when it comes to overall firewall management). In particular, it has a command channel (the socket which you communicate with the FTP server over) and a separate data channel per file (and also with the output of some remote commands, such as ls); that's what that Port set okay is about. This is not firewall-friendly, and it's easy to misconfigure firewalls in this area (especially when there is NAT also in place).
You might (i.e., try this first) want to use passive mode instead, as that reduces the complexity at the firewall level. Try issuing a passive before the mput (just as you currently issue a binary).
Here's a bash script I wrote with a similar function;
You should be able to adapt it to your needs.
Adding a few lines to SSH in and run the script should be quite trivial.
As a side note; why are you using TCL for this?
#!/bin/bash
fileName=`ls /home/user/downloads -t1 | head -n1`
latestFile="/home/user/downloads/$fileName"
echo $latestFile
hostname="HOSTNAME"
username="USER"
password="PASS"
ftp -inv $hostname << EOF
quote USER $username
quote PASS $password
cd transfer/jirabackup
binary
put $latestFile $fileName
quit

Resources