From 3143e32c1a361685886a8a7a2f253a0463c48eb0 Mon Sep 17 00:00:00 2001 From: John Wiseman Date: Fri, 18 Jul 2025 14:48:39 +0100 Subject: [PATCH] 6.0.24.76 --- ...6df7e4f1074cf4c08420c9395d2189e16.svn-base | 216 + ...3b4b1326b7fa0ba90678c16c3a62abdb1.svn-base | 126 + ...db39b291fc8d93e4da985d571dc853649.svn-base | 1479 ++++ ...c1215961c8bcfde3ae0948a1e2cd8e9e6.svn-base | 5459 ++++++++++++++ ...23d6256425d8439a9df5e9b3d58e6a40c.svn-base | 2172 ++++++ ...f3274e2d47fa0fbc2703cbcd7cfed1928.svn-base | 6313 +++++++++++++++++ ...6c08277fa15b704100105772a7f14cc4c.svn-base | 499 ++ ...2ba7c8b4d032a734faeabcdeeac283a34.svn-base | 1801 +++++ .svn/wc.db | Bin 286720 -> 286720 bytes IPCode.c | 5 +- LinBPQ.c | 5 +- TNCEmulators.c | 10 +- Versions.h | 6 +- asmstrucs.h | 4 + linether.c | 14 +- lzhuf32.c | 2 +- upnp.c | 4 +- 17 files changed, 18101 insertions(+), 14 deletions(-) create mode 100644 .svn/pristine/00/00f45c26df7e4f1074cf4c08420c9395d2189e16.svn-base create mode 100644 .svn/pristine/3e/3e298c03b4b1326b7fa0ba90678c16c3a62abdb1.svn-base create mode 100644 .svn/pristine/6c/6cf5d54db39b291fc8d93e4da985d571dc853649.svn-base create mode 100644 .svn/pristine/7f/7fe27d4c1215961c8bcfde3ae0948a1e2cd8e9e6.svn-base create mode 100644 .svn/pristine/87/87366a323d6256425d8439a9df5e9b3d58e6a40c.svn-base create mode 100644 .svn/pristine/98/98250e1f3274e2d47fa0fbc2703cbcd7cfed1928.svn-base create mode 100644 .svn/pristine/9b/9be015c6c08277fa15b704100105772a7f14cc4c.svn-base create mode 100644 .svn/pristine/ea/ea6b9a12ba7c8b4d032a734faeabcdeeac283a34.svn-base diff --git a/.svn/pristine/00/00f45c26df7e4f1074cf4c08420c9395d2189e16.svn-base b/.svn/pristine/00/00f45c26df7e4f1074cf4c08420c9395d2189e16.svn-base new file mode 100644 index 0000000..714e04d --- /dev/null +++ b/.svn/pristine/00/00f45c26df7e4f1074cf4c08420c9395d2189e16.svn-base @@ -0,0 +1,216 @@ +// 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 +#ifdef _WIN32 +#include "upnpcommands.h" +#include "miniupnpc.h" +#include "upnperrors.h" +#include +#endif +#ifdef LINBPQ +#ifndef MACBPQ +#ifndef WIN32 +#include +#include +#include +#include +#endif +#endif +#endif +#ifdef MACBPQ +#include +#include +#include +#include +#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 +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; +} + + + + diff --git a/.svn/pristine/3e/3e298c03b4b1326b7fa0ba90678c16c3a62abdb1.svn-base b/.svn/pristine/3e/3e298c03b4b1326b7fa0ba90678c16c3a62abdb1.svn-base new file mode 100644 index 0000000..b27704b --- /dev/null +++ b/.svn/pristine/3e/3e298c03b4b1326b7fa0ba90678c16c3a62abdb1.svn-base @@ -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,76 +#define KVerstring "6.0.24.76\0" + + +#ifdef CKernel + +#define Vers KVers +#define Verstring KVerstring +#define Datestring "July 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 diff --git a/.svn/pristine/6c/6cf5d54db39b291fc8d93e4da985d571dc853649.svn-base b/.svn/pristine/6c/6cf5d54db39b291fc8d93e4da985d571dc853649.svn-base new file mode 100644 index 0000000..79e9a26 --- /dev/null +++ b/.svn/pristine/6c/6cf5d54db39b291fc8d93e4da985d571dc853649.svn-base @@ -0,0 +1,1479 @@ +// C equivalents of ASM STRUCS + +// Aug 2010 Extend Applmask to 32 bit + +#ifndef _ASMSTRUCS_ +#define _ASMSTRUCS_ + +#ifdef WIN32 + +#include "winsock2.h" +#include "ws2tcpip.h" +#include "winstdint.h" + +#endif + +#define BPQICON 2 + +#define BUFFLEN 400 +#define BUFFALLOC 464 // Actual size of buffer. Extra 64 bytes for a source code tag to help find buffer leaks + + +//#define ApplStringLen 48 // Length of each config entry +#define NumberofAppls 32 // Max APPLICATIONS= values +#define ALIASLEN 48 +#define MHENTRIES 30 // Entries in MH List + +#define MaxLockedRoutes 100 + +typedef int (FAR *FARPROCY)(); + +#define NRPID 0xcf // NETROM PID + +#define L4BUSY 0x80 // BNA - DONT SEND ANY MORE +#define L4NAK 0x40 // NEGATIVE RESPONSE FLAG +#define L4MORE 0x20 // MORE DATA FOLLOWS - FRAGMENTATION FLAG +#define L4COMP 0x10 // BPQ Specific use of spare it - data is compressed + +#define L4CREQ 1 // CONNECT REQUEST +#define L4CACK 2 // CONNECT ACK +#define L4DREQ 3 // DISCONNECT REQUEST +#define L4DACK 4 // DISCONNECT ACK +#define L4INFO 5 // INFORMATION +#define L4IACK 6 // INFORMATION ACK +#define L4RESET 7 // Paula's extension + + +extern char MYCALL[]; // 7 chars, ax.25 format +extern char MYALIASTEXT[]; // 6 chars, not null terminated +extern UCHAR L3RTT[7]; // 7 chars, ax.25 format +extern UCHAR L3KEEP[7]; // 7 chars, ax.25 format +//extern int SENDNETFRAME(); +extern struct _DATABASE * DataBase; +//extern APPLCALLS APPLCALLTABLE[8]; + +extern int MAXDESTS; + +//extern VOID * GETBUFF(); +//extern VOID SETUPNODEHEADER(); +extern VOID POSTDATAAVAIL(); + + +extern int DATABASE; +extern int ENDOFDATA; +extern int L3LIVES; +extern int NUMBEROFNODES; + +struct CMDX +{ + char String[12]; // COMMAND STRING + UCHAR CMDLEN; // SIGNIFICANT LENGTH +// VOID (*CMDPROC)(struct _TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD);// COMMAND PROCESSOR + VOID (*CMDPROC)();// COMMAND PROCESSOR + size_t CMDFLAG; // FLAG/VALUE Offset + +}; + +struct APPLCONFIG +{ + char Command[12]; + char CommandAlias[48]; + char ApplCall[10]; + char ApplAlias[10]; + char L2Alias[10]; + int ApplQual; +}; + +typedef struct _MHSTRUC +{ + UCHAR MHCALL[7]; + UCHAR MHDIGIS[7][8]; + time_t MHTIME; + int MHCOUNT; + BYTE MHDIGI; + char MHFreq[12]; + char MHLocator[6]; +} MHSTRUC, *PMHSTRUC; + +// +// Session Record +// + +typedef struct _TRANSPORTENTRY +{ + UCHAR L4USER[7]; // CALL OF ORIGINATING USER + + union // POINTER TO TARGET HOST/LINK/DEST/PORT (if Pactor) + { + struct PORTCONTROL * PORT; + struct _LINKTABLE * LINK; + struct DEST_LIST * DEST; + struct _BPQVECSTRUC * HOST; + struct _EXTPORTDATA * EXTPORT; + } L4TARGET; + + UCHAR L4MYCALL[7]; // CALL WE ARE USING + + UCHAR CIRCUITINDEX; // OUR CIRCUIT INFO + UCHAR CIRCUITID; // + + UCHAR FARINDEX; // + UCHAR FARID; // OTHER END'S INFO + + UCHAR L4WINDOW; // WINDOW SIZE + UCHAR L4WS; // WINDOW START - NEXT FRAME TO ACK + UCHAR TXSEQNO; // + UCHAR RXSEQNO; // TRANSPORT LEVEL SEQUENCE INFO + UCHAR L4LASTACKED; // LAST SEQUENCE ACKED + + UCHAR FLAGS; // TRANSPORT LEVEL FLAGS + UCHAR NAKBITS; // NAK & CHOKE BITS TO BE SENT + struct _TRANSPORTENTRY * L4CROSSLINK; // POINTER TO LINKED L4 SESSION ENTRY + UCHAR L4CIRCUITTYPE; // BIT SIGNIFICANT - SEE BELOW + UCHAR KAMSESSION; // Session Number on KAM Host Mode TNC + struct DATAMESSAGE * L4TX_Q; + struct _L3MESSAGEBUFFER * L4RX_Q; + struct DATAMESSAGE * L4HOLD_Q; // FRAMES WAITING TO BE ACKED + struct _L3MESSAGEBUFFER * L4RESEQ_Q; // FRAMES RECEIVED OUT OF SEQUENCE + + UCHAR L4STATE; + USHORT L4TIMER; + UCHAR L4ACKREQ; // DATA ACK NEEDED + UCHAR L4RETRIES; // RETRY COUNTER + USHORT L4KILLTIMER; // IDLE CIRCUIT TIMER + USHORT SESSIONT1; // TIMEOUT FOR SESSIONS FROM HERE + UCHAR SESSPACLEN; // PACLEN FOR THIS SESSION + UCHAR BADCOMMANDS; // SUCCESSIVE BAD COMMANDS + UCHAR STAYFLAG; // STAY CONNECTED FLAG + UCHAR SPYFLAG; // SPY - CONNECT TO NODE VIA BBS CALLSIGN + + UCHAR RTT_SEQ; // SEQUENCE NUMBER BEING TIMED + uint32_t RTT_TIMER; // TIME ABOVE SEQUENCE WAS SENT + + USHORT PASSWORD; // AUTHORISATION CODE FOR REMOTE SYSOP + + UCHAR SESS_APPLFLAGS; // APPL FLAGS FOR THIS SESSION + + UCHAR Secure_Session; // Set if Host session from BPQTerminal or BPQMailChat + + VOID * DUMPPTR; // POINTER FOR REMOTnE DUMP MODE + VOID * PARTCMDBUFFER; // Save area for incomplete commmand + + long long Frequency; // If known - for CMS Reporting Hz + char RMSCall[10]; + UCHAR Mode; // ditto + + int UNPROTO; // Unproto Mode flag - port number if in unproto mode + int UAddrLen; // + char UADDRESS[64]; // Unproto Address String - Dest + Digis + + uint64_t LISTEN; // Port Mask if in Listen Mode + + char APPL[16]; // Set if session initiated by an APPL + int L4LIMIT; // Idle time for this Session + + // Now support compressing NetRom Sessions. + // We collect as much data as possible before compressing and re-packetizing + + int AllowCompress; + + unsigned char * unCompress; // Data being saved to uncompress + int unCompressLen; + + int Sent; + int SentAfterCompression; + + int Received; + int ReceivedAfterExpansion; + + +} TRANSPORTENTRY; + +// +// CIRCUITTYPE EQUATES +// + +#define L2LINK 1 +#define SESSION 2 +#define UPLINK 4 +#define DOWNLINK 8 +#define BPQHOST 32 +#define PACTOR 64 + +typedef struct ROUTE +{ + // Adjacent Nodes + + UCHAR NEIGHBOUR_CALL[7]; // AX25 CALLSIGN + UCHAR NEIGHBOUR_DIGI1[7]; // DIGIS EN ROUTE (MAX 2 - ?? REMOVE) + UCHAR NEIGHBOUR_DIGI2[7]; // DIGIS EN ROUTE (MAX 2 - ?? REMOVE) + + UCHAR NEIGHBOUR_PORT; + UCHAR NEIGHBOUR_QUAL; + UCHAR NEIGHBOUR_FLAG; // SET IF 'LOCKED' ROUTE + +#define LOCKEDBYCONFIG 1 +#define LOCKEDBYSYSOP 2 + + struct _LINKTABLE * NEIGHBOUR_LINK; // POINTER TO LINK FOR THIS NEIGHBOUR + + USHORT NEIGHBOUR_TIME; // TIME LAST HEARD (HH MM) + + int NBOUR_IFRAMES; // FRAMES SENT/RECEIVED + int NBOUR_RETRIES; // RETRASMISSIONS + + UCHAR NBOUR_MAXFRAME; // FOR OPTIMISATION CODE + UCHAR NBOUR_FRACK; + UCHAR NBOUR_PACLEN; + + BOOL INP3Node; + BOOL NoKeepAlive; // Suppress Keepalive Processing + int LastConnectAttempt; // To stop us trying too often + + int Status; // + int LastRTT; // Last Value Reported + int RTT; // Current + int SRTT; // Smoothed RTT + int NeighbourSRTT; // Other End SRTT +// int RTTIncrement; // Average of Ours and Neighbours SRTT in 10 ms + int BCTimer; // Time to next L3RTT Broadcast + int Timeout; // Lost Response Timer + int Retries; // Lost Response Count + struct _L3MESSAGEBUFFER * Msg; // RIF being built + + int OtherendsRouteQual; // Route quality used by other end. + int OtherendLocked; // Route quality locked by ROUTES entry. + int FirstTimeFlag; // Set once quality has been set by direct receive + +} *PROUTE; + +// Status Equates + +#define GotRTTRequest 1 // Other end has sent us a RTT Packet +#define GotRTTResponse 2 // Other end has sent us a RTT Response +#define GotRIF 4 // Other end has sent RIF, so is probably an INP3 Node + // (could just be monitoring RTT for some reason +#define SentRTTRequest 8 +#define SentOurRIF 16 // Set when we have sent a rif for our Call and any ApplCalls + // (only sent when we have seen both a request and response) + +#pragma pack(1) + +typedef struct _L3MESSAGEBUFFER +{ +// +// NETROM LEVEL 3 MESSAGE with Buffer Header +// + struct _L3MESSAGEBUFFER * Next; + UCHAR Port; + SHORT LENGTH; + UCHAR L3PID; // PID + + UCHAR L3SRCE[7]; // ORIGIN NODE + UCHAR L3DEST[7]; // DEST NODE + UCHAR L3TTL; // TX MONITOR FIELD - TO PREVENT MESSAGE GOING + // ROUND THE NETWORK FOR EVER DUE TO ROUTING LOOP +// +// NETROM LEVEL 4 DATA +// + UCHAR L4INDEX; // TRANSPORT SESSION INDEX + UCHAR L4ID; // TRANSPORT SESSION ID + UCHAR L4TXNO; // TRANSMIT SEQUENCE NUMBER + UCHAR L4RXNO; // RECEIVE (ACK) SEQ NUMBER + UCHAR L4FLAGS; // FRAGMENTATION, ACK/NAK, FLOW CONTROL AND MSG TYPE BITS + + UCHAR L4DATA[236] ; //DATA + +} L3MESSAGEBUFFER, *PL3MESSAGEBUFFER; + + +typedef struct _L3MESSAGE +{ +// +// NETROM LEVEL 3 MESSAGE - WITHOUT L2 INFO +// + UCHAR L3SRCE[7]; // ORIGIN NODE + UCHAR L3DEST[7]; // DEST NODE + UCHAR L3TTL; // TX MONITOR FIELD - TO PREVENT MESSAGE GOING // ROUND THE NETWORK FOR EVER DUE TO ROUTING LOOP +// +// NETROM LEVEL 4 DATA +// + UCHAR L4INDEX; // TRANSPORT SESSION INDEX + UCHAR L4ID; // TRANSPORT SESSION ID + UCHAR L4TXNO; // TRANSMIT SEQUENCE NUMBER + UCHAR L4RXNO; // RECEIVE (ACK) SEQ NUMBER + UCHAR L4FLAGS; // FRAGMENTATION, ACK/NAK, FLOW CONTROL AND MSG TYPE BITS + + UCHAR L4DATA[236] ; //DATA + +} L3MESSAGE, *PL3MESSAGE; + +#define MSGHDDRLEN (USHORT)(sizeof(VOID *) + sizeof(UCHAR) + sizeof(USHORT)) + +typedef struct _MESSAGE +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGE * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + +// MAY BE UP TO 56 BYTES OF DIGIS + + UCHAR CTL; + UCHAR PID; + + union + { /* array named screen */ + UCHAR L2DATA[256]; + struct _L3MESSAGE L3MSG; + }; + + UCHAR Padding[BUFFLEN - (sizeof(time_t) + (2 * sizeof(unsigned short)) + sizeof(VOID *) + 256 + MSGHDDRLEN + 16)]; // 16 = Addrs CTL PID + + time_t Timestamp; + struct _LINKTABLE * Linkptr; // For ACKMODE processing + unsigned short Process; // Process that got buffer + unsigned short GuardZone; // Should always be zero + +}MESSAGE, *PMESSAGE; + +#define MAXDATA BUFFLEN - (sizeof(time_t) + (2 * sizeof(unsigned short)) + sizeof(VOID *) + MSGHDDRLEN + 16) // 16 = Addrs CTL PID + + +typedef struct HDDRWITHDIGIS +{ +// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT + + struct _MESSAGE * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR DEST[7]; + UCHAR ORIGIN[7]; + UCHAR DIGIS[8][7]; + + UCHAR CTL; + UCHAR PID; + + union + { + UCHAR L2DATA[256]; + struct _L3MESSAGE L3MSG; + + }; + + UCHAR Padding[BUFFLEN - (sizeof(time_t) + (2 * sizeof(unsigned short)) + sizeof(VOID *) + 256 + 56 + MSGHDDRLEN + 16)]; // 16 = Addrs CTL PID + + time_t Timestamp; + struct _LINKTABLE * Linkptr; // For ACKMODE processing + unsigned short Process; // Process that got buffer + unsigned short GuardZone; + +} DIGIMESSAGE, *PDIGIMESSAGE; + + +typedef struct DATAMESSAGE +{ +// BASIC LINK LEVEL MESSAGE HEADERLAYOUT + + struct DATAMESSAGE * CHAIN; + + UCHAR PORT; + USHORT LENGTH; + + UCHAR PID; + + UCHAR L2DATA[256]; + +} *PDATAMESSAGE; + +typedef struct MSGWITHLEN +{ + // BASIC LINK LEVEL MESSAGE HEADERLAYOUT + + struct MSGWITHLEN * Next; + size_t Len; + UCHAR Data[256]; + +} *PMSGWITHLEN; + +typedef struct MSGWITHOUTLEN +{ + // Basic Chained Buffer + + struct MSGWITHLEN * Next; + UCHAR Data[256]; + +} *PMSGWITHOUTLEN; + +// +// BPQHOST MODE VECTOR STRUC +// + +#pragma pack() + +typedef struct _BPQVECSTRUC +{ + struct _TRANSPORTENTRY * HOSTSESSION; // Pointer to Session + UCHAR HOSTFLAGS; // ALLOCATED AND STATE CHANGE FLAGS + ULONG HOSTAPPLMASK; + UCHAR HOSTAPPLFLAGS; + UCHAR HOSTSTREAM; // STREAM NUMBER + PMESSAGE HOSTTRACEQ; + HWND HOSTHANDLE; // HANDLE FOR POSTING MSGS TO + ULONG HOSTAPPLNUM; // Application Number + ULONG STREAMOWNER; // PID of Process owning stream + char PgmName[32]; // Program Name; + + +} BPQVECSTRUC, *PBPQVECSTRUC; + +typedef struct _APPLCALLS +{ + +// Application Calls/Alias Supports multiple L4 application calls + + UCHAR APPLCALL[7]; // ax.25 + char APPLALIAS_TEXT[10]; // TEXT, WITH APPENDED SPACE + + char APPLCALL_TEXT[10]; + UCHAR APPLALIAS[6]; + char Filler; // So we can use ConvtoAX25 on Alias + USHORT APPLQUAL; + struct DEST_LIST * NODEPOINTER; // Pointer to "NODES" entry for this App (if L4) + + char APPLCMD[13]; // + BOOL APPLHASALIAS; + int APPLPORT; // Port used if APPL has an Alias + char APPLALIASVAL[48]; // Alias if defined + UCHAR L2ALIAS[7]; // Additional Alias foe L2 connects + +} APPLCALLS; + +// +// We store the Time Received from our neighbour. Before using it we add our current route SRTT +// This way our times adjust to changes of neighbour SRTT. We can't cater for changes to other hop RTTs, +// But if these are significant (say 25% or 100 ms) they will be retransmitted + +// We treat the Routes as an array of 6. First 3 are NODES routes, next 3 are INP3 Routes. This works, but maybe is not ideal + +typedef struct NR_DEST_ROUTE_ENTRY +{ + struct ROUTE * ROUT_NEIGHBOUR; // POINTER TO NEXT NODE IN PATH + UCHAR ROUT_QUALITY; // QUALITY + UCHAR ROUT_OBSCOUNT; + UCHAR ROUT_LOCKED; + UCHAR Padding[4]; // So Entries are the same length +} *PNR_DEST_ROUTE_ENTRY; + +typedef struct INP3_DEST_ROUTE_ENTRY +{ + struct ROUTE * ROUT_NEIGHBOUR; // POINTER TO NEXT NODE IN PATH + USHORT LastRTT; // Last Value Reported + USHORT RTT; // Current + USHORT SRTT; // Smoothed RTT + UCHAR Hops; +} *PDEST_ROUTE_ENTRY; + +typedef struct DEST_LIST +{ + // L4 Destinations (NODES) + + struct DEST_LIST * DEST_CHAIN; // SORTED LIST CHAIN + + UCHAR DEST_CALL[7]; // DESTINATION CALLSIGN (AX25 FORMAT) + UCHAR DEST_ALIAS[6]; + + UCHAR DEST_STATE; // CONTROL BITS - SETTING UP, ACTIVE ETC + UCHAR DEST_LOCKED; + + UCHAR DEST_ROUTE; // CURRENTY ACTIVE DESTINATION + UCHAR INP3FLAGS; + + struct NR_DEST_ROUTE_ENTRY NRROUTE[3]; // Best 3 NR neighbours for this dest + struct INP3_DEST_ROUTE_ENTRY INP3ROUTE[3]; // Best 3 INP neighbours for this dest + + void * DEST_Q; // QUEUE OF FRAMES FOR THIS DESTINATION + + int DEST_RTT; // SMOOTHED ROUND TRIP TIMER + int DEST_COUNT; // FRAMES SENT + +} dest_list; + +// IMNP3FLAGS Equates + +#define NewNode 1 // Just added to table, so need to pass on + +struct XDIGI +{ + struct XDIGI * Next; // Chain + + UCHAR Call[7]; + UCHAR Alias[7]; + int Port; + BOOL UIOnly; +}; + +struct WL2KInfo +{ + struct WL2KInfo * Next; + + char * Host; + short WL2KPort; + + char RMSCall[10]; + char BaseCall[10]; + char GridSquare[7]; + char Times[80]; + char ServiceCode[17]; + + BOOL UseRigCtrlFreqs; + char WL2KFreq[12]; + char xWL2KMode; // WL2K reporting mode + char WL2KModeChar; // W or N + BOOL DontReportNarrowOnWideFreqs; + +// char NARROWMODE; +// char WIDEMODE; // Mode numbers to report to WL2K + +// struct WL2KInfo WL2KInfoList[MAXFREQS]; // Freqs for sending to WL2K + + long long Freq; + char Bandwidth; +// char * TimeList; // eg 06-10,12-15 + int mode; // see below (an integer) + int baud; // see below (an integer) + int power; // actual power if known, default to 100 for HF, 30 for VHF/UHF (an integer) + int height; // antenna height in feet if known, default to 25 + int gain; // antenna gain if known, default to 0 + int direction; // primary antenna direction in degrees if known, use 000 for omni (an integer) + BOOL RPonPTC; // Set if scanning for Robust Packet on a PTC +}; + + +typedef struct PORTCONTROL +{ + UCHAR PORTCALL[7]; + UCHAR PORTALIAS[7]; //USED FOR UPLINKS ONLY + char PORTNUMBER; + char PROTOCOL; // PORT PROTOCOL + // 0 = KISS, 2 = NETROM, 4 = BPQKISS + //; 6 = HDLC, 8 = L2 + + struct PORTCONTROL * PORTPOINTER; // NEXT IN CHAIN + + PMESSAGE PORTRX_Q; // FRAMES RECEIVED ON THIS PORT + PMESSAGE PORTTX_Q; // FRAMES TO BE SENT ON THIS PORT + + void (FAR * PORTTXROUTINE)(struct _EXTPORTDATA * PORTVEC, MESSAGE * Buffer); // POINTER TO TRANSMIT ROUTINE FOR THIS PORT + void (FAR * PORTRXROUTINE)(struct _EXTPORTDATA * PORTVEC); // POINTER TO RECEIVE ROUTINE FOR THIS PORT + void (FAR * PORTINITCODE)(struct PORTCONTROL * PortVector); // INITIALISATION ROUTINE + void (FAR * PORTTIMERCODE)(struct PORTCONTROL * PortVector); // + void (FAR * PORTCLOSECODE)(struct PORTCONTROL * PortVector); // CLOSE ROUTINE + int (FAR * PORTTXCHECKCODE)(struct PORTCONTROL * PORTVEC, int Chan); // OK to TX Check + BOOL (FAR * PORTSTOPCODE)(struct PORTCONTROL * PORT); // Temporarily Stop Port + BOOL (FAR * PORTSTARTCODE)(struct PORTCONTROL * PORT); // Restart Port + BOOL PortStopped; // STOPPORT command used + BOOL PortSuspended; // Suspended by interlock + + char PORTDESCRIPTION[31];// TEXT DESCRIPTION OF FREQ/SPEED ETC (31 so null terminated) + + UCHAR PORTQUALITY; // 'STANDARD' QUALITY FOR THIS PORT + + char PORTBBSFLAG; // NZ MEANS PORT CALL/ALIAS ARE FOR BBS + char PORTL3FLAG; // NZ RESTRICTS OUTGOING L2 CONNECTS +// +// CWID FIELDS +// + + USHORT CWID[9]; // 8 ELEMENTS + FLAG + USHORT ELEMENT; // REMAINING BITS OF CURRENT CHAR + USHORT * CWPOINTER; // POINTER TO NEXT CHAR + USHORT CWIDTIMER; // TIME TO NEXT ID + char CWSTATE; // STATE MACHINE FOR CWID + char CWTYPE; // SET TO USE ON/OFF KEYING INSTEAD OF + // FSK (FOR RUH MODEMS) + UCHAR PORTMINQUAL; // MIN QUAL TO BRAOCAST ON THIS PORT + +// STATS COUNTERS + + int L2DIGIED; + int L2FRAMES; + int L2FRAMESFORUS; + int L2FRAMESSENT; + int L2TIMEOUTS; + int L2ORUNC; // OVERRUNS + int L2URUNC; // UNDERRUNS + int L1DISCARD; // FRAMES DISCARDED (UNABLE TO TX DUE TO DCD) + int L2FRMRRX; + int L2FRMRTX; + int RXERRORS; // RECEIVE ERRORS + int L2REJCOUNT; // REJ FRAMES RECEIVED + int L2OUTOFSEQ; // FRAMES RECEIVED OUT OF SEQUENCE + int L2RESEQ; // FRAMES RESEQUENCED + + USHORT SENDING; // LINK STATUS BITS + USHORT ACTIVE; + + UCHAR AVSENDING; // LAST MINUTE + UCHAR AVACTIVE; + + char PktFlags[64]; // Decode stts rom QtSM + + char PORTTYPE; // H/W TYPE + // 0 = ASYNC, 2 = PC120, 4 = DRSI + // 6 = TOSH, 8 = QUAD, 10 = RLC100 + // 12 = RLC400 14 = INTERNAL 16 = EXTERNAL + + + USHORT IOBASE; // CONFIG PARAMS FOR HARDWARE DRIVERS + + char INTLEVEL; // NEXT 4 SAME FOR ALL H/W TYPES + int BAUDRATE; // SPEED + char CHANNELNUM; // ON MULTICHANNEL H/W + struct PORTCONTROL * INTCHAIN; // POINTER TO NEXT PORT USING THIS LEVEL + UCHAR PORTWINDOW; // L2 WINDOW FOR THIS PORT + USHORT PORTTXDELAY; // TX DELAY FOR THIS PORT + UCHAR PORTPERSISTANCE; // PERSISTANCE VALUE FOR THIS PORT + UCHAR FULLDUPLEX; // FULL DUPLEX IF SET + UCHAR SOFTDCDFLAG; // IF SET USE 'SOFT DCD' - IF MODEM CANT GIVE A REAL ONE + UCHAR PORTSLOTTIME; // SLOT TIME + UCHAR PORTTAILTIME; // TAIL TIME + UCHAR PORTT1; // L2 TIMEOUT + UCHAR PORTT2; // L2 DELAYED ACK TIMER + UCHAR PORTN2; // RETRIES + UCHAR PORTPACLEN; // DEFAULT PACLEN FOR INCOMING SESSIONS + + UINT * PORTINTERRUPT; // ADDRESS OF INTERRUPT HANDLER + + UCHAR QUAL_ADJUST; // % REDUCTION IN QUALITY IF ON SAME PORT + + char * PERMITTEDCALLS; // POINTER TO PERMITED CALLS LIST + char * PORTUNPROTO; // POINTER TO UI DEST AND DIGI LIST + UCHAR PORTDISABLED; // PORT TX DISABLE FLAG + char DIGIFLAG; // ENABLE/DISABLE/UI ONLY + UCHAR DIGIPORT; // CROSSBAND DIGI PORT + USHORT DIGIMASK; // CROSSBAND DIGI MASK + UCHAR USERS; // MAX USERS ON PORT + USHORT KISSFLAGS; // KISS SPECIAL MODE BITS + UCHAR PORTINTERLOCK; // TO DEFINE PORTS WHICH CANT TX AT SAME TIME + // + // PORTINTERLOCK was also used to prevent use of Attach'able ports at the same time + // and was the RADIO number used in rig control + // + // Now a port can have both a rx and tx radio definition, so Attach now + // Checks them (Transferred to TNC->TXRADIO and RXRADIO + // + // For backward compatibility of configs, if INTERLOCK is set on an Attachable + // port it is set as a default tx and rx radio + // + // Acually may not need the following PORT fields + + UCHAR PORTXRADIO; // TO DEFINE PORTS WHICH CANT TX Attach AT SAME TIME + UCHAR PORRXRADIO; // TO DEFINE PORTS WHICH CANT TX Attach AT SAME TIME + UCHAR NODESPACLEN; // MAX LENGTH OF 'NODES' MSG + UCHAR TXPORT; // PORT FOR SHARED TX OPERATION + MHSTRUC * PORTMHEARD; // POINTER TO MH DATA + + USHORT PARAMTIMER; // MOVED FROM HW DATA FOR SYSOPH + UCHAR PORTMAXDIGIS; // DIGIS ALLOWED ON THIS PORT + UCHAR PORTALIAS2[7]; // 2ND ALIAS FOR DIGIPEATING FOR APRS + UCHAR PORTBCALL[7]; // Source call for Beacon + char PortNoKeepAlive; // Default to no Keepalives + char PortUIONLY; // UI only port - no connects + char UICAPABLE; // Pactor-style port that can do UI + + struct WL2KInfo WL2KInfo; // WL2K Report for this Port + struct in_addr PORTIPADDR; // IP address for "KISS over UDP" + int ListenPort; // For KISS over UDP, if Different TX and RX Ports needed + BOOL KISSTCP; // TCP instead of UDP for KISS + BOOL KISSSLAVE; // TCP KISS is Slave + + char * SerialPortName; // Serial Port Name for Unix + struct XDIGI * XDIGIS; // Cross port digi setup + + BOOL NormalizeQuality; // Normalise Node Qualities + BOOL IgnoreUnlocked; // Ignore Unlocked routes + BOOL INP3ONLY; // Default to INP3 and disallow NODES + BOOL ALLOWINP3; + + void (* UIHook)(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG); // Used for KISSARQ + struct PORTCONTROL * HookPort; + int PortSlot; // Index in Port Table + struct TNCINFO * TNC; // Associated TNC record + int HWType; // Hardware type of Driver. In here as external apps don't have access to TNC record + int RIGPort; // Linked port for freq resporting + unsigned int PERMITTEDAPPLS; // Appls allowed on this port (generalisation of BBSBANNED) + char * CTEXT; // Port Specific CText + char Hide; // Hide from port display and AGW connect menu + TRANSPORTENTRY * Session; // For Response to KISS command + time_t LastKISSCmdTime; + time_t LastSmartIDTime; // For SmartID - ID only if packets sent recently + time_t SmartIDNeeded; // Time to send next smart ID + time_t SmartIDInterval; // Smart ID Interval (Secs) + int SendtoM0LTEMap; + uint64_t PortFreq; // Configured freq + char * M0LTEMapInfo; + int QtSMPort; + BOOL QtSMConnected; + + int StatsPointer; + UCHAR * TX; // % Sending + UCHAR * BUSY; // % Active (Normally DCD active or TX) + + int Hardware; // TNC H_TYPE. Copied here for access from application context + + +} PORTCONTROLX, *PPORTCONTROL; + +typedef struct FULLPORTDATA +{ + struct PORTCONTROL PORTCONTROL; + UCHAR HARDWAREDATA[300]; // WORK AREA FOR HARDWARE DRIVERS +} *PFULLPORTDATA; + +// KISS Mapping of HARDWAREDATA + +typedef struct KISSINFO +{ + struct PORTCONTROL PORT; + + int LINKSTS; // CURRENT STATE + UINT * CURALP; // CURRENT BUFFER + UINT * NEXTCHR; // + void * ASYNCMSG_Q; // RECEIVED MESSAGES + void * KISSTX_Q ; // MESSAGES TO SEND + int ESCFLAG ; // ; SET IF LAST RX CHAR WAS DLE + int ESCTXCHAR; // ; CHAR TO SEND FOLLOWING DLE IF NZ + + struct KISSINFO * FIRSTPORT; // ; FIRST PORT DEFINED FOR THIS IO ADDR + struct KISSINFO * SUBCHAIN; // ; NEXT SUBCHANNEL FOR SAME PHYSICAL PORT + + int OURCTRL; // ; CONTROL BYTE FOR THIS PORT + + int XCTRL; // CONTROL BYTE TO SEND + int REALKISSFLAGS; // ; KISS FLAGS FOR ACTIVE SUBPORT + + USHORT TXCCC; // ; NETROM/BPQKISS CHECKSUMS + USHORT RXCCC; // + + int TXACTIVE; // TIMER TO DETECT 'HUNG' SENDS + + int POLLFLAG; // POLL OUTSTANDING FOR MULTIKISS + + struct KISSINFO * POLLPOINTER; // LAST GROUP POLLED + int POLLED; // SET WHEN POLL RECEIVED + + UCHAR * KISSCMD; // Commands to be sent when port opened + int KISSCMDLEN; + + int PTTMode; // PTT Mode Flags + int PTTState; // Current State + uint64_t PTTActivemS; // For Stats + uint64_t PTTonTime; // + + uint64_t BusyActivemS; // For channel busy stats + uint64_t BusyonTime; + + char * QtSMModem; + int QtSMFreq; + int QtSMStats; // Set if stats received as KISS Command + +// UCHAR WIN32INFO[16]; // FOR WINDOWS DRIVER +} *PKISSINFO; + +// EXT Driver Mapping of HARDWAREDATA + + +typedef struct _EXTPORTDATA +{ + struct PORTCONTROL PORTCONTROL ; // REMAP HARDWARE INFO + + void * (* PORT_EXT_ADDR) (int fn, int port, PDATAMESSAGE buff); // ADDR OF RESIDENT ROUTINE + char PORT_DLL_NAME[16]; + UCHAR EXTRESTART; // FLAG FOR DRIVER REINIT + HINSTANCE DLLhandle; + int MAXHOSTMODESESSIONS; // Max Host Sessions supported (Used for KAM Pactor + ax.25 support) + struct _TRANSPORTENTRY * ATTACHEDSESSIONS[27]; // For PACTOR. etc + BOOL PERMITGATEWAY; // Set if ax.25 ports can change callsign (ie SCS, not KAM + int SCANCAPABILITIES; //Type of scan control Controller supports (None, Simple, Connect Lock) +#define NONE 0 +#define SIMPLE 1 +#define CONLOCK 2 + + void * UI_Q; // Unproto Frames for Session Mode Drivers (TRK, etc) + int FramesQueued; // TX Frames queued in Driver + +} EXTPORTDATA, *PEXTPORTDATA; + +typedef struct _HDLCDATA +{ + struct PORTCONTROL PORTCONTROL ; // REMAP HARDWARE INFO +// +// Mapping of VXD fields (mainly to simplify debugging +// + + ULONG ASIOC; // A CHAN ADDRESSES + ULONG SIO; // OUR ADDRESSES (COULD BE A OR B) + ULONG SIOC; + ULONG BSIOC; // B CHAN CONTROL + + struct _HDLCDATA * A_PTR; // PORT ENTRY FOR A CHAN + struct _HDLCDATA * B_PTR; // PORT ENTRY FOR B CHAN + + VOID (FAR * VECTOR[4]) (); // INTERRUPT VECTORS + +// UINT * IOTXCA; // INTERRUPT VECTORS +// UINT * IOTXEA; +// UINT * IORXCA; +// UINT * IORXEA; + + UCHAR LINKSTS; + + UINT * SDRNEXT; + UINT * SDRXCNT; + UINT * CURALP; + UCHAR OLOADS; // LOCAL COUNT OF BUFFERS SHORTAGES + USHORT FRAMELEN; + UINT * SDTNEXT; // POINTER to NEXT BYTE to TRANSMIT + USHORT SDTXCNT; // CHARS LEFT TO SEND + UCHAR RR0; // CURRENT RR0 + UINT * TXFRAME; // ADDRESS OF FRAME BEING SENT + + UCHAR SDFLAGS; // GENERAL FLAGS + + void * PCTX_Q; // HDLC HOLDING QUEUE + void * RXMSG_Q; // RX INTERRUPT TO SDLC BG + + +//;SOFTDCD DB 0 ; RX ACTIVE FLAG FOR 'SOFT DC + UCHAR TXDELAY; // TX KEYUP DELAY TIMER + UCHAR SLOTTIME; // TIME TO WAIT IF WE DONT SEND + UCHAR FIRSTCHAR; // CHAR TO SEND FOLLOWING TXDELAY + USHORT L1TIMEOUT; // UNABLE TO TX TIMEOUT + UCHAR PORTSLOTIMER; + + USHORT TXBRG; // FOR CARDS WITHOUT /32 DIVIDER + USHORT RXBRG; + + UCHAR WR10 ; // NRZ/NRZI FLAG + + int IRQHand; + int fd; // file descriptor for LKM + + ULONG IOLEN; // Number of bytes in IO Space + + struct PORTCONTROL * DRIVERPORTTABLE; // ADDR OF PORT TABLE ENTRY IN VXD + // Used in NT Driver for Kernel Device Pointer + +}HDLCDATA, * PHDLCDATA; + + +extern struct ROUTE * NEIGHBOURS; +extern int ROUTE_LEN; +extern int MAXNEIGHBOURS; + +extern struct DEST_LIST * DESTS; // NODE LIST +extern int DEST_LIST_LEN; +extern int MAXDESTS; // MAX NODES IN SYSTEM + +extern struct _LINKTABLE * LINKS; +extern int LINK_TABLE_LEN; +extern int MAXLINKS; +/* +L4TABLE DD 0 +MAXCIRCUITS DW 50 ; NUMBER OF L4 CIRCUITS + +NUMBEROFPORTS DW 0 + +TNCTABLE DD 0 +NUMBEROFSTREAMS DW 0 + +ENDDESTLIST DD 0 ; NODE LIST+1 +*/ + +typedef struct _LINKTABLE +{ +//; +//; LEVEL 2 LINK CONTROL TABLE +//; + + UCHAR LINKCALL[7]; // CALLSIGN OF STATION + UCHAR OURCALL[7]; // CALLSIGN OF OUR END + UCHAR DIGIS[56]; // LEVEL 2 DIGIS IN PATH + + char callingCall[10]; // for reporting. Link and Our calls depand on which end connected + char receivingCall[10]; // for reporting. Link and Our calls depand on which end connected + + char Direction[4]; // In or Out + + PPORTCONTROL LINKPORT; // PORT NUMBER + UCHAR LINKTYPE; // 1 = UP, 2= DOWN, 3 = INTERNODE + + UCHAR LINKNR; + UCHAR LINKNS; // LEV 2 SEQUENCE COUNTS + UCHAR LINKWS; // WINDOW START + UCHAR LINKOWS; // OLD (LAST ACKED) WINDOW START + UCHAR LINKWINDOW; // LEVEL 2 WINDOW SIZE + + UCHAR L2FLAGS; // CONTROL BITS + UCHAR VER1FLAG; // SET IF OTHER END RUNNING VERSION 1 + + VOID * RX_Q; // PACKETS RECEIVED ON THIS LINK + VOID * TX_Q; // PACKETS TO SEND + VOID * FRAMES[8]; // FRAMES WAITING ACK + VOID * RXFRAMES[8]; // Frames received out of sequence + + UCHAR L2STATE; // PROCESSING STATE + UCHAR Ver2point2; // Set if running 2.2 + USHORT L2TIMER; // FRAME RETRY TIMER + UCHAR L2TIME; // RETRY TIMER INITIAL VALUE + USHORT L2SLOTIM; // DELAY FOR LINK VALIDATION POLL + UCHAR L2ACKREQ; // DELAYED TEXT ACK TIMER + UCHAR REJTIMER; // TO TIME OUT REJ IN VERSION 1 + USHORT LAST_F_TIME; // TIME LAST R(F) SENT + UCHAR SDREJF; // FLAGS FOR FRMR + UCHAR SDRBYTE; // SAVED CONTROL BYTE FOR FRMR + + UCHAR SDTSLOT ; // POINTER TO NEXT TXSLOT TO USE + + UCHAR L2RETRIES; // RETRY COUNTER + + UCHAR SESSACTIVE; // SET WHEN WE ARE SURE SESSION IS UP + + UINT APPLMASK; // Used when XIR processed + VOID * ALIASPTR; + + USHORT KILLTIMER; // TIME TO KILL IDLE LINK + + VOID * CIRCUITPOINTER; // POINTER TO L4 CIRCUIT TABLE ENTRY + // (IF UP/DOWN) + PROUTE NEIGHBOUR; // POINTER TO NEIGHBOUR (IF CROSSLINK) + + VOID * L2FRAG_Q; // DEFRAGMENTATION QUEUE + + int IFrameRetryCounter; // Number of times an I frame in repeated without a frame being acked + + time_t ConnectTime; // For session stats + int bytesRXed; // Info bytes only + int bytesTXed; + + // Now support compressing L2 Sessions. + // We collect as much data as possible before compressing and re-packetizing + + int AllowCompress; + + unsigned char * unCompress; // Data being saved to uncompress + int unCompressLen; + + int Sent; + int SentAfterCompression; + + int Received; + int ReceivedAfterExpansion; + + +} LINKTABLE; + +#pragma pack(1) + +struct myin_addr { + union { + struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; + struct { u_short s_w1,s_w2; } S_un_w; + uint32_t addr; + }; +}; + +typedef struct _IPMSG +{ +// FORMAT OF IP HEADER +// +// NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + UCHAR VERLEN; // 4 BITS VERSION, 4 BITS LENGTH + UCHAR TOS; // TYPE OF SERVICE + USHORT IPLENGTH; // DATAGRAM LENGTH + USHORT IPID; // IDENTIFICATION + USHORT FRAGWORD; // 3 BITS FLAGS, 13 BITS OFFSET + UCHAR IPTTL; + UCHAR IPPROTOCOL; // HIGHER LEVEL PROTOCOL + USHORT IPCHECKSUM; // HEADER CHECKSUM + struct myin_addr IPSOURCE; + struct myin_addr IPDEST; + + UCHAR Data; + +} IPMSG, *PIPMSG; + +typedef struct _PSEUDOHEADER +{ + struct myin_addr IPSOURCE; + struct myin_addr IPDEST; + UCHAR Reserved; + UCHAR IPPROTOCOL; // HIGHER LEVEL PROTUDP/TCP Length + USHORT LENGTH; // DATAGRAM LENGTH + +} PHEADER; + +typedef struct _TCPMSG +{ + +// FORMAT OF TCP HEADER WITHIN AN IP DATAGRAM + +// NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + USHORT SOURCEPORT; + USHORT DESTPORT; + + uint32_t SEQNUM; + uint32_t ACKNUM; + + UCHAR TCPCONTROL; // 4 BITS DATA OFFSET 4 RESERVED + UCHAR TCPFLAGS; // (2 RESERVED) URG ACK PSH RST SYN FIN + + USHORT WINDOW; + USHORT CHECKSUM; + USHORT URGPTR; + + +} TCPMSG, *PTCPMSG; + +typedef struct _UDPMSG +{ + +// FORMAT OF UDP HEADER WITHIN AN IP DATAGRAM + +// NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + USHORT SOURCEPORT; + USHORT DESTPORT; + USHORT LENGTH; + USHORT CHECKSUM; + UCHAR UDPData[0]; + +} UDPMSG, *PUDPMSG; + +// ICMP MESSAGE STRUCTURE + +typedef struct _ICMPMSG +{ + // FORMAT OF ICMP HEADER WITHIN AN IP DATAGRAM + + // NOTE THESE FIELDS ARE STORED HI ORDER BYTE FIRST (NOT NORMAL 8086 FORMAT) + + UCHAR ICMPTYPE; + UCHAR ICMPCODE; + USHORT ICMPCHECKSUM; + + USHORT ICMPID; + USHORT ICMPSEQUENCE; + UCHAR ICMPData[0]; + +} ICMPMSG, *PICMPMSG; + +#pragma pack() + +struct SEM +{ + UINT Flag; + int Clashes; + int Gets; + int Rels; + DWORD SemProcessID; +#ifdef WIN32 + DWORD SemThreadID; +#else + pthread_t SemThreadID; +#endif + int Line; // caller file and line + char File[MAX_PATH]; +}; + + +#define TNCBUFFLEN 8192 +#define MAXSTREAMS 32 + +// DED Emulator Stream Info + +struct StreamInfo +{ + UCHAR * Chan_TXQ; // !! Leave at front so ASM Code Finds it + // FRAMES QUEUED TO NODE + int BPQStream; + BOOL Connected; // Set if connected to Node + int CloseTimer; // Used to close session after connect failure + UCHAR MYCall[30]; + char OutgoingCall[16]; +}; + +struct TNC2StreamInfo +{ + // Not sure which of these is session specific + + UCHAR VMSR; // VIRTUAL MSR (Only Connected Bit) + int BPQPort; + BOOL MODEFLAG; // COMMAND/DATA MODE + int TPACLEN; // MAX PACKET SIZE FOR TNC GENERATED PACKETS + char RemoteCall[10]; // FOr Stream Changed Message + +/* I suspect only the above are needed + + int TRANSTIMER; // TRANPARENT MODE SEND TIMOUT + BOOL AUTOSENDFLAG; // SET WHEN TRANSMODE TIME EXPIRES + + int CMDTMR; // TRANSARENT MODE ESCAPE TIMER + int COMCOUNT; // NUMBER OF COMMAND CHARS RECEIVED + int CMDTIME ; // GUARD TIME FOR TRANS MODE EACAPE + int CMSG; // Enable CTEXT flag + int COMCHAR; // CHAR TO LEAVE CONV MODE + int PASSCHAR; // Escape char + char CTEXT[120]; + char ECHOFLAG; // ECHO ENABLED + BOOL TRACEFLAG; // MONITOR ON/OFF + BOOL FLOWFLAG; // FLOW OFF/ON + + BOOL CONOK; + BOOL CBELL; + BOOL NOMODE; // MODE CHANGE FLAGS + BOOL NEWMODE; + BOOL CONMODEFLAG; // CONNECT MODE - CONV OR TRANS + BOOL LFIGNORE; + BOOL MCON; // TRACE MODE FLAGS + BOOL MCOM; + BOOL MALL; + BOOL AUTOLF; // Add LF after CR + BOOL BBSMON; // SPECIAL SHORT MONITOR FOR BBS + BOOL MTX; // MONITOR TRANSMITTED FRAMES + BOOL MTXFORCE; // MONITOR TRANSMITTED FRAMES EVEN IF M OFF + UINT MMASK; // MONITOR PORT MASK + BOOL HEADERLN; // PUT MONITORED DATA ON NEW LINE FLAG + BOOL InEscape; // PASS Char received (treat next char as normal char not ctrl char) + + UINT APPLICATION; // APPLMASK + UINT APPLFLAGS; // FLAGS TO CONTROL APPL SYSTEM + + UINT SENDPAC; // SEND PACKET CHAR + BOOL CPACTIME; // USE PACTIME IN CONV MODE + BOOL CRFLAG ; // APPEND SENDPAC FLAG + + int TPACLEN ; // MAX PACKET SIZE FOR TNC GENERATED PACKETS + UCHAR UNPROTO[64]; // UNPROTO DEST AND DIGI STRING + + char MYCALL[10]; +*/ + +}; + + +struct TNCDATA +{ + struct TNCDATA * Next; + unsigned int Mode; // 0 = TNC2, others may follow + + UCHAR TOUSERBUFFER[TNCBUFFLEN]; // BUFFER TO USER + UCHAR TONODEBUFFER[300]; // BUFFER TO NODE + UCHAR FROMUSERBUFFER[TNCBUFFLEN]; + + char PORTNAME[80]; // for Linux Port Names + int ComPort; + BOOL VCOM; + int RTS; + int CTS; + int DCD; + int DTR; + int DSR; + int BPQPort; + int Speed; + char PortLabel[20]; + char TypeFlag[2]; + BOOL PortEnabled; + HANDLE hDevice; + BOOL NewVCOM; // Set if User Mode VCOM Port + + UCHAR VMSR; // VIRTUAL MSR + +// BIT 7 - Receive Line Signal Detect (DCD) +// 6 - Ring Indicator +// 5 - Data Set Ready +// 4 - Clear To Send +// 3 - Delta RLSD ( ie state has changed since last +// access) +// 2 - Trailing Edge Ring Detect +// 1 - Delta DSR +// 0 - Delta CTS + + + BOOL RTSFLAG; // BIT 0 SET IF RTS/DTR UP + UCHAR VLSR; // LAST RECEIVED LSR VALUE + + int RXCOUNT; // BYTES IN RX BUFFER + UCHAR * PUTPTR; // POINTER FOR LOADING BUFFER + UCHAR * GETPTR; // POINTER FOR UNLOADING BUFFER + UCHAR * CURSOR; // POSTION IN KEYBOARD BUFFER + + int MSGLEN; + int TRANSTIMER; // TRANPARENT MODE SEND TIMOUT + BOOL AUTOSENDFLAG; // SET WHEN TRANSMODE TIME EXPIRES + + int CMDTMR; // TRANSARENT MODE ESCAPE TIMER + int COMCOUNT; // NUMBER OF COMMAND CHARS RECEIVED + int CMDTIME ; // GUARD TIME FOR TRANS MODE EACAPE + int CMSG; // Enable CTEXT flag + int COMCHAR; // CHAR TO LEAVE CONV MODE + int PASSCHAR; // Escape char + int StreamSW; // Stream Switch Char + int StreamDbl; + int StreamCall; // Send call with stream switch char + int LCStream; // Stream is case independant + int Users; // Number of streams allowed + + char CTEXT[120]; + char ECHOFLAG; // ECHO ENABLED + BOOL TRACEFLAG; // MONITOR ON/OFF + BOOL FLOWFLAG; // FLOW OFF/ON + + BOOL CONOK; + BOOL CBELL; + BOOL NOMODE; // MODE CHANGE FLAGS + BOOL NEWMODE; + BOOL CONMODEFLAG; // CONNECT MODE - CONV OR TRANS + BOOL LFIGNORE; + BOOL MCON; // TRACE MODE FLAGS + BOOL MCOM; + BOOL MALL; + BOOL MUIONLY; + BOOL AUTOLF; // Add LF after CR + BOOL BBSMON; // SPECIAL SHORT MONITOR FOR BBS + BOOL MTX; // MONITOR TRANSMITTED FRAMES + BOOL MTXFORCE; // MONITOR TRANSMITTED FRAMES EVEN IF M OFF + uint64_t MMASK; // MONITOR PORT MASK + BOOL HEADERLN; // PUT MONITORED DATA ON NEW LINE FLAG + BOOL InEscape; // PASS Char received (treat next char as normal char not ctrl char) + BOOL InStreamSW; // StreamSW Char received (treat next char as new stream) + +// BOOL MODEFLAG; // TNC2 COMMAND/DATA MODE + + UINT APPLICATION; // APPLMASK + + UINT APPLFLAGS; // FLAGS TO CONTROL APPL SYSTEM + + UINT SENDPAC; // SEND PACKET CHAR + BOOL CPACTIME; // USE PACTIME IN CONV MODE + BOOL CRFLAG ; // APPEND SENDPAC FLAG + + int TPACLEN ; // MAX PACKET SIZE FOR TNC GENERATED PACKETS + UCHAR UNPROTO[64]; // UNPROTO DEST AND DIGI STRING + + char MYCALL[10]; + + // TNC2 Stream Fields + + int TXStream; // Currently Selected Stream + int RXStream; + + struct TNC2StreamInfo * TNC2Stream[26]; // For StreamSW support + + // DED Mode Fields + + int PollDelay; // Used by VCOM to slow down continuous reads on real port + + struct StreamInfo * Channels[MAXSTREAMS+1]; + char MODE; // INITIALLY TERMINAL MODE + char HOSTSTATE; // HOST STATE MACHINE + int MSGCOUNT; // LENGTH OF MESSAGE EXPECTED + int MSGLENGTH; + char MSGTYPE; + unsigned char MSGCHANNEL; + char DEDMODE; // CLUSTER MODE - DONT ALLOW DUP CONNECTS + int HOSTSTREAMS; // Default Streams + + UCHAR DEDTXBUFFER[256]; + UCHAR * DEDCURSOR; + + unsigned char MONBUFFER[258]; //="\x6"; + int MONLENGTH; + int MONFLAG; + + time_t LastDEDPollTime; // To detect lost host program + + // Kantronics Fields + + int RXBPtr; + char nextMode; // Mode after RESET + + // SCS Fields + + int FROMUSERLEN; + + BOOL Term4Mode; // Used by Airmail + BOOL PACMode; // SCS in Packet Mode + BOOL Toggle; // SCS Sequence Toggle + + char MyCall[10]; + +}; + +// Emulatiom mode equates + +#define TNC2 0 +#define DED 1 +#define KANTRONICS 2 // For future use +#define SCS 3 + + +#define MAX_ENTRIES 128 +#define MaxMHEntries 100 +#define MAX_BROADCASTS 8 +#define MAXUDPPORTS 30 + +#ifndef MAXGETHOSTSTRUCT +#define MAXGETHOSTSTRUCT 1024 +#endif + + +struct arp_table_entry +{ + unsigned char callsign[7]; + unsigned char len; // bytes to compare (6 or 7) + BOOL IPv6; + +// union +// { +// struct in_addr in_addr; +// unsigned int ipaddr; +// struct in6_addr in6_addr; +// }; + + unsigned short port; + unsigned char hostname[64]; + unsigned int error; + BOOL ResolveFlag; // True if need to resolve name + unsigned int keepalive; + unsigned int keepaliveinit; + BOOL BCFlag; // True if we want broadcasts to got to this call + BOOL AutoAdded; // Set if Entry created as a result of AUTOADDMAP + SOCKET TCPListenSock; // Listening socket if slave + SOCKET TCPSock; + int TCPMode; // TCPMaster ot TCPSlave + UCHAR * TCPBuffer; // Area for building TCP message from byte stream + int InputLen; // Bytes in TCPBuffer + + union + { + struct sockaddr_in6 destaddr6; + struct sockaddr_in destaddr; + }; + + BOOL TCPState; + pthread_t TCPThreadID; // Thread ID if TCP Master + UINT TCPOK; // Cleared when Message RXed . Incremented by timer + int SourcePort; // Used to select socket, hence from port. +// SOCKET SourceSocket; + struct AXIPPORTINFO * PORT; + BOOL noUpdate; // Don't update dest address from incoming packet + BOOL replytoSourcePort; // Update map entry dest port from source port of each packet. + time_t LastHeard; // Last Packet received from this ststiom +}; + + +struct broadcast_table_entry +{ + unsigned char callsign[7]; + unsigned char len; // bytes to compare (6 or 7) +}; + + +struct MHTableEntry +{ + unsigned char callsign[7]; + char proto; + short port; + union + { + struct in_addr ipaddr; + struct in6_addr ipaddr6; + }; + time_t LastHeard; // Time last packet received + int Keepalive; + BOOL IPv6; +}; + + +struct AXIPPORTINFO +{ + int Port; + + struct MHTableEntry MHTable[MaxMHEntries]; + struct broadcast_table_entry BroadcastAddresses[MAX_BROADCASTS]; + + int NumberofBroadcastAddreses; + BOOL Checkifcanreply; + + int arp_table_len; + int ResolveIndex; // pointer to entry being resolved + + struct arp_table_entry arp_table[MAX_ENTRIES]; + + struct arp_table_entry default_arp; + + BOOL MHEnabled; + BOOL MHAvailable; // Enabled with config file directive + + BOOL AutoAddARP; + BOOL AutoAddBC; // Broadcast flag for autoaddmap + + unsigned char hostaddr[64]; + + HWND hResWnd, hMHWnd, ConfigWnd; + + BOOL GotMsg; + + int udpport[MAXUDPPORTS+2]; + BOOL IPv6[MAXUDPPORTS+2]; + + BOOL PortIPv6; // Set if any MAPS for IPv6 + + int NumberofUDPPorts; + + BOOL needip; + BOOL NeedResolver; + BOOL NeedTCP; + + SOCKET sock; // IP 93 Sock + SOCKET udpsock[MAXUDPPORTS+2]; + + time_t ltime,lasttime; + int baseline; + int mhbaseline; + int CurrentMHEntries; + + char buf[MAXGETHOSTSTRUCT]; + + int MaxMHWindowlength; + int MaxResWindowlength; + + BOOL ResMinimized; + BOOL MHMinimized; + + HMENU hResMenu; + HMENU hMHMenu; + + pthread_t ResolveNamesThreadId; + +}; + + +#define Disconnect(stream) SessionControl(stream,2,0) +#define Connect(stream) SessionControl(stream,1,0) + +#endif + + + diff --git a/.svn/pristine/7f/7fe27d4c1215961c8bcfde3ae0948a1e2cd8e9e6.svn-base b/.svn/pristine/7f/7fe27d4c1215961c8bcfde3ae0948a1e2cd8e9e6.svn-base new file mode 100644 index 0000000..4d9abca --- /dev/null +++ b/.svn/pristine/7f/7fe27d4c1215961c8bcfde3ae0948a1e2cd8e9e6.svn-base @@ -0,0 +1,5459 @@ +/* +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 +*/ + + +// Module to provide a basic Gateway between IP over AX.25 and the Internet. + +// Uses WinPcap on Windows, TAP Driver on Linux + +// Basically operates as a mac level bridge, with headers converted between ax.25 and Ethernet. +// ARP frames are also reformatted, and monitored to build a simple routing table. +// Apps may not use ARP (MSYS is configured with an ax.25 default route, rather than an IP one), +// so the default route must be configured. + +// Intended as a gateway for legacy apps, rather than a full function ip over ax.25 router. +// Suggested config is to use the Internet Ethernet Adapter, behind a NAT/PAT Router. +// The ax.25 applications will appear as single addresses on the Ethernet LAN + +// The code can also switch packets between ax.25 interfaces + +// First Version, July 2008 + +// Version 1.2.1 January 2009 + +// Add IP Address Mapping option + +// June 2014. Convert to Router instead of MAC Bridge, and include a RIP44 decoder +// so packets can be routed from RF to/from encapsulated 44 net subnets. +// Routes may also be learned from received RF packets, or added from config file + +/* +TODo ?Multiple Adapters +*/ + +/* + Windows uses PCAP to send to both the local host (the machine running BPQ) and + to other machines on the same LAN. I may be able to add the 44/8 route but + dont at the moment. + + On Linux, the local machine doesn't see packets sent via pcap, so it uses a TAP + device for the local host, and pcap for other addresses on the LAN. The TAP is + created dynamically - it doesn't have to be predefined. A route to 44/8 via the + TAP and an ARP entry for it are also added. The TAP runs unnumbered + + 44 addresses can be NAT'ed to the local LAN address, so hosts don't have to have + both an ISP and a 44 address. You can run your local LAN as 44, but I would expect + most uses to prefer to keep their LAN with its normal (usually 192.168) addresses. + + If the PC address isn't the same as the IPGateway IPAddr a NAT entry is created + automaticaly. + + In these cases the NAT line for jnos should have TAP appended to tell + LinBPQ it is reached over the TAP. + + NAT 44.131.11.x 192.168.x.y TAP + +*/ + + +//int _winver = 0x0600; + +#pragma data_seg("_BPQDATA") + +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include + +#include "cheaders.h" + +#include "ipcode.h" + +#ifdef WIN32 +#include +#define read _read +#define write _write +#define close _close +#include +// Link with Iphlpapi.lib +#pragma comment(lib, "IPHLPAPI.lib") +#endif + +#include + +#ifdef WIN32 +int pcap_sendpacket(pcap_t *p, u_char *buf, int size); +#else +#ifndef PCAP_API +#define PCAP_API extern +#endif +PCAP_API int pcap_sendpacket(pcap_t *, const u_char *, int); +#endif + +#ifndef LINBPQ +#include "kernelresource.h" +LRESULT CALLBACK ResWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); +#endif + +//#define s_addr S_un.S_addr + +extern BPQVECSTRUC * IPHOSTVECTORPTR; + +BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port); +VOID SENDSABM(struct _LINKTABLE * LINK); +BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK); +BOOL ProcessConfig(); +VOID RemoveARP(PARPDATA Arp); +VOID AddToRoutes(PARPDATA Arp, UINT IPAddr, char Type); + +VOID ProcessTunnelMsg(PIPMSG IPptr); +VOID ProcessRIP44Message(PIPMSG IPptr); +PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found); +BOOL ProcessROUTELine(char * buf, BOOL Locked); +VOID DoRouteTimer(); +PROUTEENTRY FindRoute(uint32_t IPADDR); +VOID SendIPtoEncap(PIPMSG IPptr, uint32_t Encap); +USHORT Generate_CHECKSUM(VOID * ptr1, int Len); +VOID RecalcTCPChecksum(PIPMSG IPptr); +VOID RecalcUDPChecksum(PIPMSG IPptr); +BOOL Send_ETH(VOID * Block, DWORD len, BOOL SendToTAP); +VOID ProcessEthARPMsg(PETHARP arpptr, BOOL FromTAP); +VOID WriteIPRLine(PROUTEENTRY RouteRecord, FILE * file); +int CountBits(uint32_t in); +VOID SendARPMsg(PARPDATA ARPptr, BOOL ToTAP);; +BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls); +int C_Q_ADD_NP(VOID *PQ, VOID *PBUFF); + +#define ARPTIMEOUT 3600 + +// ARP REQUEST (AX.25) + +AXARP AXARPREQMSG = {0}; + +// ARP REQUEST/REPLY (Eth) + +ETHARP ETHARPREQMSG = {0}; + +ARPDATA ** ARPRecords = NULL; // ARP Table - malloc'ed as needed + +int NumberofARPEntries = 0; + +ROUTEENTRY ** RouteRecords = NULL; + +int NumberofRoutes = 0; + +time_t LastRIP44Msg = 0; + +//HANDLE hBPQNET = INVALID_HANDLE_VALUE; + +uint32_t OurIPAddr = 0; +char IPAddrText[20]; // Text form of Our Address + +uint32_t EncapAddr = INADDR_NONE; // Virtual Host for IPIP PCAP Mode. +char EncapAddrText[20]; // Text form of Our Address + +UCHAR RouterMac[6] = {0}; // Mac Address of our Internet Gateway. + +//uint32_t HostIPAddr = INADDR_NONE; // Makes more sense to use same addr for host + +uint32_t HostNATAddr = INADDR_NONE; // LAN address (not 44 net) of our host +char HostNATAddrText[20]; + +uint32_t OurNetMask = 0xffffffff; + +BOOL WantTAP = FALSE; +BOOL WantEncap = 0; // Run RIP44 and Net44 Encap +BOOL NoDefaultRoute = FALSE; // Don't add route to 44/8 + +int WantUDPTunnel = 0; + +SOCKET EncapSock = 0; +SOCKET UDPSendSock = 0; + +BOOL UDPEncap = FALSE; + +BOOL IPv6 = FALSE; +int UDPPort = 4473; // RX Port, Send on +1 + +BOOL BPQSNMP = FALSE; // If set process SNMP in BPQ, else pass to host + +int IPTTL = 128; + +int tap_fd = 0; + +int FramesForwarded = 0; +int FramesDropped = 0; +int ARPTimeouts = 0; +int SecTimer = 10; + +int baseline=0; + +unsigned char hostaddr[64]; + +static int nat_table_len = 0; + +static struct nat_table_entry nat_table[MAX_ENTRIES]; + + +uint32_t UCSD44 = 0; // RIP SOurce - Normally 44.0.0.1 + +// Following two fields used by stats to get round shared memmory problem + +ARPDATA Arp={0}; +int ARPFlag = -1; + +// Following Buffer is used for msgs from WinPcap. Put the Enet message part way down the buffer, +// so there is room for ax.25 header instead of Enet header when we route the frame to ax.25 +// Enet Header ia 14 bytes, AX.25 UI is 16 + +// Also used to reassemble NOS Fragmented ax.25 packets + +static UCHAR Buffer[4096] = {0}; + +#define EthOffset 30 // Should be plenty + +DWORD IPLen = 0; + +UCHAR QST[7]={'Q'+'Q','S'+'S','T'+'T',0x40,0x40,0x40,0xe0}; //QST IN AX25 + +#ifdef WIN32 +UCHAR ourMACAddr[6] = {02,'B','P','Q',2,2}; +#else +UCHAR ourMACAddr[6] = {02,'B','P','Q',1,1}; +#endif + +UCHAR RealMacAddress[6]; + +uint64_t IPPortMask = 0; + +IPSTATS IPStats = {0}; + +UCHAR BPQDirectory[260]; + +char ARPFN[MAX_PATH]; +char IPRFN[MAX_PATH]; + +HANDLE handle; + +//#ifdef WIN32 +pcap_t *adhandle = 0; +pcap_t * (FAR * pcap_open_livex)(const char *, int, int, int, char *); + +int pcap_reopen_delay; +//#endif + +static char Adapter[256]; + +int Promiscuous = 1; // Default to Promiscuous + +#ifdef WIN32 + +HINSTANCE PcapDriver=0; + +typedef void * (FAR *FARPROCX)(); +typedef int (FAR *FARPROCI)(); // Routines that return int + +int (FAR * pcap_sendpacketx)(); + +FARPROCI pcap_findalldevsx; + +FARPROCX pcap_compilex; +FARPROCX pcap_setfilterx; +FARPROCI pcap_datalinkx; +FARPROCI pcap_next_exx; +FARPROCX pcap_geterrx; +FARPROCX pcap_closex; +FARPROCX pcap_setnonblockx; + + +char Dllname[6]="wpcap"; + +FARPROCX GetAddress(char * Proc); +FARPROCI GetAddressI(char * Proc); +#else +#define pcap_findalldevsx pcap_findalldevs +#define pcap_compilex pcap_compile +#define pcap_open_livex pcap_open_live +#define pcap_setfilterx pcap_setfilter +#define pcap_datalinkx pcap_datalink +#define pcap_next_exx pcap_next_ex +#define pcap_geterrx pcap_geterr +#define pcap_sendpacketx pcap_sendpacket +#define pcap_closex pcap_close +#define pcap_setnonblockx pcap_setnonblock +#endif + +VOID __cdecl Debugprintf(const char * format, ...); + +// Out Address Space (now only have lower 3/4 of 44 net) + +DWORD Lower44; // 44.0 to 44.127 +DWORD Upper44 ; // 44.128 to 44.191 +DWORD Lower44Mask; +DWORD Upper44Mask; + + +#ifdef WIN32 + +// Routine to check if a route to 44.0.0.0/9 exists and points to us + +BOOL Check44Route(int Interface) +{ + PMIB_IPFORWARDTABLE pIpForwardTable = NULL; + PMIB_IPFORWARDROW Row; + int Size = 0; + DWORD n; + int gotLower = 0; + int gotUpper = 0; + + // First call gets the required size + + n = GetIpForwardTable(pIpForwardTable, &Size, FALSE); + + pIpForwardTable = malloc(Size); + + n = GetIpForwardTable(pIpForwardTable, &Size, FALSE); + + if (n) + return FALSE; // Couldnt read table + + Row = pIpForwardTable->table; + + for (n = 0; n < pIpForwardTable->dwNumEntries; n++) + { + if (Row->dwForwardIfIndex == Interface) + { + if (Row->dwForwardDest == Lower44 && Row->dwForwardMask == Lower44Mask) + gotLower = 1; + + if (Row->dwForwardDest == Upper44 && Row->dwForwardMask == Upper44Mask) + gotUpper = 1; + } + Row++; + } + + free(pIpForwardTable); + + if (gotLower & gotUpper) + return TRUE; + + return FALSE; +} + +BOOL Setup44Route(int Interface, char * Gateway) +{ + // Better just to call route.exe, so we can set -p flag and use runas + + char Params[256]; + + // delete old 44/8 route + + sprintf(Params, " -delete 44.0.0.0/8"); + ShellExecute(NULL, "runas", "c:\\windows\\system32\\route.exe", Params, NULL, SW_SHOWNORMAL); + + sprintf(Params, " -p add 44.0.0.0 mask 255.128.0.0 %s if %d", Gateway, Interface); + ShellExecute(NULL, "runas", "c:\\windows\\system32\\route.exe", Params, NULL, SW_SHOWNORMAL); + + sprintf(Params, " -p add 44.128.0.0 mask 255.192.0.0 %s if %d", Gateway, Interface); + ShellExecute(NULL, "runas", "c:\\windows\\system32\\route.exe", Params, NULL, SW_SHOWNORMAL); + + return 1; +} + +#endif + +char FormatIPWork[20]; + +char * FormatIP(uint32_t Addr) +{ + unsigned char work[4]; + + memcpy(work, &Addr, 4); + sprintf(FormatIPWork, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + return FormatIPWork; +} + +int CompareIPRoutes (const VOID * a, const VOID * b) +{ + PROUTEENTRY x; + PROUTEENTRY y; + + unsigned long r1, r2; + + x = * (PROUTEENTRY const *) a; + y = * (PROUTEENTRY const *) b; + + r1 = x->NETWORK; + r2 = y->NETWORK; + + r1 = htonl(r1); + r2 = htonl(r2); + + if (r1 < r2 ) return -1; + if (r1 == r2 ) return 0; + return 1; +} + + +int CompareMasks (const VOID * a, const VOID * b) +{ + PROUTEENTRY x; + PROUTEENTRY y; + + uint32_t m1, m2; + uint32_t r1, r2; + + x = * (PROUTEENTRY const *) a; + y = * (PROUTEENTRY const *) b; + + r1 = x->NETWORK; + r2 = y->NETWORK; + + m1 = x->SUBNET; + m2 = y->SUBNET; + + m1 = htonl(m1); + m2 = htonl(m2); + + r1 = htonl(r1); + r2 = htonl(r2); + + if (m1 > m2) return -1; + if (m1 == m2) + { + if (r1 < r2) return -1; + if (r1 == r2 ) return 0; + } + return 1; +} + + + +void OpenTAP(); + +BOOL GetPCAP() +{ +#ifdef WIN32 + + PcapDriver=LoadLibrary(Dllname); + + if (PcapDriver == NULL) return(FALSE); + + if ((pcap_findalldevsx=GetAddressI("pcap_findalldevs")) == 0 ) return FALSE; + + if ((pcap_sendpacketx = GetAddressI("pcap_sendpacket")) == 0 ) return FALSE; + + if ((pcap_datalinkx=GetAddressI("pcap_datalink")) == 0 ) return FALSE; + + if ((pcap_compilex=GetAddress("pcap_compile")) == 0 ) return FALSE; + + if ((pcap_setfilterx=GetAddress("pcap_setfilter")) == 0 ) return FALSE; + + pcap_open_livex = (pcap_t * (__cdecl *)(const char *, int, int, int, char *)) GetProcAddress(PcapDriver,"pcap_open_live"); + + if (pcap_open_livex == NULL) return FALSE; + + if ((pcap_geterrx = GetAddress("pcap_geterr")) == 0 ) return FALSE; + + if ((pcap_next_exx = GetAddressI("pcap_next_ex")) == 0 ) return FALSE; + + if ((pcap_closex = GetAddress("pcap_close")) == 0 ) return FALSE; + + if ((pcap_setnonblockx = GetAddress("pcap_setnonblock")) == 0 ) return FALSE; + +#endif + return TRUE; +} +Dll BOOL APIENTRY Init_IP() +{ + int ret; + + if (BPQDirectory[0] == 0) + { + strcpy(ARPFN,"BPQARP.dat"); + } + else + { + strcpy(ARPFN,BPQDirectory); + strcat(ARPFN,"/"); + strcat(ARPFN,"BPQARP.dat"); + } + + if (BPQDirectory[0] == 0) + { + strcpy(IPRFN,"BPQIPR.dat"); + } + else + { + strcpy(IPRFN,BPQDirectory); + strcat(IPRFN,"/"); + strcat(IPRFN,"BPQIPR.dat"); + } + + Lower44 = inet_addr("44.0.0.0"); + Upper44 = inet_addr("44.128.0.0"); // Loer Half + Lower44Mask = inet_addr("255.128.0.0"); // 44.128 to 44.192 + Upper44Mask = inet_addr("255.192.0.0"); + + + + // Clear fields in case of restart + + ARPRecords = NULL; // ARP Table - malloc'ed as needed + NumberofARPEntries=0; + + RouteRecords = NULL; + NumberofRoutes = 0; + + nat_table_len = 0; + + ReadConfigFile(); + + ourMACAddr[5] = (UCHAR)(OurIPAddr >> 24) & 255; + + // Clear old packets + + IPHOSTVECTORPTR->HOSTAPPLFLAGS = 0x80; // Request IP frames from Node + + // Set up static fields in ARP messages + + AXARPREQMSG.HWTYPE=0x0300; // AX25 + memcpy(AXARPREQMSG.MSGHDDR.DEST, QST, 7); + memcpy(AXARPREQMSG.MSGHDDR.ORIGIN, MYCALL, 7); + AXARPREQMSG.MSGHDDR.ORIGIN[6] |= 1; // Set End of Call + AXARPREQMSG.MSGHDDR.PID = 0xcd; // ARP + AXARPREQMSG.MSGHDDR.CTL = 03; // UI + + AXARPREQMSG.PID=0xcc00; // TYPE + AXARPREQMSG.HWTYPE=0x0300; + AXARPREQMSG.HWADDRLEN = 7; + AXARPREQMSG.IPADDRLEN = 4; + + memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7); + AXARPREQMSG.SENDIPADDR = OurIPAddr; + + memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6); + memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); + ETHARPREQMSG.MSGHDDR.ETYPE = 0x0608; // ARP + + ETHARPREQMSG.HWTYPE=0x0100; // Eth + ETHARPREQMSG.PID=0x0008; + ETHARPREQMSG.HWADDRLEN = 6; + ETHARPREQMSG.IPADDRLEN = 4; + +//#ifdef WIN32 + + if (Adapter[0] == 0) + return FALSE; + + if (GetPCAP() == FALSE) + return FALSE; + + // on Windows create a NAT entry for IPADDR. + // on linux enable the TAP device (on Linux you can't use pcap to talk to + // the local host, whereas on Windows you can. + +#ifndef MACBPQ +#ifndef FREEBSD + { + pcap_if_t * ifs = NULL, * saveifs; + char Line[80]; + char ErrBuf[256]; + + // Find IP Addr of Adapter Interface + + pcap_findalldevsx(&ifs, ErrBuf); + + saveifs = ifs; // Save for release + + while (ifs) + { + if (strcmp(ifs->name, Adapter) == 0) + break; + + ifs = ifs->next; + } + + if (ifs) + { + struct pcap_addr *address; + + address = ifs->addresses; + + while (address) + { + if (address->addr->sa_family == 2) + break; + + address = address->next; + } + + if (address) + { + memcpy(&HostNATAddr, &address->addr->sa_data[2], 4); + + sprintf(HostNATAddrText, "%d.%d.%d.%d", (UCHAR)address->addr->sa_data[2], + (UCHAR)address->addr->sa_data[3], + (UCHAR)address->addr->sa_data[4], + (UCHAR)address->addr->sa_data[5]); + } + + // We need to create a NAT entry. + + // For now do for both Windows and Linux + + sprintf(Line, "NAT %s %s", IPAddrText, HostNATAddrText); + Debugprintf("Generated NAT %s\n", Line); + ProcessLine(Line); +#ifdef WIN32 +#else + // Linux, need TAP + + WantTAP = TRUE; + +#endif + } + } + +#endif +#endif + // + // Open PCAP Driver + + if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch + { + char buf[80]; + + if (OpenPCAP()) + sprintf(buf,"IP Using %s\n", Adapter); + else + sprintf(buf," IP Unable to open %s\n", Adapter); + + WritetoConsoleLocal(buf); + + if (adhandle == NULL) + { + WritetoConsoleLocal("Failed to open pcap device - IP Support Disabled\n"); + return FALSE; + } + } + +//#else + + // Linux - if TAP requested, open it + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD + + if (WantTAP) + OpenTAP(); + +#endif +#endif +#endif + + + ReadARP(); + ReadIPRoutes(); + + // if we are running as Net44 encap, open a socket for IPIP + + if (WantUDPTunnel) + { + struct sockaddr_in sinx; + u_long param = 1; + + UDPSendSock = socket(AF_INET,SOCK_DGRAM,0); + + ioctl (UDPSendSock,FIONBIO,¶m); + + sinx.sin_family = AF_INET; + sinx.sin_addr.s_addr = INADDR_ANY; + sinx.sin_port = htons(WantUDPTunnel); + + ret = bind(UDPSendSock, (struct sockaddr *) &sinx, sizeof(sinx)); + + + } + + if (WantEncap || WantUDPTunnel) + { + union + { + struct sockaddr_in sinx; + struct sockaddr_in6 sinx6; + } sinx = {0}; + u_long param = 1; + int err, ret; + char Msg[80]; + + if (UCSD44 == 0) + UCSD44 = inet_addr("44.0.0.1"); + +#ifdef WIN32 + + // Find Interface number for PCAP Device + + { + UINT ulOutBufLen; + PIP_ADAPTER_INFO pAdapterInfo; + PIP_ADAPTER_INFO pAdapter = NULL; + DWORD dwRetVal = 0; + int Interface = 0; + + // Make an initial call to GetAdaptersInfo to get + // the necessary size into the ulOutBufLen variable + + GetAdaptersInfo(NULL, &ulOutBufLen); + + pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen); + + if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) + { + pAdapter = pAdapterInfo; + while (pAdapter) + { + if (strstr(Adapter, pAdapter->AdapterName)) + { + Interface = pAdapter->Index; + break; + } + pAdapter = pAdapter->Next; + } + free(pAdapterInfo); + } + if (NoDefaultRoute == FALSE) + { + // Check Route to 44 and if not there add + + if (Check44Route(Interface)) + WritetoConsoleLocal("Route to 44/9 and 44.128/10 found\n"); + else + { +#ifndef _winver +#define _winver 0x0600 +#pragma warning(push) +#pragma warning(disable : 4996) + if (_winver >= 0x0600) +#pragma warning(pop) + Setup44Route(Interface, "0.0.0.0"); + else + Setup44Route(Interface, EncapAddrText); +#else + Setup44Route(Interface, "0.0.0.0"); +#endif + Sleep(2000); + if (Check44Route(Interface)) + WritetoConsoleLocal("Route to44/9 and 44.128/10 added\n"); + else + WritetoConsoleLocal("Adding route to 44/9 and 44.128/10 Failed\n"); + } + } + } +#endif + + if (WantEncap) + { + + if (EncapAddr != INADDR_NONE) + { + // Using Virtual Host on PCAP Adapter (Windows) + + WritetoConsoleLocal("Net44 Tunnel opened on PCAP device\n"); + WritetoConsoleLocal("IP Support Enabled\n"); + return TRUE; + } + + if (UDPEncap) + { + // Open UDP Socket + + if (IPv6) + EncapSock = socket(AF_INET6,SOCK_DGRAM,0); + else + EncapSock = socket(AF_INET,SOCK_DGRAM,0); + + sinx.sinx.sin_port = htons(UDPPort); + } + else + { + // Open Raw Socket + + EncapSock = socket(AF_INET, SOCK_RAW, 4); + sinx.sinx.sin_port = 0; + } + + if (EncapSock == INVALID_SOCKET) + { + err = WSAGetLastError(); + sprintf(Msg, "Failed to create socket for IPIP Encap - error code = %d\n", err); + WritetoConsoleLocal(Msg); + } + else + { + + ioctl (EncapSock,FIONBIO,¶m); + + if (IPv6) + { + sinx.sinx.sin_family = AF_INET6; + memset (&sinx.sinx6.sin6_addr, 0, 16); + ret = bind(EncapSock, (struct sockaddr *) &sinx.sinx, sizeof(sinx.sinx6)); + } + else + { + sinx.sinx.sin_family = AF_INET; + sinx.sinx.sin_addr.s_addr = INADDR_ANY; + ret = bind(EncapSock, (struct sockaddr *) &sinx.sinx, sizeof(sinx.sinx)); + } + + if (ret) + { + // Bind Failed + + err = WSAGetLastError(); + sprintf(Msg, "Bind Failed for IPIP Encap socket - error code = %d\n", err); + WritetoConsoleLocal(Msg); + } + else + { + WritetoConsoleLocal("Net44 Tunnel opened\n"); + } + } + } + } + WritetoConsoleLocal("IP Support Enabled\n"); + + return TRUE; + +} + +VOID IPClose() +{ + SaveIPRoutes(); + + if (adhandle) + pcap_closex(adhandle); + +#ifdef LINBPQ + if (WantTAP && tap_fd) + close(tap_fd); +#endif + + if (EncapSock) + closesocket(EncapSock); +} + +union +{ + struct sockaddr_in rxaddr; + struct sockaddr_in6 rxaddr6; +} RXaddr; + + +Dll BOOL APIENTRY Poll_IP() +{ + int res; + struct pcap_pkthdr *header; + const u_char *pkt_data; + + // Entered every 100 mS + + // if ARPFlag set, copy requested ARP record (For BPQStatus GUI) + + if (ARPFlag != -1) + { + memcpy(&Arp, ARPRecords[ARPFlag], sizeof (ARPDATA)); + ARPFlag = -1; + } + + SecTimer--; + + if (SecTimer == 0) + { + SecTimer = 10; + DoARPTimer(); + DoRouteTimer(); + } + +Pollloop: + +//#ifdef WIN32 + + if (adhandle) + { + res = (int)pcap_next_exx(adhandle, &header, &pkt_data); + + if (res > 0) + { + PETHMSG ethptr = (PETHMSG)&Buffer[EthOffset]; + + if (header->len > 1514) + { +// Debugprintf("Ether Packet Len = %d", header->len); + goto Pollloop; + } + + memcpy(&Buffer[EthOffset],pkt_data, header->len); + + if (ethptr->ETYPE == 0x0008) + { + ProcessEthIPMsg((PETHMSG)&Buffer[EthOffset]); + // PIPMSG ipptr = (PIPMSG)&Buffer[EthOffset+14]; + // ProcessIPMsg(ipptr, ethptr->SOURCE, 'E', 255); + goto Pollloop; + } + + if (ethptr->ETYPE == 0x0608) + { + ProcessEthARPMsg((PETHARP)ethptr, FALSE); + goto Pollloop; + } + + // Ignore anything else + + goto Pollloop; + } + else + { + if (res < 0) + { + char * error = (char *)pcap_geterrx(adhandle) ; + Debugprintf(error); + if (OpenPCAP() == FALSE) + pcap_reopen_delay = 300; + } + } + } + else + { + // No handle. + + if (Adapter[0]) // Don't have to have ethernet, if used just as ip over ax.25 switch + { + // Try reopening periodically + + pcap_reopen_delay --; + + if (pcap_reopen_delay < 0) + if (OpenPCAP() == FALSE) + pcap_reopen_delay = 300; // Retry every 30 seconds + } + } + +//#endif + +#ifdef LINBPQ + +PollTAPloop: + + if (WantTAP && tap_fd) + { + int nread; + + nread = read(tap_fd, &Buffer[EthOffset], 1600); + + if (nread > 0) + { + PETHMSG ethptr = (PETHMSG)&Buffer[EthOffset]; + + if (ethptr->ETYPE == 0x0008) + { + ProcessEthIPMsg((PETHMSG)&Buffer[EthOffset]); + goto PollTAPloop; + } + + if (ethptr->ETYPE == 0x0608) + { + ProcessEthARPMsg((PETHARP)ethptr, TRUE); + goto PollTAPloop; + } + + // if 08FF pass to BPQETHER Driver +/* + if (ethptr->ETYPE == 0xFF08) + { + PBUFFHEADER axmsg; + PBUFFHEADER savemsg; + int len; + + // BPQEther Encap + + len = Buffer[EthOffset + 15]*256 + Buffer[EthOffset + 14]; + + axmsg = (PBUFFHEADER)&Buffer[EthOffset + 9]; + axmsg->LENGTH = len; + axmsg->PORT = 99; // Dummy for IP Gate + + printf("BPQ Eth Len %d PID %d\n", len, axmsg->PID); + + if ((len < 16) || (len > 320)) + goto PollTAPloop; // Probably RLI Mode Frame + + + //len-=3; + + //memcpy(&buff[7],&pkt_data[16],len); + + // len+=5; + + savemsg=axmsg; + + // Packet from AX.25 + + if (CompareCalls(axmsg->ORIGIN, MYCALL)) + return 0; // Echoed packet + + switch (axmsg->PID) + { + case 0xcc: + + // IP Message + + { + PIPMSG ipptr = (PIPMSG)++axmsg; + axmsg--; + ProcessIPMsg(ipptr, axmsg->ORIGIN, (axmsg->CTL == 3) ? 'D' : 'V', axmsg->PORT); + break; + } + + case 0xcd: + + // ARP Message + + ProcessAXARPMsg((PAXARP)axmsg); + SaveARP(); + break; + + // case 0x08: + } + + goto PollTAPloop; + } +*/ + // Ignore anything else + +// printf("TAP ype %X\n", ntohs(ethptr->ETYPE)); + + goto PollTAPloop; + } + } + +#endif + +PollEncaploop: + + if (EncapSock) + { + int nread; + int addrlen = sizeof(struct sockaddr_in6); + + nread = recvfrom(EncapSock, &Buffer[EthOffset], 1600, 0, (struct sockaddr *)&RXaddr.rxaddr,&addrlen); + + if (nread > 0) + { + PIPMSG IPptr = (PIPMSG)&Buffer[EthOffset]; + + if (IPptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet + { + ProcessTunnelMsg(IPptr); + } + + goto PollEncaploop; + } + else + res = GetLastError(); + } + + if (UDPSendSock) + { + int nread; + int addrlen = sizeof(struct sockaddr_in6); + + nread = recvfrom(UDPSendSock, &Buffer[EthOffset], 1600, 0, (struct sockaddr *)&RXaddr.rxaddr,&addrlen); + + if (nread > 0) + { + PIPMSG IPptr = (PIPMSG)&Buffer[EthOffset]; + + // RouteIPMsg(IPptr); + ProcessIPMsg(IPptr, Buffer, 'E', 255); + + goto PollEncaploop; + } + else + res = GetLastError(); + } + + + + + if (IPHOSTVECTORPTR->HOSTTRACEQ != 0) + { + PBUFFHEADER axmsg; + PBUFFHEADER savemsg; + + axmsg = (PBUFFHEADER)IPHOSTVECTORPTR->HOSTTRACEQ; + + IPHOSTVECTORPTR->HOSTTRACEQ = axmsg->CHAIN; + + savemsg=axmsg; + + // Packet from AX.25 + + if (axmsg->DEST[0] == 0xCF) // Netrom + { + // Could we use the More bit for fragmentation?? + // Seems a good idea, but would'nt be compatible with NOS + + // Have to move message down buffer + + UCHAR TEMP[7]; + + memmove(TEMP, &axmsg->DEST[1], 7); + memmove(axmsg->DEST, &axmsg->ORIGIN[1], 7); + memmove(axmsg->ORIGIN, TEMP, 7); + axmsg->PID = 0xCC; + + memmove(&axmsg->PID + 1, &axmsg->PID + 6, axmsg->LENGTH); + axmsg->PORT = 0; + } + + if (CompareCalls(axmsg->ORIGIN, MYCALL)) + { + ReleaseBuffer(axmsg); + return 0; // Echoed packet + } + + switch (axmsg->PID) + { + case 0xcc: + + // IP Message, + + { + PIPMSG ipptr = (PIPMSG)++axmsg; + axmsg--; + ProcessIPMsg(ipptr, axmsg->ORIGIN, (axmsg->CTL == 3) ? 'D' : 'V', axmsg->PORT); + break; + } + + case 0xcd: + + // ARP Message + + ProcessAXARPMsg((PAXARP)axmsg); + SaveARP(); + break; + + case 0x08: + + // Fragmented message + + // The L2 code ensures that the last fragment is present before passing the + // message up to us. It is just possible that bits are missing + { + UCHAR * ptr = &axmsg->PID; + UCHAR * nextfrag; + + int frags; + int len; + + ptr++; + + if (!(*ptr & 0x80)) + break; // Not first fragment??? + + frags=*ptr++ & 0x7f; + + len = axmsg->LENGTH; + + len-= sizeof(BUFFHEADER); + len--; // Remove Frag Control Byte + + memcpy(&Buffer[EthOffset], ptr, len); + + nextfrag = &Buffer[EthOffset]+len; + + // Release Buffer +fragloop: + ReleaseBuffer(savemsg); + + if (IPHOSTVECTORPTR->HOSTTRACEQ == 0) goto Pollloop; // Shouldn't happen + + axmsg = (PBUFFHEADER)IPHOSTVECTORPTR->HOSTTRACEQ; + IPHOSTVECTORPTR->HOSTTRACEQ = axmsg->CHAIN; + savemsg=axmsg; + + ptr = &axmsg->PID; + ptr++; + + if (--frags != (*ptr++ & 0x7f)) + break; // Out of sequence + + len = axmsg->LENGTH; + + len-= sizeof(BUFFHEADER); + len--; // Remove Frag Control Byte + + memcpy(nextfrag, ptr, len); + + nextfrag+=len; + + if (frags != 0) goto fragloop; + + ProcessIPMsg((PIPMSG)&Buffer[EthOffset+1],axmsg->ORIGIN, (axmsg->CTL == 3) ? 'D' : 'V', axmsg->PORT); + + break; + } + + } + // Release the buffer + + ReleaseBuffer(savemsg); + + goto Pollloop; + } + return TRUE; +} + + +BOOL Send_ETH(VOID * Block, DWORD len, BOOL SendtoTAP) +{ + // On Windows we don't use TAP so everything goes to pcap + + if (len < 60) len = 60; + +#ifndef WIN32 + if (SendtoTAP) + { + if (tap_fd) + write(tap_fd, Block, len); + + return TRUE; + } +#endif + + // On Windows we don't use TAP so everything goes to pcap + + if (adhandle) + { + // Send down the packet + + pcap_sendpacketx(adhandle, // Adapter + Block, // buffer with the packet + len); // size + } + return TRUE; +} + +#define AX25_P_SEGMENT 0x08 +#define SEG_REM 0x7F +#define SEG_FIRST 0x80 + +static VOID Send_AX_Datagram(PMESSAGE Block, DWORD Len, UCHAR Port, UCHAR * HWADDR) +{ + // Can't use API SENDRAW, as that tries to get the semaphore, which we already have + + // Block includes the Msg Header (7 bytes), Len Does not! + + memcpy(Block->DEST, HWADDR, 7); + memcpy(Block->ORIGIN, MYCALL, 7); + Block->DEST[6] &= 0x7e; // Clear End of Call + Block->DEST[6] |= 0x80; // set Command Bit + + Block->ORIGIN[6] |= 1; // Set End of Call + Block->CTL = 3; //UI + +#ifdef LINBPQ + + if (Port == 99) // BPQETHER over BPQTUN Port + { + // Add BPQETHER Header + + int txlen = Block->LENGTH; + UCHAR txbuff[1600]; + + if (txlen < 1 || txlen > 400) + return; + + // Length field is little-endian + + // BPQEther Header is 14 bytes before the Length + + txbuff[14]=(txlen & 0xff); + txbuff[15]=(txlen >> 8); + + + memcpy(&txbuff[16],&Block[7],txlen); + + //memcpy(&txbuff[0],&EthDest[0],6); + //memcpy(&txbuff[6],&EthSource[0],6); + //memcpy(&txbuff[12],&EtherType,2); + + write(tap_fd, Block, Len); + return; + } + +#endif + + Send_AX(Block, Len, Port); + return; + +} +VOID Send_AX_Connected(VOID * Block, DWORD Len, UCHAR Port, UCHAR * HWADDR) +{ + DWORD PACLEN = 256; + int first = 1, fragno, txlen; + UCHAR * p; + + // Len includes the 16 byte ax header (Addr CTL PID) + + // if Len - 16 is greater than PACLEN, then fragment (only possible on Virtual Circuits, + // as fragmentation relies upon reliable delivery + + + if ((Len - 16) <= PACLEN) // No need to fragment + { + SendNetFrame(HWADDR, MYCALL, Block, Len, Port); + return; + } + + Len = Len-16; // Back to real length + + PACLEN-=2; // Allow for fragment control info) + + fragno = Len / PACLEN; + + if (Len % PACLEN == 0) fragno--; + + p = Block; + p += 20; + + while (Len > 0) + { + *p++ = AX25_P_SEGMENT; + + *p = fragno--; + + if (first) + *p |= SEG_FIRST; + + txlen = (PACLEN > Len) ? Len : PACLEN; + + Debugprintf("Send IP to VC Fragment, Len = %d", txlen); + + // Sobsequent fragments only add one byte (the PID is left in place) + + if (first) + SendNetFrame(HWADDR, MYCALL, p-23, txlen+18, Port); + else + { + SendNetFrame(HWADDR, MYCALL, p-23, txlen+17, Port); // only one frag byte + p--; + } + first = 0; + p += (txlen); + Len -= txlen; + } + return; +} + + +static VOID SendNetFrame(UCHAR * ToCall, UCHAR * FromCall, UCHAR * Block, DWORD Len, UCHAR Port) +{ +// ATTACH FRAME TO OUTBOUND L3 QUEUES (ONLY USED FOR IP ROUTER) + + struct DATAMESSAGE * buffptr; + struct _LINKTABLE * LINK; + struct PORTCONTROL * PORT; + + if (Len > MAXDATA) + return; + + if (QCOUNT < 100) + return; + + memcpy(&Block[7],ToCall, 7); + memcpy(&Block[14],FromCall, 7); + + buffptr = GetBuff(); + + if (buffptr == 0) + return; // No buffers + + Len -= 15; // We added 16 before (for UI Header) but L2 send includes the PID, so is one more than datalength + + buffptr->LENGTH = (USHORT)Len + MSGHDDRLEN; + +// SEE IF L2 OR L3 + + if (Port == 0) // L3 + { + struct DEST_LIST * DEST; + L3MESSAGE * L3Header; + + if (FindDestination(ToCall, &DEST) == 0) + { + ReleaseBuffer(buffptr); + return; + } + + // We have to build the Netrom Header + + L3Header = (L3MESSAGE *)&buffptr->L2DATA[0]; + + buffptr->PID = 0xCF; // NETROM + + memcpy(L3Header->L3SRCE, FromCall, 7); + memcpy(L3Header->L3DEST, ToCall, 7); + L3Header->L3TTL = L3LIVES; + L3Header->L4FLAGS = 0; // Opcode + L3Header->L4ID = 0x0C; // IP + L3Header->L4INDEX = 0x0C; + + memcpy(L3Header->L4DATA, &Block[23], Len); // Dond Send PID - implied by OCOC above + + buffptr->LENGTH += 20; // Netrom Header + + C_Q_ADD(&DEST->DEST_Q, buffptr); + return; + } + +// SEND FRAME TO L2 DEST, CREATING A LINK ENTRY IF NECESSARY + + memcpy(&buffptr->PID, &Block[22], Len); + + if (FindLink(ToCall, FromCall, Port, &LINK)) + { + // Have a link + + C_Q_ADD(&LINK->TX_Q, buffptr); + return; + } + + if (LINK == NULL) // No spare space + { + ReleaseBuffer(buffptr); + return; + } + + LINK->LINKPORT = PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + return; // maybe port has been deleted + + LINK->L2TIME = PORT->PORTT1; // SET TIMER VALUE + + if (ToCall[7]) + LINK->L2TIME += PORT->PORTT1; // Extend Timer for Digis + + LINK->LINKWINDOW = PORT->PORTWINDOW; + + LINK->L2STATE = 2; + + memcpy(LINK->LINKCALL, ToCall, 7); + memcpy(LINK->OURCALL, FromCall, 7); + memcpy(LINK->DIGIS, &ToCall[7], 56); + + LINK->LINKTYPE = 2; // Dopwnlink + + SENDSABM(LINK); + + C_Q_ADD(&LINK->TX_Q, buffptr); + return; +} + +VOID ProcessEthIPMsg(PETHMSG Buffer) +{ + PIPMSG ipptr = (PIPMSG)&Buffer[1]; + struct nat_table_entry * NAT = NULL; + int index; + + if (memcmp(Buffer, ourMACAddr,6 ) != 0) + return; // Not for us + + if (memcmp(&Buffer[6], ourMACAddr,6 ) == 0) + return; // Discard our sends + + // See if from a NAT'ed address + + for (index=0; index < nat_table_len; index++) + { + NAT = &nat_table[index]; + + if (NAT->mappedipaddr == ipptr->IPSOURCE.addr) + { + ipptr->IPSOURCE.addr = NAT->origipaddr; + + ipptr->IPCHECKSUM = 0; // to force cksum recalc below + break; + } + } + + // if Checkum offload is active we get the packet before the NIC sees it (from PCAP) + + if (ipptr->IPCHECKSUM == 0) // Windows seems to do this + { + // Generate IP and TCP/UDP checksums + + int Len = ntohs(ipptr->IPLENGTH); + + if (Len == 0) + { + return; + } + Len-=20; + + ipptr->IPCHECKSUM = Generate_CHECKSUM(ipptr, 20); + + if (ipptr->IPPROTOCOL == 6) // TCP + { + PTCPMSG TCP = (PTCPMSG)&ipptr->Data; + PHEADER PH = {0}; + + PH.IPPROTOCOL = 6; + PH.LENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &ipptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &ipptr->IPDEST, 4); + + TCP->CHECKSUM = ~Generate_CHECKSUM(&PH, 12); + TCP->CHECKSUM = Generate_CHECKSUM(TCP, Len); + } + } + + if (ipptr->IPDEST.addr == EncapAddr) + { + if (ipptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet + { + memcpy(RouterMac, Buffer->SOURCE, 6); + ProcessTunnelMsg(ipptr); + } + return; // Ignore Others + } + + ProcessIPMsg(ipptr, Buffer->SOURCE, 'E', 255); +} + +VOID ProcessEthARPMsg(PETHARP arpptr, BOOL FromTAP) +{ + int i=0; + uint64_t Mask=IPPortMask; + PARPDATA Arp; + PROUTEENTRY Route; + BOOL Found; + + if (memcmp(&arpptr->MSGHDDR.SOURCE, ourMACAddr,6 ) == 0 ) + return; // Discard our sends + + switch (arpptr->ARPOPCODE) + { + case 0x0100: + + // Is it for our ENCAP (not on our 44 LAN) + +// printf("ARP Request for %08x Tell %08x\n", +// arpptr->TARGETIPADDR, arpptr->SENDIPADDR); + + // Process anything for 44 from TAP + // (or should the be from either..???) + +// if (FromTAP && (arpptr->TARGETIPADDR & 0xff) == 44) + if ((arpptr->TARGETIPADDR & 0xff) == 44) + goto ARPOk; + + if (arpptr->TARGETIPADDR != EncapAddr) + { + // We should only accept requests from our subnet - we might have more than one net on iterface + + if ((arpptr->SENDIPADDR & OurNetMask) != (OurIPAddr & OurNetMask)) + { + // Discard Unless it is from a NAT'ed Host + + struct nat_table_entry * NAT = NULL; + int index; + + for (index=0; index < nat_table_len; index++) + { + NAT = &nat_table[index]; + + if (NAT->mappedipaddr == arpptr->SENDIPADDR) + break; + } + + if (index >= nat_table_len) + return; + + // Also check it is for a 44. address or we send all LAN ARPS to + // RF + + if ((arpptr->TARGETIPADDR & 0xff) != 44) + return; + + } + + if (arpptr->TARGETIPADDR == 0) // Request for 0.0.0.0 + return; + + } + + // Add to our table, as we will almost certainly want to send back to it +ARPOk: +// Debugprintf("ARP Request for %08x Tell %08x\n", +// arpptr->TARGETIPADDR, arpptr->SENDIPADDR); + + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto AlreadyThere; // Already there + + if (Arp == NULL) return; // No point if table full + + Arp->IPADDR = arpptr->SENDIPADDR; + Arp->ARPTYPE = 'E'; + Arp->ARPINTERFACE = 255; + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6); + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + + // Also add to routes + + AddToRoutes(Arp,arpptr->SENDIPADDR, 'E'); + + SaveARP(); + +AlreadyThere: + + if (arpptr->TARGETIPADDR == OurIPAddr || arpptr->TARGETIPADDR == EncapAddr) + { + uint32_t Save = arpptr->TARGETIPADDR; + + arpptr->ARPOPCODE = 0x0200; + memcpy(arpptr->TARGETHWADDR, arpptr->SENDHWADDR ,6); + memcpy(arpptr->SENDHWADDR, ourMACAddr ,6); + + arpptr->TARGETIPADDR = arpptr->SENDIPADDR; + arpptr->SENDIPADDR = Save; + + memcpy(arpptr->MSGHDDR.DEST, arpptr->MSGHDDR.SOURCE ,6); + memcpy(arpptr->MSGHDDR.SOURCE, ourMACAddr ,6); + +// Debugprintf("Forus ARP Reply for %08x Targ %08x HNAT %08x\n", +// arpptr->SENDIPADDR, arpptr->TARGETIPADDR, HostNATAddr); + + Send_ETH(arpptr,60, FromTAP); + + return; + } + + // If for our Ethernet Subnet, Ignore or we send loads of unnecessary msgs to ax.25 + + // Actually our subnet could be subnetted further + + // So respond for NAT'ed addresses + + // Why not just see if we have a route first?? + + Route = FindRoute(arpptr->TARGETIPADDR); + + if (Route) + { + if (Route->TYPE == 'T' || Route->TYPE == 'U') + goto ProxyARPReply; // Assume we can always reach via tunnel + + Arp = LookupARP(Route->GATEWAY, FALSE, &Found); + + if (Arp) + { + if(Arp->ARPVALID && (Arp->ARPTYPE == 'E')) + return; // On LAN, so should reply direct + + goto ProxyARPReply; + } + } + + if ((arpptr->TARGETIPADDR & OurNetMask) == (OurIPAddr & OurNetMask)) + { + // Unless for a NAT'ed address, in which case we reply with our virtual MAC + + struct nat_table_entry * NAT = NULL; + int index; + + for (index=0; index < nat_table_len; index++) + { + NAT = &nat_table[index]; + + if (NAT->origipaddr == arpptr->TARGETIPADDR) + break; + } + + if (index >= nat_table_len) + return; + + goto ProxyARPReply; + + } + // Should't we just reply if we know it ?? (Proxy ARP) + + // Maybe, but that may mean dowstream nodes dont learnit + + // Sould we look in routes table, as we may have a gateway to it. + + Route = FindRoute(arpptr->TARGETIPADDR); + + if (Route) + { + if (Route->TYPE == 'T') + goto ProxyARPReply; // Assume we can always reach via tunnel + + Arp = LookupARP(Route->GATEWAY, FALSE, &Found); + + if (Arp) + { + if(Arp->ARPVALID && (Arp->ARPTYPE == 'E')) + return; // On LAN, so should reply direct +ProxyARPReply: + ETHARPREQMSG.TARGETIPADDR = arpptr->SENDIPADDR; + ETHARPREQMSG.ARPOPCODE = 0x0200; // Reply + ETHARPREQMSG.SENDIPADDR = arpptr->TARGETIPADDR; + + memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6); + memcpy(ETHARPREQMSG.MSGHDDR.DEST, arpptr->SENDHWADDR, 6); + +// Debugprintf("Proxy ARP Reply for %08x Targ %08x HNAT %08x\n", +// ETHARPREQMSG.SENDIPADDR, ETHARPREQMSG.TARGETIPADDR, HostNATAddr); + + // We send to TAP if request from TAP +// Send_ETH(ÐARPREQMSG, 42, ETHARPREQMSG.TARGETIPADDR == HostNATAddr); + Send_ETH(ÐARPREQMSG, 42, FromTAP); + return; + } + } + + // Not in our cache, so send to all other ports enabled for IP, reformatting as necessary + + AXARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + AXARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + memset(AXARPREQMSG.TARGETHWADDR, 0, 7); + AXARPREQMSG.ARPOPCODE = 0x0100; + + for (i = 1; i <= MaxBPQPortNo; i++) + { + if (Mask & 1) + Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, i, QST); + + Mask >>= 1; + } + + break; + + + case 0x0200: + + if (memcmp(&arpptr->MSGHDDR.DEST, ourMACAddr,6 ) != 0 ) + return; // Not for us + + // Update ARP Cache + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto Update; + + if (Arp == NULL) + goto SendBack; + + // Also add to routes + + AddToRoutes(Arp, arpptr->SENDIPADDR, 'E'); +Update: + Arp->IPADDR = arpptr->SENDIPADDR; + + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,6); + Arp->ARPTYPE = 'E'; + Arp->ARPINTERFACE = 255; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + SaveARP(); + +SendBack: + + // Send Back to Originator of ARP Request + + if (Arp && arpptr->TARGETIPADDR == OurIPAddr) // Reply to our request? + { + struct DATAMESSAGE * buffptr; + PIPMSG IPptr; + + while (Arp->ARP_Q) + { + buffptr = Q_REM_NP(&Arp->ARP_Q); + IPptr = (PIPMSG)&buffptr->L2DATA[30]; + RouteIPMsg(IPptr); + free(buffptr); + } + + break; + } + + Arp = LookupARP(arpptr->TARGETIPADDR, FALSE, &Found); + + if (Found) + { + if (Arp->ARPINTERFACE == 255) + { + ETHARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + ETHARPREQMSG.ARPOPCODE = 0x0200; // Reply + ETHARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + + memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6); + memcpy(ETHARPREQMSG.MSGHDDR.DEST, Arp->HWADDR, 6); + + Send_ETH(ÐARPREQMSG, 42, ETHARPREQMSG.TARGETIPADDR == HostNATAddr); + return; + } + else + { + AXARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + AXARPREQMSG.ARPOPCODE = 0x0200; // Reply + AXARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + + memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7); + memcpy(AXARPREQMSG.TARGETHWADDR, Arp->HWADDR, 7); + + Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, Arp->ARPINTERFACE, Arp->HWADDR); + + return; + } + } + break; + + default: + break; + } + return; +} + +VOID ProcessAXARPMsg(PAXARP arpptr) +{ + int i=0; + uint64_t Mask=IPPortMask; + PARPDATA Arp; + PROUTEENTRY Route; + + BOOL Found; + + arpptr->MSGHDDR.ORIGIN[6] &= 0x7e; // Clear end of Call + + if (memcmp(arpptr->MSGHDDR.ORIGIN, MYCALL, 7) == 0) // ?Echoed packet? + return; + + switch (arpptr->ARPOPCODE) + { + case 0x0100: + { + // Add to our table, as we will almost certainly want to send back to it + + if (arpptr->TARGETIPADDR == 0) + return; // Ignore 0.0.0.0 + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto AlreadyThere; // Already there + + if (Arp != NULL) + { + // ENTRY NOT FOUND - IF ANY SPARE ENTRIES, USE ONE + + Arp->IPADDR = arpptr->SENDIPADDR; + + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,7); + + Arp->ARPTYPE = 'D'; + Arp->ARPINTERFACE = arpptr->MSGHDDR.PORT; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + + // Also add to routes + + AddToRoutes(Arp, arpptr->SENDIPADDR, 'D'); + } + +AlreadyThere: + + if (arpptr->TARGETIPADDR == OurIPAddr) + { + arpptr->ARPOPCODE = 0x0200; + memcpy(arpptr->TARGETHWADDR, arpptr->SENDHWADDR, 7); + memcpy(arpptr->SENDHWADDR, MYCALL, 7); + + arpptr->TARGETIPADDR = arpptr->SENDIPADDR; + arpptr->SENDIPADDR = OurIPAddr; + + Send_AX_Datagram((PMESSAGE)arpptr, 46, arpptr->MSGHDDR.PORT, arpptr->MSGHDDR.ORIGIN); + + return; + } + + // Should't we just reply if we know it ?? (Proxy ARP) + + // Maybe, but that may mean dowstream nodes dont learnit + + // Sould we look in routes table, as we may have a gateway to it. + + Route = FindRoute(arpptr->TARGETIPADDR); + + if (Route) + { + if (Route->TYPE == 'T') + goto AXProxyARPReply; // Assume we can always reach via tunnel + + + Arp = LookupARP(arpptr->TARGETIPADDR, FALSE, &Found); + + if (Found) + { + // if Trarget is the station we got the request from, there is a loop + // KIll the ARP entry, and ignore + + if (memcmp(Arp->HWADDR, arpptr->SENDHWADDR, 7) == 0) + { + RemoveARP(Arp); + return; + } + +AXProxyARPReply: + + AXARPREQMSG.ARPOPCODE = 0x0200; // Reply + AXARPREQMSG.TARGETIPADDR = arpptr->SENDIPADDR; + AXARPREQMSG.SENDIPADDR = arpptr->TARGETIPADDR; + + memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7); + memcpy(AXARPREQMSG.TARGETHWADDR, arpptr->SENDHWADDR, 7); + + Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, arpptr->MSGHDDR.PORT, arpptr->SENDHWADDR); + + return; + } + } + + // Not in our cache, so send to all other ports enabled for IP, reformatting as necessary + + AXARPREQMSG.ARPOPCODE = 0x0100; + AXARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + AXARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + + for (i=1; i<=MaxBPQPortNo; i++) + { + if (i != arpptr->MSGHDDR.PORT) + if (Mask & 1) + Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, i, QST); + + Mask >>= 1; + } + + memset(ETHARPREQMSG.MSGHDDR.DEST, 0xff, 6); + memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); + + ETHARPREQMSG.ARPOPCODE = 0x0100; + ETHARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + ETHARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + memcpy(ETHARPREQMSG.SENDHWADDR, ourMACAddr, 6); + + Send_ETH(ÐARPREQMSG, 42, FALSE); + + break; + } + + case 0x0200: + + // Update ARP Cache + + Arp = LookupARP(arpptr->SENDIPADDR, TRUE, &Found); + + if (Found) + goto Update; + + if (Arp == NULL) + goto SendBack; + + // Also add to routes + + AddToRoutes(Arp, arpptr->SENDIPADDR, 'D'); +Update: + Arp->IPADDR = arpptr->SENDIPADDR; + + memcpy(Arp->HWADDR, arpptr->SENDHWADDR ,7); + Arp->ARPTYPE = 'D'; + Arp->ARPINTERFACE = arpptr->MSGHDDR.PORT; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + +SendBack: + + // Send Back to Originator of ARP Request + + if (arpptr->TARGETIPADDR == OurIPAddr) // Reply to our request? + break; + + Arp = LookupARP(arpptr->TARGETIPADDR, FALSE, &Found); + + if (Found) + { + if (Arp->ARPINTERFACE == 255) + { + ETHARPREQMSG.ARPOPCODE = 0x0200; // Reply + + ETHARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + ETHARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + + memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr,6); + memcpy(ETHARPREQMSG.TARGETHWADDR, Arp->HWADDR, 6); + memcpy(ETHARPREQMSG.MSGHDDR.DEST, Arp->HWADDR, 6); + + Send_ETH(ÐARPREQMSG, 42, ETHARPREQMSG.TARGETIPADDR == HostNATAddr); + return; + } + else + { + AXARPREQMSG.ARPOPCODE = 0x0200; // Reply + + AXARPREQMSG.TARGETIPADDR = arpptr->TARGETIPADDR; + AXARPREQMSG.SENDIPADDR = arpptr->SENDIPADDR; + + memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7); + memcpy(AXARPREQMSG.TARGETHWADDR, Arp->HWADDR, 7); + + Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, Arp->ARPINTERFACE, Arp->HWADDR); + + return; + } + } + + break; + + + default: + + return; + } +} + +VOID ProcessIPMsg(PIPMSG IPptr, UCHAR * MACADDR, char Type, UCHAR Port) +{ + uint32_t Dest; + PARPDATA Arp; + PROUTEENTRY Route; + BOOL Found; + PUDPMSG UDPptr; + + if (IPptr->VERLEN != 0x45) + { + Dest = IPptr->IPDEST.addr; + Debugprintf("IP Pkt not 45 for %s\n", FormatIP(Dest)); + return; // Only support Type = 4, Len = 20 + } + if (!CheckIPChecksum(IPptr)) + { + Dest = IPptr->IPDEST.addr; + Debugprintf("IP Pkt Bad CKSUM for %s\n", FormatIP(Dest)); + return; + } + // Make sure origin ia routable. If not, add to ARP + + Route = FindRoute(IPptr->IPSOURCE.addr); + + if (Route == NULL) + { + Arp = LookupARP(IPptr->IPSOURCE.addr, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Arp != NULL) + { + Arp->IPADDR = IPptr->IPSOURCE.addr; + + if (Type == 'E') + { + memcpy(Arp->HWADDR, MACADDR, 6); + } + else + { + memcpy(Arp->HWADDR, MACADDR, 7); + Arp->HWADDR[6] &= 0x7e; + } + Arp->ARPTYPE = Type; + Arp->ARPINTERFACE = Port; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = ARPTIMEOUT; + + // Also add to routes + + AddToRoutes(Arp, IPptr->IPSOURCE.addr, Type); + + SaveARP(); + } + } + } + + if (Route && Route->ARP) + Route->ARP->ARPTIMER = ARPTIMEOUT; // Refresh + + // See if for us - if not pass to router + + Dest = IPptr->IPDEST.addr; +// Debugprintf("IP Pkt for %s\n", FormatIP(Dest)); + + if (Dest == OurIPAddr) + goto ForUs; + + RouteIPMsg(IPptr); + + return; + +ForUs: + + // We now pass stuff addressed to us to the host, unless it is a reponse + // to our ping request + + // Not sure what to do with snmp (maybe option) + +// if (IPptr->IPPROTOCOL == 4) // AMPRNET Tunnelled Packet +// { +// ProcessTunnelMsg(IPptr); +// return; +// } + + if (IPptr->IPPROTOCOL == 1) // ICMP + { + int Len; + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + Debugprintf("FORUS ICMP Type %d", ICMPptr->ICMPTYPE); + + if (Len == 28 && ICMPptr->ICMPTYPE == 0 && memcmp(ICMPptr->ICMPData, "*BPQ", 4) == 0) + { + // Probably our response + + ProcessICMPMsg(IPptr); + return; + } + } + + // Support UDP for SNMP + + if (BPQSNMP && IPptr->IPPROTOCOL == 17) // UDP + { + UDPptr = (PUDPMSG)&IPptr->Data; + + if (UDPptr->DESTPORT == htons(161)) + { + ProcessSNMPMessage(IPptr); + return; + } + } + + // Pass rest to host + + RouteIPMsg(IPptr); + return; +} + +unsigned short cksum(unsigned short *ip, int len) +{ + uint32_t sum = 0; /* assume 32 bit long, 16 bit short */ + unsigned short copy [2048]; + + // if not word aligned copy + + if (len > 1024 || len < 0 || ip == 0) + Debugprintf("%d", len); + + +// if (&ip & 1) + { + memcpy(copy, ip, len); + ip = copy; + } + + while(len > 1) + { + sum += *(ip++); + if(sum & 0x80000000) /* if high order bit set, fold */ + sum = (sum & 0xFFFF) + (sum >> 16); + len -= 2; + } + + if(len) /* take care of left over byte */ + sum += (unsigned short) *(unsigned char *)ip; + + while(sum>>16) + sum = (sum & 0xFFFF) + (sum >> 16); + + return (unsigned short)sum; +} + +BOOL CheckIPChecksum(PIPMSG IPptr) +{ + USHORT checksum=0; + + if (IPptr->IPCHECKSUM == 0) + return TRUE; //Not used + + checksum = cksum((unsigned short *)IPptr, 20); + + if (checksum == 0xffff) + return TRUE; + else + return FALSE; + +} +BOOL Check_Checksum(VOID * ptr1, int Len) +{ + USHORT checksum; + + checksum = cksum((unsigned short *)ptr1, Len); + + if (checksum == 0xffff) + return TRUE; + else + return FALSE; + +} +USHORT Generate_CHECKSUM(VOID * ptr1, int Len) +{ + USHORT checksum=0; + + checksum = cksum((unsigned short *)ptr1, Len); + + return ~checksum ; +} + +VOID ProcessTunnelMsg(PIPMSG IPptr) +{ + UCHAR * ptr; + PIPMSG Outer = IPptr; // Save tunnel header + int Origlen; +// int InnerLen; + + // Check header length - for now drop any message with options + + if (IPptr->VERLEN != 0x45) + return; + + Origlen = htons(Outer->IPLENGTH); + + ptr = (UCHAR *)IPptr; + ptr += 20; // Skip IPIP Header + IPptr = (PIPMSG) ptr; + + // If we are relaying it from a DMZ host there will be another header + + if (IPptr->IPPROTOCOL == 4) // IPIP + { + Outer = IPptr; + ptr = (UCHAR *)IPptr; + ptr += 20; // Skip IPIP Header + IPptr = (PIPMSG) ptr; + Origlen -= 20; + } + + // First check for RIP44 Messages + + if (IPptr->IPPROTOCOL == 17) // UDP + { + PUDPMSG UDPptr = (PUDPMSG)&IPptr->Data; + + if (IPptr->IPSOURCE.addr == UCSD44 && UDPptr->DESTPORT == htons(520)) + { + ProcessRIP44Message(IPptr); + return; + } + } + + // for now drop anything not from a 44 address. + // now also need to drop 44.192/10 as it has been sold to Amazon + + if (IPptr->IPSOURCE.S_un_b.s_b1 != 44) + return; + + // Drop 44.192 - 44.255 + + if (IPptr->IPSOURCE.S_un_b.s_b2 >= 192) + return; + + // See if for us + + // Handle Pings, our Ping responses and SNMP in BPQ + // pass rest to host + + if (IPptr->IPDEST.addr == OurIPAddr) + { + if (IPptr->IPPROTOCOL == 1) // ICMP + { + int Len; + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + if (Len == 28 && ICMPptr->ICMPTYPE == 0 && memcmp(ICMPptr->ICMPData, "*BPQ", 4) == 0) + { + // Probably ours + + ProcessICMPMsg(IPptr); + return; + } + } + + if (IPptr->IPPROTOCOL == 1) // ICMP + { + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + if (ICMPptr->ICMPTYPE == 8) + { + ProcessICMPMsg(IPptr); + return; + } + } + + // Support UDP for SNMP + + if (IPptr->IPPROTOCOL == 17) // UDP + { + PUDPMSG UDPptr = (PUDPMSG)&IPptr->Data; + + if (UDPptr->DESTPORT == htons(161)) + { + ProcessSNMPMessage(IPptr); + return; + } + } + + // Others should be passed to our host + + // I think we can just drop through to RouteIPMsg + + } + + // I think anything else is just passed to the router + + RouteIPMsg(IPptr); +} + +VOID ProcessRIP44Message(PIPMSG IPptr) +{ + int Len; + PUDPMSG UDPptr = (PUDPMSG)&IPptr->Data; + PRIP2HDDR HDDR = (PRIP2HDDR)&UDPptr->UDPData; + PRIP2ENTRY RIP2; + + Len = ntohs(IPptr->IPLENGTH); + Len -= 20; + + if (UDPptr->CHECKSUM) + if (Check_Checksum(UDPptr, Len) == FALSE) + return; + + if (HDDR->Command != 2 || HDDR->Version != 2) + return; + + RIP2 = (PRIP2ENTRY) ++HDDR; + + Len -= 12; // UDP and RIP Headers + + if (RIP2->AddrFamily == 0xFFFF) + { + // Authentication Entry + + Len -= 20; + RIP2++; + } + + while (Len >= 20) // Entry Length + { + // See if already in table + + PROUTEENTRY Route; + BOOL Found; + + // if for our subnet, ignore + + // Actually don't need to, as we won't overwrite the preconfigured + // interface route + + Route = LookupRoute(RIP2->IPAddress, RIP2->Mask, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Route != NULL && RIP2->Metric < 16) + { + Route->NETWORK = RIP2->IPAddress; + Route->SUBNET = RIP2->Mask; + Route->METRIC = RIP2->Metric; + Route->Encap = RIP2->NextHop; + Route->TYPE = 'T'; + Route->RIPTIMOUT = 3600; // 1 hour for now + } + } + else + { + // Already in table + + // Should we replace an RF route with an ENCAP route?? + // For now, no. May make an option later + // Should never replace our interface routes + + if (Route->TYPE == 'T') + { + // See if same Encap, and if not, is this better metric? + + // Is this possible with RIP44?? + + if (Route->Encap != RIP2->NextHop) + { + if (Route->METRIC >= RIP2->Metric) + { + // Should also change if equal, as dynamic address could have changed + + Route->METRIC = RIP2->Metric; + Route->Encap = RIP2->NextHop; + } + } + + Route->METRIC = RIP2->Metric; + + if (RIP2->Metric >= 15) + { + // HE IS TELLING US ITS UNREACHABLE - START DELETE TIMER + + Route->RIPTIMOUT = 0; + if (Route->GARTIMOUT == 0) + Route ->GARTIMOUT = 4; + } + else + { + Route->RIPTIMOUT = 3600; // 1 hour for now + Route->GARTIMOUT = 0; // In case started to delete + } + } + } + + Len -= 20; + RIP2++; + } + + SaveIPRoutes(); + qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareMasks); + LastRIP44Msg = time(NULL); +} + + +VOID ProcessICMPMsg(PIPMSG IPptr) +{ + int Len; + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + Check_Checksum(ICMPptr, Len); + + if (ICMPptr->ICMPTYPE == 8) + { + // ICMP_ECHO + + ICMPptr->ICMPTYPE = 0; // Convert to Reply + + // CHECKSUM IT + + ICMPptr->ICMPCHECKSUM = 0; + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, Len); + + // Swap Dest to Origin + + IPptr->IPDEST = IPptr->IPSOURCE; + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPTTL = IPTTL; + + // RouteIPMsg redoes checksum + + RouteIPMsg(IPptr); // Send Back + return; + } + + if (ICMPptr->ICMPTYPE == 0) + { + // ICMP_REPLY: + + // It could be a reply to our request + // or from a our host pc + + UCHAR * BUFFER = GetBuff(); + UCHAR * ptr1; + struct _MESSAGE * Msg; + TRANSPORTENTRY * Session = L4TABLE; + char IP[20]; + unsigned char work[4]; + + // Internal Pings have Length 28 and Circuit Index as ID + + if (Len > 28 || ICMPptr->ICMPID >= MAXCIRCUITS) + { + // For Host + + ReleaseBuffer(BUFFER); + RouteIPMsg(IPptr); + return; + } + + Session += ICMPptr->ICMPID; + + if (BUFFER == NULL) + return; + + ptr1 = &BUFFER[7]; + + memcpy(work, &IPptr->IPSOURCE, 4); + sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + *ptr1++ = 0xf0; // PID + + ptr1 += sprintf(ptr1, "Ping Response from %s", IP); + + *ptr1++ = 0x0d; // CR + + Len = (int)(ptr1 - BUFFER); + + Msg = (struct _MESSAGE *)BUFFER; + Msg->LENGTH = Len; + Msg->CHAIN = NULL; + + C_Q_ADD(&Session->L4TX_Q, (UINT *)BUFFER); + + PostDataAvailable(Session); + + return; + } +} + + +VOID SendICMPMessage(PIPMSG IPptr, int Type, int Code, int P2) +{ + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + UCHAR * ptr; + + if (OurIPAddr == 0) + return; // Can't do much without one + + if (IPptr->IPPROTOCOL == ICMP && ICMPptr->ICMPTYPE == 11) + return; // Don't send Time Exceeded for TimeExceded + + // Copy the Original IP Header and first 8 bytes of packet down the buffer + + ptr = (UCHAR *) ICMPptr; + + memmove(ptr + 8, IPptr, 28); // IP header plus 8 data + +// We swap Souce to Dest, Convert to ICMP 11 and send back first 8 bytes of packet after header + + IPptr->IPDEST = IPptr->IPSOURCE; + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPPROTOCOL = ICMP; + IPptr->IPTTL = IPTTL; + IPptr->FRAGWORD = 0; + IPptr->IPLENGTH = htons(56); // IP Header ICMP Header IP Header 8 Data + + memset (ICMPptr, 0, 8); + ICMPptr->ICMPTYPE = Type; + ICMPptr->ICMPCODE = Code; + ICMPptr->ICMPSEQUENCE = htons(P2); + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, 36); + + RouteIPMsg(IPptr); +} + +VOID SendICMPTimeExceeded(PIPMSG IPptr) +{ + SendICMPMessage(IPptr, 11, 0, 0); + return; +} + +VOID SendIPtoEncap(PIPMSG IPptr, uint32_t Encap) +{ + union + { + struct sockaddr_in txaddr; + struct sockaddr_in6 txaddr6; + } TXaddr = {0}; + + int sent; + int addrlen = sizeof(struct sockaddr_in6); + int Origlen; + + TXaddr.txaddr.sin_family = AF_INET; + Origlen = htons(IPptr->IPLENGTH); + + // If we are using PCAP interface we have to add IPIP and MAC Headers. + + if (EncapAddr != INADDR_NONE) + { + UCHAR IPCopy[2048]; // Need Space to add headers + PETHMSG Ethptr = (PETHMSG)IPCopy; + PIPMSG Outer = (PIPMSG)&IPCopy[14]; + + memset(IPCopy, 0, 34); // Eth + IP Headers + + Outer->VERLEN = 0x45; + Outer->IPDEST.addr = Encap; + Outer->IPSOURCE.addr = EncapAddr; + Outer->IPPROTOCOL = 4; + Outer->IPTTL = IPTTL; + Outer->IPID = IPptr->IPID; + Outer->IPLENGTH = htons(Origlen + 20); + memcpy(&Outer->Data, IPptr, Origlen); + + Outer->IPCHECKSUM = 0; + Outer->IPCHECKSUM = Generate_CHECKSUM(Outer, 20); + + memcpy(Ethptr->DEST, RouterMac, 6); + memcpy(Ethptr->SOURCE, ourMACAddr, 6); + Ethptr->ETYPE= 0x0008; + Send_ETH(Ethptr, Origlen + 34, FALSE); + + return; + + } + if (UDPEncap) + { + UCHAR * ptr; + + memcpy(&TXaddr, &RXaddr, sizeof(struct sockaddr_in6)); + TXaddr.txaddr.sin_port = htons(UDPPort); + + // UDP Processor Needs the Encap Address, but we don't need the IPIP hearer + // as that is added by the raw send later. Just stick it on the end. + + + ptr = (UCHAR *)IPptr; + memcpy(ptr + Origlen, &Encap, 4); + Origlen += 4; + } + else + memcpy(&TXaddr.txaddr.sin_addr, &Encap, 4); + + + sent = sendto(EncapSock, (char *)IPptr, Origlen, 0, (struct sockaddr *)&TXaddr, addrlen); + sent = GetLastError(); + +} + + +BOOL RouteIPMsg(PIPMSG IPptr) +{ + PARPDATA Arp, ARPptr; + PROUTEENTRY Route; + BOOL Found; + struct nat_table_entry * NAT = NULL; + int index; + BOOL SendtoTAP = FALSE; // used on LinBPQ for NAT to This Host + + // Decremnent TTL and Recalculate header checksum + + IPptr->IPTTL--; + + if (IPptr->IPTTL == 0) + { + SendICMPTimeExceeded(IPptr); + return FALSE; + } + + // See if for a NATed Address + +// Debugprintf("RouteIPFrame IP %s\n", FormatIP(IPptr->IPDEST.addr)); + + for (index=0; index < nat_table_len; index++) + { + NAT = &nat_table[index]; + + // for the moment only map all ports + + if (NAT->origipaddr == IPptr->IPDEST.addr) + { + char Msg[80]; + int ptr; + IPLen = htons(IPptr->IPLENGTH); + + ptr = sprintf(Msg, "NAT %s to ", FormatIP(IPptr->IPDEST.addr)); + sprintf(&Msg[ptr], "%s\n", FormatIP(NAT->mappedipaddr)); + +// Debugprintf("%s", Msg); + + IPptr->IPDEST.addr = NAT->mappedipaddr; + + if (IPptr->IPPROTOCOL == 6) + RecalcTCPChecksum(IPptr); + else if (IPptr->IPPROTOCOL == 17) + RecalcUDPChecksum(IPptr); + else if (IPptr->IPPROTOCOL == 1) + { + // ICMP. If it has an inner packet (Time Exceeded or Need to Fragment) + // we also need to un-NAT the inner packet. + + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + + if (ICMPptr->ICMPTYPE == 3 || ICMPptr->ICMPTYPE == 11) + { + PIPMSG IPptr = (PIPMSG)ICMPptr->ICMPData; + + Debugprintf("NAT ICMP Unreachable or Time Exceeded %d", ICMPptr->ICMPTYPE); + IPptr->IPSOURCE.addr = NAT->mappedipaddr; + + // Dest could also need to be de-natted + + for (index=0; index < nat_table_len; index++) + { + NAT = &nat_table[index]; + + if (NAT->mappedipaddr == IPptr->IPDEST.addr) + { + IPptr->IPDEST.addr = NAT->origipaddr; + break; + } + } + + IPptr->IPCHECKSUM = 0; + IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + ICMPptr->ICMPCHECKSUM = 0; + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, IPLen - 20); + } + } + + + SendtoTAP = NAT->ThisHost; + break; + } + } + + if (IPptr->IPDEST.addr == HostNATAddr) + SendtoTAP = TRUE; + + IPptr->IPCHECKSUM = 0; + IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + // Everything is in the routes table, even arp-derived routes. so just look there + + Route = FindRoute(IPptr->IPDEST.addr); + + if (Route == NULL) + return FALSE; // ?? Dest unreachable ?? + + Route->FRAMECOUNT++; + + Arp = Route->ARP; + + if (Arp == NULL) + { + if (Route->TYPE == 'T') + { + SendIPtoEncap(IPptr, Route->Encap); + return TRUE; + } + + else if (Route->TYPE == 'U') + { + int sent; + int addrlen = sizeof(struct sockaddr_in6); + int Origlen; + + Origlen = htons(IPptr->IPLENGTH); + + sent = sendto(UDPSendSock, (char *)IPptr, Origlen, 0, (struct sockaddr *)&Route->UDPADDR, addrlen); + return TRUE; + } + + + // Look up target address in ARP + + if (Route->GATEWAY) + Arp = LookupARP(Route->GATEWAY, FALSE, &Found); + else + Arp = LookupARP(IPptr->IPDEST.addr, FALSE, &Found); + + if (!Found) + { + if (Route->GATEWAY == 0) // Interace Route + { + ARPptr = AllocARPEntry(); + if (ARPptr != NULL) + { + struct DATAMESSAGE * buffptr; + Route->ARP = ARPptr; + ARPptr->ARPROUTE = Route; + ARPptr->ARPINTERFACE = 255; + ARPptr->ARPTIMER = 5; + ARPptr->ARPTYPE = 'E'; + ARPptr->IPADDR = IPptr->IPDEST.addr; + + // Save a copy to send on if ARP reply received + + buffptr = malloc(2048); + + if (buffptr) + { + if (ntohs(IPptr->IPLENGTH) > 1600) + { + Debugprintf("Overlength IP Packet %d" , ntohs(IPptr->IPLENGTH)); + return TRUE; + } + memcpy(&buffptr->L2DATA[30], IPptr, ntohs(IPptr->IPLENGTH)); + C_Q_ADD_NP(&ARPptr->ARP_Q, buffptr); + } + + SendARPMsg(ARPptr, SendtoTAP); + + return TRUE; + } + } + return FALSE; + } + } + + if (Arp->ARPVALID) + { + if (Arp->ARPTYPE == 'T') + SendIPtoEncap(IPptr, Route->Encap); + else + if (Arp->ARPTYPE == 'E') + SendIPtoEther(IPptr, Arp->HWADDR, SendtoTAP); + else + SendIPtoAX25(IPptr, Arp->HWADDR, Arp->ARPINTERFACE, Arp->ARPTYPE); + + return TRUE; + } + return FALSE; +} + +VOID SendIPtoEther(PIPMSG IPptr, UCHAR * HWADDR, BOOL SendtoTAP) +{ + // AX.25 headers are bigger, so there will always be room in buffer for enet header + + PETHMSG Ethptr = (PETHMSG)IPptr; + int Len; + + (UCHAR *)Ethptr--; + + Len = ntohs(IPptr->IPLENGTH); + + Len+=14; // Add eth Header + + memcpy(Ethptr->DEST, HWADDR, 6); + memcpy(Ethptr->SOURCE, ourMACAddr, 6); + Ethptr->ETYPE= 0x0008; + + Send_ETH(Ethptr,Len, SendtoTAP); + + return; +} + +VOID SendIPtoAX25(PIPMSG IPptr, UCHAR * HWADDR, int Port, char Mode) +{ + PBUFFHEADER Msgptr = (PBUFFHEADER)IPptr; + int Len; + USHORT FRAGWORD = ntohs(IPptr->FRAGWORD); + USHORT OrigFragWord = FRAGWORD; + int PACLEN = 256; + + + (UCHAR *)Msgptr--; + Msgptr->PID = 0xcc; //IP + + if (Port == 0) // NETROM + PACLEN = 235; + + Len = ntohs(IPptr->IPLENGTH); + + // Don't fragment VC stuff + + if (Mode == 'V') // Virtual Circuit + { + Send_AX_Connected((PMESSAGE)Msgptr, Len + 16, Port, HWADDR); + return; + } + + while (Len > PACLEN) + { + // Need to Frgament + + USHORT Fraglen; // Max Fragment Size (PACLEN rounded down to 8 boundary)) + USHORT Datalen; // Data Content)) + + UCHAR * ptr1 = &IPptr->Data; + + //Bit 0: reserved, must be zero + //Bit 1: (DF) 0 = May Fragment, 1 = Don't Fragment. + //Bit 2: (MF) 0 = Last Fragment, 1 = More Fragments. + //Fragment Offset: 13 bits + +// FRAGWORD &= 0x3fff; // Clear Dont Fragment bit + + if (FRAGWORD & (1 << 14)) + { + SendICMPMessage(IPptr, 3, 4, PACLEN); // type 3 (dest unreachable), code 4 (frag needed but don't-fragment bit set)) + return; + } + + FRAGWORD |= (1 << 13); // Set More Fragments bit + IPptr->FRAGWORD = htons(FRAGWORD); + + Datalen = (PACLEN - 20) & 0xFFF8; // Must be multiple of 8 bytes + Fraglen = Datalen + 20; + + IPptr->IPLENGTH = htons(Fraglen); + + IPptr->IPCHECKSUM = 0; + IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + // Send First Fragment + + if (Mode == 'D') // Datagram + Send_AX_Datagram((PMESSAGE)Msgptr, Fraglen + 16, Port, HWADDR); + else + Send_AX_Connected((PMESSAGE)Msgptr, Fraglen + 16, Port, HWADDR); + + // Update Header + + FRAGWORD += Datalen / 8; + + // Move Data Down the buffer + + Len -= Datalen; + memmove(ptr1, ptr1 + (Datalen), Len); + } + + // Reset Header in case we've messed with it + + IPptr->IPLENGTH = htons(Len); + + // if this started out as a fragment before we split it more, + // we need to leave the MF bit set + + if ((OrigFragWord & 0x2000) == 0) + FRAGWORD &= 0x5fff; // Clear More Fragments bit + + IPptr->FRAGWORD = htons(FRAGWORD); + + IPptr->IPCHECKSUM = 0; + IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + Len+=16; + + if (Mode == 'D') // Datagram + { + Send_AX_Datagram((PMESSAGE)Msgptr, Len, Port, HWADDR); + return; + } + + Send_AX_Connected((PMESSAGE)Msgptr, Len, Port, HWADDR); +} + +PROUTEENTRY AllocRouteEntry() +{ + PROUTEENTRY Routeptr; + + if (NumberofRoutes == 0) + + RouteRecords = malloc(sizeof(void *)); + else + RouteRecords = realloc(RouteRecords,(NumberofRoutes + 1) * sizeof(void *)); + + Routeptr = zalloc(sizeof(ROUTEENTRY)); + + if (Routeptr == NULL) return NULL; + + RouteRecords[NumberofRoutes++] = Routeptr; + + return Routeptr; +} + + +PARPDATA AllocARPEntry() +{ + ARPDATA * ARPptr; + + if (NumberofARPEntries == 0) + + ARPRecords = malloc(sizeof(void *)); + else + ARPRecords = realloc(ARPRecords, (NumberofARPEntries+1)* sizeof(void *)); + + ARPptr = malloc(sizeof(ARPDATA)); + + if (ARPptr == NULL) return NULL; + + memset(ARPptr, 0, sizeof(ARPDATA)); + + ARPRecords[NumberofARPEntries++] = ARPptr; + + return ARPptr; +} + + VOID SendARPMsg(PARPDATA Arp, BOOL ToTAP) + { + // Send ARP. Initially used only to find default gateway + + Arp->ARPTIMER = 5; // Retry periodically + + if (Arp->ARPINTERFACE == 255) + { + ETHARPREQMSG.ARPOPCODE = 0x0100; // ; REQUEST + + ETHARPREQMSG.TARGETIPADDR = Arp->IPADDR; + memset(ETHARPREQMSG.TARGETHWADDR, 0, 6); + + ETHARPREQMSG.SENDIPADDR = OurIPAddr; + memcpy(ETHARPREQMSG.SENDHWADDR,ourMACAddr, 6); + + memcpy(ETHARPREQMSG.MSGHDDR.SOURCE, ourMACAddr, 6); + memset(ETHARPREQMSG.MSGHDDR.DEST, 255, 6); + + Send_ETH(ÐARPREQMSG, 42, ToTAP); + + return; + } + else + { + AXARPREQMSG.TARGETIPADDR = Arp->IPADDR; + memset(AXARPREQMSG.TARGETHWADDR, 0, 7); + AXARPREQMSG.ARPOPCODE = 0x0100; // ; REQUEST + AXARPREQMSG.SENDIPADDR = OurIPAddr; + + memcpy(AXARPREQMSG.SENDHWADDR, MYCALL, 7); + memcpy(AXARPREQMSG.TARGETHWADDR, Arp->HWADDR, 7); + + Send_AX_Datagram((PMESSAGE)&AXARPREQMSG, 46, Arp->ARPINTERFACE, QST); + + return; + + } + } + +PROUTEENTRY FindRoute(uint32_t IPADDR) +{ + PROUTEENTRY Route = NULL; + int i; + + for (i = 0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + + if ((IPADDR & Route->SUBNET) == Route->NETWORK) + return Route; + } + return NULL; +} + + + +PROUTEENTRY LookupRoute(uint32_t IPADDR, uint32_t Mask, BOOL Add, BOOL * Found) +{ + PROUTEENTRY Route = NULL; + int i; + + for (i = 0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + + if (Route->NETWORK == IPADDR && Route->SUBNET == Mask) + { + *Found = TRUE; + return Route; + } + } + + // Not Found + + *Found = FALSE; + + if (Add) + { + Route = AllocRouteEntry(); + return Route; + } + else + return NULL; +} + +PARPDATA LookupARP(uint32_t IPADDR, BOOL Add, BOOL * Found) +{ + PARPDATA Arp = NULL; + int i; + + for (i=0; i < NumberofARPEntries; i++) + { + Arp = ARPRecords[i]; + + if (Arp->IPADDR == IPADDR) + { + *Found = TRUE; + return Arp; + } + } + + // Not Found + + *Found = FALSE; + + if (Add) + { + Arp = AllocARPEntry(); + return Arp; + } + else + return NULL; +} +VOID RemoveARP(PARPDATA Arp); + +VOID RemoveRoute(PROUTEENTRY Route) +{ + int i; + + for (i=0; i < NumberofRoutes; i++) + { + if (Route == RouteRecords[i]) + { + while (i < NumberofRoutes) + { + RouteRecords[i] = RouteRecords[i+1]; + i++; + } + + if (Route->ARP) + { + PARPDATA Arp = Route->ARP; + Route->ARP->ARPROUTE = NULL; // Avoid recursion + if (Arp->LOCKED == 0) + RemoveARP(Arp); + } + + free(Route); + NumberofRoutes--; + return; + } + } +} + + +VOID RemoveARP(PARPDATA Arp) +{ + int i; + + while (Arp->ARP_Q) + free(Q_REM_NP((void *)&Arp->ARP_Q)); + + for (i=0; i < NumberofARPEntries; i++) + { + if (Arp == ARPRecords[i]) + { + while (i < NumberofARPEntries) + { + ARPRecords[i] = ARPRecords[i+1]; + i++; + } + + // Remove linked route + + if (Arp->ARPROUTE) + { + PROUTEENTRY Route = Arp->ARPROUTE; + Arp->ARPROUTE->ARP = NULL; // Avoid recursion + + if (Route->LOCKED == 0) + RemoveRoute(Route); + } + + free(Arp); + NumberofARPEntries--; + return; + } + } +} + + +Dll int APIENTRY GetIPInfo(VOID * ARPRecs, VOID * IPStatsParam, int index) +{ + IPStats.ARPEntries = NumberofARPEntries; + + ARPFlag = index; +#ifndef LINBPQ +#ifndef _WIN64 + _asm { + + mov esi, ARPRecs + mov DWORD PTR[ESI], offset Arp + + mov esi, IPStatsParam + mov DWORD PTR[ESI], offset IPStats + } +#endif +#endif + return ARPFlag; +} + + +static BOOL ReadConfigFile() +{ + +// IPAddr 192.168.0.129 +// IPBroadcast 192.168.0.255 +// IPGateway 192.168.0.1 +// IPPorts 1,4 + +// MAP 192.168.0.100 1001 n9pmo.dyndns.org 1000 + + char * Config; + char * ptr1, * ptr2; + PROUTEENTRY Route; + BOOL Found; + char buf[256],errbuf[256]; + + Config = PortConfig[IPConfigSlot]; // Config fnom bpq32.cfg + + if (Config) + { + // Using config from bpq32.cfg + + ptr1 = Config; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strlop(buf, ';'); + strlop(buf, '#'); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcessLine(buf)) + { + WritetoConsoleLocal("IP Gateway bad config record "); + strcat(errbuf, "\n"); + WritetoConsoleLocal(errbuf); + } + } + + // Add an Interface Route to our LAN + + Route = LookupRoute(OurIPAddr & OurNetMask, OurNetMask, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Route != NULL) + { + Route->NETWORK = OurIPAddr & OurNetMask; + Route->SUBNET = OurNetMask; + Route->GATEWAY = 0; + Route->LOCKED = 1; + Route->TYPE = 'E'; + } + } + } + return TRUE; +} + +static int ProcessLine(char * buf) +{ + char * ptr, * p_value, * p_origport, * p_host; + int port, mappedport, ipad, mappedipad; + BOOL NATTAP = FALSE; + int i; + + ptr = strtok(buf, " \t\n\r"); + p_value = strtok(NULL, " \t\n\r"); + + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if(_stricmp(ptr,"ADAPTER") == 0) + { +//#ifndef WIN32 +// WritetoConsoleLocal("IPGating to Ethernet is not supported in this build\n"); +// return TRUE; +//#endif + strcpy(Adapter,p_value); + return (TRUE); + } + + if(_stricmp(ptr,"promiscuous") == 0) + { + Promiscuous = atoi(p_value); + return (TRUE); + } + + if(_stricmp(ptr,"UDPTunnel") == 0) + { + WantUDPTunnel = atoi(p_value); + return (TRUE); + } + + if (_stricmp(ptr,"44Encap") == 0) // Enable Net44 IPIP Tunnel + { + WantEncap = TRUE; + + if (p_value == NULL) + return TRUE; + + EncapAddr = inet_addr(p_value); + + strcpy(EncapAddrText, p_value); + + if (EncapAddr != INADDR_NONE) + { + int a,b,c,d,e,f,num; + + // See if MAC Specified + + p_value = strtok(NULL, " \t\n\r"); + + if (p_value == NULL) + return TRUE; + + num=sscanf(p_value,"%x-%x-%x-%x-%x-%x",&a,&b,&c,&d,&e,&f); + + if (num == 6) + { + RouterMac[0]=a; + RouterMac[1]=b; + RouterMac[2]=c; + RouterMac[3]=d; + RouterMac[4]=e; + RouterMac[5]=f; + + p_value = strtok(NULL, " \t\n\r"); + + if (p_value == NULL) + return TRUE; + } + // see if source addr specified + + if (_stricmp(p_value, "RIPSOURCE") == 0) + { + // Set Source addr of RIP44 packets + + p_value = strtok(NULL, " =\t\n\r"); + UCSD44 = inet_addr(p_value); + return TRUE; + } + return FALSE; + } + + if (_stricmp(p_value, "UDP") == 0) + { + UDPEncap = TRUE; + + // look for options PORT and/or IPv6 + + p_value = strtok(NULL, " \t\n\r"); + + while (p_value) + { + if (_stricmp(p_value, "IPv6") == 0) + IPv6 = TRUE; + else + if (_memicmp(p_value, "PORT=", 5) == 0) + UDPPort = atoi(&p_value[5]); + + p_value = strtok(NULL, " \t\n\r"); + } + } + + return (TRUE); + } + + if (_stricmp(ptr,"IPAddr") == 0) + { + // accept /xx as a netmask + + char * p_mask = strlop(p_value, '/'); + + if (p_mask) + { + uint32_t IPMask; + int Bits = atoi(p_mask); + + if (Bits > 32) + Bits = 32; + + if (Bits == 0) + IPMask = 0; + else + IPMask = (0xFFFFFFFF) << (32 - Bits); + + OurNetMask = htonl(IPMask); // Needs to be Network order + } + + OurIPAddr = inet_addr(p_value); + + if (OurIPAddr == INADDR_NONE) return (FALSE); + + strcpy(IPAddrText, p_value); + return (TRUE); + } + +// if (_stricmp(ptr,"HostIPAddr") == 0) +// { +// HostIPAddr = inet_addr(p_value); + +// if (HostIPAddr == INADDR_NONE) return (FALSE); + +// strcpy(HostIPAddrText, p_value); + +// return (TRUE); +// } + + if (_stricmp(ptr,"IPNetMask") == 0) + { + OurNetMask = inet_addr(p_value); + + if (strcmp(p_value, "255.255.255.255") == 0) + return TRUE; + + if (OurNetMask == INADDR_NONE) return (FALSE); + + return (TRUE); + } + + if (_stricmp(ptr,"IPPorts") == 0) + { + struct _EXTPORTDATA * PORTVEC; + + while (p_value != NULL) + { + i=atoi(p_value); + if (i == 0) return FALSE; + + PORTVEC = (struct _EXTPORTDATA * )GetPortTableEntryFromPortNum(i); + + if (PORTVEC == NULL) + return FALSE; + + // if not KISS, make sure it can send UI frames + + if (PORTVEC->PORTCONTROL.PORTTYPE == 16) // EXTERNAL + if (PORTVEC->PORTCONTROL.PROTOCOL == 10) // Pactor/WINMOR + if (PORTVEC->PORTCONTROL.UICAPABLE == 0) + return FALSE; + + + IPPortMask |= (uint64_t)1 << (i-1); + p_value = strlop(p_value, ','); + } + return (TRUE); + } + + if (_stricmp(ptr,"NoDefaultRoute") == 0) + { + NoDefaultRoute = TRUE; + return TRUE; + } + + // ARP 44.131.4.18 GM8BPQ-7 1 + if (_stricmp(ptr,"ARP") == 0) + { + p_value[strlen(p_value)] = ' '; // put back together + return ProcessARPLine(p_value, TRUE); + } + + if (_stricmp(ptr,"ROUTE") == 0) + { + p_value[strlen(p_value)] = ' '; // put back together + ProcessROUTELine(p_value, TRUE); + return TRUE; + } + + if (_stricmp(ptr,"NAT") == 0) + { + PROUTEENTRY Route; + BOOL Found; + + if (OurIPAddr == 0) + { + WritetoConsoleLocal("NAT lines should follow IPAddr\n"); + return FALSE; + } + + if (!p_value) return FALSE; + ipad = inet_addr(p_value); + + if (ipad == INADDR_NONE) + return FALSE; + + p_host = strtok(NULL, " ,\t\n\r"); + if (!p_host) return FALSE; + + mappedipad = inet_addr(p_host); + if (mappedipad == INADDR_NONE) + return FALSE; + + p_origport = strtok(NULL, " ,\t\n\r"); + + if (p_origport && strcmp(p_origport, "TAP") == 0) + NATTAP = TRUE; + + // +// // Default is all ports +// +// if (p_origport) +// { +// p_port = strtok(NULL, " ,\t\n\r"); +// if (!p_port) return FALSE; +// +// port = atoi(p_origport); +// mappedport=atoi(p_port); +// } +// else + + port = mappedport = 0; + +#ifndef WIN32 + // on Linux, we send stuff for our host to TAP + + if (ipad == OurIPAddr || NATTAP) + nat_table[nat_table_len].ThisHost = TRUE; +#endif + nat_table[nat_table_len].origipaddr = ipad; + nat_table[nat_table_len].origport = ntohs(port); + nat_table[nat_table_len].mappedipaddr = mappedipad; + nat_table[nat_table_len++].mappedport = ntohs(mappedport); + + // Add a Host Route + + Route = LookupRoute(mappedipad, 0xffffffff, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Route != NULL) + { + Route->NETWORK = mappedipad ; + Route->SUBNET = 0xffffffff; + Route->GATEWAY = 0; + Route->LOCKED = 1; + Route->TYPE = 'E'; + } + } + return TRUE; + } + + if (_stricmp(ptr,"ENABLESNMP") == 0) + { + BPQSNMP = TRUE; + return TRUE; + } + // + // Bad line + // + return FALSE; + +} + +VOID DoARPTimer() +{ + PARPDATA Arp = NULL; + int i; + + for (i=0; i < NumberofARPEntries; i++) + { + Arp = ARPRecords[i]; + + if (!Arp->ARPVALID) + { + Arp->ARPTIMER--; + + if (Arp->ARPTIMER == 0) + { + // Retry Request + +// SendARPMsg(Arp); + RemoveARP(Arp); + } + continue; + } + + // Time out active entries + + if (Arp->LOCKED == 0) + { + Arp->ARPTIMER--; + + if (Arp->ARPTIMER == 0) + { + // Remove Entry + + RemoveARP(Arp); + SaveARP(); + } + } + } +} + +VOID DoRouteTimer() +{ + int i; + PROUTEENTRY Route; + time_t NOW = time(NULL); + + for (i=0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + if (Route->RIPTIMOUT) + Route->RIPTIMOUT--; + + if (Route->TYPE == 'T' && Route->RIPTIMOUT == 0 && Route->LOCKED == FALSE) + { + // Only remove Encap routes if we are still getting RIP44 messages, + // so we can keep going if UCSD stops sending updates, but can time + // out entries that are removed + + if ((NOW - LastRIP44Msg) < 3600) + { + RemoveRoute(Route); + return; // Will remove all eventually + } + } + } +} + +// PCAP Support Code + + +#ifdef WIN32 + +FARPROCX GetAddress(char * Proc) +{ + FARPROCX ProcAddr; + int err=0; + char buf[256]; + int n; + + + ProcAddr=(FARPROCX) GetProcAddress(PcapDriver,Proc); + + if (ProcAddr == 0) + { + err=GetLastError(); + + n=sprintf(buf,"Error finding %s - %d", Proc,err); + WritetoConsoleLocal(buf); + + return(0); + } + + return ProcAddr; +} + +FARPROCI GetAddressI(char * Proc) +{ + FARPROCI ProcAddr; + int err = 0; + char buf[256]; + int n; + + + ProcAddr = (FARPROCI)GetProcAddress(PcapDriver, Proc); + + if (ProcAddr == 0) + { + err = GetLastError(); + + n = sprintf(buf, "Error finding %s - %d", Proc, err); + WritetoConsoleLocal(buf); + + return(0); + } + + return ProcAddr; +} + +#endif + +void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data); + +int OpenPCAP() +{ + u_long param=1; + BOOL bcopt=TRUE; + int i=0; + char errbuf[PCAP_ERRBUF_SIZE]; + u_int netmask; + char packet_filter[256]; + struct bpf_program fcode; + char buf[256]; + int n; + +#ifndef MACBPQ + + /* Open the adapter */ + + adhandle= pcap_open_livex(Adapter, // name of the device + 65536, // portion of the packet to capture. + // 65536 grants that the whole packet will be captured on all the MACs. + Promiscuous, // promiscuous mode (nonzero means promiscuous) + 1, // read timeout + errbuf // error buffer + ); + + if (adhandle == NULL) + return FALSE; + + pcap_setnonblockx(adhandle, 1, errbuf); + + /* Check the link layer. We support only Ethernet for simplicity. */ + + if((int)pcap_datalinkx(adhandle) != DLT_EN10MB) + { + n=sprintf(buf,"\nThis program works only on Ethernet networks.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + netmask=0xffffff; + +// sprintf(packet_filter,"ether[12:2]=0x0800 or ether[12:2]=0x0806"); + + n = sprintf(packet_filter,"ether broadcast or ether dst %02x:%02x:%02x:%02x:%02x:%02x", + ourMACAddr[0], ourMACAddr[1], ourMACAddr[2], + ourMACAddr[3], ourMACAddr[4], ourMACAddr[5]); + + //compile the filter + + if (pcap_compilex(adhandle, &fcode, packet_filter, 1, netmask) <0 ) + { + n=sprintf(buf,"\nUnable to compile the packet filter. Check the syntax.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } + + //set the filter + + if (pcap_setfilterx(adhandle, &fcode)<0) + { + n=sprintf(buf,"\nError setting the filter.\n"); + WritetoConsoleLocal(buf); + + adhandle = 0; + return FALSE; + } +#endif + return TRUE; +} + + +VOID ReadARP() +{ + FILE *file; + char buf[256],errbuf[256]; + + if ((file = fopen(ARPFN,"r")) == NULL) return; + + while(fgets(buf, 255, file) != NULL) + { + strcpy(errbuf,buf); // save in case of error + + if (!ProcessARPLine(buf, FALSE)) + { + WritetoConsoleLocal("IP Gateway bad ARP record "); + WritetoConsoleLocal(errbuf); + } + + } + + fclose(file); + + return; +} + +BOOL ProcessARPLine(char * buf, BOOL Locked) +{ + char * p_ip, * p_mac, * p_port, * p_type; + int Port; + char Mac[7]; + char AXCall[64]; + BOOL Stay, Spy; + uint32_t IPAddr; + int a,b,c,d,e,f,num; + struct PORTCONTROL * PORT; + + PARPDATA Arp; + BOOL Found; + + _strupr(buf); // calls should be upper case + +// 192.168.0.131 GM8BPQ-13 1 D + + p_ip = strtok(buf, " \t\n\r"); + p_mac = strtok(NULL, " \t\n\r"); + p_port = strtok(NULL, " \t\n\r"); + p_type = strtok(NULL, " \t\n\r"); + + if(p_ip == NULL) return (TRUE); + + if(*p_ip =='#') return (TRUE); // comment + + if(*p_ip ==';') return (TRUE); // comment + + if (p_mac == NULL) return FALSE; + + if (p_port == NULL) return FALSE; + + if (p_type == NULL) return FALSE; + + IPAddr = inet_addr(p_ip); + + if (IPAddr == INADDR_NONE) return FALSE; + + _strupr(p_type); + + // Don't restore Eth addresses from the save file + + if (*p_type == 'E' && Locked == FALSE) + return TRUE; + + if (!((*p_type == 'D') || (*p_type == 'E') || (*p_type =='V'))) return FALSE; + + Port=atoi(p_port); + + if (p_mac == NULL) return (FALSE); + + if (*p_type == 'E') + { + num=sscanf(p_mac,"%x:%x:%x:%x:%x:%x",&a,&b,&c,&d,&e,&f); + + if (num != 6) return FALSE; + + Mac[0]=a; + Mac[1]=b; + Mac[2]=c; + Mac[3]=d; + Mac[4]=e; + Mac[5]=f; + + if (Port != 255) return FALSE; + } + else + { + if (DecodeCallString(p_mac, &Stay, &Spy, &AXCall[0]) == 0) + return FALSE; + + if (Port == 0 && *p_type !='V') // Port 0 for NETROM + return FALSE; + + if (Port) + { + PORT = GetPortTableEntryFromPortNum(Port); + + if (PORT == NULL) + return FALSE; + } + } + + Arp = LookupARP(IPAddr, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Arp != NULL) + { + Arp->IPADDR = IPAddr; + + if (*p_type == 'E') + { + memcpy(Arp->HWADDR, Mac, 6); + } + else + { + memcpy(Arp->HWADDR, AXCall, 64); + Arp->HWADDR[6] &= 0x7e; + } + Arp->ARPTYPE = *p_type; + Arp->ARPINTERFACE = Port; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = (Arp->ARPTYPE == 'E')? 300 : ARPTIMEOUT; + Arp->LOCKED = Locked; + + // Also add to Routes + + AddToRoutes(Arp, IPAddr, *p_type); + Arp->ARPROUTE->LOCKED = Locked; + + } + } + + return TRUE; +} + +VOID AddToRoutes(PARPDATA Arp, UINT IPAddr, char Type) +{ + PROUTEENTRY Route; + BOOL Found; + UINT IPMask = 0xffffffff; // All ARP rerived routes are Host Routes + + Route = LookupRoute(Arp->IPADDR, IPMask, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Route != NULL) + { + Route->NETWORK = IPAddr; + Route->SUBNET = IPMask; + Route->GATEWAY = IPAddr; // Host Route + Route->TYPE = Type; + } + } + + Arp->ARPROUTE = Route; + Route->ARP = Arp; // Crosslink Arp<>Routee + + // Sort into reverse mask order + + qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareMasks); +} + +// ROUTE 44.131.4.18/32 D GM8BPQ-7 1// Datagram?netrom/VC via Call !!!! No - this sohuld be an ARP entry + +// ROUTE 44.131.4.18/32 T n.n.n.n // Vis Tunnel Endpoint n.n.n.n +// ROUTE 44.131.4.18/32 E n.n.n.n // Via IP address over Ethernet + + +int CountDots(char * Addr) +{ + int Dots = 0; + + while(*Addr) + if (*(Addr++) == '.') + Dots++; + + return Dots; +} + + +BOOL ProcessROUTELine(char * buf, BOOL Locked) +{ + char * p_ip, * p_type, * p_mask, * p_gateway, * p_port; + uint32_t IPAddr, IPMask, IPGateway; + char Type = ' '; + int n, Port; + PROUTEENTRY Route; + BOOL Found; + +// ROUTE 44.131.4.18/31 44.131.4.1 // Normal Route +// ROUTE 44.131.4.18/31 1.2.3.4 T // Tunnel via 1.2.3.4 + + + p_ip = strtok(buf, " \t\n\r"); + p_gateway = strtok(NULL, " \t\n\r"); + p_type = strtok(NULL, " \t\n\r"); + + if (_stricmp(p_ip, "addprivate") == 0) + { + // From Encap.txt file + + p_ip = p_gateway; + + p_gateway = strtok(NULL, " \t\n\r"); + Type = 'T'; + p_type = NULL; + } + + if(p_ip == NULL) return (TRUE); + + if(*p_ip =='#') return (TRUE); // comment + + if(*p_ip ==';') return (TRUE); // comment + + if (p_gateway == NULL) return FALSE; + + if (p_type) + { + if (_stricmp(p_type, "UDPT") == 0) // Tunnelling over UDP + { + p_port = strtok(NULL, " \t\n\r"); + Type = 'U'; + + if (p_port) + Port = atoi(p_port); + } + else + { + Type = *p_type; + if (Type == 't') Type = 'T'; + } + } + + p_mask = strchr(p_ip, '/'); + + if (p_mask) + { + int Bits = atoi(p_mask + 1); + + if (Bits > 32) + Bits = 32; + + if (Bits == 0) + IPMask = 0; + else + IPMask = (0xFFFFFFFF) << (32 - Bits); + + *p_mask = 0; + } + else + IPMask = 32; // No mask means Host route + + IPMask = htonl(IPMask); // Needs to be Network order + + IPGateway = inet_addr(p_gateway); + + if (IPGateway == INADDR_NONE) return FALSE; + + // The encap.txt format omits trailing zeros. + + n = CountDots(p_ip); + + if (n == 2) + strcat(p_ip, ".0"); + else if (n == 1) + strcat(p_ip, ".0.0"); + else if (n == 0) + strcat(p_ip, ".0.0.0"); + + IPAddr = inet_addr(p_ip); + + if (IPAddr == INADDR_NONE) return FALSE; + + + Route = LookupRoute(IPAddr, IPMask, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Route != NULL) + { + Route->NETWORK = IPAddr; + Route->SUBNET = IPMask; + + if (Type == 'U') + { + Route->UDPADDR.sin_port = htons(Port); + Route->UDPADDR.sin_addr.s_addr = inet_addr(p_gateway); + Route->UDPADDR.sin_family = AF_INET; + } + else if (Type == 'T') + Route->Encap = IPGateway; + else + Route->GATEWAY = IPGateway; + + Route->TYPE = Type; + Route->LOCKED = Locked; + + // Link to ARP + + Route->ARP = LookupARP(Route->GATEWAY, FALSE, &Found); + } + } + return TRUE; +} + + +VOID SaveARP () +{ + PARPDATA Arp; + int i; + FILE * file; + + if ((file = fopen(ARPFN, "w")) == NULL) + return; + + for (i=0; i < NumberofARPEntries; i++) + { + Arp = ARPRecords[i]; + if (Arp->ARPVALID && !Arp->LOCKED) + WriteARPLine(Arp, file); + } + + fclose(file); + + return ; +} + +VOID WriteARPLine(PARPDATA ARPRecord, FILE * file) +{ + int SSID, Len, j; + char Mac[20]; + char Call[7]; + char IP[20]; + char Line[100]; + unsigned char work[4]; + + memcpy(work, &ARPRecord->IPADDR, 4); + + sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + if(ARPRecord->ARPINTERFACE == 255) // Ethernet + { + sprintf(Mac," %02x:%02x:%02x:%02x:%02x:%02x", + ARPRecord->HWADDR[0], + ARPRecord->HWADDR[1], + ARPRecord->HWADDR[2], + ARPRecord->HWADDR[3], + ARPRecord->HWADDR[4], + ARPRecord->HWADDR[5]); + } + else + { + for (j=0; j< 6; j++) + { + Call[j] = ARPRecord->HWADDR[j]/2; + if (Call[j] == 32) Call[j] = 0; + } + Call[6] = 0; + SSID = (ARPRecord->HWADDR[6] & 31)/2; + + sprintf(Mac,"%s-%d", Call, SSID); + } + + Len = sprintf(Line,"%s %s %d %c\n", + IP, Mac, ARPRecord->ARPINTERFACE, ARPRecord->ARPTYPE); + + fputs(Line, file); + + return; +} + +VOID ReadIPRoutes() +{ + PROUTEENTRY Route; + FILE * file; + char * Net; + char * Nexthop; + char * Encap; + char * Context; + char * Type; + char * p_mask; + + char Line[256]; + + uint32_t IPAddr, IPMask = 0xffffffff, IPGateway; + + BOOL Found; + + if ((file = fopen(IPRFN, "r")) == NULL) + return; + +// 44.0.0.1/32 0.0.0.0 T 1 8 encap 169.228.66.251 + + while(fgets(Line, 255, file) != NULL) + { + Net = strtok_s(Line, " \n", &Context); + if (Net == 0) continue; + + if (strcmp(Net, "ENCAPMAC") == 0) + { + int a,b,c,d,e,f,num; + + num=sscanf(Context,"%x:%x:%x:%x:%x:%x",&a,&b,&c,&d,&e,&f); + + if (num == 6) + { + RouterMac[0]=a; + RouterMac[1]=b; + RouterMac[2]=c; + RouterMac[3]=d; + RouterMac[4]=e; + RouterMac[5]=f; + } + continue;; + } + + Nexthop = strtok_s(NULL, " \n", &Context); + if (Nexthop == 0) continue; + + Type = strtok_s(NULL, " \n", &Context); + if (Type == 0) continue; + + p_mask = strlop(Net, '/'); + + if (p_mask) + { + int Bits = atoi(p_mask); + + if (Bits > 32) + Bits = 32; + + if (Bits == 0) + IPMask = 0; + else + IPMask = (0xFFFFFFFF) << (32 - Bits); + + IPMask = htonl(IPMask); // Needs to be Network order + } + + IPAddr = inet_addr(Net); + if (IPAddr == INADDR_NONE) continue; + + IPGateway = inet_addr(Nexthop); + if (IPGateway == INADDR_NONE) continue; + + if (Type[0] == 'T') + { + // Skip Metric, Time and "encap", get encap addr + + Encap = strtok_s(NULL, " \n", &Context); + Encap = strtok_s(NULL, " \n", &Context); + Encap = strtok_s(NULL, " \n", &Context); + Encap = strtok_s(NULL, " \n", &Context); + + if (Encap == 0) continue; + + IPGateway = inet_addr(Encap); + if (IPGateway == INADDR_NONE) continue; + + } + + Route = LookupRoute(IPAddr, IPMask, TRUE, &Found); + + if (!Found) + { + // Add if possible + + if (Route != NULL) + { + Route->NETWORK = IPAddr; + Route->SUBNET = IPMask; + + if (Type[0] == 'T') + Route->Encap = IPGateway; + else + Route->GATEWAY = IPGateway; + + Route->TYPE = Type[0]; + Route->RIPTIMOUT = 900; + + // Link to ARP + + if (Type[0] != 'T') + Route->ARP = LookupARP(Route->GATEWAY, FALSE, &Found); + } + } + } + + fclose(file); + return; +} + +VOID SaveIPRoutes () +{ + PROUTEENTRY Route; + int i; + FILE * file; + char Line[128]; + + if ((file = fopen(IPRFN, "w")) == NULL) + return; + + // Save Gateway MAC + + sprintf(Line,"ENCAPMAC %02x:%02x:%02x:%02x:%02x:%02x\n", + RouterMac[0], + RouterMac[1], + RouterMac[2], + RouterMac[3], + RouterMac[4], + RouterMac[5]); + + fputs(Line, file); + + for (i=0; i < NumberofRoutes; i++) + { + Route = RouteRecords[i]; + if (!Route->LOCKED) + WriteIPRLine(Route, file); + } + + fclose(file); + + return ; +} + +VOID WriteIPRLine(PROUTEENTRY RouteRecord, FILE * file) +{ + int Len; + char Net[20]; + char Nexthop[20]; + char Encap[20]; + + char Line[100]; + unsigned char work[4]; + + memcpy(work, &RouteRecord->NETWORK, 4); + sprintf(Net, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + memcpy(work, &RouteRecord->GATEWAY, 4); + sprintf(Nexthop, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + memcpy(work, &RouteRecord->Encap, 4); + sprintf(Encap, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + if (RouteRecord->TYPE == 'T') + Len = sprintf(Line, "%s/%d %s %c %d %d encap %s\n", + Net, CountBits(RouteRecord->SUBNET), + Nexthop, RouteRecord->TYPE, + RouteRecord->METRIC, RouteRecord->RIPTIMOUT, Encap); + else + Len = sprintf(Line, "%s/%d %s %c %d %d\n", + Net, CountBits(RouteRecord->SUBNET), + Nexthop, RouteRecord->TYPE, + RouteRecord->METRIC, RouteRecord->RIPTIMOUT); + + fputs(Line, file); + + return; +} + + + + + +int CheckSumAndSend(PIPMSG IPptr, PTCPMSG TCPmsg, USHORT Len) +{ + struct _IPMSG PH = {0}; + IPptr->IPCHECKSUM = 0; + + PH.IPPROTOCOL = 6; + PH.IPLENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &IPptr->IPDEST, 4); + + TCPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 20); + TCPmsg->CHECKSUM = Generate_CHECKSUM(TCPmsg, Len); + + // No need to do IP checksum as RouteIPMessage doesit + +// CHECKSUM IT + +// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + RouteIPMsg(IPptr); + return 0; +} + +int CheckSumAndSendUDP(PIPMSG IPptr, PUDPMSG UDPmsg, USHORT Len) +{ + struct _IPMSG PH = {0}; + + IPptr->IPCHECKSUM = 0; + + PH.IPPROTOCOL = 17; + PH.IPLENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &IPptr->IPDEST, 4); + + UDPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 20); + + UDPmsg->CHECKSUM = Generate_CHECKSUM(UDPmsg, Len); + + // No need to do IP checksum as ROuteIPMessage doesit + + // CHECKSUM IT + +// IPptr->IPCHECKSUM = Generate_CHECKSUM(IPptr, 20); + + RouteIPMsg(IPptr); + return 0; +} + +VOID RecalcTCPChecksum(PIPMSG IPptr) +{ + PTCPMSG TCPptr = (PTCPMSG)&IPptr->Data; + PHEADER PH = {0}; + USHORT Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + PH.IPPROTOCOL = 6; + PH.LENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &IPptr->IPDEST, 4); + + TCPptr->CHECKSUM = ~Generate_CHECKSUM(&PH, 12); + TCPptr->CHECKSUM = Generate_CHECKSUM(TCPptr, Len); +} + +VOID RecalcUDPChecksum(PIPMSG IPptr) +{ + PUDPMSG UDPmsg = (PUDPMSG)&IPptr->Data; + PHEADER PH = {0}; + USHORT Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + PH.IPPROTOCOL = 17; + PH.LENGTH = htons(Len); + memcpy(&PH.IPSOURCE, &IPptr->IPSOURCE, 4); + memcpy(&PH.IPDEST, &IPptr->IPDEST, 4); + + UDPmsg->CHECKSUM = ~Generate_CHECKSUM(&PH, 12); + UDPmsg->CHECKSUM = Generate_CHECKSUM(UDPmsg, Len); +} + +#ifndef WIN32 +#ifndef MACBPQ +#ifndef FREEBSD +#include +#include + +/* buffer for reading from tun/tap interface, must be >= 1500 */ + + +#define BUFSIZE 2000 + +/************************************************************************** + * tun_alloc: allocates or reconnects to a tun/tap device. The caller * + * must reserve enough space in *dev. * + **************************************************************************/ +int tun_alloc(char *dev, int flags) { + + struct ifreq ifr; + int fd, err; + char *clonedev = "/dev/net/tun"; + + if( (fd = open(clonedev , O_RDWR)) < 0 ) { + perror("Opening /dev/net/tun"); + return fd; + } + + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = flags; + + if (*dev) { + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + } + + if( (err = ioctl(fd, TUNSETIFF, (void *)&ifr)) < 0 ) { + perror("ioctl(TUNSETIFF)"); + close(fd); + return err; + } + + strcpy(dev, ifr.ifr_name); + return fd; +} + +#include +#include + + +void OpenTAP() +{ + int flags = IFF_TAP; + char if_name[IFNAMSIZ] = "LinBPQTAP"; + struct arpreq arpreq; + int s; + struct ifreq ifr; + int sockfd; + struct rtentry rm; + int err; + int n; + + uint nread, nwrite, plength; + char buffer[BUFSIZE]; + + int optval = 1; + + struct nat_table_entry * NAT = NULL; + int index; + + /* initialize tun/tap interface */ + + if ((tap_fd = tun_alloc(if_name, flags | IFF_NO_PI)) < 0 ) + { + printf("Error connecting to tun/tap interface %s!\n", if_name); + tap_fd = 0; + return; + } + + printf("Successfully connected to TAP interface %s\n", if_name); + + ioctl(tap_fd, FIONBIO, &optval); + + // Bring it up + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (sockfd < 0) + { + perror ("Socket"); + return; + } + + memset(&ifr, 0, sizeof ifr); + strncpy(ifr.ifr_name, if_name, IFNAMSIZ); + + ifr.ifr_flags |= IFF_UP; + + if ((err = ioctl(sockfd, SIOCSIFFLAGS, &ifr)) < 0) + { + perror("SIOCSIFFLAGS"); + printf("SIOCSIFFLAGS failed , ret->%d\n",err); + return; + } + + // Fix from github user isavitsky + + /* + * After some research I found that on most of my + * systems, including Raspberry Pi IV, a slight delay + * is needed before considering the TAP device + * up and running. Otherwise the interface structures + * do not initialise properly and later in the code + * around the line 4700 when we initialise our ARP + * structure: + * + * memcpy(Arp->HWADDR, xbuffer.ifr_hwaddr.sa_data, 6); + * + * the MAC address is getting filled in with random + * value which makes the communication via our TAP + * device using the configured IPADDR virtually + * impossible. + * + */ + + Debugprintf("Waiting for the TAP to become UP and RUNNING"); + + for (int i=10; i>0; i--) + { + Sleep(10); + + if ((err = ioctl(sockfd, SIOCGIFFLAGS, &ifr)) < 0) + { + perror("SIOCGIFFLAGS"); + return; + } + + if((ifr.ifr_flags & IFF_UP) && (ifr.ifr_flags & IFF_RUNNING)) + { + Debugprintf("TAP is UP and RUNNING"); + break; + } + else if (i == 1) + { + Debugprintf("TAP is still not UP and RUNNING"); + return; + } + } + + printf("TAP brought up\n"); + + // Set MTU to 256 + + memset(&ifr, 0, sizeof ifr); + strncpy(ifr.ifr_name, if_name, IFNAMSIZ); + + ifr.ifr_addr.sa_family = AF_INET; + ifr.ifr_mtu = 256; + + if (ioctl(sockfd, SIOCSIFMTU, (caddr_t)&ifr) < 0) + perror("Set MTU"); + else + printf("TAP MTU set to 256\n"); + + if (NoDefaultRoute == FALSE) + { + // Add a Route for 44/9 and 44.128/10 via TAP + + memset(&rm, 0, sizeof(rm)); + + (( struct sockaddr_in*)&rm.rt_dst)->sin_family = AF_INET; + (( struct sockaddr_in*)&rm.rt_dst)->sin_addr.s_addr = inet_addr("44.0.0.0"); + (( struct sockaddr_in*)&rm.rt_dst)->sin_port = 0; + + (( struct sockaddr_in*)&rm.rt_genmask)->sin_family = AF_INET; + (( struct sockaddr_in*)&rm.rt_genmask)->sin_addr.s_addr = inet_addr("255.128.0.0"); + (( struct sockaddr_in*)&rm.rt_genmask)->sin_port = 0; + + (( struct sockaddr_in*)&rm.rt_gateway)->sin_family = AF_INET; + (( struct sockaddr_in*)&rm.rt_gateway)->sin_addr.s_addr = 0; + (( struct sockaddr_in*)&rm.rt_gateway)->sin_port = 0; + + rm.rt_dev = if_name; + + rm.rt_flags = RTF_UP; // | RTF_GATEWAY; + + if ((err = ioctl(sockfd, SIOCADDRT, &rm)) < 0) + { + perror("SIOCADDRT"); + printf("SIOCADDRT failed , ret->%d\n",err); + return; + } + printf("Route to 44/9 added via LinBPQTAP\n"); + + memset(&rm, 0, sizeof(rm)); + + (( struct sockaddr_in*)&rm.rt_dst)->sin_family = AF_INET; + (( struct sockaddr_in*)&rm.rt_dst)->sin_addr.s_addr = inet_addr("44.128.0.0"); + (( struct sockaddr_in*)&rm.rt_dst)->sin_port = 0; + + (( struct sockaddr_in*)&rm.rt_genmask)->sin_family = AF_INET; + (( struct sockaddr_in*)&rm.rt_genmask)->sin_addr.s_addr = inet_addr("255.192.0.0"); + (( struct sockaddr_in*)&rm.rt_genmask)->sin_port = 0; + + (( struct sockaddr_in*)&rm.rt_gateway)->sin_family = AF_INET; + (( struct sockaddr_in*)&rm.rt_gateway)->sin_addr.s_addr = 0; + (( struct sockaddr_in*)&rm.rt_gateway)->sin_port = 0; + + rm.rt_dev = if_name; + + rm.rt_flags = RTF_UP; // | RTF_GATEWAY; + + if ((err = ioctl(sockfd, SIOCADDRT, &rm)) < 0) + { + perror("SIOCADDRT"); + printf("SIOCADDRT failed , ret->%d\n",err); + return; + } + printf("Route to 44.128/10 added via LinBPQTAP\n"); + + } + + // Set up ARP entries for any virtual hosts (eg jnos) + + bzero((caddr_t)&arpreq, sizeof(arpreq)); + + for (index=0; index < nat_table_len; index++) + { + struct sockaddr_in *psin; + psin = (struct sockaddr_in *)&arpreq.arp_pa; + NAT = &nat_table[index]; + + if (NAT->ThisHost && OurIPAddr != NAT->origipaddr) + { + printf("Adding ARP for %s\n", FormatIP(NAT->mappedipaddr)); + + psin->sin_family = AF_INET; + psin->sin_addr.s_addr = NAT->mappedipaddr; + + arpreq.arp_flags = ATF_PERM | ATF_COM | ATF_PUBL; + strcpy(arpreq.arp_dev, "LinBPQTAP"); + + if (ioctl(sockfd, SIOCSARP, (caddr_t)&arpreq) < 0) + perror("ARP IOCTL"); + } + } + + // Create LinBPQ ARP entry for real IP Address + + // Get Address + + struct ifreq xbuffer; + + memset(&xbuffer, 0x00, sizeof(xbuffer)); + + strcpy(xbuffer.ifr_name, "LinBPQTAP"); + + ioctl(sockfd, SIOCGIFHWADDR, &xbuffer); + + PARPDATA Arp; + PROUTEENTRY Route; + BOOL Found; + + Arp = LookupARP(HostNATAddr, TRUE, &Found); + + if (Arp != NULL) + { + Arp->IPADDR = HostNATAddr; + memcpy(Arp->HWADDR, xbuffer.ifr_hwaddr.sa_data, 6); + + Arp->ARPTYPE = 'E'; + Arp->ARPINTERFACE = 255; + Arp->ARPVALID = TRUE; + Arp->ARPTIMER = 0; + Arp->LOCKED = TRUE; + + // Also add to Routes + + AddToRoutes(Arp, HostNATAddr, 'E'); + Arp->ARPROUTE->LOCKED = TRUE; + } + + close(sockfd); +} +#endif +#endif +#endif + +extern struct DATAMESSAGE * REPLYBUFFER; +char * __cdecl Cmdprintf(TRANSPORTENTRY * Session, char * Bufferptr, const char * format, ...); + +VOID PING(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + // Send ICMP Echo Request + + uint32_t PingAddr; + UCHAR Msg[120] = ""; + PIPMSG IPptr = (PIPMSG)&Msg[40]; // Space for frame header (not used) + PICMPMSG ICMPptr = (PICMPMSG)&IPptr->Data; + time_t NOW = time(NULL); + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + if (IPRequired == FALSE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "IP Gateway is not enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + PingAddr = inet_addr(CmdTail); + + if (PingAddr == INADDR_NONE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "Invalid Address\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + // We keep the message pretty short in case running over RF + // Send "*BPQPINGID*, then Timestamp (Secs), then padding to 20 bytes + // So we can use same address for host we examine ping responses for + // the pattern, and intercept ours + + IPptr->VERLEN = 0x45; + IPptr->IPDEST.addr = PingAddr; + IPptr->IPSOURCE.addr = OurIPAddr; + IPptr->IPPROTOCOL = ICMP; + IPptr->IPTTL = IPTTL; + IPptr->FRAGWORD = 0; + IPptr->IPLENGTH = htons(48); // IP Header ICMP Header 20 Data + + ICMPptr->ICMPTYPE = 8; + ICMPptr->ICMPID = Session->CIRCUITINDEX; + strcpy(ICMPptr->ICMPData, "*BPQPINGID*"); // 12 including null + memcpy(&ICMPptr->ICMPData[12], &NOW, 4); + + ICMPptr->ICMPCHECKSUM = Generate_CHECKSUM(ICMPptr, 28); + + if (RouteIPMsg(IPptr)) + Bufferptr = Cmdprintf(Session, Bufferptr, "OK\r"); + else + Bufferptr = Cmdprintf(Session, Bufferptr, "No Route to Host\r"); + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + return; +} + +VOID SHOWARP(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + // DISPLAY IP Gateway ARP status or Clear + + int i; + PARPDATA ARPRecord, Arp; + int SSID, j, n; + char Mac[128]; + char Call[7]; + char IP[20]; + unsigned char work[4]; + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + if (IPRequired == FALSE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "IP Gateway is not enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + if (memcmp(CmdTail, "CLEAR ", 6) == 0) + { + int n = NumberofARPEntries; + int rec = 0; + + for (i=0; i < n; i++) + { + Arp = ARPRecords[rec]; + if (Arp->LOCKED) + rec++; + else + RemoveARP(Arp); + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "OK\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + SaveARP(); + + return; + } + + for (i=0; i < NumberofARPEntries; i++) + { + ARPRecord = ARPRecords[i]; + +// if (ARPRecord->ARPVALID) + { + memcpy(work, &ARPRecord->IPADDR, 4); + sprintf(IP, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + if(ARPRecord->ARPINTERFACE == 255) // Ethernet + { + sprintf(Mac," %02x:%02x:%02x:%02x:%02x:%02x", + ARPRecord->HWADDR[0], + ARPRecord->HWADDR[1], + ARPRecord->HWADDR[2], + ARPRecord->HWADDR[3], + ARPRecord->HWADDR[4], + ARPRecord->HWADDR[5]); + } + else + { + UCHAR * AXCall = &ARPRecord->HWADDR[0]; + n = 0; + + while (AXCall[0]) + { + + for (j=0; j< 6; j++) + { + Call[j] = AXCall[j]/2; + if (Call[j] == 32) Call[j] = 0; + } + + Call[j] = 0; + SSID = (AXCall[6] & 31)/2; + + if (SSID) + n += sprintf(&Mac[n], " %s-%d", Call, SSID); + else + n += sprintf(&Mac[n], " %s", Call); + + AXCall += 7; + } + } + Bufferptr = Cmdprintf(Session, Bufferptr, "%s%s %d %c %d %s\r", + IP, Mac, ARPRecord->ARPINTERFACE, ARPRecord->ARPTYPE, + (int)ARPRecord->ARPTIMER, ARPRecord->LOCKED?"Locked":""); + } + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +VOID SHOWNAT(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + // DISPLAY IP Gateway ARP status or Clear + + struct nat_table_entry * NAT = NULL; + int index; + char From[20]; + char To[20]; + + Bufferptr = Cmdprintf(Session, Bufferptr, "\r"); + + if (IPRequired == FALSE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "IP Gateway is not enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + for (index=0; index < nat_table_len; index++) + { + NAT = &nat_table[index]; + + strcpy(From, FormatIP(NAT->origipaddr)); + strcpy(To, FormatIP(NAT->mappedipaddr)); + + +#ifdef LINBPQ + Bufferptr = Cmdprintf(Session, Bufferptr, "%s to %s %s\r", From, To, + NAT->ThisHost?"via TAP":""); +#else + Bufferptr = Cmdprintf(Session, Bufferptr, "%s to %s\r", From, To); +#endif + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); +} + +int CountBits64(uint64_t in) +{ + int n = 0; + while (in) + { + if (in & 1) n ++; + in >>=1; + } + return n; +} + +int CountBits(uint32_t in) +{ + int n = 0; + while (in) + { + if (in & 1) n ++; + in >>=1; + } + return n; +} + +VOID SHOWIPROUTE(TRANSPORTENTRY * Session, char * Bufferptr, char * CmdTail, struct CMDX * CMD) +{ + // DISPLAY IP Gateway ARP status or Clear + + int i; + PROUTEENTRY RouteRecord; + char Net[20]; + char Nexthop[20]; + char Encap[20]; + char *Context; + char Reply[128]; + char UCReply[128]; + + unsigned char work[4]; + + if (IPRequired == FALSE) + { + Bufferptr = Cmdprintf(Session, Bufferptr, "\rIP Gateway is not enabled\r"); + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + return; + } + + Bufferptr = Cmdprintf(Session, Bufferptr, "%d Entries\r", NumberofRoutes); + + if (NumberofRoutes) + qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareIPRoutes); + + for (i=0; i < NumberofRoutes; i++) + { + RouteRecord = RouteRecords[i]; + +// if (RouteRecord->ARPVALID) + { + memcpy(work, &RouteRecord->NETWORK, 4); + sprintf(Net, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + memcpy(work, &RouteRecord->GATEWAY, 4); + sprintf(Nexthop, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + memcpy(work, &RouteRecord->Encap, 4); + sprintf(Encap, "%d.%d.%d.%d", work[0], work[1], work[2], work[3]); + + if (RouteRecord->TYPE == 'T') + sprintf(Reply, "%s/%d %d %c %d %d encap %s\r", + Net, CountBits(RouteRecord->SUBNET), + RouteRecord->FRAMECOUNT, RouteRecord->TYPE, + RouteRecord->METRIC, RouteRecord->RIPTIMOUT, Encap); + else + sprintf(Reply, "%s/%d %d %s %c %d %d %s\r", + Net, CountBits(RouteRecord->SUBNET), + RouteRecord->FRAMECOUNT, Nexthop, RouteRecord->TYPE, + RouteRecord->METRIC, RouteRecord->RIPTIMOUT, + RouteRecord->LOCKED?"Locked":""); + } + + // Treat any parameter as a "Find Filter" + + CmdTail = strtok_s(CmdTail, " ", &Context); + strcpy(UCReply, Reply); + _strupr(UCReply); + + if (CmdTail && CmdTail[0] && strstr(UCReply, CmdTail) == 0) + continue; + + Bufferptr = Cmdprintf(Session, Bufferptr, "%s", Reply); + } + + SendCommandReply(Session, REPLYBUFFER, (int)(Bufferptr - (char *)REPLYBUFFER)); + + if (NumberofRoutes) + qsort(RouteRecords, NumberofRoutes, sizeof(void *), CompareMasks); // Back to Maks order + +} + +// SNMP Support Code. Pretty limited - basically just for MRTG + +/* +Primitive ASN.1 Types Identifier in hex +INTEGER 02 +BIT STRING 03 +OCTET STRING 04 +NULL 05 +OBJECT IDENTIFIER 06 + +Constructed ASN.1 type Identifier in hex +SEQUENCE 30 + +Primitive SNMP application types Identifier in hex +IpAddress 40 +Opaque 44 +NsapAddress 45 +Counter64 (available only in SNMPv2) 46 +Uinteger32 (available only in SNMPv2) 47 + +Context-specific types within an SNMP Message Identifier in hex +GetRequest-PDU A0 +GetNextRequestPUD A1 +GetResponse-PDU (Response-PDU in SNMPv 2) A2 +SetRequest-PDU A3 +Trap-PDU (obsolete in SNMPv 2) A4 +GetBulkRequest-PDU (added in SNMPv 2) A5 +InformRequest-PDU (added in SNMPv 2) A6 +SNMPv2-Trap-PDU (added in SNMPv 2) A7 + +*/ + +#define Counter32 0x41 +#define Gauge32 0x42 +#define TimeTicks 0x43 + + + +UCHAR ifInOctets[] = {'+',6,1,2,1,2,2,1,10}; +UCHAR ifOutOctets[] = {'+',6,1,2,1,2,2,1,16}; + +int ifInOctetsLen = 9; // Not Inc Port +int ifOutOctetsLen = 9; // Not Inc Port + +UCHAR sysUpTime[] = {'+', 6,1,2,1,1,3,0}; +int sysUpTimeLen = 8; + +UCHAR sysName[] = {'+', 6,1,2,1,1,5,0}; +int sysNameLen = 8; + +extern time_t TimeLoaded; + +int InOctets[64] = {0}; +int OutOctets[64] = {0}; + +// ASN PDUs have to be constructed backwards, as each header included a length + +// This code assumes we have enough space in front of the buffer. + +int ASNGetInt(UCHAR * Msg, int Len) +{ + int Val = 0; + + while(Len) + { + Val = (Val << 8) + *(Msg++); + Len --; + } + + return Val; +} + + +int ASNPutInt(UCHAR * Buffer, int Offset, unsigned int Val, int Type) +{ + int Len = 0; + + // Encode in minimum space. But servers seem to sign-extend top byte, so if top bit set add another zero; + + // I think zero is sent as a zero length field + + if (Val == 0) + { + // return zero as 01 00, not zero length string + + Buffer[--Offset] = 0; // Val + Buffer[--Offset] = 1; // Len + Buffer[--Offset] = Type; + return 3; + } + + while(Val) + { + Buffer[--Offset] = Val & 255; // Value + Val = Val >> 8; + Len++; + } + + if (Len < 4 && (Buffer[Offset] & 0x80)) // Negative + { + Buffer[--Offset] = 0; + Len ++; + } + + Buffer[--Offset] = Len; // Len + Buffer[--Offset] = Type; + + return Len + 2; +} + + +int AddHeader(UCHAR * Buffer, int Offset, UCHAR Type, int Length) +{ + Buffer[Offset - 2] = Type; + Buffer[Offset - 1] = Length; + + return 2; +} + +int BuildReply(UCHAR * Buffer, int Offset, UCHAR * OID, int OIDLen, UCHAR * Value, int ReqID) +{ + int IDLen; + int ValLen = Value[1] + 2; + + // Value is pre-encoded = type, len, data + + // Contruct the Varbindings. Sequence OID Value + + Offset -= ValLen; + memcpy(&Buffer[Offset], Value, ValLen); + Offset -= OIDLen; + memcpy(&Buffer[Offset], OID, OIDLen); + Buffer[--Offset] = OIDLen; + Buffer[--Offset] = 6; // OID Type + + Buffer[--Offset] = OIDLen + ValLen + 2; + Buffer[--Offset] = 48; // Sequence + + Buffer[--Offset] = OIDLen + ValLen + 4; + Buffer[--Offset] = 48; // Sequence + + // Add the error fields (two zero ints + + Buffer[--Offset] = 0; // Value + Buffer[--Offset] = 1; // Len + Buffer[--Offset] = 2; // Int + + Buffer[--Offset] = 0; // Value + Buffer[--Offset] = 1; // Len + Buffer[--Offset] = 2; // Int + + // ID + + IDLen = ASNPutInt(Buffer, Offset, ReqID, 2); + Offset -= IDLen; + + // PDU Type + + Buffer[--Offset] = OIDLen + ValLen + 12 + IDLen; + Buffer[--Offset] = 0xA2; // Len + + return OIDLen + ValLen + 14 + IDLen; +} + + + +// snmpget -v1 -c jnos [ve4klm.ampr.org | www.langelaar.net] 1.3.6.1.2.1.2.2.1.16.5 + +int ProcessSNMPPayload(UCHAR * Msg, int Len, UCHAR * Reply, int * OffPtr) +{ + char Community[256]; + UCHAR OID[256]; + int OIDLen; + int Type; + int Length, ComLen; + int IntVal; + int ReqID; + int RequestType; + + + // ASN 1 Encoding - Type, Len, Data + + while (Len > 0) + { + Type = *(Msg++); + Length = *(Msg++); + + // First should be a Sequence + + if (Type != 0x30) + return 0; + + Len -= 2; + + Type = *(Msg++); + Length = *(Msg++); + IntVal = *(Msg++); + + // Should be Integer - SNMP Version - We support V1, identified by zero + + if (Type != 2 || Length != 1 || IntVal != 0) + return 0; + + Len -= 3; + + Type = *(Msg++); + ComLen = *(Msg++); + + // Should Be String (community) + + if (Type != 4) + return 0; + + memcpy(Community, Msg, ComLen); + Community[ComLen] = 0; + + Len -=2; // Header + Len -= ComLen; + + Msg += (ComLen); + + // A Complex Data Types - GetRequest PDU etc + + RequestType = *(Msg); + *(Msg++) = 0xA2; + Length = *(Msg++); + + Len -= 2; + + // A 2 byte value requestid + + // Next is integer requestid + + Type = *(Msg++); + Length = *(Msg++); + + if (Type != 2) + return 0; + + ReqID = ASNGetInt(Msg, Length); + + Len -= (2 + Length); + Msg += Length; + + // Two more Integers - error status, error index + + Type = *(Msg++); + Length = *(Msg++); + + if (Type != 2) + return 0; + + ASNGetInt(Msg, Length); + + Len -= (2 + Length); + Msg += Length; + + Type = *(Msg++); + Length = *(Msg++); + + if (Type != 2) + return 0; + + ASNGetInt(Msg, Length); + + Len -= (2 + Length); + Msg += Length; + + // Two Variable-bindings structs - another Sequence + + Type = *(Msg++); + Length = *(Msg++); + + Len -= 2; + + if (Type != 0x30) + return 0; + + Type = *(Msg++); + Length = *(Msg++); + + Len -= 2; + + if (Type != 0x30) + return 0; + + // Next is OID + + Type = *(Msg++); + Length = *(Msg++); + + if (Type != 6) // Object ID + return 0; + + memcpy(OID, Msg, Length); + OID[Length] = 0; + + OIDLen = Length; + + Len -=2; // Header + Len -= Length; + + Msg += Length; + + // Should Just have a null value left + + Type = *(Msg++); + Length = *(Msg++); + + if (Type != 5 || Length != 0) + return 0; + + Len -=2; // Header + + // Should be nothing left + } + + if (RequestType == 160) + { + int Offset = 255; + int PDULen = 0; + char Value[256]; + int ValLen; + + // Only Support Get + + if (memcmp(OID, sysName, sysNameLen) == 0) + { + ValLen = (int)strlen(MYNODECALL);; + Value[0] = 4; // String + Value[1] = ValLen; + memcpy(&Value[2], MYNODECALL, ValLen); + + PDULen = BuildReply(Reply, Offset, sysName, sysNameLen, Value, ReqID); + } + else if (memcmp(OID, sysUpTime, sysUpTimeLen) == 0) + { + int ValOffset = 10; + ValLen = ASNPutInt(Value, ValOffset, (int)((time(NULL) - TimeLoaded) * 100), TimeTicks); + ValOffset -= ValLen; + + PDULen = BuildReply(Reply, Offset, sysUpTime, sysUpTimeLen, &Value[ValOffset], ReqID); + } + else if (memcmp(OID, ifOutOctets, ifOutOctetsLen) == 0) + { + int Port = OID[9]; + int ValOffset = 10; + ValLen = ASNPutInt(Value, ValOffset, OutOctets[Port], Counter32); + ValOffset -= ValLen; + PDULen = BuildReply(Reply, Offset, OID, OIDLen, &Value[ValOffset], ReqID); + + } + else if (memcmp(OID, ifInOctets, ifInOctetsLen) == 0) + { + int Port = OID[9]; + int ValOffset = 10; + ValLen = ASNPutInt(Value, ValOffset, InOctets[Port], Counter32); + ValOffset -= ValLen; + PDULen = BuildReply(Reply, Offset, OID, OIDLen, &Value[ValOffset], ReqID); + + } + else + return 0; + + Offset -= PDULen; + Offset -= ComLen; + + memcpy(&Reply[Offset], Community, ComLen); + Reply[--Offset] = ComLen; + Reply[--Offset] = 4; + + // Version + + Reply[--Offset] = 0; + Reply[--Offset] = 1; + Reply[--Offset] = 2; + + Reply[--Offset] = PDULen + ComLen + 5; + Reply[--Offset] = 48; + + *OffPtr = Offset; + + return PDULen + ComLen + 7; + } + return 0; +} + +VOID ProcessSNMPMessage(PIPMSG IPptr) +{ + int Len; + PUDPMSG UDPptr = (PUDPMSG)&IPptr->Data; + UCHAR * Msg; + UCHAR Reply[256]; + int SendLen; + int Offset = 0; + + Len = ntohs(IPptr->IPLENGTH); + Len-=20; + + Check_Checksum(UDPptr, Len); + + // 4 bytes version + // Null Terminated Community + + Msg = (char *) UDPptr; + + Msg += 8; // Over UDP Header + Len -= 8; + + SendLen = ProcessSNMPPayload(Msg, Len, Reply, &Offset); + + if (SendLen == 0) + return; + + memcpy(UDPptr->UDPData, &Reply[Offset], SendLen); + + // Swap Dest to Origin + + IPptr->IPDEST = IPptr->IPSOURCE; + + IPptr->IPSOURCE.addr = OurIPAddr; + + UDPptr->DESTPORT = UDPptr->SOURCEPORT; + UDPptr->SOURCEPORT = htons(161); + SendLen += 8; // UDP Header + UDPptr->LENGTH = htons(SendLen); + IPptr->IPLENGTH = htons(SendLen + 20); + + CheckSumAndSendUDP(IPptr, UDPptr, SendLen); +} + + + diff --git a/.svn/pristine/87/87366a323d6256425d8439a9df5e9b3d58e6a40c.svn-base b/.svn/pristine/87/87366a323d6256425d8439a9df5e9b3d58e6a40c.svn-base new file mode 100644 index 0000000..689b5f4 --- /dev/null +++ b/.svn/pristine/87/87366a323d6256425d8439a9df5e9b3d58e6a40c.svn-base @@ -0,0 +1,2172 @@ +/* +Copyright 2001-2018 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 +*/ + +// Control Routine for LinBPQ + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" +#include "bpqmail.h" +#ifdef WIN32 +#include +//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h" +#else +#include +#include +#ifndef MACBPQ +#ifndef FREEBSD +#include +#endif +#endif +#endif + +#include "time.h" + +#define Connect(stream) SessionControl(stream,1,0) +#define Disconnect(stream) SessionControl(stream,2,0) +#define ReturntoNode(stream) SessionControl(stream,3,0) +#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) + +BOOL APIENTRY Rig_Init(); + + + +#define GetSemaphore(Semaphore,ID) _GetSemaphore(Semaphore, ID, __FILE__, __LINE__) + +void _GetSemaphore(struct SEM * Semaphore, int ID, char * File, int Line); +void FreeSemaphore(struct SEM * Semaphore); +VOID CopyConfigFile(char * ConfigName); +VOID SendMailForThread(VOID * Param); +VOID GetUIConfig(); +Dll BOOL APIENTRY Init_IP(); +VOID OpenReportingSockets(); +VOID SetupNTSAliases(char * FN); +int DeleteRedundantMessages(); +BOOL InitializeTNCEmulator(); +VOID FindLostBuffers(); +VOID IPClose(); +DllExport BOOL APIENTRY Rig_Close(); +Dll BOOL APIENTRY Poll_IP(); +BOOL Rig_Poll(); +BOOL Rig_Poll(); +VOID CheckWL2KReportTimer(); +VOID TNCTimer(); +VOID SendLocation(); +int ChatPollStreams(); +void ChatTrytoSend(); +VOID BBSSlowTimer(); +int GetHTMLForms(); +char * AddUser(char * Call, char * password, BOOL BBSFlag); +VOID SaveChatConfigFile(char * ConfigName); +VOID SaveMH(); +int upnpClose(); +void SaveAIS(); +void initAIS(); +void DRATSPoll(); +void RHPPoll(); + +VOID GetPGConfig(); +void SendBBSDataToPktMap(); + +extern uint64_t timeLoadedMS; + +BOOL IncludesMail = FALSE; +BOOL IncludesChat = FALSE; + +BOOL RunMail = FALSE; +BOOL RunChat = FALSE; +BOOL needAIS= FALSE; +BOOL needADSB = FALSE; + +int CloseOnError = 0; + +VOID Poll_AGW(); +BOOL AGWAPIInit(); +int AGWAPITerminate(); + +BOOL AGWActive = FALSE; + +extern int AGWPort; + +BOOL RigActive = FALSE; + +extern ULONG ChatApplMask; +extern char Verstring[]; + +extern char SignoffMsg[]; +extern char AbortedMsg[]; +extern char InfoBoxText[]; // Text to display in Config Info Popup + +extern int LastVer[4]; // In case we need to do somthing the first time a version is run + +extern HWND MainWnd; +extern char BaseDir[]; +extern char BaseDirRaw[]; +extern char MailDir[]; +extern char WPDatabasePath[]; +extern char RlineVer[50]; + +extern BOOL LogBBS; +extern BOOL LogCHAT; +extern BOOL LogTCP; +extern BOOL ForwardToMe; + +extern int LatestMsg; +extern char BBSName[]; +extern char SYSOPCall[]; +extern char BBSSID[]; +extern char NewUserPrompt[]; + +extern int NumberofStreams; +extern int MaxStreams; +extern ULONG BBSApplMask; +extern int BBSApplNum; +extern int ChatApplNum; +extern int MaxChatStreams; + +extern int NUMBEROFTNCPORTS; + +extern int EnableUI; + +extern BOOL AUTOSAVEMH; + +extern FILE * LogHandle[4]; + +#define MaxSockets 64 + +extern ConnectionInfo Connections[MaxSockets+1]; + +time_t LastTrafficTime; +extern int MaintTime; + +#define LOG_BBS 0 +#define LOG_CHAT 1 +#define LOG_TCP 2 +#define LOG_DEBUG_X 3 + +int _MYTIMEZONE = 0; + +// flags equates + +#define F_Excluded 0x0001 +#define F_LOC 0x0002 +#define F_Expert 0x0004 +#define F_SYSOP 0x0008 +#define F_BBS 0x0010 +#define F_PAG 0x0020 +#define F_GST 0x0040 +#define F_MOD 0x0080 +#define F_PRV 0x0100 +#define F_UNP 0x0200 +#define F_NEW 0x0400 +#define F_PMS 0x0800 +#define F_EMAIL 0x1000 +#define F_HOLDMAIL 0x2000 +#define F_POLLRMS 0x4000 +#define F_SYSOP_IN_LM 0x8000 +#define F_Temp_B2_BBS 0x00010000 + +/* #define F_PWD 0x1000 */ + + +extern UCHAR BPQDirectory[260]; +extern UCHAR LogDirectory[260]; +extern UCHAR ConfigDirectory[260]; + +// overrides from params +UCHAR LogDir[260] = ""; +UCHAR ConfigDir[260] = ""; +UCHAR DataDir[260] = ""; + + +BOOL GetConfig(char * ConfigName); +VOID DecryptPass(char * Encrypt, unsigned char * Pass, unsigned int len); +int EncryptPass(char * Pass, char * Encrypt); +int APIENTRY FindFreeStream(); +int PollStreams(); +int APIENTRY SetAppl(int stream, int flags, int mask); +int APIENTRY SessionState(int stream, int * state, int * change); +int APIENTRY SessionControl(int stream, int command, int Mask); + +BOOL ChatInit(); +VOID CloseChat(); +VOID CloseTNCEmulator(); + +static config_t cfg; +static config_setting_t * group; + +BOOL MonBBS = TRUE; +BOOL MonCHAT = TRUE; +BOOL MonTCP = TRUE; + +BOOL LogBBS = TRUE; +BOOL LogCHAT = TRUE; +BOOL LogTCP = TRUE; + +extern BOOL LogAPRSIS; + +BOOL UIEnabled[33]; +BOOL UINull[33]; +char * UIDigi[33]; + +extern struct UserInfo ** UserRecPtr; +extern int NumberofUsers; + +extern struct UserInfo * BBSChain; // Chain of users that are BBSes + +extern struct MsgInfo ** MsgHddrPtr; +extern int NumberofMessages; + +extern int FirstMessageIndextoForward; // Lowest Message wirh a forward bit set - limits search + +extern char UserDatabaseName[MAX_PATH]; +extern char UserDatabasePath[MAX_PATH]; + +extern char MsgDatabasePath[MAX_PATH]; +extern char MsgDatabaseName[MAX_PATH]; + +extern char BIDDatabasePath[MAX_PATH]; +extern char BIDDatabaseName[MAX_PATH]; + +extern char WPDatabasePath[MAX_PATH]; +extern char WPDatabaseName[MAX_PATH]; + +extern char BadWordsPath[MAX_PATH]; +extern char BadWordsName[MAX_PATH]; + +extern char NTSAliasesPath[MAX_PATH]; +extern char NTSAliasesName[MAX_PATH]; + +extern char BaseDir[MAX_PATH]; +extern char BaseDirRaw[MAX_PATH]; // As set in registry - may contain %NAME% +extern char ProperBaseDir[MAX_PATH]; // BPQ Directory/BPQMailChat + +extern char MailDir[MAX_PATH]; + +extern time_t MaintClock; // Time to run housekeeping + +#ifdef WIN32 +BOOL KEEPGOING = 30; // 5 secs to shut down +#else +BOOL KEEPGOING = 50; // 5 secs to shut down +#endif +BOOL Restarting = FALSE; +BOOL CLOSING = FALSE; + +int ProgramErrors; +int Slowtimer = 0; + +#define REPORTINTERVAL 15 * 549; // Magic Ticks Per Minute for PC's nominal 100 ms timer +int ReportTimer = 0; + +// Console Terminal Support + +struct ConTermS +{ + int BPQStream; + BOOL Active; + int Incoming; + + char kbbuf[INPUTLEN]; + int kbptr; + + char * KbdStack[MAXSTACK]; + int StackIndex; + + BOOL CONNECTED; + int SlowTimer; +}; + +struct ConTermS ConTerm = {0, 0}; + + +VOID CheckProgramErrors() +{ + if (Restarting) + exit(0); // Make sure can't loop in restarting + + ProgramErrors++; + + if (ProgramErrors > 25) + { + Restarting = TRUE; + + Logprintf(LOG_DEBUG_X, NULL, '!', "Too Many Program Errors - Closing"); + +/* + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + GetModuleFileName(NULL, ProgName, 256); + + Debugprintf("Attempting to Restart %s", ProgName); + + CreateProcess(ProgName, "MailChat.exe WAIT", NULL, NULL, FALSE, 0, NULL, NULL, &SInfo, &PInfo); +*/ + exit(0); + } +} + +#ifdef WIN32 + +BOOL CtrlHandler(DWORD fdwCtrlType) +{ + switch( fdwCtrlType ) + { + // Handle the CTRL-C signal. + case CTRL_C_EVENT: + printf( "Ctrl-C event\n\n" ); + CLOSING = TRUE; + Beep( 750, 300 ); + return( TRUE ); + + // CTRL-CLOSE: confirm that the user wants to exit. + case CTRL_CLOSE_EVENT: + + CLOSING = TRUE; + printf( "Ctrl-Close event\n\n" ); + Sleep(20000); + Beep( 750, 300 ); + return( TRUE ); + + // Pass other signals to the next handler. + case CTRL_BREAK_EVENT: + Beep( 900, 200 ); + printf( "Ctrl-Break event\n\n" ); + CLOSING = TRUE; + Beep( 750, 300 ); + return FALSE; + + case CTRL_LOGOFF_EVENT: + Beep( 1000, 200 ); + printf( "Ctrl-Logoff event\n\n" ); + return FALSE; + + case CTRL_SHUTDOWN_EVENT: + Beep( 750, 500 ); + printf( "Ctrl-Shutdown event\n\n" ); + CLOSING = TRUE; + Beep( 750, 300 ); + return FALSE; + + default: + return FALSE; + } +} + +#else + +#include +#include + +// Linux Signal Handlers +static void segvhandler(int sig) +{ + void *array[10]; + size_t size; + char msg[] = "\nSIGSEGV Received\n"; + + write(STDERR_FILENO, msg, strlen(msg)); + + // get void*'s for all entries on the stack + size = backtrace(array, 10); + + // print out all the frames to stderr + + backtrace_symbols_fd(array, size, STDERR_FILENO); + + write(STDOUT_FILENO, msg, strlen(msg)); + backtrace_symbols_fd(array, size, STDOUT_FILENO); + + exit(1); +} + +static void abrthandler(int sig) +{ + void *array[10]; + size_t size; + char msg[] = "\nSIGABRT Received\n"; + + write(STDERR_FILENO, msg, strlen(msg)); + write(STDOUT_FILENO, msg, strlen(msg)); + + // get void*'s for all entries on the stack + + size = backtrace(array, 10); + backtrace_symbols_fd(array, size, STDERR_FILENO); + + write(STDOUT_FILENO, msg, strlen(msg)); + backtrace_symbols_fd(array, size, STDOUT_FILENO); + + exit(1); +} + + +static void sigterm_handler(int sig) +{ + syslog(LOG_INFO, "terminating on SIGTERM\n"); + CLOSING = TRUE; +} + +static void sigint_handler(int sig) +{ + printf("terminating on SIGINT\n"); + CLOSING = TRUE; +} + + +static void sigusr1_handler(int sig) +{ + signal(SIGUSR1, sigusr1_handler); +} + +#endif + + +#ifndef WIN32 + +BOOL CopyFile(char * In, char * Out, BOOL Failifexists) +{ + FILE * Handle; + DWORD FileSize; + char * Buffer; + struct stat STAT; + + if (stat(In, &STAT) == -1) + return FALSE; + + FileSize = STAT.st_size; + + Handle = fopen(In, "rb"); + + if (Handle == NULL) + return FALSE; + + Buffer = malloc(FileSize+1); + + FileSize = fread(Buffer, 1, STAT.st_size, Handle); + + fclose(Handle); + + if (FileSize != STAT.st_size) + { + free(Buffer); + return FALSE; + } + + Handle = fopen(Out, "wb"); + + if (Handle == NULL) + { + free(Buffer); + return FALSE; + } + + FileSize = fwrite(Buffer, 1, STAT.st_size, Handle); + + fclose(Handle); + free(Buffer); + + return TRUE; +} +#endif + +int RefreshMainWindow() +{ + return 0; +} + +int LastSemGets = 0; + +extern int SemHeldByAPI; + +VOID MonitorThread(void * x) +{ + // Thread to detect stuck semaphore + + do + { + if ((Semaphore.Gets == LastSemGets) && Semaphore.Flag) + { + // It is stuck - try to release + + Debugprintf ("Semaphore locked - Process ID = %d, Held By %d from %s Line %d", + Semaphore.SemProcessID, SemHeldByAPI, Semaphore.File, Semaphore.Line); + + + Semaphore.Flag = 0; + } + + LastSemGets = Semaphore.Gets; + + Sleep(30000); +// Debugprintf("Monitor Thread Still going %d %d %d %x %d", LastSemGets, Semaphore.Gets, Semaphore.Flag, Semaphore.SemThreadID, SemHeldByAPI); + + } + while (TRUE); +} + + + + +VOID TIMERINTERRUPT(); + +BOOL Start(); +VOID INITIALISEPORTS(); +Dll BOOL APIENTRY Init_APRS(); +VOID APRSClose(); +Dll VOID APIENTRY Poll_APRS(); +VOID HTTPTimer(); + + +#define CKernel +#include "Versions.h" + +extern struct SEM Semaphore; + +int SemHeldByAPI = 0; +BOOL IGateEnabled = TRUE; +BOOL APRSActive = FALSE; +BOOL ReconfigFlag = FALSE; +BOOL APRSReconfigFlag = FALSE; +BOOL RigReconfigFlag = FALSE; + +BOOL IPActive = FALSE; +extern BOOL IPRequired; + +extern struct WL2KInfo * WL2KReports; + +int InitDone; +char pgm[256] = "LINBPQ"; + +char SESSIONHDDR[80] = ""; +int SESSHDDRLEN = 0; + + +// Next 3 should be uninitialised so they are local to each process + +UCHAR MCOM; +UCHAR MUIONLY; +UCHAR MTX; +uint64_t MMASK; + + +UCHAR AuthorisedProgram; // Local Variable. Set if Program is on secure list + +int SAVEPORT = 0; + +VOID SetApplPorts(); + +char VersionString[50] = Verstring; +char VersionStringWithBuild[50]=Verstring; +int Ver[4] = {Vers}; +char TextVerstring[50] = Verstring; + +extern UCHAR PWLen; +extern char PWTEXT[]; +extern int ISPort; + +extern char ChatConfigName[250]; + +BOOL EventsEnabled = 0; + +UCHAR * GetBPQDirectory() +{ + return BPQDirectory; +} +UCHAR * GetLogDirectory() +{ + return LogDirectory; +} +extern int POP3Timer; + +// Console Terminal Stuff + +#ifndef WIN32 + + + +#define _getch getchar + +/** + Linux (POSIX) implementation of _kbhit(). + Morgan McGuire, morgan@cs.brown.edu + */ + +#include +#include +#include + +int _kbhit() { + static const int STDIN = 0; + static int initialized = 0; + + if (! initialized) { + // Use termios to turn off line buffering + struct termios term; + tcgetattr(STDIN, &term); + term.c_lflag &= ~ICANON; + + tcsetattr(STDIN, TCSANOW, &term); + setbuf(stdin, NULL); + initialized = 1; + } + + int bytesWaiting; + ioctl(STDIN, FIONREAD, &bytesWaiting); + return bytesWaiting; +} + +#endif + +void ConTermInput(char * Msg) +{ + int i; + + if (ConTerm.BPQStream == 0) + { + ConTerm.BPQStream = FindFreeStream(); + + if (ConTerm.BPQStream == 255) + { + ConTerm.BPQStream = 0; + printf("No Free Streams\n"); + return; + } + } + + if (!ConTerm.CONNECTED) + SessionControl(ConTerm.BPQStream, 1, 0); + + ConTerm.StackIndex = 0; + + // Stack it + + if (ConTerm.KbdStack[19]) + free(ConTerm.KbdStack[19]); + + for (i = 18; i >= 0; i--) + { + ConTerm.KbdStack[i+1] = ConTerm.KbdStack[i]; + } + + ConTerm.KbdStack[0] = _strdup(ConTerm.kbbuf); + + ConTerm.kbbuf[ConTerm.kbptr]=13; + + SendMsg(ConTerm.BPQStream, ConTerm.kbbuf, ConTerm.kbptr+1); +} + +void ConTermPoll() +{ + int port, sesstype, paclen, maxframe, l4window, len; + int state, change, InputLen, count; + char callsign[11] = ""; + char Msg[300]; + + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + SessionState(ConTerm.BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) + { + // Connected + + ConTerm.CONNECTED = TRUE; + ConTerm.SlowTimer = 0; + } + else + { + ConTerm.CONNECTED = FALSE; + printf("*** Disconnected\n"); + } + } + + GetMsg(ConTerm.BPQStream, Msg, &InputLen, &count); + + if (InputLen) + { + char * ptr = Msg; + char * ptr2 = ptr; + Msg[InputLen] = 0; + + while (ptr) + { + ptr2 = strlop(ptr, 13); + + // Replace CR with CRLF + + printf("%s", ptr); + + if (ptr2) + printf("\r\n"); + + ptr = ptr2; + } + } + + if (_kbhit()) + { + unsigned char c = _getch(); + + if (c == 0xe0) + { + // Cursor control + + c = _getch(); + + if (c == 75) // cursor left + c = 8; + } + +#ifdef WIN32 + printf("%c", c); +#endif + if (c == 8) + { + if (ConTerm.kbptr) + ConTerm.kbptr--; + printf(" \b"); // Already echoed bs - clear typed char from screen + return; + } + + if (c == 13 || c == 10) + { + ConTermInput(ConTerm.kbbuf); + ConTerm.kbptr = 0; + return; + } + + ConTerm.kbbuf[ConTerm.kbptr++] = c; + fflush(NULL); + + } + + return; + +} + +#include + +static struct option long_options[] = +{ + {"logdir", required_argument, 0 , 'l'}, + {"configdir", required_argument, 0 , 'c'}, + {"datadir", required_argument, 0 , 'd'}, + {"help", no_argument, 0 , 'h'}, + { NULL , no_argument , NULL , no_argument } +}; + +char HelpScreen[] = + "Usage:\n" + "Optional Paramters\n" + "-l path or --logdir path Path for log files\n" + "-c path or --configdir path Path to Config file bpq32.cfg\n" + "-d path or --datadir path Path to Data Files\n" + "-v Show version and exit\n"; + +int Redirected = 0; + +static void segvhandler(int sig); +static void abrthandler(int sig); + +void GetRestartData(); + + +int main(int argc, char * argv[]) +{ + int i; + struct UserInfo * user = NULL; + ConnectionInfo * conn; + struct stat STAT; + PEXTPORTDATA PORTVEC; + +#ifdef WIN32 + + WSADATA WsaData; // receives data from WSAStartup + HWND hWnd = GetForegroundWindow(); + + WSAStartup(MAKEWORD(2, 0), &WsaData); + SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); + + // disable the [x] button. + + if (hWnd != NULL) + { + HMENU hMenu = GetSystemMenu(hWnd, 0); + if (hMenu != NULL) + { + DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); + DrawMenuBar(hWnd); + } + } + +#else + + signal(SIGSEGV, segvhandler); + signal(SIGABRT, abrthandler); + + setlinebuf(stdout); + struct sigaction act; + openlog("LINBPQ", LOG_PID, LOG_DAEMON); +#ifndef MACBPQ +#ifndef FREEBSD + prctl(PR_SET_DUMPABLE, 1); // Enable Core Dumps even with setcap +#endif +#endif + + // Disable Console Terminal if stdout redirected + +// printf("STDOUT %d\n",isatty(STDOUT_FILENO)); +// printf("STDIN %d\n",isatty(STDIN_FILENO)); + + if (!isatty(STDOUT_FILENO) || !isatty(STDIN_FILENO)) + Redirected = 1; + + timeLoadedMS = GetTickCount(); + +#endif + + printf("G8BPQ AX25 Packet Switch System Version %s %s\n", TextVerstring, Datestring); + printf("%s\n", VerCopyright); + + srand(time(NULL)); + + + // look for optarg format parameters + + { + int val; + UCHAR * ptr1; + UCHAR * ptr2; + int c; + + while (1) + { + int option_index = 0; + + c = getopt_long(argc, argv, "l:c:d:hv", long_options, &option_index); + + // Check for end of operation or error + + if (c == -1) + break; + + // Handle options + switch (c) + { + case 'h': + + printf("%s", HelpScreen); + exit (0); + + case 'l': + strcpy(LogDir, optarg); + printf("cc %s\n", LogDir); + break; + + case 'c': + strcpy(ConfigDir, optarg); + break; + + case 'd': + strcpy(DataDir, optarg); + break; + + + case '?': + /* getopt_long already printed an error message. */ + break; + + case 'v': + return 0; + } + } + } + + sprintf(RlineVer, "LinBPQ%d.%d.%d", Ver[0], Ver[1], Ver[2]); + + + Debugprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + +#if defined(MACBPQ) || defined(FREEBSD) + time_t dummy; + _MYTIMEZONE = -localtime(&dummy)->tm_gmtoff; +#else + _MYTIMEZONE = _timezone; +#endif + + if (_MYTIMEZONE < -86400 || _MYTIMEZONE > 86400) + _MYTIMEZONE = 0; + +#ifdef WIN32 + GetCurrentDirectory(256, BPQDirectory); +#else + getcwd(BPQDirectory, 256); +#endif + + strcpy(ConfigDirectory, BPQDirectory); + strcpy(LogDirectory, BPQDirectory); + + Consoleprintf("Current Directory is %s", BPQDirectory); + + if (LogDir[0]) + { + strcpy(LogDirectory, LogDir); + } + if (DataDir[0]) + { + strcpy(BPQDirectory, DataDir); + Consoleprintf("Working Directory is %s", BPQDirectory); + } + if (ConfigDir[0]) + { + strcpy(ConfigDirectory, ConfigDir); + Consoleprintf("Config Directory is %s", ConfigDirectory); + } + + for (i = optind; i < argc; i++) + { + if (_memicmp(argv[i], "logdir=", 7) == 0) + { + strcpy(LogDirectory, &argv[i][7]); + Consoleprintf("Log Directory is %s\n", LogDirectory); + break; + } + } + + Consoleprintf("Log Directory is %s", LogDirectory); + + // Make sure logs directory exists + + sprintf(LogDir, "%s/logs", LogDirectory); + +#ifdef WIN32 + CreateDirectory(LogDir, NULL); +#else + printf("Making Directory %s\n", LogDir); + i = mkdir(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); + if (i == -1 && errno != EEXIST) + { + perror("Couldn't create log directory\n"); + return 0; + } + chmod(LogDir, S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + if (!ProcessConfig()) + { + WritetoConsoleLocal("Configuration File Error\n"); + return (0); + } + + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for Linux (", TextVerstring); + +#ifdef MACBPQ + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for MAC (", TextVerstring); +#endif +#ifdef FREEBSD + SESSHDDRLEN = sprintf(SESSIONHDDR, "G8BPQ Network System %s for FreeBSD (", TextVerstring); +#endif + + + GetSemaphore(&Semaphore, 0); + + if (Start() != 0) + { + FreeSemaphore(&Semaphore); + return (0); + } + + for (i=0;PWTEXT[i] > 0x20;i++); //Scan for cr or null + + PWLen=i; + + SetApplPorts(); + + GetUIConfig(); + + INITIALISEPORTS(); + + if (IPRequired) IPActive = Init_IP(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + if (needAIS) + initAIS(); + + RigActive = Rig_Init(); + + FreeSemaphore(&Semaphore); + + OpenReportingSockets(); + + initUTF8(); + + InitDone = TRUE; + + Debugprintf("Monitor Thread ID %x", _beginthread(MonitorThread, 0, 0)); + + +#ifdef WIN32 +#else + openlog("LINBPQ", LOG_PID, LOG_DAEMON); + + memset (&act, '\0', sizeof(act)); + + act.sa_handler = &sigint_handler; + if (sigaction(SIGINT, &act, NULL) < 0) + perror ("SIGINT"); + + act.sa_handler = &sigterm_handler; + if (sigaction(SIGTERM, &act, NULL) < 0) + perror ("sigaction"); + + act.sa_handler = SIG_IGN; + if (sigaction(SIGHUP, &act, NULL) < 0) + perror ("SIGHUP"); + + if (sigaction(SIGPIPE, &act, NULL) < 0) + perror ("SIGPIPE"); + +#endif + + for (i = optind; i < argc; i++) + { + if (_stricmp(argv[i], "chat") == 0) + IncludesChat = TRUE; + } + + if (IncludesChat) + { + RunChat = TRUE; + + printf("Starting Chat\n"); + + sprintf (ChatConfigName, "%s/chatconfig.cfg", BPQDirectory); + printf("Config File is %s\n", ChatConfigName); + + if (stat(ChatConfigName, &STAT) == -1) + { + printf("Chat Config File not found - creating a default config\n"); + ChatApplNum = 2; + MaxChatStreams = 10; + SaveChatConfigFile(ChatConfigName); + } + + if (GetChatConfig(ChatConfigName) == EXIT_FAILURE) + { + printf("Chat Config File seems corrupt - check before continuing\n"); + return -1; + } + + if (ChatApplNum) + { + if (ChatInit() == 0) + { + printf("Chat Init Failed\n"); + RunChat = 0; + } + else + { + printf("Chat Started\n"); + } + } + else + { + printf("Chat APPLNUM not defined\n"); + RunChat = 0; + } + CopyConfigFile(ChatConfigName); + } + + // Start Mail if requested by command line or config + + for (i = optind; i < argc; i++) + { + if (_stricmp(argv[i], "mail") == 0) + IncludesMail = TRUE; + } + + + if (IncludesMail) + { + RunMail = TRUE; + + printf("Starting Mail\n"); + + sprintf (ConfigName, "%s/linmail.cfg", BPQDirectory); + printf("Config File is %s\n", ConfigName); + + if (stat(ConfigName, &STAT) == -1) + { + printf("Config File not found - creating a default config\n"); + strcpy(BBSName, MYNODECALL); + strlop(BBSName, '-'); + strlop(BBSName, ' '); + BBSApplNum = 1; + MaxStreams = 10; + SaveConfig(ConfigName); + } + + if (GetConfig(ConfigName) == EXIT_FAILURE) + { + printf("BBS Config File seems corrupt - check before continuing\n"); + return -1; + } + + printf("Config Processed\n"); + + BBSApplMask = 1<<(BBSApplNum-1); + + // See if we need to warn of possible problem with BaseDir moved by installer + + sprintf(BaseDir, "%s", BPQDirectory); + + + // Set up file and directory names + + strcpy(UserDatabasePath, BaseDir); + strcat(UserDatabasePath, "/"); + strcat(UserDatabasePath, UserDatabaseName); + + strcpy(MsgDatabasePath, BaseDir); + strcat(MsgDatabasePath, "/"); + strcat(MsgDatabasePath, MsgDatabaseName); + + strcpy(BIDDatabasePath, BaseDir); + strcat(BIDDatabasePath, "/"); + strcat(BIDDatabasePath, BIDDatabaseName); + + strcpy(WPDatabasePath, BaseDir); + strcat(WPDatabasePath, "/"); + strcat(WPDatabasePath, WPDatabaseName); + + strcpy(BadWordsPath, BaseDir); + strcat(BadWordsPath, "/"); + strcat(BadWordsPath, BadWordsName); + + strcpy(NTSAliasesPath, BaseDir); + strcat(NTSAliasesPath, "/"); + strcat(NTSAliasesPath, NTSAliasesName); + + strcpy(MailDir, BaseDir); + strcat(MailDir, "/"); + strcat(MailDir, "Mail"); + +#ifdef WIN32 + CreateDirectory(MailDir, NULL); // Just in case +#else + mkdir(MailDir, S_IRWXU | S_IRWXG | S_IRWXO); + chmod(MailDir, S_IRWXU | S_IRWXG | S_IRWXO); +#endif + + + + // Make backup copies of Databases + + // CopyConfigFile(ConfigName); + + CopyBIDDatabase(); + CopyMessageDatabase(); + CopyUserDatabase(); + CopyWPDatabase(); + + SetupMyHA(); + SetupFwdAliases(); + SetupNTSAliases(NTSAliasesPath); + + GetWPDatabase(); + + GetMessageDatabase(); + GetUserDatabase(); + GetBIDDatabase(); + GetBadWordFile(); + GetHTMLForms(); + GetPGConfig(); + GetRestartData(); + + // Make sure there is a user record for the BBS, with BBS bit set. + + user = LookupCall(BBSName); + + if (user == NULL) + { + user = AllocateUserRecord(BBSName); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if(SetupNewBBS(user)) + user->flags |= F_BBS; + } + + // if forwarding AMPR mail make sure User/BBS AMPR exists + + if (SendAMPRDirect) + { + BOOL NeedSave = FALSE; + + user = LookupCall("AMPR"); + + if (user == NULL) + { + user = AllocateUserRecord("AMPR"); + user->Temp = zalloc(sizeof (struct TempUserInfo)); + NeedSave = TRUE; + } + + if ((user->flags & F_BBS) == 0) + { + // Not Defined as a BBS + + if (SetupNewBBS(user)) + user->flags |= F_BBS; + NeedSave = TRUE; + } + + if (NeedSave) + SaveUserDatabase(); + } + + + // Make sure SYSOPCALL is set + + if (SYSOPCall[0] == 0) + strcpy(SYSOPCall, BBSName); + + // See if just want to add user (mainly for setup scripts) + + if (argc == 5 && _stricmp(argv[1], "--adduser") == 0) + { + BOOL isBBS = FALSE; + char * response; + + if (_stricmp(argv[4], "TRUE") == 0) + isBBS = TRUE; + + printf("Adding User %s\r\n", argv[2]); + response = AddUser(argv[2], argv[3], isBBS); + printf("%s", response); + exit(0); + } + // Allocate Streams + + strcpy(pgm, "BBS"); + + for (i=0; i < MaxStreams; i++) + { + conn = &Connections[i]; + conn->BPQStream = FindFreeStream(); + + if (conn->BPQStream == 255) break; + + NumberofStreams++; + + // BPQSetHandle(conn->BPQStream, hWnd); + + SetAppl(conn->BPQStream, (i == 0 && EnableUI) ? 0x82 : 2, BBSApplMask); + Disconnect(conn->BPQStream); + } + + strcpy(pgm, "LINBPQ"); + + InitialiseTCP(); + InitialiseNNTP(); + + SetupListenSet(); // Master set of listening sockets + + if (EnableUI || MailForInterval) + SetupUIInterface(); + + if (MailForInterval) + _beginthread(SendMailForThread, 0, 0); + + + // Calulate time to run Housekeeping + { + struct tm *tm; + time_t now; + + now = time(NULL); + + tm = gmtime(&now); + + tm->tm_hour = MaintTime / 100; + tm->tm_min = MaintTime % 100; + tm->tm_sec = 0; + + MaintClock = mktime(tm) - (time_t)_MYTIMEZONE; + + while (MaintClock < now) + MaintClock += MaintInterval * 3600; + + Debugprintf("Maint Clock %lld NOW %lld Time to HouseKeeping %lld", (long long)MaintClock, (long long)now, (long long)(MaintClock - now)); + + if (LastHouseKeepingTime) + { + if ((now - LastHouseKeepingTime) > MaintInterval * 3600) + { + DoHouseKeeping(FALSE); + } + } + for (i = optind; i < argc; i++) + + { + if (_stricmp(argv[i], "tidymail") == 0) + DeleteRedundantMessages(); + + if (_stricmp(argv[i], "nohomebbs") == 0) + DontNeedHomeBBS = TRUE; + } + + printf("Mail Started\n"); + Logprintf(LOG_BBS, NULL, '!', "Mail Starting"); + + APIClock = 0; + + SendBBSDataToPktMap(); + + } + } + + if (NUMBEROFTNCPORTS) + InitializeTNCEmulator(); + + AGWActive = AGWAPIInit(); + + if (Redirected == 0) + ConTerm.BPQStream = FindFreeStream(); + + +#ifndef WIN32 + + for (i = 1; i < argc; i++) + { + if (_stricmp(argv[i], "daemon") == 0) + { + + // Convert to daemon + + pid_t pid, sid; + + /* Fork off the parent process */ + pid = fork(); + + if (pid < 0) + exit(EXIT_FAILURE); + + if (pid > 0) + exit(EXIT_SUCCESS); + + /* Change the file mode mask */ + + umask(0); + + /* Create a new SID for the child process */ + + sid = setsid(); + + if (sid < 0) + exit(EXIT_FAILURE); + + /* Change the current working directory */ + + if ((chdir("/")) < 0) + exit(EXIT_FAILURE); + + /* Close out the standard file descriptors */ + + printf("Entering daemon mode\n"); + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + break; + } + } +#endif + + while (KEEPGOING) + { + Sleep(100); + GetSemaphore(&Semaphore, 2); + + if (QCOUNT < 10) + { + if (CLOSING == FALSE) + FindLostBuffers(); + CLOSING = TRUE; + } + + if (CLOSING) + { + if (RunChat) + { + CloseChat(); + RunChat = FALSE; + } + + if (RunMail) + { + int BPQStream, n; + + RunMail = FALSE; + + for (n = 0; n < NumberofStreams; n++) + { + BPQStream = Connections[n].BPQStream; + + if (BPQStream) + { + SetAppl(BPQStream, 0, 0); + Disconnect(BPQStream); + DeallocateStream(BPQStream); + } + } + +// SaveUserDatabase(); + SaveMessageDatabase(); + SaveBIDDatabase(); + SaveConfig(ConfigName); + SaveRestartData(); + } + + KEEPGOING--; // Give time for links to close + setbuf(stdout, NULL); + printf("Closing... %d \r", KEEPGOING); + } + + + if (RigReconfigFlag) + { + RigReconfigFlag = FALSE; + Rig_Close(); + Sleep(2000); // Allow CATPTT threads to close + RigActive = Rig_Init(); + + Consoleprintf("Rigcontrol Reconfiguration Complete"); + } + + if (APRSReconfigFlag) + { + APRSReconfigFlag = FALSE; + APRSClose(); + APRSActive = Init_APRS(); + + Consoleprintf("APRS Reconfiguration Complete"); + } + + if (ReconfigFlag) + { + int i; + BPQVECSTRUC * HOSTVEC; + PEXTPORTDATA PORTVEC=(PEXTPORTDATA)PORTTABLE; + + ReconfigFlag = FALSE; + +// SetupBPQDirectory(); + + WritetoConsoleLocal("Reconfiguring ...\n\n"); + OutputDebugString("BPQ32 Reconfiguring ...\n"); + + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { +// SaveWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); +// SaveAXIPWindowPos(PORTVEC->PORTCONTROL.PORTNUMBER); +// CloseDriverWindow(PORTVEC->PORTCONTROL.PORTNUMBER); + PORTVEC->PORT_EXT_ADDR(5,PORTVEC->PORTCONTROL.PORTNUMBER, NULL); // Close External Ports + } + } + PORTVEC->PORTCONTROL.PORTCLOSECODE(&PORTVEC->PORTCONTROL); + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + IPClose(); + APRSClose(); + Rig_Close(); + CloseTNCEmulator(); + + if (AGWActive) + AGWAPITerminate(); + + WL2KReports = NULL; + +// Sleep(2000); + + Consoleprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); + Consoleprintf(VerCopyright); + + Start(); + + INITIALISEPORTS(); + + SetApplPorts(); + + GetUIConfig(); + + FreeConfig(); + + for (i=1; i<68; i++) // Include Telnet, APRS, IP Vec + { + HOSTVEC=&BPQHOSTVECTOR[i-1]; + + HOSTVEC->HOSTTRACEQ=0; + + if (HOSTVEC->HOSTSESSION !=0) + { + // Had a connection + + HOSTVEC->HOSTSESSION=0; + HOSTVEC->HOSTFLAGS |=3; // Disconnected + +// PostMessage(HOSTVEC->HOSTHANDLE, BPQMsg, i, 4); + } + } + + OpenReportingSockets(); + + WritetoConsoleLocal("\n\nReconfiguration Complete\n"); + + if (IPRequired) IPActive = Init_IP(); + + APRSActive = Init_APRS(); + + if (ISPort == 0) + IGateEnabled = 0; + + RigActive = Rig_Init(); + + if (NUMBEROFTNCPORTS) + { + FreeSemaphore(&Semaphore); + InitializeTNCEmulator(); + GetSemaphore(&Semaphore, 2); + } + + FreeSemaphore(&Semaphore); + AGWActive = AGWAPIInit(); + GetSemaphore(&Semaphore, 2); + + OutputDebugString("BPQ32 Reconfiguration Complete\n"); + } + + if (IPActive) Poll_IP(); + if (RigActive) Rig_Poll(); + if (APRSActive) Poll_APRS(); + CheckWL2KReportTimer(); + + TIMERINTERRUPT(); + + FreeSemaphore(&Semaphore); + + if (Redirected == 0) + ConTermPoll(); + + if (NUMBEROFTNCPORTS) + TNCTimer(); + + if (AGWActive) + Poll_AGW(); + + DRATSPoll(); + RHPPoll(); + + HTTPTimer(); + + if (ReportTimer) + { + ReportTimer--; + + if (ReportTimer == 0) + { + ReportTimer = REPORTINTERVAL; + SendLocation(); + } + } + + Slowtimer++; + + if (RunChat) + { + ChatPollStreams(); + ChatTrytoSend(); + + if (Slowtimer > 100) // 10 secs + { + ChatTimer(); + } + } + + if (RunMail) + { + PollStreams(); + + if ((Slowtimer % 20) == 0) + FWDTimerProc(); + + if (Slowtimer > 100) // 10 secs + { + time_t NOW = time(NULL); + struct tm * tm; + + TCPTimer(); + BBSSlowTimer(); + + if (MaintClock < NOW) + { + while (MaintClock < NOW) // in case large time step + MaintClock += MaintInterval * 3600; + + Debugprintf("|Enter HouseKeeping"); + DoHouseKeeping(FALSE); + } + + if (APIClock < NOW) + { + SendBBSDataToPktMap(); + APIClock = NOW + 7200; // Every 2 hours + } + + + tm = gmtime(&NOW); + + if (tm->tm_wday == 0) // Sunday + { + if (GenerateTrafficReport && (LastTrafficTime + 86400) < NOW) + { + CreateBBSTrafficReport(); + LastTrafficTime = NOW; + } + } + } + TCPFastTimer(); + TrytoSend(); + } + + if (Slowtimer > 100) + Slowtimer = 0; + } + + printf("Closing Ports\n"); + + CloseTNCEmulator(); + + if (AGWActive) + AGWAPITerminate(); + + if (needAIS) + SaveAIS(); + + // Close Ports + + PORTVEC=(PEXTPORTDATA)PORTTABLE; + + for (i=0;iPORTCONTROL.PORTTYPE == 0x10) // External + { + if (PORTVEC->PORT_EXT_ADDR) + { + PORTVEC->PORT_EXT_ADDR(5, PORTVEC->PORTCONTROL.PORTNUMBER, NULL); + } + } + PORTVEC=(PEXTPORTDATA)PORTVEC->PORTCONTROL.PORTPOINTER; + } + + if (AUTOSAVE) + SaveNodes(); + + if (AUTOSAVEMH) + SaveMH(); + + if (IPActive) + IPClose(); + + if (RunMail) + FreeWebMailMallocs(); + + upnpClose(); + + // Close any open logs + + for (i = 0; i < 4; i++) + { + if (LogHandle[i]) + fclose(LogHandle[i]); + } + + return 0; +} + +int APIENTRY WritetoConsole(char * buff) +{ + return WritetoConsoleLocal(buff); +} + +int WritetoConsoleLocal(char * buff) +{ + return printf("%s", buff); +} + +#ifdef WIN32 +void * VCOMExtInit(struct PORTCONTROL * PortEntry); +void * V4ExtInit(EXTPORTDATA * PortEntry); +#endif +//UINT SoundModemExtInit(EXTPORTDATA * PortEntry); +//UINT BaycomExtInit(EXTPORTDATA * PortEntry); + +void * AEAExtInit(struct PORTCONTROL * PortEntry); +void * MPSKExtInit(EXTPORTDATA * PortEntry); +void * HALExtInit(struct PORTCONTROL * PortEntry); + +void * AGWExtInit(struct PORTCONTROL * PortEntry); +void * KAMExtInit(struct PORTCONTROL * PortEntry); +void * WinmorExtInit(EXTPORTDATA * PortEntry); +void * SCSExtInit(struct PORTCONTROL * PortEntry); +void * TrackerExtInit(EXTPORTDATA * PortEntry); +void * TrackerMExtInit(EXTPORTDATA * PortEntry); + +void * TelnetExtInit(EXTPORTDATA * PortEntry); +void * UZ7HOExtInit(EXTPORTDATA * PortEntry); +void * FLDigiExtInit(EXTPORTDATA * PortEntry); +void * ETHERExtInit(struct PORTCONTROL * PortEntry); +void * AXIPExtInit(struct PORTCONTROL * PortEntry); +void * ARDOPExtInit(EXTPORTDATA * PortEntry); +void * VARAExtInit(EXTPORTDATA * PortEntry); +void * SerialExtInit(EXTPORTDATA * PortEntry); +void * WinRPRExtInit(EXTPORTDATA * PortEntry); +void * HSMODEMExtInit(EXTPORTDATA * PortEntry); +void * FreeDataExtInit(EXTPORTDATA * PortEntry); +void * KISSHFExtInit(EXTPORTDATA * PortEntry); + +void * InitializeExtDriver(PEXTPORTDATA PORTVEC) +{ + // Only works with built in drivers + + UCHAR Value[20]; + + strcpy(Value,PORTVEC->PORT_DLL_NAME); + + _strupr(Value); + +#ifndef FREEBSD +#ifndef MACBPQ + if (strstr(Value, "BPQETHER")) + return ETHERExtInit; +#endif +#endif + if (strstr(Value, "BPQAXIP")) + return AXIPExtInit; + + if (strstr(Value, "BPQTOAGW")) + return AGWExtInit; + + if (strstr(Value, "AEAPACTOR")) + return AEAExtInit; + + if (strstr(Value, "HALDRIVER")) + return HALExtInit; + +#ifdef WIN32 + + if (strstr(Value, "BPQVKISS")) + return VCOMExtInit; + + if (strstr(Value, "V4")) + return V4ExtInit; + +#endif +/* + if (strstr(Value, "SOUNDMODEM")) + return (UINT) SoundModemExtInit; + + if (strstr(Value, "BAYCOM")) + return (UINT) BaycomExtInit; +*/ + if (strstr(Value, "MULTIPSK")) + return MPSKExtInit; + + if (strstr(Value, "KAMPACTOR")) + return KAMExtInit; + + if (strstr(Value, "WINMOR")) + return WinmorExtInit; + + if (strstr(Value, "SCSPACTOR")) + return SCSExtInit; + + if (strstr(Value, "SCSTRACKER")) + return TrackerExtInit; + + if (strstr(Value, "TRKMULTI")) + return TrackerMExtInit; + + if (strstr(Value, "UZ7HO")) + return UZ7HOExtInit; + + if (strstr(Value, "FLDIGI")) + return FLDigiExtInit; + + if (strstr(Value, "TELNET")) + return TelnetExtInit; + + if (strstr(Value, "ARDOP")) + return ARDOPExtInit; + + if (strstr(Value, "VARA")) + return VARAExtInit; + + if (strstr(Value, "KISSHF")) + return KISSHFExtInit; + + if (strstr(Value, "SERIAL")) + return SerialExtInit; + + if (strstr(Value, "WINRPR")) + return WinRPRExtInit; + + if (strstr(Value, "HSMODEM")) + return HSMODEMExtInit; + + if (strstr(Value, "FREEDATA")) + return FreeDataExtInit; + + return(0); +} + +int APIENTRY Restart() +{ + CLOSING = TRUE; + return TRUE; +} + +int APIENTRY Reboot() +{ + // Run sudo shutdown -r -f +#ifdef WIN32 + STARTUPINFO SInfo; + PROCESS_INFORMATION PInfo; + char Cmd[] = "shutdown -r -f"; + + + SInfo.cb=sizeof(SInfo); + SInfo.lpReserved=NULL; + SInfo.lpDesktop=NULL; + SInfo.lpTitle=NULL; + SInfo.dwFlags=0; + SInfo.cbReserved2=0; + SInfo.lpReserved2=NULL; + + return CreateProcess(NULL, Cmd, NULL, NULL, FALSE,0 ,NULL ,NULL, &SInfo, &PInfo); + return 0; +#else + + char * arg_list[] = {NULL, NULL, NULL, NULL, NULL}; + pid_t child_pid; + char * Context; + signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. + + arg_list[0] = "sudo"; + arg_list[1] = "shutdown"; + arg_list[2] = "now"; + arg_list[3] = "-r"; + + // Fork and Exec shutdown + + // Duplicate this process. + + child_pid = fork(); + + if (child_pid == -1) + { + printf ("Reboot fork() Failed\n"); + return 0; + } + + if (child_pid == 0) + { + execvp (arg_list[0], arg_list); + + /* The execvp function returns only if an error occurs. */ + + printf ("Failed to run shutdown\n"); + exit(0); // Kill the new process + } + return TRUE; +#endif + +} + +int APIENTRY Reconfig() +{ + if (!ProcessConfig()) + { + return (0); + } + SaveNodes(); + WritetoConsoleLocal("Nodes Saved\n"); + ReconfigFlag=TRUE; + WritetoConsoleLocal("Reconfig requested ... Waiting for Timer Poll\n"); + return 1; +} + +int APRSWriteLog(char * msg); + +VOID MonitorAPRSIS(char * Msg, size_t MsgLen, BOOL TX) +{ + char Line[300]; + char Copy[300]; + int Len; + struct tm * TM; + time_t NOW; + + if (LogAPRSIS == 0) + return; + + if (MsgLen > 250) + return; + + // Mustn't change Msg + + memcpy(Copy, Msg, MsgLen); + Copy[MsgLen] = 0; + + NOW = time(NULL); + TM = gmtime(&NOW); + + Len = sprintf_s(Line, 299, "%02d:%02d:%02d%c %s", TM->tm_hour, TM->tm_min, TM->tm_sec, (TX)? 'T': 'R', Copy); + + APRSWriteLog(Line); + +} + +struct TNCINFO * TNC; + +#ifndef WIN32 + +#include +#include + +#ifndef MACBPQ +#ifdef __MACH__ + +#include + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 0 + + + +int clock_gettime(int clk_id, struct timespec *t){ + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + uint64_t time; + time = mach_absolute_time(); + double nseconds = ((double)time * (double)timebase.numer)/((double)timebase.denom); + double seconds = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e9); + t->tv_sec = seconds; + t->tv_nsec = nseconds; + return 0; +} +#endif +#endif + + +uint64_t GetTickCount() +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000); +} + + + +void SetWindowText(HWND hWnd, char * lpString) +{ + return; +}; + +BOOL SetDlgItemText(HWND hWnd, int item, char * lpString) +{ + return 0; +}; + +#endif + +int GetListeningPortsPID(int Port) +{ +#ifdef WIN32 + + MIB_TCPTABLE_OWNER_PID * TcpTable = NULL; + PMIB_TCPROW_OWNER_PID Row; + int dwSize = 0; + unsigned int n; + + // Get PID of process for this TCP Port + + // Get Length of table + + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + TcpTable = malloc(dwSize); + GetExtendedTcpTable(TcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_LISTENER, 0); + + for (n = 0; n < TcpTable->dwNumEntries; n++) + { + Row = &TcpTable->table[n]; + + if (Row->dwLocalPort == Port && Row->dwState == MIB_TCP_STATE_LISTEN) + { + return Row->dwOwningPid; + break; + } + } +#endif + return 0; // Not found +} + + + +VOID Check_Timer() +{ +} + +VOID POSTDATAAVAIL(){}; + +COLORREF Colours[256] = {0, + RGB(0,0,0), RGB(0,0,128), RGB(0,0,192), RGB(0,0,255), // 1 - 4 + RGB(0,64,0), RGB(0,64,128), RGB(0,64,192), RGB(0,64,255), // 5 - 8 + RGB(0,128,0), RGB(0,128,128), RGB(0,128,192), RGB(0,128,255), // 9 - 12 + RGB(0,192,0), RGB(0,192,128), RGB(0,192,192), RGB(0,192,255), // 13 - 16 + RGB(0,255,0), RGB(0,255,128), RGB(0,255,192), RGB(0,255,255), // 17 - 20 + + RGB(6425,0,0), RGB(64,0,128), RGB(64,0,192), RGB(0,0,255), // 21 + RGB(64,64,0), RGB(64,64,128), RGB(64,64,192), RGB(64,64,255), + RGB(64,128,0), RGB(64,128,128), RGB(64,128,192), RGB(64,128,255), + RGB(64,192,0), RGB(64,192,128), RGB(64,192,192), RGB(64,192,255), + RGB(64,255,0), RGB(64,255,128), RGB(64,255,192), RGB(64,255,255), + + RGB(128,0,0), RGB(128,0,128), RGB(128,0,192), RGB(128,0,255), // 41 + RGB(128,64,0), RGB(128,64,128), RGB(128,64,192), RGB(128,64,255), + RGB(128,128,0), RGB(128,128,128), RGB(128,128,192), RGB(128,128,255), + RGB(128,192,0), RGB(128,192,128), RGB(128,192,192), RGB(128,192,255), + RGB(128,255,0), RGB(128,255,128), RGB(128,255,192), RGB(128,255,255), + + RGB(192,0,0), RGB(192,0,128), RGB(192,0,192), RGB(192,0,255), // 61 + RGB(192,64,0), RGB(192,64,128), RGB(192,64,192), RGB(192,64,255), + RGB(192,128,0), RGB(192,128,128), RGB(192,128,192), RGB(192,128,255), + RGB(192,192,0), RGB(192,192,128), RGB(192,192,192), RGB(192,192,255), + RGB(192,255,0), RGB(192,255,128), RGB(192,255,192), RGB(192,255,255), + + RGB(255,0,0), RGB(255,0,128), RGB(255,0,192), RGB(255,0,255), // 81 + RGB(255,64,0), RGB(255,64,128), RGB(255,64,192), RGB(255,64,255), + RGB(255,128,0), RGB(255,128,128), RGB(255,128,192), RGB(255,128,255), + RGB(255,192,0), RGB(255,192,128), RGB(255,192,192), RGB(255,192,255), + RGB(255,255,0), RGB(255,255,128), RGB(255,255,192), RGB(255,255,255) +}; + + +//VOID SendRPBeacon(struct TNCINFO * TNC) +//{ +//} + +int PollStreams() +{ + int state,change; + ConnectionInfo * conn; + int n; + struct UserInfo * user = NULL; + char ConnectedMsg[] = "*** CONNECTED "; + + for (n = 0; n < NumberofStreams; n++) + { + conn = &Connections[n]; + + DoReceivedData(conn->BPQStream); + DoBBSMonitorData(conn->BPQStream); + + SessionState(conn->BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) // Connected + { + GetSemaphore(&ConSemaphore, 0); + Connected(conn->BPQStream); + FreeSemaphore(&ConSemaphore); + } + else + { + GetSemaphore(&ConSemaphore, 0); + Disconnected(conn->BPQStream); + FreeSemaphore(&ConSemaphore); + } + } + } + + return 0; +} + + +VOID CloseConsole(int Stream) +{ +} + +#ifndef WIN32 + +int V4ProcessReceivedData(struct TNCINFO * TNC) +{ + return 0; +} +#endif + +#ifdef FREEBSD + +char * gcvt(double _Val, int _NumOfDigits, char * _DstBuf) +{ + sprintf(_DstBuf, "%f", _Val); + return _DstBuf; +} + +#endif + + + + + diff --git a/.svn/pristine/98/98250e1f3274e2d47fa0fbc2703cbcd7cfed1928.svn-base b/.svn/pristine/98/98250e1f3274e2d47fa0fbc2703cbcd7cfed1928.svn-base new file mode 100644 index 0000000..7fce453 --- /dev/null +++ b/.svn/pristine/98/98250e1f3274e2d47fa0fbc2703cbcd7cfed1928.svn-base @@ -0,0 +1,6313 @@ +/* +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 +*/ + + +// TNC Emulator Module for BPQ32 switch + +// Supports TNC2 and WA8DED Hostmode interfaces + + +#define _CRT_SECURE_NO_DEPRECATE + +#include "cheaders.h" + +typedef struct _TCMDX +{ + char String[12]; // COMMAND STRING + UCHAR CMDLEN; // SIGNIFICANT LENGTH + VOID (* CMDPROC)(struct TNCDATA * TNC, char * Tail, struct _TCMDX * CMD);// COMMAND PROCESSOR + size_t CMDFLAG; // FLAG/VALUE Offset + +} TCMDX; + + +#define LF 10 +#define CR 13 + +#define Connect(stream) SessionControl(stream,1,0) +#define Disconnect(stream) SessionControl(stream,2,0) +#define ReturntoNode(stream) SessionControl(stream,3,0) +#define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) + +int APIENTRY SendMsg(int stream, char * msg, int len); + +VOID SENDPACKET(struct TNCDATA * TNC); +VOID CHECKCTS(struct TNCDATA * TNC); +VOID SETCOMMANDMODE(struct TNCDATA * TNC); +VOID SETCOMM00(struct TNCDATA * TNC); +VOID SENDREPLY(struct TNCDATA * TNC, char * Msg, int Len); +VOID TNCCOMMAND(struct TNCDATA * TNC); +VOID TNC2PutChar(struct TNCDATA * TNC, int Char); +VOID KBECHO(struct TNCDATA * TNC, int Char); +VOID KBNORM(struct TNCDATA * TNC, int Char); +VOID PUTCHARINBUFFER(struct TNCDATA * TNC, int Char); +VOID TNC2GetChar(struct TNCDATA * TNC, int * returnedchar, int * moretocome); +VOID CONNECTTONODE(struct TNCDATA * TNC); +DllImport int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall); +DllImport int APIENTRY GetCallsign(int stream, char * callsign); +VOID GETDATA(struct TNCDATA * TNC); +VOID DOCONMODECHANGE(struct TNCDATA * TNC, int Stream); +VOID SEND_CONNECTED(struct TNCDATA * TNC, int ToStream); +VOID READCHANGE(int Stream); +VOID DOMONITORING(int NeedTrace, struct TNCDATA * TNC); +int APIENTRY DecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp); +time_t APIENTRY GetRaw(int stream, char * msg, int * len, int * count); +BOOL TfPut(struct TNCDATA * TNC, UCHAR character); +int DATAPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel); +int STATUSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel); +int DEDPROCESSHOSTPACKET(struct StreamInfo * Channel, struct TNCDATA * TNC); +VOID ProcessKPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len); +VOID ProcessPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len);; +int KANTConnected(struct TNCDATA * conn, struct StreamInfo * channel, int Stream); +int KANTDisconnected(struct TNCDATA * conn, struct StreamInfo * channel, int Stream); +VOID SendKISSData(struct TNCDATA * conn, UCHAR * txbuffer, int Len); +VOID ProcessSCSPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Length); +VOID TNCPoll(); +VOID DisableAppl(struct TNCDATA * TNC); +int BPQSerialSetPollDelay(HANDLE hDevice, int PollDelay); +int BPQSerialSendData(struct TNCDATA * TNC, UCHAR * Message,int MsgLen); +int BPQSerialGetData(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen); +int SendHostOK(struct TNCDATA * TNC); + +extern struct TNCDATA * TNCCONFIGTABLE; + +struct TNCDATA * TNC2TABLE = NULL; // malloc'ed +extern int NUMBEROFTNCPORTS; + +// MODEFLAG DEFINITIONS + +#define COMMAND 1 +#define TRANS 2 +#define CONV 4 + +// APPLFLAGS BITS + +//CMD_TO_APPL EQU 1B ; PASS COMMAND TO APPLICATION +//MSG_TO_USER EQU 10B ; SEND "CONNECTED" TO USER +//MSG_TO_APPL EQU 100B ; SEND "CONECTED" TO APPL + +extern char pgm[256]; + +int CloseDelay = 10; // Close after connect fail delay + +MESSAGE MONITORDATA; // RAW FRAME FROM NODE + +char NEWCALL[11]; + +//TABLELEN DW TYPE TNCDATA + +char LNKSTATEMSG[] = "Link state is: "; +char CONNECTEDMSG[] = "CONNECTED to "; +char WHATMSG[] = "Eh?\rcmd:"; +char CMDMSG[] = "cmd:"; + + +char DISCONNMSG[] = "\r*** DISCONNECTED\r"; + +char CONMSG1[] = "\r*** CONNECTED to "; +char CONCALL[10]; + + +char SIGNON[] = "\r\rG8BPQ TNC2 EMULATOR\r\r"; + +char CONMSG[] ="\r*** CONNECTED to "; +char SWITCH[] = "SWITCH\r"; +char SWITCHSP[] = "SWITCH "; + +char WAS[] = " was "; +char VIA[] = " via "; +char OFF[] = "OFF\r"; +char ON[] = "ON \r"; + +// BPQ Serial Device Support + +// On W2K and above, BPQVIrtualCOM.sys provides a pair of cross-connected devices, and a control channel +// to enumerate, add and delete devices. + +// On Win98 BPQVCOMM.VXD provides a single IOCTL interface, over which calls for each COM device are multiplexed + +#ifdef WIN32 + +#define IOCTL_SERIAL_SET_BAUD_RATE CTL_CODE(FILE_DEVICE_SERIAL_PORT, 1,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_QUEUE_SIZE CTL_CODE(FILE_DEVICE_SERIAL_PORT, 2,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_LINE_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT, 3,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_BREAK_ON CTL_CODE(FILE_DEVICE_SERIAL_PORT, 4,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_BREAK_OFF CTL_CODE(FILE_DEVICE_SERIAL_PORT, 5,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_IMMEDIATE_CHAR CTL_CODE(FILE_DEVICE_SERIAL_PORT, 6,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_TIMEOUTS CTL_CODE(FILE_DEVICE_SERIAL_PORT, 7,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_TIMEOUTS CTL_CODE(FILE_DEVICE_SERIAL_PORT, 8,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DTR CTL_CODE(FILE_DEVICE_SERIAL_PORT, 9,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DTR CTL_CODE(FILE_DEVICE_SERIAL_PORT,10,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_RESET_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,11,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_RTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,12,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_RTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,13,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_XOFF CTL_CODE(FILE_DEVICE_SERIAL_PORT,14,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_XON CTL_CODE(FILE_DEVICE_SERIAL_PORT,15,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_WAIT_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,16,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_WAIT_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,17,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_WAIT_ON_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,18,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_PURGE CTL_CODE(FILE_DEVICE_SERIAL_PORT,19,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_BAUD_RATE CTL_CODE(FILE_DEVICE_SERIAL_PORT,20,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_LINE_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT,21,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_CHARS CTL_CODE(FILE_DEVICE_SERIAL_PORT,22,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_CHARS CTL_CODE(FILE_DEVICE_SERIAL_PORT,23,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_HANDFLOW CTL_CODE(FILE_DEVICE_SERIAL_PORT,24,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_HANDFLOW CTL_CODE(FILE_DEVICE_SERIAL_PORT,25,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_MODEMSTATUS CTL_CODE(FILE_DEVICE_SERIAL_PORT,26,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_COMMSTATUS CTL_CODE(FILE_DEVICE_SERIAL_PORT,27,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_XOFF_COUNTER CTL_CODE(FILE_DEVICE_SERIAL_PORT,28,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_PROPERTIES CTL_CODE(FILE_DEVICE_SERIAL_PORT,29,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GET_DTRRTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,30,METHOD_BUFFERED,FILE_ANY_ACCESS) + + +#define IOCTL_SERIAL_IS_COM_OPEN CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_GETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_SERIAL_SET_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x803,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_SET_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x805,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_SERIAL_CLR_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x806,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x807,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_SERIAL_CLR_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x808,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_BPQ_ADD_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x809,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQ_DELETE_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80a,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQ_LIST_DEVICES CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80b,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define IOCTL_BPQ_SET_POLLDELAY CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80c,METHOD_BUFFERED,FILE_ANY_ACCESS) +#define IOCTL_BPQ_SET_DEBUGMASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80d,METHOD_BUFFERED,FILE_ANY_ACCESS) + +#define W98_SERIAL_IS_COM_OPEN 0x800 +#define W98_SERIAL_GETDATA 0x801 +#define W98_SERIAL_SETDATA 0x802 + +#define W98_SERIAL_SET_CTS 0x803 +#define W98_SERIAL_SET_DSR 0x804 +#define W98_SERIAL_SET_DCD 0x805 + +#define W98_SERIAL_CLR_CTS 0x806 +#define W98_SERIAL_CLR_DSR 0x807 +#define W98_SERIAL_CLR_DCD 0x808 + +#define W98_BPQ_ADD_DEVICE 0x809 +#define W98_BPQ_DELETE_DEVICE 0x80a +#define W98_BPQ_LIST_DEVICES 0x80b + +#define W98_BPQ_SET_POLLDELAY 0x80c +#define W98_BPQ_SET_DEBUGMASK 0x80d + +#define W98_SERIAL_GET_COMMSTATUS 27 +#define W98_SERIAL_GET_DTRRTS 30 + +#define DebugModemStatus 1 +#define DebugCOMStatus 2 +#define DebugWaitCompletion 4 +#define DebugReadCompletion 8 + + +HANDLE hControl; + +BOOL Win98; + +typedef struct _SERIAL_STATUS { + ULONG Errors; + ULONG HoldReasons; + ULONG AmountInInQueue; + ULONG AmountInOutQueue; + BOOL EofReceived; + BOOL WaitForImmediate; +} SERIAL_STATUS,*PSERIAL_STATUS; + +#endif + + +#ifndef WIN32 + +#if defined(MACBPQ) +#include +#elif defined(FREEBSD) +#if defined(__FreeBSD__) || defined(__DragonFly__) +#include +#else +#include +#endif +#endif + +extern int posix_openpt (int __oflag); +extern int grantpt (int __fd); +extern int unlockpt (int __fd); +extern char *ptsname (int __fd); +extern int ptsname_r (int __fd, char *__buf, size_t __buflen); +extern int getpt (void); + +HANDLE LinuxOpenPTY(char * Name) +{ + // Open a Virtual COM Port + + HANDLE hDevice, slave;; + char slavedevice[80]; + int ret; + u_long param=1; + struct termios term; + +#if defined(MACBPQ) || defined(FREEBSD) + + // Create a pty pair + + openpty(&hDevice, &slave, &slavedevice[0], NULL, NULL); + close(slave); + +#else + + hDevice = posix_openpt(O_RDWR|O_NOCTTY); + + if (hDevice == -1) + { + perror("posix_openpt Create PTY pair failed"); + return -1; + } + if (grantpt (hDevice) == -1) + { + perror("grantpt Create PTY pair failed"); + return -1; + } + if (unlockpt (hDevice) == -1) + { + perror("unlockpt Create PTY pair failed"); + return -1; + } + if (ptsname_r(hDevice, slavedevice, 80) != 0) + { + perror("ptsname_r Create PTY pair failed"); + return -1; + } + +#endif + + printf("slave device: %s. ", slavedevice); + + if (tcgetattr(hDevice, &term) == -1) + { + perror("tty_speed: tcgetattr"); + return FALSE; + } + + cfmakeraw(&term); + + if (tcsetattr(hDevice, TCSANOW, &term) == -1) + { + perror("tcsetattr"); + return -1; + } + + ioctl(hDevice, FIONBIO, ¶m); + + chmod(slavedevice, S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP|S_IROTH|S_IWOTH); + + unlink (Name); + + ret = symlink (slavedevice, Name); + + if (ret == 0) + printf ("symlink to %s created\n", Name); + else + printf ("symlink to %s failed\n", Name); + + return hDevice; +} +#else + +HANDLE BPQOpenSerialPort(struct TNCDATA * TNC, DWORD * lasterror) +{ + // Open a Virtual COM Port + + int port = TNC->ComPort; + char szPort[80]; + HANDLE hDevice; + int Err; + + *lasterror=0; + + if (Win98) + { + sprintf( szPort, "\\\\.\\COM%d",port) ; + + hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (hDevice == (HANDLE) -1 ) + { + // If In Use(5) ok, else fail + + if (GetLastError() == 5) + return (HANDLE)(ptrdiff_t)(port<<16); // Port Number is a pseudohandle to the device + + return (HANDLE)(ptrdiff_t) - 1; + } + + CloseHandle(hDevice); + + return (HANDLE)(ptrdiff_t)(port<<16); // Port Number is a pseudohandle to the device + } + + // Try New Style VCOM firsrt + + sprintf( szPort, "\\\\.\\pipe\\BPQCOM%d", port ) ; + + hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + Err = GetLastError(); + + if (hDevice != (HANDLE) -1) + { + TNC->NewVCOM = TRUE; + TNC->PortEnabled = TRUE; + Err = GetFileType(hDevice); + } + else + { + + // Try old style + + sprintf(szPort, "\\\\.\\BPQ%d", port ) ; + + + hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + if (TNC->PollDelay) + BPQSerialSetPollDelay(hDevice, TNC->PollDelay); + + } + if (hDevice == (HANDLE) -1 ) + { + *lasterror=GetLastError(); + } + + return hDevice; +} +#endif + +int BPQSerialSetCTS(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl,(DWORD)hDevice | W98_SERIAL_SET_CTS,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_CTS,NULL,0,NULL,0, &bytesReturned,NULL); + +#endif +} + +int BPQSerialSetDSR(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SET_DSR,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_DSR, NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} + +int BPQSerialSetDCD(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SET_DCD,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_DCD,NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} + +int BPQSerialClrCTS(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_CTS,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_CTS,NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} +int BPQSerialClrDSR(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_DSR,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_DSR,NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} + +int BPQSerialClrDCD(HANDLE hDevice) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_DCD,NULL,0,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_DCD, NULL,0,NULL,0, &bytesReturned,NULL); +#endif +} + +static int SendDataToTNC(struct TNCDATA * TNC, UCHAR * TXMsg, int n) +{ + // Used for all port types + +#ifdef WIN32 + + // WIN32 VCOM Used one of my Drivers + + if (TNC->VCOM) + BPQSerialSendData(TNC, TXMsg, n); + +#endif + + // Linux VCOM uses SOCAT Pairs and normal write + + return WriteCOMBlock(TNC->hDevice, TXMsg, n); +} + +int BPQSerialSendData(struct TNCDATA * TNC, UCHAR * Message,int MsgLen) +{ + HANDLE hDevice = TNC->hDevice; + ULONG bytesReturned; + + // Host Mode code calls BPQSerialSendData for all ports, so it a real port, pass on to real send routine + + if (!TNC->VCOM) + return WriteCOMBlock(TNC->hDevice, Message, MsgLen); + +#ifndef WIN32 + + // Linux usies normal IO for all ports + return WriteCOMBlock(TNC->hDevice, Message, MsgLen); + +#else + + if (MsgLen > 4096 ) return ERROR_INVALID_PARAMETER; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL); + else + { + if (TNC->NewVCOM) + { + // Have to escape all oxff chars, as these are used to get status info + + UCHAR NewMessage[1000]; + UCHAR * ptr1 = Message; + UCHAR * ptr2 = NewMessage; + UCHAR c; + + int Length = MsgLen; + + while (Length != 0) + { + c = *(ptr1++); + *(ptr2++) = c; + + if (c == 0xff) + { + *(ptr2++) = c; + MsgLen++; + } + Length--; + } + + return WriteFile(hDevice, NewMessage, MsgLen, &bytesReturned, NULL); + } + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL); + } +#endif +} + +int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); + +int GetDataFromTNC(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen) +{ + // Used for all port types + +#ifdef WIN32 + + // WIN32 VCOM Used one of my Drivers + + if (TNC->VCOM) + return BPQSerialGetData(TNC, Message, BufLen, MsgLen); + + *MsgLen = ReadCOMBlock(TNC->hDevice, Message, BufLen); + return 0; + +#else + + int Error = 0; + + if (TNC->VCOM == 0) + { + *MsgLen = ReadCOMBlock(TNC->hDevice, Message, BufLen); + return 0; + } + + // Linux VCOM uses SOCAT Pairs and normal Read + + // If the slave closes connection read returns 5. Need to trap and + // close/reopen. So use ReadCOMBlockEx + + *MsgLen = ReadCOMBlockEx(TNC->hDevice, Message, BufLen, &Error); + + if (Error == 5) + { + printf("Read error on TNCPORT %s - Restarting\n", TNC->PORTNAME); + close(TNC->hDevice); + TNC->hDevice = LinuxOpenPTY(TNC->PORTNAME); + } + return 0; + +#endif +} + + +int BPQSerialGetData(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen) +{ +#ifdef WIN32 + DWORD dwLength = 0; + DWORD Available = 0; + HANDLE hDevice = TNC->hDevice; + int Length, RealLen = 0; + + if (BufLen > 4096 ) return ERROR_INVALID_PARAMETER; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GETDATA,NULL,0,Message,BufLen,MsgLen,NULL); + + if (TNC->NewVCOM) + { + int ret = PeekNamedPipe(hDevice, NULL, 0, NULL, &Available, NULL); + + if (ret == 0) + { + ret = GetLastError(); + + if (ret == ERROR_BROKEN_PIPE) + { + CloseHandle(hDevice); + hDevice = INVALID_HANDLE_VALUE; + return 0; + } + } + + if (Available > BufLen) + Available = BufLen; + + if (Available) + { + UCHAR * ptr1 = Message; + UCHAR * ptr2 = Message; + UCHAR c; + + ReadFile(hDevice, Message, Available, &Length, NULL); + + // Have to look for FF escape chars + + RealLen = Length; + + while (Length != 0) + { + c = *(ptr1++); + Length--; + + if (c == 0xff) + { + c = c = *(ptr1++); + Length--; + + if (c == 0xff) // ff ff means ff + { + RealLen--; + } + else + { + // This is connection statua from other end + + RealLen -= 2; +// TNC->PortEnabled = c; + continue; + } + } + *(ptr2++) = c; + } + } + *MsgLen = RealLen; + return 0; + } + + return DeviceIoControl(hDevice,IOCTL_SERIAL_GETDATA,NULL,0,Message,BufLen,MsgLen,NULL); +} +#else + return 0; +} +#endif + +int BPQSerialGetQCounts(HANDLE hDevice,ULONG * RXCount, ULONG * TXCount) +{ +#ifndef WIN32 + return 0; +#else + + SERIAL_STATUS Resp; + int MsgLen; + int ret; + + if (Win98) + ret = DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GET_COMMSTATUS,NULL,0,&Resp,sizeof(SERIAL_STATUS),&MsgLen,NULL); + else + ret = DeviceIoControl(hDevice,IOCTL_SERIAL_GET_COMMSTATUS,NULL,0,&Resp,sizeof(SERIAL_STATUS),&MsgLen,NULL); + + *RXCount=Resp.AmountInInQueue; + *TXCount=Resp.AmountInOutQueue; + + return ret; +#endif +} + +int BPQSerialGetDeviceList(HANDLE hDevice,ULONG * Slot,ULONG * Port) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + return DeviceIoControl (hDevice,IOCTL_BPQ_LIST_DEVICES,Slot,4,Port,4,&bytesReturned,NULL); +#endif +} + +int BPQSerialIsCOMOpen(HANDLE hDevice,ULONG * Count) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_IS_COM_OPEN,NULL,0,Count,4,&bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_IS_COM_OPEN,NULL,0,Count,4,&bytesReturned,NULL); +#endif +} + +int BPQSerialGetDTRRTS(HANDLE hDevice, ULONG * Flags) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GET_DTRRTS,NULL,0,Flags,4,&bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_SERIAL_GET_DTRRTS,NULL,0,Flags,4,&bytesReturned,NULL); +#endif +} + +int BPQSerialSetPollDelay(HANDLE hDevice, int PollDelay) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + if (Win98) + return DeviceIoControl(hControl, (UINT)hDevice | W98_BPQ_SET_POLLDELAY,&PollDelay,4,NULL,0, &bytesReturned,NULL); + else + return DeviceIoControl(hDevice,IOCTL_BPQ_SET_POLLDELAY,&PollDelay,4,NULL,0, &bytesReturned,NULL); + +#endif +} + +int BPQSerialSetDebugMask(HANDLE hDevice, int DebugMask) +{ +#ifndef WIN32 + return 0; +#else + + ULONG bytesReturned; + + return DeviceIoControl(hDevice, IOCTL_BPQ_SET_DEBUGMASK, &DebugMask, 4, NULL, 0, &bytesReturned,NULL); +#endif +} + +void CheckForStreamChange(struct TNCDATA * TNC, int ToStream) +{ + // Send Stream Switched Message if changed + + char Msg[80]; + int Len; + + if (ToStream == TNC->RXStream) + return; + + TNC->RXStream = ToStream; + + // Send Message + + // |B:WA7GXD: + + if (TNC->StreamCall) + Len = sprintf(Msg, "%c%c:%s:", TNC->StreamSW, ToStream + 'A', TNC->TNC2Stream[ToStream]->RemoteCall); + else + Len = sprintf(Msg, "%c%c", TNC->StreamSW, ToStream + 'A'); + + SENDREPLY(TNC, Msg, Len); +} + +int LocalSessionState(int stream, int * state, int * change, BOOL ACK) +{ + // Get current Session State. Any state changed is ACK'ed + // automatically. See BPQHOST functions 4 and 5. + + // Local version without semaphore or checktimer + + BPQVECSTRUC * HOST = &BPQHOSTVECTOR[stream -1]; // API counts from 1 + + // CX = 0 if stream disconnected or CX = 1 if stream connected + // DX = 0 if no change of state since last read, or DX = 1 if + // the connected/disconnected state has changed since + // last read (ie. delta-stream status). + + // HOSTFLAGS = Bit 80 = Allocated + // Bit 40 = Disc Request + // Bit 20 = Stay Flag + // Bit 02 and 01 State Change Bits + + if ((HOST->HOSTFLAGS & 3) == 0) + // No Chaange + *change = 0; + else + *change = 1; + + if (HOST->HOSTSESSION) // LOCAL SESSION + // Connected + *state = 1; + else + *state = 0; + + if (ACK) + HOST->HOSTFLAGS &= 0xFC; // Clear Change Bits + + return 0; +} + + + + +VOID ONOFF(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + // PROCESS COMMANDS WITH ON/OFF PARAM + + char Param; + UCHAR * valueptr; + UCHAR oldvalue, newvalue = 0xff; + + char Response[80]; + int len; + + _strupr(Tail); + Param = *Tail; + + valueptr = (UCHAR *)TNC + CMD->CMDFLAG;; + oldvalue = (UCHAR)*valueptr; + + switch(Param) + { + case ' ': + break; + case 'Y': + newvalue = 1; + break; + case 'N': + newvalue = 0; + break; + case 'O': + if (Tail[1] == 'N') + newvalue = 1; + else + newvalue = 0; + break; + } + + if (newvalue == 255) + { + len = sprintf(Response, "%s %s\r", CMD->String, (oldvalue)?"ON":"OFF"); + } + else + { + len = sprintf(Response, "%s was %s\r", CMD->String, (oldvalue)?"ON":"OFF"); + *valueptr = newvalue; + } + SENDREPLY(TNC, Response, len); +} + + + +VOID ONOFF_CONOK(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + ONOFF(TNC, Tail, CMD); + + // UPDATE APPL FLAGS ON NODE PORT + + if (TNC->CONOK) + SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, TNC->APPLICATION); + else + SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, 0); +} + +VOID SETMYCALL(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + char Response[80]; + int len; + char Call[10] = " "; + + _strupr(Tail); + + if (*Tail == ' ') + { + // REQUEST FOR CURRENT STATE + + len = sprintf(Response, "MYCALL %s\r", TNC->MYCALL); + } + else + { + strlop(Tail,' ');; + memcpy(Call, Tail, (int)strlen(Tail) + 1); + len = sprintf(Response, "MYCALL was %s\r", TNC->MYCALL); + memcpy(TNC->MYCALL, Call, 10); + } + + SENDREPLY(TNC, Response, len); +} +VOID CTEXTCMD(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + char Response[256]; + int len, n; + + if (*Tail == ' ') + { + // REQUEST FOR CURRENT STATE + + len = sprintf(Response, "CTEXT %s\r", TNC->CTEXT); + } + else + { + Tail[TNC->MSGLEN] = 0; + n = strlen(Tail) - 1; + while(n > 0 && Tail[n] == ' ') + Tail[n--] = 0; + + if (strlen(Tail) > 119) + Tail[119] = 0; + + len = sprintf(Response, "CTEXT was %s\r", TNC->CTEXT); + strcpy(TNC->CTEXT, Tail); + } + + SENDREPLY(TNC, Response, len); +} + +VOID BTEXT(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ +} +VOID VALUE(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + // PROCESS COMMANDS WITH decimal value + + char Param = *Tail; + UCHAR * valueptr; + int oldvalue, newvalue; + + char Response[80]; + int len; + + valueptr = (UCHAR *)TNC + CMD->CMDFLAG;; + oldvalue = *valueptr; + + strlop(Tail, ' '); + + if (Tail[0]) + { + newvalue = atoi(Tail); + len = sprintf(Response, "%s was %d\r", CMD->String, oldvalue); + *valueptr = newvalue; + } + else + { + len = sprintf(Response, "%s %d\r", CMD->String, oldvalue); + } + SENDREPLY(TNC, Response, len); +} + +VOID VALHEX(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + // PROCESS COMMANDS WITH decimal value + + char Param = *Tail; + + UCHAR * valueptr; + UINT * intvalueptr; + UINT oldvalue, newvalue; + + char Response[80]; + int len; + + valueptr = (UCHAR *)TNC + CMD->CMDFLAG;; + intvalueptr = (UINT *)valueptr; + + oldvalue = *intvalueptr; + + strlop(Tail, ' '); + + if (Tail[0]) + { + if (Tail[0] == '$') + newvalue = (UINT)strtol(Tail + 1, NULL, 16); + else + newvalue = (UINT)strtol(Tail, NULL, 0); + + len = sprintf(Response, "%s was $%x\r", CMD->String, oldvalue); + *intvalueptr = newvalue; + } + else + { + len = sprintf(Response, "%s $%x\r", CMD->String, oldvalue); + } + SENDREPLY(TNC, Response, len); +} + +VOID APPL_VALHEX(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + int ApplNum = 1; + UINT APPLMASK; + + VALHEX(TNC, Tail, CMD); + + // UPDATE APPL FLAGS ON NODE PORT + + if (TNC->CONOK) + SetAppl(TNC->BPQPort, TNC->APPLFLAGS, TNC->APPLICATION); + else + SetAppl(TNC->BPQPort, TNC->APPLFLAGS, 0); + + // Set MYCALL to APPLCALL + + APPLMASK = TNC->APPLICATION; + ApplNum = 1; + + while (APPLMASK && (APPLMASK & 1) == 0) + { + ApplNum++; + APPLMASK >>= 1; + } + + if (TNC->CONOK && TNC->APPLICATION) + memcpy(TNC->MYCALL, GetApplCall(ApplNum), 10); + +} +VOID CSWITCH(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + char Response[80]; + int len; + + len = sprintf(Response, "%s", CMDMSG); + SENDREPLY(TNC, Response, len); + + CONNECTTONODE(TNC); + +} +VOID CONMODE(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + SENDREPLY(TNC, CMDMSG, 4); +} + +VOID TNCCONV(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + TNCStream->MODEFLAG |= CONV; + TNCStream->MODEFLAG &= ~(COMMAND+TRANS); +} + +VOID TNCNODE(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + // CONNECT TO NODE + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + TNCStream->VMSR |= 0x88; // SET CONNECTED + + TNCStream->MODEFLAG |= CONV; // INTO CONVERSE MODE + TNCStream->MODEFLAG &= ~(COMMAND+TRANS); + + CONNECTTONODE(TNC); +} + +VOID CStatus(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + char Response[80]; + int i, len; + char Call[10] = ""; + char Selected[3] = " "; + + for (i = 0; i < TNC->HOSTSTREAMS; i++) + { + char Selected[3] = " "; + TNCStream = TNC->TNC2Stream[i]; + + if (TNC->RXStream == i) + Selected[0] = 'I'; + + if (TNC->TXStream == i) + Selected[1] = 'O'; + + if (TNCStream->VMSR & 0x80) + { + GetCallsign(TNCStream->BPQPort, Call); + strlop(Call, ' '); + + len = sprintf(Response, "%c stream - %s CONNECTED to %s\r", i + 'A', Selected, Call); + } + else + { + len = sprintf(Response, "%c stream - %s DISCONNECTED\r", i + 'A', Selected); + } + + SENDREPLY(TNC, Response, len); + } +} + + +VOID TNCCONNECT(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + char Response[80]; + int len; + char Call[10] = ""; + + _strupr(Tail); + + if (*Tail == ' ') + { + // REQUEST FOR CURRENT STATE + + len = sprintf(Response, "%s", LNKSTATEMSG); + + if (TNCStream->VMSR & 0x80) + { + GetCallsign(TNCStream->BPQPort, Call); + strlop(Call, ' '); + + len = sprintf(Response, "%c Link state is: CONNECTED to %s\r", TNC->TXStream + 'A', Call); + } + else + { + len = sprintf(Response, "%c Link state is: DISCONNECTED\r", TNC->TXStream + 'A'); + } + + SENDREPLY(TNC, Response, len); + return; + } + + // CONNECT, BUT NOT TO SWITCH - CONNECT TO NODE, THEN PASS TO IT FOR PROCESSING + + TNCNODE(TNC, Tail, CMD); + READCHANGE(TNCStream->BPQPort); //CLEAR STATUS CHANGE (TO AVOID SUPURIOUS "CONNECTED TO") + + strcat(TNC->TONODEBUFFER, "\r"); + TNC->MSGLEN = (int)strlen(TNC->TONODEBUFFER); + + SENDPACKET(TNC); // Will now go to node + +} +VOID TNCDISC(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + Disconnect(TNCStream->BPQPort); + + SENDREPLY(TNC, CMDMSG, 4); +} + +VOID READCHANGE(int Stream) +{ + int dummy; + LocalSessionState(Stream, &dummy, &dummy, TRUE); +} + +VOID TNCRELEASE(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + ReturntoNode(TNC->BPQPort); + + TNC->VMSR &= 0x7F; // DROP DCD + TNC->VMSR |= 8; //DELTA DCD + + SENDREPLY(TNC, CMDMSG, 4); +} +VOID TNCTRANS(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + // MAKE PRETTY SURE THIS ISNT A BIT OF STRAY DATA + + if (TNC->MSGLEN > 6) + return; + + TNCStream->MODEFLAG |= TRANS; + TNCStream->MODEFLAG &= ~(COMMAND+CONV); +} +static VOID TNCRESTART(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ + // REINITIALISE CHANNEL + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + TNC->PUTPTR = TNC->GETPTR = &TNC->TOUSERBUFFER[0]; + TNC->RXCOUNT = 0; + + TNC->VLSR = 0x20; + TNC->VMSR = 0x30; + + TNCStream->MODEFLAG = COMMAND; + TNC->SENDPAC = 13; + TNC->CRFLAG = 1; + TNC->MALL = 1; + TNC->MMASK = -1; // MONITOR MASK FOR PORTS + TNC->TPACLEN = PACLEN; // TNC PACLEN + + TNC->COMCHAR = 3; + TNC->CMDTIME = 10; // SYSTEM TIMER = 100MS + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; + + SENDREPLY(TNC, SIGNON, 23); +} + + +static VOID TNCUNPROTOCMD(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) +{ +} + + +TCMDX TNCCOMMANDLIST[] = +{ + "AUTOLF ",2, ONOFF, offsetof(struct TNCDATA, AUTOLF), + "BBSMON ",6, ONOFF, offsetof(struct TNCDATA, BBSMON), + "BTEXT ",2,BTEXT,0, + "CONOK ",4,ONOFF_CONOK,offsetof(struct TNCDATA, CONOK), + "C SWITCH",8,CSWITCH,0, + "CBELL ",2,ONOFF,offsetof(struct TNCDATA, CBELL), + "CMDTIME ",2,VALUE, offsetof(struct TNCDATA, CMDTIME), + "CMSG ",4,ONOFF,offsetof(struct TNCDATA, CMSG), + "COMMAND ",3,VALHEX, offsetof(struct TNCDATA, COMCHAR), + "CONMODE ",4,CONMODE,0, + "CPACTIME",2,ONOFF,offsetof(struct TNCDATA, CPACTIME), + "CR ",2,ONOFF,offsetof(struct TNCDATA, CRFLAG), + "CSTATUS ",2,CStatus,0, + "CTEXT ",2,CTEXTCMD,0, + "APPLFLAG",5,APPL_VALHEX, offsetof(struct TNCDATA, APPLFLAGS), + "APPL ",4,APPL_VALHEX, offsetof(struct TNCDATA, APPLICATION), + "CONVERS ",4,TNCCONV,0, + "CONNECT ",1,TNCCONNECT,0, + "DISCONNE",1,TNCDISC,0, + "ECHO ",1,ONOFF,offsetof(struct TNCDATA, ECHOFLAG), + "FLOW ",4,ONOFF,offsetof(struct TNCDATA, FLOWFLAG), + "HEADERLN",2,ONOFF,offsetof(struct TNCDATA, HEADERLN), + "K ",1,TNCNODE,0, + "MTXFORCE",4,ONOFF,offsetof(struct TNCDATA, MTXFORCE), + "LCSTREAM",8,ONOFF, offsetof(struct TNCDATA, LCStream), + "LFIGNORE",3,ONOFF,offsetof(struct TNCDATA, LFIGNORE), + "MTX ",3,ONOFF,offsetof(struct TNCDATA, MTX), + "MALL ",2,ONOFF,offsetof(struct TNCDATA, MALL), + "MCOM ",4,ONOFF,offsetof(struct TNCDATA, MCOM), + "MCON ",2,ONOFF,offsetof(struct TNCDATA, MCON), + "MMASK ",2,VALHEX, offsetof(struct TNCDATA, MMASK), + "MONITOR ",3,ONOFF,offsetof(struct TNCDATA, TRACEFLAG), + "MYCALL ",2,SETMYCALL,0, + "NEWMODE ",2,ONOFF,offsetof(struct TNCDATA, NEWMODE), + "NODE ",3,TNCNODE,0, + "NOMODE ",2,ONOFF,offsetof(struct TNCDATA, NOMODE), + "SENDPAC ",2,VALHEX, offsetof(struct TNCDATA, SENDPAC), + "STREAMCA",8,ONOFF, offsetof(struct TNCDATA, StreamCall), + "STREAMDBL",7,ONOFF, offsetof(struct TNCDATA, StreamDbl), + "STREAMSW",3,VALHEX, offsetof(struct TNCDATA, StreamSW), + "PACLEN ",1,VALUE, offsetof(struct TNCDATA, TPACLEN), + "PASS ",3,VALHEX, offsetof(struct TNCDATA, PASSCHAR), + "RELEASE ",3,TNCRELEASE,0, + "RESTART ",7,TNCRESTART,0, + "TRANS ",1,TNCTRANS,0, + "UNPROTO ",1,TNCUNPROTOCMD,0, + "USERS ",2,VALUE, offsetof(struct TNCDATA, Users), +}; + + + +int NUMBEROFTNCCOMMANDS = sizeof(TNCCOMMANDLIST)/sizeof(TCMDX); + +/*NEWVALUE DW 0 +HEXFLAG DB 0 + + +NUMBER DB 4 DUP (0),CR +NUMBERH "$0000",CR + +BADMSG "?bad parameter",CR,0 + +BTHDDR ,0 ; CHAIN + DB 0 ; PORT + DW 7 ; LENGTH + DB 0F0H ; PID +BTEXTFLD DB 0DH,256 DUP (0) + +CMDENDADDR,0 ; POINTER TO END OF COMMAND + +MBOPTIONBYTE DB 0 + +NORMCALL DB 10 DUP (0) +AX25CALL DB 7 DUP (0) + +CONNECTCALL DB 10 DUP (0) ; CALLSIGN IN CONNECT MESSAGE +DIGICALL DB 10 DUP (0) ; DIGI IN CONNECT COMMAND +AX25STRING DB 64 DUP (0) ; DIGI STRING IN AX25 FORMAT +DIGIPTR ,0 ; POINTER TO CURRENT DIGI IN STRING + +NORMLEN ,0 + + EVEN +*/ + +int TRANSDELAY = 10; // 1 SEC + +//UNPROTOCALL DB "UNPROTO",80 DUP (0) + +char MONBUFFER[1000]; + +VOID TNC2GetChar(struct TNCDATA * TNC, int * returnedchar, int * moretocome) +{ + // Poll Node + + if (TNC->Mode == 0) + GETDATA(TNC); + + *returnedchar = -1; + *moretocome = 0; + + if (TNC->RXCOUNT == 0) + return; + + *returnedchar = *(TNC->GETPTR++); + + if (TNC->GETPTR == &TNC->TOUSERBUFFER[TNCBUFFLEN]) + TNC->GETPTR = &TNC->TOUSERBUFFER[0]; + + *moretocome = --TNC->RXCOUNT; //ANY MORE? + + if (TNC->RXCOUNT < 128) // GETTING LOW? + { + if (TNC->RTSFLAG & 1) // RTS UP? + { + // RTS HAD BEEN DROPPED TO STOP OTHER END SENDING - RAISE IT AGAIN + + TNC->RTSFLAG &= 0xFE; + } + } +} + +int TNCGetVMSR(struct TNCDATA * TNC, struct TNC2StreamInfo * TNCStream, BOOL ClearDeltas) +{ + // On TNC2 Connected bit should come from current stream + // flow control bits from TNC record + + int val = TNC->VMSR; + + if (TNC->Mode == TNC2) + val |= TNCStream->VMSR; + + if (ClearDeltas) + { + TNC->VMSR &= 0xF0; // CLEAR DELTA BITS + + if (TNC->Mode == TNC2) + TNCStream->VMSR &= 0xF0; + } + return val; +} + +BOOL TNCRUNNING;; + +VOID TNCBGThread(void * unused) +{ + TNCRUNNING = TRUE; + + Sleep(5000); + + while (TNCRUNNING) + { + TNCPoll(); + Sleep(50); + } +} + +VOID AllocateDEDChannel(struct TNCDATA * TNC, int Num) +{ + struct StreamInfo * Channel = zalloc(sizeof(struct StreamInfo)); + char * PNptr; + + // Only show last element of name on Streams display + + PNptr = &TNC->PORTNAME[0]; + + while (strchr(PNptr, '/')) + PNptr = strchr(PNptr, '/') + 1; + + sprintf(pgm, "DED %s", PNptr); + + TNC->Channels[Num] = Channel; + Channel->BPQStream = FindFreeStream(); + READCHANGE(Channel->BPQStream); // Prevent Initial *** disconnected + Debugprintf("BPQ32 DED Stream %d BPQ Stream %d", Num, Channel->BPQStream ); + + if (TNC->MODE) // if host mode, set appl + SetAppl(Channel->BPQStream, TNC->APPLFLAGS, TNC->APPLICATION); + + strcpy(pgm, "bpq32.exe"); +} + + +BOOL InitializeTNCEmulator() +{ + int resp, i; + ULONG OpenCount = 0; + DWORD Errorval; + int ApplNum = 1; + UINT APPLMASK; + struct TNC2StreamInfo * TNCStream; + + struct TNCDATA * TNC = TNCCONFIGTABLE; + + TNC2TABLE = TNCCONFIGTABLE; + + while (TNC) + { + // Com Port may be a hardware device (ie /dev/ttyUSB0) COMn or VCOMn (BPQ Virtual COM) + + char * Baud = strlop(TNC->PORTNAME, ':'); + char * PNptr; + + if (Baud == 0) + Baud = strlop(TNC->PORTNAME, ','); + + + PNptr = &TNC->PORTNAME[0]; + + // Only show last element of name on Streams display + + while (strchr(PNptr, '/')) + { + PNptr = strchr(PNptr, '/') + 1; + } + switch (TNC->Mode) + { + case TNC2: + + sprintf(pgm, "TNC2 %s", PNptr); + + // Start with number of streams, can add or remove with USRES command + + if (TNC->Users == 0) + TNC->Users = TNC->HOSTSTREAMS; + + if (TNC->Users == 0) + TNC->Users = TNC->HOSTSTREAMS = 1; + else + TNC->HOSTSTREAMS = TNC->Users; + + Debugprintf("TNC2 USers = %d, HOSTSTREAMS = %d\n", TNC->Users, TNC->HOSTSTREAMS); + + + for (i = 0; i < TNC->HOSTSTREAMS; i++) + { + TNCStream = TNC->TNC2Stream[i] = zalloc(sizeof(struct TNC2StreamInfo)); + + TNCStream->BPQPort = FindFreeStream(); + + if + (TNCStream->BPQPort == 0) + { + Debugprintf("Insufficient free Streams for TNC2 Emulator"); + return FALSE; + } + + READCHANGE(TNCStream->BPQPort); // Prevent Initial *** disconnected + + TNCStream->MODEFLAG = COMMAND; + + if (TNC->CONOK) + SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, TNC->APPLICATION); + else + SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, 0); + + } + + strcpy(pgm, "bpq32.exe"); + + if (TNC->TPACLEN == 0) + TNC->TPACLEN = PACLEN; // TNC PACLEN + + break; + + case DED: + + if (TNC->HOSTSTREAMS == 0) + TNC->HOSTSTREAMS = 4; // Default + + TNC->MALL = 1; + TNC->MTX = 1; + TNC->MCOM = 1; + TNC->MMASK = -1; // MONITOR MASK FOR PORTS + TNC->TPACLEN = PACLEN; // TNC PACLEN + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + AllocateDEDChannel(TNC, i); // Also used by Y command handler + } + + TNC->Channels[0] = zalloc(sizeof(struct StreamInfo)); + memcpy(TNC->Channels[0], TNC->Channels[1], sizeof(struct StreamInfo)); // For monitoring + + break; + + case KANTRONICS: + + sprintf(pgm, "KANT %s", PNptr); + + if (TNC->HOSTSTREAMS == 0) + TNC->HOSTSTREAMS = 1; // Default + + for (i = 0; i <= TNC->HOSTSTREAMS; i++) + { + struct StreamInfo * Channel; + + // Use Stream zero for defaults + + TNC->Channels[i] = malloc(sizeof (struct StreamInfo)); + memset(TNC->Channels[i], 0, sizeof (struct StreamInfo)); + + Channel = TNC->Channels[i]; + + Channel->BPQStream = FindFreeStream(); + READCHANGE(Channel->BPQStream); // Prevent Initial *** disconnected + + Debugprintf("BPQ32 KANT Init Stream %d BPQ Stream %d", i, Channel->BPQStream ); + + // channel->Chan_TXQ = 0; + // channel->BPQStream = 0; + // channel->Connected = FALSE; + // channel->MYCall[0] = 0; + + } + break; + + case SCS: + + TNC->ECHOFLAG = 1; + + if (TNC->HOSTSTREAMS == 0) + TNC->HOSTSTREAMS = 1; // Default + + TNC->MALL = 1; + TNC->MCOM = 1; + TNC->MMASK = -1; // MONITOR MASK FOR PORTS + TNC->TPACLEN = PACLEN; // TNC PACLEN + + sprintf(pgm, "SCS %s", PNptr); + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + struct StreamInfo * Channel = zalloc(sizeof(struct StreamInfo)); + + TNC->Channels[i] = Channel; + + Channel->BPQStream = FindFreeStream(); + READCHANGE(Channel->BPQStream); // Prevent Initial *** disconnected + + Debugprintf("BPQ32 SCS Init Stream %d BPQ Stream %d", i, Channel->BPQStream ); + } + + TNC->Channels[0] = zalloc(sizeof(struct StreamInfo)); + memcpy(TNC->Channels[0], TNC->Channels[1], sizeof(struct StreamInfo)); // For monitoring + + strcpy(pgm, "bpq32.exe"); + + break; + + } + + if (Baud) + TNC->Speed = atoi(Baud); + else + TNC->VCOM = TRUE; + + if (_memicmp(TNC->PORTNAME, "COM", 3) == 0) + { + TNC->VCOM = FALSE; + } + else + { + if (_memicmp(TNC->PORTNAME, "VCOM", 4) == 0) + TNC->ComPort = atoi(&TNC->PORTNAME[4]); + } + if (TNC->VCOM == 0) + { + // Real port + + TNC->hDevice = OpenCOMPort(TNC->PORTNAME, TNC->Speed, TRUE, TRUE, FALSE, 0); + + TNC->PortEnabled = 1; + + TNC->RTS = 1; +// TNC->DTR = 1; + } + else + { + // VCOM Port +#ifdef WIN32 + TNC->hDevice = BPQOpenSerialPort(TNC, &Errorval); +#else + TNC->hDevice = LinuxOpenPTY(TNC->PORTNAME); +#endif + if (TNC->hDevice != (HANDLE) -1) + { + if (TNC->NewVCOM == 0) + { + resp = BPQSerialIsCOMOpen(TNC->hDevice, &OpenCount); + TNC->PortEnabled = OpenCount; + } + + resp = BPQSerialSetCTS(TNC->hDevice); + resp = BPQSerialSetDSR(TNC->hDevice); + + TNC->CTS = 1; + TNC->DSR = 1; + } + else + { + Consoleprintf("TNC - Open Failed for Port %s", TNC->PORTNAME); + TNC->hDevice = 0; + } + } + + if (TNC->hDevice) + { + // Set up buffer pointers + + TNC->PUTPTR = TNC->GETPTR = &TNC->TOUSERBUFFER[0]; + TNC->RXCOUNT = 0; + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; + + TNC->VLSR = 0x20; + TNC->VMSR = 0x30; + +/* PUSH ECX + + MOV ESI,OFFSET UNPROTOCALL + CALL DECODECALLSTRING + + LEA EDI,UNPROTO[EBX] + MOV ECX,56 + REP MOVSB ; UNPROTO ADDR + + POP ECX +*/ + + APPLMASK = TNC->APPLICATION; + ApplNum = 1; + + while (APPLMASK && (APPLMASK & 1) == 0) + { + ApplNum++; + APPLMASK >>= 1; + } + + memcpy(TNC->MYCALL, &APPLCALLTABLE[ApplNum-1].APPLCALL_TEXT, 10); + + if (TNC->MYCALL[0] < '0') + memcpy(TNC->MYCALL, MYNODECALL, 10); + + strlop(TNC->MYCALL, ' '); + } + + TNC = TNC->Next; + } + +#ifdef LINBPQ + strcpy(pgm, "LinBPQ"); +#else + strcpy(pgm, "bpq32.exe"); +#endif + Consoleprintf("TNC Emulator Init Complete"); + + _beginthread(TNCBGThread,0,0); + + return TRUE; +} + +VOID CloseTNCEmulator() +{ + struct TNCDATA * TNC = TNC2TABLE; // malloc'ed + int i, Stream; + + TNCRUNNING = FALSE; + + while (TNC) + { + if (TNC->Mode == TNC2) + { + Stream = TNC->BPQPort; + + SetAppl(Stream, 0, 0); + Disconnect(Stream); + READCHANGE(Stream); // Prevent Initial *** disconnected + DeallocateStream(Stream); + } + else + { + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + Stream = TNC->Channels[i]->BPQStream; + + SetAppl(Stream, TNC->APPLFLAGS, 0); + Disconnect(Stream); + READCHANGE(Stream); // Prevent Initial *** disconnected + DeallocateStream(Stream); + } + } + CloseCOMPort(TNC->hDevice); + + TNC = TNC->Next; + } +} + +VOID TNCTimer() +{ + // 100 Ms Timer + + struct TNCDATA * TNC = TNC2TABLE; + struct StreamInfo * channel; + int n; + int NeedTrace = 0; + struct TNCDATA * TTNC = 0; // First TNC2 + + while (TNC) + { + if (TNC->LastDEDPollTime && (time(NULL) > TNC->LastDEDPollTime + 30)) // No polls for 30 secs + { + int Len = 0; + int Count; + + TNC->LastDEDPollTime = 0; + + Debugprintf("DED Host Application Lost"); + TNC->MODE = 0; + TNC->HOSTSTATE = 0; + + DisableAppl(TNC); + + // Clear Monitor Q + + GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); + + while (Len) + { + GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); + } + continue; + } + + + if (TNC->Mode != TNC2) + goto NotTNC2; + + if (TTNC == 0 && TNC->TNC2Stream[0]) // Save first TNC2 TNC + TTNC = TNC; + + + NeedTrace |= TNC->TRACEFLAG; //SEE IF ANY PORTS ARE MONITORING + + // CHECK FOR PACTIMER EXPIRY AND CMDTIME + + if (TNC->CMDTMR) + { + TNC->CMDTMR--; + + if (TNC->CMDTMR == 0) + { + // CMDTMR HAS EXPIRED - IF 3 COMM CHARS RECEIVED, ENTER COMMAND MODE + + if (TNC->COMCOUNT == 3) + { + // 3 ESCAPE CHARS RECEIVED WITH GUARDS - LEAVE TRAN MODE + + SETCOMM00(TNC); + + goto TIM100; //DONT RISK TRANSTIMER AND CMDTIME FIRING AT ONCE + } + + TNC->CMDTMR = 0; // RESET COUNT + goto TIM100; + + } + } + + if (TNC->TRANSTIMER) + { + TNC->TRANSTIMER--; + if (TNC->TRANSTIMER == 0) + { + if (TNC->MSGLEN) // ?MESSAGE ALREADY SENT + SENDPACKET(TNC); + } + } +TIM100: + // CHECK FLOW CONTROL + + if ((TNC->VMSR & 0x20)) // ALREADY OFF? + { + CHECKCTS(TNC); // No, so check + } + + goto NextTNC; + +NotTNC2: + + for (n = 1; n <= TNC->HOSTSTREAMS; n++) + { + channel = TNC->Channels[n]; + + if (channel->CloseTimer) + { + channel->CloseTimer--; + if (channel->CloseTimer == 0) + Disconnect(channel->BPQStream); + } + } +NextTNC: + TNC = TNC->Next; + } + if (TTNC) // Only if we have TNC2 Streams + DOMONITORING(NeedTrace, TTNC); +} + +/* +#ifndef WIN32 + +int TNCReadCOMBlock(HANDLE fd, char * Block, int MaxLength, int * err) +{ + int Length; + + *err = 0; + + Length = read(fd, Block, MaxLength); + + if (Length < 0) + { + if (errno != 11 && errno != 35) // Would Block + *err = errno; + + return 0; + } + + return Length; +} + +#endif +*/ + +void CheckForConnectStatusChange(struct TNCDATA * TNC); +void CheckForHostStatusChange(struct TNCDATA * TNC); +void CheckForDataFromHost(struct TNCDATA * TNC); +void CheckForDataFromTerminal(struct TNCDATA * TNC); + +int isTNCBusy(struct TNCDATA * TNC) +{ + // if using old VCOM check Q + +#ifdef WIN32 + + if (TNC->VCOM) + { + if (TNC->NewVCOM == 0) + { + int TXCount, RXCount; + + BPQSerialGetQCounts(TNC->hDevice, &RXCount, &TXCount); + + if (TXCount > 4096) + return TRUE; // Busy + + return FALSE; + } + + // Windows New VCOM cant check (I think!) + } + +#endif + + // + + return FALSE; + +} + +VOID TNCPoll() +{ + struct TNCDATA * TNC = TNC2TABLE; // malloc'ed + + // This logic had got very convoluted. This Tries + // to rationalize it + + while (TNC) + { + if (TNC->hDevice) + { + CheckForConnectStatusChange(TNC); + CheckForHostStatusChange(TNC); + CheckForDataFromHost(TNC); + CheckForDataFromTerminal(TNC); + } + + TNC = TNC->Next; + } +} + +void CheckForConnectStatusChange(struct TNCDATA * TNC) +{ +#ifdef WIN32 + if (TNC->VCOM && TNC->NewVCOM == 0) + { + // Can check if other end is connected + + int ConCount = 0; + + BPQSerialIsCOMOpen(TNC->hDevice, &ConCount); + + if (TNC->PortEnabled == 1 && ConCount == 0) + + // Connection has just closed - if connected, disconnect stream + + // This should close all streams on multistream port + + SessionControl(TNC->BPQPort, 2, 0); + + if (TNC->PortEnabled != ConCount) + { + TNC->PortEnabled = ConCount; + } + } +#endif +} + +void CheckForHostStatusChange(struct TNCDATA * TNC) +{ + if (TNC->Mode == KANTRONICS) + { + // Have to poll for Data and State changes + + int n, state, change; + struct StreamInfo * Channel; + + for (n = 1; n <= TNC->HOSTSTREAMS; n++) + { + Channel = TNC->Channels[n]; + + SessionState(Channel->BPQStream, &state, &change); + + if (change == 1) + { + if (state == 1) + + // Connected + + KANTConnected(TNC, Channel, n); + else + KANTDisconnected(TNC, Channel, n); + } + } + return; + } + + // ?? Should other modes check here ?? +} + +void CheckForDataFromHost(struct TNCDATA * TNC) +{ + unsigned int n; + int retval, more; + char TXMsg[1000]; + ULONG Read = 0; + + // I think we should check for space in TNC to Terminal Buffer + + if (TNC->RXCOUNT + 500 > TNCBUFFLEN) + return; + + // Only KANTRONICS and TNC2 check for data here + // DED and SCS check when polled by Host Program + + if (TNC->Mode == KANTRONICS) + { + // Have to poll for Data and State changes + + int n, len, count; + struct StreamInfo * Channel; + UCHAR Buffer[400]; + + for (n = 1; n <= TNC->HOSTSTREAMS; n++) + { + Channel = TNC->Channels[n]; + + do + { + if (TNC->RXCOUNT + 500 > TNCBUFFLEN) + return; + + GetMsg(Channel->BPQStream, &Buffer[3], &len, &count); + + if (len > 0) + { + // If a failure, set a close timer (for Airmail, etc) + + if (strstr(&Buffer[3], "} Downlink connect needs port number") || + strstr(&Buffer[3], "} Failure with ") || + strstr(&Buffer[3], "} Sorry, ")) + Channel->CloseTimer = CloseDelay * 10; + + else + Channel->CloseTimer = 0; // Cancel Timer + + if (TNC->MODE) + { + Buffer[0] = 'D'; + Buffer[1] = '1'; + Buffer[2] = n + '@'; + SendKISSData(TNC, Buffer, len+3); + } + else + SendDataToTNC(TNC, &Buffer[3], len); + } + } + while (0); //(count > 0); + } + return; + } + + if (TNC->Mode == SCS) + return; + + n = 0; + +getloop: + + TNC2GetChar(TNC, &retval, &more); + + if (retval != -1) + TXMsg[n++] = retval; + + if (more > 0 && n < 1000) goto getloop; + + if (n > 0) + WriteCOMBlock(TNC->hDevice, TXMsg, n); + + return; + +} +/* Where does this go ?? + +// We look for change on current RX Stream + + retval = TNCGetVMSR(TNC, TNC->TNC2Stream[TNC->RXStream], TRUE); + + if ((retval & 8) == 8) //' DCD (Connected) Changed + { + TNC->DCD = (retval & 128) / 128; + + if (TNC->DCD == 1) + BPQSerialSetDCD(TNC->hDevice); + else + BPQSerialClrDCD(TNC->hDevice); + } + + if ((retval & 1) == 1) //' CTS (Flow Control) Changed + { + TNC->CTS = (retval & 16) / 16; + + if (TNC->CTS == 1) + BPQSerialSetCTS(TNC->hDevice); + else + BPQSerialClrCTS(TNC->hDevice); + + } + + BPQSerialGetDTRRTS(TNC->hDevice,&ModemStat); + + + if ((ModemStat & 1) != TNC->DTR) + { + TNC->DTR=!TNC->DTR; + } + + if ((ModemStat & 2) >> 1 != TNC->RTS) + { + TNC->RTS=!TNC->RTS; + } + + TNC = TNC->Next; + continue; + } +#endif + { + // Real Port or Linux Virtual + + int Read, n; + int retval, more; + char TXMsg[500]; +#ifndef WIN32 + int err; + + // We can tell if partner has gone on PTY Pair - read returns 5 + + if (TNC->Mode == KANTRONICS || TNC->Mode == SCS) + Read = TNCReadCOMBlock(TNC->hDevice, &TNC->TOUSERBUFFER[TNC->RXBPtr], 1000 - TNC->RXBPtr, &err); + else + Read = TNCReadCOMBlock(TNC->hDevice, rxbuffer, 1000, &err); + + if (err) + { + if (TNC->PortEnabled) + { + TNC->PortEnabled = FALSE; + DisableAppl(TNC); + Debugprintf("Device %s closed", TNC->PORTNAME); + } + } + else + TNC->PortEnabled = TRUE; + +#else + if (TNC->Mode == KANTRONICS || TNC->Mode == SCS) + Read = ReadCOMBlock(TNC->hDevice, &TNC->TOUSERBUFFER[TNC->RXBPtr], 1000 - TNC->RXBPtr); + else + Read = ReadCOMBlock(TNC->hDevice, rxbuffer, 1000); +#endif + + if (Read) + { + if (TNC->Mode == TNC2) + { + for (n = 0; n < Read; n++) + TNC2PutChar(TNC, rxbuffer[n]); + } + else if (TNC->Mode == DED) + { + for (n = 0; n < Read; n++) + TfPut(TNC, rxbuffer[n]); + } + else if (TNC->Mode == KANTRONICS) + { + TNC->RXBPtr += Read; + ProcessPacket(TNC, TNC->TOUSERBUFFER, TNC->RXBPtr); + } + else if (TNC->Mode == SCS) + { + TNC->RXBPtr += Read; + ProcessSCSPacket(TNC, TNC->TOUSERBUFFER, TNC->RXBPtr); + } + } + + n=0; + + getloopR: + + TNC2GetChar(TNC, &retval, &more); + + if (retval != -1) + TXMsg[n++] = retval; + + if (more > 0 && n < 500) goto getloopR; + + if (n > 0) + + { + resp = WriteCOMBlock(TNC->hDevice, TXMsg, n); + } + } + TNC = TNC->Next; + } +} +*/ + + +void CheckForDataFromTerminal(struct TNCDATA * TNC) +{ + unsigned int n; + char rxbuffer[1000]; + ULONG Read = 0, resp; + + if (TNC->Mode == KANTRONICS) + resp = GetDataFromTNC(TNC, &TNC->TOUSERBUFFER[TNC->RXBPtr], 1000 - TNC->RXBPtr, &Read); + + else if (TNC->Mode == SCS) + resp = GetDataFromTNC(TNC, &TNC->FROMUSERBUFFER[TNC->FROMUSERLEN], TNCBUFFLEN - TNC->FROMUSERLEN, &Read); + + else + resp = GetDataFromTNC(TNC, rxbuffer, 1000, &Read); + + if (Read) + { + if (TNC->Mode == TNC2) + { + for (n = 0; n < Read; n++) + TNC2PutChar(TNC, rxbuffer[n]); + } + else if (TNC->Mode == DED) + { + for (n = 0; n < Read; n++) + TfPut(TNC, rxbuffer[n]); + } + else if (TNC->Mode == KANTRONICS) + { + TNC->RXBPtr += Read; + ProcessPacket(TNC, TNC->TOUSERBUFFER, TNC->RXBPtr); + } + else if (TNC->Mode == SCS) + { + TNC->FROMUSERLEN += Read; + ProcessSCSPacket(TNC, TNC->FROMUSERBUFFER, TNC->FROMUSERLEN); + } + } +} + + +VOID DOMONITORING(int NeedTrace, struct TNCDATA * TTNC) +{ + // IF ANY PORTS HAVE MONITOR ENABLED, SET MONITOR BIT ON FIRST PORT + + int Tracebit = 0, len, count, n; + time_t Stamp; + uint64_t SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + struct TNCDATA * TNC = TNC2TABLE; + int BPQStream = TTNC->TNC2Stream[0]->BPQPort; + + if (NeedTrace) + Tracebit = 0x80; + + if (BPQStream) + { + if (TTNC->CONOK && TTNC->Mode == TNC2) + SetAppl(BPQStream, TTNC->APPLFLAGS | Tracebit, TTNC->APPLICATION); + else + SetAppl(BPQStream, TTNC->APPLFLAGS | Tracebit, 0); + } + + Stamp = GetRaw(BPQStream, (char *)&MONITORDATA, &len, &count); + + if (len == 0) + return; + + len = DecodeFrame(&MONITORDATA, MONBUFFER, Stamp); + + while (TNC) + { + if (TNC->Mode == TNC2 && TNC->TRACEFLAG) + { + IntSetTraceOptionsEx(TNC->MMASK, TNC->MTX, TNC->MCOM, 0); + len = IntDecodeFrame(&MONITORDATA, MONBUFFER, Stamp, TNC->MMASK, FALSE, FALSE); +// printf("%d %d %d %d %d\n", len, MMASK, MTX, MCOM, MUIONLY); + IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + if (len) + { + for (n = 0; n < len; n++) + { + PUTCHARINBUFFER(TNC, MONBUFFER[n]); + } + } + } + TNC=TNC->Next; + } +} + + +VOID TNC2PutChar(struct TNCDATA * TNC, int Char) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + if (TNCStream->MODEFLAG & COMMAND) + goto KEYB06C; // COMMAND MODE - SKIP TRANS TEST + + if (TNCStream->MODEFLAG & TRANS) + goto KEYB06T; // TRANS MODE + + // CONV MODE - SEE IF CPACTIME ON + + if (TNC->CPACTIME) + TNC->TRANSTIMER = TRANSDELAY; + + goto KEYB06; // PROCESS CHAR + +KEYB06T: + +// Transparent Mode - See if Escape Sequence Received (3 esc chars, with guard timer) + +// CHECK FOR COMMAND CHAR IF CMDTIME EXPIRED OR COMAND CHAR ALREADY RECEIVED + + if (TNC->COMCOUNT) + goto KBTRN3; // ALREADY GOT AT LEAST 1 + + if (TNC->CMDTMR) + goto KBTRN5; // LESS THAN CMDTIME SINCE LAST CHAR + +KBTRN3: + + if (Char != TNC->COMCHAR) + { + TNC->COMCOUNT = 0; + goto KBTRN5; // NOT COMMAND CHAR + } + + TNC->COMCOUNT++; + +KBTRN5: + + TNC->CMDTMR = TNC->CMDTIME; // REPRIME ESCAPE TIMER + + TNC->TRANSTIMER = TRANSDELAY; + + KBNORM(TNC, Char); + return; // TRANSPARENT MODE + +KEYB06: + +// STILL JUST CONV MODE + + if (Char != TNC->SENDPAC) + goto NOTSENDPAC; + +// SEND PACKET CHAR - SHOUD WE SEND IT? + + TNC->TRANSTIMER = 0; + + if (TNC->CRFLAG) + KBNORM(TNC, Char); // PUT CR IN BUFFER + + SENDPACKET(TNC); + return; + + +NOTSENDPAC: +KEYB06C: + + // COMMAND OR CONV MODE + + // Check for Escaped + + if (TNC->InEscape) + { + TNC->InEscape = 0; + KBNORM(TNC, Char); // Process as normal chars + return; + } + + if (TNC->InStreamSW) + { + TNC->InStreamSW = 0; + + if (Char != TNC->StreamSW) + { + // Switch TX Stream if valid + + int n; + + if (TNC->ECHOFLAG) + KBECHO(TNC, Char); + + if (TNC->LCStream) + Char = toupper(Char); + + n = Char - 'A'; + + if (n >= 0 && TNC->TNC2Stream[n]) + TNC->TXStream = n; + + return; + } + } + + if (Char == TNC->PASSCHAR) + { + TNC->InEscape = 1; + return; + } + + + + if (TNC->StreamSW && Char == TNC->StreamSW) + { + TNC->InStreamSW = 1; + + if (TNC->ECHOFLAG) + KBECHO(TNC, Char); + + return; + } + + if (Char < 32) // control + { + if (Char == 10 && TNC->LFIGNORE) + return; + + if (Char == 8) + { + if (TNC->MSGLEN == 0) + return; + + TNC->MSGLEN--; + TNC->CURSOR--; + + if (TNC->ECHOFLAG) + { + KBECHO(TNC, Char); // Delete char from display + KBECHO(TNC, ' '); + KBECHO(TNC, Char); + } + return; + } + + if (Char == 26) // Ctrl/Z + { + KBNORM(TNC, Char); // FOR MBX TERMINATOR + return; + } + + if (Char == TNC->COMCHAR) + { + SETCOMMANDMODE(TNC); + return; + } + + if (TNCStream->MODEFLAG & COMMAND) + { + if (Char == 0x14) // CTRL/T + { + TNC->TRACEFLAG ^= 1; + return; + } + + if (Char == 13) + { + KBNORM(TNC, 13); // PUT CR IN BUFFER + SENDPACKET(TNC); + return; + } + } + KBNORM(TNC, Char); // Process others as normal chars + } KBNORM(TNC, Char); +} + +VOID KBNORM(struct TNCDATA * TNC, int Char) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + if (TNC->MSGLEN > 256) + goto TOOLONG; // PROTECT BUFFER + + *(TNC->CURSOR++) = Char; + TNC->MSGLEN++; + +TOOLONG: + + if (TNC->ECHOFLAG) + KBECHO(TNC, Char); + + if (TNC->MSGLEN < TNCStream->TPACLEN) + return; + +// DONT APPLY PACLEN IN COMMAND MODE + + if (TNCStream->MODEFLAG & COMMAND) + return; + + SENDPACKET(TNC); // Send what we have +} + + +VOID SETCOMMANDMODE(struct TNCDATA * TNC) +{ + if (TNC->MSGLEN) + SENDPACKET(TNC); + + SETCOMM00(TNC); +} + +VOID SETCOMM00(struct TNCDATA * TNC) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + TNCStream->MODEFLAG |= COMMAND; // BACK TO COMMAND MODE + TNCStream->MODEFLAG &= ~(CONV+TRANS); + TNC->TRANSTIMER = 0; // CANCEL TRANS MODE SEND TIMER + TNC->AUTOSENDFLAG = 0; // IN CASE ALREADY SET + + CheckForStreamChange(TNC, TNC->TXStream); // Send Stream Switched Message if changed + + SENDREPLY(TNC, CMDMSG, 4); + + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; +} + + + +VOID SENDPACKET(struct TNCDATA * TNC) +{ + // SEE IF COMMAND STATE + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + int Stream = 0; // Unprooto + + if (TNCStream->MODEFLAG & COMMAND) + { + TNCCOMMAND(TNC); // COMMAND TO TNC + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; + return; + } + + // IF CONNECTED, SEND TO L4 (COMMAND HANDLER OR DATA), + // OTHERWISE SEND AS AN UNPROTO FRAME (TO ALL PORTS) + + + if (TNCStream->VMSR & 0x80) // CONNECTED? + Stream = TNCStream->BPQPort; + + SendMsg(Stream, TNC->TONODEBUFFER, TNC->MSGLEN); + + TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START + TNC->MSGLEN = 0; + + CHECKCTS(TNC); // SEE IF NOW BUSY + + return; +} + +VOID KBECHO(struct TNCDATA * TNC, int Char) +{ + PUTCHARINBUFFER(TNC, Char); +} + +VOID TNCCOMMAND(struct TNCDATA * TNC) +{ + // PROCESS COMMAND TO TNC CODE + + char * ptr, * ptr1, * ptr2; + int n; + TCMDX * CMD; + + *(--TNC->CURSOR) = 0; + + + strcat(TNC->TONODEBUFFER, " "); + + ptr = strchr(TNC->TONODEBUFFER, ' '); + + if (ptr) + { + // convert command to upper case, leave tail + + *ptr = 0; + _strupr(TNC->TONODEBUFFER); + *ptr = ' '; + } + + if (_memicmp(ptr, " switch", 7) == 0) + _strupr(ptr); // Special Case + + ptr1 = &TNC->TONODEBUFFER[0]; // + + n = 10; + + while ((*ptr1 == ' ' || *ptr1 == 0) && n--) + ptr1++; // STRIP LEADING SPACES and nulls (from keepalive) + + if (n == -1) + { + // Null command + + SENDREPLY(TNC, CMDMSG, 4); + return; + } + + ptr2 = ptr1; // Save + + CMD = &TNCCOMMANDLIST[0]; + n = 0; + + for (n = 0; n < NUMBEROFTNCCOMMANDS; n++) + { + int CL = CMD->CMDLEN; + + ptr1 = ptr2; + + // ptr1 is input command + + if (memcmp(CMD->String, ptr1, CL) == 0) + { + // Found match so far - check rest + + char * ptr2 = &CMD->String[CL]; + + ptr1 += CL; + + if (*(ptr1) != ' ') + { + while(*(ptr1) == *ptr2 && *(ptr1) != ' ') + { + ptr1++; + ptr2++; + } + } + + if (*(ptr1) == ' ') + { + ptr1++; // Skip space + + CMD->CMDPROC(TNC, ptr1, CMD); + SENDREPLY(TNC, CMDMSG, 4); + + return; + } + } + + CMD++; + } + SENDREPLY(TNC, WHATMSG, 8); +} + +/* +; +UNPROTOCMD: +; +; EXTRACT CALLSIGN STRING +; + CMP BYTE PTR [ESI],20H + JE UNPROTODIS + + CMP BYTE PTR [ESI],"*" + JE CLEARUNPROTO + + CALL DECODECALLSTRING ; CONVERT TO AX25 FORMAT + + JZ UNPROTOOK + + JMP TNCDUFF + +CLEARUNPROTO: + + LEA EDI,UNPROTO[EBX] + MOV AL,0 + MOV ECX,63 + REP STOSB ; COPY IN + +UNPROTODIS: + + MOV AL,1 + CALL DISPLAYUNPROTO ; DISPLAY CURRENT SETTING + JMP SENDOK + +UNPROTOOK: + + PUSH ESI + MOV AL,0 + CALL DISPLAYUNPROTO ; DISPLAY OLD STRRING + POP ESI + + LEA EDI,UNPROTO[EBX] + MOV ECX,63 + REP MOVSB ; COPY IN +CONMODE: + JMP SENDOK +;CONMODE: + JMP KBRET + +DISPLAYUNPROTO: +; + PUSH EAX + + MOV ESI,OFFSET UNPROT + MOV ECX,8 + CALL PUTSTRINGINBUFFER + + MOV ESI,OFFSET WAS ; DISPLAY "was" + MOV ECX,5 + + POP EAX + OR AL,AL + JZ DISPU00 ; NO + + MOV ECX,1 ; LEAVE OUT "WAS" + +DISPU00: + + CALL PUTSTRINGINBUFFER + + LEA ESI,UNPROTO[EBX] + CMP BYTE PTR [ESI],40H + JBE DISPUPRET + + CALL CONVFROMAX25 + + PUSH ESI + + MOV ESI,OFFSET NORMCALL + + CALL PUTSTRINGINBUFFER + + POP ESI + + CMP BYTE PTR [ESI],0 + JE DISPUPRET + + PUSH ESI + + MOV ESI,OFFSET VIA + MOV ECX,5 + CALL PUTSTRINGINBUFFER + + POP ESI + +DISPUPLOOP: + + CALL CONVFROMAX25 + + PUSH ESI + MOV ESI,OFFSET NORMCALL + INC ECX + CALL PUTSTRINGINBUFFER + POP ESI + + CMP BYTE PTR [ESI],0 + JNE DISPUPLOOP + + +DISPUPRET: + MOV AL,0DH + CALL PUTCHARINBUFFER + RET + + +BTEXT: +; + CMP BYTE PTR [ESI],20H + JE BTDIS +; + PUSH ESI + MOV AL,0 + CALL DISPLAYBT ; DISPLAY OLD STRING + POP ESI + + MOV EDI,OFFSET BTEXTFLD + MOV ECX,255 +BTLOOP: + LODSB + STOSB + CMP ESI,CMDENDADDR ; END? + JE BTEND + + LOOP BTLOOP +BTEND: + XOR AL,AL + STOSB ; NULL ON END +; +; SET UP TO SEND IT AS A UI +; + MOV ECX,EDI + MOV ESI,OFFSET BTHDDR + SUB ECX,ESI + MOV MSGLENGTH[ESI],CX +; +; PASS TO SWITCH +; + MOV ESI,OFFSET BTEXTFLD + SUB ECX,6 ; DONT NEED HEADER + + MOV AH,12 ; UPDATE FUNCTIONS + MOV DX,1 ; UPDATE BT + + CALL NODE ; PASS TO NODE + + JMP SENDOK + +BTDIS: + MOV AL,1 + CALL DISPLAYBT ; DISPLAY CURRENT SETTING + JMP SENDOK + + +DISPLAYBT: +; + PUSH EAX + + MOV ESI,OFFSET BTCMD + MOV ECX,8 + CALL PUTSTRINGINBUFFER + + MOV ESI,OFFSET WAS ; DISPLAY "was" + MOV ECX,5 + + POP EAX + OR AL,AL + JZ DISPBT00 ; NO + + MOV ECX,1 ; LEAVE OUT "WAS" + +DISPBT00: + CALL PUTSTRINGINBUFFER + + MOV ESI,OFFSET BTEXTFLD +DISPBT10: + LODSB + OR AL,AL + JZ DISPBT20 + + CALL PUTCHARINBUFFER + + JMP DISPBT10 + +DISPBT20: + MOV AL,0DH + CALL PUTCHARINBUFFER + RET + + +*/ + +VOID DOCONMODECHANGE(struct TNCDATA * TNC, int Stream) +{ + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[Stream]; + + TNCStream->VMSR |= 0x88; // SET CONNECTED + + // IF NOMODE IS ON LEAVE IN TNC COMMAND MODE, ELSE PUT INTO CONV MODE + // (MAY NEED TO IMPLEMENT CONMODE SOMETIME) + + if (TNC->NOMODE) + return; + + TNCStream->MODEFLAG |= CONV; // INTO CONVERSE MODE + TNCStream->MODEFLAG &= ~(COMMAND+TRANS); +} + +VOID SENDREPLY(struct TNCDATA * TNC, char * Msg, int Len) +{ + int n = 0; + + CheckForStreamChange(TNC, TNC->TXStream); // Send Stream Switched Message if changed + + for (n= 0; n < Len; n++) + { + PUTCHARINBUFFER(TNC, Msg[n]); + } +} + + +VOID SEND_CONNECTED(struct TNCDATA * TNC, int ToStream) +{ + // SEND TAPR-STYLE *** CONNECTED TO CURRENT PORT + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[ToStream]; + + int len; + char Response[128]; + char Call[11] = ""; + int paclen, dummy; + BPQVECSTRUC * SESS; + TRANSPORTENTRY * L4 = NULL; + int stream; + + GetConnectionInfo(TNCStream->BPQPort, Call, &dummy, &dummy, &paclen, &dummy, &dummy); + + if (paclen) + TNCStream->TPACLEN = paclen; + + if (TNCStream->MODEFLAG & TRANS) + return; //NOT IF TRANSPARENT + + strlop(Call, ' '); + + strcpy(TNCStream->RemoteCall, Call); + + CheckForStreamChange(TNC, ToStream); // Send Stream Switched Message if changed + + if (TNC->CBELL) + len = sprintf(Response, "%s%s%c\r", CONMSG, Call, 7); // Add BELL char + else + len = sprintf(Response, "%s%s\r", CONMSG, Call); + + SENDREPLY(TNC, Response, len); + + // If incoming session Send CTEXT if set + + stream = TNCStream->BPQPort; + stream--; // API uses 1 - 64 + + if (stream < 0 || stream > 63) + return; + + SESS = &BPQHOSTVECTOR[stream]; + + if (SESS && SESS->HOSTSESSION) + L4 = SESS->HOSTSESSION; + + if (L4 && (L4->L4CIRCUITTYPE & DOWNLINK)) + { + if (TNC->CMSG && TNC->CTEXT[0]) + { + // Add CTEXT + int n; + char Msg[256]; + + n = sprintf(Msg, "%s\r", TNC->CTEXT); + SendMsg(TNCStream->BPQPort, Msg, n); + } + + // if CHECK_FOR_ESC set in applflags send "^d to disconnect msg + + if ((TNC->APPLFLAGS & CHECK_FOR_ESC)) // If incoming session + { + char Msg[] = "Send ^D to disconnect\r"; + + SendMsg(TNCStream->BPQPort, Msg, (int)strlen(Msg)); + } + } +} + +VOID PUTCHARINBUFFER(struct TNCDATA * TNC, int Char) +{ + // CALLED BY L4 CODE TO PASS DATA TO VIRTUAL TNC + ; + if (TNC->RXCOUNT >= TNCBUFFLEN) + { + // OVERRUN - LOSE IT + + TNC->VLSR |= 2; // SET OVERRUN ERROR + return; + } + + TNC->VLSR &= ~2; // CLEAR OVERRRUN + + *(TNC->PUTPTR++) = Char; + TNC->RXCOUNT++; + + if (TNC->PUTPTR == &TNC->TOUSERBUFFER[TNCBUFFLEN]) + TNC->PUTPTR = &TNC->TOUSERBUFFER[0]; + + if(TNC->RXCOUNT > TNCBUFFLEN-300) // ALLOW FOR FULL PACKET + { + // BUFFER GETTING FULL - DROP RTS/DTR + + TNC->RTSFLAG |= 1; // SET BUSY + } + + if (Char == 13 && TNC->AUTOLF) + PUTCHARINBUFFER(TNC, 10); // Add LF +} + + +VOID CHECKCTS(struct TNCDATA * TNC) +{ + // SEE IF CROSS-SESSION STILL BUSY + + if (RXCount(TNC->BPQPort) > 4) + { + // Busy + + if ((TNC->VMSR & 0x10) == 0) // ALREADY OFF? + return; // No Change + + TNC->VMSR &= 0xef; // Drop CTS + TNC->VMSR |= 1; // Delta bit + return; + } + + // Not busy + + if (TNC->VMSR & 0x10) // ALREADY ON? + return; // No Change + + TNC->VMSR |= 0x11; // CTS AND DELTA CTS +} + + + +VOID CONNECTTONODE(struct TNCDATA * TNC) +{ + char AXCALL[7]; + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + int SaveAuthProg = AuthorisedProgram; + + AuthorisedProgram = 1; + SessionControl(TNCStream->BPQPort, 1, TNC->APPLICATION); + AuthorisedProgram = SaveAuthProg; + + ConvToAX25(TNC->MYCALL, AXCALL); + ChangeSessionCallsign(TNCStream->BPQPort, AXCALL); + + // Set default Paclen + + TNCStream->TPACLEN = TNC->TPACLEN; +} + + +VOID GETDATA(struct TNCDATA * TNC) +{ + // I'm sure this should only be called for TNC2 devices + + struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; + + int state, change, InputLen, count, n, i; + char InputBuffer[512]; + + // LOOK FOR STATUS CHANGE + + for (i = 0; i < TNC->HOSTSTREAMS; i++) + { + TNCStream = TNC->TNC2Stream[i]; + + LocalSessionState(TNCStream->BPQPort, &state, &change, TRUE); + + if (change == 1) + { + if (state == 1) // Connected + { + SEND_CONNECTED(TNC, i); + DOCONMODECHANGE(TNC, i); // SET CONNECTED AND CHANGE MODE IF NEEDED + } + else + { + TNCStream->MODEFLAG |= COMMAND; + TNCStream->MODEFLAG &= ~(CONV+TRANS); + + TNCStream->VMSR &= 0x7F; // DROP DCD + TNCStream->VMSR |= 8; // DELTA DCD + + CheckForStreamChange(TNC, i); // Send Stream Switched Message if changed + SENDREPLY(TNC, DISCONNMSG, 18); + } + } + else + { + // No Change + + // VERIFY CURRENT STATE + + if (state == 1) // Connected + { + // SWITCH THINKS WE ARE CONNECTED + + if ((TNCStream->VMSR & 0x80) == 0) + { + // TNC Doesn't + + SEND_CONNECTED(TNC, i); + DOCONMODECHANGE(TNC, i); // SET CONNECTED AND CHANGE MODE IF NEEDED + } + } + else + { + // SWITCH THINKS WE ARE DISCONNECTED + + if (TNCStream->VMSR & 0x80) + { + // We Disagree, so force off + + TNCStream->MODEFLAG |= COMMAND; + TNCStream->MODEFLAG &= ~(CONV+TRANS); + + TNCStream->VMSR &= 0x7F; // DROP DCD + TNCStream->VMSR |= 8; // DELTA DCD + + CheckForStreamChange(TNC, i); // Send Stream Switched Message if changed + SENDREPLY(TNC, DISCONNMSG, 18); + } + } + } + + // SEE IF ANYTHING QUEUED + + if (TNC->RTSFLAG & 1) + continue; + + GetMsg(TNCStream->BPQPort, InputBuffer, &InputLen, &count); + + if (InputLen == 0) + continue; + + CheckForStreamChange(TNC, i); // Send Stream Switched Message if changed + + // if CHECK_FOR_ESC set in APPLFLAGS looks for Disconnect Escape + + if (TNC->APPLFLAGS & CHECK_FOR_ESC) + { + // look for ^D (or ^d) + + if (InputLen == 3) + { + if (_memicmp(InputBuffer, "^D\r", 3) == 0) + { + Disconnect(TNCStream->BPQPort); + continue; + } + } + } + + for (n = 0; n < InputLen; n++) + { + char c = InputBuffer[n]; + + if (TNC->StreamDbl && c == TNC->StreamSW) + PUTCHARINBUFFER(TNC, TNC->StreamSW); + + PUTCHARINBUFFER(TNC, c); + } + } +} + +// DED Mode Support + +unsigned char PARAMREPLY[]="* 0 0 64 10 4 4 10 100 18000 30 2 0 2\r\n"; + +#define PARAMPORT PARAMREPLY[2] + +#define LPARAMREPLY 39 + +unsigned char BADCMDREPLY[]="\x2" "INVALID COMMAND\x0"; + +#define LBADCMDREPLY 17 //sizeof BADCMDREPLY + +unsigned char DATABUSYMSG[]="\x2" "TNC BUSY - LINE IGNORED\x0"; +#define LDATABUSY 25 + +unsigned char BADCONNECT[]="\x2" "INVALID CALLSIGN\x0"; +#define LBADCONNECT 18 + +unsigned char BUSYMSG[]="BUSY fm SWITCH\x0"; + +//unsigned char CONSWITCH[]="\x3" "(1) CONNECTED to \x0"; + +unsigned char DEDSWITCH[]="\x1" "0:SWITCH \x0"; +#define LSWITCH 14 + +unsigned char NOTCONMSG[]="\x1" "CHANNEL NOT CONNECTED\x0"; +#define LNOTCON 23 + +unsigned char ALREADYCONMSG[]="You are already connected on another port\r"; +#define ALREADYLEN 45 + + +byte * EncodeCall(byte * Call); +VOID SENDENFORCINGPACLEN(struct StreamInfo * Channel, char * Msg, int Len); +VOID SENDCMDREPLY(struct TNCDATA * TNC, char * Msg, int Len); +int DOCOMMAND(struct TNCDATA * conn); +int PROCESSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel); + + + + +VOID PUTSTRING(struct TNCDATA * conn, UCHAR * Msg) +{ + int len = (int)strlen(Msg); + + while (len) + { + *(conn->PUTPTR++) = *(Msg++); + + if (conn->PUTPTR == &conn->TOUSERBUFFER[TNCBUFFLEN]) + conn->PUTPTR = (UCHAR *)&conn->TOUSERBUFFER; + + conn->RXCOUNT++; + + len--; + } +} + + +int PUTCHARx(struct TNCDATA * conn, UCHAR c) +{ + *(conn->PUTPTR++) = c; + + if (conn->PUTPTR == &conn->TOUSERBUFFER[TNCBUFFLEN]) + conn->PUTPTR = (UCHAR *)&conn->TOUSERBUFFER; + + conn->RXCOUNT++; + + return 0; +} + + + +VOID DisableAppl(struct TNCDATA * TNC) +{ + int i, Stream; + + for (i = 0; i <= TNC->HOSTSTREAMS; i++) + { + Stream = TNC->Channels[i]->BPQStream; + + SetAppl(Stream, TNC->APPLFLAGS, 0); + Disconnect(Stream); + READCHANGE(Stream); // Prevent Initial *** disconnected + } +} + +VOID EnableAppl(struct TNCDATA * TNC) +{ + int i; + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + SetAppl(TNC->Channels[i]->BPQStream, TNC->APPLFLAGS, TNC->APPLICATION); + } +} + +VOID EnableDEDAppl(struct TNCDATA * TNC) +{ + int i; + + SetAppl(TNC->Channels[0]->BPQStream, 2 | TNC->TRACEFLAG, TNC->APPLICATION); + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + SetAppl(TNC->Channels[i]->BPQStream, TNC->APPLFLAGS, TNC->APPLICATION); + } +} +BOOL TfPut(struct TNCDATA * TNC, UCHAR character) +{ + struct StreamInfo * Channel; + TRANSPORTENTRY * L4 = NULL; + + if (!TNC->MODE) + goto CHARMODE; + +// HOST MODE + + if (TNC->HOSTSTATE == 0) + { + TNC->MSGCHANNEL = character; + TNC->HOSTSTATE++; + return TRUE; + } + + if (TNC->HOSTSTATE == 1) + { + TNC->MSGTYPE = character; + TNC->HOSTSTATE++; + return TRUE; + } + + if (TNC->HOSTSTATE == 2) + { + TNC->MSGCOUNT = character; + TNC->MSGLENGTH = character; + TNC->MSGCOUNT++; + TNC->MSGLENGTH++; + TNC->HOSTSTATE++; + + TNC->DEDCURSOR = &TNC->DEDTXBUFFER[0]; + return TRUE; + } + +// RECEIVING COMMAND/DATA + + *(TNC->DEDCURSOR++) = character; + + TNC->MSGCOUNT--; + + if (TNC->MSGCOUNT) + return TRUE; // MORE TO COME + + TNC->HOSTSTATE=0; + + if (TNC->MSGCHANNEL <= TNC->HOSTSTREAMS) + Channel = TNC->Channels[TNC->MSGCHANNEL]; + else + Channel = TNC->Channels[1]; + + DEDPROCESSHOSTPACKET(Channel, TNC); + + TNC->HOSTSTATE = 0; + + return TRUE; + + +CHARMODE: + + if (character == 0x11) return TRUE; + + if (character == 0x18) + { + // CANCEL INPUT + + TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER; + + return(TRUE); + } + + *(TNC->CURSOR++) = character; + + if (character == 1 && (TNC->CURSOR > &TNC->TONODEBUFFER[4]) && *(TNC->CURSOR - 2) == 1 && *(TNC->CURSOR - 3) == 1) + { + // Looks like a resync request - Appl thinks we are in host mode + + TNC->MODE = 1; + TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER; + + EnableDEDAppl(TNC); + return(TRUE); + } + + + if (TNC->CURSOR == &TNC->TONODEBUFFER[300]) + TNC->CURSOR--; + + if (character == 0x0d) + { + // PROCESS COMMAND (UNLESS HOST MODE) + + *(TNC->CURSOR++) = 0; + + DOCOMMAND(TNC); + } + return TRUE; +} + + +int DEDPROCESSHOSTPACKET(struct StreamInfo * Channel, struct TNCDATA * TNC) +{ + UCHAR * TXBUFFERPTR; + int i; + int Work; + char WorkString[256]; + int State, Change, Count; + TRANSPORTENTRY * L4 = NULL; + unsigned char * MONCURSOR=0; + int SaveAuthProg = 0; + unsigned char * mcmdptr = &TNC->DEDTXBUFFER[1]; + TXBUFFERPTR = &TNC->DEDTXBUFFER[0]; + + if (Channel->Chan_TXQ == (UCHAR *)(ptrdiff_t) -1) + { + Channel->Chan_TXQ = 0; + } + + if (TNC->MSGTYPE != 0) + goto NOTDATA; + + goto HOSTDATAPACKET; + +//HOSTCMDS: +// DD 'G','I', 'J', 'C', 'D', 'L', '@', 'Y', 'M' +// DD POLL,ICMD,JCMD,CCMD,DCMD,LCMD,ATCOMMAND,YCMD,HOSTMON + +NOTDATA: + + if (TNC->DEDTXBUFFER[0] == 1) + { + // recovering + +// if (!TNC->Recovering) +// { +// sprintf(msg, "Port %d DED Recovery started\n", TNC->ComPort); +// OutputDebugString(msg); +// TNC->Recovering = TRUE; +// } + } + else + { + // Not recovery + +// if (TNC->Recovering) +// { +// sprintf(msg, "Port %d DED Recovery completed\n", TNC->ComPort); +// OutputDebugString(msg); +// TNC->Recovering = FALSE; +// } + + } + +// sprintf(msg,"DED CMD: Port %d CMD %c MSGCHANNEL %d\n", TNC->ComPort, TNC->TONODEBUFFER[0], MSGCHANNEL); +// OutputDebugString(msg); + + if (_memicmp(TNC->DEDTXBUFFER, "QRES", 4 == 0)) + return SendHostOK(TNC); + + switch (toupper(TNC->DEDTXBUFFER[0])) + { + case 1: // Recovery + + PUTCHARx(TNC, TNC->MSGCHANNEL); + + for (i=0; i < LBADCMDREPLY; i++) + { + PUTCHARx(TNC, BADCMDREPLY[i]); + } + + return TRUE; + + case 'G': + + PROCESSPOLL(TNC, Channel); + return TRUE; + + case 'I': + goto ICMD; + + case 'J': + + TNC->MODE = TNC->DEDTXBUFFER[5] & 1; + + if (TNC->MODE) + EnableDEDAppl(TNC); + else + DisableAppl(TNC); + + return SendHostOK(TNC); + + case 'C': + goto CCMD; + + case 'D': + + // DISCONNECT REQUEST + + Disconnect(Channel->BPQStream); + return SendHostOK(TNC); + + case 'L': + + // Poll + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 1); + + // GET STATE AND QUEUED BUFFERS + + if (TNC->MSGCHANNEL) + { + // Data Channel + + LocalSessionState(Channel->BPQStream, &State, &Change, FALSE); + + if (State == 0) + Work = '0'; + else + Work = '4'; // AX.25 STATE + + PUTCHARx(TNC, Change + '0'); // Status Messages + + PUTCHARx(TNC, ' '); + + // GET OTHER QUEUE COUNTS + + Count = RXCount(Channel->BPQStream); + + sprintf(WorkString, "%d", Count); // message count + + PUTSTRING(TNC, WorkString); + PUTCHARx(TNC, ' '); + + // NOT SENT IS NUMBER ON OUR QUEUE, NOT ACKED NUMBER FROM SWITCH + + // SEE HOW MANY BUFFERS ATTACHED TO Q HEADER IN BX + + Count = 0;// C_Q_COUNT(Channel->Chan_TXQ); + + sprintf(WorkString, "%d", Count); // message count + PUTSTRING(TNC, WorkString); + PUTCHARx(TNC, ' '); + + if (Count > 8) + Work = '8'; // Busy + + Count = CountFramesQueuedOnSession(L4); + + sprintf(WorkString, "%d", Count); // message count + PUTSTRING(TNC, WorkString); + PUTCHARx(TNC, ' '); + + } + else + { + //SEE IF MONITORED FRAMES AVAILABLE + + TNC->LastDEDPollTime = time(NULL); + + if (MONCount(TNC->Channels[0]->BPQStream)) + Work = 0x31; + else + Work = 0x30; + } + + PUTCHARx(TNC, '0'); + PUTCHARx(TNC, ' '); + PUTCHARx(TNC, Work); + PUTCHARx(TNC, 0); + + return TRUE; + + case '@': + goto ATCOMMAND; + + case 'Y': + goto YCMD; + + case 'E': + goto ECMD; + + case 'M': + + // Support BPQ Extensions IUSC followed by optional port list + + TNC->DEDTXBUFFER[TNC->MSGLENGTH] = 0; + + if (*mcmdptr == ' ') + mcmdptr++; + + if (mcmdptr[0] == 'N') + TNC->TRACEFLAG = 0; + else + { + char * ptr, * ptr2; + int port; + uint64_t mask = 0; + + + ptr = strlop(mcmdptr, ' '); + + _strupr(mcmdptr); + + if (strchr(mcmdptr, 'U')) + TNC->MUIONLY = 1; + + + // Ptocess Mask + + while (ptr && ptr[0]) + { + ptr2 = strlop(ptr, ','); + port = atoi(ptr); + + if (port) + { + mask |= ((uint64_t)1 << (port - 1)); + } + ptr = ptr2; + + } + + if (mask) + TNC->MMASK = mask; + + TNC->TRACEFLAG = 0x80; + } + + SetAppl(TNC->Channels[0]->BPQStream, 2 | TNC->TRACEFLAG, TNC->APPLICATION); + return SendHostOK(TNC); + + case 'K': + case 'O': + return SendHostOK(TNC); + + case 'V': // Vesrion + + PUTCHARx(TNC, TNC->MSGCHANNEL); + PUTCHARx(TNC, 1); + PUTSTRING(TNC, "DSPTNC Firmware V.1.3a, (C) 2005-2010 SCS GmbH & Co."); + PUTCHARx(TNC, 0); + + return TRUE; + + default: + return SendHostOK(TNC); + +ATCOMMAND: + + if (TNC->DEDTXBUFFER[1] == 'B') + goto BUFFSTAT; + + if (TNC->DEDTXBUFFER[1] == 'M') + goto BUFFMIN; + +// Not recognised + + PUTCHARx(TNC, TNC->MSGCHANNEL); + + for (i=0; i < LBADCMDREPLY; i++) + { + PUTCHARx(TNC, BADCMDREPLY[i]); + } + + return TRUE; + + +BUFFMIN: + + Work = MINBUFFCOUNT; + goto BUFFCOMM; + +BUFFSTAT: + + Work = QCOUNT; + +BUFFCOMM: + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 1); + + sprintf(WorkString, "%d", Work); // Buffer count + + PUTSTRING(TNC, WorkString); + + PUTCHARx(TNC, 0); + return TRUE; + +ICMD: + + { + char * Call = &TNC->DEDTXBUFFER[1]; + int len; + char Reply[80]; + char ReplyCall[10]; + + + if (TNC->MSGLENGTH > 2) + { + // Save callsign + + TNC->DEDTXBUFFER[TNC->MSGLENGTH] = 0; + + if (*Call == ' ') + *Call++; // May have leading space + + _strupr(Call); + + memset(Channel->MYCall, ' ', 10); + memcpy(Channel->MYCall, Call, (int)strlen(Call)); + + Debugprintf("DED Host I chan %d call %s", TNC->MSGCHANNEL, Call); + + strcpy(ReplyCall, Call); + +/* + if (TNC->MSGCHANNEL == 0) // if setting zero, copy to all others + { + int i; + + for (i = 1; i <= TNC->HOSTSTREAMS; i++) + { + memcpy(TNC->Channels[i]->MYCall, TNC->Channels[0]->MYCall, 10); + Debugprintf("DED Capy to chan %d call %s", i, Channel->MYCall); + } + } +*/ + + + + + + + } + else + { + memcpy(ReplyCall, Channel->MYCall, 10); + strlop(ReplyCall, ' '); + } + + len = sprintf(Reply, "\x2%s", ReplyCall); + + SENDCMDREPLY(TNC, Reply, len + 1); // include the null + + return TRUE; + } +ECMD: + + return SendHostOK(TNC); + + +CCMD: + +// CONNECT REQUEST + + if (TNC->MSGCHANNEL == 0) + return SendHostOK(TNC); // SETTING UNPROTO ADDR - JUST ACK IT + + *TNC->DEDCURSOR = 0; + + if (TNC->MSGLENGTH > 1) + goto REALCALL; + +// STATUS REQUEST - IF CONNECTED, GET CALL + + DEDSWITCH[3] = 0; + + GetCallsign(Channel->BPQStream, &DEDSWITCH[3]); + + Debugprintf("CCMD %d %d %s", TNC->MSGCHANNEL, TNC->Channels[TNC->MSGCHANNEL]->BPQStream, &DEDSWITCH[3]); + + if (DEDSWITCH[3] == 0) + SENDCMDREPLY(TNC, NOTCONMSG, LNOTCON); + else + SENDCMDREPLY(TNC, DEDSWITCH, LSWITCH); + + return TRUE; + +REALCALL: + +// If to Switch, just connect, else pass c command to Node + + Debugprintf("CCMD %d %s", TNC->MSGCHANNEL, TXBUFFERPTR); + + SaveAuthProg = AuthorisedProgram; + AuthorisedProgram =1; + Connect(Channel->BPQStream); + AuthorisedProgram = SaveAuthProg; + +// CONNECT WILL BE REPORTED VIA NORMAL STATUS CHANGE + + if (Channel->MYCall[0] > ' ') + ChangeSessionCallsign(Channel->BPQStream, EncodeCall(Channel->MYCall)); + else + ChangeSessionCallsign(Channel->BPQStream, EncodeCall(TNC->Channels[0]->MYCall)); + + _strupr(TXBUFFERPTR); + + if (strstr(TXBUFFERPTR, "SWITCH") == 0) // Not switch + { + char * Call, * Arg1; + char * Context; + char seps[] = " ,\r"; + + Call = strtok_s(TXBUFFERPTR + 1, seps, &Context); + Arg1 = strtok_s(NULL, seps, &Context); + + if (Arg1) + { + // Have a digi string + + // First digi is used as a port number. Any others are rwal digis or WINMOR/PACTOR + + if (Context[0]) + TNC->MSGLEN = sprintf(TXBUFFERPTR + 100, "C %s %s v %s\r", Arg1, Call, Context); + else + TNC->MSGLEN = sprintf(TXBUFFERPTR + 100, "C %s %s\r", Call, Arg1); + } + else + TNC->MSGLEN = sprintf(TXBUFFERPTR + 100, "C %s\r", Call); + + strcpy(TXBUFFERPTR, TXBUFFERPTR + 100); + + SendMsg(Channel->BPQStream, TXBUFFERPTR, TNC->MSGLEN); + +// READCHANGE(Channel->BPQStream); // Suppress Connected to Switch + + return SendHostOK(TNC); + } + } + + return SendHostOK(TNC); + + + +HOSTDATAPACKET: + +// } +// { +// UCHAR msg[100]; + +// sprintf(msg,"Host Data Packet: Port %d\n", TNC->ComPort); +// OutputDebugString(msg); +// } +// + + +// IF WE ALREADY HAVE DATA QUEUED, ADD THIS IT QUEUE + +// if (Channel->Chan_TXQ) +// { + +// // COPY MESSAGE TO A CHAIN OF BUFFERS + +// if (QCOUNT < 10) +// goto CANTSEND; // NO SPACE - RETURN ERROR (?) + +//QUEUEFRAME: + +// C_Q_ADD(Channel->Chan_TXQ, COPYMSGTOBUFFERS()); // RETURNS EDI = FIRST (OR ONLY) FRAGMENT + +// return SendHostOK(TNC); + + // MAKE SURE NODE ISNT BUSY + + if (TNC->MSGCHANNEL == 0) // UNPROTO Channel + goto SendUnproto; + + Count = CountFramesQueuedOnSession(L4); + +// if (Count > 4 || QCOUNT < 40) +// goto QUEUEFRAME; + + // OK TO PASS TO NODE + + SENDENFORCINGPACLEN(Channel, TNC->DEDTXBUFFER, TNC->MSGLENGTH); + return SendHostOK(TNC); + +SendUnproto: + + SendMsg(0, TXBUFFERPTR, TNC->MSGLENGTH); + return SendHostOK(TNC); + + +YCMD: + + *TNC->DEDCURSOR = 0; + + Work = atoi(&TXBUFFERPTR[1]); + + if (Work == 0) + Work = 1; // Mustn't release last stream + + if (Work >= 0 && Work <= MAXSTREAMS) + { + int Stream; + + if (Work < TNC->HOSTSTREAMS) + { + // Need to get rid of some streams + + for (i = Work + 1; i <= TNC->HOSTSTREAMS; i++) + { + Stream = TNC->Channels[i]->BPQStream; + + if (Stream) + { + Disconnect(Stream); + READCHANGE(Stream); // Prevent Initial *** disconnected + + DeallocateStream(Stream); + + Debugprintf("DED YCMD Release Stream %d", Stream); + } + + free(TNC->Channels[i]); + TNC->Channels[i] = 0; + } + } + else + { + for (i = TNC->HOSTSTREAMS+1; i <= Work; i++) + { + AllocateDEDChannel(TNC, i); // Also used by Y command handler + } + } + TNC->HOSTSTREAMS = Work; + } + + return SendHostOK(TNC); +} + +int SendHostOK(struct TNCDATA * TNC) +{ + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 0); // NOTHING DOING + + return TRUE; +} + +int PROCESSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) +{ + // DED Mode - ASK SWITCH FOR STATUS CHANGE OR ANY RECEIVED DATA + + if (TNC->MSGLENGTH == 1) // General Poll + { + if (STATUSPOLL(TNC, Channel)) + return TRUE; // Status was reported + + if (DATAPOLL(TNC, Channel)) + return TRUE; // Data Sent + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 0); // NOTHING DOING + + return TRUE; + } + + // HE'S BEING AWKWARD, AND USING SPECIFIC DATA/STATUS POLL + + if (TNC->TONODEBUFFER[1] == '0') // Data only + { + if (DATAPOLL(TNC, Channel)) + return TRUE; // Data Sent + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 0); // NOTHING DOING + + return TRUE; + } + + // Must be Status only + + if (STATUSPOLL(TNC, Channel)) + return TRUE; // Status was reported + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 0); // NOTHING DOING + + return TRUE; +} + +int ConvertToDEDMonFormat(struct TNCDATA * TNC, char * Decoded, int Len, MESSAGE * Rawdata) +{ + // convert tnc2 format monitor to ded format + + unsigned char * MONCURSOR=0; + unsigned char MONHEADER[256]; + char * From, * To, * via, * ctl, *Context, *ptr, *iptr; + int pid, NR, NS, MonLen; + char rest[20]; + +/* + + From DEDHOST Documentation + + +Codes of 4 and 5 both signify a monitor header. This is a null-terminated +format message containing the + + fm {call} to {call} via {digipeaters} ctl {name} pid {hex} + +string that forms a monitor header. The monitor header is also identical to +the monitor header displayed in user mode. If the code was 4, the monitored +frame contained no information field, so the monitor header is all you get. If +you monitor KB6C responding to a connect request from me and then poll channel +0, you'll get: + + 0004666D204B42364320746F204B42354D552063746C2055612070494420463000 + ! ! f m K B 6 C t o K B 5 M U c t l U A p i d F 0 ! + ! ! ! + ! +---- Code = 4 (Monitor, no info) Null termination ----+ + +------- Channel = 0 (Monitor info is always on channel 0) + + If the code was 5, the monitored frame did contain an information field. In +this case, another G command to channel 0 will return the monitored information +with a code of 6. Since data transmissions must be transparent, the monitored +information is passed as a byte-count format transmission. That is, it is +preceded by a count byte (one less than the number of bytes in the field). No +null terminator is used in this case. Since codes 4, 5, and 6 pertain to +monitored information, they will be seen only on channel 0. If you hear KB6C +say "Hi" to NK6K, and then poll channel 0, you'll get: + + 0005666D204B42364320746F204E4B364B2063746C204930302070494420463000 + ! ! f m K B 6 C t o N K 6 K c t l I 0 0 p i d F 0 ! + ! ! ! ! ! + ! ! or whatever ----+-+ ! + ! ! ! + ! +---- Code = 5 (Monitor, info follows) Null termination ----+ + +------ Channel = 0 (Monitor info is always on channel 0) + +and then the very next poll to channel 0 will get: + + 00 06 02 48 69 0D + ! ! ! H i CR + ! ! ! ! + ! ! ! +---- (this is a data byte) + ! ! +---- Count = 2 (three bytes of data) + ! +------- Code = 6 (monitored information) + +---------- Channel = 0 (Monitor info is always on channel 0) + + +*/ + + Decoded[Len] = 0; + + iptr = strchr(&Decoded[10], ':'); // Info if present + + MONHEADER[0] = 4; // NO DATA FOLLOWS + MONCURSOR = &MONHEADER[1]; + + if (strstr(Decoded, "NET/ROM") || strstr(Decoded, "NODES br") || strstr(Decoded, "INP3 RIF")) + return 0; //pid = 0xcf; + else + pid = 0xf0; + + From = strtok_s(&Decoded[10], ">", &Context); + To = strtok_s(NULL, " ", &Context); + + via = strlop(To, ','); + + Context = strchr(Context, '<'); + if (Context == 0) + return 0; + + ctl = strtok_s(NULL, ">", &Context); + + if (via) + MONCURSOR += sprintf(MONCURSOR, "fm %s to %s via %s ctl ", From, To, via); + else + MONCURSOR += sprintf(MONCURSOR, "fm %s to %s ctl ", From, To); + + rest[0] = 0; + + switch (ctl[1]) + { + case 'R': + + NR = ctl[strlen(ctl)-1] - 48; + strlop(ctl, ' '); + sprintf(rest, "%s%d", &ctl[1], NR); + break; + + case 'I': + + ptr = strchr(ctl, 'S'); + if (ptr) NS = ptr[1] - 48; + ptr = strchr(ctl, 'R'); + if (ptr) NR = ptr[1] - 48; + sprintf(rest, "I%d%d pid %X", NS, NR, pid); + + if (pid == 0xcf) + { + // NETROM - pass the raw data + + MonLen = Rawdata->LENGTH - (MSGHDDRLEN + 16); // Data portion of frame + memcpy(&TNC->MONBUFFER[2], &Rawdata->L2DATA[0], MonLen); + + MONHEADER[0] = 5; // Data to follow + TNC->MONFLAG = 1; // Data to follow + TNC->MONBUFFER[0] = 6; + TNC->MONLENGTH = MonLen + 2; + TNC->MONBUFFER[1] = (MonLen - 1); + } + else + { + if (iptr) + { + iptr += 2; // Skip colon and cr + MonLen = Len - (int)(iptr - Decoded); + if (MonLen > 256) + MonLen = 256; + + memcpy(&TNC->MONBUFFER[2], iptr, MonLen); + + + if (MonLen == 0) // No data + { + MONHEADER[0] = 4; // No Data to follow + TNC->MONFLAG = 0; // No Data to follow + } + else + { + MONHEADER[0] = 5; // Data to follow + TNC->MONFLAG = 1; // Data to follow + TNC->MONBUFFER[0] = 6; + TNC->MONLENGTH = MonLen + 2; + TNC->MONBUFFER[1] = (MonLen - 1); + } + } + } + break; + + case 'C': + + strcpy(rest, "SABM"); + break; + + case 'D': + + if (ctl[1] == 'M') + strcpy(rest, "DM"); + else + strcpy(rest, "DISC"); + + break; + + case 'U': + + if (ctl[2] == 'A') + strcpy(rest, "UA"); + else + { + // UI + + size_t MonLen;; + + MONHEADER[0] = 5; // Data to follow + sprintf(rest, "UI pid %X", pid); + TNC->MONFLAG = 1; // Data to follow + TNC->MONBUFFER[0] = 6; + + if (pid ==0xcf) + { + // NETROM - pass th raw data + + MonLen = Rawdata->LENGTH - (MSGHDDRLEN + 16); // Data portion of frame + memcpy(&TNC->MONBUFFER[2], &Rawdata->L2DATA[0], MonLen); + } + else + { + ptr = strchr(Context, ':'); + + if (ptr == 0) + { + TNC->MONFLAG = 0; + return 0; + } + + ptr += 2; // Skip colon and cr + MonLen = Len - (ptr - Decoded); + memcpy(&TNC->MONBUFFER[2], ptr, MonLen); + } + + if (MonLen == 0) // No data + { + MONHEADER[0] = 4; // No Data to follow + TNC->MONFLAG = 0; // No Data to follow + } + else + { + TNC->MONLENGTH = (int)MonLen + 2; + TNC->MONBUFFER[1] = (int)(MonLen - 1); + } + break; + } + + default: + rest[0] = 0; + } + + MONCURSOR += sprintf(MONCURSOR, "%s", rest); + + if (MONCURSOR == &MONHEADER[1]) + return 0; // NOTHING DOING + + *MONCURSOR++ = 0; // NULL TERMINATOR + + SENDCMDREPLY(TNC, MONHEADER, (int)(MONCURSOR - &MONHEADER[0])); + return 1; +} + +// GET THE CONTROL BYTE, TO SEE IF THIS FRAME IS TO BE DISPLAYED +/* + +static char CTL_MSG[]=" ctl "; +static char VIA_MSG[]=" via "; +static char PID_MSG[]=" pid "; +static char SABM_MSG[]="SABM"; +static char DISC_MSG[]="DISC"; +static char UA_MSG[]="UA"; + +static char DM_MSG []="DM"; +static char RR_MSG []="RR"; +static char RNR_MSG[]="RNR"; +static char I_MSG[]="I pid "; +static char UI_MSG[]="UI pid "; +static char FRMR_MSG[]="FRMR"; +static char REJ_MSG[]="REJ"; + + PUSH EDI + MOV ECX,8 ; MAX DIGIS +CTRLLOOP: + TEST BYTE PTR (MSGCONTROL-1)[EDI],1 + JNZ CTRLFOUND + + ADD EDI,7 + LOOP CTRLLOOP +; +; INVALID FRAME +; + POP EDI + RET + +CTRLFOUND: + MOV AL,MSGCONTROL[EDI] + + and AL,NOT PFBIT ; Remove P/F bit + mov FRAME_TYPE,AL + + + POP EDI +; + TEST AL,1 ; I FRAME + JZ IFRAME + + CMP AL,3 ; UI + JE OKTOTRACE ; ALWAYS DO UI + + CMP AL,FRMR + JE OKTOTRACE ; ALWAYS DO FRMR +; +; USEQ/CONTROL - TRACE IF MCOM ON +; + CMP MCOM,0 + JNE OKTOTRACE + + RET + +;-----------------------------------------------------------------------------; +; Check for MALL ; +;-----------------------------------------------------------------------------; + +IFRAME: + cmp MALL,0 + jne OKTOTRACE + + ret + +OKTOTRACE: +; +;-----------------------------------------------------------------------------; +; Get the port number of the received frame ; +;-----------------------------------------------------------------------------; +; +; CHECK FOR PORT SELECTIVE MONITORING +; + MOV CL,MSGPORT[EDI] + mov PORT_NO,CL + + DEC CL + MOV EAX,1 + SHL EAX,CL ; SHIFT BIT UP + + TEST MMASK,EAX + JNZ TRACEOK1 + + RET + +TRACEOK1: + + MOV FRMRFLAG,0 + push EDI + mov AH,MSGDEST+6[EDI] + mov AL,MSGORIGIN+6[EDI] + +; +; Display Origin Callsign ; +; + +; 0004666D204B42364320746F204B42354D552063746C2055612070494420463000 +; ! ! f m K B 6 C t o K B 5 M U c t l U A p i d F 0 ! +; ! ! ! +; ! +---- Code = 4 (Monitor, no info) Null termination ----+ + ; +------- Channel = 0 (Monitor info is always on channel 0) + + mov ESI,OFFSET FM_MSG + call NORMSTR + + lea ESI,MSGORIGIN[EDI] + call CONVFROMAX25 + mov ESI,OFFSET NORMCALL + call DISPADDR + + pop EDI + push EDI + + mov ESI,OFFSET TO_MSG + call NORMSTR +; +; Display Destination Callsign ; +; + lea ESI,MSGDEST[EDI] + call CONVFROMAX25 + mov ESI,OFFSET NORMCALL + call DISPADDR + + pop EDI + push EDI + + mov AX,MMSGLENGTH[EDI] + mov FRAME_LENGTH,AX + mov ECX,8 ; Max number of digi-peaters +; +; Display any Digi-Peaters ; +; + test MSGORIGIN+6[EDI],1 + jnz NO_MORE_DIGIS + + mov ESI,OFFSET VIA_MSG + call NORMSTR + jmp short skipspace + +NEXT_DIGI: + test MSGORIGIN+6[EDI],1 + jnz NO_MORE_DIGIS + + mov AL,' ' + call MONPUTCHAR +skipspace: + add EDI,7 + sub FRAME_LENGTH,7 ; Reduce length + + push EDI + push ECX + lea ESI,MSGORIGIN[EDI] + call CONVFROMAX25 ; Convert to call + + push EAX ; Last byte is in AH + + mov ESI,OFFSET NORMCALL + call DISPADDR + + pop EAX + + test AH,80H + jz NOT_REPEATED + + mov AL,'*' + call MONPUTCHAR + +NOT_REPEATED: + pop ECX + pop EDI + loop NEXT_DIGI + +NO_MORE_DIGIS: + +;----------------------------------------------------------------------------; +; Display ctl ; +;----------------------------------------------------------------------------; + + mov ESI,OFFSET CTL_MSG + call NORMSTR + +;-----------------------------------------------------------------------------; +; Start displaying the frame information ; +;-----------------------------------------------------------------------------; + + + mov INFO_FLAG,0 + + mov AL,FRAME_TYPE + + test AL,1 + jne NOT_I_FRAME + +;-----------------------------------------------------------------------------; +; Information frame ; +;-----------------------------------------------------------------------------; + + mov AL,'I' + call MONPUTCHAR + mov INFO_FLAG,1 + + mov ESI,OFFSET I_MSG + call NORMSTR + + lea ESI,MSGPID[EDI] + lodsb + + call BYTE_TO_HEX + + + jmp END_OF_TYPE + +NOT_I_FRAME: + +;-----------------------------------------------------------------------------; +; Un-numbered Information Frame ; +;-----------------------------------------------------------------------------; + + cmp AL,UI + jne NOT_UI_FRAME + + mov ESI,OFFSET UI_MSG + call NORMSTR + + lea ESI,MSGPID[EDI] + lodsb + + call BYTE_TO_HEX + + mov INFO_FLAG,1 + jmp END_OF_TYPE + +NOT_UI_FRAME: + test AL,10B + jne NOT_R_FRAME + +;-----------------------------------------------------------------------------; +; Process supervisory frames ; +;-----------------------------------------------------------------------------; + + + and AL,0FH ; Mask the interesting bits + cmp AL,RR + jne NOT_RR_FRAME + + mov ESI,OFFSET RR_MSG + call NORMSTR + jmp END_OF_TYPE + +NOT_RR_FRAME: + cmp AL,RNR + jne NOT_RNR_FRAME + + mov ESI,OFFSET RNR_MSG + call NORMSTR + jmp END_OF_TYPE + +NOT_RNR_FRAME: + cmp AL,REJ + jne NOT_REJ_FRAME + + mov ESI,OFFSET REJ_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_REJ_FRAME: + mov AL,'?' ; Print "?" + call MONPUTCHAR + jmp SHORT END_OF_TYPE + +; +; Process all other frame types ; +; + +NOT_R_FRAME: + cmp AL,UA + jne NOT_UA_FRAME + + mov ESI,OFFSET UA_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_UA_FRAME: + cmp AL,DM + jne NOT_DM_FRAME + + mov ESI,OFFSET DM_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_DM_FRAME: + cmp AL,SABM + jne NOT_SABM_FRAME + + mov ESI,OFFSET SABM_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_SABM_FRAME: + cmp AL,DISC + jne NOT_DISC_FRAME + + mov ESI,OFFSET DISC_MSG + call NORMSTR + jmp SHORT END_OF_TYPE + +NOT_DISC_FRAME: + cmp AL,FRMR + jne NOT_FRMR_FRAME + + mov ESI,OFFSET FRMR_MSG + call NORMSTR + MOV FRMRFLAG,1 + jmp SHORT END_OF_TYPE + +NOT_FRMR_FRAME: + mov AL,'?' + call MONPUTCHAR + +END_OF_TYPE: + + CMP FRMRFLAG,0 + JE NOTFRMR +; +; DISPLAY FRMR BYTES +; + lea ESI,MSGPID[EDI] + MOV CX,3 ; TESTING +FRMRLOOP: + lodsb + CALL BYTE_TO_HEX + + LOOP FRMRLOOP + + JMP NO_INFO + +NOTFRMR: + + MOVZX ECX,FRAME_LENGTH + + + cmp INFO_FLAG,1 ; Is it an information packet ? + jne NO_INFO + + + XOR AL,AL ; IN CASE EMPTY + + sub ECX,23 + CMP ecx,0 + je NO_INFO ; EMPTY I FRAME + +; +; PUT DATA IN MONBUFFER, LENGTH IN MONLENGTH +; + + pushad +} + TNC->MONFLAG = 1; + + _asm{ + + popad + + MOV MONHEADER,5 ; DATA FOLLOWS + + cmp ECX,257 + jb LENGTH_OK +; + mov ECX,256 +; +LENGTH_OK: +; + mov MonDataLen, ECX + + pushad + + } + + TNC->MONBUFFER[1] = MonDataLen & 0xff; + TNC->MONBUFFER[1]--; + + + TNC->MONLENGTH = MonDataLen+2; + + ptr1=&TNC->MONBUFFER[2]; + + _asm{ + + popad + + MOV EDI,ptr1 + +MONCOPY: + LODSB + CMP AL,7 ; REMOVE BELL + JNE MONC00 + + MOV AL,20H +MONC00: + STOSB + + LOOP MONCOPY + + POP EDI + RET + +NO_INFO: +; +; ADD CR UNLESS DATA ALREADY HAS ONE +; + CMP AL,CR + JE NOTANOTHER + + mov AL,CR + call MONPUTCHAR + +NOTANOTHER: +; + pop EDI + ret + +;----------------------------------------------------------------------------; +; Display ASCIIZ strings ; +;----------------------------------------------------------------------------; + +NORMSTR: + lodsb + cmp AL,0 ; End of String ? + je NORMSTR_RET ; Yes + call MONPUTCHAR + jmp SHORT NORMSTR + +NORMSTR_RET: + ret + +;-----------------------------------------------------------------------------; +; Display Callsign pointed to by SI ; +;-----------------------------------------------------------------------------; + +DISPADDR: + jcxz DISPADDR_RET + + lodsb + call MONPUTCHAR + + loop DISPADDR + +DISPADDR_RET: + ret + + +;-----------------------------------------------------------------------------; +; Convert byte in AL to nn format ; +;-----------------------------------------------------------------------------; + +DISPLAY_BYTE_2: + cmp AL,100 + jb TENS_2 + + sub AL,100 + jmp SHORT DISPLAY_BYTE_2 + +TENS_2: + mov AH,0 + +TENS_LOOP_2: + cmp AL,10 + jb TENS_LOOP_END_2 + + sub AL,10 + inc AH + jmp SHORT TENS_LOOP_2 + +TENS_LOOP_END_2: + push EAX + mov AL,AH + add AL,30H + call MONPUTCHAR + pop EAX + + add AL,30H + call MONPUTCHAR + + ret + +;-----------------------------------------------------------------------------; +; Convert byte in AL to Hex display ; +;-----------------------------------------------------------------------------; + +BYTE_TO_HEX: + push EAX + shr AL,1 + shr AL,1 + shr AL,1 + shr AL,1 + call NIBBLE_TO_HEX + pop EAX + call NIBBLE_TO_HEX + ret + +NIBBLE_TO_HEX: + and AL,0FH + cmp AL,10 + + jb LESS_THAN_10 + add AL,7 + +LESS_THAN_10: + add AL,30H + call MONPUTCHAR + ret + + + +CONVFROMAX25: +; +; CONVERT AX25 FORMAT CALL IN [SI] TO NORMAL FORMAT IN NORMCALL +; RETURNS LENGTH IN CX AND NZ IF LAST ADDRESS BIT IS SET +; + PUSH ESI ; SAVE + MOV EDI,OFFSET NORMCALL + MOV ECX,10 ; MAX ALPHANUMERICS + MOV AL,20H + REP STOSB ; CLEAR IN CASE SHORT CALL + MOV EDI,OFFSET NORMCALL + MOV CL,6 +CONVAX50: + LODSB + CMP AL,40H + JE CONVAX60 ; END IF CALL - DO SSID + + SHR AL,1 + STOSB + LOOP CONVAX50 +CONVAX60: + POP ESI + ADD ESI,6 ; TO SSID + LODSB + MOV AH,AL ; SAVE FOR LAST BIT TEST + SHR AL,1 + AND AL,0FH + JZ CONVAX90 ; NO SSID - FINISHED +; + MOV BYTE PTR [EDI],'-' + INC EDI + CMP AL,10 + JB CONVAX70 + SUB AL,10 + MOV BYTE PTR [EDI],'1' + INC EDI +CONVAX70: + ADD AL,30H ; CONVERT TO DIGIT + STOSB +CONVAX90: + MOV ECX,EDI + SUB ECX,OFFSET NORMCALL + MOV NORMLEN,ECX ; SIGNIFICANT LENGTH + + TEST AH,1 ; LAST BIT SET? + RET + + +PUTCHAR: + + pushad + push eax + push TNC + call PUTCHARx + pop eax + pop eax + popad + ret +} +} +*/ + + + +VOID SENDENFORCINGPACLEN(struct StreamInfo * Channel, char * Msg, int Len) +{ + int Paclen = 0; + + if (Len == 0) + return; + + if (BPQHOSTVECTOR[Channel->BPQStream-1].HOSTSESSION) + Paclen = BPQHOSTVECTOR[Channel->BPQStream-1].HOSTSESSION->SESSPACLEN; + + if (Paclen == 0) + goto nochange; // paclen not set + +fragloop: + + if (Len <= Paclen) + goto nochange; // msglen <= paclen + +// need to fragment + + SendMsg(Channel->BPQStream, Msg, Paclen); + + Msg += Paclen; + + Len -= Paclen; + + if (Len) + goto fragloop; + + return; + +nochange: + + SendMsg(Channel->BPQStream, Msg, Len); + return; +} + +int DOCOMMAND(struct TNCDATA * TNC) +{ + char Errbuff[500]; + int i; + +// PROCESS NORMAL MODE COMMAND + + Debugprintf(Errbuff, "BPQHOST Port %d Normal Mode CMD %s\n",TNC->ComPort, TNC->TONODEBUFFER); + + +// IF ECHO ENABLED, ECHO IT + + if (TNC->ECHOFLAG) + { + UCHAR * ptr = TNC->TONODEBUFFER; + UCHAR c; + + do + { + c = *(ptr++); + + if (c == 0x1b) c = ':'; + + PUTCHARx(TNC, c); + + } while (c != 13); + } + + if (TNC->TONODEBUFFER[0] != 0x1b) + goto NOTCOMMAND; // DATA IN NORMAL MODE - IGNORE + + switch (toupper(TNC->TONODEBUFFER[1])) + { + case 'J': + + if (TNC->TONODEBUFFER[6] == 0x0d) + TNC->MODE = 0; + else + TNC->MODE = TNC->TONODEBUFFER[6] & 1; + + if (TNC->MODE) + { + // send host mode ack + +// PUTCHARx(conn, 0); +// PUTCHARx(conn, 0); + + EnableDEDAppl(TNC); + } + else + { + DisableAppl(TNC); + return 0; + } + + break; + + case 'E': + + TNC->ECHOFLAG = TNC->TONODEBUFFER[2] & 1; + break; + + case 'I': + { + // Save call + + char * Call = &TNC->TONODEBUFFER[2]; + + *(TNC->CURSOR - 2) = 0; + + for (i = 0; i <= TNC->HOSTSTREAMS; i++) + { + strcpy(TNC->Channels[i]->MYCall, Call); + } + + break;; + } + case 'P': + +// PARAMS COMMAND - RETURN FIXED STRING + + PARAMPORT = TNC->TONODEBUFFER[2]; + + for (i=0; i < LPARAMREPLY; i++) + { + PUTCHARx(TNC, PARAMREPLY[i]); + } + + break; + + case 'S': + case 'D': + + // Return Channel Not Connected + + PUTSTRING(TNC, "* CHANNEL NOT CONNECTED *\r"); + + default: + + break; + + } + +// PUTCHARx(conn, 'c'); +// PUTCHARx(conn, 'm'); +// PUTCHARx(conn, 'd'); +// PUTCHARx(conn, ':'); +// PUTCHARx(conn, 13); + +NOTCOMMAND: + + TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER; + + return 0; + +} + +VOID SENDCMDREPLY(struct TNCDATA * TNC, char * Msg, int Len) +{ + int n; + + if (Len == 0) + return; + + PUTCHARx(TNC, TNC->MSGCHANNEL); + + for (n = 0; n < Len; n++) + { + PUTCHARx(TNC, Msg[n]); + } +} + +int STATUSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) +{ + // Status Poll + + int State, Change, i; + char WorkString[256]; + char ConMsg[64]; + + if (TNC->MSGCHANNEL == 0) // Monitor Chan + return 0; + + LocalSessionState(Channel->BPQStream, &State, &Change, TRUE); + + if (Change == 0) + return 0; + + // PORT HAS CONNECTED OR DISCONNECTED - SEND STATUS CHANGE TO PC + + if (State == 0) + { + // DISCONNECTED + + i = sprintf(ConMsg, "\x3(%d) DISCONNECTED fm 0:SWITCH\r", TNC->MSGCHANNEL); + i++; + } + else + { + // GET CALLSIGN + + GetCallsign(Channel->BPQStream, WorkString); + strlop(WorkString, ' '); + i = sprintf(ConMsg, "\x3(%d) CONNECTED to %s\r", TNC->MSGCHANNEL, WorkString); + i++; + } + + SENDCMDREPLY(TNC, ConMsg, i); + return 1; +} + +int DATAPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) +{ + unsigned char NODEBUFFER[300]; // MESSAGE FROM NODE + int Len, Count, i; + time_t stamp; + char * ptr1; + + if (TNC->MSGCHANNEL == 0) + { + // POLL FOR MONITOR DATA + + if (TNC->MONFLAG == 0) + goto NOMONITOR; + + // HAVE ALREADY GOT DATA PART OF MON FRAME OT SEND + + TNC->MONFLAG = 0; + + ptr1 = (UCHAR *)&TNC->MONBUFFER; + + if (TNC->MONLENGTH) + { + SENDCMDREPLY(TNC, ptr1, TNC->MONLENGTH); + return TRUE; + } + + Debugprintf("BPQHOST Mondata Flag Set with no data"); + +NOMONITOR: + + // SEE IF ANYTHING TO MONITOR + + stamp = GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); + + while (Len) + { + // Use Normal Decode, then reformat to DED standard + + uint64_t SaveMMASK = MMASK; + BOOL SaveMTX = MTX; + BOOL SaveMCOM = MCOM; + BOOL SaveMUI = MUIONLY; + unsigned char Decoded[1000]; + + IntSetTraceOptionsEx(TNC->MMASK, TNC->MTX, TNC->MCOM, TNC->MUIONLY); + Len = IntDecodeFrame(&MONITORDATA, Decoded, stamp, TNC->MMASK, FALSE, FALSE); + IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); + + if (Len) + { + if (ConvertToDEDMonFormat(TNC, Decoded, Len, &MONITORDATA)) + return 1; + + stamp = GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); + } + + } + return 0; + } + + // Look for session data + + GetMsg(Channel->BPQStream, NODEBUFFER, &Len, &Count); + + if (Len == 0) + return 0; + + if (Len > 256) + { + Debugprintf("BPQHOST Corrupt Length = %d", Len); + return 0; + } + + // SEND DATA + + // If a failure, set a close timer (for Airmail, etc) + + NODEBUFFER[Len] = 0; // For strstr + + if (strstr(NODEBUFFER, "} Downlink connect needs port number") || + strstr(NODEBUFFER, "} Error - TNC Not Ready") || + strstr(NODEBUFFER, "} Failure with ") || + strstr(NODEBUFFER, "} Sorry, ")) + Channel->CloseTimer = CloseDelay * 10; + else + Channel->CloseTimer = 0; // Cancel Timer + + PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL + PUTCHARx(TNC, 7); + PUTCHARx(TNC, Len - 1); + + for (i = 0; i < Len; i++) + { + PUTCHARx(TNC, NODEBUFFER[i]); + } + + return 1; // HAVE SEND SOMETHING +} + + + + + + + + + +// Kantronics Host Mode Stuff + +// Kantronics Host Mode Stuff + +#define FEND 0xC0 // KISS CONTROL CODES +#define FESC 0xDB +#define TFEND 0xDC +#define TFESC 0xDD + + +static VOID ProcessKHOSTPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len); +VOID ProcessKNormCommand(struct TNCDATA * conn, UCHAR * rxbuffer); +VOID SendKISSData(struct TNCDATA * conn, UCHAR * txbuffer, int Len); +static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len); +static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); +static int DoReceivedData(struct TNCDATA * conn, struct StreamInfo * channel); + + + +VOID ProcessPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len) +{ + UCHAR * FendPtr; + size_t NewLen; + + if (!conn->MODE) + { + // In Terminal Mode - Pass to Term Mode Handler + + ProcessKPacket(conn, rxbuffer, Len); + return; + } + + // Split into KISS Packets. By far the most likely is a single KISS frame + // so treat as special case + + if (!(rxbuffer[0] == FEND)) + { + // Getting Non Host Data in Host Mode - Appl will have to sort the mess + // Discard any data + + conn->RXBPtr = 0; + return; + } + + conn->RXBPtr = 0; // Assume we will use all data in buffer - will reset if part packet received + + FendPtr = memchr(&rxbuffer[1], FEND, Len-1); + + if (FendPtr == &rxbuffer[Len-1]) + { + ProcessKHOSTPacket(conn, &rxbuffer[1], Len - 2); + return; + } + + if (FendPtr == NULL) + { + // We have a partial Packet - Save it + + conn->RXBPtr = Len; + memcpy(&conn->TOUSERBUFFER[0], rxbuffer, Len); + return; + } + + // Process the first Packet in the buffer + + NewLen = FendPtr - rxbuffer -1; + ProcessKHOSTPacket(conn, &rxbuffer[1], (int)NewLen ); + + // Loop Back + + ProcessPacket(conn, FendPtr+1, Len - (int)NewLen -2); + return; + +} + +VOID ProcessKPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len) +{ + UCHAR Char; + UCHAR * cmdStart; + int cmdlen = 0; + + // we will often get a whole connamd at once, but may not, so be prepared to receive char by char + // Could also get more than one command per packet + + cmdStart = rxbuffer; + + if (rxbuffer[0] == FEND && rxbuffer[Len-1] == FEND) + { + // Term thinks it is hosr mode + + // Unless it is FEND FF FEND (exit KISS) or FEND Q FEND (exit host) + + if (rxbuffer[2] == FEND) + { + if (rxbuffer[1] == 255 || rxbuffer[1] == 'q') + { + // If any more , process it. + + if (Len == 3) + return; + + Len -= 3; + rxbuffer+= 3; + ProcessKPacket(conn, rxbuffer, Len); + return; + } + } + conn->MODE = 1; + return; + } + + while (Len > 0) + { + Char = *(rxbuffer++); + Len--; + cmdlen++; + +// if (conn->TermPtr > 120) conn->TermPtr = 120; // Prevent overflow + + if (conn->ECHOFLAG) BPQSerialSendData(conn, &Char, 1); + + if (Char == 0x0d) + { + // We have a command line + + *(rxbuffer-1) = 0; + ProcessKNormCommand(conn, cmdStart); + conn->RXBPtr -= cmdlen; + cmdlen = 0; + cmdStart = rxbuffer; + } + } +} + +VOID ProcessKNormCommand(struct TNCDATA * conn, UCHAR * rxbuffer) +{ +// UCHAR CmdReply[]="C00"; + UCHAR ResetReply[] = "\xC0\xC0S00\xC0"; + int Len; + + char seps[] = " \t\r"; + char * Command, * Arg1, * Arg2; + char * Context; + + if (conn->Channels[1]->Connected) + { + Len = (int)strlen(rxbuffer); + rxbuffer[Len] = 0x0d; + SendMsg(conn->Channels[1]->BPQStream, rxbuffer, Len+1); + return; + } + + Command = strtok_s(rxbuffer, seps, &Context); + Arg1 = strtok_s(NULL, seps, &Context); + Arg2 = strtok_s(NULL, seps, &Context); + + if (Command == NULL) + { + BPQSerialSendData(conn, "cmd:", 4); + return; + } + + if (_stricmp(Command, "RESET") == 0) + { + if (conn->nextMode) + BPQSerialSendData(conn, ResetReply, 6); + else + BPQSerialSendData(conn, "cmd:", 4); + + + conn->MODE = conn->nextMode; + + if (conn->MODE) + EnableAppl(conn); + else + DisableAppl(conn); + + return; + } + + if (_stricmp(Command, "K") == 0) + { + int SaveAuthProg = AuthorisedProgram; + + AuthorisedProgram = 1; + SessionControl(conn->Channels[1]->BPQStream, 1, 0); + AuthorisedProgram = SaveAuthProg; + + return; + } + + if (_memicmp(Command, "IN", 2) == 0) + { + if (Arg1) + { + if (_stricmp(Arg1, "HOST") == 0) + conn->nextMode = TRUE; + else + conn->nextMode = FALSE; + } + + BPQSerialSendData(conn, "INTFACE was TERMINAL\r", 21); + BPQSerialSendData(conn, "cmd:", 4); + return; + } + +//cmd: + +//INTFACE HOST +//INTFACE was TERMINAL +//cmd:RESET +//ÀÀS00À +//ÀC20XFLOW OFFÀ + + + //SendKISSData(conn, CmdReply, 3); + + BPQSerialSendData(conn, "cmd:", 4); + + + // Process Non-Hostmode Packet + + return; +} +int FreeBytes = 999; + +static VOID ProcessKHOSTPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len) +{ + struct StreamInfo * channel; + UCHAR Command[80]; + UCHAR Reply[400]; + UCHAR TXBuff[400]; + UCHAR CmdReply[]="C00"; + + UCHAR Chan, Stream; + int i, j, TXLen, StreamNo; + + char * Cmd, * Arg1, * Arg2, * Arg3; + char * Context; + char seps[] = " \t\r\xC0"; + int CmdLen; + + if ((Len == 1) && ((rxbuffer[0] == 'q') || (rxbuffer[0] == 'Q'))) + { + // Force Back to Command Mode + + Sleep(3000); + conn->MODE = FALSE; + BPQSerialSendData(conn, "\r\r\rcmd:", 7); + return; + } + + if (rxbuffer[0] == '?') + { + // what is this ??? + + memcpy(Reply,CmdReply,3); + SendKISSData(conn, Reply, 3); + return; + } + + Chan = rxbuffer[1]; + Stream = rxbuffer[2]; + + StreamNo = Stream == '0' ? 0 : Stream - '@'; + + if (StreamNo > conn->HOSTSTREAMS) + { + SendKISSData(conn, "C00Invalid Stream", 17); + return; + } + + switch (rxbuffer[0]) + { + case 'C': + + // Command Packet. Extract Command + + if (Len > 80) Len = 80; + + memcpy(Command, &rxbuffer[3], Len-3); + Command[Len-3] = 0; + + Cmd = strtok_s(Command, seps, &Context); + Arg1 = strtok_s(NULL, seps, &Context); + Arg2 = strtok_s(NULL, seps, &Context); + Arg3 = strtok_s(NULL, seps, &Context); + CmdLen = (int)strlen(Cmd); + + if (_stricmp(Cmd, "S") == 0) + { + // Status + + FreeBytes = 2000; + + // Ideally I should do flow control by channel, but Paclink (at least) doesn't have a mechanism + + for (i = 1; i < conn->HOSTSTREAMS; i++) + { + if (conn->Channels[i]->Connected) + if (TXCount(conn->Channels[1]->BPQStream) > 10) + FreeBytes = 0; + } + + // This format works with Paclink and RMS Packet, but it doesn't seem to conform to the spec + + // I think maybe the Channel status should be in the same Frame. + + TXLen = sprintf(Reply, "C00FREE BYTES %d\r", FreeBytes); + SendKISSData(conn, Reply, TXLen); + + for (j=1; j <= conn->HOSTSTREAMS; j++) + { + channel = conn->Channels[j]; + + if (channel->Connected) + { + TXLen = sprintf(Reply, "C00%c/V stream - CONNECTED to %s", j + '@', "SWITCH"); + SendKISSData(conn, Reply, TXLen); + } +// else +// TXLen = sprintf(Reply, "C00%c/V stream - DISCONNECTED", j + '@'); + + } + return; + } + + if (_memicmp(Cmd, "C", CmdLen) == 0) + { + int Port; + struct StreamInfo * channel; + int BPQStream; + UCHAR * MYCall; + + // Connect. If command has a via string and first call is numeric use it as a port number + + if (StreamNo == 0) + { + Stream = 'A'; + StreamNo = 1; + } + + if (Arg2 && Arg3) + { + if (_memicmp(Arg2, "via", (int)strlen(Arg2)) == 0) + { + // Have a via string as 2nd param + + Port = atoi(Arg3); + { + if (Port > 0) // First Call is numeric + { + if (strlen(Context) > 0) // More Digis + TXLen = sprintf(TXBuff, "c %s %s v %s\r", Arg3, Arg1, Context); + else + TXLen = sprintf(TXBuff, "c %s %s\r", Arg3, Arg1); + } + else + { + // First Call Isn't Numeric. This won't work, as Digis without a port is invalid + + SendKISSData(conn, "C00Invalid via String (First must be Port)", 42); + return; + + } + } + } + else + TXLen = sprintf(TXBuff, "%s %s %s %s %s\r", Cmd, Arg1, Arg2, Arg3, Context); + + } + else + { + TXLen = sprintf(TXBuff, "C %s\r", Arg1); + } + + Reply[0] = 'C'; + Reply[1] = Chan; + Reply[2] = Stream; + SendKISSData(conn, Reply, 3); + + channel = conn->Channels[StreamNo]; + BPQStream = channel->BPQStream; + MYCall = (UCHAR *)&channel->MYCall; + + Connect(BPQStream); + + if (MYCall[0] > 0) + { + ChangeSessionCallsign(BPQStream, EncodeCall(MYCall)); + } + + SendMsg(conn->Channels[StreamNo]->BPQStream, TXBuff, TXLen); + + return; + + } + + if (_stricmp(Cmd, "D") == 0) + { + // Disconnect + + if (StreamNo == 0) + { + Stream = 'A'; + StreamNo = 1; + } + + SessionControl(conn->Channels[StreamNo]->BPQStream, 2, 0); + return; + } + + if (_memicmp(Cmd, "INT", 3) == 0) + { + SendKISSData(conn, "C00INTFACE HOST", 15); + return; + } + + if (_stricmp(Cmd, "PACLEN") == 0) + { + SendKISSData(conn, "C00PACLEN 128/128", 17); + return; + } + + if (_memicmp(Cmd, "MYCALL", CmdLen > 1 ? CmdLen : 2) == 0) + { + if (strlen(Arg1) < 30) + strcpy(conn->Channels[StreamNo]->MYCall, Arg1); + } + + memcpy(Reply,CmdReply,3); + SendKISSData(conn, Reply, 3); + return; + + case 'D': + + // Data to send + + + if (StreamNo > conn->HOSTSTREAMS) return; // Protect + + TXLen = KissDecode(&rxbuffer[3], TXBuff, Len-3); + SendMsg(conn->Channels[StreamNo]->BPQStream, TXBuff, TXLen); + + conn->Channels[StreamNo]->CloseTimer = 0; // Cancel Timer + + return; + + default: + + memcpy(Reply,CmdReply,3); + SendKISSData(conn, Reply, 3); + return; + } +} + +static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len) +{ + int i,txptr=0; + UCHAR c; + + for (i=0;iBPQStream, ConnectingCall); + strlop(ConnectingCall, ' '); + + if (conn->MODE) + { + Len = sprintf (Msg, "S1%c*** CONNECTED to %s ", Stream + '@', ConnectingCall); + SendKISSData(conn, Msg, Len); + } + else + { + Len = sprintf (Msg, "*** CONNECTED to %s\r", ConnectingCall); + BPQSerialSendData(conn, Msg, Len); + BPQSerialSetDCD(conn->hDevice); + } + + channel->Connected = TRUE; + + return 0; + +} + +int KANTDisconnected (struct TNCDATA * conn, struct StreamInfo * channel, int Stream) +{ + UCHAR Msg[50]; + int Len; + + if (conn->MODE) + { + Len = sprintf (Msg, "S1%c*** DISCONNECTED", Stream + '@'); + SendKISSData(conn, Msg, Len); + } + else + { + BPQSerialSendData(conn, "*** DISCONNECTED\r", 17); + BPQSerialClrDCD(conn->hDevice); + } + + channel->Connected = FALSE; + channel->CloseTimer = 0; + + return 0; +} + +// SCS Mode Stuff + +unsigned short int compute_crc(unsigned char *buf,int len); +VOID EmCRCStuffAndSend(struct TNCDATA * conn, UCHAR * Msg, int Len); +int APIENTRY ChangeSessionPaclen(int Stream, int Paclen); + +int EmUnstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len) +{ + int i, j=0; + + for (i=0; iOutgoingCall[0]) + strcpy(ConnectedCall, Channel->OutgoingCall); + else + GetCallsign(BPQStream, ConnectedCall); + + SCSReply[2] = HostStream; + SCSReply[3] = 3; + ReplyLen = sprintf(&SCSReply[4], "(%d) CONNECTED to %s", Channel->BPQStream, ConnectedCall); + ReplyLen += 5; + EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + + Channel->Connected = TRUE; + return TRUE; + } + // Disconnected + + SCSReply[2] = HostStream; + SCSReply[3] = 3; + ReplyLen = sprintf(&SCSReply[4], "(%d) DISCONNECTED fm G8BPQ", Channel->BPQStream); + ReplyLen += 5; // Include Null + EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + + Channel->Connected = FALSE; + return TRUE; + } + return FALSE; +} + +BOOL SCSCheckForData(struct TNCDATA * conn, struct StreamInfo * Channel, int HostStream, int BPQStream) +{ + int Length, Count; + + GetMsg(BPQStream, &SCSReply[5], &Length, &Count); + + if (Length == 0) + return FALSE; + + if (strstr(&SCSReply[5], "} Downlink connect needs port number") || + strstr(&SCSReply[5], "} Failure with ") || + strstr(&SCSReply[5], "} Sorry, ")) + Channel->CloseTimer = CloseDelay * 10; + else + Channel->CloseTimer = 0; // Cancel Timer + + SCSReply[2] = HostStream; + SCSReply[3] = 7; + SCSReply[4] = Length - 1; + + ReplyLen = Length + 5; + EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + + return TRUE; +} + +VOID ProcessSCSHostFrame(struct TNCDATA * conn, UCHAR * Buffer, int Length) +{ + int Channel = Buffer[0]; + int Command = Buffer[1] & 0x3f; + int Len = Buffer[2]; + struct StreamInfo * channel; + UCHAR TXBuff[400]; + int BPQStream; + char * MYCall; + UCHAR Stream; + int TXLen, i; + BPQVECSTRUC * HOST; + + // SCS Channel 31 is the Pactor channel, mapped to the first stream + + if (Channel == 0) + Stream = -1; + else + if (Channel == 31) + Stream = 0; + else + Stream = Channel; + + channel = conn->Channels[Stream]; + + if (conn->Toggle == (Buffer[1] & 0x80) && (Buffer[1] & 0x40) == 0) + { + // Repeat Condition + + //EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + //return; + } + + conn->Toggle = (Buffer[1] & 0x80); + conn->Toggle ^= 0x80; + +// if (Channel == 255 && Len == 0) + if (Channel == 255) + { + UCHAR * NextChan = &SCSReply[4]; + + // General Poll + + // See if any channels have anything avaiilable + + for (i = 1; i <= conn->HOSTSTREAMS; i++) + { + channel = conn->Channels[i]; + HOST = &BPQHOSTVECTOR[channel->BPQStream - 1]; // API counts from 1 + + if ((HOST->HOSTFLAGS & 3)) + { + *(NextChan++) = i + 1; // Something for this channel + continue; + } + + if (RXCount(channel->BPQStream)) + *(NextChan++) = i + 1; // Something for this channel + } + + *(NextChan++) = 0; + + SCSReply[2] = 255; + SCSReply[3] = 1; + + ReplyLen = (int)(NextChan - &SCSReply[0]); + + EmCRCStuffAndSend(conn, SCSReply, ReplyLen); + return; + } + + if (Channel == 254) // Status + { + // Extended Status Poll + + //TNC->Streams[0].PTCStatus0 = Status; + // TNC->Streams[0].PTCStatus1 = PactorLevel; // Pactor Level 1-4 + // TNC->Streams[0].PTCStatus2 = Msg[7]; // Speed Level + // Offset = Msg[8]; + + SCSReply[2] = 254; + SCSReply[3] = 7; // Status + SCSReply[4] = 3; // Len -1 + + if (conn->Channels[0]->Connected) + { + SCSReply[5] = 0x0AA; + SCSReply[6] = 3; + SCSReply[7] = 5; + SCSReply[8] = 128; + } + else + { + SCSReply[5] = 0; + SCSReply[6] = 0; + SCSReply[7] = 0; + SCSReply[8] = 0; + } + ReplyLen = 9; + EmCRCStuffAndSend(conn, SCSReply, 9); + return; + } + + + if (Command == 0) + { + // Data Frame + + SendMsg(channel->BPQStream, &Buffer[3], Buffer[2]+ 1); + + goto AckIt; + } + + switch (Buffer[3]) + { + case 'J': // JHOST + + conn->MODE = FALSE; + DisableAppl(conn); + + return; + + case 'G': // Specific Poll + + if (CheckStatusChange(conn, channel, Channel, channel->BPQStream)) + return; // It has sent reply + + if (SCSCheckForData(conn, channel, Channel, channel->BPQStream)) + return; // It has sent reply + + SCSReply[2] = Channel; + SCSReply[3] = 0; + ReplyLen = 4; + EmCRCStuffAndSend(conn, SCSReply, 4); + return; + + case 'C': // Connect + + // Could be real, or just C to request status + + if (Channel == 0) + goto AckIt; + + if (Length == 0) + { + // STATUS REQUEST - IF CONNECTED, GET CALL + + return; + } + Buffer[Length - 2] = 0; + + // Save call for connected report + + if (Buffer[5] = '%' ) // Pactor long path? + { + TXLen = sprintf(TXBuff, "C %s\r", &Buffer[6]); + strcpy(channel->OutgoingCall, &Buffer[6]); + } + else + { + TXLen = sprintf(TXBuff, "C %s\r", &Buffer[5]); + strcpy(channel->OutgoingCall, &Buffer[5]); + } + + BPQStream = channel->BPQStream; + MYCall = &channel->MYCall[0]; + + if (MYCall[0] == 0) + MYCall = (char *)&conn->MYCALL; + + Connect(BPQStream); + if (MYCall[0] > 0) + { + ChangeSessionCallsign(BPQStream, EncodeCall(MYCall)); + } + + ChangeSessionPaclen(BPQStream, 100); + + SendMsg(BPQStream, TXBuff, TXLen); + + AckIt: + + SCSReply[2] = Channel; + SCSReply[3] = 0; + ReplyLen = 4; + EmCRCStuffAndSend(conn, SCSReply, 4); + return; + + case 'D': + + // Disconnect + + Disconnect(channel->BPQStream); + goto AckIt; + + case '%': + + // %X commands + + switch (Buffer[4]) + { + case 'V': // Version + + SCSReply[2] = Channel; + SCSReply[3] = 1; + strcpy(&SCSReply[4], "4.8 1.32"); + ReplyLen = 13; + EmCRCStuffAndSend(conn, SCSReply, 13); + + return; + case 'M': + + default: + + SCSReply[2] = Channel; + SCSReply[3] = 1; + SCSReply[4] = 0; + + ReplyLen = 5; + EmCRCStuffAndSend(conn, SCSReply, 5); + + return; + } + case '@': + default: + + SCSReply[2] = Channel; + SCSReply[3] = 1; + SCSReply[4] = 0; + + ReplyLen = 5; + EmCRCStuffAndSend(conn, SCSReply, 5); + } +} + + +VOID ProcessSCSTextCommand(struct TNCDATA * conn, char * Command, int Len) +{ + // Command to SCS in non-Host mode. + + // We can probably just dump anything but JHOST 4 and MYCALL + + if (Len == 1) + goto SendPrompt; // Just a CR + + Debugprintf("%s", Command); + + if (_memicmp(Command, "JHOST4", 6) == 0) + { + conn->MODE = TRUE; + conn->Toggle = 0; + EnableAppl(conn); + + return; + } + + if (_memicmp(Command, "TERM 4", 6) == 0) + conn->Term4Mode = TRUE; + + else if (_memicmp(Command, "T 0", 3) == 0) + conn->Term4Mode = FALSE; + + else if (_memicmp(Command, "PAC 4", 5) == 0) + conn->PACMode = TRUE; + + if (_memicmp(Command, "MYC", 3) == 0) + { + char * ptr = strchr(Command, ' '); + char MYResp[80]; + + Command[Len-1] = 0; // Remove CR + + if (ptr && (strlen(ptr) > 2)) + { + strcpy(conn->MYCALL, ++ptr); + } + + sprintf(MYResp, "\rMycall: >%s<", conn->MYCALL); + PUTSTRING(conn, MYResp); + } + + else if (_memicmp(Command, "SYS SERN", 8) == 0) + { + char SerialNo[] = "\r\nSerial number: 0100000000000000"; + PUTSTRING(conn, SerialNo); + } + else + { + char SerialNo[] = "\rXXXX"; + PUTSTRING(conn, SerialNo); + } + +SendPrompt: + + if (conn->PACMode) + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 'p'); + PUTCHARx(conn, 'a'); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + + return; + } + + if (conn->Term4Mode) + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 4); + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + PUTCHARx(conn, 1); + } + else + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + } + + +/* + + if (conn->Term4Mode) + PUTCHARx(conn, 4); + + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + +*/ + return; +} + + +VOID ProcessSCSPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Length) +{ + unsigned short crc; + char UnstuffBuffer[500]; + + // DED mode doesn't have an end of frame delimiter. We need to know if we have a full frame + + // Fortunately this is a polled protocol, so we only get one frame at a time + + // If first char != 170, then probably a Terminal Mode Frame. Wait for CR on end + + // If first char is 170, we could check rhe length field, but that could be corrupt, as + // we haen't checked CRC. All I can think of is to check the CRC and if it is ok, assume frame is + // complete. If CRC is duff, we will eventually time out and get a retry. The retry code + // can clear the RC buffer + +Loop: + + if (rxbuffer[0] != 170) + { + UCHAR *ptr; + int cmdlen; + + // Char Mode Frame I think we need to see CR on end (and we could have more than one in buffer + + // If we think we are in host mode, then to could be noise - just discard. + + if (conn->MODE) + { + conn->FROMUSERLEN = 0; + return; + } + + rxbuffer[Length] = 0; + + if (rxbuffer[0] == 0) + { + // Just ignore + + conn->FROMUSERLEN--; + if (conn->FROMUSERLEN) + { + memmove(rxbuffer, rxbuffer+1, conn->FROMUSERLEN + 1); + Length--; + goto Loop; + } + return; + } + if (rxbuffer[0] == 0x1b) + { + // Just ignore (I think!) + + conn->FROMUSERLEN--; + if (conn->FROMUSERLEN) + { + memmove(rxbuffer, rxbuffer+1, conn->FROMUSERLEN + 1); + Length--; + goto Loop; + } + return; + } + + if (rxbuffer[0] == 0x1e) + { + // Status POLL + + conn->FROMUSERLEN--; + if (conn->FROMUSERLEN) + { + memmove(rxbuffer, rxbuffer+1, conn->FROMUSERLEN + 1); + Length--; + goto Loop; + } + PUTCHARx(conn, 30); + PUTCHARx(conn, 0x87); + if (conn->Term4Mode) + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 4); + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + PUTCHARx(conn, 1); + } + else + { + PUTCHARx(conn, 13); + PUTCHARx(conn, 'c'); + PUTCHARx(conn, 'm'); + PUTCHARx(conn, 'd'); + PUTCHARx(conn, ':'); + PUTCHARx(conn, ' '); + } + + + return; + } + ptr = strchr(rxbuffer, 13); + + if (ptr == 0) + return; // Wait for rest of frame + + ptr++; + + cmdlen = (int)(ptr - rxbuffer); + + // Complete Char Mode Frame + + conn->FROMUSERLEN -= cmdlen; // Ready for next frame + + ProcessSCSTextCommand(conn, rxbuffer, cmdlen); + + if (conn->FROMUSERLEN) + { + memmove(rxbuffer, ptr, conn->FROMUSERLEN + 1); + + if (conn->Mode) + { + // now in host mode, so pass rest up a level + + ProcessSCSPacket(conn, conn->FROMUSERBUFFER, conn->FROMUSERLEN); + return; + } + goto Loop; + } + return; + } + + // Receiving a Host Mode frame + + if (Length < 6) // Minimum Frame Sise + return; + + if (rxbuffer[2] == 170) + { + // Retransmit Request + + conn->RXBPtr = 0; + return; // Ignore for now + } + + // Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice + + + Length = EmUnstuff(&rxbuffer[2], &UnstuffBuffer[2], Length - 2); + + if (Length == -1) + { + // Unstuff returned an errors (170 not followed by 0) + + conn->FROMUSERLEN = 0; + return; // Ignore for now + } + crc = compute_crc(&UnstuffBuffer[2], Length); + + if (crc == 0xf0b8) // Good CRC + { + conn->FROMUSERLEN = 0; // Ready for next frame + ProcessSCSHostFrame(conn, &UnstuffBuffer[2], Length); + return; + } + + // Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link. + + return; +} + + +VOID EmCRCStuffAndSend(struct TNCDATA * conn, UCHAR * Msg, int Len) +{ + unsigned short int crc; + UCHAR StuffedMsg[500]; + int i, j; + + crc = compute_crc(&Msg[2], Len-2); + crc ^= 0xffff; + + Msg[Len++] = (crc&0xff); + Msg[Len++] = (crc>>8); + + for (i = j = 2; i < Len; i++) + { + StuffedMsg[j++] = Msg[i]; + if (Msg[i] == 170) + { + StuffedMsg[j++] = 0; + } + } + + if (j != i) + { + Len = j; + memcpy(Msg, StuffedMsg, j); + } + + Msg[0] = 170; + Msg[1] = 170; + + BPQSerialSendData(conn, Msg, Len); +} + + + \ No newline at end of file diff --git a/.svn/pristine/9b/9be015c6c08277fa15b704100105772a7f14cc4c.svn-base b/.svn/pristine/9b/9be015c6c08277fa15b704100105772a7f14cc4c.svn-base new file mode 100644 index 0000000..fbd82e6 --- /dev/null +++ b/.svn/pristine/9b/9be015c6c08277fa15b704100105772a7f14cc4c.svn-base @@ -0,0 +1,499 @@ +/* +Copyright 2001-2015 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 +*/ + +// +// DLL to provide BPQEther support for G8BPQ switch in a Linux environment, + +// Normally uses a Raw socket, but that can't send to other apps on same machine. +// so can use a TAP device instead (or maybe as well??) + +#if !(defined(MACBPQ) || defined(FREEBSD)) + +#include +#include "cheaders.h" +#include + +#if !(defined(FREEBSD)) + +#include +#include +#include + +#endif +#endif + + +extern int tap_fd; + +typedef struct PCAPStruct +{ + UCHAR EthSource[6]; + UCHAR EthDest[6]; + short EtherType; + BOOL RLITX; + BOOL RLIRX; + BOOL Promiscuous; + + int s; /*socketdescriptor*/ + struct sockaddr_ll socket_address; /*target address*/ + +} PCAPINFO, *PPCAPINFO ; + +PCAPINFO PCAPInfo[32]; + +// on linux default to broadcast + +short udpport=0; + +unsigned int OurInst = 0; + +BOOL GotMsg; + +DWORD n; + +char Adapter[256]; + +static BOOL ReadConfigFile(int Port); +static int ProcessLine(char * buf,int Port, BOOL CheckPort); +int WritetoConsoleLocal(char * buff); + +static size_t ExtProc(int fn, int port, PMESSAGE buff) +{ + int len,txlen=0,res; + char txbuff[500]; + unsigned char rxbuff[1600]; + PCAPINFO * IF = &PCAPInfo[port]; + + if (IF->s == 0) + return 0; + + switch (fn) + { + case 1: // poll + + res = recvfrom(IF->s, rxbuff, ETH_FRAME_LEN, 0, NULL, NULL); + + if (res == -1) + { + if (errno == 11) + return 0; //Resource temporarily unavailable + + perror("Eth RX"); + return 0; + } + + if (res == 0) + /* Timeout elapsed */ + return 0; + + if (rxbuff[13] != 0xff) + return 0; + + if (IF->RLIRX) + + // RLI MODE - An extra 3 bytes before len, seem to be 00 00 41 + + { + len=rxbuff[18]*256 + rxbuff[17]; + + if ((len < 16) || (len > 320)) return 0; // Probably BPQ Mode Frame + + len-=3; + + memcpy(&buff->DEST, &rxbuff[19], len); + len += (1 + sizeof(void *)); + } + else + { + len=rxbuff[15]*256 + rxbuff[14]; + + if ((len < 16) || (len > 320)) return 0; // Probably RLI Mode Frame + + len-=3; + memcpy(&buff->DEST, &rxbuff[16], len); + len += (1 + sizeof(void *)); + } + + PutLengthinBuffer((PDATAMESSAGE)buff, len); + + return 1; + + + case 2: // send + + if (IF->RLITX) + + // RLI MODE - An extra 3 bytes before len, seem to be 00 00 41 + + { + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff); // 2 for CRC + txlen -= (sizeof(void *) - 2); + txbuff[16]=0x41; + txbuff[17]=(txlen & 0xff); + txbuff[18]=(txlen >> 8); + + if (txlen < 1 || txlen > 400) + return 0; + + memcpy(&txbuff[19], &buff->DEST[0], txlen); + + + } + else + { + txlen = GetLengthfromBuffer((PDATAMESSAGE)buff); // 2 for CRC + txlen -= (sizeof(void *) - 2); + + txbuff[14]=(txlen & 0xff); + txbuff[15]=(txlen >> 8); + + if (txlen < 1 || txlen > 400) + return 0; + + + memcpy(&txbuff[16], &buff->DEST[0], txlen); + } + + memcpy(&txbuff[0], &IF->EthDest[0],6); + memcpy(&txbuff[6], &IF->EthSource[0],6); + memcpy(&txbuff[12], &IF->EtherType,2); + + txlen+=14; + + if (txlen < 60) txlen = 60; + + // Send down the packet + + res = sendto(IF->s, txbuff, txlen, 0, + (const struct sockaddr *)&IF->socket_address, sizeof(struct sockaddr_ll)); + + if (res < 0) + { + perror("Eth Send"); + return 3; + } + +// if (tap_fd) +// write(tap_fd, txbuff, txlen); + + return (0); + + case 3: // CHECK IF OK TO SEND + + return (0); // OK + + case 4: // reinit + + return 0; + + case 5: // reinit + + return 0; + } + + return (0); +} + + +void * ETHERExtInit(struct PORTCONTROL * PortEntry) +{ + // Can have multiple ports, each mapping to a different Ethernet Adapter + + // The Adapter number is in IOADDR + // + + int i=0; + u_int netmask; + char buf[256]; + int n; + struct ifreq ifr; + size_t if_name_len; + PCAPINFO * IF; + int port = PortEntry->PORTNUMBER; + u_long param=1; + struct ifreq buffer; + + WritetoConsoleLocal("LinEther "); + + // + // Read config + // + + if (!ReadConfigFile(port)) + return (FALSE); + + if_name_len = strlen(Adapter); + + IF = &PCAPInfo[port]; + + IF->s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_BPQ)); + + if (IF->s == -1) + { + perror("Open Ether Socket"); + IF->s = 0; + } + else + { + ioctl(IF->s, FIONBIO, ¶m); + memcpy(ifr.ifr_name, Adapter, if_name_len); + ifr.ifr_name[if_name_len] = 0; + + if (ioctl(IF->s, SIOCGIFINDEX,&ifr) == -1) + perror("Get IF Number"); + + // Get MAC Address + + memset(&buffer, 0x00, sizeof(buffer)); + + strcpy(buffer.ifr_name, Adapter); + ioctl(IF->s, SIOCGIFHWADDR, &buffer); + memcpy(IF->EthSource, buffer.ifr_hwaddr.sa_data, 6); + } + + n=sprintf(buf,"Using %s = Interface %d\n", Adapter, ifr.ifr_ifindex); + WritetoConsoleLocal(buf); + + /*prepare sockaddr_ll*/ + + /*RAW communication*/ + IF->socket_address.sll_family = PF_PACKET; + + /*we don't use a protocoll above ethernet layer ->just use anything here*/ + IF->socket_address.sll_protocol = htons(ETH_P_IP); + + //index of the network device + + IF->socket_address.sll_ifindex = ifr.ifr_ifindex; + + /*ARP hardware identifier is ethernet*/ + IF->socket_address.sll_hatype = ARPHRD_ETHER; + + /*target is another host*/ + IF->socket_address.sll_pkttype = PACKET_BROADCAST; + + /*address length*/ + IF->socket_address.sll_halen = ETH_ALEN; + /*MAC - begin*/ + + memcpy(IF->socket_address.sll_addr, IF->EthDest, 6); + IF->socket_address.sll_addr[6] = 0x00;/*not used*/ + IF->socket_address.sll_addr[7] = 0x00;/*not used*/ + + +// n=sprintf(buf,"Using %s Adapter = Interface %d\r", ifr.ifr_ifindex); +// WritetoConsole(buf); + + return (ExtProc); +} + + + +static BOOL ReadConfigFile(int Port) +{ +//TYPE 1 08FF # Ethernet Type +//ETH 1 FF:FF:FF:FF:FF:FF # Target Ethernet AddrMAP G8BPQ-7 10.2.77.1 # IP 93 for compatibility +//ADAPTER 1 \Device\NPF_{21B601E8-8088-4F7D-96 29-EDE2A9243CF4} # Adapter Name + + char buf[256],errbuf[256]; + char * Config; + + Config = PortConfig[Port]; + + PCAPInfo[Port].Promiscuous = 1; // Default + PCAPInfo[Port].EtherType=htons(0x08FF); // Default + memset(PCAPInfo[Port].EthDest, 0xff, 6); + + + if (Config) + { + // Using config from bpq32.cfg + + char * ptr1 = Config, * ptr2; + + ptr2 = strchr(ptr1, 13); + while(ptr2) + { + memcpy(buf, ptr1, ptr2 - ptr1); + buf[ptr2 - ptr1] = 0; + ptr1 = ptr2 + 2; + ptr2 = strchr(ptr1, 13); + + strcpy(errbuf,buf); // save in case of error + + if (!ProcessLine(buf, Port, FALSE)) + { + WritetoConsoleLocal("BPQEther - Bad config record "); + WritetoConsoleLocal(errbuf); + WritetoConsoleLocal("\n"); + } + } + return (TRUE); + } + + n=sprintf(buf,"No config info found in bpq32.cfg\n"); + WritetoConsoleLocal(buf); + + return (FALSE); +} + + +static int ProcessLine(char * buf, int Port, BOOL CheckPort) +{ + char * ptr; + char * p_port; + char * p_mac; + char * p_Adapter; + char * p_type; + + int port; + int a,b,c,d,e,f,num; + + ptr = strtok(buf, " \t\n\r"); + + if(ptr == NULL) return (TRUE); + + if(*ptr =='#') return (TRUE); // comment + + if(*ptr ==';') return (TRUE); // comment + + if (CheckPort) + { + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + port = atoi(p_port); + + if (Port != port) return TRUE; // Not for us + } + + if(_stricmp(ptr,"ADAPTER") == 0) + { + p_Adapter = strtok(NULL, " \t\n\r"); + + strcpy(Adapter,p_Adapter); + return (TRUE); + } + + if(_stricmp(ptr,"TYPE") == 0) + { + p_type = strtok(NULL, " \t\n\r"); + + if (p_type == NULL) return (FALSE); + + num=sscanf(p_type,"%x",&a); + + if (num != 1) return FALSE; + + PCAPInfo[Port].EtherType=htons(a); + return (TRUE); + + } + + if(_stricmp(ptr,"promiscuous") == 0) + { + ptr = strtok(NULL, " \t\n\r"); + + if (ptr == NULL) return (FALSE); + + PCAPInfo[Port].Promiscuous = atoi(ptr); + + return (TRUE); + + } + + if(_stricmp(ptr,"RXMODE") == 0) + { + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + if(_stricmp(p_port,"RLI") == 0) + { + PCAPInfo[Port].RLIRX=TRUE; + return (TRUE); + } + + if(_stricmp(p_port,"BPQ") == 0) + { + PCAPInfo[Port].RLIRX=FALSE; + return (TRUE); + } + + return FALSE; + + } + + if(_stricmp(ptr,"TXMODE") == 0) + { + p_port = strtok(NULL, " \t\n\r"); + + if (p_port == NULL) return (FALSE); + + if(_stricmp(p_port,"RLI") == 0) + { + PCAPInfo[Port].RLITX=TRUE; + return (TRUE); + } + + if(_stricmp(p_port,"BPQ") == 0) + { + PCAPInfo[Port].RLITX=FALSE; + return (TRUE); + } + + return FALSE; + + } + + if(_stricmp(ptr,"DEST") == 0) + { + p_mac = strtok(NULL, " \t\n\r"); + + if (p_mac == NULL) return (FALSE); + + num=sscanf(p_mac,"%x-%x-%x-%x-%x-%x",&a,&b,&c,&d,&e,&f); + + if (num != 6) return FALSE; + + PCAPInfo[Port].EthDest[0]=a; + PCAPInfo[Port].EthDest[1]=b; + PCAPInfo[Port].EthDest[2]=c; + PCAPInfo[Port].EthDest[3]=d; + PCAPInfo[Port].EthDest[4]=e; + PCAPInfo[Port].EthDest[5]=f; + + + // strcpy(Adapter,p_Adapter); + + return (TRUE); + } + + if(_stricmp(ptr,"SOURCE") == 0) // not used, but ignore + return (TRUE); + + // + // Bad line + // + return (FALSE); + +} + + diff --git a/.svn/pristine/ea/ea6b9a12ba7c8b4d032a734faeabcdeeac283a34.svn-base b/.svn/pristine/ea/ea6b9a12ba7c8b4d032a734faeabcdeeac283a34.svn-base new file mode 100644 index 0000000..c1aea82 --- /dev/null +++ b/.svn/pristine/ea/ea6b9a12ba7c8b4d032a734faeabcdeeac283a34.svn-base @@ -0,0 +1,1801 @@ +/* +Copyright 2001-2018 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 +*/ + +// Mail and Chat Server for BPQ32 Packet Switch +// +// lzhuf Routines + +#include "bpqmail.h" + +BOOL isAMPRMsg(char * Addr); + +struct Country * FindCountry(char * Name); +struct UserInfo * FindAMPR(); + +/************************************************************** + lzhuf.c + written by Haruyasu Yoshizaki 1988/11/20 + some minor changes 1989/04/06 + comments translated by Haruhiko Okumura 1989/04/07 + getbit and getbyte modified 1990/03/23 by Paul Edwards + so that they would work on machines where integers are + not necessarily 16 bits (although ANSI guarantees a + minimum of 16). This program has compiled and run with + no errors under Turbo C 2.0, Power C, and SAS/C 4.5 + (running on an IBM mainframe under MVS/XA 2.2). Could + people please use YYYY/MM/DD date format so that everyone + in the world can know what format the date is in? + external storage of filesize changed 1990/04/18 by Paul Edwards to + Intel's "little endian" rather than a machine-dependant style so + that files produced on one machine with lzhuf can be decoded on + any other. "little endian" style was chosen since lzhuf + originated on PC's, and therefore they should dictate the + standard. + initialization of something predicting spaces changed 1990/04/22 by + Paul Edwards so that when the compressed file is taken somewhere + else, it will decode properly, without changing ascii spaces to + ebcdic spaces. This was done by changing the ' ' (space literal) + to 0x20 (which is the far most likely character to occur, if you + don't know what environment it will be running on. +**************************************************************/ + +int ReformatSyncMessage(CIRCUIT * conn); + +#define int __int16 + +/* crctab calculated by Mark G. Mendel, Network Systems Corporation */ + +UCHAR *infile, *outfile, * endinfile; + +short Get() +{ + if (infile == endinfile) + return -1; + else + return *(infile++); +} + +static unsigned short crctab[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + + +#define updcrc(cp, crc) ((crc << 8) ^ crctab[(cp & 0xff) ^ (crc >> 8)]) + +uint32_t textsize = 0, codesize = 0; +unsigned short crc; +int version_1; +char wterr[] = "Can't write."; + +void Error(char *message) +{ + printf("\n%s\n", message); + exit(EXIT_FAILURE); +} + +/********** LZSS compression **********/ + +#define N 2048 /* buffer size */ +#define F 60 /* lookahead buffer size */ +#define THRESHOLD 2 +#define NIL N /* leaf of tree */ + +unsigned char + text_buf[N + F - 1]; +static int match_position, match_length, + lson[N + 1], rson[N + 257], dad[N + 1]; + +static int crc_fputc(unsigned short c) +{ + crc = updcrc(c, crc); + *(outfile++) = (unsigned char)c; + return 0; +} + +short crc_fgetc() +{ + short retour = *(infile++); + + return(retour); +} + +void InitTree(void) /* initialize trees */ +{ + int i; + + for (i = N + 1; i <= N + 256; i++) + rson[i] = NIL; /* root */ + for (i = 0; i < N; i++) + dad[i] = NIL; /* node */ +} + +static void InsertNode(int r) /* insert to tree */ +{ + int i, p, cmp; + unsigned char *key; + unsigned c; + + cmp = 1; + key = &text_buf[r]; + p = N + 1 + key[0]; + rson[r] = lson[r] = NIL; + match_length = 0; + for ( ; ; ) { + if (cmp >= 0) { + if (rson[p] != NIL) + p = rson[p]; + else { + rson[p] = r; + dad[r] = p; + return; + } + } else { + if (lson[p] != NIL) + p = lson[p]; + else { + lson[p] = r; + dad[r] = p; + return; + } + } + for (i = 1; i < F; i++) + if ((cmp = key[i] - text_buf[p + i]) != 0) + break; + if (i > THRESHOLD) { + if (i > match_length) { + match_position = ((r - p) & (N - 1)) - 1; + if ((match_length = i) >= F) + break; + } + if (i == match_length) { + if ((c = ((r - p) & (N-1)) - 1) < (unsigned)match_position) { + match_position = c; + } + } + } + } + dad[r] = dad[p]; + lson[r] = lson[p]; + rson[r] = rson[p]; + dad[lson[p]] = r; + dad[rson[p]] = r; + if (rson[dad[p]] == p) + rson[dad[p]] = r; + else + lson[dad[p]] = r; + dad[p] = NIL; /* remove p */ +} + +static void DeleteNode(int p) /* remove from tree */ +{ + int q; + + if (dad[p] == NIL) + return; /* not registered */ + if (rson[p] == NIL) + q = lson[p]; + else + if (lson[p] == NIL) + q = rson[p]; + else { + q = lson[p]; + if (rson[q] != NIL) { + do { + q = rson[q]; + } while (rson[q] != NIL); + rson[dad[q]] = lson[q]; + dad[lson[q]] = dad[q]; + lson[q] = lson[p]; + dad[lson[p]] = q; + } + rson[q] = rson[p]; + dad[rson[p]] = q; + } + dad[q] = dad[p]; + if (rson[dad[p]] == p) + rson[dad[p]] = q; + else + lson[dad[p]] = q; + dad[p] = NIL; +} + +/* Huffman coding */ + +#define N_CHAR (256 - THRESHOLD + F) + /* kinds of characters (character code = 0..N_CHAR-1) */ +#define T (N_CHAR * 2 - 1) /* size of table */ +#define R (T - 1) /* position of root */ +#define MAX_FREQ 0x8000 /* updates tree when the */ + /* root frequency comes to this value. */ +typedef unsigned char uchar; + + +/* table for encoding and decoding the upper 6 bits of position */ + +/* for encoding */ +uchar p_len[64] = { + 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 +}; + +uchar p_code[64] = { + 0x00, 0x20, 0x30, 0x40, 0x50, 0x58, 0x60, 0x68, + 0x70, 0x78, 0x80, 0x88, 0x90, 0x94, 0x98, 0x9C, + 0xA0, 0xA4, 0xA8, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC, + 0xC0, 0xC2, 0xC4, 0xC6, 0xC8, 0xCA, 0xCC, 0xCE, + 0xD0, 0xD2, 0xD4, 0xD6, 0xD8, 0xDA, 0xDC, 0xDE, + 0xE0, 0xE2, 0xE4, 0xE6, 0xE8, 0xEA, 0xEC, 0xEE, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +/* for decoding */ +uchar d_code[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, + 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, + 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, + 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, + 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, + 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, + 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, + 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, 0x1B, 0x1B, + 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F, + 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, + 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, + 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, + 0x2C, 0x2C, 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, +}; + +uchar d_len[256] = { + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, +}; + +unsigned int freq[T + 1]; /* frequency table */ + +int prnt[T + N_CHAR]; /* pointers to parent nodes, except for the */ + /* elements [T..T + N_CHAR - 1] which are used to get */ + /* the positions of leaves corresponding to the codes. */ + +int son[T]; /* pointers to child nodes (son[], son[] + 1) */ + +unsigned int getbuf = 0; +uchar getlen = 0; + +static int GetBit(void) /* get one bit */ +{ + unsigned int i; + + while (getlen <= 8) { + if ((i = crc_fgetc()) < 0) i = 0; + getbuf |= i << (8 - getlen); + getlen += 8; + } + i = getbuf; + getbuf <<= 1; + getlen--; + return (int)((i & 0x8000) >> 15); +} + +static int GetByte(void) /* get one byte */ +{ + unsigned int i; + + while (getlen <= 8) { + if ((i = crc_fgetc()) == 0xffff) i = 0; + getbuf |= i << (8 - getlen); + getlen += 8; + } + i = getbuf; + getbuf <<= 8; + getlen -= 8; + return (int)((i & 0xff00) >> 8); +} + +unsigned int putbuf = 0; +uchar putlen = 0; + +static void Putcode(int l, unsigned c) /* output c bits of code */ +{ + putbuf |= c >> putlen; + if ((putlen += l) >= 8) { + if (crc_fputc(putbuf >> 8) == EOF) { + Error(wterr); + } + if ((putlen -= 8) >= 8) { + if (crc_fputc(putbuf) == EOF) { + Error(wterr); + } + codesize += 2; + putlen -= 8; + putbuf = c << (l - putlen); + } else { + putbuf <<= 8; + codesize++; + } + } +} + + +/* initialization of tree */ + +int StartHuff(void) +{ + int i, j; + + for (i = 0; i < N_CHAR; i++) { + freq[i] = 1; + son[i] = i + T; + prnt[i + T] = i; + } + i = 0; j = N_CHAR; + while (j <= R) { + freq[j] = freq[i] + freq[i + 1]; + son[j] = i; + prnt[i] = prnt[i + 1] = j; + i += 2; j++; + } + freq[T] = 0xffff; + prnt[R] = 0; + + return 0; +} + +/* reconstruction of tree */ + +static void reconst(void) +{ + int i, j, k; + unsigned int f, l; + + /* collect leaf nodes in the first half of the table */ + /* and replace the freq by (freq + 1) / 2. */ + j = 0; + for (i = 0; i < T; i++) { + if (son[i] >= T) { + freq[j] = (freq[i] + 1) / 2; + son[j] = son[i]; + j++; + } + } + /* begin constructing tree by connecting sons */ + for (i = 0, j = N_CHAR; j < T; i += 2, j++) { + k = i + 1; + f = freq[j] = freq[i] + freq[k]; + for (k = j - 1; f < freq[k]; k--); + k++; + l = (j - k) * 2; + memmove(&freq[k + 1], &freq[k], l); + freq[k] = f; + memmove(&son[k + 1], &son[k], l); + son[k] = i; + } + /* connect prnt */ + for (i = 0; i < T; i++) { + if ((k = son[i]) >= T) { + prnt[k] = i; + } else { + prnt[k] = prnt[k + 1] = i; + } + } +} + + +/* increment frequency of given code by one, and update tree */ + +static void update(int c) +{ + int i, j, l; + unsigned int k; + + if (freq[R] == MAX_FREQ) { + reconst(); + } + c = prnt[c + T]; + do { + k = ++freq[c]; + + /* if the order is disturbed, exchange nodes */ + + l = c + 1; + + if ((unsigned)k > freq[l]) + { + while ((unsigned)k > freq[++l]); + l--; + freq[c] = freq[l]; + freq[l] = k; + + i = son[c]; + prnt[i] = l; + if (i < T) prnt[i + 1] = l; + + j = son[l]; + son[l] = i; + + prnt[j] = c; + if (j < T) prnt[j + 1] = c; + son[c] = j; + + c = l; + } + } while ((c = prnt[c]) != 0); /* repeat up to root */ +} + +unsigned code, len; + +static void EncodeChar(unsigned int c) +{ + unsigned int i; + int j, k; + + i = 0; + j = 0; + k = prnt[c + T]; + + /* travel from leaf to root */ + do { + i >>= 1; + + /* if node's address is odd-numbered, choose bigger brother node */ + if (k & 1) i += 0x8000; + + j++; + } while ((k = prnt[k]) != R); + Putcode(j, i); + code = i; + len = j; + update(c); +} + +static void EncodePosition(unsigned int c) +{ + unsigned int i; + + /* output upper 6 bits by table lookup */ + i = c >> 6; + Putcode(p_len[i], (unsigned)p_code[i] << 8); + + /* output lower 6 bits verbatim */ + Putcode(6, (c & 0x3f) << 10); +} + +static void EncodeEnd(void) +{ + if (putlen) { + if (crc_fputc(putbuf >> 8) == EOF) { + Error(wterr); + } + codesize++; + } +} + +int DecodeChar(void) +{ + unsigned int c; + + c = son[R]; + + /* travel from root to leaf, */ + /* choosing the smaller child node (son[]) if the read bit is 0, */ + /* the bigger (son[]+1} if 1 */ + while (c < T) { + c += GetBit(); + c = son[c]; + } + c -= T; + update(c); + return (int)c; +} + +int DecodePosition(void) +{ + unsigned int i, j, c; + + /* recover upper 6 bits from table */ + i = GetByte(); + c = (unsigned)d_code[i] << 6; + j = d_len[i]; + + /* read lower 6 bits verbatim */ + j -= 2; + while (j--) { + i = (i << 1) + GetBit(); + } + return (int)(c | (i & 0x3f)); +} + +/* compression */ + +int32_t Encode(char * in, char * out, int32_t inlen, BOOL B1Protocol, int Compress) +{ + int i, c, len, r, s, last_match_length; + unsigned char *ptr; + + putbuf = 0; + putlen = 0; + textsize = 0; + codesize = 0; + + crc = 0; + outfile = out; + + if (B1Protocol) + outfile+=2; // Space for CRC + +// infile = &conn->MailBuffer[2]; + + textsize = inlen; + + ptr = (char *)&textsize; + +#ifdef __BIG_ENDIAN__ + crc_fputc(*(ptr+3)); + crc_fputc(*(ptr+2)); + crc_fputc(*(ptr+1)); + crc_fputc(*(ptr)); +#else + crc_fputc(*(ptr++)); + crc_fputc(*(ptr++)); + crc_fputc(*(ptr++)); + crc_fputc(*(ptr++)); +#endif + + if (textsize == 0) + return 0; + + infile = in; + endinfile = infile + inlen; + textsize = 0; /* rewind and re-read */ + + // if using uncompressed just copy in to out + // may not be optimum but is simple! + + if (Compress) + { + StartHuff(); + InitTree(); + s = 0; + r = N - F; + for (i = s; i < r; i++) + text_buf[i] = 0x20; + for (len = 0; len < F && (c = Get()) != EOF; len++) + text_buf[r + len] = (unsigned char)c; + textsize = len; + for (i = 1; i <= F; i++) + InsertNode(r - i); + InsertNode(r); + + do + { + if (match_length > len) + match_length = len; + if (match_length <= THRESHOLD) + { + match_length = 1; + EncodeChar(text_buf[r]); + } + else + { + EncodeChar(255 - THRESHOLD + match_length); + EncodePosition(match_position); + } + last_match_length = match_length; + + for (i = 0; i < last_match_length && (c = Get()) != EOF; i++) + { + DeleteNode(s); + text_buf[s] = (unsigned char)c; + + if (s < F - 1) + text_buf[s + N] = (unsigned char)c; + s = (s + 1) & (N - 1); + r = (r + 1) & (N - 1); + InsertNode(r); + } + + while (i++ < last_match_length) + { + DeleteNode(s); + s = (s + 1) & (N - 1); + r = (r + 1) & (N - 1); + if (--len) InsertNode(r); + } + } + while (len > 0); + + EncodeEnd(); + } + else + { + int32_t n = inlen; + while (n--) + crc_fputc(*(infile++)); + + codesize += inlen; + } + + if (B1Protocol) + { + out[0] = crc & 0xff; + out[1]= crc >> 8; + codesize+=2; + } + + codesize += 4; + + if (Compress) + Logprintf(LOG_BBS, NULL, '|', "Compressed Message Comp Len %d Msg Len %d CRC %x", + codesize, inlen, crc); + else + Logprintf(LOG_BBS, NULL, '|', "Uncompressed Message Sent Len %d Msg Len %d CRC %x", + codesize, inlen, crc); + + return codesize; +} + +BOOL CheckifPacket(char * Via) +{ + char * ptr1, * ptr2; + + // Message addressed to a non-winlink address + // Need to see if real smtp, or a packet address + + // No . in address assume Packet - g8bpq@g8bpq + + ptr1 = strchr(Via, '.'); + + if (ptr1 == NULL) + return TRUE; // Packet + + // Find Last Element + + ptr2 = strchr(++ptr1, '.'); + + while (ptr2) + { + ptr1 = ptr2; + ptr2 = strchr(++ptr1, '.'); + } + + if (ptr1[0] == 0) + return TRUE; // Packet + + // ptr1 is last element. If a valid continent, it is a packet message + // should really accept .WW on end as it is valid + if (FindContinent(ptr1)) + return TRUE; // Packet + + if (FindCountry(ptr1)) + return TRUE; // Packet + + if ((_stricmp(ptr1, "MARS") == 0) || (_stricmp(ptr1, "USA") == 0) || (_stricmp(ptr1, "WW") == 0)) // MARS used both MARS and USA + return TRUE; // Packet + + return FALSE; +} + +void Decode(CIRCUIT * conn, int FromSync) +{ + unsigned char *ptr; + char * StartofMsg; + short i, j, k, r; + short c; + uint32_t count; + unsigned short crc_read; + int Index = 0; + struct FBBHeaderLine * FBBHeader= &conn->FBBHeaders[0]; // The Headers from an FFB forward block + BOOL NTS = FALSE; + + getbuf = 0; + getlen = 0; + textsize = 0; + codesize = 0; + + infile = &conn->MailBuffer[0]; + + crc = 0; + + if (conn->BBSFlags & FBBB1Mode) + { + short val; + uint32_t n; + + crc_read = infile[0]; + crc_read |= infile[1] << 8; + + for (n = 2; n < conn->TempMsg->length; n++) + { + val = infile[n]; + crc = updcrc(val, crc); + } + if (crc != crc_read) + { + nodeprintf(conn, "*** Message CRC Error File %x Calc %x\r", crc_read, crc); + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } + + infile+=2; + } + + textsize = 0; + ptr = (char *)&textsize; + +#ifdef __BIG_ENDIAN__ + + ptr[3] = (unsigned char)crc_fgetc(); + ptr[2] = (unsigned char)crc_fgetc(); + ptr[1] = (unsigned char)crc_fgetc(); + ptr[0] = (unsigned char)crc_fgetc(); + +#else + + for (i = 0 ; i < sizeof(textsize) ; i++) + ptr[i] = (unsigned char)crc_fgetc(); + +#endif + + // Temp fix for duff MACBPQ (Message Length sent big-endian) + + if (textsize > 500000) + { + char x[4]; + char y[4]; + + memcpy(x, &textsize, 4); + y[0] = x[3]; + y[1] = x[2]; + y[2] = x[1]; + y[3] = x[0]; + + memcpy(&textsize, y, 4); + + if (textsize > 5000000) + { + nodeprintf(conn, "*** Message Size Invalid %d\r", textsize); + Debugprintf("*** Message Size Invalid %d\r", textsize); + free(conn->MailBuffer); + conn->MailBufferSize=0; + conn->MailBuffer=0; + conn->CloseAfterFlush = 20; // 2 Secs + return; + } + } + + Logprintf(LOG_BBS, conn, '|', "Uncompressing Message Comp Len %d Msg Len %d CRC %x", + conn->TempMsg->length, textsize, crc); + + outfile = zalloc(textsize + 10000); // Lots of space for B2 header manipulations + + if (textsize == 0) + return; + + // If compressed, decompress + + if (conn->BBSFlags & FBBCompressed) + { + StartHuff(); + + for (i = 0; i < N - F; i++) + text_buf[i] = 0x20; + + r = N - F; + + for (count = 0; count < textsize; ) + { + c = DecodeChar(); + if (c < 256) + { + *(outfile++) = (unsigned char)c; + text_buf[r++] = (unsigned char)c; + r &= (N - 1); + count++; + } + else + { + i = (r - DecodePosition() - 1) & (N - 1); + j = c - 255 + THRESHOLD; + for (k = 0; k < j; k++) + { + c = text_buf[(i + k) & (N - 1)]; + *(outfile++) = (unsigned char)c; + text_buf[r++] = (unsigned char)c; + r &= (N - 1); + count++; + } + } + } + outfile -=count; + } + else + { + count = textsize; + memcpy(outfile, infile, textsize); + } + + free(conn->MailBuffer); + conn->MailBuffer = outfile; + + conn->TempMsg->length = count; + + if (FromSync) + { + // Refomat Sync message as if from WLE + + if (ReformatSyncMessage(conn) == 0) + return; + + FBBHeader->B2Message = TRUE; + FBBHeader->MsgType = 'P'; + } + + if (!FBBHeader->B2Message) + { + // With B2 the Type is specified in the body, so can't update stats now + + if (FBBHeader->MsgType == 'P') + Index = PMSG; + else if (FBBHeader->MsgType == 'B') + Index = BMSG; + else if (FBBHeader->MsgType == 'T') + Index = TMSG; + + conn->UserPointer->Total.MsgsReceived[Index]++; + conn->UserPointer->Total.BytesForwardedIn[Index] += count; + } + + + if (FBBHeader->B2Message) + { + // Parse the Message for B2 From and To info +/* +MID: A3EDD4P00P55 +Date: 2009/07/25 10:08 +Type: Private +From: SMTP:john.wiseman@ntlworld.com +To: G8BPQ +Subject: RE: RMS Test Messaage +Mbo: SMTP +Body: 214 +File: 3556 NOLA.XLS +File: 5566 NEWBOAT.HOMEPORT.JPG + +*/ + UCHAR * ptr1, * ptr2, * ptr3; + __int32 linelen, MsgLen = 0; + struct MsgInfo * Msg = conn->TempMsg; + time_t Date; + char FullTo[100]; + char FullFrom[100]; + char ** RecpTo = NULL; // May be several Recipients + char ** HddrTo = NULL; // May be several Recipients + char ** Via = NULL; // May be several Recipients + __int32 LocalMsg[1000]; // Set if Recipient is a local wl2k address + char Type[1000]; // Message Type for each dest + __int32 B2To; // Offset to To: fields in B2 header + __int32 Recipients = 0; + __int32 RMSMsgs = 0, BBSMsgs = 0; +#ifndef LINBPQ + struct _EXCEPTION_POINTERS exinfo; + + __try { +#endif + Msg->B2Flags |= B2Msg; + + // Display the whole header for debugging + +/* + ptr1 = strstr(outfile, "\r\n\r\n"); + + if (ptr1) + { + *ptr1 = 0; + Debugprintf("B2 Header = %s", outfile); + *ptr1 = '\r'; + } +*/ + if (_stricmp(conn->Callsign, "RMS") == 0) + Msg->B2Flags |= FromCMS; + + if (conn->RadioOnlyMode == 'T') + Msg->B2Flags |= RadioOnlyMsg; + else if (conn->RadioOnlyMode == 'R') + Msg->B2Flags |= RadioOnlyFwd; + + ptr1 = outfile; + Loop: + ptr2 = strchr(ptr1, '\r'); + + linelen = (int)(ptr2 - ptr1); + + if (_memicmp(ptr1, "From:", 5) == 0) + { + memcpy(FullFrom, ptr1, linelen); + FullFrom[linelen] = 0; + + if (conn->Paclink || (conn->RMSExpress && (conn->UserPointer->flags & F_NOWINLINK) == 0)) + { + // Messages just have the call - need to add @winlink.org + + strcpy(Msg->emailfrom, "@winlink.org"); + + } + if (_memicmp(&ptr1[6], "smtp:", 5) == 0) + { + if (_stricmp(conn->Callsign, "RMS") == 0) + { + // Swap smtp: to rms: and save originator so we can reply via RMS + + strcpy(Msg->from, "RMS:"); + memcpy(Msg->emailfrom, &ptr1[11], linelen - 11); + } + else + { + strcpy(Msg->from, "SMTP:"); + memcpy(Msg->emailfrom, &ptr1[11], linelen - 11); + } + } + else + { + char SaveFrom[100]; + char * FromHA; + + // B2 From may now contain an @BBS + + strcpy(SaveFrom, FullFrom); + + FromHA = strlop(SaveFrom, '@'); + + // If it has an @ it could be an internet address - if it has a dot or longer than + // 6 chars assume it is. + // No, could also be packet + + // I don't think you can have an Internet message without a . in @field + // Why not just use checkifpacket ?? + + if (FromHA && !CheckifPacket(FromHA)) + { + // Internet address - set from empty and full email in + // emailfrom + + strcpy(Msg->from, "SMTP:"); + + if (strlen(FullFrom) > 46) + FullFrom[46] = 0; + + strcpy(Msg->emailfrom, &FullFrom[6]); + } + else + { + // Not an Internet Address so must be a callsign and therefore <= 6 chars + + if (strlen(SaveFrom) > 12) + SaveFrom[12] = 0; + + strcpy(Msg->from, &SaveFrom[6]); + + if (FromHA) + { + if (strlen(FromHA) > 39) FromHA[39] = 0; + Msg->emailfrom[0] = '@'; + strcpy(&Msg->emailfrom[1], _strupr(FromHA)); + } + } + + // Remove any SSID + + ptr3 = strchr(Msg->from, '-'); + if (ptr3) *ptr3 = 0; + } + + // If from a CMS, and no @ in message, append @winlink.org to the B2 Header. + // so messages passed via B2 know it is from Winlink + + if ((Msg->B2Flags & FromCMS) && strchr(FullFrom, '@') == NULL) + { + // Move Message down buffer - ptr2 is the insertion point + + memmove(ptr2+12, ptr2, count); + memcpy(ptr2, "@winlink.org", 12); + count += 12; + conn->TempMsg->length += 12; + + // Also set Emailfrom, in case read on BBS (eg by outpost) + + strcat(Msg->emailfrom, "@winlink.org"); + } + + } + else if (_memicmp(ptr1, "To:", 3) == 0 || _memicmp(ptr1, "cc:", 3) == 0) + { + int toLen; + + HddrTo=realloc(HddrTo, (Recipients+1) * sizeof(void *)); + HddrTo[Recipients] = zalloc(100); + + memset(FullTo, 0, 99); + memcpy(FullTo, &ptr1[4], linelen-4); + memcpy(HddrTo[Recipients], ptr1, linelen+2); + LocalMsg[Recipients] = FALSE; + Type[Recipients] = Msg->type; // Default to Type from Header + + Logprintf(LOG_BBS, conn, '?', "B2 Msg To: %s", FullTo); + + conn->TempMsg->length -= (int)strlen(HddrTo[Recipients]); + + B2To = (int)(ptr1 - outfile); + + // if ending in AMPR.ORG send via ISP if we have enabled forwarding AMPR + + toLen = (int)strlen(FullTo); + + if (_memicmp(&FullTo[toLen - 8], "ampr.org", 8) == 0) + { + // if our domain keep here. + + // if not, and SendAMPRDirect set, set as ISP, + // else set as RMS + + memcpy(Msg->via, FullTo, toLen); + + ptr3 = strchr(FullTo, '@'); + + if (ptr3) + { + ptr3++; + + if (_stricmp(ptr3, AMPRDomain) == 0) + { + // Our Message + + strcpy(Msg->via, FullTo); + BBSMsgs ++; + goto BBSMsg; + } + } + + if (SendAMPRDirect && FindAMPR()) + { + strcpy(Msg->via, FullTo); + strcpy(FullTo,"AMPR"); + BBSMsgs ++; + goto BBSMsg; + } + + strcpy(FullTo,"RMS"); + RMSMsgs ++; + } + + if (conn->BPQBBS && !CheckifPacket(FullTo)) // May be an message for RMS being passed to an intermediate BBS + { + // Internet address - send via RMS + + strcpy(Msg->via, FullTo); + strcpy(FullTo,"RMS"); + RMSMsgs ++; + } + else + { + ptr3 = strchr(FullTo, '@'); + + if (ptr3) + { + *ptr3++ = 0; + strcpy(Msg->via, ptr3); + } + else + Msg->via[0] = 0; + } + + if (conn->Paclink) + { + Msg->B2Flags |= FromPaclink; + + // Message from paclink + + // Messages to WL2K just have call. + // Messages to email or BBS addresses have smtp: + + + if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) + { + // See if Packet or SMTP + + if (CheckifPacket(Msg->via)) // If no RMS, don't check for routing to it) + { + // Packet Message + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + _strupr(Msg->via); + + // Update the saved to: line (remove the smtp:) + + strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); + BBSMsgs++; + + } + else + { + // Internet address - do we send via RMS?? + + // ??? Need to see if RMS is available + + memcpy(Msg->via, &ptr1[9], linelen); + Msg->via[linelen-9] = 0; + strcpy(FullTo,"RMS"); + RMSMsgs ++; + } + } + else + { + if (conn->RadioOnlyMode == 0 && (conn->UserPointer->flags & F_NOWINLINK) == 0) // treat as Packet Address? + { + strcpy(Msg->via, "winlink.org"); // Message for WL2K - add via + RMSMsgs ++; + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + { + BBSMsgs++; + } + } + + } +// else if (conn->RMSExpress && FindRMS()) // If no RMS, don't check for routing to it + else if (conn->RMSExpress) + { + Msg->B2Flags |= FromRMSExpress; + + // Message from RMS Express + // Messages to WL2K just have call. + // Messages to email or BBS addresses don't have smtp: + + + if (Msg->via[0]) + { + // Has an @ - See if Packet or SMTP. If to our AMPR address, treat as packet + + if (CheckifPacket(Msg->via) || _stricmp(Msg->via, AMPRDomain) == 0) + { + // Packet Message + + _strupr(FullTo); + _strupr(Msg->via); + BBSMsgs++; + } + else + { + // Internet address - do we send via RMS?? + + if (_memicmp(FullTo, "smtp/", 5) == 0 && ISP_Gateway_Enabled) + { + memcpy(Msg->via, &ptr1[9], linelen - 9); + Msg->via[linelen-9] = 0; + FullTo[0] = Msg->to[0] = 0; + } + else if (FindRMS()) // have RMS + { + memcpy(Msg->via, &ptr1[4], linelen); + Msg->via[linelen-4] = 0; + strcpy(FullTo,"RMS"); + RMSMsgs ++; + } + else if (ISP_Gateway_Enabled) + { + memcpy(Msg->via, &ptr1[4], linelen); + Msg->via[linelen-4] = 0; + FullTo[0] = Msg->to[0] = 0; + } + else if (isAMPRMsg(Msg->via)) + strcpy(Msg->to, "RMS"); // Routing will redirect it + } + } + else + { + if (conn->RadioOnlyMode == 0 && (conn->UserPointer->flags & F_NOWINLINK) == 0) // Dont default to winlink.org + { + strcpy(Msg->via, "winlink.org"); // Message for WL2K - add via + RMSMsgs ++; + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + goto BBSMsg; + } + } + + else // Not Paclink or RMS Express (or no RMS) + { + if (_memicmp(&ptr1[4], "SMTP:", 5) == 0) + { + // Airmail Sends MARS messages as SMTP + + if (CheckifPacket(Msg->via)) + { + // Packet Message + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + _strupr(Msg->via); + + // Update the saved to: line (remove the smtp:) + + strcpy(&HddrTo[Recipients][4], &HddrTo[Recipients][9]); + BBSMsgs++; + goto BBSMsg; + } + + // If a winlink.org address we need to convert to call + + if (_stricmp(Msg->via, "winlink.org") == 0) + { + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + _strupr(FullTo); + LocalMsg[Recipients] = CheckifLocalRMSUser(FullTo); + } + else + { + memcpy(Msg->via, &ptr1[9], linelen); + Msg->via[linelen - 9] = 0; + strcpy(FullTo,"RMS"); + } +// FullTo[0] = 0; + } + else + { + BBSMsg: + _strupr(FullTo); + _strupr(Msg->via); + } + } + + if (memcmp(FullTo, "RMS:", 4) == 0) + { + // remove RMS and add @winlink.org + + memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + } + + else if (memcmp(FullTo, "NTS:", 4) == 0) + { + // remove NTS and set type 'T' + + memmove(FullTo, &FullTo[4], strlen(FullTo) - 3); + Type[Recipients] = 'T'; // NTS + memmove(HddrTo[Recipients] + 4, HddrTo[Recipients] + 8, 91); + + // Replace Type: Private with Type: Traffic + + } + else if ((_memicmp(FullTo, "bull/", 5) == 0) || (_memicmp(FullTo, "bull:", 5) == 0)) + { + // remove bull/ and set type 'B' + + memmove(FullTo, &FullTo[5], strlen(FullTo) - 4); + Type[Recipients] = 'B'; // Bulletin + memmove(HddrTo[Recipients] + 4, HddrTo[Recipients] + 9, 90); + + // Replace Type: Private with Type: Bulletin + // Have to move rest of header down to make space + + } + + if (strcmp(Msg->via, "RMS") == 0) + { + // replace RMS with @winlink.org + + strcpy(Msg->via, "winlink.org"); + sprintf(HddrTo[Recipients], "To: %s@winlink.org\r\n", FullTo); + } + + if (strlen(FullTo) > 6) + FullTo[6] = 0; + + strlop(FullTo, '-'); + + strcpy(Msg->to, FullTo); + + if (SendBBStoSYSOPCall) + if (_stricmp(FullTo, BBSName) == 0) + strcpy(FullTo, SYSOPCall); + + if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0) + && (conn->Paclink || conn->RMSExpress)) + { + // No routing - check @BBS and WP + + struct UserInfo * ToUser = LookupCall(FullTo); + + Msg->via[0] = 0; // In case BPQ and not found + + if (ToUser) + { + // Local User. If Home BBS is specified, use it + + if (ToUser->HomeBBS[0]) + { + strcpy(Msg->via, ToUser->HomeBBS); + } + } + else + { + WPRecP WP = LookupWP(FullTo); + + if (WP) + { + strcpy(Msg->via, WP->first_homebbs); + + } + } + + // Fix To: address in B2 Header + + if (Msg->via[0]) + sprintf(HddrTo[Recipients], "To: %s@%s\r\n", FullTo, Msg->via); + else + sprintf(HddrTo[Recipients], "To: %s\r\n", FullTo); + + } + + RecpTo=realloc(RecpTo, (Recipients+1) * sizeof(void *)); + RecpTo[Recipients] = zalloc(10); + + Via=realloc(Via, (Recipients+1) * sizeof(void *)); + Via[Recipients] = zalloc(50); + + strcpy(Via[Recipients], Msg->via); + strcpy(RecpTo[Recipients++], FullTo); + + // Remove the To: Line from the buffer + + memmove(ptr1, ptr2+2, count); + goto Loop; + + } + else if (_memicmp(ptr1, "Type:", 4) == 0) + { + if (ptr1[6] == 'N') + Msg->type = 'T'; // NTS + else + Msg->type = ptr1[6]; + } + else if (_memicmp(ptr1, "Body:", 4) == 0) + { + MsgLen = atoi(&ptr1[5]); + StartofMsg = ptr1; + } + else if (_memicmp(ptr1, "File:", 5) == 0) + { + Msg->B2Flags |= Attachments; + } + else if (_memicmp(ptr1, "Date:", 5) == 0) + { + struct tm rtime; + char seps[] = " ,\t\r"; + + memset(&rtime, 0, sizeof(struct tm)); + + // Date: 2009/07/25 10:08 + + sscanf(&ptr1[5], "%04d/%02d/%02d %02d:%02d", + &rtime.tm_year, &rtime.tm_mon, &rtime.tm_mday, &rtime.tm_hour, &rtime.tm_min); + + rtime.tm_year -= 1900; + + Date = mktime(&rtime) - (time_t)_MYTIMEZONE; + + if (Date == (time_t)-1) + Date = time(NULL); + + } + + if (linelen) // Not Null line + { + ptr1 = ptr2 + 2; // Skip crlf + goto Loop; + } + + // Processed all headers + + + // If multiple recipents, create one copy for each BBS address, and one for all others (via RMS) + + if (Recipients == 0 || HddrTo == NULL) + { + Debugprintf("B2 Message with no recipients from %s", conn->Callsign); + Logprintf(LOG_BBS, conn, '!', "B2 Message with no recipients from %s", conn->Callsign); + SetupNextFBBMessage(conn); + return; + } + else + { + __int32 i; + struct MsgInfo * SaveMsg; + char * SaveBody; + __int32 SaveMsgLen = count; + BOOL SentToRMS = FALSE; + __int32 ToLen; + char * ToString = malloc(Recipients * 100); + char * bang; + + SaveMsg = Msg; + SaveBody = conn->MailBuffer; + + // If from WL2K, create one message for each to: or cc: that is a local user + + if (Msg->B2Flags & FromCMS) + { + struct UserInfo * user; + char Call[10]; + char * ptr; + + for (i = 0; i < Recipients; i++) + { + memcpy(Call, &HddrTo[i][4], 9); + ptr = strchr(Call, 13); + if (ptr) + *ptr = 0; + + strlop(Call, '-'); + + user = LookupCall(Call); + + if (user == 0) + continue; + + if (strcmp(Call, BBSName) != 0) // always accept to bbs call + if ((user->flags & F_POLLRMS) == 0) + continue; + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + // Add our To: + + ToLen = (int)strlen(HddrTo[i]); + + if (_memicmp(HddrTo[i], "CC", 2) == 0) // Replace CC: with TO: + memcpy(HddrTo[i], "To", 2); + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); + + conn->TempMsg->length += ToLen; + + strcpy(Msg->to, RecpTo[i]); + strcpy(Msg->via, Via[i]); + Msg->type = Type[i]; + + if (Recipients > 1) + Msg->bid[0] = 0; + + CreateMessageFromBuffer(conn); + } + } + else + { + // From a client - Create one copy with all RMS recipients, and one for each packet recipient + + // Merge all RMS To: lines + + // If don't add Winlink.org or no RMS BBS keep separate + + ToLen = 0; + ToString[0] = 0; + + for (i = 0; i < Recipients; i++) + { + if (conn->RadioOnlyMode || LocalMsg[i] || (conn->UserPointer->flags & F_NOWINLINK) || FindRMS()== NULL) + { + LocalMsg[i] = TRUE; // Make sure local copy is created + continue; // For a local RMS user + } + + // ?? Should a Bang Path override this ?? - I think so! + + if (strchr(Via[i], '!')) + continue; + + if (_stricmp(Via[i], "WINLINK.ORG") == 0 || _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + { + if (ToLen == 0) // First Addr + memcpy(HddrTo[i], "To", 2); // In Case CC + + ToLen += (int)strlen(HddrTo[i]); + strcat(ToString, HddrTo[i]); + } + } + + if (ToLen) + { + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + // Add all the To: lines back to message + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], ToString, ToLen); + + conn->TempMsg->length += ToLen; + + + if (Recipients > 1) + { + strcpy(Msg->via, "winlink.org"); + strcpy(Msg->to, "RMS"); + Msg->bid[0] = 0; // Must Change the BID + } + + // Don't change type, as we don't change the B2 Header for messages to RMS + //Msg->type = Type[0]; + + CreateMessageFromBuffer(conn); + } + + free(ToString); + + for (i = 0; i < Recipients; i++) + { + // Only Process Non - RMS Dests or local RMS Users + + // ?? Should a Bang Path override this ?? - I think so! + + if (strchr(Via[i], '!') == 0) + if (LocalMsg[i] == 0) + if (_stricmp (Via[i], "WINLINK.ORG") == 0 || + _memicmp (&HddrTo[i][4], "SMTP:", 5) == 0 || + _stricmp(RecpTo[i], "RMS") == 0) + continue; + + conn->TempMsg = Msg = malloc(sizeof(struct MsgInfo)); + memcpy(Msg, SaveMsg, sizeof(struct MsgInfo)); + + conn->MailBuffer = malloc(SaveMsgLen + 1000); + memcpy(conn->MailBuffer, SaveBody, SaveMsgLen); + + // Add our To: + + ptr = HddrTo[i]; + + // We removed any nts: or bull: earlier on, + // and saved type. We need to set type here, as + // may be sending to more than one type + // If message contains Type: Private and not 'P', + // need to changes to Traffic or Bulletin + + // But we need to remove last bang path if any + + if (strchr(ptr, '!')) + { + bang = ptr + strlen(ptr); + + while (*(--bang) != '!'); // Find last ! + + *(bang++) = 13; // Need CR; + *(bang++) = 10; // Need LF; + *(bang) = 0; // remove it; + } + + ToLen = (int)strlen(ptr); + + // if (_memicmp(HddrTo[i], "CC", 2) == 0) // Replace CC: with TO: + memcpy(HddrTo[i], "To", 2); + + memmove(&conn->MailBuffer[B2To + ToLen], &conn->MailBuffer[B2To], count); + memcpy(&conn->MailBuffer[B2To], HddrTo[i], ToLen); + + conn->TempMsg->length += ToLen; + + Msg->type = Type[i]; + + ptr = strstr(conn->MailBuffer, "\nType: "); + + if (ptr) + { + char * ptrx; + + ptr += 7; + + // This handles a message arriving with bull/ or nts/ overrides + + if (_memicmp(ptr, "Private", 7) == 0 && Msg->type != 'P') + { + if (Msg->type == 'T') + memcpy(ptr, "Traffic", 7); + else + if (Msg->type == 'B') + { + // have to make space + + memmove(ptr + 1, ptr, count); + conn->TempMsg->length++; + memcpy(ptr, "Bulletin", 8); + } + + // remove //wl2k from subject + + ptrx = strstr(conn->MailBuffer, "Subject: "); + + if (ptrx && _memicmp(ptrx + 9, "//WL2K ", 7) == 0) + { + memmove(ptrx + 9, ptrx + 16, count); + conn->TempMsg->length -= 7; + memmove(conn->TempMsg->title, &conn->TempMsg->title[7], strlen(conn->TempMsg->title) - 6); + } + } + // if we are receiving from another BBS rather + // than WLE or PAT we need to set msgtype from Type: + + Msg->type = ptr[0]; // I think it is safe to do it always + + if (Msg->type == 'N') + Msg->type = 'T'; + } + + strcpy(Msg->to, RecpTo[i]); + strcpy(Msg->via, Via[i]); + + if (i > 0 && Msg->type != 'B') // Must Change the BID + Msg->bid[0] = 0; + + // Update Stats + + if (Msg->type == 'P') + Index = PMSG; + else if (Msg->type == 'B') + Index = BMSG; + else if (Msg->type == 'T') + Index = TMSG; + + conn->UserPointer->Total.MsgsReceived[Index]++; + conn->UserPointer->Total.BytesForwardedIn[Index] += MsgLen; + + CreateMessageFromBuffer(conn); + } + } // End not from RMS + + free(SaveMsg); + free(SaveBody); + conn->MailBuffer = NULL; + conn->MailBufferSize=0; + + if (FromSync) + return; + + SetupNextFBBMessage(conn); + return; + } + +#ifndef LINBPQ + } + #define EXCEPTMSG "Error Decoding B2 Message" + #include "StdExcept.c" + + BBSputs(conn, "*** Program Error Decoding B2 Message\r"); + Flush(conn); + conn->CloseAfterFlush = 20; // 2 Secs + + return; + } +#endif + } // end if B2Msg + + CreateMessageFromBuffer(conn); + SetupNextFBBMessage(conn); +} \ No newline at end of file diff --git a/.svn/wc.db b/.svn/wc.db index 220a84260d5a29d0adb56f62a6d14b44a77418dd..78feeeb199e044fa6767eec64bd5084cf9d4664a 100644 GIT binary patch delta 8485 zcma)B378bswXS=sx~sdZs%wTB7TICOVIOK=K*I>sMb$m`oKxqX zd;b62TD!M$?cU0bgM>#Zikc4py8k*xo9NMd7F|09b=~*Yl$z)JR*SuU*}LLjG_(TU zm}MvmokI=|MMDPblrcJNZV4uMOP4HZUOC)tTc#@Yi^qV(K#~QCkUBz5Iw}IDKA^Ot zxABZsZLMwf9!#MAP$C$Qv|O=xRS4R;2OpqL={2pTxiwh1I9Og7CEd0#bs1SY3=Nrg z0oB+`c;4)3-(9k*#aX#@`HI2>i?Vu2%xrF*I_K=d*x+xBoiS(HQa^~k7>3y4DM7p^ zj@Ney-+)d+pG0*H=vk44h)~n=gQFwgLv6#knSvyY6b1>^LXuq7hRnp9+-uxR(kj#n0G7><8R_@k22X z{~|PUA7n2SSFq!S`RtwSPx&wTPx)o5wuPBxLIQW(_~uRF;7 ztI?EnXYqDZ^!aM^GCdM)EZxN3Idf# z6>n}UUU!k?)kqnAPS42X;z(CAb_iW0T-`HvYqaQUgc15F@z$U(2QKLu*(!E`DO{Yp zja->S&D8{gTYPd(@wz4YEQhYRG-VvT&`e#Y5uz zi6`T~FmM&d;sXe$77*z*Jdq3;k0-*zS>y5NIDWXM6W5c=C*mKDNnS)%;#Vk6&uq`2 z)YB;|-6nMLhxrRdhy7_{e_|qYGegs@_?2j^gilQ}=@z7uXZz9pqjd)E$0W_!H$QiM zU9^1?4il*}skNV154z@DP zeY}Gz_zBc*GJ6g_JxARcCv5!JV@eGa+wvsU)m&Ls4A%@D$#rbWFfC6uJk>E2$Mszq z-Pkinv1L`VmB7_Q%~n(|RCOuzEk(5~T?fZ}#gr_?RnQGRb5u7N=UA5H2DT65t{ob> zZ%Bb=YLaJ!zUSMfD}{!JHpG+2hAdMbU3xYaXrz*kKjVod;ZYq+cN8lK9b0kiPys(3 z(}mbbrseB0ST;S=&}DQ@^!7Zwt53Y79e2Z$K6p|0I=_1n-p9lbC(5`!dZPu8pfl%D zFHqu3;%=NLd>bArp|6Dg{0Gzv{9b-DKA&&qzr$B?Z*#xlHgW-Hb3*2IJTCJuM=!v& zOMHEJtzz1aq$m(i$@6U2^HkfD6$hT`lC3&A+SDT}Yq}i7QB*a{)Er%xT~|?q(AB~~ zl2iwE^vFt&TWQ zhpKI#RS0kEL5ZOP0IVn2WiEDYL z0xhHnzXOktv>o{GJk)A@fUK#4-FnJhcpHk*Y!o?dJKP_-8`GIMMecbFQe@PFcxHFC zxPCg^-S!ave!@)5qxw>RqQncc_hxH^h5W;Oid&vJExkLPPPMY9CO0Mi#r&43r*FiE z@Y$#nM7w{Tl@%MJ-|fYl7>4}|bsO0;h#nBNJ%*QHCi4c>5xxEdu1TR8B=I~RhtnH~ z^gK4|)BmIgCjOpyFtMJxBXLe*Sf-BoApHpQAhRA#7oQQk02k!!m)U*X@0q#TtzePXHY~p!dX{gif#y551Klq~ zXo~Jznrb_?AL@bas=jUoh6CFcZSI+4hJj-G8l+%{KF8k1UdGO3hbIpupJYrj_#hrIknzMn zivJ~EEuJmbWe;bc%ifkE-&fG}OJcx}feIPwQJt2)!FQS-Rx;lBc;g>^apoOvw!`HAI_xGS*O3Q#K)Ol~4+TK$8R4F;&Y5%s>uh%||~e zku^dsv;);w9oY*E8C0oIdSqXfgRn^oOmst!uqo-Dta_@UhdP+IP0#UF$y7Z_*27Tp z(T_@GO~v*UCA0$54HUxx?`_jiJ=^y*JFq1vY`=LEUpED#%~4}5eJ2Lc|EP|(&^9u5 z6nz%jLM|Oes{=awCDX!~6rMWuoO4$;w=}P84uJFkX#92*{R)G&MqT6SCG_Zql=znT z2WEBRuZgFb264BTX8tIy5<_t=FEY0M4Tf025Lo`l6m$;A>~n-sTjA9G(t znkAK- zUbJFrCH#L_3LhOrw?&nw(EDMV-x3`-mF|P4TuZSvDO{90KX7L`%`NaMU43BYh!ma| zv;YY_&wxjy3Lb&1vtr4LmCILoE9w`cokW^KpF0bHuL8^*onD;WLm<(vV#IpWM09IW z(pwxjegMpIF))^oTTuPh2AZKyP25b_26~v1_@}T>*e)y=W(b4%5BbNEhZB$UUHlS$ zD&H@40rx)pGCP;s%WYwQ!ClOKhpS559L;T@rzT>M;Bq?DX5Ca3&9h|_R$B%Xbxq4s zWX%P1Il=!arytpTE51UASoVZ%tl5Hdd7tup;Rwa`b#aVO*xpYySMR{%k z=SMph(N8l><_oG1SsKt+LKeLr&?nQ7MU~C;-w`^4e04E>Cd1{a3Nq^wdSLb&*|O>q z`o8XL8M}mL(Bzddb*xCFOBWk;RVLEkNR>W`^fxmlYHo$JJp)ojEXkC@)Y3#L#N(TJ zQjjZ6lW5OUdWJ;L0A|4MpeGMNKj)v~xA1P_eZHPM%zlhT=05Im^lEh84tf?M;x3wN za$q$bpfQdKWmNMe73hGfg-+nOo*x8`r&uarDcaamenZ&;HU=ERHEduAj^^r)?wFb; zg+ZX`L1>z`YN!^vE&>yu2-*~SI&jhCL&>>C`1pc-g#H_SMe-lnr?OkP(jylpir5ip@wnxNl>~B*RpI|H(m6T z9w%KLp4*CI2Br;Yq3VXD1tw58EpS~Qa3cskQ&wekeaRdXcu1h>f$J%{9#}pELGiV- zY$&3V;n`5op|ne`hkjHtNwX!>)`24Gj-x;%1E5$I@KWGGmK{QL49x^5(fW7}`EWd7 z(dqZom-LMh|F`eZKnkCtNAycoQ!ldIk<4V0JxEW8?1S``G?QA1o+djd0nZqoVe--J zH|aWt;X9};Wam~qAlmp2Jv;@y#62I<=ZG_gGZ{+UAvRi<&LrcP!bV?&lr?oOVbcqj3r#2Dr&rWq8Vt>mM(P}Kw9XYQnP z3-kSw%@j^AD0B+88vuTI1~dnX3EUm1mMLknB*{=qOvoF+EYH)tx(5qv6A=8kRi-K5 z7u3^VtN#{z$Q^?|P&*O@15V&uvJv>Yt7xvHI2Hhx1PcVLAv9%0YRXra8Yq`)PXEHegQ+470XmYNKnrT>|O>#6k-cKc~eFS_qrLlvEGOf$4>TFWZWRlhp+SCu$O! z@)e~9f-7aK=b&S;LszySNU8>j=J~P(NF}S_kEI%}7eIi-(g2m)VG1<`|2^Po zp%bcs2m1WLP)y5lWHa;>Te5PB<1EZ)OZAsqiDo@p(0?L^w=gf1s<$NvS|-nQoxpO! zoBZ>+DypEf_cveOZ`Km#%6FY_$Uci?%=u!L-S|-J{TuTW|%?G5l{hXwlN&q>OEH&VkiFee?LBW7@ zjK2Kqw!A5yDAn(n7IgSNGo- z0B~D?v2sszgi*yJ0oZoj4vExo%tbMp$JkCD;utBu88!{t_kld~9!*b54k2s>Gj+(2 z zEEy;=bU)|A-#-BS}S&(cK7RNE?wLT zX-Fl_y;Z+j)_`83bOJ{bRhjtIkm^3uC-N**&9n>oLGj2WFy%}|RnyAkgS}17dKN5Q zch;-hkI)QtGub(w89;W{F=Ih#$7Z^!vCMFPyQtG%mmO12S9b8 zwen}Us_|$xA3vFUYlYMXt#^-Iwe!~O%M>A%vrrXjy8|c4`jO0NQ2qKQTs5uClgZwy zg__vk*Z#VGR(o~6KY1O2zmrFGE4`-yRJ>^H^)w?|<=!_I~+(acrl8m$AZW zWtNNH3ZZy_!r3=JduV3+fIM0DB(5gzXhx1-eH%oz%qrSjDb&>1%Y0_oi1uOm>h9w( z0>@!mQR}o`=zDDN?X7h|H7GrNr1svYAOWi41Q(E2j7Ha!|@ZDGsq(on4M(NY-Ur`FoGF}2(Do^lZN5&k)VA@o+kA} z;jnL7Ei)LQ!6gZF!DlE=pzft9!Mfu?U2m_9b@0BeW53&PA2>xFbt*x`I%YLpvF=K; z>;PRw4%9LU^5Q7wa=P!jyr0o>)C;Ednjy9u3>*IEK1E4Ch-5}ALBdtCVnjUDfd6zjj5sB zh1@9iJ$809RbrH6p?NH~Ye8Fkdb)2A;bZb()KN0$KW9(wTigx(xMWh9iHv^OGp7e2 zk6%*Iweg&2y~$jW8X#~xs1(Oj951$H_h)tC7J=bgxdZSm?@)$mugDK6SvI#3)uQEO z+3mP$!GAt#{Z>muFQ<}|w1xjWdt+SH(c`13q%EzVG@%|=b;^MBNhu*c1)oGweJdz{ z;$&ZnBC|Vj4ZFN`X*-v%C3Yvaz~tGVLR~BKgH&&m{om}iIC*oHNQI{f{+p$;!6 z&KpqO%j`UQD}_D;wEp+!pZuwzy(&MlWE;Vm{b*1f7o%xqw#B_w*F6ZDTLwMzJa%OT{2G_HG@Ixnp`Og+8+2Z^s>wf2I1PB@k7%5V?Na0PHas9)xXeq|ttG$VyNHo*tl`H7+|Rzp%P;k?Vw+Aa%fYIk=l(kNSl}_bU zI0zosLsQ`=nqW3qjRpGM=bR-b>V{Yx=<#3C`2iFLWUZwT@*H}h3XvPHFdCZM;#`# z3NH)Qg1dHGyP*{eyY^WvKNIR+1O1_cj;eu`)_Q?60TIFAe%eg;*T58$y?ZS2 z;~>!CVu1F3x~ZSie%eNRSHLh>NQczI<3SGqFGAN2EZ_|bu-Zkc6a@$(TtxXgI3j#D z;KY8h-5_z;qoPX+-PTc?_quEzu~4xXwt2LXeDH{Imkt=A;{NQ#|}jo38P9R`W7P7EE;zK z#ZuoW6w8>|QRp5rnDs3vhAxXmuS5s}a6?}J=_&J8GZasWi6)2AF5i*oX!*iszJxo? z#gYz!afKckhsN^Q>1(Isjv*WE_Qagt7>_&&Kk~twyZpNKtx3qv@e{yqpq*w#gPOS? z(0=5k3*(8GuBW1ZK-kgJus()nPe=3g!RhF#tY*Rtz%_Euaf3+@nMB=lP$H4@?aM-2D-s6qY@u|@LBrfeH=rNUO;v-5|<*nMkO)&lF3NHu!J^$g$nhaX=sO= z5o_}v7VG;ZeF?Z)f=&{H89N-s=$Fe-AQp4L8K9lj-lD1l`GqT9@^x@VJ}kG&rSj9V zP3n<8kk(7Z(r8IBUoyXIUSpnX9%UA3t^++yH#?BG8(M{Cp=bv3=Wr)%fYV?^^EfZ+ z>q*S?YX{0fa5cSIfI{_AO(+-YZ!bdAeZy@a18MWreavpO1!AXr72TeMy!GBa2%C-EbRK27 z340$+>Z_l%&#=`S2hdAgg84G=0_TA?M}0>PR&wPJWl>sW9%p*XWD+Zci^5ob1NR?p z8aYB@@H*6sCc_pce!j(OS)+e^7;PZL>F@NRZ2HQ32b<^0W|I_8i-*04z> zJu-*9rGM}}`a=p$D}rA7tPo5QYf(2taU%4ikzqIsuA@uB@JMgFCvQ?BM3j~`b$WGa zS!s1?aaBwqEYgpJ;V%eWueV3xavUBlYFD&>k{a$C?i5MYsP+*#r!Cg(+7wbGA5>U=ih4l^f^yipVt(Sui!)ZeVq7^^M1>7#ouqz?cW?XjS%NLjBvw_DFWM}u@jrq#Bt`S!ZjQ9dYG_AOddH6y+Q>~@9 z%3w@eDkeCI_oG58_O~b}m95GmB~uw9-;j^;cW6>79-_}o#c5nifMreJ4b{V+!S_`f zIvslIW2WP!26XSvz_kps5i@a|m)(Oi{T^h?X5w}T{dC)GyaO@l-pj=~*nNShUDdj@ zgIuS;@wd1-ZI!l@`s86hq*c?TJO&rHfG){1;3|@4NfY#KdH56|R?ZYiD~s_82G_M> zJQOpytflxT2%G4G`FJAHIFmokUVwenCtz8;0PpLAC9)h7nA`!FuM8C?KM<4-VCmcF z$Ha{@Jqd|kTEXgLf`wTc0ExR1|L-7T9RP?C!9YCpgOxZl4v$9~6}I7Iclm_8Q7+)F z$uZI$;TF=!KI!lJ>}@z3GAznyeLOMigLdF+N>noOWCMghz)#WIwPJ0!`kQcw|5-hu zZd41%MBY!0QSK%k#%AXY zJXOn7q+pGlU~t9o`8qvM1%?12Wn3=mXp6 z%`4FM?n`7BrYA4qbn1TvZxHrOoZ|Fo9QMc;dgv-%&UKFV5Ynmg9d;722ei=2%_xZ; z{e}5^<30`_gzbQjODa0v`QH4>c@;G%Ez?-Tj|1tKJ-A)CmtkkIMD)c%`G4UW^yw=4 zr|)o!aO-U^r`(uK>ulJYre4Mig|5L_S_%9A$>JVEU%n1)bniVJ?(>8NI4H`nVDkL+ zFrCzc*Yf91>I_K@(jok+}vm{^@M0_{ii( zj1&3ARIrdVnq?t_bi0M<#`Vl~4bT9M1n94Ok^)m2_zHM{6ky(I_B1t!J%TRy^Q*X9 zTrN3I!axd!@GMNl8S}(}mw+ z8?6c_5p+ZZv3c1G@)HvZ5=SJ&B^Hd#A7vjtI?icnGWUt;Ow1i(Ta%Sqvsrc=I}t|% zf=M{*7YoxFBQza_4Z#k;Ck(9$+xzT928a@3tV)8C@3LbAm$fYsq#mnH5@qv_{fGJ< za`ePCHju?|G9CLiVH)Fyz4bGZWC(=tF;lZ|!*Gg+G50^*kHfuj8Q|kP5`KMf%rF$c zl&xx$FD%cb&M4A?Jsa!WdZ9PH5lA9kO4FE|Ic|C?G9x}v$wYG^tUXQsI%z<&v6^* zI}>zsDQ7!v6qIF;D;i diff --git a/IPCode.c b/IPCode.c index 54756e3..4d9abca 100644 --- a/IPCode.c +++ b/IPCode.c @@ -100,7 +100,10 @@ TODo ?Multiple Adapters #ifdef WIN32 int pcap_sendpacket(pcap_t *p, u_char *buf, int size); #else - PCAP_API int pcap_sendpacket(pcap_t *, const u_char *, int); +#ifndef PCAP_API +#define PCAP_API extern +#endif +PCAP_API int pcap_sendpacket(pcap_t *, const u_char *, int); #endif #ifndef LINBPQ diff --git a/LinBPQ.c b/LinBPQ.c index 6ba4b1c..689b5f4 100644 --- a/LinBPQ.c +++ b/LinBPQ.c @@ -913,7 +913,10 @@ int main(int argc, char * argv[]) Debugprintf("G8BPQ AX25 Packet Switch System Version %s %s", TextVerstring, Datestring); -#ifndef MACBPQ +#if defined(MACBPQ) || defined(FREEBSD) + time_t dummy; + _MYTIMEZONE = -localtime(&dummy)->tm_gmtoff; +#else _MYTIMEZONE = _timezone; #endif diff --git a/TNCEmulators.c b/TNCEmulators.c index 135d318..7fce453 100644 --- a/TNCEmulators.c +++ b/TNCEmulators.c @@ -241,8 +241,14 @@ typedef struct _SERIAL_STATUS { #ifndef WIN32 -#ifdef MACBPQ +#if defined(MACBPQ) #include +#elif defined(FREEBSD) +#if defined(__FreeBSD__) || defined(__DragonFly__) +#include +#else +#include +#endif #endif extern int posix_openpt (int __oflag); @@ -262,7 +268,7 @@ HANDLE LinuxOpenPTY(char * Name) u_long param=1; struct termios term; -#ifdef MACBPQ +#if defined(MACBPQ) || defined(FREEBSD) // Create a pty pair diff --git a/Versions.h b/Versions.h index 181a6ab..b27704b 100644 --- a/Versions.h +++ b/Versions.h @@ -10,15 +10,15 @@ #endif -#define KVers 6,0,24,75 -#define KVerstring "6.0.24.75\0" +#define KVers 6,0,24,76 +#define KVerstring "6.0.24.76\0" #ifdef CKernel #define Vers KVers #define Verstring KVerstring -#define Datestring "June 2025" +#define Datestring "July 2025" #define VerComments "G8BPQ Packet Switch (C Version)" KVerstring #define VerCopyright "Copyright © 2001-2025 John Wiseman G8BPQ\0" #define VerDesc "BPQ32 Switch\0" diff --git a/asmstrucs.h b/asmstrucs.h index aa73b93..79e9a26 100644 --- a/asmstrucs.h +++ b/asmstrucs.h @@ -1087,7 +1087,11 @@ struct SEM int Gets; int Rels; DWORD SemProcessID; +#ifdef WIN32 DWORD SemThreadID; +#else + pthread_t SemThreadID; +#endif int Line; // caller file and line char File[MAX_PATH]; }; diff --git a/linether.c b/linether.c index fb3410c..fbd82e6 100644 --- a/linether.c +++ b/linether.c @@ -22,17 +22,23 @@ along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses // Normally uses a Raw socket, but that can't send to other apps on same machine. // so can use a TAP device instead (or maybe as well??) -#ifndef MACBPQ -#include +#if !(defined(MACBPQ) || defined(FREEBSD)) +#include #include "cheaders.h" - #include + +#if !(defined(FREEBSD)) + #include #include #include +#endif +#endif + + extern int tap_fd; typedef struct PCAPStruct @@ -490,4 +496,4 @@ static int ProcessLine(char * buf, int Port, BOOL CheckPort) } -#endif + diff --git a/lzhuf32.c b/lzhuf32.c index f1ef0a4..c1aea82 100644 --- a/lzhuf32.c +++ b/lzhuf32.c @@ -1406,7 +1406,7 @@ File: 5566 NEWBOAT.HOMEPORT.JPG if (SendBBStoSYSOPCall) if (_stricmp(FullTo, BBSName) == 0) - strcpy(Msg->to, SYSOPCall); + strcpy(FullTo, SYSOPCall); if ((Msg->via[0] == 0 || strcmp(Msg->via, "BPQ") == 0 || strcmp(Msg->via, "BBS") == 0) && (conn->Paclink || conn->RMSExpress)) diff --git a/upnp.c b/upnp.c index 8190a62..714e04d 100644 --- a/upnp.c +++ b/upnp.c @@ -134,7 +134,7 @@ int upnpInit() return 0; } -#if MINIUPNPC_API_VERSION == 18 +#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)); @@ -168,7 +168,7 @@ int upnpClose() return 0; } -#if MINIUPNPC_API_VERSION == 18 +#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));