#!/usr/local/bin/perl

#######################################################
# dring (Decoder Ring 2.6)                            # 
# Written By John Storms, November 1996               # 
# Used to decode PPP traces and CRC errors from       #
# Livingston Products using a 0x51 debug mask.        #
#                                                     #
# use:                                                #
#                                                     #
#        dring <filename>                             #
#                                                     #
# NOTE: This software is not a Livingston Product and #
# is not supported by Livingston's technical staff,   #
# but its pretty darn cool and I hope you find it     #
# useful.  This is a freeware program so no guilt.    #
#                                                     #
# 2.0 Includes LCP, IPCP, IPXCP, PAP, CHAP, CRC       #
# 2.1 Additional Multilink parsing added              #
# 2.2 PAP overhauled, improved line by line parser,   #
#     fixed CRC bug                                   #
# 2.3 IPCP deprecated parsing enhanced                #
#     CCP parsing added                               #
#     CGI version incorporated                        #
# 2.4 CGI bug fixed                                   #
# 2.5 CHAP bug fixed                                  # 
# 2.6 MRU now displays data in decimal (4/30/97)      #
#     fixed a bug where hex2dec was called w/o a '&'  #
#######################################################

## Semi Constants

# The $IMP scalar variable determines how dring will 
# behave.  If cgi then it will behave as a CGI script
# using a variable named "body" for the PPP trace .
# If file then dring will look to a specified file for
# its input.

#$IMP = "file";
$IMP = "cgi";

# Flags for determining sequences to be decoded
$PAPFLAG	= "PAP_";
$CHAPFLAG	= "CHAP_";
$LCPFLAG	= "LCP";
$IPCPFLAG	= "IPCP";
$IPXFLAG	= "IPXCP";
$UNFLAG		= "UNKNOWN";
$CRCFLAG	= "Ptrace";
$REJECT		= "LCP_CODE_REJECT";
$CCPFLAG	= "CCP_";


# LCP Options Array
@lcpoption = (
	"01::Maximum-Receive-Unit",
	"02::Async-Control-Character-Map",
	"03::Authentication-Protocol",
	"04::Quality-Protocol",
	"05::Magic-Number",
	"06::RESERVED",
	"07::Protocol-Field-Compression",
	"08::Address-and-Control-Field-Compression",
	"09::FCS-Alternatives",
	"0A::Self-Describing-Pad",
	"0B::Numbered-Mode",
	"0C::Multi-Link-Procedure",
	"0D::Callback",
	"0E::Connect-Time",
	"0F::Compound-Frames",
	"10::Nominal-Data-Encapsulation",
	"11::Multilink-MRRU",
	"12::Multilink-Short-Sequence-Number-Header",
	"13::Multilink-Endpoint-Discriminator",
	"14::Proprietary",
	"15::DCE-Identifier",
	"16::Multi-Link-Plus-Procedure",
	"17::Link-Discriminator-for-BACP"
);

# IPCP Options Array
@ipcpoption = (
	"01::IP-Addresses (deprecated)",
	"02::IP-Compression-Protocol",
	"03::IP-Address",
	"81::Primary DNS Server Address",
	"82::Primary NBNS Server Address",
	"83::Secondary DNS Server Address",
	"84::Secondary NBNS Server Address",
);

# CCP Options Array (RFC 1962)
@ccpoption = (
	"00::Compression type=Organization Unique Identifier (OUI)",
	"01::Compression type=Predictor type 1",
	"02::Compression type=Predictor type 2",
	"03::Compression type=Puddle Jumper",
	"04::Compression type=unassigned",
        "05::Compression type=unassigned",
        "06::Compression type=unassigned",
        "07::Compression type=unassigned",
        "08::Compression type=unassigned",
        "09::Compression type=unassigned",
        "0A::Compression type=unassigned",
        "0B::Compression type=unassigned",
        "0C::Compression type=unassigned",
	"0D::Compression type=unassigned",
        "0E::Compression type=unassigned",
        "10::Compression type=Hewlett-Packard PPC",
        "11::Compression type=Stac Electronics LZS",
        "12::Compression type=Microsoft PPC",
        "13::Compression type=Gandalf FZA",
        "14::Compression type=V.42bis compression",
        "15::Compression type=BSD LZW Compress",
	"FF::Compression type=Reserved"
);

