master 24.71
g8bpq 8 months ago
parent 5da7d4d4f1
commit 4a7536c236

@ -0,0 +1,217 @@
// Includes code from MiniUPnPc, used subject to the following conditions:
/*
MiniUPnPc
Copyright (c) 2005-2020, Thomas BERNARD
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#define MINIUPNP_STATICLIB
#include <stdio.h>
#ifdef _WIN32
#include "upnpcommands.h"
#include "miniupnpc.h"
#include "upnperrors.h"
#include <winsock2.h>
#endif
#ifdef LINBPQ
#ifndef MACBPQ
#ifndef WIN32
#include <miniupnpc/upnpcommands.h>
#include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnperrors.h>
#include <stdio.h>
#endif
#endif
#endif
#ifdef MACBPQ
#include </usr/local/Cellar/miniupnpc/2.2.5/include/miniupnpc/upnpcommands.h>
#include </usr/local/Cellar/miniupnpc/2.2.5/include/miniupnpc/miniupnpc.h>
#include </usr/local/Cellar/miniupnpc/2.2.5/include/miniupnpc/upnperrors.h>
#include <stdio.h>
#endif
int AddMap(char * controlURL, char * eport, char * iport, char * proto);
int DeleteMap(char * controlURL, char * eport, char * iport, char * proto);
void Consoleprintf(const char * format, ...);
struct UPNP
{
struct UPNP * Next;
char * Protocol;
char * LANport;
char * WANPort;
};
extern struct UPNP * UPNPConfig;
char * controlURL = 0;
char * servicetype = 0;
char iaddr[] = "IP";
char * inClient = NULL;
#ifdef LINBPQ
char desc[] = "LinBPQ ";
#else
char desc[] = "BPQ32 ";
#endif
char * remoteHost = NULL;
char * leaseDuration = NULL;
struct UPNPDev * devlist = 0;
char lanaddr[64] = "unset"; /* my ip address on the LAN */
char wanaddr[64] = "unset"; /* my ip address on the LAN */
struct UPNPUrls urls;
struct IGDdatas data;
int i;
const char * rootdescurl = 0;
const char * multicastif = 0;
const char * minissdpdpath = 0;
#ifdef UPNP_LOCAL_PORT_ANY
int localport = UPNP_LOCAL_PORT_ANY;
#else
#pragma message "API 10"
int localport = 0;
#endif
int retcode = 0;
int error = 0;
int ipv6 = 0;
int ignore = 0;
unsigned char ttl = 2;
int upnpInit()
{
struct UPNP * Config = UPNPConfig;
int i;
#ifdef WIN32
WSADATA wsaData;
int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if(nResult != NO_ERROR)
{
fprintf(stderr, "WSAStartup() failed.\n");
return -1;
}
#endif
while (Config)
{
if (devlist == NULL)
{
#if MINIUPNPC_API_VERSION == 10
devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, &error);
#else
devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error);
#endif
if (devlist == NULL)
{
Consoleprintf("Failed to find a UPNP device");
return 0;
}
#if MINIUPNPC_API_VERSION == 18
i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), wanaddr, sizeof(wanaddr));
#else
i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
#endif
}
AddMap(devlist->descURL, Config->LANport, Config->WANPort, Config->Protocol);
Config = Config->Next;
}
return 0;
}
int upnpClose()
{
struct UPNP * Config = UPNPConfig;
int i;
while (Config)
{
if (devlist == NULL)
{
#if MINIUPNPC_API_VERSION == 10
devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, &error);
#else
devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error);
#endif
if (devlist == NULL)
{
Consoleprintf("Failed to find a UPNP device");
return 0;
}
#if MINIUPNPC_API_VERSION == 18
i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), wanaddr, sizeof(wanaddr));
#else
i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
#endif
}
DeleteMap(devlist->descURL, Config->LANport, Config->WANPort, Config->Protocol);
Config = Config->Next;
}
return 0;
}
int AddMap(char * controlURL, char * eport, char * iport, char * proto)
{
int r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
eport, iport, lanaddr, desc,
proto, remoteHost, leaseDuration);
if (r != UPNPCOMMAND_SUCCESS)
{
Consoleprintf("UPNP AddPortMapping(%s, %s, %s) failed with code %d (%s)", eport, iport, lanaddr, r, strupnperror(r));
return -2;
}
Consoleprintf("UPNP AddPortMapping(%s, %s, %s) Succeeded", eport, iport, lanaddr, r);
return 0;
}
int DeleteMap(char * controlURL, char * eport, char * iport, char * proto)
{
int r = UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, eport, proto, remoteHost);
if(r != UPNPCOMMAND_SUCCESS)
{
Consoleprintf("UPNP DeletePortMapping(%s, %s, %s) failed with code %d (%s)", eport, iport, lanaddr, r, strupnperror(r));
return -2;
}
Consoleprintf("UPNP DeletePortMapping(%s, %s, %s) Succeeded", eport, iport, lanaddr, r);
return 0;
}

@ -0,0 +1,43 @@
# LinBPQ Makefile
# To exclude i2c support run make noi2c
OBJS = pngwtran.o pngrtran.o pngset.o pngrio.o pngwio.o pngtrans.o pngrutil.o pngwutil.o\
pngread.o pngwrite.o png.o pngerror.o pngget.o pngmem.o APRSIconData.o AISCommon.o\
upnp.o APRSStdPages.o HSMODEM.o WinRPR.o KISSHF.o TNCEmulators.o bpqhdlc.o SerialPort.o\
adif.o WebMail.o utf8Routines.o VARA.o LzFind.o Alloc.o LzmaDec.o LzmaEnc.o LzmaLib.o \
Multicast.o ARDOP.o IPCode.o FLDigi.o linether.o CMSAuth.o APRSCode.o BPQtoAGW.o KAMPactor.o\
AEAPactor.o HALDriver.o MULTIPSK.o BBSHTMLConfig.o ChatHTMLConfig.o BBSUtilities.o bpqaxip.o\
BPQINP3.o BPQNRR.o cMain.o Cmd.o CommonCode.o HTMLCommonCode.o compatbits.o config.o datadefs.o \
FBBRoutines.o HFCommon.o Housekeeping.o HTTPcode.o kiss.o L2Code.o L3Code.o L4Code.o lzhuf32.o \
MailCommands.o MailDataDefs.o LinBPQ.o MailRouting.o MailTCP.o MBLRoutines.o md5.o Moncode.o \
NNTPRoutines.o RigControl.o TelnetV6.o WINMOR.o TNCCode.o UZ7HODrv.o WPRoutines.o \
SCSTrackeMulti.o SCSPactor.o SCSTracker.o HanksRT.o UIRoutines.o AGWAPI.o AGWMoncode.o \
DRATS.o FreeDATA.o base64.o Events.o nodeapi.o mailapi.o mqtt.o RHP.o
# Configuration:
CC = gcc
all: CFLAGS = -DLINBPQ -MMD -g -rdynamic -fcommon -fasynchronous-unwind-tables
all: LDFLAGS = -l:libpaho-mqtt3a.a -l:libjansson.a
all: linbpq
nomqtt: CFLAGS = -DLINBPQ -MMD -fcommon -g -rdynamic -DNOMQTT -fasynchronous-unwind-tables
nomqtt: linbpq
noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g -rdynamic -fcommon -fasynchronous-unwind-tables
noi2c: linbpq
linbpq: $(OBJS)
gcc $(OBJS) -Xlinker -Map=output.map -l:libminiupnpc.a -lrt -lm -lz $(LDFLAGS) -lpthread -lconfig -lpcap -o linbpq
sudo setcap "CAP_NET_ADMIN=ep CAP_NET_RAW=ep CAP_NET_BIND_SERVICE=ep" linbpq
-include *.d
clean :
rm *.d
rm linbpq $(OBJS)

