master
John Wiseman 4 weeks ago
parent b90e6bdaa4
commit 34f44ed4af

@ -5797,11 +5797,17 @@ VOID ProcessMsgTitle(ConnectionInfo * conn, struct UserInfo * user, char* Buffer
VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int msglen)
{
char * ptr2 = NULL;
int Index = 0;
int MsgHasCtrlZ = 0;
char * ptr;
// AEA TNC sends 2f 45 1a 3e 0d (/ E <ctrl/z> > <cr>) if the original message was input using /ex as terminator
if (((msglen < 3) && (Buffer[0] == 0x1a)) || ((msglen == 4) && (_memicmp(Buffer, "/ex", 3) == 0)))
{
int Index = 0;
if (((msglen < 4) && (Buffer[0] == 0x1a)) || ((msglen == 4) && (_memicmp(Buffer, "/ex", 3)) == 0) || memcmp(Buffer, "/E\0x1a>\0x0d", 5) || memcmp(Buffer, "/e\0x1a>\0x0d", 5))
{
gotCtrlZ:
if (conn->TempMsg->type == 'P')
Index = PMSG;
else if (conn->TempMsg->type == 'B')
@ -6112,6 +6118,19 @@ VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int ms
}
// AEA TNC dosn't always send ctrl/z at start of packet.
Buffer[msglen] = 0;
ptr = strchr(Buffer, 26); //Ctrl/Z
if (ptr)
{
MsgHasCtrlZ =1;
*(ptr) = 13; // replace with CR
}
Buffer[msglen++] = 0x0a;
if ((conn->TempMsg->length + msglen) > conn->MailBufferSize)
@ -6131,6 +6150,10 @@ VOID ProcessMsgLine(CIRCUIT * conn, struct UserInfo * user, char* Buffer, int ms
memcpy(&conn->MailBuffer[conn->TempMsg->length], Buffer, msglen);
conn->TempMsg->length += msglen;
if (MsgHasCtrlZ)
goto gotCtrlZ;
}
VOID CreateMessageFromBuffer(CIRCUIT * conn)
@ -8134,7 +8157,7 @@ BOOL ConnecttoBBS (struct UserInfo * user)
ConnectUsingAppl(conn->BPQStream, BBSApplMask);
FreeSemaphore(&ConSemaphore);
// If we are sending to a dump pms we may need to connect using the message sender's callsign.
// If we are sending to a dumb pms we may need to connect using the message sender's callsign.
// But we wont know until we run the connect script, which is a bit late to change call. Could add
// flag to forwarding config, but easier to look for SETCALLTOSENDER in the connect script.
@ -8868,6 +8891,41 @@ CheckForSID:
return FALSE;
}
/* if (memcmp(Buffer, "[AEA-", 4) == 0 || memcmp(Buffer, "[AEA PK", 7) == 0 || (conn->BBSFlags & TEXTFORWARDING))
{
// PK232. Don't send a SID, and switch to Text Mode
conn->BBSFlags |= (BBS | TEXTFORWARDING);
conn->Flags |= SENDTITLE;
// Send Message. There is no mechanism for reverse forwarding
if (FindMessagestoForward(conn) && conn->FwdMsg)
{
struct MsgInfo * Msg;
// Send S line and wait for response - SB WANT @ USA < W8AAA $1029_N0XYZ
Msg = conn->FwdMsg;
if ((conn->BBSFlags & SETCALLTOSENDER))
nodeprintf(conn, "S%c %s @ %s \r", Msg->type, Msg->to,
(Msg->via[0]) ? Msg->via : conn->UserPointer->Call);
else
nodeprintf(conn, "S%c %s @ %s < %s $%s\r", Msg->type, Msg->to,
(Msg->via[0]) ? Msg->via : conn->UserPointer->Call,
Msg->from, Msg->bid);
}
else
{
conn->BBSFlags &= ~RunningConnectScript; // so it doesn't get reentered
Disconnect(conn->BPQStream);
return FALSE;
}
return TRUE;
}
*/
if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID
{
@ -8910,7 +8968,9 @@ CheckForSID:
conn->BBSFlags &= ~RunningConnectScript;
ForwardingInfo->LastReverseForward = time(NULL);
if (memcmp(Buffer, "[AEA PK", 7) == 0 || (conn->BBSFlags & TEXTFORWARDING))
// I don't think this is needed any more but will leave in for now
/* if (memcmp(Buffer, "[AEA-", 4) == 0 || memcmp(Buffer, "(AEA PK", 7) == 0 || (conn->BBSFlags & TEXTFORWARDING))
{
// PK232. Don't send a SID, and switch to Text Mode
@ -8944,7 +9004,7 @@ CheckForSID:
return TRUE;
}
*/
if (strcmp(conn->Callsign, "RMS") == 0 || conn->SendWL2KFW)
{
// Build a ;FW: line with all calls with PollRMS Set

@ -199,7 +199,7 @@ VOID DeleteINP3Routes(struct ROUTE * Route)
if (Dest->INP3ROUTE[0].ROUT_NEIGHBOUR == Route)
{
// We are deleting the best INP3 route, so need to tell other nodes
// If this is the only one, we need to keep the entry with at 60000 rtt so
// We need to keep the entry with a 60000 rtt so
// we can send it. Remove when all gone
// How do we indicate is is dead - Maybe the 60000 is enough!
@ -208,26 +208,12 @@ VOID DeleteINP3Routes(struct ROUTE * Route)
if (DEBUGINP3) Debugprintf("Deleting First INP3 Route to %s", Call2);
if (Dest->INP3ROUTE[1].ROUT_NEIGHBOUR == 0)
{
// Only entry
Dest->INP3ROUTE[0].STT = 60000;
Dest->INP3ROUTE[0].Hops = 255;
if (DEBUGINP3) Debugprintf("Was the only INP3 route");
if (Dest->DEST_ROUTE == 4) // we were using it
Dest->DEST_ROUTE = 0;
Dest->INP3ROUTE[0].STT = 60000; // leave hops so we can check if we need to send
continue;
}
if (DEBUGINP3) Debugprintf("Was the only INP3 route");
Dest->INP3ROUTE[1].RouteLastTT[Dest->INP3ROUTE[1].ROUT_NEIGHBOUR->recNum] = Dest->INP3ROUTE[0].STT; // So next scan will check if rtt has increaced enough to need a RIF
memcpy(&Dest->INP3ROUTE[0], &Dest->INP3ROUTE[1], sizeof(struct INP3_DEST_ROUTE_ENTRY));
memcpy(&Dest->INP3ROUTE[1], &Dest->INP3ROUTE[2], sizeof(struct INP3_DEST_ROUTE_ENTRY));
memset(&Dest->INP3ROUTE[2], 0, sizeof(struct INP3_DEST_ROUTE_ENTRY));
if (Dest->DEST_ROUTE == 4) // we were using it
Dest->DEST_ROUTE = 0;
continue;
}
@ -253,6 +239,7 @@ VOID DeleteINP3Routes(struct ROUTE * Route)
// I think we should send Negative info immediately
NegTimerCount = NegativeDelay;
SendNegativeInfo();
}
@ -374,8 +361,10 @@ VOID ProcessRTTReply(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Buff)
if (RTT > 60000 || RTT < 0)
return; // Ignore if more than 60 secs (why ??)
if (DEBUGINP3) Debugprintf("INP3 RTT reply from %s - SRTT was %d, Current RTT %d", Normcall, Route->SRTT, RTT);
if (RTT == 0)
RTT = 1; // Don't allow a Node TT of zero
if (DEBUGINP3) Debugprintf("INP3 RTT reply from %s - SRTT was %d, Current RTT %d", Normcall, Route->SRTT, RTT);
Route->RTT = RTT;
@ -386,6 +375,9 @@ VOID ProcessRTTReply(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Buff)
Route->RTTIncrement = Route->SRTT / 2; // Half for one way time.
if (Route->RTTIncrement == 0)
Route->RTTIncrement = 1;
if ((Route->Status & GotRTTResponse) == 0)
{
// Link is just starting
@ -509,9 +501,6 @@ VOID UpdateNode(struct ROUTE * Route, UCHAR * axcall, UCHAR * alias, int hops,
char call[11]="";
APPLCALLS * APPL;
int App;
char Normcall[10];
Normcall[ConvFromAX25(axcall, Normcall)] = 0;
// SEE IF any of OUR CALLs - DONT WANT TO PUT IT IN LIST!
@ -538,27 +527,64 @@ VOID UpdateNode(struct ROUTE * Route, UCHAR * axcall, UCHAR * alias, int hops,
}
}
ConvFromAX25(axcall, call);
if (hops > MaxHops && hops < 255)
// We need to detect unreachable here
if (rtt >= 60000 || hops > 30) // I use 255, Paula uses 31 hops for unreachable
{
ConvFromAX25(axcall, call);
if (DEBUGINP3) Debugprintf("INP3 Node %s Hops %d RTT %d Ignored - Hop Count too high", call, hops, rtt);
// node is unreachable. I need propagate it to other neighbours.
if (DEBUGINP3) Debugprintf("INP3 Node %s is unreachable via", call);
if (FindDestination(axcall, &Dest))
{
if (Dest->INP3ROUTE[0].ROUT_NEIGHBOUR == Route) // Best route
{
Dest->INP3ROUTE[0].STT = 60000; // Will be removed once reported. leave hops so we can check if we need to send
if (Dest->DEST_ROUTE == 4) // we were using it
Dest->DEST_ROUTE = 0;
NegTimerCount = 0; // Send negative info asap
return;
}
if (Dest->INP3ROUTE[1].ROUT_NEIGHBOUR == Route)
{
if (DEBUGINP3) Debugprintf("Deleting 2nd INP3 Route to %s", call);
memcpy(&Dest->INP3ROUTE[1], &Dest->INP3ROUTE[2], sizeof(struct INP3_DEST_ROUTE_ENTRY));
memset(&Dest->INP3ROUTE[2], 0, sizeof(struct INP3_DEST_ROUTE_ENTRY));
return;
}
if (Dest->INP3ROUTE[2].ROUT_NEIGHBOUR == Route)
{
if (DEBUGINP3) Debugprintf("Deleting 3rd INP3 Route to %s", call);
memset(&Dest->INP3ROUTE[2], 0, sizeof(struct INP3_DEST_ROUTE_ENTRY));
return;
}
}
// Not found or not in table - ignore
return;
}
if (rtt > MAXRTT && rtt < 60000)
if (hops > MaxHops)
{
ConvFromAX25(axcall, call);
if (DEBUGINP3) Debugprintf("INP3 Node %s Hops %d RTT %d Ignored - rtt too high", call, hops, rtt);
if (DEBUGINP3) Debugprintf("INP3 Node %s Hops %d RTT %d Ignored - Hop Count too high", call, hops, rtt);
return;
}
if (rtt >= 60000)
if (rtt > MAXRTT)
{
if (DEBUGINP3) Debugprintf("INP3 RTT > 60000 - discarding");
if (DEBUGINP3) Debugprintf("INP3 Node %s Hops %d RTT %d Ignored - rtt too high", call, hops, rtt);
return;
}
if (FindDestination(axcall, &Dest))
goto Found;
@ -568,22 +594,23 @@ VOID UpdateNode(struct ROUTE * Route, UCHAR * axcall, UCHAR * alias, int hops,
return; // Table Full
}
// Adding New Node
if (Dest->RouteLastTT)
free(Dest->RouteLastTT);
memset(Dest, 0, sizeof(struct DEST_LIST));
memcpy(Dest->DEST_CALL, axcall, 7);
memcpy(Dest->DEST_ALIAS, alias, 6);
// Set up First Route
Dest->RouteLastTT = (uint16_t *)zalloc(MAXNEIGHBOURS * sizeof(uint16_t));
Dest->INP3ROUTE[0].Hops = hops;
Dest->INP3ROUTE[0].STT = rtt;
if (Dest->INP3ROUTE[0].RouteLastTT == 0)
Dest->INP3ROUTE[0].RouteLastTT = (uint16_t *)zalloc(MAXNEIGHBOURS * sizeof(uint16_t));
Dest->INP3ROUTE[0].RouteLastTT[Route->recNum] = 0;
Dest->RouteLastTT[Route->recNum] = 0;
Dest->INP3FLAGS = NewNode;
@ -654,6 +681,8 @@ Found:
if (DEBUGINP3) Debugprintf("INP3 adding as route[%d]", i);
AddHere(ROUTEPTR, Route, hops, rtt);
if (i == 0)
Dest->RouteLastTT[Route->recNum] = 0;
SortRoutes(Dest);
return;
}
@ -708,10 +737,6 @@ Found:
VOID AddHere(struct INP3_DEST_ROUTE_ENTRY * ROUTEPTR,struct ROUTE * Route , int hops, int rtt)
{
ROUTEPTR->Hops = hops;
if (ROUTEPTR->RouteLastTT == 0)
ROUTEPTR->RouteLastTT = (uint16_t *)zalloc(MAXNEIGHBOURS * sizeof(uint16_t));
ROUTEPTR->RouteLastTT[Route->recNum] = 0;
ROUTEPTR->STT = rtt;
ROUTEPTR->ROUT_NEIGHBOUR = Route;
@ -1055,7 +1080,7 @@ VOID SendKeepAlive(struct ROUTE * Route)
SendNetFrame(Route, Msg);
}
int BuildRIF(UCHAR * RIF, UCHAR * Call, UCHAR * Alias, int Hops, int RTT)
int BuildRIF(UCHAR * RIF, UCHAR * Call, UCHAR * Alias, int Hops, int RTT, char * Dest)
{
int AliasLen;
int RIFLen;
@ -1090,13 +1115,13 @@ int BuildRIF(UCHAR * RIF, UCHAR * Call, UCHAR * Alias, int Hops, int RTT)
RIF[12+AliasLen] = 0;
RIFLen = 13 + AliasLen;
if (DEBUGINP3) Debugprintf("INP3 sending RIF Entry %s:%s %d %d", AliasCopy, Normcall, Hops, RTT);
if (DEBUGINP3) Debugprintf("INP3 sending RIF Entry %s:%s %d %d to %s", AliasCopy, Normcall, Hops, RTT, Dest);
return RIFLen;
}
RIF[10] = 0;
if (DEBUGINP3) Debugprintf("INP3 sending RIF Entry %s %d %d", Normcall, Hops, RTT);
if (DEBUGINP3) Debugprintf("INP3 sending RIF Entry %s %d %d to %s", Normcall, Hops, RTT, Dest);
return (11);
}
@ -1109,6 +1134,15 @@ VOID SendOurRIF(struct ROUTE * Route)
int totLen = 1;
int App;
APPLCALLS * APPL;
int sendTT = Route->RTTIncrement;
char Normcall[10];
Normcall[ConvFromAX25(Route->NEIGHBOUR_CALL, Normcall)] = 0;
if (DEBUGINP3) Debugprintf("INP3 Sending Our Call and Applcalls to %s ", Normcall);
if (Route->OldBPQ) // old bpq bug - send mS not 10 mS units
sendTT *= 10;
Msg = GetBuff();
if (Msg == 0)
@ -1118,11 +1152,7 @@ VOID SendOurRIF(struct ROUTE * Route)
// send a RIF for our Node and all our APPLCalls
if (Route->OldBPQ)
RIFLen = BuildRIF(&Msg->L3SRCE[totLen], MYCALL, MYALIASTEXT, 1, Route->RTTIncrement * 10);
else
RIFLen = BuildRIF(&Msg->L3SRCE[totLen], MYCALL, MYALIASTEXT, 1, Route->RTTIncrement);
RIFLen = BuildRIF(&Msg->L3SRCE[totLen], MYCALL, MYALIASTEXT, 1, sendTT, Normcall);
totLen += RIFLen;
for (App = 0; App < NumberofAppls; App++)
@ -1131,11 +1161,7 @@ VOID SendOurRIF(struct ROUTE * Route)
if (APPL->APPLQUAL > 0)
{
if (Route->OldBPQ)
RIFLen = BuildRIF(&Msg->L3SRCE[totLen], APPL->APPLCALL, APPL->APPLALIAS_TEXT, 1, Route->RTTIncrement * 10);
else
RIFLen = BuildRIF(&Msg->L3SRCE[totLen], APPL->APPLCALL, APPL->APPLALIAS_TEXT, 1, Route->RTTIncrement);
RIFLen = BuildRIF(&Msg->L3SRCE[totLen], APPL->APPLCALL, APPL->APPLALIAS_TEXT, 1, sendTT, Normcall);
totLen += RIFLen;
}
}
@ -1185,6 +1211,12 @@ int SendRIPTimer()
}
}
if (Route->Stopped)
{
Route++;
continue;
}
// Delay more if Locked - they could be retrying for a long time
if (Route->ConnectionAttempts < 5)
@ -1352,8 +1384,9 @@ VOID SendRIF(struct ROUTE * Route, struct _L3MESSAGEBUFFER * Msg)
SendNetFrame(Route, Msg);
}
VOID SendRIFToOtherNeighbours(UCHAR * axcall, UCHAR * alias, struct INP3_DEST_ROUTE_ENTRY * Entry, int Negative, int portNum)
VOID SendRIFToOtherNeighbours(struct DEST_LIST * Dest, UCHAR * alias, struct INP3_DEST_ROUTE_ENTRY * Entry, int Negative, int portNum)
{
UCHAR * axcall = Dest->DEST_CALL;
struct ROUTE * Routes = NEIGHBOURS;
struct _L3MESSAGEBUFFER * Msg;
int count, MaxRoutes = MAXNEIGHBOURS;
@ -1368,17 +1401,19 @@ VOID SendRIFToOtherNeighbours(UCHAR * axcall, UCHAR * alias, struct INP3_DEST_RO
for (count = 0; count < MaxRoutes; count++)
{
if ((Entry->ROUT_NEIGHBOUR && Routes->INP3Node) &&
(Routes->Status) &&
// (memcmp(Routes->NEIGHBOUR_CALL
(Routes != Entry->ROUT_NEIGHBOUR)) // Dont send to originator of route
if (Routes->INP3Node && Routes->Status && Routes != Entry->ROUT_NEIGHBOUR)
{
// as the value sent will be different for each link, we need to check if change is enough here
sendHops = Entry->Hops + 1;
sendTT = Entry->STT + Entry->ROUT_NEIGHBOUR->RTTIncrement;
lastTT = Entry->RouteLastTT[Entry->ROUT_NEIGHBOUR->recNum];
if (Entry->STT < 60000)
sendTT = Entry->STT + Routes->RTTIncrement;
else
sendTT = 60000;
lastTT = Dest->RouteLastTT[Routes->recNum];
destCall[ConvFromAX25(Routes->NEIGHBOUR_CALL, destCall)] = 0;
if (!portNum)
{
@ -1405,11 +1440,14 @@ VOID SendRIFToOtherNeighbours(UCHAR * axcall, UCHAR * alias, struct INP3_DEST_RO
}
// Don't send if Node is the Neighbour we are sending to
if (DEBUGINP3) Debugprintf("INP3 SendRIFToOtherNeighbours need to send %s to %s", NodeCall, destCall);
// Don't send if Node is the Neighbour we are sending to
if (memcmp(Routes->NEIGHBOUR_CALL, axcall, 7) == 0)
{
if (DEBUGINP3) Debugprintf("INP3 SendRIFToOtherNeighbours Don't send %s to itself", NodeCall);
Dest->RouteLastTT[Routes->recNum] = sendTT; // But update or we will keep re-entering
Routes+=1;
continue;
}
@ -1423,13 +1461,19 @@ VOID SendRIFToOtherNeighbours(UCHAR * axcall, UCHAR * alias, struct INP3_DEST_RO
if (portNum)
Routes->Status &= ~SentOurRIF;
Entry->RouteLastTT[Entry->ROUT_NEIGHBOUR->recNum] = sendTT;
Dest->RouteLastTT[Routes->recNum] = sendTT;
// send, but only if within their constraints
if ((Routes->RemoteMAXHOPS == 0 || Routes->RemoteMAXHOPS >= Entry->Hops) &&
// Does it make any sense to send a node with hopcount of say 2 which was received from a node with
// maxhops 2. The next hop (with hopcount of 3 or above) will get it but won't be able to reply.
if ((Routes->RemoteMAXHOPS == 0 || Routes->RemoteMAXHOPS >= sendHops) &&
(Routes->RemoteMAXRTT == 0 || Routes->RemoteMAXRTT >= sendTT || sendTT == 60000))
{
if (sendTT == 60000)
sendHops = 31;
if (DEBUGINP3)
{
if (portNum)
@ -1442,26 +1486,18 @@ VOID SendRIFToOtherNeighbours(UCHAR * axcall, UCHAR * alias, struct INP3_DEST_RO
if (Msg == NULL)
{
if (DEBUGINP3)
{
destCall[ConvFromAX25(Entry->ROUT_NEIGHBOUR->NEIGHBOUR_CALL, destCall)] = 0;
Debugprintf("INP3 Building RIF to send to %s", destCall);
}
if (DEBUGINP3) Debugprintf("INP3 Building RIF to send to %s", destCall);
Msg = Routes->Msg = CreateRIFHeader(Routes);
}
if (Msg)
{
if (Routes->OldBPQ)
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH],
axcall, alias, sendHops, sendTT + 10);
else
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH],
axcall, alias, sendHops, sendTT);
if (Routes->OldBPQ) // old bpq bug - send mS not 10 mS units
sendTT *= 10;
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], axcall, alias, sendHops, sendTT, destCall);
if (Msg->LENGTH > 250 - 15)
// if (Msg->LENGTH > Routes->NBOUR_PACLEN - 11)
{
SendRIF(Routes, Msg);
Routes->Msg = NULL;
@ -1469,8 +1505,6 @@ VOID SendRIFToOtherNeighbours(UCHAR * axcall, UCHAR * alias, struct INP3_DEST_RO
}
}
}
Routes+=1;
}
}
@ -1482,9 +1516,11 @@ VOID SendRIFToNewNeighbour(struct ROUTE * Route)
struct INP3_DEST_ROUTE_ENTRY * Entry;
struct _L3MESSAGEBUFFER * Msg;
int sendHops, sendTT;
char Normcall[10];
if (Route->NEIGHBOUR_LINK == 0) // shouldn't happen but to be safe..
return;
Normcall[ConvFromAX25(Route->NEIGHBOUR_LINK->LINKCALL, Normcall)] = 0;
if (DEBUGINP3) Debugprintf("INP3 Sending Our Table to %s ", Normcall);
@ -1498,7 +1534,6 @@ VOID SendRIFToNewNeighbour(struct ROUTE * Route)
Entry = &Dest->INP3ROUTE[0];
if (Entry->ROUT_NEIGHBOUR && Entry->Hops && Route != Entry->ROUT_NEIGHBOUR)
{
// Best Route not via this neighbour - send, but only if within their constraints
@ -1506,9 +1541,9 @@ VOID SendRIFToNewNeighbour(struct ROUTE * Route)
sendHops = Entry->Hops + 1;
sendTT = Entry->STT + Entry->ROUT_NEIGHBOUR->RTTIncrement;
Entry->RouteLastTT[Entry->ROUT_NEIGHBOUR->recNum] = sendTT;
Dest->RouteLastTT[Entry->ROUT_NEIGHBOUR->recNum] = sendTT;
if ((Route->RemoteMAXHOPS == 0 || Route->RemoteMAXHOPS >= Entry->Hops) &&
if ((Route->RemoteMAXHOPS == 0 || Route->RemoteMAXHOPS >= Entry->Hops || Entry->Hops > 30) &&
(Route->RemoteMAXRTT == 0 || Route->RemoteMAXRTT >= Entry->STT || Entry->STT == 60000))
{
Msg = Route->Msg;
@ -1519,10 +1554,10 @@ VOID SendRIFToNewNeighbour(struct ROUTE * Route)
if (Msg == NULL)
return;
if (Route->OldBPQ)
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], Dest->DEST_CALL, Dest->DEST_ALIAS, sendHops, sendTT * 10); // old bpq bug - send mS not 10 mS units
else
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], Dest->DEST_CALL, Dest->DEST_ALIAS, sendHops, sendTT);
if (Route->OldBPQ) // old bpq bug - send mS not 10 mS units
sendTT *= 10;
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], Dest->DEST_CALL, Dest->DEST_ALIAS, sendHops, sendTT, Normcall);
if (Msg->LENGTH > 250 - 15)
{
@ -1541,30 +1576,30 @@ VOID SendRIFToNewNeighbour(struct ROUTE * Route)
VOID FlushRIFs()
{
struct ROUTE * Routes = NEIGHBOURS;
struct ROUTE * Route = NEIGHBOURS;
int count, MaxRoutes = MAXNEIGHBOURS;
for (count=0; count<MaxRoutes; count++)
{
// Make sure we've sent our local calls
if ((Routes->Status & GotRTTRequest) && (Routes->Status & GotRTTResponse) && ((Routes->Status & SentOurRIF) == 0))
if ((Route->Status & GotRTTRequest) && (Route->Status & GotRTTResponse) && ((Route->Status & SentOurRIF) == 0))
{
Routes->Status |= SentOurRIF;
SendOurRIF(Routes);
SendRIFToNewNeighbour(Routes);
Route->Status |= SentOurRIF;
SendOurRIF(Route);
SendRIFToNewNeighbour(Route);
}
if (Routes->Msg)
if (Route->Msg)
{
char Normcall[10];
Normcall[ConvFromAX25(Routes->NEIGHBOUR_CALL, Normcall)] = 0;
SendRIF(Routes, Routes->Msg);
Routes->Msg = NULL;
Normcall[ConvFromAX25(Route->NEIGHBOUR_CALL, Normcall)] = 0;
if (DEBUGINP3) Debugprintf("INP3 Flushing RIF to %s", Normcall);
SendRIF(Route, Route->Msg);
Route->Msg = NULL;
}
Routes+=1;
Route+=1;
}
}
@ -1575,7 +1610,6 @@ VOID SendNegativeInfo()
struct INP3_DEST_ROUTE_ENTRY * Entry;
char call[11]="";
Dest--;
// Send RIF for any Dests that have got worse
@ -1590,38 +1624,48 @@ VOID SendNegativeInfo()
{
Dest++;
if (Dest->DEST_CALL[0] == 0) // unused entry
continue;
Entry = &Dest->INP3ROUTE[0];
if (Entry->ROUT_NEIGHBOUR == 0)
continue;
if (Entry->RouteLastTT[Entry->ROUT_NEIGHBOUR->recNum] == 0) // if zero haven't yet reported +ve info. Shouldn't really be reporting negative without positive but just in case
SendRIFToOtherNeighbours(Dest->DEST_CALL, Dest->DEST_ALIAS, Entry, TRUE, FALSE);
else
SendRIFToOtherNeighbours(Dest->DEST_CALL, 0, Entry, TRUE, FALSE);
SendRIFToOtherNeighbours(Dest, Dest->DEST_ALIAS, Entry, TRUE, FALSE);
if (Entry->STT >= 60000)
{
// It is dead, and we have reported it if necessary, so remove if no NETROM Routes
if (Dest->NRROUTE[0].ROUT_NEIGHBOUR == 0) // No more Netrom Routes
// Wrong. We may have other INP3 routes. Move them up. This will delete first if only one
// I think I need to set lastTT on all routes.
if (Dest->INP3ROUTE[1].ROUT_NEIGHBOUR == 0) // No other INP3 routes
{
char call[11]="";
ConvFromAX25(Dest->DEST_CALL, call);
if (DEBUGINP3) Debugprintf("INP3 Deleting Node %s", call);
REMOVENODE(Dest); // Clear buffers, Remove from Sorted Nodes chain, and zap entry
if (DEBUGINP3) Debugprintf("Was the only INP3 route");
memset(&Dest->INP3ROUTE[0], 0, sizeof(struct INP3_DEST_ROUTE_ENTRY));
}
else
{
// Have a NETROM route, so zap the INP3 one
memset(Dest->RouteLastTT, 0, MAXNEIGHBOURS * sizeof(uint16_t)); // So next scan will check if rtt has increaced enough to need a RIF
memcpy(&Dest->INP3ROUTE[0], &Dest->INP3ROUTE[1], sizeof(struct INP3_DEST_ROUTE_ENTRY));
memcpy(&Dest->INP3ROUTE[1], &Dest->INP3ROUTE[2], sizeof(struct INP3_DEST_ROUTE_ENTRY));
memset(&Dest->INP3ROUTE[2], 0, sizeof(struct INP3_DEST_ROUTE_ENTRY));
NegTimerCount = 0; // Send negative info again asap to send new best
}
memset(Entry, 0, sizeof(struct INP3_DEST_ROUTE_ENTRY));
if (Dest->INP3ROUTE[0].ROUT_NEIGHBOUR == 0 && Dest->NRROUTE[0].ROUT_NEIGHBOUR == 0) // No INP3 and no Netrom Routes
{
char call[11]="";
ConvFromAX25(Dest->DEST_CALL, call);
if (DEBUGINP3) Debugprintf("INP3 No INP3 and no Netrom Routes left - Deleting Node %s", call);
REMOVENODE(Dest); // Clear buffers, Remove from Sorted Nodes chain, and zap entry
}
if (Dest->DEST_ROUTE == 4) // we were using it
Dest->DEST_ROUTE = 0;
Dest->DEST_ROUTE = 0;
}
}
}
@ -1644,7 +1688,9 @@ VOID SendPositiveInfo()
continue;
Entry = &Dest->INP3ROUTE[0];
SendRIFToOtherNeighbours(Dest->DEST_CALL, 0, Entry, FALSE, FALSE);
if (Entry->ROUT_NEIGHBOUR)
SendRIFToOtherNeighbours(Dest, Dest->DEST_ALIAS, Entry, FALSE, FALSE);
}
}
@ -1664,11 +1710,14 @@ VOID SendNewInfo()
if (Dest->INP3FLAGS & NewNode)
{
char call[10];
ConvFromAX25(Dest->DEST_CALL, call);
if (DEBUGINP3) Debugprintf("INP3 Sending New Node %s", call);
Dest->INP3FLAGS &= ~NewNode;
Entry = &Dest->INP3ROUTE[0];
SendRIFToOtherNeighbours(Dest->DEST_CALL, Dest->DEST_ALIAS, Entry, FALSE, FALSE);
SendRIFToOtherNeighbours(Dest, Dest->DEST_ALIAS, Entry, TRUE, FALSE); // Send as negative so will always be worse than zero
}
}
}
@ -1705,9 +1754,9 @@ VOID sendAlltoOneNeigbour(struct ROUTE * Route)
return;
if (Route->OldBPQ)
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], MYCALL, MYALIASTEXT, 1, Route->RTTIncrement * 10);
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], MYCALL, MYALIASTEXT, 1, Route->RTTIncrement * 10, Call);
else
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], MYCALL, MYALIASTEXT, 1, Route->RTTIncrement);
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], MYCALL, MYALIASTEXT, 1, Route->RTTIncrement, Call);
for (App = 0; App < NumberofAppls; App++)
{
@ -1716,9 +1765,9 @@ VOID sendAlltoOneNeigbour(struct ROUTE * Route)
if (APPL->APPLQUAL > 0)
{
if (Route->OldBPQ)
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], APPL->APPLCALL, APPL->APPLALIAS_TEXT, 1, Route->RTTIncrement * 10);
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], APPL->APPLCALL, APPL->APPLALIAS_TEXT, 1, Route->RTTIncrement * 10, Call);
else
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], APPL->APPLCALL, APPL->APPLALIAS_TEXT, 1, Route->RTTIncrement);
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], APPL->APPLCALL, APPL->APPLALIAS_TEXT, 1, Route->RTTIncrement, Call);
}
}
@ -1751,9 +1800,9 @@ VOID sendAlltoOneNeigbour(struct ROUTE * Route)
sendHops = Entry->Hops + 1;
sendTT = Entry->STT + Entry->ROUT_NEIGHBOUR->RTTIncrement;
lastTT = Entry->RouteLastTT[Entry->ROUT_NEIGHBOUR->recNum];
lastTT = Dest->RouteLastTT[Entry->ROUT_NEIGHBOUR->recNum];
Entry->RouteLastTT[Entry->ROUT_NEIGHBOUR->recNum] = sendTT;
Dest->RouteLastTT[Entry->ROUT_NEIGHBOUR->recNum] = sendTT;
// send, but only if within their constraints
@ -1769,7 +1818,7 @@ VOID sendAlltoOneNeigbour(struct ROUTE * Route)
if (Route->OldBPQ)
sendTT *= 10;
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], Dest->DEST_CALL, Dest->DEST_ALIAS, sendHops, sendTT);
Msg->LENGTH += BuildRIF(&Msg->L3SRCE[Msg->LENGTH], Dest->DEST_CALL, Dest->DEST_ALIAS, sendHops, sendTT, Call);
if (Msg->LENGTH > 250 - 15)
{
@ -1853,6 +1902,8 @@ VOID INP3TIMER()
#endif
SendNewInfo(); // Need to send to set up last sent time
if (NegTimerCount == 0)
{
NegTimerCount = NegativeDelay;
@ -1864,7 +1915,7 @@ VOID INP3TIMER()
if (RIPTimerCount == 0)
{
RIPTimerCount = 10;
SendNewInfo(); // Not quite so urgent
SendRIPTimer();
SendAllInfo(); // Timer Driven refresh
}

Binary file not shown.

@ -1118,7 +1118,7 @@
// Fix recently introduced crash when "Don't allow new users" is set (81)
// Skip comments before TIMES at start of Connect Script (83)
// 6.0.25.1 ??
// 6.0.25.1 Sept 2025
// Add FBB reject.sys style filters (3)
// Improve Webmail on 64 bit builds
@ -1157,6 +1157,10 @@
// Fix sending + in Webmail (80)
// Fix forwarding problem when using Web interface to change message routing (73)
// 6.0.26.x
// Fix forwarding to/from AEA TNC mailbox (22)
#include "bpqmail.h"
#include "winstdint.h"
#define MAIL

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1308,6 +1308,13 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
// Don't reset NS on RR R(F) following I(P) just on RR poll following timeout. Can get problems with delayed RR R(F) (reverted) (14)
// Ignore packets that would cause an FRMR and respond to FRMR with DM (14)
// Add option to send periodic INP3 RIF refresh (15)
// Add RHP and STREAMS nodes commands (16)
// Fix possible crash in telnet connected to application processing (16)
// Fix NodeAPI /tcpqueues output for local telnet sessions (16)
// Fix handling of disconnects when using RHP (17)
// Fix propagating unreachable in INP3 (18)
// Add STOPROUTE command (22)
@ -1528,6 +1535,7 @@ VOID PMClose();
VOID MySetWindowText(HWND hWnd, char * Msg);
BOOL CreateMonitorWindow(char * MonSize);
VOID FormatTime3(char * Time, time_t cTime);
void * zalloc(int len);
char EXCEPTMSG[80] = "";
@ -2563,7 +2571,7 @@ FirstInit()
return 0;
}
Check_Timer()
int Check_Timer()
{
if (Closing)
return 0;
@ -3901,6 +3909,9 @@ BOOL UpdateNodesForApp(int Appl)
NUMBEROFNODES++;
APPL->NODEPOINTER = DEST;
if (DEST->RouteLastTT == 0)
DEST->RouteLastTT = (uint16_t *)zalloc(MAXNEIGHBOURS * sizeof(uint16_t));
memmove (DEST->DEST_CALL,APPL->APPLCALL,13);
DEST->DEST_STATE=0x80; // SPECIAL ENTRY

273
Cmd.c

@ -72,6 +72,7 @@ int CompareRoutes(const void * a, const void * b);
void SendVARANetromNodes(struct TNCINFO * TNC, MESSAGE *Buffer);
VOID DoNetromConnect(TRANSPORTENTRY * Session, char * Bufferptr, struct DEST_LIST * Dest, BOOL Spy, int Service);
VOID sendAlltoOneNeigbour(struct ROUTE * Route);
VOID CMDSTREAMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
extern VOID KISSTX(struct KISSINFO * KISS, PMESSAGE Buffer);
@ -171,6 +172,8 @@ VOID STOPCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct
VOID STARTCMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID STOPPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID STOPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID STARTROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID FINDBUFFS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID WL2KSYSOP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
VOID AXRESOLVER(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
@ -189,7 +192,7 @@ VOID HELPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct
VOID UZ7HOCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD);
VOID QTSMCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * UserCMD);
void hookL2SessionAttempt(int Port, char * fromCall, char * toCall, struct _LINKTABLE * LINK);
VOID RHPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);
/* Paula's NetROMX includes a service number in a CREQX message which allows a node to host lots of applications without
filling the Nodes table with SSID's
@ -4692,6 +4695,8 @@ struct CMDX COMMANDS[] =
"STARTPORT ",5,STARTPORT,0,
"STOPCMS ",7,STOPCMS,0,
"STARTCMS ",8,STARTCMS,0,
"STOPROUTE ",9,STOPROUTE,0,
"STARTROUTE ",10,STARTROUTE,0,
"FINDBUFFS ",4,FINDBUFFS,0,
"KISS ",4,KISSCMD,0,
@ -4781,6 +4786,8 @@ struct CMDX COMMANDS[] =
"ROUTES ",1,CMDR00,0,
"STATS ",1,CMDSTATS,0,
"USERS ",1,CMDS00,0,
"STREAMS ",6,CMDSTREAMS,0,
"RHP ",3,RHPCMD,0,
"UNPROTO ",2,UNPROTOCMD,0,
"? ",1,CMDQUERY,0,
"DUMP ",4,DUMPCMD,0,
@ -5992,6 +5999,132 @@ VOID STARTPORT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struc
return;
}
BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE);
VOID DecayNETROMRoutes(struct ROUTE * Route);
VOID DeleteINP3Routes(struct ROUTE * Route);
void NETROMCloseTCP(struct ROUTE * Route);
VOID STOPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
char _REPLYBUFFER[1000] = "";
char * ptr, * Context, * Call;
int portno;
unsigned char Neighbour[7];
struct ROUTE * Route;
// STOPROUTE PORT CALL
// Get port number and call
ptr = strtok_s(CmdTail, " ", &Context);
Call = strtok_s(NULL, " \r", &Context);
if (ptr && Call && Call[0])
{
portno = atoi (ptr);
ConvToAX25(Call, Neighbour);
}
else
{
Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Params - Should be STOPROUTE PORT CALL\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
if (FindNeighbour(Neighbour, portno, &Route) == 0)
{
Bufferptr = Cmdprintf(Session, Bufferptr, "Route not found\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
if (Route->NEIGHBOUR_LINK == 0 || Route->NEIGHBOUR_LINK->LINKPORT == 0)
{
// Not active
Bufferptr = Cmdprintf(Session, Bufferptr, "Route not active\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
Route->Stopped = 1;
DecayNETROMRoutes(Route);
DeleteINP3Routes(Route);
Route->Status = 0; // Down
// close the link
if (Route->TCPPort == 0) // NetromTCP doesn't have a real link
{
Route->NEIGHBOUR_LINK->KILLTIMER = 0;
Route->NEIGHBOUR_LINK->L2TIMER = 1; // TO FORCE DISC
Route->NEIGHBOUR_LINK->L2STATE = 4; // DISCONNECTING
}
else
{
// but we should reset the TCP connection
NETROMCloseTCP(Route);
}
Route->BCTimer = 999999; // Wait a while before retrying
Bufferptr = Cmdprintf(Session, Bufferptr, "Route Closed\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
VOID STARTROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
char _REPLYBUFFER[1000] = "";
char * ptr, * Context, * Call;
int portno;
unsigned char Neighbour[7];
struct ROUTE * Route;
// STARTROUTE PORT CALL
// Get port number and call
ptr = strtok_s(CmdTail, " ", &Context);
Call = strtok_s(NULL, " \r", &Context);
if (ptr && Call && Call[0])
{
portno = atoi (ptr);
ConvToAX25(Call, Neighbour);
}
else
{
Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Params - Should be STARTROUTE PORT CALL\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
if (FindNeighbour(Neighbour, portno, &Route) == 0)
{
Bufferptr = Cmdprintf(Session, Bufferptr, "Route not found\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
if (Route->Stopped == 0)
{
Bufferptr = Cmdprintf(Session, Bufferptr, "Route not stopped\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
Route->Stopped = 0;
Route->BCTimer = 0;
Bufferptr = Cmdprintf(Session, Bufferptr, "Route Enabled\r");
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}
int ASYSEND(struct PORTCONTROL * PortVector, char * buffer, int count);
@ -6359,11 +6492,149 @@ VOID QTSMCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct
return;
}
DllExport int APIENTRY Get_APPLMASK(int Stream);
DllExport int APIENTRY GetStreamPID(int Stream);
DllExport int APIENTRY GetApplFlags(int Stream);
DllExport int APIENTRY GetApplNum(int Stream);
DllExport int APIENTRY GetApplMask(int Stream);
DllExport BOOL APIENTRY GetAllocationState(int Stream);
int Check_Timer();
DllExport int APIENTRY GetCallsignNoSem(int stream, char * callsign)
{
// Returns call connected on stream (BPQHOST function 8 (part)).
BPQVECSTRUC * SESS;
TRANSPORTENTRY * L4;
TRANSPORTENTRY * Partner;
UCHAR Call[11] = "SWITCH ";
UCHAR * AXCall = NULL;
Check_Timer();
stream--; // API uses 1 - 64
if (stream < 0 || stream > 63)
return 0;
SESS = &BPQHOSTVECTOR[stream];
L4 = SESS->HOSTSESSION;
if (L4 == 0)
{
return 0;
}
Partner = L4->L4CROSSLINK;
if (Partner)
{
// CONNECTED OUT - GET TARGET SESSION
if (Partner->L4CIRCUITTYPE & BPQHOST)
{
AXCall = &Partner->L4USER[0];
}
else if (Partner->L4CIRCUITTYPE & L2LINK)
{
struct _LINKTABLE * LINK = Partner->L4TARGET.LINK;
if (LINK)
AXCall = LINK->LINKCALL;
if (Partner->L4CIRCUITTYPE & UPLINK)
{
// IF UPLINK, SHOULD USE SESSION CALL, IN CASE *** LINKED HAS BEEN USED
AXCall = &Partner->L4USER[0];
}
}
else if (Partner->L4CIRCUITTYPE & PACTOR)
{
// PACTOR Type - Frames are queued on the Port Entry
EXTPORTDATA * EXTPORT = Partner->L4TARGET.EXTPORT;
if (EXTPORT)
AXCall = &EXTPORT->ATTACHEDSESSIONS[Partner->KAMSESSION]->L4USER[0];
}
else
{
// MUST BE NODE SESSION
// ANOTHER NODE
// IF THE HOST IS THE UPLINKING STATION, WE NEED THE TARGET CALL
if (L4->L4CIRCUITTYPE & UPLINK)
{
struct DEST_LIST *DEST = Partner->L4TARGET.DEST;
if (DEST)
AXCall = &DEST->DEST_CALL[0];
}
else
AXCall = Partner->L4USER;
}
if (AXCall)
ConvFromAX25(AXCall, Call);
}
memcpy(callsign, Call, 10);
return 0;
}
VOID CMDSTREAMS(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
int i;
char callsign[12] = "";
char flag[3];
UINT Mask, MaskCopy;
int Flags;
int AppNumber;
int OneBits;
Bufferptr = Cmdprintf(Session, Bufferptr, "\r| | RX | TX | MON |App|Flg| Callsign | Program |\r");
for (i=1; i <=BPQHOSTSTREAMS; i++)
{
callsign[0]=0;
if (GetAllocationState(i) == 0)
continue;
strcpy(flag,"*");
GetCallsignNoSem(i,callsign);
Mask = MaskCopy = Get_APPLMASK(i);
// if only one bit set, convert to number
AppNumber = 0;
OneBits = 0;
while (MaskCopy)
{
if (MaskCopy & 1)
OneBits++;
AppNumber++;
MaskCopy = MaskCopy >> 1;
}
Flags=GetApplFlags(i);
if (OneBits > 1)
Bufferptr = Cmdprintf(Session, Bufferptr, "|%2d%s|%4d|%4d|%5d|%3x|%3x|%10s|%-15s|\r", i, flag, RXCount(i), TXCount(i), MONCount(i), Mask, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName);
else
Bufferptr = Cmdprintf(Session, Bufferptr, "|%2d%s|%4d|%4d|%5d|%3d|%3x|%10s|%-15s|\r",i, flag, RXCount(i), TXCount(i), MONCount(i), AppNumber, Flags, callsign, BPQHOSTVECTOR[i-1].PgmName);
}
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}

@ -395,16 +395,54 @@ BOK1:
return 0;
}
void CheckFreeQueue(char * File, int Line)
{
void ** pointer;
int n = 0;
void ** debug;
PMESSAGE Test;
pointer = FREE_Q;
debug = &FREE_Q;
while (pointer)
{
// Validate pointer to make sure it is in pool - it may be a duff address if Q is corrupt
Test = (PMESSAGE)pointer;
if (Test->GuardZone || (uintptr_t)pointer < (uintptr_t)BUFFERPOOL || (uintptr_t)pointer > (uintptr_t)ENDBUFFERPOOL)
{
// Not pointing to a buffer . debug points to the buffer that this is chained from
Debugprintf("CheckFree Queue Pool Corruption n = %d, ptr %p prev %p from %s Line %d", n, pointer, debug, File, Line);
}
debug = pointer;
pointer = pointer[0];
n++;
if (n > 1000)
{
Debugprintf("CheckFreeQueue Loop searching free chain - pointer = %p %p from %s Line %d", debug, pointer, File, Line);
FindLostBuffers();
WriteMiniDump();
Restart();
}
}
}
int _C_Q_ADD(VOID *PQ, VOID *PBUFF, char * File, int Line)
{
void ** Q;
void ** BUFF = PBUFF;
void ** next;
PMESSAGE Test;
int n = 0;
CheckFreeQueue(File, Line);
// PQ may not be word aligned, so copy as bytes (for ARM5)
Q = PQ;
@ -532,6 +570,8 @@ VOID * _GetBuff(char * File, int Line)
char * fptr = 0;
unsigned char * byteaddr;
CheckFreeQueue(File, Line);
Temp = Q_REM(&FREE_Q);
// FindLostBuffers();

@ -175,8 +175,8 @@ VOID CHOSTAPI(ULONG * pEAX, ULONG * pEBX, ULONG * pECX, ULONG * pEDX, VOID ** pE
Stream = (EAX & 0xFF);
n = Stream - 1; // API Numbers Streams 1-64
if (n < 0 || n > 63)
n = 64;
if (n < 0 || n > BPQHOSTSTREAMS - 1)
n = BPQHOSTSTREAMS;
HostVec = &BPQHOSTVECTOR[n];
Session = HostVec->HOSTSESSION;

@ -752,7 +752,13 @@ void APIL2Trace(struct _MESSAGE * Message, char * Dirn)
int NR;
struct PORTCONTROL * PORT = GetPortTableEntryFromPortNum(Message->PORT);
time_t Now = time(NULL);
#ifdef LINBPQ
struct timespec ts;
#else
FILETIME SystemTimeAsFileTime;
ULARGE_INTEGER FT64;
#endif
double NowMs;
if (PORT == 0)
return;
@ -776,6 +782,20 @@ void APIL2Trace(struct _MESSAGE * Message, char * Dirn)
if ((Message->ORIGIN[6] & 1) == 0) // Digis - ignore for now
return;
#ifdef LINBPQ
if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
NowMs = Now;
else
NowMs = ts.tv_sec + ts.tv_nsec / 1000000000.0;
#else
GetSystemTimeAsFileTime(&SystemTimeAsFileTime);
// FILETIME is 100 nS interval since 1601 - subtract 116444736000000000 to get 100 nS intervels since 1970 (Unix Time)
FT64.HighPart = SystemTimeAsFileTime.dwHighDateTime;
FT64.LowPart = SystemTimeAsFileTime.dwLowDateTime;
NowMs = (FT64.QuadPart - 116444736000000000) / 10000000.0;
#endif
if ((Message->DEST[6] & 0x80) == 0 && (Message->ORIGIN[6] & 0x80) == 0)
strcpy(CR, "V1");
else if ((Message->DEST[6] & 0x80))
@ -898,9 +918,9 @@ void APIL2Trace(struct _MESSAGE * Message, char * Dirn)
// Common to all frame types
udplen = snprintf(UDPMsg, 2048,
"{\"@type\": \"L2Trace\", \"serial\": %d, \"time\": %d, \"dirn\": \"%s\", \"isRF\": %s, \"reportFrom\": \"%s\", \"port\": \"%d\", \"srce\": \"%s\", \"dest\": \"%s\", \"ctrl\": %d,"
"{\"@type\": \"L2Trace\", \"serial\": %d, \"time\": %.3f, \"dirn\": \"%s\", \"isRF\": %s, \"reportFrom\": \"%s\", \"port\": \"%d\", \"srce\": \"%s\", \"dest\": \"%s\", \"ctrl\": %d,"
"\"l2Type\": \"%s\", \"modulo\": 8, \"cr\": \"%s\"",
UDPSeq++, (int)Now, Dirn, (PORT->isRF)?"true":"false", NODECALLLOPPED, Message->PORT, srcecall, destcall, Message->CTL, Type, CR);
UDPSeq++, NowMs, Dirn, (PORT->isRF)?"true":"false", NODECALLLOPPED, Message->PORT, srcecall, destcall, Message->CTL, Type, CR);
if (UIFlag)
{
@ -949,12 +969,10 @@ void APIL2Trace(struct _MESSAGE * Message, char * Dirn)
udplen += snprintf(&UDPMsg[udplen], 2048 - udplen, ", \"rseq\": %d", NR);
}
UDPMsg[udplen++] = '}';
UDPMsg[udplen] = 0;
// Debugprintf(UDPMsg);
Debugprintf(UDPMsg);
sendto(NodeAPISocket, UDPMsg, udplen, 0, (struct sockaddr *)&UDPreportdest, sizeof(UDPreportdest));
}

@ -4319,7 +4319,7 @@ int StatusProc(char * Buff)
Len += sprintf(&Buff[Len], "<th>&nbsp;MON&nbsp;</th><th>&nbsp;App&nbsp;</th><th>&nbsp;Flg&nbsp;</th>");
Len += sprintf(&Buff[Len], "<th>Callsign&nbsp;&nbsp;</th><th width=200px>Program</th></tr><tr>");
for (i=1;i<65; i++)
for (i=1; i <=BPQHOSTSTREAMS; i++)
{
callsign[0]=0;

@ -779,7 +779,7 @@ VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buff
}
}
// IF CALL REQUEST IS FROM A LOCKED NODE WITH QUALITY ZERO, IGNORE IT
// IF CALL REQUEST IS FROM A LOCKED NODE WITH QUALITY ZERO or Stopped, IGNORE IT
if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE))
{
@ -787,7 +787,7 @@ VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buff
NO_CTEXT = 1;
if (ROUTE->NEIGHBOUR_FLAG && ROUTE->NEIGHBOUR_QUAL == 0) // Locked, qual 0
if (ROUTE->Stopped || (ROUTE->NEIGHBOUR_FLAG && ROUTE->NEIGHBOUR_QUAL == 0)) // Stopped or Locked, qual 0
{
ReleaseBuffer(Buffer);
return;
@ -1360,7 +1360,6 @@ VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffe
// if it is an INP3 connection tell INP3 it is up
if (FindNeighbour(LINK->LINKCALL, PORT->PORTNUMBER, &ROUTE))
{
if (ROUTE->INP3Node)

@ -403,6 +403,8 @@ VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT)
memset(DEST, 0, sizeof(struct DEST_LIST));
memcpy(DEST->DEST_CALL, Msg->ORIGIN, 7);
if (DEST->RouteLastTT == 0)
DEST->RouteLastTT = (uint16_t *)zalloc(MAXNEIGHBOURS * sizeof(uint16_t));
NUMBEROFNODES++;
}
@ -582,6 +584,9 @@ VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT)
memset(DEST, 0, sizeof(struct DEST_LIST));
memcpy(DEST->DEST_CALL, ptr1, 7);
if (DEST->RouteLastTT == 0)
DEST->RouteLastTT = (uint16_t *)zalloc(MAXNEIGHBOURS * sizeof(uint16_t));
NUMBEROFNODES++;
}
@ -1369,6 +1374,10 @@ VOID REMOVENODE(dest_list * DEST)
}
L4++;
}
if (DEST->RouteLastTT)
free(DEST->RouteLastTT);
memset(DEST, 0, sizeof(struct DEST_LIST));
NUMBEROFNODES--;
}
@ -1524,6 +1533,9 @@ struct DEST_LIST * CHECKL3TABLES(struct _LINKTABLE * LINK, L3MESSAGEBUFFER * Msg
memcpy(DEST->DEST_CALL, Msg->L3SRCE, 7);
if (DEST->RouteLastTT == 0)
DEST->RouteLastTT = (uint16_t *)zalloc(MAXNEIGHBOURS * sizeof(uint16_t));
NUMBEROFNODES++;
// MAKE SURE NEIGHBOUR IS DEFINED FOR DESTINATION

@ -274,7 +274,7 @@ VOID ProcessMBLLine(CIRCUIT * conn, struct UserInfo * user, UCHAR* Buffer, int l
QueueMsg(conn, MsgPtr, MsgLen);
if (user->ForwardingInfo->SendCTRLZ)
nodeprintf(conn, "\rx1a");
nodeprintf(conn, "\r\x1a");
else
nodeprintf(conn, "\r/ex\r");

@ -866,6 +866,18 @@ char * DISPLAY_NETROM(MESSAGE * ADJBUFFER, UCHAR * Output, int MsgLen)
return Output;
}
if (Index == 0x0F) // NCMP
{
Output += sprintf((char *)Output, " NCMP %x %x Type %x Code %x", Index, ID, TXNO, RXNO);
MsgLen = MsgLen - (19 + sizeof(void *));
if (MsgLen < 0 || MsgLen > 257)
return Output;// Duff
return Output;
}
}
Output += sprintf((char *)Output, " <???\?>");
@ -1021,6 +1033,7 @@ void WritePacketLogThread(void * param)
MESSAGE * MSG;
MESSAGE * Q;
char buffer[2048];
char * ptr;
while(1)
{
@ -1074,7 +1087,14 @@ void WritePacketLogThread(void * param)
MsgLen = IntDecodeFrame(MSG, buffer, MSG->Timestamp, 0xffffffffffffffff, FALSE, FALSE);
IntSetTraceOptionsEx(MMASK, SaveMTX, SaveMCOM, SaveMUI);
buffer[MsgLen++] = 0x0a; // Add lf
// lines are CR terminated. Replace with LF
ptr = buffer;
while (ptr = strchr(ptr, 0x0d))
*ptr = 0x0a;
// buffer[MsgLen++] = 0x0a; // Add lf
fwrite(buffer , 1, MsgLen, Handle);

@ -481,8 +481,15 @@ checkLen:
memcpy(Route->NEIGHBOUR_LINK->LINKCALL, axCall, 7);
Route->localport = htons(sockptr->sin.sin_port);
Debugprintf("New NRTCP Connection from %s port %d", Msg->Call, Route->localport);
if(Route->Stopped)
{
Debugprintf("Neighbour %s port %d Route stopped - closing connection", Msg->Call, portNo);
closesocket(sockptr->socket);
sockptr->SocketActive = FALSE;
memset(sockptr, 0, sizeof(struct ConnectionInfo));
Info->Call[0] = 0;
return 0;
}
if (Info->Route->INP3Node)
SendRTTMsg(Info->Route);
@ -675,6 +682,11 @@ void NETROMConnectionLost(struct ConnectionInfo * sockptr)
if (Route)
{
if (sockptr->Connecting)
TellINP3LinkSetupFailed(Route);
else
TellINP3LinkGone(Route);
Route->NEIGHBOUR_LINK = 0;
Route->TCPSession = 0;
Route->localport = 0;

64
RHP.c

@ -42,7 +42,7 @@ static int GetJSONInt(char * _REPLYBUFFER, char * Name);
struct RHPSessionInfo
{
struct ConnectionInfo * sockptr;
SOCKET Socket; // Websocks Socket
SOCKET Socket; // Websocks Socket
int BPQStream;
int Handle; // RHP session ID
int Seq;
@ -51,16 +51,10 @@ struct RHPSessionInfo
BOOL Connecting; // Set while waiting for connection to complete
BOOL Listening;
BOOL Connected;
BOOL Closing;
int Busy;
};
struct RHPConnectionInfo
{
SOCKET socket;
struct RHPSessionInfo ** RHPSessions;
int NumberofRHPSessions;
};
// Struct passed by beginhread
struct RHPParamBlock
@ -72,10 +66,6 @@ struct RHPParamBlock
};
//struct RHPConnectionInfo ** RHPSockets = NULL;
//int NumberofRHPConnections = 0;
struct RHPSessionInfo ** RHPSessions;
int NumberofRHPSessions;
@ -589,9 +579,9 @@ int processRHCPClose(SOCKET Socket, char * Msg, char * ReplyBuffer)
Disconnect(RHPSession->BPQStream);
RHPSession->Connected = 0;
RHPSession->Connecting = 0;
RHPSession->Closing = 1;
DeallocateStream(RHPSession->BPQStream);
RHPSession->BPQStream = 0;
// Stream is deallocated in disconnect event handler
return sprintf(ReplyBuffer, "{\"id\": %d, \"type\": \"closeReply\", \"handle\": %d, \"errcode\": 0, \"errtext\": \"Ok\"}", ID, Handle);
}
@ -633,6 +623,9 @@ void RHPPoll()
RHPSession = RHPSessions[n];
Stream = RHPSession->BPQStream;
if (Stream == 0)
continue;
// See if connected state has changed
SessionState(Stream, &state, &change);
@ -660,12 +653,16 @@ void RHPPoll()
}
else
{
// Disconnected. Send Close to client
// Disconnected. Send Close to client unless we initiated close
RHPMsg = malloc(256);
Len = sprintf(&RHPMsg[10], "{\"type\": \"close\", \"seqno\": %d, \"handle\": %d}", RHPSession->Seq++, RHPSession->Handle);
SendWebSockMessage(RHPSession->Socket, RHPMsg, Len);
if (RHPSession->Closing == 0)
{
RHPMsg = malloc(256);
Len = sprintf(&RHPMsg[10], "{\"type\": \"close\", \"seqno\": %d, \"handle\": %d}", RHPSession->Seq++, RHPSession->Handle);
SendWebSockMessage(RHPSession->Socket, RHPMsg, Len);
}
RHPSession->Closing = 0;
RHPSession->Connected = 0;
RHPSession->Connecting = 0;
@ -802,4 +799,35 @@ static int GetJSONInt(char * _REPLYBUFFER, char * Name)
return atoi(ptr1);
}
VOID SendCommandReply(TRANSPORTENTRY * Session, struct DATAMESSAGE * Buffer, int Len);
char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...);
extern struct DATAMESSAGE * REPLYBUFFER;
VOID RHPCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD)
{
int n;
struct RHPSessionInfo * RHP;
Bufferptr = Cmdprintf(Session, Bufferptr, "\r|Stream|Local |Remote |Handle| Seq |Busy|\r");
for (n = 0; n < NumberofRHPSessions; n++)
{
RHP = RHPSessions[n];
if (RHP->BPQStream == 0)
continue;
Bufferptr = Cmdprintf(Session, Bufferptr, "| %2d |%-9s|%-9s| %3d |%5d| %2d |\r", RHP->BPQStream, RHP->Local, RHP->Remote, RHP->Handle, RHP->Seq, RHP->Busy);
}
SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER));
return;
}

@ -5764,11 +5764,10 @@ int Telnet_Connected(struct TNCINFO * TNC, struct ConnectionInfo * sockptr, SOCK
}
else
{
struct _TRANSPORTENTRY * CROSSLINK = TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4CROSSLINK;
struct _TRANSPORTENTRY * STREAM = TNC->PortRecord->ATTACHEDSESSIONS[Stream];
if (CROSSLINK && CROSSLINK->APPL[0])
buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to %s\r",
TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4CROSSLINK->APPL);
if (STREAM && STREAM->L4CROSSLINK && STREAM->L4CROSSLINK->APPL[0])
buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to %s\r", STREAM->L4CROSSLINK->APPL);
else
buffptr->Len = sprintf(&buffptr->Data[0], "*** Connected to APPL\r");

@ -10,17 +10,17 @@
#endif
#define KVers 6,0,25,15
#define KVerstring "6.0.25.15\0"
#define KVers 6,0,25,22
#define KVerstring "6.0.25.22\0"
#ifdef CKernel
#define Vers KVers
#define Verstring KVerstring
#define Datestring "December 2025"
#define Datestring "February 2026"
#define VerComments "G8BPQ Packet Switch (C Version)" KVerstring
#define VerCopyright "Copyright © 2001-2025 John Wiseman G8BPQ\0"
#define VerCopyright "Copyright © 2001-2026 John Wiseman G8BPQ\0"
#define VerDesc "BPQ32 Switch\0"
#define VerProduct "BPQ32"
@ -31,7 +31,7 @@
#define Vers 1,0,16,2
#define Verstring "1.0.16.2\0"
#define VerComments "Internet Terminal for G8BPQ Packet Switch\0"
#define VerCopyright "Copyright © 2011-2025 John Wiseman G8BPQ\0"
#define VerCopyright "Copyright © 2011-2026 John Wiseman G8BPQ\0"
#define VerDesc "Simple TCP Terminal Program for G8BPQ Switch\0"
#define VerProduct "BPQTermTCP"
@ -42,7 +42,7 @@
#define Vers 2,2,5,2
#define Verstring "2.2.5.2\0"
#define VerComments "Simple Terminal for G8BPQ Packet Switch\0"
#define VerCopyright "Copyright © 1999-2025 John Wiseman G8BPQ\0"
#define VerCopyright "Copyright © 1999-2026 John Wiseman G8BPQ\0"
#define VerDesc "Simple Terminal Program for G8BPQ Switch\0"
#define VerProduct "BPQTerminal"
@ -53,7 +53,7 @@
#define Vers 2,2,0,3
#define Verstring "2.2.0.3\0"
#define VerComments "MDI Terminal for G8BPQ Packet Switch\0"
#define VerCopyright "Copyright © 1999-2025 John Wiseman G8BPQ\0"
#define VerCopyright "Copyright © 1999-2026 John Wiseman G8BPQ\0"
#define VerDesc "MDI Terminal Program for G8BPQ Switch\0"
#endif
@ -63,7 +63,7 @@
#define Vers KVers
#define Verstring KVerstring
#define VerComments "Mail server for G8BPQ Packet Switch\0"
#define VerCopyright "Copyright © 2009-2025 John Wiseman G8BPQ\0"
#define VerCopyright "Copyright © 2009-2026 John Wiseman G8BPQ\0"
#define VerDesc "Mail server for G8BPQ's 32 Bit Switch\0"
#define VerProduct "BPQMail"
@ -98,7 +98,7 @@
#define Vers 0,1,0,0
#define Verstring "0.1.0.0\0"
#define VerComments "Password Generation Utility for G8BPQ Packet Switch\0"
#define VerCopyright "Copyright © 2011-2025 John Wiseman G8BPQ\0"
#define VerCopyright "Copyright © 2011-2026 John Wiseman G8BPQ\0"
#define VerDesc "Password Generation Utility for G8BPQ Switch\0"
#endif
@ -108,7 +108,7 @@
#define Vers KVers
#define Verstring KVerstring
#define VerComments "APRS Client for G8BPQ Switch\0"
#define VerCopyright "Copyright © 2012-2025 John Wiseman G8BPQ\0"
#define VerCopyright "Copyright © 2012-2026 John Wiseman G8BPQ\0"
#define VerDesc "APRS Client for G8BPQ Switch\0"
#define VerProduct "BPQAPRS"
@ -119,7 +119,7 @@
#define Vers KVers
#define Verstring KVerstring
#define VerComments "Chat server for G8BPQ Packet Switch\0"
#define VerCopyright "Copyright © 2009-2025 John Wiseman G8BPQ\0"
#define VerCopyright "Copyright © 2009-2026 John Wiseman G8BPQ\0"
#define VerDesc "Chat server for G8BPQ's 32 Bit Switch\0"
#define VerProduct "BPQChat"

@ -261,6 +261,7 @@ typedef struct ROUTE
int localport; // for consistancy check
int recNum; // This entry's index it Routes table
int noV2point2; // Set to force V2.0 connect. Can be set in config or dynamically learned
int Stopped; // Set by STOPROUTE command
} *PROUTE;
@ -481,7 +482,6 @@ typedef struct _APPLCALLS
typedef struct NR_DEST_ROUTE_ENTRY
{
struct ROUTE * ROUT_NEIGHBOUR; // POINTER TO NEXT NODE IN PATH
uint16_t * Dummy; // Padding so records are same length
UCHAR ROUT_QUALITY; // QUALITY
UCHAR ROUT_OBSCOUNT;
UCHAR ROUT_LOCKED;
@ -490,7 +490,6 @@ typedef struct NR_DEST_ROUTE_ENTRY
typedef struct INP3_DEST_ROUTE_ENTRY
{
struct ROUTE * ROUT_NEIGHBOUR; // POINTER TO NEXT NODE IN PATH
uint16_t * RouteLastTT; // Last time sent should be saved for each neighbour. Area is mallod'ed as needed
USHORT STT; // Current time to dest from here (was called RTT but is one way not round trip.
// Is actually a smoothed value as is calculated from smoothed link times)
UCHAR Hops;
@ -517,7 +516,9 @@ typedef struct DEST_LIST
void * DEST_Q; // QUEUE OF FRAMES FOR THIS DESTINATION
int DEST_RTT; // SMOOTHED ROUND TRIP TIMER
int DEST_COUNT; // FRAMES SENT
int DEST_COUNT; // FRAMES SENT
uint16_t * RouteLastTT; // Last time sent should be saved for each neighbour. Area is mallod'ed as needed
} dest_list;

@ -1565,6 +1565,9 @@ BOOL Start()
DEST->DEST_STATE = 0x80; // SPECIAL ENTRY
DEST->NRROUTE[0].ROUT_QUALITY = 255;
DEST->NRROUTE[0].ROUT_OBSCOUNT = 255;
if (DEST->RouteLastTT == 0)
DEST->RouteLastTT = (uint16_t *)zalloc(MAXNEIGHBOURS * sizeof(uint16_t));
DEST++;
NUMBEROFNODES++;
}
@ -1585,6 +1588,9 @@ BOOL Start()
DEST->NRROUTE[0].ROUT_QUALITY = (UCHAR)APPL->APPLQUAL;
DEST->NRROUTE[0].ROUT_OBSCOUNT = 255;
APPL->NODEPOINTER = DEST;
if (DEST->RouteLastTT == 0)
DEST->RouteLastTT = (uint16_t *)zalloc(MAXNEIGHBOURS * sizeof(uint16_t));
DEST++;
@ -2120,6 +2126,9 @@ VOID ReadNodes()
memcpy(DEST->DEST_CALL, axcall, 7);
memcpy(DEST->DEST_ALIAS, FULLALIAS, 6);
if (DEST->RouteLastTT == 0)
DEST->RouteLastTT = (uint16_t *)zalloc(MAXNEIGHBOURS * sizeof(uint16_t));
NUMBEROFNODES++;
RouteLoop:

@ -943,10 +943,14 @@ int sendPortQState(char * response, char * token, char * param, int Local)
}
else if (Sess1->L4CIRCUITTYPE & PACTOR)
{
// PACTOR Type - Frames are queued on the Port Entry
// PACTOR Type
struct PORTCONTROL * PORT = Sess1->L4TARGET.PORT;
strcpy(Type, "HFLINK");
if (PORT && PORT->Hardware == H_TELNET)
strcpy(Type, "TELNET");
else
strcpy(Type, "HFLINK");
if (PORT)
radioport = PORT->PORTNUMBER;

Loading…
Cancel
Save

Powered by TurnKey Linux.