# IPXCP Options Array
@ipxoption = (
	"01::IPX-Network-Number",
	"02::IPX-Node-Number",
	"03::IPX-Compression-Protocol",
	"04::IPX-Routing-Protocol",
	"05::IPX-Router-Name",
	"06::IPX-Configuration-Complete",
);

# Assigned PPP DLL Protocol Numbers Array
# See RFC1700
@protolist = (
	"0001::Padding Protocol",
	"0021::Internet Protocol version 4",
	"0023::OSI Network Layer",
	"0025::Xerox NS IDP",
	"0027::DECnet Phase IV",
	"0029::Appletalk",
	"002B::Novell IPX",
	"002D::Van Jacobson Compressed TCP/IP",
	"002F::Van Jacobson Uncompressed TCP/IP",
	"0031::Bridging PDU",
	"0033::Stream Protocol (ST-II)",
	"0035::Banyan Vines",
	"0037::reserved (until 1993)",
	"0039::AppleTalk EDDP",
	"003B::AppleTalk SmartBuffered",
	"003D::Multi-Link",
	"003F::NETBIOS Framing",
	"0041::Cisco Systems",
	"0043::Ascom Timeplex",
	"0045::Fujitsu Link Backup and Load Balancing (LBLB)",
	"0047::DCA Remote Lan",
	"0049::Serial Data Transport Protocol (PPP-SDTP)",
	"004B::SNA over 802.2",
	"004D::SNA",
	"004F::IP6 Header Compression",
	"0051::KNX Bridging Data",
	"0053::Encryption",
	"0055::Individual Link Encryption",
	"0057::Internet Protocol version 6",
	"006F::Stampede Bridging",
	"0071::BAP Bandwidth Allocation Protocol",
	"0073::MP+ Protocol",
	"007D::reserved (Control Escape)",
	"007F::reserved (compression inefficient)",
	"00C1::NTCITS IPI",
	"00CF::reserved (PPP NLPID)",
	"00FB::single link compression in multilink",
	"00FD::compressed datagram",
	"00FF::reserved (compression inefficient)",
	"0201::802.1d Hello Packets",
	"0203::IBM Source Routing BPDU",
	"0205::DEC LANBridge100 Spanning Tree",
	"0207::Cisco Discovery Protocol",
	"0209::Netcs Twin Routing",
	"0231::Luxcom",
	"0233::Sigma Network Systems",
	"0235::Apple Client Server Protocol",
	"4001::Cray Communications Control Protocol",
	"4003::CDPD Mobile Network Registration Protocol",
	"4021::Stacker LZS",
	"8021::Internet Protocol Control Protocol",
	"8023::OSI Network Layer Control Protocol",
	"8025::Xerox NS IDP Control Protocol",
	"8027::DECnet Phase IV Control Protocol",
	"8029::Appletalk Control Protocol",
	"802B::Novell IPX Control Protocol",
	"802D::reserved",
	"802F::reserved",
	"8031::Bridging NCP",
	"8033::Stream Protocol Control Protocol",
	"8035::Banyan Vines Control Protocol",
	"8037::reserved till 1993",
	"8039::reserved",
	"803B::reserved",
	"803D::Multi-Link Control Protocol",
	"803F::NETBIOS Framing Control Protocol",
	"8041::Cisco Systems Control Protocol",
	"8043::Ascom Timeplex",
	"8045::Fujitsu LBLB Control Protocol",
	"8047::DCA Remote Lan Network Control Protocol (RLNCP)",
	"8049::Serial Data Control Protocol (PPP-SDCP)",
	"804B::SNA over 802.2 Control Protocol",
	"804D::SNA Control Protocol",
	"804F::IP6 Header Compression Control Protocol",
	"8051::KNX Bridging Control Protocol",
	"8053::Encryption Control Protocol",
	"8055::Individual Link Encryption Control Protocol",
	"8057::IPv6 Control Protocol",
	"806F::Stampede Bridging Control Protocol",
	"8073::MP+ Control Protocol",
	"8071::BACP Bandwidth Allocation Control Protocol",
	"807d::Not Used - reserved",
	"80C1::NTCITS IPI Control Protocol",
	"80CF::Not Used - reserved",
	"80FB::single link compression in multilink control",
	"80FD::Compression Control Protocol",
	"80FF::NotUsed - reserved",
	"8207::Cisco Discovery Protocol Control",
	"8209::Netcs Twin Routing",
	"8235::Apple Client Server Protocol Control",
	"C021::Link Control Protocol",
	"C023::Password Authentication Protocol",
	"C025::Link Quality Report",
	"C027::Shiva Password Authentication Protocol",
	"C029::CallBack Control Protocol (CBCP)",
	"C081::Container Control Protocol",
	"C223::Challenge Handshake Authentication Protocol",
	"C225::RSA Authentication Protocol",
	"C227::Extensible Authentication Protocol",
	"C229::Mitsubishi Security Info Exch Ptcl (SIEP)",
	"C26F::Stampede Bridging Authorization Protocol",
	"C281::Proprietary Authentication Protocol",
	"C283::Proprietary Authentication Protocol",
	"C481::Proprietary Node ID Authentication Protocol"
);