@ -0,0 +1,799 @@
/*
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
*/
/*
Paula (G8PZT)'s Remote Host Protocol interface.
For now only sufficient support for WhatsPac
*/
#define _CRT_SECURE_NO_DEPRECATE
#include "cheaders.h"
#include "bpq32.h"
#include "telnetserver.h"
int FindFreeStreamNoSem();
DllExport int APIENTRY DeallocateStream(int stream);
int SendMsgNoSem(int stream, char * msg, int len);
static void GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value, int Len);
static int GetJSONInt(char * _REPLYBUFFER, char * Name);
// Generally Can have multiple RHP connections and each can have multiple RHF Sessions
struct RHPSessionInfo
{
struct ConnectionInfo * sockptr;
SOCKET Socket; // Websocks Socket
int BPQStream;
int Handle; // RHP session ID
int Seq;
char Local[12];
char Remote[12];
BOOL Connecting; // Set while waiting for connection to complete
BOOL Listening;
BOOL Connected;
int Busy;
};
struct RHPConnectionInfo
{
SOCKET socket;
struct RHPSessionInfo ** RHPSessions;
int NumberofRHPSessions;
};
// Struct passed by beginhread
struct RHPParamBlock
{
unsigned char * Msg;
int Len;
SOCKET Socket;
struct ConnectionInfo * sockptr;
};
//struct RHPConnectionInfo ** RHPSockets = NULL;
//int NumberofRHPConnections = 0;
struct RHPSessionInfo ** RHPSessions;
int NumberofRHPSessions;
char ErrCodes[18][24] =
{
"Ok",
"Unspecified",
"Bad or missing type",
"Invalid handle",
"No memory",
"Bad or missing mode",
"Invalid local address",
"Invalid remote address" ,
"Bad or missing family" ,
"Duplicate socket" ,
"No such port" ,
"Invalid protocol" ,
"Bad parameter" ,
"No buffers" ,
"Unauthorised" ,
"No Route" ,
"Operation not supported"};
extern char pgm[256];
SOCKET agwsock;
extern int SemHeldByAPI;
char szBuff[80];
int WhatsPacConfigured = 1;
int RHPPaclen = 236;
int processRHCPOpen(struct ConnectionInfo * sockptr, SOCKET Socket, char * Msg, char * ReplyBuffer);
int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer);
int processRHCPClose(SOCKET Socket, char * Msg, char * ReplyBuffer);
int processRHCPStatus(SOCKET Socket, char * Msg, char * ReplyBuffer);
void SendWebSockMessage(SOCKET socket, char * Msg, int Len)
{
int Loops = 0;
int Sent;
int TxLen;
char * OutBuffer = Msg;
// WebSock Encode. Buffer has 10 bytes on front for header but header len depends on Msg len
if (Len < 126)
{
// Two Byte Header
OutBuffer[8] = 0x81; // Fin, Data
OutBuffer[9] = Len;
TxLen = Len + 2;
OutBuffer = &Msg[8];
}
else if (Len < 65536)
{
OutBuffer[6] = 0x81; // Fin, Data
OutBuffer[7] = 126; // Unmasked, Extended Len 16
OutBuffer[8] = Len >> 8;
OutBuffer[9] = Len & 0xff;
TxLen = Len + 4;
OutBuffer = &Msg[6];
}
else
{
OutBuffer[0] = 0x81; // Fin, Data
OutBuffer[1] = 127; // Unmasked, Extended Len 64 bits
// Len is 32 bits, so pad with zeros
OutBuffer[2] = 0;
OutBuffer[3] = 0;
OutBuffer[4] = 0;
OutBuffer[5] = 0;
OutBuffer[6] = (Len >> 24) & 0xff;
OutBuffer[7] = (Len >> 16) & 0xff;
OutBuffer[8] = (Len >> 8) & 0xff;
OutBuffer[9] = Len & 0xff;
TxLen = Len + + 10;
OutBuffer = &Msg[0];
}
// Send may block
Sent = send(socket, OutBuffer, TxLen, 0);
while (Sent != TxLen && Loops++ < 3000) // 100 secs max
{
if (Sent > 0) // something sent
{
TxLen -= Sent;
memmove(OutBuffer, &OutBuffer[Sent], TxLen);
}
Sleep(30);
Sent = send(socket, OutBuffer, TxLen, 0);
if (Sent == -1)
break;
}
free(Msg);
return;
}
void ProcessRHPWebSock(struct ConnectionInfo * sockptr, SOCKET Socket, char * Msg, int MsgLen);
void RHPThread(void * Params)
{
// Params and data buffer are malloced blocks so free when done with it
struct RHPParamBlock * Block = (struct RHPParamBlock *)Params;
ProcessRHPWebSock(Block->sockptr, Block->Socket, Block->Msg, Block->Len);
free(Block->Msg);
free(Params);
}
int RHPProcessHTTPMessage(void * conn, char * response, char * Method, char * URL, char * request, BOOL LOCAL, BOOL COOKIE)
{
// RHP messages can be sent over Websocks or normal http but I think WhatsPac only uses WebSocks
return 0;
}
void ProcessRHPWebSock(struct ConnectionInfo * sockptr, SOCKET Socket, char * Msg, int MsgLen)
{
int Loops = 0;
int InputLen = 0;
int Len;
char Value[16];
char * OutBuffer = malloc(250000);
// struct RHPConnectionInfo * RHPSocket = NULL;
// int n;
Msg[MsgLen] = 0;
// Find Connection Record. If none, create one
// I dont think I need connection records
/*
for (n = 0; n < NumberofRHPConnections; n++)
{
if (RHPSockets[n]->socket == socket)
{
RHPSocket = RHPSockets[n];
break;
}
}
if (RHPSocket == 0)
{
// See if there is an old one we can reuse
for (n = 0; n < NumberofRHPConnections; n++)
{
if (RHPSockets[n]-Socket == -1)
{
RHPSocket = RHPSockets[n];
RHP
break;
}
}
if (RHPSocket == 0)
NumberofRHPConnections;
RHPSockets = realloc(RHPSockets, sizeof(void *) * (NumberofRHPConnections + 1));
RHPSocket = RHPSockets[NumberofRHPConnections] = zalloc(sizeof (struct RHPConnectionInfo));
NumberofRHPConnections++;
RHPSocket->socket = socket;
}
*/
// {"type":"open","id":5,"pfam":"ax25","mode":"stream","port":"1","local":"G8BPQ","remote":"G8BPQ-2","flags":128}
// {"type": "openReply", "id": 82, "handle": 1, "errCode": 0, "errText": "Ok"}
// {"seqno": 0, "type": "status", "handle": 1, "flags": 0}
// ("seqno": 1, "type": "close", "handle": 1}
// {"id":40,"type":"close","handle":1}
// {"seqno": 0, "type": "status", "handle": 1, "flags": 2}.~.
// {"seqno": 1, "type": "recv", "handle": 1, "data": "Welcome to G8BPQ's Test Switch in Nottingham \rType ? for list of available commands.\r"}.
// {"type": "status", "handle": 0}. XRouter will reply with {"type": "statusReply", "handle": 0, "errcode": 12, "errtext": "invalid handle"}. It
// {type: 'keepalive'} if there has been no other activity for nearly 3 minutes. Replies with {"type": "keepaliveReply"}
GetJSONValue(Msg, "\"type\":", Value, 15);
if (_stricmp(Value, "open") == 0)
{
Len = processRHCPOpen(sockptr, Socket, Msg, &OutBuffer[10]); // Space at front for WebSock Header
if (Len)
SendWebSockMessage(Socket, OutBuffer, Len);
return;
}
if (_stricmp(Value, "send") == 0)
{
Len = processRHCPSend(Socket, Msg, &OutBuffer[10]); // Space at front for WebSock Header
SendWebSockMessage(Socket, OutBuffer, Len);
return;
}
if (_stricmp(Value, "close") == 0)
{
Len = processRHCPClose(Socket, Msg, &OutBuffer[10]); // Space at front for WebSock Header
SendWebSockMessage(Socket, OutBuffer, Len);
return;
}
if (_stricmp(Value, "status") == 0)
{
Len = processRHCPStatus(Socket, Msg, &OutBuffer[10]); // Space at front for WebSock Header
SendWebSockMessage(Socket, OutBuffer, Len);
return;
}
if (_stricmp(Value, "keepalive") == 0)
{
Len = sprintf(&OutBuffer[10], "{\"type\": \"keepaliveReply\"}"); // Space at front for WebSock Header
SendWebSockMessage(Socket, OutBuffer, Len);
return;
}
Debugprintf("Unrecognised RHP Message - %s", Msg);
}
void ProcessRHPWebSockClosed(SOCKET socket)
{
// Close any connections on this scoket and delete socket entry
struct RHPSessionInfo * RHPSession = 0;
int n;
// Find and close any Sessions
for (n = 0; n < NumberofRHPSessions; n++)
{
if (RHPSessions[n]->Socket == socket)
{
RHPSession = RHPSessions[n];
if (RHPSession->BPQStream)
{
Disconnect(RHPSession->BPQStream);
DeallocateStream(RHPSession->BPQStream);
RHPSession->BPQStream = 0;
}
RHPSession->Connecting = 0;
// We can't send a close to RHP endpont as socket has gone
RHPSession->Connected = 0;
}
}
}
int processRHCPOpen(struct ConnectionInfo * sockptr, SOCKET Socket, char * Msg, char * ReplyBuffer)
{
//{"type":"open","id":5,"pfam":"ax25","mode":"stream","port":"1","local":"G8BPQ","remote":"G8BPQ-2","flags":128}
struct RHPSessionInfo * RHPSession = 0;
char * Value = malloc(strlen(Msg)); // Will always be long enough
int ID;
char pfam[16];
char Mode[16];
int Port;
char Local[16];
char Remote[16];
int flags;
int Handle = 1;
int Stream;
unsigned char AXCall[10];
int n;
// ID seems to be used for control commands like open. SeqNo for data within a session (i Think!
ID = GetJSONInt(Msg, "\"id\":");
GetJSONValue(Msg, "\"pfam\":", pfam, 15);
GetJSONValue(Msg, "\"mode\":", Mode, 15);
Port = GetJSONInt(Msg, "\"port\":");
GetJSONValue(Msg, "\"local\":", Local, 15);
GetJSONValue(Msg, "\"remote\":", Remote, 15);
flags = GetJSONInt(Msg, "\"flags\":");
if (_stricmp(pfam, "ax25") != 0)
return sprintf(ReplyBuffer, "{\"type\": \"openReply\", \"id\": %d, \"handle\": %d, \"errCode\": 12, \"errText\": \"Bad parameter\"}", ID, 0);
if (_stricmp(Mode, "stream") == 0)
{
{
// Allocate a RHP Session
// See if there is an old one we can reuse
for (n = 0; n < NumberofRHPSessions; n++)
{
if (RHPSessions[n]->BPQStream == 0)
{
RHPSession = RHPSessions[n];
Handle = n + 1;
Stream = RHPSessions[n]->BPQStream;
break;
}
}
if (RHPSession == 0)
{
RHPSessions = realloc(RHPSessions, sizeof(void *) * (NumberofRHPSessions + 1));
RHPSession = RHPSessions[NumberofRHPSessions] = zalloc(sizeof (struct RHPSessionInfo));
NumberofRHPSessions++;
Handle = NumberofRHPSessions;
}
strcpy(pgm, "RHP");
Stream = FindFreeStream();
strcpy(pgm, "bpq32.exe");
if (Stream == 255)
return sprintf(ReplyBuffer, "{\"type\": \"openReply\", \"id\": %d, \"handle\": %d, \"errCode\": 12, \"errText\": \"Bad parameter\"}", ID, 0);
RHPSession->BPQStream = Stream;
RHPSession->Handle = Handle;
RHPSession->Connecting = TRUE;
RHPSession->Socket = Socket;
RHPSession->sockptr = sockptr;
strcpy(RHPSession->Local, Local);
strcpy(RHPSession->Remote, Remote);
Connect(Stream);
ConvToAX25(Local, AXCall);
ChangeSessionCallsign(Stream, AXCall);
return sprintf(ReplyBuffer, "{\"type\": \"openReply\", \"id\": %d, \"handle\": %d, \"errCode\": 0, \"errText\": \"Ok\"}", ID, Handle);
}
}
return sprintf(ReplyBuffer, "{\"type\": \"openReply\", \"id\": %d, \"handle\": %d, \"errCode\": 12, \"errText\": \"Bad parameter\"}", ID, 0);
}
int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer)
{
// {"type":"send","handle":1,"data":";;;;;;\r","id":70}
struct RHPSessionInfo * RHPSession;
int ID;
char * Data;
char * ptr;
unsigned char * uptr;
int c;
int Len;
unsigned int HexCode1;
unsigned int HexCode2;
int n;
int Handle = 1;
Data = malloc(strlen(Msg));
ID = GetJSONInt(Msg, "\"id\":");
Handle = GetJSONInt(Msg, "\"handle\":");
GetJSONValue(Msg, "\"data\":", Data, strlen(Msg) - 1);
if (Handle < 1 || Handle > NumberofRHPSessions)
{
free(Data);
return sprintf(ReplyBuffer, "{\"type\": \"sendReply\", \"id\": %d, \"handle\": %d, \"errCode\": 3, \"errtext\": \"Invalid handle\"}", ID, Handle);
}
RHPSession = RHPSessions[Handle - 1];
// Look for \ escapes, Can now also get \u00c3
ptr = Data;
Len = strlen(Data); // in case no escapes
while (ptr = strchr(ptr, '\\'))
{
c = ptr[1];
switch (c)
{
case 'r':
*ptr = 13;
memmove(ptr + 1, ptr + 2, strlen(ptr + 1));
break;
case 'u':
HexCode1 = HexCode2 = 0;
n = toupper(ptr[2]) - '0';
if (n > 9) n = n - 7;
HexCode1 |= n << 4;
n = toupper(ptr[3]) - '0';
if (n > 9) n = n - 7;
HexCode1 |= n;
n = toupper(ptr[4]) - '0';
if (n > 9) n = n - 7;
HexCode2 |= n << 4;
n = toupper(ptr[5]) - '0';
if (n > 9) n = n - 7;
HexCode2 |= n;
if (HexCode1 == 0 || HexCode1 == 0xC2)
{
uptr = ptr;
*uptr = HexCode2;
}
else if (HexCode1 == 0xc2)
{
uptr = ptr;
*uptr = HexCode2 + 0x40;
}
memmove(ptr + 1, ptr + 6, strlen(ptr + 5));
break;
case '\\':
*ptr = '\\';
memmove(ptr + 1, ptr + 2, strlen(ptr + 1));
break;
case '"':
*ptr = '"';
memmove(ptr + 1, ptr + 2, strlen(ptr + 1));
break;
}
ptr++;
Len = ptr - Data;
}
ptr = Data;
while (Len > RHPPaclen)
{
SendMsg(RHPSession->BPQStream, ptr, RHPPaclen);
Len -= RHPPaclen;
ptr += RHPPaclen;
}
SendMsg(RHPSession->BPQStream, ptr, Len);
free(Data);
return sprintf(ReplyBuffer, "{\"type\": \"sendReply\", \"id\": %d, \"handle\": %d, \"errCode\": 0, \"errText\": \"Ok\", \"status\": %d}", ID, Handle, 2);
}
int processRHCPClose(SOCKET Socket, char * Msg, char * ReplyBuffer)
{
// {"id":70,"type":"close","handle":1}
struct RHPSessionInfo * RHPSession;
int ID;
int Handle = 1;
char * OutBuffer = malloc(256);
ID = GetJSONInt(Msg, "\"id\":");
Handle = GetJSONInt(Msg, "\"handle\":");
if (Handle < 1 || Handle > NumberofRHPSessions)
return sprintf(ReplyBuffer, "{\"id\": %d, \"type\": \"closeReply\", \"handle\": %d, \"errcode\": 3, \"errtext\": \"Invalid handle\"}", ID, Handle);
RHPSession = RHPSessions[Handle - 1];
Disconnect(RHPSession->BPQStream);
RHPSession->Connected = 0;
RHPSession->Connecting = 0;
DeallocateStream(RHPSession->BPQStream);
RHPSession->BPQStream = 0;
return sprintf(ReplyBuffer, "{\"id\": %d, \"type\": \"closeReply\", \"handle\": %d, \"errcode\": 0, \"errtext\": \"Ok\"}", ID, Handle);
}
int processRHCPStatus(SOCKET Socket, char * Msg, char * ReplyBuffer)
{
// {"type": "status", "handle": 0}. XRouter will reply with {"type": "statusReply", "handle": 0, "errcode": 3, "errtext": "invalid handle"}. It
struct RHPSessionInfo * RHPSession;
int Handle = 0;
Handle = GetJSONInt(Msg, "\"handle\":");
if (Handle < 1 || Handle > NumberofRHPSessions)
return sprintf(ReplyBuffer, "{\"type\": \"statusReply\", \"handle\": %d, \"errcode\": 3, \"errtext\": \"Invalid handle\"}", Handle);
RHPSession = RHPSessions[Handle - 1];
return sprintf(ReplyBuffer, "{\"type\": \"status\", \"handle\": %d, \"flags\": 2}", RHPSession->Handle);
}
char toHex[] = "0123456789abcdef";
void RHPPoll()
{
int Stream;
int n;
int state, change;
int Len;
char * RHPMsg;
unsigned char Buffer[2048]; // Space to escape control chars
int pktlen, count;
struct RHPSessionInfo * RHPSession;
for (n = 0; n < NumberofRHPSessions; n++)
{
RHPSession = RHPSessions[n];
Stream = RHPSession->BPQStream;
// See if connected state has changed
SessionState(Stream, &state, &change);
if (change == 1)
{
if (state == 1)
{
// Connected
RHPSession->Seq = 0;
RHPSession->Connecting = FALSE;
RHPSession->Connected = TRUE;
RHPMsg = malloc(256);
Len = sprintf(&RHPMsg[10], "{\"seqno\": %d, \"type\": \"status\", \"handle\": %d, \"flags\": 2}", RHPSession->Seq++, RHPSession->Handle);
SendWebSockMessage(RHPSession->Socket, RHPMsg, Len);
// Send RHP CTEXT
RHPMsg = malloc(256);
Sleep(10); // otherwise WhatsPac doesn't display connected
Len = sprintf(&RHPMsg[10], "{\"seqno\": %d, \"type\": \"recv\", \"handle\": %d, \"data\": \"Connected to RHP Server\\r\"}", RHPSession->Seq++, RHPSession->Handle);
SendWebSockMessage(RHPSession->Socket, RHPMsg, Len);
}
else
{
// Disconnected. Send Close to client
RHPMsg = malloc(256);
Len = sprintf(&RHPMsg[10], "{\"type\": \"close\", \"seqno\": %d, \"handle\": %d}", RHPSession->Seq++, RHPSession->Handle);
SendWebSockMessage(RHPSession->Socket, RHPMsg, Len);
RHPSession->Connected = 0;
RHPSession->Connecting = 0;
DeallocateStream(RHPSession->BPQStream);
RHPSession->BPQStream = 0;
}
}
do
{
GetMsg(Stream, Buffer, &pktlen, &count);
if (pktlen > 0)
{
char * ptr = Buffer;
unsigned char c;
Buffer[pktlen] = 0;
RHPSession->sockptr->LastSendTime = time(NULL);
// Message is JSON so Convert CR to \r, \ to \\ " to \"
// Looks like I need to escape everything not between 0x20 and 0x7f eg \u00c3
while (c = *(ptr))
{
switch (c)
{
case 13:
memmove(ptr + 2, ptr + 1, strlen(ptr) + 1);
*(ptr++) = '\\';
*(ptr++) = 'r';
break;
case '"':
memmove(ptr + 2, ptr + 1, strlen(ptr) + 1);
*(ptr++) = '\\';
*(ptr++) = '"';
break;
case '\\':
memmove(ptr + 2, ptr + 1, strlen(ptr) + 1);
*(ptr++) = '\\';
*(ptr++) = '\\';
break;
default:
if (c > 127)
{
memmove(ptr + 6, ptr + 1, strlen(ptr) + 1);
*(ptr++) = '\\';
*(ptr++) = 'u';
*(ptr++) = '0';
*(ptr++) = '0';
*(ptr++) = toHex[c >> 4];
*(ptr++) = toHex[c & 15];
break;
}
else
ptr++;
}
}
RHPMsg = malloc(2048);
Len = sprintf(&RHPMsg[10], "{\"seqno\": %d, \"type\": \"recv\", \"handle\": %d, \"data\": \"%s\"}", RHPSession->Seq++, RHPSession->Handle, Buffer);
SendWebSockMessage(RHPSession->Socket, RHPMsg, Len);
}
}
while (count > 0);
}
}
static void GetJSONValue(char * _REPLYBUFFER, char * Name, char * Value, int Len)
{
char * ptr1, * ptr2;
Value[0] = 0;
ptr1 = strstr(_REPLYBUFFER, Name);
if (ptr1 == 0)
return;
ptr1 += (strlen(Name) + 1);
// "data":"{\"t\":\"c\",\"n\":\"John\",\"c\":\"G8BPQ\",\"lm\":1737912636,\"le\":1737883907,\"led\":1737758451,\"v\":0.33,\"cc\":[{\"cid\":1,\"lp\":1737917257201,\"le\":1737913735726,\"led\":1737905249785},{\"cid\":0,\"lp\":1737324074107,\"le\":1737323831510,\"led\":1737322973662},{\"cid\":5,\"lp\":1737992107419,\"le\":1737931466510,\"led\":1737770056244}]}\r","id":28}
// There may be escaped " in data stream
ptr2 = strchr(ptr1, '"');
while (*(ptr2 - 1) == '\\')
{
ptr2 = strchr(ptr2 + 2, '"');
}
if (ptr2)
{
size_t ValLen = ptr2 - ptr1;
if (ValLen > Len)
ValLen = Len;
memcpy(Value, ptr1, ValLen);
Value[ValLen] = 0;
}
return;
}
static int GetJSONInt(char * _REPLYBUFFER, char * Name)
{
char * ptr1;
ptr1 = strstr(_REPLYBUFFER, Name);
if (ptr1 == 0)
return 0;
ptr1 += (strlen(Name));
return atoi(ptr1);
}

