From ee30ef92c397ada8cfd8e709b072571e78925f27 Mon Sep 17 00:00:00 2001 From: Deon George Date: Sat, 24 Jul 2021 00:53:35 +1000 Subject: [PATCH] New algorithm for calculating packet name, EMSI/BINKP inbound processing tested, Netmail rejection and intransit processing --- app/Classes/FTN/Message.php | 2 +- app/Classes/FTN/Packet.php | 2 +- app/Classes/FTN/Process.php | 10 ++-- app/Classes/FTN/Process/Ping.php | 7 +-- app/Classes/File/Send.php | 4 +- app/Classes/Protocol/Binkp.php | 30 ++++++----- app/Classes/Protocol/Zmodem.php | 2 +- app/Classes/Sock/SocketClient.php | 24 --------- app/Console/Commands/BinkpSend.php | 2 +- app/Console/Commands/ZmodemSend.php | 2 +- app/Jobs/ProcessPacket.php | 69 ++++++++++++++++++++++-- app/helpers.php | 84 +++++++++++++++++++++++++++++ 12 files changed, 184 insertions(+), 54 deletions(-) diff --git a/app/Classes/FTN/Message.php b/app/Classes/FTN/Message.php index 4d3ce0d..23d7759 100644 --- a/app/Classes/FTN/Message.php +++ b/app/Classes/FTN/Message.php @@ -163,7 +163,7 @@ class Message extends FTNBase * * @param string $msg * @param Domain|null $domain - * @return static + * @return Message * @throws InvalidPacketException */ public static function parseMessage(string $msg,Domain $domain=NULL): self diff --git a/app/Classes/FTN/Packet.php b/app/Classes/FTN/Packet.php index ebdc9c7..c711a9c 100644 --- a/app/Classes/FTN/Packet.php +++ b/app/Classes/FTN/Packet.php @@ -62,7 +62,7 @@ class Packet extends FTNBase { $this->messages = collect(); $this->domain = NULL; - $this->name = sprintf('%08x',Carbon::now()->timestamp); + $this->name = sprintf('%08x',timew()); // If we are creating an outbound packet, we need to set our header if ($o) diff --git a/app/Classes/FTN/Process.php b/app/Classes/FTN/Process.php index 3f943b5..7f48f3a 100644 --- a/app/Classes/FTN/Process.php +++ b/app/Classes/FTN/Process.php @@ -19,7 +19,7 @@ abstract class Process /** * This function will format text to static::MSG_WIDTH, as well as adding the logo. */ - protected static function format_msg(string $text): string + public static function format_msg(string $text,array $logo = []): string { $msg = utf8_decode(join("\r",static::msg_header()))."\r"; $c = 0; @@ -29,8 +29,8 @@ abstract class Process $ll = ''; // Add our logo - if ($cuser_from,$msg->fftn)); - $reply = sprintf("Your ping was received here on %s and it took %s to get here.\r", + $reply = sprintf("Your ping was received here on %s and it looks like you sent it on %s. If that is correct, then it took %s to get here.\r", + $msg->date->toDateTimeString(), Carbon::now()->toDateTimeString(), $msg->date->diffForHumans(['parts'=>3,'syntax'=>CarbonInterface::DIFF_ABSOLUTE]) ); @@ -46,7 +47,7 @@ final class Ping extends Process $o->subject = 'Ping Reply'; $o->fftn_id = ($x=$msg->tftn_o) ? $x->id : NULL; $o->tftn_id = ($x=$msg->fftn_o) ? $x->id : NULL; - $o->msg = static::format_msg($reply); + $o->msg = static::format_msg($reply,self::$logo); $o->reply = $msg->msgid; $o->tagline = 'My ping pong opponent was not happy with my serve. He kept returning it.'; diff --git a/app/Classes/File/Send.php b/app/Classes/File/Send.php index 3eada53..f3aa205 100644 --- a/app/Classes/File/Send.php +++ b/app/Classes/File/Send.php @@ -120,6 +120,7 @@ final class Send extends Item * * @param string $file * @throws Exception + * @todo Catch if we add the same file twice */ public function add(string $file): void { @@ -277,7 +278,8 @@ final class Send extends Item throw new Exception('No file open for seek'); if ($this->sending instanceof Mail) { - $rc = ($pos < $this->size) ? $pos : $this->size; + $pos = ($pos < $this->size) ? $pos : $this->size; + $rc = TRUE; } else { $rc = (fseek($this->f,$pos,SEEK_SET) === 0); diff --git a/app/Classes/Protocol/Binkp.php b/app/Classes/Protocol/Binkp.php index 2adde5f..61691fe 100644 --- a/app/Classes/Protocol/Binkp.php +++ b/app/Classes/Protocol/Binkp.php @@ -623,14 +623,6 @@ final class Binkp extends BaseProtocol // @todo lock nodes $this->node->ftn = $o; - // Add our mail to the queue if we have authenticated - if ($this->node->aka_authed) - foreach ($this->node->aka_remote as $ao) { - $this->send->mail($ao); - } - - Log::info(sprintf('%s: = Node has [%lu] mail and [%lu] files - [%lu] items',__METHOD__,$this->send->mail_size,$this->send->file_size,$this->send->total_count)); - $rc = $this->node->aka_num; } @@ -653,8 +645,6 @@ final class Binkp extends BaseProtocol return 0; } - $this->msgs(self::BPM_NUL,sprintf('TRF %lu %lu',$this->send->mail_size,$this->send->file_size)); - if ($this->md_challenge) { $this->msgs(self::BPM_PWD,sprintf('CRAM-MD5-%s',$this->node->get_md5chal($this->md_challenge))); @@ -697,6 +687,8 @@ final class Binkp extends BaseProtocol } /** + * We received EOB from the remote. + * * @throws Exception */ private function M_eob(string $buf): int @@ -718,7 +710,7 @@ final class Binkp extends BaseProtocol } if ($this->send->total_count) - $this->sessionClear(self::SE_NOFILES); + $this->sessionClear(self::SE_NOFILES|self::SE_SENTEOB); } return 1; @@ -1066,6 +1058,14 @@ final class Binkp extends BaseProtocol } } + // Add our mail to the queue if we have authenticated + if ($this->node->aka_authed) + foreach ($this->node->aka_remote as $ao) { + $this->send->mail($ao); + } + + $this->msgs(self::BPM_NUL,sprintf('TRF %lu %lu',$this->send->mail_size,$this->send->file_size)); + Log::debug(sprintf('%s: = End',__METHOD__)); return $this->binkp_hsdone(); } @@ -1142,6 +1142,12 @@ final class Binkp extends BaseProtocol if (strlen($tmp)) $this->msgs(self::BPM_NUL,sprintf('OPT%s',$tmp)); + // Add our mail to the queue if we have authenticated + if ($this->node->aka_authed) + foreach ($this->node->aka_remote as $ao) { + $this->send->mail($ao); + } + $this->msgs(self::BPM_NUL,sprintf('TRF %lu %lu',$this->send->mail_size,$this->send->file_size)); $this->msgs(self::BPM_OK,sprintf('%ssecure',$have_pwd ? '' : 'non-')); @@ -1235,7 +1241,7 @@ final class Binkp extends BaseProtocol if ($rd && ! $this->binkp_recv()) break; - if (($this->mqueue->count() || $wd) && ! $this->binkp_send()) + if (($this->mqueue->count() || $wd) && ! $this->binkp_send() && (! $this->send->total_count)) break; } diff --git a/app/Classes/Protocol/Zmodem.php b/app/Classes/Protocol/Zmodem.php index f7715e7..7d04da5 100644 --- a/app/Classes/Protocol/Zmodem.php +++ b/app/Classes/Protocol/Zmodem.php @@ -206,7 +206,7 @@ final class Zmodem extends Protocol implements CRCInterface,ZmodemInterface if (! parent::onConnect($client)) { $this->session(self::SESSION_ZMODEM,$client); $this->client->close(); - Log::info(sprintf('%s: = End - Connection closed [%s]',__METHOD__,$client->getAddress())); + Log::info(sprintf('%s: = End - Connection closed [%s]',__METHOD__,$client->address_remote)); } return NULL; diff --git a/app/Classes/Sock/SocketClient.php b/app/Classes/Sock/SocketClient.php index c15aeea..8419ace 100644 --- a/app/Classes/Sock/SocketClient.php +++ b/app/Classes/Sock/SocketClient.php @@ -246,30 +246,6 @@ final class SocketClient { return new self($socket,$speed); } - /** - * Return the client's address - * - * @return string - * @todo change to __get() - * @deprecated - */ - public function getAddress(): string - { - return $this->address; - } - - /** - * Return the port in use - * - * @return int - * @todo change to __get() - * @deprecated - */ - public function getPort(): int - { - return $this->port; - } - /** * @param int $timeout * @return int diff --git a/app/Console/Commands/BinkpSend.php b/app/Console/Commands/BinkpSend.php index 9dfbb72..b984c6c 100644 --- a/app/Console/Commands/BinkpSend.php +++ b/app/Console/Commands/BinkpSend.php @@ -52,6 +52,6 @@ class BinkpSend extends Command $o = new Binkp(Setup::findOrFail(config('app.id'))); $o->session(Binkp::SESSION_BINKP,$client,$no); - Log::info(sprintf('Connection ended: %s',$client->getAddress()),['m'=>__METHOD__]); + Log::info(sprintf('Connection ended: %s',$client->address_remote),['m'=>__METHOD__]); } } diff --git a/app/Console/Commands/ZmodemSend.php b/app/Console/Commands/ZmodemSend.php index 674fe36..990cf63 100644 --- a/app/Console/Commands/ZmodemSend.php +++ b/app/Console/Commands/ZmodemSend.php @@ -40,6 +40,6 @@ class ZmodemSend extends Command $o = new ZmodemClass; $o->session(Protocol::SESSION_ZMODEM,$client); - Log::info(sprintf('Connection ended: %s',$client->getAddress()),['m'=>__METHOD__]); + Log::info(sprintf('Connection ended: %s',$client->address_remote),['m'=>__METHOD__]); } } \ No newline at end of file diff --git a/app/Jobs/ProcessPacket.php b/app/Jobs/ProcessPacket.php index 755bf6c..6bb88f1 100644 --- a/app/Jobs/ProcessPacket.php +++ b/app/Jobs/ProcessPacket.php @@ -7,9 +7,10 @@ use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; +use Illuminate\Support\Facades\Log; -use App\Classes\FTN\Message; -use App\Models\Setup; +use App\Classes\FTN\{Message,Process}; +use App\Models\{Netmail,Setup}; class ProcessPacket implements ShouldQueue { @@ -34,6 +35,8 @@ class ProcessPacket implements ShouldQueue // If we are a netmail if ($this->msg->isNetmail()) { // @todo Enable checks to reject old messages + // @todo Enable checks to reject duplicate + // @todo Enable checks to see if this is a file request or file send // Determine if the message is to this system, or in transit if ($ftns->search(function($item) { return $this->msg->tftn == $item->ftn; }) !== FALSE) { @@ -51,16 +54,74 @@ class ProcessPacket implements ShouldQueue // If not processed, no users here! if (! $processed) { - dump('message not processed, no users here'); + $reject = [ + 'ÚÄ¿ÚÄ¿ ÂÚÄ¿ÚÄ¿Ú¿', + '³ ³ÄÙ ³³ÄÙ³ ³ ', + 'Á ÀÄÙÀÄÙÀÄÙÀÄÙ Á ' + ]; + + Log::info(sprintf('Netmail to the Hub from (%s) [%s] but no users here.',$this->msg->user_from,$this->msg->fftn)); + + $reply = "Your Netmail was not processed by the hub and cannot be delivered to anybody (as there are no users here).\r"; + + $reply .= "\r"; + $reply .= "\r"; + $reply .= "This is your original message:\r"; + $reply .= "------------------------------ BEING MESSAGE ------------------------------\r"; + $reply .= sprintf("TO: %s\r",$this->msg->user_to); + $reply .= sprintf("SUBJECT: %s\r",$this->msg->subject); + $reply .= $this->msg->message; + $reply .= "------------------------------ CONTROL LINES ------------------------------\r"; + $reply .= sprintf("DATE: %s\r",$this->msg->date->format('Y-m-d H:i:s')); + $reply .= sprintf("MSGID: %s\r",$this->msg->msgid); + + foreach ($this->msg->kludge as $k=>$v) + $reply .= sprintf("@%s: %s\n",strtoupper($k),$v); + foreach ($this->msg->via as $via) + $reply .= sprintf("VIA: %s\n",$via); + + $reply .= "------------------------------ END MESSAGE ------------------------------\r"; + + $o = new Netmail(); + $o->to = $this->msg->user_from; + $o->from = Setup::PRODUCT_NAME; + $o->subject = 'Message Undeliverable - '.$this->msg->msgid; + $o->fftn_id = ($x=$this->msg->tftn_o) ? $x->id : NULL; + $o->tftn_id = ($x=$this->msg->fftn_o) ? $x->id : NULL; + $o->msg = Process::format_msg($reply,$reject); + $o->reply = $this->msg->msgid; + + $o->tagline = 'Do you think it was fate which brought us together? Nah, bad luck :('; + $o->tearline = sprintf('--- %s (%s)',Setup::PRODUCT_NAME,(new Setup)->version); + $o->save(); } // If in transit, store for collection } else { + Log::info(sprintf('Netmail [%s] in transit to (%s) [%s] from (%s) [%s].', + $this->msg->msgid, + $this->msg->user_to,$this->msg->tftn, + $this->msg->user_from,$this->msg->fftn, + )); + // @todo Check if the message is to a system we know about // @todo In transit loop checking // @todo In transit TRACE response - dump('netmail in transit'); + $o = new Netmail(); + $o->to = $this->msg->user_to; + $o->from = $this->msg->user_from; + $o->subject = $this->msg->subject; + $o->fftn_id = ($x=$this->msg->fftn_o) ? $x->id : NULL; + $o->tftn_id = ($x=$this->msg->tftn_o) ? $x->id : NULL; + $o->msg = $this->msg->message; + $o->tearline = $this->msg->tearline; + $o->origin = $this->msg->origin; + $o->kluge = json_encode($this->msg->kludge); + $o->flags = $this->msg->flags; + $o->cost = $this->msg->cost; + + $o->save(); } // Else we are echomail diff --git a/app/helpers.php b/app/helpers.php index 01146b3..bd0390d 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -75,4 +75,88 @@ if (! function_exists('hexstr')) { return $x; } +} + +if (! function_exists('timew')) { + /** + * Convert a time into an 32 bit value. This is primarily used to create 8 character hex filenames that + * are unique using 1/10th second precision. + * + * Time is: + * + 02 bits unused + * + 04 bits Month + * + 05 bits Day + * + 05 bits Hour + * + 06 bits Min + * + 06 bits Sec + * + 04 bits 10th Sec + * = 32 (2 bits free) + * + * @param \Carbon\Carbon|null $time + * @return int + */ + function timew(\Carbon\Carbon $time=NULL): int + { + static $delay = 0; + + // If we are not passed a time, we'll use the time now + if (! $time) { + // In case we are called twice, we'll delay 1/10th second so we have a unique result. + if (\Carbon\Carbon::now()->getPreciseTimestamp(1) == $delay) + usleep(100000); + + $time = \Carbon\Carbon::now(); + } + + $delay = $time->getPreciseTimestamp(1); + + $t = ($time->month & 0xf) << 5; + $t = ($t | ($time->day & 0x1f)) << 5; + $t = ($t | ($time->hour & 0x1f)) << 6; + $t = ($t | ($time->minute & 0x3f)) << 6; + $t = ($t | ($time->second & 0x3f)); + + return hexdec(sprintf('%07x%1x',$t,(round($time->milli/100) & 0x7f))); + } +} + +if (! function_exists('dwtime')) { + /** + * Convert a 40 bit integer into a time. + * @see timew() + * + * @param int $time + * @param int|null $year + * @return \Carbon\Carbon + */ + function wtime(int $time,int $year=NULL): \Carbon\Carbon + { + if (! $year) + $year = \Carbon\Carbon::now()->year; + + // Does the time have milli seconds? + if ($time > pow(2,26)-1) { + $milli = ($time & 0xf); + $time = $time >> 4; + + } else { + $milli = 0; + } + + $sec = ($time & 0x3f); + $time = $time >> 6; + + $min = ($time & 0x3f); + $time = $time >> 6; + + $hr = ($time & 0x1f); + $time = $time >> 5; + + $day = ($time & 0x1f); + $time = $time >> 5; + + $month = ($time & 0x1f); + + return \Carbon\Carbon::create($year,$month,$day,$hr,$min,$sec+$milli/10); + } } \ No newline at end of file