# For Multilink PPP see RFC 1717
@endpointclass = (
	"00::Null Class",
	"01::Locally Assigned Address",
	"02::IP Address",
	"03::IEEE 802.1 MAC Address",
	"04::PPP Magic-Number Block",
	"05::Public Switched Network Directory Number"
);

# Global Variables (Bad I know)
#@in				# holds the CGI input
#READ				# file handle for text input file
$line		= "";		# holds trace information line by line
$firstline	= "";		# holds first line of PPP entry
$ptr		= 0;		# line pointer for CGI input
$crc		= 0;		# crc flag
$hexstring	= "";		# holds string of hex to be parsed

# Determine how input will be handled
if($IMP eq "file") {  # Input coming from a text file
	$filename = &argument(@ARGV);		# Get filename from argument

	# Open file for parsing
	unless (open(READ, $filename)) {
       		die ("dring ERROR: Cannot open $filename\n");
	}
}
elsif($IMP eq "cgi") { # Input coming from a HTML form
	&ReadParse;			# Put Arguments into $in array
	print &PrintHeader;		# Print HTML header
	@cgi = split(/\n/,$in{body});   # Create array from HTML input
	$ptr = 0;			# Line pointer
	print "<PRE>";			# Use PRE tag for outputting data
}

print("\nDring: Decoder Ring for Livingston Product PPP traces\n");
print("-----------------------------------------------------\n");

# Cycle thru input line by line
$line = &getnextline;
while($line ne "") {
	$hexstring = "";	# initialize hex string
	if(!(index($line,$CRCFLAG) != -1)) {
		chop($line);
	}

	# If blank line then grab another one and start again.
	if($line eq "") {
		$line = &getnextline;
		next; # jump back up to while statement
	}
	$firstline = $line;
	# Identify PPP related items that do not require parsing
	if(index($line,"Open")		!= -1 ||
	   index($line,"Apparent")	!= -1 || 
	   index($line,"CRC")		!= -1 ||
	   index($line,"LMI")		!= -1 ||
	   index($line,"Annex")		!= -1 ||
	   index($line,"Re-opening")	!= -1 ||
	   index($line,"Succeeded")	!= -1 ) {
		print("**** $line\n");
		$line = &getnextline;
	}

	# Identify items we can parse
	elsif(&checkparseflags($line)) {
		# For CRC errors since hex is found in first line
		if(index($line,$CRCFLAG) != -1) {
			$crc = 1;
			($dud, $hexstring) = split(/: /,$line);
			#print "CRC[$hexstring]\n";
		}
		# For everything else
		else {
			print("\n$firstline\n");
		}

		$line = &getnextline; # grab next line
		chop($line);

		# Build hex string to be parsed and decoded
		if(&ishex($line)) {
			$hexstring = $hexstring.$line;
			$line = &getnextline;

			# checks the next line to see if it is a line of hex
			while(&ishex($line) eq "1") {
				chop($line);
				$hexstring = $hexstring.$line; # if it is hex add it on to hexstring
				$line = &getnextline;
			}
		}

		# Clean up hexstring before parsing
		$hexstring = &clean($hexstring);

		# Send hexstring to parsing routines
		if(index($firstline,$LCPFLAG)	!= -1) {
			&parselcp($hexstring);		# LCP
		}
		elsif(index($firstline,$PAPFLAG)	!= -1) {
			&parsepap($hexstring);		# PAP
		}
		elsif(index($firstline,$CHAPFLAG)	!= -1) {
			&parsechap($hexstring);		# CHAP
		}
		elsif(index($firstline,$IPCPFLAG)	!= -1) {
			&parseipcp($hexstring);		# IPCP
		}
		elsif(index($firstline,$IPXFLAG)	!= -1) {
			&parseipxcp($hexstring);	# IPXCP
		}
		elsif(index($firstline,$CCPFLAG)	!= -1) {
			&parseccp($hexstring);
		}
		elsif(index($firstline,$UNFLAG)		!= -1) {
			&parseunknown($hexstring);	# UNKNOWN
		}
		elsif(index($firstline,$REJECT)		!= -1) {
			&parseunknown($hexstring);
	 	}
		elsif(index($firstline,$CRCFLAG)	!= -1) {
			&parseunknown($hexstring);	# CRC
		}
	}
close(MAIL);
}