@ -0,0 +1,126 @@
#ifdef Kernel
#define Vers 5,2,9,2
#define Verstring "5.2.9.2\0"
#define Datestring "September 2012"
#define VerComments "G8BPQ Packet Switch V5.2.9.2\0"
#define VerCopyright "Copyright © 2001-2012 John Wiseman G8BPQ\0"
#define VerDesc "BPQ32 Switch\0"
#endif
#define KVers 6,0,24,70
#define KVerstring "6.0.24.70\0"
#ifdef CKernel
#define Vers KVers
#define Verstring KVerstring
#define Datestring "April 2025"
#define VerComments "G8BPQ Packet Switch (C Version)" KVerstring
#define VerCopyright "Copyright © 2001-2025 John Wiseman G8BPQ\0"
#define VerDesc "BPQ32 Switch\0"
#define VerProduct "BPQ32"
#endif
#ifdef TermTCP
#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 VerDesc "Simple TCP Terminal Program for G8BPQ Switch\0"
#define VerProduct "BPQTermTCP"
#endif
#ifdef BPQTerm
#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 VerDesc "Simple Terminal Program for G8BPQ Switch\0"
#define VerProduct "BPQTerminal"
#endif
#ifdef BPQTermMDI
#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 VerDesc "MDI Terminal Program for G8BPQ Switch\0"
#endif
#ifdef MAIL
#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 VerDesc "Mail server for G8BPQ's 32 Bit Switch\0"
#define VerProduct "BPQMail"
#endif
#ifdef HOSTMODES
#define Vers 1,1,8,1
#define Verstring "1.1.8.1\0"
//#define SPECIALVERSION "Test 3"
#define VerComments "Host Modes Emulator for G8BPQ Packet Switch\0"
#define VerCopyright "Copyright © 2009-2019 John Wiseman G8BPQ\0"
#define VerDesc "Host Modes Emulator for G8BPQ's 32 Bit Switch\0"
#define VerProduct "BPQHostModes"
#endif
#ifdef UIUTIL
#define Vers 0,1,3,1
#define Verstring "0.1.3.1\0"
#define VerComments "Beacon Utility for G8BPQ Packet Switch\0"
#define VerCopyright "Copyright © 2011-2019 John Wiseman G8BPQ\0"
#define VerDesc "Beacon Utility for G8BPQ Switch\0"
#define VerProduct "BPQUIUtil"
#endif
#ifdef AUTH
#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 VerDesc "Password Generation Utility for G8BPQ Switch\0"
#endif
#ifdef APRS
#define Vers KVers
#define Verstring KVerstring
#define VerComments "APRS Client for G8BPQ Switch\0"
#define VerCopyright "Copyright © 2012-2025 John Wiseman G8BPQ\0"
#define VerDesc "APRS Client for G8BPQ Switch\0"
#define VerProduct "BPQAPRS"
#endif
#ifdef CHAT
#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 VerDesc "Chat server for G8BPQ's 32 Bit Switch\0"
#define VerProduct "BPQChat"
#endif

