steptail.com

How many light bulbs does it take to change a penguin?

User Tools

Site Tools


guides:virtual_modem:script

VModem

The script that fakes a Hayes compatible modem.

vmodem.sh

The main script. This script will open the serial port for communication and execute the dialled number as a linux script. So if you dial ATD12345, the script will look for a file 12345.sh in the working directory and execute it and output contents to serial console.

This script can be run standalone, or with the accompanying T1.sh and ppp.sh scripts, which will enable point-to-point serial to ethernet connections.

vmodem.sh
#!/bin/bash
#
# Virtual Modem Shell Script
# Oliver Molini 2018
#
# Licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Public License
# https://creativecommons.org/licenses/by-nc-sa/4.0/
#
# CONFIGURATION VARIABLES
# -----------------------
 
# Variable: serport
# serport specifies which local serial device to use.
# For example, using "ttyAMA0" will tell the script
# to use /dev/ttyAMA0 for communication.
#
# Default:
# serport=ttyUSB0
#
serport=ttyUSB0
 
# Variable: baud
# baud will tell the script to open the serial port at
# specified symbol rate. When connecting, make sure
# your client computer uses the same baud than what
# has been specified here.
#
# - Common baud rates: 9600, 19200, 38400, 57600
#
# Default:
# baud=57600
#
baud=57600
 
# Variable: echoser
# echoser sets the default behaviour of echoing serial
# data back to the client terminal. The default is 1.
#
echoser=1
 
# Script version
vmodver=1.1
 
# EXPORT SHELL VARS
# -----------------
export serport
export baud
 
# DEFINE FUNCTIONS
# ----------------
#
#INITIALIZE SERIAL SETTINGS
ttyinit () {
  stty -F /dev/$serport $baud
  stty -F /dev/$serport sane
  stty -F /dev/$serport raw
  stty -F /dev/$serport -echo -icrnl clocal
}
 
# SEND MESSAGE ON SCREEN AND OVER SERIAL
sendtty () {
  echo "$1";
  echo -en "$1\x0d\x0a" >/dev/$serport
}
 
# Open serial port for use. Allocate file descriptor
# and treat the serial port as a file.
ttyinit
exec 99<>/dev/$serport
 
sendtty ""
sendtty "Virtual Modem bootstrap for PPP link v$vmodver"
sendtty "Connection speed set to $baud baud"
sendtty ""
sendtty "TYPE HELP FOR COMMANDS"
sendtty "READY."
 
# MAIN LOOP
while [ "$continue" != "1" ]; do
  charhex=`head -c 1 /dev/$serport | xxd -p -`
  char="`echo -e "\x$charhex"`"
 
  #ECHO SERIAL INPUT TO TTY
  echo -n "$char"
 
  #ECHO SERIAL INPUT
  if [ "$echoser" = "1" ]; then echo -n $char > /dev/$serport; fi
 
  #CHECK IF NEWLINE IS SENT
  if [ "$charhex" = "0d" -o "$charhex" = "0a" ]; then
    line=$buffer
    # PARSE COMMAND
    cmd=`echo -en $buffer | tr a-z A-Z`
    buffer=
    char=
 
    #NEWLINE SENT - ECHO NEWLINE TO CONSOLE
    if [ "$echoser" = "1" ]; then sendtty; fi
 
    # DUMMY HAYES COMMAND PROCESSOR
    if [[ $cmd == AT    ]] ||
       [[ $cmd == ATA*  ]] ||
       [[ $cmd == ATH*  ]] ||
       [[ $cmd == ATL*  ]] ||
       [[ $cmd == ATM*  ]] ||
       [[ $cmd == ATS*  ]] ||
       [[ $cmd == ATV*  ]] ||
       [[ $cmd == ATX*  ]] ||
       [[ $cmd == AT\&* ]]; then sendtty "OK"
    fi
 
    # ATE  -  TERMINAL ECHO
    if [[ $cmd == ATE*  ]]; then
      if [[ $cmd == ATE1* ]]; then echoser=1; else echoser=0; sendtty "OK"; fi
    fi
    if [[ $cmd == ATZ* ]]; then echoser=1; sendtty "OK"; fi
 
    if [[ $cmd = HELP ]]; then
      sendtty "Command Reference for Virtual Modem Bootstrap v$vmodver"
      sendtty
      sendtty "AT......Test serial link. Prints OK if successful"
      sendtty "ATE0....Switch terminal echo off"
      sendtty "ATE1....Switch terminal echo on"
      sendtty "ATD?....Execute program ?.sh and output to terminal"
      sendtty "ATZ.....Reset settings"
      sendtty "HELP....Display command reference"
      sendtty "LOGIN...Begin Linux login session on serial terminal"
      sendtty "EXIT....End this script"
      sendtty
      sendtty "READY."
    fi
 
    # ATD  -  DIAL
    if [[ $cmd == ATD* ]]; then
      sendtty "RINGING"; sendtty; sleep 1
      if [ -f "`echo $cmd |cut -b 4-`.sh" ]; then
        sendtty "CONNECT $baud"
        # Close serial port
        exec 99>&-
        # Execute dialed script
        /sbin/getty -8 -L $serport $baud vt100 -n -l "./`echo $cmd|cut -b 4-`.sh"
        # Reset serial settings
        ttyinit
        # Reopen serial port
        exec 99<>/dev/$serport
      fi
      sleep 1
      sendtty
      sendtty "NO CARRIER"
    fi
 
    # LOGIN  -  BEGIN LOGIN SESSION
    if [[ $cmd == LOGIN ]]; then
      exec 99>&-
      /sbin/getty -8 -L ttyAMA0 $baud vt100
      ttyinit
      exec 99<>/dev/$serport
      sendtty; sendtty "READY."
    fi
 
    # EXIT  -  EXIT SCRIPT
    if [ "$cmd" = "EXIT" ]; then sendtty "OK"; continue="1"; fi
  fi
  buffer=$buffer$char