sub parseccp {
# Parse and decode CCP packets
        # initialize local variables
        local(@newlist);
        local($data)            = "";
        local($pid)             = "";
        local($plength)         = "";
        local($pdata)           = "";
	local($pcode)		= "";
	local($length)		= "";
	local($i)		= 0;
	local($option)		= "";

        # Get and Display Packet Information
        ($pcode, $pid, $plength, $pdata) = split(/::/,&buster($_[0]));
        print(&showhex($_[0]),"\n\tPacket Info:  Code: $pcode, ID: $pid, ",&hex2dec($plength)," bytes.\n");

        # Get a list of options
        @newlist = &old($pdata);

        # Cycle thru list of options and decode them
        for($i=0;$i<@newlist;$i++) {
                ($option, $length, $data) = split(/::/,$newlist[$i]);
                #print "\t== option: $option, length: $length, data: $data ==\n";
                print("\t",&match(@ccpoption, $option)," [0x$option] length: (",&hex2dec($length)," bytes)");
                # Handles the case when there are no options.
                if($data ne "") {
                        print(" ",&match(@protolist,substr($data,0,4))," [0x$data]");
                }
                print("\n");
        }
}

sub parselcp {
# Parse and decode LCP packets
	#initialize variables
	local(@newlist)	= "";
	local($option)	= "";
	local($length)	= "";
	local($data)	= "";
	local($pcode)	= "";
	local($pid)	= "";
	local($plength)	= "";
	local($pdata)	= "";
	local($i)	= 0;

	# Get and Display General packet information
	($pcode, $pid, $plength, $pdata) = split(/::/,&buster($_[0]));
	print(&showhex($_[0]),"\n\tPacket Info:  Code: $pcode, ID: $pid, ",&hex2dec($plength)," bytes.\n");

	# Get a list of options and decode them
	@newlist = &old($pdata);
	for($i=0;$i<@newlist;$i++) {
		($option, $length, $data) = split(/::/,$newlist[$i]);

		if($option ne "00") {
			print("\t",&match(@lcpoption,$option),"[0x$option], length: (",&hex2dec($length)," bytes)");
			# Handles case of no data like LCP_ECHO_REQUESTS
			if($data ne "") {
#				print(", ",&match(@protolist,substr($data,0,4)),"[0x$data]");
				if($option eq "01") {
					print " ",&hex2dec($data)," bytes [0x$data]";
				}
				else {
					print(", ",&match(@protolist,substr($data,0,4)),"[0x$data]");
				}
			}
			print("\n");

			# Parse Multilink (RFC1717)
			if($option eq "13") {
				&parseendpoint($data);
			}
			# Parse MRRU
			elsif($option eq "11") {
				&parseMRRU($data);
			}
		}
	}
}