@ -0,0 +1,435 @@
#define _CRT_SECURE_NO_DEPRECATE
#ifndef NOMQTT
#include "MQTTAsync.h"
#ifndef WIN32
#include <jansson.h>
#endif
#include "cheaders.h"
#include "asmstrucs.h"
#include "mqtt.h"
extern int MQTT_Connecting;
extern int MQTT_Connected;
DllExport int APIENTRY MQTTSend(char * topic, char * Msg, int MsgLen);
MQTTAsync client = NULL;
time_t MQTTLastStatus = 0;
void MQTTSendStatus()
{
char topic[256];
char payload[128];
sprintf(topic, "PACKETNODE/%s", NODECALLLOPPED);
strcpy(payload,"{\"status\":\"online\"}");
MQTTSend(topic, payload, strlen(payload));
MQTTLastStatus = time(NULL);
}
void MQTTTimer()
{
if (MQTT_Connecting == 0 && MQTT_Connected == 0)
MQTTConnect(MQTT_HOST, MQTT_PORT, MQTT_USER, MQTT_PASS);
if ((time(NULL) - MQTTLastStatus) > 1800)
MQTTSendStatus();
}
void MQTTDisconnect()
{
if (MQTT_Connected)
{
MQTTAsync_disconnectOptions disc_opts = MQTTAsync_disconnectOptions_initializer;
MQTTAsync_disconnect(client, &disc_opts);
MQTT_Connecting = MQTT_Connected = 0;
// Try to recconect. If it fails system will rety every minute
MQTTConnect(MQTT_HOST, MQTT_PORT, MQTT_USER, MQTT_PASS);
}
}
DllExport int APIENTRY MQTTSend(char * topic, char * Msg, int MsgLen)
{
int rc;
MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
pubmsg.payload = Msg;
pubmsg.payloadlen = MsgLen;
rc = MQTTAsync_sendMessage(client, topic, &pubmsg, &opts);
if (rc)
MQTTDisconnect();
return rc;
}
void onConnect(void* context, MQTTAsync_successData* response)
{
MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
MQTT_Connecting = 0;
MQTT_Connected = 1;
printf("Successful MQTT connection\n");
// Send start up message
MQTTSendStatus();
}
void onConnectFailure(void* context, MQTTAsync_failureData* response)
{
printf("MQTT connection failed, rc %d\n", response ? response->code : 0);
MQTT_Connecting = 0;
}
char* jsonEncodeMessage(MESSAGE *msg)
{
char From[10];
char To[10];
char buffer[1024];
unsigned long long SaveMMASK = MMASK;
BOOL SaveMTX = MTX;
BOOL SaveMCOM = MCOM;
BOOL SaveMUI = MUIONLY;
int len;
char *msg_str;
char payload_timestamp[16];
struct tm * TM = localtime(&msg->Timestamp);
sprintf(payload_timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec);
IntSetTraceOptionsEx(MMASK, TRUE, TRUE, FALSE);
From[ConvFromAX25(msg->ORIGIN, From)] = 0;
To[ConvFromAX25(msg->DEST, To)] = 0;
len = IntDecodeFrame(msg, buffer, msg->Timestamp, 0xffffffffffffffff, FALSE, FALSE);
IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI);
buffer[len] = 0;
#ifdef WIN32
msg_str = zalloc(2048);
sprintf(msg_str, "{\"from\": \"%s\", \"to\": \"%s\", \"payload\": \"%s\", \"port\": %d, \"timestamp\": \"%s\"}",
From, To, buffer, msg->PORT, payload_timestamp);
#else
json_t *root;
root = json_object();
json_object_set_new(root, "from", json_string(From));
json_object_set_new(root, "to", json_string(To));
json_object_set_new(root, "payload", json_string(buffer));
json_object_set_new(root, "port", json_integer(msg->PORT));
sprintf(payload_timestamp, "%02d:%02d:%02d", TM->tm_hour, TM->tm_min, TM->tm_sec);
json_object_set_new(root, "timestamp", json_string(payload_timestamp));
msg_str = json_dumps(root, 0);
json_decref(root);
#endif
return msg_str;
}
void MQTTKISSTX(void *message)
{
MESSAGE *msg = (MESSAGE *)message;
char topic[256];
char *msg_str;
sprintf(topic, "PACKETNODE/ax25/trace/bpqformat/%s/sent/%d", NODECALLLOPPED, msg->PORT);
msg_str = jsonEncodeMessage(msg);
MQTTSend(topic, msg_str, strlen(msg_str));
free(msg_str);
}
void MQTTKISSTX_RAW(char* buffer, int bufferLength, void* PORT)
{
PPORTCONTROL PPORT = (PPORTCONTROL)PORT;
char topic[256];
sprintf(topic, "PACKETNODE/kiss/%s/sent/%d", NODECALLLOPPED, PPORT->PORTNUMBER);
MQTTSend(topic, buffer, bufferLength);
}
void MQTTKISSRX(void *message)
{
MESSAGE *msg = (MESSAGE *)message;
char topic[256];
char *msg_str;
sprintf(topic, "PACKETNODE/ax25/trace/bpqformat/%s/rcvd/%d", NODECALLLOPPED, msg->PORT);
msg_str = jsonEncodeMessage(msg);
MQTTSend(topic, msg_str, strlen(msg_str));
free(msg_str);
}
void MQTTKISSRX_RAW(char* buffer, int bufferLength, void* PORT)
{
PPORTCONTROL PPORT = (PPORTCONTROL)PORT;
char topic[256];
sprintf(topic, "PACKETNODE/kiss/%s/rcvd/%d", NODECALLLOPPED, PPORT->PORTNUMBER);
MQTTSend(topic, buffer, bufferLength);
}
void MQTTReportSession(char * Msg)
{
char topic[256];
sprintf(topic, "PACKETNODE/stats/session/%s", NODECALLLOPPED);
MQTTSend(topic, Msg, strlen(Msg));
}
char* replace(char* str, char* a, char* b)
{
int len = strlen(str);
int lena = strlen(a), lenb = strlen(b);
char * p;
for (p = str; p = strstr(p, a); p) {
if (lena != lenb) // shift end as needed
memmove(p + lenb, p + lena,
len - (p - str) + lenb);
memcpy(p, b, lenb);
}
return str;
}
int MQTTPublish(void *message, char *topic)
{
MESSAGE *msg = (MESSAGE *)message;
char From[10];
char To[10];
MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
unsigned long long SaveMMASK = MMASK;
BOOL SaveMTX = MTX;
BOOL SaveMCOM = MCOM;
BOOL SaveMUI = MUIONLY;
int len;
char* replaced_buffer;
char buffer[1024];
time_t timestamp = msg->Timestamp;
From[ConvFromAX25(msg->ORIGIN, From)] = 0;
To[ConvFromAX25(msg->DEST, To)] = 0;
IntSetTraceOptionsEx(8, TRUE, TRUE, FALSE);
len = IntDecodeFrame(msg, buffer, timestamp, 1, FALSE, FALSE);
IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI);
// MQTT _really_ doesn't like \r, so replace it with something
// that is at least human readable
replaced_buffer = replace(buffer, "\r", "\r\n");
pubmsg.payload = replaced_buffer;
pubmsg.payloadlen = strlen(replaced_buffer);
printf("%s\n", replaced_buffer);
return MQTTAsync_sendMessage(client, topic, &pubmsg, &opts);
}
int MQTTConnect(char* host, int port, char* user, char* pass)
{
MQTTAsync_connectOptions conn_opts = MQTTAsync_connectOptions_initializer;
int rc;
char hostString[256];
sprintf(hostString, "tcp://%s:%d", host, port);
printf("MQTT Connect to %s\n", hostString);
rc = MQTTAsync_create(&client, hostString, NODECALLLOPPED, MQTTCLIENT_PERSISTENCE_NONE, NULL);
if (rc != MQTTASYNC_SUCCESS)
{
printf("Failed to create client, return code %d\n", rc);
return rc;
}
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
conn_opts.username = user;
conn_opts.password = pass;
conn_opts.onSuccess = onConnect;
conn_opts.onFailure = onConnectFailure;
// conn_opts.automaticReconnect = 1;
// conn_opts.minRetryInterval = 30;
// conn_opts.maxRetryInterval = 300;
rc = MQTTAsync_connect(client, &conn_opts);
if (rc != MQTTASYNC_SUCCESS)
{
printf("Failed to start connect, return code %d\n", rc);
return rc;
}
MQTT_Connecting = 1;
return 0;
}
// Message Database Entry. Designed to be compatible with FBB
#define NBBBS 160 // Max BBSes we can forward to. Must be Multiple of 8, and must be 80 for FBB compatibliliy
#define NBMASK NBBBS/8 // Number of bytes in Forward bitlists.
#pragma pack(1)
struct MsgInfo
{
char type;
char status;
int number;
int length;
int xdatereceived;
char bbsfrom[7]; // ? BBS we got it from ?
char via[41];
char from[7];
char to[7];
char bid[13];
char title[61];
int nntpnum; // Number within topic (ie Bull TO Addr) - used for nntp
UCHAR B2Flags; // Not all flags specific to B2
#define B2Msg 1 // Set if Message File is a formatted B2 message
#define Attachments 2 // Set if B2 message has attachments
#define FromPaclink 4
#define FromCMS 8
#define FromRMSExpress 16
#define RadioOnlyMsg 32 // Received using call-T
#define RadioOnlyFwd 64 // Received using call-R
#define WarnNotForwardedSent 128
int xdatecreated;
int xdatechanged;
UCHAR fbbs[NBMASK];
UCHAR forw[NBMASK];
char emailfrom[41];
char Locked; // Set if selected for sending (NTS Pickup)
char Defered; // FBB response '=' received
UCHAR UTF8; // Set if Message is in UTF8 (ie from POP/SMTP)
// For 64 bit time_t compatibility define as long long
// (so struct is same with 32 or 64 bit time_t)
int64_t datereceived;
int64_t datecreated;
int64_t datechanged;
char Spare[61 - 24]; // For future use
} ;
#pragma pack()
void MQTTMessageEvent(void* message)
{
struct MsgInfo* msg = (struct MsgInfo *)message;
char *msg_str;
char * ptr;
MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
char topic[256];
json_t *root = json_object();
json_object_set_new(root, "id", json_integer(msg->number));
json_object_set_new(root, "size", json_integer(msg->length));
json_object_set_new(root, "type", json_string(msg->type == 'P' ? "P" : "B"));
json_object_set_new(root, "to", json_string(msg->to));
json_object_set_new(root, "from", json_string(msg->from));
json_object_set_new(root, "subj", json_string(msg->title));
switch(msg->status) {
case 'N':
json_object_set_new(root, "event", json_string("newmsg"));
break;
case 'F':
json_object_set_new(root, "event", json_string("fwded"));
break;
case 'R':
json_object_set_new(root, "event", json_string("read"));
break;
case 'K':
json_object_set_new(root, "event", json_string("killed"));
break;
}
msg_str = json_dumps(root, 0);
pubmsg.payload = msg_str;
pubmsg.payloadlen = strlen(msg_str);
sprintf(topic, "PACKETNODE/event/%s/pmsg", NODECALLLOPPED);
MQTTAsync_sendMessage(client, topic, &pubmsg, &opts);
}
#else
// Dummies ofr build without MQTT libraries
int MQTTConnect(char* host, int port, char* user, char* pass)
{
return 0;
}
void MQTTKISSTX(void *message) {};
void MQTTKISSTX_RAW(char* buffer, int bufferLength, void* PORT) {};
void MQTTKISSRX(void *message) {};
void MQTTKISSRX_RAW(char* buffer, int bufferLength, void* PORT) {};
void MQTTTimer() {};
void MQTTReportSession(char * Msg) {};
void MQTTMessageEvent(void* message);
#endif

