linbpq/DRATS.c

674 lines
14 KiB
C
Raw Permalink Normal View History

2022-08-28 09:35:46 +01:00
/*
2022-11-14 14:02:28 +00:00
Copyright 2001-2022 John Wiseman G8BPQ
2022-08-28 09:35:46 +01:00
This file is part of LinBPQ/BPQ32.
LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
LinBPQ/BPQ32 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
*/
// DRATS support code
#define _CRT_SECURE_NO_DEPRECATE
#include "CHeaders.h"
#include "bpq32.h"
#include "telnetserver.h"
/*
The header is the first 23 bytes of of the frame. The payload is the rest of the frame.
Byte 1 is a "magic" number. It is 0xDD if the payload is zlib compressed before being yencoded.
Bytes 2 and 3 is a 16 bit sequence number.
Byte 4 is a session number.
Byte 5 is a type. Still don't know the types.
Bytes 6 and 7, 16 bits, is the checksum of the data after any compression.
Bytes 8 and 9, 16 bits, is the length of the data.
bytes 10-18, 8 bits, are the source call sign.
bytes 19-25, 8 bits, are the destination call sign.
If a call sign is less than 8 characters, it is padded to fill the space with the "~" tilde character.
Frame types: (Some from sessions/chat.py)
0 - T_DEF
1 - T_PING_REQ - Ping request (Used in Test frame)
2 - T_PING_RSP - Ping response
3 - T_PING_ERS - Ping error status?
4 - T_STATUS - Status frame
5 - File Transfer
8 - Used in Test frame
254 - Apparently a warm up frame.
T_DEF = 0
T_PNG_REQ = 1
T_PNG_RSP = 2
T_PNG_ERQ = 3
T_PNG_ERS = 4
T_STATUS = 5
For a station status frame, the first byte of the message is an ASCII station status value.
'0' - Unknown
'1' - Online
'2' - Unattended
'9' - Offline
*/
#define T_DEF 0
#define T_PNG_REQ 1
#define T_PNG_RSP 2
#define T_PNG_ERQ 3
#define T_PNG_ERS 4
#define T_STATUS 5
#pragma pack(1)
// shorts are big-endian
struct DRATSHeader
{
unsigned char Magic;
unsigned short Seq;
unsigned char Sessno;
unsigned char Type;
unsigned short CheckSum;
unsigned short Length;
char CallFrom[8];
char CallTo[8];
unsigned char Message[2048];
};
#pragma pack()
struct DRATSSession
{
struct ConnectionInfo * sockptr;
unsigned int Seq;
unsigned int Sessno;
char CallFrom[8];
char CallTo[8];
int Stream; // BPQ Stream
int StreamState;
struct DRATSQueue * Queue;
struct DRATSSession * Next;
};
struct DRATSQueue
{
// Queue of messages to be sent to node from background (ie not under semaphore)
int Stream;
int Len;
unsigned char * Msg;
struct DRATSQueue * Next;
};
struct DRATSSession * DRATSSessions = NULL;
char peer0_2[] = { /* Packet 17 */
0x5b, 0x53, 0x4f, 0x42, 0x5d, 0xdd, 0x3d, 0x40,
0x3d, 0x40, 0x01, 0x05, 0x45, 0x78, 0x3d, 0x40,
0x18, 0x47, 0x38, 0x42, 0x50, 0x51, 0x7e, 0x7e,
0x7e, 0x43, 0x51, 0x43, 0x51, 0x43, 0x51, 0x7e,
0x7e, 0x78, 0xda, 0x33, 0xf4, 0xcf, 0xcb, 0xc9,
0xcc, 0x4b, 0x55, 0xd0, 0x70, 0xd1, 0x0d, 0x72,
0x0c, 0x09, 0xd6, 0x04, 0x3d, 0x40, 0x2a, 0x8c,
0x04, 0xb3, 0x5b, 0x45, 0x4f, 0x42, 0x5d };
void processDRATSFrame(unsigned char * Message, int Len, struct ConnectionInfo * sockptr);
int testDRATS()
{
// processDRATSFrame(peer0_1, sizeof(peer0_1), 0);
// processDRATSFrame(peer0_2, sizeof(peer0_2), 0);
// processDRATSFrame(peer1_1, sizeof(peer1_1), 0);
// processDRATSFrame(peer1_20, sizeof(peer1_20));
// processDRATSFrame(peer0_20, sizeof(peer0_20));
// processDRATSFrame(peer1_21, sizeof(peer1_21));
return 0;
}
extern char pgm[256];
extern char TextVerstring[50];
int HeaderLen = offsetof(struct DRATSHeader, Message);
int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen);
int dratscrc(unsigned char *ptr, int count);
int FindFreeStreamNoSem();
void sendDRATSFrame(struct ConnectionInfo * sockptr, struct DRATSHeader * Header);
int yEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned);
int AllocateDRATSStream(struct DRATSSession * Sess)
{
int Stream;
strcpy(pgm, "DRATS");
Stream = FindFreeStreamNoSem();
strcpy(pgm, "bpq32.exe");
if (Stream == 255) return 0;
if (memcmp(Sess->CallTo, "NODE", 6) == 0)
{
// Just connect to command level on switch
}
return Stream;
}
void ProcessDRATSPayload(struct DRATSHeader * Header, struct DRATSSession * Sess)
{
struct DRATSQueue * QEntry;
BPQVECSTRUC * HOST;
if (Sess->Stream == 0)
{
Sess->Stream = AllocateDRATSStream(Sess);
}
if (Sess->StreamState == 0)
{
unsigned char AXCall[10];
Connect(Sess->Stream); // Connect
ConvToAX25(Sess->CallFrom, AXCall);
ChangeSessionCallsign(Sess->Stream, AXCall); // Prevent triggering incoming connect code
// Clear State Changed bits (cant use SessionState under semaphore)
HOST = &BPQHOSTVECTOR[Sess->Stream -1]; // API counts from 1
HOST->HOSTFLAGS &= 0xFC; // Clear Change Bits
Sess->StreamState = 1;
}
strcat(Header->Message, "\r");
// Need to Queue to Background as we can't use SendMsg under semaphore
QEntry = zalloc(sizeof(struct DRATSQueue));
QEntry->Len = strlen(Header->Message);
QEntry->Msg = malloc(QEntry->Len);
memcpy(QEntry->Msg, Header->Message, QEntry->Len);
// Add to queue
if (Sess->Queue)
{
struct DRATSQueue * End = Sess->Queue;
// Add on end
while (End->Next)
End = End->Next;
End->Next = QEntry;
}
else
Sess->Queue = QEntry;
}
// Called under semaphore
void processDRATSFrame(unsigned char * Message, int Len, struct ConnectionInfo * sockptr)
{
unsigned char * Payload;
unsigned char * ptr;
unsigned char dest[2048];
struct DRATSHeader * Header;
int outLen;
struct DRATSSession * Sess = DRATSSessions;
unsigned short crc, savecrc;
char CallFrom[10] = "";
char CallTo[10] = "";
Message[Len] = 0;
Debugprintf(Message);
Payload = strstr(Message, "[SOB]");
if (Payload == 0)
return;
ptr = strstr(Message, "[EOB]");
if (ptr == 0)
return;
ptr[0] = 0;
Payload += 5;
Header = (struct DRATSHeader *)Payload;
// Undo = transparency
ptr = Payload;
while (ptr = strchr(ptr, '='))
{
memmove(ptr, ptr + 1, Len);
ptr[0] -= 64;
ptr++;
}
// Check CRC
savecrc = htons(Header->CheckSum);
Header->CheckSum = 0;
crc = dratscrc(Payload, htons(Header->Length) + HeaderLen);
if (crc != savecrc)
{
Debugprintf(" DRARS CRC Error %x %x", crc, savecrc); // Good CRC
return;
}
Header->Length = htons(Header->Length); // convert to machine order
if (Header->Magic == 0xdd) // Zlib compressed
{
doinflate(Header->Message, dest, Header->Length, 2048, &outLen);
memcpy(Header->Message, dest, outLen + 1);
Header->Length = outLen;
}
Debugprintf(Header->Message);
// Look for a matching From/To/Session
memcpy(CallFrom, Header->CallFrom, 8);
memcpy(CallTo, Header->CallTo, 8);
strlop(CallFrom, '~');
strlop(CallTo, '~');
if (Header->Type == T_STATUS)
{
// Status frame ?? What to do with it ??
return;
}
if (Header->Type == T_PNG_REQ)
{
// "Ping Request"
// if to "NODE" reply to it
if (strcmp(CallTo, "NODE") == 0)
{
// Reuse incoming message
strcpy(Header->CallFrom, CallTo);
strcpy(Header->CallTo, CallFrom);
Header->Type = T_PNG_RSP;
Header->Length = sprintf(Header->Message, "Running BPQ32 Version %s", TextVerstring);
sendDRATSFrame(sockptr, Header);
return;
}
// Not to us - do we route it ??
return;
}
if (Header->Type == T_PNG_RSP)
{
// Reponse is PNG_RSP then Status - 1Online (D-RATS)
// "Running D-RATS 0.3.9 (Windows 8->10 (6, 2, 9200, 2, ''))"
// "Running D-RATS 0.3.10 beta 4 (Linux - Raspbian GNU/Linux 9)"
return;
}
if (Header->Type != T_DEF)
{
return;
}
// ?? Normal Data
if (strcmp(CallTo, "NODE") != 0)
{
// Not not Node - should we route it ??
return;
}
while (Sess)
{
if (Sess->Sessno == Header->Sessno && memcmp(Sess->CallFrom, CallFrom, 8) == 0
&& memcmp(Sess->CallTo, CallTo, 8) == 0 && Sess->sockptr == sockptr)
{
ProcessDRATSPayload(Header, Sess);
return;
}
Sess = Sess->Next;
}
// Allocate a new one
Sess = zalloc(sizeof(struct DRATSSession));
Sess->Sessno = Header->Sessno;
memcpy(Sess->CallFrom, CallFrom, 8);
memcpy(Sess->CallTo, CallTo, 8);
Sess->sockptr = sockptr;
if (DRATSSessions)
{
// Add to front of Chain
Sess->Next = DRATSSessions;
}
DRATSSessions = Sess;
ProcessDRATSPayload(Header, Sess);
return;
}
void DRATSPoll()
{
struct DRATSSession * Sess = DRATSSessions;
int Stream, state, change;
int count;
struct DRATSHeader Header;
struct DRATSQueue * QEntry;
struct DRATSQueue * Save;
while (Sess)
{
Stream = Sess->Stream;
SessionState(Stream, &state, &change);
if (change == 1)
{
if (state == 1)
{
// Connected - do we need anything ??
}
else
{
// Send a disconnected message
char From[10] = "~~~~~~~~~";
char To[10] = "~~~~~~~~~";
Sess->StreamState = 0;
Header.Length = sprintf(Header.Message, "*** Disconnected from Node");
memcpy(To, Sess->CallFrom, strlen(Sess->CallFrom));
memcpy(From, Sess->CallTo, strlen(Sess->CallTo));
memcpy(Header.CallFrom, From, 8);
memcpy(Header.CallTo, To, 8);
Header.Magic = 0x22;
Header.Type = 0;
Header.Seq = 0;
Header.Sessno = Sess->Sessno;
sendDRATSFrame(Sess->sockptr, &Header);
}
}
do
{
int Len;
GetMsg(Stream, (char *)Header.Message, &Len, &count);
Header.Length = Len;
if (Header.Length)
{
char From[10] = "~~~~~~~~~";
char To[10] = "~~~~~~~~~";
memcpy(To, Sess->CallFrom, strlen(Sess->CallFrom));
memcpy(From, Sess->CallTo, strlen(Sess->CallTo));
memcpy(Header.CallFrom, From, 8);
memcpy(Header.CallTo, To, 8);
Header.Magic = 0x22;
Header.Type = 0;
Header.Seq = 0;
Header.Sessno = Sess->Sessno;
sendDRATSFrame(Sess->sockptr, &Header);
}
}
while (count > 0);
// See if anything to send to node
QEntry = Sess->Queue;
while (QEntry)
{
SendMsg(Sess->Stream, QEntry->Msg, QEntry->Len);
Save = QEntry;
QEntry = QEntry->Next;
free(Save->Msg);
free(Save);
}
Sess->Queue = 0;
Sess = Sess->Next;
}
}
unsigned char BANNED[] = {'=', 0x11, 0x13, 0x1A, 0xFD, 0xFE, 0xFF, 0};
void sendDRATSFrame(struct ConnectionInfo * sockptr, struct DRATSHeader * Header)
{
unsigned short crc;
int len;
unsigned char out[2048] = "[SOB]";
int packetLen = Header->Length + HeaderLen;
// Length is in host order
Header->Length = htons(Header->Length);
Header->CheckSum = 0;
crc = dratscrc((unsigned char *)Header, packetLen);
Header->CheckSum = htons(crc);
len = yEncode((unsigned char *)Header, out + 5, packetLen, BANNED);
memcpy(&out[len + 5], "[EOB]", 5);
Debugprintf(out);
send(sockptr->socket, out, len + 10, 0);
}
void DRATSConnectionLost(struct ConnectionInfo * sockptr)
{
// Disconnect any sessions, then free Stream and Sess record
struct DRATSSession * Sess = DRATSSessions;
struct DRATSSession * Save = 0;
BPQVECSTRUC * HOST;
while (Sess)
{
if (Sess->sockptr == sockptr)
{
if (Sess->StreamState == 1) // COnnected
{
Disconnect(Sess->Stream);
HOST = &BPQHOSTVECTOR[Sess->Stream -1]; // API counts from 1
HOST->HOSTFLAGS &= 0xFC; // Clear Change Bits
}
DeallocateStream(Sess->Stream);
// We must unhook from chain
if (Save)
Save->Next = Sess->Next;
else
DRATSSessions = Sess->Next;
// Should really Free any Queue, but unlikely to be any
free(Sess);
if (Save)
Sess = Save->Next;
else
Sess = DRATSSessions;
}
else
{
Save = Sess;
Sess = Sess->Next;
}
}
}
#ifdef WIN32
#define ZEXPORT __stdcall
#endif
#include "zlib.h"
int doinflate(unsigned char * source, unsigned char * dest, int Len, int destlen, int * outLen)
{
int ret;
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit(&strm);
if (ret != Z_OK)
return ret;
strm.avail_in = Len;
strm.next_in = source;
strm.avail_out = destlen;
strm.next_out = dest;
ret = inflate(&strm, Z_NO_FLUSH);
inflateEnd(&strm);
dest[strm.total_out] = 0;
*outLen = strm.total_out;
return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}
// No idea what this CRC is, but it works! (converted from DRATS python code)
int update_crc(int c, int crc)
{
int i;
int v;
for (i = 0; i < 8; i++)
{
if ((c & 0x80))
v = 1;
else
v = 0;
if (crc & 0x8000)
{
crc <<= 1;
crc += v;
crc ^= 0x1021;
}
else
{
crc <<= 1;
crc += v;
}
c <<= 1;
}
crc &= 0xFFFF;
return crc;
}
int dratscrc(unsigned char *ptr, int count)
{
int i;
int checksum = 0;
for (i = 0; i < count; i++)
checksum = update_crc(ptr[i], checksum);
checksum = update_crc(0, checksum);
checksum = update_crc(0, checksum);
return checksum;
}
#define OFFSET 64
int yEncode(unsigned char * in, unsigned char * out, int len, unsigned char * Banned)
{
unsigned char * ptr = out;
unsigned char c;
while (len--)
{
c = *(in++);
if (strchr(&Banned[0], c))
{
*(out++) = '=';
*(out++) = (c + OFFSET) & 0xFF;
}
else
*(out++) = c;
}
return (out - ptr);
}