sub parsepap {
# Decodes PAP packets displaying information like usernames, passwords,   
# messages, etc.
	# initialize variables
	local(@newlist);
	local($loginlen)	= "";
	local($firstbyte)	= "";
	local($login)		= "";
	local($pcode)		= "";
	local($pdata)		= "";
	local($pass)		= "";
	local($data)		= "";
	local($pid)		= "";
	local($plength)		= "";

	# Get and Display Packet Information
	($pcode, $pid, $plength, $pdata) = split(/::/,&buster($_[0]));
	print(&showhex($_[0]),"\n\tPacket Info:  Code: $pcode, ID: $pid, ",&hex2dec($plength)," bytes.\n");

	# Get a list of options (with PAP there will only be one option
	@newlist = &old($pdata);
	($loginlen, $firstbyte, $data) = split(/::/,$newlist[$i]);
	$data = $firstbyte.$data; # combine first byte with rest of data.

	# Take care of Authentication-Requests 
	if($pcode eq "01") {
		$login = substr($data,0,(&hex2dec($loginlen)*2));
		$data  = substr($data,&hex2dec($loginlen)*2,length($data)-(&hex2dec($loginlen)*2));
		$plength = &hex2dec(substr($data,0,2));
		$pass  = substr($data,2,length($data)-2);
		# Display Login Info
		print("\tLogin ID: ",&hex2asc($login));
		print(" (",&hex2dec($loginlen)," bytes)");
		print(", [0x$login]\n");
		# Display Password Info 
		print("\tPassword: ",&hex2asc($pass));
		print(" ($plength bytes)");
		print(", [0x$pass]\n");
	}

	# Take care of Authentication Acks and Naks
	else {
		print("\tMessage:  ",&hex2asc(substr($data,0,(&hex2dec($loginlen))*2))," (",&hex2dec($loginlen)," bytes),\n\t\t[0x",substr($data,0,(&hex2dec($loginlen))*2),"]\n");
	}
}

sub parsechap {
# Parse CHAP packets to display CHAP values and names.
	# initialize variables
        local(@newlist);
        local($data)            = "";
 	local($code)		= "";
        local($id)              = "";
        local($length)          = "";
	local($valsize)		= 0;
	local($value)		= 0;
	local($name)		= "";

	# Get general packet information
        ($code, $id, $length, $data) = split(/::/,&buster($_[0]));

	# Display general packet information
        print(&showhex($_[0]),"\n\tPacket Info:  Code: $code, ID: $id, ",&hex2dec($length)," bytes.\n");

	# Take Care of CHAP Challenges and Responses
	if($code eq "01" || $code eq "02") {
		# Determine CHAP parameters
	        $valsize = substr($data,0,2);
	        $value = substr($data,2,&hex2dec($valsize)*2);
	        $name = substr($data,2+(&hex2dec($valsize)*2),length($data)-(2+(&hex2dec($valsize)*2)));
		# Display CHAP parameters
		print("\tValSize[0x$valsize]: (",&hex2dec($valsize)," bytes), ");
		print("Value: [0x",$value,"]\n");
		print("\tName: ",&hex2asc($name)," [0x$name]\n");
	}

	# Take Care of CHAP Successes and Failures
	elsif($code eq "03" || $code eq "04") {
		$data = $length.$data;
		$name = substr($data,4,length($data)-8);	
		print("\tMessage: ",&hex2asc($name)," [0x$name]\n");
	}
}

sub parseipcp {
# Parses and decodes IPCP packets
	# initialize variables
	local(@newlist)	= "";
	local($option)	= "";
	local($length)	= "";
	local($data)	= "";
	local($pcode)	= "";
	local($pid)	= "";
	local($plength)	= "";
	local($pdata)	= "";
	local($i)	= 0;

	# Get and display packet information
	($pcode, $pid, $plength, $pdata) = split(/::/,&buster($_[0]));
	print(&showhex($_[0]),"\n\tPacket Info:  Code: $pcode, ID: $pid, ",&hex2dec($plength)," bytes.\n");

	# Get list of options
	@newlist = &old($pdata);

	# Parse and decode list of options
	for($i=0;$i<@newlist;$i++) {
		($option, $length, $data) = split(/::/,$newlist[$i]);
		print("\t",&match(@ipcpoption,$option)," [0x$option], length: (",&hex2dec($length)," bytes), ");

		# Convert hex ip address to dotted decimal
		if($option eq "03" || 
		   $option eq "81" ||
		   $option eq "82" ||
		   $option eq "83" ||	
		   $option eq "84") {
			$data = &hex2ip($data);
			print "[";
		} 
		# IP-Addresses Deprecated
		elsif($option eq "01") {
			print("\n\t\tSource-IP-Address:\t[",&hex2ip(substr($data,0,8)),"]\n");
			print("\t\tDestination-IP-Adress:\t[",&hex2ip(substr($data,8,8)),"]\n\t\tData: [0x");
		}
		# Decode protocol data
		else {
			print(&match(@protolist,substr($data,0,4))," [0x");
		}
		print("$data]\n");
	}
}