Binary file not shown.

@ -888,6 +888,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff)
{ {
if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime))
{ {
Debugprintf("ARDOP closing session on SessionTimelimit");
ARDOPSendCommand(TNC, "DISCONNECT", TRUE); ARDOPSendCommand(TNC, "DISCONNECT", TRUE);
STREAM->ReportDISC = 1; STREAM->ReportDISC = 1;
STREAM->AttachTime = 0; STREAM->AttachTime = 0;
@ -900,6 +901,7 @@ static size_t ExtProc(int fn, int port, PDATAMESSAGE buff)
{ {
if (STREAM->AttachTime && TNC->AttachTimeLimit && time(NULL) > (TNC->AttachTimeLimit + STREAM->AttachTime)) if (STREAM->AttachTime && TNC->AttachTimeLimit && time(NULL) > (TNC->AttachTimeLimit + STREAM->AttachTime))
{ {
Debugprintf("ARDOP closing session on AttachTimelimit");
STREAM->ReportDISC = 1; STREAM->ReportDISC = 1;
STREAM->AttachTime = 0; STREAM->AttachTime = 0;
} }
@ -3141,6 +3143,7 @@ VOID ARDOPProcessResponse(struct TNCINFO * TNC, UCHAR * Buffer, int MsgLen)
// Incoming Connect // Incoming Connect
TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit
STREAM->AttachTime = time(NULL);
// Stop other ports in same group // Stop other ports in same group

@ -1257,8 +1257,14 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
// Fix RigConrol with Chanxx but no other settings (66) // Fix RigConrol with Chanxx but no other settings (66)
// Add option to compress L2 frames (67) // Add option to compress L2 frames (67)
// Sort Routes displays (67) // Sort Routes displays (67)
// Fix Ardop session premature close (70)
// Add timestamps to log entries in Web Driver windows (70)
// Generate stack backtrace if SIGSEGV or SIGABRT occur (Linux) (70)
// Remove some debug logging from L2 code (70)
// Fix compiling LinBPQ with nomqtt option (70)
// Improve handling of binary data in RHP interface (70)
// Fix sending KISS commands to multiport or multidropped TNCs (70)
// Add MHUV and MHLV commands (Verbose listing with timestamps in clock time) (70)
#define CKernel #define CKernel

14
Cmd.c

@ -3622,7 +3622,7 @@ VOID MHCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CM
} }
else else
{ {
if (CMD->String[2] == 'V') // MHV if (CMD->String[2] == 'V' || CMD->String[3] == 'V') // MHV
{ {
Bufferptr = Cmdprintf(Session, Bufferptr, "MHeard List %s for Port %d\r", MYNODECALL, Port); 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, "Callsign Last heard Pkts RX via Digi ;) \r");
@ -4326,6 +4326,8 @@ struct CMDX COMMANDS[] =
"MHU ",3,MHCMD,0, // UTC Times "MHU ",3,MHCMD,0, // UTC Times
"MHL ",3,MHCMD,0, // Local Times "MHL ",3,MHCMD,0, // Local Times
"MHV ",3,MHCMD,0, "MHV ",3,MHCMD,0,
"MHUV ",3,MHCMD,0, // UTC Times
"MHLV ",3,MHCMD,0, // Local Times
"MHEARD ",1,MHCMD,0, "MHEARD ",1,MHCMD,0,
"APRS ",2,APRSCMD,0, "APRS ",2,APRSCMD,0,
"ATTACH ",1,ATTACHCMD,0, "ATTACH ",1,ATTACHCMD,0,
@ -5543,22 +5545,14 @@ VOID KISSCMD(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct
KISS = (struct KISSINFO *) PORT; 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 // Send Command
KissLen = KissEncode(KissString, ENCBUFF, KissLen); KissLen = KissEncode(KissString, ENCBUFF, KissLen);
PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q
PORT->Session = Session; PORT->Session = Session;
PORT->LastKISSCmdTime = time(NULL); PORT->LastKISSCmdTime = time(NULL);
PORT = (struct PORTCONTROL *)KISS->FIRSTPORT; // ALL FRAMES GO ON SAME Q
ASYSEND(PORT, ENCBUFF, KissLen); ASYSEND(PORT, ENCBUFF, KissLen);
Bufferptr = Cmdprintf(Session, Bufferptr, "Command Sent\r"); Bufferptr = Cmdprintf(Session, Bufferptr, "Command Sent\r");

@ -1061,6 +1061,7 @@ BOOL ReadConfigFile(int Port, int ProcLine(char * buf, int Port))
WritetoConsoleLocal("\n"); WritetoConsoleLocal("\n");
WritetoConsoleLocal("Bad config record "); WritetoConsoleLocal("Bad config record ");
WritetoConsoleLocal(errbuf); WritetoConsoleLocal(errbuf);
WritetoConsoleLocal("\n");
} }
} }
} }

