199 lines
5.0 KiB
PHP
199 lines
5.0 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace App\Classes\BBS\Control;
|
||
|
|
||
|
use App\Classes\BBS\Control;
|
||
|
|
||
|
/**
|
||
|
* Class Telnet
|
||
|
*
|
||
|
* This class looks after any telnet session commands
|
||
|
*
|
||
|
* TELNET http://pcmicro.com/netfoss/telnet.html
|
||
|
*
|
||
|
* @package App\Classes\Control
|
||
|
*/
|
||
|
final class Telnet extends Control
|
||
|
{
|
||
|
protected const LOGKEY = 'CT-';
|
||
|
|
||
|
/** @var int Data Byte */
|
||
|
public const TCP_IAC = 0xff;
|
||
|
/** @var int Indicates the demand that the other party stop performing, or confirmation that you are no
|
||
|
longer expecting the other party to perform, the indicated option */
|
||
|
public const TCP_DONT = 0xfe;
|
||
|
/** @var int Indicates the request that the other party perform, or confirmation that you are expecting
|
||
|
the other party to perform, the indicated option. */
|
||
|
public const TCP_DO = 0xfd;
|
||
|
/** @var int Indicates the refusal to perform, or continue performing, the indicated option. */
|
||
|
public const TCP_WONT = 0xfc;
|
||
|
/** @var int Indicates the desire to begin performing, or confirmation that you are now performing, the indicated option. */
|
||
|
public const TCP_WILL = 0xfb;
|
||
|
/** @var int Indicates that what follows is sub-negotiation of the indicated option. */
|
||
|
public const TCP_SB = 0xfa;
|
||
|
|
||
|
/** @var int The GA signal. */
|
||
|
public const TCP_GA = 0xf9;
|
||
|
/** @var int Erase Line. */
|
||
|
public const TCP_EL = 0xf8;
|
||
|
/** @var int Erase character. */
|
||
|
public const TCP_EC = 0xf7;
|
||
|
/** @var int Are you there? */
|
||
|
public const TCP_AYT = 0xf6;
|
||
|
/** @var int About output */
|
||
|
public const TCP_AO = 0xf5;
|
||
|
/** @var int Interrupt Process. */
|
||
|
public const TCP_IP = 0xf4;
|
||
|
/** @var int Break. */
|
||
|
public const TCP_BREAK = 0xf3;
|
||
|
/** @var int The data stream portion of a Synch. This should always be accompanied by a TCP Urgent notification. */
|
||
|
public const TCP_DM = 0xf2;
|
||
|
/** @var int No operation. */
|
||
|
public const TCP_NOPT = 0xf1;
|
||
|
/** @var int End of sub-negotiation parameters. */
|
||
|
public const TCP_SE = 0xf0;
|
||
|
|
||
|
public const TCP_BINARY = 0x00;
|
||
|
public const TCP_OPT_ECHO = 0x01;
|
||
|
public const TCP_OPT_SUP_GOAHEAD = 0x03;
|
||
|
public const TCP_OPT_TERMTYPE = 0x18;
|
||
|
public const TCP_OPT_WINDOWSIZE = 0x1f;
|
||
|
public const TCP_OPT_LINEMODE = 0x22;
|
||
|
|
||
|
private bool $option = FALSE;
|
||
|
private string $note;
|
||
|
private string $terminal = '';
|
||
|
|
||
|
public static function send_iac($key): string
|
||
|
{
|
||
|
$send = chr(self::TCP_IAC);
|
||
|
|
||
|
switch ($key) {
|
||
|
case 'are_you_there':
|
||
|
$send .= chr(self::TCP_AYT);
|
||
|
break;
|
||
|
|
||
|
case 'do_echo':
|
||
|
$send .= chr(self::TCP_DO).chr(self::TCP_OPT_ECHO);
|
||
|
break;
|
||
|
case 'dont_echo':
|
||
|
$send .= chr(self::TCP_DONT).chr(self::TCP_OPT_ECHO);
|
||
|
break;
|
||
|
case 'will_echo':
|
||
|
$send .= chr(self::TCP_WILL).chr(self::TCP_OPT_ECHO);
|
||
|
break;
|
||
|
case 'wont_echo':
|
||
|
$send .= chr(self::TCP_WONT).chr(self::TCP_OPT_ECHO);
|
||
|
break;
|
||
|
|
||
|
case 'do_opt_termtype':
|
||
|
$send .= chr(self::TCP_DO).chr(self::TCP_OPT_TERMTYPE);
|
||
|
break;
|
||
|
|
||
|
case 'do_suppress_goahead':
|
||
|
$send .= chr(self::TCP_DO).chr(self::TCP_OPT_SUP_GOAHEAD);
|
||
|
break;
|
||
|
|
||
|
case 'sn_end':
|
||
|
$send .= chr(self::TCP_SE);
|
||
|
break;
|
||
|
|
||
|
case 'sn_start':
|
||
|
$send .= chr(self::TCP_SB);
|
||
|
break;
|
||
|
|
||
|
case 'wont_linemode':
|
||
|
$send .= chr(self::TCP_WONT).chr(self::TCP_OPT_LINEMODE);
|
||
|
break;
|
||
|
|
||
|
case 'will_xmit_binary':
|
||
|
$send .= chr(self::TCP_WILL).chr(self::TCP_BINARY);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
throw new \Exception(sprintf('%s:! Unknown key: %s',$key));
|
||
|
}
|
||
|
|
||
|
return $send;
|
||
|
}
|
||
|
|
||
|
public function handle(string $read): string
|
||
|
{
|
||
|
$this->so->log('debug',sprintf('%s:+ Session Char [%02x] (%c)',self::LOGKEY,ord($read),$read),['complete'=>$this->complete,'option'=>$this->option]);
|
||
|
|
||
|
switch (ord($read)) {
|
||
|
// Command being sent.
|
||
|
case self::TCP_IAC:
|
||
|
$this->complete = FALSE;
|
||
|
$this->note = 'IAC ';
|
||
|
|
||
|
break;
|
||
|
|
||
|
case self::TCP_SB:
|
||
|
$this->option = TRUE;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case self::TCP_SE:
|
||
|
$this->option = FALSE;
|
||
|
$this->complete = TRUE;
|
||
|
$this->so->log('debug',sprintf('%s:%% Session Terminal: %s',self::LOGKEY,$this->terminal));
|
||
|
|
||
|
break;
|
||
|
|
||
|
case self::TCP_DO:
|
||
|
$this->note .= 'DO ';
|
||
|
|
||
|
break;
|
||
|
|
||
|
case self::TCP_WILL:
|
||
|
$this->note .= 'WILL ';
|
||
|
|
||
|
break;
|
||
|
|
||
|
case self::TCP_WONT:
|
||
|
$this->note .= 'WONT ';
|
||
|
|
||
|
break;
|
||
|
|
||
|
case self::TCP_OPT_TERMTYPE:
|
||
|
|
||
|
break;
|
||
|
|
||
|
case self::TCP_OPT_ECHO:
|
||
|
$this->note .= 'ECHO';
|
||
|
$this->complete = TRUE;
|
||
|
|
||
|
$this->so->log('debug',sprintf('%s:%% Session Note: [%s]',self::LOGKEY,$this->note));
|
||
|
|
||
|
break;
|
||
|
|
||
|
case self::TCP_OPT_SUP_GOAHEAD:
|
||
|
$this->note .= 'SUPPRESS GO AHEAD';
|
||
|
$this->complete = TRUE;
|
||
|
|
||
|
$this->so->log('debug',sprintf('%s:%% Session Note: [%s]',self::LOGKEY,$this->note));
|
||
|
|
||
|
break;
|
||
|
|
||
|
case self::TCP_OPT_WINDOWSIZE:
|
||
|
$this->note .= 'WINDOWSIZE';
|
||
|
$this->complete = TRUE;
|
||
|
|
||
|
$this->so->log('debug',sprintf('%s:%% Session Note: [%s]',self::LOGKEY,$this->note));
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
if ($this->option && $read)
|
||
|
$this->terminal .= $read;
|
||
|
else
|
||
|
$this->so->log('debug',sprintf('%s:= Unhandled char in session_init: [%02x] (%c)',self::LOGKEY,ord($read),$read));
|
||
|
}
|
||
|
|
||
|
if ($this->complete)
|
||
|
$this->so->log('debug',sprintf('%s:= TELNET control COMPLETE',self::LOGKEY));
|
||
|
|
||
|
return '';
|
||
|
}
|
||
|
}
|