sub parseipxcp {
# Parses the hexstring for IPXCP options
	# initialize variables
	local(@newlist);
	local($pcode)		= "";
	local($data)		= "";
	local($pid)		= "";
	local($plength)		= "";
	local($pdata)		= "";
	local($option)		= "";
	local($length)		= "";

	# Get and Display Packet Information
	($pcode, $pid, $plength, $pdata) = split(/::/,&buster($_[0])); 
	print(&showhex($_[0]),"\n\tPacket Info:  Code: $pcode, ID: $pid, ",&hex2dec($plength)," bytes.\n");

	# Get a list of options
	@newlist = &old($pdata);

	# Cycle thru list of options and decode them
	for($i=0;$i<@newlist;$i++) {
		($option, $length, $data) = split(/::/,$newlist[$i]);
		#print "\t== option: $option, length: $length, data: $data ==\n";
		print("\t",&match(@ipxoption, $option)," [0x$option] length: (",&hex2dec($length)," bytes)");
		# Handles the case when there are no options.
		if($data ne "") {
			print(" ",&match(@protolist,substr($data,0,4))," [0x$data]");
		}
		print("\n");
	}
}

sub parseunknown {
	# initialize variables
	local($i)		= 0;
	local($count)		= 0;
	local($hexstring)	= "";
	local($ascstring)	= "";
	local($perline)		= 10;

	@ascii_table = (
		"00::[NULL]", "01::[SOH]", "02::[STX]", "03::[ETX]",
		"04::[EOT]",  "05::[ENQ]", "06::[ACK]", "07::[BEL]",
		"08::[BS]",   "09::[HT]",  "0A::[NL]",  "0B::[VT]",
		"0C::[NP]",   "0D::[CR]",  "OE::[SO]",  "OF::[SI]",
		"10::[DLE]",  "11::[DC1]", "12::[DC2]", "13::[DC3]",
		"14::[DC4]",  "15::[NAK]", "16::[SYN]", "17::[ETB]",
		"18::[CAN]",  "19::[EM]",  "1A::[SUB]", "1B::[ESC]",
		"1C::[FS]",   "1D::[GS]",  "1E::[RS]",  "1F::[US]",
		"20::[SP]",   "21::!",     "22::\"",    "23::#", 
		"24::\$",     "25::%",     "26::&",     "27::'",
		"28::(",      "29::)",     "2A::*",     "2B::+",
		"2C::,",      "2D::-",     "2E::.",     "2F::/",
                "30::0",      "31::1",     "32::2",     "33::3",
		"34::4",      "35::5",     "36::6",     "37::7",
                "38::8",      "39::9",     "3A:::",     "3B::;",
                "3C::<",      "3D::=",     "3E::>",     "3F::?",
                "40::@",      "41::A",     "42::B",     "43::C", 
		"44::D",      "45::E",     "46::F",     "47::G",
		"48::H",      "49::I",     "4A::J",     "4B::K", 
		"4C::L",      "4D::M",     "4E::N",     "4F::O",
                "50::P",      "51::Q",     "52::R",     "53::S", 
		"54::T",      "55::U",     "56::V",     "57::W",
                "58::X",      "59::Y",     "5A::Z",     "5B::[", 
                "5C::\\",     "5D::]",     "5E::^",     "5F::_",
                "60::`",      "61::a",     "62::b",     "63::c",
		"64::d",      "65::e",     "66::f",     "67::g",
		"68::h",      "69::i",     "6A::j",     "6B::k",
		"6C::l",      "6D::m",     "6E::n",     "6F::o",
		"70::p",      "71::q",     "72::r",     "73::s", 
		"74::t",      "75::u",     "76::v",     "77::w",
		"78::x",      "79::y",     "7A::z",     "7B::{", 
		"7C::|",      "7D::}",     "7E::~",     "7F::[DEL]"
	);

	# loop thru hexstring displaying rows and columns of hex
	for($i=0;$i<length($_[0]);$i=$i+2) {
		$hexstring = $hexstring.substr($_[0],$i,2)." ";	
			$tmp = &match(@ascii_table,substr($_[0],$i,2));
			if($tmp eq "") {$tmp = "[?]";}
			$ascstring = $ascstring.$tmp;
		$count++;
		if($count > $perline) {
			#print("\t$hexstring\t",&fixascii($ascstring),"\n");
			print("\t$hexstring\t",$ascstring,"\n");
			$hexstring = "";
			$ascstring = "";
			$count=0;
		}
	}
	# Display last line.
	if($hexstring ne "") {
#		print("\t$hexstring     ",&fixascii($ascstring),"\n");
		print("\t$hexstring\t\t",$ascstring,"\n");
	}
}