@ -2431,12 +2431,11 @@ CheckNSLoop:
{ {
// Already have a copy, so discard old and keep this // 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])); ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS]));
} }
else else
{ {
Debugprintf ("Frame %d out of seq - save", NS); // Debugprintf ("Frame %d out of seq - save", NS);
} }
Buffer->CHAIN = 0; Buffer->CHAIN = 0;
@ -3919,7 +3918,6 @@ CheckNSLoop2:
struct PORTCONTROL * PORT = LINK->LINKPORT; struct PORTCONTROL * PORT = LINK->LINKPORT;
MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]); 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 PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer
// NR has been updated. // NR has been updated.

@ -381,41 +381,48 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
#include <signal.h> #include <signal.h>
// Linux Signal Handlers // Linux Signal Handlers
static void segvhandler(int sig) static void segvhandler(int sig)
{ {
void *array[10]; void *array[10];
size_t size; size_t size;
char msg[] = "SIGSEGV Received\n"; char msg[] = "\nSIGSEGV Received\n";
write(STDERR_FILENO, msg, strlen(msg));
write(STDERR_FILENO, msg, strlen(msg)); // get void*'s for all entries on the stack
size = backtrace(array, 10);
// get void*'s for all entries on the stack // print out all the frames to stderr
size = backtrace(array, 10);
// print out all the frames to stderr backtrace_symbols_fd(array, size, STDERR_FILENO);
backtrace_symbols_fd(array, size, STDERR_FILENO); write(STDOUT_FILENO, msg, strlen(msg));
backtrace_symbols_fd(array, size, STDOUT_FILENO);
exit(1); exit(1);
} }
static void abrthandler(int sig) static void abrthandler(int sig)
{ {
void *array[10]; void *array[10];
size_t size; size_t size;
char msg[] = "SIGABRT Received\n"; char msg[] = "\nSIGABRT Received\n";
write(STDERR_FILENO, msg, strlen(msg)); write(STDERR_FILENO, msg, strlen(msg));
write(STDOUT_FILENO, msg, strlen(msg));
// get void*'s for all entries on the stack // get void*'s for all entries on the stack
size = backtrace(array, 10); size = backtrace(array, 10);
backtrace_symbols_fd(array, size, STDERR_FILENO); backtrace_symbols_fd(array, size, STDERR_FILENO);
exit(1); write(STDOUT_FILENO, msg, strlen(msg));
backtrace_symbols_fd(array, size, STDOUT_FILENO);
exit(1);
} }
static void sigterm_handler(int sig) static void sigterm_handler(int sig)
{ {
syslog(LOG_INFO, "terminating on SIGTERM\n"); syslog(LOG_INFO, "terminating on SIGTERM\n");

51
RHP.c

@ -451,8 +451,13 @@ int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer)
int ID; int ID;
char * Data; char * Data;
char * ptr; char * ptr;
unsigned char * uptr;
int c; int c;
int Len; int Len;
unsigned int HexCode1;
unsigned int HexCode2;
int n;
int Handle = 1; int Handle = 1;
@ -470,9 +475,10 @@ int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer)
RHPSession = RHPSessions[Handle - 1]; RHPSession = RHPSessions[Handle - 1];
// Look for \ escapes // Look for \ escapes, Can now also get \u00c3
ptr = Data; ptr = Data;
Len = strlen(Data); // in case no escapes
while (ptr = strchr(ptr, '\\')) while (ptr = strchr(ptr, '\\'))
{ {
@ -483,23 +489,60 @@ int processRHCPSend(SOCKET Socket, char * Msg, char * ReplyBuffer)
case 'r': case 'r':
*ptr = 13; *ptr = 13;
memmove(ptr + 1, ptr + 2, strlen(ptr + 1));
break;
case 'u':
HexCode1 = HexCode2 = 0;
n = toupper(ptr[2]) - '0';
if (n > 9) n = n - 7;
HexCode1 |= n << 4;
n = toupper(ptr[3]) - '0';
if (n > 9) n = n - 7;
HexCode1 |= n;
n = toupper(ptr[4]) - '0';
if (n > 9) n = n - 7;
HexCode2 |= n << 4;
n = toupper(ptr[5]) - '0';
if (n > 9) n = n - 7;
HexCode2 |= n;
if (HexCode1 == 0 || HexCode1 == 0xC2)
{
uptr = ptr;
*uptr = HexCode2;
}
else if (HexCode1 == 0xc2)
{
uptr = ptr;
*uptr = HexCode2 + 0x40;
}
memmove(ptr + 1, ptr + 6, strlen(ptr + 5));
break; break;
case '\\': case '\\':
*ptr = '\\'; *ptr = '\\';
memmove(ptr + 1, ptr + 2, strlen(ptr + 1));
break; break;
case '"': case '"':
*ptr = '"'; *ptr = '"';
memmove(ptr + 1, ptr + 2, strlen(ptr + 1));
break; break;
} }
memmove(ptr + 1, ptr + 2, strlen(ptr + 1));
ptr++; ptr++;
Len = ptr - Data;
} }
Len = strlen(Data);
ptr = Data; ptr = Data;
while (Len > RHPPaclen) while (Len > RHPPaclen)
@ -640,7 +683,7 @@ void RHPPoll()
// Message is JSON so Convert CR to \r, \ to \\ " to \" // Message is JSON so Convert CR to \r, \ to \\ " to \"
// Looks like I need to escape everything not between 0x20 and 0x7f eg \U00c3 // Looks like I need to escape everything not between 0x20 and 0x7f eg \u00c3
while (c = *(ptr)) while (c = *(ptr))

