diff --git a/AGWAPI.c b/AGWAPI.c
index 0f2d86c..80c1ce4 100644
--- a/AGWAPI.c
+++ b/AGWAPI.c
@@ -1017,6 +1017,7 @@ int AGWDataSocket_Read(struct AGWSocketConnectionInfo * sockptr, SOCKET sock)
{
int i;
int DataLength;
+ struct AGWHeader * AGW = &sockptr->AGWRXHeader;
ioctlsocket(sock,FIONREAD,&DataLength);
@@ -1028,84 +1029,98 @@ int AGWDataSocket_Read(struct AGWSocketConnectionInfo * sockptr, SOCKET sock)
return 0;
}
-
- if (sockptr->GotHeader)
+ if (DataLength < 36) // A header
{
- // Received a header, without sufficient data bytes
-
- if (DataLength < sockptr->MsgDataLength)
- {
- // Fiddle - seem to be problems somtimes with un-Neagled hosts
-
- Sleep(500);
+ // If we don't get a header within a few ms assume a rogue connection and close it
+
+ int n = 50;
+ while (n--)
+ {
+ Sleep(10);
ioctlsocket(sock,FIONREAD,&DataLength);
+
+ if (DataLength >= 36)
+ break;
}
-
- if (DataLength >= sockptr->MsgDataLength)
- {
- // Read Data and Process Command
- sockptr->MsgData = malloc(sockptr->MsgDataLength);
-
- i = recv(sock, sockptr->MsgData, sockptr->MsgDataLength, 0);
-
- ProcessAGWCommand (sockptr);
- free(sockptr->MsgData);
-
- sockptr->GotHeader = FALSE;
+ if (n < 1)
+ {
+ Debugprintf("Corrupt AGW Packet Received");
+ AGWDataSocket_Disconnect(sockptr);
+ return 0;
}
+ }
- // Not Enough Data - wait
+ // Have a header
- }
- else // Not got header
+ i=recv(sock,(char *)&sockptr->AGWRXHeader, 36, 0);
+
+ if (i == SOCKET_ERROR)
{
- if (DataLength > 35)// A header
- {
- struct AGWHeader * AGW = &sockptr->AGWRXHeader;
+ i=WSAGetLastError();
+ AGWDataSocket_Disconnect(sockptr);
+ }
+
+ sockptr->MsgDataLength = sockptr->AGWRXHeader.DataLength;
- i=recv(sock,(char *)&sockptr->AGWRXHeader, 36, 0);
-
- if (i == SOCKET_ERROR)
- {
- i=WSAGetLastError();
+ // Validate packet to protect against accidental (or malicious!) connects from a non-agw application
- AGWDataSocket_Disconnect(sockptr);
- }
+ if (AGW->Port > 64 || AGW->filler2 != 0 || AGW->filler3 != 0 || AGW->DataLength > 400)
+ {
+ Debugprintf("Corrupt AGW Packet Received");
+ AGWDataSocket_Disconnect(sockptr);
+ return 0;
+ }
+
+ if (sockptr->MsgDataLength == 0)
+ ProcessAGWCommand (sockptr);
+ else
+ sockptr->GotHeader = TRUE; // Wait for data
+ ioctlsocket(sock,FIONREAD,&DataLength); // See if more data
- sockptr->MsgDataLength = sockptr->AGWRXHeader.DataLength;
-
- // Validate packet to protect against accidental (or malicious!) connects from a non-agw application
+ if (sockptr->GotHeader)
+ {
+ // Received a header, without sufficient data bytes
+
+ if (DataLength < sockptr->MsgDataLength)
+ {
+ // Fiddle - seem to be problems somtimes with un-Neagled hosts so wait a few ms
+ // if we don't get a full packet assume a rogue connection and close it
+ int n = 50;
- if (AGW->Port > 64 || AGW->filler2 != 0 || AGW->filler3 != 0 || AGW->DataLength > 400)
+ while (n--)
{
- Debugprintf("Corrupt AGW Packet Received");
- AGWDataSocket_Disconnect(sockptr);
- return 0;
+ Sleep(10);
+ ioctlsocket(sock,FIONREAD,&DataLength);
+
+ if (DataLength >= sockptr->MsgDataLength)
+ break;
}
- if (sockptr->MsgDataLength > 500)
- OutputDebugString("Corrupt AGW message");
-
-
- if (sockptr->MsgDataLength == 0)
- {
- ProcessAGWCommand (sockptr);
- }
- else
+ if (n < 1)
{
- sockptr->GotHeader = TRUE; // Wait for data
+ Debugprintf("Corrupt AGW Packet Received");
+ AGWDataSocket_Disconnect(sockptr);
+ return 0;
}
-
- }
+ }
- // not got 36 bytes
+ if (DataLength >= sockptr->MsgDataLength)
+ {
+ // Read Data and Process Command
+ sockptr->MsgData = malloc(sockptr->MsgDataLength);
+
+ i = recv(sock, sockptr->MsgData, sockptr->MsgDataLength, 0);
+
+ ProcessAGWCommand (sockptr);
+ free(sockptr->MsgData);
+ sockptr->GotHeader = FALSE;
+ }
}
-
return 0;
}
diff --git a/APRSCode.c b/APRSCode.c
index ff1e322..b8f02f0 100644
--- a/APRSCode.c
+++ b/APRSCode.c
@@ -88,7 +88,7 @@ double myDistance(double laa, double loa, BOOL KM);
struct STATIONRECORD * FindStation(char * Call, BOOL AddIfNotFound);
int DecodeAPRSPayload(char * Payload, struct STATIONRECORD * Station);
BOOL KillOldTNC(char * Path);
-int FromLOC(char * Locator, double * pLat, double * pLon);
+
BOOL ToLOC(double Lat, double Lon , char * Locator);
BOOL InternalSendAPRSMessage(char * Text, char * Call);
void UndoTransparency(char * input);
@@ -104,6 +104,7 @@ void ClearSavedMessages();
void GetSavedAPRSMessages();
static VOID GPSDConnect(void * unused);
int CanPortDigi(int Port);
+int FromLOC(char * Locator, double * pLat, double * pLon);
extern int SemHeldByAPI;
extern int APRSMONDECODE();
diff --git a/BBSUtilities.c b/BBSUtilities.c
index 82344bd..56e08a0 100644
--- a/BBSUtilities.c
+++ b/BBSUtilities.c
@@ -5566,14 +5566,19 @@ BOOL CreateMessage(CIRCUIT * conn, char * From, char * ToCall, char * ATBBS, cha
{
if (_memicmp(ToCall, "rms:", 4) == 0)
{
- if (!FindRMS())
+ // Could be ampr.org message
+
+ if (!isAMPRMsg(ToCall))
{
- nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r");
- return FALSE;
+ if (!FindRMS())
+ {
+ nodeprintf(conn, "*** Error - Forwarding via RMS is not configured on this BBS\r");
+ return FALSE;
+ }
}
-
via=strlop(ToCall, ':');
_strupr(ToCall);
+
}
else if (_memicmp(ToCall, "rms/", 4) == 0)
{
@@ -6877,7 +6882,7 @@ int CountMessagestoForward (struct UserInfo * user)
if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber))
{
n++;
- continue; // So we dont count twice in Flag set and NTS MPS
+ continue; // So we dont count twice if Flag set and NTS MPS
}
// if an NTS MPS, also check for any matches
@@ -6918,6 +6923,66 @@ int CountMessagestoForward (struct UserInfo * user)
return n;
}
+int CountBytestoForward (struct UserInfo * user)
+{
+ // See if any messages are queued for this BBS. If so return total bytes queued
+
+ int m, n=0;
+ struct MsgInfo * Msg;
+ int BBSNumber = user->BBSNumber;
+ int FirstMessage = FirstMessageIndextoForward;
+
+ if ((user->flags & F_NTSMPS))
+ FirstMessage = 1;
+
+ for (m = FirstMessage; m <= NumberofMessages; m++)
+ {
+ Msg=MsgHddrPtr[m];
+
+ if ((Msg->status != 'H') && (Msg->status != 'D') && Msg->type && check_fwd_bit(Msg->fbbs, BBSNumber))
+ {
+ n += Msg->length;
+ continue; // So we dont count twice if Flag set and NTS MPS
+ }
+
+ // if an NTS MPS, also check for any matches
+
+ if (Msg->type == 'T' && (user->flags & F_NTSMPS))
+ {
+ struct BBSForwardingInfo * ForwardingInfo = user->ForwardingInfo;
+ int depth;
+
+ if (Msg->status == 'N' && ForwardingInfo)
+ {
+ depth = CheckBBSToForNTS(Msg, ForwardingInfo);
+
+ if (depth > -1 && Msg->Locked == 0)
+ {
+ n += Msg->length;
+ continue;
+ }
+ depth = CheckBBSAtList(Msg, ForwardingInfo, Msg->via);
+
+ if (depth && Msg->Locked == 0)
+ {
+ n += Msg->length;
+ continue;
+ }
+
+ depth = CheckBBSATListWildCarded(Msg, ForwardingInfo, Msg->via);
+
+ if (depth > -1 && Msg->Locked == 0)
+ {
+ n += Msg->length;
+ continue;
+ }
+ }
+ }
+ }
+
+ return n;
+}
+
int ListMessagestoForward(CIRCUIT * conn, struct UserInfo * user)
{
// See if any messages are queued for this BBS
@@ -15823,6 +15888,11 @@ void SendMessageReadEvent(char * call, struct MsgInfo * Msg)
}
}
+void SendMessageForwardedToM0LTE(char * call, struct MsgInfo * Msg)
+{
+}
+
+
void SendNewMessageEvent(char * call, struct MsgInfo * Msg)
{
if (reportMailEvents)
diff --git a/BPQMail.c b/BPQMail.c
index 08d5818..5d26cd5 100644
--- a/BPQMail.c
+++ b/BPQMail.c
@@ -1143,6 +1143,8 @@
// Semaphore calls to SaveConfig
// Include SERVIC as valid from call (for Winlink Service messages) (49)
// Attempt to detect line draw characters in Webmail (50)
+// Fix sending ampr.org mail when RMS is not enabled (51)
+// Send forwarding info tp packetnodes.spots.radio database (51)
#include "bpqmail.h"
#include "winstdint.h"
@@ -1161,6 +1163,8 @@ FARPROCZ pGetLOC;
FARPROCX pRefreshWebMailIndex;
FARPROCX pRunEventProgram;
FARPROCX pGetPortFrequency;
+FARPROCX pSendWebRequest;
+FARPROCX pGetLatLon;
BOOL WINE = FALSE;
@@ -1385,6 +1389,7 @@ char * CheckToAddress(CIRCUIT * conn, char * Addr);
BOOL CheckifPacket(char * Via);
int GetHTMLForms();
VOID GetPGConfig();
+void SendBBSDataToPktMap();
struct _EXCEPTION_POINTERS exinfox;
@@ -1936,6 +1941,8 @@ BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
pRefreshWebMailIndex = GetProcAddress(ExtDriver,"_RefreshWebMailIndex@0");
pRunEventProgram = GetProcAddress(ExtDriver,"_RunEventProgram@8");
pGetPortFrequency = GetProcAddress(ExtDriver,"_GetPortFrequency@8");
+ pSendWebRequest = GetProcAddress(ExtDriver,"_SendWebRequest@16");
+ pGetLatLon = GetProcAddress(ExtDriver,"_GetLatLon@8");
if (pGetLOC)
@@ -2183,6 +2190,13 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
Debugprintf("|Enter HouseKeeping");
DoHouseKeeping(FALSE);
}
+
+ if (APIClock < NOW)
+ {
+ SendBBSDataToPktMap();
+ APIClock = NOW + 7200; // Every 2 hours
+ }
+
tm = gmtime(&NOW);
if (tm->tm_wday == 0) // Sunday
@@ -3057,7 +3071,6 @@ static PSOCKADDR_IN psin;
SOCKET sock;
-
BOOL Initialise()
{
int i, len;
@@ -3383,6 +3396,9 @@ BOOL Initialise()
CreatePipeThread();
GetPGConfig();
+
+ APIClock = 0;
+
return TRUE;
}
diff --git a/BPQMail.vcproj.LAPTOP-Q6S4RP5Q.johnw.user b/BPQMail.vcproj.LAPTOP-Q6S4RP5Q.johnw.user
new file mode 100644
index 0000000..bed4096
--- /dev/null
+++ b/BPQMail.vcproj.LAPTOP-Q6S4RP5Q.johnw.user
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BPQMail.vcproj.SKIGACER.johnw.user b/BPQMail.vcproj.SKIGACER.johnw.user
new file mode 100644
index 0000000..bbece07
--- /dev/null
+++ b/BPQMail.vcproj.SKIGACER.johnw.user
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/BPQWinAPP.vcproj.LAPTOP-Q6S4RP5Q.johnw.user b/BPQWinAPP.vcproj.LAPTOP-Q6S4RP5Q.johnw.user
new file mode 100644
index 0000000..bed4096
--- /dev/null
+++ b/BPQWinAPP.vcproj.LAPTOP-Q6S4RP5Q.johnw.user
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Bpq32.c b/Bpq32.c
index f7e1f59..1125b31 100644
--- a/Bpq32.c
+++ b/Bpq32.c
@@ -1086,7 +1086,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
// Add ? and * wildcards to NODES command (74)
// Add Port RADIO config parameter (74)
-// Version 6.0.24.1 August 2024
+// Version 6.0.24.1 August 2023
// Apply NODES command wildcard to alias as well a call (2)
// Add STOPPORT/STARTPORT to VARA Driver (2)
@@ -1234,6 +1234,7 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
// Add optional ATTACH time limit for VARA (48)
// API format fixes (48)
// AGWAPI Add protection against accidental connects from a non-agw application (50)
+// Save MH and NODES every hour (51)
#define CKernel
@@ -1374,6 +1375,9 @@ extern struct _LINKTABLE * LINKS;
extern int LINK_TABLE_LEN;
extern int MAXLINKS;
+extern double LatFromLOC;
+extern double LonFromLOC;
+
extern int BPQHOSTAPI();
extern int INITIALISEPORTS();
@@ -3068,7 +3072,7 @@ SkipInit:
if (AttachedProcesses < 2)
{
- if (AUTOSAVE == 1)
+ if (AUTOSAVE)
SaveNodes();
if (AUTOSAVEMH)
SaveMH();
@@ -6621,11 +6625,19 @@ int GetListeningPortsPID(int Port)
return 0; // Not found
}
-DllExport char * APIENTRY GetLOC()
+DllExport char * APIENTRY GetLOC()
{
return LOC;
}
+DllExport void APIENTRY GetLatLon(double * lat, double * lon)
+{
+ *lat = LatFromLOC;
+ *lon = LonFromLOC;
+ return;
+}
+
+
// UZ7HO Dll PTT interface
// 1 ext_PTT_info
diff --git a/CBPQ32.vcproj.LAPTOP-Q6S4RP5Q.johnw.user b/CBPQ32.vcproj.LAPTOP-Q6S4RP5Q.johnw.user
new file mode 100644
index 0000000..fa74d31
--- /dev/null
+++ b/CBPQ32.vcproj.LAPTOP-Q6S4RP5Q.johnw.user
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CBPQ32.vcproj.SKIGACER.johnw.user b/CBPQ32.vcproj.SKIGACER.johnw.user
new file mode 100644
index 0000000..6aa33d1
--- /dev/null
+++ b/CBPQ32.vcproj.SKIGACER.johnw.user
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CHeaders.h b/CHeaders.h
index 13cb99d..aeef69c 100644
--- a/CHeaders.h
+++ b/CHeaders.h
@@ -398,6 +398,7 @@ extern int REALTIMETICKS;
extern time_t CurrentSecs;
extern time_t lastSlowSecs;
+extern time_t lastSaveSecs;
// SNMP Variables
diff --git a/Cmd-skigdebian.c b/Cmd-skigdebian.c
new file mode 100644
index 0000000..88189b1
--- /dev/null
+++ b/Cmd-skigdebian.c
@@ -0,0 +1,5989 @@
+/*
+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. S"paclenee 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
+*/
+
+//
+// C replacement for cmd.asm
+//
+#define Kernel
+
+#define _CRT_SECURE_NO_DEPRECATE
+#pragma data_seg("_BPQDATA")
+
+//#include "windows.h"
+//#include "winerror.h"
+
+
+#include "time.h"
+#include "stdio.h"
+#include
+//#include "vmm.h"
+//#include "SHELLAPI.H"
+
+#include "CHeaders.h"
+#include "bpqaprs.h"
+#include "kiss.h"
+
+#pragma pack()
+
+#include "tncinfo.h"
+#include "telnetserver.h"
+
+//#include "GetVersion.h"
+
+//#define DllImport __declspec( dllimport )
+//#define DllExport __declspec( dllexport )
+
+BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR *AXCalls);
+VOID Send_AX_Datagram(PDIGIMESSAGE Block, DWORD Len, UCHAR Port);
+int APIENTRY ClearNodes();
+VOID GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value);
+VOID SendHTTPRequest(SOCKET sock, char * Host, int Port, char * Request, char * Params, int Len, char * Return);
+SOCKET OpenWL2KHTTPSock();
+VOID FormatTime3(char * Time, time_t cTime);
+VOID Format_Addr(unsigned char * Addr, char * Output, BOOL IPV6);
+VOID Tel_Format_Addr(struct ConnectionInfo * sockptr, char * dst);
+VOID FindLostBuffers();
+BOOL CheckCMS(struct TNCINFO * TNC);
+VOID L2SENDXID(struct _LINKTABLE * LINK);
+int CountBits(unsigned long in);
+VOID SaveMH();
+BOOL RestartTNC(struct TNCINFO * TNC);
+void GetPortCTEXT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID WriteMiniDump();
+int CheckKissInterlock(struct PORTCONTROL * PORT, int Exclusive);
+int seeifInterlockneeded(struct PORTCONTROL * PORT);
+
+extern VOID KISSTX();
+
+char COMMANDBUFFER[81] = ""; // Command Hander input buffer
+char OrigCmdBuffer[81] = ""; // Command Hander input buffer before toupper
+
+struct DATAMESSAGE * REPLYBUFFER = NULL;
+UINT APPLMASK = 0;
+UCHAR SAVEDAPPLFLAGS = 0;
+
+UCHAR ALIASINVOKED = 0;
+
+
+VOID * CMDPTR = 0;
+
+short CMDPACLEN = 0;
+
+char OKMSG[] = "Ok\r";
+
+char CMDERRMSG[] = "Invalid command - Enter ? for command list\r";
+#define CMDERRLEN sizeof(CMDERRMSG) - 1
+
+char PASSWORDMSG[] = "Command requires SYSOP status - enter password\r";
+#define LPASSMSG sizeof(PASSWORDMSG) - 1
+
+char CMDLIST[] = "CONNECT BYE INFO NODES PORTS ROUTES USERS MHEARD";
+
+#define CMDLISTLEN sizeof(CMDLIST) - 1
+
+char BADMSG[] = "Bad Parameter\r";
+char BADPORT[] = "Invalid Port Number\r";
+char NOTEXTPORT[] = "Only valid on EXT ports\r";
+char NOVALCALLS[] = "No Valid Calls defined on this port\r";
+
+char BADVALUEMSG[] = "Invalid parameter\r";
+
+char BADCONFIGMSG[] = "Configuration File check falled - will continue with old config\r";
+#ifdef LINBPQ
+char REBOOTOK[] = "Rebooting\r";
+#else
+char REBOOTOK[] = "Rebooting in 20 secs\r";
+#endif
+char REBOOTFAILED[] = "Shutdown failed\r";
+
+char RESTARTOK[] = "Restarting\r";
+char RESTARTFAILED[] = "Restart failed\r";
+
+UCHAR ARDOP[7] = {'A'+'A','R'+'R','D'+'D','O'+'O','P'+'P',' '+' '}; // ARDOP IN AX25
+UCHAR VARA[7] = {'V'+'V','A'+'A','R'+'R','A'+'A',' '+' ',' '+' '}; // VARA IN AX25
+
+int STATSTIME = 0;
+int MAXBUFFS = 0;
+int QCOUNT = 0;
+int MINBUFFCOUNT = 65535;
+int NOBUFFCOUNT = 0;
+int BUFFERWAITS = 0;
+int MAXDESTS = 0;
+int NUMBEROFNODES = 0;
+int L4CONNECTSOUT = 0;
+int L4CONNECTSIN = 0;
+int L4FRAMESTX = 0;
+int L4FRAMESRX = 0;
+int L4FRAMESRETRIED = 0;
+int OLDFRAMES = 0;
+int L3FRAMES = 0;
+
+VOID SENDSABM();
+VOID RESET2();
+
+int APPL1 = 0;
+int PASSCMD = 0;
+
+#pragma pack(1)
+
+struct _EXTPORTDATA DP; // Only way I can think of to get offets to port data into cmd table
+
+char CMDALIAS[ALIASLEN][NumberofAppls] = {0};
+char * ALIASPTR = &CMDALIAS[0][0];
+
+extern int RigReconfigFlag;
+
+CMDX COMMANDS[];
+
+int CMDXLEN = sizeof (CMDX);
+
+VOID SENDNODESMSG();
+VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID SHOWTELNET(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID SHOWAGW(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID SHOWARP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID SHOWNAT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID PING(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID SHOWIPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD);
+void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID APRSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID RECONFIGTELNET (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID HELPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+VOID UZ7HOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD);
+VOID QTSMCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD);
+void hookL2SessionAttempt(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK);
+
+
+
+char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...)
+{
+ // Send Command response checking PACLEN
+
+ char Mess[4096];
+ va_list(arglist);
+ int OldLen;
+ int MsgLen;
+ struct DATAMESSAGE * Buffer;
+ char * Messptr = Mess;
+ int Paclen = Session->SESSPACLEN;
+
+ if (Paclen == 0)
+ Paclen = 255;
+
+ va_start(arglist, format);
+
+ MsgLen = vsprintf(Mess, format, arglist);
+
+ OldLen = (int)(Bufferptr - (char *)REPLYBUFFER->L2DATA);
+
+ while ((OldLen + MsgLen) > Paclen)
+ {
+ // Have to send Paclen then get a new buffer
+
+ int ThisBit = Paclen - OldLen; // What we can send this time
+
+ if (ThisBit < 0)
+ ThisBit = 0; // How can this happen??
+
+ memcpy(Bufferptr, Messptr, ThisBit);
+ Messptr += ThisBit;
+ MsgLen -= ThisBit;
+
+ // QUEUE IT AND GET ANOTHER BUFFER
+
+ Buffer = (struct DATAMESSAGE *)GetBuff();
+
+ if (Buffer == NULL)
+
+ // No buffers, so just reuse the old one (better than crashing !!)
+
+ Buffer = REPLYBUFFER;
+ else
+ SendCommandReply(Session, REPLYBUFFER, Paclen + (4 + sizeof(void *)));
+
+
+ REPLYBUFFER = Buffer;
+ Buffer->PID = 0xf0;
+
+ Bufferptr = &Buffer->L2DATA[0];
+ OldLen = 0;
+ }
+
+ // Add last bit to buffer
+
+ memcpy(Bufferptr, Messptr, MsgLen);
+
+ return Bufferptr + MsgLen;
+}
+
+
+VOID SENDNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ SENDNODESMSG();
+
+ strcpy(Bufferptr, OKMSG);
+ Bufferptr += (int)strlen(OKMSG);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID SAVEMHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ SaveMH();
+
+ strcpy(Bufferptr, OKMSG);
+ Bufferptr += (int)strlen(OKMSG);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID SAVENODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ SaveNodes();
+
+ strcpy(Bufferptr, OKMSG);
+ Bufferptr += (int)strlen(OKMSG);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID DUMPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ WriteMiniDump();
+
+ strcpy(Bufferptr, OKMSG);
+ Bufferptr += (int)strlen(OKMSG);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID RIGRECONFIG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ if (!ProcessConfig())
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Configuration File check falled - will continue with old config");
+ }
+ else
+ {
+ RigReconfigFlag = TRUE;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Rigcontrol Reconfig requested");
+ }
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID REBOOT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ if (Reboot())
+ {
+ strcpy(Bufferptr, REBOOTOK);
+ Bufferptr += (int)strlen(REBOOTOK);
+ }
+ else
+ {
+ strcpy(Bufferptr, REBOOTFAILED);
+ Bufferptr += (int)strlen(REBOOTFAILED);
+ }
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID RESTART(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ if (Restart())
+ {
+ strcpy(Bufferptr, RESTARTOK);
+ Bufferptr += (int)strlen(RESTARTOK);
+ }
+ else
+ {
+ strcpy(Bufferptr, RESTARTFAILED);
+ Bufferptr += (int)strlen(RESTARTFAILED);
+ }
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID RESTARTTNC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ char * ptr, *Context;
+ int portno;
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ {
+ portno = atoi (ptr);
+
+ if (portno && portno < 33)
+ {
+ struct TNCINFO * TNC = TNCInfo[portno];
+
+ if (TNC == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
+ }
+ else
+ {
+ if (TNC->ProgramPath)
+ {
+ if (RestartTNC(TNC))
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Restart %s Ok\r", TNC->ProgramPath);
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Restart %s Failed\r", TNC->ProgramPath);
+ }
+ else
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "PATH not defined so can't restart TNC\r");
+ }
+ }
+ }
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
+
+ }
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+UCHAR VALNODESFLAG = 0, EXTONLY = 0;
+
+VOID PORTVAL (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD);
+
+VOID VALNODES(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ VALNODESFLAG = 1;
+ PORTVAL(Session, Bufferptr, CmdTail, CMD);
+}
+
+VOID EXTPORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ EXTONLY = 1;
+ PORTVAL(Session, Bufferptr, CmdTail, CMD);
+}
+VOID PORTVAL(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // PROCESS PORT VALUE COMMANDS
+
+ char * ptr, *Context, * ptr1;
+ int portno;
+ UCHAR oldvalue, newvalue;
+ struct PORTCONTROL * PORT = PORTTABLE;
+ int n = NUMBEROFPORTS;
+ UCHAR * valueptr;
+
+ // Get port number
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ {
+ portno = atoi (ptr);
+
+ if (portno)
+ {
+ while (n--)
+ {
+ if (PORT->PORTNUMBER == portno)
+ {
+ if (VALNODESFLAG)
+ {
+ char * VNPtr = PORT->PERMITTEDCALLS;
+ char Normcall[10];
+
+ VALNODESFLAG = 0;
+
+ if (VNPtr)
+ {
+ while (VNPtr[0])
+ {
+ Normcall[ConvFromAX25(VNPtr, Normcall)] = 0;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s ", Normcall);
+ VNPtr += 7;
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+ }
+ else
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", NOVALCALLS);
+ }
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+
+ return;
+
+ }
+
+ if (EXTONLY)
+ {
+ // Make sure an Extenal Port
+
+ EXTONLY = 0;
+
+ if (PORT->PORTTYPE != 0x10)
+ {
+ strcpy(Bufferptr, NOTEXTPORT);
+ Bufferptr += (int)strlen(NOTEXTPORT);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ }
+
+ valueptr = (UCHAR *)PORT + CMD->CMDFLAG;
+ oldvalue = *valueptr;
+
+ // Display Param Namee
+
+ ptr1 = &CMD->String[0];
+ n = 12;
+
+ while (*(ptr1) != ' ' && n--)
+ *(Bufferptr++) = *(ptr1++);
+
+ // See if another param - if not, just display current value
+
+ ptr = strtok_s(NULL, " ", &Context);
+
+ if (ptr && ptr[0])
+ {
+ // Get new value
+
+ newvalue = atoi(ptr);
+ *valueptr = newvalue;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue);
+ }
+
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+
+ }
+ PORT = PORT->PORTPOINTER;
+ }
+ }
+ }
+
+ // Bad port
+
+ strcpy(Bufferptr, BADPORT);
+ Bufferptr += (int)strlen(BADPORT);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+
+}
+
+VOID SWITCHVAL (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // Update switch 8 bit value
+
+ char * ptr, *Context, * ptr1;
+ UCHAR oldvalue, newvalue;
+ int n;
+ UCHAR * valueptr;
+
+ valueptr = (UCHAR *)CMD->CMDFLAG;
+
+ oldvalue = *valueptr;
+
+ // Display Param Name
+
+ ptr1 = &CMD->String[0];
+ n = 12;
+
+ while (*(ptr1) != ' ' && n--)
+ *(Bufferptr++) = *(ptr1++);
+
+ // See if a param - if not, just display current value
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr && ptr[0])
+ {
+ // Get new value
+
+ newvalue = atoi(ptr);
+ *valueptr = newvalue;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue);
+
+ if (memcmp(CMD->String, "NODESINT ", 8) == 0)
+ L3TIMER = L3INTERVAL;
+ }
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+
+}
+
+VOID SWITCHVALW (TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // Update switch 16 bit value
+
+ char * ptr, *Context, * ptr1;
+ USHORT oldvalue, newvalue;
+ int n;
+ USHORT * valueptr;
+
+ valueptr = (USHORT *)CMD->CMDFLAG;
+
+ oldvalue = (USHORT)*valueptr;
+
+ // Display Param Name
+
+ ptr1 = &CMD->String[0];
+ n = 12;
+
+ while (*(ptr1) != ' ' && n--)
+ *(Bufferptr++) = *(ptr1++);
+
+ // See if a param - if not, just display current value
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr && ptr[0])
+ {
+ // Get new value
+
+ newvalue = atoi(ptr);
+ *valueptr = newvalue;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, " was %d now %d\r", oldvalue, newvalue);
+ }
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, " %d\r", oldvalue);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+
+}
+
+TRANSPORTENTRY * SetupSessionFromSession(TRANSPORTENTRY * Session, PBPQVECSTRUC HOSTSESS, UINT APPLMASK)
+{
+ // Create a Transport (L4) session linked to an incoming Session
+
+ TRANSPORTENTRY * NewSess = L4TABLE;
+ int Index = 0;
+
+ while (Index < MAXCIRCUITS)
+ {
+ if (NewSess->L4USER[0] == 0)
+ {
+ // Got One
+
+ UCHAR * ourcall = &MYCALL[0];
+
+ Session->L4CROSSLINK = NewSess;
+ NewSess->L4CROSSLINK = Session;
+
+ if (APPLMASK)
+ {
+ // Circuit for APPL - look for an APPLCALL
+
+ APPLCALLS * APPL = APPLCALLTABLE;
+
+ while ((APPLMASK & 1) == 0)
+ {
+ APPLMASK >>= 1;
+ APPL++;
+ }
+ if (APPL->APPLCALL[0] > 0x40) // We have an applcall
+ ourcall = &APPL->APPLCALL[0];
+ }
+
+ memcpy(NewSess->L4USER, ourcall, 7);
+ memcpy(NewSess->L4MYCALL, Session->L4MYCALL, 7);
+
+ NewSess->CIRCUITINDEX = Index; //OUR INDEX
+ NewSess->CIRCUITID = NEXTID;
+
+ NEXTID++;
+ if (NEXTID == 0)
+ NEXTID++; // kEEP nON-ZERO
+
+ NewSess->SESSIONT1 = Session->SESSIONT1;
+ NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW;
+ NewSess->SESSPACLEN = PACLEN; // Default;
+
+ NewSess->L4TARGET.HOST = HOSTSESS;
+ NewSess->L4STATE = 5;
+ return NewSess;
+ }
+ Index++;
+ NewSess++;
+ }
+ return NULL;
+}
+
+extern int GETCONNECTIONINFO();
+
+
+BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions)
+{
+ PBPQVECSTRUC HOSTSESS = BPQHOSTVECTOR;
+ TRANSPORTENTRY * NewSess;
+ int ApplNum;
+ int n = BPQHOSTSTREAMS;
+ int ConfigedPorts = 0;
+
+ // LOOK FOR A FREE HOST SESSION
+
+ while (n--)
+ {
+ if (HOSTSESS->HOSTAPPLMASK & Mask)
+ {
+ // Right appl
+
+ ConfigedPorts++;
+
+ if (HOSTSESS->HOSTSESSION == NULL && (HOSTSESS->HOSTFLAGS & 3) == 0) // Not attached and no report outstanding
+ {
+ // WEVE GOT A FREE BPQ HOST PORT - USE IT
+
+ NewSess = SetupSessionFromSession(Session, HOSTSESS, Mask);
+
+ if (NewSess == NULL)
+ return FALSE; // Appl not available
+
+ HOSTSESS->HOSTSESSION = NewSess;
+
+ // Convert APPLMASK to APPLNUM
+
+ ApplNum = 1;
+
+ while (APPLMASK && (APPLMASK & 1) == 0)
+ {
+ ApplNum++;
+ APPLMASK >>= 1;
+ }
+
+ HOSTSESS->HOSTAPPLNUM = ApplNum;
+
+ HOSTSESS->HOSTFLAGS |= 2; // Indicate State Change
+
+ NewSess->L4CIRCUITTYPE = BPQHOST | DOWNLINK;
+
+ PostStateChange(NewSess);
+
+ NewSess->SESS_APPLFLAGS = HOSTSESS->HOSTAPPLFLAGS;
+
+ NewSess->SESSPACLEN = Paclen;
+
+ return TRUE;
+ }
+ }
+ HOSTSESS++;
+ }
+
+ *AnySessions = ConfigedPorts; // to distinguish between none and all in use
+ return FALSE;
+}
+
+VOID APPLCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ BOOL CONFAILED = 0;
+ UINT CONERROR ;
+ char APPName[13];
+ char * ptr1, *ptr2;
+ int n = 12;
+ BOOL Stay = FALSE;
+
+ // Copy Appl and Null Terminate
+
+ ptr1 = &CMD->String[0];
+ ptr2 = APPName;
+
+ while (*(ptr1) != ' ' && n--)
+ *(ptr2++) = *(ptr1++);
+
+ *(ptr2) = 0;
+
+ if (Session->LISTEN)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Can't use %s while listening\r", APPName);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+
+ if (CmdTail[0] == 'S')
+ Stay = TRUE;
+
+ Session->STAYFLAG = Stay;
+
+ memcpy(Session->APPL, CMD->String, 12);
+
+ // SEE IF THERE IS AN ALIAS DEFINDED FOR THIS COMMAND
+
+ if (ALIASPTR[0] > ' ')
+ {
+ // COPY ALIAS TO COMMAND BUFFER, THEN REENTER COMMAND HANDLER
+
+ int SaveSecure = Session->Secure_Session;
+
+ memcpy(COMMANDBUFFER, ALIASPTR, ALIASLEN);
+ _strupr(COMMANDBUFFER);
+ memcpy(OrigCmdBuffer, ALIASPTR, ALIASLEN); // In case original case version needed
+
+ ALIASINVOKED = 1; // To prevent Alias Loops
+
+ // Set secure session for application alias in case telnet outward connect
+
+ Session->Secure_Session = 1;
+ DoTheCommand(Session);
+ Session->Secure_Session = SaveSecure;
+
+ return;
+ }
+
+ if (cATTACHTOBBS(Session, APPLMASK, CMDPACLEN, &CONERROR) == 0)
+ {
+ // No Streams
+
+ if (CONERROR)
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, All %s Ports are in use - Please try later\r", APPName);
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, Application %s is not running - Please try later\r", APPName);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // IF CMD_TO_APPL SET IN APPLFLAGS, SEND INPUT MSG TO APPL
+
+ if (Session->L4CROSSLINK->SESS_APPLFLAGS & CMD_TO_APPL)
+ {
+ struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)GetBuff();
+ TRANSPORTENTRY * XSession = Session->L4CROSSLINK;
+
+ if (Msg)
+ {
+ COMMANDBUFFER[72] = 13;
+ memcpy(Msg->L2DATA, COMMANDBUFFER, 73);
+ Msg->LENGTH = 73 + 4 + sizeof(void *);
+ Msg->PID = 0xf0;
+
+ C_Q_ADD(&XSession->L4TX_Q, (UINT *)Msg);
+ PostDataAvailable(XSession);
+ }
+ }
+
+ if (Stay)
+ Session->L4CROSSLINK->L4TARGET.HOST->HOSTFLAGS |= 0x20;
+
+ // IF MSG_TO_USER SET, SEND 'CONNECTED' MESSAGE TO USER
+
+ Session->SESS_APPLFLAGS = Session->L4CROSSLINK->SESS_APPLFLAGS;
+
+ if (Session->L4CROSSLINK->SESS_APPLFLAGS & MSG_TO_USER)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Connected to %s\r", APPName);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+
+ // DONT NEED BUFFER ANY MORE
+
+ ReleaseBuffer((UINT *)REPLYBUFFER);
+ return;
+}
+
+
+VOID CMDI00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", INFOMSG);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID CMDV00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ if (sizeof(void *) == 4)
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Version %s\r", VersionString);
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Version %s (64 bit)\r", VersionString);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID BYECMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ CLOSECURRENTSESSION(Session); // Kills any crosslink, plus local link
+ ReleaseBuffer((UINT *)REPLYBUFFER);
+ return;
+}
+
+VOID CMDPAC(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // SET PACLEN FOR THIS SESSION
+
+ char * ptr, *Context;
+ int newvalue;
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr && ptr[0])
+ {
+ // Get new value
+
+ newvalue = atoi(ptr);
+ if (newvalue > 29 && newvalue < 256)
+ Session->SESSPACLEN = newvalue & 0xff;
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "PACLEN - %d\r", Session->SESSPACLEN);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID CMDIDLE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // SET IDLETIME FOR THIS SESSION
+
+ char * ptr, *Context;
+ int newvalue;
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr && ptr[0])
+ {
+ // Get new value
+
+ newvalue = atoi(ptr);
+ if (newvalue > 59 && newvalue < 901)
+ Session->L4LIMIT = newvalue;
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "IDLETIME - %d\r", Session->L4LIMIT);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+
+}
+VOID CMDT00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // SET L4 TIMEOUT FOR CONNECTS ON THIS SESSION
+
+ char * ptr, *Context;
+ int newvalue;
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr && ptr[0])
+ {
+ // Get new value
+
+ newvalue = atoi(ptr);
+ if (newvalue > 20)
+ Session->SESSIONT1 = newvalue;
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "L4TIMEOUT - %d\r", Session->SESSIONT1);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+UCHAR PWLen;
+char PWTEXT[80];
+
+VOID PWDCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ char * ptr, *Context;
+ USHORT pwsum = 0;
+ int n = 5, p1, p2, p3, p4, p5;
+
+ if (Session->Secure_Session) // HOST - SET AUTHORISED REGARDLESS
+ {
+ Session->PASSWORD = 0xFFFF; // SET AUTHORISED
+ Session->Secure_Session = 1;
+ strcpy(Bufferptr, OKMSG);
+ Bufferptr += (int)strlen(OKMSG);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr && ptr[0])
+ {
+ // Check Password
+
+ n = 5;
+
+ while (n--)
+ pwsum += *(ptr++);
+
+ if (Session->PASSWORD == pwsum)
+ {
+ Session->PASSWORD = 0xFFFF; // SET AUTHORISED
+ Session->Secure_Session = 1;
+ strcpy(Bufferptr, OKMSG);
+ Bufferptr += (int)strlen(OKMSG);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ ReleaseBuffer((UINT *)REPLYBUFFER);
+ return;
+ }
+
+ // SEND PASSWORD PROMPT
+
+ if (PWLen == 0)
+ PWLen = 1;
+
+ p1 = rand() % PWLen;
+ pwsum += PWTEXT[p1++];
+
+ p2 = rand() % PWLen;
+ pwsum += PWTEXT[p2++];
+
+ p3 = rand() % PWLen;
+ pwsum += PWTEXT[p3++];
+
+ p4 = rand() % PWLen;
+ pwsum += PWTEXT[p4++];
+
+ p5 = rand() % PWLen;
+ pwsum += PWTEXT[p5++];
+
+ Session->PASSWORD = pwsum;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%d %d %d %d %d\r", p1, p2, p3, p4, p5);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+VOID CMDSTATS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ char * ptr, *Context;
+ int Port = 0, cols = NUMBEROFPORTS, i;
+ char * uptime;
+ struct PORTCONTROL * PORT = PORTTABLE;
+ struct PORTCONTROL * STARTPORT;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ // SEE IF ANY PARAM
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr && ptr[0])
+ Port = atoi(ptr);
+
+ // IF ASKING FOR PORT STATS, DONT DO SYSTEM ONES
+
+ if (Port == 0)
+ {
+ uptime = FormatUptime(STATSTIME);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", uptime);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Semaphore Get-Rel/Clashes %9d%9d\r",
+ Semaphore.Gets - Semaphore.Rels, Semaphore.Clashes);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Buffers:Max/Cur/Min/Out/Wait%9d%9d%9d%9d%9d\r",
+ MAXBUFFS, QCOUNT, MINBUFFCOUNT, NOBUFFCOUNT, BUFFERWAITS);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Known Nodes/Max Nodes %9d%9d\r",
+ NUMBEROFNODES, MAXDESTS);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "L4 Connects Sent/Rxed %9d%9d\r",
+ L4CONNECTSOUT, L4CONNECTSIN);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "L4 Frames TX/RX/Resent/Reseq%9d%9d%9d%9d\r",
+ L4FRAMESTX, L4FRAMESRX, L4FRAMESRETRIED, OLDFRAMES);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "L3 Frames Relayed %9d\r", L3FRAMES);
+
+ if (ptr && ptr[0] == 'S')
+ {
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ }
+
+ // POSITION TO REQUESTED PORT
+
+ if (Port)
+ {
+ while (PORT && PORT->PORTNUMBER != Port)
+ {
+ PORT = PORT->PORTPOINTER;
+ cols--;
+ }
+ }
+
+ if (PORT == NULL) // REQUESTED PORT NOT FOUND
+ {
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ STARTPORT = PORT;
+
+ if (cols > 7)
+ cols = 7;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, " ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Port %02d ", PORT->PORTNUMBER);
+ PORT = PORT->PORTPOINTER;
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Digied");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2DIGIED);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Heard ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMES);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Rxed ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMESFORUS);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Frames Sent ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRAMESSENT);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Timeouts ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2TIMEOUTS);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "REJ Frames Rxed ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2REJCOUNT);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "RX out of Seq ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2OUTOFSEQ);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "L2 Resequenced ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2RESEQ);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Undrun/Poll T/o ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2URUNC);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "RX Overruns ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2ORUNC);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "RX CRC Errors ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->RXERRORS);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "FRMRs Sent ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRMRTX);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "FRMRs Received ");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L2FRMRRX);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Frames abandoned");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%9d", PORT->L1DISCARD);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ PORT = STARTPORT;
+// Bufferptr = Cmdprintf(Session, Bufferptr, "Link Active %% ");
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Active(TX/Busy) %%");
+
+ for (i = 0; i < cols; i++)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %3d ", PORT->AVSENDING, PORT->AVACTIVE);
+ PORT = PORT->PORTPOINTER;
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID CMDL00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // PROCESS 'LINKS' MESSAGE
+
+ struct _LINKTABLE * LINK = LINKS;
+ int n = MAXLINKS;
+ int len;
+ char Normcall[11] = "";
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Links\r");
+
+ while (n--)
+ {
+ if (LINK->LINKCALL[0])
+ {
+ len = ConvFromAX25(LINK->LINKCALL, Normcall);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Normcall);
+
+ len = ConvFromAX25(LINK->OURCALL, Normcall);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Normcall);
+
+ if (LINK->Ver2point2)
+ Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=2.2\r",
+ LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE);
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, " S=%d P=%d T=%d V=%d\r",
+ LINK->L2STATE, LINK->LINKPORT->PORTNUMBER, LINK->LINKTYPE, 2 - LINK->VER1FLAG);
+ }
+ LINK++;
+ }
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+
+VOID CMDS00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // PROCESS 'USERS'
+
+ int n = MAXCIRCUITS;
+ TRANSPORTENTRY * L4 = L4TABLE;
+ TRANSPORTENTRY * Partner;
+ int MaxLinks = MAXLINKS;
+ char State[12] = "", Type[12] = "Uplink";
+ char LHS[50] = "", MID[10] = "", RHS[50] = "";
+ char Line[100];
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s%d)\r", SESSIONHDDR, QCOUNT);
+
+ while (n--)
+ {
+ if (L4->L4USER[0])
+ {
+ RHS[0] = MID[0] = 0;
+
+ if ((L4->L4CIRCUITTYPE & UPLINK) == 0) //SHORT CMDS10A ; YES
+ {
+ // IF DOWNLINK, ONLY DISPLAY IF NO CROSSLINK
+
+ if (L4->L4CROSSLINK == 0) //jne CMDS60 ; WILL PROCESS FROM OTHER END
+ {
+ // ITS A DOWNLINK WITH NO PARTNER - MUST BE A CLOSING SESSION
+ // DISPLAY TO THE RIGHT FOR NOW
+
+ strcpy(LHS, "(Closing) ");
+ DISPLAYCIRCUIT(L4, RHS);
+ goto CMDS50;
+ }
+ else
+ goto CMDS60; // WILL PROCESS FROM OTHER END
+ }
+
+ if (L4->L4CROSSLINK == 0)
+ {
+ // Single Entry
+
+ DISPLAYCIRCUIT(L4, LHS);
+ }
+ else
+ {
+ DISPLAYCIRCUIT(L4, LHS);
+
+ Partner = L4->L4CROSSLINK;
+
+ if (Partner->L4STATE == 5)
+ strcpy(MID, "<-->");
+ else
+ strcpy(MID, "<~~>");
+
+ DISPLAYCIRCUIT(Partner, RHS);
+ }
+CMDS50:
+ memset(Line, 32, 100);
+ memcpy(Line, LHS, (int)strlen(LHS));
+ memcpy(&Line[35], MID, (int)strlen(MID));
+ strcpy(&Line[40], RHS);
+ strcat(&Line[40], "\r");
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Line);
+ }
+CMDS60:
+ L4++;
+ }
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+extern int MasterPort[MAXBPQPORTS+1]; // Pointer to first BPQ port for a specific MPSK or UZ7HO host
+
+VOID CMDP00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // Process PORTS Message
+
+ // If extended show state of TNC (Open, Active, etc)
+
+ struct PORTCONTROL * PORT = PORTTABLE;
+ char Extended = CmdTail[0];
+ struct PORTCONTROL * SAVEPORT;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Ports\r");
+
+ while (PORT)
+ {
+ char Status[32] = "???????";
+ int Portno = PORT->PORTNUMBER;
+
+ if (PORT->Hide)
+ {
+ PORT = PORT->PORTPOINTER;
+ continue;
+ }
+
+ if (Extended != 'E')
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %s\r", PORT->PORTNUMBER, PORT->PORTDESCRIPTION);
+
+ PORT = PORT->PORTPOINTER;
+ continue;
+ }
+
+ // Try to get port status - may not be possible with some
+
+ if (PORT->PortStopped)
+ {
+ strcpy(Status, "Stopped");
+ Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %-7s %s\r", PORT->PORTNUMBER, Status, PORT->PORTDESCRIPTION);
+
+ PORT = PORT->PORTPOINTER;
+ continue;
+ }
+
+ if (PORT->PORTTYPE == 0)
+ {
+ struct KISSINFO * KISS = (struct KISSINFO *)PORT;
+ NPASYINFO Port;
+
+ SAVEPORT = PORT;
+
+ if (KISS->FIRSTPORT && KISS->FIRSTPORT != KISS)
+ {
+ // Not first port on device
+
+ PORT = (struct PORTCONTROL *)KISS->FIRSTPORT;
+ Port = KISSInfo[Portno];
+ }
+
+ Port = KISSInfo[PORT->PORTNUMBER];
+
+ if (Port)
+ {
+ // KISS like - see if connected
+
+ if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE)
+ {
+ // KISS over UDP or TCP
+
+ if (PORT->KISSTCP)
+ {
+ if (Port->Connected)
+ strcpy(Status, "Open ");
+ else
+ if (PORT->KISSSLAVE)
+ strcpy(Status, "Listen");
+ else
+ strcpy(Status, "Closed");
+ }
+ else
+ strcpy(Status, "UDP");
+ }
+ else
+ if (Port->idComDev) // Serial port Open
+ strcpy(Status, "Open ");
+ else
+ strcpy(Status, "Closed");
+
+ PORT = SAVEPORT;
+ }
+ }
+ else if (PORT->PORTTYPE == 14) // Loopback
+ strcpy(Status, "Open ");
+
+ else if (PORT->PORTTYPE == 16) // External
+ {
+ if (PORT->PROTOCOL == 10) // 'HF' Port
+ {
+ struct TNCINFO * TNC = TNCInfo[Portno];
+
+ if (TNC == NULL)
+ {
+ PORT = PORT->PORTPOINTER;
+ continue;
+ }
+
+ switch (TNC->Hardware) // Hardware Type
+ {
+ case H_SCS:
+ case H_KAM:
+ case H_AEA:
+ case H_HAL:
+ case H_TRK:
+ case H_SERIAL:
+
+ // Serial
+
+ if (TNC->hDevice)
+ strcpy(Status, "Open ");
+ else
+ strcpy(Status, "Closed");
+
+ break;
+
+ case H_UZ7HO:
+
+ if (TNCInfo[MasterPort[Portno]]->CONNECTED)
+ strcpy(Status, "Open ");
+ else
+ strcpy(Status, "Closed");
+
+ break;
+
+ case H_WINMOR:
+ case H_V4:
+
+ case H_MPSK:
+ case H_FLDIGI:
+ case H_UIARQ:
+ case H_ARDOP:
+ case H_VARA:
+ case H_KISSHF:
+ case H_WINRPR:
+ case H_FREEDATA:
+
+ // TCP
+
+ if (TNC->CONNECTED)
+ {
+ if (TNC->Streams[0].Attached)
+ strcpy(Status, "In Use");
+ else
+ strcpy(Status, "Open ");
+ }
+ else
+ strcpy(Status, "Closed");
+
+ break;
+
+ case H_TELNET:
+
+ strcpy(Status, "Open ");
+ }
+ }
+ else
+ {
+ // External but not HF - AXIP, BPQETHER VKISS, ??
+
+ struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT;
+
+ strcpy(Status, "Open ");
+ }
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, " %2d %-7s %s\r", PORT->PORTNUMBER, Status, PORT->PORTDESCRIPTION);
+
+ PORT = PORT->PORTPOINTER;
+ }
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+char * DisplayRoute(TRANSPORTENTRY * Session, char * Bufferptr, struct ROUTE * Routes, char Verbose)
+{
+ char Normcall[10];
+ char locked[] = " ! ";
+ int NodeCount;
+ int Percent = 0;
+ char PercentString[20];
+ int Iframes, Retries;
+ char Active[10];
+ int Queued;
+
+ int Port = 0;
+
+ int len = ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall);
+
+ Normcall[9]=0;
+
+ if ((Routes->NEIGHBOUR_FLAG & 1) == 1)
+ strcpy(locked, "!");
+ else
+ strcpy(locked, " ");
+
+ NodeCount = COUNTNODES(Routes);
+
+ if (Routes->NEIGHBOUR_LINK && Routes->NEIGHBOUR_LINK->L2STATE >= 5)
+ strcpy(Active, ">");
+ else
+ strcpy(Active, " ");
+
+ if (Verbose)
+ {
+ if (Routes->NEIGHBOUR_LINK)
+ Queued = COUNT_AT_L2(Routes->NEIGHBOUR_LINK); // SEE HOW MANY QUEUED
+ else
+ Queued = 0;
+
+ Iframes = Routes->NBOUR_IFRAMES;
+ Retries = Routes->NBOUR_RETRIES;
+
+ if (Iframes)
+ {
+ Percent = (Retries * 100) / Iframes;
+ sprintf(PercentString, "%3d%%", Percent);
+ }
+ else
+ strcpy(PercentString, " ");
+
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s%2d %s %3d %3d%s%4d %4d %s %d %d %02d:%02d %d %d",
+ Active, Routes->NEIGHBOUR_PORT, Normcall,
+ Routes->NEIGHBOUR_QUAL, NodeCount, locked, Iframes, Retries, PercentString, Routes->NBOUR_MAXFRAME, Routes->NBOUR_FRACK,
+ Routes->NEIGHBOUR_TIME >> 8, (Routes->NEIGHBOUR_TIME) & 0xff, Queued, Routes->OtherendsRouteQual);
+
+ // IF INP3 DISPLAY SRTT
+
+ if (Routes->INP3Node) // INP3 Enabled?
+ {
+ double srtt = Routes->SRTT/1000.0;
+ double nsrtt = Routes->NeighbourSRTT/1000.0;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, " %4.2fs %4.2fs", srtt, nsrtt);
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+ }
+ else
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s %d %s %d %d%s\r",
+ Active, Routes->NEIGHBOUR_PORT, Normcall, Routes->NEIGHBOUR_QUAL, NodeCount, locked);
+ }
+
+ return Bufferptr;
+}
+
+
+VOID CMDR00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ struct ROUTE * Routes = NEIGHBOURS;
+ int MaxRoutes = MAXNEIGHBOURS;
+ char locked[] = " ! ";
+ int Percent = 0;
+ char * ptr, * Context;
+ char Verbose = 0;
+ int Port = 0;
+ char AXCALL[7];
+ BOOL Found;
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr && (int)strlen(ptr) > 1)
+ {
+ // Route Update
+
+ goto ROUTEUPDATE;
+ }
+
+ if (ptr)
+ {
+ Verbose = ptr[0];
+ ptr = strtok_s(NULL, " ", &Context);
+ if (ptr)
+ Port = atoi(ptr);
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Routes\r");
+
+ while (MaxRoutes--)
+ {
+ if (Routes->NEIGHBOUR_CALL[0] != 0)
+ if (Port == 0 || Port == Routes->NEIGHBOUR_PORT)
+ Bufferptr = DisplayRoute(Session, Bufferptr, Routes, Verbose);
+
+ Routes++;
+ }
+ goto SendReply;
+
+ROUTEUPDATE:
+
+ if (Session->PASSWORD != 0xFFFF)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG);
+ goto SendReply;
+ }
+
+ // Line is
+
+ // ROUTES G8BPQ-2 2 100 - Set quality to 100
+ // ROUTES G8BPQ-2 2 ! - Toggle 'Locked Route' flag
+ // ROUTES G8BPQ-2 2 100 ! - Set quality and toggle 'locked' flag
+
+
+ ConvToAX25(ptr, AXCALL);
+
+ ptr = strtok_s(NULL, " ", &Context);
+
+ if (ptr)
+ Port = atoi(ptr);
+
+ if (Port == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Port Number Missing \r");
+ goto SendReply;
+ }
+
+ Found = FindNeighbour(AXCALL, Port, &Routes);
+
+ if (Context && Context[0] > 32)
+ {
+ // More Params
+
+ ptr = strtok_s(NULL, " ", &Context);
+
+ if (ptr)
+ {
+ // Adding
+
+ memcpy(Routes->NEIGHBOUR_CALL, AXCALL, 7); // In case Add
+ Routes->NEIGHBOUR_PORT = Port;
+ Found = TRUE;
+ }
+
+ if (strcmp(ptr, "!") == 0)
+ {
+ // Toggle Lock
+
+ Routes->NEIGHBOUR_FLAG ^= 1; // FLIP LOCKED BIT
+ goto Displayit;
+ }
+
+ if (strcmp(ptr, "Z") == 0)
+ {
+ // Clear Counts
+
+ Routes->NBOUR_IFRAMES = 0;
+ Routes->NBOUR_RETRIES = 0;
+ goto Displayit;
+ }
+
+ Routes->NEIGHBOUR_QUAL = atoi(ptr);
+
+ if (Context && Context[0] == '!')
+ {
+ // Toggle Lock
+
+ Routes->NEIGHBOUR_FLAG ^= 1; // FLIP LOCKED BIT
+ goto Displayit;
+ }
+ }
+
+Displayit:
+
+ // Just display
+
+ if (Found)
+ Bufferptr = DisplayRoute(Session, Bufferptr, Routes, 1);
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r");
+
+
+
+/* MOV ROUTEDISP,1
+
+ CMP BYTE PTR [ESI],20H
+ JE SHORT JUSTDISPLAY
+
+ MOV ZAPFLAG,0
+
+ CMP BYTE PTR [ESI],'Z'
+ JNE SHORT NOTZAP
+
+ MOV ZAPFLAG,1
+ JMP SHORT JUSTDISPLAY
+
+ PUBLIC NOTZAP
+NOTZAP:
+
+ MOV ROUTEDISP,2 ; LOCK UPDATE
+
+ CMP BYTE PTR [ESI],'!'
+ JE SHORT JUSTDISPLAY
+;
+; LOOK FOR V FOR ADDING A DIGI
+;
+ CMP WORD PTR [ESI],' V' ; V [SPACE]
+ JE ADDDIGI
+
+ CALL GETVALUE ; GET NUMBER, UP TO SPACE , CR OR OFFH
+ JC SHORT BADROUTECMD ; INVALID DIGITS
+
+ MOV NEWROUTEVAL,AL
+
+ MOV ROUTEDISP,0
+
+ CALL SCAN ; SEE IF !
+ MOV AH,[ESI]
+
+
+ PUBLIC JUSTDISPLAY
+JUSTDISPLAY:
+
+
+ MOV ESI,OFFSET32 AX25CALL
+ CALL _FINDNEIGHBOUR
+ JZ SHORT FOUNDROUTE ; IN LIST - OK
+
+ CMP EBX,0
+ JE SHORT BADROUTECMD ; TABLE FULL??
+
+ MOV ECX,7
+ MOV EDI,EBX
+ REP MOVSB ; PUT IN CALL
+
+ MOV AL,SAVEPORT
+ MOV NEIGHBOUR_PORT[EBX],AL
+
+ JMP SHORT FOUNDROUTE
+
+
+ PUBLIC BADROUTECMD
+BADROUTECMD:
+
+ POP EDI
+
+ JMP PBADVALUE
+
+ PUBLIC FOUNDROUTE
+FOUNDROUTE:
+
+ CMP ZAPFLAG,1
+ JNE SHORT NOTCLEARCOUNTS
+
+ XOR AX,AX
+ MOV ES:WORD PTR NBOUR_IFRAMES[EDI],AX
+ MOV ES:WORD PTR NBOUR_IFRAMES+2[EDI],AX
+ MOV ES:WORD PTR NBOUR_RETRIES[EDI],AX
+ MOV ES:WORD PTR NBOUR_RETRIES+2[EDI],AX
+
+ JMP SHORT NOUPDATE
+
+ PUBLIC NOTCLEARCOUNTS
+NOTCLEARCOUNTS:
+
+ CMP ROUTEDISP,1
+ JE SHORT NOUPDATE
+
+ CMP ROUTEDISP,2
+ JE SHORT LOCKUPDATE
+
+ MOV AL,NEWROUTEVAL
+ MOV NEIGHBOUR_QUAL[EBX],AL
+
+ CMP AH,'!'
+ JNE SHORT NOUPDATE
+
+ PUBLIC LOCKUPDATE
+LOCKUPDATE:
+
+ XOR NEIGHBOUR_FLAG[EBX],1 ; FLIP LOCKED BIT
+
+ PUBLIC NOUPDATE
+NOUPDATE:
+
+ MOV ESI,EBX
+ POP EDI
+
+ POP EBX
+ CALL DISPLAYROUTE
+
+ JMP SENDCOMMANDREPLY
+
+ PUBLIC ADDDIGI
+ADDDIGI:
+
+ ADD ESI,2
+ PUSH ESI ; SAVE INPUT BUFFER
+
+ MOV ESI,OFFSET32 AX25CALL
+ CALL _FINDNEIGHBOUR
+
+ POP ESI
+
+ JZ SHORT ADD_FOUND ; IN LIST - OK
+
+ JMP BADROUTECMD
+
+ PUBLIC ADD_FOUND
+ADD_FOUND:
+
+ CALL CONVTOAX25 ; GET DIGI CALLSIGN
+
+ PUSH ESI
+
+ MOV ESI,OFFSET32 AX25CALL
+ LEA EDI,NEIGHBOUR_DIGI[EBX]
+ MOV ECX,7
+ REP MOVSB
+
+ POP ESI ; MSG BUFFER
+;
+; SEE IF ANOTHER DIGI
+;
+ CMP BYTE PTR [ESI],20H
+ JE SHORT NOMORE
+
+ CALL CONVTOAX25 ; GET DIGI CALLSIGN
+ MOV ESI,OFFSET32 AX25CALL
+ LEA EDI,NEIGHBOUR_DIGI+7[EBX]
+ MOV ECX,7
+ REP MOVSB
+
+ PUBLIC NOMORE
+NOMORE:
+
+ JMP NOUPDATE
+
+
+
+*/
+
+SendReply:
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+
+VOID LISTENCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // PROCESS LISTEN COMMAND
+
+ // for monitoring a remote ax.25 port
+
+ int Port = 0, index =0;
+ uint64_t ListenMask = 0;
+ char * ptr, *Context;
+ struct PORTCONTROL * PORT = NULL;
+ char ListenPortList[128] = "";
+
+ ptr = strtok_s(CmdTail, " ,", &Context);
+
+ // Now accepts a list of ports
+
+ if (ptr == 0 || memcmp(ptr, "OFF", 3) == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Listening disabled\r");
+ Session->LISTEN = 0;
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ while (ptr)
+ {
+ Port = atoi(ptr);
+
+ if (Port == 0 && NUMBEROFPORTS == 1)
+ Port = 1;
+
+ ptr = strtok_s(NULL, ", ", &Context); // Get port String
+
+ if (Port)
+ PORT = GetPortTableEntryFromPortNum(Port);
+
+ if (PORT == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port %d\r", Port);
+ continue;
+ }
+
+ if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port %d is not an ax.25 port\r", Port);
+ continue;
+ }
+
+ if (PORT->PORTL3FLAG)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port %d is for internode traffic only\r", Port);
+ continue;
+ }
+
+ if (Session->L4CIRCUITTYPE == L2LINK + UPLINK)
+ {
+ if (Session->L4TARGET.LINK->LINKPORT->PORTNUMBER == Port)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "You can't Listen to the port you are connected on\r");
+ continue;
+ }
+ }
+
+ sprintf(ListenPortList, "%s %d", ListenPortList, Port);
+
+ ListenMask |= ((uint64_t)1 << (Port - 1));
+ }
+
+ Session->LISTEN = ListenMask;
+
+ if (ListenMask)
+ {
+ if (CountBits64(ListenMask) == 1)
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Listening on port%s. Use CQ to send a beacon, LIS to disable\r", ListenPortList);
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Listening on ports%s. Use LIS to disable\r", ListenPortList);
+ }
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+
+VOID UNPROTOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // PROCESS UNPROTO COMMAND
+
+ int Port = 0, index =0;
+ char * ptr, *Context;
+ struct PORTCONTROL * PORT = NULL;
+ UCHAR axcalls[64];
+ BOOL Stay, Spy;
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ Port = atoi(ptr);
+
+ if (Port == 0 && NUMBEROFPORTS == 1)
+ Port = 1;
+ else
+ ptr = strtok_s(NULL, " ", &Context); // Get Unproto String
+
+ if (Port)
+ PORT = GetPortTableEntryFromPortNum(Port);
+
+ if (PORT == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (ptr == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Destination missing\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ ptr[strlen(ptr)] = ' '; // Put param back together
+
+ if (DecodeCallString(ptr, &Stay, &Spy, &axcalls[0]) == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (PORT->PORTL3FLAG)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for internode traffic only\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // Copy Address Info to Session Record
+
+ Session->UNPROTO = Port;
+ Session->UAddrLen = (int)strlen(axcalls);
+ memcpy(Session->UADDRESS, axcalls, 63);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Unproto Mode - enter ctrl/z or /ex to exit\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+VOID CALCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // PROCESS CAL COMMAND
+
+ int Port = 0, index = 0, Count = 0;
+ char * ptr, *Context;
+ struct PORTCONTROL * PORT = NULL;
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ Port = atoi(ptr);
+
+ if (Port == 0 && NUMBEROFPORTS == 1)
+ Port = 1;
+ else
+ ptr = strtok_s(NULL, " ", &Context); // Get Unproto String
+
+ if (Port)
+ PORT = GetPortTableEntryFromPortNum(Port);
+
+ if (PORT == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (PORT->PROTOCOL == 10 && PORT->UICAPABLE == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (ptr == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Count Missing\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ Count = atoi(ptr);
+
+ ptr = strtok_s(NULL, " ", &Context); // Get Unproto String
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Ok\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+
+
+VOID CQCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // Send a CQ Beacon on a radio port. Must be in LISTEN state
+
+ DIGIMESSAGE Msg;
+ int Port = 0;
+ int OneBits = 0;
+ uint64_t MaskCopy = Session->LISTEN;
+ int Len;
+ UCHAR CQCALL[7];
+ char Empty[] = "";
+ char * ptr1 = &OrigCmdBuffer[3];
+ UCHAR * axptr = &Msg.DIGIS[0][0];
+ char * ptr2, *Context;
+
+ while (MaskCopy)
+ {
+ if (MaskCopy & 1)
+ OneBits++;
+
+ Port++;
+ MaskCopy = MaskCopy >> 1;
+ }
+
+ if (OneBits == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "You must enter LISTEN before calling CQ\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (OneBits > 1)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "You can't call CQ if LISTENing on more than one port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+
+ Len = (int)strlen(OrigCmdBuffer) - 3;
+
+ if (Len < 0)
+ Len = 0;
+
+ memset(&Msg, 0, sizeof(Msg));
+
+ Msg.PORT = Port;
+ Msg.CTL = 3; // UI
+
+ // see if a Via specified
+
+ if (_memicmp(ptr1, "via ", 4) == 0)
+ {
+ ptr2 = strtok_s(ptr1 + 4, ",", &Context);
+
+ while (ptr2)
+ {
+ if (ConvToAX25(ptr2, axptr) == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid via string\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ axptr += 7;
+
+ if (axptr == &Msg.DIGIS[7][0])
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Too many digis\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ ptr1 = ptr2;
+ ptr2 = strtok_s(NULL, ",", &Context);
+ }
+
+ // ptr1 is start of last digi call. We need to position to data
+
+ ptr1 = strchr(ptr1, ' ');
+
+ if (ptr1 == NULL)
+ ptr1 = Empty;
+ else
+ ptr1++ ; // to message
+
+ Len = (int)strlen(ptr1);
+
+ }
+
+ ConvToAX25("CQ", CQCALL);
+ memcpy(Msg.DEST, CQCALL, 7);
+ Msg.DEST[6] |= 0x80; // set Command Bit
+ memcpy(Msg.ORIGIN, Session->L4USER, 7);
+ Msg.ORIGIN[6] ^= 0x1e; // Flip SSID
+ Msg.PID = 0xf0; // Data PID
+ memcpy(&Msg.L2DATA, ptr1, Len);
+
+ Send_AX_Datagram(&Msg, Len + 2, Port); // Len is Payload ie CTL, PID and Data
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "CQ sent\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+
+}
+
+
+TRANSPORTENTRY * SetupNewSession(TRANSPORTENTRY * Session, char * Bufferptr)
+{
+ TRANSPORTENTRY * NewSess = L4TABLE;
+ int Index = 0;
+
+ while (Index < MAXCIRCUITS)
+ {
+ if (NewSess->L4USER[0] == 0)
+ {
+ // Got One
+
+ Session->L4CROSSLINK = NewSess;
+ NewSess->L4CROSSLINK = Session;
+
+ memcpy(NewSess->L4USER, Session->L4USER, 7);
+ memcpy(NewSess->L4MYCALL, Session->L4MYCALL, 7);
+
+
+ NewSess->CIRCUITINDEX = Index; //OUR INDEX
+ NewSess->CIRCUITID = NEXTID;
+
+ NEXTID++;
+ if (NEXTID == 0)
+ NEXTID++; // kEEP nON-ZERO
+
+ NewSess->SESSIONT1 = Session->SESSIONT1;
+ NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW;
+
+ return NewSess;
+ }
+ Index++;
+ NewSess++;
+ }
+
+ if (Bufferptr)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - System Tables Full\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ }
+
+ return NULL;
+}
+
+
+VOID DoNetromConnect(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest, BOOL Spy)
+{
+ TRANSPORTENTRY * NewSess;
+
+ NewSess = SetupNewSession(Session, Bufferptr);
+
+ if (NewSess == NULL)
+ return; // Tables Full
+
+ NewSess->L4CIRCUITTYPE = SESSION + DOWNLINK;
+
+ NewSess->L4TARGET.DEST = Dest;
+ NewSess->L4STATE = 2; // CONNECTING
+
+ NewSess->SPYFLAG = Spy;
+
+ ReleaseBuffer((UINT *)REPLYBUFFER);
+
+ SENDL4CONNECT(NewSess);
+
+ L4CONNECTSOUT++;
+
+ return;
+}
+
+BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK)
+{
+ struct _LINKTABLE * LINK = LINKS;
+ struct _LINKTABLE * FIRSTSPARE = NULL;
+ int n = MAXLINKS;
+
+ while (n--)
+ {
+ if (LINK->LINKCALL[0] == 0) // Spare
+ {
+ if (FIRSTSPARE == NULL)
+ FIRSTSPARE = LINK;
+
+ LINK++;
+ continue;
+ }
+
+ if ((LINK->LINKPORT->PORTNUMBER == Port) && CompareCalls(LINK->LINKCALL, LinkCall) && CompareCalls(LINK->OURCALL, OurCall))
+ {
+ *REQLINK = LINK;
+ return TRUE;
+ }
+
+ LINK++;
+ }
+ // ENTRY NOT FOUND - FIRSTSPARE HAS FIRST FREE ENTRY, OR ZERO IF TABLE FULL
+
+ *REQLINK = FIRSTSPARE;
+ return FALSE;
+}
+
+VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD);
+
+VOID CMDC00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // PROCESS CONNECT COMMAND
+
+ TRANSPORTENTRY * NewSess;
+
+ int CONNECTPORT, Port;
+ BOOL CallEvenIfInNodes = FALSE;
+ char * ptr, *Context;
+ UCHAR axcalls[64];
+ UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted
+ int ret;
+ struct PORTCONTROL * PORT = PORTTABLE;
+ struct _LINKTABLE * LINK;
+ int CQFLAG = 0; // NOT CQ CALL
+ BOOL Stay, Spy;
+ int n;
+ char TextCall[10];
+ int TextCallLen;
+ char PortString[10];
+ char cmdCopy[256];
+ struct _EXTPORTDATA * EXTPORT = (struct _EXTPORTDATA *)PORT;
+ char toCall[12], fromCall[12];
+
+#ifdef EXCLUDEBITS
+
+ if (CheckExcludeList(Session->L4USER) == FALSE)
+ {
+ // CONNECTS FROM THIS STATION ARE NOT ALLOWED
+
+ ReleaseBuffer((UINT *)REPLYBUFFER);
+ return;
+ }
+
+#endif
+
+ if (Session->LISTEN)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Can't connect while listening\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ CONNECTPORT = 0; // NO PORT SPECIFIED
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ strcpy(cmdCopy, Context); // Save in case Telnet Connect
+
+ if (ptr == 0)
+ {
+ // No param
+
+ if (CFLAG) // C Command Disabled ?
+ {
+ // Convert to HOST (appl 32) command
+
+ //MOV _CMDPTR,OFFSET32 _HOSTCMD
+ //MOV _ALIASPTR,OFFSET32 _HOSTCMD + 32 * 31
+
+ //MOV _APPLMASK, 80000000H ; Internal Term
+
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ Port = atoi(ptr);
+
+ if (Port)
+ {
+ // IF THERE IS NOTHING FOLLOWING THE NUMBER, ASSUME IT IS A
+ // NUMERIC ALIAS INSTEAD OF A PORT
+
+ sprintf(PortString, "%d", Port);
+
+ if (strlen(PortString) < (int)strlen(ptr))
+ goto NoPort;
+
+ PORT = GetPortTableEntryFromPortNum(Port);
+
+ if (PORT == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ EXTPORT = (struct _EXTPORTDATA *)PORT;
+
+ ptr = strtok_s(NULL, " ", &Context);
+
+ if (ptr == 0)
+ {
+ // No param
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ CONNECTPORT = Port;
+
+ if (strcmp(ptr, "CMS") == 0 || strcmp(ptr, "HOST") == 0) // In case someeone has CMS or HOST as an alias
+ goto Downlink;
+
+ }
+
+NoPort:
+
+ ptr[strlen(ptr)] = ' '; // Put param back together
+
+ if (ptr[0] == '!')
+ {
+ CallEvenIfInNodes = TRUE;
+ ptr++;
+ }
+
+ if (memcmp(ptr, "RELAY ", 5) == 0 || memcmp(ptr, "SYNC ", 5) == 0)
+ {
+ // c p relay with extra parms
+
+ goto Downlink;
+ }
+
+ // Skip call validation if using a ptc to allow 1:call, 2:call format
+
+ if (Port && PORT->PROTOCOL == 10 && memcmp(EXTPORT->PORT_DLL_NAME, "SCSPACTOR", 9) == 0)
+ {
+ char * p;
+
+ if (p = strstr(cmdCopy, " S "))
+ {
+ Stay = TRUE;
+ p++;
+ *p = ' ';
+ }
+
+ if (p = strstr(cmdCopy, " Z "))
+ {
+ Spy = TRUE;
+ p++;
+ *p = ' ';
+ }
+
+ goto Downlink;
+ }
+ else
+ {
+ if (DecodeCallString(ptr, &Stay, &Spy, &axcalls[0]) == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Call\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ }
+
+ Session->STAYFLAG = Stay;
+
+ TextCallLen = ConvFromAX25(axcalls, TextCall);
+
+ if (CallEvenIfInNodes)
+ goto Downlink;
+
+ // SEE IF CALL TO ANY OF OUR HOST SESSIONS - UNLESS DIGIS SPECIFIED
+
+ if (axcalls[7] == 0)
+ {
+ // If this connect is as a result of a command alias, don't check appls or we will loop
+
+ if (ALIASINVOKED == 0)
+ {
+ APPLCALLS * APPL = APPLCALLTABLE;
+ int n = NumberofAppls;
+ APPLMASK = 1;
+
+ while (n--)
+ {
+ if (memcmp(axcalls, APPL->APPLALIAS, 6) == 0 || CompareCalls(axcalls, APPL->APPLCALL))
+ {
+ // Call to an appl
+
+ // Convert to an APPL command, so any alias is actioned
+
+ // SEE IF THERE IS AN ALIAS DEFINDED FOR THIS COMMAND
+
+ if (APPL->APPLHASALIAS && APPL->APPLALIASVAL[0] != 0x20)
+ {
+ // COPY ALIAS TO COMMAND _BUFFER, THEN REENTER COMMAND HANDLER
+
+ memcpy(COMMANDBUFFER, APPL->APPLALIASVAL, ALIASLEN);
+ COMMANDBUFFER[80] = 0;
+ _strupr(COMMANDBUFFER);
+ memcpy(OrigCmdBuffer, APPL->APPLALIASVAL, ALIASLEN); // In case original case version needed
+
+ ALIASINVOKED = TRUE; // To prevent Alias Loops
+ }
+ else
+ {
+
+ // Copy Appl Command to Command Buffer. Ensure doesn't contain old command
+
+ memset(COMMANDBUFFER, ' ', 72);
+ memcpy(COMMANDBUFFER, APPL->APPLCMD, 12);
+ }
+ DoTheCommand(Session);
+ return;
+ }
+ APPL++;
+ APPLMASK <<= 1;
+ }
+ }
+ }
+
+ if (axcalls[7] == 0)
+ {
+ // SEE IF CALL TO ANOTHER NODE
+
+ struct DEST_LIST * Dest = DESTS;
+ int n = MAXDESTS;
+
+ if (axcalls[6] == 0x60) // if SSID, dont check aliases
+ {
+ while (n--)
+ {
+ if (memcmp(Dest->DEST_ALIAS, TextCall, 6) == 0)
+ {
+ DoNetromConnect(Session, Bufferptr, Dest, Spy);
+ return;
+ }
+ Dest++;
+ }
+ }
+
+ Dest = DESTS;
+ n = MAXDESTS;
+
+ while (n--)
+ {
+ if (CompareCalls(Dest->DEST_CALL, axcalls))
+ {
+ DoNetromConnect(Session, Bufferptr, Dest, Spy);
+ return;
+ }
+ Dest++;
+ }
+ }
+
+ // Must be Downlink Connect
+
+Downlink:
+
+ if (CONNECTPORT == 0 && NUMBEROFPORTS > 1)
+ {
+ // L2 NEEDS PORT NUMBER
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Downlink connect needs port number - C P CALLSIGN\r");
+
+ // Send Port List
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // ENSURE PORT IS AVAILABLE FOR L2 USE
+
+ if (PORT->PROTOCOL >= 10) // Pactor=-style port?
+ {
+ int count;
+
+ // if Via PACTOR ARDOP WINMOR or VARA, convert to attach and call = Digi's are in AX25STRING (+7)
+
+ if (memcmp(&axcalls[7], &WINMOR[0], 6) == 0 ||
+ memcmp(&axcalls[7], &ARDOP[0], 6) == 0 ||
+ memcmp(&axcalls[7], &VARA[0], 6) == 0 ||
+ memcmp(&axcalls[7], &PACTORCALL[0], 6) == 0)
+ {
+ char newcmd[80];
+
+ TextCall[TextCallLen] = 0;
+ sprintf(newcmd, "%s %s", CmdTail, TextCall);
+
+ ATTACHCMD(Session, Bufferptr, newcmd, NULL);
+ return;
+ }
+
+ // If on a KAM or SCS with ax.25 on port 2, do an Attach command, then pass on connect
+
+ if (EXTPORT->MAXHOSTMODESESSIONS <= 1)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is not an ax.25 port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // Only Allow Attach VHF from Secure Applications or if PERMITGATEWAY is set
+
+ if (EXTPORT->PERMITGATEWAY == 0 && Session->Secure_Session == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, you are not allowed to use this port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ count = EXTPORT->MAXHOSTMODESESSIONS;
+ count--; // First is Pactor Stream, count is now last ax.25 session
+
+ while (count)
+ {
+ if (EXTPORT->ATTACHEDSESSIONS[count] == 0)
+ {
+ int Paclen, PortPaclen;
+ struct DATAMESSAGE * Buffer;
+ struct DATAMESSAGE Message = {0};
+ char Callstring[80];
+ int len;
+
+ // Found a free one - use it
+
+ // See if TNC is OK
+
+ Message.PORT = count;
+
+ ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT);
+
+ if ((ret & 0xff00) == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK
+
+ NewSess = SetupNewSession(Session, Bufferptr);
+ if (NewSess == NULL)
+ return;
+
+ // if a UZ7HO port, and the uplink is L2 or Uz7HO invert SSID bits
+
+ // We only get here if multisession
+
+ if (memcmp(EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0)
+ goto noFlip;
+
+ if ((Session->L4CIRCUITTYPE & BPQHOST))// host
+ goto noFlip;
+
+ if ((Session->L4CIRCUITTYPE & PACTOR))
+ {
+ // incoming is Pactorlike - see if UZ7HO
+
+ if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0)
+ goto noFlip;
+ else
+ NewSess->L4USER[6] ^= 0x1e; // UZ7HO Uplink - flip
+ }
+ else
+
+ // Must be L2 uplink - flip
+
+ NewSess->L4USER[6] ^= 0x1e; // Flip SSID
+noFlip:
+ EXTPORT->ATTACHEDSESSIONS[count] = NewSess;
+
+ NewSess->KAMSESSION = count;
+
+ // Set paclen to lower of incoming and outgoing
+
+ Paclen = Session->SESSPACLEN; // Incoming PACLEN
+
+ if (Paclen == 0)
+ Paclen = 256; // 0 = 256
+
+ PortPaclen = PORT->PORTPACLEN;
+
+ if (PortPaclen == 0)
+ PortPaclen = 256; // 0 = 256
+
+ if (PortPaclen < Paclen)
+ Paclen = PortPaclen;
+
+ NewSess->SESSPACLEN = Paclen;
+ Session->SESSPACLEN = Paclen;
+
+ NewSess->L4STATE = 5;
+ NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR;
+ NewSess->L4TARGET.PORT = PORT;
+
+ // Send the connect command to the TNC
+
+ Buffer = REPLYBUFFER;
+
+ Buffer->PORT = count;
+ Buffer->PID = 0xf0;
+
+ // if on Telnet Port convert use original cmd tail
+
+ // Why just on telnet - what not all ports??
+
+ if (memcmp(EXTPORT->PORT_DLL_NAME, "TELNET", 6) == 0 || memcmp(EXTPORT->PORT_DLL_NAME, "SCSPACTOR", 9) == 0)
+ {
+ NewSess->Secure_Session = Session->Secure_Session;
+ len = sprintf(Callstring,"C %s", cmdCopy);
+ }
+ else
+ {
+ TextCall[TextCallLen] = 0;
+
+ len = sprintf(Callstring,"C %s", TextCall);
+
+ if (axcalls[7])
+ {
+ int digi = 7;
+
+ // we have digis
+
+ len += sprintf(&Callstring[len], " via");
+
+ while (axcalls[digi])
+ {
+ TextCall[ConvFromAX25(&axcalls[digi], TextCall)] = 0;
+ len += sprintf(&Callstring[len], " %s", TextCall);
+ digi += 7;
+ }
+ }
+ }
+ Callstring[len++] = 13;
+ Callstring[len] = 0;
+
+ Buffer->LENGTH = len + MSGHDDRLEN + 1;
+ memcpy(Buffer->L2DATA, Callstring, len);
+ C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer);
+
+ return;
+ }
+ count--;
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Error - No free streams on this port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if ((Session->L4CIRCUITTYPE & BPQHOST) == 0 && PORT->PORTL3FLAG)
+ {
+ //Port only for L3
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for internode traffic only\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (PORT->PortUIONLY)
+ {
+ //Port only for UI
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, port is for UI traffic only\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ ret = CheckKissInterlock(PORT, TRUE);
+
+ if (ret)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, Interlocked port %d is in use\r", ret);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+
+ if (Session->L4USER[6] == 0x42 || Session->L4USER[6] == 0x44)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - Can't make ax.25 calls with SSID of T or R\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // Get Session Entry for Downlink
+
+ NewSess = SetupNewSession(Session, Bufferptr);
+ if (NewSess == NULL)
+ return;
+
+ NewSess->L4CIRCUITTYPE = L2LINK + DOWNLINK;
+
+ // FORMAT LINK TABLE ENTRY FOR THIS CONNECTION
+
+ memcpy(ourcall, NewSess->L4USER, 7);
+
+ // SSID SWAP TEST - LEAVE ALONE FOR HOST or Pactor like (unless UZ7HO)
+
+ if ((Session->L4CIRCUITTYPE & BPQHOST))// host
+ goto noFlip3;
+
+ if ((Session->L4CIRCUITTYPE & PACTOR))
+ {
+ // incoming is Pactorlike - see if UZ7HO
+
+ if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0)
+ goto noFlip3;
+
+ if (Session->L4TARGET.EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession
+ goto noFlip3;
+
+ ourcall[6] ^= 0x1e; // UZ7HO Uplink - flip
+ }
+ else
+
+ // Must be L2 uplink - flip
+
+ ourcall[6] ^= 0x1e; // Flip SSID
+
+noFlip3:
+
+ // SET UP NEW SESSION (OR RESET EXISTING ONE)
+
+ FindLink(axcalls, ourcall, Port, &LINK);
+
+ if (LINK == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry - System Tables Full\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+
+ // Should release NewSess
+
+ return;
+ }
+
+ memcpy(LINK->LINKCALL, axcalls, 7);
+ memcpy(LINK->OURCALL, ourcall, 7);
+
+ LINK->LINKPORT = PORT;
+
+ LINK->L2TIME = PORT->PORTT1;
+
+ // Copy Digis
+
+ n = 7;
+ ptr = &LINK->DIGIS[0];
+
+ while (axcalls[n])
+ {
+ memcpy(ptr, &axcalls[n], 7);
+ n += 7;
+ ptr += 7;
+
+ LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI
+ }
+
+ LINK->LINKTYPE = 2; // DOWNLINK
+ LINK->LINKWINDOW = PORT->PORTWINDOW;
+
+ RESET2(LINK); // RESET ALL FLAGS
+
+ toCall[ConvFromAX25(LINK->LINKCALL, toCall)] = 0;
+ fromCall[ConvFromAX25(LINK->OURCALL, fromCall)] = 0;
+
+ hookL2SessionAttempt(CONNECTPORT, fromCall, toCall, LINK);
+
+
+ if (CMD->String[0] == 'N' && SUPPORT2point2)
+ LINK->L2STATE = 1; // New (2.2) send XID
+ else
+ LINK->L2STATE = 2; // Send SABM
+
+ LINK->CIRCUITPOINTER = NewSess;
+
+ NewSess->L4TARGET.LINK = LINK;
+
+ if (PORT->PORTPACLEN)
+ NewSess->SESSPACLEN = Session->SESSPACLEN = PORT->PORTPACLEN;
+
+ if (CQFLAG == 0) // if a CQ CALL DONT SEND SABM
+ {
+ seeifInterlockneeded(PORT);
+
+ if (LINK->L2STATE == 1)
+ L2SENDXID(LINK);
+ else
+ SENDSABM(LINK);
+ }
+ ReleaseBuffer((UINT *)REPLYBUFFER);
+ return;
+}
+
+BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls)
+{
+ // CONVERT CALL + OPTIONAL DIGI STRING TO AX25, RETURN
+ // CONVERTED STRING IN AXCALLS. Return FALSE if invalied
+
+ char * axptr = AXCalls;
+ char * ptr, *Context;
+ int CQFLAG = 0; // NOT CQ CALL
+ int n = 8; // Max digis
+
+ *Stay = 0;
+ *Spy = 0;
+
+ memset(AXCalls, 0, 64);
+
+ ptr = strtok_s(Calls, " ,", &Context);
+
+ if (ptr == NULL)
+ return FALSE;
+
+ // First field is Call
+
+ if (ConvToAX25(ptr, axptr) == 0)
+ return FALSE;
+
+ axptr += 7;
+
+ ptr = strtok_s(NULL, " ,", &Context);
+
+ while (ptr && n--)
+ {
+ // NEXT FIELD = COULD BE CALLSIGN, VIA, OR S (FOR STAY)
+
+ if (strcmp(ptr, "S") == 0)
+ *Stay = TRUE;
+ else if (strcmp(ptr, "Z") == 0)
+ *Spy = TRUE;
+ else if (memcmp(ptr, "VIA", (int)strlen(ptr)) == 0)
+ {
+ } //skip via
+ else
+ {
+ // Convert next digi
+
+ if (ConvToAX25(ptr, axptr) == 0)
+ return FALSE;
+
+ axptr += 7;
+ }
+
+ ptr = strtok_s(NULL, " ,", &Context);
+ }
+
+ return TRUE;
+}
+
+
+VOID LINKCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // PROCESS *** LINKED to CALLSIGN
+
+ char * ptr, *Context;
+ UCHAR axcall[7];
+ int ret;
+
+ if (LINKEDFLAG == 'Y' || // UNCONDITIONAL?
+ (LINKEDFLAG == 'A' &&
+ ((Session->L4CIRCUITTYPE & BPQHOST) || Session->Secure_Session || Session->PASSWORD == 0xffff)))
+ {
+ ptr = strtok_s(CmdTail, " ", &Context);
+ if (ptr)
+ ptr = strtok_s(NULL, " ", &Context);
+
+ if (ptr)
+ {
+ ret = ConvToAX25Ex(ptr, axcall);
+
+ if (ret)
+ {
+ memcpy(Session->L4USER, axcall, 7);
+ strcpy(Bufferptr, OKMSG);
+ Bufferptr += (int)strlen(OKMSG);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ }
+
+ strcpy(Bufferptr, BADMSG);
+ Bufferptr += (int)strlen(BADMSG);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ memcpy(Bufferptr, PASSWORDMSG, LPASSMSG);
+ Bufferptr += LPASSMSG;
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+int CompareNode(const void *a, const void *b);
+int CompareAlias(const void *a, const void *b);
+
+char * DoOneNode(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest)
+{
+ char Normcall[10];
+ char Alias[10];
+ struct NR_DEST_ROUTE_ENTRY * NRRoute;
+ struct DEST_ROUTE_ENTRY * Route;
+ struct ROUTE * Neighbour;
+ int i, Active, len;
+
+ Alias[6] = 0;
+
+ memcpy(Alias, Dest->DEST_ALIAS, 6);
+ strlop(Alias, ' ');
+
+ Normcall[ConvFromAX25(Dest->DEST_CALL, Normcall)] = 0;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Routes to: %s:%s", Alias, Normcall);
+
+ if (Dest->DEST_COUNT)
+ Bufferptr = Cmdprintf(Session, Bufferptr, " RTT=%4.2f FR=%d %c %.1d\r",
+ Dest->DEST_RTT /1000.0, Dest->DEST_COUNT,
+ (Dest->DEST_STATE & 0x40)? 'B':' ', (Dest->DEST_STATE & 63));
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ NRRoute = &Dest->NRROUTE[0];
+
+ Active = Dest->DEST_ROUTE;
+
+ for (i = 1; i < 4; i++)
+ {
+ Neighbour = NRRoute->ROUT_NEIGHBOUR;
+
+ if (Neighbour)
+ {
+ len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall);
+ Normcall[len] = 0;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%c %d %d %d %s\r",
+ (Active == i)?'>':' ',NRRoute->ROUT_QUALITY, NRRoute->ROUT_OBSCOUNT, Neighbour->NEIGHBOUR_PORT, Normcall);
+ }
+ NRRoute++;
+ }
+
+ // DISPLAY INP3 ROUTES
+
+ Route = &Dest->ROUTE[0];
+
+ Active = Dest->DEST_ROUTE;
+
+ for (i = 1; i < 4; i++)
+ {
+ Neighbour = Route->ROUT_NEIGHBOUR;
+
+ if (Neighbour)
+ {
+ double srtt = Route->SRTT/1000.0;
+
+ len = ConvFromAX25(Neighbour->NEIGHBOUR_CALL, Normcall);
+ Normcall[len] = 0;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%c %d %4.2fs %d %s\r",
+ (Active == i + 3)?'>':' ',Route->Hops, srtt, Neighbour->NEIGHBOUR_PORT, Normcall);
+ }
+ Route++;
+ }
+
+ return Bufferptr;
+}
+
+
+int DoViaEntry(struct DEST_LIST * Dest, int n, char * line, int cursor)
+{
+ char Portcall[10];
+ int len;
+
+ if (Dest->NRROUTE[n].ROUT_NEIGHBOUR != 0 && Dest->NRROUTE[n].ROUT_NEIGHBOUR->INP3Node == 0)
+ {
+ len=ConvFromAX25(Dest->NRROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall);
+ Portcall[len]=0;
+
+ len=sprintf(&line[cursor],"%s %d %d ",
+ Portcall,
+ Dest->NRROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT,
+ Dest->NRROUTE[n].ROUT_QUALITY);
+
+ cursor+=len;
+
+ if (Dest->NRROUTE[n].ROUT_OBSCOUNT > 127)
+ {
+ len=sprintf(&line[cursor],"! ");
+ cursor+=len;
+ }
+ }
+ return cursor;
+}
+
+int DoINP3ViaEntry(struct DEST_LIST * Dest, int n, char * line, int cursor)
+{
+ char Portcall[10];
+ int len;
+ double srtt;
+
+ if (Dest->ROUTE[n].ROUT_NEIGHBOUR != 0)
+ {
+ srtt = Dest->ROUTE[n].SRTT/1000.0;
+
+ len=ConvFromAX25(Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, Portcall);
+ Portcall[len]=0;
+
+ len=sprintf(&line[cursor],"%s %d %d %4.2fs ",
+ Portcall,
+ Dest->ROUTE[n].ROUT_NEIGHBOUR->NEIGHBOUR_PORT,
+ Dest->ROUTE[n].Hops, srtt);
+
+ cursor+=len;
+
+ if (Dest->NRROUTE[n].ROUT_OBSCOUNT > 127)
+ {
+ len=sprintf(&line[cursor],"! ");
+ cursor+=len;
+ }
+ }
+ return cursor;
+}
+
+int WildCmp(char * pattern, char * string)
+{
+ // Check if string is at end or not.
+
+ if (*pattern == '\0')
+ return *string == '\0';
+
+ // Check for single character missing or match
+
+ if (*pattern == '?' || *pattern == *string)
+ return *string != '\0' && WildCmp(pattern + 1, string + 1);
+
+ if (*pattern == '*')
+ {
+ // Check for multiple character missing
+
+ return WildCmp(pattern + 1, string) || (*string != '\0' && WildCmp(pattern, string + 1));
+ }
+
+ return 0;
+}
+
+VOID CMDN00(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ struct DEST_LIST * Dest = DESTS;
+ int count = MAXDESTS, i;
+ char Normcall[10];
+ char Alias[10];
+ int Width = 4;
+ int x = 0, n = 0;
+ struct DEST_LIST * List[1000];
+ char Param = 0;
+ char * ptr, * param2,* Context;
+ char Nodeline[21];
+ char AXCALL[7];
+ char * Call;
+ char * Qualptr;
+ int Qual;
+ char line[160];
+ int cursor, len;
+ UCHAR axcall[7];
+ int SavedOBSINIT = OBSINIT;
+ struct ROUTE * ROUTE = NULL;
+ char Pattern[80] = "";
+ char * firststar;
+ int minqual = 0;
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+ param2 = strtok_s(NULL, " ", &Context);
+
+ if (ptr)
+ {
+ if (strcmp(ptr, "ADD") == 0)
+ goto NODE_ADD;
+
+ if (strcmp(ptr, "DEL") == 0)
+ goto NODE_DEL;
+
+ if (strcmp(ptr, "VIA") == 0)
+ goto NODE_VIA;
+ }
+
+ if (ptr)
+ {
+ // Could be C or a pattern. Accept C pattern or pattern C
+
+ if ((int)strlen(ptr) > 1)
+ {
+ strcpy(Pattern, ptr);
+ if (param2 && param2[0] == 'C')
+ Param = 'C';
+ }
+ else
+ {
+ Param = ptr[0];
+ if (param2)
+ strcpy(Pattern, param2);
+ }
+ }
+
+ // Pattern >nnn selects nodes with at least that quality
+
+ if (Pattern[0] == '>')
+ {
+ minqual = atoi(&Pattern[1]);
+ Pattern[0] = 0;
+ }
+
+ // We need to pick out CALL or CALL* from other patterns (as call use detail display)
+
+ firststar = strchr(Pattern, '*');
+
+ if ((firststar && *(firststar + 1) != 0)|| strchr(Pattern, '?')) //(* not on end)
+
+ // definitely pattern
+
+ goto DoNodePattern;
+
+ // If it works as CALL*, process, else drop through
+
+ if (Pattern[0])
+ {
+ UCHAR AXCall[8];
+ int count;
+ int paramlen = (int)strlen(ptr);
+ char parampadded[20];
+ int n = 0;
+
+ Alias[8] = 0;
+ strcpy(parampadded, Pattern);
+ strcat(parampadded, " ");
+
+ ConvToAX25(Pattern, AXCall);
+
+ // if * on end, list all ssids
+
+ if (firststar)
+ {
+ AXCall[6] = 0;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ while (AXCall[6] < 32)
+ {
+ Dest = DESTS;
+
+ for (count = 0; count < MAXDESTS; count++)
+ {
+ if (memcmp(Dest->DEST_ALIAS, parampadded, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall))
+ {
+ break;
+ }
+ Dest++;
+ }
+
+ if (count < MAXDESTS)
+ {
+ Bufferptr = DoOneNode(Session, Bufferptr, Dest);
+ n++;
+ }
+
+ AXCall[6] += 2;
+ }
+
+ if (n) // Found Some
+ {
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ Dest = DESTS; // Reset
+
+ // Drop through to try as pattern
+ }
+ else
+ {
+ // process as just call
+
+ for (count = 0; count < MAXDESTS; count++)
+ {
+ if (memcmp(Dest->DEST_ALIAS, parampadded, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall))
+ {
+ break;
+ }
+ Dest++;
+ }
+
+ if (count == MAXDESTS)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not found\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ Bufferptr = DoOneNode(Session, Bufferptr, Dest);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ }
+
+DoNodePattern:
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Nodes\r");
+
+ while (count--)
+ {
+ if (Dest->DEST_CALL[0] != 0)
+ {
+ if (Dest->NRROUTE->ROUT_QUALITY >= minqual)
+ if (Param != 'T' || Dest->DEST_COUNT)
+ List[n++] = Dest;
+
+ if (n > 999)
+ break;
+ }
+ Dest++;
+ }
+
+ if (Param == 'C')
+ qsort(List, n, sizeof(void *), CompareNode);
+ else
+ qsort(List, n, sizeof(void *), CompareAlias);
+
+
+ for (i = 0; i < n; i++)
+ {
+ int len = ConvFromAX25(List[i]->DEST_CALL, Normcall);
+ Normcall[len]=0;
+
+ memcpy(Alias, List[i]->DEST_ALIAS, 6);
+ Alias[6] = 0;
+ strlop(Alias, ' ');
+
+ if (strlen(Alias))
+ strcat(Alias, ":");
+
+ if (Alias[0] == '#' && HIDENODES == 1 && Param != '*') // Hidden Node and not N * command
+ continue;
+
+ if (Pattern[0])
+ if (!WildCmp(Pattern, Normcall) && !WildCmp(Pattern, Alias))
+ continue;
+
+ if (Param == 'T')
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s RTT=%4.2f Frames = %d %c %.1d\r",
+ Alias, Normcall, List[i]->DEST_RTT /1000.0, List[i]->DEST_COUNT,
+ (List[i]->DEST_STATE & 0x40)? 'B':' ', (List[i]->DEST_STATE & 63));
+ }
+ else
+ {
+ len = sprintf(Nodeline, "%s%s", Alias, Normcall);
+ memset(&Nodeline[len], ' ', 20 - len);
+ Nodeline[20] = 0;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Nodeline);
+
+ if (++x == Width)
+ {
+ x = 0;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+ }
+ }
+ }
+
+ if (x)
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ goto SendReply;
+
+
+NODE_VIA:
+
+ // List Nodes reachable via a neighbour
+
+ ptr = param2;
+
+ if (ptr == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Call\r");
+ goto SendReply;
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ ConvToAX25(ptr, AXCALL);
+
+ Dest = DESTS;
+
+ Dest-=1;
+
+ for (count=0; countNRROUTE[0].ROUT_NEIGHBOUR == 0 && Dest->ROUTE[0].ROUT_NEIGHBOUR == 0)
+ continue;
+
+
+ if ((Dest->NRROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))
+ || (Dest->NRROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))
+ || (Dest->NRROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->NRROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))
+
+ || (Dest->ROUTE[0].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[0].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))
+ || (Dest->ROUTE[1].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[1].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL))
+ || (Dest->ROUTE[2].ROUT_NEIGHBOUR && CompareCalls(Dest->ROUTE[2].ROUT_NEIGHBOUR->NEIGHBOUR_CALL, AXCALL)))
+ {
+ len=ConvFromAX25(Dest->DEST_CALL,Normcall);
+
+ Normcall[len]=0;
+
+ memcpy(Alias,Dest->DEST_ALIAS,6);
+
+ Alias[6]=0;
+
+ for (i=0;i<6;i++)
+ {
+ if (Alias[i] == ' ')
+ Alias[i] = 0;
+ }
+
+ cursor=sprintf(line,"%s:%s ", Alias,Normcall);
+
+ cursor = DoViaEntry(Dest, 0, line, cursor);
+ cursor = DoViaEntry(Dest, 1, line, cursor);
+ cursor = DoViaEntry(Dest, 2, line, cursor);
+ cursor = DoINP3ViaEntry(Dest, 0, line, cursor);
+ cursor = DoINP3ViaEntry(Dest, 1, line, cursor);
+ cursor = DoINP3ViaEntry(Dest, 2, line, cursor);
+
+ line[cursor++]='\r';
+ line[cursor++]=0;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", line);
+ }
+ }
+
+
+ goto SendReply;
+
+NODE_ADD:
+
+ // FORMAT IS NODE ADD ALIAS:CALL QUAL ROUTE PORT
+
+
+ if (Session->PASSWORD != 0xFFFF)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG);
+ goto SendReply;
+ }
+
+ ptr = param2;
+
+ if (ptr == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Alias:Call\r");
+ goto SendReply;
+ }
+
+ Call = strlop(ptr, ':');
+
+ if (Call == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Alias:Call\r");
+ goto SendReply;
+ }
+
+
+ ConvToAX25(Call, AXCALL);
+
+ Qualptr = strtok_s(NULL, " ", &Context);
+
+ if (Qualptr == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Quality missing\r");
+ goto SendReply;
+ }
+
+ Qual = atoi(Qualptr);
+
+ if (Qual < MINQUAL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Quality is below MINQUAL\r");
+ goto SendReply;
+ }
+
+ if (FindDestination(AXCALL, &Dest))
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Node already in Table\r");
+ goto SendReply;
+ }
+
+ if (Dest == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Node Table Full\r");
+ goto SendReply;
+ }
+
+ memcpy(Dest->DEST_CALL, AXCALL, 7);
+ memcpy(Dest->DEST_ALIAS, ptr, 6);
+
+ NUMBEROFNODES++;
+
+ ptr = strtok_s(NULL, " ", &Context);
+
+ if (ptr == NULL || ptr[0] == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Neighbour missing\r");
+ goto SendReply;
+ }
+
+ if (ConvToAX25(ptr, axcall) == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Neighbour\r");
+ goto SendReply;
+ }
+ else
+ {
+ int Port;
+
+ ptr = strtok_s(NULL, " ", &Context);
+ if (ptr == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Port missing\r");
+ goto SendReply;
+ }
+
+ Port = atoi(ptr);
+
+ if (Context[0] == '!')
+ {
+ OBSINIT = 255; //; SPECIAL FOR LOCKED
+ }
+
+ if (FindNeighbour(axcall, Port, &ROUTE))
+ {
+ PROCROUTES(Dest, ROUTE, Qual);
+ }
+
+ OBSINIT = SavedOBSINIT;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Node Added\r");
+ goto SendReply;
+ }
+
+
+
+
+/*
+PNODE48:
+
+
+; GET NEIGHBOURS FOR THIS DESTINATION
+;
+ CALL CONVTOAX25
+ JNZ SHORT BADROUTE
+;
+ CALL GETVALUE
+ MOV SAVEPORT,AL ; SET PORT FOR _FINDNEIGHBOUR
+
+ CALL GETVALUE
+ MOV ROUTEQUAL,AL
+;
+ MOV ESI,OFFSET32 AX25CALL
+
+ PUSH EBX ; SAVE DEST
+ CALL _FINDNEIGHBOUR
+ MOV EAX,EBX ; ROUTE TO AX
+ POP EBX
+
+ JZ SHORT NOTBADROUTE
+
+ JMP SHORT BADROUTE
+
+NOTBADROUTE:
+;
+; UPDATE ROUTE LIST FOR THIS DEST
+;
+ MOV ROUT1_NEIGHBOUR[EBX],EAX
+ MOV AL,ROUTEQUAL
+ MOV ROUT1_QUALITY[EBX],AL
+ MOV ROUT1_OBSCOUNT[EBX],255 ; LOCKED
+;
+ POP EDI
+ POP EBX
+
+ INC _NUMBEROFNODES
+
+ JMP SENDOK
+
+BADROUTE:
+;
+; KILL IT
+;
+ MOV ECX,TYPE DEST_LIST
+ MOV EDI,EBX
+ MOV AL,0
+ REP STOSB
+
+ JMP BADROUTECMD
+
+*/
+
+ goto SendReply;
+
+
+NODE_DEL:
+
+ if (Session->PASSWORD != 0xFFFF)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG);
+ goto SendReply;
+ }
+
+ ptr = param2;
+
+ if (ptr == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Missing Call\r");
+ goto SendReply;
+ }
+
+ if (strcmp(ptr, "ALL") == 0)
+ {
+ struct DEST_LIST * DEST = DESTS;
+ int n = MAXDESTS;
+
+ while (n--)
+ {
+ if (DEST->DEST_CALL[0] && ((DEST->DEST_STATE & 0x80) == 0)) // Don't delete appl node
+ REMOVENODE(DEST);
+
+ DEST++;
+ }
+
+ ClearNodes();
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "All Nodes Deleted\r");
+ goto SendReply;
+ }
+
+ ConvToAX25(ptr, AXCALL);
+
+ if (FindDestination(AXCALL, &Dest) == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not Found\r");
+ goto SendReply;
+ }
+
+ if (Dest->DEST_STATE & 0x80)
+ Bufferptr = Cmdprintf(Session, Bufferptr, "APPL Node - Can't delete\r");
+ else
+ {
+ REMOVENODE(Dest);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Node Deleted\r");
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Node Deleted\r");
+
+SendReply:
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID CMDQUERY(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD)
+{
+ // DISPLAY AVAILABLE COMMANDS
+
+ int n;
+ char * ptr;
+ char ApplList[2048];
+ char * out = ApplList;
+
+ CMDX * CMD = &COMMANDS[APPL1];
+
+ for (n = 0; n < NumberofAppls; n++)
+ {
+ ptr = &CMD->String[0];
+ if (*(ptr) != '*')
+ {
+ while (*ptr != ' ')
+ {
+ *(out++) = *(ptr++);
+ }
+ *(out++) = ' ';
+ }
+ CMD++;
+ }
+
+ *(out) = 0;
+
+ n = CMDLISTLEN;
+
+ if (NEEDMH == 0)
+ n -= 7; // Dont show MH
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s\r", ApplList, CMDLIST);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+char * FormatMH(MHSTRUC * MH, char Format);
+
+VOID MHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // DISPLAY HEARD LIST
+
+ int Port = 0, sess = 0;
+ char * ptr, *Context, *pattern;
+ struct PORTCONTROL * PORT = NULL;
+ MHSTRUC * MH;
+ int count = MHENTRIES;
+ int n;
+ char Normcall[20];
+ char From[10];
+ char DigiList[100];
+ char * Output;
+ int len;
+ char Digi = 0;
+
+
+ // Note that the MHDIGIS field may contain rubbish. You have to check End of Address bit to find
+ // how many digis there are
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr == NULL || ptr[0] == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Port Number needed eg MH 1\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (ptr)
+ Port = atoi(ptr);
+
+ if (Port)
+ PORT = GetPortTableEntryFromPortNum(Port);
+
+ if (PORT == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ pattern = strtok_s(NULL, " ", &Context);
+
+ if (pattern)
+ _strupr(pattern); // Optional filter
+
+ MH = PORT->PORTMHEARD;
+
+ if (MH == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "MHEARD not enabled on that port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (pattern && strstr(pattern, "CLEAR"))
+ {
+ if (Session->Secure_Session)
+ {
+ memset(MH, 0, MHENTRIES * sizeof(MHSTRUC));
+ SaveMH();
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d Cleared\r", Port);
+ }
+ else
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "MH Clear needs SYSOP status\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ }
+ else
+ {
+ if (CMD->String[2] == 'V') // MHV
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "MHeard List %s for Port %d\r", MYNODECALL, Port);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Callsign Last heard Pkts RX via Digi ;) \r");
+ Bufferptr = Cmdprintf(Session, Bufferptr, "--------- ----------- ------- ------------------------------------------\r");
+ }
+ else
+ if (pattern)
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d filtered by %s\r", Port, pattern);
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Heard List for Port %d\r", Port);
+ }
+ while (count--)
+ {
+ if (MH->MHCALL[0] == 0)
+ break;
+
+ Digi = 0;
+
+ len = ConvFromAX25(MH->MHCALL, Normcall);
+
+ Normcall[len++] = MH->MHDIGI;
+ Normcall[len++] = 0;
+
+ if (pattern && strstr(Normcall, pattern) == 0)
+ {
+ MH++;
+ continue;
+ }
+
+ n = 8; // Max number of digi-peaters
+
+ ptr = &MH->MHCALL[6]; // End of Address bit
+
+ Output = &DigiList[0];
+
+ if ((*ptr & 1) == 0)
+ {
+ // at least one digi
+
+ strcpy(Output, "via ");
+ Output += 4;
+
+ while ((*ptr & 1) == 0)
+ {
+ // MORE TO COME
+
+ From[ConvFromAX25(ptr + 1, From)] = 0;
+ Output += sprintf((char *)Output, "%s", From);
+
+ ptr += 7;
+ n--;
+
+ if (n == 0)
+ break;
+
+ // See if digi actioned - put a * on last actioned
+
+ if (*ptr & 0x80)
+ {
+ if (*ptr & 1) // if last address, must need *
+ {
+ *(Output++) = '*';
+ Digi = '*';
+ }
+
+ else
+ if ((ptr[7] & 0x80) == 0) // Repeased by next?
+ {
+ *(Output++) = '*'; // No, so need *
+ Digi = '*';
+ }
+
+}
+ *(Output++) = ',';
+ }
+ *(--Output) = 0; // remove last comma
+ }
+ else
+ *(Output) = 0;
+
+ // if we used a digi set * on call and display via string
+
+
+ if (Digi)
+ Normcall[len++] = Digi;
+ else
+ DigiList[0] = 0; // Dont show list if not used
+
+ Normcall[len++] = 0;
+
+
+ ptr = FormatMH(MH, CMD->String[2]);
+
+ if (CMD->String[2] == 'V') // MHV
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s %-10s %-10d %-30s\r",
+ Normcall, ptr, MH->MHCOUNT, DigiList);
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s %s %s\r", Normcall, ptr, DigiList);
+
+ MH++;
+ }
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+int Rig_Command(TRANSPORTENTRY * Session, char * Command);
+
+VOID RADIOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD)
+{
+ char * ptr;
+
+ if (Rig_Command(Session, CmdTail))
+ {
+ ReleaseBuffer((UINT *)REPLYBUFFER);
+ return;
+ }
+
+ // Error Message is in buffer
+
+ ptr = strchr(CmdTail, 13);
+
+ if (ptr)
+ {
+ int len = (int)(++ptr - CmdTail);
+
+ memcpy(Bufferptr, CmdTail, len);
+ Bufferptr += len;
+ }
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+
+VOID SendNRRecordRoute(struct DEST_LIST * DEST, TRANSPORTENTRY * Session);
+
+
+VOID NRRCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD)
+{
+ // PROCESS 'NRR - Netrom Record Route' COMMAND
+
+ char * ptr, *Context;
+ struct DEST_LIST * Dest = DESTS;
+ int count = MAXDESTS;
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ {
+ UCHAR AXCall[8];
+ int count;
+
+ ConvToAX25(ptr, AXCall);
+ strcat(ptr, " ");
+
+ for (count = 0; count < MAXDESTS; count++)
+ {
+ if (memcmp(Dest->DEST_ALIAS, ptr, 6) == 0 || CompareCalls(Dest->DEST_CALL, AXCall))
+ {
+ SendNRRecordRoute(Dest, Session);
+ memcpy(Bufferptr, OKMSG, 3);
+ Bufferptr += 3;
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+
+ return;
+ }
+ Dest++;
+ }
+ }
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not found\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+int CHECKINTERLOCK(struct PORTCONTROL * OURPORT)
+{
+ // See if any Interlocked ports are Busy
+
+ struct PORTCONTROL * PORT = PORTTABLE;
+ struct _EXTPORTDATA * EXTPORT;
+
+ int n = NUMBEROFPORTS;
+ int ourgroup = OURPORT->PORTINTERLOCK;
+
+ while (PORT)
+ {
+ if (PORT != OURPORT)
+ {
+ if (PORT->PORTINTERLOCK == ourgroup)
+ {
+ // Same Group - is it busy
+
+ int i = 0;
+
+ EXTPORT = (struct _EXTPORTDATA *)PORT;
+
+ while (i < 27)
+ if (EXTPORT->ATTACHEDSESSIONS[i++])
+ return PORT->PORTNUMBER;
+ }
+ }
+ PORT = PORT->PORTPOINTER;
+ }
+
+ return 0;
+}
+
+VOID ATTACHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD)
+{
+ // ATTACH to a PACTOR or similar port
+
+ TRANSPORTENTRY * NewSess;
+ struct _EXTPORTDATA * EXTPORT;
+ struct TNCINFO * TNC = 0;
+
+ int Port = 0, sess = 0;
+ char * ptr, *Context;
+ int ret;
+ struct PORTCONTROL * PORT = NULL;
+ struct DATAMESSAGE Message = {0};
+ int Paclen, PortPaclen;
+ struct DATAMESSAGE * Buffer;
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ Port = atoi(ptr);
+
+ if (Port)
+ PORT = GetPortTableEntryFromPortNum(Port);
+
+ if (PORT == NULL || PORT->PROTOCOL < 10)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // If attach on telnet port, find a free stream
+
+ EXTPORT = (struct _EXTPORTDATA *)PORT;
+
+ if (strstr(EXTPORT->PORT_DLL_NAME, "TELNET"))
+ {
+ int count = EXTPORT->MAXHOSTMODESESSIONS;
+ count--; // First is Pactor Stream, count is now last ax.25 session
+
+ while (count)
+ {
+ if (EXTPORT->ATTACHEDSESSIONS[count] == 0)
+ {
+ int Paclen, PortPaclen;
+ struct DATAMESSAGE Message = {0};
+
+ // Found a free one - use it
+
+ // See if TNC is OK
+
+ Message.PORT = count;
+
+ ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT);
+
+ if ((ret & 0xff00) == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK
+
+ NewSess = SetupNewSession(Session, Bufferptr);
+
+ if (NewSess == NULL)
+ return;
+
+ EXTPORT->ATTACHEDSESSIONS[count] = NewSess;
+
+ NewSess->Secure_Session = Session->Secure_Session;
+
+ NewSess->KAMSESSION = count;
+
+ // Set paclen to lower of incoming and outgoing
+
+ Paclen = Session->SESSPACLEN; // Incoming PACLEN
+
+ if (Paclen == 0)
+ Paclen = 256; // 0 = 256
+
+ PortPaclen = PORT->PORTPACLEN;
+
+ if (PortPaclen == 0)
+ PortPaclen = 256; // 0 = 256
+
+ if (PortPaclen < Paclen)
+ Paclen = PortPaclen;
+
+ NewSess->SESSPACLEN = Paclen;
+ Session->SESSPACLEN = Paclen;
+
+ NewSess->L4STATE = 5;
+ NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR;
+ NewSess->L4TARGET.PORT = PORT;
+
+ ptr = strtok_s(NULL, " ", &Context);
+ sess = count;
+
+ // Replace command tail with original (before conversion to upper case
+
+ Context = Context + (OrigCmdBuffer - COMMANDBUFFER);
+
+ goto checkattachandcall;
+
+
+ memcpy(Bufferptr, OKMSG, 3);
+ Bufferptr += 3;
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+
+ return;
+ }
+ count--;
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Error - No free streams on this port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ Message.PORT = 0;
+
+ ret = PORT->PORTTXCHECKCODE(PORT, Message.PORT);
+
+ if ((ret & 0xff00) == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Error - TNC Not Ready\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // See if "Attach and Call" (for VHF ports)
+
+ ptr = strtok_s(NULL, " ", &Context);
+
+ if (ptr && strcmp(ptr, "S") == 0)
+ {
+ Session->STAYFLAG = TRUE;
+ ptr = strtok_s(NULL, " ", &Context);
+ }
+
+ if (ptr)
+ {
+ // we have another param
+
+ // if it is a single char it is a channel number for vhf attach
+
+ if (strlen(ptr) == 1)
+ {
+ // Only Allow Attach VHF from Secure Applications or if PERMITGATEWAY is set
+
+ if (EXTPORT->PERMITGATEWAY == 0 && Session->Secure_Session == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, you are not allowed to use this port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ sess = ptr[0] - '@';
+
+ if (sess < 1 || sess > EXTPORT->MAXHOSTMODESESSIONS)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Invalid Channel\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ ptr = strtok_s(NULL, " ", &Context);
+
+ if (ptr && strcmp(ptr, "S") == 0)
+ {
+ Session->STAYFLAG = TRUE;
+ ptr = strtok_s(NULL, " ", &Context);
+ }
+ }
+ }
+
+ if (ret & 0x8000) // Disconnecting
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port in use\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // Check Interlock. Only ports with a TNC record can be interlocked
+
+ TNC = PORT->TNC;
+
+ if (TNC)
+ {
+ // See if any interlocked ports are in use
+
+ struct TNCINFO * OtherTNC;
+ int i;
+ int rxInterlock = TNC->RXRadio;
+ int txInterlock = TNC->TXRadio;
+
+ if (rxInterlock || txInterlock)
+ {
+ for (i=1; i <= MAXBPQPORTS; i++)
+ {
+ OtherTNC = TNCInfo[i];
+
+ if (OtherTNC == NULL)
+ continue;
+
+ if (OtherTNC == TNC)
+ continue;
+
+ if (rxInterlock && rxInterlock == OtherTNC->RXRadio || txInterlock && txInterlock == OtherTNC->TXRadio) // Same Group
+ {
+ int n;
+
+ for (n = 0; n <= 26; n++)
+ {
+ if (OtherTNC->PortRecord->ATTACHEDSESSIONS[n])
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Sorry, interlocked port %d is in use\r", i);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+
+
+ if (EXTPORT->ATTACHEDSESSIONS[sess] || PORT->PortSuspended)
+ {
+ // In use
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port in use\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ // GET CIRCUIT TABLE ENTRY FOR OTHER END OF LINK
+
+ NewSess = SetupNewSession(Session, Bufferptr);
+
+ if (NewSess == NULL)
+ return;
+
+ // if a UZ7HO port, and the uplink is L2 or Uz7HO and multisession,
+ // invert SSID bits
+
+ if (memcmp(EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0)
+ goto noFlip1;
+
+ if (EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession
+ goto noFlip1;
+
+ if ((Session->L4CIRCUITTYPE & BPQHOST)) // host
+ goto noFlip1;
+
+ if ((Session->L4CIRCUITTYPE & PACTOR))
+ {
+ // incoming is Pactorlike - see if UZ7HO
+
+ if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0)
+ goto noFlip1;
+ else
+ NewSess->L4USER[6] ^= 0x1e; // UZ7HO Uplink - flip
+ }
+ else
+
+ // Must be L2 uplink - flip
+
+ NewSess->L4USER[6] ^= 0x1e; // Flip SSID
+noFlip1:
+
+ EXTPORT->ATTACHEDSESSIONS[sess] = NewSess;
+
+ NewSess->KAMSESSION = sess;
+
+ // Set paclen to lower of incoming and outgoing
+
+ Paclen = Session->SESSPACLEN; // Incoming PACLEN
+
+ if (Paclen == 0)
+ Paclen = 256; // 0 = 256
+
+ PortPaclen = PORT->PORTPACLEN;
+
+ if (PortPaclen == 0)
+ PortPaclen = 256; // 0 = 256
+
+ if (PortPaclen < Paclen)
+ Paclen = PortPaclen;
+
+ NewSess->SESSPACLEN = Paclen;
+ Session->SESSPACLEN = Paclen;
+ NewSess->L4STATE = 5;
+ NewSess->L4CIRCUITTYPE = DOWNLINK + PACTOR;
+ NewSess->L4TARGET.PORT = PORT;
+
+checkattachandcall:
+
+ // If set freq on attach is defined, do it
+
+ if (TNC && TNC->ActiveRXFreq && TNC->RXRadio)
+ {
+ char Msg[128];
+
+ sprintf(Msg, "R%d %f", TNC->RXRadio, TNC->ActiveRXFreq);
+ Rig_Command( (TRANSPORTENTRY *) -1, Msg);
+ }
+
+ if (TNC && TNC->ActiveTXFreq && TNC->TXRadio && TNC->TXRadio != TNC->RXRadio)
+ {
+ char Msg[128];
+
+ sprintf(Msg, "R%d %f", TNC->TXRadio, TNC->ActiveTXFreq);
+ Rig_Command( (TRANSPORTENTRY *) -1, Msg);
+ }
+
+ if (ptr)
+ {
+ // we have a call to connect to
+
+ char Callstring[80];
+ int len;
+
+ Buffer = REPLYBUFFER;
+ Buffer->PORT = sess;
+ Buffer->PID = 0xf0;
+
+ len = sprintf(Callstring,"C %s", ptr);
+
+ ptr = strtok_s(NULL, " ", &Context);
+
+ while (ptr) // if any other params (such as digis) copy them
+ {
+ if (strcmp(ptr, "S") == 0)
+ {
+ Session->STAYFLAG = TRUE;
+ }
+ else
+ len += sprintf(&Callstring[len], " %s", ptr);
+
+ ptr = strtok_s(NULL, " ", &Context);
+ }
+
+ Callstring[len++] = 13;
+ Callstring[len] = 0;
+
+ Buffer->LENGTH = len + MSGHDDRLEN + 1;
+ memcpy(Buffer->L2DATA, Callstring, len);
+ C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer);
+
+ return;
+ }
+
+ memcpy(Bufferptr, OKMSG, 3);
+ Bufferptr += 3;
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+
+ return;
+}
+
+// SYSOP COMMANDS
+
+CMDX COMMANDS[] =
+{
+ "SAVENODES ",8, SAVENODES, 0,
+ "TELRECONFIG ",4, RECONFIGTELNET, 0,
+ "SAVEMH ",6, SAVEMHCMD, 0,
+ "REBOOT ",6, REBOOT, 0,
+ "RIGRECONFIG ",8 , RIGRECONFIG, 0,
+ "RESTART ",7,RESTART,0,
+ "RESTARTTNC ",10,RESTARTTNC,0,
+ "SENDNODES ",8,SENDNODES,0,
+ "EXTRESTART ",10, EXTPORTVAL, offsetof(EXTPORTDATA, EXTRESTART),
+ "TXDELAY ",3, PORTVAL, offsetof(PORTCONTROLX, PORTTXDELAY),
+ "MAXFRAME ",3, PORTVAL, offsetof(PORTCONTROLX, PORTWINDOW),
+ "RETRIES ",3, PORTVAL, offsetof(PORTCONTROLX, PORTN2),
+ "FRACK ",3,PORTVAL, offsetof(PORTCONTROLX, PORTT1),
+ "RESPTIME ",3,PORTVAL, offsetof(PORTCONTROLX, PORTT2),
+ "PPACLEN ",3,PORTVAL, offsetof(PORTCONTROLX, PORTPACLEN),
+ "QUALITY ",3,PORTVAL, offsetof(PORTCONTROLX, PORTQUALITY),
+ "PERSIST ",2,PORTVAL, offsetof(PORTCONTROLX, PORTPERSISTANCE),
+ "TXTAIL ",3,PORTVAL, offsetof(PORTCONTROLX, PORTTAILTIME),
+ "XMITOFF ",7,PORTVAL, offsetof(PORTCONTROLX, PORTDISABLED),
+ "DIGIFLAG ",5,PORTVAL, offsetof(PORTCONTROLX, DIGIFLAG),
+ "DIGIPORT ",5,PORTVAL, offsetof(PORTCONTROLX, DIGIPORT),
+ "MAXUSERS ",4,PORTVAL, offsetof(PORTCONTROLX, USERS),
+ "L3ONLY ",6,PORTVAL, offsetof(PORTCONTROLX, PORTL3FLAG),
+ "BBSALIAS ",4,PORTVAL, offsetof(PORTCONTROLX, PORTBBSFLAG),
+ "VALIDCALLS ",5,VALNODES,0,
+ "WL2KSYSOP ",5,WL2KSYSOP,0,
+ "STOPPORT ",4,STOPPORT,0,
+ "STARTPORT ",5,STARTPORT,0,
+ "STOPCMS ",7,STOPCMS,0,
+ "STARTCMS ",8,STARTCMS,0,
+
+ "FINDBUFFS ",4,FINDBUFFS,0,
+ "KISS ",4,KISSCMD,0,
+ "GETPORTCTEXT",9,GetPortCTEXT, 0,
+
+#ifdef EXCLUDEBITS
+
+ "EXCLUDE ",4,ListExcludedCalls,0,
+
+#endif
+
+ "FULLDUP ",4,PORTVAL, offsetof(PORTCONTROLX, FULLDUPLEX),
+ "SOFTDCD ",4,PORTVAL, offsetof(PORTCONTROLX, SOFTDCDFLAG),
+ "OBSINIT ",7,SWITCHVAL,(size_t)&OBSINIT,
+ "OBSMIN ",6,SWITCHVAL,(size_t)&OBSMIN,
+ "NODESINT ",8,SWITCHVAL,(size_t)&L3INTERVAL,
+ "L3TTL ",5,SWITCHVAL,(size_t)&L3LIVES,
+ "L4RETRIES ",5,SWITCHVAL,(size_t)&L4N2,
+ "L4TIMEOUT ",5,SWITCHVALW,(size_t)&L4T1,
+ "T3 ",2,SWITCHVALW,(size_t)&T3,
+ "NODEIDLETIME",8,SWITCHVALW,(size_t)&L4LIMIT,
+ "LINKEDFLAG ",10,SWITCHVAL,(size_t)&LINKEDFLAG,
+ "IDINTERVAL ",5,SWITCHVAL,(size_t)&IDINTERVAL,
+ "MINQUAL ",7,SWITCHVAL,(size_t)&MINQUAL,
+ "FULLCTEXT ",6,SWITCHVAL,(size_t)&FULL_CTEXT,
+ "HIDENODES ",8,SWITCHVAL,(size_t)&HIDENODES,
+ "L4DELAY ",7,SWITCHVAL,(size_t)&L4DELAY,
+ "L4WINDOW ",6,SWITCHVAL,(size_t)&L4DEFAULTWINDOW,
+ "BTINTERVAL ",5,SWITCHVAL,(size_t)&BTINTERVAL,
+ "PASSWORD ", 8, PWDCMD, 0,
+
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0,
+ "************", 12, APPLCMD, 0, // Apppl 32 is internal Terminal
+ "*** LINKED ",10,LINKCMD,0,
+ "CQ ",2,CQCMD,0,
+ "CONNECT ",1,CMDC00,0,
+ "NC ",2,CMDC00,0,
+ "BYE ",1,BYECMD,0,
+ "QUIT ",1,BYECMD,0,
+ "INFO ",1,CMDI00,0,
+ "HELP ",1,HELPCMD,0,
+ "VERSION ",1,CMDV00,0,
+ "NODES ",1,CMDN00,0,
+ "LINKS ",1,CMDL00,0,
+ "LISTEN ",3,LISTENCMD,0,
+ "L4T1 ",2,CMDT00,0,
+ "PORTS ",1,CMDP00,0,
+ "PACLEN ",3,CMDPAC,0,
+ "IDLETIME ",4,CMDIDLE,0,
+ "ROUTES ",1,CMDR00,0,
+ "STATS ",1,CMDSTATS,0,
+ "USERS ",1,CMDS00,0,
+ "UNPROTO ",2,UNPROTOCMD,0,
+ "? ",1,CMDQUERY,0,
+ "DUMP ",4,DUMPCMD,0,
+ "MHU ",3,MHCMD,0, // UTC Times
+ "MHL ",3,MHCMD,0, // Local Times
+ "MHV ",3,MHCMD,0,
+ "MHEARD ",1,MHCMD,0,
+ "APRS ",2,APRSCMD,0,
+ "ATTACH ",1,ATTACHCMD,0,
+ "RADIO ",3,RADIOCMD,0,
+ "AXRESOLVER ",3,AXRESOLVER,0,
+ "AXMHEARD ",3,AXMHEARD,0,
+ "TELSTATUS ",3,SHOWTELNET,0,
+ "NRR ",1,NRRCMD,0,
+ "PING ",2,PING,0,
+ "AGWSTATUS ",3,SHOWAGW,0,
+ "ARP ",3,SHOWARP,0,
+ "NAT ",3,SHOWNAT,0,
+ "IPROUTE ",3,SHOWIPROUTE,0,
+ "UZ7HO ",5,UZ7HOCMD,0,
+ "QTSM ",4,QTSMCMD,0,
+
+ "..FLMSG ",7,FLMSG,0
+};
+
+CMDX * CMD = NULL;
+
+int NUMBEROFCOMMANDS = sizeof(COMMANDS)/sizeof(CMDX);
+
+char * ReplyPointer; // Pointer into reply buffer
+
+int DecodeNodeName(char * NodeName, char * ptr)
+{
+ // NodeName is TABLE ENTRY WITH AX25 CALL AND ALIAS
+
+ // Copyies 20 byte 20 DECODED NAME IN FORM ALIAS:CALL to ptr
+ // Returns significant length of string
+
+ int len;
+ char Normcall[10];
+ char * alias = &NodeName[7];
+ int n = 6;
+ char * start = ptr;
+
+ memset(ptr, ' ', 20);
+
+ len = ConvFromAX25(NodeName, Normcall);
+
+ if (*(alias) > ' ') // Does alias start with a null or a space ?
+ {
+ while (*(alias) > ' ' && n--)
+ {
+ *ptr++ = *alias++;
+ }
+ *ptr++ = ':';
+ }
+
+ memcpy(ptr, Normcall, len);
+ ptr += len;
+
+ return (int)(ptr - start);
+}
+
+char * SetupNodeHeader(struct DATAMESSAGE * Buffer)
+{
+ char Header[20];
+ int len;
+
+ char * ptr = &Buffer->L2DATA[0];
+
+ len = DecodeNodeName(MYCALLWITHALIAS, Header);
+
+ memcpy (ptr, Header, len);
+ ptr += len;
+
+ (*ptr++) = HEADERCHAR;
+ (*ptr++) = ' ';
+
+ return ptr;
+}
+
+VOID SendCommandReply(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer, int Len)
+{
+ if (Len == (4 + sizeof(void *))) // Null Packet
+ {
+ ReleaseBuffer((UINT *)Buffer);
+ return;
+ }
+
+ Buffer->LENGTH = Len;
+
+ C_Q_ADD(&Session->L4TX_Q, (UINT *)Buffer);
+
+ PostDataAvailable(Session);
+}
+
+
+VOID CommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer)
+{
+ // ignore frames with single NULL (Keepalive)
+
+ if (Buffer->LENGTH == sizeof(void *) + 5 && Buffer->L2DATA[0] == 0)
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ if (Buffer->LENGTH > 100)
+ {
+// Debugprintf("BPQ32 command too long %s", Buffer->L2DATA);
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+InnerLoop:
+
+ InnerCommandHandler(Session, Buffer);
+
+// See if any more commands in buffer
+
+ if (Session->PARTCMDBUFFER)
+ {
+ char * ptr1, * ptr2;
+ int len;
+
+ Buffer = Session->PARTCMDBUFFER;
+
+ // Check that message has a CR, if not save buffer and exit
+
+ len = Buffer->LENGTH - (4 + sizeof(void *));
+ ptr1 = &Buffer->L2DATA[0];
+
+ ptr2 = memchr(ptr1, 13, len);
+
+ if (ptr2 == NULL)
+ return;
+
+ Session->PARTCMDBUFFER = NULL;
+
+ goto InnerLoop;
+ }
+}
+
+
+VOID InnerCommandHandler(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer)
+{
+ char * ptr1, * ptr2, *ptr3;
+ int len, oldlen, newlen, rest, n;
+ struct DATAMESSAGE * OldBuffer;
+ struct DATAMESSAGE * SaveBuffer;
+ char c;
+
+ // If a partial command is stored, append this data to it.
+
+ if (Session->PARTCMDBUFFER)
+ {
+ len = Buffer->LENGTH - (sizeof(void *) + 4);
+ ptr1 = &Buffer->L2DATA[0];
+
+ OldBuffer = Session->PARTCMDBUFFER; // Old Data
+
+ if (OldBuffer == Buffer)
+ {
+ // something has gone horribly wrong
+
+ Session->PARTCMDBUFFER = NULL;
+ return;
+ }
+
+ oldlen = OldBuffer->LENGTH;
+
+ newlen = len + oldlen;
+
+ if (newlen > 200)
+ {
+ // Command far too long - ignore previous
+
+ OldBuffer->LENGTH = oldlen = sizeof(void *) + 4;
+ }
+
+ OldBuffer->LENGTH += len;
+ memcpy(&OldBuffer->L2DATA[oldlen - (sizeof(void *) + 4)], Buffer->L2DATA, len);
+
+ ReleaseBuffer((UINT *)Buffer);
+
+ Buffer = OldBuffer;
+
+ Session->PARTCMDBUFFER = NULL;
+ }
+
+ // Check that message has a CR, if not save buffer and exit
+
+ len = Buffer->LENGTH - (sizeof(void *) + 4);
+ ptr1 = &Buffer->L2DATA[0];
+
+ // Check for sending YAPP to Node
+
+ if (len == 2 && ptr1[0] == 5 && ptr1[1] == 1)
+ {
+ ptr1[0] = 0x15; // NAK
+
+ ptr1[1] = sprintf(&ptr1[2], "Node doesn't support YAPP Transfers");
+
+ Buffer->LENGTH += ptr1[1];
+
+ C_Q_ADD(&Session->L4TX_Q, (UINT *)Buffer);
+ PostDataAvailable(Session);
+ return;
+ }
+
+
+ ptr2 = memchr(ptr1, ';', len);
+
+ if (ptr2 == 0)
+ {
+ ptr2 = memchr(ptr1, 13, len);
+
+ if (ptr2 == 0)
+ {
+ // No newline
+
+ Session->PARTCMDBUFFER = Buffer;
+ return;
+ }
+ }
+
+ ptr2++;
+
+ rest = len - (int)(ptr2 - ptr1);
+
+ if (rest)
+ {
+ // there are chars beyond the cr in the buffer
+
+ // see if LF after CR
+
+ if ((*ptr2) == 10) // LF
+ {
+ ptr2++;
+ rest--;
+ }
+
+ if (rest) // May only have had LF
+ {
+ // Get a new buffer, and copy extra data to it.
+
+ SaveBuffer = (struct DATAMESSAGE *)GetBuff();
+
+ if (SaveBuffer) //`Just ignore if no buffers
+ {
+ SaveBuffer->LENGTH = rest + MSGHDDRLEN + 1;
+ SaveBuffer->PID = 0xf0;
+ memcpy(&SaveBuffer->L2DATA[0], ptr2, rest);
+ Session->PARTCMDBUFFER = SaveBuffer;
+ }
+ }
+ }
+
+ // GET PACLEN FOR THIS CONNECTION
+
+ CMDPACLEN = Session->SESSPACLEN;
+
+ if (CMDPACLEN == 0)
+ CMDPACLEN = PACLEN; // Use default if no Session PACLEN
+
+ // If sesion is in UNPROTO Mode, send message as a UI message
+
+ if (Session->UNPROTO)
+ {
+// char LongMsg[512] =
+// "VeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessage"
+// "VeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessageVeryLongMessage";
+
+ DIGIMESSAGE Msg;
+ int Port = Session->UNPROTO;
+ int Len = Buffer->LENGTH - (MSGHDDRLEN -1); // Need PID
+
+ // First check for UNPROTO exit - ctrl/z or /ex
+
+ if (Buffer->L2DATA[0] == 26 || (Len == 6 && _memicmp(&Buffer->L2DATA[0], "/ex", 3) == 0)) // CTRL/Z or /ex
+ {
+ REPLYBUFFER = Buffer;
+
+ Session->UNPROTO = 0;
+ memset(Session->UADDRESS, 0, 64);
+
+ // SET UP HEADER
+
+ Buffer->PID = 0xf0;
+ ptr1 = SetupNodeHeader(Buffer);
+ memcpy(ptr1, OKMSG, 3);
+ ptr1 += 3;
+ SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer));
+
+ return;
+ }
+
+ memset(&Msg, 0, sizeof(Msg));
+
+ Msg.PORT = Port;
+ Msg.CTL = 3; // UI
+ memcpy(Msg.DEST, Session->UADDRESS, 7);
+ Msg.DEST[6] |= 0x80; // set Command Bit
+ memcpy(Msg.ORIGIN, Session->L4USER, 7);
+ memcpy(Msg.DIGIS, &Session->UADDRESS[7], Session->UAddrLen - 7);
+ memcpy(&Msg.PID, &Buffer->PID, Len);
+ Send_AX_Datagram(&Msg, Len, Port); // Len is Payload - CTL, PID and Data
+
+// memcpy(&Msg.PID + 1, LongMsg, 260);
+// Send_AX_Datagram(&Msg, 241, Port); // Len is Payload - CTL, PID and Data
+
+
+// SendUIModeFrame(Session, (PMESSAGE)Buffer, Session->UNPROTO);
+
+ ReleaseBuffer((UINT *)Buffer); // Not using buffer for reply
+
+ // Assume we don't allow multiple lines in buffer with UI
+
+ if (Session->PARTCMDBUFFER)
+ {
+ Buffer = Session->PARTCMDBUFFER;
+ ReleaseBuffer((UINT *)Buffer); // Not using buffer for reply
+ Session->PARTCMDBUFFER = NULL;
+ }
+ return;
+ }
+
+ memset(COMMANDBUFFER, 32, 80); // Clear to spaces
+
+ ptr1 = &Buffer->L2DATA[0];
+ ptr2 = &COMMANDBUFFER[0];
+ ptr3 = &OrigCmdBuffer[0];
+
+ memset(OrigCmdBuffer, 0, 80);
+ n = 80;
+
+ while (n--)
+ {
+ c = *(ptr1++) & 0x7f; // Mask paritu
+
+ if (c == 13 || c == ';')
+ break; // CR
+
+ *(ptr3++) = c; // Original Case
+
+ c = toupper(c);
+ *(ptr2++) = c;
+ }
+
+
+ // USE INPUT MESSAGE _BUFFER FOR REPLY
+
+ REPLYBUFFER = Buffer;
+
+ // SET UP HEADER
+
+ Buffer->PID = 0xf0;
+ ptr1 = SetupNodeHeader(Buffer);
+
+ ReplyPointer = ptr1;
+
+ ALIASINVOKED = 0; // Clear "Invoked by APPL ALIAS flag"
+
+ DoTheCommand(Session); // We also call DotheCommand when we need to reprocess - eg for alias handling
+}
+
+VOID DoTheCommand(TRANSPORTENTRY * Session)
+{
+ struct DATAMESSAGE * Buffer = REPLYBUFFER;
+ char * ptr1, * ptr2;
+ int n;
+
+ ptr1 = &COMMANDBUFFER[0]; //
+
+ n = 10;
+
+ while ((*ptr1 == ' ' || *ptr1 == 0) && n--)
+ ptr1++; // STRIP LEADING SPACES and nulls (from keepalive)
+
+ if (n == -1)
+ {
+ // Null command
+
+ ReleaseBuffer((UINT *)Buffer);
+ return;
+ }
+
+ ptr2 = ptr1; // Save
+
+
+ CMD = &COMMANDS[0];
+ n = 0;
+
+ for (n = 0; n < NUMBEROFCOMMANDS; n++)
+ {
+ int CL = CMD->CMDLEN;
+
+ ptr1 = ptr2;
+
+ CMDPTR = CMD;
+
+ if (n == APPL1) // First APPL command
+ {
+ APPLMASK = 1; // FOR APPLICATION ATTACH REQUESTS
+ ALIASPTR = &CMDALIAS[0][0];
+ }
+
+ // ptr1 is input command
+
+ if (memcmp(CMD->String, ptr1, CL) == 0)
+ {
+ // Found match so far - check rest
+
+ char * ptr2 = &CMD->String[CL];
+
+ ptr1 += CL;
+
+ if (*(ptr1) != ' ')
+ {
+ while(*(ptr1) == *ptr2 && *(ptr1) != ' ')
+ {
+ ptr1++;
+ ptr2++;
+ }
+ }
+
+ if (*(ptr1) == ' ')
+ {
+ Session->BADCOMMANDS = 0; // RESET ERROR COUNT
+
+ // SEE IF SYSOP COMMAND, AND IF SO IF PASSWORD HAS BEEN ENTERED
+
+ if (n < PASSCMD)
+ {
+ //NEEDS PASSWORD FOR SYSOP COMMANDS
+
+ if (Session->PASSWORD != 0xFFFF)
+ {
+ ptr1 = ReplyPointer;
+
+ memcpy(ptr1, PASSWORDMSG, LPASSMSG);
+ ptr1 += LPASSMSG;
+
+ SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer));
+ return;
+ }
+ }
+// VALNODESFLAG = 0; // NOT VALID NODES COMMAND
+
+ ptr1++; // Skip space
+
+ CMD->CMDPROC(Session, ReplyPointer, ptr1, CMD);
+ return;
+ }
+ }
+
+ APPLMASK <<= 1;
+ ALIASPTR += ALIASLEN;
+
+ CMD++;
+
+ }
+ Session->BADCOMMANDS++;
+
+ if (Session->BADCOMMANDS > 6) // TOO MANY ERRORS
+ {
+ ReleaseBuffer((UINT *)Buffer);
+ Session->STAYFLAG = 0;
+ CLOSECURRENTSESSION(Session);
+ return;
+ }
+
+ ptr1 = ReplyPointer;
+
+ memcpy(ptr1, CMDERRMSG, CMDERRLEN);
+ ptr1 += CMDERRLEN;
+
+ SendCommandReply(Session, Buffer, (int)(ptr1 - (char *)Buffer));
+}
+
+
+VOID StatsTimer()
+{
+ struct PORTCONTROL * PORT = PORTTABLE;
+ uint64_t sum, sum2;
+
+ // Interval is 60 secs
+
+ while(PORT)
+ {
+ int index = PORT->StatsPointer++;
+
+ if (index == 1439)
+ PORT->StatsPointer = 0; // Cyclic through 24 hours (1440 Mins)
+
+ if (PORT->TNC)
+ {
+ struct TNCINFO * TNC = PORT->TNC;
+ if (TNC->Hardware == H_ARDOP || TNC->Hardware == H_VARA)
+ {
+ sum = TNC->PTTActivemS / 600; // ms but want %
+ PORT->AVSENDING = (UCHAR)sum;
+ TNC->PTTActivemS = 0;
+
+ sum2 = TNC->BusyActivemS / 600; // ms but want %
+ PORT->AVACTIVE = (UCHAR)(sum + sum2);
+ TNC->BusyActivemS = 0;
+ }
+ }
+ else
+ {
+ // if KISS port using QtSM Average is already updated
+
+ struct KISSINFO * KISS = (struct KISSINFO *)PORT;
+
+ if (PORT->PORTNUMBER == 17)
+ {
+ int x = 17;
+ }
+
+ if (PORT->PORTTXROUTINE == KISSTX && (KISS->QtSMStats || KISS->FIRSTPORT->PORT.QtSMPort)) // KISS Port QtSM Stats
+ {
+ }
+ else
+ {
+ sum = PORT->SENDING / 11;
+ PORT->AVSENDING = (UCHAR)sum;
+
+ sum = (PORT->SENDING + PORT->ACTIVE) /11;
+ PORT->AVACTIVE = (UCHAR)sum;
+ }
+ }
+
+ if (PORT->TX == NULL && PORT->AVACTIVE)
+ {
+ PORT->TX = zalloc(1440); // Keep 1 day history
+ PORT->BUSY = zalloc(1440);
+ }
+ if (PORT->TX)
+ {
+ PORT->TX[index] = PORT->AVSENDING;
+ PORT->BUSY[index] = PORT->AVACTIVE;
+ }
+
+ PORT->SENDING = 0;
+ PORT->ACTIVE = 0;
+
+ PORT = PORT->PORTPOINTER;
+ }
+}
+
+
+
+extern struct AXIPPORTINFO * Portlist[];
+
+#define TCPConnected 4
+
+
+VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // DISPLAY AXIP Resolver info
+
+ int Port = 0, index =0;
+ char * ptr, *Context;
+ struct PORTCONTROL * PORT = NULL;
+ struct AXIPPORTINFO * AXPORT;
+ char Normcall[11];
+ char Flags[10];
+ struct arp_table_entry * arp;
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ Port = atoi(ptr);
+
+ if (Port)
+ PORT = GetPortTableEntryFromPortNum(Port);
+
+ if (PORT == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ AXPORT = Portlist[Port];
+
+ if (AXPORT == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not an AXIP port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "AXIP Resolver info for Port %d\r", Port);
+
+ while (index < AXPORT->arp_table_len)
+ {
+ arp = &AXPORT->arp_table[index];
+
+ if (arp->ResolveFlag && arp->error != 0)
+ {
+ // resolver error - Display Error Code
+ sprintf(AXPORT->hostaddr, "Error %d", arp->error);
+ }
+ else
+ {
+ if (arp->IPv6)
+ Format_Addr((unsigned char *)&arp->destaddr6.sin6_addr, AXPORT->hostaddr, TRUE);
+ else
+ Format_Addr((unsigned char *)&arp->destaddr.sin_addr, AXPORT->hostaddr, FALSE);
+ }
+
+ ConvFromAX25(arp->callsign, Normcall);
+
+ Flags[0] = 0;
+
+ if (arp->BCFlag)
+ strcat(Flags, "B ");
+
+ if (arp->TCPState == TCPConnected)
+ strcat(Flags, "C ");
+
+ if (arp->AutoAdded)
+ strcat(Flags, "A");
+
+ if (arp->port == arp->SourcePort)
+ Bufferptr = Cmdprintf(Session, Bufferptr,"%.10s = %.64s %d = %-.42s %s\r",
+ Normcall,
+ arp->hostname,
+ arp->port,
+ AXPORT->hostaddr,
+ Flags);
+
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr,"%.10s = %.64s %d<%d = %-.42s %s\r",
+ Normcall,
+ arp->hostname,
+ arp->port,
+ arp->SourcePort,
+ AXPORT->hostaddr,
+ Flags);
+
+ index++;
+ }
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID AXMHEARD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ // DISPLAY AXIP Mheard info
+
+ int Port = 0, index = 0;
+ char * ptr, *Context;
+ struct PORTCONTROL * PORT = NULL;
+ struct AXIPPORTINFO * AXPORT;
+ int n = MHENTRIES;
+ char Normcall[11];
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ Port = atoi(ptr);
+
+ if (Port)
+ PORT = GetPortTableEntryFromPortNum(Port);
+
+ if (PORT == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ AXPORT = Portlist[Port];
+
+ if (AXPORT == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not an AXIP port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "AXIP Mheard for Port %d\r", Port);
+
+ while (index < MaxMHEntries)
+ {
+ if (AXPORT->MHTable[index].proto != 0)
+ {
+ char Addr[80];
+
+ Format_Addr((unsigned char *)&AXPORT->MHTable[index].ipaddr6, Addr, AXPORT->MHTable[index].IPv6);
+
+ Normcall[ConvFromAX25(AXPORT->MHTable[index].callsign, Normcall)] = 0;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s%-15s %c %-6d %-25s%c\r", Normcall,
+ Addr,
+ AXPORT->MHTable[index].proto,
+ AXPORT->MHTable[index].port,
+ asctime(gmtime( &AXPORT->MHTable[index].LastHeard )),
+ (AXPORT->MHTable[index].Keepalive == 0) ? ' ' : 'K');
+
+ Bufferptr[-3] = ' '; // Clear CR returned by asctime
+ }
+
+ index++;
+ }
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+#pragma pack()
+
+extern char WL2KCall[10];
+extern char WL2KLoc[7];
+
+BOOL GetWL2KSYSOPInfo(char * Call, char * _REPLYBUFFER);
+BOOL UpdateWL2KSYSOPInfo(char * Call, char * SQL);
+
+VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ char _REPLYBUFFER[1000] = "";
+
+ char LastUpdated[100];
+ char Name[100] = "";
+ char Addr1[100] = "";
+ char Addr2[100] = "";
+ char City[100] = "";
+ char State[100] = "";
+ char Country[100] = "";
+ char PostCode[100] = "";
+ char Email[100] = "";
+ char Website[100] = "";
+ char Phone[100] = "";
+ char Data[100] = "";
+ char LOC[100] = "";
+ BOOL Exists = TRUE;
+ time_t LastUpdateSecs = 0;
+ char * ptr1, * ptr2;
+
+ SOCKET sock;
+
+ int Len;
+ char Message[2048];
+
+ if (WL2KCall[0] < 33)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Winlink reporting is not configured\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+
+ if (GetWL2KSYSOPInfo(WL2KCall, _REPLYBUFFER) == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Failed to connect to WL2K Database\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (strstr(_REPLYBUFFER, "\"ErrorMessage\":"))
+ Exists = FALSE;
+
+ GetJSONValue(_REPLYBUFFER, "\"SysopName\":", Name);
+ GetJSONValue(_REPLYBUFFER, "\"StreetAddress1\":", Addr1);
+ GetJSONValue(_REPLYBUFFER, "\"StreetAddress2\":", Addr2);
+ GetJSONValue(_REPLYBUFFER, "\"City\":", City);
+ GetJSONValue(_REPLYBUFFER, "\"State\":", State);
+ GetJSONValue(_REPLYBUFFER, "\"Country\":", Country);
+ GetJSONValue(_REPLYBUFFER, "\"PostalCode\":", PostCode);
+ GetJSONValue(_REPLYBUFFER, "\"Email\":", Email);
+ GetJSONValue(_REPLYBUFFER, "\"Website\":", Website);
+ GetJSONValue(_REPLYBUFFER, "\"Phones\":", Phone);
+ GetJSONValue(_REPLYBUFFER, "\"Comments\":", Data);
+ GetJSONValue(_REPLYBUFFER, "\"GridSquare\":", LOC);
+ GetJSONValue(_REPLYBUFFER, "\"Timestamp\":", LastUpdated);
+
+ ptr1 = strchr(LastUpdated, '(');
+
+ if (ptr1)
+ {
+ ptr2 = strchr(++ptr1, ')');
+
+ if (ptr2)
+ {
+ *(ptr2 - 3) = 0; // remove millisecs
+ LastUpdateSecs = atoi(ptr1);
+
+ FormatTime3(LastUpdated, LastUpdateSecs);
+ }
+ }
+
+ if (_memicmp(CmdTail, "SET ", 4) == 0)
+ {
+ if (Exists)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Record already exists in WL2K Database\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // Set New Values. Any other params are values to set, separated by |
+
+// ptr1 = strtok_s(&CmdTail[4], ",", &Context);
+
+// if (ptr1 == NULL)
+// goto DoReplace;
+
+// strcpy(Name, ptr1);
+
+//DoReplace:
+
+ Len = sprintf(Message,
+ "\"Callsign\":\"%s\","
+ "\"GridSquare\":\"%s\","
+ "\"SysopName\":\"%s\","
+ "\"StreetAddress1\":\"%s\","
+ "\"StreetAddress2\":\"%s\","
+ "\"City\":\"%s\","
+ "\"State\":\"%s\","
+ "\"Country\":\"%s\","
+ "\"PostalCode\":\"%s\","
+ "\"Email\":\"%s\","
+ "\"Phones\":\"%s\","
+ "\"Website\":\"%s\","
+ "\"Comments\":\"%s\",",
+
+ WL2KCall, WL2KLoc, Name, Addr1, Addr2, City, State, Country, PostCode, Email, Phone, Website, Data);
+
+ Debugprintf("Sending %s", Message);
+
+ sock = OpenWL2KHTTPSock();
+
+ if (sock)
+ SendHTTPRequest(sock, "api.winlink.org", 80,
+ "/sysop/add", Message, Len, NULL);
+
+ closesocket(sock);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Database Updated\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (Exists)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\rWL2K SYSOP Info for %s\r", WL2KCall);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Grid Square: %s\r", LOC);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Name: %s\r", Name);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Addr Line 1: %s\r", Addr1);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Addr Line 2: %s\r", Addr2);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "City: %s\r", City);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "State: %s\r", State);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Country: %s\r", Country);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "PostCode: %s\r", PostCode);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Email Address: %s\r", Email);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Website: %s\r", Website);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Phone: %s\r", Phone);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Additional Data: %s\r", Data);
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Last Updated: %s\r", LastUpdated);
+ }
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "No SYSOP record for %s\r", WL2KCall);
+
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+VOID CloseKISSPort(struct PORTCONTROL * PortVector);
+
+VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ char _REPLYBUFFER[1000] = "";
+ char * ptr, * Context;
+
+ int portno;
+
+ struct TNCINFO * TNC;
+ struct TCPINFO * TCP;
+ struct PORTCONTROL * PORT = PORTTABLE;
+ int n = NUMBEROFPORTS;
+
+ // Get port number
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ {
+ portno = atoi (ptr);
+
+ if (portno)
+ {
+ while (n--)
+ {
+ if (PORT->PORTNUMBER == portno)
+ {
+ TNC = TNCInfo[portno];
+
+ if (!TNC || !TNC->TCPInfo)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet Port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ TCP = TNC->TCPInfo;
+
+ TCP->CMS = 0;
+ TCP->CMSOK = FALSE;
+#ifndef LINBPQ
+ CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3);
+ SetWindowText(TCP->hCMSWnd, "CMS Off");
+#endif
+ Bufferptr = Cmdprintf(Session, Bufferptr, "CMS Server Disabled\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ PORT = PORT->PORTPOINTER;
+ }
+ }
+ }
+
+ // Bad port
+
+ strcpy(Bufferptr, BADPORT);
+ Bufferptr += (int)strlen(BADPORT);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+
+VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ char _REPLYBUFFER[1000] = "";
+ char * ptr, * Context;
+
+ int portno;
+
+ struct TNCINFO * TNC;
+ struct TCPINFO * TCP;
+ struct PORTCONTROL * PORT = PORTTABLE;
+ int n = NUMBEROFPORTS;
+
+ // Get port number
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ {
+ portno = atoi (ptr);
+
+ if (portno)
+ {
+ while (n--)
+ {
+ if (PORT->PORTNUMBER == portno)
+ {
+ TNC = TNCInfo[portno];
+
+ if (!TNC || !TNC->TCPInfo)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not a Telnet Port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ TCP = TNC->TCPInfo;
+ TCP->CMS = 1;
+#ifndef LINBPQ
+ CheckMenuItem(TCP->hActionMenu, 3, MF_BYPOSITION | TCP->CMS<<3);
+#endif
+ CheckCMS(TNC);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "CMS Server Enabled\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ PORT = PORT->PORTPOINTER;
+ }
+ }
+ }
+
+ // Bad port
+
+ strcpy(Bufferptr, BADPORT);
+ Bufferptr += (int)strlen(BADPORT);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+
+VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ char _REPLYBUFFER[1000] = "";
+ char * ptr, * Context;
+
+ int portno;
+ struct PORTCONTROL * PORT = PORTTABLE;
+ int n = NUMBEROFPORTS;
+
+ // Get port number
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ {
+ portno = atoi (ptr);
+
+ if (portno)
+ {
+ while (n--)
+ {
+ if (PORT->PORTNUMBER == portno)
+ {
+ struct KISSINFO * KISS;
+
+ if (PORT->PORTSTOPCODE)
+ {
+ // Port has Close Routine
+
+ PORT->PortStopped = TRUE;
+
+ if (PORT->PORTSTOPCODE(PORT))
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Port Closed\r");
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Port Close Failed\r");
+
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ if (PORT->PORTTYPE != 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not a serial port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ KISS = (struct KISSINFO *) PORT;
+
+ if (KISS->FIRSTPORT != KISS)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ CloseKISSPort(PORT);
+ PORT->PortStopped = TRUE;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Port Closed\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+
+ return;
+ }
+ PORT = PORT->PORTPOINTER;
+ }
+ }
+ }
+
+ // Bad port
+
+ strcpy(Bufferptr, BADPORT);
+ Bufferptr += (int)strlen(BADPORT);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+
+VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ char _REPLYBUFFER[1000] = "";
+ char * ptr, * Context;
+
+ int portno;
+ struct PORTCONTROL * PORT = PORTTABLE;
+ int n = NUMBEROFPORTS;
+
+ // Get port number
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ {
+ portno = atoi (ptr);
+
+ if (portno)
+ {
+ while (n--)
+ {
+ if (PORT->PORTNUMBER == portno)
+ {
+ struct KISSINFO * KISS;
+
+ if (PORT->PORTSTARTCODE)
+ {
+ // Port has Open Routine
+
+ PORT->PortStopped = FALSE;
+
+ if (PORT->PORTSTARTCODE(PORT))
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r");
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r");
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+
+ if (PORT->PORTTYPE != 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (PORT->PORTIPADDR.s_addr || PORT->KISSSLAVE)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not a serial port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ KISS = (struct KISSINFO *) PORT;
+
+ if (KISS->FIRSTPORT != KISS)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (OpenConnection(PORT))
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Port Opened\r");
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Port Open Failed\r");
+
+ PORT->PortStopped = FALSE;
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ PORT = PORT->PORTPOINTER;
+ }
+ }
+ }
+
+ // Bad port
+
+ strcpy(Bufferptr, BADPORT);
+ Bufferptr += (int)strlen(BADPORT);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+
+
+int ASYSEND(struct PORTCONTROL * PortVector, char * buffer, int count);
+int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len);
+
+VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ char _REPLYBUFFER[1000] = "";
+ char * ptr, * Context;
+
+ int portno = 0;
+ struct PORTCONTROL * PORT = PORTTABLE;
+ int n = NUMBEROFPORTS;
+ UCHAR KissString[128];
+ UCHAR ENCBUFF[256];
+ int KissLen = 0;
+ unsigned char * Kissptr = KissString;
+
+ // Send KISS Command to TNC
+
+ // Get port number
+
+ ptr = strtok_s(CmdTail, " ", &Context);
+
+ if (ptr)
+ {
+ portno = atoi (ptr);
+ ptr = strtok_s(NULL, " ", &Context);
+
+ while (ptr && ptr[0] && KissLen < 120)
+ {
+ *(Kissptr++) = atoi (ptr);
+ KissLen++;
+ ptr = strtok_s(NULL, " ", &Context);
+
+ }
+ }
+
+ if (portno == 0 || KissLen == 0)
+ {
+ strcpy(Bufferptr, BADMSG);
+ Bufferptr += (int)strlen(BADMSG);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ while (n--)
+ {
+ if (PORT->PORTNUMBER == portno)
+ {
+ struct KISSINFO * KISS;
+
+ if (PORT->PORTTYPE != 0 && PORT->PORTTYPE != 22)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not a KISS Port\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ KISS = (struct KISSINFO *) PORT;
+
+ if (KISS->FIRSTPORT != KISS)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Not first port of a Multidrop Set\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ // Send Command
+
+ KissLen = KissEncode(KissString, ENCBUFF, KissLen);
+
+ PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q
+
+ PORT->Session = Session;
+ PORT->LastKISSCmdTime = time(NULL);
+
+ ASYSEND(PORT, ENCBUFF, KissLen);
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Command Sent\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+ PORT = PORT->PORTPOINTER;
+ }
+
+
+ // Bad port
+
+ strcpy(Bufferptr, BADPORT);
+ Bufferptr += (int)strlen(BADPORT);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+
+VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ FindLostBuffers();
+
+#ifdef WIN32
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Lost buffer info dumped to Debugview\r");
+#else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Lost buffer info dumped to syslog\r");
+#endif
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+VOID FLMSG(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * UserCMD)
+{
+ // Telnet Connection from FLMSG
+ CLOSECURRENTSESSION(Session); // Kills any crosslink, plus local link
+ ReleaseBuffer((UINT *)REPLYBUFFER);
+}
+
+BOOL CheckExcludeList(UCHAR * Call)
+{
+ UCHAR * ptr1 = ExcludeList;
+
+ while (*ptr1)
+ {
+ if (memcmp(Call, ptr1, 6) == 0)
+ return FALSE;
+
+ ptr1 += 7;
+ }
+
+ return TRUE;
+}
+
+
+void ListExcludedCalls(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+
+ UCHAR * ptr = ExcludeList;
+ char Normcall[10] = "";
+ UCHAR AXCall[8] = "";
+
+ if (*CmdTail == ' ')
+ goto DISPLIST;
+
+ if (*CmdTail == 'Z')
+ {
+ // CLEAR LIST
+
+ memset(ExcludeList, 0, 70);
+ goto DISPLIST;
+ }
+
+ ConvToAX25(CmdTail, AXCall);
+
+ if (strlen(ExcludeList) < 70)
+ strcat(ExcludeList, AXCall);
+
+DISPLIST:
+
+ while (*ptr)
+ {
+ Normcall[ConvFromAX25(ptr, Normcall)] = 0;
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s ", Normcall);
+ ptr += 7;
+ }
+
+ *(Bufferptr++) = '\r';
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+BOOL isSYSOP(TRANSPORTENTRY * Session, char * Bufferptr)
+{
+ if (Session->PASSWORD != 0xFFFF)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s", PASSWORDMSG);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+VOID HELPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ int FileSize;
+ char MsgFile[MAX_PATH];
+ FILE * hFile;
+ char * MsgBytes;
+ struct stat STAT;
+ char * ptr1, * ptr, * ptr2;
+
+ sprintf_s(MsgFile, sizeof(MsgFile), "%s/%s", BPQDirectory, "NodeHelp.txt");
+
+ if (stat(MsgFile, &STAT) == -1)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Help file not found\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ FileSize = STAT.st_size;
+
+ hFile = fopen(MsgFile, "rb");
+
+ if (hFile == NULL)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Help file not found\r");
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ MsgBytes = malloc(FileSize+1);
+
+ fread(MsgBytes, 1, FileSize, hFile);
+
+ fclose(hFile);
+
+ MsgBytes[FileSize] = 0;
+
+ ptr1 = MsgBytes;
+
+ // Replace LF or CRLF with CR
+
+ // First remove cr from crlf
+
+ while(ptr2 = strstr(ptr1, "\r\n"))
+ {
+ memmove(ptr2, ptr2 + 1, strlen(ptr2));
+ }
+
+ // Now replace lf with cr
+
+ ptr1 = MsgBytes;
+
+ while (*ptr1)
+ {
+ if (*ptr1 == '\n')
+ *(ptr1) = '\r';
+
+ ptr1++;
+ }
+
+ ptr = ptr1 = MsgBytes;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "\r");
+
+ // Read and send a line at a time, converting any line endings into CR
+
+ while (*ptr1)
+ {
+ if (*ptr1 == '\r')
+ {
+ *(ptr1++) = 0;
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "%s\r", ptr);
+
+ ptr = ptr1;
+ }
+ else
+ ptr1++;
+ }
+
+ free(MsgBytes);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+}
+
+int UZ7HOSetFreq(int port, struct TNCINFO * TNC, struct AGWINFO * AGW, PDATAMESSAGE buff, PMSGWITHLEN buffptr);
+int UZ7HOSetModem(int port, struct TNCINFO * TNC, struct AGWINFO * AGW, PDATAMESSAGE buff, PMSGWITHLEN buffptr);
+int UZ7HOSetFlags(int port, struct TNCINFO * TNC, struct AGWINFO * AGW, PDATAMESSAGE buff, PMSGWITHLEN buffptr);
+
+
+VOID UZ7HOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ char * Cmd;
+ int port;
+ struct TNCINFO * TNC;
+ struct AGWINFO * AGW = 0;
+ PDATAMESSAGE buff;
+ PMSGWITHLEN buffptr;
+
+ CmdTail = CmdTail + (OrigCmdBuffer - COMMANDBUFFER); // Replace with original case version
+
+ Cmd = strlop(CmdTail, ' ');
+ port = atoi(CmdTail);
+
+ // remove trailing spaces
+
+ while(strlen(Cmd) && Cmd[strlen(Cmd) - 1] == ' ')
+ Cmd[strlen(Cmd) - 1] = 0;
+
+ TNC = TNCInfo[port];
+
+ if (TNC)
+ AGW = TNC->AGWInfo;
+
+ if (TNC == 0 || AGW == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Error - %d is not UZ7HO port\r", port);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ if (_memicmp(Cmd, "FREQ", 4) == 0 || _memicmp(Cmd, "MODEM", 5) == 0 || _memicmp(Cmd, "FLAGS", 5) == 0)
+ {
+ // Pass to procesing code in UZ7HO driver. This expects command in a PDATAMESSAGE amd places response in a PMSGWITHLEN buffer
+
+ buff = (PDATAMESSAGE)GetBuff();
+ buffptr = (PMSGWITHLEN)GetBuff();
+
+ if (buffptr == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "UZ7HO Command Failed - no buffers\r");
+ if (buff)
+ ReleaseBuffer(buff);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+
+
+ buff->LENGTH = sprintf(buff->L2DATA, "%s\r", Cmd) + MSGHDDRLEN + 1;
+
+ if (_memicmp(Cmd, "FREQ", 4) == 0)
+ UZ7HOSetFreq(port, TNC, AGW, buff, buffptr);
+ else if (_memicmp(Cmd, "FLAGS", 5) == 0)
+ UZ7HOSetFlags(port, TNC, AGW, buff, buffptr);
+ else
+ UZ7HOSetModem(port, TNC, AGW, buff, buffptr);
+
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, buffptr->Data);
+
+ ReleaseBuffer(buff);
+ ReleaseBuffer(buffptr);
+ }
+ else
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid UZ7HO Command (not Freq Modem or FLAGS)\r");
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+VOID QTSMCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, CMDX * CMD)
+{
+ int port;
+ struct PORTCONTROL * PORT;
+ struct KISSINFO * KISS;
+
+ CmdTail = CmdTail + (OrigCmdBuffer - COMMANDBUFFER); // Replace with original case version
+
+ port = atoi(CmdTail);
+
+ PORT = GetPortTableEntryFromPortNum(port);
+
+ if (PORT == NULL || PORT->PORTTXROUTINE != KISSTX) // Must be a kiss like port
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port %d is not a KISS port\r", port);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ KISS = (struct KISSINFO *)PORT;
+
+ if (KISS->QtSMModem == 0)
+ {
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Error - Port %d has no QtSM information\r", port);
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+ }
+
+ Bufferptr = Cmdprintf(Session, Bufferptr, "Modem %s Centre frequency %d\r",
+ (KISS->QtSMModem) ? KISS->QtSMModem : "Not Available", KISS->QtSMFreq);
+
+ SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
+ return;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CommonCode.c b/CommonCode.c
index a392b17..e3504d1 100644
--- a/CommonCode.c
+++ b/CommonCode.c
@@ -4912,7 +4912,7 @@ SOCKET OpenHTTPSock(char * Host)
{
err = WSAGetLastError();
- Debugprintf("Resolve Failed for %s %d %x", "api.winlink.org", err, err);
+ Debugprintf("Resolve Failed for %s %d %x", Host, err, err);
return 0 ; // Resolve failed
}
@@ -4945,7 +4945,7 @@ SOCKET OpenHTTPSock(char * Host)
}
static char HeaderTemplate[] = "POST %s HTTP/1.1\r\n"
- "Accept: application/json\r\n"
+ "Accept: app N B lication/json\r\n"
// "Accept-Encoding: gzip,deflate,gzip, deflate\r\n"
"Content-Type: application/json\r\n"
"Host: %s:%d\r\n"
@@ -4955,14 +4955,24 @@ static char HeaderTemplate[] = "POST %s HTTP/1.1\r\n"
"\r\n";
-VOID SendWebRequest(SOCKET sock, char * Host, char * Request, char * Params, int Len, char * Return)
+DllExport VOID WINAPI SendWebRequest(char * Host, char * Request, char * Params, char * Return)
{
+ SOCKET sock;
int InputLen = 0;
int inptr = 0;
char Buffer[4096];
char Header[256];
char * ptr, * ptr1;
int Sent;
+ int Len = strlen(Params);
+
+ if (M0LTEMap == 0)
+ return;
+
+ sock = OpenHTTPSock(Host);
+
+ if (sock == 0)
+ return;
#ifdef LINBPQ
sprintf(Header, HeaderTemplate, Request, Host, 80, Len, "linbpq/", VersionString, Params);
@@ -4976,6 +4986,7 @@ VOID SendWebRequest(SOCKET sock, char * Host, char * Request, char * Params, int
{
int Err = WSAGetLastError();
Debugprintf("Error %d from Web Update send()", Err);
+ closesocket(sock);
return;
}
@@ -4987,12 +4998,10 @@ VOID SendWebRequest(SOCKET sock, char * Host, char * Request, char * Params, int
{
int Err = WSAGetLastError();
Debugprintf("Error %d from Web Update recv()", Err);
+ closesocket(sock);
return;
}
- // As we are using a persistant connection, can't look for close. Check
- // for complete message
-
inptr += InputLen;
Buffer[inptr] = 0;
@@ -5035,6 +5044,7 @@ VOID SendWebRequest(SOCKET sock, char * Host, char * Request, char * Params, int
Debugprintf("Map Update failed - %s", Buffer);
}
+ closesocket(sock);
return;
}
}
@@ -5046,6 +5056,7 @@ VOID SendWebRequest(SOCKET sock, char * Host, char * Request, char * Params, int
{
// Just accept anything until I've sorted things with Lee
Debugprintf("%s", ptr1);
+ closesocket(sock);
Debugprintf("Web Database update ok");
return;
}
@@ -5584,19 +5595,11 @@ void SendDataToPktMap(char *Msg)
}
],
-
-
*/
// "contact": "string",
// "neighbours": [{"node": "G7TAJ","port": "30"}]
- sock = OpenHTTPSock("packetnodes.spots.radio");
-
- if (sock == 0)
- return;
-
- SendWebRequest(sock, "packetnodes.spots.radio", Request, Params, strlen(Params), Return);
- closesocket(sock);
+ SendWebRequest("packetnodes.spots.radio", Request, Params, Return);
}
// ="{\"neighbours\": [{\"node\": \"G7TAJ\",\"port\": \"30\"}]}";
diff --git a/FormatHTML.vcproj.LAPTOP-Q6S4RP5Q.johnw.user b/FormatHTML.vcproj.LAPTOP-Q6S4RP5Q.johnw.user
new file mode 100644
index 0000000..bed4096
--- /dev/null
+++ b/FormatHTML.vcproj.LAPTOP-Q6S4RP5Q.johnw.user
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/L2Code-skigdebian.c b/L2Code-skigdebian.c
new file mode 100644
index 0000000..a36854d
--- /dev/null
+++ b/L2Code-skigdebian.c
@@ -0,0 +1,4143 @@
+/*
+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
+*/
+
+//
+// C replacement for L2Code.asm
+//
+#define Kernel
+
+#define _CRT_SECURE_NO_DEPRECATE
+
+
+#pragma data_seg("_BPQDATA")
+
+#include "time.h"
+#include "stdio.h"
+
+#include "CHeaders.h"
+#include "tncinfo.h"
+
+#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE
+
+#define REJSENT 1 // SET WHEN FIRST REJ IS SENT IN REPLY
+ // TO AN I(P)
+#define RNRSET 0x2 // RNR RECEIVED FROM OTHER END
+#define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED
+#define RNRSENT 0x10 // WE HAVE SEND RNR
+#define POLLSENT 0x20 // POLL BIT OUTSTANDING
+
+#define ONEMINUTE 60*3
+#define TENSECS 10*3
+#define THREESECS 3*3
+
+
+VOID L2SENDCOMMAND();
+VOID L2ROUTINE();
+MESSAGE * SETUPL2MESSAGE(struct _LINKTABLE * LINK, UCHAR CMD);
+VOID SendSupervisCmd(struct _LINKTABLE * LINK);
+void SEND_RR_RESP(struct _LINKTABLE * LINK, UCHAR PF);
+VOID L2SENDRESPONSE(struct _LINKTABLE * LINK, int CMD);
+VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD);
+VOID ACKMSG(struct _LINKTABLE * LINK);
+VOID InformPartner(struct _LINKTABLE * LINK, int Reason);
+UINT RR_OR_RNR(struct _LINKTABLE * LINK);
+VOID L2TIMEOUT(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT);
+VOID CLEAROUTLINK(struct _LINKTABLE * LINK);
+VOID SENDFRMR(struct _LINKTABLE * LINK);
+char * SetupNodeHeader(struct DATAMESSAGE * Buffer);
+VOID CLEARSESSIONENTRY(TRANSPORTENTRY * Session);
+VOID SDFRMR(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT);
+VOID SDNRCHK(struct _LINKTABLE * LINK, UCHAR CTL);
+VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS);
+VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer);
+VOID RESET2X(struct _LINKTABLE * LINK);
+VOID RESET2(struct _LINKTABLE * LINK);
+VOID CONNECTREFUSED(struct _LINKTABLE * LINK);
+VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL);
+VOID SFRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, UCHAR CTL, UCHAR MSGFLAG);
+VOID SDIFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG);
+VOID SENDCONNECTREPLY(struct _LINKTABLE * LINK);
+VOID SETUPNEWL2SESSION(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR MSGFLAG);
+BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE);
+VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR MSGFLAG);
+VOID L2SENDUA(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER);
+VOID L2SENDDM(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER);
+VOID L2SENDRESP(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL);
+int COUNTLINKS(int Port);
+VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG);
+TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK);
+BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions);
+VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer);
+VOID L2SWAPADDRESSES(MESSAGE * Buffer);
+BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK);
+VOID SENDSABM(struct _LINKTABLE * LINK);
+VOID L2SENDXID(struct _LINKTABLE * LINK);
+VOID __cdecl Debugprintf(const char * format, ...);
+VOID Q_IP_MSG(MESSAGE * Buffer);
+VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT);
+VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG);
+BOOL CompareAliases(UCHAR * c1, UCHAR * c2);
+VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG);
+VOID Digipeat(struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR * OurCall, int toPort, int UIOnly);
+VOID DigiToMultiplePorts(struct PORTCONTROL * PORTVEC, PMESSAGE Msg);
+VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer);
+BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg);
+VOID L2SENDINVALIDCTRL(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL);
+UCHAR * SETUPADDRESSES(struct _LINKTABLE * LINK, PMESSAGE Msg);
+VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG);
+int CountBits(uint32_t in);
+void AttachKISSHF(struct PORTCONTROL * PORT, MESSAGE * Buffer);
+void DetachKISSHF(struct PORTCONTROL * PORT);
+void KISSHFConnected(struct PORTCONTROL * PORT, struct _LINKTABLE * LINK);
+void WriteConnectLog(char * fromcall, char * tocall, UCHAR * Mode);
+int seeifInterlockneeded(struct PORTCONTROL * PORT);
+int seeifUnlockneeded(struct _LINKTABLE * LINK);
+int CheckKissInterlock(struct PORTCONTROL * MYPORT, int Exclusive);
+void hookL2SessionAccepted(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK);
+void hookL2SessionDeleted(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK);
+void hookL2SessionAttempt(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK);
+
+
+extern int REALTIMETICKS;
+
+// MSGFLAG contains CMD/RESPONSE BITS
+
+#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND
+#define RESP 2 // CURRENT MSG IS RESPONSE
+#define VER1 1 // CURRENT MSG IS VERSION 1
+
+// FRMR REJECT FLAGS
+
+#define SDINVC 1 // INVALID COMMAND
+#define SDNRER 8 // INVALID N(R)
+
+
+
+UCHAR NO_CTEXT = 0;
+UCHAR ALIASMSG = 0;
+extern UINT APPLMASK;
+static UCHAR ISNETROMMSG = 0;
+UCHAR MSGFLAG = 0;
+extern char * ALIASPTR;
+
+UCHAR QSTCALL[7] = {'Q'+'Q','S'+'S','T'+'T',0x40,0x40,0x40,0xe0}; // QST IN AX25
+UCHAR NODECALL[7] = {0x9C, 0x9E, 0x88, 0x8A, 0xA6, 0x40, 0xE0}; // 'NODES' IN AX25 FORMAT
+
+extern BOOL LogAllConnects;
+
+APPLCALLS * APPL;
+
+VOID L2Routine(struct PORTCONTROL * PORT, PMESSAGE Buffer)
+{
+ // LEVEL 2 PROCESSING
+
+ MESSAGE * ADJBUFFER;
+ struct _LINKTABLE * LINK;
+ UCHAR * ptr;
+ int n;
+ UCHAR CTL;
+ uintptr_t Work;
+ UCHAR c;
+
+ // Check for invalid length (< 22 7Header + 7Addr + 7Addr + CTL
+
+ if (Buffer->LENGTH < (18 + sizeof(void *)))
+ {
+ Debugprintf("BPQ32 Bad L2 Msg Port %d Len %d", PORT->PORTNUMBER, Buffer->LENGTH);
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ PORT->L2FRAMES++;
+
+ ALIASMSG = 0;
+ APPLMASK = 0;
+ ISNETROMMSG = 0;
+
+ MSGFLAG = 0; // CMD/RESP UNDEFINED
+
+ // Check for Corrupted Callsign in Origin (to keep MH list clean)
+
+ ptr = &Buffer->ORIGIN[0];
+ n = 6;
+
+ c = *(ptr) >> 1;
+
+ if (c == ' ') // Blank Call
+ {
+ Debugprintf("BPQ32 Blank Call Port %d", PORT->PORTNUMBER);
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ while(n--)
+ {
+ // Try a bit harder to detect corruption
+
+ c = *(ptr++);
+
+ if (c & 1)
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ c = c >> 1;
+
+ if (!isalnum(c) && !(c == '#') && !(c == ' '))
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+ }
+
+ // Check Digis if present
+
+ if ((Buffer->ORIGIN[6] & 1) == 0) // Digis
+ {
+ ptr = &Buffer->CTL;
+ n = 6;
+
+ while(n--)
+ {
+ c = *(ptr++);
+
+ if (c & 1)
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ c = c >> 1;
+
+ if (!isalnum(c) && !(c == '#') && !(c == ' '))
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+ }
+ }
+
+ BPQTRACE(Buffer, TRUE); // TRACE - RX frames to APRS
+
+ if (PORT->PORTMHEARD)
+ MHPROC(PORT, Buffer);
+
+ /// TAJ added 07/12/2020 for 'all RX traffic as IfinOctects
+
+ InOctets[PORT->PORTNUMBER] += Buffer->LENGTH - MSGHDDRLEN;
+
+ // CHECK THAT ALL DIGIS HAVE BEEN ACTIONED,
+ // AND ADJUST FOR DIGIPEATERS IF PRESENT
+
+ n = 8; // MAX DIGIS
+ ptr = &Buffer->ORIGIN[6]; // End of Address bit
+
+ while ((*ptr & 1) == 0)
+ {
+ // MORE TO COME
+
+ ptr += 7;
+
+ if ((*ptr & 0x80) == 0) // Digi'd bit
+ {
+ // FRAME HAS NOT BEEN REPEATED THROUGH CURRENT DIGI -
+ // SEE IF WE ARE MEANT TO DIGI IT
+
+ struct XDIGI * XDigi = PORT->XDIGIS; // Cross port digi setup
+
+ ptr -= 6; // To start of Call
+
+ if (CompareCalls(ptr, MYCALL) || CompareAliases(ptr, MYALIAS) ||
+ CompareCalls(ptr, PORT->PORTALIAS) || CompareCalls(ptr, PORT->PORTALIAS2))
+ {
+ Digipeat(PORT, Buffer, ptr, 0, 0); // Digi it (if enabled)
+ return;
+ }
+
+ while (XDigi)
+ {
+ if (CompareCalls(ptr, XDigi->Call))
+ {
+ Digipeat(PORT, Buffer, ptr, XDigi->Port, XDigi->UIOnly); // Digi it (if enabled)
+ return;
+ }
+ XDigi = XDigi->Next;
+ }
+
+ ReleaseBuffer(Buffer);
+ return; // not complete and not for us
+ }
+ n--;
+
+ if (n == 0)
+ {
+ ReleaseBuffer(Buffer);
+ return; // Corrupt - no end of address bit
+ }
+ }
+
+ // Reached End of digis, and all actioned, so can process it
+
+ Work = (uintptr_t)&Buffer->ORIGIN[6];
+ ptr -= Work; // ptr is now length of digis
+
+ Work = (uintptr_t)Buffer;
+ ptr += Work;
+
+ ADJBUFFER = (MESSAGE * )ptr; // ADJBUFFER points to CTL, etc. allowing for digis
+
+ // GET CMD/RESP BITS
+
+ if (Buffer->DEST[6] & 0x80)
+ {
+ if (Buffer->ORIGIN[6] & 0x80) // Both set, assume V1
+ MSGFLAG |= VER1;
+ else
+ MSGFLAG |= CMDBIT;
+ }
+ else
+ {
+ if (Buffer->ORIGIN[6] & 0x80) // Only Dest Set
+ MSGFLAG |= RESP;
+ else
+ MSGFLAG |= VER1; // Neither, assume V1
+ }
+
+ // SEE IF FOR AN ACTIVE LINK SESSION
+
+ CTL = ADJBUFFER->CTL;
+
+ // IF A UI, THERE IS NO SESSION
+
+ if (FindLink(Buffer->ORIGIN, Buffer->DEST, PORT->PORTNUMBER, &LINK))
+ {
+ L2LINKACTIVE(LINK, PORT, Buffer,ADJBUFFER, CTL, MSGFLAG);
+ return;
+ }
+
+ // NOT FOR ACTIVE LINK - SEE IF ADDRESSED TO OUR ADDRESSES
+
+ // FIRST TRY PORT ADDR/ALIAS
+
+ if(PORT->PORTBBSFLAG == 1)
+ goto PORTCALLISBBS; // PORT CALL/ALIAS ARE FOR BBS
+
+ if (NODE)
+ goto USING_NODE;
+
+PORTCALLISBBS:
+
+ // NODE IS NOT ACTIVE, SO PASS CALLS TO PORTCALL/ALIAS TO BBS
+
+ APPLMASK = 1;
+
+ if (CompareCalls(Buffer->DEST, NETROMCALL))
+ {
+ ISNETROMMSG = 1;
+ goto FORUS;
+ }
+ if (PORT->PORTL3FLAG) // L3 Only Port?
+ goto NOTFORUS; // If L3ONLY, only accept calls to NETROMCALL
+
+ ISNETROMMSG = 0;
+
+USING_NODE:
+
+ if (CompareCalls(Buffer->DEST, PORT->PORTCALL))
+ goto FORUS;
+
+ ALIASMSG = 1;
+
+ if (CompareAliases(Buffer->DEST, PORT->PORTALIAS)) // only compare 6 bits - allow any ssid
+ goto FORUS;
+
+ if (NODE == 0)
+ goto TRYBBS; // NOT USING NODE SYSTEM
+
+ ALIASMSG = 0;
+
+ if (CompareCalls(Buffer->DEST, MYCALL))
+ goto FORUS;
+
+ ALIASMSG = 1;
+
+ if (CompareAliases(Buffer->DEST, MYALIAS)) // only compare 6 bits - allow any ssid
+ goto FORUS;
+
+TRYBBS:
+
+ if (BBS == 0)
+ goto NOWTRY_NODES; // NOT USING BBS CALLS
+
+ // TRY APPLICATION CALLSIGNS/ALIASES
+
+
+ APPLMASK = 1;
+ ALIASPTR = &CMDALIAS[0][0];
+
+ n = NumberofAppls;
+
+ APPL = APPLCALLTABLE;
+
+ while (n--)
+ {
+ if (APPL->APPLCALL[0] > 0x40) // Valid ax.25 addr
+ {
+ // WE MAY NOT BE ALLOWED TO USE THE BBS CALL ON SOME BANDS DUE TO
+ // THE RATHER ODD UK LICENCING RULES!
+ // For backward compatibility only apply to appl 1
+
+ if ((PORT->PERMITTEDAPPLS & APPLMASK) != 0)
+ {
+ ALIASMSG = 0;
+
+ if (CompareCalls(Buffer->DEST, APPL->APPLCALL))
+ goto FORUS;
+
+ ALIASMSG = 1;
+
+ if (CompareAliases(Buffer->DEST, APPL->APPLALIAS)) // only compare 6 bits - allow any ssid
+ goto FORUS;
+
+ if (CompareAliases(Buffer->DEST, APPL->L2ALIAS)) // only compare 6 bits - allow any ssid
+ goto FORUS;
+ }
+ }
+ APPLMASK <<= 1;
+ ALIASPTR += ALIASLEN;
+ APPL++;
+ }
+
+ // NOT FOR US - SEE IF 'NODES' OR IP/ARP BROADCAST MESSAGE
+
+NOWTRY_NODES:
+
+ if (CompareCalls(Buffer->DEST, QSTCALL))
+ {
+ Q_IP_MSG(Buffer); // IP BROADCAST
+ return;
+ }
+
+ if (ADJBUFFER->PID != 0xCF) // NETROM MSG?
+ goto NOTFORUS; // NO
+
+ if (CompareCalls(Buffer->DEST, NODECALL))
+ {
+ if (Buffer->L2DATA[0] == 0xff) // Valid NODES Broadcast
+ {
+ PROCESSNODEMESSAGE(Buffer, PORT);
+ }
+ }
+
+ ReleaseBuffer(Buffer);
+ return;
+
+NOTFORUS:
+ //
+ // MAY JUST BE A REPLY TO A 'PRIMED' CQ CALL
+ //
+ if ((CTL & ~PFBIT) == SABM)
+ if (CheckForListeningSession(PORT, Buffer))
+ return; // Used buffer to send UA
+
+ ReleaseBuffer(Buffer);
+ return;
+
+FORUS:
+
+ // if a UI frame and UIHook Specified, call it
+
+ if (PORT->UIHook && CTL == 3)
+ PORT->UIHook(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG);
+
+ L2FORUS(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG);
+}
+
+
+VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer)
+{
+ PMHSTRUC MH = PORT->PORTMHEARD;
+ PMHSTRUC MHBASE = MH;
+ int i;
+ int OldCount = 0;
+ char Freq[64] = "";
+ char DIGI = '*';
+ double ReportFreq = 0;
+
+ // if port has a freq associated with it use it
+
+ GetPortFrequency(PORT->PORTNUMBER, Freq);
+
+ // if (Buffer->ORIGIN[6] & 1)
+ DIGI = 0; // DOn't think we want to do this
+
+ // See if in list
+
+ for (i = 0; i < MHENTRIES; i++)
+ {
+ if ((MH->MHCALL[0] == 0) || (CompareCalls(Buffer->ORIGIN, MH->MHCALL) && MH->MHDIGI == DIGI)) // Spare or our entry
+ {
+ OldCount = MH->MHCOUNT;
+ goto DoMove;
+ }
+ MH++;
+ }
+
+ // TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP
+
+ i = MHENTRIES - 1;
+
+ // Move others down and add at front
+DoMove:
+ if (i != 0) // First
+ memmove(MHBASE + 1, MHBASE, i * sizeof(MHSTRUC));
+
+ memcpy (MHBASE->MHCALL, Buffer->ORIGIN, 7 * 9); // Save Digis
+ MHBASE->MHDIGI = DIGI;
+ MHBASE->MHTIME = time(NULL);
+ MHBASE->MHCOUNT = ++OldCount;
+ strcpy(MHBASE->MHFreq, Freq);
+ MHBASE->MHLocator[0] = 0;
+
+ return;
+}
+
+
+int CountFramesQueuedOnSession(TRANSPORTENTRY * Session)
+{
+ // COUNT NUMBER OF FRAMES QUEUED ON A SESSION
+
+ if (Session == 0)
+ return 0;
+
+ if (Session->L4CIRCUITTYPE & BPQHOST)
+ {
+ return C_Q_COUNT(&Session->L4TX_Q);
+ }
+
+ if (Session->L4CIRCUITTYPE & SESSION)
+ {
+ // L4 SESSION - GET NUMBER UNACKED, AND ADD NUMBER ON TX QUEUE
+
+ int Count = C_Q_COUNT(&Session->L4TX_Q);
+ UCHAR Unacked = Session->TXSEQNO - Session->L4WS;
+
+ return Count + Unacked;
+ }
+
+ if (Session->L4CIRCUITTYPE & PACTOR)
+ {
+ // PACTOR Type - Frames are queued on the Port Entry
+
+ struct PORTCONTROL * PORT = Session->L4TARGET.PORT;
+ EXTPORTDATA * EXT = (EXTPORTDATA *)PORT;
+
+ int ret = EXT->FramesQueued;
+
+ // Check L4 Queue as messages can stay there briefly
+
+ ret += C_Q_COUNT(&Session->L4RX_Q);
+
+ return ret + C_Q_COUNT(&PORT->PORTTX_Q);
+ }
+
+ // L2 CIRCUIT
+
+ {
+ int SessCount = C_Q_COUNT(&Session->L4TX_Q);
+ struct _LINKTABLE * LINK = Session->L4TARGET.LINK;
+ int L2 = COUNT_AT_L2(LINK);
+
+ return SessCount + L2;
+ }
+}
+
+int CHECKIFBUSYL2(TRANSPORTENTRY * Session)
+{
+ // RETURN TOP BIT OF AL SET IF SESSION PARTNER IS BUSY
+
+ if (Session->L4CROSSLINK) // CONNECTED?
+ {
+ Session = Session->L4CROSSLINK;
+
+ if (CountFramesQueuedOnSession(Session) > 10)
+ return L4BUSY;;
+ }
+ return 0;
+}
+
+VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG)
+{
+ // MESSAGE ADDRESSED TO OUR CALL OR ALIAS, BUT NOT FOR AN ACTIVE SESSION
+
+ // LINK points to an empty link table entry
+
+ struct ROUTE * ROUTE;
+ int CTLlessPF = CTL & ~PFBIT;
+
+ PORT->L2FRAMESFORUS++;
+
+ NO_CTEXT = 0;
+
+ // ONLY SABM or UI ALLOWED IF NO SESSION
+ // Plus XID/TEST/SABME if V2.2 support enabled
+
+ if (CTLlessPF == 3) // UI
+ {
+ // A UI ADDRESSED TO US - SHOULD ONLY BE FOR IP, or possibly addressed NODES
+
+ switch(ADJBUFFER->PID)
+ {
+ case 0xcf: // Netrom
+
+ if (Buffer->L2DATA[0] == 0xff) // NODES
+ PROCESSNODEMESSAGE(Buffer, PORT);
+
+ break;
+
+ case 0xcc: // TCP
+ case 0xcd: // ARP
+ case 0x08: // NOS FRAGMENTED AX25 TCP/IP
+
+ Q_IP_MSG( Buffer);
+ return;
+ }
+
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ if (PORT->PortUIONLY) // Port is for UI only
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ if (CTLlessPF == SABME)
+ {
+ // Although some say V2.2 requires SABME I don't agree!
+
+ // Reject until we support Mod 128
+
+ L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL);
+ return;
+ }
+
+ if (CTLlessPF == SREJ) // Used to see if other end supports SREJ on 2.0
+ {
+ // Send FRMR if dont support SREJ
+ // Send DM if we do
+
+ if (SUPPORT2point2)
+ L2SENDRESP(PORT, Buffer, ADJBUFFER, DM);
+ else
+ L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL);
+
+ return;
+ }
+
+ if (CTLlessPF == XID)
+ {
+ // Send FRMR if we only support V 2.0
+
+ if (SUPPORT2point2 == FALSE)
+ {
+ L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL);
+ return;
+ }
+ // if Support 2.2 drop through
+ }
+
+ if (CTLlessPF == TEST)
+ {
+ // I can't see amy harm in replying to TEST
+
+ L2SENDRESP(PORT, Buffer, ADJBUFFER, TEST);
+ return;
+ }
+
+
+// if (CTLlessPF != SABM && CTLlessPF != SABME)
+ if (CTLlessPF != SABM && CTLlessPF != XID)
+ {
+ if ((MSGFLAG & CMDBIT) && (CTL & PFBIT)) // Command with P?
+ L2SENDDM(PORT, Buffer, ADJBUFFER);
+ else
+ ReleaseBuffer(Buffer); // Ignore if not
+
+ return;
+ }
+
+ // Exclude and limit tests are done for XID and SABM
+
+ if (NODE == 0 && BBS == 0) // Don't want any calls
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+#ifdef EXCLUDEBITS
+
+ // CHECK ExcludeList
+
+ if (CheckExcludeList(Buffer->ORIGIN) == 0)
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+#endif
+
+ // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT
+
+ if (PORT->PERMITTEDCALLS)
+ {
+ UCHAR * ptr = PORT->PERMITTEDCALLS;
+
+ while (TRUE)
+ {
+ if (memcmp(Buffer->ORIGIN, ptr, 6) == 0) // Ignore SSID
+ break;
+
+ ptr += 7;
+
+ if ((*ptr) == 0) // Not in list
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+ }
+ }
+
+ // IF CALL REQUEST IS FROM A LOCKED NODE WITH QUALITY ZERO, IGNORE IT
+
+ if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE))
+ {
+ // From a known node
+
+ NO_CTEXT = 1;
+
+ if (ROUTE->NEIGHBOUR_FLAG == 1 && ROUTE->NEIGHBOUR_QUAL == 0) // Locked, qual 0
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+ }
+
+ // CHECK PORT CONNECT LIMITS
+
+ if (PORT->USERS)
+ {
+ if (COUNTLINKS(PORT->PORTNUMBER) >= PORT->USERS)
+ {
+ L2SENDDM(PORT, Buffer, ADJBUFFER);
+ return;
+ }
+ }
+
+ // if KISSHF, check if attached. If so, reject. If not, attach.
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ {
+ struct TNCINFO * TNC = PORT->TNC;
+
+ if (TNC->PortRecord->ATTACHEDSESSIONS[0])
+ {
+ L2SENDDM(PORT, Buffer, ADJBUFFER);
+ return;
+ }
+ }
+
+ // OK to accept SABM or XID
+
+ if (CTLlessPF == XID)
+ {
+ ProcessXIDCommand(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG);
+ return;
+ }
+
+ // Not XID, so must be SABM
+
+ L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM
+}
+
+
+VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG)
+{
+ // I think it is fairly safe to accept XID as soon as we
+ // can process SREJ, but only accept Mod 8 and 256 Byte frames
+
+ // I think the only way to run 2.2 Mod 8 is to preceed a
+ // SABM with XID, but others don't seem to agree!
+
+ // Run through XID fields, changing any we don't like,
+ // then return an XID response
+
+ // Decode and process XID
+
+ UCHAR * ptr = &ADJBUFFER->PID;
+ UCHAR * ptr1, * ptr2;
+ UCHAR TEMPDIGI[57];
+ int n;
+
+ // Check Interlock - should we also check exclude etc?. No, checked in L2FORUS
+
+ if (CheckKissInterlock(PORT, TRUE)) // Interlock with ARDOP/VARA etc
+ {
+ L2SENDDM(PORT, Buffer, ADJBUFFER);
+ return;
+ }
+
+ if (*ptr++ == 0x82 && *ptr++ == 0x80)
+ {
+ int Type;
+ int Len;
+ unsigned int value;
+ int xidlen = *(ptr++) << 8;
+ xidlen += *ptr++;
+
+ // XID is set of Type, Len, Value n-tuples
+
+ while (xidlen > 0)
+ {
+ Type = *ptr++;
+ Len = *ptr++;
+
+ value = 0;
+ xidlen -= (Len + 2);
+
+ while (Len--)
+ {
+ value <<=8;
+ value += *ptr++;
+ }
+ switch(Type)
+ {
+ case 2: //Bin fields
+
+ break;
+
+ case 3:
+
+ if ((value & OPMustHave) != OPMustHave)
+ goto BadXID;
+
+ if ((value & OPMod8) == 0)
+ goto BadXID;
+
+ if ((value & OPSREJMult) == 0)
+ goto BadXID;
+
+
+ // Reply Mod 8 SREJMULTI
+
+ value = OPMustHave | OPSREJMult | OPMod8;
+ ptr -=3;
+ *ptr++ = value >> 16;
+ *ptr++ = value >> 8;
+ *ptr++ = value;
+
+
+ break;
+
+ case 6: //RX Size
+
+ break;
+
+ case 8: //RX Window
+
+ break;
+ }
+ }
+
+ // Send back as XID response
+
+ LINK->L2STATE = 1; // XID received
+ LINK->Ver2point2 = TRUE; // Must support 2.2 if sent XID
+ LINK->L2TIME = PORT->PORTT1;
+
+ LINK->LINKPORT = PORT;
+
+ // save calls so we can match up SABM when it comes
+
+ memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7);
+ LINK->LINKCALL[6] &= 0x1e; // Mask SSID
+
+ memcpy(LINK->OURCALL, Buffer->DEST, 7);
+
+ LINK->OURCALL[6] &= 0x1e; // Mask SSID
+
+ memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT
+
+ if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address
+ {
+ // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK
+
+ memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT
+
+ ptr1 = &Buffer->ORIGIN[6]; // End of add
+ ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi
+
+ while((*ptr1 & 1) == 0) // End of address bit
+ {
+ ptr1++;
+ memcpy(ptr2, ptr1, 7);
+ ptr2[6] &= 0x1e; // Mask Repeated and Last bits
+ ptr2 -= 7;
+ ptr1 += 6;
+ }
+
+ // LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY
+
+ n = PORT->PORTMAXDIGIS;
+
+ ptr1 = ptr2 + 7; // First in TEMPDIGIS
+ ptr2 = &LINK->DIGIS[0];
+
+ while (*ptr1)
+ {
+ if (n == 0)
+ {
+ // Too many for us
+
+ CLEAROUTLINK(LINK);
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ memcpy(ptr2, ptr1, 7);
+ ptr1 += 7;
+ ptr2 += 7;
+ n--;
+ }
+ }
+
+ ADJBUFFER->CTL = CTL | PFBIT;
+
+ // Buffer->LENGTH = (UCHAR *)ADJBUFFER - (UCHAR *)Buffer + MSGHDDRLEN + 15; // SET UP BYTE COUNT
+
+ L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS
+
+ // We need to save APPLMASK and ALIASPTR so following SABM connects to application
+
+ LINK->APPLMASK = APPLMASK;
+ LINK->ALIASPTR = ALIASPTR;
+
+ PUT_ON_PORT_Q(PORT, Buffer);
+ return;
+ }
+BadXID:
+ L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL);
+ return;
+}
+
+
+
+int COUNTLINKS(int Port)
+{
+ //COUNT LINKS ON PORT
+
+ int i = MAXLINKS, n = 0;
+ struct _LINKTABLE * LINK = LINKS;
+
+ while (i--)
+ {
+ if (LINK->LINKPORT && LINK->LINKPORT->PORTNUMBER == Port)
+ n++;
+
+ LINK++;
+ }
+
+ return n;
+}
+
+
+VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG)
+{
+ // MESSAGE ON AN ACTIVE LINK
+
+ int CTLlessPF = CTL & ~PFBIT;
+
+ PORT->L2FRAMESFORUS++;
+
+ // ONLY SABM or UI ALLOWED IF NO SESSION
+
+ if (CTLlessPF == 3) // UI
+ {
+ // A UI ADDRESSED TO US - SHOULD ONLY BE FOR IP, or possibly addressed NODES
+
+ switch(ADJBUFFER->PID)
+ {
+ case 0xcf: // Netrom
+
+ if (Buffer->L2DATA[0] == 0xff) // NODES
+ PROCESSNODEMESSAGE(Buffer, PORT);
+
+ break;
+
+ case 0xcc: // TCP
+ case 0xcd: // ARP
+ case 0x08: // NOS FRAGMENTED AX25 TCP/IP
+
+ Q_IP_MSG( Buffer);
+ return;
+ }
+
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ if (CTLlessPF == DISC)
+ {
+ InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END
+ CLEAROUTLINK(LINK);
+ L2SENDUA(PORT, Buffer, ADJBUFFER);
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ DetachKISSHF(PORT);
+
+ return;
+ }
+
+
+ if (LINK->L2STATE == 1)
+ {
+ // XID State. Should be XID response if 2.2 ok or DM/FRMR if not
+
+ if (MSGFLAG & RESP)
+ {
+ if (CTLlessPF == DM || CTLlessPF == FRMR)
+ {
+ // Doesn't support XID - Send SABM
+
+ LINK->L2STATE = 2;
+ LINK->Ver2point2 = FALSE;
+ LINK->L2TIMER = 1; // USe retry to send SABM
+ }
+ else if (CTLlessPF == XID)
+ {
+ // Process response to make sure ok, Send SABM or DISC
+
+ LINK->L2STATE = 2;
+ LINK->Ver2point2 = TRUE;// Must support 2.2 if responded to XID
+ LINK->L2TIMER = 1; // USe retry to send SABM
+ }
+
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ // Command on existing session. Could be due to other end missing
+ // the XID response, so if XID just resend response
+
+ }
+
+ if (CTLlessPF == XID && (MSGFLAG & CMDBIT))
+ {
+ // XID Command on active session. Other end may be restarting. Send Response
+
+ ProcessXIDCommand(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG);
+ return;
+ }
+
+
+ if (CTLlessPF == SABM)
+ {
+ // SABM ON EXISTING SESSION - IF DISCONNECTING, REJECT
+
+ if (LINK->L2STATE == 1) // Sent XID?
+ {
+ APPLMASK = LINK->APPLMASK;
+ ALIASPTR = LINK->ALIASPTR;
+
+ L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM
+ return;
+ }
+
+ if (LINK->L2STATE == 4) // DISCONNECTING?
+ {
+ L2SENDDM(PORT, Buffer, ADJBUFFER);
+ return;
+ }
+
+ // THIS IS A SABM ON AN EXISTING SESSION
+
+ // THERE ARE SEVERAL POSSIBILITIES:
+
+ // 1. RECONNECT COMMAND TO TNC
+ // 2. OTHER END THINKS LINK HAS DIED
+ // 3. RECOVERY FROM FRMR CONDITION
+ // 4. REPEAT OF ORIGINAL SABM COS OTHER END MISSED UA
+
+ // FOR 1-3 IT IS REASONABLE TO FULLY RESET THE CIRCUIT, BUT IN 4
+ // SUCH ACTION WILL LOSE THE INITIAL SIGNON MSG IF CONNECTING TO A
+ // BBS. THE PROBLEM IS TELLING THE DIFFERENCE. I'M GOING TO SET A FLAG
+ // WHEN FIRST INFO RECEIVED - IF SABM REPEATED BEFORE THIS, I'LL ASSUME
+ // CONDITION 4, AND JUST RESEND THE UA
+
+
+ if (LINK->SESSACTIVE == 0) // RESET OF ACTIVE CIRCUIT?
+ {
+ L2SENDUA(PORT, Buffer, ADJBUFFER); // No, so repeat UA
+ return;
+ }
+
+ InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END
+ LINK->CIRCUITPOINTER = 0;
+
+ L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM
+ return;
+ }
+
+ L2_PROCESS(LINK, PORT, Buffer, CTL, MSGFLAG);
+}
+
+
+VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR MSGFLAG)
+{
+ // SET UP NEW SESSION (OR RESET EXISTING ONE)
+
+ TRANSPORTENTRY * Session;
+ int CONERROR;
+
+ char toCall[12], fromCall[12];
+
+
+ if (LINK == 0) // NO LINK ENTRIES - SEND DM RESPONSE
+ {
+ L2SENDDM(PORT, Buffer, ADJBUFFER);
+ return;
+ }
+
+ if (CheckKissInterlock(PORT, TRUE)) // Interlock with ARDOP/VARA etc
+ {
+ L2SENDDM(PORT, Buffer, ADJBUFFER);
+ return;
+ }
+
+ SETUPNEWL2SESSION(LINK, PORT, Buffer, MSGFLAG);
+
+ if (LINK->L2STATE != 5) // Setup OK?
+ {
+ L2SENDDM(PORT, Buffer, ADJBUFFER); // Failed
+ return;
+ }
+
+ // See if need to Interlock non-sharable modes, eg ARDOP and VARA
+
+ seeifInterlockneeded(PORT);
+
+ toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0;
+ fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0;
+
+
+ // IF CONNECT TO APPL ADDRESS, SET UP APPL SESSION
+
+ if (APPLMASK == 0)
+ {
+ // Not ATTACH TO APPL
+
+ // Send CTEXT if connect to NODE/Port Alias, or NODE/Port Call, and FULL_CTEXT set
+ // Dont sent to known NODEs, or appl connects
+
+ struct DATAMESSAGE * Msg;
+ int Totallen = 0;
+ int Paclen= PORT->PORTPACLEN;
+ UCHAR * ptr;
+
+ if (LogAllConnects)
+ {
+ char toCall[12], fromCall[12];
+ toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0;
+ fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0;
+ WriteConnectLog(fromCall, toCall, "AX.25");
+ }
+
+ hookL2SessionAccepted(PORT->PORTNUMBER, fromCall, toCall, LINK);
+
+ L2SENDUA(PORT, Buffer, ADJBUFFER);
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ AttachKISSHF(PORT, Buffer);
+
+ if (NO_CTEXT == 1)
+ return;
+
+ if (FULL_CTEXT == 0 && !ALIASMSG) // Any connect, or call to alias
+ return;
+
+ // if Port CTEXT defined, use it
+
+ if (PORT->CTEXT)
+ {
+ Totallen = strlen(PORT->CTEXT);
+ ptr = PORT->CTEXT;
+ }
+ else if (CTEXTLEN)
+ {
+ Totallen = CTEXTLEN;
+ ptr = CTEXTMSG;
+ }
+ else
+ return;
+
+ if (Paclen == 0)
+ Paclen = PACLEN;
+
+ while(Totallen)
+ {
+ Msg = GetBuff();
+
+ if (Msg == NULL)
+ break; // No Buffers
+
+ Msg->PID = 0xf0;
+
+ if (Paclen > Totallen)
+ Paclen = Totallen;
+
+ memcpy(Msg->L2DATA, ptr, Paclen);
+ Msg->LENGTH = Paclen + MSGHDDRLEN + 1;
+
+ C_Q_ADD(&LINK->TX_Q, Msg);
+
+ ptr += Paclen;
+ Totallen -= Paclen;
+ }
+ return;
+ }
+
+
+ // Connnect to APPL
+
+ if (LINK->LINKTYPE != 1)
+ {
+ L2SENDUA(PORT, Buffer, ADJBUFFER); // RESET OF DOWN/CROSSLINK
+ return;
+ }
+
+ if (LINK->CIRCUITPOINTER)
+ {
+ L2SENDUA(PORT, Buffer, ADJBUFFER); // ALREADY SET UP - MUST BE REPEAT OF SABM OR LINK RESET
+ return;
+ }
+
+ // IF RUNNING ONLY BBS (NODE=0), THIS MAY BE EITHER A USER OR NODE
+ // TRYING TO SET UP A L4 CIRCUIT - WE DONT WANT TO ATTACH A NODE TO
+ // THE BBS!
+
+ if (NODE == 0)
+ {
+ // NOW THINGS GET DIFICULT - WE MUST EITHER WAIT TO SEE IF A PID CF MSG
+ // ARRIVES, OR ASSUME ALL NODES ARE IN NEIGHBOURS - I'LL TRY THE LATTER
+ // AND SEE HOW IT GOES. tHIS MEANS THAT YOU MUST DEFINE ALL ROUTES
+ // IN CONFIG FILE
+
+ struct ROUTE * ROUTE;
+
+ if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE))
+ {
+ // It's a node
+
+ L2SENDUA(PORT, Buffer, ADJBUFFER); // ALREADY SET UP - MUST BE REPEAT OF SABM OR LINK RESET
+ return;
+ }
+ }
+
+
+ Session = SetupSessionForL2(LINK); // CREATE INCOMING L4 SESSION
+
+ if (Session == NULL)
+ {
+ CLEAROUTLINK(LINK);
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ DetachKISSHF(PORT);
+ L2SENDDM(PORT, Buffer, ADJBUFFER);
+
+ return;
+ }
+
+ // NOW TRY A BBS CONNECT
+ // IF APPL CONNECT, SEE IF APPL HAS AN ALIAS
+
+ if (ALIASPTR[0] > ' ')
+ {
+ struct DATAMESSAGE * Msg;
+
+ // ACCEPT THE CONNECT, THEN INVOKE THE ALIAS
+
+ L2SENDUA(PORT, Buffer, ADJBUFFER);
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ {
+ struct DATAMESSAGE * Msg;
+ int Totallen = 0;
+ int Paclen= PORT->PORTPACLEN;
+ UCHAR * ptr;
+
+ AttachKISSHF(PORT, Buffer);
+
+ // if Port CTEXT defined, use it
+
+ if (PORT->CTEXT)
+ {
+ Totallen = strlen(PORT->CTEXT);
+ ptr = PORT->CTEXT;
+ }
+ else if (HFCTEXTLEN)
+ {
+ Totallen = HFCTEXTLEN;
+ ptr = HFCTEXT;
+ }
+
+ if (Paclen == 0)
+ Paclen = PACLEN;
+
+ while(Totallen)
+ {
+ Msg = GetBuff();
+
+ if (Msg == NULL)
+ break; // No Buffers
+
+ Msg->PID = 0xf0;
+
+ if (Paclen > Totallen)
+ Paclen = Totallen;
+
+ memcpy(Msg->L2DATA, ptr, Paclen);
+ Msg->LENGTH = Paclen + MSGHDDRLEN + 1;
+
+ C_Q_ADD(&LINK->TX_Q, Msg);
+
+ ptr += Paclen;
+ Totallen -= Paclen;
+ }
+
+ }
+
+ if (LogAllConnects)
+ {
+ char toCall[12], fromCall[12];
+ toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0;
+ fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0;
+ WriteConnectLog(fromCall, toCall, "AX.25");
+ }
+
+ Msg = GetBuff();
+
+ if (Msg)
+ {
+ Msg->PID = 0xf0;
+
+ memcpy(Msg->L2DATA, ALIASPTR, 12);
+ Msg->L2DATA[12] = 13;
+
+ Msg->LENGTH = MSGHDDRLEN + 12 + 2; // 2 for PID and CR
+
+ C_Q_ADD(&LINK->RX_Q, Msg);
+ }
+
+ return;
+ }
+
+ if (cATTACHTOBBS(Session, APPLMASK, PORT->PORTPACLEN, &CONERROR) == 0)
+ {
+ // NO BBS AVAILABLE
+
+ CLEARSESSIONENTRY(Session);
+ CLEAROUTLINK(LINK);
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ DetachKISSHF(PORT);
+
+ L2SENDDM(PORT, Buffer, ADJBUFFER);
+
+ return;
+ }
+
+ if (LogAllConnects)
+ {
+ char toCall[12], fromCall[12];
+ toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0;
+ fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0;
+ WriteConnectLog(fromCall, toCall, "AX.25");
+ }
+
+ L2SENDUA(PORT, Buffer, ADJBUFFER);
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ {
+ struct DATAMESSAGE * Msg;
+ int Totallen = 0;
+ int Paclen= PORT->PORTPACLEN;
+ UCHAR * ptr;
+
+ AttachKISSHF(PORT, Buffer);
+
+ // if Port CTEXT defined, use it
+
+ if (PORT->CTEXT)
+ {
+ Totallen = strlen(PORT->CTEXT);
+ ptr = PORT->CTEXT;
+ }
+ else if (HFCTEXTLEN)
+ {
+ Totallen = HFCTEXTLEN;
+ ptr = HFCTEXT;
+ }
+ else
+ return;
+
+ if (Paclen == 0)
+ Paclen = PACLEN;
+
+ while(Totallen)
+ {
+ Msg = GetBuff();
+
+ if (Msg == NULL)
+ break; // No Buffers
+
+ Msg->PID = 0xf0;
+
+ if (Paclen > Totallen)
+ Paclen = Totallen;
+
+ memcpy(Msg->L2DATA, ptr, Paclen);
+ Msg->LENGTH = Paclen + MSGHDDRLEN + 1;
+
+ C_Q_ADD(&LINK->TX_Q, Msg);
+
+ ptr += Paclen;
+ Totallen -= Paclen;
+ }
+ return;
+ }
+}
+
+VOID SETUPNEWL2SESSION(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR MSGFLAG)
+{
+ // COPY ADDRESS INFO TO LINK TABLE
+
+ UCHAR * ptr1, * ptr2;
+ UCHAR TEMPDIGI[57];
+ int n;
+
+ memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7);
+ LINK->LINKCALL[6] &= 0x1e; // Mask SSID
+
+ memcpy(LINK->OURCALL, Buffer->DEST, 7);
+ LINK->OURCALL[6] &= 0x1e; // Mask SSID
+
+ memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT
+
+ LINK->L2TIME = PORT->PORTT1; // Set tomeoiut for no digis
+
+ if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address
+ {
+ // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK
+
+ memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT
+
+ ptr1 = &Buffer->ORIGIN[6]; // End of add
+ ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi
+
+ while((*ptr1 & 1) == 0) // End of address bit
+ {
+ ptr1++;
+ memcpy(ptr2, ptr1, 7);
+ ptr2[6] &= 0x1e; // Mask Repeated and Last bits
+ ptr2 -= 7;
+ ptr1 += 6;
+ }
+
+ // LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY
+
+ n = PORT->PORTMAXDIGIS;
+
+ ptr1 = ptr2 + 7; // First in TEMPDIGIS
+ ptr2 = &LINK->DIGIS[0];
+
+ while (*ptr1)
+ {
+ if (n == 0)
+ {
+ // Too many for us
+
+ CLEAROUTLINK(LINK);
+ return;
+ }
+
+ memcpy(ptr2, ptr1, 7);
+ ptr1 += 7;
+ ptr2 += 7;
+ n--;
+
+ LINK->L2TIME += PORT->PORTT1; // Adjust timeout for digis
+ }
+ }
+
+ // THIS MAY BE RESETTING A LINK - BEWARE OF CONVERTING A CROSSLINK TO
+ // AN UPLINK AND CONFUSING EVERYTHING
+
+ LINK->LINKPORT = PORT;
+
+ if (LINK->LINKTYPE == 0)
+ {
+ if (ISNETROMMSG && NODE == 0) // Only allow crosslink if node = 0
+ LINK->LINKTYPE = 3; // Crosslink
+ else
+ LINK->LINKTYPE = 1; // Uplink
+ }
+ LINK->L2TIMER = 0; // CANCEL TIMER
+
+ LINK->L2SLOTIM = T3; // SET FRAME SENT RECENTLY
+
+ LINK->LINKWINDOW = PORT->PORTWINDOW;
+
+ RESET2(LINK); // RESET ALL FLAGS
+
+ LINK->L2STATE = 5;
+
+ // IF VERSION 1 MSG, SET FLAG
+
+ if (MSGFLAG & VER1)
+ LINK->VER1FLAG |= 1;
+
+}
+
+VOID L2SENDUA(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER)
+{
+ L2SENDRESP(PORT, Buffer, ADJBUFFER, UA);
+}
+
+VOID L2SENDDM(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER)
+{
+ if (CheckExcludeList(Buffer->ORIGIN) == 0) // if in exclude, don't send DM
+ {
+ ReleaseBuffer(Buffer); // not sure that this is the right place for releasing?
+ return;
+ }
+
+ L2SENDRESP(PORT, Buffer, ADJBUFFER, DM);
+}
+
+VOID L2SENDRESP(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL)
+{
+ // QUEUE RESPONSE TO PORT CONTROL - MAY NOT HAVE A LINK ENTRY
+
+ // SET APPROPRIATE P/F BIT
+
+ ADJBUFFER->CTL = CTL | PFBIT;
+
+ Buffer->LENGTH = (int)((UCHAR *)ADJBUFFER - (UCHAR *)Buffer) + MSGHDDRLEN + 15; // SET UP BYTE COUNT
+
+ L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS
+
+ PUT_ON_PORT_Q(PORT, Buffer);
+
+ return;
+}
+
+
+VOID L2SENDINVALIDCTRL(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL)
+{
+ // Send FRMR Invalid Control field
+
+ // QUEUE RESPONSE TO PORT CONTROL - MAY NOT HAVE A LINK ENTRY
+
+ // SET APPROPRIATE P/F BIT
+
+ UCHAR * ptr;
+
+ ADJBUFFER->CTL = FRMR | PFBIT;
+
+ ptr = &ADJBUFFER->PID;
+
+ *(ptr++) = CTL; // MOVE REJECT C-BYTE
+ *(ptr++) = 0;
+ *(ptr++) = SDINVC; // MOVE REJECT FLAGS
+
+ Buffer->LENGTH = (int)((UCHAR *)ADJBUFFER - (UCHAR *)Buffer) + MSGHDDRLEN + 18; // SET UP BYTE COUNT
+
+ L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS
+
+ PUT_ON_PORT_Q(PORT, Buffer);
+
+ return;
+}
+
+VOID L2SWAPADDRESSES(MESSAGE * Buffer)
+{
+ // EXCHANGE ORIGIN AND DEST, AND REVERSE DIGIS (IF PRESENT)
+
+ char TEMPFIELD[7];
+ UCHAR * ptr1, * ptr2;
+ UCHAR TEMPDIGI[57];
+
+ memcpy(TEMPFIELD, Buffer->ORIGIN, 7);
+ memcpy(Buffer->ORIGIN, Buffer->DEST, 7);
+ memcpy(Buffer->DEST, TEMPFIELD, 7);
+
+ Buffer->ORIGIN[6] &= 0x1e; // Mask SSID
+ Buffer->ORIGIN[6] |= 0xe0; // Reserved and Response
+
+ Buffer->DEST[6] &= 0x1e; // Mask SSID
+ Buffer->DEST[6] |= 0x60; // Reserved
+
+ if ((TEMPFIELD[6] & 1) == 0)
+ {
+ // THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK
+
+ memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT
+
+ ptr1 = &Buffer->ORIGIN[6]; // End of add
+ ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi
+
+ while((*ptr1 & 1) == 0) // End of address bit
+ {
+ ptr1++;
+ memcpy(ptr2, ptr1, 7);
+ ptr2[6] &= 0x1e; // Mask Repeated and Last bits
+ ptr2 -= 7;
+ ptr1 += 6;
+ }
+
+ // LIST OF DIGI CALLS COMPLETE - copy back
+
+ ptr1 = ptr2 + 7; // First in TEMPDIGIS
+ ptr2 = &Buffer->CTL;
+
+ while (*ptr1)
+ {
+ memcpy(ptr2, ptr1, 7);
+ ptr1 += 7;
+ ptr2 += 7;
+ }
+
+ *(ptr2 - 1) |= 1; // End of addresses
+ }
+ else
+ {
+ Buffer->ORIGIN[6] |= 1; // End of address
+ }
+}
+
+BOOL InternalL2SETUPCROSSLINK(PROUTE ROUTE, int Retries)
+{
+ // ROUTE POINTS TO A NEIGHBOUR - FIND AN L2 SESSION FROM US TO IT, OR INITIATE A NEW ONE
+
+ struct _LINKTABLE * LINK;
+ struct PORTCONTROL * PORT;
+ int FRACK;
+
+ if (FindLink(ROUTE->NEIGHBOUR_CALL, NETROMCALL, ROUTE->NEIGHBOUR_PORT, &LINK))
+ {
+ // SESSION ALREADY EXISTS
+
+ LINK->LINKTYPE = 3; // MAKE SURE IT KNOWS ITS A CROSSLINK
+ ROUTE->NEIGHBOUR_LINK = LINK;
+ LINK->NEIGHBOUR = ROUTE;
+
+ return TRUE;
+ }
+
+ // SET UP NEW SESSION (OR RESET EXISTING ONE)
+
+ if (LINK == NULL)
+ return FALSE; // No free links
+
+
+ ROUTE->NEIGHBOUR_LINK = LINK;
+ LINK->NEIGHBOUR = ROUTE;
+
+ LINK->LINKPORT = PORT = GetPortTableEntryFromPortNum(ROUTE->NEIGHBOUR_PORT);
+
+ if (PORT == NULL)
+ return FALSE; // maybe port has been deleted
+
+ // IF ROUTE HAS A FRACK, SET IT
+
+ if (ROUTE->NBOUR_FRACK)
+ FRACK = ROUTE->NBOUR_FRACK;
+ else
+ FRACK = PORT->PORTT1;
+
+ LINK->L2TIME = FRACK; // SET TIMER VALUE
+
+ // IF ROUTE HAS A WINDOW, SET IT
+
+ if (ROUTE->NBOUR_MAXFRAME)
+ LINK->LINKWINDOW = ROUTE->NBOUR_MAXFRAME;
+ else
+ LINK->LINKWINDOW = PORT->PORTWINDOW;
+
+// if (SUPPORT2point2)
+// LINK->L2STATE = 1; // Send XID
+// else
+ LINK->L2STATE = 2;
+
+ memcpy(LINK->LINKCALL, ROUTE->NEIGHBOUR_CALL, 7);
+ memcpy(LINK->OURCALL, NETROMCALL, 7);
+
+ if (ROUTE->NEIGHBOUR_DIGI1[0])
+ {
+ memcpy(LINK->DIGIS, ROUTE->NEIGHBOUR_DIGI1, 7);
+ LINK->L2TIME += FRACK;
+ }
+
+ if (ROUTE->NEIGHBOUR_DIGI2[0])
+ {
+ memcpy(&LINK->DIGIS[7], ROUTE->NEIGHBOUR_DIGI1, 7);
+ LINK->L2TIME += FRACK;
+ }
+
+ LINK->LINKTYPE = 3; // CROSSLINK
+
+ if (Retries)
+ LINK->L2RETRIES = PORT->PORTN2 - Retries;
+
+ if (LINK->L2STATE == 1)
+ L2SENDXID(LINK);
+ else
+ SENDSABM(LINK);
+
+ return TRUE;
+}
+
+
+
+BOOL L2SETUPCROSSLINKEX(PROUTE ROUTE, int Retries)
+{
+ // Allows caller to specify number of times SABM should be sent
+
+ return InternalL2SETUPCROSSLINK(ROUTE, Retries);
+}
+
+BOOL L2SETUPCROSSLINK(PROUTE ROUTE)
+{
+ return InternalL2SETUPCROSSLINK(ROUTE, 0);
+}
+
+VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG)
+{
+ // PROCESS LEVEL 2 PROTOCOL STUFF
+
+ // SEE IF COMMAND OR RESPONSE
+
+ if ((MSGFLAG & CMDBIT) == 0)
+ {
+
+ // RESPONSE OR VERSION 1
+
+ // IF RETRYING, MUST ONLY ACCEPT RESPONSES WITH F SET (UNLESS RUNNING V1)
+
+ if ((CTL & PFBIT) || LINK->VER1FLAG == 1)
+ {
+ // F SET or V1 - CAN CANCEL TIMER
+
+ LINK->L2TIMER = 0; // CANCEL LINK TIMER
+ }
+ }
+
+ if (LINK->L2STATE == 3)
+ {
+
+ // FRMR STATE - IF C(P) SEND FRMR, ELSE IGNORE
+
+ if (CTL & PFBIT)
+ {
+ if (CTL == (FRMR | PFBIT)) // if both ends in FRMR state, reset link
+ {
+ RESET2(LINK);
+
+ LINK->L2STATE = 2; // INITIALISING
+ LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE
+ LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM
+
+ L2SENDCOMMAND(LINK, SABM | PFBIT);
+ }
+ }
+
+ if (MSGFLAG & CMDBIT)
+ {
+ // SEND FRMR AGAIN
+
+ SENDFRMR(LINK);
+ }
+
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ if (LINK->L2STATE >= 5)
+ {
+ // LINK IN STATE 5 OR ABOVE - LINK RUNNING
+
+ if ((CTL & 1) == 0) // I frame
+ {
+ SDIFRM(LINK, PORT, Buffer, CTL, MSGFLAG); // consumes buffer
+ return;
+ }
+
+ if ((CTL & 2)) // U frame
+ {
+ SDUFRM(LINK, PORT, Buffer, CTL); //consumes buffer
+ return;
+ }
+
+ // ELSE SUPERVISORY, MASK OFF N(R) AND P-BIT
+
+ switch (CTL & 0x0f)
+ {
+ // is there any harm in accepting SREJ even if we don't
+ // otherwise support 2.2?
+
+ case REJ:
+ case SREJ:
+
+ PORT->L2REJCOUNT++;
+
+ case RR:
+ case RNR:
+
+ SFRAME(LINK, PORT, CTL, MSGFLAG);
+ break;
+
+ default:
+
+ // UNRECOGNISABLE COMMAND
+
+ LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE
+ LINK->SDREJF |= SDINVC; // SET INVALID COMMAND REJECT
+ SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION
+ }
+
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ // NORMAL DISCONNECT MODE
+
+ // COULD BE UA, DM - SABM AND DISC HANDLED ABOVE
+
+ switch (CTL & ~PFBIT)
+ {
+ case UA:
+
+ // UA RECEIVED
+
+ if (LINK->L2STATE == 2)
+ {
+ // RESPONSE TO SABM - SET LINK UP
+
+ RESET2X(LINK); // LEAVE QUEUED STUFF
+
+ LINK->L2STATE = 5;
+ LINK->L2TIMER = 0; // CANCEL TIMER
+ LINK->L2RETRIES = 0;
+ LINK->L2SLOTIM, T3; // SET FRAME SENT RECENTLY
+
+ // IF VERSION 1 MSG, SET FLAG
+
+ if (MSGFLAG & VER1)
+ LINK->VER1FLAG |= 1;
+
+ // TELL PARTNER CONNECTION IS ESTABLISHED
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ KISSHFConnected(PORT, LINK);
+
+ SENDCONNECTREPLY(LINK);
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ if (LINK->L2STATE == 4) // DISCONNECTING?
+ {
+ InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END
+ CLEAROUTLINK(LINK);
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ DetachKISSHF(PORT);
+ }
+
+ // UA, BUT NOT IN STATE 2 OR 4 - IGNORE
+
+ ReleaseBuffer(Buffer);
+ return;
+
+ case DM:
+
+ // DM RESPONSE - IF TO SABM, SEND BUSY MSG
+
+ if (LINK->L2STATE == 2)
+ {
+ CONNECTREFUSED(LINK); // SEND MESSAGE IF DOWNLINK
+ return;
+ }
+
+ // DM RESP TO DISC RECEIVED - OTHER END HAS LOST SESSION
+
+ // CLEAR OUT TABLE ENTRY - IF INTERNAL TNC, SHOULD SEND *** DISCONNECTED
+
+ InformPartner(LINK, LINKLOST); // SEND DISC TO OTHER END
+ CLEAROUTLINK(LINK);
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ DetachKISSHF(PORT);
+
+ ReleaseBuffer(Buffer);
+ return;
+
+ case FRMR:
+
+ // FRAME REJECT RECEIVED - LOG IT AND RESET LINK
+
+ RESET2(LINK);
+
+ LINK->L2STATE = 2; // INITIALISING
+ LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE
+ LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM
+
+ PORT->L2FRMRRX++;
+
+ L2SENDCOMMAND(LINK, SABM | PFBIT);
+ return;
+
+ default:
+
+ // ANY OTHER - IGNORE
+
+ ReleaseBuffer(Buffer);
+ }
+}
+
+VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL)
+{
+ // PROCESS AN UNSEQUENCED COMMAND (IN LINK UP STATES)
+
+ switch (CTL & ~PFBIT)
+ {
+ case UA:
+
+ // DISCARD - PROBABLY REPEAT OF ACK OF SABM
+
+ break;
+
+ case FRMR:
+
+ // FRAME REJECT RECEIVED - LOG IT AND RESET LINK
+
+ RESET2(LINK);
+
+ LINK->L2STATE = 2; // INITIALISING
+ LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE
+ LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM
+
+ PORT->L2FRMRRX++;
+
+ L2SENDCOMMAND(LINK, SABM | PFBIT);
+ break;
+
+ case DM:
+
+ // DM RESPONSE - SESSION MUST HAVE GONE
+
+ // SEE IF CROSSLINK ACTIVE
+
+ InformPartner(LINK, LINKLOST); // SEND DISC TO OTHER END
+ CLEAROUTLINK(LINK);
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ DetachKISSHF(PORT);
+
+ break;
+
+ default:
+
+ // UNDEFINED COMMAND
+
+ LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE
+ LINK->SDREJF |= SDINVC;
+ SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION
+
+ }
+
+ ReleaseBuffer(Buffer);
+}
+
+
+VOID SFRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, UCHAR CTL, UCHAR MSGFLAG)
+{
+ // CHECK COUNTS, AND IF RNR INDICATE _BUFFER SHORTAGE AT OTHER END
+
+ if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET?
+ {
+ SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION
+ return;
+ }
+
+ SDNRCHK(LINK, CTL); // CHECK RECEIVED N(R)
+
+ if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET NOW?
+ {
+ SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION
+ return;
+ }
+
+ if ((CTL & 0xf) == SREJ)
+ {
+ // Probably safer to handle SREJ completely separately
+
+ // Can we get SREJ Command with P??(Yes)
+
+ // Can we just resend missing frame ?? (Think so!)
+
+ // We support MultiSREJ (can gave additional missing frame
+ // numbers in the Info field
+
+ // I don't see the point of Multi unless we wait fot an F bit,
+ // bur maybe not safe to assume others do the same
+
+ // So if I get SREJ(F) I can send missing frame(s)
+
+ if (MSGFLAG & RESP)
+ {
+ // SREJ Response
+
+ if (CTL & PFBIT)
+ {
+ // SREJ(F). Send Frames()
+
+ UCHAR NS = (CTL >> 5) & 7; // Frame to resend
+
+ struct PORTCONTROL * PORT;
+ UCHAR * ptr1, * ptr2;
+ UCHAR CTL;
+ int count;
+ MESSAGE * Msg;
+ MESSAGE * Buffer;
+
+ Msg = LINK->FRAMES[NS]; // is frame available?
+
+ if (Msg == NULL)
+ return; // Wot!!
+
+ // send the frame
+
+ // GET BUFFER FOR COPY OF MESSAGE - HAVE TO KEEP ORIGINAL FOR RETRIES
+
+ Buffer = GetBuff();
+
+ if (Buffer == NULL)
+ return;
+
+ ptr2 = SETUPADDRESSES(LINK, Buffer); // copy addresses
+
+ // ptr2 NOW POINTS TO COMMAND BYTE
+
+ // GOING TO SEND I FRAME - WILL ACK ANY RECEIVED FRAMES
+
+ LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED
+ LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY
+ LINK->KILLTIMER = 0; // RESET IDLE CIRCUIT TIMER
+
+ CTL = LINK->LINKNR << 5; // GET CURRENT N(R), SHIFT IT TO TOP 3 BITS
+ CTL |= NS << 1; // BITS 1-3 OF CONTROL BYTE
+
+ // SET P BIT IF NO MORE TO SEND (only more if Multi SREJ)
+
+ if (LINK->VER1FLAG == 0) // NO POLL BIT IF V1
+ {
+ CTL |= PFBIT;
+ LINK->L2FLAGS |= POLLSENT;
+ LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER
+
+ // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND (or ACK if ACKMODE)
+
+ Buffer->Linkptr = LINK;
+ }
+
+ *(ptr2++) = CTL; // TO DATA (STARTING WITH PID)
+
+ count = Msg->LENGTH - MSGHDDRLEN;
+
+ if (count > 0) // SHOULD ALWAYS BE A PID, BUT BETTER SAFE THAN SORRY
+ {
+ ptr1 = (UCHAR *)Msg;
+ ptr1 += MSGHDDRLEN;
+ memcpy(ptr2, ptr1, count);
+ }
+
+ Buffer->DEST[6] |= 0x80; // SET COMMAND
+
+ Buffer->LENGTH = (int)(ptr2 - (UCHAR *)Buffer) + count; // SET NEW LENGTH
+
+ LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER
+
+ PORT = LINK->LINKPORT;
+
+ if (PORT)
+ {
+ Buffer->PORT = PORT->PORTNUMBER;
+ PUT_ON_PORT_Q(PORT, Buffer);
+ }
+ else
+ {
+ Buffer->Linkptr = 0;
+ ReleaseBuffer(Buffer);
+ }
+ }
+ }
+
+ return;
+ }
+
+ // VALID RR/RNR RECEIVED
+
+ LINK->L2FLAGS &= ~RNRSET; //CLEAR RNR
+
+ if ((CTL & 0xf) == RNR)
+ LINK->L2FLAGS |= RNRSET; //Set RNR
+
+ if (MSGFLAG & CMDBIT)
+ {
+ // ALWAYS REPLY TO RR/RNR/REJ COMMAND (even if no P bit ??)
+
+ // FIRST PROCESS RESEQ QUEUE
+
+ //; CALL PROCESS_RESEQ
+
+ // IGNORE IF AN 'F' HAS BEEN SENT RECENTLY
+
+ if (LINK->LAST_F_TIME + 15 > REALTIMETICKS)
+ return; // DISCARD
+
+ CTL = RR_OR_RNR(LINK);
+
+ CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS
+ CTL |= PFBIT;
+
+ L2SENDRESPONSE(LINK, CTL);
+
+ LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY
+
+ LINK->L2ACKREQ = 0; // CANCEL DELAYED ACKL2
+
+ // SAVE TIME IF 'F' SENT'
+
+ LINK->LAST_F_TIME = REALTIMETICKS;
+
+ return;
+ }
+
+ // Response
+
+ if ((CTL & PFBIT) == 0 && LINK->VER1FLAG == 0)
+ {
+ // RESPONSE WITHOUT P/F DONT RESET N(S) (UNLESS V1)
+
+ return;
+
+ }
+
+ // RESPONSE WITH P/F - MUST BE REPLY TO POLL FOLLOWING TIMEOUT OR I(P)
+
+ // THERE IS A PROBLEM WITH REPEATED RR(F), SAY CAUSED BY DELAY AT L1
+
+ // AS FAR AS I CAN SEE, WE SHOULD ONLY RESET N(S) IF AN RR(F) FOLLOWS
+ // AN RR(P) AFTER A TIMEOUT - AN RR(F) FOLLOWING AN I(P) CANT POSSIBLY
+ // INDICATE A LOST FRAME. ON THE OTHER HAND, A REJ(F) MUST INDICATE
+ // A LOST FRAME. So dont reset NS if not retrying, unless REJ
+
+
+ // someone (probably WLE KISS Driver) is sending REJ followed by RR(F)
+ // after lost frame and i(p)
+
+/*
+1:Fm W4DHW-10 To W4DHW [17:08:03R] [+++]
+úJƒÑZKÀ)x@DÖBÉrNôÝ4XÔ;i‹#CäM³,ïнҼüÕrÞùOË N¿XæâïÀÄ5Ð(È|©¸ì#íÿÈUþïÒcYÞÍl—çûž)Àú璘oÑȼö>©Ï9¨*ÎG²£ëðû(6À5C‹!áL±Ÿîßì÷³ÙQð»pƒËIH”Š;ØÚi¯Ò>â9p¶B¬õ<ÌcŠEPž«<ŸÊ{0aŽ(’YÕ–´M¢†—N£+<ÇIÐ[–áÛPw–[^]6ƒ2\ù¿9äÆov{‹¥Å¸mm [17:08:03T]
+1:Fm W4DHW To W4DHW-10 [17:08:03T]
+1:Fm W4DHW To W4DHW-10 [17:08:03T]
+
+ is there a problem with restting on RR(F) following I(P)?
+
+ I think the problem is restting NS twice if you get delayed responses to
+ I or RR (P). So lets try only resetting NS once for each P sent
+
+*/
+// if ((CTL & 0xf) == REJ || LINK->L2RETRIES)
+ if ((LINK->L2FLAGS & POLLSENT))
+ {
+ RESETNS(LINK, (CTL >> 5) & 7); // RESET N(S) AND COUNT RETRIED FRAMES
+
+ LINK->L2RETRIES = 0;
+ LINK->L2TIMER = 0; // WILL RESTART TIMER WHEN RETRY SENT
+ }
+
+ LINK->L2FLAGS &= ~POLLSENT; // CLEAR I(P) or RR(P) SET
+
+ if ((CTL & 0xf) == RNR)
+ {
+ // Dont Clear timer on receipt of RNR(F), spec says should poll for clearing of busy,
+ // and loss of subsequent RR will cause hang. Perhaps should set slightly longer time??
+ // Timer may have been cleared earlier, so restart it
+
+ LINK->L2TIMER = LINK->L2TIME;
+ }
+}
+
+//*** PROCESS AN INFORMATION FRAME
+
+VOID SDIFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG)
+{
+ int NS;
+
+ if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET?
+ {
+ SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ SDNRCHK(LINK, CTL); // CHECK RECEIVED N(R)
+
+ if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET NOW?
+ {
+ SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ LINK->SESSACTIVE = 1; // SESSION IS DEFINITELY SET UP
+
+ NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S)
+
+ // IPOLL (sending an I(P) frame following timeout instead of RR(P))
+ // is a problem. We need to send REJ(F), but shouldn't add to collector.
+ // We also need to handle repeated I(P), so shouldn't set REJSENT in
+ // this state.
+
+ if ((((NS + 1) & 7) == LINK->LINKNR) && (CTL & PFBIT))
+ {
+ // Previous Frame and P set - Assume IPOLL
+
+ PORT->L2OUTOFSEQ++;
+ LINK->L2STATE = 6;
+
+ LINK->L2ACKREQ = 0; // CANCEL RR NEEDED
+
+ // We need to protect against sending multiple REJ(F) if channel
+ // delays mean we get two I(P) close together (how close is close ??)
+ // SM has default IPOLL limit of 30 bytes or about a second at 300
+ // ACKMODE should avoid this anyway, and resptime of under 3 secs
+ // is unlikely so say 2.5 secs ??
+
+ if (LINK->LAST_F_TIME + 25 > REALTIMETICKS)
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ SEND_RR_RESP(LINK, PFBIT);
+ LINK->LAST_F_TIME = REALTIMETICKS;
+
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+CheckNSLoop:
+
+ if (NS != LINK->LINKNR) // EQUAL TO OUR N(R)?
+ {
+ // There is a frame missing.
+ // if we have just sent a REJ we have at least one out
+ // of sequence frame in RXFRAMES
+
+ // so if we have frame LINK->LINKNR we can process it
+ // and remove it from RXFRAMES. If we are then back
+ // in sequence we just carry on.
+
+ if (LINK->RXFRAMES[LINK->LINKNR])
+ {
+ // We have the first missing frame. Process it.
+
+ MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]);
+
+ Debugprintf("L2 process saved Frame %d", LINK->LINKNR);
+ PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer
+
+ // NR has been updated.
+
+ goto CheckNSLoop; // See if OK or we have another saved frame
+ }
+
+ // BAD FRAME, SEND REJ (AFTER RESPTIME - OR WE MAY SEND LOTS!)
+
+ // ALSO SAVE THE FRAME - NEXT TIME WE MAY GET A DIFFERENT SUBSET
+ // AND SOON WE WILL HANDLE SREJ
+
+ PORT->L2OUTOFSEQ++;
+
+ LINK->L2STATE = 6;
+
+ // IF RUNNING VER1, AND OTHER END MISSES THIS REJ, LINK WILL FAIL
+ // SO TIME OUT REJ SENT STATE (MUST KEEP IT FOR A WHILE TO AVOID
+ // 'MULTIPLE REJ' PROBLEM)
+
+ if (LINK->VER1FLAG == 1)
+ LINK->REJTIMER = TENSECS;
+
+ // SET ACK REQUIRED TIMER - REJ WILL BE SENT WHEN IT EXPIRES
+
+ // if configured RESPTIME is longer than 3 secs use it (may be longer on HF)
+
+ if (PORT->PORTT2 > THREESECS)
+ LINK->L2ACKREQ = PORT->PORTT2;
+ else
+ LINK->L2ACKREQ = THREESECS; // EXTRA LONG RESPTIME, AS SENDING TOO MANY REJ'S IS SERIOUS
+
+ if (LINK->RXFRAMES[NS])
+ {
+ // Already have a copy, so discard old and keep this
+
+ Debugprintf ("Frame %d out of seq but already have copy - release it", NS);
+ ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS]));
+ }
+ else
+ {
+ Debugprintf ("Frame %d out of seq - save", NS);
+ }
+
+ Buffer->CHAIN = 0;
+ LINK->RXFRAMES[NS] = Buffer;
+ goto CheckPF;
+ }
+
+ // IN SEQUENCE FRAME
+
+ // Remove any stored frame with this seq
+
+ if (LINK->RXFRAMES[NS])
+ ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS]));
+
+ if (LINK->L2STATE == 6) // REJ?
+ {
+ // If using REJ we can cancel REJ state.
+ // If using SREJ we only cancel REJ if we have no stored frames
+
+ if (LINK->Ver2point2)
+ {
+ // see if any frames saved.
+
+ int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ if (LINK->RXFRAMES[i])
+ goto stayinREJ;
+ }
+ // Drop through if no stored frames
+ }
+
+ // CANCEL REJ
+
+ LINK->L2STATE = 5;
+ LINK->L2FLAGS &= ~REJSENT;
+ }
+
+stayinREJ:
+
+ PROC_I_FRAME(LINK, PORT, Buffer); // Passes on or releases Buffer
+
+
+CheckPF:
+
+ if (LINK->Ver2point2 == 0) // Unless using SREJ
+ {
+ if (LINK->L2FLAGS & REJSENT)
+ {
+ return; // DONT SEND ANOTHER TILL REJ IS CANCELLED
+ }
+ }
+
+ if (CTL & PFBIT)
+ {
+ if (LINK->L2STATE == 6)
+ LINK->L2FLAGS |= REJSENT; // Set "REJ Sent"
+ else
+ {
+ // we have all frames. Clear anything in RXFRAMES
+
+ int n = 0;
+
+ while (n < 8)
+ {
+ if (LINK->RXFRAMES[n])
+ ReleaseBuffer(Q_REM(&LINK->RXFRAMES[n]));
+
+ n++;
+ }
+ }
+ LINK->L2ACKREQ = 0; // CANCEL RR NEEDED
+
+ SEND_RR_RESP(LINK, PFBIT);
+
+ // RECORD TIME
+
+ LINK->LAST_F_TIME = REALTIMETICKS;
+ }
+ else
+ if (LINK->L2ACKREQ == 0) // Resptime is zero so send RR now
+ SEND_RR_RESP(LINK, 0);
+
+}
+
+
+VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer)
+{
+ int Length;
+ char * Info;
+ UCHAR PID;
+ struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)Buffer;
+ UCHAR * EOA;
+ int n = 8; // Max Digis
+
+ LINK->LINKNR++; // INCREMENT OUR N(R)
+ LINK->LINKNR &= 7; // MODULO 8
+
+ // ATTACH I FRAMES TO LINK TABLE RX QUEUE - ONLY DATA IS ADDED (NOT ADDRESSES)
+
+ // IF DISC PENDING SET, IGNORE FRAME
+
+ if (LINK->L2FLAGS & DISCPENDING)
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ // Copy data down the buffer so PID comes after Header (DATAMESSAGE format)
+
+ Length = Buffer->LENGTH - (MSGHDDRLEN + 15); // Buffer Header + addrs + CTL
+ Info = &Buffer->PID;
+
+ // Adjust for DIGIS
+
+ EOA = &Buffer->ORIGIN[6]; // End of address Bit
+
+ while (((*EOA & 1) == 0) && n--)
+ {
+ Length -= 7;
+ Info += 7;
+ EOA += 7;
+ }
+
+ PID = EOA[2];
+
+ switch(PID)
+ {
+ case 0xcc:
+ case 0xcd:
+
+ // IP Message
+
+ if (n < 8) // If digis, move data back down buffer
+ {
+ memmove(&Buffer->PID, &EOA[2], Length);
+ Buffer->LENGTH -= (int)(&EOA[2] - &Buffer->PID);
+ }
+
+ Q_IP_MSG( Buffer);
+ break;
+
+ case 8:
+
+ // NOS FRAGMENTED IP
+
+ if (n < 8) // If digis, move data back down buffer
+ {
+ memmove(&Buffer->PID, &EOA[2], Length);
+ Buffer->LENGTH -= (int)(&EOA[2] - &Buffer->PID);
+ }
+
+ C_Q_ADD(&LINK->L2FRAG_Q, Buffer);
+
+ if (Buffer->L2DATA[0] == 0)
+ {
+ // THERE IS A WHOLE MESSAGE ON FRAG_Q - PASS TO IP
+
+ while(LINK->L2FRAG_Q)
+ {
+ Buffer = Q_REM(&LINK->L2FRAG_Q);
+ Q_IP_MSG( Buffer);
+ }
+ }
+ break;
+
+ default:
+
+ if (Length < 1 || Length > 257)
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ // Copy Data back over
+
+ memmove(&Msg->PID, Info, Length);
+
+ Buffer->LENGTH = Length + MSGHDDRLEN;
+
+ C_Q_ADD(&LINK->RX_Q, Buffer);
+ }
+
+ LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED
+ LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER
+}
+
+//*** CHECK RECEIVED N(R) COUNT
+
+VOID SDNRCHK(struct _LINKTABLE * LINK, UCHAR CTL)
+{
+ UCHAR NR = (CTL >> 5) & 7;
+
+ if (NR >= LINK->LINKWS) // N(R) >= WINDOW START?
+ {
+ // N(R) ABOVE OR EQUAL TO WINDOW START - OK IF NOT ABOVE N(S), OR N(S) BELOW WS
+
+ if (NR > LINK->LINKNS) // N(R) <= WINDOW END?
+ {
+ // N(R) ABOVE N(S) - DOES COUNT WRAP?
+
+ if (LINK->LINKNS >= LINK->LINKWS) // Doesnt wrap
+ goto BadNR;
+ }
+
+GoodNR:
+
+ if ((CTL & 0x0f) == SREJ)
+ if ((CTL & PFBIT) == 0)
+ return; // SREJ without F doesn't ACK anything
+
+ LINK->LINKWS = NR; // NEW WINDOW START = RECEIVED N(R)
+ ACKMSG(LINK); // Remove any acked messages
+ return;
+ }
+
+ // N(R) LESS THAN WINDOW START - ONLY OK IF WINDOW WRAPS
+
+ if (NR <= LINK->LINKNS) // N(R) <= WINDOW END?
+ goto GoodNR;
+
+BadNR:
+
+ // RECEIVED N(R) IS INVALID
+
+ LINK->SDREJF |= SDNRER; // FLAG A REJECT CONDITION
+ LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE
+}
+
+VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS)
+{
+ int Resent = (LINK->LINKNS - NS) & 7; // FRAMES TO RESEND
+
+ LINK->LINKNS = NS; // RESET N(S)
+
+ if (LINK->LINKTYPE == 3) // mode-Node
+ {
+ if (LINK->NEIGHBOUR)
+ LINK->NEIGHBOUR->NBOUR_RETRIES += Resent;
+ }
+}
+
+int COUNT_AT_L2(struct _LINKTABLE * LINK)
+{
+ // COUNTS FRAMES QUEUED ON AN L2 SESSION (IN BX)
+
+ int count = 0, abovelink = 0;
+ int n = 0;
+
+ if (LINK == NULL)
+ return 0;
+
+ abovelink = C_Q_COUNT((UINT *)&LINK->TX_Q);
+
+ // COUNT FRAMES IN TSLOTS
+
+ while (n < 8)
+ {
+ if (LINK->FRAMES[n])
+ count++;
+ n++;
+ }
+
+// ADD AL,AH ; TOTAL IN AL, NUMBER ABOVE LINK IN AH
+
+ return abovelink + count;
+}
+
+//*** RESET HDLC AND PURGE ALL QUEUES ETC.
+
+VOID RESET2X(struct _LINKTABLE * LINK)
+{
+ LINK->SDREJF = 0; // CLEAR FRAME REJECT FLAGS
+ LINK->LINKWS = 0; // CLEAR WINDOW POINTERS
+ LINK->LINKOWS = 0;
+ LINK->LINKNR = 0; // CLEAR N(R)
+ LINK->LINKNS = 0; // CLEAR N(S)
+ LINK->SDTSLOT= 0;
+ LINK->L2STATE = 5; // RESET STATE
+ LINK->L2FLAGS = 0;
+}
+
+
+VOID CLEARL2QUEUES(struct _LINKTABLE * LINK)
+{
+ // GET RID OF ALL FRAMES THAT ARE QUEUED
+
+ int n = 0;
+
+ while (n < 8)
+ {
+ while (LINK->FRAMES[n])
+ ReleaseBuffer(Q_REM(&LINK->FRAMES[n]));
+ while (LINK->RXFRAMES[n])
+ ReleaseBuffer(Q_REM(&LINK->RXFRAMES[n]));
+ n++;
+ }
+
+ // GET RID OF ALL FRAMES THAT ARE
+ // QUEUED ON THE TX HOLDING QUEUE, RX QUEUE AND LEVEL 3 QUEUE
+
+
+ while (LINK->TX_Q)
+ ReleaseBuffer(Q_REM(&LINK->TX_Q));
+
+ while (LINK->RX_Q)
+ ReleaseBuffer(Q_REM(&LINK->RX_Q));
+
+}
+
+VOID RESET2(struct _LINKTABLE * LINK)
+{
+ CLEARL2QUEUES(LINK);
+ RESET2X(LINK);
+}
+
+VOID SENDSABM(struct _LINKTABLE * LINK)
+{
+ L2SENDCOMMAND(LINK, SABM | PFBIT);
+}
+
+
+VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer)
+{
+ // TIME STAMP IT
+
+ time(&Buffer->Timestamp);
+
+ if (PORT->TXPORT)
+ {
+ Buffer->PORT = PORT->TXPORT; // update port no in header
+
+ PORT = GetPortTableEntryFromPortNum(PORT->TXPORT);
+
+ if (PORT == NULL)
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+ }
+ C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer);
+}
+
+
+UCHAR * SETUPADDRESSES(struct _LINKTABLE * LINK, PMESSAGE Msg)
+{
+ // COPY ADDRESSES FROM LINK TABLE TO MESSAGE _BUFFER
+
+ UCHAR * ptr1 = &LINK->DIGIS[0];
+ UCHAR * ptr2 = &Msg->CTL;
+ int Digis = 8;
+
+ memcpy(&Msg->DEST[0], &LINK->LINKCALL[0], 14); // COPY DEST AND ORIGIN
+
+ Msg->DEST[6] |= 0x60;
+ Msg->ORIGIN[6] |= 0x60;
+
+ while (Digis)
+ {
+ if (*(ptr1)) // any more to copy?
+ {
+ memcpy(ptr2, ptr1, 7);
+ ptr1 += 7;
+ ptr2 += 7;
+ Digis--;
+ }
+ else
+ break;
+ }
+
+ *(ptr2 - 1) |= 1; // SET END OF ADDRESSES
+
+ return ptr2; // Pointer to CTL
+}
+
+VOID SDETX(struct _LINKTABLE * LINK)
+{
+ // Start sending frsmes if possible
+
+ struct PORTCONTROL * PORT;
+ int Outstanding;
+ UCHAR * ptr1, * ptr2;
+ UCHAR CTL;
+ int count;
+ MESSAGE * Msg;
+ MESSAGE * Buffer;
+
+ // DONT SEND IF RESEQUENCING RECEIVED FRAMES - CAN CAUSE FRMR PROBLEMS
+
+// if (LINK->L2RESEQ_Q)
+// return;
+
+ if (LINK->LINKPORT->PORTNUMBER == 19)
+ {
+ int i = 0;
+ }
+
+ Outstanding = LINK->LINKNS - LINK->LINKOWS; // Was WS not NS
+
+ if (Outstanding < 0)
+ Outstanding += 8; // allow for wrap
+
+ if (Outstanding >= LINK->LINKWINDOW) // LIMIT
+ return;
+
+ // See if we can load any more frames into the frame holding q
+
+ while (LINK->TX_Q && LINK->FRAMES[LINK->SDTSLOT] == NULL)
+ {
+ Msg = Q_REM(&LINK->TX_Q);
+ Msg->CHAIN = NULL;
+ LINK->FRAMES[LINK->SDTSLOT] = Msg;
+ LINK->SDTSLOT ++;
+ LINK->SDTSLOT &= 7;
+ }
+
+ // dont send while poll outstanding
+
+ while ((LINK->L2FLAGS & POLLSENT) == 0)
+ {
+ Msg = LINK->FRAMES[LINK->LINKNS]; // is next frame available?
+
+ if (Msg == NULL)
+ return;
+
+ // send the frame
+
+ // GET BUFFER FOR COPY OF MESSAGE - HAVE TO KEEP ORIGINAL FOR RETRIES
+
+ Buffer = GetBuff();
+
+ if (Buffer == NULL)
+ return;
+
+ ptr2 = SETUPADDRESSES(LINK, Buffer); // copy addresses
+
+ // ptr2 NOW POINTS TO COMMAND BYTE
+
+ // GOING TO SEND I FRAME - WILL ACK ANY RECEIVED FRAMES
+
+ LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED
+ LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY
+ LINK->KILLTIMER = 0; // RESET IDLE CIRCUIT TIMER
+
+ CTL = LINK->LINKNR << 5; // GET CURRENT N(R), SHIFT IT TO TOP 3 BITS
+ CTL |= LINK->LINKNS << 1; // BITS 1-3 OF CONTROL BYTE
+
+ LINK->LINKNS++; // INCREMENT NS
+ LINK->LINKNS &= 7; // mod 8
+
+ // SET P BIT IF END OF WINDOW OR NO MORE TO SEND
+
+ if (LINK->VER1FLAG == 0) // NO POLL BIT IF V1
+ {
+ Outstanding = LINK->LINKNS - LINK->LINKOWS;
+
+ if (Outstanding < 0)
+ Outstanding += 8; // allow for wrap
+
+ // if at limit, or no more to send, set P)
+
+ if (Outstanding >= LINK->LINKWINDOW || LINK->FRAMES[LINK->LINKNS] == NULL)
+ {
+ CTL |= PFBIT;
+ LINK->L2FLAGS |= POLLSENT;
+ LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER
+
+ // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND (or ACK if ACKMODE)
+
+ Buffer->Linkptr = LINK;
+ }
+ }
+
+ *(ptr2++) = CTL; // TO DATA (STARTING WITH PID)
+
+ count = Msg->LENGTH - MSGHDDRLEN;
+
+ if (count > 0) // SHOULD ALWAYS BE A PID, BUT BETTER SAFE THAN SORRY
+ {
+ ptr1 = (UCHAR *)Msg;
+ ptr1 += MSGHDDRLEN;
+ memcpy(ptr2, ptr1, count);
+ }
+
+ Buffer->DEST[6] |= 0x80; // SET COMMAND
+
+ Buffer->LENGTH = (int)(ptr2 - (UCHAR *)Buffer) + count; // SET NEW LENGTH
+
+ LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER
+
+ PORT = LINK->LINKPORT;
+
+ if (PORT)
+ {
+ Buffer->PORT = PORT->PORTNUMBER;
+ PUT_ON_PORT_Q(PORT, Buffer);
+ }
+ else
+ {
+ Buffer->Linkptr = 0;
+ ReleaseBuffer(Buffer);
+ }
+
+ }
+}
+
+VOID L2TimerProc()
+{
+ int i = MAXLINKS;
+ struct _LINKTABLE * LINK = LINKS;
+ struct PORTCONTROL * PORT = PORTTABLE;
+
+ while (i--)
+ {
+ if (LINK->LINKCALL[0] == 0)
+ {
+ LINK++;
+ continue;
+ }
+
+ // CHECK FOR TIMER EXPIRY OR BUSY CLEARED
+
+ PORT = LINK->LINKPORT;
+
+ if (PORT == NULL)
+ {
+ LINK++;
+ continue; // just ion case!!
+ }
+
+ if (LINK->L2TIMER)
+ {
+ LINK->L2TIMER--;
+ if (LINK->L2TIMER == 0)
+ {
+ L2TIMEOUT(LINK, PORT);
+ LINK++;
+ continue;
+ }
+ }
+ else
+ {
+ // TIMER NOT RUNNING - MAKE SURE STATE NOT BELOW 5 - IF
+ // IT IS, SOMETHING HAS GONE WRONG, AND LINK WILL HANG FOREVER
+
+ if (LINK->L2STATE < 5 && LINK->L2STATE != 2 && LINK->L2STATE != 1) // 2 = CONNECT - PROBABLY TO CQ
+ LINK->L2TIMER = 2; // ARBITRARY VALUE
+ }
+
+ // TEST FOR RNR SENT, AND NOT STILL BUSY
+
+ if (LINK->L2FLAGS & RNRSENT)
+ {
+ // Was busy
+
+ if (RR_OR_RNR(LINK) != RNR) // SEE IF STILL BUSY
+ {
+ // Not still busy - tell other end
+
+ // Just sending RR will hause a hang of RR is missed, and other end does not poll on Busy
+ // Try sending RR CP, so we will retry if not acked
+
+ LINK->L2ACKREQ = 0; // CLEAR ANY DELAYED ACK TIMER
+
+ if (LINK->L2RETRIES == 0) // IF RR(P) OUTSTANDING WILl REPORT ANYWAY
+ {
+ SendSupervisCmd(LINK);
+ LINK++;
+ continue;
+ }
+ }
+ }
+ else
+ {
+ // NOT BUSY
+
+ if (LINK->L2ACKREQ) // DELAYED ACK TIMER
+ {
+ if (LINK->L2RETRIES == 0) // DONT SEND RR RESPONSE WHILEST RR(P) OUTSTANDING
+ {
+ LINK->L2ACKREQ--;
+ if (LINK->L2ACKREQ == 0)
+ {
+ SEND_RR_RESP(LINK, 0); // NO F BIT
+ LINK++;
+ continue;
+ }
+ }
+ }
+ }
+
+ // CHECK FOR REJ TIMEOUT
+
+ if (LINK->REJTIMER)
+ {
+ LINK->REJTIMER--;
+ if (LINK->REJTIMER == 0) // {REJ HAS TIMED OUT (THIS MUST BE A VERSION 1 SESSION)
+ {
+ // CANCEL REJ STATE
+
+ if (LINK->L2STATE == 6) // REJ?
+ LINK->L2STATE = 5; // CLEAR REJ
+ }
+ }
+
+ // See if time for link validation poll
+
+ if (LINK->L2SLOTIM)
+ {
+ LINK->L2SLOTIM--;
+ if (LINK->L2SLOTIM == 0) // Time to poll
+ {
+ SendSupervisCmd(LINK);
+ LINK++;
+ continue;
+ }
+ }
+
+ // See if idle too long
+
+ LINK->KILLTIMER++;
+
+ if (L2KILLTIME && LINK->KILLTIMER > L2KILLTIME)
+ {
+ // CIRCUIT HAS BEEN IDLE TOO LONG - SHUT IT DOWN
+
+ LINK->KILLTIMER = 0;
+ LINK->L2TIMER = 1; // TO FORCE DISC
+ LINK->L2STATE = 4; // DISCONNECTING
+
+ // TELL OTHER LEVELS
+
+ InformPartner(LINK, NORMALCLOSE);
+ }
+ LINK++;
+ }
+}
+
+VOID SendSupervisCmd(struct _LINKTABLE * LINK)
+{
+ // Send Super Command RR/RNR/REJ(P)
+
+ UCHAR CTL;
+
+ if (LINK->VER1FLAG == 1)
+ {
+ // VERSION 1 TIMEOUT
+
+ // RESET TO RESEND I FRAMES
+
+ LINK->LINKNS = LINK->LINKOWS;
+
+ SDETX(LINK); // PREVENT FRMR (I HOPE)
+ }
+
+ // SEND RR COMMAND - EITHER AS LINK VALIDATION POLL OR FOLLOWING TIMEOUT
+
+ LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED
+
+ CTL = RR_OR_RNR(LINK);
+
+// MOV L2STATE[EBX],5 ; CANCEL REJ - ACTUALLY GOING TO 'PENDING ACK'
+
+ CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS
+ CTL |= PFBIT;
+
+ LINK->L2FLAGS |= POLLSENT;
+
+ L2SENDCOMMAND(LINK, CTL);
+
+ LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY
+}
+
+void SEND_RR_RESP(struct _LINKTABLE * LINK, UCHAR PF)
+{
+ UCHAR CTL;
+
+ CTL = RR_OR_RNR(LINK);
+
+// MOV L2STATE[EBX],5 ; CANCEL REJ - ACTUALLY GOING TO 'PENDING ACK'
+
+ CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS
+ CTL |= PF;
+
+ L2SENDRESPONSE(LINK, CTL);
+
+ ACKMSG(LINK); // SEE IF STILL WAITING FOR ACK
+}
+
+VOID ACKMSG(struct _LINKTABLE * LINK)
+{
+ // RELEASE ANY ACKNOWLEDGED FRAMES
+
+ while (LINK->LINKOWS != LINK->LINKWS) // is OLD WINDOW START EQUAL TO NEW WINDOW START?
+ {
+ // No, so frames to ack
+
+ if (LINK->FRAMES[LINK->LINKOWS])
+ ReleaseBuffer(Q_REM(&LINK->FRAMES[LINK->LINKOWS]));
+ else
+ {
+ char Call1[12], Call2[12];
+
+ Call1[ConvFromAX25(LINK->LINKCALL, Call1)] = 0;
+ Call2[ConvFromAX25(LINK->OURCALL, Call2)] = 0;
+
+ Debugprintf("Missing frame to ack Seq %d Calls %s %s", LINK->LINKOWS, Call1, Call2);
+ }
+
+ LINK->IFrameRetryCounter = 0;
+
+ LINK->LINKOWS++; // INCREMENT OLD WINDOW START
+ LINK->LINKOWS &= 7; // MODULO 8
+
+ // SOMETHING HAS BEEN ACKED - RESET RETRY COUNTER
+
+ if (LINK->L2RETRIES)
+ LINK->L2RETRIES = 1; // MUSTN'T SET TO ZERO - COULD CAUSE PREMATURE RETRANSMIT
+
+ }
+
+ if (LINK->LINKWS != LINK->LINKNS) // IS N(S) = NEW WINDOW START?
+ {
+ // NOT ALL I-FRAMES HAVE BEEN ACK'ED - RESTART TIMER
+
+ // Need to kill link if we are getting repeated RR(F) after timeout
+ // (Indicating other station is seeing our RR(P) but not the resent I frame)
+
+ if (LINK->IFrameRetryCounter++ > LINK->LINKPORT->PORTN2)
+ {
+ Debugprintf("Too many repeats of same I frame - closing connection");
+ LINK->L2TIMER = 1; // USE TIMER TO SEND DISC
+ LINK->L2STATE = 4; // DISCONNECTING
+ return;
+ }
+
+
+ LINK->L2TIMER = LINK->L2TIME;
+ return;
+ }
+
+ // ALL FRAMES HAVE BEEN ACKED - CANCEL TIMER UNLESS RETRYING
+ // IF RETRYING, MUST ONLY CANCEL WHEN RR(F) RECEIVED
+
+ if (LINK->VER1FLAG == 1 || LINK->L2RETRIES == 0) // STOP TIMER IF LEVEL 1 or not retrying
+ {
+ LINK->L2TIMER = 0;
+ LINK->L2FLAGS &= ~POLLSENT; // CLEAR I(P) SET (IN CASE TALKING TO OLD BPQ!)
+ }
+
+ // IF DISCONNECT REQUEST OUTSTANDING, AND NO FRAMES ON TX QUEUE, SEND DISC
+
+ if ((LINK->L2FLAGS & DISCPENDING) && LINK->TX_Q == 0)
+ {
+ LINK->L2FLAGS &= ~DISCPENDING;
+
+ LINK->L2TIMER = 1; // USE TIMER TO SEND DISC
+ LINK->L2STATE = 4; // DISCONNECTING
+ }
+}
+
+VOID CONNECTFAILED();
+
+VOID L2TIMEOUT(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT)
+{
+ // TIMER EXPIRED
+
+ // IF LINK UP (STATE 5 OR ABOVE) SEND RR/RNR AS REQUIRED
+ // IF S2, REPEAT SABM
+ // IF S3, REPEAT FRMR
+ // IF S4, REPEAT DISC
+
+
+ PORT->L2TIMEOUTS++; // FOR STATS
+
+ if (LINK->L2STATE == 0)
+ return;
+
+ if (LINK->L2STATE == 1)
+ {
+ // XID
+
+ LINK->L2RETRIES++;
+ if (LINK->L2RETRIES >= PORT->PORTN2)
+ {
+ // RETRIED N2 TIMES - Give up
+
+ CONNECTFAILED(LINK); // TELL LEVEL 4 IT FAILED
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ DetachKISSHF(PORT);
+
+ CLEAROUTLINK(LINK);
+ return;
+ }
+
+ L2SENDXID(LINK);
+ return;
+ }
+
+
+ if (LINK->L2STATE == 2)
+ {
+ // CONNECTING
+
+ LINK->L2RETRIES++;
+ if (LINK->L2RETRIES >= PORT->PORTN2)
+ {
+ // RETRIED N2 TIMES - Give up
+
+ CONNECTFAILED(LINK); // TELL LEVEL 4 IT FAILED
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ DetachKISSHF(PORT);
+
+ CLEAROUTLINK(LINK);
+ return;
+ }
+
+ SENDSABM(LINK);
+ return;
+ }
+
+ if (LINK->L2STATE == 4)
+ {
+ // DISCONNECTING
+
+ LINK->L2RETRIES++;
+
+ if (LINK->L2RETRIES >= PORT->PORTN2)
+ {
+ // RETRIED N2 TIMES - JUST CLEAR OUT LINK
+
+ if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
+ DetachKISSHF(PORT);
+
+ CLEAROUTLINK(LINK);
+ return;
+ }
+
+ L2SENDCOMMAND(LINK, DISC | PFBIT);
+ return;
+ }
+
+ if (LINK->L2STATE == 3)
+ {
+ // FRMR
+
+ LINK->L2RETRIES++;
+ if (LINK->L2RETRIES >= PORT->PORTN2)
+ {
+ // RETRIED N2 TIMES - RESET LINK
+
+ LINK->L2RETRIES = 0;
+ LINK->L2STATE = 2;
+ SENDSABM(LINK);
+ return;
+ }
+ }
+
+ // STATE 5 OR ABOVE
+
+ // SEND RR(P) UP TO N2 TIMES
+
+ LINK->L2RETRIES++;
+
+ if (LINK->L2RETRIES >= PORT->PORTN2)
+ {
+ // RETRIED N TIMES SEND A COUPLE OF DISCS AND THEN CLOSE
+
+ InformPartner(LINK, RETRIEDOUT); // TELL OTHER END ITS GONE
+
+ LINK->L2RETRIES -= 1; // Just send one DISC
+ LINK->L2STATE = 4; // CLOSING
+
+ L2SENDCOMMAND(LINK, DISC | PFBIT);
+ return;
+ }
+
+ SendSupervisCmd(LINK);
+}
+
+VOID SDFRMR(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT)
+{
+ PORT->L2FRMRTX++;
+
+ LINK->L2STATE = 3; // ENTER FRMR STATE
+
+ LINK->L2TIMER = LINK->L2TIME; //SET TIMER
+
+ SENDFRMR(LINK);
+}
+
+VOID SENDFRMR(struct _LINKTABLE * LINK)
+{
+ // RESEND FRMR
+
+ struct PORTCONTROL * PORT;
+ MESSAGE * Buffer;
+ UCHAR * ptr;
+
+ Buffer = SETUPL2MESSAGE(LINK, FRMR);
+
+ if (Buffer == NULL)
+ return;
+
+ Buffer->ORIGIN[6] |= 0x80; // SET RESPONSE
+
+ ptr = &Buffer->PID;
+
+ *(ptr++) = LINK->SDRBYTE; // MOVE REJECT C-BYTE
+
+ *(ptr++) = LINK->LINKNR << 5 | LINK->LINKNS << 1;
+
+ *(ptr++) = LINK->SDREJF; // MOVE REJECT FLAGS
+
+ Buffer->LENGTH += 3;
+
+ PORT = LINK->LINKPORT;
+ Buffer->PORT = PORT->PORTNUMBER;
+
+ if (PORT)
+ PUT_ON_PORT_Q(PORT, Buffer);
+ else
+ ReleaseBuffer(Buffer);
+
+ return;
+}
+
+VOID CLEAROUTLINK(struct _LINKTABLE * LINK)
+{
+ char toCall[12], fromCall[12];
+
+ toCall[ConvFromAX25(LINK->LINKCALL, toCall)] = 0;
+ fromCall[ConvFromAX25(LINK->OURCALL, fromCall)] = 0;
+
+ hookL2SessionDeleted(LINK->LINKPORT->PORTNUMBER, fromCall, toCall, LINK);
+
+ seeifUnlockneeded(LINK);
+
+ CLEARL2QUEUES(LINK); // TO RELEASE ANY BUFFERS
+
+ memset(LINK, 0, sizeof(struct _LINKTABLE));
+}
+
+VOID L2SENDXID(struct _LINKTABLE * LINK)
+{
+ // Set up and send XID
+
+ struct PORTCONTROL * PORT;
+ UCHAR * ptr;
+ unsigned int xidval;
+ MESSAGE * Buffer;
+
+ if (LINK->LINKPORT == 0)
+ return; //??? has been zapped
+
+ Buffer = SETUPL2MESSAGE(LINK, XID | PFBIT);
+
+ if (Buffer == NULL)
+ {
+ // NO BUFFERS - SET TIMER TO FORCE RETRY
+
+ LINK->L2TIMER = 10*3; // SET TIMER
+ return;
+ }
+
+ Buffer->DEST[6] |= 0x80; // SET COMMAND
+
+ ptr = &Buffer->PID;
+
+ // Set up default XID Mod 8
+
+ *ptr++ = 0x82; // FI
+ *ptr++ = 0x80; // GI
+ *ptr++ = 0x0;
+ *ptr++ = 0x10; // Length 16
+
+ *ptr++ = 0x02; // Classes of Procedures
+ *ptr++ = 0x02; // Length
+ *ptr++ = 0x00; //
+ *ptr++ = 0x21; // ABM Half Duplex
+
+ // We offer REJ, SREJ and SREJ Multiframe
+
+ *ptr++ = 0x03; // Optional Functions
+ *ptr++ = 0x03; // Len
+
+ // Sync TX, SREJ Multiframe 16 bit FCS, Mod 8, TEST,
+ // Extended Addressing, REJ, SREJ
+
+ xidval = OPMustHave | OPSREJ | OPSREJMult | OPREJ | OPMod8;
+ *ptr++ = xidval >> 16;
+ *ptr++ = xidval >> 8;
+ *ptr++ = xidval;
+
+
+ *ptr++ = 0x06; // RX Packet Len
+ *ptr++ = 0x02; // Len
+ *ptr++ = 0x08; //
+ *ptr++ = 0x00; // 2K bits (256) Bytes
+
+ *ptr++ = 0x08; // RX Window
+ *ptr++ = 0x01; // Len
+ *ptr++ = 0x07; // 7
+
+ Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH
+
+ LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER
+
+ // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND
+
+ Buffer->Linkptr = LINK;
+
+ PORT = LINK->LINKPORT;
+
+ if (PORT)
+ {
+ Buffer->PORT = PORT->PORTNUMBER;
+ PUT_ON_PORT_Q(PORT, Buffer);
+ }
+ else
+ {
+ Buffer->Linkptr = 0;
+ ReleaseBuffer(Buffer);
+ }
+}
+
+
+
+
+
+
+VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD)
+{
+ // SEND COMMAND IN CMD
+
+ struct PORTCONTROL * PORT;
+ MESSAGE * Buffer;
+
+ if (LINK->LINKPORT == 0)
+ return; //??? has been zapped
+
+ Buffer = SETUPL2MESSAGE(LINK, CMD);
+
+ if (Buffer == NULL)
+ {
+ // NO BUFFERS - SET TIMER TO FORCE RETRY
+
+ if (CMD & PFBIT) // RESPONSE EXPECTED?
+ LINK->L2TIMER = 10*3; // SET TIMER
+
+ return;
+ }
+
+ Buffer->DEST[6] |= 0x80; // SET COMMAND
+
+ if (CMD & PFBIT) // RESPONSE EXPECTED?
+ {
+ LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER
+
+ // FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND
+
+ Buffer->Linkptr = LINK;
+ }
+
+ PORT = LINK->LINKPORT;
+
+ if (PORT)
+ {
+ Buffer->PORT = PORT->PORTNUMBER;
+ PUT_ON_PORT_Q(PORT, Buffer);
+ }
+ else
+ {
+ Buffer->Linkptr = 0;
+ ReleaseBuffer(Buffer);
+ }
+}
+
+
+
+
+
+
+VOID L2SENDRESPONSE(struct _LINKTABLE * LINK, int CMD)
+{
+ // SEND Response IN CMD
+
+ struct PORTCONTROL * PORT;
+ MESSAGE * Buffer;
+
+ Buffer = SETUPL2MESSAGE(LINK, CMD);
+
+ if (Buffer == NULL)
+ {
+ // NO BUFFERS - SET TIMER TO FORCE RETRY
+
+ if (CMD & PFBIT) // RESPONSE EXPECTED?
+ LINK->L2TIMER = 10*3; // SET TIMER
+
+ return;
+ }
+
+ Buffer->ORIGIN[6] |= 0x80; // SET RESPONSE
+
+ LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY
+
+ PORT = LINK->LINKPORT;
+ Buffer->PORT = PORT->PORTNUMBER;
+
+ if (PORT)
+ PUT_ON_PORT_Q(PORT, Buffer);
+ else
+ ReleaseBuffer(Buffer);
+
+}
+
+
+MESSAGE * SETUPL2MESSAGE(struct _LINKTABLE * LINK, UCHAR CMD)
+{
+ MESSAGE * Buffer;
+ UCHAR * ptr;
+
+ Buffer = GetBuff();
+
+ if (Buffer == NULL)
+ return NULL;
+
+ ptr = SETUPADDRESSES(LINK, Buffer); // copy addresses
+
+ // ptr NOW POINTS TO COMMAND BYTE
+
+ *(ptr)++ = CMD;
+
+ Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH
+
+ return Buffer;
+}
+
+
+VOID L3LINKCLOSED(struct _LINKTABLE * LINK, int Reason);
+
+VOID InformPartner(struct _LINKTABLE * LINK, int Reason)
+{
+ // LINK IS DISCONNECTING - IF THERE IS A CROSSLINK, SEND DISC TO IT
+
+ if (LINK->LINKTYPE == 3)
+ {
+ L3LINKCLOSED(LINK, Reason);
+ return;
+ }
+
+ if (LINK->CIRCUITPOINTER)
+ {
+ CloseSessionPartner(LINK->CIRCUITPOINTER);
+ CLEARSESSIONENTRY(LINK->CIRCUITPOINTER);
+ }
+}
+
+
+UINT RR_OR_RNR(struct _LINKTABLE * LINK)
+{
+ UCHAR Temp;
+ TRANSPORTENTRY * Session;
+
+ LINK->L2FLAGS &= ~RNRSENT;
+
+ // SET UP APPROPRIATE SUPER COMMAND
+
+ if (LINK->LINKTYPE == 3)
+
+ // Node to Node - only busy if short of buffers
+
+ goto CHKBUFFS;
+
+// UP OR DOWN LINK - SEE IF SESSION IS BUSY
+
+ if (LINK->CIRCUITPOINTER == 0)
+ goto CHKBUFFS; // NOT CONNECTED
+
+ Session = LINK->CIRCUITPOINTER; // TO CIRCUIT ENTRY
+
+ Temp = CHECKIFBUSYL2(Session); //TARGET SESSION BUSY?
+
+ if (Temp & L4BUSY)
+ goto SENDRNR; // BUSY
+
+CHKBUFFS:
+
+ if (QCOUNT < 20)
+ goto SENDRNR; // NOT ENOUGH
+
+ // SEND REJ IF IN REJ STATE
+
+ if (LINK->L2STATE == 6)
+ {
+
+ // We may have the needed frame in RXFRAMES
+
+CheckNSLoop2:
+
+ if (LINK->RXFRAMES[LINK->LINKNR])
+ {
+ // We have the first missing frame. Process it.
+
+ struct PORTCONTROL * PORT = LINK->LINKPORT;
+ MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]);
+
+ Debugprintf("L2 about to send REJ - process saved Frame %d", LINK->LINKNR);
+ PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer
+
+ // NR has been updated.
+
+ // Clear REJ if we have no more saved
+
+ if (LINK->Ver2point2) // Using SREJ?
+ {
+ // see if any frames saved.
+
+ int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ if (LINK->RXFRAMES[i])
+ goto stayinREJ2;
+ }
+ // Drop through if no stored frames
+ }
+
+ LINK->L2STATE = 5;
+ LINK->L2FLAGS &= ~REJSENT;
+stayinREJ2:
+ LINK->L2ACKREQ = 0; // Cancel Resptime (Set by PROC_I_FRAME)
+
+ goto CheckNSLoop2; // See if OK or we have another saved frame
+ }
+ if (LINK->L2STATE == 6)
+
+ // if we support SREJ send that instesd or REJ
+
+ if (LINK->Ver2point2) // We only allow 2.2 with SREJ Multi
+ return SREJ;
+ else
+ return REJ;
+ }
+ return RR;
+
+SENDRNR:
+
+ LINK->L2FLAGS |= RNRSENT; // REMEMBER
+
+ return RNR;
+}
+
+
+VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg);
+
+VOID CONNECTFAILED(struct _LINKTABLE * LINK)
+{
+ ConnectFailedOrRefused(LINK, "Failure with");
+}
+VOID CONNECTREFUSED(struct _LINKTABLE * LINK)
+{
+ ConnectFailedOrRefused(LINK, "Busy from");
+}
+
+VOID L3CONNECTFAILED();
+
+VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg)
+{
+ // IF DOWNLINK, TELL PARTNER
+ // IF CROSSLINK, TELL ROUTE CONTROL
+
+ struct DATAMESSAGE * Buffer;
+ UCHAR * ptr1;
+ char Normcall[10];
+ TRANSPORTENTRY * Session;
+ TRANSPORTENTRY * InSession;
+
+ if (LINK->LINKTYPE == 3)
+ {
+ L3CONNECTFAILED(LINK); // REPORT TO LEVEL 3
+ return;
+ }
+
+ if (LINK->CIRCUITPOINTER == 0) // No crosslink??
+ return;
+
+ Buffer = GetBuff();
+
+ if (Buffer == NULL)
+ return;
+
+ // SET UP HEADER
+
+ Buffer->PID = 0xf0;
+
+ ptr1 = SetupNodeHeader(Buffer);
+
+ Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0;
+
+ ptr1 += sprintf(ptr1, "%s %s\r", Msg, Normcall);
+
+ Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer);
+
+ Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY
+ InSession = Session->L4CROSSLINK; // TO INCOMMING SESSION
+
+ CLEARSESSIONENTRY(Session);
+
+ if (InSession)
+ {
+ InSession->L4CROSSLINK = NULL; // CLEAR REVERSE LINK
+ C_Q_ADD(&InSession->L4TX_Q, Buffer);
+ PostDataAvailable(InSession);
+ }
+ else
+ ReleaseBuffer(Buffer);
+}
+
+VOID SENDCONNECTREPLY(struct _LINKTABLE * LINK)
+{
+ // LINK SETUP COMPLETE
+
+ struct DATAMESSAGE * Buffer;
+ UCHAR * ptr1;
+ char Normcall[10];
+ TRANSPORTENTRY * Session;
+ TRANSPORTENTRY * InSession;
+
+ if (LINK->LINKTYPE == 3)
+ return;
+
+ // UP/DOWN LINK
+
+ if (LINK->CIRCUITPOINTER == 0) // No crosslink??
+ return;
+
+ Buffer = GetBuff();
+
+ if (Buffer == NULL)
+ return;
+
+ // SET UP HEADER
+
+ Buffer->PID = 0xf0;
+
+ ptr1 = SetupNodeHeader(Buffer);
+
+ Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0;
+
+ ptr1 += sprintf(ptr1, "Connected to %s\r", Normcall);
+
+ Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer);
+
+ Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY
+ Session->L4STATE = 5;
+ InSession = Session->L4CROSSLINK; // TO INCOMMONG SESSION
+
+ if (InSession)
+ {
+ C_Q_ADD(&InSession->L4TX_Q, Buffer);
+ PostDataAvailable(InSession);
+ }
+}
+
+
+TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK)
+{
+ TRANSPORTENTRY * NewSess = L4TABLE;
+ int Index = 0;
+
+ while (Index < MAXCIRCUITS)
+ {
+ if (NewSess->L4USER[0] == 0)
+ {
+ // Got One
+
+ LINK->CIRCUITPOINTER = NewSess; // SETUP LINK-CIRCUIT CONNECTION
+
+ memcpy(NewSess->L4USER, LINK->LINKCALL, 7);
+ memcpy(NewSess->L4MYCALL, MYCALL, 7); // ALWAYS USE _NODE CALL
+
+ NewSess->CIRCUITINDEX = Index; //OUR INDEX
+ NewSess->CIRCUITID = NEXTID;
+
+ NEXTID++;
+ if (NEXTID == 0)
+ NEXTID++; // kEEP nON-ZERO
+
+ NewSess->L4TARGET.LINK = LINK;
+
+ NewSess->L4CIRCUITTYPE = L2LINK | UPLINK;
+
+ NewSess->L4STATE = 5; // SET LINK ACTIVE
+
+ NewSess->SESSPACLEN = LINK->LINKPORT->PORTPACLEN;
+
+
+ NewSess->SESSIONT1 = L4T1; // Default
+ NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW;
+
+ return NewSess;
+ }
+ Index++;
+ NewSess++;
+ }
+
+ return NULL;
+}
+
+
+VOID Digipeat(struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR * OurCall, int toPort, int UIOnly) // Digi it (if enabled)
+{
+ // WE MAY HAVE DISABLED DIGIPEAT ALTOGETHER, (DIGIFLAG=0),
+ // OR ALLOW ALLOW ONLY UI FRAMES TO BE DIGIED (DIGIFLAG=-1)
+
+ // toPort and UIOnly are used for Cross Port digi feature
+
+ int n;
+
+ if (PORT->DIGIFLAG == 0 && toPort == 0)
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ OurCall[6] |= 0x80; // SET HAS BEEN REPEATED
+
+ // SEE IF UI FRAME - scan forward for end of address bit
+
+ n = 8;
+
+ while ((OurCall[6] & 1) == 0)
+ {
+ OurCall += 7;
+
+ if ((OurCall - &Buffer->CTL) > 56)
+ {
+ // Run off end before findin end of address
+
+ ReleaseBuffer(Buffer);
+ return;
+ }
+ }
+
+ if (toPort) // Cross port digi
+ {
+ if (((OurCall[7] & ~PFBIT) == 3) || UIOnly == 0)
+ {
+ // UI or Digi all
+
+ Buffer->PORT = toPort; // update port no in header
+ PORT = GetPortTableEntryFromPortNum(toPort);
+
+ if (PORT == NULL)
+ ReleaseBuffer(Buffer);
+ else
+ PUT_ON_PORT_Q(PORT, Buffer);
+ return;
+ }
+ else
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+ }
+
+ if ((OurCall[7] & ~PFBIT) == 3)
+ {
+ // UI
+
+ // UI FRAME. IF DIGIMASK IS NON-ZERO, SEND TO ALL PORTS SET, OTHERWISE SEND TO DIGIPORT
+
+ PORT->L2DIGIED++;
+
+ if (toPort)
+ {
+ // Cross port digi
+
+ PORT = GetPortTableEntryFromPortNum(toPort);
+ Buffer->PORT = PORT->DIGIPORT; // update port no in header
+
+ if (PORT == NULL)
+ ReleaseBuffer(Buffer);
+ else
+ PUT_ON_PORT_Q(PORT, Buffer);
+
+ return;
+ }
+
+ if (PORT->DIGIMASK == 0)
+ {
+ if (PORT->DIGIPORT) // Cross Band Digi?
+ {
+ Buffer->PORT = PORT->DIGIPORT; // update port no in header
+
+ PORT = GetPortTableEntryFromPortNum(PORT->DIGIPORT);
+
+ if (PORT == NULL)
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+ }
+ PUT_ON_PORT_Q(PORT, Buffer);
+ }
+ else
+ {
+ DigiToMultiplePorts(PORT, Buffer);
+ ReleaseBuffer(Buffer);
+ }
+ return;
+ }
+
+ // Not UI - Only Digi if Digiflag not -1
+
+ if (PORT->DIGIFLAG == -1)
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ PORT->L2DIGIED++;
+
+ if (PORT->DIGIPORT) // Cross Band Digi?
+ {
+ Buffer->PORT = PORT->DIGIPORT; // update port no in header
+
+ PORT = GetPortTableEntryFromPortNum(PORT->DIGIPORT);
+
+ if (PORT == NULL)
+ {
+ ReleaseBuffer(Buffer);
+ return;
+ }
+ }
+ PUT_ON_PORT_Q(PORT, Buffer);
+}
+
+BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg)
+{
+ TRANSPORTENTRY * L4 = L4TABLE;
+ struct DATAMESSAGE * Buffer;
+ int i = MAXCIRCUITS;
+ UCHAR * ptr;
+
+ while (i--)
+ {
+ if ((CountBits64(L4->LISTEN) == 1) && ((1 << ((Msg->PORT & 0x7f) - 1) && L4->LISTEN)))
+ {
+ // See if he is calling our call
+
+ UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted
+ memcpy(ourcall, L4->L4USER, 7);
+ ourcall[6] ^= 0x1e; // Flip SSID
+
+ if (CompareCalls(Msg->DEST, ourcall))
+ {
+ // Get Session Entry for Downlink
+
+ TRANSPORTENTRY * NewSess = L4TABLE;
+ struct _LINKTABLE * LINK;
+ char Normcall[10];
+
+ int Index = 0;
+
+ while (Index < MAXCIRCUITS)
+ {
+ if (NewSess->L4USER[0] == 0)
+ {
+ // Got One
+
+ L4->L4CROSSLINK = NewSess;
+ NewSess->L4CROSSLINK = L4;
+
+ memcpy(NewSess->L4USER, L4->L4USER, 7);
+ memcpy(NewSess->L4MYCALL, L4->L4USER, 7);
+
+ NewSess->CIRCUITINDEX = Index; //OUR INDEX
+ NewSess->CIRCUITID = NEXTID;
+ NewSess->L4STATE = 5;
+ NewSess->L4CIRCUITTYPE = L2LINK+UPLINK;
+
+ NEXTID++;
+ if (NEXTID == 0)
+ NEXTID++; // kEEP nON-ZERO
+
+ NewSess->SESSIONT1 = L4->SESSIONT1;
+ NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW;
+
+ // SET UP NEW SESSION (OR RESET EXISTING ONE)
+
+ FindLink(Msg->ORIGIN, ourcall, PORT->PORTNUMBER, &LINK);
+
+ if (LINK == NULL)
+ return FALSE;
+
+ memcpy(LINK->LINKCALL, Msg->ORIGIN, 7);
+ LINK->LINKCALL[6] &= 0xFE;
+ memcpy(LINK->OURCALL, ourcall, 7);
+
+ LINK->LINKPORT = PORT;
+
+ LINK->L2TIME = PORT->PORTT1;
+/*
+ // Copy Digis
+
+ n = 7;
+ ptr = &LINK->DIGIS[0];
+
+ while (axcalls[n])
+ {
+ memcpy(ptr, &axcalls[n], 7);
+ n += 7;
+ ptr += 7;
+
+ LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI
+ }
+*/
+ LINK->LINKTYPE = 2; // DOWNLINK
+ LINK->LINKWINDOW = PORT->PORTWINDOW;
+
+ RESET2(LINK); // RESET ALL FLAGS
+
+ LINK->L2STATE = 5; // CONNECTED
+
+ LINK->CIRCUITPOINTER = NewSess;
+
+ NewSess->L4TARGET.LINK = LINK;
+
+ if (PORT->PORTPACLEN)
+ NewSess->SESSPACLEN = L4->SESSPACLEN = PORT->PORTPACLEN;
+
+ L2SENDUA(PORT, Msg, Msg); // RESET OF DOWN/CROSSLINK
+
+ L4->LISTEN = FALSE; // Take out of listen mode
+
+ // Tell User
+
+ Buffer = GetBuff();
+
+ if (Buffer == NULL)
+ return TRUE;
+
+ // SET UP HEADER
+
+ Buffer->PID = 0xf0;
+
+ ptr = &Buffer->L2DATA[0];
+
+ Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0;
+
+ ptr += sprintf(ptr, "Incoming call from %s\r", Normcall);
+
+ Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer);
+
+ C_Q_ADD(&L4->L4TX_Q, Buffer);
+ PostDataAvailable(L4);
+
+ return TRUE;
+
+ }
+ Index++;
+ NewSess++;
+ }
+ return FALSE;
+ }
+ }
+ L4++;
+ }
+ return FALSE;
+}
+
+
+int COUNTLINKS(int Port);
+VOID SuspendOtherPorts(struct TNCINFO * ThisTNC);
+VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC);
+
+
+int CheckKissInterlock(struct PORTCONTROL * PORT, int Exclusive)
+{
+ // This checks for interlocked kiss and other ports. Returns 1 if attach/connect not allowed
+
+ // If Exclusive is not set allow connects on specified port up to l2limit,
+
+ // If Exclusive is set also don't allow any connects on specified port.
+
+ // Generally use Exclusive if locking a port that doesn't allow shared access, eg ARDOP, VARAus
+
+ // Maybe only Exclusive is needed, and just check session mode ports. Sharing of KISS ports is controlled by USERS
+
+ int Interlock = PORT->PORTINTERLOCK;
+
+ if (Interlock == 0)
+ return 0; // No locking
+
+ PORT = PORTTABLE;
+
+ if (Exclusive)
+ {
+ while(PORT)
+ {
+ if (PORT->TNC)
+ {
+ struct TNCINFO * TNC = PORT->TNC;
+
+ if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group
+ {
+ // See if port in use
+
+ int n;
+
+ for (n = 0; n <= 26; n++)
+ {
+ if (TNC->PortRecord->ATTACHEDSESSIONS[n])
+ {
+ return TNC->Port; ; // Refuse Connect
+ }
+ }
+ }
+ }
+ PORT = PORT->PORTPOINTER;
+ }
+ }
+ return 0; // ok to connect
+}
+
+int seeifInterlockneeded(struct PORTCONTROL * PORT)
+{
+ // Can we just call SuspendOtherPorts - it won't do any harm if already suspended
+ // No, at that needs a TNC Record, so duplicate code here
+
+ int i;
+ int Interlock = PORT->PORTINTERLOCK;
+ struct TNCINFO * TNC;
+
+ if (Interlock == 0)
+ return 0; // No locking
+
+ for (i = 1; i <= MAXBPQPORTS; i++)
+ {
+ TNC = TNCInfo[i];
+
+ if (TNC)
+ if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group
+ if (TNC->SuspendPortProc && TNC->PortRecord->PORTCONTROL.PortSuspended == FALSE)
+ TNC->SuspendPortProc(TNC, TNC);
+ }
+
+ return 0;
+}
+
+int seeifUnlockneeded(struct _LINKTABLE * LINK)
+{
+ // We need to see if any other links are active on any interlocked KISS ports. If not, release the lock
+
+ int i;
+ int links = 0;
+
+ int Interlock;
+ struct TNCINFO * TNC;
+ struct PORTCONTROL * PORT = LINK->LINKPORT;
+
+ if (PORT == NULL)
+ return 0;
+
+ // Should only be called for KISS links, but just in case
+
+ if (PORT->PORTTYPE > 12) // INTERNAL or EXTERNAL?
+ return 0; // Not KISS Port
+
+ Interlock = PORT->PORTINTERLOCK;
+
+ if (Interlock == 0)
+ return 0; // No locking
+
+
+ // Count all L2 links on interlocked KISS ports
+
+ PORT = PORTTABLE;
+
+ while(PORT)
+ {
+ if (PORT->PORTTYPE <= 12) // INTERNAL or EXTERNAL?
+ if (Interlock == PORT->PORTINTERLOCK)
+ links += COUNTLINKS(PORT->PORTNUMBER);
+
+ PORT = PORT->PORTPOINTER;
+ }
+
+ if (links > 1) // must be the one we are closing
+ return 0; // Keep lock
+
+
+ for (i = 1; i <= MAXBPQPORTS; i++)
+ {
+ TNC = TNCInfo[i];
+
+ if (TNC)
+ if (Interlock == TNC->RXRadio || Interlock == TNC->TXRadio) // Same Group
+ if (TNC->ReleasePortProc && TNC->PortRecord->PORTCONTROL.PortSuspended == TRUE)
+ TNC->ReleasePortProc(TNC, TNC);
+ }
+
+ return 0;
+}
+
+
+
+
diff --git a/L4Code-skigdebian.c b/L4Code-skigdebian.c
new file mode 100644
index 0000000..59fc0e2
--- /dev/null
+++ b/L4Code-skigdebian.c
@@ -0,0 +1,2416 @@
+/*
+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
+*/
+
+//
+// C replacement for L4Code.asm
+//
+#define Kernel
+
+#define _CRT_SECURE_NO_DEPRECATE
+
+
+#pragma data_seg("_BPQDATA")
+
+#include "time.h"
+#include "stdio.h"
+#include
+
+#include "CHeaders.h"
+#include "tncinfo.h"
+
+extern BPQVECSTRUC BPQHOSTVECTOR[];
+#define BPQHOSTSTREAMS 64
+#define IPHOSTVECTOR BPQHOSTVECTOR[BPQHOSTSTREAMS + 3]
+
+VOID CLOSECURRENTSESSION(TRANSPORTENTRY * Session);
+VOID SENDL4DISC(TRANSPORTENTRY * Session);
+int C_Q_COUNT(VOID * Q);
+TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK);
+VOID InformPartner(struct _LINKTABLE * LINK, int Reason);
+VOID IFRM150(TRANSPORTENTRY * Session, PDATAMESSAGE Buffer);
+VOID SendConNAK(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG);
+BOOL FINDCIRCUIT(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY ** REQL4, int * NewIndex);
+int GETBUSYBIT(TRANSPORTENTRY * L4);
+BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions);
+VOID SETUPNEWCIRCUIT(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG,
+ TRANSPORTENTRY * L4, char * BPQPARAMS, int ApplMask, int * BPQNODE);
+extern char * ALIASPTR;
+VOID SendConACK(struct _LINKTABLE * LINK, TRANSPORTENTRY * L4, L3MESSAGEBUFFER * L3MSG, BOOL BPQNODE, UINT Applmask, UCHAR * ApplCall);
+VOID L3SWAPADDRESSES(L3MESSAGEBUFFER * L3MSG);
+VOID L4TIMEOUT(TRANSPORTENTRY * L4);
+struct DEST_LIST * CHECKL3TABLES(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg);
+int CHECKIFBUSYL4(TRANSPORTENTRY * L4);
+VOID AUTOTIMER();
+VOID NRRecordRoute(UCHAR * Buff, int Len);
+VOID REFRESHROUTE(TRANSPORTENTRY * Session);
+VOID ACKFRAMES(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY * L4, int NR);
+VOID SENDL4IACK(TRANSPORTENTRY * Session);
+VOID CHECKNEIGHBOUR(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg);
+VOID ProcessINP3RIF(struct ROUTE * Route, UCHAR * ptr1, int msglen, int Port);
+VOID ProcessRTTMsg(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Buff, int Len, int Port);
+VOID FRAMEFORUS(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, int ApplMask, UCHAR * ApplCall);
+void WriteConnectLog(char * fromCall, char * toCall, UCHAR * Mode);
+void SendVARANetromMsg(struct TNCINFO * TNC, PL3MESSAGEBUFFER MSG);
+
+extern UINT APPLMASK;
+
+extern BOOL LogL4Connects;
+extern BOOL LogAllConnects;
+
+// L4 Flags Values
+
+#define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED
+
+extern APPLCALLS * APPL;
+
+VOID NETROMMSG(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG)
+{
+ // MAKE SURE PID IS 0CF - IN CASE SOMEONE IS SENDING L2 STUFF ON WHAT
+ // WE THINK IS A _NODE-_NODE LINK
+
+ struct DEST_LIST * DEST;
+
+ int n;
+
+ if (L3MSG->L3PID != 0xCF)
+ {
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+
+ if (LINK->NEIGHBOUR == 0)
+ {
+ // NO ROUTE ASSOCIATED WITH THIS CIRCUIT - SET ONE UP
+
+ CHECKNEIGHBOUR(LINK, L3MSG);
+
+ if (LINK->NEIGHBOUR == 0)
+ {
+ // COULDNT SET UP NEIGHBOUR - CAN ONLY THROW IT AWAY
+
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+ }
+
+ // See if a INP3 RIF (first Byte 0xFF)
+
+ if (L3MSG->L3SRCE[0] == 0xff)
+ {
+ // INP3
+
+ ProcessINP3RIF(LINK->NEIGHBOUR, &L3MSG->L3SRCE[1], L3MSG->LENGTH - (MSGHDDRLEN + 2), L3MSG->Port); // = 2 = PID + FF Flag
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+
+ APPLMASK = 0; // NOT APPLICATION
+
+ if (NODE) // _NODE SUPPORT INCLUDED?
+ {
+
+ if (CompareCalls(L3MSG->L3DEST, MYCALL))
+ {
+ FRAMEFORUS(LINK, L3MSG, APPLMASK, MYCALL);
+ return;
+ }
+ }
+
+ // CHECK ALL L4 CALLS
+
+ APPLMASK = 1;
+ ALIASPTR = &CMDALIAS[0][0];
+
+ n = NumberofAppls;
+
+ APPL = APPLCALLTABLE;
+
+ while (n--)
+ {
+ if (APPL->APPLCALL[0] > 0x40) // Valid ax.25 addr
+ {
+ if (CompareCalls(L3MSG->L3DEST, APPL->APPLCALL))
+ {
+ FRAMEFORUS(LINK, L3MSG, APPLMASK, APPL->APPLCALL);
+ return;
+ }
+ }
+ APPLMASK <<= 1;
+ ALIASPTR += ALIASLEN;
+ APPL++;
+ }
+
+ // IS IT INP3 (L3RTT)
+
+ if (CompareCalls(L3MSG->L3DEST, L3RTT))
+ {
+ ProcessRTTMsg(LINK->NEIGHBOUR, L3MSG, L3MSG->LENGTH, L3MSG->Port);
+ return;
+ }
+
+ L3MSG->L3TTL--;
+
+ if (L3MSG->L3TTL == 0)
+ {
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+
+ // If it is a record route frame we should add our call to the list before sending on
+
+ if (L3MSG->L4FLAGS == 0 && L3MSG->L4ID == 1 && L3MSG->L4INDEX == 0)
+ {
+ // Add our call on end, and increase count
+
+ int Len = L3MSG->LENGTH;
+ int Count;
+
+ UCHAR * ptr = (UCHAR *)L3MSG;
+
+ if (Len < 248)
+ {
+ ptr += (Len - 1);
+
+ Count = (*(ptr++)) & 0x7F; // Mask End of Route
+
+ memcpy(ptr, MYCALL, 7);
+
+ ptr += 7;
+
+ Count--;
+ *(ptr) = Count;
+
+ if (Count)
+ L3MSG->LENGTH += 8;
+ }
+ }
+
+ if (L3MSG->L3TTL > L3LIVES)
+ L3MSG->L3TTL = L3LIVES; // ENFORCE LIMIT ON ALL FRAMES SENT
+
+ if (FindDestination(L3MSG->L3DEST, &DEST) == 0)
+ {
+ ReleaseBuffer(L3MSG); // CANT FIND DESTINATION
+ return;
+ }
+
+ // IF MESSAGE ORIGINTED HERE, THERE MUST BE A ROUTING LOOP -
+ // THERE IS LITTLE POINT SENDING IT OVER THE SAME ROUTE AGAIN,
+ // SO SET ANOTHER ROUTE ACTIVE IF POSSIBLE
+
+ if (CompareCalls(L3MSG->L3SRCE, MYCALL) || CompareCalls(L3MSG->L3SRCE, APPLCALLTABLE->APPLCALL))
+ {
+ // MESSAGE HAS COME BACK TO ITS STARTING POINT - ACTIVATE ANOTHER ROUTE,
+ // UNLESS THERE IS ONLY ONE, IN WHICH CASE DISCARD IT
+
+ if (DEST->NRROUTE[1].ROUT_NEIGHBOUR == 0) // No more routes
+ {
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+
+ DEST->DEST_ROUTE++;
+
+ if (DEST->DEST_ROUTE == 4) // TO NEXT
+ DEST->DEST_ROUTE = 1; // TRY TO ACTIVATE FIRST
+ }
+
+ // IF CURRENT ROUTE IS BACK THE WAY WE CAME, THEN ACTIVATE
+ //ANOTHER (IF POSSIBLE).
+
+ if (DEST->DEST_ROUTE)
+ {
+ if (DEST->NRROUTE[DEST->DEST_ROUTE -1].ROUT_NEIGHBOUR == LINK->NEIGHBOUR)
+ {
+ // Current ROUTE IS BACK THE WAY WE CAME - ACTIVATE ANOTHER IF POSSIBLE
+
+ DEST->DEST_ROUTE++;
+ if (DEST->DEST_ROUTE == 4)
+ DEST->DEST_ROUTE =1;
+ }
+ goto NO_PROBLEM;
+ }
+ else
+ {
+ // DONT HAVE AN ACTIVE ROUTE
+
+ if (DEST->NRROUTE[0].ROUT_NEIGHBOUR == LINK->NEIGHBOUR)
+ {
+ // FIRST ROUTE IS BACK THE WAY WE CAME - ACTIVATE ANOTHER IF POSSIBLE
+
+ DEST->DEST_ROUTE = 2; // WILL BE RESET BY L3 CODE IF THERE IS NOT OTHER ROUTE
+ }
+ }
+
+NO_PROBLEM:
+
+ CHECKL3TABLES(LINK, L3MSG);
+
+// EVEN IF WE CANT PUT ORIGINATING NODE INTO OUR TABLES, PASS MSG ON
+// ANYWAY - THE FINAL TARGET MAY HAVE ANOTHER WAY BACK
+
+
+ C_Q_ADD(&DEST->DEST_Q, L3MSG);
+
+ L3FRAMES++;
+}
+
+VOID SENDL4MESSAGE(TRANSPORTENTRY * L4, struct DATAMESSAGE * Msg)
+{
+ L3MESSAGEBUFFER * L3MSG;
+ struct DEST_LIST * DEST;
+ struct DATAMESSAGE * Copy;
+ int FRAGFLAG = 0;
+ int Len;
+
+ // These make it simpler to understand code
+
+#define NullPKTLen 4 + sizeof(void *) // 4 is Port, Len, PID
+#define MaxL4Len 236 + 4 + sizeof(void *) // Max NETROM Size
+
+
+ if (Msg->LENGTH == NullPKTLen)
+ {
+ // NO DATA - DISCARD IT
+
+ ReleaseBuffer(Msg);
+ return;
+ }
+
+ L3MSG = GetBuff();
+
+ if (L3MSG == 0)
+ {
+ // DONT THINK WE SHOULD GET HERE, UNLESS _QCOUNT IS CORRUPT,
+ // BUT IF WE DO, SHOULD RETURN MSG TO FREE Q - START TIMER, AND
+ // DROP THROUGH TO RELBUFF
+
+ L4->L4TIMER = L4->SESSIONT1;
+
+ ReleaseBuffer(Msg);
+ return;
+ }
+
+ L3MSG->L3PID = 0xCF; // NET MESSAGE
+
+ memcpy(L3MSG->L3SRCE, L4->L4MYCALL, 7);
+
+ DEST = L4->L4TARGET.DEST;
+ memcpy(L3MSG->L3DEST, DEST->DEST_CALL, 7);
+
+ L3MSG->L3TTL = L3LIVES;
+
+ L3MSG->L4ID = L4->FARID;
+ L3MSG->L4INDEX = L4->FARINDEX;
+
+ L3MSG->L4TXNO = L4->TXSEQNO;
+
+ // SET UP RTT TIMER
+
+ if (L4->RTT_TIMER == 0)
+ {
+ L4->RTT_SEQ = L4->TXSEQNO;
+
+ L4->RTT_TIMER = GetTickCount();
+ }
+
+ L4->TXSEQNO++;
+
+
+ L4->L4LASTACKED = L3MSG->L4RXNO = L4->RXSEQNO; // SAVE LAST NUMBER ACKED
+
+ // SEE IF CROSSSESSION IS BUSY
+
+ GETBUSYBIT(L4); // Sets BUSY in NAKBITS if Busy
+
+ L3MSG->L4FLAGS = L4INFO | L4->NAKBITS;
+
+ L4->L4TIMER = L4->SESSIONT1; // SET TIMER
+ L4->L4ACKREQ = 0; // CANCEL ACK NEEDED
+
+ Len = Msg->LENGTH;
+
+ if (Len > MaxL4Len) // 236 DATA + 8 HEADER
+ {
+ // MUST FRAGMENT MESSAGE
+
+ L3MSG->L4FLAGS |= L4MORE;
+ FRAGFLAG = 1;
+
+ Len = MaxL4Len;
+ }
+
+ Len += 20; // L3/4 Header
+
+ L3MSG->LENGTH = Len;
+
+ Len -= (20 + NullPKTLen); // Actual Data
+
+ memcpy(L3MSG->L4DATA, Msg->L2DATA, Len);
+
+ // CREATE COPY FOR POSSIBLE RETRY
+
+ Copy = GetBuff();
+
+ if (Copy == 0)
+ {
+ // SHOULD NEVER HAPPEN
+
+ ReleaseBuffer(Msg);
+ return;
+ }
+
+ memcpy(Copy, L3MSG, L3MSG->LENGTH);
+
+ // If we have fragmented, we should adjust length, or retry will send too much
+ // (bug in .asm code)
+
+ if (FRAGFLAG)
+ Copy->LENGTH = MaxL4Len;
+
+ C_Q_ADD(&L4->L4HOLD_Q, Copy);
+
+ C_Q_ADD(&DEST->DEST_Q, L3MSG);
+
+ DEST->DEST_COUNT++; // COUNT THEM
+
+ L4FRAMESTX++;
+
+ if (FRAGFLAG)
+ {
+ // MESSAGE WAS TOO BIG - ADJUST IT AND LOOP BACK
+
+ Msg->LENGTH -= 236;
+
+ memmove(Msg->L2DATA, &Msg->L2DATA[236], Msg->LENGTH - NullPKTLen);
+
+ SENDL4MESSAGE(L4, Msg);
+ }
+}
+
+
+int GETBUSYBIT(TRANSPORTENTRY * L4)
+{
+ // SEE IF CROSSSESSION IS BUSY
+
+ L4->NAKBITS &= ~L4BUSY; // Clear busy
+
+ L4->NAKBITS |= CHECKIFBUSYL4(L4); // RETURNS AL WITH BUSY BIT SET IF CROSSSESSION IS BUSY
+
+ return L4->NAKBITS;
+}
+
+VOID Q_IP_MSG(MESSAGE * Buffer)
+{
+ if (IPHOSTVECTOR.HOSTAPPLFLAGS & 0x80)
+ {
+ // CHECK WE ARENT USING TOO MANY BUFFERS
+
+ if (C_Q_COUNT(&IPHOSTVECTOR.HOSTTRACEQ) > 20)
+ ReleaseBuffer(Q_REM((void *)&IPHOSTVECTOR.HOSTTRACEQ));
+
+ C_Q_ADD(&IPHOSTVECTOR.HOSTTRACEQ, Buffer);
+ return;
+ }
+
+ ReleaseBuffer(Buffer);
+}
+
+VOID SENDL4CONNECT(TRANSPORTENTRY * Session)
+{
+ PL3MESSAGEBUFFER MSG = (PL3MESSAGEBUFFER)GetBuff();
+ struct DEST_LIST * DEST = Session->L4TARGET.DEST;
+
+ if (MSG == NULL)
+ return;
+
+ if (DEST->DEST_CALL[0] == 0)
+ {
+ Debugprintf("Trying to send L4CREQ to NULL Destination");
+ ReleaseBuffer(MSG);
+ return;
+ }
+
+ MSG->L3PID = 0xCF; // NET MESSAGE
+
+ memcpy(MSG->L3SRCE, Session->L4MYCALL, 7);
+ memcpy(MSG->L3DEST, DEST->DEST_CALL, 7);
+
+ MSG->L3TTL = L3LIVES;
+
+ MSG->L4INDEX = Session->CIRCUITINDEX;
+ MSG->L4ID = Session->CIRCUITID;
+ MSG->L4TXNO = 0;
+ MSG->L4RXNO = 0;
+ MSG->L4FLAGS = L4CREQ;
+
+ MSG->L4DATA[0] = L4DEFAULTWINDOW; // PROPOSED WINDOW
+
+ memcpy(&MSG->L4DATA[1], Session->L4USER, 7); // ORIG CALL
+ memcpy(&MSG->L4DATA[8], Session->L4MYCALL, 7);
+
+ Session->L4TIMER = Session->SESSIONT1; // START TIMER
+ memcpy(&MSG->L4DATA[15], &Session->SESSIONT1, 2); // AND PUT IN MSG
+
+ MSG->LENGTH = (int)(&MSG->L4DATA[17] - (UCHAR *)MSG);
+
+ if (Session->SPYFLAG)
+ {
+ MSG->L4DATA[17] = 'Z'; // ADD SPY ON BBS FLAG
+ MSG->LENGTH++;
+ }
+ C_Q_ADD(&DEST->DEST_Q, (UINT *)MSG);
+}
+
+void RETURNEDTONODE(TRANSPORTENTRY * Session)
+{
+ // SEND RETURNED TO ALIAS:CALL
+
+ struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)GetBuff();
+ char Nodename[20];
+
+ if (Msg)
+ {
+ Msg->PID = 0xf0;
+
+ Nodename[DecodeNodeName(MYCALLWITHALIAS, Nodename)] = 0; // null terminate
+
+ Msg->LENGTH = (USHORT)sprintf(&Msg->L2DATA[0], "Returned to Node %s\r", Nodename) + 4 + sizeof(void *);
+ C_Q_ADD(&Session->L4TX_Q, (UINT *)Msg);
+ PostDataAvailable(Session);
+ }
+}
+
+
+extern void * BUFFER;
+
+VOID L4BG()
+{
+ // PROCESS DATA QUEUED ON SESSIONS
+
+ int n = MAXCIRCUITS;
+ TRANSPORTENTRY * L4 = L4TABLE;
+ int MaxLinks = MAXLINKS;
+ UCHAR Outstanding;
+ struct DATAMESSAGE * Msg;
+ struct PORTCONTROL * PORT;
+ struct _LINKTABLE * LINK;
+ int Msglen, Paclen;
+
+ while (n--)
+ {
+ if (L4->L4USER[0] == 0)
+ {
+ L4++;
+ continue;
+ }
+ while (L4->L4TX_Q)
+ {
+ if (L4->L4CIRCUITTYPE & BPQHOST)
+ break; // Leave on TXQ
+
+ // SEE IF BUSY - NEED DIFFERENT TESTS FOR EACH SESSION TYPE
+
+ if (L4->L4CIRCUITTYPE & SESSION)
+ {
+ // L4 SESSION - WILL NEED BUFFERS FOR SAVING COPY,
+ // AND POSSIBLY FRAGMENTATION
+
+ if (QCOUNT < 15)
+ break;
+
+ if (L4->FLAGS & L4BUSY)
+ {
+ // CHOKED - MAKE SURE TIMER IS RUNNING
+
+ if (L4->L4TIMER == 0)
+ L4->L4TIMER = L4->SESSIONT1;
+
+ break;
+ }
+
+ // CHECK WINDOW
+
+ Outstanding = L4->TXSEQNO - L4->L4WS; // LAST FRAME ACKED - GIVES NUMBER OUTSTANING
+
+ // MOD 256, SO SHOULD HANDLE WRAP??
+
+ if (Outstanding > L4->L4WINDOW)
+ break;
+
+ }
+ else if (L4->L4CIRCUITTYPE & L2LINK)
+ {
+ // L2 LINK
+
+ LINK = L4->L4TARGET.LINK;
+
+ if (COUNT_AT_L2(LINK) > 8)
+ break;
+ }
+
+ // Not busy, so continue
+
+ L4->L4KILLTIMER = 0; //RESET SESSION TIMEOUTS
+
+ if(L4->L4CROSSLINK)
+ L4->L4CROSSLINK->L4KILLTIMER = 0;
+
+ Msg = Q_REM((void *)&L4->L4TX_Q);
+
+ if (L4->L4CIRCUITTYPE & PACTOR)
+ {
+ // PACTOR-like - queue to Port
+
+ // Stream Number is in KAMSESSION
+
+ Msg->PORT = L4->KAMSESSION;
+ PORT = L4->L4TARGET.PORT;
+
+ C_Q_ADD(&PORT->PORTTX_Q, Msg);
+
+ continue;
+ }
+ // non-pactor
+
+ // IF CROSSLINK, QUEUE TO NEIGHBOUR, ELSE QUEUE ON LINK ENTRY
+
+ if (L4->L4CIRCUITTYPE & SESSION)
+ {
+ SENDL4MESSAGE(L4, Msg);
+ ReleaseBuffer(Msg);
+ continue;
+ }
+
+ LINK = L4->L4TARGET.LINK;
+
+ // If we want to enforce PACLEN this may be a good place to do it
+
+ Msglen = Msg->LENGTH - (MSGHDDRLEN + 1); //Dont include PID
+ Paclen = L4->SESSPACLEN;
+
+ if (Paclen == 0)
+ Paclen = 256;
+
+ if (Msglen > Paclen)
+ {
+ // Fragment it.
+ // Is it best to send Paclen packets then short or equal length?
+ // I think equal length;
+
+ int Fragments = (Msglen + Paclen - 1) / Paclen;
+ int Fraglen = Msglen / Fragments;
+
+ if ((Msglen & 1)) // Odd
+ Fraglen ++;
+
+ while (Msglen > Fraglen)
+ {
+ PDATAMESSAGE Fragment = GetBuff();
+
+ if (Fragment == NULL)
+ break; // Cant do much else
+
+ Fragment->PORT = Msg->PORT;
+ Fragment->PID = Msg->PID;
+ Fragment->LENGTH = Fraglen + (MSGHDDRLEN + 1);
+
+ memcpy(Fragment->L2DATA, Msg->L2DATA, Fraglen);
+
+ C_Q_ADD(&LINK->TX_Q, Fragment);
+
+ memcpy(Msg->L2DATA, &Msg->L2DATA[Fraglen], Msglen - Fraglen);
+ Msglen -= Fraglen;
+ Msg->LENGTH -= Fraglen;
+ }
+
+ // Drop through to send last bit
+
+ }
+
+ C_Q_ADD(&LINK->TX_Q, Msg);
+ }
+
+ // if nothing on TX Queue If there is stuff on hold queue, timer must be running
+
+// if (L4->L4TX_Q == 0 && L4->L4HOLD_Q)
+ if (L4->L4HOLD_Q)
+ {
+ if (L4->L4TIMER == 0)
+ {
+ L4->L4TIMER = L4->SESSIONT1;
+ }
+ }
+
+ // now check for rxed frames
+
+ while(L4->L4RX_Q)
+ {
+ Msg = Q_REM((void *)&L4->L4RX_Q);
+
+ IFRM150(L4, Msg);
+
+ if (L4->L4USER[0] == 0) // HAVE JUST CLOSED SESSION!
+ goto NextSess;
+ }
+
+ // IF ACK IS PENDING, AND WE ARE AT RX WINDOW, SEND ACK NOW
+
+ Outstanding = L4->RXSEQNO - L4->L4LASTACKED;
+ if (Outstanding >= L4->L4WINDOW)
+ SENDL4IACK(L4);
+NextSess:
+ L4++;
+ }
+}
+
+VOID CLEARSESSIONENTRY(TRANSPORTENTRY * Session)
+{
+
+ // RETURN ANY QUEUED BUFFERS TO FREE QUEUE
+
+ while (Session->L4TX_Q)
+ ReleaseBuffer(Q_REM((void *)&Session->L4TX_Q));
+
+ while (Session->L4RX_Q)
+ ReleaseBuffer(Q_REM((void *)&Session->L4RX_Q));
+
+ while (Session->L4HOLD_Q)
+ ReleaseBuffer(Q_REM((void *)&Session->L4HOLD_Q));
+
+ if (C_Q_COUNT(&Session->L4RESEQ_Q) > Session->L4WINDOW)
+ {
+ Debugprintf("Corrupt RESEQ_Q Q Len %d Free Buffs %d", C_Q_COUNT(&Session->L4RESEQ_Q), QCOUNT);
+ Session->L4RESEQ_Q = 0;
+ }
+
+ while (Session->L4RESEQ_Q)
+ ReleaseBuffer(Q_REM((void *)&Session->L4RESEQ_Q));
+
+ if (Session->PARTCMDBUFFER)
+ ReleaseBuffer(Session->PARTCMDBUFFER);
+
+ memset(Session, 0, sizeof(TRANSPORTENTRY));
+}
+
+VOID CloseSessionPartner(TRANSPORTENTRY * Session)
+{
+ // SEND CLOSE TO CROSSLINKED SESSION AND CLEAR LOCAL SESSION
+
+ if (Session == NULL)
+ return;
+
+ if (Session->L4CROSSLINK)
+ CLOSECURRENTSESSION(Session->L4CROSSLINK);
+
+ CLEARSESSIONENTRY(Session);
+}
+
+
+VOID CLOSECURRENTSESSION(TRANSPORTENTRY * Session)
+{
+ MESSAGE * Buffer;
+ struct _LINKTABLE * LINK;
+
+// SHUT DOWN SESSION AND UNLINK IF CROSSLINKED
+
+ if (Session == NULL)
+ return;
+
+ Session->L4CROSSLINK = NULL;
+
+ // IF STAY FLAG SET, KEEP SESSION, AND SEND MESSAGE
+
+ if (Session->STAYFLAG)
+ {
+ RETURNEDTONODE(Session);
+ Session->STAYFLAG = 0; // Only do it once
+ return;
+ }
+
+ if (Session->L4CIRCUITTYPE & BPQHOST)
+ {
+ // BPQ HOST MODE SESSION - INDICATE STATUS CHANGE
+
+ PBPQVECSTRUC HOST = Session->L4TARGET.HOST;
+ HOST->HOSTSESSION = 0;
+ HOST->HOSTFLAGS |= 3; /// State Change
+
+ PostStateChange(Session);
+ CLEARSESSIONENTRY(Session);
+ return;
+ }
+
+ if (Session->L4CIRCUITTYPE & PACTOR)
+ {
+ // PACTOR-type (Session linked to Port)
+
+ struct _EXTPORTDATA * EXTPORT = Session->L4TARGET.EXTPORT;
+
+ // If any data is queued, move it to the port entry, so it can be sent before the disconnect
+
+ while (Session->L4TX_Q)
+ {
+ Buffer = Q_REM((void *)&Session->L4TX_Q);
+ EXTPORT->PORTCONTROL.PORTTXROUTINE(EXTPORT, Buffer);
+ }
+
+ EXTPORT->ATTACHEDSESSIONS[Session->KAMSESSION] = NULL;
+
+ CLEARSESSIONENTRY(Session);
+ return;
+ }
+
+ if (Session->L4CIRCUITTYPE & SESSION)
+ {
+ // L4 SESSION TO CLOSE
+
+ if (Session->L4HOLD_Q || Session->L4TX_Q) // WAITING FOR ACK or MORE TO SEND - SEND DISC LATER
+ {
+ Session->FLAGS |= DISCPENDING; // SEND DISC WHEN ALL DATA ACKED
+ return;
+ }
+
+ SENDL4DISC(Session);
+ return;
+ }
+
+ // Must be LEVEL 2 SESSION TO CLOSE
+
+ // COPY ANY PENDING DATA TO L2 TX Q, THEN GET RID OF SESSION
+
+ LINK = Session->L4TARGET.LINK;
+
+ if (LINK == NULL) // just in case!
+ {
+ CLEARSESSIONENTRY(Session);
+ return;
+ }
+
+ while (Session->L4TX_Q)
+ {
+ Buffer = Q_REM((void *)&Session->L4TX_Q);
+ C_Q_ADD(&LINK->TX_Q, Buffer);
+ }
+
+ // NOTHING LEFT AT SESSION LEVEL
+
+ LINK->CIRCUITPOINTER = NULL; // CLEAR REVERSE LINK
+
+ if ((LINK->LINKWS != LINK->LINKNS) || LINK->TX_Q)
+ {
+ // STILL MORE TO SEND - SEND DISC LATER
+
+ LINK->L2FLAGS |= DISCPENDING; // SEND DISC WHEN ALL DATA ACKED
+ }
+ else
+ {
+ // NOTHING QUEUED - CAN SEND DISC NOW
+
+ LINK->L2STATE = 4; // DISCONNECTING
+ LINK->L2TIMER = 1; // USE TIMER TO KICK OFF DISC
+ }
+
+ CLEARSESSIONENTRY(Session);
+
+}
+
+VOID L4TimerProc()
+{
+ // CHECK FOR TIMER EXPIRY
+
+ int n = MAXCIRCUITS;
+ TRANSPORTENTRY * L4 = L4TABLE;
+ TRANSPORTENTRY * Partner;
+ int MaxLinks = MAXLINKS;
+
+ while (n--)
+ {
+ if (L4->L4USER[0] == 0)
+ {
+ L4++;
+ continue;
+ }
+
+ // CHECK FOR L4BUSY SET AND NO LONGER BUSY
+
+ if (L4->NAKBITS & L4BUSY)
+ {
+ if ((CHECKIFBUSYL4(L4) & L4BUSY) == 0)
+ {
+ // no longer busy
+
+ L4->NAKBITS &= ~L4BUSY; // Clear busy
+ L4->L4ACKREQ = 1; // SEND ACK
+ }
+ }
+
+ if (L4->L4TIMER) // Timer Running?
+ {
+ L4->L4TIMER--;
+ if (L4->L4TIMER == 0) // Expired
+ L4TIMEOUT(L4);
+ }
+
+ if (L4->L4ACKREQ) // DELAYED ACK Timer Running?
+ {
+ L4->L4ACKREQ--;
+ if (L4->L4ACKREQ == 0) // Expired
+ SENDL4IACK(L4);
+ }
+
+ L4->L4KILLTIMER++;
+
+ // IF BIT 6 OF APPLFLAGS SET, SEND MSG EVERY 11 MINS TO KEEP SESSION OPEN
+
+ if (L4->L4CROSSLINK) // CONNECTED?
+ if (L4->SESS_APPLFLAGS & 0x40)
+ if (L4->L4KILLTIMER > 11 * 60)
+ AUTOTIMER(L4);
+
+ if (L4->L4LIMIT == 0)
+ L4->L4LIMIT = L4LIMIT;
+ else
+ {
+ if (L4->L4KILLTIMER > L4->L4LIMIT)
+ {
+ L4->L4KILLTIMER = 0;
+
+ // CLOSE THIS SESSION, AND ITS PARTNER (IF ANY)
+
+ L4->STAYFLAG = 0;
+
+ Partner = L4->L4CROSSLINK;
+ CLOSECURRENTSESSION(L4);
+
+ if (Partner)
+ {
+ Partner->L4KILLTIMER = 0; //ITS TIMES IS ALSO ABOUT TO EXPIRE
+ CLOSECURRENTSESSION(Partner); // CLOSE THIS ONE
+ }
+ }
+ }
+ L4++;
+ }
+}
+
+VOID L4TIMEOUT(TRANSPORTENTRY * L4)
+{
+ // TIMER EXPIRED
+
+ // IF LINK UP REPEAT TEXT
+ // IF S2, REPEAT CONNECT REQUEST
+ // IF S4, REPEAT DISCONNECT REQUEST
+
+ struct DATAMESSAGE * Msg;
+ struct DATAMESSAGE * Copy;
+ struct DEST_LIST * DEST;
+
+ if (L4->L4STATE < 2)
+ return;
+
+ if (L4->L4STATE == 2)
+ {
+ // RETRY CONNECT
+
+ L4->L4RETRIES++;
+
+ if (L4->L4RETRIES > L4N2)
+ {
+ // RETRIED N2 TIMES - FAIL LINK
+
+ L4CONNECTFAILED(L4); // TELL OTHER PARTNER IT FAILED
+ CLEARSESSIONENTRY(L4);
+ return;
+ }
+
+ Debugprintf("Retrying L4 Connect Request");
+
+ SENDL4CONNECT(L4); // Resend connect
+ return;
+ }
+
+ if (L4->L4STATE == 4)
+ {
+ // RETRY DISCONNECT
+
+ L4->L4RETRIES++;
+
+ if (L4->L4RETRIES > L4N2)
+ {
+ // RETRIED N2 TIMES - FAIL LINK
+
+
+ CLEARSESSIONENTRY(L4);
+ return;
+ }
+
+ SENDL4DISC(L4); // Resend connect
+ return;
+ }
+ // STATE 5 OR ABOVE - RETRY INFO
+
+
+ L4->FLAGS &= ~L4BUSY; // CANCEL CHOKE
+
+ L4->L4RETRIES++;
+
+ if (L4->L4RETRIES > L4N2)
+ {
+ // RETRIED N2 TIMES - FAIL LINK
+
+ CloseSessionPartner(L4); // SEND CLOSE TO PARTNER (IF PRESENT)
+ return;
+ }
+
+ // RESEND ALL OUTSTANDING FRAMES
+
+ L4->FLAGS &= 0x7F; // CLEAR CHOKED
+
+ Msg = L4->L4HOLD_Q;
+
+ while (Msg)
+ {
+ Copy = GetBuff();
+
+ if (Copy == 0)
+ return;
+
+ memcpy(Copy, Msg, Msg->LENGTH);
+
+ DEST = L4->L4TARGET.DEST;
+
+ C_Q_ADD(&DEST->DEST_Q, Copy);
+
+ L4FRAMESRETRIED++;
+
+ Msg = Msg->CHAIN;
+ }
+}
+
+VOID AUTOTIMER(TRANSPORTENTRY * L4)
+{
+ // SEND MESSAGE TO USER TO KEEP CIRCUIT OPEN
+
+ struct DATAMESSAGE * Msg = GetBuff();
+
+ if (Msg == 0)
+ return;
+
+ Msg->PID = 0xf0;
+ Msg->L2DATA[0] = 0;
+ Msg->L2DATA[1] = 0;
+
+ Msg->LENGTH = MSGHDDRLEN + 3;
+
+ C_Q_ADD(&L4->L4TX_Q, Msg);
+
+ PostDataAvailable(L4);
+
+ L4->L4KILLTIMER = 0;
+
+ if (L4->L4CROSSLINK)
+ L4->L4CROSSLINK->L4KILLTIMER = 0;
+}
+
+VOID L4CONNECTFAILED(TRANSPORTENTRY * L4)
+{
+ // CONNECT HAS TIMED OUT - SEND MESSAGE TO OTHER END
+
+ struct DATAMESSAGE * Msg;
+ TRANSPORTENTRY * Partner;
+ UCHAR * ptr1;
+ char Nodename[20];
+ struct DEST_LIST * DEST;
+
+ Partner = L4->L4CROSSLINK;
+
+ if (Partner == 0)
+ return;
+
+ Msg = GetBuff();
+
+ if (Msg == 0)
+ return;
+
+ Msg->PID = 0xf0;
+
+ ptr1 = SetupNodeHeader(Msg);
+
+ DEST = L4->L4TARGET.DEST;
+ Nodename[DecodeNodeName(DEST->DEST_CALL, Nodename)] = 0; // null terminate
+
+ ptr1 += sprintf(ptr1, "Failure with %s\r", Nodename);
+
+ Msg->LENGTH = (int)(ptr1 - (UCHAR *)Msg);
+
+ C_Q_ADD(&Partner->L4TX_Q, Msg);
+
+ PostDataAvailable(Partner);
+
+ Partner->L4CROSSLINK = 0; // Back to command lewel
+}
+
+
+VOID ProcessIframe(struct _LINKTABLE * LINK, PDATAMESSAGE Buffer)
+{
+ // IF UP/DOWN LINK, AND CIRCUIT ESTABLISHED, ADD LEVEL 3/4 HEADERS
+ // (FRAGMENTING IF NECESSARY), AND PASS TO TRANSPORT CONTROL
+ // FOR ESTABLISHED ROUTE
+
+ // IF INTERNODE MESSAGE, PASS TO ROUTE CONTROL
+
+ // IF UP/DOWN, AND NO CIRCUIT, PASS TO COMMAND HANDLER
+
+ TRANSPORTENTRY * Session;
+
+ // IT IS POSSIBLE TO MULTIPLEX NETROM AND IP STUFF ON THE SAME LINK
+
+ if (Buffer->PID == 0xCC || Buffer->PID == 0xCD)
+ {
+ Q_IP_MSG((MESSAGE *)Buffer);
+ return;
+ }
+
+ if (Buffer->PID == 0xCF)
+ {
+ // INTERNODE frame
+
+ // IF LINKTYPE IS NOT 3, MUST CHECK IF WE HAVE ACCIDENTALLY ATTACHED A BBS PORT TO THE NODE
+
+ if (LINK->LINKTYPE != 3)
+ {
+ if (LINK->CIRCUITPOINTER)
+ {
+ // MUST KILL SESSION
+
+ InformPartner(LINK, NORMALCLOSE); // CLOSE IT
+ LINK->CIRCUITPOINTER = NULL; // AND UNHOOK
+ }
+ LINK->LINKTYPE = 3; // NOW WE KNOW ITS A CROSSLINK
+ }
+
+ NETROMMSG(LINK, (L3MESSAGEBUFFER *)Buffer);
+ return;
+ }
+
+ if (LINK->LINKTYPE == 3)
+ {
+ // Weve receved a non- netrom frame on an inernode link
+
+ ReleaseBuffer(Buffer);
+ return;
+ }
+
+ if (LINK->CIRCUITPOINTER)
+ {
+ // Pass to Session
+
+ IFRM150(LINK->CIRCUITPOINTER, Buffer);
+ return;
+ }
+
+ // UPLINK MESSAGE WITHOUT LEVEL 4 ENTRY - CREATE ONE
+
+ Session = SetupSessionForL2(LINK);
+
+ if (Session == NULL)
+ return;
+
+ CommandHandler(Session, Buffer);
+ return;
+}
+
+
+VOID IFRM100(struct _LINKTABLE * LINK, PDATAMESSAGE Buffer)
+{
+ TRANSPORTENTRY * Session;
+
+ if (LINK->CIRCUITPOINTER)
+ {
+ // Pass to Session
+
+ IFRM150(LINK->CIRCUITPOINTER, Buffer);
+ return;
+ }
+
+ // UPLINK MESSAGE WITHOUT LEVEL 4 ENTRY - CREATE ONE
+
+ Session = SetupSessionForL2(LINK);
+
+ if (Session == NULL)
+ return;
+
+ CommandHandler(Session, Buffer);
+ return;
+}
+
+
+VOID IFRM150(TRANSPORTENTRY * Session, PDATAMESSAGE Buffer)
+{
+ TRANSPORTENTRY * Partner;
+ struct _LINKTABLE * LINK;
+
+ Session->L4KILLTIMER = 0; // RESET SESSION TIMEOUT
+
+ if (Session->L4CROSSLINK == NULL) // CONNECTED?
+ {
+ // NO, SO PASS TO COMMAND HANDLER
+
+ CommandHandler(Session, Buffer);
+ return;
+ }
+
+ Partner = Session->L4CROSSLINK; // TO SESSION PARTNER
+
+ if (Partner->L4STATE == 5)
+ {
+ C_Q_ADD(&Partner->L4TX_Q, Buffer);
+ PostDataAvailable(Partner);
+ return;
+ }
+
+
+
+ // MESSAGE RECEIVED BEFORE SESSION IS UP - CANCEL SESSION
+ // AND PASS MESSAGE TO COMMAND HANDLER
+
+ if (Partner->L4CIRCUITTYPE & L2LINK) // L2 SESSION?
+ {
+ // MUST CANCEL L2 SESSION
+
+ LINK = Partner->L4TARGET.LINK;
+ LINK->CIRCUITPOINTER = NULL; // CLEAR REVERSE LINK
+
+ LINK->L2STATE = 4; // DISCONNECTING
+ LINK->L2TIMER = 1; // USE TIMER TO KICK OFF DISC
+
+ LINK->L2RETRIES = LINK->LINKPORT->PORTN2 - 2; //ONLY SEND DISC ONCE
+ }
+
+ CLEARSESSIONENTRY(Partner);
+
+ Session->L4CROSSLINK = 0; // CLEAR CROSS LINK
+ CommandHandler(Session, Buffer);
+ return;
+}
+
+
+VOID SENDL4DISC(TRANSPORTENTRY * Session)
+{
+ L3MESSAGEBUFFER * MSG;
+ struct DEST_LIST * DEST = Session->L4TARGET.DEST;
+
+ if (Session->L4STATE < 4)
+ {
+ // CIRCUIT NOT UP OR CLOSING - PROBABLY NOT YET SET UP - JUST ZAP IT
+
+ CLEARSESSIONENTRY(Session);
+ return;
+ }
+
+ Session->L4TIMER = Session->SESSIONT1; // START TIMER
+ Session->L4STATE = 4; // SET DISCONNECTING
+ Session->L4ACKREQ = 0; // CANCEL ACK NEEDED
+
+ MSG = GetBuff();
+
+ if (MSG == NULL)
+ return;
+
+ MSG->L3PID = 0xCF; // NET MESSAGE
+
+ memcpy(MSG->L3SRCE, Session->L4MYCALL, 7);
+ memcpy(MSG->L3DEST, DEST->DEST_CALL, 7);
+ MSG->L3TTL = L3LIVES;
+ MSG->L4INDEX = Session->FARINDEX;
+ MSG->L4ID = Session->FARID;
+ MSG->L4TXNO = 0;
+ MSG->L4FLAGS = L4DREQ;
+
+ MSG->LENGTH = (int)(&MSG->L4DATA[0] - (UCHAR *)MSG);
+
+ C_Q_ADD(&DEST->DEST_Q, (UINT *)MSG);
+}
+
+
+void WriteL4LogLine(UCHAR * mycall, UCHAR * call, UCHAR * node)
+{
+ UCHAR FN[MAX_PATH];
+ FILE * L4LogHandle;
+ time_t T;
+ struct tm * tm;
+
+ char Call1[12], Call2[12], Call3[12];
+
+ char LogMsg[256];
+ int MsgLen;
+
+ Call1[ConvFromAX25(mycall, Call1)] = 0;
+ Call2[ConvFromAX25(call, Call2)] = 0;
+ Call3[ConvFromAX25(node, Call3)] = 0;
+
+
+ T = time(NULL);
+ tm = gmtime(&T);
+
+ sprintf(FN,"%s/L4Log_%02d%02d.txt", BPQDirectory, tm->tm_mon + 1, tm->tm_mday);
+
+ L4LogHandle = fopen(FN, "ab");
+
+ if (L4LogHandle == NULL)
+ return;
+
+ MsgLen = sprintf(LogMsg, "%02d:%02d:%02d Call to %s from %s at Node %s\r\n", tm->tm_hour, tm->tm_min, tm->tm_sec, Call1, Call2, Call3);
+
+ fwrite(LogMsg , 1, MsgLen, L4LogHandle);
+
+ fclose(L4LogHandle);
+}
+
+VOID CONNECTREQUEST(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, UINT ApplMask, UCHAR * ApplCall)
+{
+ // CONNECT REQUEST - SEE IF EXISTING SESSION
+ // IF NOT, GET AND FORMAT SESSION TABLE ENTRY
+ // SEND CONNECT ACK
+
+ // EDI = _BUFFER, EBX = LINK
+
+ TRANSPORTENTRY * L4;
+ int BPQNODE = 0; // NOT ONE OF MINE
+ char BPQPARAMS[10]; // Extended Connect Params from BPQ Node
+ int CONERROR;
+ int Index;
+
+ memcpy(BPQPARAMS, &L4T1, 2); // SET DEFAULT T1 IN CASE NOT FROM ANOTHER BPQ NODE
+
+ BPQPARAMS[2] = 0; // 'SPY' NOT SET
+
+ if (CheckExcludeList(&L3MSG->L4DATA[1]) == 0)
+ {
+ SendConNAK(LINK, L3MSG);
+ return;
+ }
+
+ if (FINDCIRCUIT(L3MSG, &L4, &Index))
+ {
+ // SESSION EXISTS - ASSUME RETRY AND SEND ACK
+
+ SendConACK(LINK, L4, L3MSG, BPQNODE, ApplMask, ApplCall);
+ return;
+ }
+
+
+ if (L4 == 0)
+ {
+ SendConNAK(LINK, L3MSG);
+ return;
+ }
+
+ L4->CIRCUITINDEX = Index;
+
+ SETUPNEWCIRCUIT(LINK, L3MSG, L4, BPQPARAMS, ApplMask, &BPQNODE);
+
+ if (L4->L4TARGET.DEST == 0)
+ {
+ // NODE NOT IN TABLE, AND TABLE FULL - CANCEL IT
+
+ memset(L4, 0, sizeof (TRANSPORTENTRY));
+ SendConNAK(LINK, L3MSG);
+ return;
+ }
+ // IF CONNECT TO APPL, ALLOCATE BBS PORT
+
+ if (ApplMask == 0 || BPQPARAMS[2] == 'Z') // Z is "Spy" Connect
+ {
+ SendConACK(LINK, L4, L3MSG, BPQNODE, ApplMask, ApplCall);
+
+ return;
+ }
+
+ // IF APPL CONNECT, SEE IF APPL HAS AN ALIAS
+
+
+ if (ALIASPTR[0] > ' ')
+ {
+ struct DATAMESSAGE * Msg;
+
+ // ACCEPT THE CONNECT, THEN INVOKE THE ALIAS
+
+ SendConACK(LINK, L4, L3MSG, BPQNODE, ApplMask, ApplCall);
+
+ Msg = GetBuff();
+
+ if (Msg)
+ {
+ Msg->PID = 0xf0;
+ memcpy(Msg->L2DATA, APPL->APPLCMD, 12);
+ Msg->L2DATA[12] = 13;
+ Msg->LENGTH = MSGHDDRLEN + 12 + 2; // 2 for PID and CR
+
+ C_Q_ADD(&L4->L4RX_Q, Msg);
+ return;
+ }
+ }
+
+ if (cATTACHTOBBS(L4, ApplMask, PACLEN, &CONERROR))
+ {
+ SendConACK(LINK, L4, L3MSG, BPQNODE, ApplMask, ApplCall);
+ return;
+ }
+
+ // NO BBS AVAILABLE
+
+ CLEARSESSIONENTRY(L4);
+ SendConNAK(LINK, L3MSG);
+ return;
+}
+
+VOID SendConACK(struct _LINKTABLE * LINK, TRANSPORTENTRY * L4, L3MESSAGEBUFFER * L3MSG, BOOL BPQNODE, UINT Applmask, UCHAR * ApplCall)
+{
+ // SEND CONNECT ACK
+
+ struct TNCINFO * TNC;
+
+ L4CONNECTSIN++;
+
+ L3MSG->L4TXNO = L4->CIRCUITINDEX;
+ L3MSG->L4RXNO = L4->CIRCUITID;
+
+ L3MSG->L4DATA[0] = L4->L4WINDOW; //WINDOW
+
+ L3MSG->L4FLAGS = L4CACK;
+
+ if (LogL4Connects)
+ WriteL4LogLine(ApplCall, L4->L4USER, L3MSG->L3SRCE);
+
+ if (LogAllConnects)
+ {
+ char From[64];
+ char toCall[12], fromCall[12], atCall[12];
+
+ toCall[ConvFromAX25(ApplCall, toCall)] = 0;
+ fromCall[ConvFromAX25(L4->L4USER, fromCall)] = 0;
+ atCall[ConvFromAX25(L3MSG->L3SRCE, atCall)] = 0;
+
+ sprintf(From, "%s at Node %s", fromCall, atCall);
+ WriteConnectLog(From, toCall, "NETROM");
+ }
+
+
+ if (CTEXTLEN && Applmask == 0) // Connects to Node (not application)
+ {
+ struct DATAMESSAGE * Msg;
+ int Totallen = CTEXTLEN;
+ int Paclen= PACLEN;
+ UCHAR * ptr = CTEXTMSG;
+
+ if (Paclen == 0)
+ Paclen = PACLEN;
+
+ while(Totallen)
+ {
+ Msg = GetBuff();
+
+ if (Msg == NULL)
+ break; // No Buffers
+
+ Msg->PID = 0xf0;
+
+ if (Paclen > Totallen)
+ Paclen = Totallen;
+
+ memcpy(Msg->L2DATA, ptr, Paclen);
+ Msg->LENGTH = Paclen + MSGHDDRLEN + 1;
+
+ C_Q_ADD(&L4->L4TX_Q, Msg); // SEND MESSAGE TO CALLER
+ PostDataAvailable(L4);
+ ptr += Paclen;
+ Totallen -= Paclen;
+ }
+ }
+
+ L3SWAPADDRESSES(L3MSG);
+
+ L3MSG->L3TTL = L3LIVES;
+
+ L3MSG->LENGTH = MSGHDDRLEN + 22; // CTL 20 BYTE Header Window
+
+ if (BPQNODE)
+ {
+ L3MSG->L4DATA[1] = L3LIVES; // Our TTL
+ L3MSG->LENGTH++;
+ }
+
+ TNC = LINK->LINKPORT->TNC;
+
+ if (TNC && TNC->NetRomMode)
+ SendVARANetromMsg(TNC, L3MSG);
+ else
+ C_Q_ADD(&LINK->TX_Q, L3MSG);
+}
+
+int FINDCIRCUIT(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY ** REQL4, int * NewIndex)
+{
+ // FIND CIRCUIT FOR AN INCOMING MESSAGE
+
+ TRANSPORTENTRY * L4 = L4TABLE;
+ TRANSPORTENTRY * FIRSTSPARE = NULL;
+ struct DEST_LIST * DEST;
+
+ int Index = 0;
+
+ while (Index < MAXCIRCUITS)
+ {
+ if (L4->L4USER[0] == 0) // Spare
+ {
+ if (FIRSTSPARE == NULL)
+ {
+ FIRSTSPARE = L4;
+ *NewIndex = Index;
+ }
+
+ L4++;
+ Index++;
+ continue;
+ }
+
+ DEST = L4->L4TARGET.DEST;
+
+ if (DEST == NULL)
+ {
+ // L4 entry without a Dest shouldn't happen. (I don't think!)
+
+ char Call1[12], Call2[12];
+
+ Call1[ConvFromAX25(L4->L4USER, Call1)] = 0;
+ Call2[ConvFromAX25(L4->L4MYCALL, Call2)] = 0;
+
+ Debugprintf("L4 entry without Target. Type = %02x Calls %s %s",
+ L4->L4CIRCUITTYPE, Call1, Call2);
+
+ L4++;
+ Index++;
+ continue;
+ }
+
+ if (CompareCalls(L3MSG->L3SRCE, DEST->DEST_CALL))
+ {
+ if (L4->FARID == L3MSG->L4ID && L4->FARINDEX == L3MSG->L4INDEX)
+ {
+ // Found it
+
+ *REQL4 = L4;
+ return TRUE;
+ }
+ }
+ L4++;
+ Index++;
+ }
+
+ // ENTRY NOT FOUND - FIRSTSPARE HAS FIRST FREE ENTRY, OR ZERO IF TABLE FULL
+
+ *REQL4 = FIRSTSPARE;
+ return FALSE;
+}
+
+VOID L3SWAPADDRESSES(L3MESSAGEBUFFER * L3MSG)
+{
+ // EXCHANGE ORIGIN AND DEST
+
+ char Temp[7];
+
+ memcpy(Temp, L3MSG->L3SRCE, 7);
+ memcpy(L3MSG->L3SRCE, L3MSG->L3DEST, 7);
+ memcpy(L3MSG->L3DEST, Temp, 7);
+
+ L3MSG->L3DEST[6] &= 0x1E; // Mack EOA and CMD
+ L3MSG->L3SRCE[6] &= 0x1E;
+ L3MSG->L3SRCE[6] |= 1; // Set Last Call
+}
+
+VOID SendConNAK(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG)
+{
+ L3MSG->L4FLAGS = L4CACK | L4BUSY; // REJECT
+ L3MSG->L4DATA[0] = 0; // WINDOW
+
+ L3SWAPADDRESSES(L3MSG);
+ L3MSG->L3TTL = L3LIVES;
+
+ C_Q_ADD(&LINK->TX_Q, L3MSG);
+}
+
+VOID SETUPNEWCIRCUIT(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG,
+ TRANSPORTENTRY * L4, char * BPQPARAMS, int ApplMask, int * BPQNODE)
+{
+ struct DEST_LIST * DEST;
+ int Maxtries = 2; // Just in case
+
+ L4->FARINDEX = L3MSG->L4INDEX;
+ L4->FARID = L3MSG->L4ID;
+
+ // Index set by caller
+
+ L4->CIRCUITID = NEXTID;
+
+ NEXTID++;
+ if (NEXTID == 0)
+ NEXTID++; // kEEP nON-ZERO
+
+ L4->SESSIONT1 = L4T1;
+
+ L4->L4WINDOW = (UCHAR)L4DEFAULTWINDOW;
+
+ if (L3MSG->L4DATA[0] > L4DEFAULTWINDOW)
+ L4->L4WINDOW = L3MSG->L4DATA[0];
+
+ memcpy(L4->L4USER, &L3MSG->L4DATA[1], 7); // Originator's call from Call Request
+
+ if (ApplMask)
+ {
+ // Should get APPLCALL if set ( maybe ???????????????
+ }
+
+// MOV ESI,APPLCALLTABLEPTR
+// LEA ESI,APPLCALL[ESI]
+
+ memcpy(L4->L4MYCALL, MYCALL, 7);
+
+ // GET BPQ EXTENDED CONNECT PARAMS IF PRESENT
+
+ if (L3MSG->LENGTH == MSGHDDRLEN + 38 || L3MSG->LENGTH == MSGHDDRLEN + 39)
+ {
+ *BPQNODE = 1;
+ memcpy(BPQPARAMS, &L3MSG->L4DATA[15],L3MSG->LENGTH - (MSGHDDRLEN + 36));
+ }
+
+ L4->L4CIRCUITTYPE = SESSION | UPLINK;
+ L4->L4STATE = 5;
+
+TryAgain:
+
+ DEST = CHECKL3TABLES(LINK, L3MSG);
+
+ L4->L4TARGET.DEST = DEST;
+
+ if (DEST == 0)
+ {
+ int WorstQual = 256;
+ struct DEST_LIST * WorstDest = NULL;
+ int n = MAXDESTS;
+
+ // Node not it table and table full
+
+ // Replace worst quality node with session counts of zero
+
+ // But could have been excluded, so check
+
+ if (CheckExcludeList(L3MSG->L3SRCE) == 0)
+ return;
+
+ DEST = DESTS;
+
+ while (n--)
+ {
+ if (DEST->DEST_COUNT == 0 && DEST->DEST_RTT == 0) // Not used and not INP3
+ {
+ if (DEST->NRROUTE[0].ROUT_QUALITY < WorstQual)
+ {
+ WorstQual = DEST->NRROUTE[0].ROUT_QUALITY;
+ WorstDest = DEST;
+ }
+ }
+ DEST++;
+ }
+
+ if (WorstDest)
+ {
+ REMOVENODE(WorstDest);
+ if (Maxtries--)
+ goto TryAgain; // We now have a spare (but protect against loop if something amiss)
+ }
+
+ // Nothing to delete, so just ignore connect
+
+ return;
+ }
+
+ if (*BPQNODE)
+ {
+ SHORT T1;
+
+ DEST->DEST_STATE |= 0x40; // SET BPQ _NODE BIT
+ memcpy((char *)&T1, BPQPARAMS, 2);
+
+ if (T1 > 300)
+ L4->SESSIONT1 = L4T1;
+ else
+ L4->SESSIONT1 = T1;
+ }
+ else
+ L4->SESSIONT1 = L4T1; // DEFAULT TIMEOUT
+
+ L4->SESSPACLEN = PACLEN; // DEFAULT
+}
+
+
+int CHECKIFBUSYL4(TRANSPORTENTRY * L4)
+{
+ // RETURN TOP BIT OF AL SET IF SESSION PARTNER IS BUSY
+
+ int Count;
+
+ if (L4->L4CROSSLINK) // CONNECTED?
+ Count = CountFramesQueuedOnSession(L4->L4CROSSLINK);
+ else
+ Count = CountFramesQueuedOnSession(L4);
+
+ if (Count < L4->L4WINDOW)
+ return 0;
+ else
+ return L4BUSY;
+}
+
+VOID FRAMEFORUS(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * L3MSG, int ApplMask, UCHAR * ApplCall)
+{
+ // INTERNODE LINK
+
+ TRANSPORTENTRY * L4;
+ struct DEST_LIST * DEST;
+ int Opcode;
+ char Nodename[20];
+ char ReplyText[20];
+ struct DATAMESSAGE * Msg;
+ TRANSPORTENTRY * Partner;
+ UCHAR * ptr1;
+ int FramesMissing;
+ L3MESSAGEBUFFER * Saved;
+ L3MESSAGEBUFFER ** Prev;
+ char Call[10];
+ struct TNCINFO * TNC;
+
+ L4FRAMESRX++;
+
+ Opcode = L3MSG->L4FLAGS & 15;
+
+ switch (Opcode)
+ {
+ case 0:
+
+ // OPCODE 0 is used for a variety of functions, using L4INDEX and L4ID as qualifiers
+ // 0c0c is used for IP
+
+ if (L3MSG->L4ID == 0x0C && L3MSG->L4INDEX == 0x0C)
+ {
+ Q_IP_MSG((MESSAGE *)L3MSG);
+ return;
+ }
+
+ // 00 01 Seesm to be Netrom Record Route
+
+ if (L3MSG->L4ID == 1 && L3MSG->L4INDEX == 0)
+ {
+ NRRecordRoute((UCHAR *)L3MSG, L3MSG->LENGTH);
+ return;
+ }
+
+ ReleaseBuffer(L3MSG);
+ return;
+
+ case L4CREQ:
+
+ CONNECTREQUEST(LINK, L3MSG, ApplMask, ApplCall);
+ return;
+ }
+
+ // OTHERS NEED A SESSION
+
+ L4 = &L4TABLE[L3MSG->L4INDEX];
+
+ if (L4->CIRCUITID!= L3MSG->L4ID)
+ {
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+
+ if ((L4->L4CIRCUITTYPE & SESSION) == 0)
+ {
+ // Not an L4 Session - must be an old connection
+
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+
+ // HAVE FOUND CORRECT SESSION ENTRY
+
+ switch (Opcode)
+ {
+ case L4CACK:
+
+ // CONNECT ACK
+
+ DEST = L4->L4TARGET.DEST;
+
+ // EXTRACT EXTENDED PARAMS IF PRESENT
+
+ if (L3MSG->LENGTH > MSGHDDRLEN + 22) // Standard Msg
+ {
+ DEST->DEST_STATE &= 0x80;
+ DEST->DEST_STATE |= (L3MSG->L4DATA[1] - L3MSG->L3TTL) + 0x41; // Hops to dest + x40
+ }
+
+ Partner = L4->L4CROSSLINK;
+
+ if (L3MSG->L4FLAGS & L4BUSY)
+ {
+ // Refused
+
+ CLEARSESSIONENTRY(L4);
+ if (Partner)
+ Partner->L4CROSSLINK = NULL; // CLEAR CROSSLINK
+
+ strcpy(ReplyText, "Busy from");
+ }
+ else
+ {
+ // Connect OK
+
+ if (L4->L4STATE == 5)
+ {
+ // MUST BE REPEAT MSG - DISCARD
+
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+
+ L4->FARINDEX = L3MSG->L4TXNO;
+ L4->FARID = L3MSG->L4RXNO;
+
+ L4->L4STATE = 5; // ACTIVE
+ L4->L4TIMER = 0;
+ L4->L4RETRIES = 0;
+
+ L4->L4WINDOW = L3MSG->L4DATA[0];
+
+ strcpy(ReplyText, "Connected to");
+ }
+
+ if (Partner == 0)
+ {
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+
+ Msg = (PDATAMESSAGE)L3MSG; // reuse input buffer
+
+ Msg->PID = 0xf0;
+ ptr1 = SetupNodeHeader(Msg);
+
+ Nodename[DecodeNodeName(DEST->DEST_CALL, Nodename)] = 0; // null terminate
+
+ ptr1 += sprintf(ptr1, "%s %s\r", ReplyText, Nodename);
+
+ Msg->LENGTH = (int)(ptr1 - (UCHAR *)Msg);
+
+ C_Q_ADD(&Partner->L4TX_Q, Msg);
+
+ PostDataAvailable(Partner);
+ return;
+
+ case L4DREQ:
+
+ // DISCONNECT REQUEST
+
+ L3MSG->L4INDEX = L4->FARINDEX;
+ L3MSG->L4ID = L4->FARID;
+
+ L3MSG->L4FLAGS = L4DACK;
+
+ L3SWAPADDRESSES(L3MSG); // EXCHANGE SOURCE AND DEST
+ L3MSG->L3TTL = L3LIVES;
+
+ TNC = LINK->LINKPORT->TNC;
+
+ if (TNC && TNC->NetRomMode)
+ SendVARANetromMsg(TNC, L3MSG);
+ else
+ C_Q_ADD(&LINK->TX_Q, L3MSG);
+
+ CloseSessionPartner(L4); // SEND CLOSE TO PARTNER (IF PRESENT)
+ return;
+
+ case L4DACK:
+
+ CLEARSESSIONENTRY(L4);
+ ReleaseBuffer(L3MSG);
+ return;
+
+ case L4INFO:
+
+ //MAKE SURE SESSION IS UP - FIRST I FRAME COULD ARRIVE BEFORE CONNECT ACK
+
+ if (L4->L4STATE == 2)
+ {
+ ReleaseBuffer(L3MSG); // SHOULD SAVE - WILL AVOID NEED TO RETRANSMIT
+ return;
+ }
+
+ ACKFRAMES(L3MSG, L4, L3MSG->L4RXNO);
+
+ // If DISCPENDING or STATE IS 4, THEN SESSION IS CLOSING - IGNORE ANY I FRAMES
+
+ if ((L4->FLAGS & DISCPENDING) || L4->L4STATE == 4)
+ {
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+
+ // CHECK RECEIVED SEQUENCE
+
+ FramesMissing = L3MSG->L4TXNO - L4->RXSEQNO; // WHAT WE GOT - WHAT WE WANT
+
+ if (FramesMissing > 128)
+ FramesMissing -= 256;
+
+ // if NUMBER OF FRAMES MISSING is -VE, THEN IN FACT IT INDICATES A REPEAT
+
+ if (FramesMissing < 0)
+ {
+ // FRAME IS A REPEAT
+
+ Call[ConvFromAX25(L3MSG->L3SRCE, Call)] = 0;
+ Debugprintf("Discarding repeated frame seq %d from %s", L3MSG->L4TXNO, Call);
+
+ L4->L4ACKREQ = 1;
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+
+ if (FramesMissing > 0)
+ {
+ // EXPECTED FRAME HAS BEEN MISSED - ASK FOR IT AGAIN,
+ // AND KEEP THIS FRAME UNTIL MISSING ONE ARRIVES
+
+ L4->NAKBITS |= L4NAK; // SET NAK REQUIRED
+
+ SENDL4IACK(L4); // SEND DATA ACK COMMAND TO ACK OUTSTANDING FRAMES
+
+ // SEE IF WE ALREADY HAVE A COPY OF THIS ONE
+/*
+ Saved = L4->L4RESEQ_Q;
+
+ Call[ConvFromAX25(L3MSG->L3SRCE, Call)] = 0;
+ Debugprintf("saving seq %d from %s", L3MSG->L4TXNO, Call);
+
+ while (Saved)
+ {
+ if (Saved->L4TXNO == L3MSG->L4TXNO)
+ {
+ // ALREADY HAVE A COPY - DISCARD IT
+
+ Debugprintf("Already have seq %d - discarding", L3MSG->L4TXNO);
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+
+ Saved = Saved->Next;
+ }
+
+ C_Q_ADD(&L4->L4RESEQ_Q, L3MSG); // ADD TO CHAIN
+ return;
+*/
+ }
+
+ // Frame is OK
+
+L4INFO_OK:
+
+ if (L3MSG == 0)
+ {
+ Debugprintf("Trying to Process NULL L3 Message");
+ return;
+ }
+
+ L4->NAKBITS &= ~L4NAK; // CLEAR MESSAGE LOST STATE
+
+ L4->RXSEQNO++;
+
+ // REMOVE HEADERS, AND QUEUE INFO
+
+ L3MSG->LENGTH -= 20; // L3/L4 Header
+
+ if (L3MSG->LENGTH < (4 + sizeof(void *))) // No PID
+ {
+ ReleaseBuffer(L3MSG);
+ return;
+ }
+
+ L3MSG->L3PID = 0xF0; // Normal Data PID
+
+ memmove(L3MSG->L3SRCE, L3MSG->L4DATA, L3MSG->LENGTH - (4 + sizeof(void *)));
+
+ REFRESHROUTE(L4);
+
+ L4->L4ACKREQ = L4DELAY; // SEND INFO ACK AFTER L4DELAY (UNLESS I FRAME SENT)
+
+ IFRM150(L4, (PDATAMESSAGE)L3MSG); // CHECK IF SETTING UP AND PASS ON
+
+ // See if anything on reseq Q to process
+
+ if (L4->L4RESEQ_Q == 0)
+ return;
+
+ Prev = &L4->L4RESEQ_Q;
+ Saved = L4->L4RESEQ_Q;
+
+ while (Saved)
+ {
+ if (Saved->L4TXNO == L4->RXSEQNO) // The one we want
+ {
+ // REMOVE IT FROM QUEUE,AND PROCESS IT
+
+ *Prev = Saved->Next; // CHAIN NEXT IN CHAIN TO PREVIOUS
+
+ OLDFRAMES++; // COUNT FOR STATS
+
+ L3MSG = Saved;
+ Debugprintf("Processing Saved Message %d Address %x", L4->RXSEQNO, L3MSG);
+ goto L4INFO_OK;
+ }
+
+ Debugprintf("Message %d %x still on Reseq Queue", Saved->L4TXNO, Saved);
+
+ Prev = &Saved;
+ Saved = Saved->Next;
+ }
+
+ return;
+
+ case L4IACK:
+
+ ACKFRAMES(L3MSG, L4, L3MSG->L4RXNO);
+ REFRESHROUTE(L4);
+
+ // Drop Through
+ }
+
+ // Unrecognised - Ignore
+
+ ReleaseBuffer(L3MSG);
+ return;
+}
+
+
+VOID ACKFRAMES(L3MESSAGEBUFFER * L3MSG, TRANSPORTENTRY * L4, int NR)
+{
+ // SEE HOW MANY FRAMES ARE ACKED - IF NEGATIVE, THAN THIS MUST BE A
+ // DELAYED REPEAT OF AN ACK ALREADY PROCESSED
+
+ int Count = NR - L4->L4WS;
+ L3MESSAGEBUFFER * Saved;
+ struct DEST_LIST * DEST;
+ struct DATAMESSAGE * Msg;
+ struct DATAMESSAGE * Copy;
+ int RTT;
+
+
+ if (Count < -128)
+ Count += 256;
+
+ if (Count < 0)
+ {
+ // THIS MAY BE A DELAYED REPEAT OF AN ACK ALREADY PROCESSED
+
+ return; // IGNORE COMPLETELY
+ }
+
+ while (Count > 0)
+ {
+ // new ACK
+
+ // FRAME L4WS HAS BEED ACKED - IT SHOULD BE FIRST ON HOLD QUEUE
+
+ Saved = Q_REM((void *)&L4->L4HOLD_Q);
+
+ if (Saved)
+ ReleaseBuffer(Saved);
+
+ // CHECK RTT SEQUENCE
+
+ if (L4->L4WS == L4->RTT_SEQ)
+ {
+ if (L4->RTT_TIMER)
+ {
+ // FRAME BEING TIMED HAS BEEN ACKED - UPDATE DEST RTT TIMER
+
+ DEST = L4->L4TARGET.DEST;
+
+ RTT = GetTickCount() - L4->RTT_TIMER;
+
+ if (DEST->DEST_RTT == 0)
+ DEST->DEST_RTT = RTT;
+ else
+ DEST->DEST_RTT = ((DEST->DEST_RTT * 9) + RTT) /10; // 90% Old + New
+ }
+ }
+
+ L4->L4WS++;
+ Count--;
+ }
+
+ L4->L4TIMER = 0;
+ L4->L4RETRIES = 0;
+
+ if (NR != L4->TXSEQNO)
+ {
+ // Not all Acked
+
+ L4->L4TIMER = L4->SESSIONT1; // RESTART TIMER
+ }
+ else
+ {
+ if ((L4->FLAGS & DISCPENDING) && L4->L4TX_Q == 0)
+ {
+ // All Acked and DISC Pending, so send it
+
+ SENDL4DISC(L4);
+ return;
+ }
+ }
+
+ // SEE IF CHOKE SET
+
+ L4->FLAGS &= ~L4BUSY;
+
+ if (L3MSG->L4FLAGS & L4BUSY)
+ {
+ L4->FLAGS |= L3MSG->L4FLAGS & L4BUSY; // Get Busy flag from message
+
+ if ((L3MSG->L4FLAGS & L4NAK) == 0)
+ return; // Dont send while biust unless NAC received
+ }
+
+ if (L3MSG->L4FLAGS & L4NAK)
+ {
+ // RETRANSMIT REQUESTED MESSAGE - WILL BE FIRST ON HOLD QUEUE
+
+ Msg = L4->L4HOLD_Q;
+
+ if (Msg == 0)
+ return;
+
+ Copy = GetBuff();
+
+ if (Copy == 0)
+ return;
+
+ memcpy(Copy, Msg, Msg->LENGTH);
+
+ DEST = L4->L4TARGET.DEST;
+
+ C_Q_ADD(&DEST->DEST_Q, Copy);
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+VOID SENDL4IACK(TRANSPORTENTRY * Session)
+{
+ // SEND INFO ACK
+
+ PL3MESSAGEBUFFER MSG = (PL3MESSAGEBUFFER)GetBuff();
+ struct DEST_LIST * DEST = Session->L4TARGET.DEST;
+
+ if (MSG == NULL)
+ return;
+
+ MSG->L3PID = 0xCF; // NET MESSAGE
+
+ memcpy(MSG->L3SRCE, Session->L4MYCALL, 7);
+ memcpy(MSG->L3DEST, DEST->DEST_CALL, 7);
+
+ MSG->L3TTL = L3LIVES;
+
+ MSG->L4INDEX = Session->FARINDEX;
+ MSG->L4ID = Session->FARID;
+
+ MSG->L4TXNO = 0;
+
+
+ MSG->L4RXNO = Session->RXSEQNO;
+ Session->L4LASTACKED = Session->RXSEQNO; // SAVE LAST NUMBER ACKED
+
+ MSG->L4FLAGS = L4IACK | GETBUSYBIT(Session) | Session->NAKBITS;
+
+ MSG->LENGTH = MSGHDDRLEN + 22;
+
+ C_Q_ADD(&DEST->DEST_Q, (UINT *)MSG);
+}
+
+
+
+
+/*
+ PUBLIC KILLSESSION
+KILLSESSION:
+
+ pushad
+ push ebx
+ CALL _CLEARSESSIONENTRY
+ pop ebx
+ popad
+
+ JMP L4CONN90 ; REJECT
+
+ PUBLIC CONNECTACK
+CONNECTACK:
+;
+; EXTRACT EXTENDED PARAMS IF PRESENT
+;
+
+ CMP BYTE PTR MSGLENGTH[EDI],L4DATA+1
+ JE SHORT NOTBPQ
+
+ MOV AL,L4DATA+1[EDI]
+ SUB AL,L3MONR[EDI]
+ ADD AL,41H ; HOPS TO DEST + 40H
+
+ MOV ESI,L4TARGET[EBX]
+ AND DEST_STATE[ESI],80H
+ OR DEST_STATE[ESI],AL ; SAVE
+
+ PUBLIC NOTBPQ
+NOTBPQ:
+;
+; SEE IF SUCCESS OR FAIL
+;
+ PUSH EDI
+
+ MOV ESI,L4TARGET[EBX] ; ADDR OF LINK/DEST ENTRY
+ LEA ESI,DEST_CALL[ESI]
+
+ CALL DECODENODENAME ; CONVERT TO ALIAS:CALL
+
+ MOV EDI,OFFSET32 CONACKCALL
+ MOV ECX,17
+ REP MOVSB
+
+
+ POP EDI
+
+ TEST L4FLAGS[EDI],L4BUSY
+ JNZ SHORT L4CONNFAILED
+
+ CMP L4STATE[EBX],5
+ JE SHORT CONNACK05 ; MUST BE REPEAT MSG - DISCARD
+
+ MOV AX,WORD PTR L4TXNO[EDI] ; HIS INDEX
+ MOV WORD PTR FARINDEX[EBX],AX
+
+ MOV L4STATE[EBX],5 ; ACTIVE
+ MOV L4TIMER[EBX],0 ; CANCEL TIMER
+ MOV L4RETRIES[EBX],0 ; CLEAR RETRY COUNT
+
+ MOV AL,L4DATA[EDI] ; WINDOW
+ MOV L4WINDOW[EBX],AL ; SET WINDOW
+
+ MOV EDX,L4CROSSLINK[EBX] ; POINT TO PARTNER
+;
+ MOV ESI,OFFSET32 CONNECTEDMSG
+ MOV ECX,LCONNECTEDMSG
+
+ JMP SHORT L4CONNCOMM
+
+ PUBLIC L4CONNFAILED
+L4CONNFAILED:
+;
+ MOV EDX,L4CROSSLINK[EBX] ; SAVE PARTNER
+ pushad
+ push ebx
+ CALL _CLEARSESSIONENTRY
+ pop ebx
+ popad
+
+ PUSH EBX
+
+ MOV EBX,EDX
+ MOV L4CROSSLINK[EBX],0 ; CLEAR CROSSLINK
+ POP EBX
+
+ MOV ESI,OFFSET32 BUSYMSG ; ?? BUSY
+ MOV ECX,LBUSYMSG
+
+ PUBLIC L4CONNCOMM
+L4CONNCOMM:
+
+ OR EDX,EDX
+ JNZ SHORT L4CONNOK10
+;
+; CROSSLINK HAS GONE?? - JUST CHUCK MESSAGE
+;
+ PUBLIC CONNACK05
+CONNACK05:
+
+ JMP L4DISCARD
+
+ PUBLIC L4CONNOK10
+L4CONNOK10:
+
+ PUSH EBX
+ PUSH ESI
+ PUSH ECX
+
+ MOV EDI,_BUFFER
+
+ ADD EDI,7
+ MOV AL,0F0H
+ STOSB ; PID
+
+ CALL _SETUPNODEHEADER ; PUT IN _NODE ID
+
+
+ POP ECX
+ POP ESI
+ REP MOVSB
+
+ MOV ESI,OFFSET32 CONACKCALL
+ MOV ECX,17 ; MAX LENGTH ALIAS:CALL
+ REP MOVSB
+
+ MOV AL,0DH
+ STOSB
+
+ MOV ECX,EDI
+ MOV EDI,_BUFFER
+ SUB ECX,EDI
+
+ MOV MSGLENGTH[EDI],CX
+
+ MOV EBX,EDX ; CALLER'S SESSION
+
+ LEA ESI,L4TX_Q[EBX]
+ CALL _Q_ADD ; SEND MESSAGE TO CALLER
+
+ CALL _POSTDATAAVAIL
+
+ POP EBX ; ORIGINAL CIRCUIT TABLE
+ RET
+
+
+ PUBLIC SENDCONNECTREPLY
+SENDCONNECTREPLY:
+;
+; LINK SETUP COMPLETE - EBX = LINK, EDI = _BUFFER
+;
+ CMP LINKTYPE[EBX],3
+ JNE SHORT CONNECTED00
+;
+; _NODE - _NODE SESSION SET UP - DONT NEED TO DO ANYTHING (I THINK!)
+;
+ CALL RELBUFF
+ RET
+
+;
+; UP/DOWN LINK
+;
+ PUBLIC CONNECTED00
+CONNECTED00:
+ CMP CIRCUITPOINTER[EBX],0
+ JNE SHORT CONNECTED01
+
+ CALL RELBUFF ; UP/DOWN WITH NO SESSION - NOONE TO TELL
+ RET ; NO CROSS LINK
+ PUBLIC CONNECTED01
+CONNECTED01:
+ MOV _BUFFER,EDI
+ PUSH EBX
+ PUSH ESI
+ PUSH ECX
+
+ ADD EDI,7
+ MOV AL,0F0H
+ STOSB ; PID
+
+ CALL _SETUPNODEHEADER ; PUT IN _NODE ID
+
+ LEA ESI,LINKCALL[EBX]
+
+ PUSH EDI
+ CALL CONVFROMAX25 ; ADDR OF CALLED STATION
+ POP EDI
+
+ MOV EBX,CIRCUITPOINTER[EBX]
+
+ MOV L4STATE[EBX],5 ; SET LINK UP
+
+ MOV EBX,L4CROSSLINK[EBX] ; TO INCOMING LINK
+ cmp ebx,0
+ jne xxx
+;
+; NO LINK ???
+;
+ MOV EDI,_BUFFER
+ CALL RELBUFF
+
+ POP ECX
+ POP ESI
+ POP EBX
+
+ RET
+
+ PUBLIC xxx
+xxx:
+
+ POP ECX
+ POP ESI
+ REP MOVSB
+
+ MOV ESI,OFFSET32 _NORMCALL
+ MOVZX ECX,_NORMLEN
+ REP MOVSB
+
+ MOV AL,0DH
+ STOSB
+
+ MOV ECX,EDI
+ MOV EDI,_BUFFER
+ SUB ECX,EDI
+
+ MOV MSGLENGTH[EDI],CX
+
+ LEA ESI,L4TX_Q[EBX]
+ CALL _Q_ADD ; SEND MESSAGE TO CALLER
+
+ CALL _POSTDATAAVAIL
+
+ POP EBX
+ RET
+*/
\ No newline at end of file
diff --git a/LinBPQ.c b/LinBPQ.c
index eba6b31..d25dfb7 100644
--- a/LinBPQ.c
+++ b/LinBPQ.c
@@ -76,6 +76,7 @@ void SaveAIS();
void initAIS();
void DRATSPoll();
VOID GetPGConfig();
+void SendBBSDataToPktMap();
extern uint64_t timeLoadedMS;
@@ -1281,6 +1282,10 @@ int main(int argc, char * argv[])
printf("Mail Started\n");
Logprintf(LOG_BBS, NULL, '!', "Mail Starting");
+ APIClock = 0;
+
+ SendBBSDataToPktMap();
+
}
}
@@ -1579,6 +1584,13 @@ int main(int argc, char * argv[])
DoHouseKeeping(FALSE);
}
+ if (APIClock < NOW)
+ {
+ SendBBSDataToPktMap();
+ APIClock = NOW + 7200; // Every 2 hours
+ }
+
+
tm = gmtime(&NOW);
if (tm->tm_wday == 0) // Sunday
diff --git a/MailDataDefs.c b/MailDataDefs.c
index 883963a..abfbac2 100644
--- a/MailDataDefs.c
+++ b/MailDataDefs.c
@@ -205,6 +205,7 @@ int MailForInterval = 0;
char zeros[NBMASK]; // For forward bitmask tests
time_t MaintClock; // Time to run housekeeping
+time_t APIClock; // Time to sent to MOLTE's Database
struct MsgInfo * MsgnotoMsg[100000]; // Message Number to Message Slot List.
diff --git a/MailNode.vcproj.LAPTOP-Q6S4RP5Q.johnw.user b/MailNode.vcproj.LAPTOP-Q6S4RP5Q.johnw.user
new file mode 100644
index 0000000..87b9e21
--- /dev/null
+++ b/MailNode.vcproj.LAPTOP-Q6S4RP5Q.johnw.user
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MailNode.vcproj.SKIGACER.johnw.user b/MailNode.vcproj.SKIGACER.johnw.user
new file mode 100644
index 0000000..bbece07
--- /dev/null
+++ b/MailNode.vcproj.SKIGACER.johnw.user
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MailTCP.c b/MailTCP.c
index 49478e1..91ddc3f 100644
--- a/MailTCP.c
+++ b/MailTCP.c
@@ -2897,6 +2897,8 @@ SocketConn * SMTPConnect(char * Host, int Port, BOOL AMPR, struct MsgInfo * Msg,
sinx.sin_addr.s_addr = INADDR_ANY;
sinx.sin_port = 0;
+ sockptr->Timeout = 0;
+
if (bind(sockptr->socket, (LPSOCKADDR) &sinx, addrlen) != 0 )
{
//
@@ -3590,7 +3592,6 @@ VOID ProcessPOP3ClientMessage(SocketConn * sockptr, char * Buffer, int Len)
if (sockptr->POP3MsgCount > sockptr->POP3MsgNum++)
{
sockprintf(sockptr, "RETR %d", sockptr->POP3MsgNum);
-
sockptr->State = WaitingForRETRResponse;
}
else
diff --git a/Versions.h b/Versions.h
index 5f0f730..df74f46 100644
--- a/Versions.h
+++ b/Versions.h
@@ -10,14 +10,14 @@
#endif
-#define KVers 6,0,24,50
-#define KVerstring "6.0.24.50\0"
+#define KVers 6,0,24,51
+#define KVerstring "6.0.24.51\0"
#ifdef CKernel
#define Vers KVers
#define Verstring KVerstring
-#define Datestring "October 2024"
+#define Datestring "November 2024"
#define VerComments "G8BPQ Packet Switch (C Version)" KVerstring
#define VerCopyright "Copyright © 2001-2024 John Wiseman G8BPQ\0"
#define VerDesc "BPQ32 Switch\0"
diff --git a/WinmorControl.vcproj.LAPTOP-Q6S4RP5Q.johnw.user b/WinmorControl.vcproj.LAPTOP-Q6S4RP5Q.johnw.user
new file mode 100644
index 0000000..bed4096
--- /dev/null
+++ b/WinmorControl.vcproj.LAPTOP-Q6S4RP5Q.johnw.user
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adif.c b/adif.c
index 4e221e6..040942e 100644
--- a/adif.c
+++ b/adif.c
@@ -609,7 +609,6 @@ VOID ADIFWriteFreqList()
fprintf(Handle, "[Channels]\r\n");
-
for (i = 0; i < freqCount; i++)
fprintf(Handle, "Frequency %d=%lld\r\n" , i + 1, Freqs[i]);
diff --git a/bpqmail.h b/bpqmail.h
index 4ee6628..78a1831 100644
--- a/bpqmail.h
+++ b/bpqmail.h
@@ -1191,6 +1191,7 @@ BOOL FBBDoForward(CIRCUIT * conn);
BOOL FindMessagestoForward(CIRCUIT * conn);
BOOL SeeifMessagestoForward(int BBSNumber, CIRCUIT * Conn);
int CountMessagestoForward(struct UserInfo * user);
+int CountBytestoForward(struct UserInfo * user);
VOID * GetMultiLineDialogParam(HWND hDialog, int DLGItem);
@@ -1633,6 +1634,8 @@ extern char ** SendWPAddrs; // Replacers WP To and VIA
extern BOOL DontCheckFromCall;
+extern time_t APIClock;;
+
// YAPP stuff
#define SOH 1
diff --git a/cMain.c b/cMain.c
index b63a69c..4f9dc9b 100644
--- a/cMain.c
+++ b/cMain.c
@@ -50,6 +50,7 @@ VOID SendSmartID(struct PORTCONTROL * PORT);
int CanPortDigi(int Port);
int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len);
void MQTTTimer();
+void SaveMH();
#include "configstructs.h"
@@ -1507,7 +1508,7 @@ BOOL Start()
upnpInit();
- CurrentSecs = lastSlowSecs = time(NULL);
+ lastSaveSecs = CurrentSecs = lastSlowSecs = time(NULL);
return 0;
}
@@ -2105,7 +2106,19 @@ VOID TIMERINTERRUPT()
}
*/
}
-
+
+ // Check Autosave Nodes and MH timer
+
+ if (CurrentSecs - lastSaveSecs >= 3600) // 1 per hour
+ {
+ lastSaveSecs = CurrentSecs;
+
+ if (AUTOSAVE == 1)
+ SaveNodes();
+ if (AUTOSAVEMH == 1)
+ SaveMH();
+ }
+
if (L4TIMERFLAG >= 10) // 1 PER SEC
{
L4TIMERFLAG -= 10;
diff --git a/config.c b/config.c
index 6408e25..818e24d 100644
--- a/config.c
+++ b/config.c
@@ -174,11 +174,16 @@ extern BOOL Loopflag;
extern char NodeMapServer[80];
extern char ChatMapServer[80];
+double LatFromLOC;
+double LonFromLOC;
+
+
VOID * zalloc(int len);
int WritetoConsoleLocal(char * buff);
char * stristr (char *ch1, char *ch2);
+int FromLOC(char * Locator, double * pLat, double * pLon);
VOID Consoleprintf(const char * format, ...)
{
@@ -342,7 +347,7 @@ static int routine[] =
14, 14, 14, 14,
14, 14 ,14, 14,
15, 0, 2, 9, 9,
-2, 2, 2, 2, 2, 2,
+2, 2, 1, 2, 2, 2,
2, 2, 0, 1, 20, 20} ; // Routine to process param
int PARAMLIM = sizeof(routine)/sizeof(int);
@@ -924,11 +929,21 @@ NextAPRS:
strcat(LOCATOR, ":");
strcat(LOCATOR, ptr2);
ToLOC(atof(ptr1), atof(ptr2), LOC);
+ LatFromLOC = atof(ptr1);
+ LonFromLOC = atof(ptr2);
+
}
else
{
if (strlen(ptr1) == 6)
+ {
strcpy(LOC, ptr1);
+ FromLOC(LOC, &LatFromLOC, &LonFromLOC);
+ // Randomise in square
+ LatFromLOC += ((rand() / 24.0) / RAND_MAX);
+ LonFromLOC += ((rand() / 12.0) / RAND_MAX);
+
+ }
}
}
return 0;
diff --git a/datadefs.c b/datadefs.c
index 44d1fd0..f68c1c4 100644
--- a/datadefs.c
+++ b/datadefs.c
@@ -49,6 +49,9 @@ char MAPCOMMENT[250] = "";
char LOC[7] = ""; // Must be in shared mem// Maidenhead Locator for Reporting
char ReportDest[7];
+double LatFromLOC = 0;
+double LonFromLOC = 0;
+
UCHAR BPQDirectory[260] = ".";
UCHAR ConfigDirectory[260] = ".";
UCHAR LogDirectory[260] = "";
@@ -62,6 +65,7 @@ UCHAR L3KEEP[7] = {'K'+'K','E'+'E','E'+'E','P'+'P','L'+'L','I'+'I', 0xe0}; // K
time_t CurrentSecs;
time_t lastSlowSecs;
+time_t lastSaveSecs;
char WL2KCall[10] = "";
char WL2KLoc[7] = "";
diff --git a/lzhuf32.c b/lzhuf32.c
index 3482b30..a6828ca 100644
--- a/lzhuf32.c
+++ b/lzhuf32.c
@@ -768,6 +768,9 @@ BOOL CheckifPacket(char * Via)
if (FindContinent(ptr1))
return TRUE; // Packet
+ if (FindCountry(ptr1))
+ return TRUE; // Packet
+
if ((_stricmp(ptr1, "MARS") == 0) || (_stricmp(ptr1, "USA") == 0)) // MARS used both
return TRUE; // Packet
diff --git a/mailapi.c b/mailapi.c
index f447e7b..fad0897 100644
--- a/mailapi.c
+++ b/mailapi.c
@@ -705,4 +705,391 @@ packetmail_queue_length{partner="GB7NOT"} 0 1729090716916
packetmail_queue_length{partner="GB7NWL"} 0 1729090716916
packetmail_queue_length{partner="GM8BPQ"} 0 1729090716916
-*/
\ No newline at end of file
+*/
+
+
+// Stuff send to packetnodes.spots.radio/api/bbsdata/{bbsCall}
+//https://nodes.ukpacketradio.network/swagger/index.html
+
+
+/*
+BbsData{
+callsign* [...]
+time* [...]
+hroute* [...]
+peers [...]
+software* [...]
+version* [...]
+mailQueues [...]
+messages [...]
+latitude [...]
+longitude [...]
+locator [...]
+location [...]
+unroutable [...]
+}
+
+[
+
+{
+ "callsign": "GE8PZT",
+ "time": "2024-11-25T10:07:41+00:00",
+ "hroute": ".#24.GBR.EU",
+ "peers": [
+ "GB7BBS",
+ "VE2PKT",
+ "GB7NXT",
+ "VA2OM"
+ ],
+ "software": "XrLin",
+ "version": "504a",
+ "mailQueues": [],
+ "messages": [
+ {
+ "to": "TECH@WW",
+ "mid": "20539_GB7CIP",
+ "rcvd": "2024-11-24T09:27:59+00:00",
+ "routing": [
+ "R:241124/0927Z @:GE8PZT.#24.GBR.EU [Lamanva] #:2315 XrLin504a",
+
+
+ {
+ "to": "TNC@WW",
+ "mid": "37_PA2SNK",
+ "rcvd": "2024-11-18T21:56:55+00:00",
+ "routing": [
+ "R:241118/2156Z @:GE8PZT.#24.GBR.EU [] #:2215 XrLin504a",
+ "R:241118/2156Z 12456@VE2PKT.#TRV.QC.CAN.NOAM BPQ6.0.24",
+ "R:241118/2130Z 51539@VE3KPG.#ECON.ON.CAN.NOAM BPQK6.0.23",
+ "R:241118/2130Z 26087@VE3CGR.#SCON.ON.CAN.NOAM LinBPQ6.0.24",
+ "R:241118/2130Z 37521@PA8F.#ZH1.NLD.EURO LinBPQ6.0.24",
+ "R:241118/2129Z 48377@PI8LAP.#ZLD.NLD.EURO LinBPQ6.0.24",
+ "R:241118/2129Z @:PD0LPM.FRL.EURO.NLD #:33044 [Joure] $:37_PA2SNK"
+ ]
+ }
+ ],
+ "latitude": 50.145832,
+ "longitude": -5.125,
+ "locator": "IO70KD",
+ "location": "Lamanva",
+ "unroutable": [
+ {
+ "type": "P",
+ "at": "WW"
+ },
+ {
+ "type": "P",
+ "at": "G8PZT-2"
+ },
+ {
+ "type": "P",
+ "at": "g8pzt._24.gbr.eu"
+ },
+ {
+ "type": "P",
+ "at": "G8PZT.#24.GBR.EU"
+ },
+ {
+ "type": "P",
+ "at": "GE8PZT.#24.GBR.EU"
+ },
+ {
+ "type": "P",
+ "at": "G8PZT.#24.GBR.EURO"
+ }
+ ]
+ },
+
+*/
+
+
+// https://packetnodes.spots.radio/swagger/index.html
+
+// "unroutable": [{"type": "P","at": "WW"}, {"type": "P", "at": "G8PZT.#24.GBR.EURO"}]
+
+char * ViaList[100000]; // Pointers to the Message Header field
+char TypeList[100000];
+
+int unroutableCount = 0;
+
+
+void CheckifRoutable(struct MsgInfo * Msg)
+{
+ char NextBBS[64];
+ int n;
+
+ if (Msg->status == 'K')
+ return;
+
+ if (Msg->via[0] == 0) // No routing
+ return;
+
+ strcpy(NextBBS, Msg->via);
+ strlop(NextBBS, '.');
+
+ if (strcmp(NextBBS, BBSName) == 0) // via this BBS
+ return;
+
+ if ((memcmp(Msg->fbbs, zeros, NBMASK) != 0) || (memcmp(Msg->forw, zeros, NBMASK) != 0)) // Has Forwarding Info
+ return;
+
+ // See if we already have it
+
+ for (n = 0; n < unroutableCount; n++)
+ {
+ if ((TypeList[n] == Msg->type) && strcmp(ViaList[n], Msg->via) == 0)
+ return;
+
+ }
+
+ // Add to list
+
+ TypeList[unroutableCount] = Msg->type;
+ ViaList[unroutableCount] = Msg->via;
+
+ unroutableCount++;
+}
+
+
+extern char LOC[7];
+
+
+DllExport VOID WINAPI SendWebRequest(char * Host, char * Request, char * Params, char * Return);
+
+#ifdef LINBPQ
+extern double LatFromLOC;
+extern double LonFromLOC;
+#else
+typedef int (WINAPI FAR *FARPROCX)();
+extern FARPROCX pSendWebRequest;
+extern FARPROCX pGetLatLon;
+double LatFromLOC = 0;
+double LonFromLOC = 0;
+#endif
+
+void SendBBSDataToPktMap()
+{
+ char Return[4096];
+ char Request[64];
+ char Params[50000];
+ char * ptr = Params;
+ struct MsgInfo * Msg;
+
+ struct UserInfo * ourBBSRec = LookupCall(BBSName);
+ struct UserInfo * USER;
+ char Time[64];
+ struct tm * tm;
+ time_t Date = time(NULL);
+ char Peers[2048] = "[]";
+ char MsgQueues[16000] = "[]";
+ char * Messages = malloc(1000000);
+ char * Unroutables;
+ int m;
+ char * MsgBytes;
+ char * Rlineptr;
+ char * Rlineend;
+ char * RLines;
+ char * ptr1, * ptr2;
+ int n;
+
+#ifndef LINBPQ
+ if (pSendWebRequest == 0)
+ return; // Old Version of bpq32.dll
+
+ pGetLatLon(&LatFromLOC, &LonFromLOC);
+
+#endif
+ if (ourBBSRec == 0)
+ return; // Wot!!
+
+ // Get peers and Mail Queues
+
+ ptr = &Peers[1];
+ ptr1 = &MsgQueues[1];
+
+ for (USER = BBSChain; USER; USER = USER->BBSNext)
+ {
+ if (strcmp(USER->Call, BBSName) != 0)
+ {
+ int Bytes;
+
+ int Count = CountMessagestoForward(USER);
+
+ ptr += sprintf(ptr, "\"%s\",", USER->Call);
+
+ if (Count)
+ {
+ Bytes = CountBytestoForward(USER);
+
+ ptr1 += sprintf(ptr1, "{\"peerCall\": \"%s\", \"numQueued\": %d, \"bytesQueued\": %d},",
+ USER->Call, Count, Bytes);
+ }
+ }
+ }
+
+ if ((*ptr) != ']') // Have some entries
+ {
+ ptr--; // over trailing comms
+ *(ptr++) = ']';
+ *(ptr) = 0;
+ }
+
+ if ((*ptr1) != ']') // Have some entries
+ {
+ ptr1--; // over trailing comms
+ *(ptr1++) = ']';
+ *(ptr1) = 0;
+ }
+
+ // Get Messages
+
+ strcpy(Messages, "[]");
+ ptr = &Messages[1];
+
+ for (m = LatestMsg; m >= 1; m--)
+ {
+ if (ptr > &Messages[999000])
+ break; // protect buffer
+
+ Msg = GetMsgFromNumber(m);
+
+ if (Msg == 0 || Msg->type == 0 || Msg->status == 0)
+ continue; // Protect against corrupt messages
+
+ // Paula suggests including H and K but limit it to the last 30 days or the last 100 messages, whichever is the smaller.
+
+// if (Msg->status == 'K' || Msg->status == 'H')
+// continue;
+
+ if ((Date - Msg->datereceived) > 30 * 86400) // Too old
+ continue;
+
+ CheckifRoutable(Msg);
+
+ tm = gmtime(&Msg->datereceived);
+
+ sprintf(Time, "%04d-%02d-%02dT%02d:%02d:%02d+00:00",
+ tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ // Get Routing
+
+ MsgBytes = ReadMessageFile(Msg->number);
+ RLines = malloc(Msg->length * 2); // Very unlikely to need so much but better safe..
+
+ strcpy(RLines, "[]");
+
+ ptr2 = &RLines[1];
+
+ // Need to skip B2 header if B2 Message
+
+ Rlineptr = MsgBytes;
+
+ // If it is a B2 Message, Must Skip B2 Header
+
+ if (Msg->B2Flags & B2Msg)
+ {
+ Rlineptr = strstr(Rlineptr, "\r\n\r\n");
+ if (Rlineptr)
+ Rlineptr += 4;
+ else
+ Rlineptr = MsgBytes;
+ }
+
+ // We have to process R: lines one at a time as we need to send each one as a separate string
+
+ while (memcmp(Rlineptr, "R:", 2) == 0)
+ {
+ // Have R Lines
+
+ Rlineend = strstr(Rlineptr, "\r\n");
+ Rlineend[0] = 0;
+ ptr2 += sprintf(ptr2, "\"%s\",", Rlineptr);
+
+ Rlineptr = Rlineend + 2; // over crlf
+ }
+
+ if ((*ptr2) == ']') // no entries
+ continue;
+
+ ptr2--; // over trailing comms
+ *(ptr2++) = ']';
+ *(ptr2) = 0;
+
+ ptr += sprintf(ptr, "{\"to\": \"%s\", \"mid\": \"%s\", \"rcvd\": \"%s\", \"routing\": %s},",
+ Msg->to, Msg->bid, Time, RLines);
+
+ free(MsgBytes);
+ free(RLines);
+
+ }
+
+ if ((*ptr) != ']') // Have some entries?
+ {
+ ptr--; // over trailing comms
+ *(ptr++) = ']';
+ *(ptr) = 0;
+ }
+
+ // Get unroutables
+
+ Unroutables = malloc((unroutableCount + 1) * 100);
+
+ strcpy(Unroutables, "[]");
+ ptr = &Unroutables[1];
+
+
+ for (n = 0; n < unroutableCount; n++)
+ {
+ ptr += sprintf(ptr, "{\"type\": \"%c\",\"at\": \"%s\"},", TypeList[n], ViaList[n]);
+ }
+
+ if ((*ptr) != ']') // Have some entries?
+ {
+ ptr--; // over trailing comms
+ *(ptr++) = ']';
+ *(ptr) = 0;
+ }
+
+
+
+ /*
+char * ViaList[100000]; // Pointers to the Message Header field
+char TypeList[100000];
+
+int unroutableCount = 0;
+ "unroutable": [{"type": "P","at": "WW"}, {"type": "P", "at": "G8PZT.#24.GBR.EURO"}]
+ */
+
+
+ tm = gmtime(&Date);
+
+ sprintf(Time, "%04d-%02d-%02dT%02d:%02d:%02d+00:00",
+ tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+
+ ptr = Params;
+
+ sprintf(Request, "/api/bbsdata/%s", BBSName);
+
+ ptr += sprintf(ptr, "{\"callsign\": \"%s\",\r\n", BBSName);
+ ptr += sprintf(ptr, "\"time\": \"%s\",\r\n", Time);
+ ptr += sprintf(ptr, "\"hroute\": \"%s\",\r\n", HRoute);
+ ptr += sprintf(ptr, "\"peers\": %s,\r\n", Peers);
+#ifdef LINBPQ
+ ptr += sprintf(ptr, "\"software\": \"%s\",\r\n", "linbpq");
+#else
+ ptr += sprintf(ptr, "\"software\": \"%s\",\r\n", "BPQMail");
+#endif
+ ptr += sprintf(ptr, "\"version\": \"%s\",\r\n", VersionString);
+ ptr += sprintf(ptr, "\"mailQueues\": %s,\r\n", MsgQueues);
+ ptr += sprintf(ptr, "\"messages\": %s,\r\n", Messages);
+ ptr += sprintf(ptr, "\"latitude\": %1.6f,\r\n", LatFromLOC);
+ ptr += sprintf(ptr, "\"longitude\": %.6f,\r\n", LonFromLOC);
+ ptr += sprintf(ptr, "\"locator\": \"%s\",\r\n", LOC);
+ ptr += sprintf(ptr, "\"location\": \"%s\",\r\n", ourBBSRec->Address);
+ ptr += sprintf(ptr, "\"unroutable\": %s\r\n}\r\n", Unroutables);
+
+ SendWebRequest("packetnodes.spots.radio", Request, Params, Return);
+ free(Messages);
+ free(Unroutables);
+}
diff --git a/xpaho-mqtt3a.dll b/xpaho-mqtt3a.dll
deleted file mode 100644
index e978f04..0000000
Binary files a/xpaho-mqtt3a.dll and /dev/null differ