sub old {
# Option, Length Data
# This function takes a hexstring and splits out options, lengths and data
# and returns them in a list.
# Format of returned array elements:
#   option::length::data
	# initialize variables
	local(@optionlist);
	local($string)		= @_;
	local($count)		= 0;
	local($option)		= "";
	local($length)		= "";
	local($declength)	= "";
	local($data)		= "";

	# Cycle thru string chopping off the beginning as it is parsed
	while($string ne "")
	{
		$option 	= substr($string,0,2);
		$length 	= substr($string,2,2);
		$declength 	= &hex2dec($length);
		$data 		= substr($string,4,($declength-2)*2);
		$string = substr($string,$declength*2,length($string)-length($declength*2));
		# Build array element
		$optionlist[$count] = join("::",($option,$length,$data));
		$count=$count+1;  # increment count
	}
	return(@optionlist); # return array of options
}

sub buster {
# Returns a :: delimated string containing general packet information such
# as code, ID, length and packet data.
	local($code)		= substr($_[0],0,2);
	local($id)		= substr($_[0],2,2); 
	local($length)		= substr($_[0],4,4);
	local($data)		= substr($_[0],8,length($_[0])-6);
	# Return header information
	return(join("::",($code,$id,$length,$data)));
}

sub hex2dec {
# Converts passed value from decimal to hex
	$return=hex($_[0]);
}

sub hex2ip {
# Converts hex string into a dotted decimal IP address
	local($return)	= "";
	$return=&hex2dec(substr($_[0],0,2)).".".&hex2dec(substr($_[0],2,2)).".".&hex2dec(substr($_[0],4,2)).".".&hex2dec(substr($_[0],6,2));
}

sub hex2asc {
# Converts hex string into ascii byte by byte
	local($i)	= "0";
	local($retval)	= "";
	# Cycle thru string two bytes at a time.
	for($i=0;$i<length($_[0]);$i=$i+2) {
		$retval = $retval.sprintf("%c",hex(substr($_[0],$i,2)));
	}
	return($retval);
}

sub showhex {
# Build formatted hex string to be displayed with spaces between bytes
	local($retval)	= "";
	local($i)	= 0;
	local($count)	= 0;
	local($perline)	= 17;

	for($i=0;$i<length($_[0]);$i=$i+2) {
		$retval = $retval.substr($_[0],$i,2)." ";	
		if($count > $perline) {
			$retval = $retval."\n";
			$count = 0;
		}	
		$count++;
	}
	return($retval);
}

sub fixascii {
	local($string)	= $_[0];
	local($char)	= "";
	local($retval)	= "";
	local($i)	= 0;
	local($padchar)	= " ";
	local($printable) = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890~!@#$%^&*()_+`-=[]\\{}|;':\",.<>/?";
	for($i=0;$i<length($string);$i++) {
		$char = substr($string,$i,1);
		if(index($printable,$char) != -1) {
			$retval = $retval.$char;
		}
#		elsif() {
#		}
		else {
			$retval = $retval."X";
		}
	}
	return($retval);
}

sub ishex {
# Determines is passed string is a hex string
        $_ = substr($_[0],0,2);
        if((tr/a-fA-F0-9 /a-fA-F0-9 / eq "2") && substr($_[0],2,1) eq " ") {
                return(1);	# Returns a 1 for true if string is hex
        }       
        else {
                return(0);	# Returns a 0 for false if string is not hex
        }
}

sub clean {
# Cleans up hex string before parsing
	$hexstring =~ s/ //g;		# remove spaces
	$hexstring =~ s/\n$//g;		# remove newlines
	$hexstring =~ tr/a-z/A-Z/;	# capitalize everything
	$hexstring =~ tr/A-F,0-9//cd;   
	return($hexstring);
}