@ -1720,7 +1720,6 @@ VConnected:
GetSemaphore(&Semaphore, 52); GetSemaphore(&Semaphore, 52);
VARAProcessReceivedControl(TNC); VARAProcessReceivedControl(TNC);
FreeSemaphore(&Semaphore); FreeSemaphore(&Semaphore);
Debugprintf("VARA Returned from processing control packet");
} }
if (FD_ISSET(TNC->TCPDataSock, &readfs)) if (FD_ISSET(TNC->TCPDataSock, &readfs))

@ -10,15 +10,15 @@
#endif #endif
#define KVers 6,0,24,69 #define KVers 6,0,24,70
#define KVerstring "6.0.24.69\0" #define KVerstring "6.0.24.70\0"
#ifdef CKernel #ifdef CKernel
#define Vers KVers #define Vers KVers
#define Verstring KVerstring #define Verstring KVerstring
#define Datestring "March 2025" #define Datestring "April 2025"
#define VerComments "G8BPQ Packet Switch (C Version)" KVerstring #define VerComments "G8BPQ Packet Switch (C Version)" KVerstring
#define VerCopyright "Copyright © 2001-2025 John Wiseman G8BPQ\0" #define VerCopyright "Copyright © 2001-2025 John Wiseman G8BPQ\0"
#define VerDesc "BPQ32 Switch\0" #define VerDesc "BPQ32 Switch\0"

