linbpq/UIARQ.c

1837 lines
42 KiB
C

/*
Copyright 2001-2022 John Wiseman G8BPQ
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
*/
//
// Runs FLARQ-like protocol over UI Packets
//
#define _CRT_SECURE_NO_DEPRECATE
#include "CHeaders.h"
extern int (WINAPI FAR *GetModuleFileNameExPtr)();
extern int (WINAPI FAR *EnumProcessesPtr)();
#include <stdio.h>
#include <time.h>
#include "tncinfo.h"
#include "bpq32.h"
#define MAXARQ 10
#define DLE 0x10
#define SOH 1
#define STX 2
#define EOT 4
#define FEND 0xC0
#define FESC 0xDB
#define TFEND 0xDC
#define TFESC 0xDD
#define TIMESTAMP 352
#define CONTIMEOUT 1200
#define AGWHDDRLEN sizeof(struct AGWHEADER)
extern int (WINAPI FAR *GetModuleFileNameExPtr)();
//int ResetExtDriver(int num);
int SemHeldByAPI;
static int ProcessReceivedData(int bpqport);
static int ProcessLine(char * buf, int Port);
static VOID ProcessFLDigiPacket(struct TNCINFO * TNC, char * Message, int Len);
VOID ProcessFLDigiKISSPacket(struct TNCINFO * TNC, char * Message, int Len);
struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC);
static VOID SendARQData(struct TNCINFO * TNC, PMSGWITHLEN Buffer, int Stream);
VOID SendRPBeacon(struct TNCINFO * TNC);
unsigned int CalcCRC(UCHAR * ptr, int Len);
static VOID ARQTimer(struct TNCINFO * TNC);
VOID QueueAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen);
VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen);
static VOID ProcessARQStatus(struct TNCINFO * TNC, int Stream, struct ARQINFO * ARQ, char *Input);
VOID CheckFLDigiData(struct TNCINFO * TNC);
static VOID SendPacket(struct TNCINFO * TNC, struct STREAMINFO * STREAM, UCHAR * Msg, int MsgLen);
int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len);
VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port);
int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len);
VOID ProcessARQPacket(struct PORTCONTROL * PORT, MESSAGE * Buffer);
char * strlop(char * buf, char delim);
extern UCHAR BPQDirectory[];
extern char MYALIASLOPPED[10];
#define MAXMPSKPORTS 16
static BOOL GotMsg;
static HANDLE STDOUT=0;
extern int Blocksizes[10];
static char WindowTitle[] = "UIARQ";
static char ClassName[] = "UIARQSTATUS";
static int RigControlRow = 165;
static int ExtProc(int fn, int port,unsigned char * buff)
{
PMSGWITHLEN buffptr;
unsigned int txlen=0;
struct TNCINFO * TNC = TNCInfo[port];
int Stream = 0;
struct STREAMINFO * STREAM;
if (TNC == NULL)
return 0; // Port not defined
// Look for attach on any call
for (Stream = 0; Stream <= MAXARQ; Stream++)
{
STREAM = &TNC->Streams[Stream];
if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0)
{
// New Attach
int calllen;
STREAM->Attached = TRUE;
calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall);
STREAM->MyCall[calllen] = 0;
STREAM->FramesOutstanding = 0;
SetWindowText(STREAM->xIDC_STATUS, "Attached");
SetWindowText(STREAM->xIDC_MYCALL, STREAM->MyCall);
}
}
switch (fn)
{
case 7:
// 100 mS Timer.
for (Stream = 0; Stream <= MAXARQ; Stream++)
{
STREAM = &TNC->Streams[Stream];
if (STREAM->NeedDisc)
{
STREAM->NeedDisc--;
if (STREAM->NeedDisc == 0)
{
// Send the DISCONNECT
TidyClose(TNC, Stream);
}
}
}
ARQTimer(TNC);
return 0;
case 1: // poll
// See if any frames for this port
for (Stream = 0; Stream <= MAXARQ; Stream++)
{
STREAM = &TNC->Streams[Stream];
if (STREAM->Attached)
CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete);
if (STREAM->ReportDISC)
{
STREAM->ReportDISC = FALSE;
buff[4] = Stream;
return -1;
}
if (STREAM->PACTORtoBPQ_Q == 0)
{
if (STREAM->DiscWhenAllSent)
{
STREAM->DiscWhenAllSent--;
if (STREAM->DiscWhenAllSent == 0)
STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion
}
}
else
{
int datalen;
buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q);
datalen=buffptr->Len;
buff[4] = Stream;
buff[7] = 0xf0;
memcpy(&buff[8],buffptr->Data,datalen); // Data goes to +7, but we have an extra byte
datalen += (MSGHDDRLEN + 1);
PutLengthinBuffer((PDATAMESSAGE)buff, datalen);
ReleaseBuffer(buffptr);
return (1);
}
}
if (TNC->PortRecord->UI_Q)
{
struct _MESSAGE * buffptr;
int SendLen;
char Reply[256];
int UILen;
char * UIMsg;
buffptr = Q_REM(&TNC->PortRecord->UI_Q);
UILen = buffptr->LENGTH;
UILen -= 23;
UIMsg = buffptr->L2DATA;
UIMsg[UILen] = 0;
if (UILen < 129 && STREAM->Attached == FALSE) // Be sensible!
{
// >00uG8BPQ:72 TestA
SendLen = sprintf(Reply, "u%s:72 %s", TNC->NodeCall, UIMsg);
SendPacket(TNC, STREAM, Reply, SendLen);
}
ReleaseBuffer(buffptr);
}
return (0);
case 2: // send
Stream = buff[4];
STREAM = &TNC->Streams[Stream];
// txlen=(buff[6]<<8) + buff[5] - 8;
txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - (MSGHDDRLEN + 1); // 1 as no PID;
if (STREAM->Connected)
{
buffptr = GetBuff();
if (buffptr == 0) return (0); // No buffers, so ignore
buffptr->Len = txlen;
memcpy(buffptr->Data, &buff[8], txlen);
C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr);
return (0);
}
else
{
buff[8 + txlen] = 0;
_strupr(&buff[8]);
if (_memicmp(&buff[8], "D\r", 2) == 0)
{
if (STREAM->Connected)
TidyClose(TNC, buff[4]);
STREAM->ReportDISC = TRUE; // Tell Node
return 0;
}
// See if a Connect Command.
if (toupper(buff[8]) == 'C' && buff[9] == ' ' && txlen > 2) // Connect
{
char * ptr;
char * context;
struct ARQINFO * ARQ = STREAM->ARQInfo;
int SendLen;
char Reply[80];
_strupr(&buff[8]);
buff[8 + txlen] = 0;
memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State
ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent
ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received
ARQ->OurStream = Stream + 64;
ARQ->FarStream = 64; // Not yet defined
memset(STREAM->RemoteCall, 0, 10);
ptr = strtok_s(&buff[10], " ,\r", &context);
strcpy(STREAM->RemoteCall, ptr);
//<SOH>00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36<EOT>
SendLen = sprintf(Reply, "c%s:42 %s:24 %c 7 T60R5W10",
STREAM->MyCall, STREAM->RemoteCall, ARQ->OurStream);
ARQ->ARQState = ARQ_ACTIVE;
ARQ->ARQTimerState = ARQ_CONNECTING;
SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
SetWindowText(STREAM->xIDC_STATUS, "Connecting");
SetWindowText(STREAM->xIDC_MYCALL, STREAM->MyCall);
SetWindowText(STREAM->xIDC_DESTCALL, STREAM->RemoteCall);
SetWindowText(STREAM->xIDC_DIRN, "Out");
STREAM->Connecting = TRUE;
return 0;
}
}
return (0);
case 3:
Stream = (int)buff;
STREAM = &TNC->Streams[Stream];
{
// Busy if TX Window reached
struct ARQINFO * ARQ = STREAM->ARQInfo;
int Outstanding;
Outstanding = ARQ->TXSeq - ARQ->TXLastACK;
if (Outstanding < 0)
Outstanding += 64;
TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames
if (Outstanding > ARQ->TXWindow)
return (1 | 1 << 8 | STREAM->Disconnecting << 15); // 3rd Nibble is frames unacked
else
return 1 << 8 | STREAM->Disconnecting << 15;
}
return 1 << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting
case 4: // reinit
return (0);
case 5: // Close
return 0;
}
return 0;
}
VOID UIHook(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG)
{
PORT = GetPortTableEntryFromPortNum(PORT->HookPort->PORTNUMBER);
if (PORT)
ProcessARQPacket(PORT, Buffer);
}
static VOID UpdateStatsLine(struct TNCINFO * TNC, struct STREAMINFO * STREAM)
{
char Count[16];
sprintf(Count, "%d", STREAM->bytesRXed);
SetWindowText(STREAM->xIDC_RXED, Count);
sprintf(Count, "%d", STREAM->bytesTXed);
SetWindowText(STREAM->xIDC_SEND, Count);
sprintf(Count, "%d", STREAM->BytesResent);
SetWindowText(STREAM->xIDC_RESENT, Count);
sprintf(Count, "%d", STREAM->BytesAcked);
SetWindowText(STREAM->xIDC_ACKED, Count);
}
static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL)
{
int Len = sprintf(Buff, "<html><meta http-equiv=expires content=0><meta http-equiv=refresh content=15>"
"<script type=\"text/javascript\">\r\n"
"function ScrollOutput()\r\n"
"{var textarea = document.getElementById('textarea');"
"textarea.scrollTop = textarea.scrollHeight;}</script>"
"</head><title>FLDigi Status</title></head><body id=Text onload=\"ScrollOutput()\">"
"<h2>FLDIGI Status</h2>");
Len += sprintf(&Buff[Len], "<table style=\"text-align: left; width: 500px; font-family: monospace; align=center \" border=1 cellpadding=2 cellspacing=2>");
Len += sprintf(&Buff[Len], "<tr><td width=110px>Comms State</td><td>%s</td></tr>", TNC->WEB_COMMSSTATE);
Len += sprintf(&Buff[Len], "<tr><td>TNC State</td><td>%s</td></tr>", TNC->WEB_TNCSTATE);
Len += sprintf(&Buff[Len], "<tr><td>Mode</td><td>%s</td></tr>", TNC->WEB_MODE);
Len += sprintf(&Buff[Len], "<tr><td>Channel State</td><td>%s</td></tr>", TNC->WEB_CHANSTATE);
Len += sprintf(&Buff[Len], "<tr><td>Proto State</td><td>%s</td></tr>", TNC->WEB_PROTOSTATE);
Len += sprintf(&Buff[Len], "<tr><td>Traffic</td><td>%s</td></tr>", TNC->WEB_TRAFFIC);
// Len += sprintf(&Buff[Len], "<tr><td>TNC Restarts</td><td></td></tr>", TNC->WEB_RESTARTS);
Len += sprintf(&Buff[Len], "</table>");
Len += sprintf(&Buff[Len], "<textarea rows=10 style=\"width:500px; height:250px;\" id=textarea >%s</textarea>", TNC->WebBuffer);
Len = DoScanLine(TNC, Buff, Len);
return Len;
}
UINT UIARQExtInit(EXTPORTDATA * PortEntry)
{
int i, port;
char Msg[255];
struct TNCINFO * TNC;
char * ptr;
struct PORTCONTROL * PORT;
struct STREAMINFO * STREAM;
srand((unsigned int)time(NULL));
port = PortEntry->PORTCONTROL.PORTNUMBER;
ReadConfigFile(port, ProcessLine);
TNC = TNCInfo[port];
if (TNC == NULL)
{
// Not defined in Config file
sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n");
WritetoConsole(Msg);
return (int) ExtProc;
}
for (i = 0; i <MAXARQ; i++)
{
TNC->Streams[i].ARQInfo = zalloc(sizeof(struct ARQINFO));
}
TNC->Port = port;
TNC->PortRecord = PortEntry;
if (PortEntry->PORTCONTROL.PORTCALL[0] == 0)
memcpy(TNC->NodeCall, MYNODECALL, 10);
else
ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall);
if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0)
TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK;
PortEntry->PORTCONTROL.PROTOCOL = 10;
PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream
PortEntry->PORTCONTROL.UICAPABLE = 1; // Can send beacons
PortEntry->PORTCONTROL.PORTQUALITY = 0;
PortEntry->SCANCAPABILITIES = NONE; // Scan Control - None
if (PortEntry->PORTCONTROL.PORTPACLEN == 0 || PortEntry->PORTCONTROL.PORTPACLEN > 128)
PortEntry->PORTCONTROL.PORTPACLEN = 64;
ptr=strchr(TNC->NodeCall, ' ');
if (ptr) *(ptr) = 0; // Null Terminate
TNC->Hardware = H_UIARQ;
if (TNC->BusyWait == 0)
TNC->BusyWait = 10;
PortEntry->MAXHOSTMODESESSIONS = MAXARQ;
i = 0;
while (TNC->ARQPorts[i])
{
PORT = GetPortTableEntryFromPortNum(TNC->ARQPorts[i]);
PORT->UIHook = (FARPROCY)UIHook;
PORT->HookPort = (struct PORTCONTROL *)PortEntry;
i++;
}
TNC->WEB_MODE = zalloc(50);
TNC->WEB_TRAFFIC = zalloc(100);
PortEntry->PORTCONTROL.TNC = TNC;
TNC->WebWindowProc = WebProc;
TNC->WebWinX = 520;
TNC->WebWinY = 500;
TNC->WebBuffer = zalloc(5000);
TNC->WEB_COMMSSTATE = zalloc(100);
TNC->WEB_TNCSTATE = zalloc(100);
TNC->WEB_CHANSTATE = zalloc(100);
TNC->WEB_BUFFERS = zalloc(100);
TNC->WEB_PROTOSTATE = zalloc(100);
TNC->WEB_RESTARTTIME = zalloc(100);
TNC->WEB_RESTARTS = zalloc(100);
TNC->WEB_MODE = zalloc(50);
TNC->WEB_TRAFFIC = zalloc(100);
#ifndef LINBPQ
CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 560, 350, ForcedClose);
CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " MyCall", WS_CHILD | WS_VISIBLE, 5,6,79,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " DestCall", WS_CHILD | WS_VISIBLE, 85,6,79,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " Status", WS_CHILD | WS_VISIBLE, 165,6,84,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " Sent", WS_CHILD | WS_VISIBLE, 250,6,59,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " Rxed", WS_CHILD | WS_VISIBLE, 310,6,59,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " Resent", WS_CHILD | WS_VISIBLE, 370,6,59,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " Acked", WS_CHILD | WS_VISIBLE, 430,6,59,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(WS_EX_STATICEDGE, "STATIC", " Dirn", WS_CHILD | WS_VISIBLE, 490,6,49,20, TNC->hDlg, NULL, hInstance, NULL);
for (i = 0; i <MAXARQ; i++)
{
STREAM = &TNC->Streams[i];
STREAM->xIDC_MYCALL = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 5, 26 + i*20, 79, 20, TNC->hDlg, NULL, hInstance, NULL);
STREAM->xIDC_DESTCALL = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 85, 26 + i*20, 79, 20, TNC->hDlg, NULL, hInstance, NULL);
STREAM->xIDC_STATUS = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 165, 26 + i*20, 84, 20, TNC->hDlg, NULL, hInstance, NULL);
STREAM->xIDC_SEND = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 250, 26 + i*20, 59, 20, TNC->hDlg, NULL, hInstance, NULL);
STREAM->xIDC_RXED = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 310, 26 + i*20, 59, 20, TNC->hDlg, NULL, hInstance, NULL);
STREAM->xIDC_RESENT = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 370, 26 + i*20, 59, 20, TNC->hDlg, NULL, hInstance, NULL);
STREAM->xIDC_ACKED = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 430, 26 + i*20, 59, 20, TNC->hDlg, NULL, hInstance, NULL);
STREAM->xIDC_DIRN = CreateWindowEx(WS_EX_CLIENTEDGE, "STATIC", "", WS_CHILD | WS_VISIBLE, 490, 26 + i*20, 49, 20, TNC->hDlg, NULL, hInstance, NULL);
}
TNC->ClientHeight = 360;
TNC->ClientWidth = 560;
MoveWindows(TNC);
#endif
i=sprintf(Msg,"UIARQ\n");
WritetoConsole(Msg);
return ((int) ExtProc);
}
static int ProcessLine(char * buf, int Port)
{
UCHAR * ptr;
char * p_ipad = 0;
int BPQport;
int len=510;
struct TNCINFO * TNC;
char errbuf[256];
strcpy(errbuf, buf);
BPQport = Port;
TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO));
TNC->Timeout = 50; // Default retry = 5 seconds
TNC->Retries = 6; // Default Retries
TNC->Window = 16;
TNC->InitScript = malloc(1000);
TNC->InitScript[0] = 0;
// Read Initialisation lines
while(TRUE)
{
strcpy(errbuf, buf);
if (memcmp(buf, "****", 4) == 0)
return TRUE;
ptr = strchr(buf, ';');
if (ptr)
{
*ptr++ = 13;
*ptr = 0;
}
if (_memicmp(buf, "TIMEOUT", 7) == 0)
TNC->Timeout = atoi(&buf[8]) * 10;
else if (_memicmp(buf, "RETRIES", 7) == 0)
TNC->Retries = atoi(&buf[8]);
else if (_memicmp(buf, "WINDOW", 6) == 0)
TNC->Window = atoi(&buf[7]);
else
{
char * ptr, * p_value, * p_port;
int i;
ptr = strtok(buf, "=\t\n\r");
p_value = strtok(NULL, " \t\n\r");
if (ptr == NULL) return (TRUE);
if (*ptr =='#') return (TRUE); // comment
if (*ptr ==';') return (TRUE); // comment
if (_stricmp(ptr,"Ports") == 0)
{
int n = 0;
p_port = strtok(p_value, " ,\t\n\r");
while (p_port != NULL)
{
i = atoi(p_port);
if (i == 0) return FALSE;
if (i > NUMBEROFPORTS) return FALSE;
TNC->ARQPorts[n++] = i;
p_port = strtok(NULL, " ,\t\n\r");
}
}
}
if (GetLine(buf) == 0)
return TRUE;
}
return FALSE;
}
static VOID SendPacket(struct TNCINFO * TNC, struct STREAMINFO * STREAM, UCHAR * Msg, int MsgLen)
{
DIGIMESSAGE Block = {0};
int Port = TNC->ARQPorts[0];
Block.CTL = 3;
Block.PID = 0xF0;
ConvToAX25(STREAM->RemoteCall, Block.DEST);
Block.DEST[6] |= 0x80; // set Command Bit
memcpy(Block.ORIGIN, MYCALL, 7);
Block.L2DATA[0] = STREAM->ARQInfo->FarStream;
memcpy(&Block.L2DATA[1], Msg, MsgLen);
MsgLen += 1;
Send_AX_Datagram(&Block, MsgLen + 2, Port); // Inulude CTL and PID
}
VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, int Stream);
VOID ProcessARQPacket(struct PORTCONTROL * PORT, MESSAGE * Buffer)
{
// ARQ Packet from KISS-Like Hardware
struct TNCINFO * TNC = TNCInfo[PORT->PORTNUMBER];
UCHAR * Input;
int Len;
int Stream = Buffer->L2DATA[0] - 64;
if (Stream < 0 || Stream > MAXARQ)
return;
// First Bytes is Stream Number (as ASCII Letter)
Input = &Buffer->L2DATA[1];
Len = Buffer->LENGTH - 24;
ProcessFLDigiData(TNC, Input, Len, Stream);
}
/*
<SOH>00cG8BPQ:1025 G8BPQ:24 0 8 T60R6W108E06<EOT>
<SOH>00kG8BPQ:24 G8BPQ 4 85F9B<EOT>
<SOH>00cG8BPQ:1025 GM8BPQ:24 0 7 T60R5W1051D5<EOT> (128, 5)
,<SOH>00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36<EOT>
<SOH>00kG8BPQ:24 G8BPQ 5 89FCA<EOT>
First no sees to be a connection counter. Next may be stream
<SOH>08s___ABFC<EOT>
<SOH>08tG8BPQ:73 xxx 33FA<EOT>
<SOH>00tG8BPQ:73 yyy 99A3<EOT>
<SOH>08dG8BPQ:90986C<EOT>
<SOH>00bG8BPQ:911207<EOT>
call:90 for dis 91 for dis ack 73<sp> for chat)
<SOH>08pG8BPQ<SUB>?__645E<EOT>
<SOH>00s_??4235<EOT>
<SOH>08pG8BPQ<SUB>?__645E<EOT>
<SOH>00s_??4235<EOT>
i Ident
c Connect
k Connect Ack
r Connect NAK
d Disconnect req
s Data Ack/ Retransmit Req )status)
p Poll
f Format Fail
b dis ack
t talk
a Abort
o Abort ACK
<SOH>00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36<EOT>
<SOH>00kG8BPQ:24 G8BPQ 6 49A3A<EOT>
<SOH>08s___ABFC<EOT>
<SOH>08 ARQ:FILE::flarqmail-1.eml
ARQ:EMAIL::
ARQ:SIZE::90
ARQ::STX
//FLARQ COMPOSER
Date: 09/01/2014 23:24:42
To: gm8bpq
From:
SubjectA0E0<SOH>
<SOH>08!: Test
Test Message
ARQ::ETX
F0F2<SOH>
<SOH>08pG8BPQ<SUB>!__623E<EOT>
<SOH>08pG8BPQ<SUB>!__623E<EOT>
<SOH>08pG8BPQ<SUB>!__623E<EOT>
*/
static VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, int Stream)
{
PMSGWITHLEN buffptr;
struct STREAMINFO * STREAM = &TNC->Streams[Stream];
char CTRL = Input[0];
struct ARQINFO * ARQ = STREAM->ARQInfo;
char Channel = Stream + 64;
int SendLen;
char Reply[80];
Input[Len] = 0;
// Process Message
// This processes eitrher message from the KISS or RAW interfaces.
// Headers and RAW checksum have been removed, so packet starts with Control Byte
// Only a connect request is allowed with no session, so check first
if (CTRL == 'c')
{
// Connect Request
char * call1;
char * call2;
char * port1;
char * port2;
char * ptr;
char * context;
char FarStream = 0;
int BlockSize = 6; // 64 default
int Window = TNC->Window;
APPLCALLS * APPL;
char * ApplPtr = APPLS;
int App;
char Appl[10];
struct WL2KInfo * WL2K = TNC->WL2K;
TRANSPORTENTRY * SESS;
if (Stream)
return; // Shouldn't have Stream on Connect Request
call1 = strtok_s(&Input[1], " ", &context);
call2 = strtok_s(NULL, " ", &context);
port1 = strlop(call1, ':');
port2 = strlop(call2, ':');
// See if for us
for (App = 0; App < 32; App++)
{
APPL=&APPLCALLTABLE[App];
memcpy(Appl, APPL->APPLCALL_TEXT, 10);
ptr=strchr(Appl, ' ');
if (ptr) *ptr = 0;
if (_stricmp(call2, Appl) == 0)
break;
memcpy(Appl, APPL->APPLALIAS_TEXT, 10);
ptr=strchr(Appl, ' ');
if (ptr) *ptr = 0;
if (_stricmp(call2, Appl) == 0)
break;
}
if (App > 31)
if (strcmp(TNC->NodeCall, call2) !=0)
if (strcmp(call2, MYALIASLOPPED) !=0)
return; // Not Appl or Port/Node Call
ptr = strtok_s(NULL, " ", &context);
FarStream = *ptr;
ptr = strtok_s(NULL, " ", &context);
BlockSize = atoi(ptr);
if (ARQ->ARQState > ARQ_CONNECTING)
{
// We have already received a connect request - just ACK it
goto AckConnectRequest;
}
// Get a Session
Stream = 1;
while(Stream <= MAXARQ)
{
if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0)
goto GotStream;
Stream++;
}
// No free streams - send Disconnect
return;
GotStream:
STREAM = &TNC->Streams[Stream];
ProcessIncommingConnect(TNC, call1, Stream, FALSE);
SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream];
strcpy(STREAM->MyCall, call2);
STREAM->ConnectTime = time(NULL);
STREAM->bytesRXed = STREAM->bytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0;
if (WL2K)
strcpy(SESS->RMSCall, WL2K->RMSCall);
ARQ = STREAM->ARQInfo;
memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State
ARQ->FarStream = FarStream;
ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent
ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received
ARQ->ARQState = ARQ_ACTIVE;
ARQ->OurStream = Stream + 64;
STREAM->NeedDisc = 0;
if (App < 32)
{
char AppName[13];
memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12);
AppName[12] = 0;
// Make sure app is available
if (CheckAppl(TNC, AppName))
{
char Buffer[32];
int MsgLen = sprintf(Buffer, "%s\r", AppName);
buffptr = GetBuff();
if (buffptr == 0)
{
return; // No buffers, so ignore
}
buffptr->Len = MsgLen;
memcpy(buffptr->Data, Buffer, MsgLen);
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
TNC->SwallowSignon = TRUE;
// Save Appl Call in case needed for
}
else
{
STREAM->NeedDisc = 50; // 1 sec
}
}
ARQ->TXWindow = Window;
if (BlockSize < 4) BlockSize = 4;
if (BlockSize < 9) BlockSize = 9;
ARQ->MaxBlock = Blocksizes[BlockSize];
ARQ->ARQTimer = 1; // To force CTEXT to be Queued
if (App == 32)
{
// Connect to Node - send CTEXT
if (HFCTEXTLEN > 1)
{
buffptr = GetBuff();
if (buffptr)
{
buffptr->Len = HFCTEXTLEN;
memcpy(buffptr->Data, HFCTEXT, HFCTEXTLEN);
SendARQData(TNC, buffptr, Stream);
}
}
}
if (STREAM->NeedDisc)
{
// Send Not Avail
buffptr = GetBuff();
if (buffptr)
{
buffptr->Len = sprintf(buffptr->Data, "Application Not Available\r");
SendARQData(TNC, buffptr, Stream);
}
}
SetWindowText(STREAM->xIDC_MYCALL, STREAM->MyCall);
SetWindowText(STREAM->xIDC_DESTCALL, STREAM->RemoteCall);
SetWindowText(STREAM->xIDC_STATUS, "ConPending");
SetWindowText(STREAM->xIDC_DIRN, "In");
AckConnectRequest:
SendLen = sprintf(Reply, "k%s:24 %s %c 7", call2, call1, ARQ->OurStream);
SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
ARQ->ARQTimerState = ARQ_CONNECTACK;
return;
}
// All others need a session
// if (!STREAM->Connected && !STREAM->Connecting)
// return;
if (CTRL == 'k')
{
// Connect ACK
char * call1;
char * call2;
char * port1;
char * port2;
char * ptr;
char * context;
char FarStream = 0;
int BlockSize = 6; // 64 default
int Window = 16;
char Reply[80];
int ReplyLen;
call1 = strtok_s(&Input[1], " ", &context);
call2 = strtok_s(NULL, " ", &context);
port1 = strlop(call1, ':');
port2 = strlop(call2, ':');
if (strcmp(call1, STREAM->RemoteCall) != 0)
return;
if (Channel != ARQ->OurStream)
return; // Wrong Session
ptr = strtok_s(NULL, " ", &context);
if (ptr)
FarStream = *ptr;
ptr = strtok_s(NULL, " ", &context);
if (ptr)
BlockSize = atoi(ptr);
if (STREAM->Connected)
goto SendKReply; // Repeated ACK
STREAM->ConnectTime = time(NULL);
STREAM->bytesRXed = STREAM->bytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0;
STREAM->Connected = TRUE;
ARQ->ARQTimerState = 0;
ARQ->ARQTimer = 0;
sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", STREAM->MyCall, STREAM->RemoteCall);
SetWindowText(STREAM->xIDC_MYCALL, STREAM->MyCall);
SetWindowText(STREAM->xIDC_DESTCALL, STREAM->RemoteCall);
SetWindowText(STREAM->xIDC_DIRN, "Out");
UpdateMH(TNC, STREAM->RemoteCall, '+', 'Z');
ARQ->ARQTimerState = 0;
ARQ->FarStream = FarStream;
ARQ->TXWindow = TNC->Window;
ARQ->MaxBlock = Blocksizes[BlockSize];
ARQ->ARQState = ARQ_ACTIVE;
STREAM->NeedDisc = 0;
buffptr = GetBuff();
if (buffptr)
{
ReplyLen = sprintf(Reply, "*** Connected to %s\r", STREAM->RemoteCall);
buffptr->Len = ReplyLen;
memcpy(buffptr->Data, Reply, ReplyLen);
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
}
strcpy(TNC->WEB_PROTOSTATE, "Connected");
SetWindowText(STREAM->xIDC_STATUS, "Connected");
SendKReply:
// Reply with status
SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32);
if (ARQ->RXHighest != ARQ->RXNoGaps)
{
int n = ARQ->RXNoGaps + 1;
n &= 63;
while (n != ARQ->RXHighest)
{
if (ARQ->RXHOLDQ[n] == 0) // Dont have it
SendLen += sprintf(&Reply[SendLen], "%c", n + 32);
n++;
n &= 63;
}
}
QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
return;
}
// All others need a session
//if (!STREAM->Connected)
// return;
if (CTRL == 's')
{
// Status
if (Channel != ARQ->OurStream)
return; // Wrong Session
ARQ->ARQTimer = 0; // Stop retry timer
Input[Len] = 0;
ProcessARQStatus(TNC, Stream, ARQ, &Input[1]);
return;
}
if (CTRL == 'p')
{
// Poll
char * call1;
char * context;
call1 = strtok_s(&Input[1], " \x1A", &context);
if (strcmp(call1, STREAM->RemoteCall) != 0)
return;
if (Channel != ARQ->OurStream)
return; // Wrong Session
Debugprintf("Sending Poll Resp TX NOGaps High %d %d %d", ARQ->TXSeq, ARQ->RXNoGaps, ARQ->RXHighest);
SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32);
if (ARQ->RXHighest != ARQ->RXNoGaps)
{
int n = ARQ->RXNoGaps + 1;
n &= 63;
while (n != ARQ->RXHighest)
{
if (ARQ->RXHOLDQ[n] == 0) // Dont have it
SendLen += sprintf(&Reply[SendLen], "%c", n + 32);
n++;
n &= 63;
}
}
else
ARQ->TurnroundTimer = 15; // Allow us to send it all acked
QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
return;
}
if (CTRL == 'a')
{
// Abort. Send Abort ACK - same as
char * call1;
char * context;
call1 = strtok_s(&Input[1], " :", &context);
if (strcmp(call1, STREAM->RemoteCall) != 0)
return;
if (Channel != ARQ->OurStream)
return; // Wrong Session
SendLen = sprintf(Reply, "o%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32);
if (ARQ->RXHighest != ARQ->RXNoGaps)
{
int n = ARQ->RXNoGaps + 1;
n &= 63;
while (n != ARQ->RXHighest)
{
if (ARQ->RXHOLDQ[n] == 0) // Dont have it
SendLen += sprintf(&Reply[SendLen], "%c", n + 32);
n++;
n &= 63;
}
}
QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
return;
}
if (CTRL == 'i')
{
// Ident
return;
}
if (CTRL == 't')
{
// Talk - not sure what to do with these
return;
}
if (CTRL == 'd')
{
// Disconnect Request
char * call1;
char * context;
call1 = strtok_s(&Input[1], " ", &context);
strlop(call1, ':');
if (strcmp(STREAM->RemoteCall, call1))
return;
if (Channel != ARQ->OurStream)
return; // Wrong Session
// As the Disc ACK isn't repeated, we have to clear session now
STREAM->Connected = FALSE;
STREAM->Connecting = FALSE;
STREAM->ReportDISC = TRUE;
strcpy(TNC->WEB_PROTOSTATE, "Disconncted");
SetWindowText(STREAM->xIDC_MYCALL, "");
SetWindowText(STREAM->xIDC_DESTCALL, "");
SetWindowText(STREAM->xIDC_DIRN, "");
SetWindowText(STREAM->xIDC_STATUS, "");
ARQ->ARQState = 0;
SendLen = sprintf(Reply, "b%s:91", STREAM->MyCall);
ARQ->ARQTimerState = ARQ_WAITACK;
SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
ARQ->Retries = 2;
return;
}
if (CTRL == 'b')
{
// Disconnect ACK
char * call1;
char * context;
call1 = strtok_s(&Input[1], " ", &context);
strlop(call1, ':');
if (strcmp(STREAM->RemoteCall, call1))
return;
if (Channel != ARQ->OurStream)
return; // Wrong Session
ARQ->ARQTimer = 0;
ARQ->ARQTimerState = 0;
ARQ->ARQState = 0;
if (STREAM->Connected)
{
// Create a traffic record
hookL4SessionDeleted(TNC, STREAM);
}
STREAM->Connecting = FALSE;
STREAM->Connected = FALSE; // Back to Command Mode
STREAM->ReportDISC = TRUE; // Tell Node
STREAM->Disconnecting = FALSE;
strcpy(TNC->WEB_PROTOSTATE, "Disconncted");
SetWindowText(STREAM->xIDC_MYCALL, "");
SetWindowText(STREAM->xIDC_DESTCALL, "");
SetWindowText(STREAM->xIDC_DIRN, "");
SetWindowText(STREAM->xIDC_STATUS, "");
return;
}
if (CTRL == 'u')
{
// Beacon
//>00uGM8BPQ:72 GM8BPQ TestingAD67
char * Call = &Input[1];
strlop(Call, ':');
UpdateMH(TNC, Call, '!', 0);
return;
}
if (STREAM->Connected)
{
if (Channel != ARQ->OurStream)
return; // Wrong Session
if (CTRL >= ' ' && CTRL < 96)
{
// ARQ Data
int Seq = CTRL - 32;
int Work;
// if (rand() % 5 == 2)
// {
// Debugprintf("Dropping %d", Seq);
// return;
// }
buffptr = GetBuff();
if (buffptr == NULL)
return; // Sould never run out, but cant do much else
// Remove any DLE transparency
Len -= 1;
buffptr->Len = Len;
memcpy(buffptr->Data, &Input[1], Len);
STREAM->bytesRXed += Len;
UpdateStatsLine(TNC, STREAM);
// Safest always to save, then see what we can process
if (ARQ->RXHOLDQ[Seq])
{
// Wot! Shouldn't happen
ReleaseBuffer(ARQ->RXHOLDQ[Seq]);
// Debugprintf("ARQ Seq %d Duplicate");
}
ARQ->RXHOLDQ[Seq] = buffptr;
// Debugprintf("ARQ saving %d", Seq);
// If this is higher that highest received, save. But beware of wrap'
// Hi = 2, Seq = 60 dont save s=h = 58
// Hi = 10 Seq = 12 save s-h = 2
// Hi = 14 Seq = 10 dont save s-h = -4
// Hi = 60 Seq = 2 save s-h = -58
Work = Seq - ARQ->RXHighest;
if ((Work > 0 && Work < 32) || Work < -32)
ARQ->RXHighest = Seq;
// We may now be able to process some
Work = (ARQ->RXNoGaps + 1) & 63; // The next one we need
while (ARQ->RXHOLDQ[Work])
{
// We have it
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, ARQ->RXHOLDQ[Work]);
// ReleaseBuffer(ARQ->RXHOLDQ[Work]);
ARQ->RXHOLDQ[Work] = NULL;
// Debugprintf("Processing %d from Q", Work);
ARQ->RXNoGaps = Work;
Work = (Work + 1) & 63; // The next one we need
}
ARQ->TurnroundTimer = 200; // Delay before allowing reply. Will normally be reset by the poll following data
return;
}
}
}
static VOID SendARQData(struct TNCINFO * TNC, PMSGWITHLEN Buffer, int Stream)
{
// Send Data, saving a copy until acked.
struct STREAMINFO * STREAM = &TNC->Streams[Stream];
struct ARQINFO * ARQ = STREAM->ARQInfo;
UCHAR TXBuffer[300];
SOCKET sock = TNC->TCPDataSock;
int SendLen;
UCHAR * ptr;
int Origlen = Buffer->Len;
ARQ->TXSeq++;
ARQ->TXSeq &= 63;
SendLen = sprintf(TXBuffer, "%c", ARQ->TXSeq + 32);
ptr = Buffer->Data; // Start of data;
ptr[Buffer->Len] = 0;
memcpy(&TXBuffer[SendLen], Buffer->Data, Origlen);
SendLen += Origlen;
TXBuffer[SendLen] = 0;
// if (rand() % 5 == 2)
// Debugprintf("Dropping %d", ARQ->TXSeq);
// else
ARQ->TXHOLDQ[ARQ->TXSeq] = Buffer;
STREAM->bytesTXed += Origlen;
UpdateStatsLine(TNC, STREAM);
// if waiting for ack, don't send, just queue. Will be sent when ack received
if (ARQ->ARQTimer == 0 || ARQ->ARQTimerState == ARQ_WAITDATA)
{
SendPacket(TNC, STREAM, TXBuffer, SendLen);
ARQ->ARQTimer = 15; // wait up to 1.5 sec for more data before polling
ARQ->Retries = 1;
ARQ->ARQTimerState = ARQ_WAITDATA;
}
else
STREAM->BytesResent -= Origlen; // So wont be included in resent bytes
}
VOID TidyClose(struct TNCINFO * TNC, int Stream)
{
char Reply[80];
int SendLen;
struct STREAMINFO * STREAM = &TNC->Streams[Stream];
struct ARQINFO * ARQ = STREAM->ARQInfo;
SendLen = sprintf(Reply, "d%s:90", STREAM->MyCall);
SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
ARQ->ARQTimerState = ARQ_DISC;
}
VOID ForcedClose(struct TNCINFO * TNC, int Stream)
{
TidyClose(TNC, Stream); // I don't think Hostmode has a DD
}
VOID CloseComplete(struct TNCINFO * TNC, int Stream)
{
}
static VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen)
{
// Used for Messages that need a reply. Save, send and set timeout
memcpy(ARQ->LastMsg, Msg, MsgLen + 1); // Include Null
ARQ->LastLen = MsgLen;
// Delay the send for a shot while
// SendPacket(sock, Msg, MsgLen, 0);
ARQ->ARQTimer = 1;
ARQ->Retries = TNC->Retries + 1; // First timout is the real send
return;
}
static VOID ARQTimer(struct TNCINFO * TNC)
{
PMSGWITHLEN buffptr;
struct STREAMINFO * STREAM;
struct ARQINFO * ARQ;
int SendLen;
char Reply[80];
int Stream;
//Send frames, unless held by TurnroundTimer or Window
int Outstanding;
for (Stream = 0; Stream <MAXARQ; Stream++)
{
STREAM = &TNC->Streams[Stream];
ARQ = STREAM->ARQInfo;
// TXDelay is used as a turn round delay for frames that don't have to be retried. It doesn't
// need to check for busy (or anything else (I think!)
if (ARQ->TXDelay)
{
ARQ->TXDelay--;
if (ARQ->TXDelay)
continue;
SendPacket(TNC, STREAM, ARQ->TXMsg, ARQ->TXLen);
}
// if We are alredy sending (State = ARQ_WAITDATA) we should allow it to send more (and the Poll at end)
if (ARQ->ARQTimerState == ARQ_WAITDATA)
{
while (STREAM->BPQtoPACTOR_Q)
{
Outstanding = ARQ->TXSeq - ARQ->TXLastACK;
if (Outstanding < 0)
Outstanding += 64;
TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames
if (Outstanding >= ARQ->TXWindow)
break;
buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q);
SendARQData(TNC, buffptr, Stream);
}
ARQ->ARQTimer--;
if (ARQ->ARQTimer > 0)
continue; // Timer Still Running
// No more data available - send poll
SendLen = sprintf(Reply, "p%s", STREAM->MyCall);
ARQ->ARQTimerState = ARQ_WAITACK;
// This is one message that should not be queued so it is sent straiget after data
Debugprintf("Sending Poll After Data");
memcpy(ARQ->LastMsg, Reply, SendLen + 1);
ARQ->LastLen = SendLen;
SendPacket(TNC, STREAM, Reply, SendLen);
ARQ->ARQTimer = TNC->Timeout;
ARQ->Retries = TNC->Retries;
strcpy(TNC->WEB_PROTOSTATE, "Wait ACK");
SetWindowText(STREAM->xIDC_STATUS, "Wait ACK");
continue;
}
// TrunroundTimer is used to allow time for far end to revert to RX
if (ARQ->TurnroundTimer)
ARQ->TurnroundTimer--;
if (ARQ->TurnroundTimer == 0)
{
while (STREAM->BPQtoPACTOR_Q)
{
Outstanding = ARQ->TXSeq - ARQ->TXLastACK;
if (Outstanding < 0)
Outstanding += 64;
TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q) + 1; // Make sure busy is reported to BBS
if (Outstanding >= ARQ->TXWindow)
break;
buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q);
SendARQData(TNC, buffptr, Stream);
}
}
if (ARQ->ARQTimer)
{
// Only decrement if running send poll timer
// if (ARQ->ARQTimerState != ARQ_WAITDATA)
// return;
ARQ->ARQTimer--;
{
if (ARQ->ARQTimer)
continue; // Timer Still Running
}
ARQ->Retries--;
if (ARQ->Retries)
{
// Retry Current Message
SendPacket(TNC, STREAM, ARQ->LastMsg, ARQ->LastLen);
ARQ->ARQTimer = TNC->Timeout + (rand() % 30);
continue;
}
// Retried out.
switch (ARQ->ARQTimerState)
{
case ARQ_WAITDATA:
// No more data available - send poll
SendLen = sprintf(Reply, "p%s", STREAM->MyCall);
Debugprintf("Sending Poll After Timeout??");
ARQ->ARQTimerState = ARQ_WAITACK;
// This is one message that should not be queued so it is sent straiget after data
memcpy(ARQ->LastMsg, Reply, SendLen + 1);
ARQ->LastLen = SendLen;
SendPacket(TNC, STREAM, Reply, SendLen);
ARQ->ARQTimer = TNC->Timeout;
ARQ->Retries = TNC->Retries;
strcpy(TNC->WEB_PROTOSTATE, "Wait ACK");
SetWindowText(STREAM->xIDC_STATUS, "Wait ACK");
continue;
case ARQ_CONNECTING:
// Report Connect Failed, and drop back to command mode
buffptr = GetBuff();
if (buffptr)
{
buffptr->Len = sprintf((UCHAR *)&buffptr[2], "UIARQ} Failure with %s\r", STREAM->RemoteCall);
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
}
// Send Disc to TNC in case it got the Connects, but we missed the ACKs
TidyClose(TNC, Stream);
ARQ->Retries = 2; // First timout is the real send, only send once
STREAM->Connecting = FALSE; // Back to Command Mode
ARQ->ARQState = FALSE;
break;
case ARQ_WAITACK:
case ARQ_CONNECTACK:
case ARQ_DISC:
STREAM->Connected = FALSE; // Back to Command Mode
STREAM->ReportDISC = TRUE;
ARQ->ARQState = FALSE;
while (STREAM->PACTORtoBPQ_Q)
ReleaseBuffer(Q_REM(&STREAM->PACTORtoBPQ_Q));
while (STREAM->BPQtoPACTOR_Q)
ReleaseBuffer(Q_REM(&STREAM->BPQtoPACTOR_Q));
strcpy(TNC->WEB_TNCSTATE, "Free");
strcpy(TNC->WEB_PROTOSTATE, "Disconncted");
SetWindowText(STREAM->xIDC_MYCALL, "");
SetWindowText(STREAM->xIDC_DESTCALL, "");
SetWindowText(STREAM->xIDC_DIRN, "");
SetWindowText(STREAM->xIDC_STATUS, "");
break;
}
}
}
}
static VOID ProcessARQStatus(struct TNCINFO * TNC, int Stream, struct ARQINFO * ARQ, char * Input)
{
// Release any acked frames and resend any outstanding
struct STREAMINFO * STREAM = &TNC->Streams[Stream];
int LastInSeq = Input[1] - 32;
int LastRXed = Input[2] - 32;
int FirstUnAcked = ARQ->TXLastACK;
int n = strlen(Input) - 3;
char * ptr;
int NexttoResend;
int First, Last, Outstanding;
PMSGWITHLEN Buffer;
int Acked = 0;
// First status is an ack of Connect ACK
if (ARQ->ARQTimerState == ARQ_CONNECTACK)
{
ARQ->Retries = 0;
ARQ->ARQTimer = 0;
ARQ->ARQTimerState = 0;
strcpy(TNC->WEB_PROTOSTATE, "Connected");
SetWindowText(STREAM->xIDC_STATUS, "Connected");
}
Debugprintf("Lsast In Seq, LastRXed %d %d", LastInSeq, LastRXed);
// Release all up to LastInSeq
while (FirstUnAcked != LastInSeq)
{
FirstUnAcked++;
FirstUnAcked &= 63;
Buffer = ARQ->TXHOLDQ[FirstUnAcked];
if (Buffer)
{
Debugprintf("Acked %d", FirstUnAcked);
STREAM->BytesAcked += Buffer->Len;
ReleaseBuffer(Buffer);
ARQ->TXHOLDQ[FirstUnAcked] = NULL;
Acked++;
}
}
ARQ->TXLastACK = FirstUnAcked;
Outstanding = ARQ->TXSeq - ARQ->TXLastACK;
if (Outstanding < 0)
Outstanding += 64;
TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames
if (FirstUnAcked == ARQ->TXSeq)
{
UpdateStatsLine(TNC, STREAM);
ARQ->NoAckRetries = 0;
strcpy(TNC->WEB_PROTOSTATE, "Connected");
SetWindowText(STREAM->xIDC_STATUS, "Connected");
return; // All Acked
}
// Release any not in retry list up to LastRXed.
ptr = &Input[3];
while (n)
{
NexttoResend = *(ptr++) - 32;
FirstUnAcked++;
FirstUnAcked &= 63;
while (FirstUnAcked != NexttoResend)
{
Buffer = ARQ->TXHOLDQ[FirstUnAcked];
if (Buffer)
{
Debugprintf("Acked %d", FirstUnAcked);
STREAM->BytesAcked += Buffer->Len;
ReleaseBuffer(Buffer);
ARQ->TXHOLDQ[FirstUnAcked] = NULL;
Acked++;
}
FirstUnAcked++;
FirstUnAcked &= 63;
}
// We don't ACK this one. Process any more resend values, then release up to LastRXed.
n--;
}
// Release rest up to LastRXed
while (FirstUnAcked != LastRXed)
{
FirstUnAcked++;
FirstUnAcked &= 63;
Buffer = ARQ->TXHOLDQ[FirstUnAcked];
if (Buffer)
{
Debugprintf("Acked %d", FirstUnAcked);
STREAM->BytesAcked += Buffer->Len;
ReleaseBuffer(Buffer);
ARQ->TXHOLDQ[FirstUnAcked] = NULL;
Acked++;
}
}
// Resend anything in TX Buffer (From LastACK to TXSeq
Last = ARQ->TXSeq + 1;
Last &= 63;
First = LastInSeq;
while (First != Last)
{
First++;
First &= 63;
if(ARQ->TXHOLDQ[First])
{
PMSGWITHLEN Buffer = ARQ->TXHOLDQ[First];
UCHAR TXBuffer[300];
SOCKET sock = TNC->TCPDataSock;
int SendLen;
Debugprintf("Resend %d", First);
STREAM->BytesResent += Buffer->Len;
SendLen = sprintf(TXBuffer, "%c", First + 32);
memcpy(&TXBuffer[SendLen], Buffer->Data, Buffer->Len);
SendLen += Buffer->Len;
TXBuffer[SendLen] = 0;
SendPacket(TNC, STREAM, TXBuffer, SendLen);
ARQ->ARQTimer = 10; // wait up to 1 sec for more data before polling
ARQ->Retries = 1;
ARQ->ARQTimerState = ARQ_WAITDATA;
if (Acked == 0)
{
// Nothing acked by this statis message
Acked = 1; // Dont count more thna once
ARQ->NoAckRetries++;
if (ARQ->NoAckRetries > TNC->Retries)
{
// Too many retries - just disconnect
TidyClose(TNC, Stream);
return;
}
}
}
}
UpdateStatsLine(TNC, STREAM);
}