sub match {
# Return a matching value in array
# We receive an array.  Last element of array is the item to matched against.
	# initialize variables
	local(@array)	= @_;
	local($retval)	= "";
	local($count)	= 0;
	local($string)	= "";
	local($desc)	= "";

	# cycle thru array searching for match
	for($count=0;$count<@array-1;$count++) {
		($string, $desc) = split(/::/,$array[$count]);
		if($string eq $array[@array-1]) {
			$retval = $desc; # set description to retrun value
			$count = @array; # terminate loop
		}
	}
	# pad returned strings with a space for output formatting
	if(length($retval)>0) {
		$retval = $retval." ";	
	}
	return($retval); # return description to calling function
}

sub parseMRRU {
	local($option)	= $_[0];
	local($MRRU)	= substr($_[0],0,4);
	print("\t\tMax-Receive-Reconstructed-Unit (MRRU): ",hex($MRRU)," bytes.\n");
}

sub parseendpoint {
	local($class)	= substr($_[0],0,2);
	local($address)	= substr($_[0],2,length($_[0])-2);
	print("\t\tClass [0x$class]: ",&match(@endpointclass,$class)," ");

	if($class eq "02") {
		print(&hex2ip($address));
	}
	# Public Switched Network DN
	elsif($class eq "05") {
		print("= ",&hex2asc($address));
	}
	elsif($class eq "00") {
		print("Null Address");
	}
	else {
		print(&showhex($address));
	}	
	print("\n");
}

sub argument {
	local(@arglist) = @_;

	# Check Arguments
	if($_[0] eq "") {
		print("No file name provided.\n");
		die("Usage: dring [filename]\n");
	}

	return($_[0]); # Return filename
}

sub checkparseflags {
	local($string)	= $_[0];
	local($retval)	= "";

	if(index($line,$PAPFLAG)     != -1 ||
           index($line,$IPCPFLAG)    != -1 ||
           index($line,$LCPFLAG)     != -1 ||
           index($line,$CHAPFLAG)    != -1 ||
           index($line,$UNFLAG)      != -1 ||
           index($line,$CRCFLAG)     != -1 ||
           index($line,$IPXFLAG) ) {
		$retval = 1;
	}
	else {
		$retval = 0;
	}
	return($retval);
}


sub getnextline {
# Gets next line of input 
	if($IMP eq "file") {
		return(<READ>);
	}
	elsif($IMP eq "cgi") {
		$retvall = $cgi[$ptr];
		$ptr++;
		return($retvall);		
	}
}

# ReadParse
# Reads in GET or POST data, converts it to unescaped text, and puts
# one key=value in each member of the list "@in"
# Also creates key/value pairs in %in, using '\0' to separate multiple
# selections

# If a variable-glob parameter (e.g., *cgi_input) is passed to ReadParse,
# information is stored there, rather than in $in, @in, and %in.

sub ReadParse {
    local (*in) = @_ if @_;


  local ($i, $loc, $key, $val);

  # Read in text
  if ($ENV{'REQUEST_METHOD'} eq "GET") {
    $in = $ENV{'QUERY_STRING'};
  } elsif ($ENV{'REQUEST_METHOD'} eq "POST") {
    read(STDIN,$in,$ENV{'CONTENT_LENGTH'});
  }

  @in = split(/&/,$in);

  foreach $i (0 .. $#in) {
    # Convert plus's to spaces
    $in[$i] =~ s/\+/ /g;

    # Split into key and value.
    ($key, $val) = split(/=/,$in[$i],2); # splits on the first =.
    # Convert %XX from hex numbers to alphanumeric
    $key =~ s/%(..)/pack("c",hex($1))/ge;
    $val =~ s/%(..)/pack("c",hex($1))/ge;

    # Associate key and value
    $in{$key} .= "\0" if (defined($in{$key})); # \0 is the multiple separator
    $in{$key} .= $val;

  }

  return 1; # just for fun
}

# PrintHeader
# Returns the magic line which tells WWW that we're an HTML document

sub PrintHeader {
  return "Content-type: text/html\n\n";
}

sub mailsetup {
	local($to)		= $_[0];
	local($mailprog)	= '/usr/sbin/sendmail'; # location of mail program
	local($from)		= "";
	local($subject)		= "Decoded PPP Trace";
	
	open (MAIL, "|$mailprog $to") || die "Can't open $mailprog!\n";
	print MAIL "From: $from\n";
	print MAIL "Reply-To: $from)\n";
	print MAIL "To: $to'}\n";
	print MAIL "Subject: $subject\n\n";
	print MAIL "\n\n";
}