@ -139,6 +139,10 @@ VOID WritetoTraceSupport(struct TNCINFO * TNC, char * Msg, int Len)
int LineLen, i; int LineLen, i;
UCHAR Save; UCHAR Save;
int SaveLen = Len; int SaveLen = Len;
char Time[16];
time_t T;
struct tm * tm;
if (Len < 0) if (Len < 0)
return; return;
@ -206,10 +210,16 @@ lineloop:
#endif #endif
// Write to Web Buffer // Write to Web Buffer
T = time(NULL);
tm = gmtime(&T);
sprintf_s(Time, sizeof(Time),"%02d:%02d ", tm->tm_hour, tm->tm_min);
strcat(TNC->WebBuffer, Time);
strcat(TNC->WebBuffer, Line); strcat(TNC->WebBuffer, Line);
strcat(TNC->WebBuffer, "\r\n"); strcat(TNC->WebBuffer, "\r\n");
if (strlen(TNC->WebBuffer) > 4500) if (strlen(TNC->WebBuffer) > 4500)
memmove(TNC->WebBuffer, &TNC->WebBuffer[500], 4490); // Make sure null is moved memmove(TNC->WebBuffer, &TNC->WebBuffer[500], strlen(&TNC->WebBuffer[500]) + 1); // Make sure null is moved
Skip: Skip:
ptr1 = ptr2; ptr1 = ptr2;
@ -248,10 +258,16 @@ lineloop:
#else #else
index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) ptr1 ); index=SendMessage(TNC->hMonitor, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR) ptr1 );
#endif #endif
T = time(NULL);
tm = gmtime(&T);
sprintf_s(Time, sizeof(Time),"%02d:%02d ", tm->tm_hour, tm->tm_min);
strcat(TNC->WebBuffer, Time);
strcat(TNC->WebBuffer, ptr1); strcat(TNC->WebBuffer, ptr1);
strcat(TNC->WebBuffer, "\r\n"); strcat(TNC->WebBuffer, "\r\n");
if (strlen(TNC->WebBuffer) > 4500) if (strlen(TNC->WebBuffer) > 4500)
memmove(TNC->WebBuffer, &TNC->WebBuffer[500], 4490); // Make sure null is moved memmove(TNC->WebBuffer, &TNC->WebBuffer[500], strlen(&TNC->WebBuffer[500]) + 1); // Make sure null is moved
} }
} }

@ -19,15 +19,15 @@ OBJS = pngwtran.o pngrtran.o pngset.o pngrio.o pngwio.o pngtrans.o pngrutil.o pn
CC = gcc CC = gcc
all: CFLAGS = -DLINBPQ -MMD -g -rdynamic -fcommon all: CFLAGS = -DLINBPQ -MMD -g -rdynamic -fcommon -fasynchronous-unwind-tables
all: LDFLAGS = -l:libpaho-mqtt3a.a -l:libjansson.a all: LDFLAGS = -l:libpaho-mqtt3a.a -l:libjansson.a
all: linbpq all: linbpq
nomqtt: CFLAGS = -DLINBPQ -MMD -fcommon -g -rdynamic -DNOMQTT nomqtt: CFLAGS = -DLINBPQ -MMD -fcommon -g -rdynamic -DNOMQTT -fasynchronous-unwind-tables
nomqtt: linbpq nomqtt: linbpq
noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g -rdynamic -fcommon noi2c: CFLAGS = -DLINBPQ -MMD -DNOI2C -g -rdynamic -fcommon -fasynchronous-unwind-tables
noi2c: linbpq noi2c: linbpq

@ -429,6 +429,7 @@ void MQTTKISSRX(void *message) {};
void MQTTKISSRX_RAW(char* buffer, int bufferLength, void* PORT) {}; void MQTTKISSRX_RAW(char* buffer, int bufferLength, void* PORT) {};
void MQTTTimer() {}; void MQTTTimer() {};
void MQTTReportSession(char * Msg) {}; void MQTTReportSession(char * Msg) {};
void MQTTMessageEvent(void* message);
#endif #endif

@ -93,7 +93,12 @@ int i;
const char * rootdescurl = 0; const char * rootdescurl = 0;
const char * multicastif = 0; const char * multicastif = 0;
const char * minissdpdpath = 0; const char * minissdpdpath = 0;
#ifdef UPNP_LOCAL_PORT_ANY
int localport = UPNP_LOCAL_PORT_ANY; int localport = UPNP_LOCAL_PORT_ANY;
#else
#pragma message "API 10"
int localport = 0;
#endif
int retcode = 0; int retcode = 0;
int error = 0; int error = 0;
int ipv6 = 0; int ipv6 = 0;
@ -119,8 +124,11 @@ int upnpInit()
{ {
if (devlist == NULL) if (devlist == NULL)
{ {
#if MINIUPNPC_API_VERSION == 10
devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, &error);
#else
devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error); devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error);
#endif
if (devlist == NULL) if (devlist == NULL)
{ {
Consoleprintf("Failed to find a UPNP device"); Consoleprintf("Failed to find a UPNP device");
@ -150,8 +158,11 @@ int upnpClose()
{ {
if (devlist == NULL) if (devlist == NULL)
{ {
#if MINIUPNPC_API_VERSION == 10
devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, &error);
#else
devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error); devlist = upnpDiscover(2000, multicastif, minissdpdpath, localport, ipv6, ttl, &error);
#endif
if (devlist == NULL) if (devlist == NULL)
{ {
Consoleprintf("Failed to find a UPNP device"); Consoleprintf("Failed to find a UPNP device");

Loading…
Cancel
Save

Powered by TurnKey Linux.