From 6620d4ae1f9c3941a92525bee8bd11aaf79cdbab Mon Sep 17 00:00:00 2001 From: John Wiseman Date: Thu, 5 Jun 2025 09:29:26 +0100 Subject: [PATCH] 6.0.24.72a --- AGWAPI.c | 34 +- AGWAPI.c.bak | 1721 ++++++++++++++++++++++++++++++++++++++++++++++++++ AGWMoncode.c | 92 ++- Bpq32.c | 2 + L2Code.c | 1 + Versions.h | 4 +- bpqaxip.c | 1 + 7 files changed, 1844 insertions(+), 11 deletions(-) create mode 100644 AGWAPI.c.bak diff --git a/AGWAPI.c b/AGWAPI.c index dcf3494..e41efbb 100644 --- a/AGWAPI.c +++ b/AGWAPI.c @@ -36,7 +36,8 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses struct AGWHeader { - unsigned int Port; + uint8_t Port; + uint8_t filler1[3]; unsigned char DataKind; unsigned char filler2; unsigned char PID; @@ -751,6 +752,12 @@ int AGWDoMonitorData() RXFlag = TRUE; } + if (Port == 0) + { + Debugprintf("AGWMON Port number is zero"); + return 0; + } + // Can now have different mon flags per connection, so need to run decode for each socket for (n = 1; n<= CurrentSockets; n++) @@ -829,9 +836,7 @@ int AGWDoMonitorData() } } } - - return 0; - + return 0; } int DeleteConnection(struct BPQConnectionInfo * Con) @@ -1128,6 +1133,7 @@ int AGWDataSocket_Read(struct AGWSocketConnectionInfo * sockptr, SOCKET sock) int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr) { int AGWVersion[2]={2003,999}; + byte AGWPortCaps[12] = { 0, 255, 30, 10, 63, 10, 4, 0, 1, 0, 0, 0 }; char AGWRegReply[1]; struct BPQConnectionInfo * Connection; int Stream; @@ -1293,9 +1299,7 @@ int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr) // Version memset(&AGWTXHeader,0,36); - AGWTXHeader.DataKind = 'R'; - AGWTXHeader.DataLength = 8; // Length SendtoSocket(sockptr->socket, (char *)&AGWVersion[0]); @@ -1309,15 +1313,27 @@ int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr) memset(&AGWTXHeader,0,36); - AGWTXHeader.DataKind = 'G'; - AGWTXHeader.DataLength =(int)strlen(AGWPorts)+1; // Length SendtoSocket(sockptr->socket, AGWPorts); return 0; + + case 'g': + + // Port capabilities. Currently hard-coded. + + AGWTXHeader.Port = sockptr->AGWRXHeader.Port; + AGWTXHeader.DataKind = 'g'; + AGWTXHeader.DataLength = 12; + + SendtoSocket(sockptr->socket, (char *)&AGWPortCaps[0]); + + return 0; + + case 'k': @@ -1416,6 +1432,8 @@ int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr) AGWTXHeader.DataKind = 'X'; + memcpy(&AGWTXHeader.callfrom, RegCall, 10); + AGWTXHeader.DataLength = 1; // Length AGWRegReply[0] = 1; diff --git a/AGWAPI.c.bak b/AGWAPI.c.bak new file mode 100644 index 0000000..dcf3494 --- /dev/null +++ b/AGWAPI.c.bak @@ -0,0 +1,1721 @@ +/* +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 +*/ + +/* + + AGWPE emulation Interface for BPQ32 + + Based on AGWtoBPQ + +*/ +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" + +#include "bpq32.h" + +// Internal AGW Interface + +//#define VVICON 400 + +struct AGWHeader +{ + unsigned int Port; + unsigned char DataKind; + unsigned char filler2; + unsigned char PID; + unsigned char filler3; + unsigned char callfrom[10]; + unsigned char callto[10]; + unsigned int DataLength; + int reserved; +}; + +struct AGWSocketConnectionInfo +{ + int Number; // Number of record - for AGWConnections display + SOCKET socket; + SOCKADDR_IN sin; + BOOL SocketActive; + BOOL RawFlag; + BOOL MonFlag; + BOOL useLocalTime; + BOOL doNodes; + unsigned char CallSign1[10]; + unsigned char CallSign2[10]; + BOOL GotHeader; + int MsgDataLength; + struct AGWHeader AGWRXHeader; + unsigned char * MsgData; +}; + +struct BPQConnectionInfo +{ + struct AGWSocketConnectionInfo * SocketIndex; + int BPQStream; + unsigned char CallKey[21]; // Port + two calls + BOOL Connecting; // Set while waiting for connection to complete + BOOL Listening; + int ApplMask; +} ConInfoRec; + + +char AGWPorts[1000]; + +struct AGWHeader AGWTXHeader; + +char SessionList[100]; + +struct BPQConnectionInfo AGWConnections[65]; + +#define MaxSockets 64 + +static struct AGWSocketConnectionInfo Sockets[MaxSockets+1]; + +int CurrentConnections; + +static int CurrentSockets=0; + +int AGWPort = 0; +int AGWSessions = 0; +int AGWMask = 0; + +BOOL LoopMonFlag = FALSE; +BOOL Loopflag = FALSE; + +extern char pgm[256]; + +SOCKET agwsock; + +extern BPQVECSTRUC * AGWMONVECPTR; + +extern int SemHeldByAPI; + +char szBuff[80]; + +static int VisiblePortToRealPort[MaxBPQPortNo + 1]; + +int SetUpHostSessions(); +int DisplaySessions(); +int AGWDoStateChange(int Stream); +int AGWDoReceivedData(int Stream); +int AGWDoMonitorData(); +int AGWConnected(struct BPQConnectionInfo * Con, int Stream); +int AGWDisconnected(struct BPQConnectionInfo * Con, int Stream); +int DeleteConnection(struct BPQConnectionInfo * Con); +int SendConMsgtoAppl(BOOL Incomming, struct BPQConnectionInfo * Con, char * CallSign); +int SendDisMsgtoAppl(char * Msg, struct AGWSocketConnectionInfo * sockptr); +int AGWSocket_Accept(SOCKET SocketId); +int Socket_Data(int SocketId,int error, int eventcode); +int AGWDataSocket_Read(struct AGWSocketConnectionInfo * sockptr, SOCKET sock); +int DataSocket_Write(struct AGWSocketConnectionInfo * sockptr, SOCKET sock); +int AGWGetSessionKey(char * key, struct AGWSocketConnectionInfo * sockptr); +int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr); +int SendDataToAppl(int Stream, byte * Buffer, int Length); +int InternalAGWDecodeFrame(char * msg, char * buffer, time_t Stamp, int * FrameType, int useLocalTime, int doNodes); +int AGWDataSocket_Disconnect( struct AGWSocketConnectionInfo * sockptr); +int SendRawPacket(struct AGWSocketConnectionInfo * sockptr, char *txmsg, int Length); +int ShowApps(); +int Terminate(); +int SendtoSocket(SOCKET sock,char * Msg); +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...); + +VOID Poll_AGW() +{ + int state, change, i; + int Stream; + struct BPQConnectionInfo * Con; + struct AGWSocketConnectionInfo * sockptr; + + // Look for incoming connects + + fd_set readfd, writefd, exceptfd; + struct timeval timeout; + int retval; + int n; + int Active = 0; + SOCKET maxsock; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; // poll + + FD_ZERO(&readfd); + + FD_SET(agwsock, &readfd); + + retval = select((int)agwsock + 1, &readfd, NULL, NULL, &timeout); + + if (retval == -1) + { + retval = WSAGetLastError(); + perror("listen select"); + } + + if (retval) + if (FD_ISSET((int)agwsock, &readfd)) + AGWSocket_Accept(agwsock); + + // look for data on any active sockets + + maxsock = 0; + + FD_ZERO(&readfd); + FD_ZERO(&writefd); + FD_ZERO(&exceptfd); + + // Check for data on active streams + + for (i = 0; i < CurrentConnections; i++) + { + Con = &AGWConnections[i]; + Stream = Con->BPQStream; + + SessionState(Stream, &state, &change); + + if (change == 1) + { + if (state == 1) + + // Connected + + AGWConnected(Con, Stream); + else + AGWDisconnected(Con, Stream); + } + + + if (Con->SocketIndex) // Active Session + { + AGWDoReceivedData(Stream); + + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + SessionState(Stream, &state, &change); + + if (change == 1) + { + if (state == 1) + + // Connected + + AGWConnected(Con, Stream); + else + AGWDisconnected(Con, Stream); + } + } + } + for (n = 1; n <= MaxSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive) + { + SOCKET sock = sockptr->socket; + + FD_SET(sock, &readfd); + FD_SET(sock, &exceptfd); + + Active++; + if (sock > maxsock) + maxsock = sock; + } + } + + if (Active) + { + retval = select((int)maxsock + 1, &readfd, &writefd, &exceptfd, &timeout); + + if (retval == -1) + { + perror("data select yy"); + Debugprintf("Select Error %d", WSAGetLastError()); + } + else + { + if (retval) + { + // see who has data + + for (n = 1; n <= MaxSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive) + { + SOCKET sock = sockptr->socket; + + if (FD_ISSET(sock, &exceptfd)) + AGWDataSocket_Disconnect(sockptr); + + if (FD_ISSET(sock, &readfd)) + AGWDataSocket_Read(sockptr, sock); + + } + } + } + } + } + + AGWDoMonitorData(); +} + + +static SOCKADDR_IN local_sin; /* Local socket - internet style */ + +static PSOCKADDR_IN psin; + + +BOOL AGWAPIInit() +{ + struct PORTCONTROL * PORT = PORTTABLE; + int i = 1; + int v = 0; + char * ptr; + BOOL opt=TRUE; + + if (AGWPort == 0) + return FALSE; + +// Create listening socket + + agwsock = socket( AF_INET, SOCK_STREAM, 0); + + if (agwsock == INVALID_SOCKET) + { + sprintf(szBuff, "socket() failed error %d", WSAGetLastError()); + WritetoConsole(szBuff); + return FALSE; + } + + setsockopt (agwsock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&opt,4); + + psin=&local_sin; + + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = INADDR_ANY; + psin->sin_port = htons(AGWPort); /* Convert to network ordering */ + + if (bind(agwsock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR) + { + sprintf(szBuff, "bind(sock) failed Port %d Error %d", AGWPort, WSAGetLastError()); + WritetoConsole(szBuff); + closesocket(agwsock); + + return FALSE; + } + + if (listen(agwsock, 5) < 0) + { + sprintf(szBuff, "listen(sock) failed Error %d", WSAGetLastError()); + WritetoConsole(szBuff); + + return FALSE; + } + + SetUpHostSessions(); + + // Set up port List + + ptr = &AGWPorts[0]; + + ptr += sprintf(ptr, "%d", NUMBEROFPORTS); + + *(ptr)=';'; + *(++ptr)=0; + + while (PORT) + { + if (PORT->Hide == 0) + { + VisiblePortToRealPort[v++] = i - 1; + memcpy(ptr,"Port",4); + ptr += sprintf(ptr, "%d", i); + memcpy(ptr, " with ", 6); + ptr+=6; + memcpy(ptr, PORT->PORTDESCRIPTION, 29); // ";" + ptr+=29; + + while (*(--ptr) == ' ') {} + + ptr++; + + *(ptr++)=';'; + } + i++; + PORT=PORT->PORTPOINTER; + } + + *(ptr)=0; + + AGWMONVECPTR->HOSTAPPLFLAGS = 0x80; // Requext Monitoring + + return TRUE; +} +int SetUpHostSessions() +{ + int Stream, i; + + if (AGWMask == 0) return 0; + + for (i = 1; i <= AGWSessions; i++) + { + strcpy(pgm, "AGW"); + + Stream = FindFreeStream(); + + if (Stream == 255) break; + + SetAppl(Stream, 2, AGWMask); + + strcpy(pgm, "bpq32.exe"); + + AGWConnections[CurrentConnections].CallKey[0] = 0; + AGWConnections[CurrentConnections].BPQStream = Stream; + AGWConnections[CurrentConnections].SocketIndex = 0; + AGWConnections[CurrentConnections].Connecting = FALSE; + AGWConnections[CurrentConnections].Listening = TRUE; + AGWConnections[CurrentConnections].ApplMask = AGWMask; + + CurrentConnections++; + } + + return 0; +} + +extern struct DATAMESSAGE * REPLYBUFFER; +extern BOOL AGWActive; + +VOID SHOWAGW(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + // DISPLAY AGW Session Status + + int i, con; + struct BPQConnectionInfo * Con; + byte key[21]; + + struct AGWSocketConnectionInfo * sockptr; + char IPAddr[20]; + + if (AGWActive == FALSE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "\rAGW Interface is not enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "\rSockets\r"); + for (i = 1; i <= CurrentSockets; i++) + { + sockptr=&Sockets[i]; + + if (sockptr->SocketActive) + { + unsigned char work[4]; + memcpy(work, &sockptr->sin.sin_addr.s_addr, 4); + + sprintf(IPAddr, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + Bufferptr = Cmdprintf(Session, Bufferptr, "%2d %-16s %5d %-10s %-10s\r", i, IPAddr, htons(sockptr->sin.sin_port), &sockptr->CallSign1, &sockptr->CallSign2); + } + else + { + Bufferptr = Cmdprintf(Session, Bufferptr, "%2d Idle\r", 1); + } + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "\rPort Calls Stream Socket Connecting Listening Mask\r"); + + for (con = 0; con < CurrentConnections; con++) + { + Con = &AGWConnections[con]; + + memcpy(key,Con->CallKey,21); + + if (key[0] == 0) key[0] = 32; + + key[10]=0; + key[20]=0; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%2c ", key[0]); + Bufferptr = Cmdprintf(Session, Bufferptr, "%-10s%-10s ",&key[1],&key[11]); + + Bufferptr = Cmdprintf(Session, Bufferptr, "%2d %2d %s %s %X\r", + Con->BPQStream, + (Con->SocketIndex == 0) ? 0 : AGWConnections[con].SocketIndex->Number, + (Con->Connecting == 0) ? "FALSE" : "TRUE ", + (Con->Listening == 0) ? "FALSE" : "TRUE ", + Con->ApplMask); + + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + + + +int DisplaySessions() +{ +/* char * ptr; + int i, con; + + byte key[21]; // As String, char As String + + strcpy (SessionList," Port Calls Stream Socket Connecting Listening Mask"); + + SendDlgItemMessage(MainWnd,IDC_TEXTBOX3,LB_RESETCONTENT,0,0); + SendDlgItemMessage(MainWnd,IDC_TEXTBOX3,LB_ADDSTRING,0,(LPARAM) SessionList); + + for (con = 0; con < CurrentConnections; con++) + { + memcpy(key,AGWConnections[con].CallKey,21); + + if (key[0] == 0) key[0] = 32; + + + key[10]=0; + key[20]=0; + + ptr=&SessionList[0]; + + i=sprintf(ptr,"%2d %2c ",con,key[0]); + + ptr+=i; + + i=sprintf(ptr,"%-10s%-10s ",&key[1],&key[11]); + + ptr+=i; + + i=sprintf(ptr,"%2d %2d %s %s %x", + AGWConnections[con].BPQStream, + (AGWConnections[con].SocketIndex == 0) ? 0 : AGWConnections[con].SocketIndex->Number, + (AGWConnections[con].Connecting == 0) ? "FALSE" : "TRUE ", + (AGWConnections[con].Listening == 0) ? "FALSE" : "TRUE ", + AGWConnections[con].ApplMask); + + SendDlgItemMessage(MainWnd,IDC_TEXTBOX3,LB_ADDSTRING,0,(LPARAM) SessionList); + } +*/ + return (0); + +} + +int AGWConnected(struct BPQConnectionInfo * Con, int Stream) +{ + byte ConnectingCall[10]; + byte * ApplCallPtr; + byte * keyptr; + byte ApplCall[10]=""; + int i; + + int ApplNum; + struct AGWSocketConnectionInfo * sockptr; + + GetCallsign(Stream, ConnectingCall); + + for (i=9;i>0;i--) + if (ConnectingCall[i]==32) + ConnectingCall[i]=0; + + ApplNum = GetApplNum(Stream); + + if (ApplNum == 0) + { + return 0; // Cant be an incomming connect + } + ApplCallPtr = GetApplCall(ApplNum); + + if (ApplCallPtr != 0) memcpy(ApplCall,ApplCallPtr,10); + + // Convert trailing spaces to nulls + + for (i=9;i>0;i--) + if (ApplCall[i]==32) + ApplCall[i]=0; + +// See if incomming connection + + if (Con->Listening) + { + // Allocate Session and send "c" Message + // + // Find an AGW session + + for (sockptr=&Sockets[1]; sockptr <= &Sockets[CurrentSockets]; sockptr++) + { + if (sockptr->SocketActive && + (memcmp(sockptr->CallSign1, ApplCall, 10) == 0) || (memcmp(sockptr->CallSign2, ApplCall, 10) == 0)) + { + // Create Key + + char callsign[10]; + int port; + int sesstype; + int paclen; + int maxframe; + int l4window; + + + keyptr=(byte *)&Con->CallKey; + + // Try using the BPQ Port Number if a L2 connect, first free port number if not + + GetConnectionInfo(Stream, callsign, + &port, &sesstype, &paclen, + &maxframe, &l4window); + + + if (port == 0) + port = 64; + + *(keyptr++)='0' + port; + memcpy(keyptr, ApplCall, 10); + keyptr+=10; + memcpy(keyptr,ConnectingCall, 10); + + // Make sure key is not already in use + + for (i = 0; i < CurrentConnections; i++) + { + if (Con->BPQStream == Stream) + continue; // Dont Check ourself! + + if (AGWConnections[i].SocketIndex == sockptr && + memcmp(&Con->CallKey, &AGWConnections[i].CallKey,21) == 0) + { + SendMsg(Stream, "AGWtoBPQ - Callsign is already connected\r", 43); + Sleep (500); + Disconnect(Stream); + Con->CallKey[0]=0; + + return 0; + } + } + + Con->Listening = FALSE; + Con->SocketIndex = sockptr; + + DisplaySessions(); + + SendConMsgtoAppl(TRUE, Con, ConnectingCall); + + return 0; + } + } + + SendMsg(Stream, "No AGWPE Host Sessions available\r", 33); + Sleep (500); + Disconnect(Stream); // disconnect + return (0); + } + + // Not listening ?? + + OutputDebugString("Inbound Connection on Outgoing Stream"); + + SendMsg(Stream, "AGWtoBPQ - Inbound Connection on Outgoing Stream\r", 49); + Sleep (500); + Disconnect(Stream); // disconnect + return (0); +} + +int AGWDisconnected(struct BPQConnectionInfo * Con, int Stream) +{ + struct AGWSocketConnectionInfo * sockptr; + char key[21]; + + memcpy(key,Con->CallKey,21); + + sockptr = Con->SocketIndex; + + if (sockptr != 0) + { + AGWTXHeader.Port = key[0] - 49; + + memcpy(&AGWTXHeader.callfrom,&key[11],10); + memcpy(&AGWTXHeader.callto,&key[1],10); + + // Send a "d" Message + + // DisMsg = "*** DISCONNECTED From Station " + + SendDisMsgtoAppl("*** DISCONNECTED From Station ", sockptr); + + } + + if (Con->ApplMask != 0) + { + Con->Listening = TRUE; + Con->SocketIndex = 0; + memset(&Con->CallKey ,0 ,21); + + DisplaySessions(); + } + else + { + DeleteConnection(Con); + } + + return 0; + +} +int AGWDoReceivedData(int Stream) +{ + byte Buffer[400]; + int len,count; + +//Dim n As Integer, i As Integer, j As Integer, portcount As Integer +//Dim start As Integer + + do + { + GetMsg(Stream, Buffer,&len, &count); + + if (len > 0) + SendDataToAppl(Stream, Buffer,len); + + } + while (count > 0); + + return 0; +} + +int AGWDoMonitorData() +{ + byte Buffer[500]; + int RawLen, Length; + byte Port; + struct AGWSocketConnectionInfo * sockptr; + byte AGWBuffer[1000]; + int n; + int Frametype; + BOOL RXFlag; + time_t Stamp; + + // Look for Monitor Data + + while (AGWMONVECPTR->HOSTTRACEQ) + { + MESSAGE * monbuff; + + GetSemaphore(&Semaphore, 99); + + monbuff = Q_REM((void *)&AGWMONVECPTR->HOSTTRACEQ); + + RawLen = monbuff->LENGTH; + + if (RawLen < 7 || RawLen > 350) + { + ReleaseBuffer(monbuff); + FreeSemaphore(&Semaphore); + return 0; + } + + Stamp = monbuff->Timestamp; + + memcpy(Buffer, monbuff, RawLen); + + ReleaseBuffer(monbuff); + + FreeSemaphore(&Semaphore); + +//' 4 byte chain +//' 1 byte port - top bit = transmit +//' 2 byte length (LO-HI) + + Port = Buffer[4]; + + if (Port > 127) + { + RXFlag = FALSE; + Port = Port - 128; + } + else + { + RXFlag = TRUE; + } + + // Can now have different mon flags per connection, so need to run decode for each socket + + for (n = 1; n<= CurrentSockets; n++) + { + sockptr = &Sockets[n]; + + if (sockptr->SocketActive && sockptr->MonFlag && (RXFlag || LoopMonFlag)) + { + Length = InternalAGWDecodeFrame(Buffer, AGWBuffer, Stamp, &Frametype, sockptr->useLocalTime, sockptr->doNodes); + + if (Length > 0) + { + AGWTXHeader.Port = Port - 1; // AGW Ports start from 0 + + /* + * Patch by D. van der Locht 09-10-2020 + * Added support for sending 'T' frames to AGW clients if LoopMonFlag is set. + */ + + if (RXFlag) + { + if (Frametype == 3) + { + AGWTXHeader.DataKind = 'U'; + } + else + { + if (Frametype && 1 == 0) + { + AGWTXHeader.DataKind = 'I'; + } + else + { + AGWTXHeader.DataKind = 'S'; + } + } + } + else + { + AGWTXHeader.DataKind = 'T'; + } + + AGWTXHeader.DataLength = Length; + + memset(AGWTXHeader.callfrom, 0,10); + ConvFromAX25(monbuff->ORIGIN, AGWTXHeader.callfrom); + + SendRawPacket(sockptr, AGWBuffer, Length); + } + } + } + + RawLen = RawLen - 6; + + if (RXFlag || Loopflag) // Send transmitted frames if requested + { + + // + // Send raw data to any sockets that have requested Raw frames + // + + Buffer[6]=0; + + AGWTXHeader.Port = Port - 1; // AGW Ports start from 0 + AGWTXHeader.DataKind = 'K'; + + AGWTXHeader.DataLength = RawLen; + + for (n = 1; n<= CurrentSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive && sockptr->RawFlag) + SendRawPacket(sockptr, &Buffer[6], RawLen); + + } + } + } + + return 0; + +} + +int DeleteConnection(struct BPQConnectionInfo * Con) +{ + int i; + int con; + + // + // remove specified session + // + + SetAppl(Con->BPQStream, 0, 0); + + Disconnect(Con->BPQStream); + + DeallocateStream(Con->BPQStream); + +// move all down one + + con = (int)(Con - &AGWConnections[0]); + + for (i = con; i <= CurrentConnections - 1; i++) + { + memcpy(&AGWConnections[i],&AGWConnections[i + 1],sizeof ConInfoRec); + } + + CurrentConnections--; + + return 0; +} + +int SendConMsgtoAppl(BOOL Incomming, struct BPQConnectionInfo * Con, char * CallSign) +{ + char key[21]; + char ConMsg[80]="*** CONNECTED "; + struct AGWSocketConnectionInfo * sockptr; + + + memcpy(key,&Con->CallKey,21); + + sockptr = Con->SocketIndex; + + AGWTXHeader.Port = key[0] - 49; + + memcpy(AGWTXHeader.callfrom, &key[11],10); + + memcpy(AGWTXHeader.callto, &key[1],10); + +/* ' + ' Send a "C" Message + ' +'01 00 00 00 43 00 00 00 47 4D 38 42 50 51 2D 34  C GM8BPQ-4 +'00 EA 47 4D 38 42 50 51 2D 34 00 FF 25 00 00 00 êGM8BPQ-4 ÿ% +'00 00 00 00 2A 2A 2A 20 43 4F 4E 4E 45 43 54 45 *** CONNECTE +'44 20 57 69 74 68 20 53 74 61 74 69 6F 6E 20 47 D With Station G +'4D 38 42 50 51 2D 34 0D 00 M8BPQ-4 +*/ + + AGWTXHeader.DataKind = 'C'; + + AGWTXHeader.PID = 0; + + if (Incomming) + strcat(ConMsg,"To"); + else + strcat(ConMsg,"With"); + + strcat(ConMsg," Station "); + strcat(ConMsg,CallSign); + + AGWTXHeader.DataLength = (int)strlen(ConMsg)+1; + + SendtoSocket(sockptr->socket, ConMsg); + + return 0; + +} + + + + +int SendDisMsgtoAppl(char * Msg, struct AGWSocketConnectionInfo * sockptr) +{ + byte DisMsg[100]; + + strcpy(DisMsg,Msg); + strcat(DisMsg,(const char *)&AGWTXHeader.callfrom); + + AGWTXHeader.Port = sockptr->AGWRXHeader.Port; + AGWTXHeader.DataKind = 'd'; + + strcat(DisMsg,"\r"); + + AGWTXHeader.DataLength = (int)strlen(DisMsg)+1; + + SendtoSocket(sockptr->socket, DisMsg); + + return 0; + +} + + + +int AGWSocket_Accept(SOCKET SocketId) +{ + int n,addrlen; + struct AGWSocketConnectionInfo * sockptr; + SOCKET sock; + +// Find a free Socket + + for (n = 1; n <= MaxSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive == FALSE) + { + addrlen=sizeof(struct sockaddr); + + memset(sockptr, 0, sizeof(struct AGWSocketConnectionInfo)); + + sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen); + + if (sock == INVALID_SOCKET) + { + sprintf(szBuff, "AGW accept() failed Error %d\r", WSAGetLastError()); + WritetoConsole(szBuff); + return FALSE; + } + + sockptr->socket = sock; + sockptr->SocketActive = TRUE; + sockptr->GotHeader = FALSE; + sockptr->MsgDataLength = 0; + sockptr->Number = n; + + if (CurrentSockets < n) CurrentSockets=n; //Record max used to save searching all entries + + ShowApps(); + + return 0; + } + } + + // Should accept, then immediately close + + return 0; +} + +int SendtoSocket(SOCKET sock, char * Msg) +{ + int len; + int n; + char * ptr; + + len = AGWTXHeader.DataLength; + + // Make sure calls are null terminated + + n = 10; + ptr = &AGWTXHeader.callfrom[9]; + while (n-- && *(ptr) == ' ') + { + *(ptr) = 0; + ptr--; + } + + n = 10; + ptr = &AGWTXHeader.callto[9]; + while (n-- && *(ptr) == ' ') + { + *(ptr) = 0; + n--; + } + + send(sock,(char *)&AGWTXHeader, 36,0); + if (len > 0) send(sock, Msg, len,0); + + return 0; +} + +int AGWDataSocket_Read(struct AGWSocketConnectionInfo * sockptr, SOCKET sock) +{ + int i; + int DataLength; + struct AGWHeader * AGW = &sockptr->AGWRXHeader; + + ioctlsocket(sock,FIONREAD,&DataLength); + + if (DataLength == SOCKET_ERROR || DataLength == 0) + { + // Failed or closed - clear connection + + AGWDataSocket_Disconnect(sockptr); + return 0; + } + + if (DataLength < 36) // A header + { + // If we don't get a header within a few ms assume a rogue connection and close it + + int n = 50; + + while (n--) + { + Sleep(10); + ioctlsocket(sock,FIONREAD,&DataLength); + + if (DataLength >= 36) + break; + } + + if (n < 1) + { + Debugprintf("Corrupt AGW Packet Received"); + AGWDataSocket_Disconnect(sockptr); + return 0; + } + } + + // Have a header + + i=recv(sock,(char *)&sockptr->AGWRXHeader, 36, 0); + + if (i == SOCKET_ERROR) + { + i=WSAGetLastError(); + AGWDataSocket_Disconnect(sockptr); + } + + sockptr->MsgDataLength = sockptr->AGWRXHeader.DataLength; + + // Validate packet to protect against accidental (or malicious!) connects from a non-agw application + + if (AGW->Port > 64 || AGW->filler2 != 0 || AGW->filler3 != 0 || AGW->DataLength > 400) + { + Debugprintf("Corrupt AGW Packet Received"); + AGWDataSocket_Disconnect(sockptr); + return 0; + } + + if (sockptr->MsgDataLength == 0) + ProcessAGWCommand (sockptr); + else + sockptr->GotHeader = TRUE; // Wait for data + + ioctlsocket(sock,FIONREAD,&DataLength); // See if more data + + if (sockptr->GotHeader) + { + // Received a header, without sufficient data bytes + + if (DataLength < sockptr->MsgDataLength) + { + // Fiddle - seem to be problems somtimes with un-Neagled hosts so wait a few ms + // if we don't get a full packet assume a rogue connection and close it + + int n = 50; + + while (n--) + { + Sleep(10); + ioctlsocket(sock,FIONREAD,&DataLength); + + if (DataLength >= sockptr->MsgDataLength) + break; + } + + if (n < 1) + { + Debugprintf("Corrupt AGW Packet Received"); + AGWDataSocket_Disconnect(sockptr); + return 0; + } + } + + if (DataLength >= sockptr->MsgDataLength) + { + // Read Data and Process Command + + sockptr->MsgData = malloc(sockptr->MsgDataLength); + + i = recv(sock, sockptr->MsgData, sockptr->MsgDataLength, 0); + + ProcessAGWCommand (sockptr); + free(sockptr->MsgData); + sockptr->GotHeader = FALSE; + } + } + return 0; +} + + +int ProcessAGWCommand(struct AGWSocketConnectionInfo * sockptr) +{ + int AGWVersion[2]={2003,999}; + char AGWRegReply[1]; + struct BPQConnectionInfo * Connection; + int Stream; + char AXCall[10]; + char RegCall[10]; + unsigned char TXMessage[500]; + int Digis,MsgStart,j; + byte * TXMessageptr; + char key[21]; + char ToCall[10]; + char ConnectMsg[128]; + int con,conport; + int AGWYReply = 0; + int state, change; + int n; + + // if we have hidden some ports then the port in the AGW packet will be an index into the visible ports, + // not the real port number + + switch (sockptr->AGWRXHeader.DataKind) + { + case 'C': + case 'v': + + // Connect or Connect with Digis + + // Create Session Key from port and callsign pair + + AGWGetSessionKey(key, sockptr); + + memcpy(ToCall, &key[11],10); + + strcpy(pgm, "AGW"); + + Stream = FindFreeStream(); + + strcpy(pgm, "bpq32.exe"); + + if (Stream == 255) return 0; + + Connection=&AGWConnections[CurrentConnections]; + + memcpy(&Connection->CallKey,key,21); + Connection->BPQStream = Stream; + Connection->SocketIndex = sockptr; + Connection->Connecting = TRUE; + + Connect(Stream); // Connect + SessionState(Stream, &state, &change); + + ConvToAX25(sockptr->CallSign1, AXCall); + ChangeSessionCallsign(Stream, AXCall); // Prevent triggering incoming connect code + + DisplaySessions(); + + if (memcmp(ToCall,"SWITCH",6) == 0) + { + // Just connect to command level on switch + + SendConMsgtoAppl(FALSE, Connection, ToCall); + Connection->Connecting = FALSE; + } + else + { + + // Need to convert port index (used by AGW) to port number + + conport=GetPortNumber(VisiblePortToRealPort[key[0]-48]); + + n = sprintf(ConnectMsg,"C %d %s",conport,ToCall); + + // if 'v' command add digis + + if (sockptr->AGWRXHeader.DataLength) + { + // Have digis + + char * Digis = sockptr->MsgData; + int nDigis = Digis[0]; + + Digis ++; + + while(nDigis--) + { + n += sprintf(&ConnectMsg[n], " %s", Digis); + Digis += 10; + } + } + + strcat(ConnectMsg, "\r"); + + // Send C command to Node + + SendMsg(Stream, ConnectMsg, (int)strlen(ConnectMsg)); + } + + CurrentConnections++; + + DisplaySessions(); + + return 0; + + case 'D': + + // Send Data + // + // Create Session Key from port and callsign pair + + AGWGetSessionKey(key, sockptr); + + for (con = 0; con < CurrentConnections; con++) + { + if (memcmp(AGWConnections[con].CallKey,key,21) == 0) + { + SendMsg(AGWConnections[con].BPQStream, sockptr->MsgData, sockptr->MsgDataLength); + return 0; + } + } + + return 0; + + case 'd': + + // Disconnect + + memcpy(AGWTXHeader.callto,sockptr->AGWRXHeader.callfrom,10); + memcpy(AGWTXHeader.callfrom,sockptr->AGWRXHeader.callto,10); + + SendDisMsgtoAppl("*** DISCONNECTED RETRYOUT With ", sockptr); + + AGWGetSessionKey(key, sockptr); + + for (con = 0; con < CurrentConnections; con++) + { + if (memcmp(AGWConnections[con].CallKey,key,21) == 0) + { + Disconnect(AGWConnections[con].BPQStream); + return 0; + } + } + + // There is confusion about the correct ordring of calls in the "d" packet. AGW appears to accept either, + // so I will too. + + memset(&key[1],0,20); + strcpy(&key[1],sockptr->AGWRXHeader.callto); + strcpy(&key[11],sockptr->AGWRXHeader.callfrom); + + for (con = 0; con < CurrentConnections; con++) + { + if (memcmp(AGWConnections[con].CallKey,key,21) == 0) + { + Disconnect(AGWConnections[con].BPQStream); + return 0; + } + } + + return 0; + + + case 'R': + + // Version + + memset(&AGWTXHeader,0,36); + + AGWTXHeader.DataKind = 'R'; + + AGWTXHeader.DataLength = 8; // Length + + SendtoSocket(sockptr->socket, (char *)&AGWVersion[0]); + + return 0; + + + case 'G': + + // Port info. String is in AGWPorts + + + memset(&AGWTXHeader,0,36); + + AGWTXHeader.DataKind = 'G'; + + AGWTXHeader.DataLength =(int)strlen(AGWPorts)+1; // Length + + SendtoSocket(sockptr->socket, AGWPorts); + + return 0; + + + case 'k': + + // Toggle Raw receive + + sockptr->RawFlag = !sockptr->RawFlag; + + return 0; + + case 'K': + + // Send Raw Frame + + SendRaw(sockptr->AGWRXHeader.Port+1,&sockptr->MsgData[1], sockptr->MsgDataLength - 1); + + return 0; + + case 'm': + + // Toggle Monitor receive + + if (sockptr->AGWRXHeader.DataLength == 12) // QtTermTCP monitor info + { +// Msg[AGWHDDRRLEN] = AGWUsers->MonSess->mlocaltime; +// Msg[AGWHDDRRLEN + 1] = AGWUsers->MonSess->MonitorNODES; + //Msg[AGWHDDRRLEN + 2] = AGWUsers->MonSess->MonitorColour; +// Msg[AGWHDDRRLEN + 3] = AGWUsers->MonSess->mtxparam; +// memcpy(&Msg[AGWHDDRRLEN + 4], (void *)&AGWUsers->MonSess->portmask, 8); + sockptr->useLocalTime = sockptr->MsgData[0]; + sockptr->doNodes = sockptr->MsgData[1]; + sockptr->MonFlag = 1; + } + else + sockptr->MonFlag = !sockptr->MonFlag; + + return 0; + + + case 'M': + case 'V': // Send UNProto Frame "V" includes Via string + + + ConvToAX25(sockptr->AGWRXHeader.callto,TXMessage); + ConvToAX25(sockptr->AGWRXHeader.callfrom,&TXMessage[7]); + + Digis=0; + MsgStart = 0; + + if (sockptr->AGWRXHeader.DataKind == 'V') // Unproto with VIA string + { + Digis = sockptr->MsgData[0]; // Number of digis + + for (j = 1; j<= Digis; j++) + { + ConvToAX25(&sockptr->MsgData[(j - 1) * 10 + 1],&TXMessage[7+(j*7)]); // No "last" bit + } + + // set end of call + + MsgStart = Digis * 10 + 1; // UI Data follows digis in message + } + + TXMessageptr=&TXMessage[13+(Digis*7)]; + + *(TXMessageptr++) |= 1; // set last bit + + *(TXMessageptr++) = 3; // UI + + if (sockptr->AGWRXHeader.PID == 0) + + *(TXMessageptr++) = 240; // PID + else + *(TXMessageptr++) = sockptr->AGWRXHeader.PID; + + memcpy(TXMessageptr,&sockptr->MsgData[MsgStart], sockptr->MsgDataLength - MsgStart); + + TXMessageptr += (sockptr->MsgDataLength - MsgStart); + + SendRaw(sockptr->AGWRXHeader.Port + 1, TXMessage, (int)(TXMessageptr-&TXMessage[0])); + + return 0; + + case 'X': + + // Register Callsign + + memset(&AGWTXHeader,0,36); + + memset(RegCall,0,10); + memcpy(RegCall, sockptr->AGWRXHeader.callfrom, strlen(sockptr->AGWRXHeader.callfrom)); + + if (sockptr->CallSign1[0] == 0) + memcpy(sockptr->CallSign1, RegCall, 10); + else + memcpy(sockptr->CallSign2, RegCall, 10); + + AGWTXHeader.DataKind = 'X'; + + AGWTXHeader.DataLength = 1; // Length + + AGWRegReply[0] = 1; + + SendtoSocket(sockptr->socket, AGWRegReply); + + ShowApps(); + + + return 0; + + + case 'Y': + + // Session Status + + // Create Session Key from port and callsign pair + + AGWGetSessionKey(key, sockptr); + + for (con = 0; con < CurrentConnections; con++) + { + if (memcmp(AGWConnections[con].CallKey,key,21) == 0) + { + memcpy(&AGWTXHeader,&sockptr->AGWRXHeader,36); + + AGWYReply = CountFramesQueuedOnStream(AGWConnections[con].BPQStream); + AGWTXHeader.DataLength = 4; // Length + SendtoSocket(sockptr->socket, (char *)&AGWYReply); + + return 0; + } + } + + return 0; + + + default: + + //If Debugging Then Print #10, "Unknown Message "; Chr$(Sockets(Index).AGWRXHeader(4)) + // Debug.Print "Unknown Message "; Chr$(Sockets(Index).AGWRXHeader(4)) + + return 0; + + } + + return 0; + +} + +int AGWGetSessionKey(char * key, struct AGWSocketConnectionInfo * sockptr) +{ + +// Create Session Key from port and callsign pair + + + + key[0] = sockptr->AGWRXHeader.Port + '1'; + + memset(&key[1],0,20); + strcpy(&key[1],sockptr->AGWRXHeader.callfrom); + strcpy(&key[11],sockptr->AGWRXHeader.callto); + + return 0; + +} +int SendDataToAppl(int Stream, byte * Buffer, int Length) +{ + int con; + char * i; + char ConMsg[80]; + char DisMsg[80]; + char key[21]; + struct AGWSocketConnectionInfo * sockptr; + +//Dim i As Long, Length As Long, con As Long, key As String, hilen As Long, lolen As Long +//Dim Index As Integer, ConMsg As String, DisMsg As String +//Dim BytesSent As Long + + + //' Find Connection number and call pair + + for (con = 0; con < CurrentConnections; con++) + { + if (AGWConnections[con].BPQStream == Stream) + { + memcpy(key,&AGWConnections[con].CallKey,21); + + if (key[0] == 32) + { + //Debug.Print "Data on Unconnected Session" + + Disconnect(Stream); + return (0); + } + + sockptr = AGWConnections[con].SocketIndex; + + if (sockptr == 0) + { + // No connection, but have a key - wot's going on!! + // Probably best to clear out connection + + Disconnect(Stream); + + return (0); + } + + AGWTXHeader.Port = key[0] - 49; + + memcpy(AGWTXHeader.callfrom,&key[11],10); + memcpy(AGWTXHeader.callto,&key[1],10); + + if (AGWConnections[con].Connecting) + { + + // See if *** Connected message + + i = strstr(Buffer, "Connected to"); + + if (i != 0) + { + AGWConnections[con].Connecting = FALSE; + + DisplaySessions(); + + AGWTXHeader.DataKind = 'C'; + AGWTXHeader.PID = 0; + + strcpy(ConMsg,"*** CONNECTED With Station "); + strcat(ConMsg, AGWTXHeader.callfrom); + strcat(ConMsg,"\r"); + + AGWTXHeader.DataLength = (int)strlen(ConMsg)+1; + + SendtoSocket(sockptr->socket, ConMsg); + + return (0); + + } + + i = strstr(Buffer, "Failure with"); + + if (i != 0) + { + AGWConnections[con].Connecting = FALSE; + + strcpy(DisMsg,"*** DISCONNECTED RETRYOUT With "); + + SendDisMsgtoAppl(DisMsg, sockptr); + + DeleteConnection(&AGWConnections[con]); + + return 0; + + } + + i = strstr(Buffer, "Busy from"); + + if (i != 0) + { + AGWConnections[con].Connecting = FALSE; + + strcpy(DisMsg,"*** DISCONNECTED RETRYOUT With "); + + SendDisMsgtoAppl(DisMsg, sockptr); + + DeleteConnection(&AGWConnections[con]); + + return 0; + + } + } + + AGWTXHeader.DataKind = 'D'; + AGWTXHeader.PID = 0xF0; + AGWTXHeader.DataLength = Length; + + SendtoSocket(sockptr->socket, Buffer); + } + } + + return 0; + } + + +int AGWDataSocket_Disconnect(struct AGWSocketConnectionInfo * sockptr) +{ + int con; + + closesocket(sockptr->socket); + + for (con = 0; con < CurrentConnections; con++) + { + if (AGWConnections[con].SocketIndex == sockptr) + Disconnect(AGWConnections[con].BPQStream); + } + + sockptr->SocketActive = FALSE; + sockptr->RawFlag = FALSE; + sockptr->MonFlag = FALSE; + + ShowApps(); + + + return 0; +} + +int SendRawPacket(struct AGWSocketConnectionInfo * sockptr, char *txmsg, int Length) +{ + SendtoSocket(sockptr->socket, txmsg); + + return 0; +} + +int ShowApps() +{ +/* + struct AGWSocketConnectionInfo * sockptr; + int i; + char Msg[80]; + char IPAddr[20]; + + if (ConnWnd == 0) return 0; // Not on display + + SendDlgItemMessage(ConnWnd,IDC_CONNECTIONS_LIST,LB_RESETCONTENT,0,0); + + for (i = 1; i <= CurrentSockets; i++) + { + sockptr=&Sockets[i]; + + if (sockptr->SocketActive) + { + sprintf(IPAddr,"%d.%d.%d.%d", + sockptr->sin.sin_addr.S_un.S_un_b.s_b1, + sockptr->sin.sin_addr.S_un.S_un_b.s_b2, + sockptr->sin.sin_addr.S_un.S_un_b.s_b3, + sockptr->sin.sin_addr.S_un.S_un_b.s_b4); + + sprintf(Msg,"%2d %-16s %5d %-10s",i,IPAddr,htons(sockptr->sin.sin_port),&sockptr->CallSign); + } + else + { + sprintf(Msg,"%2d Idle",i); + } + + SendDlgItemMessage(ConnWnd,IDC_CONNECTIONS_LIST,LB_ADDSTRING,0,(LPARAM) Msg); + } +*/ + return 0; +} + + +int LocalSessionState(int stream, int * state, int * change, BOOL ACK); + +int AGWAPITerminate() +{ + int con, State, Change, n; + struct BPQConnectionInfo * Connection; + struct AGWSocketConnectionInfo * sockptr; +// +// Release all streams +// + for (con = 0; con < CurrentConnections; con++) + { + Connection=&AGWConnections[con]; + + SetAppl(Connection->BPQStream, 0, 0); + + Disconnect(Connection->BPQStream); + + DeallocateStream(Connection->BPQStream); + + LocalSessionState(Connection->BPQStream, &State, &Change, TRUE); + + memset(Connection, 0, sizeof(struct AGWSocketConnectionInfo)); + + } + + CurrentConnections = 0; + + // Close Listening socket and any connections + + shutdown(agwsock, 2); + closesocket(agwsock); + + for (n = 1; n <= MaxSockets; n++) + { + sockptr=&Sockets[n]; + + if (sockptr->SocketActive) + { + SOCKET sock = sockptr->socket; + + shutdown(sock, 2); + closesocket(sock); + } + memset(sockptr, 0, sizeof(struct BPQConnectionInfo)); + } + + return 0; +} + diff --git a/AGWMoncode.c b/AGWMoncode.c index 7427804..2c26191 100644 --- a/AGWMoncode.c +++ b/AGWMoncode.c @@ -49,9 +49,14 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses #define DM 0x0F #define UA 0x63 #define FRMR 0x87 +#define XID 0xAF +#define TEST 0xE3 #define RR 1 #define RNR 5 #define REJ 9 +#define SREJ 0x0D +#define SABME 0x6F + #define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE @@ -261,6 +266,18 @@ int InternalAGWDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, int * Fra strcpy(SUP, "FRMR"); FRMRFLAG = 1; break; + + case XID: + + strcpy(SUP, "XID"); + XIDFLAG = 1; + break; + + case TEST: + + strcpy(SUP, "TEST"); + TESTFLAG = 1; + break; } Output += sprintf((char *)Output, "<%s%s%s>", SUP, CRCHAR, PFCHAR); @@ -270,7 +287,7 @@ int InternalAGWDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, int * Fra // Super int NR = (CTL >> 5) & 7; - char SUP[4] = "??"; + char SUP[5] = "??"; switch (CTL & 0x0F) { @@ -288,6 +305,13 @@ int InternalAGWDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, int * Fra strcpy(SUP, "REJ"); break; + + + case SREJ: + + strcpy(SUP, "SREJ"); + break; + } Output += sprintf((char *)Output, "<%s%s%s R%d>", SUP, CRCHAR, PFCHAR, NR); @@ -300,6 +324,72 @@ int InternalAGWDecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp, int * Fra if (FRMRFLAG) Output += sprintf((char *)Output, "%02X %02X %02X", ADJBUFFER->PID, ADJBUFFER->L2DATA[0], ADJBUFFER->L2DATA[1]); + if (XIDFLAG) + { + // Decode and display XID + + UCHAR * ptr = &ADJBUFFER->PID; + + if (*ptr++ == 0x82 && *ptr++ == 0x80) + { + int Type; + int Len; + unsigned int value; + int xidlen = *(ptr++) << 8; + xidlen += *ptr++; + + // XID is set of Type, Len, Value n-tuples + +// G8BPQ-2>G8BPQ:(XID cmd, p=1) Half-Duplex SREJ modulo-128 I-Field-Length-Rx=256 Window-Size-Rx=32 Ack-Timer=3000 Retries=10 + + + while (xidlen > 0) + { + Type = *ptr++; + Len = *ptr++; + + value = 0; + xidlen -= (Len + 2); + + while (Len--) + { + value <<=8; + value += *ptr++; + } + switch(Type) + { + case 2: //Bin fields + case 3: + + Output += sprintf((char *)Output, " %d=%x", Type, value); + break; + + case 6: //RX Size + + Output += sprintf((char *)Output, " RX Paclen=%d", value / 8); + break; + + case 8: //RX Window + + Output += sprintf((char *)Output, " RX Window=%d", value); + break; + + case 16: + + Output += sprintf((char *)Output, " Can Compress"); + break; + + case 17: + + Output += sprintf((char *)Output, " Compress ok"); + break; + } + } + } + } + + + if (Info) { // We have an info frame diff --git a/Bpq32.c b/Bpq32.c index 4d52845..cbf6017 100644 --- a/Bpq32.c +++ b/Bpq32.c @@ -1270,6 +1270,8 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Support IPv6 for Telnet outward connects (72) // Fix decaying NETROM routes (72) // Add OnlyVer2point0 config command (72) +// Add option to allow AX/UDP on a network using NAT (72) +// Include AGWAPI foxes from Martin KD6YAM to enable use with Paracon terminal (72) #define CKernel diff --git a/L2Code.c b/L2Code.c index d0c052c..76924bf 100644 --- a/L2Code.c +++ b/L2Code.c @@ -4005,6 +4005,7 @@ VOID CONNECTREFUSED(struct _LINKTABLE * LINK) ConnectFailedOrRefused(LINK, "Busy from"); } + VOID L3LINKSETUPFAILED(struct _LINKTABLE * LINK); diff --git a/Versions.h b/Versions.h index 6a9e2c5..25cfc57 100644 --- a/Versions.h +++ b/Versions.h @@ -10,8 +10,8 @@ #endif -#define KVers 6,0,24,72 -#define KVerstring "6.0.24.72\0" +#define KVers 6,0,24,73 +#define KVerstring "6.0.24.73\0" #ifdef CKernel diff --git a/bpqaxip.c b/bpqaxip.c index 8d243b1..d5afb62 100644 --- a/bpqaxip.c +++ b/bpqaxip.c @@ -2623,6 +2623,7 @@ BOOL CheckSourceisResolvable(struct AXIPPORTINFO * PORT, char * call, int FromPo if (arp->replytoSourcePort) { arp->port = FromPort; + arp->destaddr.sin_port = htons(arp->port); if (arp->SourcePort == 0) arp->SourcePort = ToPort; }