done
 
#Close serial port
exec 99>&-

T1.sh

T1 is used to establish a PPP connection. T1.sh can be called by issuing they Hayes dial command “ATDT1” using vmodem.sh. T1.sh is just a redirect script and will in turn run ppp.sh which will actually initiate the PPP connection.

T1.sh
#!/bin/bash
#
./ppp.sh

ppp.sh

This script prints a fake login shell but will not wait around for input. This is in place only to make Trumpet Winsock 3.0 think that it's logging on, and make it proceed when it receives the expected printouts. The built-in dial-up connection in Windows 95 and later operating systems by default do not expect a login prompt unless specifically told to do so.

Specifically when Trumpet Winsock is in PPP mode, by default it will expect the following output after dialing the ISP's number and establishing a connection:

  • A username prompt, matched by the text “sername:”
  • A password prompt, matched by the text “ssword:”
  • A command prompt, matched by the text “>”

This script has been tested with the default installation of Trumpet Winsock 3.0 revision D with PPP mode switched on. This script has also been tested with the default dial-up utility of Windows 95 and Windows 98 with PPP enabled.

I've added a parameter to send an LCP echo to the client to test if the connection is still up. If the connection has abruptly been closed, pppd will know this by not receiving an echo reply, and will exit and relinquish control back to the vmodem.sh script. The only reason the timeout is in there, is because it seems like Trumpet Winsock 3.0 doesn't know how to tell pppd to terminate a PPP session from within a PPP session, and it will just attempt to hang up the call. As a result, pppd daemon will be left running indefinitely. This is obviously not preferred, so LCP echo is added to let pppd know when the link has been cut. If you can think of better ways to accomplish this check, feel free to send tips on how to improve the script.

ppp.sh
#!/bin/bash
#
 
# Variable: lcpidle
# Specifies the idle timeout period in seconds for lcp-echo-interval.
# This is to ensure that pppd will not run indefinitely after sudden
# hangup and will relinquish control back to the vmodem.sh.
#
# Default:    lcpidle=5
#
lcpidle=5
 
#
# Trumpet Winsock 3.0 revision D for Windows 3.1
# by default requires a fake login shell.
#
# Windows 95 and 98 will not care for a login shell
# unless specifically told to expect one.
#
printf "\n`uname -sn`****\n"
printf "\nUsername: "; sleep 1
printf "\nPassword: "; sleep 1
printf "\nStarting pppd..."
printf "\nPPP>"
# End of fake login prompt.
 
# Set the kernel to router mode
sysctl -q net.ipv4.ip_forward=1
 
# Share eth0 over ppp0
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -t filter -A FORWARD -i ppp0 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -t filter -A FORWARD -i eth0 -o ppp0 -j ACCEPT
 
# Run PPP daemon and establish a link.
pppd noauth nodetach local lock lcp-echo-interval $lcpidle lcp-echo-failure 3 proxyarp ms-dns 8.8.4.4 ms-dns 8.8.8.8 10.0.100.1:10.0.100.2 /dev/$serport $baud
 
# Flush iptables
iptables -t filter -F FORWARD
iptables -t nat -F POSTROUTING
 
printf "\nPPP link terminated.\n"
guides/virtual_modem/script.txt · Last modified: 2018-09-23 01:24 by omolini